📄 013.txt
字号:
cmp al,CHAR_DELI
jz _argv_loop2
cmp _dwSize,1 ;如果返回缓存区满则忽略
jle @F
cmp @dwFlag,TRUE
jne @F
stosb
dec _ dwSize
@@:
jmp _argv_loop1 ;继续处理参数内容
_argv_loop2:
lodsb
or al,al
jz _argv_end
cmp al,CHAR_DELI
jz _argv_loop1
cmp _dwSize,1 ;如果返回缓存区满则忽略
jle @F
cmp @dwFlag,TRUE
jne @F
stosb
dec _dwSize
@@:
jmp _argv_loop2
_argv_end:
xor al,al
stosb
popad
ret
_argv endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
这两个通用子程序被存放在_CmdLine.asm文件中,读者可以在其他的程序中用include语句将它包含使用,其中函数_argc返回命令行参数的个数,当执行文件时没有附带参数的时候,函数的返回值一般是1,这时获取的命令行字符串中仅有一个组成部分——那就是可执行文件名;_argv函数则将指定编号的参数返回到一个缓冲区中,读者可以这样使用:
invoke _argv,dwArgv,lpReturn,dwSize
其中dwArgv参数指定要获取的参数编号,0表示获取字符串中的第一个组成部分(一般是文件名),1表示获取第二个组成部分,也就是在文件名后面输入的第1个参数,以此类推,函数对返回的字符串已经做了处理,丢弃了中间或者两端的所有双引号。lpReturn指向用来接收参数字符串的缓冲区,dwSize指定了缓冲区的大小。
这里有一个使用这两个函数的例子,源代码在所附光盘的Chapter13\CmdLine目录中,其中CmdLine.asm的内容如下:
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
szBuffer1 db 4096 dup (?)
szBuffer2 db 4096 dup (?)
szOutput db 8192 dup (?)
.const
szCaption db ~命令行参数~,0
szFormat1 db ~可执行文件名称:~,0dh,0ah,~%s~,0dh,0ah,0ah
db ~参数总数:%d~,0dh,0ah,0
szFormat2 db ~参数[%d]:%s~,0dh,0ah,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
include _Cmdline.asm ;包含公用的命令行参数处理函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
invoke GetModuleFileName,NULL,\
offset szBuffer1,sizeof szBuffer1
invoke _argc
mov ebx,eax
invoke wsprintf,addr szOutput,addr szFormat1,\
addr szBuffer1,eax
xor esi,esi
.while esi < ebx
invoke _argv,esi,addr szBuffer2,sizeof szBuffer2
invoke wsprintf,addr szBuffer1,\
addr szFormat2,esi,addr szBuffer2
invoke lstrcat,addr szOutput,addr szBuffer1
inc esi
.endw
invoke MessageBox,NULL,addr szOutput,addr szCaption,MB_OK
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
程序很简单,首先调用GetModuleFileName函数获取可执行文件的文件名,这是为了方便读者和参数中获取的文件名做个对比,然后程序调用_argc函数获得参数数量,并根据这个数量循环获取每个参数。编译链接后输入命令:
cmdline aaa bbb "ccc ddd" eee
程序会显示出如图13.1所示的消息框。
图13.1 命令行参数例子的运行结果
可见函数正确划分了命令行参数字符串中的各个参数。需要说明的是,为了能够在某个参数中使用空格,函数同样规定可以将参数中的空格用双引号包含,所以参数字符串中的"ccc ddd"被解释为一个参数并丢弃了两端的双引号。
13.2 执行可执行文件
(
13.2.1 方法一:Shell调用
Win32中可以通过ShellExecute和WinExec函数来执行另一个可执行文件,本节介绍这两个函数的用法。WinExec函数的使用方法是:
invoke WinExec,lpCmdLine,dwCmdShow
lpCmdLine参数指向一个以0结尾的字符串,这个字符串中包含可执行文件加上命令行参数,如果被执行的文件会显示一个窗口,那么函数可以在dwCmdShow参数中指定窗口的显示方式,这个参数的定义同ShowWindow函数中的dwCmdShow参数。
如果文件被成功执行,那么函数返回一个大于31的值。使用WinExec函数执行文件和在Windows“开始”菜单的“运行”中键入命令在效果上是一样的。
另一个函数ShellExecute的功能相对比较多一点,函数的语法是:
invoke ShellExecute,hWnd,lpOperation,lpFile,lpParam,\
lpDirectory,dwCmdShow
这个函数既可以用来执行一个可执行文件,也可以指定一个数据文件名让Windows自动查找关联到这个数据文件的可执行文件,并执行这个可执行文件来处理指定的数据文件,数据文件名会以命令行参数的方式传递给可执行文件。函数的参数定义如下。
● hWnd——用来指定被执行文件显示的窗口所属的父窗口。
● lpFile——用来指定文件名,文件名既可以是可执行文件也可以是数据文件。
● lpOperation——指向一个表示执行方式的字符串,字符串可以是以下取值:
■ “open”——文件被打开,这时lpFile指定的文件名可以是可执行文件、目录名或数据文件名。如果lpOperation参数为空,函数默认执行open操作。
■ “print”——文件被打印,这时lpFile指定的文件名必须是数据文件。如果指定的是可执行文件,那么函数当做“open”操作。
■ “explore”——浏览lpFile参数中指定的目录。
● lpParameters——当lpFile参数指定了一个可执行文件,本参数用来指定命令行参数。如果lpFile参数指定的是数据文件,那么本参数必须是NULL。
● lpDirectory——执行或打开文件时使用的默认目录。
● dwCmdShow——如果函数执行了一个可执行文件,这个参数指定窗口的打开方式。
如果文件被成功执行,那么函数返回一个大于31的值。这里是几个使用ShellExecute函数的例子:
.data
szFileName db ~Test.exe~,0
szCmdline db ~aaa bbb ccc~,0
szEmail db ~bigluo@telekbird.com.cn~,0
szWebPage db ~http://asm.yeah.net~,0
szHelp db ~Win32asm.chm~,0
.code
invoke ShellExecute,0,0,addr szFileName,addr szCmdline,0,SW_SHOWNORMAL (1)
invoke ShellExecute,0,0,addr szEmail,0,0,SW_SHOW (2)
invoke ShellExecute,0,0,addr szWebPage,0,0,SW_SHOW (3)
invoke ShellExecute,0,0,addr szHelp,0,0,SW_SHOW (4)
例子(1)执行Test.exe文件,并将命令行参数“aaa bbb ccc”传递给它。例子(2)打开默认的邮件收发程序并显示一个“新建邮件”窗口,指定的邮件地址会被自动地填入到收件人一栏中。例子(3)打开浏览器并自动打开网站http://asm.yeah.net。例子(4)会运行chm帮助文件阅读器hh.exe,并自动打开Win32asm.chm帮助文件。
13.2.2 方法二:创建进程
用ShellExecute和WinExec函数来执行文件是很方便的,调用这两个函数从某种意义上讲相当于创建了新的进程,但是函数返回以后,这些新建的进程却脱离了我们的控制,我们无法知道它们在什么时候结束,也无法去强制结束它们。要对进程进行后续的控制,必须使用函数CreateProcess来创建进程。
当一个进程被创建的时候,系统进行以下的操作:
● 系统为进程创建一个内核对象,并将它的初始计数设置为1,与线程对象类似,进程对象只是一个比较小的数据结构,它包含进程的一些统计信息。进程对象可以通过进程句柄来引用。
● 系统为进程创建一个虚拟地址空间,并将可执行文件装载到这个地址空间中。系统同时处理可执行文件的导入表,将导入表中登记的所有dll文件装入。每个dll文件被装入的时候,DLL的入口函数被执行,如果入口函数返回初始化失败信息的话,进程的初始化失败。可执行文件本身和所有的dll文件都被看做是单独的模块,都被分配了一个实例句柄(实例句柄在数值上等于模块装入到地址空间中的线性地址)。
● 系统为进程建立一个主线程,主线程将从可执行文件的入口地址开始执行。
对于线程来说,Windows为系统中的每个线程分配一个线程句柄和线程ID以便区分它们,同样,对于进程来说,每个进程也对应一个进程句柄和一个进程ID。
当某个进程创建了一个新的进程的时候,被创建的进程称为“子进程”,创建它的进程称为“父进程”,子进程可以从父进程那里继承环境变量以及其他一些对象,在子进程中可以继续创建“孙”进程。
创建进程使用CreateProcess函数。下面是一个使用这个函数的例子程序,程序显示如图13.2所示的对话框,允许用户输入需要执行的文件名(或者通过“浏览”按钮选择文件名)和传递给文件的命令行参数,当文件开始执行时,程序将“浏览”按钮与文件名输入框等子窗口灰化,在子进程结束以后再恢复它们。程序也可以通过“终止”按钮强制结束子进程的运行(“执行”按钮在子进程开始执行后被改为“终止”按钮)。
图13.2 建立进程的例子程序
程序的源代码在所附光盘的Chapter13\Process目录中,其中的Process.rc文件定义了上面所示的对话框:
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#include
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define ICO_MAIN 1000
#define DLG_MAIN 1000
#define IDC_FILE 1001
#define IDC_CMDLINE 1002
#define IDC_BROWSE 1003
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 111, 104, 201, 57
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "执行文件"
FONT 9, "宋体"
{
LTEXT "文件名", -1, 7, 8, 25, 8
EDITTEXT IDC_FILE, 35, 5, 160, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
LTEXT "命令行", -1, 7, 25, 25, 8
EDITTEXT IDC_CMDLINE, 35, 22, 160, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
PUSHBUTTON "浏览(&B)", IDC_BROWSE, 115, 38, 40, 14
PUSHBUTTON "执行(&E)", IDOK, 155, 38, 40, 14, WS_DISABLED | WS_TABSTOP
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
汇编源文件Process.asm的内容如下:
.386
.model flat, stdcall
option casemap :none
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -