一、概述
现在Java语言越来越受到程序员的关注。和Java相关的应用也越来越多。虽然Java是跨平台语言,但在国内有很多的应用都是运行在Windows下的。尤其是一些服务类程序。而一般基于Java的服务类程序都是以控制台方式运行的。这样虽然很直接。但如果服务程序多了,显得很乱。而且要使其在系统启动时运行也比较麻烦。因此,本文将介绍一种可以将Java程序转换为Windows服务的方法。通过这种方法。可以使Java程序象Windows服务程序一样运行。下面就让我们来进行转换吧。
一般有两种方法可以将Java程序转换为Windows服务:
1. 使用Windows服务直接在同一个进程运行Java应用程序。(这种方法服务程序无法更好地控制Java程序)。
2. 在Windows服务程序中建立一个java虚拟机实例(JVM),这个JVM实例和服务程序在同一个上下文中,而且JVM在Windows服务程序的控制之下。
第一种方法虽然实现起来简单,但这种方法不能很好地控制Java程序。因此,本文使用了第二种方法来运行Java程序。本文将带领读者一步一步地实现所有的内核代码。在实现代码之前,我们需要很好地了解Windows服务和Java本地接口(JNI)的概念和API的使用。
二、使用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"。
下面是使用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规范得到更多的细节。本文不再详细描述。
package com.test;
public class Hello{
public static void main(String[] args) {
System.out.println("Hello World");
}
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/javaokay/archive/2008/03/12/2173669.aspx