windowsapi编程:Delpih中的Windows API编程初步



   使用RAD类型工具人越来越多了虽然我对于RAD类工具向来不多作评议但我还是常常使用所以我深深知道这类工具虽然给我们带来了便利使我们能不用将更多精力放在界面上但同时也将初学者紧紧圈在了他所提供Control控件和组件中所以很多人并不能真正了解windows消息驱动原理以及windows运作过程本文中我们就起来学习下windows运作过程使我们对delphi这样个优秀编程工具有个新认识并对windows下编写有更深刻、透辟了解和认识

、消息定义

   我们先从使用角度看看windows运作过程我们都知道windows是个多任务平台使用这个平台我们可以边工作边听歌曲等等所以对于这个操作平台可以想象到它除了般操作系统所提供对文件系统内存系统等管理的外更重要就是我们所熟知消息驱动了也就是说要通过思路方法和结构可以给每个运行中例子以及其中个窗口传递其中所触发事件Windows中究竟是怎样做到呢?让我们打开安装delphi目录在其中source\\rtl\\Win\\Windows.pas文件(或者在个工程文件找到uses在其中找到Windows然后按下Ctrl键用鼠标点击单词)在其中第18919行我们可以看到这样个结构定义:

{ Message structure }
PMsg = ^TMsg ;
tagMSG = packed record
hwnd : HWND ;
message : UINT ;
wParam : WPARAM ;
lParam : LPARAM ;
time : DWORD ;
pt : TPo ;
end ;

{ $ EXTERNALSYM tagMSG }
TMsg = tagMSG ;
MSG = tagMSG ;

{ $ EXTERNALSYM MSG }

   其中hwnd字段表示触发了消息窗口ID由此可以保证消息正确发送到每个窗口去 Message 表示消息类型其中更细致解释要通过wParam和lParam起来进行区别消息wParam和lParam值也就不相同time用来记录消息触发时间Pt则表示触发位置(毕竟window中有了鼠标)我们也可以用同样思路方法打开Messages文件其中定义了windows中绝大部分消息和结构下面是我们截取其中部分

const

{ $ EXTERNALSYM WM_NULL }
WM_NULL = $0000 ;

{ $ EXTERNALSYM WM_CREATE }
WM_CREATE = $0001 ;

{ $ EXTERNALSYM WM_DESTROY }
WM_DESTROY = $0002 ;

{ $ EXTERNALSYM WM_MOVE }
WM_MOVE = $0003 ;

{ $ EXTERNALSYM WM_SIZE }
WM_SIZE = $0005 ;
…… ……
WM_APP = $8000 ;

{ NOTE : All Message Numbers below 0x0400 are RESERVED }

{ Private Window Messages Start Here }

{ $ EXTERNALSYM WM_USER }
WM_USER = $0400 ;

…… ……

{ Dialog messages }

{ $ EXTERNALSYM DM_GETDEFID }
DM_GETDEFID = ( WM_USER+0 ) ;

{ $ EXTERNALSYM DM_SETDEFID }
DM_SETDEFID = ( WM_USER+1 ) ;

{ $ EXTERNALSYM DM_REPOSITION }
DM_REPOSITION = ( WM_USER+2 ) ;

{ $ EXTERNALSYM PSM_PAGEINFO }
PSM_PAGEINFO = ( WM_USER+100 ) ;

{ $ EXTERNALSYM PSM_SHEETINFO }
PSM_SHEETINFO = ( WM_USER+101 ) ;

{ Button Notication Codes }

…… ……

   可以看到windows中每个消息都对应着个唯数值当然我们也可以定义自己消息数值只要定义在WM_USER的后保证和其中定义不想重复即可

2、消息接收

   消息到是有了但怎样才能让以及窗口接收到呢?还是从使用角度考虑可以想象到对于或窗口可以接收到来自鼠标、键盘等输入设备消息也可以接收到来自传递消息有了前面tagMSG结构我们就可以被动中接收消息了这个功能实现就是由下面实现(同样这段来自于delphi元代码):

function Tapplication . ProcessMessage ( var Msg : TMsg ) : Boolean;
var
Handled : Boolean ;
begin

Result : = False ;
PeekMessage ( Msg , 0 , 0 , 0 , PM_REMOVE ) then
begin
Result : = True ;
Msg.Message <> WM_QUIT then
begin
Handled := False ;
Assigned ( FonMessage ) then FonMessage ( Msg , Handled ) ;
not IsHMsg ( Msg ) and not Handled and not IsMDIMsg ( Msg ) and not IsKeyMsg ( Msg ) and not IsDlgMsg ( Msg ) then
begin
TranslateMessage ( Msg ) ;
DispatchMessage ( Msg ) ;
end ;
end ;

FTerminate : = True ;
end ;
end ;

其中主要是这几句:

PeekMessage ( Msg , 0 , 0 , 0 , PM_REMOVE ) then
begin
TranslateMessage ( Msg ) ;
DispatchMessage ( Msg ) ;
end ;

但更常见是:

while GetMessage ( Msg , 0 , 0 , 0 ) do
begin
TranslateMessage ( Msg ) ;
DispatchMessage ( Msg ) ;
End ;

   PeekMessage和GetMessage都是从消息队列中得到发给消息只要有消息就通过TranslateMessage ( Msg )和DispatchMessage ( Msg )两句将消息翻译为可处理格式并分派给应用所注册回调进行处理

3、消息处理

   那么消息是怎样被处理呢?回调又有什么作用呢?有了前面知识我们只要能对区别消息进行正确解释就可以做到对消息正确处理了但前面我们提到了对于区别消息传递信息使不相同所以这是API编程中最麻烦部分了



   我们这里先给出个常见而又必须消息框架

function WindowProc ( hwnd : HWnd ; Msg : UINT; Wparam : WPARAM ; Lparam : LPARAM ) : LRESULT ; stdcall ; export ;
var
dc : hdc ;
rc : Trect ;
ps : TpaStruct ;
begin

Msg of
WM_PAINT :
Begin
dc : = BeginPa ( hwnd , ps ) ;
…… ……
EndPa (hwnd, &ps) ;
Exit ;
end;

WM_COMMAND :
…… ……
WM_DESTROY :

Begin
PostQuitMessage ( 0 ) ;
Exit ;
end ;

end;

Result : = DefWindowProc ( hWnd , Msg , wParam , lParam ) ;

end ;

   在这个框架中WindowProc就是我们前面提到回调它是windows设计中重点无论是从输入输出等硬件设备传来消息还是从软件Software传来消息都要保存到系统消息队列中这个消息队列有两种种是系统消息队列主要是用来保存从输入输出等硬件设备传来消息种是每个窗口窗口消息队列主要保存每个窗口发送来消息的后对消息获取和分发工作当然是由前面讲到GetMessage 、 TranslateMessage和DispatchMessage 3个来完成至于消息处理工作则是由WindowProc来完成了也就是说它是由系统在有消息到达时才所以我们称的为回调

   WindowProc是在注册窗口类时注册窗口消息处理,当然名字可以自己命名其中参数有hwnd : HWnd ; Msg : UINT; Wparam : WPARAM ; Lparam : LPARAM 这也就是我们前面谈到消息和窗体

   这里我们主要使用了 3种消息:WM_PAINT WM_COMMAND和WM_DESTROY 但是我们可以随着而是用各种各样消息为了处理区别消息中使用了分支结构所以随着规模越来越大这个分支结构也会越来越庞大

   在这些消息中有两个点是最为重要是WM_DESTROY消息它表示个销毁窗口退出应用消息也是每个所必备对于这个消息处理方式就是通过PostQuitMessage ( 0 )传递个WM_QUIT消息准备让由GetMessage 、 TranslateMessage和DispatchMessage 3个组成消息循环中GetMessage取得当消息循环中收到WM_QUIT消息时GetMessage会传回0从而结束消息循环进而释放各种资源结束整个个重要地方是DefWindowProc我们无论多大都不可能将所有消息都处理所以我们必须有个机制让不重要不需要我们处理消息交给windows操作系统为我们处理这个过程就是由DefWindowProc来实现

   因此当我们按下窗口右上角差号或者按下左上角系统菜单中Close命令时系统会送出WM_CLOSE消息通常窗口不拦截此消息于是交由DefWindowProc来处理DefWindowProc在受到WM_CLOSE消息后DestroyWindow把窗口清除DestroyWindow又会送出WM_DESTROY消息又如前面讲到样来结束释放资源

4、建立窗口类

   知道了消息传递和处理的后我们来看看有关窗口知识

   Windows带给我们不仅是技术上创新更重要是统而又便捷窗口那么它是怎样创建呢?这就要从窗口类tagWNDCLASSA说起了

   让我们先打开delphi目录下source\\rtl\\Win\\Windows.pas文件18875行可以看到这样个结构:

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

   其中存储了个窗口所有相关信息style : UINT 表示窗口风格;lpfnWndProc : TFNWndProc 表示窗口消息处理;hInstance : HINST 表示窗口个应用例子;Icon : HICON 用来记录窗口图标;hCursor : HCURSOR 记录窗口光标;hbrBackground : HBRUSH 用来记录窗口背景色;lpszMenuName : PansiChar 表示窗口中菜单资源名称; lpszClassName : PansiChar 记录窗口类名称

