设置 TListView Header 字体样式时出现范围检查错误
Range check error when setting TListView Header Font Style
在 Windows 10 in Delphi 11 Alexandria 中的 32 位 VCL 应用程序中,我有一个 TListView
,我将 ListView 的 Header 字体样式设置为在 FormCreate 处理程序中加粗:
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;
现在,我在启动程序时在这一行随机得到一个 ERangeError
:
hCurrFont := Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0); // ERangeError
这是 Eurekalog call-stack:
这是错误信息:
Range check error at (004EDAED){MyApp.exe} [008EDAED] MainForm.TformMain.SetColumnHeaderFontBold (Line 616, "MainForm.pas") + .
是什么导致了这个错误,我该如何避免?
这里有两个问题:
- 您用错误的类型声明了多个变量。
- 处理 Windows 消息时有时需要使用类型转换。
对于第一个问题,您声明为 THandle
的每个变量都声明不正确。 THandle
用于内核句柄,它是 Delphi 类型对应 Windows C 头文件中的 HANDLE
。 None 你代码中的句柄是内核句柄,它们都是用户模式对象。
FhHeaderFont
、hCurrFont
和 hOldFont
都应声明为 HFONT
。 hHeader
应声明为 HWND
.
导致范围检查错误的问题是我列表中的第二个问题。如果您考虑 SendMessage
必须做什么,它用于任何 Windows 消息。这些消息适用于大量不同的数据类型。这意味着 SendMessage
接受的通用整数类型和 return 之间有时会存在不匹配,以及用于参数和 return 值的变量类型。这些不匹配应该通过类型转换来处理。
范围检查错误的原因是 SendMessage
returns LRESULT
这是一个带符号的整数,指针大小。但是 THandle
是一个无符号整数,指针大小。即使当您使用 HFONT
更正此错误时,也会出现相同的错误,因为 HFONT
也是一个无符号整数,指针大小。
通过类型转换 return 值来处理此问题:
hCurrFont := HFONT(Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0));
同样你应该对另一个 return 赋值做同样的事情:
hHeader := HWND(...);
严格来说,在最终调用 SendMessage
时传递 FhHeaderFont
和 wParam
时最好做同样的事情。我想我会写成:
Winapi.Windows.SendMessage(hHeader, winapi.Messages.WM_SETFONT, WPARAM(FhHeaderFont), 1);
最后要注意的是 LVM_GETHEADER
在 Winapi.CommCtrl
中定义,不应在此处重新定义。
在 Windows 10 in Delphi 11 Alexandria 中的 32 位 VCL 应用程序中,我有一个 TListView
,我将 ListView 的 Header 字体样式设置为在 FormCreate 处理程序中加粗:
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;
现在,我在启动程序时在这一行随机得到一个 ERangeError
:
hCurrFont := Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0); // ERangeError
这是 Eurekalog call-stack:
这是错误信息:
Range check error at (004EDAED){MyApp.exe} [008EDAED] MainForm.TformMain.SetColumnHeaderFontBold (Line 616, "MainForm.pas") + .
是什么导致了这个错误,我该如何避免?
这里有两个问题:
- 您用错误的类型声明了多个变量。
- 处理 Windows 消息时有时需要使用类型转换。
对于第一个问题,您声明为 THandle
的每个变量都声明不正确。 THandle
用于内核句柄,它是 Delphi 类型对应 Windows C 头文件中的 HANDLE
。 None 你代码中的句柄是内核句柄,它们都是用户模式对象。
FhHeaderFont
、hCurrFont
和 hOldFont
都应声明为 HFONT
。 hHeader
应声明为 HWND
.
导致范围检查错误的问题是我列表中的第二个问题。如果您考虑 SendMessage
必须做什么,它用于任何 Windows 消息。这些消息适用于大量不同的数据类型。这意味着 SendMessage
接受的通用整数类型和 return 之间有时会存在不匹配,以及用于参数和 return 值的变量类型。这些不匹配应该通过类型转换来处理。
范围检查错误的原因是 SendMessage
returns LRESULT
这是一个带符号的整数,指针大小。但是 THandle
是一个无符号整数,指针大小。即使当您使用 HFONT
更正此错误时,也会出现相同的错误,因为 HFONT
也是一个无符号整数,指针大小。
通过类型转换 return 值来处理此问题:
hCurrFont := HFONT(Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0));
同样你应该对另一个 return 赋值做同样的事情:
hHeader := HWND(...);
严格来说,在最终调用 SendMessage
时传递 FhHeaderFont
和 wParam
时最好做同样的事情。我想我会写成:
Winapi.Windows.SendMessage(hHeader, winapi.Messages.WM_SETFONT, WPARAM(FhHeaderFont), 1);
最后要注意的是 LVM_GETHEADER
在 Winapi.CommCtrl
中定义,不应在此处重新定义。