Win32汇编教程十一
进程控制

在这儿下载本节的所有源程序

概述

进程控制简单的说相当于在一个程序中执行另一个程序,你可以把它想象成在 Dos 下用 int 21h/4bh 功能来执行另外一个程序,如果单从执行另一个程序的目的来讲,在 Windows 中有不少方法,如使用 ShellExecute 等,但这些 Api 仅仅是“执行”而已,进程控制的意义在于可以创建一个进程,并可以通过进程句柄结束进程,同样你也可以通过进程句柄来跟踪程序,还可以用 ReadProcessMemory 和 WriteProcessMemory 来读写子进程的内存空间。

进程控制要使用的相关 API 有下面这些:

创建进程的函数为CreateProcess,该函数比较复杂,共有十个参数,但有个好消息是使用时大部分可以用 NULL。

BOOL CreateProcess(
LPCTSTR lpApplicationName, // 执行程序文件名
LPTSTR lpCommandLine, // 参数行
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程安全参数
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全参数
BOOL bInheritHandles, // 继承标记
DWORD dwCreationFlags, // 创建标记
LPVOID lpEnvironment, // 环境变量
LPCTSTR lpCurrentDirectory, // 运行该子进程的初始目录
LPSTARTUPINFO lpStartupInfo, // 创建该子进程的相关参数
LPPROCESS_INFORMATION lpProcessInformation // 创建后用于被创建子进程的信息
);


各个参数的说明如下:

调用 CreateProcess 函数有三个参数是必需的,一在 lpApplicationName 或 lpCommandLine 指定文件名,二是 lpStartupInfo 结构,三是 PROCESS_INFORMATION 结构,因为 PROCESS_INFORMATION 结构返回了进程建立后的句柄,以后的一切操作将要用到这些返回的句柄,它是由系统填写的,结构说明如下:

typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; //进程句柄
HANDLE hThread; //进程的主线程句柄
DWORD dwProcessId; //进程ID
DWORD dwThreadId; //进程的主线程ID
} PROCESS_INFORMATION;

另外还有一个关键的结构 STARTUPINFO,该结构定义如下:

typedef struct STARTUPINFO {
DWORD cb; //结构长度
LPTSTR lpReserved; //保留
LPTSTR lpDesktop; //保留
LPTSTR lpTitle; //如果为控制台进程则为显示的标题
DWORD dwX; //窗口位置
DWORD dwY; //窗口位置
DWORD dwXSize; //窗口大小
DWORD dwYSize; //窗口大小
DWORD dwXCountChars; //控制台窗口字符号宽度
DWORD dwYCountChars; //控制台窗口字符号高度
DWORD dwFillAttribute; //控制台窗口填充模式
DWORD dwFlags; //创建标记
WORD wShowWindow; //窗口显示标记,如同ShowWindow中的标记
WORD cbReserved2; //
LPBYTE lpReserved2; //
HANDLE hStdInput; //标准输入句柄
HANDLE hStdOutput; //标准输出句柄
HANDLE hStdError; //标准错误句柄
} STARTUPINFO, *LPSTARTUPINFO;

结构中 dwFlags 指定了其它的一些字段是否有效,如:dwFlags包含 STARTF_USESIZE 表示dwXSize和dwYSize有效,包含STARTF_USEPOSITION表示dwX和dwY有效,等等。如果不是有特殊的要求,我们不用自己去填写这个结构,只需用 GetStartupInfo 让 Windows 为你填写好了,这样,建立一个进程的语句就是:

...
stStartUp STARTUPINFO stProcInfo PROCESS_INFORMATION <?>
stProcInfo PROCESS_INFORMATION <?>
...

invoke GetStartupInfo,addr stStartUp
invoke CreateProcess,NULL,addr szFileName,NULL,NULL,NULL,NORMAL_PRIORITY_CLASS,NULL,NULL,offset stStartUp,offset stProcInfo

...

如果成功的话,eax 将返回非零值,注意返回在 PROCESS_INFORMATION 结构中的 hProcess,以后很多的操作都要用到它。

强制结束一个进程的 API 为 TerminateProcess

BOOL TerminateProcess(
HANDLE hProcess, // 进程句柄
UINT uExitCode // 退出代码
);

你可以使用语句 invoke TerminateProcess,structProcInfo.hProcess,0 来结束进程,要注意的是如果可能的话,尽量不要在程序中强制结束别的进程,因为使用 TerminateProcess 结束的进程,它装载的 dll 不能被正确卸载。这样可能会引起系统资源的无效占用。最好的办法在进程中自己使用 ExitProcess 退出。

