入口函数 main 和 WinMain 的返回值

http://blog.csdn.net/brantyou/article/details/7308004

自一开始接触 C 语言 和后来的 Windows 编程,我们就知道必须为程序定义一个入口函数 main 或 WinMain 。且已知的 main 函数可以提供一个返回值 int 或者不返回任何数值,即定义为 void main() / void main (int argc, char* argv[] /*, char* env[]*/)等形式。而相对应的WinMain函数作为窗口程序的入口函数,它的返回值必须定义成int 类型 ,且程序返回时,应当返回一个数值。

我们知道,当 main 或 WinMain 的return语句被执行后,应用程序也就退出了,那么既然程序已经退出了,return后面的那个值究竟又到什么地方去了,谁又会等候该值呢?毕竟在一般的函数譬如: HDC GetDC(); 返回的 HDC 自然是给调用 GetDC() 函数的主调程序所使用的,因此在设计 GetDC 这个函数时,设计者很明确返回值会被谁使用,可是,入口函数的返回值被谁使用并不是很明确。

在著作《Windows核心编程》中,作者J.Richter在进程这一章指出, 入口函数会被C运行时库(CRT)调用。且VS配套提供了这个库函数mainCRTStartup / WinMainCRTStartup 的源代码文件<crtexe.c>。顺着作者的指引,在该书中可以找到 mainCRTStrartup / WinMainCRTStartup 这个 exe 文件真正的入口函数以及它会调用我们的 main / WinMain 函数这一事实。顺着调用 main / WinMain 函数的代码往下继续查看,会发现,如果 mainCRTStartup / WinMainCRTStartup 在后续的清理过程中未检测到其它的错误,它会把 main / WinMain 函数返回的这个 int 值放到 exit(int) 这个标准的C函数中,自从学C语言以来,我们就知道,exit 函数是应用程序退出的标准函数,且查看 MSDN 关于 exit 函数的说明,文档明确指出返回值可以被 Windows 的 batch command (批处理命令)所使用。

即,如果有个程序名为c:/a.exe,我们可以为该程序建立一个批处理文件 exec_a.bat, 在文件中输入一行命令 a.exe , 就可以执行该程序,在第二行中输入@echo %errorlevel%。当a.exe结束后,echo命令会把a.exe通过 main / WinMain 函数返回的值打印在控制台上,原理是当exit(int)执行后,退出码会被放到 errorlevel 中,我们只需查看 errorlevel 就能知道程序的返回值究竟是多少,从而在批处理中,根据程序的返回值做其他的工作,譬如可以写一个这样的批处理文件:

 

::关闭回显。

@echo off

::执行a.exe。

@a.exe

::如果返回值是0,打印 正确 ,如果返回1,打印 错误 。

@if errorlevel 1 echo 错误 & goto end

@echo 正确

:end

::打开回显

echo on

 

对于控制台程序,我们不必建立批处理文件,可以直接在命令提示符后面执行程序 a 回车,然后等程序执行完了立即再执行 echo %errorlevel% ,就能知道入口函数的返回值是多少了。但由于控制台程序无法调用win32程序,因此不能使用 echo %errorlevel% 的办法来查看返回值。只能提供一个批处理文件,然后以批处理文件为媒介,执行a.exe和返回值的查看工作。当然,批处理文件本身可以通过在资源管理器中双击执行或在控制台上执行,不管被执行的是控制台程序还是win32窗口程序都不会有问题。

 

以下,给出两个测试程序,它将解析命令提示符传送给它的参数,如果能解析成一个数字,就将结果显示在控制台上(ConsoleApp.exe)或通过消息框(WindowsApp.exe)进行显示,最后返回 0,表示没有错误。遇上错误,会返回 1 或 2。最后建立两个批处理文件 ConsoleAppTest.bat 和 WindowsAppTest.bat 来进行测试。

 

ConsoleApp:

[cpp]  view plain copy
  1. // C++ code  
  2. // ConsoleApp.cpp  
  3. #include <stdio.h>  
  4. #include <tchar.h>  
  5. #include <Windows.h>  
  6.   
  7. // 函数前导声明。  
  8. void Usage();  
  9. BOOL __stdcall Str2iNumW(LPCWSTRint *, BOOL);  
  10. BOOL __stdcall Str2iNumA(LPCSTRint *, BOOL);  
  11.   
  12. // 宏定义。  
  13. #undef Str2iNum  
  14. #ifdef _UNICODE  
  15. #define Str2iNum Str2iNumW  
  16. #else  
  17. #define Str2iNum Str2iNumA  
  18. #endif  
  19.   
  20. int _tmain(int argc, _TCHAR* argv[])  
  21. {  
  22.     int nRet = 0;  
  23.     int num = 0;  
  24.     BOOL bRet = FALSE;  
  25.     DWORD dwErr = 0;  
  26.     LPCTSTR pParam = NULL;  
  27.   
  28.     _tprintf(_T("/r/nConsole App is running.../r/n/r/n"));  
  29.   
  30.     if (argc == 2)  
  31.     {  
  32.         pParam = argv[1];  
  33.         if (*pParam == _T('/'))  
  34.         {  
  35.             pParam++;  
  36.             bRet = Str2iNum(pParam, &num, FALSE);  
  37.             if (bRet != FALSE)  
  38.             {  
  39.                 _tprintf(_T("The number is %d./r/n"), num);  
  40.             }  
  41.             else  
  42.             {  
  43.                 dwErr = GetLastError();  
  44.                 if (dwErr == ERROR_INVALID_DATA)  
  45.                 {  
  46.                     _tprintf(_T("ERROR: the number '%s' is too large./r/n"), pParam);  
  47.                     _tprintf(_T("Please use the number between -2147483648 and 2147483647 !/r/n"));  
  48.                 }  
  49.                 else  
  50.                 {  
  51.                     _tprintf(_T("ERROR: invalid parameter '%s'./r/n"), argv[1]);  
  52.                     Usage();  
  53.                 }  
  54.                 nRet = 2;  
  55.             }  
  56.         }  
  57.         else  
  58.         {  
  59.             _tprintf(_T("ERROR: invalid parameter '%s'./r/n"), argv[1]);  
  60.             Usage();  
  61.             nRet = 2;  
  62.         }  
  63.     }  
  64.     else if (argc == 1)  
  65.     {  
  66.         Usage();  
  67.         nRet = 1;  
  68.     }  
  69.     else  
  70.     {  
  71.         _tprintf(_T("ERROR: invalid parameter '"));  
  72.         for (int i = 1; i < argc; i++)  
  73.         {  
  74.             if (i != 1) _puttchar(_T(' '));  
  75.             _tprintf(argv[i]);  
  76.         }  
  77.         _tprintf(_T("'./r/n"));  
  78.         Usage();  
  79.         nRet = 2;  
  80.     }  
  81.   
  82.     _tprintf(_T("/r/nConsole App will return with value '%d'./r/n/r/n"), nRet);  
  83.     //返回值为0,表示没有错误。  
  84.     //返回值为1,表示一般错误。  
  85.     //返回值为2,表示较严重错误。                          
  86.     return nRet;  
  87. }  
  88.   
  89. // 程序使用方法。  
  90. void Usage()  
  91. {  
  92.     _tprintf(_T("Usage: ConsoleApp /number/r/n"));  
  93. }  
  94.   
  95. // 字符串转换成int型整数的函数。  
  96. BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead)  
  97. {  
  98.     __int64 num = 0;  
  99.     int sign = 1;  
  100.     size_t len = 0;  
  101.     BOOL bRet = FALSE;  
  102.     WCHAR ch = 0;  
  103.   
  104.     if (pszNum == NULL || pnNum == NULL)  
  105.     {  
  106.         SetLastError(ERROR_INVALID_PARAMETER);  
  107.         return bRet;  
  108.     }  
  109.   
  110.     //跳过前导空格。  
  111.     if (bSpaceLead == TRUE)  
  112.     {  
  113.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  114.             pszNum++;  
  115.     }  
  116.   
  117.     //跳过符号位。  
  118.     ch = *pszNum;  
  119.     if (ch == _T('-'))  
  120.     {  
  121.         sign = -1;  
  122.         pszNum++;  
  123.     } else if (ch == _T('+'))  
  124.     {  
  125.         pszNum++;  
  126.     }  
  127.   
  128.     //非数字字符。  
  129.     if (*pszNum < _T('0') || *pszNum > _T('9'))  
  130.     {  
  131.         SetLastError(ERROR_BAD_FORMAT);  
  132.         return bRet;  
  133.     }  
  134.   
  135.     //第一个字符是数字字符"0"。  
  136.     if (*pszNum == _T('/0'))  
  137.     {  
  138.         if (pszNum[1] == _T('/0'))  
  139.         {  
  140.             *pnNum = 0;  
  141.             return TRUE;  
  142.         }  
  143.         else  
  144.         {  
  145.             SetLastError(ERROR_BAD_FORMAT);  
  146.             return bRet;  
  147.         }  
  148.     }  
  149.   
  150.     while (TRUE)  
  151.     {  
  152.         ch = *pszNum++;  
  153.   
  154.         //数字字符。  
  155.         if (ch >= _T('0') && ch <= _T('9'))  
  156.         {  
  157.             len++;  
  158.   
  159.             //int型整数最多十位。  
  160.             if (len > 10)  
  161.             {  
  162.                 SetLastError(ERROR_INVALID_DATA);  
  163.                 break;  
  164.             }  
  165.   
  166.             num = num * 10 + (ch - _T('0'));  
  167.   
  168.             continue;  
  169.         }  
  170.         //字符串终止符。  
  171.         else if (ch == _T('/0'))  
  172.         {  
  173.             num = sign * num;  
  174.             if (num <= INT_MAX && num >= INT_MIN)  
  175.             {  
  176.                 *pnNum = (int)num;  
  177.                 bRet = TRUE;  
  178.                 break;  
  179.             }  
  180.             //溢出。  
  181.             else  
  182.             {  
  183.                 SetLastError(ERROR_INVALID_DATA);  
  184.                 break;  
  185.             }  
  186.         }  
  187.         //非数字字符。  
  188.         else  
  189.         {  
  190.             SetLastError(ERROR_BAD_FORMAT);  
  191.             break;  
  192.         }  
  193.     }  
  194.   
  195.     return bRet;  
  196. }  
  197.   
  198. BOOL __stdcall Str2iNumA(LPCSTR pszNum, int *pnNum, BOOL bSpaceLead)  
  199. {  
  200.     //int类型的数字最多是10位,大于10的最小2次幂是16。  
  201.     WCHAR szNum[16] = { 0 };  
  202.     int nRet = 0;  
  203.     size_t cbLen = 0;  
  204.   
  205.     if (pszNum == NULL || pnNum == NULL)  
  206.     {  
  207.         SetLastError(ERROR_INVALID_PARAMETER);  
  208.         return FALSE;  
  209.     }  
  210.   
  211.     //跳过前导空格。  
  212.     if (bSpaceLead == TRUE)  
  213.     {  
  214.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  215.             pszNum++;  
  216.     }  
  217.   
  218.     cbLen = strnlen_s(pszNum, 16);  
  219.   
  220.     //过滤较长的恶意字符串。  
  221.     if (cbLen == 16)  
  222.     {  
  223.         SetLastError(ERROR_BAD_FORMAT);  
  224.         return FALSE;  
  225.     }  
  226.   
  227.     nRet = MultiByteToWideChar(CP_ACP, 0, pszNum, -1, szNum, 16);  
  228.     if (nRet == 0)  
  229.     {  
  230.         return FALSE;  
  231.     }  
  232.   
  233.     return Str2iNumW(szNum, pnNum, bSpaceLead);  
  234. }  
  235. // end of ConsoleApp.cpp  

 

