Cálculo de referencias predeterminado para cadenas

Las clases System.String y System.Text.StringBuilder tienen un comportamiento de cálculo de referencias similar.

Las referencias de cadenas se calculan como un tipo BSTR de estilo COM o como una matriz de caracteres que termina en una referencia null (Nothing en Visual Basic). Las referencias de los caracteres con la cadena pueden calcularse como Unicode o ANSI, o en función de la plataforma: Unicode en Microsoft Windows NT, Windows 2000 y Windows XP; ANSI en Windows 98 y Windows Millennium (Windows Me).

En este tema se ofrece la siguiente información sobre el cálculo de referencias de tipos de cadena:

  • Cadenas utilizadas en interfaces

  • Cadenas utilizadas en invocación de plataforma

  • Cadenas utilizadas en estructuras

  • Búferes de cadenas de longitud fija

Cadenas utilizadas en interfaces

En la tabla siguiente se muestran las opciones de cálculo de referencias para el tipo de datos String cuando se calculan las referencias como un argumento de método para código no administrado. El atributo MarshalAsAttribute proporciona varios valores de la enumeración UnmanagedType para calcular las referencias de cadenas a interfaces COM.

Tipo de enumeración

Descripción de formato no administrado

UnmanagedType.BStr (valor predeterminado)

Un BSTR de estilo COM con una longitud fija y caracteres Unicode.

UnmanagedType.LPStr

Puntero a una matriz de caracteres ANSI terminada en null.

UnmanagedType.LPWStr

Puntero a una matriz de caracteres Unicode terminada en null.

Esta tabla se aplica a las cadenas. Sin embargo, para StringBuilder, las únicas opciones que se permiten son UnmanagedType.LPStr y UnmanagedType.LPWStr.

En el ejemplo siguiente se muestran las cadenas declaradas en la interfaz 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);
);

En el ejemplo siguiente se muestra la interfaz correspondiente descrita en una 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);
);

Cadenas utilizadas en invocación de plataforma

La invocación de plataforma copia argumentos de cadena y convierte el formato de .NET Framework (Unicode) al formato no administrado de la plataforma. Las cadenas son inmutables y no se copian de la memoria no administrada a la memoria administrada al volver de la llamada.

En la tabla siguiente se muestran las opciones de cálculo de referencias para cadenas cuando las referencias se calculan como un argumento de método de una llamada de invocación de plataforma. El atributo MarshalAsAttribute proporciona varios valores de la enumeración UnmanagedType para calcular las referencias de cadenas.

Tipo de enumeración

Descripción de formato no administrado

UnmanagedType.AnsiBStr

Un BSTR de estilo COM con una longitud fija y caracteres ANSI.

UnmanagedType.BStr

Un BSTR de estilo COM con una longitud fija y caracteres Unicode.

UnmanagedType.LPStr

Puntero a una matriz de caracteres ANSI terminada en null.

UnmanagedType.LPTStr (valor predeterminado)

Puntero a una matriz de caracteres dependientes de la plataforma terminada en null.

UnmanagedType.LPWStr

Puntero a una matriz de caracteres Unicode terminada en null.

UnmanagedType.TBStr

Un BSTR de estilo COM con una longitud fija y caracteres que dependen de la plataforma.

VBByRefStr

Un valor que permite a Visual Basic .NET cambiar una cadena del código no administrado y reflejar los resultados en código administrado. Este valor sólo es compatible con la invocación de plataforma.

Esta tabla se aplica a las cadenas. Sin embargo, para StringBuilder, las únicas opciones que se permiten son LPStr, LPTStr y LPWStr.

En la definición de tipo siguiente se muestra el uso correcto de MarshalAsAttribute en llamadas de invocación de plataforma.

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

Cadenas utilizadas en estructuras

Las cadenas son miembros válidos de las estructuras; sin embargo, los búferes StringBuilder no son válidos en las estructuras. En la tabla siguiente se muestran las opciones de cálculo de referencias para el tipo de datos String cuando se calculan las referencias del tipo como un campo. El atributo MarshalAsAttribute proporciona varios valores de la enumeración UnmanagedType para calcular las referencias de cadenas a un campo.

Tipo de enumeración

Descripción de formato no administrado

UnmanagedType.BStr

Un BSTR de estilo COM con una longitud fija y caracteres Unicode.

UnmanagedType.LPStr

Puntero a una matriz de caracteres ANSI terminada en null.

UnmanagedType.LPTStr

Puntero a una matriz de caracteres dependientes de la plataforma terminada en null.

UnmanagedType.LPWStr

Puntero a una matriz de caracteres Unicode terminada en null.

UnmanagedType.ByValTStr

Matriz de caracteres de longitud fija; el tipo de la matriz se determina mediante el juego de caracteres de la estructura contenedora.

El tipo ByValTStr se utiliza para matrices insertadas de caracteres de longitud fija que aparecen dentro de una estructura. Otros tipos se aplican a referencias de cadena que se encuentran en estructuras que contienen punteros a cadenas.

El argumento CharSet del atributo StructLayoutAttribute que se aplica a la estructura contenedora determina el formato de los caracteres de cadena en las estructuras. Las siguientes estructuras de ejemplo contienen referencias de cadena y cadenas en línea, así como caracteres ANSI, Unicode y dependientes de la plataforma.

Representación de biblioteca de tipos

struct StringInfoA {
   char *    f1;
   char      f2[256];
};
struct StringInfoW {
   WCHAR *   f1;
   WCHAR     f2[256];
   BSTR      f3;
};
struct StringInfoT {
   TCHAR *   f1;
   TCHAR     f2[256];
};

En el ejemplo de código siguiente se muestra cómo utilizar el atributo MarshalAsAttribute para definir la misma estructura en diferentes formatos.

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

Búferes de cadenas de longitud fija

En algunas circunstancias, para manipular un búfer de caracteres de longitud fija se debe pasar a código no administrado. No basta sólo con pasar una cadena en este caso, ya que el destinatario de la llamada no puede modificar el contenido del búfer pasado. Aunque se pase la cadena por referencia, no hay modo de inicializar el búfer con un tamaño determinado.

La solución consiste en pasar un búfer StringBuilder como argumento en lugar de una cadena. El destinatario de la llamada puede desreferenciar un generador StringBuilder y modificarlo, siempre que no supere la capacidad del generador StringBuilder. También puede inicializarse con una longitud fija. Por ejemplo, si inicializa un búfer StringBuilder para una capacidad de N, el contador de referencias proporcionará un búfer de caracteres con el tamaño N+1. El incremento en uno se debe a que la cadena no administrada tiene un terminador null, a diferencia de StringBuilder, que no lo tiene.

Por ejemplo, la función GetWindowText de la API Win32 de Microsoft (definida en Windows.h) es un búfer de caracteres de longitud fija que debe pasarse a código no administrado para que pueda manipularse. LpString apunta a un búfer asignado a un llamador de tamaño nMaxCount. Se espera que el llamador asigne el búfer y establezca el argumento nMaxCount al tamaño del búfer asignado. En el código siguiente se muestra la declaración de la función GetWindowText tal y como se define en Windows.h.

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

El destinatario de la llamada puede desreferenciar un generador StringBuilder y modificarlo, siempre que no supere la capacidad del generador StringBuilder. En el ejemplo de código siguiente se muestra cómo puede inicializarse StringBuilder con una longitud fija.

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

Vea también

Conceptos

Tipos que pueden o que no pueden representarse como bits o bytes

Atributos direccionales

Copiar y fijar

Otros recursos

Comportamiento del cálculo de referencias predeterminado