472,794 Members | 2,165 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,794 software developers and data experts.

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
Aug 24 '07 #1
2 4016
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 );
}
Aug 24 '07 #2

"Gianni Mariani" <gi*******@mariani.wswrote in message
news:46***********************@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
Aug 24 '07 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: Steven T. Hatton | last post by:
I am trying to convert some basic OpenGL code to an OO form. This is the C version of the program: http://www.opengl.org/resources/code/basics/redbook/double.c You can see what my current...
6
by: keepyourstupidspam | last post by:
Hi, I want to pass a function pointer that is a class member. This is the fn I want to pass the function pointer into: int Scheduler::Add(const unsigned long timeout, void* pFunction, void*...
0
by: Jeffrey B. Holtz | last post by:
Has anyone used the multimedia timere timeSetEvent in C#? I'm trying to use it to get a 1ms accurate timer. All the other implementations are far to inaccurate in their resolution. 1ms Timer =...
4
by: H.B. | last post by:
Hi, I successfully implement a static callback function for my dll usign delegates. Now, I need to use member function instead of static function. How can I make that (in Managed C++). Hugo
12
by: Haxan | last post by:
Hi, I have my main application class(unmanaged) that has a none static member function that I need to pass as a delegate to managed C# method. In one of the methods of this class(unmamanged),...
4
by: Jimmy | last post by:
I need to use Asynchronous Socket functions in a server application and am learning from sources such as the MSDN2 (http://msdn2.microsoft.com/en-us/library/bbx2eya8.aspx). What I observed is that...
5
by: Jon E. Scott | last post by:
I'm a little confused with "static" methods and how to access other unstatic methods. I'm a little new to C#. I'm testing a callback routine within a DLL and the callback function returns a...
6
by: smmk25 | last post by:
Before I state the problem, I just want to let the readers know, I am knew to C++\CLI and interop so please forgive any newbie questions. I have a huge C library which I want to be able to use in...
8
by: Neviton | last post by:
Is it possible ? There is any work around? Thanx
3
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 2 August 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
linyimin
by: linyimin | last post by:
Spring Startup Analyzer generates an interactive Spring application startup report that lets you understand what contributes to the application startup time and helps to optimize it. Support for...
0
by: erikbower65 | last post by:
Here's a concise step-by-step guide for manually installing IntelliJ IDEA: 1. Download: Visit the official JetBrains website and download the IntelliJ IDEA Community or Ultimate edition based on...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Sept 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
14
DJRhino1175
by: DJRhino1175 | last post by:
When I run this code I get an error, its Run-time error# 424 Object required...This is my first attempt at doing something like this. I test the entire code and it worked until I added this - If...
5
by: DJRhino | last post by:
Private Sub CboDrawingID_BeforeUpdate(Cancel As Integer) If = 310029923 Or 310030138 Or 310030152 Or 310030346 Or 310030348 Or _ 310030356 Or 310030359 Or 310030362 Or...
0
by: lllomh | last post by:
How does React native implement an English player?
0
by: Mushico | last post by:
How to calculate date of retirement from date of birth
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.