diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-01-27 19:26:22 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-02-02 17:21:29 +0100 |
| commit | 3fbeb82c834abebf1a8852ea74ad59505c62c065 (patch) | |
| tree | 9998d665558f4a8b32f3447b75b6965c3964a092 | |
| parent | 8e5a68a39e0aa2884f44e817d6c5e088b9edf6b3 (diff) | |
| download | mullvadvpn-3fbeb82c834abebf1a8852ea74ad59505c62c065.tar.xz mullvadvpn-3fbeb82c834abebf1a8852ea74ad59505c62c065.zip | |
Add Windows windowing utilities module
| -rw-r--r-- | talpid-core/src/offline/windows.rs | 137 | ||||
| -rw-r--r-- | talpid-core/src/windows/mod.rs (renamed from talpid-core/src/windows.rs) | 2 | ||||
| -rw-r--r-- | talpid-core/src/windows/window.rs | 128 |
3 files changed, 142 insertions, 125 deletions
diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs index c04277aea5..15b5d47169 100644 --- a/talpid-core/src/offline/windows.rs +++ b/talpid-core/src/offline/windows.rs @@ -1,41 +1,21 @@ -use crate::winnet; +use crate::{ + windows::window::{create_hidden_window, WindowCloseHandle}, + winnet, +}; use futures::channel::mpsc::UnboundedSender; use parking_lot::Mutex; use std::{ ffi::c_void, io, - mem::zeroed, - os::windows::io::{IntoRawHandle, RawHandle}, - ptr, sync::{Arc, Weak}, thread, time::Duration, }; use talpid_types::ErrorExt; -use winapi::{ - shared::{ - basetsd::LONG_PTR, - minwindef::{DWORD, LPARAM, LRESULT, UINT, WPARAM}, - windef::HWND, - }, - um::{ - handleapi::CloseHandle, - libloaderapi::GetModuleHandleW, - processthreadsapi::GetThreadId, - synchapi::WaitForSingleObject, - winbase::INFINITE, - winuser::{ - CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW, - GetWindowLongPtrW, PostQuitMessage, PostThreadMessageW, SetWindowLongPtrW, - GWLP_USERDATA, GWLP_WNDPROC, PBT_APMRESUMEAUTOMATIC, PBT_APMSUSPEND, WM_DESTROY, - WM_POWERBROADCAST, WM_USER, - }, - }, +use winapi::um::winuser::{ + DefWindowProcW, PBT_APMRESUMEAUTOMATIC, PBT_APMSUSPEND, WM_POWERBROADCAST, }; -const CLASS_NAME: &[u8] = b"S\0T\0A\0T\0I\0C\0\0\0"; -const REQUEST_THREAD_SHUTDOWN: UINT = WM_USER + 1; - #[derive(err_derive::Error, Debug)] pub enum Error { #[error(display = "Unable to create listener thread")] @@ -45,8 +25,7 @@ pub enum Error { } pub struct BroadcastListener { - thread_handle: RawHandle, - thread_id: DWORD, + window: WindowCloseHandle, system_state: Arc<Mutex<SystemState>>, _callback_handle: winnet::WinNetCallbackHandle, _notify_tx: Arc<UnboundedSender<bool>>, @@ -67,7 +46,7 @@ impl BroadcastListener { let power_broadcast_state_ref = system_state.clone(); - let power_broadcast_callback = move |message: UINT, wparam: WPARAM, _lparam: LPARAM| { + let power_broadcast_callback = move |window, message, wparam, lparam| { let state = power_broadcast_state_ref.clone(); if message == WM_POWERBROADCAST { if wparam == PBT_APMSUSPEND { @@ -83,22 +62,16 @@ impl BroadcastListener { }); } } + unsafe { DefWindowProcW(window, message, wparam, lparam) } }; - let join_handle = thread::Builder::new() - .spawn(move || unsafe { - Self::message_pump(power_broadcast_callback); - }) - .map_err(Error::ThreadCreationError)?; - - let real_handle = join_handle.into_raw_handle(); + let window = create_hidden_window(power_broadcast_callback); let callback_handle = unsafe { Self::setup_network_connectivity_listener(system_state.clone())? }; Ok(BroadcastListener { - thread_handle: real_handle, - thread_id: unsafe { GetThreadId(real_handle) }, + window, system_state, _callback_handle: callback_handle, _notify_tx: notify_tx, @@ -131,88 +104,6 @@ impl BroadcastListener { (v4_connectivity, v6_connectivity) } - unsafe fn message_pump<F>(client_callback: F) - where - F: Fn(UINT, WPARAM, LPARAM), - { - let dummy_window = CreateWindowExW( - 0, - CLASS_NAME.as_ptr() as *const u16, - ptr::null_mut(), - 0, - 0, - 0, - 0, - 0, - ptr::null_mut(), - ptr::null_mut(), - GetModuleHandleW(ptr::null_mut()), - ptr::null_mut(), - ); - - // Move callback information to the heap. - // This enables us to reach the callback through a "thin pointer". - let boxed_callback = Box::new(client_callback); - - // Detach callback from Box. - let raw_callback = Box::into_raw(boxed_callback) as *mut c_void; - - SetWindowLongPtrW(dummy_window, GWLP_USERDATA, raw_callback as LONG_PTR); - SetWindowLongPtrW( - dummy_window, - GWLP_WNDPROC, - Self::window_procedure::<F> as LONG_PTR, - ); - - let mut msg = zeroed(); - - loop { - let status = GetMessageW(&mut msg, 0 as HWND, 0, 0); - - if status < 0 { - continue; - } - if status == 0 { - break; - } - - if msg.hwnd.is_null() { - if msg.message == REQUEST_THREAD_SHUTDOWN { - DestroyWindow(dummy_window); - } - } else { - DispatchMessageW(&mut msg); - } - } - - // Reattach callback to Box for proper clean-up. - let _ = Box::from_raw(raw_callback as *mut F); - } - - unsafe extern "system" fn window_procedure<F>( - window: HWND, - message: UINT, - wparam: WPARAM, - lparam: LPARAM, - ) -> LRESULT - where - F: Fn(UINT, WPARAM, LPARAM), - { - let raw_callback = GetWindowLongPtrW(window, GWLP_USERDATA); - - if raw_callback != 0 { - let typed_callback = &mut *(raw_callback as *mut F); - typed_callback(message, wparam, lparam); - } - - if message == WM_DESTROY { - PostQuitMessage(0); - return 0; - } - - DefWindowProcW(window, message, wparam, lparam) - } - /// The caller must make sure the `system_state` reference is valid /// until after `WinNet_DeactivateConnectivityMonitor` has been called. unsafe fn setup_network_connectivity_listener( @@ -252,11 +143,7 @@ impl BroadcastListener { impl Drop for BroadcastListener { fn drop(&mut self) { - unsafe { - PostThreadMessageW(self.thread_id, REQUEST_THREAD_SHUTDOWN, 0, 0); - WaitForSingleObject(self.thread_handle, INFINITE); - CloseHandle(self.thread_handle); - } + self.window.close(); } } diff --git a/talpid-core/src/windows.rs b/talpid-core/src/windows/mod.rs index 1febc5bc21..4ec1fe19f3 100644 --- a/talpid-core/src/windows.rs +++ b/talpid-core/src/windows/mod.rs @@ -31,6 +31,8 @@ use winapi::shared::{ ws2ipdef::{SOCKADDR_IN6_LH as sockaddr_in6, SOCKADDR_INET}, }; +pub mod window; + /// Result type for this module. pub type Result<T> = std::result::Result<T, Error>; diff --git a/talpid-core/src/windows/window.rs b/talpid-core/src/windows/window.rs new file mode 100644 index 0000000000..05e457e5d6 --- /dev/null +++ b/talpid-core/src/windows/window.rs @@ -0,0 +1,128 @@ +//! Utilities for windows. + +use std::{os::windows::io::AsRawHandle, ptr, thread}; +use winapi::{ + shared::{ + basetsd::LONG_PTR, + minwindef::{LPARAM, LRESULT, UINT, WPARAM}, + windef::HWND, + }, + um::{ + libloaderapi::GetModuleHandleW, + processthreadsapi::GetThreadId, + winuser::{ + CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW, + GetWindowLongPtrW, PostQuitMessage, PostThreadMessageW, SetWindowLongPtrW, + TranslateMessage, GWLP_USERDATA, GWLP_WNDPROC, WM_DESTROY, WM_USER, + }, + }, +}; + +const CLASS_NAME: &[u8] = b"S\0T\0A\0T\0I\0C\0\0\0"; +const REQUEST_THREAD_SHUTDOWN: UINT = WM_USER + 1; + +/// Handle for closing an associated window. +/// The window is not destroyed when this is dropped. +pub struct WindowCloseHandle { + thread: Option<std::thread::JoinHandle<()>>, +} + +impl WindowCloseHandle { + /// Close the window and wait for the thread. + pub fn close(&mut self) { + if let Some(thread) = self.thread.take() { + let thread_id = unsafe { GetThreadId(thread.as_raw_handle()) }; + unsafe { PostThreadMessageW(thread_id, REQUEST_THREAD_SHUTDOWN, 0, 0) }; + let _ = thread.join(); + } + } +} + +/// Creates a dummy window whose messages are handled by `wnd_proc`. +pub fn create_hidden_window<F: (Fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT) + Send + 'static>( + wnd_proc: F, +) -> WindowCloseHandle { + let join_handle = thread::spawn(move || { + let dummy_window = unsafe { + CreateWindowExW( + 0, + CLASS_NAME.as_ptr() as *const u16, + ptr::null_mut(), + 0, + 0, + 0, + 0, + 0, + ptr::null_mut(), + ptr::null_mut(), + GetModuleHandleW(ptr::null_mut()), + ptr::null_mut(), + ) + }; + + // Move callback information to the heap. + // This enables us to reach the callback through a "thin pointer". + let raw_callback = Box::into_raw(Box::new(wnd_proc)); + + unsafe { + SetWindowLongPtrW(dummy_window, GWLP_USERDATA, raw_callback as LONG_PTR); + SetWindowLongPtrW( + dummy_window, + GWLP_WNDPROC, + window_procedure::<F> as LONG_PTR, + ); + } + + let mut msg = unsafe { std::mem::zeroed() }; + + loop { + let status = unsafe { GetMessageW(&mut msg, ptr::null_mut(), 0, 0) }; + + if status < 0 { + continue; + } + if status == 0 { + break; + } + + if msg.hwnd.is_null() { + if msg.message == REQUEST_THREAD_SHUTDOWN { + unsafe { DestroyWindow(dummy_window) }; + } + } else { + unsafe { + TranslateMessage(&mut msg); + DispatchMessageW(&mut msg); + } + } + } + + // Free callback. + let _ = unsafe { Box::from_raw(raw_callback) }; + }); + + WindowCloseHandle { + thread: Some(join_handle), + } +} + +unsafe extern "system" fn window_procedure<F>( + window: HWND, + message: UINT, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT +where + F: Fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT, +{ + if message == WM_DESTROY { + PostQuitMessage(0); + return 0; + } + let raw_callback = GetWindowLongPtrW(window, GWLP_USERDATA); + if raw_callback != 0 { + let typed_callback = &mut *(raw_callback as *mut F); + return typed_callback(window, message, wparam, lparam); + } + DefWindowProcW(window, message, wparam, lparam) +} |
