방법: P/Invoke를 사용하여 함수 포인터 마샬링
관리되는 대리자는 .NET Framework P/Invoke 기능을 사용하여 관리되지 않는 함수와 상호 운용할 때 함수 포인터 대신 사용할 수 있습니다. 그러나 가능하면 C++ Interop 기능을 대신 사용하는 것이 좋습니다. P/Invoke는 컴파일 시간 오류 보고를 거의 제공하지 않으며 형식이 안전하지 않으며 구현하는 데 지루할 수 있습니다. 관리되지 않는 API가 DLL로 패키지되고 소스 코드를 사용할 수 없는 경우 P/Invoke가 유일한 옵션입니다. 그렇지 않으면 다음 문서를 참조하세요.
함수 포인터를 인수로 사용하는 관리되지 않는 API는 네이티브 함수 포인터 대신 관리되는 대리자를 사용하여 관리 코드에서 호출할 수 있습니다. 컴파일러는 자동으로 대리자를 관리되지 않는 함수에 함수 포인터로 마샬링합니다. 필요한 관리/관리되지 않는 전환 코드를 삽입합니다.
예시
다음 코드는 관리되지 않는 모듈과 관리되는 모듈로 구성됩니다. 관리되지 않는 모듈은 함수 포인터를 허용하는 함수를 TakesCallback
정의하는 DLL입니다. 이 주소는 함수를 실행하는 데 사용됩니다.
// TraditionalDll5.cpp
// compile with: /LD /EHsc
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
extern "C" {
/* Declare an unmanaged function type that takes two int arguments
Note the use of __stdcall for compatibility with managed code */
typedef int (__stdcall *CALLBACK)(int);
TRADITIONALDLL_API int TakesCallback(CALLBACK fp, int);
}
int TakesCallback(CALLBACK fp, int n) {
printf_s("[unmanaged] got callback address, calling it...\n");
return fp(n);
}
관리되는 모듈은 네이티브 코드에 마샬링되는 대리자를 함수 포인터로 정의합니다. 이 특성은 네이 DllImportAttribute 티브 TakesCallback
함수를 관리 코드에 노출하는 데 사용됩니다. 함수에서 main
대리자의 인스턴스가 만들어지고 함수에 TakesCallback
전달됩니다. 프로그램 출력은 이 함수가 네이티브 TakesCallback
함수에 의해 실행됨을 보여 줍니다.
관리되는 함수는 네이티브 함수가 실행되는 동안 .NET Framework 가비지 수집이 대리자를 재배치하지 못하도록 관리되는 대리자의 가비지 수집을 표시하지 않습니다.
// MarshalDelegate.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
public delegate int GetTheAnswerDelegate(int);
public value struct TraditionalDLL {
[DllImport("TraditionalDLL5.dll")]
static public int TakesCallback(GetTheAnswerDelegate^ pfn, int n);
};
int GetNumber(int n) {
Console::WriteLine("[managed] callback!");
static int x = 0;
++x;
return x + n;
}
int main() {
GetTheAnswerDelegate^ fp = gcnew GetTheAnswerDelegate(GetNumber);
pin_ptr<GetTheAnswerDelegate^> pp = &fp;
Console::WriteLine("[managed] sending delegate as callback...");
int answer = TraditionalDLL::TakesCallback(fp, 42);
}
기존 #include
지시문을 사용하여 관리 코드에 DLL 부분이 노출되지 않습니다. 실제로 DLL은 런타임에만 액세스되므로 컴파일 시간에 사용하여 DllImportAttribute 가져온 함수에 대한 문제를 검색할 수 없습니다.