Link to home
Start Free TrialLog in
Avatar of RJSoft
RJSoft

asked on

CreatePipe and a Callback

Hello all;

I want to know if I can use a pipe between my application and a callback function.
The examples that I find all create a thread. So I am wondering since a callback is basically it's own thread, why cant I pipe the output of the callback back to my application.

Also I can not use a named pipe. Because I want my application to be backwards compatible with Windows 98. Windows 98 does not support named pipes.

Here is the code I have so far. The code compiles clean but in the callback function I get an error message of "invalid handle" from the WriteFile.

Any ideals/clues/points apreciated!
RJ

#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <conio.h>
#include "madlldlib.h"


// Structure which encapsulates the data to be transferred from the
// writer thread to the reader thread.
struct ThreadData {
      HANDLE hWrThread;
      HANDLE hRdThread;
}thdData;


int twentyfour = 24;
unsigned long pbuffsz = 256+twentyfour;


/*
 * The callback function is used for reporting purposes and
 * garnering information about the source MP3 file. This callback
 * simply prints information to STDOUT, but it usually would be
 * used to increment a counter, calculate a percentage, or advance
 * a status bar.
 */

void __stdcall mycb (unsigned long fcnt, unsigned long bcnt, struct mad_header *mhdr)
{

      char pmsg[256];
      unsigned long bamt = 0;
      
      //thdData.hWrThread = GetCurrentThread(); //this side writes

      /*
       * If this is the first iteration (frame count is one)
       * then print out the MP3 layer information. Using this logic
       * one can retrieve information about the MP3 file, such as
       * channels, layer, etc., that might be useful to the calling
       * code.
       */      

      printf("\nIn mycb\n");
      getch();

      if (fcnt == 1) {
            

            sprintf(pmsg, "fc=%d,tb=%d,lyr=%d,md=%d,mdx=%d,emp=%d,br=%d,sr=%d",
                        fcnt,
                        bcnt,
                        mhdr->layer,
                        mhdr->mode,
                        mhdr->mode_extension,
                        mhdr->emphasis,
                        mhdr->bitrate,
                        mhdr->samplerate);


            if (WriteFile(thdData.hWrThread, &pmsg, 256, &bamt, NULL) == 0)
            {
            DWORD err=GetLastError();
             LPVOID lpMsgBuf;
            FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
            (LPTSTR) &lpMsgBuf,
            0,
            NULL
            );
            // Process any inserts in lpMsgBuf.
            // ...
            // Display the string.
            printf("\nFnct==1 %s %ld\n",(LPCTSTR)lpMsgBuf, err);
            // Free the buffer.
            LocalFree( lpMsgBuf );
              return;            
            }//endif


      }
      else {
            
            sprintf(pmsg, "fc=%d,tb=%d", fcnt, bcnt);

            if (WriteFile(thdData.hWrThread, &pmsg, 256, &bamt, NULL) == 0)
            {
            DWORD err=GetLastError();
             LPVOID lpMsgBuf;
            FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
            (LPTSTR) &lpMsgBuf,
            0,
            NULL
            );
            // Process any inserts in lpMsgBuf.
            // ...
            // Display the string.
            printf("\nElse->%s %ld\n",(LPCTSTR)lpMsgBuf, err);
            // Free the buffer.
            LocalFree( lpMsgBuf );
              return;            

            }//endif            


      }//endelse

      printf("\nat end of mycb\n");
      getch();
      
}//endcallbackfunc


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

      DWORD dwRead;
      BOOL    bReturn = FALSE;            
      char statmsg[256];
      int status;
      
      argc=4;
      argv[0]="armslen";
      argv[1]="piano.mp3";
      argv[2]="test.wav";
      argv[3]="wav";

      
      if (argc != 4) {
            printf("\nusage: %s 'input.mp3' 'output file' 'pcm|wav'\n", argv[0]);
            return 1;
      }

      
      // Creates an anonymous pipe.
      bReturn = CreatePipe(&thdData.hRdThread,&thdData.hWrThread,
            NULL,sizeof(pbuffsz));
      
      if (bReturn != FALSE)
      {
            printf("CreatePipe worked");
            getch();

            thdData.hRdThread = GetCurrentThread(); //this side reads

            //Call function here
            if (strcmp(argv[3], "pcm")==0) {            
            status = CbMpegAudioDecoder(argv[1], argv[2], 0, statmsg, mycb);
            }
            else {                                                      
            status = CbMpegAudioDecoder(argv[1], argv[2], 1, statmsg, mycb);
            }


            if(statmsg)
            {
                  printf("\n%s\n",statmsg);
                  printf("\nError in calling library function\n");
                  return 1;

            }//endif

            printf("Call to lib worked");
            getch();

            char c=-1;
            while (c != '\0')
            {

        ReadFile(thdData.hRdThread, &c, 1, &dwRead, NULL);

        if (dwRead > 0 && c != 0) putchar(c);
            }

          
      }//endif!False
      else
      {
            printf("\nError in createpipe\n");
            return 1;
      }

return 0;

}//endmain
Avatar of jkr
jkr
Flag of Germany image

How does 'mycb' get the information about 'thdData'?
Avatar of mxjijo
mxjijo


In theory, there is no problem writing to a pipe from a call back routine :)

However, as jkr suggested thdData would be a place to look at to see why you get the error.
Looks like thdData is a global variable, so make sure its fields are not getting corrupted.

I think there is one more issue with the code.

>>      // Creates an anonymous pipe.
>>     bReturn = CreatePipe(&thdData.hRdThread,&thdData.hWrThread,NULL,sizeof(pbuffsz));

I notice you do
unsigned long pbuffsz = 256+twentyfour;

Looks like you are trying to set the Pipe buffer size to be 256+24=280 bytes.
But you are passing sizeof(pbuffsize) to the CreatePipe() call which = 4.
So, just a heads up - you'll get a problem somewhere down the road.



Avatar of RJSoft

ASKER

jkr & mxjijo;

Thanks for your quick replies.

The thdData structure is in Global file namespace. So it is available anywhere in the .cpp file.
I do not believe that the value of thdData.hWrThread should get corrupted.

I am new to pipes. So I am trying to understand the basic layout and mold it to what I want.
I believe that CreatePipe simply establishes a conduit between threads just by the thread handle/name. Because in the example the handles are set after the pipe is created.

The line below establishes the read side (which is the parent in this case)
thdData.hRdThread = GetCurrentThread(); //this side reads

The next line below was an attempt to obtain a thread handle for mycb to allow the WriteFile function. But it failed as GetLastError reported "Invalid Handle" for the WriteFile function.

//thdData.hWrThread = GetCurrentThread(); //this side writes

But leaving it out produces no error but also produces no output. I almost think that it's stuck in a thread lock. But dont know. Maybe I should use something else besides GetCurrentThread?

Here is a link to the original example that I am trying to modify to use the callback.
http://www.PicPuppy.com/example.cpp it works. I am wondering if I should bother having the thread call the callback. But I want this code to be simple as I intend to use the anonymous pipe for allot more of my functions.

In the original (example.cpp) it creates a thread and the thread is the "read" side, while the application is the "write" side of the anonymous pipe. I need the opposite.

I am also wondering if the mycb might not be a child process and if an anonymous pipe expects the relationship to be strictly parent child. But I do not believe that because mycb is created / called from the main app. But I dont know. Because the callback is independant of the class so is it another process? Maybe a Windows owned process?

Later (after previous post) I changed the putchar at the end of the main in the ReadFile loop to a printf to see if I could output the char's to screen. But no results.

I am also confused how or if I should put a WaitForSingleObject maybe somewhere after the call to the library function (which calls the callback). But I dont know if that makes any sense. Because the callback is called repeatedly by the library to allow the developers to provide visual feedback. To show visual elements of the mp3 conversion to wav. I cant think of a place that would fit. Because I believe I need to syncronize the threads so they communicate through the pipe.

In the example.cpp they alternated threads by waking one up and putting the other to sleep. So a little writing gets done to fill the buffer (pipe) then a little reading gets done to display the values to the screen. Later to be modified to be sent to control of my choice. (Most likely CStatic).

What I am trying to do is modify some code found in Code project which uses named pipes. To have it backwards compatible to Windows 98. That way all bases are covered.

The madlib author gilad produced an excellent dll to convert mp3 to wav.
http://www.codeproject.com/audio/madlldlib.asp
Also my modified version is at http://www.PicPuppy.com/arm.zip
which uses the libmad GNU library. Since it is GNU/GPL and not GNU/LGPL (lesser) the only way to avoid sharing the total sum of ones private source code is to use named pipes.
Because the output produced does not belong to the GNU.

The author gilad provided a named pipe example code. armslen.cpp and armslen_test.cpp the first being the server side of the named pipe and the second the client (armslen_test.cpp).

To me this is a very important issue. Because when I began my project I did not fully understand the GNU policy. (I am not a lawyer) My understanding was that all I had to do was to provide a link to the authors site which in effect provides users of my software access to the source code and the GNU licensing scheme. I believe that using anonymous pipes will protect the rights I have to my code and not allow the GNU to use it's extortion tactics to extrapolate my hard earned God given rights to the code I produced.

Then hopefully I can create mini exe's for each GNU implementation and only be responsible to reveal that source code. As the GNU states it's "intentions" are to only obtain the code that uses the libraries/exes. But it seems they want more.

RJ


     
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
ASKER CERTIFIED SOLUTION
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

jkr is correct. the thread handles are getting overwritten
good luck.