编写自己的IDE:如何在图形界面中实时捕获控制台程序的标准输出
2008-01-19 20:24:39 来源:WEB开发网Multiline, Horizontal scroll, Auto HScroll, Vertical scroll, Auto VScroll, Want return
然后用ClassWizard为IDC_EDIT1添加一个对应的成员变量(注意变量的类型要选CEdit型而非字符串CString型)
CEdit m_edit1;
使用ClassWizard为“确定”按钮添加消息响应方法OnOK(),编辑该方法:
void CMyIDEDlg::OnOK()
{
AfxBeginThread(myThread, this);
InvalidateRect(NULL);
UpdateWindow();
}
也就是说,我们在“确定”按钮按下时,启动了后台线程myThread(),那么,myThread()到底做了些什么呢?我们先在CMyIDEDlg类的头文件myIDEDlg.h中加上一个成员函数声明:
protected:
static UINT myThread(LPVOID pParam);
然后,在CMyIDEDlg类的实现文件myIDEDlg.cpp里添加myThread()的实现代码:
UINT CMyIDEDlg::myThread(LPVOID pParam)
{
PROCESS_INFORMATION pi;
STARTUPINFO siStartInfo;
SECURITY_ATTRIBUTES saAttr;
CString Output, tmp;
char command_line[200];
DWORD dwRead;
char* buf; int len;
HANDLE hRead, hWrite;
CMyIDEDlg* pDlg = (CMyIDEDlg*)pParam;
// 创建与wSpawn.exe通讯的可继承的匿名管道
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hRead, &hWrite, &saAttr, 0))
{
AfxMessageBox("创建管道失败");
return 0;
}
// 准备wSpawn的命令行,在命令行给出写管道句柄和要wSpawn执行的命令
memset(&pi, 0, sizeof(pi));
sprintf(command_line, "wspawn -h %d cl /?", (unsigned int)hWrite);
// 子进程以隐藏方式运行
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.wShowWindow = SW_HIDE;
siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
// 创建wSpawn子进程
if (!CreateProcess( NULL, command_line, NULL, NULL, TRUE,
0, NULL, NULL, &siStartInfo, &pi))
{
AfxMessageBox("调用wSpawn时失败");
return 0;
}
// 读管道,并显示wSpawn从管道中返回的输出信息
if(!ReadFile( hRead, &len, sizeof(int), &dwRead, NULL) || dwRead == 0)
return 0;
while(len)
{
buf = new char[len + 1];
memset(buf, 0, len + 1);
if(!ReadFile( hRead, buf, len, &dwRead, NULL) || dwRead == 0)
return 0;
// 将返回信息中的"\n"替换为Edit Box可识别的"\r\n"
tmp = buf;
tmp.Replace("\n", "\r\n");
Output += tmp;
// 将结果显示在Edit Box中,并刷新对话框
pDlg->m_edit1.SetWindowText(Output);
pDlg->InvalidateRect(NULL);
pDlg->UpdateWindow();
delete[] buf;
if(!ReadFile( hRead, &len, sizeof(int), &dwRead, NULL) || dwRead == 0)
return 0;
}
// 等待wSpawn结束
WaitForSingleObject(pi.hProcess, 30000);
// 关闭管道句柄
CloseHandle(hRead);
CloseHandle(hWrite);
return 0;
}
很简单,不是吗?后台线程创建一个匿名管道,然后以隐藏方式启动wSpawn.exe并将管道句柄通过命令行传给wSpawn.exe,接下来只要从管道里读取信息就可以了。现在我们可以试着编译运行myIDE.exe了,记住要把myIDE.exe和wSpawn.exe放在同一目录下。还有,我在myThread()函数中写死了传给wSpawn.exe的待执行的命令行是“cl /?”,这模拟了一次典型的编译过程,如果你不打算改变这一行代码的话,那一定要注意在你的计算机上,C++编译器cl.exe必须位于环境变量PATH指明的路径里,否则wSpawn.exe可就找不到cl.exe了。下面是myIDE程序的运行结果:
补充一点,上面给出的wSpawn利用_popen()完成子进程创建和输入输出重定向,这一方法虽然简单,但只能重定向子进程的stdout或stdin,如果还需要重定向子进程的stderr的话(Java编译器javac就利用stderr输出结果信息),那我们就不能这么投机取巧了。根据以上讨论,你一定可以使用传统的_pipe()、_dup()等系统调用,写出功能更完整的新版wSpawn来,我这里就不再罗嗦了。
赞助商链接