Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОСиСП - методички / OS&SP_Lab_2.6(shell).doc
Скачиваний:
95
Добавлен:
18.05.2015
Размер:
1.12 Mб
Скачать

Модификация контекстного меню

Подобно другим расширениям - обработчикам контекстных меню, обработчик перетаскивания реализует интерфейс 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.

  • Делаем все что нужно с полученной строкой.

Соседние файлы в папке ОСиСП - методички