0

I started a Visual C++ Desktop Application (win32) and created a button, that creates a process.

Here is the button code:

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;
}

I want to read the process's stdout output (cmd.exe or testprogram.exe) while it is 'running' and set it as a string. After setting the content of a created Text input.

I tried this answer, but it freezes the application. (because of ReadFile() and while() loop)

How to read output from cmd.exe using CreateProcess() and CreatePipe()

I searched the forum for a better answer, but every example is for a console app.

Update: I can read the output of ping.exe but the window stays frozen until ping.exe closes. the for() loop (and ReadFile()) blocks the window .

 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());
    }

I searched for asynchronous I/O on ReadFile and pipes. If anyone can provide me an example on how to do async Readfile without for() loop would be good.

  • See [Creating a Child Process with Redirected Input and Output](https://learn.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output) on MSDN. – Remy Lebeau May 15 '21 at 22:11
  • I tried this example but it stops the Main window at `ReadFile()`, if `bInheritHandles=TRUE` and if it's FALSE, it doesn't read the stdout output. – Krisztian Nagy Zsolt May 16 '21 at 09:33
  • use asynchronous I/O on pipes. – RbMm May 16 '21 at 12:40
  • @RbMm I searched for asynchronous I/O on ReadFile and pipes, none of them worked. I updated the question on how far I have reached. – Krisztian Nagy Zsolt May 16 '21 at 16:09
  • `CreatePipe` create synchronous file handle. you need not use this api. use `ZwCreateNamedPipeFile` or `CreateNamedPipeW` for begin – RbMm May 16 '21 at 16:17

1 Answers1

2

If your UI thread stops pumping messages Windows treats it as frozen. This means you should not perform blocking I/O on this thread.

You could use asynchronous (overlapped) I/O with ReadFile but doing the whole child process operation in another thread is a lot easier. The button press would start the thread and once the thread has read stdout it can signal back to your main window by sending a WM_APP message.

Anders
  • 97,548
  • 12
  • 110
  • 164