查询一个进程状态的 API 为 GetExitCodeProcess。

BOOL GetExitCodeProcess(
HANDLE hProcess, // handle to the process
LPDWORD lpExitCode // address to receive termination status
);

如果进程尚未退出,函数将会返回STILL_ACTIVE。这个 API 是马上返回的。

等待进程执行可以用 WaitForSingleObject

这个 API 并不是单用于进程的等待,其它还可以用在线程等操作,但我们一般用它来等待进程的执行,它的申明是:

DWORD WaitForSingleObject(
HANDLE hHandle, // handle of object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds
);

如果我们要等待进程执行 1 秒钟,可以 invoke WaitForSingleObject,stProcInfo.hProcess,1000 如果要等到进程结束,可以用 WaitForSingleObject,stProcInfo.hProcess,INFINITE ,参数 2 中的 INFINITE 在 Windows.inc 中有定义,意思是无穷等待。

最后,当不再使用进程句柄的时候,不要忘了使用 CloseHandle 关闭 hProcess 和 hThread,否则会浪费系统句柄的资源。

源程序 - 汇编源文件


	.386
	.model flat, stdcall
	option casemap :none   ; case sensitive
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;	Include 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include		windows.inc
include		user32.inc
include		kernel32.inc
include		comctl32.inc
include		comdlg32.inc
include		gdi32.inc

includelib	user32.lib
includelib	kernel32.lib
includelib	comctl32.lib
includelib	comdlg32.lib
includelib	gdi32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;	Equ 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN	equ	3000
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ID_BROWSE	equ	3001
ID_RUN		equ	3002
ID_EXIT		equ	3003
ID_TEXT		equ	3004

F_RUNNING	equ	0001h	;进程在运行中
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;	数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.data?

stStartUp	STARTUPINFO		<?>
stProcInfo	PROCESS_INFORMATION	<?>
stOpenFileName	OPENFILENAME	<?>

hRunThread	dd	?
hInstance	dd	?
hWinMain	dd	?
hIcon		dd	?
szBuffer	db	512 dup	(?)

dwFlag		dd	?
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

		.data

szExcute	db	'执行(&E)',0		;按钮文字
szKill		db	'终止(&E)',0
szExcuteError	db	'启动应用程序错误!',0

szTitleOpen	db	"Open executable file...",0
szExt		db	'*.exe',0
szFilter	db	'Excutable Files',0,'*.exe;*.com',0
		db	0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;	代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.code

if		DEBUG
		include		Debug.asm
endif
include		Win.asm

;********************************************************************
; 执行程序用的线程
; 1. 用 CreateProcess 建立进程
; 2. 用 WaitForSingleOject 等待进程结束
;********************************************************************
_RunThread	proc	uses ebx ecx edx esi edi,\
		dwParam:DWORD

		or	dwFlag,F_RUNNING
;********************************************************************
; 取消“退出”按钮并把“执行”按钮改为“中止”
;********************************************************************
		invoke	GetDlgItem,hWinMain,ID_EXIT
		invoke	EnableWindow,eax,FALSE
		invoke	SendDlgItemMessage,hWinMain,ID_RUN,WM_SETTEXT,0,offset szKill

;********************************************************************
; 执行文件,如果成功则等待程序结束
;********************************************************************
		invoke	GetStartupInfo,addr stStartUp
		invoke	CreateProcess,NULL,addr szBuffer,NULL,NULL,\
			NULL,NORMAL_PRIORITY_CLASS,NULL,NULL,offset stStartUp,offset stProcInfo
		.if	eax !=	0
			invoke	WaitForSingleObject,stProcInfo.hProcess,INFINITE
			invoke	CloseHandle,stProcInfo.hProcess
			invoke	CloseHandle,stProcInfo.hThread
		.else
			invoke	MessageBox,hWinMain,addr szExcuteError,NULL,MB_OK or MB_ICONERROR
		.endif
;********************************************************************
; Enable “退出”按钮并把“中止”按钮改为“执行”
;********************************************************************
		invoke	GetDlgItem,hWinMain,ID_EXIT
		invoke	EnableWindow,eax,TRUE
		invoke	SendDlgItemMessage,hWinMain,ID_RUN,WM_SETTEXT,0,offset szExcute
		and	dwFlag,not F_RUNNING
		ret

_RunThread	endp

;********************************************************************
;	窗口程序
;********************************************************************
DialogMainProc	proc	uses ebx edi esi, \
		hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD

		mov	eax,wMsg
