<<深入核心VCL架构剖析>>笔记(1)

Windows:事件驱动机制.

事件转换为消息,再分派给应用程序.

每个应用程序都有一个消息队列(Message Queue),当事件发生时执行环境会把属于本应用程序的的消息分派到消息队列里,应用程序从消息队列里取出并处理.

创建原生Windows程序

创建原生Windows程序需要使用如下record:

TMSG定义如下:

TMsg = tagMSG;

tagMSG = record
  hwnd: HWND;
  message: UINT;
  wParam: WPARAM;
  lParam: LPARAM;
  time: DWORD;
  pt: TPoint;
end;

 

WNDCLASS 定义如下:

WNDCLASS = WNDCLASSW;

WNDCLASSW = tagWNDCLASSW;

tagWNDCLASSW = record
  style: UINT;
  lpfnWndProc: TFNWndProc;
  cbClsExtra: Integer;
  cbWndExtra: Integer;
  hInstance: HINST;
  hIcon: HICON;
  hCursor: HCURSOR;
  hbrBackground: HBRUSH;
  lpszMenuName: PWideChar;
  lpszClassName: PWideChar;
end;

 

消息处理回调函数:

function WindowProc(Window:Hwnd;AMessage: UNIT)

 

需要使用的API函数:

1.注册窗口类:

function RegisterClass(const lpWndClass: TWndClass): ATOM; stdcall;

2.创建窗体

