Tutorial: Using Dynamic Libraries in C++Builder Applications

From RAD Studio
Jump to: navigation, search

Go Up to Introductory Tutorials


This tutorial describes the main steps to use Dynamic-Link Libraries (DLLs) in your C++Builder applications (both 32-bit Windows and 64-bit Windows).

A Dynamic-Link Library is a collection of routines that can be loaded at run time. For further information on DLLs, see Libraries and Packages (Delphi).

The following example contains a DLL that exports a class describing a stack of integers. The class is declared and implemented in the DLL project. The scope of this example is to dynamically load the DLL in a Firemonkey application, and to import and use the stack of integer class.

To export the class from the DLL:

  1. Write a C++ header file that declares a virtual base class for the stack. This virtual base class must contain the methods of the class that will be exported. This header file will be included in the project where the class from the DLL is imported.
  2. Declare and implement a class deriving from the virtual class of the stack. This is where the stack methods are implemented. In the following tutorial an integer implementation will be used for the stack.
  3. Implement and export a method which returns an instance of the stack of integer class. This method will be used to get an instance of the class declared in the DLL project.

Steps & Source code

This tutorial will guide you through the steps of creating and exporting a class using Dynamic Linked Libraries. Also, you will learn how to import and use the previously exported class.

The tutorial is composed of two main projects:

  • StackLibrary.dll - This is the DLL project. Here the stack of integer is declared and implemented. Also, this project contains a method used to get an instance of the stack class. This method is exported from the DLL.
  • TestStackLibrary - This is a FireMonkey Application. In this project, the previously declared DLL is loaded and the exposed method is imported. After invoking this method, an instance of the stack class is retrieved. This project tests the methods of the stack class.

Here are the steps for creating the two projects:

The Dynamic-Link Library

  1. Select File > New > Other > C++Builder Projects > Dynamic-link Library and click OK.
  2. On the New Dynamic-link Library dialog box that opens, select Firemonkey as the Target Framework and click OK.
    Note: This DLL will not use any framework, even if the application that will load the DLL is a Firemonkey application.
  3. Select File > Save All and set the names of the created files as follows:
    1. Name the C++ file in the DLL project StackExport.cpp.
    2. Name the precompiled header file StackLibraryPCH1.h.
    3. Name the DLL project StackLibrary.
  4. Select File > New > Other > C++Builder Files > Header File or File > New > Other > C++Builder Files > CPP File as necessary to add the following new three files to the StackLibrary project:
    • BaseStack.h holds the definition of the virtual base class for the stack. This class is used in both the DLL and the Firemonkey project.
    • StackOfInt.h holds the declaration for the Stack class. Stack is a descendant of BaseStack, that describes a stack of integers.
    • StackOfInt.cpp contains the implementation for the Stack class.
  5. Add code to the files created above, as detailed in the subsections below.
  6. Build your project to generate the StackLibrary.dll DLL.

BaseStack.h

BaseStack is the virtual base class for Stack (declared in StackOfInt.h). Therefore, no implementation is required for its methods.

// Base Virtual class for Stack.
// Declared to be used both in StackLibrary.dll and TestStackLibrary.exe projects
class BaseStack {
public:
        virtual bool IsEmpty() = 0;
        virtual int Top() = 0;
        virtual int Pop() = 0;
        virtual void Push(int value) = 0;
};

StackOfInt.h

This file declares the Stack class. Stack describes a stack of integer, and declares its methods: Push, Pop, Top, and IsEmpty.

#include "BaseStack.h"

// BaseStack implementation
// Here the methods from BaseStack are implemented
class Stack : public BaseStack {
private:
        // items holds the integers from the stack
        int* items;
        // represents the number of integers that are in the stack
        int count;
public:
        // Stack constructor
        Stack();
        // Stack destructor
        ~Stack();
        // Checks whether the stack is empty
        bool IsEmpty();
        // Returns the top of the stack
        int Top();
        // Returns the top of the stack and removes the element from the stack
        int Pop();
        // Inserts <code>value</code> into the stack
        void Push(int value);
};

StackOfInt.cpp

#include "StackOfInt.h"
#define MAX_SIZE 30
#define INDEX_OUT_OF_BOUNDS_EXCEPTION 1
#define EMPTY_STACK 2

// Stack constructor
Stack::Stack() {
        count = 0;
        items = new int[MAX_SIZE];
}

// Stack destructor
Stack::~Stack() {
        delete[]items;
}

// Returns the top of the stack
int Stack::Top() {
        if (count > 0) {
                return items[count - 1];
        }
        else {
                throw EMPTY_STACK;
        }
}

// Returns the top of the stack and removes the element from the stack
int Stack::Pop() {
        if (count > 0) {
                return items[--count];
        }
        else {
                throw EMPTY_STACK;
        }
}

// Checks whether the stack is empty
bool Stack::IsEmpty() {
        if (count == 0) {
                return true;
        }
        else {
                return false;
        }
}

// Inserts <code>value</code> into the stack
void Stack::Push(int value) {
        if (count < MAX_SIZE) {
                items[count++] = value;
        }
        else {
                throw INDEX_OUT_OF_BOUNDS_EXCEPTION;
        }
}

StackExport.cpp

This file exports the GetClassInstance function. This function returns an instance of Stack.

// If your DLL exports any functions that pass String objects (or structs/
// classes containing nested Strings) as parameter or function results,
// you will need to add the library MEMMGR.LIB to both the DLL project and
// any other projects that use the DLL.  You will also need to use MEMMGR.LIB
// if any other projects which use the DLL will be performing new or delete
// operations on any non-TObject-derived classes which are exported from the
// DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling
// EXE's to use the BORLNDMM.DLL as their memory manager.  In these cases,
// the file BORLNDMM.DLL should be deployed along with your DLL.
//
// To avoid using BORLNDMM.DLL, pass string information using "char *" or
// ShortString parameters.
//
// If your DLL uses the dynamic version of the RTL, you do not need to
// explicitly add MEMMGR.LIB as this will be done implicitly for you

#pragma hdrstop
#pragma argsused

#include "StackOfInt.h"

// This is the syntax for exporting methods from DLLs:
//
//   extern "C" __declspec(dllexport) void* __stdcall GetClassInstance();
//
// This method returns an instance of Stack:

extern "C" __declspec(dllexport) void* __stdcall GetClassInstance() {
        return static_cast<void*>(new Stack);
}

extern "C" int _libmain(unsigned long reason) {
        return 1;
}

The Firemonkey Application

  1. Right-click the project group in the Project Manager, and select Add new > Multi-Device Form - C++Builder.
  2. Select File > Save All and set the names of the created files as follows:
    1. Name the Firemonkey project TestStackLibrary.
    2. Name the C++ unit in the Firemonkey project TestUnit.
    Note: The source code provided later assumes that you save these files in the same folder where you saved the library files.
  3. In the TestStackLibrary form, add the following components:
    1. Add five TButton controls. Name them as follows and set their labels accordingly:
      • LoadButton
      • PushButton
      • TopButton
      • PopButton
      • IsEmptyButton
    2. For each button, add an OnClick event handler. You can add the code for the event handlers later.
    3. Add one TEdit control.
      This is how the Firemonkey form should look:
      StackForm.png
  4. In TestUnit.h, add the following two private members:
    • BaseStack* stack, to hold an instance of the stack.
    • void __fastcall EnableButtons().
    See TestUnit.h below.
  5. Implement TestUnit.cpp as described below.

You can now run your project. In the main window, click Load Library to load the library. After you manually load the library, the rest of the buttons in the form work against the dynamically-loaded library.

TestUnit.h

//---------------------------------------------------------------------------

