All recipes for using COM include creating a type library, registering it in the registry, registering it in the server registry ...

In this case, a DLL must create a factory of objects.

If there is an executable file in C # and a DLL in C ++ - can you do without all these steps and just load the dll-ku ?

  • What made you ask a question and answer it right now? - nick_n_a
  • 2
    @nick_n_a the fact that information on this topic in the internet is very hard to find and filled with harmful tips and myths. - Pavel Mayorov
  • It is possible and without COM habrahabr.ru/post/304482 - Serginio
  • @Serginio is an excellent example of harmful advice ... Look at the amount of code in the article. - Pavel Mayorov
  • @ pavel-mayorov Well, but there is an example of interaction between the code managed and Unmanaged - Serginio

3 answers 3

Yes you can. COM technology does not require any registration in the registry for its use!

All the steps listed in the question are needed to reduce the connectivity of the modules. If these steps do not suit you, are not needed, or you are just too lazy to do them - you can skip them.

Here is a working minimal example.

C #

using System; using System.Runtime.InteropServices; [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface ICallback { void execute(); } class Program : ICallback { static void Main(string[] args) { SetCallbacks(new Program()); } [DllImport("mylib", CallingConvention=CallingConvention.StdCall)] static extern void SetCallbacks(ICallback callback); void ICallback.execute() { Console.WriteLine("Hello, world!"); } } 

C ++

 #include "windows.h" interface ICallback : IUnknown { virtual HRESULT __stdcall execute() = 0; }; extern "C" __declspec(dllexport) void __stdcall SetCallbacks(ICallback *cb) { cb->execute(); } 

In the example above, the interfaces in two languages ​​were compiled independently. In principle, this is a double work - therefore it makes sense to create an interface only 1 time, after which it is imported.

If the interface is compiled in a C ++ project in IDL, then it is sufficient to connect the type library to the C # project as a dependency. It is not necessary to register it!

If the interface is compiled in a C # project, then it is imperative to put it in addition to the Guid attribute listed above and make it public. After that, you can pull out the type library via tlbexp and connect it to a C ++ project. Register the type library in the registry, again, is not required.

  • By the way, here's another example of calling the object method github.com/Marqin/simpleCoreCLRHost/blob/master/Managed.cs - Serginio
  • @Serginio One method can be called this way. But a dozen is easier to call through the interface. In any case, the question is not about that. - Pavel Mayorov 2:43
  • It will be necessary to try for .Net Core a call of virtual methods C ++ - Serginio
  • Interested in calling virtual C ++ classes from managed code - Serginio

I’ll add the ability to interact with C ++ code with .Net Core. In .Net Core it is possible to call static .Net methods through the use of coreclr.dll from native. View examples and links here. Cross-platform use of .Net classes from unmanaged code. Or analogue IDispatch on Linux

As a result, you can pass native methods to managed code and vice versa. But I was interested in using objects and virtual methods. Therefore, I asked a question and got an answer .Net Core Calling virtual methods of native objects At the same time, the method call goes to a bunch of QueryInterface, AddRef and Release.

I will give my example.

We describe the class in C ++

 struct ICallback : IUnknown { public: // Унаследовано через IUnknown virtual HRESULT __stdcall QueryInterface(REFIID riid, void ** ppvObject) override; virtual ULONG __stdcall AddRef(void) override; virtual ULONG __stdcall Release(void) override; virtual HRESULT __stdcall execute(int value); }; typedef void(STDMETHODCALLTYPE *ManagedRunCallback)(ICallback*); 

implementation

  // {FFB46654-083E-486A-94B8-E28B5C01561D} static const GUID IID_ICallback = { 0xffb46654, 0x83e, 0x486a,{ 0x94, 0xb8, 0xe2, 0x8b, 0x5c, 0x1, 0x56, 0x1d } }; HRESULT __stdcall ICallback::execute(int value) { wprintf_s(L"ICallback from.Net %d\n", value); return NOERROR; } HRESULT __stdcall ICallback::QueryInterface(REFIID riid, void ** ppvObject) { if (!ppvObject) return E_INVALIDARG; if (riid == IID_IUnknown) { *ppvObject = static_cast<IUnknown*>(this); return S_OK; } else if (riid == IID_ICallback) { *ppvObject = static_cast<ICallback*>(this); return S_OK; } *ppvObject = nullptr; return E_NOINTERFACE; } // Меня интересует только вызов виртуальных методов // без отслеживания подсчета ссылок ULONG __stdcall ICallback::AddRef(void) { return 1; } ULONG __stdcall ICallback::Release(void) { return 0; } 

now the description in C #

  [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("FFB46654-083E-486A-94B8-E28B5C01561D")] public interface ICallback { void execute(int value); } 

and static method

  public static void CallInterface(IntPtr cb) { var cb2 = Marshal.GetObjectForIUnknown(cb) as ICallback; cb2?.execute(555); } 

I can now call the .Net method from C ++

 ManagedRunCallback pRunCallback; if (!CreateDelegate(domainId, L"CallInterface", (INT_PTR*)&pRunCallback)) return false; ICallback* cb = new ICallback(); pRunCallback(cb); 
  • Why duplicate inherited methods in an interface? - Pavel Mayorov
  • They are marked as abstract. And this interface is not a class. And they are marked as override - Serginio Nov.
  • one
    Uh ... Why does the class start with the prefix I ? :) - Pavel Mayorov
  • This is because I am still the C ++ programmer. There are no interfaces as such. There are classes (structures) with abstract methods - Serginio

Add another option using VMT in .Net Core. Calling the interface is very expensive. With for coercion to the necessary interface and reference counting there is a heap of calls QueryInterface Addref, Release Calling interface methods from C #

Define C ++ class

 struct TestThisCall { public: virtual int execute(int value1, int value2); }; typedef void(STDMETHODCALLTYPE *ManagedRunCallback2)(TestThisCall*); 

Create a method to get the method in VMT

 public static IntPtr ПолучитьАдресВиртуальногоМетода(IntPtr Объект, int ИндексВТаблицеВиртуальныхМетодов ) { int размерIntPtr = Marshal.SizeOf<IntPtr>(); // Первым полем объекта идет ссылка на VMT // Прочитаем её var АдресVMT = Marshal.ReadIntPtr(Объект); // получим адрес ссылки на метод по смещению в VMT var АдресМетодаVMT = АдресVMT + ИндексВТаблицеВиртуальныхМетодов * размерIntPtr; var АдресМетода = Marshal.ReadIntPtr(АдресМетодаVMT); return АдресМетода; } 

Now define the delegate description.

  [UnmanagedFunctionPointer(CallingConvention.ThisCall)] internal delegate int ВиртуальныйМетодОбъекта2Delegate(IntPtr self, int Число1, int Число2); 

And the static method that we will call from the native

 public static void CallTestThisCall(IntPtr ttc) { // Метод execute идет первым в VMT // Передаем индекс 0 var АдресМетода = ПолучитьАдресВиртуальногоМетода(ttc, 0); // Получим делегат по дресу var execute = Marshal.GetDelegateForFunctionPointer<ВиртуальныйМетодОбъекта2Delegate>(АдресМетода); // И вызовем метод var res= execute(ttc, ttc.ToInt32(), 777); execute(ttc, ttc.ToInt32(), res); } 

Now call this method from C ++.

 ManagedRunCallback2 pRunCallback2; if (!CreateDelegate(domainId, L"CallTestThisCall", (INT_PTR*)&pRunCallback2)) return false; TestThisCall* ttc = new TestThisCall(); pRunCallback2(ttc); 

Well, and a wonderful example on RSDN .Net Core Calling virtual methods of native objects

  • Why would it be expensive, pleasure? .. - Pavel Mayorov
  • I brought the link. There's a bunch of QueryInterface, Addref, Release coming. In my tasks, you need to call one two methods without counting links, etc. - Serginio