Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Winapi Callback C++ member function.

Credits go to Tobias Dammers for helping out!
This thread is locked; no one can reply to it. rss feed Print
Winapi Callback C++ member function.
Goalie Ca
Member #2,579
July 2002
avatar

Here's the problem: Create a self-contained class for any one of many types of winapi functions. Catch: the callback is a member function.

The issue is the stack produced by the type of call. The solution is probably achieved through binding/similar mechanism.

Traditional std::mem_fun doesn't work. boost::mem_fn can actually handle the callback and its quirks. The problem is actually passing it if i'm using boost.
Windows expects a DWORD_PTR (foolishly tried casting which failed to compile) but i've also seen people cast as DWORD (quite nasty).

Currently I have a singleton class.. the callback is static. This is fine for this class because it IS a singleton by nature. But soon i will need to make a non-singleton class with the same problem.

I've google this so far and haven't found much. I've found a thunking technique which is basically an assembly hack.. mfc uses that.

Maybe i should pick up myself that book from the apple commercial "C++ gui programming". I can see why you'd need it :P

-------------
Bah weep granah weep nini bong!

orz
Member #565
August 2000

Quote:

The problem is actually passing it if i'm using boost.
Windows expects a DWORD_PTR (foolishly tried casting which failed to compile) but i've also seen people cast as DWORD (quite nasty).

I haven't use boost, or (except every once in a while) the win32 API. Probably I'm misunderstanding something here. But here's my reaction to your question:
Why does it fail to comply if you cast a function pointer to a DWORD_PTR? Normal casts from function pointers to integers via either C-style cast or reinterpret_cast<> work fine for me (though generally aren't recommended, but perhaps win32 API leaves you little choice). Is boost::mem_fn not a template that evaluates to a function pointer?
Why is casting to a DWORD more foolish than casting to a DWORD_PTR? According to my compiler, DWORD_PTR is just a typedef to DWORD (which is in turn a typedef to unsigned long int or somesuch).

Dustin Dettmer
Member #3,935
October 2003
avatar

map<DWORD, void*> pointerLookup;

Pretty simple, when you think about it.

Goalie Ca
Member #2,579
July 2002
avatar

Thanks for your help guys but I'm pretty convinced now that there's no clean way to solve it.

Reading the implementations of bind, bind1st, mem_fn, mem_fun etc.. i realize what they do. They create structs that wrap the c++ call. This normally works fine for c++ libraries but isn't able to get around the limitation of c-libraries.

For instance.. say i could even create a static function, and all i wanted to do was bind some argument such as "this" to it (so i could do this->something(args)). This returns a struct with overloaded () operators. Naturally this struct also has the same problem as my member callback.

Apparently this is called the c++ forwarding problem.

Orz:
I didn't know DWORD_PTR = DWORD. Isn't dword = double word. DWORD_PTR would then not have different sizes on 32 and 64-bit platforms?

Dustin:
I don't understand the purpose for your map. If it was to solve the issue of reinterpret cast preventing me from turning the binded function into a DWORD then it can't actually work. I think i was confused in my understanding and now understand why you can't actually convert.

But it just got me thinking.. Windows normally passes HWND or some handle usually (after all c has to deal with it). I could use a map based on the handle! With a semaphore lock it might just work.. I'll post results when i get around to it tomorrow. Cookie if it works.

-------------
Bah weep granah weep nini bong!

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

I didn't know DWORD_PTR = DWORD. Isn't dword = double word.

Yup. And WORD on MSVC is 16-bit, so DWORD is double that.. 32-bit. :) Not sure how DWORD_PTR is handled on 64-bit targets, though.

However, for C callbacks with non-static C++ methods, the funciton needs to take a user-pointer to pass along to the callback you give it, then give it a static callback and pass 'this' as the user-pointer. Then in the static callback, cast the user-pointer to the class type, and call the non-static member.

class SomeClass {
   real_callback()
   {
      ...
   }

