Real life projects are large in size and may consist of several sub-projects. It is essential that an IDE allows debugging of large apps spanning across several projects. With Code::Blocks we can do it easily.
To learn multiple app debugging we'll create two projects—first project a DLL project and second one is a console project that depends upon first DLL project. Then save both projects under same workspace named App8
.
Go to File | New | Project | Dynamic Link Library menu option to create a DLL project. Name this project libobject
. Now rename the libobject
project files. We'll rename main.h
file to dllmain.h
and main.cpp
to dllmain.cpp
file. To do this, close all open editor files and right-click on the file name in the project tree as shown in the following screenshot:
Enter new file name in the dialog box shown in following screenshot:
This will avoid ambiguities in file names. Now replace code inside dllmain.h
file with the following code.
#ifndef __DLLMAIN_H__ #define __DLLMAIN_H__ /* To use this exported function of dll, include this header * in your project. */ #ifdef BUILD_DLL #define DLL_IMP_EXPORT __declspec(dllexport) #else #define DLL_IMP_EXPORT __declspec(dllimport) #endif #ifdef __cplusplus extern "C" { #endif void DLL_IMP_EXPORT SayHello(void); #ifdef __cplusplus } #endif class base { public: void Set(int width, int height) { m_width = width; m_height = height; } virtual int Area() = 0; protected: int m_width, m_height; }; class DLL_IMP_EXPORT Rectangle : public base { public: int Area(); }; class DLL_IMP_EXPORT Triangle : public base { public: int Area(); }; #endif // __DLLMAIN_H__
A DLL on Windows require special decoration in order to export it from a dynamic link library. This decoration statement changes while it is exported and at the time it is imported. Decoration __declspec(dllexport)
is used to export functions from a DLL and __declspec(dllimport)
is used to import function from another DLL. Decorations instruct linker to export or import a variable/function/object name with or without name mangling. A preprocessor define DLL_IMP_EXPORT
is used to indicate compiler whether a function or a class is being exported or imported.
C++ allows function/method overloading. It is achieved by introducing name mangling in the generated code. Name mangling is a process in which a function name is converted to a unique name based on function parameters, return type, and other parameters. Name mangling is compiler dependent and as a result any DLL written is C++ can't be used directly with another compiler.
C++ introduces name mangling by default for all functions. We can stop name mangling using extern "C"
keyword and are using it to stop name mangling for the exported SayHello()
function. By stopping name mangling we can use a DLL written in C++ and compiled with one compiler to be used with another compiler.
We have defined a class base
and this base
class has a member function Set()
and it sets two internal variables. There is a pure virtual function named Area()
that must be redefined derived classes. A pure virtual function is a function that has not been implemented in the base class. If a pure virtual function is called in any app it may result in a crash.
However, this base
class is not decorated with DLL_IMP_EXPORT
. This means it will not be exported in DLL and no outside app can use this class.
In order to use feature of the base
class we'll create two derived classes. Class Rectangle
and Triangle
, these are derived publicly from the base
class. We have used inheritance of classes here. These classes are declared with decoration DLL_IMP_EXPORT
. Thus these two classes will be exported in the resulting DLL.
Now replace code inside the dllmain.cpp
file of the libobject
project with the following code:
#include <windows.h> #include <iostream> #include "dllmain.h" void SayHello(void) { std::cout << "Hello World!" << std::endl; } int Rectangle::Area() { return (m_width * m_height); } int Triangle::Area() { return (m_width * m_height / 2); } extern "C" DLL_IMP_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: // attach to process // return FALSE to fail DLL load break; case DLL_PROCESS_DETACH: // detach from process break; case DLL_THREAD_ATTACH: // attach to thread break; case DLL_THREAD_DETACH: // detach from thread break; } return TRUE; // successful }
Code in the dllmain.cpp
file mainly defines all the code of publicly exported function. There is a DllMain()
function. It may be used to do any initialization or de-initialization for the DLL.
Next create a console app named App8
. Now rename workspace as App8
and save workspace as App8
. This console app will use functions defined in libobject.dll
. Replace code inside the main.cpp
file of App8
with the following code:
#include <iostream> #include "dllmain.h" int main() { Rectangle rect; rect.Set(10, 20); Triangle trigl; trigl.Set(5, 6); std::cout << "Rectangle(10, 20).Area() = " << rect.Area() << std::endl; std::cout << "Triangle(5, 6).Area() = " << trigl.Area() << std::endl; return 0; }
Next, we have to prepare our App8
project to use this DLL. To do so go to Project | Build options menu option. Select App8
in the project tree and then click on Search directories tab. Then add ..libobject
directory to the list in the Compiler tab. This instructs compiler to search for header files in that directory:
We also need to point linker to the directory where we have kept import library of libobject.dll
file. To do so select the Debug target and click on the Search directories tab. Then click on the Linker tab and add ..libobjectinDebug
folder to the list:
We have to instruct linker to find references of symbols found in libobject.dll
file. To do so click on the Linker settings tab and add libobject.a
to the Link libraries list.
We'll set up project dependencies in this step. Go to Project | Properties… menu option and then click on the Project dependencies… button. Click on the libobject
and then click on the Close button. Finally click OK button to close the Project/targets options window. This completes preparation of the App8
console app.
Now go to Build | Build workspace menu option. This will build the libobject
project first and subsequently App8
will be compiled.
In order to learn debugging multiple projects we'll set breakpoints at the following line number:
dllmain.cpp
file, libobject
projectmain.cpp
file, App8
projectBreakpoints can be verified from Breakpoints window shown in the following screenshot:
Note that DLLs can't run as a standalone process and require a host application to load them into memory. In order to debug a DLL we have to debug the host application that loads and runs it. Alternatively we can specify a host application (in our case App8.exe
) for debugging by navigating to Project | Set programs' arguments… menu option.
We'll use first approach and let our host app to load libobject.dll
, then use it to debug both libobject.dll
and App8.exe
file. Ensure that App8
project is activated in the project tree and then click on the debug/continue button in debugger toolbar:
In the preceding screenshot execution has stopped at line number 19
of the dllmain.cpp
file. Whenever DllMain()
is exported it becomes the first function to be called during the loading/unloading of any DLL. As a result execution stops there.
Loaded libraries window in the following screenshot confirms that libobject.dll
has been loaded in memory and this library can be debugged:
Click on the Continue button to continue. Execution will now pause at line number 7
of the main.cpp
file.
Click on the Continue button twice. Execution will stop at line number 10
of the main.cpp
file as shown in the following screenshot:
Click on the Continue button again and execution will stop at line number 11
of dllmain.cpp
file.
Debugger is now debugging libobject
project's source file, which is a separate project. If cursor is hovered m_height
variable debugger will evaluate this variable and show its value.
It is evident that we can debug both DLL project and console app project at the same time. Larger projects can be debugged using a similar method. With this example we conclude our multiple app debugging session. Click on the Stop button to stop debugging.
3.15.221.133