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;
版权声明:本博客文章与代码均为学习时整理的笔记,文章 [均为原创] 作品,转载请 [添加出处] ,您添加出处是我创作的动力!










