dll - 如何在 D 中创建动态库?

标签 dll d dynamic-library

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

最佳答案

好吧,我决定今天花一些时间来解决这个问题,我有点东西可以工作,至少如果主程序也是用 D 编写的(在 Linux 上,我认为它也可以在 Windows 上使用 C 语言。原因是我没有在 D 中的 .so 中链接到 phobos,所以它依赖于这些符号的 exe。我想,tbh 我不知道这里到底发生了什么,也许如果我使用它会更好共享的 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 有不同的代码,尽管它非常相似。正如我们在评论中提到的,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 lol)是的,它也有效,只是更难用眼球来做。注意:在 Windows 上,如果您这样做,.def 文件可能必须去掉前导下划线。

无论如何,是的,这为我制作了一个有效的 dll/so 和一个可以成功加载和使用它的程序。不像它可能/应该的那样漂亮,但它有效。至少对我来说。

关于dll - 如何在 D 中创建动态库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20598427/

相关文章:

c++ - Windows Hook 和 DLL 加载

syntax - DLang 中的 auto 自动功能模板

c++ - Facebook warpdrive 构建 - D 编程语言

centos - Ada 封装库项目 GNAT

c++ - Visual Studio,未找到 .dll(从未要求过)

c++ - 在注入(inject)的 Dll 的 DllMain 中运行代码会导致注入(inject)超时

c++ - 创建和使用简单的 .dylib

c++ - Clang 不导出 C++ 符号

c++ - 如何确定加载我的 DLL 的 DLL 名称(字符串)?

file - 将流移至文件开头