Передача указателя на указатель COM-интерфейса из управляемого C++ в.NET C#

У меня есть COM Dll, написанный на неуправляемом C++, который предоставляет интерфейс com. Тривиальный пример idl показан ниже:

import "oaidl.idl";
import "ocidl.idl";

[
    object,
    uuid(A806FAED-FCE2-4F1B-AE67-4B36D398508E),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IObjectToPass : IDispatch{
    [propget, id(1)] HRESULT Name([out, retval] BSTR* pVal);
    [propput, id(1)] HRESULT Name([in] BSTR newVal);
};
[
    uuid(B99537C0-BEBC-4670-A77E-06AB5350DC23),
    version(1.0),
]
library     {
    importlib("stdole2.tlb");
    [
        uuid(FB7C7D08-0E38-40D2-A160-0FC8AE76C3CF)      
    ]
    coclass ObjectToPass
    {
    [default] interface IObjectToPass;
    };
}; 

поэтому интерфейс IObjectToPass является интерфейсом, выставленным. Теперь я реализовал библиотеку.NET (ObjectConsumer) в C#, которая импортирует через Interop, созданный этим COM-объектом, tlbimp, чтобы я мог использовать интерфейс IObjectToPass. Вот ниже пример кода этой библиотеки:

using Interop.ComTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;ComTestLib

using System.Threading.Tasks;

namespace ObjectConsumer 
{
    public class Consumer
    {
        public void PassObject(IObjectToPass passobj)
        {
            string str = passobj.Name;
        }
    }
}

модуль C++ и C# компилируется без проблем

Теперь я создал простое консольное приложение в C++, которое поддерживает CLR, которое импортирует ComTestLib через импорт, а Consumer добавляет ссылку на проект консольного приложения. Моя цель - получить экземпляр ObjectToPass внутри моего консольного приложения и передать его потребителю, вызвав открытый метод PassObject, как показано ниже.

#include "stdafx.h"
#import "..\Debug\ComTest.tlb" no_namespace, raw_interfaces_only  

int main()
{
    CoInitialize(NULL);
    IObjectToPassPtr obj;

    HRESULT hr = obj.CreateInstance(__uuidof(IObjectToPass) );
    ObjectConsumer::Consumer^ consumer = gcnew ObjectConsumer::Consumer();
    obj->put_Name(_T("MyName"));
    consumer->PassObject(obj);

    CoUninitialize();
        return 0;
 }

Этот код не скомпилируется. Компилятор не может преобразовать аргумент 1 из 'IObjectToPassPtr' в 'Interop::ComTest::IObjectToPass ^'. Я делал разные попытки, но кажется, что IObjectToPass* не может быть приведен к Interop::ComTest::IObjectToPass^. Есть ли способ сделать это?

1 ответ

Пропустите интерфейс COM и просто экспортируйте фабричную функцию в C++.

#define MY_API __declspec(dllexport)
#ifdef __cplusplus
#   define EXTERNC    extern "C"
#else
#   define EXTERNC 
#endif

 EXTERNC MY_API IObjectToPass* APIENTRY MyFactory(void);
 EXTERNC MY_API  void APIENTRY SomeFunction(IObjectToPass* handle);

// Implementation:

MY_API IObjectToPass* APIENTRY MyFactory(void)
{
    return new ObjectToPass();
}
MY_API void APIENTRY SomeFunction(IObjectToPass* handle)
{
    handle->DoFunction();
}

// And then export any needed functions too. 

Теперь в C#:

public class MyWrapper{
IntPtr myReference;
public ObjectWrapper()
    {
        try
        {

            this.myReference = MyFactory();
            if (myReference == IntPtr.Zero)
            {

                throw "Error or something";
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            throw e;
        }

    }

    //Path to dll
    [DllImport(lib_path)]
    private static extern IntPtr MyFactory();

    [DllImport(lib_path)]
    private static extern void SomeFunction(IntPtr toTrigger);
 }

Подумайте о том, чтобы избавиться, по крайней мере, от одной из трех форм языка C, чтобы упростить дизайн, иначе то, что я предложил, работает, и я использовал его раньше!

Другие вопросы по тегам