WEB开发网
开发学院软件开发VC 编写自己的IDE -- 如何在图形界面中实时捕获控制... 阅读

编写自己的IDE -- 如何在图形界面中实时捕获控制台程序的标准输出

 2006-07-20 11:40:25 来源:WEB开发网   
核心提示: Multiline, Horizontal scroll, Auto HScroll, Vertical scroll, Auto VScroll, Want return然后用ClassWizard为IDC_EDIT1添加一个对应的成员变量(注意变量的类型要选CEdit型而非字符串CSt

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来,我这里就不再罗嗦了。

上一页  1 2 3 4 

Tags:编写 自己 IDE

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接