为接口中的所有成员检索隐式分配的 dispId 值

Posted

技术标签:

【中文标题】为接口中的所有成员检索隐式分配的 dispId 值【英文标题】:retrieve implicitly assigned dispId values for all members in an interface 【发布时间】:2021-08-16 01:34:17 【问题描述】:

给定一个具有 ComVisible=true 的 c# 程序集,您将如何检索类型中隐式分配的 dispId 的列表?我发现我们可以使用反射或反汇编器来获得显式定义的 dispId,但是隐式的呢?

总体目标是获取旧版本程序集的隐式 dispId,以便我们可以在所有未来版本中明确定义它们,而不会破坏与现有使用者的兼容性。

【问题讨论】:

由于知道成员名称,您可以查询接口对象的 IDispatch,然后使用其名称为每个成员调用 IDispatch::GetIDsOfNames docs.microsoft.com/en-us/windows/win32/api/oaidl/…。 @SimonMourier 不幸的是,ComVisible=true 标志似乎不会自动将 IDispatch 接口添加到公开的类型。如果我对类型使用反射并运行 GetInterfaces(),我只会看到在源代码中明确设置的类型。 您不能为此使用纯 .NET 反射。 【参考方案1】:

这里有一些 C# 示例代码,可让您执行此操作(使用 C/C++ 更容易,因为 IDispatch 在 .NET 中非常特殊):

// get the object type from progid (or use other means)
var type = Type.GetTypeFromProgID("ClassLibrary.MyClass");
var obj = Activator.CreateInstance(type);

// sadly, we can't cast to a redefined p/invoke IDispatch of ours because its a .NET COM object
// and IDispatch has a very special treatment
// so we use the vtable directly
var dispPtr = Marshal.GetIDispatchForObject(obj);
var vtablePtr = Marshal.ReadIntPtr(dispPtr);
var methodPtr = Marshal.ReadIntPtr(vtablePtr + 5 * IntPtr.Size); // GetIDsOfNames is 5 methods away from IUnknown first method)
var getIDsOfNames = Marshal.GetDelegateForFunctionPointer<GetIDsOfNames>(methodPtr);

// ask for the dispid of "MyMethod" member
var dispid = new int[1];
var hr = getIDsOfNames(dispPtr, Guid.Empty, new string[]  "MyMethod" , 1, 0, dispid);
Marshal.ThrowExceptionForHR(hr); // throw if not found

...

public delegate int GetIDsOfNames(
    IntPtr pThis,
    [MarshalAs(UnmanagedType.LPStruct)] Guid riid,
    [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] string[] rgszNames,
    int cNames,
    int lcid,
    int[] rgDispId);

【讨论】:

以上是关于为接口中的所有成员检索隐式分配的 dispId 值的主要内容,如果未能解决你的问题,请参考以下文章

函数式接口的相关概念以及常用的函数式接口(SupplierConsumerPredicateFunction)

在 C# 中实现 DISPID_VALUE 并从 C++ 中调用它

java成员变量和局部变量的初始化和内存中的运行机制

什么是显式接口成员实现以及为什么[重复]

IE BHO - 为页面加载调用 DISPID_FILEDOWNLOAD?

如何从 .NET 对 .NET 中定义的 COM 对象执行后期绑定调用(通过 DispID)?