(Ebook - Pdf) Kick Ass Delphi Programming
.pdfProducts | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.
To access the contents, click the chapter and section titles.
Kick Ass Delphi Programming
Go!
Keyword
(Publisher: The Coriolis Group)
Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin
ISBN: 1576100448
Publication Date: 09/01/96
Search this book:
Go!
-----------
Drag1, the first draft of a program that implements FMDD, is shown in Listings 2.1 and 2.2. A screen shot of the completed program is shown in Figure 2.1.
Listing 2.1 DRAG1.DPR
program drag1;
uses Forms,
dragfrm1 in 'dragfrm1.pas' {Form1};
{$R *.RES}
begin
Application.CreateForm(TForm1, Form1); Application.Run;
end.
Listing 2.2 DRAGFRM1.PAS
unit dragfrm1;
interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,
{
ShellAPI defines the drag and drop functions. The functions are implemented in 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;
{
Call DragAcceptFiles to tell the drag and drop manager that you're accepting files.
}
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 is a Handle to the internal Windows data
structure which has information about the dropped files.
}
{
Determine if the files were dropped in the client area
}
InClientArea := DragQueryPoint (hDrop, pPoint); if InClientArea then
Label2.Caption := 'In client area' else
Label2.Caption := 'Not in client area';
{
Find out total number of files dropped by passing -1 for the index parameter to DragQueryFile
}
TotalNumberOfFiles := DragQueryFile (hDrop , $FFFFFFFF, Nil, 0);
for i := 0 to TotalNumberOfFiles - 1 do begin
{
Get the length of a filename by telling DragQueryFile which file your querying about ( i ), and passing Nil
for the buffer parameter. The return value is the length of the file name.
}
nFileLength := DragQueryFile (hDrop, i , Nil, 0) + 1; GetMem (pszFileName, nFileLength);
{
Copy a file name. |
Tell |
DragQueryFile the file |
you're interested in (i) |
and the length of your buffer. |
|
NOTE: Make sure that the |
length is 1 more than the filename |
|
to make room for the nul |
character! |
}
DragQueryFile (hDrop , i, pszFileName, nFileLength);
Listbox1.Items.Add (StrPas (pszFileName));
{ free the allocated memory... } FreeMem (pszFileName, nFileLength);
end;
{
Call DragFinish to release the memory that Shell allocated for this handle.
NOTE: This is a real easy step to forget and could explain memory leaks and incorrect program performance.
}
DragFinish (hDrop); end;
{
AppMessage captures application messages. Assign this method to the Application.OnMessage property in FormCreate.
}
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean); begin
case Msg.Message of
WM_DROPFILES : WMDropFiles (Msg.wParam, Msg.hWnd); end;
end;
procedure TForm1.FormClose (Sender: TObject; var Action: TCloseAction); begin
{ Don't accept files anymore } DragAcceptFiles (Handle, False);
end;
procedure TForm1.Button1Click(Sender: TObject); begin
Listbox1.Clear;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin Close;
end;
end.
FIGURE 2.1 The completed Drag1 program.
The only real trick in DRAGFRM1.PAS is the line in TForm1.FormCreate that reads:
Application.OnMessage := AppMessage;
This line of code tells Delphi to pass Windows messages on to the TForm1.AppMessage procedure. This is the Delphi way of performing traditional handling of Windows messages. We have to do it this way because neither Delphi’s TControl class, nor any of its descendents (like TForm), know anything about WM_DROPFILES, so that message isn’t wrapped up in a nice Delphi event like OnDropFiles. More’s the pity.
There’s nothing especially wrong with the code in Listing 2.2. It works, which is the most important thing, but it’s large, it has lots of places where you can make mistakes, and (perhaps worst of all) it’s ugly. All that icky Windows code in the middle of a pure Delphi program just ain’t right! (Texans have such colorful ways of mangling the language.)
There’s another problem, too, but it’s caused by Delphi’s message handling machinery. Suppose you have two forms that want to respond to WM_DROPFILES messages. If each form assigns its own message handler to Application’s OnMessage event, only the second form will get messages. The first form’s message handler is overwritten by the second. There are a number of ways around this problem, and we’ll discuss some of them after we’ve taken care of that ugly Windows code.
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.
Go!
Keyword
To access the contents, click the chapter and section titles.
Kick Ass Delphi Programming
(Publisher: The Coriolis Group)
Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin
ISBN: 1576100448
Publication Date: 09/01/96
Search this book:
Go!
-----------
What to Do with Windows Code?
The answer to the question is “encapsulate it.” That’s what Delphi does, and does very well. The whole point of Delphi is to insulate you from the sordid little details of Windows programming so that you can concentrate on the important parts of your application. We’ll do the same with FMDD—wrap it up into a pretty little Delphi unit called FMDD.
Rather than have the form handle all of the details of WM_DROPFILES processing, we’ll define a function in the FMDD unit that the form’s OnMessage handler can call to retrieve an object that contains all available information about the drag and drop event. This object will include everything that we obtained from the Windows FMDD interface, but it will be all packaged together in a single, more manageable structure that looks like the following:
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;
In addition to the TDragDrop structure, the FMDD unit defines three functions: AcceptDroppedFiles,
UnacceptDroppedFiles, and GetDroppedFiles. The first two simply encapsulate the DragAcceptFiles function. The third, GetDroppedFiles, is called in response to the WM_DROPFILES message and returns a TDropInfo object. The code for the FMDD unit is shown in Listing 2.3.
Listing 2.3 The FMDD unit that encapsulates the Drag and Drop interface
{
FMDD.PAS -- File Mangler Drag and Drop
}
unit fmdd;
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;
procedure AcceptDroppedFiles (Handle : HWND); begin
DragAcceptFiles (Handle, True); end;
procedure UnacceptDroppedFiles (Handle : HWND); begin
DragAcceptFiles (Handle, False); end;
function GetDroppedFiles (hDrop : THandle) : TDragDropInfo; var
DragDropInfo : TDragDropInfo;
TotalNumberOfFiles, nFileLength : Integer; pszFileName : PChar;
i : Integer;
begin
{
hDrop is a Handle to the internal Windows data
structure which has information about the dropped files.
}
{
Find out total number of files dropped by passing -1 for the index parameter to DragQueryFile
}
TotalNumberOfFiles := DragQueryFile (hDrop , $FFFFFFFF, Nil, 0);
DragDropInfo := TDragDropInfo.Create (TotalNumberOfFiles);
{
Determine if the files were dropped in the client area
}
DragDropInfo.FInClientArea :=
DragQueryPoint (hDrop, DragDropInfo.FDropPoint);
for i := 0 to TotalNumberOfFiles - 1 do begin
{
Get the length of a filename by telling DragQueryFile which file your querying about ( i ), and passing Nil
for the buffer parameter. The return value is the length of the file name.
}
nFileLength := DragQueryFile (hDrop, i , Nil, 0) + 1; GetMem (pszFileName, nFileLength);
{
Copy a file name. |
Tell |
DragQueryFile the file |
you're interested in (i) |
and the length of your buffer. |
|
NOTE: Make sure that the |
length is 1 more than the filename |
|
to make room for the nul |
character! |
}
DragQueryFile (hDrop , i, pszFileName, nFileLength);
{Add the file to the string list } DragDropInfo.FFileList.Add (pszFileName);
{free the allocated memory... }
FreeMem (pszFileName, nFileLength); end;
{
Call DragFinish to release the memory that Shell allocated for this handle.
NOTE: This is a real easy step to forget and could explain memory leaks and incorrect program performance.
}
DragFinish (hDrop); Result := DragDropInfo;
end;
end.
In order to make the existing test program use the new interface, we just have to change a few lines of code. First, change the reference to unit ShellApi in the unit’s uses statement to FMDD. Then, change the form’s event handlers as shown in Listing 2.4.
Listing 2.4 Using the new File Manager Drag and Drop interface
procedure TForm1.FormCreate(Sender: TObject); begin
Application.OnMessage := AppMessage; FMDD.AcceptDroppedFiles (Handle);
end;
procedure TForm1.WMDropFiles (hDrop : THandle; hWindow : HWnd); var
DragDropInfo : TDragDropInfo; i : Integer;
begin
DragDropInfo := FMDD.GetDroppedFiles (hDrop);
{Determine if in client area } if DragDropInfo.InClientArea then
Label2.Caption := 'In client area' else
Label2.Caption := 'Not in client area';
{add each file to the list box }
for i := 0 to DragDropInfo.NumFiles - 1 do begin Listbox1.Items.Add (DragDropInfo.Files[i]);
end;
{ Destroy the DragDropInfo object } DragDropInfo.Free;
end;
procedure TForm1.FormClose (Sender: TObject; var Action: TCloseAction); begin
{ Don't accept files anymore } FMDD.UnacceptDroppedFiles (Handle);
end;
I don’t know about you, but I find it much easier to use this new interface. In the spirit of Delphi, we’ve removed the Windows handling code from our application’s code, and we’ve put it into a unit where we don’t have to look at it or worry about it. The FMDD unit handles all the mucking around in Windows internals and returns an object—something that we know how to handle. The result is less (and much cleaner) code that’s easier to write and maintain.
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.