45828 (665172)

Файл №665172 45828 (Создание в среде Borland C++ Builder dll, совместимой с Visual C++)45828 (665172)2016-07-31СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла

Создание в среде Borland C++ Builder dll, совместимой с Visual C++

Роман Мананников

Проблемы взаимодействия

Сложность использования dll, созданной с помощью Borland C++ Builder (далее BCB), в проектах, разрабатываемых в средах Microsoft, обусловлена тремя основными проблемами . Во-первых, Borland и Microsoft придерживаются разных соглашений о наименовании (naming convention) функции в dll. В зависимости от того, как объявлена экспортируемая функция, ее имя может быть дополнено компилятором определенными символами. Так, при использовании такого соглашения о вызове (calling convention), как __cdecl, BCB перед именем функции добавляет символ подчеркивания. Visual C++ (далее VC), в свою очередь, при экспорте функции как __stdcall добавит к ее имени помимо подчеркивания также информацию о списке аргументов (символ @ плюс размер списка аргументов в байтах).

ПРИМЕЧАНИЕ

Использование соглашения __stdcall означает, что вызываемая функция сама удалит из стека свои аргументы. Соглашение __cdecl, наоборот, обязывает очищать стек вызывающую функцию. Объявление функции как __cdecl приведет к некоторому (незначительному) увеличению размера конечного исполняемого файла, поскольку каждый раз после вызова этой функции требуется код по очистке стека, с другой стороны, именно из-за очистки стека вызывающей функцией допускается передача переменного числа параметров. В стек параметры и в том, и в другом случае помещаются справа налево.

В таблице 1 приведены возможные варианты наименований для экспортируемой функции MyFunction, объявленной следующим образом:

extern ”C” void __declspec(dllexport) MyFunction(int Param);

в зависимости от соглашения о вызове () и компилятора.

Соглашение о вызове

VC++

C++ Builder

__stdcall

_MyFunction@4

MyFunction

__cdecl

MyFunction

_MyFunction

Таблица 1. Наименования функций в зависимости от соглашения о вызове и компилятора.

Во-вторых, объектные двоичные файлы (.obj и .lib), создаваемые BCB, несовместимы с объектными файлами VC, и, следовательно, не могут быть прилинкованы к VC-проекту. Это означает, что при желании использовать неявное связывание (linking) c dll необходимо каким-то образом создать .lib-файл (библиотеку импорта) формата, которого придерживается Microsoft.

ПРИМЕЧАНИЕ

Следует отметить, что до появления 32-разрядной версии Visual C++ 1.0 компиляторы Microsoft использовали спецификацию Intel OMF (Object Module Format – формат объектного модуля). Все последующие компиляторы от Microsoft создают объектные файлы в формате COFF (Common Object File Format – стандартный формат объектного файла). Основной конкурент Microsoft на рынке компиляторов – Borland – решила отказаться от формата объектных файлов COFF и продолжает придерживаться формата OMF Intel. Отсюда и несовместимость двоичных объектных файлов.

В-третьих, классы и функции-методы классов, экспортируемые из BCB dll, не могут быть использованы в проекте на VC. Причина этого кроется в том, что компиляторы искажают (mangle) имена как обычных функций, так и функций-методов класса (не путайте с разными соглашениями о наименованиях). Искажение вносится для поддержки полиморфизма, то есть для того, чтобы различать функции с одинаковым именем, но разными наборами передаваемых им параметров. Если для обычных функций искажения можно избежать, используя перед определением функции директиву extern ”С” (но при этом, во-первых, на передний план выходит первая проблема – разные соглашения о наименовании функций в dll, а во-вторых, из двух и более функций с одинаковым именем директиву extern ”С” можно использовать только для одной из них, в противном случае возникнут ошибки при компиляции), то для функций-методов класса искажения имени неизбежны. Компиляторы Borland и Microsoft, как вы уже, вероятно, догадались, используют различные схемы внесения искажений. В результате VC-приложения попросту не видят классы и методы классов, экспортируемые библиотеками, скомпилированными в BCB.

ПРИМЕЧАНИЕ

От редакции: В частности, разновидностями полиморфизма времени компиляции являются перегрузка (ad-hoc полиморфизм) и шаблоны функций (параметрический полиморфизм).

Эти три проблемы осложняют использование BCB dll из приложений, созданных на VC, но все-таки это возможно. Ниже описаны три способа создания dll совместимой с VC и дальнейшего успешного использования этой dll.

Алгоритмы создания VC-совместимой dll и ее использование

Два из описанных в этом разделе алгоритмов применяют неявное связывание с dll, один – явную загрузку dll. Опишем сначала самый простой способ – использование BCB dll из проекта VC посредством ее явной загрузки в процессе выполнения программы.

Алгоритм с явной загрузкой dll

Применяя данную технику, нам не придется создавать совместимые с VC библиотеки импорта (.lib). Вместо этого добавится ряд действий по загрузке и выгрузке dll в приложении, ее использующем.

Создадим BCB dll (New -> DLL Wizard -> C++ -> Use VCL -> OK), экспортирующую для простоты всего две функции. Одна из функций будет вычислять сумму двух чисел и не будет использовать VCL-классы, а другая будет создавать окно и выводить в VCL-компонент TStringGrid элементы массива, переданного в качестве одного из аргументов.

ПРИМЕЧАНИЕ

Поскольку действия, производимые функциями, в нашем случае абсолютно не важны, данные примеры не несут смысловой нагрузки, однако стоит обратить внимание на функцию ViewStringGridWnd, которая показывает, что внутри самой dll использовать VCL-классы можно без каких-либо ограничений.

Листинг 1 - Компилятор Borland C++ Builder 5

ExplicitDll.h

#ifndef _EXPLICITDLL_

#define _EXPLICITDLL_

extern "C"

{

int __declspec(dllexport) __cdecl SumFunc(int a, int b);

HWND __declspec(dllexport) __stdcall ViewStringGridWnd(int Count,double* Values);

}

#endif

Ключевое слово __declspec с атрибутом dllexport помечает функцию как экспортируемую, имя функции добавляется в таблицу экспорта dll. Таблица экспорта любого PE-файла (.exe или .dll) состоит из трех массивов: массива имен функций (а точнее, массива указателей на строки, содержащие имена функций), массива порядковых номеров функций и массива относительных виртуальных адресов (RVA) функций. Массив имен функций упорядочен в алфавитном порядке, ему соответствует массив порядковых номеров функций. Порядковый номер после некоторых преобразований превращается в индекс элемента из массива относительных виртуальных адресов функций. При экспорте функции по имени имеет место следующая последовательность действий: по известному имени функции определяется ее индекс в массиве имен функций, далее по полученному индексу из массива порядковых номеров определяется порядковый номер функции, затем из порядкового номера, с учетом базового порядкового номера экспорта функций для данного PE-файла, вычисляется индекс, по которому из массива адресов извлекается искомый RVA функции. Помимо экспорта по имени возможен экспорт функций по их порядковым номерам (ordinal). В этом случае последовательность действий для получения индекса элемента из массива относительных виртуальных адресов сводится только к преобразованию порядкового номера функции. Для экспорта функций по номеру используется .def-файл с секцией EXPORTS, где за каждой функцией будет закреплен порядковый номер. При этом в тексте самой dll функции как экспортируемые не помечаются. Подробнее о таблице экспорта можно прочитать в статье по адресу http://www.rsdn.ru/article/baseserv/pe_coff.xml.

ExplicitDll.cpp

#include

#include

#include "ExplicitDll.h"

int __cdecl SumFunc(int a, int b)

{

return a + b;

}

HWND __stdcall ViewStringGridWnd(int Count, double* Values)

{

try

{

// создаем VCL-форму, на которой будет отображен StringGrid,

// и задаем ее основные параметры

TForm* GridForm = new TForm((TComponent *)NULL);

GridForm->Caption = "Grid Form";

GridForm->Width = 300;

GridForm->Height = 300;

// создаем компонент StringGrid и устанавливаем его размеры

TStringGrid *Grid = new TStringGrid(GridForm);

Grid->ColCount = Count + 1;

Grid->RowCount = Count + 1;

// заполняем StringGrid значениями

if (Values != NULL)

for (int i = 0; i < Count; i++)

Grid->Cells[i + 1][i + 1] = Values[i];

// задаем параметры отображения StringGrid в родительском окне

Grid->Parent = GridForm;

Grid->Align = alClient;

// показываем VCL-форму

GridForm->Show();

// возвращаем хэндл VCL-окна клиентскому приложению,

// дабы оно могло это окно при необходимости закрыть

return GridForm->Handle;

}

catch(...)

{

return NULL;

}

}

#pragma argsused

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)

{

return 1;

}

Проанализируем сформированные компилятором наименования экспортируемых функций. Воспользовавшись утилитой impdef.exe, поставляемой совместно с C++Builder (находится в каталоге $(BCB)\Bin, синтаксис командной строки – impdef.exe ExplicitDll.def ExplicitDll.dll), получим следующий .def-файл

ExplicitDll.def

LIBRARY EXPLICITDLL.DLL

EXPORTS

ViewStringGridWnd @1 ; ViewStringGridWnd

_SumFunc @2 ; _SumFunc

___CPPdebugHook @3 ; ___CPPdebugHook

Поскольку в данном примере экспортируемая функция ViewStringGridWnd использует соглашение __stdcall, ее имя осталось неизменным (см. таблицу 1), следовательно, для вызова этой функции VC-приложение воспользуется именем ViewStringGridWnd (например, при вызове GetProcAddress), а вот для вызова функции SumFunc использовать придется имя _SumFunc. Очевидно, что осуществлять вызов функции, пользуясь ее измененным именем, неудобно само по себе, а тем более, если dll пишет один программист, а работает с ней другой. Для того чтобы при использовании __cdecl-соглашения экспортируемые функции можно было использовать с их истинными именами (без символов подчеркивания), необходимо об этом позаботиться заранее, то есть на этапе создания самой dll. Для этого создается .def-файл (это можно сделать в любом текстовом редакторе), в котором определяется секция EXPORTS, содержащая псевдоним (alias) для каждой экспортируемой __cdecl-функции. В нашем случае он будет выглядеть следующим образом

ExplicitDllAlias.def

EXPORTS

; VC funcname = BCB funcname

SumFunc = _SumFunc

То есть, у функции, экспортируемой как _SumFunc, будет псевдоним SumFunc, который мы исключительно для удобства делаем идентичным оригинальному имени этой функции в коде (хотя псевдоним может быть каким угодно).

Созданный .def-файл добавляется (Project -> Add to Project) к проекту dll. После компиляции, проанализировав dll c помощью impdef.exe, получим следующее

ExplicitDll.def

libRARY EXPLICITDLL.DLL

EXPORTS

SumFunc @4 ; SumFunc

ViewStringGridWnd @2 ; ViewStringGridWnd

_SumFunc @1 ; _SumFunc

___CPPdebugHook @3 ; ___CPPdebugHook

Имеем на одну экспортируемую функцию больше, но при этом реальное количество функций в dll осталось неизменным, а функция с именем SumFunc (функция-псевдоним) является ссылкой на свой оригинал, то есть на функцию, экспортируемую под именем _SumFunc.

ПРИМЕЧАНИЕ

Более правильным будет сказать, что функция-псевдоним попросту добавляется в таблицу экспорта dll: ее имя SumFunc добавляется в массив имен функций, а в массив порядковых номеров добавляется присвоенный ей порядковый номер. Однако соответствующий функции-псевдониму RVA в массиве относительных виртуальных адресов будет равен RVA функции с именем _SumFunc. Убедиться в этом можно последовательно вызывая GetProcAddress для имен функций SumFunc и _SumFunc и анализируя возвращаемый адрес (можно, разумеется, воспользоваться различными программами, позволяющими просмотреть содержимое исполняемого файла). В обоих случаях адрес функции будет одинаков.

Таким образом, с помощью .def-файла псевдонимов при экспорте функций, определенных как __cdecl, мы избавляем пользователей от необходимости вызова функций по их измененным именам, хотя и такая возможность остается.

ПРЕДУПРЕЖДЕНИЕ

Поскольку __stdcall- и __cdecl-функции по-разному работают со стеком, не пытайтесь из клиентского приложения вызывать __stdcall-функции как __cdecl, и наоборот, иначе стек будет поврежден, и дальнейшее выполнение приложения будет невозможно.

В результате изложенного выше мы получили dll, экспортирующую функции с именами SumFunc и ViewStringGridWnd. При этом их названия не зависят от того, какое соглашение о вызове использовалось при объявлении этих функций. Теперь рассмотрим пример использования нашей dll в приложении VC. Создадим в среде Visual C++ 6.0 (или Visual C++ 7.0) простое MFC-приложение, которое будет представлять собой обычное диалоговое окно (File -> New -> MFC AppWizard(exe) -> Dialog based -> Finish). Добавим к исходному диалогу две кнопки: кнопку “SumFunc” и кнопку “ViewStringGridWnd”. Затем для каждой кнопки создадим обработчик события BN_CLICKED: OnSumFunc() и OnViewStringGridWnd() соответственно. Нам также понадобятся обработчики сообщений для событий формы WM_CREATE и WM_DESTROY. Полный рабочий код этого приложения находится в примерах к статье, здесь же будет приведена только часть, демонстрирующая работу с нашей dll, поскольку оставшаяся часть кода генерируется средой разработки.

Листинг 2 - Компилятор Visual C++ 6.0

Характеристики

Тип файла
Документ
Размер
167,04 Kb
Тип материала
Учебное заведение
Неизвестно

Тип файла документ

Документы такого типа открываются такими программами, как Microsoft Office Word на компьютерах Windows, Apple Pages на компьютерах Mac, Open Office - бесплатная альтернатива на различных платформах, в том числе Linux. Наиболее простым и современным решением будут Google документы, так как открываются онлайн без скачивания прямо в браузере на любой платформе. Существуют российские качественные аналоги, например от Яндекса.

Будьте внимательны на мобильных устройствах, так как там используются упрощённый функционал даже в официальном приложении от Microsoft, поэтому для просмотра скачивайте PDF-версию. А если нужно редактировать файл, то используйте оригинальный файл.

Файлы такого типа обычно разбиты на страницы, а текст может быть форматированным (жирный, курсив, выбор шрифта, таблицы и т.п.), а также в него можно добавлять изображения. Формат идеально подходит для рефератов, докладов и РПЗ курсовых проектов, которые необходимо распечатать. Кстати перед печатью также сохраняйте файл в PDF, так как принтер может начудить со шрифтами.

Список файлов реферата

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6508
Авторов
на СтудИзбе
302
Средний доход
с одного платного файла
Обучение Подробнее