Recently I got bitten by the 2013 reported http://qc.embarcadero.com/wc/qcmain.aspx?d=112101 (too bad the site is gone and the WayBack machine doesn’t have it archived) as a result of [WayBack] delphi – Why do I get access violations when a control’s class name is very, very long? – Stack Overflow.
It got reentered as [RSP-18399] Buffer overflow in TWinControl.CreateParams() – Embarcadero Technologies but since that requires logon, it’s not search machine indexed so it’s very hard to find unless you know where to look.
So I spent a quite some time to find out what was wrong:
Since Delphi 1, the [WayBack]
Controls.TCreateParams
Record has a 64-byteWinClassName
field that’s blindingly copied by theTWinControl.CreateParams
without range checking.The structure is used by the [WayBack]
TWinControl.CreateWnd
Method to call the Windows API [WayBack] RegisterClass function that takes a [WayBack] WNDCLASS structure with alpszClassName
field that supports up to 256 characters and it fails when it’s longer.
That overwrite cause spurious other errors depending on the memory that gets overwritten. It took almost a day to figure out the cause of the error was this field, then an hour or to track that down to the long class names created by generic code.
To my surprise, I found back [WayBack] This issue caused coworkers and me quite a few hours wasted:Long story short – refactor some forms/frames to class names longer than 64 chars and boom… – Stefan Glienke – Google+.
As of Delphi 8 (yes, that version that a lot of people want to forget, but did bring a few good things), the structure was defined as below, and the code intialising also got improved:
Params.WinClassName := ClassName;
...
Params.WinClassName := Format('%s.%d', [Params.WinClassName, AppDomain.CurrentDomain.GetHashCode]);
So there it’s a string that – if it is too long – will get rejected by the Windows API anyway just like the native Delphi VCL implementation should have done 20+ years ago.
The sad part for FMX users: that structure and code got blindingly copied to the FMX.Controls.Win
unit.
{$IF DEFINED(CLR)} TCreateParams = record Caption: string; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWND; Param: IntPtr; WindowClass: TWndClassInfo; WndProc: TFNWndProc; WinClassName: string; end; {$ELSE} TCreateParams = record Caption: PChar; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWnd; Param: Pointer; WindowClass: TWndClass; WinClassName: array[0..63] of Char; end; {$ENDIF}
–jeroen