使用Delphi VCL表单中的LParam 0确定WM_SYSCOMMAND的发送方 问题
当运行一个用C编写的应用程序(使用Delphi XE7编写的dll)时,我在下面的代码中遇到了访问冲突,该代码位于vcl库的vcl.forms.pas中使用Delphi VCL表单中的LParam 0确定WM_SYSCOMMAND的发送方 问题,delphi,access-violation,vcl,Delphi,Access Violation,Vcl,当运行一个用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 (csDes
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;
过程TCustomForm.CMAppSysCommand(var消息:TMessage);
{$如果未定义(CLR)}
类型
PWMSysCommand=^TWMSysCommand;
{$ENDIF}
开始
消息。结果:=0;
如果(csDesigning in ComponentState)或(FormStyle=fsMDIChild)或
(Menu=nil)或Menu.AutoMerge然后
{$IF-DEFINED(CLR)}
使用TWMSysCommand.Create(Message)do
{$ELSE}
使用PWMSysCommand(Message.lParam)^do
{$ENDIF}
开始
发送取消模式(无);
如果SendAppMessage(CM_APPSYSCOMMAND,CmdType,Key)0,则//调试器在此处显示访问冲突
消息。结果:=1;
终止
终止
访问冲突发生在带有SendAppMessage的行上,似乎是由Message.LParam为0这一事实引起的。该消息是WM_SYSCOMMAND消息。是否有方法跟踪此消息的来源?在调用堆栈中,所有函数都是VCL或系统文件的一部分
建议通常很难跟踪windows邮件的发件人。但是,因为在我的情况下,所有内容都在同一个应用程序中,我希望这可能会使它变得更容易
我试过什么?
否决vcl源
以前,forms.pas中也出现了相同的错误,通过向项目中添加该文件的副本,然后在此函数中检查LParam 0,可以修复该错误。
我尝试过对现在使用的vcl.forms.pas执行相同的操作,但这会导致编译错误。即使有答案,因为我无法构建它。然而,许多谷歌点击也表明,改变vcl中的内容通常是一个坏主意,所以我尽量避免这种选择
关于StackOverFlow的其他问题
向我提供了有关底层系统的详细信息,以及Message.LParam为0时可能发生的情况。但是,我不知道如何找到消息的来源,也不知道生成消息的类应该是什么
解决方案
如下面Remy接受的答案所述,通过让类提供CMAppSysCommand函数来防止LParam=0,可以解决眼前的问题 您所描述的内容在正常情况下不可能实现 整个VCL中只有两个位置发送
CM\u APPSYSCOMMAND
:
TWinControl.WMSysCommand()
,当UI控件收到WM\u SYSCOMMAND
消息时调用该命令。CM_APPSYSCOMMAND
消息的LParam
从未设置为0,它被设置为指向原始WM_SYSCOMMAND
消息的TMessage
记录的指针:
Form := GetParentForm(Self);
if (Form <> nil) and
(Form.Perform(CM_APPSYSCOMMAND, 0, Winapi.Windows.LPARAM(@Message)) <> 0) then
Exit;
CM\u APPSYSCOMMAND
,但没有说明它的LParam
在TCustomForm.CMAppSysCommand()
中如何可能为0,因为在正常情况下它不可能为0。在TApplication.WndProc()
中它可以是0,但这完全可以
我能想到的唯一可能性是,如果有人手动将一条假的CM_APPSYSCOMMAND
消息(即CM_BASE+23=$B017
,又称WM_APP+$3017
)直接发送到您的TForm
窗口。只有TWinControl才应该这样做。由于TWinControl
使用Perform()
而不是SendMessage()
进行发送,您应该在TCustomForm.CMAppSysCommand()
的调用堆栈上看到TWinControl.WMSysCommand()
。如果你没有,那么信息是假的。如果它是使用SendMessage()
而不是Perform()
发送的,则无法知道消息来自何处
然而,在任何情况下,这都是很容易防范的,不需要修改任何VCL源代码。只需使用message
指令或重写virtualWndProc()
方法,让DLL的TForm
类为CM\u APPSYSCOMMAND
提供自己的消息处理程序。无论哪种方式,如果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;
你所描述的在正常情况下是不可能的 整个VCL中只有两个位置发送
CM\u APPSYSCOMMAND
:
TWinControl.WMSysCommand()
,当UI控件收到WM\u SYSCOMMAND
消息时调用该命令。CM_APPSYSCOMMAND
消息的LParam
从未设置为0,它被设置为指向原始WM_SYSCOMMAND
消息的TMessage
记录的指针:
Form := GetParentForm(Self);
if (Form <> nil) and
(Form.Perform(CM_APPSYSCOMMAND, 0, Winapi.Windows.LPARAM(@Message)) <> 0) then
Exit;
CM\u APPSYSCOMMAND
,但没有说明它的LParam
在TCustomForm.CMAppSysCommand()
中如何可能为0,因为在正常情况下它不可能为0。在TApplication.WndProc()
中它可以是0,但这完全可以
我能想到的唯一可能性是,如果有人手动将一条假的CM_APPSYSCOMMAND
消息(即CM_BASE+23=$B017
,又称WM_APP+$3017
)直接发送到您的TForm
窗口。只有TWinControl才应该这样做。由于TWinControl
使用Perform()
而不是SendMessage()
进行发送,您应该在TCustomForm.CMAppSysCommand()
的调用堆栈上看到TWinControl.WMSysCommand()
。如果你没有,那么信息是假的。如果它是使用SendMessage()
而不是Perform()
发送的,则无法知道消息来自何处
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;