7. Если в пункте 4 воспользовались псевдонимами, удалить из .def-файла экспорта неиспользуемые наименования функций, оставив только псевдонимы.
8. Создать клиентский VC-проект.
9. Из .def-файла экспорта библиотеки при помощи утилиты lib.exe создать объектный .lib-файл формата COFF и добавить его к клиентскому VC-приложению.
10. Скопировать BCB dll и ее заголовочный файл в папку с клиентским VC-проектом.
11. В клиентском приложении подключить заголовочный файл dll.
12. Вызвать в теле программы необходимые функции, не задумываясь над тем, что они расположены во внешней dll.
Алгоритм с неявным связыванием для экспорта (импорта) __stdcall-функций
Как уже упоминалось выше, утилита lib.exe может создавать библиотеку импорта только из .def-файла экспорта, при чем lib.exe при этом никак не взаимодействует с самой dll. Однако .def-файл не содержит никакой информации, касаемой соглашений о вызове, которых придерживаются экспортируемые функции. Следовательно, и lib.exe, работая исключительно с .def-файлом, не сможет уловить, что имеет дело с __stdcall-функциями, и, как результат, не сможет в .lib-файле отобразить функции согласно Microsoft-соглашению о наименовании для __stdcall-функций. Таким образом, учитывая из предыдущего раздела, что для __cdecl-функций lib.exe генерирует вполне работоспособный .lib-файл, приходим к следующему выводу: утилита lib.exe не способна генерировать библиотеки импорта для dll, экспортирующих __stdcall-функции. Людям, пожелавшим или вынужденным (а после прочтения этого раздела думаю только вынужденным) использовать BCB dll с __stdcall-функциями в VC, этот раздел посвящается.
Исходный код BCB dll остался таким же, как в предыдущем разделе (см. Листинг 3), только ключевое слово __cdecl везде необходимо заменить ключевым словом __stdcall.
Известно, что при создании VC dll вместе с ней среда генерирует .lib-файл (библиотеку импорта), который представлен, естественно, в нужном нам формате COFF, и в котором корректно будут отображаться __stdcall-функции. Поэтомусоздадим (File -> New… -> Win32 Dynamic-Link Library -> OK -> An empty DLL project -> Finish) ложную(dummy) VC dll, которая будет экспортировать тот же набор функций, что и BCB dll. Реализация функций в ложной dll абсолютно не важна, важны исключительно их наименования. Помимо одинаковых наименований экспортируемых функций у ложной и исходной библиотек должны совпадать имена, поскольку .lib-файлы содержат наименования dll. Можно воспользоваться исходными текстами BCBdll, скопировав .h- и .cpp-файлы в директорию к ложной dll, затем добавив их к проекту (Project -> Add To Project -> Files…) и удалив тела всех функций. Если функция возвращает значение, то оставляем оператор return и возвращаем в соответствии с типом все, что угодно (можно 0, NULL и т.д.). Поскольку тела функций будут пустыми, большую часть директив #include с подключаемыми заголовочными файлами также можно удалить. В итоге получим согласно нашему примеру следующий код ложной dll:
Листинг 6 - Компилятор Visual C++ 6.0
ImplicitLinking_stdcallDummy.h
#ifdef _DLLEXPORT_ #define _DECLARATOR_ __declspec(dllexport)#else #define _DECLARATOR_ __declspec(dllimport)#endifextern "C"{ int _DECLARATOR_ __stdcall SumFunc(int a, int b); HWND _DECLARATOR_ __stdcall ViewStringGridWnd(int Count, double* Values);} |
ImplicitLinking_stdcallDummy.cpp
#define _DLLEXPORT_#include <windows.h>#include "ImplicitLinking_stdcallDummy.h"int __stdcall SumFunc(int a, int b){ return 0;}HWND __stdcall ViewStringGridWnd(int Count, double* Values){ return NULL; } |
Согласно таблице 1, VC экспортирует __stdcall-функции, добавляя к их наименованию информацию о списке аргументов и символ подчеркивания. Следовательно, в объектном .lib-файле будут имена, отличные от оригинальных имен функций, объявленных в заголовочном файле, и тем более отличные от наименований функций, экспортируемых из BCB dll, так как __stdcall-функции компилятор BCB экспортирует без изменений. Избавляться от этого несоответствия будем снова посредством .def-файла. Для нашего примера он будет следующим:
DummyDef.def
libRARY ImplicitLinking_stdcall.dllEXPORTS SumFuncViewStringGridWnd |
Строка с именем библиотеки (LIBRARY) в .def-файле не обязательна, но если она есть, то имя, указанное в ней, в точности должно совпадать с именами ложной и исходной dll. Добавляем .def-файл к VC-проекту, перекомпилируем и получаем ложную dll и необходимую нам библиотеку импорта, содержащую корректное описание экспортируемых __stdcall-функций. .lib-файл, доставшийся в наследство от ложной dll, должен добавляться (прилинковываться) к любому VC-проекту, который собирается использовать нашу исходную BCB dll.
Пример VC-приложения, импортирующего __stdcall-функции, такой же, как и в предыдущем разделе (см. Листинг 5). Не забудьте в примере подключить (#include) нужный заголовочный файл BCB dll и добавить к проекту нужную библиотеку импорта.
Алгоритм с неявным связыванием для экспорта (импорта) __stdcall-функций (см. также Демонстрационный проект, ImplicitLinkingDll_stdcall.zip):
Объявить экспортируемые функции как __stdcall.
Поместить объявления функций в блок extern ”С”. Не экспортировать классы и функции-члены классов.
Скомпилировать BCB dll.
Поскольку создать корректную библиотеку импорта с помощью утилиты lib.exe не удается, создать ложную VC dll, которая содержит такой же набор функций, как и исходная BCB dll.
Проверить идентичность названий ложной dll и dll исходной, названия должны совпасть.
Если для ложной библиотеки используются исходные тексты BCB dll, то удалить тела функций, если не используются, то создать пустые функции с такими же именами и сигнатурами, как в исходной dll.
Дабы предотвратить изменение имен функций при экспорте, добавить к VC-проекту ложной библиотеки .def-файл с секцией EXPORTS, в которой просто перечислены оригинальные наименования всех экспортируемых функций.
Скомпилировать ложную dll и получить необходимый .lib-файл с корректным отображением __stdcall-функций.
Создать клиентский VC-проект и добавить к нему полученный .lib-файл.
Скопировать BCB dll и ее заголовочный файл в папку с клиентским VC-проектом.
В клиентском приложении подключить заголовочный файл.
Вызвать в тексте программы необходимые функции, не задумываясь над тем, что они расположены во внешней dll.
Как вы могли убедиться, обеспечение успешного взаимодействия BCB dll и клиентского VC-приложения является нетривиальной задачей. Однако такое взаимодействие становится необходимым в случаях, когда использование VCL и C++ Builder-а при разработке отдельных частей приложения является более предпочтительным (например, в силу временных затрат). Используя описанные в статье алгоритмы, вы сможете создавать и успешно использовать BCB dll из VC-проекта.