枚举COM的成员信息的方法,比较多可以从类型库(TLB)中获取,也可以从COM实例自身去获取,一般来说任何COM对象都必须实现“IDispatch”接口,而此接口规定了获取当前实例的类型信息的接口,它是编译器强制性实现的,即开发人员编写一个COM类并且正确编译,那么在编译过程中编译器会对这个COM类构建它的类型信息到库中,当然这个主要是C/C++ ATL开发中,dotNET与此类似,所以不存在这个类型信息丢失无法访问的问题(当然不排除恶意的改变了实例虚函数链表的问题)。
本文代码仅仅只是初略的获取一个COM实例的“参数类型、返回值类型、成员备注”等几个一般性信息,所以不需要利用“ITypeInfo2”接口来获取,例如你期望在dotNET完成一个反射所需的“TypeBuilder”的实现且与COM调用与之相连,用于增强dotNET系统默认的TypeBuilder(辅助你所应用代码块的类型调用绑定),那么你并不一定需要获取很复杂的COM类型信息,记住一点获取的信息越加复杂那么效率也就越来越慢,这是一个很形象的问题,正如dotNET框架的TypeBuilder也仅仅只是涉及到了某个友元函数的参数类型与PARAMFLAG上面而已,PARAMFLAG_FOPT 标志指改参数“可空或可选”。
ITypeInfo/ITypeInfo2接口中定义的COM的类型信息,它并不是足够直观,即定义的相关的接口与结构有些容易令人混淆,比较关键的一点就是说例如你需求获取某个成员的信息,它在定义的过程中是以index表示的,但真实的情况下它需求memid(类型库成员编号)。
namespace TECHMARS.VM
{
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using ELEMDESC = System.Runtime.InteropServices.ComTypes.ELEMDESC;
using FUNCDESC = System.Runtime.InteropServices.ComTypes.FUNCDESC;
using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR;
unsafe class Program
{
[ComImport, Guid("00020400-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDispatch
{
void Reserved();
[PreserveSig]
int GetTypeInfo(uint nInfo, int lcid, [MarshalAs(UnmanagedType.Interface)]out ITypeInfo info);
}
[MTAThread]
static void Main(string[] args)
{
IDispatch dispatch = Activator.CreateInstance(Type.GetTypeFromProgID("MSXML2.XMLHTTP")) as IDispatch;
ITypeInfo info;
if (dispatch.GetTypeInfo(0, 0, out info) == 0)
{
IntPtr p = IntPtr.Zero;
info.GetTypeAttr(out p);
TYPEATTR pTA = (TYPEATTR)Marshal.PtrToStructure(p, typeof(TYPEATTR));
info.ReleaseTypeAttr(p);
for (int i = 0; i < pTA.cFuncs; i++)
{
info.GetFuncDesc(i, out p);
FUNCDESC pFD = (FUNCDESC)Marshal.PtrToStructure(p, typeof(FUNCDESC));
info.ReleaseFuncDesc(p);
string bstrName;
string bstrDocString;
int dwHelpContext;
string bstrHelpFile;
info.GetDocumentation(pFD.memid, out bstrName, out bstrDocString, out dwHelpContext, out bstrHelpFile);
int vtReturnType = pFD.elemdescFunc.tdesc.vt;
string bstrContents = string.Format("Name={0}, Comment={1}, HelpId={2}, HelpFile={3}, ReturnType={4}, ParamCount={5},", bstrName, bstrDocString,
dwHelpContext, bstrHelpFile, vtReturnType, pFD.cParams);
for (int j = 0; j < pFD.cParams; j++)
{
ELEMDESC* pED = (ELEMDESC*)pFD.lprgelemdescParam;
bstrContents += string.Format(" Slot={0}({1}),", pED[j].tdesc.vt, j);
}
Console.WriteLine(bstrContents);
}
}
}
}
}