Для студентов згиа специальности 080403 ”Программное обеспечение




НазваниеДля студентов згиа специальности 080403 ”Программное обеспечение
страница7/34
Дата публикации25.02.2013
Размер3.71 Mb.
ТипМетодическое пособие
uchebilka.ru > Информатика > Методическое пособие
1   2   3   4   5   6   7   8   9   10   ...   34
раздел импорта, где перечисляются имена всех необходимых DLL-модулей. Кроме того, для каждой DLL в этом разделе указывается, на какие символьные имена функций и переменных ссылается двоичный код исполняемого файла. Эти сведения потребуются загрузчику операционной системы.

Создав DLL- и EXE-модули, приложение можно запустить. При его запуске загрузчик операционной системы выполняет следующие операции:

  1. Загрузчик операционной системы создает виртуальное адресное пространство для нового процесса и проецирует на него исполняемый модуль.

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

После отображения EXE- и всех DLL-модулей на адресное пространство процесса его первичный поток готов к выполнению, и приложение может начать работу.

^ Что такое экспорт

Если перед переменной, прототипом функции или C++-классом DLL стоит модификатор __declspec(dllexport), компилятор Microsoft С/C++ встраивает в конечный OBJ-файл дополнительную информацию. Она понадобится компоновщику при сборке DLL из OBJ-файлов.

Обнаружив такую информацию, компоновщик создает LIB-файл со списком идентификаторов, экспортируемых из DLL. Этот LIB-файл нужен при сборке любого EXE-модуля, ссылающегося на такие идентификаторы. Компоновщик также вставляет в конечный DLL-файл таблицу экспортируемых идентификаторов — раздел экспорта, в котором содержится список (в алфавитном порядке) идентификаторов экспортируемых функций, переменных и классов. Туда же помещается относительный виртуальный адрес (relative virtual address, RVA) каждого идентификатора внутри DLL-модуля.

^ Что такое импорт

Пусть мы формируем исходный код EXE-модуля, который импортирует идентификаторы, экспортируемые DLL, и ссылается на них в процессе выполнения. Рекомендуется пользоваться ключевым словом __declspec(dllimport) для импортируемых функций и идентификаторов данных. Разрешая ссылки на импортируемые идентификаторы, компоновщик создает в конечном EXE-модуле раздел импорта (imports section). В нем перечисляются DLL, необходимые этому модулю, и идентификаторы, на которые есть ссылки из всех используемых DLL. Воспользовавшись утилитой DumpBin.exe (с ключом -imports), мы можем увидеть содержимое раздела импорта.

^ Выполнение EXE-модуля

При запуске EXE-файла загрузчик операционной системы создает для его процесса виртуальное адресное пространство и проецирует на него исполняемый модуль. Далее загрузчик анализирует раздел импорта и пытается спроецировать все необходимые DLL на адресное пространство процесса.

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

  1. Каталог, содержащий EXE-файл.

  2. Текущий каталог процесса.

  3. Системный каталог Windows.

  4. Основной каталог Windows.

  5. Каталоги, указанные в переменной окружения PATH.


Явная загрузка DLL

Чтобы поток мог вызвать функцию из DLL-модуля, последний надо спроецировать на адресное пространство процесса, которому принадлежит этот поток. Делается это двумя способами. Первый состоит в том, что код Вашего приложения просто ссылается на идентификаторы, содержащиеся в DLL, и тем самым заставляет загрузчик неявно загружать (и связывать) нужную DLL при запуске приложения.

Второй способ — явная загрузка и связывание требуемой DLL в период выполнения приложения. Иначе говоря, его поток явно загружает DLL в адресное пространство процесса, получает виртуальный адрес необходимой DLL-функции и вызывает ее по этому адресу. Изящество такого подхода в том, что все происходит в уже выполняемом приложении [3].

В любой момент поток может спроецировать DLL на адресное пространство процесса, вызвав одну из двух функций:

HINSTANCE LoadLibrary(PCTSTR pszDLLPathName);

HINSTANCE LoadLibraryEx( PCTSTR pszDLLPathName, HANDLE hFile, DWORD dwFlags);

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

Функция LoadLibraryEx имеет два дополнительных параметра – hFile и dwFlags. Первый зарезервирован для использования в будущих версиях и должен быть NULL. Во втором можно передать либо 0, либо комбинацию флагов DONT_RESOLVE_DLL_REFERENCES, LOAD_LIBRARY_AS_DATAFILE и LOAD_WITH_ALTERED_SEARCH_PATH.

