C++ Why loading Windows DLL at runtime?

Update 2020 Apr

Version 2.7.0

Click on the link above. Just include that single header you see in there. There is now even simpler usage, through the dll_call function:

 

Caveat Emptor

Please remember using beep from kernel32.dll is one extremely simple use case. That dll is always present in the memory of your app. You might use this on some other dll which is not loaded by windows and on some nontrivial functions. Beware.

Do not hold function pointer into an unloaded dll

Prepared for you, test.cpp (in the same repository) now has one example on how not to use this little framework. And of course, see what is in that readme over there.

Gentle reminder: one can not load 32-bit dll’s into 64-bit apps and vice versa. Are they debug or release version does not matter. Hint: Recommended practice is not to name them all with the same name.

Update 2020 Feb

Version 2.5.0

  • All dependencies cut.
  • Logging streamlined
  • It should work with any C++ version you might have.

GitHub repository still here.

 

Update 2019 December

Version 2.0.0.

No dependency on the logger.  See the test.cpp on how to install your own logger.

Please see the readme in the Github project.

Update 2018 December

WIN32 headers are truly decades old. Being able to use some WIN32 API without including the required header is actually one big deal.  But you might be not so readily convinced. In case you need it, here is one very relevant use case, for proving the dynamic dll loading usability.

Sometimes that one Windows API  function you need, has been added between two major releases. It is non trivial to compile the code that will be able to reach that function or proceed smoothly if that function is not available on some particular machine. Example.

If you need GetGeoInfoEx you are faced with a very tricky building issue, impossible to be solved just by using WIN32  compile-time macros. The issue is, the existence of this function is not tied to the particular version of Windows, but to a  particular, not major release of Windows 10. Namely, Redstone 3.

GetGeoInfoEx exist in kernel32.dll but only if run-time is Windows 10 Redstone 3 or better.

To explain the complexity better, you on your up-to-date machine, will be able to compile and link your code that calls this function. And the tests will pass with a flying colours.  But that will fail at run-time on some distant customers machine that is not updated. Or is even not Windows 10. Gasp.

The system is not checking before run-time call, if a particular function exists in a particular dll, on a particular machine!

And no. You can not fiddle with kernel32.dll on any machine.

Using WIn32 versionhelpers.h, in this particular case, will not help. You need a more fine-grained check than that. Yes, you can use WIN32 API to develop a more fine-grained runtime check function, but you might soon realize that is very far from straightforward. An non trivial development in itself.

To solve this very tricky situation quickly and efficiently you can use this dll dynamic loader as described below. Here is the snippet showing you how:

First, declare the function pointer required

Now get to the function pointer to the instance of this function inside  kernel32.dll

Required function pointer type, the dll name, function name and that is it. And here is the secret sauce:

If function is not in the dll, returned value will be nullptr. No other harm will be done.

Next we have the function pointer, which we use as any other function. With this crucial run time check included.

You can not know if your exe is running on W7 or something older or W10 bellow REDSTONE 3. In that situation  the  function pointer will be null, and you will code easily around that, no harm done. To replace that if(fp)  line with something else, an amount of WIN32 sorcery is required, to find out do we use the right Windows and the right build of it. At runtime.

Without dynamic dll loading and using this mechanism, this code will be much more involved and complex. As mentioned below the github project is here.

The Original Article

And more importantly, why would anybody do it?

This article contains a Windows programming code and a C++ code of moderate to high complexity.

Why loading dynamically a dll and calling a function from it?

Among other things, because you do not need a header file. You do not need to find it, use it and potentially “fight” with issues some other completely unrelated stuff in the same header, might do to your project. Every WIN32 aficionado knows this is a big deal.

That is a simple use case. And here are more reasons you will want DLL dynamic loading.

You have written some snazzy C++ and you are very proud of it. Being C++ it might be some deep system “mambo-jumbo” that you have been amazed about. And every good deep system “mambo-jumbo”  does not require Graphical User Interface (aka GUI). After all, you are a system programmer and your code runs on servers! But it does require some way of signaling to its human administrator’s servants, and you have decided you want it to release sounds! Like squeaking when not well, that sort of a thing.

OK. Of to yet another masterful dollop of (server-side) C++. It is kernel32.dll and yes, there is a WIN32 function called Beep() in there.  Nothing easier than using it. Bam! Done!

This is where I stop you, my dear C++ sorcerer.

The efficiency of your application, is directly proportional to its memory usage

Think. Your C++ code produces a very small and fast and perhaps useful and invisible Windows program. The last thing you want to do it is to start including DLL’s in its build. Why?

Including DLL’s in a build means your program will need to make sure (at startup time) each of these DLL’s is found and then loaded into memory. And each of them, like kernel32.dll, will do the same for itself with a myriad of other DLL’s it depends on. And so on with those other dll’s. This is called “DLL Dependency Tree”

All of a sudden the file size and the memory footprint of your little, beep emanating C++ wonder, has grown. A lot. And you wanted “just” to call the Beep(), and very rarely at that.

This is why a concept of dynamically loading (Windows) dll’s has been invented. This is the sequence of activities it is made of:

  1. Load a dll
  2. Ask it (nicely) for a function you need
  3. Call a function
  4. Unload the library. (This is a crucial step)

By now I am sure, you might have lost all the patience and are about to leave, and copy-paste next C++ code from somewhere.  This is why I have made this little DBJDBJ tool, to do the dynamic loading of Windows dll’s.

The usage

Basically, there is one function that does it all. For it to be used you need three things

  1. the dll name
  2. the function name
  3. the function signature

 

Caution: dll will be properly unloaded when an instance of the loader above goes out of scope. For the fancy test unit please look into test.cpp inside the github repository.

To start the dll dynamic loading, one needs to know where is it, and give the full path to the dll location.

For understanding the “finding rules” please read the article here. Note: we are using LoadLibrary() and not the LoadLibraryEx(). Why? Primarily because we want to enforce the usage policy titled: “Keep it simple”.

We obviously could provide more functionality and make everything more complex. I suggest that a small percentage of readers needing the extended functionality, take this code and extend it.

The full working code is on the Github here. I am sure you know what is testing code and what is a library code in there. Hint: The lib is all in one header. It is made with 2017 Community Edition of Visual Studio. It uses standard C++17.

Any questions, or bugs, fire away and ask. Please use the issues tab, on this GitHub repo.

So little C++ so much good!
So little C++ so much good!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.