Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

(Ebook - Pdf) Kick Ass Delphi Programming

.pdf
Скачиваний:
284
Добавлен:
17.08.2013
Размер:
5.02 Mб
Скачать

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!

-----------

The TFilterFile class shown in Listing 1.8 is designed to give filter programs quick byte-by-byte access to files. It encapsulates all of the buffering and, as much as possible, relieves the programmer from having to remember the mundane details of file handling (although you do still have to Open and Close your files).

Listing 1.8 The TFilterFile class implemented in FILEIO.PAS

{

FILEIO.PAS -- File input/output for filter programs

}

{$I+} { use exceptions for error handling } unit fileio;

interface

type

FileIOMode = (fioNotOpen, fioRead, fioWrite);

BuffArray = array[0..1] of byte; pBuffArray = ^BuffArray;

TFilterFile = class (TObject) private

FFilename : String; F : File;

FBufferSize : Integer;

FBuffer : pBuffArray;

FBytesInBuff : Integer;

FBuffIndx : Integer;

FFileMode : FileIOMode;

function ReadBuffer : boolean; function WriteBuffer : boolean;

public

constructor Create (AName : String; ABufSize : Integer); destructor Destroy; override;

function Open (AMode : FileIOMode) : Boolean; procedure Close;

function Eof : Boolean;

function GetByte : byte;

function PutByte (b : byte) : boolean; end;

implementation

{TFilterFile }

