获取命令行参数的各种变化

http://blog.csdn.net/silvergingko/article/details/6072083

通常,在VC++中获取命令行参数的有如下几种方式:

 

在控制台程序中:

C++运行时库通过入口函数main传递进来的参数int argc 和 char* argv[]。其中第二个参数将一个完整的命令行分割成指向各参数的字符串指针数组,数组中的每一个元素是一个指向参数的字符串指针。其中, argv[0] 指向应用程序名自身。

如果想获得像窗口形式的完整命令行参数CmdLine的话,可以调用API GetCommandLine() 获得。

 

在窗口程序中:

C++运行时库通过入口函数WinMain传递进来的参数LPTSTR pszCmdLine。pszCmdLine是一个完整的命令行参数,包含应用程序名自身。

如果想获得像控制台形式的被分割的命令行参数指针数组的话。可以使用如下代码获得:

[cpp]  view plain copy
  1. //C++ code  
  2. pszCmdLineW = GetCommandLineW();  
  3. argv = CommandLineToArgvW(pszCmdLineW, &argc);  
  4. if (argv != NULL) LocalFree(argv);  

需要注意的是, CommandLineToArgvW只有Unicode的版本,因此省略第二行的代码,而直接将入口函数中提供的参数lpCmdLine传给 CommandLineToArgvW 可能会存在问题,这取决于项目使用的字符集属性,幸好从 VS2005 开始,项目默认使用的字符集就是 Unicode!

 

然而,在实际使用命令行参数,其实并不像如上所述那么简单,还有几个不确定的因素会导致获得的命令行参数发生变化。

 

首先,给出三个测试程序, ConApp 是一个控制台程序,WinApp 是一个窗口程序,这两个程序在内部将获取命令行参数并进行显示,AppCaller是另外一个控制台程序,它将分几次调用 ConApp 和 WinApp ,并为它们传递不同的参数。

测试步骤为:

AppCaller创建ConApp 和 WinApp 子进程,并在控制台上输出传给 ConApp 和 WinApp 的参数,然后等待子进程结束。 ConApp 和 WinApp 显示命令行参数后返回, AppCaller再次调用 ConApp 和 WinAPP 并传递其他参数。

 

三个程序的代码为:

WinApp:

