Passing static member function as callback to Windows DLL



 DEVELOP > c-Plus-Plus > Passing static member function as callback to Windows DLL

LINK TO THIS PAGE  


rating :  0   |  0


  Page 1 of 1

1

 
Topic: DEVELOP > c-Plus-Plus
User: "Evan Burkitt"
Date: 23 Aug 2007 10:49:26 PM
Object: Passing static member function as callback to Windows DLL
Hi, all.
I have a Windows DLL that exports a number of functions. These functions
expect to receive a pointer to a callback function and an opaque void*
parameter. The callback functions are typedef'd to take void* parameters,
through which the DLL's function passes its void* parameter to the callback
function. This allows me to use class instances as callback handlers, as in
the example below.
//Executable and DLL know this:
typedef void (*callback)(void*);
//in DLL "Exporter.dll":
void ExportedFn(callback cb, void *opaque) { cb(opaque); }
//in executable:
class CallbackHandler
{
public:
static void Thunk(void *fromDll)
{
CallbackHandler *handler = static_cast<CallbackHandler*>(fromDll);
...
}
};
....
HANDLE dllInst = ::LoadLibrary("Exporter.dll");
typedef void (*DllExport)(callback, void*);
DllExport exportedFn = reinterpret_cast<DllFn>(::GetProcAddress(dllInst,
"ExportedFn"));
CallbackHandler handlerInst;
exportedFn(CallbackHandler::Thunk, &handlerInst); /**/
....
What bothers me is that CallbackHandler::Thunk() must have a void* parameter
to match the callback signature, even though it's part of the
CallbackHandler class and its fromDll parameter is and must be a
CallbackHandler*.
My question is, is there a 'best practices' way to let Thunk()s parameter be
a CallbackHandler* and yet have the line marked with /**/ compile? By 'best
practice', I mean less of a blunt instrument than reinterpret_cast<> or a
C-style cast.
-Evan
.

User: "Gianni Mariani"

Title: Re: Passing static member function as callback to Windows DLL 24 Aug 2007 02:12:26 AM
Evan Burkitt wrote:

Hi, all.

I have a Windows DLL that exports a number of functions. These functions
expect to receive a pointer to a callback function and an opaque void*
parameter. The callback functions are typedef'd to take void* parameters,
through which the DLL's function passes its void* parameter to the callback
function. This allows me to use class instances as callback handlers, as in
the example below.

//Executable and DLL know this:
typedef void (*callback)(void*);

//in DLL "Exporter.dll":
void ExportedFn(callback cb, void *opaque) { cb(opaque); }

//in executable:
class CallbackHandler
{
public:
static void Thunk(void *fromDll)
{
CallbackHandler *handler = static_cast<CallbackHandler*>(fromDll);
...
}
};
...
HANDLE dllInst = ::LoadLibrary("Exporter.dll");
typedef void (*DllExport)(callback, void*);
DllExport exportedFn = reinterpret_cast<DllFn>(::GetProcAddress(dllInst,
"ExportedFn"));
CallbackHandler handlerInst;
exportedFn(CallbackHandler::Thunk, &handlerInst); /**/
...

What bothers me is that CallbackHandler::Thunk() must have a void* parameter
to match the callback signature, even though it's part of the
CallbackHandler class and its fromDll parameter is and must be a
CallbackHandler*.

My question is, is there a 'best practices' way to let Thunk()s parameter be
a CallbackHandler* and yet have the line marked with /**/ compile? By 'best
practice', I mean less of a blunt instrument than reinterpret_cast<> or a
C-style cast.

