引言

原因是我需要在C++程序中调用另外一个WPF窗体打开或则关闭,进程之前通过通讯协议进行交互。由于使用不同语言开发,两者都比较复杂不方便重写,最方便的方法就是使用进程间通信,WPF窗体应用程序根据消息进行Show/Hide/Exit操作。

函数介绍CreateProcess

BOOL CreateProcess(LPCWSTR               lpApplicationName,//指向可执行模块名称的指针LPWSTR                lpCommandLine,//指向命令行字符串的指针。LPSECURITY_ATTRIBUTES lpProcessAttributes,//指向 SECURITY_ATTRIBUTES 结构的指针,指定新进程的安全属性。LPSECURITY_ATTRIBUTES lpThreadAttributes,//指向 SECURITY_ATTRIBUTES 结构的指针,指定新线程的安全属性。BOOL                  bInheritHandles,//如果为 TRUE,新进程将继承调用进程的句柄。DWORD                 dwCreationFlags,//指定附加的、用来控制优先类和进程的创建的标志。LPVOID                lpEnvironment,//指向新进程的环境块的指针。如果为 NULL,新进程将使用调用进程的环境。LPCWSTR               lpCurrentDirectory,//指向新进程的当前目录的指针。如果为 NULL,新进程将使用调用进程的当前目录。LPSTARTUPINFOW        lpStartupInfo,//指向 STARTUPINFOW 结构的指针,指定新进程的主窗口特性。LPPROCESS_INFORMATION lpProcessInformation//指向 PROCESS_INFORMATION 结构的指针,接收新进程的标识符和句柄。
);

1、lpApplicationName

即将启动的exe程序路径,该参数是一个字符串。我们可以传相对路径或者绝对路径,Windows在启动的时候,会按照一定的顺序查找exe。

1、如果该参数传递的是exe全路径,操作系统会直接启动指定的全路径exe,如果找不到要启动的exe文件,CreateProcess会启动失败。

2、查找主进程exe同级目录下是否存在要启动的exe文件。

3、查询主进程的当前目录下是否存在要启动的exe文件,一般情况下,主进程的当前目录和主进程exe是同一个目录,但是也不绝对,我们可以手动修改程序的当前目录。

4、查询Window系统目录下是否存在要启动的exe文件,就是GetSystemDirectory获取到的文件夹。

5、查询Window目录下是否存在要启动的exe文件。

6、查询环境变量Path所表示的那些目录下,数据存在要启动的exe文件。

从上面的流程看,操作系统查找要指定的exe文件是一个很复杂的流程,所以如果条件允许,我们建议传递全路径。如果不允许,也至少应该是将exe文件放到主进程exe的相对目录下,这也是我经常采用的一种方式。

2、lpCommandLine

表示要启动的进程需要接受的命令行参数。

3、lpProcessAttributes、lpThreadAttributes、bInheritHandles

这两个参数代表子进程的进程安全属性和线程安全属性,都指向SECURITY_ATTRIBUTES的一个结构体,一般情况下,我们可以传NULL,表示子进程使用默认的进程安全属性和 线程安全属性。bInheritHandles表示子进程是否可以继承父进程的句柄(父进程设置了允许继承的安全属性)。如果设置为TRUE,则表示可以继承。

4、dwCreationFlags

参数用于指定创建新进程的时候的一些附加标志,用于控制新进程的一些行为,下面是常用的一些标识:


1、CREATE_NEW_CONSOLE:为新进程创建一个新的控制台窗口。上面的代码我们使用了这个表示,主进程和新进程是两个控制台窗口,如果没有这个flag的话,主进程和新创建的进程共用一个 控制台程序。

2、CREATE_NO_WINDOW:不要为新进程启动一个窗口。如果我们需要创建一个在后台运行没有界面的进程的话,可以使用这个flag。

3、CREATE_SUSPENDED:创建新进程,不要立即执行,将进程挂起,直到调用ResumeThread函数的时候,才开始调用进程。如果我们创建多个子进程之后,需要有一个统一的同步策略,由主进程统一控制多个子进程的执行顺序的话,可以使用这个flag,在上面的代码上稍微做一点修改,新增一个flag和新增两行代码,运行起来,你会发现新创建的进程不会立即执行,而是等待5s之后由主进程控制它继续执行。

BOOL ret = CreateProcess(lpApplicationName,lpCommandLine,NULL,NULL,FALSE,CREATE_NEW_CONSOLE | CREATE_SUSPENDED,NULL,NULL,&si,&pi);

4、CREATE_UNICODE_ENVIRONMENT:创建Unicode字符的环境变量,如果使用了该flag,那么 lpEnvironment 参数指向的环境变量块将使用Unicode,否则将使用ANSI字符。

5、CREATE_DEFAULT_ERROR_MODE:每个进程都有自己的一个错误模式,可以通过SetErrorMode 函数设置,默认情况下,新进程继承父进程的错误模式,如果使用该flag,新进程将使用默认的错误模式。

6、DETACHED_PROCESS:新进程和父进程分离,不继承父进程的控制台。该flag会阻止子进程访问父进程的控制台窗口,一般我们也很少使用该flag。这个flag和CREATE_NO_WINDOW 作用一致,不能同时使用,否则程序会报错。

5、lpEnvironment

默认情况下,子进程将继承父进程的环境变量。如果想给子进程单独设置环境变量块,可以传递该参数。

6、lpCurrentDirectory

设置子进程的运行目录,如果为 NULL,新进程将使用调用进程的当前目录。当子进程有许多依赖项是务必要填入应用程序exe的绝对路径。

7、lpStartupInfo

指定新进程的主窗体特性(指定窗体大小、初始位置、控制台背景颜色字体颜色、窗口标题等信息)、标准输入、输出、错误设备句柄等

typedef struct _STARTUPINFOW {DWORD   cb;//结构的大小,以字节为单位。LPWSTR  lpReserved;//保留,必须为 NULL。LPWSTR  lpDesktop;//指向一个以空字符结尾的字符串,指定桌面名称。LPWSTR  lpTitle;//指向一个以空字符结尾的字符串,指定新进程的窗口标题。DWORD   dwX;//指定窗口的初始位置XDWORD   dwY;//指定窗口的初始位置YDWORD   dwXSize;/指定窗口的初始大小XDWORD   dwYSize;//指定窗口的初始大小YDWORD   dwXCountChars;//指定屏幕缓冲区的宽度,以字符为单位。DWORD   dwYCountChars;//指定屏幕缓冲区的高度,以字符为单位。DWORD   dwFillAttribute;//指定新控制台窗口的文本和背景颜色。DWORD   dwFlags;//指定有效的 STARTUPINFO 成员。WORD    wShowWindow;//指定窗口显示状态。WORD    cbReserved2;//保留,必须为 0。LPBYTE  lpReserved2;//保留,必须为 NULL。HANDLE  hStdInput;//新进程的标准输入句柄HANDLE  hStdOutput;//新进程的标准输出句柄HANDLE  hStdError;//新进程的标准错误设备句柄
} STARTUPINFOW, *LPSTARTUPINFOW;

8、lpProcessInformation

lpProcessInformation是一个输出参数,进程创建之后,会把子进程的进程句柄、线程句柄、进程id、线程id返回给主进程。参数的定义如下:

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池,后续可能会分配给别的进程。

试想一下。我用进程111创建了一个子进程222,子进程222的父进程确实是111,但是因为某些原因进程111被回收,并且重新分配给了一个其他的进程,如果这个时候,子进程再拿着父进程的ID:111去处理业务的话,就会出现意想不到的错误。同理,线程ID也是如此。所以,一般情况下,我们习惯用句柄操作具体业务,很少会使用ID来处理业务。那么hProcess参数和hThread参数就代表子进程的进程句柄和主线程句柄,父进程可以使用这两个参数做对应的业务逻辑。

例子