   some_static_callback(void *ptr)
   {
      static_cast<SomeClass*>(ptr)->real_callback();
   }

   foo()
   {
      SetSomeWin32Callback(some_static_callback, this);
   }

Though if you can't pass 'this' for a user pointer, then you're out of luck. :-/

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

orz
Member #565
August 2000

Quote:

Thanks for your help guys but I'm pretty convinced now that there's no clean way to solve it.

C callbacks != perfectly clean
win32 != clean
win32 C callbacks... not surprised : )

Quote:

I didn't know DWORD_PTR = DWORD. Isn't dword = double word. DWORD_PTR would then not have different sizes on 32 and 64-bit platforms?

Hm... you're probably right. All I can see here is what DWORD and DWORD_PTR is on my compiler. Perhaps on a 64 bit compiler a DWORD would still be 32 bit while a DWORD_PTR would grow to 64 bits. So, forget about my second question.

My first question still stands though. I still don't really understand what the issue is. C callbacks to template functions work fine for me, although not to functors (add an extra wrapper if mem_fn is a functor?).

Goalie Ca
Member #2,579
July 2002
avatar

Well the issue here is that say you have this (although true of any C++ class wrapping any winapi object).

1 
2class Window
3{
4 
5public:
6 
7//bunch of public
8 Window(){
9 ...
10 callback = &Window::windowProcedure //fails!
11 }
12//windows calls this! not me!
13 LRESULT CALLBACK windowProcedure(HWND, UINT, LPARAM, WPARAM){ ... }
14
15 void execute()
16 {
17 //message dispatch loop
18 }
19 
20}
21 
22int winmain(..)
23{
24 ...
25 Window();
26 Window.execute();
27}

The callback is a member function of a class. A standard c-callback in WINAPI uses __stdcall (that's what CALLBACK is defined to be). Problems.. well in C++ there is a hidden "this" parameter added to every member function call. And in C++ if you use polymorphism or anything fancier it's even more broken.

So my first misguided attempt was to try and wrap that call with mem_fn, mem_fun, etc.. But those create a struct with an overloaded () operator. This does not fix anything.

What i will try is to make a static function. Inside it I will have a map<HANDLE, CWindow*>. That map will be static. I will then call the appropriate function then.

-------------
Bah weep granah weep nini bong!

Tobias Dammers
Member #2,604
August 2002
avatar

If the HANDLE is a HWND, then you can store a pointer to the CWindow in the window's extra memory, and access it through GetWindowLong() / SetWindowLong(). This way, you don't have the map overhead.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

nonnus29
Member #2,606
August 2002
avatar

Do you know there are a couple of extensive documents on the intarwebs that go thru this procedure (wrapping win32 api)? If you're just inventing a wheel that's great, I think it's a horrible waste of time though.....

gillius
Member #119
April 2000

C# is the ultimate wrapper around the Win32 API ;). Sorry, just playing and trolling around.

Gillius
Gillius's Programming -- http://gillius.org/

Goalie Ca
Member #2,579
July 2002
avatar

Tobias FTW!!!

  static LRESULT CALLBACK staticCallback (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    CWindow* instance = reinterpret_cast<CWindow*>(GetWindowLongPtr(hwnd, DWLP_USER));

    return instance->WindowProcedure(hwnd, message, wParam, lParam);
  }

Cookies to tobias

Gillius: I've used C# winforms lots. I find it quite retarded how there's no built-in printing for text boxes, hardware accelerated blitting (gdi+ is all software based!) and how rich text box is absolutely useless because of the font attribute rules.
troll--

edit: just realized that while most buttons etc. are handle based there are also timers, waveIn, waveOut, ..etc I will have to look into that. I guess i just got exited there was a simple solution. Most of the crud on the net involved "thunk" and other crude hacks (not sure how portable that is.. its essentially an assembly hack). This technique though seems to have promise. Otherwise i guess i could still use a map for other structs.

-------------
Bah weep granah weep nini bong!

Go to: