如何在D中创建动态库? [英] How to create a Dynamic Library in D?

查看:122
本文介绍了如何在D中创建动态库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在D中创建一个动态库(跨平台),所以我做了一些谷歌搜索。过了一段时间后,我发现了这个页面。我非常惊讶于写作,编译甚至链接到DLL有多么复杂。创建一个共享库的统一方法是不是像C那样? (只是省略主要功能并传递一些标记给链接器)

解决方案

嗯,我决定今天花点时间搞定与此类似,我有一些有用的东西,至少如果主程序也写在D中(在Linux上,我认为它也可以在Windows上工作,原因是我没有链接到phobos。所以在D中,所以它依赖于这些符号的exe,我想,我不知道这里发生了什么,也许它会更好地工作,如果我使用共享的phobos lib)



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



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

  module testdll; 
import std.stdio;
extern(C)
export void lol(){
import core.stdc.stdio;
printf(来自C\\\
的hello);

writeln(hello!);
}


版本(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;
}
返回true;
}

你会注意到大多数代码是WinMain,它只是调用druntime函数。我认为主要应该是至少作为一个混合,甚至可以完全自动的,因为它是纯样板。



客户端代码:

  import core.runtime; 

别名extern(C)void function()functype;

版本(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(调用proc);
proc();
}

这个Windows和Linux有不同的代码,尽管它很相似。如我们在评论中提到的那样,druntime的东西应该很快开始照顾。



编译命令不是太糟糕,但有点怪异。 Linux首先:

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

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



构建客户端是很简单的:

  dmd testdllc.d 

请注意,文件中有pragma(lib)与-ldl选项自动链接。运行它,并得到一些你好! BTW确保两者都在同一个目录中,因为在装入程序中加载./。



现在,我们在Windows上构建。

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

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



这些是该文件的内容: p>

  LIBRARY testdll 

EXETYPE NT
CODE SHARED EXECUTE
数据写入

EXPORTS
lol

如果不使用.def文件,dll将会成功建立,但不会找到该过程,因为它不导出。 (我认为D中的export关键字应该能够自动执行,绕过hte .def文件,我相信有这样做的讨论,但现在就是我所知道的。)



客户端也很简单:

  dmd testdllc.d 

运行它并获得一些hello,如果一切顺利。



现在,为什么我在客户端做functype别名?比其他投票更容易,而且它使得它非常好(C)。



为什么lol函数extern(C)在第一位?这样它就可以在GetProcAddress / dlsym中使用更简单的名称。也可以使用pragma(mangle)或做一个进口的东西。各种各样的选择,相当简单,我只是想保持简单,使测试更容易专注。 lol是一个比_D7testdll3lolFZv更简单的名字,或者任何一个被破坏的名字将是....(OMG我手动弄坏了它!有时我认为我写得太多D哈哈)是的,这也是更难做眼球。注意:在Windows上,如果你这样做,那么.def文件可能要离开前导下划线。



无论如何,这是一个工作的dll为我和一个程序来加载和使用它成功。不如它可以/应该是美丽,但它的作品。至少对我来说。


I want to create a Dynamic library (cross-platform) in D, so I did some Googling. After some time I found this page. I am absolutely stunned by how much complexities there are in writing, compiling and even linking to a DLL. Isn't there a uniform way of creating a shared library like you would in C? (just leave out the main function and pass some flags to the linker)

解决方案

Well, I decided to spend some time today messing with this and I kinda sorta have something that works, at least if the main program is also written in D (on Linux, I think it will work from C too on Windows. The reason is I didn't link to phobos in the .so in the D one, so it relies upon the exe for those symbols. I think, tbh I don't know exactly what is going on here, maybe it would work better if I used the shared phobos lib too)

Anyway, first, let's throw some code down.

This is testdll.d and it builds our 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;
}

You'll notice most that code is the WinMain which just calls druntime functions. I think that main should be available at least as a mixin, or maybe even fully automatic, since it is pure boilerplate.

And the client code:

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();
}

This has different code for Windows and Linux, though it is pretty similar. The druntime stuff is supposed to start taking care of this soon as we mentioned in the comments.

The compile commands aren't too bad but a little weird. Linux first:

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

PIC and shared tell it to build the .so. I did the blank defaultlib because without it, loading the dll at runtime failed with "symbol already defined" errors.

Building the client is straightforward:

dmd testdllc.d

Note that there's the pragma(lib) in the file that links with the -ldl option automatically. Run it and get some hello! BTW be sure both are in the same directory since this loads ./ in the loader.

Now, let's build on Windows.

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

Tell it to output our dll, use -shared so it knows, and then the other thing is the def file, like described here http://dlang.org/dll.html/dllmain

These are the contents of that file:

LIBRARY testdll

EXETYPE NT
CODE SHARED EXECUTE
DATA WRITE

EXPORTS
        lol

If you don't use the .def file, the dll will build successfully, but the procedure won't be found because it isn't exported. (I think the export keyword in D should be able to do this automatically, bypassing hte .def file, and I believe there's a discussion on doing this, but right now it is required as far as I know.)

And the client is similarly easy:

dmd testdllc.d

Run it and get some hellos, if all goes well.

Now, why did I do the functype alias in the client? Easier than doing the other casting and such, and it makes it nicely extern(C).

Why is the lol function extern(C) in the first place? Just so it has an easier name to use in GetProcAddress/dlsym. Could have also pragma(mangle) or did .mangleof with an import thing. All kinds of options there, fairly straightforward, I just wanted to keep it simple to make the test easier to focus on. "lol" is a simpler name than "_D7testdll3lolFZv" or whatever the mangled name would be.... (OMG I mangled it correctly by hand! Sometimes I think I write too much D lol) and yeah that works too it is just harder to do by eyeball. Note: on Windows, the .def file might have to leave off the leading underscore if you do it that way.

anyway, yeah, this made a working dll/so for me and a program to load and use it successfully. Not as pretty as it could/should be, but it works. For me at least.

这篇关于如何在D中创建动态库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