#include <windows.h>
#include <iostream>
#include <string>
#include <tchar.h>
#include <thread>
bool SendCommandToWpf(const std::wstring& command)
{HANDLE hPipe;DWORD dwWritten;// 等待管道可用while (1){hPipe = CreateFile(TEXT("\\\\.\\pipe\\WpfConsoleCommunicationPipe"), // 管道名称GENERIC_READ | GENERIC_WRITE,0,              // 不共享NULL,           // 默认安全属性OPEN_EXISTING,  // 打开已存在的管道0,              // 默认属性NULL);         // 不指定模板文件// 如果管道连接成功,退出循环if (hPipe != INVALID_HANDLE_VALUE)break;// 如果错误不是ERROR_PIPE_BUSY,则失败if (GetLastError() != ERROR_PIPE_BUSY){std::cout << "无法打开管道. 错误代码: " << GetLastError() << std::endl;return false;}// 所有管道实例都忙,等待20秒if (!WaitNamedPipe(TEXT("\\\\.\\pipe\\WpfConsoleCommunicationPipe"), 20000)){std::cout << "无法在20秒内连接管道." << std::endl;return false;}}// 管道连接成功,设置读写模式DWORD dwMode = PIPE_READMODE_MESSAGE;if (!SetNamedPipeHandleState(hPipe,    // 管道句柄&dwMode,  // 新的管道模式NULL,     // 不设置最大字节数NULL))    // 不设置最大超时时间{std::cout << "设置管道模式失败. 错误代码: " << GetLastError() << std::endl;CloseHandle(hPipe);return false;}// 发送命令if (!WriteFile(hPipe,                  // 管道句柄command.c_str(),       // 消息(command.size() + 1) * sizeof(wchar_t), // 消息长度(包含null终止符)&dwWritten,             // 实际写入的字节数NULL))                  // 不重叠I/O{std::cout << "写入管道失败. 错误代码: " << GetLastError() << std::endl;CloseHandle(hPipe);return false;}CloseHandle(hPipe);return true;
}void StartWpfApplication()
{STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));si.cb = sizeof(si);ZeroMemory(&pi, sizeof(pi));// 替换为你的WPF应用程序路径std::wstring wpfAppPath = L"D:/程序代码/WPF-Window.exe";std::wstring RunDir = L"D:/程序代码/";if (!CreateProcess(&wpfAppPath[0],         // 应用程序名称NULL,                   // 命令行NULL,                   // 进程安全属性NULL,                   // 线程安全属性FALSE,                  // 不继承句柄REALTIME_PRIORITY_CLASS,// 创建新控制台窗口以便查看输出NULL,                   // 使用父进程环境块&RunDir[0],             // 使用父进程起始目录&si,                    // 启动信息&pi))                   // 进程信息{std::cout << "创建进程失败. 错误代码: " << GetLastError() << std::endl;return;}// 关闭不需要的句柄CloseHandle(pi.hProcess);CloseHandle(pi.hThread);std::this_thread::sleep_for(std::chrono::seconds(10));//确保进行已启动
}int main()
{// 启动WPF应用程序StartWpfApplication();std::cout << "控制台程序已启动. 输入命令(Show/Hide/Exit):" << std::endlstd::wstring command;while (std::getline(std::wcin, command)){if (command == L"Exit"){SendCommandToWpf(command);break;}else if (command == L"Show" || command == L"Hide"){if (!SendCommandToWpf(command)){std::cout << "发送命令失败." << std::endl;}}else{std::cout << "未知命令. 可用命令: Show, Hide, Exit" << std::endl;}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94212.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94212.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94212.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Seaborn数据可视化实战

1. Seaborn基础与实践&#xff1a;数据可视化的艺术 2. Seaborn入门&#xff1a;环境搭建与基础操作 3. Seaborn基础图表绘制入门 4. Seaborn数据可视化基础&#xff1a;从内置数据集到外部数据集的应用 5. Seaborn颜色与样式定制教程 6. Seaborn数据可视化入门&#xff1a;绘制…

BIM+写实数字孪生落地实战指南

&#x1f31f; 正文 在智慧城市与工业4.0的浪潮中&#xff0c;BIM与数字孪生的深度碰撞正在重塑建筑的生命周期。基于Revit&#xff08;RVT&#xff09;模型构建的超写实数字孪生体&#xff0c;不仅实现物理空间的毫米级镜像&#xff0c;更通过实时数据驱动&#xff0c;赋予建…

[Git] 如何拉取 GitHub 仓库的特定子目录

作为开发者&#xff0c;我们经常遇到只需要克隆大型仓库中某个子目录的场景。 Git 本身并不支持直接克隆子目录&#xff0c;但通过一些技巧可以实现类似效果。本文将介绍几种实用的方法&#xff0c;帮助获取目标代码。 为什么需要局部拉取&#xff1f; 节省时间和带宽&#xff…

修复Simulink到UE5丢包时被控船体的残影问题

提问 simulink 有一个和UE5协同的模块&#xff0c;叫做Simulation 3D Scence Configuration&#xff0c;还有一个发送来自simulink到UE5数据的模块叫做Simulation 3D Message。 现在遇到的问题是&#xff0c;这两个模块的优先级设置是正确的&#xff0c;且sample time都设置为0…

嵌入式第三十五课!!Linux下的网络编程

一、目的网络编程的目的实际上也是进程通信的一种方式&#xff0c;不过它可以在不同的主机上进行通信&#xff1b;二、需要解决的问题1. 主机与主机之间物理层面必须互联互通。指的是参与通信的计算机&#xff08;主机&#xff09;需要通过物理设备建立连接&#xff08;光纤、网…

遥感机器学习入门实战教程|Sklearn案例⑦:特征选择与重要性分析

很多同学问&#xff1a;波段/特征一多就“维度灾难”&#xff0c;训练慢、过拟合&#xff0c;且很难解释“哪些特征最关键”。本篇用 sklearn 给出一套能跑、可视化、可比较的最小工作流&#xff0c;并配上方法论速记&#xff0c;帮助你在高光谱/多特征任务里做出稳健筛选。 &a…

地理数据制备:蔚蓝地图空气质量数据的获取、清洗与坐标匹配指南

【&#x1f4ca;】手把手攻略&#xff1a;如何从“蔚蓝地图”挖宝——获取济南市可用空气质量数据全记录 一份不需要写代码也能搞定环境数据获取的实用指南 ✨ 引言&#xff1a;为什么选择蔚蓝地图&#xff1f; 作为一名环境数据爱好者&#xff0c;我经常需要获取准确、可靠、…

Unreal Engine USceneComponent

Unreal&#x1f3db; Unreal Engine - USceneComponent&#x1f4da; 定义&#x1f3f7; 类继承⚡ 关键特性⚙️ 常见配置&#x1f6e0;️ 使用方法&#x1f517; 创建与挂载&#x1f504; 获取与修改 Transform&#x1f9e9; 附加/分离组件&#x1f3ca; 典型应用场景&#x1…

2025年9月5090工作站、

在深度学习与大模型训练领域&#xff0c;算力是决定研发效率与模型性能的核心要素&#xff0c;而显卡作为算力输出的核心硬件&#xff0c;其性能参数直接影响着训练任务的速度、稳定性与成本控制。对于企业与科研机构而言&#xff0c;选择一套适配自身需求且性价比优异的显卡及…

亚矩阵云手机:亚马逊第三方店铺多账号安全合规运营的核心技术支撑

亚矩阵云手机在亚马逊第三方店铺多账号安全合规运营的技术支持&#xff0c;通过硬件级虚拟化、AI 行为建模、动态资源调度三大核心技术模块&#xff0c;构建了覆盖设备、网络、行为、数据的四维防御体系&#xff0c;确保账号在亚马逊平台规则下的长期稳定运行。以下从技术架构、…

使用C++11改进工厂方法模式:支持运行时配置的增强实现

在软件开发中&#xff0c;工厂方法模式是一种常用的设计模式&#xff0c;用于创建对象。通过使用C11的新特性&#xff0c;我们可以进一步改进工厂方法模式&#xff0c;使其更加灵活和高效。本文将详细介绍如何使用C11的std::function、lambda表达式和智能指针来实现一个支持运行…

小程序插件使用

插件介绍 插件是对一组 js 接口、自定义组件 或页面的封装&#xff0c;用于嵌入到小程序中使用。插件不能独立运行&#xff0c;必须嵌入在其他小程序中才能被用户使用&#xff1b;而第三方小程序在使用插件时&#xff0c;也无法看到插件的代码。因此&#xff0c;插件适合用来封…

要区分一张图片中的网状图(如网格结构或规则纹理)和噪点(随机分布的干扰像素),比如电路的方法 计算机视觉

要区分一张图片中的网状图&#xff08;如网格结构或规则纹理&#xff09;和噪点&#xff08;随机分布的干扰像素&#xff09;&#xff0c;需结合图像预处理、特征提取和分割算法。以下是系统化的解决方案&#xff0c;分阶段说明关键技术和算法选择&#xff1a; &#x1f50d; 一…

06_并发编程高级特性

第6课:并发编程高级特性 课程目标 掌握context包的使用 理解sync包中的同步原语 学会处理并发安全问题 掌握性能优化技巧 1. Context包 1.1 Context基础 import ("context""fmt""time" )// 基本Context使用 func basicContext()

X00238-非GNSS无人机RGB图像卫星图像视觉定位python

获取方式见文末&#xff0c;可开发票随着无人机在工业和科研领域应用的加速发展&#xff0c;在非城市环境中使用无gnss、基于视觉的方法进行无人机定位的需求日益增长。本文提出了一种基于视觉的定位算法&#xff0c;利用深度特征计算无人机在野外飞行的地理坐标。该方法基于匹…

Eino 开源框架全景解析 - 以“大模型应用的搭积木指南”方式理解

Eino 开源框架全景解析 - 大模型应用的搭积木指南 &#x1f3af; 什么是 Eino&#xff1f;一句话概括 Eino 是字节跳动开源的大语言模型应用开发框架&#xff0c;就像是一个专门为 AI 应用设计的"搭积木工具箱"&#xff0c;让开发者能够像搭乐高一样轻松构建复杂的 A…

嵌入式开发中,usb通信中输出端点和输入端点

一. 简介本文简单学习一下&#xff0c;嵌入式开发中&#xff0c;usb的输出端点和输入端点。在嵌入式开发的 USB 通信场景中&#xff0c;输出端点&#xff08;OUT Endpoint&#xff09; 和 输入端点&#xff08;IN Endpoint&#xff09; 是 USB 设备与主机&#xff08;如电脑、嵌…

【自用】Maven常用依赖

【自用】Maven常用依赖 工具类 Guava Guava&#xff08;Google Guava&#xff09;是由Google团队开发的一套Java开源工具库&#xff0c;旨在简化和增强Java开发者的日常工作。它提供了许多实用的工具和基础设施&#xff0c;覆盖了集合、并发、字符串处理、I/O、数学运算等多个…

Java 18 新特性及具体应用

目录 1. UTF-8 默认编码 (JEP 400) 2. 简单 Web 服务器 (JEP 408) 3. Javadoc 代码片段 (JEP 413) 4. switch 模式匹配 (JEP 420, 第二次预览) 5. 向量 API (JEP 417, 第三次孵化) 总结 Java 18 于 2022 年 3 月发布&#xff0c;引入了多项新特性&#xff0c;旨在提升开发…

unistd.h 常用函数速查表

在这篇文章中&#xff0c;我们将整理一份 unistd.h 常用函数速查表&#xff0c;便于快速查找和记忆&#xff0c;涵盖文件 I/O、进程管理、系统信息、用户/组信息等方面。unistd.h 常用函数速查表&#xff08;POSIX/Linux/macOS&#xff09; 1. 文件与 I/O 操作函数说明示例int …