1

我为我正在开发的一个小应用程序开发了一个非常基本的 shell 扩展。我已经使用它一段时间了,没有任何问题,但我只是注意到在 Windows xp 中,在开始菜单 -> 所有程序中,如果我右键单击那里的文件夹并选择“打开”或“探索”,我的出现小应用程序而不是资源管理器窗口。你可以想象当我看到这个的时候我是多么的高兴和自豪。我觉得这很奇怪,因为这是唯一发生的地方(到目前为止......)。我在“目录”注册表项下注册了 shell 扩展,因此它仅在右键单击文件夹时显示。

我找到了一些关于 shell 扩展的好文章,并且到目前为止我已经完成了这些,但是在这个 winapi 东西中我离我的舒适区还很远。我对id做了很多摆弄,但我不知道问题出在哪里。

这是我的 querycontextmenu 实现,我希望我在那里犯了一个愚蠢的错误,C++ 大师可以立即发现。任何帮助将不胜感激。

STDMETHODIMP ShellExtension::QueryContextMenu(HMENU hMenu,  UINT indexMenu,  UINT idCmdFirst,  UINT idCmdLast, UINT uFlags){
if (CMF_DEFAULTONLY & uFlags)
{
    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
}

UINT uID = idCmdFirst;

if (!InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
{
    return HRESULT_FROM_WIN32(GetLastError());
}

HMENU hSubmenu = CreatePopupMenu();

InsertMenu (hSubmenu, 0, MF_BYPOSITION, uID++, (this->isFrench ? SET_REF_TEXT : SET_REF_TEXT_EN));
InsertMenu (hSubmenu, 1, MF_BYPOSITION, uID++, (this->isFrench ? SET_COMP_TEXT : SET_COMP_TEXT_EN));

MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP | MIIM_SUBMENU | MIIM_DATA | MIIM_STRING;
mii.hSubMenu = hSubmenu;
mii.fType = MFT_STRING;
mii.dwTypeData = (this->isFrench ? MAIN_TEXT : MAIN_TEXT_EN);
mii.hbmpItem = IsRequirePainting() ? HBMMENU_CALLBACK : m_hMenuBmp;
mii.wID = uID++;

if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii))
{
    return HRESULT_FROM_WIN32(GetLastError());
}

if (!InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
{
    return HRESULT_FROM_WIN32(GetLastError());
}

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

谢谢!

编辑:

这是对我有用的解决方案......对于任何在这里绊倒的人:

STDMETHODIMP ShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
    if (!HIWORD(lpcmi->lpVerb))
    {
        UINT idCmd = LOWORD(lpcmi->lpVerb);

        switch (idCmd)
        {
        case 0:
            doStuffForFirstCommand();
            break;
        case 1:
            doStuffForSecondCommand();
            break;
        }

        return S_OK;
    }
    else
    {
        return E_INVALIDARG; //this is what I forgot...
    }
}

感谢您的帮助,抱歉代码缩进,似乎我无法弄清楚代码标签。

4

1 回答 1

3

我将在一个真实的答案中总结上面的讨论:

除了由直接用户操作调用之外,上下文菜单扩展还可以通过其他几种方式调用。当用户在 Windows 资源管理器中右键单击某个项目时选择“打开”或“打印”等 shell 的内置动词之一时,Windows 将依次查询每个已注册的 shell 扩展以找到处理它的那个。ShellExecute如果用户调用以编程方式调用动词,也会发生同样的事情。

这允许第三方开发人员将新动词添加到 shell 的词汇表中,但如果 shell 扩展不能处理这种情况,也会带来问题。

当上下文菜单处理程序的InvokeCommand方法不检查其参数时,就会出现问题。以下是关于该成员的文档CMINVOKECOMMANDINFO必须说的内容lpVerb(强调我的):

如果高位字不为零,则此成员是以空字符结尾的字符串的地址,该字符串指定要执行的命令的与语言无关的名称。当应用程序激活命令时,此成员通常是字符串。系统为以下命令字符串提供预定义的常量值。

这不是一个固定的集合;上下文菜单处理程序可以发明新的规范动词,应用程序可以调用它们。

如果存在规范动词并且菜单处理程序未实现规范动词,则它必须返回失败代码以使下一个处理程序能够处理此动词。不这样做会破坏系统中的功能,包括ShellExecute.

Raymond Chen 的博客文章“当然,我们这样做:上下文菜单版”描述了在这种情况下可能发生的事情。他还指出了一个不幸的事实,即足够多的 shell 扩展表现出这种不礼貌的行为,Windows 团队创建了一个特殊的注册表项来帮助处理这些错误。

当然,如果您是开发人员,您应该确保检查参数InvokeCommand并返回正确的代码,而不是依赖注册表解决方法。

于 2012-07-12T14:17:31.313 回答