Article source: blog.sina.com.cn/s/blog_4119…

 

Overview of dynamic link library technology

Dynamic link library technology is used a lot. In fact, the whole Windows is built by a dynamic link library (DLL), whether it is the system kernel, or system call API package, but also general tools (such as control panel, ActiveX plug-in, etc.), are a dynamic link library file. Dynamic link library is not the unique technology of Microsoft, it is the inevitable product of software engineering development to a certain stage. On Unix-like systems, this binary executable module technology is not called a dynamically linked library, but a shared Object or shared library, usually with the suffix.so (short for Share Object). For simplicity, this dynamic linking technology will be referred to as a DLL or shared library.

Second, Qt dynamic link library programming

Writing DLLS using C++ object-oriented classes requires a lot of attention to detail, mainly binary (ABI) compatibility.

Qt’s cross-platform nature is good, as is its support for dynamically linked libraries, the subject of this article. QT wraps around dynamic linked library programming techniques for various platforms, which are collectively called Shared Libraries. Using Qt wrapped classes and macros, you can write cross-platform shared libraries and plugins — of course, this is cross-platform at the source level, and you shouldn’t expect DLLS compiled with MSVC to be integrated into Arm-based Linux applications — which is a nice, nice idea.

QT uses two macros to export and import symbols (functions or global variables/objects) (def files are no longer available across platforms) :

1. Q_DECL_EXPORT // must be added to symbol declaration (shared library project)

2. Q_DECL_IMPORT // must be added to symbol declarations (client projects using shared libraries)

QT uses the QLibrary class to implement dynamic loading of shared libraries, that is, to decide which DLL to load at run time, used by the plug-in mechanism.

QT shared libraries and plugins

This section uses examples to implement a shared library and a plug-in. Developed on Windows platform, compiled using VS2005, QT library version 4.6.2.

This example will write the following three categories of projects:

1. Bil project: shared library project, output bil.dll and bil.lib, base interface library, define a public interface IAnimal (abstract class), used by client projects and plug-in projects;

2. Plugin project: Plug-in project. Now I write BilDog and BilPanda plug-in projects to realize the functions of IAnimal for client project loading and testing. The two projects output bildog. DLL and bilpanda.dll.

3. Test project: client application project, output test. exe, the interface can select the Animal plug-in to load, and then call Animal function, complete the Test;

1. Write a shared library — the implementation of Bil project

This project defines an abstract IAnimal class as an export interface for use by client projects and plug-in projects. The project type is shared library, and two files bil. lib and bil. DLL will be generated. Bil.lib will be referenced by Plugin project and Test project, while bil. DLL will be dynamically loaded by test.exe runtime.

Create a new header file called bil.h and enter the following code:

1.  #ifndef BIL_H  

2.  #define BIL_H  

3.  #include <Qt/qglobal.h>  

4. // Define BIL_SHARE so that users no longer have to deal with the import and export details of symbols

5.  #ifdef BIL_LIB  

6.  # define BIL_SHARE Q_DECL_EXPORT  

7.  #else  

8.  # define BIL_SHARE Q_DECL_IMPORT  

9.  #endif  

10. #endif // BIL_H  

You probably don’t know what the BIL_SHARE macro is for right now. No matter, please continue to look at the following IAnimal interface definition code:

1.  #ifndef IANIMAL_H  

2.  #define IANIMAL_H  

3.  #include “Bil.h”  

4.  class BIL_SHARE IAnimal  

5.  {  

6.  public:  

7.      IAnimal();  

8.      virtual ~IAnimal();  

9.  public:  

10.     virtual void Eat() = 0;  

11.     virtual void Run() = 0;  

12.     virtual void Sleep() = 0;  

13.};

14. #endif // IANIMAL_H  

Now you see what the BIL_SHARE macro can do. The BIL_SHARE macro automatically declares IAnimal to be an exported class or an imported class depending on whether or not the project compilation option BIL_LIB is defined. So, using the BIL_SHARE macro, all we need to do is provide the same IAnimal definition file (iAnimal.h) to the developer of the IAnimal plugin.

