10

I have a WPF C# project in which I'm implementing settings for Windows folder options. One of them is "Single-click to open an item" (instead of double-click). When I change the registry keys for that matter, I need to refresh Windows Explorer which I found a solution for. But desktop doesn't refresh, and even refreshing it manually doesn't apply the changes. I've used IActiveDesktop::ApplyChanges method but didn't work (or maybe I made a mistake). I've also used this code snippet, but it still doesn't apply the changes I've made:

SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);

And here is the full code snippet that I used for refreshing Windows Explorer (which is from this site):

[System.Runtime.InteropServices.DllImport("Shell32.dll")]
    private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);

    public static void RefreshWindowsExplorer()
    {
        // Refresh the desktop
        SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);

        // Refresh any open explorer windows
        // based on http://stackoverflow.com/questions/2488727/refresh-windows-explorer-in-win7
        Guid CLSID_ShellApplication = new Guid("13709620-C279-11CE-A49E-444553540000");
        Type shellApplicationType = Type.GetTypeFromCLSID(CLSID_ShellApplication, true);

        object shellApplication = Activator.CreateInstance(shellApplicationType);
        object windows = shellApplicationType.InvokeMember("Windows", System.Reflection.BindingFlags.InvokeMethod, null, shellApplication, new object[] { });

        Type windowsType = windows.GetType();
        object count = windowsType.InvokeMember("Count", System.Reflection.BindingFlags.GetProperty, null, windows, null);
        for (int i = 0; i < (int)count; i++)
        {
            object item = windowsType.InvokeMember("Item", System.Reflection.BindingFlags.InvokeMethod, null, windows, new object[] { i });
            Type itemType = item.GetType();

            // Only refresh Windows Explorer, without checking for the name this could refresh open IE windows
            string itemName = (string)itemType.InvokeMember("Name", System.Reflection.BindingFlags.GetProperty, null, item, null);
            if (itemName == "Windows Explorer")
            {
                itemType.InvokeMember("Refresh", System.Reflection.BindingFlags.InvokeMethod, null, item, null);
            }
        }
    }

That works for Windows Explorer but not the desktop (which is odd since desktop depends on explorer too). So how should I reload the desktop so that my changes take effect?

8
  • What if you try terminating all explorer instances and creating a new one?
    – master131
    Commented Jul 6, 2013 at 13:13
  • @master131, That does work but It's not an option since the users will lose all their explorer windows.
    – SepehrM
    Commented Jul 6, 2013 at 13:43
  • possible duplicate of How do I make the "show/hide desktop icons" setting take effect? Commented Jul 7, 2013 at 3:43
  • @ShengJiang蒋晟, The above question is different and the answers don't solve this problem. That question's main goal is to refresh desktop (F5) witch doesn't apply my changes or reload the icon witch does the same. I want to do exactly what Windows itself does after applying changes in folder options -> Click items as follow. Any idea?
    – SepehrM
    Commented Jul 7, 2013 at 5:13
  • Possible duplicate: stackoverflow.com/questions/647270/…
    – termit
    Commented Jul 9, 2013 at 13:45

2 Answers 2

7

Thanks for all your replies and comments. I finally figured out a workaround for this problem. We could just hide all desktop icons and then show them again. This will force the desktop to reload.

Update: In Window 8, SHELLDLL_DefView is the child of one of WorkerW windows. (instead of Progman) So here is the updated code which works on Windows 8 and 8.1 too:

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    enum GetWindow_Cmd : uint
    {
        GW_HWNDFIRST = 0,
        GW_HWNDLAST = 1,
        GW_HWNDNEXT = 2,
        GW_HWNDPREV = 3,
        GW_OWNER = 4,
        GW_CHILD = 5,
        GW_ENABLEDPOPUP = 6
    }

    private const int WM_COMMAND = 0x111;

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);


    public static string GetWindowText(IntPtr hWnd)
    {
        int size = GetWindowTextLength(hWnd);
        if (size++ > 0)
        {
            var builder = new StringBuilder(size);
            GetWindowText(hWnd, builder, builder.Capacity);
            return builder.ToString();
        }

        return String.Empty;
    }

    public static IEnumerable<IntPtr> FindWindowsWithClass(string className)
    {
        IntPtr found = IntPtr.Zero;
        List<IntPtr> windows = new List<IntPtr>();

        EnumWindows(delegate(IntPtr wnd, IntPtr param)
        {
            StringBuilder cl = new StringBuilder(256);
            GetClassName(wnd, cl, cl.Capacity);
            if (cl.ToString() == className && (GetWindowText(wnd) == "" || GetWindowText(wnd) == null))
            {
                windows.Add(wnd);
            }
            return true;
        },
                    IntPtr.Zero);

        return windows;
    }

    static void ToggleDesktopIcons()
    {
        var toggleDesktopCommand = new IntPtr(0x7402);
        IntPtr hWnd = IntPtr.Zero;
        if (Environment.OSVersion.Version.Major < 6 || Environment.OSVersion.Version.Minor < 2) //7 and -
            hWnd = GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD);
        else
        {
            var ptrs = FindWindowsWithClass("WorkerW");
            int i = 0;
            while (hWnd == IntPtr.Zero && i < ptrs.Count())
            {
                hWnd = FindWindowEx(ptrs.ElementAt(i), IntPtr.Zero, "SHELLDLL_DefView", null);
                i++;
            }
        }
        SendMessage(hWnd, WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
    }

Now we can just toggle desktop icons twice:

        ToggleDesktopIcons();
        ToggleDesktopIcons();

Hope this helps someone else ...

1
  • What happens if it throws an error, or hits a breakpoints between those two lines when toggling and it stays permanently off.
    – mekb
    Commented Jul 26, 2019 at 13:14
3

If you would have posted the code to change that setting, I would have tested it against the following suggestions before replying.

Have you tried:

1) Removing the statement if (itemName == "Windows Explorer") from the above code, so it refreshes every window (including the desktop)?

2) Broadcasting a WM_SETTINGCHANGE via SendMessage WIN32 API?

private const int HWND_BROADCAST = 0xffff;
private const int WM_WININICHANGE = 0x001a, WM_SETTINGCHANGE = 0x001a, INI_INTL = 1;
[DllImport("user32.dll")]
private static extern int SendMessage(int hWnd, uint wMsg, uint wParam, uint lParam);

SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, INI_INTL);

[Credit]

3) IActiveDesktop.ApplyChanges

[ComImport]
[Guid("F490EB00-1240-11D1-9888-006097DEACF9")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IActiveDesktop
{
     [PreserveSig]
     int ApplyChanges(AD_Apply dwFlags);
     // [...]
     // Note: There is a lot more to this interface,
     //        please see PInvoke.net link below.
}
private const int AD_APPLY_REFRESH = 4;

IActiveDesktop.ApplyChanges(AD_APPLY_REFRESH);

[PInvoke.net - IActiveDesktop]

If these do not work, let me know. If it comes down to it, it is possible to save all the open explorer windows & their positions, terminate explorer, wait for explorer to restart, re-open each explorer window and re-position them... if that would acceptable.

Hope this helps.

2
  • Thanks so much but unfortunately none of them worked for me. So I figured out I could hide desktop icons and then show them. That just does what I want and reloads the desktop.
    – SepehrM
    Commented Aug 8, 2013 at 19:25
  • 1
    @feedwall If that's the case, you could get the handle of desktop and send the {F5} key manually ...
    – SepehrM
    Commented Aug 23, 2013 at 15:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.