Системное программирование в среде Win32

Автор работы: Пользователь скрыл имя, 25 Марта 2014 в 14:49, лекция

Краткое описание

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

Прикрепленные файлы: 1 файл

Win32_лек_часть1.doc

— 811.00 Кб (Скачать документ)

Параметр fdwAttrsAndFlags определяет атрибуты и флаги файла. Существует 16 флагов и атрибутов. Атрибуты – это характеристики файла, а не открытого дескриптора HANDLE; они игнорируются, когда открывается существующий файл. Атрибуты файла используют младшие биты двойного слова, а флаги – старшие. Примеры атрибутов и флагов:

FILE_ATTRIBUTE_NORMAL- (значение 0х00000080) – может использоваться, только если не установлены никакие другие атрибуты (однако флаги могут быть установлены)

FILE_ATTRIBUTE_READONLY - (значение 0х00000001)– приложения не могут ни писать в файл, ни удалять из него (только для чтения).

FILE_FLAG_DELETE_ON_CLOSE - (значение 0х04000000)– полезен для  временных файлов. Файл удаляется, когда закрывается его последний открытый дескриптор.

FILE_FLAG_RANDOM_ACCESS - (значение 0х10000000)– файл предназначен для произвольного доступа, и Windows будет пытаться оптимизировать кэширование.

FILE_FLAG_SEQUENTIAL_SCAN - (значение 0х08000000)- файл предназначен  для последовательного доступа, и Windows, соответственно оптимизирует кэширование.

FILE_FLAG_WRITE_THROUGH - (значение 0х80000000) - промежуточные кэши немедленно записываются в файл на диске.

Параметр hTemplateFile – дескриптор файла, открытого с GENERIC_READ, определяющий расширенные атрибуты для вновь создаваемого файла, причем значения fdwAttrsAndFlags игнорируются. Обычно этот параметр равен NULL. Параметр fdwAttrsAndFlags игнорируется, когда открывается существующий файл. С помощью этого параметра можно сделать так, чтобы атрибуты нового файла были такими же, как у существующего.

Обращаясь к функции CreateFile, следует всегда проверять возвращаемое значение. Если создать дескриптор не удалось, вызов вернет значение INVALID_HANDLE_VALUE. В этом случае необходимо воспользоваться вызовом функции GetLastError для того, чтобы узнать причину неудачи.

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

Закрытие файлов

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

 

BOOL   CloseHandle (HANDLE hObject).

Эта функция возвращает значение TRUE, если функция выполнилась успешно, иначе – FALSE.

При завершении процесса (работы приложения) система сама закрывает все открытые дескрипторы, но рекомендуется, чтобы программы сами закрывали свои дескрипторы перед завершением.

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

Чтение файлов

Для чтения файла используется функция:

BOOL  ReadFile (HANDLE hFile,

LPVOID lpBuffer,

DWORD nNumberOfBytesToRead,

LPDWORD lpNumberOfBytesRead,

LPOVERLAPPED lpOverlapped)

 

Эта функция возвращает значение TRUE, если чтение завершается успешно (даже если не был прочитан ни один байт  из-за попытки чтения после конца файла).

Параметры функции:

hFile – дескриптор  файла с доступом GENERIC_READ;

lpBuffer указывает  на буфер памяти, в который  будут считываться данные из  файла;

nNumberOfBytesToRead – количество байтов, которое следует прочитать из файла;

lpNumberOfBytesRead – указатель  на фактическое  число байтов, прочитанное функцией ReadFile;  это значение может быть  равным нулю, если дескриптор  установлен на конец файла;

lpOverlapped указывает  на структуру OVELAPPED (перекрытия), содержащую начало области байтов.

  Запись в файлы

Запись в файлы осуществляется функцией

 BOOL  WriteFile (HANDLE hFile,

CONST  VOID *lpBuffer,

DWORD nNumberOfBytesToWrite,

LPDWORD lpNumberOfBytesWrite,

LPOVERLAPPED lpOverlapped)

 

Эта функция возвращает значение TRUE, если функция завершается успешно, иначе FALSE.  Успешное завершение функции не гарантирует, что данные фактически будут записаны на диск, если только в CreateFile не задан флаг FILE_FLAG_WRITE_THROUGH. Если дескриптор установлен в конец файла, Win32 расширяет существующий файл.

Параметры этой функции аналогичны параметрам функции чтения файла.

 

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

Пример 3.1. Программа копирования файлов с использование Win32

 

// Копирование файлов с  использованием Win32

 

#include <windows.h>

#include <stdio.h>

 

#define BUF_SIZE 256

 

int main(int argc, LPTSTR argv[])  // argc –  число аргументов при вызове

//argv содержит  имя исполняемого файла 

//программы (в  данном случае cpw) и 

//имена входного  и выходного файлов

{

HANDLE hIn, hOut;

DWORD nIn, nOut;

CHAR Buffer [BUF_SIZE];

 

if (argc != 3) { printf("Использование : cpw  file1  file2 \n  ");

return 1;

}

 

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

 

if (hIn== INVALID_HANDLE_VALUE) {

printf ("11  Нельзя открыть входной файл. Ошибка: %x\n", GetLastError());

return 2;

}

hOut= CreateFile (argv[2], GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL ,NULL);

if (hOut== INVALID_HANDLE_VALUE) {

printf ("22  Нельзя открыть выходной файл. Ошибка: %x\n", GetLastError());

return 3;

}

 

while (ReadFile (hIn, Buffer, BUF_SIZE, &nIn, NULL) && nIn >0) 

{

  WriteFile (hOut, Buffer, nIn, &nOut, NULL);

  if (nIn != nOut) {

     printf ("Неисправивая ошибка записи: %x\n", GetLastError());

     return 4;

      }

}

CloseHandle(hIn);

CloseHandle(hOut);

return 0;

}

 

Рассмотрим детально код этой программы.  Первые две строки

 

#include <windows.h>

#include <stdio.h>

 

сообщают компилятору о необходимости подключить заголовочные файлы <windows.h> и <stdio.h>.  Файл <windows.h> содержит все определения функций и типов данных Win32. Файл  <stdio.h> содержит функции библиотеки стандартного ввода-вывода языка С.

Следующая строка

#define BUF_SIZE 256

макрос, устанавливающий размер буфера

Функция main имеет  два параметра: argc – число передаваемых параметров, которые могут быть приняты за имя и аргументы программы, а параметр argv[ ] – указатель на строковый массив; элементы массива будут содержать передаваемые параметры. Первая из этих строк образует имя программы в системе (в нашем примере программа имеет имя cpw), следующие параметры (строки) содержат имена файлов (входного и выходного). Сама функция main возвращает  результат целого типа, который может принимать значения 0, 1, 2, 3 или 4 в зависимости от ситуации в ходе выполнения программы:

0 – копирование  прошло успешно;

1 – некорректный  вызов программы (количество аргументов должно быть равно 3: имя программы и два имени файлов);

2 – невозможно  открыть входной файл;

3 – невозможно  создать выходной файл;

4 – ошибка  записи: фактическое число байтов, прочитанное функцией ReadFile, не совпадает с фактическим числом байтов, записанных функцией WriteFile

 

Далее следует описание переменных

HANDLE hIn, hOut;

nIn, nOut;

CHAR Buffer [BUF_SIZE];

 

Переменные hIn, hOut (тип HANDLE ) – дескрипторы входного и выходного файлов соответственно.

Переменные nIn, nOut типа DWORD (32-разрядное беззнаковое целое) содержат фактическое число байтов, прочитанное функцией ReadFile, и фактическое число байтов, записанных функцией WriteFile.

Символьный массив Buffer - буфер памяти, в который будут считываться данные из входного файла и из которого эти данные будут записываться в выходной файл. Размер буфера задается с помощью макроса (см. выше) и равен 256 символов.

После проверки правильности вызова программы (if (argc != 3) количество аргументов при вызове должно быть равно 3. вызывается функция CreateFile. В данном случае эта функция, вызывается для открытия существующего входного файла. Далее проверяется, успешно ли выполнилась операция открытия файла (if (hIn== INVALID_HANDLE_VALUE)). В случае неудачи код ошибки возвращается с помощью функции GetLastError(). Эта функция позволяет в любой момент получить системные коды ошибок  в форме DWORD.

Далее вновь вызывается функция CreateFile, но уже для открытия выходного файла.

В цикле (до конца файла) происходит считывание из исходного файла (с помощью функции ReadFile) и запись в выходной файл (функция WriteFile). Функция ReadFile возвращает логическое значение, а не количество байтов (которое передается в параметре  &nIn) . Поэтому конец файла (условие окончания цикла) обнаруживается по нулевому количеству байтов (nIn>0)  и не приводит к неудаче операции чтения.

На каждом шаге цикла проверяется правильность записи в выходной файл  if (nIn != nOut) – количество считанных байт равно количеству записанных.

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

 

3.4. Стандартные устройства и консольный  ввод-вывод

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

HANDLE GetStdHandle (DWORD  nStdHandle)

Если функция завершается успешно, то она возвращает допустимый дескриптор, иначе – INVALID_HANDLE_VALUE. Параметр этой функции  - nStdHandle – должен иметь одно из следующих значений:

STD_INPUT_HANDLE

STD_OUTPUT_HANDLE

STD_ERROR_HANDLE

Стандартные устройства обычно назначены консоли и клавиатуре. Стандартный ввод-вывод можно перенаправлять.

Функция GetStdHandle не создает новый дескриптор стандартного устройства и не дублирует прежний. Последовательные вызовы с одним и тем же параметром возвращают одно и то же значение дескриптора. Закрытие дескриптора стандартного устройства делает это устройство недоступным для последующего использования. Поэтому примеры программ часто открывают стандартное устройство, работают с ним, но не закрывают.

 

Функция для открытия стандартного устройства:

BOOL SetStdHandle (DWORD  nStdHandle, HANDLE hHandle)

Функция возвращает значение TRUE или FALSE, в зависимости от успеха или неудачи.

Параметры этой функции:

nStdHandle имеет те же значения, что и в функции GetStdHandle;

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

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

Два имени файлов зарезервированы для консольного ввода (с клавиатуры) и консольного вывода: CONIN$ и CONOUT$. Первоначально стандартный ввод, вывод и выдача ошибок назначены на консоль. Консоль можно использовать независимо от любого перенаправления этих стандартных устройств; для этого нужно просто открыть дескрипторы CONIN$ или CONOUT$ с помощью CreateFile.

Для консольного ввода-вывода можно применять ReadFile и WriteFile, но лучше использовать специальные функции ReadConsole и WriteConsole. Основные их преимущества состоят в том, что эти функции обрабатывают универсальные символы (TCHAR), а не байты, а также учитывают режим консоли, установленный функцией SetConsoleMode.

 

BOOL SetConsoleMode (HANDLE hConsole, DWORD fdevMode)

Возвращает значение TRUE, если функция завершилась успешно. Параметры функции:

hConsole идентифицирует буфер ввода или экрана, который должен иметь атрибут доступа GENERIC_WRITE, даже если соответствующее устройство допускает только ввод.

fdevMode определяет режим обработки символов. Значение каждого флага указывает, применяется ли этот флаг к консольному вводу или выводу. При создании буфера установлены все флаги, кроме ENABLE_WINDOW_INPUT.

ENABLE_LINE_INPUT – функция чтения (ReadConsole) завершается, когда встречается символ возврата каретки.

ENABLE_ECHO_INPUT – читаемые символы дублируются на экране.

ENABLE_PROCESSED_INPUT – система обрабатывает символы возврата (backspace), возврата каретки и перевода строки.

ENABLE_ PROCESSED_OUTPUT – система обрабатывает символы возврата (backspace), табуляции, звукового сигнала, возврата каретки и перевода строки.

ENABLE_WRAP_AT_EOL_OUTPUT – разрешается перенос строк как при обычном, так и при дублированном выводе.

 

Функции ReadConsole  и WriteConsole подобны ReadFile и WriteFile.

BOOL  ReadConsole (HANDLE hConsoleInput,

LPVOID lpvBuffer, DWORD cchToRead,

LPDWORD lpcchRead, LPVOID lpvReserved)

Возвращает значение TRUE, если функция завершается успешно. Параметры  этой функции почти такие же, как у функции ReadFile. Два параметра выражаются в универсальных символах, а не в байтах, параметр lpvReserved должен иметь значение NULL.

 

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

BOOL  AllocConsole (VOID)

Для отделения процесса от его консоли предназначена функция

BOOL FreeConsole (VOID)

 

Функция AllocConsole завершается неудачно, если процесс уже имеет консоль. Чтобы избежать этой проблемы, перед AllocConsole помещается вызов функции FreeConsole.

Пример 3.2. Вывод на консоль и приглашение

Рассмотрим пример функции ConsolePrompt, которая выдает пользователю указанное приглашение и затем возвращает его ответ.

 

//вывод на консоль и ввод  с консоли

#include "stdafx.h"

#include <windows.h>

 

int main(int argc, char* argv[])

{

LPCTSTR pMsg="Hello!\n Input text: "; 

HANDLE hOut, hIn;

DWORD MsgLen, Count, TcharIn, MaxTchar=10 ;

LPTSTR pRes;

 

hOut = CreateFile  ( TEXT("CONOUT$"),

GENERIC_READ|GENERIC_WRITE, 0,

NULL, OPEN_ALWAYS,

FILE_ATTRIBUTE_NORMAL, NULL);

if (hOut == INVALID_HANDLE_VALUE)  {   return 0; }

 

//вывод на экран сообщения pMsg с использованием

//функции WriteConsole

MsgLen = lstrlen (pMsg);

WriteConsole (hOut, pMsg, MsgLen, &Count, NULL); 

 

//Чтение с консоли

hIn=CreateFile (TEXT("CONIN$"), GENERIC_READ | GENERIC_WRITE,

Информация о работе Системное программирование в среде Win32