SHChangeNotify с поведением SHCNE_RMDIR, несовместимым между Windows 7 и Windows 10

обзор

я использую SHChangeNotify с SHCNE_RMDIR уведомить оболочку о папке, которая была удалена из моего расширения пространства имен оболочки. Я ожидаю, что это приведет к тому, что любые окна обозревателя (или другой оболочки), для которых их представление папок будет перемещено в удаленную папку (или любую из ее подпапок), будут перемещены в родительскую папку удаленной папки. Такое ожидаемое поведение наблюдается в Windows 10. Однако в Windows 7 эти окна перемещаются в удаленную папку.

Вопрос

Является ли это поведение в Windows 7 ошибкой, и / или есть ли что-то, что я могу сделать (не имея специального кода для Windows 7), чтобы получить одинаковое поведение для обеих ОС?

Подробные шаги для воспроизведения проблемы

Вот краткий обзор того, как создать и наблюдать проблему с нуля. Это включает использование "встроенного" расширения пространства имен 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

    Рисунок 3 окна обозревателя перед выполнением SHChangeNotify

  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:

    Изображение, показывающее Windows 7 после выполнения SHChangeNotify

  7. Ожидается, что два окна обозревателя будут изменены, так что все они будут перемещены в My Computer\My Namespace Extension (родительская папка удаленной папки). Вот изображение ожидаемого поведения (что происходит в Windows 10): Изображение, показывающее ожидаемый результат после выполнения SHChangeNotify

Дополнительные примечания

  • Я обнаружил, что могу получить желаемое поведение для Windows 7, указав родительскую папку удаленной папки в SHChangeNotify, Например:

    // 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 что нежелательно. Если бы я пошел с этим обходным путем, мне понадобился бы другой код для разных ОС.

1 ответ

В рамках поддержки Microsoft мне была предоставлена ​​следующая информация, которая описывает, как Explorer реагирует в этом рабочем процессе. Это, однако, не дает никаких объяснений, почему Windows 10 ведет себя по-разному:


Для любого окна браузера, перемещенного в папку, указанную в уведомлении SHCNE_RMDIR, или одного из его потомков, Explorer переместит окно браузера в допустимую (предковую) папку. Процесс определения новой папки, открываемой в окне обозревателя Explorer, начиная с абсолютного ITEMIDLIST папки, указанной в уведомлении SCHNE_RMDIR:

  1. Получите IShellFolder для родителя указанного абсолютного ITEMIDLIST.
  2. Проверьте дочернюю папку, вызвав IShellFolder::GetAttributesOf с флагом SFGAO_VALIDATE.
  3. Если GetAttributesOf сообщает, что элемент недействителен (возвращает ошибку), получите абсолютный ITEMIDLIST родительской папки и вернитесь к шагу 1.
  4. Если GetAttributesOf сообщает, что элемент действителен, вызовите IShellBrowser::BrowseObject окна браузера, чтобы перейти к допустимой папке.

На этапах воспроизведения, которые вы разместили в Stackru, у нас открываются окна браузера 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. Получите IShellFolder для папки::{CLSID_MyComputer}::{CLSID_My расширение пространства имен} и вызовите IShellFolder::GetAttributesOf(SFGAO_VALIDATE) в FolderToRemove.
  2. GetAttributesOf возвращает S_OK, поскольку FolderToRemove фактически не был удален.
  3. GetAttributesOf сообщает, что FolderToRemove является допустимым, это допустимая папка, вызовите IShellBrowser::BrowseObject для::{CLSID_MyComputer}::{CLSID_My расширение пространства имен}\FolderToRemove. По сути, это запрет, так как окно браузера уже перемещено в папку.

Окно браузера переместилось на ::{CLSID_MyComputer}\::{CLSID_My Namespace Extension}\FolderToRemove\subFolder

  1. Мы начнем с ITEMIDLIST папки::{CLSID_MyComputer}::{CLSID_My расширение пространства имен}\FolderToRemove, поскольку это папка, которая была указана в уведомлении SHCNE_RMDIR, а окно браузера перемещается в папку-потомок.
  2. Получите IShellFolder для папки::{CLSID_MyComputer}::{CLSID_My расширение пространства имен} и вызовите IShellFolder::GetAttributesOf(SFGAO_VALIDATE) в FolderToRemove.
  3. GetAttributesOf возвращает S_OK, поскольку FolderToRemove фактически не был удален.
  4. GetAttributesOf сообщает, что FolderToRemove является допустимым, это допустимая папка, вызовите IShellBrowser::BrowseObject для::{CLSID_MyComputer}::{CLSID_My расширение пространства имен}\FolderToRemove.
  5. Окно браузера переходит к FolderToRemoveFolder.
Другие вопросы по тегам