Click here to Skip to main content
15,867,961 members
Articles / Programming Languages / C++
Article

Reusable Function Loader for a DLL

Rate me:
Please Sign up or sign in to vote.
4.10/5 (6 votes)
3 Oct 20037 min read 130.6K   779   34   24
Provides clean syntax for explicitly loading a DLL and its export functions. Exported DLL functions appear as local extern "C" functions or class member functions at source level.

Sample screenshot

Different ways to invoke the same DLL export function.

Introduction

This article describes the design and implementation of a DLL loader class. An instance of the described class enables explicit loading and unloading of a DLL file to and from memory as well as providing means to access any EXPORTED functions of a DLL file.

The key focus of this design is to enable a neat and clean syntax at sopurce level for executing routines that reside in a DLL. The primary design objective of this class is to provide an easy way to relocate different functions of an existing application into various dynamic-link-libraries (DLLs) so that different implementations of the same functions can be loaded at runtime.

In particular, the described class was originally designed to address the following scenario. Assume, an application is developed and compiled into a self contained exe file. The product is launched with great success and now a software architect (oh my God!) wants to factor out various common (maybe extensible) functions of this application into a DLL module. The idea is to load these functions at runtime from a DLL and invoke functions (now residing in the DLL) as if they are defined locally within the app. A DLL file utilizes the so-called PE format. Since there is an existing codebase to maintain, obviously, the programmer prefers as little changes to existing source code as possible.

///
/// macro redefinition for your brain
/// (code fix by Mr. Anonymous)
#ifdef marco
#undef marco
#define marco macro
#endif

According to this scenario, the tasks to be accomplished are:

  1. relocate functions into a DLL and export them,
  2. load DLL during application runtime,
  3. provide access to EXPORTED DLL functions and
  4. unload DLL whenever appropriate.

As shown in the image above (top of page), the code required for loading an exported function is quite simple. More importantly, the sample code there demonstrated our DLL loader design can adapt itself to provide a C or C++ interface for accessing an EXPORTED DLL function. Notice also that in contrast to other known implementations, the use of macros is stripped to a bear minimum. The current implementation provides substantially cleaner syntax than other existing techniques known to the author at the time of writing.

I would like to point out that the original concept was to enable a program to load and discover all exported functions from a given DLL during runtime. This is still not achievable.

As a side note, I always believe that the reusability of your code can be measured by the number of lines required to bring out a particular pattern. The lesser lines required, the more reusable your code is. Also, reusability means natural C++ syntax. Obscure macro based implementation reduces reusability since fellow programmers must look at the docs to find out what that marco does (i.e.: MFC macro METHOD_PROLOGUE(xx,yy)).

Design issues

The first idea that came to mind was to overload operator FunctionPointerType () of a class, where FunctionPointerType is a function pointer to a function, so that during compile time, the compiler would rank all candidate functions and automatically type-cast a variable to a function pointer type. For example, consider the following code segment:

1    class DllFunction {
2        typedef int(*FuncPtrType)(int);
3        FuncPtrType fp;
4    public:
5        // ctor()
6        DllFunction(FuncPtrType t) { fp =t;}
7
8        // typecast
9        operator FuncPtrType () {
10        return fp;
11        }
12    };
13
14    int test(int i) { cout << i << endl; }
15
16    int main() {
17    DllFunction a(test);
18
19    int bb = test(10);
20    int cc = a(10); // automatically typecast 
                     // a from class DllFunction to FuncPtrType
21    return 0;
22    }
23

This is in fact my first attempt to implement a DLL loader. Unfortunately, this code segment does not compile with the MSVC line of compilers (tested: MSVC6, VS.NET). After poking further than I should, I realized that operator FuncPtrType () was never considered to be a candidate function during the compilation stage. This is specific to MSVC line of compilers. The exact error is at line 20, "error C2064: term does not evaluate to a function". The above code segment compiles perfectly with g++. Utilizing operator overloading has one distinct advantage: it enables one to delay the explicit loading of a DLL into memory until the actual EXPORTED function is invoked. I.e.: implement some logic inside operator FuncPtrType() to load a given DLL.

First Workaround

The author has dreamt up many different ideas to get round this limitation of the MSVC compiler. For the moment, two specific implementations are worth considering.

The compilation error occurs because the variable a (on line 20 above) cannot be automatically typecast to a function pointer (due to limitation of the compiler). Some of the questions that I asked myself are: Is there a way to get around that limit? Keeping in mind that we have a large codebase to maintain, what would be the easiest way (least effort on my part) to enable a(10) to actually invoke a function call?

The answer to these questions represents my first and second solutions which will be addressed promptly. For the first solution, the key is to use a function pointer variable that is defined either locally or globally within the application. In other words, we declare a as int(* a)(int) = test;. This eliminates the need to do an explicit typecast because the actual variable is a function pointer already. The disadvantage though is that the delay load behavior can no longer be supported neatly in comparison with using a class to encapsulate a DLL function. The following code segment shows how this can be done. I wonder why the code looks awfully like standard C code?

1
2    int _stdcall test(int i) { cout << i << endl; }
3
4    int main() {
5    int (_stdcall *a) (int);    // a is now a function pointer
6    a=test;
7
8    int bb = test(10);
9    int cc = a(10);
10    return 0;
11    }
12

Second Workaround

I am a bit reluctant to reveal my second implementation because it is not source level compatible with my existing codebase. Thus, it requires a lot of search and replace to rectify my existing code to work with this design. It was in the spirit of industry non-compliance and bad programming practice (thanks to one infamous software company) that I decided to present my second implementation. Just to re-iterate, what I am about to show you is a bad design since it is:

  1. not readily compatible with my existing codebase,
  2. it does not reflect the true power of operator()() and
  3. if the programmer decided not to use my design, the developed code must be re-edited manually before it will compile again.

Having said that, everything you will see is C++.

In order to appreciate the underlying reason for developing this implementation, let's go back to a question that was raised previously. If variable a cannot be automatically typecast to a function pointer (due to limitation of a compiler), is it possible that we typecast it manually so that a(10) will indeed invoke a function. I.e.:

typedef (int)(_stdcall* FuncPtrType)(int);
(FuncPtrType a)(10);

Of course, this is possible. There are various ways that this can be done. I have chosen to utilize operator()() to return a Function Pointer Type object so that we can write code such as a()(10). The following code segment illustrates how this is done:

1    class DllFunction {
2        typedef int(*FuncPtrType)(int);
3        FuncPtrType fp;
4    public:
5        // ctor()
6        DllFunction(FuncPtrType t) { fp =t;}
7
8        // typecast... now useless bcos compiler
         // doesnt select it as candidate function
9        operator FuncPtrType () {
10        return fp;
11        }
12
13        // function operator
14        FuncPtrType operator ()() {
15        return fp;
16        }
17
18    };
19
20    int test(int i) { cout << i << endl; }
21
22    int main() {
23    DllFunction a(test);
24
25    int bb = test(10);
26    int cc = a()(10);        // a() returns a function pointer object. 
27                // a()(10) invoke the actual function
28    return 0;
29    }
30

Since this design uses objects to encapsulate a DLL EXPORTED function, it is now possible to implement a delayed load behavior. This can be done quite simply by introducing some logics into operator()(). Refer to Zip file for an example of this.

Other possibilities

  • overload operator ()(...)

    Will by default trigger a function call if we use syntax like a(10);.

    Requirements:

    • how to pass params to DLL function (already on stack!).
    • ASM jmp to DLL function address inside operator() (...) but stack organization is different with class member and extern "C" functions.
    • need to clear up on stack organization in Win32 implementation.

Downloadable Zip file

The attached downloadable zip file contains two sample implementations. The samples should be regarded as proof of concept implementations only. They are not production-ready code!!!

Please refer to the MSVC help files for more information regarding DLL mapping and loading of a function from a DLL.

Concluding remarks

The described implementations of a DLL loader enable clean and simple syntax at source level for accessing DLL functions. Four lines are required to load and execute an export function from a DLL file.

