如何使用 Powershell 告诉 Windows 资源管理器正确且完全刷新其图标?

问题描述 投票:0回答:1

我做了很多图标设计。当我修改 Windows 图标并覆盖原始图标时,资源管理器不会刷新并显示新图标。

如果我更改新图标的名称以使其具有唯一性,我所做的更改将正确显示。但是,如果它与原始未修改的图标同名,它仍然显示旧图标。看来Win10是按名称缓存图标的。

如何使用 Pwsh 强制 Windows 10 的 explorer.exe 刷新其图标缓存?

我发现这篇文章是关于同样的事情,但代码是用 C 语言编写的,我不理解或不熟练:

如何告诉Windows资源管理器刷新其图标?

有没有简单的方法可以从 Powershell 中调用

SHChangeNotify
SHUpdateImage
WM_SETTINGCHANGE

我可以使用一个 C# 程序集来定义一个帮助程序类来通知 Windows 它需要刷新其缓存吗?

到目前为止我尝试过的:

function Request-WindowsExplorerRefresh {
    param (
        [switch] $SendF5,
        [Int32] $SendF5Delay=150
    )

    $shellApplication = New-Object -ComObject Shell.Application
    $windows = $shellApplication.Windows()
    $count = $windows.Count()

    foreach( $i in 0..($count-1) ) {
        $item = $windows.Item( $i )
        if( $item.Name() -like '*Explorer*' ) {
            $item.Refresh()
        }
    }

    if($SendF5){
        $wshell = New-Object -ComObject wscript.shell;
        Start-Sleep -Milliseconds $SendF5Delay
        $wshell.SendKeys("{F5}")
        Start-Sleep -Milliseconds $SendF5Delay
        $wshell.SendKeys("{F5}")
    }
}

上面的代码使用 Com 和 Shell.Application 告诉资源管理器刷新。我还有一个可选开关,可以额外按 F5 几次来刷新窗口。不幸的是,这个功能不起作用,并且实际上并没有刷新图标缓存。

我也尝试过这种方法,利用 C# 辅助类:

function Request-WindowsExplorerRefreshAlt {
    param ()

$code = @"
using System;
namespace VSYS
{
    public static class Util
    {

        public static void RefreshExplorer(){

            Console.WriteLine("Refreshing Explorer");

            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 });
                if (item != null) {
                    Type itemType = item.GetType();
                    // only refresh windows explorer
                    string itemName = (string)itemType.InvokeMember("Name", System.Reflection.BindingFlags.GetProperty, null, item, null);
                    if ((itemName == "Windows Explorer") || (itemName == "File Explorer")) {
                        itemType.InvokeMember("Refresh", System.Reflection.BindingFlags.InvokeMethod, null, item, null);
                    }
                }
            }
        }
    }
}
"@
    Add-Type -TypeDefinition $code -Language CSharp
    Invoke-Expression "[VSYS.Util]::RefreshExplorer()"
}

此代码也不起作用。所以我现在正在旋转我的轮子,找不到任何解决方案。我认为关键与调用

SHChangeNotify
SHUpdateImage
或广播
WM_SETTINGCHANGE
有关。但我完全不知道如何在 Powershell(或 C#)中执行此操作

我确实需要一些帮助,因为当前的 Windows 行为让我陷入困境。

非常感谢任何帮助。

c# windows powershell explorer
1个回答
1
投票

要让 Windows 资源管理器知道某些内容已更改,您需要强制刷新。

根据您的情况,您可以尝试简单地重新启动资源管理器进程:

Stop-Process -ProcessName explorer

另一种方法可以使用 Win32 API,如下所示:

function Refresh-Explorer {
    $code = @'
private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff);   //  http://www.pinvoke.net/default.aspx/Constants/HWND.html
private const uint WM_SETTINGCHANGE   = (uint)0x1a;                   //  http://www.pinvoke.net/default.aspx/Constants/WM.html
private const uint SMTO_ABORTIFHUNG   = (uint)0x0002;                 //  http://www.pinvoke.net/default.aspx/Enums/SendMessageTimeoutFlags.html
private const uint SHCNE_ASSOCCHANGED = (uint)0x08000000L;            //  http://www.pinvoke.net/default.aspx/Enums/SHChangeNotifyEventID.html
private const uint SHCNF_FLUSH        = (uint)0x1000;                 //  http://www.pinvoke.net/default.aspx/Enums/SHChangeNotifyFlags.html

[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessageTimeout (IntPtr hWnd, uint Msg, IntPtr wParam, string lParam, uint fuFlags, uint uTimeout, IntPtr lpdwResult);

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

public static void Refresh()  {
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
    SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, null, SMTO_ABORTIFHUNG, 100, IntPtr.Zero);
}
'@

    Add-Type -MemberDefinition $code -Namespace Win32Refresh -Name Explorer
    [Win32Refresh.Explorer]::Refresh()
}

# call the above function to tell explorer something has changed
Refresh-Explorer
© www.soinside.com 2019 - 2024. All rights reserved.