SHChangeNotify与Windows 7和Windows 10之间的SHCNE_RMDIR行为不一致

时间:2017-05-03 03:29:31

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

概述

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

问题

Windows 7上的这种行为是一个错误,和/或我能做些什么(没有Windows 7的特殊代码)来为两个操作系统获得相同的行为?

重现问题的详细步骤

以下是如何从头开始创建和观察问题的演练。这涉及使用"内置" Microsoft命名空间扩展称为Shell Instance Object(而不是我真正的命名空间扩展)。我使用它是为了简单,并表明它与我的特定命名空间扩展无关。所有这个示例命名空间扩展都会在"我的电脑"下创建一个图标。这将浏览到您的%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%的{​​{1}}文件夹中创建一个目录。在FolderToRemove内创建一个名为FolderToRemove的子文件夹。

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

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

    Image showing 3 explorer windows before executing SHChangeNotify

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

    subFolder
  6. 执行完上述代码后,您会注意到唯一更改的资源管理器窗口是最初导航到// 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); 的窗口。请注意,它现在指向My Computer\My Namespace Extension\FolderToRemove\subFolder

    Image showing Windows 7 after having executed SHChangeNotify

  7. 期望两个浏览器窗口已更改,以便它们全部导航到My Computer\My Namespace Extension\FolderToRemove(已删除文件夹的父文件夹)。以下是预期行为的图像(在Windows 10上发生的情况): Image showing the expected result after executing SHChangeNotify

  8. 附加说明

    • 我发现通过在My Computer\My Namespace Extension中指定已删除文件夹的文件夹,我可以获得我想要的Windows 7行为。 E.g:

      SHChangeNotify

      但是当我在Windows 10上执行此代码时,当然会导致窗口导航回// 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); ,这是不受欢迎的。如果我采用这种解决方法,我将需要不同操作系统的不同代码。

1 个答案:

答案 0 :(得分:3)

作为Microsoft支持案例的一部分,我获得了以下信息,其中描述了Explorer在此工作流程中的反应。但是,它没有说明为什么Windows 10的行为有所不同:

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

  1. 为指定的绝对ITEMIDLIST的父级获取IShellFolder。
  2. 通过使用SFGAO_VALIDATE标志调用IShellFolder :: GetAttributesOf来验证子文件夹。
  3. 如果GetAttributesOf报告该项无效(返回错误),请获取父文件夹的绝对ITEMIDLIST并返回步骤1.
  4. 如果GetAttributesOf报告该项有效,请调用浏览器窗口的IShellBrowser :: BrowseObject以导航到有效文件夹。
  5. 在您发布到StackOverflow的repro步骤中,我们将Explorer浏览器窗口打开到以下文件夹:

      

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

         

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

         

    :: {CLSID_MyComputer} :: {CLSID_My命名空间   扩展} \ FolderToRemove \的子文件夹

    以下是

    的SHCNE_RMDIR通知

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

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

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

    1. 不执行任何操作,因为此文件夹不是要删除的文件夹或其后代之一。
    2. 浏览器窗口导航到::{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。这实际上是一个无操作,因为浏览器窗口已经导航到该文件夹​​。
      4. 浏览器窗口导航到::{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。