You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带子菜单的IExplorerCommand右键菜单经典模式下目录背景点击IShellItemArray为空

问题:经典上下文菜单中带子菜单的IExplorerCommand点击目录背景时IShellItemArray为空

基于IExplorerCommand实现的上下文菜单处理程序,在Windows 11现代和经典上下文菜单中均可正常显示带子菜单的右键菜单,说明MSIX包和AppxManifest.xml配置无问题。

存在以下异常现象:

  • 使用经典上下文菜单(Windows 10原生或Windows 11 Shift+右键唤起)点击目录背景时,IShellItemArray为空;而Windows 11现代上下文菜单中可正常获取内容。
  • 右键菜单无任何子菜单时,无论使用经典还是现代上下文菜单,点击目录背景后IShellItemArray都会包含当前文件夹路径;仅当菜单包含一个或多个子菜单且使用经典上下文菜单时,才会出现IShellItemArray为空的情况。
  • 右键点击文件或文件夹时,无论菜单有无子菜单,IShellItemArray均能正常填充内容。

以下是可复现问题的极简示例代码:

#include "pch.h"
#include <wrl/module.h>
#include <shobjidl_core.h>
#include <wil/resource.h>
#include <vector>

using namespace Microsoft::WRL;

HMODULE g_hModule = nullptr;

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hModule = hModule;
        break;
    case DLL_THREAD_ATTACH:
   </think_never_used_51bce0c785ca2f68081bfa7d91973934>
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

// If user right-clicks a file or folder, the selected item is in the array
// If user right-clicks the background, the array is empty
// Issue only occurs when using classic context menu

class SubCommand : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
{
public:
    SubCommand(PCWSTR title, PCWSTR icon, PCWSTR command) :
        m_title(title), m_icon(icon), m_command(command) {
    }

    IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
    {
        *name = nullptr;
        auto title = wil::make_cotaskmem_string_nothrow(m_title);
        RETURN_IF_NULL_ALLOC(title);
        *name = title.release();
        return S_OK;
    }

    IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* iconPath)
    {
        *iconPath = nullptr;
        return E_NOTIMPL;
    }

    IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip) { *infoTip = nullptr; return E_NOTIMPL; }
    IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName) { *guidCommandName = GUID_NULL; return S_OK; }
    IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState)
    {
        *cmdState = ECS_ENABLED;
        return S_OK;
    }

    IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
    {
        try
        {
            DWORD count = 0;
            if (selection)
            {
                RETURN_IF_FAILED(selection->GetCount(&count));
            }

            if (count > 0)
            {
                ComPtr<IShellItem> item;
                RETURN_IF_FAILED(selection->GetItemAt(0, &item));

                PWSTR filePath;
                RETURN_IF_FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
                wil::unique_cotaskmem_string filePathCleanup(filePath);

                WCHAR message[100];
                StringCchPrintfW(message, ARRAYSIZE(message), L"First item: %s", filePath);
                MessageBox(nullptr, message, L"Debug", MB_OK);
            }
            else
            {
                MessageBox(nullptr, L"No items selected", L"Debug", MB_OK);
            }

            return S_OK;
        }
        catch (...)
        {
            return E_FAIL;
        }
    }

    IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* flags) { *flags = ECF_DEFAULT; return S_OK; }
    IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands) { *enumCommands = nullptr; return E_NOTIMPL; }

private:
    PCWSTR m_title;
    PCWSTR m_icon;
    PCWSTR m_command;
};

class SubCommandEnum : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IEnumExplorerCommand>
{
public:
    SubCommandEnum()
    {
        m_commands.push_back(Make<SubCommand>(L"SubmenuTest", L"SubmenuTest.ico", L"SubmenuTest"));
    }

    IFACEMETHODIMP Next(ULONG celt, IExplorerCommand** pUICommand, ULONG* pceltFetched)
    {
        ULONG fetched = 0;
        while (fetched < celt && m_index < m_commands.size())
        {
            pUICommand[fetched] = m_commands[m_index].Get();
            pUICommand[fetched]->AddRef();
            ++fetched;
            ++m_index;
        }

        if (pceltFetched)
        {
            *pceltFetched = fetched;
        }

        return (fetched == celt) ? S_OK : S_FALSE;
    }

    IFACEMETHODIMP Skip(ULONG celt)
    {
        m_index += celt;
        return (m_index < m_commands.size()) ? S_OK : S_FALSE;
    }

    IFACEMETHODIMP Reset()
    {
        m_index = 0;
        return S_OK;
    }

    IFACEMETHODIMP Clone(IEnumExplorerCommand** ppenum)
    {
        auto clone = Make<SubCommandEnum>();
        clone->m_index = m_index;
        *ppenum = clone.Detach();
        return S_OK;
    }

private:
    std::vector<ComPtr<IExplorerCommand>> m_commands;
    size_t m_index = 0;
};

class __declspec(uuid("8A0E292E-E857-424B-9457-EC5377D0DCFD")) ContextMenuHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
{
public:
    IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
    {
        *name = nullptr;
        auto title = wil::make_cotaskmem_string_nothrow(L"ExplorerHelper");
        RETURN_IF_NULL_ALLOC(title);
        *name = title.release();
        return S_OK;
    }

    IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* iconPath)
    {
        *iconPath = nullptr;
        return E_NOTIMPL;
    }

    IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip) { *infoTip = nullptr; return E_NOTIMPL; }
    IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName) { *guidCommandName = GUID_NULL; return S_OK; }
    IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState)
    {
        *cmdState = ECS_ENABLED;
        return S_OK;
    }

    IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
    {
        // Any code added here no longer runs now that we have a submenu
        return S_OK;
    }

    IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* flags) { *flags = ECF_HASSUBCOMMANDS; return S_OK; }
    IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands)
    {
        *enumCommands = Make<SubCommandEnum>().Detach();
        return S_OK;
    }

    IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept { m_site = site; return S_OK; }
    IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept { return m_site.CopyTo(riid, site); }

protected:
    ComPtr<IUnknown> m_site;
};

CoCreatableClass(ContextMenuHandler)

STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
{
    return Module<ModuleType::InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
}

_Use_decl_annotations_
STDAPI DllCanUnloadNow(void)
{
    return Module<InProc>::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE;
}

_Use_decl_annotations_
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** instance)
{
    return Module<InProc>::GetModule().GetClassObject(rclsid, riid, instance);
}

内容的提问来源于stack exchange,提问作者LesFerch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 17:05:54