这些参数帮助我们为程序传入命令行参数。"argc"为命令行参数的个数,"argv"则为传入参数的数组列表。但是当我们在Visual Studio中创建Win32 GUI程序的时候,WinMain变成程序的入口函数,而该函数并没有"argc" 和"argv"参数,那我们怎样给Windows程序传入命令行参数呢?Windows程序中又怎样取得这些传入的参数呢?
第一个方案就来自WinMain函数自身。让我们看一个典型的WinMain函数声明:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
test.exe Some arguments here
变量lpCmdLine的值即为"Some arguments here"。尽管该方法不像"argc"和"argv"一样非常方便,但它是获取命令行参数的方法之一。我们需要自己写程序去分析lpCmdLine字符串,这增加了程序的复杂度。
【译注:Windows程序代码同时ANSI版本和UNICODE版本接口。其中WinMain函数为ANSI版本,wWinMain为UNICODE版本。从Visual Studio创建出来的代码主函数命名为_tWinMain。这个函数名会根据当前工程有没有定义_UNICODE宏而在编译时翻译成上面两个不同版本。当翻译成WinMain函数时候,lpCmdLine的类型为LPSTR,而当翻译成wWinMain函数时候,lpCmdLine的类型为 LPWSTR,即宽字符数组】
另外一个方法就是使用GetCommandLine() API。这个函数返回整个命令行,它把程序自身名称(包括程序的绝对路径)和所有参数放在一个字符串中。该函数非常类似于对lpCmdLine的直接访问。但它的一个好处是能够根据你当前工程的设置自动映射到GetCommandLineA()或者GetCommandLineW()函数。因此解决了访问Unicode命令行输入的问题。但是它还是既没有提供命令行参数数目,也没有类似argv那样把参数自动分割成独立变量的能力。
最后一个我要讨论的方法是CommandLineToArgvW函数。这个函数只有Unicode宽字符版本,没有对应的CommandLineToArgvA函数。它的声明如下:
LPWSTR *CommandLineToArgvW(LPCWSTR lpCmdLine, int *pNumArgs)
该函数和’argc’/'argv’一样简单,但是它并不是在Windows程序中直接访问argc和argv变量。如声明所示,函数接受两个参数,一个是需要解析的Unicode命名行字符串,另外一个是指向整型变量的指针。函数在返回时把参数数目存到这个整型变量中。
函数返回一个类似于’argv’的字符串数组。让我们看一个例子:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd)
{
LPWSTR *szArgList;
int argCount;
szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
if (szArgList == NULL)
{
MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK);
return 10;
}
for(int i = 0; i < argCount; i++)
{
MessageBox(NULL, szArgList[i], L"Arglist contents", MB_OK);
}
LocalFree(szArgList);
return 0;
}
如上所示,通过这个函数,我们可以取得命令行参数的数目(argc)和一个字符串列表(argv)。这里唯一要注意的事情是该函数会给返回参数列表分配一块内存。当我们用完列表后需要手动地释放该内存,否则就会有内存泄露。
因为Windows没有给出CommandLineToArgvA的实现,所以我们自己实现一个,如下:
commonLineHelp.cpp
#ifdef _WIN32
#include
#include
using namespace std;
string wstr_to_str(const wstring & wstr, UINT CodePage = CP_OEMCP);
string wstr_to_str(const wstring & wstr, UINT CodePage)
{
string str;
int buffer_size = 0;
char* pchar_str = NULL;
buffer_size = WideCharToMultiByte(CodePage, NULL,wstr.c_str(), -1, 0, 0, NULL, FALSE);
pchar_str = new char[buffer_size + 1];
if (pchar_str == NULL)
{
return str;
}
memset(pchar_str, 0, buffer_size + 1);
if (WideCharToMultiByte(CodePage, NULL, wstr.c_str(), -1, pchar_str, buffer_size, NULL, FALSE))
{
str = pchar_str;
}
delete pchar_str;
return str;
}
#endif
LPSTR * CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs)
{
LPWSTR *szArgList;
int argCount;
if ( NULL == lpCmdLine)
{
*pNumArgs = 0;
return NULL;
}
szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
if (szArgList == NULL)
{
*pNumArgs = 0;
return NULL;
}
char **szArgListA = NULL;
szArgListA = (char**)LocalLock(LocalAlloc(LMEM_FIXED, sizeof(char*)*argCount));
for(int i = 0; i < argCount; i++)
{
string str = wstr_to_str(szArgList[i]);
szArgListA[i] = (char*)LocalLock(LocalAlloc(LMEM_FIXED, str.length()));
strcpy(szArgListA[i], str.c_str());
}
LocalFree(szArgList);
*pNumArgs = argCount;
return szArgListA;
}
#ifndef __COMMONLINEHELP__
#define __COMMONLINEHELP__
/**
* @brief 将应用程序的命令行转化为命令行参数列表(返回字符中是窄字节)
*
* @param[in] lpCmdLine 等转化的命令行字符串
* @param[out] pNumArgs 转化后的命令行参数个数
*
* @return 命令行参数列表
*
* @note
* 返回值在使用完后记得调用LocalFree释放掉
*/
LPSTR * CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs);
#endif
LPSTR * CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs)
{
LPWSTR *szArgList;
int argCount;
BOOL bRet = FALSE;
if ( NULL == lpCmdLine)
{
*pNumArgs = 0;
return NULL;
}
szArgList = CommandLineToArgvW(lpCmdLine, &argCount);
if (szArgList == NULL)
{
*pNumArgs = 0;
return NULL;
}
char **szArgListA = NULL;
HLOCAL hLocalList = NULL, hLocalListItem = NULL;
#if 0
hLocalList = LocalAlloc(LMEM_FIXED, sizeof(char*)*argCount);
if ( !hLocalList )
{
goto T_OUT;
}
szArgListA = (char**)LocalLock(hLocalList);
#endif
szArgListA = (char**)malloc(sizeof(char*)*argCount);
for(int i = 0; i < argCount; i++)
{
string str = wstr_to_str(szArgList[i]);
#if 0
hLocalListItem = LocalAlloc(LMEM_FIXED, str.length());
szArgListA[i] = (char*)LocalLock(hLocalListItem);
#endif
szArgListA[i] = (char*)malloc(str.length());
strcpy(szArgListA[i], str.c_str());
//LocalUnlock(hLocalListItem);
}
LocalFree(szArgList);
*pNumArgs = argCount;
if ( FALSE == (bRet = LocalUnlock(hLocalList)) )
{
}
T_OUT:
return szArgListA;
}
之前版本为每个命令行的字符串申请空间(malloc)时,指定大小是str.length(),这是字符串不包括\0的串长,这样会导致字符串的\0不能复制过来,而导致串没有结束,
新生的后果非常严重:
1、打印信息如下:
我在上一版本中加入打印信息,代码如:
char** CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs)
{
LPWSTR *szArgList;
int argCount;
BOOL bRet = FALSE;
if ( NULL == lpCmdLine)
{
*pNumArgs = 0;
return NULL;
}
#if 1
OutputDebugString("==========Conver String.Original Status\n");
OutputDebugStringW(lpCmdLine);
#endif
szArgList = CommandLineToArgvW(lpCmdLine, &argCount);
if (szArgList == NULL)
{
*pNumArgs = 0;
return NULL;
}
char **szArgListA = NULL;
HLOCAL hLocalList = NULL, hLocalListItem = NULL;
szArgListA = (char**)malloc(sizeof(char*)*argCount);
memset(szArgListA, 0x0, sizeof(char*)*argCount);
OutputDebugString("==========Conver String ...\n");
for(int i = 0; i < argCount; i++)
{
string str = wstr_to_str(szArgList[i]);
szArgListA[i] = (char*)malloc(str.length());
memset(szArgListA[i], 0x0, str.length());
strncpy(szArgListA[i], str.c_str(), str.length());
szArgListA[i][str.length()] = '\0';
#if 1
OutputDebugString(str.c_str());
OutputDebugString(szArgListA[i]);
//L_TRACE("len=%d\n", strlen(szArgListA[i]));
#endif
//LocalUnlock(hLocalListItem);
}
#if 1
OutputDebugString("==========Convert String result:\n");
for (int i = 0; i < argCount; i++)
{
OutputDebugString(szArgListA[i]);
}
#endif // 0
LocalFree(szArgList);
*pNumArgs = argCount;
if ( FALSE == (bRet = LocalUnlock(hLocalList)) )
{
}
T_OUT:
return szArgListA;
}
后得到的打印如下:
00001088 2.38102412 [1180] ==========Conver String.Original Status
00001089 2.38104796 [1180] "D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe" -g 1080*768 -u xred -p xred 192.168.4.151
00001090 2.38108373 [1180] ==========Conver String ...
00001091 2.38111877 [1180] D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe
00001092 2.38114262 [1180] D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe
00001093 2.38116908 [1180] -g
00001094 2.38119221 [1180] -g
00001095 2.38121915 [1180] 1080*768
00001096 2.38124251 [1180] 1080*768
00001097 2.38126826 [1180] -u
00001098 2.38129187 [1180] -u
00001099 2.38131785 [1180] xred
00001100 2.38134098 [1180] xred
00001101 2.38136673 [1180] -p
00001102 2.38138986 [1180] -p
00001103 2.38141584 [1180] xred
00001104 2.38143897 [1180] xred
00001105 2.38146520 [1180] 192.168.4.151
00001106 2.38148880 [1180] 192.168.4.151
00001107 2.38151193 [1180] ==========Convert String result:
00001108 2.38153529 [1180] D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe
00001109 2.38155818 [1180] -g
00001110 2.38158154 [1180] 1080*768]
00001111 2.38160467 [1180] -u
00001112 2.38162780 [1180] xred
00001113 2.38165069 [1180] -p
00001114 2.38167381 [1180] xred
00001115 2.38169694 [1180] 192.168.4.151
这样导致了我们解析字符中出错而已。
2、除1、中所讲的危害外,还会引起整个进程的意常发生,时不时会提示“地址XXXX不能被读”。
(1)在CreateThread时不进入线程回调函数了,且弹出系统访问内存错误码的提示。
(2)
只修改一行代码:
将
szArgListA[i] = (char*)malloc(str.length());
szArgListA[i] = (char*)malloc(str.length()+1);
原因:自然不用说了,很明显。!!!!
因为没有结束符,所以可能访问到不是我们申请的空间,这时没有报错。而这块空间可能后来被别人申请了,这样就会导致别人的内存被我们修改了。