栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

Win32汇编系列七、窗口程序原理及实现

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Win32汇编系列七、窗口程序原理及实现

前言

学习c语言的人是不是有段这样的经历,天天面对黑框框敲来敲去,有木有?不知道是否想过用c写一个窗口程序?我想应该也很希望吧,目前我见过的书中很少有介绍窗口程序设计,如果想写的话,还需要去网上、b站搜索,或者看Window程序设计之类的书,使用c写窗口程序其实挺麻烦的,需要了解Windows消息机制、GDI或者GDI+、还有各种控件,如Button,另外自带的控件都太难看,需要使用GDI或GDI+自己绘制,这样工作量又大了很多,所以现在很少有软件用这种最"底层"的方式实现了,都是各种框架,或者使用C#、WPF,我不曾在c中大量写过,但是在VB中大量写过。

这里你可能有觉得矛盾,VB中需要你自己创建窗口、创建控件?当然不用,全是为了学习Windows机制,在VB中,可以不用新建窗口,通过sub main方式启动,在方法中调用大量系统API,完成创建窗口,窗口控件,以及控件的事件响应,这样下来也上百行了,工作量还是很大的。

另外也可以使用别的语言实现,如C#,通过调用系统API,创建窗口等,Java也可以调用Windows API,但是使用相关的API创建窗口,我并没试过(不是说JFrame)。有时间试试。

(其实不太喜欢做Java,我喜欢研究Windows,奈何当初选错了路,导致现在不能回头,如果在回头后会浪费大量时间重新学习,小伙伴做选择要慎重哦!!!也希望有共同爱好的小伙伴一起交流)

实现过程

今天的主角是汇编实现,代码量也不少,原理是invoke调用大量API完成,创建窗口不管用什么语言实现,原理都一样,先注册一个窗口类、窗口调用CreateWindow创建窗口,然后在窗口过程函数中不断处理消息即可(前提这个语言能调用Win Api)。

下面介绍三个必要的函数。

RegisterClassEx

如果要创建一个创建,首先必须调用RegisterClassEx注册一个窗口类,他的参数只有一个,WNDCLASSEX结构体,但是这个结构体有点复杂,成员非常多。

