Importing functions from a DLL loaded at run-time via ::LoadLibrary() is a cumbersome and error-prone process. You must declare a few pointers to functions, refer to these functions by name, typecast the pointer returned by ::GetProcAddress()… bah, that may be fine for a couple functions, but when the interface starts to grow, it’s a real pain to do. What’s more, the DLL may be interested in using some services provided by the loading application, but ::GetProcAddress() can’t handle that. Here I present a nice and extremely simple way to do it in C++. Applications that support runtime-loaded plugins are a clear example of this situation; in fact, this technique is inspired by (read: “ripped straight out of”) the plugin system used it Autodesk’s 3DStudio Max modelling package (wait, it’s Kinetix… er, no, it’s Discreet. Hm, never mind, by the time you read this it will have changed again).
The idea is to put all the elements that form the interface to the DLL into an abstract class. This class will be derived by actual DLL instances, which provide the implementation of the interface. The app also provides an similar interface to the DLL. A minimal but fully working example of this can be found in this MSVC++ 6 project. A brief discussion on the technique was held in this Flipcode COTD Article by Justin Blackwell. I have tried to keep the code as simple as possible so there’s no real error checking or other niceties.
// --------------
// DLLInterface.h
// --------------
#ifndef __DLLINTERFACE_H__
#define __DLLINTERFACE_H__
// Can't believe vc doesn't automate this kind of stuff
#ifdef __DO_DLL_EXPORT__
#define __DLL_FUNCTION__ __declspec(dllexport)
#else
#define __DLL_FUNCTION__ __declspec(dllimport)
#endif
// Interface that the app provides to the DLL
class IAppInterface
{
public:
virtual int Operation(int parm) = 0;
// ...
};
// Interface that the DLL provides to the app;
class IDLLInterface
{
public:
virtual void MyDLLFunction(int parm) = 0;
// ...
};
extern "C" __DLL_FUNCTION__ IDLLInterface *CreateDLLInterface(IAppInterface *pApp);
typedef IDLLInterface *(*FuncCreateDLLInterface) (IAppInterface *pApp);
#endif //__DLLINTERFACE_H__
// --------------
// DLL.cpp
// This is compiled as a DLL
// --------------
#include <windows.h>
#include <stdio.h>
#define __DO_DLL_EXPORT__
#include "DLLInterface.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
return TRUE;
}
// Actual funcionality provided by the DLL.
class CDLLImplementation: public IDLLInterface
{
IAppInterface *m_pApp;
public:
CDLLImplementation(IAppInterface *pApp): m_pApp(pApp) { }
void MyDLLFunction(int parm) { printf("Yes, %d\n", m_pApp->Operation(parm)); }
// ...
};
IDLLInterface *CreateDLLInterface(IAppInterface *pApp)
{
static CDLLImplementation dllImpl(pApp);
return &dllImpl;
}
// --------------
// App.cpp
// This is compiled as an EXE
// --------------
#include <windows.h>
#include "DLLInterface.h"
// Actual funcionality provided by the app.
class CAppImplementation: public IAppInterface
{
public:
int Operation(int parm) { return parm*2; }
};
void main()
{
HMODULE hDLL = ::LoadLibrary("DLL");
FuncCreateDLLInterface createFunc = (FuncCreateDLLInterface)::GetProcAddress(hDLL, "CreateDLLInterface");
CAppImplementation App;
IDLLInterface *pDLL = createFunc(&App);
pDLL->MyDLLFunction(10); //Calls the DLL, which in turn calls the app back.
}