繁体   English   中英

将void指针(数据)转换为函数指针

[英]Casting a void pointer (data) to a function pointer

我知道之前有人问过,但我在这里看到的情况都不像这个。 我在运行时导入一些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 *转换为函数指针。 那怎么办?

我已经阅读了无数的网站和各种令人讨厌的演员阵容,所以如果你在解释中添加代码,我会很感激。

谢谢Jess

正确的代码是这一行:

GenericLoad("kernel32.dll", (void**)&LoadedUnmapViewOfFile, "UnmapViewOfFile");

这里做的基本上是这样的:指针变量的地址(你想要放置函数地址的地址)被传递给GenericLoad - 这基本上是它所期望的。 void **表示“给我指针的地址”。 所有类型的铸造都是它的神奇之处。 C不允许指定“指向任何函数指针的指针”,因此API作者首选void **。

现在,编译器当然抱怨尝试将数据void *转换为函数指针

我的编译器根本不会对你的代码抱怨(然后再说,我没有发出警告 - 我正在使用或多或少的默认选项)。 这是来自MS,GCC和其他人的各种编译器。 您能否提供有关您正在使用的编译器和编译器选项以及您所看到的确切警告的更多详细信息?

也就是说,C不能保证函数指针可以毫无问题地转换为/从void指针转换,但实际上这在Windows上可以正常工作。

如果你想要符合标准的东西,你需要使用'泛型'函数指针而不是void指针--C保证函数指针可以转换为任何其他函数指针而不会丢失,所以无论如何都可以使用你的平台 这可能是为什么Win32 GetProcAddress() API的返回值返回FARPROCFARPROC只是函数指针的typedef,该函数指向不带参数(或至少未指定的参数)的函数,并返回指针大小的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()接口可能存在严重的设计问题 - 它无法管理库的生命周期。 如果您的意图是不允许卸载库,这可能不是问题,但这是用户可能想要的,因此您应该考虑该问题。

数据地址和函数地址是不兼容的。 我相信你的Address变量必须是BOOL定义中的函数指针: (WINAPI *MyUnmapViewOfFile)(LPCVOID) 这种类型的声明是必需的,因为为了获得指向函数的指针,必须知道返回类型以及参数的类型和数量。 这是因为当您调用函数时,必须在堆栈上分配正确的空间量以包含这些返回值和args。

考虑到这一点,我认为帕维尔答案的修正是正确的(仅供参考,他的(无效**)演员是一种类型安全措施)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM