Drecik学习经验分享
转载请注明出处:http://blog.csdn.net/drecik__/article/details/8082749
创建进程
创建进程的函数是CreateProcess,原型如下:
BOOL CreateProcesW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
接下来详细介绍各个参数:
-
lpApplicationName和lpCommandLine参数分别制定新进程要使用的可执行文件名的名称和传递给新进程的命令行字符串,其中注意lpCommandLine为LPWSTR类型所以不能直接传给常量字符串,在调用函数之前把常量字符串复制到一个临时区,在进行传入
CreateProcess会解析命令行,检查第一个标记,并假定此为想运行的可执行文件名称,如果没扩展名则默认为.exe,并按如下顺序搜索该文件:
a) 主调进程.exe所在目录
b) 主调进程的当前目录
c) Windows系统目录,即System32子文件夹
d) Windows目录
e) PATH环境变量列出的目录
如果文件名包含一个完整路径,就会在这路径中查找文件,而不搜索目录。
当lpApplicationName为NULL(99%以上情况都是如此)时进行上述操作,当第一个参数不为NULL,传入可执行文件的名称时(必须加后缀),CreateProcess只会在当前目录查找,除非文件名前有路径。
即使在lpApplicationName中指定了文件名,但是函数还是会将lpCommandLine作为命令行传给新进程
-
lpProcessAttributes, lpThreadAttributes,bInheritHandles参数
为了创建一个新进程,系统必须创建一个进程内核对象和一个线程内核对象,所以就必须设定这两个内核对象的安全属性,当然可以传入NULL来使用默认安全属性
bInheritHandles表示被创建的进程是否继承该进程的所有可以继承的内核对象
-
dwCreationFlags标示了影响新进程创建方式的标志,多个标志可以按位或起来
DEBUG_PROCESS标志向系统表面父进程希望调试子进程以及子进程将来生成的所有进程。在任何一个子进程发送特定事件将要通知父进程。
DEBUG_ONLY_THIS_PROCESS标志只有该创建的进程被调试
CREATE_SUSPENDED标志让系统在创建新进程的同时挂起其主线程。这样一来,父进程可以对创建的进程和线程进行修改,之后可以调用ResumeThread来唤醒线程。
DETACHED_PROCESS对于控制台进程,新进程没有访问父进程控制台的权限。新进程可以通过AllocConsole函数自己创建一个新的控制台。
CREATE_NEW_CONSOLE标志系统为新进程创建一个新的控制台窗口,不能和DETACHED_PROCESS同用。
CREATE_NO_WINDOW标志系统不要为应用程序创建人和控制台窗口。
CREATE_NEW_PROCESS_GROUP标志创建一个进程组,当组中一个进程处于活动状态并且按下Ctrl+C或Ctrl+Break就会通知组中所有进程。
CREATE_DEFAULT_ERROR_MODE标志新进程不会继承父进程的错误模式。
CREATE_SEPARATE_WOW_VDM(只适用于Windows NT)这个标志只有当运行一个16位的Windows应用程序时才是有效的。如果被设置,新进程将会在一个私有的虚拟DOS机(VDM)中运行。
CREATE_SHARED_WOW_VDM(只适用于Windows NT)这个标志只有当运行一个16位的Windows应用程序时才是有效的。如果WIN.INI中的Windows段的DefaultSeparateVDM选项被设置为真,这个标识使得CreateProcess函数越过这个选项并在共享的虚拟DOS机中运行新进程。
CREATE_UNICODE_ENVIRONMENT标志系统子进程的环境块应包含Unicode字符。
CREATE_FORCEDOS标志强制系统运行一个嵌入在16位OS/2应用程序中的MS-DOS应用程序。
CREATE_BREAKAWAY_FROM_JOB标志允许一个作业中的进程生成一个和作业无关的进程。
EXTENDED_STARTUPINFO_PRESENT标志向系统表明传给lpStartupInfo是一个STARTUPINFOEX结构。
该参数还可以指定进程的优先级类,但不应该这样做
-
lpEnvironment参数指向一块内存,包含新进程要使用的环境字符串。大多数传入NULL,表示子进程将使用父进程的环境字符串
- lpCurrentDirectory允许父进程设置子进程的当前驱动器和目录。如果为NULL,则新进程的工作目录与生成新进程的应用程序一样,否则则将该字符串作为工作目录,注意必须包含驱动器号。
-
lpStartupInfo是一个STARTUPINFO或STARTUPINFOEX结构,其中STARTUPINFO结构如下:
typedef struct _STARTUPINFOW { DWORD cb; // 设置为当前结构的大小; LPWSTR lpReserved; // 保留,必须为NULL; LPWSTR lpDesktop; // 标明在哪个桌面上启动,通常为NULL; LPWSTR lpTitle; // 设置控制台的窗口标题,为NULL则使用可执行文件名为标题; DWORD dwX; DWORD dwY; // 窗口位置,窗口程序只有设CW_USERDEFAULT为位置才有效; DWORD dwXSize; DWORD dwYSize;// 窗口大小,窗口程序只有设CW_USERDEFAULT为大小才有效; DWORD dwXCountChars; DWORD dwYCountChars; // 控制台窗口的宽度和高度,以字符数计算; DWORD dwFillAttribute;// 控制套窗口的文本和背景色; DWORD dwFlags; // 定义哪些结构有效; WORD wShowWindow; // 窗口程序的主窗口如何显示; WORD cbReserved2; // 保留,为NULL; LPBYTE lpReserved2; // 保留,为NULL; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; // 指定控制台输入输出缓冲区的句柄; } STARTUPINFOW, *LPSTARTUPINFOW;
dwFlags标志如下:
STARTF_USESIZE:使用dwXSize和dwYSize成员
STARTF_USESHOWWINDOW:使用wShowWindows成员
STARTF_USEPOSTION:使用dwX和dwY成员
STARTF_USECOUNTCHARS:使用dwXCountChars和dwYCountChars成员
STARTF_USEFILLATTRIBUTE:使用dwFillAttribute成员
STARTF_USESTDHANDLES:使用hStdInput, hStdOutput, hStdError成员
STARTF_RNFULLSCREEN:是控制台程序全屏
STARTF_FORCEONFEEDBACK:在创建进程时候将鼠标设置为等待样式
STARTF_FORCEOFFFEEDBACK:将不会更改鼠标的样式接下来介绍STARTUPINFOEX,结构如下:
typedefstruct _STARTUPINFOEXW { STARTUPINFOW StartupInfo; LPPROC_THREAD_ATTRIBUTE_LISTlpAttributeList; // 添加格外的属性; } STARTUPINFOEXW,*LPSTARTUPINFOEXW;
第二个参数是一系列的键值对,每个属性都有一对键值对,目前只有两个属性:
PROC_THREAD_ATTRIBUTE_HANDLE:告诉CreateProcess究竟应该继承哪些内核对象的句柄。
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS:期望的值是一个句柄,指定该句柄为创建进程的符进程,但不会改变调试进程的关系
由于属性表不透明,所以创建时需调用InitializeProcThreadAttributeList两次,第一次获得大小
BOOLInitializeProcThreadAttributeList( LPPROC_THREAD_ATTRIBUTE_LISTlpAttributeList, DWORD dwAttributeCount, // 属性表个数; DWORD dwFlags, // 始终为0; PSIZE_T lpSize // 内存块的大小; );
在使用UpdateProcThreadAttribute来添加键值对
BOOL UpdateProcThreadAttribute( LPPROC_THREAD_ATTRIBUTE_LISTlpAttributeList, DWORDdwFlags, // 始终为0; DWORD_PTRAttribute,// 接收属性标志; PVOIDlpValue, // 根据属性标志为一个句柄或一个句柄数组; SIZE_TcbSize, // 为sizeof(HANDLE)或sizeof(HANDLE)*句柄数; PVOIDlpPreviousValue, // 始终为NULL; PSIZE_TlpReturnSize // 始终为NULL; );
不使用的时候调用DeleteProcThreadAttributeList来清除,应用程序可以调用GetStartipInfo来获得当前STARTUPINFO的结构体,但不能获得STARTUPINFOEX结构体
-
lpProcessInformation用来返回生成的进程信息,结构如下
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
不需要使用时要调用CloseHandle来释放资源,最好不要利用进程或线程的ID来跟踪进程或线程,因为ID会被立即重用,所以可能跟踪到另外的进程或线程,使用句柄来跟踪可以避免这个问题
获得ID的函数:
GetCurrentProcessId和GetCurrentThreadId:获得当前进程或线程的ID
GetProcessId和GetThreadId:传入进程或线程句柄获得其ID
GetProcessIdOfThread:获得当前线程所在进程的ID