当然有办法获得当前文件夹视图的完整视图下拉菜单吗?

发布于 2024-08-13 03:09:43 字数 2662 浏览 6 评论 0原文

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

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

明显死角:

  • < strong>查询 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)。我无法想象这个菜单是由资源管理器本身静态构建的 - 它肯定是以某种方式动态创建的,与当前显示的 IShellView 一致,以允许 shell 扩展显示正确的视图选项列表(和其他菜单选项)。

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

我已经实现了以下内容来尝试正确履行此合同:

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;
}

是否有人实现了类似 Explorer 的项目,可以正确地将当前 IShellView 的菜单公开给最终用户?

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

呃!@ - 我觉得我一定很接近 - 但还不够接近!

Motivation: Creating our own file dialog that looks & acts much like the std common dialog

Problem: How to obtain the view pull-down for the current folder/shell container

Apparent Dead Ends:

  • Query the IShellFolder for its IContextMenu < NULL interface pointer.
  • Query the IShellView for its IContextMenu < NULL interface pointer.
  • IShellFolder::CreateViewObject(IID_IContextMenu...) < very limited context menu (new).
  • IShellFolder::GetUIObjectOf(IID_IContextMenu...) < limited context menu (open, explore,...).
  • Implement IShellBrowser's InsertMenusSB, RemoveMenusSB, and SetMenuSB < The menu is never populated beyond what I populate it with

I have spent some time reading Implementing a Folder View and How to host an IContextMenu. This seems to indicate that the final approach above (implementing InsertMenuSB, ...) should work. The IShellView should be populating the shared menu for the IShellBrowser, including its View submenu, with the appropriate items. However, so far all I get from that is an empty menu (unless I populate it with items - in which case, I just get the items I populate it with).

Surely there is a way to do this. Windows Explorer arrives at the menu it displays (if you press down ALT on Vista or above) from somewhere. And I cannot imagine that this menu is statically built by Explorer itself - it surely is dynamically created somehow in concert with the currently displayed IShellView to allow for shell extensions to display the correct list of view options (and other menu options).

But the documentation on InsertMenuSB, RemoveMenuSB, and SetMenuSB is confusing. It seems to indicate that, as the container server, I should populate the supplied OLEMENUGROUPWIDTHS, "in elements 0, 2, and 4 to reflect the number of menu elements it provided in the File, View, and Window menu groups."

I have implemented the following to attempt to properly fulfill this contract:

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;
}

Has anyone implemented an Explorer like project that properly exposes the current IShellView's menus to the end-user?

Is there documentation / examples on IOLEInPlaceFrame implementations that might shed some light on this murky subject?

Ugh!@ - I feel like I must be close - yet not close enough!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

岁月染过的梦 2024-08-20 03:09:43

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

use SVGIO_BACKGROUND to get the background menu of the folder, which should have a view submenu. the index, name and the command id of the "view" menu item may vary between windows versions and local languages, so this is kind of hack.

千里故人稀 2024-08-20 03:09:43

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

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");
}

For those who might be interested, here's the implementation of the given answer I'm using:

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");
}
恰似旧人归 2024-08-20 03:09:43

您正在重新实现一个众所周知的难以正确使用的控件,而且自开始使用 Windows 以来,许多人都知道并使用过该控件。任何未能使其完全正确的行为都会至少惹恼部分用户,并且“完全正确”的定义将随着 Windows 版本的不同而发生变化。

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

You are reimplementing a notoriously difficult control to get right, and one that many, many people know and have used since they started using Windows. Any failure to get it exactly right will annoy at least some subset of your users, and the definition of 'exactly right' is going to change from windows release to windows release.

Why can't you use the default one? What are you implementing that adds so much value to this dialog that the standard one is impossible to use?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文