1

I've seen a question exactly like this already exists: Redirect stdout to an edit control (Win32)

However, the solution given requires the programmer to implement a my_printf function that does a {printf; read from pipe to edit control}. I cannot do that because my printf's are in a external library.

Ideally, I'm thinking of:

  1. redirect app's stdout to edit control
  2. run app and voila

But if edit control's API only allows you to write a string to it, I would think of something like:

1 - dup'ing stdout to a pipe out descriptor
3 - read from pipe in descriptor into a buffer
4 - write from buffer to edit control

However, there is a missing step 2 there:

2 - getting a signal of when a write to that pipe out descriptor is done.

How could I automate that part. Could I use something like a socket select here?

[EDIT]

So, according to David Heffernan's comments, I would have something like:

  #define MYPRINT      1
  #define WM_MYMESSAGE (WM_USER+1)

  INT_PTR CALLBACK Foo::DialogProc(
    ...
    case WM_COPYDATA:
      {
        PCOPYDATASTRUCT pMyCDS = (PCOPYDATASTRUCT) lParam;
        LPCSTR szString = (LPCSTR)(pMyCDS->lpData);
        AppendLog(szString);
      }
      break;
    ...
  }

  /* static */
  void Foo::MainThread()
  {
    // Create worker thread
    DWORD dwThreadId = 0;
    m_hRedirectStdoutThread = CreateThread(
      // default security
      NULL,
      // default stack size
      0,
      // routine to execute
      (LPTHREAD_START_ROUTINE) &CTracesConsole::RedirectStdoutThreadRun,
      // thread parameter
      NULL,
      // immediately run the thread
      0,
      // thread Id
      &dwThreadId);
    if (NULL == m_hRedirectStdoutThread)
    {
      printf("Error creating stdin thread\n");
      return;
    }

    // Loop forever
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  /* static */
  void Foo::RedirectStdoutThreadRun()
  {
    // Redirect stdout to pipe
    int fds[2];
    _pipe(fds, 1024, O_TEXT);
    _dup2(fds[1], 1); // 1 is stdout

    char buffer[1024];
    for (;;)
    {
      // Need to flush the pipe
      _flushall();
      // Read stdout from pipe
      DWORD dwNumberOfBytesRead = 0;
      dwNumberOfBytesRead = _read(fds[0], buffer, 1024 - 1);
      buffer[dwNumberOfBytesRead] = 0;

      // Send data as a message
      COPYDATASTRUCT myCDS;
      myCDS.dwData = MYPRINT;
      myCDS.cbData = dwNumberOfBytesRead + 1;
      myCDS.lpData = buffer;
      PostMessage(g_hWindow,
                  WM_MYMESSAGE,
                  0,
                  (LPARAM)(LPVOID) &myCDS);
    }
  }

Where AppendLog writes a string to the edit control.

[EDIT]

This code works properly now. With the little inconvenience that, when I redirect stdout traces from libcurl, libcurl stops working :) But that's another story...

Community
  • 1
  • 1
rturrado
  • 7,699
  • 6
  • 42
  • 62
  • Pipe `stdout` to your app. Read `stdin` and spew it into the edit control. Job done. – David Heffernan Jun 30 '11 at 08:35
  • @David Heffernan - the point of my question is where should I do that read `stdin`. My app does write directly to the text control. Interleaved with this writes, I will get another writes to `stdout` from the external libs. What do you reckon about having a worker thread constantly doing that read `stdin` and writing to the edit control? – rturrado Jun 30 '11 at 09:05
  • I'd do the stdin reading in a worker thread and let it block when the pipe was empty. You'd need to then send the text to a window in the main thread, using a windows message, so that you adhere to thread affinity for windows. – David Heffernan Jun 30 '11 at 09:31
  • @David Heffernan - for the block, could I use a WaitForSingleObject on the pipe in descriptor? For windows message you refer to the GetMessage, DispatchMessage, and so on, Win API? – rturrado Jun 30 '11 at 10:58
  • `ReadFile` blocks until there is data to be read. `GetMessage/DispatchMessage` are already present in your app since you have an edit control. Send a message with `SendMessage`. – David Heffernan Jun 30 '11 at 11:07
  • If printf blocks when the pipe is full, this could deadlock. – Jon Jul 01 '11 at 15:26
  • @Jon - I've changed the SendMessage for a PostMessage, which doesn't wait for the receiver to process the message. – rturrado Jul 04 '11 at 07:01

2 Answers2

2

Windows supports asynchronous I/O. That makes it easy:

  1. dup'ing stdout to a pipe out descriptor
  2. Issue async read from pipe in descriptor into a buffer
  3. Wait for message or event (MsgWaitForMultipleObjects).
    • If the wait ends with one or more messages, call PeekMessage(PM_REMOVE) to remove them.
    • If the pipe event is signalled, copy text from buffer to edit control.
MSalters
  • 173,980
  • 10
  • 155
  • 350
1

As far as I'm aware you can't get 'notifications' with a pipe. If you do want to do that maybe you should use WM_COPYDATA instead which would also provide a simpler solution. You will get a message when text is posted to your window which you can then append to the edit control.

Mike Kwan
  • 24,123
  • 12
  • 63
  • 96
  • Yes I assumed the 'signal' he refers to is a Windows message. If you were to use a pipe and wanted a WM you'd need to generate that yourself – Mike Kwan Jun 30 '11 at 08:45
  • I'd imagine that ReadFile would return an error code to indicate that the pipe had been closed and that would be signal enough. If you are using pipes then I don't see the need for Windows messages. – David Heffernan Jun 30 '11 at 08:50
  • I figured he'd also want to deal with it in his WndProc as well since eventually he needs to message to WndProc anyway to interact with the edit control. Otherwise he's just going to be passing round HWNDs everywhere – Mike Kwan Jun 30 '11 at 08:59
  • I'm only after a functionality, stdout ---> edit control. I thought about using pipes, and I wondered if I could be notified when the pipe was written to. I hadn't thought about other possibilities as having a separate thread continuously reading from that pipe. – rturrado Jun 30 '11 at 11:06
  • @Mike - accepting this answer as it is the closest to the final code I'm using. Thanks. – rturrado Jul 05 '11 at 06:33