#ifndef TestUnitH
#define TestUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Edit.hpp>
#include <FMX.StdCtrls.hpp>
#include <FMX.Types.hpp>
#include <FMX.Forms.hpp>
//---------------------------------------------------------------------------
#include "BaseStack.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
        TButton *PushButton;
        TButton *PopButton;
        TButton *TopButton;
        TButton *IsEmptyButton;
        TButton *LoadButton;
        TEdit *Edit1;
        void __fastcall PushButtonClick(TObject *Sender);
        void __fastcall PopButtonClick(TObject *Sender);
        void __fastcall TopButtonClick(TObject *Sender);
        void __fastcall IsEmptyButtonClick(TObject *Sender);
        void __fastcall LoadButtonClick(TObject *Sender);
private:        // User declarations
        BaseStack* stack;
        void __fastcall EnableButtons();
public:         // User declarations
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

TestUnit.cpp

// ---------------------------------------------------------------------------

#include <fmx.h>
#pragma hdrstop

#include "TestUnit.h"

#include "StackOfInt.h"

// ---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
// Holds the name of the dynamic-link library.
const wchar_t* library = L"StackLibrary.dll";
// type definition for pointer to functions that return (void*)
typedef void* (*VoidReturnFunc)();
// syntax that declares an imported function
extern "C" __declspec(dllimport) void* __stdcall GetClassInstance();

// ---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
}

// ---------------------------------------------------------------------------
// Method used to enable the buttons when the library is loaded
void __fastcall TForm1::EnableButtons() {
        PushButton->Enabled = true;
        PopButton->Enabled = true;
        IsEmptyButton->Enabled = true;
        TopButton->Enabled = true;
}

// ---------------------------------------------------------------------------
// OnClick event handler for LoadButton
void __fastcall TForm1::LoadButtonClick(TObject *Sender) {
        // Dynamically loads the library
        HINSTANCE load = LoadLibrary(library);
        if (load) {
                ShowMessage("Library loaded!");
                EnableButtons();

                VoidReturnFunc myFunc;

                // GetProcAddress returns a pointer to the loaded method
                myFunc = (VoidReturnFunc)GetProcAddress(load, "GetClassInstance");

                // Invoke GetClassInstance method
                stack = (BaseStack*) myFunc();

                // Note:
                // The BaseStack methods are pure virtual methods, but myFunc() returns an instance of the Stack class.
                // Wherever we call stack->methodName() later, we are actually calling Stack::methodName(), not
                // BaseStack::methodName().
        }
        else {
                ShowMessage("Library not loaded!");
        }
}

// ---------------------------------------------------------------------------
// OnCLick event handler for PushButton
void __fastcall TForm1::PushButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = StrToInt(Edit1->Text);
                        stack->Push(value);
                        Edit1->Text = "";
                }
                catch (int) {
                        ShowMessage("Please insert an integer into the editbox!");
                }
        }
}

// ---------------------------------------------------------------------------
// OnClick event handler for IsEmptyButton
void __fastcall TForm1::IsEmptyButtonClick(TObject *Sender) {
        if (stack) {
                if (stack->IsEmpty()) {
                        ShowMessage("The stack is empty!");
                }
                else {
                        ShowMessage("The stack is not empty!");
                }
        }
}

// ---------------------------------------------------------------------------
// OnClick event handler for PopButton
void __fastcall TForm1::PopButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = stack->Pop();
                        ShowMessage("Popped " + IntToStr(value));
                }
                catch (int Exception) {
                        ShowMessage("Exception " + IntToStr(Exception));
                }
        }
}

// ---------------------------------------------------------------------------
// OnClick event handler for TopButton
void __fastcall TForm1::TopButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = stack->Top();
                        ShowMessage("The top of the stack is " + IntToStr(value));
                }
                catch (int Exception) {
                        ShowMessage("Exception " + IntToStr(Exception));
                }
        }
}
// ---------------------------------------------------------------------------

Uses

See Also