The Delphi Bug List

The Run Time Library (RTL) units


The color codes indicate in which version(s) of Delphi the bug occurs and what its status is.
Latest update: 9 February 1999
Bug # Delphi versions Description
141 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
The Inc procedure increments with more than 1.
See description under 'documentation'
481 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
If you pass a command line parameter to your program that is longer than 255 characters and then try to read this parameter using the ParamStr function an access violation will occur.
511 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
The Copy function (for dynamic arrays) fails when the passed array is nil
533 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
The Interface-Definition of IShellView.GetItemObject is wrong
193 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 ComObj -
ciInternal COM objects may cause spurious OLE errors
194 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 EditIntf -
Bug in function TIResourceFile.CreateEntry
513 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 IShellBrowser -
The declaration of IShellBrowser.SendControlMsg is wrong. The last parameter should be a pointer to an integer instead of the integer itself
424 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 IniFiles -
In TIniFile.WriteInteger and TIniFile.WriteString, passing a section argument that ends with a space character causes "an invalid page fault in module KRNL386.EXE at 0002:00005c53"
491 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Internet -
(Win)CGI applications processing a web request return a line like this as part of the result:
Status: 200 OK Content-Type: text/html Content-Length: 449 Content:
385 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Internet - scktcomp
There's a bug in the function TCustomWinSocket.SendStreamPiece in Unit ScktComp.pas.
415 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 JPEG - TJPEGImage
TJPEGImage has a memory leak when 256 colors are used. This bug is caused by a bug in Graphics.pas
195 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 MAPI -
The "GetProcAddress(MAPIModule, 'MapiLogOn')" (and corresponding MapiLogOff function) has the case wrong - it should be MapiLogon (No uppercase O) and MapiLogoff. The function fails until that is changed.
200 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 MMSystem -
The waveInClose function is declared wrong.
201 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 MMSystem -
The declaration for mixerMessage() is missing
196 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Math -
The function Power gives an error when zero is raised to a power.
197 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Math -
InternalRateOfReturn function gives incorrect answers
198 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Math -
There seem to be (many!) more bugs in the financial routines of the MATH unit. Karl Thompson has submitted a message describing bugs in the functions NetPresentValue, DoubleDecliningBalance and FutureValue.
I haven't had time yet to verify these bugs, but I trust they are real bugs. I'd be very happy if anyone would take the trouble of checking these bugs and let me (Reinier Sterkenburg) know the results. Any workarounds or pointing at the exact locations where the code is wrong would also be appreciated.
199 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Math -
Two more functions with bugs: InterestPayment and InterestRate. The latter does work, but the passed parameter values are not adequately explained in the help.
202 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 RichEdit -
TTextRange is declared wrong for TRichEdit
203 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 ShellAPI -
A lot of functions of the SHELL32.DLL are not declared in the Delphi 2 ShellAPI unit.
204 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 ShlObj -
IShellBrowser.SetMenuSB is declared wrong
205 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 ShlObj -
IShellView.GetItemObject is declared wrong
422 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 ShlObj -
The declaration of the HandleMenuMsg function of IContextMenu2 is wrong
216 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
Val thinks that IntToStr(Low(Integer)) is not a valid integer
217 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
EncodeDate doesn't add 1900 to the year if the year is < 100
218 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
When you call FindClose a second time, the application hangs completely.
219 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
ShowException() prints a bad address for the exception (console mode apps). This is because it subtracts OFFSET TextStart from the error in an attempt to convert it into a logical address. I think this code came straight out of D1 and just wasn't updated.
The result of this error is that you aren't given an address that you can feed into Search|Find Error.
220 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
StrPCopy only copies first 255 chars
221 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
StrToDateTime has a bug
222 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
StrToInt fails on IntToStr(Low(Integer))
See the description under Val (bug # 216)
389 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
DiskFree and DiskSize report wrong (sometimes negative) values.
507 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils -
Using (very) long strings for an Exception message can make the IDE crash.
371 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 SysUtils - Format
Using the FORMAT statement with an integer specifier larger than 15 (ie: '%.16d' ) does not pad with zeros as expected.
206 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
The Round() function can give unexpected results
207 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
ErrorAddr is always NIL in finalization section
208 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
The AssignFile / Reset procedures accept '' (empty string) for a file name and then hang up your system.
209 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
There is an undocumented difference between how Delphi 1's Readln handles error conditions and how Delphi 2 and 3 do this.
210 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
The Reset procedure doesn't work properly on files that are already open.
211 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
There is another bug in the Reset procedure when sharing files.
212 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
The third bug in the Reset procedure is that it fails on files that are Read-only. This is easily worked around by adding the statement FileMode := 0 just before calling Reset.
213 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
Read/Readln fails to read a number from a text file if it has a trailing decimal point.
214 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
Access Violation in procedure SetString for a ShortString when the copy from buffer is empty.
215 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 System -
The Val function fails on strings with spaces at the right
223 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 TLHelp32 -
Wrong length of szModule field of TModuleEntry32
224 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 TLHelp32 -
Wrong declaration: PModuleEntry32
232 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 WinSvc -
Declaration of EnumServicesStatus is wrong
225 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
BroadcastSystemMessage maps to a wrong function
226 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
You can't use the FindFirstChangeNotification as declared in Delphi 3 Windows.pas.
227 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
The declaration of TCharInfo is not correct
228 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
The declaration of TKeyEventRecord is not correct
230 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
You can't pass NIL as the last argument to OffsetWindowOrgEx, while the On-Line help says you can.
231 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
Type "TEnumLogFontEx" needed with the EnumFontFamiliesEx API function as defined is hard to use.
391 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
The parameter value -1 is specified to give the standard sound but the parameter is declared as a Word and therefore -1 is illegal.
414 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
The GetDiskFreeSpaceEx functions are declared with wrong parameter types in the Windows unit.
497 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows -
There is a bug in the WINAPI definition for the function SetWinMetaFileBits
229 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 Windows? -
The routines in both NETAPI and NETAPI32 are uncallable from Delphi.
233 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 general -
Not bugs but very annoying and sometimes hard to track down, so worth a place in these Bug Lists are the many duplicate names. Some examples are: DeleteFile, FindClose, TBitmap, and there are more.

Bug #481; last modified: 24-Dec-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Exists Exists Exists Exists Exists Exists Exists

If you pass a command line parameter to your program that is longer than 255 characters and then try to read this parameter using the ParamStr function an access violation will occur.

Description
Reported by Jordan Russell; checked by Max Masyutin
This bug affects Delphi versions 2.0-3.02. In Delphi 4, the parameter length limit has been increased from 255 to 4095 characters, but your program will still crash if this limit is exceeded.
Solution / workaround
Use the GetCommandLine Win32 function to manually parse the command line.

Max Masyutin proposed a fix to the VCL:
Replace the existing GetParamStr function withthe following code:

function GetParamStr(P: PChar; var Param: string): PChar;
  function StrLen(Str: PChar): Cardinal; assembler;
  asm
          MOV     EDX,EDI
          MOV     EDI,EAX
          MOV     ECX,0FFFFFFFFH
          XOR     AL,AL
          REPNE   SCASB
          MOV     EAX,0FFFFFFFEH
          SUB     EAX,ECX
          MOV     EDI,EDX
  end;
type
  PCharArray = ^TCharArray;
  TCharArray = array[0..MaxInt-1] of Char;
var
  BufLen, Len: Integer;
  Buffer: PCharArray;
begin
  BufLen := StrLen(P);
  GetMem(Buffer, BufLen);
  while True do
  begin
    while (P[0] <> #0) and (P[0] <= ' ') do Inc(P);
    if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break;
  end;
  Len := 0;
  while P[0] > ' ' do
    if P[0] = '"' then
    begin
      Inc(P);
      while (P[0] <> #0) and (P[0] <> '"') do
      begin
        Buffer[Len] := P[0];
        Inc(Len);
        Inc(P);
      end;
      if P[0] <> #0 then Inc(P);
    end else
    begin
      Buffer[Len] := P[0];
      Inc(Len);
      Inc(P);
    end;
  SetString(Param, Buffer^, Len);
  Result := P;
  FreeMem(Buffer, BufLen);
end;

Bug #511; last modified: 13-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 Gotcha Gotcha Gotcha

The Copy function (for dynamic arrays) fails when the passed array is nil

Description
Reported by Greg Chapman; checked by Reinier Sterkenburg
In Delphi 4, the compiler allows you to write this:
  A1:= Copy(A2);
as a shorthand for this:
  A1:= Copy(A2, 0, Length(A2));
(where A1, and A2 are dynamic arrays of the same type). Unfortunately, if A2 is nil, the shorthand version will cause an access violation because _DynArrayCopy (in system.pas) forgets to check before dereferencing A2 to get its length.
Solution / workaround
The workaround is to use the second version, which avoids this problem.

Bug #533; last modified: 9-Feb-99
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Exists

The Interface-Definition of IShellView.GetItemObject is wrong

Description
Reported by Peter Feldbaumer
In the Delphi-Unit ShlObj.pas it reads:
function GetItemObject(Item: UINT; const iid: TIID; IPtr: Pointer): HResult; stdcall;
Instead it should be:
function GetItemObject(Item: UINT; const iid: TIID;  VAR IPtr: Pointer): HResult; stdcall;
The Bug leads to crashes when using self-written shell-extensions in the Common-Dialog-Browsers.
Proof: Microsoft-Documentation
Solution / workaround
don't assign
IPtr := (pointer to my Interface)
but use:
Pointer(IPtr^):= (pointer to my Interface)
or correct ShlObj.Pas

Bug #193; 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
ComObj

ciInternal COM objects may cause spurious OLE errors

Description
Reported by Stefan Hoffmeister; confirmed by Hallvard Vassbotn and Yaliv Golan
A call to TAutoObjectFactory.Create may cause spurious OLE errors in your application if you instantiate the object with the ciInternal flag.
Example:
TAutoObjectFactory.Create( ComServer, 
                             T_my_COM_object,
                             {8CC8F401-EA44-11D0-8A63-851049D1071D},
                             ciInternal);
Now have a look at this code snippet from ComObj.pas, that will be called as a consequence of the above:
procedure TComObjectFactory.RegisterClassObject;
const
  RegFlags: array[ciSingleInstance..ciMultiInstance] of Integer =
  (REGCLS_SINGLEUSE, REGCLS_MULTIPLEUSE);
begin
  OleCheck(CoRegisterClassObject(FClassID, Self,
    CLSCTX_LOCAL_SERVER,
    RegFlags[FInstancing], FRegister));
end;
FInstancing will be ciInternal once we come to this routine.

Clearly the access of RegFlags[ciInternal] will return an undefined value; this is best seem by having turned on range checks ($R+) which will automatically give you a range check error.

Can this problem be safely ignored? Probably not, as the OLE documentation make it clear that OLE expects defined parameters to be passed.

Solution / workaround
By changing the RTL code in ComObj.pas you should be able to circumvent the problem.
  {$IFDEF Fix}
    if not (FInstancing = ciInternal) then
  {$ENDIF}
    OleCheck(CoRegisterClassObject(FClassID, Self,
      CLSCTX_LOCAL_SERVER,
      RegFlags[FInstancing], FRegister));
There appears to be no work-around for this problem.

Bug #194; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown
EditIntf

Bug in function TIResourceFile.CreateEntry

Description
Reported by Jani Järvinen
Bug with the TIResourceFile function CreateEntry (In Delphi 2.0, not checked whether also in Delphi 2.01). If this function is called before calling the GetEntry function, an "List index out of bounds" error occurs.

Problem example:

var
  RF: TIResourceFile;
  RE: TIResourceEntry;
     ...
  RE := RF.CreateEntry(PChar(MakeIntResource(rt_Bitmap)),
                       'MYBITMAP',0,0,0,0,0);
Here, a new bitmap named "MYBITMAP" should be created to the resource file pointed to by RF. However, no matter what the parameters for CreateEntry are (valid or not) an Delphi exception occurs. Because of this RE will always be nil.
Cause:
Unknown. However, it appears that the Tools API developers assumed that before calling CreateEntry, any extension DLL would loop through the previous entries anyway. However, this is not documented.
Solution / workaround
To avoid the problem, create a loop to do a dummy fetch to all previous entries in the resource. (Note that if you are going to edit the resource later, you would do this loop anyway because multiple resource entries with the same name are not allowed).
Var
  RF: TIResourceFile;
  RE: TIResourceEntry;
  I : Integer;
...
  For I := 0 to RF.GetEntryCount-1 do
    RE := RF.GetEntry(I); { dummy get to avoid Delphi bug }
    RE := RF.CreateEntry(PChar(MakeIntResource(rt_Bitmap)),
                         'MYBITMAP',0,0,0,0,0);
It might also be enough to get the last entry (RF.GetEntry(RF.GetEntryCount-1)), but I haven't tested this.

Bug #513; last modified: 20-Dec-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Exists Exists Exists Exists Exists
IShellBrowser

The declaration of IShellBrowser.SendControlMsg is wrong. The last parameter should be a pointer to an integer instead of the integer itself

Description
Reported by Mike Lischke
WRONG:
function IShellBrowser.SendControlMsg(ID, Msg: UINT; wParm: WPARAM; 
                                      lParm: LPARAM; Result: LResult): HResult; stdcall;
CORRECT: function IShellBrowser.SendControlMsg(ID, Msg: UINT; wParm: WPARAM; lParm: LPARAM; Result: PInteger): HResult; stdcall;
Solution / workaround
A workaround (apart from changing the source) is to call the function like:
var Index : Integer;

CurrentShellBrowser.SendControlMsg(FCW_TOOLBAR,TB_COMMANDTOINDEX,
  Message.ItemID,0,Integer(@Index));
note the type cast for the last parameter. BTW: The Windows help file states correctly:
LRESULT *pref   // Points to the SendMessage return value

Bug #424; last modified: 16-Jun-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Fixed Exists Exists Exists Exists Unknown Unknown Unknown
IniFiles

In TIniFile.WriteInteger and TIniFile.WriteString, passing a section argument that ends with a space character causes "an invalid page fault in module KRNL386.EXE at 0002:00005c53"

Description
Reported by Alan M. Cohen; checked by Reinier Sterkenburg
The bug can be reliably reproduced with the following code attached to a button click:
  IniFile := TIniFile.Create('Test.ini');
  IniFile.WriteString('Test ', 'Key1', 'Val1');
  IniFile.Free;
Note that the last character in the 'Test ' argument on the second line is a ' '. Deleting the ' ' ends the problem.

Interestingly, this problem does not occur in Windows' WritePrivateProfileString. In this API call, the trailing ' ' is simply stripped.

Two other notes:
1. This may be Win95 version dependent. I have not looked into this thoroughly, but this bug may not occur in some versions of Win95.
2. I've not tested TIniFile.WriteBool for this bug (but I'll bet it's there)

Notes from checker:
1. I have verified that the bug also occurs with Delphi 2.
2. The bug does not occur under Windows NT 4.0 (SP3). But the space gets truncated from the section name

Solution / workaround
Nothing better (yet?) than: avoid using spaces at the end of INI sections.

Bug #491; last modified: 15-Oct-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Exists Exists Exists Exists Unknown
Internet

(Win)CGI applications processing a web request return a line like this as part of the result:
Status: 200 OK Content-Type: text/html Content-Length: 449 Content:

Description
Reported by Rolf Eilers; checked by Anders Melander
Note: The application runs on Netscape Enterprise Server 3.0 , but it's not clear whether other servers show the same behaviour.
Solution / workaround
Anders Melander wrote:
My girl friend (Lone Bach Christensen) has had the same problem with the exact same config. The following is her solution:
In cgiapp.pas make the following modifications:
1) In TCGIResponse.SendResponse replace:
     AddHeaderItem(StatusString, 'Status: %s'#13#10);
with
     AddHeaderItem(StatusString, 'HTTP/1.0 %s'#13#10);
2) In TCGIResponse.SendRedirect replace:
     HTTPRequest.WriteString(Format('Location: %s', [URI]));
with
     HTTPRequest.WriteString(Format('Location: %s'#13#10#13#10, [URI]));
#1 is the solution to your problem. #2 is a fix for another problem she found (something to do with redirection obviously).

Fix #1 applies to both D3 and D4, but since TCGIResponse.SendRedirect has been rewritten in D4, fix #2 only applies to D3.


Bug #385; last modified: 15-Oct-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
Internet - scktcomp

There's a bug in the function TCustomWinSocket.SendStreamPiece in Unit ScktComp.pas.

Description
Reported by Pieter Gordijn; checked by Anders Melander
When sending streams with the TCustomWinSocket functions SendStream or SendStreamThenDrop and the size of a stream to send is greater than the amount which could be sent with the socket Send function, then not all the data of the stream is sent. The SendStream and SendStreamThenDrop functions however return True indicating succesfully sent. The problems occurs on the receiving side, where the incomplete stream arrives.

When I encountered these problems, I discovered a bug in the function TCustomWinSocket.SendStreamPiece in unit ScktComp.pas. Look at Line 898.

          end else if AmountInBuf > AmountSent then
--->        FSendStream.Position := StartPos + (AmountInBuf - AmountSent)
Should be replaced by:
          end else if AmountInBuf > AmountSent then
--->        FSendStream.Position := StartPos + AmountSent
I've submitted this bug to Borland Delphi Bug Report it has been confirmed by them.

Bug #200; 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
MMSystem

The waveInClose function is declared wrong.

Description
It is declared as (in MMYSTEM.PAS):
  function waveInClose(hWaveIn: PHWaveIn): Word;
Solution / workaround
Either modify MMSYSTEM.PAS and change the declaration to:
  function waveInClose(hWaveIn: HWaveIn): Word;
or use your own procedure if you do not like changing the VCL sources:
  function CorrectedwaveInClose(hWaveIn: HWaveIn): Word;
                                        external 'MMSYSTEM' index 505;

Bug #201; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Exists Fixed Fixed Fixed Fixed Fixed Fixed
MMSystem

The declaration for mixerMessage() is missing

Description
Solution / workaround
(needs VCL source):
There *is* this interesting function declared:
in the interface:
function erMessage(hmx: HMIXER; uMsg: UINT;
                   dwParam1, dwParam2: DWORD): DWORD; stdcall;
in the implementation:
function erMessage; external mmsyst name 'erMessage';
The workaround is to either fix the declaration, or do it in your own unit:
function mixerMessage(hmx: HMIXER; uMsg: UINT;
                      dwParam1, dwParam2: DWORD): DWORD; stdcall;

function mixerMessage; external mmsyst name 'mixerMessage';

Bug #196; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Exists Exists Fixed Fixed Fixed Fixed Fixed Fixed
Math

The function Power gives an error when zero is raised to a power.

Description
Reported by Stefan Hoffmeister
Borland seems to have forgotten to test "base=0" (result=0). e.g.: Math.Power(0, 0.5) == sqrt(0).
var
  x: extended;
begin
  x := Math.Power(0, 0.5);
  ShowMessage(FloatToStr(x));
This raises a "Floating point division by zero" exception.
Solution / workaround
In the function Math.Power, after
  if Exponent = 0.0 then
    Result := 1.0               { By fiat, 0**0 = 1 }
add something like:
else if Base = 0 then Result := 0 else ...

Bug #197; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Unknown Unknown Unknown Unknown Unknown Unknown
Math

InternalRateOfReturn function gives incorrect answers

Description
Reported by Mark W. Baumann
I discovered that the InternalRateofReturn() function in Math.pas gives incorrect answers.
There are many cashflows that will give incorrect results, here is one:
uses
  math;
Var
  cflow: array[0..8] of double;
  irr: double;
begin
  cflow[0] := -13560;
  cflow[1] := 340;
  cflow[2] := 470;
  cflow[3] := 611;
  cflow[4] := 756;
  cflow[5] := 906;
  cflow[6] := 1060;
  cflow[7] := 1218;
  cflow[8] := 26320;
  irr := InternalRateofReturn(0.1, cflow);
  showmessage(floattostring(irr));
  {delphi returns -0.11.. for irr, but the correct irr = 0.12304..}
end;
I submited this bug to Borland.

Bug #199; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Unknown Unknown Unknown Unknown Unknown Unknown
Math

Two more functions with bugs: InterestPayment and InterestRate. The latter does work, but the passed parameter values are not adequately explained in the help.

Description
Reported by Karl Thompson
InterestPayment gives wrong results; InterestRate causes Exceptions.
Karl has provided sample code with which the bugs can be reproduced.
Jean-Claude Valitchek has looked into these bugs and found that:

Bug #202; last modified: 15-Oct-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Exists Exists Fixed Fixed Fixed
RichEdit

TTextRange is declared wrong for TRichEdit

Description
Reported by Ulf Haueisen; checked by Stefan Hoffmeister
TTextRange, TTextRangeA and TTextRangeW are declared wrong in RICHEDIT.PAS of Delphi 3.0 (5.53). This affects the use of the EM_GETTEXTRANGE message.
Solution / workaround
Redeclare the types as follows:
  TTextRangeA = record
    chrg: TCharRange;
    lpstrText: PAnsiChar;
  end;
  TTextRangeW = record
    chrg: TCharRange;
    lpstrText: PWideChar;
  end;
  TTextRange = TTextRangeA;

Bug #203; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Exists Fixed Fixed Fixed Fixed Fixed Fixed
ShellAPI

A lot of functions of the SHELL32.DLL are not declared in the Delphi 2 ShellAPI unit.

Description
Reported by John fg
A lot of functions of the SHELL32.DLL are not declared in the Delphi 2 ShellAPI unit.
One of those is SHAddToRecentDocs, which adds a file to the Win95 recently openened documents menu.
Solution / workaround
You can fix the problem by declaring it yourself.
In the interface section of your unit, you put:
const
  SHARD_PIDL = 1;
  SHARD_PATH = 2;

procedure SHAddToRecentDocs(uFlags: Word; pv: Pointer); stdcall;
and in the implementation part:
procedure SHAddToRecentDocs; external 'shell32.dll' name
'SHAddToRecentDocs';
Now you can add a file to this menu like this: SHAddToRecentDocs(SHARD_PATH, PChar(Filename));

Bug #204; last modified: 20-Dec-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Exists Exists Exists Exists Exists
ShlObj

IShellBrowser.SetMenuSB is declared wrong

Description
Reported by Pieter Hoegee; confirmed by Stefan Hoffmeister
The declaration of the IShellBrowser in ShlObj.PAS of Delphi 3 (5.53) is incorrect for the SetMenuSB method. It is declared as
    function SetMenuSB(hMenuShared: HMENU;
      hOleMenuReserved: HOLEMENU): HResult; stdcall;
Solution / workaround
As indicated in a comment about 100 lines above in the same unit (!) the correct declaration is:
   function SetMenuSB(hmenuShared: HMENU; 
     hOleMenuReserved: HOLEMENU; 
   HwndActiveObject: Hwnd): HResult; stdcall;

Bug #205; last modified: 15-Oct-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Exists Exists Fixed Fixed Fixed
ShlObj

IShellView.GetItemObject is declared wrong

Description
Reported by Marc Hoffman; confirmed by Stefan Hoffmeister
In Delphi 3 (5.53), unit ShlObj.Pas, the interface method IShellView.GetItemObject is declared wrong
Solution / workaround
The method needs to be redeclared as:
function GetItemObject(Item: UINT; const iid: TIID;
                       IPtr: Pointer): HResult; stdcall;
Note the added "const" in front of iid.

Bug #422; last modified: 10-Jul-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Unknown Exists Exists Exists Fixed Fixed Fixed
ShlObj

The declaration of the HandleMenuMsg function of IContextMenu2 is wrong

Description
Reported by Brad Stowers; checked by Rune Moberg
ShlObj.pas declares the HandleMenuMsg function of IContextMenu2 as:
    function HandleMenuMsg(uMsg: UINT): HResult; stdcall;
This is incorrect, it should be:
    function HandleMenuMsg(uMsg: UINT; wParam: WPARAM; lParam: LPARAM):
HResult; stdcall;
This bug has been submitted to Borland. It's reference number is 19050.
Solution / workaround
I work around the problem by just redeclaring the whole IContextMenu2 type in the unit I'm working with it:
type
  { Delphi 3 got the declaration of IContextMenu2 wrong in ShlObj unit. }
  IContextMenu2 = interface(IContextMenu)
    [SID_IContextMenu2]
    function HandleMenuMsg(uMsg: UINT; wParam: WPARAM; lParam: LPARAM): 
              HResult; stdcall;
  end;
Rune Moberg: The bug has been fixed in Delphi 4; the declaration now reads exactly like Brad's declaration given here.

Bug #216; last modified: 31-Dec-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Absent Exists Exists Exists Exists Unknown Unknown Fixed
SysUtils

Val thinks that IntToStr(Low(Integer)) is not a valid integer

Description
Reported by Wolfgang Rohdewald; comments by Steve Schafer and Duncan Murdoch
Same behaviour in Delphi 3.0 583
The following code results in an EConvertError "-2147483648 is not a valid integer":
  s := IntToStr(Low(Integer));
  i := StrToInt(s);
Steve Schafer's answer was:
No, it's not a bug. Most compilers (not just Delphi) parse a string representing a negative integer as a negated positive integer. That is, the compiler sees -12345, so it evaluates 12345, and then negates it. This obviously won't work for the minimum integer, since there is no corresponding positive integer.
However I still would say it is a bug. It doesn't help if other compilers have the same bug.

Duncan Murdoch wrote:
The integer value -2147483648 is the smallest legal D2+ integer or D1+ longint. However, the compiler and string conversion routines have problems with it:
The compiler won't accept

 a := -2147483648;
presumably because it parses it as a unary minus operator applied to a positive constant. The positive constant is too large, so you get an "Integer constant too large" error. This is irritating, but not a bug.
However, the Val procedure and the StrToInt functions give an error on it too. They shouldn't; that's a bug.
Step-by-step instruction on how to re-produce the bug:
1. Compile and run the following click event handler:
procedure TForm1.Button1Click(Sender: TObject);
var
  a, code: integer;     // LongInt doesn't help
  s: string;
begin
  // This does not compile, though it would be nice if it did
  a := -2147483648;

  // This will though
  a := $80000000;

  s := IntToStr(a);
  ShowMessage('IntToStr(a) is '+s);  // Outputs a is -2147483648

  Val(s, a, code);
  ShowMessage(Format('Val(s,a,code) gives a=%d and code=%d',[a,code]));
                         // Right value, but an error is reported

  // This will generate an exception
  a := StrToInt(s);
end;
Solution / workaround
By Duncan Murdoch and Peter Ismen
You can use the hex form $80000000 to get the value as a constant in code. The following routines by Peter Ismen fix the conversion bug; they can be used as patches to Sysutils.pas, or dropped into another unit.
function StrToInt(const S: string): Integer;
var
  E: Integer;
begin
  Val(S, Result, E);
  {Bugfix: Start}
   // Val reports an error though the value is legal
  if (E = 12) and (S = '-2147483648') then exit;
  {Bugfix: End}
  if E <> 0 then 
    raise EConvertError.CreateFmt(SInvalidInteger, [S]);
end;

function StrToIntDef(const S: string; Default: Integer): Integer;
var
  E: Integer;
begin
  Val(S, Result, E);
  {Bugfix: Start}
  // Val reports an error though the value is legal
  if (E = 12) and (S = '-2147483648') then exit;
  {Bugfix: End}
  if E <> 0 then Result := Default;
end;

Bug #217; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Gotcha Gotcha Unknown Unknown Unknown Unknown Unknown Unknown
SysUtils

EncodeDate doesn't add 1900 to the year if the year is < 100

Description
The return value of EncodeDate(97, 1, 1) is always less than the value of the Date function, even when the system date is set to 1/1/96.
Solution / workaround
By Arjen Broeze

This is not really a bug. EncodeDate(97, 1, 1) returns the TDateTime value for the year 97. To get a TDateTime value for 1997 use EncodeDate(1997, 1, 1).
It may not be completely consistent, but IMHO EncodeDate should check if the year is below 1900 (shouldn't that be 100? RS) and, if so, add 1900 to it.
On the other hand (Martin Larsson pointed this out): EncodeDate(0, 1, 1) then becomes 1-Jan-1900, right? But what when the same function is used in the year 2000. Should the result then be 1-Jan-2000? The confusion would probably lead to big problems!

Bug #218; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Unknown Exists Unknown Unknown Unknown Unknown Unknown
SysUtils

When you call FindClose a second time, the application hangs completely.

Description
Reported by Sebastian Carvajal; checked by Erik Sperling Johansen
If I call FindClose 2 times my application (D3) hangs completely.
Like:
    FindClose(SearchRec);
    FindClose(SearchRec);
(I have to use Task manager to close it or, if I'm debugging, Ctrl-F2!)

Explanation (by ESJ):
This is not a Delphi bug, but a windows bug. When the invalid handle is passed to Windows FindClose, FindClose gets some value at a fixed offset from the handle value, and then starts counting it down (attempting to validate a handle??). When this fixed value is very large, the FindClose function might be counting down for a long time, and the app appears to be locked even though the function will eventually return.
This bug will only be noticed when the value in question is large, which might be a reason why it hasn't been published earlier.
You can see the value of this variable by entering the following into the watch window, and set a breakpoint on a call to FindClose (SR) PDWORD(SR.FindHandle + $14)^ On first call to FindClose, the value is typically below 0x01000000 On second call, the value is often 0xFEEEFEEE on my computer.
Correct behaviour would be for Windows FindClose to determine that the handle is invalid without the counting down, call SetLastError(ERROR_INVALID_HANDLE) and return FALSE. The SysUtils FindClose function should probably raise an exception in response to the error code.

Sebastian Carvajal added:
In the implementation of FindFirst in SysUtils.pas I found this line:

    if Result <> 0 then FindClose(F);
This makes the following example code (also from SysUtils.pas) fail in those cases where FindFirst doesn't return zero:
... FindFirst is typically used in conjunction with 
FindNext and FindClose as follows:

    Result := FindFirst(Path, Attr, SearchRec);
    while Result = 0 do
    begin
      ProcessSearchRec(SearchRec);
      Result := FindNext(SearchRec);
    end;
    FindClose(SearchRec);
Solution / workaround
Workaround (by Sebastian Carvajal):
Modify the loop as follows:
    Result := FindFirst(Path, Attr, SearchRec);
    If Result = 0      then   //  <--- this line added!
    begin
      while Result = 0 do
      begin
        ProcessSearchRec(SearchRec);
        Result := FindNext(SearchRec);
      end;
      FindClose(SearchRec);
    end;
This way FindClose is called only once.

Fix (by Erik Sperling Johansen):
Modify SysUtils.FindClose by inserting this line at the end of the function:

  F.FindHandle := INVALID_HANDLE_VALUE;
Workaround:
Always explicitly set SR.FindHandle to INVALID_HANDLE_VALUE after calling FindClose.

Bug #220; last modified: 15-Oct-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Exists Exists Exists Fixed Fixed Fixed
SysUtils

StrPCopy only copies first 255 chars

Description
Reported by Tim Knipe; checked by Chris Rankin
In Delphi 2 and 3, StrPCopy only copies the first 255 characters of the Pascal string. The function is implemented thus:
Result := StrLCopy(Dest, PChar(Source), 255);
This is obviously flawed.

Additional notes by checker:
This is definitely a bug; the code for StrPCopy has been clearly written for Delphi 2/3's long-strings since you cannot typecast a short-string to a PChar. The only explanation I can think of for the "255" value is that the original coder wanted the "maximum" length of a string, and was still in a Delphi 1 mindset. In other words, I think s/he intended to write:

Result := StrLCopy(Dest, PChar(Source), High(Integer));
since the docs do claim that StrPCopy() does not perform any range checking. (StrLCopy() copies up to either the first nul-character or the specified length, whichever it reaches first.) In that sense, Mr Knipe's workaround is "better" than StrPCopy() anyway, because you can tell StrLCopy() exactly how large the destination buffer really is.
Solution / workaround
The work around is to use StrLCopy instead, specifying a number larger than 255:
var
  s: String;
  c: array[0..4095] of Char;
begin
  StrPCopy( @c[0], s );  // BROKEN
  StrLCopy( @c[0], PChar(s), Sizeof(c) );  // not broken

Bug #221; 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
SysUtils

StrToDateTime has a bug

Description
Reported by Zsolt Makrai
I've tried to use the StrToDateTime function but I always got an Exception. After debugging the sysutils.pas file in the DELPHI/SOURCE/RTL/SYS directory I understood the reason for this. There is a missing NOT in the fourth line of the StrToDateTime function.
Solution / workaround
It should be possible to modify SysUtils and then rebuild the library, but: "I tried to rebuild the sysutils.dcu but I don't find the SFMT.OBW and FFMT.OBW files. And without these files I can't rebuild the library. Can you help me? How can I rebuild sysutils? How can I correct Borland's mistake??"

Several Bug List readers have given the answer to this question:
You can't, unless you get TASM:
"The answer is 'get TASM'. The SFMT and FFMT files are provided as ASM files, and you'll need TASM 5 (I believe) to compile them. I haven't tried this, so I don't know if that covers everything. It's a shame Borland hasn't provided everything needed for compiling the VCL and RTL. That might be considered a bug, I guess."

Bug #389; last modified: 10-Aug-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
SysUtils

DiskFree and DiskSize report wrong (sometimes negative) values.

Description
Reported by Scott Bennett; checked by Reinier Sterkenburg
I have a large hard drive, there is actually 6.4 Gig free, but DiskFree reports -1720287232 bytes. DiskFree on my other (smaller) hard drives reports correct values. My guess is that the free bytes on my large drive are outside the longint range; help says that diskfree reports an integer value. If my hypothesis is correct, the error will occur with drive space larger than 2 Gig. I see the same problem when I use DiskSize on my drives: it's OK on the smaller drives but a problem on the large one.
Solution / workaround
Workaround: You can check for diskfree < -1, and if it is, assume it's because there's actually a lot of free space.

Note from checker: This works only in the special case that the actual amount is between 2 GB amd 4 GB (or between 6 and 8 etc).

The real solution would be that all these functions will work with 64 bit integers. This has been implemented in Delphi 4.


Bug #507; last modified: 11-Nov-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
SysUtils

Using (very) long strings for an Exception message can make the IDE crash.

Description
Reported by Sergei Zhyzhyn; checked by Reinier Sterkenburg
Try this: D3.02 C/S, Professional (it also 'works' under D 4.02, RPS)
  1. Create a new Application
  2. Drop a button
  3. On click:
    procedure TForm1.Button1Click(Sender: TObject);
      var longS: string; i: integer;
    begin
      longS := '';
      for i := 1 to 200 do                     //2k string
        longS := longS + '1234567890';
      raise Exception.Create(longS);
    end;
  4. Run from Delphi with integrated debugger on.
This crashes Delphi 10 out of 10 times on my box and a couple others I tested and sometimes I need to reboot my NT4.0 SP3 as well(!)

Cause:
When you look at the code of Exception.Create (unit SysUtils), it turns out that it uses a local buffer of 1024 bytes to store the message string. When a longer string is passed, it trashes the stack, which causes unpredictable errors, among which an immediate shutdown of Delphi appears to be one of the possibilities.


Bug #371; last modified: 24-Dec-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Exists Exists Exists Exists Exists Exists Exists Exists
SysUtils - Format

Using the FORMAT statement with an integer specifier larger than 15 (ie: '%.16d' ) does not pad with zeros as expected.

Description
Reported by Ken Henke; checked by Reinier Sterkenburg
In the past I've accomplished zero-padding by using the FORMAT statement like so:
  nTemp:= 3;
  sTemp:= Format('%.6d', [nTemp])
  {sTemp now equals '000003'}
But, when I tried to do that on a field with 16 digits, it seems to ignore the specifier. For example:
  nTemp:= 3;
  sTemp:= Format('%.16d', [nTemp])
  {sTemp now equals '3' instead of '0000000000000003'}
This is a bug because the FORMAT specifier '%.16d' should be padding with zeros. There isn't a documented reason why this shouldn't work on specifiers larger than 15.
Solution / workaround
Not for the FORMAT command specifically. Can use other string-functions to achieve the desired effect.

Bug #206; last modified: 10-Aug-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
System

The Round() function can give unexpected results

Description
Reported by Jens P. Hansen; confirmed in Delphi3 by Arentjan Banck
I have catgorized this bug as a Gotcha (as opposed to 'bug') because I kept getting email from people telling me that the current behaviour is 'as designed' and that it is merely a documentation bug.
IMHO however, if anyone 'designed' this behaviour, I think it is the processor designer, not the ones who made the compiler; I think the documentation reflects what the compiler designers had in mind. (Reinier Sterkenburg).

The 'problem' exists in all versions of Delphi up to Delphi 3.0:
The behaviour of the Round() function has changed between BP 7.0 and Delphi 1 but Borland hasn't updated the Help/specification of the function. The problem is with numbers that have a fractional part of exactly 0.5. According to the On line help:

"The Round function rounds a real-type value to an integer-type value. X is a real-type expression. Round returns a Longint value that is the value of X rounded to the nearest whole number. If X is exactly halfway between two whole numbers, the result is the number with the greatest absolute magnitude."
This is not correct. In fact, Round rounds to the nearest even number. Rounding to the nearest even number appears to be known under the name of 'Banker's algorithm' and many people consider this to be the 'proper way' of rounding and therefore think this behaviour is 'as designed'.

Bug #207; last modified: 7-Jan-99
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Absent Absent Exists Unknown Unknown Unknown Unknown Fixed
System

ErrorAddr is always NIL in finalization section

Description
Reported by Stefan Hoffmeister
Contrary to the Delphi 3 documentation ErrorAddr does not always point to the address behind a runtime error:
...
  RunError(210);
...
finalization
  if ErrorAddr <> nil then { has there been a runtime error? }
  begin
    MessageBox(0, 'Error condition detected', 'Error', mb_ok);
    ErrorAddr := nil;
  end;
end.
Delphi 3 will not show the dialog. Using exactly the same code in Delphi 2.01 the dialog will be shown (thanks to Duncan Murdoch for supplying the information), so we have at least found a documentation error and an incompatibility between Delphi 3 and Delphi 2.01.

The root of the problem seems to be in SYSTEM.PAS of Delphi 3 which in routine procedure _Halt0; features this code:

  if ErrorAddr <> nil then
  begin
    MakeErrorMessage;
    if IsConsole then
      WriteLn(PChar(@runErrMsg))
    else
      MessageBox(0, runErrMsg, errCaption, 0);
 ErrorAddr := nil;
  end;
Note the interesting indentation of the second last line - it appears as if it has been added late in the development cycle, possibly without taking into account the effect of this change. Unless there is a major problem with ErrorAddr <> nil, which the analysis has not detected, it seems to be well justified to call this a bug in SYSTEM.PAS.
Solution / workaround
When exiting (because of an error condition) _Halt0 calls all exit procedures before going through the above code. So adding an exit procedure and testing ErrorAddr there works around the bug:
  procedure TestError; {$IFNDEF Win32} far; {$ENDIF}
  begin
    if ErrorAddr <> nil then { has there been a runtime error? }
    begin
      ...
  end;

  initialialization
    AddExitProc(TestError);
  end.

Bug #208; 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
System

The AssignFile / Reset procedures accept '' (empty string) for a file name and then hang up your system.

Description
You can hang up your system with:
var
  s1, s2: String;
  f: TextFile;
begin
  s1 := '';
  AssignFile(f, s1);
  Reset(f);
  Readln(f, s2);
  {here system hang up completely without any run-time error!!!}
  Close(f)
end.

Bug #209; 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
System

There is an undocumented difference between how Delphi 1's Readln handles error conditions and how Delphi 2 and 3 do this.

Description
Reported by Jacques Nomssi Nzal; checked by Duncan Murdoch
When doing a Readln on a text file, all Delphi versions generate an IO error if the text in the file can't be converted to the numeric value being read. However, in D1, the Readln aborts at the first error. In D2 and D3, as many tokens as arguments to Readln are first read, and then the conversions are attempted. The result is that the different versions files read different amounts when comments are inserted in a mostly numeric file.
Step-by-step instruction on how to re-produce the problem:
Compile and run the code below. (It should be set as a console app in D2 and D3). The data file and results are shown following it:
program _01read;
uses
  sysutils
  {$ifdef ver80}
  ,wincrt
  {$endif}
  ;
var
  i,j : integer;
  infile : text;
begin
  assign(infile,'_01read.dat');
  reset(infile);
  while not eof(infile) do
  begin
    {$i-} readln(infile,i,j); {$i+}
    if ioresult <> 0 then
      writeln('Error reading line')
    else
      writeln('Read values ',i:3,j:3);
  end;
  readln;
  close(infile);
end.
Use this data as _01read.dat (the semicolons will cause parsing errors):
;
1 1
2 2
3 3
;
4 4
5 5
6 6
To produce this output in D2 and D3:
Error reading line
Read values   2  2
Read values   3  3
Error reading line
Read values   5  5
Read values   6  6
and this output in D1:
Error reading line
Read values   1  1
Read values   2  2
Read values   3  3
Error reading line
Read values   4  4
Read values   5  5
Read values   6  6
Solution / workaround
(Workaround):
Don't rely on Readln to handle conversion errors; read the lines as strings, and parse them yourself.

Bug #210; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Fixed Exists Fixed Fixed Fixed Fixed Fixed Fixed
System

The Reset procedure doesn't work properly on files that are already open.

Description
Resetting a textfile with the reset procedure doesn't work properly on files that are already open. If a file F is already open, then reset(F) should position the record pointer to the beginning of the file so that the next readln will read the first line of the file. This doesn't work in Delphi 2.0 but it does work in Delphi 1.0. Reset(F) seems to have no effect on the record pointer for opened files in Delphi 2.0.
Solution / workaround
By Duncan Murdoch

It doesn't help to explicitly close the file; the problem is that Reset forgets to zero the Bufptr and Bufend fields in the text record, so after a reset you get a bit of leftover stuff from the old buffer, and then you start again at the beginning of the file. The easiest workaround is to call AssignFile again; this zeros both fields. If you don't want to do that, you can just use the TextRec typecast to set TextRec(f).Bufend to zero, and things should work.
Flush might work too; I haven't tried.

Bug #211; last modified: 29-Oct-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Absent Exists Fixed Fixed Fixed Fixed Fixed Fixed
System

There is another bug in the Reset procedure when sharing files.

Description
Reported by Matej Trampus and Duncan Murdoch
(MT:) I need to have a text file opened at the same time in two different parts of a program, but Delphi 2.0 reports an I/O error. Here is a piece of code which reproduces the problem:
 
var f1, f2: text;
begin
   assign(f1, 'MYFILE');
   assign(f2, 'MYFILE'); 
   reset(f1); 
   reset(f2);  {<--- I/O error 32 - sharing violation}
end;
(DM:) I can confirm the bug.
(MT:)
@@calledByReset:
     MOV     EAX,GENERIC_READ        ; open for read
     MOV     EDX,FILE_SHARE_WRITE    ;<--- I THINK, THERE SHOULD BE
FILE_SHARE_READ !!!!
(DM:) I think it should be FILE_SHARE_WRITE+FILE_SHARE_READ, to allow *either* a read or a write.
(MT:) Similar, when D2 is opening a text file for writing it opens it with FILE_SHARE_READ. Is this OK or am I missing something?
(DM:) I think this one is okay. It says that other read attempts should succeed, but other write attempts should fail. That makes sense to me.
Solution / workaround
Kerry Sanders notified us of the following:
: This problem can be fixed by downloading and installing the updated system.dcu for Delphi 2.01 from Inprise's Web site. The updated DCU file can be downloaded from this page: http://www.inprise.com/devsupport/delphi/downloads/index.html
The file itself can be downloaded using this link: http://www.inprise.com/devsupport/delphi/download_files/system.zip

Bug #213; last modified: 10-Aug-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Absent Exists Unknown Unknown Fixed Fixed Fixed Fixed
System

Read/Readln fails to read a number from a text file if it has a trailing decimal point.

Description
Reported by Alan J. Horowitz
For example:
10.0
10
will be read successfully, but
10.
will result in an error.

Delphi 1.0 and all editions of Borland Pascal and Turbo Pascal read the number correctly. I do not know of an easy work-around. Maybe someone reading this page has suggestions.

Bug #214; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Fixed Fixed Fixed Fixed Fixed Fixed
System

Access Violation in procedure SetString for a ShortString when the copy from buffer is empty.

Description
Reported by Ken Knopfli
Possibility of Access Violation in procedure 'SetString' for a ShortString is not made clear in documentation.
Documentation of SetString procedure
Note that there is no documented way to use 'SetString' on *short* string variables so that the contents are left uninitialized for later insertion! The documentation could be misinterpreted. It is also possible that Borland simply overlooked this combination of parameters.
In practice, an Access Violation is caused if 'buffer' = nil for short strings.
Reproducing the effect:
procedure TForm1.Button1StartClick(Sender: TObject);
var
  S : ShortString;  //this causes Access Violation
//  S : AnsiString; //this will *always* work
  buffer : PChar;
begin
  buffer:= nil;
  SetString(S, buffer, 10);  //Access Violation on ShortString
end;
Solution / workaround
Be aware of the problem and trap for a nil buffer. Alternatively, use 'SetLength' on short strings, instead of 'SetString'.

Bug #215; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Gotcha Gotcha Unknown Unknown Unknown Unknown Unknown Unknown
System

The Val function fails on strings with spaces at the right

Description
The Val function fails on strings with spaces at the right. Try the following code:
  Val('   1.10    ',  x,  code);
Delphi 1.0 gives: x=0; code=7. (Delphi 2.0 gives a different result, but also not the result we'd like to see: x=1.1; code=0).
The problem lies on the extra blanks on the right.
But this is not a bug, it's documented. Quote from the Help file:
"The Val function converts the string value S to its numeric representation, as if it were read from a text file with Read."
And spaces are not characters used in numbers...
Solution / workaround
Delete the blanks (at least the ones at the right) before calling Val

Bug #223; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Fixed Fixed Fixed Fixed Fixed Fixed
TLHelp32

Wrong length of szModule field of TModuleEntry32

Description
Reported by Rod Smith
The "szModule" field in the TModuleEntry32 structure is one byte too long. It is supposed to be MAX_MODULE_NAME32 + 1 characters long, but they defined it as
szModule: array[0..MAX_MODULE_NAME32 + 1] of Char;
which makes it MAX_MODULE_NAME32 + 2 characters long.
Solution / workaround
To detour around this bug, I declared a PChar, set it equal to szModule, and then decremented it if the character it was pointing to is a colon. For example,
  var p: PChar;
  p := ModuleEntry32.szModule;
  if p^ = ':' then 
    Dec (p);
This code will work even when the bug is fixed, because the first character of "szModule" is supposed to be a drive letter.

Bug #224; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Fixed Fixed Fixed Fixed Fixed Fixed
TLHelp32

Wrong declaration: PModuleEntry32

Description
Reported by Rod Smith
The PModuleEntry32 type is defined as
type PModuleEntry32 = ^TProcessEntry32;
it should defined as
type PModuleEntry32 = ^TModuleEntry32;
Solution / workaround
I detoured around this by redeclaring "PModuleEntry32" in my unit.

Bug #232; last modified: 28-Jul-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Fixed Fixed Fixed Fixed Fixed
WinSvc

Declaration of EnumServicesStatus is wrong

Description
Reported by Tomi Nurma
There is a typing error in WinSvc.pas in Delphi 3.0. The declaration of the EnumServicesStatus function is not correct:
Currently it is declared as 'EnumServiceStatusA' (note the missing 's' in between), it should be 'EnumServicesStatusA'.
Solution / workaround
Either modify WinSvc.pas directly (see the notes on Delphi 3 packages and modifying the VCL) and replace the erratic lines with:
function EnumServicesStatusA;    external advapi32 name 'EnumServicesStatusA';
function EnumServicesStatusW;    external advapi32 name 'EnumServicesStatusW';
function EnumServicesStatus;    external advapi32 name 'EnumServicesStatusA';
or declare your import functions for, but with a different name than, EnumServicesStatus, EnumServicesStatusA, and EnumServicesStatusW.

Bug #225; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Fixed Fixed Exists Unknown Unknown Unknown Unknown Unknown
Windows

BroadcastSystemMessage maps to a wrong function

Description
Reported by Ken White; checked by Stefan Hoffmeister
It seems that there is an error in Delphi 3.0's Windows.Pas file; it prototypes three types of BroadcastSystemMessage functions: The problem is that Windows 95 does not have the "A" and "W" versions of these calls, and as you can see above BroadcastSystemMessage is mapped to the "A" flavour!
Solution / workaround
Declare your own function:
function MyBroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD; 
           uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
in the header and link it to the system:
function MyBroadcastSystemMessage; external user32 
                                   name "BroadcastSystemMessage";
Use MyBroadcastSystemMessage instead of BroadcastSystemMessage...

A better solution will probably be to remap the call only if the platform is Windows 95...

Bug #226; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Fixed Fixed Gotcha Unknown Unknown Unknown Unknown Unknown
Windows

You can't use the FindFirstChangeNotification as declared in Delphi 3 Windows.pas.

Description
Reported by Tim Knipe; checked and edited by Chris Rankin
(TK): You can't use the FindFirstChangeNotification as declared in Delphi 3 Windows.pas.
While technically a Windows bug, D3 will be blamed because it worked in D2 and broke in D3.
The declaration in Windows.pas is exactly the same as Delphi 2 -
function FindFirstChangeNotification(lpPathName: PChar;
                                     bWatchSubtree: BOOL;
                                     dwNotifyFilter: DWORD): THandle; 
              stdcall; external kernel32 name 'FindFirstChangeNotificationA';
However, Delphi 3 has changed the definition of TRUE for ByteBool, WordBool and LongBool (Bool). For ActiveX and Automation compatibility FALSE=0, TRUE=-1. In Delphi 2 FALSE=0, TRUE=1. Now, for a parameter that is supposed to be BOOL, this shouldn't make any difference. In the case of FindFirstChangeNotification, if you pass TRUE as [bWatchSubtree], the function always returns INVALID_HANDLE_VALUE and GetLastError returns 'Incorrect parameter'.
Solution / workaround
(CR,TK): There are a number of workarounds, of various degrees of ugliness. By far the simplest is to pass either "BOOL(1)" or "BOOL(Boolean(True))" as the second parameter. However these typecasting games don't work if you attempt to use a BOOL variable instead of a constant. Another alternative is to provide a new interface to the FindFirstChangeNotification() function:
{$Z4 Must use DWords for this enumeration}
type TWinBool = (winFalse, winTrue);

function _FindFirstChangeNotification(lpPathName: PChar;
                                      bWatchSubtree: TWinBool;
                                      dwNotifyFilter: DWORD): THandle; 
       stdcall; external kernel32  name 'FindFirstChangeNotificationA';
Here's a sample project that demonstrates the above: FileNotify.dpr

Bug #227; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Fixed Fixed Fixed Fixed Fixed Fixed
Windows

The declaration of TCharInfo is not correct

Description
Reported by Pete Olson
Consider the following declarations, the first from Delphi help and the second from WINDOWS.PAS in the Delphi RTL.
typedef struct _CHAR_INFO { // chi

    union {                /* Unicode or ANSI character  */
        WCHAR UnicodeChar;
        CHAR AsciiChar;
    } Char;
    WORD Attributes;       // text and background colors
} CHAR_INFO, *PCHAR_INFO;


type
  PCharInfo = ^TCharInfo;
  TCharInfo = packed record
    case Integer of
      0: (
        UnicodeChar: WCHAR);
      1: (
        AsciiChar: CHAR;
        Attributes: Word);
  end;
These declaration are not equivalent; in fact sizeof(CHAR_INFO)=4 and sizeof(TCharInfo)=3. The C version from the help file appears to correct. A better translation would be:
type
  PCharInfo = ^TCharInfo;
  TCharInfo = packed record
    case Integer of
      0: (
        UnicodeChar: WCHAR;
        Attributes: Word);
      1: (
        AsciiChar: CHAR);
     end;
which lays out the record correctly, and allows proper access to the components.

Bug #228; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Fixed Fixed Fixed Fixed Fixed Fixed
Windows

The declaration of TKeyEventRecord is not correct

Description
Reported by Chris Rankin
In the declaration of TKeyEventRecord, the same mistake has been made as in TCharInfo (see previous description)

Bug #231; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Fixed Fixed Fixed Fixed Fixed Fixed
Windows

Type "TEnumLogFontEx" needed with the EnumFontFamiliesEx API function as defined is hard to use.

Description
Reported by Jani Järvinen
The type "TEnumLogFontEx" needed with the EnumFontFamiliesEx API function as defined is hard to use. All extended fields of the record are defined as arrays of bytes, although they contain normal textual information (arrays of chars).
[Please note: The actual porting from C/C++ headers 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:
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;
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 / workaround
Solution #1: Define a temporary type to equal an array of chars and do a typecast.

Solution #1 example:
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;
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:
{
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;

Bug #391; last modified: 13-Apr-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Exists Exists Exists Exists Exists Unknown Unknown Unknown
Windows

The parameter value -1 is specified to give the standard sound but the parameter is declared as a Word and therefore -1 is illegal.

Description
Reported by Chris Cheney; checked by Reinier Sterkenburg
This is completely trivial but could cause the novice programmer problems and is certainly a documentation bug.
The reason that this bug is not causing problems in Delphi 2 and Delphi 3 is because that another bug compensates for this:
The 'Windows' type UINT is translated (in unit Windows.pas) into Integer, which of course does allow negative numbers.
Solution / workaround
Workaround: use $FFFF or 65535.

Bug #414; last modified: 14-May-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A N/A Exists Exists Exists Fixed Fixed Fixed
Windows

The GetDiskFreeSpaceEx functions are declared with wrong parameter types in the Windows unit.

Description
Reported by Lookin I.; checked by Hallvard Vassbotn
In the Windows.pas unit the following functions are declared:
function GetDiskFreeSpaceExA(lpDirectoryName: PAnsiChar; 
  var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes: Integer;
  lpTotalNumberOfFreeBytes: PInteger): BOOL; stdcall;
function GetDiskFreeSpaceExW(lpDirectoryName: PWideChar; 
  var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes: Integer;
  lpTotalNumberOfFreeBytes: PInteger): BOOL; stdcall;
function GetDiskFreeSpaceEx(lpDirectoryName: PChar; 
  var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes: Integer;
  lpTotalNumberOfFreeBytes: PInteger): BOOL; stdcall;
The problem with these declaration is that they do not match Microsoft's implementation or documentation of the functions. The last three parameters should be of the type TLargeInteger instead of Integer. Because these parameters are sent by reference and are modified in the functions, calling them with the given declarations can cause access violations. Do _not_ call these functions through the declarations given in the Windows unit.
Solution / workaround
The workaround is to create new declarations for these functions. Put them in a separate unit and include this unit _after_ the standard Windows unit (or simply use unit-name qualification when calling the functions). The correct declarations are:
unit WinFix;

interface

uses Windows;

function GetDiskFreeSpaceExA(lpDirectoryName: PAnsiChar;
  var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes:
TLargeInteger;
  lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall;
function GetDiskFreeSpaceExW(lpDirectoryName: PWideChar;
  var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes:
TLargeInteger;
  lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall;
function GetDiskFreeSpaceEx(lpDirectoryName: PChar;
  var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes:
TLargeInteger;
  lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall;

implementation

function GetDiskFreeSpaceExA; external kernel32 name
'GetDiskFreeSpaceExA';
function GetDiskFreeSpaceExW; external kernel32 name
'GetDiskFreeSpaceExW';
function GetDiskFreeSpaceEx; external kernel32 name
'GetDiskFreeSpaceExA';

end.
Note that first two parameters are sent as var parameters, while the last is sent as a pointer to TLargeInteger. The reason is that the lpTotalNumberOfFreeBytes parameter can be set to nil if we are not interested in getting the result of it. Also note that these functions are only supported on NT4.0 and Win95 OSR2 and later. See Microsoft's documentation for further details.

To reproduce the bug and test the workaround, use this simple test unit:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses
  WinFix;

type
  TTestDiskFreeRec = record
    lpFreeBytesAvailableToCaller,
    Safety1,
    lpTotalNumberOfBytes,
    Safety2,
    lpTotalNumberOfFreeBytes,
    Safety3 : Integer;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  TestDiskFreeRec: TTestDiskFreeRec;
begin
  FillChar(TestDiskFreeRec, SizeOf(TestDiskFreeRec), 255);
  with TestDiskFreeRec do
    if Windows.GetDiskFreeSpaceEx('D:\', lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes,
      @lpTotalNumberOfFreeBytes) then
    begin
      // Note that the Safety fields are overwritten!
      Writeln('lpFreeBytesAvailableToCaller: ',
lpFreeBytesAvailableToCaller);
      Writeln('Safety1: ',                         Safety1);
      Writeln('lpTotalNumberOfBytes: ',
lpTotalNumberOfBytes);
      Writeln('Safety2: ',                         Safety2);
      Writeln('lpTotalNumberOfFreeBytes: ',
lpTotalNumberOfFreeBytes);
      Writeln('Safety3: ',                         Safety3);
    end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  lpFreeBytesAvailableToCaller,
  lpTotalNumberOfBytes,
  lpTotalNumberOfFreeBytes : TLargeInteger;
begin
  if WinFix.GetDiskFreeSpaceEx('D:\', lpFreeBytesAvailableToCaller,
lpTotalNumberOfBytes,
    @lpTotalNumberOfFreeBytes) then
  begin
    Writeln('lpFreeBytesAvailableToCaller: ',
lpFreeBytesAvailableToCaller.QuadPart:0:0);
    Writeln('lpTotalNumberOfBytes: ',
lpTotalNumberOfBytes.QuadPart:0:0);
    Writeln('lpTotalNumberOfFreeBytes: ',
lpTotalNumberOfFreeBytes.QuadPart:0:0);
  end;
end;

end.
Compile as a console application. Pressing button1 shows that the original declarations in Windows cause memory to be overwritten (the Safety fields). Pressing button2 demonstrates how to call the correct versions of the APIs.

Bug #497; last modified: 28-Oct-98
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
Unknown Exists Exists Exists Exists Exists Exists Exists
Windows

There is a bug in the WINAPI definition for the function SetWinMetaFileBits

Description
Reported by David Block
It's as follows (Delphi 3.01; WINDOWS.PAS):
The definition for the API function "SetWinMetaFileBits" is defined as:
        function SetWinMetaFileBits(p1: UINT; p2: PChar; p3: HDC;
                const p4: TMetaFilePict): HENHMETAFILE; stdcall;
the fourth parameter 'p4' should be defined as:
        const p4: PMetaFilePict
Solution / workaround
In any file you need to call this function from, add the correct definition:
 function SetWinMetaFileBits(p1: UINT; p2: PChar; p3: HDC;
   const p4: PMetaFilePict): HENHMETAFILE; stdcall;
in the interface section (before the implementation line, and add:
  function SetWinMetaFileBits; external gdi32 name 'SetWinMetaFileBits';
to the implementation section, after any uses clause that might be listed.

Bug #229; last modified: before April 1998
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02
N/A Exists Unknown Unknown Unknown Unknown Unknown Unknown
Windows?

The routines in both NETAPI and NETAPI32 are uncallable from Delphi.

Description
1. Windows 95 Programming

If you are running Windows 95, do a quickview on NETAPI.DLL (in the SYSTEM directory). Scroll down and look at the exports - one of which is NetRemoteDOT which returns the time and date from a remote NT/LanMan server. Now do a Quickview of NETAPI32.DLL and you will see 1 routine = NETBIOS.

2: Windows NT Programming
Assuming you get around the loss of all those 16 bit calls, we move to NT programming where NETAPI32.DLL does have all the calls - yet there is no entries in WINDOWS.PAS (/BORLAND/Delphi 2.0/Source/RTL/Win) for these calls.

3: And the loser is ...
Now it has been shown that you can't program 95 or NT NETAPI calls (need to use Delphi 1.0 - for Windows 95 and write your own interface for Win/NT) - look for NetRemoteDOT in the help, sure enough it is there..


Bug #233; last modified: 20-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
general

Not bugs but very annoying and sometimes hard to track down, so worth a place in these Bug Lists are the many duplicate names. Some examples are: DeleteFile, FindClose, TBitmap, and there are more.

Description
Very annoying (but technically these are not 'bugs') is the fact that there are name conflicts among identifiers that are exported by more than one unit. These mostly result in 'Incompatible types' compiler messages.

Examples: There are two ways to resolve these situations:
  1. Prefix the identifier with the unit form which it must be 'imported', e.g.: SysUtils.Abort
  2. Less elegant, but works too: put the unit from which the identifier must be imported later in the uses clause than the other unit which exports the identifier.
If anybody knows more of these cases, please mail them!

Index page
The Delphi Bug Lists are maintained by Reinier Sterkenburg, with help from the DeBug Team.
All feedback is appreciated. See also the feedback section of the Delphi Bug List home page.