function CreateWindow(lpClassName: PWideChar; lpWindowName: PWideChar;
  dwStyle: DWORD; X, Y, nWidth, nHeight: Integer; hWndParent: HWND;
  hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;

具体代码如下

CreateWindowprogram Project1;

uses
  Winapi.Windows,Winapi.Messages,System.SysUtils;

const
  APPNAME = 'ObjectPascalHello';

function WindowProc(Window: HWND;AMessage: UINT;WParam: WPARAM;LParam: LPARAM): LRESULT;stdcall;export;
var
  dc: HDC;
  ps: PAINTSTRUCT;
  r: TRect;
begin
  Result := 0;

  case AMessage of
    WM_PAINT:
    begin
      dc := BeginPaint(Window,ps);
      try
        GetClientRect(Window,r);
        DrawText(dc,'使用Object Pascal撰写的Native window程序',-1,r,DT_SINGLELINE or DT_CENTER or DT_VCENTER);
      finally
        EndPaint(Window,ps)
      end;
    end;
    WM_DESTROY:
    begin
      PostQuitMessage(0);
    end;
  end;

  WindowProc := DefWindowProc(Window,AMessage,WParam,LParam);
end;

function WinRegister: Boolean;
var
  WindowClass: WNDCLASS;
begin
  with WindowClass do
  begin
    style := CS_VREDRAW or CS_HREDRAW;
    lpfnWndProc := TFNWndProc(@WindowProc) ;
    cbClsExtra := 0;
    cbWndExtra := 0;
    hInstance := System.MainInstance;
    hIcon := LoadIcon(0,IDI_APPLICATION);
    hCursor := LoadCursor(0,IDC_ARROW);
    hbrBackground := GetStockObject(WHITE_BRUSH);
    lpszMenuName := nil;
    lpszClassName := APPNAME;
  end;

  Result := RegisterClass(WindowClass) <> 0;
end;

function WinCreate: HWND;
var
  hWindow: HWND;
begin
  hWindow := CreateWindow(APPNAME,'Hello world object Pascal program',WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,System.MainInstance,nil) ;
  if hWindow <> 0 then
  begin
    ShowWindow(hWindow,CmdShow);
    ShowWindow(hWindow,SW_SHOW);
    UpdateWindow(hWindow);
  end;
  Result := hWindow;
end;

var
 AMessage: TMsg;
 hWindow: HWND;

begin
  if not WinRegister then
  begin
    MessageBox(0,'Register failed',nil,MB_OK);
    Exit;
  end;

  hWindow := WinCreate;
  if LongInt(hWindow) = 0 then
  begin
    MessageBox(0,'Create window failed',nil,MB_OK);
    Exit;
  end;

  while GetMessage(AMessage,0,0,0) do
  begin
    TranslateMessage(AMessage);
    DispatchMessage(AMessage);
  end;

  Halt(AMessage.wParam);

end.
 
退出流程:

TForm.Close---->TApplication.Terminate---->PostQuitMessage---->WM_QUIT

一般主窗口关闭时会发出WM_DESTROY,而WM_DESTROY会调用PostQuitMessage:

窗口关闭---->WM_DESTROY---->PostQuitMessage---->WM_QUIT

应用程序不用处理的消息需用DefWindowProc将消息传递给操作系统,由操作系统来处理这个消息

使用OOP方式实现原生程序:

program Project1;

uses
  Winapi.Windows,Winapi.Messages;

const
  APPNAME = 'ObjectPascalHello';


type
  TMyWindow = class(TObject)
  private
    WindowClass: WNDCLASS;
    hWindow: HWND;
    AMessage: TMsg;
    FWindowProcedure: TFNWndProc;
    FApplicationName: string;

    function WinRegister: Boolean;
    procedure CreateMyWindow;
  public
    constructor Create;
    destructor Destroy;override;
    procedure WinCreate;
    procedure Run;

    property ApplicationName: string read FApplicationName write FApplicationName;
    property WindowProcedure: TFNWndProc read FWindowProcedure write FWindowProcedure;
  end;

function WindowProc(Window: HWND;AMessage: UINT;WParam: WPARAM;LParam: LPARAM): LRESULT;stdcall;export;
var
  dc: HDC;
  ps: PAINTSTRUCT;
  r: TRect;
begin
  Result := 0;

  case AMessage of
    WM_PAINT:
    begin
      dc := BeginPaint(Window,ps);
      try
        GetClientRect(Window,r);
        DrawText(dc,'使用TMyWindow类封装的Window程序.',-1,r,DT_CENTER or DT_SINGLELINE or DT_VCENTER);
      finally
        EndPaint(Window,ps)
      end;
    end;
    WM_LBUTTONDBLCLK:
    begin
      MessageBox(0,'','',MB_OK);
    end;
    WM_DESTROY:
    begin
      PostQuitMessage(0);
    end;
  end;

  WindowProc := DefWindowProc(Window,AMessage,WParam,LParam);
end;

var
  MyWindow: TMyWindow;

{ TMyWindow }

constructor TMyWindow.Create;
begin
  FWindowProcedure := @WindowProc;
  FApplicationName := APPNAME;
end;

procedure TMyWindow.CreateMyWindow;
begin
  hWindow := CreateWindow(PChar(FApplicationName),'MyWindow',WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,System.MainInstance,nil) ;
  if hWindow <> 0 then
  begin
    ShowWindow(hWindow,CmdShow);
    ShowWindow(hWindow,SW_SHOW);
    UpdateWindow(hWindow);
  end;

end;

destructor TMyWindow.Destroy;
begin

  inherited;
end;

procedure TMyWindow.Run;
begin
  while GetMessage(AMessage,0,0,0) do
  begin
    TranslateMessage(AMessage);// 翻译消息
    DispatchMessage(AMessage); // 分派消息
  end;

  Halt(AMessage.wParam);
end;

procedure TMyWindow.WinCreate;
begin
  if WinRegister then
  begin
    CreateMyWindow;
  end;
end;

function TMyWindow.WinRegister: Boolean;
begin
  with WindowClass do
  begin
    //当垂直长度改变或移动窗口时,重画整个窗口
    //当水平长度改变或移动窗口时,重画整个窗口
    style := CS_VREDRAW or CS_HREDRAW;
    //设置消息回调函数
    lpfnWndProc := FWindowProcedure ;
    cbClsExtra := 0;
    cbWndExtra := 0;
    hInstance := System.MainInstance;
    hIcon := LoadIcon(0,IDI_APPLICATION);
    hCursor := LoadCursor(0,IDC_ARROW);
    hbrBackground := GetStockObject(WHITE_BRUSH){COLOR_WINDOW} ;
    lpszMenuName := nil;
    lpszClassName := PChar(FApplicationName);
  end;

  Result := RegisterClass(WindowClass) <> 0;
end;

begin
  MyWindow := TMyWindow.Create;
  MyWindow.WinCreate;
  SetWindowText(MyWindow.hWindow,'面向对象方式设计窗口');
  try
    MyWindow.Run;
  finally
    MyWindow.Free;
    MyWindow := nil;
  end;

end.
原文地址:https://www.cnblogs.com/cxp2009/p/2277017.html