Завершение работы Windows при запуске Java-приложения из сценария bat - PullRequest
17 голосов
/ 14 февраля 2012

У меня есть скрипт bat, который запускает Java-приложение.Если я нажимаю Ctrl + C на нем, приложение завершает работу изящно, вызывая все перехватчики завершения работы.Однако, если я просто закрою окно cmd скрипта bat, перехватчики отключения никогда не будут вызываться.

Есть ли способ решить эту проблему?Может быть, есть способ рассказать сценарию bat, как завершить работу вызываемых приложений, когда его окно закрыто?

Ответы [ 3 ]

19 голосов
/ 14 февраля 2012

С addShutdownHook документация:

В редких случаях виртуальная машина может прерваться, то есть прекратить работу, не выключившись полностью.Это происходит, когда виртуальная машина завершается извне, например, с помощью сигнала SIGKILL в Unix или вызова TerminateProcess в Microsoft Windows.

Так что, к сожалению, здесь я ничего не могу сделать.


CTRL-CLOSE сигнал в консоли Windows.Кажется, не настраивается.

Цитата выше ссылка:

Система генерирует сигнал CTRL+CLOSE, когда пользователь закрывает консоль.Все процессы, подключенные к консоли, получают сигнал, давая возможность каждому процессу очиститься перед завершением.Когда процесс получает этот сигнал, функция-обработчик может выполнить одно из следующих действий после выполнения любых операций очистки:

  • Вызвать ExitProcess, чтобы завершить процесс.
  • Возврат FALSE.Если ни одна из зарегистрированных функций-обработчиков не возвращает TRUE, обработчик по умолчанию завершает процесс.
  • Return TRUE.В этом случае никакие другие функции-обработчики не вызываются, и всплывающее диалоговое окно спрашивает пользователя, следует ли завершить процесс.Если пользователь решает не прерывать процесс, система не закрывает консоль, пока процесс не завершится окончательно.

UPD .Если для вас приемлемы собственные настройки, функция WinAPI SetConsoleCtrlHandler открывает путь для подавления поведения по умолчанию.

UPD2 . Откровения по обработке и завершению сигналов Java относительно старая статья, но раздел Написание обработчиков сигналов Java действительно может содержать то, что вам нужно.


UPD3 .Я пробовал обработчики сигналов Java из статьи выше.Он прекрасно работает с SIGINT, но это не то, что нам нужно, и я решил взять его с SetConsoleCtrlHandler.Результат немного сложен и, возможно, не стоит реализовывать в вашем проекте.В любом случае, это могло бы помочь кому-то еще.

Итак, идея заключалась в следующем:

  1. Сохраняйте ссылку на поток обработчика завершения работы.
  2. Установите пользовательскую процедуру обработчика встроенной консоли с помощью JNI.
  3. Вызов пользовательского метода Java для сигнала CTRL+CLOSE.
  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" для указания правильного завершения работы.

Заполните исходные коды скомпилированными тестовыми двоичными файлами здесь .

4 голосов
/ 31 января 2014

В дополнение к приведенному выше ответу об использовании 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.

Редактировать 09.09.17: Обновлена ​​ссылка с github на gitlab.

0 голосов
/ 14 февраля 2012

Несмотря на то, что пакетный файл может быть прерван, консоль (окно), в которой запущен пакетный файл, может быть оставлена ​​открытой, в зависимости от операционной системы, командного процессора и способа запуска выполнения пакетного файла (из командной строки). или через ярлык).

taskkill - хорошая команда для завершения программы, распространяемой с Windows (я предполагаю, что вы хотите остановить РАЗНУЮ программу, а не сам пакетный файл).

Можно завершать программы по идентификатору процесса, имени исполняемого файла, заголовку окна, состоянию (т.е. не отвечает), имени DLL или имени службы.

Вот несколько примеров, основанных на том, как вы можете использовать это в своем пакетном файле:

Принудительно завершить «program.exe» (часто требуется флаг / f «force», чтобы принудительно остановить программу, просто проверьте, нужен ли он для вашего приложения методом проб и ошибок):

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. Хотя у него не так много опций, команды немного проще.

EDIT:

Я не уверен, что есть. Я бы подумал, что закрытие окна cmd похоже на force-closing приложение, то есть немедленное завершение без дальнейшего уведомления. Это имеет поведение многих приложений, которые, когда их просят force-close, на самом деле, они требуют много времени для окончательного завершения. Это связано с тем, что в попытках ОС освободить все ресурсы некоторые ресурсы (особенно определенные ресурсы ввода-вывода и / или файлов) не освобождаются немедленно.

ИМО, Java ничего не может с этим поделать, если захочет. Принудительное закрытие происходит на уровне операционной системы, после чего он освобождает память, файл и дескрипторы ввода-вывода, которые используются, и т. Д. Java не (и не любая другая программа) не контролирует принудительное закрытие. На данный момент ОС берет на себя ответственность.

Кто-нибудь, пожалуйста, поправьте меня, если я ошибаюсь.

...