HighTechTalks DotNet Forums  

Introspecting COM Object in C#

Dotnet Framework (Interop) microsoft.public.dotnet.framework.interop


Discuss Introspecting COM Object in C# in the Dotnet Framework (Interop) forum.



Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old   
MichaelQuinlan
 
Posts: n/a

Default Introspecting COM Object in C# - 10-12-2007 , 08:54 AM






My C# program is being passed a COM object. I can successfully read and write
properties in the COM object by using code like this:

object tsFields = tsInterface.GetType().InvokeMember("TSFields",
BindingFlags.GetProperty, null, tsInterface, new object[] { });

What I would like to be able to do is get a list of the public properties
and methods defined in the COM object. When I use the statement

MemberInfo[] memberInfoArr = tsInterface.GetType().GetMembers();

The result contains 7 items:
+ [0] {System.Object GetLifetimeService()} System.Reflection.MemberInfo
{System.Reflection.RuntimeMethodInfo}
+ [1] {System.Object InitializeLifetimeService()}
System.Reflection.MemberInfo {System.Reflection.RuntimeMethodInfo}
+ [2] {System.Runtime.Remoting.ObjRef CreateObjRef(System.Type)}
System.Reflection.MemberInfo {System.Reflection.RuntimeMethodInfo}
+ [3] {System.Type GetType()} System.Reflection.MemberInfo
{System.Reflection.RuntimeMethodInfo}
+ [4] {System.String ToString()} System.Reflection.MemberInfo
{System.Reflection.RuntimeMethodInfo}
+ [5] {Boolean Equals(System.Object)} System.Reflection.MemberInfo
{System.Reflection.RuntimeMethodInfo}
+ [6] {Int32 GetHashCode()} System.Reflection.MemberInfo
{System.Reflection.RuntimeMethodInfo}

None of these are the actual properties and methods defined by the COM object.

Is there any way I can get a list of the properties and methods defined by
the COM object?

Thanks,

Reply With Quote
  #2  
Old   
Walter Wang [MSFT]
 
Posts: n/a

Default RE: Introspecting COM Object in C# - 10-15-2007 , 02:20 AM






Hi Michael,

If the COM object implements IDispatch, we can use IDispatch.GetTypeInfo to
get its type information.

Please try following code:

1) Add reference to "CustomMarshalers", you should be able to find it at
%windir%\Microsoft.NET\Framework\v2.0.50727\Custom Marshalers.dll.

2) Use following code to test:

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.InteropServices.CustomMarshalers;

namespace ConsoleApplication1
{
[
ComImport,
Guid("00020400-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )
]
public interface IDispatch
{
void Reserved();
[PreserveSig]
int GetTypeInfo(uint nInfo, int lcid,
[MarshalAs(
UnmanagedType.CustomMarshaler,
MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))]
out System.Type typeInfo);
}

class Program
{
static void Main(string[] args)
{
Type t1 = Type.GetTypeFromProgID("Scripting.FileSystemObject ");
Object o1 = Activator.CreateInstance(t1);

IDispatch disp2 = o1 as IDispatch;
if (disp2 != null)
{
Type t3;
disp2.GetTypeInfo(0, 0, out t3);

MemberInfo[] mlist3 = t3.GetMembers();
}
}
}
}


References:

#.NET Remoting: Create a Custom Marshaling Implementation Using .NET
Remoting and COM Interop -- MSDN Magazine, September 2003
http://msdn.microsoft.com/msdnmag/is...tomMarshaling/


Hope this helps.


Regards,
Walter Wang (wawang (AT) online (DOT) microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.



Reply With Quote
  #3  
Old   
MichaelQuinlan
 
Posts: n/a

Default RE: Introspecting COM Object in C# - 10-15-2007 , 12:33 PM



This is exactly what I need. Thanks!

""Walter Wang [MSFT]"" wrote:

Quote:
Hi Michael,

If the COM object implements IDispatch, we can use IDispatch.GetTypeInfo to
get its type information.

Please try following code:

1) Add reference to "CustomMarshalers", you should be able to find it at
%windir%\Microsoft.NET\Framework\v2.0.50727\Custom Marshalers.dll.

2) Use following code to test:

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.InteropServices.CustomMarshalers;

namespace ConsoleApplication1
{
[
ComImport,
Guid("00020400-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )
]
public interface IDispatch
{
void Reserved();
[PreserveSig]
int GetTypeInfo(uint nInfo, int lcid,
[MarshalAs(
UnmanagedType.CustomMarshaler,
MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))]
out System.Type typeInfo);
}

class Program
{
static void Main(string[] args)
{
Type t1 = Type.GetTypeFromProgID("Scripting.FileSystemObject ");
Object o1 = Activator.CreateInstance(t1);

IDispatch disp2 = o1 as IDispatch;
if (disp2 != null)
{
Type t3;
disp2.GetTypeInfo(0, 0, out t3);

MemberInfo[] mlist3 = t3.GetMembers();
}
}
}
}


References:

#.NET Remoting: Create a Custom Marshaling Implementation Using .NET
Remoting and COM Interop -- MSDN Magazine, September 2003
http://msdn.microsoft.com/msdnmag/is...tomMarshaling/


Hope this helps.


Regards,
Walter Wang (wawang (AT) online (DOT) microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.




Reply With Quote
  #4  
Old   
Fred Petter Pedersen
 
Posts: n/a

Default Introspecting COM Object in C# - 10-16-2007 , 05:20 AM



I dont really have an answer for you but is working with exactly the same. Would like to hear from you if you find any solution. I'll keep on working on this so I'll come back with my findings some time. Good luck! ;-)

EggHeadCafe - .NET Developer Portal of Choice
http://www.eggheadcafe.com

Reply With Quote
  #5  
Old   
fredpp@online.no
 
Posts: n/a

Default Re: Introspecting COM Object in C# - 10-23-2007 , 02:19 AM



On 15 Okt, 08:20, waw... (AT) online (DOT) microsoft.com ("Walter Wang [MSFT]")
wrote:
Quote:
Hi Michael,

If the COM object implements IDispatch, we can use IDispatch.GetTypeInfotogetitstypeinformation.

Please try following code:

1) Add reference to "CustomMarshalers", you should be able to find it at
%windir%\Microsoft.NET\Framework\v2.0.50727\Custom Marshalers.dll.

2) Use following code to test:

class Program
{
static void Main(string[] args)
{
Typet1 =Type.GetTypeFromProgID("Scripting.FileSystemObjec t");
Object o1 = Activator.CreateInstance(t1);

IDispatch disp2 = o1 as IDispatch;
if (disp2 != null)
{
Typet3;
disp2.GetTypeInfo(0, 0, out t3);

MemberInfo[] mlist3 = t3.GetMembers();
}
}
}

}

References:

#.NET Remoting: Create a Custom Marshaling Implementation Using .NET
Remoting and COM Interop -- MSDN Magazine, September 2003http://msdn.microsoft.com/msdnmag/issues/03/09/CustomMarshaling/

Hope this helps.

Regards,
Walter Wang (waw... (AT) online (DOT) microsoft.com, remove 'online.')
Microsoft Online Community Support

This posting is provided "AS IS" with no warranties, and confers no rights.
Hi

I'm trying to do the exactly same but the method:

disp2.GetTypeInfo(0, 0, out t3);

takes very long time to get finished, about 4 minutes, which is too
mutch of course. What can be the reason for this? I know that my COM-
object has very many interfaces, but I cant understand why this
shouldn't be handled....... My COM is written in Delphi and I have
access to the source, is there anything I can look at in the source to
make this faster?

Thanks in advance

- RightCoder



Reply With Quote
  #6  
Old   
Jason Newell
 
Posts: n/a

Default Re: Introspecting COM Object in C# - 10-25-2007 , 05:57 PM



The reason that it is taking so long is because it's generating
in-memory interop assemblies behind the scenes. It's basically the same
thing as adding a reference to a type library in Visual Studio although
it seems to be slower than when you manually add the reference.

The faster but more difficult way to work with COM in .NET is to work
directly with the IDispatch interface. Here is a bit of code that I use
in one of my applications. The code listed below are 3 separate .cs
files. I also did not list my full source so not all functionality is
available in what I'm giving you but it should be enough to give you an
idea as to what's involved.

Once you have the code in place, you can use it something like:

Jason Newell
www.jasonnewell.net


/* Example Usage */
ComObject comObject = new
ComObject(Marshal.GetActiveObject("Word.Applicatio n"));
string[] propertyNames = comObject.GetPropertyNames();
foreach (string propertyName in propertyNames)
{
object property =
comObject.WrappedComObject.GetType().InvokeMember( propertyName,
System.Reflection.BindingFlags.GetProperty, null,
comObject.WrappedComObject, null);
}



