Delphi 3. Библиотека программиста

Что делать с кодом Windows?


Правильный ответ— инкапсулировать. Именно это делает Delphi, и делает очень успешно. Идея Delphi заключается как раз в том, чтобы оградить вас от мелких неприятных деталей Windows-программирования, чтобы все усилия можно было сосредоточить на смысловой части приложения. То же самое мы проделаем и с FMDD — «упакуем» его в одноименный модуль Delphi.

Вместо того чтобы заставлять форму возиться с обработкой WM_DROPFILES, мы определим в модуле FMDD специальную функцию, с помощью которой обработчик OnMessage формы сможет получить объект с полными сведениями о происходящем перетаскивании. Этот объект будет содержать всю информацию, полученную от интерфейса FMDD Windows, но объединенную в простую и удобную структуру:

TDragDropInfo = class (TObject) private FNumFiles : UINT; FInClientArea : Boolean; FDropPoint : TPoint; FFileList : TStringList; public constructor Create (ANumFiles : UINT); destructor Destroy; override; property NumFiles : UINT read FNumFiles; property InClientArea : Boolean read FInClientArea; property DropPoint : TPoint read FDropPoint; property Files : TStringList read FFileList; end;

Помимо структуры TDragDrop, в модуле FMDD определены три функции: AcceptDroppedFiles, UnacceptDroppedFiles и GetDroppedFiles. Две первые инкапсулируют функцию DragAcceptFiles, а третья вызывается при получении сообщения WM_DROPFILES и возвращает объект TDragDropInfo. В листинге 3.3 содержится первая версия модуля, FMDD1.PAS.

Листинг 3.3. Первая версия модуля FMDD, инкапсулирующего
интерфейс перетаскивания

{

FMDD1.PAS — Первая версия модуля, инкапсулирующего перетаскивание

файлов из File Manager

Автор: Джим Мишель



Дата последней редакции: 27/04/97

} unit fmdd1; interface uses Windows, Classes; type TDragDropInfo = class (TObject) private FNumFiles : UINT; FInClientArea : Boolean; FDropPoint : TPoint; FFileList : TStringList; public constructor Create (ANumFiles : UINT); destructor Destroy; override; property NumFiles : UINT read FNumFiles; property InClientArea : Boolean read FInClientArea; property DropPoint : TPoint read FDropPoint; property Files : TStringList read FFileList; end; function GetDroppedFiles (hDrop : THandle) : TDragDropInfo; procedure AcceptDroppedFiles (Handle : HWND); procedure UnacceptDroppedFiles (Handle : HWND); implementation uses ShellAPI; constructor TDragDropInfo.Create (ANumFiles : UINT); begin inherited Create; FNumFiles := ANumFiles; FFileList := TStringList.Create; end; destructor TDragDropInfo.Destroy; begin FFileList.Free; inherited Destroy; end; function GetDroppedFiles (hDrop : THandle) : TDragDropInfo; var DragDropInfo : TDragDropInfo; TotalNumberOfFiles, nFileLength : Integer; pszFileName : PChar; i : Integer; begin { hDrop - логический номер внутренней структуры данных Windows с информацией о перетаскиваемых файлах. } { Определяем общее количество брошенных файлов, передавая функции DragQueryFile индексный параметр -1 } TotalNumberOfFiles := DragQueryFile (hDrop , $FFFFFFFF, Nil, 0); DragDropInfo := TDragDropInfo.Create (TotalNumberOfFiles); { Проверяем, были ли файлы брошены в клиентской области } DragDropInfo.FInClientArea := DragQueryPoint (hDrop, DragDropInfo.FDropPoint); for i := 0 to TotalNumberOfFiles - 1 do begin { Определяем длину имени файла, сообщая DragQueryFile о том, какой файл нас интересует ( i ) и передавая Nil вместо длины буфера. Возвращаемое значение равно длине имени файла. } nFileLength := DragQueryFile (hDrop, i , Nil, 0) + 1; GetMem (pszFileName, nFileLength); { Копируем имя файла — сообщаем DragQueryFile о том, какой файл нас интересует ( i ), и передаем длину буфера. ЗАМЕЧАНИЕ: Проследите за тем, чтобы размер буфера на 1 байт превышал длину имени, чтобы выделить место для завершающего строку нулевого символа! } DragQueryFile (hDrop , i, pszFileName, nFileLength); { Заносим файл в список } DragDropInfo.FFileList.Add (pszFileName); { Освобождаем выделенную память... } FreeMem (pszFileName, nFileLength); end; { Вызываем DragFinish, чтобы освободить память, выделенную Shell для данного логического номера. ЗАМЕЧАНИЕ: Об этом шаге нередко забывают, в результате возникает утечка памяти, а программа начинает медленнее работать. } DragFinish (hDrop); Result := DragDropInfo; end; procedure AcceptDroppedFiles (Handle : HWND); begin DragAcceptFiles (Handle, True); end; procedure UnacceptDroppedFiles (Handle : HWND); begin DragAcceptFiles (Handle, False); end; end.

Чтобы старая тестовая программа работала с новым интерфейсом, в нее придется внести ряд изменений. Во-первых, замените ссылку на модуль ShellAPI в секции uses ссылкой на FMDD1. Затем исправьте обработ чики событий формы в соответствии с листингом 3.4. Обновленная версия программы содержится в файлах DRAG2.DPR и DRAGFRM2.PAS на прилагаемом компакт-диске.

Листинг 3.4. Использование нового интерфейса для перетаскивания
файлов из File Manager

procedure TForm1.FormCreate(Sender: TObject); begin Application.OnMessage := AppMessage; FMDD1.AcceptDroppedFiles (Handle); end; procedure TForm1.WMDropFiles (hDrop : THandle; hWindow : HWnd); var DragDropInfo : TDragDropInfo; i : Integer; begin DragDropInfo := FMDD1.GetDroppedFiles (hDrop); { Проверяем, были ли файлы брошены в клиентской области } if DragDropInfo.InClientArea then Label2.Caption := "In client area" else Label2.Caption := "Not in client area"; { Заносим все файлы в список } for i := 0 to DragDropInfo.NumFiles - 1 do begin Listbox1.Items.Add (DragDropInfo.Files[i]); end; { Уничтожаем объект DragDropInfo } DragDropInfo.Free; end; procedure TForm1.FormClose (Sender: TObject; var Action: TCloseAction); begin { Прекращаем прием файлов } FMDD1.UnacceptDroppedFiles (Handle); end;

По-моему, новым интерфейсом пользоваться намного проще. В полном соответствии с духом Delphi мы убрали код для работы с Windows API из приложения и вынесли его с глаз долой в отдельный модуль. Модуль FMDD копается во внутренностях Windows и достает оттуда нужный объект, с которым мы умеем работать. В результате код получается компактным и понятным, более простым в написании и сопровождении.



Содержание раздела