DONT_RESOLVE_DLL_REFERENCES

Этот флаг указывает системе спроецировать DLL на адресное пространство вызывающего процесса. Проецируя DLL, система обычно вызывает из нее специальную функцию DllMain (о ней — чуть позже) и с ее помощью инициализирует библиотеку. Так вот, данный флаг заставляет систему проецировать DLL, не обращаясь к DllMain.

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

LOAD_LIBRARY_AS_DATAFILE

Этот флаг очень похож на предыдущий. DLL проецируется на адресное пространство процесса так, будто это файл данных.

Этот флаг может понадобиться по нескольким причинам. Во-первых, его стоит указать, если DLL содержит только ресурсы и никаких функций. Во-вторых, он пригодится, если Вам нужны ресурсы, содержащиеся в каком-нибудь EXE-файле.

^ Явная выгрузка DLL

Если необходимость в DLL отпадает, ее можно выгрузить из адресного пространства процесса, вызвав функцию:

BOOL FreeLibrary(HINSTANCE hinstDll);

Вы должны передать в FreeLibrary значение типа HINSTANCE, которое идентифицирует выгружаемую DLL. Это значение Вы получаете после вызова LoadLibrary(Ex).

Необходимо также помнить, что на самом деле LoadLibrary и LoadLibraryEx лишь увеличивают счетчик числа пользователей указанной библиотеки, а FreeLibrary его уменьшает.

Чтобы определить, спроецирована ли DLL на адресное пространство процесса, поток может вызывать функцию GetModuleHandle:

HINSTANCE GetModuleHandle(PCTSTR pszModuleName);
^ Явное подключение экспортируемого идентификатора

Поток получает адрес экспортируемого идентификатора из явно загруженной DLL вызовом GetProcAddress:

FARPROC GetProcAddress( HINSTANCE hinstDll, PCSTR pszSymbolName);

Параметр hinstDll — описатель, возвращенный LoadLibrary(Ex) или GetModuleHandle и относящийся к DLL, которая содержит нужный идентификатор. Параметр pszSymbolName разрешается указывать в двух формах. Во-первых, как адрес строки с нулевым символом в конце, содержащей имя интересующей Вас функции:

FARPROC pfn = GetProcAddress(hinstDll, "SomeFuncInDll");

Заметьте, тип параметра pszSymbolName — PCSTR, а не PCTSTR. Это значит, что функция GetProcAddress принимает только ANSI-строки — ей нельзя передать Unicode-строку. А причина в том, что идентификаторы функций и переменных в разделе экспорта DLL всегда хранятся как ANSI-строки.

Вторая форма параметра pszSymbolName позволяет указывать порядковый номер нужной функции:

FARPROC pfn = GetProcAddress(hinstDll, MAKEINTRESOURCE(2));

Здесь подразумевается, что Вам известен порядковый номер (2) искомого идентификатора, присвоенный ему автором данной DLL. Microsoft настоятельно не рекомендует пользоваться порядковыми номерами; поэтому Вы редко встретите второй вариант вызова GetProcAddress.

При любом способе Вы получаете адрес содержащегося в DLL идентификатора. Если идентификатор не найден, GetProcAddress возвращает NULL.

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

// тип PFN_MyFunction будет объявлять указатель на функцию,

// принимающую указатель на буфер и выдающую значение типа int

typedef int (WINAPI *PFN_MyFunction)(char *);

PFN_MyFunction pfnMyFunction;

Затем следует получить дескриптор библиотеки, при помощи которого и определить адреса функций, например адрес функции с именем MyFunction:

hMyDll=::LoadLibrary("MyDLL");

pfnMyFunction=(PFN_MyFunction)::GetProcAddress(hMyDll,"MyFunction");

int iCode=(*pfnMyFunction)("Hello");//вызов функции

^ Функция входа/выхода

В DLL, может быть лишь одна функция входа/выхода. Система вызывает ее в некоторых ситуациях (о чем речь еще впереди), и обычно она используется DLL для инициализации и очистки ресурсов в конкретных процессах или потоках. Если Вашей DLL подобные уведомления не нужны, Вы не обязаны реализовывать эту функцию. Пример — DLL, содержащая только ресурсы. Но если же уведомления необходимы, функция должна выглядеть так:

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad)

