- •Методические указания для выполнения лабораторной работы №2.6 по курсу «Операционные системы и системное программирование»
- •Цель работы
- •Теоретические сведения
- •Пример 1 «Добавление пунктов в контекстное меню для отдельных расширений файлов»
- •Использование AppWizard.
- •Интерфейс инициализации
- •Интерфейс взаимодействия с контекстным меню
- •Модификация контекстного меню
- •Отображение подсказки в строке состояния
- •Выполнение выбора пользователя
- •Регистрация расширения оболочки
- •Отладка расширения оболочки
- •Результат
- •Пример 2 «Добавление пунктов в контекстное меню для всех файлов не зависимо от расширения»
- •Дополнительные функции расширения, добавляющего пункты к контекстному меню оболочки.
- •Использование AppWizard
- •Интерфейс инициализации
- •Добавление пунктов к меню
- •Подсказка в строке состояния и "действие"
- •Выполнение выбора пользователя
- •Регистрация расширения
- •Результат
- •Другие способы регистрации расширения
- •Пример 3 «Использование разделяемой с оболочкой памяти»
- •Расширение QueryInfo
- •Использование AppWizard
- •Интерфейс инициализации
- •Создание текста для тултипа
- •Регистрация расширения оболочки
- •Пример 4 «Обработчик перетаскивания контекстного меню»
- •Обработчик перетаскивания
- •Интерфейс инициализации
- •Модификация контекстного меню
- •Создание связи
- •Обеспечение подсказки в строке состояния
- •Создание связи
- •Регистрация расширения
- •Пример 5 «Добавления новых страниц в набор свойств файлов»
- •Обработчик набора свойств
- •Использование AppWizard
- •Интерфейс инициализации
- •Добавление страниц свойств
- •Неприятная ситуация с периодом жизни объектов
- •Функции обратного вызова страницы свойств
- •Обработчики сообщений страницы свойств
- •Регистрация расширения
- •Пример 6 «Обработчик сбрасывания в меню Send To»
- •Обработчик сбрасывания
- •Использование AppWizard
- •Интерфейс инициализации
- •Участвуем в операции drag and drop
- •DragEnter()
- •DragLeave()
- •Регистрация расширения
- •Пример 7 «Owner-drawn меню в расширениях контекстных меню и по созданию расширения контекстного меню, которое отзывается на правый щелчок на фоне окна каталога» Расширение 1 - Пункты меню owner-drawn.
- •Использование AppWizard
- •Интерфейс инициализации.
- •Взаимодействие с контекстным меню
- •Модифицирование контекстного меню.
- •Отображение всплывающей подсказки в строке состояния.
- •Выполнение выбора пользователя.
- •Рисование пункта меню.
- •Обработка wm_measureitem
- •Обработка wm_drawitem
- •Регистрация расширения оболочки
- •Расширение 2 - Обработка щелчка правой кнопкой мыши на фоне окна каталога.
- •Отличия в iShellExtInit::Initialize()
- •Отличия в регистрации.
- •Пример 8 «Добавление колонки в окно детального просмотра Проводника» Детальный просмотр в Windows 2000
- •Использование AppWizard
- •Интерфейс расширения
- •Инициализация
- •Перечисление новых столбцов
- •Отображение данных в столбцах
- •Небольшое отступление - обработка тэгов id3
- •Как это все выглядит?
- •Регистрация расширения оболочки
- •Еще одна полезная штучка - InfoTips
- •Пример 9 «Настройка иконок, отображаемых для файлов заданного типа» Файловые иконки в Проводнике
- •Использование AppWizard
- •Интерфейс расширения
- •Интерфейс инициализации
- •Интерфейс iExtractIcon
- •Извлечение методом 1
- •Извлечение методом 2
- •Регистрация расширения
- •Пример 10 «Расширение оболочки для изменения иконок у dll в зависимости от их типа»
- •Как установить
- •Подробности реализации
- •Задания для лабораторных работ
- •Содержание отчета
Взаимодействие с контекстным меню
Как и раньше, если 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 позже, когда прибудут другие сообщения. Это не строго необходимо, поскольку у нас только один пункт меню, но, тем не менее, является хорошей практикой, и абсолютно обязательно, если у вас много пунктов.
