首页 > 解决方案 > 在 Win32 C++ 桌面应用程序中读取进程的“stdout”输出

问题描述

我启动了一个 Visual C++ 桌面应用程序 (win32) 并创建了一个按钮,它创建了一个进程。

这是按钮代码:

INT_PTR CALLBACK Btn_Click(HWND hWnd)
{
    wchar_t cmd[] = L"cmd.exe /c testprogram.exe";

    STARTUPINFOW startInf;
    memset(&startInf, 0, sizeof startInf);
    PROCESS_INFORMATION procInf;
    memset(&procInf, 0, sizeof procInf);

    BOOL p = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
        CREATE_NO_WINDOW, NULL, NULL, &startInf, &procInf);

    if (p)
    {
        CloseHandle(procInf.hProcess);
        CloseHandle(procInf.hThread);
    }
    return TRUE;
}

我想在进程“运行”时读取进程的标准输出(cmd.exe 或 testprogram.exe)并将其设置为字符串。设置创建的文本输入的内容后。

我尝试了这个答案,但它冻结了应用程序。(因为 ReadFile() 和 while() 循环)

如何使用 CreateProcess() 和 CreatePipe() 从 cmd.exe 读取输出

我在论坛中搜索了一个更好的答案,但每个示例都是针对控制台应用程序的。

更新: 我可以读取 ping.exe 的输出,但窗口一直冻结,直到 ping.exe 关闭。for() 循环(和 ReadFile())阻塞了窗口。

 wchar_t command[] = L"ping localhost";

    STARTUPINFOW startInf;
    memset(&startInf, 0, sizeof startInf);
    PROCESS_INFORMATION procInf;
    memset(&procInf, 0, sizeof procInf);

    HANDLE cout_r = NULL;
    HANDLE cout_w = NULL;
    SECURITY_ATTRIBUTES sec_a;
    //memset(&sec_a, 1, sizeof sec_a);

    sec_a.nLength = sizeof(SECURITY_ATTRIBUTES);
    sec_a.lpSecurityDescriptor = NULL;

    CreatePipe(&cout_r, &cout_w, &sec_a, 0);
    SetHandleInformation(cout_r, HANDLE_FLAG_INHERIT, 0);

    //startInf.cb = sizeof(STARTUPINFO);
    startInf.hStdOutput = cout_w;
    startInf.dwFlags |= STARTF_USESTDHANDLES;


    BOOL p = CreateProcess(NULL, command, NULL, NULL, TRUE,
        NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, 
        &startInf, &procInf);

    if (p)
    {
        CloseHandle(procInf.hProcess);
        CloseHandle(procInf.hThread);
        CloseHandle(cout_w);
    }
    else
    {
        SetWindowTextA(hedit, "Failed");
    }

    char buf[1024];
    CHAR chBuf[4096];
    DWORD dwRead;
    for (;;)
    {
        BOOL bSuccess = ReadFile(cout_r, chBuf, 4096, &dwRead, NULL);
        if (!bSuccess || dwRead == 0) break;

        std::string s(chBuf, dwRead);
        std::wstring stemp = std::wstring(s.begin(), s.end());
        OutputDebugStringW(stemp.c_str());
    }

我在 ReadFile 和管道上搜索异步 I/O。如果有人可以为我提供一个关于如何在没有 for() 循环的情况下执行异步 Readfile 的示例,那就太好了。

标签: c++windowswinapidesktop

解决方案


如果您的 UI 线程停止发送消息,Windows 会将其视为已冻结。这意味着您不应在此线程上执行阻塞 I/O。

您可以将异步(重叠)I/O 与 ReadFile 一起使用,但在另一个线程中执行整个子进程操作要容易得多。按下按钮将启动线程,一旦线程读取了标准输出,它就可以通过发送 WM_APP 消息向您的主窗口发出信号。


推荐阅读