http://www.cr173.com/html/11519_1.html CreateProcess详解
2012-05-05 13:20
267 查看
函数原型 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函数
CreateProcess函数用于创建进程:
BOOL CreateProcess(
PCTSTR pszApplicationName,
PTSTR pszCommandLine,
PSECURITY_ATTRIBUTES psaProcess,
PSECURITY_ATTRIBUTES psaThread,
BOOL bInheritHandles,
DWORD fdwCreate,
PVOID pvEnvironment,
PCTSTR pszCurDir,
PSTARTUPINFO psiStartInfo,
PPROCESS_INFORMATION ppiProcInfo);
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
TCHAR szCommandLine[] = TEXT("NOTEPAD");
CreateProcess(NULL, szCommandLine, NULL, NULL,
FALSE, 0, NULL, NULL, &si, &pi);
psaProcess, psaThread和bInheritHandles
创建新进程时,系统会创建一个进程内核对象和一个线程内核对象(用于进程的主线程),和其它内核对象一样,创建者(在这儿是父进程)必须指定其安全属性。psaProcess和psaThread分别指定了新进程的进程内核对象和线程内核对象的安全属性。将其设为NULL时,系统为对应的内核对象指定默认的安全属性。你可以创建SECURITY_ATTRIBUTES类型的变量,设置其中各个域的值然后将变量地址传递给psaProcess或 psaThread,以应用指定的安全属性。正如在第3章讨论内核对象时谈到的,当你想要控制新的进内核对象的句柄能否被父进程以后创建的子进程继承时,你应该设置SECURITY_ATTRIBUTES变量的bInheritHandle域的值。
下面的Inherit.cpp展示了内核对象句柄继承。假设执行该代码的进程为A,它调用CreateProcess创建了进程B,接着又创建了子进程C。注意调用CreateProcess时A使用的psaProcess、psaThread和bInheritHandles参数,代码注释很详细的描述了这些参数的作用:
/************************************************************
Module name: Inherit.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************/
#include <Windows.h>
int WINAPI _tWinMain (HINSTANCE hInstanceExe,
HINSTANCE,
PTSTR pszCmdLine,
int nCmdShow) {
// Prepare a STARTUPINFO structure for spawning processes.
STARTUPINFO si = { sizeof(si) };
SECURITY_ATTRIBUTES saProcess, saThread;
PROCESS_INFORMATION piProcessB, piProcessC;
TCHAR szPath[MAX_PATH];
// Prepare to spawn Process B from Process A.
// The handle identifying the new process
// object should be inheritable.
saProcess.nLength = sizeof(saProcess);
saProcess.lpSecurityDescriptor = NULL;
saProcess.bInheritHandle = TRUE;
// The handle identifying the new thread
// object should NOT be inheritable.
saThread.nLength = sizeof(saThread);
saThread.lpSecurityDescriptor = NULL;
saThread.bInheritHandle = FALSE;
// Spawn Process B.
_tcscpy_s(szPath, _countof(szPath), TEXT("ProcessB"));
CreateProcess(NULL, szPath, &saProcess, &saThread,
FALSE, 0, NULL, NULL, &si, &piProcessB);
// The pi structure contains two handles
// relative to Process A:
// hProcess, which identifies Process B's process
// object and is inheritable; and hThread, which identifies
// Process B's primary thread object and is NOT inheritable.
// Prepare to spawn Process C from Process A.
// Since NULL is passed for the psaProcess and psaThread
// parameters, the handles to Process C's process and
// primary thread objects default to "noninheritable."
// If Process A were to spawn another process, this new
// process would NOT inherit handles to Process C's process
// and thread objects.
// Because TRUE is passed for the bInheritHandles parameter,
// Process C will inherit the handle that identifies Process
// B's process object but will not inherit a handle to
// Process B's primary thread object.
_tcscpy_s(szPath, _countof(szPath), TEXT("ProcessC"));
CreateProcess(NULL, szPath, NULL, NULL,
TRUE, 0, NULL, NULL, &si, &piProcessC);
return(0);
}
STARTUPINFO si = { sizeof(si) };
CreateProcess(..., &si, ...);
lpAttributeList(属性链表)是_PROC_THREAD_ATTRIBUTE_LIST结构的链表,其中每个结构包含一个key/value对,目前,_PROC_THREAD_ATTRIBUTE_LIST中key的取值只能是下面两种:
PROC_THREAD_ATTRIBUTE_HANDLE_LIST:告诉 CreateProcess指定的句柄可被子进程继承,当然该句柄必须是可继承的(其继承标志位为1),且无需将CreateProcess的 bInheritHandles参数设置为TRUE。使用该标志可以指定子进程继承可继承句柄的子集而不是全部。这对于需要在不同的安全环境中创建子进程的进程而言非常重要,在这种情况下,由于安全原因,某些子进程可能不应该继承全部的可继承句柄。
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS:指定一个进程句柄,指定的进程(包括其可继承句柄、亲缘性、优先级等等)会替代调用CreateProcess的当前进程,成为子进程的父进程。如果当前进程在调用CreateProcess时指定了DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS,重新指定父进程并不影响原父进程调试过程,在子进程中发生的特定事件仍然会报告给原父进程。
属性链表的内容是不透明的,因此我们需要一些函数来创建空的属性链表。创建属性链表需要以下几个步骤,首先,为其分配存储空间,然后向其中添加键值对。函数InitializeProcThreadAttributeList用来创建新的属性链表并为其分配存储空间:
BOOL InitializeProcThreadAttributeList(
PPROC_THREAD_ATTRIBUTE_LIST pAttributeList,
DWORD dwAttributeCount,
DWORD dwFlags,
PSIZE_T pSize);
pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);
BOOL UpdateProcThreadAttribute(
PPROC_THREAD_ATTRIBUTE_LIST pAttributeList,
DWORD dwFlags,
DWORD_PTR Attribute,
PVOID pValue,
SIZE_T cbSize,
PVOID pPreviousValue,
PSIZE_T pReturnSize);
VOID DeleteProcThreadAttributeList(
PPROC_THREAD_ATTRIBUTE_LIST pAttributeList);
ppiProcInfo
ppiProcInfo 参数是PROCESS_INFORMATION结构的指针,调用CreateProcess时该结构必须由开发人员手动分配。CreateProcess 在返回前会填充ppiProcInfo指向的结构的内容。PROCESS_INFORMATION定义如下:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION; CreateProcess会创建一个进程内核对象和一个线程内核对象,创建初期,系统将其引用计数分别置为1。CreateProcess返回之前会获得这两个对象的访问权限,这样每个对象的引用计数会分别增加1,CreateProcess返回之后,两个对象的引用计数变成2。这意味着如果系统要释放CreateProcess进程/线程内核对象,相应的进程/线程必须终止,并且调用CreateProcess的线程必须调用 CloseHandle关闭相应的对象句柄,这样才能使得其引用计数变为0,系统方能释放。
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
CreateProcess函数
CreateProcess函数用于创建进程:
BOOL CreateProcess(
PCTSTR pszApplicationName,
PTSTR pszCommandLine,
PSECURITY_ATTRIBUTES psaProcess,
PSECURITY_ATTRIBUTES psaThread,
BOOL bInheritHandles,
DWORD fdwCreate,
PVOID pvEnvironment,
PCTSTR pszCurDir,
PSTARTUPINFO psiStartInfo,
PPROCESS_INFORMATION ppiProcInfo);
STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; CreateProcess(NULL, TEXT("NOTEPAD"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; CreateProcess(NULL, TEXT("NOTEPAD"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);解决这个问题的方法很简单,将命令行字符串复制到临时缓冲区既可,如下所示:
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
TCHAR szCommandLine[] = TEXT("NOTEPAD");
CreateProcess(NULL, szCommandLine, NULL, NULL,
FALSE, 0, NULL, NULL, &si, &pi);
// Make sure that the path is in a read/write section of memory. TCHAR szPath[] = TEXT("WORDPAD README.TXT"); // Spawn the new process. CreateProcess(TEXT("C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE"),szPath,...); // Make sure that the path is in a read/write section of memory. TCHAR szPath[] = TEXT("WORDPAD README.TXT"); // Spawn the new process. CreateProcess(TEXT("C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE"),szPath,...);执行上面代码时,系统会打开notepad.exe(记事本),但它的命令行却是WORDPAD README.TXT(WORDPAD是写字板),这看上去非常奇怪,但CreateProcess就是这样工作的。这种 pszApplicationName提供的特性被用来支持Windows的POSIX子系统。
psaProcess, psaThread和bInheritHandles
创建新进程时,系统会创建一个进程内核对象和一个线程内核对象(用于进程的主线程),和其它内核对象一样,创建者(在这儿是父进程)必须指定其安全属性。psaProcess和psaThread分别指定了新进程的进程内核对象和线程内核对象的安全属性。将其设为NULL时,系统为对应的内核对象指定默认的安全属性。你可以创建SECURITY_ATTRIBUTES类型的变量,设置其中各个域的值然后将变量地址传递给psaProcess或 psaThread,以应用指定的安全属性。正如在第3章讨论内核对象时谈到的,当你想要控制新的进内核对象的句柄能否被父进程以后创建的子进程继承时,你应该设置SECURITY_ATTRIBUTES变量的bInheritHandle域的值。
下面的Inherit.cpp展示了内核对象句柄继承。假设执行该代码的进程为A,它调用CreateProcess创建了进程B,接着又创建了子进程C。注意调用CreateProcess时A使用的psaProcess、psaThread和bInheritHandles参数,代码注释很详细的描述了这些参数的作用:
/************************************************************
Module name: Inherit.cpp
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************/
#include <Windows.h>
int WINAPI _tWinMain (HINSTANCE hInstanceExe,
HINSTANCE,
PTSTR pszCmdLine,
int nCmdShow) {
// Prepare a STARTUPINFO structure for spawning processes.
STARTUPINFO si = { sizeof(si) };
SECURITY_ATTRIBUTES saProcess, saThread;
PROCESS_INFORMATION piProcessB, piProcessC;
TCHAR szPath[MAX_PATH];
// Prepare to spawn Process B from Process A.
// The handle identifying the new process
// object should be inheritable.
saProcess.nLength = sizeof(saProcess);
saProcess.lpSecurityDescriptor = NULL;
saProcess.bInheritHandle = TRUE;
// The handle identifying the new thread
// object should NOT be inheritable.
saThread.nLength = sizeof(saThread);
saThread.lpSecurityDescriptor = NULL;
saThread.bInheritHandle = FALSE;
// Spawn Process B.
_tcscpy_s(szPath, _countof(szPath), TEXT("ProcessB"));
CreateProcess(NULL, szPath, &saProcess, &saThread,
FALSE, 0, NULL, NULL, &si, &piProcessB);
// The pi structure contains two handles
// relative to Process A:
// hProcess, which identifies Process B's process
// object and is inheritable; and hThread, which identifies
// Process B's primary thread object and is NOT inheritable.
// Prepare to spawn Process C from Process A.
// Since NULL is passed for the psaProcess and psaThread
// parameters, the handles to Process C's process and
// primary thread objects default to "noninheritable."
// If Process A were to spawn another process, this new
// process would NOT inherit handles to Process C's process
// and thread objects.
// Because TRUE is passed for the bInheritHandles parameter,
// Process C will inherit the handle that identifies Process
// B's process object but will not inherit a handle to
// Process B's primary thread object.
_tcscpy_s(szPath, _countof(szPath), TEXT("ProcessC"));
CreateProcess(NULL, szPath, NULL, NULL,
TRUE, 0, NULL, NULL, &si, &piProcessC);
return(0);
}
typedef struct _STARTUPINFO { DWORD cb; PSTR lpReserved; PSTR lpDesktop; PSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; PBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO; typedef struct _STARTUPINFOEX { STARTUPINFO StartupInfo; struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList; } STARTUPINFOEX, *LPSTARTUPINFOEX; typedef struct _STARTUPINFO { DWORD cb; PSTR lpReserved; PSTR lpDesktop; PSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; PBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO; typedef struct _STARTUPINFOEX { STARTUPINFO StartupInfo; struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList; } STARTUPINFOEX, *LPSTARTUPINFOEX;Windows创建新进程时会使用STARTUPINFO(EX)的成员变量,大多数情况下可以使用这些变量的默认值,此时你应该该将其cb域设置为结构的大小,并将其余域清0,如下:
STARTUPINFO si = { sizeof(si) };
CreateProcess(..., &si, ...);
typedef struct _STARTUPINFOEXA { STARTUPINFOA StartupInfo; struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList; } STARTUPINFOEXA, *LPSTARTUPINFOEXA; typedef struct _STARTUPINFOEXW { STARTUPINFOW StartupInfo; struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList; } STARTUPINFOEXW, *LPSTARTUPINFOEXW; typedef struct _STARTUPINFOEXA { STARTUPINFOA StartupInfo; struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList; } STARTUPINFOEXA, *LPSTARTUPINFOEXA; typedef struct _STARTUPINFOEXW { STARTUPINFOW StartupInfo; struct _PROC_THREAD_ATTRIBUTE_LIST *lpAttributeList; } STARTUPINFOEXW, *LPSTARTUPINFOEXW;
lpAttributeList(属性链表)是_PROC_THREAD_ATTRIBUTE_LIST结构的链表,其中每个结构包含一个key/value对,目前,_PROC_THREAD_ATTRIBUTE_LIST中key的取值只能是下面两种:
PROC_THREAD_ATTRIBUTE_HANDLE_LIST:告诉 CreateProcess指定的句柄可被子进程继承,当然该句柄必须是可继承的(其继承标志位为1),且无需将CreateProcess的 bInheritHandles参数设置为TRUE。使用该标志可以指定子进程继承可继承句柄的子集而不是全部。这对于需要在不同的安全环境中创建子进程的进程而言非常重要,在这种情况下,由于安全原因,某些子进程可能不应该继承全部的可继承句柄。
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS:指定一个进程句柄,指定的进程(包括其可继承句柄、亲缘性、优先级等等)会替代调用CreateProcess的当前进程,成为子进程的父进程。如果当前进程在调用CreateProcess时指定了DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS,重新指定父进程并不影响原父进程调试过程,在子进程中发生的特定事件仍然会报告给原父进程。
属性链表的内容是不透明的,因此我们需要一些函数来创建空的属性链表。创建属性链表需要以下几个步骤,首先,为其分配存储空间,然后向其中添加键值对。函数InitializeProcThreadAttributeList用来创建新的属性链表并为其分配存储空间:
BOOL InitializeProcThreadAttributeList(
PPROC_THREAD_ATTRIBUTE_LIST pAttributeList,
DWORD dwAttributeCount,
DWORD dwFlags,
PSIZE_T pSize);
SIZE_T cbAttributeListSize = 0; BOOL bReturn = InitializeProcThreadAttributeList( NULL, 1, 0, &cbAttributeListSize); // bReturn is FALSE but GetLastError() returns ERROR_INSUFFICIENT_BUFFER SIZE_T cbAttributeListSize = 0; BOOL bReturn = InitializeProcThreadAttributeList( NULL, 1, 0, &cbAttributeListSize); // bReturn is FALSE but GetLastError() returns ERROR_INSUFFICIENT_BUFFERcbAttributeListSize 返回创建属性链表所需的内存大小,该大小与dwAttributeCount参数相关,dwAttributeCount指定了属性链表中的 key/value对的数目。接下来你可以用cbAttributeListSize为属性链表分配空间:
pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);
bReturn = InitializeProcThreadAttributeList( pAttributeList, 1, 0, &cbAttributeListSize); bReturn = InitializeProcThreadAttributeList( pAttributeList, 1, 0, &cbAttributeListSize);当属性链表初始化完成后,就可以调用UpdateProcThreadAttribute向其添加键/值对了:
BOOL UpdateProcThreadAttribute(
PPROC_THREAD_ATTRIBUTE_LIST pAttributeList,
DWORD dwFlags,
DWORD_PTR Attribute,
PVOID pValue,
SIZE_T cbSize,
PVOID pPreviousValue,
PSIZE_T pReturnSize);
STARTUPINFOEX esi = { sizeof(STARTUPINFOEX) }; esi.lpAttributeList = pAttributeList; bReturn = CreateProcess( ..., EXTENDED_STARTUPINFO_PRESENT, ... &esi.StartupInfo, ...); STARTUPINFOEX esi = { sizeof(STARTUPINFOEX) }; esi.lpAttributeList = pAttributeList; bReturn = CreateProcess( ..., EXTENDED_STARTUPINFO_PRESENT, ... &esi.StartupInfo, ...);其中pAttributeList是按前面的方法创建的属性列表。当你不再需要该属性列表时,应该调用下面的方法回收为其分配的内存:
VOID DeleteProcThreadAttributeList(
PPROC_THREAD_ATTRIBUTE_LIST pAttributeList);
VOID GetStartupInfo(LPSTARTUPINFO pStartupInfo); VOID GetStartupInfo(LPSTARTUPINFO pStartupInfo);注意,无论父进程在调用CreateProcess时参数pStartupInfo指向STARTUPINFO还是STARTUPINFOEX结构,GetStartupInfo总是返回STARTUPINFO结构的拷贝。
ppiProcInfo
ppiProcInfo 参数是PROCESS_INFORMATION结构的指针,调用CreateProcess时该结构必须由开发人员手动分配。CreateProcess 在返回前会填充ppiProcInfo指向的结构的内容。PROCESS_INFORMATION定义如下:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION; CreateProcess会创建一个进程内核对象和一个线程内核对象,创建初期,系统将其引用计数分别置为1。CreateProcess返回之前会获得这两个对象的访问权限,这样每个对象的引用计数会分别增加1,CreateProcess返回之后,两个对象的引用计数变成2。这意味着如果系统要释放CreateProcess进程/线程内核对象,相应的进程/线程必须终止,并且调用CreateProcess的线程必须调用 CloseHandle关闭相应的对象句柄,这样才能使得其引用计数变为0,系统方能释放。
相关文章推荐
- SQLite内存数据库(转自:http://www.cr173.com/html/6965_1.html)
- Java synchronized详解 http://www.cnblogs.com/devinzhang/archive/2011/12/14/2287675.html
- JQuery上传插件Uploadify使用详解 http://www.cnblogs.com/oec2003/archive/2010/01/06/1640027.html
- Linux tcpdump命令详解 http://www.cnblogs.com/ggjucheng/archive/2012/01/14/2322659.html
- java反射详解 (转至 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html)
- 结构体对齐详解http://www.cnblogs.com/motadou/archive/2009/01/17/1558438.html
- RabbitMQ服务端配置详解(转自:http://www.cnblogs.com/zhen-rh/p/6884297.html)
- Uiautomator ——API详解(转载http://www.cnblogs.com/by-dream/p/4921701.html)
- java反射详解-原文地址:http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
- MySQL用户授权 和 bin-log日志 详解和实战(http://www.cnblogs.com/it-cen/p/5234345.html)
- 转载TortoiseSVN的使用详解1(http://www.cnblogs.com/xiaobaihome/archive/2012/03/20/2407610.html)
- 模版详解(模版与宏) 转自:http://www.cppblog.com/zmllegtui/archive/2008/10/28/65316.html
- http详解(http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html)
- Android中Activity启动模式详解(原文:http://www.cnblogs.com/fanchangfa/archive/2012/08/25/2657012.html)
- 转载TortoiseSVN的使用详解1(http://www.cnblogs.com/xiaobaihome/archive/2012/03/20/2407610.html)
- Android LayoutInflater详解 (转载:http://www.cnblogs.com/top5/archive/2012/05/04/2482328.html)
- STL之list容器详解 http://www.cnblogs.com/scandy-yuan/archive/2013/01/08/2851324.html
- 常用Oracle分析函数详解 [http://www.cnblogs.com/benio/archive/2011/06/01/2066106.html]
- Window 消息大全使用详解http://www.itepub.net/html/kaifajingcui/C___VC/2006/0515/38024.html
- IIS上启用Gzip压缩(HTTP压缩) 详解(转自:http://www.jzxue.com/fuwuqi/http-iis-apache/200905/17-2128.html)