JavaFX – как обнаружить запрос на выход из системы/завершение работы Windows?

У меня есть приложение, которое должно обрабатывать некоторые методы при выходе. Однако, когда пользователь завершает работу Windows без предварительного закрытия моего приложения, Windows убивает приложение, и методы завершения работы не запускаются.

Как я могу определить, когда пользователь запросил выключение или выход из Windows? Методы, которые мне нужно запустить, занимают миллисекунды, поэтому мне не нужно откладывать или прерывать процесс завершения работы.

Я уже использую JNA для ответа на блокировку/разблокировку машины, но метод onMachineLogoff(), похоже, также не перехватывает запросы на выключение.


person Zephyr    schedule 15.12.2017    source источник
comment
Посмотрите, если stackoverflow.com/questions /42598097/ помогает (у меня нет доступного банкомата с Windows, чтобы проверить это).   -  person James_D    schedule 15.12.2017
comment
Но когда использование выключает систему, система закрывает все приложения, и вы можете добавить свои методы в setOnCloseRequest().   -  person Menai Ala Eddine - Aladdin    schedule 15.12.2017
comment
@XlintXms setOnCloseRequest() относится к окну/сцене, а не ко всему приложению.   -  person James_D    schedule 15.12.2017
comment
@James_D, вы правы, потому что в приложении некоторые задачи могут выполняться в фоновом режиме без этапа.   -  person Menai Ala Eddine - Aladdin    schedule 15.12.2017
comment
@XlintXms На самом деле, я больше думал о случае, когда вы открываете несколько окон. Вы, вероятно, не хотите запускать методы выхода каждый раз, когда один из них закрывается; только когда последний закрыт.   -  person James_D    schedule 15.12.2017
comment
Спасибо, @James_D. Эта ссылка помогла перехватить запрос на завершение работы Windows. Однако ловушка завершения работы не срабатывает, когда пользователь просто выбирает «Выход из системы» в Windows.   -  person Zephyr    schedule 19.12.2017


Ответы (1)


Существует три различных сценария, когда вы можете обрабатывать события завершения работы и выхода из системы. Я сосредоточусь на приложении Windows, потому что оно работает и для консольных приложений, и если по какой-то причине ваше приложение импортирует функции User32, то дескриптор консоли не будет работать.

В основном вам понадобятся 2 функции:

ATOM RegisterClassEx(WNDCLASSEX *lpwcx);

RegisterClassEx создайте новый вид окна с хуком (это наш обработчик выключения/выхода из системы), связанный с ним.

HWND WINAPI CreateWindowEx(
    int dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName,
    int dwStyle, int x, int y, int nWidth, int nHeight,
    HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam
);

CreateWindowEx создает экземпляр новое окно, с этим окном связан обработчик событий (зарегистрированным классом), таким образом Windows уведомит связанный обработчик с помощью все возможные события.

Вот полный рабочий образец, просто запустите его и выйдите из системы или выключите компьютер, после запуска снова взгляните на %userprofile%\shutdown-hook.log файл, который, должно быть, обрабатывал эти события, регистрируя что-то вроде

...
action=proc-callback, event=22 
...

код

public class Main {

    /**
     * <pre>
     * Steps:
     *
     * 1. Create a  WinProc (this function will handle all events)
     * 2. Create a window class using the created WinProc
     * 3. Create a window using the created window class
     * 4. Use the WinProc to handle shutdown events
     * </pre>
     */
    public static void main(String[] args) {
//  registering a window - https://msdn.microsoft.com/pt-br/library/windows/desktop/ms633587
//                          https://msdn.microsoft.com/pt-br/library/windows/desktop/ms633577
//  typedef struct tagWNDCLASSEX {
//    UINT      cbSize;
//    UINT      style;
//    WNDPROC   lpfnWndProc;
//    int       cbClsExtra;
//    int       cbWndExtra;
//    HINSTANCE hInstance;
//    HICON     hIcon;
//    HCURSOR   hCursor;
//    HBRUSH    hbrBackground;
//    LPCTSTR   lpszMenuName;
//    LPCTSTR   lpszClassName;
//    HICON     hIconSm;
//  } WNDCLASSEX, *PWNDCLASSEX;
//
//  ATOM WINAPI RegisterClassEx(
//    _In_ const WNDCLASSEX *lpwcx
//  );
        final WinUser.WNDCLASSEX clazz = new WinUser.WNDCLASSEX();
        clazz.lpszClassName = "MyCustomWindow";
        clazz.cbSize = Native.getNativeSize(WinUser.WNDCLASSEX.class, null);
        clazz.lpfnWndProc = new MyWinProc();

        WinDef.ATOM classInst = User32.INSTANCE.RegisterClassEx(clazz);
        System.out.printf("action=registerclass, clazz=%s, error=%d\n", classInst, Native.getLastError());

        WinDef.HWND w = User32.INSTANCE.CreateWindowEx(
            512, clazz.lpszClassName, "My Window",
            WinUser.WS_OVERLAPPEDWINDOW, -2147483648, -2147483648, 250, 100,
            null, null, null, null
        );
        System.out.printf("action=createWindow, w=%s, error=%d\n", w, Native.getLastError());

        WinUser.MSG msg = new WinUser.MSG();
        while (User32.INSTANCE.GetMessage(msg, null, 0, 0)) {
            User32.INSTANCE.DispatchMessage(msg);
        }
    }

