в моем следующем проекте я хочу реализовать графический интерфейс для уже существующего кода на C++. Мой план состоит в том, чтобы обернуть часть C++ в DLL и реализовать графический интерфейс на C#. Моя проблема в том, что я не знаю, как реализовать обратный вызов из неуправляемой DLL в управляемый код C#. Я уже занимался разработкой на C#, но интерфейс между управляемым и неуправляемым кодом для меня в новинку. Может ли кто-нибудь дать мне несколько советов или советов по чтению или простой пример, с которого можно начать? К сожалению, я не мог найти ничего полезного.
Как реализовать интерфейс обратного вызова из неуправляемой DLL в приложение .net?
Ответы (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. Достойный учебник доступен здесь.
P/Invoke может обрабатывать маршалинг управляемого делегата к указателю на функцию. Поэтому, если вы предоставляете API, которые регистрируют функцию обратного вызова из вашей DLL, а в C# передайте делегата этой функции.
В MSDN есть пример этого с помощью функции EnumWindows. В этой статье обратите внимание на строку в пункте 4, в которой говорится:
Однако, если функция обратного вызова может быть вызвана после завершения вызова, управляемый вызывающий объект должен предпринять шаги, чтобы убедиться, что делегат остается несобранным до завершения функции обратного вызова. Подробные сведения о предотвращении сборки мусора см. в разделе Маршалинг взаимодействия с вызовом платформы.
Это говорит о том, что вам нужно убедиться, что ваш делегат не будет собран мусором до тех пор, пока управляемый код не вызовет его, либо сохранив ссылку на него в своем коде, либо закрепив его.
См. Marshal.GetFunctionPointerForDelegate, который даст вам указатель функции для вызова управляемого (т.е. кода C#) из неуправляемого кода.