Главная » C++, C++ Builder » Построение строковой библиотеки в CBuilder

0

Мы решили, что создать библиотеку (DLL), содержащую наши строковые ресурсы, — задача нужная, так что пора этим заняться. Создание DLL в CBuilder не является сложным процессом, так же как и создание строк, входящих в эту библиотеку. Итак, поехали.

Создайте в CBuilder новый проект, выбрав команду File д New. На странице Projects страничного диалога выберите DLL из доступных типов объектов. Нажмите на кнопку OK, и CBuilder сгенерирует «скелет» DLL, включая весь предварительный код. Мы не собираемся работать с этим кодом в данном примере, так как эта DLL не будет содержать никакого дополнительного кода.

Создайте новый текстовый файл в CBuilder и добавьте в него следующие строки. Это будет наш строковый ресурс для DLL.

STRINGTABLE  DISCARDABLE BEGIN

1001 "Hello"

1002 "Aloha"

1003 "Shalom"

1004 "Hola!" END

На  случай,  если  вы  не  работали  с  таблицами  строк  раньше:  принцип  крайне  прост.  Ресурс

«таблица строк» определяется выражением STRINGTABLE DISCARDABLE. Ключевое слово STRINGTABLE говорит компилятору ресурсов, что последующие строки описывают ресурс таблицы строк. Флажок DISCARDABLE указывает, что данный блок не обязательно хранить в фиксированной области памяти и что при необходимос ти его можно выгружать на диск. После определения блока идет выражение BEGIN, которое указывает на  начало определения строк в таблице.

Каждая строка в таблице представлена целым значением и символьным значением. Целые значения нужны для идентификации каждой строки уникальным номером. Когда вы будете ссылаться на строку, используя идентификатор ресурса, вам понадобится это значение. Когда таблица строк закончена (в ней может быть любое количество элементов), вы закрываете блок описания таблицы строк выражением END.

Сохраните этот  файл как strings.rc в каталоге с вашим проектом в CBuilder. Добавьте ресурс к проекту, вызвав команду меню Project д Add to Project и выбрав файл string.rc. CBuilder знает, что делать с файлами описания ресурсов (resource script, RC), так что больше делать ничего не нужно. Закройте файл и соберите проект обычным образом, выбрав Project д Make или нажав F9. Файл ресурса скомпилируется, и, если не будет найдено ошибок, будет создан файл DLL. Вот и все о построении DLL в CBuilder.

Пример динамической загрузки строк

Следующим шагом в нашем примере является создание формы, которая бы использовала только что созданную нами «с таким трудом» DLL. В данном примере мы хотим, чтобы строки, содержащиеся в DLL, представляли собой представления статического текста в форме на разных языках. На самом деле, если вы посмотрите на данные, то  увидите, что строки содержат слово

«Hello» (привет) на разных языках (хотя в данном случае для них используется латинский алфавит).

На рис. 10.1 представлена форма, которую мы будем использовать для отображения данных. Как видите, на форме одна метка статического текста и четыре переключателя (radio buttons). Это все, что нам потребуется для реализации данного примера.

Рис. 10.1. Форма примера динамической загрузки строк

После построения формы нужно подумать о загрузке данных из  DLL. Для этого  вам придется разобраться с двумя разными функциями Windows API. Мы говорили о Windows API  в предыдущей главе, и эти две функции еще нами не исследованы.

Первой функцией API, которую надо изучить, является функция LoadLibrary; она позволяет открывать и брать данные из динамической библиотеки (DLL) на диске. Для того чтобы мы могли использовать нашу DLL  в последующем коде для формы, нам нужно открыть ее,  когда форма загружается. Так что мы используем конструктор класса формы для инициализации ссылки (handle) на библиотеку. Добавьте следующий код в конструктор формы:

__fastcall TForm1::TForm1(TComponent *Owner)

: TForm(Owner)

{

hLibHandle = LoadLibrary("project1.dll");

}

Кроме того, вам нужно добавить описание переменной hLibHandle в заголовочный файл формы. Добавьте эту строчку в секцию private описания класса формы в заголовочном файле:

private: // User declarations HINSTANCE  hLibHandle;

Эти два изменения дают вам полный доступ к содержимому библиотеки project1.dll, которую мы построили в предыдущем разделе. Функция LoadLibrary дает вам ссылку на экземпляр DLL. Почему ссылку на экземпляр? Потому что библиотеки DLL могут быть загружены одновременно несколькими приложениями, работающими в Windows. Вот почему они полезны. Если десяток разных приложений использует код или ресурсы в DLL, то они могут все использовать совместно одну и ту же DLL, загруженную в память. Когда вы используете функцию API LoadLibrary, вы увеличиваете счетчик  использования  DLL.  Библиотека DLL будет  выгружена  из  памяти только

тогда, когда все приложения, использующие ее, будут выгружены из памяти (то есть освободят ссылку на библиотеку).

Загружая библиотеку функцией LoadLibrary, не забывайте освободить  ее,  когда  работа закончена. Если вы этого не сделаете, то она будет сидеть в памяти, пока Windows не догадается избавиться от нее. Создайте обработчик события формы OnClose и добавьте в метод FormClose следующий код:

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{

if ( hLibHandle != NULL )

{

FreeLibrary( hLibHandle );

}

}

Заметьте, что  мы проверяем, не равна ли ссылка NULL. Если функция LoadLibrary не может найти файл или загрузить его в память по какой-либо причине, то она вернет ссылку, равную NULL. Нехорошо передавать пустые (NULL) ссылки функциям, которые ожидают корректные значения, так что мы делаем проверку ссылки на корректность перед тем, как освободить ее. То же самое мы делаем при каждом использовании ссылки, как вы вскоре увидите.

Итак, библиотека загружена и форма отображена. Следующий шаг — получать строки при запросе пользователя. Давайте сначала обработаем первый  переключатель  (Английский  ). Создайте обработчик для первого переключателя на форме и добавьте в него следующий код:

void __fastcall TForm1::RadioButton1Click(TObject *Sender)

{

if ( hLibHandle != NULL )

{

char szBuffer[ 256 ]; LoadString( hLibHandle, 1001,

szBuffer,

256 // размер буфера

);

Label1->Caption = szBuffer;

}

}

Обработчик сначала проверяет, была ли загружена библиотека, проверяя  ссылку.  Если  она  не равна NULL, то метод вызывает функцию API LoadString для загрузки данной строки из DLL. Функция API LoadString имеет следующий синтаксис:

int LoadString( HINSTANCE hInstance, int nResourceID, LPSTR strBuffer, int nSizeOfBuffer);

В нашем случае ссылка на экземпляр (HINSTANCE) — это ссылка на библиотеку, которую мы загрузили функцией LoadLibrary. Идентификатор ресурса (nResourceID) — это тот же идентификатор строки, что мы описали в нашей таблице строк в файле ресурса. Функция LoadString  будет  искать  строку  с  тем  же  ID  (идентификатором),  что  и  заданный.  Так  что,

посмотрев в таблицу строк, описанную выше в данной главе, вы увидите, что строка с ID 1001

является английским словом «Hello».

Параметры szBuffer и 256 — это буфер, в котором мы хотим хранить полученную строку, и, соответственно, размер этого буфера. Если функция LoadString найдет строку, более длинную, чем размер буфера, то она возвратит только первые 256 байтов строки и программа не «рухнет».

Для обработки остальных переключателей на форме мы просто меняем ID строки, которую хотим получить из DLL. Вот обработчики трех переключателей на форме:

void __fastcall TForm1::RadioButton2Click(TObject *Sender)

{

if ( hLibHandle != NULL )

{

char szBuffer[ 256 ]; LoadString( hLibHandle, 1002,

szBuffer,

256 // размер буфера

);

Label1->Caption = szBuffer;

}

}

//———————————————————————————————-

void __fastcall TForm1::RadioButton3Click(TObject *Sender)

{

if ( hLibHandle != NULL )

{

char szBuffer[ 256 ]; LoadString( hLibHandle, 1003,

szBuffer,

256 // размер буфера

);

Label1->Caption = szBuffer;

}

}

//———————————————————————————————-

void __fastcall TForm1::RadioButton4Click(TObject *Sender)

{

if ( hLibHandle != NULL )

{

char szBuffer[ 256 ]; LoadString( hLibHandle, 1004,

szBuffer,

256 // размер буфера

);

Label1->Caption = szBuffer;

}

}

Как видите, процесс одинаков во всех случаях. На самом деле мы могли бы поместить весь этот код в одну функцию, которая запрашивает нужную строку в DLL и наследует в целом весь

процесс:

void GetString( int nId, AnsiString &strLang )

{

// Сначала загрузить DLL

HINSTANCE hLibHandle = LoadLibrary("project1.dll"); if ( hLibHandle )

{

char szBuffer[ 256 ];

LoadString( hLibHandle, nId, szBuffer, 256 ); strLang = szBuffer;

FreeLibrary( hLibHandle );

}

}

Тогда весь предыдущий код мог бы использовать одну функцию GetString для получения нужных данных. Заметьте также, что мы использовали объект AnsiString для получения данных, который лучше работает со строками. Строка такого типа (AnsiString) также может быть напрямую присвоена свойству Caption (заголовок) метки статического текста.

Теперь вы овладели загрузкой строк, зависящих от языка, из DLL. Больше нечего сказать об этом, разве что можно заметить, что такой же процесс применим и к растровым рисункам (bitmaps), и к значкам (icons) в ресурсах. Если у вас на форме есть объект «растровый рисунок» (TBitmap), то вы можете присвоить ему рисунок из ресурса, получив ссылку типа HBITMAP из файла ресурса и затем присвоив ее свойству Handle (ссылка) объекта TBitmap. Это работает замечательно со всем, кроме меню. Давайте разберемся, почему это так.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии