1

I have used method mentioned here, to show file properties like in windows.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct SHELLEXECUTEINFO
{
        public int cbSize;
        public uint fMask;
        public IntPtr hwnd;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpVerb;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpFile;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpParameters;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpDirectory;
        public int nShow;
        public IntPtr hInstApp;
        public IntPtr lpIDList;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpClass;
        public IntPtr hkeyClass;
        public uint dwHotKey;
        public IntPtr hIcon;
        public IntPtr hProcess;
}
public static bool ShowFileProperties(string Filename)
{
       SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
       info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
       info.lpVerb = "properties";
       info.lpFile = Filename;
       info.nShow = SW_SHOW;
       info.fMask = SEE_MASK_INVOKEIDLIST;
       return ShellExecuteEx(ref info);
}

I am wondering if there is a way to show properties when multiple file has been selected.

Show "properties for multiple files", i mean when user holds ctrl and selects more than one file, right click-> properties. Code mentioned in the link works fine for single file. But I need to show for multiple files. Any idea how to do this ?

Community
  • 1
  • 1
3not3
  • 516
  • 1
  • 7
  • 18
  • so if you do this manually for one file you get one property window correct.. do the same for 2 files and you get one property window not two.. so I would think that you would have to write you own.. – MethodMan Feb 25 '16 at 20:01
  • @3not3 I updated my original solution. Please let me know if it works for you. –  Apr 27 '16 at 22:49
  • Exactly what I was looking for, I am accepting it as answer. Kudos :) – 3not3 May 02 '16 at 18:05

1 Answers1

4

I found a method similar to SHELLEXECUTEINFO on MSDN, which I find interesting. According to the article...

SHMultiFileProperties function

Displays a merged property sheet for a set of files. Property values common to all the files are shown while those that differ display the string (multiple values).

https://msdn.microsoft.com/en-us/library/windows/desktop/bb762230(v=vs.85).aspx

I am not sure how to convert this into usable C# code, but I will update my answer yet again when I do.

Solution

I found a solution! :D

public class Properties
{
    #region Import Methods

    [DllImport("shell32.dll", SetLastError = true)]
    static extern int SHMultiFileProperties(IDataObject pdtobj, int flags);

    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr ILCreateFromPath(string path);

    [DllImport("shell32.dll", CharSet = CharSet.None)]
    public static extern void ILFree(IntPtr pidl);

    [DllImport("shell32.dll", CharSet = CharSet.None)]
    public static extern int ILGetSize(IntPtr pidl);

    #endregion

    #region Static Methods

    #region Private

    private static MemoryStream CreateShellIDList(StringCollection filenames)
    {
        // first convert all files into pidls list
        int pos = 0;
        byte[][] pidls = new byte[filenames.Count][];
        foreach (var filename in filenames)
        {
            // Get pidl based on name
            IntPtr pidl = ILCreateFromPath(filename);
            int pidlSize = ILGetSize(pidl);
            // Copy over to our managed array
            pidls[pos] = new byte[pidlSize];
            Marshal.Copy(pidl, pidls[pos++], 0, pidlSize);
            ILFree(pidl);
        }

        // Determine where in CIDL we will start pumping PIDLs
        int pidlOffset = 4 * (filenames.Count + 2);
        // Start the CIDL stream
        var memStream = new MemoryStream();
        var sw = new BinaryWriter(memStream);
        // Initialize CIDL witha count of files
        sw.Write(filenames.Count);
        // Calcualte and write relative offsets of every pidl starting with root
        sw.Write(pidlOffset);
        pidlOffset += 4; // root is 4 bytes
        foreach (var pidl in pidls)
        {
            sw.Write(pidlOffset);
            pidlOffset += pidl.Length;
        }

        // Write the root pidl (0) followed by all pidls
        sw.Write(0);
        foreach (var pidl in pidls) sw.Write(pidl);
        // stream now contains the CIDL
        return memStream;
    }

    #endregion

    #region Public 

    public static int Show(IEnumerable<string> Filenames)
    {
        StringCollection Files = new StringCollection();
        foreach (string s in Filenames) Files.Add(s);
        var data = new DataObject();
        data.SetFileDropList(Files);
        data.SetData("Preferred DropEffect", new MemoryStream(new byte[] { 5, 0, 0, 0 }), true);
        data.SetData("Shell IDList Array", Properties.CreateShellIDList(Files), true);
        return SHMultiFileProperties(data, 0);
    }

    public static int Show(params string[] Filenames)
    {
        return Properties.Show(Filenames as IEnumerable<string>);
    }

    #endregion

    #endregion
}

This is tested and works on Windows 10. I actually came up with this based on two other SO articles because neither's code by itself worked.

Sources:

P/Invoke for shell32.dll's SHMultiFileProperties

SHMultiFileProperties doens't work on XP

Community
  • 1
  • 1