当前所在位置:珠峰网资料 >> 计算机 >> 计算机等级考试 >> 正文
计算机二级辅导:将Java代码打包为exe文件
发布时间:2010/3/13 9:30:26 来源:城市学习网 编辑:MOON
  现在有很多的工具将Java代码打包为exe文件,执行时不需要再编写批处理文件,或者在命令行输入长长的classpath信息,为用户使用提供了很大的方便。这也是很多商业软件常常使用的方法。
  将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;
  options.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 = ...{"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 0jvm NULLenv 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_TRUEserviceClass NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  FreeLibrary(jvmDll);
  printf("加载启动类失败。");
  return false;
  }
  //启动方法
  jmethodID mid = env-GetStaticMethodID(serviceClass, szStartMethod , "([Ljava/lang/String;)V");
  if (env-ExceptionCheck() JNI_TRUEmid NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  FreeLibrary(jvmDll);
  printf("查找启动方法失败。");
  return false;
  }
  //查找String类。
  jclass stringClass = env-FindClass("java/lang/String");
  if (env-ExceptionCheck() JNI_TRUEstringClass 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; inParamCount; i++)
  ...{
  jstr = env-NewStringUTF(szParams[i]);
  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_TRUEpathString NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("创建字符串失败。");
  return false;
  }
  //查找FileOutputStream类。
  jclass fileOutputStreamClass = env-FindClass("java/io/FileOutputStream");
  if (env-ExceptionCheck() JNI_TRUEfileOutputStreamClass NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("查找FileOutputStream类失败。");
  return false;
  }
  //查找FileOutputStream类构造方法。
  jmethodID fileOutputStreamConstructor = env-GetMethodID(fileOutputStreamClass, "init", "(Ljava/lang/String;)V");
  if (env-ExceptionCheck() JNI_TRUEfileOutputStreamConstructor NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("查找FileOutputStream类构造方法失败。");
  return false;
  }
  //创建FileOutputStream类的对象。
  jobject fileOutputStream = env-NewObject(fileOutputStreamClass, fileOutputStreamConstructor, pathString);
  if (env-ExceptionCheck() JNI_TRUEfileOutputStream NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("创建FileOutputStream类的对象失败。");
  return false;
  }
  //查找PrintStream类。
  jclass printStreamClass = env-FindClass("java/io/PrintStream");
  if (env-ExceptionCheck() JNI_TRUEprintStreamClass NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("查找PrintStream类失败。");
  return false;
  }
  //查找PrintStream类构造方法。
  jmethodID printStreamConstructor = env-GetMethodID(printStreamClass, "init", "(Ljava/io/OutputStream;)V");
  if (env-ExceptionCheck() JNI_TRUEprintStreamConstructor NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("查找PrintStream类构造方法失败。");
  return false;
  }
  //创建PrintStream类的对象。
  jobject printStream = env-NewObject(printStreamClass, printStreamConstructor, fileOutputStream);
  if (env-ExceptionCheck() JNI_TRUEprintStream NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf("创建PrintStream类的对象失败。");
  return false;
  }
  //查找System类。
  jclass systemClass = env-FindClass("java/lang/System");
  if (env-ExceptionCheck() JNI_TRUEsystemClass NULL)
  ...{
  env-ExceptionDescribe();
  env-ExceptionClear();
  printf( "查找System类失败。");
  return false;
  }
  //查找System类设置方法。
  jmethodID setStreamMethod = env-GetStaticMethodID(systemClass, pszMethod, "(Ljava/io/PrintStream;)V");
  if (env-ExceptionCheck() JNI_TRUEsetStreamMethod 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文件尾部。打开文件属性对话框,可看到有“压缩文件”属性页。老牌的JBuilder.exe开发工具编译生成的exe文件即采用如下方式生成。
  后记:大家在使用Eclipse 3.2和Eclipse 3.3时,在任务管理器中会看到二者的不同。Eclipse 3.2是先启动Eclipse.exe文件,然后由Eclipse.exe启动Javaw.exe文件来创建虚拟机。
  Eclipse 3.2在任务管理器中显示为Eclipse.exe和javaw.exe两个进程。
  Eclipse 3.3在任务管理器中显示为Eclipse.exe一个进程。
  从上面可以看出,Eclipse 3.2和Eclipse 3.3采用了不同的虚拟机加载方式。
  Eclipse 3.2采用创建子进程的方法调用javaw.exe来启动,在windows下面可以用CreateProcess方法,此种方法较简单,具体可参见Eclipse源码。
  Eclipse 3.3加载java虚拟机的另外一种方法是加载jvm的动态库,并通过动态库的接口来在本进程内启动java虚拟机。本文开头即采用的第二种方法。
  :
广告合作:400-664-0084 全国热线:400-664-0084
Copyright 2010 - 2017 www.my8848.com 珠峰网 粤ICP备15066211号
珠峰网 版权所有 All Rights Reserved