Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Как написать и отладить dll в C++ Builder

  1. Что такое Dll и зачем оно нам надо?
  2. Как написать Dll?
  3. Как подключить и использовать Dll в своей программе?
  4. Как отладить Dll?

Что такое Dll и зачем оно нам надо?

Ну, начнём с того, что Dll — это аббревиатура от слов Dynamic Link Library, что переводится как «динамически подключаемая библиотека». По-сути, это модуль, содержащий какой-то полезный код. Чаще всего этот полезный код содержит какие-то программные функции, но может содержать и что-то другое, например, изображения или списки локализации и тому подобное.

Главное отличие модуля Dll от обычной программы заключается в том, что сам по себе такой модуль (и содержащиеся в нём функции) не может быть исполнен как отдельная программа, зато к этому модулю могут подключаться другие программы и исполнять содержащиеся в нём куски кода. Это, например, позволяет не разрабатывать каждый раз заново функции для работы с какими-то железками, базами данных, другими программами, а описать их один раз в отдельном модуле Dll и далее использовать эту Dll во всех программах, в которых они нужны.

Программы могут связываться с модулями Dll двумя способами, называемыми: статическим связыванием и динамическим связыванием.

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

Как написать Dll?

Для создания Dll в C++ Builder есть специальный Wizard. Найти его можно по следующему пути: File->New->Other… и далее на вкладке New нужно выбрать пункт DLL Wizard.

путь в главном меню к Dll-wizard
выбор Dll-wizard

Далее, в визарде нужно поставить галочки C++, Use VCL (будем писать на C++ и пользоваться компонентами библиотеки VCL) и нажать OK:

настройка Dll в wizard-е

В итоге получаем файл cpp с заготовкой кода (пара подключенных библиотек, функция, обозначающая точку входа) и длиннющим комментарием. Комментарий читаем внимательно и стираем нафиг, а проект сохраняем в отдельной папке.

Далее идём по тому же пути File-&gb;New-&gbOther…, только теперь выбираем на вкладке New не DLL Wizard, а Header File. Cохраняем этот файл в той же папке, что и весь остальной проект (удобнее всего под тем же именем, что и файл cpp, чтобы потом не запутаться) и дописываем в файл cpp инструкцию #include «имя файла.h».

Дописываем в h-файл вот такой код:

//----------------------------------------------------------------------
#ifndef имя_файлаH
#define имя_файлаH
//----------------------------------------------------------------------
#include <vcl\Classes.hpp>
//----------------------------------------------------------------------
//сюда пишем описания своих функций
 
//----------------------------------------------------------------------
#endif

Теперь в cpp-файле можно писать какие-то свои функции:

#include "unit1.h"
//---------------------------------------------------------------------------
#include <vcl.h>
#include <windows.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
        return 1;
}
//---------------------------------------------------------------------------
// Ниже пишем свои функции

Для того, чтобы функцию можно было экспортировать, её описание в h-файле должно быть вот в таком виде:

            тип                            имя      типы входных
        возвращаемой                     функции     переменных
         переменной                       в Dll    (через запятую)
             |                              |         |
extern "C" OD_Type __declspec(dllexport) DllFunc1 (ID_Types);

Если на выходе функции нужно получить не одну, а несколько переменных, — можно использовать массивы или структуры, только нужно учесть, что вместо AnsiString лучше пользоваться Char-массивами (об этом как раз был комментарий).

Осталось только скомпилировать проект и на выходе мы получим файл с расширением Dll.

Как подключить и использовать Dll в своей программе?

Для того, чтобы использовать функции из Dll в своей программе, — нужно сделать следующее:

Сначала в заголовочном файле программы (*.h) нужно правильно описать типы импортируемых из Dll функций (чтобы описание в программе совпадало с описанием в самой Dll). Проще всего подглядеть эту информацию в самом хидере dll-ки (*.h), хотя конечно, неважно откуда у вас эта информация, главное чтобы она была. Это нужно для того, чтобы программа правильно понимала какие данные (типы, порядок, количество переменных) она будет в Dll передавать и какие принимать. Когда нужная информация у вас есть, описать в программе типы импортируемых функций можно следующим образом:

typedef OD_Type (__import Proto1(ID_Types)); // первый прототип внешней функции
        Proto1 *Func1; // объявляем импортируемую функцию описанного выше типа
typedef OD_Type (__import Proto2(ID_Types)); // второй прототип внешней функции
        Proto2 *Func2; // а разных функций второго типа будем импортировать
        Proto2 *Func3; // целых 3 штуки (так тоже можно, если функции имеют
        Proto2 *Func4; // одинаковый набор типов входных и выходных параметров)

Далее, в программе (*.cpp) нужно загрузить Dll из которой мы хотим описанные функции импортировать:

HINSTANCE DllHinst = LoadLibrary("[путь/]имя_файла.dll");

Если Dll-ка успешно загрузилась, то LoadLibrary вернёт указатель на на неё (идентификатор, по которому к ней можно обращаться), в противном случае функция вернёт NULL.

Теперь, когда у нас есть указатель на загруженную Dll, осталось найти внутри Dll-ки нужную нам функцию. Это можно сделать так:

                              указатель на    имя функции, которую мы ищем
                             загруженную Dll  (с подчеркиванием впереди)
                                    |           |
Func1 = (Proto1 *)GetProcAddress(DllHinst,"_DllFunc1");

Если запрошенная нами функция обнаружится в указанной Dll-ке, то GetProcAddress вернёт нам её адрес, в противном случае будет возвращён NULL.

Вот и всё, теперь мы можем пользоваться функцией из загруженной Dll в своей программе, вызывая её точно также, как буд-то она у нас написана в секции *.cpp.

После того, как мы попользовались функциями из Dll-ки и она нам больше не нужна, можно выгрузить её командой FreeLibrary:

FreeLibrary(DllHinst);

Как отладить Dll?

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

Когда такое приложение есть, — копируем его в папку проекта с Dll-кой, выбираем в меню Run пункт Parameters… , в открывшемся окне щёлкаем на кнопку Browse… , напротив строки Host Application, в открывшемся диалоговом окне выбираем наше приложение (после этого в строке Host Application будет прописан путь к выбранному приложению) и жмём OK.

меню выбора приложения для отладки dll
окно выбора приложения для отладки dll

Всё, теперь можно также как и в обычном приложении расставлять в нашей Dll-ке точки останова и запускать отладку кнопкой F9. При этом сначала загрузится выбранное нами выше приложение, а когда оно дойдёт до использования хранящихся в Dll-ке функций — тут-то и сработают наши точки останова, мы попадём в отладчик и сможем отлаживать написанный в Dll-ке код.

В качестве примера можно посмотреть dll-ку для шлюза, вот из этой статьи (она там с исходниками выложена).

Добавить комментарий