【问题标题】:How to create a Dynamic Library in D?如何在 D 中创建动态库?
【发布时间】:2013-12-15 19:00:21
【问题描述】:

我想在 D 中创建一个动态库(跨平台),所以我做了一些谷歌搜索。一段时间后,我找到了this 页面。我对编写、编译甚至链接到 DLL 的复杂程度感到非常震惊。没有像在 C 中那样创建共享库的统一方法吗? (只需省略主函数并将一些标志传递给链接器)

【问题讨论】:

  • 主要的复杂性来自于需要在运行时进行链接和解链接的GC。 C 默认不需要那个
  • @ratchetfreak 这不是自动完成的吗?
  • 在 druntime 中自动完成这一切的实现是不完整的。我开始写一个答案来展示如何使用它,发现它没有链接,因为功能还没有完成...... Windows 上的模块定义文件也有点痛苦,因为 export 关键字没有在那里帮助不大。关于修复它的争论正在缓慢进行,但这些事情拖了后腿。现在,最好的方法是进行大量的手动工作,调用 C 动态库函数来自己加载库......
  • 实现实际上是不完整的,但从某种意义上说它只适用于Linux。你可以在那里工作(见dlang.org/dll-linux.html)。我想事情正在发生变化,我们会在一段时间后看到更好的支持。
  • 是的,主要问题是“跨平台”部分。为了让动态库“正常工作”,已经做了很多工作,但目前主要集中在 Linux 上。

标签: dll d dynamic-library


【解决方案1】:

好吧,我决定今天花一些时间来解决这个问题,而且我有一些可以工作的东西,至少如果主程序也是用 D 编写的(在 Linux 上,我认为它也可以在 Windows 上用 C 语言工作。原因是我没有链接到 D 中的 .so 中的 phobos,所以它依赖于这些符号的 exe。我想,我不知道这里到底发生了什么,也许它会更好如果我也使用了共享的 phobos 库)

不管怎样,首先,让我们写一些代码。

这是 testdll.d,它构建了我们的 dll

module testdll;
import std.stdio;
extern(C)
export void lol() {
    import core.stdc.stdio;
    printf("hello from C\n");

    writeln("hello!");
}


version(Windows)
extern(Windows) bool DllMain(void* hInstance, uint ulReason, void*) {
import std.c.windows.windows;
import core.sys.windows.dll;
    switch (ulReason)
{
    default: assert(0);
case DLL_PROCESS_ATTACH:
    dll_process_attach( hInstance, true );
    break;

case DLL_PROCESS_DETACH:
    dll_process_detach( hInstance, true );
    break;

case DLL_THREAD_ATTACH:
    dll_thread_attach( true, true );
    break;

case DLL_THREAD_DETACH:
    dll_thread_detach( true, true );
    break;
  }
  return true;
}

您会注意到大部分代码是 WinMain,它只调用运行时函数。我认为 main 至少应该可以作为 mixin 使用,或者甚至可以是全自动的,因为它是纯样板文件。

以及客户端代码:

import core.runtime;

alias extern(C) void function() functype;

version(Posix) {
    extern(C) void* dlsym(void*, const char*);
    extern(C) void* dlopen(const char*, int);
    extern(C) char* dlerror();

    pragma(lib, "dl");
} else version(Windows) {
    extern(Windows) void* LoadLibraryA(const char* filename);
    extern(Windows) void* GetProcAddress(void*, const char*);
}

void main() {
    version(Posix) {
            auto thing = dlopen("./testdll.so", 2);
            if(thing is null) {
                    import std.conv;
                    import std.stdio;
                    writeln(to!string(dlerror()));
                    return;
            }
            auto proc = cast(functype) dlsym(thing, "lol");
    } else version(Windows) {
            auto thing = LoadLibraryA("testdll.dll");
            assert(thing !is null);
            auto proc = cast(functype) GetProcAddress(thing, "lol");
    }
    assert(proc !is null);
    //import std.stdio; writeln("calling proc");
    proc();
}

这对于 Windows 和 Linux 有不同的代码,尽管它们非常相似。正如我们在 cmets 中提到的,druntime 的东西应该尽快开始处理这个问题。

编译命令还不错,但有点奇怪。 Linux优先:

dmd -fPIC -shared testdll.d -defaultlib= # builds the dll

PIC 和 shared 告诉它构建 .so。我做了空白的 defaultlib,因为没有它,在运行时加载 dll 失败并出现“符号已定义”错误。

构建客户端很简单:

dmd testdllc.d

请注意,文件中的 pragma(lib) 会自动与 -ldl 选项链接。运行它并打个招呼!顺便说一句,确保两者都在同一个目录中,因为这会将 ./ 加载到加载器中。

现在,让我们在 Windows 上构建。

dmd -oftestdll.dll -shared testdll.d testdll.def

告诉它输出我们的dll,使用-shared让它知道,然后是def文件,就像这里描述的http://dlang.org/dll.html/dllmain

这些是该文件的内容:

LIBRARY testdll

EXETYPE NT
CODE SHARED EXECUTE
DATA WRITE

EXPORTS
        lol

如果不使用 .def 文件,则 dll 将成功构建,但由于未导出,因此无法找到该过程。 (我认为 D 中的 export 关键字应该能够自动执行此操作,绕过 hte .def 文件,并且我相信对此进行了讨论,但据我所知目前是必需的。)

客户端也同样简单:

dmd testdllc.d

如果一切顺利,运行它并打个招呼。

现在,我为什么要在客户端做 functype 别名?比进行其他类型转换等更容易,而且它非常适合 extern(C)。

为什么首先是lol函数extern(C)?只是为了在 GetProcAddress/dlsym 中有一个更容易使用的名称。也可能有 pragma(mangle) 或 .mangleof 与导入的东西。那里有各种各样的选项,相当简单,我只是想保持简单,让测试更容易集中。 “lol”是一个比“_D7testdll3lolFZv”更简单的名称,或者其他任何被破坏的名称......(天哪,我用手正确地破坏了它!有时我认为我写了太多的 D 哈哈),是的,它也能工作,只是更难用眼球来做。注意:在 Windows 上,如果您这样做,.def 文件可能必须去掉前导下划线。

无论如何,是的,这为我和一个程序成功加载和使用它提供了一个工作 dll/so。没有它可以/应该的那么漂亮,但它确实有效。至少对我来说。

【讨论】:

  • .def 文件毕竟可能不是必需的。我刚刚用损坏的名称做了一些测试,它能够成功加载它。我认为我犯的错误是忘记了 GetProcAddress 中的前导下划线。我还计划向 druntime 提交一个拉取请求,该请求可以提供一个瓶装 dllmain,这应该真正减少麻烦。
猜你喜欢
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多