[英]Windows shutdown hook on java application run from a bat script
我有一個運行java應用程序的bat腳本。 如果我按下ctrl + c,它會正常終止,調用所有關閉鈎子。 但是,如果我只關閉bat腳本的cmd窗口,則永遠不會調用關閉掛鈎。
有辦法解決這個問題嗎? 也許有一種方法可以告訴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
來攜帶它。 結果有點復雜,可能不值得在您的項目中實現。 無論如何,它可以幫助別人。
所以,這個想法是:
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.Kernel32Ex;
...
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 (!Kernel32Ex.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
,它們實際上需要很長時間才能最終終止。 這是因為在OS努力釋放所有資源時,某些資源(特別是某些I / O和/或文件資源)不會立即釋放。
IMO,如果想要的話,Java甚至都無法做到。 在OS級別發生強制關閉,此時它會釋放正在使用的內存,文件和I / O句柄等.Java不會(也沒有任何其他程序)控制強制關閉。 此時,操作系統正在負責。
如果我錯了,請有人糾正我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.