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

Dynamic DLL Loading

Rate me:
Please Sign up or sign in to vote.
4.29/5 (9 votes)
29 Dec 1999 306.1K   3.2K   80   29
How to dynamically load a DLL
  • Download demo project - 11 Kb
  • Usually, programmers use static linking when using functions in a DLL. This is the most easiest way to use a DLL. However, if a DLL is missing, application cannot be started. This is not a big problem: missing DLL means that something is wrong with application installation and the application should not be started anyway.

    However, if an application is designed in such a way as to offer some optional features with the help of one or more DLLs, missing DLL should not prevent the application to run normally (but without optional features). In this case, DLL must be loaded dynamically. This article presents a base class which offers all the necessary functionality for dynamic DLL loading. It is limited to DLLs exporting plain functions (not C++ classes). This is not a big limitation since all platform DLLs are written in this way. DLLs which export C++ classes are almost always used with static linking.

    Complete functionality is implemented in a class TDllModule. This class is not intended to be used directly, instead programmers should derive application specific class and extend it in a way explained in the following example. Only 2 virtual functions are important in this class: Create() and Destroy(). Create() function returns TRUE if the DLL is loaded correctly and an instance handle obtained. Destroy() will unload the DLL from process memory.

    Let's assume that some application needs the services of Microsoft's PSAPI (Process Status API) DLL to enumerate all running processes and display process executable filename. PSAPI DLL may not be installed on the machine since it is not part of the operating system. So, we need to dynamically load it and check whether it is loaded before using it.

    Therefore, we create a class TProcessStatusModule derived from TDllModule. We also need to create typedefs for functions needed from the DLL (this is not necessary but will keep the code easier to read).

    // This prototypes are obtained (copy and paste) from the DLL include file
    
    typedef BOOL (WINAPI *TFEnumProcesses)(
    	DWORD * lpidProcess, DWORD cb, DWORD * cbNeeded
    );
    typedef BOOL (WINAPI *TFEnumProcessModules)(
    	HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded
    );
    typedef DWORD (WINAPI *TFGetModuleBaseName)(
    	HANDLE hProcess, HMODULE hModule, LPSTR lpBaseName, DWORD nSize
    );
    typedef DWORD (WINAPI *TFGetModuleFileNameEx)(
    	HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize
    );
    typedef BOOL (WINAPI *TFGetModuleInformation)(
    	HANDLE hProcess, HMODULE hModule, LPMODULEINFO lpmodinfo, DWORD cb
    );
    
    class TProcessStatusModule : public TDllModule {
    private:
        TFEnumProcesses FEnumProcesses;
        TFEnumProcessModules FEnumProcessModules;
        TFGetModuleBaseName FGetModuleBaseName;
        TFGetModuleFileNameEx FGetModuleFileNameEx;
        TFGetModuleInformation FGetModuleInformation;
    
    public:
        TProcessStatusModule();
        virtual ~TProcessStatusModule();
    
        virtual BOOL Create(void);
        virtual void Destroy(void);
        
        BOOL EnumProcesses(DWORD * lpidProcess, DWORD cb, DWORD * cbNeeded);
        BOOL EnumProcessModules(HANDLE hProcess,HMODULE *lphModule,DWORD cb,
                                LPDWORD lpcbNeeded);
        DWORD GetModuleBaseName(HANDLE hProcess,HMODULE hModule,LPSTR lpBaseName,
                                DWORD nSize);
        DWORD GetModuleFileNameEx(HANDLE hProcess,HMODULE hModule,
                                  LPSTR lpFilename,DWORD nSize);
        BOOL GetModuleInformation(HANDLE hProcess,HMODULE hModule,
                                  LPMODULEINFO lpmodinfo,DWORD cb);
    };

    Most important function in the class example above is Create(). It needs first to call the base class that loads the DLL into the process memory and obtains a handle and second, to obtain pointers to functions in a DLL that are needed for the current application. If this function returns FALSE, then the DLL is not loaded correctly and cannot be used. Its implementation is the following:

    BOOL TProcessStatusModule::Create(void)
    {
      if (TDllModule::Create()) {
        // DLL handle is valid (DLL is loaded)
        FEnumProcesses = (TFEnumProcesses)::GetProcAddress(m_hHandle,
                                                           _T("EnumProcesses"));
        FEnumProcessModules = (TFEnumProcessModules)::GetProcAddress(m_hHandle,
                                                           _T("EnumProcessModules"));
        // Load all other needed functions
        ...
        // Can also check if all pointers to functions are different from NULL.
        return TRUE;
      }
      return FALSE;
    }

    All that is left to be done is the actual execution of functions in a DLL. All DLL functions needed in the application have a corresponding method in the class above (each method has exactly the same arguments as the actual DLL function, the return code is also the same). Example of one function is the following:

    BOOL TProcessStatusModule::EnumProcesses(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded)
    {
      ASSERT(FEnumProcesses != NULL);
      if (FEnumProcesses) 
          return FEnumProcesses(lpidProcess,cb,cbNeeded);
      return FALSE;
    }

    The only remaining issue is the error handling of the function(s) like the one above. Original DLL function returns BOOL as the function above. Even if the DLL is loaded correctly, we still do not know (if the return code of the function is FALSE) whether the function really returned FALSE or the function is not defined as exported from the DLL (this is valid for release version of the code since debug version will assert). There are 2 solutions for this problem:

    1. Check whether all needed DLL functions are exported from the DLL (in Create()) by comparing all obtained function pointers to NULL. If any one of them is NULL, unload the DLL and prevent application to use it.
    2. In the function above (and all other functions), use exception handling instead of a simple return code. If the function succeeds then it is guarantied that the return code comes from the DLL function.

    I have used this technique in many application for DLLs like Winsock2, TAPI, PDH (Performance Data Helper), PSAPI (Process Status API) and similar.

    Latest update of this article may be found here.

    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
    Web Developer SCA d.o.o.
    Serbia Serbia
    I am a cofounder of SCA Software, company that specializes in software for process control, visualization and communication. Programming for the last 10 years in C++, Delphi. Visual C++ for the last 6 years. Degree in Electronics Engineering and Telecommunications.

    Comments and Discussions

     
    QuestionDynamic Loading of DLL Export Class possible?? Pin
    rubel_dreams23-Jul-03 18:37
    rubel_dreams23-Jul-03 18:37 

    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.