This article shows you how to create a Win32 and MFC DLL to dynamically link a Library to your application. Microsoft Foundation Class (MFC) library can be used to create simplified DLLs. The MFC supports two types of DLLs, regular and extension:
Win32 DLL (non-MFC Library based) in general, only supports regular DLLs. You should use Win32 DLLs when your DLL is not using the MFC Library, Win32 is substantially more efficient.
Extension DLLs are for developing re-useable binary code and it is for advance usage (such as ATL and COM). DLLs are very useful, especially if you want to create programs that are modular. You can go to the Microsoft MSDN web page if you want to know more on DLLs.
This tutorial is in five parts. It gives a step by step procedure for developing a Win32 DLL and a regular MFC DLL object. There are three client applications, two Win32 console applications (one for Load Time linkage and the other illustrates Run Time linkage), the third DLL client application uses the shared MFC Library.
Regular DLLs execute in the same memory space as the DLL client application, you don't have to worry about marshaling data and pointers across process boundaries.
First, we are going to make the Win32 DLL core files for the project, W32DLL.xxx.
Next, we are going to make the DLL declaration/header file: DLLCode.h. Select New from the file menu, then select "C/C++ Header File" and name the file DLLCode. Click OK.
Copy and paste the following code excerpt:
/******************************************************* File name: DLLCode.h This file contains all the DLL interfacing object declarations, in this example: a class object, two global function object, and a global integer variable. Notice: we use the same header file for compiling the .DLL and the .exe (application). This header file defines a macro which export the target DLL objects if we are building a DLL, otherwise it import the DLL objects into an application which uses the DLL. If we define DLLDIR_EX (a preprocessor identifier), then the preprocessor define macro DLLDIR (a mnemonic for DLL import/export Direction) becomes an export instruction, otherwise its an import instruction by default. ************************************************************/ #ifdef DLLDIR_EX #define DLLDIR __declspec(dllexport) // export DLL information #else #define DLLDIR __declspec(dllimport) // import DLL information #endif // The extern "C" declaration allows mixed languages compactability, // it prevents the C++ compiler from using decorated (modified) // names for the functions extern "C" { void DLLDIR DLLfun1(char*); int DLLDIR DLLfun2(int); }; extern int DLLDIR DLLArg; class DLLDIR DLLclass { public: DLLclass(); // Class Constructor ~DLLclass(); // Class destructor int Add(int, int); // Class function Add int Sub(int, int); // Class function Subtract int Arg; // Warning: you should not // import class variables // since the DLL object can be dynamically unloaded. };
Save and close this header file. Now we are going to create the DLL implementation file, DLLCode.cpp.
Select New from the file menu, then select "C++ Source File" and name the file DLLCode. Then click OK.
Copy and paste the following code excerpt:
/********************************************************* File name: DLLCode.cpp The header file, DLLCode.h, prototypes all of the DLL interface objects **********************************************************/ #include "Stdafx.h" #include "DLLCode.h" #include <iostream> using namespace std; void DLLfun1(char* a) { cout << a << endl; }; int DLLfun2(int a) { return a<<1; }; int DLLArg = 100; DLLclass::DLLclass() {}; DLLclass::~DLLclass() {}; int DLLclass::Add(int a, int b) { return a + b; }; int DLLclass::Sub(int a, int b) { return a - b; };
We want to access the DLL, so we need to export it. When the DLL is built, it also builds something called an export library. The export library is similar to a regular library. It contains all of the information necessary to dynamically link to the DLL at runtime. When we export a function or a class, the function name and location is stored in the export library. The application uses the DLL links in the export library to access the DLL.
To create the DLL export library, select "setting..." from the Project menu. Select the C/C++ tab. Append, or insert, ",DLLDIR_EX" (without the quotation marks) to the Preprocessor Definition text box. Then click OK. This will prevent compiler assumptions and warnings.
Note, Visual C++ defines an export macro <projectname>_EXPORTS
, in our case, W32DLL_EXPORTS
. But we usedDLLDIR_EX
, a generic macro name.
Click the "!" button to compile, build, and run the W32DLL project.
Close the "Executable For Debug Session" dialog box, we ran the DLL prematurely.
Congratulation, you finished building the Win32 DLL and its export Library.
This is the simplest and most versatile DLL Client, without MFC. This flexibility is due to the DLL export library information.
Now, we are going to make a Win32 console application, project DLLClient1. In this application, the DLL is loaded at application startup, "Load Time" linkage.
Next, we are going to make the C/C++ source file: DLLClient1. Select New from the File menu, then select "C/C++ Source File". In the File Name box, give it a fitting name, say, DLLClient1, then press the Enter key.
Copy and paste the following code excerpt:
/*********************************************************** File name: DLLClient1.cpp ***********************************************************/ #include <iostream> #include <conio.h> #include <windows.h> #include "DLLCode.h" #pragma comment(lib,"W32DLL.lib") using namespace std; int main() { int a, b, c; DLLclass classFromDLL; classFromDLL.Arg = 6; a = classFromDLL.Add(3, 2); b = classFromDLL.Sub(3, 2); c = classFromDLL.Arg; cout << "DLL class Add function return: " << a << endl; cout << "DLL class Sub function return: " << b << endl; cout << "DLL class Arg Variable return: " << c << endl; getch(); a = DLLArg; b = DLLfun2(30); DLLfun1("this is the string pass to function DLLfun1"); cout << "\n\nDLL Variable DLLArg return: " << a << endl; cout << "DLL function DLLfun2 return: " << b << endl; getch(); return 0; }
Save and close this C++ source file, then minimize the VC++ Studio window. We must get some common files from theW32DLL project. So, Copy, don't move, the following files to the DLLClient1 project directory:
If you don't see the .DLL file in the Debug or Release folders, then select the View, or Tool (depending on the operating system) menu option, then select Folder Options. Next click on the View tab. Select option: Show all files. Then re-examine the Debug or Release folder.
Now maximize the VC++ Studio window, then click the "!" button to compile, build, and run the application.
If you have a DLL you use in many projects, then you can take advantage of the VC++ file architecture. Copy the DLL file into the system directory and the LIB file into the Visual C++ Lib directory. See Options on the Tools menu, and select the Directories tab, there are search directories for EXEs, SOURCE CODEs, and LIBs.
In this part, the DLL is loaded at application startup time. The Operating System searches the DLL in the following locations:
For simplicity, we just copy the common files to the target directory, DLLClient1.
This DLL Client is for DLLs which do not have an export library, hints, we can't import the DLL header file. Note, we are restricted to function calls.
Now, we are going to make a Win32 console application, project DLLClient2.
In this application, the DLL is loaded during execution of the application, "Run Time" DLL linkage.
Next, we are going to make the C/C++ source file DLLClient2.
Select New from the File menu, then select "C/C++ Source File". In the File Name box, give it a fitting name, say,DLLClient2, then press the Enter key.
Copy and paste the following code excerpt:
/************************************************************ File name: DLLClient2.cpp ************************************************************/ #include <conio.h> // Header file containing getch() prototype #include <iostream> #include <windows.h> using namespace std; typedef void (*MYFUN1)(char*); // pointer to: void function(char*) typedef int (*MYFUN2)(int); // pointer to: int function(int) int main() { MYFUN1 pfun1; MYFUN2 pfun2; HMODULE hMod; // handle to loaded library module BOOL bRes; // BOOL to check if DLL was successfully unloaded // returns a handle to the DLL, otherwise NULL hMod = LoadLibrary("W32DLL.DLL"); // returns the address of the DLL functions, otherwise NULL pfun1 = (MYFUN1) GetProcAddress(hMod, "DLLfun1"); pfun2 = (MYFUN2) GetProcAddress(hMod, "DLLfun2"); // (DLL function address) (function parameters) (pfun1)("this is the string pass to function DLLfun1"); // (DLL function address) (function parameters) int a = (pfun2) (30); cout << "DLL function DLLfun2 return: " << a << endl; cout << "Press a key to exit" << endl; getch(); /////////////////////////////////////////////////////// // This code will run if you compile the W32DLL project // with the W32DLL.def file to explicitly export DLLArg. int *i; i = (int*) GetProcAddress(hMod, "DLLArg"); if (i) { cout << "Variable DLLArg is: " << *i << endl; }; cout << "Press a key to exit" << endl; getch(); // returns nonzero if sucussful bRes = FreeLibrary(hMod); return 0; } // =========== Code snippet 4 ====================
Save and close this C++ source file, then minimize the VC++ studio window. Copy, don't move, the following file to the DLLClient2 project directory:
W32DLL\Debug\W32DLL.DLL
Now maximize the VC++ Studio window, then click the "!" button to compile, build, and run the application.
In this part, the DLL is loaded at application run time. The LoadLibrary
function is used to load a DLL at run time. Being that we dynamically loaded the library, we should free this resource when we are finished with it.
This subsection was added as a result to user Hing's comment.
The code in "Code snippet 4" was modified to add code for accessing the global variable DLLArg
. Note, in the application you just ran, DLLCient2, function call GetProcAddress(hMod, "DLLArg")
returned a NULL
pointer. To add the global variable name to the DLL object, you must add a new text file to the W32DLL project. Go to New on the File menu. Select "Text File" on the File tag. Name the file: "W32DLL.def", the "def" file extension is important. Click OK.
Add the following text:
;File: W32DLL.def (explicitly export the global object names.) LIBRARY W32DLL.dll EXPORTS DLLfun1 DLLfun2 DLLArg
Save and recompile the W32DLL project. Copy the new W32DLL.DLL file to the project DLLClient2 directory. Run theDLLClient2 application.
Function GetProcAddress
should find variable "DLLArg
".
Note: You CANNOT export a class object to the DLL. The library object contains that information, along with the header file.
This time, we are going to make the MFC DLL core files for the project, RMFCDLL.xxx.
Next, we are going to make the DLL declaration header file: DLLCode.h. Select New from the File menu, then select "C/C++ Header File" and name the file DLLCode. Click OK.
Copy and paste the following code excerpt:
/****************************************************** File name: DLLCode.h This file contains MFC objects, hints, you should use a MFC Client. Notice: we use the same header file for compiling the .DLL and the .exe (application). This header file defines a macro which export the target DLL objects if we are building a DLL, otherwise it import the DLL objects into an application which uses the DLL. If we define DLLDIR_EX (a preprocessor identifier), then the preprocessor define macro DLLDIR (a mnemonic for: DLL import/export Direction) becomes an export instruction, otherwise its an import instruction by default. ******************************************************/ #ifdef DLLDIR_EX #define DLLDIR __declspec(dllexport) #else #define DLLDIR __declspec(dllimport) #endif extern "C" { // We delete the function DLLfun1 (as defined // in the W32DLL DLL) it writes to // a console, not a window, it isn't appropriate to // call it from a MFC Client. int DLLDIR DLLfun2(int); void DLLDIR DrawEllipse ( CRect, CDC* ); // a MFC function call }; extern int DLLDIR DLLArg; class DLLDIR DLLclass { public: DLLclass(); // Class Constructor ~DLLclass(); // Class destructor int Add(int, int); // Class function Add int Sub(int, int); // Class function Subtract int Arg; };
Save and close this header file. Now we are going to create the DLLCode.cpp file. Select New from the File menu, then select "C++ Source File". Name the file: DLLCode. Click OK. Copy and paste the following code excerpt:
/************************************************************ File name: DLLCode.cpp The header file, DLLCode.h, prototypes all of the DLL interface objects *************************************************************/ #include "StdAfx.h" #include "DLLCode.h" int DLLfun2(int a) { return a<<1; }; int DLLArg = 100; DLLclass::DLLclass() {}; DLLclass::~DLLclass() {}; int DLLclass::Add(int a, int b) { return a + b; }; int DLLclass::Sub(int a, int b) { return a - b; }; void DrawEllipse ( CRect rect, CDC *pDC ) { CBrush brush; brush.CreateSolidBrush(RGB(0,0,255)); pDC->SelectObject(&brush); pDC->Ellipse(&rect); };
Now, select "setting..." from the Project menu. Select the C/C++ tab. Append, or insert, ",DLLDIR_EX" (without the quotation marks) to the Preprocessor Definition text box. Then click OK.
Click the "!" button to compile, build, and run the RDLLMFC project. Close the "Executable For Debug Session" dialog box, we ran the DLL prematurely. Congratulations, you finished building the Win32 DLL and its export Library.
This client application is a MFC application. It provides the framework, a window, for the MFC CBrush
object used in the DLL function DrawEllipse
.
Now, we are going to make the MFC application project MFCAp.
Select "ClassWizard..." from the View menu. The Message Maps tab should be active. In the "Class name:" dropdown box, select "CMFCApView". In the "Message functions:" list box, double click the "OnDraw
" function label.
You should see the View class OnDraw
function code. Replace that stub code with the following code excerpt, use copy and paste:
/////////////////////////////////////////////// // CMFCApView drawing void CMFCApView::OnDraw(CDC* pDC) { // DLL MFC Library function call CRect rect; rect.top=10; rect.left=10; rect.right=200; rect.bottom=200; DrawEllipse(rect,pDC); // DLL class object call int a, b, c; CString str; DLLclass classFromDLL; classFromDLL.Arg = 6; a = classFromDLL.Add(3, 2); b = classFromDLL.Sub(3, 2); c = classFromDLL.Arg; // Display data in window int y = 250, dy; TEXTMETRIC tm; pDC->GetTextMetrics(&tm); dy = tm.tmHeight + tm.tmExternalLeading; str.Format("DLL class Add function return: %d", a); pDC->TextOut(20, y, str); y += dy; str.Format("DLL class Sub function return: %d", b); pDC->TextOut(20, y, str); y += dy; str.Format("DLL class Arg Variable return: %d", c); pDC->TextOut(20, y, str); y += dy; a = DLLArg; b = DLLfun2(30); str.Format("DLL class Arg Variable return: %d", a); pDC->TextOut(20, y, str); y += dy; str.Format("DLL function \"DLLfun2\" return: %d", b); pDC->TextOut(20, y, str); }
At the top of the file MCFApView.cpp, and somewhere after the line containing: #include "stdafx.h"
, insert the following line:
#include "DLLCode.h"
Save and close the MFCApView.cpp file. Select "Setting..." from the Project menu. Select the "Link" tab. In the "Object/Library modules:" text box, enter: "RDLLMFC.lib", without the quotation marks. Then click OK.
Now, minimize the VC++ Studio window. Copy the following files to the MFCAp project directory:
Now maximize the VC++ Studio window, then click the "!" button.
A note in closing, you shouldn't change the DLL interface, especially class objects which are exported therein, because the v-table and class size are fitted at compile time. If you change the DLL and other programs are using it, you should rename the new version; or alternatively, you should re-compile all programs based on that DLL with the new interface.
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here
King Coffee United States Member |