;********************************************************************
		.if	eax ==	WM_INITDIALOG
			mov	eax,hWnd
			mov	hWinMain,eax
			call	_Init
;********************************************************************
		.elseif	eax ==	WM_CLOSE
			invoke	EndDialog,hWinMain,NULL
;********************************************************************
		.elseif	eax ==	WM_COMMAND
			mov	eax,wParam
			.if	ax ==	ID_BROWSE
				call	_BrowseFile
				call	_CheckText
			.elseif	ax ==	ID_TEXT
				invoke	GetDlgItemText,hWinMain,ID_TEXT,addr szBuffer,512
				call	_CheckText
			.elseif	ax ==	ID_RUN
;********************************************************************
; 如果没有在执行中(dwFlag 没有置位) 则建立线程,在线程中执行程序
; 如果已经在执行中,则用 TerminateProcess 终止执行
;********************************************************************
				test	dwFlag,F_RUNNING
				.if	ZERO?
					invoke	CreateThread,NULL,NULL,offset _RunThread,\
					NULL,NULL,offset hRunThread
				.else
					invoke	TerminateProcess,stProcInfo.hProcess,-1
				.endif
			.elseif	ax ==	ID_EXIT
				invoke	EndDialog,hWinMain,NULL
			.endif
		.else
;********************************************************************
;	注意:对话框的消息处理后,要返回 TRUE,对没有处理的消息
;	要返回 FALSE
;********************************************************************
			mov	eax,FALSE
			ret
		.endif
		mov	eax,TRUE
		ret

DialogMainProc	endp
;********************************************************************
; 程序入口
;********************************************************************
start:
		invoke	InitCommonControls
		invoke	GetModuleHandle,NULL
		mov	hInstance,eax
		invoke	DialogBoxParam,hInstance,DLG_MAIN,NULL,offset DialogMainProc,0
		invoke	ExitProcess,NULL
;********************************************************************
_Init		proc

		invoke	_CenterWindow,hWinMain

		invoke	SendDlgItemMessage,hWinMain,ID_TEXT,EM_LIMITTEXT,512,NULL
		invoke	GetDlgItem,hWinMain,ID_RUN
		invoke	EnableWindow,eax,FALSE

		ret

_Init		endp
;********************************************************************
; 根据 text control 中有无字符决定是否将“执行”按钮 Disable 掉
;********************************************************************
_CheckText	proc

		invoke	GetDlgItemText,hWinMain,ID_TEXT,addr szBuffer,512
		invoke	lstrlen,addr szBuffer
		.if	eax != 0 || (dwFlag & F_RUNNING)
			invoke	GetDlgItem,hWinMain,ID_RUN
			invoke	EnableWindow,eax,TRUE
		.else
			invoke	GetDlgItem,hWinMain,ID_RUN
			invoke	EnableWindow,eax,FALSE
		.endif
		ret

_CheckText	endp
;********************************************************************
_BrowseFile	proc

		mov	stOpenFileName.Flags,OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST
		mov	stOpenFileName.lStructSize,SIZEOF stOpenFileName
		mov	eax,hWinMain
		mov	stOpenFileName.hwndOwner,eax
		mov	stOpenFileName.lpstrFilter,offset szFilter	;扩展名
		mov	stOpenFileName.lpstrFile,offset szBuffer	;文件名缓冲
		mov	stOpenFileName.nMaxFile,512			;文件名缓冲长度
		mov	stOpenFileName.lpstrInitialDir,0
		mov	stOpenFileName.lpstrTitle,offset szTitleOpen
		mov	stOpenFileName.lpstrDefExt,offset szExt
		invoke	GetOpenFileName,offset stOpenFileName
		.if	eax == FALSE
			ret
		.endif
		invoke	SetDlgItemText,hWinMain,ID_TEXT,addr szBuffer
		ret

_BrowseFile	endp
;********************************************************************
		end	start


程序的分析和要点

    本程序在使用调用 GetOpenFileName 或者自己在文本框中输入执行文件名,然后通过 CreateProcess 建立进程,最后用 WaitForSingleObject 等待进程结束,如果在对话框的处理过程中等待会导致程序在进程返回前无法响应,所以程序中用 CreateThread 建立一个线程来实现这个过程,当子过程返回的时候,线程结束。dwFlag 中的 0 位作为标志位,表示是否子过程在运行中,如果这一位置 1 的话,按下“终止”按钮会用 TerminateProcess 来强制终止子进程。





(C) Copyright by LuoYunBin's Win32 ASM Page,http://asm.yeah.net