{Create -- sets up but doesn't actually open the file } constructor TFilterFile.Create

(

AName : String; ABufSize : Integer

);

begin

inherited Create; FFilename := AName; FBufferSize := ABufSize; FBytesInBuff := 0; FBuffIndx := 0; FFileMode := fioNotOpen;

{Assign but don't open } Assign (F, FFilename);

{allocate memory for buffer } GetMem (FBuffer, FBufferSize);

end;

{ Destroy -- closes the file (if open) and destroys the object } destructor TFilterFile.Destroy;

begin

{if the file's open, close it } if (FFileMode <> fioNotOpen) then

Self.Close;

{if the buffer's been allocated, free it } if (FBuffer <> Nil) then begin

FreeMem (FBuffer, FBufferSize); FBuffer := Nil;

end;

inherited Destroy; end;

{ Open -- open the file in the proper mode }

function TFilterFile.Open

(

AMode : FileIOMode ) : Boolean;

var

SaveFileMode : Byte; begin

Result := True;

SaveFileMode := FileMode; { FileMode defined in system unit }

{ try to open the file } try

case AMode of fioRead : begin

FileMode := 0;

Reset (F, 1); end;

fioWrite : begin FileMode := 1; Rewrite (F, 1);

end;

end;

FFileMode := AMode; except

Result := False; end;

FBytesInBuff := 0;

FBuffIndx := 0;

FileMode := SaveFileMode; end;

{ Close -- close the file, flushing the buffer if needed } procedure TFilterFile.Close;

begin

{ if the write buffer has stuff in it, write it out } if ((FFileMode = fioWrite) and

(FBytesInBuff > 0)) then begin WriteBuffer;

end;

try

{ close the file } System.Close (F);

finally

FFileMode := fioNotOpen; end;

end;

{ ReadBuffer -- read a block from the file into the buffer } function TFilterFile.ReadBuffer : Boolean;

begin

Result := True;

if (Self.Eof) then Result := False

else begin try

BlockRead (F, FBuffer^, FBufferSize, FBytesInBuff); except

Result := False; end;

end;

end;

{ GetByte -- return next byte in file. Read buffer if necessary } function TFilterFile.GetByte : byte;

begin

if (FBuffIndx >= FBytesInBuff) then begin if (not ReadBuffer) then begin

Result := 0; Exit;

end else

FBuffIndx := 0;

end;

Result := FBuffer^[FBuffIndx]; Inc (FBuffIndx);

end;

{WriteBuffer -- write block from buffer to file } function TFilterFile.WriteBuffer : Boolean;

begin

Result := True; try

BlockWrite (F, FBuffer^, FBytesInBuff); except

Result := False; end;

if (Result = True) then FBytesInBuff := 0;

end;

{PutByte -- put byte into buffer. Write to file if necessary } function TFilterFile.PutByte (b : byte) : Boolean;

begin

if (FBytesInBuff = FBufferSize) then begin if (not WriteBuffer) then begin

Result := False; Exit;

end

else begin FBytesInBuff := 0;

end;

end;

FBuffer^[FBytesInBuff] := b; Inc (FBytesInBuff);

Result := True; end;

{ Eof -- return True if at end of input file } function TFilterFile.Eof : Boolean;

begin

Result := (FBuffIndx >= FBytesInBuff); if Result then begin

try

Result := System.Eof (F); except

Result := True; end;

end;

end;

end.

Because TFilterFile handles most of the details, using it in place of standard text file I/O is very simple. The performance, though, isn’t similar at all. The new DoFilter procedure shown in Listing 1.9 uses TFilterFile for input and output. The resulting program is much faster than the original. The beauty is that the program isn’t any harder to read or understand than the slower version.

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.

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!

-----------

Listing 1.9 Using TFilterFile in place of standard I/O

procedure DoFilter;

implementation

uses CmdLine, FileIO;

procedure DoFilter;

const

nOptions = 2;

Options : Array [1..nOptions] of OptionRec = ( (OptionChar : 'i'; Option : otFilename; Filename : ''), (OptionChar : 'o'; Option : otFilename; Filename : '')

);

BigBufferSize = 65536;

var

cRslt : Boolean; iRec : pOptionRec; oRec : pOptionRec;

InputFile : TFilterFile; OutputFile : TFilterFile; c : char;

begin

cRslt := CmdLine.ProcessCommandLine (@Options, nOptions); if (not cRslt) then

Halt;

{ make sure input and output files were specified }

iRec := CmdLine.GetOptionRec (@Options, nOptions, 'i'); if (iRec^.Filename = '') then begin

WriteLn ('Error: input file expected'); Halt;

end;

oRec := CmdLine.GetOptionRec (@Options, nOptions, 'o'); if (oRec^.Filename = '') then begin

WriteLn ('Error: output file expected'); Halt;

end;

{ Create and open the input file }

InputFile := TFilterFile.Create (iRec.Filename, BigBufferSize); if (not InputFile.Open (fioRead)) then begin

WriteLn ('Error opening input file'); Halt;

end;

{ Create and open the output file }

OutputFile := TFilterFile.Create (oRec.Filename, BigBufferSize); if (not OutputFile.Open (fioWrite)) then begin

WriteLn ('Error opening output file'); Halt;

end;

{ process and each character } while (not InputFile.Eof) do begin

c := char (InputFile.GetByte); c := UpCase (c);

if (not OutputFile.PutByte (byte (c))) then begin WriteLn ('Write error');

Halt;

end;

end;

InputFile.Close;

InputFile.Free;

OutputFile.Close;

OutputFile.Free;

end;

Using the Filter Template

If you want to add the filter to the Repository, create a new directory under the ObjRepos directory, and save FILTER.DPR, FILTMAIN.PAS, CMDLINE.PAS, and FILEIO.PAS in that directory. Then just select Project|Add to Repository, and fill in the prompts. Next time you have to write a filter, all the tedious stuff is done. Just grab the template, fix up the options and change the processing loop.

A Critique

Have you ever had the urge, after you’ve finished a project, to turn around and do it right? Not that

there’s anything wrong with this filter template, but now that it’s finished, I look back and see where I could have done things differently.

In all, I’m pretty happy with the way it turned out, and I’ve used it to create some very useful programs, ranging in complexity from the one-shot quickie to a very useful stream editor.

The command line parsing is restrictive in its format because all parameters must be option-based, and it doesn’t allow response (or configuration) files. Requiring options for all parameters isn’t too much of a hassle, and it makes the code a lot easier. Adding support for response files would be very useful, and with the current design, shouldn’t be terribly difficult. The only other thing I’d change (and mostly from a cosmetic point of view) is the use of ShortString in string and file name parameter types. PString or maybe PChar would be a more efficient choice.

TFilterFile is another story. This class implements the bare minimum required for filter I/O. You probably noticed that it doesn’t have a block read or write mechanism, and it doesn’t allow random file I/O. Many filter programs require one or both of those features. Block operations are fairly simple to add using an untyped var parameter and a byte count, in much the same way that the standard BlockRead and BlockWrite procedures work. These procedures would have to block copy bytes between the user’s data structure and the object’s buffer, and be sure to handle reading from and writing to the file as required.

I used methods for the GetByte and PutByte operations, rather than using properties. With some modest changes to TFilterFile, I could have defined these two properties

property InByte : byte read GetByte; property OutByte : byte write PutByte;

and made Eof a property too, rather than a method. I guess I’m still stuck in the past. I can see the advantage of properties in many situations, but this isn’t one of them. I see some advantages, but not enough to make me switch.

I’m unhappy with having to cast the return value of GetByte to a char in my program. I could have easily defined GetChar and PutChar methods in TFilterFile, but dang it, a character is a byte and I should be able to treat it that way. Here’s one case where C gets it right and Object Pascal is too restrictive. It’s rare, but it happens. The typecast is okay, I guess, but I normally avoid them because, in general, typecasts are considered “bad programming practice.” You’re telling the compiler: “Yes, I know I’m breaking the rules. Shut up and do it anyway.” Second guessing the compiler is not a habit I’d like to get into.

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.

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!

-----------

CHAPTER 2

Drag and Drop the Windows Way

JIM MISCHEL

How File Manager Drag and Drop (FMDD) Works

Windows Messaging Under Delphi

Encapsulating Drag And Drop Information In An Object

Adding Drag And Drop Functionality To VCL Components

Windows Subclassing

You’d think dragging and dropping your way around the Windows UI would be easier—but they had to leave something around for KickAss programmers to chew on, right?

There are at least three different drag and drop interfaces that can be

supported by Delphi programs. The TControl class, of which all Delphi controls are descendents, defines a drag and drop interface between controls. By writing event handlers for the OnDragDrop, OnDragOver, and other similar events, a Delphi program supports internal drag and drop operations. With some fancy footwork and shared memory, it’s possible that this method could be extended to work between two Delphi programs, but it can’t be used to support drag and drop between a Delphi application and a non-Delphi program. This interface is well covered in Delphi’s documentation and example programs.

Windows’ Object Linking and Embedding (OLE) API also defines a drag and drop interface, which Delphi programs can support with the use of the built-in OLE controls. These controls will allow you to build OLE client or OLE

server applications that support full drag and drop with OLE objects. Again, Delphi’s documentation and example programs provide a good explanation of this topic.

The third kind of drag and drop supported by Delphi is the dragging and dropping of files from (as I like to put it) File Mangler (Windows NT 3.5) or Windows Exploder (Windows 95 and NT 4.0). This interface is minimal—the only things you can drag and drop are files—but it’s suprisingly useful. This interface, which I’ll call “File Manager Drag and Drop” (FMDD), is not covered at all in Delphi’s documentation, and is the subject of this chapter.

Drag and Drop the Windows Way

FMDD is implemented in Windows through the Shell interface in SHELL32.DLL. The implementation consists of four Windows API functions:

DragAcceptFiles, DragQueryFile, DragQueryPoint, and DragFinish; and one Windows message: WM_DROPFILES. In Delphi, the WM_DROPFILES message is defined in the Messages unit, and the API functions are declared in the ShellAPI unit. The documented interface supports FMDD clients, but not FMDD servers. Your programs can accept dropped files from File Manager, but they can’t send files to another program.

A typical implementation of FMDD in a Windows program requires that your code perform the following steps:

1.At program startup, call DragAcceptFiles, passing it a window handle and a True flag to enable acceptance of dragged files by that window.

2.When the window receives a WM_DROPFILES Windows message, perform the following (the Msg.wParam field in the Object Pascal message structure is a handle to memory used by the

WM_DROPFILES message):

a.Call DragQueryPoint to determine if the drop occurred in the client area of the window.

b.Call DragQueryFile with an index value of $FFFFFFFF to retrieve the number of files being dropped.

c.For each file, call DragQueryFile to copy the file name to an internal buffer.

d.Perform the desired action on each file name.

e.Free all internal memory allocated during processing of the dropped files.

f.Call DragFinish to free the memory allocated by the FMDD server (i.e. File Mangler).

3.At program shutdown, call DragAcceptFiles, passing it a window handle and a False flag to discontinue acceptance of dragged files by the window.