How to Use a DLL from Unmanaged C

How to Use a DLL from Unmanaged C++

When your application loads a DLL, Windows searches for it, just as it does when you run an executable. First the directory of the application loading the DLL is searched, and then the current directory, and then the system directory ( \Windows\System for Windows 95, 98, or XP, \Winnt\System or \Winnt\System32 for Windows NT or 2000), and then the Windows directory, and finally each directory specified in the path .

If you plan to have a DLL shared by many applications, it's a good idea to put it in the System directory ( Winnt\System or Windows\System ) where it will be found easily. When one application installs an updated version of the DLL, all applications that use it will start to use the new one. If you prefer, you can copy the DLL to the same directory as the executable that uses it. You might end up with several copies of the DLL on your hard drive as a result. Some developers prefer to know that the DLL they are using will never be changed by anyone else, and consider a little wasted drive space a small price to pay for this reassurance. In this chapter, you copy the DLL to the same directory as the executable.

The dumpbin Utility

Visual C++ comes with a utility called dumpbin that can show you what's going on inside a DLL. To run it, bring up a Visual Studio .NET 2003 command prompt, and then change directories to the directory containing the DLL. The utility takes several command-line options; the two most useful are /exports and /imports .

To see all the methods exported from the legacy DLL, change to the appropriate directory, and then enter this command:

 dumpbin /exports  legacy.dll 

You should see output like this:

 Microsoft (R) COFF/PE Dumper Version 7.10.3077 Copyright (C) Microsoft Corporation.  All rights reserved. Dump of file legacy.dll File Type: DLL   Section contains the following exports for Legacy.dll     00000000 characteristics     3F2DDC3E time date stamp Mon Aug 04 00:08:30 2003         0.00 version            1 ordinal base            2 number of functions            2 number of names     ordinal hint RVA      name           1    0 00013479 Add           2    1 000134EC Log   Summary         4000 .data         1000 .idata         5000 .rdata         3000 .reloc        25000 .text        12000 .textbss 

You can also uses the /imports option to see how many methods from other libraries this simple library uses. Try it; it's a real eye- opener !

Implicit Linking to a DLL

When you build a DLL in Visual C++, a companion .lib file is generated for you as well. This file is called the import library ; it contains the definitions of all the functions in the DLL, but not the code. Working from unmanaged C++, you don't have to write any code that finds or loads the DLL. You just link with the import library as though it were a static library. This is called implicit linking of the DLL. It's the simplest approach to use.

To test the DLL, start by creating an unmanaged console application (the details are in Chapter 2, "Creating Test Harnesses and Starter Applications") called UseLegacy . Copy legacy.h from the Legacy project folder to the UseLegacyDLL project folder, and copy both legacy.dll and legacy.lib from the Debug folder under Legacy to UseLegacyDLL.

Edit the UseLegacyDLL.cpp file so that it reads as follows :

 // UseLegacyDLL.cpp : Defines the entry point for the console application. // #include "stdafx.h"  #include "windows.h"  #include "legacy.h"  #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) {    cout << "1 + 2 is " << Add(1,2) << endl;    SYSTEMTIME st;    GetLocalTime(&st);    Log("Testing", &st);    cout << "log succeeded"  << endl;    return 0; } 

This code includes windows.h to get the declaration of SYSTEMTIME , and includes legacy.h to get the declarations of Add() and Log() . Because it writes to the screen with cout , it includes iostream and uses the std namespace. The main function exercises Add() , and then gets the local time and passes it to Log() . GetLocalTime() takes a pointer to a SYSTEMTIME structure, which it changes. It's defined in winbase.h, so you don't need any extra preparation to be able to use it.

Use the property pages for the project to add legacy.lib to the linker dependencies. (Right-click the project in Solution Explorer, choose Properties, Expand the Linker section, select Input, and add legacy.lib to Additional Dependencies.) Build and run the project without debugging: You should see output like this:

 1 + 2 is 3 log succeeded Press any key to continue 

Take a look in the root of your C drive for the file log.txt and open it in Notepad. You should see log messages (one for each time you ran the application) that look like this:

 2003/8/4 - 11:14 Testing 2003/8/4 - 11:19 Testing 2003/8/4 - 11:35 Testing 

It's as simple as that to use an unmanaged DLL from an unmanaged application. You can apply these concepts to create a DLL for any of the kinds of unmanaged class libraries discussed in Chapter 5.

