In a 32-bit VCL Application in Windows 10 in Delphi 11 Alexandria, I have a TListView
, where I set the ListView's Header Font Style to BOLD in the FormCreate handler:
private
FhHeaderFont: THandle;
procedure TformMain.FormCreate(Sender: TObject);
begin
...
SetColumnHeaderFontBold;
end;
procedure TformMain.SetColumnHeaderFontBold;
const
LVM_GETHEADER = Winapi.CommCtrl.LVM_FIRST + 31;
var
LF: Winapi.Windows.TLogFont;
hHeader, hCurrFont, hOldFont: THandle;
begin
hHeader := Winapi.Windows.SendMessage(lvMRUProjects.Handle, LVM_GETHEADER, 0, 0);
hCurrFont := Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0); // ERangeError
if GetObject(hCurrFont, SizeOf(LF), Addr(LF)) > 0 then
begin
LF.lfWeight := Winapi.Windows.FW_BOLD;
FhHeaderFont := Winapi.Windows.CreateFontIndirect(LF);
hOldFont := Winapi.Windows.SelectObject(hHeader, FhHeaderFont);
Winapi.Windows.SendMessage(hHeader, winapi.Messages.WM_SETFONT, FhHeaderFont, 1);
end;
end;
Now, I RANDOMLY get an ERangeError
in this line when starting the program:
hCurrFont := Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0); // ERangeError
This is the Eurekalog call-stack:
This is the error message:
Range check error at (004EDAED){MyApp.exe} [008EDAED] MainForm.TformMain.SetColumnHeaderFontBold (Line 616, "MainForm.pas") + $13.
What is causing this error and how can I avoid it?
There are two problems here:
For the first issue, each variable that you declare as THandle
is declared incorrectly. THandle
is used for kernel handles, it is the Delphi type corresponding to HANDLE
in the Windows C headers. None of the handles in your code is a kernel handle, they are all user mode objects.
FhHeaderFont
, hCurrFont
and hOldFont
should all be declared as HFONT
. hHeader
should be declared as HWND
.
The issue that is causing your range check error is the second one in my list. If you consider what SendMessage
has to do, it is used for any Windows message. And these messages work with a huge range of different data types. This means that there will sometimes be mismatches between the generic integer types that SendMessage
accepts and returns, and the types of the variables used for parameters and return values. These mismatches should be dealt with by type casting.
The cause of the range check error is that SendMessage
returns LRESULT
which is a signed integer, pointer sized. But THandle
is an unsigned integer, pointer sized. Even when you correct this by using HFONT
, then the same error can arise because HFONT
is also an unsigned integer, pointer sized.
Deal with this by type-casting the return value:
hCurrFont := HFONT(Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0));
Likewise you should do the same for the other return value assignment:
hHeader := HWND(...);
Strictly speaking, it would be best to do the same when passing FhHeaderFont
as wParam
in the final call to SendMessage
. I think I would write that as:
Winapi.Windows.SendMessage(hHeader, winapi.Messages.WM_SETFONT, WPARAM(FhHeaderFont), 1);
One final note is that LVM_GETHEADER
is defined in Winapi.CommCtrl
and should not be re-defined here.