次の方法で共有


型のマーシャリング

マーシャリング は、マネージド コードとネイティブ コードの間で型をクロスする必要がある場合に型を変換するプロセスです。

マーシャリングが必要なのは、マネージド コードとアンマネージド コードの型が異なるためです。 たとえば、マネージド コードでは、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.LPUTF8Str)] string parameter);

// or

[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);

属性をアセンブリに適用する場合、次のセクションの規則は適用されません。 この属性が適用されたときに.NETの値がネイティブコードに公開される方法については、「無効化されたランタイム・マーシャリング」をご参照ください。

共通型をマーシャリングする場合の既定の規則

一般に、マーシャリング時にランタイムは"正しいこと" を実行して、ユーザーの作業量を最小限にしようとします。 次の表では、パラメーターまたはフィールドで使用する場合の各型の既定のマーシャリング方法について説明します。 C99/C++11 の固定幅整数と文字型は、次の表がすべてのプラットフォームに対して正しいことを確認するために使用されます。 これらの型と同じ配置とサイズの要件を持つ任意のネイティブ型を使用できます。

この最初の表では、マーシャリングが P/Invoke とフィールド マーシャリングの両方で同じである型のマッピングについて説明します。

Important

long を使用する C 関数を呼び出す場合は、C# CLong の代わりに CULong または long (.NET 6 以降) を使用します。 以前の.NETバージョンの詳細と回避策については、「Cross-platform のデータ型に関する考慮事項を参照してください。

wchar_t 型は、Windowsでは UTF-16 (2 バイト) ですが、他のプラットフォーム (通常は Linux および macOS では UTF-32 (4 バイト)) でコンパイラ定義されています。 このため、 は単一のクロスプラットフォーム ABI として使用するのは困難です。 クロスプラットフォーム ネイティブ API を設計する際には、あるアプローチではなく、明確に定義されたエンコード契約(たとえば、UTF-8)を採用する方が望ましいです。

ネイティブ 文字列では、ライブラリまたはプラットフォームで定義されているエンコードが使用されます。 を受け取る C 関数を呼び出すときは、UTF-8 の、UTF-16 の、その他のエンコードのなど、適切な文字列マーシャリング オプションを選択して、想定されるエンコードと一致させます。

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 または構造体のエンコードに応じて、 または 。 文字セットのドキュメントを参照してください。
System.Char P/Invoke または構造体のエンコードに応じて、 または 。 文字セットのドキュメントを参照してください。
nint System.IntPtr intptr_t
nuint System.UIntPtr uintptr_t
.NET ポインター型 (例: void*) void*
から派生した型 void*
から派生した型 void*
bool System.Boolean Win32 の種類
decimal System.Decimal COM 構造体
.NET デリゲート ネイティブ関数ポインター
System.DateTime Win32 の種類
System.Guid Win32 の種類

マーシャリングのいくつかのカテゴリでは、パラメーターまたは構造体としてマーシャリングする場合、既定値が異なります。

.NET型 ネイティブ型 (パラメーター) ネイティブタイプ (フィールド)
.NET 配列 配列要素のネイティブ表現の配列の先頭へのポインター。 属性なしでは許可されません
をまたはに持つクラス クラスのネイティブ表現へのポインター クラスのネイティブ表現

次の表に、Windows専用の既定のマーシャリング規則を示します。 Windows以外のプラットフォームでは、これらの型をマーシャリングすることはできません。

.NET型 ネイティブ型 (パラメーター) ネイティブタイプ (フィールド)
System.Object VARIANT IUnknown*
System.Array COM インターフェイス 属性なしでは許可されません
System.ArgIterator va_list 許可されていない
System.Collections.IEnumerator IEnumVARIANT* 許可されていない
System.Collections.IEnumerable IDispatch* 許可されていない
System.DateTimeOffset 1601 年 1 月 1 日午前 0 時以降のティック数を表す 1601 年 1 月 1 日午前 0 時以降のティック数を表す

一部の型は、フィールドとしてではなく、パラメーターとしてのみマーシャリングできます。 これらの型を次の表に示します。

.NET型 ネイティブ型 (パラメーターのみ)
System.Text.StringBuilder P/Invoke の に応じて、 または のいずれか。 文字セットのドキュメントを参照してください。
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
デリゲート型 .NET Framework の _Delegate*。 .NET Core および .NET 5 以降では許可されていません。
System.Drawing.Color OLECOLOR
.NET配列 SAFEARRAY
System.String[]

クラスと構造体のマーシャリング

型マーシャリングのもう1つの側面として、構造体をアンマネージ メソッドに渡す方法があります。 たとえば、一部のアンマネージ メソッドでは、パラメーターとして構造体が必要です。 このような場合は、対応する構造体またはクラスをパラメーターとして使用するために、世界のマネージド部分に作成する必要があります。 ただし、クラスを定義するだけでは不十分です。また、クラス内のフィールドをアンマネージ構造体にマップする方法をマーシャラーに指示する必要もあります。 ここでは、 属性が役立ちます。

using System;
using System.Runtime.InteropServices;

Win32Interop.GetSystemTime(out Win32Interop.SystemTime systemTime);

Console.WriteLine(systemTime.Year);

internal static partial class Win32Interop
{
    [LibraryImport("kernel32.dll")]
    internal static partial void GetSystemTime(out SystemTime systemTime);

    [StructLayout(LayoutKind.Sequential)]
    internal ref 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;
    }
}

前のコードは、 関数を呼び出す簡単な例を示しています。 面白い部分は 13 行目にあります。 この属性は、クラスのフィールドを、もう一方の (アンマネージ) 側の構造体に順番にマップすることを指定します。 つまり、次の例に示すように、アンマネージド構造体に対応する必要があるため、フィールドの名前付けは重要ではなく、順序だけが重要です。

typedef struct _SYSTEMTIME {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;

時々、構造体のデフォルトのマーシャリングが期待通りに動作しない場合があります。 構造マーシャリングのカスタマイズに関する記事では、構造のマーシャリング方法をカスタマイズする方法について説明します。