全部博文(290)
分类:
2007-12-31 15:30:00
Tutorial 14: Process
第十四课:进程
We will learn what a process is and how to create and terminate it.
我们将学习什么是进程以及如何创建和终止一个进程。
Download the example here.
Preliminary:
引导:
What is a process? I quote this definition from Win32 API reference:
"A process is an executing application that consists of a private virtual address space, code, data, and other operating system resources, such as files, pipes, and synchronization objects that are visible to the process."
什么是进程?我引用win32手册中的定义:进程是一个执行中的应用程序,它由一个私有的虚拟的地址空间,代码,数据以及其它属于操作系统的资源组成。操作系统的资源有文件,管道和同步对象等。(同步对象一个进程可见的部分。)
As you can see from the definition above, a process "owns" several objects: the address space, the executing module(s), and anything that the executing modules create or open. At the minimum, a process must consist of an executing module, a private address space and a thread. Every process must have at least one thread. What's a thread? A thread is actually an execution queue. When Windows first creates a process, it creates only one thread per process. This thread usually starts execution from the first instruction in the module. If the process later needs more threads, it can explicitly create them.
从上面的定义中你能看出,一个进程拥有的对象有:地址空间,一个可执行模块,以及由这个可执行模块创建和打开的任何东西。一个进程在最小的情况下也必须包含一可执行模块,一块私有的地址空间和一个线程。每一个进程至少要有一个线程。什么是线程?线程实际上是一个执行队列。当windows创建一个进程时,它基于进程创建一线程。这个线程用于启动模块中的第一条指令开始执行。如果一个进程在将来需要更多的线程,它能明确地创建它们。(在多线程的OS中,把线程作为独立运行的基本单位,而进程作为系统资源分配的基本单位 )
When Windows receives a command to create a process, it creates the private memory address space for the process and then it maps the executable file into the space. After that it creates the primary thread for the process.
Under Win32, you can also create processes from your own programs by calling CreateProcess function.
当windows接收到一个创建进程的命令时,它为这个进程创建一个私有的内存地址空间然后它映射这个可执行文件到这个地址空间中。随后它为这个进程创建了一个主线程。(最初的,原始的,第一位的)在win32中,你还可以通过调用CreateProcess函数从你自己的应用程序中创建多个进程。
CreateProcess has the following syntax:
CreateProcess句法如下:
CreateProcess proto lpApplicationName:DWORD,\
lpCommandLine:DWORD,\
lpProcessAttributes:DWORD,\
lpThreadAttributes:DWORD,\
bInheritHandles:DWORD,\
dwCreationFlags:DWORD,\
lpEnvironment:DWORD,\
lpCurrentDirectory:DWORD,\
lpStartupInfo:DWORD,\
lpProcessInformation:DWORD
Don't be alarmed by the number of parameters. We can ignore most of them.
不要为这么多的参数惊慌,我们可以忽略它们中的大多数。
lpApplicationName --> The name of the executable file with or without pathname that you want to execute. If this parameter is null, you must provide the name of the executable file in the lpCommandLine parameter.
LpApplicationName—〉包含或不包含路径名的可执行文件名,这个可执行文件是你想执行的。如果这个参数为空,你必须在lpCommandLine参数中提供这个可执行文件的名字。
lpCommandLine --> The command line arguments to the program you want to execute. Note that if the lpApplicationName is NULL, this parameter must contain the name of the executable file too. Like this: "notepad.exe readme.txt"
lpCommandLine—〉传递给你想执行的程序的命令行参数。注意:如果lpApplicationName参数的值为空,这个参数必须包含可执行文件的名字。像这样:“notepad.exe readme.txt”
lpProcessAttributes and lpthreadAttributes --> Specify the security attributes for the process and the primary thread. If they're NULLs, the default security attributes are used.
LpProcessAttributes和lpthreadAttributes--> 为进程和主线程指定安全属性.如果它们的值为空,默认的安全属性将被使用.
bInheritHandles --> A flag that specify if you want the new process to inherit all opened handles from your process.
bInheritHandles -- > 如果你想让新创建的进程从你的进程中继承所有已打开的句柄, 那么请指定这个标志参数.
dwCreationFlags --> Several flags that determine the behavior of the process you want to created, such as, do you want to process to be created but immediately suspended so that you can examine or modify it before it runs? You can also specify the priority class of the thread(s) in the new process. This priority class is used to determine the scheduling priority of the threads within the process. Normally we use NORMAL_PRIORITY_CLASS flag.
DwCreationFlags --> 用于确定你想创建进程的行为状态的几个标志参数.例如,你可以在创建一个进程后直接暂停它,以至于你能在它运行之前检查或是修改它.你还能用它指定一个新进程的线程的优先级.优先级是用来确定新进程内部线程的调度优先数的.通常我们使用: NORMAL_PRINORITY_CLASS 标志.
lpEnvironment --> A pointer to the environment block that contains several environment strings for the new process. If this parameter is NULL, the new process inherits the environment block from the parent process.
LpEnvironment -- > 指向环境块的指针,环境块包含新进程的几个环境字串.如果这个参数是空,这个进程从它的父进程中继承环境块.
lpCurrentDirectory --> A pointer to the string that specifies the current drive and directory for the child process. NULL if you want the child process to inherit from the parent process.
LpCurrentDirectory -- > 指向一字符串的指针,这个字符串为子进程指定了当前设备和目录。值为空表示你想创建的子进程从它的父进程处继承了属性。
lpStartupInfo --> Points to a STARTUPINFO structure that specifies how the main window for the new process should appear. The STARTUPINFO structure contains many members that specifies the appearance of the main window of the child process. If you don't want anything special, you can fill the STARTUPINFO structure with the values from the parent process by calling GetStartupInfo function.
LpStartupInfo –〉 指向一个STARTUP INFO结构体的指针。这个结构体指定主窗口为了让新进程显示的方法。这个STARTUP INFO 结构包含很多成员。这些成员用于指定子进程的主窗口外观。如果你不想指定任何成员,你能通过调用GetStartupInfo函数来用父进程中的值填充这个结构。
lpProcessInformation --> Points to a PROCESS_INFORMATION structure that receives identification information about the new process. The PROCESS_INFORMATION structure has the following members:
lpProcessInformation – 〉指向一个PROCESS_INFORMATION 结构的指针.这个结构接收关于新进程的标识信息.这个结构有如下成员:
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
Process handle and process ID are two different things. A process ID is a unique identifier for the process in the system. A process handle is a value returned by Windows for use with other process-related API functions. A process handle cannot be used to identify a process since it's not unique.
进程句柄和进程ID是两个不同个概念.一个进程的ID是系统中这个进程的唯一标识.一个进程的句柄是通过调用windows中其它与进程有关的API函数后得到的一个返回值.一个进程的句柄不能用于标识一个进程因为它是不唯一的.
After the CreateProcess call, a new process is created and the CreateProcess call return immediately. You can check if the new process is still active by calling GetExitCodeProcess function which has the following syntax:
在CreateProcess调用之后,一个新的进程被创建而CreateProcess函数立即返回。你能调用GetExitCodeProcess函数来检查新进程是否一直处于活动状态,这个函数的句法如下:
GetExitCodeProcess proto hProcess:DWORD, lpExitCode:DWORD
If this call is successful, lpExitCode contains the termination status of the process in question. If the value in lpExitCode is equal to STILL_ACTIVE, then that process is still running.
如果这个调用是成功的,lpExitCode 包含进程终止态的可疑信息.如果在lpExitCode中的值为STILL_ACTIVE,表示这个进程正在运行.
You can forcibly terminate a process by calling TerminateProcess function. It has the following syntax:
你能通过调用TeminateProcess函数来强制的终止一个进程.它的句法如下:
TerminateProcess proto hProcess:DWORD, uExitCode:DWORD
You can specify the desired exit code for the process, any value you like. TerminateProcess is not a clean way to terminate a process since any dll attached to the process will not be notified that the process was terminated.
我们能为进程指定任何你想要的退出码.用TerminateProcess函数来结束进程是不干净的,因为任何与该进程相依恋的dll库文件都不会得到进程终止的通知.
Example:
The following example will create a new process when the user selects the "create process" menu item. It will attempt to execute "msgbox.exe". If the user wants to terminate the new process, he can select the "terminate process" menu item. The program will check first if the new process is already destroyed, if it is not, the program will call TerminateProcess function to destroy the new process.
当用户选定” create process”菜单的时候,下面这个例子创建一个新的进程,它将尝试执行‘msgbox.exe’文件。如果用户想终止这个新创建的进程,他能选择终止进程菜单项。程序首先检查新进程是否被销毁,如果它还存在,程序将调用TerminateProcess函数来销毁这个新进程。
.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
.const
IDM_CREATE_PROCESS equ 1
IDM_TERMINATE equ 2
IDM_EXIT equ 3
.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.
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
invoke GetMenu,hwnd
mov hMenu,eax
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL startInfo:STARTUPINFO
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_INITMENUPOPUP
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if eax==TRUE
.if ExitCode==STILL_ACTIVE
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_CREATE_PROCESS
.if processInfo.hProcess!=0
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
.endif
invoke GetStartupInfo,ADDR startInfo
invoke CreateProcess,ADDR programname,NULL,NULL,NULL,FALSE,\
NORMAL_PRIORITY_CLASS,\
NULL,NULL,ADDR startInfo,ADDR processInfo
invoke CloseHandle,processInfo.hThread
.elseif ax==IDM_TERMINATE
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if ExitCode==STILL_ACTIVE
invoke TerminateProcess,processInfo.hProcess,0
.endif
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
.else
invoke DestroyWindow,hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analysis:
分析:
The program creates the main window and retrieves the menu handle for future use. It then waits for the user to select a command from the menu. When the user selects "Process" menu item in the main menu, we process WM_INITMENUPOPUP message to modify the menu items inside the popup menu before it's displayed.
应用程序创建主窗口并且保存菜单句柄已备将来使用。然后它将等待用户选择菜单中的一个命令。当用户在主菜单中选择了”process” 菜单项时,在它( 弹出式菜单从下文中得知)被显示前,我们处理WM_INITMENUPOPUP消息来修改在弹出式菜单中的菜单项.(即让它变灰或者是正常)
.ELSEIF uMsg==WM_INITMENUPOPUP
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if eax==TRUE
.if ExitCode==STILL_ACTIVE
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
Why do we want to process this message? Because we want to prepare the menu items in the popup menu before the user can see them. In our example, if the new process is not started yet, we want to enable the "start process" and gray out the "terminate process" menu items. We do the reverse if the new process is already active.
为什么我们想处理这个消息? 因为我们想在用户能看到它们之前准备好弹出式菜单中的菜单项。(让菜单项呈现不同的外观,这样方便用户使用)在我们的例子中,如果新进程还没启动,我们让“start process “ 菜单项能用而让“terminate process “菜单项变灰。如果新进程已经是活动状态,我们做相反的工作。(即让strat process 菜单变灰而让terminate process能用)
We first check if the new process is still running by calling GetExitCodeProcess function with the process handle that was filled in by CreateProcess function. If GetExitCodeProcess returns FALSE, it means the process is not started yet so we gray out the "terminate process" menu item. If GetExitCodeProcess returns TRUE, we know that a new process has been started, but we have to check further if it is still running. So we compare the value in ExitCode to the value STILL_ACTIVE, if they're equal, the process is still running: we must gray out the "start process" menu item since we don't want to start several concurrent processes.
首先,我们 传递由CreateProcess函数返回的进程句柄 作为GetExitCodeProcess函数的参数并调用它来检查新进程是否是执行态。 如果GetExitCodeProcess返回值是FALSE,它意味着进程还没开始执行,(就绪态)所以我们让terminate process 菜单项变灰。如果GetExitCodeProcess函数返回值为TRUE,我们就知道这个新进程已经执行,但是还必须检查的远一点,看看它是否是持续执行态。所以我们用STILL_ACTIVE的值和在ExitCode中值作比较,看看它们是否相等.如果相等,进程是一直运行的: 这意味着我们必须让start process 菜单项变灰,因为我们不想启动多个并发执行的进程.
.if ax==IDM_CREATE_PROCESS
.if processInfo.hProcess!=0
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
.endif
invoke GetStartupInfo,ADDR startInfo
invoke CreateProcess,ADDR programname,NULL,NULL,NULL,FALSE,\
NORMAL_PRIORITY_CLASS,\
NULL,NULL,ADDR startInfo,ADDR processInfo
invoke CloseHandle,processInfo.hThread
When the user selects "start process" menu item, we first check if hProcess member of PROCESS_INFORMATION structure is already closed. If this is the first time, the value of hProcess will always be zero since we define PROCESS_INFORMATION structure in .data section. If the value of hProcess member is not 0, it means the child process has exited but we haven't closed its process handle yet. So this is the time to do it.
We call GetStartupInfo function to fill in the startupinfo structure that we will pass to CreateProcess function. After that we call CreateProcess function to start the new process. Note that I haven't checked the return value of CreateProcess because it will make the example more complex. In real life, you should check the return value of CreateProcess. Immediately after CreateProcess, we close the primary thread handle returned in processInfo structure. Closing the handle doesn't mean we terminate the thread, only that we don't want to use the handle to refer to the thread from our program. If we don't close it, it will cause a resource leak.
当用户选择了” start process” 菜单项时,我们首先检查PROCESS_INFORMATION结构体的hProcess成员是否已经关闭.如果这是第一次,hProcess 的值将总是0,因为我们在。Data节区中定义了PROCESS_INFORMATION结构体.如果hProcess的值不是0.它意味着有子进程存在而且我们还不能关闭它的进程句柄.所以在这时我们应该这样做.
我们调用GetStartupInfo函数将我们将传递给CreateProcess函数的成员变量填入到startupInfo结构体中.在这之后,我们调用CreateProcess函数来开始一个新进程.注意:我们不能检查CreateProcess函数的返回值,因为它将使我们的例子更复杂.在现实中,你应该检查CreateProcess的返回值.在调用CreateProcess函数之后,我们立即关闭在processInfo结构体中返回的主线程句柄.关闭了线程句柄并不意味着我们终止了线程,它仅意味着我们不想从应用程序中用句柄来引用这个线程.如果不关闭它,它将引起资源泄漏.
.elseif ax==IDM_TERMINATE
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if ExitCode==STILL_ACTIVE
invoke TerminateProcess,processInfo.hProcess,0
.endif
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
When the user selects "terminate process" menu item, we check if the new process is still active by calling GetExitCodeProcess function. If it's still active, we call TerminateProcess function to kill the process. Also we close the child process handle since we have no need for it anymore.
当用户选择了”terminate process “ 菜单项时,我们通过调用GetExitCodeProcess函数来检查新进程是否还处于运行态.如果它还是运行的,我们调用TerminateProcess函数来杀了这个进程。(终止它)我们同样关闭它所有的子进程句柄因为我们再也不需要它们了。
This article come from Iczelion's asm page
风向改变翻译于