1

在 Delphi 11 Alexandria 的 Windows 10 中的 32 位 VCL 应用程序中,我有一个TListView,我在 FormCreate 处理程序中将 ListView 的标题字体样式设置为 BOLD:

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 调用堆栈:

在此处输入图像描述

这是错误消息:

Range check error at (004EDAED){MyApp.exe} [008EDAED] MainForm.TformMain.SetColumnHeaderFontBold (Line 616, "MainForm.pas") + $13.

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

4

1 回答 1

2

这里有两个问题:

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

对于第一个问题,您声明为THandle的每个变量都被错误地声明。THandle用于内核句柄,是HANDLEWindows C头文件中对应的Delphi类型。代码中的句柄都不是内核句柄,它们都是用户模式对象。

FhHeaderFont,hCurrFont并且hOldFont都应该被声明为HFONT. hHeader应声明为HWND.

导致您的范围检查错误的问题是我列表中的第二个问题。如果您考虑SendMessage必须做什么,它会用于任何 Windows 消息。这些消息适用于大量不同的数据类型。这意味着在SendMessage接受和返回的泛型整数类型与用于参数和返回值的变量类型之间有时会出现不匹配。这些不匹配应该通过类型转换来处理。

范围检查错误的原因是SendMessage返回LRESULT一个有符号整数,指针大小。但是THandle是一个无符号整数,指针大小。即使您使用 更正此HFONT问题,也会出现相同的错误,因为HFONT它也是一个无符号整数,指针大小。

通过类型转换返回值来处理这个问题:

hCurrFont := HFONT(Winapi.Windows.SendMessage(hHeader, WM_GETFONT, 0, 0));

同样,您应该对其他返回值分配执行相同的操作:

hHeader := HWND(...);

严格来说,最好在传递时FhHeaderFontwParam在最终调用时一样做SendMessage. 我想我会这样写:

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

最后一个注意事项是在此处LVM_GETHEADER定义Winapi.CommCtrl且不应在此处重新定义。

于 2022-01-07T11:35:20.243 回答