lunedì 15 febbraio 2016

Dll and dynamic loading: tired to prototype function pointers?

Dll dynamic loading is used in a lot of contexts because give you flexibility and the power to change the code inside without touching the caller application. The base for a plugin architecture is the dynamic loading.
But this powerful has a cost in term of quantity of code to be written in order to map each of the function exported from the DLL. (yes, I know, it has also a cost in term of performance, but this is not the matter).
The code that I'm proposing is a shorter way, template based, to load dynamically a dll changing the approach used in the article called "DLLs the Dynamic Way" and based on MACROs.
My goal was to reduce the amount of the code to write, to code in more human language and to keep the advantage of the intellisense when I want to use the class wrapper fo the Dll.

Here a small unuseful Dll that exports only 2 functions:
extern "C" 
{
__declspec(dllexport) bool IsLoaded(void);
__declspec(dllexport) bool ChangeMe(unsigned char* buf, int buflen, int* lenOfBufChanged);
}
Generally, in order to load dynamically such dll you need to define 2 functions pointers in order to make your life easier when you call GetProcedureAddress/LoadLibrary and so on... Something like this
typedef short int  (__cdecl *UCharIntIntPtr)(unsigned char*, int, int*);
[...]
UCharIntIntPtr _ChangeMe;
[...]
// we assume that the Library has been correctly loaded
_ChangeMe = (UCharIntIntPtr)GetProcAddress(dllHinst,"ChangeMe");
In order to correctly use this functions in a standard class you need also to wrap them:
class MyWrapper
{
public:
       bool ChangeMe(unsigned char* buf, int buflen, int* lenOfBufChanged);
}
This approach requires that you write a lot of code, almost the same code, every time you need to load a dll. A different approach is based on my CMyLateHelper abstract class, defined in the file MyLateHelper.hpp freely downloadable here Based on templates, this class does the dirty job for you, calling LoadLibraries and mapping the functions letting you free to write a more readable code.
Using the class, step 1: derive your own class from the LateHelper.
class CDLLWrapper :
 public CMyLateHelper
{
public:
 CDLLWrapper();
 virtual ~CDLLWrapper(){};
        // to write less, you can simply copy/paste the signatures from export file of the dll
        bool ChangeMe(unsigned char* buf, int buflen, int* lenOfBufChanged);
        bool IsLoaded(void);
protected:
        virtual bool LoadFunctions(); // overload this member in order to map the functions of your dll

};
Setp 2: define your class and map the functions
CDLLWrapper::CDLLWrapper()
 :CMyLateHelper("MyDll.dll")
{
     // do some other if you need it
}

bool CDLLWrapper::LoadFunctions()
{
 try
 {
     if (!MapMemberFunction("IsLoaded"))
        return false;

     if (!MapMemberFunction("ChangeMe"))
        return false;

 }
 catch (...)
 {
  return false;
 }

 return true;
}

bool CDLLWrapper::ChangeMe(unsigned char* buf, int buflen, int* lenOfBufChanged)
{
   bool rv = false;

   try
   {
     rv = ExecuteIt("ChangeMe",buf,bufLen,lenOfBufChanged);
   }
   catch (...)
   {
     rv = false;
   }

 return rv;
}

bool CDLLWrapper::IsLoaded(void)
{
   bool rv = false;

   try
   {
     rv = ExecuteIt("IsLoaded");
   }
   catch (...)
   {
     rv = false;
   }

 return rv;
}
Now you can use the wrapper in your app. Remember to call Initialize method before to use the other member functions.
This class has been tested on Windows but only using cdecl calling convention for exported functions.
Suggestions or improvements are encouraged.
P.S. the script used to format the code has some bug displaying the template definition. Obviously the empty string added at the ned of the call of teh function MapMemberFunction are wrong.

Nessun commento:

Posta un commento

Mi raccomando, non costringermi a censurare il tuo commento, perciò sii educato!