views:

78

answers:

2

I have been doing a lot of searching and still can't seem to figure out how to fix my issue. I am writing a GUI program (in WinAPI so no MFC please) to communicate with another program (command line based). I am using anonymous pipes since everything is local (but perhaps named pipes would be better?) which then I use CreateProcess(); to run the program I am trying to get the output from.

Now, I just moved from synchronous to asynchronous a few hours ago and am running into a few problems (even though synchronous did not do what I wanted anyway). The first problem is that I am still running into the same problem I had while running synchronous i/o; if I run my "read" (or "write" for that matter) function more than once, the program will freeze. I cannot have this because the purpose of the program is to periodically update to the GUI what the output of the command prompt is.

The second problem, and ultimately more serious is a new one with my asynchronous i/o; it does not read the entire output like my synchronous did. It reads until the program I am reading from sends the return character (or else it is simply coincidental that that is the point where it happens to stop reading). I feel like perhaps I am not fully understanding the capabilities of the OVERLAP, but I'm feeling bogged down by reading so much of the MSDN right now, so perhaps I am overlooking some important aspects of it.

So basically, the code below is the bare minimum of what I am doing. I have tried using various for() and while() loop techniques to try to get through all of the output data, but it seems to be no go. Note that BUFSIZE is defined at 0x1000 which is in fact more than the little test program I wrote for this needs even.

    ::ReadFile(_hChild_Out_Rd, chBuf, BUFSIZE, &dwRead, &o1);
chBuf[dwRead] = '\0';
::SetDlgItemTextA(global,IDO_WORLDOUT,chBuf);

So does anybody have any ideas?

Thanks so much for your help!

Regards,
Dennis M.

+2  A: 

That looks like synchronous code. With asynchronous (OVERLAPPED) I/O, you can't use the buffer until the operation completes. Set the hEvent member of the OVERLAPPED structure, and change your main loop from PeekMessage to MsgWaitForMultipleObjects so that your program can respond to I/O events. Then you can wait on both the OVERLAPPED operation handle, and also the process handle, so you know when the other program exited.

Ben Voigt
My only question now is how can I write the process to a named pipe? I created a name pipe and tried to run a process through it in the same fashion as an anonymous pipe, but cannot access the output. Any ideas?
RageD
An anonymous pope should work just fine for this purpose, but if you want to use named pipes, be aware that you have to open both ends separately (and both ends are bidirectional), unlike the anonymous case where you get a pair of handles to either end of a unidirectional pipe with a single API call.
Ben Voigt
Do the file handles returned by `CreatePipe()` even have `FILE_FLAG_OVERLAPPED` set? `CreatePipe()` doesn't have a "file mode" or "open mode" parameter.
bk1e
Yes, in my CreateNamedPipe(); code I have PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED being set on all the pipes. I am trying to create 2 instances for the pipe. One for the read end and one for the write end (using a for() loop to init them). I am using ::CreateNamedPipe(TEXT("\\\\.\\pipe\\arcworld"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,2, BUFSIZE*sizeof(TCHAR),BUFSIZE*sizeof(TCHAR), 10000, NULL);in order to (attempt) accomplish this. Before that though, I am creating an event and assigning it to my overlap event beforehand.
RageD
One side should call `CreateNamedPipe` (<-- this is the end you will read the results from), one side should call `ConnectNamedPipe` (<-- this is the end you should hand to the other program during CreateProcess). And for use as a standard output of another process, you should use stream-mode, not message-mode, for your pipe. Message-mode pipes require different functions, such as `TransactNamedPipe`, which the other program won't know to use.
Ben Voigt
A: 

You may want to look into memory mapped files, you can construct something in memory that is a queue and write from one place and read from the other. In a project I did I had a circular queue where one process was writing to the queue and the other was reading whenever it had time.

You create a memory mapped file like this:

SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;
sa.nLength = sizeof( sa );
sa.lpSecurityDescriptor = NULL;

hdFile = CreateFileMapping(INVALID_HANDLE_VALUE,&sa,
   PAGE_READWRITE,0,dwBufSize, L"SomeName");

Then you get a pointer to the shared memory like this

pOutSharedMemory=MapViewOfFile(hdFile,FILE_MAP_ALL_ACCESS,0,0,dwBufSize);

now you can map pointers to the memory

e.g. a couple of counters to keep track of where you are in memory (within dwBufSize)

pdwReadOffset = reinterpret_cast<DWORD*>(pOutSharedMemory)
pdwWriteOffset = pdwReadOffset + sizeof(DWORD)

then have some struct which you then read/write to the memory using memmove

you would have to take care of synchronization in place using critical sections etc.

Anders K.
You really do need to use a pipe, I'm not aware of any way to set the `hStdOutput` member of `STARTUPINFO` to a memory-mapped region, nor would the child process know how to synchronize access to the circular buffer. http://msdn.microsoft.com/en-us/library/ms686331(v=VS.85).aspx
Ben Voigt
by having different read write counters and having a mutex should be enough to synchronize them but I agree that a named pipe may be the best in OPs situation, just presenting an alternative that I have used a few years back.
Anders K.