2

动机:创建我们自己的文件对话框,其外观和行为与标准通用对话框非常相似

问题:如何获取当前文件夹/shell容器的视图下拉

明显的死胡同:

  • 查询 IShellFolder 的 IContextMenu < NULL 接口指针。
  • 查询 IShellView 的 IContextMenu < NULL 接口指针。
  • IShellFolder::CreateViewObject(IID_IContextMenu...) < 非常有限的上下文菜单(新)。
  • IShellFolder::GetUIObjectOf(IID_IContextMenu...) < 有限的上下文菜单(打开、探索、...)。
  • 实现 IShellBrowser 的 InsertMenusSB、RemoveMenusSB 和 SetMenuSB

我花了一些时间阅读实现文件夹视图如何托管 IContextMenu。这似乎表明上面的最终方法(实现 InsertMenuSB,...)应该有效。IShellView 应该使用适当的项目填充 IShellBrowser 的共享菜单,包括其视图子菜单。但是,到目前为止,我从中得到的只是一个空菜单(除非我用项目填充它——在这种情况下,我只得到我用它填充的项目)。

当然有办法做到这一点。Windows 资源管理器从某个地方到达它显示的菜单(如果您在 Vista 或更高版本上按下 ALT)。而且我无法想象这个菜单是由 Explorer 本身静态构建的 - 它肯定是动态创建的,与当前显示的 IShellView 一致,以允许 shell 扩展显示正确的视图选项列表(和其他菜单选项)。

但是有关InsertMenuSBRemoveMenuSBSetMenuSB的文档令人困惑。这似乎表明,作为容器服务器,我应该填充提供的OLEMENUGROUPWIDTHS“在元素 0、2 和 4 中以反映它在文件、视图和窗口菜单组中提供的菜单元素的数量。”

我已实施以下措施以尝试正确履行本合同:

HRESULT STDMETHODCALLTYPE ShellBrowserDlgImpl::InsertMenusSB(__RPC__in HMENU hmenuShared, /* [out][in] */ __RPC__inout LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
    TRACE("IShellBrowser::InsertMenusSB\n");

    // insert our main pull-downs
    struct  
    {
        UINT    id;
        LPCTSTR label;
    } pull_downs[] = {
        { FCIDM_MENU_FILE, "File" },
        { FCIDM_MENU_EDIT, "Edit" },
        { FCIDM_MENU_VIEW, "View" },
        { FCIDM_MENU_TOOLS, "Tools" },
        { FCIDM_MENU_HELP, "Help" },
    };
    for (size_t i = 0; i < countof(pull_downs); ++i)
    {
        VERIFY(AppendMenu(hmenuShared, MF_POPUP, pull_downs[i].id, pull_downs[i].label));
        ASSERT(GetMenuItemID(hmenuShared, i) == pull_downs[i].id);
    }

    // set the count of menu items we've inserted into each *group*
    lpMenuWidths->width[0] = 2; // FILE: File, Edit
    lpMenuWidths->width[2] = 2; // VIEW: View, Tools
    lpMenuWidths->width[4] = 1; // WINDOW: Help

    return S_OK;
}

有没有人实现了一个类似资源管理器的项目,可以正确地向最终用户公开当前 IShellView 的菜单?

是否有关于IOLEInPlaceFrame实现的文档/示例可以阐明这个模糊的主题?

啊!@ - 我觉得我必须很接近 - 但还不够接近!

4

3 回答 3

3

使用 SVGIO_BACKGROUND 获取文件夹的背景菜单,它应该有一个视图子菜单。“查看”菜单项的索引、名称和命令 ID 可能因 Windows 版本和本地语言而异,所以这是一种 hack。

于 2009-12-03T03:21:27.953 回答
1

对于那些可能感兴趣的人,这是我正在使用的给定答案的实现:

void ShellBrowserDlgImpl::ViewModeDropDown(const CPoint & pt)
{
    // ask the view for its context menu interface
    CComPtr<IContextMenu> pcm;
    if (FAILED(m_hresult = m_shell_view->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pcm))) || !pcm)
        throw CLabeledException("Unable to query the context menu interface from the shell view: ");

    // create a blank menu to store it in
    CMenu menu;
    if (!menu.CreateMenu())
        throw CContextException("Unable to create an empty menu in which to store the context menu: ");

    // populate the context menu
    if (FAILED(m_hresult = pcm->QueryContextMenu(menu, 0, SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST, CMF_NORMAL)))
        throw CLabeledException("Unable to query the context menu for the current folder");

    // obtain the "view" submenu to use as our drop-down menu
    //HACK: we assume that the view pop-up is the first entry (true in English)
    //TODO: We need some way to scan for the correct submenu
    // if we knew of a given command that exists under view - we could FindMenuContaining()
    // of if we could scan for an invariant command name using a similar technique
    // or we could possibly...?
    CMenu * pViewMenu = menu.GetSubMenu(0);

    // get the proper orientation for the drop-menu
    UINT uFlags = ::GetSystemMetrics(SM_MENUDROPALIGNMENT) ? TPM_RIGHTALIGN|TPM_HORNEGANIMATION : TPM_LEFTALIGN|TPM_HORPOSANIMATION;

    // display the menu to the user
    BOOL nCmdID = ::TrackPopupMenu(*pViewMenu, TPM_RETURNCMD|uFlags, pt.x, pt.y, 0, m_shell_view_hwnd, NULL);

    // check if the user canceled the menu
    if (!nCmdID)
        return;

    // create the command to execute
    CMINVOKECOMMANDINFO ici = {0};
    ici.cbSize = sizeof(ici);
    ici.hwnd = m_shell_view_hwnd;
    ici.lpVerb = MAKEINTRESOURCE(nCmdID-1); //NOTE: not sure if the -1 is due to the position of the submenu we're pulling out, or something else - might be invalid for other OSes or languages
    if (FAILED(m_hresult = pcm->InvokeCommand(&ici)))
        throw CLabeledException("Unable to execute your command");
}
于 2009-12-03T16:52:53.217 回答
0

您正在重新实现一个众所周知的难以正确控制的控件,并且自开始使用 Windows 以来,许多人都知道并使用过这个控件。任何未能完全正确的操作都会惹恼至少一部分用户,并且“完全正确”的定义将从 Windows 版本更改为 Windows 版本。

为什么不能使用默认的?您正在实施什么为该对话框增加了如此多的价值,以至于标准的对话框无法使用?

于 2009-12-02T23:40:36.550 回答