Пример 3B — работа с каталогами




 

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

В данном примере информирование приложения о смене содержимого каталога осуществляется с помощью функций FindFirstChangeNotification и FindNextChangeNotification, специфичных для Win32 API. Схожего эффекта можно было–бы добиться при использовании средств File Manager Extension (заголовочный файл fmext.h) в рамках Windows API. Схожего, но не такого же. Например, использование расширений диспетчера файлов в Windows API для получения информации об обновлении содержимого каталога ограничено только одним приложением в системе — два запущенных приложения уже не могут получать такие уведомления.

Функции FindFirstChangeNotification и FindNextChangeNotification используют специальный объект, который переводится в занятое состояние до ближайшего изменения каталога. Напрашивается простейший способ — вызвать функцию FindFirstChangeNotification и затем сразу функцию WaitForSingleObject, так что приложение остановится до тех пор, пока оно не получит уведомление об изменении каталога. Способ надежный, но остановленное приложение не сможет обновлять своего содержимого в окне (если его окно на время перекроют другие окна), вы даже не сможете завершить ваше приложение, так как оно ждет уведомления об обновлении каталога.

Из этой ситуации можно предложить два выхода — один, исключительно в стиле Win32 — создать отдельный поток, который будет ждать обновлений. Наверное, это самый эффективный способ, но о создание многопотоковых приложений будет обсуждено существенно позже. Другой путь — скрестить средства Windows API и Win32 API — вместо ожидания освобождения объекта, проверять его состояние по таймеру. В этом приеме практически не требуется дополнительного программирования, достаточно добавить инициализацию таймера и обработку сообщений WM_TIMER.

Собственно, именно таким путем и решена данная задача. Она построена аналогично примеру 2B — оглавление каталога, сформированное в виде текста, передается специальному окну–редактору, которое закрывает всю внутреннюю область главного окна приложения. В оконную процедуру главного окна приложения добавляется обработка сообщения WM_TIMER. Сам таймер инициализируется и освобождается в функции WinMain. Обработчик сообщений таймера — Cls_OnTimer — проверяет состояние синхронизирующего объекта (созданного при создании окна функцией FindFirstChangeNotification) и, если он освободился, принимает меры для обновления окна и снова переводит объект его в занятое состояние с помощью функции FindNextChangeNotification. Конечно, вместо таймера эффективнее было бы применить отдельный поток; однако приведенный вариант сравнительно легко адаптируется под 16ти разрядную платформу, в которой многопотоковые приложения не реализованы.

Функция, получающая оглавление каталога и формирующая отображаемый список вынесена в обработчик специального сообщения MY_REFRESH. Номер этому сообщению мы присваиваем сами, так, что бы он не конфликтовал со стандартными сообщениями окна. Для этого можно воспользоваться символом WM_USER, соответствующим самому младшему номеру сообщения, не являющего стандартным. Все номера сообщений равные или превышающие WM_USER стандартным окном[vi] не используются. Это же позволит продемонстрировать определение распаковщиков для собственного сообщения.

Файл 3B.CPP

#define STRICT

#include <windows.h>

#include <windowsx.h>

#define UNUSED_ARG(arg) (arg)=(arg)

// определяем собственное сообщение и его распаковщик

#define MY_REFRESH WM_USER+1

/* void Cls_OnMyRefresh(HWND hwnd); */

#define HANDLE_MY_REFRESH(hwnd,wParam,lParam,fn) ((fn)(hwnd),0L)

#define FORWARD_MY_REFRESH(hwnd,fn) (void)(fn)((hwnd),MY_REFRESH,0,0L)

// резервируем глобальные переменные

static char szWndClass[]= "File & folder operations";

static char *pszCurDir;

static HINSTANCE hInstance;

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

static void Cls_OnMyRefresh(HWND hwnd)

{int nSize;

char *pszList, szTemp[ 1024 ];

char szCreate[ 20 ], szRead[ 20 ], szWrite[ 20 ], szAttr[ 6 ];

WIN32_FIND_DATA fd;

FILETIME ftLocal;

SYSTEMTIME stTime;

HANDLE hFD;

static char szFileMask[] = "*.*";

static char szTimeFmt[] = "%02u-%02u-%04u %02u:%02u:%02u";

hFD = FindFirstFile(szFileMask, &fd);

if (hFD) {

// так как список файлов может быть длинным, то определяем его дину

nSize = lstrlen(pszCurDir) + 82; // длина заголовка списка

do {

nSize += lstrlen(fd.cFileName) + 76; // длина записи о каждом файле

} while (FindNextFile(hFD, &fd));

FindClose(hFD);

// резервируем необходимое пространство, включая последний ‘\0’ символ

pszList = new char [ nSize + 1 ];

if (pszList) {

// готовимся к получению списка файлов

hFD = FindFirstFile(szFileMask, &fd);

if (hFD) {

// формируем заголовок

pszList[0] = '[';

lstrcpy(pszList + 1, pszCurDir);

nSize = lstrlen(pszCurDir) + 1;

lstrcpy(

pszList + nSize,

"]\r\n creation | last write"

" | last access | attrs | name");

nSize += 81;

do {

// перебираем все файлы по одному

// учитываем переход от Гринвича к локальному времени

FileTimeToLocalFileTime(&(fd.ftCreationTime), &ftLocal);

// преобразуем к приемлемому для чтения формату

FileTimeToSystemTime(&ftLocal, &stTime);

// записываем дату и время

wsprintf(

szCreate, szTimeFmt, stTime.wDay, stTime.wMonth,

stTime.wYear, stTime.wHour, stTime.wMinute, stTime.wSecond);

// аналогично записываем время изменения

FileTimeToLocalFileTime(&(fd.ftLastWriteTime), &ftLocal);

FileTimeToSystemTime(&ftLocal, &stTime);

wsprintf(

szWrite, szTimeFmt, stTime.wDay, stTime.wMonth,

stTime.wYear, stTime.wHour, stTime.wMinute, stTime.wSecond);

// и время последнего доступа

FileTimeToLocalFileTime(&(fd.ftLastAccessTime), &ftLocal);

FileTimeToSystemTime(&ftLocal, &stTime);

wsprintf(

szRead, szTimeFmt, stTime.wDay, stTime.wMonth,

stTime.wYear, stTime.wHour, stTime.wMinute, stTime.wSecond);

// расшифровываем атрибуты файла

szAttr[0] = fd.dwFileAttributes&FILE_ATTRIBUTE_ARCHIVE? 'A':'-';

szAttr[1] = fd.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN? 'H':'-';

szAttr[2] = fd.dwFileAttributes&FILE_ATTRIBUTE_SYSTEM? 'S':'-';

szAttr[3] = fd.dwFileAttributes&FILE_ATTRIBUTE_READONLY? 'R':'-';

szAttr[4] = fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY?'D':'-';

szAttr[5] = '\0';

// и формируем строку для вывода и добавляем ее в список

wsprintf(

pszList + nSize,

"\r\n%19.19s | %19.19s | %19.19s | %5.5s | ",

szCreate, szWrite, szRead, szAttr);

nSize += 76;

lstrcpy(pszList + nSize, fd.cFileName);

nSize += lstrlen(fd.cFileName);

} while (FindNextFile(hFD, &fd));

FindClose(hFD);

SetWindowText((HWND)GetWindowLong(hwnd, 0), pszList);

// после передачи текста окну просмотра

// выделенный для него буфер больше не нужен.

delete pszList;

return;

// дальше мы можем оказаться только в случае ошибки}

delete pszList;}}

SetWindowText((HWND)GetWindowLong(hwnd, 0), "*** Found errors! ***");}

// а это обработчик сообщения WM_CREATE. Создаем дочернее окно просмотра и // синхронизирующий объект, для получения уведомлений об изменении каталога

static BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)

{UNUSED_ARG(lpCreateStruct);

HWND hwndView;

RECT rc;

HANDLE hWait;

// создаем окно просмотра

GetClientRect(hwnd, &rc);

hwndView = CreateWindow(

"EDIT", "",

WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|ES_MULTILINE|ES_READONLY,

0, 0, rc.right, rc.bottom, hwnd, (HMENU)1, hInstance, NULL);

if (!IsWindow(hwndView)) {

MessageBox(NULL, "Cannot create viewer window!", NULL, MB_OK);

return FALSE;}

SetWindowFont(hwndView, GetStockObject(ANSI_FIXED_FONT), FALSE);

SetWindowLong(hwnd, 0, (LONG)hwndView);

// инициализируем синхронизирующий объект

hWait = FindFirstChangeNotification(

pszCurDir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);

SetWindowLong(hwnd, 4, (LONG)hWait);

// первоначально заполняем окно просмотра

FORWARD_MY_REFRESH(hwnd, PostMessage);

return TRUE;}

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

static void Cls_OnSize(HWND hwnd, UINT state, int cx, int cy)

{UNUSED_ARG(state);

HWND hwndView = (HWND)GetWindowLong(hwnd, 0);

if (IsWindow(hwndView)) MoveWindow(hwndView, 0,0, cx, cy, TRUE);}

// когда главное окно закрывается уничтожаем окно просмотра и синхронизирующий объект

static void Cls_OnDestroy(HWND hwnd)

{HWND hwndView = (HWND)GetWindowLong(hwnd, 0);

if (IsWindow(hwndView)) DestroyWindow(hwndView);

HANDLE hWait = (HANDLE)GetWindowLong(hwnd, 4);

if (hWait) FindCloseChangeNotification(hWait);

PostQuitMessage(0);}

// при получении главным окном фокуса ввода, передаем его окну просмотра

static void Cls_OnSetFocus(HWND hwnd, HWND hwndOldFocus)

{UNUSED_ARG(hwndOldFocus);

HWND hwndView = (HWND)GetWindowLong(hwnd, 0);

if (IsWindow(hwndView)) SetFocus(hwndView);}

// по таймеру (у нас - каждые 2 секунды) проверяем состояние синхронизирующего объекта

void Cls_OnTimer(HWND hwnd, UINT id)

