设置 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") + .

是什么导致了这个错误,我该如何避免?

这里有两个问题:

  1. 您用错误的类型声明了多个变量。
  2. 处理 Windows 消息时有时需要使用类型转换。

对于第一个问题,您声明为 THandle 的每个变量都声明不正确。 THandle 用于内核句柄,它是 Delphi 类型对应 Windows C 头文件中的 HANDLE。 None 你代码中的句柄是内核句柄,它们都是用户模式对象。

FhHeaderFonthCurrFonthOldFont 都应声明为 HFONThHeader 应声明为 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 时传递 FhHeaderFontwParam 时最好做同样的事情。我想我会写成:

Winapi.Windows.SendMessage(hHeader, winapi.Messages.WM_SETFONT, WPARAM(FhHeaderFont), 1);

最后要注意的是 LVM_GETHEADERWinapi.CommCtrl 中定义,不应在此处重新定义。