深入delphi编程理解之消息(一)WINDOWS原生窗口编写及消息处理过程
程序员文章站
2022-03-03 08:18:59
通过以sdk方式编制windows窗口程序,对理解windows消息驱动机制和delphi消息编程有很大的帮助。 sdk编制windows窗口程序的步骤: 1、对TWndClass对象进行赋值; 2、向系统注册wndclass对象(RegisterClass); 3、CreateWindow创建窗口 ......
通过以sdk方式编制windows窗口程序,对理解windows消息驱动机制和delphi消息编程有很大的帮助。
sdk编制windows窗口程序的步骤:
1、对twndclass对象进行赋值;
2、向系统注册wndclass对象(registerclass);
3、createwindow创建窗口,获得窗口句柄hwnd;
4、显示窗口(showwindow);
5、通过getmessage函数不断获取系统消息,交给程序处理,程序过通回调函数(wndproc)处理系统消息。(消息处理部分)
程序代码如下:
program project2; //{$apptype console} uses windows, messages; //============================================================================== // 定义回调函数 wndproc(),处理windows消息; // 参数说明: // hwnd 可以理解为窗口句柄 // amessage 可理解为消息的编号 // wparam 消息的高位 // lparam 消息的低位 //============================================================================== function wndproc(hwnd: thandle; amessage: longint; wparam: wparam; lparam: lparam): lresult; stdcall; begin case amessage of wm_destroy: //处理窗口退出消息 begin postquitmessage(0); result := 0; end else {将未处理的消息交给系统处理} result := defwindowprocw(hwnd, amessage, wparam, lparam); end; end; procedure wndmain; var msg: tmsg; wndclass: twndclass; hwnd: thandle; begin {给窗口类赋值} wndclass.lpfnwndproc := @wndproc; //窗口回调函数,处理windows消息 wndclass.style := cs_hredraw or cs_vredraw; ; //窗口类型 wndclass.cbclsextra := 0; wndclass.cbwndextra := 0; wndclass.hinstance := hinstance; wndclass.hicon := loadicon(0, idi_application); wndclass.hcursor := loadcursor(0, idc_arrow); wndclass.lpszclassname := 'mywndclass'; wndclass.lpszmenuname := nil; wndclass.hbrbackground := getstockobject(gray_brush); {向系统注册窗口类} registerclass(wndclass); {创建窗口} hwnd := createwindow(wndclass.lpszclassname, '用sdk编写的windows窗口', ws_overlapped, 100, 100, 300, 200, 0, 0, hinstance, 0); if hwnd <> 0 then begin {显示窗口} showwindow(hwnd, sw_shownormal); updatewindow(hwnd); {处理窗口消息} while getmessage(msg, 0, 0, 0) do begin translatemessage(msg); dispatchmessage(msg); end; end; end; begin wndmain; { todo -ouser -cconsole main : insert code here } end.
一、运行界面如下:
二、程序函数说明:
(一)、窗口类型 twndclass 其实是一个结构, 是 tagwndclassa 结构的重命名.
{tagwndclassa 结构:} tagwndclassa = packed record style: uint; {窗口风格, 见下表} lpfnwndproc: tfnwndproc; {窗口回调函数的指针, 后面要详细分析} cbclsextra: integer; {为窗口类分配的额外空间, 一般为 0} cbwndextra: integer; {为窗口实例分配的额外空间, 一般为 0} hinstance: hinst; {窗口所在程序实例的句柄, 就是 hinstance} hicon: hicon; {指定窗口图标, 一般用 loadicon 加载; 不指定可设为 0} hcursor: hcursor; {指定窗口光标, 一般用 loadcursor 加载; 不指定可设为 0} hbrbackground: hbrush; {指定窗口背景画刷, 这需要用 getstockobject 函数检索; 也可以直接指定系统颜色} lpszmenuname: pansichar; {菜单资源名称; 一般置为 nil, 表示窗口没有默认菜单} lpszclassname: pansichar; {给该窗口类命名; createwindow 函数将使用这个名称} end; //窗口风格参数 style 可选值: cs_vredraw = dword(1); {窗口高度变化时将被重绘} cs_hredraw = dword(2); {窗口宽度变化时将被重绘} cs_keycvtwindow = 4; {} cs_dblclks = 8; {不忽略鼠标双击的消息} cs_owndc = $20; {给用该类建立的每一个窗口分配独立的设备 dc} cs_classdc = $40; {让属于该类的所有窗口共享一个设备 dc} cs_parentdc = $80; {允许窗口的子窗口继承一些共同特性} cs_nokeycvt = $100; {} cs_noclose = $200; {禁用系统菜单的 close命令,同时窗口没有关闭按钮} cs_savebits = $800; {当窗口被覆盖时, 用位图缓存被覆盖区, 从而避免 wm_paint 消息, 一般用于菜单或对话框} cs_bytealignclient = $1000; {通过字节对齐, 增强客户区的绘制性能} cs_bytealignwindow = $2000; {通过字节对齐, 增强窗口的绘制性能} cs_globalclass = $4000; {全局窗口类, 一般用于 dll; 没有此选项, 窗口类和窗口建立函数中指定的实例句柄须相同} //关于窗口背景画刷: {系统预定义了一些画刷, 需要用 getstockobject 根据指定的常数检索;} {但 getstockobject 返回的句柄有可能是画刷、画笔、调色板或系统字体的句柄,} {所以还需要把 getstockobject 返回的句柄进行类型转换, 譬如: hbrush(getstockobject(常数))} //下面是 getstockobject 函数参数的可选值: white_brush = 0; ltgray_brush = 1; gray_brush = 2; dkgray_brush = 3; black_brush = 4; null_brush = 5; hollow_brush = null_brush; white_pen = 6; black_pen = 7; null_pen = 8; oem_fixed_font = 10; ansi_fixed_font = 11; ansi_var_font = 12; system_font = 13; device_default_font = 14; default_palette = 15; system_fixed_font = $10; default_gui_font = 17; dc_brush = 18; dc_pen = 19; stock_last = 19; {另外背景画刷还可以使用 windows 定义系统颜色常量, 譬如: hbrush(color_window + 1) } color_scrollbar = 0; color_background = 1; color_activecaption = 2; color_inactivecaption = 3; color_menu = 4; color_window = 5; color_windowframe = 6; color_menutext = 7; color_windowtext = 8; color_captiontext = 9; color_activeborder = 10; color_inactiveborder = 11; color_appworkspace = 12; color_highlight = 13; color_highlighttext = 14; color_btnface = 15; color_btnshadow = $10; color_graytext = 17; color_btntext = 18; color_inactivecaptiontext = 19; color_btnhighlight = 20; color_3ddkshadow = 21; color_3dlight = 22; color_infotext = 23; color_infobk = 24; color_hotlight = 26; color_gradientactivecaption = 27; color_gradientinactivecaption = 28; color_menuhilight = 29; color_menubar = 30; color_endcolors = color_menubar; color_desktop = color_background; color_3dface = color_btnface; color_3dshadow = color_btnshadow; color_3dhighlight = color_btnhighlight; color_3dhilight = color_btnhighlight; color_btnhilight = color_btnhighlight;
(二)、认识 createwindow 函数
createwindow( lpclassname: pchar; {窗口类的名字} lpwindowname: pchar; {窗口标题} dwstyle: dword; {窗口样式, 参加下表} x,y: integer; {位置; 默认的x,y可以指定为: integer(cw_usedefault)} nwidth,nheight: integer;{大小; 默认的宽度、高度可以指定为: integer(cw_usedefault)}} hwndparent: hwnd; {父窗口句柄} hmenu: hmenu; {主菜单句柄} hinstance: hinst; {模块实例句柄, 也就是当前 exe 的句柄} lpparam: pointer {附加参数, 创建多文档界面时才用到, 一般设为 nil} ): hwnd; {返回所创建的窗口的句柄} //dwstyle 窗口样式参数可选值: ws_overlapped = 0; {重叠式窗口, 应带标题栏和边框} ws_popup = dword($80000000); {弹出式窗口, 不能与 ws_child 一起使用} ws_child = $40000000; {子窗口, 不能与 ws_popup 一起使用} ws_minimize = $20000000; {最小化窗口} ws_visible = $10000000; {初始时可见} ws_disabled = $8000000; {禁止输入} ws_clipsiblings = $4000000; {裁剪子窗口, 也就是子窗口重绘不影响重叠的其他子窗口, 应与 ws_child 一起使用} ws_clipchildren = $2000000; {在父窗口中绘图时绕开子窗口区域, 创建父窗口是使用} ws_maximize = $1000000; {最大化窗口} ws_caption = $c00000; {有标题栏} ws_border = $800000; {有细线边框} ws_dlgframe = $400000; {对话框窗口} ws_vscroll = $200000; {有垂直滚动条} ws_hscroll = $100000; {有水平滚动条} ws_sysmenu = $80000; {带系统标题栏, 须同时指定 ws_caption} ws_thickframe = $40000; {带宽边框, 宽边框用于改变窗口大小} ws_group = $20000; {能用方向键转移焦点} ws_tabstop = $10000; {能用 tab 转移焦点} ws_minimizebox = $20000; {有最小化按钮} ws_maximizebox = $10000; {有最大化按钮} ws_tiled = ws_overlapped; ws_iconic = ws_minimize; ws_sizebox = ws_thickframe; ws_overlappedwindow = (ws_overlapped or ws_caption or ws_sysmenu or ws_thickframe or ws_minimizebox or ws_maximizebox); ws_tiledwindow = ws_overlappedwindow; ws_popupwindow = (ws_popup or ws_border or ws_sysmenu); ws_childwindow = (ws_child); //另外还有一些扩展样式: ws_ex_dlgmodalframe = 1; {指定双边界窗口; 藉此指定 ws_caption 创建标题栏} ws_ex_noparentnotify = 4; {在窗口创建或取消时不向父窗口发送 wm_parentnotify 消息} ws_ex_topmost = 8; {在所有非最顶层窗口的上面} ws_ex_acceptfiles = $10; {可接受拖放文件} ws_ex_transparent = $20; {透明样式, 在同属窗口已重画时该窗口才可重画} ws_ex_mdichild = $40; {创建一个 mdi 子窗口} ws_ex_toolwindow = $80; {工具窗口} ws_ex_windowedge = $100; {带立体的边框} ws_ex_clientedge = $200; {带阴影的边界} ws_ex_contexthelp = $400; {标题包含一个问号标志, 不能与 ws_maximizebox 和 ws_minimizebox 同时使用} ws_ex_right = $1000; {窗口具有右对齐属性} ws_ex_left = 0; {窗口具有左对齐属性, ws_ex_left 是缺省设置} ws_ex_rtlreading = $2000; {窗口文本从右到左} ws_ex_ltrreading = 0; {窗口文本从左到右, ws_ex_ltrreading 是缺省设置} ws_ex_leftscrollbar = $4000; {垂直滚动条在左边界, 只用于特殊语言环境} ws_ex_rightscrollbar = 0; {垂直滚动条在右边界, ws_ex_rightscrollbar 是缺省设置} ws_ex_controlparent = $10000; {允许用户使用 tab 键在窗口的子窗口间搜索} ws_ex_staticedge = $20000; {窗口不可用时创建一个三维边界} ws_ex_appwindow = $40000; {当窗口可见时, 将一个顶层窗口放置到任务条上} ws_ex_overlappedwindow = (ws_ex_windowedge or ws_ex_clientedge); {立体边框并带阴影} ws_ex_palettewindow = (ws_ex_windowedge or ws_ex_toolwindow or ws_ex_topmost); {立体边框、工具条窗口样式、在顶层} ws_ex_layered = $00080000; {分层或透明窗口, 该样式可使用混合特效} ws_ex_noinheritlayout = $00100000; {子窗口不继承父窗口的布局} ws_ex_layoutrtl = $00400000; {从右到左的布局} ws_ex_composited = $02000000; {用双缓冲从下到上绘制窗口的所有子孙} ws_ex_noactivate = $08000000; {处于顶层但不激活}
分析:
首先要用 createwindow 创建窗口, 才能用 showwindow 显示窗口; 因为 showwindow 需要 createwindow 返回的句柄.
在 createwindow 的参数中, 位置与大小与窗口标题无须多说;
父窗口与菜单, 暂时都不需要, 先可置为 0;
程序实例的句柄, delphi 已经为我们准备好了: hinstance; (参见原来的说明)
窗口样式在前面的例子中我们使用了: ws_overlappedwindow, 它代表几种特点的组合, 表示了常规窗口.
createwindow 还有一个重要参数(第一个参数 lpclassname): 窗口类的名字.
windows 要求我们要登记并注册一个窗口类以后, 才可以用 createwindow 建立窗口!
(三)认识消息机制函数
一个程序会有一个或多个线程, 系统有一个线程队列(就是个链表)管理所有这些线程, 并为每个线程建立一个消息队列.
当消息产生时(譬如点击了窗口), 系统会把该消息放到窗口所在的消息队列, 等待窗口处理.
窗口应该时刻待命, 准备从所在的线程队列中取出消息并处理!
从消息队列中取出消息, 一般用 getmessage 函数; 要随时取出消息, 需要用个循环, 譬如:
while(getmessage(msg, 0, 0, 0)) do begin ... end;
取出消息怎么处理? 用 dispatchmessage 函数将消息交给(前面提到的)窗口回调函数, 一般是这样:
while(getmessage(msg, 0, 0, 0)) do begin translatemessage(msg); {有些键盘消息需要用 translatemessage 函数并发出系统需要的更具体的键盘消息} dispatchmessage(msg); end;
如果 getmessage 函数不返回 0 ; 这个消息循环就是死循环, 什么时候 getmessage 可以返回 0 呢?
只有当 getmessage 收到 wm_quit 消息时才返回 0.
我们可在需要的时候, 在回调函数中通过 postquitmessage 函数向线程的消息队列中发送一条 wm_quit 消息, 以关闭线程.
postquitmessage 函数的参数是给应用程序的退出码, postquitmessage(0) 中的 0 就是我们指定的退出码, 它将作为 wm_quit 消息的 wparam 参数. 譬如:
function wndproc(wnd: hwnd; msg: uint; wparam: integer; lparam: integer): lresult; stdcall; begin result := 0; if msg = wm_destroy then postquitmessage(0) else result := defwindowproc(wnd, msg, wparam, lparam); end;
上一篇: 2022年1月中国手游发行商收入排名《王者荣耀》收入环比增长92%
下一篇: 字节的合并