ShellExecAndWait时获取WM_LBUTTONDOWN消息?

时间:2017-11-16 16:14:18

标签: delphi delphi-10.1-berlin jedi

在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;

现在点击按钮。发生以下情况:

  1. " WM_LBUTTONDOWN"出现在表格的标题栏上。

  2. " WM_LBUTTONUP"出现在表格的标题栏上。

  3. 记事本已执行。

  4. 然后再次单击按钮,这次启动另一个记事本实例,但这次没有在表格的标题栏上写任何内容。

    显然,程序卡在JclShell.ShellExecAndWait中,只有在记事本关闭时才会返回。因此,当记事本关闭时,任何鼠标单击都会再次写入表单的标题栏。

    所以我们可以看到,当记事本在JclShell.ShellExecAndWait中运行时,程序中的所有内容都能正常工作:当记事本在JclShell.ShellExecAndWait中运行时,您甚至可以进行数学计算。在记事本运行时,不会触发ApplicationEvents1Message

    那么当记事本在WM_LBUTTONDOWN中运行时如何才能收到JclShell.ShellExecAndWait消息?

1 个答案:

答案 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