- •Методические указания для выполнения лабораторной работы №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. Первое, что нужно сделать - это добавить IShellExtInit к списку интерфейсов, которые реализует CHardLinkShlExt. Откройте HardLinkShlExt.hи добавьте выделенные строки:
|
#include <comdef.h> #include <shlobj.h>
class ATL_NO_VTABLE CTxtInfoShlExt : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CTxtInfoShlExt, &CLSID_TxtInfoShlExt>, public IDispatchImpl<ITxtInfoShlExt, &IID_ITxtInfoShlExt, &LIBID_TXTINFOLib>, public IShellExtInit { BEGIN_COM_MAP(CTxtInfoShlExt) COM_INTERFACE_ENTRY(ITxtInfoShlExt) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IShellExtInit) END_COM_MAP()
public: // IShellExtInit STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY); |
Нам также понадобятся несколько переменных - для картинки к пункту меню и имен перетаскиваемых файлов:
|
protected: // IHardLinkShlExt CBitmap m_bitmap; TCHAR m_szFolderDroppedIn [MAX_PATH]; CStringList m_lsDroppedFiles; |
Также нужно добавить несколько директив #define в stdafx.h чтобы сделать доступными новые функции из shlwapi.dll:
|
#define WINVER 0x0500 #define _WIN32_WINNT 0x0500 #define _WIN32_IE 0x0400 |
Определение WINVER как 0x0500 задействует возможности, специфичные для Win98 и 2000, а определение _WIN32_WINNT как 0x0500 делает доступными новые возможности Win2000. Определение _WIN32_IE как 0x0400 задействует возможности предоставляемые IE 4.
Теперь приступим к методу Initialize(). В этот раз я покажу, как использовать MFC, чтобы получить список перетаскиваемых файлов. В MFC есть класс COleDataObject - обертка интерфеса IDataObject. Раньше нам приходилось вызывать методы IDataObject непосредственно. Но, к счастью, MFC немного облегчает нашу работу. Чтобы освежить вашу память, вот прототип Initialize():
|
HRESULT IShellExtInit::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID ); |
Для расширений - обработчиков перетаскивания pidlFolder - это PIDL папки, куда сбрасываются объекты. (Подробнее об этом PIDL я расскажу позже). pDataObj - это интерфейс IDataObject, с помощью которого мы перечисляем все сброшенные объекты. hProgID - открытый HKEY нашего расширения под ключом HKEY_CLASSES_ROOT.
Первый шаг - загрузка картинки для пункта меню. Затем мы подключаем переменную COleDataObject к интерфейсу IDataObject.
|
HRESULT CHardLinkShlExt::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID ) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC
COleDataObject dataobj; HGLOBAL hglobal; HDROP hdrop; TCHAR szRoot [MAX_PATH]; TCHAR szFileSystemName [256]; TCHAR szFile [MAX_PATH]; UINT uFile, uNumFiles;
m_bitmap.LoadBitmap ( IDB_LINKBITMAP );
dataobj.Attach ( pDataObj, FALSE ); |
Передача FALSE как второго параметра в Attach() подразумевает, что не нужно освобождать интерфейс IDataObject, если переменная dataobj уничтожена. Следующий шаг - получить каталог, где были сброшены объекты. У нас есть PIDL этого каталога, но как получить весь путь? Настало время для небольшого отступления.
"PIDL" - сокращение от pointer to an ID list - указатель на список идентификаторов объектов. Т.о. PIDL это способ уникальной идентификации объекта в пределах иерархии, предоставленой проводником. Каждый объект в оболочке, независимо от того, является он частью файловой системы или нет, имеет свой PIDL. Точная структура PIDL зависит от того, где объект находится, но если вы не пишете свое namespace-раширение, вам нечего волноваться по поводу внутренней структуры PIDL.
Мы можем для своих целей использовать API оболочки, чтобы перевести PIDL в стандартный путь. Это сделает функция SHGetPathFromIDList(). Если целевая папка не является каталогом в файловой системе (как, например, панель управления) SHGetPathFromIDList() возвращает ошибку и мы спокойно вываливаемся.
|
if ( !SHGetPathFromIDList ( pidlFolder, m_szFolderDroppedIn )) { return E_FAIL; } |
Далее, нужно проверить, находится ли целевой каталог на томе NTFS. Мы получим корневой компонент пути (например E:\) и информацию об этом томе. Если файловая система не NTFS мы не сможем установить связи и возвратим E_FAIL.
|
lstrcpy ( szRoot, m_szFolderDroppedIn ); PathStripToRoot ( szRoot );
if ( !GetVolumeInformation ( szRoot, NULL, 0, NULL, NULL, NULL, szFileSystemName, 256 )) { // не удалось определить тип файловой системы. return E_FAIL; }
if ( 0 != lstrcmpi ( szFileSystemName, _T("ntfs") )) { // файловая система - не NTFS, а значит не поддерживает жесткие связи. return E_FAIL; } |
Далее мы получаем дескриптор HDROP из объекта данных, который мы используем, чтобы перечислить сброшенные файлы. Это похоже на метод из части III, за исключением того, что мы используем класс MFC для получения доступа к данным. COleDataObject оперирует загадочными для нас структурами FORMATETC и STGMEDIUM.
|
hglobal = dataobj.GetGlobalData ( CF_HDROP );
if ( NULL == hglobal ) return E_INVALIDARG;
hdrop = (HDROP) GlobalLock ( hglobal );
if ( NULL == hdrop ) return E_INVALIDARG; |
Затем мы используем дескриптор HDROP, чтобы перечислить сброшенные файлы. Для каждого из них делаем проверку, не является ли он каталогом. Каталоги не могут быть связаны, поэтому, если найден каталог - возвращаем E_FAIL.
|
uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 );
for ( uFile = 0; uFile < uNumFiles; uFile++ ) { if ( DragQueryFile ( hdrop, uFile, szFile, MAX_PATH )) { if ( PathIsDirectory ( szFile )) { // Мы нашли каталог! Прерываем перебор. m_lsDroppedFiles.RemoveAll(); break; } |
Мы также проверяем, находились ли сброшенные файлы на том же самом томе, что и целевой каталог. Для этого сравниваем корневые компоненты для каждого файла и целевого каталога. Если они различаются - возвращаем E_FAIL. Вообще-то это не полное решение, поскольку в Win2000 вы можете установить том в середине другого тома. Например, вы можете иметь том C:\ и смонтировать как другой том каталог C:\DEV. Этот код не исключает попытку установить связь из C:\DEV куда-нибудь еще на C:.
Вот сравнение корневых компонент:
|
if ( !PathIsSameRoot ( szFile, m_szFolderDroppedIn )) { // Файлы переносятся между разными томами. Прерываем перебор. m_lsDroppedFiles.RemoveAll(); break; } |
Если файл успешно прошел обе проверки, мы добавляем его к m_lsDroppedFiles, который является объектом класса CStringList (связанный список строк CString).
|
// Добавляем файл к списку сброшенных файлов m_lsDroppedFiles.AddTail ( szFile ); } } // end for |
После цикла for мы освобождаем ресурсы и возвращаем управление проводнику. Если список содержит какие-либо имена файлов, мы возвращаем S_OK, показывая, что нам необходимо модифицировать контекстное меню. В противном случае мы возвращаем E_FAIL, чтобы наше расширение больше не вызывалось для данного события перетаскивания.
|
GlobalUnlock ( hglobal );
return ( m_lsDroppedFiles.GetCount() > 0 ) ? S_OK : E_FAIL; } |
