从GetProcAddress强制转换的函数的签名必须完全匹配吗?

Does the signature of the function casted from GetProcAddress have to match exactly?

本文关键字:GetProcAddress 转换 函数      更新时间:2023-10-16

这个问题基本上可以归结为"我可以安全地将函数指针强制转换为具有可转换为这些类型的参数的指针吗?",但如果没有实际示例,这听起来非常可疑。

我做了一个小的实用程序函数从DLL中调用一些东西,给定DLL名称,函数名称,适当的返回类型,参数类型和参数。但是,在使用此实用程序时,可能会出现超出所需的额外膨胀。我想知道不显式指定参数类型是否安全。以Windows API MessageBoxW为例:

callStdcallDllFunction<int, HWND, PCWSTR, PCWSTR, UINT>(
    L"user32", "MessageBoxW", nullptr, nullptr, nullptr, 0
);

这里我显式地指定了MessageBoxW的返回类型和四个参数类型。当然,如果参数从右开始,与函数中的类型相同,我就不需要指定它们。但是,只要首先传递给函数的参数是有效的(这些都是),就可以推断类型吗?

callStdcallDllFunction<int>(L"user32", "MessageBoxW", nullptr, nullptr, nullptr, 0);

在这里,我相信GetProcAddress的结果将被铸造到int(__stdcall *)(std::nullptr_t, std::nullptr_t, std::nullptr_t, int);。不过,传入的参数仍然适合实际的函数。这仍然是定义良好的行为吗?它总是做我想做的吗?到目前为止,每次我测试它都是有效的。

当我在它,它是可以离开返回类型作为void,如果它是未使用的?

//return type is void instead of the actual int
callStdcallDllFunction(L"user32", "MessageBoxW", nullptr, nullptr, nullptr, 0);

据我所知,每次我都试过了。如果这些确实保证工作,我希望能够像这样使用它们,但我真的不知道它们是否总是有效,而且我不打算冒我所写的未定义行为的风险。

确切的代码与问题无关,但是您可以在这里找到可以用于测试的stdcall版本(例如MessageBox是什么)。这些版本与cdecl版本之间的唯一区别是在函数签名中添加了__stdcall

您必须知道调用约定的确切细节,以及过去能够给出精确答案的ABI。

在某些调用约定中,参数在堆栈上传递。只要参数的大小相同,并且不涉及额外的转换,就不会有崩溃的风险。返回值:如果在堆栈上返回,要小心。如果它返回到寄存器中,那就无关紧要了。

通常具有相同长度的类型不是问题。但是要小心一些更奇特的类型,比如成员(函数)指针,它们不一定和void*一样大。

可转换性不够严格。char可以转换为long类型,但在堆栈上它们占用的字节数不同,因此这样的函数指针强制转换会导致不同的堆栈内容解释。