Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Tanto as classes como System.String as System.Text.StringBuilder têm um comportamento de agrupamento semelhante.
As cadeias de caracteres são agrupadas como um tipo de estilo BSTR COM ou como uma cadeia de caracteres terminada em nulo (uma matriz de caracteres que termina com um caractere nulo). Os caracteres dentro da string podem ser convertidos como Unicode, que é o padrão em sistemas Windows, ou ANSI.
Cadeias de caracteres usadas em interfaces
A tabela a seguir mostra as opções de empacotamento para o tipo de dados de cadeia de caracteres quando empacotado como um argumento de método para código não gerenciado. O MarshalAsAttribute atributo fornece vários UnmanagedType valores de enumeração para marshal strings para interfaces COM.
| Tipo de enumeração | Descrição do formato não gerenciado |
|---|---|
UnmanagedType.BStr (padrão) |
Um estilo BSTR COM com um comprimento prefixado e caracteres Unicode. |
UnmanagedType.LPStr |
Um ponteiro para uma matriz terminada em nulo de caracteres ANSI. |
UnmanagedType.LPWStr |
Um ponteiro para uma matriz terminada em nulo de caracteres Unicode. |
Esta tabela aplica-se a String. Para StringBuilder, as únicas opções permitidas são UnmanagedType.LPStr e UnmanagedType.LPWStr.
O exemplo a seguir mostra cadeias de caracteres declaradas na interface 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
O exemplo a seguir mostra a interface correspondente descrita em uma biblioteca de tipos.
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);
};
Strings usadas na invocação de plataforma
Quando o CharSet é Unicode ou um argumento de string é explicitamente marcado como [MarshalAs(UnmanagedType.LPWSTR)] e a string é passada por valor (not ref ou out), a string é fixada e usada diretamente pelo código nativo. Caso contrário, a plataforma invoca as cadeias de caracteres copiadas, convertendo do formato .NET Framework (Unicode) para o formato não gerido da plataforma. As cadeias de caracteres são imutáveis e não são copiadas de volta da memória não gerenciada para a memória gerenciada quando a chamada retorna.
O código nativo só é responsável por liberar a memória quando a cadeia de caracteres é passada por referência e atribui um novo valor. Caso contrário, o runtime .NET é o proprietário da memória e liberta a memória após a chamada.
A tabela a seguir lista as opções de empacotamento para strings quando empacotadas como um argumento de método de uma chamada de invocação de plataforma. O MarshalAsAttribute atributo fornece vários UnmanagedType valores de enumeração para gerir strings.
| Tipo de enumeração | Descrição do formato não gerenciado |
|---|---|
UnmanagedType.AnsiBStr |
Um estilo BSTR COM com um comprimento prefixado e caracteres ANSI. |
UnmanagedType.BStr |
Um estilo BSTR COM com um comprimento prefixado e caracteres Unicode. |
UnmanagedType.LPStr (padrão) |
Um ponteiro para uma matriz terminada em nulo de caracteres ANSI. |
UnmanagedType.LPTStr |
Um ponteiro para uma matriz terminada em nulo de caracteres dependentes da plataforma. |
UnmanagedType.LPUTF8Str |
Um ponteiro para uma matriz de caracteres terminada em nulo codificados em UTF-8. |
UnmanagedType.LPWStr |
Um ponteiro para uma matriz terminada em nulo de caracteres Unicode. |
UnmanagedType.TBStr |
Um estilo BSTR COM com um comprimento prefixado e caracteres dependentes da plataforma. |
VBByRefStr |
Um valor que permite ao Visual Basic alterar uma cadeia de caracteres em código não gerido e ter os resultados refletidos no código gerido. Este valor é suportado apenas para invocação de plataforma. Este é o valor padrão em Visual Basic para cadeias ByVal. |
Esta tabela aplica-se a String. Para StringBuilder, as únicas opções permitidas são LPStr, LPTStr, e LPWStr.
A definição de tipo a seguir mostra o uso correto de MarshalAsAttribute para invocação de chamadas de plataforma.
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
Cordas usadas em estruturas
Strings são membros válidos de estruturas; no entanto, StringBuilder os buffers são inválidos em estruturas. A tabela a seguir mostra as opções de empacotamento para o String tipo de dados quando o tipo é empacotado como um campo. O MarshalAsAttribute atributo fornece vários UnmanagedType valores de enumeração para organizar cadeias de caracteres para um campo.
| Tipo de enumeração | Descrição do formato não gerenciado |
|---|---|
UnmanagedType.BStr |
Um estilo BSTR COM com um comprimento prefixado e caracteres Unicode. |
UnmanagedType.LPStr (padrão) |
Um ponteiro para uma matriz terminada em nulo de caracteres ANSI. |
UnmanagedType.LPTStr |
Um ponteiro para uma matriz terminada em nulo de caracteres dependentes da plataforma. |
UnmanagedType.LPUTF8Str |
Um ponteiro para uma matriz de caracteres terminada em nulo codificados em UTF-8. |
UnmanagedType.LPWStr |
Um ponteiro para uma matriz terminada em nulo de caracteres Unicode. |
UnmanagedType.ByValTStr |
Uma matriz de caracteres de comprimento fixo; O tipo da matriz é determinado pelo conjunto de caracteres da estrutura que a contém. |
O ByValTStr tipo é usado para matrizes de caracteres embutidas de comprimento fixo que aparecem dentro de uma estrutura. Outros tipos se aplicam a referências de cadeia de caracteres contidas em estruturas que contêm ponteiros para cadeias de caracteres.
O argumento CharSet do StructLayoutAttribute aplicado à estrutura que contém determina o formato de caracteres das cadeias de caracteres em estruturas. As estruturas de exemplo a seguir contêm referências de cadeia de caracteres e cadeias de caracteres embutidas, bem como ANSI, Unicode e caracteres dependentes de plataforma. A representação dessas estruturas em uma biblioteca de tipos é mostrada no seguinte código C++:
struct StringInfoA
{
char * f1;
char f2[256];
};
struct StringInfoW
{
WCHAR * f1;
WCHAR f2[256];
BSTR f3;
};
struct StringInfoT
{
TCHAR * f1;
TCHAR f2[256];
};
O exemplo a seguir mostra como usar o MarshalAsAttribute para definir a mesma estrutura em formatos diferentes.
[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
Buffers de cadeia de caracteres de comprimento fixo
Em algumas circunstâncias, um buffer de caracteres de comprimento fixo deve ser passado para código não gerenciado para ser manipulado. Simplesmente passar uma cadeia de caracteres não funciona neste caso porque o destinatário não pode modificar o conteúdo do buffer passado. Mesmo que a cadeia de caracteres seja passada por referência, não há como inicializar o buffer para um determinado tamanho.
A solução é passar um byte[] ou char[], dependendo da codificação esperada, como o argumento em vez de um String. A matriz, quando marcada com [Out], pode ser desreferenciada e modificada pelo destinatário, desde que não exceda a capacidade da matriz alocada.
Por exemplo, a função API Windows GetWindowText (definida em winuser.h) exige que o chamador passe um buffer de caracteres de comprimento fixo onde a função escreve o texto da janela. O lpString argumento aponta para um buffer de tamanho nMaxCountalocado pelo chamador. Espera-se que o chamador aloque o buffer e defina o nMaxCount argumento para o tamanho do buffer alocado. O exemplo a seguir mostra a declaração de GetWindowText função conforme definido em winuser.h.
int GetWindowText(
HWND hWnd, // Handle to window or control.
LPTStr lpString, // Text buffer.
int nMaxCount // Maximum number of characters to copy.
);
A char[] pode ser desreferenciado e modificado pelo destinatário. A abordagem recomendada é usar ArrayPool<T> para obter um char[], o que evita alocações repetidas no heap. O seguinte exemplo de código demonstra este padrão.
using System;
using System.Buffers;
using System.Runtime.InteropServices;
internal static class NativeMethods
{
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern int 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);
try
{
int length = NativeMethods.GetWindowText(h, buffer, buffer.Length);
return new string(buffer, 0, length);
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}
}
}
Imports System
Imports System.Buffers
Imports System.Runtime.InteropServices
Friend Class NativeMethods
Public Declare Auto Function GetWindowText Lib "User32.dll" _
(hWnd As IntPtr, <Out> lpString() As Char, nMaxCount As Integer) 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)
Try
Dim length As Integer = NativeMethods.GetWindowText(h, buffer, buffer.Length)
Return New String(buffer, 0, length)
Finally
ArrayPool(Of Char).Shared.Return(buffer)
End Try
End Function
End Class
Também podes considerar passar um StringBuilder em vez de um String. O buffer criado quando um StringBuilder é serializado pode ser desreferenciado e modificado pela função chamada, desde que não exceda a capacidade do StringBuilder. Ele também pode ser inicializado em um comprimento fixo. Por exemplo, se inicializar um StringBuilder buffer com uma capacidade de N, o marshaller fornecerá um buffer de tamanho de (N+1) caracteres. O +1 explica o facto de a cadeia não gerida ter um terminador nulo, enquanto StringBuilder não tem.
Atenção
Evite StringBuilder parâmetros quando o desempenho importa.
StringBuilder
cria sempre uma cópia nativa do buffer. Uma chamada típica para obter uma string do código nativo pode resultar em quatro alocações.
- Um amortecedor gerido
StringBuilder. - Um buffer nativo atribuído durante o marshalling.
- Se
[Out], o conteúdo nativo do buffer é copiado para um array gerido recém-alocado. - Um
stringalocado porToString().
Reutilizar o mesmo StringBuilder entre chamadas poupa apenas uma alocação. Usar um buffer de caráter alugado a partir de ArrayPool<char> é muito mais eficiente — reduz as chamadas subsequentes apenas à alocação para ToString().
Além disso, a StringBuilder capacidade não inclui um terminador nulo oculto, o que o interop tem sempre em conta. Isto é um erro comum, porque a maioria das APIs quer o tamanho do buffer incluindo o null. Isto pode resultar em alocações desperdiçadas ou desnecessárias e impede que o tempo de execução otimize o processo de marshaling para minimizar cópias.
Para obter mais informações, veja Parâmetros de String e CA1838: Evitar StringBuilder parâmetros para P/Invokes.