Поделиться через


Маршалинг по умолчанию для строк

Оба класса System.String и System.Text.StringBuilder ведут себя одинаково при маршалинге.

Строки маршалируются как тип BSTR стиля COM или как массив символов, заканчивающийся пустой ссылкой (Nothing в Visual Basic). Символы в строке могут маршалироваться как символы в кодировке Юникод или ANSI, либо же способом, зависящим от платформы (Юникод в Microsoft Windows NT, Windows 2000 и Windows XP; ANSI в Windows 98 и Windows Millennium Edition (Windows Me)).

В данном разделе представлены следующие сведения о маршалинге строковых типов:

  • Строки, используемые в интерфейсах

  • Строки, используемые в вызовах неуправляемого кода

  • Строки, используемые в структурах

  • Буферы строк фиксированной длины

Строки, используемые в интерфейсах

В следующей таблице показаны варианты маршалинга строкового типа данных в неуправляемый код в качестве аргумента метода. Атрибут MarshalAsAttribute предоставляет несколько значений перечисления UnmanagedType для выполнения маршалинга строк в COM-интерфейсы.

Тип перечисления

Описание неуправляемого формата

UnmanagedType.BStr (по умолчанию)

Тип BSTR стиля COM с предварительно фиксированной длиной и знаками в кодировке Юникод.

UnmanagedType.LPStr

Указатель на строку знаков в кодировке ANSI, завершающуюся нулевым значением.

UnmanagedType.LPWStr

Указатель на строку знаков в кодировке Юникод, завершающуюся нулевым значением.

Эта таблица применяется к строкам. Однако для StringBuilder единственными разрешенными вариантами являются UnmanagedType.LPStr и UnmanagedType.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);
);

В следующем примере показан соответствующий интерфейс, описанный в библиотеке типов.

[…]
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);
);

Строки, используемые в вызовах неуправляемого кода

Вызов неуправляемого кода копирует строковые аргументы, выполняя преобразование из формата .NET Framework (Юникод) в неуправляемый формат платформы. При возврате из вызова строки не изменяются и не копируются назад из неуправляемой памяти в управляемую.

В следующей таблице перечислены варианты маршалинга для строк в случае маршалинга в качестве аргумента метода для вызова неуправляемого кода. Атрибут MarshalAsAttribute предоставляет несколько значений перечисления UnmanagedType для выполнения маршалинга строк.

Тип перечисления

Описание неуправляемого формата

UnmanagedType.AnsiBStr

Тип BSTR стиля COM с предварительно фиксированной длиной и знаками в кодировке ANSI.

UnmanagedType.BStr

Тип BSTR стиля COM с предварительно фиксированной длиной и знаками в кодировке Юникод.

UnmanagedType.LPStr

Указатель на строку знаков в кодировке ANSI, завершающуюся нулевым значением.

UnmanagedType.LPTStr (по умолчанию)

Указатель на строку знаков в кодировке, зависящей от платформы, завершающуюся нулевым значением.

UnmanagedType.LPWStr

Указатель на строку знаков в кодировке Юникод, завершающуюся нулевым значением.

UnmanagedType.TBStr

Тип BSTR стиля COM с предварительно фиксированной длиной и знаками в кодировке, зависящей от платформы.

VBByRefStr

Значение, позволяющее Visual Basic .NET изменять строку в неуправляемом коде и получать результаты, отраженные в управляемом коде. Это значение поддерживается только для вызова неуправляемого кода.

Эта таблица применяется к строкам. Однако для StringBuilder единственными разрешенными вариантами являются LPStr, LPTStr и LPWStr.

В следующем определении типа показано правильное использование атрибута MarshalAsAttribute для вызовов неуправляемого кода.

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 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
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 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);
}

Строки, используемые в структурах

Строки являются допустимыми элементами структур, но буферы StringBuilder buffers недопустимы в структурах. В следующей таблице показаны варианты маршалинга для строкового типа данных при маршалинге типа как поля. Атрибут MarshalAsAttribute содержит несколько значений перечисления UnmanagedType для выполнения маршалинга строк в поле.

Тип перечисления

Описание неуправляемого формата

UnmanagedType.BStr

Тип BSTR стиля COM с предварительно фиксированной длиной и знаками в кодировке Юникод.

UnmanagedType.LPStr

Указатель на строку знаков в кодировке ANSI, завершающуюся нулевым значением.

UnmanagedType.LPTStr

Указатель на строку знаков в кодировке, зависящей от платформы, завершающуюся нулевым значением.

UnmanagedType.LPWStr

Указатель на строку знаков в кодировке Юникод, завершающуюся нулевым значением.

UnmanagedType.ByValTStr

Массив знаков фиксированной длины, тип массива определяется кодировкой содержащей его структуры.

Тип ByValTStr используется для встраиваемых массивов символов фиксированной длины, расположенных в структуре. Другие типы применяются к ссылкам на строки, включенным в структуры и содержащим указатели на строки.

Аргумент CharSet атрибута StructLayoutAttribute, применяемого к содержащей указатели структуре, определяет формат знаков для строк в структурах. Ниже приведен пример структур, содержащих ссылки на строки и встроенные строки, а также знаки в кодировках ANSI, Юникод и кодировке, определяемой платформой.

Представление библиотеки типов

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)> _
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
[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;
}

Буферы строк фиксированной длины

При некоторых обстоятельствах необходимо передавать в неуправляемый код для обработки символьные буферы фиксированной длины. Простая передача строки в этом случае не работает, поскольку вызываемый объект не может изменять содержимое переданного буфера. Даже если строка передается по ссылке, не существует способа инициализации буфера заданного размера.

Решением является передача в качестве аргумента буфера StringBuilder вместо строки. Буфер StringBuilder может быть разыменован и изменен вызываемым объектом, при условии, что он не превышает емкость StringBuilder. Его также можно инициализировать, задав для него фиксированную длину. Например, если инициализируется буфер StringBuilder емкостью N, упаковщик предоставляет буфер размером (N+1) знаков. +1 учитывает, что неуправляемая строка имеет ограничитель (null), а у буфера StringBuilder такой ограничитель не отсутствует.

Например, функция GetWindowText интерфейса Microsoft Win32 API (определенная в Windows.h) является буфером знаков фиксированной длины, который должен быть передан в неуправляемый код для обработки. Указатель LpString указывает на выделенный вызывающим объектом буфер размером nMaxCount. Ожидается, что вызывающий объект выделяет буфер и задает аргумент nMaxCount равным размеру выделяемого буфера. В следующем коде показано объявление функции GetWindowText,соответствующее объявлению в файле Windows.h.

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

Буфер StringBuilder может быть разыменован и изменен вызываемым объектом, при условии, что он не превышает емкость StringBuilder. В следующем примере кода показана инициализация буфера StringBuilder с фиксированной длиной.

Public Class Win32API
Public Declare Auto Sub GetWindowText Lib "User32.Dll" _
(h As Integer, s As StringBuilder, nMaxCount As Integer)
End Class
Public Class Window
   Friend h As Integer ' Friend handle to Window.
   Public Function GetText() As String
      Dim sb As New StringBuilder(256)
      Win32API.GetWindowText(h, sb, sb.Capacity + 1)
   Return sb.ToString()
   End Function
End Class
public class Win32API {
[DllImport("User32.Dll")]
public static extern void GetWindowText(int h, StringBuilder s, 
int nMaxCount);
}
public class Window {
   internal int h;        // Internal handle to Window.
   public String GetText() {
      StringBuilder sb = new StringBuilder(256);
      Win32API.GetWindowText(h, sb, sb.Capacity + 1);
   return sb.ToString();
   }
}

См. также

Основные понятия

Преобразуемые и непреобразуемые типы

Атрибуты направления

Копирование и закрепление

Другие ресурсы

Поведение маршалинга по умолчанию