六、资源在Win32汇编语言程序中的使用
资源在Win32应用程序中是很重要的,笔者编写了一个使用资源的C语言程序,该程序实现了菜单和一个“About”对话框,程序如下:
源程序(GENERIC.C):
#include #include "resource.h" static HINSTANCE hInst; static char szWindowClass[]="GENERIC"; LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { WNDCLASSEXA wcex; HWND hWnd; MSG msg; hInst=hInstance; 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=MAKEINTRESOURCE(IDR_MAINMENU); wcex.lpszClassName=szWindowClass; wcex.hIconSm=LoadIconA(hInstance,IDI_APPLICATION); if(!RegisterClassExA(&wcex)) return FALSE; } hWnd=CreateWindowExA(0,szWindowClass,"Generic", 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_COMMAND: switch(LOWORD(wParam)) { case IDM_EXIT: SendMessageA(hWnd,WM_CLOSE,0,0); return 0; case IDM_ABOUT: DialogBoxParamA(hInst,MAKEINTRESOURCE(IDD_ABOUT), hWnd,(DLGPROC)AboutDlgProc,0); return 0; default: return DefWindowProcA(hWnd,message,wParam,lParam) ; } 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; } BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if((LOWORD(wParam)==IDOK)||(LOWORD(wParam)==IDCANCEL)) { EndDialog(hDlg,LOWORD(wParam)); return TRUE; } return FALSE; default: return FALSE; } }
资源源文件(GENERIC.RC,使用Visual C++ 6.0建立):
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS / // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" / #undef APSTUDIO_READONLY_SYMBOLS / // Chinese (P.R.C.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) #ifdef _WIN32 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #endif //_WIN32 / // // Menu // IDR_MAINMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&Exit", IDM_EXIT END POPUP "&Help" BEGIN MENUITEM "&About...", IDM_ABOUT END END #ifdef APSTUDIO_INVOKED / // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED / // // Dialog // IDD_ABOUT DIALOG DISCARDABLE 0, 0, 92, 65 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About" FONT 9, "宋体" BEGIN LTEXT "Generic V1.0",IDC_STATIC1,20,10,50,10 DEFPUSHBUTTON "OK",IDOK,20,40,50,15 END / // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_ABOUT, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 85 TOPMARGIN, 7 BOTTOMMARGIN, 58 END END #endif // APSTUDIO_INVOKED #endif // Chinese (P.R.C.) resources / #ifndef APSTUDIO_INVOKED / // // Generated from the TEXTINCLUDE 3 resource. // / #endif // not APSTUDIO_INVOKED 资源头文件(RESOURCE.H,Visual C++ 6.0自动建立): //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by GENERIC.rc // #define IDR_MAINMENU 101 #define IDD_ABOUT 102 #define IDC_STATIC1 1000 #define IDM_EXIT 40001 #define IDM_ABOUT 40002 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40003 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif 本程序与一般Win32编程资料中的GENERIC程序基本相同,只是只使用ANSI字符集。 现在笔者用Win32汇编语言程序实现本程序的功能,程序如下: 包含文件(GENERIC.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_COMMAND = 0111h WM_CLOSE = 0010h WM_PAINT = 000fh WM_DESTROY = 0002h WM_INITDIALOG = 0110h IDOK = 1 IDCANCEL = 2 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 SendMessageA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD DialogBoxParamA PROTO stdcall, :HINSTANCE,:LPCSTR,:HWND,:DWORD,: DWORD BeginPaint PROTO stdcall, :HWND,:LPPAINTSTRUCT EndPaint PROTO stdcall, :HWND,:PTR PAINTSTRUCT PostQuitMessage PROTO stdcall, :DWORD DefWindowProcA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD EndDialog PROTO stdcall, :HWND,:DWORD 资源包含文件(RESOURCE.INC): IDR_MAINMENU = 101 IDD_ABOUT = 102 IDC_STATIC1 = 1000 IDM_EXIT = 40001 IDM_ABOUT = 40002 源程序(GENERIC.ASM): .386p .MODEL flat,stdcall INCLUDE GENERIC.INC INCLUDE RESOURCE.INC WinMain PROTO stdcall, :HINSTANCE,:HINSTANCE,:LPSTR,: DWORD .STACK 4096 .DATA WindowClass BYTE 'GENERIC',0 WindowTitle BYTE 'Generic',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,IDR_MAINMENU and 0000ffffh 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_COMMAND mov eax,wParam .IF ax==IDM_EXIT INVOKE SendMessageA,hWnd,WM_CLOSE,0,0 mov eax,0 ret .ELSEIF ax==IDM_ABOUT mov ebx,OFFSET AboutDlgProc INVOKE DialogBoxParamA,hInst1,IDD_ABOUT and 0000ffffh,hWnd,ebx,0 mov eax,0 ret .ELSE INVOKE DefWindowProcA,hWnd,message,wParam, lParam ret .ENDIF .ELSEIF 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 AboutDlgProc PROC hDlg:HWND,message:UINT,wParam:DWORD,lParam:DWORD .IF message==WM_INITDIALOG mov eax,TRUE ret .ELSEIF message==WM_COMMAND mov eax,wParam .IF (ax==IDOK)||(ax==IDCANCEL) INVOKE EndDialog,hDlg,ax mov eax,TRUE ret .ENDIF mov eax,FALSE ret .ELSE mov eax,FALSE ret .ENDIF mov eax,FALSE ret AboutDlgProc ENDP PUBLIC _start END
汇编连接本程序的命令如下:
ml /c /coff /Cp generic.asm rc generic.rc link /subsystem:windows /entry:_start generic.obj generic.res kernel32. lib user32.lib gdi32.lib
运行汇编连接后生成的GENERIC.EXE文件,屏幕上将显示出一个带有菜单的窗口,窗口的标题是“Generic”,菜单有两个主菜单项,分别是“File”和“Help”,选择“File”菜单项下的“Exit”菜单项可以退出程序,选择“Help”菜单项下的“About”菜单项可以显示“About”对话框。本程序与SIMPLE.C程序很相似,Win32汇编语言程序中使用资源的方法也与C语言程序没有很大的差别,都可以用资源编辑工具生成资源源文件和资源头文件,然后使用资源编译器编译资源源文件,将生成的资源文件(RES文件)与汇编器生成的目标文件和引入库文件连接在一起就可以了(资源头文件需要移植到汇编语言上,建立一个资源包含文件)。
C语言程序中的MAKEINTRESOURCE宏的实质是将资源标识符数值的高位字(不用)清零,然后强行转换成字符指针,Win32汇编语言程序中可以将资源标识符数值与0000FFFFH作AND运算。
七、控制台Win32汇编语言程序
学习过Win32SDK编程的读者一定知道控制台Win32应用程序,控制台Win32应用程序运行在控制台(MS-DOS窗口)下,与DOS下的C语言程序十分相似,程序入口点是main函数,使用标准C语言I/O函数进行I/O,也可以调用API。实际上控制台Win32应用程序与普通Win32应用程序没有本质上的区别,标准C语言I/O函数实际上还是调用了API,在控制台上进行I/O。控制台Win32汇编语言程序与C语言程序有一定的差别,需要获取控制台I/O句柄,然后使用控制台I/O句柄进行I/O(与文件句柄I/O相似),笔者以MASM 6.11中自带的控制台Win32汇编语言程序实例(HELLO.ASM)为例,程序如下:
.386 .MODEL flat, stdcall STD_OUTPUT_HANDLE EQU -11 GetStdHandle PROTO NEAR32 stdcall, nStdHandle:DWORD WriteFile PROTO NEAR32 stdcall, hFile:DWORD, lpBuffer:NEAR32, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32 ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD .STACK 4096 .DATA msg DB "Hello, world.", 13, 10 written DD 0 hStdOut DD 0 .CODE _start: INVOKE GetStdHandle, STD_OUTPUT_HANDLE ; Standard output handle mov hStdOut, eax INVOKE WriteFile, hStdOut, ; File handle for screen NEAR32 PTR msg, ; Address of string LENGTHOF msg, ; Length of string NEAR32 PTR written, ; Bytes written 0 ; Overlapped mode INVOKE ExitProcess, 0 ; Result code for parent process PUBLIC _start END
汇编连接本程序的命令如下:
ml /c /coff /Cp hello.asm link /subsystem:console /entry:_start hello.obj kernel32.lib
连接命令中/subsystem:console选项表示连接器生成控制台Win32应用程序。在MS-DOS窗口(控制台)下运行汇编连接后生成的HELLO.EXE文件,将会像MS-DOS程序一样显示出“Hello, world.”字符串。本程序调用了GetStdHandle函数获取标准控制台输出设备句柄,然后调用WriteFile函数向标准控制台输出设备句柄写字符串,完成控制台字符串输出,最后调用了ExitProcess函数终止程序的执行。
八、结束语
本教程读者阅读到这里,可能会莞尔一笑,原来Win32汇编语言也不过就是这么回事呀,确实,MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程。汇编语言确实比较复杂,但是Win32汇编语言对某些特殊方面有高级语言不可比拟的优点,如果你正在想编程清除Win32病毒(例如CIH病毒),或者你正在编写对速度要求较高的程序(例如大量计算的程序),不妨试试Win32汇编语言——或许正能够解决你的燃眉之急。本教程还简单介绍了Win32应用程序的执行机制,相信会对你探索Win32深层有一定的帮助。
资源在Win32应用程序中是很重要的,笔者编写了一个使用资源的C语言程序,该程序实现了菜单和一个“About”对话框,程序如下:
源程序(GENERIC.C):
#include #include "resource.h" static HINSTANCE hInst; static char szWindowClass[]="GENERIC"; LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { WNDCLASSEXA wcex; HWND hWnd; MSG msg; hInst=hInstance; 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=MAKEINTRESOURCE(IDR_MAINMENU); wcex.lpszClassName=szWindowClass; wcex.hIconSm=LoadIconA(hInstance,IDI_APPLICATION); if(!RegisterClassExA(&wcex)) return FALSE; } hWnd=CreateWindowExA(0,szWindowClass,"Generic", 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_COMMAND: switch(LOWORD(wParam)) { case IDM_EXIT: SendMessageA(hWnd,WM_CLOSE,0,0); return 0; case IDM_ABOUT: DialogBoxParamA(hInst,MAKEINTRESOURCE(IDD_ABOUT), hWnd,(DLGPROC)AboutDlgProc,0); return 0; default: return DefWindowProcA(hWnd,message,wParam,lParam) ; } 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; } BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if((LOWORD(wParam)==IDOK)||(LOWORD(wParam)==IDCANCEL)) { EndDialog(hDlg,LOWORD(wParam)); return TRUE; } return FALSE; default: return FALSE; } }
资源源文件(GENERIC.RC,使用Visual C++ 6.0建立):
//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS / // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" / #undef APSTUDIO_READONLY_SYMBOLS / // Chinese (P.R.C.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) #ifdef _WIN32 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #endif //_WIN32 / // // Menu // IDR_MAINMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&Exit", IDM_EXIT END POPUP "&Help" BEGIN MENUITEM "&About...", IDM_ABOUT END END #ifdef APSTUDIO_INVOKED / // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED / // // Dialog // IDD_ABOUT DIALOG DISCARDABLE 0, 0, 92, 65 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About" FONT 9, "宋体" BEGIN LTEXT "Generic V1.0",IDC_STATIC1,20,10,50,10 DEFPUSHBUTTON "OK",IDOK,20,40,50,15 END / // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_ABOUT, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 85 TOPMARGIN, 7 BOTTOMMARGIN, 58 END END #endif // APSTUDIO_INVOKED #endif // Chinese (P.R.C.) resources / #ifndef APSTUDIO_INVOKED / // // Generated from the TEXTINCLUDE 3 resource. // / #endif // not APSTUDIO_INVOKED 资源头文件(RESOURCE.H,Visual C++ 6.0自动建立): //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by GENERIC.rc // #define IDR_MAINMENU 101 #define IDD_ABOUT 102 #define IDC_STATIC1 1000 #define IDM_EXIT 40001 #define IDM_ABOUT 40002 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40003 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif 本程序与一般Win32编程资料中的GENERIC程序基本相同,只是只使用ANSI字符集。 现在笔者用Win32汇编语言程序实现本程序的功能,程序如下: 包含文件(GENERIC.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_COMMAND = 0111h WM_CLOSE = 0010h WM_PAINT = 000fh WM_DESTROY = 0002h WM_INITDIALOG = 0110h IDOK = 1 IDCANCEL = 2 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 SendMessageA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD DialogBoxParamA PROTO stdcall, :HINSTANCE,:LPCSTR,:HWND,:DWORD,: DWORD BeginPaint PROTO stdcall, :HWND,:LPPAINTSTRUCT EndPaint PROTO stdcall, :HWND,:PTR PAINTSTRUCT PostQuitMessage PROTO stdcall, :DWORD DefWindowProcA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD EndDialog PROTO stdcall, :HWND,:DWORD 资源包含文件(RESOURCE.INC): IDR_MAINMENU = 101 IDD_ABOUT = 102 IDC_STATIC1 = 1000 IDM_EXIT = 40001 IDM_ABOUT = 40002 源程序(GENERIC.ASM): .386p .MODEL flat,stdcall INCLUDE GENERIC.INC INCLUDE RESOURCE.INC WinMain PROTO stdcall, :HINSTANCE,:HINSTANCE,:LPSTR,: DWORD .STACK 4096 .DATA WindowClass BYTE 'GENERIC',0 WindowTitle BYTE 'Generic',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,IDR_MAINMENU and 0000ffffh 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_COMMAND mov eax,wParam .IF ax==IDM_EXIT INVOKE SendMessageA,hWnd,WM_CLOSE,0,0 mov eax,0 ret .ELSEIF ax==IDM_ABOUT mov ebx,OFFSET AboutDlgProc INVOKE DialogBoxParamA,hInst1,IDD_ABOUT and 0000ffffh,hWnd,ebx,0 mov eax,0 ret .ELSE INVOKE DefWindowProcA,hWnd,message,wParam, lParam ret .ENDIF .ELSEIF 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 AboutDlgProc PROC hDlg:HWND,message:UINT,wParam:DWORD,lParam:DWORD .IF message==WM_INITDIALOG mov eax,TRUE ret .ELSEIF message==WM_COMMAND mov eax,wParam .IF (ax==IDOK)||(ax==IDCANCEL) INVOKE EndDialog,hDlg,ax mov eax,TRUE ret .ENDIF mov eax,FALSE ret .ELSE mov eax,FALSE ret .ENDIF mov eax,FALSE ret AboutDlgProc ENDP PUBLIC _start END
汇编连接本程序的命令如下:
ml /c /coff /Cp generic.asm rc generic.rc link /subsystem:windows /entry:_start generic.obj generic.res kernel32. lib user32.lib gdi32.lib
运行汇编连接后生成的GENERIC.EXE文件,屏幕上将显示出一个带有菜单的窗口,窗口的标题是“Generic”,菜单有两个主菜单项,分别是“File”和“Help”,选择“File”菜单项下的“Exit”菜单项可以退出程序,选择“Help”菜单项下的“About”菜单项可以显示“About”对话框。本程序与SIMPLE.C程序很相似,Win32汇编语言程序中使用资源的方法也与C语言程序没有很大的差别,都可以用资源编辑工具生成资源源文件和资源头文件,然后使用资源编译器编译资源源文件,将生成的资源文件(RES文件)与汇编器生成的目标文件和引入库文件连接在一起就可以了(资源头文件需要移植到汇编语言上,建立一个资源包含文件)。
C语言程序中的MAKEINTRESOURCE宏的实质是将资源标识符数值的高位字(不用)清零,然后强行转换成字符指针,Win32汇编语言程序中可以将资源标识符数值与0000FFFFH作AND运算。
七、控制台Win32汇编语言程序
学习过Win32SDK编程的读者一定知道控制台Win32应用程序,控制台Win32应用程序运行在控制台(MS-DOS窗口)下,与DOS下的C语言程序十分相似,程序入口点是main函数,使用标准C语言I/O函数进行I/O,也可以调用API。实际上控制台Win32应用程序与普通Win32应用程序没有本质上的区别,标准C语言I/O函数实际上还是调用了API,在控制台上进行I/O。控制台Win32汇编语言程序与C语言程序有一定的差别,需要获取控制台I/O句柄,然后使用控制台I/O句柄进行I/O(与文件句柄I/O相似),笔者以MASM 6.11中自带的控制台Win32汇编语言程序实例(HELLO.ASM)为例,程序如下:
.386 .MODEL flat, stdcall STD_OUTPUT_HANDLE EQU -11 GetStdHandle PROTO NEAR32 stdcall, nStdHandle:DWORD WriteFile PROTO NEAR32 stdcall, hFile:DWORD, lpBuffer:NEAR32, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32 ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD .STACK 4096 .DATA msg DB "Hello, world.", 13, 10 written DD 0 hStdOut DD 0 .CODE _start: INVOKE GetStdHandle, STD_OUTPUT_HANDLE ; Standard output handle mov hStdOut, eax INVOKE WriteFile, hStdOut, ; File handle for screen NEAR32 PTR msg, ; Address of string LENGTHOF msg, ; Length of string NEAR32 PTR written, ; Bytes written 0 ; Overlapped mode INVOKE ExitProcess, 0 ; Result code for parent process PUBLIC _start END
汇编连接本程序的命令如下:
ml /c /coff /Cp hello.asm link /subsystem:console /entry:_start hello.obj kernel32.lib
连接命令中/subsystem:console选项表示连接器生成控制台Win32应用程序。在MS-DOS窗口(控制台)下运行汇编连接后生成的HELLO.EXE文件,将会像MS-DOS程序一样显示出“Hello, world.”字符串。本程序调用了GetStdHandle函数获取标准控制台输出设备句柄,然后调用WriteFile函数向标准控制台输出设备句柄写字符串,完成控制台字符串输出,最后调用了ExitProcess函数终止程序的执行。
八、结束语
本教程读者阅读到这里,可能会莞尔一笑,原来Win32汇编语言也不过就是这么回事呀,确实,MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程。汇编语言确实比较复杂,但是Win32汇编语言对某些特殊方面有高级语言不可比拟的优点,如果你正在想编程清除Win32病毒(例如CIH病毒),或者你正在编写对速度要求较高的程序(例如大量计算的程序),不妨试试Win32汇编语言——或许正能够解决你的燃眉之急。本教程还简单介绍了Win32应用程序的执行机制,相信会对你探索Win32深层有一定的帮助。