19

我想知道为什么链接器不能仅仅通过查阅获得实际实现代码的实际 .dll 文件中的信息来完成他们的工作?我的意思是为什么链接器仍然需要 .lib 文件来进行隐式链接?

对于这种链接,导出和相对地址表还不够吗?

无论如何,有没有人可以只使用 .dll 而不使用 .lib 存根/代理文件来进行隐式链接?

我认为 Windows 可执行加载器只会代表程序执行 LoadLibrary/LoadLibraryEx 调用(因此称为隐式链接),这是与显式链接的主要区别。如果这是真的,那么在没有 .lib 的情况下显式执行它应该表明它在没有它的情况下是可行的,对吧?还是我只是在说无意义?

4

1 回答 1

10

我能想到几个原因。

  • 使用 .lib 文件意味着您可以构建与系统上不同版本的 DLL,只要您安装了正确的 SDK。
  • 编译器和链接器需要支持跨平台编译 - 您可能在 32 位平台上为 64 位目标构建,反之亦然,并且不存在正确的体系结构 DLL。
  • .lib 文件使您能够“隐藏”您的实现的某些部分 - 您可以拥有不显示在 .lib 中但可以通过 GetProcAddress 发现的私有导出。您还可以进行序号导出,在这种情况下,它们没有导出的友好名称,但在 .lib 中会有一个友好名称。
  • 本机 DLL 没有强名称,因此可能会选择错误的 DLL 版本。
  • 最重要的是,这项技术是在 1980 年代设计的。如果它是今天设计的,它可能更接近您所描述的 - 例如,.NET 您只需要引用目标程序集并且您拥有使用它所需的一切。

我不知道有什么方法可以单独使用 DLL 进行隐式链接 - 快速搜索发现了几个工具,但我没有使用过其中任何一个。

在这种情况下,我将创建一个包含您需要使用的函数的单独源文件,并根据需要动态加载 DLL 并绑定它们。例如:

// using global variables and no-error handling for brevity.

HINSTANCE theDll = NULL;
typedef void (__stdcall * FooPtr)();
FooPtr pfnFoo = NULL;
INIT_ONCE initOnce;

BOOL CALLBACK BindDLL(PINIT_ONCE initOnce, PVOID parameter, PVOID context)
{
    theDll = LoadLibrary();
    pfnfoo = GetProcAddress(dll, "Foo");

    return TRUE;
}

// Export for foo
void Foo()
{
    // Use one-time init for thread-safe lazy initialization
    InitOnceExecuteOnce(initOnce, BinDll, NULL, NULL)
    pfnFoo();
}
于 2009-08-18T23:07:40.793 回答