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

Step by Step: Calling C++ DLLs from VC++ and VB - Part 4

Rate me:
Please Sign up or sign in to vote.
4.86/5 (57 votes)
2 Mar 2004CPOL9 min read 424.5K   7.2K   132   42
This series of articles is a step-by-step guide to constructing C++ DLLs that include C++ functions and C++ classes, and then calling the DLL functions and classes from VC++ and VB programs.

Introduction

This series of articles discusses the following common situations when working with DLLs:

Part 1Calling a DLL C++ function from a VC++ application
Calling a DLL C++ class from a VC++ application
Part 2Calling a DLL C++ function from a VB application
Part 3Calling a DLL C++ class from a VB application
Part 4Loading a C++ DLL dynamically from a VC++ application

Loading a C++ DLL dynamically from a VC++ application

VB does something automatically that takes some effort to accomplish in VC++ programs: dynamically loading DLLs. You can prove this for yourself very easily - just rename DLL3.dll in Part 3 and then try to run VB3.exe. It will start fine, but when you click the button, you will get an error message that the DLL cannot be found.

What is Dynamic Loading?

Normally, when you link to a DLL via a LIB file (for example, the MFC DLLs), the DLL is loaded when your application starts up. This kind of loading is referred to as implicit linking, because the system takes care of the DLL loading for you - all you have to do is link with the LIB file.

Dynamic loading (a.k.a. dynamic linking) means that your application loads a DLL just before you call a function in the DLL. For dynamic loading, you do not use a LIB file. Instead, what you do is call a pair of Win32 API functions (LoadLibrary/GetProcAddress) that load the DLL and then retrieve the address of a function in the DLL. Because you explicitly invoke these APIs, this kind of loading is also referred to as explicit linking. To summarize:

  • implicit linking - DLL is loaded automatically when your app starts
  • explicit linking - you write code to load DLL

This table will help sort out these terms:

Loading MethodSynonyms
static loadingstatic linking
implicit linking
implicit loading
automatic loading
dynamic loadingdynamic linking
explicit linking
explicit loading
manual loading

Why Use Dynamic Loading?

Before I get into the details of dynamically loading DLLs, let me first answer the question: When is it desirable to dynamically load a DLL? Here are the typical scenarios:

  1. You don't have a lib file to link with - this is a pretty lame reason, since if you worked at it you could generate a LIB file. On the whole, though, generating a LIB file is probably more work than just using LoadLibrary/GetProcAddress to dynamically load a DLL.
  2. A DLL may not always be present - if you want to provide for some graceful program degradation, you must dynamically load any DLL that may or may not be present on the target machine (example: UXTHEME.DLL, which exists only on XP). If you used implicit linking, your application would never have the chance to degrade gracefully - the system simply would not allow your app to start, and would instead display some alarming message to your user.
  3. You need to support multiple feature sets - this is one of the historically valid reasons for using dynamic loading. If you have a product that supports many different features, and you want to load only those features that the customer has paid for, then what you do is package each feature set in its own DLL, and ship the DLL to the customer when he orders it. This is also a very convenient way to add new features (read: plug-ins) to your product, essentially making it open-ended.
  4. You need to support multiple platforms - this is also one of the historically valid reasons for using dynamic loading. You need to support multiple platforms (Win98, Win2000, WinXP) and each platform requires slightly different code for some reason. A simple solution is to segregate the code for each platform in its own DLL.
  5. You need to speed up the time it takes to load your application - this is another historical reason for using dynamic loading. You will start thinking about this when customers start complaining about how slow your app is to load. The idea is to identify what DLLs are necessary to display the core UI, and then dynamically load all the other DLLs that your app needs.

A Modern Alternative: Delay Loading

If you have been paying close attention, you will be wondering by now why I am using the term "historical". If you think there is a more modern way to implement dynamic loading other than the LoadLibrary/GetProcAddress technique, you are correct. In scenarios 4 and 5 above, you may be able to use a technique called delay loading to effectively implement dynamic loading in your application, with very little work.

OK, so what is delay loading? This is a new feature introduced with Visual C++® 6.0 that is implemented by a new /DELAYLOAD linker option. The interesting thing about delay loading is that it is not dependent on any operating system support - once you have linked your application with delay loading under VC 6 or later, it will work on any Windows OS. This is because the VC++ 6 linker actually generates code (no previous MS linker had ever generated code before). When you use delay loading, your VC++ application works just like a VB application - the DLL you have specified to be delay loaded won't be loaded until you make a call to one of its functions (personally, I think Microsoft missed a trick by not calling this "Just In Time Loading"). In practice, what you would do is test some condition, and then execute some code depending on the condition:

if (IsWinXP())
    DisplaySpiffyXPThemedUI();
else
    DisplayBoringUI();

Inside DisplaySpiffyXPThemedUI(), you know it is safe to call the functions in UXTHEME.DLL, because you've already verified that you're running on XP. The first UXTHEME.DLL function that you call will cause UXTHEME.DLL to be loaded by the stub that the linker generated.