/* IDispatch.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
[Guid("00020400-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IDispatch
{
int GetTypeInfoCount();
System.Runtime.InteropServices.ComTypes.ITypeInfo
GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo,
[MarshalAs(UnmanagedType.U4)] int lcid);
[PreserveSig]
int GetIDsOfNames(ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
string[] rgsNames, int cNames, int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
[PreserveSig]
int Invoke(int dispIdMember, ref Guid riid,
[MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
}
}

/* ComObject.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComObject : IDisposable
{
private object _object;
private IDispatch _dispatch;
private IntPtr _pTypeAttr = IntPtr.Zero;
private System.Runtime.InteropServices.ComTypes.ITypeInfo
_typeInfo;
private string _typeName, _typeDescription, _typeHelpFile;
private int _typeHelpContext;
private SolidEdgeSpy.InteropServices.ComTypeLibrary
_comTypeLibrary;

public ComObject(object comObject)
{
System.Runtime.InteropServices.ComTypes.ITypeLib ppTLB = null;
int pIndex = 0;

_dispatch = comObject as IDispatch;

if (_dispatch != null)
{
_object = comObject;
_typeInfo = _dispatch.GetTypeInfo(0, 0);
_typeInfo.GetTypeAttr(out _pTypeAttr);
_typeInfo.GetDocumentation(-1, out _typeName, out
_typeDescription, out _typeHelpContext, out _typeHelpFile);
_typeInfo.GetContainingTypeLib(out ppTLB, out pIndex);
_comTypeLibrary = new ComTypeLibrary(ppTLB);
}
else
{
throw new InvalidComObjectException();
}
}

~ComObject()
{
Dispose();
}

public void Dispose()
{
try
{
if (_typeInfo != null)
{
_typeInfo.ReleaseTypeAttr(_pTypeAttr);
}

if (_object != null) {
Marshal.ReleaseComObject(_object); _object = null; }
if (_dispatch != null) {
Marshal.ReleaseComObject(_dispatch); _dispatch = null; }
}
catch
{
}
}

public string[] GetPropertyNames()
{
System.Collections.ArrayList list = new
System.Collections.ArrayList();

try
{
for (int i = 0; i < this.TypeAttr.cFuncs; i++)
{
IntPtr pFuncDesc = IntPtr.Zero;
System.Runtime.InteropServices.ComTypes.FUNCDESC
funcDesc;
string strName, strDocString, strHelpFile;
int dwHelpContext;

_typeInfo.GetFuncDesc(i, out pFuncDesc);
funcDesc =
(System.Runtime.InteropServices.ComTypes.FUNCDESC) Marshal.PtrToStructure(pFuncDesc,
typeof(System.Runtime.InteropServices.ComTypes.FUN CDESC));

switch (funcDesc.invkind)
{
case
System.Runtime.InteropServices.ComTypes.INVOKEKIND .INVOKE_PROPERTYGET:
_typeInfo.GetDocumentation(funcDesc.memid,
out strName, out strDocString, out dwHelpContext, out strHelpFile);
list.Add(strName);
break;
}
}
}
catch (System.Exception ex)
{
throw ex;
}

return (string[])list.ToArray(typeof(string));
}

public string TypeName { get { return this._typeName; } }
public string TypeFullName { get { return
this.ComTypeLibrary.Name + "." + this._typeName; } }
public string TypeDescription { get { return
this._typeDescription; } }
public int TypeHelpContext { get { return
this._typeHelpContext; } }
public string TypeHelpFile { get { return this._typeHelpFile; } }
public object WrappedComObject { get { return _dispatch; } }
public ComTypeLibrary ComTypeLibrary { get { return
_comTypeLibrary; } }
public System.Runtime.InteropServices.ComTypes.TYPEATTR
TypeAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPEATTR) Marshal.PtrToStructure(_pTypeAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP EATTR)); } }
public string TypeVersion
{
get
{
string version = String.Empty;
try
{
version = TypeAttr.wMajorVerNum.ToString() + "." +
TypeAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}
}
}

/* ComTypeLibrary.cs */
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComTypeLibrary
{
private System.Runtime.InteropServices.ComTypes.ITypeLib _typeLib;
IntPtr _pTypeLibAttr = IntPtr.Zero;
string _Name, _Description, _HelpFile;
int _HelpContext;

public
ComTypeLibrary(System.Runtime.InteropServices.ComT ypes.ITypeLib typeLib)
{
_typeLib = typeLib;
_typeLib.GetLibAttr(out _pTypeLibAttr);
_typeLib.GetDocumentation(-1, out _Name, out _Description,
out _HelpContext, out _HelpFile);
}

public string Name { get { return this._Name; } }
public string Description { get { return this._Description; } }
public int HelpContext { get { return this._HelpContext; } }
public string HelpFile { get { return this._HelpFile; } }
public System.Runtime.InteropServices.ComTypes.TYPELIBATT R
TypeLibAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPELIBAT TR)Marshal.PtrToStructure(_pTypeLibAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP ELIBATTR)); } }
public string TypeLibVersion
{
get
{
string version = String.Empty;
try
{
version = TypeLibAttr.wMajorVerNum.ToString() + "."
+ TypeLibAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}

public Version Version
{
get
{
return new Version(TypeLibAttr.wMajorVerNum,
TypeLibAttr.wMinorVerNum, 0, 0);
}
}

public override int GetHashCode()
{
return this.TypeLibAttr.guid.GetHashCode() +
this.TypeLibAttr.wMajorVerNum.GetHashCode() +
this.TypeLibAttr.wMinorVerNum.GetHashCode();
}

public override bool Equals(object obj)
{
ComTypeLibrary comTypeLibrary = obj as ComTypeLibrary;
if (comTypeLibrary != null)
{
if
(this.TypeLibAttr.guid.Equals(comTypeLibrary.TypeL ibAttr.guid))
{
if
(this.TypeLibAttr.wMajorVerNum.Equals(comTypeLibra ry.TypeLibAttr.wMajorVerNum))
{
if
(this.TypeLibAttr.wMinorVerNum.Equals(comTypeLibra ry.TypeLibAttr.wMinorVerNum))
{
return true;
}
}
}
return base.Equals(obj);
}
else
{
return base.Equals(obj);
}
}

public override string ToString()
{
return this.Name + " - " + this.Description;
}
}
}


fredpp (AT) online (DOT) no wrote:
Quote:
On 15 Okt, 08:20, waw... (AT) online (DOT) microsoft.com ("Walter Wang [MSFT]")
wrote:
Hi Michael,

If the COM object implements IDispatch, we can use IDispatch.GetTypeInfotogetitstypeinformation.

Please try following code:

1) Add reference to "CustomMarshalers", you should be able to find it at
%windir%\Microsoft.NET\Framework\v2.0.50727\Custom Marshalers.dll.

2) Use following code to test:

class Program
{
static void Main(string[] args)
{
Typet1 =Type.GetTypeFromProgID("Scripting.FileSystemObjec t");
Object o1 = Activator.CreateInstance(t1);

IDispatch disp2 = o1 as IDispatch;
if (disp2 != null)
{
Typet3;
disp2.GetTypeInfo(0, 0, out t3);

MemberInfo[] mlist3 = t3.GetMembers();
}
}
}

}

