方法: P/Invoke を使用して文字列をマーシャリングする
C スタイルの文字列を受け入れるネイティブ関数は、.NET Framework Platform Invoke (P/Invoke) サポートを使用して CLR 文字列型 System::String
を使用して呼び出すことができます。 可能であれば、P/Invoke の代わりに C++ 相互運用機能を使用することをお勧めします。 P/Invoke はコンパイル時のエラー報告をほとんど提供せず、タイプ セーフではなく、実装するのが面倒な場合があるためです。 アンマネージド API が DLL としてパッケージ化されていて、ソース コードが使用できない場合は、P/Invoke が唯一のオプションです。 それ以外の場合は、C++ 相互運用機能の使用 (暗黙的な P/Invoke) を参照してください。
マネージド文字列とアンマネージド文字列ではメモリ上のレイアウトが異なるため、マネージド関数からアンマネージド関数に文字列を渡すためには MarshalAsAttribute 属性を指定して、文字列データを正しく安全にマーシャリングするために必要な変換メカニズムを挿入するようにコンパイラに指示する必要があります。
組み込みデータ型のみを使用する関数と同様に、 DllImportAttribute ネイティブ関数にマネージド エントリ ポイントを宣言するために使用されます。 文字列を渡す関数では、これらのエントリ ポイントを C スタイルの String 文字列として定義する代わりに、型へのハンドルを使用できます。 この型を使用すると、必要な変換を実行するコードを挿入するようにコンパイラに求められます。 文字列を受け取るアンマネージ関数の関数引数ごとに、属性を MarshalAsAttribute 使用して、オブジェクトを C スタイルの String
文字列としてネイティブ関数にマーシャリングする必要があることを示します。
マーシャラーは、アンマネージド関数の呼び出しを非表示のラッパー ルーチンでラップします。 ラッパー ルーチンは、アンマネージド コンテキストでローカルに割り当てられた文字列にマネージド文字列をピン留めしてコピーします。 その後、ローカル コピーがアンマネージ関数に渡されます。 アンマネージ関数が返されると、ラッパーはリソースを削除します。 または、スタック上にあった場合は、ラッパーがスコープ外になったときに再利用されます。 アンマネージ関数は、このメモリに対して責任を負いません。 アンマネージ コードでは、独自の CRT によって設定されたヒープ内のメモリのみが作成および削除されるため、別の CRT バージョンを使用するマーシャラーに問題はありません。
アンマネージド関数から文字列が返された場合、それが戻り値か out パラメーターである場合、マーシャラーはそれを新しいマネージド文字列にコピーし、その後メモリを解放します。 詳細については、「既定のマーシャリングの動作」と「プラットフォーム呼び出しによるデータのマーシャリング」を参照してください。
例
次のコードは、アンマネージド モジュールとマネージド モジュールで構成されています。 アンマネージ モジュールは、呼び出 TakesAString
された関数を定義する DLL です。 TakesAString
は、C 形式の狭い文字列を char*
受け取ります。
// TraditionalDll2.cpp
// compile with: /LD /EHsc
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
extern "C" {
TRADITIONALDLL_API void TakesAString(char*);
}
void TakesAString(char* p) {
printf_s("[unmanaged] %s\n", p);
}
マネージド モジュールは、関数をインポートTakesAString
するコマンド ライン アプリケーションですがSystem.String
char*
、.. この MarshalAsAttribute 属性は、呼び出されたときに TakesAString
マネージド文字列をマーシャリングする方法を示すために使用されます。
// MarshalString.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
value struct TraditionalDLL
{
[DllImport("TraditionalDLL2.dll")]
static public void
TakesAString([MarshalAs(UnmanagedType::LPStr)]String^);
};
int main() {
String^ s = gcnew String("sample string");
Console::WriteLine("[managed] passing managed string to unmanaged function...");
TraditionalDLL::TakesAString(s);
Console::WriteLine("[managed] {0}", s);
}
この手法では、アンマネージ ヒープ上に文字列のコピーが作成されるため、ネイティブ関数によって文字列に加えられた変更は、文字列のマネージド コピーには反映されません。
DLL の一部は、従来 #include
のディレクティブによってマネージド コードに公開されません。 実際、DLL は実行時にのみアクセスされるため、使用して DllImport
インポートされた関数の問題はコンパイル時には検出されません。