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

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

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

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

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

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

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

0,  NULL, OPEN_ALWAYS, 

FILE_ATTRIBUTE_NORMAL, NULL);

 

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

 

SetConsoleMode (hIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);

 

pRes = new char[80];

ReadConsole (hIn, pRes, MaxTchar, &TcharIn, NULL); 

WriteConsole (hOut, pRes, MaxTchar, &Count, NULL); 

 

delete [] pRes;

 

CloseHandle (hIn);

CloseHandle (hOut);

 

Sleep(1000);

return 0;

}

 

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

BOOL CharToOem(

LPCTSTR lpszSrc, //указатель на исходную строку 

  LPSTR lpszDst  // указатель на строку после перекодировки

);

Например, в примере 4 русский текст в строке pMsg: можно получить следующим образом:

LPCTSTR  pMsg1="Привет  всем!!!\n Введите  текст: "; 

LPSTR pMsg =new char[80];

CharToOem(pMsg1,pMsg); //перекодирует русские буквы

 

Пример 3.3. Копирование нескольких файлов на стандартное устройство вывода

#include <windows.h>

#include <conio.h>

 

void MB(char *s)

// эта функция используется  для удобства работы с MessageBox

{MessageBox(NULL,s,NULL,MB_OK|MB_ICONSTOP); }

 

// Эта функция выполняет  основную работу по выводу  на экран файла

void docat(char *fname)

{HANDLE f=CreateFile(fname, GENERIC_READ, 0, NULL,

OPEN_EXISTING, 0, NULL);

HANDLE out=GetStdHandle(STD_OUTPUT_HANDLE);

    if (f==INVALID_HANDLE_VALUE)

{MB("Невозможно  открыть файл!"); exit(1); }

 

char buf[4096];

unsigned long n;

     do

{unsigned long wct;

  if (!ReadFile(f,buf,sizeof(buf),&n,NULL)) break;

  if (n) WriteFile(out,buf,n,&wct,NULL);

}

while (n==sizeof(buf));  // при достижении  конца файла (EOF) это 

//условие  не выполняется

CloseHandle(f);

}

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

{    if (argc==1)

     { MB("запуск программы: ntcat имя_файла1 [имя_файла2 ....]");

exit(9);  }

// обрабатывать все файлы

while (--argc) { docat(*++argv); getche();}

exit(0);

}

3.5. Использование функции CreateFile для  работы с COM-портом

Особым случаем работы с файлом является работа с коммуникационным  портом. Для того чтобы  можно было работать с портом, в основном используются те же самые функции CreateFile(), ReadFile() WriteFile(), которые используются и при работе с обычными файлами. Тем не менее, в связи с тем, что порт является не просто файлом, у него есть свои особенности.

Итак, что нужно сделать для того, чтобы получить доступ к последовательному порту? Прежде всего, при помощи функции CreateFile() необходимо этот порт открыть. Какое имя файла необходимо указывать при открытии порта? Для последовательных портов зарезервированы имена «СОМ1», «COM2», COM3» и так далее.

Далее следует определить режим разделения доступа к файлу dwShareMode. Последовательный порт - это неразделяемый ресурс, поэтому при его открытии нам необходимо указывать режим эксклюзивного доступа к файлу, то есть 0.

Следующий вопрос - а как быть с режимом открытия файла dwCreationDisposition? Естественно, что программным способом мы не сможем ни создать, ни удалить коммуникационный порт, мы можем использовать только существующие порты. Следовательно, при открытии порта в качестве режима открытия файла мы должны указать OPEN_EXISTING, и никакой другой.

Теперь определим, какие атрибуты файла (порта) должны быть указаны при его открытии? Если мы хотим, чтобы порт работал синхронно с нашей программой, мы должны параметр dwFlagsAndAttributes сделать равным нулю. Если же мы укажем в качестве этого параметра значение FILE_FLAG_OVERLAPPED, это будет означать, что порт будет работать асинхронно.

И, наконец, параметр hTemplateFile при работе с последовательным портом всегда должен быть равен нулю.

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

INVALID_HANDLE_VALUE.

 

И, наконец, раз мы порт открываем, то, естественно, перед окончанием работы с портом он должен быть закрыт. Закрытие порта осуществляется обычным образом, при помощи функции CloseHandle().

Пример открытия и закрытия порта:

HANDLE  hComPort;

hComPort = CreateFile  ("COM1",

GENERIC_READ|GENERIC_WRITE, 0,

0NULL, OPEN_EXISTING, 0, NULL);

 

if(hComPort==INVALID_HANDLE_ VALUE) {

// Какие-то  действия, обычно оповещение пользователя 

// о невозможности открыть порт. }

else {   CloseHandle( hComPort);}

 

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

BOOL SetCommState (HANDLE hFile, LPDCB IpDCB);

Первым аргументом этой функции является хэндл открытого порта. Второй аргумент - это указатель на структуру типа DCB. Эта структура содержит поля, определяющие скорость работы порта, порядок контроля четности, действия при ошибке, порядок использования сигналов RTS, DSR и многое другое. (Подробнее см. [6 Румянцев Файлы] ).

Заполнить структуру DCB можно при помощи функции:

BOOL GetCommState(HANDLE hFile, LPDCB IpDCB);

 

Эта функция считывает данные из внутренних регистров порта и управляющих структур драйвера. Первым аргументом этой функции является хэндл файла (в данном случае - порта), а вторым аргументом является указатель на структуру DCB, в которую будет записана необходимая информация о порте. В случае нормального завершения функция возвращает TRUE. Значение FALSE свидетельствует о том, что при считывании информации о параметрах порта произошла какая-то ошибка.

Вполне вероятно, что программе нужно будет изменить всего один или два параметра в этой структуре, например, скорость передачи. В этом случае вызов функции GetCommState() может намного сократить текст программы и заодно уменьшить количество возможных ошибок.

Итак, заполнив DCB и вызвав функцию SetCommState(), мы можем установить параметры работы порта.

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

BOOL SetCommTimeouts (HANDLE hFile,

LPCOMMTIMEOUTS lpCommTimeouts);

Нетрудно догадаться, что первым аргументом при обращении к функции является хэндл файла (порта - в нашем случае). Второй аргумент - это указатель на структуру типа COMMTIMEOUTS, которая в файле winbase.h описана следующим образом:

typedef struct ._COMMTIMEOUTS {

DWORD ReadIntervalTimeout;

DWORD ReadTotalTimeoutMultiplier;

DWORD ReadTotalTimeoutConstant; 

DWORD WriteTotalTimeoutMultipiier;

DWORD WriteTotalTimeoutConstant;

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

Поле ReadIntervalTimeout определяет максимальный допустимый интервал (в миллисекундах) между появлением на коммуникационной линии двух последовательных символов.

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

Поле ReadTotalTimeoutConstant задаёт задержку (в миллисекундах), необходимую для завершения операции чтения. Для вычисления общей длительности операции чтения эта задержка прибавляется к результату умножения ReadTotalTimeoutMuitiplier на число считываемых символов.

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

Поле WriteTotalTimeoutConstant задаёт задержку (в миллисекундах), необходимую для завершения операции записи. Для вычисления общей длительности операции записи эта задержка прибавляется к результату умножения WriteTotalTimeoutMultiplier на число записываемых символов.

И в данном случае, как и в случае с функцией SetCommState(), мы можем получить установленные ранее значения задержек. Это делается при помощи функции:

BOOL GetCommTimeouts (HANDLE hFile,

LPCOMMTIMEOUTS lpCommTimeouts);

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

 

Для получения информации о настройках порта можно использовать также функцию:

BOOL GetCommProperties (HANDLE hFile,

LPCOMMPROP lpCommProp);

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

 

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

BOOL CommConfigDialog(LPCSTR lpszName, HWND  hWnd,

LPCOMMCONFIG lpCC);

 

Первый аргумент этой функции – указатель на строку, в которой содержится имя устройства, для которого вызывается диалоговое окно. Второй аргумент - хэндл родительского окна. Третий аргумент является указателем на структуру типа COMMCONFIG, в которой перед вызовом диалогового окна содержится начальная информация. После завершения работы окна в этой структуре будут находиться новые значения параметров.

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

BOOL SetCommConfig(HANDLE hCommDev,

LPCOMMCONFIG lpCC, DWORD dwSize);

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

С помощью функции

BOOL GetDefaultCommConfig((LPCWSTR lpszName,

LPCOMMCONFIG lpCC),

DWORD dwSize);

можно определить параметры порта, установленные системой по умолчанию.

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

Можно установить собственные значения по умолчанию с помощью функции:

BOOL SetDefaultCommConfig((LPCWSTR lpszName,

LPCOMMCONFIG lpCC),

DWORD dwSize);

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

Кроме перечисленных функций еще имеется ряд функций, позволяющих управлять портом в процессе работы (подробнее об этих функциях [6]).

4. Управление файлами, каталогами, ДИсками

4.1. Управление файлами

Win32 содержит  множество функций управления  файлами, как правило, довольно простых. Приведенные ниже функции удаляют, копируют и переименовывают файлы. Также есть функция, создающая имена временных файлов.

Удаление файлов

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

BOOL DeleteFile (LPCTSTR lpszFileName)

LpszFileName – указатель на буфер, в котором хранится имя удаляемого файла.

Следует отметить одну особенность работы этой функции в Windows 9х: вызвав эту функцию, можно удалить и открытый файл, и файл, отображенный в память, что, в свою очередь, может привести к безвозвратной потере данных. Чтобы предотвратить потерю данных, файл перед удалением следует закрывать.В Windows 2000/NT нельзя удалить открытый файл, попытка сделать это в NT приведет к ошибке.

Копирование файлов

Скопировать файл можно с помощью всего одной функции.

BOOL CopyFile (LPCTSTR lpszExistingFile,

LPCTSTR lpszNewFile,

BOOL  fFailExists)

CopyFile копирует  определенный по имени существующий  файл (имя этого файла хранится  в буфере по адресу lpszExistingFile) и  присваивает копии указанное  новое имя из буфера с адресом lpszNewFile. В случае, если файл с новым именем уже существует, работа функции зависит от значения третьего параметра fFailExists. Если fFailExists равно FALSE, то функция удалит старый файл и создаст новый. Если же fFailExists равно TRUE, функция CopyFile вернет значение FALSE, которое фактически является сообщением об ошибке. В обычной ситуации возвращенное функцией значение  TRUE говорит о том, что работа функции завершена успешно, а значение FALSE – о том, что при работе функции встретилась какая-то ошибка, код которой можно узнать с помощью функции GetLastError().

 

Перемещение файлов

Для перемещения файла служат функции MoveFile() и MoveFileEx(). Эти функции также могут работать с каталогами (действие функций DeleteFile и CopyFile ограничено файлами).

 

BOOL MoveFile (LPCTSTR lpszExisting, LPCTSTR lpszNew)

 

BOOL MoveFileEx (LPCTSTR lpszExisting, LPCTSTR lpszNew,

DWORD fdwFlags)

 

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

Параметры функций управления файлами:

lpszExisting  определяет имя существующего  файла или каталога.

lpszNew определяет имя нового файла или каталога, который в MoveFile не должен  существовать. Новый файл может находиться в другой файловой системе или на другом диске, но новые каталоги должны быть на том же диске. Если этот параметр имеет значение NULL, существующий файл удаляется.

FdwFlags определяет  следующие опции:

  • MOVEFILE_REPLACE_EXISTING -  используется для замены существующего файла;
  • MOVEFILE_WRITETHROUGH – гарантирует, что функция не возвращает управление, пока скопированный файл не будет переписан из промежуточного буфера на диск;
  • MOVEFILE_COPY_ALLOWED – когда новый файл находится на другом томе, перемещение осуществляется путем выполнения CopyFile  и DeleteFile;
  • MOVEFILE_DELAY_UNTIL_REBOOT – этот флаг, который не может применяться вместе с MOVEFILE_COPY_ALLOWED, разрешен только для администраторов и задерживает фактическое перемещение файла до перезапуска системы;

 

Перемещение (переименование) файлов связано с несколькими важными ограничениями.

  • Поскольку в Windows 9х не реализована функция MoveFileEx, ее необходимо заменять последовательностью CopyFile и DeleteFile. Это означает, что  в какой-то момент времени будут существовать две копии, что может вызвать проблемы при почти заполненном диске или большом файле. Этот способ влияет на атрибуты файла иначе, чем «настоящее» перемещение.
  • В именах файлов или каталогов не допускаются подстановочные знаки. Следует указывать конкретное имя.

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