在GUI程序中使用控制台的两种方法-方法2

文章转载自 http://blog.csdn.net/nanyu/article/details/6474939

 

2. 方法-2:外挂式

先接前一篇《在GUI程序中使用控制台的两种方法-方法.1》。方法1采用的是“内嵌式”,即由GUI程序自身申请一个控制台,然后在上面输入输出,用到一个API:“ AllocConsole  ”。

2.1 API

这一次我们使用另一个函数:“AttachConsole”:

Code:
  1. BOOL WINAPI AttachConsole(  
  2.   __in  DWORD dwProcessId  
  3. );  

Attach的意思很明显:“贴上,依附上,缠上,赖上……:( ”,而它的参数也很直白: 进程ID。 这样,一个GUI程序,完全可以“缠上”另一个进程的控制台(假设它有的话)。

那么怎么得到另一个进程的ID呢?有点难,但如果是要取“父进程”的ID,就方便多了——事实上是不用取,只要填-1就可以了。

 

2.2 “外挂式”思路

一个进程可以启动另一个进程,后者就称为前者的“子进程”,而前者则称为“父进程”。这个关系可以一直传递下去,即子进程也可以有自己的子进程。

基本上, 电脑用户通过操作系统运行一个程序,比如画笔,比如浏览器,比如Word,都是以一个叫“Explorer.exe”的子进程身份启动的。而我们在IDE里调试程序,则程序会以调试器的子进程运行……,可见子进程其实是相当常见的。现在,我们事先写一个控制台的程序,假设称为P程序,以后当S程序(通常是一个GUI程序)需要附加的控制台来输入输出时(效果像方法.1),我们就用P程序来启动S程序,S程序中则通过“AttachConsole(-1)”来“挂”到其父程序(也就是P)的控制台。完成调试之后,就把P程序丢一边,以普通方式运行S程序,则AttachConsole(-1)失败,那些用于调试的cout/cin自然失效。

 

2.3 GUI 测试例子

我们先来写GUI程序的一个例子,也就上面说的子进程S程序。代码和方法1中的几乎一样的,为了简单,我们干脆只写出WinMain函数。

Code:
  1. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)  
  2. {  
  3.     hInst = hInstance;  
  4.   
  5.     if (0 == AttachConsole((DWORD)-1))  
  6.     {  
  7.         ::MessageBox(0, "AttachConsole fail""msg", 0);  
  8.     }  
  9.     else  
  10.     {  
  11.         freopen("conin$""r+t", stdin);    
  12.         freopen("conout$""w+t", stdout);    
  13.         freopen("conout$""w+t", stderr);     
  14.   
  15.         ::MessageBox(0, "AttachConsole OK""msg", 0);          
  16.     }  
  17.       
  18.     std::cout << std::hex << _WIN32_WINNT << std::endl;  
  19.       
  20.     // The user interface is a modal dialog box  
  21.     return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DialogProc);  
  22. }  

当“AttachConsole”失败时,返回0,为了测试,我故意让它弹出一个消息框,实际程序当然不应这样扰民。当成功时,我们还是调用freopen来绑定cin,cout,cerr。

注意: 第18,我立即用cout 输出一个宏(整数)的值(采用16进制),这个宏 _WIN32_WINNT 在有些IDE里,默认是0x40的,而AttachConsole的文档里写到:

To compile an application that uses this function, define _WIN32_WINNT as 0x0501 or later. For more information, see Using the Windows Headers.

所以,我们必须通过IDE预定好_WIN32_WINNT,让它变成0x50好了。对于Code::Blocks和 VC,这都是很简单的事,你应该知道如何做。

上面的代码需要一些宏定义和头文件包含:

Code:
  1. #define WIN32_LEAN_AND_MEAN  
  2.  
  3. #include <iostream>  
  4. #include <windows.h>  

 

2.4 带控制台的父进程

GUI程序默认不带控制台,所以我们现在写一个控制台程序,用它来调用要调试的GUI程序。这本来是一件再简单不过的事了。只要用C语言的system程序就可以了,并且是跨平台的——问题是微软大叔总爱干些半拉子的事。Windows从DOS起家,都过去多少年了,它对控制台支持还是怪怪的,所以system函数在它身上,居然不支持带空格的路径,我们只好找CreateProcess来出气。

新建一个控制台程序:

Code:
  1. int main(int argc, char** argv)  
  2. {  
  3.     if (argc < 2)  
  4.     {  
  5.         cout << "Usage:" << argv[0] << " <filename> <args ...>" << endl;  
  6.         return 1;  
  7.     }  
  8.   
  9.     cout << argv[1] << endl;  
  10.   
  11.     //return system(argv[1]); linux下就这样...  
  12.   
  13.     STARTUPINFO si;  
  14.     ZeroMemory(&si, sizeof(si));  
  15.     si.cb = sizeof(si);  
  16.       
  17.     PROCESS_INFORMATION pi;  
  18.     ZeroMemory(π, sizeof(pi));  
  19.       
  20.     ::CreateProcess (argv[1]  
  21.         , NULL  
  22.         , NULL  //LPSECURITY_ATTRIBUTES  
  23.         , NULL  //LPSECURITY_ATTRIBUTES  
  24.         , FALSE  //bInheritHandles  
  25.         , 0     //dwCreationFlags  
  26.         , NULL  //lpEnvironment  
  27.         , NULL  //LPCTSTR  
  28.         , &si   //LPSTARTUPINFO  
  29.         , π); //LPPROCESS_INFORMATION  
  30.       
  31.     ::WaitForSingleObject(pi.hProcess, INFINITE);  
  32.       
  33.      DWORD exitCode = 0;  
  34.       
  35.      int ret;  
  36.       
  37.      if (!::GetExitCodeProcess(pi.hProcess, &exitCode))  
  38.      {  
  39.          ret = -1;  
  40.      }  
  41.      else  
  42.      {  
  43.          ret = exitCode;  
  44.      }  
  45.       
  46.     ::CloseHandle(pi.hProcess);  
  47.     ::CloseHandle(pi.hThread);  
  48.       
  49.      return ret;  
  50. }  
  51.     

 我们必须用WaitForSingleObject来调待子进程退出后,父进程才能退出。我们还用GetExitCodeProcess来得到子程序的退出代码。

说到CreateProcess ,我们还可做更多的事,比如我们可以倒过来,用它来创建一个带有控制台(也就是说,天生有cout,cin,cerr)的程序,然后抓取它的输出(cout, cerr),甚至也可以连通它的输入(cin),这样就可以倒过来,为一个控制台程序,挂一个GUI界面了,典型的的案例,比如WinRAR最初的版本。当然,今天我们谈的是 如何为一个GUI程序,挂一个控制台,那么,这就完事了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值