2013-12-15 3 views
6

Я хочу создать динамическую библиотеку (кросс-платформу) в D, поэтому я сделал несколько Google. Через некоторое время я нашел this. Я абсолютно ошеломлен тем, насколько сложны в письменной форме, компиляция и даже привязка к DLL. Разве нет единого способа создания общей библиотеки, как в C? (просто оставьте основную функцию и передайте некоторые флагов в компоновщик)Как создать динамическую библиотеку в D?

+0

Основная сложность исходит от GC, который необходимо связать и деинсталлировать во время выполнения. C не нуждается в том, что по умолчанию –

+0

@ratchetfreak Есть ли причина, по которой это не делается автоматически? –

+1

Реализация, которая делает все это автоматически во время выполнения, является неполной. Я начал писать ответ, чтобы показать, как его использовать, и обнаружил, что он не связан, поскольку функции еще не выполнены ... файл определения модуля в Windows тоже немного боль, поскольку ключевое слово export doesn ' Так много помогает. Там медленно продолжаются дебаты об исправлении, но эти вещи перетащить. Прямо сейчас, лучший способ сделать это - с достаточным количеством ручной работы, вызывая функции динамической библиотеки C для загрузки библиотеки самостоятельно ... –

ответ

5

Ну, я решил потратить некоторое время на то, чтобы сегодня испортить это, и у меня есть что-то, что работает, по крайней мере, если основная программа также написана в D (в Linux, я думаю, он будет работать с C тоже на Windows. Причина в том, что я не ссылался на phobos в .so в D, поэтому он полагается на exe для этих символов. Я думаю, tbh I не знаю точно, что здесь происходит, может быть, это сработает лучше, если я тоже воспользуюсь общей библиотекой phobos).

В любом случае, во-первых, давайте бросим код.

Это testdll.d и строит наша длл

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, который просто вызывает druntime функции. Я думаю, что основная часть должна быть доступна, по крайней мере, в виде микса или, может быть, даже полностью автоматической, так как это чистый шаблон.

И код клиента:

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, хотя это очень похоже. Предполагается, что материал для прослушивания начнет заботиться об этом в ближайшее время, как мы упоминали в комментариях.

Команды компиляции не так уж плохи, но немного странные. Linux:

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

PIC и поделились им, чтобы построить .so. Я сделал пустой defaultlib, потому что без него загрузка dll во время выполнения не удалась с ошибками «уже определенные символы».

Построение клиента проста:

dmd testdllc.d 

Обратите внимание, что есть прагма (ОМТ) в файле, который связывает с -ldl вариант автоматически. Запустите его и получите привет! BTW убедитесь, что оба находятся в одном каталоге, так как это загружает ./ в загрузчик.

Теперь давайте построим на Windows.

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

рассказывайте вывести нашу DLL, использовать -shared поэтому он знает, и тогда другое дело файл Защиту, как описано здесь http://dlang.org/dll.html/dllmain

Это содержимое этого файла:

LIBRARY testdll 

EXETYPE NT 
CODE SHARED EXECUTE 
DATA WRITE 

EXPORTS 
     lol 

Если вы не используете файл .def, dll будет успешно сгенерирована, но процедура не будет найдена, потому что она не экспортируется. (Я думаю, что ключевое слово export в D должно быть в состоянии сделать это автоматически, минуя hte .def файл, и я считаю, что есть дискуссия об этом, но сейчас это необходимо, насколько мне известно.)

И клиент так же легко:

dmd testdllc.d 

Выполнить это и получить некоторые приветы, если все идет хорошо.

Теперь, почему я сделал псевдоним functype в клиенте? Легче, чем делать другие кастинга и т. Д., И это делает его красивым внешним (C).

Почему внешний вид функции lol (C) в первую очередь? Просто он имеет более легкое имя для использования в GetProcAddress/dlsym. Может также иметь прагму (mangle) или сделать .mangleof с импортом. Всевозможные варианты там, довольно простые, я просто хотел, чтобы это упростило, чтобы сделать тест легче сосредоточиться. «lol» - это более простое имя, чем «_D7testdll3lolFZv», или как бы искомое имя не было ... (OMG я правильно рубил его! Иногда я думаю, что пишу слишком много D lol), и да, это тоже работает делать глазным яблоком. Примечание. В Windows, файл .def, возможно, придется оставить верхний знак подчеркивания, если вы это сделаете.

В любом случае, да, это создало рабочую dll/so для меня и программу для ее загрузки и использования. Не так красиво, как может/должно быть, но это работает. По крайней мере, для меня.

+0

Возможно, файл .def может не понадобиться. Я просто сделал несколько тестов с измененным именем, и он смог успешно загрузить его. Я думаю, что ошибка, которую я совершил, - это забыть о главном подчеркивании в GetProcAddress. Я также планирую отправить запрос на загрузку в druntime, который может предоставить бутылочный dllmain, который должен действительно свести на нет хлопот. –

Смежные вопросы