Цель работы
Изучить связывание процесса с динамически загружаемыми
библиотеками на этапе загрузки и на этапе выполнения.
Список используемых системных вызовов
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);
}
Вывод: изучили связывание процесса с
динамически загружаемыми библиотеками на этапе загрузки и на этапе выполнения.
Комментариев нет:
Отправить комментарий