Как реализовать интерфейс обратного вызова из неуправляемой DLL в приложение .net?

в моем следующем проекте я хочу реализовать графический интерфейс для уже существующего кода на C++. Мой план состоит в том, чтобы обернуть часть C++ в DLL и реализовать графический интерфейс на C#. Моя проблема в том, что я не знаю, как реализовать обратный вызов из неуправляемой DLL в управляемый код C#. Я уже занимался разработкой на C#, но интерфейс между управляемым и неуправляемым кодом для меня в новинку. Может ли кто-нибудь дать мне несколько советов или советов по чтению или простой пример, с которого можно начать? К сожалению, я не мог найти ничего полезного.


person chrmue    schedule 30.01.2010    source источник


Ответы (4)


Вам не нужно использовать Marshal.GetFunctionPointerForDelegate(), маршаллер P/Invoke делает это автоматически. Вам потребуется объявить делегата на стороне C#, сигнатура которого совместима с объявлением указателя функции на стороне C++. Например:

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

class UnManagedInterop {
  private delegate int Callback(string text);
  private Callback mInstance;   // Ensure it doesn't get garbage collected

  public UnManagedInterop() {
    mInstance = new Callback(Handler);
    SetCallback(mInstance);
  }
  public void Test() {
    TestCallback();
  }

  private int Handler(string text) {
    // Do something...
    Console.WriteLine(text);
    return 42;
  }
  [DllImport("cpptemp1.dll")]
  private static extern void SetCallback(Callback fn);
  [DllImport("cpptemp1.dll")]
  private static extern void TestCallback();
}

И соответствующий код C++, используемый для создания неуправляемой DLL:

#include "stdafx.h"

typedef int (__stdcall * Callback)(const char* text);

Callback Handler = 0;

extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
  Handler = handler;
}

extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
  int retval = Handler("hello world");
}

Этого достаточно, чтобы вы начали с ним. Есть миллион деталей, которые могут доставить вам неприятности, вы обязательно столкнетесь с некоторыми из них. Гораздо более продуктивный способ заставить работать такой код — написать оболочку на языке C++/CLI. Это также позволяет обернуть класс C++, чего нельзя сделать с помощью P/Invoke. Достойный учебник доступен здесь.

person Hans Passant    schedule 30.01.2010
comment
Спасибо, nobugz, скоро попробую. И я надеюсь, что у меня не будет особых проблем ;-) - person chrmue; 30.01.2010

P/Invoke может обрабатывать маршалинг управляемого делегата к указателю на функцию. Поэтому, если вы предоставляете API, которые регистрируют функцию обратного вызова из вашей DLL, а в C# передайте делегата этой функции.

В MSDN есть пример этого с помощью функции EnumWindows. В этой статье обратите внимание на строку в пункте 4, в которой говорится:

Однако, если функция обратного вызова может быть вызвана после завершения вызова, управляемый вызывающий объект должен предпринять шаги, чтобы убедиться, что делегат остается несобранным до завершения функции обратного вызова. Подробные сведения о предотвращении сборки мусора см. в разделе Маршалинг взаимодействия с вызовом платформы.

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

person shf301    schedule 30.01.2010
comment
+1 для критического - использование только P/Invoke гарантирует действительность указателя только во время вызова - если вы его кешируете, вам нужно его закрепить (не могу говорить о скрытии ссылки, я подозреваю, но это не это много значит :-) - person Mark Mullin; 09.01.2013

См. Marshal.GetFunctionPointerForDelegate, который даст вам указатель функции для вызова управляемого (т.е. кода C#) из неуправляемого кода.

person Håvard S    schedule 30.01.2010