Битное соединение с сервером Sybase
b>32-битное соединение с сервером Sybase
Данный документ содержит информацию, позволяющую осуществить подключение к базе данных Sybase через 32-битный пакет от фирмы Borland Sybase SQL Links, поставляемый в составе Delphi 2.x. Клиентское программное обеспечение Sybase займет на вашем жестком диске приблизительно 10+ мегабайт свободного пространства.
Шаги для подключения:
Убедитесь в том, что пакет SQL Links установлен на вашем локальном диске. При полной установке Delphi 2.x это должно быть уже установлено в системе.
Инсталируйте клиентское программное обеспечение Sybase.
При появлявлении в процессе установки диалога выбора 16- и 32-разрядной версии Sybase links, выберите только 32-битную версию (отметьте галочкой) и убедитесь в том, что опция 16-битной версии выключена.
После того, как клиентское программное обеспечение будет установлено на вашем жестком диске, у вас попросят разрешение на автоматическую программную коррекцию вашего файла AUTOEXEC.BAT. Выберите YES.
На запрос по поводу редактирования вашего файла SQL.INI ответьте YES.
В секции "Input Server Name:" (введите имя сервера) укажите псевдоним сервера. Щелкните на кнопке 'Add' (добавить) для внесения имени сервера в список "Server Entry:". Затем убедитесь в том, что поля редактирования "Service Type:" (тип сервиса) (должно быть 'query' (запрос)), "Platform:" (платформа) (по умолчанию обычно устанавливается в NT, dos или Win3), и "Net-Library Driver:" (драйвер сетевой библиотеки) (должен быть NLWNSCK или NLNWLINK) содержат верные сведения. Заполните поле редактирования "Connection Information/Network Address:" (адрес информационного/сетевого соединения), введя сетевой адрес сервера, с которым вы хотите иметь соединение. Щелкните на кнопке 'Add Service' (добавить сервис). Вы можете теперь пропинговать ваш сервер, щелкая по кнопке 'Ping'. Сохраните текущие настройки и выйдите из программы.
Завершите работу Windows и перегрузите машину.
В меню пуск выберите программную группу Delphi и запустите Database Explorer.
В Навигаторе баз данных (Database explorer) щелкните на закладке Database. Активизируйте пункт меню Object | New... В диалоговом окне в выпадающем списке должно стоять имя STANDARD. Щелкните на стрелке и выберите из появившегося списка SYBASE.
Теперь там должен быть псевдоним для вашего соединения с Sybase с именем SYBASE1. Убедитесь в том, что это имя выделено. Щелкните в Database Explorer на следующей закладке. В секции "Server Name" (имя сервера) выберите имя одного из серверов, которые вы поместили в ваш SQL.INI, и который пингуется. В секции "User Name" укажите имя пользователя, имеющего права на доступ к определенному в секции "Server Name" серверу. Убедитесь в том, что вы знаете пароль только что назначенного пользователя.
Дважды щелкните на имене псевдонима (SYBASE1) и в появившемся диалоговом окне введите имя пользователя и его пароль. Имя пользователя должно совпадать с именем, определенным в секции "User Name" для псевдонима Sybase. Введите пароль, соответствующий данному пользователю. Нажмите кнопку OK. Теперь около псевдонима Sybase (SYBASE1) вы должны увидеть иконку, обозначающую маленький зеленый ящик. Это означает успешное установление соединения.
Тестирование вашего соединения с помощью Delphi 2.x:
Разместите на пустой форме компоненты TDataSource, TTable и TDBGrid.
В Инспекторе Объектов (Object Inspector) установите для TDataSource свойство DataSet в 'Table1' (без кавычек).
В Инспекторе Объектов установите для TTable имя базы данных в SYBASE1. Переместитесь ниже до свойства TableName, и дважды щелкните на поле редактирования, расположенного около данного свойства. Должно появиться диалоговое окно с требованием ввести имя пользователя и его пароль. При этом должно уже отображаться имя пользователя, которое вы определили в Database Explorer для псевдонима Sybase. Введите соответствующий пароль. Нажмите на кнопку OK.
Теперь вы должны увидеть спискок, состоящий из имен таблиц. Выберите одно.
Щелкните на TDBGrid. Присвойте его свойству DataSource значение DataSource1.
Установите свойство Active компонента TTable в TRUE.
Теперь вы можете увидеть данные в TDBGrid. После запуска приложения должно появиться диалоговое окно с требованием ввести имя пользователя и его пароль. Введите пароль и нажмите OK. Теперь вы должны увидеть данные в табличной сетке.
Сообщения об ошибках:
Ошибка, связанная с невозможностью нахождения сетевой библиотеки: Данная ошибка означает, что программе не удалось найти нужную ей .DLL. Следующие файлы должны распологаться в вашем каталоге \Sybase\DLL:
Libblk.dll
Libcomn.dll
Libcs.dll
Libct.dll
Libintl.dll
Libsrv.dll
Libsybdb.dll
Libtcl.dll
Mscvrt10.dll
Nldecnet.dll
Nlmsnmp.dll
Nlnwadvt.exe
Nlnwlink.dll
Nlwnsck.dll
b>Предостережение:
Данный документ не гарантирует установление соединения с сервером, он демонстрирует самый лучший и быстрый способ сделать это.b>Взято из
Access to table disabled because of previous error. Read failure.
Access to table disabled because of previous error. Read failure.
При добавлении новых записей с помощью метода TTable.AppendRecord в индексированную таблицу FoxPro через какое-то время (то есть при одновременном добавлении большого количества записей) возникает ошибка:
"Access to table disabled because of previous error. Read failure. File <имя_файла.cdx>".
Возможно, причина заключается в том, что операции чтения-записи в файл, содержащий таблицу FoxPro, особенно при использовании кэширования, предоставляемого операционной системой, конфликтуют с содержимым индексного файла (это часто происходит при многопользовательской работе). Дело в том, что ранние версии dBase, FoxPro, Clipper работали с неподдерживаемыми индексами, то есть индексные файлы не обновлялись одновременно с таблицей, и для их синхронизации требовалось выполнять специальные операции. Но соответствующие средства разработки, применявшиеся в то время, обычно не поддерживали никаких аналогов транзакций - записи чаще всего вставлялись по одной.
В случае применения старых версий формата FoxPro следует избегать кэширования при выполнении дисковых операций с файловым сервером, содержащим базу данных FoxPro. Кроме того, следует проверить и, если необходимо, изменить в настройках BDE параметры MINBUFSIZE, MAXBUFSIZE, LOCAL SHARE - возможно, проблема заключается в недостаточной величине буферов BDE для кэширования данных или в одновременном доступе к данным приложений, использующих и не использующих BDE.
Еще одним из способов решения этой проблемы (самым радикальным) является замена FoxPro на какую-нибудь из серверных СУБД. Например, InterBase неплохо справляется с одновременным вводом большого количества записей благодаря некоторым специфическим особенностям архитектуры этого сервера.
Наталия Елманова
Взято с Исходников.ru
Access to table disabled because of previous error. Read failure
При добавлении новых записей с помощью метода TTable.AppendRecord в индексированную таблицу FoxPro через какое-то время (то есть при одновременном добавлении большого количества записей) возникает ошибка:
"Access to table disabled because of previous error. Read failure. File <имя_файла.cdx>".
Возможно, причина заключается в том, что операции чтения-записи в файл, содержащий таблицу FoxPro, особенно при использовании кэширования, предоставляемого операционной системой, конфликтуют с содержимым индексного файла (это часто происходит при многопользовательской работе). Дело в том, что ранние версии dBase, FoxPro, Clipper работали с неподдерживаемыми индексами, то есть индексные файлы не обновлялись одновременно с таблицей, и для их синхронизации требовалось выполнять специальные операции. Но соответствующие средства разработки, применявшиеся в то время, обычно не поддерживали никаких аналогов транзакций - записи чаще всего вставлялись по одной.
В случае применения старых версий формата FoxPro следует избегать кэширования при выполнении дисковых операций с файловым сервером, содержащим базу данных FoxPro. Кроме того, следует проверить и, если необходимо, изменить в настройках BDE параметры MINBUFSIZE, MAXBUFSIZE, LOCAL SHARE - возможно, проблема заключается в недостаточной величине буферов BDE для кэширования данных или в одновременном доступе к данным приложений, использующих и не использующих BDE.
Еще одним из способов решения этой проблемы (самым радикальным) является замена FoxPro на какую-нибудь из серверных СУБД. Например, InterBase неплохо справляется с одновременным вводом большого количества записей благодаря некоторым специфическим особенностям архитектуры этого сервера.
Взято из
Access Violation при передаче неполного параметра
Access Violation при передаче неполного параметра
Автор: Дмитрий Померанцев
Проблема обнаружена под операционной системой Windows 2000 SP3, в среде Delphi6, Delphi7 (скорее всего не зависит от версии Delphi) с использованием Microsoft Jet DB Engine версия 4, SP3.
Некоторый, вполне типичный, код заполнения запроса в процессе выполнения вызывает Access Violation, притом, что согласно документации все должно работать корректно.
Пример кода:
Допустим, есть база данных в MS Access 2000, имеющая таблицу main и в ней целочисленное (INT) поле id в качестве главного ключа. Так же есть компонент ADOQuery1: TADOQuery, для доступа к базе данных. Максимальное значение поля id может быть получено следующим кодом:
ADOQuery1.Active := false;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add('SELECT max(id)'); // -- Сбой здесь !!!
ADOQuery1.SQL.Add('AS idmax');
ADOQuery1.SQL.Add('FROM main');
ADOQuery1.Active := true;
Как было показано в комментарии, исключение возникает в процессе добавления текста в запрос, но при этом в сообщении об ошибке указывалось, что исключение произошло внутри библиотеки Jet.
Исследование исходных текстов компонента TADOQuery показало следущее: свойство SQL, типа TStrings связано с полем FSQL: TStrings, создаваемого как экземляр класса TStringList, при этом объекту FSQL назначается обработчик события OnChange ? метод QueryChanged (protected, статический), что исключает его возможную перегрузку.
Этот метод устанавливает свойство Active в False и присваивает содержимое FSQL.Text полю CommandText объекта ADO.
За отсутствием исходных текстов библиотеки Jet, дальнейшее исследование пришлось прекратить, но можно сделать несколько выводов:
Корни проблемы в невполне корректном поведении как кода от Borland, так и от Microsoft. Компонент TADOQuery передает в ADO неоконченный SQL-запрос, а Jet начинает анализировать этот запрос до того, как он полностью поступит. Возможно, Microsoft пытался реализовать упреждающее выполнение запросов, чтобы снизить время обработки запроса после получения команды на выполнение.
Теоретически и другие драйвера баз данных могут быть чувствительны к неполным запросам, так что данная ошибка может появляться и при работе с другими СУБД.
При дополнительном исследовании были выяснены интересные подробности:
Данный код не прерывает выполнения при возникновении exception, т.е. теоретически даже try..except не нужен. Похоже, это происходит из-за того, что jet является COM-объектом, а их методы вызываются как safecall. Дальнейшие тесты подтвердили это предположение ? при снятии галочки Stop on Delphi Exceptions и в варианте exe-файла ошибка не проявлялась. Таким образом, ситуация несколько меняется ? исключение возникает только в среде разработки, что, правда, является слабым утешением, т.к. многие програмисты работают с настройками по-умолчанию, и в случае его возникновения могут долго ломать голову, ища свою ошибку там где ее нет.
ТИПОВЫЕ РЕШЕНИЯ
1. Передавать запрос целиком ? одной строкой. Пример:
ADOQuery1.Active := false;
ADOQuery1.SQL.Text := 'SELECT max(id) AS idmax FROM main;';
ADOQuery1.Active := true;
2. Отключить галочку Tools->Debugger Options->Language Exceptions->Stop on Delphi Exceptions
3. Просто игнорировать это исключение (в этом случае в процессе разработки придется периодически несколько раз нажимать OK, что, конечно, менее удобно)
Напоследок: Небольшое исследование исходного кода компонент данных BDE и dbExpress показало, что в них передача SQL-запроса происходит через промежуточное текстовое поле, что, на мой взгляд, исключает в них возможность появления аналогичной ошибки.
КОММЕНТАРИЙ:
Компонент TADOQuery от Delphi 5 содержит аналогичный код (метод QueryChanged), приводящий к ошибке.
Еще один вариант решения - использовать стандартные возможности TStrings по управлению обновлением:
ADOQuery1.SQL.BeginUpdate;
try
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add('SELECT max(id)');
ADOQuery1.SQL.Add('AS idmax');
ADOQuery1.SQL.Add('FROM main');
finally
ADOQuery1.SQL.EndUpdate;
end;
В этом случае событие OnChange произойдет только при выполнении EndUpdate.
Взято из
Accessing InterBase via dbExpress with Kylix produces error: Unable to load libgds.so
Accessing InterBase via dbExpress with Kylix produces error: Unable to load libgds.so
If you install the dbExpress InterBase client driver, you will need to have libcrypt.so installed. Some Linux distributions omit this library or do not include it in a base install. If your distribution does not include libcrypt.so, contact the package maintainer, or search online Linux resources, such as {http://rpmfind.net/}.
Some Linux distributions provide all the libraries required to run Kylix, but do not use the naming conventions that Kylix expects. The most common problem is library names with embedded version information, while Kylix expects version-independent names. If Kylix software fails to run because of missing shared libraries, check for similarly named libraries in /lib and /usr/lib. You can then create a symbolic link to help Kylix find the library. For example, if you are missing libcrypt.so but find /lib/libcrypt.so.1, you would enter the following shell commands as root:
cd /lib
ln -s libcrypt.so.1 libcrypt.so
Action, ActionList
Action, ActionList
Cодержание раздела:
ActiveX, COM, DCOM, MIDAS, CORBA, интерфейсы, OLE, DDE
ActiveX, COM, DCOM, MIDAS, CORBA, интерфейсы, OLE, DDE
Cодержание раздела:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
(
раздел)·
(
раздел)·
·
·
·
·
·
·
·
·
·
ADO+Delphi5: постоянные ошибки BOF... или EOF...
ADO+Delphi5: постоянные ошибки BOF... или EOF...
Обновлённый MDAC конфликтует с старыми компонентами VCL.
Путь разрешения проблемы:
Идём на сайт Борланда, скачиваем SP1 для Delphi5 + специальный ADO Patch.
Сам ADO Patch здесь:
Service Pack здесь:
Enterprise:
Professional:
Standard:
Автор Vit
.
Advantage Database Server
Advantage Database Server
1) Краткое описание - Advantage Database Server(ADS) - разработка фирмы Extended System, Inc (http://www.AdvantageDatabase.com). Развивается с начала 90-x годов. Первоначально была известен как Advantage X-Base Server и предназначался для работы в клиент-серверном режиме с таблицами формата dbf(Clipper, Foxpro) и базировался до 4 версии только на Novell платформе. К отличительной особенностью является использование ISAМ(indexed sequentil access method) - индексный последовательный метод доступа. С версии 5.0 появилась версия для NT и добавлен собственный форма таблиц, а с версии 5.5 поддерживается SQL. В настоящее время выпущена версия с номером 7.0
2) возможности -
Небольшое отступление: ADS с таблицами работает в двух режимах Free Tables и Database. Режим Free Tables предполагает работу с таблицами как с отдельными несвязанными с друг другом структурными единицами. При режиме Database группа таблиц рассматривается как единая база данных со всеми вытекающими последствиями. Открыть таблицу, входящую в Database как самостоятельную таблицу в режиме Free Tables невозможно.
- количество баз данных на сервере - Так как физически базы данных представляют набор файлов: таблицы, индексы, словари, то ограничения определяются только возможностями операционной системы и мощностью компьютера. Имеется оганичения на количество одновременно открытых таблиц на одно соединение(не путать с пользователем) - не более 250. Но пользователь может иметь неограниченное количество соединений.
- размер таблиц - Так как в ADS могут использоваться одновременно два типа таблиц, то дается характеристики отдельно на каждую
Формат DBF
Максимальный количество индексов в индексном файле 50
Максимальное число открытых индексных файлов на таблицу 15
Максимальный размер файла (таблица, индексный файл, memo файл) 4 Гб
Максимальное число записей 2 миллиарда
Максимальная длина записи 65530 bytes
Максимальная длина имени поля 10 characters
Максимальная длина имени итдекса 10 characters
Максимальный размер binary/image/BLOB поля 4 Гб
Максимальное число полей на таблицу 2035
Формат ADT
Максимальный количество индексов в индексном файле 50
Максимальное число открытых индексных файлов на таблицу 15
Максимальный размер таблицы
Windows 95/98/ME 4 gigabytes (4,294,967,296 bytes)
Windows NT/2000 with NTFS 16 exabytes (18,446,744,073,709,551,616 bytes)
Windows NT/2000 with FAT32 4 gigabytes (4,294,967,296 bytes)
NetWare 4 gigabytes (4,294,967,296 bytes)
Linux pre-2.1.2 - 11 glibc and pre-2.4 kernel 2 gigabytes (2,147,483,648 bytes)
Linux glibc 2.1.2 - 11+ with kernel 2.4+ 8 exabytes (9,223,372,036,854,775,807 bytes)
Максимальный размер индексного файлa
Windows 95/98/ME 4 gigabytes (4,294,967,296 bytes)
Windows NT/2000 with NTFS 4 gigabytes * (Index Page Size) : Max 35 terabytes
Windows NT/2000 with FAT32 4 gigabytes (4,294,967,296 bytes)
NetWare 4 gigabytes (4,294,967,296 bytes)
Linux pre-2.1.2 - 11 glibc and pre-2.4 kernel 2 gigabytes (2,147,483,648 bytes)
Linux glibc 2.1.2 - 11+ with kernel 2.4+ 4 gigabytes * (Index Page Size) : Max 35 terabytes
Максимальный размер memo файла
Windows 95/98/ME 4 gigabytes (4,294,967,296 bytes)
Windows NT/2000 with NTFS 4 gigabytes * (Memo Page Size) : Max 4 terabytes
Windows NT/2000 with FAT32 4 gigabytes (4,294,967,296 bytes)
NetWare 4 gigabytes (4,294,967,296 bytes)
Linux pre-2.1.2 - 11 glibc and pre-2.4 kernel 2 gigabytes (2,147,483,648 bytes)
Linux glibc 2.1.2 - 11+ with kernel 2.4+ 4 gigabytes * (Memo Page Size) : Max 4 terabytes
Максимальное число записей 2 миллиард .
Максимальная длина записи 65530 bytes
Максимальная длина имени поля 128 characters
Максимальная длина имени индекса 128 characters
Максимальный размер binary/image/BLOB поля 4 Гб
Максимальное число полей в таблице зависит от длинны имени полей , и может быть вычислено: 65135 / ( 10 + AverageFieldNameLength ).
Например, если средняя длина имен полей 10, то максимальное число полей - 3256
Для обоих форматов
Максимальное число транзакций ограничено размером памяти
Максимальное число соеединений ограничено размером памяти
Максимальное число одновременно открытых файлов ограничено размером памяти
Максимальное число блокировок ограничено размером памяти
- количество пользователей и количество одновременных подключений
количество одновременно подключенных пользователей ограничено лицензионными соглашениями, количество соединений на пользователя неограничено.
наличие View -возможность создания View предусмотрена в режиме работы Database. Хранится как объект справочника (dictionary). Могут быть созданы с помощью SQL-выражение CREATE VIEW или с помощью соотвествующего диалогового окна в архитекторе (ARC32)
наличие SP, языка программирования - собственного языка нет, роль SP играют Advantage Extended Procedure (AEP), которые представляют собой либо dll или COM-библиотеки (для Windows), или shared object (для Linux). Соответственно написать их можно практически на чем угодно : Delphi/C++Builder, VB, VC++ и т.д. Для обращения в таблицам используется либо API, либо компоненты (Delphi/C++Builder). Регистрация процедур производится посредством SQL-выражения CREATE PROCEDURE , либо с помощью соотвествующего диалогового окна в архитекторе (ARC32)
Пример AEP
////////////////////////////////////////////////////////////////////////////
// ## Назначение: Точка входа хранимой процедуры "Обновление справочника серий"
// ## Описание:
// ## Аргументы: Параметры хранимой процедуры:
// ## Входные: Нет
// ## Выходные: Таблица со списком новых серий
// ## ID integer - идентификатор записи,
// ## Grup char(5) - группа,
// ## NNum char(13) - номенклатурный номер,
// ## Name char(34) - наименование сертификата,
// ## Series char(25) - серия
// ## Возврат: Код ошибки
// ## Исключения: нет
extern "C" UNSIGNED32 __declspec(dllexport) WINAPI RefreshSeries
(
UNSIGNED32 a_ConnectionID, // Идентификатор сессии
UNSIGNED8 *a_UserName, // Имя пользователя (логин)
UNSIGNED8 *a_Password, // Пароль пользователя
UNSIGNED8 *a_ProcName, // Имя хранимой процедуры(не пользоваться:
// будет исключено в следующей версии
UNSIGNED32 a_RecNum, // Аргумент зарезервирован для тригера
UNSIGNED8 *a_InpName, // Имя таблицы входных аргументов хранимой процедуры
UNSIGNED8 *a_OutName // Имя таблицы выходных аргументов хранимой процедуры
// или возращаемого курсора данных
)
{
try
{
TModuleAEP* ModuleAEP = (TModuleAEP*)gAEPSessionMgr->GetDM(a_ConnectionID);
ModuleAEP->ParamsReconnect((char*)a_InpName, (char*)a_OutName);
ModuleAEP->RefreshSeries();
}
catch( EADSDatabaseError *E )
{
return E->ACEErrorCode;
}
return AE_SUCCESS;
}
void __fastcall TCertModuleAEP::RefreshSeries(void)
{
try
{
FreeSeries_->Active = true;
Series_->Active = true;
NewSeries_->Active = true;
try
{
Series_->Last();
int LastID = Series_ID->AsInteger;
NewSeries_->AdsCopyTableContents(Series_);
Series_->Filter = Format("ID > %d",ARRAYOFCONST((LastID)));
Series_->Filtered = true;
Series_->AdsCopyTableContents(FreeSeries_);
Series_->AdsCopyTableContents(Output_);
}
__finally
{
Series_->Filtered = false;
Series_->Active = false;
FreeSeries_->Active = false;
NewSeries_->Active = false;
}
}
catch(Exception& Exc)
{
Output_->Append();
Output_->FieldByName("Name")->AsString = Exc.Message;
Output_->Post();
}
}
- наличие триггеров - имееются с в режиме Database, начиная с версии 7. Поддерживаются три вида триггеров BEFORE, AFTER и INSTEAD OF. Триггера могут быть написаны либо также как AEP, в виде dll, COM, либо они могут предствалять из себя SQL-выражение
CREATE TRIGGER mytrigger ON orders AFTER DELETE BEGIN INSERT INTO backup_orders SELECT * FROM __old; END
CREATE TRIGGER mytrigger ON orders INSTEAD OF UPDATE
FUNCTION MyFunction IN ASSEMBLY MyAssembly.MyClass PRIORITY 2
Пример кода тригера INSTEAD OF INSERT, который заполняет поле при вставке новой записи значением GUID
library AutoGUID;
{$INCLUDE versions.inc}
{$IFDEF ADSDELPHI7_OR_NEWER}
{$WARN UNSAFE_TYPE OFF}
{$WARN UNSAFE_CODE OFF}
{$WARN UNSAFE_CAST OFF}
{$ENDIF}
uses
SysUtils,
Classes,
ace,
adscnnct,
adsset,
adsdata,
adstable,
COMobj;
// Utility Function Prototype
procedure SetError ( conn : TAdsConnection; code : UNSIGNED32; err : string ); forward;
// Sample Advantage Trigger function. If you change the name of this
// function, remember to also change the name in the exports list at the bottom
// of this file.
function InsertGUID
(
ulConnectionID : UNSIGNED32; // (I) Unique ID identifying the user causing this trig
hConnection : ADSHANDLE; // (I) Active ACE connection handle user can perform
// operations on
pcTriggerName : PChar; // (I) Name of the trigger object in the dictionary
pcTableName : PChar; // (I) Name of the base table that caused the trigger
ulEventType : UNSIGNED32; // (I) Flag with event type (insert, update, etc.)
ulTriggerType : UNSIGNED32; // (I) Flag with trigger type (before, after, etc.)
ulRecNo : UNSIGNED32 // (I) Record number of the record being modified
) : UNSIGNED32;
{$IFDEF WIN32}stdcall;{$ENDIF}{$IFDEF LINUX}cdecl;{$ENDIF} // Do not change the prototype.
const
// In this case, the first field is the Primary Key field
// in the base table that needs the AutoGUID value.
// This constant definition is necessary because
// triggers don't take parameters.
iGUIDfieldNum : Integer = 0;
var
oConn : TAdsConnection;
oNewTable, oSourceTable : TAdsTable;
iFieldNum : Integer;
begin
// Result is currently reserved and not used. Always return zero.
Result := 0;
// Allocate a connection object using an active connection, no need to open it after this.
oConn := TAdsConnection.CreateWithHandle( nil, hConnection );
try
try
oConn.Name := 'conn';
oNewTable := TAdsTable.Create( nil );
oNewTable.DatabaseName := oConn.Name;
oNewTable.TableName := '__new';
oNewTable.Open;
oSourceTable := TAdsTable.Create( nil );
oSourceTable.DatabaseName := oConn.Name;
oSourceTable.TableName := pcTableName;
oSourceTable.Open;
oSourceTable.Insert;
// Copy all new field values over without posting.
for iFieldNum := 0 to Pred(oSourceTable.FieldCount) do
if not oNewTable.Fields[iFieldNum].IsNull then
oSourceTable.Fields[iFieldNum].Value :=
oNewTable.Fields[iFieldNum].Value
else
oSourceTable.Fields[iFieldNum].Clear;
// Now set the GUID field value to a GUID value.
oSourceTable.Fields[iGUIDfieldNum].AsString := CreateClassID;
oSourceTable.Post;
except
on E : EADSDatabaseError do
SetError( oConn, E.ACEErrorCode, E.message );
on E : Exception do
SetError( oConn, 0, E.message );
end;
finally
FreeAndNil(oSourceTable);
FreeAndNil(oNewTable);
FreeAndNil(oConn);
end;
end;
// Utility function to return an error from a trigger.
procedure SetError
(
conn : TAdsConnection;
code : UNSIGNED32;
err : string
);
begin
// Errors can be returned by placing a row into the __error table.
conn.Execute( 'INSERT INTO __error VALUES( ' + IntToStr( code ) +
', ' + QuotedStr( err ) + ' )' );
end;
exports
InsertGUID;
begin
// Because this DLL is used by a multi-threaded application (the Advantage
// server), we must set the Delphi IsMultiThread global variable to TRUE.
IsMultiThread := TRUE;
end.
- репликация и синхронизация, перенос данных, средства backup - встроенных механизмов в настоящее время нет, их появление запланировано в версии 8, но возможно использовании внешнего Advantage Replication, выполненного на основе сервера приложений этой же фирмы - OneBridge Mobile Groupware (ранее известного как XTNDConnect Server). Кстати этот репликатор может применяться как между серверам различных(других) фирм, так и для синхронизации баз данных между клиентом и сервером в режиме работы briefcase. Синхронизация может проходить по определенному алгоритму с заданием полей, приоритетов и правил разрешений конфликтов. Кроме того, по-скольку базы данных представляют собой набор файлов возможно использование стандартных файловых backup-систем.
- поддержка кластеров - в настоящее время,нет. Запланировано в версии 7.1
- возможность взаимодействия между серверами, включая сервера других типов. - Непосредствено в одном запросе обратиться к двум таблицам из разных баз данных, расположенных физически на разных серверах нельзя. Такую операцию можно сделать если
1). базы данных расположены на одном сервере
2). запрос направлен к локальному серверу и нужно к еще подключить таблицу(ы) от удаленного сервера.
В этом случае используется понятие Link, при создании которого указываеися алиас, путь к справочнику БД, имя пользователя и пароль, под которым происходит подключение. Если имя пользователя и пароль не задан, то подключение будет производится под тем же именем, под которым пользователь подключен к текущей
-- в примерах backup и Link1 - линки к другим базам данных
UPDATE Customers SET address = ( SELECT address FROM backup.Customers b WHERE b.cust_id = Customers.cust_id )
CREATE VIEW Link1_T1 AS SELECT cust_name FROM Link1.customers WHERE credit_limit > 50000
Кроме того, так как хранимые процедуры - это dll, COM или shared object, то посредством них можно обеспечить доступ к любым СУБД, например: подключиться к MS SQL, получить данные, вставить эти данные в набор, заполнить поля дополнительными данными из таблицы ADS, и этот набор вернуть в качестве результата работы AEP.
Можно, например, организовать цепочку вызовов процедур на удаленных серверах, например :
Клиент - > AEP(Server1)
AEP(Server1)->AEP(Server2) ->AEP(Server4)->...
AEP(SErver1)->AEP(Server4)
и т.д
- поддерживаемые типы данных -
Формат DBF
Character -- фиксированая строка(
Numeric -- число с фиксированной запятой.
Data -- дата(CCYYMMDD).
Logical -- логическое значение ('0', '1', 'T', 't', 'Y', and 'y').
Memo -- мемо-поле
Формат ADT
Character -- фиксированная строка
Date -- Дата.
Logical -- логическое значение
Memo -- memo-поле для строковых данных
Double -- число с плавающей запятой
Integer -- целое
Image -- мемополе содержащее графические данные
Binary -- мемополе для бинарных
ShortInteger -- короткое целое (-32,767 to 32,766)
Time -- время.
TimeStamp -- Дата-время
AutoIncrement -- автоинкрементное поле (0 to 4,294,967,296)
Raw -- типонезависимое поле фиксированоой длины (1 to 65530)
CurDouble -- поле для денежных расчетов (хранится два знака после запятой)
Money -- храниться четыре знака после запятой
поддерживаемые конструкции SQL-
ALTER TABLE
BEGIN TRANSACTION
COMMIT WORK
CREATE DATABASE - создание базы данных
CREATE INDEX
CREATE PROCEDURE
CREATE TABLE
CREATE TRIGGER
CREATE VIEW
DELETE
DROP INDEX
DROP PROCEDURE
DROP TABLE
DROP TRIGGER
DROP VIEW
EXECUTE PROCEDURE
GRANT -- давать права пользователю(группе пользователей) на выполнение операции на таблице(столбце)
QUOTE
// разрешается sales_group просматривать поле accounts таблицы customers GRANT SELECT ON customers.accounts TO sales_group //разрешается user1 вставке записи вводить значение для поля accounts таблицы customers GRANT INSERT( accounts ) ON customers TO user1 //для managers разрешаются все действия над таблицей customers GRANT ALL ON customers TO managers
GRANT быть применен к таблице(столбцу), view, процедуре линку со следующими ключами (в зависимости от типа объекта и типа операции)
SELECT, SELECT( columnname )
INSERT, INSERT( columnname )
UPDATE, UPDATE( columnname )
ACCESS
EXECUTE
INHERIT
ALL
INSERT
REVOKE -- запрещать пользователю(группе пользователей) выполнение операции (см GRANT)
ROLLBACK WORK
SELECT
SET TRANSACTION
UPDATE
Кроме того имеется возможность получить с помощью SQL - выражения всю информацию по метаданным используюя системные псевдо таблицы
Например, получение всех объектов из справочника БД
QUOTE
SELECT * FROM system.objects
system.dictinary - информация об базе данных :версия (не путать с версией сервера, имеется в виду именно версия БД), путь, ключ шифрования(если применено и только если запршивает админ), разрешение работы через интернет, прав доступа и .тд.
system.objects - все объекты
system.tables - таблицы
system.columns - столбцы
system.users
system.usersgroup
system.usergroupmembers
system.indexfiles
system.indexes
system.permission
system.relation -
system.views
system.storedprocedures
system.links
system.triggers
- поддержка транзакций - есть
- системы репортинга, в том числе для Web - возможно использование других репортинговых систем: Crystal Report, Fast Report, Quick Report, Rave и д.р. Собственного репортинга ориентированного на Web нет.
- наличие собственного агента для выполнения заданий по расписанию - нет \
3) Защита данных, шифрование - Возможно как шифрование отдельно взятых таблиц, так и шифрование базы данных в целом(вместе с метаданными). Используется профессиональный 160-битный алгоритм шифрования.
4) простота использования - Сам сервер после установки в администрировании не нужается. Адинистрирование необходимо только для текущего ведения(создание, реорганизация, модификация) баз данных и таблиц
- наличие встроенных средств администрирования с GUI интерфейсом - Имеется менеджер ARC32, реализующего все функции для создания баз данных и манипуляции с ними.
- возможность удалённого и Web администрирования - так как сервер имеет встроенный инернет-сервис(до шестой версии это был отдельный продукт) , то имеется возможность подключиться к серверу через интернет(например с помпощь того же ARC32) и выполнить все операции по реорганизации БД удаленно
- сложность перевода проекта написанного под другую базу данных на рассматриваемую - В зависимости от первоначальной БЗ, стпень переноса может быть разной. Программы, написанные на Clipper могут быть перенесены достаточно легко. Для того, что бы Clipper приложение интегрировать с Advantage необходимо его перелинковать с новым RDD. Для линковки (сборки) нужны OBJ модули. Если потерян только код, а OBJ сохранились, то проблем нет. А вот если нет OBJ, то единственное, что можно сделать - попытаться программой типа DECLIP восстановить исходные коды или подменить драйвер. Эта утилита распространяется бесплатно, она есть на многих BBS и в интернете. Имеется и также возможность импортирования таблиц (встроена в ACR32) в следующих вариантах подключения
-- ADO Data Source
-- Paradox, dBase, Advantage Compatible
-- BDE
-- PervasiveSQL(Btrieve)
-- Text file
Имеется также методика конвертирования приложений, использующих TTablе:
Converting Delphi TTable Instances to Advantage TAdsTable Instances
- сложность в установке и настройке - установка автоматическая, проблем не возникает
- насколько сложно администрирование сервера - администрирование практически не требуется
- наличие утилит для автоматизации операций для работы в командной строке - имеется в менеджере ARC32
- наличие собственных утилит для отладки запросов (выполнение SQL, построение плана выполнения кверей, профайлер и т.п.), утилиты для слежения за производительностью сервера. - ARC32
5) платформы
- на которых может работать сервер - - Npvell, Windows 9X, WinNT/2000, Linux
- на которых может работать клиент - MS DOS, Windows 9X, WinNT/2000, Linux
6) версии продуктов, краткая характеристика отличий
Текущая весия 7.0, техническая поддержка оказывается для версий 6.xx
Версия 5.0 - поддержка сервера на платформы WinNT(ранее был только Novell)
Версия 5.5 - поддержка SQL
Версия 5.7 - поддержка сервера на плптформе Win9x, поддержка транзакций
Версия 6.0 - поддержка сервера и клиента на платформе Linux, работа в режиме Database, хранимые процедуры, поддерка ссылочной целостности.
Версия 7.0 - триггера
7) способы доступа
Advantage Client Engine API
Advantage .NET Data Provider
Advantage ODBC Driver (version 3)
Advantage JDBC Driver (Type 4)
Advantage OLE DB Provider
Advantage Perl DBI Driver
Advantage PHP Extension
Advantage CA-Clipper RDD
Advantage CA-Visual Objects RDDs
Набор компонентов Advantage TDataSet Descendant for Delphi/Kylix/C++Builder
- языки программирования - Clipper, Delphi/C++Builder/Kylix, Microsoft Visual Basic, Microsoft Visual C/C++, Java, Perl , Php, CA-Visual Objects
Автор Vyacheslav (vingrad.ru)
Алгоритм градиентной заливки
Алгоритм градиентной заливки
Иногда бывает нужно сложить два или более цветов для получения что-то типа переходного цвета. Делается это весьма просто. Координаты получаемого цвета будут равны среднему значению соответствующих координат всех цветов.
Например, нужно сложить красный и синий. Получаем
(255,0,0)+(0,0,255)=((255+0)div 2,(0+0) div 2,(0+255) div 2)=(127,0,127).
В результате получаем сиреневый цвет. Также надо поступать, если цветов более чем 2: сложить соответствующие координаты, потом каждую сумму разделить нацело на количество цветов.
Поговорим теперь о градиентной заливке. Градиентная заливка - это заливка цветом с плавным переходом от одного цвета к другому.
Итак, пусть заданы 2 цвета своими координатами ((A1, A2, A3) и (B1, B2, B3)) и линия (длиной h пикселов), по которой нужно залить. Тогда каждый цвет каждого пиксела, находящегося на расстоянии x пикселов от начала будет равен (A1-(A1-B1)/h*x, A2-(A2-B2)/h*x, A3-(A3-B3)/h*x). Теперь, имея линию с градиентной заливкой, можно таким образом залить совершенно любую фигуру: будь то прямоугольник, круг или просто произвольная фигура.
Вот как выглядит описанный алгоритм:
{Считается, что координаты первого цвета
равны (A1, A2, A3), а второго (B1, B2, B3)
Кроме того, линия начинается в координатах
(X1,Y1), а заканчивается в (X2,Y1)}
var
h, i: integer;
begin
h:=X2-X1-1;
for i:=0 to h do
with PaintBox1.Canvas do
begin
Pen.Color:=RGB(A1-(A1-B1)/h*i, A2-(A2-B2)/h*i, A3-(A3-B3)/h*i);
Rectangle(I,Y1,I+1,Y1);
end;
end.
Взято из
Алгоритм переноса русского текста по слогам?
Алгоритм переноса русского текста по слогам?
interface
uses
Windows,Classes,SysUtils;
Function SetHyph(pc:PChar;MaxSize:Integer):PChar;
Function SetHyphString(s : String):String;
Function MayBeHyph(p:PChar;pos:Integer):Boolean;
implementation
Type
TSymbol=(st_Empty,st_NoDefined,st_Glas,st_Sogl,st_Spec);
TSymbAR=array [0..1000] of TSymbol;
PSymbAr=^TSymbAr;
Const
HypSymb=#$1F;
Spaces=[' ', ',',';', ':','.','?','!','/', #10, #13 ];
GlasCHAR=['?', 'L', 'х', '+', 'v', '-','р', '-', 'ю', '+', ' ', '-',
'ш', 'L', '¦', '¦', '?', '¦',
{ english }
'e', 'E', 'u', 'U','i', 'I', 'o', 'O', 'a', 'A', 'j', 'J'
];
SoglChar=['?', 'г' , 'ъ', '¦' ,'э', '=' , 'у', '+' , '°', '+' , '-' ,
'ч', '¦' , '?', '-' ,'?', 'L' , 'т', 'T' , 'я', '¦' , 'Ё', '¦' ,
'ы', 'T' , 'ф', '-' ,'ц', '¦' , '?', '+' , 'ё', 'T' , 'ь', '¦' ,
'?', 'T' , 'с', '+' ,
{ english }
'q', 'Q','w', 'W', 'r', 'R','t', 'T','y', 'Y','p', 'P','s',
'S',
'd', 'D','f', 'F', 'g', 'G','h', 'H','k', 'K','l', 'L','z',
'Z',
'x', 'X','c', 'C', 'v', 'V', 'b', 'B', 'n', 'N','m', 'M' ];
SpecSign= [ '·', '-','c', '-', 'щ', 'г'];
Function isSogl(c:Char):Boolean;
begin
Result:=c in SoglChar;
end;
Function isGlas(c:Char):Boolean;
begin
Result:=c in GlasChar;
end;
Function isSpecSign(c:Char):Boolean;
begin
Result:=c in SpecSign;
end;
Function GetSymbType(c:Char):TSymbol;
begin
if isSogl(c) then begin Result:=st_Sogl;exit;end;
if isGlas(c) then begin Result:=st_Glas;exit;end;
if isSpecSign(c) then begin Result:=st_Spec;exit;end;
Result:=st_NoDefined;
end;
Function isSlogMore(c:pSymbAr;start,len:Integer):Boolean;
var i:Integer;
glFlag:Boolean;
begin
glFlag:=false;
for i:=Start to Len-1 do
begin
if c^[i]=st_NoDefined then begin Result:=false;exit;end;
if (c^[i]=st_Glas)and((c^[i+1]<>st_Nodefined)or(i<>Start))
then
begin
Result:=True;
exit;
end;
end;
Result:=false;
end;
{ Ёрёё?рты ыър яхЁхэюёют }
Function SetHyph(pc:PChar;MaxSize:Integer):PChar;
var
HypBuff : Pointer;
h : PSymbAr;
i : Integer;
len : Integer;
Cur : Integer; { }
cw : Integer; { =юьхЁ с?ътv т ёыютх }
Lock: Integer; { ё?х??шъ сыюъшЁютюъ }
begin
Cur:=0;
len := StrLen(pc);
if (MaxSize=0)OR(Len=0) then
begin
Result:=nil;
Exit;
end;
GetMem(HypBuff,MaxSize);
GetMem(h,Len+1);
for i:=0 to len-1 do h^[i]:=GetSymbType(pc[i]);
cw:=0;
Lock:=0;
for i:=0 to Len-1 do
begin
PChar(HypBuff)[cur]:=PChar(pc)[i];Inc(Cur);
if i>=Len-2 then Continue;
if h^[i]=st_NoDefined then begin cw:=0;Continue;end else Inc(cw);
if Lock<>0 then begin Dec(Lock);Continue;end;
if cw<=1 then Continue;
if not(isSlogMore(h,i+1,len)) then Continue;
if
(h^[i]=st_Sogl)and(h^[i-1]=st_Glas)and(h^[i+1]=st_Sogl)and(h^[i+2]<>st_Spec)
then begin PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1;end;
if
(h^[i]=st_Glas)and(h^[i-1]=st_Sogl)and(h^[i+1]=st_Sogl)and(h^[i+2]=st_Glas)
then begin PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1;end;
if
(h^[i]=st_Glas)and(h^[i-1]=st_Sogl)and(h^[i+1]=st_Glas)and(h^[i+2]=st_Sogl)
then begin PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1;end;
if (h^[i]=st_Spec) then begin
PChar(HypBuff)[cur]:=HypSymb;Inc(Cur);Lock:=1; end;
end;
{}
FreeMem(h,Len+1);
PChar(HypBuff)[cur]:=#0;
Result:=HypBuff;
end;
Function Red_GlasMore(p:Pchar;pos:Integer):Boolean;
begin
While p[pos]<>#0 do
begin
if p[pos] in Spaces then begin Result:=False; Exit; end;
if isGlas(p[pos]) then begin Result:=True; Exit; end;
Inc(pos);
end;
Result:=False;
end;
Function Red_SlogMore(p:Pchar;pos:Integer):Boolean;
Var BeSogl,BeGlas:Boolean;
begin
BeSogl:=False;
BeGlas:=False;
While p[pos]<>#0 do
begin
if p[pos] in Spaces then Break;
if Not BeGlas then BeGlas:=isGlas(p[pos]);
if Not BeSogl then BeSogl:=isSogl(p[pos]);
Inc(pos);
end;
Result:=BeGlas and BeSogl;
end;
Function MayBeHyph(p:PChar;pos:Integer):Boolean;
var i:Integer;
len:Integer;
begin
i:=pos;
Len:=StrLen(p);
Result:=
(Len>3)
AND
(i>2)
AND
(i<Len-2)
AND
(not (p[i] in Spaces))
AND
(not (p[i+1] in Spaces))
AND
(not (p[i-1] in Spaces))
AND
(
(isSogl(p[i])and isGlas(p[i-1])and isSogl(p[i+1])and
Red_SlogMore(p,i+1))
OR
((isGlas(p[i]))and(isSogl(p[i-1]))and(isSogl(p[i+1]))and(isGlas(p[i+2])))
OR
((isGlas(p[i]))and(isSogl(p[i-1]))and(isGlas(p[i+1])) and
Red_SlogMore(p,i+1) )
OR
((isSpecSign(p[i])))
);
end;
Function SetHyphString(s : String):String;
Var Res:PChar;
begin
Res:=SetHyph(PChar(S),Length(S)*2)
Result:=Res;
FreeMem(Res,Length(S)*2);
end;
end.
Alex Gorbunov
acdc@media-press.donetsk.ua
www.media-press.donetsk.ua
(2:465/85.4)
.
Взято из
FAQ:Delphi and Windows API Tips'n'Tricks
olmal@mail.ru
http://www.chat.ru/~olmal
Алгоритм поворота изображения
Алгоритм поворота изображения
Вот алгоритм поворота изображения. Пусть O - это центр поворота, а M - некая точка исходного изображения.
Для каждой точки M нужно найти угол alpha между отрезком OM и горизонталью и длину r отрезка OM.
Теперь, чтобы повернуть изображение на угол beta, нужно каждой точке M
присвоить цвет точки исходного изображения с координатами x,y, где
x = xo + r * cos(alpha + beta)
y = yo + r * sin(alpha + beta)
(xo,yo - центр поворота, r - длина отрезка OM).
Важно именно каждой точке нового изображения сопоставлять точку старого изображения,
а не наоборот, так как иначе некоторые точки нового изображения останутся не закрашенными.
Эту программу можно сильно ускорить, если исходное изображение записать в массив и
обращаться к реальной переменной, а не к свойству Canvas.Pixels.
uses Math;
procedure TForm1.Button1Click(Sender: TObject);
var
bm, bm1: TBitMap;
x, y: integer;
r, a: single;
xo, yo: integer;
s, c: extended;
begin
bm := TBitMap.Create;
bm.LoadFromFile('ex.bmp');
xo := bm.Width div 2;
yo := bm.Height div 2;
bm1 := TBitMap.Create;
bm1.Width := bm.Width;
bm1.Height := bm.Height;
a := 0;
repeat
for y := 0 to bm.Height - 1 do begin
for x := 0 to bm.Width - 1 do begin
r := sqrt(sqr(x - xo) + sqr(y - yo));
SinCos(a + arctan2((y - yo), (x - xo)), s, c);
bm1.Canvas.Pixels[x,y] := bm.Canvas.Pixels[
round(xo + r * c), round(yo + r * s)];
end;
Application.ProcessMessages;
end;
Form1.Canvas.Draw(xo, yo, bm1);
a := a + 0.05;
Application.ProcessMessages;
until Form1.Tag <> 0;
bm.Destroy;
bm1.Destroy;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Form1.Tag := 1;
end;
Взято с сайта
Алгоритм шифрования MD5
Functionmd5(s:string):string;
var a:array[0..15] of byte;
i:integer;
LenHi, LenLo: longword;
Index: DWord;
HashBuffer: array[0..63] of byte;
CurrentHash: array[0..3] of DWord;
procedure Burn;
begin
LenHi:= 0; LenLo:= 0;
Index:= 0;
FillChar(HashBuffer,Sizeof(HashBuffer),0);
FillChar(CurrentHash,Sizeof(CurrentHash),0);
end;
procedure Init;
begin
Burn;
CurrentHash[0]:= $67452301;
CurrentHash[1]:= $efcdab89;
CurrentHash[2]:= $98badcfe;
CurrentHash[3]:= $10325476;
end;
function LRot32(a, b: longword): longword;
begin
Result:= (a shl b) or (a shr (32-b));
end;
procedure Compress;
var
Data: array[0..15] of dword;
A, B, C, D: dword;
begin
Move(HashBuffer,Data,Sizeof(Data));
A:= CurrentHash[0];
B:= CurrentHash[1];
C:= CurrentHash[2];
D:= CurrentHash[3];
A:= B + LRot32(A + (D xor (B and (C xor D))) + Data[ 0] + $d76aa478,7);
D:= A + LRot32(D + (C xor (A and (B xor C))) + Data[ 1] + $e8c7b756,12);
C:= D + LRot32(C + (B xor (D and (A xor B))) + Data[ 2] + $242070db,17);
B:= C + LRot32(B + (A xor (C and (D xor A))) + Data[ 3] + $c1bdceee,22);
A:= B + LRot32(A + (D xor (B and (C xor D))) + Data[ 4] + $f57c0faf,7);
D:= A + LRot32(D + (C xor (A and (B xor C))) + Data[ 5] + $4787c62a,12);
C:= D + LRot32(C + (B xor (D and (A xor B))) + Data[ 6] + $a8304613,17);
B:= C + LRot32(B + (A xor (C and (D xor A))) + Data[ 7] + $fd469501,22);
A:= B + LRot32(A + (D xor (B and (C xor D))) + Data[ 8] + $698098d8,7);
D:= A + LRot32(D + (C xor (A and (B xor C))) + Data[ 9] + $8b44f7af,12);
C:= D + LRot32(C + (B xor (D and (A xor B))) + Data[10] + $ffff5bb1,17);
B:= C + LRot32(B + (A xor (C and (D xor A))) + Data[11] + $895cd7be,22);
A:= B + LRot32(A + (D xor (B and (C xor D))) + Data[12] + $6b901122,7);
D:= A + LRot32(D + (C xor (A and (B xor C))) + Data[13] + $fd987193,12);
C:= D + LRot32(C + (B xor (D and (A xor B))) + Data[14] + $a679438e,17);
B:= C + LRot32(B + (A xor (C and (D xor A))) + Data[15] + $49b40821,22);
A:= B + LRot32(A + (C xor (D and (B xor C))) + Data[ 1] + $f61e2562,5);
D:= A + LRot32(D + (B xor (C and (A xor B))) + Data[ 6] + $c040b340,9);
C:= D + LRot32(C + (A xor (B and (D xor A))) + Data[11] + $265e5a51,14);
B:= C + LRot32(B + (D xor (A and (C xor D))) + Data[ 0] + $e9b6c7aa,20);
A:= B + LRot32(A + (C xor (D and (B xor C))) + Data[ 5] + $d62f105d,5);
D:= A + LRot32(D + (B xor (C and (A xor B))) + Data[10] + $02441453,9);
C:= D + LRot32(C + (A xor (B and (D xor A))) + Data[15] + $d8a1e681,14);
B:= C + LRot32(B + (D xor (A and (C xor D))) + Data[ 4] + $e7d3fbc8,20);
A:= B + LRot32(A + (C xor (D and (B xor C))) + Data[ 9] + $21e1cde6,5);
D:= A + LRot32(D + (B xor (C and (A xor B))) + Data[14] + $c33707d6,9);
C:= D + LRot32(C + (A xor (B and (D xor A))) + Data[ 3] + $f4d50d87,14);
B:= C + LRot32(B + (D xor (A and (C xor D))) + Data[ 8] + $455a14ed,20);
A:= B + LRot32(A + (C xor (D and (B xor C))) + Data[13] + $a9e3e905,5);
D:= A + LRot32(D + (B xor (C and (A xor B))) + Data[ 2] + $fcefa3f8,9);
C:= D + LRot32(C + (A xor (B and (D xor A))) + Data[ 7] + $676f02d9,14);
B:= C + LRot32(B + (D xor (A and (C xor D))) + Data[12] + $8d2a4c8a,20);
A:= B + LRot32(A + (B xor C xor D) + Data[ 5] + $fffa3942,4);
D:= A + LRot32(D + (A xor B xor C) + Data[ 8] + $8771f681,11);
C:= D + LRot32(C + (D xor A xor B) + Data[11] + $6d9d6122,16);
B:= C + LRot32(B + (C xor D xor A) + Data[14] + $fde5380c,23);
A:= B + LRot32(A + (B xor C xor D) + Data[ 1] + $a4beea44,4);
D:= A + LRot32(D + (A xor B xor C) + Data[ 4] + $4bdecfa9,11);
C:= D + LRot32(C + (D xor A xor B) + Data[ 7] + $f6bb4b60,16);
B:= C + LRot32(B + (C xor D xor A) + Data[10] + $bebfbc70,23);
A:= B + LRot32(A + (B xor C xor D) + Data[13] + $289b7ec6,4);
D:= A + LRot32(D + (A xor B xor C) + Data[ 0] + $eaa127fa,11);
C:= D + LRot32(C + (D xor A xor B) + Data[ 3] + $d4ef3085,16);
B:= C + LRot32(B + (C xor D xor A) + Data[ 6] + $04881d05,23);
A:= B + LRot32(A + (B xor C xor D) + Data[ 9] + $d9d4d039,4);
D:= A + LRot32(D + (A xor B xor C) + Data[12] + $e6db99e5,11);
C:= D + LRot32(C + (D xor A xor B) + Data[15] + $1fa27cf8,16);
B:= C + LRot32(B + (C xor D xor A) + Data[ 2] + $c4ac5665,23);
A:= B + LRot32(A + (C xor (B or (not D))) + Data[ 0] + $f4292244,6);
D:= A + LRot32(D + (B xor (A or (not C))) + Data[ 7] + $432aff97,10);
C:= D + LRot32(C + (A xor (D or (not B))) + Data[14] + $ab9423a7,15);
B:= C + LRot32(B + (D xor (C or (not A))) + Data[ 5] + $fc93a039,21);
A:= B + LRot32(A + (C xor (B or (not D))) + Data[12] + $655b59c3,6);
D:= A + LRot32(D + (B xor (A or (not C))) + Data[ 3] + $8f0ccc92,10);
C:= D + LRot32(C + (A xor (D or (not B))) + Data[10] + $ffeff47d,15);
B:= C + LRot32(B + (D xor (C or (not A))) + Data[ 1] + $85845dd1,21);
A:= B + LRot32(A + (C xor (B or (not D))) + Data[ 8] + $6fa87e4f,6);
D:= A + LRot32(D + (B xor (A or (not C))) + Data[15] + $fe2ce6e0,10);
C:= D + LRot32(C + (A xor (D or (not B))) + Data[ 6] + $a3014314,15);
B:= C + LRot32(B + (D xor (C or (not A))) + Data[13] + $4e0811a1,21);
A:= B + LRot32(A + (C xor (B or (not D))) + Data[ 4] + $f7537e82,6);
D:= A + LRot32(D + (B xor (A or (not C))) + Data[11] + $bd3af235,10);
C:= D + LRot32(C + (A xor (D or (not B))) + Data[ 2] + $2ad7d2bb,15);
B:= C + LRot32(B + (D xor (C or (not A))) + Data[ 9] + $eb86d391,21);
Inc(CurrentHash[0],A);
Inc(CurrentHash[1],B);
Inc(CurrentHash[2],C);
Inc(CurrentHash[3],D);
Index:= 0;
FillChar(HashBuffer,Sizeof(HashBuffer),0);
end;
procedure Update(const Buffer; Size: longword);
var
PBuf: ^byte;
begin
Inc(LenHi,Size shr 29);
Inc(LenLo,Size*8);
if LenLo< (Size*8) then
Inc(LenHi);
PBuf:= @Buffer;
while Size> 0 do
begin
if (Sizeof(HashBuffer)-Index)<= DWord(Size) then
begin
Move(PBuf^,HashBuffer[Index],Sizeof(HashBuffer)-Index);
Dec(Size,Sizeof(HashBuffer)-Index);
Inc(PBuf,Sizeof(HashBuffer)-Index);
Compress;
end
else
begin
Move(PBuf^,HashBuffer[Index],Size);
Inc(Index,Size);
Size:= 0;
end;
end;
end;
procedure Final(var Digest);
begin
HashBuffer[Index]:= $80;
if Index>= 56 then Compress;
PDWord(@HashBuffer[56])^:= LenLo;
PDWord(@HashBuffer[60])^:= LenHi;
Compress;
Move(CurrentHash,Digest,Sizeof(CurrentHash));
Burn;
end;
begin
Init;
Update(s[1],Length(s));
Final(a);
result:='';
for i:=0 to 15 do
result:=result+IntToHex(a[i],0);
Burn;
end;
Автор:
VitВзято из
Алгоритмы потухания текста и обратного ему эффекта
Алгоритмы потухания текста и обратного ему эффекта
Вот небольшой участок кода из купленного мною CD-ROM "How To Book". Файл с именем "HowUtils.Pas" содержит реализацию алгоритма "потухания" текста и обратного ему эффекта на холсте, откуда вы можете почерпнуть необходимую вам информацию.
functionTFadeEffect.FadeInText(Target: TCanvas; X, Y: integer; FText: string):
TRect;
var
Pic: TBitmap;
W, H: integer;
PicRect, TarRect: TRect;
begin
Pic := TBitmap.Create;
Pic.Canvas.Font := Target.Font;
W := Pic.Canvas.TextWidth(FText);
H := Pic.Canvas.TextHeight(FText);
Pic.Width := W;
Pic.Height := H;
PicRect := Rect(0, 0, W, H);
TarRect := Rect(X, Y, X + W, Y + H);
Pic.Canvas.CopyRect(PicRect, Target, TarRect);
SetBkMode(Pic.Canvas.Handle, Transparent);
Pic.Canvas.TextOut(0, 0, FText);
FadeInto(Target, X, Y, Pic);
Pic.Free;
FadeInText := TarRect;
end;
procedure TFadeEffect.FadeOutText(Target: TCanvas; TarRect: TRect; Orig:
TBitmap);
var
Pic: TBitmap;
PicRect: TRect;
begin
Pic := TBitmap.Create;
Pic.Width := TarRect.Right - TarRect.Left;
Pic.Height := TarRect.Bottom - TarRect.Top;
PicRect := Rect(0, 0, Pic.Width, Pic.Height);
Pic.Canvas.CopyRect(PicRect, Orig.Canvas, TarRect);
FadeInto(Target, TarRect.Left, TarRect.Top, Pic);
Pic.Free;
end;
Взято из
Алгоритмы работы с графикой
Алгоритмы работы с графикой
Cодержание раздела:
См. также статьи в других разделах:
Алгоритмы сортировки
Алгоритмы сортировки
Алгоритм 1. Сортировка вставками.
Это изящный и простой для понимания метод. Вот в чем его суть: создается новый ма ссив, в который мы последовательно вставляем элементы из исходного массива так, чтобы новый массив был упорядоченным. Вставка происходит следующим образом: в конце нового массива выделяется свободная ячейка, далее анализируется элемент, стоящий перед пустой ячейкой (если, конечно, пустая ячейка не стоит на первом месте), и если этот элемент больше вставляемого, то подвигаем элемент в свободную ячейку (при этом на том месте, где он стоял, образуется пустая ячейка) и сравниваем следующий элемент. Так мы прейдем к ситуации, когда элемент перед пустой ячейкой меньше вставляемого, или пустая ячейка стоит в начале массива. Помещаем вставляемый элемент в пустую ячейку . Таким образом, по очереди вставляем все элементы исходного массива. Очевидно, что если до вставки элемента массив был упорядочен, то после вставки перед вставленным элементом расположены все элементы, меньшие его, а после ? большие. Так как порядок элементов в новом массиве не меняется, то сформированный массив будет упорядоченным после каждой вставки. А значит, после последней вставки мы получим упорядоченный исходный массив. Вот как такой алгоритм можно реализовать на языке программирования Pascal:
ProgramInsertionSort;
Var A,B : array[1..1000] of integer;
N,i,j : integer;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
for i:=1 to N do
begin
j:=i;
while (j>1) and (B[j-1]>A[i]) do
begin
B[j]:=B[j-1];
j:=j-1;
end;
B[j]:=A[i];
end;
{Вывод массива B}
…
End.
В принципе, данную сортировку можно реализовать и без дополнительного массива B, если сортировать массив A сразу при считывании, т. е. осуществлять вставку нового элемента в массив A.
Алгоритм 2. Пузырьковая сортировка.
Реализация данного метода не требует дополнительной памяти. Метод очень прост и состоит в следующем: берется пара рядом стоящих элементов, и если элемент с меньшим индексом оказывается больше элемента с большим индексом, то мы меняем их местами. Эти действия продолжаем, пока есть такие пары. Легко понять, что когда таких пар не останется, то данные будут отсортированными. Для упрощения поиска таких пар данные просматриваются по порядку от начала до конца. Из этого следует, что за такой просмотр находится максимум, который помещается в конец массива, а потому следующий раз достаточно просматривать уже меньшее количество элементов. Максимальный элемент как бы всплывает вверх, отсюда и название алгоритма Так как каждый раз на свое место становится по крайней мере один элемент, то не потребуется более N проходов, где N ? количество элементов. Вот как это можно реализовать:
Program BubbleSort;
Var A : array[1..1000] of integer;
N,i,j,p : integer;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
for i:=1 to n do
for j:=1 to n-i do
if A[j]>A[j+1] then
begin {Обмен элементов}
p:=A[j];
A[j]:=A[j+1];
A[j+1]:=P;
end;
{Вывод отсортированного массива A}
…
End.
Алгоритм 3. Сортировка Шейкером.
Когда данные сортируются не в оперативной памяти, а на жестком диске, особенно если ключ связан с большим объемом дополнительной информации, то количество перемещений элементов существенно влияет на время работы. Этот алгоритм уменьшает количество таких перемещений, действуя следующим образом: за один проход из всех элементов выбирается минимальный и максимальный. Потом минимальный элемент помещается в начало массива, а максимальный, соответственно, в конец. Далее алгоритм выполняется для остальных данных. Таким образом, за каждый проход два элемента помещаются на свои места, а значит, понадобится N/2 проходов, где N ? количество элементов. Реализация данного алгоритма выглядит так:
Program ShakerSort;
Var A : array[1..1000] of integer;
N,i,j,p : integer;
Min, Max : integer;
Begin
{Определение размера массива A ? N) и его заполнение}
…
{сортировка данных}
for i:=1 to n div 2 do
begin
if A[i]>A[i+1] then
begin
Min:=i+1;
Max:=i;
end
else
begin
Min:=i;
Max:=i+1;
end;
for j:=i+2 to n-i+1 do
if A[j]>A[Max] then
Max:=j
else
if A[j]<A[Min] then
Min:=j;
{Обмен элементов}
P:=A[i];
A[i]:=A[min];
A[min]:=P;
if max=i then
max:=min;
P:=A[N-i+1];
A[N-i+1]:=A[max];
A[max]:=P;
end;
{Вывод отсортированного массива A}
…
End.
Рассмотрев эти методы, сделаем определенные выводы. Их объединяет не только то, что они сортируют данные, но также и время их работы. В каждом из алгоритмов присутствуют вложенные циклы, время выполнения которых зависит от размера входных данных. Значит, общее время выполнения программ есть O(n2) (константа, умноженная на n2). Следует отметить, что первые два алгоритма используют также O(n2) перестановок, в то время как третий использует их O(n). Отсюда следует, что метод Шейкера является более выгодным для сортировки данных на внешних носителях информации.
Если вы думаете, что бравые «алгоритмщики» остановились на достигнутом, то вы ошибаетесь. Видите ли, временная оценка O(n2) показалась им слишком громоздкой, и они, жадины такие, решили еще потратить свое время, чтобы впоследствии сэкономить наше. Итак, давайте теперь рассмотрим более быстрые алгоритмы.
Алгоритм 4. Сортировка слиянием.
Эта сортировка использует следующую подзадачу: есть два отсортированных массива, нужно сделать (слить) из них один отсортированный. Алгоритм сортировки работает по такому принципу: разбить массив на две части, отсортировать каждую из них, а потом слить обе части в одну отсортированную. Корректность данного метода практически очевидна, поэтому перейдем к реализации.
Program SlivSort;
Var A,B : array[1..1000] of integer;
N : integer;
Procedure Sliv(p,q : integer); {процедура сливающая массивы}
Var r,i,j,k : integer;
Begin
r:=(p+q) div 2;
i:=p;
j:=r+1;
for k:=p to q do
if (i<=r) and ((j>q) or (a[i]<a[j])) then
begin
b[k]:=a[i];
i:=i+1;
end
else
begin
b[k]:=a[j];
j:=j+1;
end ;
for k:=p to q do
a[k]:=b[k];
End;
Procedure Sort(p,q : integer); {p,q ? индексы начала и конца сортируемой части массива}
Begin
if p<q then {массив из одного элемента тривиально упорядочен}
begin
Sort(p,(p+q) div 2);
Sort((p+q) div 2 + 1,q);
Sliv(p,q);
end;
End;
Begin
{Определение размера массива A ? N) и его заполнение}
…
{запуск сортирующей процедуры}
Sort(1,N);
{Вывод отсортированного массива A}
…
End.
Чтобы оценить время работы этого алгоритма, составим рекуррентное соотношение. Пускай T(n) ? время сортировки массива длины n, тогда для сортировки слиянием справедливо T(n)=2T(n/2)+O(n) (O(n) ? это время, необходимое на то, чтобы слить два массива). Распишем это соотношение:
T(n)=2T(n/2)+O(n)=4T(n/4)+2O(n/2)+O(n)=4T(n/4)+2O(n)= … = 2kT(1)+kO(n)
Осталось оценить k. Мы знаем, что 2k=n, а значит k=log2n. Уравнение примет вид T(n)=nT(1)+ log2nO(n). Так как T(1) ? константа, то T(n)=O(n)+log2nO(n)=O(nlog2n). То есть, оценка времени работы сортировки слиянием меньше, чем у первых трех алгоритмов (я прошу прощения у тех, кто не понял мои рассуждения или не согласен с ними, ? просто поверьте мне на слово). Перед тем как объяснить, чем этот метод лучше, рассмотрим еще один алгоритм.
Алгоритм 5. Сортировка двоичной кучей
Проблема первых трех алгоритмов, описанных в прошлой части статьи, состояла в том, что после того как элемент занимал свое место, информация об уже произведенных сравнениях никак не использовалась. Структура двоичного дерева позволяет сохранить эту информацию. Итак, представим массив в виде дерева (Рис. 1). Корень дерева ? элемент с индексом 1; элемент с индексом i является «родителем» для элементов с индексами 2*i и 2*i+1, а те, в свою очередь, являются его «детьми». Каждый элемент кроме первого имеет «родителя» и может иметь до двух «детей» ? речь ведь идет именно о ДВОИЧНОМ дереве. Очевидно, что корнем дерева является наименьший элемент, а наибольший не имеет детей. Тут возникают два вопроса: как нам такую кучу наплодить? И зачем нам это вообще нужно? Пренебрегая порядком, отвечу сразу на второй вопрос: мы хотим извлечь из кучи минимальный элемент, а потом как-то преобразовать и восстановить кучу. Таким образом, по очереди извлечь все элементы и получить отсортированный массив. И вот как мы собираемся это сделать: пусть поддеревья с корнями 2*i и 2*i+1 уже имеют свойство кучи, мы же хотим, чтобы такое свойство имело и поддерево с корнем i. Для этого, если корень больше наименьшего своего «ребенка», мы меняем корень дерева (элемент с индексом i) с этим «ребенком», после повторяем алгоритм для поддерева, куда перешел бывший корень. Выполняя этот алгоритм «снизу вверх» (сначала для маленьких поддеревьев, потом для больших), мы добьемся того, что свойство кучи будет выполняться для всего дерева. Извлечение элемента происходит очень простым способом: мы ставим последний элемент на первое место и запускаем алгоритм исправления кучи от корня дерева… Я тут много наговорил, но на самом деле, реализация совсем несложная:
ProgramHeapSort;
Var A,B : array[1..1000] of integer;
N,i,P : integer;
Procedure Heapi(ind : integer); {процедура, формирующая и исправляющяя кучу}
Var k : integer;
Begin
k:=ind*2;
If k<=N then
begin
if (k+1<=N) and (A[k]>A[k+1]) then
k:=k+1;
if A[ind]>A[k] then
begin
P:=A[ind];
A[ind]:=A[k];
A[k]:=P;
Heapi(k);
end;
end;
End;
Begin
{Определение размера массива A ? N) и его заполнение}
…
{формирование кучи}
for i:=N div 2 downto 1 do
Heapi(i);
{формирование массива B}
for i:=1 to N do
begin
B[i]:=A[1];
A[1]:=A[N];
N:=N-1;
Heapi(1);
end;
{Вывод отсортированного массива B}
…
End.
А теперь главное, т. е. оценка сложности. Время работы процедуры исправляющей кучу зависит от высоты дерева. Высота всего дерева равна log2n, значит, время работы процедуры есть O(log2n). Программа состоит из двух частей: формирование кучи и создание отсортированного массива B. Время исполнения каждой из частей не больше O(n log2n) (в каждой части исправляющая процедура вызывается не более n раз). Значит, время работы то же, что и в сортировке слиянием.
Теперь лирическое отступление насчет времени работы. Может, читатель думает, что быстрые алгоритмы сложны в исполнении и проще написать что-то вроде сортировки вставками. Что ж, рассмотрим простой пример: допустим, вы написали сортировку вставками, тщательно, с помощью ассемблера, и время работы получилось 2n2, а какой-нибудь раздолбай написал сортировку слиянием со временем работы 50nlog2n. И тут появилась необходимость отсортировать 1000000 элементов (что в наше время не редкость). Вы использовали крутой компьютер, который делает 108 операций сравнения и перестановки в секунду, а у него компьютер похуже ? всего 106 операций в секунду. И вы будете ждать 2*(106)2/108 = 20 000 секунд (приблизительно 5.56 часов), а ваш конкурент ? 50*(106)*log2(106)/106 = 1000 секунд (приблизительно 17 минут). Надеюсь, вы проведете это время (5 часов) с пользой для себя и поймете, что хороший алгоритм ? быстрый алгоритм :-). Хотя, если вы будете сортировать маленький массив или много маленьких массивов, то 2n2 для вас будет лучше, чем 50nlog2n. Эту закономерность использует один из способов оптимизации сортировки слиянием: сортировать маленькие части массива вставками.
Теперь переходим к самому интересному, а именно к одной из самых быстрых и эффективных из известных сортировок, которая так и называется ? «быстрая сортировка».
Алгоритм 6. Быстрая сортировка.
Как и в сортировке слиянием, массив разбивается на две части, с условием, что все элементы первой части меньше любого элемента второй. Потом каждая часть сортируется отдельно. Разбиение на части достигается упорядочиванием относительно некоторого элемента массива, т. е. в первой части все числа меньше либо равны этому элементу, а во второй, соответственно, больше либо равны. Два индекса проходят по массиву с разных сторон и ищут элементы, которые попали не в свою группу. Найдя такие элементы, их меняют местами. Тот элемент, на котором индексы пересекутся, и определяет разбиение на группы. Классическая реализация алгоритма выглядит так:
Program QuickSort;
Var A : array[1..1000] of integer;
N,T : integer;
Procedure Sort(p,q : integer); {p,q ? индексы начала и конца сортируемой части массива}
Var i,j,r : integer;
Begin
if p<q then {массив из одного элемента тривиально упорядочен}
begin
r:=A[p];
i:=p-1;
j:=q+1;
while i<j do
begin
repeat
i:=i+1;
until A[i]>=r;
repeat
j:=j-1;
until A[j]<=r;
if i<j then
begin
T:=A[i];
A[i]:=A[j];
A[j]:=T;
end;
end;
Sort(p,j);
Sort(j+1,q);
end;
End;
Begin
{Определение размера массива A ? N) и его заполнение}
…
{запуск сортирующей процедуры}
Sort(1,N);
{Вывод отсортированного массива A}
…
End.
Что же делает данный алгоритм таким быстрым? Ну во-первых, если массив каждый раз будет делится на приблизительно равные части, то для него будет верно то же соотношение, что и для сортировки слиянием, т. е. время работы будет O(nlog2n). Это уже само по себе хорошо. Кроме того, константа при nlog2n очень мала, ввиду простоты внутреннего цикла программы. В комплексе это обеспечивает огромную скорость работы. Но как всегда есть одно «но». Вы, наверное, уже задумались: а что если массив не будет делится на равные части? Классическим примером является попытка «быстро» отсортировать уже отсортированный массив. При этом данные каждый раз будут делиться в пропорции 1 к n-1, и так n раз. Общее время работы при этом будет O(n2), тогда как вставкам, для того чтобы «понять», что массив уже отсортирован, требуется всего-навсего O(n). А на кой нам сортировка, которая одно сортирует хорошо, а другое плохо? А собственно, что она сортирует хорошо? Оказывается, что лучше всего она сортирует случайные массивы (порядок элементов в массиве случаен). И поэтому нам предлагают ввести в алгоритм долю случайности. А точнее, вставить randomize и вместо r:=A[p]; написать r:=A[random(q-p)+p]; т. е. теперь мы разбиваем данные не относительно конкретного, а относительно случайного элемента. Благодаря этому алгоритм получает приставку к имени «вероятностный». Особо недоверчивым предлагаю на своем опыте убедится, что данная модификация быстрой сортировки сортирует любые массивы столь же быстро.
А теперь еще один интересный факт: время O(nlog2n) является минимальным для сортировок, которые используют только попарное сравнение элементов и не использует структуру самих элементов. Тем, кому интересно, откуда это взялось, рекомендую поискать в литературе, доказательство я здесь приводить не намерен, не Дональд Кнут, в конце концов :-). Но вы обратили внимание, что для рассмотренных алгоритмов в принципе не важно, что сортировать ? такими методами можно сортировать хоть числа, хоть строки, хоть какие-то абстрактные объекты. Следующие сортировки могут сортировать только определенные типы данных, но за счет этого они имеют рекордную временную оценку O(n).
Алгоритм 7. Сортировка подсчетом.
Этот метод подходит для сортировки целых чисел из не очень большого диапазона (сравнимого с размером массива). Идея вот в чем: для каждого элемента найти, сколько элементов, меньших определенного числа, и поместить это число на соответствующие место. Делается это так: за линейный проход по массиву мы для каждого из возможных значений подсчитываем, сколько элементов имеют такое значение. Потом добавляем к каждому из найденных чисел суму всех предыдущих. Получая, таким образом, сколько есть элементов, значения которых не больше данного значения. Далее, опять-таки за линейный проход, формируем из исходного массива новый отсортированный. При этом следим, чтобы два одинаковых элемента не были записаны в одно место. Если все равно непонятно, смотрите реализацию:
Program CountingSort;
Var A,B : array[1..1000] of byte;
C : array[byte] of integer;
N,i : integer;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
for i:=0 to 255 do
C[i]:=0;
for i:=1 to N do
C[A[i]]:=C[A[i]]+1;
for i:=1 to 255 do
C[i]:=C[i-1]+C[i];
for i:=N downto 1 do
begin
B[C[A[i]]]:=A[i];
C[A[i]]:=C[A[i]]-1; {здесь мы избегаем возможности записи двух одинаковых чисел в одну ячейку}
end;
{Вывод массива B}
…
End.
Этот простой метод не использует вложенных циклов и, учитывая небольшой диапазон значений, время его работы есть O(n).
Рассмотрев такое количество сортировок, можно задуматься: а будет ли результат их работы одинаковым? Странный вопрос, ведь все сортировки правильно сортируют данные, так почему же результат работы может быть разным? Хорошо, объясню: меньшие элементы всегда расположены перед большими, но порядок одинаковых элементов может быть нарушен. Если мы сортируем данные, которые состоят из одного ключа, то мы, конечно, не заметим разницы. Но если к ключу прилагается дополнительная информация, то одна сортировка может вернуть нам 1977 "Иванов" и 1977 "Сидоров", а другая ? 1977 "Сидоров" и 1977 "Иванов". Значит, порядок одинаковых элементов может в процессе сортировки стать другим. Правда, это бывает далеко не всегда и не в каждой сортировке. В сортировках вставками, пузырьком, подсчетом и слиянием порядок элементов с одинаковыми ключами всегда такой же, как и в изначальном массиве. Такие сортировки называются устойчивыми, и сейчас я познакомлю вас с улучшенной сортировкой подсчетом, которая позволяет сортировать числа большего диапазона, используя другую устойчивую сортировку.
Алгоритм 8. Цифровая сортировка.
Этой сортировкой можно сортировать целые неотрицательные числа большого диапазона. Идея состоит в следующем: отсортировать числа по младшему разряду, потом устойчивой сортировкой сортируем по второму, третьему, и так до старшего разряда. В качестве устойчивой сортировки можно выбрать сортировку подсчетом, в виду малого времени работы. Реализация такова:
Program RadixSort;
Var A,B : array[1..1000] of word;
N,i : integer;
t : longint;
Procedure Sort; {сортировка подсчетом}
Var C : array[0..9] of integer;
j : integer;
Begin
For j:=0 to 9 do
C[j]:=0;
For j:=1 to N do
C[(A[j] mod (t*10)) div t]:= C[(A[j] mod (t*10)) div t]+1;
For j:=1 to 9 do
C[j]:=C[j-1]+C[j];
For j:=N downto 1 do
begin
B[C[(A[j] mod (t*10)) div t]]:=A[j];
C[(A[j] mod (t*10)) div t] := C[(A[j] mod (t*10)) div t]-1;
end;
End;
Begin
{Определение размера массива A (N) и его заполнение}
…
{сортировка данных}
t:=1;
for i:=1 to 5 do
begin
Sort;
A:=B;
t:= t*10;
end;
{Вывод массива A}
…
End.
Так как сортировка подсчетом вызывается константное число раз, то время работы всей сортировки есть O(n). Заметим, что таким способом можно сортировать не только числа, но и строки, если же использовать сортировку слиянием в качестве устойчивой, то можно сортировать объекты по нескольким полям.
Теперь вы владеете достаточным арсеналом, чтобы сортировать все что угодно и как угодно. Помните, что выбор нужной вам сортировки зависит от того, какие данные вы будете сортировать и где вы их будете сортировать.
P.S. Все программы рабочие ? если, конечно, вам не лень будет заменить три точки на код ввода и вывода массивов :-).
Владимир ТКАЧУК vova.tkachuk@ua.fm
Взято с
См. также статьи в других разделах:
Аналог функции С memcmp
Аналог функции С memcmp
Автор: Dennis Passmore
Я создал следующие две функции, существенно повышающие произвотельность в приложениях, активно работающих с данными. Вам нужно всего-лишь обеспечить контроль типов и границ допустимого диапазона, все остальное они сделают с любым типом данных лучше нас :-) .
functionKeys_are_Equal(var OldRec, NewRec;
KeyLn : word): boolean; assembler;
asm
PUSH DS
MOV AL,01
CLD
LES DI,NewRec
LDS SI,OldRec
MOV CX,KeyLn
CLI
REPE CMPSB
STI
JZ @1
XOR AL,AL
@1:
POP DS
end;
function First_Key_is_Less(var NewRec, OldRec; Keyln : word): boolean; assembler;
asm
PUSH DS
MOV AL,01
CLD
LES DI,NewRec
LDS SI,OldRec
MOV CX,KeyLn
CLI
REPE CMPSB
STI
JZ @5
JGE @6
@5: XOR AL,AL
@6: POP DS
end;
Взято с
Примечание от Jin X
Примеры приведены для 16-битного Pascal'я и не могут использоваться в 32-битном Delphi! Зато можно делать так:
{ Возвращает -1 при X<Y, 0 при X=Y, 1 при X>Y. }
{ Сравнение идёт по DWord'ам, будто сравнивается массив чисел Integer или Cardinal, }
{ т.е. 01 02 03 04 05 06 07 08 > 01 02 03 04 05 06 08 07, }
{ т.к. 04030201 = 04030201, но 08070605 > 07080605 (hex). }
{ Однако, если Size and 3 <> 0, то последние Size mod 4 байт сравниваются побайтно! }
function memcmp(const X, Y; Size: DWord): Integer;
asm
mov esi,X
mov edi,Y
mov ecx,Size
mov dl,cl
and dl,3
shr ecx,2
xor eax,eax
rep cmpsd
jb @@less
ja @@great
mov cl,dl
rep cmpsb
jz @@end
ja @@great
@@less:
dec eax
jmp @@end
@@great:
inc eax
@@end:
end;
Анимация без DirectX
Анимация без DirectX
Автор: http://sunsb.dax.ru
При попытке изобразить некую анимацию использую только средства TCanvas, на экране получается черте-чего. Все мельтешит, дергается, одним словом - не годится.
Для получения " гладкой" ( не мельтешащей ) анимация в программах не использующих DirectX, я обычно использую следующую технику.
Узким местом в процессе является момент изменения картинки на экране, поэтому рисование нужно проводить на невидимом для пользователя канвасе, и только подготовив там обновляемые участки выводить их на видимый экран.
Для того, чтобы стереть кртинку в том месте где ее уже нет, нужно помнить позицию в которой она была выведена в прошлый раз. Обзовем эту позицию Old: TRect, текущую позицию запомним в New: TRect.
TRect я использую, на сучай если размер отображаемой картинки может изменяться.
Стандартным подходом является написание двух процедур - Hide и Show, одна из которых прячет картинку в старой позиции, выводя участок фона поверх нее, а вторая выводит в новой позиции.
Такой вариант не проходит и приводит к мерцанию изображения.
Я предлагаю оставить процедуру Hide в покое, и пользоваться ей только если картинку нужно совсем убрать с экрана.
Процедура Show будет выполнять обе нужные функции. Для обновления экрана нам нужно погасить картинку в старой позиции и показать в новой.
Тут возможны два варианта.
Первый - старый и новый прямоугольники пересекаются. В этом случае мы создаем временный TBItmap - tmp с размером их объединения, заполняем его требуемым участком фона, и рисуем на нем картинку. После такой подготовки выводим tmp в нужной позиции экрана.
Второй - старый и новый прямоугольники не пересекаются. В этом случае мы просто копируем прямоугольник old с невидимой копии фона на экран ( процедура Hide ), и рисуем нужную картинку в прямоугольнике new.
При таком подходе мы избегаем двойной перерисовки экрана, что исключает мерцание.
Ниже программа которая все это делает.
varwsrf: TPaintBox; // видимый экран
var ssrf: TBitmap; // скрытый неизменяемый фон
var bmp : TBitmap; // картинка для анимации
var tmp : TBitmap; // временное хранилище
function hasIntersect( const A,B : TRect): boolean;
var R: trect; // пересекаются ли прямоугольники
begin
result := false;
R.Left := max( A.Left, B.Left );
R.Right := min( A.Right, B.Right );
if R.Left > = R.Right then exit;
R.Top := max( A.Top, B.Top );
R.Bottom:= min( A.Bottom, B.Bottom );
if R.Top > = R.Bottom then exit;
result := true;
end;
function Union( A, B: TRect ):TRect;
begin // результат - объединение
if EmptyRect( A ) then result := B
else if EmptyRect( B ) then result := A
else begin
Result.Left := min( A.Left, B.Left );
Result.Top := min( A.Top, B.Top );
Result.Right := max( A.Right, B.Right );
Result.Bottom:= max( A.Bottom, B.Bottom );
end;
end;
procedure TOneTooth.Hide;
begin
tmp.Width := bmp.Width;
tmp.Height:= bmp.Height;
tmp.Canvas.CopyRect( bmpRect(tmp), ssrf.Canvas, old );
wsrf.Canvas.Draw( old.Left, old.Top, tmp );
end;
procedure TOneTooth.Show;
var R, R1 : TRect;
begin
now.Right := now.Left + bmp.Width ;
//корректировка now на случай
now.Bottom := now.Top + bmp.Height;
//изменения размеров bmp
if hasIntersect( old, now ) then begin
R := Union( old, now );
tmp.Width := R.Right-R.Left;
tmp.Height:= R.Bottom-R.Top;
tmp.Canvas.CopyRect( bmpRect(tmp), ssrf.Canvas, R );
// фон
tmp.Canvas.Draw( now.left-r.left, now.Top-r.top, bmp )
// фон + картинка
end else begin
Hide;
tmp.Canvas.CopyRect( bmpRect(bmp), ssrf.Canvas, now );
// фон
tmp.Canvas.Draw( 0, 0, bmp ); // фон + картинка
R:=now;
end;
wsrf.Canvas.Draw( R.Left, R.Top, tmp );
old := now;
end;
Взято с
Анимированная кнопка Пуск?
Анимированная кнопка Пуск?
Автор: I MD.CIPTAYASA
Итак, если Вам надоело привычное статическое изображение кнопки "Пуск", то предлагаю немного оживить её :) Надеюсь, что это доставит Вам удовольствие.
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls,ShellAPI;
const
MAX_BUFFER = 6;
type
TForm1 = class(TForm)
Button1: TButton;
Timer1: TTimer;
Button2: TButton;
Image1: TImage;
Edit1: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Edit1KeyPress(Sender: TObject; var Key: Char);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button3Click(Sender: TObject);
private
HW : HWND;
DC : HDC;
R : TRect;
FNumber : integer;
Buffer : array[1..MAX_BUFFER] of TBitmap;
TrayIcon : TNotifyIconData;
procedure CreateFrames;
procedure DestroyFrames;
procedure BuildFrames;
procedure NotifyIcon(var Msg : TMessage);message WM_USER + 100;
procedure OnMinimizeEvt(Sender : TObject);
end;
var
Form1: TForm1;
implementation
uses Math;
{$R *.DFM}
// Создаём буфер для спрайтов
procedure TForm1.CreateFrames;
var
i : integer;
begin
for i:=1 to MAX_BUFFER do
begin
Buffer[i] := TBitmap.Create;
Buffer[i].Height := R.Bottom-R.Top;
Buffer[i].Width := R.Right-R.Left;
Buffer[i].Canvas.Brush.Color := clBtnFace;
Buffer[i].Canvas.Pen.Color := clBtnFace;
Buffer[i].Canvas.Rectangle(0,0,Buffer[i].Width,Buffer[i].Height);
end;
end;
procedure TForm1.DestroyFrames;
var
i : integer;
begin
for i:=1 to MAX_BUFFER do
begin
Buffer[i].Destroy;
end;
end;
// Подготавливает сегменты/спрайты для анимации
procedure TForm1.BuildFrames;
var
i,j,k,H,W : integer;
Y : double;
begin
H := R.Bottom-R.Top;
W := R.Right-R.Left;
Image1.Width := W;
Image1.Height:= H;
for i := 1 to MAX_BUFFER-1 do //Буфер[MAX_BUFFER] используется для хранения оригинального битмапа
for j:= 1 to W do
for k:=1 to H do
begin
Y := 2*Sin((j*360/W)*(pi/180)-20*i);
Buffer[i].Canvas.Pixels[j,k-Round(Y)]:= Buffer[6].Canvas.Pixels[j,k];
end;
end;
procedure TForm1.OnMinimizeEvt(Sender : TObject);
begin
ShowWindow(Application.Handle,SW_HIDE);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
HW := FindWindowEx(FindWindow('Shell_TrayWnd',nil),0,'Button',nil);
GetWindowRect(HW,R);
DC := GetWindowDC(HW);
CreateFrames;
FNumber :=1;
TrayIcon.cbSize := SizeOf(TrayIcon);
TrayIcon.Wnd := Form1.Handle;
TrayIcon.uID := 100;
TrayIcon.uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
TrayIcon.uCallbackMessage := WM_USER + 100;
TrayIcon.hIcon := Application.Icon.Handle;
Shell_NotifyIcon(NIM_ADD,@TrayIcon);
Application.OnMinimize := OnMinimizeEvt;
end;
// Уведомляем обработчик
procedure TForm1.NotifyIcon(var Msg : TMessage);
begin
case Msg.LParam of
WM_LBUTTONDBLCLK :
begin
ShowWindow(Application.Handle,SW_SHOW);
Application.Restore;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
//Получаем изображение оригинальной кнопки, чтобы потом использовать его
//когда анимация завершится
BitBlt(Buffer[MAX_BUFFER].Canvas.Handle,0,0,R.Right-R.Left,R.Bottom-R.Top,
DC,0,0,SRCCOPY);
BuildFrames;
Image1.Canvas.Draw(0,0,Buffer[MAX_BUFFER]);
Button2.Enabled := true;
if Edit1.Text <> '' then
Timer1.Interval := StrToInt(Edit1.Text)
else
begin
Timer1.Interval := 100;
Edit1.Text := '100';
end;
end;
// Освобождение ресурсов
procedure TForm1.FormDestroy(Sender: TObject);
begin
Timer1.Enabled := false;
BitBlt(DC,0,0,R.Right-R.Left,R.Bottom-R.Top,
Buffer[MAX_BUFFER].Canvas.Handle,0,0,SRCCOPY);
ReleaseDC(HW,DC);
DestroyFrames; // не забудьте сделать это !!!
Shell_NotifyIcon(NIM_DELETE,@TrayIcon);
end;
// Анимация начинается здесь
procedure TForm1.Timer1Timer(Sender: TObject);
begin
BitBlt(DC,0,0,R.Right-R.Left,R.Bottom-R.Top,
Buffer[FNumber].Canvas.Handle,0,0,SRCCOPY);
Inc(FNumber);
if (FNumber > MAX_BUFFER-1) then FNumber := 1;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Timer1.Enabled := not Timer1.Enabled;
if not Timer1.Enabled then
begin
BitBlt(DC,0,0,R.Right-R.Left,R.Bottom-R.Top,
Buffer[MAX_BUFFER].Canvas.Handle,0,0,SRCCOPY);
Button2.Caption := '&Animate';
Button1.Enabled := true;
end
else
begin
Button2.Caption := '&Stop';
Button1.Enabled := false;
end;
end;
// Обеспечиваем ввод числовых значений
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if not (Key in ['0'..'9']) and (Key <> Chr(VK_BACK)) then
Key := #0;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caNone;
Application.Minimize;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
PostMessage(Form1.Handle,WM_DESTROY,0,0);
Application.Terminate;
end;
end.
Взято с Исходников.ru
ANSI ---> ASCII
ANSI ---> ASCII
{преобразованиеAnsi to Ascii}
function AnToAs(s: String) : String;
Var i,kod : Integer;
begin
Result:=s;
for i:=1 to length(s) do
begin
kod:=Ord(s[i]);
if kod 13 then Result[i]:=' ';
if ( kod>=192) and ( kod=239) then
Result[i]:=Chr(kod-64);
if ( kod>=240) and ( kod=255) then
Result[i]:=Chr(kod-16);
if kod=168 then Result[i]:=Chr(240);
if kod=184 then Result[i]:=Chr(241);
end;
end;
Взято с
Архитектура BDE (статья)
Архитектура BDE (статья)
Архитектура BDE и его особенности при работе с SQL-серверами
Этот материал основан на изучении документации и справочных материалов по BDE и на собственном опыте. На самом деле информация из этого документа частично появлялась и раньше как в FAQ Borland так и в материалах других авторов (в частности Epsylon Technologies). Однако до сих пор большое количество разработчиков используют BDE. Но в последнее время все больше людей работают с SQL-серверами, и более популярными становятся компоненты прямого доступа - IBObjects/FreeIBComponents/IBExpress, Direct Oracle Access и другие. Кроме того, BDE не будет поддерживать Interbase 6.0 (диалект 3), да и вообще похоже, прекратит свое существование. В Delphi 6 наряду со старым BDE и в Kylix (Delphi и C++Builder для Linux) будет использоваться другая библиотека - dbExpress. Поэтому, чтобы поставить жирную точку (или крест, как хотите) на BDE, я и решил написать этот документ.
В большей степени этот текст напоминает то, что я читал на курсах по Delphi и разработке баз данных 3-4 года назад. Привет вам, курсанты! Можете прочитать этот документ хотя бы для того, чтобы освежить память.
Введение
Для начала вернемся лет на 10 назад. В те времена на компьютерах властвовали настольные СУБД - dBase, Paradox, FoxPro, Clipper и т.п. SQL-сервера в основном работали на мэйнфреймах. Среди форматов настольных СУБД был полный разнобой, и например, хотя Clipper, FoxPro и dBase работали с форматом DBF, использовать таблицы друг друга они фактически не могли из-за мелких, но существенных различий. Обмениваться данными в те времена между разными СУБД можно было разве что при помощи импорта-экспорта. Многие компании понимали, что так дальше продолжаться не может. Некоторые встраивали в свои продукты несколько "движков", но это приводило к распуханию продукта, да и чаще всего пользователи работали только с одним форматом данных, а не несколькими одновременно.
В 1990-м году Borland приобрел компанию Ashton-Tate, а вместе с ней и dBase (и Interbase). Таким образом у Borland появилось две настольные СУБД, с совершенно разными форматами - dBase и Paradox. Понятно, что для дальнейшего развития этих продуктов усилия по развитию форматов данных и работы с ними фактически удваивались. И в частности поэтому было принято решение создать некое универсальное ядро доступа к данным, которое могло бы работать с несколькими форматами данных единым образом. Созданию такого ядра также способствовало появление Windows, а следовательно и разделяемых библиотек - DLL. Можно было выпускать несколько продуктов, используя одни и те же dll доступа к данным. Это вполне соответствовало объектно-ориентированной концепции разработки ПО, которая не только использовалась в Turbo Pascal и в Turbo C++, но и при разработке собственных приложений Borland, таких как dBase, Paradox и Quattro (все для Windows).
примечание:
дальнейшая информация по датам взята из документа, подзаголовок "Evolution of BDE/IDAPI Technology: 1990 - 94".
Технология была названа Open Database Application Programming Interface - ODAPI, и впервые была использована в Quattro Pro 1.0 for Windows в сентябре 1992 года. В январе 1993-го эта же версия ODAPI 1.0 была использована в Paradox 1.0 for Windows, а затем и в dBase 1.0 for Windows. ODAPI пока поддерживал только форматы dBase и Paradox, и мог выполнять запросы к обоим форматам при помощи механизма Query By Example (QBE), пришедшего из Paradox for DOS.
справка:
драйверы ODBC 1.0 от Microsoft впервые появились в августе 1993 года.
Информация из MSDN.
Всего через полгода, в сентябре 1993, ODAPI 1.1 уже поддерживала работу с SQL-серверами Interbase, Oracle, Sybase и Microsoft. Версия 2.0 была переименована в IDAPI (слово Open было заменено на Integrated), и работами по расширению и стандартизации этого интерфейса уже занимался не только Borland, а целый комитет с IBM, Novell и Wordperfect включительно. В этой версии появился Local SQL - ядро для выполнения запросов SQL к локальным форматам данных, и IDAPtor - механизм для подключения ODBC-драйверов к IDAPI.
Последняя 16-ти разрядная версия IDAPI 2.5 использовалась в Delphi 1. Далее, начиная с 3.0 (12 января 1996 года в составе Paradox 5.0 for Windows), пошли 32-разрядные версии. Собственно, на этом развитие функциональности BDE закончилось. Добавлялись новые драйверы для доступа к SQL-серверам DB2, Informix, в BDE 3.5 появились кэшированные обновления (CachedUpdates), появился драйвер FoxPro и сопряжение с DAO, но все это происходило на протяжении достаточно длительного срока - с 1996 по 2000.
С одной стороны, функциональность BDE можно назвать даже избыточной. С другой стороны повлияла конкуренция со стороны Microsoft, стандарта ODBC. Собственно, по функциональности ODBC является подмножеством BDE, но Microsoft в те годы предпринимала очень активные действия по продвижению ODBC, и главным в этом был выпуск ODBC SDK, с помощью которого любая фирма могла разработать собственный ODBC-драйвер (надо сказать, что в те годы их было огромное количество, причем большинство было весьма низкого качества и невысокой производительности). А BDE был более "закрытым". Например, BDE SDK так и не увидел свет, и был доступен разве что избранным (я оказался в их числе, и надо сказать, что качество BDE SDK и удобство написания драйверов было на высоте). С третьей стороны, к этому времени WordPerfect был куплен Novell, Paradox также был продан Novell, а затем Corel, а IBM похоже просто потеряла к IDAPI интерес.
Короче, комитет IDAPI распался, а Microsoft задавил конкуренцией.
Несмотря на перечисленные негативные моменты, BDE активно использовался не только самим Borland, но и многими другими фирмами. Это Novell (продукт InForms), ReportSmith (впоследствии купленный и проданный Borland), CrystalReports (вплоть до версии 5.0 использовал BDE) и так далее.
Архитектура
Увлекшись историей я немного пропустил, зачем все это (BDE) делалось. Частичная цель упоминалась выше - предоставить универсальное ядро доступа к локальным форматам данных. Основная - обеспечить прозрачную работу приложений как с локальными форматами, так и с SQL-серверами. Как сейчас помню, что именно удобство при работе с SQL-серверами рекламировалось как основное. Однако в последние 2-3 года именно эта возможность вызывала наибольшее количество нареканий. Давайте рассмотрим архитектуру BDE.
Основная работа с BDE производится посредством внешнего интерфейса IDAPI (IDAPI32.DLL). Формат данных выбирается в псевдониме (alias) соединения, и в принципе дальше работа с разными форматами ничем не отличается. В том числе и неважно, как работает приложение с BDE - через компоненты VCL DB, которые используют функции BDE, или напрямую (все равно компоненты используют те же функции BDE).
Дальше функции IDAPI транслируют вызовы в функции соответствующего драйвера. Если это драйвер локального формата (dBase, Paradox, FoxPro), то драйвер формата сам работает с соответствующими файлами (таблицами и индексами). Если это SQL Link, то вызовы транслируются в вызовы функций API клиентской части конкретного SQL-сервера. Для каждого сервера SQL Link свой.
IDAPTOR (соединитель с ODBC) и интерфейс к DAO работает точно также как и SQL Link, т.е. просто транслирует вызовы BDE в вызовы ODBC или DAO, непосредственно к формату не имея никакого отношения.
Если посмотреть на файлы BDE, то можно подробно рассмотреть его составные части.
IDAPI32.DLL
Основной интерфейс
BLW32.DLL, BANTAM.DLL
Языковые функции
*.BTL
Файлы с языковыми кодировками.
IDBAT32.DLL
Операции пакетного копирования данных
IDDR32.DLL
Модуль работы с Data Repository
IDASCI32.DLL
Драйвер для работы с текстовым форматом
IDDAO32.DLL
Драйвер трансляции вызовов к DAO
IDODBC32.DLL
Драйвер трансляции вызовов к ODBC
IDPDX32.DLL
Драйвер для работы с форматом Paradox
IDDBAS32.DLL
Драйвер для работы с форматом dBase и FoxPro
IDQBE32.DLL
Ядро обработки запросов QBE
IDSQL32.DLL
Ядро обработки запросов SQL
SQLINT32.DLL
SQLLink-драйвер трансляции вызовов к Interbase API
SQLORA32.DLL
SQLLink-драйвер трансляции вызовов к Oracle Call Level Interface
SQL*32.DLL
Другие SQLLink-драйверы
Таким образом, при установке BDE "лишние" файлы можно без проблем выкинуть.
Также, надеюсь, понятно, почему BDE "не работает" с SQL-сервером, если не установлена клиентская часть этого сервера (то же самое по отношению к DAO - без дистрибутива DAO BDE не будет работать с файлами MS Access). Вообще клиентские части SQL-серверов несовместимы между собой абсолютно. Поэтому невозможно написать универсальный SQL Link.
Данный рисунок и список файлов, возможно, развеет популярный миф о том, что Delphi хорошо приспособлена для работы с Interbase. Как видите, Interbase для Delphi столь же равноправен, как скажем, Oracle или любой ODBC-драйвер. В отличие от продуктов Microsoft в BDE нет никаких "обходных" функций для работы со своими форматами, т.е. работа с IB ведется только через SQL Link (без sqlint32.dll BDE вообще не знает, что такое Interbase).
Отдельное место в архитектуре BDE и среди упомянутых файлов занимают Local SQL и QBE Engine. Эти механизмы запросов будут рассмотрены чуть дальше.
TTable и TQuery
TTable и TQuery являются основными компонентами, используемыми при программировании приложений баз данных (TStoredProc не в счет, и без него можно прекрасно обойтись, вызывая процедуры через select или execute в компоненте TQuery). TTable предоставляет доступ как к таблицам, а TQuery позволяет выполнять произвольные запросы. Если с TQuery все понятно - он выполняет тот запрос, который написан в свойстве TQuery.SQL - то TTable скрывает очень много подробностей своей работы от программиста. Без SQL Monitor увидеть все тонкости невозможно (если кто не знает - SQL Monitor находится в меню Database).
Итак, запустите Delphi, откройте SQL Monitor, положите на форму компонент TDatabase, подсоединитесь к серверу, затем положите компонент TTable, присоедините его к алиасу TDatabase и выберите любую таблицу из списка (свойство TableName). Переключитесь на SQL Monitor, сотрите все что там появилось, переключитесь обратно, и включите TTable.Active:=True; Смотрим в SQL Monitor (лог с самого начала):
·первым запросом BDE хочет убедиться, что выбранная нами таблица существует.
·второй запрос выбирает список полей выбранной таблицы, их названий, типов, условий проверки и т.п.
·третий запрос выбирает информацию об индексах указанной таблицы. Определяется, есть ли среди них первичный ключ, и по каким полям построены индексы.
·четвертый запрос почти повторяет второй, и выбирает информацию о полях - условия проверки, "вычисляемость" поля, допустимость NULL и прочее.
·собственно, пятый запрос открывает таблицу, формируя запрос SELECT FIELD1, FIELD2, ... FROM TABLE ORDER BY PK_FIELD ASC.
·
Заметьте, что подобные запросы выполняются каждый раз при открытии таблицы (любой) компонентом TTable. Перечитывания этих данных можно избежать, если включить у используемого алиаса параметр ENABLE SCHEMA CACHE. При этом считанную первый раз информацию BDE размещает на диске в указанном каталоге (SCHEMA CACHE DIR) в специальном файле, кэширует информацию для SCHEMA CACHE SIZE количества таблиц, и держит эту информацию в кэше в течение SCHEMA CACHE TIME секунд (если -1, то вечно). Если структуры таблиц закэшированы, то при их изменении на сервере (например, добавили новое поле) приложение будет работать со старой структурой, что может вызвать серьезные проблемы в работе приложения. SCHEMA CACHE нужно использовать только тогда, когда структура базы данных определена окончательно и не изменяется. Если все же очень сильно хочется использовать кэширование структур таблиц, то не забывайте правильно установить параметр SCHEMA CACHE TIME. Или при первом за день подключении приложения к серверу сначала кэширование структур можно выключить, отсоединиться, включить и подсоединиться снова - таким образом в самом начале работы кэш структур таблиц будет создан, и будет использоваться в течение дня.
примечание:
параметры SCHEMA CACHE не имеют абсолютно никакого отношения к механизму Cached Updates или к кэшированию данных.
Вернемся к запросу, которым TTable открыл таблицу. В конце запроса стоит указание порядка сортирвки - ORDER BY FIELD ASC. По умолчанию TTable сортирует данные в порядке поля первичного ключа. И кстати, если пользоваться свойством TTable.IndexName, то все равно к запросу будет добавляться ORDER BY INDEXFIELD ASC. Таким образом получается, что свойство IndexName при работе с SQL-серверами бессмыслено. Вместо него нужно просто использовать свойство IndexFieldNames. Даже если в этом свойстве указать поле, по которому нет индекса, то все равно BDE "прицепит" к запросу ORDER BY FIELD ASC. Кстати, BDE абсолютно игнорирует направление индекса, и всегда в запросе добавляет ASC, даже если индекс по этому полю создан как DESCENDING (по убыванию). Получается, что отсортировать таблицу в TTable по убыванию нельзя.
примечание:
можно было бы отнести этот недостаток на SQL Link для IB, но вполне возможно что просто TTable не в состоянии кэшировать и обновлять данные, отсортированные по убыванию (см. дальше о кэше данных).
Кэширование данных
Как видно из предыдущего раздела, TTable работает с таблицами сервера не каким-то хитрым образом, а формируя самые нормальные SQL-запросы. И тут начинается самое интересное. Оказывается, при выполнении запроса сервер выдает записи клиенту (приложению) по очереди и по одной записи. Причем только "сверху вниз". Как только записи на сервере кончились, сервер сообщает клиенту об этом сигналом EOF вместо выдачи очередной записи. Конечно, в некоторых современных серверах есть произвольное позиционирование и проход по выборке не только сверху вниз но и в обратном порядке, но это требует от сервера достаточно больших ресурсов.
примечание:
разумеется, клиентская часть SQL-сервера может принимать записи от сервера "пачками". Но в любом случае получение записи инициируется только вызовом функции fetch, и по этой команде "выбирается" только одна запись. Т.е. приложение получает записи по одной независимо от того, буферизируются они на сервере/клиенте или нет.
Поскольку BDE - вещь универсальная, то он должен обеспечить возможность перемещения по записям вверх и вниз независимо от сервера. Т.е. он должен обеспечивать кэширование записей самостоятельно. Взял запись с сервера - положил в кэш. Это означает, что если вы открыли таблицу в 100 тысяч записей, и нажали в гриде Ctrl-End, то все 100 тысяч записей "приедут" к вам на клиентский компьютер. С таким пожиранием ресурсов надо как то бороться. Если в TQuery можно ограничить количество выбираемых записей условиями запроса, то в TTable этого сделать нельзя, поскольку как мы уже видели, TTable формирует запросы самостоятельно.
Живой и мертвый кэш, или TTable и TQuery
Для решения проблем кэширования TTable было введено два разных механизма кэширования. Для TTable - "живой" кэш, а для TQuery - "мертвый". Для простоты лучше сначала рассмотреть "мертвый" кэш. Вообще тут и рассматривать нечего - при перемещении по записям записи помещаются в кэш. Как только все записи помещены (пользователь "доехал" до конца выборки каким-либо способом), обращения к серверу прекращаются, и любые передвижения пользователя по записям совершаются только в кэше.
У "мертвого" кэша есть побочный эффект - если вызвать метод Locate для поиска записи, то TQuery принудительно выберет все записи в кэш, и только потом будет искать нужную запись в кэше. Собственно, пока запись не считана, неизвестно - попадает она под условие Locate или нет. Поэтому другим способом здесь искать записи невозможно.
"Мертвый" кэш существует до тех пор, пока запрос не будет закрыт (TQuery.Close).
Живой кэш более сложен, и для его понимания придется использовать чуть больше компонент на уже открытой в Delphi форме. Добавьте к TDatabase и TTable компоненты TDataSource и TDBGrid. Grid растяните по вертикали так, чтобы в нем было видно штук 5 записей (7, или 9, не больше). Желательно чтобы в таблице при этом было не меньше 20-30 записей. Поместите кнопку рядом с Grid-ом, в которой на OnClick напишите
Table1.IndexFieldNames := 'FIELD';
где FIELD - любое имя поля открытой таблицы, главное чтобы не поле первичного ключа. Наличие индекса по этому полю не обязательно.
Скомпилируйте приложение, запустите его (не забыв запустить SQL Monitor). Поместите курсор грида посередине, как показано на рисунке (на содержимое грида не обращайте внимания - у вас оно будет совершенно другим, в зависимости от выбранной таблицы).
Сотрите все в SQL Monitor. И нажмите кнопку Button1. Теперь возвращаемся к началу лога в SQL Monitor:
·первый запрос выбирает значение поля, по которому нужно отсортировать данные.
·второй запрос выбирает данные от текущей записи и "выше" - см добавку WHERE FIELD < ? ORDER BY FIELD DESC
·третий запрос выбирает запись, которую нужно поместить на место текущей (выборка запомненного первым запросом значения). Кстати, этот запрос у меня почему-то выполнился аж три раза (BDE 5.1.1). Раньше он обычно выполнялся всего один раз.
·четвертый запрос выбирает данные от текущей записи и ниже - см. добавку WHERE FIELD > ? ORDER BY FIELD ASC
Вот это и есть "живой" кэш. Т.е. при любых операциях перемещения по набору данных, отличных от перемещения на одну запись (или PageUp/PageDown) вверх или вниз, TTable уничтожает текущий кэш, перечитывает данные столь экзотическим образом, и создает новую копию кэша. По количеству вызовов isc_dsql_fetch вы можете понять, что как "вверх" так и "вниз" от текущей записи второй и четвертый запросы выбрали ровно столько записей, сколько помещается в Grid. Если вы продолжите движение курсором по одной строке вверх или вниз, то увидите каким способом (зачастую неэффективным) BDE довыбирает необходимые записи (особенно неэффективность проявляется при движении вверх).
Если же курсор находится вверху или внизу грида, то вместо трех запросов отображения данных будет два - выборка текущей записи и выборка записей только вверх или вниз от текущей записи.
Существенный момент - выборка "вверх" всегда использует сортировку по убыванию. Если по полю сортировки нет индекса по убыванию, то Interbase (или другой сервер) будет сортировать результат в памяти или на диске, что существенно медленнее сортировки с использованием индекса. Поэтому "резкие" перемещения, например в конец таблицы при помощи Ctrl-End будут приводить к значительной паузе, пока сервер отсортирует данные и выдаст резульат. Повысить скорость в этом случае можно только использованием ClientDataSet, который сортирует кэш вместо выдачи серверу SQL-запросов.
Locate в живом кэше в отличие от мертвого кэша выполняется намного быстрее, т.к. TTable применяет ту же самую технику очистки кэша для поиска нужной записи.
Фильтрация
Фильтрация TTable и TQuery происходит с учетом живого или мертвого кэша. Для TTable при наложении фильтра конструируется соответствующий SQL-запрос, а TQuery производит фильтрацию буквально при помощи Locate (т.е. сначала выбираются все записи в кэш, а затем идет фильтрация уже в кэше).
О вреде UNIQUE constraint
В Interbase уникальность поля можно обеспечить тремя способами: создать первичный ключ, создать unique constraint, и создать уникальный индекс. Но при чем здесь Interbase? А при том, что BDE открывает TTable по умолчанию с использованием уникального индекса. Если таблица одновременно содержит как первичный ключ, так и unique constraint, то в результате у таблицы 2 уникальных индекса. При обращении к списку индексов TTable берет для сортировки по умолчанию первый попавшийся. Если уникальность поля обеспечивается обычным уникальным индексом, то проблем нет. А вот если та же уникальность обеспечивается через UNIQUE constraint, то при backup/restore базы данных есть шанс что порядковые номера индексов поменяются (поскольку для IB это constraint целостности), и BDE будет брать в качестве первого попавшегося индекс от unique constraint вместо индекса от primary key. Вреда от этого, в общем, никакого нет, но в результате это вызывает нежелательный порядок сортировки по умолчанию в приложениях.
"Живые" запросы
Если способность TTable редактировать и удалять записи ни у кого не вызывает удивления, то TQuery требует, чтобы свойство RequestLive было установлено в True. Если при False запрос отправлялся непосредственно на сервер, то при True запрос предварительно обрабатывается локальным SQL (модуль IDSQL32.DLL). Это необходимо для того, чтобы TQuery смог сформировать запросы INSERT/UPDATE/DELETE на основании заданного SELECT. Для TTable построение таких запросов не представляет сложности, т.к. задано только имя таблицы, имена полей считаны и т.п. А существующий SQL-запрос нужно синтаксически разобрать, чтобы понять, сколько в нем используется таблиц, какие выбираются поля и из каких таблиц, и можно ли вообще сформировать запросы на вставку, обновление и удаление данных.
Именно таким разбором SQL и занимается Local SQL. Разумеется, он поддерживает весьма ограниченный синтаксис SQL, что не позволяет делать "живыми" запросы, использующие расширенные конструкции SQL, пользовательские функции или специфические для конкретного сервера особенности. Например, для организации живого запроса вместо
SELECT * FROM TABLE
WHERE FIELD STARTING WITH 'A'
придется писать
SELECT * FROM TABLE
WHERE FIELD LIKE 'A%'
Подобную замену еще можно пережить, но не всегда возможно найти замену конструкции, которую не понимает Local SQL, и прекрасно понимает сервер.
примечание:
вы сами можете убедиться в изложенном, поместив первый запрос в TQuery, переключив RequestLive в True. Попытайтесь установить Active компонента в True и посмотрите что получится.
Собственно, как вы поняли, на самом деле никаких "живых" запросов не существует. В SQL оператор SELECT выполняет только чтение, а вставить, обновить или удалить записи можно только операторами INSERT, UPDATE и DELETE, и никак иначе.
При переключении TQuery.RequestLive:=True TQuery начинает вести себя как TTable - т.е. он сначала разбирает запрос, извлекает оттуда имя таблицы, и потом выбирает информацию из системных таблиц о полях таблицы, индексах и т.п. Вы можете все это увидеть в SQL Monitor.
Кроме RequestLive можно еще воспользоваться и компонентом UpdateSQL. Об этом см. дальше в разделе CachedUpdates.
SQLQUERYMODE
Кроме RequestLive на выполнение запросов влияет и параметр алиаса или драйвера IB SQLQUERYMODE. Когда этот параметр установлен в LOCAL, BDE всегда производит разбор SQL-конструкций при помощи Local SQL. Если параметр установлен в "пусто", то BDE сначала пытается отправить SQL на сервер, а при получении ошибки пытается выполнить его Local SQL. При установленном параметре SERVER запросы всегда отправляются только на сервер (за исключением "живых").
Таким образом, при установке LOCAL запросы будут всегда выполняться локальным ядром SQL BDE, и функциональность SQL IB будет недоступна (не будут выполняться запросы с containing и др. синтаксисом, который не поддерживает Local SQL). Избавиться от такого поведения лучше всего установив раз и навсегда значение SERVER.
Refresh и атомарность запросов
Читатель уже после информации о живом и мертвом кэше, наверное, давно хочет спросить - а как же BDE видит новые записи, добавляемые другими приложениями? Да никак. С TTable все понятно - в любой момент можно вызвать refrech, что приведет к удалению "живого" кэша и переоткрытию TTable как мы уже видели в разделе о кэшах записей. TTable перед своим закрытием запоминает запись, на которой стоял курсор грида, и поэтому после открытия может спозиционироваться на эту же запись.
TQuery работает с "мертвым" кэшем, поэтому обновлять его невозможно. BDE не знает о том, какое из полей в запросе является первичным ключом, да и вообще по скольким таблицам построен запрос. Поэтому единственным вариантом для refresh является переоткрытие TQuery (Close/Open). Текущая запись при этом будет потеряна. Можно, правда, попытаться использовать TBookmark чтобы запомнить запись и вернуться к ней после открытия TQuery, но как и Locate это вызовет выборку всех записей с сервера в кэш TQuery и при большом количестве выбираемых записей может занять длительное время.
примечание:
Даже если компонент IBX IBTable и поддерживает Refresh, то он его выполняют точно таким же образом, что и BDE. А компонент IBDataSet выполняет Refresh только для одной, текущей, записи.
В чтение актуальных данных вмешивается еще и атомарность операторов SQL. Применительно к SELECT это означает, что он будет выбирать только те записи, которые существовали на момент выполнения этого SELECT. Это означает, что если открыть TQuery, а затем через 5 минут подсоединить его к гриду, то в нем будут видны только те записи, которые были в базе данных 5 минут назад. Даже если за это время это же самое приложение в этой же транзакции успело добавить, изменить или удалить 1 или сколь угодно большее количество записей, попадающих под условия выборки данного SELECT.
В буквальном смысле это означает, что если вставить запись в открытый select, то увидеть новую запись нельзя. Для этого придется переоткрыть запрос. По отношению к TQuery это справедливо, а вот TTable "обманывает" пользователя, помещая данные успешно вставленной записи прямо в свой собственный кэш. Таким образом, вставка в TTable как бы помещает данные прямо в открытую выборку. Чего, собственно, на самом деле на сервере не происходит.
Вообще перечитывание данных почти всегда вызывает проблемы. Сервер не уведомляет клиентов, что определенные записи изменились, появились или были удалены. EventAlerter может сообщить только информацию что некое событие (например, была изменена таблица) произошло, но не "номер записи". В многопользовательской среде перечитывание данных по таймеру может вызвать большой сетевой трафик. Да и кроме того, клиент обычно видит в гриде только какую-то часть данных, и идеальным вариантом было бы не только узнать, что в именно этой части данных произошли изменения, но и перечитать только эту часть. В итоге, самым разумным вариантом является помещение в приложение на нужную форму кнопки Refresh (Перечитать). Пусть пользователь решает, когда ему нужно это сделать.
примечание:
в отличие от атомарного SELECT, оператор FOR SELECT внутри процедур IB не является атомарным. Т.е. если в цикле FOR SELECT добавлять записи, то они могут попасть в область видимости FOR SELECT, и может произойти "бесконечный цикл". Также в IB неатомарной является конструкция INSERT INTO ... SELECT FROM.
Завершение транзакций
BDE устроен так, что компонент TDatabase может работать только с одной транзакцией одновременно. При этом может быть два режима - неявная работа с транзакциями (AUTOCOMMIT, NOAUTOCOMMIT), и явная работа с транзакциями (методы StartTransaction, Commit и Rollback). В режиме AUTOCOMMIT BDE самостоятельно завершает транзакцию и стартует новую при любых модификациях данных (insert/update/delete) или при вызове TStoredProc.ExecProc. Таким образом изменения автоматически сохраняются в базе данных. Однако чтение данных и вообще работа с ними может быть выполнена только в контексте транзакции. Т.е. вне транзакции с данными работать нельзя, т.к. не будет обеспечиваться целостность данных. При этом данные, прочитанные в одной транзакции, неактуальны для другой транзакции. Если посмотреть справку BDE32.HLP по функции dbiEndTran, то можно обнаружить, что BDE при завершении явной или неявной транзакции ведет себя следующим образом:
открытый query довыбирает данные.
открытый table закрывается
другие случаи я не упомянул, потому что IB SQL Link их не поддерживает. То есть при любом завершении транзакции (и открытии новой) данные будут перечитываться. Для TTable это не смертельно, т.к. он знает первичный ключ записи, на которой стоял курсор грида, и может перечитать немного данных, чтобы заново отобразить их. А вот для TQuery, который не знает никаких первичных ключей, происходит полная выборка всех данных, что эквивалентно вызову Locate, FetchAll или Last. Так что если ваше приложение при обновлении данных почему-то сильно тормозит, или возникают паузы, то нужно срочно смотреть в SQL Monitor, какие именно запросы перечитываются.
примечание:
иногда по неизвестным причинам BDE перечитывает запросы, которые совершенно этого не требуют. Например мне встречалась ситуация с неявным перевыполнением запроса при перемещении по grid-у detail-таблицы, причем запрос никак не был связан ни с master ни с detail-таблицами. Избавиться от проблемы не удалось.
Соответственно, чтобы предотвратить плохую производительность, нужно или держать минимум данных открытыми в TQuery, или стремиться к минимизации количества записей, выбираемых TQuery. Также можно открыть второй TDatabase, и работать например со справочными таблицами только в нем. Таким образом изменения будут идти в одном коннекте, и не будут вызывать завершение транзакции и перечитывание данных в другом. В компонентах прямого доступа это решается более простым способом, т.к. там поддерживается произвольное количество транзакций для одного коннекта. Есть, кстати, и оригинальное решение, которое позволяет использовать коннект TDatabase совместно с компонентами FreeIBComponents или IBX:
var
h: tisc_db_handle;
DB := TIBDatabase.Create(nil);
try
Dbtables.Check(DbiGetProp(HDBIOBJ(DMCommBilling.Database.Handle), dbNATIVEHNDL, @h, sizeof(tisc_db_handle), l));
DB.DBName := 'Cloned';
DB.Handle := h;
TR := TIBTransaction.Create(nil);
try
и так далее. Таким образом, в приложении BDE можно дополнительно обрабатывать данные в транзакциях IBX. Приложение получается комбинированным, поскольку для доступа к данным в новой транзакции придется использовать компоненты IBX.
Record/Key deleted
Надо сказать, что BDE облегчает жизнь программисту хотя бы тем, что перечитывает запись, которую собирается редактировать пользователь. Т.е. как только BDE переводит TTable или "живой" TQuery в режим Edit, он производит выборку текущей записи (по первичному ключу) и показывает для редактирования самые последние, актуальные, данные. Правда, пока пользователь редактирует запись, ее могут изменить или даже удалить другие пользователи - BDE никоим образом не "блокирует" запись, которая редактируется, т.к. в SQL вообще нет команды вроде "заблокировать запись". Поэтому после Post клиент может обнаружить, что его изменения не попадут в базу данных, т.к. запись уже изменилась или удалена. И обнаружит он это или нет, зависит от режима TDataSet.UpdateMode.
UpdateMode имеет 3 режима:
upWhereAll
По умолчанию - BDE пытается сделать UPDATE с внесением в условие WHERE всех значений полей, которые были ДО момента редактирования. Если при этом произошла ошибка, значит хотя бы одно поле у редактируемой записи уже было кем-то изменено (с момента входа в режим редактирования до момента Post).
upWhereChanged
BDE пытается сделать UPDATE с условием WHERE, проверяющим старые значения только измененных полей. Т.е. чтобы убедиться, что пользователь поменял именно те значения полей, которые видел, на новые. Если произошла ошибка, то это значит что одно из изменяемых полей было уже кем-то изменено.
upWhereKeyOnly
BDE обновляет запись, устанавливая в WHERE поиск записи только по ее первичному ключу.
Соответственно, если запись не найдена, то выдается упомянутое в заголовке сообщение Record/Key deleted. Обратите внимание, что успешное обновление записи в режимах upWhereChanged или upWhereKeyOnly может вызвать проблемы с конкурентным обновлением. Например, существует таблица TABLE, у которой три поля: ID, NAME и PRICE.
Два пользователя открывают таблицу. Один видит, что для данного имени товара неверно указана цена. Другой счел, что цена правильная, только имя товара указано с ошибкой. У обоих UpdateMode установлен в upWhereKeyOnly или upWhereChanged.
После изменения пользователи по очереди нажимают Post (вероятность одновременного нажатия достаточно низка, а кто из них нажал на кнопку первым не имеет значения). В результате оказалось изменено и название товара и его цена, и комбинация этих полей опять содержит неправильную информацию!
В данном частном случае избавиться от проблемы можно установкой UpdateMode только upWhereAll, чтобы запрос при обновлении проверял все зависимые поля. Или, можно подключить компонент TUpdateSQL и прописать для обновления данных запрос, который будет проверять на "старые" значения и имя товара и его цену. Однако работать с TUpdateSQL без CachedUpdates невозможно.
Другая причина, по которой может происходить сообщение Record/Key deleted - перечитывание данных после их обновления. BDE таким образом (по крайней мере для TTable) пытается вставить запись в нужное место (в порядке сортировки) кэша. Но если после вставки или обновления запись на сервере изменилась - другим пользователем, default-условием или триггером (с генератором) - то BDE не сможет ее найти и выдаст упомянутое сообщение.
Если запись от момента редактирования до момента перечитывания была изменена другим пользователем, то тут ничего нельзя сделать. Если это был default или триггер, то вполне возможно, что лучше отказаться от считывания таких полей в DBGrid (вызовите FieldEditor). Если же это поле первичного ключа, которому в триггере присваивается значение генератора, то вам явно стоит прочитать статью, которая за 4 года существования не потеряла своей актуальности.
Cached Updates
При работе без CachedUpdates изменения, производимые над данными, отправляются на сервер немедленно. Это достаточно удобно, т.к. позволяет немедленно обнаруживать конфликты изменений, но не всегда хорошо для сетевого трафика если нет явного управления транзакциями или приводит к накоплению версий записей при длительных явных транзакциях. В первую очередь режим CachedUpdates подходит для "блокировочных" серверов, в которых чтение данных блокирует их от изменения (например MS SQL, Sybase).
CachedUpdates позволяет накопить изменения, и затем "выстрелить" их на сервер одним пакетом. При этом время блокировок минимально, минимален также сетевой трафик, но существует высокая вероятность что данные уже успели измениться. Поэтому при использовании CU необходимо тщательно планировать именно процесс обращения к таблицам и режимы UpdateMode.
За более подробной информацией по CachedUpdates обращайтесь к документации или к книге Шумакова ("Delphi 3 и создание приложений баз данных", в том числе последующие издания для Delphi 4 и 5 в соавторстве с Фароновым), где все это очень хорошо описано. Нас сейчас CU больше интересует как замена RequestLive.
Действительно, "оживление" запроса выполняется следующим образом - к компоненту TQuery подключается компонент TUpdateSQL, в котором прописываются вручную или автоматически запросы на вставку, удаление или изменение записи. Заметьте, только одной записи. После включения CachedUpdates:=True при модификации данных именно эти запросы, а не конструируемые Local SQL при RequestLive=True, будут отправляться на сервер (отправляются они только в момент ApplyUpdates, а не в момент реального обновления записи).
Самым непонятным является то, почему связка TQuery и TUpdateSQL не может работать без CachedUpdates. Например компоненты IBX без проблем обеспечивают такой режим, да и вообще там у TIBQuery нет свойства RequestLive (т.к. нет парсера SQL на клиентской стороне). Т.е. в IBX, конечно, можно использовать CachedUpdates, но разве что при действительной в нем необходимости.
Гетерогенные запросы
BDE обладает уникальной способностью выполнять запросы, которые обращаются к таблицам, находящимся на разных серверах баз данных (или к таблицам разных форматов). Это так называемые "гетерогенные" запросы. Иногда их называют "распределенными", т.к. данные "распределены" по разным базам данных возможно одного и того же SQL-сервера.
Выполнить гетерогенный запрос можно следующим образом:
Открыть 2 или более TDatabase, каждый для соответствующей базы данных. Например, один компонент подсоединен как A к алиасу TEST, а другой, как
Открыть TDatabase, который подсоединен к драйверу типа STANDARD(т.е. к локальным таблицам. Существование оных необязательно). См. окно свойств TDatabase.
Выполнить запрос в компоненте TQuery, подсоединенном к "стандартному" TDatabase. В результате должна получиться такая "конструкция"
а запрос иметь вид
SELECT C.CLIENT_NAME
FROM ":A:CLIENTS" C, ":B:EMPLOYEE" E
WHERE E.EMP_NO = C.CLIENT_ID
Конечно, по смыслу это полная чушь, но зато показывает пример указания таблиц из разных базах данных. Еще один пример запроса можно найти по ключевой фразе 'heterogeneous joins' в BDE32.HLP.
Пока я готовил и проверял этот пример, установка Query1.Active в true вызывала страшные содрогания винчестера. Дело в том, что подобные запросы выполняются следующим образом:
Ядро Local SQL "разбирает" запрос, и выясняет, какие таблицы из каких баз данных используются в запросе
Данные из каждой таблицы вытаскиваются в локальный кэш (т.е. на клиента), в память или временные таблицы.
Извлеченные данные обрабатываются локальным SQL (join, where, order by и т.п.).
Однако происходит так не всегда. По крайней мере в моем тестовом случае Local SQL начал выполнять просто чудовищные операции:
Сначала для одной, а затем для другой таблицы был выполнен SELECT COUNT(*). Т.е. Local SQL сначала пытается понять, во что ему обойдется скачивание данных на клиентскую часть. Очевидно, записей в CLIENTS ему показалось мало, и он вытащил все записи из EMPLOYEE, а потом начал последовательно выбирать соответствующие записи из CLIENTS отдельными запросами для каждой записи (проверяя соответствие условия WHERE). Буквально SELECT ... FROM CLIENTS WHERE CLIENT_ID = ? ORDER BY CLIENT_ID ASC.
(зачем здесь нужен order by - неизвестно). Почему произошло не наоборот, т.е. меньшая таблица не была выбрана в память, неясно.
Можно даже не упоминать, что select count(*) на реальных данных может выполняться долго (даже без учета возможной сборки мусора). Не говоря о том, что в EMPLOYEE было 42 записи, и отдельных запросов к таблице CLIENTS получилось тоже 42.
Вот такая веселая арифметика. Зато получены четкие объяснения, почему "трещал" винчестер.
Однако, пусть даже и таким жутким способом, но BDE умеет выполнять гетерогенные запросы. Благодаря Local SQL и тому, что BDE умеет работать с локальными таблицами (которые он использует для хранения промежуточных данных таких запросов). Ни IBObjects, ни FIBC/IBX, ни IB API не имеют таких возможностей, и соответственно, не могут выполнять гетерогенные запросы.
Итог
После прочтения этой статьи может сложиться впечатление, что BDE вообще не пригоден для работы с SQL-серверами. На самом деле это не так. Если знать его архитектуру (надеюсь, статья вам в этом помогла), то можно снизить неэффективность BDE в приложениях до минимума.
Другой важный момент - скорость разработки. Она до сих пор остается самой высокой по сравнению с другими наборами компонент (даже с IBObjects). А скорость разработки - это в первую очередь более низкая стоимость разработки системы.
Кстати, может оказаться, что вся эта "неэффективность" в смысле большого объема передаваемых данных на вашей 100мбит сети и не проявится. А если сеть гигабитная, то вы вообще никакого лишнего трафика не заметите. И наоборот - для модемных соединений BDE, конечно, никуда не годится. Или если вам нужно тщательное планирование и управление транзакциями IB, то BDE здесь тоже делать нечего.
Есть и более жесткие критерии выбора - если вы собираетесь переходить на Kylix или IB6 (диалект 3), то c BDE придется расстаться. Если же в течение ближайшего года или полутора вы не собираетесь этого делать - забудьте об альтернативах, и продолжайте работать привычным способом.
Взято с
Арифметика, системы счисления, комплексные числа
Арифметика, системы счисления, комплексные числа
Cодержание раздела:
Арифметика указателей
Арифметика указателей
Сначала короткое объяснение арифметике указателя. Когда вы имеете дело с динамическими страницами памяти, то все, что вы имеете - это указатели на начала блоков памяти. Вы хотите просмотреть всю строку памяти, чтобы понять какие функции необходимы для работы с данными, хранящимися в памяти? Это возможно путем изменения места в памяти, на которое указывает указатель. Это называется арифметикой указателя.
Основополагающая идея при занятиях арифметикой с указателем - указатель должен быть увеличен на значение корректного приращения. (Корректное приращение определяется размером объекта, на который показывает указатель. Например, char = 1 байт; integer = 2 байта; double = 8 байт и т.д.) Функции Inc() и Dec() изменяют значение корректного приращения. (Компилятор знает правильный размер объекта.)
Если вы осуществляете динамическое распределение памяти, то делать это можно примерно так:
usesWinCRT;
procedure TForm1.Button1Click(Sender: TObject);
var
MyArray: array[0..30] of char;
b: ^char;
i: integer;
begin
StrCopy(MyArray, 'Дельфи - рулез фарева!');
{помещаем что-то в память для организации указателя}
b := @MyArray; { назначаем указатель на текущую позицию памяти }
for i := StrLen(MyArray) downto 0 do
begin
write(b^); { пишем символ в текущую позицию указателя. }
inc(b); { перемещаем указатель на следующий байт памяти }
end;
end;
Нижеследующий код демонстрирует работу функций Inc() и Dec(), увеличивающих или уменьшающих указатель на размер соответствующего типа:
var
P1, P2: ^LongInt;
L: LongInt;
begin
P1 := @L; { назначаем оба указателя на одно и то же место }
P2 := @L;
Inc(P2); { Увеличиваем один }
{ Здесь мы получаем разницу между смещениями двух
указателей. Поскольку первоначально они указывали на одно
и то же место памяти, то результатом данного вызова
будет разница между двумя указателями после вызова Inc(). }
L := Ofs(P2^) - Ofs(P1^); { L = 4; т.е. sizeof(longInt) }
end;
Вы можете изменить тип объекта, на который указывает P1 и P2, на какой-то другой и убедиться, что (SizeOf(P1^)) всегда возвращает величину корректного приращения (проще сказать, что это размер объекта - В.О.).
Взято с
ASCII драйвер для CSV-файлов
ASCII драйвер для CSV-файлов
Использование драйвера ASCII для файлов с разделительной запятой
Delphi (и BDE) имеют способность использовать ASCII файлы для хранения таблиц. Драйвер ASCII имеет возможность транслировать значения данных ASCII-поля фиксированной длины или файла с разделительной запятой в поля и величины, которые могут отображаться компонентом TTable. Трансляция ASCII файла целиком зависит от сопровождающего файла схемы (Schema File). Файл схемы для файла ASCII данных определяет различные атрибуты, необходимые для преобразования данных ASCII файла в значения отдельных полей. Определения полей для файла с ASCII полями фиксированной длины достаточно простая задача, необходимо знать позиции всех полей, для всех строк они одинаковы. Для файлов с разделительной запятой данный процесс чуть более усложнен из-за того, что не все данные в таком файле во всех строках имеют одинаковую длину. Данный совет как раз и концентрируется на описании этой трудной темы, связанной с чтением данных из файлов с разделительной запятой, имеющих варьируемую длину поля.
Файл схемы
Файл схемы для файла данных ASCII содержит информацию, которая определяет оба типа файла (версии с разделительной запятой и полем с фиксированной длиной), а также определяет поля, которые представлены значениями данных в каждой строке файла данных ASCII. (Все поля файла схемы нечуствительны к регистру, поэтому написание "ascii" равнозначно написанию "ASCII".) Для того, чтобы файл схемы был признан в качестве такового, он должен иметь то же имя, что и файл данных ASCII, для которого он содержит схему, но иметь расширение .SCH (SCHema - схема). Атрибуты описания файла:
File name: Располагаемый в квадратных скобках, данный атрибут определяет
имя файла ASCII данных (с расширением имени файла,
которое должно быть .TXT).
Filetype: Определяет, имеет ли файл ASCII данных структуру файла с
полями фиксированной длины (используется атрибут FIXED) или
файлом с разделительной запятой (со значениями данных, которые
потенциально могут изменять длину (используется атрибут VARYING).
Delimiter: Определяет символ, которым "окантуривают" значения данных типа
String (обычно двойные кавычки, десятичный ASCII код 34).
Separator: Определяет символ, который используется для разделения отдельных
значений данных (обычно запятая). Данный символ должен быть
видимым символом, т.е. не может быть пробелом (десятичный ASCII
код 32).
CharSet: Определяет драйвер языка (используется атрибут ASCII).
Расположенные ниже атрибуты файла являются определениями поля, задающими правила для каждой строки файла данных ASCII. Данные определения служат источником информации для Delphi и BDE, первоначально необходимой для создания виртуального поля в памяти, в свою очередь служащее для хранения значений данных; тип данных виртуального поля определяется после чтения и трансляции данных из ASCII файла, определения размера и применения атрибутов. Различные атрибуты, определяющие поле файла данных ASCII:
Field: Имя виртуального поля (всегда будет "Field"), сопровождаемое
целым числом, определяющим порядковый номер поля относительно
других полей в файле данных ASCII. Например, первое поле -
Field1, второе Field2, и т.д..
Field name: Определяет выводимое имя поля, отображаемое в виде
заголовка колонки в TDBGrid. Соглашения имен для
таблиц ASCII такие же, как и для таблиц Paradox.
Field type: Определяет, какой тип данных BDE должен использоваться при
трансляции значений данных каждого поля и сообщает
Delphi тип виртуального поля, которое необходимо создать.
Используйте определение Для значений типа
----------------------- ----------------------------
CHAR Символ
FLOAT 64-битное число с плавающей точкой
NUMBER 16-битное целое
BOOL Boolean (T или F)
LONGINT 32-битное длинное целое
DATE Поле Date.
TIME Поле Time.
TIMESTAMP Поле Date + Time.
(Фактически формат для значений данных даты и времени
будет определяться текущими настройками конфигурации BDE,
страница с закладкой Date.)
Data value length: Максимальная длина значения данных соответствующего поля.
Данный атрибут определяет длину виртуального поля,
создаваемое Delphi для получения считываемых значений из
ASCII-файла.
Number of decimals: Приложение к полю типа FLOAT; определяет количество цифр
справа от десятичной точки; необходимо для включения в
определение виртуального поля.
Offset: Отступ от начала строки, позиция начала данных описываемого
поля; задается для всех строк файла.
Например, приведенное ниже определение поля относится к первому полю таблицы ASCII. Данная строка определяет значения данных типа String с именем "Text", максимальная длина значения данных составляет три символа (и в Delphi компонентах для работы с базами данных, типа TDBGrid, поле будет отображаться только тремя символами), десятичный порядок (значение данных типа String никогда не сможет иметь десятичные значения, тем более после запятой), и смещение относительно нулевой позиции (поскольку описываемая область первая, то она сама начинается с нулевой позиции, перед ней не находится ни одно поле).
Field1=Text,Char,3,00,00
Вот пример файла схемы с тремя полями, первое поле имеет тип String, второе и третье тип Date. Данный файл схемы должен содержаться в файле с именем DATES.SCH и обеспечивать определения полей для файла данных ASCII с именем DATES.TXT.
[DATES]
Filetype=VARYING
Delimiter="
Separator=,
CharSet=ascii
Field1=Text,Char,3,00,00
Field2=First Contact,Date,10,00,03
Field3=Second,Date,10,00,13
Данная схема определяет поле с разделительной запятой, где все данные могут быть отнесены к типу String, значения полей "окантурены" двойными кавычками и отдельные значения полей разделены запятой (за исключением любых запятых, которые могут находится между разделительными запятыми, внутри отдельных значений полей типа String). Первое поле типа character имеет длину три символа, без определения десятичного порядка и с нулевым отступом от начала строки. Второе поле данных имеет длину 10, без определения десятичного порядка и отступ, равный трем. Третье поле данных имеет длину 10, без определения десятичного порядка и отступ, равный 13.
Для чтения файлов ASCII с разделительной запятой, параметры длины и отступа для определения поля относятся не к значениям данных в файлах ASCII (что явно противоположно для файлов с полями фиксированной длиной), а к виртуальным полям, определяемым в приложении, в котором будет размещены считываемые данные. Параметр длины должен отражать максимальную длину значений данных для каждого поля, не считая огриничительных кавычек и разделительной запятой. Это наиболее трудно при оценке значений данных типа String, поскольку фактическая длина значений данных может быть существенно различаться для каждой строки в файле данных ASCII. Параметр отступа для каждого поля не будет являться позицией значений данных в файле ASCII (что относится к файлам с полями фиксированной длины), а представляет собой совокупную длину всех предыдущих полей (и снова, определение полей в памяти, не значения данных в файле ASCII).
Вот файл данных с именем DATES.TXT, который соответствует описанному выше файлу схемы:
"A",08/01/1995,08/11/19955
"BB",08/02/1995,08/12/1995
"CCC",08/03/1995,08/13/1995
Максимальная длина фактических значений данных в первом поле составляет три символа ("CCC"). Поскольку это первое поле и предшествующих полей не существует, отступ для данного поля равен нулю. Длина первого поля (3) используется в качестве отступа для второго поля. Длина второго поля, значение date, равно 10 и отражает максимальную длину значения данных этого поля. Совокупная длина первого и второго полей используется в качестве значения отступа для третьего поля (3 + 10 = 13).
Только когда соответствующая длина значения данных ASCII файла или длина каждого поля добавляется к длине предыдущих полей, вычисляется значение отступа и получается позиция очередного поля, только тогда данный процесс правильно считает данные. Если из-за неправильных установочных параметров в файле схемы данные транслируются неверно, то в большинстве типов полей могут возникнуть неблагоприятные эффекты типа обрезания строк, или интерпретирование цифр как нулей. Обычно в таком случае данные выводятся, но ошибки не возникает. Тем не менее, значения определенного формата в процессе трансляции в подходящий тип данных могут вызвать ошибку, если считываемые символы не соответствуют символам, например, в типе date. В контексте вышесказанного, ошибка с типом datе может возникнуть и-за того, что при неправильном определении в значения данных могут попасть данные другого, соседнего поля. При таком стечение обстоятельств трансляция данных прерывается, и в файле схемы требуется установка правильной длины поля и его отступа.
Взято с
ASCII-файл с использованием полей
ASCII-файл с использованием полей
В том случае, когда вы собираетесь использовать содержимое текстового файла таким образом, как будто он имеет поля, вам необходим файл схемы, содержащий описание формата текстового файла и который необходим для осуществления вызовов при работе с полями (Fields / FieldByName / Post / и др.). Ниже приводится код, который вы можете использовать при создании своей программы:
{Подразумеваем, что Table1 - файл, который мы хотим скопировать в ASCII-файл. Используем TBatchMove, поскольку быстро работает. Также это автоматически создаст файл схемы }
procedure TForm1.Button1Click(Sender: TObject);
var
oDest: TTable;
oBMove: TBatchMove;
begin
try
oDest := nil;
oBMove := nil;
Table1.Close;
oDest := TTable.Create(nil);
with oDest do
begin
DatabaseName := 'c:\delphi\files';
TableName := 'Test.Txt';
TableType := ttASCII;
end; {Обратите внимание на то, что нет необходимости вызывать CreateTable}
oBMove := TBatchMove.Create(nil);
with oBMove do
begin
Source := Table1;
Destination := oDest;
Mode := batCopy;
Execute;
end;
finally
if Assigned(oDest) then oDest.Free;
if Assigned(oBMove) then oBMove.Free;
end;
end;
{ Теперь, допустим, файл схемы существует; сам текстовый файл может как быть, так его может и не быть. С помощью файла схемы мы уже можем работать с полями }
procedure TForm1.Button2Click(Sender: TObject);
var
oTxt: TTable;
i: Integer;
f: System.Text;
begin
try
oTxt := nil;
if not FileExists('c:\delphi\files\Test.Txt') then
begin
AssignFile(f, 'c:\delphi\files\Test.Txt');
Rewrite(f);
CloseFile(f);
end;
oTxt := TTable.Create(nil);
with oTxt do
begin
DatabaseName := 'c:\delphi\files';
TableName := 'Test.Txt';
TableType := ttASCII;
Open;
end;
with Table1 do
begin
DisableControls;
if not Active then Open;
First;
while not EOF do
begin
oTxt.Insert;
{ В данном случае файл схемы описывает формат текстового файла; в этом
примере фактически один к одному воспроизводятся поля таблицы
в логическое определение полей в .sch-файле }
for i := 0 to FieldCount - 1 do
oTxt.Fields[i].AsString := Fields[i].AsString;
oTxt.Post;
Next;
end;
end;
finally
Table1.EnableControls;
if Assigned(oTxt) then oTxt.Free;
end;
end;
OAmiry/Borland
Взято из
Советов по Delphi от
Сборник Kuliba
АТ команды модема
АТ команды модема
A
Команда ответа (Answer Command)Bn
Настройка связи (Communications Options)D
Команда набора (Dial Command)En
Команда выбора символа эха (Select Command Character Echo Option)Hn
Управление Switchhook - эмуляция нажатия телефонного рычага (Control The Switchhook)I0
Идентификация кода продукта (Identify The Product Code)I2
Выполнение теста контрольной суммы ROM ( Perform ROM Checksum Test)I7
Номер версии (Version Number)Ln
Выбор уровня громкости динамика (Select Speaker Volume Level)Mn
Функция выбора опций динамика (Select Speaker Function Option)Nn
Выбор опций для установления связи (Select Negotiate Handshake Option)On
Переход к онлайновым командам (Go Online Command)P
Выбор метода пульсового набора (Select Pulse Dialing Method)Qn
Выбор опции результирующего кода (Select Result Code Option)Sn=
Запись в S-регистр (Write To An S-Register)Sn?
Чтение S-регистра (Read An S-Register)T
Выбор метода тонового набора (Select Tone Dialing Method)Vn
Выбор опции формата ответа (Select Response Format Option)Wn
Выбор расширенного результирующего кода (Select Extended Result Code)Xn
Выбор опции модемного вызова (Select Call Progress Option)Yn
Выбор опции бездействия для разъединения (Select Long Space Disconnect Option)Zn
Выполнение мягкого сброса (Perform Soft Reset)&An
Выбор роли автоответчика (Select Originate/Answer Role For Autoanswer)&Cn
Выбор опции определения передаваемых данных (Select Data Carrier Detect Option)&Dn
Выбор опции готовности терминала данных (Select Data Terminal Ready Option)&F
Загрузка заводских установок (Load Factory Default Profile)&Gn
Выбор опции защиты тонового набора (Select Guard Tone Option)&Kn
Выбор опций потока ConTDol (Select Flow ConTDol Option)&Pn
Выбор параметров пульсового набора (Select Pulse Dialing Parameters)&Qn
Выбор опций режима связи (Select Communications Mode Option)&Rn
Выбор опций RTS/CTS (Select RTS/CTS Option)&Sn
Выбор опций готовности передачи данных (Select Data Set Ready Option)&T0
Тест завершения в процессе (Terminate Test In Process)&T1
Инициирование локального аналога сетевой петли (Initiate Local Analog Loopback)&T3
Выполнение локальной цифровой сетевой петли (Perform Local Digital Loopback)&T4
Включение предоставления RDL-запросов (Enable Granting Of RDL Requests)&T5
Запрет предоставления RDL-запросов (Deny Granting Of RDL Requests)&T6
Инициирование удаленной цифровой сетевой петли (Initiate Remote Digital Loopback)&T7
Иниицирование внутреннего теста RDL (Initiate RDL With Self Test)&T8
Внутренний тест локальной сетевой петли (Local Loopback With Self Test)&T19
Выполнение теста RTS/CTS кабеля (Perform RTS/CTS Cable Test)&Un
Отмена TDellis кодирования (Disable TDellis Coding)&V
Просмотр профилей конфигурации (View Configuration Profiles)&Wn
Сохранение активного профиля (Store Active Profile)&Xn
Выбор источника синхронизации времени TDansmit (Store Active Profile)&Yn
Выбор сохранения профиля для аппаратного перезапуска (Select Stored Profile For Hard Reset)&Zn=
Сохранение телефонного номера (Store Telephone Number),
Пауза (Perform Pause)=
Запись в S-регистр (Write To An S-Register)?
Чтение S-регистра (Read An S-Register)P
Выбор пульсового набора (Select Pulse Dialing)Т
Тоновый набор (Tone)Взято из
Советов по Delphi от
Сборник Kuliba
AVI файл проигрывается снова и снова
AVI файл проигрывается снова и снова
В примере AVI файл проигрывается снова и снова - используем событие MediaPlayer'а Notify
procedure TForm1.MediaPlayer1Notify(Sender: TObject);
begin with MediaPlayer1 do
if NotifyValue = nvSuccessful then
begin
Notify := True;
Play;
end;
end;
Взято с сайта
Avoiding server side locking (including DEADLOCK)
Avoiding server side locking (including DEADLOCK)
What can I do to help avoid server side locking (including DEADLOCK)
problems when working with Microsoft SQL Server (DBLIB) and Sybase
SQL Server (DBLIB)?
The following suggestions may help you tune your application and
server. The server side suggestions may not apply to all server
and database installations.
From the client application you may want to take greater control
over the size of results sets (this may mean using TQueries),
minimize the length of transactions (usually not an issue if the
SQLPASSTHRU MODE is set to ...AUTOCOMMIT), and only open dbaware
controls when necessary to help minimize resource drain on the
server and, possibly in this case, deadlock potential.
The means by which the BDE selects data from each of the supported
servers does not make assumptions about how each server chooses
to ensure data integrity.
Delphi/BDE suggestions:
Work with smaller result sets (TQueries, server views, etc.)
also see above form more info.
Check the SQL Links MSSQL Driver "TDS Packet Size" param
making sure that it is set to, at least, 4096.
Minimize the length of transactions.
Investigate creating appropriate indexes.
Filter results before opening a dataset or use tqueries
(live or otherwise) to limit the number of rows selected.
Investigate using the BDE SQL PASSTHRU MODE parameter
"NOT SHARED" (please see BDEADMIN.HLP and BDE32.HLP for
addtional information on the SQL PASSTHRU MODE parameter)
Please note:
BDE/SQL Links 4.01 will not only detect and raise a deadlock
error but it will "reset" its database transaction state when
it detects an MSSQL error 1205. It is not necessary to rollback
the explicit transaction (Database1.rollback) after the
deadlock has been detected.
The error 1205 signals to the client that the server has
"resolved" a deadlock and chosen one of the users to end the
deadlock. This user's transaction is automatically rolled back.
Please refer to the MS SQL Server documentation for more
information on deadlock detection and server error 1205.
MS SQL and Sybase Server topics: (the following is by no means a
comprehensive list. Please check your Sybase and MS SQL Server
docs for tips on optimizing your server and databases)
Create indexes on the remote tables where possible (the
server may require more locks for unindexed tables.)
TEXT and IMAGE columns can take up more pages (columns
can be omitted from a SELECT statement if working with TQueries
whose REQUEST LIVE property is false)
page sizes on the server can be adjusted to better match
expected row sizes (this can help prevent the server from locking
adjacent rows.)
The server will create a table lock if the LOCK ESCALATION
level is reached (part of sp_configure)
Please also see:
MS SQl Server documentation (printed or Books Online)
If using TQueries:
TABLOCKX
UPDLOCK
For more information on the options above:
Analyzing locks Topic
(also see Database Developer's Companion Errata)
Автоматическая ширина колонок в TStringGrid
Автоматическая ширина колонок в TStringGrid
Можно ли сделать так чтобы TStringGrid автоматически изменял ширину колонок, чтобы вместить самую длинную строчку в колонке?
procedure AutoSizeGridColumn(Grid: TStringGrid; column: integer);
var
i: integer;
temp: integer;
max: integer;
begin
max := 0;
for i := 0 to (Grid.RowCount - 1) do
begin
temp := Grid.Canvas.TextWidth(grid.cells[column, i]);
if temp > max then max := temp;
end;
Grid.ColWidths[column] := Max + Grid.GridLineWidth + 3;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AutoSizeGridColumn(StringGrid1, 1);
end;
Автоматически нажимающаяся кнопка
Автоматически нажимающаяся кнопка
Этот компонент представляет из себя кнопку, на которую не надо нажимать, чтобы получить событие OnClick. Достаточно переместить курсор мышки на кнопку. При создании такого компонента традиционным способом, требуется довольно много времени, так как необходимо обрабатывать мышку, перехватывать её и т.д. Однако результат стоит того!
Предлагаю взглянуть на две версии данного компонента. В более простой версии обработчик перемещения мышки просто перехватывает сообщения Windows с нужным кодом и вызывает обработчик события OnClick:
type
TAutoButton1 = class(TButton)
private
procedure WmMouseMove (var Msg: TMessage);
message wm_MouseMove;
end;
procedure TAutoButton1.WmMouseMove (var Msg: TMessage);
begin
inherited;
if Assigned (OnClick) then
OnClick (self);
end;
Вторая версии имеет больше исходного кода, так как в ней я просто пытаюсь повторить событие мышки OnClick когда пользователь перемещает мышку над кнопкой либо по истечении определённого времени. Далее следует объявление класса:
type
TAutoKind = (akTime, akMovement, akBoth);
TAutoButton2 = class(TButton)
private
FAutoKind: TAutoKind;
FMovements: Integer;
FSeconds: Integer;
// really private
CurrMov: Integer;
Capture: Boolean;
MyTimer: TTimer;
procedure EndCapture;
// обработчики сообщений
procedure WmMouseMove (var Msg: TWMMouse);
message wm_MouseMove;
procedure TimerProc (Sender: TObject);
procedure WmLBUttonDown (var Msg: TMessage);
message wm_LBUttonDown;
procedure WmLButtonUp (var Msg: TMessage);
message wm_LButtonUp;
public
constructor Create (AOwner: TComponent); override;
published
property AutoKind: TAutoKind
read FAutoKind write FAutoKind default akTime;
property Movements: Integer
read FMovements write FMovements default 5;
property Seconds: Integer
read FSeconds write FSeconds default 10;
end;
Итак, когда курсор мышки попадает в область кнопки (WmMouseMove), то компонент запускает таймер либо счётчик количества сообщений о перемещении. По истечении определённого времени либо при получении нужного количества сообщений о перемещении, компонент эмулирует событие нажатия кнопкой.
procedure TAutoButton2.WmMouseMove (var Msg: TWMMouse);
begin
inherited;
if not Capture then
begin
SetCapture (Handle);
Capture := True;
CurrMov := 0;
if FAutoKind <> akMovement then
begin
MyTimer := TTimer.Create (Parent);
if FSeconds <> 0 then
MyTimer.Interval := 3000
else
MyTimer.Interval := FSeconds * 1000;
MyTimer.OnTimer := TimerProc;
MyTimer.Enabled := True;
end;
end
else // захватываем
begin
if (Msg.XPos > 0) and (Msg.XPos < Width)
and (Msg.YPos > 0) and (Msg.YPos < Height) then
begin
// если мы подсчитываем кол-во движений...
if FAutoKind <> akTime then
begin
Inc (CurrMov);
if CurrMov >= FMovements then
begin
if Assigned (OnClick) then
OnClick (self);
EndCapture;
end;
end;
end
else // за пределами... стоп!
EndCapture;
end;
end;
procedure TAutoButton2.EndCapture;
begin
Capture := False;
ReleaseCapture;
if Assigned (MyTimer) then
begin
MyTimer.Enabled := False;
MyTimer.Free;
MyTimer := nil;
end;
end;
procedure TAutoButton2.TimerProc (Sender: TObject);
begin
if Assigned (OnClick) then
OnClick (self);
EndCapture;
end;
procedure TAutoButton2.WmLBUttonDown (var Msg: TMessage);
begin
if not Capture then
inherited;
end;
procedure TAutoButton2.WmLButtonUp (var Msg: TMessage);
begin
if not Capture then
inherited;
end;
Взято с Исходников.ru
Автоматический logon к локальной InterBase
Автоматический logon к локальной InterBase
Используйте компонент TDatabase. В строках Params пропишите:
USER NAME=sysdba
PASSWORD=masterkey
Затем установите свойство компонента TDataBase LoginPrompt в False.
После этого, с помощью свойства DataBaseName, вы должны создать прикладной псевдоним (Alias) и связать TQuery/TTable с вашим компонентом TDataBase
Взято из
Советов по Delphi от
Сборник Kuliba
Автозагрузка програм (как и откуда?)
Автозагрузка програм (как и откуда?)
По материалам:
1. Autostart folder
C:\windows\start menu\programs\startup {english}
This Autostart Directory is saved in :
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders]
Startup="C:\windows\start menu\programs\startup"
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders]
Startup="C:\windows\start menu\programs\startup"
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\explorer\User Shell Folders]
"Common Startup"="C:\windows\start menu\programs\startup"
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\explorer\Shell Folders]
"Common Startup"="C:\windows\start menu\programs\startup"
By setting it to anything other then C:\windows\start menu\programs\startup will lead to execution of ALL and EVERY executable inside set directory.
2. Win.ini
[windows]
load=file.exe
run=file.exe
3. System.ini
[boot]
Shell=Explorer.exe file.exe
4. c:\windows\winstart.bat
'Note behaves like an usual BAT file. Used for copying deleting specific files. Autostarts everytime.
5. Registry
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices]
"Whatever"="c:\runfolder\program.exe"
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce]
"Whatever"="c:\runfolder\program.exe"
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run]
"Whatever"="c:\runfolder\program.exe"
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce]
"Whatever"="c:\runfolder\program.exe"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx\000x]
"RunMyApp"="notepad.exe"
The format is: "DllFileName|FunctionName|CommandLineArguements" -or- "command parameters"
Microsoft Windows 98 Microsoft
Windows 2000 Professional
Microsoft Windows 2000 Server
Microsoft Windows 2000 Advanced Server
Microsoft Windows Millennium Edition
http://support.microsoft.com/support/kb/articles/Q232/5/09.ASP
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run]
"Whatever"="c:\runfolder\program.exe"
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce]
"Whatever"="c:\runfolder\program.exe"
6. c:\windows\wininit.ini
'Often Used by Setup-Programs when the file exists it is run ONCE and then is deleted by windows
Example content of wininit.ini :
[Rename]
NUL=c:\windows\picture.exe
' This example sends c:\windows\picture.exe to NUL, which means that it is being deleted. This requires no interactivity with the user and runs totaly stealth.
7. Autoexec.bat
Starts everytime at Dos Level.
8. Registry Shell Spawning
[HKEY_CLASSES_ROOT\exefile\shell\open\command] @="%1" %*
[HKEY_CLASSES_ROOT\comfile\shell\open\command] @="%1" %*
[HKEY_CLASSES_ROOT\batfile\shell\open\command] @="%1" %*
[HKEY_CLASSES_ROOT\htafile\Shell\Open\Command] @="%1" %* [HKEY_CLASSES_ROOT\piffile\shell\open\command] @="%1" %*
[HKEY_LOCAL_MACHINE\Software\CLASSES\batfile\shell\open\command] @="%1" %*
[HKEY_LOCAL_MACHINE\Software\CLASSES\comfile\shell\open\command] @="%1" %*
[HKEY_LOCAL_MACHINE\Software\CLASSES\exefile\shell\open\command] @="%1" %*
[HKEY_LOCAL_MACHINE\Software\CLASSES\htafile\Shell\Open\Command] @= "%1" %*
[HKEY_LOCAL_MACHINE\Software\CLASSES\piffile\shell\open\command] @="%1" %*
The key should have a value of Value <"%1" %*>, if this is changed to <server.exe "%1 %*">, the server.exe is executed EVERYTIME an exe/pif/com/bat/hta is executed.
Known as Unkown Starting Method and is currently used by Subseven.
9. Icq Inet
[HKEY_CURRENT_USER\Software\Mirabilis\ICQ\Agent\Apps\test]
"Path"="test.exe"
"Startup"="c:\\test"
"Parameters"=""
"Enable"="Yes"
[HKEY_CURRENT_USER\Software\Mirabilis\ICQ\Agent\Apps\
This key includes all the APPS which are executed IF ICQNET Detects an Internet Connection.
10. Explorer start-up
Windows 95,98,ME
Explorer.exe ist started through a system.ini entry, the entry itself contains no path information so if c:\explorer.exe exist it will be started instead of c:\$winpath\explorer.exe.
Windows NT/2000
The Windows Shell is the familiar desktop that's used for interacting with Windows. During system startup, Windows NT 4.0 and Windows 2000 consult the "Shell" registry entry, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell, to determine the name of the executable that should be loaded as the Shell.
By default, this value specifies Explorer.exe.
The problem has to do with the search order that occurs when system startup is in process. Whenever a registry entry specifies the name of a code module, but does it using a relative path, Windows initiates a search process to find the code. The search order is as follows:
Search the current directory.
If the code isn't found, search the directories specified in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path, in the order in which they are specified.
If the code isn't found, search the directories specified in HKEY_CURRENT_USER\Environment\Path, in the order in which they are specified.
More info : [URL=http://www.microsoft.com/technet/security/bulletin/fq00-052.asp]http://www.microsoft.com/technet/security/bulletin/fq00-052.asp[/URL]
Patch : [URL=http://www.microsoft.com/technet/support/kb.asp?ID=269049]http://www.microsoft.com/technet/support/kb.asp?ID=269049[/URL]
General :
If a trojan installs itself as c:\explorer no run keys or other start-up entries are needed. If c:\explorer.exe is a corrupted file the user will be locked out of the system. Affects all windows version as of today.
10. Active-X Component
HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed Components\KeyName
StubPath=C:\PathToFile\Filename.exe
Believe it or not, this does start filename.exe BEFORE the shell and any other Program normaly started over the Run Keys.
Misc Information
[HKEY_LOCAL_MACHINE\Software\CLASSES\ShellScrap] @="Scrap object"
"NeverShowExt"=""
The NeverShowExt key has the function to HIDE the real extension of the file (here) SHS. This means if you rename a file as "Girl.jpg.shs" it displays as "Girl.jpg" in all programs including Explorer.
Your registry should be full of NeverShowExt keys, simply delete the key to get the real extension to show up.
Взято с Vingrad.ru
Автозаполнение формы для нового письма
Автозаполнение формы для нового письма
А вот пример автозаполнения формы для нового письма в почтовой программе установленной по умолчанию:
uses shellapi;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
shellexecute(handle,
'Open',
'mailto:vit@vingrad.ru?subject=Regarding your advice&Body=First%20Line%0D%0ASecond%20line&CC=somebodyelse@vingrad.ru',
nil, nil, sw_restore);
end;
Немного пояснений:
1) Пробелы в тексте желательно заполнять сочетанием %20
2) Конец строки обозначать как %0D%0A
3) Поля отделять друг от друга символом &
Автор ответа Vit
Взято с Vingrad.ru
Base64 кодирование
Base64 кодирование
functionDecode(const S: AnsiString): AnsiString;
const
Map: array[Char] of Byte = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0);
var
I: LongInt;
begin
case Length(S) of
2:
begin
I := Map[S[1]] + (Map[S[2]] shl 6);
SetLength(Result, 1);
Move(I, Result[1], Length(Result))
end;
3:
begin
I := Map[S[1]] + (Map[S[2]] shl 6) + (Map[S[3]] shl 12);
SetLength(Result, 2);
Move(I, Result[1], Length(Result))
end;
4:
begin
I := Map[S[1]] + (Map[S[2]] shl 6) + (Map[S[3]] shl 12) +
(Map[S[4]] shl 18);
SetLength(Result, 3);
Move(I, Result[1], Length(Result))
end
end
end;
function Encode(const S: AnsiString): AnsiString;
const
Map: array[0..63] of Char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz0123456789+/';
var
I: LongInt;
begin
I := 0;
Move(S[1], I, Length(S));
case Length(S) of
1:
Result := Map[I mod 64] + Map[(I shr 6) mod 64];
2:
Result := Map[I mod 64] + Map[(I shr 6) mod 64] +
Map[(I shr 12) mod 64];
3:
Result := Map[I mod 64] + Map[(I shr 6) mod 64] +
Map[(I shr 12) mod 64] + Map[(I shr 18) mod 64]
end
end;
Взято с сайта
type TAByte = array [0..maxInt-1] of byte;
type TPAByte = ^TAByte;
function Encode(data:string) : string; overload;
const b64 : array [0..63] of char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var ic,len : integer;
pi, po : TPAByte;
c1 : dword;
begin
len:=length(data);
if len > 0 then begin
SetLength(result, ((len + 2) div 3) * 4);
pi := pointer(data);
po := pointer(result);
for ic := 1 to len div 3 do begin
c1 := pi^[0] shl 16 + pi^[1] shl 8 + pi^[2];
po^[0] := byte(b64[(c1 shr 18) and $3f]);
po^[1] := byte(b64[(c1 shr 12) and $3f]);
po^[2] := byte(b64[(c1 shr 6) and $3f]);
po^[3] := byte(b64[(c1 ) and $3f]);
inc(dword(po), 4);
inc(dword(pi), 3);
end;
case len mod 3 of
1 : begin
c1 := pi^[0] shl 16;
po^[0] := byte(b64[(c1 shr 18) and $3f]);
po^[1] := byte(b64[(c1 shr 12) and $3f]);
po^[2] := byte('=');
po^[3] := byte('=');
end;
2 : begin
c1 := pi^[0] shl 16 + pi^[1] shl 8;
po^[0] := byte(b64[(c1 shr 18) and $3f]);
po^[1] := byte(b64[(c1 shr 12) and $3f]);
po^[2] := byte(b64[(c1 shr 6) and $3f]);
po^[3] := byte('=');
end;
end;
end else
result := '';
end;
function Decode(data:string) : string; overload;
var i1,i2,len : integer;
pi, po : TPAByte;
ch1 : char;
c1 : dword;
begin
len:=length(data);
if (len > 0) and (len mod 4 = 0) then begin
len := len shr 2;
SetLength(result, len * 3);
pi := pointer(data);
po := pointer(result);
for i1 := 1 to len do begin
c1 := 0;
i2 := 0;
while true do begin
ch1 := char(pi^[i2]);
case ch1 of
'A'..'Z' : c1 := c1 or (dword(ch1) - byte('A') );
'a'..'z' : c1 := c1 or (dword(ch1) - byte('a') + 26);
'0'..'9' : c1 := c1 or (dword(ch1) - byte('0') + 52);
'+' : c1 := c1 or 62;
'/' : c1 := c1 or 63;
else begin
if i2 = 3 then begin
po^[0] := c1 shr 16;
po^[1] := byte(c1 shr 8);
SetLength(result, Length(result) - 1);
end else begin
po^[0] := c1 shr 10;
SetLength(result, Length(result) - 2);
end;
exit;
end;
end;
if i2 = 3 then
break;
inc(i2);
c1 := c1 shl 6;
end;
po^[0] := c1 shr 16;
po^[1] := byte(c1 shr 8);
po^[2] := byte(c1);
inc(dword(pi), 4);
inc(dword(po), 3);
end;
end else
result := '';
end;
....
var a,b:string;
begin
a:='aaa';
b:=Encode( a );
showmessage( b );
a:=Decode( b );
showmessage( a );
Автор P.O.D.
Базы данных
Базы данных
Cодержание раздела:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
(
раздел)·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
· (раздел)
·
· (раздел)
·
·
·
·
·
·
·
·
·
·
·
Базы данных с нуля (статья)
Базы данных с нуля (статья)
Итак, решил я открыть серию статей для помощи в овладении основными навыками программирования баз данных (БД, или DB - database). Я буду рассчитывать, что читатель имеет понятие о программировании в Дельфи, но абсолютно не знаком с базами данных. Первые несколько постингов будут о базах данных вообще, поэтому прошу не ругать меня - это не офтопик, очень быстро мы доберёмся до Дельфи.
Автор ответа: Vit
Взято с Vingrad.ru
BDE Error 2B04 Too many open files
BDE Error 2B04 Too many open files
Open the tool 'BDE Administrator' and choose the right tab 'Configuration'. There open the node 'System' and click on subnode 'INIT'. Then update 'MaxFileHandles' on the right side (it is 48 by default).
Взято с
Delphi Knowledge BaseBDE: Несколько SQL-запросов одним махом
BDE: Несколько SQL-запросов одним махом
BDE такую возможность не поддерживает, но есть компонент в библиотеке RxLib - TSQLScript (соответственно и в JVCL - TJvSQLScript), поставляются они в исходниках, и переделать их на любой другой доступ проще-простого. Компонет делает парсинг SQL и выполняет несколько SQL запросов.
Автор:
ПетровичВзято из
BEEP для дельфи, который работает, как в TP
BEEP для дельфи, который работает, как в TP
Взято из FAQ:
BEEP , для дельфи , который работает, как в B.Pascal 7.0
Я применяю следующий код, однако он работает только под Win9x/me
(Под WinNT/2000/XP вы можете использовать Beep(Tone, Duration)
- задавать тон и продолжительность звучания).
procedure Sound(Freq : Word);
var B : Byte;
begin
if Freq > 18 then
begin
Freq := Word(1193181 div LongInt(Freq));
B := Byte(GetPort($61));
if (B and 3) = 0 then
begin
SetPort($61, Word(B or 3));
SetPort($43, $B6);
end;
SetPort($42, Freq);
SetPort($42, Freq shr 8);
end;
end;
procedure NoSound;
var Value: Word;
begin
Value := GetPort($61) and $FC;
SetPort($61, Value);
end;
procedure SetPort(address, Value:Word);
var bValue: byte;
begin
bValue := trunc(Value and 255);
asm
mov dx, address
mov al, bValue
out dx, al
end;
end;
function GetPort(address:word):word;
var bValue: byte;
begin
asm
mov dx, address
in al, dx
mov bValue, al
end;
GetPort := bValue;
end;
Взято с Vingrad.ru
Berkeley DB
Berkeley DB
Berkeley DB ? это встроенная система баз данных с открытым кодом, имеющая ряд преимуществ перед другими подобными программными продуктами. Она проста в обращении, поддерживает возможность одновременного доступа нескольких пользователей, реализует поддержку транзакций на промышленном уровне и восстановление баз данных после системных и дисковых сбоев. В статье описывается архитектура и технические особенности Berkeley DB, схема ее распространения и лицензирования.
Введение
Berkeley Database (Berkeley DB) ? встроенная система баз данных, которую можно использовать в приложениях, нуждающихся в высокопроизводительном механизме хранения и извлечения пар ключ-значение, поддерживающем одновременный доступ. Программный продукт распространяется в виде библиотеки, которая может быть подключена к коду приложения. Функции библиотеки доступны через ряд API-интерфейсов, в том числе для языков Си, C++, Java, Perl, Python и Tcl. Загрузить Berkeley DB можно с Web-сайта Sleepycat Software по адресу www.sleepycat.com.
Sleepycat распространяет Berkeley DB, как программный продукт с открытым кодом. Компания предусматривает лицензионные отчисления за определенные виды использования ПО, а также взимает плату за поддержку и услуги.
История
Berkeley DB зародилась, как новая реализация метода доступа hash, созданного взамен hsearch и различных вариантов dbm (а именно, dbm корпорации AT&T, ndbm от Berkeley и gdbm, разрабатывавшегося в рамках проекта GNU). Продукт был разработан и выпущен под названием Hash в 1990 году программистами Зельцер и Йигит [8].
В первой крупной редакции Berkeley DB, вышедшей в 1991 году, были внесены некоторые изменения в пользовательский интерфейс и добавлен новый метод доступа, B+tree. Примерно в то же время Зельтцер и Олсон разработали прототип транзакционной системы на основе Berkeley DB под названием LIBTP [9], код которой не публиковался.
В 1992 году Berkeley DB 1.85 была включена в состав операционной системы 4.4BSD UNIX. В начале 90-х Зельцер и Бостик сопровождали код в Беркли и Массачусетсе. В то время он получил широкое распространение среди пользователей.
К середине 1996 года появились пользователи, нуждающиеся в коммерческой поддержке продукта. Учтя их пожелания, Бостик и Зельтцер образовали компанию Sleepycat Software. Она совершенствует, распространяет и поддерживает Berkeley DB, а также сопутствующее программное обеспечение и документацию. В середине 1997 года Sleepycat выпустила Berkeley DB 2.1, содержащую ряд важных новшеств, в том числе возможность многопользовательского доступа к базам данных. Компания выпускает около трех коммерческих версий продукта в год. Последняя выпущенная ею версия Berkeley DB имеет номер 3.1.
Краткий обзор Berkeley DB
Си-интерфейсы, входящие в комплект поставки Berkeley DB, позволяют реализовать управление записями баз данных в dbm-стиле и предоставляют доступ к множеству расширений, в числе которых механизм элегантного решения задачи обработки идентичных записей, а также системы поддержки многопользовательского доступа и транзакций. Последнее расширение позволяет параллельно завершать по нескольку транзакций (с перманентной модификацией данных) или откатывать их (с восстановлением баз данных до состояния перед началом транзакции).
Интерфейсы для C++ и Java реализуют небольшой набор классов для работы с базами данных. Корневой класс в обоих языках носит название Db ? он содержит методы, инкапсулирующие упомянутые выше интерфейсы dbm-стиля.
Имеются также интерфейсы для работы с Berkeley DB при помощи языков сценариев Perl, Python и Tcl.
Berkeley DB можно подключать к коду приложения динамически или статически.
Использование Berkeley DB
Библиотека Berkeley DB поддерживает возможность многопользовательского доступа к базам данных. Ее можно подключить к коду автономных приложений, к набору совместно работающих приложений или к серверам, обрабатывающим клиентские запросы и выполняющим в соответствии с ними операции над базами данных.
На наш взгляд, Berkeley DB проще в понимании и обращении, чем автономные СУБД. Она хранит и извлекает записи, состоящие из пар ключ-значение. Ключи используются для обнаружения элементов и могут представлять собой данные любого типа или любые структуры, поддерживаемые применяемым языком программирования.
Программист может указать Berkeley DB использовать написанные им самим функции выполнения операций над ключами. Например, метод доступа B+tree может использовать произвольную функцию сравнения, а Hash ? произвольную хэш-функцию. Если пользовательские функции не определены, Berkeley DB использует собственные. В противном случае система вообще не просматривает и не интерпретирует ключи и значения. Значения могут быть произвольной длины.
Важно понимать, чем Berkeley DB не является. Berkeley DB ? не сервер баз данных, обрабатывающий запросы, поступающие по сети. Это также не SQL-ядро, выполняющее запросы. Не является Berkeley DB и реляционной или объектно-ориентированной СУБД.
Все перечисленные системы можно построить поверх Berkeley DB, которая сама по себе является всего лишь встраиваемым механизмом баз данных. Разработчики постарались сделать его переносимым, компактным, быстрым и надежным.
Приложения, использующие Berkeley DB
Система Berkeley DB встроена в ряд продуктов, как с открытым кодом, так и коммерческих. В данном разделе перечислены некоторые из них.
Серверы каталогов, хранящие и извлекающие данные с применением протокола каталогов LDAP, а также реализующие службу именования и поиска по каталогам в локальных сетях. Основа этой службы ? опрос и обновление базы данных, но с применением простого протокола, а не SQL или ODBC. Berkeley DB выполняет функции встроенного диспетчера данных в большинстве существующих серверов каталогов, включая LDAP-серверы компаний Netscape, MessagingDirect и других.
Berkeley DB встроена в большое число почтовых серверов. Intermail компании Software.com использует Berkeley DB в качестве хранилища сообщений и в качестве основного хранилища для своего сервера каталогов. Сервер sendmail (как коммерческий Sendmail Pro компании Sendmail, так и версия, распространяемая через sendmail.org) использует Berkeley DB для хранения псевдонимов и другой информации. Postfix (ранее носивший название VMailer) также использует Berkeley DB для хранения административной информации.
Кроме того, Berkeley DB встроен во множество других программных продуктов. Система применяется для управления списками контроля доступа; хранения пользовательских ключей в инфраструктурах, использующих шифрование с открытым ключом; записи таблиц соответствий машинных и сетевых адресов в адресных серверах; хранения конфигурации и информации об устройствах в программах видеомонтажа.
Berkeley DB также входит в состав многих программ с открытым кодом, распространяемых по Internet. В частности, система встроена в Web-сервер Apache и в графическую оболочку Gnome. Все коммерческие дистрибутивы Linux, а также многие разновидности BSD содержат Berkeley DB.
Методы доступа
В терминологии баз данных метод доступа ? это совокупность размещаемой на диске структуры, используемой для хранения данных и операций над этой структурой. Например, многие системы баз данных поддерживают метод доступа B+tree. Он позволяет производить поиск по точному соответствию («найти ключи, равные некоторой константе»), поиск интервала («найти ключи, попадающие в интервал между двумя константами»), а также вставку и удаление записи.
Berkeley DB поддерживает четыре метода доступа: B+tree, Persistent Queues (Queue), Extended Linear Hashing (Hash) и Fixed- or Variable-length Records (Recno). В методах B+tree и Hash ключи могут иметь произвольную структуру. В методах доступа Queue и Recno каждой записи присваивается номер, который и служит ключом. Во всех методах доступа значение может иметь произвольную структуру. Если программист подставляет собственные функции сравнения или хэширования, Berkeley DB хранит и извлекает значения, не интерпретируя их.
В качестве основного хранилища все методы доступа используют файловую систему ОС.
Hash
Berkeley DB поддерживает метод доступа Hash, реализующий расширяемую линейную хэш-таблицу [4]. Особенность такого хэширования в том, что хэш-функция адаптируется по мере роста таблицы, и все хэш-корзины в устойчивом состоянии по мере возможности остаются заполненными не до конца.
Метод доступа Hash поддерживает возможность вставки и удаления записей, а также поиска, но лишь по точному соответствию. Допускается повторение всех записей, хранимых в таблице, но порядок их возврата не определен.
B+tree
Berkeley DB поддерживает метод доступа B+tree [1]. B+деревья хранят пары ключ-значение в листовых страницах, а пары «ключ, адрес дочерней страницы» ? на внутренних узлах. Ключи в дереве хранятся в отсортированном порядке, заданном функцией сравнения, указанной при создании базы данных. Для упрощения задачи обхода дерева страницы на листовом уровне содержат указатели на соседей. B+tree поддерживает поиск по точному соответствию (равенству) или интервалу (допустимы лишь интервалы «больше или равно»). Как и хэш-таблицы, B+деревья поддерживают вставку, удаление и повторение всех записей в дереве.
По мере заполнения записями страниц они разбиваются ? примерно половина ключей уходит на новую страницу, находящуюся на том же уровне дерева. Большинство реализаций В+дерева после деления оставляют оба узла заполненными наполовину. В общем случае это приводит к снижению производительности, когда вызывающий вставляет ключи по порядку. Но Berkeley DB запоминает порядок вставки и разбивает страницы не поровну, а таким образом, чтобы они заполнялись более, чем наполовину. Благодаря этому уменьшается размер дерева и общий объем базы данных, повышается производительность поиска.
По удалении пустые страницы воссоединяются посредством обратного разбиения. Данный метод доступа не принимает никаких дополнительных мер для балансировки страниц при удалении или вставке. Ключи при каждом обновлении не перемещаются от страницы к странице. Реализация такой возможности могла бы в отдельных случаях снизить время поиска, но дополнительная сложность кода привела бы к замедлению обновлений и частым взаимным блокированиям.
Queue
Berkeley DB реализует метод доступа, управляющий высокопроизводительными долговременными очередями. Он построен на базе метода доступа B+tree, но использует механизмы блокировки и протоколирования, необходимые для работы в условиях интенсивного многопользовательского доступа к голове и хвосту очереди. Процесс обновления головы или хвоста очереди блокирует другие потоки на протяжении не всей транзакции, а лишь на время своего выполнения. Благодаря этому качество поддержки параллельного выполнения транзакций значительно улучшается.
Recno
Berkeley DB содержит метод доступа к записям фиксированной или переменной длины под названием Recno. Он присваивает каждой из записей логический номер, по которому может осуществляться поиск и обновление. Recno может, например, загрузить в базу данных текстовый файл, рассматривая каждую строку в качестве записи. Используя эту возможность, в текстовых редакторах можно производить быстрый поиск по номеру строки [10].
Recno построен на базе метода доступа В+tree и предоставляет простой интерфейс для хранения значений, упорядоченных по номеру. Recno генерирует номера записей самостоятельно. С точки зрения программиста значения нумеруются последовательно, начиная с единицы. Разработчик может указать системе, должны ли записи автоматически перенумеровываться после добавления или удаления записей с меньшими номерами. Если да, то новые ключи можно будет вставлять между существующими.
Особенности
В данном разделе описываются важнейшие особенности Berkeley DB. Разработчик имеет возможность использовать в своем приложении только необходимые ему функции системы.
Например, когда приложение открывает базу данных, оно может назначить требуемый ему уровень поддержки многопользовательского доступа и восстановления. Простые автономные приложения, в частности перенесенные приложения, использовавшие dbm или какую-то из его разновидностей, обычно не требуют поддержки многопользовательского доступа и восстановления после сбоев. Однако, например СУБД корпоративного класса, хранящие данные по операциям службы сбыта и другую критически важную информацию, нуждаются в полноценной поддержке транзакций. В однопользовательском режиме система работает быстрее, чем в многопользовательском, ввиду отсутствия задержек, создаваемых блокировками. С отключенной системой восстановления Berkeley DB также работает быстрее, поскольку в этом случае при внесении изменений в базах данных их не нужно протоколировать.
Некоторые подсистемы уровня ядра, в частности механизмы блокировки и протоколирования могут использоваться и вне контекста методов доступа. Существует, нечасто используемая, возможность изолированного применения в приложении менеджера блокировки Berkeley DB, без каких-либо функций работы с базами данных. Кроме того, с помощью системы двухэтапной блокировки Berkeley DB вызывающий может реализовать блокирование не принадлежащих к базе данных ресурсов, подчиняя их таким образом транзакционной семантике.
Интерфейсы программирования
Berkeley DB предоставляет простой API-интерфейс для управления базами данных. В комплект поставки программного обеспечения не входят стандартные интерфейсы типа ODBC, OLEDB или SQL. Эти интерфейсы весьма удобны, однако они создавались с целью обеспечения интероперабельности систем баз данных, но не простоты или высокой производительности.
Идя навстречу пожеланиям пользователей, создатели Berkeley DB в версии 2.5 реализовали поддержку стандарта XA [5]. XA позволяет Berkeley DB принимать участие в распределенных транзакциях под управлением монитора обработки транзакций, подобного Tuxedo компании BEA Systems. Подобно XA, поверх ядра системы можно построить и другие стандартные интерфейсы. Стандарты не являются частью внутреннего механизма Berkeley DB, так как в их поддержке нуждаются не все приложения.
Работа с записями
Пользователю базы данных может понадобиться произвести поиск определенных ключей или просто просмотреть имеющиеся записи. Berkeley DB поддерживает как доступ по ключам, т. е. возможность найти все записи с заданным ключом, так и последовательный доступ ? возможность извлечь сразу все записи базы данных. Порядок, в котором возвращаются записи, зависит от метода доступа. Базы данных в форматах В+tree и Recno возвращают записи в порядке сортировки, а Hash-базы ? в случайном порядке.
В Berkeley DB предусмотрены также простые интерфейсы для вставки, обновления и удаления записей в базе данных.
Длинные ключи и значения
Berkeley DB может работать с ключами и значениями размером до 232 байт. Поскольку время, затрачиваемое на копирование записи, возрастает пропорционально ее размеру, в Berkeley DB предусмотрены интерфейсы для работы с фрагментами записей. Если приложению нужна лишь часть большой записи, оно может запросить извлечение лишь требуемого ему фрагмента. Это позволяет экономить и время, и память.
Berkeley DB позволяет программисту задать тип данных как для ключей, так и для значений. Разработчик может использовать любые типы данных, поддерживаемых применяемым языком программирования.
Крупные базы данных
Максимальный размер базы данных, поддерживаемый Berkeley DB, составляет 248 байт или 256 Тбайт. Поскольку Berkeley DB использует в качестве основного хранилища базы данных файловую систему ОС, она должна поддерживать работу с файлами больших размеров. У Sleepycat Software есть клиенты, пользующиеся Berkeley DB для управления базами данных, размер которых превышает 1 Тбайт.
Базы данных, размещаемые в основной памяти
Приложения, не требующие долговременного хранения информации, могут создавать базы данных, размещаемые лишь в основной памяти. Таким базам данных в принципе не свойственны ограничения по производительности, налагаемые подсистемой ввода/вывода.
Существуют приложения, нуждающиеся в дисковом пространстве для основного хранилища, но работающие на системах с очень большим объемом памяти. Berkeley DB может выполнять роль диспетчера очень больших участков памяти, совместно используемой для управления блокированием, хранения кэшированных страниц данных и записей протокола. Например, участок, используемый для кэширования страниц данных, может иметь размеры в несколько гигабайт ? таким образом уменьшается вероятность того, что при операции чтения в устойчивом состоянии потребуется доступ к диску. Размер кэш-региона декларируется при старте программы.
Кроме того, многие операционные системы поддерживают файловые функции с отображением памяти ? они работают намного быстрее, чем стандартные операции. Berkeley DB может отображать файлы своих баз данных в памяти, когда требуется их использование в режиме «только чтение». В этом случае приложение работает с записями, хранимыми непосредственно на страницах памяти, не расходуя ресурсы на кэширование. Поскольку приложение получает указатели непосредственно на страницы Berkeley DB, выполнять операции записи в таком режиме нельзя. В противном случае изменения могут миновать системы блокирования и протоколирования, и приложение может повредить структуру базы данных. Приложения, работающие в режиме «только чтение», могут использовать поддерживаемые Berkeley DB файловые операции с отображением в память на большинстве архитектур.
Определяемый размер страниц
Программист декларирует размер страниц, используемых применяемым методом доступа, при создании базы данных. Хотя в Berkeley DB предусмотрены разумные значения по умолчанию, разработчик может модифицировать их с целью оптимизации производительности системы. При небольшом размере страниц снижается число записей на страницу. Соответственно, при блокировании такой страницы блокируется меньше записей и улучшается качество многопользовательского доступа. Конечно, затраты ресурсов на страницу в обратной пропорции к их размеру, но зато разработчик, если того требует приложение, имеет возможность увеличить производительность.
Компактность
Berkeley DB ? компактная система. Двоичные файлы полного комплекта, в том числе всех методов доступа, механизмов восстановления и транзакций, занимают на стандартных архитектурах около 200 Кбайт.
Курсоры
В терминологии СУБД курсор ? это указатель на метод доступа, который циклически вызывается для последовательного возвращения записей. В Berkeley DB имеются курсорные интерфейсы для всех методов доступа. Применение курсоров позволяет, например, обойти В+дерево, просматривая записи в порядке обхода. Указатели на записи в курсорах являются долговременными, так что будучи захваченной один раз, запись может быть впоследствии обновлена на прежнем месте. Кроме того, курсоры позволяют работать с цепочками повторяющихся данных в различных методах доступа.
Соединения
В терминологии СУБД соединение это операция, объединяющая несколько таблиц (или в случае с Berkeley DB, несколько файлов баз данных) в одну. Приведем пример: компания хранит сведения о своих клиентах в одной таблице, а информацию о сбыте ? в другой. Допустим, приложению понадобилось просмотреть данные о сбыте по именам клиентов; это потребует приведения в соответствие записей в двух таблицах, имеющих общее поле «имя покупателя». Такое объединение записей из двух таблиц называется соединением.
Berkeley DB содержит интерфейсы для соединения двух или более таблиц.
Транзакции
Транзакция обладает четырьмя свойствами [2].
- Атомарность. Транзакция должна быть выполнена полностью, или не выполнена вообще. Это свойство позволяет, например, единой атомарной операцией осуществить денежный перевод между двумя счетами, уменьшив баланс на одном и увеличив на другом.
- Согласованность. Изменения, вносимые транзакцией в базу данных, не должны повредить ее.
- Изолированность. Независимо от числа пользователей, одновременно работающих с базой данных, у каждого из них должна быть иллюзия, что никаких параллельных операций над базой данных не выполняется.
- Долговечность. Даже если утрачен диск, на котором хранилась база данных, должна быть возможность восстановить ее до состояния после выполнения последней согласованной транзакции.
Сочетание свойств атомарности, согласованности, изолированности и долговечности обычно обозначают сокращением ACID (atomicity, consistency, isolation, durability). Berkeley DB, как и большинство систем баз данных, обеспечивает ACID за счет набора базовых служб.
Программисты могут пользоваться поддержкой транзакций Berkeley DB при необходимости.
Протоколирование с упреждением
При запуске Berkeley DB программист может активизировать систему протоколирования. В процессе транзакции приложение вносит ряд изменений в базу данных. Каждое изменение фиксируется в отдельной записи протокола, предназначенного для хранения состояния записей базы данных до и после внесения изменений. Berkeley DB гарантирует, что запись протокола будет сохранена на устойчивую систему хранения до выполнения операции записи измененных страниц с данными. Такое поведение ? запись протокола до записи страниц данных ? называется протоколированием с упреждением.
В любой точке процесса выполнения транзакции приложение может подтвердить ее результаты (commit), сделав изменения перманентными, или откатить (roll back), отменив все изменения и восстановив базу данных до состояния перед началом транзакции. Если приложение откатывает транзакцию, протокол будет содержать состояние всех измененных страниц до начала транзакции, и Berkeley DB просто восстановит базу данных до этого состояния. Если приложение завершает транзакцию, Berkeley DB сохраняет записи протокола на диск. Копии страниц данных, находящиеся в памяти, уже содержат внесенные изменения и будут сброшены на диск установленным порядком в ходе нормальной обработки. Тем самым повышается производительность, поскольку протокол ведется последовательно, а запись в страницы данных происходит в случайной последовательности.
Сбои и восстановление
Для завершения и отката транзакций в Berkeley DB используется система протоколирования с упреждением. Она же предоставляет системе восстановления информацию, необходимую для защиты от потери или повреждения данных в случае сбоя. Berkeley DB может восстановить данные после сбоев приложения, системных сбоев и даже катастрофических сбоев вроде утраты жесткого диска.
Восстановление после сбоев требует хранения данных в нескольких различных местах. В ходе нормальной обработки Berkeley DB содержит в памяти копии активных записей протокола и недавно использовавшихся страниц данных. Записи протокола сбрасываются на диск протокола после завершения транзакции. Страницы данных копируются на диск данных по мере своего прохождения через буферный кэш. Периодически системный администратор создает в безопасном месте резервные копии диска данных. После создания резервной копии базы данных протокол можно урезать. Для достижения максимальной отказоустойчивости диск протокола и диск данных должны находиться на разных физических устройствах.
Различные системные сбои могут привести к повреждению содержимого памяти, диска протокола или диска данных. Berkeley DB способна восстанавливаться после утраты любого из этих хранилищ, не теряя ни одной из завершенных транзакций.
В случае потери содержимого компьютерной памяти в результате сбоя системы или приложения все завершенные транзакции сохраняются в протоколе. При перезапуске система восстановления сверяет базу данных с протоколом, внося все изменения в дисковые страницы, находящиеся в памяти в момент сбоя. Поскольку протокол содержит состояния записей до и после внесения изменений, система также использует его для восстановления всех страниц, измененных незавершенными транзакциями.
В случае утраты диска данных системный администратор может восстановить его содержимое по последней резервной копии. Система восстановления сверит базу данных по протоколу, внося все зафиксированные в нем изменения. По окончании этого процесса база данных будет содержать все изменения, внесенные всеми завершенными когда-либо транзакциями.
В случае утраты диска с протоколом система восстановления может использовать находящиеся в памяти копии записей протокола для отката всех незавершенных транзакций, сброса находящихся в памяти страниц на диск данных и корректного закрытия приложения. После этого системный администратор может сделать резервную копию диска с базой данных, установить новый диск для протокола и перезапустить систему.
Контрольные точки
В Berkeley DB имеется служба контрольных точек, взаимодействующая с системой восстановления. В ходе нормальной обработки и протокол, и база данных непрерывно меняются. В любой заданный момент дисковые версии того и другого могут оказаться не соответствующими друг другу. Протокол может содержать изменения, еще не внесенные в базу данных.
С применением контрольных точек все изменения, зафиксированные в протоколе, гарантированно будут внесены и в дисковые данные. Использование контрольных точек в ходе нормальной обработки отнимает некоторое количество ресурсов, но зато снижает временные затраты на восстановление после сбоев.
После сбоя приложения или ОС, системе восстановления необходимо просмотреть протокол не с самого начала, а лишь за две контрольных точки до последней записи. (Не за одну, потому что нет гарантии, что последняя контрольная точка была корректно выставлена ? возможно, операция была прервана сбоем, в результате которого еще до ее окончания запустилась система восстановления). Без контрольных точек до перезапуска системы после сбоя может пройти неопределенно много времени. При наличии контрольных точек интервал до перезапуска может быть задан программистом. Процедура восстановления гарантированно будет завершаться за секунду-две.
Сбои программного обеспечения происходят гораздо чаще, чем дисковые сбои. Разработчики стараются обеспечить гарантию того, что ошибки в программах не разрушат данных, но на случай такого редкостного явления, как сбой диска, обычно предусматривают возможность восстановления резервной копии с ленты, в результате которого все же теряется один-два дня работы. Berkeley DB дает возможность обрезать протокол по контрольным точкам. В случае наличия двух последних контрольных точек система восстановления гарантирует, что после сбоя программного обеспечения не будет потеряно ни одной завершенной транзакции. При использовании контрольных точек система восстановления не требует размещения протокола и данных на раздельных дисках, хотя их разнесение все же позволяет поднять производительность за счет параллельной записи.
Двухфазная блокировка
В Berkeley DB имеется служба двухфазной блокировки. В целях снижения вероятности взаимного блокирования и обеспечения свойств ACID системы баз данных осуществляют блокировку в два этапа. В ходе выполнения транзакции блоки только ставятся, но не снимаются. В конце транзакции блоки только снимаются, но не ставятся. На практике большинство систем баз данных, включая Berkeley DB, ставят блоки по требованию в ходе транзакции, затем сбрасывают протокол на диск, затем снимают все блоки.
Berkeley DB может блокировать файлы баз данных (соответствующие таблицам) целиком, или же отдельные страницы внутри их. Блокировку на уровне записей Berkeley DB не осуществляет. Однако, уменьшив размер страницы, разработчик может добиться того, что на каждой из них будет храниться лишь небольшое число записей. Тем самым снижается уровень конфликтов.
При включенной функции блокирования операции чтения и записи базы данных устанавливают двухфазные блоки, не снимаемые до завершения транзакции. То, какие объекты и в каком порядке блокируются, зависит от рабочей нагрузки транзакции. Возможно взаимное блокирование двух и более транзакций, когда каждая из них ждет освобождения объекта, заблокированного параллельно выполняемой транзакцией.
Berkeley DB распознает взаимные блокирования и автоматически откатывает одну из транзакций. Этим снимаются установленные ею блоки, и остальные транзакции продолжают свое выполнение. Вызывающий получает извещение о том, что его транзакция не завершена, и может запустить ее снова. Разработчик может задать интервал запуска процедуры проверки наличия взаимного блокирования и политику выбора транзакции для отката.
Из приложений, к которым подключается Berkeley DB, интерфейсы двухфазной блокировки можно вызывать независимо от других, хотя до сих пор лишь немногим пользователям понадобилось задействовать эту возможность изолированно. Эти интерфейсы открывают доступ к быстрой, переносимой системе блокировки общего назначения. Berkeley DB также позволяет включать внешние по отношению к базе данных объекты в транзакции, контролируя доступ к этим объектам точно так же, как если бы они принадлежали к базе данных.
Механизм двухфазной блокировки построен на самых быстрых примитивах корректного блокирования, поддерживаемых архитектурой нижнего уровня. Это означает, что для каждой из Unix-платформ система блокировки реализована в последней версии Berkeley DB по-разному, не говоря уже о реализации для Windows NT. Как показывает наш опыт, самыми сложными этапами оптимизации производительности является поиск наиболее быстрых примитивов блокирования, корректно работающих на конкретной архитектуре, и интеграция нового интерфейса с уже существующими.
Идеальным был бы случай, если бы во всех операционных систем были реализованы блокирующие примитивы POSIX, и если бы существовала гарантия, что наиболее быстрой операцией является установка блока, на который не претендует другая операция. Блокировки должны работать как между потоками внутри одного процесса, так и между процессами.
Параллелизм
Хорошая производительность при параллельных операциях ? одна из важнейших целей, которые ставили разработчики при создании Berkeley DB. Хотя сама по себе Berkeley DB не является многопоточной, она корректно работает в приложениях, использующих потоки. Мы считаем, что решение об использовании потоков и выборе механизма их реализации должны приниматься разработчиком, и поэтому предпочли реализовать механизм (возможность потокового или обычного выполнения), позволяющий приложению поступать сообразно своим потребностям.
Подсистемы блокировки, протоколирования и управления буферным пулом для обмена информацией используют совместный доступ к памяти или другие имеющиеся в от ОС функции совместного доступа. Блокировки, захваты буферных пулов и операции записи в протокол одинаково ведут себя, работая в потоках, в едином процессе и в разных процессах, выполняющихся на одной машине.
Благодаря этому многопользовательские БД-приложения могут запускать отдельный процесс для каждого нового пользователя, создать единый сервер, порождающий отдельный поток для каждого клиентского запроса или воспользоваться любой политикой, комбинирующей эти подходы.
Berkeley DB была тщательно спроектирована таким образом, чтобы минимизировать конфликты и обеспечить максимальный параллелизм. Диспетчер кэша позволяет всем потокам или процессам пользоваться операциями ввода-вывода, выполняемыми одним из них. Совместно используемые ресурсы иногда необходимо блокировать для предоставления эксклюзивного доступа одному управляющему потоку. Мы позаботились о том, чтобы важнейшие функции Berkeley DB были небольшими по объему и чтобы в ходе системных вызовов блоки критических ресурсов не удерживались слишком долго, чтобы создать препятствие для блокирующего потока или процесса. У Sleepycat Software есть клиенты, у которых с одной базой данных параллельно работают сотни пользователей.
Философия проектирования
Berkeley DB по сути представляет собой набор методов доступа и служб, таких как протоколирование, блокировка и использующий их транзакционный доступ. И в научном, и в коммерческом сообществе методы построения систем, подобных Berkeley DB, уже давно хорошо изучены.
Свои преимущества Berkeley DB получила благодаря тому пристальному вниманию, которое уделялось деталям проектирования на протяжении всего существования программного продукта. Мы позаботились о том, чтобы ключевые функции, такие как блокировка и ввод/вывод либо были доступными через удобные интерфейсы, либо работали прозрачно для вызывающего. Будучи программистами, мы понимаем ценность простоты и много работали над упрощением интерфейсов, через которые пользователь обращается к системе баз данных.
Berkeley DB старается не налагать ограничения на код использующего ее приложения. Система не налагает практических ограничений на размер ключей, значений или баз данных; они могут расти, заполняя все имеющееся пространство хранения.
При разработке подсистем блокировки и протоколирования одни критические участки мы делали как можно более компактными, от других отказывались; выполнялась также работа над сжатием размеров блокированных регионов и записей протокола. Все это делалось с целью снижения уровня конфликтов и улучшения пропускной способности.
В архитектуре и реализации Berkeley DB нет никаких новшеств. Проектируя систему, мы старались как можно лучше реализовать традиционные подходы. В результате получилась встроенная система баз данных, превосходящая сходные решения по качеству.
Разработчики большинства систем баз данных приносят простоту в жертву полноте реализации. Система либо проста в использовании, либо она поддерживает параллельный доступ и возможность восстановления после сбоев. Berkeley DB благодаря тщательному проектированию обладает и простотой, и полнотой реализации.
Система немного занимает в памяти, обеспечивает простоту выполнения несложных операций (вставка новой записи занимает всего лишь несколько строк кода), и корректно ведет себя в условиях интенсивного многопользовательского доступа, системных сбоев и даже катастрофических потерь вроде утраты жесткого диска.
Распространение Berkeley DB 3.x
Berkeley DB распространяется в исходных текстах. Пользователи имеют право без ограничений загружать и компилировать наше ПО, а также использовать его в своих приложениях.
Состав дистрибутива
Дистрибутив представляет собой заархивированный файл. Он содержит исходный текст библиотеки Berkeley DB, документацию, тестовые комплекты и утилиты сопровождения.
Исходный текст содержит директивы компиляции для всех поддерживаемых платформ. В Unix для идентификации разновидности ОС, сборки библиотеки и утилит Berkeley DB использует распространяемый на условиях лицензии GNU инструмент автоконфигурирования autoconf. Berkeley DB содержит директивы сборки и для других платформ, в том числе для ОС реального времени VxWorks, а также Windows 95/98/NT/2000.
Документация
Документация поставляется в формате HTML. Она состоит из двух частей ? руководство в традиционном для Unix стиле для программистов и справочное руководство, составленное по принципу учебника.
Тестовый комплект
В дистрибутив входит полноценный текстовый комплект, написанный на Tcl. На наш взгляд, тестовый комплект ? главное преимущество Berkeley DB перед подобными ей системами.
Во-первых, он позволяет убедиться, что программа нормально работает после сборки.
Во-вторых, тестовый комплект дает нам возможность скрупулезно проверять каждую очередную версию системы. Узнавая о новых ошибках, мы воспроизводим их на тестовом комплекте. Мы постоянно работаем с тестовым комплектом в ходе разработки, всегда используя его до выпуска. В результате уже на этапе бета-версии система становится весьма стабильной.
Двоичный дистрибутив
Sleepycat предоставляет заказчикам скомпилированные библиотеки и полноценные двоичные дистрибутивы за деньги.
Поддерживаемые платформы
Berkeley DB работает на любой операционной системе с интерфейсом POSIX 1003.1 [3], который имеется практически во всех разновидностях Linux и Unix. Кроме того, программный продукт работает в среде ОС реального времени VxWorks компании Wind River Systems, а также на VMS, Windows 95, Windows 98, Windows NT и Windows 2000.
Лицензирование Berkeley DB 3.x
Berkeley DB распространяется как продукт с открытым кодом. Его можно бесплатно получить, загрузив его с нашего Web-сайта или иными способами. Пользователи имеют возможность без ограничений загружать и включать его в состав своих приложений.
Berkeley DB версий 1.х сопровождалась лицензией UC Berkeley, которая позволяла свободно распространять наше ПО в исходных текстах. После создания Sleepy Software нам потребовалось было разработать новую лицензию, согласующуюся с предыдущей. Ввиду серьезных отличий UC Berkeley от GPL мы не могли просто использовать последнюю. Создание новой лицензии, условия которой противоречили бы первой, означал бы отказ от прежних принципов.
По ряду причин Sleepycat намеревалась продолжать разработку Berkeley DB по принципу открытого кода. Мы согласны с Эриком Рэймондом [7] и другими в том, что ПО с открытым кодом, как правило, отличается от собственнических продуктов, распространяемых только в двоичном виде, более высоким качеством. Наши клиенты пользуются услугами сообщества разработчиков, хорошо знающих Berkeley DB, и способных помочь в проектировании, отладке приложения и оптимизации его производительности. Благодаря широкому распространению исходного текста ошибки обычно выявляются уже на ранних этапах, и исправления вносятся весьма оперативно. Таким образом обеспечивается высокая надежность Berkeley DB. Открытый, бесплатно распространяемый код дает и преимущества с точки зрения бизнеса. Мы имеем и прибыль, и рынок сбыта для своих продуктов и услуг. Кроме того, доступность исходных текстов сокращает нагрузку на нашу службу поддержки, поскольку во многих случаях клиенты могут самостоятельно находить и исправлять ошибки, не обращаясь к нам.
Новая лицензия, контролирующая Berkeley DB 2.x и 3.х, сохраняет принципы распространения кода предыдущих версий системы. Мы позаимствовали из GPL условия, делающие невозможным превращение открытого кода в собственнический.
Вкратце условия использования и распространения Berkeley DB таковы:
приложение с Berkeley DB должно быть предназначено только для внутреннего применения, или
должно быть свободно распространяемым в исходных текстах, или
пользователь должен получить у нас лицензию.
Клиентам, не желающим распространять свои продукты в виде открытого кода, мы предлагаем по разумным ценам лицензии на использование, распространение и расширение Berkeley DB.
Мы стараемся прислушиваться к нуждам сообщества открытого кода. В частности, мы разработали специальные лицензионные соглашения с проектом Gnome, поощряющие использование и распространение в его составе Berkeley DB.
Berkeley DB соответствует определению открытого кода [6]. Лицензия была тщательно составлена таким образом, чтобы Berkeley DB с одной стороны оставалась продуктом с открытым кодом, а с другой обеспечивала прибыль на инвестицию, достаточную для финансирования дальнейшей разработки и поддержки продукта. Ныне действующее лицензионное соглашение уже в течение трех лет дает нам возможность поддерживать разработку Berkeley DB. Если бы не эта лицензия, такого программного продукта могло и не быть.
Резюме
Berkeley DB реализует уникальный набор возможностей, адресованных разработчикам, приложениям которых необходимы простые и надежные службы управления базами данных. Благодаря своей совершенной архитектуре и реализации, а также благодаря тщательному проектированию продукт стал лучшим в своем классе.
Berkeley DB ? программный продукт с открытым кодом. В дистрибутив входит все необходимое для сборки и развертывания или переноса его на новые операционные системы.
Sleepycat Software распространяет Berkeley DB на условиях лицензионного соглашения, базирующегося на Berkeley UC и GPL. Лицензия гарантирует, что Berkeley DB останется продуктом открытого кода и обеспечивает Sleepycat возможность зарабатывать средства на финансирование дальнейшего развития своего программного обеспечения.
Библиотека для работы с LAN
Библиотека для работы с LAN
unit NetProcs;
interface
uses Classes, Windows;
type
TAdapterStatus = record
adapter_address: array [0..5] of Char;
filler: array [1..4*SizeOf(Char)+19*SizeOf(Word)
+3*SizeOf(DWORD)] of Byte;
end;
THostInfo = record
username: PWideChar;
logon_domain: PWideChar;
oth_domains: PWideChar;
logon_server: PWideChar;
end;{record}
function IsNetConnect : Boolean;
{Возвращает TRUE если компьютер подключен к сети, иначе - FALSE}
function AdapterToString(Adapter: TAdapterStatus): string;
{Преобразует MAC адес в привычный xx-xx-xx-xx}
function GetMacAddresses(const Machine: string;
const Addresses: TStrings): Integer;
{Заполняет Addresses MAC-адресами компьютера с сетевым именем Machine.
Возвращает число МАС адресов на компьютере}
function GetNetUser(HostName: WideString): THostInfo;
{Возвращает LOGIN текущего пользователя на HOSTNAME компьютере}
implementation
uses NB30, SysUtils;
function IsNetConnect : Boolean;
begin
if GetSystemMetrics(SM_NETWORK) AND $01 = $01
then Result:= True
else Result:= False;
end;{function}
function AdapterToString(Adapter: TAdapterStatus): string;
begin
with Adapter do Result :=
Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x', [
Integer(adapter_address[0]), Integer(adapter_address[1]),
Integer(adapter_address[2]), Integer(adapter_address[3]),
Integer(adapter_address[4]), Integer(adapter_address[5])]);
end;{function}
function GetMacAddresses(const Machine: string;
const Addresses: TStrings): Integer;
const NCBNAMSZ = 16; // absolute length of a net name
MAX_LANA = 254; // lana's in range 0 to MAX_LANA inclusive
NRC_GOODRET = $00; // good return
NCBASTAT = $33; // NCB ADAPTER STATUS
NCBRESET = $32; // NCB RESET
NCBENUM = $37; // NCB ENUMERATE LANA NUMBERS
type
PNCB = ^TNCB;
TNCBPostProc = procedure (P: PNCB); stdcall;
TNCB = record
ncb_command: Byte;
ncb_retcode: Byte;
ncb_lsn: Byte;
ncb_num: Byte;
ncb_buffer: PChar;
ncb_length: Word;
ncb_callname: array [0..NCBNAMSZ - 1] of Char;
ncb_name: array [0..NCBNAMSZ - 1] of Char;
ncb_rto: Byte;
ncb_sto: Byte;
ncb_post: TNCBPostProc;
ncb_lana_num: Byte;
ncb_cmd_cplt: Byte;
ncb_reserve: array [0..9] of Char;
ncb_event: THandle;
end;
PLanaEnum = ^TLanaEnum;
TLanaEnum = record
length: Byte;
lana: array [0..MAX_LANA] of Byte;
end;
ASTAT = record
adapt: TAdapterStatus;
namebuf: array [0..29] of TNameBuffer;
end;
var
NCB: TNCB;
Enum: TLanaEnum;
I: Integer;
Adapter: ASTAT;
MachineName: string;
begin
Result := -1;
Addresses.Clear;
MachineName := UpperCase(Machine);
if MachineName = ''
then MachineName := '*';
FillChar(NCB, SizeOf(NCB), #0);
NCB.ncb_command := NCBENUM;
NCB.ncb_buffer := Pointer(@Enum);
NCB.ncb_length := SizeOf(Enum);
if Word(NetBios(@NCB)) = NRC_GOODRET
then begin
Result := Enum.Length;
for I := 0 to Ord(Enum.Length) - 1
do begin
FillChar(NCB, SizeOf(TNCB), #0);
NCB.ncb_command := NCBRESET;
NCB.ncb_lana_num := Enum.lana[I];
if Word(NetBios(@NCB)) = NRC_GOODRET
then begin
FillChar(NCB, SizeOf(TNCB), #0);
NCB.ncb_command := NCBASTAT;
NCB.ncb_lana_num := Enum.lana[i];
StrLCopy(NCB.ncb_callname, PChar(MachineName),NCBNAMSZ);
StrPCopy(@NCB.ncb_callname[Length(MachineName)],
StringOfChar(' ', NCBNAMSZ - Length(MachineName)));
NCB.ncb_buffer := PChar(@Adapter);
NCB.ncb_length := SizeOf(Adapter);
if Word(NetBios(@NCB)) = NRC_GOODRET
then Addresses.Add(AdapterToString(Adapter.adapt));
end;
end;
end;
end;{function}
function
NetWkstaUserEnum(servername: PWideChar;
level : DWord;
var bufptr: Pointer;
prefmaxlen: DWord;
var entriesread: PDWord;
var totalentries: PDWord;
var resumehandle: PDWord ): LongInt ;
stdcall; external 'netapi32.dll' name 'NetWkstaUserEnum';
function GetNetUser(HostName: WideString): THostInfo;
var
Info: Pointer;
ElTotal: PDWord;
ElCount: PDWord;
Resume: PDWord;
Error: LongInt;
begin
Resume:=0;
NetWkstaUserEnum(PWideChar(HostName),1, Info,0,
ElCount,ElTotal,Resume);
Error:=NetWkstaUserEnum(PWideChar(HostName),1,Info,256*Integer(ElTotal),
ElCount,ElTotal,Resume);
case Error of
ERROR_ACCESS_DENIED: Result.UserName:= 'ERROR - ACCESS DENIED';
ERROR_MORE_DATA: Result.UserName:= 'ERROR - MORE DATA';
ERROR_INVALID_LEVEL: Result.UserName:= 'ERROR - INVALID LEVEL';
else if Info <> nil
then Result:=THostInfo(info^)
else begin
Result.username:= '???';
Result.logon_domain:= '???';
Result.oth_domains:= '???';
Result.logon_server:= '???';
end;{if}
end;{case}
end; {function}
end.
Автор Alex
Взято с сайта
Библиотека KOL
Библиотека KOL
KOL - кодоэкономичная объектная библиотека для Delphi
Кладов В.Л.
Цель данной статьи - убедить читателя (я надеюсь, этот текст попадет в руки программиста), привыкшего к большим размерам современных программ (о, нет, приложений, программы-то как раз были еще не очень большими) в том, что его бессовестно надувают. Когда утверждают, что программа для среды Windows, если она что-то полезное умеет делать, никак не может быть меньше... ну, скажем, трехсот килобайт. А если это очень "умная" программа, содержащая очень много полезных возможностей, хороший интерфейс, отлично взаимодействующая с пользователем, поддерживает различные форматы данных, современные клиент-серверные технологии, то без полсотни мегабайт ну никак не обойтись. Чушь несусветная. Нас обманывают!
На самом деле, объектное программирование позволяет создавать очень экономичный по размеру код. Причем, достаточно эффективный. Примеры? Пожалуйства. Ява - объектно-ориентированное программирование. Ява-апплеты очень невелики по своим размерам, а как много полезного они умеют делать! Впрочем, речь пойдет не о Яве. Предметом данного разговора будет среда Delphi.
Как ни странно, именно Delphi оказался тем инструментом, с помощью которого оказалось возможным изготовить библиотеку KOL - Key Objects Library (Ключевую Объектную Библиотеку). Странно потому, может быть, что программы, изготовленные средствами Delphi, обычно маленькими не бывают. Минимальный стартовый размер приложения, представляющего из себя одно пустое окно, которое можно подвигать по экрану и закрыть, и которое, собственно, ничего больше делать не умеет, составляет около трехсот килобайт. Причем, с выпуском каждой очередной версии Delphi этот стартовый размер вырастает еще на несколько десятков ни в чем неповинных килобайт.
Библиотека KOL позволяет изготавливать не менее мощные приложения, чем стандартная библиотека Delphi - VCL (Visual Component Library, Визуальная Библиотека Компонентов). И при этом добиваться уменьшения размеров программ в 5-15 раз! Например, приложение DirComp, доступное для загрузки на сайте KOL, занимает без сжатия упаковывающими программами около 65 килобайт. Аналогичное приложение, написанное за два года до этого с использованием стандартной библиотеки Delphi, занимало 750 килобайт. Разница впечатляет, не правда ли?
KOL - не только объектно-ориентированная, но и визуальная библиотека. Программы и их графический интерфейс возможно проектировать практически так же, как и в визуальной среде VCL. В дополнение к KOL идет библиотека MCK (Mirror Classes Kit, Библиотека Зеркальных Классов), которая содержит VCL-компоненты, устанавливающиеся на палитру обычным образом. Единственное отличие в том, что зеркальные компоненты библиотеки MCK существуют только на стадии разработки (design time), участвуя в генерации "настоящего" кода, совместимого с требованиями библиотеки KOL. Во время работы (run time) выполняется этот код, и тот, который был добавлен самим разработчиком. В коде времени исполнения нет ссылок на компоненты VCL, есть только объекты KOL, компактные и эффективные.
В чем же заключается секрет компактности кода? Ответ не один, но выделить главные составляющие все же представляется возможным. В первую очередь следует отметить способность компилятора Delphi не включать в код конечного приложения невостребованный код. Процедуры и переменные, на которые нет ссылок из того кода, который уже внесен в список участков кода, подлежащих включению в конечный продукт, отбрасываются и в дальнейшей сборке не учавствуют. К сожалению, данная способность компилятора Delphi, называемая самими разработчиками компилятора "smart linking" (умное связывание), несколько ограничена. В частности, виртуальные методы используемых классов и объектов не могут быть изъяты из процесса компиляции и сборки приложения. Соответственно, и те переменные и процедуры (методы), на которые имеются ссылки из таких виртуальных методов, также не могут быть отброшены.
При разработке библиотеки KOL это обстоятельство было учтено. Автору пришлось отказаться от жесткого следования канонам объектно-ориентированного программирования. В частности, в KOL один и тот же объектный тип может использоваться для инкапсуляции нескольких подобных друг другу объектов. Например, тип TControl не является базовым для описания визуальных объектов подобно тому, как это сделано в VCL. Представители объектного типа TControl в библиотеке KOL уже без какого-либо наследования могут выполнять роль различных визуальных объектов (кнопок, меток, панелек, и т.п.) - в зависимости от того, какая глобальная функция использовалась для конструирования каждого конкретного объекта (например, NewPanel, NewButton и т.д.)
Такое совмещение нескольких объектов в одном объектном типе, вообще говоря, может приводить к некоторой путанице, поскольку наряду с методами и свойствами, общими для всех объектов, инкапсулированных этим объектным типом, могут иметься методы и свойства сугубо индивидуальные, характерные только для некоторой конкретной разновидности объектов. Поскольку тип (класс) тот же самый, существует вероятность ошибочного применения метода, не свойственного для данной разновидности объекта. Единственная причина, заставившая автора поступать так, это необходимость избежать большого числи виртуальных методов.
Разумеется, если бы виртуальные методы благополучно пропускались компилятором в тех случаях, когда они не нужны (а потенциально такая возможность существует), структуру объектов можно было бы сделать более ясной. Тем не менее, даже и в этом случае VCL не позволил бы программам стать намного компактнее. И проблема здесь уже в том, что разработчики VCL спроектировали свою библиотеку так, что многие объекты создаются и многие действия производятся еще до того, как будет известно, понадобятся ли они вообще, или так и останутся лежать в коде программы мертвым грузом. Например, если создается визуальный объект, то для него инициализируется шрифт, полотно для рисования, менеджеры перетаскивания, множество других объектов - на всякий случай: а вдруг понадобятся! Конечно, программе может понадобиться что-нибудь нарисовать, или изменить какой-нибудь шрифт. Программа может быть спроектирована для использования популярного интерфейса расположения плавающих панелей drag-and-dock. Может, но ведь не обязана, так?
В противоположность VCL, библиотека KOL поступает с необязательными действиями и объектами значительно более аккуратно. Они (действия) выполняются и (объекты) инициализируются только тогда, когда они впервые потребуются. Очистка ресурсов и памяти по завершении использования при этом проблем как раз не представляет. Один и тот же (виртуальный) метод Free прекрасно справляется с освобождением отработавших подчиненных объектов, независимо от их типа. Собственно, это и есть главная причина того, почему программы, изготовленные с использованием библиотеки KOL, настолько кодоэкономичны.
В описываемой библиотеке используется несколько различных способов реализации такого отложенного принятия решения, в зависимости от природы необязательного к включению в программу кода. Важнее не столько перечислить эти способы, которые, вообще говоря, являются просто удачным применением общепринятых программистских приемов, сколько объяснить и понять суть их действия.
Как известно, исходный текст программы превращается в исполняемый машинный код в результате работы иногда нескольких программ: прекомпилятора, компилятора, сборщика. Отметим, что когда речь идет о среде Delphi, разделять эти шаги особого смысла не имеет, так как все они выполняются одной и той же программой. Поэтому, говоря "компилятор", будем подразумевать все эти этапы вместе. Поскольку именно компилятор Delphi принимает решение, подключать или не подключать код той или иной процедуры к программе, откладывание решения о необходимости ее использования путем первого обращения к ней в коде только тогда, когда ее функциональность оказалась востребована разработчиком проекта, требует пояснения.
На самом деле, все очень просто разъясняется слеующим небольшим примером. Пусть наш визуальный объект (кнопка, к примеру) содержит свойство Font (шрифт). В отличие от VCL, не будем создавать подчиненный объект, соответствующий этому свойству, в конструкторе нашего объекта (кнопки, хозяина шрифта). Создадим его в том методе, который выполняет чтение свойства Font (в методе GetFont), в случае если он еще не создан. В итоге, если в приложении к свойству Font нигде нет обращений (т.е. разработчику не было нужды изменять шрифт своих визуальных объектов, и его устраивают стандартные шрифты, настроенные пользователем), компилятор не обнаружит и ни одного вызова метода GetFont, и соответственно, не включит в программу код этого метода. Следовательно, ни одной ссылки не будет обнаружено ни на конструктор объекта шрифта, ни на другие процедуры, которые иначе бы оказались задействованы и попали бы в исполнимый модуль.
Понятно, что приведенный пример поясняет только один из многих использованных приемов. Но принцип всех таких приемов один и тот же, а именно, как уже сказано выше: отложить принятие решения о подключении дополнительного кода до тех пор, пока он не потребуется разработчику программногопродукта. По мнению автора, данный принцип в корне расходится со сложившейся практикой программирования. Пожалуй, на примере KOL, в частности доказана нелепость общепринятого подхода, который приводит к тому, что 90% кода в современных приложениях - это шлак, мусор, который если и работает, то вхолостую, и лишь попусту затрачивает ресурсы процессора, оперативной памяти, занимает место на жестком диске, отнимает время при передаче лишних сотен килобайт по сети и через интернет, и залезает при этом в ваш карман. И недаром в ответ на вопрос, как уменьшить размер программы, иногда можно получить такой ответ, что, дескать, зачем уменьшать? - чем больше объем, тем больше заплатят. (Варианты: "солидней", заказчик больше уважает). Не бессмыслица ли?
Если кто-то из Delphi-программистов, прочитавших эту статью, заинтересуется, то милости прошу на интернет-страницу KOL/MCK, берите себе эти библиотеки (все совершенно бесплатно, в исходных кодах), и обязательно попробуйте. Уверяю: не пожалеете!
Интернет-страница KOL/MCK: http://xcl.cjb.net
Почта: bonanzas@xcl.cjb.net
Взято с сайта
Библиотека WinLight
Библиотека WinLight
////////////////////////////////////////////////////////////////////////////////
// WinLite, библиотека классов и функций для работы с Win32 API
// (c) Николай Мазуркин, 1999-2000
// _____________________________________________________________________________
// Оконные классы
////////////////////////////////////////////////////////////////////////////////
unit WinLite;
interface
uses Windows, Messages;
Инициализационные структуры
Объявление структур, которые используются для формирования параметров вновь создаваемых окон и диалогов соответственно.
////////////////////////////////////////////////////////////////////////////////
// Параметры для создания окна
////////////////////////////////////////////////////////////////////////////////
type
TWindowParams = record
Caption : PChar;
Style : DWord;
ExStyle : DWord;
X : Integer;
Y : Integer;
Width : Integer;
Height : Integer;
WndParent : THandle;
WndMenu : THandle;
Param : Pointer;
WindowClass : TWndClass;
end;
////////////////////////////////////////////////////////////////////////////////
// Параметры для создания диалога
////////////////////////////////////////////////////////////////////////////////
type
TDialogParams = record
Template : PChar;
WndParent : THandle;
end;
Декларация базового класса TLiteFrame
Базовый класс для окон и диалогов. Инкапсулирует в себе дескриптор окна и объявляет общую оконную процедуру. Реализует механизм message-процедур.
////////////////////////////////////////////////////////////////////////////////
// TLiteFrame
// _____________________________________________________________________________
// Базовый класс для объектов TLiteWindow, TLiteDialog, TLiteDialogBox
////////////////////////////////////////////////////////////////////////////////
type
TLiteFrame = class(TObject)
private
FWndCallback: Pointer;
FWndHandle : THandle;
FWndParent : THandle;
function WindowCallback(hWnd: HWnd; Msg, WParam, LParam:Longint):Longint; stdcall;
protected
procedure WindowProcedure(var Msg: TMessage); virtual;
public
property WndHandle: THandle read FWndHandle;
property WndCallback: Pointer read FWndCallback;
public
constructor Create(AWndParent: THandle); virtual;
destructor Destroy; override;
end;
Декларация оконного класса TLiteWindow
Создание уникального класса окна и создание окна. Возможность субклассинга стороннего окна.
////////////////////////////////////////////////////////////////////////////////
// TLiteWindow
// _____________________________________________________________________________
// Оконный класс
////////////////////////////////////////////////////////////////////////////////
type
TLiteWindow = class(TLiteFrame)
private
FWndParams : TWindowParams;
FWndSubclass: Pointer;
protected
procedure CreateWindowParams(var WindowParams: TWindowParams); virtual;
public
procedure DefaultHandler(var Msg); override;
constructor Create(AWndParent: THandle); override;
constructor CreateSubclassed(AWnd: THandle); virtual;
destructor Destroy; override;
end;
Декларация диалогового класса TLiteDialog
Загрузка шаблона диалога и создание диалога.
////////////////////////////////////////////////////////////////////////////////
// TLiteDialog
// _____________________________________________________________________________
// Диалоговый класс
////////////////////////////////////////////////////////////////////////////////
type
TLiteDialog = class(TLiteFrame)
private
FDlgParams : TDialogParams;
protected
procedure CreateDialogParams(var DialogParams: TDialogParams); virtual;
public
procedure DefaultHandler(var Msg); override;
constructor Create(AWndParent: THandle); override;
destructor Destroy; override;
end;
Декларация модального диалогового класса TLiteDialogBox
Загрузка шаблона диалога и создание диалога. Модальный показ диалога.
////////////////////////////////////////////////////////////////////////////////
// TLiteDialogBox
// _____________________________________________________________________________
// Модальный диалоговый класс
////////////////////////////////////////////////////////////////////////////////
type
TLiteDialogBox = class(TLiteFrame)
private
FDlgParams : TDialogParams;
protected
procedure CreateDialogParams(var DialogParams: TDialogParams); virtual;
public
procedure DefaultHandler(var Msg); override;
public
function ShowModal: Integer;
end;
Реализация базового класса TLiteFrame
implementation
////////////////////////////////////////////////////////////////////////////////
// TLiteFrame
// _____________________________________________________________________________
// Инициализация / финализация
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Конструктор
////////////////////////////////////////////////////////////////////////////////
constructor TLiteFrame.Create(AWndParent: THandle);
begin
inherited Create;
// Запоминаем дескриптор родительского окна
FWndParent := AWndParent;
// Создаем место под блок обратного вызова
FWndCallback := VirtualAlloc(nil,12,MEM_RESERVE or MEM_COMMIT,PAGE_EXECUTE_READWRITE);
// Формируем блок обратного вызова
asm
mov EAX, Self
mov ECX, [EAX].TLiteFrame.FWndCallback
mov word ptr [ECX+0], $6858 // pop EAX
mov dword ptr [ECX+2], EAX // push _Self_
mov word ptr [ECX+6], $E950 // push EAX
mov EAX, OFFSET(TLiteFrame.WindowCallback)
sub EAX, ECX
sub EAX, 12
mov dword ptr [ECX+8], EAX // jmp TLiteFrame.WindowCallback
end;
end;
////////////////////////////////////////////////////////////////////////////////
// Деструктор
////////////////////////////////////////////////////////////////////////////////
destructor TLiteFrame.Destroy;
begin
// Уничтожаем структуру блока обратного вызова
VirtualFree(FWndCallback, 0, MEM_RELEASE);
// Уничтожение по умолчанию
inherited;
end;
////////////////////////////////////////////////////////////////////////////////
// TLiteFrame
// _____________________________________________________________________________
// Функции обработки сообщений
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Функция обратного вызова для получения оконных сообщений
////////////////////////////////////////////////////////////////////////////////
function TLiteFrame.WindowCallback(hWnd: HWnd; Msg, WParam, LParam: Integer): Longint;
var
WindowMsg : TMessage;
begin
// Запоминаем дескриптор окна, если это первый вызов оконной процедуры
if FWndHandle = 0 then FWndHandle := hWnd;
// Формируем сообщение
WindowMsg.Msg := Msg;
WindowMsg.WParam := WParam;
WindowMsg.LParam := LParam;
// Обрабатываем его
WindowProcedure(WindowMsg);
// Возвращаем результат обратно системе
Result := WindowMsg.Result;
end;
////////////////////////////////////////////////////////////////////////////////
// Виртуальная функция для обработки оконных сообщений
////////////////////////////////////////////////////////////////////////////////
procedure TLiteFrame.WindowProcedure(var Msg: TMessage);
begin
// Распределяем сообщения по обработчикам
Dispatch(Msg);
end;
Реализация оконного класса TLiteWindow
////////////////////////////////////////////////////////////////////////////////
// TLiteWindow
// _____________________________________________________________________________
// Инициализация / финализация
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Конструктор
////////////////////////////////////////////////////////////////////////////////
constructor TLiteWindow.Create(AWndParent: THandle);
begin
inherited;
// Формируем параметры окна
CreateWindowParams(FWndParams);
// Регистрируем класс окна
RegisterClass(FWndParams.WindowClass);
// Создаем окно
with FWndParams do
CreateWindowEx(ExStyle, WindowClass.lpszClassName, Caption,
Style, X, Y, Width, Height,
WndParent, WndMenu, hInstance, Param
);
end;
////////////////////////////////////////////////////////////////////////////////
// Конструктор элемента с субклассингом
////////////////////////////////////////////////////////////////////////////////
constructor TLiteWindow.CreateSubclassed(AWnd: THandle);
begin
inherited Create(GetParent(AWnd));
// Сохраняем оконную функцию
FWndSubclass := Pointer(GetWindowLong(AWnd, GWL_WNDPROC));
// Сохраняем дескриптор окна
FWndHandle := AWnd;
// Устанавливаем свою оконную функцию
SetWindowLong(FWndHandle, GWL_WNDPROC, DWord(WndCallback));
end;
////////////////////////////////////////////////////////////////////////////////
// Деструктор
////////////////////////////////////////////////////////////////////////////////
destructor TLiteWindow.Destroy;
begin
// Наш объект - объект субклассиннга ?
if FWndSubclass = nil then
begin
// Уничтожаем класс окна
UnregisterClass(FWndParams.WindowClass.lpszClassName, hInstance);
// Уничтожаем окно
if IsWindow(FWndHandle) then DestroyWindow(FWndHandle);
end
else
// Восстанавливаем старую оконную функцию
SetWindowLong(FWndHandle, GWL_WNDPROC, DWord(FWndSubclass));
// Уничтожение по умолчанию
inherited;
end;
////////////////////////////////////////////////////////////////////////////////
// Формирование параметров окна по умолчанию
////////////////////////////////////////////////////////////////////////////////
procedure TLiteWindow.CreateWindowParams(var WindowParams: TWindowParams);
var
WndClassName : string;
begin
// Формируем имя класса
Str(DWord(Self), WndClassName);
WndClassName := ClassName+':'+WndClassName;
// Заполняем информацию о классе окна
with FWndParams.WindowClass do
begin
style := CS_DBLCLKS;
lpfnWndProc := WndCallback;
cbClsExtra := 0;
cbWndExtra := 0;
lpszClassName := PChar(WndClassName);
hInstance := hInstance;
hIcon := LoadIcon(0, IDI_APPLICATION);
hCursor := LoadCursor(0, IDC_ARROW);
hbrBackground := COLOR_BTNFACE + 1;
lpszMenuName := '';
end;
// Заполняем информацию об окне
with FWndParams do
begin
WndParent := FWndParent;
Caption := 'Lite Window';
Style := WS_OVERLAPPEDWINDOW or WS_VISIBLE;
ExStyle := 0;
X := Integer(CW_USEDEFAULT);
Y := Integer(CW_USEDEFAULT);
Width := Integer(CW_USEDEFAULT);
Height := Integer(CW_USEDEFAULT);
WndMenu := 0;
Param := nil;
end;
end;
////////////////////////////////////////////////////////////////////////////////
// TLiteWindow
// _____________________________________________________________________________
// Функции обработки сообщений
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Обработчик сообщений по умолчанию
////////////////////////////////////////////////////////////////////////////////
procedure TLiteWindow.DefaultHandler(var Msg);
begin
// Наш объект - объект субклассиннга ?
if FWndSubclass = nil then
// Вызываем системную функцию обработки сообщений
with TMessage(Msg) do
Result := DefWindowProc(FWndHandle, Msg, WParam, LParam)
else
// Вызываем старую оконную функцию обработки сообщений
with TMessage(Msg) do
Result := CallWindowProc(FWndSubclass, FWndHandle, Msg, WParam, LParam);
end;
Реализация диалогового класса TLiteDialog
////////////////////////////////////////////////////////////////////////////////
// TLiteDialog
// _____________________________________________________________________________
// Инициализация / финализация
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Конструктор
////////////////////////////////////////////////////////////////////////////////
constructor TLiteDialog.Create(AWndParent: THandle);
begin
inherited;
// Формируем параметры диалога
CreateDialogParams(FDlgParams);
// Создаем диалог
with FDlgParams do
CreateDialogParam(hInstance, Template, WndParent, WndCallback, 0);
end;
////////////////////////////////////////////////////////////////////////////////
// Деструктор
////////////////////////////////////////////////////////////////////////////////
destructor TLiteDialog.Destroy;
begin
// Уничтожаем диалог
if IsWindow(FWndHandle) then DestroyWindow(FWndHandle);
// Уничтожение по умолчанию
inherited;
end;
////////////////////////////////////////////////////////////////////////////////
// Формирование параметров диалога по умолчанию
////////////////////////////////////////////////////////////////////////////////
procedure TLiteDialog.CreateDialogParams(var DialogParams: TDialogParams);
begin
DialogParams.WndParent := FWndParent;
DialogParams.Template := '';
end;
////////////////////////////////////////////////////////////////////////////////
// Обработка сообщений по умолчанию
////////////////////////////////////////////////////////////////////////////////
procedure TLiteDialog.DefaultHandler(var Msg);
begin
// Возвращаемые значения по умолчанию
with TMessage(Msg) do
if Msg = WM_INITDIALOG then Result := 1
else Result := 0;
end;
Реализация модального диалогового класса TLiteDialogBox
////////////////////////////////////////////////////////////////////////////////
// TLiteDialogBox
// _____________________________________________________________________________
// Инициализация / финализация
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Формирование параметров диалога по умолчанию
////////////////////////////////////////////////////////////////////////////////
procedure TLiteDialogBox.CreateDialogParams(var DialogParams: TDialogParams);
begin
DialogParams.WndParent := FWndParent;
DialogParams.Template := '';
end;
////////////////////////////////////////////////////////////////////////////////
// Активизация модального диалога
////////////////////////////////////////////////////////////////////////////////
function TLiteDialogBox.ShowModal: Integer;
begin
// Формируем параметры диалога
CreateDialogParams(FDlgParams);
// Показываем диалог
with FDlgParams do
Result := DialogBoxParam(hInstance, Template, WndParent, WndCallback, 0);
end;
////////////////////////////////////////////////////////////////////////////////
// Обработка сообщений по умолчанию
////////////////////////////////////////////////////////////////////////////////
procedure TLiteDialogBox.DefaultHandler(var Msg);
begin
// Возвращаемые значения по умолчанию
with TMessage(Msg) do
if Msg = WM_INITDIALOG then Result := 1
else Result := 0;
end;
end.
BIOS
BIOS
Cодержание раздела:
Bitmap без формы
Bitmap без формы
Автор: Mike Scott
Как мне загрузить изображение (BMP) и отобразить это на рабочем столе без использования формы? (Я хочу отображать это из DLL).
Существует один способ сделать это: создать холст TCanvas, получить контекст устройства для рабочего стола и назначить его дескриптору холста. После рисования на холсте десктоп отобразит ваше творение. Вот пример:
var
DesktopCanvas: TCanvas;
begin
DesktopCanvas := TCanvas.Create;
try
DesktopCanvas.Handle := GetDC(0);
try
DesktopCanvas.MoveTo(0, 0);
DesktopCanvas.LineTo(Screen.Width, Screen.Height);
finally
ReleaseDC(0, DesktopCanvas.Handle);
DesktopCanvas.Handle := 0;
end;
finally
DesktopCanvas.Free;
end;
end;
Вы можете создать TBitmap и загрузить в него BMP-файл. Единственная гнустная вещь может произойти, если вы используете изображение с 256-цветной палитрой при работе в режиме с 256 цветами. Обойти это припятствие можно так: создать форму без границ и заголовка, установить ее высоту и ширину в ноль, поместить на нее компонент TImage и загрузить в него необходимое изображение. VCL реализует для вас нужную палитру.
Взято из
Bitmap.PixelFormat:=pf1bit;
Bitmap.PixelFormat:=pf1bit;
Доступ к pf8bit-изображениям осуществляется достаточно легко, с тех пор, как они стали использовать один байт на пиксель. Но вы можете сохранить много памяти, если вам необходим единственный бит на пиксель (как, например, с различными масками) в случае pf1bit-изображения.
Как и в случае с pf8bit-изображениями, используйте TByteArray для доступа к pf1bit-ным линиям чередования (Scanlines). Но для доступа к отдельным пикселям вам понадобиться работать с битами отдельного байта. Так, ширина линии чередования равна Bitmap.Width DIV 8 байт.
Нижеприведенный код показывает как можно создать шаблон 1-битного изображения: черный, белый, полоски, "g", "стрелка" и случайный -- опция "инвертировано" также доступна. (Надеюсь, технологию вы освоете без труда.)
Создайте форму с Image1: для TImage я использую одно изображение Image1 размером 256x256 и свойством Stretch := TRUE, чтобы отдельные пиксели было легко разглядеть. Кнопки Black, White и Stripes имеют свойство tags, c соответствующими значениями 0, 255, и 85 ($55 = 01010101 в двоичной системе исчисления), вызывающие при нажатии обработчик события ButtonStripesClick.
Кнопки "g" и "arrow" имеют собственные обработчики событий, позволяющие корректно распечатать тестовые изображения на принтере HP Laserjet.
"Random" случайным образом устанавливает биты в 1-битном изображении.
"Invert" меняет нули на единички и наоборот.
//Пример того, как использовать Bitmap.Scanline для PixelFormat=pf1Bit.
// По просьбе Mino Ballone из Италии.
//
// Авторское право (C) 1997, Earl F. Glynn, Overland Park, KS.
// Все права защищены.
// Может свободно использоваться для некоммерческих целей.
unit ScreenSingleBit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
ButtonBlack: TButton;
ButtonWhite: TButton;
ButtonStripes: TButton;
ButtonG: TButton;
ButtonArrow: TButton;
ButtonRandom: TButton;
ButtonInvert: TButton;
procedure ButtonStripesClick(Sender: TObject);
procedure ButtonGClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ButtonRandomClick(Sender: TObject);
procedure ButtonInvertClick(Sender: TObject);
procedure ButtonArrowClick(Sender: TObject);
private
Bitmap: TBitmap;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
const
BitsPerPixel = 8;
procedure TForm1.ButtonStripesClick(Sender: TObject);
var
i: INTEGER;
j: INTEGER;
Row: pByteArray;
Value: BYTE;
begin
Value := (Sender as TButton).Tag;
// Value = $00 = 00000000 в двоичном исчислении для черного
// Value = $FF = 11111111 в двоичном исчислении для белого
// Value = $55 = 01010101 в двоичном исчислении для черных и белых полос
for j := 0 to Bitmap.Height - 1 do
begin
Row := pByteArray(Bitmap.Scanline[j]);
for i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do
begin
Row[i] := Value
end
end;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.ButtonGClick(Sender: TObject);
const
{Изображение "g" было адаптировано для печати на принтере
LaserJet IIP в соответствии с техническим руководством}
G: array[0..31, 0..3] of BYTE =
{ 0}(($00, $FC, $0F, $C0), {00000000 11111100 00001111 11000000}
{ 1}($07, $FF, $1F, $E0), {00000111 11111111 00011111 11100000}
{ 2}($0F, $FF, $9F, $C0), {00001111 11111111 10011111 11000000}
{ 3}($3F, $D7, $DE, $00), {00111111 11010111 11011110 00000000}
{ 4}($3E, $01, $FE, $00), {00111110 00000001 11111110 00000000}
{ 5}($7C, $00, $7E, $00), {01111100 00000000 01111110 00000000}
{ 6}($78, $00, $7E, $00), {01111000 00000000 01111110 00000000}
{ 7}($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}
{ 8}($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}
{ 9}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{10}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{11}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{12}($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{13}($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}
{14}($78, $00, $3E, $00), {01111000 00000000 00111110 00000000}
{15}($78, $00, $3E, $00), {01111000 00000000 00111110 00000000}
{16}($78, $00, $7E, $00), {01111000 00000000 01111110 00000000}
{17}($3C, $00, $FE, $00), {00111100 00000000 11111110 00000000}
{18}($1F, $D7, $DE, $00), {00011111 11010111 11011110 00000000}
{19}($0F, $FF, $5E, $00), {00001111 11111111 10011110 00000000}
{20}($07, $FF, $1E, $00), {00000111 11111111 00011110 00000000}
{21}($00, $A8, $1E, $00), {00000000 10101000 00011110 00000000}
{22}($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}
{23}($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}
{24}($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}
{25}($00, $00, $3E, $00), {00000000 00000000 00111110 00000000}
{26}($00, $00, $3C, $00), {00000000 00000000 00111100 00000000}
{27}($00, $00, $7C, $00), {00000000 00000000 01111100 00000000}
{28}($00, $01, $F8, $00), {00000000 00000001 11111000 00000000}
{29}($01, $FF, $F0, $00), {00000001 11111111 11110000 00000000}
{30}($03, $FF, $E0, $00), {00000011 11111111 11100000 00000000}
{31}($01, $FF, $80, $00)); {00000001 11111111 10000000 00000000}
var
i: INTEGER;
j: INTEGER;
Row: pByteArray;
begin
for j := 0 to Bitmap.Height - 1 do
begin
Row := pByteArray(Bitmap.Scanline[j]);
for i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do
begin
Row[i] := G[j, i]
end
end;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.ButtonArrowClick(Sender: TObject);
const
{Изображение "стрелка" было адаптировано для печати на принтере
LaserJet IIP в соответствии с техническим руководством}
Arrow: array[0..31, 0..3] of BYTE =
{ 0}(($00, $00, $80, $00), {00000000 00000000 10000000 00000000}
{ 1}($00, $00, $C0, $00), {00000000 00000000 11000000 00000000}
{ 2}($00, $00, $E0, $00), {00000000 00000000 11100000 00000000}
{ 3}($00, $00, $F0, $00), {00000000 00000000 11110000 00000000}
{ 4}($00, $00, $F8, $00), {00000000 00000000 11111000 00000000}
{ 5}($00, $00, $FC, $00), {00000000 00000000 11111100 00000000}
{ 6}($00, $00, $FE, $00), {00000000 00000000 11111110 00000000}
{ 7}($00, $00, $FF, $00), {00000000 00000000 11111111 00000000}
{ 8}($00, $00, $FF, $80), {00000000 00000000 11111111 10000000}
{ 9}($FF, $FF, $FF, $C0), {11111111 11111111 11111111 11000000}
{10}($FF, $FF, $FF, $E0), {11111111 11111111 11111111 11100000}
{11}($FF, $FF, $FF, $F0), {11111111 11111111 11111111 11110000}
{12}($FF, $FF, $FF, $F8), {11111111 11111111 11111111 11111000}
{13}($FF, $FF, $FF, $FC), {11111111 11111111 11111111 11111100}
{14}($FF, $FF, $FF, $FE), {11111111 11111111 11111111 11111110}
{15}($FF, $FF, $FF, $FF), {11111111 11111111 11111111 11111111}
{16}($FF, $FF, $FF, $FF), {11111111 11111111 11111111 11111111}
{17}($FF, $FF, $FF, $FE), {11111111 11111111 11111111 11111110}
{18}($FF, $FF, $FF, $FC), {11111111 11111111 11111111 11111100}
{19}($FF, $FF, $FF, $F8), {11111111 11111111 11111111 11111000}
{20}($FF, $FF, $FF, $F0), {11111111 11111111 11111111 11110000}
{21}($FF, $FF, $FF, $E0), {11111111 11111111 11111111 11100000}
{22}($FF, $FF, $FF, $C0), {11111111 11111111 11111111 11000000}
{23}($00, $00, $FF, $80), {00000000 00000000 11111111 10000000}
{24}($00, $00, $FF, $00), {00000000 00000000 11111111 00000000}
{25}($00, $00, $FE, $00), {00000000 00000000 11111110 00000000}
{26}($00, $00, $FC, $00), {00000000 00000000 11111100 00000000}
{27}($00, $00, $F8, $00), {00000000 00000000 11111000 00000000}
{28}($00, $00, $F0, $00), {00000000 00000000 11110000 00000000}
{29}($00, $00, $E0, $00), {00000000 00000000 11100000 00000000}
{30}($00, $00, $C0, $00), {00000000 00000000 11000000 00000000}
{31}($00, $00, $80, $00)); {00000000 00000000 10000000 00000000}
var
i: INTEGER;
j: INTEGER;
Row: pByteArray;
begin
for j := 0 to Bitmap.Height - 1 do
begin
Row := pByteArray(Bitmap.Scanline[j]);
for i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do
begin
Row[i] := arrow[j, i]
end
end;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Bitmap := TBitmap.Create;
with Bitmap do
begin
Width := 32;
Height := 32;
PixelFormat := pf1bit
end;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Bitmap.Free
end;
procedure TForm1.ButtonRandomClick(Sender: TObject);
var
i: INTEGER;
j: INTEGER;
Row: pByteArray;
begin
for j := 0 to Bitmap.Height - 1 do
begin
Row := pByteArray(Bitmap.Scanline[j]);
for i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do
begin
Row[i] := Random(256)
end
end;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.ButtonInvertClick(Sender: TObject);
var
i: INTEGER;
j: INTEGER;
Row: pByteArray;
begin
for j := 0 to Bitmap.Height - 1 do
begin
Row := pByteArray(Bitmap.Scanline[j]);
for i := 0 to (Bitmap.Width div BitsPerPixel) - 1 do
begin
Row[i] := not Row[i]
end
end;
Image1.Picture.Graphic := Bitmap
end;
end.
Взято с
Bitmap.PixelFormat:=pf8bit;
Bitmap.PixelFormat:=pf8bit;
Доступ к такому формату изображения легко получить, используя TByteArray (определен в SysUtils.PAS):
PByteArray= ^TByteArray;
TByteArray = array[0..32767] of Byte;
(Я думаю (но сам этого не пробовал), что вы сможете получить доступ к pf16bit-изображениям, используя следующие определения в SysUtils.PAS:
PWordArray = ^TWordArray;
TWordArray = array[0..16383] of Word;
Для того, чтобы обработать 8-битное (pf8bit) изображение, используйте конструктор подобный этому, который создает гистограмму изображения:
TYPE
THistogram = ARRAY[0..255] OF INTEGER;
...
VAR
Histogram: THistogram;
i : INTEGER;
j : INTEGER;
Row : pByteArray;
...
FOR i := Low(THistogram) TO High(THistogram) DO
Histogram[i] := 0;
IF Bitmap.PixelFormat = pf8bit THEN
BEGIN
FOR j := Bitmap.Height-1 DOWNTO 0 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := Bitmap.Width-1 DOWNTO 0 DO
BEGIN
INC (Histogram[Row[i]])
END
END
END
...
Взято с
Bitmap.PixelFormat:=pf24bit;
Bitmap.PixelFormat:=pf24bit;
Для pf24bit-изображений необходимо определить:
CONST
PixelCountMax= 32768;
TYPE
pRGBArray = ^TRGBArray;
TRGBArray = ARRAY[0..PixelCountMax-1] OF TRGBTriple;
Примечание: TRGBTriple определен в модуле Windows.PAS.
Для того, чтобы к существующему 24-битному изображению иметь доступ как к изображению, созданному с разрешением 3 байта на пиксел, сделайте следующее:
...
VAR
i : INTEGER;
j : INTEGER;
RowOriginal : pRGBArray;
RowProcessed: pRGBArray;
BEGIN
IF OriginalBitmap.PixelFormat <> pf24bit THEN
RAISE EImageProcessingError.Create('GetImageSpace: ' +
'Изображение должно быть 24-х битным.');
{Шаг через каждую строчку изображения.}
FOR j := OriginalBitmap.Height-1 DOWNTO 0 DO
BEGIN
RowOriginal := pRGBArray(OriginalBitmap.Scanline[j]);
RowProcessed := pRGBArray(ProcessedBitmap.Scanline[j]);
FOR i := OriginalBitmap.Width-1 DOWNTO 0 DO
BEGIN
// Доступ к RGB-цветам отдельных пикселей должен осуществляться следующим образом:
// RowProcessed[i].rgbtRed := RowOriginal[i].rgbtRed;
// RowProcessed[i].rgbtGreen := RowOriginal[i].rgbtGreen;
// RowProcessed[i].rgbtBlue := RowOriginal[i].rgbtBlue;
END
END
END
...
Взято с
Bitmap.PixelFormat - подробности
Bitmap.PixelFormat - подробности
Общее
Новое в Delphi 3 свойство scanline допускает быстрый доступ к отдельным пикселям, но необходимо указать с каким Bitmap.PixelFormat вы работаете, прежде чем сможете иметь доступ к пикселям.
Возможные PixelFormats включают:
pfDevice
pf4bit
pf15bit
pf16bit
pf32bit
Взято с
Bitmap в StringGrid ячейке.
Bitmap в StringGrid ячейке.
Автор: Olivio Moura
В обработчике события OnDrawCell элемента StringGrid поместите следующий код:
with (Sender as TStringGrid) do
with Canvas do
begin
{...}
Draw(Rect.Left, Rect.Top, Image1.Picture.Graphic);
{...}
end;
Используйте метод Draw() или StretchDraw() класса TCanvas. Image1 - это TImage с предварительно загруженным в него bitmap-ом.
Взято с Исходников.ru
Благодарности
Благодарности
Хочу выразить свою благодарность администрации форумов Vingrad.ru и Sources.ru любезно разрешивших составить этот документ.
Выражаю так же искреннюю признательность всем тем, кто взял на себя труд тестирования настоящего документа и высказавших свои конструктивные, крайне полезные и информативные замечания, позволившие сделать это документ значительно лучше. Особую признательность хочу выразить для участников обсуждения под никами: p0s0l, Bas, StayAtHome - огромное Вам спасибо!