2009-05-15 6 views
7

JNI経由でC++ DLLを使用するJavaアプリケーションがあります。いくつかのDLLのメソッドは文字列引数を取り、そのうちのいくつかは文字列も含むオブジェクトを返します。ダブルバイト(WCHAR)文字列をC++からJNI経由でJavaに渡す

現在DLLは、Unicodeをサポートしていないので、文字列の取り扱いがかなり容易である。

  • Javaは(するString.getBytesを呼び出し)と単にcharとしてデータを扱うDLLに得られた配列を渡し*。
  • DLLは、newStringUTF()を使用してconst char *からjstringを作成します。

TCHAR型(UNICODEがWindowsのWCHARデータ型を使用するように定義されている場合)を使用して、UnicodeをサポートするようにDLLを変更しています。 DLLの修正はうまくいっていますが、コードのJNI部分を変更する方法がわかりません。

私は今考えることができる唯一の事はこれです:

  • JavaがするString.getBytes(文字列たcharsetName)を呼び出し、* wchar_t型としてデータを扱うDLLへの結果の配列を渡します。
  • DLLは文字列を作成するのではなく、生の文字列データとともにjbyteArraysを渡します。 JavaはString(byte [] bytes、String charsetName)コンストラクタを使用して実際にStringを作成します。

このメソッドの唯一の問題は、使用する文字セット名がわからないことです。 WCHARは2バイトの長さなので、私はそれがUTF-16だと確信していますが、Java側には3つの可能性があります。 UTF-16、UTF-16BE、およびUTF-16LEがあります。私はバイトオーダーが何であるか教えてくれるドキュメンテーションを見つけられませんでしたが、いくつかの簡単なテストからそれを理解することができます。

良い方法がありますか?可能であれば、DLL内でjstringオブジェクトを構築していきたいと思います。そうすれば、それらのメソッドの使用法を変更する必要はありません。ただし、NewString JNIメソッドは文字セット識別子を取りません。

答えて

7

This answerは、Windows上にあるので、あなたがUTF-8にWCHARsを変換するWideCharToMultiByteをしようとしてから、既存のJNIコードを使用することができます

... WCHARSのバイト順序が保証されていないことを示唆しています。

パラメータでバッファオーバーランが発生する可能性があるため、WideCharToMultiByteを使用する場合は注意が必要です。これを実現するには、最初にlpMultiByteStrNULLに設定し、cbMultiByteをゼロに設定して関数を2回呼び出す必要があります。これは、書き込みを試みずに必要なlpMultiByteStrバッファの長さを返します。長さを取得したら、必要なサイズのバッファを割り当てて、関数を再度呼び出すことができます。

例コード:

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は、最初にワイド文字列をutf-8文字列に変換することを検討していませんでした。私はCP_UTF8コードページの引数を必要とするだろうそのメソッドを使用すると仮定? – Herms

+0

はい、CodePage引数はCP_UTF8でなければなりません。 –

+0

サンプルコードをありがとう。私はそれらの議論のいくつかについて完全には確信していませんでした。そして、私が正しいと推測していることを確認するのはうれしいです。 :) – Herms

2

私はバイトオーダーマークについてa little faqを見つけました。 また、よくある質問から:

UTF-16とUTF-32はそれぞれ2バイトと4バイトの長さのコード単位を使用します。これらのUTFには、BE、LE、およびマークされていない3つのサブフレーバーがあります。BE形式はビッグエンディアンのバイトシリアル化(最上位バイトが最初)を使用し、LE形式はリトルエンディアンバイトシリアル化(最下位バイトが最初に)を使用し、マークされていない形式はデフォルトでビッグエンディアンバイトシリアル化を使用しますが、使用された実際のバイトシリアル化を示すために先頭にマークを付けます。

私は、Java側でこのBOMを見つけようとし、エンコーディングを正しく処理しようとしています。我々は、すべてのため、コメントの...危険な仮定がいかに

編集を知っている:

マイクロソフトは、UTF16リトルエンディアンを使用しています。 Java UTF-16はBOMを解釈しようとします。 BOMがない場合、デフォルトはUTF-16BEになります。 BEおよびLEバリアントはBOMを無視します。

+0

ああ、私は知っています異なるUTF-16バージョンは何ですか、私はちょうどWCHARのためにどちらのWindowsが実際に使用しているのか分かりません。 – Herms