45828 (665172), страница 2

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

Текст из файла (страница 2)

UsingExplicitDLLDlg.cpp

// код, генерируемый средой разработки

// хэндл тестируемой DLL

HINSTANCE hDll = NULL;

// тип указателя на функцию ViewStringGridWnd

typedef HWND (__stdcall *ViewStringGridWndProcAddr) (int Count, double* Values);

// хэндл окна с VCL-компонентом StringGrid

HWND hGrid = NULL;

// тип указателя на функцию SumFunc

typedef int (__cdecl *SumFuncProcAddr) (int a, int b);

// код, генерируемый средой разработки

// обработчик нажатия кнопки SumFunc

void CUsingExplicitDLLDlg::OnSumFunc()

{

// указатель на функцию SumFunc

SumFuncProcAddr ProcAddr = NULL;

if( hDll != NULL )

{

// получение адреса функции

ProcAddr = (SumFuncProcAddr) GetProcAddress(hDll, "SumFunc");

if( ProcAddr != NULL )

{

// вызов функции

int result = (ProcAddr)(5, 6);

// отображение результата в заголовке диалога

char str[10];

this->SetWindowText(itoa(result, str ,10));

}

}

}

// обработчик нажатия кнопки ViewStringGridWnd

void CUsingExplicitDLLDlg::OnViewStringGridWnd()

{

// указатель на функцию ViewStringGridWnd

ViewStringGridWndProcAddr ProcAddr = NULL;

if( hDll != NULL )

{

// получение адреса функции

ProcAddr = (ViewStringGridWndProcAddr) GetProcAddress(hDll,

"ViewStringGridWnd");

if( ProcAddr != NULL )

{

// инициализация аргументов

const int count = 5;

double Values[count] = {2.14, 3.56, 6.8, 8, 5.6564};

// закрываем ранее созданное окно, чтобы они не плодились

if( hGrid != NULL )

::SendMessage(hGrid, WM_CLOSE, 0, 0);

// вызов функции

hGrid = (ProcAddr)(count, Values);

}

}

}

// обработчик события окна WM_DESTROY

void CUsingExplicitDLLDlg::OnDestroy()

{

CDialog::OnDestroy();

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

if( hGrid != NULL )

::SendMessage(hGrid, WM_CLOSE, 0, 0);

// выгрузка dll из памяти

FreeLibrary( hDll );

}

// обработчик события окна WM_CREATE

int CUsingExplicitDLLDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CDialog::OnCreate(lpCreateStruct) == -1)

return -1;

// загрузка dll в память

hDll = LoadLibrary("ExplicitDll.dll");

return 0;

}

Явная загрузка dll имеет как преимущества, так и недостатки. В нашем случае большим плюсом является то, что явная загрузка избавляет от какого бы то ни было взаимодействия с исходным кодом dll, в частности нет необходимости подключать заголовочный .h-файл с объявлениями функций. Клиентское приложение компилируется и работает независимо от используемой dll, а случаи неудачной загрузки библиотеки или неудачного получения адреса функции всегда можно обыграть так, чтобы они не повлияли на дальнейшее выполнение основного приложения.

ПРИМЕЧАНИЕ

Следует отметить, что использование экспортируемых unmanaged-функций из управляемого кода (managed code) в .NET осуществляется исключительно посредством явной загрузки dll. К процессу вызова функции в этом случае помимо стандартных шагов (таких как загрузка dll в память посредством LoadLibrary, получение адреса требуемой функции с помощью GetProcAddress и непосредственно вызов), добавляется также процесс маршалинга (marshaling), то есть процесс преобразования типов данных .NET в их аналоги в традиционном двоичном коде (при проталкивании аргументов в стек) и обратно (при анализе возвращаемого значения). Для указания, что метод импортируется из dll, используется атрибут DllImport, параметры которого содержат информацию, необходимую для вызова LoadLibrary и GetProcAddress.

Таким образом, для вызова экспортируемой функции из dll, скомпилированной в BCB, необходимо выполнить следующую последовательность действийя:

Объявить экспортируемые функции либо как __cdecl, либо как __stdcall. Если используется только соглашение __stdcall, пропускаем пункт 3.

Поместить объявления функций в блок extern ”С”. Не экспортировать классы и функции-члены классов, поскольку это все равно не удастся.

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

