The Case of the Unexpected Overload
So are you using C++ in a KMDF driver and scratching your head over some unexpectedly unresolved references?
I recently engaged in a somewhat prolonged conversation with Doron and a dev I won’t name [since I didn’t ask for permission] about C++ in KMDF driver. This dev (a very good dev, by the way) had hit a stumbling point converting a driver from C to C++ and was wondering if we had ever tried this in QA.
Yes, I know that means he/she doesn’t read this blog, since I’ve mentioned doing just that on more than one occasion. But that’s OK, not many people do- and since I’ve got a tendency to write about whatever strikes my fancy at any moment, and I’m not the most dazzlingly brilliant human on the face of the planet, that’s not much of a surprise to me. You are of course free to believe what you like about that statement.
But in an attempt to be useful today- the problem was one of those that I guess I’ve hit so many times I don’t give it much thought. But it gave our dev a bit of trouble, and not much gives this person that kind of trouble. So perhaps a word to the wise will give the three or four of you still reading this a leg up the next time you use C++ in a KMDF driver.
The Lesson for Today Is…
It was just a simple conversion of a driver from C to C++ (to gauge my familiarity with the process, I did that with a half dozen or more drivers last week, and it was far from the only thing I did that week- in fact the big job was actually that I also made them W4 and PFD clean along with quite a few more drivers I’d already converted to C++- almost two dozen drivers total), but it ran afoul of this little bit of goodness:
typedef
__drv_functionClass(EVT_WDF_OBJECT_CONTEXT_CLEANUP)
__drv_sameIRQL
__drv_maxIRQL(DISPATCH_LEVEL)
VOID
EVT_WDF_OBJECT_CONTEXT_CLEANUP(
__in
WDFOBJECT Object
);
typedef EVT_WDF_OBJECT_CONTEXT_CLEANUP *PFN_WDF_OBJECT_CONTEXT_CLEANUP;
That’s the “role typedef” for any WDF object’s context cleanup routine. The SDV tool can use role typedefs to do a better job of statically verifying your driver code, so some special cleanup typedefs are defined to make it a better tool, and you can see them in this (by the way, everything I’m posting is from the WDK- no deep dark secrets here).
typedef EVT_WDF_OBJECT_CONTEXT_CLEANUP EVT_WDF_DEVICE_CONTEXT_CLEANUP;
typedef EVT_WDF_OBJECT_CONTEXT_DESTROY EVT_WDF_DEVICE_CONTEXT_DESTROY;
typedef EVT_WDF_OBJECT_CONTEXT_CLEANUP EVT_WDF_IO_QUEUE_CONTEXT_CLEANUP_CALLBACK;
typedef EVT_WDF_OBJECT_CONTEXT_DESTROY EVT_WDF_IO_QUEUE_CONTEXT_DESTROY_CALLBACK;
typedef EVT_WDF_OBJECT_CONTEXT_CLEANUP EVT_WDF_FILE_CONTEXT_CLEANUP_CALLBACK;
typedef EVT_WDF_OBJECT_CONTEXT_DESTROY EVT_WDF_FILE_CONTEXT_DESTROY_CALLBACK;
So, what the original driver, being a good sample [because SDV suitability also matters], had was something like this (not exactly like this- nobody with my twisted sense of humor writes sample code, because humor never works globally- someone always takes offense- I’m just not inclined to care about that anymore, because I’m a callous old bastard with less than a dozen years to retirement age and not much “career” left to lose):
// In the header file:
EVT_WDF_DEVICE_CONTEXT_CLEANUP HideTheBodies;
// In the implementation
VOID
HideTheBodies(
__in WDFDEVICE MortTheMortuaryTechnicianIsOnTheJob
)
{
...
Of course, in C, that sort of works- but in C++, that says you have two overloaded versions of HideTheBodies- one takes a WDFOBJECT as input, and isn’t defined, because nobody wrote it that way. The one that is defined takes a WDFDEVICE as input. More annoying, of course, is that from the typedef name, you’d expect it would take a WDFDEVICE as input, so it isn’t an error that’s glaringly obvious.
As is- the code will compile fine, and PFD will be happy, too [they’re not the same function, remember]. But when you link it, you get an unresolved reference for HideTheBodies- decorated to indicate it has a WDFOBJECT parameter. Now, unless you really look closely at such decorations (something my fine history of mistyping the simplest of code has taught me to do), you’re looking at a mystery. It took my colleague digging through preprocessor output and object files and such to finally hit upon the error- a reasonably annoying circumstance!
Another thing you can do (and this is what I usually do) is tie the callbacks to the context itself (with the context a class) as static member functions, as I described earlier (effectively you do the cleanup by calling the context’s “delete” operator). PFD currently doesn’t match the roletype if you do this, so I suppress those warnings (and there is no SDV for C++)- I think we’re trying to get that fixed… But the compiler will catch the parameter type mismatch- a bit earlier than the linker.
But I’ve hosed this both ways- so, until something on the subject gets posted by a recognized authority, this post can at least be found by the diligent searcher…