Как я могу развернуть смешанное приложение C ++ / Java (JNI)? - PullRequest
3 голосов
/ 03 февраля 2012

tl; плагин dr: C ++ должен вызывать библиотеку Java .jar. Как развернуть это для пользователей без лишней головной боли?

Я пишу плагин Qt для приложения Qt. Плагин должен совершать звонки в существующую библиотеку Java. Это необходимо для кросс-платформенной (Win, Mac, Linux) и архитектуры (32-битной и 64-битной Intel, без PPC).

Я получил простой пример компиляции и запуска JNI "hello world". Я обновил сценарий CMake на «find_package (JNI REQUIRED)» и т. Д., Чтобы он компилировался с заголовком jni.h и динамически связывался с библиотекой JVM.

По крайней мере, в Windows, CMake хорошо работает над поиском подходящей JVM для использования во время компиляции. Меня беспокоит поиск нужного JRE (jvm.dll и т. Д.) Во время выполнения, поскольку у меня меньше контроля над компьютерами пользователя.

Как это будет работать, когда я отправлю плагин своим пользователям? Им потребуется JRE для правильной архитектуры. Но это не означает, что каталог (и) JRE lib будут находиться на их пути. Если это не так, плагин просто вылетает и не загружается.

Также проблематично, что в Windows 64-битный JDK установил jvm.dll для:

C:\Program Files\Java\jre7\bin\server\jvm.dll

Но 32-битный JDK установил его на:

C:\Program Files (x86)\Java\jre7\bin\client\jvm.dll

Я понимаю разницу между PF и PFx86, но я не понимаю, что такое сервер / клиент. Это действительно разные JRE?

Будет ли это работать, если я скомпилировал / связал с одной версией JRE, а у пользователя другая версия?

Полагаю, в Linux / Mac все будет проще, но я еще не дошел до этого.

Любая помощь приветствуется. Я не привязан к использованию JNI, но не могу позволить себе компилятор за 2000 $, чтобы превратить Java в библиотеку нативного кода (не то, чтобы у меня был исходный код в любом случае), и я слышал, что gcj может не справиться с задачей (и, вероятно, не сильно поможет в Windows).

Ответы [ 3 ]

2 голосов
/ 07 февраля 2012

Некоторые более подробные примечания, которые нужно добавить к ответу @ hmjd, многие из этих деталей меня смутили.

Это командная строка, которую я использовал для компиляции и компоновки тестовой программы:

cl
  -I"C:\Program Files (x86)\Java\jdk1.7.0_02\include"
  -I"C:\Program Files (x86)\Java\jdk1.7.0_02\include\win32"
  /EHsc
  -MD
  Test.cpp
  jvm.lib
  delayimp.lib
  /link
  /LIBPATH:"C:\Program Files (x86)\Java\jdk1.7.0_02\lib"
  /DELAYLOAD:jvm.dll

Несколько замечаний:

  1. Включите пути к файлам заголовков (jni.h и т. Д.)
  2. Добавьте /EHsc, чтобы избежать этого предупреждения: "c:\ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ INCLUDE \ xlocale (323): предупреждение C4530: обработчик исключений C ++ используется, но семантика разматывания не включена. Укажите / EHsc "
  3. Еще необходимо включитьjvm.lib, даже если он загружен с задержкой.
  4. delayimp.lib должен быть включен.
  5. /LIBPATH должен идти после опции /link, чтобы компоновщик ее получил.Это путь к файлу .lib .
  6. /DELAYLOAD получает файл .dll , а не файл .lib!Если вы случайно даете ему файл .lib, вы не получаете полезного сообщения об ошибке, он просто говорит: «LINK: предупреждение LNK4199: /DELAYLOAD:jvm.lib игнорируется; импорт из jvm.lib не найден».

В файле .cpp, #include "windows.h", выясните, в каком каталоге есть jvm.dll, и выполните такой вызов:

std::string temp = "C:\\Program Files (x86)\\Java\\jdk1.7.0_02\\jre\\bin\\client";
SetDllDirectory(temp.c_str());

Сделайте это перед вызовом любой функции из библиотеки., поэтому, когда вызывается LoadLibrary (), он знает, где найти DLL.Альтернативой является изменение переменной PATH с помощью SetEnvironmentVariable(), но SetDllDirectory() кажется лучшим выбором.

Где-то в вашем коде запуска добавьте код, подобный этому:

__try
{
  // call a function from the DLL to make sure it can be loaded
  ::JNI_CreateJavaVM(...);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
  // If not, fail
}

Лучше всего поместить это в свою собственную функцию где-нибудь, поскольку материал __try / __except Структурная обработка исключений (SEH) не допускает совместного использования функции с кодом, который может выполнять раскручивание объекта.

Без SEHКроме того, программа просто падает, если DLL не может быть найдена.

Некоторые полезные ссылки:

1 голос
/ 03 февраля 2012

Возможное решение только для Windows.

Сборка плагина QT с использованием функции отложенной загрузки DLL

См. DELAYLOAD о том, как это сделать, но он просто добавляет /DELAYLOAD:jvm.dll и Delayimp.lib к команде компоновщика. Это означает, что jvm.dll не будет загружен, когда подключаемый модуль QT загружен, но в тот момент, когда это требуется (обратите внимание, что не требует использования LoadLibrary() и GetProcAddress()). (я не знаю, есть ли подобная функция в Linux или Mac)

Предоставить механизм для информирования плагина QT о том, какую JRE использовать

Это может быть либо значение реестра, файл конфигурации или переменная среды специально для плагина (определенно не переменные среды JAVA_HOME или JRE_HOME, от которых могут зависеть другие приложения). Пример переменных среды:

  • DANQT_32_JRE_HOME = C: \ Program Files (x86) \ Java \ jre7 (для 32-разрядной JRE)
  • DANQT_64_JRE_HOME = C: \ Program Files \ Java \ jre7 (для 64-битной JRE)

Плагин QT Модифицирует свой путь

Перед тем, как плагин QT вызывает любые функции, зависящие от JRE, он изменяет свою переменную окружения PATH, вставляя, например, %DANQT_32_JRE_HOME%\bin\server;%DANQT_32_JRE_HOME%\bin\client; в начале значения для PATH. Это означает, что когда плагин QT выполняет свое первое действие, требующее JRE, он будет загружен из вставленных каталогов. (Различные переменные окружения для 64-битных). Что касается bin\server и bin\client, я понимаю, что они по сути одинаковы, но server выполняет больше при инициализации по причинам производительности во время выполнения.

Я не уверен в совместимости, если плагин QT был построен против JRE 6 и JRE 7 был установлен. Если есть проблемы с совместимостью, сделайте это обязательным требованием к установке или, если разрешено (я не уверен в законности), поставьте jvm.dll с плагином QT.

0 голосов
/ 06 марта 2012

Поскольку я не могу найти эквивалент /DELAYLOAD в Linux (спрашивается здесь: Как я могу заставить ленивую / отложенную загрузку работать в Linux? ), я застрял, используя dlopen(),Это не так плохо, как я думал, - я могу использовать описанную здесь технику: Альтернативы dlsym () и dlopen () в C ++ .На самом деле, похоже, что заголовок jni.h был разработан для этого подхода.Вероятно, то, что я должен был сделать в первую очередь, на Windows тоже.

...