rustdesk/src/virtual_display_manager.rs

841 lines
28 KiB
Rust
Raw Normal View History

use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultType};
use std::sync::atomic;
// This string is defined here.
// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40
pub const RUSTDESK_IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0";
pub const AMYUNI_IDD_DEVICE_STRING: &'static str = "USB Mobile Monitor Virtual Display\0";
const IDD_IMPL: &str = IDD_IMPL_AMYUNI;
const IDD_IMPL_RUSTDESK: &str = "rustdesk_idd";
const IDD_IMPL_AMYUNI: &str = "amyuni_idd";
const IS_CAN_PLUG_OUT_ALL_NOT_SET: i8 = 0;
const IS_CAN_PLUG_OUT_ALL_YES: i8 = 1;
const IS_CAN_PLUG_OUT_ALL_NO: i8 = 2;
static IS_CAN_PLUG_OUT_ALL: atomic::AtomicI8 = atomic::AtomicI8::new(IS_CAN_PLUG_OUT_ALL_NOT_SET);
pub fn is_can_plug_out_all() -> bool {
IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) != IS_CAN_PLUG_OUT_ALL_NO
}
// No need to consider concurrency here.
pub fn set_can_plug_out_all(v: bool) {
if IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) == IS_CAN_PLUG_OUT_ALL_NOT_SET {
IS_CAN_PLUG_OUT_ALL.store(
if v {
IS_CAN_PLUG_OUT_ALL_YES
} else {
IS_CAN_PLUG_OUT_ALL_NO
},
atomic::Ordering::Relaxed,
);
}
}
pub fn is_amyuni_idd() -> bool {
IDD_IMPL == IDD_IMPL_AMYUNI
}
pub fn get_cur_device_string() -> &'static str {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => RUSTDESK_IDD_DEVICE_STRING,
IDD_IMPL_AMYUNI => AMYUNI_IDD_DEVICE_STRING,
_ => "",
}
}
pub fn is_virtual_display_supported() -> bool {
#[cfg(target_os = "windows")]
{
is_windows_version_or_greater(10, 0, 19041, 0, 0)
}
#[cfg(not(target_os = "windows"))]
{
false
}
}
pub fn plug_in_headless() -> ResultType<()> {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_headless(),
IDD_IMPL_AMYUNI => amyuni_idd::plug_in_headless(),
_ => bail!("Unsupported virtual display implementation."),
}
}
pub fn get_platform_additions() -> serde_json::Map<String, serde_json::Value> {
let mut map = serde_json::Map::new();
if !crate::platform::windows::is_self_service_running() {
return map;
}
map.insert("idd_impl".into(), serde_json::json!(IDD_IMPL));
match IDD_IMPL {
IDD_IMPL_RUSTDESK => {
let virtual_displays = rustdesk_idd::get_virtual_displays();
if !virtual_displays.is_empty() {
map.insert(
"rustdesk_virtual_displays".into(),
serde_json::json!(virtual_displays),
);
}
}
IDD_IMPL_AMYUNI => {
let c = amyuni_idd::get_monitor_count();
if c > 0 {
map.insert("amyuni_virtual_displays".into(), serde_json::json!(c));
}
}
_ => {}
}
map
}
#[inline]
pub fn plug_in_monitor(idx: u32, modes: Vec<virtual_display::MonitorMode>) -> ResultType<()> {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_index_modes(idx, modes),
IDD_IMPL_AMYUNI => amyuni_idd::plug_in_monitor(),
_ => bail!("Unsupported virtual display implementation."),
}
}
pub fn plug_out_monitor(index: i32) -> ResultType<()> {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => {
let indices = if index == -1 {
rustdesk_idd::get_virtual_displays()
} else {
vec![index as _]
};
rustdesk_idd::plug_out_peer_request(&indices)
}
IDD_IMPL_AMYUNI => amyuni_idd::plug_out_monitor(index),
_ => bail!("Unsupported virtual display implementation."),
}
}
pub fn plug_in_peer_request(modes: Vec<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_peer_request(modes),
IDD_IMPL_AMYUNI => {
amyuni_idd::plug_in_monitor()?;
Ok(vec![0])
}
_ => bail!("Unsupported virtual display implementation."),
}
}
pub fn plug_out_monitor_indices(indices: &[u32]) -> ResultType<()> {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_out_peer_request(indices),
IDD_IMPL_AMYUNI => {
for _idx in indices.iter() {
amyuni_idd::plug_out_monitor(0)?;
}
Ok(())
}
_ => bail!("Unsupported virtual display implementation."),
}
}
pub fn reset_all() -> ResultType<()> {
match IDD_IMPL {
IDD_IMPL_RUSTDESK => rustdesk_idd::reset_all(),
IDD_IMPL_AMYUNI => crate::privacy_mode::turn_off_privacy(0, None).unwrap_or(Ok(())),
_ => bail!("Unsupported virtual display implementation."),
}
}
pub mod rustdesk_idd {
use super::windows;
use hbb_common::{allow_err, bail, lazy_static, log, ResultType};
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex},
};
// virtual display index range: 0 - 2 are reserved for headless and other special uses.
const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0;
const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 1;
const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 5;
lazy_static::lazy_static! {
static ref VIRTUAL_DISPLAY_MANAGER: Arc<Mutex<VirtualDisplayManager>> =
Arc::new(Mutex::new(VirtualDisplayManager::default()));
}
#[derive(Default)]
struct VirtualDisplayManager {
headless_index_name: Option<(u32, String)>,
peer_index_name: HashMap<u32, String>,
is_driver_installed: bool,
}
impl VirtualDisplayManager {
fn prepare_driver(&mut self) -> ResultType<()> {
if !self.is_driver_installed {
self.install_update_driver()?;
}
Ok(())
}
fn install_update_driver(&mut self) -> ResultType<()> {
if let Err(e) = virtual_display::create_device() {
if !e.to_string().contains("Device is already created") {
bail!("Create device failed {}", e);
}
}
// Reboot is not required for this case.
let mut _reboot_required = false;
virtual_display::install_update_driver(&mut _reboot_required)?;
self.is_driver_installed = true;
Ok(())
}
fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> {
if let Err(e) = virtual_display::plug_in_monitor(index) {
bail!("Plug in monitor failed {}", e);
}
if let Err(e) = virtual_display::update_monitor_modes(index, &modes) {
log::error!("Update monitor modes failed {}", e);
}
Ok(())
}
}
pub fn install_update_driver() -> ResultType<()> {
VIRTUAL_DISPLAY_MANAGER
.lock()
.unwrap()
.install_update_driver()
}
#[inline]
fn get_device_names() -> Vec<String> {
windows::get_device_names(Some(super::RUSTDESK_IDD_DEVICE_STRING))
}
pub fn plug_in_headless() -> ResultType<()> {
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
manager.prepare_driver()?;
let modes = [virtual_display::MonitorMode {
width: 1920,
height: 1080,
sync: 60,
}];
let device_names = get_device_names().into_iter().collect();
VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?;
let device_name = get_new_device_name(&device_names);
manager.headless_index_name = Some((VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, device_name));
Ok(())
}
pub fn plug_out_headless() -> bool {
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
if let Some((index, _)) = manager.headless_index_name.take() {
if let Err(e) = virtual_display::plug_out_monitor(index) {
log::error!("Plug out monitor failed {}", e);
}
true
} else {
false
}
}
fn get_new_device_name(device_names: &HashSet<String>) -> String {
for _ in 0..3 {
let device_names_af: HashSet<String> = get_device_names().into_iter().collect();
let diff_names: Vec<_> = device_names_af.difference(&device_names).collect();
if diff_names.len() == 1 {
return diff_names[0].clone();
} else if diff_names.len() > 1 {
log::error!(
"Failed to get diff device names after plugin virtual display, more than one diff names: {:?}",
&diff_names
);
return "".to_string();
}
// Sleep is needed here to wait for the virtual display to be ready.
std::thread::sleep(std::time::Duration::from_millis(50));
}
log::error!("Failed to get diff device names after plugin virtual display",);
"".to_string()
}
pub fn get_virtual_displays() -> Vec<u32> {
VIRTUAL_DISPLAY_MANAGER
.lock()
.unwrap()
.peer_index_name
.keys()
.cloned()
.collect()
}
pub fn plug_in_index_modes(
idx: u32,
mut modes: Vec<virtual_display::MonitorMode>,
) -> ResultType<()> {
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
manager.prepare_driver()?;
if !manager.peer_index_name.contains_key(&idx) {
let device_names = get_device_names().into_iter().collect();
if modes.is_empty() {
modes.push(virtual_display::MonitorMode {
width: 1920,
height: 1080,
sync: 60,
});
}
match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) {
Ok(_) => {
let device_name = get_new_device_name(&device_names);
manager.peer_index_name.insert(idx, device_name);
}
Err(e) => {
log::error!("Plug in monitor failed {}", e);
}
}
}
Ok(())
}
pub fn reset_all() -> ResultType<()> {
if super::is_virtual_display_supported() {
return Ok(());
}
if let Err(e) = plug_out_peer_request(&get_virtual_displays()) {
log::error!("Failed to plug out virtual displays: {}", e);
}
let _ = plug_out_headless();
Ok(())
}
pub fn plug_in_peer_request(
modes: Vec<Vec<virtual_display::MonitorMode>>,
) -> ResultType<Vec<u32>> {
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
manager.prepare_driver()?;
let mut indices: Vec<u32> = Vec::new();
for m in modes.iter() {
for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT {
if !manager.peer_index_name.contains_key(&idx) {
let device_names = get_device_names().into_iter().collect();
match VirtualDisplayManager::plug_in_monitor(idx, m) {
Ok(_) => {
let device_name = get_new_device_name(&device_names);
manager.peer_index_name.insert(idx, device_name);
indices.push(idx);
}
Err(e) => {
log::error!("Plug in monitor failed {}", e);
}
}
break;
}
}
}
Ok(indices)
}
pub fn plug_out_peer_request(indices: &[u32]) -> ResultType<()> {
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
for idx in indices.iter() {
if manager.peer_index_name.contains_key(idx) {
allow_err!(virtual_display::plug_out_monitor(*idx));
manager.peer_index_name.remove(idx);
}
}
Ok(())
}
pub fn is_virtual_display(name: &str) -> bool {
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
if let Some((_, device_name)) = &lock.headless_index_name {
if windows::is_device_name(device_name, name) {
return true;
}
}
for (_, v) in lock.peer_index_name.iter() {
if windows::is_device_name(v, name) {
return true;
}
}
false
}
fn change_resolution(index: u32, w: u32, h: u32) -> bool {
let modes = [virtual_display::MonitorMode {
width: w,
height: h,
sync: 60,
}];
match virtual_display::update_monitor_modes(index, &modes) {
Ok(_) => true,
Err(e) => {
log::error!("Update monitor {} modes {:?} failed: {}", index, &modes, e);
false
}
}
}
pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option<bool> {
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
if let Some((index, device_name)) = &lock.headless_index_name {
if windows::is_device_name(device_name, name) {
return Some(change_resolution(*index, w, h));
}
}
for (k, v) in lock.peer_index_name.iter() {
if windows::is_device_name(v, name) {
return Some(change_resolution(*k, w, h));
}
}
None
}
}
pub mod amyuni_idd {
use super::windows;
use crate::platform::win_device;
use hbb_common::{bail, lazy_static, log, tokio::time::Instant, ResultType};
use std::{
ops::Sub,
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 {
Data1: 0xb5ffd75f,
Data2: 0xda40,
Data3: 0x4353,
Data4: [0x8f, 0xf8, 0xb6, 0xda, 0xf6, 0xf1, 0xd8, 0xca],
};
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<Mutex<()>> = Default::default();
static ref LAST_PLUG_IN_HEADLESS_TIME: Arc<Mutex<Instant>> = Arc::new(Mutex::new(Instant::now().sub(Duration::from_secs(100))));
}
fn get_deviceinstaller64_work_dir() -> ResultType<Option<Vec<u8>>> {
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() {
return Ok(None);
}
let exe_path = work_dir.join(INSTALLER_EXE_FILE);
if !exe_path.exists() {
return Ok(None);
}
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);
Ok(Some(work_dir2))
}
pub fn uninstall_driver() -> ResultType<()> {
if let Ok(Some(work_dir)) = get_deviceinstaller64_work_dir() {
if crate::platform::windows::is_x64() {
log::info!("Uninstalling driver by deviceinstaller64.exe");
install_if_x86_on_x64(&work_dir, "remove usbmmidd")?;
return Ok(());
}
}
log::info!("Uninstalling driver by SetupAPI");
let mut reboot_required = false;
let _ = unsafe { win_device::uninstall_driver(HARDWARE_ID, &mut reboot_required)? };
Ok(())
}
// SetupDiCallClassInstaller() will always fail if current_exe() is built as x86 and running on x64.
// So we need to call another x64 version exe to install and uninstall the driver.
fn install_if_x86_on_x64(work_dir: &[u8], args: &str) -> ResultType<()> {
const SW_HIDE: i32 = 0;
let mut args = args.bytes().collect::<Vec<_>>();
args.push(0);
let mut exe_file = INSTALLER_EXE_FILE.bytes().collect::<Vec<_>>();
exe_file.push(0);
let hi = unsafe {
ShellExecuteA(
null_mut(),
"open\0".as_ptr() as _,
exe_file.as_ptr() as _,
args.as_ptr() as _,
work_dir.as_ptr() as _,
SW_HIDE,
) as i32
};
if hi <= 32 {
log::error!("Failed to run deviceinstaller: {}", hi);
bail!("Failed to run deviceinstaller.")
}
Ok(())
}
// If the driver is installed by "deviceinstaller64.exe", the driver will be installed asynchronously.
// The caller must wait some time before using the driver.
fn check_install_driver(is_async: &mut bool) -> ResultType<()> {
let _l = LOCK.lock().unwrap();
let drivers = windows::get_display_drivers();
if drivers
.iter()
.any(|(s, c)| s == super::AMYUNI_IDD_DEVICE_STRING && *c == 0)
{
*is_async = false;
return Ok(());
}
if let Ok(Some(work_dir)) = get_deviceinstaller64_work_dir() {
if crate::platform::windows::is_x64() {
log::info!("Installing driver by deviceinstaller64.exe");
install_if_x86_on_x64(&work_dir, "install usbmmidd.inf usbmmidd")?;
*is_async = true;
return Ok(());
}
}
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();
log::info!("Installing driver by SetupAPI");
let mut reboot_required = false;
let _ =
unsafe { win_device::install_driver(&inf_path, HARDWARE_ID, &mut reboot_required)? };
*is_async = false;
Ok(())
}
#[inline]
fn plug_monitor_(add: bool) -> Result<(), win_device::DeviceError> {
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(())
}
// `std::thread::sleep()` with a timeout is acceptable here.
// Because user can wait for a while to plug in a monitor.
fn plug_in_monitor_(add: bool, is_driver_async_installed: bool) -> ResultType<()> {
let timeout = Duration::from_secs(3);
let now = Instant::now();
loop {
match plug_monitor_(add) {
Ok(_) => {
break;
}
Err(e) => {
if is_driver_async_installed {
if let win_device::DeviceError::WinApiLastErr(_, e2) = &e {
if e2.raw_os_error() == Some(ERROR_NO_MORE_ITEMS as _) {
if now.elapsed() < timeout {
std::thread::sleep(Duration::from_millis(100));
continue;
}
}
}
}
return Err(e.into());
}
}
}
Ok(())
}
pub fn plug_in_headless() -> ResultType<()> {
let mut tm = LAST_PLUG_IN_HEADLESS_TIME.lock().unwrap();
if tm.elapsed() < Duration::from_secs(3) {
bail!("Plugging in too frequently.");
}
*tm = Instant::now();
drop(tm);
let mut is_async = false;
if let Err(e) = check_install_driver(&mut is_async) {
log::error!("Failed to install driver: {}", e);
bail!("Failed to install driver.");
}
plug_in_monitor_(true, is_async)
}
pub fn plug_in_monitor() -> ResultType<()> {
let mut is_async = false;
if let Err(e) = check_install_driver(&mut is_async) {
log::error!("Failed to install driver: {}", e);
bail!("Failed to install driver.");
}
if get_monitor_count() == 4 {
bail!("There are already 4 monitors plugged in.");
}
plug_in_monitor_(true, is_async)
}
pub fn plug_out_monitor(index: i32) -> ResultType<()> {
let all_count = windows::get_device_names(None).len();
let amyuni_count = get_monitor_count();
let mut to_plug_out_count = match all_count {
0 => return Ok(()),
1 => {
if amyuni_count == 0 {
bail!("No virtual displays to plug out.")
} else {
if super::is_can_plug_out_all() {
1
} else {
bail!("This only virtual display cannot be pulled out.")
}
}
}
_ => {
if all_count == amyuni_count {
if super::is_can_plug_out_all() {
all_count
} else {
all_count - 1
}
} else {
amyuni_count
}
}
};
if to_plug_out_count != 0 && index != -1 {
to_plug_out_count = 1;
}
for _i in 0..to_plug_out_count {
let _ = plug_monitor_(false);
}
Ok(())
}
#[inline]
pub fn get_monitor_count() -> usize {
windows::get_device_names(Some(super::AMYUNI_IDD_DEVICE_STRING)).len()
}
#[inline]
pub fn is_my_display(name: &str) -> bool {
windows::get_device_names(Some(super::AMYUNI_IDD_DEVICE_STRING))
.iter()
.any(|s| windows::is_device_name(s, name))
}
}
mod windows {
use std::ptr::null_mut;
use winapi::{
shared::{
devguid::GUID_DEVCLASS_DISPLAY,
minwindef::{DWORD, FALSE},
ntdef::ULONG,
},
um::{
cfgmgr32::{CM_Get_DevNode_Status, CR_SUCCESS},
cguid::GUID_NULL,
setupapi::{
SetupDiEnumDeviceInfo, SetupDiGetClassDevsW, SetupDiGetDeviceRegistryPropertyW,
SP_DEVINFO_DATA,
},
wingdi::{
DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_MIRRORING_DRIVER,
},
winnt::HANDLE,
winuser::{EnumDisplayDevicesW, EnumDisplaySettingsExW, ENUM_CURRENT_SETTINGS},
},
};
const DIGCF_PRESENT: DWORD = 0x00000002;
const SPDRP_DEVICEDESC: DWORD = 0x00000000;
const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
#[inline]
pub(super) fn is_device_name(device_name: &str, name: &str) -> bool {
if name.len() == device_name.len() {
name == device_name
} else if name.len() > device_name.len() {
false
} else {
&device_name[..name.len()] == name && device_name.as_bytes()[name.len() as usize] == 0
}
}
pub(super) fn get_device_names(device_string: Option<&str>) -> Vec<String> {
let mut device_names = Vec::new();
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as DWORD;
let mut i_dev_num = 0;
loop {
let result = unsafe { EnumDisplayDevicesW(null_mut(), i_dev_num, &mut dd, 0) };
if result == 0 {
break;
}
i_dev_num += 1;
if 0 == (dd.StateFlags & DISPLAY_DEVICE_ACTIVE)
|| (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0
{
continue;
}
let mut dm: DEVMODEW = unsafe { std::mem::zeroed() };
dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
dm.dmDriverExtra = 0;
let ok = unsafe {
EnumDisplaySettingsExW(
dd.DeviceName.as_ptr(),
ENUM_CURRENT_SETTINGS,
&mut dm as _,
0,
)
};
if ok == FALSE {
continue;
}
if dm.dmPelsHeight == 0 || dm.dmPelsWidth == 0 {
continue;
}
if let (Ok(device_name), Ok(ds)) = (
String::from_utf16(&dd.DeviceName),
String::from_utf16(&dd.DeviceString),
) {
if let Some(s) = device_string {
if ds.len() >= s.len() && &ds[..s.len()] == s {
device_names.push(device_name);
}
} else {
device_names.push(device_name);
}
}
}
device_names
}
pub(super) fn get_display_drivers() -> Vec<(String, u32)> {
let mut display_drivers: Vec<(String, u32)> = Vec::new();
let device_info_set = unsafe {
SetupDiGetClassDevsW(
&GUID_DEVCLASS_DISPLAY,
null_mut(),
null_mut(),
DIGCF_PRESENT,
)
};
if device_info_set == INVALID_HANDLE_VALUE {
println!(
"Failed to get device information set. Error: {}",
std::io::Error::last_os_error()
);
return display_drivers;
}
let mut device_info_data = SP_DEVINFO_DATA {
cbSize: std::mem::size_of::<SP_DEVINFO_DATA>() as u32,
ClassGuid: GUID_NULL,
DevInst: 0,
Reserved: 0,
};
let mut device_index = 0;
loop {
let result = unsafe {
SetupDiEnumDeviceInfo(device_info_set, device_index, &mut device_info_data)
};
if result == 0 {
break;
}
let mut data_type: DWORD = 0;
let mut required_size: DWORD = 0;
// Get the required buffer size for the driver description
let mut buffer;
unsafe {
SetupDiGetDeviceRegistryPropertyW(
device_info_set,
&mut device_info_data,
SPDRP_DEVICEDESC,
&mut data_type,
null_mut(),
0,
&mut required_size,
);
buffer = vec![0; required_size as usize / 2];
SetupDiGetDeviceRegistryPropertyW(
device_info_set,
&mut device_info_data,
SPDRP_DEVICEDESC,
&mut data_type,
buffer.as_mut_ptr() as *mut u8,
required_size,
null_mut(),
);
}
let Ok(driver_description) = String::from_utf16(&buffer) else {
println!("Failed to convert driver description to string");
device_index += 1;
continue;
};
let mut status: ULONG = 0;
let mut problem_number: ULONG = 0;
// Get the device status and problem number
let config_ret = unsafe {
CM_Get_DevNode_Status(
&mut status,
&mut problem_number,
device_info_data.DevInst,
0,
)
};
if config_ret != CR_SUCCESS {
println!(
"Failed to get device status. Error: {}",
std::io::Error::last_os_error()
);
device_index += 1;
continue;
}
display_drivers.push((driver_description, problem_number));
device_index += 1;
}
display_drivers
}
}