0

As previously discussed here, I'm trying to find a workaround for the LNK2019 issue that arises when building a static library which utilizes C++ templates, and separating the source from the header to keep the code private from other projects. I believe I've nearly come to a working conclusion (for my particular situation), but I'm not entirely sure if this is the correct/best way to go about it and was wondering if anyone has any suggestions, improvements/comments to add?

The goal is to do some type checking to see if the template's signature matches the target prototype function's signature, do some private processing, and return whether or not it was sucessful. NOTE that I have removed SdkHookMgr.h and SdkHookMgr.cpp from the prior version of the solution in the above link, and merged everything back into SdkLib.h and SdkLib.cpp, into a static class for a bit of clarity.

SdkLib.h:

#include <typeinfo>
#ifdef MY_EXPORTS
#   define MYDECL __declspec(dllexport)
#else
#   define MYDECL
#endif

// Prototypes
typedef HMODULE (WINAPI *HookLoadLibraryA)( LPCSTR lpFileName );
//...

class CHook;
class CHookManager;


MYDECL BOOL WINAPI ValidateHook( CHook *hook );

class CHook
{
public:
    CHook() : m_type(NULL), m_target(NULL), m_result(FALSE) {};
    CHook( const char *type, PVOID target ) : m_type(type), m_target(target) {
        m_result = ValidateHook(this);
    };
    const char *m_type;
    PVOID m_target;
    BOOL m_result;
};

class CHookManager
{
public:
    template <typename HookFunction> static BOOL Hook(HookFunction target)
    {
        const type_info& type = typeid(HookFunction);
        CHook *hook = new CHook( type.name(), target );
        return hook->m_result;
    }
};

SdkLib.cpp:

#include <SdkLib.h>
IDXDECL BOOL WINAPI ValidateHook( CHook *hook )
{
    // Do type checking, private processing, etc here...
    return TRUE;
}

DemoDLL.cpp:

#include <SdkLib.h>
HMODULE WINAPI Hooked_LoadLibraryA( LPCSTR lpFileName )
{
    DebugBreak();
}

// The function that starts the rollercoaster.
// - Syntax: Hook< prototype >( target )
if!(CHookManager::Hook<HookLoadLibraryA>(Hooked_LoadLibraryA))
    cout << "Failed to create hook for LoadLibraryA!" << endl;
Community
  • 1
  • 1
RectangleEquals
  • 1,825
  • 2
  • 25
  • 44

1 Answers1

0

You may find that the results of typeid are not consistent between the DLL and the main program. (See, for example, typeid result across different dll's.)

Since your list of possible hooks is limited, it strikes me that overloaded functions would be a better choice than templates. You'd then have no DLL issues, and the validity of each hook would be checked at compile time. Here's an example of the sort of thing I'm thinking of; obviously in practice you'd split this into separate definition and declaration, with the definitions living in the DLL so it's all cleanly separated out.

class CHookManager {
public:
    BOOL Hook(HookLoadLibraryA hook) {
        assert(sizeof hook<=sizeof(uintptr_t));
        return ValidateHook((uintptr_t)hook,"LoadLibraryA");
    }

    BOOL Hook(HookLoadLibraryW hook) {
        assert(sizeof hook<=sizeof(uintptr_t));
        return ValidateHook((uintptr_t)hook,"LoadLibraryW");
    }
};

(Note that this shows up one disadvantage of this approach - you can only have one hook per function signature. I mention this for completeness' sake, but I'll assume this hasn't proven an issue.)

(You might like to replace the assert with a compile-time assert, if you have one.)

ValidateHook would use strcmp to figure out which hook is being hooked. Once it's figured out which hook it is, it would then cast the uintptr_t to the appropriate function pointer type. It knows the pointer was originally of the correct type for that hook, because you're using the C++ overload mechanism to do it all. (Or you could have an enum, say, for all the hook types, rather than passing in a string - it's up to you. The key part is that you have full control over the values being passed, so that the DLL and the calling code are definitely using matching values.)

This code would be a little tiresome to generate, but if you already have the list of typedef names then you could create the corresponding code using regular expression search and replace, or keyboard macros, in your editor of choice. Or you could use something like the so-called "X-Macro" to automate the generation of the whole thing.

Community
  • 1
  • 1
Tom Seddon
  • 2,648
  • 1
  • 19
  • 28
  • I actually just read that a bit ago and wasn't happy to hear it. I originally started out like this until I realized the following: 1.) The full source contains over 100 unique prototypes (and growing), of which I would need to write very similar code in each of their definitions (the only major difference being the function signatures). 2.) Overloaded functions would limit 3rd party developers from further extending this list, forcing them to use only predefined prototypes in my sdk. Nevertheless, if I can't find a better solution then I'll mark yours as the answer. – RectangleEquals Nov 09 '12 at 20:15