VC program in Win32 environment dynamic link library of DLL programming principle

  • 2020-04-02 02:54:45
  • OfStack

This article describes the VC program in Win32 environment dynamic link library (DLL) programming principle. Share with you for your reference. Specific analysis is as follows:

Typically, large applications are made up of modules that perform relatively independent functions and collaborate with each other to complete the work of the entire software system. Some of these modules may be more versatile and still be used when building other software systems. In the construction of software system, if the source code of all modules are statically compiled into the entire application EXE file, there will be some problems: one disadvantage is to increase the size of the application, it will take up more disk space, the program will also consume more memory space, resulting in a waste of system resources; Another disadvantage is that when you write a large EXE program, you have to adjust the compilation of all the source code every time you modify and rebuild, which increases the complexity of the compilation process and is not conducive to staged unit testing.

Windows provides a completely different and more efficient programming and runtime environment, where you can create separate program modules into smaller DLL(Dynamic Linkable Library) files and compile and test them separately. At runtime, these DLL modules are loaded into the memory space only if the EXE program actually calls them. This approach not only reduces the size of EXE files and memory requirements, but also enables these DLL modules to be used by multiple applications simultaneously. Microsoft Windows itself will be some of the main system functions in the form of DLL modules. For example, some basic functions in IE are implemented by DLL files, which can be called and integrated by other applications.

Typically, a DLL is a disk file (usually with a DLL extension) that consists of global data, service functions, and resources that are loaded by the system at run time into the virtual space of a process and become part of the calling process. If there is no conflict with other DLLS, the file is typically mapped to the same address in the process's virtual space. The DLL module contains various export functions that are used to provide services to the outside world. Windows matches process function calls to DLL file export functions when loading DLL modules.

In a Win32 environment, each process copies its own read/write global variable. If you want to share memory with other processes, you must either use a memory-mapped file or declare a Shared data segment. The stack memory required by the DLL module is allocated from the stack of the running process.
DLLS are getting easier to write. Win32 has greatly simplified its programming model and has a lot of support from AppWizard and MFC class libraries.

The matching of the export and import functions

The DLL file contains a table of exported functions. These derived functions are associated with the outside world by their symbolic names and integers called identifiers. The function table also contains the addresses of the functions in the DLL. When an application loads a DLL module, it does not know the actual address of the calling function, but it does know the symbolic name and identifier of the function. The dynamic linking process dynamically creates a table of function calls and function addresses when the DLL module is loaded. If you recompile and rebuild DLL files, you do not need to modify the application unless you change the symbol name and parameter sequence of the exported function.
Simple DLL files only provide export functions for applications, more complex DLL files in addition to providing export functions, but also call other DLL functions. This way, a special DLL can have both an import function and an import function. This is not a problem because dynamic linking processes can handle cross-correlation situations.
In the DLL code, the export function must be explicitly declared as follows:

__declspec(dllexport) int MyFunction(int n);

It is also possible to list the exported functions in the module definition (DEF) file, but this often causes more trouble. On the application side, it is required to explicitly declare the corresponding input function as follows:
__declspec(dllimport) int MyFuncition(int n);

Import and export declarations alone do not link function calls within the application to corresponding DLL files. The application's project must specify the required input library (LIB file) for the linker. And the application must in fact contain at least one call to a DLL function.

Ii. Link with DLL module

There are two ways in which an application import function can be linked to an export function in a DLL file: implicitly and explicitly. An implicit link is an application that does not specify the actual DLL storage path, and the programmer does not care about the actual loading of the DLL. Explicit linking is the opposite.
Using implicit linking, programmers in the creation of a DLL, the link program will automatically generate a corresponding LIB import file. This file contains the symbolic name and optional identifier of each DLL exported function, but does not contain the actual code. The LIB file is compiled into the application project as an alternative to the DLL. When the programmer compiles the generated application statically, the calling function in the application matches the exported symbols in the LIB file, which go into the generated EXE file. The LIB file also contains the corresponding DLL file name (but not the full pathname), which the link program stores inside the EXE file. When an application needs to load a DLL file while running, Windows USES this information to discover and load the DLL, and then dynamically links the DLL functions by symbolic name or id number.
Explicit linking is suitable for integrated development languages such as VB. With explicit linking, programmers no longer need to use the import file, but instead call Win32's LoadLibary function directly and specify the DLL's path as an argument. LoadLibary returns the HINSTANCE parameter that the application USES when it calls the GetProcAddress function. The GetProcAddress function converts the symbol name or identity number to the address inside the DLL. Suppose you have a DLL file that exports the following functions:

