Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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();
}

И соответствующий код С++, используемый для создания неуправляемой библиотеки 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");
}

Достаточно того, чтобы вы начали с этого. Есть миллион деталей, которые могут вызвать у вас неприятности, вы обязательно столкнетесь с некоторыми из них. Более эффективный способ получить этот тип кода - писать обертку на языке С++/CLI. Это также позволяет вам обернуть класс С++, что вы не можете сделать с P/Invoke. Приятный учебник доступен здесь.

Ответ 2

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

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

Если, однако, функция обратного вызова может вызывается после возврата вызова, управляемый звонящий должен предпринять шаги для обеспечить, чтобы делегат оставался не выбрано до обратного вызова функция заканчивается. Для подробного информация о предотвращении мусора коллекции, см. Interop Marshaling с вызовом платформы.

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

Ответ 3

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