views:

123

answers:

3

Could someone give an example on how to use the shell function SHOpenFolderAndSelectItems from C#? I don't quite get how to use these kind of functions and couldn't find it on pinvoke.net... =/

Say I have three files called

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

I then want to open up the X:\Pictures folder with a.jpg, s.jpg and d.jpg selected.

A: 

Most of the needs would of SHOpoenFolderSelectedItems would be handled by:

For file selection in C# you would normally use: System.Windows.Forms.OpenFileDialog.

For folder selection in C# you would normally use: System.Windows.Forms.FolderBrowserDialog.

You would set an appropriate filter and you can set a selected item initially.

Perhaps this is close enough to what you need?

Brian R. Bondy
No, those I know about. They are for selecting folders or files to open in your application. What I want is to open up explorer windows from my application with files selected. Like you can do in for example Windows Media Player where you can mark a number of files, right-click and click on "Show in Explorer" or what it says there. When you do that, an explorer window will open up with all the files selected that you had selected in WMP.
Svish
A: 

Check http://www.cnblogs.com/qiuyi21/archive/2009/06/24/1510592.html. The example uses IShellLink to get pidls from path before launching SHOpenFolderAndSelectItems, I would use ILCreateFromPath instead.

Sheng Jiang 蒋晟
Hm, I don't read Chineese, hehe. Anyways, could you create a simple example of using ILCreateFromPath and SHOpenFolderAndSelectItems to select the 3 files I mentioned in my question?
Svish
A: 

As you seem to have asked twice the same question (the other being C#: How to open Windows Explorer windows with a number of files selected that had no answer) i post my solution to both questions i don't know if i should make one a community wiki.

Searching for an answer after a coworker had the issue i found none so i wrote a small class to do this. The code is on Gist and i will paste the curent version at the end of this post.

With your sample files, the syntax will be :

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

There are some limitations to my code compared to the low level API, mainly :

  • Selecting on the desktop is not implemented
  • The parent directory must be a directory or a drive, so you can't select multiple drives in the My Computer folder for example.

Anyway, here is the ShowSelectedInExplorer class source code :

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<FileSystemInfo> PathToFileSystemInfo(IEnumerable<string> 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<string> filenames)
    {
        if (filenames == null) throw new ArgumentNullException("filenames");
        if (filenames.Count == 0) return;

        var parentPidl = PathToAbsolutePIDL(parentDirectory);
        try
        {
            var parent = PIDLToShellFolder(parentPidl);

            List<IntPtr> filesPidl = new List<IntPtr>(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<string>)paths);
    }

    public static void FilesOrFolders(IEnumerable<string> paths)
    {
        FilesOrFolders(PathToFileSystemInfo(paths));
    }

    public static void FilesOrFolders(IEnumerable<FileSystemInfo> 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());
        }
    }
}
VirtualBlackFox