2

I'm trying to require a user to confirm a Windows shutdown while my program is performing critical tasks.

In order to do this, I want to listen for a WM_QUERYENDSESSION message (in a seperate thread from the main program).

If this message is received, I'm calling ShutdownBlockReasonCreate() to prevent the shutdown.

You'll find my current implementation below.

Since the Win32 API also states that only visible windows are allowed to block shutdowns, i made the window that listens to the messages visible once a WM_QUERYENDSESSION is received.

My issue is, that the program does not seem to receive any WM_QUERYENDSESSION messages ever. Logging all messages did reveal it receiving other messages, as expected, as well as messages 799 and 800, which i could not find in any Windows Messages Documentation.

I'm greatful for any help you can give me, as I feel very lost here.

My current implementation:

use std::ptr::null_mut;
use winapi::shared::minwindef::{LPARAM, LRESULT, UINT, WPARAM, HINSTANCE};
use winapi::shared::windef::{HWND, HMENU};
use winapi::um::winuser::{
    CreateWindowExW, DefWindowProcW, DispatchMessageW, GetMessageW, PostQuitMessage,
    RegisterClassW, ShowWindow, TranslateMessage, MSG, CW_USEDEFAULT,
    WM_CREATE, WM_DESTROY, WM_QUERYENDSESSION, SW_HIDE, SW_SHOW,
    WS_EX_TOOLWINDOW,
    ShutdownBlockReasonCreate, ShutdownBlockReasonDestroy,
};

pub fn start_preventing_shutdown() {
    let class_name = "ProgramNameClass";
    let window_name = "ProgramName";
    let hinstance = null_mut();
    let hwnd_parent = null_mut();
    let hmenu = null_mut();
    let hwnd = create_window(
        class_name, window_name, hinstance, hwnd_parent, hmenu, SW_HIDE,
    );

    let mut msg: MSG = Default::default();
    loop {
        unsafe {
            GetMessageW(&mut msg, hwnd, 0, 0);
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }
}

fn create_window(
    class_name: &str,
    window_name: &str,
    hinstance: HINSTANCE,
    hwnd_parent: HWND,
    hmenu: HMENU,
    n_cmd_show: i32,
) -> HWND {
    let wnd_class = winapi::um::winuser::WNDCLASSW {
        style: 0,
        lpfnWndProc: Some(wnd_proc),
        hInstance: hinstance,
        lpszClassName: class_name.encode_utf16().collect::<Vec<u16>>().as_ptr(),
        cbClsExtra: 0,
        cbWndExtra: 0,
        hIcon: null_mut(),
        hCursor: null_mut(),
        hbrBackground: null_mut(),
        lpszMenuName: null_mut(),
    };
    unsafe {
        RegisterClassW(&wnd_class);
        let hwnd = CreateWindowExW(
            WS_EX_TOOLWINDOW, // set the window style to WS_EX_TOOLWINDOW
            class_name.encode_utf16().collect::<Vec<u16>>().as_ptr(),
            window_name.encode_utf16().collect::<Vec<u16>>().as_ptr(),
            0,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            hwnd_parent,
            hmenu,
            hinstance,
            null_mut(),
        );
        ShowWindow(hwnd, n_cmd_show); // hide the window when it is created
        hwnd
    }
}

unsafe extern "system" fn wnd_proc(
    hwnd: HWND,
    msg: UINT,
    w_param: WPARAM,
    l_param: LPARAM,
) -> LRESULT {

    match msg {
        WM_CREATE => {
            0
        }
        WM_DESTROY => {
            ShutdownBlockReasonDestroy(hwnd); // remove the shutdown block when the application is closing
            PostQuitMessage(0);
            0
        }
        WM_QUERYENDSESSION => {
            ShowWindow(hwnd, SW_SHOW); // show the window when the system is trying to shut down
            ShutdownBlockReasonCreate(hwnd, "Preventing shutdown".encode_utf16().collect::<Vec<u16>>().as_ptr()); // call ShutdownBlockReasonCreate before returning from the message handler
            1
        }
        _ => {
            DefWindowProcW(hwnd, msg, w_param, l_param)
        }
    }
}



MrSchildkr
  • 21
  • 2
  • 5
    Does the window receive a `WM_QUERYENDSESSION` message if it isn't hidden? – IInspectable Feb 28 '23 at 19:02
  • Wouldn't `"Preventing shutdown".encode_utf16().collect::>().as_ptr()` create a dangling pointer? The vector needs to outlive the pointer. – cdhowie Feb 28 '23 at 20:39
  • I also don't think `encode_utf16()` nul-terminates the generated string. I think you need to do something like this: `"Preventing shutdown\0".encode_utf16().collect::>()` and you then need to store this somewhere that will outlive winapi's use of the pointer. – cdhowie Feb 28 '23 at 20:50
  • The [windows](https://crates.io/crates/windows) crate has the convenient [`w!`](https://microsoft.github.io/windows-docs-rs/doc/windows/macro.w.html) macro to generate a UTF-16 encoded, null-terminated character string literal (with `'static` lifetime). – IInspectable Feb 28 '23 at 21:09
  • @IInspectable Nice, that is probably the better way to go then. – cdhowie Feb 28 '23 at 21:16

0 Answers0