DLL Part Two - During Runtime

Load and Unload a DLL file when needed...

    Introduction

    As shown on the previous tutorial, creating DLL files is a very simple procedure. As mentioned in that article, the beauty of using a Dynamic Link Library is the fact that you can load it whenever you want during program runtime; something that we did not cover in the previous article. So now in this tutorial we will go over dynamically loading DLL files during an application's runtime. Due to this being part two, it is assumed that you have gone through the previous article. If you are not sure you understand everything at this point, go back and reassure yourself by reading over it again. So then you can proceed with this tutorial.

    In this tutorial, we are creating two projects inside the one solution just like the previous tutorial; one for the DLL, another for the running application. The major difference is there will not be any project dependency linking of the projects, as this will not load the DLL at load time. The application will be linking to the DLL from inside the program's code which will be called during runtime.

    As we will be concentrating more on code specifics, we will first work on setting up the projects. As before, there will also be a source and header file inside the DLL project which will this time be called DynamicLib, which again will be called interface.c and interface.h respectively. Inside the application project which will be called DynamicApplication, there will only be one source file, called main.c. Once the projects and solution is ready, it should look like the image below.

    Example Code In Detail

    Now we have the files ready, we can proceed with the coding. Like the previous tutorial, we will start with the DLL code. Starting with interface.h, the code is not too dissimilar to the previous tutorial.

    #ifndef INTERFACE_H
    #define INTERFACE_H
    
    #include <Windows.h>
    #include <stdio.h>
    
    #ifdef RELIANT_EXPORTS
    #define RELIANT_API __declspec(dllexport)
    #else
    #define RELIANT_API __declspec(dllimport)
    #endif
    
    RELIANT_API void HelloDLL();
    
    #endif

    As we have coded the header file, you may have noticed the only difference to the previous tutorial code is the change of the function name; this time will be called HelloDLL() as the header prototype function shows. Now in the accompanying source code interface.c, we start the same way with defined that the function we are using will be an export interface function.

    #define RELIANT_EXPORTS
    #include "interface.h"

    Now on to the single function, it is renamed as mentioned earlier. It is literally the same code (code again is self-explanatory), except for the change of message in the printf string. Once you get to the end of the subroutine, you are prompt to enter to exit the function; thus closing the DLL file.

    void HelloDLL()
    {
    	printf("Now you have loading the DLL File\n");
    	printf("During Runtime. You Need to unload it...\n");
    	printf("Press Enter Key to Close and Exit");
    	fflush(stdin);
    	getchar();
    }

    So unlike the previous tutorial as stated earlier there should not be any project dependencies. The only reason for the two projects to be in the same solution is for ease of management during development. As previously stated, you can use any DLL file while you know the functions to operate with. Now we have completed the source and header for the DynamicLib project, we will move onto the executable DynamicApplication project. As there is again only source file main.c, we will go through this in detail as it uses functions and procedures we have not dealt with before as we are now loading the DLL during the program's runtime. First we will include the system header file windows.h, so we can use the DLL loading functions. Then you can see the HINSTANCE data member which has been given the identifier hInstance; as for this example will handle the instance of the DLL during loading into the program. Then you can see a declaration which starts as a void followed by a parenthesis; which within has an asterisk (also known as a dereference operator) and an identifier named HelloDLL. After that you can see another parenthesis with void. That is a declaration of a function pointer. Function pointers are a special data type; as they point to a function, which are declared as a prototype variable data type, except for the parenthesis and asterisk. Instead of assigning a variable or other member data, you place a function instead.

    #include <Windows.h>
    
    	HINSTANCE hInstance;
    
    // DECLARING THE FUNCTION POINTER USED FOR THE DLL CALL
    void (*HelloDLL)(void);

    Once we enter the int main, we assign the LoadLibrary to the hInstance; which was mentioned earlier handles the DLL instance. The name of the DLL is obviously DynamicLib.dll, as it is assigned to the one argument parameter, which asks for you to place the name of the DLL you want to load. Once that is done without problems, the DLL is loaded in memory.

    int main()
    {
    	// ALLOW THE SYSTEM TO LOAD THE REQUIRED DLL
    	hInstance = LoadLibrary(L"DynamicLib.dll");

    When loading the DLL, the hInstance handle will check if it was a success through a conditional statement. If it is a success, the code will proceed to the statement and assign the GetProcAddress to the HelloDLL function pointer. GetProcAddress is a function which grabs the exported function from the DLL. The first parameter argument passed is the DLL handler from where you are extracting the function, and the second argument is the string name of the function you want to load and export from the DLL. As the HelloDLL is a function pointer, it is important to typecast the GetProcAddress when assigning; with (inside the parenthesis a void with an asterisk) a dereferenced operator and void keyword.

    	// NOW CHECK TO SEE IF THE DLL FILE LOAD WAS SUCCESSFUL
    	if (hInstance != NULL)
    	{
    		// CALL THE FUNCTION YOU WANT TO USE FROM WITHIN THE DLL
    		HelloDLL = (void*) GetProcAddress(hInstance, "HelloDLL");
    		
    		// IF FUNCTION IS AVAILABLE
    		if (HelloDLL != NULL){
    			HelloDLL();
    		}

    GetProcAddress will try to acquire the function from the DLL. Another conditional statement will be used to see if the GetProcAddress was successful. If so, it will call the HelloDLL function pointer, which now has the exported function from DynamicLib.dll. Now the code from the DLL will call and as you remember with the function; it will simply write a few lines, and exit. If the function does not load, the code (shown below) is invoked and it will come with a message dialogue, which prompts the user that the function name was wrong. Whether if the operation was successful, the FreeLibrary function will close and remove the hInstance handle from memory; thus removing the instance of the DLL from the program's memory.

    		else
    		{
    			// DOESN'T SEEM TO BE AVAILABLE - CALL UP ERROR MESSAGE...
    			MessageBox(NULL,L"Wrong Function Name",L"Error - Function Not Loaded", MB_ICONERROR);
    		}
    		// ONCE CODE HAS RUN OR UNSUCCESSFUL, CLEAN UP DLL LOADING INSTANCE FROM MEMORY
    		FreeLibrary(hInstance);
    	}

    So in a nutshell, the function runs from the DLL. And then exits the scope. Now the last code below, basically follows the conditional statement if the DynamicLib.dll file had failed loading. A simple dialogue message would come up notifying the user that the DLL file was not available.

    	else
    	{
    		// ERROR MESSAGE TO NOTIFY THAT THE DLL LOADING WAS UNSUCCESSFUL
    		MessageBox(NULL,L"Dynamic Link Library Not Found",L"Error - DLL Not Found", MB_ICONERROR);
    	}
    	return 0;
    }

    Whether it run successfully or not, it will reach to the end of the int main scope, where it will reach return 0, and close the program.

    Summary

    So that was it. Not to dissimilar to the previous program, except the main application code. So when you use DLL files, you can load and unload the DLL file when it is needed; and you can create and use as many functions from the DLL. Remember, it will be good to have components you need whenever you need it during the running of your application. It is good to unload the DLL when unneeded; as it frees the program memory usage, thus better overall performance. After completing this tutorial, why not add more functions to the DynamicLib.dll module; and be more adventurous. Once you get used to it, your programming will be more modular, expandable, and more importantly flexible.

    #ifndef INTERFACE_H
    #define INTERFACE_H
    
    #include <Windows.h>
    #include <stdio.h>
    
    #ifdef RELIANT_EXPORTS
    #define RELIANT_API __declspec(dllexport)
    #else
    #define RELIANT_API __declspec(dllimport)
    #endif
    
    RELIANT_API void HelloDLL();
    
    #endif
    /*
            Using DLL File - Part 2 - Runtime Load - interface.c      Reliant Code
            Creating a DLL file to run dynamically with the executable
    		Not too dissimilar to the previous tutorial
    */
    #define RELIANT_EXPORTS
    #include "interface.h"
    
    void HelloDLL()
    {
    	printf("Now you have loading the DLL File\n");
    	printf("During Runtime. You Need to unload it...\n");
    	printf("Press Enter Key to Close and Exit");
    	fflush(stdin);
    	getchar();
    }
    /*
            Using DLL File - Part 2 - Runtime Load - main.c      Reliant Code
            Loading a DLL File During Run Time
    */
    #include <Windows.h>
    
    HINSTANCE hInstance;
    
    // DECLARING THE FUNCTION POINTER USED FOR THE DLL CALL
    void (*HelloDLL)(void);
    
    int main()
    {
    	// ALLOW THE SYSTEM TO LOAD THE REQUIRED DLL
    	hInstance = LoadLibrary(L"DynamicLib.dll");
    
    	// NOW CHECK TO SEE IF THE DLL FILE LOAD WAS SUCCESSFUL
    	if (hInstance != NULL)
    	{
    		// CALL THE FUNCTION YOU WANT TO USE FROM WITHIN THE DLL
    		HelloDLL = (void*) GetProcAddress(hInstance, "HelloDLL");
    		
    		// IF FUNCTION IS AVAILABLE
    		if (HelloDLL != NULL){
    			HelloDLL();
    		}
    		else
    		{
    			// DOESN'T SEEM TO BE AVAILABLE - CALL UP ERROR MESSAGE...
    			MessageBox(NULL,L"Wrong Function Name",L"Error - Function Not Loaded", MB_ICONERROR);
    		}
    		// ONCE CODE HAS RUN OR UNSUCCESSFUL, CLEAN UP DLL LOADING INSTANCE FROM MEMORY
    		FreeLibrary(hInstance);
    	}
    	else
    	{
    		// ERROR MESSAGE TO NOTIFY THAT THE DLL LOADING WAS UNSUCCESSFUL
    		MessageBox(NULL,L"Dynamic Link Library Not Found",L"Error - DLL Not Found", MB_ICONERROR);
    	}
    	return 0;
    }
    blog comments powered by Disqus
    Download Files Here

    PDF Version Here

    Download this Article in PDF

    Feedback on Article

    Report Bugs, Errors or Send Feedback

    MD5 Hash Check : What is MD5?