As we discussed in our previous article, the functions in the DLL can export using the “__declspec(dllexport)” keyword or “.DEF” file. We have already discussed exporting the functions using the “__declspec(dllexport)” keyword in our previous article. In this article I am going to discuss exporting functions in the DLL using “.DEF” file.
Let’s start this article with a simple “C++” code:
// sample.cpp #include <stdio.h> extern "C" void SayHello() { printf("Hello, World!\n"); }
The above code contains a “SayHello” function which will display a “Hello, World!” message on the screen. Observe that we have used the extern “C” keyword to instruct the compiler to not to generate decorated names. We have already discussed this clearly in our previous article.
Now we will export the “SayHello” function; so, other programming languages can call this function from the DLL. Remember that, in our previous article we have used the “__declspec(dllexport)” keyword to export the function. But in this article we will export the function using “.DEF” file.
Before writing a “.DEF” file, let’s discuss briefly the “.DEF” file.
What is a module definition file (.DEF) & how to define module definition files?
“.DEF” files are module-definition files (text files) that contain 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 the 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 list the name of the exported functions.
So, our “sample.def” file looks like below:
LIBRARY Sample EXPORTS SayHello
Now it’s 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.
Export functions from a DLL using “.DEF” files
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
Let’s check whether the generated DLL contains exported functions information or not. For this, we can use DUMPBIN utility. DUMPBIN utility will provide 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 the 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.
Sample application to call exported functions from the DLL
Now we will use a simple test application to call our “SayHello” function from “sample.dll”. Let’s create a simple test application using “Visual F#”.
Step 1. Create a “Visual F#” Windows project using the “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 of un-managed code within the managed code. So, we need to add the below statement in our “Visual F#” code to import the “System.Runtime.InteropServices” namespace.
open System.Runtime.InteropServices
Step 3. Use the DllImport attribute in the “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 the full path of the DLL in the DllImport attribute. Or we can place the “sample.dll” into the 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 a “Hello, World!” message on the console window.
It has been proved that our “sample.dll” is working fine from other applications.
**