Страницы

Поиск по вопросам

среда, 25 декабря 2019 г.

Как создать библиотеку импорта для существующей DLL

#cpp #windows #visual_studio #dll


Есть несколько DLL, требуется использовать функции из них в проекте на Си++. Доступа
ни к исходникам ни к разработчикам у меня нет. То есть в наличии просто набор DLL как
вешь в себе и документ с описанием интерфейса к ним.

Сейчас я получаю адреса функций вручную c помощью LoadLibrary и GetProcAdrress, но
под мою задачу было бы удобнее иметь библиотеки импорта для них. Чтобы никаких телодвижений
по инициализации не делать, не следить за ними и так далее. Так вот, как библиотеки
импорта создать и вообще как они работают?
    


Ответы

Ответ 1



В принципе, это неподдерживаемый сценарий: например, функции могут быть декорированы нестандартным образом, или иметь нетрадиционное соглашение о вызове. Но тем не менее, если вы знаете точную сигнатуру функций, то можно попытаться сделать, как описано здесь. Идея №1: создать DEF-файл вручную. Это подойдёт, если функции, которые вы импортируете, есть C-функции, и вы знаете их calling convention (например, __cdecl или PASCAL (__stdcall)). Для начала, установим, что это за функции. Для этого можно воспользоваться стандартной утилитой dumpbin с ключом /exports (не забывайте, что её нужно запускать из-под Visual Studio command prompt) или очень полезной при нативной разработке под Windows-платформу утилитой depends.exe. Для dumpbin вы получите примерно такой вывод: ...> dumpbin /exports "C:\Program Files\Far\Plugins\arclite\arclite.dll" Microsoft (R) COFF/PE Dumper Version 11.00.61030.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file C:\Program Files\Far3\Plugins\arclite\arclite.dll File Type: DLL Section contains the following exports for arclite.dll 00000000 characteristics 54BA6E68 time date stamp Sat Jan 17 15:15:04 2015 0.00 version 1 ordinal base 19 number of functions 19 number of names ordinal hint RVA name 2 0 00016170 AnalyseW 3 1 00016920 CloseAnalyseW 4 2 000175F0 ClosePanelW 5 3 000182A0 ConfigureW 6 4 00017DA0 DeleteFilesW 1 5 000185E0 ExitFARW 7 6 00017B00 FreeFindDataW 8 7 00017B60 GetFilesW 9 8 000179F0 GetFindDataW 10 9 00015F20 GetGlobalInfoW 11 A 000177C0 GetOpenPanelInfoW 12 B 00016030 GetPluginInfoW 13 C 00017EC0 MakeDirectoryW 14 D 00016970 OpenW 15 E 00017FD0 ProcessHostFileW 16 F 00018180 ProcessPanelInputW 17 10 00017C80 PutFilesW 18 11 000178A0 SetDirectoryW 19 12 00015FB0 SetStartupInfoW Summary 5000 .data 16000 .rdata 9000 .reloc 1000 .rsrc 87000 .text Отсюда вы берёте имена функций: AnalyseW, CloseAnalyseW и т. д., и превращаете их в .def-файл: EXPORTS ; EntryName [=InternalName] [@Ordinal] [NONAME] [CONSTANT] AnalyseW CloseAnalyseW Так работает для функций с calling convention __cdecl (она обычно принята по умолчанию). Для других вы должны задекорировать имя функции самостоятельно, согласно таблице: | Calling convention | extern "C" or .c file | .cpp, .cxx or /TP | ------------------------------------------------------------------------------------- | C convention (__cdecl) | _test | ?test@@ZAXXZ | | Fastcall convention (__fastcall) | @test@0 | ?test@@YIXXZ | | Standard call convention (__stdcall) | _test@0 | ?test@@YGXXZ | | Vector call convention (__vectorcall) | test@@0 | ?test@@YQXXZ | — и прописать их как алиасы: EXPORTS ; EntryName [=InternalName] [@Ordinal] [NONAME] [CONSTANT] CdeclFunction PascalFunction=_PascalFunction@8 (число после @ означает количество байт в стеке, отводимое под параметры). Теперь можно использовать команду lib /def:yourfile.def из командной строки Visual Studio, чтобы построить .exp и .lib для линковки. Идея №2 — это создать фейк-библиотеку с такими же сигнатурами функций, построить её, и использовать её .exp и .lib вместо отсутствующих. Для этого вам придётся построить С- (или хуже того, C++-) сигнатуры нужных функций. Для сишных функций (ну или тех, которые были определены как extern "C" в исходнике) это просто: вы смотрите на декорированное имя (например, в том же depends.exe или dumpbin), определяете по таблице сверху их calling convention, и кладёте такую функцию в исходник. Не забудьте указать __declspec(dllexport) для всех, и добавочно extern "C" для сишных функций. Реализуйте функции как угодно, чтобы компилятор скомпилировал это. Если у вас декорированные C++-сигнатуры (они выглядят как-то так: ?test@@YGXXZ) их можно превратить в правильные C++-декларации при помощи утилиты undname: ...> undname ?test@@YGXXZ Microsoft (R) C++ Name Undecorator Copyright (C) Microsoft Corporation. All rights reserved. Undecoration of :- "?test@@YGXXZ" is :- "void __stdcall test(void)" Если у вас есть header, идущий с библиотекой, то ничего угадывать (с риском ошибиться и получить креш) не надо, просто возьмите прототипы функций оттуда (только не забудьте поменять __declspec(dllimport) на __declspec(dllexport)). Скомпилируйте полученный файл в .obj при помощи команды cl /c /Ob0 dllname.cpp Ключ /c нужен, чтобы компилятор произвёл .obj, а /Ob0 — чтобы не занилайнил случайно какие-нибудь функции, которые ему покажутся ненужными (это может быть нужно в случае экспорта классов, и в любом случае не повредит). Имея .obj, вы можете получить .lib и .def при помощи команды lib /def: dllname.obj

Комментариев нет:

Отправить комментарий