如何在窗口程序中使用控制台(/subsystem /entry)
2011-11-14 14:53
363 查看
1. 原理 首先我们来看一下linker的 /subsystem 选项 该选项的语法形式如下: /SUBSYSTEM:{CONSOLE|EFI_APPLICATION|EFI_BOOT_SERVICE_DRIVER| EFI_ROM|EFI_RUNTIME_DRIVER|NATIVE|POSIX|WINDOWS|WINDOWSCE} [,major[.minor]] 这个链接选项告诉操作系统如何运行可执行文件 CONSOLE: win32 字符模式应用程序,此种类型的应用程序在运行的时候会产生一个类似DOS 窗口的控制台窗口,如果在应用程序的主函数为main()或者wmain(),在默认情况下 该应用程序就是一个控制台应用程序 Extensible Firmware Interface 和CPU具体架构相关的一个参数选项,并不常用,在这里暂不详细介绍. 如果对此有兴趣的可以访问intel主页来查看相关内容 NATIVE; 设备驱动器选项,如果/DRIVER:WDM选项被设定的话,该链接选项(NATIVE)就为默认选项 POSIX: 在windows NT 种运行在POSIX子系统上的应用程序 WINDOWS: 该类型的应用程序不产生console窗口,该类型的应用程序的窗口由用户自己创建,简而言之 就是一个标准的Win32 application,其入口地址为WinMain()函数或者wWinMain()函数的地址 如果你在应用程序种定义的主函数为WinMain或者wWinMain,在默认情况下该应用程序就是一个 Win32 Application ! WINDOWSCE: 运行在windows CE上的应用程序 major and minor (optional): 主版本号和次版本号,该选项为可选,该选项为0~65535之间的十进制整数 从上面可以看出如果我们建立一个win32 console application的话,linker的/subsystem选项应该为 CONSOLE,可以在VC开发环境的project->setting->link->project option中看到! 接下来我们再看看应用程序是如何运行的! 我们知道用VC编写的程序,运行的时候是需要 C\C++运行库支持的.当我们运行一个C/C++程序的时候 链接器会首先寻找应用程序的启动函数,例如: 如果你建立了一个console程序的话,编译器得链接开关会是以下这种形式 /subsystem:"console" /entry:"mainCRTStartup" (ANSI) /subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE) 如果你建立了一个win32 application,编译器得链接开关则会是一下形式 /subsystem:"windows" /entry:"WinMain" (ANSI) /sbusystem:"windows" /entry:"wWinMain" (UINCODE) 上面的两种形式可以再project->setting->link->project option中看到 上面的subsystem和entry并不需要都设置,如果你只设置了/subsystem:"console" 的话,那么默认的entry开关在默认情况下应为/entry:"mainCRTStartup" 反之,如果你在应用程序中定义了main函数的话,默认情况下,你的/subsystem开关 应该为/system:"console" 在默认情况下/subsystem 和/entry开关是匹配的,也就是 console对应mainCRTStartup或者wmainCRTStartup windows对应WinMain或者wWinMain 但是我们也可以通过手动改动的方式使他们不匹配 例如我们可以这样改动 #pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) // 设置入口地址 int main(int argc, char* argv[]) { MessageBox(NULL, "hello", "Notice", MB_OK); return 0; } 在默认情况下链接器看到/subsystem下是windows选项的时候,它会自动寻找WinMain或者wWinMain 但我们强制指定入口地址,这样运行程序的时候默认的console窗口就会隐藏! 上面是在代码中使用#pragma指令来设置,还有一种就是直接在开发环境的 project->setting->link->project option中手工改动! 在明白了通过/subsystem选项可以控制链接程序的类型后,我们可以根据需要来生成具有控制台的Windows窗口程序。 2. 生成具有console窗口的Win32窗口程序(不使用MFC)使用Visual Studio.Net 2003建立一个Win 32窗口项目(不使用MFC):Win32WithConsole,在项目的属性对话框中,依次选择‘配置属性’->‘链接器’->‘system’,在‘子系统’一项中,将‘Windows (/SUBSYSTEM:WINDOWS)’改为‘控制台(/SUBSYSTEM:CONSOLE)’ 。现在,该项目所生成的可执行文件的入口函数将是mainCRTStartup或是wmainCRTStartup,我们只需要定义一个main函数,并进行适当的入口参数转换,同时在该main函数中调用原来的入口函数_tWinMain即可。下面是Win32WithConsole.cpp文件中我们需要添加的main函数:int _tmain(int argc, _TCHAR* argv[]) { HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); HINSTANCE hPreInstance = NULL; TCHAR szCmdLine[1024]; szCmdLine[0] = 0; LPTSTR lpCmdLine = szCmdLine; for ( int i = 1; i < argc; i++ ) { if ( i > 1 ) { _tcscpy(lpCmdLine, _T(" ")); lpCmdLine = lpCmdLine + _tcslen(_T(" ")); } _tcscpy(lpCmdLine, argv[i]); lpCmdLine = lpCmdLine + _tcslen(argv[i]); } lpCmdLine = szCmdLine; int nCmdShow = SW_SHOWNORMAL; int ret = _tWinMain(hInstance, hPreInstance, lpCmdLine, nCmdShow); return 0; } 可以参考附加的文件Win32WithConsole.rar。 3.生成具有console窗口的MFC窗口应用程序使用向导生成一个多文档的MFC应用程序,MFCWithConsole。同样,将该项目配置为‘控制台(/SUBSYSTEM:CONSOLE)’,下面我们需要找到MFC应用程序的入口函数。通过调试该程序,我们可以发现,MFC框架通过AfxWinMain来调用项目中全局CWinApp变量theApp的InitInstance成员函数,从而启动整个应用程序。因此,我们可以使用两种方式来显式调用AfxWinMain函数,从而创建一个具有console窗口的MFC窗口应用程序。第一种方法是在MFCWithConsole项目中加入AfxWinMain的定义,该函数的定义可以从winmain.cpp文件中,下面是其具体内容: int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL); int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); // AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure; // App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure; // Perform specific initializations if (!pThread->InitInstance()) { if (pThread->m_pMainWnd != NULL) { TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n"); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } nReturnCode = pThread->Run(); InitFailure: #ifdef _DEBUG // Check for missing AfxLockTempMap calls if (AfxGetModuleThreadState()->m_nTempMapLock != 0) { TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock); } AfxLockTempMaps(); AfxUnlockTempMaps(-1); #endif AfxWinTerm(); return nReturnCode; } 第二种方法是显式加载mfc71d.dll或是mfc71.dll,并调用其中的AfxWinMain函数。不过,这两个动态链接库都是使用NONAME的方式来导出函数的,因此只能通过函数序号的方式来调用AfxWinMain函数。通过在IDA Pro中对这两个动态链接库进行反编译,我们可以发现AfxWinMain在mfc71d.dll中的序号为1589,而在mfc71.dll中的序号为1207,下面即是使用动态链接库的方式调用AfxWinMain的方法。 // wrong // typedef int __stdcall (*MYPROC)(HINSTANCE, HINSTANCE,LPTSTR, int); typedef int (__stdcall *AFXWINMAIN_FUNC)(HINSTANCE, HINSTANCE,LPTSTR, int); #ifdef _DEBUG #define MFC_DLL_NAME _T("mfc71d.dll") #define AFXWINMAIN_ORDINAL 1589 #else #define MFC_DLL_NAME _T("mfc71.dll") #define AFXWINMAIN_ORDINAL 1207 #endif int _tmain() { #ifndef _AFXDLL char _afxInitAppState = (char)(AfxInitialize(FALSE, _MFC_VER), atexit(&_AfxTermAppState)); #else char _afxInitAppState = (char)(AfxInitialize(FALSE, _MFC_VER)); #endif HINSTANCE hinstLib = LoadLibrary(MFC_DLL_NAME); AFXWINMAIN_FUNC ProcAdd; int ret = 0; // If the handle is valid, try to get the function address. if (hinstLib != NULL) { ProcAdd = (AFXWINMAIN_FUNC) GetProcAddress(hinstLib, MAKEINTRESOURCE(AFXWINMAIN_ORDINAL)); // If the function address is valid, call the function. if (NULL != ProcAdd) { HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); HINSTANCE hPrevInstance = NULL; LPTSTR lpCmdLine = NULL; int nCmdShow = SW_SHOWNORMAL; ret = (*ProcAdd)(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } // Free the DLL module. FreeLibrary(hinstLib); } return ret; } 需要说明的是,在GetProcAddress函数中,我们需要调用MAKEINTRESOURCE来将函数序号进行转化。 另外,对于AFXWINMAIN_FUNC的定义一定要加上__stdcall的调用约定,因为AfxWinMain是采用__stdcall方式来调用的。最后还有一点需要注意,我们需要使用AfxInitialize来注册退出函数,否则程序将不能正确退出。 |
相关文章推荐
- 如何在控制台程序中使用窗口
- GUI程序如何使用控制台输出调试信息[zz][MFC调用win32窗口显示调试信息,使用AllocConsole 函数 ]
- Qt在Windows下如何创建无CMD窗口控制台程序
- 屏蔽控制台应用程序的窗口 #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
- VC++如何创建无CMD窗口控制台程序
- win32和MFC下如何使用控制台窗口
- 屏蔽控制台应用程序的窗口#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
- C# 控制台程序如何能不显示窗口
- VC 控制台程序如何不显示控制台窗口
- 图形界面程序使用控制台窗口
- 屏蔽控制台应用程序的窗口#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
- Visual 中控制台程序如何使用MFC类库
- win32和MFC下如何使用控制台窗口
- 在java程序中如何能获取到另一个程序的windows句柄,又如何使用这个句柄向这个窗口发送消息
- Visual Studio屏蔽应用程序控制台窗口#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
- 如何在Win32窗口程序中发命令打开一个控制台,往里面打印(printf())输出一些数据
- C# 控制台程序如何能不显示窗口
- Win32控制台程序如何使用CString的方法
- 【MFC】如何在mfc窗口程序中调用控制台
- 【C#】如何让多个或全部窗口使用同一个图标(icon),减小程序体积