1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
use windows::Win32::System::Com::{CoCreateInstance, CLSCTX_ALL};
use windows::core::{GUID, PCWSTR} ;
use windows::Win32::Devices::PortableDevices::{PortableDeviceFTM, IPortableDevice};
use widestring::U16CString;
use crate::device::device_values::AppIdentifiers;
pub mod device_values;
mod content;
pub use content::Content;
/// Basic info about an MTP device
///
/// To access its content, you must call [`BasicDevice::open`]
#[derive(Clone)]
pub struct BasicDevice {
device_id: U16CString,
friendly_name: String,
}
impl BasicDevice {
pub(crate) fn new(device_id: U16CString, friendly_name: String) -> Self {
Self{ device_id, friendly_name }
}
pub fn device_id(&self) -> String {
self.device_id.to_string_lossy() // We trust Windows for not providing invalid UTF-16 characters
}
pub fn friendly_name(&self) -> &str {
&self.friendly_name
}
/// Turns this device into an "opened" device, that has more features
/// (e.g. being able to browse its content)
///
/// Some devices (e.g. ones that are backed by a FAT filesystem) use case-insensitive paths. In this case, you want to set `case_sensitive` to false.
/// Otherwise, you would often get `Err`s, e.g. when you try to create or replace a file (or folder) with a similar name but different casing.<br/>
/// Unfortunately, the Windows API does not look to be able to give this info.
pub fn open(&self, app_identifiers: &AppIdentifiers, case_sensitive_fs: bool) -> crate::WindowsResult<Device> {
// Fill out information about your application, so the device knows
// who they are speaking to.
let device_values = device_values::make_values_for_open_device(app_identifiers)?;
let com_device: IPortableDevice = unsafe {
CoCreateInstance(
&PortableDeviceFTM as *const GUID,
None,
CLSCTX_ALL
)
}?;
unsafe { com_device.Open(PCWSTR::from_raw(self.device_id.as_ptr()), &device_values) }.unwrap();
Ok(Device{
com_device,
case_sensitive_fs,
})
}
}
/// An MTP device that as been opened
pub struct Device {
com_device: IPortableDevice,
case_sensitive_fs: bool,
}
impl Device {
/// Returns the underlying COM object
///
/// This is useful in case you want to call a function that has no Rust wrapper (yet?) in this crate
pub fn raw_device(&self) -> &IPortableDevice {
&self.com_device
}
pub fn content(&self) -> crate::WindowsResult<Content> {
let com_content = unsafe { self.com_device.Content() }?;
Ok(Content::new(com_content, self.case_sensitive_fs))
}
}