一旦DLL模块被显式加载,线程就必须获取它要引用的符号的地址,方法是调用下面的函数:
FARPROC GetProcAddress(
HINSTANCE hinstDll,
PCSTR pszSymbolName);
参数hinstDll是调用LoadLibrary(Ex)或GetModuleHandle函数而返回的,它用于设定包含符号的DLL的句柄。参数pszSymbolName可以采用两种形式。第一种形式是以0结尾的字符串的地址,它包含了你想要其地址的符号的名字:
FARPROC pfn = GetProcAddress(hinstDll, "SomeFuncInDll");
注意,参数pszSymbolName的原型是PCSTR,而不是PCTSTR。这意味着GetProcAddress函数只接受ANSI字符串,决不能将Unicode字符串传递给该函数,因为编译器/链接程序总是将符号名作为ANSI字符串存储在DLL的输出节中。
参数pszSymbolName的第二种形式用于指明你想要其地址的符号的序号:
FARPROC pfn = GetProcAddress(hinstDll, MAKEINTRESOURCE(2));
这种用法假设你知道你需要的符号名被DLL创建程序赋予了序号值2。同样,我要再次强调,Microsoft非常反对使用序号,因此你不会经常看到GetProcAddress的这个用法。
这两种方法都能够提供包含在DLL中的必要符号的地址。如果DLL模块的输出节中不存在你需要的符号,GetProcAddress就返回NULL,表示运行失败。
应该知道,调用GetProcAddress的第一种方法比第二种方法要慢,因为系统必须进行字符串的比较,并且要搜索传递的符号名字符串。对于第二种方法来说,如果传递的序号尚未被分配给任何输出的函数,那么GetProcAddress就会返回一个非NULL值。这个返回值将会使你的应用程序错误地认为你已经拥有一个有效的地址,而实际上你并不拥有这样的地址。如果试图调用该地址,肯定会导致线程引发一个访问违规。我在早期从事Windows编程时,并不完全理解这个行为特性,因此多次出现这样的错误。所以一定要小心(这个行为特性是应该避免使用序号而使用符号名的另一个原因)。