extern "C" __declspec(dllexport) double SquareRoot(double d);

Here is an example of an application explicitly linking to the exported function:
typedef double(SQRTPROC)(double);
HINSTANCE hInstance;
SQRTPROC* pFunction;
VERIFY(hInstance=::LoadLibrary("c://winnt//system32//mydll.dll"));
VERIFY(pFunction=(SQRTPROC*)::GetProcAddress(hInstance,"SquareRoot"));
double d=(*pFunction)(81.0);// Call the DLL function

In implicit linking, all DLL files called by the application are loaded into memory when the EXE file is loaded. But with explicit linking, the programmer can decide when the DLL files are loaded or not. The explicit link determines which DLL file to load at run time. For example, you can load one DLL module with string resources in English and another in Spanish. The application loads the corresponding DLL file after the user has selected the appropriate language.

Use symbolic name link and identification number link

In the Win16 environment, symbolic name link is less efficient, all then the identification link is the main way to link. In the Win32 environment, the efficiency of symbolic name linking is improved. Microsoft now recommends symbolic name links. But the DLL version in the MFC library still USES the id link. A typical MFC program might link to hundreds of MFC DLL functions. The body of the EXE file for an application linked with an identity number is relatively small because it does not have to contain the long string symbolic name of the imported function.

Write the DllMain function

The DllMain function is the default entry point for a DLL module. This function is called when Windows loads a DLL module. The system first calls the constructor of the global object, then the global function DLLMain. The DLLMain function is called not only when the DLL link is loaded into the process, but also when the DLL module is separated from the process (and other times). Here is an example of the framework DLLMain function.

HINSTANCE g_hInstance;
extern "C" int APIENTRY DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
{
if(dwReason==DLL_PROCESS_ATTACH)
{
TRACE0("EX22A.DLL Initializing!/n");
//Initialize
here }
else if(dwReason=DLL_PROCESS_DETACH)
{
TRACE0("EX22A.DLL Terminating!/n");
//Clean up here
}
return 1;//Success < br / > }

If the programmer does not write a DLLMain function for the DLL module, the system introduces a default version of the DLLMain function that does nothing from other runtimes. The DLLMain function is also called when a single thread starts and terminates. As indicated by the dwReason parameter.

Module handle

Each DLL module in the process is identified by a globally unique 32-byte HINSTANCE handle. The process itself also has an HINSTANCE handle. All of these module handles are valid only within a particular process and represent the starting address of the DLL or EXE module in the process virtual space. In Win32, the values of HINSTANCE and HMODULE are the same, and the two types can be used interchangeably. The process module handle is almost always equal to 0x400000, while the default handle for the DLL module's load address is 0x10000000. If the program USES several DLL modules at once, each will have a different HINSTANCE value. This is because a different base address is specified when the DLL file is created, or because the loader relocates the DLL code.
Module handles are especially important for loading resources. Win32's FindResource function takes an HINSTANCE parameter. Both EXE and DLL have their own resources. If the application needs a resource from a DLL, specify this parameter as the DLL's module handle. If you need the resources contained in the EXE file, specify the module handle for EXE.
But there is a problem before using these handles, how do you get them? If you need to get the EXE module handle, call the Win32 function GetModuleHandle with a Null argument. If you need a DLL module handle, you call the Win32 function GetModuleHandle, which takes the DLL file name as an argument.

How do applications find DLL files

If your application USES LoadLibrary explicit linking, you can specify the full path to the DLL file in the arguments to this function. If you do not specify a path, or if you implicitly link, Windows will follow the following search order to locate the DLL:

1. Directory containing EXE files,
2. The current working directory of the process,
3. Windows system directory,
4. Windows directory,
5. A list of directories listed in the Path environment variable.

There is a trap that can easily go wrong. If you use VC++ for project development and create a special project for the DLL module, then the generated DLL file will be copied to the system directory, from the application to call the DLL module. So far, so good. Next, I made some changes to the DLL module and generated a new DLL file, but you forgot to copy the new DLL file to the system directory. The next time you run an application, it still loads an older version of the DLL, be careful!

Debugging DLL program

Microsoft's VC++ is an effective tool for developing and testing DLLS by simply running a debugger from a DLL project. When you do this for the first time, the debugger will ask you for the path to the EXE file. The debugger then loads the EXE file automatically every time the DLL is run in the debugger. The EXE file then finds the DLL using the search sequence above, which means that you must set the Path environment variable to include the disk Path of the DLL, or you can copy the DLL to the directory Path in the search sequence.

Hope that this article described the VC programming for you to help.


Related articles: