C#:如何使用SHOpenFolderAndSelectItems

有人可以举例说明如何使用来自C#的shell函数SHOpenFolderAndSelectItems吗? 我不太懂得如何使用这些函数而无法在pinvoke.net上找到它… = /

说我有三个文件叫

  • X:\Pictures\a.jpg
  • X:\Pictures\s.jpg
  • X:\Pictures\d.jpg

然后我想打开X:\Pictures文件夹,选择a.jpgs.jpgd.jpg

你似乎已经问过两次相同的问题(另一个是C#:如何打开Windows资源管理器窗口,选择了一些没有答案的文件 )我将解决方案发布到这两个问题我不知道是否应该制作一个社区维基。

在一位同事遇到问题后寻找答案我没有找到,所以我写了一个小class来做这件事。 代码在Gist上 ,我将在本文末尾粘贴当前版本。

使用示例文件,语法将为:

  ShowSelectedInExplorer.FilesOrFolders( @"X:\Pictures\a.jpg", @"X:\Pictures\s.jpg", @"X:\Pictures\d.jpg" ); 

与低级API相比,我的代码存在一些限制,主要是:

  • 未在桌面上进行选择
  • 父目录必须是目录或驱动器,因此您无法在“我的电脑”文件夹中选择多个驱动器。

无论如何,这里是ShowSelectedInExplorer类的源代码:

 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; static class ShowSelectedInExplorer { [Flags] internal enum SHCONT : ushort { SHCONTF_CHECKING_FOR_CHILDREN = 0x0010, SHCONTF_FOLDERS = 0x0020, SHCONTF_NONFOLDERS = 0x0040, SHCONTF_INCLUDEHIDDEN = 0x0080, SHCONTF_INIT_ON_FIRST_NEXT = 0x0100, SHCONTF_NETPRINTERSRCH = 0x0200, SHCONTF_SHAREABLE = 0x0400, SHCONTF_STORAGE = 0x0800, SHCONTF_NAVIGATION_ENUM = 0x1000, SHCONTF_FASTITEMS = 0x2000, SHCONTF_FLATLIST = 0x4000, SHCONTF_ENABLE_ASYNC = 0x8000 } [ComImport, Guid("000214E6-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss] internal interface IShellFolder { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void ParseDisplayName(IntPtr hwnd, [In, MarshalAs(UnmanagedType.Interface)] IBindCtx pbc, [In, MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, [Out] out uint pchEaten, [Out] out IntPtr ppidl, [In, Out] ref uint pdwAttributes); [PreserveSig] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int EnumObjects([In] IntPtr hwnd, [In] SHCONT grfFlags, [MarshalAs(UnmanagedType.Interface)] out IEnumIDList ppenumIDList); [PreserveSig] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int BindToObject([In] IntPtr pidl, [In, MarshalAs(UnmanagedType.Interface)] IBindCtx pbc, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out IShellFolder ppv); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void BindToStorage([In] ref IntPtr pidl, [In, MarshalAs(UnmanagedType.Interface)] IBindCtx pbc, [In] ref Guid riid, out IntPtr ppv); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void CompareIDs([In] IntPtr lParam, [In] ref IntPtr pidl1, [In] ref IntPtr pidl2); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void CreateViewObject([In] IntPtr hwndOwner, [In] ref Guid riid, out IntPtr ppv); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetAttributesOf([In] uint cidl, [In] IntPtr apidl, [In, Out] ref uint rgfInOut); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetUIObjectOf([In] IntPtr hwndOwner, [In] uint cidl, [In] IntPtr apidl, [In] ref Guid riid, [In, Out] ref uint rgfReserved, out IntPtr ppv); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetDisplayNameOf([In] ref IntPtr pidl, [In] uint uFlags, out IntPtr pName); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetNameOf([In] IntPtr hwnd, [In] ref IntPtr pidl, [In, MarshalAs(UnmanagedType.LPWStr)] string pszName, [In] uint uFlags, [Out] IntPtr ppidlOut); } [ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IEnumIDList { [PreserveSig] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int Next(uint celt, IntPtr rgelt, out uint pceltFetched); [PreserveSig] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int Skip([In] uint celt); [PreserveSig] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int Reset(); [PreserveSig] [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int Clone([MarshalAs(UnmanagedType.Interface)] out IEnumIDList ppenum); } class NativeMethods { static readonly int pointerSize = Marshal.SizeOf(typeof(IntPtr)); [DllImport("ole32.dll", EntryPoint = "CreateBindCtx")] public static extern int CreateBindCtx_(int reserved, out IBindCtx ppbc); public static IBindCtx CreateBindCtx() { IBindCtx result; Marshal.ThrowExceptionForHR(CreateBindCtx_(0, out result)); return result; } [DllImport("shell32.dll", EntryPoint = "SHGetDesktopFolder", CharSet = CharSet.Unicode, SetLastError = true)] static extern int SHGetDesktopFolder_([MarshalAs(UnmanagedType.Interface)] out IShellFolder ppshf); public static IShellFolder SHGetDesktopFolder() { IShellFolder result; Marshal.ThrowExceptionForHR(SHGetDesktopFolder_(out result)); return result; } [DllImport("shell32.dll", EntryPoint = "SHOpenFolderAndSelectItems")] static extern int SHOpenFolderAndSelectItems_( [In] IntPtr pidlFolder, uint cidl, [In, Optional, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, int dwFlags ); public static void SHOpenFolderAndSelectItems(IntPtr pidlFolder, IntPtr[] apidl, int dwFlags) { var cidl = (apidl != null) ? (uint)apidl.Length : 0U; var result = NativeMethods.SHOpenFolderAndSelectItems_(pidlFolder, cidl, apidl, dwFlags); Marshal.ThrowExceptionForHR(result); } [DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ILCreateFromPath([In, MarshalAs(UnmanagedType.LPWStr)] string pszPath); [DllImport("shell32.dll")] public static extern void ILFree([In] IntPtr pidl); } static IntPtr GetShellFolderChildrenRelativePIDL(IShellFolder parentFolder, string displayName) { var bindCtx = NativeMethods.CreateBindCtx(); uint pchEaten; uint pdwAttributes = 0; IntPtr ppidl; parentFolder.ParseDisplayName(IntPtr.Zero, null, displayName, out pchEaten, out ppidl, ref pdwAttributes); return ppidl; } static IntPtr PathToAbsolutePIDL(string path) { var desktopFolder = NativeMethods.SHGetDesktopFolder(); return GetShellFolderChildrenRelativePIDL(desktopFolder, path); } static Guid IID_IShellFolder = typeof(IShellFolder).GUID; static int pointerSize = Marshal.SizeOf(typeof(IntPtr)); static IShellFolder PIDLToShellFolder(IShellFolder parent, IntPtr pidl) { IShellFolder folder; var result = parent.BindToObject(pidl, null, ref IID_IShellFolder, out folder); Marshal.ThrowExceptionForHR((int)result); return folder; } static IShellFolder PIDLToShellFolder(IntPtr pidl) { return PIDLToShellFolder(NativeMethods.SHGetDesktopFolder(), pidl); } static void SHOpenFolderAndSelectItems(IntPtr pidlFolder, IntPtr[] apidl, bool edit) { NativeMethods.SHOpenFolderAndSelectItems(pidlFolder, apidl, edit ? 1 : 0); } public static void FileOrFolder(string path, bool edit = false) { if (path == null) throw new ArgumentNullException("path"); var pidl = PathToAbsolutePIDL(path); try { SHOpenFolderAndSelectItems(pidl, null, edit); } finally { NativeMethods.ILFree(pidl); } } static IEnumerable PathToFileSystemInfo(IEnumerable paths) { foreach (var path in paths) { string fixedPath = path; if (fixedPath.EndsWith(Path.DirectorySeparatorChar.ToString()) || fixedPath.EndsWith(Path.AltDirectorySeparatorChar.ToString())) { fixedPath = fixedPath.Remove(fixedPath.Length - 1); } if (Directory.Exists(fixedPath)) yield return new DirectoryInfo(fixedPath); else if (File.Exists(fixedPath)) yield return new FileInfo(fixedPath); else { throw new FileNotFoundException("The specified file or folder doesn't exists : " + fixedPath, fixedPath); } } } public static void FilesOrFolders(string parentDirectory, ICollection filenames) { if (filenames == null) throw new ArgumentNullException("filenames"); if (filenames.Count == 0) return; var parentPidl = PathToAbsolutePIDL(parentDirectory); try { var parent = PIDLToShellFolder(parentPidl); List filesPidl = new List(filenames.Count); foreach (var filename in filenames) { filesPidl.Add(GetShellFolderChildrenRelativePIDL(parent, filename)); } try { SHOpenFolderAndSelectItems(parentPidl, filesPidl.ToArray(), false); } finally { foreach (var pidl in filesPidl) { NativeMethods.ILFree(pidl); } } } finally { NativeMethods.ILFree(parentPidl); } } public static void FilesOrFolders(params string[] paths) { FilesOrFolders((IEnumerable)paths); } public static void FilesOrFolders(IEnumerable paths) { FilesOrFolders(PathToFileSystemInfo(paths)); } public static void FilesOrFolders(IEnumerable paths) { if (paths == null) throw new ArgumentNullException("paths"); if (paths.Count() == 0) return; var explorerWindows = paths.GroupBy(p => Path.GetDirectoryName(p.FullName)); foreach (var explorerWindowPaths in explorerWindows) { var parentDirectory = Path.GetDirectoryName(explorerWindowPaths.First().FullName); FilesOrFolders(parentDirectory, explorerWindowPaths.Select(fsi => fsi.Name).ToList()); } } } 

不是100%的答案,但此片段显示了如何从C#中选择资源管理器中的单个项目。

  private void SelectInFileExplorer(string fullPath) { if (string.IsNullOrEmpty(fullPath)) throw new ArgumentNullException("fullPath"); fullPath = Path.GetFullPath(fullPath); IntPtr pidlList = NativeMethods.ILCreateFromPathW(fullPath); if (pidlList != IntPtr.Zero) try { // Open parent folder and select item Marshal.ThrowExceptionForHR(NativeMethods.SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0)); } finally { NativeMethods.ILFree(pidlList); } } static class NativeMethods { [DllImport("shell32.dll", ExactSpelling=true)] public static extern void ILFree(IntPtr pidlList); [DllImport("shell32.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] public static extern IntPtr ILCreateFromPathW(string pszPath); [DllImport("shell32.dll", ExactSpelling=true)] public static extern int SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags); } 

查看http://www.cnblogs.com/qiuyi21/archive/2009/06/24/1510592.html 。 该示例使用IShellLink在启动SHOpenFolderAndSelectItems之前从路径获取pidl,我将使用ILCreateFromPath。

SHOpoenFolderSelectedItems大多数需求都将通过以下方式处理:

对于C#中的文件选择,通常使用: System.Windows.Forms.OpenFileDialog 。

对于C#中的文件夹选择,通常使用: System.Windows.Forms.FolderBrowserDialog 。

您可以设置适当的filter,并且可以最初设置所选项目。

也许这足够接近你所需要的?