Solved

CreatePipe and a Callback

Posted on 2004-08-23
6
836 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
Comment Utility
How does 'mycb' get the information about 'thdData'?
0
 
LVL 8

Expert Comment

by:mxjijo
Comment Utility

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
Comment Utility
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
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 86

Assisted Solution

by:jkr
jkr earned 250 total points
Comment Utility
>>//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
Comment Utility

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
Comment Utility

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

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

With most software applications trying to cater to multiple user needs nowadays, the focus is to make them as configurable as possible. For e.g., when creating Silverlight applications which will connect to WCF services, the service end point usuall…
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and 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…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

771 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now