use std::ffi::OsStr;
use std::time::SystemTime;
use windows::core::{GUID, PCWSTR};
use windows::Win32::Foundation::E_FAIL;
use windows::Win32::System::Com::{CoCreateInstance, CLSCTX_ALL, CoTaskMemFree};
use windows::Win32::System::Variant::VT_DATE;
use windows::Win32::Storage::FileSystem::SECURITY_IMPERSONATION;
use windows::Win32::Devices::PortableDevices::{
PortableDeviceValues, IPortableDeviceValues,
WPD_CLIENT_NAME,
WPD_CLIENT_MAJOR_VERSION,
WPD_CLIENT_MINOR_VERSION,
WPD_CLIENT_REVISION,
WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE,
WPD_OBJECT_NAME,
WPD_OBJECT_PARENT_ID,
WPD_OBJECT_CONTENT_TYPE,
WPD_CONTENT_TYPE_FOLDER,
WPD_OBJECT_SIZE,
WPD_OBJECT_ORIGINAL_FILE_NAME,
};
use widestring::{U16CStr, U16CString};
#[derive(Debug, Clone)]
pub struct AppIdentifiers {
pub app_name: String,
pub app_major: u32,
pub app_minor: u32,
pub app_patch: u32,
}
#[macro_export]
macro_rules! make_current_app_identifiers {
() => {
$crate::device::device_values::AppIdentifiers {
app_name: env!("CARGO_PKG_NAME").to_string(),
app_major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0),
app_minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0),
app_patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0),
}
};
}
pub(crate) fn make_values_for_open_device(current_app_identifiers: &AppIdentifiers) -> crate::WindowsResult<IPortableDeviceValues> {
let device_values: IPortableDeviceValues = unsafe {
CoCreateInstance(
&PortableDeviceValues as *const GUID,
None,
CLSCTX_ALL
)
}?;
let current_app_name_wide = U16CString::from_str_truncate(¤t_app_identifiers.app_name);
let pcwstr_current_app_name = PCWSTR::from_raw(current_app_name_wide.as_ptr());
unsafe{ device_values.SetStringValue(&WPD_CLIENT_NAME as *const _, pcwstr_current_app_name) }?;
unsafe{ device_values.SetUnsignedIntegerValue(&WPD_CLIENT_MAJOR_VERSION as *const _, current_app_identifiers.app_major) }?;
unsafe{ device_values.SetUnsignedIntegerValue(&WPD_CLIENT_MINOR_VERSION as *const _, current_app_identifiers.app_minor) }?;
unsafe{ device_values.SetUnsignedIntegerValue(&WPD_CLIENT_REVISION as *const _, current_app_identifiers.app_patch) }?;
unsafe{ device_values.SetUnsignedIntegerValue(&WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE as *const _, SECURITY_IMPERSONATION.0) }?;
Ok(device_values)
}
pub(crate) fn make_values_for_create_folder(parent_id: &U16CStr, folder_name: &OsStr) -> crate::WindowsResult<IPortableDeviceValues> {
let device_values: IPortableDeviceValues = unsafe {
CoCreateInstance(
&PortableDeviceValues as *const GUID,
None,
CLSCTX_ALL
)
}?;
let folder_name_wide = U16CString::from_os_str_truncate(folder_name);
let pcwstr_folder_name = PCWSTR::from_raw(folder_name_wide.as_ptr());
unsafe{ device_values.SetStringValue(&WPD_OBJECT_PARENT_ID as *const _, PCWSTR::from_raw(parent_id.as_ptr())) }?;
unsafe{ device_values.SetStringValue(&WPD_OBJECT_NAME as *const _, pcwstr_folder_name) }?;
unsafe{ device_values.SetStringValue(&WPD_OBJECT_ORIGINAL_FILE_NAME as *const _, pcwstr_folder_name) }?;
unsafe{ device_values.SetGuidValue(&WPD_OBJECT_CONTENT_TYPE as *const _, &WPD_CONTENT_TYPE_FOLDER as *const _) }?;
Ok(device_values)
}
pub(crate) fn make_values_for_create_file(parent_id: &U16CStr, file_name: &OsStr, file_size: u64) -> crate::WindowsResult<IPortableDeviceValues> {
let device_values: IPortableDeviceValues = unsafe {
CoCreateInstance(
&PortableDeviceValues as *const GUID,
None,
CLSCTX_ALL
)
}?;
let file_name_wide = U16CString::from_os_str_truncate(file_name);
let pcwstr_file_name = PCWSTR::from_raw(file_name_wide.as_ptr());
unsafe{ device_values.SetStringValue(&WPD_OBJECT_PARENT_ID as *const _, PCWSTR::from_raw(parent_id.as_ptr())) }?;
unsafe{ device_values.SetUnsignedLargeIntegerValue(&WPD_OBJECT_SIZE as *const _, file_size) }?;
unsafe{ device_values.SetStringValue(&WPD_OBJECT_ORIGINAL_FILE_NAME as *const _, PCWSTR::from_raw(pcwstr_file_name.as_ptr())) }?;
Ok(device_values)
}
pub struct DeviceValues(IPortableDeviceValues);
impl DeviceValues {
pub fn new(values: IPortableDeviceValues) -> Self {
Self(values)
}
pub fn get_string(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<U16CString> {
let pwstr = unsafe{ self.0.GetStringValue(key as *const _) }?;
let result = U16CString::from_vec_truncate(unsafe{ pwstr.as_wide() });
unsafe{ CoTaskMemFree(Some(pwstr.as_ptr() as *const _)) };
Ok(result)
}
pub fn get_u32(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<u32> {
unsafe{ self.0.GetUnsignedIntegerValue(key as *const _) }
}
pub fn get_i32(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<i32> {
unsafe{ self.0.GetSignedIntegerValue(key as *const _) }
}
pub fn get_u64(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<u64> {
unsafe{ self.0.GetUnsignedLargeIntegerValue(key as *const _) }
}
pub fn get_f32(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<f32> {
unsafe{ self.0.GetFloatValue(key as *const _) }
}
pub fn get_guid(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<GUID> {
unsafe{ self.0.GetGuidValue(key as *const _) }
}
pub fn get_bool(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<bool> {
unsafe{ self.0.GetBoolValue(key as *const _) }.map(|b| b.as_bool())
}
pub fn get_date(&self, key: &crate::PROPERTYKEY) -> crate::WindowsResult<SystemTime> {
const SECONDS_PER_DAY: f64 = 86_400.0;
const DAYS_BETWEEN_1899_AND_1970: f64 = 25_569.0;
let prop_variant = unsafe{ self.0.GetValue(key as *const _) }?;
let inner = &unsafe { prop_variant.Anonymous.Anonymous };
if inner.vt != VT_DATE {
return Err(crate::WindowsError::from(E_FAIL));
}
let vt_date = unsafe { inner.Anonymous.date };
let days_since_unix_epoch = vt_date - DAYS_BETWEEN_1899_AND_1970;
let seconds_since_unix_epoch = days_since_unix_epoch * SECONDS_PER_DAY;
Ok(std::time::UNIX_EPOCH + std::time::Duration::from_secs_f64(seconds_since_unix_epoch))
}
}