C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Подключение DLL, Подключение DLL
** Jack
Отправлено: 07.12.2004, 12:04


Не зарегистрирован







Срочно пож.
Как подключить внешнюю DLL к проекту ?
joynter
Отправлено: 07.12.2004, 12:23


Станционный диспетчер

Группа: Участник
Сообщений: 137



1. объявляешь прототипы функций
2. LoadLibrary("my_lib.dll")
3. вызываешь функции
4. FreeLibrary
** Jack
Отправлено: 07.12.2004, 16:34


Не зарегистрирован







Хотелось бы по подробней, если не трудно
joynter
Отправлено: 07.12.2004, 17:01


Станционный диспетчер

Группа: Участник
Сообщений: 137



совсем не трудно
QUOTE

Создание DLL с помощью C++Builder.

( выдержки из книги "Borland C++Builder 6. Руководство
разработчика" за авторством коллектива в составе: Джаррод Холингворт, Боб Сворт,
Марк Кэшмэн, Поль Густавсон)

В среде С++ Билдер ДЛЛ можно создать двумя способами:
1. С использованием Мастера (Wizard).
2. "С нуля", т.е. начиная с абсолютно пустых файлов.

Первая функция, которая должна присутствовать в любой ДЛЛ, — главная точка
входа. У Майкрософт эта ф-ция именуется DllMain().
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
return 1;
}
В мире Борланда эта ф-ция называется DllEntryPoint().
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void *lpReserved)
{
return 1;
}

NB!!! Начиная с версии 6.0 С++Билдер поддерживает обе ф-ции. В целях бОльшей
совместимости рекомендуется использовать ф-цию DllMain().

1. Использование Мастера.

На первом шаге Мастер создания ДЛЛ предложит выбрать несколько опций:
- C. При выборе этого переключателя в качестве исходного языка будет
использоваться стандартный Си. Доступны опции MultiThread, VC++ Style DLL,
но блокированы Use VCL Use CLX.
- С++. Все опции доступны. При компиляции используется компилятор С++ со
всеми вытекающими.
- Use VCL. Данный флажок позволяет использовать VCL при написании кода ДЛЛ.
- Use VCL. Данный флажок позволяет использовать CLX при написании кода ДЛЛ.
/*При подключении любой из двух вышеуказанных библиотек автоматически
устанавливается флаг MultiThread и его невозможно сбросить.*/
- MultiThread. Подключение/отключение поддержки многопоточности.
- VC++ Style DLL. Задаёт имя точки входа в ДЛЛ. См. выше.

После того, как Вы установите все необходимые опции и нажмёте ОК, С++Билдер
автоматически сгенерирует каркас ДЛЛ-приложения. При этом в исходном файле
генерируется пустая ф-ция — точка входа в библиотеку.

При использовании Мастера создаётся несколько файлов: *.c (или *.cpp),
файл ресурсов *.res и файл проекта *.bpf. В большинстве случаев имеет значение
только файл с исходным текстом и связанный с ним заголовочный файл, который
Мастером НЕ создаётся. Заголовочный файл необходим для предоставления всей
функциональности библиотеки для других приложений.

2. НАПИСАНИЕ КОДА ДЛЛ.

Для лучшего понимания работы ДЛЛ, заполним ф-цию точки входа (simple.c):
//----------------------------- simple.c --------------------------------------
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdReason, LPVOID lpvReserved)
{
   switch(fwdReason)
   {
case DLL_PROCESS_ATTACH:
           if (lpvReserved)
                 MessageBox(NULL,"Process has attached to DLL (Static Load)",
                     "DLLMain",MB_OK);
           else
                 MessageBox(NULL,"Process has attached to DLL (Dynamic Load)",
                       "DLLMain",MB_OK);
           break;
case DLL_THREAD_ATTACH:
           MessageBox(NULL,"Thread has attached to DLL","DLLMain",MB_OK);
           break;
case DLL_THREAD_DETACH:
           MessageBox(NULL,"Thread has detached to DLL","DLLMain",MB_OK);
           break;
case DLL_PROCESS_DETACH:
           MessageBox(NULL,"Process has detached to DLL","DLLMain",MB_OK);
           break;
   }

   return 1; // always return true;
}
//-----------------------------------------------------------------------------

В этом примере ДЛЛ выполняет действия, связанные с загрузкой/выгрузкой ДЛЛ. Ф-ция
точки входа передаются три параметра. Первый параметр, hinstDLL, указывает деск-
риптор ДЛЛ. Второй, fwdReason, указывает тип действий, связанных с загрузкой
или выгрузкой процесса или потока. Третий, lpvReserved, равен нулю, если ДЛЛ за-
гружается динамически, и не равна нулю в противном случае. Больше всего в дан-
ном примере нас интересует значение, передаваемое посредством fwdReason. Зная о
том, что именно происходит, можно корректно инициализировать переменные ДЛЛ при
её загрузке, и освободить занятые ресурсы при выгрузке.
Для того, чтобы ДЛЛ была полезна для других приложений, в нее следует добавить
некоторую функциональность. Добавим в файл с исходным кодом simple.c ф-ции,
указывающие версию ДЛЛ, а также выполняющие преобразования единиц длины:
//------------------------ simple.c -------------------------------------------
const double version = 1.1;

double simpleGetLibVersion()  //Возвращает текущую версию ДЛЛ.
{
return version;
}

//-----------------------------------------------------------------------------

double feet_to_meters(double feet)
{
return (feet * 0.3048);
}

//-----------------------------------------------------------------------------

double meters_to_feet(double meters)
{
return (meters * 3.2808);
}
//-----------------------------------------------------------------------------
Обратите внимание на константу version. Когда приложение вызывает ф-цию ДЛЛ
simpleGetLibVersion(), возвращается значение, идентифицируемое этой константой.
При вызове приложением ф-ции feet_to_meters() значение, передаваемое как пара-
метр feet, преобразуется в метры и полученное значение возвращается вызывающей
ф-ции. Ф-ция meters_to_feet() выполняет обратное преобразование.

3. ЗАГОЛОВОЧНЫЙ ФАЙЛ ДЛЛ.

Для того, чтобы ф-ции из ДЛЛ были доступны внешним приложениям, необходимо
создать заголовочный файл (хеадер), который показывает, какие именно ф-ции ДЛЛ
доступны внешним программам.
Simple.h:
//------------------ Simple.h -------------------------------------------------
#ifndef __SIMPLE_H
#define __SIMPLE_H

#if defined(__cplusplus)
extern "C"
{
#endif
__declspec( dllexport ) double simpleGetLibVersion();
__declspec( dllexport ) double feet_to_meters(double feet);
__declspec( dllexport ) double meters_to_feet(double meters);
#if defined(__cplusplus)
}
#endif

#define WM_FRUIT_CHANGE  (WM_USER + 1001)
#endif // __SIMPLE_H
//-----------------------------------------------------------------------------
Хеадер предоставляет основу интерфейса ДЛЛ. Значение __cplusplus определяет-
ся C++Билдер при компиляции кода С++. В данном примере при компиляции исходного
кода компилятором языка С выражение extern "C" будет проигнорировано; если же
будет использоваться компилятор С++ (вощем чё выбрал в Мастера wink.gif), ключевое
слово extern "C" сообщит С++Билдер о необходимости использовать соглашения о
вызовеах, принятые в языке С. Т.о., любой компилятор, поддерживающий экспорт
С — ф-ций, будет способен импортировать указанные таким образом ф-ции.
В приведённом примере используется ещё один описатель-__declspec(dllexport),
который позволяет экспортировать ф-ции, данные и обьекты из ДЛЛ. Здесь экспор-
тируются ф-ции, которые должны вызываться из любого приложения, загрузившего
данную ДЛЛ. Кроме того, это описание обеспечивает совместимость данной ДЛЛ с
приложениями Microsoft C/C++ и устраняет необходимость в файле определения мо-
дуля *.def.

4. СБОРКА ДЛЛ.

Сборка ДЛЛ в среде С++Билдер практически ничем не отличается от сборки ис-
полняемого файла. Для этого достаточно выбрать пункт меню Project->Build.
Если Вы собираетесь загружать ДЛЛ статически, то желательно установить опцию
Generate .lib file на вкладке Linker опций проекта. При этом отпадает необходи-
мость использования утилиты implib для получения .lib файла, необходимого для
сборки исполняемого файла при статической загрузке ДЛЛ.

5. СПОСОБЫ ЗАГРУЗКИ ДЛЛ.

