Link to home
Start Free TrialLog in
Avatar of MellowD0c
MellowD0c

asked on

CreatePipe() guru(s) needed

I have 3 programs:

filesrc.exe : This file reads a text file and spits out all data

filefilter.exe :  This file takes in data coming from filesrc.exe and does few checks and then spits it out

filewrite.exe:  This file takes in data coming form filefilter and then puts it in some file.

(Thats how i should copy one file to another)

I have all the above 3 files working but can't figure out how will i write one main application to control the above 3 files. In other words i want to make a main file that would execute the above programs and properly pass on the required data to one another using handles , CreateProcess() and CreatePipe().


So far i have this much(below), but i am lost as to how will i hand over a handle from one process to another( data bouncing in b/w the above three files). More or less copy file from one to another with the help of above files and in above mentioned manner.


int main(int argc, char *argv[])
{
   SECURITY_ATTRIBUTES pipeAttr;
 
   pipeAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
   pipeAttr.bInheritHandle = TRUE;
   pipeAttr.lpSecurityDescriptor = NULL;

   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &pipeAttr, 0))
   {

        cout << "error creating pipe" << endl;
          exit (1);
     }
   .........?



ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany 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
Avatar of Salte
Salte

You should CreatePipe() twice in your program.

This provides 4 file handles, 2 for each pipe A and B.

Each of these pipe have a read handle and a write handle, the read handle for A is RA and WA is the write handle. Similarly for RB and WB.

Ok. Then you CreateProcess for the first filesrc.exe provide the arguments (presumably the file to read from is an argument) but most importantly provide it a STDOUT handle of WA. I.e. you provide WA as that process' stdout handle.

Then you CreateProcess for the filefilter and provide RA as the STDIN handle and WB as the STDOUT handle.

Then you CreateProcess for the filewrite.exe and provide RB as the STDIN handle and presumably an argument for the file to write to.

Then you just sit and wait for the processes to finish. Specifically the last process to finish is the filewrite.exe process so a good bet is to wait for that process. You should probably also wait for the others but that can be done after filewrite.exe has stopped.

If all processes exit with success (normally with 0 as exit-code) the job is done.

If you write the code for the processes you determine if they return 0 or other code for success/failure but the regular thing to do is 0 for success and 1 for failure. However, some programs do things differently, be aware that shell scripting etc usually assume 0 -> success, 1 -> failure.

If you don't know how to CreateProcess() to create a process and provide a specific handle as STDIN/STDOUT etc the above html link provided by jkr will tell you the details on how to do that.

Alf
You can use regular shell pipes instead of programmatically do it. (It should work on any unix shell and also on windows/msdos shell).

Just write from a shell:
filesrc.exe | filefilter.exe | filewrite.exe > fileName

fileName is the name of the file you want to write too.

If you want, you can also run the above command programmatically  using
the "system" system call. Just pass the above string
to system and it will create a shell that do the rest.

Hope it helps.
You can use regular shell pipes instead of programmatically do it. (It should work on any unix shell and also on windows/msdos shell).

Just write from a shell:
filesrc.exe | filefilter.exe | filewrite.exe > fileName

fileName is the name of the file you want to write too.

If you want, you can also run the above command programmatically  using
the "system" system call. Just pass the above string
to system and it will create a shell that do the rest.

Hope it helps.
Avatar of MellowD0c

ASKER

int main (int argc, char* argv [])