typedef struct WNDCLASSEXA {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;
  LPCSTR    lpszClassName;
  HICON     hIconSm;
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;

这里不全部介绍了,但是有一个很重要的需要说一下,就是lpfnWndProc成员,指向一个函数过程,这就牵扯出Windows的消息机制。

窗口过程函数

这个不太好理解,但是写过一次Windows程序的人就明白了,当我们操作一个窗口时,不论是单机、双击、右击、鼠标移动、还是任何你发出的事件,它就会接收到系统发过来的消息,消息中就会标识这个事件是什么,然后我们需要根据消息标识做出不同响应,而就需要我们写一个窗口过程函数,告诉系统,发生消息时,你给我回调到这个地方,如果在这个函数中不处理消息,就可以调用DefWindowProc函数来处理,这是系统的默认消息处理函数。

这个函数过程的参数也是固定的,在官方文档中是这样定义的,这里面又会牵扯出句柄的概念,简单说一下,句柄就是标识一个窗口的一个整数,就像身份证号一样。参数uMsg就是消息标识,也是个整数,系统把他们都定义好了,常量名都是WM_开头得,如WM_MOVE,在窗口移动窗口时系统会通知我们。

LRESULT CALLBACK WindowProc(
  _In_ HWND   hwnd,
  _In_ UINT   uMsg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);
CreateWindowEx

创建一个窗口,返回窗口句柄,参数都比较简单,这里最有意思的是窗口的样式参数,有非常多的样式可选择,平常我们是见不到这些样式的。

HWND CreateWindowExA(
  DWORD     dwExStyle,
  LPCSTR    lpClassName,
  LPCSTR    lpWindowName,
  DWORD     dwStyle,
  int       X,
  int       Y,
  int       nWidth,
  int       nHeight,
  HWND      hWndParent,
  HMENU     hMenu,
  HINSTANCE hInstance,
  LPVOID    lpParam
);

CreateWindow完成之后,就需要不断循环从消息队列获取消息,进行分发。

好,上代码!!!

.386 
.model flat,stdcall 
option casemap:none 
include c:masm32includewindows.inc 
include c:masm32includeuser32.inc 
includelib c:masm32libuser32.lib           
include c:masm32includekernel32.inc 
includelib c:masm32libkernel32.lib 

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "汇编实现窗口",0       
.DATA?              
hInstance HINSTANCE ?
.CODE             
start: 
invoke GetModuleHandle, NULL   
                        
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 
    LOCAL hwnd:HWND 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, addr wc   
    invoke CreateWindowEx,NULL, 
                ADDR ClassName, 
                ADDR AppName, 
                WS_OVERLAPPEDWINDOW, 
                0, 
                0, 
                300, 
                300, 
                NULL, 
                NULL, 
                hInst, 
                NULL 
    mov   hwnd,eax 
    invoke ShowWindow, hwnd,SW_SHOWDEFAULT   
    invoke UpdateWindow, hwnd               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
       invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
    ret 
WndProc endp 

end start 

运行后,得出个窗口,使用了80多行。

判断鼠标单击

接下来,我们做个简单的修改,当鼠标单击窗口的时候,在窗口打印"Hello GUI",但是这里面又涉及到HDC的知识,小伙伴自行了解哦。

.386 
.model flat,stdcall 
option casemap:none 
include c:masm32includewindows.inc 
include c:masm32includeuser32.inc 
includelib c:masm32libuser32.lib           
include c:masm32includekernel32.inc 
includelib c:masm32libkernel32.lib 

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "汇编实现窗口",0       
HelloGUI db "HelloGUI",0     
.DATA?              
hInstance HINSTANCE ?
hWindowHdc HDC ?
.CODE             
start: 

invoke GetModuleHandle, NULL   
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 
    LOCAL hwnd:HWND 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, ADDR wc   
    invoke CreateWindowEx,NULL, 
                ADDR ClassName, 
                ADDR AppName, 
                WS_OVERLAPPEDWINDOW, 
                0, 
                0, 
                300, 
                300, 
                NULL, 
                NULL, 
                hInst, 
                NULL 
    mov   hwnd,eax 
    invoke GetDC,eax
    mov  hWindowHdc,eax
    invoke ShowWindow, hwnd,SW_SHOWDEFAULT   
    invoke UpdateWindow, hwnd               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
		LOCAL strRect:RECT
		.IF uMsg == WM_LBUTTONDOWN
			mov strRect.right,100
			mov strRect.bottom,100
			mov strRect.left,0
			mov strRect.top,0
			invoke DrawText,hWindowHdc, ADDR HelloGUI,8,ADDR  strRect,DT_VCENTER
		.ELSE
			invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
			ret
		.ENDIF
    ret 
WndProc endp 

end start 

效果:

创建按钮

创建控件其实也是通过CreateWindowEx来完成,并不需要我们注册类名,使用系统定义好的类名即可,这部分可以查看文档:https://docs.microsoft.com/en-us/windows/win32/winmsg/about-window-classes。

最终还要给按钮设置WS_TABSTOP OR WS_VISIBLE OR WS_CHILD OR BS_DEFPUSHBUTTON这几个样式,其中WS_CHILD样式代表作为一个子窗口,否则会出现意料之外的。

当按钮单击后,系统会回调WM_COMMAND消息, wParam 低两字节是命令ID,这就需要在CreateWindowEx时候指明。

.386 
.model flat,stdcall 
option casemap:none 
include c:masm32includewindows.inc 
include c:masm32includeuser32.inc 
includelib c:masm32libuser32.lib           
include c:masm32includekernel32.inc 
includelib c:masm32libkernel32.lib 

WinMain proto :DWORD

.DATA                   
ClassName db "MyWindowClass",0       
AppName db "汇编实现窗口",0    
ButtonClassName db "Button",0   
ButtonTitle db "按钮",0
HelloGUI db "HelloGUI",0

Message db "单机了按钮",0
MessageTitle db "提示",0
.DATA?              
hInstance HINSTANCE ?
hWindowHdc HDC ?
hButton HWND ?
hWindow HWND ?
.CONST 
ButtonID equ 1 
.CODE             


start: 

invoke GetModuleHandle, NULL   
mov hInstance,eax 
invoke WinMain, hInstance
invoke ExitProcess, eax       
WinMain proc hInst:HINSTANCE
    LOCAL wc:WNDCLASSEX              
    LOCAL msg:MSG 

    mov   wc.cbSize,SIZEOF WNDCLASSEX   
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, ADDR wc   
    invoke CreateWindowEx,NULL, 
                ADDR ClassName, 
                ADDR AppName, 
                WS_OVERLAPPEDWINDOW, 
                0, 
                0, 
                300, 
                300, 
                NULL, 
                NULL, 
                hInst, 
                NULL 
    mov   hWindow,eax

    invoke CreateWindowEx,NULL,ADDR ButtonClassName,ADDR ButtonTitle, WS_TABSTOP OR  WS_VISIBLE OR WS_CHILD OR BS_DEFPUSHBUTTON  ,
    0,0,100,100,hWindow,ButtonID,NULL,NULL
    mov hButton,eax

    invoke GetDC,eax
    mov  hWindowHdc,eax
    invoke ShowWindow, hWindow,SW_SHOWDEFAULT   
    invoke UpdateWindow, hWindow               

    .WHILE TRUE                                             
		invoke GetMessage, ADDR msg,NULL,0,0 
        .BREAK .IF (!eax) 
        invoke TranslateMessage, ADDR msg 
        invoke DispatchMessage, ADDR msg 
   .ENDW 
    mov     eax,msg.wParam                      
    ret 
WinMain endp 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
		LOCAL strRect:RECT
		.IF uMsg == WM_LBUTTONDOWN
			mov strRect.right,100
			mov strRect.bottom,100
			mov strRect.left,0
			mov strRect.top,0
			invoke DrawText,hWindowHdc, ADDR HelloGUI,8,ADDR  strRect,DT_VCENTER
		.ELSEIF uMsg ==WM_COMMAND
				mov eax,wParam 
				.IF ax == ButtonID 
					invoke MessageBox,hWindow,ADDR Message,ADDR MessageTitle,MB_OK
				.ENDIF
				
		.ELSE
			invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
			ret
		.ENDIF
    ret 
WndProc endp 

end start 



怎么样,学废了吗?

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/907981.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号