Пример: использование обработчиков завершения для повышения качества программ

Пример: использование обработчиков завершения для повышения качества программ

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

В программе toupper (программа 4.2) эти моменты иллюстрируются с привлечением идей, почерпнутых в программном коде предшествующих примеров. toupper обрабатывает несколько файлов, имена которых указываются в командной строке, переписывая их с преобразованием всех букв в верхний регистр. Имена преобразованных файлов получаются путем добавления префикса UC_ к исходным именам, и согласно "спецификации" программы запись поверх существующих файлов не производится. Преобразование файлов осуществляется в памяти машины, поэтому для каждого файла выделяется большая буферная область (достаточная для размещения всего файла). Кроме того, чтобы исключить любую возможность изменения файлов другими процессами, а также для того, чтобы вновь создаваемые выходные файлы строго соответствовали преобразованным входным файлам, оба вида файлов блокируются во время обработки. Понятно, что на каждой стадии обработки существует вероятность возникновения самых различных сбойных ситуаций, но в программе должна быть предусмотрена защита от подобных ошибок, и она должна располагать средствами, позволяющими ей восстановить свое нормальное состояние и попытаться обработать все остальные файлы, имена которых были указаны в командной строке. Программа 4.2 решает все эти задачи, обеспечивая разблокирование файлов во всех необходимых случаях без применения громоздкой логики операторов ветвления, к которым пришлось бы прибегнуть, если бы не были использованы средства SEH. Более подробные комментарии к программе содержатся в программном коде, находящемся на Web-сайте книги.

Программа 4.2. toupper: обработка файлов с восстановлением нормального состояния программы после сбоев

/* Глава 4. Команда toupper. */

/* Преобразование содержимое одного и более файлов с заменой всех букв на прописные. Имя выходного файла получается из имени входного файла добавлением к нему префикса UC_. */

#include "EvryThng.h"

int _tmain(DWORD argc, LPTSTR argv[]) {

 HANDLE hIn = INVALID_HANDLE_VALUE, hOut = INVALID_HANDLE_VALUE;

 DWORD FileSize, nXfer, iFile, j;

 CHAR OutFileName [256] = "", *pBuffer = NULL;

 OVERLAPPED ov = {0, 0, 0, 0, NULL}; /* Используется для блокирования файлов. */

 if (argc <= 1) ReportError(_T("Использование: toupper файлы"), 1, FALSE);

 /* Обработать все файлы, указанные в командной строке. */

 for (iFile = 1; iFile < argc; iFile++) __try { /* Блок исключений. */

  /* Все дескрипторы файлов недействительны, pBuffer == NULL, а файл OutFileName пуст. Выполнение этих условий обеспечивается обработчиками. */

  _stprintf(OutFileName, "UC_%s", argv[iFile]);

  __try { /* Внутренний блок try-finally. */

   /* Ошибка на любом шаге сгенерирует исключение, и следующий */

   /* файл будет обрабатываться только после "уборки мусора". */

   /* Объем работы по очистке зависит от того, в каком месте */

   /* программы возникла ошибка. */

   /* Создать выходной файл (завершается с ошибкой, если файл уже существует). */

   hIn = CreateFile(argv[iFile], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

   if (hIn == INVALID_HANDLE_VALUE) ReportException(argv[iFile], 1);

   FileSize = GetFileSize(hIn, NULL);

   hOut = CreateFile(OutFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);

   if (hOut == INVALID_HANDLE_VALUE) ReportException(OutFileName, 1);

   /* Распределить память под содержимое файла. */

   pBuffer = malloc(FileSize);

   if (pBuffer == NULL) ReportException(_T("Ошибка при распределении памяти"), 1);

   /* Блокировать оба файла для обеспечения целостности копии. */

   if (!LockFileEx(hIn, LOCKFILE_FAIL_IMMEDIATELY, 0, FileSize, 0, &ov) ReportException(_T("Ошибка при блокировании входного файла"), 1);

   if (!LockFileEx(hOut, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, FileSize, 0, &ov) ReportException(_T("Ошибка при блокировании выходного файла "), 1);

   /* Считать данные, преобразовать их и записать в выходной файл. */

   /* Освободить ресурсы при завершении обработки или возникновении */

   /* ошибки; обработать следующий файл. */

   if (!ReadFile(hIn, pBuffer, FileSize, &nXfer, NULL)) ReportException(_T("Ошибка при чтении файла"), 1);

   for (j = 0; j < FileSize; j++) /* Преобразовать данные. */

    if (isalpha(pBuffer [j])) pBuffer[j] = toupper(pBuffer [j]);

   if(WriteFile(hOut, pBuffer, FileSize, &nXfer, NULL)) ReportException(T("Ошибка при записи в файл"), 1);

  } __finally {

   /*Освобождение блокировок, закрытие дескрипторов файлов,*/

   /*освобождение памяти и повторная инициализация */

   /*дескрипторов и указателя. */

   if (pBuffer != NULL) free (pBuffer);

   pBuffer = NULL;

   if (hIn != INVALID_HANDLE_VALUE) {

    UnlockFileEx(hIn, 0, FileSize, 0, &ov);

    CloseHandle(hIn);

    hIn = INVALID_HANDLE_VALUE;

   }

   if (hOut != INVALID_HANDLE_VALUE) {

    UnlockFileEx(hOut, 0, FileSize, 0, &ov);

    CloseHandle(hOut);

    hOut = INVALID_HANDLE_VALUE;

   }

   _tcscpy(OutFileName, _T(""));

  }

 }

 /* Конец основного цикла обработки файлов и блока try. */

 /* Обработчик исключений для тела цикла. */

 __except(EXCEPTION_EXECUTE_HANDLER) {

  _tprintf(_T("Ошибка при обработке файла %s "), argv[iFile]);

  DeleteFile (OutFileName);

 }

 _tprintf(_T("Обработаны все файлы, кроме указанных выше "));

 return 0;

}