2023-11-14 12:11:38 +08:00
|
|
|
use super::{PrivacyMode, PrivacyModeState, INVALID_PRIVACY_MODE_CONN_ID, NO_DISPLAYS};
|
|
|
|
use crate::virtual_display_manager;
|
|
|
|
use hbb_common::{allow_err, bail, config::Config, log, ResultType};
|
2023-11-20 17:15:15 +08:00
|
|
|
use std::{
|
|
|
|
io::Error,
|
|
|
|
ops::{Deref, DerefMut},
|
|
|
|
};
|
2023-11-14 12:11:38 +08:00
|
|
|
use virtual_display::MonitorMode;
|
|
|
|
use winapi::{
|
|
|
|
shared::{
|
|
|
|
minwindef::{DWORD, FALSE},
|
|
|
|
ntdef::{NULL, WCHAR},
|
|
|
|
},
|
|
|
|
um::{
|
|
|
|
wingdi::{
|
|
|
|
DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_ATTACHED_TO_DESKTOP,
|
|
|
|
DISPLAY_DEVICE_MIRRORING_DRIVER, DISPLAY_DEVICE_PRIMARY_DEVICE, DM_POSITION,
|
|
|
|
},
|
|
|
|
winuser::{
|
|
|
|
ChangeDisplaySettingsExW, EnumDisplayDevicesW, EnumDisplaySettingsExW,
|
|
|
|
EnumDisplaySettingsW, CDS_NORESET, CDS_RESET, CDS_SET_PRIMARY, CDS_UPDATEREGISTRY,
|
|
|
|
DISP_CHANGE_SUCCESSFUL, EDD_GET_DEVICE_INTERFACE_NAME, ENUM_CURRENT_SETTINGS,
|
|
|
|
ENUM_REGISTRY_SETTINGS,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_virtual_display";
|
|
|
|
|
|
|
|
const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0";
|
|
|
|
const CONFIG_KEY_REG_RECOVERY: &str = "reg_recovery";
|
|
|
|
|
|
|
|
struct Display {
|
|
|
|
dm: DEVMODEW,
|
|
|
|
name: [WCHAR; 32],
|
|
|
|
primary: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PrivacyModeImpl {
|
2023-11-19 23:48:18 +08:00
|
|
|
impl_key: String,
|
2023-11-14 12:11:38 +08:00
|
|
|
conn_id: i32,
|
|
|
|
displays: Vec<Display>,
|
|
|
|
virtual_displays: Vec<Display>,
|
|
|
|
virtual_displays_added: Vec<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TurnOnGuard<'a> {
|
|
|
|
privacy_mode: &'a mut PrivacyModeImpl,
|
|
|
|
succeeded: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Deref for TurnOnGuard<'a> {
|
|
|
|
type Target = PrivacyModeImpl;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
self.privacy_mode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> DerefMut for TurnOnGuard<'a> {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
self.privacy_mode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Drop for TurnOnGuard<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if !self.succeeded {
|
|
|
|
self.privacy_mode
|
|
|
|
.turn_off_privacy(INVALID_PRIVACY_MODE_CONN_ID, None)
|
|
|
|
.ok();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PrivacyModeImpl {
|
2023-11-19 23:48:18 +08:00
|
|
|
pub fn new(impl_key: &str) -> Self {
|
|
|
|
Self {
|
|
|
|
impl_key: impl_key.to_owned(),
|
|
|
|
conn_id: INVALID_PRIVACY_MODE_CONN_ID,
|
|
|
|
displays: Vec::new(),
|
|
|
|
virtual_displays: Vec::new(),
|
|
|
|
virtual_displays_added: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-14 12:11:38 +08:00
|
|
|
// mainly from https://github.com/fufesou/rustdesk/blob/44c3a52ca8502cf53b58b59db130611778d34dbe/libs/scrap/src/dxgi/mod.rs#L365
|
|
|
|
fn set_displays(&mut self) {
|
|
|
|
self.displays.clear();
|
|
|
|
self.virtual_displays.clear();
|
|
|
|
|
|
|
|
let mut i: DWORD = 0;
|
|
|
|
loop {
|
|
|
|
#[allow(invalid_value)]
|
|
|
|
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
|
|
|
|
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as _;
|
|
|
|
let ok = unsafe { EnumDisplayDevicesW(std::ptr::null(), i, &mut dd as _, 0) };
|
|
|
|
if ok == FALSE {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
if 0 == (dd.StateFlags & DISPLAY_DEVICE_ACTIVE)
|
|
|
|
|| (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#[allow(invalid_value)]
|
|
|
|
let mut dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
|
|
|
|
dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
|
|
|
|
dm.dmDriverExtra = 0;
|
|
|
|
unsafe {
|
|
|
|
if FALSE
|
|
|
|
== EnumDisplaySettingsExW(
|
|
|
|
dd.DeviceName.as_ptr(),
|
|
|
|
ENUM_CURRENT_SETTINGS,
|
|
|
|
&mut dm as _,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if FALSE
|
|
|
|
== EnumDisplaySettingsExW(
|
|
|
|
dd.DeviceName.as_ptr(),
|
|
|
|
ENUM_REGISTRY_SETTINGS,
|
|
|
|
&mut dm as _,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let primary = (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) > 0;
|
|
|
|
let display = Display {
|
|
|
|
dm,
|
|
|
|
name: dd.DeviceName,
|
|
|
|
primary,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Ok(s) = std::string::String::from_utf16(&dd.DeviceString) {
|
|
|
|
if &s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING {
|
|
|
|
self.virtual_displays.push(display);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.displays.push(display);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn restore(&mut self) {
|
|
|
|
Self::restore_displays(&self.displays);
|
|
|
|
Self::restore_displays(&self.virtual_displays);
|
|
|
|
allow_err!(Self::commit_change_display(0));
|
|
|
|
self.restore_plug_out_monitor();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn restore_plug_out_monitor(&mut self) {
|
|
|
|
let _ = virtual_display_manager::plug_out_peer_request(&self.virtual_displays_added);
|
|
|
|
self.virtual_displays_added.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn restore_displays(displays: &[Display]) {
|
|
|
|
for display in displays {
|
|
|
|
unsafe {
|
|
|
|
let mut dm = display.dm.clone();
|
|
|
|
let flags = if display.primary {
|
|
|
|
CDS_NORESET | CDS_UPDATEREGISTRY | CDS_SET_PRIMARY
|
|
|
|
} else {
|
|
|
|
CDS_NORESET | CDS_UPDATEREGISTRY
|
|
|
|
};
|
|
|
|
ChangeDisplaySettingsExW(
|
|
|
|
display.name.as_ptr(),
|
|
|
|
&mut dm,
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
flags,
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_primary_display(&mut self) -> ResultType<()> {
|
|
|
|
let display = &self.virtual_displays[0];
|
|
|
|
|
|
|
|
#[allow(invalid_value)]
|
|
|
|
let mut new_primary_dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
|
|
|
|
new_primary_dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
|
|
|
|
new_primary_dm.dmDriverExtra = 0;
|
|
|
|
unsafe {
|
|
|
|
if FALSE
|
|
|
|
== EnumDisplaySettingsW(
|
|
|
|
display.name.as_ptr(),
|
|
|
|
ENUM_CURRENT_SETTINGS,
|
|
|
|
&mut new_primary_dm,
|
|
|
|
)
|
|
|
|
{
|
|
|
|
bail!(
|
2023-11-20 17:38:53 +08:00
|
|
|
"Failed EnumDisplaySettingsW, device name: {:?}, error: {}",
|
2023-11-14 12:11:38 +08:00
|
|
|
std::string::String::from_utf16(&display.name),
|
2023-11-20 17:15:15 +08:00
|
|
|
Error::last_os_error()
|
2023-11-14 12:11:38 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut i: DWORD = 0;
|
|
|
|
loop {
|
|
|
|
let mut flags = CDS_UPDATEREGISTRY | CDS_NORESET;
|
|
|
|
#[allow(invalid_value)]
|
|
|
|
let mut dd: DISPLAY_DEVICEW = std::mem::MaybeUninit::uninit().assume_init();
|
|
|
|
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as _;
|
|
|
|
if FALSE
|
|
|
|
== EnumDisplayDevicesW(NULL as _, i, &mut dd, EDD_GET_DEVICE_INTERFACE_NAME)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if dd.DeviceName == display.name {
|
|
|
|
flags |= CDS_SET_PRIMARY;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(invalid_value)]
|
|
|
|
let mut dm: DEVMODEW = std::mem::MaybeUninit::uninit().assume_init();
|
|
|
|
dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
|
|
|
|
dm.dmDriverExtra = 0;
|
|
|
|
if FALSE
|
|
|
|
== EnumDisplaySettingsW(dd.DeviceName.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm)
|
|
|
|
{
|
|
|
|
bail!(
|
2023-11-20 17:38:53 +08:00
|
|
|
"Failed EnumDisplaySettingsW, device name: {:?}, error: {}",
|
2023-11-14 12:11:38 +08:00
|
|
|
std::string::String::from_utf16(&dd.DeviceName),
|
2023-11-20 17:15:15 +08:00
|
|
|
Error::last_os_error()
|
2023-11-14 12:11:38 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
dm.u1.s2_mut().dmPosition.x -= new_primary_dm.u1.s2().dmPosition.x;
|
|
|
|
dm.u1.s2_mut().dmPosition.y -= new_primary_dm.u1.s2().dmPosition.y;
|
|
|
|
dm.dmFields |= DM_POSITION;
|
|
|
|
let rc = ChangeDisplaySettingsExW(
|
|
|
|
dd.DeviceName.as_ptr(),
|
|
|
|
&mut dm,
|
|
|
|
NULL as _,
|
|
|
|
flags,
|
|
|
|
NULL,
|
|
|
|
);
|
|
|
|
|
|
|
|
if rc != DISP_CHANGE_SUCCESSFUL {
|
|
|
|
log::error!(
|
|
|
|
"Failed ChangeDisplaySettingsEx, device name: {:?}, flags: {}, ret: {}",
|
|
|
|
std::string::String::from_utf16(&dd.DeviceName),
|
|
|
|
flags,
|
|
|
|
rc
|
|
|
|
);
|
|
|
|
bail!("Failed ChangeDisplaySettingsEx, ret: {}", rc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn disable_physical_displays(&self) -> ResultType<()> {
|
|
|
|
for display in &self.displays {
|
|
|
|
let mut dm = display.dm.clone();
|
|
|
|
unsafe {
|
|
|
|
dm.u1.s2_mut().dmPosition.x = 10000;
|
|
|
|
dm.u1.s2_mut().dmPosition.y = 10000;
|
|
|
|
dm.dmPelsHeight = 0;
|
|
|
|
dm.dmPelsWidth = 0;
|
|
|
|
let flags = CDS_UPDATEREGISTRY | CDS_NORESET;
|
|
|
|
let rc = ChangeDisplaySettingsExW(
|
|
|
|
display.name.as_ptr(),
|
|
|
|
&mut dm,
|
|
|
|
NULL as _,
|
|
|
|
flags,
|
|
|
|
NULL as _,
|
|
|
|
);
|
|
|
|
if rc != DISP_CHANGE_SUCCESSFUL {
|
|
|
|
log::error!(
|
|
|
|
"Failed ChangeDisplaySettingsEx, device name: {:?}, flags: {}, ret: {}",
|
|
|
|
std::string::String::from_utf16(&display.name),
|
|
|
|
flags,
|
|
|
|
rc
|
|
|
|
);
|
|
|
|
bail!("Failed ChangeDisplaySettingsEx, ret: {}", rc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn default_display_modes() -> Vec<MonitorMode> {
|
|
|
|
vec![MonitorMode {
|
|
|
|
width: 1920,
|
|
|
|
height: 1080,
|
|
|
|
sync: 60,
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ensure_virtual_display(&mut self) -> ResultType<()> {
|
|
|
|
if self.virtual_displays.is_empty() {
|
|
|
|
let displays =
|
|
|
|
virtual_display_manager::plug_in_peer_request(vec![Self::default_display_modes()])?;
|
|
|
|
self.virtual_displays_added.extend(displays);
|
|
|
|
self.set_displays();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn commit_change_display(flags: DWORD) -> ResultType<()> {
|
|
|
|
unsafe {
|
|
|
|
// use winapi::{
|
|
|
|
// shared::windef::HDESK,
|
|
|
|
// um::{
|
|
|
|
// processthreadsapi::GetCurrentThreadId,
|
|
|
|
// winnt::MAXIMUM_ALLOWED,
|
|
|
|
// winuser::{CloseDesktop, GetThreadDesktop, OpenInputDesktop, SetThreadDesktop},
|
|
|
|
// },
|
|
|
|
// };
|
|
|
|
// let mut desk_input: HDESK = NULL as _;
|
|
|
|
// let desk_current: HDESK = GetThreadDesktop(GetCurrentThreadId());
|
|
|
|
// if !desk_current.is_null() {
|
|
|
|
// desk_input = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
|
|
|
|
// if desk_input.is_null() {
|
|
|
|
// SetThreadDesktop(desk_input);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
let ret = ChangeDisplaySettingsExW(NULL as _, NULL as _, NULL as _, flags, NULL as _);
|
|
|
|
if ret != DISP_CHANGE_SUCCESSFUL {
|
|
|
|
bail!("Failed ChangeDisplaySettingsEx, ret: {}", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if !desk_current.is_null() {
|
|
|
|
// SetThreadDesktop(desk_current);
|
|
|
|
// }
|
|
|
|
// if !desk_input.is_null() {
|
|
|
|
// CloseDesktop(desk_input);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PrivacyMode for PrivacyModeImpl {
|
|
|
|
fn init(&self) -> ResultType<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clear(&mut self) {
|
|
|
|
allow_err!(self.turn_off_privacy(self.conn_id, None));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType<bool> {
|
2023-11-20 21:44:25 +08:00
|
|
|
if !virtual_display_manager::is_virtual_display_supported() {
|
2023-11-21 11:04:58 +08:00
|
|
|
bail!("idd_not_support_under_win10_2004_tip");
|
2023-11-20 21:44:25 +08:00
|
|
|
}
|
|
|
|
|
2023-11-14 12:11:38 +08:00
|
|
|
if self.check_on_conn_id(conn_id)? {
|
|
|
|
log::debug!("Privacy mode of conn {} is already on", conn_id);
|
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
self.set_displays();
|
|
|
|
if self.displays.is_empty() {
|
|
|
|
log::debug!("No displays");
|
|
|
|
bail!(NO_DISPLAYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut guard = TurnOnGuard {
|
|
|
|
privacy_mode: self,
|
|
|
|
succeeded: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
guard.ensure_virtual_display()?;
|
|
|
|
if guard.virtual_displays.is_empty() {
|
|
|
|
log::debug!("No virtual displays");
|
|
|
|
bail!("No virtual displays");
|
|
|
|
}
|
|
|
|
|
|
|
|
let reg_connectivity_1 = reg_display_settings::read_reg_connectivity()?;
|
|
|
|
guard.set_primary_display()?;
|
|
|
|
guard.disable_physical_displays()?;
|
|
|
|
Self::commit_change_display(CDS_RESET)?;
|
|
|
|
let reg_connectivity_2 = reg_display_settings::read_reg_connectivity()?;
|
|
|
|
|
|
|
|
if let Some(reg_recovery) =
|
|
|
|
reg_display_settings::diff_recent_connectivity(reg_connectivity_1, reg_connectivity_2)
|
|
|
|
{
|
|
|
|
Config::set_option(
|
|
|
|
CONFIG_KEY_REG_RECOVERY.to_owned(),
|
|
|
|
serde_json::to_string(®_recovery)?,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
reset_config_reg_connectivity();
|
|
|
|
};
|
|
|
|
|
|
|
|
// OpenInputDesktop and block the others' input ?
|
|
|
|
guard.conn_id = conn_id;
|
|
|
|
guard.succeeded = true;
|
|
|
|
|
|
|
|
allow_err!(super::win_input::hook());
|
|
|
|
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn turn_off_privacy(
|
|
|
|
&mut self,
|
|
|
|
conn_id: i32,
|
|
|
|
state: Option<PrivacyModeState>,
|
|
|
|
) -> ResultType<()> {
|
|
|
|
self.check_off_conn_id(conn_id)?;
|
|
|
|
super::win_input::unhook()?;
|
|
|
|
self.restore();
|
|
|
|
restore_reg_connectivity();
|
|
|
|
|
|
|
|
if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID {
|
|
|
|
if let Some(state) = state {
|
|
|
|
allow_err!(super::set_privacy_mode_state(
|
|
|
|
conn_id,
|
|
|
|
state,
|
|
|
|
PRIVACY_MODE_IMPL.to_string(),
|
|
|
|
1_000
|
|
|
|
));
|
|
|
|
}
|
|
|
|
self.conn_id = INVALID_PRIVACY_MODE_CONN_ID.to_owned();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn pre_conn_id(&self) -> i32 {
|
|
|
|
self.conn_id
|
|
|
|
}
|
2023-11-19 23:48:18 +08:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn get_impl_key(&self) -> &str {
|
|
|
|
&self.impl_key
|
|
|
|
}
|
2023-11-14 12:11:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for PrivacyModeImpl {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID {
|
|
|
|
allow_err!(self.turn_off_privacy(self.conn_id, None));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn reset_config_reg_connectivity() {
|
|
|
|
Config::set_option(CONFIG_KEY_REG_RECOVERY.to_owned(), "".to_owned());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn restore_reg_connectivity() {
|
|
|
|
let config_recovery_value = Config::get_option(CONFIG_KEY_REG_RECOVERY);
|
|
|
|
if config_recovery_value.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if let Ok(reg_recovery) =
|
|
|
|
serde_json::from_str::<reg_display_settings::RegRecovery>(&config_recovery_value)
|
|
|
|
{
|
|
|
|
if let Err(e) = reg_display_settings::restore_reg_connectivity(reg_recovery) {
|
|
|
|
log::error!("Failed restore_reg_connectivity, error: {}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reset_config_reg_connectivity();
|
|
|
|
}
|
|
|
|
|
|
|
|
mod reg_display_settings {
|
|
|
|
use hbb_common::ResultType;
|
|
|
|
use serde_derive::{Deserialize, Serialize};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use winreg::{enums::*, RegValue};
|
|
|
|
const REG_GRAPHICS_DRIVERS_PATH: &str = "SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers";
|
|
|
|
const REG_CONNECTIVITY_PATH: &str = "Connectivity";
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub(super) struct RegRecovery {
|
|
|
|
path: String,
|
|
|
|
key: String,
|
|
|
|
old: (Vec<u8>, isize),
|
|
|
|
new: (Vec<u8>, isize),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn read_reg_connectivity() -> ResultType<HashMap<String, HashMap<String, RegValue>>>
|
|
|
|
{
|
|
|
|
let hklm = winreg::RegKey::predef(HKEY_LOCAL_MACHINE);
|
|
|
|
let reg_connectivity = hklm.open_subkey_with_flags(
|
|
|
|
format!("{}\\{}", REG_GRAPHICS_DRIVERS_PATH, REG_CONNECTIVITY_PATH),
|
|
|
|
KEY_READ,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let mut map_connectivity = HashMap::new();
|
|
|
|
for key in reg_connectivity.enum_keys() {
|
|
|
|
let key = key?;
|
|
|
|
let mut map_item = HashMap::new();
|
|
|
|
let reg_item = reg_connectivity.open_subkey_with_flags(&key, KEY_READ)?;
|
|
|
|
for value in reg_item.enum_values() {
|
|
|
|
let (name, value) = value?;
|
|
|
|
map_item.insert(name, value);
|
|
|
|
}
|
|
|
|
map_connectivity.insert(key, map_item);
|
|
|
|
}
|
|
|
|
Ok(map_connectivity)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn diff_recent_connectivity(
|
|
|
|
map1: HashMap<String, HashMap<String, RegValue>>,
|
|
|
|
map2: HashMap<String, HashMap<String, RegValue>>,
|
|
|
|
) -> Option<RegRecovery> {
|
|
|
|
for (subkey, map_item2) in map2 {
|
|
|
|
if let Some(map_item1) = map1.get(&subkey) {
|
|
|
|
let key = "Recent";
|
|
|
|
if let Some(value1) = map_item1.get(key) {
|
|
|
|
if let Some(value2) = map_item2.get(key) {
|
|
|
|
if value1 != value2 {
|
|
|
|
return Some(RegRecovery {
|
|
|
|
path: format!(
|
|
|
|
"{}\\{}\\{}",
|
|
|
|
REG_GRAPHICS_DRIVERS_PATH, REG_CONNECTIVITY_PATH, subkey
|
|
|
|
),
|
|
|
|
key: key.to_owned(),
|
|
|
|
old: (value1.bytes.clone(), value1.vtype.clone() as isize),
|
|
|
|
new: (value2.bytes.clone(), value2.vtype.clone() as isize),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn restore_reg_connectivity(reg_recovery: RegRecovery) -> ResultType<()> {
|
|
|
|
let hklm = winreg::RegKey::predef(HKEY_LOCAL_MACHINE);
|
|
|
|
let reg_item = hklm.open_subkey_with_flags(®_recovery.path, KEY_READ | KEY_WRITE)?;
|
|
|
|
let cur_reg_value = reg_item.get_raw_value(®_recovery.key)?;
|
|
|
|
let new_reg_value = RegValue {
|
|
|
|
bytes: reg_recovery.new.0,
|
|
|
|
vtype: isize_to_reg_type(reg_recovery.new.1),
|
|
|
|
};
|
|
|
|
if cur_reg_value != new_reg_value {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
let reg_value = RegValue {
|
|
|
|
bytes: reg_recovery.old.0,
|
|
|
|
vtype: isize_to_reg_type(reg_recovery.old.1),
|
|
|
|
};
|
|
|
|
reg_item.set_raw_value(®_recovery.key, ®_value)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn isize_to_reg_type(i: isize) -> RegType {
|
|
|
|
match i {
|
|
|
|
0 => RegType::REG_NONE,
|
|
|
|
1 => RegType::REG_SZ,
|
|
|
|
2 => RegType::REG_EXPAND_SZ,
|
|
|
|
3 => RegType::REG_BINARY,
|
|
|
|
4 => RegType::REG_DWORD,
|
|
|
|
5 => RegType::REG_DWORD_BIG_ENDIAN,
|
|
|
|
6 => RegType::REG_LINK,
|
|
|
|
7 => RegType::REG_MULTI_SZ,
|
|
|
|
8 => RegType::REG_RESOURCE_LIST,
|
|
|
|
9 => RegType::REG_FULL_RESOURCE_DESCRIPTOR,
|
|
|
|
10 => RegType::REG_RESOURCE_REQUIREMENTS_LIST,
|
|
|
|
11 => RegType::REG_QWORD,
|
|
|
|
_ => RegType::REG_NONE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|