我有一个运行Java应用程序的蝙蝠脚本。 如果我按ctrl + c,应用程序将优雅地终止,调用所有的closures钩子。 但是,如果我只closuresbat脚本的cmd窗口,就不会调用closures钩子。
有没有办法解决这个问题? 也许有办法告诉蝙蝠脚本如何终止被closures的窗口被调用的应用程序?
来自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
来执行它。 结果有点复杂,可能不值得在你的项目中实现。 无论如何,它可以帮助别人。
所以,这个想法是:
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"); } }
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没有(也没有任何其他程序)能够控制强制关闭。 此时,操作系统正在负责。
有人请纠正我,如果我错了。