Передача двухбайтовых (WCHAR) строк из C ++ в Java через JNI - PullRequest
7 голосов
/ 15 мая 2009

У меня есть приложение Java, которое использует C ++ DLL через JNI. Несколько методов DLL принимают строковые аргументы, а некоторые возвращают объекты, которые также содержат строки.

В настоящее время DLL не поддерживает Unicode, поэтому обработка строк довольно проста:

  • Java вызывает String.getBytes () и передает полученный массив в DLL, которая просто обрабатывает данные как символ *.
  • DLL использует NewStringUTF () для создания jstring из const char *.

Сейчас я нахожусь в процессе изменения DLL для поддержки Unicode, переключаясь на использование типа TCHAR (который при определении UNICODE использует тип данных WCHAR в Windows). Модификация DLL идет хорошо, но я не уверен, как изменить часть кода JNI.

Единственное, о чем я могу думать сейчас, это:

  • Java вызывает String.getBytes (String charsetName) и передает полученный массив в DLL, которая обрабатывает данные как wchar_t *.
  • DLL больше не создает Strings, а вместо этого передает jbyteArrays с необработанными строковыми данными. Java использует конструктор String (byte [] bytes, String charsetName) для фактического создания String.

Единственная проблема этого метода в том, что я не уверен, какое имя кодировки использовать. WCHAR длиной 2 байта, так что я уверен, что это UTF-16, но на стороне java есть 3 возможности. UTF-16, UTF-16BE и UTF-16LE. Я не нашел никакой документации, в которой говорилось бы, каков порядок байтов, но я, вероятно, смогу выяснить это после некоторого быстрого тестирования.

Есть ли лучший способ? Если возможно, я бы хотел продолжить создание объектов jstring в DLL, так как таким образом мне не придется изменять использование этих методов. Однако метод JNI NewString не принимает идентификатор кодировки.

Ответы [ 2 ]

7 голосов
/ 15 мая 2009

Этот ответ предполагает, что порядок следования байтов в WCHARS не гарантируется ...

Поскольку вы работаете в Windows, вы можете попробовать <a href="http://msdn.microsoft.com/en-us/library/aa450989.aspx" rel="nofollow noreferrer">WideCharToMultiByte</a> преобразовать WCHAR в UTF-8, а затем использовать существующий код JNI.

Вам нужно быть осторожным при использовании WideCharToMultiByte из-за возможности переполнения буфера в параметре lpMultiByteStr. Чтобы обойти это, вы должны вызвать функцию дважды, сначала с lpMultiByteStr, установленным в NULL и cbMultiByte, установленным в ноль - это вернет длину необходимого буфера 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
}
2 голосов
/ 15 мая 2009

Я нашел небольшой ответ о метке порядка байтов. Также из этого FAQ:

UTF-16 и UTF-32 используют кодовые единицы длиной два и четыре байта соответственно. Для этих UTF существует три подвкуса: BE, LE и без опознавательных знаков. Форма BE использует сериализацию байтов с прямым порядком байтов (сначала старший значащий байт), форма LE использует сериализацию байтов с младшим порядком байтов (сначала младший байт), а по умолчанию неотмеченная форма использует сериализацию байтов с прямым порядком байтов по умолчанию, но может включать порядок байтов отметьте в начале, чтобы указать фактическую использованную сериализацию байтов.

Я предполагаю, что на стороне Java UTF-16 попытается найти эту спецификацию и правильно разобраться с кодировкой. Мы все знаем, насколько опасными могут быть предположения ...

Редактировать из-за комментария:

Microsoft использует UTF16 с прямым порядком байтов. Java UTF-16 пытается интерпретировать спецификацию. При отсутствии спецификации по умолчанию используется UTF-16BE. Варианты BE и LE игнорируют спецификацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...