References:

#.NET Remoting: Create a Custom Marshaling Implementation Using .NET
Remoting and COM Interop -- MSDN Magazine, September 2003http://msdn.microsoft.com/msdnmag/issues/03/09/CustomMarshaling/

Hope this helps.

Regards,
Walter Wang (waw... (AT) online (DOT) microsoft.com, remove 'online.')
Microsoft Online Community Support

This posting is provided "AS IS" with no warranties, and confers no rights.

Hi

I'm trying to do the exactly same but the method:

disp2.GetTypeInfo(0, 0, out t3);

takes very long time to get finished, about 4 minutes, which is too
mutch of course. What can be the reason for this? I know that my COM-
object has very many interfaces, but I cant understand why this
shouldn't be handled....... My COM is written in Delphi and I have
access to the source, is there anything I can look at in the source to
make this faster?

Thanks in advance

- RightCoder


Reply With Quote
  #7  
Old   
RightCoder
 
Posts: n/a

Default Re: Introspecting COM Object in C# - 10-29-2007 , 06:12 AM



Hi Jason

Thank you for your response. I must admit I have not had a closer look at
your code because it seems to be a lot of work to be done before I'm there. I
have no experience with COM nor working with IDispatch so this looks like a
mountain for me. I dont have the time either to sit down and understand all
the details.

I may be asking for much here, but I'm looking for a complete solution and I
was hoping it was a matter of changing an input-parameter or doing another
methodcall. Is it really that difficult to introspect COM from .NET?

What else could be done to make this work without taking so long time? Could
I make the COMs TLB available on the client? There must be an easier way or a
workaround that is doable for a mere mortal like me (ASP.NET developer)? ;-)

"Jason Newell" wrote:

Quote:
The reason that it is taking so long is because it's generating
in-memory interop assemblies behind the scenes. It's basically the same
thing as adding a reference to a type library in Visual Studio although
it seems to be slower than when you manually add the reference.

The faster but more difficult way to work with COM in .NET is to work
directly with the IDispatch interface. Here is a bit of code that I use
in one of my applications. The code listed below are 3 separate .cs
files. I also did not list my full source so not all functionality is
available in what I'm giving you but it should be enough to give you an
idea as to what's involved.

Once you have the code in place, you can use it something like:

Jason Newell
www.jasonnewell.net


/* Example Usage */
ComObject comObject = new
ComObject(Marshal.GetActiveObject("Word.Applicatio n"));
string[] propertyNames = comObject.GetPropertyNames();
foreach (string propertyName in propertyNames)
{
object property =
comObject.WrappedComObject.GetType().InvokeMember( propertyName,
System.Reflection.BindingFlags.GetProperty, null,
comObject.WrappedComObject, null);
}



/* IDispatch.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
[Guid("00020400-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IDispatch
{
int GetTypeInfoCount();
System.Runtime.InteropServices.ComTypes.ITypeInfo
GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo,
[MarshalAs(UnmanagedType.U4)] int lcid);
[PreserveSig]
int GetIDsOfNames(ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
string[] rgsNames, int cNames, int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
[PreserveSig]
int Invoke(int dispIdMember, ref Guid riid,
[MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
}
}

/* ComObject.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComObject : IDisposable
{
private object _object;
private IDispatch _dispatch;
private IntPtr _pTypeAttr = IntPtr.Zero;
private System.Runtime.InteropServices.ComTypes.ITypeInfo
_typeInfo;
private string _typeName, _typeDescription, _typeHelpFile;
private int _typeHelpContext;
private SolidEdgeSpy.InteropServices.ComTypeLibrary
_comTypeLibrary;

public ComObject(object comObject)
{
System.Runtime.InteropServices.ComTypes.ITypeLib ppTLB = null;
int pIndex = 0;

_dispatch = comObject as IDispatch;

if (_dispatch != null)
{
_object = comObject;
_typeInfo = _dispatch.GetTypeInfo(0, 0);
_typeInfo.GetTypeAttr(out _pTypeAttr);
_typeInfo.GetDocumentation(-1, out _typeName, out
_typeDescription, out _typeHelpContext, out _typeHelpFile);
_typeInfo.GetContainingTypeLib(out ppTLB, out pIndex);
_comTypeLibrary = new ComTypeLibrary(ppTLB);
}
else
{
throw new InvalidComObjectException();
}
}

~ComObject()
{
Dispose();
}

public void Dispose()
{
try
{
if (_typeInfo != null)
{
_typeInfo.ReleaseTypeAttr(_pTypeAttr);
}

if (_object != null) {
Marshal.ReleaseComObject(_object); _object = null; }
if (_dispatch != null) {
Marshal.ReleaseComObject(_dispatch); _dispatch = null; }
}
catch
{
}
}

public string[] GetPropertyNames()
{
System.Collections.ArrayList list = new
System.Collections.ArrayList();

try
{
for (int i = 0; i < this.TypeAttr.cFuncs; i++)
{
IntPtr pFuncDesc = IntPtr.Zero;
System.Runtime.InteropServices.ComTypes.FUNCDESC
funcDesc;
string strName, strDocString, strHelpFile;
int dwHelpContext;

_typeInfo.GetFuncDesc(i, out pFuncDesc);
funcDesc =
(System.Runtime.InteropServices.ComTypes.FUNCDESC) Marshal.PtrToStructure(pFuncDesc,
typeof(System.Runtime.InteropServices.ComTypes.FUN CDESC));

switch (funcDesc.invkind)
{
case
System.Runtime.InteropServices.ComTypes.INVOKEKIND .INVOKE_PROPERTYGET:
_typeInfo.GetDocumentation(funcDesc.memid,
out strName, out strDocString, out dwHelpContext, out strHelpFile);
list.Add(strName);
break;
}
}
}
catch (System.Exception ex)
{
throw ex;
}

return (string[])list.ToArray(typeof(string));
}

public string TypeName { get { return this._typeName; } }
public string TypeFullName { get { return
this.ComTypeLibrary.Name + "." + this._typeName; } }
public string TypeDescription { get { return
this._typeDescription; } }
public int TypeHelpContext { get { return
this._typeHelpContext; } }
public string TypeHelpFile { get { return this._typeHelpFile; } }
public object WrappedComObject { get { return _dispatch; } }
public ComTypeLibrary ComTypeLibrary { get { return
_comTypeLibrary; } }
public System.Runtime.InteropServices.ComTypes.TYPEATTR
TypeAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPEATTR) Marshal.PtrToStructure(_pTypeAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP EATTR)); } }
public string TypeVersion
{
get
{
string version = String.Empty;
try
{
version = TypeAttr.wMajorVerNum.ToString() + "." +
TypeAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}
}
}

/* ComTypeLibrary.cs */
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComTypeLibrary
{
private System.Runtime.InteropServices.ComTypes.ITypeLib _typeLib;
IntPtr _pTypeLibAttr = IntPtr.Zero;
string _Name, _Description, _HelpFile;
int _HelpContext;

public
ComTypeLibrary(System.Runtime.InteropServices.ComT ypes.ITypeLib typeLib)
{
_typeLib = typeLib;
_typeLib.GetLibAttr(out _pTypeLibAttr);
_typeLib.GetDocumentation(-1, out _Name, out _Description,
out _HelpContext, out _HelpFile);
}

public string Name { get { return this._Name; } }
public string Description { get { return this._Description; } }
public int HelpContext { get { return this._HelpContext; } }
public string HelpFile { get { return this._HelpFile; } }
public System.Runtime.InteropServices.ComTypes.TYPELIBATT R
TypeLibAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPELIBAT TR)Marshal.PtrToStructure(_pTypeLibAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP ELIBATTR)); } }
public string TypeLibVersion
{
get
{
string version = String.Empty;
try
{
version = TypeLibAttr.wMajorVerNum.ToString() + "."
+ TypeLibAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}

public Version Version
{
get
{
return new Version(TypeLibAttr.wMajorVerNum,
TypeLibAttr.wMinorVerNum, 0, 0);
}
}

public override int GetHashCode()
{
return this.TypeLibAttr.guid.GetHashCode() +
this.TypeLibAttr.wMajorVerNum.GetHashCode() +
this.TypeLibAttr.wMinorVerNum.GetHashCode();
}

public override bool Equals(object obj)
{
ComTypeLibrary comTypeLibrary = obj as ComTypeLibrary;
if (comTypeLibrary != null)
{
if
(this.TypeLibAttr.guid.Equals(comTypeLibrary.TypeL ibAttr.guid))
{
if
(this.TypeLibAttr.wMajorVerNum.Equals(comTypeLibra ry.TypeLibAttr.wMajorVerNum))
{
if
(this.TypeLibAttr.wMinorVerNum.Equals(comTypeLibra ry.TypeLibAttr.wMinorVerNum))
{
return true;
}
}
}
return base.Equals(obj);
}
else
{
return base.Equals(obj);
}
}

public override string ToString()

Reply With Quote
  #8  
Old   
Jason Newell
 
Posts: n/a

Default Re: Introspecting COM Object in C# - 10-29-2007 , 09:31 AM



If there is an easier way to do it with existing .NET API's, then I
guess I've wasted a considerable amount of time developing a lot of
code. I always look for existing .NET API's before I dive off doing
things myself, but in this case, I found no other solution.

Fortunately for me, I do have a lot of COM experience so I'm able to
write the code. The API that I've written closely resembles the
existing System.Reflection namespace. Everything that I've done starts
with the name Com. i.e. ComType, ComObject, ComMethodInfo,
ComPropertyInfo, ComParameterInfo, etc.

The code that I posted is still very much a work in progress. It sounds
like it could be useful to others so I'll post a copy of the source onto
my website. Maybe even write a Code Project article about it.

Jason Newell
www.jasonnewell.net

RightCoder wrote:
Quote:
Hi Jason

Thank you for your response. I must admit I have not had a closer look at
your code because it seems to be a lot of work to be done before I'm there. I
have no experience with COM nor working with IDispatch so this looks like a
mountain for me. I dont have the time either to sit down and understand all
the details.

I may be asking for much here, but I'm looking for a complete solution and I
was hoping it was a matter of changing an input-parameter or doing another
methodcall. Is it really that difficult to introspect COM from .NET?

What else could be done to make this work without taking so long time? Could
I make the COMs TLB available on the client? There must be an easier way or a
workaround that is doable for a mere mortal like me (ASP.NET developer)? ;-)

"Jason Newell" wrote:

The reason that it is taking so long is because it's generating
in-memory interop assemblies behind the scenes. It's basically the same
thing as adding a reference to a type library in Visual Studio although
it seems to be slower than when you manually add the reference.

The faster but more difficult way to work with COM in .NET is to work
directly with the IDispatch interface. Here is a bit of code that I use
in one of my applications. The code listed below are 3 separate .cs
files. I also did not list my full source so not all functionality is
available in what I'm giving you but it should be enough to give you an
idea as to what's involved.

Once you have the code in place, you can use it something like:

Jason Newell
www.jasonnewell.net


/* Example Usage */
ComObject comObject = new
ComObject(Marshal.GetActiveObject("Word.Applicatio n"));
string[] propertyNames = comObject.GetPropertyNames();
foreach (string propertyName in propertyNames)
{
object property =
comObject.WrappedComObject.GetType().InvokeMember( propertyName,
System.Reflection.BindingFlags.GetProperty, null,
comObject.WrappedComObject, null);
}



/* IDispatch.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
[Guid("00020400-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IDispatch
{
int GetTypeInfoCount();
System.Runtime.InteropServices.ComTypes.ITypeInfo
GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo,
[MarshalAs(UnmanagedType.U4)] int lcid);
[PreserveSig]
int GetIDsOfNames(ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
string[] rgsNames, int cNames, int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
[PreserveSig]
int Invoke(int dispIdMember, ref Guid riid,
[MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
}
}

/* ComObject.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComObject : IDisposable
{
private object _object;
private IDispatch _dispatch;
private IntPtr _pTypeAttr = IntPtr.Zero;
private System.Runtime.InteropServices.ComTypes.ITypeInfo
_typeInfo;
private string _typeName, _typeDescription, _typeHelpFile;
private int _typeHelpContext;
private SolidEdgeSpy.InteropServices.ComTypeLibrary
_comTypeLibrary;

public ComObject(object comObject)
{
System.Runtime.InteropServices.ComTypes.ITypeLib ppTLB = null;
int pIndex = 0;

_dispatch = comObject as IDispatch;

if (_dispatch != null)
{
_object = comObject;
_typeInfo = _dispatch.GetTypeInfo(0, 0);
_typeInfo.GetTypeAttr(out _pTypeAttr);
_typeInfo.GetDocumentation(-1, out _typeName, out
_typeDescription, out _typeHelpContext, out _typeHelpFile);
_typeInfo.GetContainingTypeLib(out ppTLB, out pIndex);
_comTypeLibrary = new ComTypeLibrary(ppTLB);
}
else
{
throw new InvalidComObjectException();
}
}

~ComObject()
{
Dispose();
}

public void Dispose()
{
try
{
if (_typeInfo != null)
{
_typeInfo.ReleaseTypeAttr(_pTypeAttr);
}

if (_object != null) {
Marshal.ReleaseComObject(_object); _object = null; }
if (_dispatch != null) {
Marshal.ReleaseComObject(_dispatch); _dispatch = null; }
}
catch
{
}
}

public string[] GetPropertyNames()
{
System.Collections.ArrayList list = new
System.Collections.ArrayList();

try
{
for (int i = 0; i < this.TypeAttr.cFuncs; i++)
{
IntPtr pFuncDesc = IntPtr.Zero;
System.Runtime.InteropServices.ComTypes.FUNCDESC
funcDesc;
string strName, strDocString, strHelpFile;
int dwHelpContext;

_typeInfo.GetFuncDesc(i, out pFuncDesc);
funcDesc =
(System.Runtime.InteropServices.ComTypes.FUNCDESC) Marshal.PtrToStructure(pFuncDesc,
typeof(System.Runtime.InteropServices.ComTypes.FUN CDESC));

switch (funcDesc.invkind)
{
case
System.Runtime.InteropServices.ComTypes.INVOKEKIND .INVOKE_PROPERTYGET:
_typeInfo.GetDocumentation(funcDesc.memid,
out strName, out strDocString, out dwHelpContext, out strHelpFile);
list.Add(strName);
break;
}
}
}
catch (System.Exception ex)
{
throw ex;
}

return (string[])list.ToArray(typeof(string));
}

public string TypeName { get { return this._typeName; } }
public string TypeFullName { get { return
this.ComTypeLibrary.Name + "." + this._typeName; } }
public string TypeDescription { get { return
this._typeDescription; } }
public int TypeHelpContext { get { return
this._typeHelpContext; } }
public string TypeHelpFile { get { return this._typeHelpFile; } }
public object WrappedComObject { get { return _dispatch; } }
public ComTypeLibrary ComTypeLibrary { get { return
_comTypeLibrary; } }
public System.Runtime.InteropServices.ComTypes.TYPEATTR
TypeAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPEATTR) Marshal.PtrToStructure(_pTypeAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP EATTR)); } }
public string TypeVersion
{
get
{
string version = String.Empty;
try
{
version = TypeAttr.wMajorVerNum.ToString() + "." +
TypeAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}
}
}

/* ComTypeLibrary.cs */
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComTypeLibrary
{
private System.Runtime.InteropServices.ComTypes.ITypeLib _typeLib;
IntPtr _pTypeLibAttr = IntPtr.Zero;
string _Name, _Description, _HelpFile;
int _HelpContext;

public
ComTypeLibrary(System.Runtime.InteropServices.ComT ypes.ITypeLib typeLib)
{
_typeLib = typeLib;
_typeLib.GetLibAttr(out _pTypeLibAttr);
_typeLib.GetDocumentation(-1, out _Name, out _Description,
out _HelpContext, out _HelpFile);
}

public string Name { get { return this._Name; } }
public string Description { get { return this._Description; } }
public int HelpContext { get { return this._HelpContext; } }
public string HelpFile { get { return this._HelpFile; } }
public System.Runtime.InteropServices.ComTypes.TYPELIBATT R
TypeLibAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPELIBAT TR)Marshal.PtrToStructure(_pTypeLibAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP ELIBATTR)); } }
public string TypeLibVersion
{
get
{
string version = String.Empty;
try
{
version = TypeLibAttr.wMajorVerNum.ToString() + "."
+ TypeLibAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}

public Version Version
{
get
{
return new Version(TypeLibAttr.wMajorVerNum,
TypeLibAttr.wMinorVerNum, 0, 0);
}
}

public override int GetHashCode()
{
return this.TypeLibAttr.guid.GetHashCode() +
this.TypeLibAttr.wMajorVerNum.GetHashCode() +
this.TypeLibAttr.wMinorVerNum.GetHashCode();
}

public override bool Equals(object obj)
{
ComTypeLibrary comTypeLibrary = obj as ComTypeLibrary;
if (comTypeLibrary != null)
{
if
(this.TypeLibAttr.guid.Equals(comTypeLibrary.TypeL ibAttr.guid))
{
if
(this.TypeLibAttr.wMajorVerNum.Equals(comTypeLibra ry.TypeLibAttr.wMajorVerNum))
{
if
(this.TypeLibAttr.wMinorVerNum.Equals(comTypeLibra ry.TypeLibAttr.wMinorVerNum))
{
return true;
}
}
}
return base.Equals(obj);
}
else
{
return base.Equals(obj);
}
}

public override string ToString()

Reply With Quote
  #9  
Old   
RightCoder
 
Posts: n/a

Default Re: Introspecting COM Object in C# - 10-29-2007 , 11:14 AM



Yes, I really think this would be a great article at Code Project. This is an
area that most of us have little or no experience with, I can say that
because I cant find much information about it on the Internet. Even MSDN have
litle documentation on topics like this.

I hope you'll share your code and experience with the rest of us either by
your own homepage/blog or Code Project. What would be great if you could make
a component/class that had some methods I could use that implemented all the
magic you are doing, but that I, as a user, really dont have to know anything
about. You could even earn some money on it ;-)

I'll try to learn some COM-programming the next few weeks and see how far
I'll come. Thanks anyway, Jason.

"Jason Newell" wrote:

Quote:
If there is an easier way to do it with existing .NET API's, then I
guess I've wasted a considerable amount of time developing a lot of
code. I always look for existing .NET API's before I dive off doing
things myself, but in this case, I found no other solution.

Fortunately for me, I do have a lot of COM experience so I'm able to
write the code. The API that I've written closely resembles the
existing System.Reflection namespace. Everything that I've done starts
with the name Com. i.e. ComType, ComObject, ComMethodInfo,
ComPropertyInfo, ComParameterInfo, etc.

The code that I posted is still very much a work in progress. It sounds
like it could be useful to others so I'll post a copy of the source onto
my website. Maybe even write a Code Project article about it.

Jason Newell
www.jasonnewell.net

RightCoder wrote:
Hi Jason

Thank you for your response. I must admit I have not had a closer look at
your code because it seems to be a lot of work to be done before I'm there. I
have no experience with COM nor working with IDispatch so this looks like a
mountain for me. I dont have the time either to sit down and understand all
the details.

I may be asking for much here, but I'm looking for a complete solution and I
was hoping it was a matter of changing an input-parameter or doing another
methodcall. Is it really that difficult to introspect COM from .NET?

What else could be done to make this work without taking so long time? Could
I make the COMs TLB available on the client? There must be an easier way or a
workaround that is doable for a mere mortal like me (ASP.NET developer)? ;-)

"Jason Newell" wrote:

The reason that it is taking so long is because it's generating
in-memory interop assemblies behind the scenes. It's basically the same
thing as adding a reference to a type library in Visual Studio although
it seems to be slower than when you manually add the reference.

The faster but more difficult way to work with COM in .NET is to work
directly with the IDispatch interface. Here is a bit of code that I use
in one of my applications. The code listed below are 3 separate .cs
files. I also did not list my full source so not all functionality is
available in what I'm giving you but it should be enough to give you an
idea as to what's involved.

Once you have the code in place, you can use it something like:

Jason Newell
www.jasonnewell.net


/* Example Usage */
ComObject comObject = new
ComObject(Marshal.GetActiveObject("Word.Applicatio n"));
string[] propertyNames = comObject.GetPropertyNames();
foreach (string propertyName in propertyNames)
{
object property =
comObject.WrappedComObject.GetType().InvokeMember( propertyName,
System.Reflection.BindingFlags.GetProperty, null,
comObject.WrappedComObject, null);
}



/* IDispatch.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
[Guid("00020400-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown )]
public interface IDispatch
{
int GetTypeInfoCount();
System.Runtime.InteropServices.ComTypes.ITypeInfo
GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo,
[MarshalAs(UnmanagedType.U4)] int lcid);
[PreserveSig]
int GetIDsOfNames(ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
string[] rgsNames, int cNames, int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
[PreserveSig]
int Invoke(int dispIdMember, ref Guid riid,
[MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
}
}

/* ComObject.cs */
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComObject : IDisposable
{
private object _object;
private IDispatch _dispatch;
private IntPtr _pTypeAttr = IntPtr.Zero;
private System.Runtime.InteropServices.ComTypes.ITypeInfo
_typeInfo;
private string _typeName, _typeDescription, _typeHelpFile;
private int _typeHelpContext;
private SolidEdgeSpy.InteropServices.ComTypeLibrary
_comTypeLibrary;

public ComObject(object comObject)
{
System.Runtime.InteropServices.ComTypes.ITypeLib ppTLB = null;
int pIndex = 0;

_dispatch = comObject as IDispatch;

if (_dispatch != null)
{
_object = comObject;
_typeInfo = _dispatch.GetTypeInfo(0, 0);
_typeInfo.GetTypeAttr(out _pTypeAttr);
_typeInfo.GetDocumentation(-1, out _typeName, out
_typeDescription, out _typeHelpContext, out _typeHelpFile);
_typeInfo.GetContainingTypeLib(out ppTLB, out pIndex);
_comTypeLibrary = new ComTypeLibrary(ppTLB);
}
else
{
throw new InvalidComObjectException();
}
}

~ComObject()
{
Dispose();
}

public void Dispose()
{
try
{
if (_typeInfo != null)
{
_typeInfo.ReleaseTypeAttr(_pTypeAttr);
}

if (_object != null) {
Marshal.ReleaseComObject(_object); _object = null; }
if (_dispatch != null) {
Marshal.ReleaseComObject(_dispatch); _dispatch = null; }
}
catch
{
}
}

public string[] GetPropertyNames()
{
System.Collections.ArrayList list = new
System.Collections.ArrayList();

try
{
for (int i = 0; i < this.TypeAttr.cFuncs; i++)
{
IntPtr pFuncDesc = IntPtr.Zero;
System.Runtime.InteropServices.ComTypes.FUNCDESC
funcDesc;
string strName, strDocString, strHelpFile;
int dwHelpContext;

_typeInfo.GetFuncDesc(i, out pFuncDesc);
funcDesc =
(System.Runtime.InteropServices.ComTypes.FUNCDESC) Marshal.PtrToStructure(pFuncDesc,
typeof(System.Runtime.InteropServices.ComTypes.FUN CDESC));

switch (funcDesc.invkind)
{
case
System.Runtime.InteropServices.ComTypes.INVOKEKIND .INVOKE_PROPERTYGET:
_typeInfo.GetDocumentation(funcDesc.memid,
out strName, out strDocString, out dwHelpContext, out strHelpFile);
list.Add(strName);
break;
}
}
}
catch (System.Exception ex)
{
throw ex;
}

return (string[])list.ToArray(typeof(string));
}

public string TypeName { get { return this._typeName; } }
public string TypeFullName { get { return
this.ComTypeLibrary.Name + "." + this._typeName; } }
public string TypeDescription { get { return
this._typeDescription; } }
public int TypeHelpContext { get { return
this._typeHelpContext; } }
public string TypeHelpFile { get { return this._typeHelpFile; } }
public object WrappedComObject { get { return _dispatch; } }
public ComTypeLibrary ComTypeLibrary { get { return
_comTypeLibrary; } }
public System.Runtime.InteropServices.ComTypes.TYPEATTR
TypeAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPEATTR) Marshal.PtrToStructure(_pTypeAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP EATTR)); } }
public string TypeVersion
{
get
{
string version = String.Empty;
try
{
version = TypeAttr.wMajorVerNum.ToString() + "." +
TypeAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}
}
}

