方法: P/Invoke を使用して埋め込みポインターをマーシャリングする
アンマネージド DLL に実装された関数は、Platform Invoke (P/Invoke) 機能を使ってマネージド コードから呼び出すことができます。 DLL のソース コードを使用できない場合は、P/Invoke が相互運用の唯一のオプションです。 ただし、他の .NET 言語と異なり、Visual C++ には P/Invoke の代替手段が用意されています。 詳細については、「 C++ 相互運用機能 (暗黙的な P/Invoke) の使用」および「 方法: C++ 相互運用機能を使用して埋め込みポインターをマーシャリングするを参照してください。
例
構造体をネイティブ コードに渡すには、ネイティブ構造体とデータ レイアウトが同等のマネージド構造体を作成する必要があります。 ただし、ポインターを含む構造体には特別な処理が必要です。 ネイティブ構造体の埋め込みポインターごとに、IntPtr 型のインスタンスが構造体のマネージド バージョンに含まれている必要があります。 また、これらのインスタンスのメモリは、AllocCoTaskMem、StructureToPtr、FreeCoTaskMem の各メソッドを使って、明示的に割り当て、初期化し、解放する必要があります。
次のコードは、アンマネージド モジュールとマネージド モジュールで構成されています。 アンマネージ モジュールは、ポインターを含む ListString
という構造体を受け入れる関数と、 TakesListStruct
という関数を定義する DLL です。
// 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
関数をインポートし、IntPtr インスタンスでdouble*
が表されることを除き、ネイティブ ListStruct
と同等のMListStruct
と呼ばれる構造体を定義するコマンド ライン アプリケーションです。 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 を使用してインポートされた関数の問題はコンパイル時に検出できません。