次の方法で共有


文字列に対する既定のマーシャリング

System.StringクラスとSystem.Text.StringBuilder クラスのマーシャリング動作はどちらも同様です。

文字列は、COM スタイルの BSTR 型または null で終わる文字列 (null 文字で終わる文字配列) としてマーシャリングされます。 文字列内の文字は、Unicode (Windows システムでは既定) または ANSI としてマーシャリングできます。

インターフェイスで使用される文字列

次の表に、アンマネージ コードのメソッド引数としてマーシャリングする場合の文字列データ型のマーシャリング オプションを示します。 MarshalAsAttribute属性は、文字列を COM インターフェイスにマーシャリングするためのいくつかのUnmanagedType列挙値を提供します。

列挙型 アンマネージ形式の説明
UnmanagedType.BStr (既定値) 長さと Unicode 文字がプレフィックスされた COM スタイル BSTR
UnmanagedType.LPStr ANSI 文字の null で終わる配列へのポインター。
UnmanagedType.LPWStr Unicode 文字の null で終わる配列へのポインター。

この表は、 Stringに適用されます。 StringBuilderの場合、使用できるオプションはUnmanagedType.LPStrUnmanagedType.LPWStrのみです。

次の例は、 IStringWorker インターフェイスで宣言された文字列を示しています。

public interface IStringWorker
{
    void PassString1(string s);
    void PassString2([MarshalAs(UnmanagedType.BStr)] string s);
    void PassString3([MarshalAs(UnmanagedType.LPStr)] string s);
    void PassString4([MarshalAs(UnmanagedType.LPWStr)] string s);
    void PassStringRef1(ref string s);
    void PassStringRef2([MarshalAs(UnmanagedType.BStr)] ref string s);
    void PassStringRef3([MarshalAs(UnmanagedType.LPStr)] ref string s);
    void PassStringRef4([MarshalAs(UnmanagedType.LPWStr)] ref string s);
}
Public Interface IStringWorker
    Sub PassString1(s As String)
    Sub PassString2(<MarshalAs(UnmanagedType.BStr)> s As String)
    Sub PassString3(<MarshalAs(UnmanagedType.LPStr)> s As String)
    Sub PassString4(<MarshalAs(UnmanagedType.LPWStr)> s As String)
    Sub PassStringRef1(ByRef s As String)
    Sub PassStringRef2(<MarshalAs(UnmanagedType.BStr)> ByRef s As String)
    Sub PassStringRef3(<MarshalAs(UnmanagedType.LPStr)> ByRef s As String)
    Sub PassStringRef4(<MarshalAs(UnmanagedType.LPWStr)> ByRef s As String)
End Interface

次の例は、タイプ ライブラリで説明されている対応するインターフェイスを示しています。

interface IStringWorker : IDispatch
{
    HRESULT PassString1([in] BSTR s);
    HRESULT PassString2([in] BSTR s);
    HRESULT PassString3([in] LPStr s);
    HRESULT PassString4([in] LPWStr s);
    HRESULT PassStringRef1([in, out] BSTR *s);
    HRESULT PassStringRef2([in, out] BSTR *s);
    HRESULT PassStringRef3([in, out] LPStr *s);
    HRESULT PassStringRef4([in, out] LPWStr *s);
};

プラットフォーム呼び出しで使用される文字列

CharSet が Unicode であるか、文字列引数が明示的に [MarshalAs(UnmanagedType.LPWSTR)] としてマークされ、文字列が値 ( ref または outではなく) 渡される場合、文字列は固定され、ネイティブ コードによって直接使用されます。 それ以外の場合、プラットフォーム呼び出しでは文字列引数がコピーされ、.NET Framework 形式 (Unicode) からプラットフォームのアンマネージド形式に変換されます。 文字列は不変であり、呼び出しが戻るときにアンマネージ メモリからマネージド メモリにコピーされません。

ネイティブ コードは、文字列が参照渡しされ、新しい値が割り当てられた場合にのみメモリを解放します。 それ以外の場合、.NET ランタイムはメモリを所有し、呼び出し後に解放します。

次の表に、プラットフォーム呼び出しのメソッド引数としてマーシャリングする場合の文字列のマーシャリング オプションを示します。 MarshalAsAttribute属性は、文字列をマーシャリングするためのいくつかのUnmanagedType列挙値を提供します。

