refactor(windows_api): use handle trait from 0.20

The 0.20.0 release of windows-rs includes a Handle trait which provides
ok() and invalid() fns for implementors, including HWND and HANDLE.

This is pretty cool (and also a big breaking change since the release
takes away is_null() at the same time...), so the code in windows_api.rs
has been updated to make use of this by implementing a
ProcessWindowsCrateResult trait with a process() fn.

When implemented for a windows::Result<T>, it will do any required
processing for T, and ensure that windows::Error is converted to an
eyre-compatible Report.

Switching to this means that I have been able to get rid of some of the
hacky error handling for weird behaviours encountered previously. So
far, they don't seem to be presenting again, but I will run with this
build for a couple of days to see if the false-negative errors are
really gone for good with this update.
This commit is contained in:
LGUG2Z
2021-09-20 18:21:40 -07:00
parent 63cf48daa5
commit 2807cafdd0
4 changed files with 96 additions and 112 deletions

28
Cargo.lock generated
View File

@@ -1245,9 +1245,9 @@ dependencies = [
[[package]]
name = "tracing"
version = "0.1.27"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ba9ab62b7d6497a8638dfda5e5c4fb3b2d5a7fca4118f2b96151c8ef1a437e"
checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
@@ -1319,9 +1319,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.2.23"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c42e73a9d277d4d2b6a88389a137ccf3c58599660b17e8f5fc39305e490669"
checksum = "fdd0568dbfe3baf7048b7908d2b32bca0d81cd56bec6d2a8f894b01d74f86be3"
dependencies = [
"ansi_term",
"chrono",
@@ -1452,9 +1452,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef84dd25f4c69a271b1bba394532bf400523b43169de21dfc715e8f8e491053d"
checksum = "0a0b63f34b1cf0fcb7a2e387189936a7c9822123ef124a95da2b8a0b493bc69d"
dependencies = [
"const-sha1",
"windows_gen",
@@ -1463,9 +1463,9 @@ dependencies = [
[[package]]
name = "windows_gen"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac7bb21b8ff5e801232b72a6ff554b4cc0cef9ed9238188c3ca78fe3968a7e5d"
checksum = "7213e17fead412ec608804cbe190988db6f40b2a946ef58dd67fd9cdf39da144"
dependencies = [
"windows_quote",
"windows_reader",
@@ -1473,9 +1473,9 @@ dependencies = [
[[package]]
name = "windows_macros"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5566b8c51118769e4a9094a688bf1233a3f36aacbfc78f3b15817fe0b6e0442f"
checksum = "661a56e1edb9f9d466a9cb59c392edfad0d273b66bb20b1f5f4aea6db5ad35d6"
dependencies = [
"syn",
"windows_gen",
@@ -1485,15 +1485,15 @@ dependencies = [
[[package]]
name = "windows_quote"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4af8236a9493c38855f95cdd11b38b342512a5df4ee7473cffa828b5ebb0e39c"
checksum = "6d16ae0ecb5b0a365ff465ca9b9780e70986f951b4e06a95f87ac54a421d3767"
[[package]]
name = "windows_reader"
version = "0.19.0"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8d5cf83fb08083438c5c46723e6206b2970da57ce314f80b57724439aaacab"
checksum = "c75040b326c26dda15a9c18970a7a15bf503dc22597d55dd559df16435f4a550"
[[package]]
name = "winput"

View File

@@ -7,7 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
windows = "0.19"
windows = "0.20"
[build-dependencies]
windows = "0.19"
windows = "0.20"

View File

@@ -1 +1,4 @@
pub use windows::Handle;
pub use windows::Result;
::windows::include_bindings!();

View File

@@ -7,6 +7,8 @@ use color_eyre::eyre::anyhow;
use color_eyre::eyre::Error;
use color_eyre::Result;
use bindings::Handle;
use bindings::Result as WindowsCrateResult;
use bindings::Windows::Win32::Foundation::BOOL;
use bindings::Windows::Win32::Foundation::HANDLE;
use bindings::Windows::Win32::Foundation::HWND;
@@ -92,36 +94,6 @@ pub enum WindowsResult<T, E> {
Ok(T),
}
impl From<BOOL> for WindowsResult<(), Error> {
fn from(return_value: BOOL) -> Self {
if return_value.as_bool() {
Self::Ok(())
} else {
Self::Err(std::io::Error::last_os_error().into())
}
}
}
impl From<HWND> for WindowsResult<isize, Error> {
fn from(return_value: HWND) -> Self {
if return_value.is_null() {
Self::Err(std::io::Error::last_os_error().into())
} else {
Self::Ok(return_value.0)
}
}
}
impl From<HANDLE> for WindowsResult<HANDLE, Error> {
fn from(return_value: HANDLE) -> Self {
if return_value.is_null() {
Self::Err(std::io::Error::last_os_error().into())
} else {
Self::Ok(return_value)
}
}
}
macro_rules! impl_from_integer_for_windows_result {
( $( $integer_type:ty ),+ ) => {
$(
@@ -148,6 +120,40 @@ impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
}
}
pub trait ProcessWindowsCrateResult<T> {
fn process(self) -> Result<T>;
}
macro_rules! impl_process_windows_crate_result {
( $($input:ty => $deref:ty),+ $(,)? ) => (
paste::paste! {
$(
impl ProcessWindowsCrateResult<$deref> for WindowsCrateResult<$input> {
fn process(self) -> Result<$deref> {
match self {
Ok(value) => Ok(value.0),
Err(error) => Err(error.into()),
}
}
}
)+
}
);
}
impl_process_windows_crate_result!(
HWND => isize,
);
impl<T> ProcessWindowsCrateResult<T> for WindowsCrateResult<T> {
fn process(self) -> Result<T> {
match self {
Ok(value) => Ok(value),
Err(error) => Err(error.into()),
}
}
}
pub struct WindowsApi;
impl WindowsApi {
@@ -155,14 +161,16 @@ impl WindowsApi {
callback: MONITORENUMPROC,
callback_data_address: isize,
) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
unsafe {
EnumDisplayMonitors(
HDC(0),
std::ptr::null_mut(),
Option::from(callback),
LPARAM(callback_data_address),
)
}))
}
.ok()
.process()
}
pub fn valid_hmonitors() -> Result<Vec<isize>> {
@@ -184,9 +192,9 @@ impl WindowsApi {
}
pub fn enum_windows(callback: WNDENUMPROC, callback_data_address: isize) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
EnumWindows(Option::from(callback), LPARAM(callback_data_address))
}))
unsafe { EnumWindows(Option::from(callback), LPARAM(callback_data_address)) }
.ok()
.process()
}
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> {
@@ -225,9 +233,9 @@ impl WindowsApi {
}
pub fn allow_set_foreground_window(process_id: u32) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
AllowSetForegroundWindow(process_id)
}))
unsafe { AllowSetForegroundWindow(process_id) }
.ok()
.process()
}
pub fn monitor_from_window(hwnd: HWND) -> isize {
@@ -250,7 +258,7 @@ impl WindowsApi {
}
pub fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
unsafe {
SetWindowPos(
hwnd,
position,
@@ -260,7 +268,9 @@ impl WindowsApi {
layout.bottom,
SET_WINDOW_POS_FLAGS(flags),
)
}))
}
.ok()
.process()
}
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
@@ -282,39 +292,25 @@ impl WindowsApi {
}
pub fn foreground_window() -> Result<isize> {
Result::from(WindowsResult::from(unsafe { GetForegroundWindow() }))
unsafe { GetForegroundWindow() }.ok().process()
}
pub fn set_foreground_window(hwnd: HWND) -> Result<()> {
match WindowsResult::from(unsafe { SetForegroundWindow(hwnd) }) {
WindowsResult::Ok(_) => Ok(()),
WindowsResult::Err(error) => {
// TODO: Figure out the odd behaviour here, docs state that a zero value means
// TODO: that the window was not brought to the foreground, but this contradicts
// TODO: the behaviour that I have observed which resulted in this check
if error.to_string() == "The operation completed successfully. (os error 0)" {
Ok(())
} else {
Err(error)
}
}
}
unsafe { SetForegroundWindow(hwnd) }.ok().process()
}
#[allow(dead_code)]
pub fn top_window() -> Result<isize> {
Result::from(WindowsResult::from(unsafe { GetTopWindow(HWND::NULL).0 }))
unsafe { GetTopWindow(HWND::default()) }.ok().process()
}
pub fn desktop_window() -> Result<isize> {
Result::from(WindowsResult::from(unsafe { GetDesktopWindow() }))
unsafe { GetDesktopWindow() }.ok().process()
}
#[allow(dead_code)]
pub fn next_window(hwnd: HWND) -> Result<isize> {
Result::from(WindowsResult::from(unsafe {
GetWindow(hwnd, GW_HWNDNEXT).0
}))
unsafe { GetWindow(hwnd, GW_HWNDNEXT) }.ok().process()
}
#[allow(dead_code)]
@@ -335,30 +331,24 @@ impl WindowsApi {
pub fn window_rect(hwnd: HWND) -> Result<Rect> {
let mut rect = unsafe { std::mem::zeroed() };
Result::from(WindowsResult::from(unsafe {
GetWindowRect(hwnd, &mut rect)
}))?;
unsafe { GetWindowRect(hwnd, &mut rect) }.ok().process()?;
Ok(Rect::from(rect))
}
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
Result::from(WindowsResult::from(unsafe { SetCursorPos(x, y) }))
unsafe { SetCursorPos(x, y) }.ok().process()
}
pub fn cursor_pos() -> Result<POINT> {
let mut cursor_pos: POINT = unsafe { std::mem::zeroed() };
Result::from(WindowsResult::from(unsafe {
GetCursorPos(&mut cursor_pos)
}))?;
let mut cursor_pos = POINT::default();
unsafe { GetCursorPos(&mut cursor_pos) }.ok().process()?;
Ok(cursor_pos)
}
pub fn window_from_point(point: POINT) -> Result<isize> {
Result::from(WindowsResult::from(unsafe { WindowFromPoint(point) }))
unsafe { WindowFromPoint(point) }.ok().process()
}
pub fn window_at_cursor_pos() -> Result<isize> {
@@ -388,24 +378,13 @@ impl WindowsApi {
}
pub fn attach_thread_input(thread_id: u32, target_thread_id: u32, attach: bool) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
AttachThreadInput(thread_id, target_thread_id, attach)
}))
unsafe { AttachThreadInput(thread_id, target_thread_id, attach) }
.ok()
.process()
}
pub fn set_focus(hwnd: HWND) -> Result<()> {
match WindowsResult::from(unsafe { SetFocus(hwnd) }) {
WindowsResult::Ok(_) => Ok(()),
WindowsResult::Err(error) => {
// If the window is not attached to the calling thread's message queue, the return value is NULL
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setfocus
if error.to_string() == "The operation completed successfully. (os error 0)" {
Ok(())
} else {
Err(error)
}
}
}
unsafe { SetFocus(hwnd) }.ok().map(|_| ()).process()
}
#[allow(dead_code)]
@@ -457,9 +436,9 @@ impl WindowsApi {
inherit_handle: bool,
process_id: u32,
) -> Result<HANDLE> {
Result::from(WindowsResult::from(unsafe {
OpenProcess(access_rights, inherit_handle, process_id)
}))
unsafe { OpenProcess(access_rights, inherit_handle, process_id) }
.ok()
.process()
}
pub fn process_handle(process_id: u32) -> Result<HANDLE> {
@@ -471,14 +450,16 @@ impl WindowsApi {
let mut path: Vec<u16> = vec![0; len as usize];
let text_ptr = path.as_mut_ptr();
Result::from(WindowsResult::from(unsafe {
unsafe {
QueryFullProcessImageNameW(
handle,
PROCESS_NAME_FORMAT(0),
PWSTR(text_ptr),
&mut len as *mut u32,
)
}))?;
}
.ok()
.process()?;
Ok(String::from_utf16(&path[..len as usize])?)
}
@@ -553,9 +534,9 @@ impl WindowsApi {
let mut monitor_info: MONITORINFO = unsafe { std::mem::zeroed() };
monitor_info.cbSize = u32::try_from(std::mem::size_of::<MONITORINFO>())?;
Result::from(WindowsResult::from(unsafe {
GetMonitorInfoW(hmonitor, (&mut monitor_info as *mut MONITORINFO).cast())
}))?;
unsafe { GetMonitorInfoW(hmonitor, (&mut monitor_info as *mut MONITORINFO).cast()) }
.ok()
.process()?;
Ok(monitor_info)
}
@@ -577,9 +558,9 @@ impl WindowsApi {
pv_param: *mut c_void,
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
) -> Result<()> {
Result::from(WindowsResult::from(unsafe {
SystemParametersInfoW(action, ui_param, pv_param, update_flags)
}))
unsafe { SystemParametersInfoW(action, ui_param, pv_param, update_flags) }
.ok()
.process()
}
#[allow(dead_code)]