If you're stuck with having to call ::GetProcAddress and pull names from
a DLL, so be it, however there are much more elegant ways of doing this.
One way of looking at this is that the DLL contains a number of
factories. Upon loading, the factories automagically register
themselves and you can avoid all of the casts. I use this technique
with DLL's and it's portable - no need to worry about mangled names.
Below is a suggestion - it uses templates to create yet another function
that calls the function you want. There are some limitations.
#include <iostream>
//Executable and DLL know this:
typedef void (*callback)(void*);
//in DLL "Exporter.dll":
void ExportedFn(callback cb, void *opaque) { cb(opaque); }
template <typename T, void (*F)( T* )>
struct Thunker
{
static void Do( void * fromDll )
{
F( static_cast<T*>(fromDll) );
}
};
//in executable:
class CallbackHandler
{
public:
static void Thunk(CallbackHandler *handler)
{
std::cout << "callback handler called\n";
}
};
typedef void (*DllExport)(callback, void*);
template <typename T, void (*F)( T* )>
void CallDll( T * ptr, DllExport exfn )
{
exfn( & Thunker<T,F>::Do, static_cast<void *>( ptr ) );
}
//HANDLE dllInst = ::LoadLibrary("Exporter.dll");
DllExport exportedFn = ExportedFn;
CallbackHandler handlerInst;
int main()
{
CallDll<CallbackHandler,&CallbackHandler::Thunk>( & handlerInst,
exportedFn );
}
.
User: "Evan Burkitt"

Title: Re: Passing static member function as callback to Windows DLL 24 Aug 2007 05:37:07 PM
"Gianni Mariani" <gi3nospam@mariani.ws> wrote in message
news:46ce84dd$0$27809$5a62ac22@per-qv1-newsreader-01.iinet.net.au...

If you're stuck with having to call ::GetProcAddress and pull names from a
DLL, so be it, however there are much more elegant ways of doing this. One
way of looking at this is that the DLL contains a number of factories.
Upon loading, the factories automagically register themselves and you can
avoid all of the casts. I use this technique with DLL's and it's
portable - no need to worry about mangled names.

Yes, this DLL exposes only a C interface, which will eventually be called by
non-C/C++ languages. I'm constrained to only use simple types for function
parameter lists and return types.

Below is a suggestion - it uses templates to create yet another function
that calls the function you want. There are some limitations.

#include <iostream>

//Executable and DLL know this:
typedef void (*callback)(void*);

//in DLL "Exporter.dll":
void ExportedFn(callback cb, void *opaque) { cb(opaque); }

template <typename T, void (*F)( T* )>
struct Thunker
{
static void Do( void * fromDll )
{
F( static_cast<T*>(fromDll) );
}
};

//in executable:
class CallbackHandler
{
public:
static void Thunk(CallbackHandler *handler)
{
std::cout << "callback handler called\n";
}
};

typedef void (*DllExport)(callback, void*);

template <typename T, void (*F)( T* )>
void CallDll( T * ptr, DllExport exfn )
{
exfn( & Thunker<T,F>::Do, static_cast<void *>( ptr ) );
}

//HANDLE dllInst = ::LoadLibrary("Exporter.dll");


DllExport exportedFn = ExportedFn;

CallbackHandler handlerInst;

int main()
{
CallDll<CallbackHandler,&CallbackHandler::Thunk>( & handlerInst,
exportedFn );
}

In my case I can use your Thunker struct on the caller's side. Although for
completeness my example included a call to the callback as well, my
implementation only needs to supply a function pointer suitable (to the
compiler) for the signature of the callback. The actual call to it is made
from the DLL, which believes the parameter to be a void* anyway.
Using terminology from our examples, what I did boils down to:
ExportedFn(Thunker<CallbackHandler, &CallbackHandler::Thunk>::Do,
&handlerInst);
This is still essentially a typecast, but wrapped up in such a way as to
cause compiler errors if the signature of CallbackHandler::Thunk() changes,
which was my biggest objection to brute-force casting the function pointer
itself.
Working through this has forced me to learn a bit more about template usage.
I appreciate your help.
-Evan
.



  Page 1 of 1

1

 


Related Articles
 

NEWER

pg.1232     pg.940     pg.716     pg.544     pg.412     pg.311     pg.234     pg.175     pg.130     pg.96     pg.70     pg.50     pg.35     pg.24     pg.16     pg.10     pg.6     pg.3     pg.1

OLDER