2009-05-15 39 views
7

Tôi có một ứng dụng Java sử dụng DLL C++ qua JNI. Một vài phương thức của DLL lấy các đối số chuỗi và một số trong số chúng trả về các đối tượng có chứa các chuỗi.Truyền chuỗi hai byte (WCHAR) từ C++ sang Java qua JNI

Hiện nay DLL không hỗ trợ Unicode, vì vậy việc xử lý chuỗi là khá dễ dàng:

  • Java gọi String.getBytes() và vượt qua các mảng kết quả vào DLL, mà chỉ đơn giản xử lý các dữ liệu như một char *.
  • DLL sử dụng NewStringUTF() để tạo một chuỗi từ một const char *.

Tôi hiện đang trong quá trình sửa đổi DLL để hỗ trợ Unicode, chuyển sang sử dụng loại TCHAR (khi UNICODE được định nghĩa sử dụng kiểu dữ liệu WCHAR của Windows). Sửa đổi các DLL đang diễn ra tốt đẹp, nhưng tôi không chắc chắn làm thế nào để sửa đổi phần JNI của mã.

Điều duy nhất tôi có thể nghĩ ngay bây giờ là thế này:

  • Java gọi String.getBytes (String charsetName) và vượt qua các mảng kết quả vào DLL, mà xử lý các dữ liệu như một wchar_t *.
  • DLL không còn tạo chuỗi, nhưng thay vào đó chuyển jbyteArrays bằng dữ liệu chuỗi thô. Java sử dụng hàm tạo String (byte [] bytes, String charsetName) để tạo chuỗi.

Vấn đề duy nhất với phương pháp này là tôi không chắc chắn nên sử dụng tên bộ ký tự nào. WCHARs dài 2 byte, vì vậy tôi khá chắc chắn đó là UTF-16, nhưng có 3 vị trí ở phía java. UTF-16, UTF-16BE và UTF-16LE. Tôi đã không tìm thấy bất kỳ tài liệu nào cho tôi biết thứ tự byte là gì, nhưng tôi có thể tìm ra nó từ một số thử nghiệm nhanh.

Có cách nào tốt hơn không? Nếu có thể tôi muốn tiếp tục xây dựng các đối tượng jstring trong DLL, như vậy tôi sẽ không phải sửa đổi bất kỳ tập quán nào của các phương thức đó. Tuy nhiên, phương thức NewString JNI không nhận định danh ký tự.

Trả lời

7

This answer gợi ý rằng các byte-Trật tự của WCHARS không được bảo đảm ...

Vì bạn đang ở trên Windows bạn có thể thử WideCharToMultiByte để chuyển đổi WCHARs sang UTF-8 và sau đó sử dụng mã JNI hiện tại của bạn.

Bạn sẽ cần phải cẩn thận khi sử dụng WideCharToMultiByte do khả năng tràn bộ đệm trong thông số lpMultiByteStr. Để làm tròn điều này, bạn nên gọi hàm hai lần, trước tiên với lpMultiByteStr đặt thành NULLcbMultiByte đặt thành 0 - điều này sẽ trả về độ dài của bộ đệm yêu cầu lpMultiByteStr mà không cố ghi vào đó. Một khi bạn có chiều dài, bạn có thể phân bổ một bộ đệm của kích thước cần thiết và gọi lại hàm.

Ví dụ mã:

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, đã không xem xét việc chuyển đổi chuỗi char rộng thành chuỗi utf-8 trước tiên. Tôi giả sử sử dụng phương pháp đó tôi muốn đối số mã CP_UTF8? – Herms

+0

Có, đối số CodePage phải là CP_UTF8. –

+0

Cảm ơn mã ví dụ. Tôi đã không hoàn toàn chắc chắn về một vài lý lẽ đó, và thật tuyệt khi được xác nhận rằng tôi đoán đúng. :) – Herms

2

tôi thấy a little faq về dấu thứ tự byte. Cũng từ Câu hỏi thường gặp đó:

UTF-16 và UTF-32 sử dụng đơn vị mã tương ứng hai và bốn byte. Đối với những UTF này, có ba hương vị phụ: BE, LE và không được đánh dấu.Dạng BE sử dụng tuần tự byte lớn (byte quan trọng nhất trước), dạng LE sử dụng tuần tự byte nhỏ (ít nhất là byte quan trọng đầu tiên) và biểu mẫu chưa được đánh dấu sử dụng tuần tự byte lớn theo mặc định, nhưng có thể bao gồm một thứ tự byte đánh dấu vào đầu để cho biết tuần tự byte thực được sử dụng.

Tôi giả định ở phía java UTF-16 sẽ cố gắng tìm BOM này và xử lý đúng cách mã hóa. Chúng ta đều biết các giả định nguy hiểm như thế nào có thể là ...

Chỉnh sửa vì nhận xét:

Microsoft sử dụng UTF16 ít endian. Java UTF-16 cố gắng diễn giải BOM. Khi thiếu BOM, nó mặc định là UTF-16BE. Các biến thể BE và LE bỏ qua BOM.

+0

Ồ, tôi biết các phiên bản UTF-16 khác nhau là gì, tôi chỉ không biết Windows nào thực sự đang sử dụng cho WCHAR. – Herms

Các vấn đề liên quan