Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основы программирования для информатиков и инженеров. Часть 2 Прикладное программирование в Windows.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
603.45 Кб
Скачать

13.2. Создание собственных динамических библиотек (dll-файлов)

 

В этом параграфе Вы узнаете, что такое DLL (Dynamic Link Library – динами­чески присоединяемая библиотека), как создать DLL с помощью Visual C++, и как использовать DLL в своих прикладных программах.

Рис. 13.1. Результаты работы программы с Проводником

 

Что такое DLLDLL является библиотечным файлом, содержащим функции. Програм­мист может включить dll-файл в свою программу и вызывать его функции. Например, можно создать dll-файл с именем Circle.dll, содержащий функции, относящиеся к операциям с кругами, такие как DrawCircle(), CalculateCirdeArea() и т.д. После этого Вы можете передать файл Circle.dll другим программистам, а они включат эти функции в свои программы.

Как и подразумевает название, DLL является библиотекой, которая дина­мически присоединяется к использующей ее программе. Это означает, что, когда Вы создаете ехе-файл программы, вам не нужно компоновать dll-файл со своей программой и этот файл будет динамически скомпонован с вашей программой во время ее выполнения. Таким образом, если Вы пишете программу, которая использует DLL, то должны распространять dll-файл вместе с ЕХЕ-файлом программы.

Если программа использует для работы dll-файл, он должен находиться в одной из следующих папок:

     в \Windows\System (при использовании Windows Me/98/95);

     в \Windows\System32 (для  Windows XP/2000/NT);

     в текущей папке, откуда запускается ехе-файл Вашей программы.

 

Обычно программа Install (установка) копирует dll-файл в системный каталог пользователя \Windows\System32/ В этом случае другие программы также могут обращаться к этому dll-файлу, а работа вашей программы не будет зависеть от текущих установок маршрута.

DLL-файл может использоваться любым языком программирования, ко­торый поддерживает такие файлы (например, Visual C++ и Visual Basic). Далее Вы создадите простой dll-файл и напишете программу Visual C++, которая к нему обращается.

Сле­дующим шагом будет написание этого служебного кода для двух файлов: исходного текста библиотеки MyDLL.cpp и файла определений MyDLL.def.

Создайте новую папку для проекта (например, Library-13-2), куда поместите эти два файла. В этой же папке будет создаваться проект (назовем его MyDLL), в рамках которого разрабатывается библиотека; этот проект не создает программного приложения.

С помощью Visual Studio проект создается следующим образом (путь из главного меню Visual Studio):

File → New → Win32 Dinamic Link Library → Location (указывается папка \Library-13-2) → Project Name (указывается MyDLL).

 

Исходный текст библиотеки MyDLL.cpp состоит из двух частей: служебной и функциональной. Служебная часть связана с инициализацией работы программ библиотеки и с прекращением работы этих программ. Она имеет следующий вид:

 

//  Пример MyDLL.cpp

// 

//  DllEntryPoint() : Входная точка инициализации DLL

#include <Windows.h>

DllEntryPoint(HINSTANCE hDLL,

              DWORD dwReason, LPVOID Reserved)

   {

   switch(dwReason)

      {

      case DLL_PROCESS_ATTACH:

         {

         break;

         }

      case DLL_PROCESS_DETACH:

         {

         break;

         }

      }

   return TRUE;

   }

 

Код, который мы написали файле MyDLL.cpp, содержит единст­венную функцию: DllEntryPoint(), которая является входной точкой DLL. Когда ехе-программа, которая обращается к DLL, загружает DLL, функция DllEntryPoint() выполняется автоматически. (Вы напишете ехе-программу, загружающую MyDLL.dll, позже в этом параграфе.)

Функция DllEntryPoint()  состоит из оператора switch:

 

switch (dwReason)

   {

   case DLL_PROCESS_ATTACH:

      {

      break;

      }

   case DLL_PROCESS_DETACH:

      {

      break;

      }

   }

 

Оператор switch оценивает значение dwReason (второй параметр функции DllEntryPoint() ).

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

Код во втором блоке переключателя case DLL_PROCESS_DETACH выполня­ется, когда DLL отсоединяется от ехе-файл. Например, когда ехе-файл, который использует DLL, завершается, то выполняется код во втором блоке переключателя case DLL_PROCESS_DETACH. Следовательно, в блоке case dll_process_detach можно написать код, выполняющий процедуру очи­стки.

Теперь Вы добавите две простых функции в файл MyDll.cpp:

1) MyBeep()    – вызывает звуковой сигнал;

2) MyDelay() – осуществляет задержку процесса выполнения приложения.

Текст MyBeep():

 

int MyBeep(DWORD frequency, // Звуковой сигнал

           DWORD duration)

   {

   Beep(frequensy,duration);

   return 1;

   }

 

Текст MyDelay():

 

int MyDelay(long wait)      // Задержка

   {

   Sleep(wait);

   return 1;

   }

 

Теперь нужно собрать весь файл MyDll.cpp:

 

//  Пример MyDLL.cpp

//

#include <Windows.h>

//  Объявление прототипов функций DLL

int MyBeep(DWORD, DWORD);

int MyDelay(long);

//  DllEntryPoint() : Входная точка инициализации DLL

DllEntryPoint(HINSTANCE hDLL,

              DWORD dwReason, LPVOID Reserved)

   {

   switch(dwReason)

      {

      case DLL_PROCESS_ATTACH:

         {

         break;

         }

      case DLL_PROCESS_DETACH:

         {

         break;

         }

      }

   return TRUE;

   }

// Звуковой сигнал

int MyBeep(DWORD frequency, // Звуковой сигнал

        DWORD duration)

{

Beep(frequency, duration);

return 1;

}

// Задержка

int MyDelay(long wait)      // Задержка

   {

   Sleep(wait);

   return 1;

   }

 

Итак, файл с исходным текстом библиотеки подготовлен. Переходим к созданию файла определений.

 

Файл определений MyDLL.def имеет одинаковую структуру для всех библиотек. Он также имеет две части: служебную и функциональную.

Служебная часть:

 

;

;   def-файл для библиотеки MyDLL.dll

;

LIBRARY mydll

CODE PRELOAD MOVEBLE DISCARDABLE

DATA PRELOAD SINGLE

EXPORTS

   ; Имена (точки входа) DLL функций

 

Здесь имена DLL функций – это имена тех функций, которые  включены в библиотеку и «экспортируются» после загрузки библиотеки (оператор EXPORTS). Заметим, что комментарии в def-файлах начинаются не с двойной наклонной черты, а с точки с запятой.

Функциональная часть этого файла выглядит очень просто:

 

MyBeep

MyDelay

 

В целом файл MyDLL.def в нашем случае выглядит так:

 

;

;   def-файл для библиотеки MyDLL.dll

;

LIBRARY mydll

CODE PRELOAD MOVEBLE DISCARDABLE

DATA PRELOAD SINGLE

EXPORTS

   ; Имена (точки входа) DLL функций

MyBeep

MyDelay

Создание прикладной программы, использующей DLL. Прикладная программа создается в рамках проекта, не связанного с библиотекой. Назовем этот проект Ex_13_02. Все файлы этого проекта будут собираться в папке Example-13-2. Для создания этой программы нам потребуются следующие переменные:

1) переменная-дескриптор для регистрации экземпляра библиотеки

HINSTANCE gLibMyDLL = NULL;

2) объявление функции MyBeep() библиотеки MyDLL.dll

typedef int(*MYBEEP)(DWORD, DWORD);

MYBEEP  MyBeep = NULL;

3) объявление функции MyDelay() библиотеки MyDLL.dll

typedef int(*MYDELAY)(long);

MYDELAY MyDelay = NULL;

 

Загрузка DLL выполняется с помощью функции LoadLibrary():

 

gLibMyDLL = LoadLibrary("MYDLL.DLL");

 

Напомним, что функция  LoadLibrary() будет искать библиотеку в одном из следующих мест:

        в \Windows\System (при использовании Windows Me/98/95);

        в \Windows\System32 (для  Windows XP/2000/NT);

        в текущей папке, откуда запускается ехе-файл Вашей программы.

 

Если там нет библиотеки, то значение переменной gLibMyDLL останется NULL. Это обстоятельство можно использовать на практике.

После успешной загрузка библиотеки необходимо определить адреса функций в библиотеке. Для этого применяется функция

 

  FARPROC GetProcAddress(HMODULE hModule,

                         LPCSTR  lpProcName);

 

Первый аргумент этой функции hModule – дескриптор загруженной библиотеки, а второй lpProcName – имя функции, адрес которой нужно получить. Если в библиотеке запрашиваемой функции нет, то возвращается значение NULL.

Адреса обращений к нашим двум функциям получаются так:

 

MyBeep =(MYBEEP)GetProcAddress(gLibMyDLL,"MyBeep");

MyDelay=(MYDELAY)GetProcAddress(gLibMyDLL,"MyDelay");

 

Пример 13-2. Программа работы с DLL-библиотекой

 

В следующем примере в качестве каркаса также используется программа, рассмотренная в примере 8-2. К ней дописаны описанные выше средства, демонстрирующие возможность работы с DLL-библиотекой. Дополним меню программы двумя новыми кнопками: Загрузить DLL и Звуковой сигнал.

Файл ресурсов Mydialog.rc после включения этих кнопок выглядит следующим образом:

 

#include <Windows.h>

#include "Text.h"

MYMENU MENU

   {

   MENUITEM "&Текст",           ID_SHOW

   MENUITEM "&Сначала",         ID_RESET

   MENUITEM "&Загрузить DLL",   ID_LOAD

   MENUITEM "&Звуковой сигнал", ID_BEEP

   MENUITEM "Помощь",           ID_HELP

   }

MYMENU ACCELERATORS

   {

   VK_F2, ID_SHOW,  VIRTKEY

   VK_F3, ID_RESET, VIRTKEY

   VK_F4, ID_LOAD,  VIRTKEY

   VK_F5, ID_BEEP,  VIRTKEY

   VK_F1, ID_HELP,  VIRTKEY

   }

 

Соответственно, изменится и вспомогательный файл Text.h:

 

#define ID_SHOW    100

#define ID_RESET   101

#define ID_LOAD    102

#define ID_BEEP    104

#define ID_HELP    105

 

Распишем команду Загрузить DLL:

 

   case ID_LOAD:

      // Если файл MyDLL.DLL уже загружен,

      // сообщить

      // пользователю и завершить эту функцию

      if(gLibMyDLL != NULL)

         MessageBox(hwnd,

            "Библиотека MyDLL уже загружена",

            "Внимание:",MB_OK);

      else

         {

         MessageBox(hwnd,"Загрузить DLL",

                         "Команда:",MB_OK);

         // Загрузить MyDLL.dll

         gLibMyDLL=LoadLibrary("MYDLL.DLL");

         // Если DLL не был загружен, вывести

         // окно сообщения об ошибке

         if(gLibMyDLL == NULL)

            {

            MessageBox(hwnd,

               "Невозможно загрузить MyDLL. "

               "Убедитесь, что она находится"

               " в соответствующей папке"

               "Ошибка:",MB_OK);

            return 0;

            }

         }

      // Получить адрес функции МуВеер()

      // библиотеки MyDLL.dll

      MyBeep=(MYBEEP)GetProcAddress(gLibMyDLL,

                                    "MyBeep");

      // Получить адрес функции MyDelay()

      // библиотеки MyDLL.dll

      MyDelay=(MYDELAY)GetProcAddress(gLibMyDLL,

                                      "MyDelay");

      InvalidateRect(hwnd,NULL,1); //Сообщить

      break;

 

Распишем команду Звуковой сигнал:

 

      case ID_BEEP:

         if(MyDelay != NULL && MyBeep != NULL 

                    && gLibMyDLL != NULL)

            {

            // Вызвать функцию MyBeep()

            MyBeep(600,250); // 600 Гц, 0,25 сек

            // Вызвать функцию MyDelay()

            MyDelay(500);    // 0,5 сек

            // Вызвать функцию MyBeep()

            MyBeep(300,250); // 300 Гц, 0,25 сек

            MessageBox(hwnd,"Звуковой сигнал",

                            "Команда:", MB_OK);

            }

         else

            MessageBox(hwnd,

                       "Причины: библиотека MyDLL "

                       "не загружена, либо "

                       "отсутствуют функции "

                       "MyBeep и MyDelay ",

                       "Ошибка:", MB_OK);

         InvalidateRect(hwnd,NULL,1); //Сообщить

         break;

 

Полный текст примера программы, демонстрирующей работу с библиотекой, представлен ниже.

 

// Демонстрация работы с библиотекой MyDLL,

// с использованием виртуального окна

#include <Windows.h>

#include <String.h>

#include <Stdio.h>

#include "Text.h"

 

LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);

char szWinName[] = "МоеОкно";  // Имя класса окна

char str[255];       // Буфер строки вывода

int X=0, Y=0;        // Текущие координаты строки

int maxX, maxY;      // Размеры экрана

HDC     memdc;       // DC виртуального окна

HBITMAP hbit;        // Растр - это виртуальное окно

HBRUSH  hbrush;      // Дескриптор кисти

 

