|
Contents
Components I have developed.
Tools for Delphi Programmers.
Patches to the VCL code.
|
|
Borland Delphi is a fine product. Someone at Borland really had their
thinking cap on when they designed the Object Pascal language and the
Integrated Development Environment. I can find nothing wrong with these
parts of the product. The Visual Component Library (VCL)
is adequate but does contain a number of design flaws, limitations,
and bugs. I estimate that at least 75% of the over 300 hours of time
I devoted to developing
Columbine Bookmark Merge
was time spent debugging
the VCL. The purpose of this page is to convey some of the improvements
I've developed and collected for the VCL. Hopefully, this will save other programmers
much time. This page applies to Delphi 1.02 (16-bit)
only. Some of the material below may apply to the 32-bit version as
well, but I don't normally program with Delphi 2.
You can contact Gary Cramblitt
via e-mail at garyc@clark.net
I'd like to hear from you.
Components I have developed
License to freely use these components is hereby granted.
- WwwDde.PAS -
A component for using DDE to control the NetScape or Microsoft Internet
Explorer web browsers. See the source code for important notes about
which versions of the web browsers it work's with. This unit also
fixes a defect in the Borland TDdeClientConv.RequestData method.
This
component was improved 3/2/97 to prevent hanging in the CaptureBrowserDoc
function.
- NETBIOS.PAS -
A unit for communicating over networks using the NetBIOS protocol. Here's
some sample code that uses this unit to obtain the MAC (hardware)
ethernet address of a network interface card:
function GetNICMACAddress: string;
{ Uses the NetBIOS Adapter_Status function to retrieve
the network interface card's permanent station
(MAC) address. }
var
NCB: NetworkControlBlock;
AdapterStatus: Adapter_Status_Buffer;
J: integer;
begin
Result := '';
SetNCBDefaults(NCB);
NCB.Num := 1; { physical card }
NCB.Cmd := Cmd_Adapter_Status;
NCB.Buffer := Addr(AdapterStatus);
NCB.Len := SizeOf(AdapterStatus);
SetAdapterName(NCB.CallName,'* ',0);
CallNetBIOS(NCB);
if NCB.RetCode = RetCode_Success then
Result := Result +
HexByte(AdapterStatus.Ethernet_Address[1]) +
HexByte(AdapterStatus.Ethernet_Address[2]) +
'.' +
HexByte(AdapterStatus.Ethernet_Address[3]) +
HexByte(AdapterStatus.Ethernet_Address[4]) +
'.' +
HexByte(AdapterStatus.Ethernet_Address[5]) +
HexByte(AdapterStatus.Ethernet_Address[6]);
if Result = '' then Result := 'NOTAVAIL';
end;
- BIOS.PAS -
A unit for accessing memory in the BIOS of your machine. For example,
you can extract the BIOS DATE using this code. This code ONLY
works for 16-bit operating systems; not Win95 or WinNT. Contains
highly machine-specific constants, but the technique should work on
any machine.
ResWatch.PAS -
A component that automatically watches Windows resources
(GDI, User, and global memory). When resources
drop to preset thresholds, warns user to save their work. Only works
under 16-bit Windows. Just drop this component on your main form, set the
threshold properties and you're done. If someone can provide sample
code for measuring Windows Dos memory, we'd be grateful.
Coming Soon -- F32Ctrl.PAS -- a set of components and utility routines
that permit Delphi 16-bit programs to support long file and directory
names when running under Windows 95 or Windows NT. Includes the
Explorer-like file browsing component featured in
Columbine Bookmark Merge.
Invaluable Tools for Delphi Programmers
- WinMiser Pro -
This amazing program will detect and automatically fix memory and resource
leaks in any Windows 3.1 or 3.11 program. It should be part of every
programmer's workbench. All of the resource leak patches listed below
were discovered with it.
Bug Fixes - Patches to the Delphi 1.02 VCL
Warning: Borland never distributed the source code for 1.02. The
latest code they distributed was for 1.01. Applying patches to this
source code can introduce bugs that were fixed in 1.02. Caveat Emptor.
One way to get around this is to obtain the source code for Delphi 2 and
incorporate any fixes from it as well as those listed below. But you
should be very careful and selective when doing this.
- OUTLINE.PAS - This is one of the more
buggy VCL components. This file contains the following fixes:
- Numerous fixes extracted from the Delphi 2 source code.
OwnerDraw fixes per Craig Ward
with modifications by Gary Cramblitt. Here's Craig's info:
I've heard that there is a bug in TOutline Component's OnDrawItem Event for OwnerDraw TOulines.
so I made an effort to fix it ! and here it is all for you !
NOW you can create ownerdraw outlines as many as you like ! (I hope)
i also made few improvements:
new properties :
- TopRow - just like grid toprow property - controls the top displayed row.
- VisibleRowCount - again just like grid , counts the visible items on screen.
- TopItem : longint - controls the top shown item !
diferent from toprow by the fact that it takes care of invisble
items by the fact that setting it to an unvisible item will cause
the outline to show on top the first visible parent.
- GRIDS.PAS -
Patches as documented by
Stefan Hoffmeister,
fixes problem in drawing the outline when the outline is resized.
Here's Stefan's fix:
implementation
uses WinProcs, Consts;
{$IFDEF FixHorzScrollbarBug}
const
Unit_GlobalBool_PatchFocus : boolean = false;
{$ENDIF}
procedure TCustomGrid.ModifyScrollBar(ScrollBar, ScrollCode, Pos: Word);
{ LOCAL PROCEDURE CUT OUT }
begin
if CanFocus and TabStop and not (csDesigning in ComponentState)
{$IFDEF FixHorzScrollbarBug} and not Unit_GlobalBool_PatchFocus {$ENDIF} then
SetFocus;
{$IFDEF FixHorzScrollbarBug}
Unit_GlobalBool_PatchFocus := false;
{$ENDIF}
if (ScrollBar = SB_HORZ) and (ColCount = 1) then
begin
ModifyPixelScrollBar(ScrollCode, Pos);
Exit;
end;
.........
end;
procedure TCustomGrid.UpdateScrollPos;
var
DrawInfo: TGridDrawInfo;
MaxTopLeft: TGridCoord;
procedure SetScroll(Code: Word; Value: Integer);
var
NewOffset,
OldOffset : integer;
R: TGridRect;
begin
if GetScrollPos(Handle, Code) <> Value then
begin
SetScrollPos(Handle, Code, Value, True);
{$IFDEF FixHorzScrollbarBug}
if IsWindowVisible(Handle) then
begin
if Value < 0 then
Value := 0;
Unit_GlobalBool_PatchFocus := true;
{
this global variable is needed to prevent "SetFocus" from being
called in the course of "ModifyScrollBar", see above
It is initalized const := false and only set here to true.
As soon as the "Unit_GlobalBool_PatchFocus" has been queried and "SetFocus"
has been passed it is immediately set back to := false
It would have been MUCH nicer if, instead of using "Unit_GlobalBool_PatchFocus",
one had used a private class variable, e.g. FPatchFocus (which would actually
have worked as well), but then the object would have changed in the INTERFACE
part and thus all dependant units would have to be recompiled.
Thus in favour of a plug-and-play replacement solution the rather unelegant way
was chosen.
If you don't like it - just move the global variable to the private section,
it automatically gets initialized with the correct value (=false)
}
ModifyScrollBar(Code, SB_THUMBPOSITION, Value);
end;
{$ENDIF}
- DBGRIDS.PAS -
There is a font leak in Delphi 1.02, unit DBGRIDS.PAS. Here's the fix if
you have the VCL code. The bug is in the TCustomDBGrid object and affects
all components that inherit from it, including TDBLookupCombo,
TDBLookupList, and TDBGrid.
destructor TCustomDBGrid.Destroy;
begin
FDataLink.Free;
FDataLink := nil;
FIndicators.Free;
{ 12/22/96: Fix GDI (font) leak. grc }
FTitleFont.Free; { Add this line for the fix. }
inherited Destroy;
ReleaseBitmap;
end;
- DBLOOKUP.PAS -
There is a GDI resource leak in Delphi 1.02 DBLOOKUP.PAS. The leak only
occurs if the style is csDropDown. If you have
the VCL source code, here is the fix.
destructor TDBLookupCombo.Destroy;
begin
FFieldLink.OnDataChange := nil;
FFieldLink.Free;
FFieldLink := nil;
{ Following line plugs a GDI resource leak. Gary Cramblitt. }
FCanvas.Free;
inherited Destroy;
end;
- MENUS.PAS -
There is a menu resource leak in MENUS.PAS. Borland's code looks like this:
destructor TMenuItem.Destroy;
begin
if FParent <> nil then
begin
FParent.Remove(Self);
FParent := nil;
end;
if FHandle <> 0 then
begin
MergeWith(nil);
DestroyMenu(FHandle);
ClearHandles;
end;
while Count > 0 do Items[0].Free;
FItems.Free;
DisposeStr(FCaption);
if FCommand <> 0 then CommandPool[FCommand] := False;
inherited Destroy;
end;
procedure TMenuItem.ClearHandles;
procedure Clear(Item: TMenuItem);
var
I: Integer;
begin
with Item do
begin
FHandle := 0;
for I := 0 to GetCount - 1 do Clear(FItems[I]);
end;
end;
begin
Clear(Self);
end;
The fix for the Menu resource leak is to remove the ClearHandles method call from Destroy. What I don't understand now is why did Borland even write that code? The
while Count > 0 do Items[0].Free;
loop that follows the ClearHandles call will destroy all the Menu objects that ClearHandles is clearing. So why do that? Even stranger, if I move the ClearHandles to below the while loop, the leak is not fixed, i.e., like this
if FHandle <> 0 then
begin
MergeWith(nil);
DestroyMenu(FHandle);
end;
while Count > 0 do Items[0].Free;
ClearHandles;
What is ClearHandles doing? Does it have something to do with menu merging?
- GRAPHICS.PAS
There is a bug in Delphi 1.0 that causes a memory leak when
using the TImage component with bitmaps that have a size which is a
multiple of 64KB (e.g 768 X 512 with 256 colors). Delphi 2.0 does not
have the same bug. The solution was provided by
Ray Lischner of Tempest Software, the
Author of Secrets of Delphi 2
Edit Graphics.pas. Look
for all the uses of MemAlloc. Change the FreeMem calls that free the
MemAlloc-ed memory to use MemFree:
procedure MemFree(Ptr: Pointer; Size: LongInt);
begin
if Size > $FFFF then
GlobalFreePtr(Ptr)
else
FreeMem(Ptr, Word(Size))
end;
- DDEMAN.PAS -
Fix to TDdeServerItem.CopyToClipboard per
Brad Choate of Choate Consulting,.
procedure TDdeServerItem.CopyToClipboard;
var
Data: THandle;
LinkData: string;
DataPtr: Pointer;
begin
if FServerConv <> nil then
LinkData := ddeMgr.AppName + #0 + FServerConv.Name + #0 + Name
else if (Owner =nil) then
Exit
else if Owner is TForm then
LinkData := ddeMgr.AppName + #0 + TForm(Owner).Caption + #0 + Name;
try
Clipboard.Open; { 12/28/96. Fix per Brad Choate. grc }
Clipboard.AsText := Text;
Data := GlobalAlloc(GMEM_MOVEABLE, Length(LinkData) + 1);
try
DataPtr := GlobalLock(Data);
try
{ 12/28/96 Following line change was suggested by Brad Choate,
but will not compile. ?? grc
Move(PChar(LinkData)^, DataPtr^, Length(LinkData) + 1);
}
StrPCopy(DataPtr, LinkData);
Clipboard.SetAsHandle(DdeMgr.LinkClipFmt, Data);
finally
GlobalUnlock(Data);
end;
except
GlobalFree(Data);
raise;
end;
finally
Clipboard.Close;
end;
end;
Please visit our Columbine
Bookmark Merge page to learn about a free web browser accessory,
written in Delphi 1.02, which you can download.
(c) Copyright 1997 by Gary Cramblitt and Columbine Enterprises.
Last update 3/2/97.
|