Как уже упоминалось, ДЛЛ может быть загружена либо статически, либо динами-
чески. Очень важно понимать разницу между способами загрузки. Статически загру-
жаемая ДЛЛ связывается с приложением при сборке последнего путём указания соот-
ветствующей библиотеки (lib) как части проекта; вызов ф-ций из ДЛЛ при этом ни-
чем не отличается от обычного вызова. В этом случае ДЛЛ загружается в
память при запуске приложения. Динамически всязываемая ДЛЛ может загружаться и
выгружаться при необходимости; в этом случае приложение предьявляет меньше тре-
бований к ресурсам, а обращение к ф-циям ДЛЛ несколько усложнено.
Ещё одним отличием между статической и динамической загрузкой является то, что
при статическом связывании программа не может начать работу без ДЛЛ, в то время
как при динамическом связывании наличие ДЛЛ для запуска приложения не обяза-
тельно. Однако если при этом приложение попытается вызвать ф-цию из отсутствую-
щей ДЛЛ или воспользуется расположенным в ней ресурсом, произойдёт ошибка.

6. СТАТИЧЕСКОЕ СВЯЗЫВАНИЕ.

Для понимания процесса создадим приложение, использующее ранее написанную ДЛЛ
simple.dll. ДЛя этого создается демо проект AppStatic.bpr, в который включается
ранее полученный lib — файл (если Вы не пользуетесь Менеджером проектов (да и
при его использовании тоже моежно) это делается командой меню Project->Add to
Project... Shift+F11). В данном примере форма содержит Кнопки и Поля ввода (Edit).
При нажатии на кнопку Get DLL Version из ДЛЛ вызывается ф-ция simpleGetLibVersion()
и заполняет соответствующее поле ввода полученным значением. Нажатие на кнопку
Convert To Meters приведёт к пересчету значения из соответствующего поля ввода в
метры. Нажав на Convert To Feet Вы преобразуете сответствующее число из метров в
футы.
//--------------------- AppStaticForm.cpp -------------------------------------

#include
#pragma hdrstop

#include "AppStaticForm.h"
#include "simple.h"
//-----------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-----------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
{
}
//-----------------------------------------------------------------------------

void __fastcall TForm1::ButtonGetVersionClick(TObject *Sender)
{
 EditVersion->Text = AnsiString(simpleGetLibVersion());
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ButtonConvertToMetersClick(TObject *Sender)
{
    double meters =  feet_to_meters(EditFeet->Text.ToDouble());
    EditMeters->Text = AnsiString(meters);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ButtonConvertToFeetClick(TObject *Sender)
{
    double feet =  meters_to_feet(EditMeters->Text.ToDouble());
    EditFeet->Text = AnsiString(feet);
}
//---------------------------------------------------------------------------

Обратите внимание!!! В файл с исходным кодом включён хеадер от ДЛЛ simple.h
#include "simple.h".
Если Вы попытаетесь скомпилировать и собрать приложение без указания в про-
екте связанной с ДЛЛ библиотеки, то получите сообщение об ошибке:
[Linker Error] Unresolved external '_simpleGetVersion' referenced from
APPSTATICFORM.OBJ

После сборки проекта запустите его (или совместите методом Run (F9) wink.gif). При
запуске Ва увидете сообщение "Process has attached to DLL (Static Load)", сге-
нерированное ДЛЛ (см. ф-цию точки входа в ДЛЛ в файле simple.c (листинг в начале
статьи)). После вывода сообщения на жкране появляется графический интерфейс при-
ложения и Вы можете вдоволь помучить его smile.gif.

Вот и всё, что касается статического связывания. Ещё раз обращаю внимание -
при отсутствии ДЛЛ прога работать не будет!!!

7. ДИНАМИЧЕСКАЯ ЗАГРУЗКА.

Для загрузки ДЛЛ в процессе выполнения приложения необходимо:

- Загрузить ДЛЛ и получить указатель на нее;
- Получить указатедь на ф-цию, которуб хотите вызвать;
- Вызвать ф-цию;
- Освободить ДЛЛ;

Для динамической загрузки ДЛЛ можно использовать одну из двух ф-ций АПИ -
LoadLibrary() или LoadLibraryEx(). Для того, чтобы понять, как происходит дина-
мическое подключение ДЛЛ, рассмотрим проект AppDynamic.bpr, в котором происходит
динамическая загрузка всё тойже simple.dll.
Первое, что нужно сделать — обьявить в программе переменную для получения
дескриптора (по сути — указателя) экземпляра ДЛЛ. В С++Билдер обьявление деск-
риптора можно разместить в классе главной формы:
//--------------- AppDynamicForm.h --------------------------------------------
private: // User declarations
       HINSTANCE dllhandle;
//-----------------------------------------------------------------------------
Загрузка ДЛЛ:
//--------------- AppDynamicForm.cpp ------------------------------------------
void __fastcall TForm1::ButtonLoadLibraryClick(TObject *Sender)
{
   dllhandle = LoadLibrary("simple.dll"); // keep track of the handle
   EditDLLHandle->Text = AnsiString((int)dllhandle);
   if (dllhandle)
   {
       ButtonUnloadLibrary->Enabled = true;
       ButtonProcAddress->Enabled = true;

   }
   else
   {
       ShowMessage("Unable to load the DLL");
   }
}

//-----------------------------------------------------------------------------
Ф-ция LoadLibrary() пытается загрузить ДЛЛ, идентифицируемую именем ее файла.
Здесь можно использовать также полный путь к файлу.

/* ОБРАТИТЕ ВНИМАНИЕ!!! Если указано только имя файла, прграмма будет пытаться
загрузить его из ТЕКУЩКГО каталога. Поэтому будте внимательны, используя подоб-
ные вызовы — например операция выбора файла с использованием стандартного диало-
га выбора (открыть/сохранить файл) приводит к установке текущим каталога, в ко-
тором находится указанный в диалоге файл.
*/
Загрузив ДЛЛ, необходимо получить указатель на ф-цию, которую предстоит выз-
вать. Для импорта ф-ции из ДЛЛ необходимо:
- Создать новое определение typdef с использованием прототипа экспортируемой
ф-ции.
- Привемти результат квызова ф-ции GetProcAddress() к прототипу экспортируе-
мой ф-ции.
Необходимо обьявить переменную для получения адреса требующейся ф-ции. Однако
перед ее обьявлением требуется определить тип используемой ф-ции с помощью
typedef:
//------------ AppDynamicForm.h -----------------------------------------------
   typedef double (*SIMPLEGETLIBVERSION)();
       SIMPLEGETLIBVERSION simpleGetLibVersion;

       typedef double (*METERS_TO_FEET)(double);
       METERS_TO_FEET meters_to_feet;

       typedef double (*FEET_TO_METERS)(double);
       FEET_TO_METERS feet_to_meters;
//-----------------------------------------------------------------------------
Обычно этот код размещают в хеадере, который затем включают в код программы.
В рассматриваемом коде необходимо привести  результаты вызова GetProcAddress
к типу указателей на ф-ции simpleGetLibVersion, meters_to_feet и feet_to_meters.
//------------ AppDynamicForm.cpp ---------------------------------------------
void __fastcall TForm1::ButtonProcAddressClick(TObject *Sender)
{
 if (dllhandle)
 {
   simpleGetLibVersion  =
       (SIMPLEGETLIBVERSION)GetProcAddress(dllhandle, "_simpleGetLibVersion");
   if (simpleGetLibVersion)  ButtonGetVersion->Enabled = true;
   meters_to_feet  =
       (METERS_TO_FEET)GetProcAddress(dllhandle, "_meters_to_feet");
   if (meters_to_feet)  ButtonConvertToMeters->Enabled = true;
   feet_to_meters  =
       (FEET_TO_METERS)GetProcAddress(dllhandle, "_feet_to_meters");
   if (feet_to_meters)  ButtonConvertToFeet->Enabled = true;
 }
}
//-----------------------------------------------------------------------------

/* Начальные подчеркивания в именах ф-ций, передаваемых ф-ции GetProcAddress
 требуются потому, что Билдер добавляет их автоматически к именам ф-ций,
 экспортируемых из ДЛЛ.
 Для бОльшей совместимости можно отключить данное свойство Билдера, сбросив
 флаг Generate Underscores вкладки Advanced Compiler опций проекта.
*/

/* ВНИМАНИЕ!!! Всегда проверяйте успешность вызова GetProcAddress(). Если она
возвратила NULL, значит вызов ф-ции был неудачен. Попытка использования нуле-
вого указателя на ф-цию приведёт к нарушению доступа к памяти (Acsess Violation)
*/

После получения достоверного указателя, его можно использовать как обычную
ф-цию:
//----------- AppDynamicForm.cpp ----------------------------------------------
void __fastcall TForm1::ButtonGetVersionClick(TObject *Sender)
{

 if (dllhandle)
 {
   EditVersion->Text = AnsiString(simpleGetLibVersion());
 }
}
//-----------------------------------------------------------------------------

void __fastcall TForm1::ButtonConvertToMetersClick(TObject *Sender)
{
    double meters =  feet_to_meters(EditFeet->Text.ToDouble());
    EditMeters->Text = AnsiString(meters);
}
//-----------------------------------------------------------------------------

void __fastcall TForm1::ButtonConvertToFeetClick(TObject *Sender)
{
    double feet =  meters_to_feet(EditMeters->Text.ToDouble());
    EditFeet->Text = AnsiString(feet);
}
//-----------------------------------------------------------------------------
Как видите, эта часть кода такая же, как и при использовании статической за-
грузки.
По окончании работы с ДЛЛ ее следует освободить посредством ф-ции АПИ
FreeLibrary(), которой в качестве параметра передаётся дескриптор ДЛЛ:
//----------- AppDynamicForm.cpp ----------------------------------------------
void __fastcall TForm1::ButtonUnloadLibraryClick(TObject *Sender)
{
   if (dllhandle)
   {
       FreeLibrary(dllhandle);
       ButtonUnloadLibrary->Enabled = false;
       ButtonProcAddress->Enabled = false;
       ButtonGetVersion->Enabled = false;
       ButtonConvertToMeters->Enabled = false;
       ButtonConvertToFeet->Enabled = false;
       EditDLLHandle->Text = "";
   }
}
//-----------------------------------------------------------------------------

Это всё, что касается динамической загрузки ДЛЛ.
Boyko
Отправлено: 17.12.2004, 17:39


Станционный диспетчер

Группа: Участник
Сообщений: 88



QUOTE (joynter @ 07/12/2004, 17:03)
совсем не трудно
QUOTE


Это всё, что касается динамической загрузки ДЛЛ.

А как Class исползовать???
Gedeon
Отправлено: 20.12.2004, 12:46


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (Boyko @ 17/12/2004, 18:41)
А как Class исползовать???

Для экспортирования класса из длл обьявление происходит практически так же как и для функций, кроме спецификатора extern C.
dllClass.h
CODE

__declspec(__dllexport) class dllClass{
private:
int intSample;
public:
dllClass();
~dllClass();
bool boolSample;
void FuncSample;
}

dllClass.cpp
CODE

dllClass::dllClass(){
intSample = 0;
}
//------------------------------------------------------------------------------------
dllClass::~dllClass(){
}
//------------------------------------------------------------------------------------
void dllClass::FuncSample(){
Beep(1000,1000);
}
//------------------------------------------------------------------------------------


Ну и в Вашем проекте напишите в h
CODE

#include "dllClass.h"
//....
private:
//....
dllClass *DllC;

ну и в срр
CODE

DllC = new dllClass;
//........ тут его используем
delete DllC


PS это статическое подключение, где-то было и про динамическое, но найти чет не могу, если найду — выложу.

Отредактировано Gedeon — 20/12/2004, 13:52
xim
Отправлено: 21.12.2004, 12:11


Станционный диспетчер

Группа: Участник
Сообщений: 143



По поводу динамического подключения класса можно почитать здесь:
http://www.rsdn.ru/article/baseserv/dlluse.xml
olegenty
Отправлено: 22.12.2004, 10:33


Ветеран

Группа: Модератор
Сообщений: 2412



вот эта ссылка кульнее:
Метод класса в DLL

у devnvd таких постов — пруд пруди smile.gif
Boyko
Отправлено: 22.12.2004, 16:21


Станционный диспетчер

Группа: Участник
Сообщений: 88



2Gedeon
Я статическое подключение уже сделал и искал что-нибудь про динамическое. Все таки Спасибо!

2xim
Большое Спасибо!!!

2olegenty
Тоже Спасибо, но вот что я нашел:
This site is defaced!!!
NeverEverNoSanity WebWorm generation 11.

ohmy.gif
Георгий
Отправлено: 22.12.2004, 21:34


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



QUOTE (Boyko @ 22/12/2004, 17:23)
2olegenty
Тоже Спасибо, но вот что я нашел:
This site is defaced!!!
NeverEverNoSanity WebWorm generation 11.

клёво — живность развлекается!
вообще весь xportal завалили!!


Отредактировано Георгий — 22/12/2004, 22:38

Вернуться в Вопросы программирования в C++Builder