如何调用C函数,该函数仅在特定的OS版本中可用?

时间:2017-12-28 17:06:16

标签: c windows operating-system

我用高级语言编程" (Nim),我必须"去C"出于性能原因。我想做这样的事情:

/* Pseudocode */
include <VersionHelpers.h>
/* ...*/
if (isWindows8OrGreater()) {
    /** use InterlockedIncrementNoFence64() */
} else {
    /** use InterlockedIncrement64() ;( */
}

但我被告知它会在Windows 7上崩溃,因为如果我引用InterlockedIncrementNoFence64(),它必须是可用的,即使我没有尝试调用它。

我正在编写一个多线程应用程序,并使用&#34;消息&#34;实现通信。 (即使在同一个线程内)。这将针对每条消息进行调用,因此内存屏障会对性能产生重大影响。

我已经习惯了Java,在那里工作的东西,以及如何在C中完成。

1 个答案:

答案 0 :(得分:2)

具体案例InterlockedIncrementNoFence64未从任何dll导入,而是在大多数平台上使用编译器内在函数实现( x86 - 通过_InterlockedCompareExchange64 amd64 - _InterlockedIncrement64 arm / arm64 - _InterlockedIncrement64_nf)。因此具有此具体调用的代码将适用于任何Windows版本(当然也在win7中)。

在更一般的情况下,如果我们需要使用几个不在所有操作系统上导出的函数 - 我们可以将它声明为函数指针并在运行时解析。例如,让LoadPackagedLibrary api

我们可以声明函数指针:

HMODULE (WINAPI * LoadPackagedLibraryAddr)(
                                   _In_       LPCWSTR lpwLibFileName,
                                   _Reserved_ DWORD   Reserved
                                   );

在运行时解决它:

*(void**)&LoadPackagedLibraryAddr = GetProcAddress(
    GetModuleHandleW(L"kernel32"), "LoadPackagedLibrary");

并使用:

if (LoadPackagedLibraryAddr)
{
    LoadPackagedLibraryAddr(L"***",0);
}
else {...}

使用__declspec(dllimport)声明的函数的另一种可能方式(使用此前缀声明的大多数windows api)使用下一个语法:

extern "C" {
    PVOID __imp_LoadPackagedLibrary;
}

#ifdef _M_IX86 
__pragma(comment(linker, "/alternatename:__imp__LoadPackagedLibrary@8=___imp_LoadPackagedLibrary"))
#endif

__imp_LoadPackagedLibrary = GetProcAddress(
    GetModuleHandleW(L"kernel32"), "LoadPackagedLibrary");

#pragma warning(disable : 4551)
if (LoadPackagedLibrary)//if (__imp_LoadPackagedLibrary)
{
    LoadPackagedLibrary(L"***",0);
}

然而,两种方式都生成绝对相同的二进制代码,只使用不同的语法。

注意,根本不需要查询windows版本,只需要尝试获取指向api的指针。或者我们得到它,可以使用,或者不使用。

使用__declspec(selectany)#pragma comment(linker, "/alternatename:_pWeakValue=_pDefaultWeakValue")

的方式

在这里不起作用,因为我们在链接时解析符号(结果对于所有Windows版本都是常见的),但我们实际上需要在运行时解析符号