2008-09-16 18 views
5

Tengo un objeto BSTR que me gustaría convertir para copiar a un objeto wchar__t. Lo complicado es que la longitud del objeto BSTR puede ser de unos pocos kilobytes a unos pocos cientos de kilobytes. ¿Existe una forma eficiente de copiar los datos? Sé que podría simplemente declarar una matriz wchar_t y siempre asignar la máxima cantidad posible de datos que alguna vez necesitaría contener. Sin embargo, esto significaría asignar cientos de kilobytes de datos para algo que posiblemente solo requiera unos pocos kilobytes. ¿Alguna sugerencia?¿Cómo se puede copiar eficientemente BSTR a wchar_t []?

Respuesta

5

Los objetos BSTR contienen un prefijo de longitud, por lo que encontrar la longitud es barato. Averigüe la longitud, asigne una nueva matriz lo suficientemente grande como para contener el resultado, procese en eso y recuerde liberarlo cuando haya terminado.

0

Use ATL, y CStringT luego puede simplemente usar el operador de asignación. O puede usar las macros USES_CONVERSION, estas usan la asignación de montón, por lo que estará seguro de no perder memoria.

4

Nunca hay necesidad de conversión. Un puntero BSTR apunta al primer carácter de la cadena y termina en nulo. La longitud se almacena antes del primer carácter en la memoria. BSTR s siempre son Unicode (UTF-16/UCS-2). Hubo en un momento algo llamado 'ANSI BSTR' - hay algunas referencias en API heredadas - pero puede ignorarlas en el desarrollo actual.

Esto significa que puede pasar un BSTR de forma segura a cualquier función esperando un wchar_t.

En Visual Studio 2008 puede obtener un error de compilación, porque BSTR se define como un puntero a unsigned short, mientras que wchar_t es un tipo nativo. Puede activar o desactivar el wchar_t según /Zc:wchar_t.

+1

wchar_t no está garantizado para ser exactamente del tamaño de un corto. – ben

+0

Creo que esta operación siempre es segura, pero no siempre da los resultados esperados. Un BSTR puede contener caracteres nulos en su cuerpo (de ahí el prefijo de longitud), mientras que una función que espera un wchar_t * interpretará el primer carácter nulo como el final de la cadena. – Martin

+8

No se puede "pasar un BSTR de forma segura a cualquier función esperando un wchar_t *". Compara SysStringLen (NULL) y wcslen (NULL). – Constantin

3

Una cosa a tener en cuenta es que las cadenas BSTR pueden, y a menudo lo hacen, contener nulos incorporados. Un nulo no significa el final de la cadena.

7

En primer lugar, es posible que en realidad no tenga que hacer nada en absoluto, si todo lo que tiene que hacer es leer el contenido. Un tipo BSTR es un puntero a una matriz wchar_t terminada en nulo ya. De hecho, si usted comprueba las cabeceras, se encuentra que BSTR se define esencialmente como:

typedef BSTR wchar_t*; 

Por lo tanto, el compilador no puede distinguir entre ellos, a pesar de que tienen una semántica diferente.

Hay dos advertencias importantes.

  1. Se supone que los BSTR son inmutables. Nunca debe cambiar el contenido de un BSTR después de que se haya inicializado. Si lo "cambia", debe crear uno nuevo, asigne el nuevo y suelte el anterior (si es el propietario).
    [ACTUALIZACIÓN: esto no es cierto; ¡lo siento! Puede modificar los BSTR en su lugar; Pocas veces he tenido la necesidad.]

  2. BSTR pueden contener caracteres nulos incorporados, mientras que las cadenas tradicionales C/C++ no lo son.

Si usted tiene una buena cantidad de control de la fuente de la BSTR, y se puede garantizar que el BSTR no tiene valores NULL incrustados, se puede leer desde el BSTR como si fuera un wchar_t y utilizar cuerdas convencionales métodos (wcscpy, etc.) para acceder a él. Si no, tu vida se vuelve más difícil. Deberá manipular siempre sus datos como más BSTR o como una matriz de wchar_t asignada dinámicamente. La mayoría de las funciones relacionadas con cadenas no funcionarán correctamente.

Supongamos que usted controla sus datos, o no se preocupe por NULL. Supongamos también que realmente necesita hacer una copia y no puede simplemente leer el BSTR existente directamente. En ese caso, se puede hacer algo como esto:

UINT length = SysStringLen(myBstr);  // Ask COM for the size of the BSTR 
wchar_t *myString = new wchar_t[lenght+1]; // Note: SysStringLen doesn't 
              // include the space needed for the NULL 

wcscpy(myString, myBstr);     // Or your favorite safer string function 

// ... 

delete myString; // Done 

Si está utilizando contenedores de clase para su BSTR, la envoltura debe tener una manera de llamar SysStringLen() para usted. Por ejemplo:

CComBString use .Length(); 
_bstr_t  use .length(); 

ACTUALIZACIÓN: Este es un buen artículo sobre el tema por alguien mucho más conocimiento que yo:
"Eric [Lippert]'s Complete Guide To BSTR Semantics"

ACTUALIZACIÓN: se ha sustituido strcpy() con wcscpy() en ejemplo

+0

AFAIK, BSTRs * no * se supone que son inmutables. Es por eso que no están declarados const *. – Constantin

+0

Hmmm ... No puedo encontrar ninguna referencia que respalde mi posición. ¿Qué estaba pensando? Voy a corregir eso. –

+0

¿no debería usar wcscpy en lugar de strcpy? – arolson101