【IT168 专稿】

一、概述

现在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"); } }

三、集成JNI和Windows服务API

    为了演示Windows服务的功能,我在这设计了一h个叫Dummy的Java类。这个类在main方法等待一个停止事件。并实现了shutdown方法,在这个方法里设置并调用了stop事件。这将保证main线程安全地退出。在Dummy类中还实现了shutdown钩子,这个钩子主要给java.exe使用。Dummy.java的代码如下:

import java.io.*; public class Dummy...{ public static Dummy xyz = null; private boolean stopped = false; public static PrintWriter pw; public Dummy() throws IOException ...{ pw = new PrintWriter(new OutputStreamWriter( new FileOutputStream("C://dummy.log")), true); } public void start() ...{ pw.println("started"); while(!stopped) ...{ synchronized(this) ...{ try ...{ wait(); } catch (InterruptedException ie) ...{} } } pw.println("stopped"); } public void stop() ...{ pw.println("stopping"); synchronized(this) ...{ stopped = true; notifyAll(); } try ...{ Thread.sleep(1000); } catch(Exception ex)...{} } public static void main(String[] args) ...{ try ...{ xyz = new Dummy(); Runtime.getRuntime().addShutdownHook(new Thread() ...{ public void run() ...{ pw.println("inside shutdown hook"); xyz.stop(); } }); xyz.start(); } catch (Exception ex) ...{} } public static void shutdown() ...{ xyz.stop(); } }

典型的Windows服务(除了设备驱动服务外)是由服务控制管理器(SCM,也就是众所周知的services.exe进程)按着服务进程创建和管理的。一个单独的进程包含有多个服务。但在本文的例子中一个服务进程只包含一个JVM实例服务。

我们可以使用如下的API在SCM中安装一个服务:

 
SC_HANDLE schSCManager = OpenSCManager(..),
SC_MANAGER_CREATE_SERVICE);
SC_HANDLE schService = CreateService(..)
 
也可以使用如下的API来卸载Windows服务程序:
SC_HANDLE schSCManager = OpenSCManager(.., SERVICE_ALL_ACCESS);
SC_HANDLE schService = OpenService( schSCManager, ..);
DeleteService(schService)

    为了启动服务,我们需要使用StartServiceCtrlDispatcher来注册ServiceMain函数。这个ServiceMain函数包含了我们的主要功能。在我们的例子中,就是InvokeMain函数。接下来,调用RegisterServiceCtrlHandler(SERVICE_NAME, ServiceHandler);,这个函数注册一个Handler,并从SCM接收响应的命令。为了防止由于JVM崩溃而导致整个服务瘫痪,我们在另外一个线程里调用InvokeMain方法。上面的程序被写在DummyService.cpp中,通过VS2005将其编译成DummyService.exe。上面的代码可以通过点击此处下载

    接下来我们使用如下的命令来安装、启动、停止以及卸载Windows服务:
 
DummyService /i   // 安装服务
net start DummyService    // 启动服务
net stop DummyService    // 停止服务
DummyService /u   // 卸载服务

    上面的程序只是使用了最小的配置。其实要想充分使用JNI,得需要使用很多参数。一般需要至少15至20个配置参数。下面是在定制满足我们需要的程序的配置参数:
 
1. Windows服务参数:
(1) 服务名
(2) 演示名
(3) 描述
     (4) 执行路径
(5) 启动类型(自动/手动)
(6) 在注册中被保存的参数
     (7) 工作目录
(8) 存在于每个进程的服务或在单进程中的许多服务
     (9) 和桌面交互的选项(如果服务有一个UI接口)
     (10) 登录用户名和密码(当使用用户帐号或本地系统帐号来运行服务时需要)

2. 和Java应用相关的参数:
(11) jvm.dll的路径
(12) JVM选项(-D, -X)
     (13) 类名
(14) 命令行参数
     (15) 关闭超时

3. 日志参数:
(16) 事件日志和文件日志
(17) 用于重定向输入、输出的参数

我们可以根据具体的要求选择使用哪些参数。我们可以将这些参数保存在被推荐的注册表的位置:HKLM/System/CurrentControlSet/[Service Name]/Parameters. 中。
四、安全地退出Java程序

我们首先调用System.exit(0)来退出程序。虽然这个方法同时调用了所有的shutdown钩子,也试着关闭JVM。但它在清除和用于和SCM建立的Windows服务通讯的管道时抛出异常。这些错误信息如下:

System error 109 has occurred.
     The pipe has been ended.
    因此,本文采用了更好的方式来关闭服务,也就是实现shutdown方法,并调用它。我建议在线程中使用public static void shutdown()方法(如果这个方法被实现的话),并设置一定的超时,如20秒。这将防止服务如果shutdown方法未返回而收到未响应的信息。但要保证这个超时比系统超时少,否则SCM将终止我们的服务进程。

