Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi MessageBoxEx停止更新操作_Delphi_Delphi 7 - Fatal编程技术网

Delphi MessageBoxEx停止更新操作

Delphi MessageBoxEx停止更新操作,delphi,delphi-7,Delphi,Delphi 7,我使用Delphi7,我的项目有几个非模态可见表单。问题是,如果其中一个调用了MessageBoxEx,则在关闭MessageBoxEx表单之前,应用程序的所有操作都不会更新。在我的项目中,它会破坏应用程序的业务逻辑 当MessageBoxEx的窗口显示时,不会调用TApplication.HandleMessage方法,因此它不会调用DoActionIdle,也不会更新操作 我认为我需要的是在应用程序空闲时捕获它的状态,并更新所有操作的状态 首先,我实现了TApplication。OnIdle

我使用Delphi7,我的项目有几个非模态可见表单。问题是,如果其中一个调用了MessageBoxEx,则在关闭MessageBoxEx表单之前,应用程序的所有操作都不会更新。在我的项目中,它会破坏应用程序的业务逻辑

当MessageBoxEx的窗口显示时,不会调用TApplication.HandleMessage方法,因此它不会调用DoActionIdle,也不会更新操作

我认为我需要的是在应用程序空闲时捕获它的状态,并更新所有操作的状态

首先,我实现了TApplication。OnIdle处理程序:

procedure TKernel.OnIdle(Sender: TObject; var Done: Boolean);
begin
  {It’s only to switch off the standard updating from TApplication.Idle. It's to make the CPU usage lower while MessageBoxEx's window isn't shown }
 Done := False;
end;

implementation

var
  MsgHook: HHOOK;

{Here is a hook}
function GetMsgHook(nCode: Integer; wParam: Longint; var Msg: TMsg): Longint; stdcall;
var
  m: TMsg;
begin
  Result := CallNextHookEx(MsgHook, nCode, wParam, Longint(@Msg));
  if (nCode >= 0) and (_instance <> nil) then
  begin
    {If there aren’t the messages in the application's message queue then the application is in idle state.}
    if not PeekMessage(m, 0, 0, 0, PM_NOREMOVE) then
    begin
      _instance.DoActionIdle;
      WaitMessage;
    end;
  end;
end;

initialization
    MsgHook := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgHook, 0, GetCurrentThreadID);

finalization
  if MsgHook <> 0 then
    UnhookWindowsHookEx(MsgHook);
状态更新似乎比通常要频繁得多(我将使用profiler找出哪里有问题)

此外,当鼠标指针不在应用程序的窗口上时,CPU的使用率会严重增加(在我的双核奔腾上,CPU的使用率约为25%)

你对我的问题和我解决问题的方法有什么看法?使用钩子是个好主意还是有更好的方法来捕获应用程序空闲状态?在设置挂钩时,我是否需要使用WH_CALLWNDPROCRET

为什么MessageBoxEx会阻止TApplication.HandleMessage?有没有办法防止这种行为?我尝试使用MB_APPLMODAL、MB_SYSTEMMODAL、MB_TASKMODAL标志调用它,但没有任何帮助。

MessageBox/Ex()
是一个模式对话框,因此它在内部运行自己的消息循环,因为调用线程的正常消息循环被阻止
MessageBox/Ex()
接收调用线程的消息队列中的任何消息,并将它们正常地分派到目标窗口(因此基于窗口的计时器仍然工作,例如
TTimer
),但其模式消息循环没有VCL特定消息的概念,例如操作更新,并将丢弃它们
TApplication.HandleMessage()
仅由主VCL消息循环、
TApplication.ProcessMessages()
方法和
TForm.ShowModal()
方法调用(这就是为什么modal VCL窗体窗口不会遇到此问题的原因),在运行
MessageBox/Ex()
时,不会调用这些方法(对于任何操作系统模式对话框也是如此)

要解决您的问题,您有两个选择:

  • 在调用
    MessageBox/Ex()
    之前,通过
    SetWindowsHookEx()
    设置线程本地消息挂钩,然后在
    MessageBox/Ex()
    退出后立即释放挂钩。这允许您查看
    MessageBox/Ex()发出的每条消息
    根据需要将它们接收并发送到VCL处理程序。不要在消息挂钩内调用
    peek消息()
    GetMessage()
    WaitMessage()

    type
      TApplicationAccess = class(TApplication)
      end;
    
    function GetMsgHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    var
      Msg: TMsg;
    begin
      if (nCode >= 0) and (wParam = PM_REMOVE) then
      begin
        Msg := PMsg(lParam)^;
        with TApplicationAccess(Application) do begin
          if (not IsPreProcessMessage(Msg))
            and (not IsHintMsg(Msg))
            and (not IsMDIMsg(Msg))
            and (not IsKeyMsg(Msg))
            and (not IsDlgMsg(Msg)) then
          begin
          end;
        end;
      end;
      Result := CallNextHookEx(MsgHook, nCode, wParam, lParam);
    end;
    
    function DoMessageBoxEx(...): Integer;
    var
      MsgHook: HHOOK;
    begin
      MsgHook := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgHook, 0, GetCurrentThreadID);
      Result := MessageBoxEx(...);
      if MsgHook <> 0 then UnhookWindowsHookEx(MsgHook);
    end;
    

  • 谢谢你的回答!对我来说,第一种解决方案看起来更吸引人。不幸的是,他们都认为我需要找到MessageBox的所有调用并替换。但我认为这不会太难。什么是Message?它是来自任何旧版本的Delphi的方法吗?嗯。我认为第一种方法不正确,是吗?t应用程序ion在队列中没有消息(没有任何VCL消息来更新操作)时调用DoActionIdle(更新操作),当MessageBox的窗口接收到一些消息时执行钩子时,应用程序变为空闲。我猜这与此不同。
    IsPreProcessMessage()
    允许MDI MainForm或当前关注的
    TWinControl
    在处理消息时获得第一个DIB。它是在D2005中引入的。至于VCL的空闲处理,为了在第一个解决方案中调用它,您可以调用public
    TApplication.DoApplicationIdle()
    方法。诀窍在于确定何时调用它,因为您无法检测模式对话框的内部消息循环何时空闲。我可能会使用一个短的一次性计时器,其中每个接收到的消息(重新)设置计时器,然后在它消失时调用
    DoApplicationIdle()
    type
      TApplicationAccess = class(TApplication)
      end;
    
    function GetMsgHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    var
      Msg: TMsg;
    begin
      if (nCode >= 0) and (wParam = PM_REMOVE) then
      begin
        Msg := PMsg(lParam)^;
        with TApplicationAccess(Application) do begin
          if (not IsPreProcessMessage(Msg))
            and (not IsHintMsg(Msg))
            and (not IsMDIMsg(Msg))
            and (not IsKeyMsg(Msg))
            and (not IsDlgMsg(Msg)) then
          begin
          end;
        end;
      end;
      Result := CallNextHookEx(MsgHook, nCode, wParam, lParam);
    end;
    
    function DoMessageBoxEx(...): Integer;
    var
      MsgHook: HHOOK;
    begin
      MsgHook := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgHook, 0, GetCurrentThreadID);
      Result := MessageBoxEx(...);
      if MsgHook <> 0 then UnhookWindowsHookEx(MsgHook);
    end;
    
    type
      TMessageBoxThread = class(TThread)
      protected
        procedure Execute; override;
        ...
      public
        constructor Create(...);
      end;
    
    constructor TMessageBoxThread.Create(...);
    begin
      inherited Create(False);
      ...
    end;
    
    function TMessageBoxThread.Execute;
    begin
      ReturnValue := MessageBoxEx(...);
    end;
    
    function DoMessageBoxEx(...): Integer;
    var
      Thread: TMessageBoxThread;
      WaitResult: DWORD;
    begin
      Thread := TMessageBoxThread.Create(...);
      try
        repeat
          WaitResult := MsgWaitForMultipleObjects(1, Thread.Handle, False, INFINITE, QS_ALLINPUT);
          if WaitResult = WAIT_FAILED then RaiseLastOSError;
          if WaitResult = WAIT_OBJECT_0 + 1 then Application.ProcessMessages;
        until WaitResult = WAIT_OBJECT_0;
        Result := Thread.ReturnVal;
      finally
        Thread.Free;
      end;
    end;