Delphi主消息循环研究(Application.Run和Application.Initialize执行后的情况)

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;

第一步,貌似什么都不做,但如果提前定义InitProc就不一样了

procedure TApplication.Initialize;
begin
  if InitProc <> nil then TProcedure(InitProc);
end;

第二步,创建一部分Form,特别是MainForm

procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
  Instance: TComponent;
begin
  Instance := TComponent(InstanceClass.NewInstance);
  TComponent(Reference) := Instance;
  try
    Instance.Create(Self);
  except
    TComponent(Reference) := nil;
    raise;
  end;
  if (FMainForm = nil) and (Instance is TForm) then
  begin
    TForm(Instance).HandleNeeded; // 这句话大有讲究,执行了许多动作。包括递归创建Parent的Handle
    FMainForm := TForm(Instance);
  end;
end;

第三步,使用repeat建立消息循环

procedure TApplication.Run;
var
  i: integer;
  d1,d2: TDateTime;
begin
  i:=0;
  d1:=now;
  FRunning := True;
  try
    AddExitProc(DoneApplication);
    if FMainForm <> nil then
    begin
      case CmdShow of
        SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
        SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
      end;
      if FShowMainForm then
        if FMainForm.FWindowState = wsMinimized then Minimize 
    else FMainForm.Visible := True;
      // 注意1,当鼠标移出当前窗口的范围时,不会继续执行当前repeat循环
      // 注意2,经测试发现,每次点击鼠标或者按键,都会产生5个消息。
      // 注意3,这里给每一个消息处理都包裹了一个异常处理。
      repeat
      begin
        try
          HandleMessage;
        except
          HandleException(Self);
        end;
    // 这里可以观察,当前窗口处理了多少个消息
        inc(i);
        if (i=200) then begin d2:=now; ShowMessage(IntToStr(MinutesBetween(d1,d2))); end;
        MainForm.Canvas.TextOut(0,0,IntToStr(i));
      end
      until Terminated;
    end;
  finally
    FRunning := False;
  end;
end;

第3.1步,具体处理每一个消息循环

procedure TApplication.HandleMessage;
var
  Msg: TMsg;
begin
  if not ProcessMessage(Msg) then
  begin
    Idle(Msg);
  end;
end;

第3.2步,取得消息并分发消息,但是分发前好像还会先执行FOnMessage(Msg, Handled);

function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
  Handled: Boolean;
begin
  Result := False;
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  begin
    Result := True;
    if Msg.Message <> WM_QUIT then
    begin
      Handled := False;
      if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
      if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
      begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      end;
    end
    else
      FTerminate := True;
  end;
end;

第3.3步 处理Hint,同步主线程,再调用 WaitMessage

procedure TApplication.Idle(const Msg: TMsg);
var
  Control: TControl;
  Done: Boolean;
begin
  Control := DoMouseIdle;
  if FShowHint and (FMouseControl = nil) then CancelHint;
  Application.Hint := GetLongHint(GetHint(Control));
  Done := True;
  try
    if Assigned(FOnIdle) then FOnIdle(Self, Done);
    if Done then DoActionIdle;
  except
    HandleException(Self);
  end;
  if (GetCurrentThreadID = MainThreadID) and CheckSynchronize then
    Done := False;
  // 当一个线程的消息队列中无其它消息时,该函数就将控制权交给另外的线程,同时将该线程挂起,直到一个新的消息被放入线程的消息队列之中才返回。
  // 在指定类型的新的输入消息抵达之前,它是不会返回的。
  // 如果没有这句,或者不调用这个Idle,当前消息循环会不间断疯狂的去队列里取消息,1分钟即可执行30多万次,CPU 100%被占用
  if Done then WaitMessage; 
end;

第四步,程序员手工建立消息循环:
自己建立一个消息处理循环(while),把当前消息队列的所有消息一次性处理完毕,且不调用Idle。可以在while加上计数,看每次处理了多少个消息。

procedure TApplication.ProcessMessages;
var
  Msg: TMsg;
begin
  while ProcessMessage(Msg) do {loop};
end;

 个人感想:程序的任何一个地方,都可以主动执行PeekMessage等消息函数,接管主程序的消息循环,参考:

http://blog.csdn.net/mengde666/article/details/4045656

原文地址:https://www.cnblogs.com/findumars/p/3267093.html