Лабораторная работа: Динамически загружаемые библиотеки

Цель работы

Изучить связывание процесса с динамически загружаемыми библиотеками на этапе загрузки и на этапе выполнения.

Список используемых системных вызовов

LoadLibrary, FreeLibrary, FreeLibraryAndExitThread, GetModuleHandle, GetProcAddress, DllMain.

Методические указания

Динамически загружаемые библиотеки (DLL) связываются с программами и между собой с помощью специальных таблиц экспорта и импорта, находящихся внутри файлов библиотек и программ. Просмотреть содержимое этих таблиц можно с помощью утилиты dumpbin.exe. Ниже приведены примеры использования этой утилиты для библиотеки kernel32.dll:
dumpbin.exe kernel32.dll \exports > kernel32.exports
dumpbin.exe kernel32.dll \imports > kernel32.imports
Таблица импорта содержит запрашиваемые ресурсы, таблица экспорта – предоставляемые ресурсы библиотеки.
Для создания динамически загружаемой библиотеки необходимо указать при создании тип проекта DLL. У DLL отсутствует функция WinMain. Вместо нее используется функция DllMain, вызываемая в четырех случаях:
1.      при загрузке библиотеки процессом (отображении в виртуальное адресное пространство процесса);
2.      при создании новой нити;
3.      при завершении созданной нити;
4.      при завершении процесса или при выгрузке библиотеки.
Любую функцию, переменную или класс библиотеки можно сделать экспортируемыми, т.е. подключаемыми извне с помощью таблицы экспорта. Чтобы адрес ресурса был помещен в таблицу экспорта, необходимо указать непосредственно перед определением ресурса ключевые слова __declspec(dllexport). Примеры экспортирования ресурсов:
__declspec(dllexport) int i;
__declspec(dllexport) void func();
class __declspec(dllexport) Class;
DLL могут загружаться процессом при старте программы (динамическое связывание) или явно с помощью функции LoadLibrary. После компиляции библиотеки компоновщик создает два файла для каждой динамически загружаемой библиотеки – с расширениями .lib и .dll. Файл с расширением .lib необходимо подключить при компоновке программы, использующей динамическое связывание. Это делается в опциях проекта для компоновщика «additional dependencies». Программа может импортировать ресурсы из DLL с помощью ключевых слов __declspec(dllimport). Примеры импортирования ресурсов:
__declspec( dllimport ) int i;
__declspec(dllimport) void func();
class __declspec(dllimport) Class;
При запуске программы файл библиотеки с расширением .dll должен находиться в одном каталоге с файлом программы или быть доступен по путям поиска.
Второй способ загрузки динамической библиотеки основывается на вызове функции LoadLibrary. В параметре lpFileName явно указывается путь до файла библиотеки. Результатом вызова этой функции является загрузка библиотеки в виртуальное адресное пространство процесса. Процессу становится доступен дескриптор (HMODULE) библиотеки. Для получения адреса ресурса, находящегося в библиотеке, необходимо вызвать функцию GetProcAddress. Параметр lpProcName должен содержать указатель на верное имя ресурса. Следует отметить, что имя ресурса в таблице экспорта отличается от имени ресурса, определенного в библиотеке. Его следует получать с помощью утилиты dumpbin.exe.
Для выгрузки DLL из виртуального адресного пространства процесса используется функция FreeLibrary. В параметре hModule следует указать дескриптор библиотеки, полученный с помощью функции LoadLibrary.

Задания

Необходимо разработать программу состоящую из головной программы и двух динамически загружаемых библиотек. Одна библиотека должна загружаться с использованием динамического связывания, а другая – с использованием функции LoadLibrary. Библиотеки должны выполнять действия по вариантам заданий из лабораторной работы №3.

                                                          
Листинг программы.
laba6.cpp
#include <stdio.h>
#include <iostream>
#include <windows.h>
#include <conio.h>

using namespace std;

#include "laba3_1.h"
typedef  void (WINAPI *cFunc)();

void PrepareStatic();

