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:
-
-
- #include <stdio.h>
- #include <tchar.h>
- #include <Windows.h>
-
-
- void Usage();
- BOOL __stdcall Str2iNumW(LPCWSTR, int *, BOOL);
- BOOL __stdcall Str2iNumA(LPCSTR, int *, BOOL);
-
-
- #undef Str2iNum
- #ifdef _UNICODE
- #define Str2iNum Str2iNumW
- #else
- #define Str2iNum Str2iNumA
- #endif
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- int nRet = 0;
- int num = 0;
- BOOL bRet = FALSE;
- DWORD dwErr = 0;
- LPCTSTR pParam = NULL;
-
- _tprintf(_T("/r/nConsole App is running.../r/n/r/n"));
-
- if (argc == 2)
- {
- pParam = argv[1];
- if (*pParam == _T('/'))
- {
- pParam++;
- bRet = Str2iNum(pParam, &num, FALSE);
- if (bRet != FALSE)
- {
- _tprintf(_T("The number is %d./r/n"), num);
- }
- else
- {
- dwErr = GetLastError();
- if (dwErr == ERROR_INVALID_DATA)
- {
- _tprintf(_T("ERROR: the number '%s' is too large./r/n"), pParam);
- _tprintf(_T("Please use the number between -2147483648 and 2147483647 !/r/n"));
- }
- else
- {
- _tprintf(_T("ERROR: invalid parameter '%s'./r/n"), argv[1]);
- Usage();
- }
- nRet = 2;
- }
- }
- else
- {
- _tprintf(_T("ERROR: invalid parameter '%s'./r/n"), argv[1]);
- Usage();
- nRet = 2;
- }
- }
- else if (argc == 1)
- {
- Usage();
- nRet = 1;
- }
- else
- {
- _tprintf(_T("ERROR: invalid parameter '"));
- for (int i = 1; i < argc; i++)
- {
- if (i != 1) _puttchar(_T(' '));
- _tprintf(argv[i]);
- }
- _tprintf(_T("'./r/n"));
- Usage();
- nRet = 2;
- }
-
- _tprintf(_T("/r/nConsole App will return with value '%d'./r/n/r/n"), nRet);
-
-
-
- return nRet;
- }
-
-
- void Usage()
- {
- _tprintf(_T("Usage: ConsoleApp /number/r/n"));
- }
-
-
- BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead)
- {
- __int64 num = 0;
- int sign = 1;
- size_t len = 0;
- BOOL bRet = FALSE;
- WCHAR ch = 0;
-
- if (pszNum == NULL || pnNum == NULL)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
-
- if (bSpaceLead == TRUE)
- {
- while (*pszNum == _T(' ') || *pszNum == _T('/t'))
- pszNum++;
- }
-
-
- ch = *pszNum;
- if (ch == _T('-'))
- {
- sign = -1;
- pszNum++;
- } else if (ch == _T('+'))
- {
- pszNum++;
- }
-
-
- if (*pszNum < _T('0') || *pszNum > _T('9'))
- {
- SetLastError(ERROR_BAD_FORMAT);
- return bRet;
- }
-
-
- if (*pszNum == _T('/0'))
- {
- if (pszNum[1] == _T('/0'))
- {
- *pnNum = 0;
- return TRUE;
- }
- else
- {
- SetLastError(ERROR_BAD_FORMAT);
- return bRet;
- }
- }
-
- while (TRUE)
- {
- ch = *pszNum++;
-
-
- if (ch >= _T('0') && ch <= _T('9'))
- {
- len++;
-
-
- if (len > 10)
- {
- SetLastError(ERROR_INVALID_DATA);
- break;
- }
-
- num = num * 10 + (ch - _T('0'));
-
- continue;
- }
-
- else if (ch == _T('/0'))
- {
- num = sign * num;
- if (num <= INT_MAX && num >= INT_MIN)
- {
- *pnNum = (int)num;
- bRet = TRUE;
- break;
- }
-
- else
- {
- SetLastError(ERROR_INVALID_DATA);
- break;
- }
- }
-
- else
- {
- SetLastError(ERROR_BAD_FORMAT);
- break;
- }
- }
-
- return bRet;
- }
-
- BOOL __stdcall Str2iNumA(LPCSTR pszNum, int *pnNum, BOOL bSpaceLead)
- {
-
- WCHAR szNum[16] = { 0 };
- int nRet = 0;
- size_t cbLen = 0;
-
- if (pszNum == NULL || pnNum == NULL)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
-
-
- if (bSpaceLead == TRUE)
- {
- while (*pszNum == _T(' ') || *pszNum == _T('/t'))
- pszNum++;
- }
-
- cbLen = strnlen_s(pszNum, 16);
-
-
- if (cbLen == 16)
- {
- SetLastError(ERROR_BAD_FORMAT);
- return FALSE;
- }
-
- nRet = MultiByteToWideChar(CP_ACP, 0, pszNum, -1, szNum, 16);
- if (nRet == 0)
- {
- return FALSE;
- }
-
- return Str2iNumW(szNum, pnNum, bSpaceLead);
- }
-
WindowsApp:
-
-
- #include <Windows.h>
- #include <tchar.h>
- #include <strsafe.h>
-
-
- #if defined _M_IX86
- #pragma comment(linker,"/manifestdependency:/"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'/"")
- #endif
-
-
- BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead);
-
- int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE ,
- LPTSTR pszCmdLine, int nShowWindow)
- {
- int nRet = 0;
- int num = 0;
- int argc = 0;
- DWORD dwErr = 0;
- HRESULT hr = S_OK;
- BOOL bRet = FALSE;
- LPWSTR *argv = NULL;
- LPWSTR pszCmdLineW = NULL;
- LPCWSTR pParam = NULL;
- TCHAR szText[256] = { 0 };
-
-
- pszCmdLineW = GetCommandLineW();
- argv = CommandLineToArgvW(pszCmdLine, &argc);
-
- if (argc == 1)
- {
- pParam = argv[0];
- if (*pParam == _T('/'))
- {
- pParam++;
- bRet = Str2iNumW(pParam, &num, FALSE);
- if (bRet != FALSE)
- {
- hr = StringCchPrintf(szText, _countof(szText),
- _T("The number is %d./r/n")
- _T("Windows App will return with value '%d'."), num, nRet);
- }
- else
- {
- nRet = 1;
- dwErr = GetLastError();
- if (dwErr == ERROR_INVALID_DATA)
- {
- hr = StringCchPrintf(szText, _countof(szText),
- _T("ERROR: the number '%s' is too large./r/n")
- _T("Please use the number between ")
- _T("-2147483648 and 2147483647 !/r/n")
- _T("Windows App will return with value '%d'."),
- pParam, nRet);
- }
- else
- {
- hr = StringCchPrintf(szText, _countof(szText),
- _T("ERROR: invalid parameter '%s'./r/n")
- _T("Windows App will return with value '%d'."),
- argv[0], nRet);
- }
- }
- }
- else
- {
- nRet = 1;
- hr = StringCchPrintf(szText, _countof(szText),
- _T("ERROR: invalid parameter '%s'./r/n")
- _T("Windows App will return with value '%d'."), argv[0], nRet);
- }
- }
- else
- {
- nRet = 1;
- hr = StringCchPrintf(szText, _countof(szText),
- _T("ERROR: invalid parameter '%s'./r/n")
- _T("Windows App will return with value '%d'."),
- pszCmdLine, nRet);
- }
-
- if (argv != NULL) LocalFree(argv);
-
- if ((SUCCEEDED(hr)) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)
- {
- MessageBox(NULL, szText, _T("Windows App"), MB_OK | MB_ICONINFORMATION);
- }
-
-
-
-
- return nRet;
- }
-
-
- BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead)
- {
- __int64 num = 0;
- int sign = 1;
- size_t len = 0;
- BOOL bRet = FALSE;
- WCHAR ch = 0;
-
- if (pszNum == NULL || pnNum == NULL)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return bRet;
- }
-
-
- if (bSpaceLead == TRUE)
- {
- while (*pszNum == _T(' ') || *pszNum == _T('/t'))
- pszNum++;
- }
-
-
- ch = *pszNum;
- if (ch == _T('-'))
- {
- sign = -1;
- pszNum++;
- } else if (ch == _T('+'))
- {
- pszNum++;
- }
-
-
- if (*pszNum < _T('0') || *pszNum > _T('9'))
- {
- SetLastError(ERROR_BAD_FORMAT);
- return bRet;
- }
-
-
- if (*pszNum == _T('/0'))
- {
- if (pszNum[1] == _T('/0'))
- {
- *pnNum = 0;
- return TRUE;
- }
- else
- {
- SetLastError(ERROR_BAD_FORMAT);
- return bRet;
- }
- }
-
- while (TRUE)
- {
- ch = *pszNum++;
-
-
- if (ch >= _T('0') && ch <= _T('9'))
- {
- len++;
-
-
- if (len > 10)
- {
- SetLastError(ERROR_INVALID_DATA);
- break;
- }
-
- num = num * 10 + (ch - _T('0'));
-
- continue;
- }
-
- else if (ch == _T('/0'))
- {
- num = sign * num;
- if (num <= INT_MAX && num >= INT_MIN)
- {
- *pnNum = (int)num;
- bRet = TRUE;
- break;
- }
-
- else
- {
- SetLastError(ERROR_INVALID_DATA);
- break;
- }
- }
-
- else
- {
- SetLastError(ERROR_BAD_FORMAT);
- break;
- }
- }
-
- return bRet;
- }
-
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 函数中的返回值。