我知道之前已经问过这个问题,但我在这里看到的情况都不像这个。 我在运行时导入一些API函数,这些函数的一般声明如下:
// Masks for UnmapViewOfFile and MapViewOfFile
typedef BOOL (WINAPI *MyUnmapViewOfFile)(LPCVOID);
typedef LPVOID (WINAPI *MyMapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
// Declarations
MyUnmapViewOfFile LoadedUnmapViewOfFile;
MyMapViewOfFile LoadedMapViewOfFile;
然后我调用一个通用的“加载”函数,它调用GetProcAddress以从正确的DLL获取导出函数的地址。该地址以无效**返回。此void **是泛型加载中的参数之一,如:
int GenericLoad(char* lib, void** Address, char* TheFunctionToLoad)
我会称这个函数为:
void *Address;
GenericLoad("kernel32.dll", &Address, "UnmapViewOfFile");
LoadedUnmapViewOfFile = (MyUnmapViewOfFile) Address;
或类似的东西。 现在,编译器当然抱怨尝试将数据void *转换为函数指针。那我该怎么办?
我读过无数网站和各种讨厌的演员表,所以如果你在解释中添加代码,我会很感激。
由于 杰斯
答案 0 :(得分:3)
正确的代码就是这一行:
GenericLoad("kernel32.dll", (void**)&LoadedUnmapViewOfFile, "UnmapViewOfFile");
这里做的基本上是这样的:指针变量的地址(你想要放置函数地址的地址)被传递给GenericLoad - 这基本上是它所期望的。 void **表示“给我指针的地址”。所有类型的铸造都是它的神奇之处。 C不允许指定“指向任何函数指针的指针”,因此API作者首选void **。
答案 1 :(得分:3)
现在,编译器当然抱怨尝试将数据void *转换为函数指针
我的编译器根本不会对你的代码抱怨(然后再说,我没有警告 - 我正在使用或多或少的默认选项)。这是来自MS,GCC和其他人的各种编译器。您能否提供有关您正在使用的编译器和编译器选项以及您所看到的确切警告的更多详细信息?
也就是说,C不能保证函数指针可以毫无问题地转换为void指针,但实际上这在Windows上可以正常工作。
如果你想要符合标准的东西,你需要使用'泛型'函数指针而不是void指针--C保证函数指针可以转换为任何其他函数指针而不会丢失,所以这无论您的平台如何,都可以使用这可能是为什么Win32 GetProcAddress()
API的返回值返回FARPROC
的原因,它只是函数指针的typedef,该函数指向不带参数的函数(或至少未指定的参数)并返回指针-sized int。类似的东西:
typedef INT_PTR (FAR WINAPI *FARPROC)();
FARPROC
将是Win32关于“通用”函数指针的想法。所以你需要做的就是有一个类似的typedef(如果你出于某种原因不想使用FARPROC
):
typedef intptr_t (*generic_funcptr_t)(); // intptr_t is typedef'ed appropriately elsewhere,
// like in <stdint.h> or something
int GenericLoad(char* lib, generic_funcptr_t* Address, char* TheFunctionToLoad)
generic_funcptr_t Address;
GenericLoad("kernel32.dll", &Address, "UnmapViewOfFile");
LoadedUnmapViewOfFile = (MyUnmapViewOfFile) Address;
或者你可以省去中间人并传递你真正想要获得价值的指针:
GenericLoad2("kernel32.dll", (generic_funcptr_t *) &LoadedUnmapViewOfFile, "UnmapViewOfFile");
虽然这比使用中间变量的方法更危险 - 例如,如果你在最后一个例子中省略了&符号,编译器将不会给出诊断,但是在前面的例子中,它通常会给出至少一个警告你从Address
参数中省略了&符号。与Bug#1类似:http://blogs.msdn.com/sdl/archive/2009/07/28/atl-ms09-035-and-the-sdl.aspx
现在你应该被设定。但是,无论你如何看待它,你都需要进行一些危险的铸造。即使在带有模板的C ++中,您也必须在某个级别执行强制转换(尽管您可以在模板函数中隐藏它),因为GetProcAddress()
API不知道函数指针的实际类型'正在检索。
另请注意,GenericLoad()
的界面可能存在严重的设计问题 - 它无法管理库的生命周期。如果您的意图是不允许卸载库,那可能不是问题,但这是用户可能想要的,因此您应该考虑该问题。
答案 2 :(得分:2)
数据地址和函数地址是不兼容的。我相信您的Address
变量必须是BOOL
定义中的函数指针:(WINAPI *MyUnmapViewOfFile)(LPCVOID)
。这种类型的声明是必需的,因为为了获得指向函数的指针,必须知道返回类型以及参数的类型和数量。这是因为当您调用函数时,必须在堆栈上分配正确的空间量以包含这些返回值和args。
考虑到这一点,我认为帕维尔答案的修正是正确的(仅供参考,他的(无效**)演员是一种类型安全措施)。