C#:如何打开选择了多个文件的 Windows 资源管理器窗口

Posted

技术标签:

【中文标题】C#:如何打开选择了多个文件的 Windows 资源管理器窗口【英文标题】:C#: How to open Windows Explorer windows with a number of files selected 【发布时间】:2010-11-07 14:01:48 【问题描述】:

在 Windows Media Player 的库中,您可以选择一个或多个音乐文件。然后您可以右键单击并在其上下文菜单中选择打开文件位置。这将为文件所在的每个目录打开一个 Windows 资源管理器窗口,并为您选择文件。

假设我们的库中有一堆 mp3 文件,其中三个是:

Z:\Music\Thursday Blues\01。我希望是friday.mp3 Z:\音乐\数羊\01。绵羊#1.mp3 Z:\音乐\数羊\02。绵羊#2.mp3

如果我们选择这三个(在所有这些都可见的视图中)并执行打开文件位置,则会弹出两个资源管理器窗口。一个是 Z:\Music\Thursday Blues 文件夹,01。我希望它是 friday.mp3 被选中,而另一个将是 *Z:\Music\Counting Sheep** 文件夹,01.绵羊#1.mp302。 Sheep #2.mp3 已选中。

我自己如何在 C# 中做到这一点?我们有一个应用程序要将数据导出为各种格式,例如 CSV 和 Excel,我想打开资源管理器窗口,并在创建这些文件并准备好查看时选择这些文件。目前我只做Process.Start(path),这是可行的,但我也希望能够突出显示这些特定文件。会使刚刚创建的文件更加明显。


Windows Media Player 做得很好...我也想这样做 =/ 这里有微软员工可以弄清楚如何做到这一点吗? (一)

【问题讨论】:

【参考方案1】:

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

使用您的示例文件,语法将是:

ShowSelectedInExplorer.FilesOrFolders(
    @"Z:\Music\Thursday Blues\01. I wish it was friday.mp3",
    @"Z:\Music\Counting Sheep\01. Sheep #1.mp3",
    @"Z:\Music\Counting Sheep\02. Sheep #2.mp3"
    );

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

未实现在桌面上选择 父目录必须是目录或驱动器,因此不能在我的电脑文件夹中选择多个驱动器。

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

