技术开发 频道

将Java程序转换为Windows服务



二、使用JVM API模拟Java运行时

    由于Windows任务管理器将所有的Java进程都显示为"Java",因此,我们根本无法通过这种方式区分某一个Java进程。为了给每一个Java应用程序指定一个特殊的名子。我们需要使用JVM API来模拟Java运行时。

    下面先来看看Java运行时(也就是java.exe)在运行时需要些什么。当我们使用java <类名>来运行java程序时,java.exe从系统路径动态装载了一些DLL库。这些Dll如下:
 
1.       java\jdk\jre\bin\client\jvm.dll
这个dll提供了JVM所需要的API。一但我们建立了一个JVM,jvm.dll就会依次装载所需的Dll,这些Dll如下:

2. java\jdk\jre\bin\hpi.dll
3. java\jdk\jre\bin\verify.dll
4. java\jdk\jre\bin\java.dll
5. java\jdk\jre\bin\zip.dll

    下面是java.exe如何处理Dll的过程:
 
1. 装载JVM Dll。
2. 建立一个JVM。
3. 装载指定的Java类。
4. 调用main方法,也就是public static void main (String[] args)。

我们可以使用在jni.h中定义的JNI_CreateJavaVM方法来建立一个JVM实例。我们可
以在JDK的安装目录中找到jni.h。
 
    下面是一个简单的Java程序,在控制台中打印出"Hello World"

package com.test; public class Hello{ public static void main(String[] args) { System.out.println("Hello World"); } }
下面是使用JNI来模拟java.exe的例子代码。在这里我们使用动态装载jvm.dll方法,而不是静态绑定jvm.lib。这样会更有弹性,如可以自由地选择java的版本。代码如下:
 
int InvokeMain() {
       JavaVM *vm;
       JavaVMInitArgs vm_args;
       JavaVMOption options[1];
       jint res;
       JNIEnv *env;
       jclass cls;
       jmethodID mid;
 
       options[0].optionString = CLASS_PATH;
       vm_args.version = JNI_VERSION_1_4;   // 设置JDK的版本
       vm_args.options = options;
       vm_args.nOptions = 1;
       vm_args.ignoreUnrecognized = JNI_FALSE;
 
       //装载jvm.dll
       HINSTANCE handle = LoadLibrary(RUNTIME_DLL);
       if( handle == 0) {
              printf("Failed to load jvm dll %s\n",
                     RUNTIME_DLL);
              return -1;
       }
    // 得到JNI_CreateJVM指针

       createJVM = (CreateJavaVM)GetProcAddress(handle,
                     "JNI_CreateJavaVM");
 
       res = createJVM(&vm, (void **)&env, &vm_args);
       if (res < 0) {
              printf("Error creating JVM");
              return -1;
       }
 
       // 装载指定的类
       cls = env->FindClass(CLASS_NAME);
       if(cls == 0) {
              printf("Exception in thread \"main\"
                     java.lang.NoClassDefFoundError: %s\n",
                            CLASS_NAME);
              return -1;
       }
       //得到main方法
       mid = env->GetStaticMethodID(cls, "main",
              "([Ljava/lang/String;)V");
       if(mid == 0) {
              printf("Exception in thread \"main\"
                     java.lang.NoSuchMethodError: main\n");
              return -1;
       }
 
    // 调用main方法(不传递参数)
       env->CallStaticVoidMethod(cls, mid, 0);
    // 如果程序抛出异常,打印它
       if(env->ExceptionCheck()) {
              env->ExceptionDescribe();
              return -1;
       }
       return 0;
}

    在装载com.test.Hello时,我们必须使用"/"分割符(如com/test/Hello)。还有我们需要理解JNI调用Java方法的格式。如,为了调用void main(String[] args)方法,格式为:([Ljava/lang/String;)V:,"["表示数组;"L<classname>;"描述一个Java对象,V表示这个方法返回void。我们可以从JNI规范得到更多的细节。本文不再详细描述。
0
相关文章