C 有效地为不同的Windows版本使用不同的API
我面临着在可用的情况下使用新的Windows运行时函数的需要,而在新函数不存在的情况下,我需要使用旧的Windows运行时函数 代码路径对性能至关重要。如果平台(Windows 8/Windows Server 2012)提供,我需要调用C 有效地为不同的Windows版本使用不同的API,c,windows,winapi,dll,C,Windows,Winapi,Dll,我面临着在可用的情况下使用新的Windows运行时函数的需要,而在新函数不存在的情况下,我需要使用旧的Windows运行时函数 代码路径对性能至关重要。如果平台(Windows 8/Windows Server 2012)提供,我需要调用GetSystemTimePreciseSFileTime,如果新版本不存在,则返回到GetSystemTimeAsFileTime 我知道我可以使用LoadLibrary和GetProcAddress来加载GetSystemTimePrecisesFileTi
GetSystemTimePreciseSFileTime
,如果新版本不存在,则返回到GetSystemTimeAsFileTime
我知道我可以使用LoadLibrary
和GetProcAddress
来加载GetSystemTimePrecisesFileTime
,如果失败,请返回到GetSystemTimeAsFileTime
。我关心的是,将这样的东西称为性能关键的东西会对性能产生影响,尽管这是另一个间接层
是否有任何内置机制延迟加载一个符号,而不是整个DLL?因此,我可以在启动时测试Windows版本,并在Win8上调用使用GetSystemTimePreciseSFileTime
的函数,否则调用使用GetSystemTimeAsFileTime
的函数,而不会导致整个可执行文件因旧Windows版本上的链接错误而失败
或者我应该停止担心,使用
GetProcAddress
?正如对这个问题的各种评论所说,GetProcAddress()
的成本是最低的。通过函数指针调用函数的成本也接近于零
但我们可以说,我们的工作是超批判性的。我们注意到您要调用的两个函数都有签名
VOID WINAPI GetSystemTimePreciseAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
void WINAPI GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
也就是说,他们的签名几乎相同;唯一的区别是奇怪的返回类型VOID
,所以他们完全一样
这是什么意思?这意味着您可以使用单个函数指针变量,在程序开始时初始化一次,如下所示:
// global scope
VOID (*WINAPI myGetSystemTime)(LPFILETIME);
// in your initalization code
FARPROC fp;
myGetSystemTime = GetSystemTimeAsFileTime;
if (/* LoadLibraryW(L"kernel32.dll") worked */) {
fp = GetProcAddress(/* library handle */, "GetSystemTimePreciseAsFileTime");
if (fp != NULL)
myGetSystemTime = (VOID (*WINAPI)(LPFILETIME)) fp;
}
// and don't worry about errors from LoadLibraryW() or GetProcAddress() since we want to use the fallback; you may log a warning if you so choose
然后,当需要有选择地调用函数时,只需调用
(*myGetSystemTime)(&time)
。没有条件分支或不断轮询DLL,应该没有可测量的性能影响。我认为andlabs的答案可能是正确的;通过指针调用函数的性能损失不大。另一方面,它确实需要额外的RAM往返(或者导致指针占用缓存中可能更好使用的一行),因此它不是绝对确定的
如果配置文件显示您确实存在问题,请考虑将性能关键代码放入DLL中。有两个DLL版本,每个API一个。安装应用程序后,请将适当的版本放到适当的位置。
我会说“不要担心,使用GetProcAddress”。额外的间接寻址可能非常小。尝试进入GetSystemTimeAsFileTime,一次使用GetProcAddr,一次不使用,可能几乎没有区别。没有“间接寻址层”。否则,一个探查器或一个小测试程序可以很容易地向您展示一些东西。为什么您会担心一个函数调用的性能,这个函数在启动时调用了两次,以后就再也不会运行了?如果签名不同,您可以在其周围放置一个包装函数,并存储指向它的指针。