`
yiqieanhao
  • 浏览: 66096 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

优雅地关闭java console界面

    博客分类:
  • JAVA
阅读更多
对于发布后的jar或class,我们经常会写一些bat脚本来方便地使之运行,比如:
java -jar XXX.jar
一旦双击这个bat脚本,会启动一个黑色的console界面,如果不需要这个,可以修改为:
@echo off
start javaw -jar xxx.jar 

某些时候,我们是需要这个console界面的,因为日志(system.out.print或logger.debug等)都可以很好地实时显示。但问题是:当需要关闭程序时,你指望别人会去按ctrl+C来终止,从而调用你的shutdownHook?呵呵,一般不会。那么,怎么实时看到日志,又可以优雅地关闭程序?我们有两个思路:

  • 双击bat后,执行自己的Java桌面应用,不显示console界面,把日志包括系统的system.out.print和log4j的日志都显示在自己的界面里。此时要关闭事件可以很容易捕捉。
  • 双击bat后,显示console界面,当用户关闭窗口时,捕获事件,回调java的善后处理方法。

第一个方法经过测试是可以实现的,但貌似很暴力,而且大多时候我只是想运行的时候看看日志,为什么还要拿个swing程序来封装?第二个办法还是更人性化一些。下面我们就看看如何实现。

实现依据:当用户关闭console窗口时,触发的是ctrl+close事件,详见这里。所以需要依靠JNI或是JNA来捕获底层事件。关于JNI的实现可以参考这里,下面我再整理一下:

JNI实现:
import java.io.File;
import java.io.IOException;

public class TestConsoleHandler {

	private static Thread hook;

	public static void main(String[] args) {
		System.out.println("Start");
		hook = new ShutdownHook();
		Runtime.getRuntime().addShutdownHook(hook);
		replaceConsoleHandler(); // actually not "replace" but "add"

		try {
			Thread.sleep(10000); // You have 10 seconds to close console
		} catch (InterruptedException e) {
		}
	}

	public static void shutdown() {
		hook.run();
	}

	private static native void replaceConsoleHandler();

	static {
		System.loadLibrary("TestConsoleHandler");
	}
}

class ShutdownHook extends Thread {
	public void run() {
		try {
			// do some visible work
			new File("d:/shutdown.mark").createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Shutdown");
	}
}


执行javah TestConsoleHandler来获取TestConsoleHandler.h。
在visual studio里新建c++项目  -> win32(dll)项目TestConsoleHandler,将TestConsoleHandler.h导入头文件,编写TestConsoleHandler.cpp如下:
#include "stdafx.h"
#include "TestConsoleHandler.h"

JavaVM *jvm;

BOOL WINAPI HandlerRoutine(__in DWORD dwCtrlType) {
    if (dwCtrlType == CTRL_CLOSE_EVENT) {
        JNIEnv *env;
        jint res =  jvm->AttachCurrentThread((void **)(&env), &env);
        jclass cls = env->FindClass("TestConsoleHandler");
        jmethodID mid = env->GetStaticMethodID(cls, "shutdown", "()V");
        env->CallStaticVoidMethod(cls, mid);
        jvm->DetachCurrentThread();
        return TRUE;
    }
    return FALSE;
}

JNIEXPORT void JNICALL Java_TestConsoleHandler_replaceConsoleHandler(JNIEnv *env, jclass clazz) {
 env->GetJavaVM(&jvm);
 SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
}

编译成dll拷贝到java项目根目录即可。

JNA实现:
import java.io.File;
import java.io.IOException;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;


public class TestJnaConsoleHandler {
	private static Thread hook;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("Start");
		hook = new ShutdownHook2();
		Runtime.getRuntime().addShutdownHook(hook);
        CLib lib = (CLib) Native.loadLibrary("TestJnaConsoleHandler", CLib.class);
        lib.init(new CLib.OpenFunc() {
            public void invoke(String name, int i) {                
            	hook.run();        		
            }
        });
        lib.replaceConsoleHandler();
        
    	try {
			Thread.sleep(10000); // You have 10 seconds to close console
		} catch (InterruptedException e) {
		}
	}
	public interface CLib extends Library{
	    public interface OpenFunc extends Callback {
	        void invoke(String name, int i);
	    }
	    void init(OpenFunc openfunc);
	    void replaceConsoleHandler();
	}
}

class ShutdownHook2 extends Thread {
	public void run() {
		try {
			// do some visible work
			new File("d:/shutdown.mark").createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Shutdown");
	}
}



c++实现:
#include "stdafx.h"
#include <iostream>

using namespace std;

#define MYLIBAPI  extern   "C"     __declspec( dllexport ) 


typedef void (*OpenFunc)(const char*,int);
OpenFunc openfunc = NULL;
MYLIBAPI void init(OpenFunc func) {
        cout << "init called" <<endl;
        openfunc = func;
}

BOOL WINAPI HandlerRoutine(__in DWORD dwCtrlType) {
    if (dwCtrlType == CTRL_CLOSE_EVENT) {

        (*openfunc)("test", 0);

        return TRUE;
    }
    return FALSE;
}

MYLIBAPI void replaceConsoleHandler() {
 SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
}


至此,当用户关闭console窗口或是按下ctrl+C时,都会调用预定义的方法来优雅地处理。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics