Я написал программу на Java, ориентированную на Windows Vista и Windows 10.
Программа работает во встроенной среде выполнения Java, поэтому пользователю не нужно возиться с установкой Java, чтобы заставить ее работать, Цель этого проекта - загрузить пользовательский интерфейс -> запустить один установщик -> запустить программу и заставить его чувствовать себя как родной .Никаких других шагов от пользователя не требуется.
Для достижения этой цели мне нужно было написать собственный исполняемый файл на C ++, который вызывает JVM напрямую (а не вызывая java.exe), поэтому Windows TakBarфункция закрепления работает правильно - если я использую исполняемую оболочку, такую как launch4j , для передачи выполнения в java.exe в любой момент, тогда закрепление не работает так, как должно.Мой собственный исполняемый файл должен оставаться запущенным в течение всей программы, чтобы закрепление работало правильно.
Я написал собственную программу запуска C ++, которая использует JNI для вызова виртуальной машины Java через jvm.dll
вместо java.exe
, и она работает.Это 100-строчная прокладка (код внизу этого поста).Он импортирует только windows.h
и использует следующие функции: HINSTANCE
, GetProcAddress
, WinMain
.Он явно ссылается на встроенный jvm.dll
во время выполнения.
Однако, если я скомпилирую эту оболочку с помощью Visual Studio 2017, результирующий исполняемый файл зависит от того, какой пользователь установил распространяемый Visual C ++ 2015.Если этого не произойдет, программа выдаст ошибку «Не удается запустить, потому что VCRUNTIME140.dll отсутствует на вашем компьютере».
Я попытался скомпилировать эту программу, используя флаг / MT, а такжевыбрав «Использовать MFC в статической библиотеке» на странице общих свойств проекта Visual Studio. Это не имеет значения; я получаю одну и ту же ошибку независимо от этого.
Хотя многие пользователи Windows Vista / Windows 7 будутУ меня уже установлены среды выполнения, а некоторые могут и не быть, и мой приоритет для этого проекта - плавное, безошибочное взаимодействие пользователей от загрузки до установки.
У меня нет проблем с объединением дополнительных библиотек с моим проектом.У меня нет проблем с компиляцией с MinGW или Cygwin, если это имеет смысл. Единственное, что мне нужно, это какой-то гарантированный путь к download -> install -> run
для любой версии Windows от Vista до Windows 10.
Как создать исполняемый файлкоторая стоит отдельно или может быть распространена вместе с несколькими библиотеками dll, так что она автономна в Vista и nкогда-либо версия Windows?
Минимальный завершенный проверяемый пример: Вы можете скачать zip или сделать все вручную самостоятельно, выполнив следующие действия:
Шаг 1 - Инициализация: Создайте папку josh-problem
.
Шаг 2 - Загрузите JRE: Загрузите Windows 64-битный zip-файл Java 11 и поместитеон josh-problem/jre
такой, что содержимое josh-problem/jre
- это java-папки с именами lib
, legal
, jmods
, include
, conf
, bin
и т. д.
Шаг 3 - Настройка Java-программы: Создать josh-problem/src/net/joshuad/test/Main.java
.Сделать его содержимое:
package net.joshuad.test;
public class Main {
public static void main(String[] args) {
System.out.println ( "Hi from Java, launched via embedded jre, via native executable." );
}
}
Шаг 4 - Создать манифест Создать файл josh-problem\MANIFEST.MF
с содержанием в одну строку:
Main-Class: net.joshuad.test.Main
Шаг 5- Скомпилируйте и создайте main.jar : перейдите в папку josh-problem
в cmd.exe.Выполните следующие команды:
jre\bin\javac.exe -d bin src\net\joshuad\test\Main.java
jre\bin\jar.exe cfm main.jar MANIFEST.MF -C bin .
Шаг 6 - Подтвердите работу баночки : Запуститекоманда:
jre\bin\java.exe -jar main.jar
Вы должны увидеть вывод: «Привет из Java, запущен через встроенный jre, через собственный исполняемый файл.»
Шаг 7 -Скомпилируйте Native Launcher Создайте проект в Visual Studio 2017 и передайте ему один файл cpp с исходным кодом в самом низу этого поста.Добавьте в свои списки josh-problem\jre\include
и josh-problem\jre\include\win32
.
Шаг 8 - Попробуйте запустить - Переместите исполняемый файл вывода в josh-problem
.Попробуйте запустить исполняемый файл на чистой машине с Windows 7 с пакетом обновления 1.Это выдаст ошибку: «Программа не может запуститься, потому что VCRUNTIME140D.dll отсутствует на вашем компьютере.».
Шаг 9 - Запуск через java - На том же чистом компьютере с Windows 7 запустите программу, вызвав java.exe, она работает.
jre\bin\java.exe -jar main.jar
Шаг 10 - Установите распространяемый Visual C ++ и попробуйте наш exe снова - Загрузите и установите 2015 распространяемый Visual C ++ начистая машина Windows 7.Попробуйте наш исполняемый файл снова.Это работает.
** Как мне скомпилировать этот код, чтобы он не нуждался в распространяемом Visual C ++?Я знаю, что это возможно, потому что мы только что продемонстрировали, что делает java.exe.Как мне сделать, чтобы моя программа это сделала? *
Загрузки Я знаю, что этот вопрос довольно полный.Для вашего удобства я проделал вышеуказанную работу и поместил в zip-файл для загрузки , который включает в себя jar, исходный код java, исходный код C ++ и результирующий исполняемый файл.Вам нужно будет скачать jre, используя вышеуказанную ссылку на папку jre;Я не хотел создавать огромный почтовый индекс.
Я также загрузил свой визуальный студийный проект в zip .
win-launcher.cpp
#include <jni.h>
#include <windows.h>
typedef UINT(CALLBACK* JVMDLLFunction)(JavaVM**, void**, JavaVMInitArgs*);
int main(int argc, char** argv) {
HINSTANCE jvmDLL = LoadLibrary(".\\jre\\bin\\server\\jvm.dll");
if (!jvmDLL) {
printf("failed to find jvm.dll at specified location, exiting.\n");
return 1;
}
JVMDLLFunction createJavaVMFunction = (JVMDLLFunction)GetProcAddress(jvmDLL, "JNI_CreateJavaVM");
if (!createJavaVMFunction) {
printf("Failed to get pointer to JNI_CreateJavaVM function from jvm.dll, exiting\n");
return 1;
}
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
int index = 0;
options[index].optionString = (char *)"-Djava.class.path=./main.jar";
vm_args.version = JNI_VERSION_10;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
createJavaVMFunction(&jvm, (void**)&env, &vm_args);
delete options;
jmethodID main = NULL;
jclass cls = NULL;
cls = env->FindClass("net/joshuad/test/Main");
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
printf("Unable to find net.joshuad.hypnos.Main, exiting.\n");
return 0;
}
if (cls != NULL) {
main = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
}
else {
printf("Unable to find main() in java\n");
return 0;
}
if (main != NULL) {
jclass classString = env->FindClass("java/lang/String");
jobjectArray argsToJava = env->NewObjectArray(argc - 1, classString, NULL);
for (int i = 1; i < argc; i++) {
printf("Converting: %s", argv[i]);
jstring arg = env->NewStringUTF(argv[i]);
env->SetObjectArrayElement(argsToJava, i - 1, arg);
}
env->CallStaticVoidMethod(cls, main, argsToJava);
}
else {
printf("main method not found");
}
jvm->DestroyJavaVM();
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
return main(__argc, __argv);
}