Win32 调用外部进程

1. 概述

写代码的过程难免会遇到在一个进程中调用另一个进程的需求,下面主要介绍Windows下常用的几个方法。

2. 方法

2.1 WinExec

// 
//UINT WinExec( LPCSTR lpCmdLine, UINT uCmdShow);
//  lpCmdLine:命令的执行路径
//  uCmdShow:表示是否显示命令窗口,常用的有SW_HIDE和SW_SHOW,也可以是ShowWindow函数的nCmdShow的任何值
//  返回值
// WinExec为了与 16 位 Windows 兼容推荐是使用CreateProcess
//  返回值
//  1. 0:系统内存或资源不足。
//  2. ERROR_BAD_FORMAT:.exe 文件无效。
//  3. ERROR_FILE_NOT_FOUND:未找到指定文件。
//  4. ERROR_PATH_NOT_FOUND:未找到指定路径。
//  5. 如果函数成功,则返回值大于 31。

2.2 system

// 常用语C语言
// 参数为要执行的命令
// 返回值:
// 1. 如果command为NULL并且找到命令解释器,则返回一个非零值。如果未找到命令解释器,则返回0,并将设置 errno 为 ENOENT
// 2. 如果command不为NULL,则system返回由命令解释器返回的值。仅当命令解释器返回值0时,它才会返回值0。返回值-1指示错误,并errno将设置为以下值之一:
//  1. E2BIG 自变量列表(与系统相关)太大。
//  2. ENOENT 无法找到命令解释器。
//  3. ENOEXEC 由于格式无效,无法执行命令解释器文件。
//  4. ENOMEM 没有足够的内存可用于执行命令;或可用内存已损坏;或存在无效块,这表明进行调用的进程未正确进行分配。

2.3 ShellExecute

// HINSTANCE ShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd);
// 参数:
// 1. hwnd:用于显示UI或错误消息的父窗口的句柄。如果操作与窗口无关,则此值可以为NULL
// 2. lpOperation: 指定要执行的操作,可设置内容如下:
//  1. "edit":启动编辑器并打开文档进行编辑,如果lpFile不是文档文件,该函数将失败
//  2. "explore":浏览由 lpFile 指定的文件夹
//  3. "find":从 lpDirectory 指定的目录开始搜索
//  4. "open":打开由 lpFile 参数指定的项目。 该项目可以是文件或文件夹
//  5. "print":打印 lpFile 指定的文件。 如果 lpFile 不是文档文件,则函数失败
//  6. "runas":以管理员身份启动应用程序。 用户帐户控制 (UAC) 将提示用户同意运行提升的应用程序或输入用于运行应用程序的管理员帐户的凭据
//  7. NULL:根据lpFile指定默认操作,如果lpFile没有默认操作,则以"open"模式操作。如果两个都不可用,则在注册表中找到第一个的操作类型进行操作
// 3. lpFile::指向以空字符结尾的字符串的指针,该字符串指定要在其上执行指定谓词的文件或对象。 
//		要指定 Shell 命名空间对象,请传递完全限定的解析名称。 请注意,并非所有对象都支持所有动词。 
//		例如,并非所有文档类型都支持“打印”动词。 如果 lpDirectory 参数使用相对路径,则不要使用 lpFile 的相对路径。
// 4. lpParameters:如果 lpFile 指定一个可执行文件,则此参数是一个指向空终止字符串的指针,该字符串指定要传递给应用程序的参数。 
//		此字符串的格式由要调用的动词决定。 如果 lpFile 指定一个文档文件,lpParameters 应该是 NULL。
// 5. lpDirectory:指向以空字符结尾的字符串的指针,该字符串指定操作的默认(工作)目录。 如果此值为 NULL,则使用当前工作目录。 
//		如果在 lpFile 中提供了相对路径,则不要为 lpDirectory 使用相对路径。
// 6. nShowCmd:指定应用程序在打开时如何显示的标志。 如果 lpFile 指定了一个文档文件,则该标志将简单地传递给相关联的应用程序。 由应用程序决定如何处理它。 
//		它可以是可以在 ShowWindow 函数的 nCmdShow 参数中指定的任何值。
// 返回值:
//	1. 如果函数成功,它返回一个大于 32 的值,如果函数失败,它返回一个指示失败原因的错误值。返回值被强制转换为 HINSTANCE,以便与 16 位 Windows 应用程序向后兼容
//	2. 转换后的HINSTANCE不是真正的HINSTANCE,它只能转换为int并于以下值比较:
//		1. 0:操作系统内存或资源不足
//		2. ERROR_FILE_NOT_FOUND:未找到指定的文件
//		3. ERROR_PATH_NOT_FOUND:未找到指定的路径
//		4. ERROR_BAD_FORMAT:.exe 文件无效(非 Win32 .exe 或 .exe 映像中的错误)
//		5. SE_ERR_ACCESSDENIED:操作系统拒绝访问指定文件
//		6. SE_ERR_ASSOCINCOMPLETE:文件名关联不完整或无效
//		7. SE_ERR_DDEBUSY:无法完成 DDE 事务,因为正在处理其他 DDE 事务
//		8. SE_ERR_DDEFAIL:DDE 事务失败
//		9. SE_ERR_DDETIMEOUT:由于请求超时,无法完成 DDE 事务
//		10. SE_ERR_DLLNOTFOUND:未找到指定的 DLL
//		11. SE_ERR_NOASSOC:没有与给定文件扩展名关联的应用程序。如果您尝试打印不可打印的文件,也会返回此错误
//		12. SE_ERR_OOM:没有足够的内存来完成操作
//		13. SE_ERR_PNF:未找到指定的路径
//		14. SE_ERR_SHARE:发生共享冲突
// DDE(动态数据交换):https://docs.microsoft.com/zh-cn/windows/win32/dataxchg/dynamic-data-exchange

