diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml
index f6123e4a2..0168420f9 100644
--- a/.github/workflows/flutter-build.yml
+++ b/.github/workflows/flutter-build.yml
@@ -264,7 +264,10 @@ 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
+ # Do not remove x64 files, because the user may run the 32bit version on a 64bit system.
+ # Do not remove ./usbmmidd_v2/deviceinstaller64.exe, as x86 exe cannot install and uninstall drivers when running on x64,
+ # we need the x64 exe to install and uninstall the driver.
+ rm -rf ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/usbmmidd.bat
mv ./usbmmidd_v2 ./Release || true
- name: find Runner.res
diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp
index bea7c0525..c21035b8b 100644
--- a/res/msi/CustomActions/CustomActions.cpp
+++ b/res/msi/CustomActions/CustomActions.cpp
@@ -598,6 +598,19 @@ 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 = L"deviceinstaller64.exe";
+ WCHAR exePath[1024] = L"";
+
BOOL rebootRequired = FALSE;
hr = WcaInitialize(hInstall, "RemoveAmyuniIdd");
@@ -605,7 +618,49 @@ UINT __stdcall RemoveAmyuniIdd(
UninstallDriver(L"usbmmidd", rebootRequired);
+ // Only for x86 app on x64
+ GetNativeSystemInfo(&si);
+ if (si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64) {
+ goto LExit;
+ }
+
+ 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 not found, %d", workDir, fileAttributes);
+ goto LExit;
+ }
+
+ hr = StringCchPrintfW(exePath, 1024, L"%ls\\%ls", workDir, exe);
+ ExitOnFailure(hr, "Failed to compose a resource identifier string");
+ fileAttributes = GetFileAttributesW(exePath);
+ if (fileAttributes == INVALID_FILE_ATTRIBUTES) {
+ goto LExit;
+ }
+
+ 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");
+ }
+
LExit:
+ if (pwzData) {
+ ReleaseStr(pwzData);
+ }
+
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs
index a79f870b8..31dbbc66d 100644
--- a/res/msi/Package/Components/RustDesk.wxs
+++ b/res/msi/Package/Components/RustDesk.wxs
@@ -29,6 +29,7 @@
+
@@ -73,6 +74,7 @@
+
diff --git a/src/platform/win_device.rs b/src/platform/win_device.rs
index f010c7f8a..265006502 100644
--- a/src/platform/win_device.rs
+++ b/src/platform/win_device.rs
@@ -46,16 +46,20 @@ pub enum DeviceError {
Raw(String),
}
+impl DeviceError {
+ #[inline]
+ fn new_api_last_err(api: &str) -> Self {
+ Self::WinApiLastErr(api.to_string(), io::Error::last_os_error())
+ }
+}
+
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(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiCreateDeviceInfoList"));
}
Ok(Self(dev_info))
@@ -77,10 +81,7 @@ impl DeviceInfo {
)
};
if dev_info == null_mut() {
- return Err(DeviceError::WinApiLastErr(
- "SetupDiGetClassDevsExW".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiGetClassDevsExW"));
}
Ok(Self(dev_info))
}
@@ -133,10 +134,7 @@ pub unsafe fn install_driver(
null_mut(),
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiGetINFClassW".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiGetINFClassW"));
}
let dev_info = DeviceInfo::setup_di_create_device_info_list(&mut class_guid)?;
@@ -157,10 +155,7 @@ pub unsafe fn install_driver(
&mut dev_info_data,
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiCreateDeviceInfoW".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiCreateDeviceInfoW"));
}
if SetupDiSetDeviceRegistryPropertyW(
@@ -171,17 +166,13 @@ pub unsafe fn install_driver(
(hardware_id.len() * 2) as _,
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiSetDeviceRegistryPropertyW".to_string(),
- io::Error::last_os_error(),
+ return Err(DeviceError::new_api_last_err(
+ "SetupDiSetDeviceRegistryPropertyW",
));
}
if SetupDiCallClassInstaller(DIF_REGISTERDEVICE, *dev_info, &mut dev_info_data) == FALSE {
- return Err(DeviceError::WinApiLastErr(
- "SetupDiCallClassInstaller".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiCallClassInstaller"));
}
let mut reboot_required_ = FALSE;
@@ -193,9 +184,8 @@ pub unsafe fn install_driver(
&mut reboot_required_,
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "UpdateDriverForPlugAndPlayDevicesW".to_string(),
- io::Error::last_os_error(),
+ return Err(DeviceError::new_api_last_err(
+ "UpdateDriverForPlugAndPlayDevicesW",
));
}
*reboot_required = reboot_required_ == TRUE;
@@ -219,9 +209,8 @@ unsafe fn is_same_hardware_id(
null_mut(),
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiGetDeviceRegistryPropertyW".to_string(),
- io::Error::last_os_error(),
+ return Err(DeviceError::new_api_last_err(
+ "SetupDiGetDeviceRegistryPropertyW",
));
}
@@ -245,9 +234,8 @@ pub unsafe fn uninstall_driver(
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(),
+ return Err(DeviceError::new_api_last_err(
+ "SetupDiGetDeviceInfoListDetailW",
));
}
@@ -300,17 +288,13 @@ pub unsafe fn uninstall_driver(
std::mem::size_of::() as _,
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiSetClassInstallParams".to_string(),
- io::Error::last_os_error(),
+ return Err(DeviceError::new_api_last_err(
+ "SetupDiSetClassInstallParams",
));
}
if SetupDiCallClassInstaller(DIF_REMOVE, *dev_info, &mut devinfo_data) == FALSE {
- return Err(DeviceError::WinApiLastErr(
- "SetupDiCallClassInstaller".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiCallClassInstaller"));
}
let mut device_params = SP_DEVINSTALL_PARAMS_W {
@@ -371,10 +355,7 @@ pub unsafe fn device_io_control(
);
CloseHandle(h_device);
if result == FALSE {
- return Err(DeviceError::WinApiLastErr(
- "DeviceIoControl".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("DeviceIoControl"));
}
if outbuf_max_len > 0 {
outbuf.set_len(bytes_returned as _);
@@ -403,10 +384,7 @@ unsafe fn get_device_path(interface_guid: &GUID) -> Result, DeviceError
&mut device_interface_data,
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiEnumDeviceInterfaces".to_string(),
- io::Error::last_os_error(),
- ));
+ return Err(DeviceError::new_api_last_err("SetupDiEnumDeviceInterfaces"));
}
let mut required_length = 0;
@@ -444,14 +422,14 @@ unsafe fn get_device_path(interface_guid: &GUID) -> Result, DeviceError
null_mut(),
) == FALSE
{
- return Err(DeviceError::WinApiLastErr(
- "SetupDiGetDeviceInterfaceDetailW".to_string(),
- io::Error::last_os_error(),
+ return Err(DeviceError::new_api_last_err(
+ "SetupDiGetDeviceInterfaceDetailW",
));
}
let mut path = Vec::new();
- let device_path_ptr = std::ptr::addr_of!((*device_interface_detail_data).DevicePath) as *const u16;
+ 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 {
@@ -465,7 +443,6 @@ unsafe fn get_device_path(interface_guid: &GUID) -> Result, DeviceError
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,
@@ -476,10 +453,7 @@ unsafe fn open_device_handle(interface_guid: &GUID) -> Result String {
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
",
before_uninstall=get_before_uninstall(kill_self),
- uninstall_amyuni_idd=get_uninstall_amyuni_idd(&path),
+ uninstall_amyuni_idd=get_uninstall_amyuni_idd(),
app_name = crate::get_app_name(),
)
}
@@ -2369,7 +2370,7 @@ impl Drop for WallPaperRemover {
}
}
-fn get_uninstall_amyuni_idd(path: &str) -> String {
+fn get_uninstall_amyuni_idd() -> String {
match std::env::current_exe() {
Ok(path) => format!("\"{}\" --uninstall-amyuni-idd", path.to_str().unwrap_or("")),
Err(e) => {
@@ -2390,3 +2391,13 @@ pub fn is_service_running(service_name: &str) -> bool {
is_service_running_w(service_name.as_ptr() as _)
}
}
+
+pub fn is_x64() -> bool {
+ const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9;
+
+ let mut sys_info = SYSTEM_INFO::default();
+ unsafe {
+ GetNativeSystemInfo(&mut sys_info as _);
+ }
+ unsafe { sys_info.u.s().wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 }
+}
diff --git a/src/privacy_mode.rs b/src/privacy_mode.rs
index 859eef123..c062fbff8 100644
--- a/src/privacy_mode.rs
+++ b/src/privacy_mode.rs
@@ -6,7 +6,12 @@ use crate::{
display_service,
ipc::{connect, Data},
};
-use hbb_common::{anyhow::anyhow, bail, lazy_static, tokio, ResultType};
+use hbb_common::{
+ anyhow::anyhow,
+ bail, lazy_static,
+ tokio::{self, sync::oneshot},
+ ResultType,
+};
use serde_derive::{Deserialize, Serialize};
use std::{
collections::HashMap,
@@ -51,6 +56,8 @@ 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;
@@ -200,7 +207,40 @@ fn get_supported_impl(impl_key: &str) -> String {
cur_impl
}
-pub fn turn_on_privacy(impl_key: &str, conn_id: i32) -> Option> {
+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> {
// 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 7fd27b60b..fdfcfcba6 100644
--- a/src/privacy_mode/win_topmost_window.rs
+++ b/src/privacy_mode/win_topmost_window.rs
@@ -72,6 +72,10 @@ 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 7e7543d67..04a9d776c 100644
--- a/src/privacy_mode/win_virtual_display.rs
+++ b/src/privacy_mode/win_virtual_display.rs
@@ -360,6 +360,10 @@ 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 afd090a92..9cd9221c9 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);
+ let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id).await;
match turn_on_res {
Some(Ok(res)) => {
if res {
diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs
index 4358e6561..72a39a987 100644
--- a/src/virtual_display_manager.rs
+++ b/src/virtual_display_manager.rs
@@ -403,9 +403,16 @@ pub mod rustdesk_idd {
pub mod amyuni_idd {
use super::windows;
use crate::platform::win_device;
- use hbb_common::{bail, lazy_static, log, ResultType};
- use std::sync::{Arc, Mutex};
- use winapi::shared::guiddef::GUID;
+ use hbb_common::{bail, lazy_static, log, tokio::time::Instant, ResultType};
+ use std::{
+ ptr::null_mut,
+ sync::{Arc, Mutex},
+ time::Duration,
+ };
+ use winapi::{
+ shared::{guiddef::GUID, winerror::ERROR_NO_MORE_ITEMS},
+ um::shellapi::ShellExecuteA,
+ };
const INF_PATH: &str = r#"usbmmidd_v2\usbmmIdd.inf"#;
const INTERFACE_GUID: GUID = GUID {
@@ -416,49 +423,116 @@ pub mod amyuni_idd {
};
const HARDWARE_ID: &str = "usbmmidd";
const PLUG_MONITOR_IO_CONTROL_CDOE: u32 = 2307084;
+ const INSTALLER_EXE_FILE: &str = "deviceinstaller64.exe";
lazy_static::lazy_static! {
static ref LOCK: Arc> = Default::default();
}
+ fn get_deviceinstaller64_work_dir() -> ResultType