VCL中訊息處理初探 (轉)

gugu99發表於2008-03-07
VCL中訊息處理初探 (轉)[@more@]

T是基類,所以我們先看一下TObject的DISPATCH方法。Dispatch根據傳入的message來尋找相應的訊息處理方法,如果找不到的話,就繼續向上到父類的訊息處理方法表中尋找響應的處理方法,一直到找到為止,如果找到頂還沒有,則DefaultHandle來處理該訊息。message可以是任何的型別,Dispatch假設message的頭兩位是訊息的ID,它就是根據ID來尋找訊息處理方法的。雖然任何型別的message都可以被接受,但是TObject的子類還是希望傳入的message引數是TMessage的記錄型別或其他證明的記錄型別。
以下宣告和註釋摘自與system.pas:
{ TObject.Dispatch accepts any data type as its Message parameter.  The
  first 2 bytes of the data are taken as the message id to search for
  in the object's message methods.  TDispatchMessage is an example of
  such a structure with a field for the message id.
}
  TDispatchMessage = record
  MsgID: Word;
  end;
類的繼承關係如下:
TObject->TPersistent->TComponent->TControl
TControl是所以視覺化的父類,TControl提供了一個新的方法,WndProc:
procedure TControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
  KeyState: TKeyboardState; 
  WheelMsg: TCMMouseWheel;
begin
  //如果處在設計期
  if (csDesigning in ComponentState) then
  begin
  Fo:= GetParentForm(Self);//得到擁有該元件的窗體
  if (Form <> nil) and (Form.Designer <> nil) and
  Form.Designer.IsDesignMsg(Self, Message) then Exit //訊息由窗體來處理
  end;

  //窗體可以為其擁有的元件來處理鍵盤訊息。
  if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then
  begin
  Form := GetParentForm(Self);
  if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;
  end

  //關於滑鼠的訊息
  else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
  begin
  //如果元件不可以接受和處理雙擊訊息,就將雙擊訊息對映為單擊訊息。
  if not (csDoubleClicks in ControlStyle) then
  case Message.Msg of
  WM_LBUTTONLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
  Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
  end;
 
  case Message.Msg of
  WM_MOUSEMOVE: Application.HintMouseMessage(Self, Message);//如果是滑鼠移動的訊息,則出現hint視窗
  WM_LBUTTONDOWN, WM_LBUTTONDBLCLK://如果是左鍵被按下,或者雙擊,如果是自動拖動,則開始拖動,並將左鍵按下的狀態加入元件的狀態。
  begin
  if FDragMode = dmAutomatic then
  begin
  BeginAutoDrag;
  Exit;
  end;
  Include(FControlState, csLButtonDown);
  end;
  WM_LBUTTONUP:
  Exclude(FControlState, csLButtonDown); //如果是左鍵放開,則將左鍵按下的狀態剔除。
  else
  with Mouse do
  if WheelPresent and (RegWheelMessage <> 0) and  //如果滑鼠有滾輪,並且滾輪滑動時發出了訊息
  (Message.Msg = RegWheelMessage) then
  begin
  GetKeyboardState(KeyState);  //將256虛擬鍵的狀態複製到快取中去
  with WheelMsg do //填充記錄
  begin
  Msg := Message.Msg;
  ShiftState := KeyboardStateToShiftState(KeyState);
  WheelDelta := Message.WParam;
  P:= TSmallPoint(Message.LParam);
  end;
  MouseWheelHandler(TMessage(WheelMsg)); //派發滑鼠滾輪的訊息
  Exit;
  end;
  end;
  end
  else if Message.Msg = CM_VISIBLECHANGED then
  with Message do
  SendDockNotification(Msg, WParam, LParam);  //處理自定義訊息
  Dispatch(Message); //派發未處理的訊息
end;
但是隻有TWinControl可以獲得焦點:
procedure TWinControl.WndProc(var Message: TMessage);
var
  Form: TCustomForm;
begin
  case Message.Msg of
  WM_SETFOCUS:  //設定的焦點
  begin
  Form := GetParentForm(Self);
  if (Form <> nil) and not Form.SetFocusedControl(Self) then Exit;
  end;
  WM_KILLFOCUS:
  if cocusing in ControlState then Exit;
  //當滑鼠有活動的時候發出該訊息,如果滑鼠沒有被捕捉到,則訊息發往滑鼠下面的那個視窗,否則訊息將發往捕捉到滑鼠的那個視窗。
  WM_NCHITTEST:
  begin
  inherited WndProc(Message); //呼叫父類的處理方法
  //如果窗體被擋住並且在指定的點沒有控制元件,則返回結果為在client區。
  if (Message.Result = HTTRANSPARENT) and (ControlAtPos(ScreenToClient(
  SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <> nil) then
  Message.Result := HTCLIENT;
  Exit;
  end;
  WM_MOUSEFIRST..WM_MOUSELAST:
  if IntrolMouseMsg(TWMMouse(Message)) then  //滑鼠訊息是否直接發往元件的窗體子元件
  begin
  { Check HandleAllocated because IsControlMouseMsg might have freed the
  window if user code executed something like Parent := nil. }
  if (Message.Result = 0) and HandleAllocated then
  DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);//呼叫預設的的訊息處理方法對該訊息進行預設處理。
  Exit;
  end;
  WM_KEYFIRST..WM_KEYLAST:
  if Dragging then Exit;
  WM_CANCELMODE:
  if (GetCapture = Handle) and (CaptureControl <> nil) and
  (CaptureControl.Parent = Self) then
  CaptureControl.Perform(WM_CANCELMODE, 0, 0);
  end;
  inherited WndProc(Message);
end;

TApplication在中發揮著重要的作用:
Application.Run;

procedure TApplication.Run;
begin
  FRunning := True;
  try
  AddExitProc(DoneApplication);
  if FMainForm <> nil then
  begin
  case CmdShow of
  SW_SHOWMINNOACTIVE: FMainForm.Ftate := wsMinimized;
  SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
  end;
  if FShowMainForm then
  if FMainForm.FWindowState = wsMinimized then
  Minimize else
  FMainForm.Visible := True;

  //一個訊息迴圈直到Tenated為True時才退出。
  repeat
  try
  HandleMessage;
  except
  HandleException(Self);
  end;
  until Terminated;
  end;
  finally
  FRunning := False;
  end;
end;

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

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;


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1000521/,如需轉載,請註明出處,否則將追究法律責任。

相關文章