| Bug # | Delphi versions | Description |
| 1 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Stefan Hoffmeister has written some very useful comments about fixing bugs in Borland supplied code (read: the VCL and RTL units). If you intend to apply some of the bug fixes mentioned on the rest of this page, you should take note of these comments! |
| 2 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
The VCL/RTL sources that come with the Professional and the C/S version do NOT match the compiled DCUs and packages as distributed by Borland. |
| 3 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Another list with VCL bugs that are not mentioned here yet: The Fookes Delphi Bug List Here you will find a list of lesser known bugs that I (Eric Fookes) have come across in Delphi 1 and 2. I have included workarounds to many of them in my component packages. |
| 4 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
The WM_EndSession message is not handled properly by the VCL. |
| 105 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
The procedure ShortCutToKey (in the VCL unit Menus) has a bug. |
| 108 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Application adds icons to Win95 taskbar for each window. |
| 453 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Memory/resource leaks in Delphi 4 VCL |
| 458 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
HintHook resource leak: Handle created by CreateThread not freed by call to CloseHandle |
| 470 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
A Form's WindowState property is not reported correctly in the FormResize event when maximizing/minimizing/restoring. The WindowState property gets updated AFTER the FormResize event. |
| 411 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Drag and Drop TDragObjects created in OnStartDrag are not freed |
| 412 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Drag and Drop If the source of a drag'n'drop operation <> the focused control the complete drag'n'drop system can be killed... |
| 408 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Forms.pas There's a memory loss of 4KB (or more) on every dynamic load of a DLL written in Delphi 3.0x |
| 106 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Printing / Printers Printing will fail after Printer.Abort |
| 107 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Printing / Printers Printing text uses wrong (too small) font size |
| 14 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TApplication Run method does not honor the Run Minimized/Maximized setting. |
| 493 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TClipboard TClipboard.HasFormat is declared as taking a word type parameter, but this should be a 32-bits (unsigned) integer. |
| 29 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TComponent Writing a floating point property value to a stream and then reading it may go wrong. |
| 30 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TComponent String property values of certain lengths will get damaged. |
| 436 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TControl Anchored Controls are not resized if Form's initial WindowState set to wsMaximized |
| 509 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TControl TControl.WM_CancelMode does not handle the Mouse state correctly |
| 31 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TCustomControl Drag & Drop: OnStartDrag event occurs too early |
| 32 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TCustomControl Drag & Drop: OnEndDrag: Target parameter doesn't always get correct value |
| 44 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TFont TFont.Size and TFont.Height behave different from what's specified in the Help. This is interpreted as not a bug but a documentation error. |
| 45 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TForm If you create a main form with WindowState=wsMaximized and Position=poScreenCenter, the main form displays incorrectly (sometimes about 10-15 pixels low and 20-25 pixels to the right) |
| 46 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TForm TForm.Name is empty at runtime. |
| 437 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TForm Forms do not honor the windows animation routine upon minimize/maximize |
| 457 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
THintWindow THintWindow has a resource leak: the Handle retrieved by GetWindowDC is not released by ReleaseDC |
| 67 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TObject This applies to all descendants of TObject, IOW: to all class types! Calling the Free method is not always safe |
| 505 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TRegistry The on-line documentation for the TRegistry.OpenKeyReadOnly is incorrect. |
| 506 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TRegistry The NT-specific comments in the on-line documentation for the TRegistry.DeleteKey method are incorrect. |
| 532 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TRegistry TRegistry.ReadString does not raise an exception when the value does not exist. |
| 90 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TStringList Find method does not always work. |
| 91 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TStringList The Clear method does not Free the corresponding objects. |
| 413 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TStringList TStringList.SaveToStream causes memory loss. |
| 445 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TStringList Memory leakage with TStringList.AddObject (Sorted = TRUE; Duplicates = dupIgnore) |
| 448 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TThread There's a documentation bug concerning TThread.OnTerminate. Contrary to what the Help says, it is not safe to free the thread object in the OnTerminate handler. |
| 459 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TThread Race condition in TThread's implementation |
| 102 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TWinControl TWinControl.Destroy destroys each of its controls before destroying itself, while it does not own its controls. |
| 103 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
TWinControl RecreateWnd is dangerous to API routines that rely on the window handle. |
Bug #2; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | N/A | Exists | Unknown | Unknown | Unknown | Unknown | Unknown |
It appears as if the VCL source code shipped with Delphi 3 (build 5.53) does NOT match the compiled DCUs and packages as distributed by Borland.
At least one example of this mismatch can be found in MMSYSTEM.PAS, where the source code lists the TWaveOutCapsA record NOT as "packed" (which is a bug) while in fact in the DCU it *is* packed (which is correct).
Bug #4; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.HookMainWindow(HookProc);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Application.UnHookMainWindow(HookProc);
end;
function TForm1.HookProc(var Message: TMessage): boolean;
begin
Result := false;
if (Message.Msg = WM_EndSession) and WordBool(Message.wParam) then
Halt;
end;
The cleanest solution would have been to call Application.Terminate
or MainForm.Close. Unfortunately this will not always work because
these are message based and Windows does not guarantee that any messages
will be processed after WM_EndSession has been sent.
So my solution is to simply call Halt. This might seem drastic, but it actually calls all ExitProcs, that again call all destructors for all forms closing them down properly, calling a ExitProc that close down the BDE properly and so on.
This solution will not call your OnClose handler, but it will call your OnDestroy handler. Put all your essential clean-up code there and you should be ok.
Bug #105; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
procedure test;
var a: Word;
b: TShiftState;
begin
ShortCutToKey(MyItem.Shortcut, a, b);
{Now, if I interrogate A, it is what I would expect, but B will be [] }
{Now if I say :}
MyItem.ShortCut := Shortcut(a, b) ; {MyItem no longer has a shortcut.}
end;
procedure ShortCutToKey(ShortCut: TShortCut; var Key: Word;
var Shift: TShiftState);
begin
Key := ShortCut and not (scShift + scCtrl + scAlt);
Shift := [];
if ShortCut and scShift <> 0 then Include(Shift, ssShift);
if ShortCut and scCtrl <> 0 then Include(Shift, ssCtrl);
if ShortCut and scAlt <> 0 then Include(Shift, ssAlt);
end;
Bug #108; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
program MyProgram;
Var
wStyle: LongInt;
begin
{ Set the Win95 to have only one icon for your app. }
wStyle := getWindowLong(Application.Handle, GWL_Style);
wStyle := wStyle or ws_Caption;
setWindowLong(Application.Handle, GWL_Style, wStyle);
.
.
Bug #453; last modified: 5-Aug-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Unknown | Unknown | Exists | Unknown | Unknown |
Bug #458; last modified: 17-Aug-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Unknown | Exists | Fixed | Fixed | Fixed |
The problem is reproducible:
Comment from checker:
Confirmed. This has also been fixed in D4 (they didn't bother to check
that HintThread <> 0 before calling CloseHandle, btw).
procedure UnhookHintHooks;
begin
if HintHook <> 0 then UnhookWindowsHookEx(HintHook);
HintHook := 0;
if HintThread <> 0 then
begin
SetEvent(HintDoneEvent);
if GetCurrentThreadId <> HintThreadID then
WaitForSingleObject(HintThread, INFINITE);
if HintThread <> 0 then
CloseHandle(HintThread);
HintThread := 0;
end;
end;
Bug #470; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Absent | Absent | Absent | Absent | Absent | Exists | Exists | Exists |
To fix this bug, you need to edit the FORMS.PAS file. I haven't attempted a workaround that doesn't involve editing the VCL code, although I'm sure it would be possible by trapping the WM_SIZE message.
You need to adjust the TCustomForm.WMSize procedure so that the "inherited" line comes AFTER the case statement that sets the WindowState property. i.e.:
procedure TCustomForm.WMSize(var Message: TWMSize);
begin
{inherited;} // remove this line
if not (csDesigning in ComponentState) then
case Message.SizeType of
SIZENORMAL: FWindowState := wsNormal;
SIZEICONIC: FWindowState := wsMinimized;
SIZEFULLSCREEN: FWindowState := wsMaximized;
end;
inherited; // move line to here.
RequestAlign;
......
I am using US English Delphi 4.0 Professional updated with the 12 August
1998 patch.
Bug #411; last modified: 7-May-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Unknown | Exists | Unknown | Unknown | Unknown |
Use the OnStartDrag event handler to implement special processing when the
user starts to drag the control or an object it contains. Sender is self,
the control that is about to be dragged, or that contains the object about
to be dragged.
The OnStartDrag event handler can create a TDragObject object for the
DragObject parameter to specify the drag cursor, or, optionally, a drag
image list. There is no need to call the Free method for the
DragObject when dragging is over.
Let's take a look at the VCL Source:
in DragDone you'll find this line of code:
if DragFreeObject then DragSave.Free;the only other occurrence of DragFreeObject is here:
procedure DragInitControl(Control: TControl; Immediate: Boolean);
var
DragObject: TDragObject;
begin
DragControl := Control;
try
DragObject := nil;
DragFreeObject := False;
Control.DoStartDrag(DragObject);
if DragObject = nil then
begin
DragObject := TDragControlObject.Create(Control);
DragFreeObject := True;
end;
DragInit(DragObject, Immediate);
except
DragControl := nil;
raise;
end;
end;
as you can see DragFreeObject is set to false before the call to
Control.DoStartDrag (which calls OnStartDrag...).
If the DragObject is set to something other than nil (as the help text
sugests for implementing own dragobjects) DragFreeObject is never set to
something other than false.... so the Object is never freed!!
Bug #412; last modified: 2-Feb-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Unknown | Exists | Exists | Exists | Exists |
It seems anything which causes the DragObject's window to lose the mouse capture before the end of the drag will run into this bug. The best workaround would be for TDragObject's MouseMsg window method to handle the WM_CAPTURECHANGED message so that it cancels the drag automatically if something grabs the mouse capture. Unfortunately, any fix will probably require changing controls.pas. Also note that, as it is now, CancelDrag calls DragDone which ultimately calls ReleaseCapture. If called in response to a WM_CAPTURECHANGED message, CancelDrag will release the current capture (if any), not DragObject's capture (which has already been released).
Bug #408; last modified: 15-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Exists | Exists | Exists | Exists | Unknown |
Cause:
The problem happens in the MakeObjectInstance (or rather because of...)
in MakeObjectInstance is the following line
Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);which allocates memory in 4K blocks; this is not released again :-(
Procedure DeleteInstBlockList;
Var
Block : PInstanceBlock;
Begin
Repeat
Block := InstBlockList^.Next;
VirtualFree(InstBlockList, 4096, MEM_DECOMMIT);
InstBlockList := Block;
Until Block = NIL;
end;
and must then be called from the finalization section of the forms.pas unit
finalization .. .. DeleteInstBlockList; end.
Bug #106; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
uses
Printers;
procedure TForm1.Button1Click(Sender: TObject);
begin
Printer.BeginDoc;
if Printer.Printing then
begin
Printer.Abort;
Printer.EndDoc;
exit;
end;
Printer.EndDoc;
end;
This problem is due to an oversight by Borland in PRINTERS.PAS.
Printer.Abort;with
Printer.Abort;
Winprocs.AbortDoc(Printer.Handle);
or replace TPrinter.Abort in PRINTERS.PAS with
procedure TPrinter.Abort;
begin
CheckPrinting(True);
{$IFDEF FixPrinterAbortBug}
WinProcs.AbortDoc(DC);
{$ENDIF}
FAborted := True;
end;
Bug #107; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown |
Printer.BeginDoc; Printer.Canvas.Font := Label1.Font; Printer.Canvas.TextOut(10, 10, Label1.Caption); Printer.EndDoc;will print the caption of the label by far too small. The reason for this is that a printer has a resolution of (say) 300dpi while the screen has a resolution of (say) 96-120dpi. You have set the printer's font to label's one, which will have it's PixelsPerInch property set to Screen.PixelsPerInch (100ish). So the font will print out about a third of the desired size.
Printer.BeginDoc;
Printer.Canvas.Font.PixelsPerInch :=
GetDeviceCaps(Printer.Handle, logPixelsY);
Printer.Canvas.Font.Size := 12;
Printer.Canvas.TextOut(10, 10, Label1.Caption);
Printer.EndDoc;
Bug #14; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
procedure TApplication.Run;
begin
AddExitProc(DoneApplication);
if FMainForm <> nil then
begin
{$IFDEF FixHonourRunMinMax}
if (CmdShow = sw_ShowMinimized) or
(CmdShow = sw_ShowMinNoActive) or { Win95 sets this }
(CmdShow = sw_ShowMaximized)
then ShowWindow(FMainForm.Handle, CmdShow);
{$ENDIF}
FMainForm.Visible := True;
repeat
HandleMessage
until Terminated;
end;
end;
If you do not have, or prefer not to modify, the VCL source, add the following
to your main form's OnCreate event handler:
if (CmdShow = sw_ShowMinimized) or
(CmdShow = sw_ShowMaximized)
then ShowWindow(Handle, CmdShow);
Note, that for this fix (and the old one, too) an application started with
a CmdShow <> sw_ShowNormal will always override the form's WindowState
setting. As this may be undesirable sometimes, you will have to set WindowState
:= wsXXX; in the (main) form's OnShow event handler.
Bug #493; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Absent | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha |
This is a bug because the wrong size parameter (16-bit) is being passed to the VCL function. If you register your own clipboard format (RegisterClipboardFormat), it returns a 32-bit value and that may not fit in a 16-bit value and so it would get truncated and the function would incorrectly return false.
Comment by Chris Jobson:
While it is correct that IsClipboardFormatAvailable and RegisterClipboardFormat use
UINTs, in fact clipboard formats are always 16-bit values and so I don't believe that
there will ever be a problem with using TClipboard.HasFormat.
The MSDN documentation for RegisterClipbioardFormat states "Registered clipboard formats are identified by values in the range 0xC000 through 0xFFFF", and some other Win32 API calls using clipboard formats treat them as 16-bit values (for example, the DDEADVISE structure used with WM_DDE_ADVISE messages stores the clipboard format as a "short"). I think the real problem here is Microsoft's API is not consistent!
Bug #29; last modified: 15-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Exists | Exists | Exists | Exists | Fixed | Fixed | Fixed |
(ML/HV:) The routines ObjectBinaryToText and ObjectTextToBinary in the Classes unit convert the persistent storage of objects from binary to text and vice versa. This is used to convert DFM files to TXT files and back, for instance. When ObjectBinaryToText converts properties it does not store any type information only the name and the value of the property. So when ObjectTextToBinary converts the text back to binary format it must deduce the type of the properties by looking at how the values are formatted. String properties are recognized by the starting quote, while floating point properties are recognized by a decimal point.
In the standard VCL components, only TBCDField and TFloatField publish
floating point properties: MaxValue and MinValue.
The problem can also be experienced with the Convert.EXE utility. It
can convert the DFM file to TXT but gives the same error when you try to
convert the TXT file back to a DFM file.
tkFloat: SetFloatProp(Instance, PropInfo, ReadFloat);to
tkFloat: If NextValue in [vaInt8,vaInt16,vaInt32] then SetFloatProp(Instance, PropInfo, ReadInteger); else SetFloatProp(Instance, PropInfo, ReadFloat);(HV:) You can of course change the implementation of ObjectBinaryToText so that it always store floating-point values with a decimal part, but that will not help very much, as the problem is mostly about the version of ObjectBinaryToText that has been compiled into the Delphi IDE and Convert binaries.
Another workaround is to use values with decimal parts (i.e. 2147483648.1 instead of 2147483648) or restrict yourself to use the range -MaxInt..MaxInt. If you use CONVERT.EXE in batch-file routines you could also introduce a post-processing utility that simply adds proper floating point parts to any numerical property that exceeds the integer range.
Component writers should be aware of this bug and explain to their users how it can be avoided.
Bug #30; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Absent | Absent | Exists | Unknown | Fixed | Fixed | Fixed | Fixed |
Step-by-step instruction on how to re-produce the bug:
if ((I - K) >= LineLength) and (I < L) then
begin
WriteStr(' +');
NewLine;
K := I;
end;
This should be changed to:
if ((I - K) >= LineLength) and (I <= L) then
begin
WriteStr(' +');
NewLine;
K := I;
end;
Notice that the '<' has been changed to '<='.
The only other known workaround is to avoid strings of length 65, 129, 193 etc in all string and stringlist properties (including TQuery.SQL property). A viable option is to fix the code for ObjectBinaryToText and then write your own Convert.Exe utility (this is fairly straightforward).
Bug #436; last modified: 29-Dec-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | N/A | N/A | N/A | N/A | Exists | Exists | Exists |
I believe this problem occurs because most of the special resizing code is turned off during form loading. Note in particular TWinControl.UpdateLastResize which appears to be crucial in saving information for the next time AlignControls is called. If I am correct, this problem should not be limited to setting WindowState:= wsMaximized; it will also show up for any property changed during form loading which causes the form to have a different size than it had in the Delphi form-designer. I'm not sure how big a problem that may be.
Bug #509; last modified: 9-Dec-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha |
procedure TControl.WMCancelMode(var Message: TWMCancelMode);
begin
inherited;
if MouseCapture then
begin
MouseCapture := False;
if csLButtonDown in ControlState then Perform(WM_LBUTTONUP, 0,
$FFFFFFFF);
end;
end;
What the Delphi programmers have tried to do is to correct the Windows
problem whereby a mousedown triggering creation of a new window causes the
associated mouse up to be delivered to the new window rather than the
original window.
procedure TControl.WMCancelMode(var Message: TWMCancelMode);
begin
inherited;
if MouseCapture then
begin
MouseCapture := False;
if csLButtonDown in ControlState then Perform(WM_LBUTTONUP, 0,
Integer($FFFFFFFF));
end
else
Exclude(FControlState, csLButtonDown);
end;
This ensures that the ControlState is maintained correctly (a good thing)
but they completely missed the actual bug that was causing this problem in
the first place!
Bug #31; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Exists | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown |
Bug #45; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Exists | Exists | Unknown | Unknown | Unknown | Unknown | Unknown |
Bug #46; last modified: 7-May-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
Alternative solution:
Set the form's name property at run time e.g. in the form's OnCreate event routine.
Bug #437; last modified: 22-Jul-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha |
This behaviour can be explained by the fact that the application maintains
a hidden 'Main Window'; your own main window is therefore not the real
main window and therefore it does not minimize like 'normal' applications do.
I do not remember where this is documented, but I am quite sure that
the above explanation is right. If someone would provide a good (Borland supplied)
reference to this information, this would be very much appreciated.
Bug #457; last modified: 17-Aug-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Unknown | Exists | Fixed | Fixed | Fixed |
procedure THintWindow.WMNCPaint(var Message: TMessage);
var
R: TRect;
begin
Canvas.Handle := GetWindowDC(Handle);
with Canvas do
try
R := Rect(0, 0, Width, Height);
DrawEdge(Handle, R, BDR_RAISEDOUTER, BF_RECT);
finally
ReleaseDC(Self.Handle, Canvas.Handle);
Canvas.Handle := 0;
end;
end;
Bug #67; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Gotcha |
Bug #505; last modified: 4-Nov-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | N/A | N/A | N/A | N/A | Exists | Exists | Exists |
Key is opened with the security access value KEY_ALL_ACCESS. ... "
That is not true. As seen from the source code, TRegistry.OpenKeyReadOnly calles WinAPI function RegOpenKeyEx with KEY_READ, not KEY_ALL_ACCESS, which it naturally used in TRegistry.OpenKey. I suppose the bug in documentation is just a misprint.
Bug #506; last modified: 4-Nov-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | Absent | Exists | Exists | Exists | Exists | Exists | Exists |
That is not true. The subkeys are deleted under Windows 95 as well as for Windows NT. Moreover, the source code of TRegistry.DeleteKey recursively calls WinAPI RegDeleteKey (which behaviour really IS OS dependent) and does not check the OS.
Bug #532; last modified: 9-Feb-99| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | Exists | Exists | Exists | Exists | Exists | Exists | Exists |
...
uses Registry;
...
var Reg: TRegistry;
...
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CURRENT_USER;
If Reg.OpenKey('Software', False) then
begin
try
ShowMessage('The value ThisNameDoesNotExist contains <'
+ Reg.ReadString('ThisNameDoesNotExist') + '>');
except
on ERegistryException do
ShowMessage('Value not found');
else raise;
end;
end;
Reg.Free;
Change Reg.ReadString(...) to IntToStr(Reg.ReadInteger(...)) to check
the correct behaviour.
It's a direct consequence of the code for TRegistry.ReadString in VCL\REGISTRY.PAS: a check is made for GetDataSize > 0 and if false the function returns '', but GetDataSize returns -1 when the value does not exist, thus causing the function to return '' instead of raising an exception as it should.
Bug #90; last modified: 25-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
Erik Berry pointed out that this behaviour is as designed; it should be considered a documentation bug. In the Delphi versions after D1, this has been corrected; the documentation does mention that Find is only meant to work for sorted lists.
Bug #91; last modified: 15-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Gotcha | Gotcha | Gotcha | Gotcha | Fixed | Fixed | Fixed |
Bug #413; last modified: 1-Jun-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Fixed | Exists | Fixed | Fixed | Fixed | Fixed | Fixed | Fixed |
Jordan Russell:
The statement that this bug was fixed in Delphi 2.01 is NOT correct -- I
just tested and checked the VCL source code (classes.pas). Delphi 3.00 is
the first version to fix the bug.
Bug #445; last modified: 27-Jul-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Exists | Exists | Exists | Exists | Exists | Exists | Unknown |
AddObject('string1', object1);
AddObject('string2', object2);
AddObject('string3', object3);
AddObject('string2', object4);
In the brief example above the resulting TStringList is as below:
I'd expect it to be
string1 object1
string2 object2
string3 object3
with a fail flag returned so that I can dispose of the object I created (in
this instance object4).
eg.
object4 := create;
if (AddObject('string2', object4) = -1) then
object4.Free;
In the online help for Delphi under TStringList.Duplicates it says that
dupIgnore should 'ignore attempts to add duplicate string to the sorted
list'. This is not the case when doing AddObject.
Bug #459; last modified: 18-Aug-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | Absent | Absent | Absent | Absent | Exists | Exists | Unknown |
The source of the problem is a race condition between a thread that is destroying and another thread that is creating and the result of the problem is that TThread.Synchronize *might* stop working.
In short here's what happens:
It might be difficult to reproduce the bug since the timing of the two threads has to be "right" for it to occur. Anyway, the following code reproduces the bug on my system (400MHz P2 running NT4):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
// A form with 2 buttons on it
TForm1 = class(TForm)
ButtonStart: TButton;
ButtonDie: TButton;
procedure ButtonStartClick(Sender: TObject);
procedure ButtonDieClick(Sender: TObject);
end;
TDummyThread = class(TThread)
protected
procedure Execute; override;
procedure DoCount;
end;
var
Form1: TForm1;
PleaseTerminate: boolean = False;
Counter: integer = 0;
implementation
{$R *.DFM}
procedure TDummyThread.Execute;
begin
FreeOnTerminate := True;
while not(terminated) do
begin
Sleep(10);
Synchronize(DoCount);
end;
end;
procedure TDummyThread.DoCount;
begin
inc(Counter);
Form1.Caption := IntToStr(Counter);
if (PleaseTerminate) then
begin
// Acknowledge terminate request and do it
PleaseTerminate := False;
Terminate;
end;
end;
procedure TForm1.ButtonStartClick(Sender: TObject);
begin
ButtonStart.Enabled := False;
ButtonDie.Enabled := True;
TDummyThread.Create(False);
end;
procedure TForm1.ButtonDieClick(Sender: TObject);
var
Msg : TMsg;
begin
// Request that thread terminate itself
PleaseTerminate := True;
// Wait for thread to acknowledge terminate request
while (PleaseTerminate) do
Application.ProcessMessages;
// The following Sleep(0) is used (as an example) to
// let the thread resume execution. While we sleep,
// the thread will decrement the thread counter,
// detect that it was the last thread and therefore
// signal the thread window to delete itself by
// posting a CM_DESTROYWINDOW message
// to it.
Sleep(0);
// Once the message has been posted, execution
// *might* continue in this thread...
// Here we create a new thread. The new thread will
// see that it is the first thread (thread counter is
// zero), and thus create a new thread window...
// The problem is that the CM_DESTROYWINDOW
// message hasn't been processed yet and when it at
// some later time does get processed, it will delete
// the new thread window instead of the old one (which
// by the way has now been lost/leaked because we have
// overwritten the thread window variable with a new
// value).
TDummyThread.Create(False);
end;
end.
CM_DESTROYWINDOW:
begin
FreeThreadWindow;
Result := 0;
end;
with
CM_DESTROYWINDOW:
begin
DestroyWindow(Window);
Result := 0;
end;
Bug #102; last modified: 25-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Exists | Unknown | Unknown | Unknown | Unknown | Unknown | Fixed | Fixed |
destructor TWinControl.Destroy;
{Snip}
I := ControlCount;
while I <> 0 do
begin
Dec(I);
Instance := Controls[I];
Remove(Instance);
Instance.Destroy;
end;
{Snip}
end;
with (new code):
destructor TWinControl.Destroy;
{Snip}
while ControlCount <> 0 do
begin
Instance := Controls[ControlCount-1];
Remove(Instance);
Instance.Destroy;
end;
{Snip}
end;
Bug #103; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Gotcha | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown |
Another possibility is to simply make sure that you do not do anything that would cause the component's window to be recreated. Some conditions which cause a window to be recreated are: