- •Методические указания для выполнения лабораторной работы №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 в зависимости от их типа»
- •Как установить
- •Подробности реализации
- •Задания для лабораторных работ
- •Содержание отчета
Регистрация расширения
Хэндлер иконок регистрируется под ключом типа файлов, для которого он предназначен, в нашем случае это HKCR\txtfile. Как и для других расширений используется ключ ShellEx под txtfile. Затем следует ключ IconHandler. Значение умолчанию для этого ключа - GUID расширения. Заметьте, что может быть только один хэндлер иконок для каждого типа файлов. Мы также должны изменить значение ключа DefaultIcon на "%1", чтобы наш обработчик был задействован.
Вот RGS сценарий регистрации расширения:
|
HKCR { NoRemove txtfile { NoRemove DefaultIcon = s '%%1' NoRemove ShellEx { ForceRemove IconHandler = s '{DF4F5AE4-E795-4C12-BC26-7726C27F71AE}' } } } |
Отметим, что для того, чтобы определить строку "%1", мы должны писать в RGS файле "%%1", т.к. % это спецсимвол, используемый для указания заменяемых параметров (например "%MODULE%").
Тот факт, что мы перезаписываем существующее значение DefaultIcon имеет большое значение. Как нам правильно деинсталировать свое расширение, если мы уничтожили старое значение DefaultIcon? Выход в том, что мы сохраняем значение DefaultIcon в DllRegisterServer() и восстанавливаем его в DllUnregisterServer(). Мы должны сделать это в процессе деинсталляции и оставить иконки текстовых файлов такими, какими они были до нашего вмешательства.
Посмотрите код функций регистрации/разрегистрации чтобы увидеть как они работают. Отметим, что мы делали сохранение перед вызовом ATL для выполнения RGS скрипта, поскольку если мы сделаем по другому, значение DefaultIcon будет перезаписано прежде, чем мы получить возможность сохранить его.
Пример 10 «Расширение оболочки для изменения иконок у dll в зависимости от их типа»
будет вот что – в зависимости от типа dll (COM, .NET, “обычная” dll) в окне проводника для неё будет показываться своя иконка:
Крупные
значки
Мелкие
значки
Ага, не знали что mfc42.dll – COM сервер :-)
Как установить
Скопируйте dllinfo.dll на локальный диск и зарегистрируйте командой
|
Regsvr32 dllinfo.dll |
После этого нужно или перерегистрироваться в windows, или каким-либо другим способом заставить Проводник перестроить кэш иконок.
Подробности реализации
О написании расширения, устанавливающего иконку для файла в зависимости от его содержимого можно прочитать в Руководстве по написанию расширения для настройки иконок, отображаемых для файлов заданного типа от Michael Dunn, здесь же имеет смысл остановиться на способе определения типа dll:
|
// получаем тип dll DLLTYPE Cdlltester::GetDllType() { WORD ImageType = 0; DLLTYPE result = ITISNOTDLL; HRESULT ( STDAPICALLTYPE * pfn ) ();
// загружаем, но DllMain не вызываем, ни к чему HINSTANCE hi = ::LoadLibraryEx ( m_szFilename, NULL, DONT_RESOLVE_DLL_REFERENCES );
if ( hi ) { // пробуем на COM ( FARPROC& ) pfn = ::GetProcAddress ( hi, "DllGetClassObject" );
if ( pfn ) // это COM dll ! result = COMDLL; else { // DLL, но не COM // может .NET ? ImageType = ::GetExecutableImageType( hi );
if ( ! EIT_IS_DLL( ImageType ) ) result = ITISNOTDLL; else { if ( EIT_IS_DOTNET( ImageType ) ) result = NETDLL; else // если, неCOM, неNET, значит просто dll result = WIN32DLL; }
}
::Sleep( 0 ); ::FreeLibrary ( hi ); }
return result; } |
Как видно из текста программы, для проверки на COM просто проверяется наличие экспортируемой функции DllGetClassObject. Исполняемые файлы .NET - это обычные файлы формата PE (Portable Executable). Для получения адреса той области, где располагается специфичная для .NET информация, необходимо обратиться к элементу IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR массива IMAGE_DATA_DIRECTORY, расположенному в конце заголовка PE-файла (структуры IMAGE_NT_HEADERS).
|
В статье Matt Pietrek “An In-Depth Look into the Win32 Portable Executable File Format” есть упоминание о том, что в более новых версиях системных заголовочных файлов IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR переименован в IMAGE_DIRECTORY_ENTRY_COMHEADER, однако в ноябрьском 2001 года Platform SDK нового названия еще нет. |
Каждый элемент массива IMAGE_DATA_DIRECTORY содержит информацию о расположении данных, определяемых этим элементом.
|
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; //относительный виртуальный адрес данных DWORD Size; //размер данных } |
Таким образом, для определения принадлежности файла к .NET достаточно проверить поле Size элемента IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR массива DataDirectory. Если в файле есть .NET информация, размер данных, Size будет отличным от нуля.
|
#define EIT_IS_DLL(t) ((-1!=(t))&&((t) & 0x1)) #define EIT_IS_DOTNET(t) ((-1!=(t))&&((t) & 0x2))
//------------------------------------------------------------------- // Функция для проверки типа уже загруженного исполняемого файла // // Параметры: // // hinst - HINSTANCE загруженного модуля // // Возвращаемое значение: // // Если младший бит возвр.значения установлен, то файл является // динамической библиотекой (dll), иначе это exe-файл. Для // проверки можно использовать макрос EIT_IS_DLL(); // // Второй бит возвр.значения установлен, то файл является // файлом .NET. Для проверки можно использовать макрос // EIT_IS_DOTNET(); // // В случае неудачи возвращается -1. //------------------------------------------------------------------- WORD GetExecutableImageType( HINSTANCE hinst ) { PBYTE pFileBase; PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNtHeader; WORD result = 0;
if ( !hinst ) return -1;
pFileBase = ( PBYTE ) hinst;
// Убеждаемся в корректности pDosHeader = ( PIMAGE_DOS_HEADER ) pFileBase;
if ( IMAGE_DOS_SIGNATURE == pDosHeader->e_magic ) { pNtHeader = ( PIMAGE_NT_HEADERS ) ( pFileBase + pDosHeader->e_lfanew );
if ( ( !IsBadReadPtr( pNtHeader, sizeof( pNtHeader->Signature ) ) ) && ( IMAGE_NT_SIGNATURE == pNtHeader->Signature ) && ( pNtHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE ) ) { // Делаем необходимые проверки if ( pNtHeader->FileHeader. Characteristics & IMAGE_FILE_DLL ) result |= 0x1;
if ( pNtHeader->OptionalHeader. DataDirectory[ IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ].Size ) result |= 0x2; } }
return result; } |
