1. 程式人生 > >CreateProcess 建立帶命令列引數的程序時,報錯或者提示記憶體位置無效的可能的一個原因

CreateProcess 建立帶命令列引數的程序時,報錯或者提示記憶體位置無效的可能的一個原因

可能的一個原因:命令列引數使用了常量。

例如:

CreateProcess(NULL, "notepad",NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&si,&pi);

解釋:

pszApplicationName和pszCommandLine分別表示程序使用的可執行檔名和向其傳遞的命令列字串,注意pszCommandLine是PTSTR,這意味著你必須為其傳遞指向非常量字串的地址。CreateProcess內部會更改向其傳遞的命令列字串,但在CreateProcess返回之前,它會將該字串恢復原樣。這一點是非常重要的,因為如果你向CreateProcess傳遞的命令列字串位於程序的只讀儲存區,就會發生Access Violation錯誤。

微軟在其C++編譯器選項中提供了/GF開關,/GF開啟時,程式中所有用到的常量字串將只維護單一副本,且位於只讀儲存部分。在呼叫 CreateProcess時,開發人員應該開啟/GF開關並使用緩衝區。我們希望微軟在未來版本的Windows中會改進CreateProcess,使其接受常量字串作為命令列引數,並在其內部分配/釋放臨時緩衝區而不是讓API呼叫者來做。另外,假如你使用常量ANSI字串作為 CreateProcess引數,並不會發生Access Violation錯誤,我們在前面的章節已經提到過,許多WinAPI函式的ANSI版本會將ANSI引數轉換為UNIDOE編碼後呼叫其 Unicode版本,CreateProcess會把ANSI字串轉換為Unicode編碼後放在臨時緩衝區,並呼叫Unicode版的 CreateProcess,因此不會觸發Access Violation。

你如果想要一個動態長度命令列引數,可以參考下面的程式碼

STARTUPINFO si = { sizeof(si) };

PROCESS_INFORMATION pi;
LPTSTR pszCmd = new TCHAR[strCmd.length()+1];
memset(pszCmd,0,sizeof(TCHAR)*(strCmd.length()+1));
_tcscpy(pszCmd,strCmd.c_str());
CreateProcess(NULL, pszCmd,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&si,&pi);
WaitForSingleObject(pi.hProcess,INFINITE);

CloseHandle(pi.hProcess);


額外說一點,我在做上述測試時發現,呼叫CreateProcess來呼叫cmd.exe,我的程式沒報錯,但是系統卻報cmd.exe應用程式錯誤(無法正常啟動0xc0000142)。但是我手動啟動cmd.exe,又可以正常啟動。

通過查詢一篇文章發現:STARTUPINFO si = { sizeof(si) };很關鍵。

當你直接寫STARTUPINFO si;此時si裡面一些內容可能是隨機的。如果你沒有清空其內容,STARTUPINFO(EX)的內容會是呼叫執行緒堆疊上的一些資料。將這些垃圾資料傳遞給CreateProcess可能導致無法預料的結果,為了讓CreateProcess正常工作,你必須將STARTUPINFO(EX)中沒有用到的域清0。


個人水平有限,如有錯誤請聯絡我更改。活到老,學到老!