WindowsApp:

[cpp]  view plain copy
  1. // C++ code  
  2. // WindowsApp.cpp  
  3. #include <Windows.h>  
  4. #include <tchar.h>  
  5. #include <strsafe.h>  
  6.   
  7. //使用XP风格的控件。  
  8. #if defined _M_IX86  
  9. #pragma comment(linker,"/manifestdependency:/"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'/"")  
  10. #endif  
  11.   
  12. //函数前导声明。  
  13. BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead);  
  14.   
  15. int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/,  
  16.     LPTSTR pszCmdLine, int nShowWindow)  
  17. {  
  18.     int nRet = 0;  
  19.     int num = 0;  
  20.     int argc = 0;  
  21.     DWORD dwErr = 0;  
  22.     HRESULT hr = S_OK;  
  23.     BOOL bRet = FALSE;  
  24.     LPWSTR *argv = NULL;  
  25.     LPWSTR pszCmdLineW = NULL;  
  26.     LPCWSTR pParam = NULL;  
  27.     TCHAR szText[256] = { 0 };  
  28.   
  29.     //获取分割后的命令行参数。  
  30.     pszCmdLineW = GetCommandLineW();  
  31.     argv = CommandLineToArgvW(pszCmdLine, &argc);  
  32.       
  33.     if (argc == 1)  
  34.     {  
  35.         pParam = argv[0];  
  36.         if (*pParam == _T('/'))  
  37.         {  
  38.             pParam++;  
  39.             bRet = Str2iNumW(pParam, &num, FALSE);  
  40.             if (bRet != FALSE)  
  41.             {  
  42.                 hr = StringCchPrintf(szText, _countof(szText),  
  43.                     _T("The number is %d./r/n")  
  44.                     _T("Windows App will return with value '%d'."), num, nRet);  
  45.             }  
  46.             else  
  47.             {  
  48.                 nRet = 1;  
  49.                 dwErr = GetLastError();  
  50.                 if (dwErr == ERROR_INVALID_DATA)  
  51.                 {  
  52.                     hr = StringCchPrintf(szText, _countof(szText),  
  53.                         _T("ERROR: the number '%s' is too large./r/n")  
  54.                         _T("Please use the number between ")  
  55.                         _T("-2147483648 and 2147483647 !/r/n")  
  56.                         _T("Windows App will return with value '%d'."),  
  57.                         pParam, nRet);  
  58.                 }  
  59.                 else  
  60.                 {  
  61.                     hr = StringCchPrintf(szText, _countof(szText),  
  62.                         _T("ERROR: invalid parameter '%s'./r/n")  
  63.                         _T("Windows App will return with value '%d'."),  
  64.                         argv[0], nRet);  
  65.                 }  
  66.             }  
  67.         }  
  68.         else  
  69.         {  
  70.             nRet = 1;  
  71.             hr = StringCchPrintf(szText, _countof(szText),  
  72.                 _T("ERROR: invalid parameter '%s'./r/n")  
  73.                 _T("Windows App will return with value '%d'."), argv[0], nRet);  
  74.         }  
  75.     }  
  76.     else  
  77.     {  
  78.         nRet = 1;  
  79.         hr = StringCchPrintf(szText, _countof(szText),  
  80.             _T("ERROR: invalid parameter '%s'./r/n")  
  81.             _T("Windows App will return with value '%d'."),  
  82.             pszCmdLine, nRet);  
  83.     }  
  84.   
  85.     if (argv != NULL) LocalFree(argv);  
  86.   
  87.     if ((SUCCEEDED(hr)) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)  
  88.     {  
  89.         MessageBox(NULL, szText, _T("Windows App"), MB_OK | MB_ICONINFORMATION);  
  90.     }  
  91.   
  92.     //返回值为0,表示没有错误。  
  93.     //返回值为1,表示一般错误。  
  94.     //返回值为2,表示较严重错误。                          
  95.     return nRet;  
  96. }  
  97.   
  98. //字符串转换成int型整数的函数。  
  99. BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead)  
  100. {  
  101.     __int64 num = 0;  
  102.     int sign = 1;  
  103.     size_t len = 0;  
  104.     BOOL bRet = FALSE;  
  105.     WCHAR ch = 0;  
  106.   
  107.     if (pszNum == NULL || pnNum == NULL)  
  108.     {  
  109.         SetLastError(ERROR_INVALID_PARAMETER);  
  110.         return bRet;  
  111.     }  
  112.   
  113.     //跳过前导空格。  
  114.     if (bSpaceLead == TRUE)  
  115.     {  
  116.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  117.             pszNum++;  
  118.     }  
  119.   
  120.     //跳过符号位。  
  121.     ch = *pszNum;  
  122.     if (ch == _T('-'))  
  123.     {  
  124.         sign = -1;  
  125.         pszNum++;  
  126.     } else if (ch == _T('+'))  
  127.     {  
  128.         pszNum++;  
  129.     }  
  130.   
  131.     //非数字字符。  
  132.     if (*pszNum < _T('0') || *pszNum > _T('9'))  
  133.     {  
  134.         SetLastError(ERROR_BAD_FORMAT);  
  135.         return bRet;  
  136.     }  
  137.   
  138.     //第一个字符是数字字符"0"。  
  139.     if (*pszNum == _T('/0'))  
  140.     {  
  141.         if (pszNum[1] == _T('/0'))  
  142.         {  
  143.             *pnNum = 0;  
  144.             return TRUE;  
  145.         }  
  146.         else  
  147.         {  
  148.             SetLastError(ERROR_BAD_FORMAT);  
  149.             return bRet;  
  150.         }  
  151.     }  
  152.   
  153.     while (TRUE)  
  154.     {  
  155.         ch = *pszNum++;  
  156.   
  157.         //数字字符。  
  158.         if (ch >= _T('0') && ch <= _T('9'))  
  159.         {  
  160.             len++;  
  161.   
  162.             //int型整数最多十位。  
  163.             if (len > 10)  
  164.             {  
  165.                 SetLastError(ERROR_INVALID_DATA);  
  166.                 break;  
  167.             }  
  168.   
  169.             num = num * 10 + (ch - _T('0'));  
  170.   
  171.             continue;  
  172.         }  
  173.         //字符串终止符。  
  174.         else if (ch == _T('/0'))  
  175.         {  
  176.             num = sign * num;  
  177.             if (num <= INT_MAX && num >= INT_MIN)  
  178.             {  
  179.                 *pnNum = (int)num;  
  180.                 bRet = TRUE;  
  181.                 break;  
  182.             }  
  183.             //溢出。  
  184.             else  
  185.             {  
  186.                 SetLastError(ERROR_INVALID_DATA);  
  187.                 break;  
  188.             }  
  189.         }  
  190.         //非数字字符。  
  191.         else  
  192.         {  
  193.             SetLastError(ERROR_BAD_FORMAT);  
  194.             break;  
  195.         }  
  196.     }  
  197.   
  198.     return bRet;  
  199. }  
  200. // end of WindowsApp.cpp  

 

