I have read a lot of DLL programming books, but I haven't written them in actual work. the compiling of the DLL has always been in the unknown state. If you have no time in the past two days, send a blog summary!
If you are good at compiling and linking through the command line, you can refer to this blog post ).
Http://www.blogjava.net/wxb_nudt/archive/2007/09/11/144371.html
Source code link from original blog ).
Http://www.blogjava.net/Files/wxb_nudt/DLL_SRC.rar
If you are still using the IDE, just like me, then let's take a look at the following content, which is basically the same as the content in the above blog post. It's just a part of using the command line, I changed it to VS2010.
The beginning is to thank the original author!
The simplest dll
Create an empty project with VS2010.
The simplest dll is not more difficult than c's helloworld, as long as it is a DllMain function, including a header file of objbase. h header file that supports COM technology ). If you think the header file name is hard to remember, you can use windows. H. Add a file main. cpp to the project. The content is as follows:
#include <objbase.h>
#include <iostream>
using namespace std;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
{
HANDLE g_hModule;
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
cout<<"Dll is attached!"<<endl;
g_hModule = (HINSTANCE)hModule;
break;
case DLL_PROCESS_DETACH:
cout<<"Dll is detached!"<<endl;
g_hModule=NULL;
break;
}
return true;
}
Then select generate solution. Is the error fatal error LNK1561 reported? The entry point must be defined?
This is because the IDE generates the exe file by default. This file does not contain the main function. Of course, an error is returned.
Make the following settings.
Project-> properties-> Configuration properties-> General-> default project-> dynamic library (. dll) for configuration type selection ).
After the solution is generated again, will it succeed this time? Find the dll in the project directory.
DllMain is the entry function of each dll, just like the main function of c. DllMain has three parameters. hModule indicates that the instance handle of this dll does not understand it, but does not understand it if it has been written to a windows program.) dwReason indicates the current status of the dll, for example, DLL_PROCESS_ATTACH indicates that the dll has just been loaded into a process, and DLL_PROCESS_DETACH indicates that the dll has just been detached from a process. Of course, it also indicates the state of loading to and detaching from the thread, Which is omitted here. The last parameter is a retained parameter which is currently related to some dll states, but rarely used ).
From the above program, we can see that when the dll is loaded into a process, the dll prints "Dll is attached! "Statement; when the dll is detached from the process, print" Dll is detached! "Statement.
Load DLL explicit call)
There are two methods to use dll: explicit call and implicit call. Here we will first introduce explicit calls. Create an empty project and add the file main. cpp. The content is as follows:
#include <windows.h>
#include <iostream>
using namespace std;
int main(void)
{
//加载我们的dll
HINSTANCE hinst=::LoadLibrary("dll_nolib.dll");
if (NULL != hinst)
{
cout<<"dll loaded!"<<endl;
}
return 0;
}
Note: when calling the dll using the LoadLibrary function, its parameters are the dll path and name, and the return value is the dll handle.
Put the previously generated dll in the running directory and compile and run the program directly. The following result is displayed:
Dll is attached!
Dll loaded!
Dll is detached!
The above results indicate that the dll has been loaded by the client. However, in this way, only the dll can be loaded into the memory, and the functions in the dll cannot be found.
Many dll function viewers can be downloaded from the Internet. Open the previously generated dll in the dll function viewer and you can find that there are no functions in the current dll.
How to define output functions in dll
In general, there are two methods. One is to add a def definition file, in which the function to be output in the dll is defined; the second is to add the _ declspec (dllexport) keyword before the function to be output in the source code.
Def File
First, write a dll with an output function. The source code main. cpp is as follows:
#include <objbase.h>
#include <iostream>
using namespace std;
void FuncInDll (void)
{
cout<<"FuncInDll is called!"<<endl;
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
{
HANDLE g_hModule;
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
g_hModule = (HINSTANCE)hModule;
break;
case DLL_PROCESS_DETACH:
g_hModule=NULL;
break;
}
return TRUE;
}
The def file of this dll is as follows: dll_def.def
LIBRARY dll_def.dll
EXPORTS
FuncInDll @1 PRIVATE
You will find that the syntax of def is very simple. First, it is the LIBRARY keyword, specifying the dll name, and then the EXPORTS keyword, followed by all the function names or variable names to be output in the dll, then connect @ and the numbers numbered in turn from 1 to N), and finally the modifier (optional ).
Next, generate the dll file. Open the dll file in the dll function viewer and you can find that an export function is available.
Explicitly call functions in DLL
Write a client program for dll_def.dll: dll_def_client
#include <windows.h>
#include <iostream>
using namespace std;
int main(void)
{
//Define a function pointer
typedef void (* DLLWITHLIB )(void);
//Define a function pointer variable
DLLWITHLIB pfFuncInDll = NULL;
//Load our DLL
HINSTANCE hinst=::LoadLibrary("dll_def.dll");
if (NULL != hinst)
{
cout<<"dll loaded!"<<endl;
}
//Find funcindll function for DLL
pfFuncInDll = (DLLWITHLIB)GetProcAddress(hinst, "FuncInDll");
//Call function in DLL
if (NULL != pfFuncInDll)
{
(*pfFuncInDll)();
}
Return 0;
}
There are two points worth noting: the first is the definition and use of function pointers. If you don't understand them, you can look for this c ++ book. The second is the use of GetProcAddress, this API is used to find the function address in the dll. The first parameter is the DLL handle, that is, the handle returned by LoadLibrary, and the second parameter is the function name in the dll, that is, the function name seen in the dll function viewer. The function name here refers to the compiled function name, not necessarily the function name in the dll source code ).
Compile and link. After running, you can see:
This indicates that the client successfully calls the function FuncInDll In the dll.
_ Declspec (dllexport)
It is complicated to write def for each dll. Currently, def is rarely used, and _ declspec (dllexport) is used to define the dll output function in the source code.
Dll is written in the same way as above. Remove the def file and add the Declaration _ declspec (dllexport) before each function to be output. For example:
_ Declspec (dllexport) void FuncInDll (void)
Regenerate the dll file and view it in the viewer.
The name of the compiled function is? FuncInDll @ YAXXZ, not FuncInDll. This is because the c ++ compiler will change the function name based on the function overload considerations. When explicit calling is used, you must also use the changed function name, which is obviously troublesome for the customer. To avoid this problem, you can use the extern "C" command to command the c ++ compiler to name the function in the form of a c compiler. The modified function declaration is:
Extern "C" _ declspec (dllexport) void FuncInDll (void)
After the function is regenerated, the function name becomes normal again.
In this way, you only need to find the function named FuncInDll for explicit calling.
Extern "C"
Using the extern "C" keyword is actually equivalent to a compiler switch. It can compile a function in c ++ into a function name in C language. That is, keep the compiled function symbol name equal to the function name in the source code.
Call DLL implicitly
Explicit calling is very complex. Every time LoadLibrary is required, and each function must use GetProcAddress to obtain the function pointer. This is a problem for a large number of users who use dll functions. Implicit calling can use functions in dll like using c function libraries, which is very convenient and quick.
The following is an implicit call example: dll contains two files: dll_withlibAndH.cpp and dll_withlibAndH.h.
The Code is as follows: dll_withlibAndH.h
extern "C" __declspec(dllexport) void FuncInDll (void);
Dll_withlibAndH.cpp
#include <objbase.h>
#include <iostream>
using namespace std;
#Include "DLL" with libandh. H "/ / see? This is the header file we added
extern "C" __declspec(dllexport) void FuncInDll (void)
{
cout<<"FuncInDll is called!"<<endl;
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
{
HANDLE g_hModule;
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
g_hModule = (HINSTANCE)hModule;
Break;
case DLL_PROCESS_DETACH:
g_hModule=NULL;
Break;
}
Return TRUE;
}
Generate the dll.
When performing an implicit call, you need to introduce the header file on the client and specify the lib file dll corresponding to the dll as long as there is function output at the link, the link will generate a lib file with the same name as the dll) location and name. Then, call the functions in the dll just like the functions in the api function library, without the explicit LoadLibrary and GetProcAddress. It is the most convenient to use. The client code is as follows: dll_withlibAndH_client.cpp
#include "dll_withlibAndH.h"
#Pragma comment (LIB, "DLL" with libandh. Lib ") / / you can also use this sentence directly in project - > properties - > configuration properties - >
//Add the address of this Lib in linker - > Input - > Add dependency
int main(void)
{
Funcindll(); / / as long as this is the case, we can call the functions in DLL
Return 0;
}
Remember to put dll_withlibAndH.h, dll_withlibandh.dll, and dll_withlibAndH.h.lib in the project directory. Among them, the. h and. lib files are required during compilation and the. dll files are required during runtime.
_ Declspec (dllexport) and _ declspec (dllimport) pairs
The above implicit call method is good, but problems may occur when calling objects in DLL and overload functions. Because the output function is modified using the extern "C", the overload function is certainly a problem, because the C language does not support the heavy load because they will all be compiled into the same output symbol string ).
In fact, it is feasible not to use extern "C". At this time, the function will be compiled into a c ++ symbol string, such? FuncInDll @ YAXH @ Z ,? FuncInDll @ YAXXZ), which can be called correctly and implicitly when the client is also c ++.
Consider the following situation: If DLL1.CPP is the source, DLL2.CPP uses the function in DLL1, but DLL2 is also a DLL, and some functions must be output for Client. CPP. In DLL2, how can we declare all functions, including the functions introduced from DLL1 and the functions to be output. In this case, you need to use both _ declspec (dllexport) and _ declspec (dllimport. The former is used to modify the output functions in this dll, and the latter is used to modify the functions introduced from other dll.
All source codes include DLL1.H, DLL1.CPP, DLL2.H, DLL2.CPP, and Client. cpp. The source code can be found in the downloaded package. You can compile the link and run it.
It is worth noting that a encoding method is used in both DLL1 and DLL2. For details, see DLL2.H.
#ifdef DLL_DLL2_EXPORTS
#define DLL_DLL2_API __declspec(dllexport)
#else
#define DLL_DLL2_API __declspec(dllimport)
#endif
DLL_DLL2_API void FuncInDll2(void);
DLL_DLL2_API void FuncInDll2(int);
Define the macros DLL_DLL2_EXPORTS and DLL_DLL2_API in the header file in this way to ensure that the functions of the DLL end are modified with _ declspec (dllexport), while the functions of the client end are modified with _ declspec (dllimport.
The code generated by VC is also like this! It turns out that I copied it, hoho!
Global variables and objects in DLL
Solve the Problem of overload functions, so the global variables and objects in the dll are not a problem, but there is a little attention to the syntax. As shown in the source code: dll_object.h
#ifdef DLL_OBJECT_EXPORTS
#define DLL_OBJECT_API __declspec(dllexport)
#else
#define DLL_OBJECT_API __declspec(dllimport)
#endif
DLL_OBJECT_API void FuncInDll(void);
extern DLL_OBJECT_API int g_nDll;
class DLL_OBJECT_API CDll_Object
{
public:
CDll_Object(void);
void show(void);
// TODO: add your methods here.
};
The Cpp file dll_object.cpp is as follows:
#define DLL_OBJECT_EXPORTS
#include <objbase.h>
#include <iostream>
using namespace std;
#include "dll_object.h"
DLL_OBJECT_API void FuncInDll(void)
{
cout<<"FuncInDll is called!"<<endl;
}
DLL_OBJECT_API int g_nDll = 9;
CDll_Object::CDll_Object()
{
cout<<"ctor of CDll_Object"<<endl;
}
void CDll_Object::show()
{
cout<<"function show in class CDll_Object"<<endl;
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
{
HANDLE g_hModule;
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
g_hModule = (HINSTANCE)hModule;
break;
case DLL_PROCESS_DETACH:
g_hModule=NULL;
break;
}
return TRUE;
}
View the generated dll. You can see five symbols.
They represent CDll_Object, constructor, FuncInDll, global variable g_nDll, and member function show of the class respectively. The following is the client code: dll_object_client.cpp
#include "dll_object.h"
#include <iostream>
using namespace std;
#pragma comment(lib,"dll_object.lib")
int main(void)
{
cout<<"call dll"<<endl;
cout<<"call function in dll"<<endl;
Funcindll(); / / as long as this is the case, we can call the functions in DLL
cout<<"global var in dll g_nDll ="<<g_nDll<<endl;
cout<<"call member function of class CDll_Object in dll"<<endl;
CDll_Object obj;
Obj.show ();
Return 0;
}
Run this client and you will see:
We can see that the client successfully accesses the global variables in the dll, creates the C ++ object defined in the dll, and calls the member functions of the object.
Summary
Keep in mind that, in the end, DLL is a dynamic link technology corresponding to C language, which is convenient and quick to output C Functions and variables. In addition, various means are required to Output C ++ classes and functions, and there is no perfect solution, unless the client is also c ++.
Remember, only COM is a technology that corresponds to the C ++ language.
The following is a summary of each problem.
Explicit call and implicit call
When is explicit call used? When to use implicit call? In my opinion, it is reasonable to use explicit calls only when the client is not C/C ++. In this case, it cannot be called implicitly. For example, use VB to call the dll written in C ++. Vb I won't, so there is no example)
Def and _ declspec (dllexport)
In fact, def functions are equivalent to extern "C" _ declspec (dllexport), so it can only process C functions, rather than overload functions. The use of _ declspec (dllexport) and _ declspec (dllimport) can adapt to any situation. Therefore, _ declspec (dllexport) is a more advanced method. Therefore, the general view is that def files are not used. I also agree with this view.
Finally, I ended. Finally, I would like to thank the original blogger for making me happy to be a porter!
This article from the "Three Take Tiger" blog, please be sure to keep this source http://darhx.blog.51cto.com/7920146/1298950