Introduction:
This post describes how to create and use Static Library (.Lib) and Dynamic (Run-Time) DLLs in C++ Builder. A library is a collection of pre-compiled code that can be re-used by programs. There are 2 types of libraries: static library and dynamic library.
Static Library (.lib) vs Dynamic Library (.dll)
A static library (.LIB file) (or archive) contains code that is linked to users’ programs at compile time. The executable file generated keeps its own copy of the library code.
A dynamic library (.dll) (or shared library) contains code designed to be shared by multiple programs. The content in the library is loaded to memory at runtime. Each executable does not maintain its replication of the library.
What are the differences between static and dynamic libraries?
Static libraries (.lib), while reusable in multiple programs, are locked into a program at compile time. Dynamic (.dll), or shared libraries, on the other hand, exist as separate files outside of the executable file.
C++ Builder Dynamic Library (.DLL) and Static (.Lib) Library
Let’s jump right into creating a C++ Builder Dynamic Library (.DLL)
This post is using C++ Builder 11.3
Steps:
1. File | New | Other | C++ Builder | Dynamic Library , select Source type = C++ , check Multi-Threaded, and select Target Framework = No Framework
2. Save the generated project to a new folder: File | Save Project As | New Folder | CppMyDLL (C:UsersamannarinoDocumentsEmbarcaderoStudioProjectsCppMyDLL). Rename project to CppMyDLL.cbproj, and rename Unit1.cpp to CppMyDLL.cpp
3. The generated CppMyDLL.cpp file has this code:
extern “C” int _libmain(unsigned long reason)
The _libmain function is the entry point into the dynamic library. When the dynamic library is loaded, the _libmain function is called. This function usually holds initialization code.
4. To be able to use the DLL and the Client .EXE on any computer (that does not have C++ Builder installed), use: Project | Options | C++ Linker | Link with Dynamic RTL = False
And Packages | RunTime Packages | Link with Run Time Packages = False
5. Build the application (Project | Build). You should get a success Build and Link!
6. Your source code output folder: (C:Users/amannarino/Documents/Embarcadero/Studio/Projects/CppMyDLL/Win32/Debug) should have a CppMyDLL.dll (Dynamic Library) and a CppMyDLL.lib (Static Library). The Static Library (.lib) is automatically generated from the Dynamic Library (,dll).
7. A helpful free utility for seeing the contents of the DLL is Dependency Walker. Dependency Walker is a free utility that scans any 32-bit or 64-bit Windows module (exe, dll, ocx, sys, etc.) and builds a hierarchical tree diagram of all dependent modules. The Download link for Dependency Walker is: https://www.dependencywalker.com/
8. Next, lets add a function to the CppMyDLL.cpp :
double Sum(double a, double b) {
return a +b;
}
Note: DLLs can contain functions that are hidden from the clients, and other functions that are public to the clients.
9. To make this Sum function visible to clients, we add: __declspec(dllexport) like this:
double __declspec(dllexport) Sum(double a, double b) {
10. Using Dependency Walker this function SUM shows as @Sum$add To remove the characters @ and the $add (that descibes the function) we add extern “C” to the function, like this:
extern “C” double __declspec(dllexport) Sum(double a, double b) {
11. Now, using Dependency Walker, the Sum function shows as: _Sum
12. To remove the underscore ‘_’, from the function name, we add __stdcall to say that this function uses standard calling conventions, like this:
extern “C” double __declspec(dllexport) __stdcall Sum(double a, double b) {
13. Now, using Dependency Walker, the Sum function shows as: Sum
So now with Dynamic or Static linking we can use the function name Sum, instead of those complicated characters: @Sum$add
Create a C++ Builder VCL application that calls (consumes) the DLL
14. Next, we can create a C++ Builder VCL application that calls (consumes) the DLL. In the same project, right-click on the ProjectGroup1, select Add New Project, select C++ Builder, Windows VCL Application.
15. File | Save Project As | in the same folder as the DLL project (C:Users/amannarino/Documents/Embarcadero/Studio/Projects/CppMyDLL)
Rename project name to ClientDLL.cbproj. Rename Unit1.cpp to uClient.cpp
16. Build the Blank VCL form application, to generate the /Win32/Debug folder:
This Win32/Debug folder should have the ClientDLL.exe and the CppMyDLL.dll and the CppMyDLL.LIB files from the above steps. Notice that both a .dll (Dynamic) and .lib (Static) file are generated.
17. For the ClientDLL.cbproj project: use Project | Options, C++ Shared Options, Final output directory = .$(Platform)$(Config) Click SAVE.
18. For the ClientDLL.cbproj project: Project | Options, C++ Linker, Link with Dynamic RTL = False Click SAVE.
19. For the ClientDLL.cbproj project: Project | Options, Packages | Runtime Packages, Link with runtime packages = False Click SAVE.
These options will add all the needed dependencies into the .EXE file so that it can be run on any computer, than does not have C++ Builder installed 🙂
20. Next, let’s try STATIC Linking to the CppMyDLL.LIB file. Static Linking is when the application requires the DLL at the very beginning of the applications start-up. For Static Linking, you need to add the CppMyDLL.LIB file to the search path of the .EXE file. The .LIB file contains all the information in the .DLL file.
Use Project | Add to Project, and find and select the CppMyDLL.LIB file. Click Open.
The Project Manager, should now show the CppMyDLL.LIB file added to your project:
Note: The .LIB file contains the information about the functions in the .DLL file. It’s a description of the .DLL.
21. Now that we have the .LIB file added to the Client project, we now need to provide the Client with the Function prototype, like this:
extern “C” double __declspec(dllexport) __stdcall Sum(double a, double b);
You can copy this function prototype from the CppMyDLL.cpp and paste it into the Client application, uClient.cpp , like this:
Or create a Header (.h) file that has these prototypes, and include the Header file with the Client application.
22. So, now that the Client app has the function prototype and the .LIB file added, we can now call the SUM function.
23. In the uClient.cpp (Client application) add a Button with Caption: Call Sum Function:
24. For the Button click Event, add this code:
ShowMessage(Sum(4,7));
25. Build and Run the Client application. Click the Button, and you should see:
Congratulations, you just performed Static Linking to the CppMyDLL.LIB file using C++ Builder!
The Sum of 4 + 7 = 11.
Note: The Client application does not have the body of the Sum function, but only has the prototype. The body is located inside the .DLL file. And that is why this Client application needs to have the .DLL file available at start-up for the Static Linking to work.
If the Client application cannot find the .DLL file, you will get an error like this:
26. Next, let’s implement Run-Time (Dynamic) Linking.
For Dynamic linking we do not need the .LIB file, nor do we need the function prototype, but it is useful to leave the function prototype in the code as a comment, so users know how to call the function.
For Dynamic linking, we need to use code like this for the Button Click:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
void __fastcall TForm1::Button1Click(TObject* Sender) { // For Static Linking using the function prototype and the .LIB // file, we can call the Sum function: // ShowMessage(Sum(4,7)); // For Dynamic Linking, we use code like this for // Run-Time Linking. With Run-Time (Dynamic) Linking, you load // the DLL manually, using LoadLibrary, and you locate the // function you wnat to call, using GetProcAddress, HINSTANCE CppMyDll; typedef double(__stdcall * pfSum)(double, double); pfSum Sum; if ((CppMyDll = LoadLibraryW(L"CppMyDLL.dll")) == NULL) { ShowMessage(L"Cannot load DLL!"); return; } if ((Sum = (pfSum)GetProcAddress(CppMyDll, "Sum")) == NULL) { ShowMessage(L"Cannot find DLL function!"); return; } ShowMessage(Sum(25, 75)); FreeLibrary(CppMyDll); } |
For Dynamic Linking, we use code like the above for Run-Time Linking. With Run-Time (Dynamic) Linking, you load the DLL manually, using LoadLibrary, and you locate the function you want to call, using GetProcAddress, and then we can call our Sum function. And after we no longer need the function in the DLL we can call FreeLibrary(MyDLL); to free the DLL.
27. Since we no longer need the .LIB file, Remove the CppMyDll.LIB from the project:
In the Project Manager, select the CppMyDll.lib, right-click, Remove from Project:
Click Yes, to remove the CppMyDll.Lib from the project.
28. Build and Run the Application. Click the Button for Dynamic Linking DLL:
Congratulations! You have now called the Sum function in the DLL using Dynamic Linking!
29. Lastly, in the Project Manager, Right-Click on the ProjectGroup1, Save the Project Group as: CppDLL_PG
In summary, C++ Builder supports both Static Library and Dynamic (Run-Time) Library Linking.
Additional details on C++ Builder Building Static Packages (.Lib) is here: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Building_Static_Packages
Additional details on C++ Builder Creating DLLs (.dll) is here: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Creating_DLLs_in_C%2B%2BBuilder
Please download the free 30 day trial of C++ Builder and try creating and running both Static Library and Dynamic (Run-Time) Library Linking from here: C++ Builder Trial Download
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition