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

       

Перетаскивание


В системе Windows FMDD реализуется через интерфейс Shell из библиотеки SHELL32.DLL. При этом используются четыре функции API — DragAcceptFiles, DragQueryFile, DragQueryPoint и DragFinish, а также одно сообщение Windows, WM_DROPFILES. В Delphi сообщение WM_DROPFILES определено в модуле Messages, а функции API — в модуле ShellAPI. Документированный интерфейс относится к клиентам , но не серверам FMDD. Ваша программа сможет принимать файлы, перетаскиваемые из File Manager, но ей не удастся отправить файлы в другую программу.

Типичная реализация FMDD в программе для Windows требует выполнения следующих действий:

  1. При запуске программы вызовите функцию DragAcceptFiles с логическим номером окна и флагом True, чтобы окно могло принимать перетаскивае мые файлы.
  2. При получении окном сообщения WM_DROPFILES выполните следующие действия (поле Msg.wParam в структуре сообщений Object Pascal соответствует логическому номеру области памяти, используемой сообщением WM_DROPFILES):

    a) вызовите функцию DragQueryPoint, чтобы узнать, был ли перетаскивае мый объект брошен в клиентской области окна;

    б) вызовите функцию DragQueryFile с параметром $FFFFFFFF, чтобы определить количество брошенных файлов;

    в) для каждого файла вызовите DragQueryFile, чтобы скопировать его имя во внутренний буфер;

    г) выполните с каждым файлом необходимые действия;

    д) освободите всю внутреннюю память, выделенную при обработке перетаскивания;

    е) вызовите функцию DragFinish, чтобы освободить память, занятую сервером FMDD (то есть File Manager).



  3. При завершении программы вызовите функцию DragAcceptFiles с логическим номером окна и флагом False, чтобы прервать прием файлов окном.

В листингах 3.1 и 3.2 содержится черновой набросок программы, поддерживающей FMDD. На рис. 3.1 показано, как выглядит окно готовой программы.

Рис. 3.1. Готовая программа Drag1

Листинг 3.1. Файл DRAG1.DPR

{

DRAG1.DPR — Первый эксперимент с перетаскиванием

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

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

} program drag1; uses Forms, dragfrm1 in "dragfrm1.pas" {Form1}; {$R *.RES} begin Application.CreateForm(TForm1, Form1); Application.Run; end.

Листинг 3.2. Модуль DRAGFRM1.PAS

{

DRAGFRM1.PAS — Первая реализация перетаскивания

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

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

} unit dragfrm1; interface uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, { Функции перетаскивания определены в ShellAPI. Они реализованы в библиотеке SHELL32.DLL. } ShellAPI; type TForm1 = class(TForm) ListBox1: TListBox; Button1: TButton; Button2: TButton; Label1: TLabel; Label2: TLabel; procedure FormCreate(Sender: TObject); procedure AppMessage(var Msg: TMsg; var Handled: Boolean); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } procedure WMDropFiles (hDrop : THandle; hWindow : HWnd); public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin Application.OnMessage := AppMessage; { Вызываем DragAcceptFiles, чтобы сообщить менеджеру перетаскивания о том, что наша программа собирается принимать файлы. } DragAcceptFiles (Handle, True); end; procedure TForm1.WMDropFiles (hDrop : THandle; hWindow : HWnd); Var TotalNumberOfFiles, nFileLength : Integer; pszFileName : PChar; pPoint : TPoint; i : Integer; InClientArea : Boolean; Begin { hDrop — логический номер внутренней структуры данных Windows с информацией о перетаскиваемых файлах. } { Проверяем, были ли файлы брошены в клиентской области } InClientArea := DragQueryPoint (hDrop, pPoint); if InClientArea then Label2.Caption := "In client area" else Label2.Caption := "Not in client area"; { Определяем общее количество сброшенных файлов, передавая функции DragQueryFile индексный параметр -1 } TotalNumberOfFiles := DragQueryFile (hDrop , $FFFFFFFF, Nil, 0); 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); Listbox1.Items.Add (StrPas (pszFileName)); { Освобождаем выделенную память... } FreeMem (pszFileName, nFileLength); end; { Вызываем DragFinish, чтобы освободить память, выделенную Shell для данного логического номера. ЗАМЕЧАНИЕ: Об этом шаге нередко забывают, в результате возникает утечка памяти, а программа начинает медленнее работать. } DragFinish (hDrop); end; { AppMessage получает сообщения приложения. Этот обработчик следует назначить свойству Application.OnMessage в FormCreate. } procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean); begin case Msg.Message of WM_DROPFILES : begin WMDropFiles (Msg.wParam, Msg.hWnd); Handled := True; end; else Handled := False; end; end; procedure TForm1.FormClose (Sender: TObject; var Action: TCloseAction); begin { Прекращаем прием файлов } DragAcceptFiles (Handle, False); end; procedure TForm1.Button1Click(Sender: TObject); begin Listbox1.Clear; end; procedure TForm1.Button2Click(Sender: TObject); begin Close; end; end.

Во всей программе по-настоящему заслуживает внимания всего одна строка из TForm1.FormCreate:

Application.OnMessage := AppMessage;

Она докладывает программе о том, что сообщения Windows должны передаваться процедуре TForm1.AppMessage. Так в Delphi организуется традиционная обработка сообщений. Нам пришлось это сделать из-за того, что ни класс TControl, ни его потомки (например, TForm) ничего не знают о сообщении WM_DROPFILES, поэтому поступающее сообщение не будет «упаковано» в какое-нибудь приятное событие Delphi типа OnDropFiles. Неприятно, однако ничего не поделаешь.

И все же листинг 3.2 не радует. Конечно, программа работает (а это самое главное), но она получилась большой, чреватой ошибками, а самое главное — уродливо й. Как хотите, но в программе на Delphi весь этот кошмарный код Windows неуместен.

Существует и другая проблема, обусловленная механизмом обработки сообщений Delphi. Предположим, у вас имеются две формы, каждая из которых должна реагировать на сообщение WM_DROPFILES. Если каждая форма назначит событию OnMessage объекта Application свой собственный обработчик, то сообщения будут поступать лишь во вторую форму. Первый обработчик будет попросту перекрыт вторым. Эту проблему можно обойти несколькими способами, и мы рассмотрим некоторые из них после того, как расправимся с уродливым кодом Windows.



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