Использование win32 api. Что такое Win32: основные понятия и простейшие методы устранения возникающих ошибок. Вызов подпрограммы Win32 API

Win32 API (Application Program Interface) используется для расширения возможностей разрабатываемого приложения VBA. Средства Win32 API, применяемые как в приложениях Windows, так и в самом Windows 95/98 или Windows NT, можно использовать в разрабатываемой программе. Эти средства имеют большие различия друг от друга: одни задают поведение операционной системы, а другие просто подают звуковой сигнал. При использовании функций Win32 API требуется особая осторожность: при неправильном применении подпрограммы API. возникает сбой работы приложения VBA или даже Windows.

Основные вопросы:

Что такое Win32 API

Описание функций Win32 API

Программирование Win32 API с помощью VBA

Использование Win32 API

ЧТО ТАКОЕ WIN32 API?

API (Application Programming Interface) - интерфейс программирования приложений и всегда связан с другим приложением. Например, Microsoft Excel, Lotus Organizer и множество других приложений имеют API. Pазработчики программного обеспечения не покупают программный интерфейс, они строят его при создании приложений.

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

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

Рассмотрев, как используется API, требуется описать, что такое API? API - это обычно не более чем просто набор функций, с помощью которых можно обратиться к средствам разрабатываемого приложения. Программа, реализующая API, часто занимает не больше 10 или 20 процентов всего приложения, однако, она должна обеспечивать доступ к 100 процентам функций этого приложения.

Win32 API идеально подходит под это описание: он обеспечивает доступ практически ко всем функциям Windows 95/98 и Windows NT. Win32 API помогает Windows 95/98 и Windows NT управлять памятью, различными устройствами, например принтером, обрабатывать события, рисовать на экране диалоговые окна и т. д.

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

Во многих программах, например, Microsoft Excel и Lotus cc:Mail, также используется Win32 API. Если приложению или модулю Windows 9х или Wiindows NT требуется некоторое средство, то обычно вызывается функция Win32 API.

Использование библиотек динамической компоновки

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

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

Одни файлы библиотек динамической компоновки имеют расширение DLL, другие - расширение ЕХЕ. Следующие файлы составляют большую часть Win32 API:

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

Когда нужно использовать Win32 API?

С помощью Win32 API можно использовать в разрабатываемом приложении не только средства VBA или основного приложения, но и те же фунции, что применяет Windows 9х или Windows NT. Эти средства позволяю пример, управлять памятью или создавать диалоговые окна для установки системного времени. Хотя в проекте VBA обычно используется только процент функций Win32 API, однако доступны практически все 100 процентов.

Win32 АPI включает более 1500 функций, поэтому здесь невозможно описать каждое средство. Вместо этого приводится классификация функцией API:

Управление Windows. Данная группа функций управляет рисованием окон на экране, а также обрабатывает нажатия клавиш и действия мышью при работе с окнами.

Элементы управления Windows. Данная группа функций управляет инструментами управления, используемыми в приложениях Windows, например, полями, кнопками и списками, а также стандартными диалоговыми окнами, такими как диалоговые окна "Открытие файла" и "Печать".

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

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

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

Языковая поддержка. Данная группа обеспечивает языковую поддержку для Windows 9х, Windows NT и их приложений.

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

Подробную информацию о группах и функциях Win32 API смотрите в руководстве по Win32 SDK, которое поставляется Microsoft.

ПРОГРАММИРОВАНИЕ WIN32 API В VBA

Описание подпрограммы

До использования функции или подпрограммы Win32 API, необходимо объявить ее в разделе описаний любого модуля. При этом можно создать отдельный модуль, в котором хранятся только описания. Если проект VBA держит ряд подпрограмм Win32 API, то полезно сгруппировать все описания, а также объявить используемые типы данных и константы в одном модуле.

При описании функций требуется задать:

Библиотеку динамической компоновки, в которой хранится требуемая функция

Передаваемые в функцию параметры и их тип

Тип возвращаемого значения

Предупреждение

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

Приведем пример описания функций Win32 API:

Declare Function GetWindowsDirectory Lib "kernel32" Alias _ "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As _ Long) As Long - получает путь к каталогу Windows

Declare Function GetSystemDirectory Lib "kernel32" Alias _ "GetSystemDirectoryA" (ByVal IpBuffer As String, ByVal nSize As _ Long) As Long - получает путь к системному каталогу Windows

Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" _ (ByVal nBufferLength As Long, ByVal IpBuffer As String) As Long - получает путь к каталогу, выделенному под временные файлы

Declare Function SetCurrentDirectory Lib "kernel32" Alias _ "SetCurrentDirectoryA"(ByVal lpPathName As String) As Long - задает текущий каталог

На первый взгляд приведенные примеры могут показаться очень сложным. Однако усвоив только несколько основных правил, можно безопасно и эффективно использовать Win32 API.

Использование WIN32API.TXT

Microsoft поставляет вместе со многими приложениями, включая Visual Basic, файл WIN32API.TXT, который содержит описание всех функций Win32 API. В данном файле находятся также описание структур данных" констант, используемых подпрограммами API. Длина файла слишком велика, чтобы включать его в каждый проект VBA, поэтому можно скопировать в модуль описание требуемых функций, а также типов данных или констант.

Синтаксис описания

Приведем синтаксис инструкции описания функции Win32 API:

Declare Function | Sub имя Lib "имя библиотеки" (аргументы)

Ниже приводится описание параметров функции.

Функции и процедуры

В Win32 API имеются функции и процедуры. В первом примере показано описание процедуры, а во втором - описание функции:

Declare Sub GetSystemTime Lib "kernel32" Alias "GetSystemTime" _ (lpSystemTime As SYSTEMTIME) - получает текущее системное время