{
     DWORD i;
     HANDLE hReadPipe, hWritePipe;
     char command1 [MAX_PATH];
     char command2 [MAX_PATH];
     SECURITY_ATTRIBUTES PipeSA = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
               /* Init for inheritable handles. */
         
     PROCESS_INFORMATION ProcInfo1, ProcInfo2;//, ProcInfo3;
     STARTUPINFO StartInfoCh1, StartInfoCh2;//, StartInfoCh3;
     

     /* Startup info for the two child processes. */

     GetStartupInfo (&StartInfoCh1);
     GetStartupInfo (&StartInfoCh2);
     //GetStartupInfo (&StartInfoCh3);

     

     /* Create an anonymous pipe with default size.
          The handles are inheritable. */

     if (!CreatePipe (&hReadPipe, &hWritePipe, &PipeSA, 0))
     {
          cerr << "Anon pipe create failed." <<endl;
          return true;
     }

     /* Set the output handle to the inheritable pipe handle,
          and create the first processes. */

     StartInfoCh1.hStdInput  = GetStdHandle (STD_INPUT_HANDLE);
     StartInfoCh1.hStdError  = GetStdHandle (STD_ERROR_HANDLE);
     StartInfoCh1.hStdOutput = hWritePipe;
     StartInfoCh1.dwFlags = STARTF_USESTDHANDLES;

     strcpy(command1, "filesrc.exe ");
     strcat(command1, argv[1]);

     if (!CreateProcess (NULL, command1, NULL, NULL,
               TRUE,               /* Inherit handles. */
               0, NULL, NULL, &StartInfoCh1, &ProcInfo1))
     {
          cout << "CreateProc1 failed." <<endl;
          return true;
     }
     CloseHandle (ProcInfo1.hThread);
     CloseHandle (hWritePipe);

     /* Repeat (symmetrically) for the second process. */

     StartInfoCh2.hStdInput  = hReadPipe;
     StartInfoCh2.hStdError  = GetStdHandle (STD_ERROR_HANDLE);
     StartInfoCh2.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
     StartInfoCh2.dwFlags = STARTF_USESTDHANDLES;

     strcpy(command2, "filewrite.exe ");
     strcat(command2, argv[2]);

     if (!CreateProcess (NULL, command2, NULL, NULL,
               TRUE,               /* Inherit handles. */
               0, NULL, NULL, &StartInfoCh2, &ProcInfo2))
     {
          cout << "CreateProc1 failed." <<endl;
          return true;
     }
     CloseHandle (ProcInfo2.hThread);
     CloseHandle (hReadPipe);

     /* Wait for both processes to complete.
          The first one should finish first, although it really does not matter. */
     cout << "i am here" << endl;
     WaitForSingleObject (ProcInfo1.hProcess, INFINITE);
     WaitForSingleObject (ProcInfo2.hProcess, INFINITE);
     CloseHandle (ProcInfo1.hProcess);
     CloseHandle (ProcInfo2.hProcess);
     return 0;
}

Salte, actually what you told is what i wanted to do, but i am still not sure as to how i will hand over handle from one process to another. Is it like i start process and then for instance my source.exe will read and write to stdout and then other process comes and read THAT stdout as stdin and processes it. Then will come my third process read the stdout of last process and make it as stdin and then make stdout to a file mentioned as argv[2] in command line. But... for the time i have take out my middle file just to have an idea of how things are working, so here is what above code SHOULD do, but it doesn't.

User types: pipe sometexttoread.txt sometexttowrite.txt

Now the above program should start and call my sourc.exe function to read the file and then put that to stdout and my filewrite.exe to get that into stdin and write out to file. However, it just does nothing, though it goes all the way and prints out "i am here" in end. What am i doing wrong here?

I am not 100% sure of the STARTF_USE_STDHANDLES. I assume it means that the hStdInput etc contain valid values and as such that part seems ok.

The only question is if the handles are inheritable and what the filesrc.exe does with that handle.

Is it possible for you to write some debug info to some other place. For example proc1 has stderr set to same place as the parent process so the reader program could write some info to cerr, indicating it is starting up - reading from pipe etc... and so on until you see it working.

The code you wrote above appear to be correct but it could be some nitty details in the CreateProcess() call.

One thing is that you should wait for process2 before waiting for process 1. This is because when process2 is done you "know" that process1 is already done and so WaitForSingleObject on process1 shouldn't actually do any waiting.

Not sure if closing the handle to the thread is necessarily a good idea. If you move that to after WaitForSingleObject etc, would that help?

Kinda hard for me to check your code as I am sitting on a linux machine at the moment :-) I have windows at home and can check out some stuff when I get home though.

Bottom line: I am kinda suspicious concerning the inheritability of the handles and the setup. It appear correct but there could be a detail or two that we both overlook. I am also suspicous about that closehandle on the thread, it should be OK to close the thread though so I doubt it is a problem but I am not 100% confident it is no problem. The pipe should be safe to close though if the child process has started and gotten it from startupinfo.

Alf
Have you checked the link I posted? The sample code on the page covers this issue...

Ok guys, here is it finally, The following program Works 100%, if i make a few changes and that is that, if i take out 3rd process from program, it works fine and copies the file. But if i insert the third process now, it works too but it creates a copy of blank file 0 bytes. Can anyone check and tell me what i have done wrong? i am pretty sure that the information from 2nd process doesn't seem to be passing on to 3rd one, but what specifically i would be doing wrong and how can i correct it so that pipes up correctly and hands over data from 1st process to 3rd as is.



#include <iostream>
#include <windows.h>
#include <winbase.h>
#include <iomanip.h>

using namespace std;

void DisplayError(char *pszAPI);

int main (int argc, char* argv [])


