From 4f47d4482b22f3344a098874ffc87b38edb41c9b Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 22 Apr 2024 10:37:08 +0800 Subject: [PATCH] refact: win, idd control (#7789) * refact: win, idd control Signed-off-by: fufesou * refact: win device control, better addr of Signed-off-by: fufesou * refact: simple refact Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 3 +- Cargo.lock | 1 + Cargo.toml | 15 +- libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/lib.rs | 1 + res/msi/CustomActions/Common.h | 1 + res/msi/CustomActions/CustomActions.cpp | 49 +- res/msi/CustomActions/CustomActions.vcxproj | 3 +- res/msi/CustomActions/DeviceUtils.cpp | 84 ++++ res/msi/Package/Components/RustDesk.wxs | 2 - src/core_main.rs | 6 + src/platform/mod.rs | 3 + src/platform/win_device.rs | 485 ++++++++++++++++++++ src/platform/windows.rs | 37 +- src/privacy_mode.rs | 45 +- src/privacy_mode/win_topmost_window.rs | 4 - src/privacy_mode/win_virtual_display.rs | 4 - src/server/connection.rs | 2 +- src/virtual_display_manager.rs | 92 ++-- 19 files changed, 657 insertions(+), 181 deletions(-) create mode 100644 res/msi/CustomActions/DeviceUtils.cpp create mode 100644 src/platform/win_device.rs diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index fde9ff6cb..f6123e4a2 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -118,7 +118,7 @@ jobs: Expand-Archive usbmmidd_v2.zip -DestinationPath . python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack --virtual-display Remove-Item -Path usbmmidd_v2\Win32 -Recurse - Remove-Item -Path usbmmidd_v2\deviceinstaller.exe + Remove-Item -Path "usbmmidd_v2\deviceinstaller64.exe", "usbmmidd_v2\deviceinstaller.exe", "usbmmidd_v2\usbmmidd.bat" mv -Force .\usbmmidd_v2 ./flutter/build/windows/x64/runner/Release/ - name: find Runner.res @@ -264,6 +264,7 @@ jobs: echo "output_folder=./Release" >> $GITHUB_OUTPUT curl -LJ -o ./usbmmidd_v2.zip https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip unzip usbmmidd_v2.zip + rm -rf ./usbmmidd_v2/x64 ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/deviceinstaller64.exe ./usbmmidd_v2/usbmmidd.bat mv ./usbmmidd_v2 ./Release || true - name: find Runner.res diff --git a/Cargo.lock b/Cargo.lock index dd018c6b1..ce54262ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2904,6 +2904,7 @@ dependencies = [ "socket2 0.3.19", "sodiumoxide", "sysinfo", + "thiserror", "tokio", "tokio-socks", "tokio-util", diff --git a/Cargo.toml b/Cargo.toml index 10a0ecf8d..8a9d3d5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,20 @@ system_shutdown = "4.0" qrcode-generator = "4.1" [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi", "shellapi", "devguid", "setupapi", "cguid", "cfgmgr32"] } +winapi = { version = "0.3", features = [ + "winuser", + "wincrypt", + "shellscalingapi", + "pdh", + "synchapi", + "memoryapi", + "shellapi", + "devguid", + "setupapi", + "cguid", + "cfgmgr32", + "ioapiset", +] } winreg = "0.11" windows-service = "0.6" virtual_display = { path = "libs/virtual_display", optional = true } diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 2da853420..9f77abf0e 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -40,6 +40,7 @@ toml = "0.7" uuid = { version = "1.3", features = ["v4"] } # crash, versions >= 0.29.1 are affected by #GuillaumeGomez/sysinfo/1052 sysinfo = { git = "https://github.com/rustdesk-org/sysinfo" } +thiserror = "1.0" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index d360d3583..eed2331fb 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -51,6 +51,7 @@ pub use serde_json; pub use sysinfo; pub use toml; pub use uuid; +pub use thiserror; #[cfg(feature = "quic")] pub type Stream = quic::Connection; diff --git a/res/msi/CustomActions/Common.h b/res/msi/CustomActions/Common.h index 631fc5d38..e01262f00 100644 --- a/res/msi/CustomActions/Common.h +++ b/res/msi/CustomActions/Common.h @@ -13,3 +13,4 @@ bool MyStopServiceW(LPCWSTR serviceName); std::wstring ReadConfig(const std::wstring& filename, const std::wstring& key); +void UninstallDriver(LPCWSTR hardwareId, BOOL &rebootRequired); diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 9a019ea49..bea7c0525 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -70,7 +70,7 @@ UINT __stdcall RemoveInstallFolder( } LExit: - ReleaseStr(installFolder); + ReleaseStr(pwzData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); @@ -598,57 +598,14 @@ UINT __stdcall RemoveAmyuniIdd( HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; - int nResult = 0; - LPWSTR installFolder = NULL; - LPWSTR pwz = NULL; - LPWSTR pwzData = NULL; - - WCHAR workDir[1024] = L""; - DWORD fileAttributes = 0; - HINSTANCE hi = 0; - - SYSTEM_INFO si; - LPCWSTR exe = NULL; + BOOL rebootRequired = FALSE; hr = WcaInitialize(hInstall, "RemoveAmyuniIdd"); ExitOnFailure(hr, "Failed to initialize"); - hr = WcaGetProperty(L"CustomActionData", &pwzData); - ExitOnFailure(hr, "failed to get CustomActionData"); - - pwz = pwzData; - hr = WcaReadStringFromCaData(&pwz, &installFolder); - ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); - - hr = StringCchPrintfW(workDir, 1024, L"%lsusbmmidd_v2", installFolder); - ExitOnFailure(hr, "Failed to compose a resource identifier string"); - fileAttributes = GetFileAttributesW(workDir); - if (fileAttributes == INVALID_FILE_ATTRIBUTES) { - WcaLog(LOGMSG_STANDARD, "Amyuni idd dir \"%ls\" is out found, %d", workDir, fileAttributes); - goto LExit; - } - - GetNativeSystemInfo(&si); - if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - exe = L"deviceinstaller64.exe"; - } else { - // No need to check if is other architecture. - // Because the driver is only for x86 and x64. It will not work at on other architectures. - exe = L"deviceinstaller.exe"; - } - WcaLog(LOGMSG_STANDARD, "Remove amyuni idd %ls in %ls", exe, workDir); - hi = ShellExecuteW(NULL, L"open", exe, L"remove usbmmidd", workDir, SW_HIDE); - // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew - if ((int)hi <= 32) { - WcaLog(LOGMSG_STANDARD, "Failed to remove amyuni idd : %d, last error: %d", (int)hi, GetLastError()); - } - else { - WcaLog(LOGMSG_STANDARD, "Amyuni idd is removed"); - } + UninstallDriver(L"usbmmidd", rebootRequired); LExit: - ReleaseStr(installFolder); - er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index d2e5fb570..1bff7b154 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -60,6 +60,7 @@ + @@ -81,4 +82,4 @@ - + \ No newline at end of file diff --git a/res/msi/CustomActions/DeviceUtils.cpp b/res/msi/CustomActions/DeviceUtils.cpp new file mode 100644 index 000000000..5bc6c10bc --- /dev/null +++ b/res/msi/CustomActions/DeviceUtils.cpp @@ -0,0 +1,84 @@ +#include "pch.h" + +#include +#include +#include +#include + +#pragma comment(lib, "SetupAPI.lib") + + +void UninstallDriver(LPCWSTR hardwareId, BOOL &rebootRequired) +{ + HDEVINFO deviceInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT); + if (deviceInfoSet == INVALID_HANDLE_VALUE) + { + WcaLog(LOGMSG_STANDARD, "Failed to get device information set, last error: %d", GetLastError()); + return; + } + + SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail; + devInfoListDetail.cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA); + if (!SetupDiGetDeviceInfoListDetailW(deviceInfoSet, &devInfoListDetail)) + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + WcaLog(LOGMSG_STANDARD, "Failed to call SetupDiGetDeviceInfoListDetail, last error: %d", GetLastError()); + return; + } + + SP_DEVINFO_DATA deviceInfoData; + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + DWORD dataType; + WCHAR deviceId[MAX_DEVICE_ID_LEN] = { 0, }; + + DWORD deviceIndex = 0; + while (SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, &deviceInfoData)) + { + if (!SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID, &dataType, (PBYTE)deviceId, MAX_DEVICE_ID_LEN, NULL)) + { + WcaLog(LOGMSG_STANDARD, "Failed to get hardware id, last error: %d", GetLastError()); + deviceIndex++; + continue; + } + if (wcscmp(deviceId, hardwareId) != 0) + { + deviceIndex++; + continue; + } + + SP_REMOVEDEVICE_PARAMS remove_device_params; + remove_device_params.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + remove_device_params.ClassInstallHeader.InstallFunction = DIF_REMOVE; + remove_device_params.Scope = DI_REMOVEDEVICE_GLOBAL; + remove_device_params.HwProfile = 0; + + if (!SetupDiSetClassInstallParamsW(deviceInfoSet, &deviceInfoData, &remove_device_params.ClassInstallHeader, sizeof(SP_REMOVEDEVICE_PARAMS))) + { + WcaLog(LOGMSG_STANDARD, "Failed to set class install params, last error: %d", GetLastError()); + deviceIndex++; + continue; + } + + if (!SetupDiCallClassInstaller(DIF_REMOVE, deviceInfoSet, &deviceInfoData)) + { + WcaLog(LOGMSG_STANDARD, "ailed to uninstall driver, last error: %d", GetLastError()); + deviceIndex++; + continue; + } + + SP_DEVINSTALL_PARAMS deviceParams; + if (SetupDiGetDeviceInstallParamsW(deviceInfoSet, &deviceInfoData, &deviceParams)) + { + if (deviceParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) + { + rebootRequired = true; + } + } + + WcaLog(LOGMSG_STANDARD, "Driver uninstalled successfully"); + deviceIndex++; + } + + SetupDiDestroyDeviceInfoList(deviceInfoSet); +} diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index 31dbbc66d..a79f870b8 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -29,7 +29,6 @@ - @@ -74,7 +73,6 @@ - diff --git a/src/core_main.rs b/src/core_main.rs index 1f5de5e30..5055626cd 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -232,6 +232,12 @@ pub fn core_main() -> Option> { _is_run_as_system, ); return None; + } else if args[0] == "--uninstall-amyuni-idd" { + #[cfg(all(windows, feature = "virtual_display_driver"))] + hbb_common::allow_err!( + crate::virtual_display_manager::amyuni_idd::uninstall_driver() + ); + return None; } } if args[0] == "--remove" { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 8100b6516..6f7a028f3 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -8,6 +8,9 @@ pub use windows::*; #[cfg(windows)] pub mod windows; +#[cfg(windows)] +pub mod win_device; + #[cfg(target_os = "macos")] pub mod macos; diff --git a/src/platform/win_device.rs b/src/platform/win_device.rs new file mode 100644 index 000000000..f010c7f8a --- /dev/null +++ b/src/platform/win_device.rs @@ -0,0 +1,485 @@ +use hbb_common::{log, thiserror}; +use std::{ + ffi::OsStr, + io, + ops::{Deref, DerefMut}, + os::windows::ffi::OsStrExt, + ptr::null_mut, + result::Result, +}; +use winapi::{ + shared::{ + guiddef::GUID, + minwindef::{BOOL, DWORD, FALSE, MAX_PATH, PBOOL, TRUE}, + ntdef::{HANDLE, LPCWSTR, NULL}, + windef::HWND, + winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_ITEMS}, + }, + um::{ + cfgmgr32::MAX_DEVICE_ID_LEN, + fileapi::{CreateFileW, OPEN_EXISTING}, + handleapi::{CloseHandle, INVALID_HANDLE_VALUE}, + ioapiset::DeviceIoControl, + setupapi::*, + winnt::{GENERIC_READ, GENERIC_WRITE}, + }, +}; + +#[link(name = "Newdev")] +extern "system" { + fn UpdateDriverForPlugAndPlayDevicesW( + hwnd_parent: HWND, + hardware_id: LPCWSTR, + full_inf_path: LPCWSTR, + install_flags: DWORD, + b_reboot_required: PBOOL, + ) -> BOOL; +} + +#[derive(thiserror::Error, Debug)] +pub enum DeviceError { + #[error("Failed to call {0}, {1:?}")] + WinApiLastErr(String, io::Error), + #[error("Failed to call {0}, returns {1}")] + WinApiErrCode(String, DWORD), + #[error("{0}")] + Raw(String), +} + +struct DeviceInfo(HDEVINFO); + +impl DeviceInfo { + fn setup_di_create_device_info_list(class_guid: &mut GUID) -> Result { + let dev_info = unsafe { SetupDiCreateDeviceInfoList(class_guid, null_mut()) }; + if dev_info == null_mut() { + return Err(DeviceError::WinApiLastErr( + "SetupDiCreateDeviceInfoList".to_string(), + io::Error::last_os_error(), + )); + } + + Ok(Self(dev_info)) + } + + fn setup_di_get_class_devs_ex_w( + class_guid: *const GUID, + flags: DWORD, + ) -> Result { + let dev_info = unsafe { + SetupDiGetClassDevsExW( + class_guid, + null_mut(), + null_mut(), + flags, + null_mut(), + null_mut(), + null_mut(), + ) + }; + if dev_info == null_mut() { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetClassDevsExW".to_string(), + io::Error::last_os_error(), + )); + } + Ok(Self(dev_info)) + } +} + +impl Drop for DeviceInfo { + fn drop(&mut self) { + unsafe { + SetupDiDestroyDeviceInfoList(self.0); + } + } +} + +impl Deref for DeviceInfo { + type Target = HDEVINFO; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DeviceInfo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub unsafe fn install_driver( + inf_path: &str, + hardware_id: &str, + reboot_required: &mut bool, +) -> Result<(), DeviceError> { + let driver_inf_path = OsStr::new(inf_path) + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + let hardware_id = OsStr::new(hardware_id) + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + + let mut class_guid: GUID = std::mem::zeroed(); + let mut class_name: [u16; 32] = [0; 32]; + + if SetupDiGetINFClassW( + driver_inf_path.as_ptr(), + &mut class_guid, + class_name.as_mut_ptr(), + class_name.len() as _, + null_mut(), + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetINFClassW".to_string(), + io::Error::last_os_error(), + )); + } + + let dev_info = DeviceInfo::setup_di_create_device_info_list(&mut class_guid)?; + + let mut dev_info_data = SP_DEVINFO_DATA { + cbSize: std::mem::size_of::() as _, + ClassGuid: class_guid, + DevInst: 0, + Reserved: 0, + }; + if SetupDiCreateDeviceInfoW( + *dev_info, + class_name.as_ptr(), + &class_guid, + null_mut(), + null_mut(), + DICD_GENERATE_ID, + &mut dev_info_data, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiCreateDeviceInfoW".to_string(), + io::Error::last_os_error(), + )); + } + + if SetupDiSetDeviceRegistryPropertyW( + *dev_info, + &mut dev_info_data, + SPDRP_HARDWAREID, + hardware_id.as_ptr() as _, + (hardware_id.len() * 2) as _, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiSetDeviceRegistryPropertyW".to_string(), + io::Error::last_os_error(), + )); + } + + if SetupDiCallClassInstaller(DIF_REGISTERDEVICE, *dev_info, &mut dev_info_data) == FALSE { + return Err(DeviceError::WinApiLastErr( + "SetupDiCallClassInstaller".to_string(), + io::Error::last_os_error(), + )); + } + + let mut reboot_required_ = FALSE; + if UpdateDriverForPlugAndPlayDevicesW( + null_mut(), + hardware_id.as_ptr(), + driver_inf_path.as_ptr(), + 1, + &mut reboot_required_, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "UpdateDriverForPlugAndPlayDevicesW".to_string(), + io::Error::last_os_error(), + )); + } + *reboot_required = reboot_required_ == TRUE; + + Ok(()) +} + +unsafe fn is_same_hardware_id( + dev_info: &DeviceInfo, + devinfo_data: &mut SP_DEVINFO_DATA, + hardware_id: &str, +) -> Result { + let mut cur_hardware_id = [0u16; MAX_DEVICE_ID_LEN]; + if SetupDiGetDeviceRegistryPropertyW( + **dev_info, + devinfo_data, + SPDRP_HARDWAREID, + null_mut(), + cur_hardware_id.as_mut_ptr() as _, + cur_hardware_id.len() as _, + null_mut(), + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceRegistryPropertyW".to_string(), + io::Error::last_os_error(), + )); + } + + let cur_hardware_id = String::from_utf16_lossy(&cur_hardware_id) + .trim_end_matches(char::from(0)) + .to_string(); + Ok(cur_hardware_id == hardware_id) +} + +pub unsafe fn uninstall_driver( + hardware_id: &str, + reboot_required: &mut bool, +) -> Result<(), DeviceError> { + let dev_info = + DeviceInfo::setup_di_get_class_devs_ex_w(null_mut(), DIGCF_ALLCLASSES | DIGCF_PRESENT)?; + + let mut device_info_list_detail = SP_DEVINFO_LIST_DETAIL_DATA_W { + cbSize: std::mem::size_of::() as _, + ClassGuid: std::mem::zeroed(), + RemoteMachineHandle: null_mut(), + RemoteMachineName: [0; SP_MAX_MACHINENAME_LENGTH], + }; + if SetupDiGetDeviceInfoListDetailW(*dev_info, &mut device_info_list_detail) == FALSE { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceInfoListDetailW".to_string(), + io::Error::last_os_error(), + )); + } + + let mut devinfo_data = SP_DEVINFO_DATA { + cbSize: std::mem::size_of::() as _, + ClassGuid: std::mem::zeroed(), + DevInst: 0, + Reserved: 0, + }; + + let mut device_index = 0; + loop { + if SetupDiEnumDeviceInfo(*dev_info, device_index, &mut devinfo_data) == FALSE { + let err = io::Error::last_os_error(); + if err.raw_os_error() == Some(ERROR_NO_MORE_ITEMS as _) { + break; + } + return Err(DeviceError::WinApiLastErr( + "SetupDiEnumDeviceInfo".to_string(), + err, + )); + } + + match is_same_hardware_id(&dev_info, &mut devinfo_data, hardware_id) { + Ok(false) => { + device_index += 1; + continue; + } + Err(e) => { + log::error!("Failed to call is_same_hardware_id, {:?}", e); + device_index += 1; + continue; + } + _ => {} + } + + let mut remove_device_params = SP_REMOVEDEVICE_PARAMS { + ClassInstallHeader: SP_CLASSINSTALL_HEADER { + cbSize: std::mem::size_of::() as _, + InstallFunction: DIF_REMOVE, + }, + Scope: DI_REMOVEDEVICE_GLOBAL, + HwProfile: 0, + }; + + if SetupDiSetClassInstallParamsW( + *dev_info, + &mut devinfo_data, + &mut remove_device_params.ClassInstallHeader, + std::mem::size_of::() as _, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiSetClassInstallParams".to_string(), + io::Error::last_os_error(), + )); + } + + if SetupDiCallClassInstaller(DIF_REMOVE, *dev_info, &mut devinfo_data) == FALSE { + return Err(DeviceError::WinApiLastErr( + "SetupDiCallClassInstaller".to_string(), + io::Error::last_os_error(), + )); + } + + let mut device_params = SP_DEVINSTALL_PARAMS_W { + cbSize: std::mem::size_of::() as _, + Flags: 0, + FlagsEx: 0, + hwndParent: null_mut(), + InstallMsgHandler: None, + InstallMsgHandlerContext: null_mut(), + FileQueue: null_mut(), + ClassInstallReserved: 0, + Reserved: 0, + DriverPath: [0; MAX_PATH], + }; + + if SetupDiGetDeviceInstallParamsW(*dev_info, &mut devinfo_data, &mut device_params) == FALSE + { + log::error!( + "Failed to call SetupDiGetDeviceInstallParamsW, {:?}", + io::Error::last_os_error() + ); + } else { + if device_params.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT) != 0 { + *reboot_required = true; + } + } + + device_index += 1; + } + + Ok(()) +} + +pub unsafe fn device_io_control( + interface_guid: &GUID, + control_code: u32, + inbuf: &[u8], + outbuf_max_len: usize, +) -> Result, DeviceError> { + let h_device = open_device_handle(interface_guid)?; + let mut bytes_returned = 0; + let mut outbuf: Vec = vec![]; + let outbuf_ptr = if outbuf_max_len > 0 { + outbuf.reserve(outbuf_max_len); + outbuf.as_mut_ptr() + } else { + null_mut() + }; + let result = DeviceIoControl( + h_device, + control_code, + inbuf.as_ptr() as _, + inbuf.len() as _, + outbuf_ptr as _, + outbuf_max_len as _, + &mut bytes_returned, + null_mut(), + ); + CloseHandle(h_device); + if result == FALSE { + return Err(DeviceError::WinApiLastErr( + "DeviceIoControl".to_string(), + io::Error::last_os_error(), + )); + } + if outbuf_max_len > 0 { + outbuf.set_len(bytes_returned as _); + Ok(outbuf) + } else { + Ok(Vec::new()) + } +} + +unsafe fn get_device_path(interface_guid: &GUID) -> Result, DeviceError> { + let dev_info = DeviceInfo::setup_di_get_class_devs_ex_w( + interface_guid, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE, + )?; + let mut device_interface_data = SP_DEVICE_INTERFACE_DATA { + cbSize: std::mem::size_of::() as _, + InterfaceClassGuid: *interface_guid, + Flags: 0, + Reserved: 0, + }; + if SetupDiEnumDeviceInterfaces( + *dev_info, + null_mut(), + interface_guid, + 0, + &mut device_interface_data, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiEnumDeviceInterfaces".to_string(), + io::Error::last_os_error(), + )); + } + + let mut required_length = 0; + if SetupDiGetDeviceInterfaceDetailW( + *dev_info, + &mut device_interface_data, + null_mut(), + 0, + &mut required_length, + null_mut(), + ) == FALSE + { + let err = io::Error::last_os_error(); + if err.raw_os_error() != Some(ERROR_INSUFFICIENT_BUFFER as _) { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceInterfaceDetailW".to_string(), + err, + )); + } + } + + let predicted_length = required_length; + let mut vec_data: Vec = Vec::with_capacity(required_length as _); + let device_interface_detail_data = vec_data.as_mut_ptr(); + let device_interface_detail_data = + device_interface_detail_data as *mut SP_DEVICE_INTERFACE_DETAIL_DATA_W; + (*device_interface_detail_data).cbSize = + std::mem::size_of::() as _; + if SetupDiGetDeviceInterfaceDetailW( + *dev_info, + &mut device_interface_data, + device_interface_detail_data, + predicted_length, + &mut required_length, + null_mut(), + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceInterfaceDetailW".to_string(), + io::Error::last_os_error(), + )); + } + + let mut path = Vec::new(); + let device_path_ptr = std::ptr::addr_of!((*device_interface_detail_data).DevicePath) as *const u16; + let steps = device_path_ptr as usize - vec_data.as_ptr() as usize; + for i in 0..(predicted_length - steps as u32) / 2 { + if *device_path_ptr.offset(i as _) == 0 { + path.push(0); + break; + } + path.push(*device_path_ptr.offset(i as _)); + } + Ok(path) +} + +unsafe fn open_device_handle(interface_guid: &GUID) -> Result { + let device_path = get_device_path(interface_guid)?; + println!("device_path: {:?}", String::from_utf16_lossy(&device_path)); + let h_device = CreateFileW( + device_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + 0, + null_mut(), + OPEN_EXISTING, + 0, + null_mut(), + ); + if h_device == INVALID_HANDLE_VALUE || h_device == NULL { + return Err(DeviceError::WinApiLastErr( + "CreateFileW".to_string(), + io::Error::last_os_error(), + )); + } + Ok(h_device) +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 4e213c501..d0b995406 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -42,7 +42,6 @@ use winapi::{ }, securitybaseapi::GetTokenInformation, shellapi::ShellExecuteW, - sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO}, winbase::*, wingdi::*, winnt::{ @@ -2370,37 +2369,13 @@ impl Drop for WallPaperRemover { } } -pub fn get_amyuni_exe_name() -> Option { - let mut sys_info = SYSTEM_INFO::default(); - unsafe { - GetNativeSystemInfo(&mut sys_info as _); - } - const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0; - const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9; - - let exe = match unsafe { sys_info.u.s().wProcessorArchitecture } { - PROCESSOR_ARCHITECTURE_INTEL => "deviceinstaller.exe", - PROCESSOR_ARCHITECTURE_AMD64 => "deviceinstaller64.exe", - _ => { - log::error!("Unsupported machine architecture"); - return None; - } - }; - Some(exe.to_string()) -} - fn get_uninstall_amyuni_idd(path: &str) -> String { - let Some(exe) = get_amyuni_exe_name() else { - return "".to_string(); - }; - let work_dir = PathBuf::from(path).join("usbmmidd_v2"); - if work_dir.join(&exe).exists() { - format!( - "pushd {} && .\\{exe} remove usbmmidd && popd", - work_dir.to_string_lossy() - ) - } else { - "".to_string() + match std::env::current_exe() { + Ok(path) => format!("\"{}\" --uninstall-amyuni-idd", path.to_str().unwrap_or("")), + Err(e) => { + log::warn!("Failed to get current exe path, cannot get command of uninstalling idd, Zzerror: {:?}", e); + "".to_string() + } } } diff --git a/src/privacy_mode.rs b/src/privacy_mode.rs index f6eafcf41..859eef123 100644 --- a/src/privacy_mode.rs +++ b/src/privacy_mode.rs @@ -6,12 +6,7 @@ use crate::{ display_service, ipc::{connect, Data}, }; -use hbb_common::{ - anyhow::anyhow, - bail, lazy_static, - tokio::{self, sync::oneshot}, - ResultType, -}; +use hbb_common::{anyhow::anyhow, bail, lazy_static, tokio, ResultType}; use serde_derive::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -56,8 +51,6 @@ pub enum PrivacyModeState { } pub trait PrivacyMode: Sync + Send { - fn is_async_privacy_mode(&self) -> bool; - fn init(&self) -> ResultType<()>; fn clear(&mut self); fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType; @@ -207,41 +200,7 @@ fn get_supported_impl(impl_key: &str) -> String { cur_impl } -#[inline] -pub async fn turn_on_privacy(impl_key: &str, conn_id: i32) -> Option> { - if is_async_privacy_mode() { - turn_on_privacy_async(impl_key.to_string(), conn_id).await - } else { - turn_on_privacy_sync(impl_key, conn_id) - } -} - -#[inline] -fn is_async_privacy_mode() -> bool { - PRIVACY_MODE - .lock() - .unwrap() - .as_ref() - .map_or(false, |m| m.is_async_privacy_mode()) -} - -#[inline] -async fn turn_on_privacy_async(impl_key: String, conn_id: i32) -> Option> { - let (tx, rx) = oneshot::channel(); - std::thread::spawn(move || { - let res = turn_on_privacy_sync(&impl_key, conn_id); - let _ = tx.send(res); - }); - match hbb_common::timeout(5000, rx).await { - Ok(res) => match res { - Ok(res) => res, - Err(e) => Some(Err(anyhow!(e.to_string()))), - }, - Err(e) => Some(Err(anyhow!(e.to_string()))), - } -} - -fn turn_on_privacy_sync(impl_key: &str, conn_id: i32) -> Option> { +pub fn turn_on_privacy(impl_key: &str, conn_id: i32) -> Option> { // Check if privacy mode is already on or occupied by another one let mut privacy_mode_lock = PRIVACY_MODE.lock().unwrap(); diff --git a/src/privacy_mode/win_topmost_window.rs b/src/privacy_mode/win_topmost_window.rs index a7f80a02d..7fd27b60b 100644 --- a/src/privacy_mode/win_topmost_window.rs +++ b/src/privacy_mode/win_topmost_window.rs @@ -72,10 +72,6 @@ pub struct PrivacyModeImpl { } impl PrivacyMode for PrivacyModeImpl { - fn is_async_privacy_mode(&self) -> bool { - false - } - fn init(&self) -> ResultType<()> { Ok(()) } diff --git a/src/privacy_mode/win_virtual_display.rs b/src/privacy_mode/win_virtual_display.rs index 04a9d776c..7e7543d67 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -360,10 +360,6 @@ impl PrivacyModeImpl { } impl PrivacyMode for PrivacyModeImpl { - fn is_async_privacy_mode(&self) -> bool { - virtual_display_manager::is_amyuni_idd() - } - fn init(&self) -> ResultType<()> { Ok(()) } diff --git a/src/server/connection.rs b/src/server/connection.rs index 9cd9221c9..afd090a92 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2883,7 +2883,7 @@ impl Connection { } } - let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id).await; + let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id); match turn_on_res { Some(Ok(res)) => { if res { diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 1a0a03b01..4358e6561 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -402,58 +402,31 @@ pub mod rustdesk_idd { pub mod amyuni_idd { use super::windows; - use crate::platform::windows::get_amyuni_exe_name; + use crate::platform::win_device; use hbb_common::{bail, lazy_static, log, ResultType}; - use std::{ - ptr::null_mut, - sync::{Arc, Mutex}, + use std::sync::{Arc, Mutex}; + use winapi::shared::guiddef::GUID; + + const INF_PATH: &str = r#"usbmmidd_v2\usbmmIdd.inf"#; + const INTERFACE_GUID: GUID = GUID { + Data1: 0xb5ffd75f, + Data2: 0xda40, + Data3: 0x4353, + Data4: [0x8f, 0xf8, 0xb6, 0xda, 0xf6, 0xf1, 0xd8, 0xca], }; - use winapi::um::shellapi::ShellExecuteA; + const HARDWARE_ID: &str = "usbmmidd"; + const PLUG_MONITOR_IO_CONTROL_CDOE: u32 = 2307084; lazy_static::lazy_static! { static ref LOCK: Arc> = Default::default(); } - fn run_deviceinstaller(args: &str) -> ResultType<()> { - let Some(exe_name) = get_amyuni_exe_name() else { - bail!("Cannot get amyuni exe name.") - }; - - let cur_exe = std::env::current_exe()?; - let Some(cur_dir) = cur_exe.parent() else { - bail!("Cannot get parent of current exe file."); - }; - - let work_dir = cur_dir.join("usbmmidd_v2"); - if !work_dir.exists() { - bail!("usbmmidd_v2 does not exist.",); - } - let Some(work_dir) = work_dir.to_str() else { - bail!("Cannot convert work_dir to string."); - }; - let mut work_dir2 = work_dir.as_bytes().to_vec(); - work_dir2.push(0); - + pub fn uninstall_driver() -> ResultType<()> { + let mut reboot_required = false; unsafe { - const SW_HIDE: i32 = 0; - let mut args = args.bytes().collect::>(); - args.push(0); - let mut exe_name = exe_name.bytes().collect::>(); - exe_name.push(0); - let hi = ShellExecuteA( - null_mut(), - "open\0".as_ptr() as _, - exe_name.as_ptr() as _, - args.as_ptr() as _, - work_dir2.as_ptr() as _, - SW_HIDE, - ) as i32; - if hi <= 32 { - log::error!("Failed to run deviceinstaller: {}", hi); - bail!("Failed to run deviceinstaller.") - } - Ok(()) + win_device::uninstall_driver(HARDWARE_ID, &mut reboot_required)?; } + Ok(()) } fn check_install_driver() -> ResultType<()> { @@ -466,7 +439,32 @@ pub mod amyuni_idd { return Ok(()); } - run_deviceinstaller("install usbmmidd.inf usbmmidd") + let exe_file = std::env::current_exe()?; + let Some(cur_dir) = exe_file.parent() else { + bail!("Cannot get parent of current exe file"); + }; + + let inf_path = cur_dir.join(INF_PATH); + if !inf_path.exists() { + bail!("Driver inf file not found."); + } + let inf_path = inf_path.to_string_lossy().to_string(); + + let mut reboot_required = false; + unsafe { + win_device::install_driver(&inf_path, HARDWARE_ID, &mut reboot_required)?; + } + Ok(()) + } + + #[inline] + fn plug_in_monitor_(add: bool) -> ResultType<()> { + let cmd = if add { 0x10 } else { 0x00 }; + let cmd = [cmd, 0x00, 0x00, 0x00]; + unsafe { + win_device::device_io_control(&INTERFACE_GUID, PLUG_MONITOR_IO_CONTROL_CDOE, &cmd, 0)?; + } + Ok(()) } pub fn plug_in_headless() -> ResultType<()> { @@ -479,7 +477,7 @@ pub mod amyuni_idd { bail!("Failed to install driver."); } - run_deviceinstaller("enableidd 1") + plug_in_monitor_(true) } pub fn plug_in_monitor() -> ResultType<()> { @@ -492,7 +490,7 @@ pub mod amyuni_idd { bail!("There are already 4 monitors plugged in."); } - run_deviceinstaller("enableidd 1") + plug_in_monitor_(true) } pub fn plug_out_monitor(index: i32) -> ResultType<()> { @@ -527,7 +525,7 @@ pub mod amyuni_idd { to_plug_out_count = 1; } for _i in 0..to_plug_out_count { - let _ = run_deviceinstaller(&format!("enableidd 0")); + let _ = plug_in_monitor_(false); } Ok(()) }