diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 1285ef4368..b7d95bf96d 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -30,6 +30,8 @@ windows = {version = "0.32.0", features = [ "Win32_Foundation", "Win32_Security_Credentials", "Win32_System_WinRT", + "Win32_UI_Input_KeyboardAndMouse", + "Win32_UI_WindowsAndMessaging", ]} [target.'cfg(windows)'.dev-dependencies] diff --git a/apps/desktop/desktop_native/src/biometric/windows.rs b/apps/desktop/desktop_native/src/biometric/windows.rs index d956da064d..f49730cc55 100644 --- a/apps/desktop/desktop_native/src/biometric/windows.rs +++ b/apps/desktop/desktop_native/src/biometric/windows.rs @@ -1,19 +1,33 @@ use anyhow::Result; use windows::{ - core::factory, Foundation::IAsyncOperation, Security::Credentials::UI::*, - Win32::Foundation::HWND, Win32::System::WinRT::IUserConsentVerifierInterop, + core::{factory, HSTRING}, + Foundation::IAsyncOperation, + Security::Credentials::UI::*, + Win32::{ + Foundation::HWND, + System::WinRT::IUserConsentVerifierInterop, + UI::{ + Input::KeyboardAndMouse::{ + self, keybd_event, GetAsyncKeyState, SetFocus, KEYEVENTF_EXTENDEDKEY, + KEYEVENTF_KEYUP, VK_MENU, + }, + WindowsAndMessaging::{self, SetForegroundWindow}, + }, + }, }; pub fn prompt(hwnd: Vec, message: String) -> Result { - let interop = factory::()?; - - let h = isize::from_le_bytes(hwnd.try_into().unwrap()); + let h = isize::from_le_bytes(hwnd.clone().try_into().unwrap()); let window = HWND(h); - let operation: IAsyncOperation = - unsafe { interop.RequestVerificationForWindowAsync(window, message)? }; + // The Windows Hello prompt is displayed inside the application window. For best result we + // should set the window to the foreground and focus it. + set_focus(window); - let result: UserConsentVerificationResult = operation.get()?; + let interop = factory::()?; + let operation: IAsyncOperation = + unsafe { interop.RequestVerificationForWindowAsync(window, &HSTRING::from(message))? }; + let result = operation.get()?; match result { UserConsentVerificationResult::Verified => Ok(true), @@ -31,6 +45,31 @@ pub fn available() -> Result { } } +fn set_focus(window: HWND) { + let mut pressed = false; + + unsafe { + // Simulate holding down Alt key to bypass windows limitations + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate#return-value + // The most significant bit indicates if the key is currently being pressed. This means the + // value will be negative if the key is pressed. + if GetAsyncKeyState(VK_MENU.0 as i32) >= 0 { + pressed = true; + keybd_event(VK_MENU.0 as u8, 0, KEYEVENTF_EXTENDEDKEY, 0); + } + SetForegroundWindow(window); + SetFocus(window); + if pressed { + keybd_event( + VK_MENU.0 as u8, + 0, + KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, + 0, + ); + } + } +} + #[cfg(test)] mod tests { use super::*;