如何在D中创建动态库?

Jer*_*oen 6 dll d dynamic-library

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

Ada*_*ppe 6

好吧,我今天决定花一些时间来搞乱这个,我有点像有一些有用的东西,至少如果主程序也是用D编写的(在Linux上,我认为它也可以在Windows上运行.原因是我没有链接到D中的.so中的phobos,所以它依赖于那些符号的exe.我想,我不知道到底发生了什么,也许如果我使用它会更好共享的phobos lib也)

无论如何,首先,让我们抛出一些代码.

这是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;
}
Run Code Online (Sandbox Code Playgroud)

您会注意到大多数代码是WinMain,它只调用druntime函数.我认为主要应该至少可以作为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();
}
Run Code Online (Sandbox Code Playgroud)

这有不同的Windows和Linux代码,虽然它非常相似.如我们在评论中提到的那样,应该尽快开始解决这个问题.

编译命令不是太糟糕,但有点奇怪.Linux优先:

dmd -fPIC -shared testdll.d -defaultlib= # builds the dll
Run Code Online (Sandbox Code Playgroud)

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

构建客户端非常简单:

dmd testdllc.d
Run Code Online (Sandbox Code Playgroud)

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

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

dmd -oftestdll.dll -shared testdll.d testdll.def
Run Code Online (Sandbox Code Playgroud)

告诉它输出我们的dll,使用-shared让它知道,然后另一件事就是def文件,如http://dlang.org/dll.html/dllmain所述

这些是该文件的内容:

LIBRARY testdll

EXETYPE NT
CODE SHARED EXECUTE
DATA WRITE

EXPORTS
        lol
Run Code Online (Sandbox Code Playgroud)

如果不使用.def文件,则dll将成功构建,但由于未导出该过程,因此无法找到该过程.(我认为D中的export关键字应该能够自动执行此操作,绕过hte .def文件,我相信有关于这样做的讨论,但就我所知,现在需要它.)

客户端同样容易:

dmd testdllc.d
Run Code Online (Sandbox Code Playgroud)

如果一切顺利的话,运行它并得到一些hellos.

现在,为什么我在客户端中使用functype别名?比做其他铸造等更容易,并且它很好地外部(C).

为什么lol函数extern(C)首先?这样它在GetProcAddress/dlsym中使用的名称更容易.可能还有pragma(mangle)或带有导入内容的.mangleof.那里有各种各样的选项,相当简单,我只想保持简单,让测试更容易关注."lol"是一个比"_D7testdll3lolFZv"更简单的名字,或者任何被破坏的名字......(OMG我用手正确地破坏了它!有时候我觉得我写了太多D lol)而且是的,它也有效,它只是更难通过眼球来做.注意:在Windows上,如果以这种方式执行,.def文件可能必须不使用前导下划线.

无论如何,是的,这为我和一个程序加载和成功使用它做了一个工作的DLL.不像它应该/应该那么漂亮,但它的工作原理.对我来说至少.