    public interface User32 extends Library {

        User32 INSTANCE = Native.loadLibrary("User32", User32.class, W32APIOptions.UNICODE_OPTIONS);

//      ATOM WINAPI RegisterClassEx(
//          _In_ const WNDCLASSEX *lpwcx
//      );
        WinDef.ATOM RegisterClassEx(WinUser.WNDCLASSEX lpwcx);

//  HWND WINAPI CreateWindowEx(
//    _In_     DWORD     dwExStyle,
//    _In_opt_ LPCTSTR   lpClassName,
//    _In_opt_ LPCTSTR   lpWindowName,
//    _In_     DWORD     dwStyle,
//    _In_     int       x,
//    _In_     int       y,
//    _In_     int       nWidth,
//    _In_     int       nHeight,
//    _In_opt_ HWND      hWndParent,
//    _In_opt_ HMENU     hMenu,
//    _In_opt_ HINSTANCE hInstance,
//    _In_opt_ LPVOID    lpParam
//  );
        WinDef.HWND CreateWindowEx(
            int dwExStyle,
            String lpClassName,
            String lpWindowName,
            int dwStyle,
            int x,
            int y,
            int nWidth,
            int nHeight,
            WinDef.HWND hWndParent,
            WinDef.HMENU hMenu,
            WinDef.HINSTANCE hInstance,
            WinDef.LPVOID lpParam
        );

//      BOOL WINAPI GetMessage(
//          _Out_    LPMSG lpMsg,
//          _In_opt_ HWND  hWnd,
//          _In_     UINT  wMsgFilterMin,
//          _In_     UINT  wMsgFilterMax
//      );
        boolean GetMessage(WinUser.MSG lpMsg, WinDef.HWND hWnd, int wMsgFilterMin, int wMsgFilterMax);

//      LRESULT WINAPI DispatchMessage(
//          _In_ const MSG *lpmsg
//      );
        WinDef.LRESULT DispatchMessage(WinUser.MSG lpmsg);

        WinDef.LRESULT DefWindowProc(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam);
    }

    /**
     * <pre>
     * All Possible events -
     * https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927.aspx#system_defined
     * https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/um/WinUser.h
     * </pre>
     */
    public static class MyWinProc implements WinUser.WindowProc {
        private final OutputStream out;

        public MyWinProc() {
            try {
                // this is unsafe because this file will never be closed, anyway it is just for a example
                out = new FileOutputStream(new File(System.getProperty("user.home") + File.separator + "shutdown-hook.log"));
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
            final String msg = String.format("action=proc-callback, event=%d %n", uMsg);
            System.out.print(msg);
            try {
                out.write(msg.getBytes());
                switch (uMsg){
                    case 0x0016:
                        out.write("shutdown".getBytes());
                        break;
                    case 0x0011:
                        out.write("logoff".getBytes());
                        break;
                }
                out.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
    }
}

obs: просто предложение, в зависимости от ваших требований, я думаю, что, возможно, может иметь больше смысла, если вместо этого вы просто запускаете фоновый поток и время от времени выполняете задачу, которую вы должны сделать, потому что, если Windows получает синий экран смерти или отключится питание, или кто-то просто отключит питание, а затем события Windows тебе не поможет. В любом случае решение фонового потока довольно простое.

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args) {
        Executors.newSingleThreadExecutor().execute(() -> {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("do a background stuff");
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {/*I will look at that in the while clause*/}
            }
        });
        System.out.println("doing another stuff");
    }
}

Мои зависимости

compile group: 'net.java.dev.jna', name: 'jna', version: '4.5.0'
compile group: 'net.java.dev.jna', name: 'jna-platform', version: '4.5.0'
person deFreitas    schedule 21.12.2017