Declare Function VerLanguageName Lib "kerne132" Alias _ "VerLanguageNameA" (ByVal wLang As Long, ByVal szLang As String, _ ByVal nSize As Long) As Long - получает текстовое название языка по идентификатору (&H4E3 - Windows (кириллица)

Обратите внимание на то, что в первом описании используется ключевое слово Sub.

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

Declare Function SomeFunction Lib "SomeLib" (MyArgument) as Long

Dim MyReturn as String

MyReturn = SomeFunction(My Argument)

Задание имени библиотеки

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

Имя библиотеки указывается в кавычках после ключевого слова Lib. При этом обычно не требуется задавать расширение или путь к библиотеке.

Примечание

При обращении к функции Win32 API сначала производится поиск библиотеки в папке, из которой запущено приложение, а затем просматривается каталог SYSTEM папки Windows (SYSTEM32 для Windows NT). Если VBA не находит библиотеку в каталоге SYSTEM/SYSTEM32, то он пытается отыскать библиотеку в основном каталоге Windows. Таким образом, если не перемещать файлы из папки Windows, то разработчику совсем необязательно знать расположение библиотек на диске, т. к. они отыскиваются aавтоматически. Более того, перемещать файлы из папки SYSTEM/SYSTEM32 не рекомендуется при любых обстоятельствах.

Использование псевдонимов в описаниях

Псевдонимы позволяют вызвать функцию API в программе VBA под именем, которое отличается от заданного в библиотеке динамической компоновки названия. Псевдонимы появились в Win16 API, используемом в Windows 3.x и 16-битных приложениях Windows, из-за того, что имена функций API совпадали с некоторыми зарезервированными словами языка Visual Basic. По той же причине при описании функций Win32 API задаются псевдонимы, с помощью которых можно вызвать требуемые подпрограммы, т используя имен, эквивалентных ключевым словам VBA. Если имя функции совпадает с ключевым словом, требуется вызвать функцию, ссылаясь на нее c помощью псевдонима.

Наиболее критической и потенциально ошибочной частью описания является список параметров. Поскольку Win32 API разработан прежде всего для использования в языке программирования С, а к подпрограммам обращаются из VBA, то необходимо знать, как передавать аргументы по ссылке и по значению.

В большинство используемых в VBA подпрограмм API требуется передать параметры. По умолчанию VBA передает параметры в подпрограмму по ссылке. Это означает, что вместо того, чтобы использовать в процедуре копию переменной, VBA передает ее 32-битный адрес в памяти. Располагая адресом переменной, т. е. ссылкой на нее, процедура может обращаться и изменять значение самой переменной, а не ее копии.

Кроме того, учитывая, что Win32 API предназначен для использования в языке С, в API используется соглашение этого языка о том, что по умолчанию параметры передаются в подпрограммы по значению, а не по ссылке. Это относится ко всем типам данным, исключая строки и массивы, которые всегда передаются по ссылке. Необходимо также отметить, что для некоторых функций API передача параметров по ссылке обязательна.

Declare Function ReadFile Lib "kernel32" Alias "ReadFile" (ByVal _ hFile As Long, IpBuffer As Any, ByVal nNumberOfBytesToRead As Long, _ IpNumberOfBytesRead As Long, IpOverlapped As OVERLAPPED) As Long

При задании аргументов необходимо правильно указать способ передачи аргументов. При этом рекомендуется придерживаться следующих правил:

Тщательно изучите описание процедуры в файле WIN32API.TXT, чтобы узнать тип передаваемых параметров.

Тщательно изучите документацию по API и по требуемой процедуре. Документация по Win32 API поставляется вместе с Win32SDK, а также с Visual Basic.

Задание строк в качестве параметров описаний

Как уже отмечалось ранее, строки передаются в процедуры библиотеки динамической компоновки по ссылке, т. е. по адресу первого байта строки в оперативной памяти. Для этого в описании процедур используется ключевое слово ByVal. При передаче ссылки на строку она преобразуется в форму, которая применяется в языке С: в конце строки ставится символ Null, который указывает на ее окончание. Приведем пример использования функции Win32 API для запуска приложения со строками в качестве параметров:

Declare Function CreateProcess Lib "kerne132" Alias "CreateProcessA" (ByVal IpApplicationName As String, ByVal IpCommandLine As String, IpProcessAttributes As SECURITY_ATTRIBUTES, IpThreadAttributes As SECURITY_ATTRIBUTES, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal IpCurrentDriectory As String, IpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long

Проверка описания

По завершении задания описания необходимо проверить его. Для этого необходимо запустить любую подпрограмму, которая имеется в одном модуле с указанным описанием. При этом можно выявить ошибки в описании, а также неправильное использование констант. Необходимо отметить, что поскольку при запуске процедуры VBA автоматически компилируется модуль, в котором она находится, можно использовать любую подпрограмму она может быть не закончена или не использована в разрабатываемом приложении. Например, следующая подпрограмма устанавливает значение переменной. Конечно, вместо запуска проверочной процедуры можно просто скомпилировать проект.

Public Sub CheckDex ()

Вызов подпрограммы Win32 API

После описания подпрограмм необходимо вызвать их. Обратиться к функции Win32 API настолько же просто, как и вызвать собственную функцию или подпрограмму. Необходимо только следовать следующим правилам:

Правильно описать используемые в подпрограмме переменные

Правильно обработать возвращаемые функцией значения

В программе показан вызов функции, которая определяет тип дисковода, заданного символом "F". Обратите внимание, как передается в функцию строка, как возвращаемое функцией значение записывается в переменную типа Long, и как используется блок Case... Select для обработки всех возвращаемых значений.

" Значения, возвращаемые функцией GetDriveType

Public Const DRIVE_REMOVABLE = 2"

Public Const DRIVE_FIXED = 3

Public Const DRIVE__REMOTE = 4

Public Const DRIVE_CDROM = 5.

Public Const DRIVE_RAMDISK = 6

Declare Function GetDriveType Lib "kerne132" Alias "GetDriveTypeA" (ByVal nDrive As String) As Long

Public Sub DisplayDriveType ()

Dim sDriveLetter As String

Dim lDriveType As Long

sDriveLetter = "F:"

lDriveType = GetDriveType (sDriveLetter)

Select Case lDriveType

Case DRIVE_REMOVABLE

Debug.Print "Дисковод ", sDriveLetter, " используется для чтения дискет."

Case DRIVE_FIXED

Debug.Print "Диск ", sDriveLetter, " - жесткий диск."

Case DRIVE_REMOTE

Debug.Print "Диск ", sDriveLetter, " - сетевой диск."

Base DRIVE_CDROM

Debug.Print "Дисковод ", sDriveLetter, "используется для чтения компакт-дисков."

Case DRIVE__RAMDISK

Debug.Print "Диск ", sDriveLetter, " - виртуальньй диск."

Debug.Print "Ошибка вызова функции."

Использование строк в качестве возвращаемых значений

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

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

return = GetTempPath (PathLength, Path)

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

Return = GetTempPath (len (ThePath) , ThePath)

Debug.Print ThePath

Предупреждение

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

Public Sub PrintTempPathO

Dim sThePath as String

Dim iPathLength as Long

Dim lResult as Long

iPathLength =256

sthePath = String$(iPathLength,0)

lResult = GetTempPath(iPathLength,sThePath)

Значения, возвращаемые функциями Win32 API

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

Dim lReturn as long

lReturn = SetCurrentDirectory(С:\WORK")

Если функция установила папку "C:\WORK" текущей, то переменная lReturn имеет значение отличное от 0. В противном случае, например, если каталог не существует, переменной lReturn присваивается значение 0.

Примечание

Одни функции Win32 API возвращают значение 0 в случае успешного завершения, а другие - в случае ошибки. Подробную информацию о возвращаемых функциями Win32 API значениях смотрите в документации по Win32 SDK.

Кроме того, некоторые функции присваивают значение передаваемому в них параметру, а также имеют возвращаемое значение. Рассмотрим функцию GetPrivateProfile (), которая используется для чтения данных из файла INI, хранящего информацию о конфигурации 16-битных приложений Windows:

Dim lSize as long

Dim strSize as long

Dim strValue as string

StrValue = String$ (strSize, 0)

lSize = GetPrivateProfileString ("CoolApplication", _ "ApplicationPath", "", strValue, strSize, "C:\MYINI.INI")

Функция GetPrivateProfile () извлекает значение параметра ApplicationPath раздела CoolApplication и присваивает его строковой переменной strValue, которая передается в функцию в качестве параметра. Кроме того, в переменную lSize записывается длина строки, присвоенной переменной strValue. Необходимо отметить, что если при выполнении функции GetPrivateProfile () возникает ошибка, то переменной lSize присваивается значение Null.

Работа с дескрипторами

Gри работе с Win32 API используются дескрипторы. Дескриптор - это 32-битное целое число, которое однозначно идентифицирует компоненты, используемые при программировании Win32 API, например, диалоговые окна, элементы управления в них, окна, битовые изображения, кисти, используемые для рисования картинок на экране, аппаратные средства. Необходимо отметить, что компоненту сначала назначается дескриптор, а затем этот дескриптор используется для работы с компонентом. Имена дескрипторов обычно задают имена, начинающиеся с префикса h.

В следующем описании параметр hWnd используется для задания дескриптора окна:

Declare Function GetMessage Lib "user32" Alias "GetMessageA" (lpMsg_ As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin В As Long, ByVal _ wMsgFilterMax As Long) As Long

В следующем описании параметр hDevice является дескриптором конфигурации устройства:

Declare Function DeviceIoControl Lib "kerne132" Alias _ "DeviceIoControl" (ByVal hDevice As Long, ByVal dwIoControlCode As _ Long, lpInBuffer As Any, ByVal nInBufferSize As Long, lpOutBuffer _ As Any, ByVal nOutBufferSize As Long, lpBytesReturned As Long, _ lpOverlapped As OVERLAPPED) As Long

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

Другие полезные примеры

Здесь приводятся несколько полезных примеров применения функций Win32 АРI.

Завершение работы Windows:

Declare Function ExitWindows Lib "user32" Alias "ExitWindowsEx" _ (ByVal dwReserved As Long, ByVal uReturnCode As Long) As Long

Const EWX_LOGOFF=0

Const EWX_SHUTDOWN=1

Const EWX_REBOOT=2

Const EWX_FORCE=4

Const EWX_POWEROFF=8

Private Sub CommandButton1_Click()

Dim flag As Long

Dim Result As Long

If Me.OptionButton1.Value = True Then flag = 0

Else If Me.OptionButton2.Value = True Then flag = 8

Else If Me.OptionButton3.Value = True Then flag = 2

Result = ExitWindows(flag, 0)

Переключение на русскую кодировку:

Public Declare Function ActivateKeyboardLayout Lib "user32" (ByVal _ HKL As Long, ByVal flags As Long) As Long

Public Declare Function GetKeyboardLayout Lib "user32" (ByVal _ dwLayout As Long) As Long

Private Sub UserForm_Initialize()

Dim lang As Long

lang = GetKeyboardLayout(0)

If lang <> 68748313 Then i = ActivateKeyboardLayout(68748313, 0)

Закрытие окна:

Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long

Declare Function DestroyWindow Lib "user32.dll" (ByVal hwnd As Long) As Long

Private Sub CommandButton3_Click()

Dim hwnd As Long, retval As Long

Dim temp As String

hwnd = FindWindow(CLng(0), temp) " look for the window

retval = DestroyWindow(hwnd)

Работа с реестром

В Windows 9х и Windows NT, a также приложениях, работающих под их управлением, используется специальная база данных, в которой хранится требуемая для выполнения программ информация: данные о компьютере, на котором инсталлировано программное обеспечение, о пользователях, об установленных аппаратных средствах и т. д. Эта база данных называется реестром. Для доступа к реестру из VBA используются функции Win32 АРI. Кроме того, чтобы просмотреть и отредактировать реестр вручную, можно запустить программу REGEDIT (REGEDT32 для Windows NT).

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

Структура реестра

Реестр имеет структуру дерева. В нем имеются шесть основных поддеревьев (пять в Windows NT). Элементы реестра называют ключами. Ключ может иметь подключи, а подключи - включать дополнительные ключи, например HKEY_CURRENT_USER\ControlPanel\ Accessibility.

Данные в реестре используются приложениями и операционной системой способами:

Иногда в приложении требуется просто проверить существование ключа. Например, приложение ищет ключ HKEY_CURRENT_CONFIG\Display\Settings и не проверяет наличие в нем подключей или параметров.

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

Ключи реестра

В верхней части дерева реестра имеются шесть основных ключей. Рассмотрим каждый из них:

HKEY_LOCAL_MACHINE. Под данным ключом хранятся данные, описывающие установленные на компьютере аппаратные средства и программное обеспечение.

HKEY_CURRENT_CONFIG. Под данным ключом хранится информация о конфигурации установленных на компьютере аппаратных средств.

HKEY_DYN_DATA (только Windows 9х). Под данным ключом хранится информация об установленных на компьютере самонастраивающихся устройств Plug и Play.

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

HKEY_USERS. Под данным ключом хранится информация о пользователях.

HKEY_CURRENT_USER. Под данным ключом хранится информация о текущем пользователе.

Использование VBA для доступа к реестру с помощью функций Win32 API

В VBA можно управлять реестром с помощью функций Win32 API, например:

Создать новые ключи в реестре

Экспортировать реестр в файл

Импортировать данные реестра из внешнего файла

Описания, константы и типы данных реестра

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

Открытие ключа

Чтобы работать с ключами реестра, необходимо предварительно открыть их. При открытии ключа возвращается его дескриптор. Как уже отмечалось ранее, для ссылки на данные можно использовать их дескриптор, в данном случае для доступа к ключу используется дескриптор ключа. Для открытия ключа используется функция RegOpenKey (), которая возвращает значение, указывающее на то, было ли ее выполнение успешным или нет. Кроме того, данная функция присваивает своему аргументу дескриптор ключа. Покажем пример использования функции RegOpenKey (). В программе выводится число 0 в окне отладки, если при выполнении функции не возникло ошибок, а также отображается значение параметра hSubKeyHandle, который является дескриптором ключа

Программа открытия ключа:

Public Sub OpenRegistryKeyDemo()

Dim lReturn As Long

Dim sSubKey As String

Dim hSubKeyHandle As Long

lReturn = RegOpenKey&(HKEY_LOCAL_MACHINE, "Config", hSubKeyHandle)

Debug.Print lReturn

Debug.Print hSubKeyHandle

Чтение значения параметра реестра

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

Функция RegQueryValueO. Извлекает значение параметра по умолчанию

Функция RegQueryValueEx (). Извлекает значение именованного параметра.

Чтобы получить значение именованного параметра, используйте функцию RegQueryValueEx(). Например, под ключом HKEY_CURRENT_СОNFIG\Display\Settings имеется параметр Resolution, в котором хранится разрешение экрана. В программе показан пример чтения значения именованного параметра. Программа требует некоторых пояснений:

Функция RegOpenKey () используется для получения дескриптора требуемого ключа. Дескриптор передается в функцию RegQueryValueEx ().

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

Третий аргумент функции RegQueryValueEx () зарезервирован для использования Windows, ему требуется присвоить значение 0.

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

Public Sub GetRegistryNameValueDemo ()

Dim lReturn As Long

Dim sSubKey As String

Dim hSubKeyHandle As Long

Dim sValue As String

Dim lSize As Long

SValue=String$(lSize, 0)

lReturn = RegOpenKeyEx(HKEY_CURRENT__CONFIG, Display\Settings", 0, _ KEY_ALL_ACCESS, hSubKeyHandle)

If lReturn<> 0 Then Exit Sub

lReturn = RegQueryValueEx(hSubKeyHandle, "Resolution", 0, REG_SZ, _ sValue, lSize)

Debug.Print sValue

Создание ключа

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

Функция RegCreateKey () используется для создания ключа реестра. Кроме того, для этого применяется функция CreateRegKeyEx (), которая имеет большее число аргументов, например, параметры, гадающие защиту.

С помощью функции RegCreateKey () можно создать как один, так и структуру из нескольких ключей, например, добавить ключ "NEWKEY" под ключ HKEY_CURRENT_USER, или создать структуру KEYONE\KEYTWO\KEYTHREE под ключом HKEY_CURRENT_USER. В программе показано создание структуры разделов под ключом HKEY_LOCAL_MACHINE.

Программа создания ключей реестра

Public Sub CreateRegistryKeyDemo ()

Dim lReturn As Long

Dim sSubKey As String

Dim hSubKeyHandle As Long

sSubKey = "SOFTWARE\Использование VBA\Win32 API\Обзор"

lReturn = RegCreateKey& (HKEY_LOCAL_MACHINE, sSubKey, _ hSubKeyHandle)

Установка значения параметра

Для установки значений параметров реестра по умолчанию используется функция RegSetValue (), а для задания величин именованных параметров- функция RegSetValueEx(). Необходимо отметить, что функцию RegSetValueExО можно применять, например, для установки значений именованных параметров ключа, созданного предыдущей программой. В следующей программе показано использование функции RegSeiValue(). Перед установкой значений требуется открыть ключ, под которым находится требуемый параметр. Единственным исключением из этого правила являются параметры реестра, которые хранятся под одним из шести основных ключей (пяти в Windows NT). В этом случае следует задать имя ключа (например, HKEY_LOCAL_MACHINE), а не дескриптор ключа в качестве первого параметра функции RegSetValue ().

Программа требует некоторых пояснений:

Имя параметра и устанавливаемое значение непосредственно передаются в функцию RegSetValueEx ().

Переменная lValueSize задает длину присваиваемого параметру строкового значения.

В качестве четвертого аргумента функции RegSetValueEx () требуется задать тип устанавливаемого значения (REG_SZ). Список констант, соответствующих допустимым типам, приводится в разделе "Описания, константы и типы данных реестра".

Третий аргумент функции RegSetValueEx () всегда равен 0.

Public Sub SetRegistryValue ()

Dim lReturn As Long

Dim hSubKeyHandle As Long

Dim sSubKeyName as String

Dim sValueName As String

Dim sValue As String

sSubKeyWame = "SOFTWARE\Использование VBA\Win32 API\Обзop"

"Открытие ключа и получение его дескриптора

lReturn = RegOpenKey(HKEY_LOCAL_MACHINE, sSubKeyNaine, hSubKeyHandle)

If lresult <> 0 Then Exit Sub

"Установка первого значения

sValueMame = "Скучно?"

sValue = "Нет"

lValueSize = Len (sValue)

" Установка второго значения

lsValueName = "Весело?"

sValue = "Конечно"

lValueSize = Len (sValue)

lReturn = RegSetValueEx (hSubKeyHandle, sValueName, 0, REG_SZ, sValue, lValueSize)

Все программы Win32 имеют схожий интерфейс: окно с заголовком, границами, системным меню, кнопками “закрыть”, “свернуть” “развернуть/восстановить” и рабочей областью. Окно можно переместить мышью, потянув за заголовок, можно изменить его размеры, перемещая границы. Рабочая область часто содержит меню, панель(и) инструментов. ОС работает с ресурсом через его объект, а пользователь работает с окнами.

    Приложения

    Работа с окном

  • Работа в окне

Оснастка – ПО, устанавливаемое в комплекте с ОС. В нее входит рабочее окно – desktop: кнопка Start, My Computer, Recycle Bin, My Documents… Также устанавливается дополнительный набор программ – notepad, paint, explorer, find, служебные программы (scandisk, defrag, system information), …

    Работа с оборудованием. Профили оборудования.

Control Panel Device Manager (Диспетчер устройств)

Свойства (общие, драйвер, ресурсы), добавить , удалить .

Control Panel Мастер установки оборудования

Все устройства делятся на наследованные (автоматически не распознаются), PnPработают не полностью,PnPполностью работают. Устройство,BIOSи ОС должны поддерживатьPnP, иначе устройство не будетPnP.

ControlPanelУстановка принтера

Сначала установить ПО, потом принтер, либо установить драйвер без присутствия принтера.

ControlPanelSystemПрофили оборудования

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

    Пользовательские настройки среды Windows. Профили пользователя.

ControlPanelPasswords

Можно установить один профиль для всех пользователей

ControlPanelUsers

В профиль пользователя входят:

  • Избранное

    Звуковое сопровождение

    Language и International Standards

Единица размера (кегля) – пункт – 1/32 дюйма, ~0.36 мм. Шрифты из одного семейства различаются какими-либо параметрами. Они бывают пропорциональными и непропорциональными.Кернинг – размещение букв таким образом, чтобы расстояние между ними казалось одинаковым.

Эффекты – Bold , Italic , Underlined , Strike out …

Типы шрифтов Microsoft:

    Битовые (растровые) FON

    Векторные FON(Используются геометрические единицы)

    TrueTypeTTF(Используются математические формулы)

Кроме того:

    PostScriptработают быстро, но для каждого параметра нужен свой растр (Mac,Adobe)

  1. Настройка производительности среды Windows.

      Видеосистема

Уровень использования аппаратного ускорения

Разрешение экрана

Рисунок на рабочем столе, различные эффекты

      Файловая система

ControlPanelSystemПроизводительность

Настройка кэша на чтение и запись.

Имена в формате 8.3

Дефрагментация диска

      Печать

ControlPanelPrinter

Печатать после загрузки одной страницы или всего документа

      Виртуальная память

ControlPanelSystemПроизводительность

Файл подкачки – для памяти, вытесняемой из RAM. Авто-выбор зависит от размера свободно пространства.

  1. Характеристика приложений. Поддержка приложений Win32.

Win32,Win16,DOS16 – три вида приложений. Все программыWin32 выполняются в одной виртуальной машине.

Приложения Win32:

Формат EXEPE

Стандарт API(стандартные средства интерфейса и функции ядра)

Поддержка работы в сети

Поддержка механизма OLE(хранение объектов различных приложений в одном документе,OLEпривязано к структуре каталогов, следовательно, плохо переносимо)

Поддержка P&P

Стандартная установка и удаление

Windows API - набор функций операционной системы

Аббревиатура API многим начинающим программистам кажется весьма таинственной и даже пугающей. На самом же деле Application Programming Interface (API) - это просто некоторый готовый набор функций, который могут использовать разработчики приложений. В общем случае данное понятие эквивалентно тому, что раньше чаще называли библиотекой подпрограмм. Однако обычно под API подразумевается особая категория таких библиотек.

В ходе разработки практически любого достаточно сложного приложения (MyAppication) для конечного пользователя формируется набор специфических внутренних функций, используемых для реализации данной конкретной программы, который называется MyApplication API. Однако часто оказывается, что эти функции могут эффективно использоваться и для создания других приложений, в том числе другими программистами. В этом случае авторы, исходя из стратегии продвижения своего продукта, должны решить вопрос: открывают они доступ к этому набору для внешних пользователей или нет? При утвердительном ответе в описании программного пакета в качестве положительной характеристики появляется фраза: «Комплект включает открытый набор API-функций» (но иногда за дополнительные деньги).

Таким образом, чаще всего под API подразумевается набор функций, являющихся частью одного приложения, но при этом доступных для использования в других программах. Например, Excel, кроме интерфейса для конечного пользователя, имеет набор функций Excel API, который может использоваться, в частности, при создании приложений с помощью VB.

Соответственно Windows API - это набор функций, являющийся частью самой операционной системы и в то же время - доступный для любого другого приложения, в том числе написанного с помощью VB. В этом плане вполне оправданна аналогия с набором системных прерываний BIOS/DOS, который фактически представляет собой DOS API.

Отличие заключается в том, что состав функций Windows API, с одной стороны, значительно шире по сравнению с DOS, с другой - не включает многие средства прямого управления ресурсами компьютера, которые были доступны программистам в предыдущей ОС. Кроме того, обращение к Windows API выполняется с помощью обыкновенных процедурных обращений, а вызов функций DOS - через специальную машинную команду процессора, которая называется Interrupt («прерывание»).

Зачем нужен Win API для VB-программистов

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

При знакомстве с Win API обнаруживается, что многие встроенные VB-функции - не что иное, как обращение к соответствующим системным процедурам, но только реализованное в виде синтаксиса данного языка. С учетом этого необходимость использования API определяется следующим вариантами:

  1. API-функции, которые полностью реализованы в виде встроенных VB-функций. Тем не менее иногда и в этом случае бывает полезным перейти к применению API, так как это позволяет порой существенно повысить производительность (в частности, за счет отсутствия ненужных преобразований передаваемых параметров).
  2. Встроенные VB-функции реализуют лишь частный случай соответствующей API-функции. Это довольно обычный вариант. Например, API-функция CreateDirectory обладает более широкими возможностями по сравнению со встроенным VB-оператором MkDir.
  3. Огромное число API-функций вообще не имеет аналогов в существующем сегодня варианте языка VB. Например, удалить каталог средствами VB нельзя - для этого нужно использовать функцию DeleteDirectory.

Следует также подчеркнуть, что некоторые API-функции (их доля в Win API весьма незначительна) не могут вызываться из VB-программ из-за ряда ограничений языка, например из-за отсутствия возможности работы с адресами памяти. Но в ряде случаев могут помочь нетривиальные приемы программирования (в частности, в случае с теми же адресами).

Личная точка зрения автора такова - вместо расширения от версии к версии встроенных функций VВ следовало бы давать хорошее описание наиболее ходовых API-функций. В то же время хочется посоветовать разработчикам не ждать появления новой версии средства с расширенными функциями, а внимательнее изучить состав существующего Win API - вполне вероятно, что нужные вам возможности можно было реализовать уже в версии VB 1.0 выпуска 1991 года.

Как изучать Win API

Это не такой простой вопрос, если учесть, что число функций Win32 API оценивается величиной порядка 10 тысяч (точной цифры не знает никто, даже Microsoft).

В состав VB (версий 4-6) входит файл с описанием объявлений Win API - WIN32API.TXT (подробнее о его применении мы расскажем позднее). Но, во-первых, с его помощью можно получить сведения о назначении той или иной функции и ее параметрах только по используемым мнемоническим именам, а во-вторых - перечень функций в этом файле далеко не полный. В свое время (семь лет назад) в VB 3.0 имелись специальные справочные файлы с описанием функций Win16 API. Однако уже в v.4.0 эта полезная информация с удобным интерфейсом исчезла.

Исчерпывающую информацию о Win32 API можно найти в справочной системе Platform Software Development Kit, которая, в частности, находится на компакт-дисках MSDN Library, включенных в состав VB 5.0 и 6.0 Enterprise Edition и Office 2000 Developer Edition. Однако разыскать там нужную информацию и разобраться в ней совсем не просто. Не говоря уж о том, что все описания там приводятся применительно к языку C.

Общепризнанным в мире пособием для изучения API-программирования в среде VB являются книги известного американского эксперта Даниэля Эпплмана (Daniel Appleman). Его серия Dan Appleman’s Visual Basic Programmer’s Guide to the Windows API (для Win16, Win32, применительно к разным версиям VB) с 1993 года неизменно входит в число бестселлеров для VB-программистов. Книгу Dan Appleman’s VB 5.0 Programmer’s Guide to the Win32 API, выпущенную в 1997 году, автору привез из США приятель, который нашел ее в первом же книжном магазине небольшого провинциального городка.

Эта книга объемом свыше 1500 страниц включает описание общей методики API-программирования в среде VB, а также более 900 функций. Прилагаемый компакт-диск содержит полный текст книги и всех программных примеров, а кроме того, несколько дополнительных глав, не вошедших в печатный вариант. В 1999 году Дэн Эпплман выпустил новую книгу Dan Appleman’s Win32 API Puzzle Book and Tutorial for Visual Basic Programmers, которая включает сведения о еще 7600 функциях (хотя и не столь обстоятельные).

Win API и Dynamic Link Library (DLL)

Набор Win API реализован в виде динамических DLL-библиотек. Далее речь фактически пойдет о технологии использования DLL в среде VB на примере библиотек, входящих в состав Win API. Однако, говоря о DLL, необходимо сделать несколько важных замечаний.

В данном случае под DLL мы подразумеваем традиционный вариант двоичных динамических библиотек, которые обеспечивают прямое обращение приложений к нужным процедурам - подпрограммам или функциям (примерно так же, как это происходит при вызове процедур внутри VB-проекта). Такие библиотеки могут создаваться с помощью разных инструментов: VC++, Delphi, Fortran, кроме VB (посмотрим, что появится в версии 7.0) - последний может делать только ActiveX DLL, доступ к которым выполняется через интерфейс OLE Automation.

Обычно файлы динамических библиотек имеют расширение.DLL, но это совсем не обязательно (для Win16 часто применялось расширение.EXE); драйверы внешних устройств обозначаются с помощью.DRV.

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

А теперь несколько советов.

Совет 1. Следите за правильным оформлением объявления DL L-процедур

Само обращение к DLL-процедурам в программе выглядит точно так же, как к «обычным» процедурам Visual Basic, например:

Call DllName ([список аргументов])

Однако для использования внешних DLL-функций (в том числе и Win API) их нужно обязательно объявить в программе с помощью оператора Declare, который имеет следующий вид:

Declare Sub ИмяПроцедуры Lib _ “ИмяБиблиотеки” _ [([СписокАргументов])]

Declare Function ИмяФункции _ Lib “ИмяБиблиотеки” _ [([СписокАргументов])]

Здесь в квадратных скобках приведены необязательные элементы оператора, курсивом выделены переменные выражения, остальные слова - ключевые. В справочной системе приведено достаточно хорошее описание синтаксиса оператора, поэтому сейчас мы только отметим некоторые моменты.

Объявления внешних функций должны размещаться в секции General Declarations модуля. Если вы размещаете его в модуле формы, то обязательно нужно указать ключевое слово Private (это объявление будет доступно только внутри данного модуля) - таково ограничение для всех процедур модуля формы.

Набор Win32 API реализован только в виде функций (в Win16 API было много подпрограмм Sub). В большинстве своем - это функции типа Long, которые чаще всего возвращают код завершения операции.

Оператор Declare появился в MS Basic еще во времена DOS, причем он использовался и для объявления внутренних процедур проекта. В Visual Basic этого не требуется, так как объявлением внутренних процедур автоматически является их описание Sub или Function. По сравнению с Basic/DOS в новом описании обязательно указывать имя файла-библиотеки, где находится искомая процедура. Библиотеки Wip API размещаются в системном каталоге Windows, поэтому достаточно привести только название файла. Если же вы обращаетесь к DLL, которая находится в произвольном месте, нужно записать полный путь к данному файлу.

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

Declare Function GetTempPath _ Lib “kernel32” Alias “GetTempPathA” _ (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long

В этом случае все основные элементы описания разнесены на разные строчки и поэтому хорошо читаются.

Совет 2. Будьте особенно внимательны при работе с DLL-функциями

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

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

Использование напрямую функций Windows API или других DLL-библиотек снимает такой контроль за передачей данных и процессом выполнения кода вне среды VB. Поэтому ошибка в обращении к внешним функциям может привести к неработоспособности и VB и операционной системы. Это особенно актуально на этапе разработки программы, когда наличие ошибок - дело вполне естественное. Таким образом, применяя более широкие возможности функций базового слоя системы, программист берет на себя ответственность за правильность их применения.

Проблема усугубляется еще и тем, что разные языки программирования используют различные способы передачи параметров между процедурами. (Точнее, разные способы передачи используются по умолчанию, так как многие языки могут поддерживать несколько способов.) Win API реализованы на C/C++ и применяют соглашения о передаче параметров, принятые в этой системе, которые отличаются от привычного для VB варианта.

В связи с этим следует отметить, что появление встроенных в VB аналогов API-функций оправданно именно адаптацией последних к синтаксису VB и реализацией соответствующего механизма контроля обмена данными. Обратим также внимание, что на этапе опытной отладки приложения при создании исполняемого модуля лучше использовать вариант компиляции P-code вместо Native Code (машинный код). В первом случае программа будет работать под управлением интерпретатора - медленнее по сравнению с машинным кодом, но более надежно с точки зрения возможного ошибочного воздействия на операционную систему и обеспечивая более удобный режим выявления возможных ошибок.

Совет 3. Десять рекомендаций Дэна Эпплмана по надежному API-программированию в среде VB

Использование функции API требует более внимательного программирования с использованием некоторых не очень привычных методов обращения к процедурам (по сравнению с VB). Далее мы будем постоянно обращаться к этим вопросам. А сейчас приведем изложение сформулированных Дэном Эпплманом советов на эту тему (их первый вариант появился еще в 1993 году) с некоторыми нашими дополнениями и комментариями.

1. Помните о ByVal. Наиболее частая ошибка, совершаемая при обращении к функциям API и DLL, заключается в некорректном использовании ключевого слова ByVal: его или забывают ставить, или, наоборот, ставят, когда в нем нет необходимости.

На этих примерах показано влияние оператора ByVal на передачу параметров

Тип параметра С ByVal Без ByVal
Integer В стек помещается 16-разрядное целое В стек помещается 32-разрядный адрес 16-разрядного целого
Long В стек помещается 32-разрядное целое В стек помещается 32-разрядный адрес 32-разрядного целого
String Строка преобразуется в формат, используемый в С (данные и завершающий нулевой байт). 32-разрядный адрес новой строки помещается в стек В стек помещается VB-дескриптор строки. (Такие дескрипторы никогда не используются самим Windows API и распознаются только в DLL, реализованных специально для VB.)

Здесь следует напомнить, что передача параметров в любой системе программирования, в том числе и VB, выполняется двумя основными путями: по ссылке (ByRef) или по значению (ByVal). В первом случае передается адрес переменной (этот вариант используется в VB по умолчанию), во втором - ее величина. Принципиальное отличие заключается в том, что с помощью ссылки обеспечивается возврат в вызывающую программу измененного значения передаваемого параметра.

Чтобы разобраться в этом, проведите эксперимент с помощью таких программ:

Dim v As Integer v = 2 Call MyProc(v) MsgBox “v = “ & v Sub MyProc (v As Integer) v = v + 1 End Sub

Запустив на выполнение этот пример, вы получите сообщение со значением переменной, равным 3. Дело в том, что в данном случае в подпрограмму MyProc передается адрес переменной v, физически созданной в вызывающей программе. Теперь измените описание процедуры на

Sub MyProc (ByVal v As Integer)

В результате при выполнении теста вы получите v = 2, потому что в процедуру передается лишь исходное значение переменной - результат выполненных с ним операций не возвращается в вызывающую программу. Режим передачи по значению можно поменять также с помощью оператора Call следующим образом:

Sub MyProc (v As Integer) ... Call MyProc((v)) ‘ (v) - скобки указывают режим _ передачи по значению.

Однако при обращении к внутренним VB-процедурам использование в операторе Call ключевого слова ByVal запрещено - вместо него применяются круглые скобки. Этому есть свое объяснение.

В классическом случае (С, Fortran, Pascal) различие режимов ByRef и ByVal зависит от того, что именно помещается в стек обмена данными - адрес переменной или ее значение. В Basic исторически используется вариант программной эмуляции ByVal - в стеке всегда находится адрес, но только при передаче по значению для этого создается временная переменная. Чтобы отличить два этих варианта (классический и Basic), используются разные способы описания режима ByVal. Отметим, что эмуляция режима ByVal в VB обеспечивает более высокую надежность программы: перепутав форму обращения, программист рискует лишь тем, что в вызывающую программу вернется (или не вернется) исправленное значение переменной. В «классическом» же варианте такая путаница может привести к фатальной ошибке при выполнении процедуры (например, когда вместо адреса памяти будет использоваться значение переменной, равное, скажем, нулю).

DLL-функции реализованы по «классическим» принципам и поэтому требуют обязательного описания того, каким образом происходит обмен данными с каждым из аргументов. Именно этой цели служат объявления функций через описание Declare (точнее, списка передаваемых аргументов). Чаще всего передача параметров в функцию Windows API или DLL выполняется с помощью ключевого слова ByVal. Причем оно может быть задано как в операторе Declare, так и непосредственно при вызове функции.

Последствия неправильной передачи параметров легко предугадать. В случае получения явно недопустимого адреса вам будет выдано сообщение GPF (General Protection Fault - ошибка защиты памяти). Если же функция получит значение, совпадающее с допустимым адресом, то функция API залезет в чужую область (например, в ядро Windows) со всеми вытекающими отсюда катастрофическими последствиями.

2. Проверяйте тип передаваемых параметров. Не менее важны верное число и тип передаваемых параметров. Необходимо, чтобы объявленные в Declare аргументы соответствовали ожидаемым параметрам в функции API. Наиболее часто встречающийся случай ошибки в передаче параметров связан с различием между NULL и строкой нулевой длины - следует помнить, что это не одно и то же.

3. Проверяйте тип возвращаемого значения.

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

  • DLL-функция, не возвращающая значения (аналог void в ‘C’), должна быть объявлена как VB Sub.
  • функция API, возвращающая целое значение (Integer или Long), может быть определена или как Sub, или как Function, возвращающая значение соответствующего типа.
  • ни одна из функций API не возвращает числа с плавающей точкой, но некоторые DLL вполне могут возвращать такой тип данных.

4. С большой осторожностью используйте конструкцию «As Any». Множество функций Windows API имеют возможность принимать параметры различных типов и используют при этом обращение с применением конструкции As Any (интерпретация типа выполняется в зависимости от значения других передаваемых параметров).

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

5. Не забывайте инициализировать строки. В Win API существует множество функций, возвращающих информацию путем загрузки данных в передаваемые как параметр строковые буферы. В своей программе вы можете вроде бы все сделать правильно: не забыть о ByVal, верно передать параметры в функцию. Но Windows не может проверить, насколько велик размер выделенного под строку участка памяти. Размер строки должен быть достаточным для размещения всех данных, которые могут быть в него помещены. Ответственность за резервирование буфера нужного размера лежит на VB-программисте.

Следует отметить, что в 32-разрядных Windows при использовании строк производится преобразование из Unicode (двухбайтовая кодировка) в ANSI (однобайтовая) и обратно, причем с учетом национальных установок системы. Поэтому для резервирования буферов порой удобнее использовать байтовые массивы вместо строковых переменных. (Подробнее об этом будет рассказано ниже.)

Чаще всего функции Win API позволяют вам самим определить максимальный размер блока. В частности, иногда для этого нужно вызвать другую функцию API, которая «подскажет» размер блока. Например, GetWindowTextLength позволяет определить размер строки, необходимый для размещения заголовка окна, получаемого функцией GetWindowText. В этом случае Windows гарантирует, что вы не выйдете за границу.

6. Обязательно используйте Option Explicit.

7. Внимательно проверяйте значения параметров и возвращаемых величин. VB обладает хорошими возможностями по проверке типов. Это означает, что, когда вы пытаетесь передать неверный параметр в функцию VB, самое плохое, что может случиться, - вы получите сообщение об ошибке от VB. Но данный механизм, к сожалению, не работает при обращении к функциям Windows API.

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

Здесь можно посоветовать использовать несколько способов отладки ошибки данного типа:

  • используйте пошаговый режим отладки или команду Debug.Print для проверки каждого подозрительного вызова функции API. Проверьте результаты этих вызовов, чтобы удостовериться, что все в пределах нормы и функция корректно завершилась;
  • используйте Windows-отладчик типа CodeView и отладочную версию Windows (имеется в Windows SDK). Эти средства могут обнаружить ошибку параметров и по меньшей мере определить, какая функция API приводит к ошибке;
  • используйте дополнительные средства третьих фирм для проверки типов параметров и допустимости их значений. Такие средства могут не только находить ошибки параметров, но даже указать на строку кода VB, где произошла ошибка.

Кроме того, нужно обязательно проверять результат выполнения API-функции.

8. Помните, что целые числа в VB и в Windows - не одно и то же. В первую очередь следует иметь в виду, что под термином «Integer» в VB понимается 16-разрядное число, в документации Win 32 - 32-разрядное. Во-вторых, целые числа (Integer и Long) в VB - это величины со знаком (то есть один разряд используется как знак, остальные - как мантисса числа), в Windows - используются только неотрицательные числа. Это обстоятельство нужно иметь в виду, когда вы формируете передаваемый параметр с помощью арифметических операций (например, вычисляете адрес с помощью суммирования некоторой базы и смещения). Для этого стандартные арифметические функции VB не годятся. Как быть в этом случае, мы поговорим отдельно.

9. Внимательно следите за именами функций. В отличие от Win16 имена всех функций Win32 API являются чувствительными к точному использованию строчных и прописных букв (в Win16 такого не было). Если вы где-то применяете строчную букву вместо прописной или наоборот, то нужная функция не будет найдена. Следите также за правильным использованием суффикса A или W в функциях, применяющих строковые параметры. (Подробнее об этом – см. ниже.)

10. Чаще сохраняйте результаты работы. Ошибки, связанные с неверным использованием DLL и Win API, могут приводить к аварийному завершению работы VB-среды, а возможно - и всей операционной системы. Вы должны позаботиться о том, чтобы написанный вами код перед тестовым запуском был сохранен. Самое простое - это установить режим автоматической записи модулей проекта перед запуском проекта в среде VB.

После прочтения предыдущего совета может возникнуть мысль, что использование функций Win API - дело рискованное. В какой-то степени это так, но только в сравнении с безопасным программированием, предоставляемым самим VB. Но при умелом их применении и знании возможных подводных камней этот риск минимален. К тому же полностью отказаться от применения Win API зачастую просто невозможно - они все равно потребуются при сколь-нибудь серьезной разработке.

К тому же ранее мы упоминали о «подводных» камнях для широкого класса DLL. В случае с Win API все обстоит гораздо проще, так как здесь четко унифицирована форма обращения к этим функциям. При этом следует иметь в виду следующие основные моменты:

  1. Функции Win32 API являются именно функциями, то есть процедурами типа Function (в Win16 API было много подпрограмм Sub). Все это функции типа Long, поэтому их описания записываются в следующем виде: Declare Function name ... As Long ‘ тип функции _ определяется в явном виде

    Declare Function name& ‘ тип функции _ определяется с помощью суффикса

    Обращение к API-функции выглядит так:

Result& = ApiName& ([СписокАргументов ]
  1. Чаще всего возвращаемое значение функции является кодом завершения операции. Причем ненулевое значение означает в данном случае нормальное завершение, нулевое - ошибку. Обычно (но не всегда) уточнить характер ошибки можно с помощью обращения к функции GetLastError. Описание этой функции имеет такой вид: Declare Function GetLastError& Lib “kernel32” ()

    ВНИМАНИЕ! При работе в среде VB для получения значения уточненного кода ошибки лучше использовать свойство LastDLLError объекта Err, так как иногда VB обнуляет функцию GetLastError в промежутке между обращением к API и продолжением выполнения программы.

    Интерпретировать код, возвращаемый GelLastError, можно с помощью констант, записанных в файле API32.TXT, с именами, начинающимися с суффикса ERROR_.

    Наиболее типичные ошибки имеют следующие коды:

    • ERROR_INVALID_HANDLE = 6& - неверный описатель
    • ERROR_CALL_NOT_IMPLEMENTED = 120& - вызов в Windows 9x функции, доступной только для Windows NT
    • ERROR_INVALID_PARAMETER = 87& - неверное значение параметра

    Однако многие функции возвращают значение некоторого запрашиваемого параметра (например, OpenFile возвращает значение описателя файла). В таких случаях ошибка определяется каким-либо другим специальным значением Return&, чаще всего 0 или –1.

  2. Win32 API используют строго фиксированные способы передачи самых простых типов данных. а) ByVal ... As Long

    С помощью переменных типа Long выполняется не менее 80% передачи аргументов. Обратите внимание, что аргумент всегда сопровождается ключевым словом ByVal, а это, кроме всего прочего, означает, что выполняется односторонняя передача данных - от VB-программы к API-функции.

    Б) ByVal ... As String

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

    Первая - резервирование памяти под строку производится в вызывающей программе, поэтому если API-функция будет заполнять строки, то нужно перед ее вызовом создать строку необходимого размера. Например, функция GetWindowsDirectory возвращает путь к каталогу Windows, который по определению не должен занимать более 144 символов. Соответственно обращение к этой функции должно выглядеть примерно так:

    WinPath$ = Space$(144) ‘ резервируем строку в _ 144 символа Result& = GetWindowsDirectory& (WinTath$, 144) _ ‘заполнение буфера ‘ Result& - фактическое число символов в имени _ каталога WinPath$ = Left$(WinPath, Result&)

    Вторая проблема заключается в том, что при обращении к API-функции производится преобразование исходной строки в ее некоторое внутреннее представление, а при выходе из функции - наоборот. Если во времена Win16 эта операция заключалась лишь в добавлении нулевого байта в конце строки, то с появлением Win32 к этому добавилась трансформация двухбайтной кодировки Unicode в ANSI и наоборот. (Об этом подробно говорилось в статье «Особенности работы со строковыми переменными в VB», КомпьютерПресс 10’99 и 01’2000). Сейчас же только отметим, что с помощью конструкции ByVal ... As String можно обмениваться строками только с символьными данными.

    В) ... As Any

    Это означает, что в стек будет помещен некоторый адрес буфера памяти, интерпретация содержимого которого будет выполняться API-функцией, например, в зависимости от значения других аргументов. Однако As Any может использоваться только в операторе Declare - при конкретном обращении к функции в качестве аргумента должна быть определена конкретная переменная.

    Г) ... As UserDefinedType

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

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

Пример обращения к API-функции

Проиллюстрируем сказанное выше на примере использования двух полезных функций работы с файлами - lopen и lread, которые описываются следующим образом:

Declare Function lopen Lib “kernel32” _ Alias “_lopen” (_ ByVal lpFileName As String, _ ByVal wReadWrite As Long) As Long Declare Function lread Lib “kernel32” _ Alias “_lread” (_ ByVal hFile As Long, lpBuffer As Any, _ ByVal wBytes As Long) As Long

В VB их аналогами - в данном случае точными - являются операторы Open и Get (для режима Binary). Обратим сразу внимание на использование ключевого слова Alias в объявлении функции - это как раз тот случай, когда без него не обойтись. Настоящие названия функции в библиотеке начинаются с символа подчеркивания (типичный стиль для языка C), что не разрешается в VB.

Операция открытия файла может выглядеть следующим образом:

Const INVALID_HANDLE_VALUE = -1 ‘ неверное _ значение описателя lpFileName$ = “D:\calc.bas” ‘ имя файла wReadWrite& = 2 ‘ режим “чтения-записи” hFile& = lopen(lpFileName$, wReadWrite&) _ ‘ определяем описатель файла If hFile& = INVALID_HANDLE_VALUE Then _ ‘ ошибка открытия файла ‘ уточняем код ошибки CodeError& = Err.LastDllError ‘CodeError& = GetLastError _ ‘ эта конструкция не работает End If

Здесь нужно обратить внимание на два момента:

  • в качестве значения функции мы получаем значение описателя файла. Ошибке соответствует значение –1;
  • как раз в данном случае не срабатывает обращение к функции GetLastError - для получения уточненного значения ошибки мы обратились к объекту Err (о возможности такой ситуации мы говорили выше).

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

Dim MyVar As Single wBytes = lread (hFile&, MyVar, Len(MyVar) ‘ чтение вещественного числа, 4 байта ‘ wBytes - число фактически прочитанных данных, ‘ -1 - ошибка... Type MyStruct x As Single i As Integer End Type Dim MyVar As MyStruct wBytes = lread (hFile&, MyVar, Len(MyVar)) ‘ чтение структуры данных, 6 байтов

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

Dim MyVar As String MyVar = Space$(10) ‘резервируем переменную для 10 символов wBytes = lread (hFile&, ByVal MyVar, Len(MyVar)) ‘ чтение символьной строки, 10 символов

Здесь видно важное отличие от приведенного ранее примера - строковая переменная обязательно сопровождается ключевым словом ByVal.

Чтение содержимого файла в массиве (для простоты будем использовать одномерный байтовый массив) выполняется следующим образом:

Dim MyArray(1 To 10) As Byte wBytes = lread (hFile&, MyArray(1), _ Len(MyArray(1))* 10) ‘ чтение 10 элементов массива

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

WBytes = lread (hFile&, MyArray(4), _ Len(MyArray(1))* 5) ‘ чтение элементов массива с 4-го по 8-й

Совет 5. Используйте Alias для передач и параметров As Any

Здесь на основе предыдущего примера мы раскроем суть четвертого совета Дэна Эпплмана.

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

Declare Function lreadString Lib “kernel32” _ Alias “_lread” (_ ByVal hFile As Long, ByVal lpBuffer As String, _ ByVal wBytes As Long) As Long

При работе с этим описанием указывать ByVal при обращении уже не нужно:

WBytes = lreadString (hFile&, MyVarString, _ Len(MyVarString)) ‘

Казалось бы, синтаксис оператора Declare позволяет сделать подобное специальное описание для массива:

Declare Function lreadString Lib “kernel32” Alias “_lread” (_ ByVal hFile As Long, lpBuffer() As Byte, _ ByVal wBytes As Long) As Long

Однако обращение

WBytes = lreadArray (hFile&, MyArray(), 10)

неизбежно приводит к фатальной ошибке программы.

Это продолжение разговора об особенностях обработки строковых переменных в Visual Basic: VB использует двухбайтную кодировку Unicode, Win API - однобайтную ANSI (причем с форматом, принятым в С, - с нулевым байтом в конце). Соответственно при использовании строковых переменных в качестве аргумента всегда автоматически производится преобразование из Unicode в ANSI при вызове API-функции (точнее, DLL-функции) и обратное преобразование при возврате.

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

Как известно, тип String можно использовать для описания пользовательской структуры. В связи с этим нужно помнить следующее:

  • Категорически нельзя использовать для обращения к Win API конструкцию следующего вида: Type MyStruct x As Single s As String ‘ строка переменной длины End Type

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

  • Можно использовать в качестве элемента структуры строку фиксированной длины: Type MyStruct x As Single s As String*8 ‘ строка фиксированной длины End Type

При этом производится соответствующее преобразование кодировок.

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

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

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

Мнение автора таково: сколь-нибудь серьезное занятие программированием требует от разработчика владения по крайней мере двумя инструментами. Разумеется, в современных условиях четкого разделения труда очень сложно быть отличным экспертом даже по двум системам, поэтому более логичной является схема «основной и вспомогательный языки». Идея здесь заключается в том, что даже поверхностное знание «вспомогательного» языка (написание довольно простых процедур) может очень заметно повысить эффективность применения «основного». Отметим, что знание VB хотя бы в качестве вспомогательного является сегодня практически обязательным требованием для профессионального программиста. Кстати, во времена DOS для любого программиста, в том числе Basic, было крайне желательным знание основ Ассемблера.

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

При изучении межпроцедурного интерфейса следует обратить внимание на следующие возможные «подводные камни»:

  • Разные языки могут использовать различные соглашения о правилах написания идентификаторов. Например, часто используется знак подчеркивания в начале имени процедуры, что запрещено в VB. Эта проблема легко решается с помощью ключевого слова Alias в операторе Declare (см. пример совета 2.3).
  • Может быть использована разная последовательность записи передаваемых аргументов в стек. Например, во времена DOS (честно признаюсь - не знаю, как это выглядит сейчас в среде Windows), C записывал аргументы с конца списка, другие языки (Fortran, Pascal, Basic) - с начала.
  • По умолчанию используются разные принципы передачи параметров - по ссылке или по значению.
  • Различные принципы хранения строковых переменных. Например, в C (так же как в Fortran и Pascal) длина строки определяется нулевым байтом в ее конце, а в Basic длина записывается в явном виде в дескрипторе строки. Разумеется, нужно иметь в виду возможность использования разных кодировок символов.
  • При передаче многомерных массивов следует помнить, что возможны различные варианты преобразования многомерных структур в одномерные (начиная с первого индекса или с последнего, применительно к двухмерным массивам - «по строчкам» или «по столбцам»).

С учетом всего этого можно сформулировать следующие рекомендации:

  • Используйте самые простые, проверенные способы передачи аргументов в DLL-функции. Стандарты, принятые для Win API, вполне годятся в качестве образца.
  • Ни в коем случае не передавайте массивы строковых переменных.
  • Очень внимательно используйте передачу простых строковых переменных и многомерных массивов.
  • Обязательно специальным образом проверяйте работоспособность механизма передачи аргументов в вызываемую процедуру и обратно. Напишите специальный тест для проверки передачи данных. Отдельно проверьте правильность передачи каждого аргумента. Например, если у вас есть процедура с несколькими аргументами, проверьте сначала корректность передачи каждого параметра для варианта с одним аргументом, а уж потом - для всего списка.

А что делать, если DLL-функция уже написана, например, на Фортране, но ее входной интерфейс не очень хорошо вписывается в приведенные выше стандарты VB? Здесь можно дать два совета. Первый: напишите тестовую DLL-функцию и с ее помощью постарайтесь методом проб и ошибок подобрать нужное обращение из VB-программы. Второй: напишите процедуру-переходник на том же Фортране, который бы обеспечивал простой интерфейс между VB и DLL-функцией с преобразованием простых структур данных в сложные (например, преобразовывал многомерный байтовый массив в строковый массив).

Итак: используйте DLL-функции. Но сохраняйте бдительность...

КомпьютерПресс 9"2000


Windows (правда, это не относится к Windows 9x и Windows СЕ) сертифицирована Управлением национальной безопасности (National Security Agency, NSA) как система, обеспечивающая уровень безопасности С2.

Применение большинства других ОС, отличных от UNIX, Linux и Windows, ограничивается только системами, предоставляемыми единственным поставщиком.

Операционные системы семейства Windows предлагают ряд возможностей, которые в стандартной системе UNIX отсутствуют, хотя и могут быть доступными в некоторых реализациях. В качестве примера можно привести систему безопасности уровня С2, а также службы NT Services.

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

Windows, стандарты и открытые системы

Эта книга посвящена разработке приложений с использованием Windows API. Вполне естественно, что у программистов, воспитанных на UNIX и открытых системах, могут возникнуть следующие вопросы: "Является ли Windows открытой системой?", "Представляет ли собой Windows промышленный стандарт?", "Не является ли Windows всего лишь очередным патентованным API?" Ответы на эти вопросы во многом зависят от того, что именно понимается под определениями открытая (open), промышленный стандарт (industry standard) или патентованный (proprietary), а также от того, какие преимущества ожидаются от использования открытых систем.

Windows API полностью отличается от API стандарта POSIX, поддерживаемого системами Linux и UNIX. Windows не подчиняется стандарту Х/Open, как не подчиняется и никакому другому открытому промышленному стандарту из тех, которые были предложены соответствующими органами стандартизации или промышленными консорциумами.

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

Унифицированные реализации быстрее достигают рынка.

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

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

Базовая аппаратная платформа является открытой. Разработчики могут выбирать любого из многочисленных поставщиков платформ по своему усмотрению.

Жаркие споры относительно того, к добру ли такая ситуация для пользователей и компьютерной индустрии в целом, или она только вредит общему делу, еще не закончились. Мы не будем пытаться участвовать в этом споре; задача данной книги состоит лишь в том, чтобы помочь разработчикам приложений как можно скорее приступить к работе в Windows.

В действительности системы Windows поддерживают многие важные стандарты. Так, Windows поддерживает стандартные библиотеки С и С+ и целый ряд открытых стандартов межплатформенного взаимодействия. В качестве примера можно привести сокеты Windows (Windows Sockets), предоставляющие стандартный интерфейс сетевого программирования, который обеспечивает возможность использования TCP/IP и других сетевых протоколов и тем самым открывает возможности доступа в Internet и взаимодействия с системами, не принадлежащими семейству Windows. To же самое остается справедливым и по отношению к протоколу удаленного вызова процедур (Remote Procedure Calls, RPC). Системы самой различной природы могут связываться с высокоуровневыми системами управления базами данных (СУБД) при помощи языка структурированных запросов (SQL). Наконец, в общий круг предложений Windows входит поддержка Internet, обеспечиваемая Web-серверами и серверам иного рода. Windows поддерживает такие ключевые стандарты, как TCP/IP, а на активно действующем рынке поставщиков решений Windows вам предлагают приобрести за разумную плату множество других ценных дополнительных продуктов, в том числе клиенты и серверы X Window.

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

Библиотеки совместимости

Несмотря на наличие библиотек совместимости (compatibility libraries), ими пользуются очень редко. Существуют две возможности.

В системах на основе UNIX, Linux, Macintosh и некоторых других может быть развернута одна из библиотек совместимости Windows, например, эмулятор Windows с открытым исходным кодом Wine, что обеспечивает переносимость исходного кода из Windows.

За счет использования программного обеспечения с открытым исходным кодом и набора инструментальных средств Windows Resource Kit компании Microsoft поверх подсистемы Windows может быть развернута библиотека совместимости POSIX. Весьма ограниченная по своим возможностям библиотека совместимости входит в состав среды визуальной разработки при ложений Microsoft Visual C++.

Таким образом, имеется, пусть даже и редко используемая, возможность выбора одного API и развертывания разработанных с его помощью переносимых приложений на системах Windows, POSIX и даже Macintosh.

Принципы, лежащие в основе Windows

Полезно никогда не забывать о некоторых базовых принципах Windows. В Windows API имеется множество как самых незаметных, так и значительных отличий от других API, таких как POSIX API, с которым знакомы программисты, работающие в UNIX и Linux. И хотя с применением Windows не связаны какие-либо специфические трудности в работе, она потребует от вас внесения некоторых изменений в привычные стиль и методику программирования.

Ниже описаны некоторые из важнейших характеристик Windows, с которыми вы ближе познакомитесь по мере дальнейшего изложения материала.

Многие системные ресурсы Windows представляются в виде объектов ядра (kernel objects), для идентификации и обращения к которым используются дескрипторы (handles). По смыслу эти дескрипторы аналогичны дескрипторам (descriptors) файлов и идентификаторам (ID) процессов в UNIX.

Любые манипуляции с объектами ядра осуществляются только с использованием Windows API. "Лазеек" для обхода этого правила нет. Подобная организация работы согласуется с принципами абстрагирования данных, используемыми в объектно-ориентированном программировании, хотя сама система Windows объектно-ориентированной не является.

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

Windows - богатый возможностями и гибкий интерфейс. Во-первых, одни и те же или аналогичные задачи могут решаться с помощью сразу нескольких функций; так, имеются вспомогательные функции (convenience functions), полученные объединением часто встречающихся последовательностей функциональных вызовов в одну функцию (к числу подобных функций принадлежит и функция CopyFile, используемая в одном из примеров далее в этой главе). Во-вторых, функции часто имеют многочисленные параметры и флаги, многие из которых обычно игнорируются. Данная книга не претендует на роль энциклопедического справочника, и основное внимание в ней концентрируется лишь на наиболее важных функциях и параметрах.

Windows предлагает многочисленные механизмы синхронизации и взаимодействия, обеспечивающие удовлетворение самых разнообразных запросов.

Базовой единицей выполнения в Windows является поток (thread). В одном процессе (process) могут выполняться один или несколько потоков.

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

Существует также несколько соглашений, регулирующих порядок использования имен типов:

Имена предопределенных типов данных, необходимых API, также являются описательными, и в них должны использоваться прописные буквы.

К числу наиболее распространенных относятся следующие типы данных:

BOOL (определен как 32-битовый объект, предназначенный для хранения одного логического значения)

DWORD (вездесущее 32-битовое целое без знака)

LPTSTR (указатель на строку, состоящую из 8– или 16-битовых символов)

LPSECURITY_ATTRIBUTES

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

В именах предопределенных типов указателей операция * не используется, и они отражают дополнительные отличия между указателями различного типа, как, например, в случае типов LPTSTR (определен как TCHAR *) и LPCTSTR (определен как const TCHAR *). Примечание. Тип TCHAR может обозначать как обычный символьный тип char, так и двухбайтовый тип wchar_t.

В отношении использования имен переменных, - по крайней мере, в прототипах функций, - также имеются определенные соглашения. Так, имя lpszFileName соответствует "длинному указателю на строку, завершающуюся нулевым символом", которая содержит имя файла. Этот пример иллюстрирует применение так называемой "венгерской нотации", которой мы в данной книге, как правило, не стремимся придерживаться. Точно так же, dwAccess - двойное слово (32 бита), содержащее флаги прав доступа к файлу, где "dw" означает "double word" - "двойное слово".

Примечание

Будет очень полезно, если вы просмотрите системные заголовочные (включаемые) файлы, в которых содержатся определения функций, констант, флагов, кодов ошибок и тому подобное. Многие из представляющих для нас интерес файлов, аналогичных тем, которые предложены ниже в качестве примера, являются частью среды Microsoft Visual C++ и обычно устанавливаются в каталоге Program Files\Microsoft Visual Studio.NET\Vc7\PlatformSDK\Include (или Program Files\Microsoft Visual Studio\VC98\Include в случае VC++ 6.0):

WINDOWS.H (файл, обеспечивающий включение всех остальных заголовочных файлов)

Наконец, несмотря на то что оригинальный API Win32 с самого начала разрабатывался как совершенно независимый интерфейс, он проектировался с учетом обеспечения обратной совместимости с API Winl6, входившим в состав Windows 3.1. Это привело к некоторым досадным с точки зрения программиста последствиям:

В названиях типов встречаются элементы анахронизма, как, например, в случае типов LPTSTR и LPDWORD, ссылающихся на "длинный указатель", который является простым 32– или 64-битовым указателем. Необходимость в указателях какого-либо иного типа отсутствует. Иногда составляющая "длинный" опускается, и тогда, например, типы LPVOID и PVOID являются эквивалентными.

В имена некоторых символических констант, например WIN32_FIND_DATA, входит компонент "WIN32", хотя те же константы используются и в Win64.

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

Подготовка к работе с Win64

Интерфейс Win64, который во время написания данной книги поддерживался Windows XP и Windows Server 2003 на процессорах семейства AMD64 (Opteron и Athlon 64) компании AMD и процессорах семейства Itanium (ранее известных под кодовыми названиями Merced, McKinley, Madison и IA-64) компании Intel, будет играть все более важную роль при создании крупных приложений. Существенные отличия между Win32 и Win64 обусловлены различиями в размере указателей (64 бита в Win64) и объеме доступного виртуального адресного пространства.

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

С точки зрения программиста основные отличия при переходе к Win64 обусловлены размерами указателей и необходимостью помнить о том, что длины указателя и целочисленной переменной (LONG, DWORD и так далее) не обязательно должны совпадать. С этой целью определены, например, типы DWORD32 и DWORD64, позволяющие явно управлять размером переменных. Два других типа, POINTER_32 и POINTER_64, позволяют управлять размером указателей.

Как вы сами убедитесь, приложив лишь самые незначительные усилия, можно добиться того, чтобы программы работали как в Win32, так и в Win64, и поэтому мы будем часто ссылаться на API просто как на Windows или, иногда, Win32. Дополнительная информация относительно Win64 содержится в главе 16, где, в частности, обсуждаются вопросы совместимости исходных и двоичных кодов.

Программисты, работающие с UNIX и Linux, столкнутся в Windows с рядом интересных особенностей. Так, в Windows дескрипторы HANDLE являются "непрозрачными". Они не представляют собой ряд последовательно возрастающих целых чисел. В то же время, например, в UNIX дескрипторы файлов 0, 1 и 2 имеют специальное назначение, что должно обязательно учитываться при написании программ. Ничего подобного в Windows вы не обнаружите.

Многие из различий, например грань между идентификаторами процессов и дескрипторами файлов, в Windows оказываются стертыми. В Windows объекты обеих типов описываются дескрипторами типа HANDLE. Во многих важных функциях могут наравне использоваться дескрипторы файлов, процессов, событий, каналов и других объектов.

Программистам, которые, работая в UNIX, привыкли к коротким именам функций и параметров и использовали преимущественно строчные буквы, придется приспосабливаться к более пространному стилю Windows. Стиль Windows близок к стилю интерфейса компании Hewlett Packard (ранее - DEC и Compaq); программистам, работающим с OpenVMS, многое покажется знакомым. Указанное сходство между OpenVMS и Windows частично объясняется тем, что Дэвид Катлер (David Cutler), создатель первоначальной архитектуры VMS, предполагал, что она должна играть ту же роль, что и NT или Windows.

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

В завершение следует отметить, что в текстовых файлах Windows конец строки отмечается последовательностью управляющих символов CR-LF, а не LF, как в это принято в UNIX.

О целесообразности привлечения функций стандартной библиотеки C для обработки файлов

Несмотря на всю уникальность возможностей Windows, старый добрый язык С и его стандартная библиотека ANSI С по-прежнему могут с успехом использоваться при решении большинства задач, связанных с обработкой файлов. Кроме того, библиотека С (указание на ее соответствие стандарту ANSI С мы будем часто опускать) содержит большое число очень нужных функций, аналогов которых среди системных вызовов нет. К их числу относятся, например, функции, описанные в заголовочных файлах , и , а также функции форматированного и символьного ввода/вывода. В то же время, имеются и такие функции, как fopen и fread, описанные в заголовочном файле , для которых находятся близко соответствующие им системные вызовы.

В каких же случаях при обработке файлов можно обойтись библиотекой С, а в каких необходимо использовать системные вызовы Windows? Тот же вопрос можно задать и в отношении использования потоков (streams) ввода/вывода C++ или системы ввода/вывода, которая предоставляется платформой.NET. Простых ответов на эти вопросы не существует, но если во главу угла поставить переносимость программ на платформы, отличные от Windows, то в тех случаях, когда приложению требуется только обработка файлов, а не, например, управление процессами или другие специфические возможности Windows, предпочтение следует отдавать библиотеке С и потокам ввода/вывода C++. Вместе с тем, многими программистами ранее уже делались попытки выработать рекомендации относительно адекватности использования библиотеки С в тех или иных случаях, и эти же рекомендации должны быть применимы и в отношении Windows. Кроме того, с учетом возможностей расширения функциональности, а также повышения производительности и гибкости программ, обеспечиваемые Windows, нередко оказывается более удобным или даже необходимым не ограничиваться библиотекой С, в чем вы постепенно станете убеждаться уже начиная с главы 3. К числу возможностей Windows, не поддерживаемых библиотекой С, относятся блокирование и отображение файлов (необходимое для разделения общих областей памяти), асинхронный ввод/вывод, произвольный доступ к файлам чрезвычайно крупных размеров (4 Гбайт и выше) и взаимодействие между процессами.

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

Что требуется для работы с данной книгой

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

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

Система с установленной ОС Windows.

Компилятор С и любая подходящая среда разработки приложений, например, Microsoft Visual Studio .NET или Microsoft Visual C++ версии 6.0. Имеются также системы разработки приложений от других поставщиков, и хотя примеры из книги нами на них не тестировались, из поступивших от нескольких читателей писем нам стало известно, что примеры, пусть даже после внесения в них незначительных изменений, в некоторых случаях успешно выполнялись даже при использовании других систем. Кроме того, в приложении А содержится информация, касающаяся использования инструментальных средств с открытым исходным кодом. Примечание. Наше внимание будет сосредоточено на разработке консольных приложений Windows, и поэтому возможности Microsoft Visual Studio .NET будут задействованы далеко не в полной мере.

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

Привод компакт-диска, системного или сетевого, для установки среды разработки приложений.

Оперативная документация наподобие той, которая поставляется вместе с Microsoft Visual C++. Желательно, чтобы вы установили эту документацию на своем жестком диске, поскольку к ней будет требоваться частый доступ. Дополнительную информацию вы всегда сможете получить на Web-сайте компании Microsoft.

Пример: простое последовательное копирование файла

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

1. С использованием библиотеки С.

2. С использованием Windows.

3. С использованием вспомогательной функции Windows - CopyFile.

Кроме того, что эти примеры дают возможность сопоставить между собой различные модели программирования, они также демонстрируют возможности и ограничения, присущие библиотеке С и Windows. Альтернативные варианты реализации усилят программу, увеличивая ее производительность и повышая гибкость.

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

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

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

Само по себе копирование файлов не представляет особого интереса, однако сравнение программ не только позволит вам быстро оценить, чем отличаются друг от друга различные системы, но и послужит хорошим предлогом для знакомства с Windows. В последующих примерах реализуется ограниченный вариант одной из команд UNIX - cp, осуществляющей копирование одного файла в другой и требующей задания имен файлов в командной строке. В приведенных программах организована лишь простейшая проверка ошибок, которые могут возникать на стадии выполнения, а существующие файлы просто перезаписываются. Эти и другие недостатки будут учтены в последующих Windows-реализациях этой и других программ. Примечание. Реализация программы для UNIX находится на Web-сайте книги.

Копирование файлов с использованием стандартной библиотеки С

Как видно из текста программы 1.1, стандартная библиотека С поддерживает объекты потоков ввода/вывода FILE, которые напоминают, несмотря на меньшую общность, объекты Windows HANDLE, представленные в программе 1.2.

Программа 1.1. срC: копирование файлов с использованием библиотеки С
/* Глава 1. Базовая программа копирования файлов cp. Реализация, использующая библиотеку С. */
/* cp файл1 файл2: Копировать файл1 в файл2. */

int main(int argc, char *argv) {
printf("Использование: срС файл1 файл2\n");
in_file = fopen(argv , "rb");
out_file = fopen(argv , "wb");
/* Обработать входной файл по одной записи за один раз. */
while ((bytes_in = fread(rec, 1, BUF_SIZE, in_file)) > 0) {
bytes_out = fwrite(rec, 1, bytes_in, out_file);
perror("Неустранимая ошибка записи.");

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

1. Объекты открытых файлов идентифицируются указателями на структуры FILE (в UNIX используются целочисленные дескрипторы файлов). Указателю NULL соответствует несуществующий объект. По сути, указатели являются разновидностью дескрипторов объектов открытых файлов.

2. В вызове функции fopen указывается, каким образом должен обрабатываться файл - как текстовый или как двоичный. В текстовых файлах содержатся специфические для каждой системы последовательности символов, используемых, например, для обозначения конца строки. Во многих системах, включая Windows, в процессе выполнения операций ввода/вывода каждая из таких последовательностей автоматически преобразуется в нулевой символ, который интерпретируется в языке С как метка конца строки, и наоборот. В нашем примере оба файла открываются как двоичные.

3. Диагностика ошибок реализуется с помощью функции perror, которая, в свою очередь, получает информацию относительно природы сбоя, возникающего при вызове функции fopen, из глобальной переменной errno. Вместо этого можно было бы воспользоваться функцией ferror, возвращающей код ошибки, ассоциированный не с системой, а с объектом FILE.

4. Функции fread и fwrite возвращают количество обработанных байтов непосредственно, а не через аргумент, что оказывает существенное влияние на логику организации программы. Неотрицательное возвращаемое значение говорит об успешном выполнении операции чтения, тогда как нулевое - о попытке чтения метки конца файла.

5. Функция fclose может применяться лишь к объектам типа FILE (аналогичное утверждение справедливо и в отношении дескрипторов файлов UNIX).

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

7. Для вывода сообщений об ошибках удобно использовать входящую в библиотеку С функцию ввода/вывода printf, которая даже будет использована в первом примере Windows-программы.

Преимуществом реализации, использующей библиотеку С, является ее переносимость на платформы UNIX, Windows, а также другие системы, которые поддерживают стандарт ANSI С. Кроме того, как показано в приложении В, в том, что касается производительности, вариант, использующий функции ввода/вывода библиотеки С, ничуть не уступает другим вариантам реализации. Тем не менее, в этом случае программы вынуждены ограничиваться синхронными операциями ввода/вывода, хотя влияние этого ограничения будет несколько ослаблено использованием потоков Windows (начиная с главы 7).

Как и их эквиваленты в UNIX, программы, основанные на функциях для работы с файлами, входящих в библиотеку С, способны выполнять операции произвольного доступа к файлам (с использованием функции fseek или, в случае текстовых файлов, функций fsetpos и fgetpos), но это является уже потолком сложности для функций ввода/вывода стандартной библиотеки С, выше которого они подняться не могут. Вместе с тем, Visual C++ предоставляет нестандартные расширения, способные, например, поддерживать блокирование файлов. Наконец, библиотека С не позволяет управлять средствами защиты файлов.

Резюмируя, можно сделать вывод, что если простой синхронный файловый или консольный ввод/вывод - это все, что вам надо, то для написания переносимых программ, которые будут выполняться под управлением Windows, следует использовать библиотеку С.

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

В программе 1.2 решается та же задача копирования файлов, но делается это с помощью Windows API, а базовые приемы, стиль и соглашения, иллюстрируемые этой программой, будут использоваться на протяжении всей этой книги.

Программа 1.2. cpW: копирование файлов с использованием Windows, первая реализация
/* Глава 1. Базовая программа копирования файлов cp. Реализация, использующая Windows. */
/* cpW файл1 файл2: Копировать файл1 в файл2. */

printf ("Использование: cpW файл1 файл2\n");
hIn = CreateFile(argv , GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hIn == INVALID_HANDLE_VALUE) {
printf("Невозможно открыть входной файл. Ошибка: %х\n", GetLastError());
hOut = CreateFile(argv, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hOut == INVALID_HANDLE_VALUE) {
printf("Невозможно открыть выходной файл. Ошибка: %x\n", GetLastError());
while (ReadFile(hIn, Buffer, BUF_SIZE, &nIn, NULL) && nIn > 0) {
WriteFile(hOut, Buffer, nIn, &nOut, NULL);
printf ("Неустранимая ошибка записи: %x\n", GetLastError());

Этот простой пример иллюстрирует некоторые особенности программирования в среде Windows, к подробному рассмотрению которых мы приступим в главе 2.

1. В программу всегда включается файл , в котором содержатся все необходимые определения функций и типов данных Windows.

2. Все объекты Windows идентифицируются переменными типа Handle, причем для большинства объектов можно использовать одну и ту же общую функцию CloseHandle.

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

4. Windows определяет многочисленные символические константы и флаги. Обычно они имеют длинные имена, нередко поясняющие назначение данного объекта. В качестве типичного примера можно привести имена INVALID_HANDLE_VALUE и GENERIC_READ.

5. Функции ReadFile и WriteFile возвращают булевские значения, а не количества обработанных байтов, для передачи которых используются аргументы функций. Это определенным образом изменяет логику организации работы циклов. Нулевое значение счетчика байтов указывает на попытку чтения метки конца файла и не считается ошибкой.

6. Функция GetLastError позволяет получать в любой точке программы коды системных ошибок, представляемые значениями типа DWORD. В программе 1.2 показано, как организовать вывод генерируемых Windows текстовых сообщений об ошибках.

7. Windows NT обладает более мощной системой защиты файлов, описанной в главе 15. В данном примере защита выходного файла не обеспечивается.

8. Такие функции, как CreateFile, обладают богатым набором дополнительных параметров, но в данном примере использованы значения по умолчанию.

Копирование файлов с использованием вспомогательной функции Windows

Для повышения удобства работы в Windows предусмотрено множество вспомогательных функций (convenience functions), которые, объединяя в себе несколько других функций, обеспечивают выполнение часто встречающихся задач программирования. В некоторых случаях использование этих функций может приводить к повышению производительности (см. приложение В). Например, благодаря применению функции CopyFile значительно упрощается программа копирования файлов (программа 1.3). Помимо всего прочего, это избавляет нас от необходимости заботиться о буфере, размер которого в двух предыдущих программах произвольно устанавливался равным 256.

Программа1.3.cpCF: копирование файлов с использованием вспомогательной функции Windows
/* Глава 1. Базовая программа копирования файлов cp. Реализация, в которой для повышения удобства использования и производительности программы используется функция Windows CopyFile. */
/* cpCF файл1 файл2: Копировать файл1 в файл2. */

int main (int argc, LPTSTR argv ) {
printf ("Использование: cpCF файл1 файл2\n");
if (!CopyFile(argv, argv, FALSE)) {
printf("Ошибка при выполнении функции CopyFile: %x\n", GetLastError());

Резюме

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

Целевыми платформами для данной книги и содержащихся в ней примеров являются системы NT5 (Windows XP, 2000 и Server 2003). Тем не менее, большая часть материала книги применима также к ранним версиям NT и системам Windows 9x (95, 98 и Me).

В следующих главах

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

Дополнительная литература

Win32

Двумя доступными в настоящее время книгами, в которых вопросы программирования для Windows рассматриваются с всех возможных точек зрения, являются и . В то же время, существует множество других книг, которые не обновлялись и не отражают прогресс, достигнутый с момента выхода Windows 95 или Windows NT.

По каждой функции Microsoft Visual C++ имеется оперативная гипертекстовая справочная документация, но ту же информацию можно получить, посетив домашнюю страницу компании Microsoft - http://www.microsoft.com, где вы найдете целый ряд ссылок на технические статьи, посвященные различным аспектам Windows. Начните с раздела MSDN (Microsoft Developer"s Network) и произведите поиск по любой интересующей вас теме. Вы обнаружите огромное разнообразие официальной документации, описаний продуктов, примеров программного кода, а также другую полезную информацию.

Win64

Win64 обсуждается в нескольких книгах, но обширный материал по этой теме можно найти на домашней странице компании Microsoft.

Архитектура Windows NT и история ее развития

Читателям, которые хотят больше узнать о целях проектирования Windows NT или понять основные принципы, лежащие в основе ее архитектуры, будет полезна книга . В этой книге рассматриваются объекты, процессы, потоки, виртуальная память, ядро и подсистемы ввода/вывода. Вместе с тем, собственно функции API, а также Windows 9x и СЕ в ней не обсуждаются. Рекомендуем время от времени заглядывать в упомянутую книгу для получения дополнительной информации. Кроме того, обратитесь к ранее вышедшим книгам и , в которых содержится важный ретроспективный анализ эволюции NT.

UNIX

В книге , написанной ныне покойным Уильямом Ричардом Стивенсом (W. Richard Stevens), UNIX обсуждается во многом в тех же терминах, которые в настоящей книге используются для обсуждения Windows. Книга Стивенса по-прежнему остается стандартным справочником по средствам UNIX, но в ней не рассматриваются потоки. Стандартизация UNIX претерпела изменения, однако в книге Стивенса содержатся удобные рабочие определения всего того, что предлагается в UNIX, а также в Linux. В этой книге сопоставлены возможности функций файлового ввода/вывода библиотеки С и функций ввода/вывода системы UNIX, что имеет отношение и к Windows.

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

Программирование с использованием Windows GUI

Пользовательский интерфейс в настоящей книге не рассматривается. В случае необходимости можете обратиться к или .

Теория операционных систем

Существует масса хороших учебников по общей теории ОС. Одной из наиболее популярных является книга .

Стандартная библиотека ANSI С

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

Windows СЕ

Тем, кто хочет применить материал настоящей книги к Windows СЕ, можно порекомендовать книгу .

Эмуляция Windows в UNIX

Для получения необходимой информации по этому вопросу и загрузки пакета с открытым исходным кодом Wine, позволяющего эмулировать Windows API поверх UNIX и X, посетите сайт http://www.winehq.com.

Упражнения

1.1. Скомпилируйте, скомпонуйте и выполните каждую из трех программ, предназначенных для копирования файлов. К числу других возможных вариантов реализации относится использование библиотек совместимости с UNIX, включая библиотеку Microsoft Visual C++ (программа, использующая эту библиотеку, доступна на Web-сайте книги). Примечание. На Web-сайте книги на ходятся исходные коды всех программ. Краткие рекомендации относительно порядка использования этих кодов в средах Microsoft Visual Studio .NET и Microsoft Visual C++ 6.0 вы найдете в приложении А.

1.2. Ознакомьтесь с одной из сред разработки приложений, например, Microsoft Visual Studio .NET или Microsoft Visual C++. В частности, научитесь создавать в выбранной среде консольные приложения. Для проведения самостоятельных экспериментов с использованием рассмотренных в данной главе программ пользуйтесь отладчиком. Инструкции относительно того, как следует приступать к работе, содержатся в приложении А, а обширную дополнительную информацию вы найдете на Web-сайте компании Microsoft и в документации к используемой вами среде разработки приложений.

1.3. В Windows в качестве метки конца строки используется последовательность символов "возврат каретки-перевод строки" (CR-LF). Определите, как изменится поведение программы 1.1, если входной файл открывать в двоичном режиме, а выходной - в текстовом, или наоборот. Как это будет проявляться в системе UNIX и в других системах?

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

Примечания:

Тем не менее, в тех местах книги, где речь идет о средствах, неприменимых в Windows 9х, делаются соответствующие оговорки.

Замечания, сделанные в адрес UNIX, в равной степени относятся также к Linux и некоторым другим системам, поддерживающим POSIX API.

Иногда, имея в виду в основном серверы, но не исключая и персональные приложения, говорят о возможной угрозе преобладанию Windows со стороны Linux. Хотя сама по себе эта тема является чрезвычайно интересной, размышления о путях будущего развития систем, не имеющие непосредственного отношения к рассмотрению сравнительных достоинств и недостатков Windows и Linux, выходят за рамки данной книги.

О том, насколько разнообразен круг систем, на которых может быть развернута Windows, говорит хотя бы тот факт, что диапазон компьютеров, использованных для тестирования приведенных в этой книге примеров программ, простирается от давно забытой 486-й модели с 16 Мбайт ОЗУ до четырехпроцессорного (процессоры Xeon с рабочей частотой 2 ГГц) сервера масштаба предприятия, оборудованного ОЗУ емкостью 8 Гбайт.

Протоколы Windows Sockets и RPC не являются частью самой Windows, что не воспрепятствовало описанию сокетов в данной книге, поскольку они самым непосредственным образом укладываются в рамки интересующей нас общей темы и используемого подхода.

Несмотря на аналогию между упомянутыми дескрипторами и дескрипторами HWND и HDC, используемыми при написании программ для Windows GUI, между ними существует ряд отличий.

Такие типы, как PVOID, входят в include-файлы без префикса, но в примерах мы будем придерживаться правил их употребления, принятых во многих книгах и документации Microsoft.

О том, какими быстрыми темпами улучшаются показатели стоимости и производительности, вы можете судить хотя бы по тому факту, что еще в 1997 году в первом издании этой книги автор, без тени смущения или неловкости, в качестве необходимых требований указывал 16 Мбайт ОЗУ и 256 Мбайт свободного места на жестком диске. Для написания настоящего, третьего издания книги используется лэптоп стоимостью менее $1000, с объемом ОЗУ в более чем 10 раз превышающим прежний (что больше ранее требуемого объема дискового пространства), 100-кратной емкостью жесткого диска и 50-кратным превышением быстродействия процессора по сравнению с аналогичными характеристиками компьютера стоимостью $2500, который использовался при подготовке первого издания.

В приложении А показано, как исключить ненужные определения для ускорения компиляции и экономии дискового пространства.

Обратите внимание на то, что логика цикла зависит от принятого в стандарте ANSI С порядка вычисления логических операций "и" (&&) и "или" (||) в направлении слева направо.

Интерфейс прикладного программирования WindowsAPI (applicationprogramminginterface) является интерфейсом системного программирования в пользовательском режиме для семейства операционных систем Windows. До выхода 64-разрядных версий Windows программный интерфейс для 32-разрядных версий операционных систем Windows назывался Win32 API, чтобы его можно было отличить от исходной 16-разрядной версии Windows API (которая служила интерфейсом программирования для начальных 16-разрядных версий Windows).

Windows API состоит из нескольких тысяч вызываемых функций, которые разбиты на следующие основные категории:

  • Базовыеслужбы (Base Services).
  • Службыкомпонентов (Component Services).
  • Службы пользовательского интерфейса (User Interface Services).
  • Графические и мультимедийные службы (Graphics and Multimedia Services).
  • Обмен сообщениями и совместная работа (Messaging and Collaboration).
  • Сеть (Networking).
  • Веб-службы (Web Services).

Описание WindowsAPI можно найти в документации по набору инструментальных средств разработки программного обеспечения - WindowsSoftwareDevelopmentKit (SDK). Эта документация доступна на веб-сайте www.msdn.microsoft.com. Она также включена со всеми уровнями подписки в сеть MicrosoftDeveloperNetwork (MSDN), предназначенную для разработчиков.

Microsoft .NET Framework состоит из библиотеки классов под названием Framework Class Library (FCL) и управляемой среды выполнения кода -Common Language Runtime (CLR). CLR обладает функциями своевременной компиляции, проверки типов, сборки мусора и обеспечения безопасности доступа к коду. Предлагая эти функции, CLR предоставляет среду разработки, повышающую производительность работы программистов и сокращающую количество наиболее распространенных ошибок программирования.

Среда CLR реализована, как классический COM-сервер, код которого находится в стандартной Windows DLL-библиотеке, предназначенной для работы в пользовательском режиме. Фактически все компоненты.NET Framework реализованы как стандартные Windows DLL-библиотеки пользовательского режима, наложенные поверх неуправляемых функций Windows API. (Ни один из компонентов.NET Framework не работает в режиме ядра.) Взаимоотношения между этими компонентами показаны на рисунке.

Службы, функции и стандартные программы.

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

  • Функции Windows API . Документированные, вызываемые подпрограммы в WindowsAPI. Например, CreateProcess, CreateFile и GetMessage.
  • Собственные системные службы (или системные вызовы) . Недокументированные базовые службы в операционной системе, вызываемые при работе в пользовательском режиме. Например, NtCreateUserProcess является внутренней службой, которую функция Windows CreateProcess вызывает для создания нового процесса.
  • Функции поддержки ядра (или подпрограммы) . Подпрограммы внутри операционной системы Windows, которые могут быть вызваны только из режима ядра. Например, ExAllocatePoolWithTag является подпрограммой, вызываемой драйверами устройств для выделения памяти из системных динамически распределяемых областей Windows (называемых пулами).
  • Службы Windows. Процессы, запускаемые Диспетчером управления службами (Windowsservicecontrolmanager). Например, служба Диспетчер задач запускается в виде процесса, работающего в пользовательском режиме, в котором поддерживается команда at (аналогичная UNIX-командам at или cron).
  • Библиотеки DLL (dynamic-link libraries - динамически подключаемые библиотеки) . Набор вызываемых подпрограмм, связанных вместе в виде двоичного файла, который может быть загружен в динамическом режиме приложениями, которые используют эти подпрограммы. В качестве примера можно привести Msvcrt.dll (библиотеку времени выполнения для приложений, написанных на языке C) и Kernel32.dll (одну из библиотек подсистемы Windows API). DLL-библиотеки широко используются компонентами и приложениями Windows, которые работают в пользовательском режиме. Преимущество, предоставляемое DLL-библиотеками по сравнению со статическими библиотеками, заключается в том, что они могут использоваться сразу несколькими приложениями, и Windows обеспечивает наличие в памяти только одной копии кода DLL-библиотеки для тех приложений, в которых имеются ссылки на эту библиотеку. Следует заметить, что неисполняемые.NET-сборки компилируются как DLL-библиотеки, но без каких-либо экспортируемых подпрограмм. CLR анализирует скомпилированные метаданные для доступа к соответствующим типам и элементам классов.

История Win32 API.

Интересно, что Win32 не планировался в качестве исходного интерфейса программирования для той системы, которая в ту пору называлась Windows NT. Поскольку проект Windows NT запускался в качестве замены для OS/2 версии 2, первоначальным интерфейсом программирования был 32-разрядный OS/2 PresentationManagerAPI. Но через год после запуска проекта произошел взлет поступившей в продажу Microsoft Windows 3.0. В результате этого Microsoft сменила направление и сделала Windows NT будущей заменой семейства продуктов Windows, а не заменой OS/2. В связи с этим и возникла необходимость выработать спецификацию Windows API - до этого, в Windows 3.0, API существовал только в виде 16-разрядного интерфейса.

Хотя в Windows API намечалось введение множества новых функций, недоступных в Windows 3.1, Microsoft решила сделать новый API, по возможности, максимально совместимым по именам, семантике и используемым типам данных с 16-разрядным Windows API, чтобы облегчить бремя переноса существующих 16-разрядных Windows-приложений в Windows NT. Этим, собственно, и объясняется тот факт, что многие имена функций и интерфейсов могут показаться непоследовательными: так нужно было для обеспечения совместимости нового Windows API со старым 16-разрядным Windows API.