Link to home
Start Free TrialLog in
Avatar of oskii
oskii

asked on

Asynchronous I/O to spawned process

Hi!

I want to perform asynchronous I/O on stdout/stdin on a spawned process.
So far i have only been able to do synchronous I/O. I have read that
asynchrounous I/O is possible using named pipes, but i haven't been able
to make it work. Does anyone have a clue?

Some code to illustrate my problem
<-- asyncapp.cpp -->
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

int main(void)
{
      for(int x=10; x--;)
      {
            printf("%d ", x);
            Sleep(1000);
      }
      return 0;
}



<-- redirectedconsole.cpp -->
#define WIN32_LEAN_AND_MEAN
#define BUFSIZE 4096
#include <windows.h>
#include <stdio.h>
#include <conio.h>

HANDLE m_hChildStdoutRd;

DWORD WINAPI readthread(LPVOID lpParameter)
{
      DWORD dwRead;
      TCHAR chBuf[BUFSIZE+1];

      for (;;)
      {
            if(!ReadFile(m_hChildStdoutRd, chBuf, BUFSIZE, &dwRead, NULL) || dwRead==0)
                  break;

            chBuf[dwRead]='\0';
            printf(chBuf);
      }

      return 0;
}

int main(void)
{
      HANDLE hChildStdoutRdTmp, m_hChildStdoutWr, hSaveStdout;
      SECURITY_ATTRIBUTES saAttr;
      PROCESS_INFORMATION piProcInfo;
      STARTUPINFO siStartInfo;
      DWORD id;

      // Set the bInheritHandle flag so pipe handles are inherited.
      saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
      saAttr.bInheritHandle = TRUE;
      saAttr.lpSecurityDescriptor = NULL;

      // Save the handle to the current STDOUT.
      hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

      // Create a pipe for the child process's STDOUT.
      if(!CreatePipe(&hChildStdoutRdTmp, &m_hChildStdoutWr, &saAttr, 0))
            return FALSE;

      // Set a write handle to the pipe to be STDOUT.
      if(!SetStdHandle(STD_OUTPUT_HANDLE, m_hChildStdoutWr))
            return FALSE;

      // Create noninheritable read handle and close the inheritable read handle.
      if(!DuplicateHandle(GetCurrentProcess(), hChildStdoutRdTmp, GetCurrentProcess(), &m_hChildStdoutRd, 0, TRUE, DUPLICATE_SAME_ACCESS))
            return FALSE;

      // Close original handle
      CloseHandle(hChildStdoutRdTmp);

      // Restore saved handle
      if(!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
            return FALSE;

      // Set up STARTUPINFO
      ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
      siStartInfo.cb = sizeof(STARTUPINFO);
      siStartInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
      siStartInfo.wShowWindow = SW_HIDE;
      siStartInfo.hStdInput = NULL;
      siStartInfo.hStdOutput = m_hChildStdoutWr;
      siStartInfo.hStdError = m_hChildStdoutWr;

      // Create child process
      if(!CreateProcess(NULL, "asyncapp.exe", NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &piProcInfo))
            return FALSE;

      // Create read thread
      if(!CreateThread(NULL, 0, readthread, 0, 0, &id))
            return FALSE;

      getch();
      return TRUE;
}
Avatar of mxjijo
mxjijo


I dont know if I totally understood what u need to achive but..

In general, most of the I/O access could be made asynchronous by setting proper flags during creation time or later, depending up on what u are creating. For example if you specify FILE_FLAG_OVERLAPPED flag, you can perform async operation on that file.

In your case, as far as I know CreatePipe() doesnot support async operations. But, try CreateNamedPipe() with PE_NOWAIT flag. Then you'll be able to do non-blocking IO operations. Take a look at the following links to see more on async IO operations.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ipc/base/synchronous_and_overlapped_input_and_output.asp

~ J
Avatar of oskii

ASKER

Instead of create pipe I have tried to create a named pipe with:

OVERLAPPED OverlapObj;
hChildStdoutRdTmp=CreateNamedPipe("\\\\.\\pipe\\apipe", PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 4096, 4096, 500, NULL);
m_hChildStdoutWr=CreateFile("\\\\.\\pipe\\apipe", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
ZeroMemory(&OverlapObj, sizeof(OverlapObj));
ConnectNamedPipe(hChildStdoutRdTmp, &OverlapObj);

If I then put WaitForSingleObject() in the read thread, it will never return, i suppose something is wrong...

Explain me a little more... how did you do it ?

Did you setup the OVERLAPPED structure properly ?
What id the return value of ReadFile() ??
What hande are you passing to WaitForSingleObject() ?

~ J


Avatar of oskii

ASKER

I have not done any setup of the overlapped structure (other than ZeroMemory). I dont really know what i shoud fill it with, this might be the problem.
I passed the m_hChildStdoutRd handle to WaitForSingleObject. ReadFile returns nonzero and sets last error to 997 (Overlapped I/O operation is in progress)

Thank you mxjijo for your interest in this question!

Its not the way Async IO works.

Its fine that u're passing Zero'd OV structure to ConnectPipe().

But you have to prepare another one for ReadFile too, which in your case should not be Zero'd.

As you mentioned, your ReadFile() will return immediately with IN_PROGRESS error. Now, you have to wait for that IO operation to compleate. This could be done as follows

* Prepare an OVERLAPPED structure and pass it to ReadFile() with an Event, created prior to this. System will set that event once the IO operation is compleated. Infact this is the handle that you should pass to your WaitForSingleObject().

   Wait - Not ovet yet. Now, once the Event is triggered, you can assume the io operation is over, but there could be an error. So you should check the status of this operation using GetOverlappedResult(), passing the OV structure and Pipe handle above. Now, you have more clear idea how many bytes has been transferred, what eas the result etc.

   You should read up on the link I gave you before. It explains the typical Async IO operation.

~ J
Avatar of oskii

ASKER

Thank you for your reply, i have not been able to make overlapped I/O work yet, but i am confused weither overlapped communication really solves the main problem, if you compile and run the asyncapp.cpp (you can probably do it mentaly), you will se that it displays 9 <one sec delay> 8 ... and so on when the same program is being executed with redirected stdout it will be a 10 second delay, and after that all numbers are displayed. If i put fflush(stdout) in the loop in asyncapp the application will work as it is supposed to do even when spawned with redirected handles. The problem is that i am trying to redirect a third-party application for which i dont have the sources.

Will overlapped I/O solve this problem, or should i seek answers elsewhere?

Thanks in advice,
Oskar Sjöberg

I have not compiled your code, but as a first impression, I dont think Overlapped IO will NOT solve your problem. I have not tried io redirection my myself yet. However I had a similar problem on Unix I remember, and fflush() was the method I used there either:) Yes there is some buffering assiciated with stdio's. I dont know if there is a way to get aroung this..  I'll let u know if I find something..

~ J
ASKER CERTIFIED SOLUTION
Avatar of CetusMOD
CetusMOD
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial