Поделиться через


Практическое руководство. Маршалирование структур с помощью PInvoke

Обновлен: Ноябрь 2007

В этом разделе описывается, как вызывать неуправляемые функций, которые поддерживают строки в стиле C, из управляемых функций посредством предоставления экземпляра класса String с помощью вызова P/Invoke. В Visual C++ вместо этого рекомендуется использовать взаимодействие C++, поскольку при использовании вызова P/Invoke обнаруживается лишь малое число ошибок компиляции, не обеспечивается строгая типизация и возникают определенные сложности реализации. Если неуправляемый интерфейс API упакован в виде библиотеки DLL, а исходный код недоступен, единственным вариантом является использование вызова P/Invoke. Дополнительные сведения по любым другим случаям см. в следующих разделах:

По умолчанию неуправляемые и управляемые структуры размещаются в памяти разными способами. В связи с этим для успешного переноса структур между неуправляемым и управляемым кодом необходимо выполнить дополнительные действия по обеспечению целостности данных.

В этом разделе описывается порядок определения управляемых эквивалентов для неуправляемых структур, а также передачи полученных структур в неуправляемые функции. В этом разделе предполагается применение простых структур, не содержащих строки или указатели. Дополнительные сведения о взаимодействии с непреобразуемыми типами см. в разделе Использование взаимодействия языка C++ (неявный PInvoke).

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

Иногда для повышения производительности порядок следования членов управляемых структур изменяется. В таких случаях необходимо использовать атрибут StructLayoutAttribute, указывающий последовательную компоновку структуры. Также рекомендуется явно задавать параметры упаковки структуры, соответствующие используемым в неуправляемой структуре. (По умолчанию в Visual C++ используется 8-байтная упаковка структуры как для неуправляемого, так и для управляемого кода.)

  1. Далее вызовите метод DllImportAttribute, чтобы объявить точки входа, соответствующие любым неуправляемым функциям, в которых принимается структура. В этом случае в сигнатурах функций необходимо использовать управляемые версии структур, что может привести к противоречиям при использовании одинаковых имен для обоих версий структуры.

  2. Теперь в управляемом коде реализована передача управляемых версий структуры в неуправляемые функции, как если бы они являлись управляемыми. Эти структуры можно передавать по значению или по ссылке, как показано в следующем примере.

Пример

В следующем примере кода представлены управляемый и неуправляемый модули. Неуправляемый модуль представляет собой библиотеку DLL, в которой определяется структура Location и функция GetDistance, принимающая два экземпляра структуры Location. Второй модуль представляет собой управляемое приложение командной строки, в котором функция GetDistance импортируется, но определяется с помощью управляемого эквивалента структуры Location — MLocation. На практике, в большинстве случаев для обеих версий структуры используются одинаковые имена. В этом примере разные имена используются, чтобы показать определение прототипа метода DllImport с помощью управляемой версии.

Управляемый модуль компилируется с параметром /clr. Также допускается использование параметра /clr:pure.

Обратите внимание, что в управляемом коде никакие компоненты библиотеки DLL не предоставляются с помощью стандартной директивы #include. Фактически, обращение к библиотеке DLL осуществляется только во время выполнения. Поэтому невозможно определить во время компиляции ошибки функций, импортированных с помощью метода DllImport.

// TraditionalDll3.cpp
// compile with: /LD /EHsc
#include <iostream>
#include <stdio.h>
#include <math.h>

#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
   #define TRADITIONALDLL_API __declspec(dllexport)
#else
   #define TRADITIONALDLL_API __declspec(dllimport)
#endif

#pragma pack(push, 8)
struct Location {
   int x;
   int y;
};
#pragma pack(pop)

extern "C" {
   TRADITIONALDLL_API double GetDistance(Location, Location);
   TRADITIONALDLL_API void InitLocation(Location*);
}

double GetDistance(Location loc1, Location loc2) {
   printf_s("[unmanaged] loc1(%d,%d)", loc1.x, loc1.y);
   printf_s(" loc2(%d,%d)\n", loc2.x, loc2.y);

   double h = loc1.x - loc2.x;
   double v = loc1.y = loc2.y;
   double dist = sqrt( pow(h,2) + pow(v,2) );

   return dist;
}

void InitLocation(Location* lp) {
   printf_s("[unmanaged] Initializing location...\n");
   lp->x = 50;
   lp->y = 50;
}

// MarshalStruct_pi.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MLocation {
   int x;
   int y;
};

value struct TraditionalDLL {
   [DllImport("TraditionalDLL3.dll")]
   static public double GetDistance(MLocation, MLocation);
   [DllImport("TraditionalDLL3.dll")]
   static public double InitLocation(MLocation*);
};

int main() {
   MLocation loc1;
   loc1.x = 0;
   loc1.y = 0;

   MLocation loc2;
   loc2.x = 100;
   loc2.y = 100;

   double dist = TraditionalDLL::GetDistance(loc1, loc2);
   Console::WriteLine("[managed] distance = {0}", dist);

   MLocation loc3;
   TraditionalDLL::InitLocation(&loc3);
   Console::WriteLine("[managed] x={0} y={1}", loc3.x, loc3.y);
}

[unmanaged] loc1(0,0) loc2(100,100)
[managed] distance = 141.42135623731
[unmanaged] Initializing location...
[managed] x=50 y=50

См. также

Другие ресурсы

Использование явного вызова Pinvoke в C++ (атрибут DllImport)