您的位置:首页 > 编程语言

Windows编程-进程的创建

2013-06-08 14:44 197 查看
1. 进程(Process)和线程(Thread)
(1)进程
进程是一个正在运行的程序,它拥有自己的虚拟内存地址空间,拥有自己的代码、数据和其他系统资源,如进程创建的文件、管道、同步对象等。一个进程也包含了一个或者多个运行在此进程内的线程。
简单来说,磁盘上的可执行文件被载入内存执行之后,就变成“进程”了。
(2)线程
线程是进程内执行代码的独立实体。没有它,进程中的程序代码是不可能执行的。操作系统创建进程后,会创建一个线程执行进程中的代码,这个线程称作主线程,有主线程创建的线程成为辅助线程。

2.应用程序的启动过程
应用程序的启动过程就是进程的创建过程,操作系统是通过调用CreateProcess函数来创建新的进程的。当一个线程调用CreateProcess函数的时候,系统会创建一个进程内核对象。此进程内核对象不是进程本身,仅仅是一个系统用来管理这个进程的小的数据结构。系统然后会为新的进程创建一个虚拟地址空间,加载应用程序运行时所需要的代码和数据。
系统紧接着会为新进程创建一个主线程,这个主线程通过执行C/C++运行期启动代码开始运行,C/C++运行期启动代码又会调用main函数。

3. 进程创建
进程A可以通过CreateProcess函数创建一个新的进程B, A被称为父进程,B被称为子进程。系统在创建新的进程时会为新的进程指定一个STARTUPINFO类型的变量,这个结构包含了父进程传递给子进程的一些现实信息。对图形界面应用程序来说,这些信息将影响新的进程中主线程的窗口显示;对控制台应用程序来说,如果有一个新的控制台窗口被创建的话,这些信息将影响这个控制台窗口。typedef struct _STARTUPINFOW {
DWORD cb; // 本结构的长度,总是应该被设为sizeof(STARTUPINFO)
LPWSTR lpReserved; // 保留字段,程序不使用这个参数
LPWSTR lpDesktop; // 指定桌面名称
LPWSTR lpTitle; // 控制台应用程序使用,指定控制台窗口标题
DWORD dwX; // 指定新创建窗口的为位置坐标(dwX,dwY)和大小信息
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars; // 控制台程序使用,指定控制台窗口的行数
DWORD dwYCountChars;
DWORD dwFillAttribute; // 控制台程序使用,指定控制台窗口的背景色
DWORD dwFlags; // 标志,它的值决定了STARTUPINFO结构中哪些成员的值是有效的
WORD wShowWindow; // 窗口的显示方式
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput; // 控制台程序使用,几个标准句柄
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOW, *LPSTARTUPINFOW;

一个进程可以调用GetStartupInfo函数来得到父进程创建自己时使用的STARTUPINFO 结构。 事实上,Windows系统就是通过调用这个函数来取得当前进程的创建信息,以便对新进程中主窗口的属性设置默认值。

void GetStartupInfo(LPSTARTUPINFO lpStartupInfo); // 取得当前进程被创建时指定的STARTUPINFO结构


定义一个STARTUPINFO结构的对象以后,总要在使用此对象之前将对象的cb成员初始化为STARTUPINFO结构的大小,如下所示:

STARTUPINFO si = {sizeof(si)};  //将cb成员初始化为sizeof(si), 其他成员初始化为0

CreateProcess函数
#ifdef UNICODE
#define CreateProcess  CreateProcessW
#else
#define CreateProcess  CreateProcessA
#endif // !UNICODE

WINBASEAPI
BOOL
WINAPI
CreateProcessA(
LPCSTR lpApplicationName,					// 可执行文件的名称
LPSTR lpCommandLine,					// 指定了要传递给执行模块的参数
LPSECURITY_ATTRIBUTES lpProcessAttributes,			// 进程安全性,值为NULL的话表示使用默认的安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes,			// 进程安全性,值为NULL的话表示使用默认的安全属性
BOOL bInheritHandles,					// 指定了当前进程中的可继承句柄是否可被新进程继承
DWORD dwCreationFlags,					// 指定了新进程的优先级以及其他创建标志
LPVOID lpEnvironment,					// 制定新进程使用的环境变量
LPCSTR lpCurrentDirectory,					// 新进程使用的当前目录
LPSTARTUPINFOA lpStartupInfo,				// 指定新进程中主窗口的位置、大小和标准句柄等
LPPROCESS_INFORMATION lpProcessInformation			// [OUT]返回新建进程的标识信息,如ID、句柄等
);
WINBASEAPI
BOOL
WINAPI
CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

关于CreateProcess函数下次讨论,这里先了解基本信息。

lpApplication 和 lpCommandLine 参数指定了新的进程将要使用的可执行文件的名称和传递给新进程的参数。

dwCreationFlags参数指定的标志会影响新的进程如何创建。

lpStartupInfo参数是一个指向STARTUPINFO结构的指针。

lpProcessInformation参数是一个指向PROCESS_INFORMATION结构的指针。CreateProcess函数在返回之前会初始化此结构的成员。

typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;		// 新建进程的内核句柄
HANDLE hThread;		// 新建进程中主线程的内核句柄
DWORD dwProcessId;		// 新建进程的ID
DWORD dwThreadId;		// 新建进程的主线程ID
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;


创建一个新的进程将促使系统创建一个进程内核对象和一个线程内核对象。在创建它们的时候,系统将每个对象的使用计数初始化为1。然后,在CreateProcess返回之前,这个函数打开此进程内核对象和线程内核对象的句柄,并将它们的值传递给上述结构的 hProcess和 hThread成员。 CreateProcess在内部打开这些对象的时候,对象的使用计数会增加到2。 因此,父进程中必须有一个线程调用CloseHandle 关闭CreateProcess函数返回的两个内核对象的句柄,否则即便是子进程已经终止了,该进程的进程内核对象和主线程的内核对象仍然没有释放。

当一个进程/线程内核对象创建以后,系统会为这个内核对象分派一个唯一的ID号。

4. 进程创建示例

#include <stdio.h>
#include <windows.h>

int main()
{
char szCommandLine[] = "cmd";
STARTUPINFO si = {sizeof(si)};		//将cb成员初始化为sizeof(si), 其他成员初始化为0
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESHOWWINDOW;	// 指定wShowWindow成员有效
si.wShowWindow = TRUE;			// 此成员设为TRUE的话则显示新建进程的主窗口
// 为FALSE的话则不显示

BOOL bRet = ::CreateProcess(NULL,	// 不在此指定可执行文件的文件名
szCommandLine,		// 命令行参数
NULL,			// 默认进程安全性
NULL,			// 默认线程安全性
FALSE,			// 指定当前进程内的句柄不可以被子进程继承
CREATE_NEW_CONSOLE,	// 为新进程创建一个新的控制台窗口
NULL,			// 使用本进程的环境变量
NULL,			// 使用本进程的驱动器和目录
&si,
&pi);
if(bRet)
{
//既然不使用两个句柄,最好立刻将他们关闭
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
printf(" 新进程的进程ID号:%d \n", pi.dwProcessId);
printf(" 新进程的主线程ID号:%d \n", pi.dwThreadId);
}
return 0;
}


该程序打开了Windows自带的命令行程序cmd.exe。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息