我已经编写了一个Java程序,目标是从WindowsVista到Windows10。
该程序在嵌入式java运行时环境中运行,因此用户无需费心安装java即可使其工作。
本项目的目标是下载用户体验->运行单个安装程序->运行程序
让它感觉自己是本地人
. 用户不需要其他步骤。
为了实现这个目标,我需要编写一个C++中的本地可执行文件,它直接调用JVM(而不是调用java .exe),因此Windows Tabar特征钉扎工作正常——如果我使用可执行包装器
launch4j
如果要在任何时候将执行传递给java.exe,那么钉扎就不能按预期的方式工作。我自己的可执行文件需要在整个运行过程中保持程序的运行状态,以便固定到正确的位置。
我编写了一个本地启动程序C++程序,它使用JNI调用java虚拟机
jvm.dll
而不是
java.exe
windows.h
并使用以下功能:
HINSTANCE
,
GetProcAddress
,
WinMain
jvm.dll
在运行时。
但是,如果我使用VisualStudio 2017编译这个垫片,结果的可执行文件取决于安装了VisualC++ 2015可重新分配的用户。如果没有,程序将显示错误“无法启动,因为您的计算机缺少VCRUNTIME140.dll”。
我尝试过使用/MT标志编译这个程序,也尝试过在visualstudio的常规项目属性页中选择“在静态库中使用MFC”。这没什么区别;不管怎样,我都会得到同样的错误。
尽管Windows Vista/Windows 7上的许多用户已经安装了运行时,但有些用户可能没有安装,而且我对该项目的优先考虑是从下载到安装再到运行的流畅、无错误的用户体验。
我可以在我的项目中绑定其他DLL。如果使用MinGW或Cygwin进行编译更有意义的话,我没有问题。我唯一想要的就是找到一条通往
download -> install -> run
适用于从Vista到Windows 10的任何Windows版本。
最小完整可验证示例:
You can download a zip
josh-problem
.
下载
Windows 64 bit zip of Java 11
把它放在
josh-problem/jre
josh问题/jre
java文件夹是否命名为
lib
,
legal
,
jmods
,
include
conf
,
bin
等
创造
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
:导航到该文件夹
乔希问题
-
jre\bin\javac.exe -d bin src\net\joshuad\test\Main.java
-
jre\bin\jar.exe cfm main.jar MANIFEST.MF -C bin .
步骤6-确认jar工作正常
:运行命令:
jre\bin\java.exe -jar main.jar
在Visual Studio 2017中创建一个项目,并为其提供一个cpp文件,其中包含本文底部的源代码。添加到您的包含项中
josh-problem\jre\include
josh-problem\jre\include\win32
.
乔希问题
. 尝试在干净的Windows 7 Service Pack 1计算机上运行可执行文件。它将给出错误:“程序无法启动,因为您的计算机缺少VCRUNTIME140D.dll。”。
jre\bin\java.exe-jar main.jar
第10步-安装VisualC++重新分发并再次尝试exe
2015 Visual C++ Redistributable
在干净的Windows 7计算机上。请重试我们的可执行文件。它起作用了。
**我如何编译这个代码,以便它不需要VisualC++的可再分发?我知道这是可能的,因为我们刚刚演示了java.exe正在做的事情。我如何让我的程序做到这一点*
下载
我知道这个问题很难回答。为了您的方便,我已经完成了上述工作并将其
in a zip to download
,包括jar、java源、C++源以及结果可执行文件。您必须使用上述链接下载jre到jre文件夹;我不想创造一个巨大的拉链。
visual studio project in a 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);
}