Of course, we need to define the BIL_LIB macro in the Bil project’s compile options first, so that BIL_SHARE is the declaration of the exported symbol in the Bil project. Do not define BIL_LIB in the Animal plugin project because IAnimal is the import symbol.

How do compile options define macros? If you are using Visual Studio project files, go to Project Properties -> Config Properties ->C/C++-> Preprocessor and add the macro BIL_LIB to the preprocessor definition. For QT project files, add the following definition to the QT project file bil. pro:

1.  DEFINES += BIL_LIB  

Eat(), Run(), and Sleep() are three pure virtual functions that represent the actions of eating, running, and sleeping. This is abstract, because different animals have different eating and sleeping positions, and there are many more animals in the world — it doesn’t matter. Let the different representations of these specific animals be left to the authors of the IAnimal plugin — that’s the beauty of the interface, plus the idea of plug-ins, and the whole application becomes open and extensible!

Ianimal.cpp:

1.  #include “IAnimal.h”  

2.  IAnimal::IAnimal()  

3.  {  

4.}

5.  IAnimal::~IAnimal()  

6.  {  

7.}

Although it is necessary that only the constructors and destructors are implemented and nothing is done, we do not use inline constructors and destructors for the time being, otherwise we may get linking errors when implementing IAnimal in the plug-in project.

Ok, let’s start compiling and generate the entire Bil project. We end up with two output files: bil.lib and bil.dll.

We provide Animal plugin developers with:

· Two header files: bil.h and ianimal.h

· Two library files: bil.lib and bil.dll

The following plug-in class projects and client projects rely on these files. You may prefer to think of Bil as a generic DLL library, like QT or MFC – which it is, as a dynamic shared library.

2. Write Animal plugins — implementation of BilDog and BilPanda projects

Now, let’s implement two small plug-ins. The BilDog plugin is simple, just reporting “I’m Dog and I’m eating a bone”; The same is true for BilPanda — this is just a test, and you can do as much as you want with the project — that’s right, with the IAnimal interface.

Create a BilDog project and add bil. h, iAnimal. h, and bil. lib output from the Bil project to the project.

Create the Dog class header file dog.h:

1.  #ifndef CLASS_DOG_H  

2.  #define CLASS_DOG_H  

3.  #include “IAnimal.h”  

4.  class Dog : public IAnimal  

5.  {  

6.  public:  

7.      Dog(void);  

8.      virtual ~Dog(void);  

9.  public:  

10.     virtual void Eat();  

11.     virtual void Run();  

12.     virtual void Sleep();  

13.};

14. #endif // CLASS_DOG_H  

Create the implementation file dog.cpp for the Dog class:

1.  #include <QtGui/QMessageBox>  

2.  #include “Dog.h”  

3.  Dog::Dog(void)  

4.  {  

5.}

6.  Dog::~Dog(void)  

7.  {  

8.}

9.  void Dog::Eat()  

10. {  

11. QMessageBox::information(NULL, “Hello”, “Dog eating …” );

12.}

13. void Dog::Run()  

14. {  

15. QMessageBox::information(NULL, “Hello”, “Dog running …” );

16.}

17. void Dog::Sleep()  

18. {  

19. QMessageBox::information(NULL, “Hello”, “Dog sleeping …” );

20.}

Call QT’s QMessageBox:: Information () function to pop up an information dialog box.

It is also very important that we provide an interface to create (and release) Animal concrete objects (Dog in this case) and export these functions so that the main program (test.exe) can parse this interface function, dynamically create the Animal object, and access its functions.

To create a new bildog.h file, enter the following code:

1.  #ifndef BILDOG_H  

2.  #define BILDOG_H  

3.  #include “Dog.h”  

4. // extern “C” generates an export symbol without any decorations, making it easy for the main program to find it

5.  extern “C”  

6.  {  

7.      Q_DECL_EXPORT IAnimal * CreateAnimal();  

8.      Q_DECL_EXPORT void ReleaseAnimal(IAnimal * animal);  

9.}

10. #endif // BILDOG_H  

The work of these two functions is simple: create and release objects directly. Here is the code for bildog.cpp:

1.  #include “bildog.h”  

2.  IAnimal * CreateAnimal()  

3.  {  

4.      return new Dog();  

5.}

6.  void ReleaseAnimal(IAnimal * animal)  

7.  {  

8.      delete animal;  

9.}

At this point, an Animal plugin is complete. Compile, generate BilDog project, output bildog. DLL plug-in file for the main program test.exe to call dynamically.

The BilPanda project is similar to the BilDog project, so I won’t post the code here. This is how we develop Animal plugins (even from third parties).

We are not going to output the.lib files and those headers for the project, because we are going to have the main program load the DLL plug-in and call the plug-in’s functions as needed at runtime, rather than having the main program project specify the specific plug-in at compile time.

3. Write the client program — the implementation of the Test project

The Test project is a Test application project, but it acts as the main application, a client that can use the Animal plugin.

Again, this project uses the Bil shared library, so import several output files from the Bil project into the Test project first.

Let’s assume that the Test main program is a dialog box with an edit box and a “load and call” button. In the edit box, the end user types in the file name of the Animal plug-in (such as BilDog, the suffix can be omitted, and Qt determines whether to look for.dll or.so depending on the platform). Click “Load and call” to load the shared library and call the Eat() function of the dynamically created IAnimal object (of course you can call Run() or Sleep(), this is just an example).

The following functions will be called by the trigger event of the load and call button:

1.  // …  

2.  #include <QString>  

3.  #include <QLibrary>  

4.  #include <IAnimal.h>  

5.  // …  

6. // strPluginName is the name of the plug-in. The suffix can be omitted

7.  void MainDlg::LoadAndAction(QString strPluginName)  

8.  {  

// Load the plug-in DLL

10.     QLibrary lib(strPluginName);  

11.     if (lib.load())  

12.     {  

13. // Define prototypes for the two export functions in the plug-in

14.         typedef IAnimal* (*CreateAnimalFunction)();  

15.         typedef void (*ReleaseAnimalFunction)(IAnimal* animal);  

// Parse the export function

17.         CreateAnimalFunction createAnimal =   

18.                 (CreateAnimalFunction) lib.resolve(“CreateAnimal”);  

19.         ReleaseAnimalFunction releaseAnimal =   

20.                 (ReleaseAnimalFunction) lib.resolve(“ReleaseAnimal”);  

21.         if (createAnimal && releaseAnimal)  

22.         {  

// Create the Animal object

24.             IAnimal * animal = createAnimal();  

25.             if (animal)  

26.             {  

27. // Use the plug-in function

28.                 animal->Eat();  

29.                 animal->Sleep();  

30. // When the plug-in is used, delete the object

31.                 releaseAnimal(animal);  

32.}

33.}

34. // Uninstall the plug-in

35.         lib.unload();  

36.}

37.}

Generate the Test project and output test.exe. Exe, bildog. DLL, BilDog. DLL, BilPanda. DLL in the same directory. Note that bildog.dll or Bilpanda.dll relies on the base interface library bil.dll. If the system does not find bil.dll, it will not be able to load Bildog.dll or Bilpanda.dll, so put them in the same directory.

Four, some regrets

The wish of DLLS is good, as long as the interface is consistent, users can change modules at will. But if you don’t pay attention to the details, it’s easy to get bogged down in DLL Hell!

The main causes of DLL hell are as follows:

1. Poor version control (mainly interface version)

DLLS are shared, and if a program updates a shared DLL, other programs that also rely on the DLL may not work properly!

2. Binary Compatibility Issues (ABI)

Shared libraries and programs written by different compilers (or even different versions of the same compiler) may not work together even on the same platform.