- •В.В. Чуркин
- •Содержание
- •Ознакомление с Интегрированной Средой Разработки (иср)
- •Основное окно иср
- •Библиографический список
- •Занятие 1 Формы и компоненты Включение в проект новой формы
- •Размещение компонентов на форме
- •Депозитарий – хранилище форм и проектов
- •Задание 7. Опробовать режимы заимствования форм из Депозитария
- •Контрольные вопросы
- •Библиографический список
- •Занятие 2 Отладка приложений Компиляция и компоновка проекта
- •Сообщения компилятора и компоновщика
- •Действия в случае ошибки выполнения
- •Дальнейшие действия
- •Окно наблюдения Список Часов
- •Окно оценки и модификации Оценить|Изменить
- •Пошаговое выполнение приложения
- •Точки прерывания
- •Использование окна Инспектора Отладки
- •Другие средства отладки
- •Контрольные вопросы
- •Библиографический список
- •Занятие 3 Компоненты ввода и отображения текстовой информации
- •Компоненты Label, StaticText, Panel
- •Компоненты Edit, LabeledEdit, MaskEdit
- •Компоненты Memo, RichEdit
- •Компоненты ListBox, CheckListBox, ValueListEditor
- •Компонент StringGrid
- •Контрольные вопросы
- •Библиографический список
- •Занятие 4 Диаграммы и графики Компонент Chart
- •Компонент Image
- •Контрольные вопросы
- •Библиографический список
- •Контрольные вопросы
- •Библиографический список
- •Формирование квадратных матриц
- •Контрольные вопросы
- •Библиографический список
- •Кнопки, индикаторы, управляющие элементы
- •Управляющие кнопки Button и BitBtn
- •Кнопка с фиксацией SpeedButton
- •Группы радиокнопок − компоненты RadioGroup, RadioButton и GroupBox
- •Индикаторы CheckBox и CheckListBox
- •Ползунки и полосы прокрутки − компоненты TrackBar и ScrollBar
- •Заголовки − компоненты HeaderControl и Header
- •Таймер − компонент Timer
- •Календарь − компонент DateTimePicker
- •Панели и компоненты внешнего оформления
- •Панели общего назначения − компоненты Panel,
- •Многостраничные панели − компоненты TabControl,
- •Инструментальные панели − компоненты ToolBar и PageScroller
- •Перестраиваемые панели − компоненты CoolBar и ControlBar
- •Контрольные вопросы
- •Библиографический список
- •Занятие 8 Системные диалоги
- •Диалоги открытия и сохранения файлов – компоненты
- •Фрагменты диалогов – компоненты
- •Диалог выбора шрифта – компонент FontDialog
- •Диалоги выбора цвета – компоненты ColorDialog, ColorBox
- •Контрольные вопросы
- •Библиографический список
- •Занятие 9 Технология разработки приложений. Диспетчеризация действий
- •Диспетчеризация действий на основе компонента ActionList
- •Диспетчеризация действий на основе компонентов ActionManager, ActionMainMenuBar, ActionToolBar, CustomizeDlg
- •Контрольные вопросы
- •Библиографический список
- •Занятие 10 Динамически присоединяемые библиотеки dll Назначение dll
- •Статическое и динамическое связывание dll с приложением
- •Создание dll
- •Примеры статического и динамического связывания dll
- •Контрольные вопросы
- •Библиографический список
Статическое и динамическое связывание dll с приложением
Библиотеки DLL могут связываться с приложением двумя путями: статическим связыванием или динамическим связыванием.
При статическом связывании DLL загружается сразу, как только начинает выполняться приложение, которое будет ее использовать. Это наиболее простой способ использования DLL. Вызов функций в приложении при этом почти не отличается от вызова любых других функций. Здесь время загрузки увеличивается и нельзя выполнять приложение, если нет соответствующего файла DLL. Кроме того, без DLL приложение не может выполняться и в сокращенном, также рабочем варианте. Недостатком статического связывания является и то, что DLL занимает память все время, в течение которого выполняется приложение, независимо от того, вызываются ли в данном сеансе работы с приложением какие-то функции библиотеки, или нет.
Статическое связывание подразумевает, что для DLL создан специальный файл описаний импортируемых функций (import library file). Этот файл имеет расширение .lib и то же имя, что и соответствующая DLL, и должен быть связан с приложением на этапе компиляции.
При динамическом связывании DLL загружается только в тот момент, когда необходимо выполнить какую-то хранящуюся в ней функцию. Затем DLL можно выгрузить из памяти. Но при более эффективном использовании памяти вызов функций DLL существенно усложняется, и время вызова увеличивается.
Для вызова библиотечной функции надо сначала загрузить библиотеку функцией LoadLibrary API Windows. Затем с помощью функции GetProcAddress надо получить указатель на нужную функцию библиотеки. Только после этого можно выполнять функцию. А затем с помощью функции FreeLibrary надо выгрузить библиотеку из памяти.
Предположим, создана библиотека mydll.dll, содержащая некоторую функцию char* MyFunction(char*). Тогда для загрузки DLL надо выполнить оператор вида:
//загрузка DLL
HINSTANSE dllInstanse = LoadLibrary(“mydll.dll”);
Получить указатель на импортируемую функцию можно следующим кодом:
//получение указателя на функцию
typedef char* (__import FType(char*));
FType * MyFunc;
MyFunc = (FType*) GetProcAddress(dllInstanse, “_MyFunction”);
Объявление typedef вводит пользовательский тип (тип-функция) с произвольным именем FType. Введенный тип используется для задания типа указателя на функцию MyFunc. Для получения значения этого указателя используется функция API Windows GetProcAddress. Она принимает в качестве параметров указатель на загруженный модуль DLL и имя функции, а возвращает указатель на функцию. Этот указатель приводится к типу указателя на используемую функцию библиотеки.
Вызов функции осуществляется с помощью указателя на неё:
//вызов функции
char* S = MyFunc(“Привет!”);
Когда работа с DLL завершена, её можно выгрузить из памяти оператором вида:
//выгрузка DLL
FreeLibrary(dllInstanse);
Если нет острой необходимости использовать именно динамическое связывание, лучше всегда использовать статическое связывание.
Создание dll
Создание своей DLL начинается с выполнения команды Файл/Новый/Другое и выбором в окне Новые элементы на странице Новый пиктограммы Мастер DLL. На форме появляется окно задания опций создания DLL (рис.10.1).
Рис.10.1 - окно задания опций создания DLL
В окне выбран язык DLL – C++; индикатор Исп.VCL позволит создать DLL, которая может содержать компоненты библиотеки VCL. При этом в модуль включится файл vcl.h и установятся опции компоновки, обеспечивающие совместимость с объектами VCL. Индикатор VC++ Style DLL обеспечит создание DLL в стиле Microsoft Visual C++. После установки опций согласно рис.10.1 и щелчка на OK попадают в окно Редактора Кода с заготовкой модуля DLL. В тексте комментария заготовки рекомендуется при передаче строк в функцию и из функции использовать тип char*, а не, например, AnsiString. Приведем пример такой функции для включения в DLL.
char * Code(char * s, char Key)
{
for (int i = 0; ; i++)
{
if (s[i] == ’\0’) break;
s[i] = s[i] ^ Key;
}
return s;
}
Функция воспринимает строку типа char* и ключ типа char и возвращает зашифрованную этим ключом строку. Кодировка такова: каждый символ строки складывается с ключом по операции исключающее ИЛИ. При дешифровании достаточно с закодированной строкой выполнить ту же операцию с тем же ключом. Далее имя функции заменено на Code_Dec.
Текст комментария нужно удалить.
В заготовке модуля DLL строка #include <vcl.h> подключает заголовочный файл vcl.h. Он будет нужен, если при создании DLL будут использоваться какие-то классы, формы, функции, связанные с библиотекой визуальных компонентов. Иначе – эту строку можно удалить из текста DLL.
В конце текста заготовки модуля DLL используется функция DllEntryPoint, необходимая для загрузки и выгрузки библиотеки. Она создает дескриптор hinst создаваемой DLL. Он требуется при выполнении некоторых функций, например, LoadIcon, LoadCursor и др. В этих случаях можно использовать параметр hinst для создания соответствующей глобальной переменной.
Перед описанием функции DllEntryPoint необходимо вставить директиву включения головного файла #include “UMyDLL.h”. Файла UMyDLL.h пока нет, - он будет создан ниже.
После выполнения последовательности очевидных действий и сохранения модуля под именем UMyDLL, а проекта – под именем MyDLL, получим файл реализации с именем UMyDLL.cpp (рис.10.2).
Рис.10.2 – файл реализации UMyDLL.cpp библиотеки
Теперь создадим заголовочный файл UMyDLL.h библиотеки. Выполним команду Файл/Новый/Другое и выберем в окне Новые элементы на странице Новый пиктограмму Файл Заголовка. Сохраним файл под именем UMyDLL.h. В появившееся окно Редактора Кода загрузим текстовый файл – запишем в файл следующий код (рис.10.3):
Рис.10.3 – заголовочный файл UMyDLL.h библиотеки
Идентификаторы _UMYDLL_H и DLL_EI могут быть любыми.
Логика работы первого фрагмента в заголовочном файле следующая: если не определен идентификатор _UMYDLL_H (ifndef _UMYDLL_H), то он определяется, т.е. замещается пустой строкой (define _UMYDLL_H ). Этот фрагмент не позволяет многократно включать заголовочный файл в файлы реализации библиотеки или приложения. Логика работы второго фрагмента в заголовочном файле следующая: если определен идентификатор __DLL__ (ifdef __DLL__), то идентификатор DLL_EI раскрывается как __declspec(dllexport); если же идентификатор __DLL__ не определен (else), то идентификатор DLL_EI раскрывается как __declspec(dllimport). C++Builder автоматически определяет __DLL__ в случае, если создается проект DLL, и не определяет этот идентификатор при создании объекта приложения. Таким образом, в зависимости от того, включается ли заголовочный файл в библиотеку или в приложение, он будет выглядеть по-разному. При компиляции библиотеки строка, определяющая функцию Code_Dec, после раскрытия макроса будет восприниматься как
extern "C" char * __declspec(dllexport) Code_Dec (char *s, char Key);
Здесь конструкция __declspec(dllexport) означает, что функция может экспортироваться из библиотеки, т.е. может вызываться внешними приложениями. Подобным же образом должны быть перечислены все функции DLL, предназначенные для прямого использования в приложениях. Помимо таких функций в DLL могут быть вспомогательные функции-утилиты, предназначенные только для использования другими функциями. Подобные утилиты не должны определяться как экспортируемые.
Когда тот же заголовочный файл включается в приложение, то эта же строка после раскрытия макроса имеет другой вид:
extern "C" char * __declspec(dllimport) Code_Dec (char *s, char Key);
Здесь конструкция __declspec(dllimport) означает, что функция импортируется, т.е. вносится в модуль приложения из DLL. Таким образом, один и тот же заголовочный файл может использоваться и при создании DLL, и в приложениях, обращающихся к данной DLL.
Продолжим создание DLL.
Выполним команду Проект/Опции и в окне опций проекта на странице Компоновщик убедимся, что включен индикатор опции Создать библиотеку импорта. Эта опция обеспечит при создании DLL автоматическую генерацию файла .lib, необходимого для статического присоединения DLL к проектам. Индикатор опции Создать библиотеку импорта будет доступен только при наличии проекта MyDLL, файлов UMyDLL.cpp и UMyDLL.h.
Выполним команду Проект/Создать MyDLL. В результате будут созданы файлы MyDLL.dll и MyDLL.lib. Убедитесь в этом.
Рис.10.4 – сообщение об ошибке
Попробуем протестировать создаваемую DLL и выполним команду Запуск/Запустить (F9). Получим сообщение об ошибке (рис.10.4), которое означает, что сначала с помощью команды Запуск/Параметры… нужно определить хост – тестирующее приложение.