namespace SHOpenFolderAndSelectItems

    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]
        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]
        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)]
        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);
        

        static class NativeMethods
        
            [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 = SHOpenFolderAndSelectItems_(pidlFolder, cidl, apidl, dwFlags);
                Marshal.ThrowExceptionForHR(result);
            

            [DllImport("shell32.dll")]
            public static extern void ILFree([In] IntPtr pidl);
        

        static IntPtr GetShellFolderChildrenRelativePIDL(IShellFolder parentFolder, string displayName)
        
            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 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<FileSystemInfo> PathToFileSystemInfo(IEnumerable<string> paths)
        
            foreach (var path in paths)
            
                var 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
                        (string.Format("The specified file or folder doesn't exists : 0", fixedPath),
                        fixedPath);
                
            
        

        public static void FilesOrFolders(string parentDirectory, ICollection<string> filenames)
        
            if (filenames == null) throw new ArgumentNullException("filenames");
            if (filenames.Count == 0) return;

            var parentPidl = PathToAbsolutePIDL(parentDirectory);
            try
            
                var parent = PIDLToShellFolder(parentPidl);
                var filesPidl = filenames
                    .Select(filename => GetShellFolderChildrenRelativePIDL(parent, filename))
                    .ToArray();

                try
                
                    SHOpenFolderAndSelectItems(parentPidl, filesPidl, false);
                
                finally
                
                    foreach (var pidl in filesPidl)
                    
                        NativeMethods.ILFree(pidl);
                    
                
            
            finally
            
                NativeMethods.ILFree(parentPidl);
            
        

        public static void FilesOrFolders(params string[] paths)
        
            FilesOrFolders((IEnumerable<string>)paths);
        

        public static void FilesOrFolders(IEnumerable<string> paths)
        
            if (paths == null) throw new ArgumentNullException("paths");

            FilesOrFolders(PathToFileSystemInfo(paths));
        

        public static void FilesOrFolders(IEnumerable<FileSystemInfo> paths)
        
            if (paths == null) throw new ArgumentNullException("paths");
            var pathsArray = paths.ToArray();
            if (pathsArray.Count() == 0) return;

            var explorerWindows = pathsArray.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());
            
        
    

    class Program
    
        static void Main()
        

            var test = 3;
            switch (test)
            
                case 0:
                    var mydocs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                    ShowSelectedInExplorer.FileOrFolder(Path.Combine(mydocs, "Visual Studio 2010"), edit: true);

                    break;

                case 1:
                    ShowSelectedInExplorer.FileOrFolder(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
                    break;

                case 2:
                    ShowSelectedInExplorer.FilesOrFolders(@"C:\Windows\", new[]  "Microsoft.NET", "System32", "Setup" );
                    break;

                case 3:
                    ShowSelectedInExplorer.FilesOrFolders(@"C:\Windows\Microsoft.NET\", @"C:\Windows\System32", @"C:\Windows\Setup", @"C:\Program Files");
                    break;
            
        
    

【讨论】:

这是否适用于分布在多个文件夹中的更多文件? 我修复了添加此功能的代码,它现在在这种情况下会打开多个资源管理器窗口。 @m0s:公共域或 BSD 2 子句,我并不真正关心 API 包装器。【参考方案2】:

免责声明:我认为VirtualBlackFox's answer 比我的要好,尽管目前它的票数较少,所以请向下滚动并先阅读:)

简单的方法(可能不适用于所有平台):

Process.Start(String, String)

第一个参数是应用程序,第二个参数是应用程序的命令行参数..

例如:

Process.Start("explorer.exe",
"/select,Z:\Music\Thursday Blues\01. I wish it was friday.mp3")

Process.Start("explorer.exe",
"/select,Z:\Music\Counting Sheep\01. Sheep #1.mp3 /select,Z:\Music\Counting Sheep\02. Sheep #2.mp3")

(如果文件路径有空格,我认为您可能需要在文件路径周围使用转义引号)。

更多信息:http://msdn.microsoft.com/en-us/library/h6ak8zt5.aspx

(根据this question的几个答案编译)


更难的方法,但更有可能奏效,取自this answer to another question:

使用shell函数SHOpenFolderAndSelectItems

以下是一些示例代码,展示了如何在 C/C++ 中使用该函数,无需进行错误检查:

//Directory to open
ITEMIDLIST *dir = ILCreateFromPath(_T("C:\\"));

//Items in directory to select
ITEMIDLIST *item1 = ILCreateFromPath(_T("C:\\Program Files\\"));
ITEMIDLIST *item2 = ILCreateFromPath(_T("C:\\Windows\\"));
const ITEMIDLIST* selection[] = item1,item2;
UINT count = sizeof(selection) / sizeof(ITEMIDLIST);

//Perform selection
SHOpenFolderAndSelectItems(dir, count, selection, 0);

//Free resources
ILFree(dir);
ILFree(item1);
ILFree(item2);

【讨论】:

最后一个不起作用,因为没有 Process.Start(string, string, string) 这样的东西。这些参数是否应该以某种方式在同一个字符串中? 是的,你是对的。我回答后意识到并更新了答案;现在可以用了吗? 嗯。它会运行,但只会打开一个资源管理器窗口并选择第一个文件。 在这种情况下,可能无法通过此方法完成多个文件。不知道从那里去哪里,除非你想通过这个解决方法:***.com/questions/9355/…【参考方案3】:

尝试开始这个:

explorer.exe /select,Z:\Music\Thursday Blues\01. I wish it was friday.mp3

【讨论】:

适用于一个文件,但如何处理多个文件?【参考方案4】:

总结:无法使用 Process.Start

来自: programatically-select-multiple-files-in-windows-explorer

flashk's answer of "This should be possible with the shell function SHOpenFolderAndSelectItems" 对我来说似乎可行,但我没有厌倦它

这是一个老问题,但出现了 第一次当我在谷歌搜索类似的时候,所以 我试图得出结论 更容易找到。

【讨论】:

【参考方案5】:

也许您可以使用 ProcessExplorer 找出从 Media Player 启动 explorer.exe 时使用了哪些参数?

【讨论】:

首先设置资源管理器,以便资源管理器的每个实例都在新进程中启动。然后从媒体播放器中打开文件。在 ProcessExplorer 中,您应该看到媒体播放器的子进程。双击它,完整的命令行应该会显示在属性的某处。 这就是我尝试过的......但没有弹出资源管理器的新实例:S Windows 7 是否忽略该设置或其他什么? 抱歉,我还没有在 Windows 7 上尝试过。如果你手动启动一个新的资源管理器,你会得到一个新的进程吗? 我相信该选项会在同一进程中打开所有文件夹,但与任务栏分开。 (我说的是“在单独的进程中启动文件夹窗口”的选项。如果有一个选项可以单独打开每个窗口,请随时启发我!)

以上是关于C#:如何打开选择了多个文件的 Windows 资源管理器窗口的主要内容,如果未能解决你的问题,请参考以下文章

C#选择文件选择文件夹打开文件(或者文件夹)

C# mvc怎么打开选择文件夹

C# - 如何自定义 OpenFileDialog 以选择多个文件夹和文件?

选择文件打开 Windows 资源管理器(或焦点,如果存在)的代码

C#选择文件对话框

windows怎么删除nodejs