从 bat 脚本运行的 Java 应用程序上的 Windows 关闭挂钩 [英] Windows shutdown hook on java application run from a bat script

查看:38
本文介绍了从 bat 脚本运行的 Java 应用程序上的 Windows 关闭挂钩的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个运行 java 应用程序的 bat 脚本.如果我按 ctrl+c ,应用程序会正常终止,调用所有关闭挂钩.但是,如果我只是关闭 bat 脚本的 cmd 窗口,则永远不会调用关闭挂钩.

I have a bat script which runs a java application. If I press ctrl+c on it, it the application terminates gracefully, invoking all the shutdown hooks. However, if I just close the cmd window of the bat script, the shutdown hooks are never invoked.

有没有办法解决这个问题?也许有一种方法可以告诉 bat 脚本如何在其窗口关闭时终止调用的应用程序?

Is there a way to solve this? Perhaps there's a way to tell the bat script how to terminate the invoked applications when its window is closed?

推荐答案

来自 addShutdownHook 文档:

在极少数情况下,虚拟机可能会中止,即在没有彻底关闭的情况下停止运行.当虚拟机从外部终止时会发生这种情况,例如在 Unix 上使用 SIGKILL 信号或在 Microsoft Windows 上使用 TerminateProcess 调用.

In rare circumstances the virtual machine may abort, that is, stop running without shutting down cleanly. This occurs when the virtual machine is terminated externally, for example with the SIGKILL signal on Unix or the TerminateProcess call on Microsoft Windows.

不幸的是,我觉得这里没什么可做的.

So i think nothing to do here, unfortunately.

CTRL-CLOSE Windows 控制台中的信号.似乎不可调整.

CTRL-CLOSE signal in Windows Console. Seems non-tweakable.

引用上面的链接:

当用户关闭控制台时,系统会生成一个 CTRL+CLOSE 信号.连接到控制台的所有进程都会收到信号,让每个进程都有机会在终止前进行清理.当进程收到此信号时,处理程序函数可以在执行任何清理操作后采取以下操作之一:

The system generates a CTRL+CLOSE signal when the user closes a console. All processes attached to the console receive the signal, giving each process an opportunity to clean up before termination. When a process receives this signal, the handler function can take one of the following actions after performing any cleanup operations:

  • 调用 ExitProcess 来终止进程.
  • 返回FALSE.如果注册的处理程序函数均未返回 TRUE,则默认处理程序终止进程.
  • 返回TRUE.在这种情况下,不会调用其他处理函数,并且会弹出一个对话框询问用户是否终止进程.如果用户选择不终止进程,系统不会关闭控制台,直到进程最终终止.
    • Call ExitProcess to terminate the process.
    • Return FALSE. If none of the registered handler functions returns TRUE, the default handler terminates the process.
    • Return TRUE. In this case, no other handler functions are called, and a pop-up dialog box asks the user whether to terminate the process. If the user chooses not to terminate the process, the system does not close the console until the process finally terminates.
    • UPD.如果您可以接受本机调整,WinAPI SetConsoleCtrlHandler 函数为抑制默认行为开辟了道路.

      UPD. If native tweaks are acceptable for you, WinAPI SetConsoleCtrlHandler function opens way for suppressing of default behavior.

      UPD2.Java 信号处理和终止的启示 相对较旧的文章,但部分 编写 Java 信号处理程序确实可能包含您需要的内容.

      UPD2. Revelations on Java signal handling and termination relatively old article, but section Writing Java signal handlers really may contain what you need.

      UPD3.我已经尝试了上面文章中的 Java 信号处理程序.它可以很好地与 SIGINT 配合使用,但它不是我们所需要的,因此我决定将它与 SetConsoleCtrlHandler 一起使用.结果有点复杂,可能不值得在您的项目中实施.不管怎样,它可以帮助别人.

      UPD3. I've tried Java signal handlers from article above. It works with SIGINT nicely, but it not what we need, and i decided to carry it with SetConsoleCtrlHandler. The result is a bit complicated and may be not worth to implement in your project. Anyway, it could help someone else.

      所以,这个想法是:

      1. 保持对关闭处理程序线程的引用.
      2. 使用 JNI 设置自定义本机控制台处理程序.
      3. CTRL+CLOSE 信号调用自定义 Java 方法.
      4. 从该方法调用关闭处理程序.
      1. Keep reference to shutdown handler thread.
      2. Set custom native console handler routine with JNI.
      3. Call custom Java method on CTRL+CLOSE signal.
      4. Call shutdown handler from that method.

      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" 以指示正确关闭.

      And it works. In JNI code all error checks are omitted for clearance. Shutdown handler creates empty file "d:shutdown.mark" to indicate correct shutdown.

      带有编译测试二进制文件的完整源代码此处.

      Complete sources with compiled test binaries here.

      这篇关于从 bat 脚本运行的 Java 应用程序上的 Windows 关闭挂钩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