(Ebook - Pdf) Kick Ass Delphi Programming
.pdfTo 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.