如何通过Rust获取Windows活动资源管理器中选中文件的路径?
获取活动Windows资源管理器窗口中选中文件的路径(Rust实现)
原代码问题说明
原代码中GetWindowModuleFileNameA无法正确获取资源管理器的进程路径——该函数仅能获取调用进程自身或其拥有窗口的模块路径,而资源管理器的窗口结构特殊,不属于调用进程,因此返回空或错误路径。
解决方案实现
要实现需求,需要分两步:验证当前前台窗口是资源管理器,再通过COM接口IShellFolderViewDual2获取选中文件路径。
1. Cargo依赖配置
确保Cargo.toml中包含所需的windows-rs功能:
[dependencies] windows = { version = "0.52", features = [ "Win32_Foundation", "Win32_UI_WindowsAndMessaging", "Win32_System_Threading", "Win32_System_ProcessStatus", "Win32_Shell", "Win32_System_Com", "Win32_System_Ole", ] }
2. 完整代码实现
use std::{thread, time}; use windows::{ core::{Interface, Result, GUID, HSTRING}, Win32::{ Foundation::{CloseHandle, BOOL, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM}, System::{ Com::{CoInitializeEx, COINIT_APARTMENTTHREADED}, Ole::{IDispatch, Variant}, ProcessStatus::GetModuleFileNameExA, Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ}, }, UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowThreadProcessId, SendMessageA, WM_GETOBJECT}, }, }; // IShellFolderViewDual2的GUID const IID_IShellFolderViewDual2: GUID = GUID::from_u128(0x00020430_0000_0000_c000_000000000046); fn get_process_path(hwnd: HWND) -> Option<String> { let mut pid = 0; unsafe { GetWindowThreadProcessId(hwnd, &mut pid) }; if pid == 0 { return None; } let process_handle = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, BOOL(0), pid) }; if process_handle.is_invalid() { return None; } let mut buffer = [0u8; 260]; let len = unsafe { GetModuleFileNameExA(process_handle, HINSTANCE(0), &mut buffer) }; unsafe { CloseHandle(process_handle) }; if len == 0 { None } else { String::from_utf8_lossy(&buffer[..len as usize]).into_owned() } } fn get_selected_items(hwnd: HWND) -> Result<Vec<String>> { // 初始化COM环境 unsafe { CoInitializeEx(std::ptr::null_mut(), COINIT_APARTMENTTHREADED)? }; // 通过WM_GETOBJECT消息获取IShellFolderViewDual2接口 let result = unsafe { SendMessageA(hwnd, WM_GETOBJECT, WPARAM(0), LPARAM(IID_IShellFolderViewDual2.as_ptr() as isize)) }; if result == LRESULT(0) { return Err(windows::core::Error::from_win32()); } // 将返回值转换为IDispatch,再查询目标接口 let dispatch: IDispatch = unsafe { Interface::from_raw(result as *mut _) }; let shell_folder_view: windows::Win32::Shell::IShellFolderViewDual2 = dispatch.cast()?; // 获取选中项集合 let items = shell_folder_view.SelectedItems()?; let count = items.Count()?; let mut paths = Vec::new(); for i in 1..=count { let item = items.Item(&Variant::I4(i as i32))?; let folder_item: windows::Win32::Shell::IShellFolderItem = item.cast()?; let path = folder_item.Path()?; paths.push(path.to_string()); } Ok(paths) } fn main() -> Result<()> { // 等待用户切换到目标资源管理器窗口 thread::sleep(time::Duration::from_millis(2000)); let hwnd = unsafe { GetForegroundWindow() }; if hwnd.is_invalid() { println!("无法获取前台窗口"); return Ok(()); } // 验证是否为资源管理器进程 match get_process_path(hwnd) { Some(path) if path.to_lowercase().ends_with("explorer.exe") => { match get_selected_items(hwnd) { Ok(paths) => { println!("选中文件路径:"); for path in paths { println!("{}", path); } } Err(e) => println!("获取选中文件失败:{}", e), } } _ => println!("当前前台窗口不是资源管理器"), } Ok(()) }
关键步骤说明
- 进程路径验证:通过
GetWindowThreadProcessId获取窗口所属进程ID,再用OpenProcess和GetModuleFileNameExA获取进程路径,确保目标窗口属于资源管理器。 - COM接口调用:
- 调用
CoInitializeEx初始化COM环境(需单线程公寓模式)。 - 通过
WM_GETOBJECT消息向资源管理器窗口请求IShellFolderViewDual2接口。 - 从返回的
IDispatch转换到IShellFolderViewDual2,访问其SelectedItems属性获取选中项集合。 - 遍历集合,通过
IShellFolderItem的Path方法获取每个文件的完整路径。
- 调用
内容的提问来源于stack exchange,提问作者Wolffe2
相关产品推荐
相关产品推荐

