在本课中,我们将用汇编语言写一个 Windows 程序,程序运行时将弹出一个消息框并显示"Win32 assembly
is great!"。
理论:
Windows
为编写应用程序提供了大量的资源。其中最重要的是Windows API (Application Programming
Interface)。 Windows API是一大组功能强大的函数,它们本身驻扎在 Windows
中供人们随时调用。这些函数的大部分被包含在几个动态链接库(DLL)中,譬如:kernel32.dll、 user32.dll 和
gdi32.dll。
Kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。除了上面主要的三个动态链接库,您还可以调用包含在其他动态链接库中的函数,当然您必须要有关于这些函数的足够的资料。
动态链接库,顾名思义,这些 API 的代码本身并不包含在 Windows
可执行文件中,而是当要使用时才被加载。为了让应用程序在运行时能找到这些函数,就必须事先把有关的重定位信息嵌入到应用程序的可执行文件中。这些信息存在于引入库中,由链接器把相关信息从引入库中找出插入到可执行文件中。您必须指定正确的引入库,因为只有正确的引入库才会有正确的重定位信息。
当应用程序被加载时 Windows
会检查这些信息,这些信息包括动态链接库的名字和其中被调用的函数的名字。若检查到这样的信息,Windows
就会加载相应的动态链接库,并且重定位调用的函数语句的入口地址,以便在调用函数时控制权能转移到函数内部。
接下来我们来看看 includelib 伪指令,和 include
不同,它仅仅是告诉编译器您的程序引用了哪个库。当编译器处理到该指令时会在生成的目标文件中插入链接命令告诉链接器链入什么库。当然您还可以通过在链接器的命令行指定引入库名称的方法来达到和用includelib指令相同的目的,但考虑到命令行仅能够传递128个字符而且要不厌其烦地在命令行敲字符,所以这种方法是非常不可取的。
.386 .model flat,stdcall option casemap:none
include \masm32\include\windows.inc include
\masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc includelib
\masm32\lib\user32.lib
.data MsgBoxCaption db "Iczelion
Tutorial No.2",0 MsgBoxText db "Win32 Assembly is Great!",0
编译、链接上面的程序段,得到可执行文件。运行,哈哈,窗口上弹出了一个对话框,上面有一行字:“Win32 Assembly
is Great!”。想一想,我们是用汇编写出来的,所以我们有理由为编写了一个最简单的 WIN32
程序感到高兴。(译者注:如果明天我们能够像在 DOS 下那样每一行都用汇编写,那我们有理由为自己感到自豪。)
Windows 程序中,在写图形用户界面时需要调用大量的标准 Windows Gui
函数。其实这对用户和程序员来说都有好处,对于用户,面对的是同一套标准的窗口,对这些窗口的操作都是一样的,所以使用不同的应用程序时无须重新学习操作。对程序员来说,这些
Gui
源代码都是经过了微软的严格测试,随时拿来就可以用的。当然至于具体地写程序对于程序员来说还是有难度的。为了创建基于窗口的应用程序,必须严格遵守规范。作到这一点并不难,只要用模块化或面向对象的编程方法即可。
下面我就列出在桌面显示一个窗口的几个步骤:
得到您应用程序的句柄(必需);
得到命令行参数(如果您想从命令行得到参数,可选); 注册窗口类(必需,除非您使用 Windows 预定义的窗口类,如
MessageBox 或 dialog box; 产生窗口(必需); 在桌面显示窗口(必需,除非您不想立即显示它);
刷新窗口客户区; 进入无限的获取窗口消息的循环; 如果有消息到达,由负责该窗口的窗口回调函数处理;
如果用户关闭窗口,进行退出处理。 相对于单用户的 DOS 下的编程来说,Windows
下的程序框架结构是相当复杂的。但是 Windows 和 DOS 在系统架构上是截然不同的。Windows
是一个多任务的操作系统,故系统中同时有多个应用程序彼此协同运行。这就要求 Windows
程序员必须严格遵守编程规范,并养成良好的编程风格。
内容:
下面是我们简单的窗口程序的源代码。在进入复杂的细节前,我将提纲挈领地指出几点要点:
您应当把程序中要用到的所有常量和结构体的声明放到一个头文件中,并且在源程序的开始处包含这个头文件。这么做将会节省您大量的时间,也免得一次又一次的敲键盘。目前,最完善的头文件是
hutch 写的,您可以到 hutch 或我的网站下载。您也可以定义您自己的常量和结构体,但最好把它们放到独立的头文件中 用
includelib 指令,包含您的程序要引用的库文件,譬如:若您的程序要调用 "MessageBox",
您就应当在源文件中加入如下一行: includelib user32.lib 这条语句告诉 MASM
您的程序将要用到一些引入库。如果您不止引用一个库,只要简单地加入 includelib
语句,不要担心链接器如何处理这么多的库,只要在链接时用链接开关 /LIBPATH 指明库所在的路径即可。
在其它地方运用头文件中定义函数原型,常数和结构体时,要严格保持和头文件中的定义一致,包括大小写。在查询函数定义时,这将节约您大量的时间;
在编译,链接时用makefile文件,免去重复敲键。 .386 .model flat,stdcall
option casemap:none include \masm32\include\windows.inc
include \masm32\include\user32.inc includelib
\masm32\lib\user32.lib ; calls to functions in user32.lib and
kernel32.lib include \masm32\include\kernel32.inc includelib
\masm32\lib\kernel32.lib
WinMain proto WORD,WORD,WORD,WORD
.DATA ; initialized data ClassName db "SimpleWinClass",0
; the name of our window class AppName db "Our First Window",0 ;
the name of our window
.DATA? ; Uninitialized data
hInstance HINSTANCE ? ; Instance handle of our program
CommandLine LPSTR ? .CODE ; Here begins our code start:
invoke GetModuleHandle, NULL ; get the instance handle of our
program. ; Under Win32, hmodule==hinstance mov hInstance,eax
mov hInstance,eax invoke GetCommandLine ; get the command
line. You don't have to call this function IF ; your program
doesn't process the command line. mov CommandLine,eax invoke
WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT ; call the main
function invoke ExitProcess, eax ; quit our program. The exit
code is returned in eax from WinMain.
WinMain proc
hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShowWORD
LOCAL wc:WNDCLASSEX ; create local variables on stack LOCAL
msg:MSG LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
; fill values in members of wc 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 ; register our
window class invoke CreateWindowEx,NULL,\ ADDR ClassName,\
ADDR AppName,\ WS_OVERLAPPEDWINDOW,\ CW_USEDEFAULT,\
CW_USEDEFAULT,\ CW_USEDEFAULT,\ CW_USEDEFAULT,\
NULL,\ NULL,\ hInst,\ NULL mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow ; display our window on desktop
invoke UpdateWindow, hwnd ; refresh the client area
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM,
lParam:LPARAM .IF uMsg==WM_DESTROY ; if the user closes our
window invoke PostQuitMessage,NULL ; quit our application
.ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; Default
message processing ret .ENDIF xor eax,eax ret
WndProc endp
end start
分析:
看到一个简单的
Windows 程序有这么多行,您是不是有点想撤?
但是您必须要知道的是上面的大多数代码都是模板而已,模板的意思即是指这些代码对差不多所有标准 Windows 程序来说都是相同的。在写
Windows 程序时您可以把这些代码拷来拷去,当然把这些重复的代码写到一个库中也挺好。其实真正要写的代码集中在 WinMain
中。这和一些 C 编译器一样,无须要关心其它杂务,集中精力于 WinMain 函数。唯一不同的是 C
编译器要求您的源代码有必须有一个函数叫 WinMain。否则 C
无法知道将哪个函数和有关的前后代码链接。相对C,汇编语言提供了较大的灵活性,它不强行要求一个叫 WinMain 的函数。
下面我们开始分析,您可得做好思想准备,这可不是一件太轻松的活。
.386 .model
flat,stdcall option casemap:none
WinMain proto
:DWORD,:DWORD,:DWORD,:DWORD
include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc includelib
\masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
您可以把前三行看成是"必须"的.
.386告诉MASN我们要用80386指令集。 . model
flat,stdcall告诉MASM 我们用的内存寻址模式,此处也可以加入stdcall告诉MASM我们所用的参数传递约定。
.DATA
"分段"包含了您应用程序的所有代码,这些代码必须都在 .code 和 end 之间。至于 label 的命名只要遵从 Windows
规范而且保证唯一则具体叫什么倒是无所谓。我们程序的第一条语句是调用 GetModuleHandle
去查找我们应用程序的句柄。在Win32下,应用程序的句柄和模块的句柄是一样的。您可以把实例句柄看成是您的应用程序的 ID
号。我们在调用几个函数是都把它作为参数来进行传递,所以在一开始便得到并保存它就可以省许多的事。
特别注意:WIN32下的实例句柄实际上是您应用程序在内存中的线性地址。
WIN32
中函数的函数如果有返回值,那它是通过 eax 寄存器来传递的。其他的值可以通过传递进来的参数地址进行返回。一个 WIN32
函数被调用时总会保存好段寄存器和 ebx,edi,esi和ebp 寄存器,而 ecx和edx
中的值总是不定的,不能在返回是应用。特别注意:从 Windows API 函数中返回后,eax,ecx,edx
中的值和调用前不一定相同。当函数返回时,返回值放在eax中。如果您应用程序中的函数提供给 Windows
调用时,也必须尊守这一点,即在函数入口处保存段寄存器和 ebx,esp,esi,edi
的值并在函数返回时恢复。如果不这样一来的话,您的应用程序很快会崩溃。从您的程序中提供给 Windows
调用的函数大体上有两种:Windows 窗口过程和 Callback 函数。
上面是WinMain的定义。注意跟在 proc 指令后的parameter:type形式的参数,它们是由调用者传给
WinMain 的,我们引用是直接用参数名即可。至于压栈和退栈时的平衡堆栈工作由 MASM 在编译时加入相关的前序和后序汇编指令来进行。
LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND LOCAL
伪指令为局部变量在栈中分配内存空间,所有的 LOCAL 指令必须紧跟在 PROC 之后。LOCAL 后跟声明的变量,其形式是
变量名:变量类型。譬如 LOCAL wc:WNDCLASSEX 即是告诉 MASM 为名字叫 wc 的局部边量在栈中分配长度为
WNDCLASSEX 结构体长度的内存空间,然后我们在用该局部变量是无须考虑堆栈的问题,考虑到 DOS
下的汇编,这不能不说是一种恩赐。不过这就要求这样申明的局部变量在函数结束时释放栈空间,(也即不能在函数体外被引用),另一个缺点是您因不能初始化您的局部变量,不得不在稍后另外再对其赋值。
WNDCLASSEX 中最重要的成员莫过于lpfnWndProc了。前缀 lpfn
表示该成员是一个指向函数的长指针。在 Win32中由于内存模式是 FLAT 型,所以没有 near 或 far
的区别。每一个窗口类必须有一个窗口过程,当 Windows
把属于特定窗口的消息发送给该窗口时,该窗口的窗口类负责处理所有的消息,如键盘消息或鼠标消息。由于窗口过程差不多智能地处理了所有的窗口消息循环,所以您只要在其中加入消息处理过程即可。下面我将要讲解
WNDCLASSEX 的每一个成员
Windows
中的文本是一个GUI(图形用户界面)对象。每一个字符实际上是由许多的像素点组成,这些点在有笔画的地方显示出来,这样就会出现字符。这也是为什么我说“绘制”字符,而不是写字符。通常您都是在您应用程序的客户区“绘制”字符串(尽管您也可以在客户区外“绘制”)。Windows
下的“绘制”字符串方法和 Dos 下的截然不同,在 Dos 下,您可以把屏幕想象成 85 x 25 的一个平面,而 Windows
下由于屏幕上同时有几个应用程序的画面,所以您必须严格遵从规范。Windows
通过把每一个应用程序限制在他的客户区来做到这一点。当然客户区的大小是可变的,您随时可以调整。
在您在客户区“绘制”字符串前,您必须从 Windows 那里得到您客户区的大小,确实您无法像在 DOS
下那样随心所欲地在屏幕上任何地方“绘制”,绘制前您必须得到 Windows 的允许,然后 Windows
会告诉您客户区的大小,字体,颜色和其它 GUI 对象的属性。您可以用这些来在客户区“绘制”。
什么是“设备环境”(DC)呢? 它其实是由 Windows
内部维护的一个数据结构。一个“设备环境”和一个特定的设备相连。像打印机和显示器。对于显示器来说,“设备环境”和一个个特定的窗口相连。
我们将写一个应用程序,它会在客户区的中心显示一行 "Win32 assembly is great and easy!"
.386 .model flat,stdcall option casemap:none
WinMain proto WORD,WORD,WORD,WORD
include
\masm32\include\windows.inc include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib include
\masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib
.DATA ClassName db "SimpleWinClass",0 AppName db
"Our First Window",0 OurText db "Win32 assembly is great and
easy!",0
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc includelib
\masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.386 .model flat,stdcall option
casemap:none WinMain proto WORD,WORD,WORD,WORD
include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc include
\masm32\include\gdi32.inc includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib includelib
\masm32\lib\gdi32.lib
.data ClassName db
"SimpleWinClass",0 AppName db "Our First Window",0 char
WPARAM 20h ; the character the program receives from keyboard
例子: .386 .model flat,stdcall option casemap:none
WinMain proto WORD,WORD,WORD,WORD
include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc include
\masm32\include\gdi32.inc includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib includelib
\masm32\lib\gdi32.lib
.data ClassName db
"SimpleWinClass",0 AppName db "Our First Window",0
MouseClick db 0 ; 0=no click yet
.data? hInstance
HINSTANCE ? CommandLine LPSTR ? hitpoint POINT <>
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib
.data ClassName db
"SimpleWinClass",0 AppName db "Our First Window",0 MenuName
db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0 Hello_string
db "Hello, my friend",0 Goodbye_string db "See you again, bye",0
MenuName db "FirstMenu",0 ; The name of our menu in the
resource file. Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0 Goodbye_string db "See
you again, bye",0
--------------------------------------------------------------------------------
本课中我们将探讨控件,这些控件是我们程序主要的输入输出设备。 理论: WINDOWS
提供了几个预定义的窗口类以方便我们的使用。大多数时间内,我们把它们用在对话框中,所以我们一般就它们叫做子窗口控件。子窗口控件会自己处理消息,并在自己状态发生改变时通知父窗口。这样就大大地减轻了我们的编程工作,所以我们应尽可能地利用它们。本课中我们把这些控件放在窗口中以简化程序,但是大多数时间内子窗口控件都是放在对话框中的。我们示例中演示的子窗口控件包括:按钮、下拉菜单、检查框、单选按钮、编辑框等。使用子窗口控件时,先调用CreateWindow
或 CreateWindowEx。在这里由于WINDOWS
已经注册了这些子控件,所以无须我们再注册。当然我们不能改变它们的类名称。譬如:如果您想产生一个按钮,在调用上述两个函数时就必须指定类名为"button"。其他必须指定的参数还有父窗口的句柄和将要产生的子控件的ID号。子控件的ID号是用来标识子控件的,故也必须是唯一
的。子控件产生后,当其状态改变时将会向父窗口发送消息。一般我们应在父窗口的WM_CREATE消息中产生字控件。子控件向父窗口发送的消息是WM_COMMAND,并在传递的参数wPara的底位中包括控件的ID号,消息号在wParam的高位,lParam中则包括了子控件的窗口的句柄。各类控件有不同的消息代码集,详情请参见WIN32
API参考手册。父窗口也可以通过调用函数SendMessage向子控件发送消息,其中第一个参数是子控件的窗口句柄,第二个参数是要发送的消息号,附加的参数可以在wParam和lParam中传递,其实只要知道了某个窗口的句柄就可以用该函数向其发送相关消息。所以产生了子窗口后必须处理WM_COMMAND消息以便可以接收到子控件的消息。
例子: 我们将产生一个窗口,在该窗口中有一个编辑框和一个按钮。当您按下按钮时
,会弹出一个对话框其中显示了您在编辑框中输入的内容。另外,该应用程序还有一个菜单,其中有四个菜单项: Say Hello --
把一个字符串输入编辑控件; Clear Edit Box -- 清除编辑控件中的字符串; Get Text --
弹出对话框显示编辑控件中的字符串; Exit -- 退出应用程序。 .386 .model
flat,stdcall option casemap:none WinMain proto
screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib
.data ClassName db
"SimpleWinClass",0 AppName db "Our First Window",0 MenuName
db "FirstMenu",0 ButtonClassName db "button",0 ButtonText db
"My First Button",0 EditClassName db "edit",0 TestString db
"Wow! I'm in an edit box now",0
.data? hInstance
HINSTANCE ? CommandLine LPSTR ? hwndButton HWND ?
hwndEdit HWND ? buffer db 512 dup(?) ; buffer to store the
text retrieved from the edit box
.const ButtonID equ 1 ;
The control ID of the button control EditID equ 2 ; The control
ID of the edit control IDM_HELLO equ 1 IDM_CLEAR equ 2
IDM_GETTEXT equ 3 IDM_EXIT equ 4
.386 .model flat,stdcall option casemap:none
WinMain proto
screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib .data ClassName db "DLGCLASS",0
MenuName db "MyMenu",0 DlgName db "MyDialog",0 AppName
db "Our First Dialog Box",0 TestString db "Wow! I'm in an edit
box now",0
MyMenu MENU
BEGIN POPUP "Test Controls" BEGIN MENUITEM "Get
Text", IDM_GETTEXT MENUITEM "Clear Text", IDM_CLEAR MENUITEM
"", , 0x0800 /*MFT_SEPARATOR*/ MENUITEM "E&xit", IDM_EXIT
END END
.386 .model flat,stdcall option casemap:none
DlgProc proto
screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib
.data DlgName db "MyDialog",0
AppName db "Our Second Dialog Box",0 TestString db "Wow! I'm
in an edit box now",0
IDR_MENU1 MENU BEGIN POPUP "Test Controls"
BEGIN MENUITEM "Get Text", IDM_GETTEXT MENUITEM "Clear
Text", IDM_CLEAR MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
MENUITEM "E&xit", IDM_EXIT END END
分析如下: DlgProc proto
screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD,screen.width-300)this.width=screen.width-300'>WORD
例子:
下例中,我们演示了当用户选择"File->Open"时,将弹出一个打开文件对话框,当用户选择了某个文件打开时,会弹出一个对话框,告知要打开的文件的全路径名,文件名和文件扩展名。
.386 .model flat,stdcall option casemap:none WinMain
proto WORD,WORD,WORD,WORD include \masm32\include\windows.inc
include \masm32\include\user32.inc include
\masm32\include\kernel32.inc include
\masm32\include\comdlg32.inc includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib includelib
\masm32\lib\comdlg32.lib
.data ClassName db "SimpleWinClass",0 AppName db
"Our Main Window",0 MenuName db "FirstMenu",0 ofn
OPENFILENAME <> FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0 buffer db MAXSIZE dup(0)
OurTitle db "-=Our First Open File Dialog Box=-: Choose the file
to open",0 FullPathName db "The Full Filename with Path is: ",0
FullName db "The Filename is: ",0 ExtensionName db "The
Extension is: ",0 OutputString db OUTPUTSIZE dup(0) CrLf db
0Dh,0Ah,0
分析: mov ofn.lStructSize,SIZEOF ofn push hWnd pop
ofn.hwndOwner push hInstance pop ofn.hInstance
我们在此填充结构体OPENFILENAME变量ofn的有关成员。
mov ofn.lpstrFilter,
OFFSET FilterString
这里FilterString
是文件过滤模式的字符串地址,我们指定的过滤模式字符串如下:
FilterString db "All
Files",0,"*.*",0 db "Text Files",0,"*.txt",0,0
注意:所有的模式串都是配对的,前一个是描述,后一个才是真正的模式,次处"*.*"和"*.txt"是WIONDOWS用来寻找匹配的欲打开的文件的。我们当能可以指定任何模式,但是不要忘记在结尾处加0以代表字符串已结束,否则您的对话框在操作时可能不稳定。
mov ofn.lpstrFile, OFFSET buffer mov ofn.nMaxFile,MAXSIZE
这里是把缓冲区的地址放到结构体中,同时必须设定大小。以后我们可以随意编辑在该缓冲区中返回的信息。
mov
ofn.Flags, OFN_FILEMUSTEXIST or \ OFN_PATHMUSTEXIST or
OFN_LONGNAMES or\ OFN_EXPLORER or OFN_HIDEREADONLY
.386 .model flat,stdcall option casemap:none
WinMain proto WORD,WORD,WORD,WORD include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc include
\masm32\include\comdlg32.inc includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib includelib
\masm32\lib\comdlg32.lib
.data ClassName db "Win32ASMEditClass",0 AppName db
"Win32 ASM Edit",0 EditClass db "edit",0 MenuName db
"FirstMenu",0 ofn OPENFILENAME <> FilterString db "All
Files",0,"*.*",0 db "Text Files",0,"*.txt",0,0 buffer db
MAXSIZE dup(0)
.data? hInstance HINSTANCE ?
CommandLine LPSTR ? hwndEdit HWND ? ; Handle to the edit
control hFile HANDLE ? ; File handle hMemory HANDLE ?
;handle to the allocated memory block pMemory DWORD ? ;pointer
to the allocated memory block SizeReadWrite DWORD ? ; number of
bytes actually read or write
.data ClassName db "Win32ASMFileMappingClass",0
AppName db "Win32 ASM File Mapping Example",0 MenuName db
"FirstMenu",0 ofn OPENFILENAME <> FilterString db "All
Files",0,"*.*",0 db "Text Files",0,"*.txt",0,0 buffer db
MAXSIZE dup(0) hMapFile HANDLE 0 ; Handle to the memory mapped
file, must be ;initialized with 0 because we also use it as
;a flag in WM_DESTROY section too
.data? hInstance
HINSTANCE ? CommandLine LPSTR ? hFileRead HANDLE ? ; Handle
to the source file hFileWrite HANDLE ? ; Handle to the output
file hMenu HANDLE ? pMemory DWORD ? ; pointer to the data in
the source file SizeWritten DWORD ? ; number of bytes actually
written by WriteFile
PROCESS_INFORMATION STRUCT hProcess HANDLE ? ; handle to
the child process hThread HANDLE ? ; handle to the primary
thread of the child process dwProcessId DWORD ? ; ID of the
child process dwThreadId DWORD ? ; ID of the primary thread of
the child process PROCESS_INFORMATION ENDS
进程句柄和进程ID是两个不同的概念。进程ID好似一个唯一值,而进程句柄是调用相关的WINDOWS API
后得到的一个返回值。不能用进程句柄来标识一个进程的唯一性,因为这个值并不唯一。在调用CreateProcess产生新进程后,该进程就被创建,而且CerateProcess函数立即返回。您可以调用函数GetExitCodeProcess来检验进程是否结束。该函数的原型如下:
GetExitCodeProcess proto hProcessWORD, lpExitCodeWORD
.data ClassName db "Win32ASMProcessClass",0
AppName db "Win32 ASM Process Example",0 MenuName db
"FirstMenu",0 processInfo PROCESS_INFORMATION <>
programname db "msgbox.exe",0
.data? hInstance
HINSTANCE ? CommandLine LPSTR ? hMenu HANDLE ? ExitCode
DWORD ? ; contains the process exitcode status from
GetExitCodeProcess call.
.data ClassName db "Win32ASMThreadClass",0
AppName db "Win32 ASM MultiThreading Example",0 MenuName db
"FirstMenu",0 SuccessString db "The calculation is completed!",0
WM_FINISH equ WM_USER+100h WM_USER消息是我们能够使用的最小消息值。
显然我们一看到WM_FINISH,就能从字面上理解该消息的意义。主线程接收到该消息后,会弹出一个对话框告诉用户,计算线程已经结束了。
通过线程之间的通讯,用户可以多次选择"Create Thread",那样就可以运行多个计算线程了。
本例子中,线程之间的通讯是单向的。如果您想让主线程也能向工作者线程发送消息的话,譬如加入一个菜单项来控制工作者线程的结束,您可以这样做:
add a menu item saying something like "Kill Thread" in the menu
a global variable which is used as a command flag. TRUE=Stop the
thread, FALSE=continue the thread Modify ThreadProc to check the
value of the command flag in the loop.
设立一个全局变量,当线程启动前,我们设置它的值为FALSE,当用户激活了我们加的菜单项时,该值变成TRUE。在线程的代码段ThreadProc中每次减1前,判断该值,如果为TRUE的话线程就结束循环体中的计算并退出线程。
--------------------------------------------------------------------------------
第十六课 事件对象
.data
ClassName db "Win32ASMEventClass",0 AppName db "Win32 ASM
Event Example",0 MenuName db "FirstMenu",0 SuccessString db
"The calculation is completed!",0 StopString db "The thread is
stopped",0 EventStop BOOL FALSE
;---------------------------------------------------------------------------------------------
; UseDLL.asm
;----------------------------------------------------------------------------------------------
.386 .model flat,stdcall option casemap:none include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc includelib
\masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib
.data LibName db "DLLSkeleton.dll",0 FunctionName db
"TestHello",0 DllNotFound db "Cannot load library",0 AppName
db "Load Library",0 FunctionNotFound db "TestHello function not
found",0
例子代码: .386
.model flat,stdcall option casemap:none include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc include
\masm32\include\comctl32.inc includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib WinMain PROTO WORD,WORD,WORD,WORD
.data ClassName db
"CommonControlWinClass",0 AppName db "Common Control Demo",0
ProgressClass db "msctls_progress32",0 ; the class name of the
progress bar Message db "Finished!",0 TimerID dd 0
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM,
lParam:LPARAM .if uMsg==WM_CREATE invoke
CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
WS_CHILD+WS_VISIBLE,100,\ 200,300,20,hWnd,IDC_PROGRESS,\
hInstance,NULL mov hwndProgress,eax mov eax,1000 ; the
lParam of PBM_SETRANGE message contains the range mov
CurrentStep,eax shl eax,16 ; the high range is in the high word
invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax invoke
SendMessage,hwndProgress,PBM_SETSTEP,10,0 invoke
CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS mov
hwndStatus,eax invoke SetTimer,hWnd,IDC_TIMER,100,NULL ; create
a timer mov TimerID,eax .elseif uMsg==WM_DESTROY invoke
PostQuitMessage,NULL .if TimerID!=0 invoke
KillTimer,hWnd,TimerID .endif .elseif uMsg==WM_TIMER ; when
a timer event occurs invoke
SendMessage,hwndProgress,PBM_STEPIT,0,0 ; step up the progress in
the progress bar sub CurrentStep,10 .if CurrentStep==0
invoke KillTimer,hWnd,TimerID mov TimerID,0 invoke
SendMessage,hwndStatus,SB_SETTEXT,0,addr Message invoke
MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
invoke SendMessage,hwndStatus,SB_SETTEXT,0,0 invoke
SendMessage,hwndProgress,PBM_SETPOS,0,0 .endif .else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .endif
xor eax,eax ret WndProc endp end start
TVM_GETNEXTITEM wParam = 标志 lParam =
树型视图的句柄(仅仅当wParam的值是某些标志位时才是必须的)。 wParam中的值非常重要, 我解释如下:
TVGN_CARET 选中的项目 TVGN_CHILD hitem参数指定项目的第一个子项目
TVGN_DROPHILITE 拖-拉操作的目的项目 TVGN_FIRSTVISIBLE 第一个可见项目
TVGN_NEXT 下一个同级项目 TVGN_NEXTVISIBLE
下一个可见项目,指定的项目必须可见。发送消息TVM_GETITEMRECT 来决定项目是否可见 TVGN_PARENT
指定项目的父项目 TVGN_PREVIOUS 前一个同级项目 TVGN_PREVIOUSVISIBLE
前一个可见项目,指定的项目必须可见。发送消息TVM_GETITEMRECT 来决定项目是否可见 TVGN_ROOT 根项目
由此您可以通过发送该消息来得到项目的句柄,然后在发送消息TVM_GETITEM时在结构体变量TV_ITEM的成员变量hItem中放入该项目的句柄就可以得到关于该项目的有关信息了。
在树型视图中进行拖-拉操作
也就是因为这一部分我才决定写这课教程。当我按照InPrise公司的WIN32帮助来运行例子时,发现它的帮助中缺少真正重要的信息。我只有通过自己做实验,最后总算弄明白来个中来由。希望您不要和我一样再去走这些弯路,下面我把我所知的在树型视图中进行拖-拉操作的步骤描述如下:
当用户要拖动一个项目时,树型视图控件会给它的父窗口发送TVN_BEGINDRAG通知消息。您可以在此处创建表示项目处在拖动操作中的图象,这可以通过发送TVM_CREATEDRAGIMAGE消息给树型视图,让其为目前使用的图象产生一副缺省的图象来实现。树型视图控件将创建一个图象列表,其中仅包含一副在拖动中显示的图象,图象列表创建后,您可以得到它的句柄。
在拖拉的图象生成后,您可以通过调用ImageList_BeginDrag来指定拖动图象的热点位置。
ImageList_BeginDrag PROTO himlTrackWORD, \ iTrackWORD , \
dxHotspotWORD, \ dyHotspotWORD himlTrack
是包含了拖拉时显示的图象的图象列表的句柄 iTrack 是选中的图象在图象列表中的索引号。 dxHotspot
因为在拖动中该图象被用来取代光标,所以我们必须指定图象中的哪一点是光标的左上角的位置。dxHotspot是水平相对位置。
dyHotspot 是垂直相对位置。
iTrack等于0。如果您要想光标的热点在拖拉中显示的图象的左上角,把dxHotspot和dyHotspot都设成0。
当拖拉的图象要显示时,我们调用ImageList_DragEnter 在树型视图中显示该图象。
ImageList_DragEnter PROTO hwndLockWORD, xWORD, yWORD
hwndLock 是进行拖拉中的窗口的句柄,拖拉的动作限制在该窗口中。 x 和
y是在拖拉时显示图象的初始位置的坐标值。这些值是相对于窗口的左上角而不是客户区的左上角。
既然可以显示拖动中的图象了,我们就要处理拖动操作了。在这里有一个小问题。我们监视拖动是通过监视鼠标光标的移动来实现的,譬如在移动时我们通过捕获WM_MOUSEMOVE消息来得到移动中的坐标位置,通过捕获WM_LBUTTONUP消息来获知用户的放下操作。但这时如果鼠标光标移过子窗口时父窗口就无法再得到鼠标光标的移动以及鼠标的按键消息了。解决办法是调用SetCapture函数了锁定鼠标事件,这样无论鼠标移到那里和有什么动作,我们的窗口都可以知道了。
在处理WM_MOUSEMOVE消息时,您可以调用ImageList_DragMove来更新图象移动的轨迹。
该函数可以移动拖放操作中的图象位置。另外,如果您想让移动中的图象经过某些项目时高量度显示,可以调用TVM_HITTEST
来确定是否经过某个项目的上面。如果是的话,您可以发送TVM_SELECTITEM消息并设置
TVGN_DROPHILITE标志位使得那个项目高亮度显示。注意:在发送消息TVM_SELECTITEM前,您必须先隐藏图象列表,否则会留下非常难看的轨迹。要隐藏拖动中的图象可以调用ImageList_DragShowNolock,在显示完高亮度的图象后再调用该函数以让拖动中的图象再正常显示。
当用户释放主键后,您必须做几件事。
如果您在高亮度显示的时候释放鼠标主键(表示您想把该项目加到此处),您必须使该项目变成正常地显示,这可以通过发送消息TVM_SELECTITEM消息并设置标志位TVGN_DROPHILITE来实现,只是这时lParam必须为0。如果您不让高亮度显示的项目恢复正常,那就会发生一个奇怪的现象:当您再选择另外的项目时,那个项目的图象会包含在一个正方形中,当时高亮度显示的项目依旧是上一个项目。接下来必须调用ImageList_EndDrag和ImageList_DragLeave。还有调用ReleaseCapture来释放捕获的鼠标。如果您创建了一个图象列表,那还要调用calling
ImageList来将它销毁,在拖放操作结束后您可以进行另外其它的操作。 例子代码: .386 .model
flat,stdcall option casemap:none include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc include
\masm32\include\comctl32.inc include \masm32\include\gdi32.inc
includelib \masm32\lib\gdi32.lib includelib
\masm32\lib\comctl32.lib includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib WinMain PROTO
WORD,WORD,WORD,WORD .const IDB_TREE equ 4006 ; ID of the
bitmap resource .data ClassName db "TreeViewWinClass",0
AppName db "Tree View Demo",0 TreeViewClass db
"SysTreeView32",0 Parent db "Parent Item",0 Child1 db
"child1",0 Child2 db "child2",0 DragMode dd FALSE ; a flag
to determine if we are in drag mode
.data? hInstance
HINSTANCE ? hwndTreeView dd ? ; handle of the tree view control
hParent dd ? ; handle of the root tree view item hImageList
dd ? ; handle of the image list used in the tree view control
hDragImageList dd ? ; handle of the image list used to store the
drag image
如果你曾经在 Windows
环境下编过程序,有时候就会发现:有一个现成的窗口,几乎有你所需要的全部功能,但还不完全一样(否则就没有必要讲这一节了)。你曾遇到过这样的处境吗,如果你需要一个具有过滤特殊字符功能的
Edit 控件。当然最直接的方法就是自己用代码来实现,但这的确是一个费时又很困难的任务,而窗口子类化就可以用来做这种事情。
现在来解释实现细节:当用户往文本框中输入字符时,Windows 会给Edit控件的窗口函数发送 WM_CHAR
消息。这个窗口函数本身寄生于 Windows
中,因此不能直接修改它。但是我们可以重定向这个消息使之发送到我们自己编写的窗口处理函数。如果自定义窗口要处理这个消息那就可以处理它,如果不处理就可以把这个消息转发到它原来窗口处理函数。通过这种方式,自定义的窗口处理函数就把它自己插入到
Windows 系统和 Edit 控件之间。
.386 .model flat,stdcall option casemap:none
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc includelib
\masm32\lib\comctl32.lib includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib WinMain PROTO
WORD,WORD,WORD,WORD EditWndProc PROTO WORD,WORD,WORD,WORD
.data ClassName db "SubclassWinClass",0 AppName db
"Subclassing Demo",0 EditClass db "EDIT",0 Message db "You
pressed Enter in the text box!",0
.386 .model
flat,stdcall option casemap:none include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc include
\masm32\include\gdi32.inc includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib WinMain PROTO WORD,WORD,WORD,WORD
.const IDR_MAINMENU equ 101 ; the ID of the main menu
IDM_ASSEMBLE equ 40001
.data ClassName db
"PipeWinClass",0 AppName db "One-way Pipe Example",0 EditClass
db "EDIT",0 CreatePipeError db "Error during pipe creation",0
CreateProcessError db "Error during process creation",0
CommandLine db "ml /c /coff /Cp test.asm",0
.386 .model
flat,stdcall option casemap:none include
\masm32\include\windows.inc include \masm32\include\user32.inc
include \masm32\include\kernel32.inc includelib
\masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
WM_SUPERCLASS equ WM_USER+5 WinMain PROTO
WORD,WORD,WORD,WORD EditWndProc PROTO WORD,WORD,WORD,WORD
.data ClassName db "SuperclassWinClass",0 AppName db
"Superclassing Demo",0 EditClass db "EDIT",0 OurClass db
"SUPEREDITCLASS",0 Message db "You pressed the Enter key in the
text box!",0
.你可以在TOOLINFO结构的uFlags成员指定
TTF_SUBCLASS标志。此标志告诉控件子类化包含"工具"的窗口以便无需窗口的协作便可捕获鼠标消息。由于除了控件自己处理截获的鼠标消息和指定TTF_SUBCLASS标志之外不用编写多余的代码,因此很易于使用。
就是这些了,到这步为止,控件已经全功能了.还有几个你应当知道的相关消息.
TTM_ACTIVATE.如果你想动态的允许或者禁止工具提示控件,这个小消息就是为你而备.wParam值为TRUE,允许控件.若为FALSE,禁止控件.控件初始创建的时候无需发送消息激活他,便被自动设为允许状态.
TTM_GETTOOLINFO and TTM_SETTOOLINFO.
如果你想在把TOOLINFO结构传递给控件之后获得或者改变其值,使用此消息.你需要用正确的uId and
hWnd值指定要改变的"工具".如果你只想改变rect成员的值,使用TTM_NEWTOOLRECT
消息,如果仅想改变提示文本,使用TTM_UPDATETIPTEXT消息. TTM_SETDELAYTIME.
使用此消息指定控件显示提示文本时的时间延迟. 例子:
例子是一个有两个按钮的对话框,对话框的客户区分为4部分:左上、右上、左下、右下.每个区域都指定为有自己提示文本的"工具",两个按钮也有自己的提示文本.
.386 .model flat,stdcall option casemap:none include
\masm32\include\windows.inc include \masm32\include\kernel32.inc
include \masm32\include\user32.inc include
\masm32\include\comctl32.inc includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib includelib
\masm32\lib\kernel32.lib DlgProc proto WORD,WORD,WORD,WORD
EnumChild proto WORD,WORD SetDlgToolArea proto
WORD,WORD,WORD,WORD,WORD .const IDD_MAINDIALOG equ 101
.data ToolTipsClassName db "Tooltips_class32",0
MainDialogText1 db "This is the upper left area of the dialog",0
MainDialogText2 db "This is the upper right area of the
dialog",0 MainDialogText3 db "This is the lower left area of the
dialog",0 MainDialogText4 db "This is the lower right area of
the dialog",0 .data? hwndTool dd ? hInstance dd ?
.code start: invoke GetModuleHandle,NULL mov
hInstance,eax invoke
DialogBoxParam,hInstance,IDD_MAINDIALOG,NULL,addr DlgProc,NULL
invoke ExitProcess,eax
DlgProc proc
hDlgWORD,uMsgWORD,wParamWORD,lParamWORD LOCAL ti:TOOLINFO
LOCAL idWORD LOCAL rect:RECT .if uMsg==WM_INITDIALOG
invoke InitCommonControls invoke CreateWindowEx,NULL,ADDR
ToolTipsClassName,NULL,\ TTS_ALWAYSTIP,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInstance,NULL mov hwndTool,eax mov id,0 mov
ti.cbSize,sizeof TOOLINFO mov ti.uFlags,TTF_SUBCLASS push
hDlg pop ti.hWnd invoke GetWindowRect,hDlg,addr rect
invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText1,id,addr
rect inc id invoke SetDlgToolArea,hDlg,addr ti,addr
MainDialogText2,id,addr rect inc id invoke
SetDlgToolArea,hDlg,addr ti,addr MainDialogText3,id,addr rect
inc id invoke SetDlgToolArea,hDlg,addr ti,addr
MainDialogText4,id,addr rect invoke EnumChildWindows,hDlg,addr
EnumChild,addr ti
.elseif uMsg==WM_CLOSE invoke
EndDialog,hDlg,NULL .else mov eax,FALSE ret .endif
mov eax,TRUE ret DlgProc endp
EnumChild proc
uses edi hwndChildWORD,lParamWORD LOCAL buffer[256]:BYTE mov
edi,lParam assume editr TOOLINFO push hwndChild pop
[edi].uId or [edi].uFlags,TTF_IDISHWND invoke
GetWindowText,hwndChild,addr buffer,255 lea eax,buffer mov
[edi].lpszText,eax invoke
SendMessage,hwndTool,TTM_ADDTOOL,NULL,edi assume edi:nothing
ret EnumChild endp
SetDlgToolArea proc uses edi esi
hDlgWORD,lptiWORD,lpTextWORD,idWORD,lprectWORD mov edi,lpti
mov esi,lprect assume esitr RECT assume editr TOOLINFO
.if id==0 mov [edi].rect.left,0 mov [edi].rect.top,0
mov eax,[esi].right sub eax,[esi].left shr eax,1 mov
[edi].rect.right,eax mov eax,[esi].bottom sub eax,[esi].top
shr eax,1 mov [edi].rect.bottom,eax .elseif id==1
mov eax,[esi].right sub eax,[esi].left shr eax,1 inc
eax mov [edi].rect.left,eax mov [edi].rect.top,0 mov
eax,[esi].right sub eax,[esi].left mov [edi].rect.right,eax
mov eax,[esi].bottom sub eax,[esi].top mov
[edi].rect.bottom,eax .elseif id==2 mov [edi].rect.left,0
mov eax,[esi].bottom sub eax,[esi].top shr eax,1 inc
eax mov [edi].rect.top,eax mov eax,[esi].right sub
eax,[esi].left shr eax,1 mov [edi].rect.right,eax mov
eax,[esi].bottom sub eax,[esi].top mov [edi].rect.bottom,eax
.else mov eax,[esi].right sub eax,[esi].left shr
eax,1 inc eax mov [edi].rect.left,eax mov
eax,[esi].bottom sub eax,[esi].top shr eax,1 inc eax
mov [edi].rect.top,eax mov eax,[esi].right sub
eax,[esi].left mov [edi].rect.right,eax mov eax,[esi].bottom
sub eax,[esi].top mov [edi].rect.bottom,eax .endif
push lpText pop [edi].lpszText invoke
SendMessage,hwndTool,TTM_ADDTOOL,NULL,lpti assume edi:nothing
assume esi:nothing ret SetDlgToolArea endp
EnumChildProc proto hwndChildWORD,
lParamWORD hwndChild是EnumChildWindows函数枚举的句柄. lParam
就是你传递给EnumChildWindows函数的同一个lParam. 在例子中.我们如此调用 EnumChildWindows
函数: invoke EnumChildWindows,hDlg,addr EnumChild,addr ti
我们把TOOLINFO结构的地址放在lParam参数中传递,是因为我们要在EnumChild函数中注册每个子控件.如果我们不使用这种方法,就需要将ti声明为全局变量,但这可能会引入很多bug.
当我们调用 EnumChildWindows时,
Windows会枚举出对话框上所有的子控件并为每个子控件调用一次EnumChild f函数.
这样如果我们的对话框有两个控件,EnumChild将被调用两次. EnumChild 函数填充TOOLINFO
结构的相应成员并向控件注册. EnumChild proc uses edi hwndChildWORD,lParamWORD
LOCAL buffer[256]:BYTE mov edi,lParam assume editr
TOOLINFO push hwndChild pop [edi].uId ; we use the whole
client area of the control as the tool or
[edi].uFlags,TTF_IDISHWND invoke GetWindowText,hwndChild,addr
buffer,255 lea eax,buffer ; use the window text as the tooltip
text mov [edi].lpszText,eax invoke
SendMessage,hwndTool,TTM_ADDTOOL,NULL,edi assume edi:nothing
ret EnumChild endp
注意在例子中,我们使用了一种不同"工具":覆盖整个客户区的"工具",因此我们需要用包含"工具"窗口的句柄来填充uID成员,也必须在uFlags
成员中指定TTF_IDISHWND标志.
.386 .model flat,stdcall option casemap:none
include \masm32\include\windows.inc include
\masm32\include\kernel32.inc include
\masm32\include\comdlg32.inc include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib includelib
\masm32\lib\comdlg32.lib includelib \masm32\lib\user32.lib
.data AppName db "Win32 Debug Example no.2",0
ClassName db "SimpleWinClass",0 SearchFail db "Cannot find
the target process",0 TargetPatched db "Target patched!",0
buffer dw 9090h
;--------------------------------------------------------------------
; The partial source code of win.asm, our debuggee. It's
actually ; the simple window example in tutorial 2 with an
infinite loop inserted ; just before it enters the message loop.
;----------------------------------------------------------------------
调用GetThreadContext, 指定 ContextFlags为CONTEXT_CONTROL,
来获得标志寄存器的值 设置CONTEXT结构成员标志寄存器regFlag中的陷阱标志位 调用
SetThreadContext 等待调式事件。被调试程序将按单步模式执行,在每执行一条指令后,我们将得到调试
事件,u.Exception.pExceptionRecord.ExceptionCode值为EXCEPTION_SINGLE_STEP
如果要跟踪下一条指令,需要再次设置陷阱标志位。 例: .386 .model flat,stdcall
option casemap:none include \masm32\include\windows.inc
include \masm32\include\kernel32.inc include
\masm32\include\comdlg32.inc include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib includelib
\masm32\lib\comdlg32.lib includelib \masm32\lib\user32.lib
.data AppName db "Win32 Debug Example no.4",0 ofn
OPENFILENAME <> FilterString db "Executable
Files",0,"*.exe",0 db "All Files",0,"*.*",0,0 ExitProc db
"The debuggee exits",0Dh,0Ah db "Total Instructions executed :
%lu",0 TotalInstruction dd 0
.data? buffer db 512
dup(?) startinfo STARTUPINFO <> pi PROCESS_INFORMATION
<> DBEvent DEBUG_EVENT <> context CONTEXT
<>
Field name Meanings imask
一组标志位标明该结构体中那些成员变量中的值有效。它的意义和上面我们提到的LV_COLUMN型结构体中向对应的成员变量基本相同。更详细的信息,可以查询WIN32
API 手册。 iItem 该结构体代表的项目的索引号。索引号是从0开始编号的。该值和表单的“行”类似。
iSubItem
和上一个成员变量指定的项目相连的子项目的索引号。您可以把它当作表单的“列”。譬如您想要把一个项目插入到新创建的列表视图控件,iItem的值应为0(因为该项目是第一个项目),iSubItem的值也应当为0(我们想把该项目插到第一列)。如果你想指定一个子项目和该项目相连,iItem中应该是您想要相连的项目的索引号,iSubItem的值应当是大于0的值,具体的值取决于您想把该子项目插在那一列。如果你的列表视图控件一共有4列的化,第一列包含了项目,其余3列是留给子项目的。如果您想把子项目插在第四列,应当指定该值为3。
state
该成员变量包含的标志位反应了项目的状态。状态的改变可能是由用户的操作引起的或是程序改变的。这些状态包括:是否有焦点/高亮度显示/被选中(由于被剪切)/被选中等。另外还包括,以1为基数的索引用来代表是否处使用重叠/状态图标。
.386 .model flat,stdcall option casemap:none
include \masm32\include\windows.inc include
\masm32\include\user32.inc include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc includelib
\masm32\lib\comctl32.lib includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data ClassName db
"ListViewWinClass",0 AppName db "Testing a ListView Control",0
ListViewClassName db "SysListView32",0 Heading1 db
"Filename",0 Heading2 db "Size",0 FileNamePattern db "*.*",0
FileNameSortOrder dd 0 SizeSortOrder dd 0 template db
"%lu",0
String2Dword proc uses ecx edi edx esi StringWORD LOCAL
ResultWORD
mov Result,0 mov edi,String invoke
lstrlen,String .while eax!=0 xor edx,edx mov dl,byte ptr
[edi] sub dl,"0" mov esi,eax dec esi push eax
mov eax,edx push ebx mov ebx,10 .while esi > 0
mul ebx dec esi .endw pop ebx add Result,eax
pop eax inc edi dec eax .endw mov eax,Result
ret String2Dword endp
CompareFunc proc uses edi
lParam1WORD, lParam2WORD, SortTypeWORD LOCAL buffer[256]:BYTE
LOCAL buffer1[256]:BYTE LOCAL lvi:LV_ITEM
.data ClassName db "MDIASMClass",0
MDIClientName db "MDICLIENT",0 MDIChildClassName db
"Win32asmMDIChild",0 MDIChildTitle db "MDI Child",0 AppName
db "Win32asm MDI Demo",0 ClosePromptMessage db "Are you sure you
want to close this window?",0