zoukankan      html  css  js  c++  java
  • C/C++ 打开外部程序

    WinExec

    int main()
    {
    	/*  WinExec
    		1.#include <Windows.h>
    		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;
    }
    

    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;
    }
    

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

    int main()
    {
    	/*  ShellExecuteEX
    		1.ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));       // 用 0x00 初始化内存
    		2.sei.cbSize = sizeof(SHELLEXECUTEINFO);			// 必须加,否则无法打开程序
    	*/
    
    	// 初始化
    	SHELLEXECUTEINFO sei;
    	/* 参考:https://docs.microsoft.com/zh-cn/windows/win32/api/shellapi/ns-shellapi-shellexecuteinfoa
    	
    	typedef 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;
    }
    

    CreateProcess 这个函数启动外部程序可以说是一波三折,首先,先看下 ASCII 或者叫 UTF-8:

    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-flags
            NULL,           								// 使用本进程的环境变量
            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-flags
            NULL,           								// 使用本进程的环境变量
            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-flags
            NULL,           								// 使用本进程的环境变量
            NULL,           								// 使用本进程的驱动器和目录
            &si,											// STARTUPINFO 结构体指针
            &pi												// PROCESS_INFORMATION 结构体指针
         );if(!bRet){cout << "取色器 GetLastError = " << GetLastError() << endl; }
    
    	getchar();
        return 0;
    }
    

    然后就可以正常运行了:

    最后介绍下这两个结构体:

    // 进程创建时的窗体信息
    typedef struct _STARTUPINFO {
      DWORD  cb;				// 结构体大小
      LPWSTR lpReserved;		// 保留,NULL
      LPWSTR 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-startupinfow
      WORD   wShowWindow;		// 
      WORD   cbReserved2;		// 保留,NULL
      LPBYTE lpReserved2;		// 保留,NULL
      HANDLE hStdInput;			// 标准输入句柄(默认是键盘缓冲区)
      HANDLE hStdOutput;		// 标准输出句柄(默认是控制台缓冲区)
      HANDLE hStdError;			// 标准错误句柄(默认是控制台缓冲区)
    } STARTUPINFO, *LPSTARTUPINFO;
    
    /*******************************************/
    
    // 新进程创建时的进程和主线程信息
    typedef struct _PROCESS_INFORMATION {
      HANDLE hProcess;			// 新创建进程的句柄
      HANDLE hThread;			// 新创建进程的主线程的句柄
      DWORD  dwProcessId;		// 新创建进程的 PID
      DWORD  dwThreadId;		// 新创建进程的主线程的 TID
    } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
    

    许可协议: 文章中的代码均为学习时整理的笔记,博客中除去明确标注有参考文献的文章,其他文章【均为原创】作品,转载请务必【添加出处】,您添加出处是我创作的动力!
  • 相关阅读:
    Hibernate4.0之HibernateSessionFactory源码详解
    Struts.xml中Action的method与路径的三种匹配方法
    codeforces B. Ohana Cleans Up
    codeforces C. Vanya and Scales
    Hibernate主键生成策略
    codeforces B
    FileUpload实现文件上传(包含多文件)
    structs2之多文件上传
    svn的安装与使用
    codeforces Soldier and Number Game(dp+素数筛选)
  • 原文地址:https://www.cnblogs.com/LyShark/p/15019594.html
Copyright © 2011-2022 走看看