マーシャリング は、マネージド コードとネイティブ コードの間で型をクロスする必要がある場合に型を変換するプロセスです。
マーシャリングが必要なのは、マネージド コードとアンマネージド コードの型が異なるためです。 たとえば、マネージド コードには string
があり、アンマネージ文字列は .NET string
エンコード (UTF-16)、ANSI コード ページ エンコード、UTF-8、null 終端、ASCII などです。既定では、P/Invoke サブシステムは、この記事で説明されている既定の動作に基づいて適切な処理を実行しようとします。 ただし、追加の制御が必要な状況では、 MarshalAs 属性を使用して、アンマネージ側で想定される型を指定できます。 たとえば、文字列を null で終わる UTF-8 文字列として送信する場合は、次のようにできます。
[LibraryImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPStr)] string parameter);
// or
[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);
System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute
属性をアセンブリに適用する場合、次のセクションの規則は適用されません。 この属性が適用されたときに .NET 値をネイティブ コードに公開する方法については、 無効なランタイム マーシャリングを参照してください。
共通型のマーシャリングの既定の規則
一般に、マーシャリング時にランタイムは"正しいこと" を実行して、ユーザーの作業量を最小限にしようとします。 次の表では、パラメーターまたはフィールドで使用する場合の各型の既定のマーシャリング方法について説明します。 C99/C++11 の固定幅整数と文字型は、次の表がすべてのプラットフォームに対して正しいことを確認するために使用されます。 これらの型と同じ配置とサイズの要件を持つ任意のネイティブ型を使用できます。
この最初の表では、マーシャリングが P/Invoke とフィールド マーシャリングの両方で同じであるさまざまな型のマッピングについて説明します。
C# キーワード | .NET 型 | ネイティブ型 |
---|---|---|
byte |
System.Byte |
uint8_t |
sbyte |
System.SByte |
int8_t |
short |
System.Int16 |
int16_t |
ushort |
System.UInt16 |
uint16_t |
int |
System.Int32 |
int32_t |
uint |
System.UInt32 |
uint32_t |
long |
System.Int64 |
int64_t |
ulong |
System.UInt64 |
uint64_t |
char |
System.Char |
P/Invoke または構造体のエンコードに応じて、 char または char16_t 。
文字セットのドキュメントを参照してください。 |
System.Char |
P/Invoke または構造体のエンコードに応じて、 char* または char16_t* 。
文字セットのドキュメントを参照してください。 |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
.NET ポインター型 (例: void* ) |
void* |
|
派生した型 System.Runtime.InteropServices.SafeHandle |
void* |
|
派生した型 System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Win32 BOOL の種類 |
decimal |
System.Decimal |
COM DECIMAL 構造体 |
.NET デリゲート | ネイティブ関数ポインター | |
System.DateTime |
Win32 DATE の種類 |
|
System.Guid |
Win32 GUID の種類 |
マーシャリングのいくつかのカテゴリでは、パラメーターまたは構造体としてマーシャリングする場合、既定値が異なります。
.NET 型 | ネイティブ型 (パラメーター) | ネイティブ型 (フィールド) |
---|---|---|
.NET 配列 | 配列要素のネイティブ表現の配列の先頭へのポインター。 |
[MarshalAs] 属性なしでは許可されません |
Sequential またはLayoutKind を持つクラスExplicit |
クラスのネイティブ表現へのポインター | クラスのネイティブ表現 |
次の表に、Windows 専用の既定のマーシャリング規則を示します。 Windows 以外のプラットフォームでは、これらの型をマーシャリングすることはできません。
.NET 型 | ネイティブ型 (パラメーター) | ネイティブ型 (フィールド) |
---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
COM インターフェイス |
[MarshalAs] 属性なしでは許可されません |
System.ArgIterator |
va_list |
許可されていない |
System.Collections.IEnumerator |
IEnumVARIANT* |
許可されていない |
System.Collections.IEnumerable |
IDispatch* |
許可されていない |
System.DateTimeOffset |
int64_t 1601 年 1 月 1 日の午前 0 時からのティック数を表す |
int64_t 1601 年 1 月 1 日の午前 0 時からのティック数を表す |
一部の型は、フィールドとしてではなく、パラメーターとしてのみマーシャリングできます。 これらの型を次の表に示します。
.NET 型 | ネイティブ型 (パラメーターのみ) |
---|---|
System.Text.StringBuilder |
P/Invoke のCharSet に応じて、char* またはchar16_t* 。
文字セットのドキュメントを参照してください。 |
System.ArgIterator |
va_list (Windows x86/x64/arm64 のみ) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
これらの既定値が目的の動作を正確に行わない場合は、パラメーターのマーシャリング方法をカスタマイズできます。 パラメーター マーシャリングに関する記事では、さまざまなパラメーター型のマーシャリング方法をカスタマイズする方法について説明します。
COM シナリオでの既定のマーシャリング
.NET で COM オブジェクトに対してメソッドを呼び出す場合、.NET ランタイムは、一般的な COM セマンティクスに一致するように既定のマーシャリング 規則を変更します。 次の表に、.NET ランタイムが COM シナリオで使用する規則を示します。
.NET 型 | ネイティブ型 (COM メソッド呼び出し) |
---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
デリゲート型 |
_Delegate* .NET Framework で。 .NET Core および .NET 5 以降では許可されていません。 |
System.Drawing.Color |
OLECOLOR |
.NET 配列 | SAFEARRAY |
System.String[] |
SAFEARRAY
BSTR 数 |
クラスと構造体のマーシャリング
型マーシャリングのもう 1 つの側面は、構造体をアンマネージ メソッドに渡す方法です。 たとえば、一部のアンマネージ メソッドでは、パラメーターとして構造体が必要です。 このような場合は、対応する構造体またはクラスをパラメーターとして使用するために、世界のマネージド部分に作成する必要があります。 ただし、クラスを定義するだけでは不十分です。また、クラス内のフィールドをアンマネージ構造体にマップする方法をマーシャラーに指示する必要もあります。 ここでは、 StructLayout
属性が役立ちます。
[LibraryImport("kernel32.dll")]
static partial void GetSystemTime(out SystemTime systemTime);
[StructLayout(LayoutKind.Sequential)]
struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Millisecond;
}
public static void Main(string[] args)
{
SystemTime st = new SystemTime();
GetSystemTime(st);
Console.WriteLine(st.Year);
}
前のコードは、 GetSystemTime()
関数を呼び出す簡単な例を示しています。 興味深いビットは 4 行目にあります。 この属性は、クラスのフィールドを、もう一方の (アンマネージ) 側の構造体に順番にマップすることを指定します。 つまり、次の例に示すように、アンマネージド構造体に対応する必要があるため、フィールドの名前付けは重要ではなく、順序だけが重要です。
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
構造体の既定のマーシャリングで必要な処理が行われない場合があります。 構造マーシャリングのカスタマイズに関する記事では、構造のマーシャリング方法をカスタマイズする方法について説明します。
.NET