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

Интерфейс инициализации

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

}

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