/* ComTypeLibrary.cs */
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace SolidEdgeSpy.InteropServices
{
public class ComTypeLibrary
{
private System.Runtime.InteropServices.ComTypes.ITypeLib _typeLib;
IntPtr _pTypeLibAttr = IntPtr.Zero;
string _Name, _Description, _HelpFile;
int _HelpContext;

public
ComTypeLibrary(System.Runtime.InteropServices.ComT ypes.ITypeLib typeLib)
{
_typeLib = typeLib;
_typeLib.GetLibAttr(out _pTypeLibAttr);
_typeLib.GetDocumentation(-1, out _Name, out _Description,
out _HelpContext, out _HelpFile);
}

public string Name { get { return this._Name; } }
public string Description { get { return this._Description; } }
public int HelpContext { get { return this._HelpContext; } }
public string HelpFile { get { return this._HelpFile; } }
public System.Runtime.InteropServices.ComTypes.TYPELIBATT R
TypeLibAttr { get { return
(System.Runtime.InteropServices.ComTypes.TYPELIBAT TR)Marshal.PtrToStructure(_pTypeLibAttr,
typeof(System.Runtime.InteropServices.ComTypes.TYP ELIBATTR)); } }
public string TypeLibVersion
{
get
{
string version = String.Empty;
try
{
version = TypeLibAttr.wMajorVerNum.ToString() + "."
+ TypeLibAttr.wMinorVerNum.ToString();
}
catch
{
}
return version;
}
}

public Version Version
{
get
{
return new Version(TypeLibAttr.wMajorVerNum,
TypeLibAttr.wMinorVerNum, 0, 0);

Reply With Quote
Reply




Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Powered by vBulletin Version 3.5.4
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.