void main()
{

      //явный вызов динамической библиотеки
      printf("\nWork functoin from dll.dll!!!");

//***************************************************************
      HINSTANCE hModule=NULL;
      hModule=::LoadLibrary("dll.dll");
      if (hModule!=NULL)
      {
            cFunc Schet = (cFunc)::GetProcAddress((HMODULE)hModule,"Schet");
            if (Schet!=NULL)
            {
                  Schet();
            }
            else cout << "Error Load function" << endl;
            ::FreeLibrary(hModule);
      }
      else cout << "error load Dll" << endl;

      PrepareStatic();
      Schet();

      getch();
}

void PrepareStatic()
{
#pragma comment(lib, "dll.lib")
}
laba3_1.h
#ifndef _DLLTEST_H_
#define _DLLTEST_H_

#include <iostream>
#include <stdio.h>
#include <windows.h>


extern "C" __declspec(dllexport) void Schet();


#endiflaba3.h
#ifndef _DLLTEST_H_
#define _DLLTEST_H_

#include <iostream.h>
#include <stdio.h>
#include <windows.h>


extern "C" __declspec(dllexport) void Schet();


#endif
laba3.cpp
extern "C" __declspec(dllexport) void Schet()
{
      HANDLE hFile;
      OVERLAPPED over;
      DWORD dByte,dwError;
      int iByteToRead=1;
      BOOL bResult;
      int iWord=0,tek=0,pred=0,iBreak=0,iBegin=1,iEnd=1;         //новое слово - tek=1,pted=0;
     
     
     
      over.Offset=0;
      over.OffsetHigh=0;
     
      char cBuffer[1];
      // Создаём событие для контроля за асинхронным чтением
      over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
      if(over.hEvent==NULL)
      {
            printf("\nError create event!!!");
            exit(0);
            // Ошибка создания события
      }
     
      hFile=CreateFile("test.txt",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
      if(hFile==INVALID_HANDLE_VALUE)
      {
            printf("\nError opening file!!!");
            exit(0);
      }
      while(1)
      {
            bResult = ReadFile(hFile,cBuffer,iByteToRead,&dByte,&over);
            // если возникает проблема или асинхронная операция
            // все еще ожидает обработки ...
           
            if (!bResult)
            {
                  // решаем что делать с кодом ошибки
                  switch (dwError = GetLastError())
                  {
                  case ERROR_HANDLE_EOF:
                        {
                             // мы достигли конца файла
                             // в течение вызова к ReadFile
                             iBreak=1;
                             break;
                        }
                       
                  case ERROR_IO_PENDING:
                        {
                             // асинхронный ввод-вывод все еще происходит
                            
                             // сделаем кое-что пока он идет
                             //    GoDoSomethingElse() ;
                             over.Offset++;
                            
                             pred=tek;
                             if(cBuffer[0]==' '|| cBuffer[0]=='\x0D' || cBuffer[0]=='\n')tek=0;
                             else tek=1;
                             if(tek==1 && pred==0)iWord++;
                             // проверим результат работы асинхронного чтения
                             bResult = GetOverlappedResult(hFile, &over,    &dByte, FALSE) ;
                            
                             // если возникла проблема ...
                             if (!bResult)
                             {
                                   // решаем что делать с кодом ошибки
                                   switch (dwError = GetLastError())
                                   {
                                   case ERROR_HANDLE_EOF:
                                         {
                                               // мы достигли конца файла
                                               // в ходе асинхронной
                                               // операции
                                               iBreak=1;
                                               break;
                                         }
                                   default:
                                         {
                                               printf("\nError work to file!!!");
                                               iBreak=1;
                                               break;// решаем что делать с другими случаями ошибок
                                         }
                                   }// конец процедуры switch (dwError = GetLastError())
                             }
                             break;
                        } // конец процедуры case
                  default:
                        {
                             printf("\nError work to file!!!");
                             iBreak=1;
                             break;      // решаем что делать с другими случаями ошибок
                        }
                       
                  } // конец процедуры switch (dwError = GetLastError())
            } // конец процедуры if
            if(iBreak==1)break;
            over.Offset++;
            pred=tek;
            if(cBuffer[0]==' ' || cBuffer[0]=='\x0D' || cBuffer[0]=='\n')tek=0;
            else tek=1;
            if(tek==1 && pred==0)iWord++;
           
           
      }
      printf("\nIn text %d words!!!\n", iWord);

}

Вывод: изучили связывание процесса с динамически загружаемыми библиотеками на этапе загрузки и на этапе выполнения.

Комментариев нет:

Отправить комментарий