HINSTANCE gLibMyDLL = NULL;

typedef int(*MYBEEP)(DWORD, DWORD);

MYBEEP  MyBeep = NULL;

typedef int(*MYDELAY)(long);

MYDELAY MyDelay = NULL;

 

int WINAPI WinMain (HINSTANCE hThisInst,

                    HINSTANCE hPrevInst,

                    LPSTR lpszArgs,

                    int nWinMode)

   {

   HWND hwnd;

   MSG msg;

   WNDCLASS wcl;

   HACCEL hAccel;

   // Определить класс окна

   wcl.hInstance=hThisInst;   // Дескриптор приложения

   wcl.lpszClassName=szWinName;  // Имя класса окна

   wcl.lpfnWndProc=WindowFunc;   // Функция окна

   wcl.style=0;                  // Стиль по умолчанию

   wcl.hIcon=LoadIcon(NULL,IDI_APPLICATION); // Иконка 

   wcl.hCursor=LoadCursor(NULL,IDC_ARROW) ;  // Курсор

   wcl.lpszMenuName="MYMENU";                // Меню

   wcl.cbClsExtra=0;             // Без дополнительной

   wcl.cbWndExtra=0;             // информации

   // Определить заполнение окна белым цветом

   wcl.hbrBackground=

                  (HBRUSH)GetStockObject(WHITE_BRUSH);

   if(!RegisterClass(&wcl)) // Зарегистр. класс окна

      return 0;

   // Создать окно

   hwnd=CreateWindow(szWinName,          // Имя класса

          " Демонстрация работы с библиотекой MyDLL ", 

                     WS_OVERLAPPEDWINDOW,// Стиль окна

                     CW_USEDEFAULT,    // Х-координата

                     CW_USEDEFAULT,    // Y-координата

                     CW_USEDEFAULT,     // Ширина окна

                     CW_USEDEFAULT,     // Высота окна

                     HWND_DESKTOP,  // Нет родит. окна 

                     NULL,                 // Нет меню

                     hThisInst, // Дескрип. приложения 

                     NULL); // Без дополит. аргументов

   // Загрузить акселераторы

   hAccel=LoadAccelerators(hThisInst,"MYMENU");

   ShowWindow(hwnd,nWinMode) ; // Показать окно и 

   UpdateWindow(hwnd);      // перерисовать содержимое

   // Запустить цикл обработки сообщений

   while(GetMessage (&msg,NULL,0,0))

      if(!TranslateAccelerator(hwnd,hAccel,&msg))

         {

         TranslateMessage(&msg); // Использ.Клавиатуры

         DispatchMessage (&msg); // Возврат к Windows

         }

   return msg. wParam;

   }

 

// Следующая функция вызывается операционной системой

// Windows и получает в качестве параметров сообщения

// из очереди сообщений данного приложения

 

