Solved

CreatePipe and a Callback

Posted on 2004-08-23
6
851 Views
Last Modified: 2013-12-03
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
0
Comment
Question by:RJSoft
  • 3
  • 2
6 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 11872344
How does 'mycb' get the information about 'thdData'?
0
 
LVL 8

Expert Comment

by:mxjijo
ID: 11872828

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.



0
 
LVL 3

Author Comment

by:RJSoft
ID: 11874945
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


     
0
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
LVL 86

Assisted Solution

by:jkr
jkr earned 250 total points
ID: 11875067
>>//thdData.hWrThread = GetCurrentThread(); //this side writes
>>But leaving it out produces no error but also produces no output.

You need to null that oud on the reading side also. The pipe handles is what you want to store there, not the thread handles. You are overwriting what you created using

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

when you do these assignments.
0
 
LVL 8

Accepted Solution

by:
mxjijo earned 250 total points
ID: 11875089

Pipe handles has nothing to do with thread handles.
CreatePipe() creates two handles at a time in stead of calling the API twise (like Create named pipe)
Take a look at this simple example



#include <windows.h>
#include <stdio.h>

HANDLE hPipeReadHandle = NULL;
HANDLE hPipeWriteHandle = NULL;
BOOL Quit = FALSE;

void
ReaderThread(void* pHandle)
{
      HANDLE hReader = (HANDLE)pHandle;
      char szBuffer[80];
      DWORD BytesRead = 0;

      while(!Quit)
      {
            memset(szBuffer, 0, 80);
            BytesRead = 0;

            BOOL Ok = ReadFile(hReader, szBuffer, 80, &BytesRead, NULL);
            if (!Ok)
                  printf("\nReadFile Failed. Err=%d", GetLastError());
            else
                  printf("\n%d Byes read. szBuffer = [%s]", BytesRead, szBuffer);
      }
}



void
WriterThread(void* pHandle)
{
      char szBuffer[80];
      DWORD BytesWritten = 0;

      HANDLE hWriter = (HANDLE)pHandle;
      sprintf(szBuffer, "Hello! from Writer thread");
      while(!Quit)
      {
            BytesWritten = 0;
            BOOL Ok =
                  WriteFile(hWriter, szBuffer, strlen(szBuffer), &BytesWritten, NULL);

            if (!Ok || BytesWritten != strlen(szBuffer))
                  printf("\nWriteFile Failed. Err=%d", GetLastError());

            Sleep(1000);
      }
}

void
main()
{

      BOOL Ok = CreatePipe(&hPipeReadHandle,
                                     &hPipeWriteHandle,
                                     NULL,
                                     1024);

      if (Ok)
      {
            HANDLE hThread;
            DWORD Id = 0;

            hThread = CreateThread(NULL, 0,
                              (LPTHREAD_START_ROUTINE)ReaderThread, hPipeReadHandle,
                              0,
                              &Id);

            hThread = CreateThread(NULL, 0,
                              (LPTHREAD_START_ROUTINE)WriterThread, hPipeWriteHandle,
                              0,
                              &Id);

            getchar();

            Quit = TRUE;
      }
}
0
 
LVL 8

Expert Comment

by:mxjijo
ID: 11875100

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

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

As more and more people are shifting to the latest .Net frameworks, the windows presentation framework is gaining importance by the day. Many people are now turning to WPF controls to provide a rich user experience. I have been using WPF controls fo…
For a while now I'v been searching for a circular progress control, much like the one you get when first starting your Silverlight application. I found a couple that were written in WPF and there were a few written in Silverlight, but all appeared o…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
I've attached the XLSM Excel spreadsheet I used in the video and also text files containing the macros used below. https://filedb.experts-exchange.com/incoming/2017/03_w12/1151775/Permutations.txt https://filedb.experts-exchange.com/incoming/201…

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question