Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Как классы System.String , так и System.Text.StringBuilder имеют аналогичное поведение маршаллинга.
Строки маршалируются как тип COM-стиля BSTR
или как строка, завершающаяся значением NULL (массив символов, заканчивающийся нулевым символом). Символы в строке можно маршалировать как Юникод (по умолчанию в системах Windows) или ANSI.
Строки, используемые в интерфейсах
В следующей таблице показаны параметры маршалинга для строкового типа данных при передаче в качестве аргумента метода в неуправляемый код. Атрибут MarshalAsAttribute предоставляет несколько значений перечисления UnmanagedType для маршалирования строк в интерфейсы COM.
Тип перечисления | Описание неуправляемого формата |
---|---|
UnmanagedType.BStr (по умолчанию) |
COM-стиль BSTR с префиксной длиной и символами Юникод. |
UnmanagedType.LPStr |
Указатель на массив символов ANSI, завершающийся значением NULL. |
UnmanagedType.LPWStr |
Указатель на массив символов Юникода, завершающийся значением NULL. |
Эта таблица относится к String. Для 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);
}
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);
};
Строки, используемые в Platform Invoke (вызове платформы)
Если CharSet имеет значение Unicode или строковый аргумент явно помечен как [MarshalAs(UnmanagedType.LPWSTR)] и строка передается по значению (не как ref
или out
), строка закрепляется и используется непосредственно в нативном коде. В противном случае вызов платформы копирует строковые аргументы, преобразуя формат .NET Framework (Юникод) в неуправляемый формат платформы. Строки неизменяемы и не копируются из неуправляемой памяти в управляемую память при возврате вызова.
Нативный код отвечает только за освобождение памяти, если строка передается по ссылке и ей назначается новое значение. В противном случае среда выполнения .NET владеет памятью и отпустит ее после вызова.
В следующей таблице перечислены варианты маршалинга строк в качестве аргументов метода платформенного вызова. Атрибут MarshalAsAttribute предоставляет несколько UnmanagedType значений перечисления для маршалирования строк.
Тип перечисления | Описание неуправляемого формата |
---|---|
UnmanagedType.AnsiBStr |
Стиль COM BSTR с префиксной длиной и символами ANSI. |
UnmanagedType.BStr |
COM-стиль BSTR с префиксной длиной и символами Юникод. |
UnmanagedType.LPStr (по умолчанию) |
Указатель на массив символов ANSI, завершающийся значением NULL. |
UnmanagedType.LPTStr |
Указатель на массив символов, зависимых от платформы, завершаемых значением NULL. |
UnmanagedType.LPUTF8Str |
Указатель на массив символов, завершаемых значением NULL, в кодировке UTF-8. |
UnmanagedType.LPWStr |
Указатель на массив символов Юникода, завершающийся значением NULL. |
UnmanagedType.TBStr |
Стиль COM BSTR с префиксной длиной и зависимыми от платформы символами. |
VBByRefStr |
Значение, позволяющее Visual Basic изменять строку в неуправляемом коде и отражать результаты в управляемом коде. Это значение поддерживается только для вызова платформы. Это значение по умолчанию в Visual Basic для ByVal строк. |
Эта таблица относится к String. Для StringBuilder, допустимы только варианты LPStr
, LPTStr
и LPWStr
.
В следующем определении типа показано правильное использование MarshalAsAttribute
для вызовов платформы platform invoke.
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 (по умолчанию) |
Указатель на массив символов ANSI, завершающийся значением NULL. |
UnmanagedType.LPTStr |
Указатель на массив символов, зависимых от платформы, завершаемых значением NULL. |
UnmanagedType.LPUTF8Str |
Указатель на массив символов, завершаемых значением NULL, в кодировке UTF-8. |
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]
, может быть разыменован и изменен вызывающей стороной, если он не превышает объём выделенного массива.
Например, функция API Windows GetWindowText
(определенная в winuser.h) требует, чтобы вызывающий объект передал буфер символов фиксированной длины, в который функция записывает текст окна. Аргумент lpString
указывает на выделенный вызывающим буфер размером nMaxCount
. Ожидается, что вызывающая сторона выделяет буфер и указывает аргумент nMaxCount
, равный размеру выделенного буфера. В следующем примере показано GetWindowText
объявление функции, определённое в winuser.h.
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
аргументов не рекомендуется, если вы обеспокоены производительностью. Дополнительные сведения см. в разделе "Строковые параметры".