views:

34

answers:

1

I am creating an application which will read the output from another application (which I did not write) which sends its output to a command prompt. I need to be able to write this information an "Edit" dialog within my GUI program. I have accomplished this using anonymous pipes and multi-threading (a worker thread for writing and another for reading). The program is initialized by CreateProcess(); Now, how can I get this to update the GUI in NEAR real-time. If there is a few second delay, that won't be an issue, but I cannot wait for the program to return 0; before the input is processed (which appears to be the current issue).

Here is some relevant code to how I am doing this. These are my callbacks from the threads.

DWORD WINAPI Arc_readPipe(LPVOID threadParam)
{
 Arc_Redirect ar;
 CHAR chBuf[BUFSIZE];
 DWORD dwRead;
 HANDLE hPipe = (HANDLE)threadParam;
 HWND g1  = FindWindow("MyGUI",NULL);
 HWND dlg = GetDlgItem(g1,IDO_WORLDOUT);

 while(bRunThread)
 {
  while(TRUE)
  {
   if(!ReadFile(hPipe,chBuf,BUFSIZE,&dwRead,NULL) || !dwRead)
    if(GetLastError() == ERROR_BROKEN_PIPE)
    {
     memset(chBuf,0,sizeof(chBuf));
     break; // Normal exit
    } else {
     throw GetLastError();
    }

   chBuf[dwRead] = '\0';
   //MessageBox(g1,"Read data","Read",MB_OK);
   ar.appendText(dlg,chBuf);
  }
 }
 return 1;
}

DWORD WINAPI Arc_writePipe(LPVOID threadParam)
{
 Arc_Redirect ar;
 DWORD dwWrote;
 CHAR chBuf[BUFSIZE];
 HANDLE hPipe = (HANDLE)threadParam;
 HWND g1   = FindWindow("MyGUI",NULL);
 HWND dlg  = GetDlgItem(g1,IDO_WORLDOUT);
 int nLength = GetWindowTextLength(GetDlgItem(g1,IDO_WORLDINPUT));

 while(bRunThread)
 {
  if(GetDlgItemText(g1,IDO_WORLDINPUT,chBuf,BUFSIZE))
  {
   chBuf[sizeof(GetDlgItemText(g1,IDO_WORLDINPUT,NULL,NULL))] = '\0';
   if(!WriteFile(hPipe,chBuf,sizeof(GetDlgItemText(g1,IDO_WORLDINPUT,NULL,NULL)),&dwWrote,NULL))
   {
    SetDlgItemText(g1,IDO_WORLDINPUT,NULL);
    if(GetLastError() == ERROR_NO_DATA)
     break; // Normal :)
    else
     MessageBox(g1,"Error: Could not WriteFile();","Error",MB_ICONERROR);
   }
  }
 }
 return 1;
}

Excuse the mess in the code; since I have been working on it, it's not as clean as it could be. bRunThread is a BOOL variable set in the global scope just to keep the thread looping. Any ideas?

Thanks for any help!

A: 

If I understand your question correctly, your program is working as you expect, except that the ReadFile is blocking until all of the output from the child process has been written, and instead you'd like to process it in close to realtime as the output is generated?

According to MSDN:

The ReadFile function returns when one of the following conditions occur:

  • The number of bytes requested is read.
  • A write operation completes on the write end of the pipe.
  • An asynchronous handle is being used and the read is occurring asynchronously.
  • An error occurs.

I don't know what your BUFSIZE is, or how often the child process is making write calls for its output. However if your program is blocking on the ReadFile call, it's because none of these conditions have occurred.

Since you can't control the output of the child program, you could change the value that you are passing to the nNumberOfBytesToRead parameter of ReadFile to something much smaller, say 1, which will read each byte individually. If that works, you can increase it to something appropriate to the amount and rate of data you are processing, 10 or 100 or whatever.

Also you currently have a buffer overflow in your code, as you are setting chBuf[dwRead] to a null byte which if ReadFile manages to read the entire BUFSIZE that you requested will be BUFSIZE, one past the end of the array.

As already noted, using FindWindow in this case is not a good idea, it's entirely possible for you to get back a window that is not the one you wanted. Even a global variable would be preferable in this case, at least then you have complete control over what its value will be.

And finally:

chBuf[sizeof(GetDlgItemText(g1,IDO_WORLDINPUT,NULL,NULL))] = '\0';

makes no sense at all. sizeof will tell you the size of the data type that is passed to it, at compile time. In this case, it will be the size of the return type of GetDlgItemText, UINT, which will always be 4. You can get the length of text retrieved by simply storing the return value of your first call to GetDlgItemText.

Brook Miles
I corrected things as you said and am currently trying to read only 1 byte at a time, however, I still am not getting the data in real-time. Any ideas? Thank you so much for your comment by the way, it really did help me get much closer to solving my issue!
RageD
Maybe you can clarify what IS happening. Have you ran your app in a debugger? Is your ReadFile loop running as expected but your UI not updating? Or does ReadFile block until the child process is complete even when reading a single byte? What kinds of time scales are we talking about, how long does the output of the child process take? Are you sure the child process is even outputting data in real-time and not just dumping it out at the end? Can you get the results you expect if you write a test child app that just continually outputs text?
Brook Miles
I can definitely clarify, thanks for taking the time to help. I have run my app in debugger and my ReadFile blocks until child process ends (or for some reason in this case Sleep()'s). I am currently using a test app which I did write which prints out characters on iteration and then sleeps for 3 seconds. The readfile properly reads at Sleep() and then won't read again until the program ends. It takes one minute for the program to finish its final iterations after the sleep and I do not get the rest of the output until that time.
RageD