
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.9.175] |
![]() |
|
![]() |
|
|
Доброго времени суток.
Ситуация следующая. Существует сторонняя dll-библиотека без доступа к исходникам(STF.dll) и программа на java. Необходимо использовать функции библиотеки в программе с помощью JNI(обязательное условие). Сейчас подключение выглядит так. java: ![]() ![]() public class DllUse { private static String dirPath; static{ try{ dirPath = ..путь..; ///путь до библиотеки System.load(dirPath+"libDllUse.dll"); } catch(Exception ex) {} } public static int connectDl(){ return new DllUse().connectDll(dirPath+"STF.dll"); } private native int connectDll(String path); } jni прослойка libDllUse.с ![]() ![]() static HINSTANCE mod; JNIEXPORT jint JNICALL Java_DllUse_connectDll (JNIEnv *env, jobject obj, jstring path) { const char* ipstr=(*env)->GetStringUTFChars(env, path, NULL); wprintf((const wchar_t *)ipstr); //стр1 mod=LoadLibrary((const wchar_t *)ipstr); (*env)->ReleaseStringUTFChars(env, path, ipstr1); if (mod == NULL) { return -1;} return 1; } После вызова функция connectDl() возвращает -1, следовательно подключение не происходит. стр1 выводит: ..путь..\STF.dll_Разнообразная_Абракадабра Как скормить jstring path функции ![]() ![]() HMODULE WINAPI LoadLibrary(_In_ LPCTSTR lpFileName); Или что ещё можно придумать в рамках заданных условий? Буду рада любой помощи. PS. ![]() ![]() LoadLibrary(TEXT("..путь..\STF.dll")); |
Сообщ.
#2
,
|
|
|
Цитата Anka-BOIN @ public static int connectDl(){ return new DllUse().connectDll(dirPath+"STF.dll"); } Пробовали в си-стиле делать? Для wchar_t* ![]() ![]() public static int connectDl(){ return new DllUse().connectDll(dirPath+"STF.dll\0x00"); } Добавлено В общем, попробую объяснить свою позицию. В яве строки (String) более правильные с точки зрения человека, то есть, имеется адрес начала строки, и длинна строки. В сях, ваш wchar_t* имеет адрес начала, но, не имеет длинны. Отсюда, чтобы узнать длину строки в яве, достаточно прочитать эту длину в области, недалеко от адреса начала массива данных (собственно, самой строки), как это сделано более подробно - это другой вопрос. Для того чтобы узнать длинну строки в сях, введено такое понятие как терминатор строки, суть в следующем, обращаемся по адресу начала строки, далее, смещаем курсор, читая по одному (или несколько) байту до тех пор, пока не натолкнёмся на \0x00 терминатор (если читаем по несколько байт, скорее всего, достаточно того случая когда первый байт 0x00, но, могу ошибаться, и важно заполнить весь буфер \0x00 терминаторами). Вот и получается - вы в памяти резервируете явой строку dirPath+"libDllUse.dll", сишный код начинает искать конец этой строки, и пропускает курсор далеко за вашу строку, пока где то там, в памяти не наткнётся на \0x00 терминатор. |
![]() |
Сообщ.
#3
,
|
|
Цитата Anka-BOIN @ jni прослойка libDllUse.с ![]() ![]() static HINSTANCE mod; JNIEXPORT jint JNICALL Java_DllUse_connectDll (JNIEnv *env, jobject obj, jstring path) { const char* ipstr=(*env)->GetStringUTFChars(env, path, NULL); wprintf((const wchar_t *)ipstr); // стр1 mod=LoadLibrary((const wchar_t *)ipstr); (*env)->ReleaseStringUTFChars(env, path, ipstr1); if (mod == NULL) { return -1;} return 1; } После вызова функция connectDl() возвращает -1, следовательно подключение не происходит. стр1 выводит: ..путь..\STF.dll_Разнообразная_Абракадабра Я, может, чего-то не понимаю в C, но каким магическим образом, по-твоему, массив UTF-8 байт (char* ipstr), должен преобразоваться в массив wchar_t* обычным тайпкастом? https://maxammann.github.io/2015/01/11/c-utf8-to-wchar/ Добавлено Цитата VisualProg @ В общем, попробую объяснить свою позицию. В яве строки (String) более правильные с точки зрения человека, то есть, имеется адрес начала строки, и длинна строки. В сях, ваш wchar_t* имеет адрес начала, но, не имеет длинны. Отсюда, чтобы узнать длину строки в яве, достаточно прочитать эту длину в области, недалеко от адреса начала массива данных (собственно, самой строки), как это сделано более подробно - это другой вопрос. Для того чтобы узнать длинну строки в сях, введено такое понятие как терминатор строки, суть в следующем, обращаемся по адресу начала строки, далее, смещаем курсор, читая по одному (или несколько) байту до тех пор, пока не натолкнёмся на \0x00 терминатор (если читаем по несколько байт, скорее всего, достаточно того случая когда первый байт 0x00, но, могу ошибаться, и важно заполнить весь буфер \0x00 терминаторами). Вот и получается - вы в памяти резервируете явой строку dirPath+"libDllUse.dll", сишный код начинает искать конец этой строки, и пропускает курсор далеко за вашу строку, пока где то там, в памяти не наткнётся на \0x00 терминатор. Судя по этой информации: Цитата UTF-8 strings are always terminated with the '\0' character, whereas Unicode strings are not. To find out how many bytes are needed to represent a jstring in the UTF-8 format, JNI programmers can either call the ANSI C function strlen on the result of GetStringUTFChars, or call the JNI function GetStringUTFLength on the jstring reference directly. — https://stackoverflow.com/a/16700206 дело не в '\0'. |
Сообщ.
#4
,
|
|
|
Цитата korvin @ Я, может, чего-то не понимаю в C, но каким магическим образом, по-твоему, массив UTF-8 байт (char* ipstr), должен преобразоваться в массив wchar_t* обычным тайпкастом? А в чём может быть проблема? Хотите сказать, что в wchar_t байты с младшими и старшими разрядами перевёрнуты? |
![]() |
Сообщ.
#5
,
|
|
Цитата VisualProg @ А в чём может быть проблема? Хотите сказать, что в wchar_t байты с младшими и старшими разрядами перевёрнуты? Хочу сказать, что UTF-8 — мультибайтовая кодировка: один code point может быть представлен как одним, так и шестью байтами. Да что там говорить, даже если взять любую однобайтовую кодировку, каким образом, по-твоему, простой сишный тайпкаст должен преобразовать массив char'ов в массив wchar_t'ов, которые, в винде представляют двухбайтный UTF-16, а в *nix'ах могут и UTF-32? Ну и byte-order, конечно, тоже. Например: ![]() ![]() import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.function.Function; import java.util.stream.Stream; public final class Charsets { public static void main(String[] args) { printBytes("Hello"); printBytes("Привет"); printBytes("こんにちは"); } private static void printBytes(String s) { Stream.of(StandardCharsets.US_ASCII, StandardCharsets.UTF_8, StandardCharsets.UTF_16) .map(showBytes(s)) .forEach(System.out::println); System.out.println(); } private static Function<Charset, String> showBytes(String s) { return cs -> String.format("%s in %10s = %s", s, cs, Arrays.toString(s.getBytes(cs))); } } получаем: ![]() ![]() Hello in US-ASCII = [72, 101, 108, 108, 111] Hello in UTF-8 = [72, 101, 108, 108, 111] Hello in UTF-16 = [-2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111] Привет in US-ASCII = [63, 63, 63, 63, 63, 63] Привет in UTF-8 = [-48, -97, -47, -128, -48, -72, -48, -78, -48, -75, -47, -126] Привет in UTF-16 = [-2, -1, 4, 31, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66] こんにちは in US-ASCII = [63, 63, 63, 63, 63] こんにちは in UTF-8 = [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95, -29, -127, -81] こんにちは in UTF-16 = [-2, -1, 48, 83, 48, -109, 48, 107, 48, 97, 48, 111] Вот теперь представь, что у тебя char* utf8 = [72, 101, 108, 108, 111], а ты его кастишь к wchar_t* utf16 = (wchar_t*) utf8, берёшь utf16[0] и получаешь, например (в зависимости от byte order), (72<<8)+101 = 18533. Какой это символ в Unicode? Дальше читаешь utf16[1], получаешь (108<<8)+108, потом utf16[2] -> (111<<8)+0 (тот самый '\0'), а дальше выход за пределы массива, UB, segfault и всё такое. Да, ты прав, что '\0' пропускается, но причины другие. Вот попробуй: ![]() ![]() #include <wchar.h> #include <stdio.h> int main(void) { char* s = "Hello"; printf("%s\n", s); wprintf((const wchar_t*) s); printf("\n--------\n"); const wchar_t* ws = (const wchar_t*) s; for (int i = 0; i < 5; i++) { printf("%d ", ws[i]); } printf("\n--------\n"); return 0; } Я удивлён, что у него вообще wprintf что-то напечатал внятное. Ideone, с 4-хбайтным wchar_t: ![]() ![]() Hello -------- 1819043144 755630191 757935405 2960685 2122789 -------- |
Сообщ.
#6
,
|
|
|
Цитата korvin @ wprintf((const wchar_t*) s); Скорее всего, я не правильно объяснился. Вот про что я имел ввиду. ![]() ![]() #include <wchar.h> #include <stdio.h> int main(void) { char* s1 = "\0H\0e\0l\0l\0o"; char* s2 = "H\0e\0l\0l\0o\0"; wprintf((const wchar_t*) s1); wprintf((const wchar_t*) s2); printf("\n--------\n"); const wchar_t* ws1 = (const wchar_t*) s1; for (int i = 0; i < 5; i++) { printf("%d ", ws1[i]); } printf("\n"); const wchar_t* ws2 = (const wchar_t*) s2; for (int i = 0; i < 5; i++) { printf("%d ", ws2[i]); } printf("\n--------\n"); return 0; } Я просто не поверю что ява кодирует символы одним байтом) И на этом примере вижу, что действительно ничего не получится, потому что я даже не подозревал что wchar_t 32 битный и использует 4 байта. |
![]() |
Сообщ.
#7
,
|
|
Цитата VisualProg @ Скорее всего, я не правильно объяснился. Вот про что я имел ввиду. Мне из этого не понятно, что ты имел в виду. Цитата VisualProg @ Я просто не поверю что ява кодирует символы одним байтом) Какая ява? java.lang.String хранит массив char (Java'вских char'ов, не Сишных), которые двухбайтный UTF-16. JNI'шный метод GetStringUTFChars, которым пользуется ТС, согласно документации на сайте, возврашает массив байтов (сишных char'ов) UTF-8, которая мультибайтовая, т.е. символы (юникодные code point'ы) там кодируются последовательностями от 1 до 6 байт, в зависимости от символа. Так, например, символы из ASCII кодируются одним байтом, символы кириллицы — двумя. В википедии есть же схема, ну. Сишный же wchar_t имеет размер (в винде) — 2 байта, следовательно массив UTF-8 char и массив UTF-16 wchar_t — не совместимы и кастить их нельзя, я же показал в какие последовательности байт преобразуются стринги, возьми UTF-8, подставь в char* s1 = …, и посмотри, что получится в wchar_t* ws1 = (wchar_t*) ws1; Цитата VisualProg @ даже не подозревал что wchar_t 32 битный и использует 4 байта. Его размер зависит от платформы (ОС) и, наверное, ещё чего-то. На MSDN можно найти указание, что в винде он 16-битный. |