0

I'm trying to get a list of selected files from the Desktop. I get the correct amount of files, but I get weird file names like this for example: 㺘ݔ䁐\u0086\u0002. I'm using the LVM_GETITEM message to get the info. This is my code:

        public string GetItemText(int idx)
        {
            const int MAX_SIZE = 512;
            byte[] szBuffer = new byte[MAX_SIZE];

            LVITEM lvi = new LVITEM
            {
                mask = LVIF_TEXT,
                cchTextMax = MAX_SIZE,
                iItem = idx,
                iSubItem = 0,
                pszText = Marshal.AllocHGlobal(MAX_SIZE)
            };

            // Fill LVITEM structure
            IntPtr ptrLvi = Marshal.AllocHGlobal(Marshal.SizeOf(lvi));
            Marshal.StructureToPtr(lvi, ptrLvi, false);
            int readBytes = 0;
            try
            {
                readBytes = SendMessagePtr(ShellListViewHandle, LVM_GETITEM, IntPtr.Zero, ptrLvi);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

            // Get the text
            string itemText = Marshal.PtrToStringAuto(lvi.pszText);
            return itemText;
        }
spunit
  • 523
  • 2
  • 6
  • 23
  • 2
    Not gonna work. You'd need to allocate the memory in the explorer process. However, why are you hacking. Isn't there a shell interface to do this? – David Heffernan Jun 27 '20 at 17:01
  • 1
    Note sure what you mean exactly by "get a list of selected files from the desktop". If you mean get a list of selected items from a view opened on the desktop, you can use the Shell API in C# https://learn.microsoft.com/en-us/windows/win32/shell/shellfolderview-selecteditems and an example: https://stackoverflow.com/questions/8292953/get-current-selection-in-windowsexplorer-from-a-c-sharp-application – Simon Mourier Jun 27 '20 at 17:39
  • No I mean when I select some files, right on my desktop. Clicking on one of these files for example https://www.howtogeek.com/wp-content/uploads/2018/08/img_5b6e4770e6897.jpg – spunit Jun 27 '20 at 22:09
  • I'm sure you can do that from the shell api also. It's a mistake in my view to try to hack at the specific implementation of the desktop. That aside, the reason you code fails is that you are providing memory allocated in the **wrong** process. – David Heffernan Jun 28 '20 at 08:06
  • Do you have a code example how to do it with the Shell API? If that's easier it'd be really great. – spunit Jun 28 '20 at 08:39

1 Answers1

2

You cannot do that using the Shell Objects for Scripting AFAIK. You can only start with it and then use native Shell interfaces.

This principles are explained here Manipulating the positions of desktop icons in C/C++.

Here is a similar C# Console app that dumps out all items names currently selected in the desktop, you'll have to adapt or expand depending on what you need:

public class Program
{
    static void Main()
    {
        // we basically follow https://devblogs.microsoft.com/oldnewthing/20130318-00/?p=4933
        dynamic app = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
        var windows = app.Windows;

        const int SWC_DESKTOP = 8;
        const int SWFO_NEEDDISPATCH = 1;
        var hwnd = 0;
        var disp = windows.FindWindowSW(Type.Missing, Type.Missing, SWC_DESKTOP, ref hwnd, SWFO_NEEDDISPATCH);

        var sp = (IServiceProvider)disp;
        var SID_STopLevelBrowser = new Guid("4c96be40-915c-11cf-99d3-00aa004ae837");

        var browser = (IShellBrowser)sp.QueryService(SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
        var view = (IFolderView)browser.QueryActiveShellView();

        view.Items(SVGIO.SVGIO_SELECTION, typeof(IShellItemArray).GUID, out var items);
        if (items is IShellItemArray array)
        {
            for (var i = 0; i < array.GetCount(); i++)
            {
                var item = array.GetItemAt(i);
                Console.WriteLine(item.GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY));
            }
        }
    }

    [Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IServiceProvider
    {
        [return: MarshalAs(UnmanagedType.IUnknown)]
        object QueryService([MarshalAs(UnmanagedType.LPStruct)] Guid service, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
    }

    // note: for the following interfaces, not all methods are defined as we don't use them here
    [Guid("000214E2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IShellBrowser
    {
        void _VtblGap1_12(); // skip 12 methods https://stackoverflow.com/a/47567206/403671

        [return: MarshalAs(UnmanagedType.IUnknown)]
        object QueryActiveShellView();
    }

    [Guid("cde725b0-ccc9-4519-917e-325d72fab4ce"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IFolderView
    {
        void _VtblGap1_5(); // skip 5 methods

        [PreserveSig]
        int Items(SVGIO uFlags, Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object items);
    }

    [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IShellItem
    {
        [return: MarshalAs(UnmanagedType.IUnknown)]
        object BindToHandler(System.Runtime.InteropServices.ComTypes.IBindCtx pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);

        IShellItem GetParent();

        [return: MarshalAs(UnmanagedType.LPWStr)]
        string GetDisplayName(SIGDN sigdnName);

        // 2 other methods to be defined
    }

    [Guid("b63ea76d-1f85-456f-a19c-48159efa858b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IShellItemArray
    {
        void _VtblGap1_4(); // skip 4 methods

        int GetCount();
        IShellItem GetItemAt(int dwIndex);
    }

    private enum SIGDN
    {
        SIGDN_NORMALDISPLAY,
        SIGDN_PARENTRELATIVEPARSING,
        SIGDN_DESKTOPABSOLUTEPARSING,
        SIGDN_PARENTRELATIVEEDITING,
        SIGDN_DESKTOPABSOLUTEEDITING,
        SIGDN_FILESYSPATH,
        SIGDN_URL,
        SIGDN_PARENTRELATIVEFORADDRESSBAR,
        SIGDN_PARENTRELATIVE,
        SIGDN_PARENTRELATIVEFORUI
    }

    private enum SVGIO
    {
        SVGIO_BACKGROUND,
        SVGIO_SELECTION,
        SVGIO_ALLVIEW,
        SVGIO_CHECKED,
        SVGIO_TYPE_MASK,
        SVGIO_FLAG_VIEWORDER
    }
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298