[cpp]  view plain copy
  1. //C++ code  
  2. //Win32 project.  
  3. //WinApp.cpp  
  4.   
  5. #include <Windows.h>  
  6. #include <tchar.h>  
  7. #include <strsafe.h>  
  8. #include <stdarg.h>  
  9.   
  10. //使用XP风格的控件。  
  11. #if defined _M_IX86  
  12. #pragma comment(linker,"/manifestdependency:/"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'/"")  
  13. #endif  
  14.   
  15. //函数前导声明。  
  16. HRESULT FormatTextW(WCHAR **, size_t *, LPCWSTR, ...);  
  17. HRESULT AppendTextW(WCHAR **, size_t *, LPCWSTR);  
  18. HRESULT AppendMemHexW(WCHAR **, size_t *, LPBYTEint);  
  19.   
  20. int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/,  
  21.     LPTSTR lpCmdLine, int nCmdShow)  
  22. {  
  23.     int argc = 0;  
  24.     int chLen = 0;  
  25.     size_t bufLen = 0;  
  26.     HRESULT hr = S_OK;  
  27.     LPWSTR pszCmdLineW = NULL;  
  28.     LPWSTR *argv = NULL;  
  29.     WCHAR szText[1024] = { 0 };  
  30.     LPWSTR pCur = NULL;  
  31.   
  32.     pszCmdLineW = GetCommandLineW();  
  33.     argv = CommandLineToArgvW(pszCmdLineW, &argc);  
  34.   
  35.     //对命令行参数进行一些格式化,存储到szText中,最后通过MessageBox进行显示。  
  36.     bufLen = _countof(szText);  
  37.     pCur = szText;  
  38.     if (SUCCEEDED(  
  39.         hr = FormatTextW(&pCur, &bufLen, L"GetCommandLineW=%s", pszCmdLineW)  
  40.         ) && SUCCEEDED(  
  41.         hr = AppendMemHexW(&pCur, &bufLen, (LPBYTE)pszCmdLineW,  
  42.         wcslen(pszCmdLineW)*sizeof(WCHAR))  
  43.         ) && SUCCEEDED(  
  44.         hr = AppendTextW(&pCur, &bufLen, L"/r/n")  
  45.         ) && SUCCEEDED(  
  46.         hr = AppendTextW(&pCur, &bufLen, L"CommandLineToArgvW:/r/n")  
  47.         ) && SUCCEEDED(  
  48.         hr = FormatTextW(&pCur, &bufLen, L"argc=%d/r/nargv:/r/n", argc)))  
  49.     {  
  50.         for (int i = 0; i < argc; i++)  
  51.         {  
  52.             hr = FormatTextW(&pCur, &bufLen, L"[#%u]%s", i, argv[i]);  
  53.             if (FAILED(hr)) break;  
  54.             hr = AppendMemHexW(&pCur, &bufLen, (LPBYTE)argv[i],  
  55.                 wcslen(argv[i])*sizeof(WCHAR));  
  56.             if (FAILED(hr)) break;  
  57.         }  
  58.     }  
  59.   
  60.     if (argv != NULL) LocalFree(argv);  
  61.   
  62.     if ((SUCCEEDED(hr)) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))  
  63.     {  
  64.         MessageBoxW(NULL, szText, L"Win App", MB_OK | MB_ICONINFORMATION);  
  65.     }  
  66.   
  67.     return 0;  
  68. }  
  69.   
  70. //格式化文本到缓冲区。  
  71. HRESULT FormatTextW(WCHAR **ppBuf, size_t *pBufLen, LPCWSTR pszFormat, ...)  
  72. {  
  73.     va_list argList;  
  74.     va_start(argList, pszFormat);  
  75.     int chLen = 0;  
  76.     HRESULT hr = S_OK;  
  77.   
  78.     hr = StringCchVPrintfExW(*ppBuf, *pBufLen, ppBuf, pBufLen,  
  79.         STRSAFE_IGNORE_NULLS, pszFormat, argList);  
  80.     va_end(argList);  
  81.   
  82.     return hr;  
  83. }  
  84.   
  85. //添加文本到缓冲区。  
  86. HRESULT AppendTextW(WCHAR **ppBuf, size_t *pBufLen, LPCWSTR pszText)  
  87. {  
  88.     return StringCchCatExW(*ppBuf, *pBufLen, pszText, ppBuf, pBufLen,  
  89.         STRSAFE_IGNORE_NULLS);  
  90. }  
  91.   
  92. //添加16进制形式的内存字节到缓冲区。  
  93. HRESULT AppendMemHexW(WCHAR **ppBuf, size_t *pBufLen, LPBYTE pMem, int cbLen)  
  94. {  
  95.     HRESULT hr = S_OK;  
  96.     WCHAR sz[512] = { 0 };  
  97.   
  98.     hr = StringCchCatEx(*ppBuf, *pBufLen, L"(", ppBuf, pBufLen,  
  99.         STRSAFE_IGNORE_NULLS);  
  100.   
  101.     for (int i = 0; i < cbLen; i += 2)  
  102.     {  
  103.         hr = StringCchPrintfEx(*ppBuf, *pBufLen, ppBuf, pBufLen,  
  104.             STRSAFE_IGNORE_NULLS, L"%02x%02x", pMem[i], pMem[i+1]);  
  105.         if (FAILED(hr)) break;  
  106.         if (i != (cbLen - 2))  
  107.         {  
  108.             hr = StringCchCatEx(*ppBuf, *pBufLen, L" ", ppBuf, pBufLen,  
  109.                 STRSAFE_IGNORE_NULLS);  
  110.             if (FAILED(hr)) break;  
  111.         }  
  112.     }  
  113.   
  114.     if (SUCCEEDED(hr))  
  115.     {  
  116.         hr = StringCchCatEx(*ppBuf, *pBufLen, L")/r/n", ppBuf, pBufLen,  
  117.             STRSAFE_IGNORE_NULLS);  
  118.     }  
  119.     return hr;  
  120. }  
  121.   
  122. //end of WinApp.cpp  

 

