C Language Review: Compilation

C interface packaging and design topics

Dynamic Link Library (DLL) Programming Principle in Win32 Environment

 

Larger application programs are composed of many modules, these modules respectively complete relatively independent functions, and they cooperate with each other to complete the work of the entire software system. There may be some modules whose functions are more general and will still be used when constructing other software systems. When building a software system, if the source code of all modules is statically compiled into the entire application EXE file, some problems will arise: one disadvantage is that it increases the size of the application, it will take up more disk space, and the program runs It also consumes a large amount of memory space, resulting in a waste of system resources; another disadvantage is that when writing a large EXE program, all source codes must be adjusted and compiled each time it is modified and rebuilt, which increases the complexity of the compilation process. , and is also not conducive to staged unit testing.

  The Windows system platform provides a completely different and more efficient programming and running environment. You can create independent program modules as smaller DLL (Dynamic Linkable Library) files, and compile and test them separately. At runtime, only when the EXE program really wants to call these DLL modules, the system will load them into the memory space. This approach not only reduces EXE file size and memory space requirements, but also enables these DLL modules to be used by multiple applications at the same time. Microsoft Windows itself implements some major system functions in the form of DLL modules. For example, some basic functions in IE are realized by DLL files, which can be called and integrated by other applications.

   Generally speaking, a DLL is a disk file (usually with a DLL extension) that consists of global data, service functions, and resources, and is loaded into the virtual space of a process by the system at runtime and becomes part of the calling process. If there are no conflicts with other DLLs, the file is usually mapped to the same address in the process virtual space. The DLL module contains various exported functions for providing services to the outside world. Windows matches process function calls to the exported functions of the DLL file when loading the DLL module.

  In a Win32 environment, each process replicates its own read/write global variables. If you want to share memory with other processes, you must 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 and easier to write these days. Win32 has greatly simplified its programming model and has a lot of support from AppWizard and MFC class libraries.

  1. The matching

   DLL file for exporting and importing functions contains an export function table. These exported functions are linked to the outside world by their symbolic names and integers called identification numbers. 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 identification number of the function. The dynamic linking process dynamically builds a function call and function address correspondence table when the DLL module is loaded. If you recompile and rebuild the DLL file, you don't need to modify the application unless you change the symbolic names and parameter sequences of the exported functions.

  Simple DLL files only provide export functions for applications, while more complex DLL files not only provide export functions, but also call functions in other DLL files. In this way, a particular DLL can have both import and export functions. This is not a problem because the dynamic linking process can handle cross-correlation situations.

  In the DLL code, the exported functions must be explicitly declared like this:

__declspec(dllexport) int MyFunction(int n);


  but it is also possible to list the exported functions in the module definition (DEF) file, but doing so often causes more trouble . On the application side, the corresponding input functions are required to be explicitly declared as follows:

__declspec(dllimport) int MyFuncition(int n);

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

  Second, establish a link with the DLL module

  There are two ways to link an application's imported function with an exported function in a DLL file: implicit linking and explicit linking. The so-called implicit link means that the actual storage path of the DLL file does not need to be specified in the application program, and the programmer does not need to care about the actual loading of the DLL file. Explicit linking is the opposite.

   Using the implicit link method, when the programmer creates a DLL file, the linker will automatically generate a corresponding LIB import file. This file contains the symbolic name and optional identification number of each DLL exported function, but does not contain the actual code. LIB files are compiled into application projects as replacements for DLLs. When the programmer compiles and generates an application program through static linking, the calling functions in the application program match the exported symbols in the LIB file, and these symbols or identification numbers enter the generated EXE file. The LIB file also contains the corresponding DLL filename (but not the full pathname), which the linker stores inside the EXE file. When the application program needs to load the DLL file during the running process, Windows finds and loads the DLL according to the information, and then realizes the dynamic link to the DLL function through the symbol name or identification number.

  Explicit linking is more suitable for integrated development languages ​​such as VB. With explicit linking, the programmer no longer has to use the import file, but directly calls the Win32 LoadLibary function, specifying the path to the DLL as a parameter. LoadLibary returns the HINSTANCE parameter, which the application uses when calling the GetProcAddress function. The GetProcAddress function converts a symbolic name or identification number to an address inside the DLL. Suppose there is a DLL file that exports the following function:

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

  the following is an example of an application explicitly linking the exported function:
c====>Application

win/linux system programming

