Solution about GetProcAddress returning error code 127

Solution about GetProcAddress returning error code 127

Solution about GetProcAddress returning error code 127

Load the DLL library explicitly during runtime, when LoadLibrary can return the handle of the current DLL module, and when the address of the function obtained by GetProcAddress is empty. The error code obtained through GetLastError is 127. It is very likely that the function declaration in the dynamic link library project does not add extern "C".

solution

OnlyProvide solutions for dll projects written by yourself,
if this problem occurs when using a third-party dynamic link library,Not in this solution.
Usually when writing a dll project, the header file will have something similar to this

#ifndef PCH_H
#define PCH_H
#else
#endif //PCH_H

When we want to explicitly export our own functions for use in other projects, we need to use extern "C" to export our own functions.
Regarding why extern "C" is used, it seems that there is no standard in C++.Different compilers may produce different modifications.
With the addition of extren "C", there will be no additional modifications, so it is unified.
The header file should be written like this

#ifndef PCH_H
#define PCH_H_API extern "C" __declspec(dllexport)
#else
#define PCH_H_API __declspec(dllimport)
#endif //PCH_H

PCH_H_API void ExportFunc(LPCTSTR pszContent);//这里就是需要导出的函数,增加了extern "C"的修饰

Simple macros are used here, otherwise, it would be a bit much to write them all out.

extern "C" {
	__declspec(dllexport) void ExportFunc(LPCTSTR pszContent);
} //这个代码块等同于上面的PCH_H_API void ExportFunc(LPCTSTR pszContent)

Then test it in another project

int main()
{
	HMODULE hModule = ::LoadLibrary("..\\DllTest\\Debug\\DllTest.dll");
	if (hModule != NULL)
	{
		PFNEXPORTFUNC mExportFunc = (PFNEXPORTFUNC)::GetProcAddress(hModule, "ExportFunc");
		int n = GetLastError();
		if (mExportFunc != NULL)
		{
			mExportFunc("大家好");
		}

		::FreeLibrary(hModule);
	}
	return 0;
}

In this way, GetLastError() will not return 127, and the mExportFunc function in the dll can be called. The above are the main codes, and some other codes such as the code implemented by mExportFunc in Dll are not listed.

Use of extern keyword

C++ To be compatible with C, C++ can use the keyword extern "C" to declare or define a C symbol:

extern "C"{
    int func(int);
    int var;
}

C++ will treat the code within the curly brackets of extern "C" as C language code (.c file); that is, the
name modification mechanism of C++ in the curly brackets will not work. Different compilers have different name modification methods. :

1.如果是VC++平台,上述代码中的fun和var会被修饰(名称修饰)为_func和_var;
2.但是如果是linux下的gcc编译器,前面的_也会被去掉,extern “C”里面的符号就是修饰后符号(为func和var)。

[Supplement]
gcc compiles .c files, because it is compiled according to the c method, so the function name remains unchanged; gcc compiles the .cpp file, g++ compiles the .c file, g++ compiles the .cpp file, because it is compiled according to the c++ method, so the function name Only then will additional information be added. (The object files and library files (object files) compiled by the current compiler should be compiled by the same compiler) ps: To
declare
a function or variable separately as a C symbol, you can directly
extern "C" int func( int);

Problems caused by extern "C"
  are often encountered: some header files declare some functions and variables of C language, but this header file may be included by C language or C++ language. For example:
For the function void *memset (void *, int, size_t), if extern "C" is not used, consider the following two situations:

1. When the C language program contains string.h, if the memset function is used, the compiler will perform the correct function name modification _memset (C language name modification, add _ in front), and can also correctly connect to C memset symbols in the language library.
2. If the memset function is included in the C++ program, the compiler will think that this is a C++ function, and the function name will be modified to _Z6memsetPvii (you can understand the name modification of C++ by yourself). In this case, the linker will not be able to do it. Find the corresponding symbol address in the C language library. In this case extern "C" must be used.

Symbol decoration and function signatures

C++ symbol modification
function signature contains information about a function, including function name, its parameter type, its class and name (name) space and other information.
As shown in the example:

int func(int);
float func(float);
 
class C {
    
    
    int func(int);
    class C2 {
    
    
        int func(int);
    }
};
 
namespace N {
    
    
    int func(int);
    class C {
    
    
        int func(int);
    };
};

For the above code, there are 6 funcs.
Under the GCC compiler, the corresponding modified names of the above 6 functions are as follows:

image

GCC's basic C++ name modification method is as follows: all symbols begin with "_Z", for nested names (in namespaces or within classes), followed by "N", and then for each namespace and class Name, each name is preceded by the length of the name string, and ends with "E". For example, N::C::func is _ZN1N1C4funcE after name modification. For a function, its parameter list follows "E", which for int types is the letter "i". So the entire N::C::func(int) function signature is modified to _ZN1N1C4funcEi.

Signature and name modification mechanismsNot only used in functions, global variables and static variables in C++ also have the same mechanism. For global variables, it is a globally visible name like a function. It also follows the above name modification mechanism. For example, the global variable bar in the namespace foo has a modified name: _ZN3foo3barE. It is worth noting that the type of the variable is not added to the modified name, so whether the variable is an integer, a floating point type or even a global object, its name is the same.
The name decoration mechanism is also used to prevent name conflicts for static variables. For example, there is a static variable called foo in the main() function, and there is also a static variable called foo in the func() function. In order to distinguish these two variables, GCC will modify their symbolic names into two different names _ZZ4mainE3foo and _ZZ4funcvE3foo, thus distinguishing the two variables.

Implementation of compatibility issues between C and C++

The method is to use the C++ macro __cplusplus

#ifdef __cplusplus
extern "C" {
#endif
// 代码
#ifdef __cplusplus
}
#endif

The meaning is that if it is C++ code, then memset will be declared in extern "C",
if it is C code, it will be declared directly.

An example of the use of extern when using function pointers
:
(1) In global.h below in the project

...
typedef void(*xivc_picture_data_t)(img_params *img);
...
extern xivc_picture_data_t decode_image;  // important
...

(2) In ldecode.c below the project

#include "global.h"
...
xivc_picture_data_t decode_image;  // important
...
decode_image = picture_decode_mt;
...

(3) Chinese can use decode_image in image.c below the project

#include "global.h"
...
decode_image(img);
...

等价如下
【ldecode.c】
typedef void(*xivc_picture_data_t)(img_params *img);
...
xivc_picture_data_t decode_image;  // important
...
decode_image = picture_decode_mt;
...
【image.c】
extern xivc_picture_data_t decode_image;  // important
...
decode_image(img);
...

Record

The use of extern in the above situation is the basic usage
in the header file:

extern int g_Int;

Its function isKeyword that declares the scope of a function or global variable, the functions and variables it declares can be used in this module or other modules, remember that it isa declaration rather than a definition!
For example, a function defined in source file A is invisible (that is, inaccessible) in other source files. In order to call this function in source file B, an external declaration should be added to the head of B:
extern function prototype;
In this way, that function can also be called in source file B. Note the difference in wording here:
in A it is a definition, in B it is a statement.
A function can only (and must) be defined in one source file, but can be declared in multiple other source files.
Definition causes storage allocation, which actually creates that entity. while declaration does not cause storage allocation.
To give a crude analogy: after declaring it in source file B, it is like opening a window in B so that it can see the function in A.

basic rules

Extern is often used in variable declarations. You declare a global variable in the *.c file. If this global variable wants to be referenced, put it in *.h and declare it with extern.

Notice

Another method is to use global variables in a public *.h file. If two *.c files include the .c file at the same time, you can avoid using extern and synchronize function updates. In the above example, just use xivc_picture_data_t decode_image; directly in the global.h file. However, the above method may cause the problem of repeated definitions. The best way is to use extern and develop good habits.

Replenish

Using extern int f(); and int f(); at the front of a .c file are equivalent. They
both indicate that the f function may be defined in other functions, but is only declared to be used in the current function. But it is best to use the extern keyword to avoid misunderstandings.

Guess you like

Origin blog.csdn.net/u010523811/article/details/124603624