А. Григорьев - О чём не пишут в книгах по Delphi
- Название:О чём не пишут в книгах по Delphi
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2008
- Город:СПб
- ISBN:978-5-9775-019003
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.
Для программистов
О чём не пишут в книгах по Delphi - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Разработчики Delphi не стали полностью копировать этот механизм, видимо, этому помешала существующая в Delphi раздельная компиляция модулей, из-за которой невозможно определением одного символа заставить все модули перекомпилироваться (тем более что часть из них может не иметь исходных кодов). Поэтому в Delphi нет типа, аналогичного TCHAR
.
Рассмотрим, как та же функция RegisterWindowMessage
импортируется модулем Windows (листинг 1.19).
RegisterWindowMessage
interface
...
function RegisterWindowMessage(lpString: PChar): UINT; stdcall;
function RegisterWindowMessageA(lpString: PAnsiChar): UINT;
stdcall; function RegisterWindowMessageW(lpString: PWideChar): UINT; stdcall;
...
implementation
...
function RegisterWindowMessage;
external user32 name 'RegisterWindowMessageA';
function RegisterWindowMessageA;
external user32 name 'RegisterWindowMessageA';
function RegisterWindowMessageW;
external user32 name 'RegisterWindowMessageW';
Видно, что функция RegisterWindowMessageA
импортируется дважды: один раз под своим настоящим именем, а второй раз — под именем RegisterWindowMessage
. Любое из этих имен подходит для вызова ANSI-варианта этой функции (напоминаю, что типы PChar
и PAnsiChar
эквивалентны). Чтобы вызвать Unicode-вариант функции, потребуется функция RegisterWindowMessageW
.
Структуры, содержащие строковые данные, также имеют ANSI- и Unicode-вариант. Например, структура WNDCLASS
в модуле Windows представлена типами TWndClassA
(с синонимами WNDCLASSA
и tagWNDCLASSA
) и TWndClassW
(с синонимами WNDCLASSW
и tagWHDCLASSW
). Тип TWndClass
(и его синонимы WNDCLASS
и tagWNDCLASS
) эквивалентен типу TWndClassA
. Соответственно, при вызове функций RegisterClassA
и RegisterClassExA
используется тип TWndClassA
, при вызове RegisterClassW
и RegisterClassExW
— тип TWndClassW
.
1.1.13. Строки в Windows API
Unicode в Delphi встречается редко, т.к. программы, использующие эту кодировку, не работают в Windows 9x/МЕ. Библиотека VCL также игнорирует возможность работы с Unicode, ограничиваясь ANSI. Поэтому далее мы будем говорить только об ANSI. С кодировкой Unicode можно работать аналогично, заменив PChar
на PWideChar
и string
на WideString
.
Для работы со строками в Delphi наиболее распространен тип AnsiString
, обычно называемый просто string
(более детально отношения между этими типами рассмотрены в главе 3 ). Переменная типа string
является указателем на строку, хранящуюся в динамической памяти. Этот указатель указывает на первый символ строки. По отрицательному смещению хранится число символов в строке и счетчик ссылок, который позволяет избежать ненужных копирований строки, реализуя так называемое "копирование при необходимости". Если присвоить одной строковой переменной значение другой строковой переменной, то строка не копируется, а просто обе переменные начинают указывать на одну и ту же строку. Счетчик ссылок при этом увеличивается на единицу. Когда строка модифицируется, проверяется счетчик ссылок: если он не равен единице, то строка копируется, счетчик ссылок старой копии уменьшается на единицу, у новой копии счетчик ссылок будет равен единице, и переменная, которая меняется, будет указывать на новую копию. Таким образом, строка копируется только тогда, когда одна из ссылающихся на нее переменных начинает изменять эту строку, чтобы изменения не коснулись остальных переменных. При любых модификациях строки в ее конец автоматически добавляется нулевой символ (при подсчете длины строки с помощью функции Length
он игнорируется). Но если присвоить строковой переменной пустую строку, то эта переменная станет нулевым указателем ( nil
), память для хранения одного символа #0 выделена не будет. При выходе строковой переменной из области видимости (т. е., например, при завершении процедуры, в которой она является локальной переменной, или при уничтожении объекта, полем которого она является) она автоматически финализируется, т.е. счетчик ссылок уменьшается на единицу и, если он оказывается равен нулю, память, выделенная для строки, освобождается. (О внутреннем устройстве AnsiString
см. также разд. 3.3. )
Механизм выделения и освобождения памяти и подсчета ссылок прозрачен для программы. От разработчика требуется только не вмешиваться в его работу с помощью низкоуровневых операций с указателями, чтобы менеджер памяти не запутался.
В отличие от string
, тип WideString
не имеет счетчика ссылок, и любое присваивание переменных этого типа приводит к копированию строки. Это сделано в целях совместимости с системным типом BSTR
, использующимся в COM/DCOM и OLE.
Функции Windows API не поддерживают тип string
. Они работают со строками, оканчивающимися на #0
( нуль-терминированные строки, null-termi nated strings). Это означает, что строкой называется указатель на цепочку символов. Признаком конца такой цепочки является символ с кодом 0. Раньше для таких строк существовал термин ASCIIZ. ASCII — название кодировки, Z — zero. Сейчас кодировка ASCII в чистом виде не встречается, поэтому этот термин больше не применяется, хотя это те же самые по своей сути строки. Как уже говорилось, в Delphi ко всем строкам типа string
неявно добавляется нулевой символ, не учитывающийся при подсчете числа символов. Это сделано для совместимости с нуль-терминированными строками. Однако эта совместимость ограничена.
Для работы с нуль-терминированными строками в Delphi обычно служит тип PChar
. Формально это указатель на один символ типа Char
, но подразумевается, что это только первый символ строки, и за ним следуют остальные символы. Где будут эти символы размещены и какими средствами для них будет выделена память, программист должен решить самостоятельно. Он же должен позаботиться о том, чтобы в конце цепочки символов стоял #0
.
Строку, на которую указывает PChar
, можно использовать везде, где требуется string — компилятор сам выполнит необходимые преобразования. Обратное неверно. Фактически, string
— это указатель на начало строки, завершающейся нулем, т.е. тот самый указатель, который требуется при работе с PChar
. Однако, как уже отмечалось, некорректные манипуляции с этим указателем могут привести к нежелательным эффектам, поэтому компилятор требует явного приведения переменных и выражений типа string
к PChar
. В свою очередь, программист должен ясно представлять, к каким последствиям это может привести.
Если посмотреть описание функций API, имеющих строковые параметры, в справке, можно заметить, что в некоторых случаях строковые параметры имеют тип LPCTSTR
(как, например, у функции SetWindowText
), а в некоторых — LPTSTR
( GetWindowText
). Ранее мы говорили, что появление префикса C
после LP
указывает на то, что это указатель на константу, т.е. то, на что указывает такой указатель, не может быть изменено. Тип LPCTSTR
имеют те строковые параметры, содержимое которых функция только читает, но не модифицирует. С такими параметрами работать проще всего. Рассмотрим на примере функции SetWindowText
, как можно работать с такими параметрами (листинг 1.20).
Интервал:
Закладка: