C++ 打开外部程序

1. WinExec

int main()
{/*  WinExec1.#include 2.某些 exe 如果不使用管理员权限运行 VS 则会报 740 错误*/WinExec("E:\\MyToolBar\\Programming\\取色器.exe", SW_SHOWNORMAL);cout << "取色器 GetLastError = " << GetLastError() << endl;WinExec("D:\\MyFiles\\WeGame\\tgp_daemon.exe", SW_SHOWNORMAL);cout << "tgp_daemon GetLastError = " << GetLastError() << endl;WinExec("C:\\Windows\\SysNative\\calc.exe", SW_SHOWNORMAL);cout << "calc GetLastError = " << GetLastError() << endl;getchar();return 0;
}

没有使用管理员权限打开 VS2019:在这里插入图片描述
使用管理员权限打开 VS2019:在这里插入图片描述

WinExec(LPCSTR lpCmdLine,			// cmd 命令UINT   uCmdShow			// 显示方式 更多宏定义参考:https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-showwindow
);

也就是说, WinExec 这个函数并不是专门用来打开进程的,而是调用了 cmd 功能,因为我们再 cmd 中输入 WinExec 的第一个参数也会达到相同的效果:在这里插入图片描述

2.ShellExecute

值得一提的是,ShellExecute 在不使用管理员权限运行 VS2019 的情况下仍然可以正常打开任何程序,不报 740 错误。