列挙型 アンマネージ形式の説明
UnmanagedType.AnsiBStr 長さと ANSI 文字がプレフィックスされた COM スタイル BSTR
UnmanagedType.BStr 長さと Unicode 文字がプレフィックスされた COM スタイル BSTR
UnmanagedType.LPStr (既定値) ANSI 文字の null で終わる配列へのポインター。
UnmanagedType.LPTStr プラットフォームに依存する文字の null で終わる配列へのポインター。
UnmanagedType.LPUTF8Str UTF-8 でエンコードされた文字の null で終わる配列へのポインター。
UnmanagedType.LPWStr Unicode 文字の null で終わる配列へのポインター。
UnmanagedType.TBStr COM スタイルの BSTR には、プレフィックス付きの長さとプラットフォームに依存する文字が含まれます。
VBByRefStr Visual Basic がアンマネージ コードの文字列を変更し、結果をマネージド コードに反映できるようにする値。 この値は、プラットフォーム呼び出しでのみサポートされます。 これは、 ByVal 文字列の Visual Basic の既定値です。

この表は、 Stringに適用されます。 StringBuilderの場合、使用できるオプションは、LPStrLPTStr、およびLPWStrのみです。

次の型定義は、プラットフォーム呼び出しの MarshalAsAttribute の正しい使用を示しています。

class StringLibAPI
{
    [DllImport("StringLib.dll")]
    public static extern void PassLPStr([MarshalAs(UnmanagedType.LPStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassLPWStr([MarshalAs(UnmanagedType.LPWStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassLPTStr([MarshalAs(UnmanagedType.LPTStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassLPUTF8Str([MarshalAs(UnmanagedType.LPUTF8Str)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassBStr([MarshalAs(UnmanagedType.BStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassAnsiBStr([MarshalAs(UnmanagedType.AnsiBStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassTBStr([MarshalAs(UnmanagedType.TBStr)] string s);
}
Class StringLibAPI
    Public Declare Auto Sub PassLPStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPStr)> s As String)
    Public Declare Auto Sub PassLPWStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPWStr)> s As String)
    Public Declare Auto Sub PassLPTStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPTStr)> s As String)
    Public Declare Auto Sub PassLPUTF8Str Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPUTF8Str)> s As String)
    Public Declare Auto Sub PassBStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.BStr)> s As String)
    Public Declare Auto Sub PassAnsiBStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.AnsiBStr)> s As String)
    Public Declare Auto Sub PassTBStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.TBStr)> s As String)
End Class

構造体で使用される文字列

文字列は構造体の有効なメンバーです。ただし、 StringBuilder バッファーは構造体では無効です。 次の表は、型がフィールドとしてマーシャリングされるときに String データ型のマーシャリング オプションを示しています。 MarshalAsAttribute属性は、文字列をフィールドにマーシャリングするためのいくつかのUnmanagedType列挙値を提供します。

列挙型 アンマネージ形式の説明
UnmanagedType.BStr 長さと Unicode 文字がプレフィックスされた COM スタイル BSTR
UnmanagedType.LPStr (既定値) ANSI 文字の null で終わる配列へのポインター。
UnmanagedType.LPTStr プラットフォームに依存する文字の null で終わる配列へのポインター。
UnmanagedType.LPUTF8Str UTF-8 でエンコードされた文字の null で終わる配列へのポインター。
UnmanagedType.LPWStr Unicode 文字の null で終わる配列へのポインター。
UnmanagedType.ByValTStr 固定長の文字配列。配列の型は、格納する構造体の文字セットによって決まります。

ByValTStr型は、構造体内に表示されるインラインの固定長文字配列に使用されます。 その他の型は、文字列へのポインターを含む構造体内に含まれる文字列参照に適用されます。

格納構造体に適用されるCharSetStructLayoutAttribute引数によって、構造体内の文字列の文字形式が決まります。 次の構造体の例には、文字列参照とインライン文字列のほか、ANSI、Unicode、プラットフォームに依存する文字が含まれています。 タイプ ライブラリでのこれらの構造体の表現は、次の C++ コードに示されています。

struct StringInfoA
{
    char *  f1;
    char    f2[256];
};

struct StringInfoW
{
    WCHAR * f1;
    WCHAR   f2[256];
    BSTR    f3;
};

struct StringInfoT
{
    TCHAR * f1;
    TCHAR   f2[256];
};

次の例は、 MarshalAsAttribute を使用して同じ構造を異なる形式で定義する方法を示しています。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct StringInfoA
{
    [MarshalAs(UnmanagedType.LPStr)] public string f1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct StringInfoW
{
    [MarshalAs(UnmanagedType.LPWStr)] public string f1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
    [MarshalAs(UnmanagedType.BStr)] public string f3;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct StringInfoT
{
    [MarshalAs(UnmanagedType.LPTStr)] public string f1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
}
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Ansi)> _
Structure StringInfoA
    <MarshalAs(UnmanagedType.LPStr)> Public f1 As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
    Public f2 As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Structure StringInfoW
    <MarshalAs(UnmanagedType.LPWStr)> Public f1 As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
    Public f2 As String
<MarshalAs(UnmanagedType.BStr)> Public f3 As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Auto)> _
Structure StringInfoT
    <MarshalAs(UnmanagedType.LPTStr)> Public f1 As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
    Public f2 As String
End Structure

固定長文字列バッファー

場合によっては、固定長文字バッファーをアンマネージ コードに渡して操作する必要があります。 この場合、呼び出し先は渡されたバッファーの内容を変更できないため、文字列を渡すだけでは機能しません。 文字列が参照渡しされた場合でも、バッファーを特定のサイズに初期化する方法はありません。

解決策は、byte[]の代わりに、予想されるエンコーディングに応じて、char[]またはStringを引数として渡すことです。 [Out]でマークされている配列は、割り当てられた配列の容量を超えていない場合、呼び出し先によって逆参照および変更できます。

たとえば、Windows GetWindowText API 関数 ( winuser.h で定義) では、呼び出し元は、関数がウィンドウのテキストを書き込む固定長文字バッファーを渡す必要があります。 lpString引数は、呼び出し元によって割り当てられたサイズnMaxCountバッファーを指します。 呼び出し元はバッファーを割り当て、 nMaxCount 引数を割り当てられたバッファーのサイズに設定することが想定されています。 次の例は、GetWindowText で定義されている関数の宣言を示しています。

int GetWindowText(
    HWND hWnd,        // Handle to window or control.
    LPTStr lpString,  // Text buffer.
    int nMaxCount     // Maximum number of characters to copy.
);

char[]は、呼び出し先によって逆参照および変更できます。 次のコード例では、 ArrayPool<char> を使用して char[]を事前に割り当てる方法を示します。

using System;
using System.Buffers;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("User32.dll", CharSet = CharSet.Unicode)]
    public static extern void GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);
}

public class Window
{
    internal IntPtr h;        // Internal handle to Window.
    public string GetText()
    {
        char[] buffer = ArrayPool<char>.Shared.Rent(256 + 1);
        NativeMethods.GetWindowText(h, buffer, buffer.Length);
        return new string(buffer);
    }
}
Imports System
Imports System.Buffers
Imports System.Runtime.InteropServices

Friend Class NativeMethods
    Public Declare Auto Sub GetWindowText Lib "User32.dll" _
        (hWnd As IntPtr, <Out> lpString() As Char, nMaxCount As Integer)
End Class

Public Class Window
    Friend h As IntPtr ' Friend handle to Window.
    Public Function GetText() As String
        Dim buffer() As Char = ArrayPool(Of Char).Shared.Rent(256 + 1)
        NativeMethods.GetWindowText(h, buffer, buffer.Length)
        Return New String(buffer)
   End Function
End Class

もう 1 つの解決策は、StringBuilderの代わりに引数としてStringを渡すことです。 StringBuilderのマーシャリング時に作成されたバッファーは、StringBuilderの容量を超えていない場合、呼び出し先によって逆参照および変更できます。 また、固定長に初期化することもできます。 たとえば、 StringBuilder バッファーを N の容量に初期化すると、マーシャラーはサイズ (N+1) 文字のバッファーを提供します。 +1 は、アンマネージ文字列に null ターミネータが含まれているのに対し、 StringBuilder は含まないという事実を考慮しています。

一般に、パフォーマンスが気になる場合は、 StringBuilder 引数を渡すことはお勧めしません。 詳細については、「 文字列パラメーター」を参照してください。

こちらも参照ください