
Работаю в техподдержке, в один прекрасный день столкнулся с проблемой запуска Архивации или Восстановления в Windows 7. У пользователя на компьютере при выборе в «Панели управления» вкладки «Архивация или восстановление» перестало работать нажатие на «Настроить резервное копирование». Чтобы не искать вкладку, можете просто запустить программу C:\Windows\System32\sdclt.exe
Поиск решения проблемы через Yandex/Google ни к чему не привел. О проблеме много говорят, но реально никто не знает, что происходит, кроме того, что советуют деинсталлировать CompareIt! или PowerISO, которые некорректно обрабатывают встраивание в Проводник через Shell Context Menu. Начал проверять, нет ли похожих программ в нашем случае и выяснилось, что действительно, недавно была инсталлирована одна полезная утилита, которая меняет контекстное меню Проводника, расширяя его возможности.
После деинсталляции этой программы проблемы с Архивацией исчезли. Обратился в техподдержку, меня переключили на разработчиков, которые ничего внятного сказать не могли, кроме того, что утилита была написана достаточно давно и не предназначена для работы с Windows 7. Кроме того, в приватной беседе выяснилось, что для встраивания в Shell Context Menu был использован OpenSource код с сайта CodeGuru.
CtxMenu, Copyright 1999, Smaller Animals Software
This code may be modifed and distributed free of charge or restrictions.
This code is provided as-is. If you use this code in any application, any bugs in the code are your responsibility.
Что же на самом деле происходит?
Как ни странно, ноги растут именно с сайта CodeGuru, где еще в 1999 году Chris Losinger опубликовал свою статью, содержащую пример работы с ContextMenu, которая впоследствии широко разошлась по просторам Интернета. Подозреваю, что вышеуказанные проблемы CompareIt! и PowerISO также связаны с использованием этого кода.
Рассмотрим подробнее файл ShellCtxMenu.cpp, функция InvokeCommand:
STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
//
//немного кода skipped
//
HRESULT hr = NOERROR;
if (!HIWORD(lpcmi->lpVerb))
{
idCmd = LOWORD(lpcmi->lpVerb);
// process it
switch (idCmd)
{
default:
case 0: // operation 1
case 1: // operation 2
//
//много кода skipped
//
break;
} // switch on command
}
return hr;
}
Легко увидеть, что внутри switch обработка ветки default и других case происходит одинаково, а в конце функции всегда возвращается NOERROR. Это неправильно. А где же код возврата E_INVALIDARG — его нужно обязательно использовать, чтобы просигнализировать, что мы не обработали idCmd.
Меняем код на такой:
STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
//
//немного кода skipped
//
HRESULT hr = E_INVALIDARG;
if (!HIWORD(lpcmi->lpVerb))
{
idCmd = LOWORD(lpcmi->lpVerb);
// process it
switch (idCmd)
{
default:
goto err_exit;
case 0: // operation 1
case 1: // operation 2
//
//много кода skipped
//
break;
} // switch on command
hr = NOERROR;
}
err_exit:
return hr;
}
В этом случае возвращаем E_INVALIDARG, если это не наша команда или NOERROR — если наша и она была обработана. После этих изменений средство Архивации или Восстановления стало работать корректно.
Напомню, что готовый файл CtxMenu.Dll следует зарегистрировать путем вызова команды «regsvr32.exe CtxMenu.Dll» под Администратором, деинициализация и удаление из Context Menu Проводника должна выполняться с ключом /u посредством «regsvr32.exe /u CtxMenu.Dll».
Добавлю, что в английском варианте Windows 7 средство «Архивация или Восстановление» пункт «Настроить резервное копирование» называются «Backup and Restore» и «Set up backup» соответственно.
Спасибо за внимание.