Возвращенный метод управляемого объекта, не вызванный из C++ в COM-взаимодействии
Это продолжение моего предыдущего поста. Прочитайте этот пост для контекста. Обратите внимание, что это не строгое 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*.
Проблема возникает, когда я пытаюсь вызвать метод initialize для этого указателя IPluginBase*. Он никогда не достигает управляемого кода (ни одна точка останова не достигнута - другие точки останова работают нормально), а код возврата - 0x80004003. Похоже, что обертка делает некоторый перехват здесь...
Мой вопрос: правильно ли объявлено управляемое представление CreateInstance? Что мне здесь не хватает? (это должно быть простое ванильное взаимодействие, не так ли?)
Другие предложения по объявлению "стиль" также приветствуются. Спасибо, Марк.
РЕДАКТИРОВАТЬ: Кажется, проблема заключается в методе 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 работает (точка останова в обращениях к управляемому коду).
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);
}
}