{

switch (fdwReason) {
case DLL_PROCESS_ATTACH:

// DLL проецируется на адресное пространство процесса

break;
case DLL_THREAD_ATTACH:

// создается поток

break;
case DLL_THREAD_DETACH:

// поток корректно завершается

break;
case DLL_PROCESS_DETACH

// DLL отключается от адресного пространства процесса

break;

}
return(TRUE);

// используется только для DLL_PROCESS_ATTACH

}

Параметр hinstDll содержит описатель экземпляра DLL. Это значение — виртуальный адрес проекции файла DLL на адресное пространство процесса. Последний параметр, fImpLoad, отличен от 0, если DLL загружена неявно, и равен 0, если она загружена явно.

Параметр fdwReason сообщает о причине, по которой система вызвала эту функцию. Он принимает одно из четырех значений: DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH или DLL_THREAD_DETACH.


    1. ^ Работа с дисками, файлами, каталогами в Win32

      1. Получение информации о дисках, установленных на компьютере

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

DWORD GetLogicalDrives(VOID);

Двойное слово, которое возвращает эта функция, фактически является логической шкалой, нулевой бит (самый младший) в которой соответствует диску A, первый бит – диску B, второй – диску C и так далее.

Пример:

int n;

char dd[4];

DWORD dr = GetLogicalDrives();
for( int i = 0; i < 26; i++ )

{

n = ((dr>>i)&0x00000001);

if( n == 1 )

{

dd[0] = char(65+i); dd[1] = ':'; dd[2] = '\'; dd[3] = 0;

cout << "Available disk drives : " << dd << endl;

}

}

Более удобной является функция GetLogicalDriveStrings:

DWORD GetLogicalDriveStrings(

DWORD nBufferLength, // size of buffer

LPTSTR lpBuffer // drive strings buffer

);

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

Узнать типы дисков можно с помощью функции GetDriveType:

UINT GetDriveType(

LPCTSTR lpRootPathName // root directory

);

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

Значение

Описание

DRIVE_UNKNOWN

Тип диска определить не удалось.

DRIVE_NO_ROOT_DIR

Корневой директории не существует.

DRIVE_REMOVABLE

Дисковод со сменным носителем – дискета или.

DRIVE_FIXED

Жесткий диск.

DRIVE_REMOTE

Удаленный дисковод – сетевой диск.

DRIVE_CDROM

Дисковод для CD-ROM дисков.

DRIVE_RAMDISK

Эмулируемый в оперативной памяти диск – RAM disk.

Более подробную информацию о диске (серийный номер, тип файловой системы и т.д.) можно получить с помощью функции GetVolumeInformation.

Информацию о разбивке тома на сектора и кластеры, а также информацию о свободном пространстве на диске можно получить с помощью функции GetDiskFreeSpace:

BOOL GetDiskFreeSpace(

__in LPCTSTR lpRootPathName,//корневая директория диска

__out LPDWORD lpSectorsPerCluster,//количество секторов в кластере

__out LPDWORD lpBytesPerSector,// количество байтов в секторе

__out LPDWORD lpNumberOfFreeClusters,//количество свободных кластеров

__out LPDWORD lpTotalNumberOfClusters// общее количество кластеров

);

Эта функция не работает для дисков, размер которых превышает 2Gb. Для таких дисков рекомендуется использовать функцию GetDiskFreeSpaceEx.

Функция GetDiskFreeSpaceEx выдаёт информацию о доступном месте на диске.

BOOL GetDiskFreeSpaceEx(

LPCTSTR lpDirectoryName, // имя диска(директории) [in]

PULARGE_INTEGER lpFreeBytesAvailable, // доступно для использования(байт) [out]

PULARGE_INTEGER lpTotalNumberOfBytes, // максимальный объём( в байтах ) [out]

PULARGE_INTEGER lpTotalNumberOfFreeBytes // свободно на диске( в байтах ) [out]

);

Пример:

DWORD FreeBytesAvailable;

DWORD TotalNumberOfBytes;

DWORD TotalNumberOfFreeBytes;
BOOL GetDiskFreeSpaceFlag = GetDiskFreeSpaceEx(

"c:\\", // directory name

(PULARGE_INTEGER)&FreeBytesAvailable, // bytes available to caller

(PULARGE_INTEGER)&TotalNumberOfBytes, // bytes on disk

(PULARGE_INTEGER)&TotalNumberOfFreeBytes // free bytes on disk

);
if(GetDiskFreeSpaceFlag != 0)

