C#:如何在Windows文件资源管理器活动窗口中获取选中文件路径?
问题描述
我正在为集成于Windows文件资源管理器的企业产品数据管理工具「Vault」开发插件,已成功创建仅在Vault内生效的自定义右键菜单命令「SW Quick View」,但目前无法实现右键选中文件后直接获取活动资源管理器窗口中选中文件的路径并执行命令逻辑。
当前实现流程繁琐:用户需在资源管理器任意位置右键选择该命令,通过OpenFileDialog弹窗选择文件后才能执行逻辑。
期望实现:用户选中文件后右键点击「SW Quick View」,直接获取所有选中文件的路径并执行预设逻辑,无需弹窗或额外操作。
现有代码如下:
public static void OpenWithQuickViewer() { string[] fileNames; OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect = true; if (ofd.ShowDialog() == DialogResult.OK) { fileNames = ofd.FileNames; foreach (string filePath in fileNames) { // 已验证可用的“SW Quick View”逻辑 } } }
解决方案
方法1:利用Vault API获取选中文件(推荐)
优先使用Vault官方API对接其选择系统,这是最稳定的实现方式。以下示例基于Autodesk Vault的.NET API:
using Autodesk.Connectivity.WebServices; using Autodesk.DataManagement.Client.Framework.Vault.Currency.Connections; using Autodesk.DataManagement.Client.Framework.Vault.Selection; using System.Linq; public static void OpenWithQuickViewer() { // 获取当前Vault连接实例 var connection = VaultApplication.Instance.Connection; if (connection == null) return; // 获取选中的文件对象 var selectionService = VaultApplication.Instance.SelectionService; var selectedFiles = selectionService.GetSelectedItems<File>(); if (selectedFiles.Any()) { foreach (var file in selectedFiles) { // 获取文件本地路径(根据逻辑需求也可使用服务器路径:file.FullName) string localFilePath = connection.FileManager.GetLocalPath(file); // 执行已验证的“SW Quick View”逻辑 } } }
注意:需确保项目引用对应Vault版本的程序集,如Autodesk.DataManagement.Client.Framework.dll、Autodesk.Connectivity.WebServices.dll等。
方法2:通过Windows Shell API获取资源管理器选中文件
若Vault API无法满足需求,可直接通过Windows Shell COM接口获取当前活动资源管理器的选中文件:
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Windows.Forms; public static class ExplorerSelectionHelper { [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); [DllImport("ole32.dll")] private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot); [DllImport("ole32.dll")] private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc); public static List<string> GetSelectedFilesFromExplorer() { var selectedFiles = new List<string>(); var hwnd = GetForegroundWindow(); if (hwnd == IntPtr.Zero) return selectedFiles; GetWindowThreadProcessId(hwnd, out var processId); if (processId == 0) return selectedFiles; GetRunningObjectTable(0, out var rot); rot.EnumRunning(out var monikerEnumerator); monikerEnumerator.Reset(); var monikers = new IMoniker[1]; while (monikerEnumerator.Next(1, monikers, IntPtr.Zero) == 0) { CreateBindCtx(0, out var bindCtx); monikers[0].GetDisplayName(bindCtx, null, out var displayName); if (displayName.Contains("Explorer") && displayName.Contains(processId.ToString())) { monikers[0].BindToObject(bindCtx, null, typeof(IShellBrowser).GUID, out var obj); var shellBrowser = (IShellBrowser)obj; shellBrowser.QueryActiveShellView(out var shellView); shellView.GetItemObject((int)SVGIO.SVGIO_SELECTION, typeof(IDataObject).GUID, out var dataObj); var dataObject = (IDataObject)dataObj; var filePaths = (string[])dataObject.GetData(DataFormats.FileDrop); if (filePaths != null) selectedFiles.AddRange(filePaths); break; } } return selectedFiles; } // 所需COM接口定义 [ComImport, Guid("0000010c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IPersist { [PreserveSig] int GetClassID(out Guid pClassID); } [ComImport, Guid("0000010e-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IRunningObjectTable { [PreserveSig] int EnumRunning(out IEnumMoniker ppenumMoniker); [PreserveSig] int GetObject(IMoniker pmkObjectName, out object ppunkObject); [PreserveSig] int Register(int grfFlags, object punkObject, IMoniker pmkObjectName); [PreserveSig] int Revoke(int dwRegister); [PreserveSig] int IsRunning(IMoniker pmkObjectName); } [ComImport, Guid("0000010f-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IEnumMoniker { [PreserveSig] int Next(int celt, IMoniker[] rgelt, IntPtr pceltFetched); [PreserveSig] int Skip(int celt); [PreserveSig] int Reset(); [PreserveSig] int Clone(out IEnumMoniker ppenum); } [ComImport, Guid("0000000e-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IBindCtx { [PreserveSig] int RegisterObjectBound(object punkObject); [PreserveSig] int RevokeObjectBound(object punkObject); [PreserveSig] int ReleaseBoundObjects(); [PreserveSig] int SetBindOptions(ref BIND_OPTS pbindopts); [PreserveSig] int GetBindOptions(ref BIND_OPTS pbindopts); [PreserveSig] int GetRunningObjectTable(out IRunningObjectTable prot); [PreserveSig] int RegisterObjectParam(string pszKey, object punkObject); [PreserveSig] int GetObjectParam(string pszKey, out object ppunkObject); [PreserveSig] int RevokeObjectParam(string pszKey); [PreserveSig] int EnumObjectParam(out IEnumString ppenum); [PreserveSig] int GetDisplayName(IMoniker pmk, int dwDisplayOpts, out string ppszDisplayName); [PreserveSig] int GetMonikerDispID(string pszDisplayName, out int pdwDispID); } [StructLayout(LayoutKind.Sequential)] private struct BIND_OPTS { public int cbStruct; public int grfFlags; public int grfMode; public int dwTickCountDeadline; } [ComImport, Guid("000214E2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellBrowser { [PreserveSig] int QueryActiveShellView(out IShellView ppshv); [PreserveSig] int BrowseObject(IntPtr pidl, uint wFlags); } [ComImport, Guid("000214E3-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IShellView { [PreserveSig] int GetItemObject(int uItem, Guid riid, out object ppv); } private enum SVGIO { SVGIO_SELECTION = 0x00000001 } [ComImport, Guid("00000101-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IEnumString { [PreserveSig] int Next(int celt, string[] rgelt, IntPtr pceltFetched); [PreserveSig] int Skip(int celt); [PreserveSig] int Reset(); [PreserveSig] int Clone(out IEnumString ppenum); } } // 修改后的执行方法 public static void OpenWithQuickViewer() { var selectedFiles = ExplorerSelectionHelper.GetSelectedFilesFromExplorer(); foreach (var filePath in selectedFiles) { // 执行已验证的“SW Quick View”逻辑 } }
注意事项
- 方法1为官方推荐方案,与Vault选择系统深度集成,不受窗口切换等场景影响;
- 方法2需处理COM对象的释放逻辑,避免内存泄漏,且需兼容不同Windows版本;
- 确保右键菜单绑定到文件对象而非空白区域,保证触发时存在选中文件。
内容的提问来源于stack exchange,提问作者Kraz
相关产品推荐
相关产品推荐