{
      //DWORD i;
      HANDLE hReadPipe, hWritePipe, hReadPipe2, hWritePipe2;
      char command1 [MAX_PATH];
      char command2 [MAX_PATH];
      SECURITY_ATTRIBUTES PipeSA = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
                  /* Init for inheritable handles. */
            
      PROCESS_INFORMATION ProcInfo1, ProcInfo2, ProcInfo3;
      STARTUPINFO StartInfoCh1, StartInfoCh2, StartInfoCh3;
      

      /* Startup info for the two child processes. */

      GetStartupInfo (&StartInfoCh1);
      GetStartupInfo (&StartInfoCh2);
      GetStartupInfo (&StartInfoCh3);

      

      /// Create an anonymous pipe with default size.
            //The handles are inheritable.

      if (!CreatePipe (&hReadPipe, &hWritePipe, &PipeSA, 0))
      {
            DisplayError("Pipe create failed." );
      }

      if (!CreatePipe (&hReadPipe2, &hWritePipe2, &PipeSA, 0))
      {
            DisplayError("Pipe create failed." );
      }
      // Set the output handle to the inheritable pipe handle,
      //and create the first processes.

      StartInfoCh1.hStdInput  = GetStdHandle (STD_INPUT_HANDLE);
      StartInfoCh1.hStdError  = GetStdHandle (STD_ERROR_HANDLE);
      StartInfoCh1.hStdOutput = hWritePipe;
      StartInfoCh1.dwFlags = STARTF_USESTDHANDLES;
      
      strcpy(command1, "source.exe ");
      strcat(command1, argv[1]);

      
      if (!CreateProcess (NULL, command1, NULL, NULL,
                  TRUE,                  // Inherit handles.
                  0, NULL, NULL, &StartInfoCh1, &ProcInfo1))
      {
            DisplayError("CreateProc1 failed.");
                  
      }
      CloseHandle (ProcInfo1.hThread);
      CloseHandle (hWritePipe);

   
      StartInfoCh2.hStdInput  = hReadPipe;
      StartInfoCh2.hStdError  = GetStdHandle (STD_ERROR_HANDLE);
      StartInfoCh2.hStdOutput = hWritePipe2;
      StartInfoCh2.dwFlags = STARTF_USESTDHANDLES;
      //HANDLE hwritefile;
      //StartInfoCh2.hStdOutput = hwritefile;

      
      strcpy(command2, "filter.exe ");
      //strcat(command2, argv[2]);
      
      
      if (!CreateProcess (NULL, command2, NULL, NULL,
                  TRUE,                  // Inherit handles.
                  0, NULL, NULL, &StartInfoCh2, &ProcInfo2))
      {
            DisplayError("CreateProc2 failed." );
            //exit(1);
      }
      CloseHandle (ProcInfo2.hThread);
      CloseHandle (hReadPipe);
      CloseHandle (hWritePipe2);


      //Repeat (symmetrically) for the second process.
      HANDLE hwritefile;
      hwritefile= argv[2];
      StartInfoCh3.hStdInput  = hReadPipe2;
      StartInfoCh3.hStdError  = GetStdHandle (STD_ERROR_HANDLE);
      StartInfoCh3.hStdOutput = hwritefile;
      //StartInfoCh3.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
      StartInfoCh3.dwFlags = STARTF_USESTDHANDLES;
      
      
      strcpy(command2, "sink.exe ");
      strcat(command2, argv[2]);
      
      
      if (!CreateProcess (NULL, command2, NULL, NULL,
                  TRUE,                  /* Inherit handles. */
                  0, NULL, NULL, &StartInfoCh3, &ProcInfo3))
      {
            DisplayError("CreateProc3 failed." );
            //exit(1);
      }
      CloseHandle (ProcInfo3.hThread);
      CloseHandle (hReadPipe2);
      //CloseHandle (ProcInfo1.hThread);
      CloseHandle (hwritefile);



      


      // Wait for both processes to complete.
      WaitForSingleObject (ProcInfo2.hProcess, INFINITE);
      WaitForSingleObject (ProcInfo1.hProcess, INFINITE);
      WaitForSingleObject (ProcInfo3.hProcess, INFINITE);
      
      CloseHandle (ProcInfo1.hProcess);
      CloseHandle (ProcInfo2.hProcess);
      CloseHandle (ProcInfo3.hProcess);
      return 0;
}
//********taken from http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B190351***
 ///////////////////////////////////////////////////////////////////////
   // DisplayError
   // Displays the error number and corresponding message.
   ///////////////////////////////////////////////////////////////////////
   void DisplayError(char *pszAPI)
   {
       LPVOID lpvMessageBuffer;
       CHAR szPrintBuffer[512];
       DWORD nCharsWritten;

       FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
                NULL, GetLastError(),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lpvMessageBuffer, 0, NULL);

       wsprintf(szPrintBuffer,
         "ERROR: API    = %s.\n   error code = %d.\n   message    = %s.\n",
                pszAPI, GetLastError(), (char *)lpvMessageBuffer);

       WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
                     lstrlen(szPrintBuffer),&nCharsWritten,NULL);

       LocalFree(lpvMessageBuffer);
       ExitProcess(GetLastError());
   }
   //**********************************************************

jkr, thanks to you as welll, i did check it and got lost. There was just too much info an i needed only this much. But hey, i stole that function to display errors, and its cool!
Also, i have uploaded my filter.exe sink.exe and source.exe to these places, so if you want to download and check for what i could be doing wrong??

http://saudaziz.tripod.com/Debug.zip

put mouseover, rightclick and save target

otherwise you will get error from Tripod...
ok guys sorry, but i found the problem, i will find some EE guy to split up the points and give to jkr and salte
The hwritefile handle seem suspicious, you declare the variable but never provide it a value...

Alf
MellowD0c

You asked to split points between Salte and jkr
I have reduced the points on this question from 155 to 76 as indicated by your request at Community Support. Please copy the URL and create a new question in this topic area for the other Experts to whom you wish to award points. The title of the question should read "Points for", followed by the Expert's name. In the question itself, you should paste the link to the original question and perhaps a comment stating that the points are for their help with that question. Once you have created the new questions, you can go back to the original, and accept the comment from the Expert for whom you did not create a new question. The Experts will  comment in your new "Points for" question(s), which you then accept and grade to close.
If you have any questions, please don't hesitate to ask.
Thank you.

** Mindphaser - Community Support Moderator **