Delphi直接用Windows API编程

更新时间:2024-07-08 13:35:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

Delphi程序员往往习惯了用VCL元件编程,其实Delphi也能进行基于WINDOWS API SDK的编程。而且用Delphi在某些方面效果似乎比用Visual C++效果还要好。比如本例程,用Delphi 6编译出来只有9216字节(9k)而同样的Visual C++程序却有16896字节(17k)。(此例程是笔者从网上下载的c++源码例程,其中有c源程序,和编译好的.exe文件。源代码经笔者改写成Delphi代码。)这证明Delphi编译器的优化效果非常好。

API是(Application Programming Interface)的缩写,意为应用编程界面,它包含了编写Windows所有函数、数据类型。VCL就是以它为基础进行封装的,它是应用程序在Windows 上运行的基础。通过熟悉使用WINDOWS API SDK直接编制WINDOWS程序,程序员将对WINDOWS的执行机制有更深入的了解,从而编写出更高效、实用的程序。

下面是我们用API函数建立的第一个程序:

1 : program HELLOWIN; 2 :

3 : uses

4 : windows, Messages ,mmsystem; 5 : 6 : 7 : 8 : var

9 : sz_appname:array [0..8] of char='HelloWin'#0; 10 : Win_Class: WNDCLASSEX; //窗口类

11 : w_Handle,inst:HWND;//w_Handle窗口句柄、程序句柄 12 : w_msg:TMSG; //消息数据 13 :

14 : function WindowProc(h_Wnd,u_Msg,w_Param,l_Param: LONGINT):LRESULT;stdcall; 15 : //回调函数

16 : var p_hdc:hdc; 17 : p_rect:trect;

18 : ps : PAINTSTRUCT ; 19 : begin 20 : 21 :

22 : case u_msg of

23 : WM_DESTROY : PostQuitMessage (0);

