Home » Blog
date 2.Mar.2014

■ A simple callback thunk C++ class for both 32 and 64 bit programming


In an object oriented language like C++ everything is nicely encapsulated within a class object. But windows programming requires here and there the use of callback functions, e.g a window procedure that handles the behaviour of a window on your screen. Such callbacks are global, meaning they are not part of a C++ class, and a big issue is providing context information for a global callback function, or effectively using a local member function as a callback. Many articles have been written about this topic, e.g. see here for an introduction.

Over the years people have devised all sorts of ingenious tricks to submit callbacks into class membership. They are collectively known as thunking. Take ATL's CStdCallThunk for example, this uses assembly language to replace the first HWND (window) argument of WindowProc with the wrapping object's "this" pointer:

LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);

As the object includes the window handle (m_hWnd), we don't lose any information after swapping out the original HWND callback argument. The callback stub can then call the WindowProc local member function using "this" pointer.

(Another way to provide context information to a window procedure is to use GWLP_USERDATA but let's stay with thunking)

ATL thunking does its job and does it well. However the idea relies on hot swapping the HWND argument, and it is not always possible to sacrifise a callback argument. Take for example the callback used with SetWindowsHookEx for WH_CBT hooks, all the arguments are essential and cannot be inferred any other way.

For hook callback thunking a very smart and simple solution is the CAuxThunk class of the old ATL/AUX library. Using inline assembly, it pokes "this" pointer in the ECX register of the CPU leaving all the arguments passed on the stack intact. That's perfect sneaking in the class context — but there's a snag: C++ uses the ECX only for 32 bit windows. This trick cannot work for 64 bit programming.

I was looking for a 64 bit compatible hook thunk for a long time. I don't know enough assembly programming to write the code myself. So I was excited the other day when I discovered a post over at stack overflow. It is not as clean as efficient as CAuxThunk but it works. Using my faint assembly knowledge I adapted the original code for C++ thiscall thunking and wrapped it in a class that is compatible with the original CAuxThunk.

CBT hook thunking class source code


This small class uses the efficient original CAuxThunk for 32 bit programming and my botched not-quite-perfect-but-good-enough 64 bit equivalent CBT callback thunk. It takes quite a bit of head-scratching to understand what's going on, but all you have to do is include a small self-contained header file to your code:

Download hookThunk.h (5KB, zipped)

As with all the code I publish, you will find no nasty cross dependencies with anything big and and restrictive (no boost). Just derive your hook from my CCBTHook and add your hook code


//#define _WIN64 (this in project settings)
#include "hookThunk.h"

class CTestHook : public CCBTHook
{
public:
   CTestHook() {
      Hook();
      m_ncnt = 0;
   }

   ~CTestHook() {
      Hook(0);
   }

   virtual LRESULT HookProc(int nCode, WPARAM wParam, LPARAM lParam) 
   {
      if(HCBT_SETFOCUS == nCode) {
         m_ncnt++;
         // you would want to do something more useful in your code :)
      }

      return CCBTHook::HookProc(nCode, wParam, lParam); // calls next hook
   }

   int m_ncnt; // sample context
};

Using the base CAuxThunk class you can implement member callbacks for other situations, not just CBT hooks. Just mind the nArgs parameter in the x64 version of InitThunk, it should match the number of arguments of the target callback function.

Post a comment on this topic »

Share |

©2002-2014 ZABKAT, all rights reserved | Privacy policy | Sitemap