А. Григорьев - О чём не пишут в книгах по Delphi
Описанные здесь правила именования типов могут внести некоторую путаницу при использовании 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 мы не обязаны сохранять имена параметров, значение имеют лишь их типы и порядок следования. С учетом всего этого функция, описанная в справке как
HMETAFILE CopyMetaFile(HMETAFILE hmfSrc, LPCTSTR lpszFile);
в Delphi имеет вид
function СоруМеtaFile(hnfSrc: HMETAFILE; lpszFile: LPCTSTR): HMETAFILE;
или, что то же самое.
function CopyMetaFile(hnfSrc: HMETAFILE; lpszFile: PChar): HMETAFILE;
ПримечаниеКомпилятор Delphi допускает, чтобы имя параметра процедуры или функции совпадало с именем типа, поэтому мы в дальнейшем увидим, что иногда имя параметра и его тип совпадают, только записываются в разном регистре, чтобы прототип функции на Delphi максимально соответствовал исходному прототипу на C/C++. При этом следует учитывать что соответствующий идентификатор внутри функции будет рассматриваться как имя переменной, а не типа, поэтому, например, объявлять локальную переменную данного типа придется с явным указанием имени модуля, в котором данный тип объявлен.
Несколько особняком стоит тип VOID (или void, что то же самое, но в Windows API этот идентификатор встречается существенно реже). Если функции имеет такой тип, то в Паскале она описывается как процедура. Если вместо параметров у функции в скобках указан void, это означает, что функция не имеет параметров. Например, функция
VOID CloseLogFile(VOID);
в Delphi описывается как
procedure CloseLogFile;
ПримечаниеЯзык C++, в отличие от С, допускает объявление функций без параметров, т.е. функцию CloseLogFile можно было бы объявить так: VOID CloseLogFile(); В C++ эти варианты объявления эквивалентны, но в Windows API варианту явного параметра встречается существенно реже из-за несовместимости с C.
Когда тип параметра является указателем на другой тип (обычно начинается с букв LP), при описании этой функции в Delphi можно пользоваться параметром-переменной, т.к. в этом случае функции передается указатель. Например, функция
int GetRgnBox(HRGN hrgn, LPRECT lprc);
в модуле Windows описана как
function GetRgnBox(RGN: HRGN; var p2: TRec): Integer;
Такая замена целесообразна в том случае, если значение параметра не может быть нулевым указателем, потому что при использовании var передать такой указатель будет невозможно. Нулевой указатель в C/C++ обозначается константой NULL. NULL и 0 в этих языках взаимозаменяемы, поэтому в справке можно и про целочисленный параметр встретить указание, что он может быть равен NULL.
И наконец, если не удается понять, как функция, описанная в справке, должна быть переведена на Паскаль, можно попытаться найти описание этой функции в исходных текстах модулей, поставляемых вместе с Delphi. Эти модули находятся в каталоге $(DELPHI)SourceRTLWin (до Delphi 7) или $(BDS)SourceWin32RTLWin (BDS 2006 и выше). Можно также воспользоваться подсказкой, которая всплывает в редакторе Delphi после того, как будет набрано имя функции.
Если посмотреть справку, например, по функции GetSystemMetrics, то видно, что эта функция должна иметь один целочисленный параметр. Однако далее в справке предлагается при вызове этой функции подставлять в качестве параметра не числа, a SM_ARRANGE, SM_CLEANBOOT и т.д. Подобная ситуация и со многими другими функциями Windows API. Все эти SM_ARRANGE, SM_CLEANBOOT и т.д. являются именами числовых констант. Эти константы описаны в том же модуле, в котором описана функция, использующая их, поэтому можно не выяснять численные значения этих констант, а указывать при вызове функций их имена, например, GetSystemMetrics(SM_ARRANGE);. Если по каким-то причинам все-таки потребовалось выяснить численные значения, то в справочной системе их искать не стоит — их там нет. Их можно узнать из исходных текстов модулей Delphi, в которых эти константы описаны. Так, например, просматривая Windows.pas, можно узнать, что SM_ARRANGE = 56.
В справке, поставляемой вместе с Delphi до 7-й версии включительно, в описании многих функций Windows API вверху можно увидеть три ссылки: QuickInfo, Overview и Group. Первая дает краткую информацию о функции: какой библиотекой реализуется, в каких версиях Windows работает и т.п. (напоминаю, что к информации о версиях в этой справке нужно относиться очень критично). Overview — это обзор какой-то большой темы. Например, для любой функции, работающей с растровыми изображениями, обзор будет объяснять, зачем в принципе нужны эти самые растровые изображения и как они устроены. Страница, на которую ведет ссылка Overview обычно содержит весьма лаконичные сведения, но, нажав кнопку >>, расположенную в верхней части окна, можно получить продолжение обзора. И, наконец, Group. Эта ссылка приводит к списку всех функций, родственных данной. Например, для функции CreateRectRgn группу будут составлять все функции, имеющие отношение к регионам. Если теперь нажимать на кнопку <<, то будут появляться страницы с кратким описанием возможных применений объектов, с которыми работают функции (в приведенном примере — описание возможностей регионов). Чтобы читать их в нормальной последовательности, лучше всего нажать на кнопку << столько раз, сколько возможно, а затем пойти в противоположном направлении с помощью кнопки >>.
MSDN (а также справка BDS 2006 и выше) предоставляет еще больше полезной информации. В нижней части описания каждой функции есть раздел Requirements, в котором написано, какая библиотека и какая версия Windows требуется для ее использования. В самом низу описания функции расположены ссылки See also. Первая ссылка — обзор соответствующей темы (например, для уже упоминавшейся функции CreateRectRgn — она называется Regions Overview). Вторая список родственных функций (Region Functions в данном случае). Она ведет на страницу, где перечислены все функции, родственные выбранной. После этих двух обязательных ссылок идут ссылки на описание функций и типов, которые обычно используются совместно с данной функцией.
Основные типы, константы и функции Windows API объявлены в модулях Windows и Messages. Но многие функции объявлены в других модулях, которые не подключаются к программе по умолчанию, программист должен сам выяснить, в каком модуле находится требуемый ему идентификатор, и подключить этот модуль. Ни справка, поставляемая с Delphi, ни MSDN, разумеется, не могут дать необходимую информацию. Чтобы выяснить, в каком модуле объявлен нужный идентификатор, можно воспользоваться поиском по всем файлам с расширением pas, находящимся в папке с исходными кодами стандартных модулей. Этим методом можно, например, выяснить, что весьма популярная функция ShellExecute находится в модуле ShellAPI, CoCreateInstance — в модуле ActiveX (а также в модуле Ole2, оставленном для совместимости со старыми версиями Delphi).