Warning!
Using static appears in this article_ Cast and reinterpret_cast conversion between object pointer and function pointer is Microsoft extended syntax
The syntax itself is undefined
Please do not use it in multi system compatibility projects. It does not guarantee any compatibility, nor does it guarantee that it is legal in non MSVC compilers
To ensure universality, please use C style conversion
Actual process
1. Dynamically load dynamic library
LoadLibrary("xxxx.dll") // The macro function determines the function version according to the character format of the project definition LoadLibraryA("xxxx.dll")// Narrow character version LoadLibraryW("xxxx.dll")// Wide character version
This function returns a module handle
Type: HINSTANCE
The. int structure is a variable of type
You can use HMODULE instead
Or use auto
Example 1:
HINSTANCE MyModule{ LoadLibraryA("MyCode.dll") }; //If the module address in the object will not change, const can be added
Example 2:
HMODULE MyModule{ LoadLibraryA("MyCode.dll") }; //If the module address in the object will not change, const can be added
Example 3:
auto MyModule{ LoadLibraryA("MyCode.dll") }; //If the module address in the object will not change, const can be added
2. Get the address of the exported function
GetProcAddress(Module Handle , "Export function name")
This function returns the address of the exported function
Type: FARPROC
The naming of this type is a problem left over by history. The early computer hardware technology is poor and the resources are limited. The memory address is divided into near & far address
Storing remote addresses requires types that can express a wider range
You can use this type as in previous versions
This type is essentially a parameterless stdcall calling standard function pointer of integer type return value
3. Call by exporting the function address
When you know the address of the exported function is not enough, you also need to know the prototype of the function
Assume that the exported function prototype is as follows
int Add(int,int)
Calling principle:
(int(*)(int,int))(Export function address)(Parameter 1,Parameter 2);
Call to change the address in a legal way
The compiler will rely on this to generate assembly code
4. Pseudo code
Two function prototypes
int Add(int,int);
double Add(double,double);
When exporting overloaded functions, ensure that each overloaded function has a unique exported function name
The function name is the key to ensuring that you get the address of the exported function
For specific implementation method, please search: def module definition file
Example 1:
if (const auto MyModule{ LoadLibraryA("MyCode.dll") }) { void* IntegerAdd{ GetProcAddress(MyModule, "IntegerAdd") }; void* DoubleAdd{ GetProcAddress(MyModule,"DoubleAdd") }; std::cout << static_cast<int(*)(int, int)>(IntegerAdd)(500, 123); std::cout << std::endl; std::cout << static_cast<double(*)(double, double)>(DoubleAdd)(123.321, 123.321); FreeLibrary(MyModule); }
Example 2:
if (const auto MyModule{ LoadLibraryA("MyCode.dll") }) { std::cout << reinterpret_cast<int(*)(int, int)>(GetProcAddress(MyModule, "IntegerAdd"))(1, 2); std::cout << std::endl; std::cout << reinterpret_cast<double(*)(double, double)>(GetProcAddress(MyModule, "DoubleAdd"))(1., 2.); FreeLibrary(MyModule); }
Example 3:
if (const auto MyModule{ LoadLibraryA("MyCode.dll") }) { int(*IntegerAdd)(int, int) { reinterpret_cast<int(*)(int, int)>(GetProcAddress(MyModule, "IntegerAdd")) }; double(*DoubleAdd)(double, double) { reinterpret_cast<double(*)(double, double)>(GetProcAddress(MyModule, "DoubleAdd")) }; std::cout << IntegerAdd(1, 2); std::cout << std::endl; std::cout << DoubleAdd(1., 2.); FreeLibrary(MyModule); }
If the variable that stores the address of the exported function will not be re assigned, you can use the const modifier
If you don't want to write a long list of function pointer declarations, you can use auto
Example 4:
if (const auto MyModule{ LoadLibraryA("MyCode.dll") }) { const auto IntegerAdd {reinterpret_cast<int(*)(int, int)>(GetProcAddress(MyModule, "IntegerAdd")) }; const auto DoubleAdd { reinterpret_cast<double(*)(double, double)>(GetProcAddress(MyModule, "DoubleAdd")) }; std::cout << IntegerAdd(1, 2); std::cout << std::endl; std::cout << IntegerAdd(55, 2); std::cout << std::endl; std::cout << DoubleAdd(1., 2.); FreeLibrary(MyModule); }
Extra - about function calls
As long as you master the principle, there are many ways to call the dynamic library export function
The function name can be understood as the first address of the function in memory (first operation address of the whole function flow)
Like the subscript 0 of an array, it is its first address in memory space
Every time you write down xxx(); Such a function call statement
Can be understood as function address ();
The address is an unsigned integer type
A legitimate call to a function is the data you need to transfer to the function and the data you need to receive to the function
C/Cpp will not restrict your grammar, as long as you comply with the rules from the perspective of computer
You can even call functions like this, if your teachers or colleagues like it
#include <iostream> void I'm a function() { std::cout << "I'm sorry to meet you this way" << std::endl; } int main() { void* I'm the function address{ static_cast<void*>(&I'm a function) }; static_cast<void(*)()>(I'm the function address)(); // I'm now equivalent to a function and called return 0; }
This is completely legal 0error(s)0warning(s), which is very reasonable in the eyes of the compiler (computer)
Dynamic loading DLL principle
1. Load the DLL into the virtual memory space of the target process
2. Get the address of DLL export function
When you call a function, you will jump to the first address of the called function to execute the statement
The function exported by DLL is also a principle, so you need to know the first address of the function exported by DLL
For example, if the first address of function A exported by DLL is 0x10086, you should get this address
With this address, you can use this function