2009-05-15 17 views
7

Tengo una aplicación Java que utiliza una DLL C++ a través de JNI. Algunos de los métodos de la DLL toman argumentos de cadena y algunos de ellos también devuelven objetos que contienen cadenas.Transferencia de cadenas de doble byte (WCHAR) de C++ a Java a través de JNI

Actualmente la DLL no es compatible con Unicode, por lo que el manejo de cadenas es bastante fácil:

  • Java llama String.getBytes() y pasa la matriz resultante a la DLL, que simplemente trata los datos como un char *.
  • La DLL usa NewStringUTF() para crear una cadena js desde un const char *.

Ahora estoy en el proceso de modificar la DLL para que sea compatible con Unicode, cambiando al uso del tipo TCHAR (que cuando se define UNICODE usa el tipo de datos WCHAR de Windows). Modificar la DLL va bien, pero no estoy seguro de cómo modificar la parte JNI del código.

Lo único que se me ocurre en este momento es la siguiente:

  • Java llama String.getBytes (cadena charsetName) y pasa la matriz resultante a la DLL, que trata los datos como un wchar_t *.
  • DLL ya no crea cadenas, sino que pasa jbyteArrays con los datos de cadena sin formato. Java usa el constructor String (byte [] bytes, String charsetName) para crear realmente el String.

El único problema con este método es que no estoy seguro de qué nombre de conjunto de caracteres utilizar. Los WCHAR tienen 2 bytes de longitud, así que estoy bastante seguro de que es UTF-16, pero hay 3 posibilidades en el lado de Java. UTF-16, UTF-16BE y UTF-16LE. No he encontrado ninguna documentación que me diga cuál es el orden de bytes, pero probablemente pueda averiguarlo a partir de algunas pruebas rápidas.

¿Hay una manera mejor? Si es posible, me gustaría continuar construyendo los objetos jstring dentro de la DLL, ya que de esa manera no tendré que modificar ninguno de los usos de esos métodos. Sin embargo, el método NewString JNI no toma un identificador de juego de caracteres.

Respuesta

7

This answer sugiere que el byte-ordenamiento de wchars no está garantizada ...

Puesto que usted está en Windows podría intentar WideCharToMultiByte para convertir los wchars a UTF-8 y luego usar su código JNI existente.

Deberá tener cuidado al usar WideCharToMultiByte debido a la posibilidad de sobrepasamientos del búfer en el parámetro lpMultiByteStr. Para evitar esto, debe llamar a la función dos veces, primero con lpMultiByteStr establecido en NULL y cbMultiByte establecido en cero; esto devolverá la longitud del búfer lpMultiByteStr requerido sin intentar escribir en él. Una vez que tenga la longitud, puede asignar un búfer del tamaño requerido y llamar de nuevo a la función.

código Ejemplo:

int utf8_length; 

wchar_t* utf16 = ...; 

utf8_length = WideCharToMultiByte(
    CP_UTF8,   // Convert to UTF-8 
    0,     // No special character conversions required 
        // (UTF-16 and UTF-8 support the same characters) 
    utf16,    // UTF-16 string to convert 
    -1,    // utf16 is NULL terminated (if not, use length) 
    NULL,    // Determining correct output buffer size 
    0,     // Determining correct output buffer size 
    NULL,    // Must be NULL for CP_UTF8 
    NULL);    // Must be NULL for CP_UTF8 

if (utf8_length == 0) { 
    // Error - call GetLastError for details 
} 

char* utf8 = ...; // Allocate space for UTF-8 string 

utf8_length = WideCharToMultiByte(
    CP_UTF8,   // Convert to UTF-8 
    0,     // No special character conversions required 
        // (UTF-16 and UTF-8 support the same characters) 
    utf16,    // UTF-16 string to convert 
    -1,    // utf16 is NULL terminated (if not, use length) 
    utf8,    // UTF-8 output buffer 
    utf8_length,  // UTF-8 output buffer size 
    NULL,    // Must be NULL for CP_UTF8 
    NULL);    // Must be NULL for CP_UTF8 

if (utf8_length == 0) { 
    // Error - call GetLastError for details 
} 
+0

Hm, no había considerado convertir primero la cadena de caracteres anchos en una cadena utf-8. Supongo que para usar ese método, ¿me gustaría el argumento de la página de códigos CP_UTF8? – Herms

+0

Sí, el argumento de la página de códigos debe ser CP_UTF8. –

+0

Gracias por el código de ejemplo. No estaba completamente seguro acerca de algunos de esos argumentos, y es bueno tener la confirmación de que acerté. :) – Herms

2

he encontrado a little faq acerca de la marca de orden de bytes. También desde que FAQ:

UTF-16 y UTF-32 unidades de código uso que son dos y cuatro bytes de longitud, respectivamente. Para estos UTF, hay tres sub-sabores: BE, LE y sin marcar.La forma BE usa serialización de bytes grandes (el byte más significativo primero), la forma LE usa serialización de bytes little-endian (bytes menos significativos primero) y la forma no marcada usa serialización de bytes big-endian por defecto, pero puede incluir un orden de bytes marque al comienzo para indicar la serialización de byte real utilizada.

Supongo que por el lado java, el UTF-16 intentará encontrar esta lista de materiales y tratará adecuadamente con la codificación. Todos sabemos cómo pueden ser peligrosas suposiciones ...

Editar debido comentario:

Microsoft utiliza UTF16 Little Endian. Java UTF-16 intenta interpretar la lista de materiales. Cuando falta una lista de materiales, su valor predeterminado es UTF-16BE. Las variantes BE y LE ignoran la lista de materiales.

+0

Oh, lo sé cuáles son las diferentes versiones de UTF-16, simplemente no sé cuál es el que realmente usa Windows para WCHAR. – Herms

Cuestiones relacionadas