GetProcAddress 与 __declspec(dllimport)

GetProcAddress vs __declspec( dllimport )

两者有什么区别?

即用于查找 Nt___ 或 Zw___

等函数

GetProcAddress() 可让您在运行时查找函数。它对于可选的导入或诸如可以动态加载的插件之类的东西很有用。它的另一个用途是导出但没有头文件或库文件的未记录函数。

使用 __declspec( dllimport ) 将函数添加到您的导入 table 中,因此它会随您的 executable 自动加载。

在 MS Windos 中,隐式链接显式链接是有区别的。

隐式链接

可执行文件链接到随附的库(.lib 文件),该库提供从 DLL 导出的符号。 (导入的函数用__declspec(dllimport)注释。)隐式链接的DLL加载可执行文件。

显式链接

程序载入一个显式调用 LoadLibrary() 的 DLL。要调用 DLL 的函数,必须使用 GetProcAddress().

确定其地址

但是,GetProcAddress() 也可用于来自隐式链接 DLL 的函数。如果意外地在多个 DLL 中使用相同的符号(例如,如果使用了链接到不同运行时 DLL 的 DLL),这会很有帮助。

有时,提供的 DLL 没有导入库。一个众所周知的例子是 OpenGL,微软在 1.2 版中停止了对它的支持。然而,如果有足够的 H/W 和 up-to-date 驱动程序,当前 OpenGL 版本的所有功能都可能可用(并且可以在 run-time 处使用 GetProcAdress() 加载)。

一段OpenGL绑定的示例代码MyGL.cc:

  // version 2.0
  glAttachShader
    = (PFNGLATTACHSHADERPROC)wglGetProcAddress(
      "glAttachShader");
  glCompileShader
    = (PFNGLCOMPILESHADERPROC)wglGetProcAddress(
      "glCompileShader");
  glCreateProgram
    = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress(
      "glCreateProgram");
  glCreateShader
    = (PFNGLCREATESHADERPROC)wglGetProcAddress(
      "glCreateShader");
  glDeleteProgram
    = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress(
      "glDeleteProgram");
  glDeleteShader
    = (PFNGLDELETESHADERPROC)wglGetProcAddress(
      "glDeleteShader");

MyGL.h:

// Version 2.0
extern MY_GL_API PFNGLATTACHSHADERPROC glAttachShader;
extern MY_GL_API PFNGLCOMPILESHADERPROC glCompileShader;
extern MY_GL_API PFNGLCREATEPROGRAMPROC glCreateProgram;
extern MY_GL_API PFNGLCREATESHADERPROC glCreateShader;
extern MY_GL_API PFNGLDELETEPROGRAMPROC glDeleteProgram;
extern MY_GL_API PFNGLDELETESHADERPROC glDeleteShader;

其中 MY_GL_API 在编译 MyGL.dll 时定义为 __declspec(dllexport),否则 __declspec(dllimport)。 (所以,实际上 __declspec(dllimport) GetProcAddress() 而不是 vs. 因为函数指针本身是 dllexported但初始化为 run-time unsing GetProcAddress()。)

PFNGL 宏扩展为具有适当签名的函数指针类型。它们包含在 kronos.org 提供的 header 中。)

GetProcAddress() 的另一个重要用途是用于可能不存在于特定版本 Windows 之前的函数(或其他可能在 DLL 中可用或不可用的函数)。因此,当 GetProcAddress() 无法实现预期功能时,可以向后兼容地编写应用程序,提供替代方案 fall-back。

MSDN 上为 GetProcAddress() 提供的示例:

typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);

// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.

   PGNSI pGNSI;
   SYSTEM_INFO si;

   ZeroMemory(&si, sizeof(SYSTEM_INFO));

   pGNSI = (PGNSI) GetProcAddress(
      GetModuleHandle(TEXT("kernel32.dll")), 
      "GetNativeSystemInfo");
   if (NULL != pGNSI) {
      pGNSI(&si);
   } else {
       GetSystemInfo(&si);
   }

进一步阅读:MSDN: Link an executable to a DLL