日期:2012-01-30  浏览次数:20617 次

在.Net 中枚举COM对象的方法和属性名称

Author:Zee

恩,以前满世界问过这个问题,没有人理偶的说,还是自己动手搞定比较好。

一般来说,一个COM对象在提供的时候,通常还会提供一个类型库,在其中定义了COM对象的所有方法名称、参数名称、属性名称等等信息。我们要做的就是从类型库中取出这些信息。
当然,某些只供C++程序员使用的COM对象没有类型库,而代之以C++的头文件和/或idl文件,对这种情况,一般没有办法在程序中枚举出对象的方法属性:毕竟去找C++头文件不太现实,何况在非开发环境下,根本就没有头文件的说。
因此,我们将讨论当COM对象存在TypeLib的情况下,枚举方法/属性名称的问题。
从COM对象定位到TypeLib
在一般情况下,COM对象的TypeLib信息存储在注册表中:在HK_CLASSROOT\CLSID\{ClassID}\的注册表项下,有一个名为TypeLib的子项,其中定义了这个COM对象类型库的ID;而在HK_CLASSROOT\TypeLib 注册表项下,列举了系统中所有TypeLib。
看看我们首先要做什么:从ProgID 取得 ClassID,这个工作可以通过调用COM 基础库的 CLSIDFromProgID 函数来完成,在Platform SDK中,该函数的定义如下:
HRESULT CLSIDFromProgID(
  LPCOLESTR lpszProgID,
  LPCLSID pclsid

);
为了在.Net中使用这个函数,我们用DllImport Attribute 把这个函数引入.Net 中:
class UnsafeNativeMethods{
[DllImport("ole32.dll",CharSet=CharSet.Unicode,PreserveSig=false)]
public static extern void CLSIDFromProgID([In,MarshalAs(UnmanagedType.BStr)] string lpszProgID,[Out]out Guid pclsid);
………
然后,我们可以在.Net 中调用这个函数取得ClassID了:
Guid clsid;
UnsafeNativeMethods.CLSIDFromProgID(progID,out clsid);
OK, 升级宝物Class ID 入手,Level Up!Strength + 3, Life + 5,必杀技 dll import 习得。 :) 下一个任务:取得TypeLib。
l         取得TypeLib。
为访问TypeLib,COM 提供了二个接口:ITypeLib 和 ITypeInfo,其中ITypeLib 提供对 TypeLib 的访问,而ITypeInfo 则表示TypeLib中定义的某一项ITypeInfo。
要获得ITypeInfo,COM有二个函数可以做这件事情:LoadTypeLib 和 LoadRegTypeLib。其中 LoadTypeLib 需要 TypeLib 文件的路径作为参数,而LoadRegTypeLib 则根据TypeLib的TypeLib ID和TypeLib的版本号取得 ITypeLib。在这里,我们用LoadRegTypeLib来取得ITypeLib 接口。
先来准备需要的参数:TypeLibID和TypeLib的版本号,这些信息需要从注册表里得到:
RegistryKey regKey = Registry.ClassesRoot;
regKey = regKey.OpenSubKey("CLSID\\{" + clsid.ToString() + "}\\TypeLib");
Guid typeLibID = new Guid(regKey.GetValue("").ToString());
//Get TypeLib Versions
short iMajorVer,iMinusVer;
regKey = Microsoft.Win32.Registry.ClassesRoot;
regKey = regKey.OpenSubKey("TypeLib\\{" + typeLibID.ToString() + "}");
string[] aryTemp = regKey.GetSubKeyNames();
string sVersion = aryTemp[0];
aryTemp = sVersion.Split('.');
iMajorVer = short.Parse(aryTemp[0],System.Globalization.NumberStyles.AllowHexSpecifier);
iMinusVer = short.Parse(aryTemp[1] ,System.Globalization.NumberStyles.AllowHexSpecifier);
这里要注意一点:在注册表里记录的TypeLib版本号是以十六进制格式表示的,运气好的话,你会发现类似”1.a”之类的版本号,所以我们最好把它们看成16进制来转换。
现在可以调用LoadRegTypeLib 了,和CLSIDFromProgID一样,先import进来:
这是LoadRegTypeLib 的函数原型:
HRESULT LoadRegTypeLib( 
  REFGUID  rguid,             
  unsigned short  wVerMajor,  
  unsigned short  wVerMinor,  
  LCID  lcid,                 
  ITypeLib FAR* FAR*  pptlib  

);
恩,在.Net里,这个函数是这个样子的:
[DllImport("oleaut32.dll",CharSet=CharSet.Unicode,PreserveSig=false)]
[LCIDConversion(3)]
public static extern UCOMITypeLib LoadRegTypeLib(ref Guid rguid, [In,MarshalAs(UnmanagedType.U2)]short wVerMajor, [In,MarshalAs(UnmanagedType.U2)]short wVerMinor);
哈,一个小小的技巧:在.Net 的定义里,偶没有定义原型里 lcid 这个参数,这是因为偶应用了LCIDConversionAttribute,这个Attribute 意思就是说这个方法需要一个LCID做参数,参数的位置嘛:[LCIDConversion(3)]——第三个参数。这样在调用这个方法的时候,.Net 的封送拆收器将自动提供 LCID 参数。不错把:)
另外,在.Net Framwork的System.Runtime.InteropServices 命名空间里,定义了一些常用COM Interface的.Net托管定义,虽然不多,但幸运的是我们要用到的ITypeLib和 ITypeInfo这二个接口都有,也就是System.Runtime.InteropService.UCOMITypeLibSystem.Runtime.InteropService.UCOMITypeInfo。这就省下了我们自己定义接口的工作。
好了,接下来的事情狠简单了: