来自addShutdownHook 文档:
在极少数情况下,虚拟机可能会中止,即在没有完全关闭的情况下停止运行。当虚拟机在外部终止时会发生这种情况,例如 Unix 上的 SIGKILL 信号或 Microsoft Windows 上的 TerminateProcess 调用。
不幸的是,我觉得这里没什么可做的。
CTRL-CLOSE Windows 控制台中的信号。似乎不可调整。
引用以上链接:
当用户关闭控制台时,系统会生成CTRL+CLOSE 信号。连接到控制台的所有进程都接收到信号,从而使每个进程都有机会在终止之前进行清理。当进程收到此信号时,处理函数可以在执行任何清理操作后采取以下操作之一:
- 调用
ExitProcess终止进程。
- 返回
FALSE。如果已注册的处理程序函数均未返回TRUE,则默认处理程序将终止进程。
- 返回
TRUE。在这种情况下,不会调用其他处理函数,并且会弹出一个对话框询问用户是否终止进程。如果用户选择不终止进程,系统不会关闭控制台,直到进程最终终止。
UPD。如果您可以接受本机调整,WinAPI SetConsoleCtrlHandler 函数打开了抑制默认行为的方式。
UPD2。 Revelations on Java signal handling and termination 比较老的文章,但是Writing Java signal handlers部分确实可能包含你需要的东西。
UPD3。
我已经尝试了上面文章中的 Java 信号处理程序。它可以很好地与SIGINT 一起使用,但这不是我们需要的,我决定将它与SetConsoleCtrlHandler 一起使用。结果有点复杂,可能不值得在您的项目中实施。无论如何,它可以帮助别人。
所以,这个想法是:
- 保留对关闭处理程序线程的引用。
- 使用 JNI 设置自定义本机控制台处理程序例程。
- 在
CTRL+CLOSE 信号上调用自定义Java 方法。
- 从该方法调用关闭处理程序。
Java 代码:
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");
}
}
原生replaceConsoleHandler:
JNIEXPORT void JNICALL Java_TestConsoleHandler_replaceConsoleHandler(JNIEnv *env, jclass clazz) {
env->GetJavaVM(&jvm);
SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
}
以及处理程序本身:
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;
}
而且它有效。在 JNI 代码中,所有错误检查都被忽略以清除。关机处理程序创建空文件"d:\shutdown.mark" 以指示正确关机。
带有已编译测试二进制文件的完整源代码here。