{

cout << " Total Number Of Free Bytes = " << (unsigned long)TotalNumberOfFreeBytes

<< "( " << double(unsigned long(TotalNumberOfFreeBytes))/1024/1000

<< " Mb )" << endl;

cout << " Total Number Of Bytes = "

<< (unsigned long)TotalNumberOfBytes

<< "( " << double(unsigned long(TotalNumberOfBytes))/1024/1000

<< " Mb )" << endl;

}

else cout << " Not Present (GetDiskFreeSpace)" << endl;

Если необходимо работать с диском на более низком уровне, то удобно использовать функцию DeviceIoControl, которая предназначена для посылки команд и приема информации непосредственно от драйвера диска [5].

      1. ^ Работа с каталогами

Функция GetCurrentDirectory позволяет получить путь к текущему каталогу данного процесса:

DWORD GetCurrentDirectory(DWORD nBufferLength, LPTSTR lpBuffer);

Функция SetCurrentDirectory изменяет рабочий каталог для текущего процесса:

BOOL SetCurrentDirectory(LPCTSTR lpPathName);

Функция GetSystemDirectory возвращает путь к системной директории:

UINT GetSystemDirectory(

LPTSTR lpBuffer, // буфер для системной директории [out]

UINT uSize // размер буфера [in]

);

Функция GetWindowsDirectory возвращает путь к Windows директории.

UINT GetWindowsDirectory(

LPTSTR lpBuffer, // буфер для Windows директории [out]

UINT uSize // размер буфера [in]

);

Для создания директории надо использовать функцию CreateDirectory:

BOOL CreateDirectory(

LPCTSTR lpPathName,

LPSECURITY_ATTRIBUTES lpsa

);

Первый аргумент этой функции – это указатель на буфер, в котором записано наименование той директории, которую мы пытаемся создать.

Для того чтобы удалить директорию, нам необходимо воспользоваться функцией RemoveDirectory.

      1. ^ Работа с файлами
1   2   3   4   5   6   7   8   9   10   ...   34

Похожие:

Для студентов згиа специальности 080403 ”Программное обеспечение iconМетодические указания к лабораторному практикуму для студентов згиа...
Методические указания к лабораторному практикуму для студентов згиа специальности 080403 «Программное обеспечение автоматизированных...

Для студентов згиа специальности 080403 ”Программное обеспечение iconМетодические указания для студентов специальности 080403
Згиа [8, 10] и других вузов [4]. Наиболее полно описаны курсовые, дипломные и квалификационные работы, однако большинство положений...

Для студентов згиа специальности 080403 ”Программное обеспечение iconМетодические указания к курсовой работе по дисциплине «Системное...
Методические указания к курсовой работе по дисциплине «Системное программирование и операционные системы» для студентов специальности...

Для студентов згиа специальности 080403 ”Программное обеспечение iconПравила использования программного обеспечения в рамках программы...
Используя программное обеспечение вы тем самым подтверждаете свое согласие придерживаться этих правил. Если вы не согласны, не используйте...

Для студентов згиа специальности 080403 ”Программное обеспечение iconМетодические указания по выполнению лабораторных работ по курсу “
Информационные управляющие системы и технологии, 080403 – Программное обеспечение автоматизированных систем

Для студентов згиа специальности 080403 ”Программное обеспечение iconКлассификация программного обеспечения
В отличие от аппаратного обеспечения, программы, которые выполняются на нем, неосязаемы и классифицируются как программное обеспечение....

Для студентов згиа специальности 080403 ”Программное обеспечение iconКурсовая работа выполняется на основании 'Задания на курсовую работу'...
Целью курсовой работы является закрепление практических навыков самостоятельной постановки и решения задачи обработки данных с помощью...

Для студентов згиа специальности 080403 ”Программное обеспечение icon1. Классификация программного обеспечения
Назначением ЭВМ является выполнение программ. Программа содержит команды, определяющие порядок действии компьютера. Совокупность...

Для студентов згиа специальности 080403 ”Программное обеспечение iconОпорный конспект лекций по дисциплине Компьютерная графика для специальности...
Тема. Основные понятия компьютерной графики. Аппаратное и программное обеспечение

Для студентов згиа специальности 080403 ”Программное обеспечение iconМетодические указания и задание к выполнению курсового проекта по...
Методические указания и задание к выполнению курсового проекта по дисциплине «Алгоритмическое и программное обеспечение электротехнических...

Вы можете разместить ссылку на наш сайт:
Школьные материалы


При копировании материала укажите ссылку © 2013
контакты
uchebilka.ru
Главная страница


<