Откомпилировать dll.

Создать клиентский (то есть использующий BCB библиотеку) VC-проект.

Скопировать созданную BCB dll в папку с клиентским VC-приложением.

Загрузить dll из клиентского приложения в память при помощи LoadLibrary.

Получить адрес требуемой функции с помощью GetProcAddress и присвоить его указателю на функцию.

Вызвать функцию с помощью указателя на нее.

По окончании использования выгрузить dll из памяти с помощью FreeLibrary.

Алгоритм с неявным связыванием для экспорта (импорта) __cdecl-функций

Как следует из названия раздела, данный способ предназначен для экспорта (а на клиентской стороне – для импорта) функций с __cdecl-соглашением о вызове. Чтобы воспользоваться неявным связыванием, прежде всего, необходимо создать объектный .lib-файл (библиотеку импорта), содержащий ссылку на dll и перечень находящихся в dll функций. Данный объектный файл можно создать по .def-файлу экспорта библиотеки с помощью утилиты lib.exe. При этом полученный .lib-файл будет в нужном нам формате COFF, поскольку компилятор VC придерживается именно этой спецификации (утилита lib.exe поставляется совместно с VC и умеет создавать библиотеки импорта только по .def-файлу). Готовый .lib-файл прилинковывается к клиентскому проекту.

При неявном связывании приложение не подозревает, что использует dll, поэтому функции, вызываемые из динамической библиотеки, как и любые другие, должны быть объявлены в тексте клиентской программы. Для объявления функций воспользуемся исходным заголовочным файлом BCB dll, но функции в нем должны быть помечены уже не как __declspec(dllexport), а как __declspec(dllimport), то есть как импортируемые извне, поскольку по отношению к клиентскому приложению эти функции являются именно импортируемыми.

Исходный текст dll на этот раз будет выглядеть следующим образом:

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

ImplicitLinking_cdecl.h

#ifndef _IMPLICITDLL_

#define _IMPLICITDLL_

// если макрос-идентификатор _DLLEXPORT_ был определен ранее,

// то макрос _DECLARATOR_ пометит функцию как экспортируемую,

// в противном случае функция будет помечена как импортируемая.

// Данная конструкция из директив препроцессора позволяет

// воспользоваться заголовочным файлом библиотеки как на этапе

// создания DLL, так и на этапе ее использования, а именно, при

// неявном связывании.

#ifdef _DLLEXPORT_

#define _DECLARATOR_ __declspec(dllexport)

#else

#define _DECLARATOR_ __declspec(dllimport)

#endif

extern "C"

{

int _DECLARATOR_ __cdecl SumFunc(int a, int b);

HWND _DECLARATOR_ __cdecl ViewStringGridWnd(int Count, double* Values);

}

#endif

ImplicitLinking_cdecl.cpp

#include

#include

// определение _DLLEXPORT_, дабы вместо макроса _DECLARATOR_

// в заголовочном файле было подставлено __declspec(dllexport),

// и функции были объявлены как экспортируемые

#define _DLLEXPORT_

#include "ImplicitLinking_cdecl.h"

int __cdecl SumFunc( int a, int b )

{ // тело функции такое же как в предыдущем разделе

}

HWND __cdecl ViewStringGridWnd( int Count, double* Values )

{ // тело функции такое же как в предыдущем разделе

}

#pragma argsused

int WINAPI DllEntryPoint(HINSTANCE hinst,

unsigned long reason,

void* lpReserved)

{

return 1;

}

Основная возникающая при этом проблема заключается в том, что, согласно таблице 1, функции с __cdecl-соглашением о вызове будут экспортироваться с символом подчеркивания, следовательно, .lib-файл, созданный по .def-файлу экспорта библиотеки, будет содержать измененные имена функций. С другой стороны, во-первых, компилятор VC будет ожидать неизмененных наименований __cdecl-функций, потому что сам VC, экспортируя функции с __cdecl-соглашением о вызове, ничего к их наименованию не добавляет, а во-вторых, заголовочный файл BCB dll, подключаемый к клиентскому приложению, содержит объявления функций с их реальными (без символа подчеркивания) именами. В результате этого, если в тексте клиентского приложения встретится хотя бы один вызов нашей функции, то VC при связывании попытается найти описание этой импортируемой функции в добавленной к проекту библиотеке импорта (.lib-файле), с тем, чтобы добавить соответствующую запись в таблицу импорта приложения. Но из-за несоответствия имен функций в заголовочном и объектном файлах линковщик, естественно, в .lib-файле ничего не найдет, о чем не замедлит выдать сообщение (например, такое - error LNK2001: unresolved external symbol __imp__SumFunc).