LRESULT CALLBACK WindowFunc(HWND hwnd,

                            UINT message,

                            WPARAM wParam,

                            LPARAM lParam)

   {

   HDC hdc;

   PAINTSTRUCT paintstruct;

   TEXTMETRIC tm;

   SIZE size;

   switch(message)

      {

      case WM_CREATE:       // Получаем размеры экрана

         maxX=GetSystemMetrics(SM_CXSCREEN);

         maxY=GetSystemMetrics(SM_CYSCREEN);

         hdc=GetDC(hwnd);   // Совмест. с окном растр

         memdc=CreateCompatibleDC(hdc);

         hbit=CreateCompatibleBitmap(hdc,maxX,maxY);

         SelectObject(memdc,hbit);

         hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);

         SelectObject(memdc,hbrush);

         PatBlt(memdc,0,0,maxX,maxY,PATCOPY);

         ReleaseDC(hwnd,hdc);

         break;

      case WM_COMMAND:

         switch(LOWORD(wParam))

            {

            case ID_SHOW:

               SetTextColor(memdc,RGB(0,0,0)); // Черн

               SetBkColor(memdc,RGB(0,255,255)); //Син

               GetTextMetrics(memdc,&tm);   // Метрики

               sprintf(str,

                       "Высота шрифта %ld пикселей",

                       tm.tmHeight);

               TextOut(memdc,X,Y,str,strlen(str));

               Y=Y+tm.tmHeight            // Следующая

                  +tm.tmExternalLeading;  // строка

               strcpy(str,"Это следующая строка.");

               TextOut(memdc,X,Y,str,strlen(str));

               GetTextExtentPoint32(memdc,str, //Длина

                                    strlen(str), 

                                    &size);

               sprintf(str,

                       "Длина предыдущей строки %ld", 

                       size.cx);

               X=size.cx; // В конец предыдущей строки

               TextOut(memdc,X,Y,str,strlen(str));

               Y=Y+tm.tmHeight            // Следующая

                  +tm.tmExternalLeading;  // строка

               X=0;                // X опять в начало

               sprintf(str,"Размеры экрана %d x %d",

                       maxX,maxY);

               TextOut(memdc,X,Y,str,strlen(str));

               Y=Y+tm.tmHeight            // Следующая

                  +tm.tmExternalLeading;  // строка

               InvalidateRect(hwnd,NULL,1); //Сообщить

               break;

            case ID_RESET:

               X=Y=0;     // Стереть перерисовкой фона

               PatBlt(memdc,0,0,maxX,maxY,PATCOPY);

               InvalidateRect(hwnd,NULL,1); //Сообщить

               break;

            case ID_LOAD:

               // Если файл MyDLL.DLL уже загружен,

               // сообщить

               // пользователю и завершить эту функцию

               if(gLibMyDLL != NULL)

                  MessageBox(hwnd,

                     "Библиотека MyDLL уже загружена",

                     "Внимание:",MB_OK);

               else

                  {

                  MessageBox(hwnd,"Загрузить DLL",

                                  "Команда:",MB_OK);

                  // Загрузить MyDLL.dll

                  gLibMyDLL=LoadLibrary("MYDLL.DLL");

                  // Если DLL не был загружен, вывести

                  // окно сообщения об ошибке

                  if(gLibMyDLL == NULL)

                     {

                     MessageBox(hwnd,

                        "Невозможно загрузить MyDLL. "

                        "Убедитесь, что она находится"

                        " в соответствующей папке",

                        "Ошибка:",MB_OK);

                     return 0;

                     }

                  }

               // Получить адрес функции МуВеер()

               // библиотеки MyDLL.dll

               MyBeep=(MYBEEP)

                      GetProcAddress(gLibMyDLL,

                                     "MyBeep");

               // Получить адрес функции MyDelay()

               // библиотеки MyDLL.dll

               MyDelay=(MYDELAY)

                       GetProcAddress(gLibMyDLL,

                                      "MyDelay");

               InvalidateRect(hwnd,NULL,1); //Сообщить

               break;

            case ID_BEEP:

               if(MyDelay != NULL && MyBeep != NULL 

                          && gLibMyDLL != NULL)

                  {

                  // Вызвать функцию MyBeep()

                  MyBeep(600,250); // 600 Гц, 0,25 сек

                  // Вызвать функцию MyDelay()

                  MyDelay(500);    // 0,5 сек

                  // Вызвать функцию MyBeep()

                  MyBeep(300,250); // 300 Гц, 0,25 сек

                  MessageBox(hwnd,"Звуковой сигнал",

                                  "Команда:", MB_OK);

                  }

               else

                  MessageBox(hwnd,

                          "Причины: библиотека MyDLL "

                          "не загружена, либо "

                          "отсутствуют функции "

                          "MyBeep и MyDelay ",

                          "Ошибка:", MB_OK);

               InvalidateRect(hwnd,NULL,1); //Сообщить

               break;

            case ID_HELP:

               MessageBox(hwnd,"F2: Вывести текст  \n"

                               "F3: В начала экрана\n"

                               "F4: Загрузить DLL  \n"

                               "F5: Звуковой сигнал",

                               "Помощь",MB_OK);

               InvalidateRect(hwnd,NULL,1); //Сообщить

               break;

            }

         break;

      case WM_PAINT:               // Перерисовка окна

         hdc=BeginPaint(hwnd,&paintstruct); // Пол. DC

         // Теперь копируем растр из памяти на экран

         BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);

         EndPaint(hwnd,&paintstruct); // Освободить DC

         break;

      case WM_DESTROY:         // Завершение программы

         DeleteDC(memdc);  // Удалить виртуальное окно

         PostQuitMessage(0);

         break;

      default:

         // Все сообщения, не обрабатываемые в данной

         // функции, направляются на обработку по 

         // умолчанию

         return DefWindowProc(hwnd,message,

                              wParam,lParam);

      }

   return 0;

   }

 

Результаты выполнения функций из динамической библиотеки показаны на рис. 13.2.

Рис. 13.2. Демонстрация работы с библиотекой MyDLL