Практическое руководство. Маршал внедренных указателей с помощью P/Invoke
Функции, реализованные в неуправляемых библиотеках DLL, можно вызывать из управляемого кода с помощью функций P/Invoke. Если исходный код библиотеки DLL недоступен, P/Invoke является единственным вариантом взаимодействия. Однако, в отличие от других языков .NET, Visual C++ предоставляет альтернативу P/Invoke. Дополнительные сведения см. в разделе "Использование взаимодействия C++ (неявное P/Invoke) и практическое руководство. Маршал внедренных указателей с помощью взаимодействия C++.
Пример
Передача структур в машинный код требует создания управляемой структуры, эквивалентной с точки зрения макета данных в собственной структуре. Однако структуры, содержащие указатели, требуют специальной обработки. Для каждого внедренного указателя в собственной структуре управляемая версия структуры должна содержать экземпляр IntPtr типа. Кроме того, память для этих экземпляров должна быть явно выделена, инициализирована и освобождена с помощью AllocCoTaskMemметодов и StructureToPtrFreeCoTaskMem методов.
Следующий код состоит из неуправляемого и управляемого модуля. Неуправляемый модуль — это библиотека DLL, которая определяет функцию, которая принимает структуру ListString
, которая содержит указатель и функцию TakesListStruct
.
// TraditionalDll6.cpp
// compile with: /EHsc /LD
#include <stdio.h>
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
#pragma pack(push, 8)
struct ListStruct {
int count;
double* item;
};
#pragma pack(pop)
extern "C" {
TRADITIONALDLL_API void TakesListStruct(ListStruct);
}
void TakesListStruct(ListStruct list) {
printf_s("[unmanaged] count = %d\n", list.count);
for (int i=0; i<list.count; i++)
printf_s("array[%d] = %f\n", i, list.item[i]);
}
Управляемый модуль — это приложение командной строки, которое импортирует TakesListStruct
функцию и определяет структуру MListStruct
, которая эквивалентна собственному ListStruct
, за исключением того, что double*
он представлен экземпляром IntPtr . Перед вызовом TakesListStruct
main
функция выделяет и инициализирует память, на которую ссылается это поле.
// EmbeddedPointerMarshalling.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MListStruct {
int count;
IntPtr item;
};
value struct TraditionalDLL {
[DllImport("TraditionalDLL6.dll")]
static public void TakesListStruct(MListStruct);
};
int main() {
array<double>^ parray = gcnew array<double>(10);
Console::WriteLine("[managed] count = {0}", parray->Length);
Random^ r = gcnew Random();
for (int i=0; i<parray->Length; i++) {
parray[i] = r->NextDouble() * 100.0;
Console::WriteLine("array[{0}] = {1}", i, parray[i]);
}
int size = Marshal::SizeOf(double::typeid);
MListStruct list;
list.count = parray->Length;
list.item = Marshal::AllocCoTaskMem(size * parray->Length);
for (int i=0; i<parray->Length; i++) {
IntPtr t = IntPtr(list.item.ToInt32() + i * size);
Marshal::StructureToPtr(parray[i], t, false);
}
TraditionalDLL::TakesListStruct( list );
Marshal::FreeCoTaskMem(list.item);
}
Часть библиотеки DLL не предоставляется управляемому коду с помощью традиционной #include
директивы. На самом деле библиотека DLL доступна только во время выполнения, поэтому проблемы в функциях, импортированных с помощью, DllImportAttribute не могут быть обнаружены во время компиляции.
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по