gpt4 book ai didi

delphi - 在 Delphi VCL 表单中使用 LParam 0 确定 WM_SYSCOMMAND 的发送者

转载 作者:行者123 更新时间:2023-12-01 23:19:35 25 4
gpt4 key购买 nike

问题

当运行一个用 C 编写的应用程序时,该应用程序使用一些用 Delphi XE7 编写的 dll,我在以下代码中遇到了访问冲突,该代码位于 vcl 库的 vcl.forms.pas 中。

procedure TCustomForm.CMAppSysCommand(var Message: TMessage);
{$IF NOT DEFINED(CLR)}
type
PWMSysCommand = ^TWMSysCommand;
{$ENDIF}
begin
Message.Result := 0;
if (csDesigning in ComponentState) or (FormStyle = fsMDIChild) or
(Menu = nil) or Menu.AutoMerge then
{$IF DEFINED(CLR)}
with TWMSysCommand.Create(Message) do
{$ELSE}
with PWMSysCommand(Message.lParam)^ do
{$ENDIF}
begin
SendCancelMode(nil);
if SendAppMessage(CM_APPSYSCOMMAND, CmdType, Key) <> 0 then //Here the debugger shows the access violation
Message.Result := 1;
end;
end;

访问冲突发生在SendAppMessage 线上,似乎是由Message.LParam 为0 引起的。该消息是WM_SYSCOMMAND 消息。有没有办法追踪这条消息的来源?在调用堆栈中,所有函数都是VCL或系统文件的一部分。

This answer表明一般来说追踪 Windows 消息的发件人是困难的。但是,由于就我而言,所有内容都在同一个应用程序中,因此我希望这会让事情变得更容易。

我尝试了什么?

覆盖 vcl 源代码

以前,同样的错误也出现在 forms.pas 中,并通过将该文件的副本添加到项目中然后检查此函数中的 LParam <> 0 来修复。我尝试对现在使用的 vcl.forms.pas 执行相同的操作,但这会导致编译错误。即使答案为 here我无法构建它。然而,许多谷歌点击也表明,改变 vcl 中的内容通常是一个坏主意,所以我尽量避免这个选项。

有关 StackOverFlow 的其他问题

This article为我提供了有关底层系统以及如何发生 Message.LParam 为 0 的良好信息。但是,我不知道如何查找消息的来源,也不知道应该查找生成该消息的类。

解决方案

正如下面 Remy 接受的答案中所述,可以通过让该类提供 CMAppSysCommand 函数来防止 LParam = 0 来解决眼前的问题。

最佳答案

您所描述的情况在正常情况下应该是不可能的。

整个VCL中只有两个地方CM_APPSYSCOMMAND发送自:

  1. TWinControl.WMSysCommand() ,当 UI 控件收到 WM_SYSCOMMAND 时调用它信息。 LParam CM_APPSYSCOMMAND的message 永远不会设置为 0,它被设置为指向 TMessage 的指针。原记录WM_SYSCOMMAND消息:

    Form := GetParentForm(Self);
    if (Form <> nil) and
    (Form.Perform(CM_APPSYSCOMMAND, 0, Winapi.Windows.LPARAM(@Message)) <> 0) then
    Exit;
  2. TCustomForm.CMAppSysCommand() ,当表单收到 CM_APPSYSCOMMAND 时调用它信息。它将消息转发到 TApplication窗口(使用 SendAppMessage() ,它只是使用提供的参数调用 SendMessage(Application.Handle, ...) ):

    with PWMSysCommand(Message.lParam)^ do
    begin
    ...
    if SendAppMessage(CM_APPSYSCOMMAND, CmdType, Key) <> 0 then
    Message.Result := 1;
    end;

other question你提到解释如何CM_APPSYSCOMMAND由 VCL 使用,但没有说明任何内容 LParam TCustomForm.CMAppSysCommand() 中可能为 0 ,因为在正常情况下它不可能为 0。 TApplication.WndProc() 中可以为 0 ,但这完全没问题。

我能想到的唯一可能性是,如果有人手动发送假的 CM_APPSYSCOMMAND消息(即 CM_BASE + 23 = $B017 ,又名 WM_APP + $3017 )直接发送到您的 TForm window 。仅TWinControl应该永远这样做。自从 TWinControl使用Perform()而不是SendMessage()对于该发送,您应该看到 TWinControl.WMSysCommand()TCustomForm.CMAppSysCommand() 的调用堆栈上。如果您不这样做,则该消息是假的。如果使用SendMessage()发送而不是Perform() ,无法知道消息来自哪里。

但是,无论如何,这很容易防范,无需更改任何 VCL 源代码。只需拥有您的 DLL TForm类为 CM_APPSYSCOMMAND 提供自己的消息处理程序,或者使用 message指令,或通过覆盖虚拟 WndProc()方法。无论哪种方式,如果LParam,您都可以丢弃该消息。为0,例如:

type
TMyForm = class(TForm)
...
private
procedure CMAppSysCommand(var Message: TMessage); message CM_APPSYSCOMMAND;
...
end;

procedure TMyForm.CMAppSysCommand(var Message: TMessage);
begin
if Message.LParam = 0 then
Message.Result := 0
else
inherited;
end;

type
TMyForm = class(TForm)
...
protected
procedure WndProc(var Message: TMessage); override;
...
end;

procedure TMyForm.WndProc(var Message: TMessage);
begin
if (Message.Msg = CM_APPSYSCOMMAND) and (Message.LParam = 0) then
Message.Result := 0
else
inherited;
end;

关于delphi - 在 Delphi VCL 表单中使用 LParam 0 确定 WM_SYSCOMMAND 的发送者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43396221/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com