GetModuleHandle与包含头之间的区别

san*_*uro 2 c c++ winapi

也许是愚蠢的问题,但我不知道答案.使用GetModuleHandle或LoadLibrary加载DLL(然后使用该DLL的功能)并直接包含所需的标头有什么区别.例如,使用GetModuleHandle:

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);  //calling function through pointer
else GetSystemInfo(&si);
Run Code Online (Sandbox Code Playgroud)

但我可以包含windows.h标头直接从我的代码中调用该函数:

#include <windows.h>

SYSTEM_INFO si;
ZeroMemory(&si, sizeof(SYSTEM_INFO));
GetNativeSystemInfo(&si);
Run Code Online (Sandbox Code Playgroud)

这同样适用于opengl32.dll,我不知道在我的项目中包含opengl函数的头文件是否更好,或者使用Getmodulehandle和GetProcAdress来调用所需的函数.有什么不同?第一种方法是以某种方式使用getmodulehandle的好处吗?谢谢你的回答.

The*_*ish 6

首先,请确保您了解GetModuleHandleLoadLibrary并不完全等效.但是,由于这不是你问题的直接部分,我将给出一个重要的解释,并建议你确保理解这两个链接中的文档.


要直接使用dll函数,就像它像任何其他函数一样,您不只是包含标题.除了标题之外,在项目的某个地方,它被告知要链接到相应的lib文件.在您的示例中,它将是kernel32.lib.这可以通过各种方式完成,例如项目中的链接器设置或#pragma comment (lib, ...)文件中的文件.

当程序像这样构建时,编译器会编写代码以在程序启动时加载该DLL.如果在实际尝试运行程序时找不到有问题的DLL,则它将失败并显示错误消息.您无法编写代码来捕获故障并采取其他替代操作.

对于属于操作系统的dll(如kernel32.dll)或者至少通常提供的dll,这种立即加载行为不是问题,因为您可以安全地假设dll将始终存在.另一方面,如果你要构建一个通常不存在的dll,那么你会有更多的担忧.要么你必须确保这样的dll与你的程序一起分发,否则试图确保用户安装其他任何必要的软件包以在他们的系统上获得该dll.

此外,如果dll加载但您尝试从该DLL使用的任何函数实际上并不存在,那么它将立即失败并显示错误消息.(它不会等到你的程序试图调用该函数.它会在程序启动时发现这种差异然后中止.)如果世界上存在不同版本的dll,这可能是个问题.

现在,当您使用LoadLibrary/GetProcAddress时,您要求在您选择时加载dll并要求查找该dll提供的特定函数.如果这些步骤中的任何一个失败,您就能够以合理的方式编写代码来处理它.

这可以用于各种目的.例如,您可以创建一个插件机制,程序在其中搜索并动态从某个特定文件夹加载插件dll.由于程序不知道哪个插件会出现,LoadLibrary是唯一的方法.

LoadLibrary/GetProcAddress可以使用的另一件事是加载一个DLL并从中调用一个函数,即使你没有正确的头文件和lib文件.如果您知道dll的名称,函数的名称以及函数的确切签名(参数类型,返回类型,调用约定),那么您就足以编写代码来加载该DLL并成功调用该函数.偶尔这可能很有用.例如,这是人们能够使用Windows dll提供的某些"未记录的"功能的一种方式.

最后,LoadLibrary/GetModuleHandle/GetProcAddress可用于让您使用不一定存在于您要支持的所有操作系统上的功能.这似乎是您调用GetNativeSystemInfoGetSystemInfo的代码段的原因.前者仅在WinXP/2003上可用,而后者在Win2000上可用.如果代码刚刚被编写为对GetNativeSystemInfo的直接调用,那么程序将无法在Windows 2000上运行.但是,你在那里检查当前操作系统上是否存在GetNativeSystemInfo,如果是,则仅使用它,否则它会回到更广泛支持的GetSystemInfo上.

因此,对于您的示例,您选择调用该函数的技术取决于您打算支持的操作系统.如果你的软件不需要在Windows 2000上运行,那么只需直接调用GetNativeSystemInfo就容易多了(而且很可能是最好的方法).