Введение в Windows API
ИФ Гортис, Санкт-Петербург
API (Application Programming Interface) - это интерфейс программирования приложений, термин, часто упоминаемый разработчиками программного обеспечения. Если разрабатываемое вами приложение имеет функцию, позволяющую обращаться к нему из других приложений, то это - API вашего приложения. Параметры, которые принимает ваша функция, образуют её API, так как они являются средством, при помощи которого другие приложения взаимодействуют с данной функцией.
Операционная система Windows предоставляет большой набор функций, позволяющих различным приложениям, в том числе и приложениям Visual FoxPro, обмениваться информацией с Windows на достаточно низком уровне. Эти функции принято называть Windows API. Использование Windows API в приложениях Visual FoxPro позволяет реализовать возможности, недостижимые стандартными средствами языка.
Объявление Windows API функций в Visual FoxPro
Функции Windows API скомпонованы в динамически связанные библиотеки (Dynamic Link Library, DLL). Как правило, файлы таких библиотек имеют расширение dll. Перед тем, как использовать Windows API функцию в вашем приложении, вы должны её объявить. Для объявления функции применяется команда DECLARE..DLL:
DECLARE [cFunctionType] FunctionName IN LibraryName [AS AliasName] ; [cParamType1 [@] ParamName1, cParamType2 [@] ParamName2, ...]
Параметры команды:
cFunctionType
необязательный параметр, указывает тип данных, возвращаемых
функцией:
cFunctionType | Размер, байт |
Описание |
Short |
2 |
16-ти разрядное целое число |
Integer, Long | 4 | 32-х разрядное целое число |
Single | 4 | 32-х разрядное вещественное число |
Double | 8 | 64-х разрядное вещественное число |
String | - | Строка символов |
FunctionName
имя функции в DLL-библиотеке. Имя функции
чувствительно к регистру символов, то
есть GetDC и GETDC - это имена совершенно
разных функций.
LibraryName
наименование DLL-библиотеки, в которой
находится функция. Для библиотек Kernel32.dll,
Gdi32.dll, User32.dll, Mpr.dll и Advapi32.dll можно
использовать синоним WIN32API.
AliasName
необязательный параметр, позволяет вместо имени функции
использовать придуманный вами псевдоним.
Написание псевдонима, в отличие от имени
функции, не чувствительно к регистру
символов. Как правило, псевдоним
используется, когда имя API функции
совпадает с именем встроенной (или вашей)
функции Visual FoxPro.
cParamType
указывает тип данных передаваемого функции
значения:
cParamType | Размер, байт |
Описание |
Integer, Long | 4 | 32-х разрядное целое число |
Single | 4 | 32-х разрядное вещественное число |
Double | 8 | 64-х разрядное вещественное число |
String | - | Строка символов |
Параметр может передаваться как по значению, так и по ссылке. Для указания того, что параметр передаётся по ссылке, используется символ "@".
С точки зрения Visual FoxPro не существует никакой разницы между типами данных Long и Integer. Обычно тип Integer применяется для обозначения целых чисел со знаком, а тип Long - целых чисел без знака.
ParamName
необязательный параметр, носит чисто описательный характер и, как
правило, игнорируется.
Все функции Windows API, как, впрочем, и сама Windows, написаны на языке программирования Си. Поэтому для того, чтобы понять, как правильно использовать API функции в Visual FoxPro (который, кстати, так же написан на Си, по крайней мере, его ядро), познакомимся, какие типы данных применяются в Си и Windows, и, что не менее важно, разберёмся с такими типами данных, как перечисления, структуры и указатели. Кроме того, вы узнаете, что такое прототипы функций в Си, и как, основываясь на описании прототипа функции в MSDN, правильно объявить её в команде DECLARE..DLL.
Базовые типы данных Си
Если вы знакомы с языком программирования Си, то знаете, как легко в нём можно создавать различные типы данных. Достаточно написать следующий код:
typedef int INT32;
и вот у вас новый тип INT32, который полностью соответствует типу int. Но, с точки зрения Си, это совершенно разные типы, и попытка присвоить переменной типа INT32 значение переменной типа int приведёт к ошибке!
Изобилие типов данных заставляет многих разработчиков думать, что программирование с использованием API является трудным. Но это не так! В Си в основном используются следующие типы данных:
-
тип char - символ в формате ANSI. Имеет длину 8 разрядов (один байт).
-
тип wchar - символ в формате Unicode. Имеет длину 16 разрядов (два байта).
-
тип int - целые числа. Они делятся в Си на три типа: int, short int и long int. Последние обычно сокращаются до short и long. Тип short - это 16-ти разрядное, а типы int и long - 32-х разрядные целые числа.
-
тип float - вещественные числа, имеющие дробную часть. Имеют длину 32 разряда (4 байта).
-
тип double - вещественные числа двойной точности. Имеют длину 64 разряда (8 байт).
-
тип enum - перечисляемый тип данных.
-
тип void используется для обозначения величин, имеющих нулевую длину и не имеющих значения.
-
тип pointer - указатель; он не содержит информацию в общепринятом смысле - как другие типы Си; вместо этого, в каждом указателе находится адрес ячейки памяти, где хранятся реальные данные. Имеет длину 32 разряда (4 байта).
Как ни странно, строковый тип в Си отсутствует. На самом деле все строки представлены в Си как массивы символов.
Некоторые типы могут объявляться как беззнаковые. Модификатор unsigned (без знака) используется со следующими типами данных: char, short, int и long.
Например, следующее объявление переменной в Си:
usigned int имя_переменной;
означает, что эта переменная - целое 32-х разрядное целое без знака.
Модификатор const указывает, что переменная указанного типа является константой, то есть её значение не может быть изменено.
Перечисляемый тип enum связывает с переменной набор именованных констант, называемых перечисляемыми константами. Объявление перечисляемого типа выглядит так:
enum поле_тега { const1, const2, ... } переменная;
Если поле_тега опускается, то после закрывающей фигурной скобки необходимо указать переменную. Если поле_тега указано, то не указывается переменная.
Исторически сложилось так, что тип enum равнозначен типу int - то есть переменная перечисляемого типа занимает в памяти 4 байта. Каждая перечисляемая константа имеет значение, определяемое её порядковым номером в списке; нумерация начинается с нуля. Рассмотрим перечисление CombineMode:
enum CombineMode{ CombineModeReplace, CombineModeIntersect, CombineModeUnion, CombineModeXor, CombineModeExclude, CombineModeComplement };
В этом перечислении константа CombineModeReplace имеет значение 0, константа CombineModeIntersect имеет значение 1, и так далее; константа CombineModeComplement имеет значение 5.
Значения перечисляемых констант могут быть указаны явно, как, например, в следующем примере:
enum DashCap{ DashCapFlat = 0, DashCapRound = 2, DashCapTriangle = 3 };
Перечисленные типы данных покрывают 99% всех типов данных, используемых в программировании Windows API. Это звучит слишком просто, не так ли? Почему же описания API функций содержат все эти типы - HWND, HINSTANCE, POINT и им подобные?
Причиной тому является то, что Cи имеет особенность, называемую strict-typing. Однажды появившись, переменная одного типа может принимать только те значения, которые соответствуют ее типу. Вы не можете сначала сохранить в переменной строку, а затем присвоить ей число. В Visual FoxPro мы обычно стараемся симулировать подобное путем соглашения о наименованиях. Например, cName представляет собой переменную символьного типа, тогда как nCount - числовую. Strict-typing позволяет создать новый тип данных, присвоив существующему типу данных новое имя. Каждый новый тип представляется отличным от других типов, несмотря на то, что внутренне они хранят одно и то же.
Windows усложняет использование этой концепции. К примеру, тип LONG в действительности представляет собой long int, а тип UINT - unsigned int. Оба типа являются 32-разрядными целыми числами. Производные типы данных определяются в различных include-файлах (файлы с расширением .h). Если вы приобрели Visual Studio.NET, то можете найти эти файлы в папке ..\VC7\PlatformSDK\Include\.
Типы данных Windows
Определение того, какой из базовых типов Си действительно представляет тип данных, используемый в API функции, является одной из тяжелейших задач в программировании API. Используйте следующее правило: если вы не можете найти слово float, double, char или str где-либо в имени функции или параметра, тогда это обычно 32-разрядное целое. Потребуется некоторое время для понимания и выработки навыков, но потом вы будете запросто преобразовывать типы данных. В следующей таблице приведены основные типы данных Windows и соответствующие им типы, используемые при объявлении функции в Visual FoxPro:
Тип данных Windows |
Тип в
объявлении функции |
Описание |
BOOL | Long | 32-х разрядное целое число. 0 означает false, все остальное означает true. |
BOOLEAN | Long | тоже самое, что и BOOL. |
BYTE | String | 8-ми разрядное целое число |
CHAR | String | 8-ми разрядное целое число |
CLSID | String | 128-разрядное число (16 байт) |
COLORREF | Long | 32-х разрядное целое число |
DWORD | Long | 32-х разрядное целое число |
DOUBLE | Double | 64-х разрядное вещественное число |
FLOAT | Single | 32-х разрядное вещественное число |
GUID | String | 128-разрядное число (16 байт) |
HANDLE | Long | 32-х разрядное целое число без знака |
HBITMAP | Long | 32-х разрядное целое число без знака |
HDC | Long | 32-х разрядное целое число без знака |
HICON | Long | 32-х разрядное целое число без знака |
HGLOBAL | Long | 32-х разрядное целое число без знака |
HKL | Long | 32-х разрядное целое число без знака |
HLOCAL | Long | 32-х разрядное целое число без знака |
HINSTANCE | Long | 32-х разрядное целое число без знака |
HRESULT | Long | 32-х разрядное целое число без знака |
HWND | Long | 32-х разрядное целое число без знака |
LONG | Long | 32-х разрядное целое число |
LPARAM | Long | 32-х разрядное целое число без знака |
SHORT | Integer | 16-ти разрядное целое число |
SIZE_T | Long | 32-х разрядное целое число без знака |
TCHAR | String | Соответствует типу CHAR для строк формата ANSI и WCHAR для строк формата Unicode |
UCHAR | String | Символ в ANSI кодировке |
UINT | Long | 32-х разрядное целое число без знака |
ULONG | Long | 32-х разрядное целое число без знака |
USHORT | Integer | 16-ти разрядное целое число без знака |
UUID | String | 128-разрядное число (16 байт) |
VOID | нет | Не имеет значения |
WCHAR | String | UNICODE character |
WNDPROC | Long | 32-х разрядное целое число без знака |
WORD | Integer | 16-ти разрядное целое число без знака |
WPARAM | Long | 32-х разрядное целое число без знака |
Указатели
Другой концепцией, широко используемой в Си, являются указатели (pointers). Указатель представляет собой переменную, которая содержит адрес области памяти, по которому хранятся данные. Тип указателя всегда определяется типом данных, на которые он указывает; его размер всегда равен четырём байтам. Например, указатель на переменную типа SHORT представляет собой 32-разрядное целое число, как и указатель на любой другой тип данных. Описание указателя, принятое в программировании Windows API, начинается с символов "LP", что означет Long Pointer, или "длинный указатель", работающий с 32-х разрядной моделью памяти. Затем может следовать символ "C" (const), указывающий, что данные не должны изменяться. Далее следует описание типа данных переменной, адрес которой хранится в указателе. Например, LPDWORD - указатель на переменную типа DWORD.
Указатели на числовые данные при объявлении Windows API функции передаются по ссылке. Как пример рассмотрим функцию GetFileSize. Вот её прототип (подробнее о прототипах функций буде рассказано ниже):
DWORD GetFileSize( HANDLE hFile, // дескриптор файла LPDWORD lpFileSizeHigh // указатель );
Второй параметр, передаваемый функции - указатель на переменную типа DWORD, в которую функция поместит значение размера файла в байтах.
Объявление этой функции в Visual FoxPro:
DECLARE GetFileSize IN WIN32API Long hFile, Long @ FileSizeHight
Как видите, параметр FileSizeHight передаётся функции по ссылке, потому что передача по ссылке - это и есть передача указателя.
Сложнее обстоит дело со строками. Как уже говорилось, символьные строки в Cи - это массивы, поэтому в программировании API функций применяется тип str, определяющий массив символов типа CHAR (соответственно, тип wstr определяет массив символов типа WCHAR). В следующей таблице показаны типы указателей на символьные строки:
Тип
указателя на строку |
Описание |
LPSTR | Указатель на модифицируемую нуль-терминированную строку ANSI-формата. Передаётся по ссылке |
LPCSTR | Указатель на немодифицируемую нуль-терминированную строку ANSI-формата. Передаётся по значению |
LPTSTR | Соответствует типу LPSTR для строк формата ANSI и типу LPWSTR для строк формата UNICODE. Передаётся по ссылке. |
LPCTSTR | Соответствует типу LPSTR для строк формата ANSI и типу LPWSTR для строк формата UNICODE. Передаётся по значению. |
LPWSTR | Указатель на модифицируемую нуль-терминированную строку UNICODE. Передаётся по ссылке |
LPCWSTR | Указатель на немодифицируемую нуль-терминированную строку UNICODE. Передаётся по значению |
Указатели на символьные данные могут передаваться как по ссылке, так и по значению - тип String в объявлении функции в Visual FoxPro всегда передаёт указатель на переменную, содержащую символьные данные. Используйте передачу символьных данных по ссылке только тогда, когда API функция должна изменить значение параметра.
Структуры
Структуру можно рассматривать как набор переменных различных типов, образующих единое целое. В Си структура создаётся при помощи ключевого слова struct, за которым следует необязательное поле тега (tag) и список элементов структуры:
struct поле_тега { тип_элемента элемент1; тип_элемента элемент2; ..... тип_элемента элементN; };
Возможно и такое объявление структуры:
struct { тип_элемента элемент1; тип_элемента элемент2; ..... тип_элемента элементN; } переменная;
В этом объявлении отсутствует поле тега и создаётся так называемый анонимный структурный тип; такой синтаксис позволяет связать с этим структурным типом одну или несколько переменных, как, например, в следующем примере:
struct { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME;
Структуры очень похожи на записи таблиц Visual FoxPro. Так, если запись таблицы personal содержит поля fio, address, tlfnumber и email, то для обращения к полю tlfnumber используется следующий синтаксис:
personal.tlfnumber
Так же выглядит и обращение к полю структуры:
SYSTEMTIME.wMinute
Для формирования структур в Visual FoxPro используются переменные, содержащие строки символов. Например, для рассмотренной выше структуры SYSTEMTIME вам понадобится переменная длиной 16 байт. В первые два байта этой переменной заносится значение поля wYear, в следующие два байта - значение поля wMonth, в следующие два байта - значение поля wDayOfWeek, и так далее, пока структура не будет полностью сформирована. А при объявлении API функции в Visual FoxPro тип параметра, в котором передаётся переменная, содержащая структуру, должен быть типа String. Как записать в строковую переменную числовые данные, вы узнаете чуть позже.
При программировании Windows API на Си описание указателя на структуру начинается с символов LP (Long Pointer), за которыми следует наименование структуры. Так, указатель нас структуру SYSTEMTIME будет иметь тип LPSYSTEMTIME, указатель на структуру POINT будет иметь тип LPPOINT, и так далее. Как видите, ничего сложного, но, благодаря этой концепции, существует чрезвычайно большое количество типов указателей на структуры.
Если данные в передаваемой структуре не должны изменяться, то указатель на такую структуру объявляется так:
CONST имя_стуктуры *
Здесь модификатор CONST означает, что данные в структуре не должны меняться, а символ (*) после имени структуры означает, что вся эта строка есть описание указателя на структуру. В следующем примере показан прототип функции CopyRect, которая копирует одну структуру в другую:
BOOL CopyRect( LPRECT lprcDst, CONST RECT * lprcSrc );
Описание прототипов функций в MSDN
Теперь, когда с типами данных всё стало более-менее понятно, познакомимся подробнее с таким понятием Си, как прототипы функций.
Согласно стандарта ANSI, все функции в Си должны иметь прототипы. Прототип функции достаточно прост:
возвращаемый_тип имя_функции(тип_параметра(ов) имя_параметра(ов));
Если в прототипе указан тип VOID как возвращаемый_тип, то это означает, что функция не возвращает никаких значений. Если тип VOID указан как тип_параметра, то это означает, что функция не имеет параметров.
Информацию о прототипах Windows API функций, включенных в библиотеки Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll и Advapi32.dll, в MSDN для Visual Studio.NET вы можете найти, последовательно открывая следующие разделы оглавления (Contents) справки:
MSDN Library
Windows Development Win32 API SDK Documentacion Reference
В разделе Reference вы можете посмотреть описания функций, открыв один из следующих подразделов:
Наименование раздела MSDN | Что можно посмотреть |
Windows Data Types | Здесь находится информация о типах данных Windows |
Functions By Category | Здесь приводится список Win32 API функций по категориям |
Function In Alphabetical Order | Здесь приводится список Win32 API функций в алфавитном порядке |
Вот ещё один адрес в MSDN, по которому так же имеется информация об API функциях:
MSDN Library
User Interface Design and Development SDK Documentacion Windows Shell Shell Reference Shell Functions
На следующем рисунке показан фрагмент окна справочной системы MSDN:
Вот как, например, описана в MSDN функция CopyRect:
CopyRect
The CopyRect function copies the coordinates of one rectangle to another.
BOOL CopyRect(
LPRECT lprcDst, // destination rectangle
CONST RECT* lprcSrc // source rectangle
);Parameters
lprcDst
[out] Pointer to the RECT structure that receives the logical coordinates of the source rectangle.
lprcSrc
[in] Pointer to the RECT structure whose coordinates are to be copied in logical units.Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Windows NT/2000/XP: To get extended error information, call GetLastError.Remarks
Because applications can use rectangles for different purposes, the rectangle functions do not use an explicit unit of measure. Instead, all rectangle coordinates and dimensions are given in signed, logical values. The mapping mode and the function in which the rectangle is used determine the units of measure.
Example Code
For an example, see Using Rectangles.
Requirements
Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
Как видите, информация достаточно исчерпывающая. Функция возвращает значение типа BOOL, ей передаются два параметра: типа LPRECT и CONST RECT* - указатели на структуры типа RECT. При объявлении этой функции в Visual FoxPro вы должны указать, что первый параметр передаётся по ссылке, а второй - по значению:
DECLARE Long CopyRect IN User32.dll String @ Dst, String Src
А как я определил, что эта функция находится в библиотеке User32.dll? Очень просто. В разделе рекомендаций (Requirements ) пункт Library гласит: Use User32.lib. Подставьте вместо расширения lib расширение dll - и всё! Кстати, там же, в пункте Header, сообщается, в каком include-файле содержится описание прототипа функции.
Но это ещё не всё! Так как функция работает со структурами, то в её описании присутствует гиперссылка на структуру RECT. Щёлкните мышью по этой гиперссылке, и на экране появится подробное описание структуры.
Формирование структур в Visual FoxPro
В девятой версии Visual FoxPro существенно расширены возможности встроенных функций BINTOC и CTOBIN. Теперь эти функции можно применять для преобразования числовых данных в формат, пригодный для использования в структурах. Напомню, что функция BINTOC выполняет преобразование числа в строку, а функция CTOBIN - строки в число.
Синтаксис функции BINTOC:
BINTOC(nExpression, eFlag)
где:
nExpression | преобразуемое числовое значение |
eFlag | тип преобразования |
Из всех возможных значений, которые может принимать параметр eFlag, нас интересуют следующие:
eFlag | Описание |
"2RS" | Преобразует 16-ти разрядное целое число в двухбайтовую строку |
"4RS" | Преобразует 32-х разрядное целое число в четырёхбайтовую строку |
"F" | Преобразует 32-х разрядное вещественное число в четырёхбайтовую строку |
"B" | Преобразует 64-х разрядное вещественное число в восьмибайтовую строку |
Синтаксис функции CTOBIN:
CTOBIN(cExpression, eFlag)
где:
cExpression | преобразуемая строка символов |
eFlag | тип преобразования |
Возможные значения параметра eFlag:
eFlag | Описание |
"2RS" | Преобразует двухбайтовую строку в 16-ти разрядное целое число |
"4RS" | Преобразует четырёхбайтовую строку в 32-х разрядное целое число |
"4N" | Преобразует четырёхбайтовую строку в 32-х разрядное вещественное число |
"8N" | Преобразует восьмибайтовую строку в 64-х разрядное вещественное число |
Ниже показаны примеры использования этих функций:
? CTOBIN(BINTOC(1000.55,'4RS'), '4RS') && Результат: 1000 ? CTOBIN(BINTOC(-1000.55,'4RS'), '4RS') && Результат: -1000 ? CTOBIN(BINTOC(1000.55,'F'), '4N') && Результат: 1000.549987929 ? CTOBIN(BINTOC(-1000.55,'F'), '4N') && Результат: -1000.549987929 ? CTOBIN(BINTOC(1000.55,'B'), '8N') && Результат: 1000.55 ? CTOBIN(BINTOC(-1000.55,'B'), '8N') && Результат: -1000.55
В качестве примера запишем в переменную Visual FoxPro структуру RECT. Эта структура используется в рассмотренной ранее функции CopyRect для описания координат прямоугольной области. Вот как эта структура описывается в MSDN:
typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT;
Как видите, структура RECT содержит четыре поля, в каждом из которых хранится значение типа LONG. Для её формирования в Visual FoxPro понадобится строка длиной 16 байт.
Ниже показан код, в котором объявляется функция CopyRect, формируются структуры Dst и Src для передачи их как параметров функции, и затем выполняется копирование одной структуры в другую. В примере используется функция BINTOC для преобразования числа в строку:
DECLARE Long CopyRect IN WIN32API String @ Dst, String Src * Формируем структуру Src cSrc = BINTOC(nLeft,'4RS') + BINTOC(nTop,'4RS') + ; BINTOC(nRight,'4RS') + BINTOC(nBottom,'4RS') * Подготавливаем место для структуры Dst cDst = REPLICATE(CHR(0),16) nResult = CopyRect(@cDst, cSrc) && Копирование
В следующем примере показано, как, используя функцию CTOBIN, можно "разобрать" структуру, получив числовые значения её полей:
nLeft = CTOBIN(SUBSTR(cDst,1,4), '4RS') && RECT.left nTtop = CTOBIN(SUBSTR(cDst,5,4), '4RS') && RECT.top nRight = CTOBIN(SUBSTR(cDst,9,4), '4RS') && RECT.right nBottom = CTOBIN(SUBSTR(cDst,13,4), '4RS') && RECT.bottom
Структуры, содержащие указатели
Достаточно часто встречается ситуация, когда передаваемая Windows API функции структура содержит указатели. В качестве примера рассмотрим функцию StartDoc, создающую документ для печати на принтере. Вот её прототип:
int StartDoc( HDC hdc, // handle to DC CONST DOCINFO* lpdi // contains file names );
Как видите, второй передаваемый функции параметр - это указатель на структуру DOCINFO. Вот эта структура:
typedef struct { int cbSize; LPCTSTR lpszDocName; LPCTSTR lpszOutput; LPCTSTR lpszDatatype; DWORD fwType; } DOCINFO, *LPDOCINFO;
Первое поле структуры, cbSize, содержит значение длины структуры в байтах. А вот следующие три поля - это указатели на переменные, содержащие символьные данные. В частности, поле lpszDocName содержит указатель на строку с наименованием печатаемого документа (это то самое имя документа, которое вы видите, просматривая очередь печатаемых документов).
В Visual FoxPro достаточно сложно сформировать структуру, содержащую указатели. Во-первых, нужно выделить блок памяти и получить указатель на него. Во-вторых, необходимо переписать в эту память значение переменной Visual FoxPro - таким образом, у нас будет полностью реализован механизм указателей. Последнее, что остаётся сделать - это поместить значение указателя в структуру. При этом нужно выполнить одно существенное требование: выделенная память не должна быть перемещаемой - иначе может оказаться, что наш указатель в какой-то момент будет показывать на область, к которой наши данные не имеют никакого отношения!
Есть несколько возможностей получить блок памяти. Можно взять "кусочек" как из общей памяти Windows, так и из памяти, выделенной процессу (то есть вашему приложению). Второй способ имеет более высокое быстродействие, тем не менее здесь мы рассмотрим способ работы с памятью Windows как более простой.
Функция GlobalAlloc получает у Windows блок памяти указанного размера и возвращает указатель на него. Вот прототип этой функции:
HGLOBAL GlobalAlloc( UINT uFlags, // атрибуты распределения памяти SIZE_T dwBytes // размер в байтах );
Параметр uFlags определяет, как будет распределяться память. В MSDN написано, что он может принимать одно из следующих значений:
uFlag | Описание |
GHND | Комбинация значений GMEM_MOVEABLE и GMEM_ZEROINIT |
GMEM_FIXED | Распределяется фиксированная (неперемещаемая) область памяти |
GMEM_MOVEABLE | Распределяется перемещаемая память |
GMEM_ZEROINIT | Выделенный блок памяти заполняется нулями |
GPTR | Комбинация значений GMEM_FIXED и GMEM_ZEROINIT |
Из таблицы следует, что для параметра uFlags следует использовать значение GPTR. Но как узнать, какое это значение? Найдите в MSDN описание функции GlobalAlloc и в разделе Requirements посмотрите, в каком include-файле находится её прототип. Это файл Winbase.h. Именно в нём и следует искать описание значений констант. Вот фрагмент этого файла, в котором определяются перечисленные в таблице константы:
/* Global Memory Flags */ #define GMEM_FIXED 0x0000 #define GMEM_MOVEABLE 0x0002 #define GMEM_NOCOMPACT 0x0010 #define GMEM_NODISCARD 0x0020 #define GMEM_ZEROINIT 0x0040 #define GMEM_MODIFY 0x0080 #define GMEM_DISCARDABLE 0x0100 #define GMEM_NOT_BANKED 0x1000 #define GMEM_SHARE 0x2000 #define GMEM_DDESHARE 0x2000 #define GMEM_NOTIFY 0x4000 #define GMEM_LOWER GMEM_NOT_BANKED #define GMEM_VALID_FLAGS 0x7F72 #define GMEM_INVALID_HANDLE 0x8000 #define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT) #define GPTR (GMEM_FIXED | GMEM_ZEROINIT)
Следовательно, GPTR = GMEM_FIXED + GMEM_ZEROINIT = 0x0000 + 0x0040 = 0x0040.
Какой размер должен иметь выделяемый блок памяти? Конечно, равный длине строки, в которой хранится наименование документа. В следующем примере показаны действия, начиная с объявления API функции и заканчивая выделением блока памяти:
#DEFINE GPTR 0x0040 DECLARE Long GlobalAlloc IN WIN32API Long uFlags, Long dwBytes cDocumentName = 'Имя печатаемого документа' && Имя документа nLenDocumentName = LEN(cDocumentName) && Длина строки hGlobal = GlobalAlloc(GPTR, nLenDocumentName)
Следующая задача - переписать содержимое переменной cDocumentName в полученный блок памяти. Воспользуемся для этого встроенной функцией Visual FoxPro SYS(2600). Вот её синтаксис:
SYS(2600, dwAddress, nLenght [, cNewString])
Функция ведёт себя по разному в зависимости от того, указан параметр cNewString или нет.
Если параметр cNewString указан, то функция копирует nLenght байт из переменной cNewString в память по адресу, указанному в dwAddress.
Если параметр cNewString не указан, то функция возвращает nLenght байт из памяти по адресу, указанному в dwAddress.
Как видите, параметр dwAddress - это указатель.
Запишем содержимое строки cDocumentName в выделенную функцией GlobalAlloc память:
= SYS(2600, hGlobal, nLenDocumentName, cDocumentName)
Вот полный код формирования структуры DOCINFO:
#DEFINE GPTR 0x0040 DECLARE Long GlobalAlloc IN WIN32API Long uFlags, Long dwBytes cDocumentName = 'Имя печатаемого документа' nLenDocumentName = LEN(cDocumentName) hGlobal = GlobalAlloc(GPTR, nLenDocumentName) SYS(2600, dwAddress, nLenght [, cNewString]) * Начинаем формировать структуру DOCINFO * cDocInfo - переменная, в которой формируется структура cDocInfo = BINTOC(20,'4RS') && DOCINFO.cbSize cDocInfo = cDocInfo + BINTOC(hGlobal,'4RS') && DOCINFO.lpszDocName cDocInfo = cDocInfo + REPLICATE(CHR(0),12) && Остальные поля структуры * Конец формирования структуры
Структура формируется в переменной cDocInfo. В первые четыре байта записываем число 20 - это размер структуры (поле cbSize). Следующие четыре байта, добавляемые в переменную, - это указатель на область памяти, в которую переписано содержимое переменной cDocumentName - наименование документа. Затем к переменной добавляются ещё двенадцать нулевых байтов - это поля структуры lpszOutput, lpszDatatype и fwType, которые, согласно документации, могут игнорироваться; это означает, что поля должны иметь нулевые значения. Таким образом, получилась строка длиной 20 байтов - что и требовалось.
Особенности использования памяти
Применяя в ваших приложениях функции Windows API, вы должны помнить о том, Visual FoxPro не может управлять памятью, резервируемой этими функциями. Поэтому, если вы распределяете память, например, при помощи функции GlobalAlloc, то вы обязательно должны после использования вернуть эту память Windows, вызвав функцию GlobalFree. Для каждой API функции, резервирующей память, имеется функция - антипод, освобождающая зарезервированную память.
Вот прототип функции GlobalFree:
HGLOBAL GlobalFree( HGLOBAL hMem // указатель на блок памяти );
Функция получает только один параметр - указатель на блок памяти. Вот её объявление и использование в Visual FoxPro:
DECLARE Long GlobalFree IN WIN32API Long hGlobal GlobalFree(hGlobal)
здесь hGlobal - указатель на блок памяти, возвращаемый функцией GlobalAlloc.
Если вы будете забывать возвращать распределённую память, то тогда вы рискуете столкнуться с проблемой, называемой утечкой памяти. Последствия такой утечки могут быть весьма печальны - от резкого замедления работы вашего приложения, вызванного фрагментацией памяти, до краха операционной системы.
Передача в функцию массивов
Массив с точки зрения Си - это переменная, содержащая несколько элементов одного типа. Доступ к каждому отдельному элементу массива осуществляется при помощи индекса. Все элементы массива имеют одинаковый размер, нельзя описывать массивы со смешанными типами данных. Все элементы массива хранятся в памяти последовательно, один за другим, при этом минимальное значение индекса соответствует первому элементу, а максимальное - последнему.
Массив Visual FoxPro имеет совершенно другую организацию, которая позволяет хранить в его элементах совершенно разные типы данных. Поэтому невозможно передать массив из Visual FoxPro в Windows API функцию, просто указав его имя как параметр. Более того, в команде DECLARE..DLL нет такого типа данных, как массивы.
Тем не менее выход из этого положения есть. Как и для структур, для передачи массивов используются символьные переменные. Числовые данные из массива должны быть преобразованы в строку, которую вы и передаёте как параметр в Windows API функцию. В следующем примере показан код функции ArrayToString, формирующей строку из массива. Функция получает получает массив taArray (по ссылке) и флаг tlTypeOfValue, указывающий, как нужно преобразовать значения элементов массива - как целые (tlTypeOfValue=.F.) или вещественные (tlTypeOfValue=.T.) числа:
FUNCTION ArrayToString PARAMETERS taArray, tlTypeOfValue EXTERNAL ARRAY taArray LOCAL lnLenArray, lnIndex, lcStruct, lcType lcType = IIF(tlTypeOfValue = .t., 'F', '4RS') lnLenArray = ALEN(taArray) && Количество элементов в массиве lcStruct = "" FOR lnIndex = 1 TO lnLenArray lcStruct = lcStruct + BINTOC(taArray[lnIndex], lcType) ENDFOR RETURN lcStruct ENDFUNC
Функция работает как с одномерными, так и с двумерными массивами.
Кодировка символьных данных: форматы ANSI и UNICODE
В кодировке ANSI (применяемой в Visual FoxPro) каждый символ определяется одним байтом, то есть максимальное количество символов равно 256, что, конечно, очень мало. Например, при русификации часть стандартных символов ANSI заменяется на символы кириллицы. А в некоторых алфавитах, например, японской кане, столько символов, что одного байта для их кодировки просто недостаточно. Для поддержки таких языков, и, что более важно, для облегчения "перевода" программ на другие языки, была разработана кодировка Unicode. Каждый символ в Unicode состоит из двух байтов, что позволило расширить набор допустимых символов до 65536.
Достаточно большое количество функций Windows API используют при работе с символьными строками формат Unicode. Для работы с такими функциями необходимо выполнять конвертирование символьных данных из одного формата в другой.
Встроенная функция Visual FoxPro STRCONV выполняет конвертирование строк как из формата ANSI в UNICODE, так и обратно. Вот её синтаксис:
STRCONV(cExpression, nConversionSetting [, nRegionalIdentifier [, nRegionalIDType]])
Параметр cExpression - это строка, которую необходимо конвертировать. Параметр nConversionSetting указывает характер конвертирования. Из всех его возможных значений нас интересуют только два:
- 5 - конвертирование из ANSI в UNICODE
- 6 - конвертирование из UNICODE в ANSI
Необязательные параметры nRegionalIdentifier и nRegionalIDType определяют дополнительные региональные настройки и могут быть безболезненно проигнорированы.
Ниже показаны примеры использования функции STRCONV:
cUnicodeString = STRCONV(cANSIString, 5) && Конвертирование в Unicode cANSIString = STRCONV(cUnicodeString, 6) && Конвертирование в ANSI
Возможно, при прочтении этого раздела у вас сложилось впечатление, что работать с Windows API функциями в Visual FoxPro достаточно просто. И да, и нет. Например, некоторые API функции используют типы данных, не поддерживаемые в команде DECLARE..DLL, например, 64-х разрядные целые числа. Так же есть ряд функций, называемых функциями обратного вызова. В прототипе такой функции перед описанием возвращаемого типа присутствует модификатор CALLBACK. К сожалению, вы не можете использовать такие функции, по крайней мере, напрямую. Так же бывает, что структура содержит указатель на функцию - такие API в Visual FoxPro так же нельзя использовать.