473,416 Members | 1,553 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,416 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 4079
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
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

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.