当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 分配给Application.OnMessage 的TMultiCaster 传递给您的TApplicationEvents.OnMessage。这不是在 JCL 源代码中完成的。
其他问题可能是 JCL 目前不了解有关消息的 UniCode 并且没有处理 WM_QUIT。
如何处理这也取决于您想要实现的目标。您为什么要首先收到此消息?
我的意思是可以更改 JCL 源 - 如果您愿意这样做 - 并将 VCL.Forms 添加到使用中,然后调用事件处理程序(如果像 VCL 那样分配):
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);
或者甚至调用Application.ProcessMessages,以进行与 VCL 相同的消息处理:
while WaitForSingleObject(Sei.hProcess, 10) = WAIT_TIMEOUT do
Application.ProcessMessages;
CloseHandle(Sei.hProcess);
这是有效的,我看不到评论者建议的任何副作用。但在以这种方式更改 JCL 源之前,我可能会实现自己的ShellExecAndWait。根据您想要实现的目标,消息的正常发送应该仍然有效。因此,如果您的 TFrom 具有例如
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 的多个控件,请将其分配给您要禁用的每个控件,并与TAction 的Enabled 属性一起禁用所有这些控件。
或者,在打开 notepad.exe 时隐藏您的表单?
Hide;
try
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
finally
Show;
end;
或者您可以删除事件处理程序:
var
ATmpOnClick: TNotifyEvent;
begin
ATmpOnClick := Button1.OnClick;
Button1.OnClick := nil;
try
JclShell.ShellExecAndWait('C:\Windows\system32\notepad.exe');
Self.Caption := 'Notepad closed';
finally
Button1.OnClick := ATmpOnClick;
end;
end;