Why DLL
Let's take a look at the differences between the static library and the DLL.
Executable File generation (link phase): the former is very slow (because all symbol definitions in the library need to be linked to the EXE file ), the latter is very fast (because the latter is unsigned by the link's import and storage file)
The size of the executable file: the former is very large, and the latter is very small (the size of the DLL is similar to that of the former)
The running speed of executable files: the former is fast (search for symbols directly in the memory of the EXE module), and the latter is slow (you need to search in the memory of the DLL module, searching in the memory of another module is naturally slow)
Sharability: the former cannot be shared. That is to say, if two EXE files use the same static library, there are actually two copies of the library in the memory, while the latter can be shared.
Upgradeability: the former cannot be upgraded (because the static library symbol has been compiled into the EXE, And the EXE needs to be re-compiled to be upgraded). The latter can be upgraded (as long as the interface remains unchanged, dll can be upgraded to different implementations)
Based on the above, select static library or DLL
1. static libraries are suitable for stable code, while dynamic libraries are suitable for regular code changes (of course, the interface must remain unchanged). After DLL changes (only partial implementation), you do not need to re-compile the project, you only need to use the new DLL.
2. Because the static library is very fond of generating executable files (link period), if the link time of the executable files is sensitive, use DLL.
Use DLL
Before introducing how to create a DLL, let's first understand how it is used.
1. explicit call (also called dynamic call)
Explicitly call the API function loadlibrary or the afxloadlibrary provided by MFC to load the DLL to the memory, and then use getprocaddress () to obtain the introduced function address in the memory, then you can call this function just like using a local function. Before exiting the application, use the afxloadlibrary provided by freelibrary or MFC to release the DLL.
The following is an example of a display call. Assume that you already have a test. dll, and the name of a function in the DLL is test. Its declaration is void ();
# Include <iostream>
Using namespace STD;
Typedef void (* test )();
Int main (char argc, char * argv []) {
Const char * dllname = "test. dll ";
Const char * funcname = "test ";
Hmodule hdll = loadlibrary (dllname );
If (hdll! = NULL ){
Test func = test (getprocaddress (hdll, funcname ));
If (func! = NULL ){
Func ();
}
Else {
Cout <"unable to find function/'" <funcname <"/'! "<Endl;
}
Freelibrary (hdll );
}
Else {
Cout <"unable to load dll/'" <dllname <"/'! "<Endl;
}
Return 0;
}
Note:
1. Display call using getprocaddress, so only functions can be loaded, and variables and classes cannot be loaded.
2. you can't find test directly. In this case, you must change the function name to the correct modified name, the following is a possibility (the call convention of this function in DLL is _ cdecl ):
Const char * funcname = "? Test @ yaxxz ";
2. implicit call (also called static call)
For implicit calling, you must provide the DLL header file and import to the database (which can be considered as a lightweight static library (without the definition of a symbol, but indicates the DLL in which the symbol is located )).
With header files and import files into the database, the use of DLL is no different from that of common static libraries, except that DLL and exe must be released together.
Show Advantages and Disadvantages of call and implicit call
The display call is complex, but the memory can be used more effectively, because the DLL is loaded at the Run Time of the EXE, the user must use loadlibrary and freelibrary to control the time when the DLL is loaded or detached from the memory. You can also load DLL functions written in other languages.
Static calling is easy to use, but does not control the DLL loading time. When the EXE is loaded to the memory, the DLL is automatically loaded to the memory. When the EXE exits, the DLL is also uninstalled.
Create DLL
The following describes how to create a DLL under VC.
First, create a Win32 DLL project. Transfer all the files in the common static library to the DLL project, and then:
Add the _ declspec (dllexport) keyword to all the class declarations so that the compiler can automatically generate imported files for you (otherwise you need to write the file yourself. def files are generated, so the details are not described here because they are not commonly used)
For DLL users, the class Declaration requires another keyword _ declspec (dllimport) (this keyword is not required for classes and functions, but it is required for variables ). So we usually define a macro for packaging, such
# Ifdef mydll_exports
# Define mydll_api _ declspec (dllexport)
# Else
# Define mydll_api _ declspec (dllimport)
# Endif
In this way, we can write the following class
Class mydll_api myclass {
...
};
Of course, you must include Preprocessor (preprocessing indicator) mydll_exports in the project where the DLL is created, and mydll_exports should not be included in the user project.
In fact, the VC mentioned above has already helped us. If the DLL project you created is named test, the project automatically contains test_exports. If you select exprot symbols when creating the project, VC will automatically create a sample file test. H, which defines
# Ifdef test_exports
# Define test_api _ declspec (dllexport)
# Else
# Define test_api _ declspec (dllimport)
# Endif
Your custom file should contain this file to use test_api. (If you do not select exprot symbols, you have to write this macro by yourself)
The example file also contains a class, variable, and global function writing method.
Class test_api ctest {
Public:
Ctest (void );
// Todo: add your methods here.
};
Extern test_api int ntest;
Test_api int fntest (void );
The preceding example shows the global (or namespace) variables and function declaration methods.
Detail Discussion
1. The DLL entry function dllmain is not required. Without it, the compiler will automatically generate a dllmain that does not do anything.
2. If it is something that can be defined in the header file: including macros, constants, inline functions (including member inline functions) and templates. Therefore, test_api does not need to be included in the definition of DLL, which is no different from normal definition.
If a class is completely composed of inline functions and pure virtual functions, you do not need keywords such as test_api. The same is true for a struct without a member function.
3. All Classes exported without _ declspec (dllexport) can only be used as internal DLL classes. Of course, the external can still use its inline member function (provided that the member function should not call any unexported function or class member function)
Release DLL
1. the author of the library should publish the three sets of headers, including imported files and DLL files to users. The header files and imported files are designed for users who call static files, that is, the C/C ++ user. (In addition, if the header file used in some DLL is not directly # include by the interface header file, the header file does not need to be published, which is the same as the static library ).
2. dll users only need to publish the DLL and executable programs together.
C ++ programs use C language DLL (the same as static Library Rules)
C does not have a class, so the DLL created by C will not have a class. The use of C and C ++ for global variables is no different, so we focus on global functions (In addition, the rules of global variables are the same ).
We know that C ++ programs must add the extern "C" keyword before using C-language functions, which is no different for static libraries and DLL. However, this keyword cannot appear directly in the header file function declaration. Otherwise, the DLL cannot be compiled. The reason is very simple. The C language does not have the extern "C" keyword.
1. One approach is to assign the responsibility for C migration to C ++ to the DLL creator. Define a macro so that the DLL (C Project) does not show extern "C" or is only extern, but remains unchanged in the user Project (C ++ project. Fortunately, Windows has already designed everything for us. This macro is extern_c (which exists in winnt. h ):
# Ifdef _ cplusplus
# Define extern_c extern "C"
# Else
# Define extern_c extern
# Endif
Note that the above must be extern rather than null. Although the function declaration of C does not have to add extern, the variable declaration must add extern.
With extern_c, define the function in the header file as follows:
Extern_c test_api int fntest (void );
2. Another method is to assign the responsibility for C migration to C ++ to the user. When the user includes the DLL header file, use extern "c" to include:
Extern "C "{
# Include "mydll. H"
# Include "mydllcore. H"
...
}
You can put the above extern "C" segment in the new header file and release it together with the DLL, so that the C ++ user only needs to include this header file. For example, the Lua library comes with an ETC/Lua. HPP file. This file is usually provided by the DLL author, so the responsibility for migration is still the responsibility of the DLL creator.
Note that you should not try to re-declare the function with extern "C", because repeated declarations are allowed, but must be the same as the declarations in the header file.
3. One transformation of this approach is to include all function and variable declarations in the header file "extern" C "directly, so that you do not need to provide an additional header file as above. Normally like this (mydll header file ):
# Include header file segment
# Ifdef _ cplusplus
Extern "C "{
# Endif
Function and variable Declaration
...
# Ifdef _ cplusplus
}
# Endif
Create a standard DLL for use in other languages
We usually want the DLL to be used by other languages, so our DLL often does not provide class definitions, but only function definitions. (Because many programming languages do not support classes ).
In this case, the function call Convention must be _ stdcall (_ cdecl by default in VC, so you have to add it manually), because most languages do not support _ cdecl, but it supports _ stdcall (such as VBScript and Delphi ).
In addition, we hope that the function names exported to the DLL can be more easily identified (it is easier for users to use). That is to say, the DLL should compile a C function name without modification.
So we may
1. if you only use the C language, you must create a DLL in the C file (automatically compile the C symbol name). Considering the potential C ++ users (such users use the DLL in static call mode, so we need to see its function declaration). We also need to use the extern_c keyword (see the above discussion ).
2. If you use C ++, you must create a DLL using a C ++ file. In this case, you should directly modify the DLL with extern "C.
Conclusion
So to create a DLL that can be used for any programming language, we should
1. Only create functions
2. Declaration _ stdcall call Convention (or winapi, callback, provided that you must include Windows header files)
3. Use the CPP file + extern "C" or C file + extern_c
4. Of course, you also need to declare the dll_api (if dllexport is available, the DLL can only be displayed and called ).
More perfect
Instead of using dllexport and extern "C", use the. Def file. Although the C function name in our dll has been quite concise after the above four steps, it is still not perfect: for example, a function declared as a function, the actual DLL name may be function @#. Using the. Def file, we can make the function names in the DLL consistent with the declared function names.
For detailed usage of. Def, refer:
Microsoft DLL
Export from DLL using def File
Switch between DLL and static library
As I mentioned earlier, the user _ declspec (dllimport) keyword for using DLL is dispensable, provided that the DLL does not include variable definitions.
Therefore, to make the library both DLL and static library, we should make a definition similar to the following:
1. dll does not include variable definition
# Ifdef test_exports
# Define test_api _ declspec (dllexport)
# Else
# Define test_api
# Endif
Then, you only need to put the configuration type of the project in the static library (. lib) or dynamic library (. DLL) Switch (VC automatically adds the pre-processor test_exports for the DLL, and cancels test_exports for the static library ).
2. dll contains variable definitions, which is also a standard practice
# Ifdef test_build_as_dll
# Ifdef test_exports
# Define test_api _ declspec (dllexport)
# Else
# Define test_api _ declspec (dllimport)
# Endif
# Else
# Define test_api
# Endif
If you want to make the library into a DLL, you need the DLL creator to add the pre-processor test_build_as_dll and test_exports, which are usually automatically added by the compiler. If the library is made into a static library, no pre-processor needs to be added.
You can use dynamic or static libraries by adding or canceling test_build_as_dll.
For DLL users, the name test_build_as_dll does not seem to be good. The following practices may be more reasonable:
# If defined (test_api)
# Error "macro alreday defined! "
# Endif
# If defined (test_build_dll) & defined (test_use_dll)
# Error "macro conflict! "
# Endif
# If defined (test_build_dll) // build DLL
# Define test_api _ declspec (dllexport)
# Elif defined (test_use_dll) // use DLL
# Define test_api _ declspec (dllimport)
# Else // build or use library, no Preprocessor needs
# Define test_api
# Endif