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

ЖАНРЫ

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:
Примечание

Несмотря на очень высокое качество разделов MSDN, относящихся к Window API, ошибки иногда бывают и там. Со временем их исправляют. Поэтому, если вы столкнулись с ситуацией, когда есть подозрение, что какая-либо функция Windows API ведёт себя не так, как это описано в вашей offline-справке, есть смысл заглянуть в online-справку — возможно, там уже появились дополнительные сведения по данной функции.

Рис. 1.3. Окно справки Delphi 2007 (функция

DeleteObject
)

Система Windows написана на C++, поэтому все описания функций Windows API, а также примеры их использования приведены на этом языке (это касается как MSDN, так и справки, поставляемой с Delphi). При этом, прежде всего, необходимо разобраться с типами данных. Большинство типов, имеющихся в Windows API. определены в Delphi. Соответствие между ними показано в табл. 1.1.

Таблица 1.1. Соответствие типов Delphi

системным типам

Тип Windows API Тип Delphi
INT
INT
UINT
LongWord
WORD
Word
SHORT
SmallInt
USHORT
Word
CHAR
Чаще всего соответствует типу
Char
, но может трактоваться также как
ShortInt
, т.к. в C++ нет разницы между символьным и целочисленным типами
UCHAR
Чаще всего соответствует типу
Byte
, но может трактоваться также как
Char
DWORD
LongWord
BYTE
Byte
WCHAR
WideChar
BOOL
LongBool
int
Integer
long
LongInt
short
SmallInt
unsigned int
Cardinal

Название типов указателей имеет префикс P или LP (Pointer или Long Pointer, в 16-разрядных версиях Windows были короткие и длинные указатели. В 32-разрядных все указатели длинные, поэтому оба префикса имеют одинаковый смысл). Например,

LPDWORD
эквивалентен типу
^DWORD
,
PUCHAR
^Byte
. Иногда после префикса P или LP стоит еще префикс C — он означает, что это указатель на константу. В C++ возможно объявление таких указателей, которые указывают на константное содержимое, т.е. компилятор разрешает это содержимое читать, но не модифицировать. В Delphi такие указатели отсутствуют, и при портировании эти типы заменяются обычными указателями, т.е. префикс C игнорируется.

Типы

PVOID
и
LPVOID
соответствуют нетипизированным указателям (
Pointer
).

Для передачи символов чаще всего используется тип

TCHAR
. Windows поддерживает две кодировки: ANSI (1 байт на символ) и Unicode (2 байта на символ; о поддержке Unicode в Windows мы будем говорить далее). Тип
CHAR
соответствует символу в кодировке ANSI,
WCHAR
— Unicode. Для программ, которые используют ANSI, тип
TCHAR
эквивалентен типу CHAR, для использующих Unicode —
WCHAR
. В Delphi нет прямого аналога типу
TCHAR
. Программист сам должен следить за тем, какой символьный тип требуется в данном месте. Строки в Windows API передаются как указатели на цепочку символов, завершающихся нулем. Поэтому указатель на
TCHAR
может указывать как на единичный символ, так и на строку. Чтобы было легче разобраться, где какой указатель, в Windows API есть типы
LPTCHAR
и
LPTSTR
. Они эквивалентны друг другу, но первый принято использовать там, где требуется указатель на одиночный символ, а второй — на строку. Если строка передается в функцию только для чтения, обычно используют указатель на константу, т.е. тип
LPCTSTR
. В Delphi это соответствует PChar для ANSI и PWideChar для Unicode. Здесь следует отметить особенность записи строковых литералов в языках C/C++. Символ \ в литерале имеет специальное значение: после него идет один или несколько управляющих символов. Например, \n означает перевод строки, \t — символ табуляции и т.п. В Delphi таких последовательностей нет, поэтому при переводе примеров из MSDN следует явно писать коды соответствующих символов. Например, литерал
"а\nb"
в Delphi превращается в
'a\'#13'b'
. После символа 
\
может идти число — в этом случае оно трактуется как код символа, т.е. литерал
"a\0b9"
 в C/C++ эквивалентен литералу '
а'#0'b'#9
в Delphi. Если нужно,
чтобы строковый литерал включал в себя сам символ \, его удваивают, т.е. литерал
"\\"
в C++ соответствует
'\'
в Delphi. Кроме того, в примерах кода, приведенных в MSDN, можно нередко увидеть, что строковые литералы обрабатываются макросами
TEXT
или
_T
, которые служат для унификации записи строковых литералов в кодировках ANSI и Unicode. При переводе такого кола на Delphi эти макросы можно просто опустить. С учетом сказанного такой, например, код (взят из примера использования Named pipes):

LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

на Delphi будет выглядеть так:

var

 lpszPipeName: PChar;

...

lpszPipeName:= '\\.\pipe\mynamedpipe';

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

HWND
, первый параметр сообщения — тип
WPARAM
(в старых 16-разрядных Windows он был эквивалентен типу
Word
, в 32-разрядных —
LongInt
). Эти специальные типы также описаны в модуле Windows.

Записи (

record
) в C/C++ называются структурами и объявляются с помощью слова
struct
. Из-за особенностей описания структур на языке С структуры в Windows API получают два имени: одно основное имя, составленное из главных букв, которое затем и используется, и одно вспомогательное, получающееся из основного добавлением префикса
tag
. Начиная с четвертой версии Delphi приняты следующие правила именования таких типов: простое и вспомогательное имена остаются без изменений и еще добавляется новое имя, получающееся из основного присоединением общеупотребительного в Delphi префикса
T
. Например, в функции
CreatePenIndirect
одни из параметром имеет тип
LOGPEN
. Это основное имя данного типа, а вспомогательное —
tagLOGPEN
. Соответственно, в модуле Windows определена запись
tagLOGPEN
и ее синонимы —
LOGPEN
и
TLogPen
. Эти три идентификатора в Delphi взаимозаменяемы. Вспомогательное имя встречается редко, программисты, в зависимости от личных предпочтений, выбирают либо основное имя типа, либо имя с префиксом
T
.

Описанные здесь правила именования типов могут внести некоторую путаницу при использовании VCL. Например, для описания растра в Windows API определен тип

BITMAP
(он же—
tagBITMAP
). В Delphi соответствующий тип имеет еще одно имя —
TBitmap
. Но такое же имя имеет класс
TBitmap
, описанный в модуле
Graphics
. В коде, который Delphi создает автоматически, модуль
Graphics
находится в списке
uses
после модуля
Windows
, поэтому идентификатор
TBitmap
воспринимается компилятором как
Graphics.TBitmap
, а не как
Windows.TBitmap
. Чтобы использовать
Windows.ТBitmap
, нужно явно указать имя модуля или воспользоваться одним из альтернативных имен. В более ранних версиях
Delphi
были другие правила именования типов. Например. в Delphi 2 существовал тип
BITMAP
, но не было
TBitmap
и
tagBITMAP
, а в Delphi 3 из этих трех типов был только
TBitmap
.

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

packed
, запрещающее выравнивание.

При описании структур Windows API можно иногда встретить ключевое слово

union
(см., например, структуру
in_addr
). Объединение нескольких полей с помощью этого слова означает, что все они будут размещены по одному адресу. В Delphi это соответствует вариантным записям (т. е. использованию сазе в record). Объединения в C/C++ гибче, чем вариантные записи Delphi, т.к. позволяют размещать вариантную часть в любом месте структуры, а не только в конце. При переносе таких структур в Delphi иногда приходится вводить дополнительные типы.

Теперь рассмотрим синтаксис описания самой функции в C++ (листинг 1.1).

Листинг 1.1. Синтаксис описания функции на C++

<Тип функции> <Имя функции> ' ('

 [<Тип параметра> {<Имя параметра>}

(',' <Тип параметра> {<Имя параметра>} }

 ]

')';

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

VOID
, о которых написано далее).

Необходимо помнить, что в C/C++ различаются верхний и нижний регистры, поэтому

HDC
,
hdc
,
hDC
и т.д. — это разные идентификаторы (автор С очень любил краткость и хотел, чтобы можно было делать не 26, а 52 переменные с именем из одной буквы). Поэтому часто можно встретить, что имя параметра и его тип совпадают с точностью до регистра. К счастью, при описании функции в Delphi мы не обязаны сохранять имена параметров, значение имеют лишь их типы и порядок следования. С учетом всего этого функция, описанная в справке как

Поделиться с друзьями: