Win32汇编语言程序(2)

四、一个简单的Win32汇编语言程序

读者可能一听到“汇编语言”四个字就觉得十分头疼!汇编语言给人的第一印象就是一大堆难以看懂又不直观的指令,而且不结构化,大量的标号、无条件跳转指令(JMP)和条件跳转指令让你难以看懂程序;过程(或者函数)的调用参数传递又不直观,要么直接使用寄存器传递参数,不符合结构化程序设计原则;要么使用堆栈传递参数,又不能有效地检验参数类型……想必Win32汇编语言更麻烦吧!还好,MASM 6.0以上版本的汇编器提供了很多结构化汇编语言伪指令,可以方便地实现汇编语言结构化程序设计,当你看完本教程以后,你可能会感觉到:Win32汇编语言并不比C语言麻烦多少。(如果读者看不懂本教程中的汇编语言源程序也不要紧,可以对照MASM 6.11的帮助看)和C语言Win32编程需要WINDOWS.H头文件和其他Win32 API定义头文件定义常量、数据结构和API一样,Win32汇编语言也需要包含文件(INC文件)定义常量、数据结构和API。不过笔者找了很长时间也没有找到一个完整的可用于Win32汇编语言的WINDOWS.INC文件或者WIN32.INC文件(倒是找到了用于Win16汇编语言的WINDOWS.INC文件),Turbo MASM 5.0中提供的WIN32.INC文件也不完整,只能用于自带的WAP32例子程序,而且与MASM 6.11不太兼容(听说NASM中有完整的WIN32.INC文件,可惜没有找到,也不知道与MASM 6.11是否兼容)。笔者只好自己定义常量、数据结构和API(根据WINDOWS.H头文件和其他Win32 API定义头文件定义),不过这倒
带来了不少好处——可以更好地了解Win32汇编语言的编程方法和原理。笔者编写了一个简单的Win32汇编语言程序,该程序的功能很简单:在屏幕上显示一个消息框。本程序只调用了两个API函数:MessageBox函数和ExitProcess函数,程序如下:
包含文件(MSGBOX.INC):

UINT TYPEDEF DWORD LPSTR TYPEDEF PTR BYTE LPCSTR TYPEDEF LPSTR PVOID TYPEDEF PTR HANDLE TYPEDEF PVOID HWND TYPEDEF HANDLE MB_ICONINFORMATION = 00000040h MB_OK = 00000000h MessageBoxA PROTO stdcall, :HWND,:LPCSTR,:LPCSTR,:UINT ExitProcess PROTO stdcall, :UINT 源程序(MSGBOX.ASM): .386p .MODEL flat,stdcall INCLUDE MSGBOX.INC .STACK 4096 .DATA WindowTitle BYTE 'MsgBox',0 Message1 BYTE 'This is a simple MessageBox Win32 application.',0 .CODE _start: INVOKE MessageBoxA,0,ADDR Message1,ADDR WindowTitle, MB_ICONINFORMATION or MB_OK INVOKE ExitProcess,0 PUBLIC _start END 汇编连接本程序的命令如下: ml /c /coff /Cp msgbox.asm link /subsystem:windows /entry:_start msgbox.obj kernel32.lib user32.lib

汇编命令中的/c选项表示只汇编,不自动连接;/coff选项表示生成COFF格式的OBJ文件(如果使用Borland的连接器不能使用/coff参数);/Cp选项表示标识符区分大小写。连接命令中/subsystem:windows选项表示连接器生成普通Windows可执行文件;/entry:_start选项表示程序入口点是_start标识符。连接时连接KERNEL32.LIB和USER32.LIB引入库。运行汇编连接后生成的MSGBOX.EXE文件,屏幕上将显示出一个消息框,消息框的标题是“MsgBox”,消息框中的字符串是“This is a simple MessageBox Win32 application.”。Win32汇编语言源程序应该由.386p伪指令和.MODEL flat,stdcall伪指令开始,指示汇编器汇编386保护模式指令,并使用平坦内存模式(Win32内存模式)和stdcall函数调用方式(Win32标准函数调用方式)。PROTO伪指令定义函数原型(与C语言中函数原型的定义相似),可以定义函数名、调用方式和参数,INVOKE伪指令调用由PROTO伪指令定义的函数,可以方便地传递参数和检查参数类型。MSGBOX.INC文件中使用PROTO伪指令定义API函数,MSGBOX.ASM文件中使用INVOKE伪指令调用API函数,可见MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程(本程序一条汇编语言指令也没有用到)。本程序调用了MessageBox函数显示消息框以后,调用了ExitProcess函数终止程序的执行,ExitProcess函数的作用是终止当前进程。

五、显示一个窗口的Win32汇编语言程序

学习过Win32SDK编程的读者编写的第一个应用程序可能就是显示一个窗口的C语言程序,笔者也编写了这样一个C语言程序,该程序文件名为SIMPLE.C,程序如下:

#include static char szWindowClass[]="SIMPLE"; LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { WNDCLASSEXA wcex; HWND hWnd; MSG msg; if(!hPrevInstance) { wcex.cbSize=sizeof(WNDCLASSEXA); wcex.style=CS_HREDRAW|CS_VREDRAW; wcex.cbClsExtra=0; wcex.cbWndExtra=0; wcex.lpfnWndProc=WndProc; wcex.hInstance=hInstance; wcex.hIcon=LoadIconA(hInstance,IDI_APPLICATION); wcex.hCursor=LoadCursorA(0,IDC_ARROW); wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName=NULL; wcex.lpszClassName=szWindowClass; wcex.hIconSm=LoadIconA(hInstance,IDI_APPLICATION); if(!RegisterClassExA(&wcex)) return FALSE; } hWnd=CreateWindowExA(0,szWindowClass,"SIMPLE", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, 0,0,hInstance,NULL); if(!hWnd) return FALSE; ShowWindow(hWnd,nShowCmd); UpdateWindow(hWnd); while(GetMessageA(&msg,0,0,0)) { TranslateMessage(&msg); DispatchMessageA(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; switch(message) { case WM_PAINT: hDC=BeginPaint(hWnd,&ps); EndPaint(hWnd,&ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProcA(hWnd,message,wParam,lParam); } return -1; } 本程序与一般的显示一个窗口的C语言程序基本相同,只是只使用ANSI字符集。现在笔者用Win32汇编语言程序实现本程序的功能,程序如下: 包含文件(SIMPLE.INC): UINT TYPEDEF DWORD LONG TYPEDEF DWORD LPSTR TYPEDEF PTR BYTE LPCSTR TYPEDEF LPSTR PVOID TYPEDEF PTR LPVOID TYPEDEF PVOID HANDLE TYPEDEF PVOID HINSTANCE TYPEDEF HANDLE HWND TYPEDEF HANDLE HMENU TYPEDEF HANDLE HDC TYPEDEF HANDLE HGDIOBJ TYPEDEF HANDLE HICON TYPEDEF HANDLE HCURSOR TYPEDEF HANDLE HBRUSH TYPEDEF HANDLE tagWNDCLASSEXA STRUCT cbSize UINT ? style UINT ? lpfnWndProc DWORD ? cbClsExtra DWORD ? cbWndExtra DWORD ? hInstance DWORD ? hIcon DWORD ? hCursor DWORD ? hbrBackground DWORD ? lpszMenuName DWORD ? lpszClassName DWORD ? hIconSm DWORD ? tagWNDCLASSEXA ENDS WNDCLASSEXA TYPEDEF tagWNDCLASSEXA tagPOINT STRUCT x LONG ? y LONG ? tagPOINT ENDS POINT TYPEDEF tagPOINT tagMSG STRUCT message UINT ? wParam DWORD ? lParam DWORD ? time DWORD ? pt POINT <> tagMSG ENDS MSG TYPEDEF tagMSG LPMSG TYPEDEF PTR MSG tagRECT STRUCT left LONG ? top LONG ? right LONG ? bottom LONG ? tagRECT ENDS RECT TYPEDEF tagRECT tagPAINTSTRUCT STRUCT hdc DWORD ? fErase DWORD ? rcPaint RECT <> fRestore DWORD ? fIncUpdate DWORD ? rgbReserved BYTE 32 DUP(?) tagPAINTSTRUCT ENDS PAINTSTRUCT TYPEDEF tagPAINTSTRUCT LPPAINTSTRUCT TYPEDEF PTR PAINTSTRUCT NULL = 0 TRUE = 0ffffffffh FALSE = 0 SW_SHOWDEFAULT = 10 CS_HREDRAW = 0002h CS_VREDRAW = 0001h IDI_APPLICATION = 32512 IDC_ARROW = 32512 COLOR_WINDOW = 5 WS_OVERLAPPEDWINDOW = 00cf0000h CW_USEDEFAULT = 80000000h WM_PAINT = 000fh WM_DESTROY = 0002h GetModuleHandleA PROTO stdcall, :LPCSTR GetCommandLineA PROTO stdcall ExitProcess PROTO stdcall, :UINT LoadIconA PROTO stdcall, :HINSTANCE,:LPCSTR LoadCursorA PROTO stdcall, :HINSTANCE,:LPCSTR RegisterClassExA PROTO stdcall, :PTR WNDCLASSEXA CreateWindowExA PROTO stdcall, :DWORD,:LPCSTR,:LPCSTR,:DWORD,: DWORD,:DWORD,:DWORD,:DWORD,:HWND,:HMENU,:HINSTANCE,:LPVOID ShowWindow PROTO stdcall, :HWND,:DWORD UpdateWindow PROTO stdcall, :HWND GetMessageA PROTO stdcall, :LPMSG,:HWND,:UINT,:UINT TranslateMessage PROTO stdcall, :PTR MSG DispatchMessageA PROTO stdcall, :PTR MSG BeginPaint PROTO stdcall, :HWND,:LPPAINTSTRUCT EndPaint PROTO stdcall, :HWND,:PTR PAINTSTRUCT PostQuitMessage PROTO stdcall, :DWORD DefWindowProcA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD 源程序(SIMPLE.ASM): .386p .MODEL flat,stdcall INCLUDE SIMPLE.INC WinMain PROTO stdcall, :HINSTANCE,:HINSTANCE,:LPSTR,: DWORD .STACK 4096 .DATA WindowClass BYTE 'SIMPLE',0 WindowTitle BYTE 'SIMPLE',0 hInst1 HINSTANCE 0 lpCmdLine1 LPSTR 0 .CODE _start: INVOKE GetModuleHandleA,NULL mov hInst1,eax INVOKE GetCommandLineA mov lpCmdLine1,eax INVOKE WinMain,hInst1,NULL,lpCmdLine1,SW_SHOWDEFAULT INVOKE ExitProcess,eax WinMain PROC hInst:HINSTANCE,hPrevInst:HINSTANCE,lpCmdLine:LPSTR, nShowCmd:DWORD LOCAL wcex:WNDCLASSEXA LOCAL hWnd:HWND LOCAL msg:MSG .IF !hPrevInst mov wcex.cbSize,SIZEOF WNDCLASSEXA mov wcex.style,CS_HREDRAW or CS_VREDRAW mov wcex.cbClsExtra,0 mov wcex.cbWndExtra,0 mov wcex.lpfnWndProc,OFFSET WndProc mov eax,hInst mov wcex.hInstance,eax INVOKE LoadIconA,hInst,IDI_APPLICATION mov wcex.hIcon,eax INVOKE LoadCursorA,0,IDC_ARROW mov wcex.hCursor,eax mov wcex.hbrBackground,COLOR_WINDOW+1 mov wcex.lpszMenuName,NULL mov wcex.lpszClassName,OFFSET WindowClass INVOKE LoadIconA,hInst,IDI_APPLICATION mov wcex.hIconSm,eax INVOKE RegisterClassExA,ADDR wcex .IF !eax mov eax,FALSE ret .ENDIF .ENDIF INVOKE CreateWindowExA,0,ADDR WindowClass,ADDR WindowTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,0,0,hInst,NULL mov hWnd,eax .IF !eax mov eax,FALSE ret .ENDIF INVOKE ShowWindow,hWnd,nShowCmd INVOKE UpdateWindow,hWnd .WHILE TRUE INVOKE GetMessageA,ADDR msg,0,0,0 .BREAK .IF !eax INVOKE TranslateMessage,ADDR msg INVOKE DispatchMessageA,ADDR msg .ENDW mov eax,msg.wParam ret WinMain ENDP WndProc PROC hWnd:HWND,message:UINT,wParam:DWORD,lParam:DWORD LOCAL hDC:HDC LOCAL ps:PAINTSTRUCT .IF message==WM_PAINT INVOKE BeginPaint,hWnd,ADDR ps mov hDC,eax INVOKE EndPaint,hWnd,ADDR ps mov eax,0 ret .ELSEIF message==WM_DESTROY INVOKE PostQuitMessage,0 mov eax,0 ret .ELSE INVOKE DefWindowProcA,hWnd,message,wParam,lParam ret .ENDIF mov eax,0ffffffffh ret WndProc ENDP PUBLIC _start END
汇编连接本程序的命令如下:

ml /c /coff /Cp simple.asm
link /subsystem:windows /entry:_start simple.obj kernel32.lib user32.lib
gdi32.lib

运行汇编连接后生成的SIMPLE.EXE文件,屏幕上将显示出一个标准的窗口,窗口的标题是“SIMPLE”。学习过Win32SDK编程的读者都知道,Win32应用程序的入口点是WinMain函数,实际上WinMain函数是被C语言的初始化和结束代码调用的,Win32应用程序的真正入口点和DOS应用程序没有什么区别,都是在文件头中指定的应用程序起始点。Win32汇编语言没有C语言的初始化和结束代码,必须自己编写初始化和结束代码调用
WinMain函数(过程),WinMain函数的原型是:
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd); WinMain函数有4个参数,分别是: hInstance——应用程序当前实例的句柄,可以通过调用GetModuleHandle函数获取. hPrevInstance——应用程序前一个实例的句柄,Win32中当前地址空间中不会有应用程序的其他实例在运行,该参数通常设置为NULL(提供该参数只是便于移植Win16应用程序源程序)。 lpCmdLine——命令行参数,可以通过调用GetCommandLine函数获取。nShowCmd——主窗口的显示状态,可以设置成SW_SHOWDEFAULT(缺省状态)。本程序的初始化和结束代码如下: INVOKE GetModuleHandleA,NULL mov hInst1,eax INVOKE GetCommandLineA mov lpCmdLine1,eax INVOKE WinMain,hInst1,NULL,lpCmdLine1,SW_SHOWDEFAULT INVOKE ExitProcess,eax Win32标准函数调用方式,函数返回值通过EAX寄存器返回。可见本程序的初始化和结束代码很简单,只是调用GetModuleHandle函数获取应用程序当前实例的句柄,调用GetCommandLine函数获取命令行参数,然后调用WinMain函数(过程),WinMain函数返回后调用ExitProcess函数终止程序的执行。将本程序与SIMPLE.C程序比较,可以看出程序结构十分相似,本程序大量使用了MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令,并不比C语言麻烦多少。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值