api
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 the implicit link mode, all DLL files called by the application will be in the application EXE The file is loaded into memory when it is loaded; but with explicit linking, the programmer can decide when the DLL file is loaded or not loaded. Explicit linking determines at runtime which DLL file to load. For example, one DLL module with string resources can be loaded in English and another in Spanish. The application program loads the corresponding DLL file after the user selects the appropriate language.

  3. Use symbolic name links and identification number links

   In the Win16 environment, symbolic name links are less efficient, so identification number links are the main linking method at that time. In the Win32 environment, the efficiency of symbolic linking has been improved. Microsoft now recommends using symbolic links. But the DLL version in the MFC library still uses the ID link. A typical MFC program may link to hundreds of MFC DLL functions. The EXE file body of an application linked with an identification number is relatively small because it does not have to contain long string symbolic names of imported functions.

 

Fourth, write the DllMain function

  DllMain function is the default entry point of the DLL module. This function is called when Windows loads the DLL module. The system first calls the constructor of the global object, and then calls 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 detached from the process (and other times). Below is an example of a 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
}

  If the programmer does not write a DLLMain function for the DLL module, the system will A default version of the DLLMain function that does nothing is introduced in other runtime libraries. The DLLMain function is also called when individual threads are started and terminated. As indicated by the dwReason parameter.

  5. Module handle

   Each DLL module in a process is identified by a globally unique 32-byte HINSTANCE handle. The process itself also has a HINSTANCE handle. All these module handles are only valid inside a specific process, they 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 these two types can be used interchangeably. The process module handle is almost always equal to 0x400000, while the default handle for the load address of a DLL module is 0x10000000. If a program uses several DLL modules at the same time, each will have a different HINSTANCE value. This is because a different base address was specified when the DLL file was created, or because the DLL code was relocated by the loader.
Module handles are especially important for loading resources. The Win32 FindResource function takes a HINSTANCE parameter. Both EXEs and DLLs have their own resources. If the application needs resources from the DLL, specify this parameter as the DLL's module handle. If you need resources contained in the EXE file, specify the module handle of the 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 the Null parameter; if you need the DLL module handle, call the Win32 function GetModuleHandle with the DLL file name parameter. 6. How the application finds the DLL file  If the application uses LoadLibrary to link explicitly, the full path to the DLL file can be specified in the parameters of this function. If you do not specify a path, or perform an implicit link, Windows will follow the following search order to locate the DLL:   1. The directory containing the EXE file,   2. 2. The current working directory of the process   . Windows system directory,   3. Windows directory,

  







  5. A sequence of directories listed in the Path environment variable.

   There's a pitfall that can easily go wrong here. If you use VC++ for project development, and create a project specifically for the DLL module, then copy the generated DLL file to the system directory, and call the DLL module from the application. So far so good. Next, make some changes to the DLL module and regenerate the new DLL file, but you forgot to copy the new DLL file to the system directory. The next time you run the application, it still loads the old version of the DLL file, so be careful! 7. Debugging DLL programs  Microsoft's VC++ is an effective tool for developing and testing DLLs, just run the debugger from the DLL project. When you do this for the first time, the debugger will ask you for the path to the EXE file. Every time the DLL is run in the debugger thereafter, the debugger automatically loads the EXE file. The EXE file then uses the above search sequence to find the DLL file, which means you have to set the Path environment variable to include the disk path of the DLL file, or you can copy the DLL file to the directory path in the search sequence.

  

 

 

How to release memory allocated by DLL in EXE

Summarize the following points:

1. Ensure the uniformity of memory allocation and cleanup: If a DLL provides a function that can allocate memory, then the DLL should also provide a function to release the memory. Data creation and cleanup should be at the same level.

I have encountered such an example: a piece of memory is allocated in the dll, and its address is passed to the application through PostMessage. Then the application releases it, and the result is always an exception.

2. If the exe is generated by MFC Appwizard and the dll is generated by win32, an error will occur during runtime. Further single-step tracking, it is found that the new operator in mfc mode and win32 mode is implemented in different ways, and the source program is in the files Afxmem.cpp and new.cpp in the VC directory respectively. If you are interested, you can follow it yourself.

Because after the dll outputs the function, it does not know which simulation calls it, so it is best to pair new and delete in one file, which can ensure consistency.

3. The main problem is that the heaps for memory allocation in the DLL and EXE main programs are different. You can use new and delete instead of new and delete.

1) ::HeapAlloc(::GetProcessHeap(),...)和::HeapFree(::GetProcessHeap(),...)

2) ::GlobalAlloc()和::GlobalFree()

These two pairs of API, so that whether in the DLL or in the main program are allocated in the process default heap, there will be no error.

4. Another way is to change the Use Run-time liberary of Code Generation in the C/C++ tab of the Settings of the dll to Debug Multithreaded DLL, and in the Release version to Multithreaded DLL, you can use new and delete directly. . But MFC can not use Shared mode.

    Excerpted from the Internet, invaded and deleted.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324859043&siteId=291194637