ПРИМЕЧАНИЕ

Таблица импорта любого PE-файла содержит массив структур IMAGE_IMPORT_DESCRIPTOR. Каждая такая структура соответствует одной из dll, с которой неявно связан PE-файл. Структура IMAGE_IMPORT_DESCRIPTOR среди прочих полей содержит поле с RVA строки-наименования dll, которой она соответствует, и два поля с RVA массивов двойных слов, предназначенных для хранения информации об импортируемых функциях. При запуске приложения загрузчик PE-файлов заполняет один из этих массивов (так называемую таблицу адресов импорта) адресами импортируемых функций, загрузив перед этим dll, в которой эти функции находятся. Адрес импортируемой функции вычисляется как сумма адреса, по которому была загружена экспортирующая данную функцию dll, и смещения (RVA) самой функции относительно начала dll.

Описанную выше проблему несоответствия заголовочного и объектного файлов можно решить двумя способами – снова воспользоваться рассмотренным в предыдущем разделе .def-файлом с псевдонимами или использовать в заголовочном файле нашей библиотеки директиву препроцессора #define.

Использование псевдонимов

Следуя этому способу, создаем и добавляем к проекту BCB dll следующий .def-файл:

ImplicitLinkingAliases.def

EXPORTS

; MSVC name = Borland name

SumFunc = _SumFunc

ViewStringGridWnd = _ViewStringGridWnd

После компиляции наша dll будет экспортировать функции

ImplicitLinking_cdecl.def

libRARY IMPLICITLINKING_CDECL.DLL

EXPORTS

SumFunc @4 ; SumFunc

ViewStringGridWnd @5 ; ViewStringGridWnd

_SumFunc @1 ; _SumFunc

_ViewStringGridWnd @2 ; _ViewStringGridWnd

___CPPdebugHook @3 ; ___CPPdebugHook

Таким образом, в таблицу экспорта dll добавляются функции-псевдонимы, имена которых соответствуют функциям, объявленным в заголовочном файле нашей библиотеки. Для полного соответствия (хотя этого можно и не делать) удалим из ImplicitLinking_cdecl.def упоминания обо всех посторонних для приложения-клиента функциях, так как заголовочный файл содержит объявления только двух функций. В результате получим .def-файл готовый для генерации из него объектного .lib-файла:

ImplicitLinking_cdecl.def

libRARY IMPLICITLINKING_CDECL.DLL

EXPORTS

SumFunc @4 ; SumFunc

ViewStringGridWnd @5 ; ViewStringGridWnd

ПРИМЕЧАНИЕ

В единственной статье, которую мне удалось найти по данной теме (на сайте bcbdev.com), рекомендовалось, помимо удаления из .def-файла посторонних функций, заменить наименование секции EXPORTS на IMPORTS. Делать этого не следует по той простой причине, что утилита lib.exe (по крайней мере, поставляемая с 6-ой и 7-ой Visual Studio) секцию IMPORTS не поддерживает, поэтому игнорирует все последующие описания функций и создает пустой .lib-файл. Утилита lib.exe находится в каталоге $(VC)\Bin, но запустить ее обычно с первого раза не удается, поскольку для работы ей требуется библиотека mspdb60.dll (для lib.exe, поставляемой с Visual Studio 7 – mspdb70.dll). mspdb60.dll лежит в папке $(Microsoft Visual Studio)\Common\MSDev98\Bin, а mspdb70.dll – в папке $(Microsoft Visual Studio .NET)\Common7\IDE.

С помощью утилиты lib.exe создадим необходимый для неявного связывания .lib-файл в формате COFF, для этого в командной строке наберем

lib.exe /def:ImplicitLinking_cdecl.def

либо

lib.exe /def:ImplicitLinking_cdecl.def /out:ImplicitLinking_cdecl.lib

Полученный .lib-файл добавим к проекту VC-клиента (Project -> Add To Project -> Files…).

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

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

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

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