【问题标题】:SHChangeNotify with SHCNE_RMDIR behavior inconsistent between Windows 7 and Windows 10SHChangeNotify 与 SHCNE_RMDIR 行为在 Windows 7 和 Windows 10 之间不一致
【发布时间】:2017-05-03 03:29:30
【问题描述】:

概述

我正在使用SHChangeNotifySHCNE_RMDIR 来通知外壳程序已从我的shell namespace extension 中删除的文件夹。我的期望是,这将导致其文件夹视图导航到已删除文件夹(或其任何子文件夹)的任何资源管理器(或其他 shell)窗口被导航到已删除文件夹的父文件夹。此预期行为在 Windows 10 上发生。但是,在 Windows 7 上,这些窗口被导航到 removed 文件夹。

问题

Windows 7 上的这种行为是否是一个错误,和/或我可以做些什么(无需为 Windows 7 提供特殊代码)来获得两个操作系统的相同行为?

重现问题的详细步骤

这里介绍了如何从头开始创建和观察问题。这涉及使用名为Shell Instance Object 的“内置”Microsoft 命名空间扩展(而不是我真正的命名空间扩展)。为了简单起见,我使用它来表明它与我的特定命名空间扩展无关。这个示例命名空间扩展所做的只是在“我的电脑”下创建一个图标,该图标将浏览到您的 %TEMP% 目录。

  1. 安装命名空间扩展并在“我的电脑”下注册。为此,请在注册表中输入以下内容:

    HKEY_CURRENT_USER\Software\Classes\CLSID
    
      {0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}=REG_SZ_EXPAND:"My Namespace Extension"
        DescriptionID=REG_DWORD:0x00000008
        System.IsPinnedToNameSpaceTree=REG_DWORD:0x00000001
        DefaultIcon=REG_EXPAND_SZ:"%SystemRoot%\system32\main.cpl,9"
        InProcServer32=REG_EXPAND_SZ:"%SystemRoot%\system32\shdocvw.dll"
          ThreadingModel=REG_SZ:"Apartment"
        ShellFolder
          Attributes=REG_DWORD:0x60000000
        Instance
          CLSID=REG_SZ:"{0AFACED1-E828-11D1-9187-B532F1E9575D}"
          InitPropertyBag
            Attributes=REG_DWORD:0x00000011
            Target=REG_SZ_EXPAND:"%TEMP%"
    

    这是一个 .reg 文件,它会为您自动执行该操作:

    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}]
    @="My Namespace Extension"
    "System.IsPinnedToNameSpaceTree"=dword:00000001
    "DescriptionID"=dword:00000008
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}\DefaultIcon]
    @=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
      00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,6d,00,61,00,\
      69,00,6e,00,2e,00,63,00,70,00,6c,00,2c,00,39,00,00,00
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}\InProcServer32]
    @=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
      00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,73,00,68,00,\
      64,00,6f,00,63,00,76,00,77,00,2e,00,64,00,6c,00,6c,00,00,00
    "ThreadingModel"="Apartment"
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}\Instance]
    "CLSID"="{0AFACED1-E828-11D1-9187-B532F1E9575D}"
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}\Instance\InitPropertyBag]
    "Attributes"=dword:00000011
    "Target"=hex(2):25,00,54,00,45,00,4d,00,50,00,25,00,00,00
    
    [HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}\ShellFolder]
    "Attributes"=dword:60000000
    
    [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\Namespace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}]
    @="My Namespace Extension"
    

    以下 .reg 文件可让您轻松删除上述注册表项:

    Windows Registry Editor Version 5.00
    
    [-HKEY_CURRENT_USER\Software\Classes\CLSID\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}]
    
    [-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\Namespace\{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}]
    
  2. 此时,当您打开资源管理器窗口并浏览“我的电脑”时,您应该会看到“我的命名空间扩展”。浏览它应该会显示您的 %TEMP% 目录文件夹/文件。

  3. 在您的 %TEMP% 文件夹中创建一个名为 FolderToRemove 的目录。在FolderToRemove 中创建一个名为subFolder 的子文件夹。

  4. 打开 3 个资源管理器窗口并浏览到以下位置:

    • 我的电脑\我的命名空间扩展
    • 我的电脑\我的命名空间扩展\FolderToRemove
    • 我的电脑\我的命名空间扩展\FolderToRemove\subFolder

  5. 在 Windows 7 上,执行以下 C++ 代码:

    // This path represents My Computer\My Namespace Extension\FolderToRemove
    const wchar_t * pPath = L"::{20d04fe0-3aea-1069-a2d8-08002b30309d}\\::{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}\\FolderToRemove";
    SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pPath, NULL);
    
  6. 执行上述代码后,您会注意到唯一更改的浏览器窗口是最初导航到My Computer\My Namespace Extension\FolderToRemove\subFolder 的浏览器窗口。注意它现在指向My Computer\My Namespace Extension\FolderToRemove

  7. 预计其中两个资源管理器窗口已更改,因此它们都导航到 My Computer\My Namespace Extension(已删除文件夹的父文件夹)。这是预期行为的图像(这是在 Windows 10 上发生的情况):

补充说明

  • 我发现我可以通过在SHChangeNotify 中指定已删除文件夹的 文件夹来获得我想要的Windows 7 行为。例如:

    // This path represents My Computer\My Namespace Extension
    const wchar_t * pPath = L"::{20d04fe0-3aea-1069-a2d8-08002b30309d}\\::{0672A6D1-A6E0-40FE-AB16-F25BADC6D9E4}";
    SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pPath, NULL);
    

    但是,当我在 Windows 10 上执行此代码时,它当然会导致窗口导航回 My Computer,这是不希望的。如果我采用这种解决方法,我将需要针对不同操作系统的不同代码。

【问题讨论】:

  • 您实际上并没有删除您告诉 Shell 您删除的文件夹,那么为什么您希望资源管理器在导航到的文件夹实际上没有消失时做出反应?当您实际删除文件夹时会发生什么?
  • 您是否为已删除文件夹的父文件夹发送SHCNE_UPDATEDIR?我还发现了这个花絮:verycomputer.com/137_8630153c209e1f9e_1.htm#p3 "例如,在处理 SHCNE_RMDIR 时,树可能会绑定到您传递给 SHChangeNotify 的 pidl 的父 IShellFolder 并使用您的文件夹的相对 pidl 调用 IShellFolder::CompareIDs正在发送更改通知和缓存在树视图中的相对 pidl 以查找匹配项。树还可能有其他 IShellFolder 方法,例如 GetAttributesOf。确保返回正确的值"
  • 上面关于实际上没有将该项目报告为已删除(在发送删除通知后)的评论是正确的,基于 Microsoft 的响应(在下面的答案中)。资源管理器尝试查找以报告为已删除的文件夹开头的有效文件夹。

标签: winapi shell-extensions shell32 shell-namespace-extension


【解决方案1】:

作为 Microsoft 支持案例的一部分,我获得了以下信息,其中描述了 Explorer 在此工作流程中的反应。然而,它并没有给出任何关于 Windows 10 行为不同的原因:


对于导航到 SHCNE_RMDIR 通知中指定的文件夹或其后代之一的任何浏览器窗口,资源管理器会将浏览器窗口导航到有效(祖先)文件夹。从 SCHNE_RMDIR 通知中指定的文件夹的绝对 ITEMIDLIST 开始,确定要在 Explorer 浏览器窗口中打开的新文件夹的过程是:

  1. 为指定的绝对 ITEMIDLIST 的父级获取 IShellFolder。
  2. 通过使用 SFGAO_VALIDATE 标志调用 IShellFolder::GetAttributesOf 来验证子文件夹。
  3. 如果 GetAttributesOf 报告项目无效(返回错误),获取父文件夹的绝对 ITEMIDLIST 并返回步骤 1。
  4. 如果 GetAttributesOf 报告项目有效,则调用浏览器窗口的 IShellBrowser::BrowseObject 以导航到有效文件夹。

在您发布到 StackOverflow 的重现步骤中,我们将 Explorer 浏览器窗口打开到以下文件夹:

::{CLSID_MyComputer}::{CLSID_My 命名空间扩展}

::{CLSID_MyComputer}::{CLSID_My 命名空间扩展}\FolderToRemove

::{CLSID_MyComputer}::{CLSID_My 命名空间 扩展名}\FolderToRemove\subFolder

以下是 SHCNE_RMDIR 通知的方式

::{CLSID_MyComputer}\::{CLSID_My Namespace Extension}\FolderToRemove

文件夹由浏览器窗口处理:

浏览器窗口导航到::{CLSID_MyComputer}\::{CLSID_My Namespace Extension}:

  1. 什么都不做,因为此文件夹不是要删除的文件夹或其后代之一。

浏览器窗口导航到::{CLSID_MyComputer}\::{CLSID_My Namespace Extension}\FolderToRemove

  1. 为 ::{CLSID_MyComputer}::{CLSID_My Namespace Extension} 文件夹获取 IShellFolder,并在 FolderToRemove 上调用 IShellFolder::GetAttributesOf(SFGAO_VALIDATE)。
  2. GetAttributesOf 返回 S_OK,因为 FolderToRemove 并未实际删除。
  3. GetAttributesOf 报告 FolderToRemove 有效这是一个有效文件夹,请在 ::{CLSID_MyComputer}::{CLSID_My Namespace Extension}\FolderToRemove 上调用 IShellBrowser::BrowseObject。这本质上是无操作的,因为浏览器窗口已经导航到该文件夹​​。

浏览器窗口导航到::{CLSID_MyComputer}\::{CLSID_My Namespace Extension}\FolderToRemove\subFolder

  1. 我们从 ::{CLSID_MyComputer}::{CLSID_My Namespace Extension}\FolderToRemove 文件夹的 ITEMIDLIST 开始,因为这是在 SHCNE_RMDIR 通知中指定的文件夹,并且浏览器窗口导航到一个后代文件夹。
  2. 为 ::{CLSID_MyComputer}::{CLSID_My Namespace Extension} 文件夹获取 IShellFolder,并在 FolderToRemove 上调用 IShellFolder::GetAttributesOf(SFGAO_VALIDATE)。
  3. GetAttributesOf 返回 S_OK,因为 FolderToRemove 并未实际删除。
  4. GetAttributesOf 报告 FolderToRemove 有效这是一个有效文件夹,请在 ::{CLSID_MyComputer}::{CLSID_My Namespace Extension}\FolderToRemove 上调用 IShellBrowser::BrowseObject。
  5. 浏览器窗口导航到 FolderToRemoveFolder。

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 2018-10-27
    • 1970-01-01
    • 2021-03-13
    • 2016-12-14
    相关资源
    最近更新 更多