- •Методические указания для выполнения лабораторной работы №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 в зависимости от их типа»
- •Как установить
- •Подробности реализации
- •Задания для лабораторных работ
- •Содержание отчета
Модификация контекстного меню
Подобно другим расширениям - обработчикам контекстных меню, обработчик перетаскивания реализует интерфейс IContextMenu. Чтобы добавить его к нашему расширению, откроем HardLinkShlExt.h и добавим выделенные строки:
|
class ATL_NO_VTABLE CHardLinkShlExt : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CHardLinkShlExt, &CLSID_HardLinkShlExt>, public IDispatchImpl<IHardLinkShlExt, &IID_IHardLinkShlExt, &LIBID_HARDLINKLib>, public IShellExtInit, public IContextMenu { BEGIN_COM_MAP(CHardLinkShlExt) COM_INTERFACE_ENTRY(IHardLinkShlExt) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IShellExtInit) COM_INTERFACE_ENTRY(IContextMenu) END_COM_MAP()
public: // IContextMenu STDMETHOD(GetCommandString)(UINT, UINT, UINT*, LPSTR, UINT); STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO); STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT); |
Проводник вызывает нашу функцию QueryContextMenu(), чтобы позволить нам модифицировать контекстное меню. Все это вам уже знакомо. Мы просто добавляем один пункт меню и устанавливаем для него картинку.
|
HRESULT CHardLinkShlExt::QueryContextMenu ( HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags ) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC
// если выставлен флаг CMF_DEFAULTONLY мы не должны что-либо делать if ( uFlags & CMF_DEFAULTONLY ) { return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 ); }
// добавляем пункт в меню. InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uidFirstCmd, _T("Create hard link(s) here") );
if ( NULL != m_bitmap.GetSafeHandle() ) { SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, (HBITMAP) m_bitmap.GetSafeHandle(), NULL ); }
// возвращаем 1, чтобы сообщить оболочке, что мы добавили в меню 1 пункт верхнего уровня. return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 ); } |
Вот как выглядит новый пункт меню:
Создание связи
Если пользователь щелкнет на нашем пункте меню, проводник вызовет нашу InvokeCo... Что это? Я упустил еще одну функцию? Простите.
Обеспечение подсказки в строке состояния
|
HRESULT CHardLinkShlExt::GetCommandString ( UINT idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax ) { return E_NOTIMPL; } |
Я серьезно. :) Для обработчика перетаскивания проводник не вызывает GetCommandString(). А теперь вернемся к ...
Создание связи
Как я уже говорил, проводник вызывает InvokeCommand(), когда пользователь щелкнет на нашем пункте меню. Мы установим связи для всех сброшенных файлов. Имена будут такими "Hard link to <filename>", или, если это имя уже использовалось, "Hard link (2) to <filename>". Номер может быть произвольным. Мы установим предел равным 99.
Сначала - локальные переменные и проверка параметра lpVerb (он должен равняться нулю, поскольку у нас всего один пункт меню).
|
HRESULT CHardLinkShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pInfo ) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC
TCHAR szNewFilename [MAX_PATH+32]; CString sSrcFile; TCHAR szSrcFileTitle [MAX_PATH]; CString sMessage; UINT uLinkNum; POSITION pos;
// перепроверяем, что вызван наш пункт меню - lpVerb должен быть 0. if ( 0 != pInfo->lpVerb ) { return E_INVALIDARG; } |
Далее, мы получаем значение типа POSITION , указывающее на начало списка строк. POSITION - непрозрачный тип данных, который не используется непосредственно, но мы передаем его другим методам класса CStringList. В этом отличие от класса STL итераторов, у которого есть операторы для получения доступа к списку данных. Чтобы получить POSITION заголовка списка, мы вызываем GetHeadPosition():
|
pos = m_lsDroppedFiles.GetHeadPosition(); ASSERT ( NULL != pos ); |
pos принимает значение NULL, когда список пуст, но список не должен быть пустым, поэтому я добавил ASSERT, чтобы проверить этот случай. Далее идет начало цикла, который проходит по именам файлов и устанавливает связи для каждого из них.
|
while ( NULL != pos ) { // получаем следующее имя файла sSrcFile = m_lsDroppedFiles.GetNext ( pos );
// удаляем путь - преобразуем "C:\xyz\foo\stuff.exe" к "stuff.exe" lstrcpy ( szSrcFileTitle, sSrcFile ); PathStripPath ( szSrcFileTitle );
// создаем имя для жесткой связи - сначала пробуем // "Hard link to stuff.exe" wsprintf ( szNewFilename, _T("%sHard link to %s"), m_szFolderDroppedIn, szSrcFileTitle ); |
GetNext() возвращает строку в позиции pos и наращивает pos, чтобы указать на следующую строку. Если мы достигаем конца списка, то pos станет =NULL (вот как закончится цикл while).
В этом месте szNewFilename содержит полное имя жесткой связи. Мы проверяем, существует ли файл с таким именем, и если существует, пытаемся добавить номера от 0 до 99, в зависимости от того, используется ли это имя уже, или нет. Мы также должны быть уверены, что длина имени связи (включая завершающий 0) не превышает MAX_PATH.
|
for ( uLinkNum = 2; PathFileExists ( szNewFilename ) && uLinkNum < 100; uLinkNum++ ) { // составляем другое имя для связи wsprintf ( szNewFilename, _T("%sHard link (%u) to %s"), m_szFolderDroppedIn, uLinkNum, szSrcFileTitle );
// если длина имени превышает MAX_PATH, выводим сообщение об ошибке if ( lstrlen ( szNewFilename ) >= MAX_PATH ) { sMessage.Format ( _T("Failed to make a link to %s. The resulting filename would be too long.\n\nDo you want to continue making links?"), (LPCTSTR) sSrcFile );
if ( IDNO == MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), MB_ICONQUESTION | MB_YESNO )) break; else continue; } } |
Окно сообщения позволяет отменить всю операцию, если вы захотите. Далее, проверяем, не достигли ли мы предела в 99 связей. Снова мы позволяем пользователю отменить всю операцию.
|
if ( 100 == uLinkNum ) { sMessage.Format ( _T("Failed to make a link to %s. Reached limit of 99 links in a single directory.\n\nDo you want to continue making links?"), (LPCTSTR) sSrcFile );
if ( IDNO == MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), MB_ICONQUESTION | MB_YESNO )) break; else continue; } |
Все что осталось - установить жесткую связь. Я не привожу обработку ошибок для большей ясности кода.
|
CreateHardLink ( szNewFilename, sSrcFile, NULL ); } // end while loop
return S_OK; } |
Внешне жестко связанные файлы ничем не отличаются. Они выглядят как обычные файлы, но если вы модифицируете одну копию, изменения будут отражены в другой.
Подведем итоги по использованию класса CStringList:
Объявляем переменную типа POSITION, например pos
Вызываем CStringList::GetHeadPosition() для установки pos на первый элемент в списке.
Запускаем цикл while с условием (pos != NULL).
Вызываем CStringList::GetNext(pos), получаем строку из списка, одновременно наращивая pos.
Делаем все что нужно с полученной строкой.
