【并发编程二】c++创建子进程CreateProcess()
一、创建子进程
二、demo
三、构建、编译、运行
四、相关知识介绍
1、CreateProcess 参数介绍
1.1、lpApplicationName
1.2、lpCommandLine
1.3、lpProcessAttributes
1.4、lpThreadAttributes
1.5、bInheritHandles
1.6、dwCreationFlags
1.7、lpEnvironment
1.8、lpCurrentDirectory
1.9、lpStartupInfo
1.10、lpProcessInformation
2、int main(int argc, char*argv[])
五、进程相关的API
一、创建子进程
windows系统
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 // 新进程返回的信息
);
Linux系统popen()会调用fork()产生子进程
FILE * popen( const char * command,const char * type);
二、demo
1、父进程
main.cpp
#include
#include
using namespace std;
int main(int argc, char*argv[])
{
cout << "i am father process" << endl;
STARTUPINFO si = { sizeof(STARTUPINFO) };//在产生子进程时,子进程的窗口相关信息
PROCESS_INFORMATION pi; //子进程的ID/线程相关信息
DWORD returnCode;//用于保存子程进的返回值;
// char commandLine[] = "C:\\Users\\jx\\Desktop\\test01\\lib\\Debug\\childprocess.exe -l"; //测试命令行参数一
char commandLine[] = "childprocess.exe -l a b c"; //测试命令行参数一
BOOL bRet = CreateProcess( //调用失败,返回0;调用成功返回非0;
NULL, //一般都是空;(另一种批处理情况:此参数指定"cmd.exe",下一个命令行参数 "/c otherBatFile")
commandLine, //命令行参数
NULL, //_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL, //_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
FALSE, //_In_ BOOL bInheritHandles,
CREATE_NEW_CONSOLE, //新的进程使用新的窗口。
NULL, //_In_opt_ LPVOID lpEnvironment,
NULL, //_In_opt_ LPCTSTR lpCurrentDirectory,
&si, //_In_ LPSTARTUPINFO lpStartupInfo,
&pi); //_Out_ LPPROCESS_INFORMATION lpProcessInformation
if (0 != bRet)
{
std::cout << "Create Child Process sucess!" << std::endl;
//等待子进程结束
WaitForSingleObject(pi.hProcess, -1);
std::cout << "Child Process is finished" << std::endl;
//获取子进程的返回值
GetExitCodeProcess(pi.hProcess, &returnCode);
std::cout << "Child Process return code:" << returnCode << std::endl;
}
else
{
std::cout << "Create child Process error!"< return 0; } system("pause"); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; } CmakeLists.txt CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0) PROJECT(process) ADD_EXECUTABLE(process main.cpp) ADD_SUBDIRECTORY(childprocess) SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib") 2、子进程 main.cpp #include using namespace std; int main(int argc,char* argv[]) { std::cout << "i am a child process." << std::endl; if (argc>= 2) { int paramID = 0; while (paramID < argc-1) { cout << argv[paramID] << endl; paramID++; } std::cout << "sucess:the return code will be 0" << std::endl; system("pause"); return 0; } else { std::cout << "failed:the return code will be -1" << std::endl; system("pause"); return -1; } } CmakeLists.txt CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0) SET(TARGET "childprocess") ADD_EXECUTABLE(childprocess main.cpp) SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib") SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib") 三、构建、编译、运行 1、构建 cmake -B build 2、编译 cmake --build build 3、运行 父进程输出 子进程输出 四、相关知识介绍 1、CreateProcess 参数介绍 CreatProcess()需要若干参数来指定新进程的运行方式,但实际使用中多半参数都是用不到的,可以设置为NULL 1.1、lpApplicationName 该参数的类型是LPCTSTR,其实就是一个const char*的传统C字符串, 实际使用中常常把lpApplicationName设置为Null,lpCommandLine参数来指定可执行文件名 1.2、lpCommandLine 该参数的类型是LPTSTR,即一个以’0’结尾的char*的C传统字符串 这个参数用于指定传递给新进程的命令行参数,更多时候我们把可执行文件名也包含在内(这意味着lpApplicationName应该设置为Null) 如果lpApplicationName不为Null,那lpCommandLine就会原封不动的作为命令行参数传递,所以当指定了lpApplicationNAme后就不应该再在lpCommandLine中写可执行文件名了 1.3、lpProcessAttributes 进程对象设置安全性,即返回的新进程对象句柄能否被子进程继承。 一般传递NULL,让windows把进程内核对象设为默认安全性(默认不能被继承) 1.4、lpThreadAttributes 进程对象设置安全性, 一般也传递NULL设置为默认安全性,也就是句柄不能被继承, 1.5、bInheritHandles 这个同样是关于安全性的标识符,是个BOOL型变量,用于控制新进程是否可以从调用进程处继承所有可继承的句柄, 1.6、dwCreationFlags 这是个DWORD的标识,用于设置新进程创建的方式 CREATE_NEW_CONSOLE //要求系统为新进程创建一个新的控制台窗口(否则会和原进程共用一个控制台窗口)CREATE_NO_WINDOW //不为新进程创建窗口,可以用这个来创建一个没有窗口的应用程序DEBUG_PROCESS //将新进程作为被调试程序而原进程被当做调试器,新进程和其创建的其他进程中发生的特定事件都会被通知原进程CREATE_UNICODE_ENVIRONMENT //告诉系统新进程的环境块包含Unicode字符(默认为ANSI) 1.7、lpEnvironment 这个参数指向一块包含新进程要使用的环境字符串的内存,通常传入NULL表示继承原进程的环境字符串, 1.8、lpCurrentDirectory 该参数用于设置新进程的当前驱动器和目录,必须是一个以’0’结尾的绝对路径字符串,如果用不到特殊设置可以直接传递NULL表示新进程的工作目录和创建新进程的原进程工作目录一致,一般就用NULL。 1.9、lpStartupInfo 这个参数指向一个STARTUPINFO或STARTUPINFOEX的结构,一般应用程序会期待仅使用默认值,所以大可以全部置零 1.10、lpProcessInformation 输出参数,指向一个 PROCESS_INFORMATION 结构,返回被创建进程的信息。 typedef struct _PROCESS_INFORMATION {HANDLE hProcess;HANDLE hThread;DWORD dwProcessId;DWORD dwThreadId;} PROCESS_INFORMATION, *PPROCESS_INFORMATION,*LPPROCESS_INFORMATION; 分别是进程句柄、线程句柄、进程ID和线程ID 每创建一个新进程都会产生一个进程内核对象和一个线程内核对象,前两个参数就是对应的进程对象的句柄和主线程对象的句柄,而每一个进程和线程windows都会分配一个独一无二的标识符,后两个参数就对应创建的进程的ID和进程的主线程的ID 通常情况下我们会忽略进程ID和线程ID,一般使用相应的句柄进行跟踪控制,如果一定要使用ID的话应该注意一件事:系统中ID是可重用的,这意味着当一个进程或线程终止后同一个ID会分配给其他的新进程或线程,应当及时更新ID,避免出错. 一个进程可以有多个进程句柄,但是只有一个进程id。 进程句柄和现场句柄不再使用时,建议关闭。handle(进程句柄表中的索引),这是指向对象的间接指针(在您的具体情况下,ETHREAD)。如果没有关闭,则句柄-对象(ETHREAD)将不是空闲的(直到您的进程终止,并且所有句柄都将关闭)参考进程终止后是否需要关闭线程句柄? 2、int main(int argc, char*argv[]) main 函数的参数只能有两个,还规定 argc 是整型变量,argv 是指向字符串的指针数组。 2.1、argc 是命令行总的参数个数。 2.2、char *argv[ ] 是指针数组,数组中的每个元素都是 char * 类型,即数组中每个元素都会指向一个字符串。从本文demo可以看到,子进程的argv就是父进程传递进来的lpCommandLine。 五、进程相关的API Windows多进程编程 参考1、链接: WinAPI执行外部程序和创建新进程:CreateProcess()的使用2、链接: Windows多进程编程3、链接: windows下创建进程,CreateProcess()详解及用法