将Java程序转换为Windows服务 作者:IT168 seasky 2007-09-19相关推荐

  1. 如何注册java程序为windows服务

    最近想找个软件来控制电脑的关机时间,在网上找了几个,都是可视化界面的可以设置具体的关机时间的软件.由于我想编写的关机程序是运行在别人机器上,只能让该机器在晚上17 点到23 点25 分才上网,到了23 ...

  2. 把java程序作为windows服务运行

    参考: https://www.jianshu.com/p/fc9e4ea61e13 https://blog.csdn.net/qq_28566071/article/details/8088250 ...

  3. java windows wrapper_Java Service Wrapper 发布Java程序为Windows服务

    现在目前只要32位有免费的,64位免费版目前还没有做出来.官网上(社区)那一列是免费的. 1.先把你的项目用MANIFEST.MF打成jar包,例如bb.jar.新建目录,例如:dist,放在D盘下面 ...

  4. Java Service Wrapper 发布Java程序为Windows服务

    下载Windows版本:https://www.krenger.ch/blog/java-service-wrapper-3-5-37-for-windows-x64/ 转自:F:\java\bhGe ...

  5. java如何编写屏幕保护程序_将Java App转换为Windows屏幕保护程序

    小编典典 Windows屏幕保护程序只是接受某些命令行参数的程序.因此,为了使您的程序可以作为屏幕保护程序运行,您必须对其进行编码以接受这些参数. 接下来,您可能希望屏幕保护程序以全屏模式运行.这在J ...

  6. 【Windows】普通控制台EXE程序转为windows服务方式运行的详细步骤

    背景 NSSM(Non-Sucking Service Manager)是一个免费的第三方Windows服务管理器,可以将任何可执行文件转换为Windows服务.官网下载地址为:https://nss ...

  7. 黄聪:使用srvany.exe将任何程序作为Windows服务运行

    srvany.exe是什么? srvany.exe是Microsoft Windows Resource Kits工具集的一个实用的小工具,用于将任何EXE程序作为Windows服务运行.也就是说sr ...

  8. 使用srvany.exe将任何程序作为Windows服务运行

    使用srvany.exe将任何程序作为Windows服务运行 学习了:http://wangye.org/blog/archives/42/ https://www.cnblogs.com/huang ...

  9. 树莓派Java程序运行_树莓派上Java程序作为linux服务并开机自动启动

    http://www.iigrowing.cn/shu_mei_pai_shang_java_cheng_xu_zuo_wei_linux_fu_wu_bing_kai_ji_zi_dong_qi_d ...

最新文章

  1. LightOJ-1028 Trailing Zeroes (I)---因子数目
  2. 从零开始一步一步搭建Ubuntu Server服务器、修改数据源、安装Docker、配置镜像加速器、Compose部署Gitlab服务
  3. mysql 各种恢复_Mysql数据库备份和还原常用的命令
  4. python 函数式 panda_python – 反向中的Pandas分裂函数
  5. 【测试点2超时问题】1046 Shortest Distance (20 分)_21行代码AC
  6. 用Swift完成不同View Controller之间的切换
  7. thymeleaf 基础教程-搭建杂货铺项目环境(一)
  8. ppt里quicktime不可用_PPT插入音乐、视频的全部疑难问题,一站式帮你解决!
  9. 在Ubuntu Server14.04上编译Android6.0源码
  10. web性能优化--缓存
  11. php浮点数用法,php中让人头疼的浮点数运算分析
  12. 一个非常好用的插件-FeHelper
  13. 刀剑神域:突破界限I
  14. html5qq授权登陆,php 第三方登陆接入qq互联
  15. 【ICCV2019论文阅读】PU-GAN:点云上采样对抗网络
  16. 关于hcaptcha (vm wasm ob)三合一
  17. open_source_team
  18. 判断是否为平衡二叉树(Java)
  19. R语言C指数,在R中求一致性指数( Harrell'concordance index:C-index)案例
  20. swift4.0语法杂记(精简版)

热门文章

  1. ShardingSphere分库分表核心原理精讲第十一节 分布式事务详解
  2. 爬虫—微博博主动态及相册的请求构造规律
  3. python中raise啥意思_python raise什么意思
  4. java谐音,那位有If I were a boy 谐音啊?!
  5. 百题突击12:1,SVM算法的优缺点 2,SVM的超参数C如何调节 3,SVM的核函数如何选择 4,简述SVM硬间隔推导过程 5,简述SVM软间隔推导过程
  6. mysql 字符串截取
  7. ProSci 艾美捷CCR3抗体
  8. Linux基础入门之内外命令讲解篇
  9. 35位嘉宾的“2022年元宇宙趋势研判”!丨2022元宇宙云峰会金句集锦
  10. 黑镜.潘达斯奈基 高清中字