Обработчик ContextMenu на левой панели проводника

Я пытаюсь написать обработчик контекстного меню с помощью С++. Я хотел бы, чтобы обработчик работал со всеми файлами и всеми папками, а также с теми, которые находятся на левой панели проводника. Я начал на основе проекта CodeProject, который я пытаюсь адаптировать к своим потребностям. Я получил его из здесь.

До сих пор все работает как положено, за исключением папок на левой панели проводника. Там я получаю исключение AccessViolationException. Исключение возникает между щелчком правой кнопкой мыши и отображением контекстного меню. Я удалил код в разных функциях, пока он снова не заработает, и кажется, что проблема связана с методом QueryContextMenu.

Кто-нибудь знает, что я делаю неправильно?

Вот мой текущий код, который работает (несколько очевидно, потому что ни один элемент не добавлен):

STDMETHODIMP ShellExt::Initialize (LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID )
{
    FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    STGMEDIUM stg = { TYMED_HGLOBAL };
    HDROP     hDrop;

    // Look for CF_HDROP data in the data object.
    if ( FAILED( pDataObj->GetData ( &fmt, &stg ) ))
    {
        // Nope! Return an "invalid argument" error back to Explorer.
        return E_INVALIDARG;
    }

    // Get a pointer to the actual data.
    hDrop = (HDROP) GlobalLock ( stg.hGlobal );

    // Make sure it worked.
    if ( NULL == hDrop )
        return E_INVALIDARG;

    // Sanity check - make sure there is at least one filename.
    UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
    HRESULT hr = S_OK;

    if ( 0 == uNumFiles )
    {
        GlobalUnlock ( stg.hGlobal );
        ReleaseStgMedium ( &stg );
        return E_INVALIDARG;
    }

    // Get the name of the first file and store it in our member variable m_szFile.
    if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ) )
        hr = E_INVALIDARG;

    GlobalUnlock ( stg.hGlobal );
    ReleaseStgMedium ( &stg );

    return hr;
}

STDMETHODIMP ShellExt::QueryContextMenu (HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
}

STDMETHODIMP ShellExt::GetCommandString (UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax)
{
    return E_INVALIDARG;
}

STDMETHODIMP ShellExt::InvokeCommand (LPCMINVOKECOMMANDINFO pCmdInfo)
{
    return S_OK;
}

Но теперь, если я добавлю элемент в ShellExt::QueryContextMenu, левая панель проводника выдает исключение, а файлы и папки на правой панели работают отлично. Я попробовал следующие две версии, которые я нашел в Интернете, но у обеих одна и та же проблема:

STDMETHODIMP ShellExt::QueryContextMenu (HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

    InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T("Test Item") );

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 );
}

и

STDMETHODIMP ShellExt::QueryContextMenu (HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

    UINT uID = uidFirstCmd;
    UINT pos = uMenuIndex;

    MENUITEMINFO mii = { sizeof(mii) };
    mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
    mii.fType = MFT_STRING;
    mii.dwTypeData = _T("Menu 1");
    mii.fState = MFS_ENABLED;
    mii.wID = uID++;

    if (!InsertMenuItem(hmenu, pos++, TRUE, &mii))
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd);
}

Параметры, которые я получаю в QueryContextMenu:
- hmenu = 0x0000000001480405
- uMenuIndex = 3
- uidFirstCmd = 148
- uidLastCmd = 32762
- uFlags = 1044

Детали исключения.

Код в QueryContextMenu и GetCommandString выполняется. Исключение возникает где-то после этого, но еще до того, как контекстное меню становится видимым. К сожалению, подробности для меня мало что значат:

Исключение по адресу 0x00007FF9B1C4C613 (shell32.dll) в explorer.exe: 0xC0000005: место чтения с нарушением прав доступа 0x00007FF9776D9868. произошел

Трассировки стека:

shell32.dll!00007ff9b1c4c613()  
ExplorerFrame.dll!00007ff98d24993a()  
ExplorerFrame.dll!00007ff98d2c69a4()  
ExplorerFrame.dll!00007ff98d24de6c()  
ExplorerFrame.dll!00007ff98d251286()  
ExplorerFrame.dll!00007ff98d1fbad0()  
ExplorerFrame.dll!00007ff98d1ad5b3()  
ExplorerFrame.dll!00007ff98d180f2e()  
user32.dll!00007ff9b079ca66()  
user32.dll!00007ff9b079c34b()  
comctl32.dll!00007ff99d78b0da()  
comctl32.dll!00007ff99d78b017()  
ExplorerFrame.dll!00007ff98d187521()  
ExplorerFrame.dll!00007ff98d17d2e0()  
comctl32.dll!00007ff99d78b0da()  
comctl32.dll!00007ff99d78aef2()  
user32.dll!00007ff9b079ca66()  
user32.dll!00007ff9b079c34b()  
duser.dll!00007ff98106dff5()  
atlthunk.dll!00007ff983061208()  
user32.dll!00007ff9b079ca66()  
user32.dll!00007ff9b079c78c()  
user32.dll!00007ff9b07afa83()  
ntdll.dll!00007ff9b34833a4()  
win32u.dll!00007ff9b0071184()  
user32.dll!00007ff9b079bfbe()  
user32.dll!00007ff9b079be38()  
comctl32.dll!00007ff99d77a099()  
comctl32.dll!00007ff99d80b556()  
comctl32.dll!00007ff99d7b1302()  
user32.dll!00007ff9b079ca66()  
user32.dll!00007ff9b079c34b()  
comctl32.dll!00007ff99d78b0da()  
comctl32.dll!00007ff99d78b017()  
ExplorerFrame.dll!00007ff98d1bf33b()  
ExplorerFrame.dll!00007ff98d1bf27b()  
comctl32.dll!00007ff99d78b0da()  
comctl32.dll!00007ff99d78aef2()  
user32.dll!00007ff9b079ca66()  
user32.dll!00007ff9b079c582()  
ExplorerFrame.dll!00007ff98d1748a3()  
ExplorerFrame.dll!00007ff98d1747a9()  
ExplorerFrame.dll!00007ff98d1746f6()  
ExplorerFrame.dll!00007ff98d175a12()  
ExplorerFrame.dll!00007ff98d1870c2()  
windows.storage.dll!00007ff9af8db38c()  
windows.storage.dll!00007ff9af8db045()  
windows.storage.dll!00007ff9af8daf25()  
SHCore.dll!00007ff9b164c315()  
kernel32.dll!00007ff9b06281f4()  
ntdll.dll!00007ff9b344a251()

person Eric Pitz    schedule 03.03.2019    source источник
comment
Итак, в какой строке возникает это исключение? Как выглядит стек вызовов? Что такое параметры функции?   -  person user7860670    schedule 03.03.2019
comment
Вы можете отлаживать процесс проводника (explorer.exe) из Visual Studio, чтобы знать, где и когда возникает ошибка. Или вы можете предоставить минимальный полный код воспроизведения?   -  person Simon Mourier    schedule 03.03.2019
comment
@SimonMourier Та же проблема уже возникает в проекте, который я скачал с CodeProject, который уже довольно минимален. Поэтому я просто изменил функцию регистрации, чтобы зарегистрировать ее для папок. Как я могу передать решение с этим изменением?   -  person Eric Pitz    schedule 03.03.2019
comment
Универсальная проблема с расширениями оболочки заключается в том, что вы не единственный, кто до этого додумался. Машины программистов особенно склонны к ошибкам, которых вы раньше не замечали. Вам нужна лучшая трассировка стека, google visual-c++, как включить сервер символов.   -  person Hans Passant    schedule 03.03.2019
comment
Спасибо вам всем за то, что нашли время подумать о моей проблеме. И спасибо, Ганс, за совет, он, безусловно, будет полезен для следующей ошибки, которую я получу :)   -  person Eric Pitz    schedule 05.03.2019


Ответы (1)


Кажется, я решил проблему. Я переработал оставшуюся часть проекта, то есть реализацию IClassFactory, а также реализацию интерфейса IUnknown класса shellextension, с целью понять, для чего нужны эти «вещи» в этом проекте.

При этом я обнаружил некоторые ошибки, неинициализированные переменные, приведения к неправильным типам и т. д. Я также изменил некоторые счетчики с UINT на LONG, чтобы можно было использовать InterlockedIncrement вместо ++. И после этого проблема как бы ушла. Я больше не могу точно сказать, какая ошибка вызвала AccessViolationException, но в конце концов это было не в методе QueryContextMenu, как я думал...

person Eric Pitz    schedule 04.03.2019