C#: как открыть окна Windows Explorer с несколькими выбранными файлами

В библиотеке проигрывателя Windows Media вы можете выбрать один или несколько музыкальных файлов. Затем вы можете щелкнуть правой кнопкой мыши и в контекстном меню выбрать Открыть расположение файла. Это откроет одно окно проводника Windows для каждого каталога, в котором находятся файлы, и файлы будут выбраны для вас.

Итак, допустим, у нас есть куча mp3-файлов в нашей библиотеке, три из них:

  • Z:\Music\Thursday Blues\01. Если бы это была пятница.mp3
  • Z:\Music\Counting Sheep\01. Овца #1.mp3
  • Z:\Music\Counting Sheep\02. Овца #2.mp3

Если мы выберем эти три (в представлении, где они все видны) и выполним Открыть расположение файла, то появятся два окна проводника. Одной из них будет папка Z:\Music\Thursday Blues с 01. Я бы хотел, чтобы был выбран файл friday.mp3, а другой была бы папка *Z:\Music\Counting Sheep** с обоими файлами 01. Овца #1.mp3 и 02. Овца #2.mp3 выбрана.

Как я могу сделать это сам на С#? У нас есть приложение, которое будет экспортировать данные в различные форматы, например CSV и Excel, и я хотел бы открыть окна проводника с этими файлами, выбранными, когда они созданы и готовы к просмотру. В настоящее время я просто делаю Process.Start(path), и это работает, но я хотел бы также выделить эти конкретные файлы. Сделает только что созданные файлы более очевидными.


Проигрыватель Windows Media делает это так хорошо... Я тоже хочу это сделать =/ Есть ли здесь сотрудники Microsoft, которые могли бы понять, как это можно сделать? (А)


person Svish    schedule 02.07.2009    source источник


Ответы (5)


В поисках ответа после того, как у коллеги возникла проблема, я не нашел ни одного, поэтому я написал небольшой класс для этого. Код находится в 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;
            }
        }
    }
}
person Julien Roncaglia    schedule 26.08.2010
comment
Работает ли это с большим количеством файлов, разбросанных более чем в одной папке? - person Svish; 27.08.2010
comment
Я исправил код, чтобы добавить эту функциональность, теперь в этом случае открывается несколько окон проводника. - person Julien Roncaglia; 27.08.2010
comment
@m0s: общественное достояние или пункт BSD 2, меня не волнует только оболочка API. - person Julien Roncaglia; 14.05.2014

Отказ от ответственности: я думаю, Ответ VirtualBlackFox лучше моего, хотя в настоящее время у него меньше голосов, поэтому прокрутите вниз и сначала прочитайте его :)

Простой метод (может работать не на всех платформах):

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

(составлено из нескольких ответов на этот вопрос)


Более сложный метод, но, скорее всего, сработает, взятый из этого ответа на еще вопрос:

Используйте функцию оболочки 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);
person Colin Pickard    schedule 02.07.2009
comment
Последнее не работает, потому что не существует такой вещи, как Process.Start(string, string, string). Должны ли эти аргументы каким-то образом быть в одной строке или что-то в этом роде? - person Svish; 02.07.2009
comment
Да, ты прав. Я понял после того, как ответил и обновил ответ; это работает сейчас? - person Colin Pickard; 02.07.2009
comment
Хм. Он запускается, но открывает только одно окно проводника с первым выбранным файлом. - person Svish; 02.07.2009
comment
В этом случае, возможно, несколько файлов не могут быть выполнены этим методом. Не уверен, куда идти оттуда, если вы не хотите использовать этот обходной путь: stackoverflow.com/questions/9355/ - person Colin Pickard; 02.07.2009

Попробуйте запустить это:

explorer.exe /select,Z:\Music\Thursday Blues\01. I wish it was friday.mp3
person Blindy    schedule 02.07.2009
comment
Работает для одного файла, но как сделать это для нескольких? - person Svish; 02.07.2009

Подводя итог: использование Process.Start невозможно.

From: programmatically-select-multiple-files-in-windows-explorer

flashk answer "Это должно быть возможно с функция оболочки SHOpenFolderAndSelectItems" кажется мне возможной , но мне это не надоело

Это старый вопрос, но он возник первым, когда я гуглил подобное, поэтому я пытаюсь упростить поиск вывода.

person Thymine    schedule 10.06.2010

Может быть, вы можете использовать ProcessExplorer, чтобы узнать, какие аргументы используются при запуске explorer.exe из медиаплеера?

person Sebastiaan M    schedule 06.12.2009
comment
Сначала настройте проводник, чтобы каждый экземпляр проводника запускался в новом процессе. Затем откройте файлы из медиаплеера. В ProcessExplorer вы должны увидеть дочерний процесс медиаплеера. Дважды щелкните по нему, и где-то в свойствах должна отобразиться полная командная строка. - person Sebastiaan M; 06.12.2009
comment
Это то, что я пробовал ... но новый экземпляр проводника не появляется: S Windows 7 игнорирует этот параметр или что-то в этом роде? - person Svish; 06.12.2009
comment
Извините, я еще не пробовал это на Windows 7. Если вы запускаете новый проводник вручную, вы получаете новый процесс? - person Sebastiaan M; 07.12.2009
comment
Я считаю, что эта опция открывает все папки в одном процессе, но отдельно от панели задач. (Я говорю об опции под названием «Запускать окна папки в отдельном процессе». Если есть опция, которая открывает каждое окно отдельно, не стесняйтесь, просветите меня!) - person Nulano; 07.12.2015