2.4 CreateProcess

// BOOL CreateProcess(LPCSTR lpApplicationName, LPSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,
//	LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,DWORD dwCreationFlags, LPVOID lpEnvironment,
//	LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
// 创建一个新进程及其主线程。 新进程在调用进程的安全上下文中运行。
// 参数:
//	1. lpApplicationName: 指定要执行的模块的完整路径和文件名,也可以是相对路径
//		此参数必须包含文件扩展名;不假定默认扩展名(需要验证)
//		为NULL时,可执行文件名称必须包含在lpCommandLine,例:lpCommandLine = "ping.exe 127.0.0.1"
//		如果可执行程序为16位程序,lpApplicationName必须为空
//		要运行批处理程序时lpApplicationName必须为"cmd.exe",且lpCommandLine格式为 "/c batch_name"
//	2. lpCommandLine:要执行的命令,此字符串的最大长度为 32767个字符,包括终止空字符。
//		如果 lpApplicationName 为 NULL,则 pCommandLine的模块名称(指"ping.exe")部分限制为MAX_PATH个字符
//		lpCommandLine为NULL时,该函数使用lpApplicationName指向的字符串作为命令行
//		如果lpApplicationName和lpCommandLine都非NULL,则lpApplicationName为要执行的程序,lpCommandLine为指定要执行的命令行。
//		  被创建的新进程可以使用 GetCommandLine 来获取整个命令行。用 C 编写的控制台进程可以使用 argc 和 argv 参数来解析命令行
//		
//		注意:
//			1. 此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。所以不能传递常量字符串否则可能导致访问冲突
//			2. 函数内部会向命令行字符串添加一个终止空字符以程序名与参数分开。
//	3. lpProcessAttributes:指向 SECURITY_ATTRIBUTES 的结构体指针,该结构确定返回的新进程对象的句柄是否可以被子进程继承。 
//		如果 lpProcessAttributes 为 NULL,则不能继承句柄。该结构的 lpSecurityDescriptor 成员为新进程指定了一个安全描述符。 
//		如果 lpProcessAttributes 为 NULL 或 lpSecurityDescriptor 为 NULL,则进程获得默认安全描述符。 
//	4. lpThreadAttributes:指向 SECURITY_ATTRIBUTES 的结构体指针,该结构确定返回的新线程对象句柄是否可以由子进程继承。 
//		如果 lpThreadAttributes 为 NULL,则不能继承句柄。该结构的 lpSecurityDescriptor 成员指定主线程的安全描述符。 
//		如果 lpThreadAttributes 为 NULL 或 lpSecurityDescriptor 为 NULL,则线程获得默认安全描述符。 
//	5. bInheritHandles: 如果该参数为TRUE,则调用进程中的每个可继承句柄都将被新进程继承。如果参数为FALSE,则不会继承句柄。
//		注意,继承的句柄具有与原始句柄相同的值和访问权限。
//		终端服务:不能跨会话继承句柄。此外,如果该参数为TRUE,则必须在与调用者相同的会话中创建进程。
//		Protected Process Light(PPL)进程 : 当PPL进程创建一个非PPL进程时,通用句柄继承被阻塞,因为不允许从非PPL进程到PPL进程使用PROCESS_DUP_HANDLE
//	6. dwCreationFlags: 控制优先级类和进程创建的标志。
// 		该参数还控制新进程的优先级类,该优先级类用于确定进程的线程的调度优先级。有关值的列表,请参见GetPriorityClass。如果没有指定优先级类标志,
//		优先级类默认为NORMAL_PRIORITY_CLASS,除非创建进程的优先级类为IDLE_PRIORITY_CLASS或BELOW_NORMAL_PRIORITY_CLASS。在这种情况下,
//		子进程接收调用进程的默认优先级类
//		如果dwCreationFlags参数的值为0:
//			1. 该进程继承了调用者的错误模式和父级的控制台。
//			2. 假定新进程的环境块包含 ANSI 字符(有关其他信息,请参阅 lpEnvironment 参数)。
//			3. 16 位基于 Windows 的应用程序在共享的虚拟 DOS 机(VDM) 中运行。
//	7. lpEnvironment: 指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境
//		如果此参数为NULL,并且父进程的环境块包含Unicode 字符,则还必须确保dwCreationFlags 包含CREATE_UNICODE_ENVIRONMENT
//		每个字符串都是name=value的形式
//		如果进程的环境块的总大小超过 32,767 个字符,则此函数的 ANSI 版本 CreateProcessA 将失败
//		ANSI 环境块由两个零字节终止:一个用于最后一个字符串,另一个用于终止块。 Unicode 环境块由四个零字节终止:两个用于最后一个字符串,另外两个用于终止块
//	8. lpCurrentDirectory: 进程当前目录的完整路径。如果此参数为 NULL,则新进程将与调用进程具有相同的当前驱动器和目录。 (此功能主要为需要启动应用程序并指定其初始驱动器和工作目录的 shell 提供。)
//	9. lpStartupInfo: 要设置扩展属性,请使用 STARTUPINFOEX 结构并在 dwCreationFlags 参数中指定 EXTENDED_STARTUPINFO_PRESENT。
//		STARTUPINFO 或 STARTUPINFOEX 中的句柄在不再需要时必须用 CloseHandle 关闭
//		调用者负责确保 STARTUPINFO 中的标准句柄字段包含有效的句柄值。 即使 dwFlags 成员指定了 STARTF_USESTDHANDLES,这些字段也会原封不动地复制到子进程中而无需验证。 不正确的值会导致子进程行为不端或崩溃。 使用应用程序验证程序运行时验证工具检测无效句柄。
//	10. lpProcessInformation: 指向 PROCESS_INFORMATION 结构的指针,该结构接收有关新进程的标识信息。
//		PROCESS_INFORMATION 中的句柄在不再需要时必须用 CloseHandle 关闭。
// 返回值:如果函数成功,则返回值非零

3. 例子

#include <iostream>
#include <map>
#include <Windows.h>

class Process {
public:
	void CallProcess(std::string str_type) {
		std::map<std::string, char> type;
		type.insert({ "WinExec",0 });
		type.insert({ "System",1 });
		type.insert({ "ShellExecute",2 });
		type.insert({ "CreateProcess",3 });
		switch (type[str_type])
		{
		case 0:
		{
			WinExec("dir", SW_SHOW);
			break;
		}
		case 1:
		{
			system("cls");
			break;
		}
		case 2:
		{
			// 打开网页
			ShellExecute(NULL, TEXT("open"), TEXT("https://www.baidu.com"), NULL, NULL, SW_SHOWNORMAL);
			break;
		}
		case 3:
		{
			STARTUPINFO si;
			PROCESS_INFORMATION pi;

			ZeroMemory(&si, sizeof(si));
			si.cb = sizeof(si);
			ZeroMemory(&pi, sizeof(pi));

			// Start the child process. 
			if (!CreateProcess(NULL,   // No module name (use command line)
				(LPWSTR)TEXT("cls"),        // Command line
				NULL,           // Process handle not inheritable
				NULL,           // Thread handle not inheritable
				FALSE,          // Set handle inheritance to FALSE
				0,              // No creation flags
				NULL,           // Use parent's environment block
				NULL,           // Use parent's starting directory 
				&si,            // Pointer to STARTUPINFO structure
				&pi)           // Pointer to PROCESS_INFORMATION structure
				)
			{
				break;
			}

			// 等待子进程退出
			WaitForSingleObject(pi.hProcess, INFINITE);

			//关闭进程和线程句柄
			CloseHandle(pi.hProcess);
			CloseHandle(pi.hThread);
			break;
		}
		default:
			break;
		}
	}
};

int main() {
	Process process;
	process.CallProcess("WinExec");
	return 0;
}

欢迎大家关注「CoderPro」公众号,请大家多多支持、多多关照/(ㄒoㄒ)/~~
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值