The neat thing is that all this happens transparently; you don't have to make any code changes at all to use delay loading (except, of course, you have to make sure that UXTHEME.DLL is present on the machine, before attempting to call its functions).

NOTE: There is one difference between delay loading and dynamic loading: with delay loading, you still need to link to a .LIB file, because the linker needs this information in order to create the delay-loading stub.

Using Delay Loading

To try out delay loading, I used the sample app EXE4 contained in the download for this article. After linking, I verified that the app was set to delay load DLL3 by using dumpbin /dependents exe4.exe:

Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file exe4.exe

File Type: EXECUTABLE IMAGE

  Image has the following dependencies:

    MFC42.DLL
    MSVCRT.dll
    KERNEL32.dll
    USER32.dll
    GDI32.dll
    ADVAPI32.dll
    SHELL32.dll
    VERSION.dll

  Image has the following delay load dependencies:

    DLL3.dll

  Summary

        2000 .data
        2000 .rdata
        1000 .rsrc
        5000 .text

There are several ways to give linker commands in VC++ 6, but the way I prefer is to use #pragma commands. This makes it less obscure, and so less likely to cause maintenance problems later. You can insert the #pragma commands anywhere in the app's source code, even in the stdafx.h file. Here are the commands I use:

#pragma comment(linker, "/ignore:4199")    // no imports found
#pragma message("automatic link to delayimp.lib")
#pragma comment(lib, "delayimp.lib")
#pragma message("DLL3.dll will be delay loaded")
#pragma comment(linker, "/delayload:DLL3.dll")

TIP: To suppress the linker warning "no imports found from DLL3.dll", you can use the undocumented linker command /ignore:4199. If you are wondering whether delay loading is even happening, try renaming DLL3.dll to something else and then run EXE4.exe. With delay loading, EXE4 will start. Without delay loading, you will immediately get an error message.

WARNING: If you specify a DLL as delay loaded, you must ensure that it is present on the target machine, or there will be a very unfriendly application crash when your app calls into the DLL.

Using Dynamic Loading

Now, let's get back to dynamic loading. In the demo app EXE4.exe, I show how to call DLL3 using four techniques:

  1. Call via LIB class export - this calls the DLL via the standard exported LIB symbol for CDLL3.
  2. Call via class wrapper objptr - this calls the CreateDll3() wrapper function to get an object pointer, and then calls the class methods directly.
  3. Call via class wrapper function - this uses the class wrapper functions exported in the LIB file.
  4. Call via dynamic linking (LoadLibrary) - this calls the class wrapper functions using LoadLibrary/GetProcAddress. The ::LoadLibrary() API is encapsulated in the class CXLoadLibrary, which is included in XLoadLibrary.cpp in the download for this article.

This last technique demonstrates how to load a DLL dynamically, and then use GetProcAddress() to get the address of a function in the DLL. Here are the steps:

  1. Load the library:
    CXLoadLibrary lib;
    if (!lib.LoadLibrary(_T("DLL3.dll")))
        return;
    
  2. Define a typedef for the function in the DLL - note the use of __stdcall:
    MC++
    typedef void * (__stdcall *CreateFn)();
    
  3. Get the address of the function:
    CreateFn pfnCreate =
        (CreateFn) GetProcAddress((HINSTANCE) lib, _T("CreateDll3"));
    
  4. Call the function via the typedef'd variable:
    void * objptr = pfnCreate();
    

Warning: The examples presented in this article have skimped on error checking. When using GetProcAddress(), you must check the return address!

Macros For Dynamic Loading

There are quite a few steps involved when you use the LoadLibrary/GetProcAddress APIs to call several functions in a DLL. Jason De Arte has developed a set of LateLoad macros that simplify calling these APIs. They are available in his article LateLoad DLL Wrapper.

The download for this article includes DLL3Wrapper.h, which shows how to use the LateLoad macros to load the functions in DLL3.dll. The advantage of using these macros is that they simplify the code and provide consistent error checking in case the DLL or one of its functions cannot be found.

DLL3Wrapper.h contains this code:

LATELOAD_BEGIN_CLASS(CDLL3Wrapper, DLL3, FALSE, TRUE)
    LATELOAD_FUNC_0(NULL,void *,STDAPICALLTYPE,CreateDll3)
    LATELOAD_FUNC_1(0,int,STDAPICALLTYPE,GetCpuSpeedDll3,void *)
    LATELOAD_FUNC_1_VOID(STDAPICALLTYPE,DestroyDll3,void *)
LATELOAD_END_CLASS()

and this code shows how the macros are used:

CDLL3Wrapper wrapper;

if (!wrapper.Is_CreateDll3())
{
    AfxMessageBox(_T("can't find CreateDLL3"));
}
else
{
    void * objptr = wrapper.CreateDll3();
    int nSpeed = wrapper.GetCpuSpeedDll3(objptr);
    CString s;
    s.Format(_T("wrapper.GetCpuSpeedDll3() returned %d"), nSpeed);
    m_Speed1.SetWindowText(s);
    wrapper.DestroyDll3(objptr);
}

Demo

Here is the demo program that is included in the download:

Image 1

Key Concepts

  • Applications can link to DLLs by either static linking or dynamic linking.
  • Using dynamic loading for DLLs gives you added flexibility for differing runtime environments.
  • Using delay loading for DLLs is an easy way to speed up application loading.

Conclusion

In these articles, I have discussed how to call functions and classes in C++ DLLs, from both VC++ and VB programs. DLLs are a great way to encapsulate functionality and improve chances for reusability. When used correctly, DLLs will improve your productivity, allow you to enhance your applications with plug-ins, and utilize VB in a continuous rapid prototyping product life cycle.

Revision History

Version 1.0 - 2004 March 2

  • Initial public release.

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
QuestionC2664 Errors Pin
Frank Muir26-Nov-12 18:26
Frank Muir26-Nov-12 18:26 
AnswerRe: C2664 Errors Pin
Albert Holguin3-Jul-13 12:12
professionalAlbert Holguin3-Jul-13 12:12 
GeneralRe: C2664 Errors Pin
Frank Muir3-Jul-13 13:00
Frank Muir3-Jul-13 13:00 
Generalcannot convert from 'const char *' to 'TCHAR * Pin
ABlokha7718-Mar-11 4:39
ABlokha7718-Mar-11 4:39 
GeneralRe: cannot convert from 'const char *' to 'TCHAR * Pin
Hans Dietrich18-Mar-11 8:25
mentorHans Dietrich18-Mar-11 8:25 
GeneralRe: cannot convert from 'const char *' to 'TCHAR * Pin
Member 85333206-Jan-12 3:44
Member 85333206-Jan-12 3:44 
GeneralRe: cannot convert from 'const char *' to 'TCHAR * Pin
Hoitabuam26-Feb-12 0:13
Hoitabuam26-Feb-12 0:13 
QuestionVB to VC++ Pin
TungstenX20-Jan-09 2:21
TungstenX20-Jan-09 2:21 
QuestionError returning value from C++ DLL to VC++ project. Pin
kishore.jena@gmail.com16-Oct-08 13:15
kishore.jena@gmail.com16-Oct-08 13:15 
QuestionHave a error when i calls the function in C++ DLL by VB [modified] Pin
mummum8612-Aug-08 7:48
mummum8612-Aug-08 7:48 
GeneralImportant detail is missing Pin
Lucas_Coder26-Mar-08 6:05
Lucas_Coder26-Mar-08 6:05 
The important detail of decoration is missing. I think you should explain in this dynamic loading how to write the ".def" file, in order to avoid decoration problems. Then the article would be perfect.

Can you give an example, where I use __declspec(dllimport)?
GeneralRe: Important detail is missing Pin
Hans Dietrich26-Mar-08 11:47
mentorHans Dietrich26-Mar-08 11:47 
GeneralDifference between normal C++ and Managed C++ Pin
chandrareddy26-Feb-08 22:03
chandrareddy26-Feb-08 22:03 
GeneralIt was really good article... Pin
ash_dwivedi2000@yahoo.com21-Jul-07 4:15
ash_dwivedi2000@yahoo.com21-Jul-07 4:15 
GeneralError when compiling Pin
Tacho20-Jun-07 12:57
Tacho20-Jun-07 12:57 
GeneralRe: Error when compiling Pin
Hans Dietrich20-Jun-07 14:16
mentorHans Dietrich20-Jun-07 14:16 
GeneralRe: Error when compiling Pin
Tacho21-Jun-07 6:39
Tacho21-Jun-07 6:39 
GeneralNON MFC C++ calling a C++ DLL Pin
jbeck69627-Apr-06 15:54
jbeck69627-Apr-06 15:54 
GeneralRe: NON MFC C++ calling a C++ DLL Pin
basoo12-May-07 4:54
basoo12-May-07 4:54 
GeneralSuch a nice contribution Pin
Eytukan6-Feb-06 2:40
Eytukan6-Feb-06 2:40 
Generalfunction address always 0x00000000 Pin
toxcct29-Nov-05 21:57
toxcct29-Nov-05 21:57 
AnswerRe: function address always 0x00000000 Pin
quovades13-Sep-07 15:05
quovades13-Sep-07 15:05 
QuestionTackling the Impossible Pin
Bobamagoo24-Nov-05 13:27
Bobamagoo24-Nov-05 13:27 
GeneralDLL Callbacks Pin
JRaiden14-Nov-05 18:08
JRaiden14-Nov-05 18:08 
Generaltwo methods with the same signature Pin
minnmaxx21-Oct-05 9:28
minnmaxx21-Oct-05 9:28 

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.