刷新Win7中的Windows资源管理器

17
我的程序设置了注册表键值 "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced""Hidden" 属性。但是我无法刷新资源管理器以使这个更改生效。我尝试了以下方法:

1)

    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);` 

2)

    SHELLSTATE state = new SHELLSTATE(); 
    state.fShowAllObjects = (uint)1; 
    SHGetSetSettings(ref state, SSF.SSF_SHOWALLOBJECTS, true); 

3)
    SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0, SMTO_ABORTIFHUNG, 5000, ref dwResult); 

4)
    SendMessage(HWND_BROADCAST, WM_COMMAND, 28931 /* Refresh */, 0); 

什么都不起作用。那我该怎么办?如果我自己用F5刷新Explorer,那就可以了。然而,我想要一些优雅的解决方案,这样它就可以在所有地方刷新显示,甚至在当前打开的OpenFile/SaveFile对话框中也可以。

我正在使用C# .NET,Win7。

状态更新#1

正如Anders所指出的那样,使用COM可以简单地刷新资源管理器窗口:

Guid CLSID_ShellApplication = new Guid("13709620-C279-11CE-A49E-444553540000");
Type shellApplicationType = Type.GetTypeFromCLSID(CLSID_ShellApplication, true);
dynamic shellApplication = Activator.CreateInstance(shellApplicationType);
dynamic windows = shellApplication.Windows();
for (int i = 0; i < windows.Count; i++)
    windows.Item(i).Refresh();

这部分完成了。但是我仍然需要刷新OpenFile/SaveFile对话框,上面的代码并不能做到。有人知道如何刷新这些对话框吗?

一个重要的点是,如果我在控制面板的文件夹选项中更改“显示隐藏文件”,那么系统不会刷新这些OpenFile/SaveFile对话框,我必须手动按F5刷新它们。我只是想找到一种使用C#刷新所有这些对话框的方法,这样我就不需要再按F5了...

状态更新 #2

好的,所以上面的代码有一个新问题——它不仅刷新windows资源管理器,还刷新internet explorer...有没有办法只刷新windows资源管理器?


@SLaks:也许它是一个调整工具? - Max Shawabkeh
@Max:不是的。https://dev59.com/hHE95IYBdhLWcg3wCJXk#2488645 - SLaks
@SLaks:没错,我没有想到去查看之前的问题,因为发帖者只有1个声望值。 - Max Shawabkeh
@SLaks:链接中的应用程序与我同时开发的其他应用程序不同。这个问题中的应用程序只是一个程序,它注册一个热键(例如Ctrl+H),并切换资源管理器的“隐藏”设置并刷新资源管理器... - Paya
我明白了,听起来很有用。我道歉。虽然我没有答案。 - SLaks
我也尝试使用SendKeys发送F5,但这也不起作用。当手动按下F5时,似乎右侧的文件窗格需要处于焦点状态才能刷新--如果窗口框架或文件夹窗格处于焦点状态,则F5无法刷新。 - dbkk
5个回答

18

我找到了一种方法来检查窗口是否为Windows资源管理器窗口,而且我的声望还不够高无法添加评论,所以我想把它提交为答案来帮助您,因为这个问题也帮助了我。

        // based on https://dev59.com/e3E95IYBdhLWcg3wCJXk
        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 explorers
            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);
            }
        }

然而,Win7中的Windows Explorer有些奇怪。如果我在注册表中设置“隐藏”值,而没有运行Windows Explorer,然后启动Windows Explorer,它将不会考虑到注册表的更改,我需要在启动后刷新它。你是否知道如何强制Win Explorer考虑到这个更改呢?定期刷新所有Explorer以确保所有新启动的Explorer都得到刷新并不是一个很好的解决方案。 :-( - Paya
2
起初,这段代码在我的Windows 8上无法运行。我添加了一个额外的检查,判断itemName是否等于“文件资源管理器”。if (itemName == "Windows Explorer" || itemName == "File Explorer") 添加后,代码可以正常工作了。这篇文章说在Windows 8中,它被重命名为“文件资源管理器”。谢谢。 - bhavik shah
我偶尔会得到一个空项。通过在尝试获取项目类型之前简单地检查是否为空来修复它,即:if(item!= null)Type itemType = item.GetType(); - Lee
“Windows Explorer”和“文件资源管理器”在非英文版Windows上无法使用,我们需要获取资源管理器窗口的本地化名称。请查看我的答案:https://dev59.com/e3E95IYBdhLWcg3wCJXk#75970123 - Ahmed Osama

2
我不了解打开/保存对话框,但可以通过COM自动化获取打开资源管理器窗口的列表。使用Shell.Application对象可获得一个窗口集合,或直接使用CoCreate IID_IShellWindows,列表中的每个窗口都有一个刷新方法。

WSH/JScript:

for(var sw=new ActiveXObject("Shell.Application").Windows(),i=0;i<sw.Count; ++i)
   sw.Item(i).Refresh();

我不知道C#,但是这里有一些处理shell窗口的示例,使用WSH/JScriptc++


我只找到了这种方法:http://web.archive.org/web/20080205152949/http://www.experts-exchange.com/Programming/Programming_Languages/C_Sharp/Q_20907891.html 来在C#中执行上述代码。希望我错过了什么,因为那种方式并不是很方便... - Paya
你可以在 C# 中调用 COM 代码 (或者将我的代码放入 .js 文件中,并使用 wscript.exe 执行它)。 - Anders
我应该将从System.Runtime.InteropServices.Marshal.GetActiveObject("Shell.Application")返回的对象转换成什么类型?我找不到任何合适的接口(我对COM了解不多)。 - Paya
我就是无法让它运行。我尝试了这段代码:http://pastebin.com/GFhPCWwX,但在GetActiveObject(MK_E_UNAVAILABLE)上抛出了异常。我已经尝试以管理员权限运行程序,但仍然出现相同的异常...我错过了什么吗? - Paya

1

随着Windows 10更改了资源管理器窗口的名称:

if ((itemName == "Windows Explorer") || (itemName == "File Explorer")) {
    itemType.InvokeMember("Refresh", System.Reflection.BindingFlags.InvokeMethod, null, item, null);
}

"Windows Explorer"和"File Explorer"在非英语版的Windows上无法使用,我们需要获取资源管理器窗口的本地化名称。 - Ahmed Osama
请检查我的答案:https://dev59.com/e3E95IYBdhLWcg3wCJXk#75970123 - Ahmed Osama

0
当您安装注册文件类型的应用程序时,资源管理器窗口通常会刷新以指示新的关联 - 您是否可以监视安装程序正在进行的API调用,以了解它如何刷新窗口?

我猜它可能会调用SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);,但这不会刷新隐藏文件,我猜(没有尝试过)。 - Paya
现在我想了想,你可以在打开隐藏文件时挂接窗口。那么你就可以得到精确的API调用了... - ultrafez
我尝试挂钩Windows消息,看起来系统正在发送WM_WININICHANGE消息。但由于某种奇怪的原因,如果我将该消息发送到资源管理器窗口,它们会被忽略。 - Paya
可能是完整性级别的问题吗?如果您正在以低完整性进程运行,则系统将阻止您的窗口消息到达高完整性进程,我想... - Jonathan Gilbert
@JonathanGilbert 我无法想象Paya仍在为这个问题苦苦挣扎,考虑到他们6年前就提出了这个问题... - ultrafez
哈哈,说得好 -- 我没注意到日期 :-) 但也许其他人会偶然发现这个帖子,遇到类似的问题,我的评论也许能指引他们朝正确的方向。 - Jonathan Gilbert

0

在 @Adam 的回答 https://dev59.com/e3E95IYBdhLWcg3wCJXk#2863647 的基础上,针对非英文版 Windows 系统,"Windows Explorer" 和 "File Explorer" 是不起作用的,我们需要通过读取 explorer.exe MUI 资源文件来获取资源窗口的本地化名称。

我们将使用 SHLoadIndirectString API 从 explorer.exe.mui 文件中读取文本资源。

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);


    static void Main(string[] args)
    {

        var resourcePath = @"@%SystemRoot%\en-US\explorer.exe.mui,-6020"; //en-US should be replaced with the current Windows language code
        resourcePath = Environment.ExpandEnvironmentVariables(resourcePath);

        StringBuilder outBuff = new StringBuilder(1024);

        var result = SHLoadIndirectString(resourcePath, outBuff, outBuff.Capacity, IntPtr.Zero);

        if (result == 0)
        {
            Console.WriteLine(outBuff.ToString());
        }
        else
        {
            Console.WriteLine("SHLoadIndirectString method failed, error code: {0}", result);
        }

        Console.ReadLine();

    }

在Windows 10上,这将输出:文件资源管理器


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接