返回的托管对象方法未从 COM 互操作中的C++调用
本文关键字:互操作 COM C++ 调用 对象 方法 返回 | 更新日期: 2023-09-27 18:36:11
这是我上一篇文章的后续文章。 阅读该帖子以了解上下文。请注意,它不是严格的 COM 互操作 - 但C++接口与 COM 兼容。
我试图在 C# 中实现这个C++接口
class IPluginFactory : public FUnknown
{
virtual tresult PLUGIN_API createInstance (FIDString cid, FIDString iid, void** obj) = 0;
};
我的 C# 代码如下所示:
[ComImport]
[Guid(Interfaces.IPluginFactory)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginFactory
{
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 CreateInstance([In] ref Guid classId, [In] ref Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown), In, Out] ref object instance);
}
该实现将一个新的对象实例分配给"instance"参数并返回 0 (S_OK)。我什至投射到预期的(托管)界面。
instance = (IPluginBase)new PluginBase();
return 0;
返回的对象由以下C++接口表示:
class IPluginBase: public FUnknown
{
public:
virtual tresult PLUGIN_API initialize (FUnknown* context) = 0;
virtual tresult PLUGIN_API terminate () = 0;
};
在我的 C# 实现中如下所示:
[ComImport]
[Guid(Interfaces.IPluginBase)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginBase
{
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 Initialize([MarshalAs(UnmanagedType.IUnknown), In] object context);
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 Terminate();
}
在我编写的非托管C++测试应用程序中,我可以成功调用 createInstance 并接收代码用作 IPluginBase* 的非空指针。
当我尝试在此 IPluginBase* 指针上调用"初始化"方法时,问题就来了。它永远不会到达托管代码(未命中断点 - 其他断点工作正常),返回代码0x80004003。看起来包装器在这里做了一些拦截...
我的问题是:创建实例的托管表示形式的声明是否正确?我在这里错过了什么?(这应该是普通的香草互操作,不是吗?
也欢迎对声明"风格"提出其他建议。感谢,马克。
编辑:问题似乎出在createInstance方法上。我无法获得 iid 参数要求的返回接口。
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
Int32 CreateInstance([In] ref Guid classId, [In] ref Guid interfaceId, [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 1), In, Out] ref object instance);
我也尝试过UnmanagedType.Interface与IidParameterIndex的组合,但两者都会导致IUnknown被封送回来。如果我重新查询 IPluginBase 接口,则 IPluginBase::initialize 方法有效(托管代码命中中的断点)。
编辑:问题似乎出在createInstance方法上。我是 无法获取 IID 请求的返回接口 参数。
在此处指定IidParameterIndex
无济于事。createInstance
的实现应如下所示:
public int createInstance(ref Guid classId, ref Guid riid, ref IntPtr instance)
{
if (instance != IntPtr.Zero)
return E_POINTER;
// substitute your actual object creation code for CreateObject
object obj = CreateObject(classId)
// return the correct interface
IntPtr unk = Marshal.GetIUnknownForObject(obj);
try
{
return Marshal.QueryInterface(unk, ref riid, out instance);
}
finally
{
Marshal.Release(unk);
}
}