下面是个例子:

WindowClass . style : = CS_VREDRAW + CS_HREDRAW + CS_DBLCLKS ;
WindowClass . lpfnWndProc : = @DefWindowProc ;
WindowClass . hCursor : = LoadCursor ( 0 , IDC_ARROW ) ;
WindowClass . hbrBackground : = 0 ;
WindowClass . hInstance : = Hinstance ;
StrPCopy ( WinClassName , ClassName ) ;

5、注册窗口类

   当我们按照要求创建了这个窗口类的后我们就可以在系统中注册它了这就要用到function RegisterClass(const lpWndClass: TWndClass): ATOM; stdcall;这样他只有个参数就是我们先前说注册窗口类

6、创建窗口

   有了前面几步现在我们可以创建我们所注册窗口看看她真面目了

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

   这个可以帮助我们创建我们先前注册窗口其中参数lpClassName : Pchar表示我们前面注册窗口类名称lpWindowName : PChar 表示窗口标题; dwStyle : DWORD 表示窗口风格; X , Y , nWidth , nHeight : Integer ; 表示窗口位置和宽度高度;

7、显示窗口

   窗口创建了但我们只有在function ShowWindow ( hWnd : HWND ; nCmdShow : Integer ) : BOOL ; stdcall ;的后才会显示出来这个很简单hWnd : HWND 表示窗口句柄 nCmdShow : Integer则是窗口显示方式function UpdateWindow ( hWnd : HWND ) : BOOL ; stdcal l ;则会送出个WM_PAINT消息使窗体得到更新



   也许你会觉得很烦人但这是所有windows基础即便是我们用delphi编程时也都是这样运行只是delphi创造者将切都隐藏到了个美丽外表的下

   下面我们用大家最常见个例子对前面知识加以整理总结在这个例子中我们将在窗体中显示“ hello , world ! ” 下面是及其运行效果:

program Project1;

{ $ APPTYPE CONSOLE }
uses
Windows ,
Messages ;
{ uses SysUtils ; }

var
wClass : TWndClass; // 主窗口类
hInst , //应用句柄
Handle : HWnd ; // 主窗口
aMsg : TMsg ; //消息
RCT : TRect ; //区域
ps : TPaStruct ; //显示
dc : hdc ; //设备上下文

//:WindowProc
//作用:处理主窗口消息
function WindowProc ( hWnd , Msg , wParam , lParam : Long ) : Long ; stdcall ;
begin

WindowProc : = 0 ;
Msg of
WM_PAINT :
begin
dc : =BeginPa ( hWnd , ps ) ;
GetClientRect ( hWnd , RCT ) ;
DrawText ( dc , \' hello , world ! \' , -1 , RCT , Dt_SINGLELINE or DT_CENTER or DT_VCENTER ) ;
EndPa ( hWnd , ps ) ;
Exit ;
end ;
WM_DESTROY : //结束应用
Begin
PostQuitMessage ( 0 ) ;
Exit ;
end ;
end ;

Result : = DefWindowProc ( hWnd , Msg , wParam , lParam ) ; //消息默认处理
end ;

//主窗口
begin

// hInst : = GetModuleHandle ( nil ) ; // 获得应用句柄
with wClass do //化窗口类
begin
hInstance : = system.MainInstance ;
Style : = CS_HREDRAW or CS_VREDRAW ;
HIcon : = LoadIcon ( 0 , IDI_APPLICATION ) ;
LpfnWndProc : = @WindowProc ;
HbrBackground : = GetStockObject ( WHITE_BRUSH ) ;
lpszClassName : = \' Sample Class \' ;
hCursor : = LoadCursor ( 0 , IDC_ARROW ) ;
end ;

RegisterClass ( wClass ) ; // 注册窗口类

//创建主窗口
Handle : = CreateWindow (
\' Sample Class \' , // 窗口类名
\' Windows API在Delphi中应用 \' , //窗口标题
WS_OVERLAPPEDWINDOW or WS_VISIBLE , // 窗口风格
10 , //左边界坐标
10 , //上边界坐标
400 , // 宽度
300 , // 高度
0 , // 父窗口句柄
0 , //菜单句柄
system . MainInstance , // 应用例子
nil //创建窗口附加参数
) ;

Handle <> 0 then
begin

ShowWindow ( Handle , SW_SHOW ) ;
UpdateWindow ( Handle ) ;

end ;

while ( GetMessage ( aMsg , Handle , 0 , 0 ) ) do //消息循环
begin
TranslateMessage ( aMsg ) ; //翻译消息
DispatchMessage ( aMsg ) ; //发送消息
end ;

end .

效果如下图:



8、初步封装 面向过程方式编写

   可以看到对于任何个Windows创建和运行都要经过上面几个步骤而且这些步骤又很有条理所以我们又可以将区别功能封装在几个命名规范标准且容易理解的中下面是修改后代码:

program Project1;

{ $ APPTYPE CONSOLE }

uses
Windows ,
Messages ;
{ uses SysUtils ; }

var
wClass : TWndClass; // 主窗口类
hInst , //应用句柄
Handle : HWnd ; // 主窗口
aMsg : TMsg ; //消息
RCT : TRect ; //区域
ps : TPaStruct ; //显示
dc : hdc ; //设备上下文

//:WindowProc
//作用:处理主窗口消息
function WindowProc ( hWnd , Msg , wParam , lParam : Long ) : Long ; stdcall ;
begin

WindowProc : = 0 ;

Msg of
WM_PAINT :
begin
dc : =BeginPa ( hWnd , ps ) ;
GetClientRect ( hWnd , RCT ) ;
DrawText ( dc , \' hello , world ! \' , -1 , RCT , Dt_SINGLELINE or DT_CENTER or DT_VCENTER ) ;
EndPa ( hWnd , ps ) ;
Exit ;
end ;

WM_DESTROY : //结束应用
Begin
PostQuitMessage ( 0 ) ;
Exit ;
end ;

end ;

Result : = DefWindowProc ( hWnd , Msg , wParam , lParam ) ; //消息默认处理

end ;

//:WinRegister
//作用:注册窗口类
function WinRegister : Boolean ;
begin

with wClass do //化窗口类
begin
hInstance : = system.MainInstance ;
Style : = CS_HREDRAW or CS_VREDRAW ;
HIcon : = LoadIcon ( 0 , IDI_APPLICATION ) ;


LpfnWndProc : = @WindowProc ;
HbrBackground : = GetStockObject ( WHITE_BRUSH ) ;
lpszClassName : = \' Sample Class \' ;
hCursor : = LoadCursor ( 0 , IDC_ARROW ) ;
end ;

ReSult : = RegisterClass ( wClass ) <> 0 ; // 注册窗口类
end ;

//:WinCreate
//作用:创建窗口
function WinCreate : HWnd ;
begin

//创建主窗口
Handle : = CreateWindow (
\' Sample Class \' , // 窗口类名
\' Windows API在Delphi中应用 \' , //窗口标题
WS_OVERLAPPEDWINDOW or WS_VISIBLE , // 窗口风格
10 , //左边界坐标
10 , //上边界坐标
400 , // 宽度
300 , // 高度
0 , // 父窗口句柄
0 , //菜单句柄
system . MainInstance , // 应用例子
nil //创建窗口附加参数
) ;

Handle <> 0 then
begin
ShowWindow ( Handle , SW_SHOW ) ;
UpdateWindow ( Handle ) ;
end ;

Result : = Handle ;
end ;


//主窗口
//进入点
begin
not WinRegister then //:WinRegister注册窗口类
begin
MessageBox ( 0 , ‘ Register failed ‘ , nil , MB_OK ) ;
Exit ;
end ;

Handle : = WinCreate ; //:WinCreate创建窗口

long ( Handle ) = 0 then
begin
MessageBox ( 0 , ‘ WinCreate failed ‘ , nil , MB_OK ) ;
Exit ;
end;

while ( GetMessage ( aMsg , Handle , 0 , 0 ) ) do //消息循环
begin
TranslateMessage ( aMsg ) ; //翻译消息
DispatchMessage ( aMsg ) ; //发送消息
end ;

end .


9、进步封装面向对象思路方法编写

   更进我们就可以考虑用面向对象思路方法来编写

   可以看到如果我们要用上面思路方法编写工作量是十分繁重尤其在规模越来越大情况下出错可能性也就会成指数性增长即使我们使用粘贴拷贝仍然要对进行修改出错可能人不会避免而面向对象编程思路方法可以有效克服以上不足大大提高了代码利用率所以下面我们就用面向对象思路方法来看看同样我们应该怎样编写

   面向对象核心就是要用类思路方法来建立框架然后用类例子思路方法属性等手段来实现最终效果所以第步我们首先又来建立个窗口其中共有成员Create和 Destroy分别用来创建和销毁个我们建立窗口例子共有成员WinCreate则私有成员WinRegister和 CreateMyWindow来注册和创建窗口其中属性变量ApplicationName 和 WindowProcedure 则是用来获得窗口类名和窗口消息处理下面是修改后源代码:

//用面向对象思路方法来编写
program TMyWindowClass;

uses
Windows,
Messages,
SysUtils;

//创建窗口类
type
TMyWindow = ( TObject )

private

{ 定义私有变量 }

WindowClass : WndClass;
hWindow : HWnd ;
AMessage : TMsg ;
FAppName : String ;
FWndProc : TFNWndProc ;
function WinRegister : Boolean ; virtual ;

procedure CreateMyWindow ;

public

{定义公有变量 }

constructor Create ;
destructor Destroy ; override ;
procedure WinCreate ; virtual ;
procedure Run ;

{定义属性 }

property ApplicationName : String read FAppName write FAppName ;
property WindowProcedure : TFNWndProc read FWndProc write FWndProc ;

end ;

const

AppName = \' MyClassshili \' ;
var
myWindow : tMyWindow ;

{ TMyWindow类中公有实现 }

constructor TMyWindow . Create ;
begin

end ;

destructor TMyWindow . Destroy ;
begin
inherited ;
end ;

procedure TMyWindow . CreateMyWindow ; //创建窗口
begin
hWindow : = CreateWindow ( AppName , \' hello , world \' , ws_OverlappedWindow , cw_UseDefault , cw_UseDefault , cw_UseDefault , cw_UseDefault , 0 , 0 , system . MainInstance , nil ) ;

hWindow <> 0 then begin
ShowWindow ( hWindow , CmdShow ) ;
ShowWindow ( hWindow , SW_SHOW ) ;
UpdateWindow ( hWindow ) ;
end ;

end ;

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

end ;


function TMyWindow . WinRegister : Boolean ; //注册窗口类
begin


WindowClass.Style : = cs_hRedraw or cs_vRedraw ;
WindowClass . lpfnWndProc : = FWndProc ;
WindowClass . cbClsExtra : = 0 ;
WindowClass . cbWndExtra : = 0 ;
WindowClass . hInstance : = system.MainInstance ;
WindowClass . hIcon : = LoadIcon ( 0 , idi_Application ) ;
WindowClass . hCursor : = LoadCursor ( 0 , idc_Arrow ) ;
WindowClass . hbrBackground : = GetStockObject ( WHITE_BRUSH ) ;
WindowClass . lpszMenuName : = nil ;
WindowClass . lpszClassName : = PChar ( FAppName ) ;
Result : = RegisterClass ( WindowClass ) <> 0 ;
end ;


function WindowProc ( Window : HWnd ; Amessage : UINT ; WParam : WPARAM ;
LParam : LPARAM ) : LRESULT ; stdcall ; export ;

//消息处理
var
dc : hdc ;
ps : TPaStruct ;
r : TRect ;

begin
WindowProc : = 0 ;
AMessage of
WM_PAINT :
begin
dc : = BeginPa ( Window , ps ) ;
GetClientRect ( Window , r ) ;
DrawText ( dc , \' hello , world ! ! \' , -1 , r , DT_SINGLELINE or DT_CENTER or DT_VCENTER ) ;
EndPa ( Window , ps ) ;
Exit ;
end ;

wm_Destroy :
begin
PostQuitMessage ( 0 ) ;
Exit ;
end ;
end ;

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


procedure TMyWindow . MyRun ;
begin
while GetMessage ( AMessage , 0 , 0 , 0 ) do begin
TranslateMessage ( AMessage ) ;
DispatchMessage ( AMessage ) ;
end ;

Halt ( AMessage . wParam ) ;
end ;


//主运行进入口
begin

myWindow : = TMyWindow . Create ;
myWindow . ApplicationName : = AppName ;
myWindow . WindowProcedure : = TFNWndProc ( @WindowProc ) ;
myWindow . WinCreate ;

try
myWindow . MyRun ;

finally
FreeAndNil ( myWindow ) ;

end ;
end .

好了就这些吧相信有了这些基础应该在看delphi源代码时容易了许多毕竟很多代码在此都很熟悉了

Tags:  windowsapi函数 windowsapi delpih windowsapi编程

延伸阅读

最新评论

发表评论