DTWAIN Notifications Using a Callback Function

Top  Previous  Next

To retrieve DTWAIN notifcations using a user-defined callback function, the DTWAIN_SetCallback or DTWAIN_SetCallback64 function must be called.

 

This section discusses mostly the use of DTWAIN_SetCallback.  The DTWAIN_SetCallback64 function works exactly the same way as DTWAIN_SetCallback, except that the user-defined data for DTWAIN_SetCallback64 is a 64-bit integer, as opposed to a 32-bit integer.

 


 

Once your callback function has been called by DTWAIN, you will need to know exactly what DTWAIN is informing your application of.  To get the notification, the notification code is always passed as the 'WPARAM' value of the DTWAIN message.  The current Source is always sent as the LPARAM value.

 

The advantage of using callback functions over a window procedure to route the notifications is that an application may not even have a window, but would still desire to retrieve notifications.   Another advantage is that you can keep the window procedure clean of any code associated with DTWAIN.

 

A small section of code is provided here to illustrate how to use DTWAIN_SetCallback function properly (C / C++ example):

 

#include <dtwain.h>

/* Prototype for the function */

LRESULT CALLBACK AcquireProc(WPARAM w, LPARAM l, LONG UserData);

 

void SomeFunc( )

{

    DTWAIN_SOURCE Source;

....

    DTWAIN_SysInitialize( );

 

    /* Set notification flag */

    DTWAIN_EnableMsgNotify( TRUE );

 

    /* Set the callback function */

    DTWAIN_SetCallback(AcquireProc, 0);

 

    /* ... */

}

 

LRESULT CALLBACK AquireProc(WPARAM wParam, LPARAM lParam, LONG UserData)

{

   switch (wParam )      

   {

       /* Notification of acquisition being started */

       case DTWAIN_TN_ACQUIRESTARTED:

       break;

 

       /* Transfer is about to take place */

       case DTWAIN_TN_TRANSFERREADY:

       break;

 

        /* Transfer was done! */

       case DTWAIN_TN_TRANSFERDONE:

       break;

 

       /* Acquired all pages.  We can get the DIBs! */

       case DTWAIN_TN_ACQUIREDONE:

       break;

    }

    return 1;  /* Always return 1, except for some notifications noted in the DTWAIN Notification Codes section */

}

 

AcquireProc is called automatically by DTWAIN whenever a notification is sent while acquiring the image.  Note that the AcquireProc in the example above always returns 1 (this is very important).  Note that AcquireProc is a function pointer with the following prototype (C/C++ is given as an example):

 

LRESULT  CALLBACK DTWAINCallbackProc(WPARAM wParam, LPARAM lParam, LONG UserData);

 

For DTWAIN_SetCallback64, the UserData is a 64-bit integer type, rather than a 32-bit type:

LRESULT  CALLBACK DTWAINCallbackProc64(WPARAM wParam, LPARAM lParam, DTWAIN_LONG64 UserData);

 

The DTWAIN notification callback functions must be coded with these parameters (of course, they do not have to be named AcquireProc or DTWAINCallbackProc -- any legal function name appropriate to the programmer will do).

 

The LRESULT is actually a LONG value.   The CALLBACK is a constant defined by windows.h to signify that this function will be a callback function (the CALLBACK constant is only important for C or C++ programs).   The wParam and lParam values are the WPARAM and LPARAM of the notification that is sent to the procedure.  The WPARAM and LPARAM values are exactly the same values that are sent when a DTWAIN notification is sent to a window.  For example, if WPARAM is DTWAIN_TN_ACQUIREDONE, the LPARAM is actually the DTWAIN_SOURCE that has finished acquiring an image.

 

The DTWAIN callback function must always return TRUE for proper processing to be done.  The only exception to this rule is if the WPARAM value is DTWAIN_TN_PAGECONTINUE or DTWAIN_TN_TRANSFERREADY (see DTWAIN Notification Codes for more information).  

 

To enable DTWAIN_SetCallback, the DTWAIN_EnableMsgNotify( ) must have previously been called with a TRUE value.

 

If both DTWAIN_StartTwainSession( ) has been called with a valid window handle as the first parameter and there is a callback set with DTWAIN_SetCallback, the window procedure will always get the notification first, and then the callback will get the same notification afterwards.  Therefore if both notification techniques (window procedure and callback) are used, your application must make sure that the callback will ultimately control what happens, since it is the final functions that will be called when a notification is sent.

 

Note that the return value for DTWAIN_SetCallback is the previously set callback function.  The reason for the return value being the previously set function is that this will allow function chaining.  For example, if you need to override the behavior of a previous callback function, you install your function, and store the pointer to the old function someplace.  Whenever your callback gets called, you can do whatever processing needs to be done with your handler, and then call the old handler that your application saved somewhere to do the default handling (or possibly you call the old handler first, and then your new handler finishes up).

 

 

Other Languages:

Which languages can use callback functions?  Certainly C and C++ can be used.  So can later versions (Version 5 and above) of Visual Basic.  How would you know if your language supports callback functions?  The easiest way to find out is if the language you are working with can call the Windows API function EnumWindows.  The EnumWindows function requires a pointer to a function as one of the arguments.  If you can get your language to call this function successfully, you can call DTWAIN_SetCallback with no problems.

 

Note to C++ programmers:  Please read!!

You cannot, repeat, cannot use a pointer to a non-static class member function as the callback function!!  The reason why is that the callback is prototyped as a general, non-class function.  A non-static class member function is not compatible with a traditional C or C++ function.  A non-static class member function contains a hidden this pointer that makes it impossible to fit in with the prototype of the callback function.  Also, the syntax required to call a non-static member using a pointer requires that the object be known to the caller.  Since DTWAIN knows absolutely nothing about your applications classes and objects, it is impossible to call your non-static member function.

 

However a static class member function can be used as a callback function, since there is no this pointer argument hidden in the prototype, and you do not need to know the class to make the callback work.

 

Example:

class A {

   public:

           LRESULT CALLBACK AcquireProc1(WPARAM w, LPARAM l, LONG UData);

           static LRESULT CALLBACK AcquireProc2(WPARAM w, LPARAM l, LONG UData);

           Func( );

};

 

 

void DummyFunc( )

{

    A objectA;

    DTWAIN_SetCallback(objectA.AcquireProc1, 0);  /* Error!  Cannot assign non-static member function to non-member function pointer */

    DTWAIN_SetCallback(A::AcquireProc2, 0); /* OK!  Static functions are compatible with non-member function pointers */

}

 

The error for the first attempted call will be something like this (Visual C++ 6):

 

error C2664: 'DTWAIN_SetCallback':  cannot convert parameter 1 from 'long (unsigned int,long)' to 'long (__stdcall *)(unsigned int,long)'

 

A lot of novice C++ programmers, as well as advanced 'C' programmers that are starting in C++ make the mistake of trying to coerce a non-static member function to be the target of a callback and are perplexed by the error that the compiler spits out back at them.  Don't waste time trying to do this, since this is an impossible task; the C++ language just does not allow this.   Use traditional functions as the callback (as the example above suggests), or at least, use static class member functions as callback functions.    If you need to access members of a certain object, pass a pointer to the object (or the this pointer) in the UserData parameter of DTWAIN_SetCallback:

 

DTWAIN_SetCallback(A::AcquireProc2,  (LONG)&objectA); /* Passing address of object to callback function */

 

When the callback is invoked, your application can retrieve the instance from the User Data parameter:

 

LRESULT CALLBACK A::AcquireProc2(WPARAM w, LPARAM l, UserData Data)

{

  A* pA = static_cast<A*>(Data);  // Cast the data to get the correct pointer.

  pA->Func( );

  return TRUE;

}