Explicit Linking to a DLL

Although implicit linking is the simplest approach, and works well for the sample in this chapter, it's not always the right approach. Explicit linking is needed when one of these situations apply:

  • You must minimize the memory used by your application.

  • The application must start up as quickly as possible.

  • You want to use one of two DLLs (with different filenames) holding functions with the same names, and want to choose which DLL to load while the application is running.

Explicit linking works well in this situation because your application loads each DLL explicitly just before calling a function from it. The benefits are maximized when the functions in the DLL aren't always called each time the application runs. With implicit linking, all the DLLs are found and loaded just as the application starts running. That can make the application slow to start up, and increase the memory it uses. With explicit linking, a DLL that is never used is never loaded, and it's possible that fewer DLLs are in memory at any particular time.

Most developers have no need to write the extra code to link the DLL explicitly. If you do, here are the steps:

  • Before your first call to a function from the DLL, call LoadLibrary() to find and load the DLL.

  • Replace all calls to the DLL with two lines: one that gets a function pointer by calling GetProcAddress() and one that uses the function pointer.

  • When you have finished calling functions from the DLL, call FreeLibrary() to unload it.

You can try this with the DLL for this chapter by creating a Win32 console application called UseLegacyEx . Build the new project to create a Debug folder, and then copy legacy.dll from the Legacy Debug folder into the UseLegacyEx Debug folder. Do not copy the import library, legacy.lib, or the header file, legacy.h.

Edit the UseLegacyEx.cpp file so that it looks like this:

 // UseLegacyEx.cpp : Defines the entry point for the console application. // #include "stdafx.h"  #include "windows.h"  #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) {    typedef bool (*FPLog)(char*, SYSTEMTIME*);    typedef double (*FPAdd)(double, double);    HINSTANCE hDLL;    FPAdd fpadd;    FPLog fplog;    hDLL = LoadLibrary("legacy.dll");    fpadd = (FPAdd)GetProcAddress(hDLL, "Add");    cout << "1 + 2 is " << fpadd(1,2) << endl;    SYSTEMTIME st;    GetLocalTime(&st);    fplog = (FPLog)GetProcAddress(hDLL, "Log");    fplog("Testing", &st);    cout << " log succeeded"  << endl;    FreeLibrary(hDLL);    return 0; } 

This code includes windows.h for the declaration of SYSTEMTIME , and iostream for cout . It also uses the std namespace. Notice that it does not include legacy.h or declare Add() and Log() .


The code that links the DLL explicitly is more complicated than the code that links it implicitly. It should, however, be more complicated still. Error checking has been omitted so the flow is easy to see.

The call to LoadLibrary() should be followed by an if block that tests hDLL is not a null pointer: Only if hDLL is not null should the rest of the code execute. If it is null, the user should probably be informed of the problem.

Each call to GetProcAddress() should be followed by an if block that tests the function pointer is not null; if it is null, FreeLibrary() should be called and the rest of the code should not execute.

You could consider using exceptions to make your error-handling less intrusive . Whether you use if checks or exceptions, remember to implement error checking and handling in your own DLL-loading applications.

Working with function pointers is a lot simpler if you use a typedef. That's especially true in this case, because GetProcAddress returns a generic pointer that must be cast to the correct kind of function pointer. You will need one typedef for each signature (parameter types and return type) in your DLL. Because Add() and Log() have different signatures, they each need a typedef.

After setting up the typedefs and declaring a DLL handle and the function pointers, this code calls LoadLibary() to actually load the DLL, and saves the return value; GetProcAddress() and FreeLibrary() will need it.

Each call to GetProcAddress() needs the DLL handle and the name of the function. Because name decoration was turned off in the Legacy DLL, you can pass undecorated names such as Add and Log . Once the function pointer is pointing to a function inside the DLL, you call it just as though the function pointer was a function, passing the parameters that are destined for Add() or Log() . Once all the functions have been called, FreeLibrary() unloads the DLL, reducing the memory used by the application.

The extra work involved in linking a DLL explicitly is not enormous . Nonetheless, you should always start by writing code that links the DLL implicitly, and then test to see whether there are performance issues that explicit loading would improve. Only if it's necessary should you take on this extra work.

Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory © 2008-2017.
If you may any questions please contact us: