Наблюдение за изменениями в каталоге
HANDLE FindFirstChangeNotification (
LPCTSTR lpPathName, // имя каталога
BOOL bWatchSubtree, // опция наблюдения
DWORD dwNotifyFilter // условия фильтра );
В случае неудачи функция возвращает значение INVALID_HANDLE_VALUE.
dwNotifyFilter задает события, которые отслеживаются:
FILE_NOTIFY_CHANGE_FILE_NAME– изменение имени файла;
FILE_NOTIFY_CHANGE_DIR_NAME– изменение имени каталога;
FILE_NOTIFY_CHANGE_ATTRIBUTES– изменение атрибутов;
FILE_NOTIFY_CHANGE_SIZE– изменение размеров;
Функция для наблюдения за последующими изменениями, происходящими в каталоге.
BOOL FindNextChangeNotification(
HANDLE hChangeHandle // дескриптор для наблюдения за изменениями
);
После завершения наблюдения за изменениями в каталоге нужно вызвать функцию FindCloseChangeNotification, которая закрывает дескриптор наблюдения за изменениями в каталоге.
BOOL FindCloseChangeNotification(
HANDLE hChangeHandle // дескриптор для наблюдения за изменениями
);
Листинг 1. Пример приложения, демонстрирующего работу с каталогами.
#include <windows.h>
#include <iostream>
#include <conio.h>
using namespace std;
int main()
{
char buf[256],buf1[256];
int iMenu;
char cMenu[200]="ВЫБЕРИТЕ ДЕЙСТВИЕ:\n1 - СОЗДАТЬ КАТАЛОГ\n2 - НАЙТИ ФАЙЛЫ В КАТАЛОГЕ\n3 - УДАЛИТЬ КАТАЛОГ\n4 - ПЕРЕМЕСТИТЬ КАТАЛОГ\n5 - ОПРЕДЕЛИТЬ/ИЗМЕНИТЬ ТЕКУЩИЙ КАТАЛОГ\n6 - НАБЛЮДАТЬ ЗА КАТАЛОГОМ\n7 - ВЫЙТИ\n";
CharToOem(cMenu, cMenu);
while(1)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
LONG cFiles=1;
BOOL bFindMore = TRUE;
system("cls");
cout << cMenu; cin >> iMenu;
switch(iMenu)
{
system("cls");
case 1:
CharToOem("СОЗДАТЬ КАТАЛОГ\n Имя каталога? ", buf); cout << buf; cin >> buf;
if(!CreateDirectory(buf,NULL))
{
cerr << "Create directory failed!" <<endl
<< "The last error code: " << GetLastError() <<endl;
cout << "Press any key to continue";
getch();
break;
}
else
cout << "directory created";
getch();
break;
case 2:
CharToOem("НАЙТИ ФАЙЛ В КАТАЛОГЕ\n Спецификация файла? ", buf); cout << buf; cin >> buf;
if ((hFind=FindFirstFile(buf,&FindFileData)) == INVALID_HANDLE_VALUE)
{
cerr << endl << "Find First File failed!" << endl
<< "The last error code: " << GetLastError() <<endl;
cout << "Press any key to continue";
getch();
break;
}
cout << endl << FindFileData.cFileName << endl;
while (bFindMore)
{
if(bFindMore = FindNextFile(hFind, &FindFileData))
{
cout << FindFileData.cFileName << endl;
cFiles++;
}
}
cout << endl << "The search is completed." << endl
<< cFiles << " files were found" << endl;
cout<<"\nPress any key to continue!";
getch();
break;
case 3:
CharToOem("УДАЛИТЬ КАТАЛОГ\n Имя каталога?", buf); cout << buf;cin>>buf;
if(!RemoveDirectory(buf))
{
cerr << endl << "Remove Directory failed!" << endl
<< "The last error code: " << GetLastError() <<endl;
cout << "Press any key to continue";
getch();
break;
}
cout << "directory removed";
cout<<"\nPress any key to continue!";
getch();
break;
case 4:
CharToOem("ПЕРЕМЕСТИТЬ КАТАЛОГ\n Имя каталога источника?", buf); cout << buf; cin >> buf;
CharToOem("Имя каталога получателя?", buf1); cout << buf1;cin >> buf1;
if(!MoveFile(buf,buf1))
{
cerr << endl << "Move Directory failed!" << endl
<< "The last error code: " << GetLastError() <<endl;
cout << "Press any key to continue";
getch();
break;
}
cout << "directory moved";
cout<<"\nPress any key to continue!";
getch();
break;
case 5:
CharToOem("ТЕКУЩИЙ КАТАЛОГ:\n", buf); cout << buf;
GetCurrentDirectory(sizeof(buf),buf);
cout << buf <<endl;
CharToOem("Изменить текущий каталог?(y/n) ", buf); cout << buf; cin >> buf;
if(*buf=='y')
{
CharToOem("Задайте новый каталог! ", buf);cout << buf; cin >> buf;
if(!SetCurrentDirectory(buf))
{
cerr << endl << "Set Current failed!" << endl
<< "The last error code: " << GetLastError() <<endl;
}
else cout << "The current directory is set" << endl;
}
cout<<"\nPress any key to continue!";
getch();
break;
case 6:
CharToOem("НБЛЮДАТЬ ЗА КАТАЛОГОМ\n Имя каталога?", buf); cout << buf; cin>>buf;
if ((hFind=FindFirstChangeNotification(buf,false, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_SIZE)) == INVALID_HANDLE_VALUE)
{
cerr << endl << "Find First Notification failed!" << endl
<< "The last error code: " << GetLastError() <<endl;
cout << "Press any key to continue";
getch();
break;
} else cout << "Watch.." << endl;
while (WaitForSingleObject(hFind,10000)!=WAIT_OBJECT_0) { }
cout << endl << "The watched directory has been changed." << endl;
bFindMore=false;
cout << "Find next change notification?(y/n)"; cin >>buf;
if(*buf='y') bFindMore=true;
while (bFindMore)
{
if(bFindMore = FindNextChangeNotification(hFind))
{
cout << "Watch.." << endl;
while (WaitForSingleObject(hFind, 10000) != WAIT_OBJECT_0) {}
cout << endl << "The watched directory has been changed again." << endl;
cout << "Find next change notyfication?(y/n)"; cin >>buf;
if(*buf=='y') bFindMore=true; else bFindMore=false;;
}
else
{
cerr << endl << "Find Notification failed!" << endl
<< "The last error code: " << GetLastError() <<endl;
bFindMore=false;
}
}
FindCloseChangeNotification(hFind);
cout<<"\nPress any key to continue!";
getch();
break;
case 7: return 0;
}
}
return 0;
}
Задание на выполнение
1. Написать программу в которой были бы реализованы следующие функции работы с каталогами:
− создание каталога;
− поиск файлов в каталоге;
− удаление каталога;
− перемещение каталога;
− определение и установка текущего каталога;
− наблюдение за изменениями в каталоге.
2. Выбор функции организовать в виде меню, пути до файлов и каталогов вводить с клавиатуры.
3. Ознакомится с основными параметрами данных функций, исследовать их работу при изменении основных параметров.
Контрольные вопросы
1. Какие функции используются для создания каталогов?
2. Какие функции используются для удаления, перемещения и копирования каталогов?
3. С помощью какой функции можно организовать поиск файла в каталоге?
4. Поясните задачу функции: FindFirstFile и ее параметры.
5. Поясните задачу функци: FindNextFile и ее параметры.
6. Поясните задачу функции: FindClose и ее параметры.
7. Какую задачу выполняет функция GetFileAttributes и ее параметры?
8. Какова структура параметра lhffd?
9. Какой параметр функции FindFirstFile указывает на каталог?
Лабораторная работа №11
Тема: Использование динамических библиотек для создания приложений
Цель работы:
1. Изучение основных функций Win32 API, используемых для создания динамических библиотек DLL.
2. Разработка приложения, демонстрирующего создание, подключение и использование динамических библиотек.
Краткое теоретическое введение
Наиболее прямой путь создания программы – собрать исходный код всех функций, скомпилировать его и скомпоновать все в единый исполняемый образ. Эта монолитная модель с одним исполняемым образом проста, но обладает рядом недостатков.
Динамические библиотеки могут быть использованы для создания разделяемых библиотек. Одной динамической библиотекой пользуются несколько программ, а в память загружается только одна ее копия. Все программы отображают адресные пространства своих процессов в код библиотеки, при этом каждый поток будет иметь собственную копию неразделяемой памяти в стеке.
DLL (англ. Dynamic Link Library – динамически загружаемая библиотека) – реализованные компанией Microsoft общие библиотеки в ОС Windows и OS / 2. Как правило библиотеки имеют расширение файла *. dll, *. ocx (для библиотек, содержащих элементы управления ActiveX) или *. drv (драйверы старых версий ОС). Структура DLL такая же, как и в PE-файлов (Portable Executable) для 32 -, 64-разрядных Windows, и New-Executable (NE) для 16-битных Windows. DLL может содержать код, данные и ресурсы любой комбинации.
Рисунок 1. Иллюстрация механизма связывания
В DLL главная функция называется DllMain и вызывается операционной системой при загрузке DLL в адресное пространство процесса и при создании этим процессом нового потока. Главное назначение функции DllMain заключается в инициализации DLL при ее загрузке, а также захвате и освобождении необходимых ресурсов при создании и завершении нового потока в процессе. Эта функция имеет следующий прототип:
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // дескриптор DLL
DWORD fdwReason, // флаг причины вызова функции DllMain
LPVOID lpvReserved // зарезервировано Windows
);
Неявное связывание
Неявное связывание, или связывание во время загрузки – более простой из двух способов. При использовании языка Microsoft Visual C+ + для этого необходим ряд шагов:
1. Функции для новой библиотеки собираются и компонуются как библиотека DLL, а не как, например, консольное приложение.
2. В процессе компоновки создается библиотечный файл .LIB, который является суррогатом для основного кода. Этот файл необходимо поместить в каталог вызывающей программы.
3. Процесс компоновки создает и файл .DLL, содержащий исполняемый образ. Обычно этот файл помещается в тот же каталог, что и приложение, которое будет его использовать, а приложение загружает .DLL во время инициализации.
Экспорт и импорт функций
Наиболее существенное изменение, необходимое для переноса функции в динамическую библиотеку - это ее определение как экспортируемой. Это достигается путем использования модификатора для функции, помещаемой в динамическую библиотеку как показано ниже:
_declspec (dllexport)
DWORD MyFunction ( . . . ) ;
_declspec – модификатор функции,
MyFunction – функция, помещаемая в библиотеку.
В этом случае процесс компоновки создаст файл библиотеки .DLL и файл .LIB. Файл .LIB – это суррогат библиотеки, который должен быть связан с вызывающей ее программой для разрешения внешних ссылок и создания действительных связей с файлом .DLL во время загрузки и подключаемый на стадии компоновки.
Вызывающая функцию программа должна определить импортируемую функцию путем использования модификатора
_declspec (dllimport)
DWORD MyFunction (...);
При компоновке вызывающей библиотеку программы, т.е. перед созданием .exe файла, необходимо в меню Project -->Setting на вкладке Link в окне Project_Options набрать путь и имя файла библиотеки MyFunction.lib. После этого необходимо убедиться в том, что файл библиотеки .DLL доступен ей. Обычно это обеспечивается помещением файла .DLL в тот же каталог, в котором находится исполняемый файл.
Явное связывание
Явное связывание, или связывание во время выполнения, несколько сложнее и требует от программы специального запроса для загрузки библиотеки (функция LoadLibrary) или ее выгрузки (функция FreeLibrary). Затем программа получает адрес нужной точки входа и использует его как указатель в вызове функции. В вызывающей программе функция не объявляется; вместо этого необходимо объявить переменную как указатель на функцию. Поэтому при компоновке программы нет потребности в файле библиотеки. Необходимы три функции: LoadLibrary, GetProcAddress и FreeLibrary.
HINSTANCE LoadLibrary (LPCTSTR lpLibFileName);
Возвращаемый дескриптор (типа HINSTANCE, а не HANDLE) не примет значения NULL в случае неудачи. Расширение .DLL в имени файла не требуется. С помощью функции LoadLibrary можно загрузить и файл типа .ЕХЕ. Так как динамические библиотеки разделяемые, система ведет учет ссылок на каждую DLL (их число увеличивается при вызове функции LoadLibrary), поэтому не требуется отображения фактически существующего файла. Даже если файл DLL найден, функция LoadLibrary выполнится неудачно в случае, если библиотека неявно связана с другой библиотекой, которая не может быть найдена.
Аналогичная функция LoadLibraryEx имеет несколько флагов, используемых для указания альтернативных путей поиска и загрузки библиотеки как файла данных.
После работы с данным экземпляром или для загрузки его другой версии необходимо освободить дескриптор библиотеки, освобождая тем самым ресурсы, включая и виртуальное адресное пространство, занятое библиотекой. Если счетчик ссылок указывает на то, что библиотека используется другим процессом, она остается загруженной в память:
BOOL FreeLibrary (HINSTANCE hLibModule);
После загрузки динамической библиотеки и до ее выгрузки можно получить адреса точек входа с использованием функции GetProcAddress.
FARPROC GetProcAddress ( HMODULE module, LPCSTR lpProcName);
Параметр hModule, несмотря на другой тип (тип HINSTANCE определен как HMODULE), является экземпляром, получаемым от функций LoadLibrary или GetModuleHandle, которые здесь не описаны. Параметр lpProcName, который не может быть строкой стандарта Unicode, является именем точки входа. В случае неудачи возвращаемое функцией значение будет NULL.
Используя функцию GetModuleFileName, можно получить имя файла, связанного с дескриптором hModule. И наоборот, по заданному имени файла (файл типа .ехе или .dll) функция GetModuleHandle возвращает дескриптор, связанный с файлом, если тот был загружен текущим процессом. Функция входа в библиотеку также в выполняется при создании или завершении процессом новых потоков.
Листинг 1.Пример приложения, в котором одна функция выводит сообщение, другая функция возвращает принятое значение используя DLL.
#include "stdafx.h"
#include <iostream>
using namespace std;
extern "C++" __declspec(dllexport) void LetterList()
{
cout << "This function was called from LetterList() " << endl;
}
extern "C++" __declspec(dllexport) int PutInt(int param)
{
return param;
}
Листинг 2.Пример динамической библиотеки.
// myFirstDll.dll
#include <windows.h>
int WINAPI DllMain(HINSTANCE hInstance,
DWORD fdReason,
PVOID pvReserved)
{
return TRUE;
}
extern "C" __declspec(dllexport) int MyFunction(char *str)
{
MessageBox(NULL, str,"Function from DLL",MB_OK);
return 1;
}