了解消息处理过程的堆栈跟踪

Understanding stack trace for message handling procedure

我在我的主窗体上放置了一个 TApplicationEvents 组件并添加了这个事件代码:

procedure TAniWinMainForm.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin
    if (ActiveMDIChild is TFormStartDialog) and
       (Msg.Message = WM_KEYDOWN) and (Msg.WParam = VK_F4) then
        Handled := True;
end;

部署应用程序后,用户通过以下 EurekaLog 错误报告报告了 EInvalidOp 错误:

Modul Name   : KERNELBASE.dll
Typ          : EInvalidOp


|77A4E60A|ntdll.dll   |           |               |NtCallbackReturn                        |         |
|74515EAA|win32u.dll  |           |               |NtUserGetPointerInfoList                |         |
|754595F9|user32.dll  |           |               |GetPointerTouchInfo                     |         |
|778E3923|msvcrt.dll  |           |               |sqrt                                    |         |
|778E388D|msvcrt.dll  |           |               |sqrt                                    |         |
|778E387B|msvcrt.dll  |           |               |_CIsqrt                                 |         |
|778E3870|msvcrt.dll  |           |               |_CIsqrt                                 |         |
|75460AC0|user32.dll  |           |               |SendMessageW                            |         |
|77A235DB|ntdll.dll   |           |               |RtlDeactivateActivationContextUnsafeFast|         |
|75470090|user32.dll  |           |               |CallWindowProcA                         |         |
|00CA5549|Program.exe |MAIN.pas   |TMainForm      |ApplicationEvents1Message               |700[1]   |
|7547BC0B|user32.dll  |           |               |DispatchMessageA                        |         |
|7547BC00|user32.dll  |           |               |DispatchMessageA                        |         |
|00D1376A|Program.exe |Program.dpr|               |                                        |1145[477]|
|772962C2|KERNEL32.DLL|           |               |BaseThreadInitThunk                     |         |

我想这个错误与我的代码无关,但我不明白可能发生了什么。 谁能解释是什么导致了这个堆栈跟踪? 我不应该看到哪个函数正在调用 CallWindowProcA 吗?

我的猜测是 CallWindowProcA 的调用是由 ActiveMDIChild 发出的。那是一个 属性,其 getter 看起来像这样:

function TCustomForm.GetActiveMDIChild: TForm;
begin
  Result := nil;
  if (FormStyle = fsMDIForm) and (FClientHandle <> 0) then
    Result := TForm(FindControl(SendMessage(FClientHandle, WM_MDIGETACTIVE, 0,
      0)));
end;

您希望在调用堆栈中的 ApplicationEvents1Message 上方看到 SendMessage,但我怀疑 EurekaLog 堆栈跟踪代码不够好,无法在 Win32 API函数。现在对 SendMessage 的调用将调用客户端 window 的 window 过程,因此对 CallWindowProcA 的调用非常有意义。

至于实际问题,这听起来很像是 Win32 代码的问题,希望屏蔽浮点异常。我建议您在引用 ActiveMDIChild.

之前屏蔽异常

我还强烈建议您更改 if 语句中条件的顺序。您的程序处理的每条排队消息都会调用此事件。您真的不想为每个人阅读 ActiveMDIChild 属性。像这样写 if 语句:

if (Msg.Message = WM_KEYDOWN) and
   (Msg.WParam = VK_F4) and
   (ActiveMDIChild is TFormStartDialog) then

因此您可以像这样重写您的事件处理程序:

procedure TAniWinMainForm.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
var
  Save8087CW: Word;
begin
  if (Msg.Message = WM_KEYDOWN) and (Msg.WParam = VK_F4) then
  begin
    Save8087CW := Get8087CW;
    Set8087CW(7F); // this is the default Windows control word, with floating point exceptions masked
    if ActiveMDIChild is TFormStartDialog then
      Handled := True;
    Set8087CW(Save8087CW);
  end;
end;