在Delphi IDE中,创建一个VCL Forms Application
。然后在表单上添加TApplicationEvents
组件和TButton
。然后添加这两个事件处理程序:
uses
JclShell;
procedure TForm3.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
if Msg.Message = WM_LBUTTONDOWN then
begin
Self.Caption := 'WM_LBUTTONDOWN';
end
else if Msg.Message = WM_LBUTTONUP then
begin
Self.Caption := 'WM_LBUTTONUP';
end
end;
procedure TForm3.Button1Click(Sender: TObject);
begin
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
end;
现在点击按钮。发生以下情况:
" WM_LBUTTONDOWN"出现在表格的标题栏上。
" WM_LBUTTONUP"出现在表格的标题栏上。
记事本已执行。
然后再次单击按钮,这次启动另一个记事本实例,但这次没有在表格的标题栏上写任何内容。
显然,程序卡在JclShell.ShellExecAndWait
中,只有在记事本关闭时才会返回。因此,当记事本关闭时,任何鼠标单击都会再次写入表单的标题栏。
所以我们可以看到,当记事本在JclShell.ShellExecAndWait
中运行时,程序中的所有内容都能正常工作:当记事本在JclShell.ShellExecAndWait
中运行时,您甚至可以进行数学计算。在记事本运行时,不会触发ApplicationEvents1Message
。
那么当记事本在WM_LBUTTONDOWN
中运行时如何才能收到JclShell.ShellExecAndWait
消息?
答案 0 :(得分:0)
当JclShell.ShellExecAndWait
等待生成的进程退出时,使用以下基本消息泵:
while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
repeat
Msg.hwnd := 0;
Res := PeekMessage(Msg, Sei.Wnd, 0, 0, PM_REMOVE);
if Res then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
until not Res;
CloseHandle(Sei.hProcess);
我说基本的,因为VCL消息泵的循环部分看起来像这样:
if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
begin
Unicode := (Msg.hwnd = 0) or IsWindowUnicode(Msg.hwnd);
if Unicode then
MsgExists := PeekMessageW(Msg, 0, 0, 0, PM_REMOVE)
else
MsgExists := PeekMessageA(Msg, 0, 0, 0, PM_REMOVE);
if MsgExists then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsPreProcessMessage(Msg) and not IsHintMsg(Msg) and
not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
if Unicode then
DispatchMessageW(Msg)
else
DispatchMessageA(Msg);
end;
end
else
begin
{$IF DEFINED(CLR)}
if Assigned(FOnShutDown) then FOnShutDown(self);
DoneApplication;
{$IFEND}
FTerminate := True;
end;
end;
代码行
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
是通过TApplicationEvents.OnMessage
分配给TMultiCaster
的{{1}}将邮件传递给TApplicationEvents
的邮件。这不是在JCL源中完成的。
其他问题可能,此时JCL并非UniCode了解有关消息的信息,并且Application.OnMessage
没有处理。
该怎么做取决于你想要达到的目标。您为什么要首先收到此消息?
我的意思是可以更改JCL源代码 - 如果您愿意这样做 - 并将WM_QUIT
添加到使用中,然后调用事件处理程序(如果像VCL那样分配的话):
VCL.Forms
甚至可以调用while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
repeat
Msg.hwnd := 0;
Res := PeekMessage(Msg, Sei.Wnd, 0, 0, PM_REMOVE);
if Res then
begin
Handled := False;
if Assigned(Application.OnMessage) then
Application.OnMessage(Msg, Handled);
if not Handled then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
until not Res;
CloseHandle(Sei.hProcess);
,让VCL执行相同的消息处理:
Application.ProcessMessages
这是有效的,我看不出像评论者所说的任何副作用。但在以这种方式更改JCL源代码之前,我可能会实现自己的while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
Application.ProcessMessages;
CloseHandle(Sei.hProcess);
。根据您希望实现的目标,正常的消息调度应该仍然有效。因此,如果你的TFrom有例如
ShellExecAndWait
实施后,应该调用它。但是,只有在消息指向您的表单时才会出现这种情况。如果单击该按钮,则生成的消息将定向到按钮本身。那么你需要实现自己的后代类。
也许是一个完全不同的建议?
如果您的目标是在procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
等待时不让某些控件被点击,并且使用消息处理是您实现这一目标的方法,那么您可以尝试其他方法。
为什么不禁用?这也将为用户提供可见的指示,即点击是不合格的。
JclShell.ShellExecAndWait
如果您担心多个控件,请创建 Button1.Enabled := False;
try
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
finally
Button1.Enabled := True;
end;
,将其分配给您要禁用的每个控件,并将所有控件与TAction
的{{1}}属性一起禁用。
或者,在打开notepad.exe的同时隐藏表单?
Enabled
或者您可以删除事件处理程序:
TAction