【并发编程二】c++创建子进程CreateProcess()
一、创建子进程
- 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<iostream>
#include<windows.h>
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!"<<std::endl;
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<iostream>
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
参考
1、链接: WinAPI执行外部程序和创建新进程:CreateProcess()的使用
2、链接: Windows多进程编程
3、链接: windows下创建进程,CreateProcess()详解及用法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2014-03-16 source code analyzer 功能强大的C/C++源代码分析软件 Celerity CRACK 破解版
2014-03-16 分析函数调用关系图(call graph)的几种方法
2014-03-16 用CodeViz绘制函数调用关系图(call graph)
2014-03-16 C++的辅助工具介绍
2013-03-16 位运算中的异或运算 .
2013-03-16 按位与、或、异或等运算方法
2012-03-16 Javascript 对象用法