Возвращенный метод управляемого объекта не вызван из 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 *.

Проблема возникает, когда я пытаюсь вызвать метод инициализации для этого указателя 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 работает (точка останова при попадании в управляемый код).


person obiwanjacobi    schedule 20.10.2013    source источник
comment
Это E_POINTER, неверный указатель. Он недоволен переданным вами аргументом, не передавайте null. Трудно догадаться, что это за контекст. Обратитесь к автору этого кода за поддержкой.   -  person Hans Passant    schedule 20.10.2013
comment
Почему вы не можете передать NULL в качестве значения параметра? Можно ли использовать атрибут AllowNull? ;-) «Контекст» - это IUnknown *. Автор кода - это я, то есть определения интерфейса C ++ являются сторонним стандартом, с которым я пытаюсь взаимодействовать в .NET.   -  person obiwanjacobi    schedule 20.10.2013


Ответы (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);
    }
}
person noseratio    schedule 21.10.2013
comment
Ах, стреляй! Нет ли способа заставить это работать с объектом как с типом возврата (выхода)? Ваше решение действительно работает. Спасибо! - person obiwanjacobi; 21.10.2013
comment
Я не знаю лучшего способа, поскольку совсем недавно имел дело с аналогичная проблема. - person noseratio; 21.10.2013