- •Методические указания для выполнения лабораторной работы №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 в зависимости от их типа»
- •Как установить
- •Подробности реализации
- •Задания для лабораторных работ
- •Содержание отчета
Пример 1 «Добавление пунктов в контекстное меню для отдельных расширений файлов»
Далее в качестве примера создадим и запустим расширение, которое просто выводит сообщение о том, что оно загружено и работает. Мы привяжем его к TXT-файлам, так что наше расширение будет вызываться, когда пользователь щелкнет правой кнопкой мыши на текстовом файле.
Использование AppWizard.
Я еще не рассказал вам, как использовать интерфейсы расширений оболочки? Не беспокойтесь, я объясню, когда дойду до этого. Это лучше усвоится, если будет сопровождаться примерами. Я объясню понятия и буду сопровождать их кодом примера. Можно было-бы объяснить все сначала, а затем дать код, но я нахожу, что это тяжелее для понимания.
В любом случае запускайте MS VC и мы начнем.
Запустите AppWizard и создайте новый ATL COM проект. Назовем его SimpleExt. Сохраните все установки по-умолчанию и нажмите "Finish". Теперь у нас есть пустой ATL- проект, из которого будет создана DLL, но нам необходимо добавить в него наш COM объект - расширение оболочки.
В дереве ClassView щелкните правой кнопкой на пункте SimpleExt Classes и выберите "New ATL Object". В "ATL Object" - мастере на первой странице уже отмечено "Simple Object", поэтому просто идите дальше. На второй панели введите SimpleShlExt в поле Short Name и щелкните OK. (Други редактируемые поля на панели будут заполнены автоматически). Созданный класс CSimpleShlExt содержит основной код COM - объекта. Мы добавим наш код к этому классу.
Интерфейс инициализации
Когда наше расширение загружается, проводник вызывает функцию QueryInterface(), чтобы получить указатель на интерфейс IShellExtInit. Этот интерфейс имеет только один метод, Initialize():
|
HRESULT IShellExtInit::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID ); |
Проводник использует этот метод, чтобы давать нам различную информацию. pidlFolder - это PIDL (pointer to an ID list) папки, содержащей активизированные файлы. (PIDL - это структура данных, которая однозначно идентифицирует любой объект в оболочке, независимо от того, объект это файловой системы или нет). pDataOdj - указатель на интерфейс IDataObject, с помощью которого мы извлекаем имена активизированных файлов. hProgID открывает HKEY, который мы используем, чтобы получить доступ к ключу реестра, содержащему регистрационные данные нашей DLL. Для нашего расширения необходимо использовать параметр pDataObj.
Чтобы добавить IShellExtInit к нашему проекту, откройте SimpleShExt.h и введите выделенные строки:
|
#include <shlobj.h> #include <comdef.h>
class ATL_NO_VTABLE CSimpleShlExt : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSimpleShlExt, &CLSID_SimpleShlExt>, public IDispatchImpl<ISimpleShlExt, &IID_ISimpleShlExt, &LIBID_SIMPLEEXTLib>, public IShellExtInit { BEGIN_COM_MAP(CSimpleShlExt) COM_INTERFACE_ENTRY(ISimpleShlExt) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IShellExtInit) END_COM_MAP() |
COM_MAP- это то, как ATL осуществляет QueryInterface().
Затем в декларации класса добавьте прототип Initialize(). Нам также необходим буфер, чтобы сохранить имя файла:
|
protected: TCHAR m_szFile [MAX_PATH];
public: // IShellExtInit STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY); |
В SimpleShlExt.cpp добавьте определение функции:
|
HRESULT CSimpleShlExt::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID ) |
В ней мы получим имя файла, отмеченного правым щелчком мыши, и покажем его в выводимом сообщении. Если файлов больше одного, мы могли бы получить доступ к ним ко всем через указатель pDataObj, но, поскольку мы хотим упростить работу мы получим только имя первого из них.
Имя файла сохранено в том же самом формате, в каком оно использовалось, когда вы переносили файл, используя стиль WS_EX_ACCEPTFILES. Это означает, что мы можем получить имена файлов используя то же API - DragQueryFile(). Начнем писать функцию, получающую доступ к данным, содержащимся в IDataObject:
|
{ FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stg = { TYMED_HGLOBAL }; HDROP hDrop;
// Look for CF_HDROP data in the data object. if ( FAILED( pDataObj->GetData ( &fmt, &stg ))) { // Nope! Return an "invalid argument" error back to Explorer. return E_INVALIDARG; }
// Get a pointer to the actual data. hDrop = (HDROP) GlobalLock ( stg.hGlobal );
// Make sure it worked. if ( NULL == hDrop ) { return E_INVALIDARG; } |
Заметим, что крайне важно избежать любых ошибок, особенно с указателями. поскольку наше расширение запускается в пространстве процесса проводника, и, если мы ошибемся, то разрушим проводник. В Win 9х такая ошибка приводит к необходимости перезагружать компьютер.
Теперь, когда мы имеем дескриптор HDROP, мы можем получить нужное имя файла:
|
// Sanity check – make sure there is at least one filename. UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
if ( 0 == uNumFiles ) { GlobalUnlock ( stg.hGlobal ); ReleaseStgMedium ( &stg ); return E_INVALIDARG; }
HRESULT hr = S_OK;
// Get the name of the first file and store it in our member variable m_szFile. if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH )) { hr = E_INVALIDARG; }
GlobalUnlock ( stg.hGlobal ); ReleaseStgMedium ( &stg );
return hr; } |
Если мы возвратим E_INVALIDARG, проводник не вызовет наше расширение повторно при правом щелчке мыши. Если мы возвратим S_OK, проводник вызовет QueryInterface() еще раз и получит указатель на другой интерфейс, который мы добавим - IContextMenu.
