Visual C++: Exporting functions from a DLL using “.DEF” file.

As we discussed in our previous article, the functions in the DLL can export using “__declspec(dllexport)” keyword or “.DEF” file. We have already discussed about exporting the functions using “__declspec(dllexport)” keyword in our previous article. In this article I am going to discuss about exporting functions in the DLL using “.DEF” file.

Lets start this article with a simple “C++” code:

// sample.cpp
#include <stdio.h>

extern "C" void SayHello()
{
    printf("Hello, World!\n");
}

Above code contains a “SayHello” function which will display “Hello, World!” message on the screen. Observe that we have used extern “C” keyword to instruct the compiler to not to generate decorated names. We have already discussed about this clearly in our previous article.

Now we will export “SayHello” function; so, other programming languages can call this function from the DLL. Remember that, in our previous article we have used “__declspec(dllexport)” keyword to export the function. But in this article we will export the function using “.DEF” file.

Before writing a “.DEF” file, lets discuss briefly about the “.DEF” file.

“.DEF” files are module-definition files (text files) contains module-definition statements that describe various attributes of a DLL. We need to mention the exported function names in the module-definition text file. Commonly we use only two module-definition statements. One is LIBRARY statement and the other one is EXPORTS statement.

LIBRARY statement must be the first statement in the module definition file; it is followed by the name of the DLL.

EXPORTS statement is required to lists the name of the exported functions.

So, our “sample.def” file looks like below:

LIBRARY Sample
   EXPORTS
     SayHello

Now its time to generate our DLL file. This is an important step. If we don’t generate the DLL properly, when we try to call the function from the DLL, the application will through the below error message:

The application was unable to start correctly (0xc000007b). Click OK to close the application.

Hence, we need to generate our DLL properly. Remember that we are exporting our function “SayHello” and we are not mentioned this in our “sample.cpp” file; but we have provided the information in “sample.def” file. Because of this, we must include “sample.def” file while generating the DLL. This will add the export functions information into the DLL. So, other programs can call the DLL exported functions. Below is the command to generate the DLL: Run the below command at the command prompt.

cl sample.cpp sample.def /LD

Once we run the above command, it will generate the below files:

sample.dll
sample.exp
sample.lib
sample.obj

Lets check whether the generated DLL contains exported functions information or not. For this, we can use DUMPBIN utility. DUMPBIN utility will provide the information about the format, symbols available in the DLL, library and executable files. Run the below command to get the information about our “sample.dll”.

dumpbin /exports sample.dll

Once we run the above command at the command prompt; it will display below information:

c:\>dumpbin /exports sample.dll
Microsoft (R) COFF/PE Dumper Version 11.00.61030.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file sample.dll

File Type: DLL

  Section contains the following exports for Sample.dll

    00000000 characteristics
    53BEBDD1 time date stamp Thu Jul 10 21:52:41 2014
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00001000 SayHello

  Summary

        3000 .data
        5000 .rdata
        2000 .reloc
        8000 .text

Observe that, the output displays our “SayHello” function name. That means, our “sample.dll” has our exported function.

Now we will use a simple test application to call our “SayHello” function from “sample.dll”. Lets create a simple test application using “Visual F#”.

Step 1. Create a “Visual F#” Windows project using “F# Application” wizard in Visual Studio 2012 IDE. Visual Studio will create a console based application.

Step 2. The code generated using “Visual F#” is managed code. But our “sample.dll” was generated by using native code (un-managed code). “.Net Framework” provides “System.Runtime.InteropServices” namespace to allow to use un-managed code within the managed code. So, we need to add the below statement in our “Visual F#” code to import “System.Runtime.InteropServices” namespace.

open System.Runtime.InteropServices

Step 3. Use DllImport attribute in “Visual F#” code to indicate our “SayHello” function is exposed by an un-managed code.

[<DllImport("sample.dll")>]
extern void SayHello();

We need to specify full path of the DLL in DllImport attribute. Or we can place the “sample.dll” into project’s “bin\Debug” or “bin\Release” folder depending on the project’s configuration. While building the project, if it fails to find the DLL, the compiler will through below error message:

Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'sample.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

   at Program.SayHello()
   at Program.main(String[] argv) in C:\TestFSharpApp\TestFSharpApp\Program.fs:line 8

Step 4. Call our “SayHello” function.

Here is the actual code:

open System.Runtime.InteropServices

[<DllImport("sample.dll")>]
extern void SayHello();

[<EntryPoint>]
let main argv = 
   SayHello()
    0 // return an integer exit code

Step 5. Build the project. And once we run the project, it will display “Hello, World!” message on the console window.

It has been proved that our “sample.dll” is working fine from other applications.

**

Leave a Reply