这是我previous post的后续跟进。阅读该帖子的上下文。请注意,它不是严格的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 *指针上调用'initialize'方法时出现问题。它永远不会到达托管代码(没有命中断点 - 其他断点工作正常),返回码为0x80004003。看起来包装器在这里有一些拦截......
我的问题是:CreateInstance的托管表示的声明是否正确? 我在这里错过了什么? (这应该是普通的香草互操作,如果不是吗?)
关于声明'风格'的其他建议也是受欢迎的。 Thanx,Marc。
编辑:问题似乎在于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方法可以正常工作(托管代码中的断点命中)。
答案 0 :(得分:1)
编辑:问题似乎在于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);
}
}