{UNUSED_ARG(id);

HANDLE hWait;

hWait = (HANDLE)GetWindowLong(hwnd, 4);

if (hWait) {

if (WaitForSingleObject(hWait, 0) == WAIT_OBJECT_0) {

// объект освобожден - каталог изменился

MessageBeep(MB_OK); // звуковой сигнал для привлечения внимания

FORWARD_MY_REFRESH(hwnd, PostMessage); // снова загрузить список

FindNextChangeNotification(hWait); // и ждать дальше...}}}

LONG WINAPI _export WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{switch (uMsg) {

HANDLE_MSG(hWnd, WM_CREATE, Cls_OnCreate);

HANDLE_MSG(hWnd, WM_DESTROY, Cls_OnDestroy);

HANDLE_MSG(hWnd, WM_SIZE, Cls_OnSize);

HANDLE_MSG(hWnd, WM_SETFOCUS, Cls_OnSetFocus);

HANDLE_MSG(hWnd, MY_REFRESH, Cls_OnMyRefresh);

HANDLE_MSG(hWnd, WM_TIMER, Cls_OnTimer);

default: break;}

return DefWindowProc(hWnd, uMsg, wParam, lParam);}

static BOOL init_instance(HINSTANCE hInst)

{WNDCLASS wc;

hInstance = hInst;

wc.style = 0;

wc.lpfnWndProc = WinProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 8;

wc.hInstance = hInst;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wc.lpszMenuName = NULL;

wc.lpszClassName = szWndClass;

return RegisterClass(&wc) == NULL? FALSE: TRUE;}

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)

{UNUSED_ARG(lpszCmdLine);

MSG msg;

HWND hWnd;

int nSize;

nSize = GetCurrentDirectory(0, NULL);

pszCurDir = new char [ nSize ];

GetCurrentDirectory(nSize, pszCurDir);

if (!hPrevInst) {

if (!init_instance(hInst)) return 1;}

hWnd= CreateWindow(

szWndClass, // class name

szWndClass, // window name

WS_OVERLAPPEDWINDOW, // window style

CW_USEDEFAULT,CW_USEDEFAULT, // window position

CW_USEDEFAULT,CW_USEDEFAULT, // window size

NULL, // parent window

NULL, // menu

hInst, // current instance

NULL // user-defined parameters);

if (!hWnd) return 1;

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

// инициализируем таймер так, что бы сообщения передавались каждые 2 секунды

SetTimer(hWnd, 0, 2000, (TIMERPROC)NULL);

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

TranslateMessage(&msg);

DispatchMessage(&msg);}

if (pszCurDir) delete pszCurDir;

return msg.wParam;}

 

Чтение и запись файлов

 

HANDLE CreateFile(lpszName, fdwAccess, fdwShareMode, lpszSA, fdwCreate, fdwAttrsAndFlags, hTemplateFile);

INVALID_HANDLE_VALUE = (HANDLE)-1!!!!!!!!!!

BOOL CloseHandle(hFile);

BOOL ReadFile(hFile, lpvBuffer, nBytes, lpdwReaded, lpOverlapped);

BOOL WriteFile(hFile, lpvBuffer, nBytes, lpdwWritten, lpOverlapped);

DWORD SetFilePointer(hFile, lDistanceLow, lDistanceHigh, dwMethod);

BOOL SetEndOfFile(hFile);

BOOL FlushFileBuffers(hFile);

BOOL LockFile(hFile, dwOffsetLow, dwOffsetHigh, cbLockLow, cbLockHigh);

BOOL UnlockFile(hFile, dwOffsetLow, dwOffsetHigh, cbLockLow, cbLockHigh);

BOOL LockFileEx(hFile, dwFlags, dwUnused, cbLockLow, cbLockHigh, lpsOverlapped); // not in 95!

BOOL UnlockFileEx(hFile, dwFlags, dwUnused, cbLockLow, cbLockHigh, lpOverlapped); // not in 95!

BOOL GetOverlappedResult(hFile, lpOverlapped, lpcbTransfer, fWait);

DWORD WaitForSingleObject(hObject, dwTimeOut);

BOOL ReadFileEx(hFile, lpvBuffer, nBytes, lpOverlapped, lpfnCompletionFunc);

BOOL WriteFileEx(hFile, lpvBuffer, nBytes, lpOverlapped, lpfnCompletionFunc);


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

[ii] Подробнее о работе с окнами смотри в соответствующих разделах.

[iii] Реально тестирование проводилось еще и на третьем компьютере с системой Windows–98, однако ее поведение ничем не отличалось от Windows–95, за исключением уже отмеченного нюанса в реализации функций DefineDosDevice и QueryDosDevice.

[iv] Такое поведение типично для MSCDEX.EXE, который обеспечивает доступ к дискам с CDFS в среде MS–DOS; при отсутствии диска MSCDEX часто использует данные, оставшиеся в кэше от предыдущего диска, может быть даже от того, который был раньше вставлен, либо вообще от другого. Это, кстати, может приводить к ошибке — при попытке проверить наличие какого–либо конкретного файла на томе с CDFS вы можете получить положительный ответ, даже если том вообще отсутствует в приводе! При проверке наличия файла на CD–ROM лучше не просто проверять его наличие, а открывать и выполнять чтение небольшого фрагмента, что бы убедиться в действительном присутствии файла.

[v] Аналогичная группа функций существует и для получения информации о принтерах (FindFirstPrinterChangeNotification, FindNextPrinterChangeNotification и FindClosePrinterChangeNotification), только она реализована исключительно для Windows NT. Согласно описанию для Windows–95 или Windows 3.x с Win32s эти функции не доступны.

[vi] То есть окном, чья процедура построена на функции DefWindowProc. Другие окна, как, скажем, редакторы, кнопки, списки, диалоги и прочее используют сообщения с номерами, большими WM_USER. Однако такие сообщения они только получают сами, но главное окно приложения их не получит.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-06-03 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: