Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОСиСП - методички / OS&SP_Lab_2.6(shell).doc
Скачиваний:
95
Добавлен:
18.05.2015
Размер:
1.12 Mб
Скачать

Взаимодействие с контекстным меню

Как и раньше, если IShellExtInit::Initialize() возвращает S_OK, проводник запрашивает у нашего расширения интерфейс IContextMenu. Чтобы позволить нашему расширению создавать пункты owner-drawn меню он также запрашивает IContextMenu3. IContextMenu3 добавляет метод к IContextMenu, который мы будем использовать для нашего рисования.

Существует интерфейс IContextMenu2, который, как заявляет Microsoft, поддерживается оболочкой версии 4.0. Однако, на Windows95 оболочка никогда не вызывает IContextMenu2. В результате вы просто не сможете сделать пункты меню owner-drawn в оболочке версии 4.0 (на NT4 может быть по-другому. У меня не было возможности проверить на NT4). Вы можете сделать так, чтобы пункт меню отображал битмап, но это приведет к тому, что когда этот пункт меню выбран, он выглядит просто безобразно.(Это то, что делает Pica View).

IContextMenu3 унаследован от IContextMenu2 и добавляет метод HandleMenuMsg2() к IContextMenu. Этот метод дает нам возможность отреагировать на два сообщения меню: WM_MEASUREITERM и WM_DROWITEM. Документация говорит, что метод HandleMenuMsg2() будет вызван, так что он вполне может обработать WM_INITMENUPOPUP и WM_MENUCHAR, однако во время моего тестирования в оболочке 4.72(Win95) и 5.0(Win2K) HandleMenuMsg2() никогда не получал ни одного из этих сообщений.

Поскольку IContextMenu3 наследуется от IContextMenu2 (который сам наследуется от IContextMenu) нам надо, чтобы наш класс С++ просто наследоввался от IContextMenu3. Откройте BmpCtxMenuExt.h и добавьте выделенные строки:

class ATL_NO_VTABLE CBmpCtxMenuExt :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CBmpCtxMenuExt, &CLSID_BmpCtxMenuExt>,

public IDispatchImpl<IBmpCtxMenuExt, &IID_IBmpCtxMenuExt, &LIBID_BMPVIEWEREXTLib>,

public IShellExtInit,

public IContextMenu3

{

BEGIN_COM_MAP(CSimpleShlExt)

COM_INTERFACE_ENTRY(ISimpleShlExt)

COM_INTERFACE_ENTRY(IDispatch)

COM_INTERFACE_ENTRY(IShellExtInit)

COM_INTERFACE_ENTRY(IContextMenu)

COM_INTERFACE_ENTRY(IContextMenu2)

COM_INTERFACE_ENTRY(IContextMenu3)

END_COM_MAP()

public:

// IContextMenu

STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);

STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO);

STDMETHOD(GetCommandString)(UINT_PTR, UINT, UINT*, LPSTR, UINT);

// IContextMenu2

STDMETHOD(HandleMenuMsg)(UINT, WPARAM, LPARAM);

// IContextMenu3

STDMETHOD(HandleMenuMsg2)(UINT, WPARAM, LPARAM, LRESULT*);

Модифицирование контекстного меню.

Как и раньше мы выполняем работу в трех методах IContextMenu. Мы добавляем свой пункт меню в QueryContextMenu(). Сначала мы проверяем версию оболочки. Если это 4.71 или выше, то мы можем добавить пункт меню owner-drawn. В других случаях мы добавляем пункт, который показывает битмап. В последнем случае это все, что мы должны сделать. Меню само позаботится об отображении битмапа.

Итак, во-первых, код для проверки версии оболочки. Он вызывает экспортируемую функцию DllGetVersion() для извлечения версии. Если эта функция отсутствует, то DLL версии 4.0, так как DllGetVersion() не было в shell32.dll версии 4.0.

STDMETHODIMP CBmpCtxMenuExt::QueryContextMenu (

HMENU hmenu,

UINT uIndex,

UINT uidCmdFirst,

UINT uidCmdLast,

UINT uFlags )

{

// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.

if ( uFlags & CMF_DEFAULTONLY )

{

return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

}

bool bUseOwnerDraw = false;

HINSTANCE hinstShell;

hinstShell = GetModuleHandle ( _T("shell32") );

if ( NULL != hinstShell )

{

DLLGETVERSIONPROC pProc;

pProc = (DLLGETVERSIONPROC) GetProcAddress(hinstShell, "DllGetVersion");

if ( NULL != pProc )

{

DLLVERSIONINFO rInfo = { sizeof(DLLVERSIONINFO) };

if ( SUCCEEDED( pProc ( &rInfo ) ))

{

if ( rInfo.dwMajorVersion > 4 ||

rInfo.dwMinorVersion >= 71 )

{

bUseOwnerDraw = true;

}

}

}

}

В этой точке значение bUseOwnerDrow дает добро на использование owner-drawn пунктов меню. Если TRUE, то мы вставляем пункт owner-drawn (смотри строку, которая устанавливает mii.fType). Если FALSE, мы добавляем пункт - битмап, и затем сообщаем меню хэндл битмапа для отображения. Для добавления пункта код использует InsertMenuItem(); использование старой функции InsertMenu() требует, чтобы вы вернулись и вызвали ModifyMenu() для изменения стиля пункта меню на owner-drawn.

MENUITEMINFO mii;

mii.cbSize = sizeof(MENUITEMINFO);

mii.fMask = MIIM_ID | MIIM_TYPE;

mii.fType = bUseOwnerDraw ? MFT_OWNERDRAW : MFT_BITMAP;

mii.wID = uidCmdFirst;

if ( !bUseOwnerDraw )

{

// NOTE: This will put the full-sized bitmap in the menu, which is

// obviously a bit less than optimal. Compressing the bitmap down

// to a thumbnail is left as an exercise.

mii.dwTypeData = (LPTSTR) m_bmp.GetSafeHandle();

}

InsertMenuItem ( hmenu, uIndex, TRUE, &mii );

// Store the menu item's ID so we can check against it later when

// WM_MEASUREITEM/WM_DRAWITEM are sent.

m_uOurItemID = uidCmdFirst;

// Tell the shell we added 1 top-level menu item.

return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 );

}

Мы сохраняем ID пункта меню в m_UOurItemID, так мы опознаем ID позже, когда прибудут другие сообщения. Это не строго необходимо, поскольку у нас только один пункт меню, но, тем не менее, является хорошей практикой, и абсолютно обязательно, если у вас много пунктов.

Соседние файлы в папке ОСиСП - методички