use crate::utils::WMIResult;
use crate::WMIError;
use log::debug;
use std::marker::PhantomData;
use windows::core::BSTR;
use windows::Win32::Foundation::RPC_E_TOO_LATE;
use windows::Win32::System::Com::{
CoCreateInstance, CoSetProxyBlanket, CLSCTX_INPROC_SERVER, RPC_C_AUTHN_LEVEL_CALL,
};
use windows::Win32::System::Com::{
CoInitializeEx, CoInitializeSecurity, COINIT_MULTITHREADED, EOAC_NONE,
RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
};
use windows::Win32::System::Rpc::{RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE};
use windows::Win32::System::Wmi::{
IWbemLocator, IWbemServices, WbemLocator, WBEM_FLAG_CONNECT_USE_MAX_WAIT,
};
#[derive(Clone, Copy, Debug)]
pub struct COMLibrary {
_phantom: PhantomData<*mut ()>,
}
impl COMLibrary {
pub fn new() -> WMIResult<Self> {
let instance = Self::without_security()?;
match instance.init_security() {
Ok(()) => {}
Err(WMIError::HResultError { hres }) if hres == RPC_E_TOO_LATE.0 => {}
Err(err) => return Err(err),
}
Ok(instance)
}
pub fn without_security() -> WMIResult<Self> {
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED)? }
let instance = Self {
_phantom: PhantomData,
};
Ok(instance)
}
pub unsafe fn assume_initialized() -> Self {
Self {
_phantom: PhantomData,
}
}
fn init_security(&self) -> WMIResult<()> {
unsafe {
CoInitializeSecurity(
None,
-1, None,
None,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
None,
EOAC_NONE,
None,
)?;
};
Ok(())
}
}
fn _test_com_lib_not_send(_s: impl Send) {}
#[derive(Clone, Debug)]
pub struct WMIConnection {
_com_con: COMLibrary,
pub svc: IWbemServices,
}
impl WMIConnection {
pub fn new(com_lib: COMLibrary) -> WMIResult<Self> {
Self::with_namespace_path("ROOT\\CIMV2", com_lib)
}
pub fn with_namespace_path(namespace_path: &str, com_lib: COMLibrary) -> WMIResult<Self> {
let loc = create_locator()?;
let svc = create_services(&loc, namespace_path)?;
let this = Self {
_com_con: com_lib,
svc,
};
this.set_proxy()?;
Ok(this)
}
fn set_proxy(&self) -> WMIResult<()> {
debug!("Calling CoSetProxyBlanket");
unsafe {
CoSetProxyBlanket(
&self.svc,
RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, None,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, None, EOAC_NONE, )?;
}
Ok(())
}
}
fn create_locator() -> WMIResult<IWbemLocator> {
debug!("Calling CoCreateInstance for CLSID_WbemLocator");
let loc = unsafe { CoCreateInstance(&WbemLocator, None, CLSCTX_INPROC_SERVER)? };
debug!("Got locator {:?}", loc);
Ok(loc)
}
fn create_services(loc: &IWbemLocator, path: &str) -> WMIResult<IWbemServices> {
debug!("Calling ConnectServer");
let object_path_bstr = BSTR::from(path);
let svc = unsafe {
loc.ConnectServer(
&object_path_bstr,
&BSTR::new(),
&BSTR::new(),
&BSTR::new(),
WBEM_FLAG_CONNECT_USE_MAX_WAIT.0,
&BSTR::new(),
None,
)?
};
debug!("Got service {:?}", svc);
Ok(svc)
}
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_can_create_multiple_connections() {
{
let com_lib = COMLibrary::new().unwrap();
let _ = WMIConnection::new(com_lib);
}
{
let com_lib = COMLibrary::new().unwrap();
let _ = WMIConnection::new(com_lib);
}
}
}