将Java代码打包为exe文件
将Java代码打包为exe文件,一般需要两个步骤:
1. 编写本地代码,创建虚拟机,加载并执行Main Class。
2. 将Java代码打包为jar文件,并与本地代码exe文件合并。
下面的代码,会加载jvm.dll,并调用
JNI_CreateJavaVM
导出函数创建
Java
虚拟机,得到
JNIEnv
指针,然后调用
FindClass
查找
Main Class
,之后调用
GetStaticMethodID
方法得到
main
方法,并执行
main
方法。代码如下:
引用:
#include <windows.h>
#include <jni.h>
//#pragma comment( linker, "/subsystem:"console" /entry:"mainCRTStartup"" )
#pragma comment( linker, "/subsystem:"windows" /entry:"WinMainCRTStartup"" )
typedef jint (JNICALL *JNICREATEPROC)(JavaVM **, void **, void *);
bool setStream(JNIEnv *env, const char * pszFileName, const char * pszMethod);
// 启动 java 虚拟机方法
//bool main(int argc,char *argv[])
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
//jvm 动态库的路径
const char szJvmPath[] = "d:/jdk1.5.0_07/jre/bin/server/jvm.dll";
//java 虚拟机的启动参数,每个参数写一项,不能合在一起写
int nOptionCount = 2;
JavaVMOption options[2];
options[1].optionString = "-Xmx256M";
// 设置 classpath
options[0].optionString = "-Djava.class.path=./Test.exe";
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = nOptionCount;
vm_args.ignoreUnrecognized = JNI_TRUE;
// 启动类 , 注意分割符是 / ,例如启动类 test.JTest 应该写成 test/JTest
const char szStartClass[] = "com/primeton/test/TestClass";
// 启动方法,通常是 main 函数,你也可以设定成其他函数
const char szStartMethod[] = "main";
// 重导向文件
const char szStdoutFileName[] = "stdout.txt";
const char szStderrFileName[] = "stderr.txt";
//java 程序的命令行参数
int nParamCount = 2;
const char *szParams[2] = {"arg1","arg2"};
// 加载 JVM 。
HINSTANCE jvmDll = LoadLibrary(szJvmPath);
if (jvmDll == NULL)
{
printf(" 加载 JVM 动态库错误。 %l", ::GetLastError());
return false ;
}
// 查找 JNI_CreateJavaVM 过程。
JNICREATEPROC jvmCreateProc = (JNICREATEPROC)GetProcAddress(jvmDll, "JNI_CreateJavaVM");
if (jvmCreateProc == NULL)
{
FreeLibrary(jvmDll);
printf(" 查找 JNI_CreateJavaVM 过程错误。 %l", ::GetLastError());
return false ;
}
// 创建 JVM 。
JNIEnv *env;
JavaVM *jvm;
jint r = (jvmCreateProc)(&jvm, ( void **)&env, &vm_args);
if (r < 0 || jvm == NULL || env == NULL)
{
FreeLibrary(jvmDll);
printf( " 创建 JVM 发生错误。 ");
return false ;
}
// 重导向 stdout, stderr 到输出文件
if (!setStream(env, szStdoutFileName, "setOut"))
{
printf(" 设置 stdout 输出文件失败 ");
return false ;
}
if (!setStream(env, szStderrFileName, "setErr"))
{
printf(" 设置 stderr 输出文件失败 ");
return false ;
}
// 加载启动类。
jclass serviceClass = env->FindClass(szStartClass);
if (env->ExceptionCheck() == JNI_TRUE || serviceClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
printf(" 加载启动类失败。 ");
return false ;
}
// 启动方法
jmethodID mid = env->GetStaticMethodID(serviceClass, szStartMethod , "([Ljava/lang/String;)V");
if (env->ExceptionCheck() == JNI_TRUE || mid == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
printf(" 查找启动方法失败。 ");
return false ;
}
// 查找 String 类。
jclass stringClass = env->FindClass("java/lang/String");
if (env->ExceptionCheck() == JNI_TRUE || stringClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
printf(" 查找 String 类失败。 ");
return false ;
}
jstring jstr;
jobjectArray args = 0;
args = env->NewObjectArray(2, stringClass, 0);
for ( int i=0; i<nParamCount; i++)
{
jstr = env->NewStringUTF(szParams);
if (jstr == 0) {
printf(" 分配 String 失败 ");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
return false ;
}
env->SetObjectArrayElement(args, i, jstr);
if (env->ExceptionCheck() == JNI_TRUE)
{
printf(" 设置参数失败 ");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
return false ;
}
}
// 调用启动类的启动方法启动 Java 程序
//env->CallStaticVoidMethod(serviceClass, mid, parameterArray);
env->CallStaticVoidMethod(serviceClass, mid, args);
if (env->ExceptionCheck() == JNI_TRUE)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
return false ;
}
MSG msg ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return true ;
}
// 设置输出流的方法
bool setStream(JNIEnv *env, const char * pszFileName, const char * pszMethod)
{
int pBufferSize = 1024;
char * pBuffer = new char [pBufferSize];
// 创建字符串对象。
jstring pathString = env->NewStringUTF(pszFileName);
if (env->ExceptionCheck() == JNI_TRUE || pathString == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 创建字符串失败。 ");
return false ;
}
// 查找 FileOutputStream 类。
jclass fileOutputStreamClass = env->FindClass("java/io/FileOutputStream");
if (env->ExceptionCheck() == JNI_TRUE || fileOutputStreamClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 FileOutputStream 类失败。 ");
return false ;
}
// 查找 FileOutputStream 类构造方法。
jmethodID fileOutputStreamConstructor = env->GetMethodID(fileOutputStreamClass, "<init>", "(Ljava/lang/String;)V");
if (env->ExceptionCheck() == JNI_TRUE || fileOutputStreamConstructor == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 FileOutputStream 类构造方法失败。 ");
return false ;
}
// 创建 FileOutputStream 类的对象。
jobject fileOutputStream = env->NewObject(fileOutputStreamClass, fileOutputStreamConstructor, pathString);
if (env->ExceptionCheck() == JNI_TRUE || fileOutputStream == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 创建 FileOutputStream 类的对象失败。 ");
return false ;
}
// 查找 PrintStream 类。
jclass printStreamClass = env->FindClass("java/io/PrintStream");
if (env->ExceptionCheck() == JNI_TRUE || printStreamClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 PrintStream 类失败。 ");
return false ;
}
// 查找 PrintStream 类构造方法。
jmethodID printStreamConstructor = env->GetMethodID(printStreamClass, "<init>", "(Ljava/io/OutputStream;)V");
if (env->ExceptionCheck() == JNI_TRUE || printStreamConstructor == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 PrintStream 类构造方法失败。 ");
return false ;
}
// 创建 PrintStream 类的对象。
jobject printStream = env->NewObject(printStreamClass, printStreamConstructor, fileOutputStream);
if (env->ExceptionCheck() == JNI_TRUE || printStream == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 创建 PrintStream 类的对象失败。 ");
return false ;
}
// 查找 System 类。
jclass systemClass = env->FindClass("java/lang/System");
if (env->ExceptionCheck() == JNI_TRUE || systemClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf( " 查找 System 类失败。 ");
return false ;
}
// 查找 System 类设置方法。
jmethodID setStreamMethod = env->GetStaticMethodID(systemClass, pszMethod, "(Ljava/io/PrintStream;)V");
if (env->ExceptionCheck() == JNI_TRUE || setStreamMethod == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 System 类设置方法失败。 ");
return false ;
}
// 设置 System 类的流。
env->CallStaticVoidMethod(systemClass, setStreamMethod, printStream);
if (env->ExceptionCheck() == JNI_TRUE)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 设置 System 类的流失败。 ");
return false ;
}
return true ;
}
#include <jni.h>
//#pragma comment( linker, "/subsystem:"console" /entry:"mainCRTStartup"" )
#pragma comment( linker, "/subsystem:"windows" /entry:"WinMainCRTStartup"" )
typedef jint (JNICALL *JNICREATEPROC)(JavaVM **, void **, void *);
bool setStream(JNIEnv *env, const char * pszFileName, const char * pszMethod);
// 启动 java 虚拟机方法
//bool main(int argc,char *argv[])
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
//jvm 动态库的路径
const char szJvmPath[] = "d:/jdk1.5.0_07/jre/bin/server/jvm.dll";
//java 虚拟机的启动参数,每个参数写一项,不能合在一起写
int nOptionCount = 2;
JavaVMOption options[2];
options[1].optionString = "-Xmx256M";
// 设置 classpath
options[0].optionString = "-Djava.class.path=./Test.exe";
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = nOptionCount;
vm_args.ignoreUnrecognized = JNI_TRUE;
// 启动类 , 注意分割符是 / ,例如启动类 test.JTest 应该写成 test/JTest
const char szStartClass[] = "com/primeton/test/TestClass";
// 启动方法,通常是 main 函数,你也可以设定成其他函数
const char szStartMethod[] = "main";
// 重导向文件
const char szStdoutFileName[] = "stdout.txt";
const char szStderrFileName[] = "stderr.txt";
//java 程序的命令行参数
int nParamCount = 2;
const char *szParams[2] = {"arg1","arg2"};
// 加载 JVM 。
HINSTANCE jvmDll = LoadLibrary(szJvmPath);
if (jvmDll == NULL)
{
printf(" 加载 JVM 动态库错误。 %l", ::GetLastError());
return false ;
}
// 查找 JNI_CreateJavaVM 过程。
JNICREATEPROC jvmCreateProc = (JNICREATEPROC)GetProcAddress(jvmDll, "JNI_CreateJavaVM");
if (jvmCreateProc == NULL)
{
FreeLibrary(jvmDll);
printf(" 查找 JNI_CreateJavaVM 过程错误。 %l", ::GetLastError());
return false ;
}
// 创建 JVM 。
JNIEnv *env;
JavaVM *jvm;
jint r = (jvmCreateProc)(&jvm, ( void **)&env, &vm_args);
if (r < 0 || jvm == NULL || env == NULL)
{
FreeLibrary(jvmDll);
printf( " 创建 JVM 发生错误。 ");
return false ;
}
// 重导向 stdout, stderr 到输出文件
if (!setStream(env, szStdoutFileName, "setOut"))
{
printf(" 设置 stdout 输出文件失败 ");
return false ;
}
if (!setStream(env, szStderrFileName, "setErr"))
{
printf(" 设置 stderr 输出文件失败 ");
return false ;
}
// 加载启动类。
jclass serviceClass = env->FindClass(szStartClass);
if (env->ExceptionCheck() == JNI_TRUE || serviceClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
printf(" 加载启动类失败。 ");
return false ;
}
// 启动方法
jmethodID mid = env->GetStaticMethodID(serviceClass, szStartMethod , "([Ljava/lang/String;)V");
if (env->ExceptionCheck() == JNI_TRUE || mid == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
printf(" 查找启动方法失败。 ");
return false ;
}
// 查找 String 类。
jclass stringClass = env->FindClass("java/lang/String");
if (env->ExceptionCheck() == JNI_TRUE || stringClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
printf(" 查找 String 类失败。 ");
return false ;
}
jstring jstr;
jobjectArray args = 0;
args = env->NewObjectArray(2, stringClass, 0);
for ( int i=0; i<nParamCount; i++)
{
jstr = env->NewStringUTF(szParams);
if (jstr == 0) {
printf(" 分配 String 失败 ");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
return false ;
}
env->SetObjectArrayElement(args, i, jstr);
if (env->ExceptionCheck() == JNI_TRUE)
{
printf(" 设置参数失败 ");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
return false ;
}
}
// 调用启动类的启动方法启动 Java 程序
//env->CallStaticVoidMethod(serviceClass, mid, parameterArray);
env->CallStaticVoidMethod(serviceClass, mid, args);
if (env->ExceptionCheck() == JNI_TRUE)
{
env->ExceptionDescribe();
env->ExceptionClear();
FreeLibrary(jvmDll);
return false ;
}
MSG msg ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return true ;
}
// 设置输出流的方法
bool setStream(JNIEnv *env, const char * pszFileName, const char * pszMethod)
{
int pBufferSize = 1024;
char * pBuffer = new char [pBufferSize];
// 创建字符串对象。
jstring pathString = env->NewStringUTF(pszFileName);
if (env->ExceptionCheck() == JNI_TRUE || pathString == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 创建字符串失败。 ");
return false ;
}
// 查找 FileOutputStream 类。
jclass fileOutputStreamClass = env->FindClass("java/io/FileOutputStream");
if (env->ExceptionCheck() == JNI_TRUE || fileOutputStreamClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 FileOutputStream 类失败。 ");
return false ;
}
// 查找 FileOutputStream 类构造方法。
jmethodID fileOutputStreamConstructor = env->GetMethodID(fileOutputStreamClass, "<init>", "(Ljava/lang/String;)V");
if (env->ExceptionCheck() == JNI_TRUE || fileOutputStreamConstructor == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 FileOutputStream 类构造方法失败。 ");
return false ;
}
// 创建 FileOutputStream 类的对象。
jobject fileOutputStream = env->NewObject(fileOutputStreamClass, fileOutputStreamConstructor, pathString);
if (env->ExceptionCheck() == JNI_TRUE || fileOutputStream == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 创建 FileOutputStream 类的对象失败。 ");
return false ;
}
// 查找 PrintStream 类。
jclass printStreamClass = env->FindClass("java/io/PrintStream");
if (env->ExceptionCheck() == JNI_TRUE || printStreamClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 PrintStream 类失败。 ");
return false ;
}
// 查找 PrintStream 类构造方法。
jmethodID printStreamConstructor = env->GetMethodID(printStreamClass, "<init>", "(Ljava/io/OutputStream;)V");
if (env->ExceptionCheck() == JNI_TRUE || printStreamConstructor == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 PrintStream 类构造方法失败。 ");
return false ;
}
// 创建 PrintStream 类的对象。
jobject printStream = env->NewObject(printStreamClass, printStreamConstructor, fileOutputStream);
if (env->ExceptionCheck() == JNI_TRUE || printStream == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 创建 PrintStream 类的对象失败。 ");
return false ;
}
// 查找 System 类。
jclass systemClass = env->FindClass("java/lang/System");
if (env->ExceptionCheck() == JNI_TRUE || systemClass == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf( " 查找 System 类失败。 ");
return false ;
}
// 查找 System 类设置方法。
jmethodID setStreamMethod = env->GetStaticMethodID(systemClass, pszMethod, "(Ljava/io/PrintStream;)V");
if (env->ExceptionCheck() == JNI_TRUE || setStreamMethod == NULL)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 查找 System 类设置方法失败。 ");
return false ;
}
// 设置 System 类的流。
env->CallStaticVoidMethod(systemClass, setStreamMethod, printStream);
if (env->ExceptionCheck() == JNI_TRUE)
{
env->ExceptionDescribe();
env->ExceptionClear();
printf(" 设置 System 类的流失败。 ");
return false ;
}
return true ;
}
第二步,将Java文件打包为exe文件,也很简单。在Dos提示符下执行copy命令:
C:/>copy test.exe+test.jar test.exe
其实,就是将Java打包文件追加到exe文件尾部。打开文件属性对话框,可看到有“压缩文件”属性页。