Delphi 2.0 Bug List
Posted: (EET/GMT+2)
Delphi 2.0 Bug List
Here are some bugs I've found while using Delphi 2.0. I have submitted these bugs to the Delphi 2.0 Bug List maintained by Reinier Sterkenburg. You can find these bugs from there, and possibly elsewhere.Currently 14 bugs are documented.
[ VCL | RTL | IDE | Compiler ]
Problem Example:
The above modification is not automatically
reflected on-screen.
Solution: Call Refresh after modification.
Solution Example:
Problem Example:
The above modifications to the Left and Top properties are lost
after the Parent property has been set.
Problem location:
The ComCtrls.pas file in procedural method TCustomListView.CreateWnd.
The code calls the FMemStream.ReadComponentRes method to reload
the whole component, but instead it should only read the column/item values.
Solution: Set Parent property before setting other properties.
Solution Example:
Problem example:
The above only sets the read-only style correctly, but does not
change the color of the control.
Solution: Set the color manually.
Solution example:
[Note: This bug does not apply to TListBox.]
Problem example:
The above code sets the ItemIndex of the combobox to the same
value it already was. This causes the the control to be redrawn
unnecessarily. This can lead to performance problems, or at least
unpleasant flicker especially if the combobox items are
"owner-drawn".
Problem location: The STDCTRLS.PAS file in procedural method
TCustomComboBox.SetItemIndex. The code sends a CB_SETCURSEL
message to the control without checking if the item index has actually
changed.
Solution #1: Check to see if the ItemIndex actually needs to be
set.
Solution #1 example:
Solution #2: If you want to avoid this problem in the future and
you have the VCL source code, you could modify STDCTRLS.PAS
directly.
Solution #2 example:
Problem example:
Insert a TEdit and a TUpDown component on a form. Associate the UpDown
with the Edit. Set the UpDown's properties as follows (for example):
Problem cause: This appears to be a Windows common control bug.
Solution: None known. Untested suggestion: manually change the
position to a correct value when the associated Edit changes (OnChange
event). However, this is a quite small bug, and as such there is
really no need to do anything. Besides, negative values are less
common with this control.
Problem example:
Solution: Call SetFocus before calling EditCaption if focus is
elsewhere.
Solution example:
[Please note: The actual "porting" from C/C++ could be correct.
The Win32 API Help files state that the fields are arrays of
"BCHARS". IMO this is a bit confusing anyway.]
Problem example:
In this example, the data in "elfFullName" should be added to a
string list (for example, a listbox item list). But because the
the field is defined as an array of bytes, the compiler cannot
convert the array correctly to a (long) string.
Problem location: The Win32 API header/import file WINDOWS.PAS.
Solution #1: Define a temporary type to equal an array of chars
and do a typecast.
Solution #1 example:
Solution #2: If you want to avoid this problem in the future and
you have the RTL (VCL) source code, you could modify WINDOWS.PAS
directly.
Solution #2 example:
Problem example:
Then save your project. Now close the project, and load it again.
Everything is OK. Next, simply change the active tab sheet to the other
one (on which Edit2 is). Save the project again, close it, and try
to reload it. Delphi just informs you: "Error creating form: Cannot
focus a disabled or invisible window."
The problem is that you cannot even view or edit the form
anymore to set the original tab sheet back.
Solution #2: Open the Form file manually as text, and remove the
line which sets the form's ActiveControl property.
Solution #2 How To:
Problem example:
Solution:
Problem example:
(Try the longer version of $D - it won't work)
Now create a similar program with no descrption:
Both compiles result in a 7,168 byte EXE file. The problem is that the
first program doesn't include the module description as it should.
Even TDUMP won't display it. Doing a file compare results:
So the compiler simply ignored the directive.
Using the other comment marks "(*" and "*)" doesn't help. I also tried
the other "long" compiler directives, but they appear to work as
expected. Next I checked the compiler module (DCC.DLL and DCC32.EXE) for
misspellings, to no avail. Command line compiling doesn't help
either.
Also, linking resources with $R directive or using other
units in the code doesn't help.
Solution: If you're compiling inside the IDE, use the Project Options
EXE description field. Otherwise, use the long version of the
directive, and quote the description: {$DESCRIPTION 'Copyright...'}.
Still, the $D directive won't work.
Problem example:
The above code compiles happily. For example in Borland
Pascal 7.0 the above fails with error message "Identifier
redeclared.". If the Uses clause is moved to the
Interface section, Delphi doesn't compile the unit.
Solution:
Problem example:
Above, normal arrays of strings can be accessed in two ways (1 and 2).
However, array properties can only be accessed with the case 4. Case 3
gives error message "Too many actual parameters".
Problem cause (supposition):
Solution:
Problem example:
Above, this simple program should display zero as its result. On
statement A, the program relies on the fact that global variables
initially have a zero value (I, J both equal zero) and thus the
addition is unnecessary and could be an assignment instead (I :=
J). On B, the code does a substract, and the result should be
zero. However, if optimization is on, a garbage result (usually
something like -5636096) will occur.
Problem cause:
Solution:
(Alternative: modify statement A to not to use I:
"I := J")
Also rearranging or rewriting portions of code seems to
help. However, until this kind of bugs have been fully
documented, the only way to be really sure is to turn
optimization off. However, this bug seems to be a special
case and should not haunt "normal" Delphi programs.
Problem example:
Above, the code should Increase I from 1 to 10, and then exit without
displaying anything. However, if the above code is executed, usually
an access violation will occur when Continue is called.
Note that if the Until expression would have been False (Until False),
Continue would work correctly.
Problem cause:
If you look at the code generated by the compiler, you can see that
the Continue procedure really jumps to a rather random address, thus
causing access violations. Occasionally, the destination address might
contain valid executable code, but usually not.
Problem solutions:
Of course, changing code to use more "intelligent"
constructs is the best solution.
Note about the bug:
This crash could result from other things of course, but
it is still very strongly linked with the Continue bug.
Visual Component Library (VCL)
Now run the project. The initial position (value) of the Edit is zero.
When you click the down arrow, the value changes to -16, while it
should be -10. Changing the position between positive and negative
leads to incorrect negative values that have only a little to do with
the Increment. The number 6 seems to repeat with different Increments.
TListView1.SubItems[0] := 'XYZ';
TListView1.SubItems[0] := 'XYZ';
TListView1.Refresh; { call Refresh to see the new value }
With TListView1 do Begin
Left := 123;
Top := 321;
Parent := Form2;
End;
With TListView1 do Begin
Parent := Form2; { set the Paremeter property first }
Left := 123;
Top := 321;
End;
Edit1.ReadOnly := True;
With Edit1 do Begin
ReadOnly := True; { set to read-only }
Color := clBtnFace; { indicate different behavior to user}
...
ReadOnly := False; { back to read-write }
Color := clWindow; { restore normal color }
End;
Var I : Integer;
...
With ComboBox1 do Begin
I := ItemIndex;
ItemIndex := I; { re-set to old value --> flicker }
End;
Var I : Integer;
...
With ComboBox1 do Begin
I := 123;
{ set to the new value }
If (I <> ItemIndex) Then { compare with old value }
ItemIndex := I; { if not equal, set property }
End;
{
New version of SetItemIndex: this version checks to see
if the "new" value is actually different than the previous one.
}
procedure TCustomComboBox.SetItemIndex(Value: Integer);
begin
if GetItemIndex <> Value then
SendMessage(Handle,CB_SETCURSEL, Value, 0);
end;
Increment: 10
Max: 100
Min: -100
ListView1.Selected.EditCaption;
The above code starts the editing only if the list view has the
focus. If the method was called for example from a button OnClick
handler, nothing would happen. This is true even if HideSelection
is False.
ListView1.SetFocus; { set focus first }
ListView1.Selected.EditCaption;
Please note that the current EditCaption implementation simply
sends a Windows message to the list view, and thus this is not a
real VCL bug. Still, this bug would be very easy to avoid, if the
VCL developers would have included the SetFocus call in the
implementation.
Runtime Library (RTL)
Function MyFontEnumProc(lpelfe : PEnumLogFontEx;
lpntme : PNewTextMetricEx; FontType : Integer;
LParam : LongInt) : LongInt; StdCall;
Begin
TStrings(LParam).Add(lpelfe^.elfFullName); { this line won't compile }
Result := 1; { continue font enumeration }
End;
Function MyFontEnumProc(lpelfe : PEnumLogFontEx;
lpntme : PNewTextMetricEx; FontType : Integer;
LParam : LongInt) : LongInt; StdCall;
Type
TelfFullName = { define temporary type (see WINDOWS.PAS) }
Array[0..lf_FullFaceSize-1] of Char;
Begin
TStrings(LParam).Add(TelfFullName(lpelfe^.elfFullName));
{ do a typecast }
Result := 1; { continue font enumeration }
End;
{ Redefine TEnumLogFontEx record to allow quick
conversion of the fields to long strings. }
PEnumLogFontEx = ^TEnumLogFontEx;
TEnumLogFontEx = packed record
elfLogFont: TLogFont;
elfFullName: array[0..LF_FULLFACESIZE - 1] of char; { modify }
elfStyle: array[0..LF_FACESIZE - 1] of char; { modify }
elfScript: array[0..LF_FACESIZE - 1] of char; { modify }
end;
Integrated Development Environment (IDE)
Create a new project. On the form, drop a TPageControl component.
Create two new TTabSheets on it. Create two TEdits, one on each tab sheet.
Set the form's ActiveControl property to Edit1 (on either tab sheet). Be
sure the tab sheet on which Edit1 is is active.
Solution #1: Time to restore the backups... ;-)Choose Open from the File menu. Choose to list only form
files (*.dfm), and open the form which is causing problems.
(Note that you must close the project causing problems, otherwise Delphi
will try to show the form again.) The form will be shown as text. Remove
the line which sets the ActiveControl property. Usually this is the 6th
line in the file. Save the form, and re-open your project. Everything
should be OK now.
Place the character ' (quotation mark) to its own
line in your code. Compile the code, and you
get two error messages: "Unterminated string" and
"Missing operator or semicolon". Notice how the focus is
shifted to the left. If you now try to select an area or
scroll the screen, you notice bizarre effects in the left
margin.
Try to avoid the situation. Normal behaviour can be
returned by moving the cursor, minimizing the editor window
and then restoring it back.
Compiler
Create a plain simple program with a description:
program Test;
{$D My Program v2.6b}
begin
{ do nothing }
end.
program Test;
begin
{ do nothing }
end.
[Win95]E:\delphi32\exefiles>fc test.exe test2.exe /b
Comparing files test.exe and test2.exe
FC: no differences encountered
Create an unit:
Unit MyUnit;
Inteface
Implementation
Uses MyUnit; { this should not compile }
End.
None needed, as this bug doesn't result in any
problems.
Type TMyArray = Array[1..2] of String;
Var MyArray : TMyArray;
...
1: If (MyArray[1,1] = 'A') Then ... { this works }
2: If (MyArray[1][1] = 'A') Then ... { this works too }
...
3: If (Memo1.Lines[1,1] = 'B') Then ... { this doesn't work }
4: If (Memo1.Lines[1][1] = 'B') Then ... { this works OK }
The compiler compares the array indices with the
parameters of the read access method, and notices that
there are too many parameters. However, the compiler
forgets to check if the property type is an indexable type
(like strings are). The other indexing method (case 4 above)
is interpreted differently from case 3 inside the compiler
and thus doesn't cause an error.
Use the method in case 4 above, or first assign the
property value to a string variable, and then access the
individual characters of the string.
program Project1;
{$OPTIMIZATION ON}
Uses Dialogs,SysUtils;
Var I,J : Integer;
begin
J := 123;
A: I := I+J;
B: I := J-I; { compiler warning here: "I" not initialized }
ShowMessage(IntToStr(I));
end.
Because the compiler optimizes the variables to be on CPU
registers and not in memory, the register for I is not
cleared. If you look at the assembled code, you can see
that the EBX register is not cleared and contains a
garbage value. The reason for this seems to be the
incorrect warning that variable I might not be
initialized on statement B (shouldn't the warning occur
on statement A?) and thus the optimizer gets confused
producing bad code. Stack or memory based variables seem
to work OK in this kind of situations.
Initialize global variables, even though that is
unnecessary:
...
begin
I := 0; { initialize even if not needed }
J := 123;
...
procedure ...
Var I : Integer;
begin
I := 1;
Repeat
Inc(I);
If (I < 10) Then Continue
Else Exit;
ShowMessage('Bug (inside Repeat)!');
Until True;
ShowMessage('Bug (after Repeat)!');
end;
Because the Until expression is always True, the control would always
go through the Repeat loop like it never existed. However, in the code
above, this is not the case because Continue and Exit procedures are
used. The compiler correctly "optimizes away" the unnecessary Until
expression, but on the same time leaves the Continue procedure
destination to an arbitary address.
While a "Repeat..Until True" is a rather obscure
programming solution, it is still useful when avoiding
Gotos. To avoid any problems with Continue in this
*particular situation*, either:
Or alternatively:
...
Until False;
...
This appears not to be only a code generation bug. If you create a
plain simple project with the above code as a button's OnClick event
handler, Delphi will create an access violation the second time you
try to compile the project in a row (provided that you modify the file
to force it's compilation). On my computer the second compilation
always results in access violation at address 832A99. After this,
Delphi must be restarted to restore normal behaviour.