代码之家  ›  专栏  ›  技术社区  ›  Grumblesaurus

如何创建一个不需要Vista/WIN7用户安装VisualC++的可重新分配的Windows可执行文件?

  •  0
  • Grumblesaurus  · 技术社区  · 6 年前

    我已经编写了一个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);
    }
    
    3 回复  |  直到 6 年前
        1
  •  3
  •   PeterT    6 年前

    enter image description here

    或者添加 \jre\bin 到库搜索路径,因为DLL位于那里。

    例如 SetDllDirectoryA("jre\\bin")

        2
  •  3
  •   Alan Birtles    6 年前

        3
  •  1
  •   rustyx    6 年前

    如果您的本机代码很小,并且只使用基本的C标准库或Win32函数,那么您可以使用MinGW构建,它与 msvcrt.dll 而不是 msvcr1xx.dll -static 标志嵌入LBSTDC + +,如果你使用C++标准库函数。

    否则,只需使用VC++和 /MT 旗帜您的可执行文件将变得更大,但它是一个更可移植的解决方案。