int main()
{/*  ShellExecute不需要使用管理员权限打开 VS2019 也可以打开 WinExec 不能打开的程序*/ShellExecute(NULL,												// 父窗口句柄L"open",											// edit:编辑,open:打开,print:打印,explore:浏览,find:搜索L"E:\\MyToolBar\\Programming\\取色器.exe",			// 文件全路径或文件夹名NULL,												// 程序启动时的命令行参数NULL,												// 默认操作目录为当前目录SW_SHOWNORMAL										// 显示方式 更多宏定义参考:https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-showwindow); cout << "取色器 GetLastError = " << GetLastError() << endl;ShellExecute(NULL,L"open",L"D:\\MyFiles\\WeGame\\tgp_daemon.exe",NULL,NULL,SW_SHOWNORMAL);cout << "tgp_daemon GetLastError = " << GetLastError() << endl;ShellExecute(NULL, L"open", L"C:\\Windows\\SysNative\\calc.exe", NULL, NULL, SW_SHOWNORMAL);cout << "calc GetLastError = " << GetLastError() << endl;getchar();return 0;
}

在这里插入图片描述

3.ShellExecuteEX

与 ShellExecute 一样,ShellExecuteEX 也不需要管理员启动就可以打开所有进程

int main()
{/*  ShellExecuteEX1.ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));       // 用 0x00 初始化内存2.sei.cbSize = sizeof(SHELLEXECUTEINFO);			// 必须加,否则无法打开程序*/// 初始化SHELLEXECUTEINFO sei;/* 参考:https://docs.microsoft.com/zh-cn/windows/win32/api/shellapi/ns-shellapi-shellexecuteinfoatypedef struct _SHELLEXECUTEINFOA {DWORD     cbSize;				// 必须存在,可以用 sizeof(SHELLEXECUTEINFO) 赋值ULONG     fMask;				// 指定结构成员的有效性HWND      hwnd;				// 父窗口句柄LPCSTR    lpVerb;				// edit:编辑、explore:浏览、find:搜索、open:打开(默认)、print:打印、properties:显示属性、runas:管理员运行LPCSTR    lpFile;				// 目标文件LPCSTR    lpParameters;		// 程序启动参数LPCSTR    lpDirectory;		// 工作目录int       nShow;				// 显示方式HINSTANCE hInstApp;			// 接收 ShellExcuteEX 的返回值void      *lpIDList;			// 指向 ITEMIDLIST  对象的指针LPCSTR    lpClass;			// 附加信息,可以是程序标识符、协议类型、文件后缀、注册表路径HKEY      hkeyClass;			// 当 fMask = SEE_MASK_CLASSNAME 时使用DWORD     dwHotKey;			// 与应用程序关联的键盘快捷键union {HANDLE hIcon;		// 目标文件图标句柄,fMask = SEE_MASK_ICON 时使用HANDLE hMonitor;	// 文档监视器句柄,fMask = SEE_MASK_HMONITOR 时使用} DUMMYUNIONNAME;HANDLE    hProcess;			// 新启动的应用程序的句柄} SHELLEXECUTEINFOA, *LPSHELLEXECUTEINFOA;*/ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));// 打开程序sei.cbSize = sizeof(SHELLEXECUTEINFO);sei.lpFile = L"E:\\MyToolBar\\Programming\\取色器.exe";ShellExecuteEx(&sei); cout << "取色器 GetLastError = " << GetLastError() << endl;sei.lpFile = L"D:\\MyFiles\\WeGame\\tgp_daemon.exe";ShellExecuteEx(&sei);cout << "tgp_daemon GetLastError = " << GetLastError() << endl;sei.lpFile = L"C:\\Windows\\SysNative\\calc.exe";ShellExecuteEx(&sei);cout << "calc GetLastError = " << GetLastError() << endl;getchar();return 0;
}

在这里插入图片描述

4.CreateProcess

这个函数启动外部程序可以说是一波三折,首先,先看下 ASCII 或者叫 UTF-8(也就是 VS2019 里使用多字节字符):在这里插入图片描述

int main()
{// 初始化STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));ZeroMemory(&pi, sizeof(pi));BOOL bRet = CreateProcess(NULL,           								// 不在此指定可执行文件的文件名"E:\\MyToolBar\\Programming\\取色器.exe",      	// 命令行参数NULL,           								// 默认进程安全性NULL,           								// 默认线程安全性FALSE,          								// 指定当前进程内的句柄不可以被子进程继承CREATE_NEW_CONSOLE, 							// 为新进程创建一个新的控制台窗口,更多宏定义参考:https://docs.microsoft.com/zh-cn/windows/win32/procthread/process-creation-flagsNULL,           								// 使用本进程的环境变量NULL,           								// 使用本进程的驱动器和目录&si,											// STARTUPINFO 结构体指针&pi												// PROCESS_INFORMATION 结构体指针);if(!bRet){cout << "取色器 GetLastError = " << GetLastError() << endl; }getchar();return 0;
}

在这里插入图片描述
正常打开,没有什么问题 ——
下面再看 Unicode 编码,仍然用上面的代码,运行一下,发现程序在调用 CreateProcess 的时候触发了空指针异常:在这里插入图片描述
将代码改成如下:

int main()
{// 初始化WCHAR* szCommandLine = L"E:\\MyToolBar\\Programming\\取色器.exe";STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));ZeroMemory(&pi, sizeof(pi));BOOL bRet = CreateProcess(NULL,           								// 不在此指定可执行文件的文件名szCommandLine ,      							// 命令行参数NULL,           								// 默认进程安全性NULL,           								// 默认线程安全性FALSE,          								// 指定当前进程内的句柄不可以被子进程继承CREATE_NEW_CONSOLE, 							// 为新进程创建一个新的控制台窗口,更多宏定义参考:https://docs.microsoft.com/zh-cn/windows/win32/procthread/process-creation-flagsNULL,           								// 使用本进程的环境变量NULL,           								// 使用本进程的驱动器和目录&si,											// STARTUPINFO 结构体指针&pi												// PROCESS_INFORMATION 结构体指针);if(!bRet){cout << "取色器 GetLastError = " << GetLastError() << endl; }getchar();return 0;
}

发现异常仍然存在:在这里插入图片描述
看下 MSDN 的介绍,发现 CreateProcess 的第二个参数有一句这么写到:在这里插入图片描述

也就是说 CreateProcessW 的第二个参数不能是一个常量字符串,或者是一个指向只读地址的指针,这么也就说通了,因为L"E:\\MyToolBar\\Programming\\取色器.exe" 是常量字符串,WCHAR* szCommandLine = L"E:\\MyToolBar\\Programming\\取色器.exe"; 是一个指针,那么最后验证一下它指向的是否是一个只读内存就可以了。在这里插入图片描述 思路如此清晰,这已经不像我 😕

我使用 CE 验证的,通过勾选和不勾选 “可写”,可以发现 WCHAR* szCommandLine 的确是只读变量(指针):在这里插入图片描述在这里插入图片描述
既然这样我们只能修改代码:

int main()
{// 初始化WCHAR szCommandLine[] = L"E:\\MyToolBar\\Programming\\取色器.exe";STARTUPINFO si;PROCESS_INFORMATION pi;ZeroMemory(&si, sizeof(si));ZeroMemory(&pi, sizeof(pi));BOOL bRet = CreateProcess(NULL,           								// 不在此指定可执行文件的文件名szCommandLine ,      							// 命令行参数NULL,           								// 默认进程安全性NULL,           								// 默认线程安全性FALSE,          								// 指定当前进程内的句柄不可以被子进程继承CREATE_NEW_CONSOLE, 							// 为新进程创建一个新的控制台窗口,更多宏定义参考:https://docs.microsoft.com/zh-cn/windows/win32/procthread/process-creation-flagsNULL,           								// 使用本进程的环境变量NULL,           								// 使用本进程的驱动器和目录&si,											// STARTUPINFO 结构体指针&pi												// PROCESS_INFORMATION 结构体指针);if(!bRet){cout << "取色器 GetLastError = " << GetLastError() << endl; }getchar();return 0;
}

然后就可以正常运行了:在这里插入图片描述
最后介绍下这两个结构体:

// 进程创建时的窗体信息
typedef struct _STARTUPINFO {DWORD  cb;				// 结构体大小LPWSTR lpReserved;		// 保留,NULLLPWSTR lpDesktop;			// 进程 窗口站/桌面 名称LPWSTR lpTitle;			// 控制台进程的窗口标题DWORD  dwX;				// 窗口左上角 x坐标DWORD  dwY;				// 窗口左上角 y坐标DWORD  dwXSize; 			// 窗口宽DWORD  dwYSize;			// 窗口高DWORD  dwXCountChars;		// 屏幕缓冲区宽度DWORD  dwYCountChars;		// 屏幕缓冲区高度DWORD  dwFillAttribute;	// 控制台初始字体和背景色DWORD  dwFlags;			// 控制其他参数哪个生效的宏 (关键)参考:https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfowWORD   wShowWindow;		// WORD   cbReserved2;		// 保留,NULLLPBYTE lpReserved2;		// 保留,NULLHANDLE hStdInput;			// 标准输入句柄(默认是键盘缓冲区)HANDLE hStdOutput;		// 标准输出句柄(默认是控制台缓冲区)HANDLE hStdError;			// 标准错误句柄(默认是控制台缓冲区)
} STARTUPINFO, *LPSTARTUPINFO;/*******************************************/// 新进程创建时的进程和主线程信息
typedef struct _PROCESS_INFORMATION {HANDLE hProcess;			// 新创建进程的句柄HANDLE hThread;			// 新创建进程的主线程的句柄DWORD  dwProcessId;		// 新创建进程的 PIDDWORD  dwThreadId;		// 新创建进程的主线程的 TID
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部