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;