WinMain函数的结构: GetModuleHandle -> RtlZeroMemory -> LoadCursor -> RegisterClassEx -> CreatWindowEx -> ShowWindow -> UpdateWindow -> CreateWindowEx创建窗口,ShowWindow则把窗口显示在屏幕上
GetMessage -> TranslateMessage -> DispatchMessage 这是和消息有关的循环,叫消息循环
在屏幕上显示一个窗口的过程: 1.得到应用程序的句柄(GetModuleHandle) 2.注册窗口类(RegisterClassEx)。在注册之前,要先填写RegisterClassEx的参数WNDCLASSEX结构。 3.建立窗口(CreateWindowEx)。 4.显示窗口(ShowWindows)。 5.刷新窗口客户区(UpdateWindow) 6.进入无限的消息获取和处理的循环。首先获取消息(GetMessage),如果消息到达,则将消息分派到回调函数(DispatchMessage),如果消息是WM_QUIT,则推出循环。
取模块句柄使用的API函数是GetModuleHandle,使用方法是: invoke GetModuleHandle,lpModuleName lpModuleName参数是一个指向含有模块名称字符串的指针,可以用这个函数取得程序地址空间中各个模块的句柄 例如: szUserDll db 'User32.dll',0 invoke GetModuleHandle,addr szUserDll .if eax mov hUserDllHandle,eax .endif
如果使用参数NULL调用GetModuleHandle,那么得到的是调用者本模块的句柄,我们可以这样使用 invoke GetModuleHandle,NULL mov hInstance,eax
注册窗口类的API函数是RegisterClassEx,最后的Ex是扩展的意思,一个窗口类定义了窗口的一些主要属性,这些属性是定义在一个WNDCLASSEX结构中,再把结构的地址当参数一次性传给RegisterClassEx WNDCLASSEX STRUCT cbSize dword ? :结构的字节数 Style dword ? :类的风格 LpfnWndProc dword ? :窗口过程的地址 cbClsExtra dword ? : cbWndExtra dword ? : HInstance dword ? :所属的实例句柄 HIcon dword ? :窗口图标 HCursor dword ? :窗口光标 HbrBackground dword ? :背景色 LpszMenuName dword ? :窗口菜单 LpszClassName dword ? :类名字符串的地址 HIconSm dword ? :小图标 WNDCLASSEX ENDS 实例: local @stWndClass:WNDCLASSEX ; ... invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass invoke LoadCursor,0,IDC_ARROW mov @stWndClass.hCursor,eax push hInstance pop @stWndClass.hInstance mov @stWndClass.cbSize,sizeof WNDCLASSEX mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW mov @stWndClass.lpfnWndProc,offset _ProcWinMain mov @stWndClass.hbrBackground,COLOR_WINDOW+1 mov @stWndClass.lpszClassName,offset szClassName invoke RegisterClassEx,addr @etWndClass 解释:RtlZeroMemory将变量@stWndClass全填为零 hIcon 图标句柄,指定在窗口标题栏左上角的图标,widows已经预定义了一些图标,程序可以使用资源文件中定义的图标,用LoadIcon函数获得 hCursor 光标句柄,指定了鼠标在窗口中的光标形状,widows也预定义了一些光标,可以用LoadCursor获得他们的句柄,IDC_ARROW是windows预定义的箭头光标,如果想使用自定义的光标,也可以使用资源文件 lpszMenuName 指定窗口上显示的默认菜单,他指向一个字符串,描述资源文件中的名称,如果资源文件中菜单是用数值定义的,那么这里使用菜单资源的数值,窗口菜单也可以在建立窗口函数CreateWindowEx的参数中指定 hInstance 指定要注册的窗口类属于哪个模块,用GetModuleHandle获得 cbSize 指定WNDCLASSEX结构的长度,用sizeof伪操作来获取 style 窗口风格,CS_HREDRAW,CS_VREDRAW表示窗口的宽度或高度改变时是否重画窗口。比较重要的是CS_DBLCLKS,指定了他,windows才会把窗口中快速两次单击鼠标的行为翻译成双击消息WM_LBUTIONDBLCLK发给窗口过程。 hbrBackground 窗口客户区的背景色。hbr表示他是一个刷子(Brush)的句柄,windows预定义了一些刷子,如BLACK_BRUSH,WHITE_BRUSH等,可以用下列语句来得到他们的句柄: invoke GetObjectStocx,WHITE_BRUSH 在这里也可以使用颜色值,windows已经预定义了一些颜色值,COLOR_BACKGROUND,COLOR_HIGHLIGHT,COLOR_MENU,COLOR_WINDOW等使用这些颜色值时,windows规定必须在颜色值上加1 mov @stWndClass.hbrBackground,COLOR_WINDOW+1 lpszClassName 指定程序员要建立的类命名,方便以后用这个名称来引用他 cbWndExtra,cbClsExtra分别是在windows内部保存的窗口结构和类结构中给程序员预留的空间大小,用来存放自定义的数据,他们的单位是字节,不使用的话,这两个就是0 lpfnWndProc 最重要的参数,他指定了基于这个类建立的窗口的窗口过程地址,通过这个参数,windows就知道了在DispatchMessage函数中把窗口消息发到哪里去,一个窗口过程可以为多个窗口服务,只要这些窗口是基于同一个窗口类建立的, ****强烈建议用or,不要用+
建立窗口:CreateWindowEX invoke CreateWindowEx,dwExStyle,lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu, hInstance,lpParam lpClassName 建立窗口使用的类名字符串指针,在FirstWindow中指向MyClass字符串表示使用MyClass类建立的窗口,这里可以是windows预定义的类名 lpWindowName 指向表示窗口名称的字符串,该名称会显示在标题栏上,如果该参数空白,则标题栏上什么都没有 hMenu 窗口上要出现的菜单的句柄,在注册窗口类的时候也定义了一个菜单,那是窗口默认的菜单,当建立的窗口是子窗口时,(dwStyle指向了WS_CHILD),这个参数是另一个含义,这时hMenu参数指定的是子窗口的ID号 lpParam 这是一个指针,指向一个欲传给窗口的参数,这个参数在WM_CREATE消息可以被获取,一般情况不用 hInstance 模块句柄,和注册窗口类时一样,指定了窗口所属的程序模块 hWndParent 窗口所属的父窗口 x,y 指定窗口左上角位置,默认时可以指定为CW_USEDEFAULT nWidth,nHeight 窗口的高度,宽度也可指定为CW_USEDEFAULT dwStyle,dwExStyle决定了窗口的外形和行为 dwStyle的定义,它们是以WS(WINDOW STYLE的缩写)为开头的预定义值 WS_OVERLAPPED 普通的重叠式窗口 WS_POPUP 弹出式窗口 WS_CHILD 子窗口 WS_MINIMIZE 初始状态是最小化的 WS_VISIBLE 初始状态是可见的 WS_DISABLED 初始状态是被禁止的 WS_MAXIMIZE 初始状态是最大化的 WS_BORDER 单线条边框 WS_DLGFRAME 对话框类型的边框 WS_VSCROLL 垂直滚动条 WS_HSCROLL 水平滚动条 WS_SYSMENU 带系统菜单 WS_THICKPRAME 可以拖动调整大小的边框 WS_MINIMIZEBOX 有最小化按钮 WS_MAXIMIZEBOX 有最大化按钮
WS_CHILDWINDOW WS_CHILD WS_TILED WS_OVERLAPPED WS_OVERLAPPEDWINDOW WS_OVERLAPPED OR WS_CAPTION OR WS_SYSMENU OR WS_THICKFRAME OR WS_MINIMIZEBOX OR WS_MAXIMIZEBOX
dwExStyle是Win32扩展的,是一些以WS_EX_开头的预定义值,主要是一些特殊风格 WS_EX_TOPMOST 总在顶层的窗口 WS_EX_ACCEPTFILES 允许窗口进行鼠标拖放操作 WS_EX_TOOLWINDOW 工具边框 WS_EX_WINDOWEDGE 立体感的边框 WS_EX_CLIENTEDGE 客户区立体边框 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_PALETTEWINDOW可以很方便的构成浮在其他窗口前面的工具栏
ShowWindow函数主要控制窗口的显示状态(显示或隐藏),大小控制(最大化,最小化或原始大小)和是否激活,用窗口句柄做第一个参数,第二个参数是显示的方式 SW_HIDE 隐藏窗口,大小不变,激活状态不变 SW_MAXIMIZE 最大化窗口,显示状态不变,激活状态不变 SW_MINIMIZE 最小化窗口。。。。。。。。。。。。。。。 SW_RESTORB 从最大化或最小化恢复正常大小。。。。。。 SW_SHOW 显示并激活窗口,大小状态不变 SW_SHOWMAXIMIZED 显示并激活窗口,以最大化显示 SW_SHOWMINIMIZED 显示并激活窗口,以最小化显示 SW_SHOWMINNOACTIVE 显示窗口并最小化,激活状态不变 SW_SHOWMA 显示窗口,大小状态不变,激活状态不变 SW_SHOWNOACTIVATE 显示并从最大化或最小化恢复正常大小,激活状态不变 SW_SHOWNORMAL 显示并激活窗口,恢复正常大小(初始化时用这个参数)
用UpdateWindow绘制客户区,它实际上就是向窗口发送了一条WM_PAINT消息,到此,一个顶层窗口就建立显示出来了
CreateWindowEx可以也用来建立子窗口,Windows中有很多预定义的子窗口类,如按钮和文本框的类名分别是 "Button"和"Edit"。要建立一个按钮,只要把lpClassName指向"Button"字符串就可以了
消息循环 其一般形式 .while TURE invoke GetMessage,addr @stMsg,NULL,0,0 .break .if eax==0 invoke TranslateMessage,addr @stMsg invoke DispatchMessage,addr @stMsg .endw
消息循环中的MSG结构 MSG STRUCT hwnd dword ? message dword ? wParam dword ? lParam dword ? time dword ? pt dword ? MSG ENDS
hwnd 消息要发向的窗口句柄 message 消息标识符 wParam 消息参数之一 lParam 消息参数之二 time 消息放入消息队列的时间 pt 这是一个POINT数据结构,表示消息放入消息队列时的鼠标坐标
GetMessage就是从消息队列中取出这样一条消息: invoke GetMessage,lpMsg,hwnd,wMsgFilterMin,wMsgFilterMax 函数的lpMsg指向一个MSG结构,函数会在这里返回取得的消息,hWnd参数指定要获取哪个窗口的消息,例子指定为NULL,表示获取的是所有本程序所属窗口的消息,后两个参数为零表示获取所有编号的消息 如果获取的是WM_QUIT消息,那么eax中的返回值是0,否则返回非零值,所以用.break .if eax==0来检查返回值 TranslateMessage将MSG结构传给windows进行一些键盘消息的转换,有键盘按下和放开时,windows产生WM_KEYDOWN和WM_KEYUP或WM_SYSKEYDOWN和WM_SYSKEYUP,这些消息参数包含的时按键的扫描码,,因为不方便,所以TranslateMessage遇到键盘消息则将扫描码转换成ASCII码,并在消息队列中插入WM_CHAR或WM_SYSCHAR的消息,如此一来,只要处理WM_CHAR消息就好了,遇到别的则不处理
窗口过程 WindowProc proc hwnd,uMsg,wParam,lParam
wParam,lParam参数是消息所附带的参数,随消息的不同而不同,如:WM_MOUSEMOVE消息中,wParam是标志,lParam是鼠标的位置,在WM_GETTEXT消息中,wParam是要获取的字符数,lParam是缓冲区的地址,对于WM_COPY来说,它不需要额外的信息
消息: WM_CREATE 放置窗口初始化代码,如建立各种子窗口(状态栏和工具栏等) WM_SIZE 放置位置安排的代码,因为建立的子窗口可能需要随窗口大小的改变而移动位置 WM_PAINT 如果需要自己绘制客户区,则在这里安排代码 WM_CLOSE 向用户确认是否退出,如推出则摧毁窗口并发送WM_QUIT消息 WM_DESTROY 窗口摧毁,在这里放置释放资源等扫尾代码
DefWindowProc对一些消息的默认处理方式 WM_PAINT 擦除背景 WM_ERASEBKGND WM_CLOSE 调用DestroyWindow摧毁窗口 WM_NCLBUTTONUP WM_NCPAINT 非客户区绘制消息,将绘制边框和客户区
int wsprintf{ LPTSTR lpOut,//输出缓冲区的地址 LPCTSTR lpFmt//格式化串地址 ... //变量列表 }
使用右键弹出菜单: 由TrackPopupMenu,hMenu,uFlags,x,y,nReserved,hWnd,lpRect实现。 执行后,在参数指定的x,y位置弹出一个属于hWnd窗口(也就是WM_COMMAND消息发到这个窗口)的菜单,菜单句柄是hWnd,坐标是以整个屏幕左上角为基准的,uFlag参数指定一些和位置有关的选项,可以是:PM_CENTERALIGN,TPM_LEFTALIGN,TPM_RIGNTALIGN三者之一,表示(x,y)代表弹出菜单位置中间,左上角,还是右上角,一般习惯用TPM_LEFTALIGN,这样,菜单会在鼠标点击处的右边弹出。uFlag同时还可以指定用鼠标右键还是左键选定菜单项,定义值可以是TPM_LEFTBUTTON,TPM_RIGHTBUTTON,如果是TPM_RIGHTBUTTON,在菜单项按鼠标左键是没有反应的 lpRect指向一个RECT结构,用来指定一个区域,当菜单弹出后,在这个区域外单击,菜单才会消息,用NULL就可以了 调用此函数前,要调用invoke GetCursorPos,lpPoint lpPoint是一个POINT数据结构,含两个子项,x,y invoke GetSubMenu,hMenu,nPos .if eax mov hSubMenu,eax .endif
local stPos,POINT invoke GetCursorPos,addr stPos invoke TrackPopupMenu,hSubMenu,TPM_LEFTLIGN,stPos.x,stPos.y,NULL,hWnd,NULL 对菜单项状态的检测可以用invoke GetMenuState,hMenu,uID,uFlags hMenu,菜单句柄,uID,用来检测的菜单项,当uFlag是MF_BYCOMMAND的时候,uID用菜单ID指定,当uFlag是MF_BYPOSTTION时,uID的位置是索引,返回值为-1时表示失败,否则会是MF_CHECHED,MF_DISABLED,MF_GRAYED,MF_HILITE,MF_MENUBARBREAK,MF_MENUBREAK,MF_SEPARATOR,的组合值,它们分别表示菜单项的状态是选中,禁用,灰化,高亮显示已经3种分隔线,可以用test指令测试相应的数据位来分辨菜单项处于哪个状态 invoke GetMenuState,hMenu,IDM_XXX,MF_BYCOMMAND .if eax & MF_CHECKED;表示此菜单项是选中状态 .endif 设置菜单项可以用 invoke EnableMenuItem,hMenu,uIDEnableItem,uEnable invoke CheckMenuItem,hMenu,uIDCheckItem,uCheck invoke CheckMenuRadioItem,hMenu,idFirst,idLast,idCheck,uFlags EnableMenuItem可以将菜单项在禁用,可用,灰化之间切换 uEnable可以取值:MF_DISABLED,MF_ENABLED,MF_GRAYED CheckMenuItem可以在非互斥的选定状态和非选定状态之间切换 nCheck的取值:MF_CHECKED,MF_UNCHECKED CheckMenuRadioItem可以在互斥的选定状态和非选定状态之间切换,idFirst,idLast指定这个互斥范围,
要在标题栏设置图标可以向窗口发送WM_SETICON消息 invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,hIcon,wParam可以是ICON_BIG,ICON_SMALL