Конечно, есть способ получить полное раскрывающееся меню View для текущего представления папки?

Мотивация: Создание собственного файлового диалогового окна, которое выглядит и действует так же, как стандартный стандартный диалог.

Проблема: как получить раскрывающееся меню для текущей папки или контейнера оболочки.

Очевидные Тупики:

  • Запросить у IShellFolder его IContextMenu ‹NULL указатель интерфейса.
  • Запросить у IShellView его IContextMenu ‹NULL указатель интерфейса.
  • IShellFolder :: CreateViewObject (IID_IContextMenu ...) ‹очень ограниченное контекстное меню (новое).
  • IShellFolder :: GetUIObjectOf (IID_IContextMenu ...) ‹ограниченное контекстное меню (открыть, изучить, ...).
  • Реализуйте IShellBrowser InsertMenusSB, RemoveMenusSB и SetMenuSB ‹Меню никогда не заполняется сверх того, чем я его заполняю

Я потратил некоторое время на чтение Реализация представления папки и Как разместить IContextMenu. Похоже, это указывает на то, что последний подход, описанный выше (реализация InsertMenuSB, ...), должен работать. IShellView должен заполнять общее меню для IShellBrowser, включая его подменю View, соответствующими элементами. Однако пока все, что я получаю от этого, - это пустое меню (если я не заполняю его элементами - в этом случае я просто получаю элементы, которыми заполняю его).

Конечно, есть способ сделать это. Проводник Windows попадает в меню, которое он отображает (если вы нажмете ALT в Vista или более поздних версиях) откуда-то. И я не могу представить, что это меню статически создается самим проводником - оно наверняка динамически создается каким-то образом вместе с отображаемым в данный момент IShellView, чтобы расширения оболочки отображали правильный список параметров просмотра (и других параметров меню).

Но документация по InsertMenuSB, RemoveMenuSB и SetMenuSB сбивает с толку. Кажется, это указывает на то, что как сервер контейнера я должен заполнить предоставленные 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, которые могут пролить свет на эту непонятную тему?

Ух! @ - Мне кажется, я должен быть близко - но недостаточно близко!


person Mordachai    schedule 02.12.2009    source источник


Ответы (3)


используйте SVGIO_BACKGROUND, чтобы получить фоновое меню папки, в котором должно быть подменю просмотра. индекс, имя и идентификатор команды пункта меню «просмотр» могут отличаться в зависимости от версии Windows и локальных языков, так что это своего рода взлом.

person Sheng Jiang 蒋晟    schedule 03.12.2009
comment
Я не думаю, что у вас есть идеи, как убедиться, что я могу получить подменю просмотра независимо от ОС, в которой я работаю (как версии, так и языка). - person Mordachai; 03.12.2009
comment
В английских версиях Windows вы можете искать в подменю то, текст которого - View, и надеяться, что Microsoft не изменит его на что-то другое позже. Я сомневаюсь, что Microsoft изменит это в ближайшее время, но это все еще недокументированное поведение. - person Sheng Jiang 蒋晟; 03.12.2009

Для тех, кому может быть интересно, вот реализация данного ответа, который я использую:

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");
}
person Mordachai    schedule 03.12.2009

Вы заново реализуете заведомо трудный элемент управления, который нужно исправить, и который многие, многие люди знают и используют с тех пор, как начали использовать Windows. Любая неспособность сделать это правильно будет раздражать по крайней мере часть ваших пользователей, и определение «точно правильно» будет меняться от выпуска Windows к выпуску Windows.

Почему вы не можете использовать тот, который используется по умолчанию? Что вы реализуете, что добавляет этому диалоговому окну столько ценности, что стандартное невозможно использовать?

person Dan Davies Brackett    schedule 02.12.2009
comment
Хотелось бы, чтобы мы могли придерживаться стандартного. В нашем приложении есть расширенная панель мест и собственная панель предварительного просмотра файлов, а также более общие настройки диалогового окна файлов. Начиная с Vista, Microsoft, конечно же, изменила способ настройки и использования диалогового окна файла, и этот интерфейс исключает как нашу расширенную панель мест, так и нашу частную панель предварительного просмотра. Этот интерфейс дополнительно ограничен Vista +, и мы должны поддерживать совместимость с 2K. Однако, если мы используем более старый подход к настройке, который работает для 2K и XP, наш диалог не работает. Полное объяснение выходит за рамки этой возможности. - person Mordachai; 03.12.2009