24 : WM_CREATE : PlaySound (pchar('hellowin.wav'#0), 0, SND_FILENAME or SND_ASYNC) ;

25 : WM_PAINT :begin

26 : p_hdc := BeginPaint (h_wnd, ps) ;

1

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : GetClientRect (h_wnd, p_rect);

DrawText (p_hdc, pchar('Hello, Windows!'#0), -1, p_rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER) ; EndPaint (h_wnd, ps) ; end;

end;

Result := DefWindowProc(h_Wnd, u_Msg, w_Param, l_Param); end;

begin

Inst := hInstance;

win_class.cbSize := sizeof (win_class) ;

win_class.style := CS_HREDRAW or CS_VREDRAW ; win_class.lpfnWndProc := @WindowProc ; win_class.cbClsExtra := 0 ; win_class.cbWndExtra := 0 ; win_class.hInstance := Inst ;

win_class.hIcon := LoadIcon (0, IDI_APPLICATION) ; win_class.hCursor := LoadCursor (0, IDC_ARROW) ;

win_class.hbrBackground := HBRUSH (GetStockObject (WHITE_BRUSH)) ; win_class.lpszMenuName := nil ;

win_class.lpszClassName := @sz_AppName ;

win_class.hIconSm := LoadIcon (0, IDI_APPLICATION) ; RegisterClassEx(Win_Class);

w_Handle:=CreateWindow(@sz_appname, pchar('The Hello Program'#0), WS_OVERLAPPEDWINDOW,200,200,300,300,0,0, Inst,nil) ;

ShowWindow (w_Handle, SW_SHOWNORMAL) ; UpdateWindow(w_Handle);

while(GetMessage(w_msg, 0, 0, 0)) do begin

TranslateMessage(w_msg); DispatchMessage(w_msg); end;

end.

2

以上源程序读者可以直接拷贝到记事本中,把行标去掉后另存为.dpr文件,然后用Delphi直接打开,就可以编译运行,之后你就会听到电脑向你发出的问候。(hellowin.wav文件可以自己用windows录音机录制)

其中主程序(40-70行)的功能如下:

设置窗口变量(42-53行)

注册窗口(54行)

显示窗口(59-60行)

消息循环(63-67行)

以下根据以上四部分分别介绍:

一、设置窗口变量:每一个标准的Windows应用程序都至少有一个主窗口,在本程序中代表主窗口的变量就是Win_Class,它的类型是 WNDCLASSEX,其在windows.pas的声明如下:

tagWNDCLASSEXA = packed record

cbSize: UINT; //设置成窗口类型的大小 style: UINT; //窗口类风格

lpfnWndProc: TFNWndProc; //指向该窗口的回调函数(本程序为WindowProc) cbClsExtra: Integer; //窗口类变量的扩展字节数 cbWndExtra: Integer; //窗口实例的扩展字节数 hInstance: HINST; //窗口的实例句柄 hIcon: HICON; //窗口的图标句柄 hCursor: HCURSOR; //窗口鼠标指针句柄

hbrBackground: HBRUSH; //刷新窗口用户区背景的画刷句柄 lpszMenuName: PAnsiChar; //窗口类包含的菜单的名称 lpszClassName: PAnsiChar;; //窗口类的名称 hIconSm: HICON; //窗口的小图标句柄 end;

WNDCLASSEXA = tagWNDCLASSEXA; WNDCLASSEX = WNDCLASSEXA;

? 其中style的设置是以下各值通过位运算符or(或)联接合成。 风 格 含 义 3

CS_HREDRAW CS_VREDRAW CS_DBLCLKS CS_NOCLOSE CS_OWNDC CS_CLASSDC CS_PARENTDC CS_SAVEBITS 如果窗口宽度发生改变,重画整个窗口 如果窗口高度发生改变,重画整个窗口 能感受窗口中的双击消息 禁用系统菜单中的“关闭”命令 为该窗口类的各窗口分配各自独立的设备环境 为该窗口类的各窗口分配一个共享的设备环境 指定子窗口继承其父窗口的设备环境 把被窗口遮掩的屏幕图象部分作为位图保存起来。当该窗口被移动时,Windows使用被保存的位图来重建屏幕图象 ? LpfnWndProc域是一个函数指针,它指向窗口的“回调函数”。本程序回调函数是WindowProc。

回调函数指的是本窗口的消息处理函数。所谓“回调”指的是此函数在本程序中没有语句直接调用它,而调用它的是windows。每个窗口程序建立好之后,windows为每个程序维护一个“消息队列”。用户的操作如鼠标、键盘输入等都是先由windows处理接收的,windows判断如果用户的操作是针对本窗口程序的,就把此消息放到程序的消息队列中。而程序窗口通过消息循环语句(本程序的63-67行)取出消息(GetMessage函数),最后通过调用DispatchMessage函数把消息传回windows,再由windows调用回调函数(本程序是WindowProc)处理消息,回调函数把消息处理完后(很多缺省消息的处理是回调函数再调用windows来处理的),windows结束DispatchMessage调用。应用程序进行下一个消息处理循环。其关系如下图所示:

? cbClsExtra和wc.cbWndExtra在大多数情况下都会设为0。

? hInstance它的值是应用程序的实例句柄,表明该窗口与该实例是相

4

关联的。

? hIcon是窗口的图标句柄。

? hCursor是窗口的鼠标光标句柄。 ? hbrBackground是窗口的背景颜色。

? lpszMenuName是标志菜单资源的字符串。 ? lpszClassName此窗口类的名称。 ? hIconSm是窗口的小图标句柄,它是显示在任务栏上的小图标,和窗口左上角的图标。

二、注册窗口类:用RegisterClassEx函数注册窗口。其在windows.pas的声明如下:

function RegisterClassEx(const WndClass: TWndClassEx): ATOM; stdcall;

此函数的参数就是窗口类TwndClassEx,注册成功后返回非零,反之出错返回0。

三、创建显示窗口。

创建窗口用CreateWindow函数,他返回一个窗口句柄。其在windows.pas的声明如下:

function CreateWindow(lpClassName: PChar; lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer; hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND; CreateWindow函数的参数说明如下:

? lpClassName 创建窗口所用的窗口类的名称 ? lpWindowName 窗口标题

? dwStyle 窗口风格,定义为普通型* ? X 窗口位置的x坐标 ? Y 窗口位置的y坐标 ? nWidth 窗口的宽度 ? nHeigh 窗口的高度 ? hWndParent 父窗口句柄 ? hMenu 菜单句柄

? hInstance 应用程序实例句柄 ? lpParam 是附加数据指针 第三个参数dwStyle 的值是窗口的风格,下表列出了常用的风格:

风 格 含 义 WS_OVERLAPPEDWINDOW 创建一个层叠式窗口,有边框、标题栏、系统菜单、最大最小化按钮,是以下几种风格的集合:WS_OVERLAPPED, 5

WS_POPUPWINDOW WS_OVERLAPPED WS_POPUP WS_BORDER WS_CAPTION WS_CHILD WS_DISABLED WS_HSCROLL WS_ICONIC WS_MAXIMIZE WS_MAXIMIZEBOX WS_MINIMIZE WS_MINIMIZEBOX WS_SIZEBOX WS_SYSMENU WS_THICKFRAME WS_TILED WS_VISIBLE WS_VSCROLL WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, WS_MAXIMIZEBOX 创建一个弹出式窗口,是以下几种风格的集合: WS_BORDER,WS_POPUP,WS_SYSMENU。WS_CAPTION与WS_POPUPWINDOW风格必须一起使用才能使窗口菜单可见 创建一个层叠式窗口,它有标题栏和边框,与WS_TILED风格一样 该窗口为弹出式窗口,不能与WS_CHILD同时使用 窗口有单边框 窗口有标题栏 该窗口为子窗口,不能与WS_POPUP同时使用 该窗口为无效,即对用户操作不产生任何反应 窗口有水平滚动条 窗口初始化为最小化 窗口初始化为最大化 窗口有最大化按钮 与WS_MAXIMIZE一样 窗口有最小化按钮 边框可进行大小控制的窗口 创建一个有系统菜单的窗口,必须与WS_CAPTION风格同时使用 创建一个大小可控制的窗口,与WS_SIZEBOX 风格一样. 创建一个层叠式窗口,有标题栏 窗口为可见 窗口有垂直滚动条

显示窗口用ShowWindow函数显示窗口。其在windows.pas的声明如下:

function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall; 其第一个参数hWnd是窗口句柄。

第二个参数nCmdShow决定了如何显示窗口,其取值含义如下表: 值 S W_FORCEMINIMIZE 含义 在WindowNT5.0中最小化窗口,即使拥有窗口的线程被挂起也会最小化。在从其他线程最小化窗口时才使用这个参数。 隐藏窗口并激活其他窗口。 最大化指定的窗口。 最小化指定的窗口并且激活在Z序中的下一个顶层窗口。 激活并显示窗口。如果窗口最小化或最大化,则系统将6

SW_MIOE SW_MAXIMIZE SW_MINIMIZE SW_RESTORE

SW_SHOW SW_SHOWDEFAULT SW_SHOWMAXIMIZED SW_SHOWMINIMIZED SW_SHOWMINNOACTIVATE SW_SHOWNA SW_SHOWNOACTIVATE SW_SHOWNOMAL: 窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。 在窗口原来的位置以原来的尺寸激活和显示窗口。 依据在STARTUPINFO结构中指定的SW_FLAG标志设定显示状态,STARTUPINFO 结构是由启动应用程序的程序传递给CreateProcess函数的。 激活窗口并将其最大化。 激活窗口并将其最小化。 窗口最小化,激活窗口仍然维持激活状态。 以窗口原来的状态显示窗口。激活窗口仍然维持激活状态。 以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态。 激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小。应用程序在第一次显示窗口的时候应该指定此标志。

四、建立消息循环。

前面在叙述回调函数时,已经讨论了部分消息循环这方面的内容,在这我们再详细阐述这方面的内容。先看代码63-67行:

63 64 65 66 67

: : : : :

while(GetMessage(w_msg, w_Handle, 0, 0)) do begin

TranslateMessage(w_msg); DispatchMessage(w_msg); end;

我们已经知道GetMessage函数的功能是程序从消息队列中取出消息,其在windows.pas中的声明如下:

function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): BOOL; stdcall;

第一个参数是要接收消息的MSG结构变量,其声明为: tagMSG = packed record hwnd: HWND; message: UINT; wParam: WPARAM; lParam: LPARAM; time: DWORD; pt: TPoint; end;

7

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

{$EXTERNALSYM MSG} 其各域的说明如下:

? hwnd接收消息的窗口句柄,如果一个应用程序中有多个窗口,此参数就可决定让哪个窗口接收消息。

? message是一个32位无符号整数,它唯一标识了一种消息类型。每种消息类型都在Messages.PAS文件中定义了,这些常量都以WM_开头。比如说当窗口建立时,Windows就向应用程序发送一条WM_CREATE消息。

? wParam副消息值,其具体含义依赖于主消息值。 ? lParam副消息值,其具体含义依赖于主消息值。 ? time消息放入消息队列中的时间,是从Windows启动后所测量的时间值。Windows用这个域来使用消息保持正确的顺序。 ? pt消息放入消息队列时的鼠标坐标。

GetMessage函数第二个参数表示窗口句柄,NULL则表示要获取该应用程序创建的所有窗口的消息;第三,四参数指定消息范围。后面三个参数被设置为默认值(0,0,0),这就是表示程序将接收所有属于它的任何一个窗口的所有消息。在接收到WM_QUIT消息后GetMessage函数返回假(FALSE),除了WM_QUIT消息GetMessage函数对其余消息都返回TRUE。因此,在接收到WM_QUIT之前,程序中的消息循环(63-67行)可以一直循环下去。在接收到WM_QUIT之后意味着程序消息循环结束,终止程序。

在GetMessage函数执行后是TranslateMessage函数,此函数进行一些键盘消息转换(以后在详细讨论)。

然后是DispatchMessage函数将消息结构回传给Windows,Windows再调用程序本身的回调函数来完成各种消息的处理,以便程序实现各种功能。在此我们详细看一下回调函数的结构。本程序的回调函数是从14行到35行:

14 15 16 17 18 19 20

:function WindowProc(h_Wnd,u_Msg,w_Param,l_Param: LONGINT):LRESULT;stdcall; ://回调函数 :var p_hdc:hdc; : p_rect:trect;

: ps : PAINTSTRUCT ; :begin :

8

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 :

:case u_msg of

: WM_DESTROY : PostQuitMessage (0);

: WM_CREATE :PlaySound(pchar('hellowin.wav'#0),0,SND_FILENAME or SND_ASYNC) ; : WM_PAINT :begin

: p_hdc := BeginPaint (h_wnd, ps) ; : GetClientRect (h_wnd, p_rect);

: DrawText (p_hdc, pchar('Hello, Windows!'#0), -1, p_rect, : DT_SINGLELINE or DT_CENTER or DT_VCENTER) ; : EndPaint (h_wnd, ps) ; : end; : : end;

: Result := DefWindowProc(h_Wnd, u_Msg, w_Param, l_Param); :end;

回调函数(本例是WindowProc)的参数格式是固定的,四个32位长整数: ? h_wnd是接收消息窗口的句柄(实际上就是32位长整数) ? u_Msg是主消息值 ? w_Param是副消息值 ? l_Param是副消息值

容易看出本程序处理了三个消息:

? WM_DESTROY:注销窗口消息,第23行,施行停机处理。此消息只出

现一次,即窗口关闭时。 ? WM_CREATE:窗口建立消息,第24行用PlaySound函数播放一段.wav

声音文件文件。此消息只出现一次,即窗口创建时。

? WM_PAINT:窗口重画消息,第25行到第31行,在窗口的用户区当

中位置显示“Hello, Windows!”。此消息发生的比较频繁,当窗口被调整大小时,被遮挡重新显示时等都发生此消息。此段程序的原理在下一节详细论述。

由程序结构可以知道,回调函数主要用了case语句解析判断u_msg中的消息,做分门别类的处理。以上程序只处理了三条消息,实际上在消息队列中还有很多消息,比如:WM_MOVE(窗口移动)、WM_KEYDOWN(键盘消息)、WM_RBUTTONDOWN(鼠标按键消息)等等,这些消息需要Windows进行处理,为此在函数的最后,需要调用DefWindowProc函数让Windows来对所有消息进行缺省处理。

综合以上所述,一个标准的Windows程序分成两部分:

9

一、 主程序建立程序框架,即初始化窗口,并建立消息循环。 二、 回调函数响应Windows消息,在相应的消息处理过程中实现程序功

能。

Windows中的句柄、标识符和数据类型:

另外我们在以上的叙述中经常看到一个词“句柄”。什么是句柄呢?句柄是一个32位的数,它在Windows中代表了一个对象。句柄的值主要是Windows分配提供的,如程序的窗口句柄就是CreateWindow函数返回的。应用程序需要保存这些句柄,以便以后调用API函数使用。在Windows定义了很多种类的句柄,下表示一些常用的句柄类型:

句柄类型 HANDLE HBITMAP HBRUSH HCTR HCURSOR HDC HDLG HFONT HICON HINSTANCE HMENU HMODULE HPALETTE HPEN HRGN HTASK HWND 说明 指向对象的句柄 保存位图信息的内存域的句柄 画刷句柄 子窗口控件句柄 鼠标光标句柄 设备描述表句柄 对话框句柄 字体句柄 图标句柄 应用程序的实例句柄 菜单句柄 模块句柄 颜色调色板句柄 在设备上画图时用于指明线型的笔的句柄 剪贴区域句柄 独立于已执行任务的句柄 窗口句柄

另外在Windows.PAS和Messages.PAS中定义了很多常量标识符,用以表示Windows中用到的数据信息。比如窗口重画消息:WM_DESTORY,再如窗口风格:WS_OVERLAPPEDWINDOW,可见有些标识符是由两个或三个字母组成并后跟下划线作为前缀,下表列出了常用的标识符前缀:

类别 前缀 CS CW 窗口类风格(窗口类设置) 创建窗口选项 10

DT IDI IDC MB SND WM WS 绘制文本选项 图标ID号 光标ID号 信息框选项 声音选项 窗口消息选项 窗口风格选项

本实例,讲述一个窗口应用程序的基本框架,和一些基本的API函数、数据结构的用法。读者至此应该对Windows的运营机制有了一个初步的了解,更可以体会到直接用API编程的威力,生成的程序只有9K左右,如果你用VCL建立一个同样的程序大概生成的程序要有二三百K。这不表明VCL编程就没用了,VCL编程是一种快速,功能强大的开发手段。而用API编程可以在一些对代码执行效率要求很高和实现一些特殊功能时大有用武之地。并且对程序员提高编程水平和认识Windows程序的本质有很大帮助。

11

本文来源:https://www.bwwdw.com/article/1kd.html

Top