ConApp:

[cpp]  view plain copy
  1. //C++ code  
  2. //Console project.  
  3. //ConApp.cpp  
  4.   
  5. #include <stdio.h>  
  6. #include <tchar.h>  
  7. #include <Windows.h>  
  8.   
  9. //函数前导声明。  
  10. void PrintMem(LPBYTEint);  
  11.   
  12. int _tmain(int /*argc*/, _TCHAR* /*argv[]*/)  
  13. {  
  14.     int argc = 0;  
  15.     LPWSTR pszCmdLineW = GetCommandLineW();  
  16.     LPWSTR *argv = CommandLineToArgvW(pszCmdLineW, &argc);  
  17.   
  18.     //对命令行参数进行一些格式化,然后输出到控制台上。  
  19.     wprintf(L"GetCommandLineW=%s", pszCmdLineW);  
  20.     PrintMem((LPBYTE)pszCmdLineW, wcslen(pszCmdLineW)*sizeof(WCHAR));  
  21.     _tprintf(_T("/r/n"));  
  22.   
  23.     _tprintf(_T("CommandLineToArgvW:/r/n"));  
  24.     _tprintf(_T("argc=%d/r/nargv:/r/n"), argc);  
  25.     for (int i = 0; i < argc; i++)  
  26.     {  
  27.         wprintf(L"[#%u]%s", i, argv[i]);  
  28.         PrintMem((LPBYTE)argv[i], wcslen(argv[i])*sizeof(WCHAR));  
  29.     }  
  30.     return 0;  
  31. }  
  32.   
  33. //输出16进制形式的内存字节。  
  34. void PrintMem(LPBYTE pMem, int len)  
  35. {  
  36.     _puttchar(_T('('));  
  37.   
  38.     for (int i = 0; i < len; i += 2)  
  39.     {  
  40.         //内存字节是 Unicode 字符集数据,一次打印两个字节。  
  41.         _tprintf(_T("%02x%02x"), pMem[i], pMem[i+1]);  
  42.         if (i != (len - 2))  
  43.         {  
  44.             _puttchar(_T(' '));  
  45.         }  
  46.     }  
  47.     _tprintf(_T(")/r/n"));  
  48. }  
  49.   
  50. //end of ConApp.cpp  

 

AppCaller:

[c-sharp]  view plain copy
  1. //C++ code  
  2. //Console project.  
  3. //AppCaller.cpp  
  4.  
  5. #include <stdio.h>  
  6. #include <tchar.h>  
  7. #include <Windows.h>  
  8.   
  9. //函数前导声明。  
  10. void CallApp(LPCTSTR, LPTSTR);  
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.     //假设 WinApp.exe 和 CmdApp.exe 在当前目录中。  
  15.     LPCTSTR pszWinApp = _T("WinApp.exe");  
  16.     LPCTSTR pszConApp = _T("ConApp.exe");  
  17.     TCHAR szCmdLine1[] = { _T("/t123") };  
  18.     TCHAR szCmdLine2[] = { _T(" 123") };  
  19.     TCHAR szWinCmdLine1[] = { _T("/"WinApp.exe/"") };  
  20.     TCHAR szWinCmdLine2[] = { _T("WinApp.exe") };  
  21.     TCHAR szConCmdLine1[] = { _T("/"ConApp.exe/"") };  
  22.     TCHAR szConCmdLine2[] = { _T("ConApp.exe") };  
  23.       
  24.     //窗口程序测试。  
  25.     CallApp(pszWinApp, NULL);  
  26.     CallApp(pszWinApp, szWinCmdLine1);  
  27.     CallApp(pszWinApp, szWinCmdLine2);  
  28.     CallApp(pszWinApp, szCmdLine1);  
  29.     CallApp(pszWinApp, szCmdLine2);  
  30.       
  31.     //控制台程序测试。  
  32.     CallApp(pszConApp, NULL);  
  33.     CallApp(pszConApp, szConCmdLine1);  
  34.     CallApp(pszConApp, szConCmdLine2);  
  35.     CallApp(pszConApp, szCmdLine1);  
  36.     CallApp(pszConApp, szCmdLine2);  
  37.       
  38.     return 0;  
  39. }  
  40.   
  41. void CallApp(LPCTSTR pszApp, LPTSTR pszCmdLine)  
  42. {  
  43.     static int c = 0;  
  44.     BOOL bRet = FALSE;  
  45.     STARTUPINFO si = { sizeof(si) };  
  46.     PROCESS_INFORMATION pi = { 0 };  
  47.   
  48.     _tprintf(_T("------------------------------------------/r/n/r/n"));  
  49.     _tprintf(_T("<CASE %d>/r/n/r/n"), c);  
  50.     c++;  
  51.     _tprintf(_T("pszCmdLine -- %s/r/n"),  
  52.         pszCmdLine == NULL ? _T("NULL") : pszCmdLine);  
  53.     bRet = CreateProcess(pszApp, pszCmdLine, NULL, NULL, FALSE, 0,  
  54.         NULL, NULL, &si, &pi);  
  55.     if (bRet != FALSE)  
  56.     {  
  57.         if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, INFINITE))  
  58.         {  
  59.             CloseHandle(pi.hThread);  
  60.             CloseHandle(pi.hProcess);  
  61.         }  
  62.     }  
  63.     _tprintf(_T("/r/n------------------------------------------/r/n"));  
  64. }  
  65.   
  66. //end of AppCaller.cpp  

 

如果单独测试 ConApp 和 WinApp ,不为它们传递任何参数,

WinApp 在 VS 调试环境中按 F5 调试启动 或者在资源管理器中双击文件启动,显示的消息框内容都为:

 

ConApp 在 VS 调试环境中按 F5 调试启动,输出的内容为:

GetCommandLineW="E:/test/CppConsole/ConApp/Debug/ConApp.exe"  (2200 4500 3a00 5c
00 7400 6500 7300 7400 5c00 4300 7000 7000 4300 6f00 6e00 7300 6f00 6c00 6500 5c
00 4300 6f00 6e00 4100 7000 7000 5c00 4400 6500 6200 7500 6700 5c00 4300 6f00 6e
00 4100 7000 7000 2e00 6500 7800 6500 2200 2000 2000)

 

CommandLineToArgvW:
argc=1
argv:
[#0]E:/test/CppConsole/ConApp/Debug/ConApp.exe(4500 3a00 5c00 7400 6500 7300 740
0 5c00 4300 7000 7000 4300 6f00 6e00 7300 6f00 6c00 6500 5c00 4300 6f00 6e00 410
0 7000 7000 5c00 4400 6500 6200 7500 6700 5c00 4300 6f00 6e00 4100 7000 7000 2e0
0 6500 7800 6500)

 

如果在控制台中启动,输出的内容为:

GetCommandLineW=ConApp(4300 6f00 6e00 4100 7000 7000)

 

CommandLineToArgvW:

argc=1

argv:

[#0]ConApp(4300 6f00 6e00 4100 7000 7000)

 

首先,在窗口和控制台应用程序都未获得参数时,调用 GetCommandLine 和 CommandLineToArgvW 都会成功,返回的内容都是应用程序文件名(含扩展名),且 argc 等于1。

但是,通过比较 ConApp 在两种启动方式中,获得的内容是不同的。

在 VS 调试模式中启动,命令行参数包含文件的完整路径,在控制台中启动,不包含文件路径

 

然后,启动 AppCaller 后, ConApp 输出的内容与 WinApp 输出的内容基本上是一致的,且 AppCaller 是在 VS 中启动还是在控制台中启动,都没有差异。并且当没有参数传递给 WinApp 和 ConApp 时,这两个子进程所获得的命令行参数是它们对应的文件名,且不含文件路径。

 

AppCaller 和 ConApp 在控制台中输出的内容为:

 

------------------------------------------

<CASE 5>

pszCmdLine -- NULL
GetCommandLineW="ConApp.exe"(2200 4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6
500 2200)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 6>

pszCmdLine -- "ConApp.exe"
GetCommandLineW="ConApp.exe"(2200 4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6
500 2200)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 7>

pszCmdLine -- ConApp.exe
GetCommandLineW=ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 8>

pszCmdLine --   123
GetCommandLineW=        123(0900 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]()
[#1]123(3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 9>

pszCmdLine --  123
GetCommandLineW= 123(2000 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]()
[#1]123(3100 3200 3300)

------------------------------------------

 

 需要注意的问题是,比较 CASE 5 和 CASE 6 :

CASE 5 中, pszCmdLine 是 NULL ,这意味着没有参数传给 ConApp ,ConApp 调用 GetCommandLineW 获得的文件名被双引号扩住(Unicode值0x2200),但调用 CommandLineToArgvW 获得的 argv[0] ,对应的文件名是不包含双引号的。而且,如果不是通过CreateProcess启动 ConApp ,而是在控制台中直接启动 ConApp ,正如上面给出的结果, ConApp 调用 GetCommandLineW 是不包含双引号的。

在 CASE 6 中,pszCmdLine 为 "ConApp.exe"  ,注意它是参数,不是程序名,完整的命令行类似于 ConApp "ConApp.exe" ,在参数中特意加了双引号,此时,ConApp 调用 GetCommandLineW 和 CommandLineToArgvW 后,获得的命令行参数与 CASE 5 中的内容完全一致, 即:不为 ConApp 传递参数 和为 ConApp 传递且仅传递一个参数 "ConApp" 时,两者在获取命令行方面没有差异!

因此,在写关于获取及解析命令行参数的代码时,需要考虑这种极为特殊的情况。事实上,似乎是没有办法来区分用户到底是用哪种方式调用了 ConApp 。也许,在控制台输入 ConApp "ConApp"的用户,本意是想调用ConApp,并传递一个参数 "ConApp" ,但如果在程序中只是简单解析成,该命令行不包含任何参数(因为无法区分 CASE 5 还是 CASE 6),并如传统做法那样显示一个关于程序的 Usage ,会造成用户的困惑。

 

因此,在开发程序时,如果程序允许用户传递参数给程序,则该程序应当设计成,参数含前导字符的形式: 如 ConApp /"ConApp" 或 ConAPP -"ConApp", 这样的程序才是正确的设计方法。同时,也能避免 CASE 8 和 CASE 9 将前导空格符(Unicode值0x2200)和TAB制表符(Unicode值0x0900)当做参数进行解析的问题。

 

以上讨论的结果是基于这样一种情况:调用 CreateProcess 时,为第一个参数传递文件名,为第二个参数传递子进程需要使用的参数。根据 MSDN 文档解释, 调用 CreateProcess 时,可以将第一个参数设为 NULL ,为第二个参数传递一个完整的命令行参数即可,即第二个参数的形式为 ConApp /123 。尽管这两种调用方式都能成功创建子进程,然而,对于要解析命令行的子进程而言,这两种不同的调用方式会直接影响到子进程对命令行参数的解析。原因是, 子进程在不同的调用方式中被创建后, 子进程调用 GetCommandLine 和 CommandLineToArgvW 所获得的内容是不一样的。以上已列出了其中一种调用方式,传递各种不同参数, 子进程获得命令行参数的结果。

 

接下去,需要修改 AppCaller 的代码,对 CreateProcess 使用两种调用方式,观察它对子进程的影响。

新的 AppCaller代码为:

[cpp]  view plain copy
  1. //C++ code  
  2. //Console project.  
  3. //(New)AppCaller.cpp  
  4.   
  5. #include <stdio.h>  
  6. #include <tchar.h>  
  7. #include <Windows.h>  
  8.   
  9. //函数前导声明。  
  10. void CallApp(LPCTSTRLPTSTR);  
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.     //假设 WinApp.exe 和 CmdApp.exe 在当前目录中。  
  15.     BOOL bRet = FALSE;  
  16.       
  17.     // 构建一组 ConApp 命令行测试用例。  
  18.     // ----------------------------------------------------------------------------------  
  19.     LPCTSTR pszConApp0 = _T("ConApp.exe"); //szCmdLine0 is NULL.  
  20.     LPCTSTR pszConApp1 = _T("ConApp.exe"); TCHAR szConCmdLine1[] = { _T("/123") };  
  21.     LPCTSTR pszConApp2 = _T("ConApp.exe"); TCHAR szConCmdLine2[] = { _T("/t/123") };  
  22.     LPCTSTR pszConApp3 = _T("ConApp.exe"); TCHAR szConCmdLine3[] = { _T(" /123") };  
  23.     LPCTSTR pszConApp4 = _T("ConApp.exe"); TCHAR szConCmdLine4[] = { _T("ConApp.exe") };  
  24.     LPCTSTR pszConApp5 = _T("ConApp.exe"); TCHAR szConCmdLine5[] = { _T("/"ConApp.exe/"") };  
  25.     // ----------------------------------------------------------------------------------  
  26.     LPCTSTR pszConApp6 = NULL;             TCHAR szConCmdLine6[] = { _T("ConApp.exe") };  
  27.     LPCTSTR pszConApp7 = NULL;             TCHAR szConCmdLine7[] = { _T("ConApp.exe /123") };  
  28.     LPCTSTR pszConApp8 = NULL;             TCHAR szConCmdLine8[] = { _T("ConApp.exe /t/123") };  
  29.     LPCTSTR pszConApp9 = NULL;             TCHAR szConCmdLine9[] = { _T("ConApp.exe  /123") };  
  30.     LPCTSTR pszConApp10 = NULL;            TCHAR szConCmdLine10[] = { _T("ConApp.exe ConApp.exe") };  
  31.     LPCTSTR pszConApp11 = NULL;            TCHAR szConCmdLine11[] = { _T("ConApp.exe /"ConApp.exe/"") };  
  32.       
  33.     LPCTSTR ppszConApps[] =  
  34.     {  
  35.         pszConApp0, pszConApp1, pszConApp2, pszConApp3,  
  36.         pszConApp4, pszConApp5, pszConApp6, pszConApp7,  
  37.         pszConApp8, pszConApp9, pszConApp10, pszConApp11  
  38.     };  
  39.     LPTSTR ppszConCmdLines[] =  
  40.     {  
  41.         NULL,           szConCmdLine1, szConCmdLine2, szConCmdLine3,  
  42.         szConCmdLine4,  szConCmdLine5, szConCmdLine6, szConCmdLine7,  
  43.         szConCmdLine8,  szConCmdLine9, szConCmdLine10, szConCmdLine11  
  44.     };  
  45.   
  46.     // 构建一组 WinApp 命令行测试用例。  
  47.     // ----------------------------------------------------------------------------------  
  48.     LPCTSTR pszWinApp0 = _T("WinApp.exe"); //szCmdLine0 is NULL.  
  49.     LPCTSTR pszWinApp1 = _T("WinApp.exe"); TCHAR szWinCmdLine1[] = { _T("/123") };  
  50.     LPCTSTR pszWinApp2 = _T("WinApp.exe"); TCHAR szWinCmdLine2[] = { _T("/t/123") };  
  51.     LPCTSTR pszWinApp3 = _T("WinApp.exe"); TCHAR szWinCmdLine3[] = { _T(" /123") };  
  52.     LPCTSTR pszWinApp4 = _T("WinApp.exe"); TCHAR szWinCmdLine4[] = { _T("WinApp.exe") };  
  53.     LPCTSTR pszWinApp5 = _T("WinApp.exe"); TCHAR szWinCmdLine5[] = { _T("/"WinApp.exe/"") };  
  54.     // ----------------------------------------------------------------------------------  
  55.     LPCTSTR pszWinApp6 = NULL;             TCHAR szWinCmdLine6[] = { _T("WinApp.exe") };  
  56.     LPCTSTR pszWinApp7 = NULL;             TCHAR szWinCmdLine7[] = { _T("WinApp.exe /123") };  
  57.     LPCTSTR pszWinApp8 = NULL;             TCHAR szWinCmdLine8[] = { _T("WinApp.exe /t/123") };  
  58.     LPCTSTR pszWinApp9 = NULL;             TCHAR szWinCmdLine9[] = { _T("WinApp.exe  /123") };  
  59.     LPCTSTR pszWinApp10 = NULL;            TCHAR szWinCmdLine10[] = { _T("WinApp.exe WinApp.exe") };  
  60.     LPCTSTR pszWinApp11 = NULL;            TCHAR szWinCmdLine11[] = { _T("WinApp.exe /"WinApp.exe/"") };  
  61.   
  62.     LPCTSTR ppszWinApps[] =  
  63.     {  
  64.         pszWinApp0, pszWinApp1, pszWinApp2, pszWinApp3,  
  65.         pszWinApp4, pszWinApp5, pszWinApp6, pszWinApp7,  
  66.         pszWinApp8, pszWinApp9, pszWinApp10, pszWinApp11  
  67.     };  
  68.     LPTSTR ppszWinCmdLines[] =  
  69.     {  
  70.         NULL,           szWinCmdLine1, szWinCmdLine2, szWinCmdLine3,  
  71.         szWinCmdLine4,  szWinCmdLine5, szWinCmdLine6, szWinCmdLine7,  
  72.         szWinCmdLine8,  szWinCmdLine9, szWinCmdLine10, szWinCmdLine11  
  73.     };  
  74.   
  75.     // 进行测试。  
  76.     for (size_t i = 0; i < _countof(ppszConApps); i++)  
  77.     {  
  78.         CallApp(ppszConApps[i], ppszConCmdLines[i]);  
  79.     }  
  80.   
  81.     /* 
  82.     for (size_t i = 0; i < _countof(ppszWinApps); i++) 
  83.     { 
  84.         CallApp(ppszWinApps[i], ppszWinCmdLines[i]); 
  85.     } 
  86.     */  
  87.     return 0;  
  88. }  
  89.   
  90. void CallApp(LPCTSTR pszApp, LPTSTR pszCmdLine)  
  91. {  
  92.     static int c = 0;  
  93.     BOOL bRet = FALSE;  
  94.     STARTUPINFO si = { sizeof(si) };  
  95.     PROCESS_INFORMATION pi = { 0 };  
  96.   
  97.     _tprintf(_T("------------------------------------------/r/n/r/n"));  
  98.     _tprintf(_T("<CASE %d>/r/n/r/n"), c);  
  99.     c++;  
  100.     _tprintf(_T("pszCmdLine -- %s/r/n"),  
  101.         pszCmdLine == NULL ? _T("NULL") : pszCmdLine);  
  102.   
  103.     bRet = CreateProcess(pszApp, pszCmdLine, NULL, NULL, FALSE, 0,  
  104.         NULL, NULL, &si, &pi);  
  105.     if (bRet != FALSE)  
  106.     {  
  107.         if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, INFINITE))  
  108.         {  
  109.             CloseHandle(pi.hThread);  
  110.             CloseHandle(pi.hProcess);  
  111.         }  
  112.     }  
  113.     _tprintf(_T("/r/n------------------------------------------/r/n"));  
  114. }  
  115.   
  116. //end of (New)AppCaller.cpp  

 

输出结果为:

------------------------------------------

<CASE 0>

pszCmdLine -- NULL
GetCommandLineW="ConApp.exe"(2200 4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6
500 2200)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 1>

pszCmdLine -- /123
GetCommandLineW=/123(2f00 3100 3200 3300)

CommandLineToArgvW:
argc=1
argv:
[#0]/123(2f00 3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 2>

pszCmdLine --   /123
GetCommandLineW=        /123(0900 2f00 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]()
[#1]/123(2f00 3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 3>

pszCmdLine --  /123
GetCommandLineW= /123(2000 2f00 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]()
[#1]/123(2f00 3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 4>

pszCmdLine -- ConApp.exe
GetCommandLineW=ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 5>

pszCmdLine -- "ConApp.exe"
GetCommandLineW="ConApp.exe"(2200 4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6
500 2200)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 6>

pszCmdLine -- ConApp.exe
GetCommandLineW=ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

CommandLineToArgvW:
argc=1
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 7>

pszCmdLine -- ConApp.exe /123
GetCommandLineW=ConApp.exe /123(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 650
0 2000 2f00 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)
[#1]/123(2f00 3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 8>

pszCmdLine -- ConApp.exe        /123
GetCommandLineW=ConApp.exe      /123(4300 6f00 6e00 4100 7000 7000 2e00 6500 780
0 6500 2000 0900 2f00 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)
[#1]/123(2f00 3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 9>

pszCmdLine -- ConApp.exe  /123
GetCommandLineW=ConApp.exe  /123(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 65
00 2000 2000 2f00 3100 3200 3300)

CommandLineToArgvW:
argc=2
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)
[#1]/123(2f00 3100 3200 3300)

------------------------------------------
------------------------------------------

<CASE 10>

pszCmdLine -- ConApp.exe ConApp.exe
GetCommandLineW=ConApp.exe ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 78
00 6500 2000 4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

CommandLineToArgvW:
argc=2
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)
[#1]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------
------------------------------------------

<CASE 11>

pszCmdLine -- ConApp.exe "ConApp.exe"
GetCommandLineW=ConApp.exe "ConApp.exe"(4300 6f00 6e00 4100 7000 7000 2e00 6500
7800 6500 2000 2200 4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500 2200)

CommandLineToArgvW:
argc=2
argv:
[#0]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)
[#1]ConApp.exe(4300 6f00 6e00 4100 7000 7000 2e00 6500 7800 6500)

------------------------------------------

 

对于新的输出内容进行比对,例如对 CASE 1 和 CASE 7 进行比对可以发现:

 

CASE 1 调用 CreateProcess ,为第一个参数传递了子进程的文件名 ConApp.exe ,为第二个参数传递了1个参数 /123 ,子进程调用 GetCommandLine 获得的字符窜为 /123 ,调用 CommandLineToArgvW 后, argc 等于 1 , argv[0] 为 /123 ;

 

CASE 7 调用 CreateProcess ,为第一个参数传递 NULL ,为第二个参数传递 ConApp.exe /123 ,子进程调用 GetCommandLine 获得的字符串为 ConApp.exe /123 ,调用 CommandLineToArgvW 后, argc 等于 2 , argv[0] 为 ConApp.exe , argv[1] 为 /123 。

 

最后,总结来看无论是解析接受到的命令行参数,还是作为父进程创建子进程并需要为子进程传递参数时,都应该要小心,否则被调用的子进程会出现预期之外的结果。一般而言,开发解析命令行参数的程序时, 规定传递的参数要带参数前缀,如 / 、 - 等常用的参数前缀。在开发创建子进程的程序时,如果调用 CreateProcess ,将第一个参数设置为 NULL ,为第二个参数传递包含子进程文件名的完整命令行参数。需要小心的是第一个参数类型是一个 const 类型的,这通常没问题,第二个参数不是一个 const 类型,是一个 LPTSTR ,这要求只能自己构造一个命令行缓冲区传递给 CreateProcess ,传递一个字符串常量就会调用失败。还有就是第一个参数指定子进程的文件名不能省略文件扩展名,第二个参数指定子进程的文件名时,可以省略扩展名。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值