ConsoleAppTest.bat

 

::ConsoleAppTest.bat
::清屏
cls
::测试程序返回值
@echo ---------------------------------------
ConsoleApp
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /0
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /-3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp 3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /a
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /3a
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /9876543210
@echo errorlevel %errorlevel%
@echo ---------------------------------------
ConsoleApp /3 /3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
::暂停
@pause
::end of ConsoleAppTest.bat

 

WindowsAppTest.bat

 

::WindowsAppTest.bat
::清屏
cls
::测试程序返回值
@echo ---------------------------------------
WindowsApp
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /0
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /-3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp 3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /a
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /3a
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /9876543210
@echo errorlevel %errorlevel%
@echo ---------------------------------------
WindowsApp /3 /3
@echo errorlevel %errorlevel%
@echo ---------------------------------------
::暂停
@pause
::end of WindowsAppTest.bat

 

在 ConsoleApp.cpp 和 WindowsApp.cpp 中使用了两个自定义函数 Str2iNumW 和 Str2iNumA 。使用它们的理由可以参见我的这篇博客文章“关于atoi字符串转换成int整数的一些问题”,链接地址:http://blog.csdn.net/silvergingko/archive/2010/12/11/6070138.aspx

 

如果在一个程序中通过调用 CreateProcess 函数调用创建了一个子进程,可以调用 WaitForSingleObject 函数等待子进程结束,然后再调用 GetExitCodeProcess ,就可以在代码中实现查看子进程在 main / WinMain 函数中的返回值。


你可能感兴趣的:(编程,windows,测试,command,null,linker)