The author would like to re-iterate that the original concept was to enable a program to load and discover all exported functions from a given DLL during runtime. This is still not achievable currently, though there has been significant progress in that direction.

Finally, may I please point out that there are existing macro based implementations that work very well for general DLL usage. For example, the implementation by Yao Zhifeng A class to wrap DLL functions dated:26 Jun 2002.

History

  • version 1.0: 15Oct2002 2.50a - dklt
  • version 1.1: 12Nov2002 6.02a - dklt
  • version 1.2: 28Mar2003 5.22p - dklt
  • (submitted with 10 embedded typos) version 1.3: 04Oct2003 11.41p - dklt
    • License

      This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

      A list of licenses authors might use can be found here


      Written By
      Researcher
      Australia Australia
      Computer Vision Research
      Postgrad @Monash, Australia

      Comments and Discussions

       
      Generalmarco... Pin
      Anonymous7-Oct-03 16:54
      Anonymous7-Oct-03 16:54 
      GeneralRe: marco... Pin
      Anonymous7-Oct-03 16:54
      Anonymous7-Oct-03 16:54 
      GeneralRe: marco... Pin
      Anonymous7-Oct-03 16:55
      Anonymous7-Oct-03 16:55 
      GeneralRe: marco... Pin
      beetung8-Oct-03 15:49
      beetung8-Oct-03 15:49 
      GeneralLatest implementation Pin
      beetung28-Mar-03 4:31
      beetung28-Mar-03 4:31 
      GeneralRe: Latest implementation Pin
      beetung3-Apr-03 17:14
      beetung3-Apr-03 17:14 
      GeneralAny Info about Proxy DLL (Trojan DLL) Pin
      Fad B5-Mar-03 10:29
      Fad B5-Mar-03 10:29 
      GeneralOne issue Pin
      Todd Smith14-Oct-02 15:30
      Todd Smith14-Oct-02 15:30 
      GeneralRe: One issue Pin
      beetung14-Oct-02 22:58
      beetung14-Oct-02 22:58 
      GeneralRe: One issue Pin
      AlphaDog15-Oct-02 11:01
      AlphaDog15-Oct-02 11:01 
      GeneralRe: One issue Pin
      beetung16-Oct-02 3:40
      beetung16-Oct-02 3:40 
      GeneralRe: One issue Pin
      Uwe Keim9-Nov-02 4:33
      sitebuilderUwe Keim9-Nov-02 4:33 
      GeneralRe: One issue Pin
      AlphaDog10-Nov-02 6:04
      AlphaDog10-Nov-02 6:04 
      GeneralRe: One issue Pin
      beetung10-Nov-02 22:03
      beetung10-Nov-02 22:03 
      GeneralRe: One issue Pin
      beetung11-Nov-02 2:56
      beetung11-Nov-02 2:56 
      GeneralRe: One issue Pin
      beetung11-Nov-02 8:11
      beetung11-Nov-02 8:11 
      GeneralRe: One issue Pin
      beetung28-Mar-03 4:35
      beetung28-Mar-03 4:35 
      GeneralRe: One issue Pin
      Anonymous17-Oct-02 1:21
      Anonymous17-Oct-02 1:21 
      GeneralNasty assertion Pin
      Anonymous14-Oct-02 14:51
      Anonymous14-Oct-02 14:51 
      GeneralRe: Nasty assertion Pin
      Todd Smith14-Oct-02 15:39
      Todd Smith14-Oct-02 15:39 
      GeneralRe: Nasty assertion Pin
      beetung14-Oct-02 21:56
      beetung14-Oct-02 21:56 
      GeneralRe: Nasty assertion - my bad! Pin
      Anonymous15-Oct-02 7:59
      Anonymous15-Oct-02 7:59 
      Generalsource code readibility Pin
      Stephane Rodriguez.14-Oct-02 9:02
      Stephane Rodriguez.14-Oct-02 9:02 
      GeneralRe: source code readibility Pin
      beetung14-Oct-02 21:25
      beetung14-Oct-02 21:25 

      General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

      Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.