簡體   English   中英

從bat腳本運行java應用程序上的Windows關閉掛鈎

[英]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來攜帶它。 結果有點復雜,可能不值得在您的項目中實現。 無論如何,它可以幫助別人。

所以,這個想法是:

  1. 保持對shutdown handler thread的引用。
  2. 使用JNI設置自定義本機控制台處理程序例程。
  3. CTRL+CLOSE信號上調用自定義Java方法。
  4. 從該方法調用shutdown handler。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM