2009-05-15 26 views
7

Ho un'applicazione Java che utilizza una DLL C++ tramite JNI. Alcuni dei metodi della DLL accettano argomenti stringa e alcuni di essi restituiscono anche oggetti contenenti stringhe.Passaggio di stringhe a doppio byte (WCHAR) da C++ a Java tramite JNI

Attualmente il DLL non supporta Unicode, così la gestione delle stringhe è piuttosto facile:

  • Java chiama String.getBytes() e passa la matrice risultante alla DLL, che tratta semplicemente i dati come char *.
  • DLL usa NewStringUTF() per creare una jstring da un const char *.

Sono ora in fase di modifica della DLL per supportare Unicode, passando all'utilizzo del tipo TCHAR (che quando UNICODE è definito utilizza il tipo di dati WCHAR di Windows). La modifica della DLL sta andando bene, ma non sono sicuro di come modificare la parte JNI del codice.

L'unica cosa che mi viene in mente in questo momento è questa:

  • Java chiama String.getBytes (String charsetName) e passa la matrice risultante alla DLL, che tratta i dati come un wchar_t *.
  • DLL non crea più stringhe, ma passa jbyteArrays con i dati di stringa non elaborati. Java utilizza il costruttore String (byte [] bytes, String charsetName) per creare effettivamente la stringa.

L'unico problema con questo metodo è che non sono sicuro del nome del set di caratteri da utilizzare. I WCHAR sono lunghi 2 byte, quindi sono abbastanza sicuro che sia UTF-16, ma ci sono 3 possibilità sul lato java. UTF-16, UTF-16BE e UTF-16LE. Non ho trovato alcuna documentazione che mi indichi quale sia l'ordine dei byte, ma posso probabilmente capirlo da alcuni test rapidi.

C'è un modo migliore? Se possibile mi piacerebbe continuare a costruire gli oggetti jstring all'interno della DLL, in questo modo non dovrò modificare nessuno degli usi di quei metodi. Tuttavia, il metodo JNI NewString non accetta un identificatore del set di caratteri.

risposta

7

This answer suggerisce che il byte-ordinamento delle WCHARS non è garantito ...

Dal momento che siete su Windows si potrebbe provare WideCharToMultiByte per convertire i WCHARs in UTF-8 e quindi utilizzare il codice JNI esistente.

È necessario prestare attenzione utilizzando WideCharToMultiByte a causa della possibilità di sovraccarichi del buffer nel parametro lpMultiByteStr. Per ovviare a ciò è necessario chiamare la funzione due volte, prima con lpMultiByteStr impostato su NULL e cbMultiByte impostato su zero - questo restituirà la lunghezza del bufferrichiesto senza tentare di scriverlo. Una volta ottenuta la lunghezza, è possibile allocare un buffer della dimensione richiesta e richiamare di nuovo la funzione.

codice Esempio:

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, non aveva considerato di convertire prima la stringa wide-char in una stringa utf-8. Presumo che per usare quel metodo vorrei l'argomento della codepage CP_UTF8? – Herms

+0

Sì, l'argomento CodePage deve essere CP_UTF8. –

+0

Grazie per il codice di esempio. Non ero completamente sicuro di un paio di questi argomenti, ed è bello avere conferma che ho indovinato. :) – Herms

2

ho trovato a little faq circa il byte order mark. Anche da queste domande frequenti:

UTF-16 e UTF-32 utilizzano unità di codice lunghe rispettivamente due e quattro byte. Per queste UTF, ci sono tre sotto-sapori: BE, LE e unmarked.La forma BE utilizza la serializzazione di byte big-endian (il byte più significativo prima), la forma LE utilizza la serializzazione di byte little-endian (prima il byte meno significativo) e la forma non marcata utilizza la serializzazione di byte big-endian per impostazione predefinita, ma può includere un ordine di byte segnare all'inizio per indicare la serializzazione dei byte effettiva utilizzata.

Sto assumendo dal lato Java che l'UTF-16 proverà a trovare questo BOM e gestirà correttamente la codifica. Sappiamo tutti quanto le ipotesi possono essere pericolosi ...

Modifica a causa del commento:

Microsoft utilizza UTF16 little endian. Java UTF-16 tenta di interpretare la BOM. Quando manca una distinta base, per impostazione predefinita UTF-16BE. Le varianti BE e LE ignorano il BOM.

+0

Oh, lo so quali sono le diverse versioni UTF-16, non so quale Windows stia effettivamente utilizzando per WCHAR. – Herms