Java应用程序上的Windowsclosures挂钩从bat脚本运行

我有一个运行Java应用程序的蝙蝠脚本。 如果我按ctrl + c,应用程序将优雅地终止,调用所有的closures钩子。 但是,如果我只closuresbat脚本的cmd窗口,就不会调用closures钩子。

有没有办法解决这个问题? 也许有办法告诉蝙蝠脚本如何终止被closures的窗口被调用的应用程序?

Solutions Collecting From Web of "Java应用程序上的Windowsclosures挂钩从bat脚本运行"

来自addShutdownHook文档:

在极少数情况下,虚拟机可能会中止,即停止运行而不干净地关闭。 当虚拟机在外部终止时会发生这种情况,例如Unix上的SIGKILL信号或Microsoft Windows上的TerminateProcess调用。

所以我觉得不幸的是,这里没有什么可做的。


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

引用上面的链接:

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

  • 调用ExitProcess来终止进程。
  • 返回FALSE 如果没有注册的处理程序函数返回TRUE ,则默认处理程序将终止该进程。
  • 返回TRUE 在这种情况下,没有其他处理函数被调用,并且弹出对话框询问用户是否终止进程。 如果用户选择不终止进程,则系统不会关闭控制台,直到进程终止。

UPD 如果本地调整是可以接受的,WinAPI SetConsoleCtrlHandler函数打开方式来抑制默认行为。

UPD2 对Java信号处理和终止的启示相对较老的文章,但是编写Java信号处理程序确实可能包含你所需要的东西。


UPD3 我已经尝试从上面的文章的Java信号处理程序 它很好地适用于SIGINT ,但它不是我们所需要的,我决定用SetConsoleCtrlHandler来执行它。 结果有点复杂,可能不值得在你的项目中实现。 无论如何,它可以帮助别人。

所以,这个想法是:

  1. 继续引用关闭处理程序线程。
  2. 用JNI设置自定义本地控制台处理程序例程。
  3. CTRL+CLOSE信号上调用自定义Java方法。
  4. 从该方法调用关闭处理程序。

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

Native 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"以指示正确的关机。

完整的源代码与编译的测试二进制文件。

除了使用SetConsoleCtrlHandler的上述答案之外,您还可以使用JNA执行此操作,而不是编写自己的本机代码。

你可以在kernel32上创建你自己的接口,或者使用在这个优秀的框架中提供的接口: https : //gitlab.com/axet/desktop

一些示例代码:

 import com.github.axet.desktop.os.win.GetLastErrorException; import com.github.axet.desktop.os.win.handle.HANDLER_ROUTINE; import com.github.axet.desktop.os.win.libs.coreel32Ex; ... private static HANDLER_ROUTINE handler = new HANDLER_ROUTINE() { @Override public long callback(long dwCtrlType) { if ((int)dwCtrlType == CTRL_CLOSE_EVENT) { // *** do your shutdown code here *** return 1; } return 0; } }; public static void assignShutdownHook() { if (!coreel32Ex.INSTANCE.SetConsoleCtrlHandler(handler, true)) throw new GetLastErrorException(); } 

请注意,我将匿名类分配给一个字段。 我最初在SetConsoleCtrlHandler的调用中定义了它,但是我认为它是由JVM收集的。

编辑4/9/17:更新了从github到gitlab的链接。

尽管批处理文件可能会被终止,但批处理文件已经运行的控制台(窗口)可能仍处于打开状态,具体取决于操作系统,命令处理器以及如何启动批处理文件执行(从命令提示符或通过捷径)。

taskkill是一个很好的命令来结束与Windows分发的程序(我假设你想停止一个不同的程序,而不是批处理文件本身)。

可以通过进程ID,可执行文件名称,窗口标题,状态(即不响应),DLL名称或服务名称来结束程序。

以下是一些基于如何在批处理文件中使用它的例子:

强制“program.exe”停止(通常需要/ f“强制”标志强制程序停止,只需通过反复试验检查是否需要该程序):

 taskkill /f /im program.exe 

停止任何不响应的程序:

 taskkill /fi "Status eq NOT RESPONDING" 

根据窗口标题停止一个程序(这里允许使用通配符来匹配任何东西):

 taskkill /fi "WindowTitle eq Please Login" taskkill /fi "WindowTitle eq Microsoft*" 

您甚至可以使用它来停止网络上另一台计算机上的程序(尽管在批处理文件中写入帐户的密码不是一个好主意)。

 taskkill /s JimsPC /u Jim /p James_007 /im firefox.exe 

Windows的另一种选择是tskill 虽然没有太多的选项,但是这些命令稍微简单一些。

编辑:

我不确定。 我会认为关闭cmd窗口类似于force-closing应用程序,即立即终止,没有进一步的通知。 这具有许多应用程序的行为,当它们被要求force-close ,它们实际上需要很长时间才能最终终止。 这是因为在操作系统释放所有资源的努力中,一些资源(特别是某些I / O和/或文件资源)不能立即放弃。

国际海事组织(IMO),如果它想要的话,Java什么也做不了。 强制关闭发生在操作系统级别,在这一点上,它释放内存,文件和I / O句柄,等等。Java没有(也没有任何其他程序)能够控制强制关闭。 此时,操作系统正在负责。

有人请纠正我,如果我错了。