다음을 통해 공유


문자열에 대한 기본 마샬링

System.String 클래스와 System.Text.StringBuilder 클래스 모두 유사한 마샬링 동작을 갖습니다.

문자열은 COM 스타일 BSTR 형식 또는 null로 끝나는 문자열(null 문자로 끝나는 문자 배열)로 마샬링됩니다. 문자열 내의 문자는 유니코드(Windows 시스템의 기본값) 또는 ANSI로 마샬링할 수 있습니다.

인터페이스에 사용되는 문자열

다음 표에서는 관리되지 않는 코드에 대한 메서드 인수로 마샬링될 때 문자열 데이터 형식에 대한 마샬링 옵션을 보여 있습니다. 이 특성은 MarshalAsAttribute 문자열을 COM 인터페이스로 마샬링하는 여러 UnmanagedType 열거형 값을 제공합니다.

열거형 형식 관리되지 않는 형식에 대한 설명
UnmanagedType.BStr(기본값) 접두사 길이와 유니코드 문자가 있는 COM 스타일 BSTR 입니다.
UnmanagedType.LPStr Null로 끝나는 ANSI 문자 배열에 대한 포인터입니다.
UnmanagedType.LPWStr 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이 유니코드이거나 문자열 인수가 명시적으로 [MarshalAs(UnmanagedType.LPWSTR)]로 표시되고 문자열이 값(또는 아님 refout)으로 전달되면 문자열이 고정되고 네이티브 코드에서 직접 사용됩니다. 플랫폼 호출이 문자열 인수를 복사하여 .NET Framework 형식(유니코드)에서 플랫폼의 관리되지 않는 형식으로 변환합니다. 문자열은 변경할 수 없으며 호출이 반환될 때 관리되지 않는 메모리에서 관리되는 메모리로 다시 복사되지 않습니다.

네이티브 코드는 문자열이 참조로 전달되고 새 값을 할당하는 경우에만 메모리를 해제합니다. 그렇지 않으면 .NET 런타임은 메모리를 소유하고 호출 후에 해제합니다.

다음 표에서는 플랫폼 호출 호출의 메서드 인수로 마샬링될 때 문자열에 대한 마샬링 옵션을 나열합니다. 이 특성은 MarshalAsAttribute 문자열을 마샬링하는 여러 UnmanagedType 열거형 값을 제공합니다.

열거형 형식 관리되지 않는 형식에 대한 설명
UnmanagedType.AnsiBStr 접두사 길이와 ANSI 문자가 있는 COM 스타일 BSTR 입니다.
UnmanagedType.BStr 접두사 길이와 유니코드 문자가 있는 COM 스타일 BSTR 입니다.
UnmanagedType.LPStr(기본값) Null로 끝나는 ANSI 문자 배열에 대한 포인터입니다.
UnmanagedType.LPTStr 플랫폼 종속 문자의 null로 끝나는 배열에 대한 포인터입니다.
UnmanagedType.LPUTF8Str UTF-8로 인코딩된 문자의 null로 끝나는 배열에 대한 포인터입니다.
UnmanagedType.LPWStr Null로 끝나는 유니코드 문자 배열에 대한 포인터입니다.
UnmanagedType.TBStr 접두사 길이 및 플랫폼 종속 문자가 있는 COM 스타일 BSTR 입니다.
VBByRefStr Visual Basic에서 관리되지 않는 코드의 문자열을 변경하고 결과가 관리 코드에 반영되도록 하는 값입니다. 이 값은 플랫폼 호출에 대해서만 지원됩니다. 문자열에 대한 ByVal Visual Basic의 기본값입니다.

이 테이블은 String에 적용됩니다. StringBuilder에 있어서 허용되는 유일한 옵션은 LPStr, LPTStr, 및 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 접두사 길이와 유니코드 문자가 있는 COM 스타일 BSTR 입니다.
UnmanagedType.LPStr(기본값) Null로 끝나는 ANSI 문자 배열에 대한 포인터입니다.
UnmanagedType.LPTStr 플랫폼 종속 문자의 null로 끝나는 배열에 대한 포인터입니다.
UnmanagedType.LPUTF8Str UTF-8로 인코딩된 문자의 null로 끝나는 배열에 대한 포인터입니다.
UnmanagedType.LPWStr Null로 끝나는 유니코드 문자 배열에 대한 포인터입니다.
UnmanagedType.ByValTStr 고정 길이 문자 배열입니다. 배열의 형식은 포함하는 구조체의 문자 집합에 의해 결정됩니다.

ByValTStr 형식은 구조체 내에 나타나는 인라인 고정 길이 문자 배열에 사용됩니다. 다른 형식은 문자열에 대한 포인터를 포함하는 구조체 내에 포함된 문자열 참조에 적용됩니다.

CharSet 포함하는 구조체에 StructLayoutAttribute 적용되는 인수는 구조체에서 문자열의 문자 형식을 결정합니다. 다음 예제 구조체에는 문자열 참조 및 인라인 문자열뿐만 아니라 ANSI, 유니코드 및 플랫폼 종속 문자도 포함됩니다. 형식 라이브러리에서 이러한 구조체의 표현은 다음 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

또 다른 해결 방법은 StringBuilder 대신 String 을 인수로 전달하는 것입니다. 마샬링 StringBuilder 할 때 생성된 버퍼는, StringBuilder의 용량을 초과하지 않는 한, 호출 수신자가 역참조하고 수정할 수 있습니다. 고정 길이로 초기화할 수도 있습니다. 예를 들어, StringBuilder 용량이 N인 버퍼를 초기화하는 경우, 마샬러는 크기가 (N+1) 문자인 버퍼를 제공합니다. +1은 관리되지 않는 문자열에는 null 종결자가 있지만 StringBuilder에는 없다는 사실을 고려한 것입니다.

비고

일반적으로 성능이 우려되는 경우 인수를 전달하는 StringBuilder 것은 권장되지 않습니다. 자세한 내용은 문자열 매개 변수를 참조하세요.

참고하십시오