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

Дублирование компонентов и их потомков во время выполнения приложения 2.

Автор: Xavier
Три подруги: одна вышла замуж за сотрудника компании IBM, вторая за сотрудника компании APPALE, третья за сотрудника Microsoft... После первой брачной ночи расказывают: Первая: Класс.. Такая надежность, такая устойчивость, такое время наработки на отказ... Вторая: Кайф просто... Такой дружественный интерфейс, такая совместимость, такая производительность... Третья: А мой всю ночь расказывал как это будет хорошо...
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBTables, Outline;
function Replicator(C: TComponent): TComponent;
implementation
{ Следующая процедура "клонирует" свойства C1 и записывает их в C2.
C1 и C2 должны иметь один и тот же тип. Используйте данный метод для
компонентов, не имеющих метода Assign. }
procedure CloneComponent(C1: TComponent; C2: TComponent);
var
  S: TMemoryStream;
begin
  if C1.ClassType <> C2.ClassType then
    raise EComponentError.Create('Типы объектов не совместимы');
  if C1 is TWinControl then
    TWinControl(C2).Parent := TWinControl(C1).Parent;
  S := TMemoryStream.Create; { создаем поток для работы с памятью }
  with S do
  begin
    WriteComponent(C1); { пишем свойства C1 в поток }
    Seek(0, 0); { перемещаемся в начало потока }
    ReadComponent(C2); { читаем свойства из потока в C2 }
    Free; { освобождаем поток }
  end;
end;
{ Следующая функция "реплицирует" компонент C и возвращает новый
компонент типа и со свойствами компонента C. }
function Replicator(C: TComponent): TComponent;
begin
  Result := TComponentClass(C.ClassType).Create(C.Owner); { создаем компонент }
  CloneComponent(C, Result); { клонируем его }
end;
end.

Вот как это использовать:

var
  BitBtn: TBitBtn;
begin
  { Если BitBtn1 уже существует }
  TComponent(BitBtn) := Replicator(BitBtn1);
end;

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

procedure CreateMSAccessDB(filename : String);
var
  DBEngine, Workspace: Variant;
const
  {Important to use the following constant as is}
  dbLangGeneral = ';LANGID=0x0409;CP=1252;COUNTRY=0';
  dbVersion30 = 32;
begin
  DBEngine := CreateOleObject('DAO.DBEngine');
  {DBEngine := CreateOleObject('DAO.DBEngine.35'); For DAO 3.5}
  Workspace := DBEngine.Workspaces[0];
  try
    Workspace.CreateDatabase(filename, dbLangGeneral, dbVersion30);
  except on EOleException do
    ShowMessage('Database already exists');
  end;
end;

Создание события во время выполнения приложения.

На примере переопределения события в Memo:

memo.onchange:=memo1Change;
procedure TForm1.Memo1Change(Sender: TObject);
begin
  panel1.caption:='Содержимое было изменено';
end;

Ошибки времени выполнения.

Виндоват - испраглюсь!
type
str31  = string[31];
function ErrMeaning (ResultCode: Integer): str31;
{----------------------------------------------------------}
{ Возвращает строковое значение по коду ошибки.            }
{----------------------------------------------------------}
const
NumOfEntries = 108;
type
ErrorEntry = record
Code: Integer;
Meaning: str31;
end;
ErrorMeaningsArray = array [1..NumOfEntries] of ErrorEntry;
const
MeaningsArray: ErrorMeaningsArray =
{Ошибки DOS}
((Code:   1;  Meaning: 'Неверный номер DOS-функции' {Invalid DOS function number}),
(Code:   2;  Meaning: 'Файл не найден' {File not found}),
(Code:   3;  Meaning: 'Путь не найден' {Path not found}),
(Code:   4;  Meaning: 'Слишком много открытых файлов' {Too many open files}),
(Code:   5;  Meaning: 'В доступе к файлу отказано' {File access denied}),
(Code:   6;  Meaning: 'Неверный дескриптор файла' {Invalid file handle}),
(Code:   7;  Meaning: 'Разрушены управляющие блоки памяти' {Memory control blocks destroyed}),
(Code:   8;  Meaning: 'Недостаточно памяти DOS' {Insufficient DOS memory}),
(Code:   9;  Meaning: 'Неверный адрес блока памяти' {Invalid memory block address}),
(Code:  10;  Meaning: 'Неверное окружение DOS' {Invalid DOS environment}),
(Code:  11;  Meaning: 'Неверный формат (DOS)' {Invalid format (DOS)}),
(Code:  12;  Meaning: 'Неверный код доступа к файлу' {Invalid file access code}),
(Code:  13;  Meaning: 'Неверные данные (DOS)' {Invalid data (DOS)}),
(Code:  15;  Meaning: 'Неверный номер устройства' {Invalid drive number}),
(Code:  16;  Meaning: 'Невозможно удалить текущую директорию' {Cannot remove current directory}),
(Code:  17;  Meaning: 'Невозможно переименовать драйвер' {Cannot rename across drives}),
(Code:  18;  Meaning: 'Файлов больше нет' {No more files}),
(Code:  19;  Meaning: 'Диск защищен от записи' {Disk write-protected}),
(Code:  20;  Meaning: 'Неизвестное устройство (DOS)' {Unknown unit (DOS)}),
(Code:  21;  Meaning: 'Устройство не готово' {Drive not ready}),
(Code:  22;  Meaning: 'Неизвестная команда DOS' {Unknown DOS command}),
(Code:  23;  Meaning: 'CRC-ошибка' {CRC error}),
(Code:  24;  Meaning: 'Плохой запрос длины структуры' {Bad request structure length}),
(Code:  25;  Meaning: 'Ошибка поиска' {Seek error}),
(Code:  26;  Meaning: 'Неизвестный тип носителя' {Unknown media type}),
(Code:  27;  Meaning: 'Сектор диска не найден' {Disk sector not found}),
(Code:  28;  Meaning: 'Недостаточно бумаги' {Out of paper}),
(Code:  29;  Meaning: 'Ошибка записи' {Write fault}),
(Code:  30;  Meaning: 'Ошибка чтения' {Read fault}),
(Code:  31;  Meaning: 'Общий сбой' {General failure}),
(Code:  32;  Meaning: 'Нарушение общего доступа к файлу' {File sharing violation}),
(Code:  33;  Meaning: 'Нарушение блокировки файла' {File lock violation}),
(Code:  34;  Meaning: 'Неверная смена диска' {Invalid disk change}),
(Code:  35;  Meaning: 'Управляющий блок файла недоступен' {File control block unavailable}),
(Code:  36;  Meaning: 'Переполнение разделяемого буфера' {Sharing buffer overflow}),
(Code:  37;  Meaning: 'Несовпадение кодовой страницы' {Code page mismatch}),
(Code:  38;  Meaning: 'Ошибка обработки EOF' {Error handling EOF}),
(Code:  39;  Meaning: 'Дескриптор диска полон' {Handle disk full}),
(Code:  50;  Meaning: 'Сетевой запрос не поддерживается' {Network request not supported}),
(Code:  51;  Meaning: 'Удаленный компьютер недоступен' {Remote computer not listening}),
(Code:  52;  Meaning: 'Дубликат имени в сети' {Duplicate name on network}),
(Code:  53;  Meaning: 'Сетевое имя не найдено' {Network name not found}),
(Code:  54;  Meaning: 'Сеть занята' {Network busy}),
(Code:  55;  Meaning: 'Сетевое устройство больше не существует' {Network device no longer exists}),
(Code:  56;  Meaning: 'Превышен лимит команды NetBIOS' {NetBIOS command limit exceeded}),
(Code:  57;  Meaning: 'Ошибка сетевого адаптера' {Network adaptor error}),
(Code:  58;  Meaning: 'Неверный сетевой ответ' {Incorrect network response}),
(Code:  59;  Meaning: 'Неожиданная ошибка сети' {Unexpected network error}),
(Code:  60;  Meaning: 'Несовместимый удаленный адаптер' {Incompatible remote adaptor}),
(Code:  61;  Meaning: 'Очередь на печать переполнена' {Print queue full}),
(Code:  62;  Meaning: 'Нет достаточного места для файла печати' {Not enough space for print file}),
(Code:  63;  Meaning: 'Файл печати удален' {Print file deleted}),
(Code:  64;  Meaning: 'Сетевое имя удалено' {Network name deleted}),
(Code:  65;  Meaning: 'Доступ запрещен' {Access denied}),
(Code:  66;  Meaning: 'Неверный тип сетевого устройства' {Network device type incorrect}),
(Code:  67;  Meaning: 'Сетевое имя не найдено' {Network name not found}),
(Code:  68;  Meaning: 'Превышен предел сетевого имени' {Network name limit exceeded}),
(Code:  69;  Meaning: 'Превышен предел сеансов NetBIOS' {NetBIOS session limit exceeded}),
(Code:  70;  Meaning: 'Временная пауза' {Temporarily paused}),
(Code:  71;  Meaning: 'Сетевой запрос не принят' {Network request not accepted}),
(Code:  72;  Meaning: 'Пауза переадресации печати/диска' {Print/disk redirection paused}),
(Code:  80;  Meaning: 'Файл уже существует' {File already exists}),
(Code:  82;  Meaning: 'Невозможно создать каталог' {Cannot make directory entry}),
(Code:  83;  Meaning: 'Ошибка прерывания 24' {Fail on interrupt 24}),
(Code:  84;  Meaning: 'Слишком много переадресаций' {Too many redirections}),
(Code:  85;  Meaning: 'Дубликат переадресации' {Duplicate redirection}),
(Code:  86;  Meaning: 'Неверный пароль' {Invalid password}),
(Code:  87;  Meaning: 'Неверный параметр' {Invalid parameter}),
(Code:  88;  Meaning: 'Ошибка данных сети' {Network data fault}),
{Ошибки ввода/вывода (I/O errors)}
(Code: 100;  Meaning: 'Ошибка чтения диска' {Disk read error}),
(Code: 101;  Meaning: 'Ошибка записи диска' {Disk write error}),
(Code: 102;  Meaning: 'Файл не назначен' {File not assigned}),
(Code: 103;  Meaning: 'Файл не открыт' {File not open}),
(Code: 104;  Meaning: 'Не открыт файл для приема' {File not open for input}),
(Code: 105;  Meaning: 'Не открыт файл для выдачи' {File not open for output}),
(Code: 106;  Meaning: 'Неверный числовой формат' {Invalid numeric format}),
{Критические ошибки (Только для реального или защищенного режима)}
(Code: 150;  Meaning: 'Диск защищен от записи' {Disk is write protected}),
(Code: 151;  Meaning: 'Неизвестное устройство' {Unknown unit}),
(Code: 152;  Meaning: 'Устройство не готово' {Drive not ready}),
(Code: 153;  Meaning: 'Неизвестная команда DOS' {Unknown DOS command}),
(Code: 154;  Meaning: 'Ошибка CRC в данных' {CRC error in data}),
(Code: 155;  Meaning: 'Плохой запрос длины структуры устройства' {Bad drive request struct length}),
(Code: 156;  Meaning: 'Ошибка позиционирования диска' {Disk seek error}),
(Code: 157;  Meaning: 'Неизвестный тип носителя' {Unknown media type}),
(Code: 158;  Meaning: 'Сектор не найден' {Sector not found}),
(Code: 159;  Meaning: 'Недостаточно бумаги в принтере' {Printer out of paper}),
(Code: 160;  Meaning: 'Ошибка записи устройства' {Device write fault}),
(Code: 161;  Meaning: 'Ошибка чтения устройства' {Device read fault}),
(Code: 162;  Meaning: 'Аппаратный сбой' {Hardware failure}),
{Фатальные ошибки}
(Code: 200;  Meaning: 'Деление на ноль' {Division by zero}),
(Code: 201;  Meaning: 'Ошибка проверки диапазона' {Range check error}),
(Code: 202;  Meaning: 'Ошибка переполнения стека' {Stack overflow error}),
(Code: 203;  Meaning: 'Ошибка переполнения кучи' {Heap overflow error}),
(Code: 204;  Meaning: 'Неверная операция с указателем' {Invalid pointer operation}),
(Code: 205;  Meaning: 'Переполнение числа с плавающей точкой' {Floating point overflow}),
(Code: 206;  Meaning: 'Потеря значимости числа с плавающей точкой' {Floating point underflow}),
(Code: 207;  Meaning: 'Неверная операция с числом с плавающей точкой' {Invalid floating pt. operation}),
(Code: 208;  Meaning: 'Не установлен оверлей-менеджер' {Overlay manager not installed}),
(Code: 209;  Meaning: 'Ошибка чтения оверлей-файла' {Overlay file read error}),
(Code: 210;  Meaning: 'Объект не инициализирован' {Object not initialised}),
(Code: 211;  Meaning: 'Вызов абстрактного метода' {Call to abstract method}),
(Code: 212;  Meaning: 'Ошибка регистрации потока' {Stream registration error}),
(Code: 213;  Meaning: 'Индекс TCollection вышел за границы диапазона' {TCollection index out of range}),
(Code: 214;  Meaning: 'Ошибка переполнения TCollection' {TCollection overflow error}),
(Code: 215;  Meaning: 'Ошибка арифметического переполнения' {Arithmetic overflow error}),
(Code: 216;  Meaning: 'Общая ошибка защиты' {General Protection Fault}),
(Code: 217;  Meaning: 'Необработанное исключение' {Unhandled exception}),
(Code: 219;  Meaning: 'Неверное приведение типа' {Invalid typecast}));
var
Low, High, Mid, Diff: Integer;
begin
Low := 1;
High := NumOfEntries;
while Low <= High do
begin
Mid := (Low + High) div 2;
Diff := MeaningsArray[Mid].Code - ResultCode;
if Diff < 0 then Low  := Mid + 1 else
if Diff > 0 then High := Mid - 1 else
begin {нашли это}
ErrMeaning := MeaningsArray[Mid].Meaning;
Exit; {ErrMeaning}
end;
end; {while}
ErrMeaning := 'Ошибка ' + IntToStr(ResultCode) +
' (неизвестное значение)';
end; {ErrMeaning}

Ещё...

  var
    i: Integer;
  begin
    for i := 1 to NumOfEntries do
      if MeaningsArray[i].Code < ResultCode then
        Continue {до следующей итерации цикла FOR loop}
      else
      begin
        if MeaningsArray[i].Code = ResultCode then
        begin
          ErrMeaning := MeaningsArray[i].Meaning;
          Exit; {ErrMeaning}
        end
        else {Code in array > ResultCode}
          Break; {выход из цикла FOR}
      end;
    ErrMeaning := 'Ошибка ' + IntToStr(ResultCode) +
                  ' (неизвестное значение)';
  end; {ErrMeaning}

Функции редактора полей во время выполнения программы.

Автор: Lindsay Reichmann Возможен ли вызов функций редактора полей (Fields Editor) во время выполнения программы? Да. Если вы определили поля во время разработки приложения, то во время выполнения можно менять их свойства (например, Size).

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

procedure SetupFieldsAndOpenDataset(DataSet: TDataSet);
var
  FieldNum, DefNum: Integer;
begin
  with DataSet do
  begin
    if Active then
      Close;
    FieldDefs.Update; {набор данных должен быть закрыт}
    {ищем каждое предопределенное TField в DataSet.FieldDefs:}
    for FieldNum := FieldCount - 1 downto 0 do
      with Fields[FieldNum] do
      begin
        DefNum := FieldDefs.IndexOf(FieldName);
        if DefNum < 0 then
          raise Exception.CreateFmt(
            'Поле "%s" не найдено в наборе данных "%s"',
            [FieldName, Dataset.Name]);
        {устанавливаем свойство size:}
        Size := FieldDefs[DefNum].Size;
      end;
    Open;
  end;
end;

RTTI в действии.

Интернетчик проснулся ночью. Смотрит - вокруг темно, фонари не горят, ничего не видно. "Network is unreachable", - подумал Интернетчик.

Бывает, что надо всем контролам на форме поменять OnMouseMove Event. Или же нужно сбросить свойство Hint у всех элементов, у которых это свойство есть. Контролы могут быть самые разные TEdit, TLabel, TListView и т.п. Такаие возможности в Delphi есть, более того, на них построена вся среда визуальной разработки IDE Delphi.

Для справки:

Run-time type information (RTTI) - это специальный механизм определения типа объекта во время выполнения. Чаще всего это актуально для определения типа объекта по указателю. Многие каркасные библиотеки самостоятельно поддерживают этот мехзанизм. В том числе и VCL.

Приведем пример использования данного механизма в Delphi. Для начала рассмотрим работу с методами, а затем - со свойствами.

Следующий пример устанавливает у всех компонентов на форме обработчик OnMouseMove на собственную процедуру.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    //...эта ф-ия обязательно должна быть описана здесь, а не в одном из
    // следующих разделов, иначе MethodAddress() ее не видит.
    procedure CommonMouseMove (Sender: TObject;
    Shift: TShiftState; X, Y: Integer);
  private
  public
    procedure SetEventMethodToAllComponentsInForm( FormX: TForm );
end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
uses
  TypInfo;
procedure TForm1.SetEventMethodToAllComponentsInForm( FormX: TForm );
var
  PropInfo: PPropInfo;
  i: integer;
  CommonMethod: TMethod;
begin
  for i:=0 to FormX.ComponentCount-1 do
  begin
    PropInfo := GetPropInfo( FormX.Components[i].ClassInfo, 'OnMouseMove');
    if PropInfo <> nil then
    begin
      CommonMethod.Data := FormX.Components[i];
      CommonMethod.Code := Form1.MethodAddress('CommonMouseMove');
      SetMethodProp( FormX.Components[i], PropInfo, CommonMethod );
    end;
  end;
end;
procedure TForm1.CommonMouseMove (Sender: TObject;
Shift: TShiftState; X,Y: Integer);
begin
  ShowMessage('Delphi World is COOL!');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  //...передадим для пробы ссылку на эту же форму
  SetEventMethodToAllComponentsInForm( self );
end;

Это что касается методов. Свойства тоже не обделены такими возможностями. Для из изменения нужно использовать процедуру procedure SetOrdProp(Instance: TObject; PropInfo: PPropInfo; Value: Longint);

// procedure SetOrdProp(Instance: TObject;
// PropInfo: PPropInfo; Value: Longint);
// Например следующий код устанавливает
// свойство Color у всех компонентов формы,
// которые этим свойством располагают:
for i:=0 to FormX.ComponentCount-1 do
begin
  PropInfo := GetPropInfo( FormX.Components[i].ClassInfo, 'Color');
  if PropInfo <> nil then
    SetOrdProp( FormX.Components[i], PropInfo, clGreen );
end;

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

Жизнь и смерть в режиме run-time.

Оформил: DeeCo

Автор: Елена Филиппова

Лирическое вступление.

Конец ноября в этом году выдался довольно забавный, и погода в Москве была неестественная для этого времени года и респондентов Круглого стола как прорвало. Если раньше можно было выделить десяток основных животрепещущих вопросов, например как выдвинуть CD-Rom, поместить иконку в Tray и обязательно добавить в Caption формы непрямоугольную кнопку, то теперь появился новый и явный лидер. И в личной почте и в вопросах на Круглый стол, и даже по Аське, несмотря на отсутствие авторизации, я получила за неделю десяток практически дословно повторяющихся вопросов от совершенно разных и незнакомых мне людей. "Как создать кнопку во время работы? Как назначить ей обработчики событий? Почему нельзя написать (Edit + IntToStr(i)).Text:='' и как же тогда можно писать? Не перебирать же все Еdit'ы у меня их на форме 100 штук! Ну Дельфя и дает!.."И так далее с небольшими вариациями. Может курсовые задачи во всех ВУЗах одинаковые или это магнитные бури с солнечной активностью... А может "Дельфя" дает далеко не всем.

Итак, зачетной сессии посвящается...

Материал предназначен для начинающих программистов, умеющих работать с компонентами Delphi в режиме design-time но уже не считающих, что программировать это значит "накликать мышкой форму". Никаких интересных моментов для профи статья не содержит, это исключительно учебный материал. Цель статьи ответить на вопросы и показать : Каким образом можно создавать и располагать на форме визуальные компоненты? Каким образом обрабатывать группы компонент, меняя их свойства "одним махом"? Как назначать обработчики для разных событий? Зачем нужно свойство Tag? Как можно двигать компоненты по форме? И все это - во время работы программы (режим run-time).

Пояснения к проекту:

Скачать проект (откомпилировано в Delphi 5) : Исходники + Exe-файл RunTime.zip (200 K) Только исходники RunTime_Source.zip (8 K) Проект показывает сразу все перечисленные возможности работы с компонентами, он представляет собой форму , на которой есть панель (PanelTest : TPanel), управляющие компоненты и меню. Все наши компоненты, которые мы будем создавать, будут располагаться на панели PanelTest. Чтобы не путать тестовые компоненты с управляющими. Создавать будем TEdit , TButton , TCheckBox и TLabel по выбору. Управляющие элементы формы: rgComponents : TRadioGroup - выбор типа компонента ColorGrid : TColorGrid - выбор цвета фонта для меню "Изменить цвет" MainMenu : TMainMenu - действия с группами компонент pmComponent : TPopupMenu - изменения свойств конкретного компонента и его удаление. Некоторые элементы MainMenu и pmComponent будут достраиваться и изменять свои свойства в run-time. Создаем компонент. Это очень просто, главное - знать какой именно это компонент и где он будет лежать. Например, создадим TButton на нашей панели PanelTest.
var
  New: TButton;
begin
  // Создаем НОВЫЙ экземпляр класса TButton
  // и кладем ссылку на него в переменную New
  New := TButton.Create(PanelTest);
  // Координаты левого верхнего угла новой кнопки на панели
  New.Top := ...;
  New.Left := ...;
  New.Name := 'Button';
  // А вот эта процедура  будет вызываться
  // при нажатии на новенькую кнопку
  // то есть - обработка событи OnClick
  New.OnClick := OnClickButton;
  // Оп! И делаем кнопку видимой на PanelTest
  New.Parent := PanelTest;
end;
Комментарии к коду. Хотя код короткий и довольно простой, для "новичков" стоит дать некоторые пояснения. Определение переменной Var New : TButton НЕ СОЗДАЕТ новой кнопки, а только говорит о том, что в переменной New будет лежать ссылка на экземпляр класса TButton. Этот момент надо понять очень четко. Извиняюсь перед продвинутыми начинающими за прописные истины, но получаемые мной многочисленные письма заставляют обращать внимание на эти мелочи. Это довольно важные мелочи. Примечание: Советую прочесть статью Максима Игнатьева "Путешествуя по TObject. Или как оно работает." Итак, создаем новый экземпляр класса TButton, а попросту говоря, нашу кнопку:
New := TButton.Create(PanelTest);
Конструктор имеет входной параметр
TButton.Create(AOwner: TObject);
где указан Owner , то есть "хозяин", создаваемого объекта. Хозяин компонента отвечает за его корректное удаление и освобождение памяти. В качестве хозяина мы передаем ему панель PanelTest, это означает, что при удалении PanelTest будет удалена и наша кнопка. Если в качестве параметра указать nil, то и заботиться об удалении кнопки придется самим. Какие комментарии можно добавить к прозрачным командам New.Top:= ... ? Ну вот, например такие: если мы создаем компонент, для которого требуется задать не только координаты левого верхнего угла, но и его ширину и высоту, то вместо четырех присвоений используйте метод TControl.SetBounds . Назначение имени для динамических компонентов совершенно не обязательно, они прекрасно могут существовать и без имен. Только тогда невозможно будет найти их методом FindComponent, что, впрочем, не всегда необходимо. В нашем случае я назначаю имя всем компонентам исключительно от лени - присваивание New.Name:='Button' автоматически заполнит поле Text для TEdit и поле Caption для TLabel, TButton и TCheckBox и об этом можно не заботиться. Созданная кнопка почти готова к самостоятельно жизни, за одним маленьким исключением... ее пока не видно. Посмотрите повнимательнее Help на тему TControl.Parent, это то самое свойство, которое нам нужно. Parent - это визуальный родитель контрола. Присваивание New.Parent:=PanelTest помещает кнопку New в массив дочерних контролов для ранели PanelTest.
property Controls[Index: Integer]: TControl;
Теперь кнопка будет не просто видна на панели, а будет принадлежать этой панели. То есть при возможном перемещении PanelTest или изменении ее размеров , наша кнопка будет оставаться внутри PanelTest. Но и это еще не все. Если кнопка нам нужна не для украшения формы, то хотелось бы, чтобы она адекватно реагировала на некоторые события, ну хотя бы на свое собственное нажатие. Читаем help :
property OnClick: TNotifyEvent;
Это свойство определяет реакцию на событие, которое возникает при клике мышки или нажатии Enter на контрол. Лирическое отступление:Смотрим help по теме "Procedural types" , то есть "процедурные типы". Например тип :
type
  TMyProcedure = procedure(I: Integer);
это процедурный тип, который является ссылкой(указателем) на адрес процедуры с определенным списком параметров.
var
  MyProcedure: TMyProcedure;
procedure X(I: Integer);
begin
  ...
end;
...
  MyProcedure := X;
Переменная MyProcedure содержит адрес соответствущей процедуры. Таким образом процедуру можно передавать как параметр в другие процедуры и функции.
type
  TNotifyEvent = procedure(Sender: TObject) of object;
Тип TNotifyEvent это не просто указатель на процедуру, это ссылка на метод, об этом говорит директива "of object". Чем отличается ссылка на процедуру от ссылки на метод? На самом деле тип "ссылка на метод" реально содержит две ссылки - непосредственно адрес метода(процедуры) и ссылку на сам объект, которому этот метод принадлежит. Для того, чтобы назначить свой собственный обработчик события, мы должны создать процедуру с параметром Sender: TObject , назовем ее OnClickButton. Процедура эта будет незамысловатая - выдается сообщение о том, какая именно кнопка нажата. Для того, чтобы понять какая кнопка нажата, обратимся к параметру Sender, который передается нам в процедуру. Sender это тот компонент, который инициирует событие, то есть в нашем случае - нажатая кнопка.
procedure TForm1.OnClickButton(Sender: TObject);
var
  Value: string;
begin
  MessageDlg('Нажата кнопка ' + TControl(Sender).Name, mtInformation, [mbOk],
    0);
end;
И назначим эту процедуру в качестве обработчика события OnClick созданной кнопки:
New.OnClick := OnClickButton;

Теперь наша кнопка прям как настоящая!

Лень, как источник вдохновения. Итак, начнем. Компоненты будем создавать при двойном клике мышкой на панели PanelTest. В реальных примерах в run-time приходится добавлять на форму РАЗНЫЕ компоненты. В нашем примере будем создавать не только кнопки (TButton) , а еще и TLabel, TEdit и TCheckBox. Радиокнопки rgComponents как раз указывают, что именно мы собираемся создавать. Можно пойти самым простым путем, воспользуемся уже разобранным кодом, добавим CASE на все наши случаи и для каждого класса перепишем этот код, изменяя тип создаваемого компонента. Самый простой путь не всегда самый правильный. Во-первых, если Вам понадобится добавить еще один тип, придется еще дописать кусок кода. А во-вторых, лениво повторять один и тот же код сто раз. :о) То, что не красиво выглядит, с большой степенью вероятности, не совсем верно реализовано. Настоящий программист, человек довольно ленивый... Именно это заставляет его оптимизировать процесс разработки. Именно по этой причине появился первый компилятор :о) По этой же причине мы пойдем другим путем.

Ссылка на класс.

Хорошо бы передать Delphi тип компонента, экземпляр которого мы хотим создать. Тогда бы задача упростилась. Формируем массивчик типов, передаем очередной элемент массива и Delphi сама вызывает нужные методы именно того класса, который мы ей дали! А ведь мы можем это сделать : для этой цели существуют ссылки на класс - class references. Посмотрите help по этой теме, там довольно подробно объясняется это понятие и приводятся примеры. Для примера: class of TObject это ссылка на класс TObject.
type
  TClass = class of TObject;
Переменная типа TClass не может содержать экземпляр TObject, это только ссылка на определенный класс. Мы можем определить свой собственный тип, как ссылку на класс, который является родительским по отношению ко ВСЕМ нашим типам создаваемых компонент. Нам нужна ссылка на TControl. В Delphi есть уже "готовые" типы ссылок на классы. Кроме TClass есть еще несколько , в том числе и TControlClass.
type
  TControlClass = class of TControl;
Воспользуемся им. Нам понадобится список классов, с которыми будм работать :
type
  TListClass = array[0..3] of TControlClass;
const
  ListClass: TListClass = (TEdit, TButton, TCheckBox, TLabel);
На событие OnDblClick панели PanelTest разбираем, что именно мы должны создать и создаем это...
procedure TForm1.PanelTestDblClick(Sender: TObject);
const
  No: integer = 0;
var
  TypeClass: TControlClass;
  New: TControl;
  Point: TPoint;
begin
  if (rgComponents.ItemIndex >= 0) and
    (rgComponents.ItemIndex < rgComponents.Items.Count) then
  begin
    // Получаем ссылку на выбранный класс
    TypeClass := TControlClass(ListClass[rgComponents.ItemIndex]);
    Inc(No); // увеличиваем счетчик компонент
    // Создаем компонент - вызываем конструктор выбранного класса
    New := TypeClass.Create(PanelTest);
    Point := PanelTest.ScreenToClient(Mouse.CursorPos);
    New.Top := Point.y;
    New.Left := Point.x;
    // Имя = название класса + номер нового компонента
    New.Name := New.ClassName + IntToStr(No);
    New.Tag := 1;
    // Навешиваем меню по правой кнопке
    TEdit(New).PopupMenu := pmComponent;
    // Если это кнопка - назначим обработчик
    if TypeClass = TButton then
      TButton(New).OnClick := OnClickButton;
    // И помещаем новенького на панель
    New.Parent := PanelTest;
  end;
end;
Все! И никаких САSЕ'ов :о)

Комментарий:

Point := PanelTest.ScreenToClient(Mouse.CursorPos);
Привязываем положение нового компонента к той точке, в которой кликнули мышкой. Процедра ScreenToClient пересчитывает абсолютные (экранные) координаты глобального объекта Mouse в координаты относительно левого верхнего угла панели. Изменение свойств группы компонент. Вы обратили внимание на строку New.Tag:=1 ? Что это за свойство и в чем его смысл? Это целое число типа LongInt(длиной 4 байта), которое является полноценным свойством компонента и никак не учитывается средой. Delphi на него не обращает внимания. Можете там что-нибудь сохранить. На всякий случай. Итак, присвоим нашим новеньким Tag равный 1. Строка
TEdit(New).PopupMenu := pmComponent;
связывает наши компоненты с popup-меню pmComponent, которое теперь будет вызываться при нажатии правой кнопки на компоненте. Не смущайтесь приведением к типу TEdit, дело в том, что свойство PopupMenu у класса TControl определено как protected и может быть использовано только его потомками. Чтобы не перебирать все наши типы, выбираем один и используем его для проникновения к нужному нам свойству. Так как PopupMenu есть у всех наших классов и оно то самое, которое наследовано от TControl.PopupMenu , то присвоение будет верным. Меню pmComponent содержит три опции :
Tag = 1
Tag = 2
------
//Удалить компонент
Первая устанавливает свойство Tag равным 1, вторая меняет его на 2. И последняя опция удаляет компонент по нашему желанию. Обратите внимание на основное меню окна, а именно на его пункт "Измененить цвет" Будем изменять цвет шрифта наших компонентов по выбранному условию. Все три пункта меню "Измененить цвет" используют один и тот же обработчик:
procedure TForm1.AllColorClick(Sender: TObject);
var
  i: Integer;
begin
  if TControl(Sender).Tag = 0 then
    for i := 0 to PanelTest.ControlCount - 1 do
      TEdit(PanelTest.Controls[i]).Font.Color := ColorGrid.ForegroundColor
  else
    for i := 0 to PanelTest.ControlCount - 1 do
      if TWinControl(PanelTest.Controls[i]).Tag = TControl(Sender).Tag then
        TEdit(PanelTest.Controls[i]).Font.Color := ColorGrid.ForegroundColor
end;
Sender в этой процедуре - выбранный пункт меню. Обратите внимание в самом проекте, как расставлено свойство Tag у пунктов меню "Измененить цвет". Перебираем в цикле массив Controls панели PanelTest, он содержит наши компоненты, и изменяем цвет шрифта по условию - если значение Tag выбранного пункта меню совпадает со значением Tag очередного компонента, это наш клиент! Здесь мы изменяли свойство у всех компонентов, незавсимо от их типа. А в меню "Очистить" проводится почти такая же операция, но условием выступает принадлежность к определенному типу. Если мы хотим очистить TEdit (то есть очистить его свойство Text ), то перебор списка Controls будет выглядеть так:
for i := 0 to PanelTest.ControlCount - 1 do
begin
  if (PanelTest.Controls[i] is TEdit) then
    TEdit(PanelTest.Controls[i]).Text := ''
end;
В удалении компонента нет никаких сложностей. В нашем примере только надо понять, что именно удаляется. Воспользуемся свойством TPopupMenu.PopupComponent : TComponent , определяющим компонент, над которым в данный момент нажали правую кнопку мыши и вызвали это меню.
pmComponent.PopUpComponent.Free;

На рисунке показан момент работы проекта:

Статьи по Delphi (015)… Таинственное свойство Tag. Было бы обидно, если Delphi разрешала бы программистам хранить для своих нужд только целочисленный признак. А мало ли какие у кого нужды? Если внимательно прочесть Help, то в самой последней строке нам откроется истина : в свойстве Tag можно хранить что угодно длинной 4 байта ( any 32-bit value such as a component reference or a pointer ) ! Это может быть целое число , а может быть ссылка (pointer). Например, ссылка на компонент. Чувствуете, какие открываются перспективы?! Пункт "Отметить CheckBox" основного меню в design-time имеет только один пункт (полоску считать не будем). По смыслу задачи в этот пункт должны добавляться подпункты со списком созданных к этому моменту ТСhескВох'ов. При нажатии на нужный пункт, соответствующий TCheckBox на панели будет отмечаться (Checked:=True). Чудное(ударение на первый слог) свойство Tag поможет очень изящно решить эту задачу. Достраиваться меню будет в момент его вызова (событие OnClick), ибо зачем нам в другие моменты эта информация?
procedure TForm1.miCheckBoxClick(Sender: TObject);
var
  Item: TMenuItem;
  i: Integer;
begin
  // очищаем  список
  for i := TMenuItem(Sender).Count - 1 downto 2 do
    TMenuItem(Sender).Delete(i);
  // Формируем свежий вариант меню по текущему списку СhескВох'ов
  for i := 0 to PanelTest.ControlCount - 1 do
    if (PanelTest.Controls[i] is TCheckBox) then
    begin
      Item := NewItem(TCheckBox(PanelTest.Controls[i]).Caption, 0, False, True,
        miCheckedClick, 0, '');
      TMenuItem(Sender).Add(Item);
      Item.Tag := LongInt(PanelTest.Controls[i]);
    end;
end;
Не ищите функцию NewItem в проекте, ее там нет. Это функция из VCL модуля Menus. Посмотрите его, найдете еще много-много интересного. Вообще читайте исходники почаще. Это самый лучший учебный материал. Итак, для каждого TCheckBox на нашей панели создаем новый пункт в меню и... вот оно!
Item.Tag := LongInt(PanelTest.Controls[i]);
Приведение типов к LongInt необходимо для компилятора, иначе он не разрешит нам это присваивание. В Delphi класс сам по себе является ссылкой, то есть PanelTest.Controls[i] содержит не сам компонент, а его адрес. Свойству Tag присваем адрес соответствующего компонента. Собственно и все. Вы обратили внимание на то, что мы сразу при создании пункта меню привязали к нему обработчик на событие OnClick? Вот этот обработчик:
procedure TForm1.miCheckedClick(Sender: TObject);
var
  i: Integer;
begin
  //Функция Ptr конвертирует 4 байта в тип Pointer.
  //function Ptr(Address: Integer): Pointer
  if TMenuItem(Sender).Tag = 0 then
  begin
    for i := 0 to PanelTest.ControlCount - 1 do
      if PanelTest.Controls[i] is TCheckBox then
        TCheckBox(PanelTest.Controls[i]).Checked := True
  end
  else
    TCheckBox(Ptr(TMenuItem(Sender).Tag)).Checked := True;
end;
IF TMenuItem(Sender).Tag = 0 - проверка на пункт "Отметить все", здесь мы пользуемся старым способом перебора массива PanelTest.Controls. А вот если это пункт для единичного ТСhескВох'а ... используем свойство Tag иным манером :о) В принципе неплохо бы еще и проверять, что возвращает Ptr. Примерно так
var
  TagPtr: Ponter;
  ...
    TagPtr := Ptr(TMenuItem(Sender).Tag);
  if TagPtr <> nil then
    if TagPtr is TCheckBox then
      TCheckBox(TagPtr).Checked := True;
Это общий случай - защита от ошибочных ситуаций. В нашем примере в TMenuItem(Sender).Tag ничего иного быть и не может, так что проверять не обязательно... Хотя, с проверкой надежнее :о) Что бы еще такого придумать интересненького?.. А вот, например, давайте для каждой кнопки менять в run-time текст выводимого ею сообщения (помните нашу процедуру OnClickButton ? ). Где хранить этот текст для каждой кнопки? Создать массив или динамический список строк и синхронизировать его со списком компонент? Можно конечно... но зачем так сложно? Ведь в этом замечательном свойстве Tag можно хранить и строки. Изменим немного код создания компонент:
var
  ...
    MessTag: PChar;
  ...
  if TypeClass = TButton then
  begin
    TButton(New).OnClick := OnClickButton;
    // выделяем память под строку
    GetMem(MessTag, Length('Нажата кнопка №' + IntToStr(No)) + 1);
    StrCopy(MessTag, PChar('Нажата кнопка №' + IntToStr(No)));
    TButton(New).Tag := LongInt(MessTag);
  end
  else
    New.Tag := 1;
  ...
И процедура OnClickButton изменится соответствующим образом:
procedure TForm1.OnClickButton(Sender: TObject);
var
  Value: string;
begin
  Value := PChar(Ptr(TButton(Sender).Tag));
  MessageDlg(Value, mtInformation, [mbOk], 0);
end;
Изменение строки привяжем к меню pmComponent, добавив туда еще один пункт. Обратите внимание, что пункт этот имеет смысл только для кнопок. Поэтому перед показом popup-меню, на событие OnPopup, делаем соответствующий пункт видимым или невидимым.
procedure TForm1.pmComponentPopup(Sender: TObject);
begin
  pmTag1.Checked := TPopupMenu(Sender).PopupComponent.Tag = 1;
  pmTag2.Checked := not pmTag1.Checked;
  pmLine.Visible := TPopupMenu(Sender).PopupComponent is TButton;
  pmNewMessage.Visible := pmLine.Visible;
end;
Да! Перед тем, как записать адрес новой строки в Tag, освободим память от предыдущей, ведь она нам больше не нужна.
procedure TForm1.pmNewMessageClick(Sender: TObject);
var
  Value: string;
  MessTag: PChar;
begin
  MessTag := PChar(TButton(pmComponent.PopupComponent).Tag);
  Value := MessTag;
  if InputQuery('Сменить сообщение',
    'Для кнопки ' + TButton(pmComponent.PopupComponent).Name, Value) then
  begin
    FreeMem(MessTag);
    GetMem(MessTag, Length(Value) + 1);
    StrCopy(MessTag, PChar(Value));
    TButton(pmComponent.PopupComponent).Tag := LongInt(MessTag);
  end;
end;
При удалении компонента нам теперь надо учесть этот момент - если удаляется кнопка, надо удалить ее строку. Движение - это жизнь Напоследок, для полного оживления картины, сделаем наши компоненты подвижными. Разрешим им свободно перемещаться по панели, таская их мышкой. Это будет не drag & drop, а совершенно обычное перемещение, как в режиме design-time. Для его реализации нам понадобится обработать самим два события для передвигаемого компонента: OnMouseDown - если нажата левая кнопка мышки, запомним ее текущее положение в переменной DragPoint и и будем считать эти координаты точкой отсчета. OnMouseMove - если нажата левая кнопка мышки, передвигаем компонент по новым координатам, сдвигая его так же, как сдвинулась мышка относительно нашей точки отсчета. Тем самым компонент будет плавно передвигаться вслед за мышкой. Пишем эти процедуры сами, список параметров должен соответствовать описаниям этих событий ( для этого снова к Неlр'у ). В принципе можно посмотреть, как создает обработчики этих событий сама Delphi и оттуда переписать параметры.
procedure TForm1.ControlMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  DragPoint := Point(X, Y);
end;
procedure TForm1.ControlMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if (ssLeft in Shift) then
  begin
    TControl(Sender).Left := TControl(Sender).Left + x - DragPoint.X;
    TControl(Sender).Top := TControl(Sender).Top + y - DragPoint.y;
  end;
end;
Естественно, что при создании компонент необходимо добавить назначение этих процедур в качестве обработчиков нужных нам событий.
...
New.Top := Point.y;
New.Left := Point.x;
TLabel(New).OnMouseDown := ControlMouseDown;
TLabel(New).OnMouseMove := ControlMouseMove;
...
Ну, а теперь, мышку в руки и смело вперед!

Итого...

В статье я довольно подробно разобрала некоторые вопросы, некоторые затронула вскользь. Не дала прямого ответа на вопрос, почему нельзя писать "(Edit + IntToStr(i)).Text:=''". В самом проекте оставлено несколько забавных нюансов, от которых бы надо избавиться (например, при перетаскивании компонентов срабатывает событие OnClick, что делает кнопки особенно навязчивыми). Это не от лени, в этом была стратегическая задумка... Никакой Интернет со всеми его конференциями, статьями и примерами никогда не заменит программисту собственного опыта. Только то, что добыто (разобрано и понято) своими силами, запоминается надолго и приносит пользу. Опыта надо набираться обязательно. Читайте help, ищите ответ в исходниках, экспериментируйте с проектом сколько душе угодно, но только обязательно сами... Удачи! Скачать проект (откомпилировано в Delphi 5) : Исходники + Exe-файл RunTime.zip (200 K) Только исходники RunTime_Source.zip (8 K)

Менять главную форму во время выполнения.

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

procedure SetAsMainForm(aForm:TForm);
 var
   P:Pointer;
 begin
   P := @Application.Mainform;
   Pointer(P^) := aForm;
 end;
 {************************************}
 // Example of Usage: 
{ 
Question: 
  If my application is main.exe and the main form is form1, form1 displays 
  when it runs. I would like to display other forms based on the parameter 
  passed. 
  main.exe param1 will display form2 as the first form 
  main.exe param2 with display form3 as the first form 
Answer: 
}
 program Project1;
 uses
   Forms,
   Unit1 in 'Unit1.pas' {Form1},
   Unit2 in 'Unit2.pas' {Form2},
   Unit3 in 'Unit3.pas' {Form3};
 {$R *.res}
 procedure SetAsMainForm(aForm:TForm);
 var
   P:Pointer;
 begin
   P := @Application.Mainform;
   Pointer(P^) := aForm;
 end;
 begin
   Application.Initialize;
   Application.CreateForm(TForm1, Form1);
   Application.CreateForm(TForm2, Form2);
   Application.CreateForm(TForm3, Form3);
   if Paramstr(1) = 'Param1' then
     SetAsMainForm(Form2);
   if Paramstr(1) = 'Param2' then
     SetAsMainForm(Form3);
   Application.Run;
 end.

Имя свойства в течение выполнения программы.

Если бы строители строили здания так же, как программисты пишут программы, первый залетевший дятел разрушил бы цивилизацию.
If ( Sender is TLabel ) then.. 

Или, если вам заранее известен тип объекта, то можно так:

TLabel( Sender ).Caption 

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

formname:=(myform as tobject).name; 

Или.

formname:=(myform as tcontrol).name; 
Это должно работать. Причина того, что этого не хочет делать сама Delphi в том, что у нее есть определенные элементы управления, у которых нет специфического имени и, следовательно, компилятор должен проверять каждый вызов для проверки его валидности.

Если вы используете свойство ClassName подобно этому:

with Sender as TForm do
  Label1.Caption := copy(ClassName,2,length(ClassName)-1);
Это даст желаемый эффект без дополнительного кодирования в обработчике формы OnCreate. 'Sender' может и не быть формой, которую вы пытаетесь обработать, и ваша программа получит исключительную ситуацию, сообщающую о неверном приведении типа. Я не уверен в том, что содержание (если оно есть) Sender в действительности является самой формой.

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

If Sender is TForm then
  Label1.Caption := (Sender as TForm).Name ;

Это в случае, если вы пытаетесь выполнить следующее:

Label1.Caption := Form1.Name ;

Это целый сонм проблем. Я читал, что при исполнении этого кода или не доступно свойство Form (во время выполнения программы), или, что чаще, возникает заблуждение относительно инициализируемого класса (вернее, его имени, Form1). Если вы тщательно читали документацию по Delphi, то наверняка заметили тот факт, что установка свойства в Инспекторе Объектов (Object Inspector) НЕ означает его автоматическую установку во время выполнения программы. Чтобы с честью выбраться из этой ситуации, необходимо явно установить свойство (в нашем случае свойство .Name) в методе фомы .Create. Это может выглядеть приблизительно так:

procedure TForm1.Create( Sender : TObject ) ;
begin
  Form1.Name := 'Form1' ;
end ;
procedure TForm1.Button1Click( Sender : TObject ) ;
begin
  Label1.Caption := Form1.Name ;
end ;
var
  TC: TComponent;
begin
  TC := label1.Owner;
  label1.Caption := TC.ClassName;
end;

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

name := 'AName';

Затем, после щелчка на кнопке, я мог бы щелкнуть на форме, и заголовок метки изменился бы на 'AName'. Решением может служить определение свойства Name в обработчике события создания формы. Т.е., если вы назвали форму именем MyForm, то в обработчике события OnCreate вы должны написать следующее:

name := 'MyForm';

Это решит вашу проблему, а я закругляюсь.

Заменить элемент управления во время выполнения.

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

unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, ComCtrls, ExtCtrls;
 type
   TForm1 = class(TForm)
     Panel1: TPanel;
   private
     { Private-Deklarationen }
   public
     { Public-Deklarationen }
   end;
   (* Neue Komponente mit eigenen Eigenschaften ersetzt erst
      zur Programmlaufzeit das Verhalten von TPanel        *)
   (* The new component replace at runtime TPanel *)
   TExtPanel = class(TCustomControl)
   protected
     procedure WMSize(var Message: TWMSize); message WM_Size;
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TExtPanel.WMSize;
 begin
   Caption := 'Width: ' + IntToStr(Width) +
     ', Height: ' + IntToStr(Height);
 end;
 procedure ReplaceParentClass(DelphiClass,
   OldParent,
   NewParent: TClass);
 var
   aClassPointer: ^Byte;
   pVCL, pNew: ^Pointer;
   dwProtect: DWORD;
 begin
   if Assigned(NewParent) then
     if Assigned(DelphiClass) then
     begin
       // ClassParent-Zeiger suchen 
      //Search ClassParent Pointer 
      while (DelphiClass.ClassParent <> OldParent) do
       begin
         with DelphiClass do
         begin
           if (ClassParent = nil) or
             (ClassParent = NewParent) then Exit;
           // DelphiClass.ClassParent zuweisen 
          // Set DelphiClass.ClassParent 
          DelphiClass := ClassParent;
         end
       end;
       // Parent-Zeiger der originalen VCL-Klasse suchen 
      // Search Parent-Pointer of original VCL-Class 
      aClassPointer := Pointer(DelphiClass);
       Inc(aClassPointer, vmtParent);
       pVCL          := Pointer(aClassPointer);
       aClassPointer := Pointer(NewParent);
       Inc(aClassPointer, vmtSelfPtr);
       pNew := Pointer(aClassPointer);
       // Schreibberechtigung holen, Zeigerwerte tauschen 
      // Set write permission, swap pointers 
      VirtualProtect(pVCL, SizeOf(Pointer), PAGE_READWRITE, @dwProtect);
       pVCL^ := pNEW;
       // alte Zugriffsrechte aktivieren 
      // avtivate old access right 
      VirtualProtect(pVCL, SizeOf(Pointer), dwProtect, @dwProtect);
     end
   end;
 initialization
   ReplaceParentClass(TPanel, TCustomControl, TExtPanel);
 finalization
   ReplaceParentClass(TPanel, TExtPanel, TCustomControl);
 end.

Назначение события во время выполнения программы.

Автор: Mike Scott Вот процедура, назначающая событие компонента обработчику события другого (или того же самого) компонента, где во время выполнения программа само событие и его обработчик заданы в виде строки. В случае неверных имен события или его обработчика процедура возбуждает исключительную ситуацию. Вы можете "очищать" событие, передавая компоненту NIL с обработчиком или нулевое имя самого обработчика. Для демонстрации того, как это можно использовать, я включил в пример пару маленьких событий для кнопок. Во время работы, вы могли бы потестировать это с парой областей редактирования, имеющих другой тип и другие имена обработчиков.

Почуствуйте мощь RTTI (runtime type information)!

procedure SetEvent(ComponentWithEvent: TComponent;
  const Event: string;
  ComponentWithHandler: TComponent;
  const Handler: string);
var
  PropInfo: PPropInfo;
  Method: TMethod;
begin
  PropInfo := GetPropInfo(ComponentWithEvent.ClassInfo, Event);
  if PropInfo = nil then
    raise Exception.CreateFmt('Событие %s не найдено в классе %s',
      [Event, ComponentWithEvent.ClassName]);
  Method.Code := nil;
  if Assigned(ComponentWithHandler) and (Handler <> '') then
  begin
    Method.Code := ComponentWithHandler.MethodAddress(Handler);
    if Method.Code = nil then
      raise Exception.CreateFmt('Класс %s не имеет метода с именем %s',
        [ComponentWithHandler.ClassName,
        Handler]);
  end;
  Method.Data := ComponentWithHandler;
  SetMethodProp(ComponentWithEvent, PropInfo, Method);
end;
{ примеры, показывающие как использовать SetEvent }
procedure TForm1.SetBtnClick(Sender: TObject);
begin
  SetEvent(MenuItem, 'OnClick', Self, 'Test1Click');
end;
procedure TForm1.ClearBtnClick(Sender: TObject);
begin
  SetEvent(MenuItem, 'OnClick', nil, '');
end;

Пример реализации резиновой нити.

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, ComCtrls;
type
  TMainForm = class(TForm)
    ColorDlg: TColorDialog;
    StatusBar: TStatusBar;
    Timer: TTimer;
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure TimerTimer(Sender: TObject);
  private
    MouseRect: TRect;
    IsDown: Boolean;
    RectColor: TColor;
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    with MouseRect do
    begin
      IsDown := True;
      Left := X;
      Top := Y;
      Right := X;
      Bottom := Y;
      Canvas.Pen.Color := RectColor;
    end;
  if (Button = mbRight) and ColorDlg.Execute
    then RectColor := ColorDlg.Color;
end;
procedure TMainForm.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  IsDown := False;
  Canvas.Pen.Color := Color;
  with MouseRect do
    Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right, Bottom),
      Point(Left, Bottom), Point(Left, Top)]);
  with StatusBar do
  begin
    Panels[4].Text := '';
    Panels[5].Text := '';
  end;
end;
procedure TMainForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  with StatusBar do
  begin
    Panels[2].Text := 'X: ' + IntToStr(X);
    Panels[3].Text := 'Y: ' + IntToStr(Y);
  end;
  if not IsDown then Exit;
  Canvas.Pen.Color := Color;
  with mouserect do
  begin
    Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right, Bottom),
      Point(Left, Bottom), Point(Left, Top)]);
    Right := X;
    Bottom := Y;
    Canvas.Pen.Color := RectColor;
    Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right, Bottom),
      Point(Left, Bottom), Point(Left, Top)]);
  end;
  with StatusBar do
  begin
    Panels[4].Text := 'Ширина: ' + IntToStr(Abs(MouseRect.Right - MouseRect.Left));
    Panels[5].Text := 'Высота: ' + IntToStr(Abs(MouseRect.Bottom - MouseRect.Top));
  end;
end;
procedure TMainForm.TimerTimer(Sender: TObject);
begin
  with StatusBar do
  begin
    Panels[0].Text := 'Дата: ' + DateToStr(Now);
    Panels[1].Text := 'Время: ' + TimeToStr(Now);
  end;
end;
end.
Скачать весь проект

Использование функций из RunDll32.

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

Call functions from Rundll always like that: Aufruf von Funktionen aus Rundll stets nach dem Schema aufrufen: WinExec(PChar('rundll32 ...'),SW_SHOWNORMAL); where rundll32... should be replaced by one of the functions below. wobei rundll32... nаtьг1iсh durch eine der unten stehenden Aufrufe ersetzt werden muss. * Shutdown Windows: Windows beenden: Rundll32.exe user,ExitWindows * Install a Screensaver: Installation eines Bildschirmschoners: rundll32.exe desk.cpl,InstallScreenSaver c:\windows\system\ssaver.scr * Call the Standard newsreader: Standard Newsreader aufrufen: rundll32.exe url.dll,NewsProtocolHandler borland.public.delphi.objectpascal * Call the Standard Mailprogram: Standard Mailprogramm aufrufen: rundll32.exe url.dll,MailToProtocolHandler info@swissdelphicenter.ch * Make a Telnet Connection: Eine Telnetverbindung aufbauen: rundll32.exe url.dll,TelnetProtocolHandler 172.134.77.119 * Dialog: Copy Disk: Dialog: Diskette kopieren aufrufen: rundll32.exe diskcopy.dll,DiskCopyRunDll * Dialog: Format Disk: Dialog: Diskette formatieren: rundll32.exe shell32.dll,SHFormatDrive * Call a Connection with a configured Connection: Verbinden mit Dialog einer konfigurierten Verbindung aufrufen: rundll32.exe rnaui.dll,RnaDial Internetzugang * Call Assistants of a Ras-Connection without previous Explanation: Assistenten fьг eine DFЬ-Verbindung ohne vorherige Егк1дгиng aufrufen: rundll32.exe rnaui.dll,RnaWizard @1 * The same with Explanations: Das selbe mit Егк1дгиng: rundll32.exe rnaui.dll,RnaWizard * Print a Testpage on a printer: Eine Testseite auf dem Drucker ausdrucken: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL PrintTestPage * Call Assistants to install a printer: Assistenten zur Installation eines Druckers aufrufen: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter * Call Add/Remove Software: Eigenschaften Software Нinzиfьgеn/Еntfеrnеn aufrufen: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl * Show Properties for Modems: Eigenschaften fьг Modems anzeigen: rundll32.exe shell32.dll,Control_RunDLL modem.cpl * Call Assistants to configure a Modem: Assistent zum Einrichten eines neuen Modems aufrufen: rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add * Show the Network configuration: Netzwerkkonfiguration anzeigen: rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl * Assistant to install hardware Assistenten zur Hardwareinstallation aufrufen: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1 * Properties for the Display: Eigenschften von Anzeige aufrufen: -Background -Hintergrund: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0 -Screensaver -Bildschirmschoner rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1 -Display -Darstellung rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2 -Settings -Einstellungen rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3 * Open a file with its associated application : eine Datei mit assoziierter Anwendung aufrufen: rundll32.exe url.dll,FileProtocolHandler mydocument.doc

Количество запущенных копий программы.

Землетрясение в Штатах сорвало выступление Билла Гейтса. Наконец-то старый спор всех пользователей компьютеров: "кто главнее и круче - Билл или Бог?" полностью разрешен в пользу последнего. Билли посрамлен.
program CrtApp;
uses
  SysUtils, WinTypes, WinProcs, WinCrt;
var
  NumInstances, SavePrevInst: word;
procedure GetInstanceData(hInst, Offset, Size: Word); far; external 'KERNEL';
begin
  SavePrevInst := hPrevInst;
  NumInstances := 0;
  while hPrevInst <> 0 do
  begin
    GetInstanceData(hPrevInst, Ofs(hPrevInst), SizeOf(hPrevInst));
    Inc(NumInstances);
  end;
  Writeln('Уже запущено ', NumInstances, ' копий программы');
  hPrevInst := SavePrevInst;
end.

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

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

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage((Sender as TButton).Caption);
end;

А основное действие свершится по созданию окна:

procedure TForm1.FormCreate(Sender: TObject);
var
  Button1: TButton;
  i: integer;
begin
  for i:=0 to 4 do
  begin
    Button1:=TButton.Create(Form1);
    with Button1 do
    begin
      Parent := Form1;
      Caption := 'Кнопка ' + IntToStr(i + 1);
      Height := 25;
      Width := 75;
      Top := i * 25 + 25;
      Left := 50;
      OnClick := Button1Click;
    end;
  end;
end;

Как перемещать компоненты во время работы программы.

Фидошник едет в троллейбусе и орет: - Сакс, сакс!!!! Бабулька рядом говорит ему: - Hy что все сакс, да сакс... Ты еще какие-нибудь слова знаешь? - Да, Windows... - А что это? - САКС!!!!!
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1MouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
    procedure Button1MouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Integer);
    procedure Button1MouseUp(Sender: TObject; Button:
    TMouseButton; Shift: TShiftState; X, Y: Integer);
  private
    {Private declarations}
  public
    {Public declarations}
    MouseDownPoint : TPoint;
    Moving : bool;
end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
  if ssCtrl in Shift then
  begin
    SetCapture(Button1.Handle);
    Moving := true;
    MouseDownPoint.X := x;
    MouseDownPoint.Y := Y;
  end;
end;
procedure TForm1.Button1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if Moving then
  begin
    Button1.Left := Button1.Left - (MouseDownPoint.x - x);
    Button1.Top := Button1.Top - (MouseDownPoint.y - y);
  end;
end;
procedure TForm1.Button1MouseUp(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Moving then
  begin
    ReleaseCapture;
    Moving := false;
    Button1.Left := Button1.Left - (MouseDownPoint.x - x);
    Button1.Top := Button1.Top - (MouseDownPoint.y - y);
  end;
end;

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

Автор: Thomas von Stetten
Новость: фирма MICROSOFT, для увеличения надёжности работы своего сервера, установила на него операционную систему UNIX.

Я написал компонент-отладчик, выводящий в дереве все компоненты. Попробуйте этот код. Вызывайте функцию DisplayProperties как показано ниже:

DisplayProperties(Form1,                {Вы можете использовать любой компонент}
                  Outline1.Lines,       {Допускается любой TStrings-объект}
                  0);                   {0 - "стартовый", корневой уровень}
DisplayProperties(AObj: TObject; AList: TStrings; iIndentLevel: Integer);
var
  Indent: string;
  ATypeInfo: PTypeInfo;
  ATypeData: PTypeData;
  APropTypeData: PTypeData;
  APropInfo: PPropInfo;
  APropList: PPropList;
  iProp: Integer;
  iCnt: Integer;
  iCntProperties: SmallInt;
  ASecondObj: TObject;
procedure AddLine(sLine: string);
begin
  AList.Add(Indent + #160 + IntToStr(iProp) + ': ' + APropInfo^.Name
    + ' (' + APropInfo^.PropType^.Name + ')' + sLine);
end;
begin
  try
    Indent := GetIndentSpace(iIndentLevel);
    ATypeInfo := AObj.ClassInfo;
    ATypeData := GetTypeData(ATypeInfo);
    iCntProperties := ATypeData^.PropCount;
    GetMem(APropList, SizeOf(TPropInfo) * iCntProperties);
    GetPropInfos(ATypeInfo, APropList);
    for iProp := 0 to ATypeData^.PropCount - 1 do
    begin
      APropInfo := APropList^[iProp];
      case APropInfo^.PropType^.Kind of
        tkInteger:
          AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)));
        tkChar:
          AddLine(' := ' + chr(GetOrdProp(AObj, APropInfo)));
        tkEnumeration:
          begin
            APropTypeData := GetTypeData(APropInfo^.PropType);
            if APropTypeData^.BaseType^.Name <> APropInfo^.PropType^.Name then
              AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)))
            else
              AddLine(' := ' + APropTypeData^.NameList);
          end;
        tkFloat:
          AddLine(' := ' + FloatToStr(GetFloatProp(AObj, APropInfo)));
        tkString:
          AddLine(' := "' + GetStrProp(AObj, APropInfo) + '"');
        tkSet:
          begin
            AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)));
          end;
        tkClass:
          begin
            ASecondObj := TObject(GetOrdProp(AObj, APropInfo));
            if ASecondObj = nil then
              AddLine(' := NIL')
            else
            begin
              AddLine('');
              DisplayProperties(ASecondObj, AList, iIndentLevel + 1);
            end;
          end;
        tkMethod:
          begin
            AddLine('');
          end;
      else
        AddLine(' := >>НЕИЗВЕСТНО<<');
      end;
    end;
  except {Выводим исключение и продолжаем дальше}
    on e: Exception do
      ShowMessage(e.Message);
  end;
  FreeMem(APropList, SizeOf(TPropInfo) * iCntProperties);
end;
function GetIndentSpace(iIndentLevel: Integer): string;
var
  iCnt: Integer;
begin
  Result := '';
  for iCnt := 0 to iIndentLevel - 1 do
    Result := Result + #9;
end;

Запуск программы.

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

{ Open a file or starts a programm (without parameters) }
 procedure OpenFile(FileName: string);
 var
   c: array[0..800] of Char;
 begin
   StrPCopy(c, FileName);
   ShellExecute(Application.Handle, 'open', c, nil, nil, SW_NORMAL);
 end;
 { Starts a programm with commandline parameters }
 procedure OpenProgram(prog, params: string);
 var
   c, p: array[0..800] of Char;
 begin
   StrPCopy(c, prog);
   StrPCopy(p, params);
   ShellExecute(Application.Handle, 'open', c, p, nil, SW_NORMAL);
 end;
 { Starts a program and wait until its terminated: 
  WindowState is of the SW_xxx constants }
 function ExecAndWait(const FileName, Params: string;
   WindowState: Word): Boolean;
 var
   SUInfo: TStartupInfo;
   ProcInfo: TProcessInformation;
   CmdLine: string;
 begin
   { Enclose filename in quotes to take care of 
    long filenames with spaces. }
   CmdLine := '"' + FileName + '"' + Params;
   FillChar(SUInfo, SizeOf(SUInfo), #0);
   with SUInfo do
   begin
     cb := SizeOf(SUInfo);
     dwFlags := STARTF_USESHOWWINDOW;
     wShowWindow := WindowState;
   end;
   Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
     CREATE_NEW_CONSOLE or
     NORMAL_PRIORITY_CLASS, nil,
     PChar(ExtractFilePath(FileName)),
     SUInfo, ProcInfo);
   { Wait for it to finish. }
   if Result then
     WaitForSingleObject(ProcInfo.hProcess, INFINITE);
 end;
 { Execute a complete shell command line and waits until terminated. }
 function ExecCmdLineAndWait(const CmdLine: string;
   WindowState: Word): Boolean;
 var
   SUInfo: TStartupInfo;
   ProcInfo: TProcessInformation;
 begin
   { Enclose filename in quotes to take care of 
    long filenames with spaces. }
   FillChar(SUInfo, SizeOf(SUInfo), #0);
   with SUInfo do
   begin
     cb := SizeOf(SUInfo);
     dwFlags := STARTF_USESHOWWINDOW;
     wShowWindow := WindowState;
   end;
   Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
     CREATE_NEW_CONSOLE or
     NORMAL_PRIORITY_CLASS, nil,
     nil {PChar(ExtractFilePath(Filename))},
     SUInfo, ProcInfo);
   { Wait for it to finish. }
   if Result then
     WaitForSingleObject(ProcInfo.hProcess, INFINITE);
 end;
 { Execute a complete shell command line without waiting. }
 function OpenCmdLine(const CmdLine: string;
   WindowState: Word): Boolean;
 var
   SUInfo: TStartupInfo;
   ProcInfo: TProcessInformation;
 begin
   { Enclose filename in quotes to take care of 
    long filenames with spaces. }
   FillChar(SUInfo, SizeOf(SUInfo), #0);
   with SUInfo do
   begin
     cb := SizeOf(SUInfo);
     dwFlags := STARTF_USESHOWWINDOW;
     wShowWindow := WindowState;
   end;
   Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
     CREATE_NEW_CONSOLE or
     NORMAL_PRIORITY_CLASS, nil,
     nil {PChar(ExtractFilePath(Filename))},
     SUInfo, ProcInfo);
 end;

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

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

Запустить аплет Панели управления можно вызвав функцию WinExec, для выполнения файла control.exe, которому передано имя аплета. Обычно аплеты панели управления расположены в каталоге System Windows и имеют расширение .cpl.

procedure TForm1.Button1Click(Sender: TObject);
begin
  WinExec('C:\WINDOWS\CONTROL.EXE TIMEDATE.CPL', sw_ShowNormal);
end;

Запускать можете следующие апплеты:

Desk.cpl - свойства экрана Inetcpl.cpl - свойства Internet Intl.cpl - свойства "Язык и Стандарты" Joy.cpl - игровые устройства Mmsys.cpl - свойства мультимедиа Modem.cpl - свойства модемы Netcpl.cpl - сеть Odbccp32.cpl - ODBC Data Source Administrator Password.cpl - свойства пароли Powercfg.cpl - свойства "Управление электропитанием" Access.cpl - свойства "Специальные возможности" Sticpl.cpl - свойства "Сканеры м камеры" Sysdm.cpl - свойства системы Telephon.cpl - параметры набора номера Appwiz.cpl - установка и удаление программ Main.cpl - мышь Timedate.cpl - свойства "Дата и время" dtccfg.cpl - настройка клиента MS DTC Mlcfg32.cpl - свойства Microsoft Outlook Findfast.cpl - Microsoft FrontPage bdeadmin.cpl - BDE Administrator ibmgr.cpl - Interbase manager

Открыть меню кнопки ПУСК или запустить диалог запуска программ.

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

// Show the Startmenu: 
keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), 0, 0);
 keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), KEYEVENTF_KEYUP, 0)
 //Show the Run-Dialog: 
keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), 0, 0);
 keybd_event(Ord('R'), MapVirtualKey(Ord('R'), 0), 0, 0);
 keybd_event(Ord('R'), MapVirtualKey(Ord('R'), 0), KEYEVENTF_KEYUP, 0);
 keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), KEYEVENTF_KEYUP, 0)

Как запускать внешнюю программу сразу с высоким приоритетом.

Автор: Shadow Предлагаю Вашему вниманию пример, который изменяет приоритет приложения. Изменение приоритета следует использовать с осторожностью, так как присвоение слишком высокого приоритета может привести к медленной работе остальных программ и системы в целом. См. Win32 help for SetThreadPriority() function.

Пример:

procedure TForm1.Button1Click(Sender: TObject);
var
  ProcessID: DWORD;
  ProcessHandle: THandle;
  ThreadHandle: THandle;
begin
  ProcessID := GetCurrentProcessID;
  ProcessHandle := OpenProcess(PROCESS_SET_INFORMATION, false, ProcessID);
  SetPriorityClass(ProcessHandle, REALTIME_PRIORITY_CLASS);
  ThreadHandle := GetCurrentThread;
  SetThreadPriority(ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
end;

Запустить консольное приложение и получить его стандартный вывод.

- Что делать, если мышь уже дошла до края коврика, а курсор еще не дошел до края экрана? Возможные ответы: - перестать пользоватся мышью - выбросить такой маленький коврик - купить новый "экран" - осторожно придерживая большим пальцем курсор мыши, двигать монитор - прогнать драйвер мыши под отладчиком - Windows СУКС, этот Билли даже мышь нормальную не может сделать, Linux forever! - запустить антивирус! - да у тебя просто руки кривые! - сколько раз можно задавать один и тот же вопрос, предлагаю внести его в FAQ - Е...! про мышей - это не к нам
{ 
This function runs a program (console or batch) and adds its output 
to Memo1 
} 
{....} 
  private 
    function RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean; 
     
{....} 
function TForm1.RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean; 
var 
  start: TStartupInfo; 
  procInfo: TProcessInformation; 
  tmpName: string; 
  tmp: Windows.THandle; 
  tmpSec: TSecurityAttributes; 
  res: TStringList; 
  return: Cardinal; 
begin 
  Result := False; 
  try 
    { Setze ein Temporares File } 
    { Set a temporary file } 
    tmpName := 'Test.tmp'; 
    FillChar(tmpSec, SizeOf(tmpSec), #0); 
    tmpSec.nLength := SizeOf(tmpSec); 
    tmpSec.bInheritHandle := True; 
    tmp := Windows.CreateFile(PChar(tmpName), 
           Generic_Write, File_Share_Write, 
           @tmpSec, Create_Always, File_Attribute_Normal, 0); 
    try 
      FillChar(start, SizeOf(start), #0); 
      start.cb          := SizeOf(start); 
      start.hStdOutput  := tmp; 
      start.dwFlags     := StartF_UseStdHandles or StartF_UseShowWindow; 
      start.wShowWindow := SW_Minimize; 
      { Starte das Programm } 
      { Start the program } 
      if CreateProcess(nil, PChar(_exeName + ' ' + _cmdLine), nil, nil, True, 
                       0, nil, PChar(_dirName), start, procInfo) then 
      begin 
        SetPriorityClass(procInfo.hProcess, Idle_Priority_Class); 
        WaitForSingleObject(procInfo.hProcess, Infinite); 
        GetExitCodeProcess(procInfo.hProcess, return); 
        Result := (return = 0); 
        CloseHandle(procInfo.hThread); 
        CloseHandle(procInfo.hProcess); 
        Windows.CloseHandle(tmp); 
        { Die Ausgaben hinzufugen } 
        { Add the output } 
        res := TStringList.Create; 
        try 
          res.LoadFromFile(tmpName); 
          Memo1.Lines.AddStrings(res); 
        finally 
          res.Free; 
        end; 
        Windows.DeleteFile(PChar(tmpName)); 
      end 
      else 
      begin 
        Application.MessageBox(PChar(SysErrorMessage(GetLastError())), 
          'RunCaptured Error', MB_OK); 
      end; 
    except 
      Windows.CloseHandle(tmp); 
      Windows.DeleteFile(PChar(tmpName)); 
      raise; 
    end; 
  finally 
  end; 
end; 
// Example: 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  RunCaptured('C:\', 'cmd.exe', '/c dir'); 
end;

Как запустить текущий ScreenSaver.

Статьи по Delphi (015)…
В зоопарке ребенок, возбужденно тыча пальцем на клетку с приматами (обезьянами :), кричит: - Мама ! Мама ! Смотри - программисты ! - Почему ты так решил ? - Они как папа ! - не мытые, лохматые и мозоль на попе !!!
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_SCREENSAVE, 0); 

Как запустить текущий ScreenSaver 2.

О, Гюльчатай, сними же свой скринсейвер!

Сначала мы проверяем, установлен ли Screen Saver, если нет - возвращаемся с отрицательным ответом, в противном случае - запускаем его и возвращаем true.

function RunScreenSaver: bool;
var
  b: boolean;
begin
  result := false;
  if SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, @b, 0) <> true then
    exit;
  if not b then
    exit;
  PostMessage(GetDesktopWindow, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
  result := true;
end;

Показать диалог для запуска приложения.

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

 uses
   ComObj;
 procedure TForm1.FormCreate(Sender: TObject);
 var
   ShellApplication: Variant;
 begin
   ShellApplication := CreateOleObject('Shell.Application');
   ShellApplication.FileRun;
 end;

Показать диалог для запуска приложения 2.

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

 { This code uses the undocumented RunFileDlg function to show the "run" dialog }
 { Dieser Code verwendet die undokumentierte RunFileDlg Funktion, um den Ausfuhren Dialog anzuzeigen }
 // For Win NT 
procedure RunFileDlgW(OwnerWnd: HWND; Icon: HICON; lpstrDirectory: PWideChar;
   lpstrTitle: PWideChar; lpstrDescription: PWideChar; Flags: Longint); stdcall;
   external 'Shell32.dll' Index 61;
 // For Win 9x (Win NT to show standard captions ) 
procedure RunFileDlg(OwnerWnd: HWND; Icon: HICON; lpstrDirectory: PChar;
   lpstrTitle: PChar; lpstrDescription: PChar; Flags: Longint); stdcall;
   external 'Shell32.dll' Index 61;
 const
   RFF_NOBROWSE = 1; //Removes the browse button. 
  RFF_NODEFAULT = 2; // No default item selected. 
  RFF_CALCDIRECTORY = 4; // Calculates the working directory from the file name. 
  RFF_NOLABEL = 8; // Removes the edit box label. 
  RFF_NOSEPARATEMEM = 14; // Removes the Separate Memory Space check box (Windows NT only). 
function ShowRunFileDialg(OwnerWnd: HWND; InitialDir, Title, Description: PChar;
   flags: Integer; StandardCaptions: Boolean): Boolean;
 var
   HideBrowseButton: Boolean;
   TitleWideChar, InitialDirWideChar, DescriptionWideChar: PWideChar;
   Size: Integer;
 begin
   if (Win32Platform = VER_PLATFORM_WIN32_NT) and not StandardCaptions then
   begin
     Size := SizeOf(WideChar) * MAX_PATH;
     InitialDirWideChar := nil;
     TitleWideChar := nil;
     DescriptionWideChar := nil;
     GetMem(InitialDirWideChar, Size);
     GetMem(TitleWideChar, Size);
     GetMem(DescriptionWideChar, Size);
     StringToWideChar(InitialDir, InitialDirWideChar, MAX_PATH);
     StringToWideChar(Title, TitleWideChar, MAX_PATH);
     StringToWideChar(Description, DescriptionWideChar, MAX_PATH);
     try
       RunFileDlgW(OwnerWnd, 0, InitialDirWideChar, TitleWideChar, DescriptionWideChar, Flags);
     finally
       FreeMem(InitialDirWideChar);
       FreeMem(TitleWideChar);
       FreeMem(DescriptionWideChar);
     end;
   end else
     RunFileDlg(OwnerWnd, 0, PChar(InitialDir), PChar(Title), PChar(Description), Flags);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowRunFileDialg(FindWindow('Shell_TrayWnd', nil), nil, nil, nil, RFF_NOBROWSE, True);
 end;

Как написать DLL, которую можно было-бы выполнить с помощью RunDll, RunDll32.

Автор: Nomadic Вы должны определить в программе вызываемую снаружи функцию. Функция должна быть __stdcall (или WINAPI, что то же самое ;)) и иметь четыре аргумента. Первый - HWND окна, порождаемого rundll32 (можно использовать в качестве оwnеr'а своих dialog bох'ов), второй - HINSTANCE задачи, третий - остаток командной строки (LPCSTR, даже под NT), четвертый - не знаю ;). Например -
int __stdcall __declspec(dllexport) Test
(
HWND hWnd,
HINSTANCE hInstance,
LPCSTR lpCmdLine,
DWORD dummy
)
{
MessageBox(hWnd, lpCmdLine, "Command Line", MB_OK);
return 0;
}
Исполняем таким образом -
rundll32 test.dll,_Test@16 this is a command line
выдаст message box со строкой "this is a command line".

На Паскале -

function Test(
  hWnd: Integer;
  hInstance: Integer;
  lpCmdLine: PChar;
  dummy: Longint
  ): Integer; stdcall; export;
begin
  Windows.MessageBox(hWnd, lpCmdLine, 'Command Line', MB_OK);
  Result := 0;
end;
Давненько я ждал эту информацию! Сел проверять и наткнулся на очень забавную вещь. А именно -- пусть у нас есть исходник на Си примерно такого вида:
int WINAPI RunDll( HWND hWnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, DWORD dummy );
......
int WINAPI RunDllW( HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, DWORD dummy );
......
и .def-файл примерно такого вида:
EXPORTS
RunDll
RunDllA=RunDll
RunDllW

То rundll32 становится разборчивой -- под NT вызывает UNICODE-версию. Под 95, разумеется, ANSI.

Запустить на выполнение файл от имени любого пользователя.

Автор: Диюк Анрей.

unit HSAdvApi;
interface
uses
  Windows;
procedure MyCreateProcess(ConstCommandLine: string);
function CreateProcessWithLogonW(const lpUsername: PWideChar;
  const lpDomain: PWideChar; const lpPassword: PWideChar;
  dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD;
  lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
  lpStartupInfo: PStartupInfo;
  lpProcessInfo: PProcessInformation): Boolean; stdcall;
const
  LOGON_WITH_PROFILE = $00000001;
  LOGON_NETCREDENTIALS_ONLY = $00000002;
  LOGON_ZERO_PASSWORD_BUFFER = $80000000;
implementation
uses
  SysUtils;
{$WARN SYMBOL_DEPRECATED OFF}
{ ADVAPI32.DLL functions }
type
  TCreateProcessWithLogonW =
    function(const lpUsername: PWideChar;
    const lpDomain: PWideChar; const lpPassword: PWideChar;
    dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
    lpCommandLine: PWideChar; dwCreationFlags: DWORD;
    lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
    lpStartupInfo: PStartupInfo;
    lpProcessInfo: PProcessInformation): Boolean; stdcall;
const
  DllName = 'advapi32.dll';
var
  DllHandle: THandle;
  _CreateProcessWithLogonW: TCreateProcessWithLogonW;
function InitLib: Boolean;
begin
  if DllHandle = 0 then
    if Win32Platform = VER_PLATFORM_WIN32_NT then
    begin
      DllHandle := LoadLibrary(DllName);
      if DllHandle <> 0 then
      begin
        @_CreateProcessWithLogonW := GetProcAddress(DllHandle,
          'CreateProcessWithLogonW');
      end;
    end;
  Result := (DllHandle <> 0);
end;
function NotImplementedBool: Boolean;
begin
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  Result := false;
end;
function CreateProcessWithLogonW(const lpUsername: PWideChar;
  const lpDomain: PWideChar; const lpPassword: PWideChar;
  dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD;
  lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
  lpStartupInfo: PStartupInfo;
  lpProcessInfo: PProcessInformation): Boolean; stdcall;
begin
  if InitLib and Assigned(_CreateProcessWithLogonW) then
    Result := _CreateProcessWithLogonW(lpUsername, lpDomain, lpPassword,
      dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags,
      lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInfo)
  else
    Result := NotImplementedBool;
end;
procedure MyCreateProcess(ConstCommandLine: string);
const
  UserName: WideString = 'ADMIN';
  Password: WideString = 'creyc';
  //ConstCommandLine : String = 'MONIC_S.EXE ';
  Title: WideString = 'SERVISE';
  Domain: WideString = 'SOKAL';
var
  MyStartupInfo: STARTUPINFO;
  ProcessInfo: PROCESS_INFORMATION;
  CommandLine: array[0..512] of WideChar;
begin
  FillChar(MyStartupInfo, SizeOf(MyStartupInfo), 0);
  MyStartupInfo.cb := SizeOf(MyStartupInfo);
  StringToWideChar(ConstCommandLine, CommandLine,
    Sizeof(CommandLine) div SizeOf(WideChar));
  MyStartupInfo.lpTitle := PWideChar(Title);
  if not CreateProcessWithLogonW(PWideChar(UserName), PWideChar(Domain),
    PWideChar(Password), LOGON_WITH_PROFILE, nil,
    CommandLine, 0, nil, nil, @MyStartupInfo, @ProcessInfo) then
    RaiseLastWin32Error()
  else
  begin
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end;
end;
initialization
finalization
  if DllHandle <> 0 then
    FreeLibrary(DllHandle);
end.

Запустить процесс в защищенной области другого пользователя.

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

uses
   JwaWinBase; ( http://members.chello.nl/m.vanbrakel2/ ) 
//... 
procedure TForm1.Button1Click(Sender: TObject);
 var
    si: STARTUPINFOW;
    pif: PROCESS_INFORMATION;
    res: Bool;
   s: string;
 begin
   //erstmal die StartUpInfoW setzen 
  //set StartUpInfoW first 
  si.cb := SizeOf(startupinfow);
   si.dwFlags  := STARTF_USESHOWWINDOW;
   si.wShowWindow := SW_SHOWDEFAULT;
   si.lpReserved := nil;
   si.lpDesktop := nil;
   si.lpTitle := 'Konsole';
   // dann CreateProcessWithLogonW аиsfьhгеn... 
  // run CreateProcessWithLogonW... 
  res := CreateProcessWithLogonW('Security', 'ArViCor', 'test', LOGON_WITH_PROFILE,
     'c:\win2kas\system32\regedt32.exe', nil
     , CREATE_DEFAULT_ERROR_MODE, nil, nil, si, pif);
   if booltostr(res) = '0' then
    begin
     //wenn ein Fehler auftritt, soll der Fehlercode ausgegeben werden 
    //ььег 'net helpmsg ' in der Kommandoeingabeaufforderung 
    //kann dieser entziffert werden 
    //if an error occures, show the error-code 
    //this code can be 'translated' with 'net helpmsg ' on command-prompt 
    str(GetLastError, s);
     ShowMessage('CreateProcessWithLogonResult: ' + booltostr(res) + #10 +
       'GetLastError: ' + s);
   end;
 end;
 // Verbesserungen gerne als Mail an mich! 

Как можно запустить lnk.

Автор: Олег Кулабухов
Заходит старушка в трамвай, предупреждая всех что у нее в сумке яйца. Проходя мимо молодого человека, задевает его маленький мешочек. ОН: - Ах..., острожнее, тв@ю мать. ОНА, слегка ударяя по мешку ножкой: - А что у тебя там? ОН, ударяя ногой по ее сумке со всей дури и разбивая к чертям собачьим все яйца. - ДУРА! Там же ВИНЧЕСТЕР!
uses ShellApi;
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(0, nil, 'C:\WINDOWS\START MENU\DELPHI\Delphi3.lnk',
    nil, nil, SW_SHOWNORMAL);
end;

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

Автор: Vit WEB-сайт: http://forum.vingrad.ru

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

program Project1;
uses
  Forms, classes, windows, Sysutils, ShellApi,
  Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
var
  f: textFile;
  FileName: string;
begin
  if paramstr(1) <> '/runasis' then
  begin
    CopyFile(PChar(Paramstr(0)), PChar('c:\' + extractFilename(paramstr(0))),
      True);
    shellexecute(0, 'Open', PChar(extractFilename(paramstr(0))), '/runasis',
      'c:\', sw_restore);
    FileName := changefileext(paramstr(0), '.bat');
    assignFile(f, FileName);
    rewrite(f);
    writeln(f, ':1');
    writeln(f, format('Erase "%s"', [paramstr(0)]));
    writeln(f, format('If exist "%s" Goto 1', [paramstr(0)]));
    writeln(f, format('Erase "%s"', [FileName]));
    closefile(f);
    ShellExecute(0, 'Open', PChar(FileName), nil, nil, sw_hide);
  end
  else
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
end.

Как запустить и остановить сервис (или получить его статус).

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

- Почему программисты не заливают в машину 95-й бензин? - Боятся, что повиснет.

Здесь представлены две функции ServiceStart и ServiceStop, которые показывают, как пользоваться API функциями OpenSCManager, OpenService и т.д.:

function ServiceStart(aMachine, aServiceName: string ): boolean;
// aMachine это UNC путь, либо локальный компьютер если пусто
var
  h_manager,h_svc: SC_Handle;
  svc_status: TServiceStatus;
  Temp: PChar;
  dwCheckPoint: DWord;
begin
  svc_status.dwCurrentState := 1;
  h_manager := OpenSCManager(PChar(aMachine), nil, SC_MANAGER_CONNECT);
  if h_manager > 0 then
  begin
    h_svc := OpenService(h_manager, PChar(aServiceName),
    SERVICE_START or SERVICE_QUERY_STATUS);
    if h_svc > 0 then
    begin
      temp := nil;
      if (StartService(h_svc,0,temp)) then
        if (QueryServiceStatus(h_svc,svc_status)) then
        begin
          while (SERVICE_RUNNING <> svc_status.dwCurrentState) do
          begin
            dwCheckPoint := svc_status.dwCheckPoint;
            Sleep(svc_status.dwWaitHint);
            if (not QueryServiceStatus(h_svc,svc_status)) then
              break;
            if (svc_status.dwCheckPoint then
            begin
              // QueryServiceStatus не увеличивает dwCheckPoint
              break;
            end;
          end;
        end;
      CloseServiceHandle(h_svc);
    end;
    CloseServiceHandle(h_manager);
  end;
  Result := SERVICE_RUNNING = svc_status.dwCurrentState;
end;
function ServiceStop(aMachine,aServiceName: string ): boolean;
// aMachine это UNC путь, либо локальный компьютер если пусто
var
  h_manager, h_svc: SC_Handle;
  svc_status: TServiceStatus;
  dwCheckPoint: DWord;
begin
  h_manager:=OpenSCManager(PChar(aMachine),nil, SC_MANAGER_CONNECT);
  if h_manager > 0 then
  begin
    h_svc := OpenService(h_manager,PChar(aServiceName),
    SERVICE_STOP or SERVICE_QUERY_STATUS);
    if h_svc > 0 then
    begin
      if(ControlService(h_svc,SERVICE_CONTROL_STOP, svc_status))then
      begin
        if(QueryServiceStatus(h_svc,svc_status))then
        begin
          while(SERVICE_STOPPED <> svc_status.dwCurrentState)do
          begin
            dwCheckPoint := svc_status.dwCheckPoint;
            Sleep(svc_status.dwWaitHint);
            if(not QueryServiceStatus(h_svc,svc_status))then
            begin
              // couldn't check status
              break;
            end;
            if(svc_status.dwCheckPoint then
              break;
          end;
        end;
      end;
      CloseServiceHandle(h_svc);
    end;
    CloseServiceHandle(h_manager);
  end;
  Result := SERVICE_STOPPED = svc_status.dwCurrentState;
end;

Чтобы узнать состояние сервиса, используйте следующую функцию:

function ServiceGetStatus(sMachine, sService: string ): DWord;
var
  h_manager, h_service: SC_Handle;
  service_status: TServiceStatus;
  hStat: DWord;
begin
  hStat := 1;
  h_manager := OpenSCManager(PChar(sMachine) ,nil, SC_MANAGER_CONNECT);
  if h_manager > 0 then
  begin
    h_svc := OpenService(h_manager,PChar(sService), SERVICE_QUERY_STATUS);
    if h_svc > 0 then
    begin
      if(QueryServiceStatus(h_svc, service_status)) then
        hStat := service_status.dwCurrentState;
      CloseServiceHandle(h_svc);
    end;
    CloseServiceHandle(h_manager);
  end;
  Result := hStat;
end;

Она возвращает одну из следующих констант:

SERVICE_STOPPED SERVICE_RUNNING SERVICE_PAUSED SERVICE_START_PENDING SERVICE_STOP_PENDING SERVICE_CONTINUE_PENDING SERVICE_PAUSE_PENDING

Всё что, что Вам нужно, это unit WinSvc!

Другой способ запуска чужого приложения.

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

uses
   libc;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   iPrg: Integer;
 begin
   //Execute kcalc - A calculator for KDE 
  iPrg := libc.system('kcalc');
   if iPrg = -1 then
     ShowMessage('Error executing your program');
 end;

Просмотрщик запущенных процессов.

Автор: Василий WEB-сайт: http://www.sources.ru Программа не видна по Ctrl+Alt+Del, и сама оттуда же может спрятать любой из процессов(правда, не все с самого начала "светятся" по Ctrl+Alt+Del) или завершить его. Простой пример для знакомства с ToolHelp32.

В исходном коде есть недоработки, например, процедура Delproc получает в качестве параметра строку, затем переводит ее в целочисленный тип(integer), хотя можно передавать сразу число. Заморочка была в проверке числа-индекса на подлинность, а так как я выдрал часть кода из более ранней своей проги, я не стал это менять, а просто подогнал до рабочей версии. Оптимизацией кода вы можете заняться сами по желанию(вы можете, если хотите, а если не хотите, то вы не обязаны, вы посто могли бы... да... :))) Программа не работала в WinNT 4.0, но в Win9x работать должна.

unit main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, tlhelp32, StdCtrls, ComCtrls, Buttons;
type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    Button4: TButton;
    Button5: TButton;
    StatusBar1: TStatusBar;
    Button6: TButton;
    procedure Button4Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
  private
    { Private declarations }
    procedure ListProcesses;
    procedure Delproc(numb: string);
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
  processID: array[1..50] of integer;
function RegisterServiceProcess(dwProcessID, dwType: integer): integer; stdcall;
  external 'kernel32.dll';
implementation
{$R *.DFM}
procedure TForm1.delproc(numb: string);
var
  c1: Cardinal;
  pe: TProcessEntry32;
  s1, s2: string;
  x: integer;
begin
  x := 0;
  try
    Strtoint(numb);
  except
    Statusbar1.SimpleText := 'Invalid number';
    exit;
  end;
  c1 := CreateToolHelp32Snapshot(TH32CS_SnapProcess, 0);
  if c1 = INVALID_HANDLE_VALUE then
  begin
    Statusbar1.SimpleText := 'Process listing failed';
    exit;
  end;
  try
    pe.dwSize := sizeof(pe);
    if Process32First(c1, pe) then
      repeat
        inc(x);
        s1 := ExtractFileName(pe.szExeFile);
        s2 := ExtractFileExt(s1);
        Delete(s1, length(s1) + 1 - length(s2), maxInt);
        if x = strtoint(numb) then
          if terminateprocess(OpenProcess(PROCESS_ALL_ACCESS, false,
            pe.th32ProcessID), 1) then
          begin
            Statusbar1.SimpleText := 'Process ' + s1 + ' terminated.';
          end
          else
            Statusbar1.SimpleText := ('Couldnt terminate process' +
              pe.szExeFile);
      until not Process32Next(c1, pe);
  finally CloseHandle(c1);
  end;
end;
procedure Tform1.ListProcesses;
var
  c1: Cardinal;
  pe: TProcessEntry32;
  s1, s2: string;
  x: integer;
begin
  X := 0;
  c1 := CreateToolHelp32Snapshot(TH32CS_SnapProcess, 0);
  if c1 = INVALID_HANDLE_VALUE then
  begin
    Statusbar1.SimpleText := ('Информация о процессах закрыта.');
    exit;
  end;
  try
    pe.dwSize := sizeof(pe);
    if Process32First(c1, pe) then
      repeat
        inc(x);
        s1 := ExtractFileName(pe.szExeFile);
        s2 := ExtractFileExt(s1);
        Delete(s1, length(s1) + 1 - length(s2), maxInt);
        Listbox1.Items.Add(Inttostr(x) + '  ' + s1 + '  :  ' + pe.szExeFile);
        ProcessId[x] := pe.th32ProcessID;
        //ListBox1.Items.Add(inttostr(pe.th32ProcessID));
      until not Process32Next(c1, pe);
  finally CloseHandle(c1);
  end;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
  Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Enabled := false;
  Button5.Enabled := false;
  Button6.Enabled := false;
  ListProcesses;
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(GetCurrentProcessID, 1);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  Listbox1.Clear;
  ListProcesses;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  p: integer;
begin
  //hide
  with Listbox1 do
    p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(ProcessID[p], 1);
  with Listbox1 do
    StatusBar1.SimpleText := (Listbox1.items[itemindex] + ' hidden');
end;
procedure TForm1.Button5Click(Sender: TObject);
var
  p: integer;
begin
  //show
  with Listbox1 do
    p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(ProcessID[p], 0);
  with Listbox1 do
    StatusBar1.SimpleText := (Listbox1.items[itemindex] + ' shown');
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
  Button1.Enabled := true;
  Button5.Enabled := true;
  Button6.Enabled := true;
end;
procedure TForm1.Button6Click(Sender: TObject);
var
  p: integer;
begin
  with Listbox1 do
    p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
  delproc(inttostr(p));
end;
end.

Как узнать, запущен ли процесс в Win9x.

function IsRunning(sName: string): boolean;
var
  han: THandle;
  ProcStruct: PROCESSENTRY32; // from "tlhelp32" in uses clause
  sID: string;
begin
  Result := false;
  // Get a snapshot of the system
  han := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
  if han = 0 then
    exit;
  // Loop thru the processes until we find it or hit the end
  ProcStruct.dwSize := sizeof(PROCESSENTRY32);
  if Process32First(han, ProcStruct) then
  begin
    repeat
      sID := ExtractFileName(ProcStruct.szExeFile);
      // Check only against the portion of the name supplied, ignoring case
      if uppercase(copy(sId, 1, length(sName))) = uppercase(sName) then
      begin
        // Report we found it
        Result := true;
        Break;
      end;
    until not Process32Next(han, ProcStruct);
  end;
  // clean-up
  CloseHandle(han);
end;

Бегущая строка.

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

procedure TForm1.Timer1Timer(Sender: TObject);
const
  LengthGoString = 10;
  Gostring = 'Этот код был взят с проекта Delphi World'+
  ' Выпуск 2002 - 2003! Этот код б';
  // Повторить столько символов - сколько в LengthGoString
const
  i: Integer = 1;
begin
  Label1.Caption := Copy(GoString, i, LengthGoString);
  Inc(i);
  if Length(GoString) - LengthGostring < i then
    i:=1;
end;

Создание компонент в Run-Time.

Надпись на могиле программера: Причина смерти: Run-time error at 18:12:97 Причина рождения: GPF at: 18:12:97

Итак, рассмотрим на первый взгляд сложный вопрос о создании компонентов в Run_time (то есть во время работы программы). Но на самом деле этот вопрос довольно просто решается.

Давайте вместе попробуем написать код, чтобы при нажатии на кнопку на форме появлялось текстовое поле с каким-нибудь текстом. Для этого надо проделать следующие действия: объявить переменную необходимого вам типа компонента, в нашем случае TEdit, затем необходимо описать процедуру Create для нашей переменной, задать обязательное свойство Parent (то есть где появится наш компонент), ну и установить необязательные параметры типа высоты, ширины и т.д. Лучше разбираться на конкретном примере, так что смотрите какой у меня получился код:

procedure TForm1.Button1Click(Sender: TObject);
var
  x: TEdit; // объявляем переменную типа TEdit
begin
  x:=TEdit.create(self);// создаем экземпляр компонента
  x.parent:=form1;// текстовое поле появится на форме
  x.left:=10;
  x.top:=10;
  x.Width:=250;
  x.Text:='Delphi World is (:-0) !!!';
end;

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

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

procedure TForm1.Button2Click(Sender: TObject);
var
  y: TFontDialog; // объявляем переменную типа TFontDialog
begin
  y:=TFontdialog.Create(self);
  y.Execute; //только для демонстрации. Показать что работает.
end;

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

Теперь мы попробуем вместе проделать такую вещь: мы отсортируем находящиеся в Memo данные. Для этого поставьте на форму Memo и напишите в ней 4 строки, например:

Memo4 Memo1 Memo3 Memo2

Поскольку само Memo сортировать данные не умеет, то нам придется во время работы программы создать какую-нибудь переменную абстрактного класса TStringList (этот класс сортировать умеет), затем присвоить этому классу строки из Memo, отсортировать их и присвоить их обратно Memo. Поставим еще одну кнопку на форму, которая будет запускать процесс. Код получится примерно таким:

procedure TForm1.Button3Click(Sender: TObject);
var
  t: TStringList;
begin
  t:=TStringList.Create; //создаем
  t.AddStrings(memo1.lines); //присваиваем переменной t строки из Memo
  t.Sort; // сортируем
  memo1.Clear;
  memo1.Lines.AddStrings(t); // присваиваем memo уже отсортированные строки
end;

Теперь рассмотрим еще один вопрос, касающийся создания компонентов в Run-time. Допустим вам надо создать 20 полей для ввода текста (Edit) и еще десять меток (Label), не будете же вы 20 раз писать одно и тоже для каждого edit'a. В этой ситуации есть очень элегантный выход: воспользоваться массивом компонентов. В общем виде объявление массива компонентов может выглядеть так: имя_переменной:array[нижний_индекс..верхний_индекс] of тип_компонента. Теперь поупражняемся в этом. Создайте новое приложение. На форму поместите только кнопку. Обработчик события OnClick которой у меня получился таким:

procedure TForm1.Button1Click(Sender: TObject);
var
  a: array [1..20] of TEdit; // массив элементов Edit
  b: array [1..10] of TLabel; //массив меток
  i, j: integer;
begin
  for i:=1 to 20 do
  begin
    a[i]:=TEdit.create(self);
    a[i].parent:=form1;
    a[i].left:=10;
    a[i].text:='элемент # '+inttostr(i);
    a[i].top:=i*20;
  end;
  for j:=1 to 10 do
  begin
    b[j]:=TLabel.create(self);
    b[j].parent:=form1;
    b[j].left:=200;
    b[j].Caption:='элемент # '+inttostr(j);
    b[j].top:=j*30;
  end;
end;

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

Как упаковать базу (DBase, Paradox) в RunTime.

Для dBase:

uses
  DbiProcs;
with Table do
begin
  OldState := Active;
  Close;
  Exclusive := True;
  Open;
  DbiPackTable(DBHandle, Handle, nil, nil, True);
  {^ здесь можно добавить check()}
  Close;
  Exclusive := False;
  Active := OldState;
  { при желании можно сохранить закладку }
end;

Для Paradox:

DbiDoRestructure(DBHandle, 1, ?, nil, nil, nil, False); { см. dbiProcs.int }

Вместо ? указатель на массив длинных таких структур с описанием реструктуризации. Кто делал на Px, я думаю, дадут пример.

Это насчет упаковки db (может что-то и лишнее, что-то можно сделать по-другому, но работает ).

Пример для Paradox:

uses
  BDE; // for D3, для D2 непомню (что-то типа DbiProc и еще что-то)
// для примера
tLog: TTable; // таблица юзающая d:\db\log.db
var
  TblDesc: CRTblDesc;
  rslt: DBIResult;
  Dir: string; //имеется в виду huge string т.е. {$H+}
  hDb: hDbiDb;
begin
  tLog.Active:=False; //деактивируем TTable
  SetLength(Dir, dbiMaxNameLen + 1);
  DbiGetDirectory(tLog.DBHandle, False, PChar(Dir));
  SetLength(Dir, StrLen(PChar(Dir)));
  DbiOpenDatabase(nil, nil, dbiReadWrite, dbiOpenExcl,
  nil, 0, nil, nil, hDb);
  DbiSetDirectory(hDb, PChar(Dir));
  FillChar(TblDesc, sizeof(CRTblDesc), 0);
  StrPCopy(TblDesc.szTblName, 'd:\db\log.db');
  // здесь должно быть полное имя файла
  // которое можно:
  // а) ввести руками;
  // б) вытащить из пропертей таблицы;
  // в) вытащить из алиаса;
  // г) см. FAQ
  StrCopy(TblDesc.szTblType, szParadox);
  //BTW тут может и szDBase стоять
  TblDesc.bPack := TRUE;
  DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False);
  DbiCloseDatabase(hDb);
end;

Как запустить приложение и подождать пока оно отработает 7.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск программы и ожидание ее завершения.
Функция запускает внешнюю программу, заданную параметром Path, и ждет ее завершения.
Во время выполнения внешней программы, текущее приложение скрывается (
т.к. не может перерисовать главное окно, что некрасиво), поэтому фактически
происходит переключение с текущей программы на внешнюю и,
после завершения внешней программы, обратно.
В качестве внешней программы может выступать любой выполняемый файл (EXE, COM, BAT, SCR).
Зависимости: Windows, Forms
Автор:       Евгений Валяев (RhinoFC), rhinofc@sniiggims.ru, ICQ:55263922, Новосибирск
Copyright:   RhinoFC
Дата:        5 июня 2002 г.
***************************************************** }
function SwitchToProg(const Path: string): Boolean;
var
  SI: TStartupInfo;
  PI: TProcessInformation;
  ExitCode: Cardinal;
begin
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  Result := CreateProcess(nil, PChar(Path), nil, nil, False, 0, nil, nil,
    SI, PI);
  if Result then
  begin
    Application.MainForm.Hide;
    while GetExitCodeProcess(PI.hProcess, ExitCode) and
      (ExitCode = STILL_ACTIVE) do
      ; // ждем завершения, пустой цикл
    Application.MainForm.Show;
  end;
end;

Как запустить приложение и подождать пока оно отработает 8.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Дождаться завершение внешнего приложения
Дожидается завершения внешнего процесса
Зависимости: shellAPI
Автор:       elena_tark, elena_tark@freemail.ru, Калуга
Copyright:   Собственное
Дата:        1 августа 2003 г.
***************************************************** }
procedure ShellExec(Sender: TObject);
var
  ProcInfo: PShellExecuteInfo;
begin
  (Sender as TControl).Enabled := False;
  GetMem(ProcInfo, SizeOf(ProcInfo^));
  with ProcInfo^ do
  begin
    Wnd := Handle;
    cbSize := SizeOf(ProcInfo^);
    lpFile := PChar('notepad.exe');
    lpParameters := nil;
    lpVerb := 'open';
    nShow := SW_SHOW;
    fMask := SEE_MASK_DOENVSUBST or SEE_MASK_NOCLOSEPROCESS;
  end;
  try
    Win32check(ShellExecuteEx(ProcInfo));
    while not Application.Terminated and
      (WaitForSingleObject(ProcInfo.hProcess, 100) = WAIT_TIMEOUT) do
      Application.ProcessMessages;
  finally
    if ProcInfo.hProcess <> 0 then
      CloseHandle(ProcInfo.hProcess);
    Dispose(ProcInfo);
    (Sender as TControl).Enabled := True;
  end;
end;

Запуск программы и ожидание завершения определенное время.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск программы и ожидание завершения опр.время
Запускает внешнюю программу и ожидает его завершения определённое время,
если истекает время то, программа продолжает работу не зависимо от
состояния внешней программы (На случай подвисания вн. прог-мы, особенно DOS)
Зависимости: Windows
Автор:       Kostas, kostas@tut.by, РБ Гомельская обл. г. Речица
Copyright:   вроде delphi.mastak.ru (слегка доработанно)
Дата:        03 мая 2002 г.
***************************************************** }
function ExecAndWait(aCmd: string; WaitTimeOut: cardinal = INFINITE): Cardinal;
var
  si: STARTUPINFO;
  pi: PROCESS_INFORMATION;
  res: BOOL;
  r: cardinal;
begin
  with si do
  begin
    cb := sizeof(si);
    lpReserved := nil;
    lpDesktop := nil;
    lpTitle := PChar('External program "' + aCmd + '"');
    dwFlags := 0;
    cbReserved2 := 0;
    lpReserved2 := nil;
  end;
  res := CreateProcess(nil, PChar(aCmd), nil, nil, FALSE, 0, nil, nil, si, pi);
  if res then
    WaitForSingleObject(pi.hProcess, WaitTimeOut);
  GetExitCodeProcess(pi.hProcess, r);
  result := r;
end;
// Пример использования:
procedure Exec(CommandStr, WaitTime);
begin
  if ExecAndWait(CommandStr, WaitTime) = WAIT_FAILED then
    ShowMessage('Истекло время ожидания внешней программы ' + CommandStr);
end;

Запуск программы и ожидание ее окончания, принудительное завершение если Timeout.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск программы и ожидание ее окончания, принудительное завершение если timeout
Функция создавалась для запуска и сопровождения приложения в указаном интервале времени.
Я лично для наблюдения за работой приложения включил функцию в нить,
которая (обрывалась) по завершении процесса.
Зависимости: Classes, SysUtils, Windows, strUtils, Consts;
Автор:       August, august@torba.com, ICQ:176381214
Copyright:   doctorAugust
Дата:        5 февраля 2003 г.
***************************************************** }
function ExecuteFileTm(FileName: string; Params: string; Dsk: boolean; TimeOut:
  cardinal): boolean;
var
  Res: LongBool;
  PIn: PROCESS_INFORMATION;
  SII: STARTUPINFO;
  RetCode: cardinal;
  TmOut: cardinal; //In seconds
  i: cardinal;
  CreationsFlags: cardinal;
begin
  TmOut := TimeOut;
  try
    if Dsk then
    begin
      CreationsFlags := CREATE_DEFAULT_ERROR_MODE or NORMAL_PRIORITY_CLASS
        or CREATE_UNICODE_ENVIRONMENT;
    end
    else
    begin
      CreationsFlags := CREATE_DEFAULT_ERROR_MODE or CREATE_NO_WINDOW
        or NORMAL_PRIORITY_CLASS
        or CREATE_UNICODE_ENVIRONMENT;
    end;
    getstartupinfo(SII);
    Res := createprocess(
      pchar(FileName),
      pchar(Params),
      nil,
      nil,
      True,
      CreationsFlags,
      nil,
      pchar(extractfilepath(FileName)),
      SII,
      PIn);
  except
    CloseHandle(Pin.hProcess);
    ExecuteFileTm := False;
    exit;
  end;
  if not Res then
  begin
    CloseHandle(Pin.hProcess);
    ExecuteFileTm := False;
    exit;
  end;
  i := 0;
  RetCode := STILL_ACTIVE;
  while (i <= TmOut) and (RetCode = STILL_ACTIVE) do
  begin
    try
      GetExitCodeProcess(Pin.hProcess, RetCode);
    except
      CloseHandle(Pin.hProcess);
      ExecuteFileTm := False;
      exit;
    end;
    Sleep(1000);
    inc(i);
  end;
  if (i > TmOut) and (RetCode = STILL_ACTIVE) then
  begin
    TerminateProcess(Pin.hProcess, RetCode);
    CloseHandle(Pin.hProcess);
    // ExecuteFilePrExt:=false;
    ExecuteFileTm := false; // ION T
    exit;
  end;
  CloseHandle(Pin.hProcess);
  ExecuteFileTm := True;
end;

Пример запуска макроса в MS Word.

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

Сдает новый русский компьютер по гарантии. Смотрят - у монитора здоровенная дыра пробита в правом нижнем углу. - Как же это так? - Да вы чё мне продали!!! Я полгорода держу!!! Передо мною вся братва строится!!! А тут какая-то скрепка мне пальцами да у виска крутит!!!
...      
vvWord:= CreateOleObject('Word.Application.8');  
vvWord.Application.Visible:=true;  
vvWord.Documents.Open( TempFileName );  
vvWord.ActiveDocument.SaveAs( FileName, 1 ); // as .DOC  
vvWord.Application.Run( 'Macros Name' );  
...

Русификация консольных приложений в Delphi.

Автор: Eugene Kasnerik
Приказ министра обороны по русификации сообщений WINDOWS. Yes - Есть! No - Никак нет! OK - Так точно! Pause - Можно оправиться и закурить. Stop - Смирно! Continue - Вольно! Abort - Расстрелять! Retry - Кругом! Ignore - Выполнить любой ценой! Login - Стой, кто идет? Password - Предъявите пропуск! Shutdown - Отбой! Access denied - Не положено! Message - Рапорт. Exception - Нештатная ситуация. А вместо слова WINDOW, приказываю использовать - АМБРАЗУРА.

С периодичностью раз в месяц-полтора конференция RU.DELPHI оглашается стонами на тему “Консоль не поет по-русски”, за которыми стоит вывод текста в консольных приложениях в кодировке OEM (Delphi IDE, как и все GUI, работает в ANSI).

С точки зрения набора символов эти кодовых таблицы не совпадают: позиции символов кириллицы в них различны (отсюда и неприятные эффекты), кроме того, в ANSI присутствуют диакритические символы, которых нет в OEM, но в последней имеются символы псевдографики, незаменимые при изображении таблиц (интересно, это еще кем-то востребовано? На ум приходит только FAR). Впрочем, возможности для вывода текстовой информации у этих таблиц одинаковы, что в нашем случае позволяет говорить о взаимозаменяемости.

Рассмотрим некоторые способы, которыми можно решить возникающие проблемы (три из них встречаются в различных FAQ, последний менее тривиален, но, видимо, в наибольшей степень отвечает задаче).

Работа в OEM-редакторе. Вместо борьбы с выводом «не тех» символов можно пойти иным путем – изначально готовить текст программы (точнее те его части, которые критичны к кодовой таблице) в редакторе, работающем в кодировке OEM. Решение простенькое, но на удивление эффективное, особенно при написании каких-либо локальных утилит, в которых, тем не менее, сильно востребован вывод информации.

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

Если же проект не содержит hard-coded (включенных непосредственно в код) строк, то возможно организовать вынесение всех строковых ресурсов в отдельные модули с последующей их локализацией под требуемую кодировку – утилит, меняющих кодировку файлов существует предостаточно.

Использование фильтрующих процедур. Windows API содержит функции для преобразования между кодировками OEM и ANSI OemToChar, CharToOem, которые и предлагается использовать при выводе текста, заменяя фрагменты.

Writeln('Delphi World - это круто!!!');

На:

procedure MyWriteln(const S: string);
var
  NewStr: string;
begin
  SetLengtn(NewStr, Length(S));
  CharToOem(PChar(S), PChar(NewStr));
  Writeln(NewStr);
end;
...
MyWriteln('Delphi World - это круто!!!');

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

Изменение кодовой страницы консоли. В принципе, для решения задачи есть документированный способ – изменение кодовой страницы консоли средствами Windows API. Проблема лишь в том, что в Win95/98 функция не работает. Впрочем, если приложение будет работать только в Windows NT, можно воспользоваться функцией SetConsoleOutputCP(866).

Перекрытие процедур вывода в RTL. Вывод в Pascal (еще в версиях от Borland для DOS) через Write-процедуры осуществляется посредством передачи выводимой информации в файл Output, который вполне можно подвергнуть легкой модификации с целью упростить себе жизнь.

Известно, что Write/Writeln без указания файла осуществляет вывод в файл Output. Output имеет тип TextFile, он же TTextRec, содержимое которого описано в SysUtils.pas. Есть там и поля, содержащие адреса процедур, в которые приходит на обработку поток выводимых приложением данных (в случае вывода). Не вдаваясь в подробности (желающие могут посмотреть устройство механизмов вывода в исходниках RTL), покажем, что происходит в процедуре, отвечающей за вывод (TTextRec.InOutFunc):

{ Реконструкция TextOut из Assign.asm }
function TextOut(var Text: TTextRec): Integer;
var
  Dummy: Cardinal;
  SavePos: Integer;
begin
  SavePos := Text.BufPos;
  if SavePos > 0 then
  begin
    Text.BufPos := 0;
    if WriteFile(Text.Handle, Text.BufPtr^, SavePos, Dummy, nil) then
      Result := 0
    else
      Result := GetLastError;
  end
  else
    Result := 0;
end;

Теперь видно, что нужно сделать для вывода символов в нужной кодовой таблице – перед выводом в файл средствами ОС модифицировать данные в выходном буфере структуры Text, вписав следующую строку:

CharToOemBuff(Text.BufPtr, Text.BufPtr, SavePos);

Модифицировать буфер можно, т.к. после операции записи в файл содержимое буфера фактически сбрасывается (когда в Text.BufPos записывается 0 – именно столько актуальных данных остается в буфере). Если не завязываться на эту особенность реализации, можно распределить буфер и модифицировать данные уже в нем. Впрочем, решение в любом случае достаточно сильно опирается на особенности реализации, поэтому проверить его пригодность при смене версии Delphi рекомендуется в любом случае. С другой стороны, вероятность отхода Borland от наработанного решения крайне мала.

Заметим, что кроме InOutFunc вывод в файл ОС происходит и в FlushFunc, которая в файле Output указывает на ту же функцию, что и InOutFunc. С учетом всего вышесказанного модуль, осуществляющий «русификацию» консольных приложений «на лету» будет совсем небольшим:

{
Модуль “русификации“ консольных приложений
(c) Eugene Kasnerik, 1999
e-mail: eugene1975@mail.ru
}
unit EsConsole;
interface
implementation
uses
  Windows;
{
Описание структуры приведено здесь с единственной целью –
не подключать SysUtils и, соответственно, код инициализации
этого модуля. Консольные приложения обычно малы и 25К кода
обработки исключений – несколько высокая плата за описание
единственной структуры.
}
type
  TTextRec = record
    Handle: Integer;
    Mode: Integer;
    BufSize: Cardinal;
    BufPos: Cardinal;
    BufEnd: Cardinal;
    BufPtr: PChar;
    OpenFunc: Pointer;
    InOutFunc: Pointer;
    FlushFunc: Pointer;
    CloseFunc: Pointer;
    UserData: array[1..32] of Byte;
    name: array[0..259] of Char;
    Buffer: array[0..127] of Char;
end;
function ConOutFunc(var Text: TTextRec): Integer;
var
  Dummy: Cardinal;
  SavePos: Integer;
begin
  SavePos := Text.BufPos;
  if SavePos > 0 then
  begin
    Text.BufPos := 0;
    CharToOemBuff(Text.BufPtr, Text.BufPtr, SavePos);
    if WriteFile(Text.Handle, Text.BufPtr^, SavePos, Dummy, nil) then
      Result := 0
    else
      Result := GetLastError;
  end
  else
    Result := 0;
end;
initialization
  Rewrite(Output); // Проводим инициализацию файла
  { И подменяем обработчики. Есть в этом что-то от
  хака, но цель оправдывает средства }
  TTextRec(Output).InOutFunc := @ConOutFunc;
  TTextRec(Output).FlushFunc := @ConOutFunc;
end.

Для русификации приложения достаточно лишь подключить вышеуказанный модуль в любом месте программы (как правило, в проекте), после чего вывод ANSI-символов будет осуществлен в ожидаемом виде. Однако следует иметь в виду, что не будут доступны символы псевдографики (для них нет аналогов в ANSI, т.е. в редакторе их не введешь) и часть диакритических знаков (для них нет аналогов в OEM). Впрочем, для модификации значений выводимых символов не обязательно использовать системные функции, что открывает простор для консольного вывода в самых разных кодировках.

Выравнивание текста по ширине с автоматическим переносом русских слов.

Оформил: DeeCo

Автор: Антон Григорьев.

Краткое описание :

Для переноса слов программа не использует словарь, а анализирует каждое слово с точки зрения правил русского языка. Правила эти достаточно сложны, поэтому иногда программа выбирает не самое изящное, хотя и не неправильное решение. Прежде всего это касается тех слов, в которых три или более согласных идут подряд. Программа может вставить перенос между любыми из них, хотя есть дополнительные правила, которые при этом игнорируются. Например, то, что приставка должна оставаться целой. Слишком муторно было бы заставлять компьютер учить все приставки. Да ещё и объяснять, что, например, в слове "президент" "пре" - это не приставка, а в слове "предлог" приставка не "пре", а "пред". Тогда уж лучше и в самом деле словарь. Впрочем, демонстрационная программа и есть демонстрационная программа, кто захочет, посмотрит, поймёт основные принципы и сделает лучше. Из улучшений, которые так и напрашиваются, стоит назвать красную строку и разбивку текста на несколько абзацев. Но это делается настолько элементарно, что мне даже не хочется на этом останавливаться. Можно ввести перенос английских слов (сейчас программа их переносить не умеет, потому что я не слишком хорошо знаком с английскими правилами переноса), можно ввести запрет на перенос слов из прописных букв - возможностей для творчества хватает, было бы желание. Я же реализовал простейший вариант - при запуске программа ищет файл ByWidth.txt, мешает все слова в одну кучу, удаляет все лишние пробелы и разбивает на строки нужной длины, перенося при необходимости слова. Предполагается, что в исходном тексте нет переносов, в том числе и тех слов, которые пишутся через дефис. Таким образом, исходный текст может быть написан хоть в одну строку, хоть по одному слову на строку - разницы не будет. И можно вставлять пустые строки - они всё равно игнорируются.

Примечание:

Защита от дурака в программе отсутствует, поэтому если файл ByWidth.txt не будет содержать никакого текста, никаких вежливых предупреждений не будет. Тот, кто захочет приспособить этот пример для чего-то полезного, без проблем добавит эту защиту, а загромождать демонстрацинную программу вряд ли стоит. Скачать проект : ByWidth.zip файлы проекта + exe (189 K) ByWidthPr.zip только файлы проекта (16,5 K)

Вставка рисунка в TRxRichEdit.

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

uses
   RichEdit;
 // Stream Callback function 
type
   TEditStreamCallBack = function(dwCookie: Longint; pbBuff: PByte;
     cb: Longint; var pcb: Longint): DWORD;
   stdcall;
   TEditStream = record
     dwCookie: Longint;
     dwError: Longint;
     pfnCallback: TEditStreamCallBack;
   end;
 // RichEdit Type 
type
   TMyRichEdit = TRxRichEdit;
 // EditStreamInCallback callback function 
function EditStreamInCallback(dwCookie: Longint; pbBuff: PByte;
   cb: Longint; var pcb: Longint): DWORD; stdcall;
   // by P. Below 
var
   theStream: TStream;
   dataAvail: LongInt;
 begin
   theStream := TStream(dwCookie);
   with theStream do
   begin
     dataAvail := Size - Position;
     Result := 0;
     if dataAvail <= cb then
     begin
       pcb := read(pbBuff^, dataAvail);
       if pcb <> dataAvail then
         Result := UINT(E_FAIL);
     end
     else
     begin
       pcb := read(pbBuff^, cb);
       if pcb <> cb then
         Result := UINT(E_FAIL);
     end;
   end;
 end;
 // Insert Stream into RichEdit 
procedure PutRTFSelection(RichEdit: TMyRichEdit; SourceStream: TStream);
   // by P. Below 
var
   EditStream: TEditStream;
 begin
   with EditStream do
   begin
     dwCookie := Longint(SourceStream);
     dwError := 0;
     pfnCallback := EditStreamInCallBack;
   end;
   RichEdit.Perform(EM_STREAMIN, SF_RTF or SFF_SELECTION, Longint(@EditStream));
 end;
 // Convert Bitmap to RTF Code 
function BitmapToRTF(pict: TBitmap): string;
 // by D3k 
var
   bi, bb, rtf: string;
   bis, bbs: Cardinal;
   achar: ShortString;
   hexpict: string;
   I: Integer;
 begin
   GetDIBSizes(pict.Handle, bis, bbs);
   SetLength(bi, bis);
   SetLength(bb, bbs);
   GetDIB(pict.Handle, pict.Palette, PChar(bi)^, PChar(bb)^);
   rtf := '{\rtf1 {\pict\dibitmap ';
   SetLength(hexpict, (Length(bb) + Length(bi)) * 2);
   I := 2;
   for bis := 1 to Length(bi) do
   begin
     achar := Format('%x', [Integer(bi[bis])]);
     if Length(achar) = 1 then
       achar := '0' + achar;
     hexpict[I - 1] := achar[1];
     hexpict[I] := achar[2];
     Inc(I, 2);
   end;
   for bbs := 1 to Length(bb) do
   begin
     achar := Format('%x', [Integer(bb[bbs])]);
     if Length(achar) = 1 then
       achar := '0' + achar;
     hexpict[I - 1] := achar[1];
     hexpict[I] := achar[2];
     Inc(I, 2);
   end;
   rtf := rtf + hexpict + ' }}';
   Result := rtf;
 end;
 // Example to insert image from Image1 into RxRichEdit1 
procedure TForm1.Button1Click(Sender: TObject);
 var
   SS: TStringStream;
   BMP: TBitmap;
 begin
   BMP := TBitmap.Create;
   BMP := Image1.Picture.Bitmap;
   SS  := TStringStream.Create(BitmapToRTF(BMP));
   try
     PutRTFSelection(RxRichEdit1, SS);
   finally
     SS.Free;
   end;
 end;

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

Статьи по Delphi (015)…
unit Dates;
interface
uses
  SysUtils, Classes;
type
  TDate = class (TComponent)
  private
    FMonth, FDay, FYear: Integer;
    FOnChange: TNotifyEvent;
  protected
    function DaysInMonth: Integer;
    procedure SetMonth (Value: Integer);
    procedure SetYear (Value: Integer);
    procedure SetDay (Value: Integer);
    procedure DoChange; virtual;
  public
    constructor Create (AOwner: TComponent); override;
    constructor Init (m, d, y: Integer);
    procedure SetValue (m, d, y: Integer);
    function LeapYear: Boolean;
    procedure Increase;
    procedure Decrease;
    procedure Add (NumberOfDays: Integer);
    procedure Subtract (NumberOfDays: Integer);
    function GetText: string;
    // properties:
    property Text: string read GetText;
  published
    property Day: Integer read FDay write SetDay;
    property Month: Integer read FMonth write SetMonth;
    property Year: Integer read FYear write SetYear;
    // event:
    property OnChange: TNotifyEvent
      read FonChange write FOnChange;
  end;
// dates exception
type
  EDateOutOfRange = class (Exception);
procedure Register;
implementation
constructor TDate.Create (AOwner: TComponent);
var
  Y, D, M: Word;
begin
  inherited Create (AOwner);
  // today...
  DecodeDate (Now, Y, M, D);
  FYear := Y;
  FMonth := M;
  FDay := D;
end;
constructor TDate.Init (m, d, y: Integer);
begin
  SetValue (m, d, y);
end;
procedure TDate.DoChange;
begin
  if Assigned (FOnChange) then
    FOnChange (self);
end;
procedure TDate.SetValue (m, d, y: Integer);
var
  OldY, OldM: Integer;
begin
  // store the old value
  OldY := FYear;
  OldM := FMonth;
  // assing the new value
  try
    FYear := y;
    // check the ranges
    SetMonth (m);
    SetDay (d);
    DoChange;
  except
    on EDateOutOfRange do
    begin
      // reset the values
      FYear := OldY;
      FMonth := OldM;
      // let the error show up
      raise;
    end;
  end;
end;
procedure TDate.SetMonth (Value: Integer);
begin
  if (Value >= 1) and (Value <= 12) then
  begin
    FMonth := Value;
    DoChange;
  end
  else
    raise EDateOutOfRange.Create ('Month out of range');
end;
procedure TDate.SetYear (Value: Integer);
begin
  FYear := Value;
  DoChange;
end;
procedure TDate.SetDay (Value: Integer);
begin
  if (Value >= 1) and (Value <= DaysInMonth) then
  begin
    FDay := Value;
    DoChange;
  end
  else
    raise EDateOutOfRange.Create ('Day out of range');
end;
function TDate.LeapYear: Boolean;
begin
  // compute leap years, considering "exceptions"
  if (FYear mod 4 <> 0) then
    LeapYear := False
  else if (FYear mod 100 <> 0) then
    LeapYear := True
  else if (FYear mod 400 <> 0) then
    LeapYear := False
  else
    LeapYear := True;
end;
function TDate.DaysInMonth: Integer;
begin
  case FMonth of
    1, 3, 5, 7, 8, 10, 12:
      DaysInMonth := 31;
    4, 6, 9, 11:
      DaysInMonth := 30;
    2:
      if (LeapYear) then
        DaysInMonth := 29
      else
        DaysInMonth := 28;
    else
      // if the month is not correct
      DaysInMonth := 0;
  end;
end;
procedure TDate.Increase;
begin
  // if this day is not the last of the month
  if FDay < DaysInMonth then
    Inc (FDay) // increase the value by 1
  else
  // if it is not in December
    if FMonth < 12 then
    begin
      // Day 1 of next month
      Inc (FMonth);
      FDay := 1;
    end
    else
    begin
      // else it is next year New Year's Day
      Inc (FYear);
      FMonth := 1;
      FDay := 1;
    end;
  DoChange;
end;
// exactly the reverse of the Increase method
procedure TDate.Decrease;
begin
  if FDay > 1 then
    Dec (FDay) // decrease the value by 1
  else
    // it is the first of a month
    if FMonth > 1 then
    begin
      // assign last day of previous month
      Dec (FMonth);
      FDay := DaysInMOnth;
    end
    else
    // it is the first of January
    begin
      // assign last day of previous year
      Dec (FYear);
      FMonth := 12;
      FDay := DaysInMOnth;
    end;
  DoChange;
end;
function TDate.GetText: string;
begin
  GetText :=  Format ('%s %d, %d',
    [LongMonthNames[Month], Day, Year]);
end;
procedure TDate.Add (NumberOfDays: Integer);
var
  N: Integer;
begin
  // increase the day n times
  for N := 1 to NumberOfDays do
    Increase;
end;
procedure TDate.Subtract (NumberOfDays: Integer);
var
  N: Integer;
begin
  // decrease the day n times
  for N := 1 to NumberOfDays do
    Decrease;
end;
procedure Register;
begin
  RegisterComponents ('Md3', [TDate]);
end;
end.
unit SList;
interface
uses
  Classes;
type
  TSafeList = class
  private
    LType: TClass;
    FList: TList;
    function Get (Index: Integer): TObject;
    procedure Put (Index: Integer; Item: TObject);
    function GetCount: Integer;
  public
    constructor Create (CType: TClass);
    destructor Destroy; override;
    function Add (Item: TObject): Integer;
    function Equals(List: TSafeList): Boolean;
    property Count: Integer read GetCount;
    property Items [Index: Integer]: TObject
      read Get write Put; default;
  end;
implementation
uses
   SysUtils;
constructor TSafeList.Create (CType: TClass);
begin
  FList := TList.Create;
  LType := CType;
end;
destructor TSafeList.Destroy;
begin
  FList.Free;
  inherited Destroy;
end;
function TSafeList.Get(Index: Integer): TObject;
begin
  Result := FList [Index];
end;
function TSafeList.Add (Item: TObject): Integer;
var
  Test: Boolean;
begin
  try
    Test := Item is LType;
  except
    on Exception do
      raise EInvalidCast.Create (Format (
        'SafeList: Cannot add a non-object to a list of %s objects',
        [LType.ClassName]));
  end;
  if Test then
    Result := FList.Add (Item)
  else
    raise EInvalidCast.Create (Format (
      'SafeList: Cannot add a %s object to a list of %s objects',
      [Item.ClassName, LType.ClassName]));
end;
procedure TSafeList.Put(Index: Integer; Item: TObject);
var
  Test: Boolean;
begin
  try
    Test := Item is LType;
  except on Exception do
    raise EInvalidCast.Create (Format (
      'SafeList: Cannot put a non-object into a list of %s objects',
      [LType.ClassName]));
  end;
  if Test then
    FList [Index] := Item
  else
    raise EInvalidCast.Create (Format (
      'SafeList: Cannot put a %s object into a list of %s objects',
      [TObject(Item).ClassName, LType.ClassName]));
end;
function TSafeList.GetCount: Integer;
begin
  Result := FList.Count;
end;
function TSafeList.Equals(List: TSafeList): Boolean;
var
  I: Integer;
begin
  Result := False;
  if List.Count <> FList.Count then
    Exit;
  for I := 0 to List.Count - 1 do
    if List[I] <> FList[I] then
      Exit;
  Result := True;
end;
end.
unit SafeForm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, SList;
type
  TForm1 = class(TForm)
    ButtonAddDates: TButton;
    ButtonAddButton: TButton;
    ButtonAddPointer: TButton;
    ButtonNewDate: TButton;
    ListBox1: TListBox;
    ButtonNewButton: TButton;
    ButtonNewPointer: TButton;
    procedure ButtonAddDatesClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ButtonAddButtonClick(Sender: TObject);
    procedure ButtonAddPointerClick(Sender: TObject);
    procedure ButtonNewDateClick(Sender: TObject);
    procedure ButtonNewButtonClick(Sender: TObject);
    procedure ButtonNewPointerClick(Sender: TObject);
  private
    List: TSafeList;
  public
    procedure UpdateList;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
uses
  Dates;
procedure TForm1.ButtonAddDatesClick(Sender: TObject);
var
  I: Integer;
begin
  Randomize;
  try
    for I := 1 to 10 do
      List.Add (TDate.Init (
        1 + Random (12), 1 + Random (28),
        1900 + Random (200)));
  finally
    UpdateList;
  end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  List := TSafeList.Create (TDate);
end;
procedure TForm1.ButtonAddButtonClick(Sender: TObject);
begin
  List.Add (Sender);
  UpdateList;
end;
procedure TForm1.ButtonAddPointerClick(Sender: TObject);
var
  P: Pointer;
begin
  P := @Form1;
  List.Add (P);
  UpdateList;
end;
procedure TForm1.UpdateList;
var
  I: Integer;
begin
  ListBox1.Clear;
  for I := 0 to List.Count - 1 do
    Listbox1.Items.Add ((
      TDate(List [I]).GetText));
end;
procedure TForm1.ButtonNewDateClick(Sender: TObject);
begin
  List [1] := TDate.Create (self);
  UpdateList;
end;
procedure TForm1.ButtonNewButtonClick(Sender: TObject);
begin
  List [1] := Sender;
  UpdateList;
end;
procedure TForm1.ButtonNewPointerClick(Sender: TObject);
var
  S: String;
begin
  S := 'Hi';
  List [1] := Pointer(S);
  UpdateList;
end;
end.

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

Как определить, что два слова имеют схожее произношение.

Автор: Lloyd Функция Soundex определяет схожесть звучания двух слов. Алгоритм Soundex опубликован в одной из статей журнала PC Magazine и предназначен для работы с английским языком (может кто-нибудь портирует для работы с нашим могучим? Пишите). Функции передается строка. Возвращаемое Soundex значение также имеет тип строки. Эта величина может сохраняться в базе данных или сравниваться с другим значением Soundex. Если два слова имеют одинаковое значение Soundex, можно предположить, что звучат они одинаково (более или менее). Вы должны иметь в виду, что алгоритм Soundex игнорирует первую букву слова. Таким образом, "won" и "one" будут иметь различное значение Soundex, а "Won" и "Wunn" - одинаковое.

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

function Soundex(OriginalWord: string): string;
var
  Tempstring1, Tempstring2: string;
  Count: integer;
begin
  Tempstring1 := '';
  Tempstring2 := '';
  OriginalWord := Uppercase(OriginalWord);
    {Переводим исходное слово в верхний регистр}
  Appendstr(Tempstring1, OriginalWord[1]); {Используем первую букву слова}
  for Count := 2 to length(OriginalWord) do
    {Назначаем числовое значение каждой букве, за исключением первой}
    case OriginalWord[Count] of
      'B', 'F', 'P', 'V': Appendstr(Tempstring1, '1');
      'C', 'G', 'J', 'K', 'Q', 'S', 'X', 'Z': Appendstr(Tempstring1, '2');
      'D', 'T': Appendstr(Tempstring1, '3');
      'L': Appendstr(Tempstring1, '4');
      'M', 'N': Appendstr(Tempstring1, '5');
      'R': Appendstr(Tempstring1, '6');
      {Все другие буквы, цифры и знаки пунктуации игнорируются}
    end;
  Appendstr(Tempstring2, OriginalWord[1]);
  {Удаляем из результата все последовательно повторяющиеся цифры.}
  for Count := 2 to length(Tempstring1) do
    if Tempstring1[Count - 1] <> Tempstring1[Count] then
      Appendstr(Tempstring2, Tempstring1[Count]);
  Soundex := Tempstring2; {Это - значение soundex}
end;

SoundAlike - функция, проверяющая схожесть звучания двух слов. При схожести звучания она возвратит значение True и значение False в противном случае. Она демонстрирует пример использования функции Soundex.

function SoundAlike(Word1, Word2: string): boolean;
begin
  if (Word1 = '') and (Word2 = '') then
    result := True
  else if (Word1 = '') or (Word2 = '') then
    result := False
  else if (Soundex(Word1) = Soundex(Word2)) then
    result := True
  else
    result := False;
end;
Дополнение Существует алгоритм ("параметрической корреляции", если я вообще правильно называю его), основанный на оценке схожести слов по количеству совпадающих букв идущих друг за другом. Примечание: буквы не обязательно идут *непосредственно* друг за другом, т.е. без других букв. Пример:
    Андрей vs. Андрей - 6
    ндрей vs. Андрей - 5
    Анрей vs. Андрей - 5
    Андрей vs. Александр - 4
    Андрей vs. Иннокентий - 2
    АнХрей vs. Андрей - 3, но в то же время с другими словами результат
будет на уровне 0..2
Andrew V. Fionik

O сохранении иконок 32хЗ2 в 256-цветном формате.

Автор: Владимир WEB-сайт: http://forum.vingrad.ru Суть вопроса: я столкнулся с проблемой сохранения полноцветных иконок, когда понадобилось немного изменить имеющиеся у меня для своих программ. Ни родной Image Editor от Delphi6, ни другие редакторы не смогли мне помочь. Могли это делать платные редакторы, но они не для нас. Начав разбираться, я обнаружил, что созданная функцией CreateIconIndirect иконка нормально выглядит, если после создания ее кинуть на форму, однако после записи Icon.SaveToFile иконка обезображивается. Это происходит на стадии записи иконки. Поискав информацию, облазив форумы, я понял, что либо этой проблемой никто не занимался, либо с ней все мирятся и заниматься не хотят, хотя интересующиеся имеются. Став заниматься проблемой вплотную, я решил использовать нетипизированные файлы, которым все равно, что в них записывают. Эти файлы хорошо подходят для работы с массивами байт и, если создать массив полностью соответствующий структуре иконки, то записав массив в нетипизированный файл, получим искомую иконку. Пришлось изучить структуру файла ICO и вот результат. Для использования процедуры нет необходимости изучать файл ICO, если только вы не хотите ничего изменять, например, размеры иконки. В принципе, можно сделать универсальную процедуру, которая бы определяла размер иконки и, в соответствии с размерами, создавала иконку. Все адреса и размеры, указанные в процедуре, являются абсолютными и изменению не подлежат, так как именно от них зависит, будет ли соответствовать создаваемый массив файлу ICO. В процедуру введено автоопределение количества цветов палитры, от этого зависит структура файла. Вам нет нужды указывать количество используемых цветов.

Кроме того, введено определение размера рисунка и если он не 32хЗ2, то процедура прекратит работу. Это произойдет и в случае если кому-то захочется использовать более 256 цветов. Тем, кто хочет узнать о структуре файлов ICO, читать после кода процедуры. Используя этот метод можно работать с любыми файлами, например, для открытия и побайтного изменения EXE-файлов. Для работы этой процедуры нужен рисунок 32хЗ2 на канве (у меня Image2), диалоговое окно SaveDialog1 (но не SavePictureDialog1) и все. Если нужно, измените имя канвы, оно встречается в двух строчках кода, и имя диалогового окна. Процедура с комментариями ниже:

//Записываем картинку как иконку (файл .ico)
{Так как стандартные функции не дают возможности создавать 256 цветные
иконки, мы пойдем другим путем, хоть он и медленнее и длиннее.
Создадим массив байт, который запишем как файл, это и будет
искомая иконка. Для этого создадим промежуточные массивы, иначе процедура
станет слишком навороченной, затем проверим количество используемых цветов
и, в зависимости от этого, создадим окончательный массив.}
procedure TForm1.ToolButton6Click(Sender: TObject);
var
  MAnd, MXor, MOsn, MCol: array of Byte;
  MPix, MOrg: array of TColor;
  a, b, c, n, m: integer;
  Bt, Bu, Indicator: Byte;
  p: TColor;
  k: boolean;
  F: file;
  LenOsn, LenXor, LenCol: integer;
begin
  if (Image2.Picture.Height <> 32) or (Image2.Picture.Width <> 32) then
  begin
    ShowMessage('Исходный рисунок не 32хЗ2');
    exit;
  end;
  {Создание массивов: MAnd - массив маски AND, MXor - массив маски Xor,
  основного массива MOsn - куда будет собираться вся информация,
  массив MPix - массив пикселов рисунка начиная с левого нижнего угла,
  MCol - массив таблицы цветов - палитры, MOrg - массив оригинальных цветов}
  {Заполнение массивов MPix и MAnd}
  {Активизация массивов}
  SetLength(MPix, 1024);
  SetLength(MAnd, 128);
  {Установка счетчиков}
  b := 0; //Счетчик битов
  a := 0; //Счетчик пикселов картинки
  c := 0; //Счетчик байтов маски MAnd
  Bt := 0; //Формируемый байт маски MAnd
  {Преобразование картинки в удобный формат, с началом от 0,
  и перенос ее в таком виде в массив MPix, меняя по пути белый -
  будущий прозрачный цвет - на черный, заодно заполняем массив MAnd}
  for m := 31 downto 0 do
    for n := 0 to 31 do
    begin
      MPix[a] := Image2.Canvas.Pixels[n, m];
      p := MPix[a];
      if p = RGB(255, 255, 255) then
      begin
        MPix[a] := RGB(0, 0, 0);
        if b = 0 then
          Bt := Bt + 128;
        if b = 1 then
          Bt := Bt + 64;
        if b = 2 then
          Bt := Bt + 32;
        if b = 3 then
          Bt := Bt + 16;
        if b = 4 then
          Bt := Bt + 8;
        if b = 5 then
          Bt := Bt + 4;
        if b = 6 then
          Bt := Bt + 2;
        if b = 7 then
          Bt := Bt + 1;
      end;
      b := b + 1;
      if b = 8 then
      begin
        MAnd[c] := Bt;
        Bt := 0;
        c := c + 1;
        b := 0;
      end;
      a := a + 1;
    end;
  {Заполнение маски MOrg нулями, предполагая 256 цветов}
  SetLength(MOrg, 256);
  for n := 0 to 255 do
    MOrg[n] := RGB(255, 255, 255);
  {Оцениваем количество цветов и от результата переделываем MOrg}
  a := 0; //Счетчик количества цветов
  for n := 0 to 1023 do
  begin
    p := MPix[n];
    k := false;
    for m := 0 to 255 do
    begin
      if k = false then
        if p = MOrg[m] then
          k := true;
    end;
    if k = false then
    begin
      if a > 255 then
      begin
        ShowMessage('Ваш рисунок имеет более 256 цветов');
        MOrg := nil;
        MPix := nil;
        MAnd := nil;
        exit;
      end;
      MOrg[a] := p;
      a := a + 1;
    end;
  end;
  LenOsn := 2238;
  LenXor := 1024;
  LenCol := 1024;
  Indicator := 1;
  if a < 15 then
  begin
    LenOsn := 766;
    LenXor := 512;
    Indicator := 0;
  end;
  {Заполняем маски нулями}
  SetLength(MXor, LenXor);
  SetLength(MCol, LenCol);
  for n := 0 to LenXor - 1 do
  begin
    MXor[n] := 0;
  end;
  for n := 0 to LenCol - 1 do
  begin
    MCol[n] := 0;
  end;
  {Заполнение массива MXor согласно массива MPix}
  Bu := 0;
  b := 0;
  c := 0;
  for n := 0 to 1023 do
  begin
    p := MPix[n];
    k := false;
    for Bt := 0 to 255 do
    begin
      if k = false then
        if p = MOrg[Bt] then
        begin
          k := true;
          if indicator = 1 then
            MXor[n] := Bt
          else if b = 1 then
          begin
            Bu := 16 * Bu + Bt;
            MXor[c] := Bu;
            c := c + 1;
            b := 0;
          end
          else
          begin
            Bu := Bt;
            b := 1;
          end;
        end;
    end;
  end;
  {Заполняем массив MCol согласно массива MOrg}
  begin
    a := 0;
    for n := 0 to 255 do
    begin
      p := MOrg[n];
      MCol[a] := GetBValue(p);
      MCol[a + 1] := GetGValue(p);
      MCol[a + 2] := GetRValue(p);
      a := a + 4;
    end;
  end;
  {Заполняем MOsn нулями}
  SetLength(MOsn, LenOsn);
  for n := 0 to LenOsn - 1 do
    MOsn[n] := 0;
  {Заполняем массив MOsn для 256 цветной иконки}
  if indicator = 1 then
  begin
    MOsn[2] := 1;
    MOsn[4] := 1;
    MOsn[6] := 32;
    MOsn[7] := 32;
    MOsn[14] := $A8;
    MOsn[15] := 8;
    MOsn[18] := $16;
    MOsn[22] := $28;
    MOsn[26] := 32;
    MOsn[30] := $40;
    MOsn[34] := 1;
    MOsn[36] := 8;
    MOsn[42] := $80;
    MOsn[43] := 4;
    MOsn[55] := 1;
    m := 0;
    for n := 62 to 1085 do
    begin
      MOsn[n] := MCol[m];
      m := m + 1;
    end;
    m := 0;
    for n := 1086 to 2109 do
    begin
      MOsn[n] := MXor[m];
      m := m + 1;
    end;
    m := 0;
    for n := 2110 to 2237 do
    begin
      MOsn[n] := MAnd[m];
      m := m + 1;
    end;
  end;
  {Заполняем массив MOsn для 16-цветной}
  if indicator = 0 then
  begin
    MOsn[2] := 1;
    MOsn[4] := 1;
    MOsn[6] := 32;
    MOsn[7] := 32;
    MOsn[8] := 4;
    MOsn[14] := $E8;
    MOsn[15] := 2;
    MOsn[18] := $16;
    MOsn[22] := $28;
    MOsn[26] := 32;
    MOsn[30] := 64;
    MOsn[34] := 1;
    MOsn[36] := 4;
    MOsn[43] := 2;
    MOsn[54] := 16;
    m := 0;
    for n := 62 to 125 do
    begin
      MOsn[n] := MCol[m];
      m := m + 1;
    end;
    m := 0;
    for n := 126 to 637 do
    begin
      MOsn[n] := MXor[m];
      m := m + 1;
    end;
    m := 0;
    for n := 638 to 765 do
    begin
      MOsn[n] := MAnd[m];
      m := m + 1;
    end;
  end;
  {Закрываем все массивы, кроме MOsn}
  MAnd := nil;
  MXor := nil;
  MOrg := nil;
  MCol := nil;
  MPix := nil;
  {Записываем массив MOsn в файл, для этого создаем нетипизированный файл,
  активизируем его и побайтно пишем в него данные из массива MOsn}
  if SaveDialog1.Execute then
  begin
    AssignFile(f, SaveDialog1.FileName + '.ico');
    Rewrite(f, 1);
    for n := 0 to LenOsn - 1 do
    begin
      Bt := MOsn[n];
      BlockWrite(f, Bt, 1);
    end;
    CloseFile(f);
    MOsn := nil;
  end;
end;
Существуют иконки более сложной структуры. Они могут содержать несколько рисунков. Я с ними не разбирался. Может быть кто-то из профессионалов скажет - примитивщина, нагородил. Ну что ж, может быть есть пути проще и лучше, но когда я спрашивал на форумах никто не смог помочь. Главное, работает без проблем. Я всего месяц как познакомился с Delphi и Паскалем, думаю можно кое-что и простить.

О структуре иконки. Информации немного в интернете, но кое-что я раскопал. Пришлось просмотреть коды разных иконок, чтобы иметь о них представление. Файл ICO очень похож на BMP: в начале файла заголовок BITMAPFILEHEADER, размером обычно 22 байта. В нем содержится информация о файле. За ним идет BITMAPINFOHEADER, 40 байт, содержит информацию о ширине и высоте иконки в пикселах, количество бит на пиксел в структуре картинки, занимаемое место таблицей цветов и другая информация. Для примера оба заголовка для 16-цветной иконки. В квадратных скобках номер байта в файле, далее эго значение.

[2]  := 1;   // Всегда 1 
[4]  := 1;   // Всегда 1 
[6]  := 32;  // Ширина и 
[7]  := 32;  // высота в пикселах? 
[8]  := 4    // Не знаю 
[14] := $E8; // Младший байт размера файла минус 22(размер BITMAPFILEHEADER) 
[15] := 2;   // Старший байт размера файла 
[18] := 22;  // Размер BITMAPFILEHEADER 
[22] := 40;  // Размер заголовка BITMAPINFOHEADER 
[26] := 32;  // Ширина в пикселах 
[30] := 64;  // Высота в пикселах обоих масок в сумме 
[34] := 1;   // Число плоскостей 
[36] := 4;   // бит/пиксел для таблицы пикселов 
[43] := 2;   // Старший байт размера таблицы пикселов (т.е.$200=512) 
[54] := 16;  // Число используемых цветов (не обязательно) 
Остальные не указанные байты и до адреса 62 все 0. После заголовков находится таблица цветов. Каждый цвет занимает 4 байта. По порядку - синий, зеленый, красный и нулевой байты. Первые 3 байта образуют цвет пиксела. То есть, каждый пиксел на иконке может иметь 256х256х256 оттенков. При 16 цветной иконке таблица цветов занимает 16хЧ=64 байта, при 256 цветной - 1024 байта. По идее, при записи иконки любая программа должна просканировать иконку и все найденные цвета записать в таблицу.Почему этого не происходит я не знаю. Windows эти цвета при записи игнорирует и устанавливает свои. Если же при 16 цветной палитре используются только, допустим, 6 цветов, то остальные 10 цветов не нужны, однако место для них остается, но оно не используется. Следующим блоком в файле идет таблица пикселов. Первый пиксел это самый нижний в левом углу. И переписываются они в таблицу построчно. Основной параметр, по которому таблица организуется, это количество бит на пиксел. Каждый элемент этой таблицы - это номер цвета в таблице цветов. При выводе на экран иконки берется очередной элемент, то есть номер цвета в таблице цветов, берет по номеру цвет из таблицы цветов и этот цвет выводится на экран. Так как для определения номера в таблице из 16 цветов достаточно 4 бит, то каждый байт таблицы пикселов определяет 2 пиксела. Причем, младшие 4 разряда относятся к первому, а старшие - к следующему пикселам. Для 256 цветов необходим полный байт. Поэтому, таблица пикселов занимает соответственно 512 и 1024 байтов. Таблица пикселов это образ маски XOR иконки, цвет, который должен быть прозрачным, заменен черным, в остальном же это рисунок, который вы создали в редакторе. И последним идет таблица маски AND. Это двухцветная маска вашего рисунка, имеет только белый цвет (бит включен) и черный цвет (бит равен 0). Белый цвет находится там, где будет прозрачный цвет, черный скрывает все остальное. При наложении двух масок друг на друга тот пиксел, у которого на маске AND белый, а на маске XOR черный цвет, будет невидим. Так как маска AND монохромная, то один байт содержит информацию о 8 пикселах, а размер маски будет 1024/8=128 байт. Имеем: для 16-цветной иконки размер 22+40+64+512+128=766 байт, для 256 - 22+40+1024+1024+128=2238 байт.

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

Сохранение и выдёргивание ресурсов в DLL или EXE.

В трамвае: - Девушка! А девушка! А вы наверно программистка? - Да, но как вы догадались?!! - У вас очень глупое лицо! - ДУРАК!!! - Да, я тоже программист...

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

Далее рассмотрим, как создать файл ресурсов, содержащий корию какого-либо файла. После создания такого файла его можно легко прицепить к Вашему проекту директивой {$R}. Файл ресурсов, который мы будем создавать имеет следующий формат:

заголовок заголовок для нашего RCDATA ресурса собственно данные - RCDATA ресурс

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

Заголовок ресурса выглядит следующим образом:

TResHeader = record
  DataSize: DWORD;        // размер данных
  HeaderSize: DWORD;      // размер этой записи
  ResType: DWORD;         // нижнее слово = $FFFF => ordinal
  ResId: DWORD;           // нижнее слово = $FFFF => ordinal
  DataVersion: DWORD;     // *
  MemoryFlags: WORD;
  LanguageId: WORD;       // *
  Version: DWORD;         // *
  Characteristics: DWORD; // *
end;

Поля помеченны звёздочкой Мы не будем использовать.

Приведённый код создаёт файл ресурсов и копирует его в данный файл:

procedure CreateResourceFile(
  DataFile, ResFile: string; // имена файлов
  ResID: Integer // id ресурсов
  );
var
  FS, RS: TFileStream;
  FileHeader, ResHeader: TResHeader;
  Padding: array [0..SizeOf(DWORD)-1] of Byte;
begin
  { Open input file and create resource file }
  FS := TFileStream.Create( // для чтения данных из файла
  DataFile, fmOpenRead);
  RS := TFileStream.Create( // для записи файла ресурсов
  ResFile, fmCreate);
  { Создаём заголовок файла ресурсов - все нули, за исключением
  HeaderSize, ResType и ResID }
  FillChar(FileHeader, SizeOf(FileHeader), #0);
  FileHeader.HeaderSize := SizeOf(FileHeader);
  FileHeader.ResId := $0000FFFF;
  FileHeader.ResType := $0000FFFF;
  { Создаём заголовок данных для RC_DATA файла
  Внимание: для создания более одного ресурса необходимо
  повторить следующий процесс, используя каждый раз различные
  ID ресурсов }
  FillChar(ResHeader, SizeOf(ResHeader), #0);
  ResHeader.HeaderSize := SizeOf(ResHeader);
  // id ресурса - FFFF означает "не строка!"
  ResHeader.ResId := $0000FFFF or (ResId shl 16);
  // тип ресурса - RT_RCDATA (from Windows unit)
  ResHeader.ResType := $0000FFFF
  or (WORD(RT_RCDATA) shl 16);
  // размер данных - есть размер файла
  ResHeader.DataSize := FS.Size;
  // Устанавливаем необходимые флаги памяти
  ResHeader.MemoryFlags := $0030;
  { Записываем заголовки в файл ресурсов }
  RS.WriteBuffer(FileHeader, sizeof(FileHeader));
  RS.WriteBuffer(ResHeader, sizeof(ResHeader));
  { Копируем файл в ресурс }
  RS.CopyFrom(FS, FS.Size);
  { Pad data out to DWORD boundary - any old
  rubbish will do!}
  if FS.Size mod SizeOf(DWORD) <> 0 then
    RS.WriteBuffer(Padding, SizeOf(DWORD) -
    FS.Size mod SizeOf(DWORD));
  { закрываем файлы } 
  FS.Free; 
  RS.Free;
end;

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

Извлечение ресурсов из EXE

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

Вся процедура заключается в создании потока ресурса, создании файлового потока и копировании из потока ресурса в поток файла.

procedure ExtractToFile(Instance:THandle; ResID:Integer; ResType, FileName:string);
var
  ResStream: TResourceStream;
  FileStream: TFileStream;
begin
  try
    ResStream := TResourceStream.CreateFromID(Instance, ResID, pChar(ResType));
    try
      //if FileExists(FileName) then
      //DeleteFile(pChar(FileName));
      FileStream := TFileStream.Create(FileName, fmCreate);
      try
        FileStream.CopyFrom(ResStream, 0);
      finally
        FileStream.Free;
      end;
    finally
      ResStream.Free;
    end;
  except
    on E:Exception do
    begin
      DeleteFile(FileName);
      raise;
    end;
  end;
end;

Всё, что требуется, это получить Instance exe-шника или dll (у Вашего приложения это Application.Instance или Application.Handle, для dll Вам придётся получить его самостоятельно :).

ResID тот же самый ID , который был присвоен ресурсу ResType: WAVEFILE, BITMAP, CURSOR, CUSTOM это типы ресурсов, с которыми возможно работать, но у меня получилось успешно проделать процедуру только с CUSTOM FileName это имя файла, который мы хотим создать из ресурса

Запись массива на диск.

Автор: Steve Schafer
Format винту не товарищ.

Скажем, ваша структура данных выглядит следующим образом:

type
  TMyRec = record
    SomeField: Integer;
    SomeOtherField: Double;
    TheRest: array[0..99] of Single;
  end;

И TBlobField имеет имя MyBlobField. TMyRec назван как MyRec. Для копирования содержимого MyRec в MyBlobField необходимо сделать следующее:

var
  Stream: TBlobStream;
begin
  Stream := TBlobStream.Create(MyBlobField, bmWrite);
  Stream.Write(MyRec, SizeOf(MyRec));
  Stream.Free;
end;

Есть другой путь:

var
  Stream: TBlobStream;
begin
  Stream := TBlobStream.Create(MyBlobField, bmRead);
  Stream.Read(MyRec, SizeOf(MyRec));
  Stream.Free;
end;

Сохранение массива c изображениями.

Автор: Michael Vincze Я решил проблему записи массива TBitmap в файл и его повторного чтения. Идея заключается в загрузке каждого TBitmap во временный TMemoryStream. Член TMemoryStream.Size информирует о размере данных, которые нужно сохранить на диске. Затем мы пишем размер и сопровождаем его данными типа TFileStream. Эту манипуляцию мы проделываем для каждого TBitmap в массиве. Для процедуры чтения сначала мы должны считать из потока размер данных TBitmap. Затем мы распределяем область для типа TMemoryStream полученного размера и считываем данные. Затем переписываем из TFileStream в TMemoryStream. И, наконец, мы читает из TMemoryStream сам TBitmap. Эту манипуляцию мы проделываем для каждого TBitmap в массиве. Ниже я привел код, который я реально использовал. Код из игры Bingo, которую я разрабатываю, имеет сетку 5x5, чьи ячейки содержат изображение.

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

procedure TMainForm.SaveBoard;
var
  MemoryStream: TMemoryStream;
  FileStream: TFileStream;
  Writer: TWriter;
  Buffer: Pointer;
  Size: Longint;
  Column: Integer;
  Row: Integer;
begin
  MemoryStream := TMemoryStream.Create;
  FileStream := TFileStream.Create(SaveFilename, fmCreate);
  Writer := TWriter.Create(FileStream, $1000);
  try
    for Column := 0 to 4 do
      for Row := 0 to 4 do
      begin
        MemoryStream.Clear;
        Bitmaps[Column, Row].SaveToStream(MemoryStream);
        Buffer := MemoryStream.Memory;
        Size := MemoryStream.Size;
        Writer.WriteInteger(Size);
        Writer.Write(Buffer^, Size);
      end;
  finally
    Writer.Free;
    FileStream.Free;
    MemoryStream.Free;
  end;
end;
procedure TMainForm.Open1Click(Sender: TObject);
var
  MemoryStream: TMemoryStream;
  FileStream: TFileStream;
  Buffer: Pointer;
  Reader: TReader;
  Column: Integer;
  Row: Integer;
  Size: Longint;
begin
  OpenDialog2.Filename := SaveFilename;
  if not OpenDialog2.Execute then
    Exit;
  MemoryStream := TMemoryStream.Create;
  FileStream := TFileStream.Create(OpenDialog2.Filename, fmOpenRead);
  Reader := TReader.Create(FileStream, $1000);
  try
    for Column := 0 to 4 do
      for Row := 0 to 4 do
      begin
        Size := Reader.ReadInteger;
        MemoryStream.SetSize(Size);
        Buffer := MemoryStream.Memory;
        Reader.Read(Buffer^, Size);
        Bitmaps[Column, Row].LoadFromStream(MemoryStream);
      end;
  finally
    Reader.Free;
    FileStream.Free;
    MemoryStream.Free;
  end;
  DrawGrid1.Repaint;
  SaveFilename := OpenDialog2.Filename;
  Caption := 'Bingo-создатель - ' + ExtractFilename(SaveFilename);
end;

Сохранить содержимое буфера обмена или потока как картинку.

Напрямую, конечно, нет. вставь в richedit и так:

procedure TForm1.Button1Click(Sender: TObject);
var
  b : tbitmap;
  fr: TFormatRange;
  r : TRect;
begin
  b:=tbitmap.create;
  b.width:=rxrichedit1.width;
  b.height:=rxrichedit1.height;
  r:=rect(0,0,RXRichEdit1.Width*screen.Pixelsperinch,
    RXRichEdit1.Height*screen.Pixelsperinch);
  fr.hdc:=b.Canvas.handle;
  fr.hdctarget:=b.Canvas.handle;
  fr.rc:=r;
  fr.rcpage:=r;
  fr.chrg.cpMin:=0;
  fr.chrg.cpMax:=-1;
  Sendmessage(RXRichEdit1.handle,EM_FORMATRANGE,1,longint(@fr));
  image1.Picture.assign(b);
  b.free;
end;

Сохранение компонента в файле.

Как мне сохранить а потом загрузить целый TListView (ViewStyle=vsReport) со всеми ячейками в файле?

var f: TFileStream;
begin
  f := TFileStream.Create('c:\stream.vcl', fmcreate);
  f.WriteComponent(lb);
  f.Free;
end;
...
var
  f: TFileStream;
begin
  f := TFileStream.Create('c:\stream.vcl', fmOpenRead);
  f.ReadComponent(lvFiles);
  f.Free;

Сохранение состояния ВСЕХ компонентов.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохраняет состояние !!!ВСЕХ!!! компонентов (втч и вложенных, втч TYesOrNoDialog)
на форме в реестр. Знает кучу классов. Очень удобен, напримр, для руссификации приложения
(Создаете *.reg файл, с переведенными property text, caption и т.д.)
TStateSaver.RegistryPath:String - то мето в реестре, куда сохранять.
.WriteTop, .WriteLeft, .WriteHeight, ... :Boolean - сохранять ли соответствующее
Property (нет динамического списка, который можно было бы реализовать, используя
GetFieldAddress, по причине невозможности определить состояние ReadOnly :( )
property OnNewComponentSaving:TSaverEvent read _ONNC write _ONNC;
property OnNewComponentLoading:TSaverEvent read _ONNCL write _ONNCL; - еvеnt'ы
вызывающиеся при сохранении/загрузки состояния какого-то компонента.
Параметр DoIt:Boolean - сохранить/загрузить или нет.
procedure SaveComponentState(C:TComponent; preffix, postfix:String);
procedure LoadComponentState(C:TComponent; preffix, postfix:String);
-сохранить/загрузить состояние всех под-компонентов компонента C.
preffix и postfic - префикс и постфикс имени при сохранении в реестр.
Зависимости: Windows, Messages, SysUtils, Classes, Registry, Dialogs, Controls,
StdCtrls, ExtCtrls, Buttons, UBPFD.YesOrNoDialog, Menus;
Автор:       Святослав, lisin@asicdesign.ru, ICQ:138752432, Saint Petersburg
Copyright:   (C) NetBreaker666[AWD]<SP666>@Svjatoslav_Lisin - т.е. я сам
Дата:        11 августа 2002 г.
***************************************************** }
unit StateSaver;
interface
uses
  Windows, Messages, SysUtils, Classes, Registry, Dialogs, Controls, StdCtrls,
    ExtCtrls, Buttons, YesOrNoDialog, Menus;
type
  TSaverEvent = procedure(Sender: TObject; Target: TComponent; var DoIt: Boolean)
    of object;
  TStateSaver = class(TComponent)
  private
    { Private declarations }
    RegPath: string;
    RegRoot: string;
    RegRootHKEY: HKEY;
    WTOP, WLEFT, WWIDTH, WHEIGHT, WTAG: Boolean;
    WCAPTION, WTEXT, WCOLOR: Boolean;
    WEnabled, WVisible, WChecked: Boolean;
    _ONNC, _ONNCL: TSaverEvent;
    procedure SetRegRoot(S: string);
    procedure SetRegRootHKEY(HK: HKEY);
  protected
    { Protected declarations }
  public
    { Public declarations }
  published
    { Published declarations }
    property RegistryRoot: string read RegROOT write SetRegROOT;
    property RegistryRootHKEY: HKey read RegRootHKEY write SetRegRootHKEY default
      HKEY_CURRENT_USER;
    property RegistryPath: string read RegPath write RegPath;
    property WriteTop: Boolean read WTOP write WTOP;
    property WriteLeft: Boolean read WLeft write WLeft;
    property WriteWidth: Boolean read WWIDTH write WWidth;
    property WriteHeight: Boolean read WHEIGHT write WHeight;
    property WriteCaption: Boolean read WCaption write WCaption;
    property WriteText: Boolean read WText write WText;
    property WriteColor: boolean read WColor write WColor;
    property WriteTag: Boolean read WTAG write WTag;
    property WriteEnabled: Boolean read WEnabled write WEnabled;
    property WriteVisible: Boolean read WVisible write WVisible;
    property WriteChecked: Boolean read WChecked write WChecked;
    property OnNewComponentSaving: TSaverEvent read _ONNC write _ONNC;
    property OnNewComponentLoading: TSaverEvent read _ONNCL write _ONNCL;
    procedure SaveComponentState(C: TComponent; preffix, postfix: string);
    procedure LoadComponentState(C: TComponent; preffix, postfix: string);
  end;
  TUPC = class(TControl)
  public
    property Color;
    property Caption;
    property Text;
  end;
procedure Register;
implementation
procedure Register;
begin
  RegisterComponents('NetBreakers', [TStateSaver]);
end;
procedure TStateSaver.SetRegRoot(S: string);
begin
  S := UpperCase(S);
  if S = 'HKEY_LOCAL_MACHINE' then
  begin
    RegRootHKEY := HKEY_LOCAL_MACHINE;
    RegRoot := S;
    Exit;
  end;
  if S = 'HKEY_CURRENT_USER' then
  begin
    RegRootHKEY := HKEY_CURRENT_USER;
    RegRoot := S;
    Exit;
  end;
  if S = 'HKEY_CLASSES_ROOT' then
  begin
    RegRootHKEY := HKEY_CLASSES_ROOT;
    RegRoot := S;
    Exit;
  end;
  if S = 'HKEY_USERS' then
  begin
    RegRootHKEY := HKEY_USERS;
    RegRoot := S;
    Exit;
  end;
  if S = 'HKEY_PERFORMANCE_DATA' then
  begin
    RegRootHKEY := HKEY_PERFORMANCE_DATA;
    RegRoot := S;
    Exit;
  end;
  if S = 'HKEY_CURRENT_CONFIG' then
  begin
    RegRootHKEY := HKEY_CURRENT_CONFIG;
    RegRoot := S;
    Exit;
  end;
  if S = 'HKEY_DYN_DATA' then
  begin
    RegRootHKEY := HKEY_DYN_DATA;
    RegRoot := S;
    Exit;
  end;
  ShowMessage('Invalid registry key.');
end;
procedure TStateSaver.SetRegRootHKEY(HK: HKEY);
begin
  case HK of
    HKEY_LOCAL_MACHINE:
      begin
        RegRoot := 'HKEY_LOCAL_MACHINE';
      end;
    HKEY_CURRENT_USER:
      begin
        RegRoot := 'HKEY_CURRENT_USER';
      end;
    HKEY_CLASSES_ROOT:
      begin
        RegRoot := 'HKEY_CLASSES_ROOT';
      end;
    HKEY_USERS:
      begin
        RegRoot := 'HKEY_USERS';
      end;
    HKEY_PERFORMANCE_DATA:
      begin
        RegRoot := 'HKEY_PERFORMANCE_DATA';
      end;
    HKEY_CURRENT_CONFIG:
      begin
        RegRoot := 'HKEY_CURRENT_CONFIG';
      end;
    HKEY_DYN_DATA:
      begin
        RegRoot := 'HKEY_DYN_DATA';
      end;
  else
    begin
      ShowMessage('Unknown registry key.');
      Exit;
    end;
  end;
  RegRootHKEY := HK;
end;
procedure TStateSaver.SaveComponentState(C: TComponent; preffix, postfix:
  string);
var
  T: TControl;
  R: TRegistry;
  I: Integer;
  CC: Boolean;
begin
  CC := True;
  if Assigned(_ONNC) then
    _ONNC(self, C, CC);
  if CC then
  begin
    if C is TControl then
    begin
      T := C as TControl;
      R := TRegistry.Create;
      R.RootKey := RegRootHKEY;
      if R.OpenKey(RegPath, True) then
      begin
        try
          if WTOP then
            R.WriteInteger(preffix + C.GetNamePath + '.TOP' + postfix, T.Top);
        except
        end;
        try
          if WEnabled then
            R.WriteBool(preffix + C.GetNamePath + '.Enabled' + postfix,
              T.Enabled);
        except
        end;
        try
          if WVisible then
            R.WriteBool(preffix + C.GetNamePath + '.TOP' + postfix, T.Visible);
        except
        end;
        try
          if WLEFT then
            R.WriteInteger(preffix + C.GetNamePath + '.LEFT' + postfix, T.Left);
        except
        end;
        try
          if WTAG then
            R.WriteInteger(preffix + C.GetNamePath + '.TAG' + postfix, T.Tag);
        except
        end;
        try
          if WHEIGHT then
            R.WriteInteger(preffix + C.GetNamePath + '.HEIGHT' + postfix,
              T.Height);
        except
        end;
        try
          if WWIDTH then
            R.WriteInteger(preffix + C.GetNamePath + '.WIDTH' + postfix,
              T.Width);
        except
        end;
        if WTEXT then
        begin
          try
            R.WriteString(preffix + C.GetNamePath + '.Text' + postfix,
              TUPC(T).Text);
          except
            try
              if T is TCustomEdit then
                R.WriteString(preffix + C.GetNamePath + '.Text' + postfix,
                  TCustomEdit(T).Text);
            except
            end;
          end;
        end;
        if WCOLOR then
        begin
          try
            R.WriteInteger(preffix + C.GetNamePath + '.Color' + postfix,
              Integer(TUPC(T).color));
          except
          end;
        end;
        if WCAPTION then
        begin
          try
            R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
              TUPC(T).caption);
          except
            try
              if T is TButton then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TButton(T).Caption);
              if T is TCustomLabel then
                R.WriteString(preffix + C.GetNamePath + '.caption' + postfix,
                  TCustomLabel(T).Caption);
              if T is TCheckBox then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TCheckBox(T).Caption);
              if T is TRadioButton then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TRadioButton(T).Caption);
              if T is TGroupBox then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TGroupBox(T).Caption);
              if T is TRadioGroup then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TRadioGroup(T).Caption);
              if T is TPanel then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TPanel(T).Caption);
              if T is TSpeedButton then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TSpeedButton(T).Caption);
              if T is TStaticText then
                R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
                  TStaticText(T).Caption);
            except
            end;
          end;
        end;
      end
      else
      begin
        //ShowMessage('Couldn''t open key "'+RegPath+'".');
        Exit;
      end;
      R.Free;
    end
    else
    begin
      if C is TYesOrNoDialog then
      begin
        R := TRegistry.Create;
        R.RootKey := RegRootHKEY;
        if R.OpenKey(RegPath, True) then
        begin
          if WCaption then
            R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
              TYesOrNoDialog(C).caption);
          if WText then
            R.WriteString(preffix + C.GetNamePath + '.Text' + postfix,
              TYesOrNoDialog(C).Text);
        end;
        R.Free;
      end;
      if C is TPopupMenu then
      begin
        R := TRegistry.Create;
        R.RootKey := RegRootHKEY;
        if R.OpenKey(RegPath, True) then
        begin
          for I := 0 to TPopupMenu(C).Items.Count - 1 do
          begin
            if WCaption then
              R.WriteString(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Caption' + postfix, TPopupMenu(C).Items[I].caption);
            if WEnabled then
              R.WriteBool(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Enabled' + postfix, TPopupMenu(C).Items[I].Enabled);
            if WVisible then
              R.WriteBool(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Visible' + postfix, TPopupMenu(C).Items[I].Visible);
            if WChecked then
              R.WriteBool(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Checked' + postfix, TPopupMenu(C).Items[I].Checked);
          end;
        end;
        R.Free;
      end;
    end;
  end;
  for I := 0 to C.ComponentCount - 1 do
    SaveComponentState(C.Components[i], preffix + C.GetNamePath + '.', postfix);
end;
procedure TStateSaver.LoadComponentState(C: TComponent; preffix, postfix:
  string);
var
  T: TControl;
  R: TRegistry;
  I: Integer;
  CC: Boolean;
begin
  CC := True;
  if Assigned(_ONNCL) then
    _ONNCL(self, C, CC);
  if CC then
  begin
    if C is TControl then
    begin
      T := C as TControl;
      R := TRegistry.Create;
      R.RootKey := RegRootHKEY;
      if R.OpenKey(RegPath, False) then
      begin
        try
          if WTOP then
            if R.ValueExists(preffix + C.GetNamePath + '.TOP' + postfix) then
              T.Top := R.ReadInteger(preffix + C.GetNamePath + '.TOP' +
                postfix);
        except
        end;
        try
          if WEnabled then
            if R.ValueExists(preffix + C.GetNamePath + '.Enabled' + postfix)
              then
              T.Enabled := R.ReadBool(preffix + C.GetNamePath + '.Enabled' +
                postfix);
        except
        end;
        try
          if WVisible then
            if R.ValueExists(preffix + C.GetNamePath + '.TOP' + postfix) then
              T.Visible := R.ReadBool(preffix + C.GetNamePath + '.TOP' +
                postfix);
        except
        end;
        try
          if WLEFT then
            if R.ValueExists(preffix + C.GetNamePath + '.LEFT' + postfix) then
              T.Left := R.ReadInteger(preffix + C.GetNamePath + '.LEFT' +
                postfix);
        except
        end;
        try
          if WTAG then
            if R.ValueExists(preffix + C.GetNamePath + '.TAG' + postfix) then
              T.Tag := R.ReadInteger(preffix + C.GetNamePath + '.TAG' +
                postfix);
        except
        end;
        try
          if WHEIGHT then
            if R.ValueExists(preffix + C.GetNamePath + '.HEIGHT' + postfix) then
              T.Height := R.ReadInteger(preffix + C.GetNamePath + '.HEIGHT' +
                postfix);
        except
        end;
        try
          if WWIDTH then
            if R.ValueExists(preffix + C.GetNamePath + '.WIDTH' + postfix) then
              T.Width := R.ReadInteger(preffix + C.GetNamePath + '.WIDTH' +
                postfix);
        except
        end;
        if WTEXT then
          if R.ValueExists(preffix + C.GetNamePath + '.Text' + postfix) then
          begin
            try
              TUPC(T).Text := R.ReadString(preffix + C.GetNamePath + '.Text' +
                postfix);
            except
              try
                if T is TCustomEdit then
                  TCustomEdit(T).Text := R.ReadString(preffix + C.GetNamePath +
                    '.Text' + postfix);
              except
              end;
            end;
          end;
        if WCOLOR then
          if R.ValueExists(preffix + C.GetNamePath + '.Color' + postfix) then
          begin
            try
              TUPC(T).Color := R.ReadInteger(preffix + C.GetNamePath + '.Color'
                + postfix);
            except
            end;
          end;
        if WCaption then
          if R.ValueExists(preffix + C.GetNamePath + '.Caption' + postfix) then
          begin
            try
              TUPC(T).Caption := R.ReadString(preffix + C.GetNamePath +
                '.Caption' + postfix);
            except
              try
                if T is TButton then
                  TButton(T).Caption := R.ReadString(preffix + C.GetNamePath +
                    '.Caption' + postfix);
                if T is TCustomLabel then
                  TCustomLabel(T).Caption := R.ReadString(preffix + C.GetNamePath
                    + '.caption' + postfix);
                if T is TCheckBox then
                  TCheckBox(T).Caption := R.ReadString(preffix + C.GetNamePath +
                    '.Caption' + postfix);
                if T is TRadioButton then
                  TRadioButton(T).Caption := R.ReadString(preffix + C.GetNamePath
                    + '.Caption' + postfix);
                if T is TGroupBox then
                  TGroupBox(T).Caption := R.ReadString(preffix + C.GetNamePath +
                    '.Caption' + postfix);
                if T is TRadioGroup then
                  TRadioGroup(T).Caption := R.ReadString(preffix + C.GetNamePath
                    + '.Caption' + postfix);
                if T is TPanel then
                  TPanel(T).Caption := R.ReadString(preffix + C.GetNamePath +
                    '.Caption' + postfix);
                if T is TSpeedButton then
                  TSpeedButton(T).Caption := R.ReadString(preffix + C.GetNamePath
                    + '.Caption' + postfix);
                if T is TStaticText then
                  TStaticText(T).Caption := R.ReadString(preffix + C.GetNamePath
                    + '.Caption' + postfix);
              except
              end;
            end;
          end;
      end;
      R.Free;
    end
    else
    begin
      if C is TYesOrNoDialog then
      begin
        R := TRegistry.Create;
        R.RootKey := RegRootHKEY;
        if R.OpenKey(RegPath, False) then
        begin
          if WCaption then
            if R.ValueExists(preffix + C.GetNamePath + '.Caption' + postfix)
              then
              TYesOrNoDialog(C).caption := R.ReadString(preffix + C.GetNamePath +
                '.Caption' + postfix);
          if WText then
            if R.ValueExists(preffix + C.GetNamePath + '.Text' + postfix) then
              TYesOrNoDialog(C).text := R.ReadString(preffix + C.GetNamePath +
                '.Text' + postfix);
        end;
        R.Free;
      end;
      if C is TPopupMenu then
      begin
        R := TRegistry.Create;
        R.RootKey := RegRootHKEY;
        if R.OpenKey(RegPath, True) then
        begin
          for I := 0 to TPopupMenu(C).Items.Count - 1 do
          begin
            if WCaption then
              if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Caption' + postfix) then
                TPopupMenu(C).Items[I].caption := R.ReadString(preffix +
                  C.GetNamePath + '.Item[' + IntToStr(I) + '].Caption' + postfix);
            if WEnabled then
              if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Enabled' + postfix) then
                TPopupMenu(C).Items[I].Enabled := R.ReadBool(preffix +
                  C.GetNamePath + '.Item[' + IntToStr(I) + '].Enabled' + postfix);
            if WVisible then
              if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Visible' + postfix) then
                TPopupMenu(C).Items[I].Visible := R.ReadBool(preffix +
                  C.GetNamePath + '.Item[' + IntToStr(I) + '].Visible' + postfix);
            if WChecked then
              if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
                '].Checked' + postfix) then
                TPopupMenu(C).Items[I].Checked := R.ReadBool(preffix +
                  C.GetNamePath + '.Item[' + IntToStr(I) + '].Checked' + postfix);
          end;
        end;
        R.Free;
      end;
    end;
  end;
  for I := 0 to C.ComponentCount - 1 do
    LoadComponentState(C.Components[i], preffix + C.GetNamePath + '.', postfix);
end;
end.

Сохранение компонента полностью со всеми свойствами в БД.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение компонента полностью, со всеми свойствами в БД.
Данная функция использовалась и используется для сохранения отчетов,
формируемые пользователем, в базу данных. БД используется: Informix и Oracle,
под другие БД не пробовал. Отчеты строились в ReportBuilder5.5.
Для других компонентов не пробывал использовать
Зависимости: стандартные
Автор:       Z-man, nik7777@land.ru, ICQ:160236098, Astana
Copyright:   Z-man ®
Дата:        28 мая 2002 г.
***************************************************** }
function ComponentToString(Component: TComponent): string;
var
  BinStream: TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  BinStream := TMemoryStream.Create;
  try
    StrStream := TStringStream.Create(s);
    try
      BinStream.WriteComponent(Component);
      BinStream.Seek(0, soFromBeginning);
      ObjectBinaryToText(BinStream, StrStream);
      StrStream.Seek(0, soFromBeginning);
      Result := StrStream.DataString;
    finally
      StrStream.Free;
    end;
  finally
    BinStream.Free
  end;
end;

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

OraTReport.FieldByName('report').AsString := ComponentToString(rbReport);
//* где: *//
//* rbReport - ReportBuilder5 (желтый отчет)*//
//* OraTReport - TOraTable из пакета компонентов ODAC (визуальные *//
//* компоненты для прямого доступа к Oracle)*//

Сохранение данных в Clipboard.

Автор: Vladimir Timonin [Q:]Мне нужно использовать clipboard для сохранения данных в собственном формате и я хочу для этого написать набор процедур ввода/вывода с использованием потоков (streams). Возможно ли создать объект TMemoryStream, эаполнить его и поместить в Clipboard?

[A:]Не только возможно, именно так поступают функции Clipboard.GetComponent и Clipboard.SetComponent. Сначала вы должны зарегистрировать свой собственный формат данных для Clipboard с помощью функции RegisterClipboardFormat:

     CF_MYFORMAT := RegisterClipboardFormat('My Format Description');
Далее вы должны выполнить шаги: 1. Создать поток (memory stream) и записать туда данные. 2. Создать глобальный буфер в памяти и скопировать поток туда. 3. Вызвать Clipboard.SetAsHandle(), чтобы поместить буфер в Clipboard.

Пример:

     var
       hBuf: THandle;
       Bufptr: Pointer;
       MStream: TMemoryStream;
     begin
       MStream := TMemoryStream.Create;
       try
       { write your data to the stream }
         hBuf := GlobalAlloc(GMEM_MOVEABLE, MStream.Size);
         try
           BufPtr := GlobalLock(hBuf);
           try
             Move(MStream.Memory^, BufPtr^, MStream.Size);
             Clipboard.SetAsHandle(CF_MYFORMAT, hBuf);
           finally
             GlobalUnlock(hBuf);
           end;
         except
           GlobalFree(hBuf);
           raise;
         end;
       finally
         MStream.Free;
       end;
     end;
Внимание: не уничтожайте буфер, созданный с GlobalAlloc. Поскольку вы поместили его в Clipboard, это уже дело сliрbоаrd'а его уничтожить. Опять же, получая буфер из Clipboard, не уничтожайте этот буфер - просто сделайте копию содержимого.

Для обратного получения потока и данных, сделайте что-нибудь вроде этого:

     var
       hBuf: THandle;
       BufPtr: Pointer;
       MStream: TMemoryStream;
     begin
       hBuf := Clipboard.GetAsHandle(CF_MYFORMAT);
       if hBuf <> 0 then
       begin
         BufPtr := GlobalLock(hBuf);
         if BufPtr <> nil then
         try
           MStream := TMemoryStream.Create;
           try
             MStream.WriteBuffer(BufPtr^, GlobalSize(hBuf));
             MStream.Position := 0;
           { read your data from the stream }
           finally
             MStream.Free;
           end;
         finally
           GlobalUnlock(hBuf);
         end;
       end;
     end;

Сохранить порядок колонок в DBGrid, после того как пользовать их перестроил.

РЕКЛАМА!!! Телефонный звонок в 2 часа ночи. Сонный мужик подходит к телефону: - АЛЛО?!!! Тишина. Кладет трубку. Ровно через 45 секунд опять звонок. - АЛЛО?!!!! Тишина. И так повторяентся 74 раза с регулярностью 45 секунд. Мужик: - ПИИИИИИИи (длинный продолжительный писк, которым на телевидении заглушают матные слова) На другом конце провода довольный СисОп с лыбой во всю широту натуры, обнимающий небольшую коробочку. Голос за кадром: - Делай всё, что тебе нравится вместе с модемом ZyXel!!!
procedure TMainForm.NewIni(const NomeIni: string);
var F: System.Text;
    i: Byte;
begin
  System.Assign(F, NomeIni);
  System.ReWrite(F);
  System.WriteLn(F, '[Campi_Ordine]');
  for i:=1 to Table1.FieldCount do
    System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].FieldName);
  System.WriteLn(F, '');
  System.WriteLn(F, '[Campi_Size]');
  for i:=1 to Table1.FieldCount do
    System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].DisplayWidth);
  System.Close(F);
end;
procedure TMainForm.SaveIni(const FN: String);
var Ini: TIniFile;
    i: Integer;
begin
  NewIni(FN);
  Ini := TIniFile.Create(FN);
  with Ini do
  begin
    for i:=1 to Table1.FieldCount do
    begin
      S:= Table1.Fields[i-1].FieldName;
      WriteString('Campi_Ordine', 'Campo'+IntToStr(i),
        Table1.Fields[i-1].FieldName);
      WriteInteger('Campi_Size', 'Campo'+IntToStr(i),
        Table1.Fields[i-1].DisplayWidth);
    end;
  end;
  Ini.Free;
end;
procedure TMainForm.LoadIni(const FN: String);
var Ini: TIniFile;
    i: Integer;
    j: Longint;
    S: String;
    function MyReadInteger(const Section, Ident: string): Longint;
    begin
      result := Ini.ReadInteger(Section, Ident, -1);
      if result=-1 then
        raise Exception.Create('Errore nel file di configurazione.');
    end;
    function MyReadString(const Section, Ident: string): String;
    begin
      result := Ini.ReadString(Section, Ident, '');
      if result='' then
        raise Exception.Create('Errore nel file di configurazione.');
    end;
begin
  Ini := TIniFile.Create(FN);
  try
    with Ini do
    begin
      for i:=1 to Table1.FieldCount do
      begin
        S:= MyReadString('Campi_Ordine', 'Campo'+IntToStr(i));
        j:= MyReadInteger('Campi_Size', 'Campo'+IntToStr(i));
        Table1.FieldByName(S).Index := i-1;
        Table1.FieldByName(S).DisplayWidth := j;
      end;
    end;
  finally
    Ini.Free;
  end;
end; 

Сохранить Excel файл как текстовый.

uses 
  ComObj; 
function ExcelSaveAsText(ExcelFile, TextFile: TFileName): Boolean; 
const 
  xlText = -4158; 
var 
  ExcelApp: OleVariant; 
  vTemp1, vTemp2, vTemp3: OLEVariant; 
begin 
  Result := False; 
  try 
    ExcelApp := CreateOleObject('Excel.Application'); 
  except 
    // Fehler beim цffnеn von Excel... 
    // Error occured... 
    Exit; 
  end; 
  try 
    ExcelApp.Workbooks.Open(ExcelFile); 
    ExcelApp.DisplayAlerts := False; 
    vTemp3 := False; 
    vTemp2 := xlText; 
    vTemp1 := TextFile;
    ExcelApp.ActiveWorkbook.SaveAs(vTemp1, vTemp2, vTemp3); 
    Result := True; 
  finally 
    ExcelApp.Quit; 
    ExcelApp := Unassigned; 
  end; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ExcelSaveAsText('C:\YouExcelFile.xls','C:\YourTextFile.txt'); 
end;

Сохранение размеров, позиции и состояния окна.

Приведу код WindowRestorer - восстановителя размера и состояния окна. ОПИСАНИЕ: Вы наверное замечали, что профессионально написанные программы "запоминают" состояние и позицию окон с момента их последнего показа? А большинство RAD-приложений это игнорируют? Вы можете исправить эту ошибку, взяв на вооружение этот модуль. Он позволяет сохранять позицию, размер и состояние окна, поэтому пользователь, открыв его вновь, увидит его в том же состоянии, в котором он видел его в последний раз. ИСПОЛЬЗОВАНИЕ: Поместите WINRSTOR в список используемых модулей главной или любой другой формы, состояние, размер и позицию которой вы хотите сохранить. (Если вы сэкономить время и для восстановления дочерних форм использовать WinSaveChildren и WinRestoreChildren из главной формы, вы должны объявить этот модуль только в главной форме.)

В MainForm.Create, инициализируйте глобальный объект WinRestorer следующим образом (он должен предварительно быть объявлен, но еще не инициализирован):

GlobalWinRestorer := TWinRestorer.create( Application, TRUE, WHATSAVE_ALL);

Или так:

GlobalWinRestorer := TWinRestorer.create( Application, TRUE, [location, size, state]);

Затем в MainForm.Destroy вы должны разрушить глобальный объект WinRestorer следующим образом:

GlobalWinRestorer.free; 

Хорошее место для сохранения статуса формы - в обработчике события queryclose или в специально созданной кнопке или пункте меню. Я обычно создаю этот пункт в меню "Файл" с именем '&Сохранение рабочей области' и обрабатываю следующим образом:

GlobalWinRestorer.SaveChildren(Self, [default]);

И при закрытии основной формы необходимо сделать следующее:

GlobalWinRestorer.SaveWin(Self, [WHATSAVE_ALL]);

Восстановить состояние дочерних форм можно следующим образом:

GlobalWinRestorer.RestoreWin(Self, [default]);

Я же переместил данный код в обработчик события show моей главной формы:

GlobalWinRestorer.RestoreWin(Self, [default]);
GlobalWinRestorer.RestoreChildren(Self, [default]);

Подсказки: Если вы установили TForm.Position в poScreenCenter или что-то подобное, данный модуль вам не поможет. poDesigned кажется, работает как положено. Можно добавить обработку исключения, если вы пытаетесь установить верхнюю или левую позицию при значении формы poScreenCentere, но при этом вы должны быть осторожными при использовании WinRestoreChildren. Я не проверял это со значениями координат (позиции) и оставил это на усмотрение разработчиков.

unit WinRstor;
interface
uses SysUtils, Forms;
type
  {=============================================================}
  {------------------------------------------------------------------
  Восстановитель окон классовых объектов и связанных типов.
  -------------------------------------------------------------------}
  EWinRestorer = class(Exception);
  TWhatSave = (default, size, location, state);
  STWhatSave = set of TWhatSave;
  TWinRestorer = class(TObject)
  protected
    mIniFile: string;
    mIniSect: string[80];
    mIsInitialized: boolean;
    mDefaultWhat: STWhatSave;
  public
    constructor Create(TheApp: TApplication;
      LocalDir: boolean; DefaultWhatSave: STWhatSave);
    {Если localDir = true, каталог ini = каталогу приложения.
    Else, ini dir is the windows dir.}
    procedure SaveWin(TheForm: TForm; What: STWhatSave);
    procedure SaveChildren(TheMDIForm: TForm; What: STWhatSave);
    procedure RestoreWin(TheForm: TForm; What: STWhatSave);
    procedure RestoreChildren(TheMDIForm: TForm; What: STWhatSave);
    property IniFileName: string read mIniFile;
  end;
const
  WHATSAVE_ALL = [size, location, state];
var
  GlobalWinRestorer: TWinRestorer;
implementation
uses IniFiles;
constructor TWinRestorer.create;
var
  fname, path: string[100];
begin
  inherited create;
  {Получаем имя ini-файла}
  if default in DefaultWhatSave then
    raise EWinRestorer.create(
      'Попытка инициализации параметров с позицией окна по умолчанию ' +
      ' с установленным элементом [default]. ' +
      'Параметры по умолчанию могут содержать только установленные элементы -
      [size, location, state]. ')
  else
    mDefaultWhat := DefaultWhatSave;
  fname := ChangeFileExt(ExtractFileName(TheApp.exeName), '.INI');
  if LocalDir then
  begin {вычисляем путь и добавляем к нему имя файла}
    path := ExtractFilePath(TheApp.exeName);
    if path[length(path)] <> '\' then
      path := path + '\';
    fname := path + fname;
  end;
  {заполняем поля объекта}
  mIniFile := fname;
  mIniSect := 'WindowsRestorer';
  {Для культуры напишем некоторое примечание
  в секцию с именем [WinRestorer Notes]}
end;
procedure TWinRestorer.RestoreWin;
var
  FormNm, SectionNm: string[80];
  ini: TIniFile;
  n, l, t, w, h: integer; {Left, Top Width, Height}
begin
  ini := TIniFile.create(mIniFile);
  try
    SectionNm := mIniSect;
    FormNm := TheForm.classname;
    if default in What then
      What := mDefaultWhat;
    {При необходимости обновляем состояние окна}
    if state in What then
      n := ini.ReadInteger(SectionNm, FormNm + '_WindowState', 0);
    case n of
      1: TheForm.WindowState := wsMinimized;
      2: TheForm.WindowState := wsNormal;
      3: TheForm.WindowState := wsMaximized;
    end;
    {При необходимости обновляем размеры и позицию.}
    with TheForm do
    begin
      l := left;
      t := top;
      h := height;
      w := width;
    end; {Сохраняем текущие значения.}
    if size in What then
    begin
      w := ini.ReadInteger(SectionNm, FormNm + '_Width', w);
      h := ini.ReadInteger(SectionNm, FormNm + '_Height', h);
    end;
    if location in What then
    begin
      t := ini.ReadInteger(SectionNm, FormNm + '_Top', t);
      l := ini.ReadInteger(SectionNm, FormNm + '_Left', l);
    end;
    TheForm.SetBounds(l, t, w, h);
  finally
    ini.free;
  end;
end;
procedure TWinRestorer.RestoreChildren;
var
  i: integer;
begin
  if TheMDIForm.formstyle <> fsMDIForm then
    raise
      EWinRestorer.create('Попытка сохранения размеров дочернего
        окна для не-MDI окна родителя.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      RestoreWin(TheMDIForm.MDIChildren[i], what);
end;
procedure TWinRestorer.SaveWin;
var
  FormNm, SectionNm: string[80];
  w: STWhatsave;
  ini: TIniFile;
begin
  ini := TIniFile.create(mIniFile);
  try
    SectionNm := mIniSect;
    FormNm := TheForm.ClassName;
    if default in What then
      w := mDefaultWhat
    else
      w := mDefaultWhat;
    if size in w then
    begin
      ini.WriteInteger(SectionNm, FormNm + '_Width', TheForm.Width);
      ini.WriteInteger(SectionNm, FormNm + '_Height', TheForm.Height);
    end;
    if location in w then
    begin
      ini.WriteInteger(SectionNm, FormNm + '_Top', TheForm.Top);
      ini.WriteInteger(SectionNm, FormNm + '_Left', TheForm.Left);
    end;
    if state in w then
      case TheForm.WindowState of
        wsMinimized: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 1);
        wsNormal: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 2);
        wsMaximized: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 3);
      end;
  finally
    ini.free;
  end;
end;
procedure TWinRestorer.SaveChildren;
var
  i: integer;
begin
  if TheMDIForm.formstyle <> fsMDIForm then
    raise
      EWinRestorer.create('Попытка восстановления размеров дочернего
        окна для не-MDI окна родителя.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      SaveWin(TheMDIForm.MDIChildren[i], what);
end;
initialization
end.

Сохранение в файле и загрузка из файла формы с компонентами.

Статьи по Delphi (015)…
unit Cref_f;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes,
  Graphics, Controls, Forms, Dialogs, StdCtrls,
  ExtCtrls, Menus;
type
  CRefType = class of TControl;
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Exit1: TMenuItem;
    N1: TMenuItem;
    SaveAs1: TMenuItem;
    Open1: TMenuItem;
    New1: TMenuItem;
    Help1: TMenuItem;
    About1: TMenuItem;
    Panel1: TPanel;
    RadioRadioButton: TRadioButton;
    ButtonRadioButton: TRadioButton;
    EditRadioButton: TRadioButton;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    SaveAs2: TMenuItem;
    N2: TMenuItem;
    SaveDialog2: TSaveDialog;
    MemoRadioButton: TRadioButton;
    procedure RadioButtonRadioClick(Sender: TObject);
    procedure RadioButtonButtonClick(Sender: TObject);
    procedure RadioButtonEditClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure New1Click(Sender: TObject);
    procedure Open1Click(Sender: TObject);
    procedure SaveAs1Click(Sender: TObject);
    procedure About1Click(Sender: TObject);
    procedure Exit1Click(Sender: TObject);
    procedure SaveAs2Click(Sender: TObject);
    procedure MemoRadioButtonClick(Sender: TObject);
  private
    ClassRef: CRefType;
    Counter: Integer;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.RadioButtonRadioClick(Sender: TObject);
begin
  ClassRef := TRadioButton;
end;
procedure TForm1.RadioButtonButtonClick(Sender: TObject);
begin
  ClassRef := TButton;
end;
procedure TForm1.RadioButtonEditClick(Sender: TObject);
begin
  ClassRef := TEdit;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  ClassRef := TRadioButton;
  Counter := 0;
end;
procedure TForm1.FormMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  MyObj: TControl;
  MyName: String;
begin
  {create an object using the current class reference}
  MyObj := ClassRef.Create (self);
  MyObj.Visible := False;
  MyObj.Parent := self;
  MyObj.Left := X;
  MyObj.Top := Y;
  Inc (Counter);
  {define the name using the class name, without the
  initial T, and the number of the Counter}
  MyName := ClassRef.ClassName + IntToStr (Counter);
  Delete (MyName, 1, 1);
  MyObj.Name := MyName;
  MyObj.Visible := True;
end;
procedure TForm1.New1Click(Sender: TObject);
var
  I: Integer;
begin
  {delete all existing components, except the panel}
  for I := ControlCount - 1 downto 0 do
    if Controls[I].ClassName <> 'TPanel' then
      Controls[I].Free;
  Counter := 0;
end;
procedure TForm1.Open1Click(Sender: TObject);
var
  S: TFileStream;
  New: TComponent;
  Reader1: TReader; // new line
begin
  if OpenDialog1.Execute then
  begin
    {remove existing controls}
    New1Click (self);
    {open the stream}
    S := TFileStream.Create (OpenDialog1.FileName,
      fmOpenRead);
    try
      while S.Position < S.Size do
      begin
        {read a component and add it to the form}
        // old code:
//        New := S.ReadComponent (nil);
//        InsertControl (New as TControl);
        // new code:
        Reader1 := TReader.Create (S, 4096);
        try
          Reader1.Parent := self;
          New := Reader1.ReadRootComponent (nil);
          InsertComponent (New);
        finally
          Reader1.Free;
        end;
        Inc (Counter);
      end;
    finally
      S.Free;
    end;
  end;
end;
procedure TForm1.SaveAs1Click(Sender: TObject);
var
  S: TFileStream;
  I: Integer;
begin
  if SaveDialog1.Execute then
  begin
    {open or create the stream file}
    S := TFileStream.Create (SaveDialog1.FileName,
      fmOpenWrite or fmCreate);
    try
      {save each component except the panel}
      for I := 0 to ControlCount - 1 do
        if Controls[I].ClassName <> 'TPanel' then
          S.WriteComponent (Controls[I]);
    finally
      S.Free;
    end;
  end;
end;
procedure TForm1.About1Click(Sender: TObject);
begin
  MessageDlg ('CREF2 Example: Save components to file' +
    Chr(13) + 'From "Mastering Delphi", by Marco Саntщ',
    mtInformation, [mbOk], 0);
end;
procedure TForm1.Exit1Click(Sender: TObject);
begin
  Close;
end;
{save form file}
procedure TForm1.SaveAs2Click(Sender: TObject);
begin
  if SaveDialog2.Execute then
    WriteComponentResFile
      (SaveDialog2.Filename, self);
end;
procedure TForm1.MemoRadioButtonClick(Sender: TObject);
begin
  ClassRef := TMemo;
end;
initialization
  {register the classes of the components; this code is
  required by the stream loader}
  RegisterClasses ([TRadioButton, TEdit, TButton, TMemo]);
end.

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

Сохранить изображение в формате JPEG.

Пришел програмист фотки печатать дал негатив служащим а они его спрашивают: - В каком формате печатать? - В "jpg" конечно

В комплект поставки Delphi входит модуль JPEG. Он позволяет работать с изображениями в формате JPEG. Эта программа сохраняет изображение экрана в файле C:\Screen.jpg.

uses Jpeg;
procedure TForm1.Button1Click(Sender: TObject);
var
  JpegIm: TJpegImage;
  bm: TBitMap;
begin
  bm := TBitMap.Create;
  bm.Width := Screen.Width;
  bm.Height := Screen.Height;
  BitBlt(bm.Canvas.Handle, 0, 0,
  bm.Width, bm.Height,
  GetDC(0), 0, 0, SRCCOPY);
  JpegIm := TJpegImage.Create;
  JpegIm.Assign(bm);
  JpegIm.CompressionQuality := 20;
  JpegIm.Compress;
  JpegIm.SaveToFile('C:\Screen.jpg');
  bm.Destroy;
  JpegIm.Destroy;
end;

Сохранение и загрузка данных в объекты на примере коллекций.

Если в Вашей программе используются классы для описания объектов некоторой предметной области, то данные, их инициализирующие, можно хранить и в базе данных. Но можно выбрать гораздо более продуктивный подход, который доступен в Delphi/C++ Builder. Среда разработки Delphi/C++ Builder хранит ресурсы всех форм в двоичных или текстовых файлах и эта возможность доступна и для разрабатываемых с ее помощью программ. В данном случае, для оценки удобств такого подхода лучше всего рассмотреть конкретный пример.

Необходимо реализовать хранение информации о некоей службе рассылки и ее подписчиках. Будем хранить данные о почтовом сервере и список подписчиков. Каждая запись о подписчике хранит его личные данные и адрес, а также список тем(или каталогов), на которые он подписан. Как большие поклонники Гради Буча (Grady Booch), а также будучи заинтересованы в удобной организации кода, мы организуем информацию о подписчиках в виде объектов. В Delphi для данной задачи идеально подходит класс TCollection, реализующий всю необходимую функциональность для работы со списками типизированных объектов. Для этого мы наследуемся от TCollection, называя новый класс TMailList, а также создаем наследника от TCollectionItem - TMailClient. Последний будет содержать все необходимые данные о подписчике, а также реализовывать необходимые функции для работы с ним.

Начнем с TMailClient.

type
  TMailClient = class(TCollectionItem)
  private
    FName: string;
    FAddress: string;
    FEnabled: boolean;
    FFolders: TStringList;
  public
    Files: TStringList;
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    procedure PickFiles;
  published
    property name: string read FName write FName;
    property Address: string read FAddress write FAddress;
    property Enabled: boolean read FEnabled write FEnabled default true;
    property Folders: TStringList read FFolders write FFolders;
  end;

Класс содержит сведения о имени клиента, его адресе, его статусе(Enabled), а также список каталогов, на которые он подписан. Процедура PickFiles составляет список файлов к отправке и сохраняет его в свойстве Files. Класс TMailList, хранящий объекты класса TMailClient, приведен ниже.

  TMailList = class(TCollection)
  public
   function GetMailClient(index: Integer): TMailClient;
   procedure SetMailClient(index: Integer; Value: TMailClient);
  public
   function Add: TMailClient;
   property Items[index: Integer]: TMailClient read GetMailClient
   write SetMailClient; default;
  end;

Теперь поместим класс TMailList в класс TMailer, содержащий также и данные о параметрах доступа к почтовому серверу для отправки почты.

  TMailer = class(TComponent)
  private
    name: string;
    EMail: string;
    FMailList: TMailList;
    FLastActivated: TDateTime;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property MailList: TMailList read FMailList write FMailList;
    property LastActivated: TDateTime read FLastActivated write FLastActivated;
  end;

В данном случае мы наследуемся от класса TComponent, для того, чтобы была возможности записи данных объекта в файл. Свойство MailList содержит уже объект класса TMailList, а свойство LastActivated - дату последнего запуска программы рассылки.

Реализация всех приведенных классов приведена ниже.

constructor TMailClient.Create(Collection: TCollection);
begin
  inherited;
  Folders := TStringList.Create;
  Files := TStringList.Create;
  FEnabled := true;
end;
destructor TMailClient.Destroy;
begin
  Folders.Free;
  Files.Free;
  inherited;
end;
procedure TMailClient.PickFiles;
var
  i: integer;
begin
  for i := 0 to Folders.Count - 1 do
    CreateFileList(Files, Folders[i]);
end;
function TMailList.GetMailClient(index: Integer): TMailClient;
begin
  Result := TMailClient(inherited Items[index]);
end;
procedure TMailList.SetMailClient(index: Integer; Value: TMailClient);
begin
  Items[index].Assign(Value);
end;
function TMailList.Add: TMailClient;
begin
  Result := TMailClient(inherited Add);
end;
constructor TMailer.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  MailList := TMailList.Create(TMailClient);
  FLastActivated := now;
end;
destructor TMailer.Destroy;
begin
  MailList.Free;
  inherited;
end;

Функция CreateFileList создает по каким-либо правилам список файлов на основе переданного ей списка каталогов, обходя их рекурсивно.

procedure CreateFileList(sl: TStringList; FilePath: string);
var
  sr: TSearchRec;
  procedure ProcessFile;
  begin
    if (sr.name = '.') or(sr.name = '..') then
      exit;
    if sr.Attr <> faDirectory then
      sl.Add(FilePath + '\' + sr.name);
    if sr.Attr = faDirectory then
      CreateFileList(sl, FilePath + '\' + sr.name);
  end;
begin
  if not DirectoryExists(FilePath) then
    exit;
  if FindFirst(FilePath + '\' + Settings.IncludeFileMasks,
  faAnyFile , sr) = 0 then
    ProcessFile;
  while FindNext(sr) = 0 do
    ProcessFile;
  FindClose(sr);
end;

В итоге мы располагаем классом TMailer, содержащим всю необходимую нам информацию. Теперь перейдем к созданию объекта, их сохранению и загрузке.

var
  sDataFile: string;
  MLN: TMailLateNight;
begin
  MLN := TMailLateNight.Create(nil);
  sDataFile := ExtractFilePath(ParamStr(0)) + 'users.dat';
  //...загрузка данных из файла
  if FileExists(sDatsFile) then
    LoadUsersFromTextFile(MLN, sDatsFile);
  ...
  //...работа с объектами
  for i:=0 to MLN.MailList.Count-1 do
  begin
    s := MLN.MailList[i].name;
    s := MLN.MailList[i].Address;
    MLN.MailList[i].PickFiles;
    for j:=0 to MLN.Files.Count-1 do
    begin
      s := MLN.MailList[i].Files[j];
      ...
      //...сохранение данных в файл
      SaveComponentToTextFile(MLN, sDataFile);

Хранение данных в файле позволяет оказаться от использования БД, если объем данных не слишком велик и нет необходимости в совместном доступе к данным.

Самое главное - мы организуем все данные в виде набора удобных для работы классов и не тратим время на их сохранение и инициализацию из БД.

Далее приведен код функций для сохранения/чтения компонента.

//...процедура удаляет все дочерние компоненты из формы
procedure DeleteComponents(Form: TForm);
var
  i: integer;
begin
  for i := Form.ComponentCount - 1 downto 0 do
    Form.Components[i].Free;
end;
// ...процедура загружает(инициализирует)
// компонент из текстового файла с ресурсом
procedure LoadComponentFromTextFile(Component: TComponent;
FileName: string);
var
  ms: TMemoryStream;
  fs: TFileStream;
begin
  ms := TMemoryStream.Create;
  fs := TFileStream.Create(FileName, fmOpenRead);
  try
    ObjectTextToBinary(fs, ms);
    ms.position := 0;
    ms.ReadComponent(Component);
  finally
    ms.Free;
    fs.free;
  end;
end;
//...процедура сохраняет компонент в текстовый файл
procedure SaveComponentToTextFile(Component: TComponent;
FileName: string);
var
  ms: TMemoryStream;
  fs: TFileStream;
begin
  fs := TFileStream.Create(FileName, fmCreate or fmOpenWrite);
  ms := TMemoryStream.Create;
  try
    ms.WriteComponent(Component);
    ms.position := 0;
    ObjectBinaryToText(ms, fs);
  finally
    ms.Free;
    fs.free;
  end;
end;

Сохранение и чтение TStringGrid.

procedure SaveGrid;
var
  f: textfile;
  x, y: integer;
begin
  assignfile(f, 'Filename');
  rewrite(f);
  writeln(f, stringgrid.colcount);
  writeln(f, stringgrid.rowcount);
  for X := 0 to stringgrid.colcount - 1 do
    for y := 0 to stringgrid.rowcount - 1 do
      writeln(F, stringgrid.cells[x, y]);
  closefile(f);
end;
procedure LoadGrid;
var
  f: textfile;
  temp, x, y: integer;
  tempstr: string;
begin
  assignfile(f, 'Filename');
  reset(f);
  readln(f, temp);
  stringgrid.colcount := temp;
  readln(f, temp);
  stringgrid.rowcount := temp;
  for X := 0 to stringgrid.colcount - 1 do
    for y := 0 to stringgrid.rowcount - 1 do
    begin
      readln(F, tempstr);
      stringgrid.cells[x, y] := tempstr;
    end;
  closefile(f);
end;

При чтении почты POP3 отделять Attachment и сохранять в файл.

Привет, дорогая моя! Наконец-то я добилась от этого придурка разрешения пользоваться компьютером! Он даже сделал мне собственный почтовый ящик, так что теперь мы сможем общаться не только по телефону. Пиши мне на адрес ***@JIZNI.NET

Почту читаю, используя компонент TNMPOP3 (стандартный в поставке D5), еще пробовал использовать библитеку Indy, но не помогло. А проблема заключается в том, что после прочтения письма, невозможно отделить аттачмент от тела. Но это происходит не со всеми письмами. Если я отправлю письмо с аттачем, то я могу его нормально читать и разбирать, а если отправляет Заказчик, то получается то, что я описал. Причем, Outlook и theBat, эти письма нормально читают и аттач МОЖНО сохранить.

for intIndex := 0 to Pred(Msg.MessageParts.Count) do
begin
  if (Msg.MessageParts.Items[intIndex] is TIdAttachment) then
  begin //general attachment
    TIdAttachment(Msg.MessageParts.Items[intIndex]).SaveToFile(
      TIdAttachment(Msg.MessageParts.Items[intIndex]).Filename);
    TIdAttachment.Create(Msg1.MessageParts,
      TIdAttachment(Msg.MessageParts.Items[intIndex]).Filename);
  end
  else
  begin //body text
    if Msg.MessageParts.Items[intIndex] is TIdText then
    begin
      Memo1.Lines.Clear;
      Memo1.Lines.AddStrings(TIdText(Msg.MessageParts.Items[intIndex]).Body);
    end
  end;
end;

Сохранить несколько контролов в один файл.

{ 
 -> Question: 
 How can I store some TRichEdit components and TEdit Components in a single file? 
 -> Answer: 
 Use a filestream and a reader or writer object. 
 These ease the tasks of writing strings to a binary stream and reading them back. 
} 
{ 
 -> Frage: 
 Wie kann ich den Inhalt einiger TRichEdit und TEdit Komponenten in einer einzigen 
 Datei  speichern? 
 -> Antwort: 
 Verwende einen FileStraem und ein Reader/Writer Objekt. 
 Damit kann man leicht strings in einen ьinдгеn Stream speichern und zигьск1еsеn. 
} 
// Save routine 
// Speichern Routine 
procedure SaveEditcontrols(const FileName: string; const Controls: array of TCustomEdit); 
var 
  fs: TFilestream; 
  writer: TWriter; 
  i: Integer; 
  ss: TStringstream; 
begin 
  fs := TFilestream.Create(FileName, fmCreate); 
  try 
    writer := TWriter.Create(fs, 4096); 
    try 
      for i := Low(Controls) to High(Controls) do 
        if Controls[i] is TCustomRichedit then  
        begin 
          ss := TStringstream.Create(EmptyStr); 
          try 
            with TRichedit(Controls[i]) do  
            begin 
              Plaintext := False; 
              Lines.SaveToStream(ss); 
            end; 
            writer.WriteString(ss.Datastring); 
          finally 
            ss.Free; 
          end; 
        end 
      else 
        writer.WriteString(Controls[i].Text); 
    finally 
      writer.Free; 
    end; 
  finally 
    fs.Free; 
  end; 
end; 
// Load routine 
// Lade Routine 
procedure LoadEditcontrols(const FileName: string; const Controls: array of TCustomEdit); 
var 
  fs: TFilestream; 
  reader: Treader; 
  i: Integer; 
  ss: TStringstream; 
begin 
  fs := TFilestream.Create(FileName, fmOpenread or fmShareDenyWrite); 
  try 
    reader := Treader.Create(fs, 4096); 
    try 
      for i := Low(Controls) to High(Controls) do 
        if Controls[i] is TCustomRichedit then  
        begin 
          ss := TStringstream.Create(reader.ReadString); 
          try 
            with TRichedit(Controls[i]) do  
            begin 
              Plaintext := False; 
              Lines.LoadfromStream(ss); 
            end; 
          finally 
            ss.Free; 
          end; 
        end 
      else 
        Controls[i].Text := reader.ReadString; 
    finally 
      reader.Free; 
    end; 
  finally 
    fs.Free; 
  end; 
end; 
// Example to store 2 TRichEdits and 3 Edit Controls to one file 
// Beispiel, um  3 TRichEdits und 3 TEdit Controls in einer Datei zu speichern 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  SaveEditControls('C:\temp\temp.dat', 
    [richedit1, richedit2, edit1, edit2, edit3]); 
end;

Как хранить настройки для программы. Работа с INI-файлами.

Разговор двух украинских программистов. - Слышь Микола, как москали КД-ПЗУ называют? - Как? - CD-ROM! - Поубывал бы.
var
  IniFile: TIniFile;
  //конкретные переменные - мои - храню их в инишке
  IniPortItem: integer;
  IniRTSChecked: boolean;
const
  //значения по умолчанию
  DefPortItem = 1;
  DefRTSChecked = false;
procedure ReadIni;
var
  IniPath: string;
  FileName: string;
begin
  GetDir(0,IniPath);
  FileName:=IniPath+'\muk.ini';
  IniFile:=TIniFile.Create(FileName);
  IniPortItem:=IniFile.ReadInteger('Port','Number',DefPortItem);
  IniRTSChecked:=IniFile.ReadBool('Port','RTS/CTS',DefRTSChecked);
  IniFile.Free;
end;
procedure SaveIni;
var
  IniPath: string;
  FileName: string;
begin
  GetDir(0,IniPath);
  FileName:=IniPath+'\muk.ini';
  IniFile:=TIniFile.Create(FileName);
  IniFile.WriteInteger('Port','Number',IniPortItem);
  IniFile.WriteBool('Port','RTS/CTS',IniRTSChecked);
  IniFile.Free;
end;

Сохранять настройки в ini-файле.

Этот пример показывает, как можно сохранять настройки с помощью ini-файла. Ini-файлы удобны тем, что после удаления программы без деинсталяции в реестре не остается "следов". Еще это удобно тем, что для "сброса" настроек программы достаточно удалить ini-файл.

Эта программа сохраняет позицию и размер окна, содержимое поля ввода Edit1, число, введенное в Edit2, флажок CheckBox1, а также содержимое ListBox1 (добавить в него строчку можно, нажав на кнопку).

uses IniFiles;
procedure TForm1.FormCreate(Sender: TObject);
var
  F: TIniFile;
  names: TStringList;
  i: integer;
begin
  F := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'window.ini');
  Form1.Left := F.ReadInteger('position', 'left', 0);
  Form1.Width := F.ReadInteger('position', 'width', 200);
  Form1.Top := F.ReadInteger('position', 'top', 0);
  Form1.Height := F.ReadInteger('position', 'height', 200);
  Edit1.Text := F.ReadString('tools', 'edit1', 'no text');
  Edit2.Text := IntToStr(F.ReadInteger('tools', 'koef', 0));
  CheckBox1.Checked := F.ReadBool('tools', 'check', true);
  names := TStringList.Create;
  F.ReadSection('files', names);
  for i := 0 to Names.Count - 1 do
    ListBox1.Items.Add(F.ReadString('files', names.Strings[i], ''));
  names.Destroy;
  F.Destroy;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
  F: TIniFile;
  i: integer;
begin
  F := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'window.ini');
  F.WriteInteger('position', 'left', Form1.Left);
  F.WriteInteger('position', 'width', Form1.Width);
  F.WriteInteger('position', 'top', Form1.Top);
  F.WriteInteger('position', 'height', Form1.Height);
  F.WriteString('tools', 'edit1', Edit1.Text);
  F.WriteInteger('tools', 'koef', StrToIntDef(Edit2.Text, 0));
  F.WriteBool('tools', 'check', CheckBox1.Checked);
  for i := 0 to ListBox1.Items.Count - 1 do
    F.WriteString('files', 'file' + IntToStr(i+1), ListBox1.Items.Strings[i]);
  F.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add(Edit1.Text);
end;

Сохранение прикреплений Outlook.

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

{ 
  Won’t some backups of your outlook attachments are filtered 
  some incoming log files? Here's the function. 
}
 uses
   ComObj;
 {...}
 function ManageAttachments(SendersName, AttachmentPath: string;
   MailDelete: Boolean): Boolean;
 var
   oApp: Variant;
   oNs: Variant;
   oFolder: Variant;
   oMsg: Variant;
   AtC: Variant;
   AttFilename: Variant;
   FileName: string;
   CheckSender: string;
   Counter: integer;
   MailCounter: integer;
 begin
   try
     oApp := CreateOLEObject('outlook.application');
     try
       oNs         := oApp.GetNamespace('MAPI');
       ofolder     := oNS.GetDefaultFolder(6); // FolderTypeEnum  (olFolderInbox) 
      MailCounter := 1;
       // If there is any email in the Inbox 
      if ofolder.Items.Count > 0 then
       begin
         repeat
           // Get the first Email 
          oMsg := ofolder.Items(MailCounter);
           // Check the name or Email 
          //   Use CheckSender := oMsg.subject to search on Subject; 
          CheckSender := oMsg.sendername;
           if CheckSender = SendersName then
           //  Remove this line to backup all your attachments. 
          begin
             // Check how many attachments 
            atc := oMsg.Attachments.Count;
             if atc > 0 then
             begin
               // Get all the attachments and save them 
              for Counter := 1 to atc do
               begin
                 AttFilename := oMsg.Attachments.item(Counter).FileName;
                 //filename := IncludeTrailingBackslash(AttachmentPath)+AttFilename; {Use this line for D5)} 
                FileName := AttachmentPath + '\' + AttFilename;
                 oMsg.Attachments.Item(Counter).SaveAsFile(FileName);
               end;
             end;
             if MailDelete then
             begin
               oMsg.Delete;
               // There's 1 Email less, so MailCounter - 1 
              Dec(MailCounter);
             end;
           end;
           // Get the next Email 
          Inc(MailCounter);
           // Do until there is no more Email to check 
        until MailCounter > ofolder.Items.Count;
       end;
     finally
       oApp.quit;
     end;
   except
     Result := False;
     Exit;
   end;
   Result := True;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   // ManageAttachments(Email or name, Backup directory, MailDelete yes or no) 
  ManageAttachments('info@cleys.com', 'F:\test', False);
 end;
 { 
  Warning! 
  All your selected Email will be deleted if MailDelete = true 
  Achtung! 
  Alle E-Mails werden geloscht, wenn MailDelete = true ist. 
}

Как сохранить значение свойства в поток.

Автор: Rick Rogers WEB-сайт: http://www.lmc-mediaagentur.de How can I save properties of a TList to a stream? I need the entire list to be saved as a whole and not as individual objects.

A TList doesn't have any intrinsic streaming capability built into it, but it is very easy to stream anything that you want with a little elbow grease. Think about it: a stream is data. Classes have properties, whose values are data. It isn't too hard to write property data to a stream. Here's a simple example to get you going. This is but just one of many possible approaches to saving object property data to a stream:

unit uStreamableExample;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    StdCtrls, Contnrs;
type
  TStreamableObject = class(TPersistent)
  protected
    function ReadString(Stream: TStream): string;
    function ReadLongInt(Stream: TStream): LongInt;
    function ReadDateTime(Stream: TStream): TDateTime;
    function ReadCurrency(Stream: TStream): Currency;
    function ReadClassName(Stream: TStream): ShortString;
    procedure WriteString(Stream: TStream; const Value: string);
    procedure WriteLongInt(Stream: TStream; const Value: LongInt);
    procedure WriteDateTime(Stream: TStream; const Value: TDateTime);
    procedure WriteCurrency(Stream: TStream; const Value: Currency);
    procedure WriteClassName(Stream: TStream; const Value: ShortString);
  public
    constructor CreateFromStream(Stream: TStream);
    procedure LoadFromStream(Stream: TStream); virtual; abstract;
    procedure SaveToStream(Stream: TStream); virtual; abstract;
  end;
  TStreamableObjectClass = class of TStreamableObject;
  TPerson = class(TStreamableObject)
  private
    FName: string;
    FBirthDate: TDateTime;
  public
    constructor Create(const AName: string; ABirthDate: TDateTime);
    procedure LoadFromStream(Stream: TStream); override;
    procedure SaveToStream(Stream: TStream); override;
    property Name: string read FName write FName;
    property BirthDate: TDateTime read FBirthDate write FBirthDate;
  end;
  TCompany = class(TStreamableObject)
  private
    FName: string;
    FRevenues: Currency;
    FEmployeeCount: LongInt;
  public
    constructor Create(const AName: string; ARevenues: Currency; AEmployeeCount:
      LongInt);
    procedure LoadFromStream(Stream: TStream); override;
    procedure SaveToStream(Stream: TStream); override;
    property Name: string read FName write FName;
    property Revenues: Currency read FRevenues write FRevenues;
    property EmployeeCount: LongInt read FEmployeeCount write FEmployeeCount;
  end;
  TStreamableList = class(TStreamableObject)
  private
    FItems: TObjectList;
    function Get_Count: LongInt;
    function Get_Objects(Index: LongInt): TStreamableObject;
  public
    constructor Create;
    destructor Destroy; override;
    function FindClass(const AClassName: string): TStreamableObjectClass;
    procedure Add(Item: TStreamableObject);
    procedure Delete(Index: LongInt);
    procedure Clear;
    procedure LoadFromStream(Stream: TStream); override;
    procedure SaveToStream(Stream: TStream); override;
    property Objects[Index: LongInt]: TStreamableObject read Get_Objects;
      default;
    property Count: LongInt read Get_Count;
  end;
  TForm1 = class(TForm)
    SaveButton: TButton;
    LoadButton: TButton;
    procedure SaveButtonClick(Sender: TObject);
    procedure LoadButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    Path: string;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
resourcestring
  DEFAULT_FILENAME = 'test.dat';
procedure TForm1.SaveButtonClick(Sender: TObject);
var
  List: TStreamableList;
  Stream: TStream;
begin
  List := TStreamableList.Create;
  try
    List.Add(TPerson.Create('Rick Rogers', StrToDate('05/20/68')));
    List.Add(TCompany.Create('Fenestra', 1000000, 7));
    Stream := TFileStream.Create(Path + DEFAULT_FILENAME, fmCreate);
    try
      List.SaveToStream(Stream);
    finally
      Stream.Free;
    end;
  finally
    List.Free;
  end;
end;
{ TPerson }
constructor TPerson.Create(const AName: string; ABirthDate: TDateTime);
begin
  inherited Create;
  FName := AName;
  FBirthDate := ABirthDate;
end;
procedure TPerson.LoadFromStream(Stream: TStream);
begin
  FName := ReadString(Stream);
  FBirthDate := ReadDateTime(Stream);
end;
procedure TPerson.SaveToStream(Stream: TStream);
begin
  WriteString(Stream, FName);
  WriteDateTime(Stream, FBirthDate);
end;
{ TStreamableList }
procedure TStreamableList.Add(Item: TStreamableObject);
begin
  FItems.Add(Item);
end;
procedure TStreamableList.Clear;
begin
  FItems.Clear;
end;
constructor TStreamableList.Create;
begin
  FItems := TObjectList.Create;
end;
procedure TStreamableList.Delete(Index: LongInt);
begin
  FItems.Delete(Index);
end;
destructor TStreamableList.Destroy;
begin
  FItems.Free;
  inherited;
end;
function TStreamableList.FindClass(const AClassName: string):
  TStreamableObjectClass;
begin
  Result := TStreamableObjectClass(Classes.FindClass(AClassName));
end;
function TStreamableList.Get_Count: LongInt;
begin
  Result := FItems.Count;
end;
function TStreamableList.Get_Objects(Index: LongInt): TStreamableObject;
begin
  Result := FItems[Index] as TStreamableObject;
end;
procedure TStreamableList.LoadFromStream(Stream: TStream);
var
  StreamCount: LongInt;
  I: Integer;
  S: string;
  ClassRef: TStreamableObjectClass;
begin
  StreamCount := ReadLongInt(Stream);
  for I := 0 to StreamCount - 1 do
  begin
    S := ReadClassName(Stream);
    ClassRef := FindClass(S);
    Add(ClassRef.CreateFromStream(Stream));
  end;
end;
procedure TStreamableList.SaveToStream(Stream: TStream);
var
  I: Integer;
begin
  WriteLongInt(Stream, Count);
  for I := 0 to Count - 1 do
  begin
    WriteClassName(Stream, Objects[I].ClassName);
    Objects[I].SaveToStream(Stream);
  end;
end;
{ TStreamableObject }
constructor TStreamableObject.CreateFromStream(Stream: TStream);
begin
  inherited Create;
  LoadFromStream(Stream);
end;
function TStreamableObject.ReadClassName(Stream: TStream): ShortString;
begin
  Result := ReadString(Stream);
end;
function TStreamableObject.ReadCurrency(Stream: TStream): Currency;
begin
  Stream.Read(Result, SizeOf(Currency));
end;
function TStreamableObject.ReadDateTime(Stream: TStream): TDateTime;
begin
  Stream.Read(Result, SizeOf(TDateTime));
end;
function TStreamableObject.ReadLongInt(Stream: TStream): LongInt;
begin
  Stream.Read(Result, SizeOf(LongInt));
end;
function TStreamableObject.ReadString(Stream: TStream): string;
var
  L: LongInt;
begin
  L := ReadLongInt(Stream);
  SetLength(Result, L);
  Stream.Read(Result[1], L);
end;
procedure TStreamableObject.WriteClassName(Stream: TStream; const Value:
  ShortString);
begin
  WriteString(Stream, Value);
end;
procedure TStreamableObject.WriteCurrency(Stream: TStream; const Value:
  Currency);
begin
  Stream.Write(Value, SizeOf(Currency));
end;
procedure TStreamableObject.WriteDateTime(Stream: TStream; const Value:
  TDateTime);
begin
  Stream.Write(Value, SizeOf(TDateTime));
end;
procedure TStreamableObject.WriteLongInt(Stream: TStream; const Value: LongInt);
begin
  Stream.Write(Value, SizeOf(LongInt));
end;
procedure TStreamableObject.WriteString(Stream: TStream; const Value: string);
var
  L: LongInt;
begin
  L := Length(Value);
  WriteLongInt(Stream, L);
  Stream.Write(Value[1], L);
end;
{ TCompany }
constructor TCompany.Create(const AName: string; ARevenues: Currency;
  AEmployeeCount: Integer);
begin
  FName := AName;
  FRevenues := ARevenues;
  FEmployeeCount := AEmployeeCount;
end;
procedure TCompany.LoadFromStream(Stream: TStream);
begin
  FName := ReadString(Stream);
  FRevenues := ReadCurrency(Stream);
  FEmployeeCount := ReadLongInt(Stream);
end;
procedure TCompany.SaveToStream(Stream: TStream);
begin
  WriteString(Stream, FName);
  WriteCurrency(Stream, FRevenues);
  WriteLongInt(Stream, FEmployeeCount);
end;
procedure TForm1.LoadButtonClick(Sender: TObject);
var
  List: TStreamableList;
  Stream: TStream;
  Instance: TStreamableObject;
  I: Integer;
begin
  Stream := TFileStream.Create(Path + DEFAULT_FILENAME, fmOpenRead);
  try
    List := TStreamableList.Create;
    try
      List.LoadFromStream(Stream);
      for I := 0 to List.Count - 1 do
      begin
        Instance := List[I];
        if Instance is TPerson then
          ShowMessage(TPerson(Instance).Name);
        if Instance is TCompany then
          ShowMessage(TCompany(Instance).Name);
      end;
    finally
      List.Free;
    end;
  finally
    Stream.Free;
  end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  Path := ExtractFilePath(Application.ExeName);
end;
initialization
  RegisterClasses([TPerson, TCompany]);
end.

Как сохранить значение свойства в поток 2.

Автор: Yoav WEB-сайт: http://www.lmc-mediaagentur.de The solution above will work, but it forces you to implement streaming support for each of the TStreamableObject objects. Delphi has already implemented this mechanism in for the TPersistent class and the TComponent class, and you can use this mechanism. The class I include here does the job. It holds classes that inherit from TUmbCollectionItem (which in turn inherits from Delphi TCollectionItem), and handles all the streaming of the items. As the items are written with the Delphi mechanism, all published data is streamed.

Notes: This class does not support working within the delphi IDE like TCollection. All objects inheriting from TUmbCollectionItem must be registered using the Classes.RegisterClass function. All objects inheriting from TUmbCollectionItem must implement the assign function. By default, the TUmbCollection owns its items (frees them when the collection is freed), but this functionality can be changed.

unit UmbCollection;
interface
uses
  Windows, Messages, SysUtils, Classes, contnrs;
type
  TUmbCollectionItemClass = class of TUmbCollectionItem;
  TUmbCollectionItem = class(TCollectionItem)
  private
    FPosition: Integer;
  public
    {when overriding this method, you must call the inherited assign.}
    procedure Assign(Source: TPersistent); override;
  published
    {the position property is used by the streaming mechanism to place the object in the
    right position when reading the items. do not use this property.}
    property Position: Integer read FPosition write FPosition;
  end;
  TUmbCollection = class(TObjectList)
  private
    procedure SetItems(Index: Integer; Value: TUmbCollectionItem);
    function GetItems(Index: Integer): TUmbCollectionItem;
  public
    function Add(AObject: TUmbCollectionItem): Integer;
    function Remove(AObject: TUmbCollectionItem): Integer;
    function IndexOf(AObject: TUmbCollectionItem): Integer;
    function FindInstanceOf(AClass: TUmbCollectionItemClass; AExact: Boolean =
      True;
      AStartAt: Integer = 0): Integer;
    procedure Insert(Index: Integer; AObject: TUmbCollectionItem);
    procedure WriteToStream(AStream: TStream); virtual;
    procedure ReadFromStream(AStream: TStream); virtual;
    property Items[Index: Integer]: TUmbCollectionItem read GetItems write
      SetItems; default;
  published
    property OwnsObjects;
  end;
implementation
{ TUmbCollection }
function ItemsCompare(Item1, Item2: Pointer): Integer;
begin
  Result := TUmbCollectionItem(Item1).Position -
    TUmbCollectionItem(Item2).Position;
end;
function TUmbCollection.Add(AObject: TUmbCollectionItem): Integer;
begin
  Result := inherited Add(AObject);
end;
function TUmbCollection.FindInstanceOf(AClass: TUmbCollectionItemClass;
  AExact: Boolean; AStartAt: Integer): Integer;
begin
  Result := inherited FindInstanceOf(AClass, AExact, AStartAt);
end;
function TUmbCollection.GetItems(Index: Integer): TUmbCollectionItem;
begin
  Result := inherited Items[Index] as TUmbCollectionItem;
end;
function TUmbCollection.IndexOf(AObject: TUmbCollectionItem): Integer;
begin
  Result := inherited IndexOf(AObject);
end;
procedure TUmbCollection.Insert(Index: Integer; AObject: TUmbCollectionItem);
begin
  inherited Insert(Index, AObject);
end;
procedure TUmbCollection.ReadFromStream(AStream: TStream);
var
  Reader: TReader;
  Collection: TCollection;
  ItemClassName: string;
  ItemClass: TUmbCollectionItemClass;
  Item: TUmbCollectionItem;
  i: Integer;
begin
  Clear;
  Reader := TReader.Create(AStream, 1024);
  try
    Reader.ReadListBegin;
    while not Reader.EndOfList do
    begin
      ItemClassName := Reader.ReadString;
      ItemClass := TUmbCollectionItemClass(FindClass(ItemClassName));
      Collection := TCollection.Create(ItemClass);
      try
        Reader.ReadValue;
        Reader.ReadCollection(Collection);
        for i := 0 to Collection.Count - 1 do
        begin
          item := ItemClass.Create(nil);
          item.Assign(Collection.Items[i]);
          Add(Item);
        end;
      finally
        Collection.Free;
      end;
    end;
    Sort(ItemsCompare);
    Reader.ReadListEnd;
  finally
    Reader.Free;
  end;
end;
function TUmbCollection.Remove(AObject: TUmbCollectionItem): Integer;
begin
  Result := inherited Remove(AObject);
end;
procedure TUmbCollection.SetItems(Index: Integer; Value: TUmbCollectionItem);
begin
  inherited Items[Index] := Value;
end;
procedure TUmbCollection.WriteToStream(AStream: TStream);
var
  Writer: TWriter;
  CollectionList: TObjectList;
  Collection: TCollection;
  ItemClass: TUmbCollectionItemClass;
  ObjectWritten: array of Boolean;
  i, j: Integer;
begin
  Writer := TWriter.Create(AStream, 1024);
  CollectionList := TObjectList.Create(True);
  try
    Writer.WriteListBegin;
    {init the flag array and the position property of the TCollectionItem objects.}
    SetLength(ObjectWritten, Count);
    for i := 0 to Count - 1 do
    begin
      ObjectWritten[i] := False;
      Items[i].Position := i;
    end;
    {write the TCollectionItem objects. we write first the name of the objects class,
    then write all the object of the same class.}
    for i := 0 to Count - 1 do
    begin
      if ObjectWritten[i] then
        Continue;
      ItemClass := TUmbCollectionItemClass(Items[i].ClassType);
      Collection := TCollection.Create(ItemClass);
      CollectionList.Add(Collection);
      {write the items class name}
      Writer.WriteString(Items[i].ClassName);
      {insert the items to the collection}
      for j := i to Count - 1 do
        if ItemClass = Items[j].ClassType then
        begin
          ObjectWritten[j] := True;
          (Collection.Add as ItemClass).Assign(Items[j]);
        end;
      {write the collection}
      Writer.WriteCollection(Collection);
    end;
  finally
    CollectionList.Free;
    Writer.WriteListEnd;
    Writer.Free;
  end;
end;
{ TUmbCollectionItem }
procedure TUmbCollectionItem.Assign(Source: TPersistent);
begin
  if Source is TUmbCollectionItem then
    Position := (Source as TUmbCollectionItem).Position
  else
    inherited;
end;
end.

Сохранение точных размеров при печати.

Приведенный ниже модуль демонстрирует принцип использования GetDeviceCaps для получения исчерпывающей информации о вашем принтере, включая HORZRES и VERTRES (горизонтальное и вертикальное разрешение в пикселах) на дюйм бумаги. Используя значения LOGPIXELSX и LOGPIXELSY, вы можете откалибровать принтер для точного задания количества точек на дюйм в горизонтальном и вертикальном направлениях. В дополнение к информации о принтере, приведенный модуль демонстрирует способ печати, при котором изображение выводится на принтер "естественного" размера; также показана возможность размещения изображения на бумаге в определенной позиции и определенного размера (т.е. с применением масштабирования). Я думаю это должно вам помочь в деле управления принтером.

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

unit Tstpr2fm;
{Пример использования объекта Printer из модуля TPrinter.
Приведен избыточный стиль программирования для облегчения
восприятия материала.
Демонстрация величин, возвращаемых функцией Windows API GetDeviceCaps.}
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ExtCtrls;
type
  TForm1 = class(TForm)
    Print: TButton;
    Image1: TImage;
    procedure PrintClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
uses
  Printers;
{Константы WINAPI GetDeviceCaps получены из C++ windows.h и wingdi.h}
{Отдельные константы здесь приведены только для информации о их наличии}
const
  DRIVERVERSION = 0;
  TECHNOLOGY = 2; {Смотри windows.h для значения маски}
  HORZSIZE = 4;
  VERTSIZE = 6;
  HORZRES = 8;
  VERTRES = 10;
  BITSPIXEL = 12;
  PLANES = 14;
  NUMBRUSHES = 16;
  NUMPENS = 18;
  NUMMARKERS = 20;
  NUMFONTS = 22;
  NUMCOLORS = 24;
  PDEVICESIZE = 26;
  CURVECAPS = 28; {Смотри windows.h для значения маски}
  LINECAPS = 30; {Смотри windows.h для значения маски}
  POLYGONALCAPS = 32; {Смотри windows.h для значения маски}
  TEXTCAPS = 34; {Смотри windows.h для значения маски}
  CLIPCAPS = 36; {Смотри windows.h для значения маски}
  RASTERCAPS = 38; {Смотри windows.h для значения маски}
  ASPECTX = 40;
  ASPECTY = 42;
  ASPECTXY = 44;
  LOGPIXELSX = 88;
  LOGPIXELSY = 90;
  SIZEPALETTE = 104;
  NUMRESERVED = 106;
  COLORRES = 108;
  PHYSICALWIDTH = 110; {Смотри определение в windows.h}
  PHYSICALHEIGHT = 111; {Смотри определение в windows.h}
  PHYSICALOFFSETX = 112; {Смотри определение в windows.h}
  PHYSICALOFFSETY = 113; {Смотри определение в windows.h}
  SCALINGFACTORX = 114; {Смотри определение в windows.h}
  SCALINGFACTORY = 115; {Смотри определение в windows.h}
  DeviceCapsString: array[1..34] of string =
  ('DRIVERVERSION', 'TECHNOLOGY', 'HORZSIZE',
    'VERTSIZE', 'HORZRES', 'VERTRES',
    'BITSPIXEL', 'PLANES', 'NUMBRUSHES',
    'NUMPENS', 'NUMMARKERS', 'NUMFONTS',
    'NUMCOLORS', 'PDEVICESIZE', 'CURVECAPS',
    'LINECAPS', 'POLYGONALCAPS', 'TEXTCAPS',
    'CLIPCAPS', 'RASTERCAPS', 'ASPECTX',
    'ASPECTY', 'ASPECTXY', 'LOGPIXELSX',
    'LOGPIXELSY', 'SIZEPALETTE', 'NUMRESERVED',
    'COLORRES', 'PHYSICALWIDTH', 'PHYSICALHEIGHT',
    'PHYSICALOFFSETX', 'PHYSICALOFFSETY', 'SCALINGFACTORX',
    'SCALINGFACTORY');
  DeviceCapsIndex: array[1..34] of INTEGER =
  (0, 2, 4, 6, 8, 10, 12, 14, 16, 18,
    20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
    40, 42, 44, 88, 90, 104, 106, 108, 110, 111,
    112, 113, 114, 115);
{$R *.DFM}
function iPosition(const i: INTEGER): INTEGER;
begin
  RESULT := Integer(i * LongInt(Printer.PageWidth) div 1000)
end {iPosition};
function jPosition(const j: INTEGER): INTEGER;
begin
  RESULT := Integer(j * LongInt(Printer.PageHeight) div 1000)
end {jPosition};
procedure TForm1.PrintClick(Sender: TObject);
var
  DestinationRectangle: TRect;
  GraphicAspectRatio: DOUBLE;
  i: INTEGER;
  j: INTEGER;
  iBase: INTEGER;
  iPixelsPerInch: WORD;
  jBase: INTEGER;
  jDelta: INTEGER;
  jPixelsPerInch: WORD;
  OffScreen: TBitMap;
  PixelAspectRatio: DOUBLE;
  SourceRectangle: TRect;
  TargetRectangle: TRect;
  value: INTEGER;
  x: DOUBLE;
  y: DOUBLE;
begin
  Printer.Orientation := poLandscape;
  Printer.BeginDoc;
  {Делаем прямоугольник для показа полей}
  Printer.Canvas.Rectangle(0, 0, Printer.PageWidth, Printer.PageHeight);
  {Свойства принтера и страницы}
  Printer.Canvas.Font.Name := 'Times New Roman';
  Printer.Canvas.Font.Size := 12;
  Printer.Canvas.Font.Style := [fsBold];
  Printer.Canvas.TextOut(iPosition(50), jPosition(40),
    'Свойства принтера и страницы');
  Printer.Canvas.Font.Style := [];
  Printer.Canvas.Font.Size := 10;
  iBase := iPosition(50);
  jBase := 60;
  jDelta := 18;
  Printer.Canvas.TextOut(iPosition(50), jPosition(jBase),
    Printer.Printers.Strings[Printer.PrinterIndex]);
  INC(jBase, jDelta);
  Printer.Canvas.TextOut(iBase, jPosition(jBase),
    'Пикселей:  ' + IntToStr(Printer.PageWidth) + ' X ' +
    IntToStr(Printer.PageHeight));
  INC(jBase, jDelta);
  Printer.Canvas.TextOut(iBase, jPosition(jBase),
    'Дюймов:  ' + FormatFloat('0.000',
    Printer.PageWidth / Printer.Canvas.Font.PixelsPerInch) + ' X ' +
    FormatFloat('0.000',
    Printer.PageHeight / Printer.Canvas.Font.PixelsPerInch));
  INC(jBase, 2 * jDelta);
  Printer.Canvas.TextOut(iBase, jPosition(jBase),
    'Шрифт:  ' + Printer.Canvas.Font.Name + '   Размер:  ' +
    IntToStr(Printer.Canvas.Font.Size));
  INC(jBase, jDelta);
  Printer.Canvas.TextOut(iBase, jPosition(jBase),
    'Пикселей в дюйме:  ' + IntToStr(Printer.Canvas.Font.PixelsPerInch));
  INC(jBase, jDelta);
  Printer.Canvas.TextOut(iBase, jPosition(jBase),
    '''ТЕКСТ'':  ' + IntToStr(Printer.Canvas.TextWidth('ТЕКСТ')) + ' X ' +
    IntToStr(Printer.Canvas.TextHeight('ТЕКСТ')) + '
    пикселей');
    {Значения GetDeviceCaps}
    INC(jBase, 2 * jDelta);
    Printer.Canvas.Font.Size := 12;
    Printer.Canvas.Font.Style := [fsBold];
    Printer.Canvas.TextOut(iBase, jPosition(jBase), 'GetDeviceCaps');
    INC(jBase, jDelta);
    Printer.Canvas.Font.Size := 10;
    Printer.Canvas.Font.Style := [];
    for j := LOW(DeviceCapsIndex) to HIGH(DeviceCapsIndex) do
    begin
      value := GetDeviceCaps(Printer.Handle, DeviceCapsIndex[j]);
      Printer.Canvas.TextOut(iBase, jPosition(jBase), DeviceCapsString[j]);
      if (DeviceCapsIndex[j] < 28) or (DeviceCapsIndex[j] > 38) then
        Printer.Canvas.TextOut(iPosition(250), jPosition(jBase), Format('%-8d',
          [value]))
      else
        Printer.Canvas.TextOut(iPosition(250), jPosition(jBase), Format('%.4x',
          [value]));
      INC(jBase, jDelta);
    end;
    {Помещаем изображение в левый нижний угол}
    Printer.Canvas.Draw(iPosition(300), jPosition(100),
    Form1.Image1.Picture.Graphic);
    {Помещаем то же изображение, имеющее ширину 1" и пропорциональную
    высоту в позиции 4"-правее и 1"-ниже верхнего левого угла}
    GraphicAspectRatio := Form1.Image1.Picture.Height /
    Form1.Image1.Picture.Width;
    iPixelsPerInch := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
    jPixelsPerInch := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
    PixelAspectRatio := jPixelsPerInch / iPixelsPerInch;
    TargetRectangle := Rect(4 * iPixelsPerInch, {4"}
    jPixelsPerInch, {1"}
    6 * iPixelsPerInch, {6" -- 2" ширина}
    jPixelsPerInch +
    TRUNC(2 * iPixelsPerInch * GraphicAspectRatio *
    PixelAspectRatio));
    Printer.Canvas.TextOut(4 * iPixelsPerInch, jPixelsPerInch -
    Printer.Canvas.TextHeight('X'),
    '2" ширина от (4", 1")');
    Printer.Canvas.StretchDraw(TargetRectangle, Form1.Image1.Picture.Graphic);
    {Создаем изображение в памяти и затем копируем его на холст принтера}
    SourceRectangle := Rect(0, 0, 3 * iPixelsPerInch - 1, 2 * jPixelsPerInch -
      1);
    {Это не должно работать!  Rectangle = Left, Top, Right, Bottom
    Top и Bottom считаются зарезервированными?}
    DestinationRectangle := Rect(4 * iPixelsPerInch, 6 * jPixelsPerInch,
    7 * iPixelsPerInch - 1, 4 * jPixelsPerinch - 1);
    Printer.Canvas.TextOut(4 * iPixelsPerInch, 4 * jPixelsPerInch -
    Printer.Canvas.TextHeight('X'),
    IntToStr(3 * iPixelsPerInch) + ' пикселей на ' +
    IntToStr(2 * jPixelsPerInch) + ' пикселей -- ' +
    '3"-на-2" в (4",4")');
    OffScreen := TBitMap.Create;
    try
      OffScreen.Width := SourceRectangle.Right + 1;
      OffScreen.Height := SourceRectangle.Bottom + 1;
      with OffScreen.Canvas do
      begin
        Pen.Color := clBlack;
        Brush.Color := clWhite;
        Rectangle(0, 0, 3 * iPixelsPerInch - 1, 2 * jPixelsPerInch - 1);
        Brush.Color := clRed;
        MoveTo(0, 0);
        LineTo(3 * iPixelsPerInch - 1, 2 * jPixelsPerInch - 1);
        Brush.Color := clBlue;
        MoveTo(0, 0);
        for i := 0 to 3 * iPixelsPerInch - 1 do
        begin
          x := 12 * PI * (i / (3 * iPixelsPerInch - 1));
          y := jPixelsPerInch + jPixelsPerInch * SIN(x);
          LineTo(i, TRUNC(y));
        end
      end;
      Printer.Canvas.CopyRect(DestinationRectangle, OffScreen.Canvas,
        SourceRectangle);
    finally
      OffScreen.Free
    end;
    {Список шрифтов для данного принтера}
    iBase := iPosition(750);
    Printer.Canvas.Font.Name := 'Times New Roman';
    Printer.Canvas.Font.Size := 12;
    Printer.Canvas.Font.Style := [fsBold];
    Printer.Canvas.TextOut(iBase, jPosition(40), 'Шрифты');
    Printer.Canvas.Font.Style := [];
    Printer.Canvas.Font.Size := 10;
    jDelta := 16;
    for j := 0 to Printer.Fonts.Count - 1 do
    begin
      Printer.Canvas.TextOut(iBase, jPosition(60 + jDelta * j),
        Printer.Fonts.Strings[j])
    end;
    Printer.EndDoc;
end;
end.

Сохранение пользовательских настроек.

Автор: Mike Scott
Сын системного администратора перед сном просит папу: - Па-па... Расскажи сказку про умного, толкового, доброго, смелого юзера...
...и я хотел бы новые пользовательские цвета записывать на место старых, записываемых Delphi в файл ресурса, чтобы при следующем открытии формы, она автоматически сама отображала бы новую цветовую схему.

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

const
  FileName = 'Form1.stm';
constructor TForm1.Create(AOwner: TComponent);
begin
  if FileExists(FileName) then
  begin
    CreateNew(AOwner);
    ReadComponentResFile(FileName, Self);
  end
  else
    inherited Create(AOwner);
  { поместите здесь код в стиле 'OnCreate' }
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  WriteComponentResFile(FileName, Self);
end;

Имейте в виду, что это не вызывает OnCreate. В действительности, вам OnCreate и не нужен, поскольку сюда вы можете поместить код, необходимый в обработчике OnCreate; данный код следует поместить после конструктора Create, как и указано в комментарии.

Как сохранить исходник HTML из TWebBrowser.Document на диск.

Статьи по Delphi (015)…
Серьезная организация возьмет на высокооплачиваемую должность опытного хакера. Резюме просим размещать на главной странице сайта www.microsoft.com.

TWebBrowser.Document включает в себя IPersistStreamInit который содержит метод Save(). Всё, что нам нужно знать, это как использовать данный метод с объектом, который включён в IStream. Для этого мы просто воспользуемся TStreamAdapter.

Обратите внимание, что интерфейсы IPersistStreamInit и IStream объявлены внутри ActiveX unit.

Итак, вот так это выглядит.

procedure TForm1.SaveHTMLSourceToFile(const FileName: string;
WB: TWebBrowser);
var
  PersistStream: IPersistStreamInit;
  FileStream: TFileStream;
  Stream: IStream;
  SaveResult: HRESULT;
begin
  PersistStream := WB.Document as IPersistStreamInit;
  FileStream := TFileStream.Create(FileName, fmCreate);
  try
    Stream := TStreamAdapter.Create(FileStream, soReference) as IStream;
    SaveResult := PersistStream.Save(Stream, True);
    if FAILED(SaveResult) then
      MessageBox(Handle, 'Fail to save HTML source', 'Error', 0);
  finally
    { В ответ на уничтожение объекта TFileStream, передаём
    soReference в конструктор TStreamAdapter. }
    FileStream.Free;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  if SaveDialog1.Execute then
    SaveHTMLSourceToFile(SaveDialog1.FileName, WebBrowser1);
end;

А как сохранить вместе с исходником все файлы (.CSS, JPG, GIF и т.д...) ?

try 
  WebBrowser1.ExecWB(4, 0); 
except 
  on E: Exception do 
    msError := true; 
end; 

Сохранение и восстановление положения и размеров окон.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение и восстановление положения и размеров окон
Процедуры SaveWndPos и SetWndPos работают в паре.
Сохраняют для конкретного пользователя Windows в реестре местоположение,
размер окон (и прочих Соntrоl'ов при явном указании),
а также ширины и порядок расположения колонок в DВGrid'ах на форме.
Не дает "потерять" колонки совсем.
Возможные проблемы:
1. С записью в реестр без прав (в некую левую ветку).
2. При изменении имен и количества полей в DВGrid'е.
3. С окнами неизменяемого размера, если его в Design-time поменяли.
Комментарии:
1. Статус колонки DBGrid (visible, readonly) не сохраняется, но сделать
это несложно.
2. Для формы, которая используются для разных целей, но хотелось бы
сохранить ее характеристики для каждой цели отдельно, можно явно
указывать УНИКАЛЬНЫЙ класс.
Важное уточнение:
Сохранение параметров DBGrid происходит ТОЛЬКО при статически
описанных колонках в самом гриде.
Еще комментарий:
параметр lSetSize в SetWndPos применяется только к размерам формы, а
не к ширине колонок. Но можно и это учесть.
Зависимости: Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, Buttons, Registry, Math, DB, DbTables, dbGrids
Автор:       Александр Хабаров aka Desdechado, x_a_u@mail.ru, Феодосия
Copyright:   Александр Хабаров
Дата:        13 мая 2002 г.
***************************************************** }
const
  cRegKey = '\Sоftwаrе\СооlSоft\Настройки\';
procedure SaveWndPos(frm: TControl; cClass: string = '');
var
  rct: TRect;
  oReg: TRegistry;
  grid: TDBGrid;
  i, j: Integer;
begin
  {--- определить параметры ---}
  rct := frm.BoundsRect;
  if (cClass = '') then
    cClass := frm.ClassName;
  {--- сохранить в реестре ---}
  oReg := TRegistry.Create();
  with (oReg) do
  try
    RootKey := HKEY_CURRENT_USER;
    {--- ... размер окна ---}
    if (OpenKey(cRegKey + cClass, True)) then
    begin
      if (frm is TForm) then
        WriteInteger('Show', Ord((frm as TForm).WindowState));
      if (not (frm is TForm) or
        ((frm as TForm).WindowState = wsNormal)) then
      begin
        WriteInteger('Left', rct.Left);
        WriteInteger('Top', rct.Top);
        WriteInteger('Right', rct.Right);
        WriteInteger('Bottom', rct.Bottom);
      end;
    end;
    CloseKey();
    {--- ... размеры и положение колонок в Grid'ах ---}
    for i := 0 to frm.ComponentCount - 1 do
      if (frm.Components[i].ClassName = D
        if (frm.Components[i].ClassName = 'TDBGrid') then
        begin
          grid := TDBGrid(frm.Components[i]);
          for j := 0 to grid.Columns.Count - 1 do
          begin
            if (OpenKey(cRegKey + cClass + '\' + grid.Name + '\' +
              IntToStr(j), True)) then
            begin
              WriteString('Name', grid.Columns[j].FieldName);
              WriteInteger('Width', grid.Columns[j].Width);
            end;
            CloseKey();
          end; // for( j )
        end;
  finally
    CloseKey();
    Free;
  end;
end;
procedure SetWndPos(frm: TControl; lSetSize: Boolean = True;
  cClass: string = '');
var
  rct: TRect;
  oReg: TRegistry;
  nShow, i, j, k: Integer;
  grid: TDBGrid;
  cName: string;
begin
  nShow := Ord(wsNormal);
  {--- текущие параметры (на случай, если нет в реестре) ---}
  rct := frm.BoundsRect;
  if (cClass = '') then
    cClass := frm.ClassName;
  if (frm is TForm) then
    nShow := Ord((frm as TForm).WindowState);
  {--- считать из реестра ---}
  oReg := TRegistry.Create();
  with (oReg) do
  try
    RootKey := HKEY_CURRENT_USER;
    {--- ... размер окна ---}
    if (OpenKeyReadOnly(cRegKey + cClass)) then
    begin
      if (frm is TForm) then
        nShow := ReadInteger('Show');
      if (ValueExists('Left')) then
        rct.Left := ReadInteger('Left');
      if (ValueExists('Top')) then
        rct.Top := ReadInteger('Top');
      if (ValueExists('Right')) then
        rct.Right := ReadInteger('Right');
      if (ValueExists('Bottom')) then
        rct.Bottom := ReadInteger('Bottom');
    end;
    CloseKey();
    {--- ... размеры и положение колонок в Grid'ах ---}
    for i := 0 to frm.ComponentCount - 1 do
      if (frm.Components[i].ClassName = 'TDBGrid') then
      begin
        grid := TDBGrid(frm.Components[i]);
        for j := 0 to grid.Columns.Count - 1 do
        begin
          if (OpenKeyReadOnly(cRegKey + cClass + '\' + grid.Name + '\' +
            IntToStr(j))) then
          begin
            cName := ReadString('Name');
            for k := 0 to grid.Columns.Count - 1 do
              if (grid.Columns[k].FieldName = cName) then
              begin
                grid.Columns[k].Index := j;
                break;
              end;
            grid.Columns[j].Width := ReadInteger('Width');
          end;
          CloseKey();
          {--- экстремальные ширины - нормировать ---}
          grid.Columns[j].Width := Max(grid.Columns[j].Width, 3);
          grid.Columns[j].Width := Min(grid.Columns[j].Width,
            grid.ClientWidth - 3);
        end; // for( j )
      end;
  finally
    CloseKey();
    Free;
  end;
  {--- применить считанное к окну ---}
  if (lSetSize) then
    frm.BoundsRect := rct
  else
  begin
    frm.Top := rct.Top;
    frm.Left := rct.Left;
  end;
  if ((frm is TForm) and (nShow = Ord(wsMaximized))) then
    (frm as TForm).WindowState := wsMaximized;
end;
procedure FitDeskTop(frm: TControl);
var
  rct: TRect;
begin
  rct := frm.BoundsRect;
  if (rct.Top < 0) then
    rct.Top := 0
  else if (rct.Bottom >= Screen.DeskTopHeight) then
    rct.Top := rct.Top + Screen.DeskTopHeight - rct.Bottom - 1;
  if (rct.Left < 0) then
    rct.Left := 0
  else if (rct.Right >= Screen.DeskTopWidth) then
    rct.Left := rct.Left + Screen.DeskTopWidth - rct.Right - 1;
  rct.Bottom := rct.Top + frm.Height;
  rct.Right := rct.Left + frm.Width;
  frm.BoundsRect := rct;
end;

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

{
0. не забудьте поменять константу для СВОЕЙ ветки в реестре
1. после создания окна перед Show
2. перед frm.Release (удалением) надо вызывать SaveWndPos
}
procedure TfrmCard.FormCreate(Sender: TObject);
begin
  // прочий код здесь
  SetWndPos(Self);
end;
procedure TfrmCard.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // прочий код здесь
  SaveWndPos(Self);
  Action := caFree;
end;

Сохранить документ Word как RTF.

uses 
  ComObj; 
function ConvertDoc2Rtf(var FileName: string) : Boolean;
var 
  oWord: OleVariant; 
  oDoc: OleVariant; 
begin 
  Result := False; 
  try 
    oWord := GetActiveOleObject('Word.Application'); 
  except 
    oWord := CreateOleObject('Word.Application'); 
  end; 
  oWord.Documents.Open(FileName); 
  oDoc  := oWord.ActiveDocument; 
  FileName := ChangeFileExt(FileName, '.rtf'); 
  oDoc.SaveAs(FileName); 
  oWord.ActiveDocument.Close(wdDoNotSaveChanges, EmptyParam, EmptyParam); 
  oWord.Quit(EmptyParam, EmptyParam, EmptyParam); 
  oDoc := VarNull; 
  oWord := VarNull; 
  Result := True; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
const 
  FileName = 'c:\Document.doc'; 
begin 
  if ConvertDoc2Rtf(FileName) then 
  begin 
    ShowMessage('Word document has been converted to .rtf'); 
    RichEdit1.Lines.LoadFromFile(FileName); 
  end; 
end;

Изменить шрифт TStatusBar.

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

{ 
  To use this example, you need a TStatusBar and at least one Panel. 
  Then change the style of StatusBar1.Panels[0] to 
  psOwnerDraw and add the code below to the OnDrawPanel handler. 
  Es wird eine TStatusBar fur dieses Beispiel benotigt mit 
  mindestens einem Panel. 
  Der Style von StatusBar1.Panels[0] muss zu psOwnerDraw 
  geandert werden. 
  Fuge dann folgenden Code im OnDrawPanel Ereignis hinzu: 
}
 procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar;
   Panel: TStatusPanel; const Rect: TRect);
 var
   SomeText: string;
 begin
   if Panel = StatusBar1.Panels[0] then
   begin
     SomeText    := 'Hello!';
     Panel.Width := Trunc(StatusBar1.Canvas.TextWidth(SomeText) * 1.5);
     with StatusBar.Canvas do
      begin
       Brush.Color := clWhite;
       FillRect(Rect);
       Font.Name  := 'Arial';
       Font.Color := clRed;
       Font.Style := Font.Style + [fsItalic, fsBold];
       TextRect(Rect, Rect.Left + 1, Rect.Top, SomeText);
     end;
   end;
 end;

Из ресурсов поочередно загружать глифы для кнопок SpeedButton.

Автор: Dennis Passmore Могу ли я из ресурсов поочередно загружать глифы для кнопок speedbutton и, если да, то как это сделать?

Например, если в вашем проекте используется TDBGrid, то иконки кнопок компонента DBNavigator могут линковаться вашей программой, и их можно загрузить для использования в ваших speedbutton следующим образом:

SpeedButton.Caption := '';
SpeedButton1.Glyph.LoadFromResourcename(HInstance,'DBN_REFRESH');
SpeedButton1.NumGlyphs := 2;
Другие зарезервированные имена: DBN_PRIOR, DBN_DELETE, DBN_CANCEL, DBN_EDIT, DBN_FIRST, DBN_INSERT, DBN_LAST, DBN_NEXT, DBN_POST

Все имена должны использовать верхний регистр.

Прокрутка TScrollBox с помощью стрелок вверх и вниз.

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

private
   procedure CMDialogKey(var Msg: TCMDialogKey); message CM_DIALOGKEY;
 end;
 implementation
 procedure TForm1.CMDialogkey;
 begin
   with Scrollbox1.VertScrollBar do
   begin
     case Msg.CharCode of
       // Delphi takes care of Position < 0 or Position > Range 
      VK_DWON: Position := Position + Increment; // down arrow key 
      VK_Up: Position   := Position - Increment; // up arrow key 
      else
         inherited;
     end;
   end;
 end;

Масштабирование окна.

Автор: Andrey Kozlov.

implementation
const
  ScreenWidth: Integer = 800; {Я разрабатывал свою форму в режиме 800x600.}
  ScreenHeight: Integer = 600;
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
  x, y: LongInt; {Тип Integer не достаточно большой для наших значений.}
begin
  form1.scaled := true;
  x := getSystemMetrics(SM_CXSCREEN);
  y := getSystemMetrics(SM_CYSCREEN);
  if (x <> ScreenHeight) or (y <> ScreenWidth) then
  begin
    form1.height := form1.height * x div ScreenWidth;
    form1.width := form1.width * y div ScreenHeight;
  end;
  if x <> ScreenWidth then
    scaleBy(x, ScreenWidth);
end;
Дополнение

Файл DELSEQ07.FAQ содержит код примера отображения форм в различных разрешениях. К сожалению, он не учитывал ширину границы окна. Я публикую изменение, масштабирующее компоненты вне зависимости от разрешения экрана и ширины границ окон. Включите нижеследующий модуль в секцию uses каждого модуля и вызывайте ScaleForm в обработчике формы OnCreate, передавая в качестве параметра имя формы. Я надеюсь что помог тем, кто столкнулся с данной проблемой.

unit scale;
interface
uses
  Forms, WinTypes, WinProcs, SysUtils;
procedure ScaleForm(Sender: TObject);
implementation
procedure ScaleForm(Sender: TObject);
const
  {измените это так, чтобы это соответствовало
  режиму разрешения во время разработки}
  DesignScrY: LongInt = 480;
  DesignScrX: LongInt = 640;
  DesignBorder: LongInt = 4; {значение в Панели Управления + 1}
var
  SystemScrY: LongInt;
  SystemScrX: LongInt;
  SystemBorder: LongInt;
  OldHeight: LongInt;
  OldWidth: LongInt;
begin
  SystemScrY := GetSystemMetrics(SM_CYSCREEN);
  SystemScrX := GetSystemMetrics(SM_CXSCREEN);
  SystemBorder := GetSystemMetrics(SM_CYFRAME);
  with Sender as TForm do
  begin
    Scaled := True;
    AutoScroll := False;
    Top := Top * SystemScrX div DesignScrX;
    Left := Left * SystemScrX div DesignScrX;
    OldHeight := Height + (DesignBorder - SystemBorder) * 2;
    OldWidth := Width + (DesignBorder - SystemBorder) * 2;
    ScaleBy((OldWidth * SystemScrX div DesignScrX - SystemBorder * 2),
      (OldWidth - DesignBorder * 2));
    {
    Для форм не имеющих границ измените предшествующие
    три строки следующим способом:
    OldHeight := Height;
    OldWidth  := Width;
    ScaleBy(SystemScrX, DesignScrX);
    }
    Height := OldHeight * SystemScrY div DesignScrY;
    Width := OldWidth * SystemScrX div DesignScrX;
  end;
end;
begin
end.

Масштабирование окна 2.

Вывод формы с различными разрешениями экрана.

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

implementation
const
  ScreenWidth: LongInt = 800; {Я разрабатывал мою форму в режиме 800x600.}
  ScreenHeight: LongInt = 600;
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
  scaled := true;
  if (screen.width <> ScreenWidth) then
  begin
    height := longint(height) * longint(screen.height) div ScreenHeight;
    width := longint(width) * longint(screen.width) div ScreenWidth;
    scaleBy(screen.width, ScreenWidth);
  end;
end;

Затем, вероятно, вы захотите иметь нечто, проверяющее размер шрифтов, OK. Прежде, чем вы измените размер шрифта, вам необходимо убедиться, что объект имеет свойство font. Это может быть сделано следующим образом:

uses typinfo;
var
  i: integer;
begin
  for i := componentCount - 1 downtto 0 do
    with components[i] do
    begin
      if GetPropInfo(ClassInfo, 'font') <> nil  then
        font.size := (NewFormWidth DIV OldFormWidth) * font.size;
    end;
end;
Примечание: При разработке приложения для различных режимов разрешения вам необходимо учитывать следующие рекомендации: Заранее, в самом начале этапа разработки, решите для себя - собираетесь ли вы разрешать масштабировать форму или нет. Преимущество запрета масштабирования в том, что вам ничего не нужно менять во время выполнения приложения. Недостаток запрета масштабирования - во время выполнения приложения никаких изменений не происходит (ваша форма может быть слишком малой или слишком большой для работы в некоторых режимах при отсутствии масштабирования). Если вы НЕ собираетесь масштабировать форму, установите свойство Scaled в False. В противном случае, установите свойство формы Scaled в True. Установите AutoScroll в False. AutoScroll = True означает 'не изменять размер окна формы во время выполнения приложения', что приводит к "плохому виду" формы, если ее содержимое меняет размер. Установите шрифты формы в масштабируемые TrueType-шрифты типа Arial. MS San Serif также подойдет в качестве альтернативы, только помните, это не TrueType, а bitmapped-шрифт. Только Arial может правильно изменять свою высоту с дискретностью 1 пиксел. Примечание: Если используемый шрифт не установлен на машине пользователя, Windows выбирает альтернативный шрифт из данной линейки (семьи) шрифтов. Размеры нового шрифта могут отличаться от размеров оригинального шрифта, что также может вызвать проблемы. Установите свойство формы Position во что-нибудь другое, чем poDesigned. poDesigned всегда показывает форму в первозданном виде, и, если форма разрабатывалась в разрешении 1280x1024, то вы можете себе представить, что будет при разрешении 640x480? Не "слепляйте" на форме элементы управления, оставляйте между ними, по крайней мере, 4 пикселя, в противном случае, при изменении месторасположения границы на 1 пиксель (это происходит при масштабировании), элементы управления наедут друг на друга. Для однострочных компонентов Label, у которых свойство Aligned равно alLeft или alRight, установите AutoSize в True. В противном случае, установите AutoSize в False. Убедитесь в том, что компоненты Label имеют достаточный запас по ширине (требуется, примерно, 25%) от длины текущего текста. (При переводе вашего приложения на другие языки вам необходимо примерно 30%-ный запас от текущей ширины текста). Если AutoSize - False, убедитесь, что ширины компонента Label достаточно для размещения реального текста. Если AutoSize - True, убедитесь, что на компоненту Label достаточно места (например, на форме) для размещения всего текста плюс небольшой запас для его роста при смене шрифтов. В случае многострочного текста и компонентов Label с переносом слов, убедитесь, что в нижней части у вас имеется, по крайней мере, еще одна строчка. Она необходима вам для того, чтобы не допустить переполнения строки, если размер шрифта увеличивается при масштабировании. Не думайте, что, если вы используете большие шрифты и переполнения не возникает, то эта проблема снята - кто-нибудь может использовать шрифты с еще большим размером, чем у вас! Будьте осторожными при открытии проекта в IDE с другим разрешением. Свойство формы PixelsPerInch будет изменено как только вы откроете форму, и сохранено в DFM-файле при сохранении проекта. Лучше всего запускать приложение отдельно от IDE, а редактировать его при одном разрешении. Редактируя формы при различных разрешениях и размерах шрифтов, вы инициируете проблему "дрейфа" компонентов по форме и изменения их размера. Говоря о дрейфе компонент, не следует многократно масштабировать форму, как во время разработки, так и во время выполнения приложения. Каждое изменение размеров сопровождается ошибками округления, которые достаточно быстро накапливаются с тех пор, как координаты стали строго целочисленными. Поскольку при калькулировании новых размеров дробная часть отбрасывается, вновь пересчитанные размеры оказываются меньше, а координаты элементов управления северо-западнее. Если вы решили разрешить пользователю изменять масштабы форм, начинайте масштабирование с последней загруженной/созданной формы, этим вы уменьшите накапливаемые при масштабировании ошибки. Старайтесь не изменять значение свойства формы PixelsPerInch. В общих словах, нет необходимости разрабатывать формы для всех возможных режимов, перед окончательным релизом вашего приложения вы должны оценить поведение формы в пограничных режимах - 640x480 с маленькими и большими шрифтами, и при высоком разрешении и, также, с маленькими и большими шрифтами. Это должно быть частью ваших регулярных проверок на предмет системной совместимости, для ведения так называемой тестирующей контрольной таблицы. Обратите пристальное внимание на "однострочные компоненты TMemo" - типа TDBLookupCombo. Системные многострочные редакторы всегда выводят только целые строки текста - если ширина элемента управления слишком мала для своего шрифта, то TMemo вообще ничего не показывает (TEdit показывает обрезанный текст). Размер таких компонентов лучше сделать на несколько пикселей больше, чем на несколько пикселей меньше, тем самым можно определеть наличие в компоненте оставшейся части текста. Обратите внимание на то, что масштабирование во время проектирования и во время выполнения программы отличается коэффициентом и зависит от высоты шрифта, а не от экранного разрешения в пикселях. Помните также, что "начало" компонент будет изменяться в зависимости от масштаба формы, и для их "броуновского" движения также необходимо небольшое пространство.

Масштабирование размера формы и размера шрифтов.

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

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

unit geScale;
interface
uses Forms, Controls;
procedure geAutoScale(MForm: TForm);
implementation
type
  TFooClass = class(TControl); { необходимо выяснить защищенность }
  { свойства Font }
procedure geAutoScale(MForm: TForm);
const
  cScreenWidth: integer = 800;
  cScreenHeight: integer = 600;
  cPixelsPerInch: integer = 96;
  cFontHeight: integer = -11; {В режиме проектирование значение из Font.Height}
var
  i: integer;
begin
  {
  ВАЖНО!! : Установите в Инспекторе Объектов свойство Scaled TForm в FALSE.
  Следующая программа масштабирует форму так, чтобы она выглядела одинаково
  внезависимости от размера экрана и пикселей на дюйм. Расположенный ниже
  участок кода проверяет, отличается ли размер экрана во время выполнения
  от размера во время проектирования. Если да, Scaled устанавливается в True
  и компоненты снова масштабируются так, чтобы они выводились в той же
  позиции экрана, что и во время проектирования.
  }
  if (Screen.width &; lt > cScreenWidth) or (Screen.PixelsPerInch <>
    cPixelsPerInch) then
  begin
    MForm.scaled := TRUE;
    MForm.height := MForm.height * screen.Height div cScreenHeight;
    MForm.width := MForm.width * screen.width div cScreenWidth;
    MForm.ScaleBy(screen.width, cScreenWidth);
  end;
  {
  Этот код проверяет, отличается ли размер шрифта во времы выполнения от
  размера во время проектирования. Если во время выполнения pixelsperinch
  формы отличается от pixelsperinch во время проектирования, шрифты снова
  масштабируются так, чтобы форма не отличалась от той, которая была во
  время разработки. Масштабирование производится исходя из коэффициента,
  получаемого путем деления значения font.height во время проектирования
  на font.height во время выполнения. Font.size в этом случае работать не
  будет, так как это может дать результат больший, чем текущие размеры
  компонентов, при этом текст может оказаться за границами области компонента.
  Например, форма создана при размерах экрана 800x600 с установленными
  маленькими шрифтами, имеющими размер font.size = 8. Когда вы запускаете
  в системе с 800x600 и большими шрифтами, font.size также будет равен 8,
  но текст будет бОльшим чем при работе в системе с маленькими шрифтами.
  Данное масштабирование позволяет иметь один и тот же размер шрифтов
  при различных установках системы.
  }
  if (Screen.PixelsPerInch <> cPixelsPerInch) then
  begin
    for i := MForm.ControlCount - 1 downto 0 do
      TFooClass(MForm.Controls[i]).Font.Height :=
        (MForm.Font.Height div cFontHeight) *
        TFooClass(MForm.Controls[i]).Font.Height;
  end;
end;
end.

Технология без интересного имени или как работать со сканером.

Автор: Павел В настоящее время в конференциях то и дело встречаются вопросы типа: как мне получить изображение со сканера, с web камеры и т.д.. При том, что и интернете практически полностью отсутствуют материалы по этим вопросам на русском языке и при достаточном разнообразии их на английском. Эта статья должна помочь начинающему программисту на Delphi разобраться в них. В статье подробно, с примерами описана работа со сканером с использованием популярной библиотеки Easy TWAIN. Введение В отличие от принтеров сканеры изначально не поддерживались ОС Windows и не имеют API для работы с ними. В начале своего появления сканеры взаимодействовали с программами посредством уникального для каждой модели сканера интерфейса, что серьезно затрудняло включение поддержки работы со сканером в прикладные программы. Для решения этой проблемы был разработан TWAIN - индустриальный стандарт интерфейса программного обеспечения для передачи изображений из различных устройств в Windows и Macintosh. Стандарт издан и поддерживается TWAIN рабочей группой - официальный сайт www.twain.org. Стандарт издан в 1992 г. В настоящее время действует версия 1.9 от января 2000 г. Абревеатура TWAIN изначально не имела какого-то определенного смысла хотя позже была придумана расшифровка: (Technology Without An Interesting Name - Технология без интересного имени). TWAIN - не протокол аппаратного уровня, он требует драйвера (названного Data Source или DS) для каждого устройства. К настоящему времени (май 2000 г.) TWAIN доступен для Windows 3.1 и выше (Intel и совместимые процессоры), Macintosh и OS/2. Для Linux самый близкий стандарт - SANE. Менеджер TWAIN (DSM) - действует как координатор между приложениями и Источником Данных (Data Source). DSM имеет минимальный пользовательский интерфейс - только выбор DS. Все взаимодействие с пользователем вне прикладной программы осуществляется по средствам DS. Каждый источник данных разрабатывается непосредственно производителем соответствующих устройств. И их поддержка стандарта TWAIN осуществляется на добровольной основе. Использование TWAIN DSM и DS это DLLs загружаемые в адресное пространство приложения и работают как подпрограммы приложения. DSM использует межпроцессную связь, что бы координировать действия со своими копиями, когда больше чем одна программа использует TWAIN.

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

Открыть диалог настройки соответствующего устройства (диалог отображает DS) и задать соответствующие настройки. Приложение ожидает сообщение от DS, что изображение готово. Во время ожидания все зарегистрированные сообщения будут направляться через TWAIN. Если это не будет выполняться, то приложение не получит сообщения о готовности изображения. Приложение принимает изображение от DS. TWAIN определяет три типа передачи изображения: Native - в Windows это DIB в памяти Memory - как блоки пикселей в буферах памяти File - DS записывает изображение непосредственно в файл (не обязательно поддерживается) Приложение закрывает DS. Использование EZTWAIN Данная библиотека была разработана, что бы упростить разработку программ использующих TWAIN предоставляя разработчику упрощенную версию TWAIN API. EZTWAN обеспечивает передачу всех windows сообщений через TWAIN и ожидает сообщения о готовности изображения. Библиотека EZTWAIN является свободно распространяемой библиотекой с открытыми исходными кодами. В настоящее время выпущена версия 1.12. Библиотеку можно свободно скачать с сайта: www.dosadi.com, библиотека написана на C и предназначена для использования как DLL, необходимый для ее использования с Delphi модуль так же можно скачать с сайта. Кроме нее у меня с сайта можно скачать модификацию данной библиотеки, предназначенную для статической компоновки с программой на Delphi. Указанная версия (MultiTWAIN for Delphi) не требует наличия библиотеки EZTW32.DLL. Структура программы Используемые функции. Перед вызовом функций сканирования необходимо вызвать функцию: TWAIN_SelectImageSource(hwnd: HWND): Integer; Данная функция позволяет выбрать источник получения данных из списка TWAIN совместимых устройств, в качестве параметра она получает хендл основного окна прикладной программы. Следует заменить, что если в системе имеется одно TWAIN совместимое устройство, то вызывать функцию не обязательно. Для получения изображения служит функция: TWAIN_AcquireNative(hwnd: HWND; pixmask: Integer): HBitmap; где: hwnd - хендел основного окна прикладной программы (допускается указывать 0); pixmask - режим сканирования ( необходимо задавать 0 - указание другого режима может приводить к ошибке); hBitmap - указатель на область памяти, содержащей полученные данные в DIB формате. По окончании работы с DIB данными их необходимо удалить вызвав процедуру: TWAIN_FreeNative(hDIB: HBitmap); где: hDIB - указатель, полученный при вызове функции TWAIN_AcquireNative.

Для облегчения обработки полученных DIB данных в библиотеке имеется несколько сервисных функций:

TWAIN_DibWidth(hDib: HBitmap): Integer;
 // Получает ширину изображения в пикселях
 TWAIN_DibHeight(hDib: HBitmap): Integer;
// Получает высоту изображения в пикселях
 TWAIN_CreateDibPalette(hdib: HBitmap): Integer;
// Получает цветовую палитру изображения
 TWAIN_DrawDibToDC(hDC: HDC;
  dx, dy, w, h: Integer;
  hDib: HBitmap;
  sx, sy: Integer);
// Передает DIB данные в формате совместимым
// с указанным контекстом устройства.
Пример программы

Полный текст примера можно взять отсюда. Мы рассмотрим только функцию получения данных с TWAIN устройства:

procedure TForm1.Accquire1Click(Sender: TObject);
var
  dat: hBitMap;
  PInfo: PBitMapInfoHeader;
  Height, Width: integer;
  {Функция возведения 2 в степень s}
  function stp2(s: byte): longint;
  var
    m: longint;
    i: byte;
  begin
    m := 2;
    for i := 2 to s do
      m := m * 2;
    stp2 := m;
  end;
begin
  {Получаем указатель на графические данные}
  dat := TWAIN_AcquireNative(Handle, 0);
  if dat <> 0 then
  begin
    {Получаем указатель на область памяти содержащей DIB
     данные и блокируем область памяти}
    PInfo := GlobalLock(dat);
    {Анализируем полученные данные}
    Height := PInfo.biHeight;
    Width := PInfo.biWidth;
    {Узнаем размер полученного изображения в сантиметрах}
    Wcm.Caption := floatToStrF(100 / PInfo.biXPelsPerMeter * Width, ffNumber, 8,
      3)
      + ' cm';
    Hcm.Caption := floatToStrF(100 / PInfo.biYPelsPerMeter * Height, ffNumber,
      8, 3)
      + ' cm';
    {Определяем число цветов в изображении}
    Colors.Caption := floatToStrF(stp2(PInfo.biBitCount), ffNumber, 8, 0) +
      ' цветов';
    {Разблокируем память}
    GlobalUnlock(dat);
    {Передаем в битовую матрицу графические данные}
    {И устанавливаем перехват ошибок}
    try
      MyBitMap.Palette := TWAIN_CreateDibPalette(dat);
      MyBitMap.Width := Width;
      MyBitMap.Height := Height;
      TWAIN_DrawDibToDC(MyBitMap.Canvas.Handle, 0, 0, Width, Height, dat, 0, 0);
    except
      // Обрабатываем наиболее вероятную ошибку связанную
      // с не хваткой ресурсов для загрузки изображения
      on EOutOFResources do
        MessageDlg('TBitMap: Нет ресурсов для загрузки изображения!',
          mtError, [mbOk], 0);
    end;
    {Отображаем графические данные}
    Image1.Picture.Graphic := MyBitMap;
    {Освобождаем память занятую графическими данными}
    TWAIN_FreeNative(dat);
  end;
end;
Обработка ошибок необходима, так как объект TBitMap имеет серьезные ограничения на размер создаваемого изображения. При этом производится обработка наиболее вероятной ошибки, в случае возникновения другой ошибки, ее обработка будет передана обработчику по умолчанию. Обработка ошибки в данном случае заключается в выдаче диагностического сообщения, в прикладной программе можно реализовать выполнение любых необходимых действий, например, произвести уменьшение разрешения и повторно подать на загрузку в TBitMap. Заключение

Приведенный здесь пример тестировался на сканере Umax 2000P с драйвером VistaScan32 V3.52. При получении изображений следует помнить, что максимальный размер блока памяти, который может распределить Windows, составляет 2 Гб и при попытке сканировании страниц формата АЧ с высоким разрешением можно превысить этот предел. Кроме того, достаточно простой в обращении объект TBitMap имеет куда более серьезные ограничения на размер загружаемых изображений, что требует непосредственной работы с DIB данными. Но это уже тема для отдельной статьи. Если у Вас появились вопросы или предложения пишите мне: speclab@4unet.ru.

Сканирование версии структуры базы данных.

Спасибо за идеи, высказанные в группах новостей и присланные по электронной почте. Я думаю, что нашел лучшее решение.

Очевидно, BDE содержит номер версии структуры, по крайней мере для файлов Paradox. (Я не могу поручиться за dBase и другие форматы.) Всякий раз при изменении структуры (например, в Database Desktop) BDE увеличивает номер версии. Следующий модуль содержит функцию, которая возвращает версию структуры базы данных:

(*****************************************************************************
* DbUtils.pas
*
* Утилита для работы с базами данных
*
* Создана 09/20/96
*****************************************************************************)
unit Dbutils;
(****************************************************************************)
(****************************************************************************)
interface
(****************************************************************************)
(****************************************************************************)
uses DbTables;
function DbGetVersion(table: TTable): LongInt;
(****************************************************************************)
(****************************************************************************)
implementation
(****************************************************************************)
(****************************************************************************)
uses
  Db, DbiProcs, DbiTypes, {DbiErrs,}
  SysUtils;
{---------------------------------------------------------------------------}
(*
* Цель:                    определение номера версии структуры таблицы
* Параметры:               table (I) - интересующая нас таблица
* Возвращаемая величина:   номер версии
* Исключительная ситуация: EDatabaseError
*)
function DbGetVersion(table: TTable): LongInt;
var
  hCursor: hDBICur;
  tableDesc: TBLFullDesc;
  cName: array[0..255] of Char;
begin
  { копируем имя таблицы в строку 'с' }
  StrPCopy(cName, table.TableName);
  { просим BDE создать запись, содержащую информацию об определенной таблице }
  Check(DbiOpenTableList(table.DBHandle, True, False, cName, hCursor));
  { получаем запись, содержащую информацию о структуре }
  Check(DbiGetNextRecord(hCursor, dbiNOLOCK, @tableDesc, nil));
  { возвращаем поле записи, содержащее номер версии структуры нашей таблицы }
  Result := tableDesc.tblExt.iRestrVersion;
  Check(DbiCloseCursor(hCursor));
end;
end.

Рекурсивный проход дерева каталогов.

Автор: Vit WEB-сайт: http://forum.vingrad.ru

procedure ScanDir(Dir: string);
var
  SearchRec: TSearchRec;
begin
  if Dir <> '' then
    if Dir[length(Dir)] <> '\' then
      Dir := Dir + '\';
  if FindFirst(Dir + '*.*', faAnyFile, SearchRec) = 0 then
    repeat
      if (SearchRec.name = '.') or (SearchRec.name = '..') then
        continue;
      if (SearchRec.Attr and faDirectory) <> 0 then
        ScanDir(Dir + SearchRec.name)
          //we found Directory: "Dir+SearchRec.name"
      else
        Showmessage(Dir + SearchRec.name);
      //we found File: "Dir+SearchRec.name"
    until
      FindNext(SearchRec) <> 0;
  FindClose(SearchRec);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  ScanDir('c:');
end;

Как получить список файлов со всеми подкаталогами.

Оформил: DeeCo

Автор: Анатолий Подгорецкий.

procedure ScanDir(StartDir: string; Mask: string; List: TStrings);
var
  SearchRec: TSearchRec;
begin
  if Mask = '' then
    Mask := '*.*';
  if StartDir[Length(StartDir)] <> '\' then
    StartDir := StartDir + '\';
  if FindFirst(StartDir + Mask, faAnyFile, SearchRec) = 0 then
  begin
    repeat Application.ProcessMessages;
      if (SearchRec.Attr and faDirectory) <> faDirectory then
        List.Add(StartDir + SearchRec.Name)
      else if (SearchRec.Name <> '..') and (SearchRec.Name <> '.')thenbegin
        List.Add(StartDir + SearchRec.Name + '\');
      ScanDir(StartDir + SearchRec.Name + '\', Mask, List);
  end;
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
end;
Пример вызова, параметры 1. имя папки 2. маска, по умолчанию *.* 3. хранилище для резульатат, любой наследник от TString, например TStringList
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Clear;
  ScanDir('c:', '', ListBox1.Items);
  Label1.Caption := IntToStr(ListBox1.Items.Count);
end;

Сканируем файл в поисках текста.

function ScanFile(const FileName: string; 
  const forString: string; 
  caseSensitive: Boolean): Longint; 
  { returns position of string in file or -1, if not found } 
const 
  BufferSize = $8001;  { 32K+1 bytes } 
var 
  pBuf, pEnd, pScan, pPos: PChar; 
  filesize: LongInt; 
  bytesRemaining: LongInt; 
  bytesToRead: Word; 
  F: file; 
  SearchFor: PChar; 
  oldMode: Word; 
begin 
  Result := -1;  { assume failure } 
  if (Length(forString) = 0) or (Length(FileName) = 0) then Exit; 
  SearchFor := nil; 
  pBuf      := nil; 
  { open file as binary, 1 byte recordsize } 
  AssignFile(F, FileName); 
  oldMode  := FileMode; 
  FileMode := 0;    { read-only access } 
  Reset(F, 1); 
  FileMode := oldMode; 
  try { allocate memory for buffer and pchar search string } 
    SearchFor := StrAlloc(Length(forString) + 1); 
    StrPCopy(SearchFor, forString); 
    if not caseSensitive then  { convert to upper case } 
      AnsiUpper(SearchFor); 
    GetMem(pBuf, BufferSize); 
    filesize       := System.Filesize(F); 
    bytesRemaining := filesize; 
    pPos           := nil; 
    while bytesRemaining > 0 do  
    begin 
      { calc how many bytes to read this round } 
      if bytesRemaining >= BufferSize then 
        bytesToRead := Pred(BufferSize) 
      else 
        bytesToRead := bytesRemaining; 
      { read a buffer full and zero-terminate the buffer } 
      BlockRead(F, pBuf^, bytesToRead, bytesToRead); 
      pEnd  := @pBuf[bytesToRead]; 
      pEnd^ := #0; 
       { scan the buffer. Problem: buffer may contain #0 chars! So we 
         treat it as a concatenation of zero-terminated strings. } 
      pScan := pBuf; 
      while pScan < pEnd do  
      begin 
        if not caseSensitive then { convert to upper case } 
          AnsiUpper(pScan); 
        pPos := StrPos(pScan, SearchFor);  { search for substring } 
        if pPos <> nil then  
        begin { Found it! } 
          Result := FileSize - bytesRemaining + 
            Longint(pPos) - Longint(pBuf); 
          Break; 
        end; 
        pScan := StrEnd(pScan); 
        Inc(pScan); 
      end; 
      if pPos <> nil then Break; 
      bytesRemaining := bytesRemaining - bytesToRead; 
      if bytesRemaining > 0 then  
      begin 
       { no luck in this buffers load. We need to handle the case of 
         the search string spanning two chunks of file now. We simply 
         go back a bit in the file and read from there, thus inspecting 
         some characters twice 
       } 
        Seek(F, FilePos(F) - Length(forString)); 
        bytesRemaining := bytesRemaining + Length(forString); 
      end; 
    end; { While } 
  finally 
    CloseFile(F); 
    if SearchFor <> nil then StrDispose(SearchFor); 
    if pBuf <> nil then FreeMem(pBuf, BufferSize); 
  end; 
end; { ScanFile }

Сканирование shared-ресурсов.

Оформил: DeeCo

В результате экспериментов с NetBIOS был реализован метод сканирования Internet в поисках открытых для записи разделяемых (shared) ресурсов. Подобная идея уже применялась в Legion, но простое дополнение этой схемы простейшим алгоритмом подбора паролей позволило добиться неожиданно "хороших" результатов.

Алгоритм сканирования

Сканирование выполняется параллельно для каждого из заданных адресов (при наличии достаточных ресурсов у атакующего). Сканируется сеть класса C, кроме 1.2.3.0 и 1.2.3.255.

Первая стадия - сбор данных.

Послать запрос ping. Если нет ответа, то пропустить этот адрес (и не атаковать эту машину на второй стадии). Определить имя DNS (на всякий случай, оно нигде не используется) Попробовать установить null session(1). Попробовать получить список shared resources. Если null session установить не удалось, то закончить первую стадию для этого адреса. Попробовать получить список имен пользователей. Попробовать получить информацию об операционной системе, а именно Платформу (Windows 9x, Windows NT) Версию LAN Manager (Для NT: 4.00 => NT4, 5.00 => Windows 2000) Роль системы в сети: флаги Master Browser, Backup Browser, PDC, BDC, RAS dialin server.

Вторая стадия - подбор паролей. Все имена пользователей, полученные на первой стадии, объединяются в один список, что позволяет учесть доменную архитектуру систем на базе Windows NT. На этой стадии проверяются соединения типа net use \\1.2.3.4\IPC$ password /user:username. Если соединение успешно, то проверяются все возможность подключения ко всем shared resources, и, когда подключение успешно, возможность записи.

Проверить подключение со случайным именем (используется результат GetTickCount() в десятичном представлении) и пустым паролем. Если подключение успешно, то считать, что разрешено подключение Guest, и дальнейшие проверки паролей будут бессмысленны(2). Проверить shared resources и больше не подбирать пароли на эту машину. Проверить подключения со всеми именами, собранными на первой стадии. Проверяется пустой пароль и пароль, совпадающий с именем пользователя. Если подключение успешно, то проверить shared resources.

(1) Null session - анонимное соединение (с пустым именем и паролем). То же, что и net use \\1.2.3.4\IPC$ "" /user:"" (2) Это не совсем верно. Правильным вариантом было бы все равно проверять все имена, и каждый раз отслеживать изменения прав доступа к shared resources.

Защита

Атака может быть предотвращена следующими методами:

отключением NetBIOS over TCP/IP запретом доступа к портам 135 и 139 при помощи firewall запретом анонимного доступа к системе путем установки в 1 значения ключа HKLM\System\CurrentControlSet\Control\Lsa\RestrictAnonymous выбором устойчивых паролей запретом пользователя Guest или введением для него login station restrictions специальными программными средствами

Введение account lockout смысла, скорее всего, не имеет, поскольку проводятся всего две попытки угадать пароль.

Стандартное протоколирование доступа методами NT не позволяет установить IP атакующего (только NetBIOS имя машины и домен, которые могут быть произвольно изменены). Однако протокол позволяет идентифицировать прошедшую атаку: сначала идет одна запись об успешном входе в систему пользователя ANONYMOUS, а потом (после паузы около минуты) серия записей о неуспешных попытках входа с разными именами пользователей (в том числе и тех, которых нет на этой машине; по две записи на имя). Если среди них есть хотя бы одна запись об успешном входе, систему следует считать скомпрометированной.

Реализация

Для проверки метода была написана программа, реализующая вышеописанный алгоритм (это заняло около 6 часов вместе с тестированием и перерывами на покурить). Тестовые сканирования проводились на Dual Celeron 400MHz 96Mb RAM с одновременным запуском до 128 потоков.

Статистика

Был проведен ряд экспериментальных сканирований. В качестве источника адресов подсетей был использован Intruders list, полученный при помощи BlackICE, однако разработка алгорима быстрого определения "населенных" подсетей не представляет никакой сложности. Сканирование 254 адресов занимает от двух до десяти минут в зависимости от качества связи и общего количества имен пользователей в сети.

Кратко сформулированный результат такой:

примерно 10% паролей пользователей Windows может быть угадано с двух попыток примерно 40% Windows-машин, доступных по NetBIOS over TCP/IP, выставляют ресурсы в общее пользование без (достаточной) защиты примерно каждая третья share, доступная из Internet, разрешает запись

Кроме того: за два часа не очень напряженной работы можно найти три машины с Windows NT, к которым можно подключиться по сети с правами администратора (CONNECT: Administrator : <NO PASSWORD> :).

Детальная статистика:

Всего адресов просканировано 1524 100% На ping ответило 442 29% Null session установлено с 102 6.7% Guest разрешен(1) 57 3.7% Уязвимых машин(2) 45 3.0%

(1) Т.е. возможно подключение к IPC$ со случайным именем (2) Уязвимыми считались все машины, которые позволяли подключить разделяемый ресурс хотя бы на чтение.

Распределение платформ. Всего успешных запросов 102 100% Windows 95/98 58 57% Windows NT или SAMBA 44 43%

Пользователи. Всего имен проверено 177 100% С пустым паролем 10 6% С паролем, совпадающим с именем 10 6%

Shared resources. Всего директорий 278 100% Доступно на чтение 171 62% Доступно на запись 91 33%

Перспективы применения

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

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

Как получить Регистрационный Номер для Sceleton ScreenSaver.

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

Взять программу можно тут: http://psy.plex.ru или тут: http://www.bik.donetsk.ua/pc_club/ в разделе software) tutorial by vallkor (vallkor@chat.ru) В руки мне попался этот скринсевер и, впринципе, мне понравился. На самом деле, это скорее всего обысная модель из 3dStudioMax, ну да ладно, не об этом я. Так вот, скачал я эту программу и хотел на нее посмотреть, но жестоко обломался, когда увидел надпись на экране: "Зарегестрировать можно на psy.plex.ru". Ну, думаю, люди ваще офигели: даже в скринсейвера встраивают регистрацию. Как оказалось - регистрация бесплатна, но для этого нужно лезть в инет и искать что-то, т.к. у меня с инетом напряг, то лучше я её ломану :) Я открыл свойства рабочего стола, потом "Заставка", выбрал в меню заставок "Sceleton" и потом выбрал "Настройки", затем нажимаю "Register" и что же я вижу: "После регистрации текст с экрана исчезнет" - очень приятно Ж) Рег. имя: Рег. номер: Вводим vallkor/123321 окрываем СофтАйс (далее айс) и ставим брекпоинт bpx GetWindowTextA и нажимаем OK. брык! Выскочили в айсе, так, значит нажимаем F5, чтобы программа и наш регистрационный номер считала. Опа! Опять выскочили в айсе. Теперь нажимаем F11 чтобы выйти из вызова функции. И попадаем сюда:
lea edx,[ebp-08]
lea eax,[ebp-020C] ;в eax введенный нами РН
call 0040791C
mov edx,[ebp-08]  ;в edx введенный нами РН
mov eax,[ebp-04]  ;в eax введенное нами имя
call 0043A7F4     ;вызов функции генерации и проверки РН
Зайдем в эту функцию (F8) и вывалимся тут:
push ebp
mov ebp, esp
add esp, -10
push ebx
xor ecx, ecx
Это не то всё! Поэтому идем вниз по программе (F10) пока не наткнемся на это место:
call 0043A6FC ;здесь и "куют" нужный нам РН
mov eax,[ebp-0C] ;здесь нужный нам РН! нажимай d eax и он твой! (1)
mov edx,[ebp-08] ;а это введенный нами (2)
call 00403BB0    ;а это проверка наших РН'ов
Итак, как уже было сказано и показано в строке (1) в eax записывается сгенерированный РН, поэтому в строке (2) вводим d eax и в окне памяти в айсе мы видим нужный РН, пишем его на бумагу, закрываем айс, вводим его вместо "123321" и можно пользовать. ЗЫ: как оказалось впоследствие сгенерированный РН не затирается даже после сообщения о неверном РН, что ООЧень глупо! Поэтому можно было сделать так:
s ds:0 l ffffffff "123321" и рядом в памяти валялся бы нужный нам номер!
ЗЗЫ: спасибо автору icedump'a за встроенный тетрис :) Все пожелания и оскорбления присылайте сюда: vallkor@chat.ru

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

Оформил: DeeCo

Автор: Олег Агранович.

Программа для выключения компьютера в заданное время. Если запустить с параметром, указав время, то программа запустится скрытно и выключит компьютер в указанное время. Проверенно на Windows XP. Для выключения используется процедура:
procedure ShutdownComputer;
var
  ph: THandle;
  tp, prevst: TTokenPrivileges;
  rl: DWORD;
begin
  OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or
    TOKEN_QUERY, ph);
  LookupPrivilegeValue(nil, 'SeShutdownPrivilege', tp.Privileges[0].Luid);
  tp.PrivilegeCount := 1;
  tp.Privileges[0].Attributes := 2;
  AdjustTokenPrivileges(ph, FALSE, tp, SizeOf(prevst), prevst, rl);
  ExitWindowsEx(EWX_SHUTDOWN or EWX_POWEROFF, 0);
end;
Скачать (обновление от 01.07.02): Исполняемый файл Sleeper.zip (161 K) Исходные коды Sleeper_sources.zip (6 K)

ScreenMate.

Новая игра. Казахский DOOM! Никаких тебе лабиринтов! Голая степь!
Многие из вас знакомы с этим термином. Так характеризуют программы, которые выводят на экран спрайтового персонажа, не создавая при этом окна. Я очень давно искал данный пример в сети, и теперь решил вас порадовать. Программа состоит из нескольких узлов, кои будут приведены ниже...

P.s К сожалению вам надо позаботиться о кадрах анимации этого персонажа самим т.к рисунки я послать немогу...

{*******************************************************}
                                                      { }
                           { Delphi VCL Extensions (RX) }
                                                      { }
                    { Copyright (c) 1995, 1996 AO ROSNO }
                 { Copyright (c) 1997, 1998 Master-Bank }
                                                      { }
{*******************************************************}
unit Animate;
interface
{$I RX.INC}
uses Messages, {$IFDEF WIN32}Windows, {$ELSE}WinTypes, WinProcs,
{$ENDIF}
  SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, Menus,
  ExtCtrls;
type
  TGlyphOrientation = (goHorizontal, goVertical);
  { TRxImageControl }
  TRxImageControl = class(TGraphicControl)
  private
    FDrawing: Boolean;
  protected
    FGraphic: TGraphic;
    function DoPaletteChange: Boolean;
    procedure DoPaintImage; virtual; abstract;
    procedure PaintDesignRect;
    procedure PaintImage;
    procedure PictureChanged;
  public
    constructor Create(AOwner: TComponent); override;
  end;
  { TAnimatedImage }
  TAnimatedImage = class(TRxImageControl)
  private
    { Private declarations }
    FActive: Boolean;
    FAutoSize: Boolean;
    FGlyph: TBitmap;
    FImageWidth: Integer;
    FImageHeight: Integer;
    FInactiveGlyph: Integer;
    FOrientation: TGlyphOrientation;
    FTimer: TTimer;
    FNumGlyphs: Integer;
    FGlyphNum: Integer;
    FStretch: Boolean;
    FTransparentColor: TColor;
    FOpaque: Boolean;
    FTimerRepaint: Boolean;
    FOnFrameChanged: TNotifyEvent;
    FOnStart: TNotifyEvent;
    FOnStop: TNotifyEvent;
    procedure DefineBitmapSize;
    procedure ResetImageBounds;
    procedure AdjustBounds;
    function GetInterval: Cardinal;
    procedure SetAutoSize(Value: Boolean);
    procedure SetInterval(Value: Cardinal);
    procedure SetActive(Value: Boolean);
    procedure SetOrientation(Value: TGlyphOrientation);
    procedure SetGlyph(Value: TBitmap);
    procedure SetGlyphNum(Value: Integer);
    procedure SetInactiveGlyph(Value: Integer);
    procedure SetNumGlyphs(Value: Integer);
    procedure SetStretch(Value: Boolean);
    procedure SetTransparentColor(Value: TColor);
    procedure SetOpaque(Value: Boolean);
    procedure ImageChanged(Sender: TObject);
    procedure UpdateInactive;
    procedure TimerExpired(Sender: TObject);
    function TransparentStored: Boolean;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  protected
    { Protected declarations }
    function GetPalette: HPALETTE; override;
    procedure Loaded; override;
    procedure Paint; override;
    procedure DoPaintImage; override;
    procedure FrameChanged; dynamic;
    procedure Start; dynamic;
    procedure Stop; dynamic;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DoPaintImageOn(Mycanvas: Tcanvas; x, y: integer);
      virtual;
  published
    { Published declarations }
    property Active: Boolean read FActive write SetActive default
      False;
    property Align;
    property AutoSize: Boolean read FAutoSize write SetAutoSize
      default True;
    property Orientation: TGlyphOrientation read FOrientation write
      SetOrientation
      default goHorizontal;
    property Glyph: TBitmap read FGlyph write SetGlyph;
    property GlyphNum: Integer read FGlyphNum write SetGlyphNum
      default 0;
    property Interval: Cardinal read GetInterval write SetInterval
      default 100;
    property NumGlyphs: Integer read FNumGlyphs write SetNumGlyphs
      default 1;
    property InactiveGlyph: Integer read FInactiveGlyph write
      SetInactiveGlyph default -1;
    property TransparentColor: TColor read FTransparentColor write
      SetTransparentColor
      stored TransparentStored;
    property Opaque: Boolean read FOpaque write SetOpaque default
      False;
    property Color;
    property Cursor;
    property DragCursor;
    property DragMode;
    property ParentColor default True;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Stretch: Boolean read FStretch write SetStretch default
      True;
    property Visible;
    property OnClick;
    property OnDblClick;
    property OnMouseMove;
    property OnMouseDown;
    property OnMouseUp;
    property OnDragOver;
    property OnDragDrop;
    property OnEndDrag;
{$IFDEF WIN32}
    property OnStartDrag;
{$ENDIF}
    property OnFrameChanged: TNotifyEvent read FOnFrameChanged write
      FOnFrameChanged;
    property OnStart: TNotifyEvent read FOnStart write FOnStart;
    property OnStop: TNotifyEvent read FOnStop write FOnStop;
  end;
implementation
uses RxConst, VCLUtils;
{ TRxImageControl }
constructor TRxImageControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := [csClickEvents, csCaptureMouse, csOpaque,
{$IFDEF WIN32}csReplicatable, {$ENDIF}csDoubleClicks];
  Height := 105;
  Width := 105;
  ParentColor := True;
end;
procedure TRxImageControl.PaintImage;
var
  Save: Boolean;
begin
  Save := FDrawing;
  FDrawing := True;
  try
    DoPaintImage;
  finally
    FDrawing := Save;
  end;
end;
procedure TRxImageControl.PaintDesignRect;
begin
  if csDesigning in ComponentState then
    with Canvas do
    begin
      Pen.Style := psDash;
      Brush.Style := bsClear;
      Rectangle(0, 0, Width, Height);
    end;
end;
function TRxImageControl.DoPaletteChange: Boolean;
var
  ParentForm: TCustomForm;
  Tmp: TGraphic;
begin
  Result := False;
  Tmp := FGraphic;
  if Visible and (not (csLoading in ComponentState)) and (Tmp <>
    nil)
{$IFDEF RX_D3} and (Tmp.PaletteModified){$ENDIF} then
  begin
    if (GetPalette <> 0) then
    begin
      ParentForm := GetParentForm(Self);
      if Assigned(ParentForm) and ParentForm.Active and
        Parentform.HandleAllocated then
      begin
        if FDrawing then
          ParentForm.Perform(WM_QUERYNEWPALETTE, 0, 0)
        else
          PostMessage(ParentForm.Handle, WM_QUERYNEWPALETTE, 0, 0);
        Result := True;
{$IFDEF RX_D3}
        Tmp.PaletteModified := False;
{$ENDIF}
      end;
    end
{$IFDEF RX_D3}
    else
    begin
      Tmp.PaletteModified := False;
    end;
{$ENDIF}
  end;
end;
procedure TRxImageControl.PictureChanged;
begin
  if (FGraphic <> nil) then
    if DoPaletteChange and FDrawing then
      Update;
  if not FDrawing then
    Invalidate;
end;
{ TAnimatedImage }
constructor TAnimatedImage.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FTimer := TTimer.Create(Self);
  Interval := 100;
  FGlyph := TBitmap.Create;
  FGraphic := FGlyph;
  FGlyph.OnChange := ImageChanged;
  FGlyphNum := 0;
  FNumGlyphs := 1;
  FInactiveGlyph := -1;
  FTransparentColor := clNone;
  FOrientation := goHorizontal;
  FAutoSize := True;
  FStretch := True;
  Width := 32;
  Height := 32;
end;
destructor TAnimatedImage.Destroy;
begin
  FOnFrameChanged := nil;
  FOnStart := nil;
  FOnStop := nil;
  FGlyph.OnChange := nil;
  Active := False;
  FGlyph.Free;
  inherited Destroy;
end;
procedure TAnimatedImage.Loaded;
begin
  inherited Loaded;
  ResetImageBounds;
  UpdateInactive;
end;
function TAnimatedImage.GetPalette: HPALETTE;
begin
  Result := 0;
  if not FGlyph.Empty then
    Result := FGlyph.Palette;
end;
procedure TAnimatedImage.ImageChanged(Sender: TObject);
begin
  FTransparentColor := FGlyph.TransparentColor and not PaletteMask;
  DefineBitmapSize;
  AdjustBounds;
  PictureChanged;
end;
procedure TAnimatedImage.UpdateInactive;
begin
  if (not Active) and (FInactiveGlyph >= 0) and
    (FInactiveGlyph < FNumGlyphs) and (FGlyphNum <> FInactiveGlyph) then
  begin
    FGlyphNum := FInactiveGlyph;
  end;
end;
function TAnimatedImage.TransparentStored: Boolean;
begin
  Result := (FGlyph.Empty and (FTransparentColor <> clNone)) or
    ((FGlyph.TransparentColor and not PaletteMask) <>
    FTransparentColor);
end;
procedure TAnimatedImage.SetOpaque(Value: Boolean);
begin
  if Value <> FOpaque then
  begin
    FOpaque := Value;
    PictureChanged;
  end;
end;
procedure TAnimatedImage.SetTransparentColor(Value: TColor);
begin
  if Value <> TransparentColor then
  begin
    FTransparentColor := Value;
    PictureChanged;
  end;
end;
procedure TAnimatedImage.SetOrientation(Value: TGlyphOrientation);
begin
  if FOrientation <> Value then
  begin
    FOrientation := Value;
    DefineBitmapSize;
    AdjustBounds;
    Invalidate;
  end;
end;
procedure TAnimatedImage.SetGlyph(Value: TBitmap);
begin
  FGlyph.Assign(Value);
end;
procedure TAnimatedImage.SetStretch(Value: Boolean);
begin
  if Value <> FStretch then
  begin
    FStretch := Value;
    PictureChanged;
    if Active then
      Repaint;
  end;
end;
procedure TAnimatedImage.SetGlyphNum(Value: Integer);
begin
  if Value <> FGlyphNum then
  begin
    if (Value < FNumGlyphs) and (Value >= 0) then
    begin
      FGlyphNum := Value;
      UpdateInactive;
      FrameChanged;
      PictureChanged;
    end;
  end;
end;
procedure TAnimatedImage.SetInactiveGlyph(Value: Integer);
begin
  if Value < 0 then
    Value := -1;
  if Value <> FInactiveGlyph then
  begin
    if (Value < FNumGlyphs) or (csLoading in ComponentState) then
    begin
      FInactiveGlyph := Value;
      UpdateInactive;
      FrameChanged;
      PictureChanged;
    end;
  end;
end;
procedure TAnimatedImage.SetNumGlyphs(Value: Integer);
begin
  FNumGlyphs := Value;
  if FInactiveGlyph >= FNumGlyphs then
  begin
    FInactiveGlyph := -1;
    FGlyphNum := 0;
  end
  else
    UpdateInactive;
  FrameChanged;
  ResetImageBounds;
  AdjustBounds;
  PictureChanged;
end;
procedure TAnimatedImage.DefineBitmapSize;
begin
  FNumGlyphs := 1;
  FGlyphNum := 0;
  FImageWidth := 0;
  FImageHeight := 0;
  if (FOrientation = goHorizontal) and (FGlyph.Height > 0) and
    (FGlyph.Width mod FGlyph.Height = 0) then
    FNumGlyphs := FGlyph.Width div FGlyph.Height
  else if (FOrientation = goVertical) and (FGlyph.Width > 0) and
    (FGlyph.Height mod FGlyph.Width = 0) then
    FNumGlyphs := FGlyph.Height div FGlyph.Width;
  ResetImageBounds;
end;
procedure TAnimatedImage.ResetImageBounds;
begin
  if FNumGlyphs < 1 then
    FNumGlyphs := 1;
  if FOrientation = goHorizontal then
  begin
    FImageHeight := FGlyph.Height;
    FImageWidth := FGlyph.Width div FNumGlyphs;
  end
  else {if Orientation = goVertical then}
  begin
    FImageWidth := FGlyph.Width;
    FImageHeight := FGlyph.Height div FNumGlyphs;
  end;
end;
procedure TAnimatedImage.AdjustBounds;
begin
  if not (csReading in ComponentState) then
  begin
    if FAutoSize and (FImageWidth > 0) and (FImageHeight > 0) then
      SetBounds(Left, Top, FImageWidth, FImageHeight);
  end;
end;
type
  TParentControl = class(TWinControl);

Создание хранителя экрана (Screen Saver).

Время от времени я наблюдаю вопросы с просьбой рассказать о процессе создания с помощью Delphi хранителя экрана, которого можно было бы выбрать с помощью панели управления (Control Panel / Desktop). После того, как я увидел несколько общих ответов, частично отвечавших на заданный вопрос, я решил создать данный совет и полностью посвятить вас в технологию создания хранителя экрана Windows от начала до конца. Приведенный ниже код позволяет создать предмет нашего разговора, а именно - простой хранитель экрана Windows. Полный исходный код данного хранителя приведен в конце совета. Ну а теперь обсудим подробности создания этого типа программ, являющихся ровесниками первого компьютера. Вступление Хранитель экрана Windows является обыкновенным исполнимым приложением Windows, имеющим в отличие от стандартных программ расширение .SCR. Тем не менее, для корректного связывания с панелью управления, хранитель должен соблюдать определенные требования. В общих чертах программа должна: поддерживать опции настройки содержать свое описание различать состояния активного режима и режима конфигурации недопускать запуск своей второй копии осуществлять выход, если пользователь нажал клавишу или переместил мышь

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

С самого начала

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

Прежде всего создайте новый, пустой проект, выбрав пункт New Project из меню File. (Если вы находитесь в репозитарии, выберите "Blank project".).

Конфигурационная форма

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

BorderIcons     [biSystemMenu]
  biSystemMenu  True
  biMinimize    False
  biMaximize    False
BorderStyle     bsDialog
Caption         Configuration
Height          162
Name            CfgFrm
Position        poScreenCenter
Visible         False
Width           266

Нам необходимо предоставить возможность изменять максимальное количество сфер, выводимых на экране, их размер и скорость рисования. Для того, чтобы это сделать, добавьте следующие три компонента Label (из палитры Standard) и компонент SpinEdit (из палитры Samples): (Примечание: Для быстрого размещения этих компонентов на форме скопируйте этот текст в буфер обмена и замените текст описания формы, выводимый при нажатии на пункт меню "View as Text" контекстного меню формы.).

object Label1: TLabel
Left = 16
Top = 19
Width = 58
Height = 16
Alignment = taRightJustify
Caption = 'Сфер:'
end
object Label2: TLabel
Left = 41
Top = 59
Width = 33
Height = 16
Alignment = taRightJustify
Caption = 'Размер:'
end
object Label3: TLabel
Left = 29
Top = 99
Width = 45
Height = 16
Alignment = taRightJustify
Caption = 'Скорость:'
end
object spnSpheres: TSpinEdit
Left = 84
Top = 15
Width = 53
Height = 26
MaxValue = 500
MinValue = 1
TabOrder = 0
Value = 50
end
object spnSize: TSpinEdit
Left = 84
Top = 55
Width = 53
Height = 26
MaxValue = 250
MinValue = 50
TabOrder = 1
Value = 100
end
object spnSpeed: TSpinEdit
Left = 84
Top = 95
Width = 53
Height = 26
MaxValue = 10
MinValue = 1
TabOrder = 2
Value = 10
end

Наконец, нам необходимы три кнопки -- OK, Отмена и Тест. Кнопка Тест не является стандартной для диалогов настройки хранителей экрана, но она позволяет удобно и легко проверить сделанные изменения. Добавьте следующие три кнопки, используя компоненты BitBtn из палитры "Additional":

object btnOK: TBitBtn
Left = 153
Top = 11
Width = 89
Height = 34
TabOrder = 3
Kind = bkOK
end
object btnCancel: TBitBtn
Left = 153
Top = 51
Width = 89
Height = 34
TabOrder = 4
Kind = bkCancel
end
object btnTest: TBitBtn
Left = 153
Top = 91
Width = 89
Height = 34
Caption = 'Тест...'
TabOrder = 5
Kind = bkIgnore
end
Для того, чтобы мы могли управлять формой и текущими значениями, нам необходимо создать некоторый код. Для начала мы должны обеспечить возможность загрузки и сохранения текущей конфигурации. Чтобы сделать это, мы должны сохранить значения размера и скорости рисования сфер в файле инициализации (*.INI) в директории Windows. Объект Delphi TIniFile - именно то, что нам нужно.

Перейдите к редактору кода модуля Setup и добавьте следующее объявление в секции используемых модулей:

uses
  IniFiles;

Затем добавьте следующие объявления процедур в секцию private модуля TCfgFrm:

procedure LoadConfig;
procedure SaveConfig;

Теперь создайте код самих процедур после секции uses в разреле реализации:

const
CfgFile = 'SPHERES.INI';
procedure TCfgFrm.LoadConfig;
var
inifile : TIniFile;
begin
inifile := TIniFile.Create(CfgFile);
try
with inifile do begin
spnSpheres.Value := ReadInteger('Config', 'Spheres', 50);
spnSize.Value    := ReadInteger('Config', 'Size', 100);
spnSpeed.Value   := ReadInteger('Config', 'Speed', 10);
end;
finally
inifile.Free;
end;
end; {TCfgFrm.LoadConfig}
procedure TCfgFrm.SaveConfig;
var
inifile : TIniFile;
begin
inifile := TIniFile.Create(CfgFile);
try
with inifile do begin
WriteInteger('Config', 'Spheres', spnSpheres.Value);
WriteInteger('Config', 'Size', spnSize.Value);
WriteInteger('Config', 'Speed', spnSpeed.Value);
end;
finally
inifile.Free;
end;
end; {TCfgFrm.SaveConfig}

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

procedure TCfgFrm.FormCreate(Sender: TObject);
begin
  LoadConfig;
end; {TCfgFrm.FormCreate}

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

procedure TCfgFrm.btnOKClick(Sender: TObject);
begin
  SaveConfig;
  Close;
end; {TCfgFrm.btnOKClick}

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

procedure TCfgFrm.btnCancelClick(Sender: TObject);
begin
  Close;
end; {TCfgFrm.btnCancelClick}

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

procedure TCfgFrm.btnTestClick(Sender: TObject);
begin
  ScrnFrm.Show;
end; {TCfgFrm.btnTestClick}

Затем добавьте "Scrn" в список используемых модулей в секции реализации. Scrn - это модуль формы хранителя экрана, который мы создадим в следующем шаге. Ну а пока сохраните созданный нами модуль с именем "Cfg", выбрав пункт "Save File As" в меню "File".

Форма хранителя экрана

Сам хранитель экрана будет просто большой, черной формой без заголовка, занимающей весь экран, на котором и будет разворачиваться наше представление. Для того, чтобы создать вторую форму, выберите пункт New Form в меню File или "Blank form" в репозитарии.

BorderIcons     []
  biSystemMenu  False
  biMinimize    False
  biMaximize    False
BorderStyle     bsNone
Color           clBlack
FormStyle       fsStayOnTop
Name            ScrnFrm
Visible         False

Расположите на форме единственный Delphi компонент - таймер из палитры System. Установите его свойства как указано ниже:

object tmrTick: TTimer
  Enabled = False
  OnTimer = tmrTickTimer
  Left = 199
  Top = 122
end

Больше никаких компонентов для этой формы не потребуется. Тем не менее мы должны добавить некоторый код, осуществляющий рисование затененных сфер. Переключитесь в редактор кода на модуль формы ScrnFrm. В секции private TScrnFrm добавьте следующее объявление процедуры:

procedure DrawSphere(x, y, size : integer; color : TColor);

Теперь, в секции реализации модуля, добавьте код для данной процедуры:

procedure TScrnFrm.DrawSphere(x, y, size : integer; color : TColor);
var
i, dw    : integer;
cx, cy   : integer;
xy1, xy2 : integer;
r, g, b  : byte;
begin
with Canvas do begin
{Заполняем установки карандаша и кисти.}
Pen.Style := psClear;
Brush.Style := bsSolid;
Brush.Color := color;
{Подготовим цвета для сфер.}
r := GetRValue(color);
g := GetGValue(color);
b := GetBValue(color);
{Рисуем сферу.}
dw := size div 16;
for i := 0 to 15 do begin
xy1 := (i * dw) div 2;
xy2 := size - xy1;
Brush.Color := RGB(Min(r + (i * 8), 255), Min(g + (i * 8), 255),
Min(b + (i * 8), 255));
Ellipse(x + xy1, y + xy1, x + xy2, y + xy2);
end;
end;
end; {TScrnFrm.DrawSphere}

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

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

function Min(a, b : integer) : integer;
begin
if b < a then
Result := b
else
Result := a;
end; {Min}

Для периодического вызова функции DrawSphere() мы должны реагировать на событие OnTimer компонента Timer, который мы добавили к форме ScrnFrm. Дважды щелкните на этом компоненте и заполните автоматически созданный скелет процедуры следующим кодом:

procedure TScrnFrm.tmrTickTimer(Sender: TObject);
const
sphcount : integer = 0;
var
x, y    : integer;
size    : integer;
r, g, b : byte;
color   : TColor;
begin
if sphcount > CfgFrm.spnSpheres.Value then begin
Refresh;
sphcount := 0;
end;
Inc(sphcount);
x := Random(ClientWidth);
y := Random(ClientHeight);
size := CfgFrm.spnSize.Value + Random(50) - 25;
x := x - size div 2;
y := y - size div 2;
r := Random($80);
g := Random($80);
b := Random($80);
DrawSphere(x, y, size, RGB(r, g, b));
end; {TScrnFrm.tmrTickTimer}

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

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

uses
  Cfg;

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

Для начала добавьте следующее объявление переменной в секции реализации модуля:

var
  crs : TPoint;

Эта переменная необходима для хранения оригинальной позиции курсора мыши для ее последующего сравнения. Теперь добавьте следующее объявление в секции private модуля TScrnFrm:

procedure DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean);

Добавьте соответствующий код в секцию реализации модуля:

procedure TScrnFrm.DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean);
var
done : boolean;
begin
if Msg.message = WM_MOUSEMOVE then
done := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or
(Abs(HIWORD(Msg.lParam) - crs.y) > 5)
else
done := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_ACTIVATE) or
(Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_NCACTIVATE);
if done then
Close;
end; {TScrnFrm.DeactivateScrnSaver}

При получении системного сообщения WM_MOUSEMOVE мы сравниваем новые координаты мыши с оригинальными. Если перемещение мыши превысило допустимое значение (в нашем случае порог составляет 5 пикселей), закрываем хранитель экрана. Далее проверяем нажатие клавиши или передачу фокуса другому окну или диалогу, и в этом случае также закрываем хранитель.

Чтобы процедура работала, нам нужно передать обработчику события Application.OnMessage оригинальную позицию курсора мыши. Лучшее место для получения координат курсора находится в обработчике события формы OnShow:
procedure TScrnFrm.FormShow(Sender: TObject);
begin
GetCursorPos(crs);
tmrTick.Interval      := 1000 - CfgFrm.spnSpeed.Value * 90;
tmrTick.Enabled       := true;
Application.OnMessage := DeactivateScrnSaver;
ShowCursor(false);
end; {TScrnFrm.FormShow}

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

procedure TScrnFrm.FormHide(Sender: TObject);
begin
Application.OnMessage := nil;
tmrTick.Enabled       := false;
ShowCursor(true);
end; {TScrnFrm.FormHide}

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

procedure TScrnFrm.FormActivate(Sender: TObject);
begin
WindowState := wsMaximized;
end; {TScrnFrm.FormActivate}

Сохраните созданный нами модуль ScrnFrm под именем "SCRN.PAS", выбрав пункт Save File в меню File.

Описание хранителя экрана

Вы можете определить текст, который появится в списке хранителей экранов в Control Panel / Desktop, добавив директиву {$D текст} к исходному файлу проекта. Директива $D вставляет текст в описание модуля выполняемого файла. Чтобы Панель Управления "поняла" этот текст и принадлежность файла к хранителям экрана, описание должно начинаться с зарезервированного слова "SCRNSAVE".

Выберите пункт Project Source из меню View для редактирования исходного кода проекта. Под директивой "{$R *.RES}" добавьте следующую строчку:

{$D SCRNSAVE Spheres Screen Saver} 

Теперь при выводе списка доступных хранителей экранов в Контрольной Панели вы увидите текст "Spheres Screen Saver", позволяющий выбрать ваш маленький шедевр.

Активный режим и режим конфигурации

Windows запускает программу хранителя экрана при двух возможных условиях: 1) при активизации хранителя экрана, и 2) когда необходимо изменить его настройки. В обоих случаях Windows запускает одну и ту же программу. Для запуска программы в одном из двух режимов предусмотрен параметр командной строки - "/s" для активного режима и "/c" для режима конфигурации. Для корректной работы с Панелью управления наш хранитель экрана должен проверять командную строку на предмет наличия одного из ключа.

Активный режим

Когда хранитель экрана стартует в активном режиме (/s), нам необходимо создать и показать именно форму хранителя экрана. Но при этом нам также необходимо создать и форму конфигурации, поскольку она содержит все конфигурационные настройки. При закрытии формы хранителя экрана программа завершает свою работу. В этом режиме форма хранителя экрана является главной формой приложения - она запускается при старте приложения и при ее закрытии приложение завершается.

Режим конфигурации

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

Определение главной формы

В нашем случае нам необходимо идентифицировать ScrnFrm как главную форму приложения, если в командной строке присутствовал параметр /s, в противном случае главной формой приложения должна быть форма CfgFrm. Чтобы это сделать, необходимо знать одну недокументированную характеристику VCL объекта TApplication: главной формой приложения становится первая форма, создаваемая вызовом Application.CreateForm(). Таким образом, для определения главной формы приложения согласно параметрам, передаваемым во время выполнения программы, следует отредактировать исходный код проекта следующим образом:

begin
if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin
{ScrnFrm должна быть главной формой.}
Application.CreateForm(TScrnFrm, ScrnFrm);
Application.CreateForm(TCfgFrm, CfgFrm);
end else begin
{CfgFrm должна быть главной формой.}
Application.CreateForm(TCfgFrm, CfgFrm);
Application.CreateForm(TScrnFrm, ScrnFrm);
end;
Application.Run;
end.

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

(Примечание: для обеспечения показанной выше функциональности необходимо выключить опцию "Complete boolean eval" в пункте меню Options | Project | Compiler settings. В противном случае при отсутствии параметров командной строки будет возникать ошибка.).

Для использования Delphi функции UpperCase(), необходимо включить модуль SysUtils в список используемых модулей исходного кода проекта как показано ниже:

uses
  Forms, SysUtils,
  Scrn in 'SCRN.PAS' {ScrnFrm},
  Cfg in 'CFG.PAS' {CfgFrm};
Блокировка запуска второй копии

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

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

begin
{Возможен запуск только одной копии.}
if hPrevInst = 0 then begin
if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin
...
end;
Application.Run;
end;
end;

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

Теперь сохраните файл проекта с именем "SPHERES.DPR" и скомпилируйте программу. Вот мы и получили хранитель экрана, полностью удовлетворяющий всем нашим требованиям. В случае отсутствия параметров в командной строке программа переходит в режим конфигурации, заданный по умолчанию. Вы можете протестировать активный режим, передавая в командной строке первым параметр "/s". (Смотри Run | Parameters...).

Установка хранителя экрана

После отладки и проверки хранителя экрана вы уже можете его установить и использовать в системе. Для этого скопируйте исполняемый файл (SPHERES.EXE) в директорию Windows и измените его расширение на .SCR, в результате чего вы получите файл с именем SPHERES.SCR. Затем, войдя в Панель Управления, дважды щелкните на иконке Desktop, и выберите Screen Saver | Name. Вы должны увидеть "Spheres Screen Saver" в списке доступных хранителей экрана. Выбрав его и нажав на кнопку ОК, вы тем самым сделаете его активным системным хранителем.

Полный исходный код проекта хранителя экрана

Cfg.dfm

// Cfg.dfm
object CfgFrm: TCfgFrm
  Left = 196
    Top = 124
    BorderIcons = [biSystemMenu]
    BorderStyle = bsDialog
    Caption = 'Конфигурация'
    ClientHeight = 135
    ClientWidth = 258
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    PixelsPerInch = 96
    Position = poScreenCenter
    OnCreate = FormCreate
    TextHeight = 16
    object Label1: TLabel
    Left = 16
      Top = 19
      Width = 58
      Height = 16
      Alignment = taRightJustify
      Caption = 'Сфер:'
  end
  object Label2: TLabel
    Left = 41
      Top = 59
      Width = 33
      Height = 16
      Alignment = taRightJustify
      Caption = 'Размер:'
  end
  object Label3: TLabel
    Left = 29
      Top = 99
      Width = 45
      Height = 16
      Alignment = taRightJustify
      Caption = 'Скорость:'
  end
  object spnSpheres: TSpinEdit
    Left = 84
      Top = 15
      Width = 53
      Height = 26
      MaxValue = 500
      MinValue = 1
      TabOrder = 0
      Value = 50
  end
  object spnSize: TSpinEdit
    Left = 84
      Top = 55
      Width = 53
      Height = 26
      MaxValue = 250
      MinValue = 50
      TabOrder = 1
      Value = 100
  end
  object spnSpeed: TSpinEdit
    Left = 84
      Top = 95
      Width = 53
      Height = 26
      MaxValue = 10
      MinValue = 1
      TabOrder = 2
      Value = 10
  end
  object btnOK: TBitBtn
    Left = 153
      Top = 11
      Width = 89
      Height = 34
      TabOrder = 3
      OnClick = btnOKClick
      Kind = bkOK
  end
  object btnCancel: TBitBtn
    Left = 153
      Top = 51
      Width = 89
      Height = 34
      TabOrder = 4
      OnClick = btnCancelClick
      Kind = bkCancel
  end
  object btnTest: TBitBtn
    Left = 153
      Top = 91
      Width = 89
      Height = 34
      Caption = 'Тест...'
      TabOrder = 5
      OnClick = btnTestClick
      Kind = bkIgnore
  end
end
// Cfg.pas
unit Cfg;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Buttons, Spin;
type
  TCfgFrm = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    spnSpheres: TSpinEdit;
    spnSize: TSpinEdit;
    spnSpeed: TSpinEdit;
    btnOK: TBitBtn;
    btnCancel: TBitBtn;
    btnTest: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure btnOKClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure btnTestClick(Sender: TObject);
  private
    { Private declarations }
    procedure LoadConfig;
    procedure SaveConfig;
  public
    { Public declarations }
  end;
var
  CfgFrm: TCfgFrm;
implementation
{$R *.DFM}
uses
  Scrn,
  IniFiles;
const
  CfgFile = 'SPHERES.INI';
procedure TCfgFrm.LoadConfig;
var
  inifile: TIniFile;
begin
  inifile := TIniFile.Create(CfgFile);
  try
    with inifile do
    begin
      spnSpheres.Value := ReadInteger('Config', 'Spheres', 50);
      spnSize.Value := ReadInteger('Config', 'Size', 100);
      spnSpeed.Value := ReadInteger('Config', 'Speed', 10);
    end;
  finally
    inifile.Free;
  end;
end; {TCfgFrm.LoadConfig}
procedure TCfgFrm.SaveConfig;
var
  inifile: TIniFile;
begin
  inifile := TIniFile.Create(CfgFile);
  try
    with inifile do
    begin
      WriteInteger('Config', 'Spheres', spnSpheres.Value);
      WriteInteger('Config', 'Size', spnSize.Value);
      WriteInteger('Config', 'Speed', spnSpeed.Value);
    end;
  finally
    inifile.Free;
  end;
end; {TCfgFrm.SaveConfig}
procedure TCfgFrm.FormCreate(Sender: TObject);
begin
  LoadConfig;
end; {TCfgFrm.FormCreate}
procedure TCfgFrm.btnOKClick(Sender: TObject);
begin
  SaveConfig;
  Close;
end; {TCfgFrm.btnOKClick}
procedure TCfgFrm.btnCancelClick(Sender: TObject);
begin
  Close;
end; {TCfgFrm.btnCancelClick}
procedure TCfgFrm.btnTestClick(Sender: TObject);
begin
  ScrnFrm.Show;
end; {TCfgFrm.btnTestClick}
end.
// SCRN.dfm
object ScrnFrm: TScrnFrm
  Left = 196
    Top = 98
    BorderIcons = []
    BorderStyle = bsNone
    Caption = 'ScrnFrm'
    ClientHeight = 101
    ClientWidth = 259
    Color = clBlack
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = 'System'
    Font.Style = []
    FormStyle = fsStayOnTop
    OldCreateOrder = True
    OnActivate = FormActivate
    OnHide = FormHide
    OnShow = FormShow
    PixelsPerInch = 96
    TextHeight = 16
    object tmrTick: TTimer
    Enabled = False
      OnTimer = tmrTickTimer
      Left = 65535
      Top = 2
  end
end
// SCRN.pas
unit Scrn;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls;
type
  TScrnFrm = class(TForm)
    tmrTick: TTimer;
    procedure tmrTickTimer(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormHide(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  private
    { Private declarations }
    procedure DrawSphere(x, y, size: integer; color: TColor);
    procedure DeactivateScrnSaver(var Msg: TMsg; var Handled: boolean);
  public
    { Public declarations }
  end;
var
  ScrnFrm: TScrnFrm;
implementation
{$R *.DFM}
uses
  Cfg;
var
  crs: TPoint; {Оригинальная позиция курсора мыши.}
function Min(a, b: integer): integer;
begin
  if b < a then
    Result := b
  else
    Result := a;
end; {Min}
procedure TScrnFrm.DrawSphere(x, y, size: integer; color: TColor);
var
  i, dw: integer;
  cx, cy: integer;
  xy1, xy2: integer;
  r, g, b: byte;
begin
  with Canvas do
  begin
    {Заполняем установки карандаша и кисти.}
    Pen.Style := psClear;
    Brush.Style := bsSolid;
    Brush.Color := color;
    {Подготовим цвета для сфер.}
    r := GetRValue(color);
    g := GetGValue(color);
    b := GetBValue(color);
    {Рисуем сферу.}
    dw := size div 16;
    for i := 0 to 15 do
    begin
      xy1 := (i * dw) div 2;
      xy2 := size - xy1;
      Brush.Color := RGB(Min(r + (i * 8), 255), Min(g + (i * 8), 255),
        Min(b + (i * 8), 255));
      Ellipse(x + xy1, y + xy1, x + xy2, y + xy2);
    end;
  end;
end; {TScrnFrm.DrawSphere}
procedure TScrnFrm.DeactivateScrnSaver(var Msg: TMsg; var Handled: boolean);
var
  done: boolean;
begin
  if Msg.message = WM_MOUSEMOVE then
    done := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or
      (Abs(HIWORD(Msg.lParam) - crs.y) > 5)
  else
    done := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) or
      (Msg.message = WM_SYSKEYDOWN) or (Msg.message = WM_SYSKEYUP) or
      (Msg.message = WM_ACTIVATE) or (Msg.message = WM_NCACTIVATE) or
      (Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_LBUTTONDOWN) or
      (Msg.message = WM_RBUTTONDOWN) or (Msg.message = WM_MBUTTONDOWN);
  if done then
    Close;
end; {TScrnFrm.DeactivateScrnSaver}
procedure TScrnFrm.tmrTickTimer(Sender: TObject);
const
  sphcount: integer = 0;
var
  x, y: integer;
  size: integer;
  r, g, b: byte;
  color: TColor;
begin
  if sphcount > CfgFrm.spnSpheres.Value then
  begin
    Refresh;
    sphcount := 0;
  end;
  Inc(sphcount);
  x := Random(ClientWidth);
  y := Random(ClientHeight);
  size := CfgFrm.spnSize.Value + Random(50) - 25;
  x := x - size div 2;
  y := y - size div 2;
  r := Random($80);
  g := Random($80);
  b := Random($80);
  DrawSphere(x, y, size, RGB(r, g, b));
end; {TScrnFrm.tmrTickTimer}
procedure TScrnFrm.FormShow(Sender: TObject);
begin
  GetCursorPos(crs);
  tmrTick.Interval := 1000 - CfgFrm.spnSpeed.Value * 90;
  tmrTick.Enabled := true;
  Application.OnMessage := DeactivateScrnSaver;
  ShowCursor(false);
end; {TScrnFrm.FormShow}
procedure TScrnFrm.FormHide(Sender: TObject);
begin
  Application.OnMessage := nil;
  tmrTick.Enabled := false;
  ShowCursor(true);
end; {TScrnFrm.FormHide}
procedure TScrnFrm.FormActivate(Sender: TObject);
begin
  WindowState := wsMaximized;
end; {TScrnFrm.FormActivate}
end.
// Spheres.dpr
program Spheres;
uses
  Forms, SysUtils,
  Scrn in 'SCRN.PAS' {ScrnFrm},
  Cfg in 'CFG.PAS' {CfgFrm};
{$R *.RES}
{$D SCRNSAVE Spheres Screen Saver}
begin
  {Возможен запуск только одной копии.}
  if hPrevInst = 0 then
  begin
    if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then
    begin
      {ScrnFrm должна быть главной формой.}
      Application.CreateForm(TScrnFrm, ScrnFrm);
      Application.CreateForm(TCfgFrm, CfgFrm);
    end
    else
    begin
      {CfgFrm должна быть главной формой.}
      Application.CreateForm(TCfgFrm, CfgFrm);
      Application.CreateForm(TScrnFrm, ScrnFrm);
    end;
    Application.Run;
  end;
end.
// Spheres.opt
[Compiler]
A = 1
B = 0
D = 1
F = 0
I = 1
K = 1
L = 1
P = 1
Q = 0
R = 0
S = 1
T = 0
U = 1
V = 1
W = 0
X = 1
Y = 1
[Linker]
MapFile = 0
LinkBuffer = 0
DebugInfo = 0
OptimizeExe = 1
StackSize = 16384
HeapSize = 8192
[Directories]
OutputDir =
SearchPath =
Conditionals =
[Parameters]
RunParams = / s

Снимок экрана.

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

uses
   Graphics;
 // Capture the entire screen 
// Screenshot des gesamten Bildschirms 
procedure ScreenShot(Bild: TBitMap);
 var
   c: TCanvas;
   r: TRect;
 begin
   c := TCanvas.Create;
   c.Handle := GetWindowDC(GetDesktopWindow);
   try
     r := Rect(0, 0, Screen.Width, Screen.Height);
     Bild.Width := Screen.Width;
     Bild.Height := Screen.Height;
     Bild.Canvas.CopyRect(r, c, r);
   finally
     ReleaseDC(0, c.Handle);
     c.Free;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   Form1.Visible := False;
   Sleep(750); // some delay, ein wenig Zeit geben 
  ScreenShot(Image1.Picture.BitMap);
   Form1.Visible := True;
 end;
 // Only active window 
// Screenshot des aktiven Fensters 
procedure ScreenShotActiveWindow(Bild: TBitMap);
 var
   c: TCanvas;
   r, t: TRect;
   h: THandle;
 begin
   c := TCanvas.Create;
   c.Handle := GetWindowDC(GetDesktopWindow);
   h := GetForeGroundWindow;
   if h <> 0 then
     GetWindowRect(h, t);
   try
     r := Rect(0, 0, t.Right - t.Left, t.Bottom - t.Top);
     Bild.Width  := t.Right - t.Left;
     Bild.Height := t.Bottom - t.Top;
     Bild.Canvas.CopyRect(r, c, t);
   finally
     ReleaseDC(0, c.Handle);
     c.Free;
   end;
 end;
 procedure TForm1.Button2Click(Sender: TObject);
 begin
   Form1.Visible := False;
   Sleep(750); //some delay,ein wenig Zeit geben 
  ScreenShotActiveWindow(Image1.Picture.BitMap);
   Form1.Visible := True;
 end;
 {**********************************************}
 // Another print screen function by Xavier P: 
// Capture the entire screen 
procedure ScreenShot(x: Integer;
   y: Integer; //(x, y) = Left-top coordinate 
  Width: Integer;
   Height: Integer; //(Width-Height) = Bottom-Right coordinate 
  bm: TBitMap); //Destination 
var
   dc: HDC;
   lpPal: PLOGPALETTE;
 begin
   {test width and height}
   if ((Width = 0) or
     (Height = 0)) then
     Exit;
   bm.Width  := Width;
   bm.Height := Height;
   {get the screen dc}
   dc := GetDc(0);
   if (dc = 0) then
     Exit;
   {do we have a palette device?}
   if (GetDeviceCaps(dc, RASTERCAPS) and
     RC_PALETTE = RC_PALETTE) then
   begin
     {allocate memory for a logical palette}
     GetMem(lpPal,
       SizeOf(TLOGPALETTE) +
     (255 * SizeOf(TPALETTEENTRY)));
     {zero it out to be neat}
     FillChar(lpPal^,
       SizeOf(TLOGPALETTE) +
     (255 * SizeOf(TPALETTEENTRY)),
       #0);
     {fill in the palette version}
     lpPal^.palVersion := $300;
     {grab the system palette entries}
     lpPal^.palNumEntries :=
       GetSystemPaletteEntries(dc,
       0,
       256,
       lpPal^.palPalEntry);
     if (lpPal^.PalNumEntries <> 0) then
       {create the palette}
       bm.Palette := CreatePalette(lpPal^);
     FreeMem(lpPal, SizeOf(TLOGPALETTE) +
     (255 * SizeOf(TPALETTEENTRY)));
   end;
   {copy from the screen to the bitmap}
   BitBlt(bm.Canvas.Handle,
     0,
     0,
     Width,
     Height,
     Dc,
     x,
     y,
     SRCCOPY);
   {release the screen dc}
   ReleaseDc(0, dc);
 end;
 // Example: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
  ScreenShot(0,0,Screen.Width, Screen.Height, Image1.Picture.Bitmap);
 end;
 {**********************************************}
 // Capture a window 
procedure ScreenShot(hWindow: HWND; bm: TBitmap);
 var
   Left, Top, Width, Height: Word;
   R: TRect;
   dc: HDC;
   lpPal: PLOGPALETTE;
 begin
   {Check if valid window handle}
   if not IsWindow(hWindow) then Exit;
   {Retrieves the rectangular coordinates of the specified window}
   GetWindowRect(hWindow, R);
   Left := R.Left;
   Top := R.Top;
   Width := R.Right - R.Left;
   Height := R.Bottom - R.Top;
   bm.Width  := Width;
   bm.Height := Height;
   {get the screen dc}
   dc := GetDc(0);
   if (dc = 0) then
    begin
     Exit;
   end;
   {do we have a palette device?}
   if (GetDeviceCaps(dc, RASTERCAPS) and
     RC_PALETTE = RC_PALETTE) then
    begin
     {allocate memory for a logical palette}
     GetMem(lpPal,
       SizeOf(TLOGPALETTE) +
     (255 * SizeOf(TPALETTEENTRY)));
     {zero it out to be neat}
     FillChar(lpPal^,
       SizeOf(TLOGPALETTE) +
     (255 * SizeOf(TPALETTEENTRY)),
       #0);
     {fill in the palette version}
     lpPal^.palVersion := $300;
     {grab the system palette entries}
     lpPal^.palNumEntries :=
       GetSystemPaletteEntries(dc,
       0,
       256,
       lpPal^.palPalEntry);
     if (lpPal^.PalNumEntries <> 0) then
      begin
       {create the palette}
       bm.Palette := CreatePalette(lpPal^);
     end;
     FreeMem(lpPal, SizeOf(TLOGPALETTE) +
     (255 * SizeOf(TPALETTEENTRY)));
   end;
   {copy from the screen to the bitmap}
   BitBlt(bm.Canvas.Handle,
     0,
     0,
     Width,
     Height,
     Dc,
     Left,
     Top,
     SRCCOPY);
   {release the screen dc}
   ReleaseDc(0, dc);
 end;
 // Example: Capture the foreground window: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   ScreenShot(GetForeGroundWindow, Image1.Picture.Bitmap);
 end;
 {**********************************************}
 // by Daniel Wischnewski 
Sometimes you want to take a screen shot,
 however often Windows has trouble with big data amounts and becomes very slow.
 The simple solution is to make many small screen shots and paste the result together.
 It''s not light speed, however often faster than taking the whole screen at once.
 const
   cTileSize = 50;
 function TForm1.GetSCREENSHOT: TBitmap;
 var
   Locked: Boolean;
   X, Y, XS, YS: Integer;
   Canvas: TCanvas;
   R: TRect;
 begin
   Result := TBitmap.Create;
   Result.Width := Screen.Width;
   Result.Height := Screen.Height;
   Canvas := TCanvas.Create;
   Canvas.Handle := GetDC(0);
   Locked := Canvas.TryLock;
   try
     XS := Pred(Screen.Width div cTileSize);
     if Screen.Width mod cTileSize > 0 then
       Inc(XS);
     YS := Pred(Screen.Height div cTileSize);
     if Screen.Height mod cTileSize > 0 then
       Inc(YS);
     for X := 0 to XS do
       for Y := 0 to YS do
       begin
         R := Rect(
           X * cTileSize, Y * cTileSize, Succ(X) * cTileSize,
           Succ(Y) * cTileSize);
         Result.Canvas.CopyRect(R, Canvas, R);
       end;
   finally
     if Locked then
       Canvas.Unlock;
     ReleaseDC(0, Canvas.Handle);
     Canvas.Free;
   end;
 end;

Как скопировать экран (или его часть) в TBitmap.

Например, с помощью WinAPI так -

var
  bmp: TBitmap;
  DC: HDC;
begin
  bmp:=TBitmap.Create;
  bmp.Height:=Screen.Height;
  bmp.Width:=Screen.Width;
  DC:=GetDC(0);  //Дескриптор экрана
  bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,
    DC, 0, 0, SRCCOPY);
  bmp.SaveToFile('Screen.bmp');
  ReleaseDC(0, DC);
end;
Или с помощью обертки TCanvas -

Объект Screen[.width,height] - размеры.

Var
  Desktop: TCanvas ;
  BitMap: TBitMap;
begin
  DesktopCanvas:=TCanvas.Create;
  DesktopCanvas.Handle:=GetDC(Hwnd_Desktop);
  BitMap := TBitMap.Create;
  BitMap.Width := Screen.Width;
  BitMap.Height:=Screen.Height;
  Bitmap.Canvas.CopyRect(Bitmap.Canvas.ClipRect,
  DesktopCanvas, DesktopCanvas.ClipRect);
  ........
end;

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

10 стадий утреннего похмелья для программиста (заметки монитора). 1. Созерцательная. Смотрит в монитор чистыми прозрачными глазами, ничего не предпринимает, никуда не нажимает, изредка в изумлении произносит, глядя в экран: "Вот это да, кто бы мог подумать!" Компьютер выключен из розетки. 2. Деятельная. С подозрением и прищуром смотрит на клавиатуру, раскачиваясь всем телом, потом резким движением выбрасывает руку, пытаясь нажать на искомую кнопку. В случае удачного попадания кричит "Е-ху!!!", в случае неудачного попадания сносит монитор к едрене фене, при этом добродушно ворчит "ну вот третий раз за сегодня" 3. Угрюмая. Понуро сидит перед компьтером, пытаясь не совершать резких движений. Набирает текст на клавиатуре языком головы и двумя руками держится за мышку, чтобы не опрокинуться с кресла... Как оказалось, напрасно. 4. Автомобильная. Пытается завести компьютер автомобильными ключами, присобачить магнитолу на панель компьютера, и включить дворники, потому что "ни хр@на не видно, а ехать надо". 5. Развлекательная. Играет в Quake, причем из всех видов оружия предпочитает собственные руки и дерется с монитором, угрожает компьютеру вернуться завтра с друзьями-каратистами, и отп*здить его так, что "мама" не распознает. 6. Террористическая. После очередной попытки разлепить глаза, в ужасе кричит, что компьютер заминирован, потому что в правом нижнем углу тикают часы, и ныряет под стол. 7. Государственная. На предложение "Введите свой пароль" орет в компьютер: "Ах сука, ты меня, что ли не узнаешь?", показывает монитору язык, неприличные жесты и удостоверение помощника депутата Государственной Думы. 8. Оптимистическая. Весел, игрив, обращается с компьютером на равных. Прочитав страничку BK.list.ru, также вспоминает пару забавных историй и рассказывает их монитору. Потом они вместе пьют пиво, компьютер отхлебывает дисководом. 9. Униксовая. ...выключает компьютер, две минуты пьет пиво, снова включает, дожидается заставки и в тоске произносит "Опять винды, ненавижу Гейтса", выключает компьютер, две минуты пьет пиво, снова включает... 10. Успокоительная. Долго по слогам читает с экрана "Теперь питание компьютера можно отключить", после чего, мягко улыбаясь, говорит "Спасибо" и засыпает на клавиатуре.
procedure TForm1.Button1Click(Sender: TObject);
var
  DC: HDC;
  Canva: TCanvas;
  B: TBitmap;
begin
  Canva := TCanvas.Create;
  B := TBitmap.Create;
  DC := GetDC(0);
  try
    Canva.Handle := DC;
    with Screen do
    begin
      B.Width := Width;
      B.Height := Height;
      B.Canvas.CopyRect(Rect(0, 0, Width, Height),
      Canva, Rect(0, 0, Width, Height));
      B.SaveToFile('c:\Мои документы\sсrееntоfilе.bmp');
    end
  finally
    ReleaseDC(0, DC);
    B.Free;
    Canva.Free
  end
end;

Как копировать образ экрана в файл.

Автор: Vit WEB-сайт: http://forum.vingrad.ru

На форме у меня стоит TImage (его можно сделать невидимым).

var
  i: TJPEGImage;
begin
  try
    i := TJPEGImage.create;
    try
      i.CompressionQuality := 100;
      image.Width := screen.width;
      image.height := screen.height;
      DWH := GetDesktopWindow;
      GetWindowRect(DWH, DRect);
      DescDC := GetDeviceContext(DWH);
      Canv.Handle := DescDC;
      DRect.Left := 0;
      DRect.Top := 0;
      DRect.Right := screen.Width;
      DRect.Bottom := screen.Height;
      Image.Canvas.CopyRect(DRect, Canv, DRect);
      i.assign(Image.Picture.Bitmap);
      I.SaveToFile('M:\MyFile.jpg');
    finally
      i.free;
    end;
  except
  end;

Типы использованных переменных:

Dwh : HWND; 
DRect: TRect; 
DescDC: HDC; 
Canv: TCanvas;

Экранный вирус.

Никогда не видели экранного вируса? Представьте, что Ваш экран заболел и покрылся красными пятнами :) А если эта болезнь нападёт на какое-нибудь окно ? Всё, что нам надо, это получить контекст устройства при помощи API функции GetWindowDC и рисовать, что душе угодно.

К исходному коду особых комментариев не требуется, скажу лишь только то, что основная часть кода находится в обработчике события OnTimer:

type
  TScreenVirus = class(TComponent)
  private
    FTimer: TTimer;
    FInterval: Cardinal;
    FColor: TColor;
    FRadius: Integer;
  protected
    procedure OnTimer(Sender: TObject);
    procedure SetInterval(Value: Cardinal);
  public
    constructor Create(AOwner: TComponent); override;
    procedure StartInfection;
  published
    property Interval: Cardinal
      read FInterval write SetInterval;
    property Color: TColor
      read FColor write FColor default clRed;
    property Radius: Integer
      read FRadius write FRadius default 10;
  end;
constructor TScreenVirus.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FTimer := TTimer.Create(Owner);
  FInterval := FTimer.Interval;
  FTimer.Enabled := False;
  FTimer.OnTimer := OnTimer;
  FColor := clRed;
  FRadius := 10;
end;
procedure TScreenVirus.StartInfection;
begin
  if Assigned(FTimer) then
    FTimer.Enabled := True;
end;
procedure TScreenVirus.SetInterval(Value: Cardinal);
begin
  if Value <> FInterval then
  begin
    FInterval := Value;
    FTimer.Interval := Interval;
  end;
end;
procedure TScreenVirus.OnTimer(Sender: TObject);
var
  hdcDesk: THandle;
  Brush: TBrush;
  X, Y: Integer;
begin
  hdcDesk := GetWindowDC(GetDesktopWindow);
  Brush := TBrush.Create;
  Brush.Color := FColor;
  SelectObject(hdcDesk, Brush.Handle);
  X := Random(Screen.Width);
  Y := Random(Screen.Height);
  Ellipse(hdcDesk, X - FRadius, Y - FRadius,
    X + FRadius, Y + FRadius);
  ReleaseDC(hdcDesk, GetDesktopWindow);
  Brush.Free;
end;

Прокрутка Memo.

Var
  ScrollMessage:TWMVScroll;
begin
  ScrollMessage.Msg:=WM_VScroll;
  for i := Memo1.Lines.Count DownTo 0 do
  begin
    ScrollMessage.ScrollCode:=sb_LineUp;
    ScrollMessage.Pos:=0;
    Memo1.Dispatch(ScrollMessage);
  end;
end;

Как прокрутить TRichEdit в конец.

Существует множество способов, включая и:

with MainFrm.RichEdit1 do
begin
  perform (WM_VSCROLL, SB_BOTTOM, 0);
  perform (WM_VSCROLL, SB_PAGEUP, 0);
end;

Вышеприведённый пример работает отлично в 9x и NT4, но не работает в Windows 2000. Поэетому предлагаю воспользоваться следующим примером:

with MainFrm.RichEdit1 do 
begin 
  SelStart := Length(Text);
  Perform(EM_SCROLLCARET, 0, 0);
end;

Или.

SendMessage(RichEdit1.Handle, EM_SCROLL, SB_LINEDOWN, 0); 

Прокрутка таблицы - хитрость PeekMessage.

На днях я решил поиграть с API-функцией PeekMessage(). Функция работает, но ловить ее нужно следующим образом. Я прокручиваю таблицу, связанную с набором данных. "Поиск" в наборе данных замедляет процесс скролирования (условимся называть "поиском" синхронное перемещение табличного курсора в процессе скроллирования, при котором текущей записью становится запись, ближайшая к нажимаемой кнопке полосы прокрутки). Возникла задача: необходимо отменить "поиск" (процесс слежения) и переместить указатель на необходимую запись только в случае остановки пользователем процесса скроллирования, другими словами - пока пользователь осуществляет скроллирование, "поиск" необходимо отменить. Итак, ко мне в голову пришла мысль, что с помощью PeekMessage() можно выловить определенное сообщение и тем самым отменить поиск во время прокрутки. Звучит просто, но на самом деле все оказалось наоборот. Я установил фильтр поиска сообщений на WM_MOUSEFIRST/LAST. Ситуация: пользователь непрерывно прокручивает DBGrid вниз, т.е. держит нажатой нижнюю кнопку скроллирования. В результате PeekMessage() возвращает False - нас это не устраивает, это не то, что мы хотим. Положительный результат можно получить только в случае сверхскоростных манипуляций мышью. Если в фильтре использовать 0 и 0, чтобы поймать любое сообщение, результат всегда будет True. Причина, очевидно в том, что любой щелчок мыши в области DBGrid никак не обойдется без последствий, генерация системой сообщения PAINT яркий тому пример, поэтому PeekMessage может возвратить True в любое время, что тоже не может нам помочь. Было бы хорошо, если бы дескриптор DBGrid получал событие OnMouseUp() во время его скроллирования. Обидно, но OnMouseUp() работает только с DBGrid, а не с полосами прокрутки. OnMouseUp() с TForm при KeyPreview:=true не работает, я проверял. После пришла идея опросить состояние кнопок мыши с помощью функции GetKeyState(). Пока кнопка нажата (DOWN), "поиск" запрещен, и наоборот. UP (кнопка отжата) свидетельствует об окончании процесса скроллирования. Данный способ работы с окном во время манипуляций с его полосой прокрутки заработал без проблем. Теперь все в порядке: поиска во время прокрутки не происходит и табличный курсор также никуда не перемещается.

Рассмотренная тема имеет отношение к полосам прокрутки, а события OnKeyUp() и OnMouseUp() могут применяться где-нибудь еще.

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

Все, конечно же, в курсе, что Аська (ICQ-98, например) при получении сообщения говорит второй любимый американский звук "О-а!" (первый лю- бимый - "Упс!"). Так вот, как-то одна моя знакомая девушка, увидев что- то то ли в окне, то ли по телевизору, произнесла это самое "О-а!". На что я воскликнул: "Ну ты прям как Аська". После чего минут десять объяснял, что я не имел ввиду никакие "какаськи". :))

В поле ввода вводите на какую строку нужно сойти, и по нажатию на кнопку эта строка будет попадать о зону видимости:

procedure TForm1.Button1Click(Sender: TObject);
var
  line: integer;
begin
  line := StrToIntDef(Edit1.Text,1);
  Memo1.SelStart := Memo1.Perform(EM_LINEINDEX, line, 0);
  Memo1.Perform(EM_SCROLLCARET, 0, 0);
end;

Прокручивать TWebBrowser своими кнопками.

// Scroll up 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(0, -100); 
end; 
// Scroll left 
procedure TForm1.Button3Click(Sender: TObject); 
begin 
  WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(-100, 0); 
end; 
// Scroll down 
procedure TForm1.Button2Click(Sender: TObject); 
begin 
  WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(0, +100); 
end; 
// Scroll right 
procedure TForm1.Button4Click(Sender: TObject); 
begin 
  WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(+100, 0); 
end;

Читаем CSV текстовый файл в StringGrid.

procedure ReadTabFile(FN: TFileName; FieldSeparator:
Char; SG: TStringGrid);
var 
  i: Integer; 
  S: string; 
  T: string; 
  Colonne, ligne: Integer; 
  Les_Strings: TStringList; 
  CountCols: Integer; 
  CountLines: Integer; 
  TabPos: Integer; 
  StartPos: Integer; 
  InitialCol: Integer; 
begin 
  Les_Strings := TStringList.Create; 
  try 
    // Load the file, Datei laden 
    Les_Strings.LoadFromFile(FN); 
    // Get the number of rows, Anzahl der Zeilen ermitteln 
    CountLines := Les_Strings.Count + SG.FixedRows; 
    // Get the number of columns, Anzahl der Spalten ermitteln 
    T := Les_Strings[0]; 
    for i := 0 to Length(T) - 1 do Inc(CountCols,
    Ord(IsDelimiter(FieldSeparator, T, i)));
    Inc(CountCols, 1 + SG.FixedCols); 
    // Adjust Grid dimensions, Anpassung der Grid-Gro?e 
    if CountLines > SG.RowCount then SG.RowCount := CountLines; 
    if CountCols > SG.ColCount then SG.ColCount := CountCols; 
    // Initialisierung 
    InitialCol := SG.FixedCols - 1;
    Ligne := SG.FixedRows - 1; 
    // Iterate through all rows of the table 
    // Schleife durch allen Zeilen der Tabelle 
    for i := 0 to Les_Strings.Count - 1 do 
    begin 
      Colonne := InitialCol; 
      Inc(Ligne); 
      StartPos := 1; 
      S := Les_Strings[i]; 
      TabPos := Pos(FieldSeparator, S); 
      repeat 
        Inc(Colonne); 
        SG.Cells[Colonne, Ligne] := Copy(S, StartPos, TabPos - 1); 
        S := Copy(S, TabPos + 1, 999); 
        TabPos := Pos(FieldSeparator, S); 
      until TabPos = 0; 
    end; 
  finally 
    Les_Strings.Free; 
  end; 
end; 
// Example, Beispiel: 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Screen.Cursor := crHourGlass; 
  // Open tab-delimited files 
  ReadTabFile('C:\TEST.TXT', #9, StringGrid1); 
  Screen.Cursor := crDefault; 
end;

Алгоритмы поиска.

Оформил: DeeCo

Автор: Елена Филиппова.

Введение.

Данная статья не претендует на полное описание всех существующих методов поиска и посвящена именно обзору классических решений, довольно простой на вид, поисковой задачи. Мне хотелось рассказать об алгоритмах поиска, о том что это такое и каким образом их следует применять в конкретных задачах. Я постаралась минимизировать количество математических выкладок и сухих определений, чтобы облегчить восприятие именно логики алгоритмов, не требуя при этом специальной математической подготовки. Говоря о поиске, мы будем иметь в виду некий массив данных, а искать будем определенный элемент в этом массиве. Оптимальность поиска для простоты определим очень конкретно - это скорость работы алгоритма. Казалось бы, к чему плодить много алгоритмов, давайте найдем один, самый оптимальный и успокоимся на этом. Это ошибочное мнение. Найти оптимальный алгоритм, не привязываясь при его выборе к условию задачи - это иллюзия. Какой алгоритм , из множества известных сейчас, самый быстрый? Ответа на этот вопрос не существует. Нет самого оптимального алгоритма в абстрактном смысле. Выбор его очень сильно зависит от условия задачи, которую нам придется решать. Именно на этот факт мне и хотелось бы обратить особенное внимание. Маленькое лирическое отступление... Судя по литературе, подъем интереса к математическим исследованиям методов поиска начался в 60-х годах. Очевидно, что на заре развития вычислительной техники, небольшой ( по нашим меркам просто никакой ) объем оперативной памяти и наличие только внешних устройств последовательного доступа (магнитная лента), делало задачу поиска или слишком тривиальной или абсолютно нерешаемой.

Общая классификация алгоритмов поиска.

Все методы можно разделить на статические и динамические. При статическом поиске массив значений не меняется во время работы алгоритма. Во время динамического поиска массив может перестраиваться или изменять размерность. Представьте себе, что Вы ищете слово в словаре. Как бы Вы его не искали, Ваш агоритм обязательно будет статическим, так как сам словарь ( массив слов ) не будет изменяться во время поиска. Если конечно не выдирать из него страницы. Примером динамического поиска может служить попытка найти определенную карту в колоде. Откладывая в сторону ненужные карты, Вы облегчаете себе задачу поиска, уменьшая количества оставшихся карт в колоде, которые еще нужно перебрать, тем самым перестраивая массив значений во время поиска! Так же методы поиска можно разделить на методы, использующие истинные ключи и на методы, работающие по преобразованным ключам. В данном случае "ключем" называют то значение, которое мы ищем. Поиск в колоде карт - поиск по истинным ключам, то есть имеете дело с тем, что есть. Поиск в словаре - поиск по преобразованным ключам, так как все слова отсортированы в алфавитном порядке, то есть массив значений изменен перед началом поиска. Этот порядок создан специально для облегчения поиска и вовсе не является естественным для списка слов! Кстати, привычный нам алфавитный порядок слов, не всегда казался людям естественным и является изобретением не очень древним. Ну и еще вариант классификации - методы основанные на сравнении самих значений и методы, основанные на их цифровых свойствах. Так называемые методы хэширования. Рассматриваемые в данном обзоре алгоритмы являются комбинациями первых двух способов классификации. О методе хэширования я лишь упомяну исключительно из академического интереса, так как он не входит в круг рассматриваемых здесь задач. Представьте себе, что нам надо найти определенную карту в колоде. Для этого мы выбрали довольно нестандартный способ, мы придумали такую функцию, которая вычисляет номер нужной карты в колоде. Например
f("крестовая дама") = 5 ,  а f("туз пик")=12
То есть, подставив в функцию искомое значение, мы получаем местоположение этого значения в нашем массиве. Вот здорово! Это же самый что ни на есть ПРЯМОЙ доступ , по сравнению с ПОСЛЕДОВАТЕЛЬНЫМ перебором всех ( или некоторых ) значений в искомом массиве. Этот метод и называется методом хэширования. Все бы хорошо, но вот беда, функция f(К) должна определять ОДНОЗНАЧНОЕ соответствие для каждого значения в массиве, а находить подобные функции довольно сложно.

Подробное рассмотрение алгоритмов.

Попытаемся решить простейшую задачу - найти нужное значение в массиве. На этом первом примере мне хочется показать, как оптимальность решения зависит от того, в каком массиве мы будем искать. Перед Вами колода игральных карт, в ней , как правило, 54 карты ( ну или 36 ). Карты лежат в беспорядке. Необходимо найти определенную карту, например пикового короля. Что Вы предпримете? Думаю, что догадаться нетрудно, первое, что приходит в голову, это последовательно перебирать все карты, до тех пор пока нужная не найдется. При самом плохом стечении обстоятельств Вам придется перебрать все карты в колоде , что на самом деле не так долго, если честно. Попробуем немного изменить условие. Искать нам надо не карту, а конверт с определенным адресом. Конвертов этих перед Вами лежит немерянное количество, если окинуть взглядом заваленные столы, ну несколько сотен как минимум. Нет, нет, не пожимайте удивленно плечами, задача не надуманная, а самая что ни на есть реальная. Если вдруг кто-нибудь ( а мир так удивительно тесен! ) знаком с Константиновым Николаем Николаевичем, тот скорее всего знаком с этой задачей не понаслышке. Итак, я отвлеклась, перед нами несколько сотен конвертов с именами, нужно найти вполне определенный конверт. Способ, только что опробованный на колоде карт, очень скоро утомит. Вряд ли кто в этом сомневается, не правда ли? :о) А вот если предварительно все эти конверты разложить в алфавитном порядке, то дальнейшая работа по нахождению вполне определенного имени уже не представляется такой ужасной! Конечно, на сортировку необходимо затратить вполне определенные силы и время, но они окупятся при многоразовом поиске, вот что самое главное Вспомнив пример с картами, отметим, что в том случае тратить время на предварительную сортировку колоды вряд ли стоит... Итак, мы можем сделать первый и очень важный вывод - оптимальность метода поиска зависит от размера массива, в котором мы ищем! Прямой перебор всех значений прост в понимании, легок в исполнении и достаточно быстр на малых массивах данных. Насчет легкости и простоты , это не лирика, это весомый аргумент, ведь Вам после того, как будет выбран определенный метод, необходимо еще формализовать его и закодировать. В итоге программиста инетересует код. Не будем об этом забывать. Итак, все это в совокупности позволяет сказать, что метод прямого перебора оптимален для малых массивов. Если же массив данных достаточно велик, то оптимальность поиска достигается предварительной сортировкой значений в массиве. Ну что же, с задачей поиска при малом количестве значений мы разобрались, она достаточно тривиальна. Давайте займемся поиском в о-о-очень длинном массиве... Это гораздо интереснее. Итак, приняв все вышесказанное за истину, мы отсортировали наш массив. Все значения лежат в некотором порядке. Можно искать! И, собственно, что?... Как искать-то? Существует несколько классических способов поиска значения в отсортированном массиве. Мы рассмотрим три способа. Они были предложены в 60-х годах и мало изменились в наше время. Один из них очень известный, ведь Вам наверняка приходилось слышать такие названия как "метод дихотомии", "бинарного дерева" или "метод половинного деления", что собственно одно и тоже. Вот с него и начнем.

1. Алгоритм поиска по бинарному дереву.

Суть этого алгоритма достаточно проста. Представим себе, что у нас есть набор карточек с телефонными номерами и адресами людей. Карточки отсортированы в порядке возрастания телефонных номеров. Необходимо найти адрес человека, если мы знаем, что его номер телефона 222-22-22. Наши действия? Берем карточку из середины пачки, номер карточки 444-44-44. Сравнивая его с искомым номером, мы видим, что наш меньше и , значит, находится точно в первой половине пачки. Смело откладываем вторую часть пачки в сторону, она нам не нужна, массив поиска мы сузили ровно в два раза. Теперь берем карточку из середины оставшейся пачки, на ней номер 123-45-67. Из чего следует, что нужная нам карточка лежит во второй половине оставшейся пачки... Таким образом , при каждом сравнении , мы уменьшаем зону поиска в два раза. Отсюда и название метода - половинного деления или дихотомии. Не буду приводить математического доказательства, так как это не является целью данной статьи, а просто отмечу, что скорость сходимости этого алгоритма пропорциональна Log(2)N ¹ . Это означает буквально то, что не более, чем через Log(2)N сравнений , мы либо найдем нужное значение, либо убедимся в его отсутствии. Другое название этого алгоритма – «метод бинарного дерева» происходит из представления "пути" поиска в виде дерева (у которого каждая следующая ветвь разделяется на две, по одной из которых мы и движемся в дальнейшем).. Способ очень распространенный в наше время, возможно по причине его эффективности вкупе с простотой программирования этого алгоритма. Именно бинарный поиск используется при поиске в индексах таблиц. Есть еще один алгоритм, основанный на делении искомого массива на части, аналогичный предыдущему. Я упомяну о нем скорее из-за некоторой его экзотичности.

2. Поиск по "дереву Фибоначчи".

Рассмотрим его исключительно из академического интереса. Для тех, кто пожелает разобраться в методе более детально, список литературы в конце статьи. Трудноватый для восприятия метод, но эффективность его немного выше, чем у поиска по Бинарному дереву, хотя так же пропорциональна Log(2)N. В дереве Фибоначчи числа в дочерних узлах, отличаются от числа в родительском узле на одну и ту же величину, а именно на число Фибоначчи. Суть метода в том, что сравнивая наше искомое значение с очередным значением в массиве , мы не делим пополам новую зону поиска , как в бинарном поиске, а отступаем от предыдущего значения, с которым сравнивали, в нужную сторону на число Фибоначчи. Почему этот способ считается более эффективным, чем предыдущий? Ответ достаточно неожиданный и боюсь , что нам с Вами оценить его должным образом вряд ли удастся. Дело в том, что метод Фибоначчи включает в себя только такие арифметические операции, как сложение и вычитание, нет необходимости в делении на 2, тем самым экономится процессорное время! Хочется напомнить, что речь идет о том времени, когда был изобретен метод, а именно - о 60-х годах. В то время процессорное время можно было экономить на подобных подходах.

3. Метод экстраполяций.

Переходя к следующему методу, давайте представим, что Вам срочно понадобилось узнать, как переводится на русский язык английское слово treasure. То есть перед нами задача - найти это слово в словаре. По сути все наши дальнейшие действия будут ничем иным, как реализацией некоторого алгоритма поиска. Массив значений - это словарь, все значения в нем отсортированы по алфавиту. Искомое слово нам известно. Значит сейчас мы будем искать в отсортированном массиве. Если честно, то я с трудом представляю себе, что Вы станете делить все страницы книги пополам, смотреть что там в середине и отлистывать в одну или другую сторону ровно ¼ страниц словаря и так далее... То есть метод дихотомии Вы проигнорируете. И уж совсем неожиданно предположить, что Вы воспользуетесь деревом Фибоначчи. Вы просто возьмете и ... и найдете нужное слово, не правда ли? А теперь остановимся и еще раз повторим поиск , при этом очень внимательно проанализируем наши действия. Итак, искомое слово начинается на букву T , открываем словарь немного дальше, чем на середине. Нам попалась буква R, ясно , что искать надо во второй части словаря, а на сколько отступить? На половину? "Ну зачем же на половину, это больше, чем надо", скажете Вы и будете правы. Ведь нам не просто известно, в какой части массива искомое значение, мы владеем еще и информацией о том, насколько далеко надо шагнуть! Вот мы и подошли к сути рассматриваемого метода. В отличии от первых двух , он не просто определяет зону нового поиска, но и оценивает величину нового шага. Алгоритм носит название экстраполяционного метода и имеет скорость сходимости большую, чем первые два метода. Примечание: Если при поиске по бинарному дереву за каждый шаг массив поиска уменьшался с N значений до N/2 , то при этом методе за каждый шаг зона поиска уменьшается с N значений до корня квадратного из N. Если К лежит между Kn и Km , то следующий шаг делаем от n на величину
(n - m)*(K - Kn)/(Km - Kn)
Скорость экстраполяционнго метода начинает существенно превышать скорость метода половинного деления при больших значениях N. Итак, мы рассмотрели конкретные алгоритмы поиска в отсортированном массиве. Таким образом можно подвести итоги: Если необходимо искать в небольшом массиве и искать нужно нечасто, проще воспользоваться методом перебора всех значений массива; Если массив значений большой и искать нужно часто, отсортируйте массив и воспользуйтесь методом половинного деления или интерполяционным методом. Каким из них именно, выбирайте исходя из того, насколько велик массив и какой метод Вам самим легче реализовать в коде. При предложенном выборе возникает резонный вопрос , а "большой массив" это какой? При оценке времени, которое затрачивает алгорит на поиск имеют значение две величины. Во-первых, это размерность массива (количество элементов) и, во-вторых, это количество обращений (то есть, сколько раз нам нужно в нем искать). Статьи по Delphi (015)… Итак, имеем массив размерностью N и проводим М раз в нем поиск. Количество операций, которые выполнит алгоритм прямого перебора, пропорционально M*N. Метод дихотомии совершит 2М*Log(2)N операции , да еще и время на сортировкку надо прибавить - N*Log(2)N Итак, имеем два выражения :
T1 =  M*N;
T2 =  2М*Log(2)N + N*Log(2)N 
При Т1 = Т2 мы находимся на границе, на которой одинаково оптимальны оба алгоритма. Из этого равенства можно получить области в системе координат "Количество обращений - Размерность массива", определяющие оптимальность одного алгоритма относительно другого (см. рисунок).

Способы коррекции алгоритмов поиска.

Основным критерием выбора оптимального метода был размер массива, в котором мы ищем. А более конкретные условия задачи могут существенно повысить скорость уже выбранного метода. До сих пор мы считали, что каждое значение массива с одинаковой вероятностью может оказаться искомым. Но это утверждение не для всех задач верно. Предположим, что у нас есть конверты с адресами учеников. Конвертов немного и сортировать мы их не стали. Ученики эти принимали участие в различных конкурсах и заняли там призовые места. Причем один человек мог отличиться не в одном , а во многих конкурсах. Перед нами задача: по спискам каждого конкурса найти отличившихся и вложить в его конверт соответствующее уведомление. Естественно, что уведомлений будет столько , сколько состязаний ученик выиграл. Через некоторое время, вкладывая очередное n-ое уведомление в конверт очередного юного гения и в глубине души догадываясь, что это не последний выигранный им конкурс, Вам вдруг захочется, чтобы его конверт лежал где-то в начале пачки.... И совершенно не важно, каким методом поиска Вы пользуетесь, Важно другое - исходя из условий конкретной задачи этот метод нуждается в коррекции. Вот тут нам поможет довольно простая схема динамического метода коррекции поиска или метод "Самоорганизующегося" списка. Идея метода проста - когда мы находим нужный конверт, мы кладем его в начало пачки. То есть перемещаем значение в начало массива, помните перестраиваемый массив при динамическом поиске? В итоге, часто используемые элементы будут расположены довольно близко к началу массива. Таким образом, количество сравнений до удачного поиска будет минимизироваться "само-собой". И через несколько иттераций в начале пачки автоматически окажутся конверты будущих ломоносовых. Математически это можно объяснить так: предположим , что значение Ki будет разыскиваться с вероятностью Pi, причем
P1 + P2 + ... Pn = 1 
Время удачного поиска пропорционально С , среднее значение которого :
 С = P1 + 2*P2 + .... N*Pn .
Минимального значения С достигнет при :
P1 > P2 > ... > Pn 
То есть тогда, когда наиболее часто используемые записи находятся в НАЧАЛЕ таблицы. Данный способ, естественно, стоит применять в случае наибольшей вероятности именно удачного поиска. Не будем загромождать статью математическими выкладками, ибо тема несколько иная, но в результате оказывается, что оптимальное ( с точки зрения частоты обращений) расположение значений экономит около трети поискового времени! Возможно этот метод кому-то покажется экзотическим ( опять же, вероятности какие-то, это вам не Ctrl-C & Ctrl-V , ужас просто) , но, в зависимости от условия задачи, метод может быть очень эффективным. Менее экзотический, но тоже сильно зависящий от конкретных условий задачи, динамический метод "очищаемого" списка. Это не самостоятельный метод поиска, а именно коррекция одного из методов описанных выше. Когда при удачном поиске значения, запись ему соответствующая, удаляется из массива, уменьшая такми образом его размерность. И еще один момент. До сих пор мы считали, что в рассматриваемых задачах вероятность удачного поиска достаточно велика. Представим себе, что поиск поисходит в неком массиве, про который мы точно знаем, что искомые значения скорее всего там не содержатся! Чем же нам это помогает? А вот чем: мы можем минимизировать количество сравнений, заранее отсекая неудачный поиск, то есть значения ,которых нет в массиве. Предположим, что у нас есть большой массив отсортированных значений. Достаточно перед началом поиска очередного значения проверить, а попадает ли оно в принципе в массив? Это совершенно просто :
если (Ki < K1)  или (Ki > Kn)
, то , собственно говоря, чего суетиться-то? Но применять этот способ коррекции алгоритма можно только в том случае, если вероятность неудачного поиска достаточно велика. Потому как при большой размерности массива еще и прибавлять сравнение на каждом шаге можно только в том случае, если очередное сравнение СКОРЕЕ ВСЕГО заменит нам очередной виток поиска. Оценим, какова должна быть вероятность неудачного поиска, то есть отсутствие искомого значения в массиве, чтобы было выгодно применять эту модификацию алгоритма. (Приведенная ниже оценка вероятности представлена Николаевым А.) Пусть Р - вероятность того, что искомое значение отсутствует между минимальным и максимальным значением массива. Тогда мы, отсекая сверхэкстремальные значения, в среднем не производим Р*(С - 2) операций, где С - среднее число операций при проведении немодифицированного поиска. В то же время мы вынуждены впустую совершить 2*(1 - Р) операций для сравнения значения с минимальным и максимальным в массиве. Так как мы должны быть в выигрыше, то
Р*(С - 2)  >  2*(1 - Р)  , следовательно PC -2P + 2P - 2 > 0
РС > 2 , ну и наконец : P > 2/C
Таким образом, при P > 2/C мы будем в выигрыше. Например, для поиска по бинарному дереву в случае массива с 32 элементами достаточно один раз из четырех задавать сверхэкстремальное значение для поиска, чтобы модифицированный алгоритм работал быстрее. Вот, пожалуй, и все.

Историческая справка:

Бинарный поиск впервые упомянут Джоном Мочли в 1946г. Экстраполяционный поиск предложен У.Петерсоном 1957. Фибоначчиев поиск изобретен Д.Фергюсоном в 1960.

Для тех, кто интересуется математической основой упомянутых методов:

Д.Кнут "Искусство программирования для ЭВМ", 1978г, издательство "МИР", том 3 "Сортировка и поиск". Популярные лекции по математике, выпуск №6. 1969г. Издательство "Наука" Н.Н. Воробьев. "Числа Фибоначчи" Р.Альсведе, И.Вегенер "Задачи поиска" , 1982г, Издательство "Мир"

Поиск и фильтрация данных в Delphi.

Автор: Storm Введение Базы данных являются удобным средством хранения структурированной информации. Однако само по себе накопление и хранение информации делает базу данных большой кучей мусора. Удобно организованный поиск и отбор информации способен резко повысить эффективность ее использования. В данной статье мы попытаемся разобраться в методах поиска информации, реализованных в Delphi. Предполагается, что Вы в общих чертах знакомы с теорией реляционных баз данных и имеете некоторый опыт программирования на Delphi. Общие положения Для понимания реализации методов поиска Delphi необходимо сделать краткое отступление относительно идеологии построения баз данных. Итак, всю систему работы с БД можно представить в виде трех слоев или модулей (рис.1). Хранилище данных отвечает за хранение информации и обеспечение ее целостности и непротиворечивости. Бизнес логика реализует набор правил предметной области приложения, т.е правил изменения и дополнения информации. Презентационный слой выводит данные в форме доступной пользователю и обеспечивает интерфейс для взаимодействия с бизнес логикой. Очевидно, что нас будет интересовать взаимодействие двух верхних модулей с хранилищем данных. В зависимости от специфики реализации базы данных данные слои могут объединяться или реализовываться как отдельные независимые модули. Для нашей задачи системы управления базами данных можно классифицировать по следующим признакам: По типу хранилища данных: - файловые - серверные Файловые хранилища представляют собой набор файлов таблиц БД, сосредоточенных, как правило, в одной директории. Клиентское приложение имеет доступ к таблицам на уровне файловой системы. Серверные - хранилище данных представляет собой программу, извлекающую информацию из файла(ов) БД по запросам клиентских приложений. При этом не на ПК клиента не требуется иметь непосредственный доступ к файловой системе сервера. По количеству слоев: - Двухуровневые - Многослойные Двухуровневые СУБД совмещают в клиентском приложении бизнес логику и презентационные функции. Примером работы такой схемы является классическое приложение СУБД, где клиентское приложение осуществляет отображение данных пользователю и реализует функции проверки, контроля и отправки информации в хранилище данных. Многослойные (как правило 3-х) приложения кроме клиентской программы имеют специальную программу - сервер приложений. Сервер приложений реализует функции доступа к хранилищу данных и передает запрошенные данные клиентской программе, проверяя правильность запросов. Поиск данных Универсальные методы класса TDataSet

Класс TDataSet является базовым классом компонента, реализующим функции доступа к БД. Многие из его методов являются абстрактными и реализуются в потомках. Он имеет два метода для поиска данных: Locate и Lookup. Данные методы ищут запись, удовлетворяющую заданным условиям.

function Locate(const KeyFields: string; const KeyValues: Variant;
  Options: TLocateOptions): Boolean; virtual;
function Lookup(const KeyFields: string; const KeyValues: Variant;
  const ResultFields: string): Variant; virtual;
Разница между ними в том, что функция Locate при поиске записи позиционирует курсор на найденную запись, а Lookup не делает этого. Если поля указанные для поиска индексированы, то поиск производится с использованием индекса, что значительно ускоряет поиск. В качестве примера рассмотрим использование данных методов на примере TTable в двухуровневом приложении. Итак, начнем с генерации нового приложения File/New Application. Мы получим проект приложения с главной формой. На данную форму поместим компонент Table с закладки Data Access, по умолчанию он будет иметь имя Table1. Теперь настроим компонент Table1 на взаимодействие с таблицей country из базы данных DBDEMOS. Для этого необходимо установить следующие свойства Table1 в следующей последовательности: 1. DatabaseName ==> DBDEMOS 2. TableName ==> country.db

Теперь можно активизировать компонент Table1, установив его свойство Active в значение true. Далее разместим на форме компонент DataSource, позволяющий сделать данные Table1 доступными для визуальных компонентов отображения данных. Свяжем DataSource1 c Table1, установив св-во DataSet компонента DataSource1 равным Table1. Разместим визуальные компоненты отображения данных со страницы Data Controls: DBGrid и DBNavigator. Установим св-во DataSource обоих равным DataSource1. В результате мы получим форму, изображенную на рис. 2. Откомпилировав и запустив данный проект, мы сможем просматривать и редактировать данные таблицы country. Остается добавить элементы для организации поиска в таблице. Разместим на форме компонент Edit1 и две кнопки. Св-во Caption кнопки Button1 установим равным "Locate", а Button2 "Lookup". Далее в обработчике события OnClick кнопки Button1 организуем вызов метода Locate. Код обработчика события приведен ниже:

Статьи по Delphi (015)… Рис. 1. Статьи по Delphi (015)… Рис. 2.
TForm1.Button1Click(Sender: TObject);
begin
  if not Table1.Locate('Name', Edit1.Text, []) then
    ShowMessage('Запись не найдена');
end;
Разберем код более подробно. Строка Table1.Locate организует поиск записи в таблице Country. Первый параметр этой функции - поля, значения которых нужно проверять. В данном случае мы ищем запись по одному полю Name. Второй параметр, что шаблон поиска и третий опции поиска. Функция возвращает значение типа boolean, указывающее на успешность поиска. Теперь пришло время протестировать наш пример. Запустим программу на выполнение, в строке ввода пишем Cuba и нажимаем кнопку Locate. Курсор в DBGrid1 должен переместиться на запись, имеющую в поле Name введенное значение (рис.3).

Однако наш пример имеет пока один недостаток, в строку редактирования необходимо вводить полное имя c учетом регистра, т.е если мы вместо Cuba введем, например Cu или cuba, то наш поиск будет безрезультатным. Естественно это не может нас не устраивать. Поэтому пришло время рассмотреть более подробно опции поиска. Данный параметр имеет тип TlocateOptions и позволяет задавать набор из двух параметров поиска: loCaseInsensitive и loPartialKey. Установка первого из них отменяет чувствительность к регистру в текстовых полях, а второй позволяет искать запись частично соответствующие заданному условию. С учетом вышесказанного код обработчика событий будет выглядеть следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Table1.Locate('Name', Edit1.Text, [loCaseInsensitive,
    loPartialKey]) then
    ShowMessage('Запись не найдена');
end;

Следующей проблемой является поиск записи по нескольким полям. Для организации поиска по имени страны и континенту добавим на форму еще один компонент Edit2. Код обработчика события нажатия на кнопку Locate изменим следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Table1.Locate(
    'Continent;Name',
    VarArrayOf([Edit2.Text, Edit1.Text]),
    [loCaseInsensitive, loPartialKey]) then
    ShowMessage('Запись не найдена');
end;
Как видно, при поиске по нескольким полям, все они перечисляются в параметре функции Locate Запустив приложение, в строке поиска континента пишем South america, а в строке "страна" - C. Нажимаем кнопку Lookup - результат поиска - установка курсора в DBGrid на запись Chile. В ходе написания этой статьи выяснилась одна особенность. Частичный поиск при поиске по нескольким полям работает лишь для последнего поля, указанного в списке.

Модифицируем наш пример для использования функции Lookup. Ниже приведен код обработчика события нажатия на кнопку Lookup:

procedure TForm1.Button2Click(Sender: TObject);
var
  Res: Variant;
begin
  Res := Table1.Lookup('Continent;Name', VarArrayOf([Edit2.Text,
    Edit1.Text]), 'Area');
  if Res <> Null then
    ShowMessage('Area ' + string(Res));
end;
Как видно из кода, мы проводим поиск по полям Continent, Name. При нахождении записи мы выдаем сообщение о площади страны, при это курсор на найденную запись не перемещается. К сожалению в параметрах функции отсутствует LocateOptions. Исходный код данного примера расположен в файле. Итак, подведем некоторые итоги. Функции Locate и Lookup предназначены для поиска в базе данных одной записи, удовлетворяющей заданным условиям. Данные методы определены в классе TDataSet как виртуальные и могут быть переопределены в классах потомках. Метод Locate устанавливает курсор на обнаруженную запись, Lookup этого не делает. Метод Locate, хотя и с некоторыми оговорками, может искать записи по частично заданному ключу. Необходимо отметить, что очень часто необходимо найти сразу несколько записей. Для решения такого рода задач нужно применять методы, предоставляемые классом Ttable, Tquery …, либо проводить фильтрацию. Как это сделать описано ниже. Методы класса TTable Компонент TTable предназначен для работы с таблицей база данных. Данный компонент используется в двухуровневых приложениях баз данных, либо в сервере приложений в трехуровневой БД. Как правило, использование TTable для работы с клиент-серверной БД оказывается менее эффективным, чем TQuery, поскольку TTable извлекает сразу ВСЕ записи из таблицы, а в TQuery лишь удовлетворяющие условиям запроса. Компонент имеет несколько специфических методов для поиска данных. Данные методы используются для поиска только по индексированным полям (для dBase и Paradox как минимум). Все они делятся на две группы: - Методы поиска одной записи - Методы поиска диапазона записей. Рассмотрим сначала первую группу методов. К ним относятся GotoKey, FindKey, GotoNearest, FindNearest. Первые два метода используются для поиска строго соответствия, другие ищут частичное соответствие. В качестве примера создадим новое приложение, на главной форме разместим компоненты Table, DataSource, DBGrid, DBNavigator. Установим св-ва данных компонентов в соответствии с таблицей 1. Отметим, что таблица country.db имеет индексированное поле Name, по которому мы далее организуем поиск.

Таблица 1

Table1
DatabaseName BCDEMOS
TableName Country.db
Active true
DataSource1
DataSet Table1
DBNavigator1, DBGrid1
DataSource DataSource1

Далее разместим на форме компонент Edit и четыре кнопки, установив их св-во Caption в GotoKey, GotoNearest, FindKey, FindNearest. Ниже приведен код обработчиков событий нажатия на эти кнопки.

//Использование GotoKey
procedure TForm1.Button1Click(Sender: TObject);
begin
  with Table1 do
  begin
    EditKey;
    FieldByName('Name').AsString := Edit1.Text;
    if not GotoKey then
      ShowMessage('Not found');
  end;
end;
// Использование GotoNearest
procedure TForm1.Button2Click(Sender: TObject);
begin
  with Table1 do
  begin
    EditKey;
    FieldByName('Name').AsString := Edit1.Text;
    GotoNearest;
  end;
end;
// Использование FindKey
procedure TForm1.Button3Click(Sender: TObject);
begin
  with Table1 do
  begin
    if not FindKey([Edit1.Text]) then
      ShowMessage('Not found');
  end;
end;
// Использование FindNearest
procedure TForm1.Button4Click(Sender: TObject);
begin
  with Table1 do
  begin
    FindNearest([Edit1.Text]);
  end;
end;
Прокомментируем вышеприведенный код. Методы GotoKey, FindKey проводят поиск на точное соответствие заданному ключу. Они возвращают значение типа boolean, говорящее об успехе поиска. Методы GotoNearest, FindNearest проводят поиск первой записи хотя бы частично соответствующей ключу. Они не возвращают значений, т.к такой вид поиска всегда будет успешным. Перед вызовом методов GotoKey, GotoNearest необходимо вызывать метод EditKey или SetKey, чтобы перевести компонент Table в режим редактирования ключа поиска. Код данного примера содержится в файле. Методы поиска диапазона записей позволяют отобразить записи таблицы, лежащие в указанном диапазоне значений поля. Для таблиц Paradox и dBase данные методы работают только для индексированных полей. К данным методам относятся SetRangeStart, SetRangeEnd, EditRangeStart, EditRangeEnd, ApplyRange, CancelRange. Техника использования данных функций очень проста. Сначала необходимо установить начало и конец диапазона вызовом функций SetRangeStart, SetRangeEnd, EditRangeStart, EditRangeEnd, указывая при этом значения полей формирования диапазона. Затем вызовом ApplyRange применить указанный диапазон. Сброс установленного диапазона позволяет вновь отобразить все записи таблицы. Он выполняется вызовом функции CancelRange.

Для иллюстрации вышесказанного создадим новый проект, на главную форму поместим компоненты Table, DataSource, DBGrid, DBNavigator. Установим их свойства как указано в таблице 1. Далее разместим две строки ввода и две кнопки SetRange и CancelRange. Обработчики событий нажатия этих кнопок приведены ниже.

procedure TForm1.SetRangeClick(Sender: TObject);
begin
  with Table1 do
  begin
    SetRangeStart; {Входим в режим установки начала диапазона}
    FieldByName('Name').AsString := Edit1.Text; {значение начала диапазона}
    SetRangeEnd; { Входим в режим установки конца диапазона }
    FieldByName('Name').AsString := Edit2.Text; {значение конца диапазона}
    ApplyRange; { Применяем указанный диапазон}
  end;
end;
procedure TForm1.CancelRangeClick(Sender: TObject);
begin
  Table1.CancelRange; {Очистка диапазона}
end;
Код данного примера содержится в файле. Использование TQuery Компонент TQuery предназначен для извлечения данных с помощью языка SQL (Structured query language). Компонент используется в двухуровневых приложениях работы с базами данных и в сервере приложений в трехуровневых. Он, как правило, применяется для работы с клиент-серверными базами данных. Сам компонент TQuery не имеет специальных методов поиска записей, однако, возможности поиска заложены в языке SQL.Ограничения на простые данные, получаемые предложением select, реализуются с помощью предложения where. В качестве иллюстрации приведем следующий пример. Создадим новое приложение, разместим на его главной форме компоненты: Query, DataSource, DBGrid, DBNavigator, Memo, две кнопки Button1 и Button2. Установим свойства компонентов, как это указано в таблице 2.

Таблица 2

Query1
DatabaseName BCDEMOS
SQL SELECT Name, Capital, Continent, Area, PopulationFROM "country.db" CountryWHERE (Area > 50000) AND (Population > 20000000)
Active true
DataSource1
DataSet Query1
DBNavigator1, DBGrid1
DataSource DataSource1
Button1
Caption Применить
Button2
Caption Отменить

Вид главной формы на этапе разработки приведен на рис. 4.

Статьи по Delphi (015)… Рис. 3.

В обработчике события OnCreate формы реализуем копирование текста запроса из Query1, код приведен ниже.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Text := Query1.SQL.Text;
end;

Кнопка Button1 при нажатии будет переносить текст запроса, исправленный пользователем, из Memo1 обратно в Query1. Обработчик нажатия Button1 будет выглядеть так:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Query1.Active := false;
  Query1.SQL.Text := Memo1.Text;
  Query1.Active := true;
end;
Обработчик события нажатия Button2 установим, указывающим на FormCreate. Откомпилируем и запустим нашу программу. Убедимся в том, что при изменении текста запроса (площади и населения) происходит изменение записей в DBGrid1.

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

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

Код данного примера содержится в файле.

Более приемлемым является применение параметризированных запросов. В этом случае в тексте запроса содержатся параметры для дальнейшей подстановки конкретных значений на этапе выполнения. Параметры в тексте запроса начинаются с двоеточия. Итак, создадим новое приложение, разместим на главной форме компоненты Query, DataSource, DBGrid, DBNavigator, кноку Button1. Установим их свойства аналогично предыдущему примеру. Далее текст св-ва SQL компонента установим равным:
SELECT Name, Capital, Continent, Area, Population 
FROM "country.db" Country 
WHERE (Name = :Name_param) 
В последней строке мы определили параметр запроса Name_param. Теперь открыв в инспекторе объектов редактор св-ва Params компонента Query1, мы увидим элемент, соответствующий определенному параметру. Выбрав данный элемент отредактируем его свойства. DataType - тип значения параметра (по умолчанию ftUnknown) установим в ftString. ParamType - тип параметра (по умолчанию ptUnknown) установим в ptInput.

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  Query1.Close;
  if not Query1.Prepared then
    Query1.Prepare;
  Query1.ParamByName('Name_param').Value := Edit1.Text;
  Query1.Open;
end;
Для увеличения скорости поиска в строках 4 и 5 осуществляется проверка подготовки запроса. Если запрос не подготовлен (что случается при старте программы) вызывается метод Prepare. При этом сервер анализирует и компилирует запрос, сохраняя его на сервере. Далее серверу необходимо отсылать лишь значения параметров запроса.

Скомилируем и запустим на выполнение созданное приложение. В строке поиска введем значение Canada и нажмем кнопку "Применить". Результатом поиска будет вывод в строки в DBGrid1, как это показано на рис. 5.

Статьи по Delphi (015)… Рис. 4. Код данного примера содержится в файле.

Для поиска записей частично соответствующих введенному критерию изменим текст запроса компонента Query1 следующим образом:

SELECT Name, Capital, Continent, Area, Population
FROM "country.db" Country
WHERE   (Name Like :Name_param)
Как видно мы заменили оператор строго равенства (=) на LIKE. Оператор Like позволяет искать текстовые значения нестрого соответствующие заданному критерию. При этом в критерии поиска можно использовать знаки подстановки. % - любые символы, _ - один символ. Например, критерий поиска С% в нашем случае даст нам вывод четырех записей (Canada, Chile, Colombia, Cuba). В одном запросе можно комбинировать несколько условий поиска с помощью операторов OR, AND итд. Конечно, мы осветили лишь небольшую часть возможностей SQL по поиску данных. Если Вы хотите получить более подробную информацию, рекомендую приобрести какую-либо книгу по SQL. Фильтрация В отличие от методов поиска, предполагающих извлечение данных из хранилища данных фильтрация предполагает отбор уже извлеченных данных в клиентском приложении. Для реализации данного подхода в Delphi в компонентах доступа к данным введены два свойства Filter и Filtered. Установка свойства Filtered типа boolean в true переводит компонент в режим фильтрации. В свойстве Filter при этом можно определить значение фильтра для отбора записей. Построение фильтра во многом похоже на построение условия where в SQL запросе. Основное отличие в том, что слово where не пишется, используются другие знаки подстановки, в тексте фильтра нельзя после знаков сравнения вставлять имена полей для локальных таблиц. Если имя поля содержит пробелы, то оно заключается в квадратные скобки, например [Home directory] Свойство FilterOptions позволяет установить дополнительные параметры фильтрации, а именно foCaseInsensitive - нечувствительность к регистру в текстовых полях; foNoPartialCompare - отсутствие поиска по частичному условию, при установке данной опции знак * воспринимается как литера, а не как знак подстановки любые символы. Пример фильтрации можно найти в проекте Demos\DB\Filter из примеров, поставляемых с Delphi.

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

type TFilterRecordEvent = procedure(DataSet: TDataSet; 
  var Accept: Boolean) of object; 

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

Accept := DataSet['DateOfPayment'] > DataSet['DateOfPurchase'] + 30; 
Не забывайте, что обработчик события дополняет, а не замещает свойство Filter, т.е если включена фильтрация (Filtered := true) и св-во фильтр содержит значение фильтра, то в обработчике события и фильтр связаны логическим отношением "AND". Заключение

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

Поиск звуковой платы.

На уроке литературы в компьютерном лицее: - Герасим был это... этим... Ну, звуковой карты у него не было.
uses MMsystem;
begin
  if WaveOutGetNumDevs>0 then
    Result:='Yes'
  else
    Result:='No';
end;

Процедура поиска закладок и перехода по ним.

// процедура поиска закладок и перехода по ней
procedure WordGotoBookmark(rBookmark: string);
var
  Whatr: OLEVariant;
  // OleEnum;
  // OLEVariant;
  // What :TWordDocument;
  Which: OLEVariant;
  Count: OLEVariant;
  Name: OLEVariant;
begin
  Whatr := wdGoToBookmark;
  Which := unAssigned;
  Count := unAssigned;
  Name := rBookmark;
  Form1.WrdApp.Selection.GoTo_(Whatr, Which, Count, Name);
end;

Произвести поиск байта или слова в строке.

Автор: http://sunsb.dax.ru У семейства x86 есть группа специализированных строковых инструкций, одна из которых - scasb/scasw - производит поиск байта/слова в строке. Использовать преимущества этой инструкции в Delphi позволяют длинные строки, которых в старых паскалях не было.

Никаких сложностей с пониманием, на мой взгляд, быть не должно. Единственное это смена режима открытия файла (FileMode := 0), которая позволит открыть файлы заблокированные ядром Windows и сдвиг указателя файла при чтении нового блока влево на длину искомой строки. Сдвиг делается на случай разрезания искомой строки на части при чтении файла. Полный текст проверенной программы:

program search;
{$APPTYPE CONSOLE}
uses  SysUtils;
const buffSize  = 16384;
var F           : File;
var buff        : AnsiString;
var oldFileMode : integer;
var SearchString: shortString='SunSB';
var SearchPos   : integer = -1;
var readed      : integer;
var blockStart: integer;
begin
   SetLength( buff, buffSize);
   assignFile( F, 'Speedometer2.exe');
   oldFileMode := FileMode;
   FileMode := 0;
   reset( F,1);
   while not eof( F ) do begin
      blockStart := filePos( F );
      blockRead( F, buff[1], buffSize, readed);
      SearchPos:=Pos( SearchString, buff );
      if SearchPos >  0 then begin
         WriteLn( 'Substr found at pos ',
                        blockStart+SearchPos );
         break;
      end;
      if readed=buffSize then
         seek( F, ( filePos( F ) -
                    length( SearchString )));
   end;
   closeFile( F );
   FileMode := oldFileMode;
   SetLength( buff, 0 );
   if SearchPos = 0 then
      WriteLn( 'Substr not found.');
   readLn;
end.

Поиск класса.

Автор: Mike Scott Могу ли я во время выполнения приложения определить, существует ли класс с именем Tlog? Используйте в модуле TLog RegisterClass( TLog ) или потомка TLog, затем FindClass( 'TLog' ) или FindClass( 'TLogSubclass' ) для получения ссылки на класс в вызывающем модуле, позволяя тем самым пользоваться объектами данного класса. Вы также могли бы добавить классовый метод, возвращающий существующий экземпляр или NIL, или который создает и возвращает новый экземпляр при отсутствии текущего. Самое необходимое, что нужно сделать, это создать абстрактный, чисто виртуальный базовый класс TLog, и TLogSubclass, реально наполненный функциональным назначением. Вызывающему оператору необходимо знать всего лишь о TLog, а не о TLogSubclass, чтобы получить доступ к его методам и свойствам.

Тем не менее, я считаю не лучшим решением технологию поиска класса по его имени. Вот что предлагаю я:

unit LogUnit;
interface
type
  TLog = class
  public
    constructor Create;
    procedure LogMessage(const Message: string); virtual; abstract;
  end;
var
  Log: TLog;
implementation
constructor TLog.Create;
begin
  Log := Self;
end;
procedure TidyUp; far;
begin
  Log.Free;
end;
initialization
  AddExitProc(TidyUp);
end.
unit LogImpl;
interface
implementation
uses Log;
type
  TLogImplementation = class(TLog)
  public
    procedure LogMessage(const Message: string); override;
  end;
procedure TLogImplementation.LogMessage(const Message: string);
begin
  { записываем сообщение в журнальный файл }
end;
initialization
  TLogImplementation.Create;
end.
Обратите внимание на то, что здесь используются "скрытые" данные - класс TLogImplementation объявлен внутри секции реализации модуля LogImpl, поэтому никакой другой модуль их не видит. Фактически, интерфейсная часть полностью пустая! Вы можете протестировать это, изучив Log и увидев NIL в самом начале.

Кроме того, вы могли бы иметь ничего не делающий TLog.LogMessage. Затем вы могли бы создать экземпляр TLog в секции инициализации модуля LogUnit.pas, и освобождать его перед созданием экземпляра TLogImplementation в LogImpl.pas. Таким образом, для подключения к приложению класса, нужно просто добавить к проекту модуль LogImpl.

Поиск DOS-окна.

Статьи по Delphi (015)…
Как отладить Windows. Заходите в DOS, набираете "format c:", а затем нажимаете "y". По другому он не понимает.

При поиске окон, как отмечалось, нужен класс и имя, так вот - если Вы ищите DOS-окно, то его класс всегда = 'tty'.

Поиск файлов.

Статьи по Delphi (015)…
Новая русскоязычная поисковая система "Иван Сусанин"

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

В Delphi существует две функции для поиска файлов. Это:

function FindFirst(const Path: string; Attr: Integer;
  var F: TSearchRec): Integer;
function FindNext(var F: TSearchRec): Integer;

Разберемся, что же означают эти функции. Для начала возьмем первую - FindFirst. Разберемся сначала с переменными.

Path Это переменная, как видно из названия, показывающая путь к директории, где будет производиться поиск файла. Кроме этого, в эту переменную входит также и имя файла (файлов), которые должны быть найдены. Причем, в названии файла можно пользоваться такими символами: * (звездочка) и ? (знак вопроса). Значения этих символов стандартны: знак вопроса - любой допустимый символ, звездочка - комбинация любых допустимых символов. Под допустимыми символами я понимаю символы, которые могут использоваться в операционной системе для обозначения имен файлов.

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

{поиск файлов с любым именем в корне диска C:}
Path:='c:\*.*';
{поиск файлов в директории E:\AUDIO с именем, которое
начинается на song3, с расширением .wav}
Path:='e:\audio\song3?.wav';

Обратите внимание: Недопустимо использовать символы * и ? в названии директории. Эти символы могут использоваться только в имени файла.

Attr Переменная задает тип файлов, которые будут найдены. Тип переменной - Integer. Чтобы не мучаться с запоминанием цифр, рекомендую Вам запомнить такие слова: faReadOnly - файлы, у которых установлен аттрибут "Только для чтения". faHidden - файлы, у которых установлен атрибут "Скрытые". faSysFile - файлы, у которых установлен атрибут "Системный". faArchive - файлы, у которых установлен атрибут "Архивный". faDirectory - директория. То есть поиск поддиректорий в директории. faAnyFile - любой файл (в том числе и faDirectory, и faVolumeID).

Теперь с этими словами можно обращаться как с цифрами - складывать их и вычитать. Например:

{поиск Скрытых и Системных файлов}
Attr := faHidden + faSysFile;
{поиск всех файлов, кроме файлов,
имеющих атрибут "Только для чтения"}
Attr := faAnyFile - faReadOnly;
Однако учтите: При применении вычитания результат может получиться несколько неожиданным, поэтому им лучше не злоупотреблять.

Теперь разберемся, что же выдает функция. Функция возвращает 0, если была выполнена успешно, или, в противном случае, код ошибки. Кроме того, если функция нашла файл, удовлетворяющий и условиям переменной Path, и условиям переменной Attr, то она записывает результат в переменную F (типа TSearchRec), которая, естественно, должна быть объявлена командой Var.

Тип TSearchRec можно представить как:

type TSearchRec = record
  Time: Integer;
  Size: Integer;
  Attr: Integer;
  name: TFileName;
  ExcludeAttr: Integer;
  FindHandle: THandle;
  FindData: TWin32FindData;
end;

Теперь разберемся, что означает функция FindNext.

Если команда FindFirst нашла какой-либо файл, то, возможно, если имя файла задано с символами * и/или ?, есть еще один или несколько файлов, удовлетворяющих условию поиска. В этом случае и используется команда FindNext. Функция также возвращает 0, если была выполнена успешно, или, в противном случае, код ошибки. И также записывает данные в переменную F.

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

Простейший алгоритм:

var
  F: TSearchRec;
  Path: string;
  Attr: Integer;
begin
  {Искать все файлы в заданной директории с расширение .wav}
  Path := 'e:\audio\album31\*.wav';
  {которые имеют атрибуты "Только для чтения" и "Архивный"}
  Attr := faReadOnly + faArchive;
  FindFirst(Path, Attr, F);
  {Если хотя бы один файл найден, то продолжить поиск}
  if F.name <> '' then
  begin
    ListBox1.Items.Add(F.name); {Добавление в TListBox имени найденного файла}
    while FindNext(F) = 0 do
      ListBox1.Items.Add(F.name);
  end;
  FindClose(F);
end.

Обратите внимание на процедуру FindClose. Она освобождает память, которую заняли функции FindFirst и FindNext.

Как запустить диалог поиска файла.

Автор: Олег Кулабухов
У программиста потерялся файл, и он его никак не может найти. После пяти минут поисков он громко объявляет: - Так, потерялся файл. Просьба никому не выходить из комнаты!
procedure TForm1.Button1Click(Sender: TObject);
begin
  with TDDEClientConv.Create(Self) do
  begin
    ConnectMode := ddeManual;
    ServiceApplication := 'explorer.exe';
    SetLink('Folders', 'AppProperties');
    OpenLink;
    ExecuteMacro('[FindFolder(, C:\DOWNLOAD)]', False);
    CloseLink;
    Free;
  end;
end;

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

Автор: Xavier Pacheco

{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, FileCtrl, Grids, Outline, DirOutln;
type
  TMainForm = class(TForm)
    dcbDrives: TDriveComboBox;
    edtFileMask: TEdit;
    lblFileMask: TLabel;
    btnSearchForFiles: TButton;
    lbFiles: TListBox;
    dolDirectories: TDirectoryOutline;
    procedure btnSearchForFilesClick(Sender: TObject);
    procedure dcbDrivesChange(Sender: TObject);
  private
    FFileName: string;
    function GetDirectoryName(Dir: string): string;
    procedure FindFiles(APath: string);
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
function TMainForm.GetDirectoryName(Dir: string): string;
{ This function formats the directory name so that it is a valid
  directory containing the back-slash (\) as the last character. }
begin
  if Dir[Length(Dir)] <> '\' then
    Result := Dir + '\'
  else
    Result := Dir;
end;
procedure TMainForm.FindFiles(APath: string);
{ This is a procedure which is called recursively so that it finds the
  file with a specified mask through the current directory and its
  sub-directories. }
var
  FSearchRec,
    DSearchRec: TSearchRec;
  FindResult: integer;
  function IsDirNotation(ADirName: string): Boolean;
  begin
    Result := (ADirName = '.') or (ADirName = '..');
  end;
begin
  APath := GetDirectoryName(APath); // Obtain a valid directory name
  { Find the first occurrence of the specified file name }
  FindResult := FindFirst(APath + FFileName, faAnyFile + faHidden +
    faSysFile + faReadOnly, FSearchRec);
  try
    { Continue to search for the files according to the specified
      mask. If found, add the files and their paths to the listbox.}
    while FindResult = 0 do
    begin
      lbFiles.Items.Add(LowerCase(APath + FSearchRec.Name));
      FindResult := FindNext(FSearchRec);
    end;
    { Now search the sub-directories of this current directory. Do this
      by using FindFirst to loop through each subdirectory, then call
      FindFiles (this function) again. This recursive process will
      continue until all sub-directories have been searched. }
    FindResult := FindFirst(APath + '*.*', faDirectory, DSearchRec);
    while FindResult = 0 do
    begin
      if ((DSearchRec.Attr and faDirectory) = faDirectory) and not
        IsDirNotation(DSearchRec.Name) then
        FindFiles(APath + DSearchRec.Name); // Recursion here
      FindResult := FindNext(DSearchRec);
    end;
  finally
    FindClose(FSearchRec);
  end;
end;
procedure TMainForm.btnSearchForFilesClick(Sender: TObject);
{ This method starts the searching process. It first changes the cursor
  to an hourglass since the process may take awhile. It then clears the
  listbox and calls the FindFiles() function which will be called
  recursively to search through sub-directories }
begin
  Screen.Cursor := crHourGlass;
  try
    lbFiles.Items.Clear;
    FFileName := edtFileMask.Text;
    FindFiles(dolDirectories.Directory);
  finally
    Screen.Cursor := crDefault;
  end;
end;
procedure TMainForm.dcbDrivesChange(Sender: TObject);
begin
  dolDirectories.Drive := dcbDrives.Drive;
end;
end.

Поиск файлов по маске по всему дереву каталогов, начиная с указанного.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Поиск файлов по маске по всему дереву каталогов, начиная с указанного
Продедура производит фалов по маске в указанном и во всех его вложенных
каталогах.
Параметр StartFolder указывает на начальный каталог поиска,
параметр Mask указывает маску поиска (например '*.pas'),
параметр List указывает на список строк, в которых будут записаны
результаты поиска,
Необязательный параметр ScanSubFolders указывает на необходимость
поиска во вложенных подкаталогах.
Зависимости: Windows, SysUtils, Classes, Masks
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        29 апреля 2002 г.
***************************************************** }
procedure FindFiles(StartFolder, Mask: string; List: TStrings;
  ScanSubFolders: Boolean = True);
var
  SearchRec: TSearchRec;
  FindResult: Integer;
begin
  List.BeginUpdate;
  try
    StartFolder := IncludeTrailingBackslash(StartFolder);
    FindResult := FindFirst(StartFolder + '*.*', faAnyFile, SearchRec);
    try
      while FindResult = 0 do
        with SearchRec do
        begin
          if (Attr and faDirectory) <> 0 then
          begin
            if ScanSubFolders and (Name <> '.') and (Name <> '..') then
              FindFiles(StartFolder + Name, Mask, List, ScanSubFolders);
          end
          else
          begin
            if MatchesMask(Name, Mask) then
              List.Add(StartFolder + Name);
          end;
          FindResult := FindNext(SearchRec);
        end;
    finally
      FindClose(SearchRec);
    end;
  finally
    List.EndUpdate;
  end;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  FindFiles('C:\Program Files\', '*.exe', memo1.Lines, true);
end;

Поиск в базе данных ADO.

Самая распространённая задача, которую решают приложения работающие с базами данных - это поиск необходимых записей по заданному критерию. В Delphi, компоненты ADOExpress включают в себя методы поиска записей, аналогичные тем, которые используются в BDE. В данной статье будут рассмотрены различные способы поиска данных разработке ADO-приложений в Delphi Обычно алгоритм поиска строится по следующей схеме: начинаем поиск с начала таблицы, проверяем поле в каждой строке на предмет удовлетворения нашему критерию, останавливаем цикл на выбранной записи. Давайте рассмотрим несколько способов расположения данных, полученных из БД посредствам компонента ADODataset (для Таблицы и для Запроса). Locate

Этот универсальный метод поиска устанавливает текущую запись как первую строку, удовлетворяющую набору критериев поиска. Используя метод Locate мы можем искать значения одного или более полей, расположенных в массиве переменных. В приведённом ниже коде, метод Locate ищет первую запись, содержащую строку 'Zoom' в поле 'Name'. Если вызов Locate возвращает True - то запись найдена и установлена как текущая.

AdoTable1.Locate('Name','Zoom',[]);
{...или...}
var ffield, fvalue: string;
    opts : TLocateOptions;
  
ffield := 'Name';
fvalue := 'zoom';
opts := [loCaseInsensitive];
if not AdoTable1.Locate(ffield, fvalue, opts) then
  ShowMessage(fvalue + ' not found in ' + ffield);
Lookup Метод Lookup не перемещает курсор в соответствующую строку, а только возвращает её значение. Lookup возвращает массив переменных, содержащих значения из полей, указанных в разделённом точкой с запятой списке имён, значения которых должны быть возвращены из интересующей нас строки. Если соответствующих нашему запросу строк не найдено, то Lookup вернёт пустую (Null) переменную.

Следующий пример заполняет заполняет массив переменных LookupRes.

var LookupRes: Variant;
LookupRes := ADOTable1.Lookup
  ('Name', 'Zoom', 'Author; Description');
if not VarIsNull(LookupRes) then
 ShowMessage(VarToStr(LookupRes[0])) //имя автора
Одно из преимуществ методов Locate и Lookup, состоит в том, что они не требуют, чтобы таблица была проиндексирована. Однако, функция Locate будет работать намного быстрее, если таблица будет проиндексирована. Индексирование Индексирование помогает находить и сортировать записи намного быстрее. Вы можете создавать индексы основанные на одном поле либо на нескольких полях. Индексирование нескольких полей позволяет Вам различать записи, в которых первое поле может иметь то же самое значение. В большинстве случаев при частом поиске/сортировке желательно индексировать поля. Например, если Вы ищете определённый тип приложения в поле Type, то Вы можете создать индекс на это поле для ускорения поиска по типу. Следует упомянуть, что первичный ключ таблицы автоматически проиндексирован, а так же Вы не можете индексировать поля с типом данных OLE Object. И ещё, обратите внимание, что если многие из значений в поле те же самые, то индексирование в данном случае не ускорит процесс получения данных из БД. BDE (не ADO) Delphi предоставляет нам определённые функции для работы с таблицами базы данных, которые позволяют нам производить поиск необходимых значений. Вот некоторые из них Goto, GoToKey, GoToNearest, Find, FindKey, Find Nearest, и т.д. Для более полной справки по этим методам, Вам следует посмотреть в справке Delphi, в разделе: Searching for records based on indexed fields. ADO напротив не поддерживает эти методы. Вместо этого он представляет метод Seek. Seek В ADO метод Seek использует индекс для поиска данных. Наример, при поиске в базе данных Access, если не задать индекс, то базSеек используется для поиска записей с указанным значением (или значениями) в поле (либо полях) на которых основан текущий индекс. Если Seek не находит желаемую строку, то никакой ошибки не выдаётся, а курсор устанавливается в конец данных. Seek возвращает значение boolean, указывающее на успешность поиска: True если запись была найдена либо False если записей удовлетворяющих нашим требований не было найдено.

Метод GetIndexNames в компоненте TADOTable возвращает список (например: ячеек combo box) доступных индексов для таблицы.

ADOTable1.GetIndexNames(ComboBox1.Items);
Этот же список доступен в режиме разработки в свойстве IndexName компонента TADOTable. Свойство IndexFieldNames может использоваться как альтернативный метод для определения индекса используемого в таблице. В IndexFieldNames, мы указываем имя каждого поля для использования в таблице.

Метод Seek имеет следующее определение:

function Seek(const KeyValues: Variant; SeekOption:
 TSeekOption = soFirstEQ): Boolean; 
KeyValues массив значений Variant. Так как индекс состоит из одного или более столбцов, то массив содержит значения, которые будут сравниваться с соответствующими столбцами. SeekOption указывает на тип сравнивания между колонками индекса и соответствующим KeyValues.

SeekOption - Назначение.

soFirstEQ Указатель на запись позиционируется в первую удовлетворяющую требованиям запись, если она найдена, либо в конец таблицы, если не найдена soLastEQ Указатель на запись позиционируется на последнюю удовлетворяющую требованиям запись если она найдена, либо в конец таблицы если нет. soAfterEQ Указатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо сразу после той, которая была найдена. soAfter Указатель на запись позиционируется сразу после той, которая была найдена. soBeforeEQ Указатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо перед той, которая была найдена. soBefore Указатель на запись позиционируется перед той записью, которая была найдена. Примечание 1: метод Seek поддерживает курсоры только на стороне сервера (server-side). Seek не будет работать, если значение свойства CursorLocation равно clUseClient. Для этого используется метод Supports для определения основного провайдера, поддерживающего Seek. Примечание 2: когда Вы используйте метод Seek для нескольких полей, то Seek поля должны быть в том же самом порядке как поля в основной таблице. Если это не так, то метод Seek выдаст ошибку. Примечание 3: Вы не сможете использовать метод Seek в компоненте TADOQuery.

Чтобы определять, была ли соответствующая запись найдена, мы используем свойства BOF или EOF (в зависимости от направления поиска). Следующий код использует индекс, указанный в ComboBox, чтобы найти значение, содержащееся в окне редактирования Edit1.

var strIndex: string;
strIndex := ComboBox1.Text; //из примера выше
if ADOTable1.Supports(coSeek) then begin
 with ADOTable1 do begin
   Close;
   IndexName := strIndex;
   CursorLocation := clUseServer;
   Open;
   Seek (Edit1.Text, soFirstEQ);
  end;
  if ADOTable1.EOF then
   ShowMessage ('Record value NOT found');
end;

Поиск записи в больших таблицах.

Автор: Александр Куприн

Прочитал Ваши "Советы по Delphi" - спасибо большое: понравилось и, главное, помогло - никак не получалось нарисовать круглую форму. В свою очередь хочу предложить на Ваш суд небольшую процедуру, которая мне очень помогла. Процедура позволяет переходить на любую из записей в таблице (формат Paradox или DBase). Необходимость в ней возникла, когда мне пришлось работать с таблицей размером в 10 и более тысяч записей у которой было несколько калькулируемых полей и полей подлинкованных из объектов TQuery. При использовании метода TTable.MoveBy программа медленно и печально замолкала (вообще-то она работала, но как?!). Встретил я этот пример в технической документации Borland (2656), где сравнивались функции Paradox Engine и BDE. Пример был написан на C. Вот его интерпретация на Delphi:

uses BDE;
...
procedure MoveToRec(RecNo: longint; taSingle: TDBDataSet);
// переход на логическую запись
var
  ErrorCode: DBIResult;
  CursorProps: CurProps;
begin
  ErrorCode := DbiGetCursorProps(taSingle.Handle, CursorProps);
  if ErrorCode = DBIERR_NONE then
  begin
    case TTable(taSingle).TableType of
      ttParadox: ErrorCode := DbiSetToSeqNo(taSingle.Handle, RecNo);
      ttDBase: ErrorCode := DbiSetToRecordNo(taSingle.Handle, RecNo);
    end; { case..}
    taSingle.Resync([rmCenter]);
  end { if..}
end; { procedure MoveToRec }

Поиск в базе данных.

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

- Я спросил y Рэмблера: "где моя любимая?" - Ты б еще у Яндекса, идиот, спросил!

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

В данной статье будут рассмотрены различные способы поиска данных разработке ADO-приложений в Delphi.

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

Давайте рассмотрим несколько способов расположения данных, полученных из БД посредствам компонента ADODataset (для Таблицы и для Запроса).

Locate

Этот универсальный метод поиска устанавливает текущую запись как первую строку, удовлетворяющую набору критериев поиска. Используя метод Locate мы можем искать значения одного или более полей, расположенных в массиве переменных. В приведённом ниже коде, метод Locate ищет первую запись, содержащую строку 'Zoom' в поле 'Name'. Если вызов Locate возвращает True - то запись найдена и установлена как текущая.

AdoTable1.Locate('Name','Zoom',[]);
{...или...}
var
  ffield, fvalue: string;
  opts : TLocateOptions;
ffield := 'Name';
fvalue := 'zoom';
opts := [loCaseInsensitive];
if not AdoTable1.Locate(ffield, fvalue, opts) then
  ShowMessage(fvalue + ' not found in ' + ffield);

Lookup

Метод Lookup не перемещает курсор в соответствующую строку, а только возвращает её значение. Lookup возвращает массив переменных, содержащих значения из полей, указанных в разделённом точкой с запятой списке имён, значения которых должны быть возвращены из интересующей нас строки. Если соответствующих нашему запросу строк не найдено, то Lookup вернёт пустую (Null) переменную.

Следующий пример заполняет заполняет массив переменных LookupRes.

var
  LookupRes: Variant;
LookupRes := ADOTable1.Lookup
('Name', 'Zoom', 'Author; Description');
if not VarIsNull(LookupRes) then
  ShowMessage(VarToStr(LookupRes[0])) //имя автора

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

Индексирование

Индексирование помогает находить и сортировать записи намного быстрее. Вы можете создавать индексы основанные на одном поле либо на нескольких полях. Индексирование нескольких полей позволяет Вам различать записи, в которых первое поле может иметь то же самое значение. В большинстве случаев при частом поиске/сортировке желательно индексировать поля. Например, если Вы ищете определённый тип приложения в поле Type, то Вы можете создать индекс на это поле для ускорения поиска по типу. Следует упомянуть, что первичный ключ таблицы автоматически проиндексирован, а так же Вы не можете индексировать поля с типом данных OLE Object. И ещё, обратите внимание, что если многие из значений в поле те же самые, то индексирование в данном случае не ускорит процесс получения данных из БД.

BDE (не ADO) Delphi предоставляет нам определённые функции для работы с таблицами базы данных, которые позволяют нам производить поиск необходимых значений. Вот некоторые из них Goto, GoToKey, GoToNearest, Find, FindKey, Find Nearest, и т.д. Для более полной справки по этим методам, Вам следует посмотреть в справке Delphi, в разделе: Searching for records based on indexed fields. ADO напротив не поддерживает эти методы. Вместо этого он представляет метод Seek.

Seek

В ADO метод Seek использует индекс для поиска данных. Наример, при поиске в базе данных Access, если не задать индекс, то база данных будет использовать Первичный индексный ключ.

Seek используется для поиска записей с указанным значением (или значениями) в поле (либо полях) на которых основан текущий индекс. Если Seek не находит желаемую строку, то никакой ошибки не выдаётся, а курсор устанавливается в конец данных. Seek возвращает значение boolean, указывающее на успешность поиска: True если запись была найдена либо False если записей удовлетворяющих нашим требований не было найдено.

Метод GetIndexNames в компоненте TADOTable возвращает список (например: ячеек combo box) доступных индексов для таблицы.

ADOTable1.GetIndexNames(ComboBox1.Items);

Этот же список доступен в режиме разработки в свойстве IndexName компонента TADOTable. Свойство IndexFieldNames может использоваться как альтернативный метод для определения индекса используемого в таблице. В IndexFieldNames, мы указываем имя каждого поля для использования в таблице.

Метод Seek имеет следующее определение:

function Seek(const KeyValues: Variant;
SeekOption: TSeekOption = soFirstEQ): Boolean;
KeyValues массив значений Variant. Так как индекс состоит из одного или более столбцов, то массив содержит значения, которые будут сравниваться с соответствующими столбцами. SeekOption указывает на тип сравнивания между колонками индекса и соответствующим KeyValues.

Значения SeekOption:

sоFirstЕQУказатель на запись позиционируется в первую удовлетворяющую требованиям запись, если она найдена, либо в конец таблицы, если не найдена sоLаstЕQУказатель на запись позиционируется на последнюю удовлетворяющую требованиям запись если она найдена, либо в конец таблицы если нет. sоАftеrЕQУказатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо сразу после той, которая была найдена. sоАftеrУказатель на запись позиционируется сразу после той, которая была найдена. sоВеfоrеЕQУказатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо перед той, которая была найдена. sоВеfоrеУказатель на запись позиционируется перед той записью, которая была найдена.

Примечание 1: метод Seek поддерживает курсоры только на стороне сервера (server-side). Seek не будет работать, если значение свойства CursorLocation равно clUseClient. Для этого используется метод Supports для определения основного провайдера, поддерживающего Seek.

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

Примечание 3: Вы не сможете использовать метод Seek в компоненте TADOQuery.

Чтобы определять, была ли соответствующая запись найдена, мы используем свойства BOF или EOF (в зависимости от направления поиска). Следующий код использует индекс, указанный в ComboBox, чтобы найти значение, содержащееся в окне редактирования Edit1.

var
  strIndex: string;
strIndex := ComboBox1.Text; //из примера выше
if ADOTable1.Supports(coSeek) then
begin
  with ADOTable1 do
  begin
    Close;
    IndexName := strIndex;
    CursorLocation := clUseServer;
    Open;
    Seek (Edit1.Text, soFirstEQ);
  end;
  if ADOTable1.EOF then
    ShowMessage ('Record value NOT found');
end;

Поиск в DBGride без ввода искомого значения в поле ввода.

Твой Windows свопиться украдкой На твой заполненый хард драйв В него тихонько кинь гранаткой Чтоб больше места он не взял.

Можно обрабатывать событие OnKeyPress компонента DBGrid:

procedure TSubscriber_frm.RxDBGrid1KeyPress(Sender: TObject;
          var Key: Char);
begin
  // если набор номера (численного значения)
  if ((Ord(key)>=48) and (Ord(key)or ((Ord(key)>=96) and (Ord(key)then
  begin
    edit1.Visible:=True;
    edit1.SetFocus;
    edit1.Text:=key;
    edit1.SelStart:=1;
  end;
  // если набор текста
  if (Ord(key)>=192) and (Ord(key)then
  begin
    edit2.Visible:=True;
    edit2.SetFocus;
    edit2.Text:=key;
    edit2.SelStart:=1;
  end;
end;

А в обработке события OnChange edit1 и edit2 применить ф-цию FindNearest по нужному полю. Прятать edit-ы , после удачного поиска, либо по нажатии пользователем стрелки, Enter-a и т.п.

Простейший сканер диска.

Первое правило сисадина: "Лежаший на столе винчестер с данными выглядит точно так же как и винчестер без данных".

Вот пример, который ищет мпЗ файлы на жестком диске...

unit Audit1;
interface
uses windos;
var
  dest: string;
procedure dorecurse(dir: string);
implementation
{$R *.DFM}
procedure Process(dir: string; Searchrec: tsearchrec);
begin
  showmessage(Searchrec.name);
  case Searchrec.attr of
    $10:
      if (searchrec.name <> '.') and (searchrec.name <> '..') then
      begin
        dorecurse(dir + '\' + searchrec.name);
        writeln(dir);
      end;
  end;
end;
procedure Dorecurse(dir: string);
var
  Searchrec: Tsearchrec;
  pc: array[0..79] of Char;
begin
  StrPCopy(pc, dir + '\*.mp3');
  FindFirst(pc, FaAnyfile, SearchRec);
  Process(dir, SearchRec);
  while FindNext(SearchRec) <> -18 do
  begin
    Process(dir, SearchRec);
  end;
end;
procedure startsearch;
begin
  dorecurse(paramstr(1));
end;
begin
  startsearch;
end.

Поиск кратчайшего пути.

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

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

procedure TForm1.Button1Click(Sender: TObject);
const
  N = 10; { кол-во вершин графа}
var
  map: array[1..N, 1..N] of integer;
  // Карта.map[i,j] не 0,если
  // точки i и j соединены
  road: array[1..N] of integer;
  // Дорога — номера точек карты
  incl: array[1..N] of boolean; // incl[1]равен TRUE,если точка
  // с номером i включена в road
  start, finish: integer;
  // Начальная и конечная точки
  found: boolean; len: integer; // длина найденного (минимального) маршрута
  c_len: integer; // длина текущего (формируемого) маршрута
  i, j: integer;
  // выбор очередной точки
  procedure step(s, f, p: integer);
  var
    с: integer; { Номер точки, в которую делаем очередной шаг }
    i: integer;
  begin
    if s = f then
    begin
      len := c_len; { сохраним длину найденного маршрута }
      { вывод найденного маршрута }
      for i := 1 to p - 1 do
        Label1.caption := Label1.caption + ' ' + IntToStr(road[i]);
      Label1.caption := Label1.caption
        + ', длина:' + IntToStr(len) + #13;
    end
    else
      { выбираем очередную точку }
      for c := l to N do { проверяем все вершины }
        if (map[s, c] <> 0) and (not incite])
          and ((len = 0) or (c_len + map[s, c] < len)) then
        begin
          // точка соединена с текущей, но не включена в маршрут
          roadtp] := c; { добавим вершину в путь }
          incl[c] := TRUE; { пометим вершину как включенную }
          c_len := c_len + map[s, с];
          step(c, f, p + l);
          incite := FALSE;
          roadtp := 0;
          c_len := c_len - map[s, с];
        end;
  end;
  { конец процедуры step }
begin
  Labell.caption := '';
  { инициализация массивов }
  for i: = 1 to N do
    road[i]: = 0;
  for i := l to N do
    incl[i] := FALSE;
  { ввод описания карты из SrtingGrid.Cells}
  for i := l to N do
    for j := 1 to N do
      if StringGridl.Cells[i, j] <> '' then
        mapti, j] := StrToInt(StringGridl.Cells[i, j])
      else
        mapti, j] := 0;
  len := 0; // длина найденного (минимального) маршрута
  len := 0, // длина текущего (формируемого) маршрута
  start := StrToInt(Edit1.text);
  finish := StrToInt(Edit2.text);
  road[1] := start; { внесем точку в маршрут }
  incl[start] := TRUE; { пометим ее как включенную }
  step(start, finish, 2); {ищем вторую точку маршрута }
  // проверим, найден ли хотя бы один путь
  if not found then
    Label1.caption := 'Указанные точки не соединены!';
end;

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

Поиск пути.

Статьи по Delphi (015)…
unit road_;
interface
uses
  Windows, Messages, SysUtils, Classes,
  Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids;
type
  TForml = class(TForm)
    StringGridl: TStringGrid;
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Button1: TButton;
    Label4: TLabel;
    procedure FormActivate(Sender: TObject);
    procedure ButtonlClickfSender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForml.FormActivate(Sender: TObject);
var
  i: integer;
begin
  // нумерация строк
  for i := 1 to 10 do
    StringGridl.Cells[0, i] := IntToStr(i); // нумерация колонок
  for i := l to 10 do
    StringGridl.Cells[1, 0] := IntToStr(i);
  // описание предопределенной карты
  StringGridl.Cells[1,2]:='1'
  StringGridl.Cells[2,l]:='1'
  StringGridl.Cells[1, 3] := '1'
  StringGridl.Cells[3, 1] := '1'
  StringGridl.Cells[1, 4] := '1'
  StringGridl.Cells[4, 1] := '1'
  StringGridl.Cells[3, 7] := '1'
  StringGridl.Cells[7, 3] := '1'
  StringGridl.Cells[4, 6] := '1'
  StringGridl.Cells[6, 4] := '1'
  StringGridl.Cells[5, 6] := '1'
  StringGridl.Cells[6, 5] := '1'
  StringGridl.Cells[5, 7] := '1'
  StringGridl.Cells[7, 5] := '1'
  StringGridl.Cells[6, 7] := '1'
  StringGridl.Cells[7, 6] := '1'
end;
procedure TForml.ButtonlClick(Sender: TObject);
const
  N = 10; // кол-во вершин графа var
  map: array[1..N, 1..N] of integer; // Карта.map[i,j]ne 0,
  // если точки i и j соединены
  road: array[1..N] of integer;
  // Дорога - номера точек карты
  incl: array[1..N] of boolean; // incl[1]равен TRUE, если точка
  // с номером i включена в road
  start, finish: integer; // Начальная и конечная точки
  found: boolean; i, j: integer;
  procedure step(s, f, p: integer);
  var
    с: integer; // Номер точки, в которую делаем очередной шаг
    i: integer;
  begin
    if s = f then
    begin
      // Точки s и f совпали !
      found := TRUE;
      Labell.caption := Labell.caption + #13 + 'Путь:';
      for i := l to p - 1 do
        Labell.caption := Labell.caption + ' '
          + IntToStr(road[i]);
    end
    else
    begin
      // выбираем очередную точку
      for c:=l to N do
      begin // проверяем все вершины
        // точка соединена с текущей и не включена в маршрут
        if (map[s, c] <> 0) and (not incite1) then
        begin
          road[p] := c; // добавим вершину в путь
          incl[c] := TRUE; // пометим вершину как включенную
          step(c, f, p + l); incite] := FALSE;
          road[p] := 0;
        end;
      end;
    end;
  end; // конец процедуры step
begin
  Label1.caption: = ' ';
  // инициализация массивов
  for i := l to N do
    road[i] := 0;
  for i := l to N do
    incl[i] := FALSE;
  // ввод описания карты из SrtingGrid.Cells
  for i := l to N do
    for j := 1 to N do
      if StringGrid1.Cells[i, j] <> '' then
        map[i, j] := StrToInt(StringGridl.Cells[i, j];
      else
        map[i, j] := 0;
  start := StrToInt(Editl.text);
  finish := StrToInt(Edit2.text);
  road[l] := start; // внесем точку в маршрут
  incl[start] := TRUE; // пометим ее как включенную
  step(start, finish, 2); //ищем вторую точку маршрута
  // проверим, найден ли хотя бы один путь
  if not found then
    Labell.caption := 'Указанные точки не соединены!';
end;
end.
При запуске программы в момент активизации формы приложения происходит событие onActivate, процедура обработки которого заполняет массив StringGridl.cells значениями, представляющими описание карты. Этаже процедура нумерует строки и столбцы таблицы, заполняя зафиксированные ячейки первого столбца и первой строки StringGridl.

Поиск маршрута инициирует процедура TFormi.Buttoniciick, которая запускается щелчком на кнопке Поиск. Данная процедура для поиска точки, соединенной с исходной точкой, вызывает процедуру step, которая после выбора первой точки, соединенной с начальной, и включения ее в маршрут вызывает сама себя. При этом в качестве начальной точки задается уже не исходная, а текущая, только что включенная в маршрут.

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

Автор: Xavier Pacheco

Статьи по Delphi (015)…
unit Main;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, Menus, SrchIni,
  SrchU, ComCtrls, AppEvnts;
type
  TMainForm = class(TForm)
    lbFiles: TListBox;
    StatusBar: TStatusBar;
    pnlControls: TPanel;
    PopupMenu: TPopupMenu;
    FontDialog: TFontDialog;
    pnlOptions: TPanel;
    gbParams: TGroupBox;
    LFileSpec: TLabel;
    LToken: TLabel;
    lPathName: TLabel;
    edtFileSpec: TEdit;
    edtToken: TEdit;
    btnPath: TButton;
    edtPathName: TEdit;
    gbOptions: TGroupBox;
    cbCaseSensitive: TCheckBox;
    cbFileNamesOnly: TCheckBox;
    cbRecurse: TCheckBox;
    cbRunFromAss: TCheckBox;
    pnlButtons: TPanel;
    btnSearch: TBitBtn;
    btnClose: TBitBtn;
    btnPrint: TBitBtn;
    btnPriority: TBitBtn;
    Font1: TMenuItem;
    Clear1: TMenuItem;
    Print1: TMenuItem;
    N1: TMenuItem;
    Exit1: TMenuItem;
    ApplicationEvents: TApplicationEvents;
    procedure btnSearchClick(Sender: TObject);
    procedure btnPathClick(Sender: TObject);
    procedure lbFilesDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure Font1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnPrintClick(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure lbFilesDblClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure btnPriorityClick(Sender: TObject);
    procedure edtTokenChange(Sender: TObject);
    procedure Clear1Click(Sender: TObject);
    procedure ApplicationEventsHint(Sender: TObject);
  private
    procedure ReadIni;
    procedure WriteIni;
  public
    Running: Boolean;
    SearchPri: Integer;
    SearchThread: TSearchThread;
    procedure EnableSearchControls(Enable: Boolean);
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
uses Printers, ShellAPI, StrUtils, FileCtrl, PriU;
procedure PrintStrings(Strings: TStrings);
{ This procedure prints all of the strings in the Strings parameter }
var
  Prn: TextFile;
  I: Integer;
begin
  if Strings.Count = 0 then // Are there strings?
    raise Exception.Create('No text to print!');
  AssignPrn(Prn); // assign Prn to printer
  try
    Rewrite(Prn); // open printer
    try
      for I := 0 to Strings.Count - 1 do // iterate over all strings
        WriteLn(Prn, Strings.Strings[I]); // write to printer
    finally
      CloseFile(Prn); // close printer
    end;
  except
    on EInOutError do
      MessageDlg('Error Printing text.', mtError, [mbOk], 0);
  end;
end;
procedure TMainForm.EnableSearchControls(Enable: Boolean);
{ Enables or disables certain controls so options can't be modified }
{ while search is executing. }
begin
  btnSearch.Enabled := Enable; // enable/disable proper controls
  cbRecurse.Enabled := Enable;
  cbFileNamesOnly.Enabled := Enable;
  cbCaseSensitive.Enabled := Enable;
  btnPath.Enabled := Enable;
  edtPathName.Enabled := Enable;
  edtFileSpec.Enabled := Enable;
  edtToken.Enabled := Enable;
  Running := not Enable; // set Running flag
  edtTokenChange(nil);
  with btnClose do
  begin
    if Enable then
    begin // set props of Close/Stop button
      Caption := '&Close';
      Hint := 'Close Application';
    end
    else
    begin
      Caption := '&Stop';
      Hint := 'Stop Searching';
    end;
  end;
end;
procedure TMainForm.btnSearchClick(Sender: TObject);
{ Called when Search button is clicked.  Invokes search thread. }
begin
  EnableSearchControls(False); // disable controls
  lbFiles.Clear; // clear listbox
  { start thread }
  SearchThread := TSearchThread.Create(cbCaseSensitive.Checked,
    cbFileNamesOnly.Checked, cbRecurse.Checked, edtToken.Text,
    edtPathName.Text, edtFileSpec.Text);
end;
procedure TMainForm.edtTokenChange(Sender: TObject);
begin
  btnSearch.Enabled := not Running and (edtToken.Text <> '');
end;
procedure TMainForm.btnPathClick(Sender: TObject);
{ Called when Path button is clicked.  Allows user to choose new path. }
var
  ShowDir: string;
begin
  ShowDir := edtPathName.Text;
  if SelectDirectory('Choose a search path...', '', ShowDir) then
    edtPathName.Text := ShowDir;
end;
procedure TMainForm.lbFilesDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
{ Called in order to owner draw listbox. }
var
  CurStr: string;
begin
  with lbFiles do
  begin
    CurStr := Items.Strings[Index];
    Canvas.FillRect(Rect); // clear out rect
    if not cbFileNamesOnly.Checked then // if not filename only...
      { if current line is filename... }
      if (Pos('File ', CurStr) = 1) and
        (CurStr[Length(CurStr)] = ':') then
        with Canvas.Font do
        begin
          Style := [fsUnderline]; // underline font
          Color := clRed; // paint red
        end
      else
        Rect.Left := Rect.Left + 15; // otherwise, indent
    DrawText(Canvas.Handle, PChar(CurStr), Length(CurStr), Rect,
      DT_SINGLELINE);
  end;
end;
procedure TMainForm.Font1Click(Sender: TObject);
{ Allows user to pick new font for listbox }
begin
  { Pick new listbox font }
  if FontDialog.Execute then
    lbFiles.Font := FontDialog.Font;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
{ OnDestroy event handler for form }
begin
  WriteIni;
end;
procedure TMainForm.FormCreate(Sender: TObject);
{ OnCreate event handler for form }
begin
  ReadIni; // read INI file
end;
procedure TMainForm.btnPrintClick(Sender: TObject);
{ Called when Print button is clicked. }
begin
  if MessageDlg('Send search results to printer?', mtConfirmation,
    [mbYes, mbNo], 0) = mrYes then
    PrintStrings(lbFiles.Items);
end;
procedure TMainForm.btnCloseClick(Sender: TObject);
{ Called to stop thread or close application }
begin
  // if thread is running then terminate thread
  if Running then
    SearchThread.Terminate
      // otherwise close app
  else
    Close;
end;
procedure TMainForm.lbFilesDblClick(Sender: TObject);
{ Called when user double-clicks in listbox. Invokes viewer for }
{ highlighted file. }
var
  ProgramStr, FileStr: string;
  RetVal: THandle;
begin
  { if user clicked on a file.. }
  if (Pos('File ', lbFiles.Items[lbFiles.ItemIndex]) = 1) then
  begin
    { load text editor from INI file.  Notepad is default. }
    ProgramStr := SrchIniFile.ReadString('Defaults', 'Editor', 'notepad');
    FileStr := lbFiles.Items[lbFiles.ItemIndex]; // Get selected file
    FileStr := Copy(FileStr, 6, Length(FileStr) - 5); // Remove prefix
    if FileStr[Length(FileStr)] = ':' then // Remove ":"
      DecStrLen(FileStr, 1);
    if cbRunFromAss.Checked then
      { Run file from shell association }
      RetVal := ShellExecute(Handle, 'open', PChar(FileStr), nil, nil,
        SW_SHOWNORMAL)
    else
      { View file using text editor }
      RetVal := ShellExecute(Handle, 'open', PChar(ProgramStr),
        PChar(FileStr), nil, SW_SHOWNORMAL);
    { Check for error }
    if RetVal < 32 then
      RaiseLastWin32Error;
  end;
end;
procedure TMainForm.FormResize(Sender: TObject);
{ OnResize event handler. Centers controls in form. }
begin
  { divide status bar into two panels with a 1/3 - 2/3 split }
  with StatusBar do
  begin
    Panels[0].Width := Width div 3;
    Panels[1].Width := Width * 2 div 3;
  end;
end;
procedure TMainForm.btnPriorityClick(Sender: TObject);
{ Show thread priority form }
begin
  ThreadPriWin.Show;
end;
procedure TMainForm.ReadIni;
{ Reads default values from Registry }
begin
  with SrchIniFile do
  begin
    edtPathName.Text := ReadString('Defaults', 'LastPath', 'C:\');
    edtFileSpec.Text := ReadString('Defaults', 'LastFileSpec', '*.*');
    edtToken.Text := ReadString('Defaults', 'LastToken', '');
    cbFileNamesOnly.Checked := ReadBool('Defaults', 'FNamesOnly', False);
    cbCaseSensitive.Checked := ReadBool('Defaults', 'CaseSens', False);
    cbRecurse.Checked := ReadBool('Defaults', 'Recurse', False);
    cbRunFromAss.Checked := ReadBool('Defaults', 'RunFromAss', False);
    Left := ReadInteger('Position', 'Left', Left);
    Top := ReadInteger('Position', 'Top', Top);
    Width := ReadInteger('Position', 'Width', Width);
    Height := ReadInteger('Position', 'Height', Height);
  end;
end;
procedure TMainForm.WriteIni;
{ writes current settings back to Registry }
begin
  with SrchIniFile do
  begin
    WriteString('Defaults', 'LastPath', edtPathName.Text);
    WriteString('Defaults', 'LastFileSpec', edtFileSpec.Text);
    WriteString('Defaults', 'LastToken', edtToken.Text);
    WriteBool('Defaults', 'CaseSens', cbCaseSensitive.Checked);
    WriteBool('Defaults', 'FNamesOnly', cbFileNamesOnly.Checked);
    WriteBool('Defaults', 'Recurse', cbRecurse.Checked);
    WriteBool('Defaults', 'RunFromAss', cbRunFromAss.Checked);
    WriteInteger('Position', 'Left', Left);
    WriteInteger('Position', 'Top', Top);
    WriteInteger('Position', 'Width', Width);
    WriteInteger('Position', 'Height', Height);
  end;
end;
procedure TMainForm.Clear1Click(Sender: TObject);
begin
  lbFiles.Items.Clear;
end;
procedure TMainForm.ApplicationEventsHint(Sender: TObject);
{ OnHint event handler for Application }
begin
  { Display application hints on status bar }
  StatusBar.Panels[0].Text := Application.Hint;
end;
end.
Скачать весь проект

Поиск свойств.

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

Хороший материал по этому вопросу вы найдете в typinfo.pas (каталог Docs); нижеследующий код заполнит компонент Memo именами свойств:

Uses TypInfo;
procedure TForm1.Button1Click(Sender: TObject);
var
  PropList: pPropList;
  J, I: Integer;
begin
  Memo1.Lines.Clear;
  getMem(PropList, sizeof(tPropList));
  J := GetPropList(TypeInfo(tButton), tkProperties, PropList);
  for I := 0 to J - 1 do
    Memo1.Lines.Add(PropList^[I]^.Name);
  FreeMem(PropList, sizeof(tPropList));
end;

Поиск строки текста в наследниках TCustomEdit.

Автор: Aleksey

Пришло мне письмо от Алексея. На этот раз он прислал (цитирую): "юнит для поиска строки(текста) в TEdit, TMemo, или других компонентах (дочерних ТСustоmЕdit'у)." Так как тескт "авторский" (более того, здесь также присутствует наследование), помещаю его здесь в том виде, в котором он был прислан, т.е. без перевода. В случае каких-либо вопросов и недоразумений обращайтесь по вышеуказанносу адресу электронной почты.

{ПРИМЕР :
[...]
implementation
uses Search;}
{$R *.DFM}
{procedure TForm1.Button1Click(Sender: TObject);
begin
SearchMemo(RichEdit1, 'Найди меня', [frDown]);
end;
В опции поиска можно подключать, отключать, комбинировать следующие
параметры:
frDown - указывает на то, что происходит поиск вниз по тексту от курсора(при
отключенном frDоwn'е будет происходит поиск вверх по тексту).
frMatchCase - указывает на то, что следует проводить поиск с учетом
регистра.
frWholeWord - указывает на то, что следует искать только слово целиком.
[...]
Авторские права на этот юнит пренадлежат неизвесно кому.
В каком виде этот юнит попал мне, практически в этом же
виде я отдаю его вам. Пользуйтесь и благодарите неизвесного
героя.}
unit Search;
interface
uses
  WinProcs, SysUtils, StdCtrls, Dialogs;
const
  {****************************************************************************
  * Default word delimiters are any character except the core alphanumerics. *
  ****************************************************************************}
  WordDelimiters: set of Char = [#0..#255] - ['a'..'z', 'A'..'Z', '1'..'9',
    '0'];
  {******************************************************************************
  * SearchMemo scans the text of a TEdit, TMemo, or other TCustomEdit-derived  *
  * component for a given search string. The search starts at the current      *
  * caret position in the control.  The Options parameter determines whether   *
  * the search runs forward (frDown) or backward from the caret position,      *
  * whether or not the text comparison is case sensitive, and whether the      *
  * matching string must be a whole word.  If text is already selected in the  *
  * control, the search starts at the 'far end' of the selection (SelStart if  *
  * searching backwards, SelEnd if searching forwards).  If a match is found,  *
  * the control's text selection is changed to select the found text and the   *
  * function returns True.  If no match is found, the function returns False.  *
  ******************************************************************************}
function SearchMemo(Memo: TCustomEdit;
  const SearchString: string;
  Options: TFindOptions): Boolean;
{******************************************************************************
* SearchBuf is a lower-level search routine for arbitrary text buffers.      *
* Same rules as SearchMemo above. If a match is found, the function returns  *
* a pointer to the start of the matching string in the buffer. If no match,  *
* the function returns nil.                                                  *
******************************************************************************}
function SearchBuf(Buf: PChar; BufLen: Integer;
  SelStart, SelLength: Integer;
  SearchString: string;
  Options: TFindOptions): PChar;
implementation
function SearchMemo(Memo: TCustomEdit;
  const SearchString: string;
  Options: TFindOptions): Boolean;
var
  Buffer, P: PChar;
  Size: Word;
begin
  Result := False;
  if (Length(SearchString) = 0) then
    Exit;
  Size := Memo.GetTextLen;
  if Size = 0 then
    Exit;
  Buffer := StrAlloc(Size + 1);
  try
    Memo.GetTextBuf(Buffer, Size + 1);
    P := SearchBuf(Buffer, Size, Memo.SelStart, Memo.SelLength, SearchString,
      Options);
    if P <> nil then
    begin
      Memo.SelStart := P - Buffer;
      Memo.SelLength := Length(SearchString);
      Result := True;
    end;
  finally
    StrDispose(Buffer);
  end;
end;
function SearchBuf(Buf: PChar; BufLen: Integer;
  SelStart, SelLength: Integer;
  SearchString: string;
  Options: TFindOptions): PChar;
var
  SearchCount, I: Integer;
  C: Char;
  Direction: Shortint;
  CharMap: array[Char] of Char;
  function FindNextWordStart(var BufPtr: PChar): Boolean;
  begin { (True XOR N) is equivalent to (not N) }
    //    Result := False;      { (False XOR N) is equivalent to (N)    }
    { When Direction is forward (1), skip non delimiters, then skip delimiters. }
    { When Direction is backward (-1), skip delims, then skip non delims }
    while (SearchCount > 0) and
      ((Direction = 1) xor
      (BufPtr^ in WordDelimiters)) do
    begin
      Inc(BufPtr, Direction);
      Dec(SearchCount);
    end;
    while (SearchCount > 0) and
      ((Direction = -1) xor
      (BufPtr^ in WordDelimiters)) do
    begin
      Inc(BufPtr, Direction);
      Dec(SearchCount);
    end;
    Result := SearchCount > 0;
    if Direction = -1 then
    begin {back up one char, to leave ptr on first non delim}
      Dec(BufPtr, Direction);
      Inc(SearchCount);
    end;
  end;
begin
  Result := nil;
  if BufLen <= 0 then
    Exit;
  if frDown in Options then
  begin {if frDown...}
    Direction := 1;
    Inc(SelStart, SelLength); { start search past end of selection }
    SearchCount := BufLen - SelStart - Length(SearchString);
    if SearchCount < 0 then
      Exit;
    if Longint(SelStart) + SearchCount > BufLen then
      Exit;
  end {if frDown...}
  else
  begin {else}
    Direction := -1;
    Dec(SelStart, Length(SearchString));
    SearchCount := SelStart;
  end; {else}
  if (SelStart < 0) or (SelStart > BufLen) then
    Exit;
  Result := @Buf[SelStart];
  { Using a Char map array is faster than calling AnsiUpper on every character }
  for C := Low(CharMap) to High(CharMap) do
    CharMap[C] := C;
  if not (frMatchCase in Options) then
  begin {if not (frMatchCase}
    AnsiUpperBuff(PChar(@CharMap), sizeof(CharMap));
    AnsiUpperBuff(@SearchString[1], Length(SearchString));
  end; {if not (frMatchCase}
  while SearchCount > 0 do
  begin {while SearchCount}
    if frWholeWord in Options then
    begin
      if not FindNextWordStart(Result) then
        Break;
    end;
    I := 0;
    while (CharMap[Result[I]] = SearchString[I + 1]) do
    begin {while (CharMap...}
      Inc(I);
      if I >= Length(SearchString) then
      begin {if I >=...}
        if (not (frWholeWord in Options)) or
          (SearchCount = 0) or
          (Result[I] in WordDelimiters) then
          Exit;
        Break;
      end; {if I >=...}
    end; {while (CharMap...}
    Inc(Result, Direction);
    Dec(SearchCount);
  end; {while SearchCount}
  Result := nil;
end;
end.

Алгоритмы поиска в тексте.

Оформил: DeeCo

Автор: Андрей Боровский.

Наверное, каждому, кто много работает за компьютером, знакома подобная ситуация: перелистывая страницы книги в поисках нужного фрагмента, невольно начинаешь думать о том, как вызвать команду «поиск по всему тексту». Действительно, современные программы обработки текста приучили нас к такой удобной возможности, как поиск и замена фрагментов, и если вы разрабатываете подобную программу, пользователь вправе ожидать, что вы предоставите в его распоряжение соответствующие команды. Эту проблему нельзя эффективно решить при помощи стандартных функций Delphi/Kylix, поскольку большинство средств разработки включает только малоэффективные средства. Во-первых, в стандартных функциях не всегда используются самые эффективные алгоритмы, а во-вторых, вполне возможно, что вам понадобится изменить стандартное поведение этих функций (например, предусмотреть возможность поиска по шаблону).

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

Алгоритм грубой силы и простой вариант алгоритма Бойера-Мура.

Прежде, чем приступить к рассмотрению алгоритма Бойера-Мура, приведем простейший (и самый медленный) алгоритм поиска, называемый «методом грубой силы».

function Find(const S, P: string): Integer;
var
  i, j: Integer;
begin
  Result := 0;
  if Length(P) > Length(S) then
    Exit;
  for i := 1 to Length(S) - Length(P) + 1 do
    for j := 1 to Length(P) do
      if P[j] <> S[i + j - 1] then
        Break
      else if j = Length(P) then
      begin
        Result := i;
        Exit;
      end;
end;

Функция Find ищет подстроку P в строке S и возвращает индекс первого символа подстроки или 0, если подстрока не найдена. Хотя в общем случае этот метод, как и большинство методов грубой силы, малоэффективен, в некоторых ситуациях он вполне приемлем. Мы сами воспользуемся похожим алгоритмом при построении таблицы смещений для метода Бойера-Мура.

Алгоритм Бойера-Мура, разработанный двумя учеными – Бойером (Robert S. Boyer) и Муром (J. Strother Moore), считается наиболее быстрым среди алгоритмов общего назначения, предназначенных для поиска подстроки в строке. Прежде чем рассмотреть работу этого алгоритма, уточним некоторые термины. Под строкой мы будем понимать всю последовательность символов текста. Собственно говоря, речь не обязательно должна идти именно о тексте. В общем случае строка – это любая последовательность байтов. Поиск подстроки в строке осуществляется по заданному образцу, т. е. некоторой последовательности байтов, длина которой не превышает длину строки. Наша задача заключается в том, чтобы определить, содержит ли строка заданный образец.

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

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

Величина смещения для каждого символа образца зависит только от порядка символов в образце, поэтому смещения удобно вычислить заранее и хранить в виде одномерного массива, где каждому символу алфавита соответствует смещение относительно последнего символа образца. Поясним все вышесказанное на простом примере. Пусть у нас есть набор символов из пяти символов: a, b, c, d, e и мы хотим найти вхождение образца “abbad” в строке “abeccacbadbabbad”. Следующие схемы иллюстрируют все этапы выполнения алгоритма:

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

Таблица смещений для образца “abbad”.

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

Начало поиска. Последний символ образца не совпадает с наложенным символом строки. Сдвигаем образец вправо на 5 позиций:

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

Три символа образца совпали, а четвертый – нет. Сдвигаем образец вправо на одну позицию:

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

Последний символ снова не совпадает с символом строки. В соответствии с таблицей смещений сдвигаем образец на 2 позиции:

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

Еще раз сдвигаем образец на 2 позиции:

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

Теперь, в соответствии с таблицей, сдвигаем образец на одну позицию, и получаем искомое вхождение образца:

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

Реализуем указанный алгоритм на языке ObjectPascal. Прежде всего следует определить тип данных «таблица смещений». Для кодовой таблицы, состоящей из 256 символов, определение этого типа будет выглядеть так:

type
  TBMTable = array[0..255] of Integer;

Далее приводится процедура, вычисляющая таблицу смещений для образца P.

procedure MakeBMTable(var BMT: TBMTable; const P: string);
var
  i: Integer;
begin
  for i := 0 to 255 do
    BMT[i] := Length(P);
  for i := Length(P) downto 1 do
    if BMT[Byte(P[i])] = Length(P) then
      BMT[Byte(P[i])] := Length(P)– i;
end;

Теперь напишем функцию, осуществляющую поиск.

function BMSearch(StartPos: Integer; const S, P: string; const BMT: TBMTable):
  Integer;
var
  Pos, lp, i: Integer;
begin
  lp := Length(P);
  Pos := StartPos + lp –1;
  while Pos do
    if P[lp] <> S[Pos] then
      Pos := Pos + BMT[S[Pos]]
    else
      for i := lp - 1 downto 1 do
        if P[i] <> S[Pos – lp + i] then
        begin
          Inc(Pos);
          Break;
        end
        else if i = 1 then
        begin
          Result := Pos – lp + 1;
          Exit;
        end;
  Result := 0;
end;

Функция BMSearch возвращает позицию первого символа первого вхождения образца P в строке S. Если последовательность P в S не найдена, функция возвращает 0 (напомню, что в ObjectPascal нумерация символов в строке String начинается с 1). Параметр StartPos позволяет указать позицию в строке S, с которой следует начинать поиск. Это может быть полезно в том случае, если вы захотите найти все вхождения P в S. Для поиска с самого начала строки следует задать StartPos равным 1. Если результат поиска не равен нулю, то для того, чтобы найти следующее вхождение P в S, нужно задать StartPos равным значению «предыдущий результат плюс длина образца».

Более эффективный вариант

Хотя рассмотренный упрощенный алгоритм вполне пригоден с практической точки зрения (и часто применяется), нельзя не заметить, что результаты сравнений используются недостаточно эффективно. Действительно, на втором шаге, когда у нас совпали три символа, мы, зная, что последовательность “bad” встречается в образце только один раз, могли бы сразу сместить образец на всю его длину, а не на один символ. Теперь мы рассмотрим другой, немного более сложный вариант алгоритма Бойера-Мура, позволяющий учесть результаты предыдущих сравнений в случае частичного совпадения образца и подстроки. Прежде всего изменим принцип построения таблицы смещений. В этом варианте алгоритма таблица – двумерная, каждому символу образца соответствует один столбец таблицы, а каждой букве алфавита – одна строка. В ячейках таблицы содержатся значения смещений, на которые нужно сдвинуть образец, если при проверке данного символа образца обнаружено несовпадение и вместо искомого символа получен символ алфавита, соответствующий некоторой строке в таблице. Например, таблица последовательности “abdab” для нашего пятибуквенного алфавита будет выглядеть следующим образом:

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

Заполнение таблицы начинается с последнего столбца. Самый правый столбец таблицы фактически соответствует массиву, который используется в упрощенном варианте алгоритма. Следующие столбцы содержат значения сдвига для образца при условии, что предыдущие (при движении справа налево) символы совпали. Проводя поиск при помощи этой таблицы, мы последовательно просматриваем значения ячеек, лежащих на пересечении столбца, соответствующего символу образца и строки, соответствующей символу текста. Просмотр начинается с последнего столбца. Если в каждом столбце выбранная ячейка содержит 0, значит, подстрока найдена. Если значение ячейки отличается от нуля, мы сдвигаем образец на соответствующее значение.

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

Далее приводятся функции и определения, реализующие алгоритм для 256-символьного набора.

type
  TIntVect = array[0..255] of Integer;
  TBMTable = array[0..0] of TIntVect;
  PBMTable = ^TBMTable;
function FindRightmost(const S, P: string; n: Integer): Integer;
var
  i, j, lp: Integer;
begin
  Result := 0;
  lp := Length(P);
  if lp > n then
    Exit;
  for i := n - lp + 1 downto 1 do
    for j := 1 to lp do
      if P[j] <> S[i + j - 1] then
        Break
      else if j = lp then
      begin
        Result := i;
        Exit;
      end;
end;
procedure MakeBMTable(var BMT: PBMTable; const P: string);
var
  i, j, lp, MaxShift, CurShift, SufPos: Integer;
  Suffix: string;
begin
  lp := Length(P);
  GetMem(BMT, SizeOf(TIntVect) * lp);
  for i := 0 to 255 do
    BMT^[lp - 1][i] := lp;
  for i := lp downto 1 do
    if BMT^[lp - 1][Byte(P[i])] = lp then
      BMT^[lp - 1][Byte(P[i])] := lp - i;
  MaxShift := lp;
  for i := lp - 1 downto 1 do
  begin
    SetLength(Suffix, lp - i);
    Move(P[i + 1], Suffix[1], lp - i);
    if Pos(Suffix, P) = 1 then
      MaxShift := i;
    for j := 0 to 255 do
    begin
      CurShift := MaxShift;
      SetLength(Suffix, lp - i + 1);
      Suffix[1] := Char(j);
      Move(P[i + 1], Suffix[2], lp - i);
      SufPos := FindRightmost(P, Suffix, lp - 1);
      if SufPos <> 0 then
        CurShift := i - SufPos;
      BMT^[i - 1][j] := CurShift;
    end;
    BMT^[i - 1][Byte(P[i])] := 0;
  end;
end;
function BMSearch(StartPos, lp: Integer; const S: string; BMT: PBMTable):
  Integer;
var
  Pos, i: Integer;
begin
  Pos := StartPos + lp - 1;
  while Pos do
    for i := lp downto 1 do
      if BMT^[i - 1][Byte(S[Pos - lp + i])] <> 0 then
      begin
        Pos := Pos + BMT^[i - 1][Byte(S[Pos - lp + i])];
        Break;
      end
      else if i = 1 then
      begin
        Result := Pos - lp + 1;
        Exit;
      end;
  Result := 0;
end;

Служебная функция FindRightmost возвращает самое последнее вхождение образца P среди n первых символов строки S. Формат вызова функции BMSearch отличается от предыдущего. В параметре lp передается длина строки образца, сама же строка не нужна, так как таблица смещений однозначно описывает образец. Следует также учесть, что функция MakeBMTable динамически выделяет память для таблицы смещений, и после окончания использования функции BMSearch эту память необходимо освободить при помощи функции FreeMem. Следующий фрагмент кода иллюстрирует поиск всех вхождений образца P в строке S.

MakeBMTable(BMT, P);
PatPos := BMSearch(1, Length(P), S, BMT);
while PatPos <> 0 dobegin...PatPos := BMSearch(PatPos + 1, Length(P), S, BMT);
end;
FreeMem(BMT);

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

function WCBeginsWith(const P, S: string): Boolean;
var
  i, lp: Integer;
begin
  Result := False;
  lp := Length(P);
  if lp > Length(S) then
    Exit;
  for i := 1 to lp do
    if (P[i] <> S[i]) and (P[i] <> '?') and (S[i] <> '?') then
      Exit;
  Result := True;
end;
function WCFindRightmost(const S, P: string; l: Integer): Integer;
var
  i, j, lp: Integer;
begin
  Result := 0;
  lp := Length(P);
  if lp > l then
    Exit;
  for i := l - lp + 1 downto 1 do
    for j := 1 to lp do
      if (P[j] <> S[i + j - 1]) and (P[j] <> '?') and (S[i + j - 1] <> '?') then
        Break
      else if j = lp then
      begin
        Result := i;
        Exit;
      end;
end;
procedure WCMakeBMTable(var BMT: PBMTable; const P: string);
var
  i, j, lp, MaxShift, CurShift, SufPos: Integer;
  Suffix: string;
begin
  lp := Length(P);
  GetMem(BMT, SizeOf(TIntVect) * lp);
  if P[lp] = '?' then
    for i := 0 to 255 do
      BMT^[lp - 1][i] := 0
  else
  begin
    for i := 0 to 255 do
      BMT^[lp - 1][i] := lp;
    for i := lp downto 1 do
      if BMT^[lp - 1][Byte(P[i])] = lp then
        BMT^[lp - 1][Byte(P[i])] := lp - i;
  end;
  MaxShift := lp;
  for i := lp - 1 downto 1 do
  begin
    SetLength(Suffix, lp - i);
    Move(P[i + 1], Suffix[1], lp - i);
    if WCBeginsWith(Suffix, P) then
      MaxShift := i;
    if P[i] = '?' then
      for j := 0 to 255 do
        BMT^[i - 1][j] := 0
    else
      for j := 0 to 255 do
      begin
        CurShift := MaxShift;
        SetLength(Suffix, lp - i + 1);
        Suffix[1] := Char(j);
        Move(P[i + 1], Suffix[2], lp - i);
        SufPos := WCFindRightmost(P, Suffix, lp - 1);
        if SufPos < >
        0 then
          CurShift := i - SufPos;
        BMT^[i - 1][j] := CurShift;
      end;
    BMT^[i - 1][Byte(P[i])] := 0;
  end;
end;

Например, если в качестве шаблона функции WCMakeBMTable передать строку «брос?ть», и передать полученную таблицу функции BMSearch, в тексте S будут найдены все вхождения слов «бросать» и «бросить» (а также «забросать», «забросить», «бросаться» и т.п., так как не указаны предваряющий и завершающий пробелы).

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

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

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

unit BMSearch;
(* -------------------------------------------------------------------
Поиск строки методом Boyer-Moore.
Это - один из самых быстрых алгоритмов поиска строки.
See a description in:
R. Boyer и S. Moore.
Быстрый алгоритм поиска строки.
Communications of the ACM 20, 1977, страницы 762-772
------------------------------------------------------------------- *)
interface
type
{$IFDEF WINDOWS}
  size_t = Word;
{$ELSE}
  size_t = LongInt;
{$ENDIF}
type
  TTranslationTable = array[char] of char; { таблица перевода }
  TSearchBM = class(TObject)
  private
    FTranslate: TTranslationTable; { таблица перевода }
    FJumpTable: array[char] of Byte; { таблица переходов }
    FShift_1: integer;
    FPattern: pchar;
    FPatternLen: size_t;
  public
    procedure Prepare(Pattern: pchar; PatternLen: size_t; IgnoreCase: Boolean);
    procedure PrepareStr(const Pattern: string; IgnoreCase: Boolean);
    function Search(Text: pchar; TextLen: size_t): pchar;
    function Pos(const S: string): integer;
  end;
implementation
uses SysUtils;
(* -------------------------------------------------------------------
Игнорируем регистр таблицы перевода
------------------------------------------------------------------- *)
procedure CreateTranslationTable(var T: TTranslationTable; IgnoreCase: Boolean);
var
  c: char;
begin
  for c := #0 to #255 do
    T[c] := c;
  if not IgnoreCase then
    exit;
  for c := 'a' to 'z' do
    T[c] := UpCase(c);
  { Связываем все нижние символы с их эквивалентом верхнего регистра }
  T['Б'] := 'A';
  T['А'] := 'A';
  T['Д'] := 'A';
  T['В'] := 'A';
  T['б'] := 'A';
  T['а'] := 'A';
  T['д'] := 'A';
  T['в'] := 'A';
  T['Й'] := 'E';
  T['И'] := 'E';
  T['Л'] := 'E';
  T['К'] := 'E';
  T['й'] := 'E';
  T['и'] := 'E';
  T['л'] := 'E';
  T['к'] := 'E';
  T['Н'] := 'I';
  T['М'] := 'I';
  T['П'] := 'I';
  T['О'] := 'I';
  T['н'] := 'I';
  T['м'] := 'I';
  T['п'] := 'I';
  T['о'] := 'I';
  T['У'] := 'O';
  T['Т'] := 'O';
  T['Ц'] := 'O';
  T['Ф'] := 'O';
  T['у'] := 'O';
  T['т'] := 'O';
  T['ц'] := 'O';
  T['ф'] := 'O';
  T['Ъ'] := 'U';
  T['Щ'] := 'U';
  T['Ь'] := 'U';
  T['Ы'] := 'U';
  T['ъ'] := 'U';
  T['щ'] := 'U';
  T['ь'] := 'U';
  T['ы'] := 'U';
  T['с'] := 'С';
end;
(* -------------------------------------------------------------------
Подготовка таблицы переходов
------------------------------------------------------------------- *)
procedure TSearchBM.Prepare(Pattern: pchar; PatternLen: size_t;
  IgnoreCase: Boolean);
var
  i: integer;
  c, lastc: char;
begin
  FPattern := Pattern;
  FPatternLen := PatternLen;
  if FPatternLen < 1 then
    FPatternLen := strlen(FPattern);
  { Данный алгоритм базируется на наборе из 256 символов }
  if FPatternLen > 256 then
    exit;
  { 1. Подготовка таблицы перевода }
  CreateTranslationTable(FTranslate, IgnoreCase);
  { 2. Подготовка таблицы переходов }
  for c := #0 to #255 do
    FJumpTable[c] := FPatternLen;
  for i := FPatternLen - 1 downto 0 do
  begin
    c := FTranslate[FPattern[i]];
    if FJumpTable[c] >= FPatternLen - 1 then
      FJumpTable[c] := FPatternLen - 1 - i;
  end;
  FShift_1 := FPatternLen - 1;
  lastc := FTranslate[Pattern[FPatternLen - 1]];
  for i := FPatternLen - 2 downto 0 do
    if FTranslate[FPattern[i]] = lastc then
    begin
      FShift_1 := FPatternLen - 1 - i;
      break;
    end;
  if FShift_1 = 0 then
    FShift_1 := 1;
end;
procedure TSearchBM.PrepareStr(const Pattern: string; IgnoreCase: Boolean);
var
  str: pchar;
begin
  if Pattern <> '' then
  begin
{$IFDEF Windows}
    str := @Pattern[1];
{$ELSE}
    str := pchar(Pattern);
{$ENDIF}
    Prepare(str, Length(Pattern), IgnoreCase);
  end;
end;
{ Поиск последнего символа & просмотр справа налево }
function TSearchBM.Search(Text: pchar; TextLen: size_t): pchar;
var
  shift, m1, j: integer;
  jumps: size_t;
begin
  result := nil;
  if FPatternLen > 256 then
    exit;
  if TextLen < 1 then
    TextLen := strlen(Text);
  m1 := FPatternLen - 1;
  shift := 0;
  jumps := 0;
  { Поиск последнего символа }
  while jumps <= TextLen do
  begin
    Inc(Text, shift);
    shift := FJumpTable[FTranslate[Text^]];
    while shift <> 0 do
    begin
      Inc(jumps, shift);
      if jumps > TextLen then
        exit;
      Inc(Text, shift);
      shift := FJumpTable[FTranslate[Text^]];
    end;
    { Сравниваем справа налево FPatternLen - 1 символов }
    if jumps >= m1 then
    begin
      j := 0;
      while FTranslate[FPattern[m1 - j]] = FTranslate[(Text - j)^] do
      begin
        Inc(j);
        if j = FPatternLen then
        begin
          result := Text - m1;
          exit;
        end;
      end;
    end;
    shift := FShift_1;
    Inc(jumps, shift);
  end;
end;
function TSearchBM.Pos(const S: string): integer;
var
  str, p: pchar;
begin
  result := 0;
  if S <> '' then
  begin
{$IFDEF Windows}
    str := @S[1];
{$ELSE}
    str := pchar(S);
{$ENDIF}
    p := Search(str, Length(S));
    if p <> nil then
      result := 1 + p - str;
  end;
end;
end.

Как найти директорию TEMP в Windows.

Статьи по Delphi (015)…
function c_GetTempPath: string;
var
  Buffer: array[0..1023] of Char;
begin
  SetString(Result, Buffer, GetTempPath(Sizeof(Buffer) - 1, Buffer));
end;

Этот код так же можно использовать для:

GetCurrentDirectory GetSystemDirectory GetWindowsDirectory

Как найти каталог Windows.

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

Преподаватель в театральном вузе - студенту-компьютерщику: - Нежность надо изобразить! Нежность! А не эту идиотскую ухмылку! Ну, представьте, что вы смотрите на монитор с OS/2. Хорошо. Вот так лучше. А теперь представьте на мониторе Юникс. Браво! Отлично! Сам Станиславский был бы доволен. А теперь изобразите чувство негодования. Опять не получается. Хорошо. Представьте на мониторе Windows 95.Я просил негодование, а не взрыв бешенства. Ладно. Тогда представьте на мониторе Windows 3.11. Это скепсис, а не негодование. Представьте тогда Windows NT. Мне не нужна ласковая улыбка! Мне, нужно негодование! Ну, давайте вообразим на мониторе Internet Ехр1огег. О! Негодование сыграно отлично. Ну, и наконец, сами изобразите мне какое-нибудь чувство, представив на экране Лексикон. Молодой человек! Не надо блевать в аудитории!
public
  { Public declarations }
  Windir: string;
  WindirP: PChar;
  Res: Cardinal;
procedure TForm1.Button1Click(Sender: TObject);
begin
  WinDirP := StrAlloc(MAX_PATH);
  Res := GetWindowsDirectory(WinDirP, MAX_PATH);
  if Res > 0 then
    WinDir := StrPas(WinDirP);
  Label1.Caption := WinDir;
end;

Как найти каталог Windows 2.

Win95 как самолет - тошнит, а выйти некуда!
{$IFNDEF WIN32}
const MAX_PATH = 144;
{$ENDIF}
...
implementation
...
var
  a : array[0..MAX_PATH] of char;
begin
  GetWindowsDirectory(a, sizeof(a));
  ShowMessage(StrPas(a));
  GetSystemDirectory(a, sizeof(a));
  ShowMessage(StrPas(a));

Как найти каталог Windows 3.

Продается стиральная машина "Windows-95". Стирает все.

%WinDir - что бы это значило?

Часто можно видеть что-то похожее на это: "файл располагается в директории %WinDir\file.ext, где %WinDir - системная директория Windows." Так что бы это значило - %WinDir?

Если Вы устанавливали Windows, принимая все по умолчанию, то Windows у Вас установлен в директорию C:\WINDOWS. Однако иногда люди по каким-либо побуждениям устанавливают Windows в другую директорию, например, C:\WIN0000.

Иногда нужно бывает обратиться к какому-либо файлу, который находится как раз в этой %WinDir директории. Можно, конечно, написать C:\WINDOWS, но, тогда с уверенностью можно сказать, что на части компьютеров эта программа не пройдет.

Предлагаю Вам такую процедуру:

procedure TForm1.Button2Click(Sender: TObject);
var
  F: TextFile;
  St, Res: string;
begin
  AssignFile(F,'c:\msdos.sys');
  Reset(F);
  while not Eof(F) do
  begin
    ReadLn(F, St);
    if Copy(St, 1, 6) = 'WinDir' then
      Break;
  end;
  CloseFile(F);
  Res := Copy(St, 8, Length(St) - 7);
  Edit1.Text := res;
end;

После выполнения этой процедуры в переменную Res записывается значение %WinDir.

Перевести секунды в формат времени.

В одной старой-старой стране, в старом-старом городе, в старом-старом замке, в старой-старой комнате, сидит, окутанный старой-старой паутиной, старый-старый скелет. Перед ним монитор, а на мониторе надпись: - Windows is now loading. Please wait!
const 
  SecPerDay = 86400; 
  SecPerHour = 3600; 
  SecPerMinute = 60; 
function SecondToTime(const Seconds: Cardinal): Double; 
var 
  ms, ss, mm, hh, dd: Cardinal; 
begin 
  dd := Seconds div SecPerDay; 
  hh := (Seconds mod SecPerDay) div SecPerHour; 
  mm := ((Seconds mod SecPerDay) mod SecPerHour) div SecPerMinute; 
  ss := ((Seconds mod SecPerDay) mod SecPerHour) mod SecPerMinute; 
  ms := 0; 
  Result := dd + EncodeTime(hh, mm, ss, ms); 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  label1.Caption := DateTimeToStr(Date + SecondToTime(86543)); 
end;

Преобразование количества секунд в формат TTIME.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Преобразование количества секунд в формат TTIME (ЧЧ:ММ:СС).
Преобразование количества секунд в формат TTIME (ЧЧ:ММ:СС).
На выходе функции, получаем TTIME
Зависимости: system, sysutils
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        14 июня 2002 г.
***************************************************** }
function SecToTime(Sec: Integer): TTime;
var
  H, M, S: INTEGER;
  HS, MS, SS: string;
begin
  S := Sec;
  M := Round(INT(S / 60));
  S := S - M * 60; //Seconds
  H := Round(INT(M / 60)); //Hours
  M := M - H * 60; //Minutes
  if H < 10 then
    HS := '0' + Inttostr(H)
  else
    HS := inttostr(H);
  if M < 10 then
    MS := '0' + Inttostr(M)
  else
    MS := inttostr(M);
  if S < 10 then
    SS := '0' + inttostr(S)
  else
    SS := inttostr(S);
  RESULT := StrToTime(HS + ':' + MS + ':' + SS);
end;

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

ShowMessage(TimeToStr(SecToTime(50)));
//получаем сообщение:
"00:00:50 "

Получение второго цвета заголовков форм.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение «второго» цвета заголовков форм
Данная фунция возвращает значение цвета, использующегося для
отображения плавного перехода цветов в заголоках форм Windows 98, ME и 2000
Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        21 мая 2002 г.
***************************************************** }
function clGradientActiveCaption: Integer;
var
  B: BOOL;
begin
  SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, @B, 0);
  if B then
    Result := GetSysColor(COLOR_GRADIENTACTIVECAPTION)
  else
    Result := GetSysColor(COLOR_ACTIVECAPTION);
end;

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  self.Color := clGradientActiveCaption;
end;

Сворачивание вторичной формы на панель задач.

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

type
   TForm = class(TForm)
     {...}
   protected
     procedure CreateParams(var Params: TCreateParams); override;
   end;
 implementation
 {...}
 procedure TForm2.CreateParams(var Params: TCreateParams);
 begin
   inherited CreateParams(Params);
   Params.ExStyle   := Params.ExStyle or WS_EX_APPWINDOW;
   Params.WndParent := GetDesktopWindow;
 end;

Определить, установлен ли Macromedia Flash.

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

program SeekFlash;
 uses
   Forms, Windows, INIFiles, SysUtils, Dialogs, ShellApi;
 {$R *.RES}
 type
   TVersionInfo = record
     dwSignature,
     dwStrucVersion,
     dwFileVersionMS,
     dwFileVersionLS,
     dwProductVersionMS,
     dwProductVersionLS,
     dwFileFlagsMask,
     dwFileFlags,
     dwFileOS,
     dwFileType,
     dwFileSubtype,
     dwFileDateMS,
     dwFileDateLS: DWORD;
   end;
 var
   //Reg:TRegistry; 
  Ini: TIniFile;
   Text: array[1..4] of string;
   AppPath, IniFile, MySec: string;
   SetupFile, RunFile, SetupClass: string;
   VersionStr, BrowserStr: string;
   {RegSubKey,} OpenBrowser, PlugInName, UseExt: string;
   MainVersion: Word;
   // FoundOne :Boolean; 
  PVer, DPchar, POpenBrowser: PChar;
   MyPoint: Pointer;
   PLen: Cardinal;
   version: ^TVersionInfo;
   DumD: DWORD;
   LWord, HWord: Word;
   CheckHandle: Hwnd;
   //--------------------------- 
  //Gets the Plugin (file-) Version 
  //--------------------------- 
procedure GetVersion(pluginName: string);
 begin
   DPChar := StrAlloc(255);
   DPchar := PChar(PluginName);
   PVer := StrAlloc(getFileVersionInfoSize(DPchar, Plen));
   getFileVersionInfo(DPChar, 0, 255, PVer);
   VerQueryValue(Pver, '\', MyPoint, Plen);
   Version := myPoint;
   dumD  := Version.dwFileVersionMS;
   hword := dumD shr 16;
   lword := dumD and 255;
   MainVersion := hword;
   VersionStr  := IntToStr(Hword) + '.' + IntToStr(LWord);
   dumD  := Version.dwFileVersionLs;
   hword := dumD shr 16;
   lword := dumD and 255;
   versionStr := versionStr + '.' + IntToStr(Hword) + '.' + IntToStr(lWord);
 end;
 begin
   appPath := extractFileDir(Application.exeName);
   // runFile:= 'test.htm'; 
  //runFile must be a html File to determin wich browser(NC or IE) is used 
  POpenBrowser := StrAlloc(255);
   FindExecutable(PChar(extractFileName(runFile)),
     PChar(extractFileDir(runFile)), POpenBrowser);
   OpenBrowser := POpenBrowser;
   if not Fileexists(openBrowser) then
   begin
     MessageDlg(Text[4], mtInformation, [mbOK], 0);
     halt;
   end;
   //set the FileLocations for Netscape or IE 
  if Pos('NETSCAPE.EXE', uppercase(trim(OpenBrowser))) <> 0 then
   begin //found Netscape 
    BrowserStr := 'Netscape Comunicator';
     PlugInName := Copy(OpenBrowser, 1, Pos('NETSCAPE.EXE',
       uppercase(trim(OpenBrowser))) - 1);
     PluginName := PlugInName + 'Plugins\NPSWF32.dll';
   end
   else
   //found IEExplorer 
  begin
     BrowserStr := 'Internet Explorer';
     DPChar := StrAlloc(255);
     GetSystemDirectory(DPChar, 255);
     PluginName := DPChar + '\Macromed\Flash\swflash.ocx';
     DPChar     := nil;
   end;
   GetVersion(pluginName);
   //returned Version Number, may be checked 
  while mainVersion < 4 do
   begin
     if messagedlg(Text[2], mtInformation, [mbYes, mbNo], 0) = 6 then
     begin
       //installFlash; 
    end
     // if message 
    else
       halt;
     getVersion(pluginName);
   end; // while mainVer 
end.

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

unit consoleoutput;
interface
uses
  Controls, Windows, SysUtils, Forms;
function GetDosOutput(const CommandLine: string): string;
implementation
function GetDosOutput(const CommandLine: string): string;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of Char;
  BytesRead: Cardinal;
  WorkDir, Line: string;
begin
  Application.ProcessMessages;
  with SA do
  begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  // созда¸м пайп для перенаправления стандартного вывода
  CreatePipe(StdOutPipeRead, // дескриптор чтения
    StdOutPipeWrite, // дескриптор записи
    @SA, // аттрибуты безопасности
    0 // количество байт принятых для пайпа - 0 по умолчанию
    );
  try
    // Созда¸м дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
    // а так же проверяем, чтобы он не показывался на экране.
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    // Запускаем компилятор из командной строки
    WorkDir := ExtractFilePath(CommandLine);
    WasOK := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
    // Теперь, когда дескриптор получен, для безопасности закрываем запись.
    // Нам не нужно, чтобы произошло случайное чтение или запись.
    CloseHandle(StdOutPipeWrite);
    // если процесс может быть создан, то дескриптор, это его вывод
    if not WasOK then
      raise Exception.Create('Could not execute command line!')
    else
    try
        // получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
      Line := '';
      repeat
          // читаем блок символов (могут содержать возвраты каретки и переводы строки)
        WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          // есть ли что-нибудь ещ¸ для чтения?
        if BytesRead > 0 then
        begin
            // завершаем буфер PChar-ом
          Buffer[BytesRead] := #0;
            // добавляем буфер в общий вывод
          Line := Line + Buffer;
        end;
      until not WasOK or (BytesRead = 0);
        // жд¸м, пока завершится консольное приложение
      WaitForSingleObject(PI.hProcess, INFINITE);
    finally
        // Закрываем все оставшиеся дескрипторы
      CloseHandle(PI.hThread);
      CloseHandle(PI.hProcess);
    end;
  finally
    result := Line;
    CloseHandle(StdOutPipeRead);
  end;
end;
end.

Как скопировать выбранные в DBGrid записи в клипборд.

Автор: Тенцер А.Л.

const
 FIELD_DELIMITER = #9;
 RECORD_DELIMITER = #10;
procedure CopyDBGridToClipboard( Grid : TDBGrid );
var
 BM : String;
 S : String;
 S1: String;
 I : Integer;
begin
 with Grid  do begin
  if Assigned( DataSource ) and
     Assigned( DataSource.DataSet ) and
     DataSource.DataSet.Active then
  with DataSource.DataSet do begin
   S := '';
   DisableControls;
   BM := BookMark;
   for I := 0 to Pred( Columns.Count ) do begin
    if Assigned(Columns.Items[I].Field) then
      S := S + Columns.Items[I].Title.Caption + FIELD_DELIMITER;
   end;
   S[ Length( S ) ] := RECORD_DELIMITER;
   First;
   while not Eof do begin
    S1 := '';
    for I := 0 to Pred( Columns.Count ) do begin
      if Assigned(Columns.Items[I].Field) then
        S1 := S1 + FieldByName( Columns[I].FieldName ).AsString +
FIELD_DELIMITER;
    end;
    S1[ Length( S1 ) ] := RECORD_DELIMITER;
    S := S + S1;
    Next;
   end;
   BookMark := BM;
   EnableControls;
//   Clipboard.SetTextBuf( PChar( S ) );
   SendToClipboard( S );
  end;
 end;
end;

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

Автор: Vit WEB-сайт: http://forum.vingrad.ru

type
  TFake = class(TDBNavigator);
procedure TForm1.Button1Click(Sender: TObject);
begin
  TFake(DBNavigator1).buttons[nbNext].Perform(WM_RBUTTONDOWN, 0, 0);
  TFake(DBNavigator1).buttons[nbNext].Perform(WM_RBUTTONUP, 0, 0);
end;

Каким образом выбирать размер шрифта.

Автор: Nomadic Каким образом выбирать размер шрифта, т.к. все мои страдания по выборупараметров шрифта в CreateFont() никак не отражались на его размере. Все что я придумал, это юзать glScale(), но в этом случае получаем плохое качество (по сравнению с той-же Вордой) при малом размере символов

Вот часть работающего примера на Си (переведенного мною на Паскаль (АА)).

procedure GLSetupRC(pData: Pointer)
  //void GLSetupRC(void *pData)
//{
var
  //  HDC hDC;
  hDC: HDC;
  //  HFONT hFont;
  hFont: HFONT;
  //  GLYPHMETRICSFLOAT agmf[128];
  agmf: array[0..127] of GLYPHMETRICSFLOAT;
  //  LOGFONT logfont;
  logfont: LOGFONT;
begin
  logfont.lfHeight := -10;
  logfont.lfWidth := 0;
  logfont.lfEscapement := 0;
  logfont.lfOrientation := 0;
  logfont.lfWeight := FW_BOLD;
  logfont.lfItalic := FALSE;
  logfont.lfUnderline := FALSE;
  logfont.lfStrikeOut := FALSE;
  logfont.lfCharSet := ANSI_CHARSET;
  logfont.lfOutPrecision := OUT_DEFAULT_PRECIS;
  logfont.lfClipPrecision := CLIP_DEFAULT_PRECIS;
  logfont.lfQuality := DEFAULT_QUALITY;
  logfont.lfPitchAndFamily := DEFAULT_PITCH;
  //strcpy(logfont.lfFaceName,"Arial");
  //  strcpy(logfont.lfFaceName,"Decor");
  StrPCopy(logfont.lfFaceName, 'Decor');
  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST); // Hidden surface removal
  glFrontFace(GL_CCW); // Counter clock-wise polygons face out
  glEnable(GL_CULL_FACE); // Do not calculate insides
  glShadeModel(GL_SMOOTH); // Smooth shading
  glEnable(GL_AUTO_NORMAL);
  glEnable(GL_NORMALIZE);
  glEnable(GL_COLOR_MATERIAL);
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glEnable(GL_LIGHTING);
  glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
  glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
  glEnable(GL_LIGHT0);
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
  glMateriali(GL_FRONT, GL_SHININESS, 100);
  // Blue 3D Text
  glRGB(0, 0, 255);
  // Select the font into the DC
  hDC := (HDC)pData;
  //  hFont = CreateFontIndirect(&logfont);
  hFont := CreateFontIndirect(Addr(logfont));
  SelectObject(hDC, hFont);
  //create display lists for glyphs 0 through 255 with 0.3 extrusion
  // and default deviation. The display list numbering starts at 1000
  // (it could be any number).
  //  if(!wglUseFontOutlines(hDC, 0, 128, 1000, 0., 0.3,
  //                            WGL_FONT_POLYGONS, agmf))
  if not wglUseFontOutlines(hDC, 0, 128, 1000, 0., 0.3,
    //>                                         ``` - это тебе поможет
    //> Выводить текст можно в любым масштабе
    WGL_FONT_POLYGONS, agmf) then
    Windows.MessageBox(nil, 'Could not create Font Outlines',
      'Error', MB_OK or MB_ICONSTOP);
  // Delete the font now that we are done
  DeleteObject(hFont);
  //}
end;
// void GLRenderScene(void *pData)
procedure GLRenderScene(pData: Pointer);
begin
  (*  ...  *)
  // Draw 3D text
  glListBase(1000);
  glPushMatrix();
  // Set up transformation to draw the string.
  glTranslatef(-35.0, 0.0, -5.0);
  glScalef(60.0, 60.0, 60.0);
  glCallLists(3, GL_UNSIGNED_BYTE, 'Decor');
  glPopMatrix(); // Clear the window with current clearing color
  (* ... *)
end;

Как выделить в RichEdit 4 строки.

Год назад. Раздается в нашей конторе звонок. Поднимает трубку человек, там кого-то просят, кто в другой комнате ... человек оглядывает присутствующих "Как в соседню комнату перевести? Какай там адрес? www а дальше что?"

Строки нумеруются с 0!

with richedit1 do
begin
  selstart:=FindText(lines[3],0,length(text), [stWholeWord]);
  sellength:=length(lines[3]);
  selattributes.color:=clBlue;
end;

Работает, если строка уникальная, иначе можно - вычислять начало.

sstart := 0;
for i := 0 to numstr - 1 do
  sstart := sstart + length(lines[i]) + 2; // numstr=3
selstart := sstart; // для 4 строки

Выделить строку в Memo.

Возвращаюсь вечером с работы домой, устал естессно, причём день выдался сплошь да рядом связаный с аккаунтами... подхожу к двери подъезда, вставляю ключ, дёргаю дверь... дверь не открывается - первая мысль "БЛИН , ОПЯТЬ ПАРОЛЬ СМЕНИЛИ"...(на самом деле ключ не до конца вставил)
procedure TfrmMain.Memo1Click(Sender: TObject); 
var 
  Line: Integer; 
begin 
  with (Sender as TMemo) do 
  begin 
    Line      := Perform(EM_LINEFROMCHAR, SelStart, 0); 
    SelStart  := Perform(EM_LINEINDEX, Line, 0); 
    SelLength := Length(Lines[Line]); 
  end; 
end;

Выделение меню.

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

- Как размножается Microsoft? - Делением.
Function SelectMenu(winname,item,subitem:string):boolean;
// winname - имя окна, item - имя пункта меню, subitem - имя подпункта
var winhandle,menuhandle,submenuhandle,i,j,res:integer;
   itemname,subitemname:pchar;
begin
res:=-1;
winhandle:=FindWindow(nil,pchar(winname));
menuhandle:=getmenu(winhandle);
getmem(itemname,255);
getmem(subitemname,255);
for i:=0 to getmenuitemcount(menuhandle)-1 do
   begin
   getmenustring(menuhandle,i,itemname,255,MF_BYPOSITION);
   if string(itemname)=item then begin
   submenuhandle:=getsubmenu(menuhandle,i);
   for j:=0 to getmenuitemcount(submenuhandle)-1 do
   begin
   getmenustring(submenuhandle,j,subitemname,255,MF_BYPOSITION);
   if string(subitemname)=subitem then
res:=SendMessage(winhandle,WM_COMMAND,makelong(getmenuitemid(submenuhandle,j),0
),0);
   end;
   end;
end;
freemem(itemname);
freemem(subitemname);
if res=0 then result:=true else result:=false;
end;

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

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

Автор: Даниил Карапетян WEB сайт: http://program.dax.ru
- Как три программиста могут организовать бизнес? - Один пишет вирусы, а другой антивирусы. - А третий? - Операционные системы, под которыми это все работает.
Как реализовать выделение " резиновым прямоугольником" . Иными словами, когда пользоватьль нажимает на левую кнопку мыши и сдвигает ее, на экране появляется прямоугольник, изменяющий размеры при движении мыши, причем все объекты, попавшие в этот прямоугольник выделяются.

В качестве объекта я взял Label, меняющий цвет в зависимости от того, выделен он или нет. При нажатии мышью на форме в FirstPoint кладутся координата курсора. При дальнейшем движении мыши координаты прямоугольника будут высчитываться по FirstPoint и текущим координатам курсора. Причем, чтобы программа нормально отрабатывала случай, когда высота или ширина прямоугольника отрицательная (это произойдет, если увести мышь левее или выше начальной точки), создана процедура NormalRect. NormalRect устанавливает ко Скачать необходимые для компиляции файлы проекта можно на program.dax.ru/subscribe/. http://program.dax.ru/subscribe/index.htm.

uses stdctrls;
var
  Selecting: boolean = false;
  FirstPoint: TPoint;
  sel: TRect;
procedure DrawRect;
begin
  with Form1.Canvas do begin
    Pen.Style := psDot;
    Pen.Color := clGray;
    Pen.Mode := pmXor;
    Brush.Style := bsClear;
    Rectangle(sel.Left, sel.Top, sel.Right, sel.Bottom);
  end;
end;
procedure NormalRect(p1, p2: TPoint);
begin
  if p1.x <  p2.x then begin
    sel.Left := p1.x;
    sel.Right := p2.x;
  end else begin
    sel.Left := p2.x;
    sel.Right := p1.x;
  end;
  if p1.y <  p2.y then begin
    sel.Top := p1.y;
    sel.Bottom := p2.y;
  end else begin
    sel.Top := p2.y;
    sel.Bottom := p1.y;
  end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
  randomize;
  for i := 1 to random(5) + 5 do begin
    with TLabel.Create(Form1) do begin
      Caption := 'Label' + IntToStr(i);
      Left := random(Form1.ClientWidth - Width);
      Top := random(Form1.ClientHeight - Height);
      Visible := true;
      Parent := Form1;
    end;
  end;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if selecting or (Button < >  mbLeft) then Exit;
  SetCapture(Form1.Handle);
  Selecting := true;
  FirstPoint := Point(X, Y);
  sel := Bounds(X, Y, 0, 0);
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
  procedure SelectLebel(lb: TLabel; r: TRect);
  var
    select: boolean;
    res: TRect;
  begin
    select := IntersectRect(res, lb.BoundsRect, r);
    if select and (lb.Color = clNavy) then Exit;
    if select then begin
      lb.Color := clNavy;
      lb.Font.Color := clWhite;
    end else begin
      lb.Color := clBtnFace;
      lb.Font.Color := clBlack;
    end;
  end;
var
  i: integer;
begin
  if not Selecting then Exit;
  DrawRect;
  NormalRect(FirstPoint, Point(X, Y));
  for i := 0 to Form1.ComponentCount - 1 do
    if (Form1.Components[i] is TLabel) then
      SelectLebel(Form1.Components[i] as TLabel, sel);
  Application.ProcessMessages;
  DrawRect;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (not Selecting) or (Button < >  mbLeft) then Exit;
  NormalRect(FirstPoint, Point(X, Y));
  DrawRect;
  ReleaseCapture;
  Selecting := false;
end;
{
Программа была протестирована:
Дмитрий Бажин mailto:bsdriver@mail.ru
Юрий Кравченко mailto:ykravchenko@ukr.net
Все советы и замечания, пожалуйста,
присылайте на subscribe@program.dax.ru
Всего доброго,
Даниил Карапетян.
}

Что из себя представляет Self.

Автор: Vit WEB-сайт: http://forum.vingrad.ru Self - это явное задание экземпляра класса в его методе.

Например для твоей формы это указание на саму форму:

procedure TForm1.Button1Click(Sender: TObject);
begin
  showmessage(self.classname + #13#10 + self.name);
end;

Если например это MDI форма то это будет указатель именно на тот экземпляр для которого выполняется этот код. На практике Self обычно применяется при написании своих классов, когда ты пишешь класс или компонент, то у тебя нет переменной с экземпляром этого компонента, следовательно чтобы обратится к экземпляру (который появится только в коде конечного пользователя, который будет использовать компонент) класса нужна переменная - вот она и берётся за self.

Что из себя представляет Self 2.

Автор: Fantasist WEB-сайт: http://forum.vingrad.ru

Чтобы понять, что такое self надо понять что такое метод класса. Метод класса - это просто функция(процедура) который имеет дополнительный неявный параметр - указатель на экземпляр класса. То есть:

TMy = class
  x: integer;
  procedure Proc(val: integer);
end;
procedure TMy.Proc(val: integer);
begin
  x := val;
end;

После компиляции это будет практически то же самое, что:

procedure Proc(self: TMy; val: integer);
begin
  self.x := val;
end;

То есть на самом деле в методе Proc обращаясь к x мы на самом деле обращаемся к self.x, просто переменная self опускается. В скомпилированном коде нет такого понятия как классы - есть только код и память. Все методы классов превращаются в обыкновенные функции, в которым качестве первого параметра передается указатель на область памяти где лежит созданный пользователем экземпляр класса, который они и используют для чтения или записи(а так же для вызова) того, что мы называем членами класса.

var
  m1, m2: TMy;
begin
  ...
  m1.Proc(4); // Proc(m1,4)
  m2.Proc(4); // Proc(m2,4)
end;

Создание самоуничтожающегося объекта.

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

type
   ISelfDestroy = interface;
   //forget about GUID, if you are not using COM 
  TSelfDestroy = class(TInterfacedObject, ISelfDestroy)
   private
     FObject: TObject;
   public
     constructor Create(AObject: TObject);
     destructor Destroy; override;
   end;
 implementation
 constructor TSelfDestroy.Create(AObject: TObject);
 begin
   FObject := AObject;
 end;
 destructor TSelfDestroy.Destroy;
 begin
   FreeAndNil(FObject);
   inherited;
 end;
 // So when you use, just do like this... 
procedure TForm1.Button1Click(Sender: TObject);
 var
   MyObject: TMyObject;
   SelfDestroy: TSelfDestroy;
   begin
   MyObject    := TMyObject.Create;
   SelfDestroy := TSelfDestroy.Create(MyObject);
   // The MyObject will free automatically as soon as TSelfDestroy 
  // goes out of scope. 
  // Carry on your code here... 
end;

Диалог для выбора компьютера.

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

uses
   ShlObj, ActiveX;
 function BrowseComputer(DialogTitle: string; var CompName: string;
   bNewStyle: Boolean): Boolean;
   // bNewStyle: If True, this code will try to use the "new" 
  // BrowseForFolders UI on Windows 2000/XP 
const
   BIF_USENEWUI = 28;
 var
   BrowseInfo: TBrowseInfo;
   ItemIDList: PItemIDList;
   ComputerName: array[0..MAX_PATH] of Char;
   Title: string;
   WindowList: Pointer;
   ShellMalloc: IMalloc;
 begin
   if Failed(SHGetSpecialFolderLocation(Application.Handle, CSIDL_NETWORK, ItemIDList)) then
     raise Exception.Create('Unable open browse computer dialog');
   try
     FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
     BrowseInfo.hwndOwner := Application.Handle;
     BrowseInfo.pidlRoot := ItemIDList;
     BrowseInfo.pszDisplayName := ComputerName;
     Title := DialogTitle;
     BrowseInfo.lpszTitle := PChar(Pointer(Title));
     if bNewStyle then
       BrowseInfo.ulFlags := BIF_BROWSEFORCOMPUTER or BIF_USENEWUI
     else
       BrowseInfo.ulFlags := BIF_BROWSEFORCOMPUTER;
     WindowList := DisableTaskWindows(0);
     try
       Result := SHBrowseForFolder(BrowseInfo) <> nil;
     finally
       EnableTaskWindows(WindowList);
     end;
     if Result then CompName := ComputerName;
   finally
     if Succeeded(SHGetMalloc(ShellMalloc)) then
       ShellMalloc.Free(ItemIDList);
   end;
 end;
 // Example 
procedure TForm1.Button1Click(Sender: TObject);
 var
   Computer: string;
 begin
   BrowseComputer('...', Computer, True);
   label1.Caption := Computer;
 end;

Показать диалог выбора директории с кнопкой для создания новой.

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

{....}
 uses
   ShlObj, ActiveX;
 {....}
 { 
  This code shows the SelectDirectory dialog with additional expansions: 
  - an edit box, where the user can type the path name, 
  - also files can appear in the list, 
  - a button to create new directories. 
  Dieser Code zeigt den SelectDirectory-Dialog mit zusatzlichen Erweiterungen: 
  - eine Edit-Box, wo der Benutzer den Verzeichnisnamen eingeben kann, 
  - auch Dateien konnen in der Liste angezeigt werden, 
  - eine Schaltflache zum Erstellen neuer Verzeichnisse. 
}
 function AdvSelectDirectory(const Caption: string; const Root: WideString;
   var Directory: string; EditBox: Boolean = False; ShowFiles: Boolean = False;
   AllowCreateDirs: Boolean = True): Boolean;
   // callback function that is called when the dialog has been initialized 
  //or a new directory has been selected 
  // Callback-Funktion, die aufgerufen wird, wenn der Dialog initialisiert oder 
  //ein neues Verzeichnis selektiert wurde 
  function SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: lParam): Integer;
     stdcall;
   var
     PathName: array[0..MAX_PATH] of Char;
   begin
     case uMsg of
       BFFM_INITIALIZED: SendMessage(Wnd, BFFM_SETSELECTION, Ord(True), Integer(lpData));
       // include the following comment into your code if you want to react on the 
      //event that is called when a new directory has been selected 
      // binde den folgenden Kommentar in deinen Code ein, wenn du auf das Ereignis 
      //reagieren willst, das aufgerufen wird, wenn ein neues Verzeichnis selektiert wurde 
      {BFFM_SELCHANGED: 
      begin 
        SHGetPathFromIDList(PItemIDList(lParam), @PathName); 
        // the directory "PathName" has been selected 
        // das Verzeichnis "PathName" wurde selektiert 
      end;}
     end;
     Result := 0;
   end;
 var
   WindowList: Pointer;
   BrowseInfo: TBrowseInfo;
   Buffer: PChar;
   RootItemIDList, ItemIDList: PItemIDList;
   ShellMalloc: IMalloc;
   IDesktopFolder: IShellFolder;
   Eaten, Flags: LongWord;
 const
   // necessary for some of the additional expansions 
  // notwendig fur einige der zusatzlichen Erweiterungen 
  BIF_USENEWUI = $0040;
   BIF_NOCREATEDIRS = $0200;
 begin
   Result := False;
   if not DirectoryExists(Directory) then
     Directory := '';
   FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
   if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
   begin
     Buffer := ShellMalloc.Alloc(MAX_PATH);
     try
       RootItemIDList := nil;
       if Root <> '' then
       begin
         SHGetDesktopFolder(IDesktopFolder);
         IDesktopFolder.ParseDisplayName(Application.Handle, nil,
           POleStr(Root), Eaten, RootItemIDList, Flags);
       end;
       OleInitialize(nil);
       with BrowseInfo do
       begin
         hwndOwner := Application.Handle;
         pidlRoot := RootItemIDList;
         pszDisplayName := Buffer;
         lpszTitle := PChar(Caption);
         // defines how the dialog will appear: 
        // legt fest, wie der Dialog erscheint: 
        ulFlags := BIF_RETURNONLYFSDIRS or BIF_USENEWUI or
           BIF_EDITBOX * Ord(EditBox) or BIF_BROWSEINCLUDEFILES * Ord(ShowFiles) or
           BIF_NOCREATEDIRS * Ord(not AllowCreateDirs);
         lpfn    := @SelectDirCB;
         if Directory <> '' then
           lParam := Integer(PChar(Directory));
       end;
       WindowList := DisableTaskWindows(0);
       try
         ItemIDList := ShBrowseForFolder(BrowseInfo);
       finally
         EnableTaskWindows(WindowList);
       end;
       Result := ItemIDList <> nil;
       if Result then
       begin
         ShGetPathFromIDList(ItemIDList, Buffer);
         ShellMalloc.Free(ItemIDList);
         Directory := Buffer;
       end;
     finally
       ShellMalloc.Free(Buffer);
     end;
   end;
 end;
 // Example: 
procedure TForm1.Button1Click(Sender: TObject);
 var
   dir: string;
 begin
   AdvSelectDirectory('Caption', 'c:\', dir, False, False, True);
   Label1.Caption := dir;
 end;

Почти полный аналог метода SendKeys из VB.

Автор: Ken Henderson.

(*
SendKeys routine for 32-bit Delphi.
Written by Ken Henderson
Copyright (c) 1995 Ken Henderson     email:khen@compuserve.com
This unit includes two routines that simulate popular Visual Basic
routines: Sendkeys and AppActivate.  SendKeys takes a PChar
as its first parameter and a boolean as its second, like so:
SendKeys('KeyString', Wait);
where KeyString is a string of key names and modifiers that you want
to send to the current input focus and Wait is a boolean variable or value
that indicates whether SendKeys should wait for each key message to be
processed before proceeding.  See the table below for more information.
AppActivate also takes a PChar as its only parameter, like so:
AppActivate('WindowName');
where WindowName is the name of the window that you want to make the
current input focus.
SendKeys supports the Visual Basic SendKeys syntax, as documented below.
Supported modifiers:
+ = Shift
^ = Control
% = Alt
Surround sequences of characters or key names with parentheses in order to
modify them as a group.  For example, '+abc' shifts only 'a', while  '+(abc)' shifts
all three characters.
Supported special characters
~ = Enter
( = begin modifier group (see above)
) = end modifier group (see above)
{ = begin key name text (see below)
} = end key name text (see below)
Supported characters:
Any character that can be typed is supported.  Surround the modifier keys
listed above with braces in order to send as normal text.
Supported key names (surround these with braces):
BKSP, BS, BACKSPACE
BREAK
CAPSLOCK
CLEAR
DEL
DELETE
DOWN
END
ENTER
ESC
ESCAPE
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
F16
HELP
HOME
INS
LEFT
NUMLOCK
PGDN
PGUP
PRTSC
RIGHT
SCROLLLOCK
TAB
UP
Follow the keyname with a space and a number to send the specified key a
given number of times (e.g., {left 6}).
*)
unit sndkey32;
interface
Uses SysUtils, Windows, Messages;
Function SendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;
function AppActivate(WindowName : PChar) : boolean;
{Buffer for working with PChar's}
const
  WorkBufLen = 40;
var
  WorkBuf : array[0..WorkBufLen] of Char;
implementation
type
  THKeys = array[0..pred(MaxLongInt)] of byte;
var
  AllocationSize : integer;
(*
Converts a string of characters and key names to keyboard events and
passes them to Windows.
Example syntax:
SendKeys('abc123{left}{left}{left}def{end}456{left 6}ghi{end}789', True);
*)
Function SendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;
type
  WBytes = array[0..pred(SizeOf(Word))] of Byte;
  TSendKey = record
    Name : ShortString;
    VKey : Byte;
  end;
const
  {Array of keys that SendKeys recognizes.
  If you add to this list, you must be sure to keep it sorted alphabetically
  by Name because a binary search routine is used to scan it.}
  MaxSendKeyRecs = 41;
  SendKeyRecs : array[1..MaxSendKeyRecs] of TSendKey =
  (
   (Name:'BKSP';            VKey:VK_BACK),
   (Name:'BS';              VKey:VK_BACK),
   (Name:'BACKSPACE';       VKey:VK_BACK),
   (Name:'BREAK';           VKey:VK_CANCEL),
   (Name:'CAPSLOCK';        VKey:VK_CAPITAL),
   (Name:'CLEAR';           VKey:VK_CLEAR),
   (Name:'DEL';             VKey:VK_DELETE),
   (Name:'DELETE';          VKey:VK_DELETE),
   (Name:'DOWN';            VKey:VK_DOWN),
   (Name:'END';             VKey:VK_END),
   (Name:'ENTER';           VKey:VK_RETURN),
   (Name:'ESC';             VKey:VK_ESCAPE),
   (Name:'ESCAPE';          VKey:VK_ESCAPE),
   (Name:'F1';              VKey:VK_F1),
   (Name:'F10';             VKey:VK_F10),
   (Name:'F11';             VKey:VK_F11),
   (Name:'F12';             VKey:VK_F12),
   (Name:'F13';             VKey:VK_F13),
   (Name:'F14';             VKey:VK_F14),
   (Name:'F15';             VKey:VK_F15),
   (Name:'F16';             VKey:VK_F16),
   (Name:'F2';              VKey:VK_F2),
   (Name:'F3';              VKey:VK_F3),
   (Name:'F4';              VKey:VK_F4),
   (Name:'F5';              VKey:VK_F5),
   (Name:'F6';              VKey:VK_F6),
   (Name:'F7';              VKey:VK_F7),
   (Name:'F8';              VKey:VK_F8),
   (Name:'F9';              VKey:VK_F9),
   (Name:'HELP';            VKey:VK_HELP),
   (Name:'HOME';            VKey:VK_HOME),
   (Name:'INS';             VKey:VK_INSERT),
   (Name:'LEFT';            VKey:VK_LEFT),
   (Name:'NUMLOCK';         VKey:VK_NUMLOCK),
   (Name:'PGDN';            VKey:VK_NEXT),
   (Name:'PGUP';            VKey:VK_PRIOR),
   (Name:'PRTSC';           VKey:VK_PRINT),
   (Name:'RIGHT';           VKey:VK_RIGHT),
   (Name:'SCROLLLOCK';      VKey:VK_SCROLL),
   (Name:'TAB';             VKey:VK_TAB),
   (Name:'UP';              VKey:VK_UP)
  );
  {Extra VK constants missing from Delphi's Windows API interface}
  VK_NULL=0;
  VK_SemiColon=186;
  VK_Equal=187;
  VK_Comma=188;
  VK_Minus=189;
  VK_Period=190;
  VK_Slash=191;
  VK_BackQuote=192;
  VK_LeftBracket=219;
  VK_BackSlash=220;
  VK_RightBracket=221;
  VK_Quote=222;
  VK_Last=VK_Quote;
  ExtendedVKeys : set of byte =
  [VK_Up,
   VK_Down,
   VK_Left,
   VK_Right,
   VK_Home,
   VK_End,
   VK_Prior,  {PgUp}
   VK_Next,   {PgDn}
   VK_Insert,
   VK_Delete];
const
  INVALIDKEY = $FFFF {Unsigned -1};
  VKKEYSCANSHIFTON = $01;
  VKKEYSCANCTRLON = $02;
  VKKEYSCANALTON = $04;
  UNITNAME = 'SendKeys';
var
  UsingParens, ShiftDown, ControlDown, AltDown, FoundClose : Boolean;
  PosSpace : Byte;
  I, L : Integer;
  NumTimes, MKey : Word;
  KeyString : String[20];
procedure DisplayMessage(Message : PChar);
begin
  MessageBox(0,Message,UNITNAME,0);
end;
function BitSet(BitTable, BitMask : Byte) : Boolean;
begin
  Result:=ByteBool(BitTable and BitMask);
end;
procedure SetBit(var BitTable : Byte; BitMask : Byte);
begin
  BitTable:=BitTable or Bitmask;
end;
procedure KeyboardEvent(VKey, ScanCode : Byte; Flags : Longint);
var
  KeyboardMsg : TMsg;
begin
  keybd_event(VKey, ScanCode, Flags,0);
  If (Wait) then While (PeekMessage(KeyboardMsg,0,WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do begin
    TranslateMessage(KeyboardMsg);
    DispatchMessage(KeyboardMsg);
  end;
end;
procedure SendKeyDown(VKey: Byte; NumTimes : Word; GenUpMsg : Boolean);
var
  Cnt : Word;
  ScanCode : Byte;
  NumState : Boolean;
  KeyBoardState : TKeyboardState;
begin
  If (VKey=VK_NUMLOCK) then begin
    NumState:=ByteBool(GetKeyState(VK_NUMLOCK) and 1);
    GetKeyBoardState(KeyBoardState);
    If NumState then KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] and not 1)
    else KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] or 1);
    SetKeyBoardState(KeyBoardState);
    exit;
  end;
  ScanCode:=Lo(MapVirtualKey(VKey,0));
  For Cnt:=1 to NumTimes do
    If (VKey in ExtendedVKeys)then begin
      KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
      If (GenUpMsg) then
        KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
    end  else begin
      KeyboardEvent(VKey, ScanCode, 0);
      If (GenUpMsg) then KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
    end;
end;
procedure SendKeyUp(VKey: Byte);
var
  ScanCode : Byte;
begin
  ScanCode:=Lo(MapVirtualKey(VKey,0));
  If (VKey in ExtendedVKeys)then
    KeyboardEvent(VKey, Sca
  else KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end;
procedure SendKey(MKey: Word; NumTimes : Word; GenDownMsg : Boolean);
begin
  If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyDown(VK_SHIFT,1,False);
  If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyDown(VK_CONTROL,1,False);
  If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyDown(VK_MENU,1,False);
  SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
  If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyUp(VK_SHIFT);
  If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyUp(VK_CONTROL);
  If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyUp(VK_MENU);
end;
{Implements a simple binary search to locate special key name strings}
function StringToVKey(KeyString : ShortString) : Word;
var
  Found, Collided : Boolean;
  Bottom, Top, Middle : Byte;
begin
  Result:=INVALIDKEY;
  Bottom:=1;
  Top:=MaxSendKeyRecs;
  Found:=false;
  Middle:=(Bottom+Top) div 2;
  Repeat
    Collided:=((Bottom=Middle) or (Top=Middle));
    If (KeyString=SendKeyRecs[Middle].Name) then begin
       Found:=True;
       Result:=SendKeyRecs[Middle].VKey;
    end  else begin
       If (KeyString>SendKeyRecs[Middle].Name) then Bottom:=Middle
       else Top:=Middle;
       Middle:=(Succ(Bottom+Top)) div 2;
    end;
  Until (Found or Collided);
  If (Result=INVALIDKEY) then DisplayMessage('Invalid Key Name');
end;
procedure PopUpShiftKeys;
begin
  If (not UsingParens) then begin
    If ShiftDown then SendKeyUp(VK_SHIFT);
    If ControlDown then SendKeyUp(VK_CONTROL);
    If AltDown then SendKeyUp(VK_MENU);
    ShiftDown:=false;
    ControlDown:=false;
    AltDown:=false;
  end;
end;
begin
  AllocationSize:=MaxInt;
  Result:=false;
  UsingParens:=false;
  ShiftDown:=false;
  ControlDown:=false;
  AltDown:=false;
  I:=0;
  L:=StrLen(SendKeysString);
  If (L>AllocationSize) then L:=AllocationSize;
  If (L=0) then Exit;
  while  (Ibegin
    case SendKeysString[I] of
    '(' : begin
            UsingParens:=True;
            Inc(I);
          end;
    ')' : begin
            UsingParens:=False;
            PopUpShiftKeys;
            Inc(I);
          end;
    '%' : begin
             AltDown:=True;
             SendKeyDown(VK_MENU,1,False);
             Inc(I);
          end;
    '+' :  begin
             ShiftDown:=True;
             SendKeyDown(VK_SHIFT,1,False);
             Inc(I);
           end;
    '^' :  begin
             ControlDown:=True;
             SendKeyDown(VK_CONTROL,1,False);
             Inc(I);
           end;
    '{' : begin
            NumTimes:=1;
            If (SendKeysString[Succ(I)]='{') then begin
              MKey:=VK_LEFTBRACKET;
              SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
              SendKey(MKey,1,True);
              PopUpShiftKeys;
              Inc(I,3);
              Continue;
            end;
            KeyString:='';
            FoundClose:=False;
            while  (I<=L) do begin
              Inc(I);
              If (SendKeysString[I]='}') then begin
                FoundClose:=True;
                Inc(I);
                Break;
              end;
              KeyString:=KeyString+Upcase(SendKeysString[I]);
            end;
            If (Not FoundClose) then begin
               DisplayMessage('No Close');
               Exit;
            end;
            If (SendKeysString[I]='}') then begin
              MKey:=VK_RIGHTBRACKET;
              SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
              SendKey(MKey,1,True);
              PopUpShiftKeys;
              Inc(I);
              Continue;
            end;
            PosSpace:=Pos(' ',KeyString);
            If (PosSpace<>0) then begin
               NumTimes:=StrToInt(Copy(KeyString,Succ(PosSpace),Length(KeyString)-PosSpace));
               KeyString:=Copy(KeyString,1,Pred(PosSpace));
            end;
            If (Length(KeyString)=1) then MKey:=vkKeyScan(KeyString[1])
            else MKey:=StringToVKey(KeyString);
            If (MKey<>INVALIDKEY) then begin
              SendKey(MKey,NumTimes,True);
              PopUpShiftKeys;
              Continue;
            end;
          end;
    '~' : begin
            SendKeyDown(VK_RETURN,1,True);
            PopUpShiftKeys;
            Inc(I);
          end;
    else  begin
             MKey:=vkKeyScan(SendKeysString[I]);
             If (MKey<>INVALIDKEY) then begin
               SendKey(MKey,1,True);
               PopUpShiftKeys;
             end else DisplayMessage('Invalid KeyName');
             Inc(I);
          end;
    end;
  end;
  Result:=true;
  PopUpShiftKeys;
end;
{AppActivate
This is used to set the current input focus to a given window using its
name.  This is especially useful for ensuring a window is active before
sending it input messages using the SendKeys function.  You can specify
a window's name in its entirety, or only portion of it, beginning from
the left.
}
var
  WindowHandle : HWND;
function EnumWindowsProc(WHandle: HWND; lParam: LPARAM): BOOL; export; stdcall;
const
  MAX_WINDOW_NAME_LEN = 80;
var
  WindowName : array[0..MAX_WINDOW_NAME_LEN] of char;
begin
  {Can't test GetWindowText's return value since some windows don't have a title}
  GetWindowText(WHandle,WindowName,MAX_WINDOW_NAME_LEN);
  Result := (StrLIComp(WindowName,PChar(lParam), StrLen(PChar(lParam))) <> 0);
  If (not Result) then WindowHandle:=WHandle;
end;
function AppActivate(WindowName : PChar) : boolean;
begin
  try
    Result:=true;
    WindowHandle:=FindWindow(nil,WindowName);
    If (WindowHandle=0) then EnumWindows(@EnumWindowsProc,Integer(PChar(WindowName)));
    If (WindowHandle<>0) then begin
      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_HOTKEY, WindowHandle);
      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_RESTORE, WindowHandle);
    end else Result:=false;
  except
    on Exception do Result:=false;
  end;
end;
end.

Как отправить нажатие клавиши с кодом 255 в элемент управления Windows.

Ученые исследовательского подразделения MicroSoft подсчитали точное количество клавиш на клавиатуре...

Функция keybd_event() принимает значения до 244 - как мне отправить нажатие клавиши с кодом #255 в элемент управления Windows? Это может понадобится для иностранных языков или для специальных символов. (например, в русских шрифтах символ с кодом #255 - я прописное). Приведенный в примере метод, не стоит использовать в случае если символ может быть передан обычным способом (функцией keybd_event()).

procedure TForm1.Button1Click(Sender: TObject);
var
      KeyData : packed record
                RepeatCount : word;
                ScanCode : byte;
                Bits : byte;
        end;
begin
        {Let the button repaint}
        Application.ProcessMessages;
        {Set the focus to the window}
        Edit1.SetFocus;
        {Send a right so the char is added to the end of the line}
        //  SimulateKeyStroke(VK_RIGHT, 0);
        keybd_event(VK_RIGHT, 0,0,0);
        {Let the app get the message}
        Application.ProcessMessages;
        FillChar(KeyData, sizeof(KeyData), #0);
        KeyData.ScanCode := 255;
        KeyData.RepeatCount := 1;
        SendMessage(Edit1.Handle, WM_KEYDOWN, 255,LongInt(KeyData));
        KeyData.Bits := KeyData.Bits or (1 shl 30);
        KeyData.Bits := KeyData.Bits or (1 shl 31);
        SendMessage(Edit1.Handle, WM_KEYUP, 255, LongInt(KeyData));
        KeyData.Bits := KeyData.Bits and not (1 shl 30);
        KeyData.Bits := KeyData.Bits and not (1 shl 31);
        SendMessage(Edit1.Handle, WM_CHAR, 255, LongInt(KeyData));
        Application.ProcessMessages;
end;

Отправить письмо на E-mail так, чтобы пользователь не подозревал об отправке.

Статьи по Delphi (015)…
У одной провайдерской фирмы спросили: - Почему Вы так активно создаёте сервисы бесплатного e-mail? - Ну, как Вам сказать... А Вы читали когда-нибудь чужую почту?
unit Email;
interface
uses
  Windows, SusUtils, Classes;
function SendEmail(const RecipName, RecipAddress,
Subject, Attachment: string): Boolean;
function IsOnline: Boolean;
implementation
uses Mapi;
function SendEmail(const RecipName, RecipAddress,
Subject, Attachment: string): Boolean;
var
  MapiMessage: TMapiMessage;
  MapiFileDesc: TMapiFileDesc;
  MapiRecipDesc: TMapiRecipDesc;
  i: integer;
  s: string;
begin
  with MapiRecipDesc do
  begin
    ulRecerved:= 0;
    ulRecipClass:= MAPI_TO;
    lpszName:= PChar(RecipName);
    lpszAddress:= PChar(RecipAddress);
    ulEIDSize:= 0;
    lpEntryID:= nil;
  end;
  with MapiFileDesc do
  begin
    ulReserved:= 0;
    flFlags:= 0;
    nPosition:= 0;
    lpszPathName:= PChar(Attachment);
    lpszFileName:= nil;
    lpFileType:= nil;
  end;
  with MapiMessage do
  begin
    ulReserved := 0;
    lpszSubject := nil;
    lpszNoteText := PChar(Subject);
    lpszMessageType := nil;
    lpszDateReceived := nil;
    lpszConversationID := nil;
    flFlags := 0;
    lpOriginator := nil;
    nRecipCount := 1;
    lpRecips := @MapiRecipDesc;
    if length(Attachment) > 0 then
    begin
      nFileCount:= 1;
      lpFiles := @MapiFileDesc;
    end
    else
    begin
      nFileCount:= 0;
      lpFiles:= nil;
    end;
  end;
  Result:= MapiSendMail(0, 0, MapiMessage, MAPI_DIALOG
  or MAPI_LOGON_UI or MAPI_NEW_SESSION, 0) = SUCCESS_SUCCESS;
end;
function IsOnline: Boolean;
var
  RASConn: TRASConn;
  dwSize,dwCount: DWORD;
begin
  RASConns.dwSize:= SizeOf(TRASConn);
  dwSize:= SizeOf(RASConns);
  Res:=RASEnumConnectionsA(@RASConns, @dwSize, @dwCount);
  Result:= (Res = 0) and (dwCount > 0);
end;
end.

Как отправить бинарные данные из CGI приложения.

Интернетчик является утром домой. Жена спрашивает: - Где был?! - У любовницы... - Врешь, опять всю ночь в Интернете просидел!

Не для кого не секрет, как просто можно получать данные различного типа из CGI приложения. Однако, иногда необходимо, чтобы данные сохранялись в виде файла с определённым именем, типа "Test.ZIP". Для этого необходимо добавить в заголовок HTTP пункт "Content-Disposition".

В Delphi для этого используется свойство CustomHeaders. В это TStrings свойство можно добавлять пункты в виде "name=value" - так как HTTP синтакс name:value здесь не используется.

procedure TWebModule1.WebModule1CHECKSTATUSAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  s: TFileStream;
begin
  s := nil;
  if request.query='download' then
    try
      response.Title := 'Download Test.ZIP';
      response.CustomHeaders.Add ('Content-Disposition=filename=Test.zip');
      response.ContentType := 'application/zip';
      s := TFileStream.Create (fmOpenRead+fmShareDenyNone,'Test.zip');
      response.contentstream := s;
      response.sendresponse;
    finally
      s.Free;
    end;
end;

Как послать широковещательный UDP пакет.

Смотрит ламер на папку "Удаленный доступ к сети", и думает: "Как он ещё тут, когда его уже удалили?"
procedure TMainForm.FormCreate(Sender: TObject);
var
  Init: TWSAData;
  SockOpt: BOOL;
  Sock: TSocket;
  Target: TSockAddrIn;
begin
  WSAStartup($101,Init);
  Sock:=Socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
  SockOpt:=TRUE;
  SetSockOpt(Sock,SOL_SOCKET,SO_BROADCAST, PChar(@SockOpt),SizeOf(SockOpt)) ;
  Target.sin_port:=htons(8167); //номер порта
  Target.sin_addr.S_addr:=INADDR_BROADCAST;
  Target.sa_family:=AF_INET;
  SendTo(Sock,Data,DataBytes,0,Target,SizeOf(Target));
  WSACleanup;
end;

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

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

{ 
I set up a file type (.myfile) to be run by my program, and everything works 
okay. The program opens it fine. The problem is, if I double-click some file 
on my HD, it opens a new instance of my app, rather than sending the file to 
the already opened one. How can I fix this? 
}
 { 
Example for using DDE to open files from Explorer: 
  http://codecentral.borland.com/codecentral/ccweb.exe/finder 
  and search for submission ID 17787 
}
 {That is the comprehensive solution <g>. There are simpler ones but they tend 
to get problems if more than one file is opened at once from Explorer. Those 
problems can be handled, but it gets a bit complex to do that. 
Basically you proceed like this: 
In your programs main block (DPR file) you create a named global kernel 
object, e.g. a Mutex or memory-mapped file, using a unique name (e.g. a GUID). 
The first instance will create this object successfully and hold on to the 
objects handle until it closes. All further instances will detect that the 
object already exists, so know that they are not supposed to show up to the 
user. But they have to pass over any command-line they may have been handed 
from Explorer. They do that by sending a WM_COPYDATA message with the command 
line contents to the first instances main window. For that they have to find 
the windows handle, for which they use FindWindow with the main forms 
classname (which should definitely be somewhat more unique than "Form1"!). The 
problem is that the first instance may not have gotten around to creating its 
main window yet (if more than one file has been opened from Explorer and the 
program was not already running). So the second instance may have to wait in a 
loop (using Sleep to suspend itself for a little bit) until the first 
instances window shows up. 
Bits and pieces of this process have been posted many times on the groups in 
the past, but i don't remember if a solution covering all bases was among 
them. So since it is Sunday and i'm a wee bit bored let's try for a generic 
solution. The meat is in the PBOnceOnly unit given further down. I explain its 
usage first. Note that this has been tested on Windows 2000 only, and not very 
extensively either. 
In the projects DPR file you have code looking like this: 
}
 program OneInstanceDemo;
 uses
   Forms,
   Unit1 in 'Unit1.pas' {OneInstanceDemoMainform},
   PBOnceOnly;
 {$R *.res}
 const
   ProcessName = '{53F0DF5B-B69D-40B7-9B2C-A9E515CCFC80}';
 begin
   if AlreadyRunning(ProcessName, TOneInstanceDemoMainform) then
     Exit;
   Application.Initialize;
   Application.CreateForm(TOneInstanceDemoMainform, OneInstanceDemoMainform);
   Application.Run;
 end.
 {You can create a GUID for the processname via Ctrl-Shift-G in the IDE, just 
remove the enclosing square brackets. 
The main form needs a message handler for WM_COPYDATA, and also a method to 
handle a command-line parameter. The example form only shows the passed 
parameter in a memo.}
 unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   ComCtrls, Buttons, StdCtrls;
 type
   TOneInstanceDemoMainform = class(TForm)
     Memo1: TMemo;
     procedure FormCreate(Sender: TObject);
   private
     procedure WMCopyData(var msg: TWMCopyData); message WM_COPYDATA;
     procedure HandleParameter(const param: string);
   public
     { Public declarations }
   end;
 var
   OneInstanceDemoMainform: TOneInstanceDemoMainform;
 implementation
 uses PBOnceOnly;
 {$R *.DFM}
 procedure TOneInstanceDemoMainform.FormCreate(Sender: TObject);
 begin
   memo1.Text := Format('Thread ID: %x'#13#10, [GetCurrentThreadID]);
   HandleCommandline(HandleParameter);
 end;
 procedure TOneInstanceDemoMainform.HandleParameter(const param: string);
 begin
   memo1.Lines.Add(param);
 end;
 procedure TOneInstanceDemoMainform.WMCopyData(var msg: TWMCopyData);
 begin
   HandleSendCommandline(msg.CopyDataStruct^, HandleParameter);
 end;
 end.
 {The work of dissecting the passed commandline is left to the PBOnceOnly unit, 
since it "knows" how it packaged the parameters in the other instance. The 
technique used by the unit is rather simple: the first instance creates a 
memory mapped file and stores its main threads thread ID into this file. It 
cannot store the main forms handle since the form has not been created yet 
when AlreadyRunning is called. It would be a bad idea anyway since a forms 
handle can change over the form objects lifetime. The second instance gets 
this handle, uses EnumThreadWindows to find the first instances main form 
handle (doing this way avoids problems with the IDE designers form instance 
during development), packages the command line and sends it over to the found 
window. The second instance will then terminate since AlreadyRunning returns 
true in it. It never creates any of the autocreated forms or datamodules and 
never enters its message loop.}
 {== PBOnceOnly ========================================================}
 {: Implements a function to detect a running instance of the program and 
  (optionally) pass over any command line to the first instances main 
  window. 
@author Dr. Peter Below 
@desc   Version 1.0 created 2003-02-23<BR> 
        Last modified       2003-02-23<P> 
If a command line has to be passed over we need the window handle of the 
first instances main window, to send a WM_COPYDATA message to it. Since 
the first instance may not have gotten around to creating its main 
form window handle yet we retry a couple of times and wait a bit in 
between. This process can be configured by setting the MAX_RETRIES and 
RETRIES_INTERVAL variables before calling AlreadyRunning.   }
 {======================================================================}
 {$BOOLEVAL OFF} {Unit depends on shortcut boolean evaluation}
 unit PBOnceOnly;
 interface
 uses Windows;
 var
   {: Specifies how often we retry to find the first instances main 
     window. }
   MAX_RETRIES: Integer = 10;
   {: Specifies how long, in milliseconds, we sleep between retries. }
   RETRIES_INTERVAL: Integer = 1000;
 {-- AlreadyRunning ----------------------------------------------------}
 {: Checks for another instance of the program and optionally passes over 
  this instances command line. 
@Param aProcessName is a unique name to be used to identify this program. 
@Param aMainformClass is the programs main form class, can be nil. 
@Param passCommandline indicates whether to pass the command line, true 
  by default. 
@Param allowMultiuserInstances indicates whether to allow other 
  instances of the program to run in another user context. Only applies 
  to Windows terminal server or XP. True by default. 
@Returns true if there is another instance running, false if not. 
@Precondition The function has not been called already. It must only 
  be called once per program run. 
@Desc Creates a memory mapped file with the passed process name, 
  optionally with an added 'Global' prefix. If the MMF already existed 
  we know that this is a second instance. The first instance stores its 
  main thread ID into the MMF, the second one uses that with 
  EnumThreadWindows to find the first instances main window and sends 
  the command line via WM_COPYDATA to this window, if requested. 
@Raises Exception if creation of the MMF fails for some reason. 
}{ Created 2003-02-23 by P. Below 
-----------------------------------------------------------------------}
 function AlreadyRunning(const aProcessName: string;
   aMainformClass: TClass = nil;
   passCommandline: Boolean = true;
   allowMultiuserInstances: Boolean = true): Boolean;
 type
   {: Callback type used by HandleSendCommandline. The callback will 
     be handed one parameter at a time. }
   TParameterEvent = procedure(const aParam: string) of object;
 {-- HandleSendCommandline ---------------------------------------------}
 {: Dissect a command line passed via WM_COPYDATA from another instance 
@Param data contains the data received via WM_COPYDATA.
 @Param onParameter is a callback that will be called with every passed
   parameter in turn.
 @Precondition  onParameter <> nil
 }{ Created 2003-02-23 by P. Below 
-----------------------------------------------------------------------}
 procedure HandleSendCommandline(const data: TCopyDataStruct;
   onParameter: TParameterEvent);
 {-- HandleCommandline -------------------------------------------------}
 {: This is a convenience procedure that allows handling of this 
  instances command line parameters to be done the same way as 
  a command line send over from another instance. 
@Param onParameter will be called for every command line parameter in turn. 
@Precondition  onParameter <> nil 
}{ Created 2003-02-23 by P. Below 
-----------------------------------------------------------------------}
 procedure HandleCommandline(onParameter: TParameterEvent);
 implementation
 uses Messages, Classes, Sysutils;
 { The THandledObject and TShareMem classes come from the D6 IPCDemos 
  demo project. }
 type
   THandledObject = class(TObject)
   protected
     FHandle: THandle;
   public
     destructor Destroy; override;
     property Handle: THandle read FHandle;
   end;
 { This class simplifies the process of creating a region of shared memory. 
  In Win32, this is accomplished by using the CreateFileMapping and 
  MapViewOfFile functions. }
   TSharedMem = class(THandledObject)
   private
     FName: string;
     FSize: Integer;
     FCreated: Boolean;
     FFileView: Pointer;
   public
     constructor Create(const Name: string; Size: Integer);
     destructor Destroy; override;
     property Name: string read FName;
     property Size: Integer read FSize;
     property Buffer: Pointer read FFileView;
     property Created: Boolean read FCreated;
   end;
 procedure Error(const Msg: string);
 begin
   raise Exception.Create(Msg);
 end;
 { THandledObject }
 destructor THandledObject.Destroy;
 begin
   if FHandle <> 0 then
     CloseHandle(FHandle);
 end;
 { TSharedMem }
 constructor TSharedMem.Create(const Name: string; Size: Integer);
 begin
   try
     FName := Name;
     FSize := Size;
     { CreateFileMapping, when called with $FFFFFFFF for the handle value, 
      creates a region of shared memory }
     FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
       Size, PChar(Name));
     if FHandle = 0 then abort;
     FCreated := GetLastError = 0;
     { We still need to map a pointer to the handle of the shared memory region 
}
     FFileView := MapViewOfFile(FHandle, FILE_MAP_WRITE, 0, 0, Size);
     if FFileView = nil then abort;
   except
     Error(Format('Error creating shared memory %s (%d)', [Name,
       GetLastError]));
   end;
 end;
 destructor TSharedMem.Destroy;
 begin
   if FFileView <> nil then
     UnmapViewOfFile(FFileView);
   inherited Destroy;
 end;
 var
   { This object is destroyed by the unit finalization }
   ProcessInfo: TSharedMem = nil;
 { Check if we are running in a terminal client session }
 function IsRemoteSession: Boolean;
 const
   sm_RemoteSession = $1000; { from WinUser.h }
 begin
   Result := GetSystemMetrics(sm_RemoteSession) <> 0;
 end;
 { Check if we are running on XP or a newer version. XP is Windows NT 5.1 }
 function IsXP: Boolean;
 begin
   Result :=
     (Sysutils.Win32Platform = VER_PLATFORM_WIN32_NT)
     and
     ((Sysutils.Win32MajorVersion > 5)
     or
     ((Sysutils.Win32MajorVersion = 5)
     and
     (Sysutils.Win32MinorVersion > 0)
     )
     );
 end;
 { Check if we are running in a Windows terminal client session or on 
  Windows XP.  }
 function IsWTSOrXP: Boolean;
 begin
   Result := IsRemoteSession or IsXP
 end;
 type
   { Helper class to hold classname and found window handle for 
    EnumThreadWindows }
   TEnumhelper = class
   public
     FClassname: string;
     FWnd: HWND;
     constructor Create(const aClassname: string);
     function Matches(wnd: HWND): Boolean;
   end;
 constructor TEnumhelper.Create(const aClassname: string);
 begin
   inherited Create;
   FClassname := aClassname;
 end;
 function TEnumhelper.Matches(wnd: HWND): Boolean;
 var
   classname: array[0..127] of Char;
 begin
   classname[0] := #0;
   Windows.GetClassname(wnd, classname, sizeof(classname));
   Result := AnsiSametext(Fclassname, classname);
   if result then
     FWnd := wnd;
 end;
 function EnumProc(wnd: HWND; helper: TEnumHelper): BOOL; stdcall;
 begin
   Result := not helper.Matches(wnd);
 end;
 function FindFirstInstanceMainform(const aClassname: string): HWND;
 var
   threadID: DWORD;
   helper: TEnumHelper;
 begin
   threadID := PDWORD(Processinfo.FFileView)^;
   helper := TEnumHelper.Create(aclassname);
   try
     EnumThreadWindows(threadID, @EnumProc, Integer(helper));
     Result := helper.FWnd;
   finally
     helper.Free;
   end;
 end;
 function AlreadyRunning(const aProcessName: string;
   aMainformClass: TClass = nil;
   passCommandline: Boolean = true;
   allowMultiuserInstances: Boolean = true): Boolean;
   function Processname: string;
   begin
     if not allowMultiuserInstances and IsWTSorXP then
       Result := 'Global\' + aProcessName
     else
       Result := aProcessName;
   end;
   procedure StoreThreadID;
   begin
     PDWORD(ProcessInfo.FFileView)^ := GetCurrentThreadID;
   end;
   function GetCommandline: string;
   var
     sl: TStringlist;
     i: Integer;
   begin
     if ParamCount = 1 then
       Result := ParamStr(1)
     else begin
       sl := TStringlist.Create;
       try
         for i := 1 to ParamCount do
           sl.Add(ParamStr(i));
         Result := sl.Text;
       finally
         sl.free;
       end; { Finally }
     end;
   end;
   procedure DoPassCommandline;
   var
     wnd: HWND;
     S: string;
     copydata: TCopyDataStruct;
     retries: Integer;
   begin
     retries := 0;
     repeat
       wnd := FindFirstInstanceMainform(aMainformclass.Classname);
       if wnd <> 0 then
       begin
         S := GetCommandline;
         copydata.dwData := Paramcount;
         copydata.cbData := Length(S) + 1;
         copydata.lpData := PChar(S);
         SendMessage(wnd, WM_COPYDATA, 0, integer(@copydata));
       end
       else begin
         Inc(retries);
         Sleep(RETRIES_INTERVAL);
       end;
     until (wnd <> 0) or (retries > MAX_RETRIES);
   end;
 begin
   Assert(not Assigned(ProcessInfo),
     'Do not call AlreadyRunning more than once!');
   ProcessInfo := TSharedMem.Create(Processname, Sizeof(DWORD));
   Result := not ProcessInfo.Created;
   if Result then
   begin
     if passCommandline and Assigned(aMainformClass) and (ParamCount > 0) then
         DoPassCommandline;
   end
   else
     StoreThreadID;
 end;
 procedure HandleSendCommandline(const data: TCopyDataStruct;
   onParameter: TParameterEvent);
 var
   i: Integer;
   sl: TStringlist;
 begin
   Assert(Assigned(onParameter), 'OnParameter cannot be nil');
   if data.dwData = 1 then
     onParameter(PChar(data.lpData))
   else
   begin
     sl := TStringlist.Create;
     try
       sl.Text := PChar(data.lpData);
       for i := 0 to sl.Count - 1 do
         onParameter(sl[i]);
     finally
       sl.Free;
     end; { Finally }
   end;
 end;
 procedure HandleCommandline(onParameter: TParameterEvent);
 var
   i: Integer;
 begin
   Assert(Assigned(onParameter), 'OnParameter cannot be nil');
   for i := 1 to ParamCount do
     onParameter(ParamStr(i));
 end;
 initialization
 finalization
   ProcessInfo.Free;
 end.

Пересылка данных в ячейки Excel.

Автор: Mikhail Andronov
Новые компьютерные вирусы: "Виагра" - делает из вашей старой гибкой дискеты - жёсткий диск. "Монка Левински" - высасывает из вашего жёсткого диска информацию и тут же сообщает всем по сети о случившемся. "Рональд Рейган" - сохраняет все ваши данные, но забывает, где они находятся. "Борис Ельцин" - выставляет в биосе, что ваш 486 - это Р-III, обьясняет медленную скорость работы тем, что подцепил легкий вирус, постоянно обновляет системный регистр и драйвера. Проблемы 2000 для него не существует. Его дочерние версии могут тайком перекачивать деньги на зарубежные счета. "Майк Тайсон" - вырубает ваш компьютер с первых двух байтов. "Арнольд Шварцнеггер" - Terminate all programs and say -I'LL BE BACK!!! "Титаник" - показывает вам физиономию Ди-Каприо до тех пор, пока вы не утопите свой PC в ванной со льдом.
Возможно, не все знают, что время пересылки данных из своего приложения в ячейки Excel можно существенно сократить, если пересылать все значения для некоторого диапазона разом. Для этого используется вариантный массив (см. функцию VarArrayCreate). Небольшой пример, который прилагается к письму, все подробно иллюстрирует.

Привожу полностью все файлы проекта:

// *-*-*-*-*-*-*-*
// SelectToExcel.dpr
// *-*-*-*-*-*-*-*
program SelectToExcel;
uses
  Forms,
  Main in 'Main.pas' {Form1};
{$R *.RES}
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
// *-*-*-*-*-*-*-*
// Main.dfm
// *-*-*-*-*-*-*-*
object Form1: TForm1
  Left = 267
    Top = 137
    AutoScroll = False
    Caption = 'Экспорт результатов SELECT в Excel'
    ClientHeight = 277
    ClientWidth = 519
    Color = clBtnFace
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'MS Sans Serif'
    Font.Style = []
    OldCreateOrder = False
    Position = poScreenCenter
    PixelsPerInch = 96
    TextHeight = 13
    object Label1: TLabel
    Left = 8
      Top = 4
      Width = 114
      Height = 13
      Caption = 'Предложение SELECT'
  end
  object Label2: TLabel
    Left = 8
      Top = 224
      Width = 91
      Height = 13
      Caption = 'Имя базы данных'
  end
  object btnExport: TButton
    Left = 436
      Top = 20
      Width = 75
      Height = 25
      Caption = 'Экспорт'
      TabOrder = 0
      OnClick = btnExportClick
  end
  object memSelect: TMemo
    Left = 8
      Top = 20
      Width = 417
      Height = 197
      TabOrder = 1
  end
  object edtDatabaseName: TEdit
    Left = 8
      Top = 240
      Width = 413
      Height = 21
      TabOrder = 2
  end
  object queSelect: TQuery
    Left = 24
      Top = 20
  end
end
// *-*-*-*-*-*-*-*
// Main.pas
// *-*-*-*-*-*-*-*
unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Db, DBTables;
type
  TForm1 = class(TForm)
    queSelect: TQuery;
    btnExport: TButton;
    memSelect: TMemo;
    edtDatabaseName: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure btnExportClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
uses
  ComObj;
{$R *.DFM}
procedure TForm1.btnExportClick(Sender: TObject);
var
  XL, // Приложение Excel
  TableVals: Variant; // Врем. массив для переноса значений в Excel
  i, LineCounter, // Счетчик строк для переноса записей в Excel
  queSelectRecCount,
    queSelectFieldsCount: Integer;
begin
  inherited;
  try
    Application.ProcessMessages;
    Screen.Cursor := crSQLWait;
    with queSelect do
    begin
      SQL.Assign(memSelect.Lines);
      DatabaseName := edtDatabaseName.Text;
      Open;
      {AMA: Экспорт в Excel}
      queSelectRecCount := RecordCount;
      queSelectFieldsCount := FieldCount;
      TableVals := VarArrayCreate([0, queSelectRecCount - 1, //кол-во строк
        0, queSelectFieldsCount - 1], // кол-во столбцов
        varOleStr);
      First;
      LineCounter := 0;
      while not EOF do
      begin
        for i := 0 to queSelectFieldsCount - 1 do
          if not Fields[i].IsNull then
            TableVals[LineCounter, i] := Fields[i].AsString
          else
            TableVals[LineCounter, i] := '';
        LineCounter := LineCounter + 1;
        Next;
      end;
      Close;
    end;
    try
      try
        XL := GetActiveOleObject('Excel.Application');
      except
        XL := CreateOleObject('Excel.Application');
      end;
    except
      raise Exception.Create('Не могу запустить Excel');
    end;
    XL.Visible := True;
    XL.Workbooks.Add;
    XL.Range[XL.Cells[1, 1],
      XL.Cells[queSelectRecCount,
      queSelectFieldsCount]].Value := TableVals;
    XL.Range[XL.Cells[1, 1],
      XL.Cells[queSelectRecCount,
      queSelectFieldsCount]].Borders.Weight := 2;
  finally
    Screen.Cursor := crDefault;
  end;
end;
end.

Отправить E-mail.

BATMAN - человек-почтовый ящик.
procedure TForm1.Button1Click(Sender: TObject);
begin 
  NMSMTP1.Host   := 'mail.host.com'; 
  NMSMTP1.UserID := 'Username'; 
  NMSMTP1.Connect; 
  NMSMTP1.PostMessage.FromAddress       := 'your_mail@host.com';
  NMSMTP1.PostMessage.ToAddress.Text    := 'delphiworld@mail.ru';
  NMSMTP1.PostMessage.ToCarbonCopy.Text := 'your_mail@host.com';
  NMSMTP1.PostMessage.ToBlindCarbonCopy.Text := 'second_your_mail@host.com';
  NMSMTP1.PostMessage.Body.Text         := 'This is the message from Delphi World project';
  NMSMTP1.PostMessage.Attachments.Text  := 'c:\File.txt';
  NMSMTP1.PostMessage.Subject           := 'Delphi World is Great!';
  NMSMTP1.SendMail;
  ShowMessage('Mail sent !');
  NMSMTP1.Disconnect;
end;

Отправлять электронную почту.

Приходит хакер к психиатру: - Док, помоги, у меня раздвоение виртуальной личности. - Не понял, это как? - Я со своего второго ника на третий E-Маi1ы начал получать.
В D5 Вы можете попробовать использовать компоненты TNMSMTP и TNMPOP3, в D6 и Kylix компоненты от NetMasters заменены компонентами Indy.

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

необходимо заполнить структуру PostMessage; отправить эту структуру при помощи метода SendMail.

Структура PostMessage:

(*) FromName: string - имя отправителя; FromAddress: string - Почтовый адресс отправителя ReplyToAddress: string - Адрес, по которому следует ответить на письмо; Organization: string LocalProgram: string - Имя почтовой программы отправителя; (*) ToAddress: TStringList - Почтовый адрес главного получателя (ей) ToCC: TStringList - Список получателей Carbon Copy ToBCC: TSringList - Список получателей Blind Carbon Copy Attachments: TStringList - Список вложений Body: TStringList - Собственно текст письма Subject: string - Строка темы письма. (*) - помечены обязательные поля. С остальным не сложно разобраться самому. Также это можно сделать при помощи MAPI. Т.е. письмо будет отправлено средствами _почтовой_программы_по_умолчанию_ - со всеми недостатками :) (например письмо будет помещено в папку "отправленные"). Зато без всяких там компонент и просто :)

Ниже готовая функция, в которой:

From - от кого Dest - кому Subject - тема Text - текст письма FileName - путь к вложению (если '', то соответственно ничего и не вкладываем) Outlook - показывать или нет письмо перед отправкой (в твоём случае False)
Uses MAPI;
...
function SendMail(const From, Dest, Subject, Text, FileName: PChar;
Outlook: boolean):Integer;
var
  Message: TMapiMessage;
  Recipient, Sender: TMapiRecipDesc;
  File_Attachment: TMapiFileDesc;
                                                               
  function MakeMessage: TMapiMessage;
  begin
    FillChar(Sender, SizeOf(Sender), 0);
    Sender.ulRecipClass := MAPI_ORIG;
    Sender.lpszAddress := From;
                                               
    FillChar(Recipient, SizeOf(Recipient), 0);
    Recipient.ulRecipClass := MAPI_TO;
    Recipient.lpszAddress := Dest;
    FillChar(File_Attachment, SizeOf(File_Attachment), 0);
    File_Attachment.nPosition := Cardinal(-1);
    File_Attachment.lpszPathName := FileName;
    FillChar(Result, SizeOf(Result), 0);
    with Message do begin
      lpszSubject := Subject;
      lpszNoteText := Text;
      lpOriginator := @Sender;
      nRecipCount := 1;
      lpRecips := @Recipient;
      nFileCount := 1;
      lpFiles := @File_Attachment;
    end;
  end;
var
  SM: TFNMapiSendMail;
  MAPIModule: HModule;
  MAPI_FLAG: Cardinal;
begin
  if Outlook then
   MAPI_FLAG:=MAPI_DIALOG
  else
   MAPI_FLAG:=0;
  MAPIModule := LoadLibrary(PChar(MAPIDLL));
  if MAPIModule = 0 then
    Result := -1
  else
    try
      @SM := GetProcAddress(MAPIModule, 'MAPISendMail');
      if @SM <> nil then begin
        MakeMessage;
        Result := SM(0, Application.Handle, Message, MAPI_FLAG, 0);
      end else Result := 1;
    finally
      FreeLibrary(MAPIModule);
    end;
end;

Отправить письмо, чтобы пользователь об этом не знал (D7).

Автор: Прокопец Антон.

// Кинуть на форму компонент IdSMTP1
...
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdMessageClient, IdSMTP, StdCtrls, idMessage, IdEMailAddress;
...
procedure TForm1.Button1Click(Sender: TObject);
var
  M: TIdMessage;
begin
  M := TIdMessage.Create(Form1);
  M.Body.Add('Типа первая строчка собщения');
  M.Body.Add('Типа вторая строчка собщения');
  M.From.Text := '"Рустик" <boris@uacity.com>'; //отправитель
  M.Recipients.Add;
  M.Recipients.Items[0].Text := '"Тест" <antohha@ukr.net>'; //Получатель
  M.Subject := 'Ну чо? потестим? =)'; //Тема письма
  IdSMTP1.AuthenticationType := atLogin;
  IdSMTP1.Host := 'uacity.com';
  IdSMTP1.Username := 'boris';
  IdSMTP1.Password := '1234'; // - ну свой пароль я те не выдам :-Р
  IdSMTP1.Connect();
  if IdSMTP1.Connected then
  begin
    IdSMTP1.Send(M);
  end;
  IdSMTP1.Disconnect;
end;

Отправка E-mail через MAPI.

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

Автор: Eugene Mayevski.

unit MapiControl;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs;
type
  { Вводим новый тип события для получения Errorcode }
  TMapiErrEvent = procedure(Sender: TObject; ErrCode: Integer) of object;
  TMapiControl = class(TComponent)
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
    private
    { Private-объявления }
    FSubject: string;
    FMailtext: string;
    FFromName: string;
    FFromAdress: string;
    FTOAdr: TStrings;
    FCCAdr: TStrings;
    FBCCAdr: TStrings;
    FAttachedFileName: TStrings;
    FDisplayFileName: TStrings;
    FShowDialog: Boolean;
    FUseAppHandle: Boolean;
    { Error Events: }
    FOnUserAbort: TNotifyEvent;
    FOnMapiError: TMapiErrEvent;
    FOnSuccess: TNotifyEvent;
    { +> Изменения, внесённые Eugene Mayevski [mailto:Mayevski@eldos.org]}
    procedure SetToAddr(newValue : TStrings);
    procedure SetCCAddr(newValue : TStrings);
    procedure SetBCCAddr(newValue : TStrings);
    procedure SetAttachedFileName(newValue : TStrings);
    { +
  protected
    { Protected-объявления }
  public
    { Public-объявления }
    ApplicationHandle: THandle;
    procedure Sendmail();
    procedure Reset();
  published
    { Published-объявления }
    property Subject: string read FSubject write FSubject;
    property Body: string read FMailText write FMailText;
    property FromName: string read FFromName write FFromName;
    property FromAdress: string read FFromAdress write FFromAdress;
    property Recipients: TStrings read FTOAdr write SetTOAddr;
    property CopyTo: TStrings read FCCAdr write SetCCAddr;
    property BlindCopyTo: TStrings read FBCCAdr write SetBCCAddr;
    property AttachedFiles: TStrings read FAttachedFileName write SetAttachedFileName;
    property DisplayFileName: TStrings read FDisplayFileName;
    property ShowDialog: Boolean read FShowDialog write FShowDialog;
    property UseAppHandle: Boolean read FUseAppHandle write FUseAppHandle;
    { события: }
    property OnUserAbort: TNotifyEvent read FOnUserAbort write FOnUserAbort;
    property OnMapiError: TMapiErrEvent read FOnMapiError write FOnMapiError;
    property OnSuccess: TNotifyEvent read FOnSuccess write FOnSuccess;
end;
procedure register;
implementation
uses Mapi;
{ регистрируем компонент: }
procedure register;
begin
  RegisterComponents('expectIT', [TMapiControl]);
end;
{ TMapiControl }
constructor TMapiControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FOnUserAbort := nil;
  FOnMapiError := nil;
  FOnSuccess := nil;
  FSubject := '';
  FMailtext := '';
  FFromName := '';
  FFromAdress := '';
  FTOAdr := TStringList.Create;
  FCCAdr := TStringList.Create;
  FBCCAdr := TStringList.Create;
  FAttachedFileName := TStringList.Create;
  FDisplayFileName := TStringList.Create;
  FShowDialog := False;
  ApplicationHandle := Application.Handle;
end;
{ +> Изменения, внесённые Eugene Mayevski [mailto:Mayevski@eldos.org]}
procedure TMapiControl.SetToAddr(newValue : TStrings);
begin
  FToAdr.Assign(newValue);
end;
procedure TMapiControl.SetCCAddr(newValue : TStrings);
begin
  FCCAdr.Assign(newValue);
end;
procedure TMapiControl.SetBCCAddr(newValue : TStrings);
begin
  FBCCAdr.Assign(newValue);
end;
procedure TMapiControl.SetAttachedFileName(newValue : TStrings);
begin
  FAttachedFileName.Assign(newValue);
end;
{ +
destructor TMapiControl.Destroy;
begin
  FTOAdr.Free;
  FCCAdr.Free;
  FBCCAdr.Free;
  FAttachedFileName.Free;
  FDisplayFileName.Free;
  inherited destroy;
end;
{ Сбрасываем все используемые поля}
procedure TMapiControl.Reset;
begin
  FSubject := '';
  FMailtext := '';
  FFromName := '';
  FFromAdress := '';
  FTOAdr.Clear;
  FCCAdr.Clear;
  FBCCAdr.Clear;
  FAttachedFileName.Clear;
  FDisplayFileName.Clear;
end;
{ Эта процедура составляет и отправляет Email }
procedure TMapiControl.Sendmail;
var
  MapiMessage: TMapiMessage;
  MError: Cardinal;
  Sender: TMapiRecipDesc;
  PRecip, Recipients: PMapiRecipDesc;
  PFiles, Attachments: PMapiFileDesc;
  i: Integer;
  AppHandle: THandle;
begin
  { Перво-наперво сохраняем Handle приложения, if not
  the Component might fail to send the Email or
  your calling Program gets locked up. }
  AppHandle := Application.Handle;
  { Нам нужно зарезервировать память для всех получателей }
  MapiMessage.nRecipCount := FTOAdr.Count + FCCAdr.Count + FBCCAdr.Count;
  GetMem(Recipients, MapiMessage.nRecipCount * sizeof(TMapiRecipDesc));
  try
    with MapiMessage do
    begin
      ulReserved := 0;
      { Устанавливаем поле Subject: }
      lpszSubject := PChar(Self.FSubject);
      { ... Body: }
      lpszNoteText := PChar(FMailText);
      lpszMessageType := nil;
      lpszDateReceived := nil;
      lpszConversationID := nil;
      flFlags := 0;
      { и отправителя: (MAPI_ORIG) }
      Sender.ulReserved := 0;
      Sender.ulRecipClass := MAPI_ORIG;
      Sender.lpszName := PChar(FromName);
      Sender.lpszAddress := PChar(FromAdress);
      Sender.ulEIDSize := 0;
      Sender.lpEntryID := nil;
      lpOriginator := @Sender;
      PRecip := Recipients;
      { У нас много получателей письма: (MAPI_TO)
      установим для каждого: }
      if nRecipCount > 0 then
      begin
        for i := 1 to FTOAdr.Count do
        begin
          PRecip^.ulReserved := 0;
          PRecip^.ulRecipClass := MAPI_TO;
          { lpszName should carry the Name like in the
          contacts or the adress book, I will take the
          email adress to keep it short: }
          PRecip^.lpszName := PChar(FTOAdr.Strings[i - 1]);
          { Если Вы используете этот компонент совместно с Outlook97 или 2000
          (не Express версии) , то Вам прийдётся добавить
          'SMTP:' в начало каждого (email-) адреса.
          }
          PRecip^.lpszAddress := PChar('SMTP:' + FTOAdr.Strings[i - 1]);
          PRecip^.ulEIDSize := 0;
          PRecip^.lpEntryID := nil;
          Inc(PRecip);
        end;
        { То же самое проделываем с получателями копии письма: (CC, MAPI_CC) }
        for i := 1 to FCCAdr.Count do
        begin
          PRecip^.ulReserved := 0;
          PRecip^.ulRecipClass := MAPI_CC;
          PRecip^.lpszName := PChar(FCCAdr.Strings[i - 1]);
          PRecip^.lpszAddress := PChar('SMTP:' + FCCAdr.Strings[i - 1]);
          PRecip^.ulEIDSize := 0;
          PRecip^.lpEntryID := nil;
          Inc(PRecip);
        end;
        { ... тоже самое для Bcc: (BCC, MAPI_BCC) }
        for i := 1 to FBCCAdr.Count do
        begin
          PRecip^.ulReserved := 0;
          PRecip^.ulRecipClass := MAPI_BCC;
          PRecip^.lpszName := PChar(FBCCAdr.Strings[i - 1]);
          PRecip^.lpszAddress := PChar('SMTP:' + FBCCAdr.Strings[i - 1]);
          PRecip^.ulEIDSize := 0;
          PRecip^.lpEntryID := nil;
          Inc(PRecip);
        end;
      end;
      lpRecips := Recipients;
      { Теперь обработаем прикреплённые к письму файлы: }
      if FAttachedFileName.Count > 0 then
      begin
        nFileCount := FAttachedFileName.Count;
        GetMem(Attachments, MapiMessage.nFileCount * sizeof(TMapiFileDesc));
        PFiles := Attachments;
        { Во первых установим отображаемые на экране имена файлов (без пути): }
        FDisplayFileName.Clear;
        for i := 0 to FAttachedFileName.Count - 1 do
          FDisplayFileName.Add(ExtractFileName(FAttachedFileName[i]));
        if nFileCount > 0 then
        begin
          { Теперь составим структурку для прикреплённого файла: }
          for i := 1 to FAttachedFileName.Count do
          begin
            { Устанавливаем полный путь }
            Attachments^.lpszPathName := PChar(FAttachedFileName.Strings[i - 1]);
            { ... и имя, отображаемое на дисплее: }
            Attachments^.lpszFileName := PChar(FDisplayFileName.Strings[i - 1]);
            Attachments^.ulReserved := 0;
            Attachments^.flFlags := 0;
            { Положение должно быть -1, за разьяснениями обращайтесь в WinApi Help. }
            Attachments^.nPosition := Cardinal(-1);
            Attachments^.lpFileType := nil;
            Inc(Attachments);
          end;
        end;
        lpFiles := PFiles;
      end
      else
      begin
        nFileCount := 0;
        lpFiles := nil;
      end;
    end;
    {
    Send the Mail, silent or verbose:
    Verbose means in Express a Mail is composed and shown as setup.
    In non-Express versions we show the Login-Dialog for a new
    session and after we have choosen the profile to use, the
    composed email is shown before sending
    Silent does currently not work for non-Express version. We have
    no Session, no Login Dialog so the system refuses to compose a
    new email. In Express Versions the email is sent in the
    background.
    }
    if FShowDialog then
      MError := MapiSendMail(0, AppHandle, MapiMessage, MAPI_DIALOG 
      or MAPI_LOGON_UI or MAPI_NEW_SESSION, 0)
    else
      MError := MapiSendMail(0, AppHandle, MapiMessage, 0, 0);
    {
    Теперь обработаем сообщения об ошибках. В MAPI их присутствует достаточное.
    количество. В этом примере я обрабатываю только два из них: USER_ABORT и SUCCESS,
    относящиеся к специальным.
    Сообщения, не относящиеся к специальным:
    MAPI_E_AMBIGUOUS_RECIPIENT,
    MAPI_E_ATTACHMENT_NOT_FOUND,
    MAPI_E_ATTACHMENT_OPEN_FAILURE,
    MAPI_E_BAD_RECIPTYPE,
    MAPI_E_FAILURE,
    MAPI_E_INSUFFICIENT_MEMORY,
    MAPI_E_LOGIN_FAILURE,
    MAPI_E_TEXT_TOO_LARGE,
    MAPI_E_TOO_MANY_FILES,
    MAPI_E_TOO_MANY_RECIPIENTS,
    MAPI_E_UNKNOWN_RECIPIENT:
    }
    case MError of
      MAPI_E_USER_ABORT:
      begin
        if Assigned(FOnUserAbort) then
          FOnUserAbort(Self);
      end;
      SUCCESS_SUCCESS:
      begin
        if Assigned(FOnSuccess) then
          FOnSuccess(Self);
      end
      else
      begin
        if Assigned(FOnMapiError) then
          FOnMapiError(Self, MError);
      end;
    end;
  finally
    { В заключение освобождаем память }
    FreeMem(Recipients, MapiMessage.nRecipCount * sizeof(TMapiRecipDesc));
  end;
end;
{ Вопросы и замечания присылайте Автору. }
end.

Передать файл через Socket соединение.

Статьи по Delphi (015)…
- Ну, ты молодец! Вчера сделал больше чем за целый месяц!! - Да просто у меня Интернет не работал!

Для данной цели можно воспользоваться компонентами TNMStrmServ и TNMStrm, которые предназначены для обмена потоками данных.

Обработчик для клиента:

var
  MyStream: TMemoryStream;
begin
  MyStream := TMemoryStream.Create;
  MyStream.LoadFromFile('c:\windоws\рабочий стол\DеlрhiWоrld.txt');
  NMStrm1.PostIt(MyStream);
  MyStream.Free;
end;

Обработчик для сервера:

procedure TForm1.NMStrmServ1MSG(Sender: TComponent;
const sFrom: string; strm: TStream);
var
  MyStream: TMemoryStream;
begin
  MyStream := TMemoryStream.Create;
  MyStream.CopyFrom(strm, NMStrmServ1.BytesTotal);
  MyStream.SaveToFile('c:\windоws\рабочий стол\DеlрhiWоrld2.txt');
end;

Переслать файл в Корзину.

Статьи по Delphi (015)…
- С 25 - Ранен. - С 26 - Ранен. - С 27 - Файл win. com на диске С: убит!

Сначала нужно в директиве uses подключить модуль ShellAPI, чтобы мы смогли воспользоваться API-функцией SHFileOperation(). А затем уже можно использовать такой вот процедурой:

procedure SendToPomoyka(FileName: string);
var
  SHF: TSHFileOpStruct;
begin
  with SHF do
  begin
    Wnd := Application.Handle;
    wFunc := FO_DELETE;
    pFrom := PChar(FileName);
    fFlags := FOF_SILENT or FOF_ALLOWUNDO;
  end;
  SHFileOperation(SHF);
end;

Как передать фокус следующему контролу.

Perform(WM_NEXTDLGCTL, 0, 0);

Отправить сообщение на ICQ.

Обменяю 7-значный UIN ICQ на два 6-значных.
{ 
  You need 3 TEdits, 1 TMemo und 1 TClientSocket. 
  Set the  TClientsocket's Port to 80 and the Host to wwp.mirabilis.com. 
} 
var 
  Form1: TForm1; 
  csend: string; 
implementation 
{$R *.dfm} 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  cSend := 'POST http://wwp.icq.com/scripts/WWPMsg.dll HTTP/2.0' + chr(13) + chr(10); 
  cSend := cSend + 'Referer: http://wwp.mirabilis.com' + chr(13) + chr(10); 
  cSend := cSend + 'User-Agent: Mozilla/4.06 (Win95; I)' + chr(13) + chr(10); 
  cSend := cSend + 'Connection: Keep-Alive' + chr(13) + chr(10); 
  cSend := cSend + 'Host: wwp.mirabilis.com:80' + chr(13) + chr(10); 
  cSend := cSend + 'Content-type: application/x-www-form-urlencoded' + chr(13) + chr(10); 
  cSend := cSend + 'Content-length:8000' + chr(13) + chr(10); 
  cSend := cSend + 'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*' + 
    chr(13) + chr(10) + chr(13) + chr(10); 
  cSend := cSend + 'from=' + edit1.Text + ' &fromemail=' + edit2.Text + 
    ' &fromicq:110206786' + ' &body=' + memo1.Text + ' &to=' + edit3.Text + '&Send='; 
  clientsocket1.Active := True; 
end; 
procedure TForm1.ClientSocket1Connect(Sender: TObject; 
  Socket: TCustomWinSocket); 
begin 
  clientsocket1.Socket.SendText(csend); 
  clientsocket1.Active := False; 
end;

Посылка кода клавиши или текста в окно.

unit Unit1;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    AppInst: THandle;
    AppWind: THandle;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
uses ShellAPI;
procedure SendShift(H: HWnd; Down: Boolean);
var
  vKey, ScanCode, wParam: Word;
  lParam: longint;
begin
  vKey := $10;
  ScanCode := MapVirtualKey(vKey, 0);
  wParam := vKey or ScanCode shl 8;
  lParam := longint(ScanCode) shl 16 or 1;
  if not (Down) then
    lParam := lParam or $C0000000;
  SendMessage(H, WM_KEYDOWN, vKey, lParam);
end;
procedure SendCtrl(H: HWnd; Down: Boolean);
var
  vKey, ScanCode, wParam: Word;
  lParam: longint;
begin
  vKey := $11;
  ScanCode := MapVirtualKey(vKey, 0);
  wParam := vKey or ScanCode shl 8;
  lParam := longint(ScanCode) shl 16 or 1;
  if not (Down) then
    lParam := lParam or $C0000000;
  SendMessage(H, WM_KEYDOWN, vKey, lParam);
end;
procedure SendKey(H: Hwnd; Key: char);
var
  vKey, ScanCode, wParam: Word;
  lParam, ConvKey: longint;
  Shift, Ctrl: boolean;
begin
  ConvKey := OemKeyScan(ord(Key));
  Shift := (ConvKey and $00020000) <> 0;
  Ctrl := (ConvKey and $00040000) <> 0;
  ScanCode := ConvKey and $000000FF or $FF00;
  vKey := ord(Key);
  wParam := vKey;
  lParam := longint(ScanCode) shl 16 or 1;
  if Shift then
    SendShift(H, true);
  if Ctrl then
    SendCtrl(H, true);
  SendMessage(H, WM_KEYDOWN, vKey, lParam);
  SendMessage(H, WM_CHAR, vKey, lParam);
  lParam := lParam or $C0000000;
  SendMessage(H, WM_KEYUP, vKey, lParam);
  if Shift then
    SendShift(H, false);
  if Ctrl then
    SendCtrl(H, false);
end;
function EnumFunc(Handle: HWnd; TF: TForm1): Bool; far;
begin
  TF.AppWind := 0;
  if GetWindowWord(Handle, GWW_HINSTANCE) = TF.AppInst then
    TF.AppWind := Handle;
  result := (TF.AppWind = 0);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Text: array[0..255] of char;
begin
  AppInst := ShellExecute(Handle, 'open', 'notepad.exe', nil, '', SW_NORMAL);
  EnumWindows(@EnumFunc, longint(self));
  AppWind := GetWindow(AppWind, GW_CHILD);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  SendKey(AppWind, 'T');
  SendKey(AppWind, 'e');
  SendKey(AppWind, 's');
  SendKey(AppWind, 't');
end;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if AppWind <> 0 then
    SendKey(AppWind, Key);
end;
end.

Как посылать нажатие клавиш в элемент управления.

Статьи по Delphi (015)…
Фирма Microsoft выпустила "Windows for woman" Основное отличие продукта в том, что две кнопки- "Yes" и "No" заменены одной- "Maybe..."

Ниже приведена процедура, позволяющаю отправлять нажатия в любой элемент управления (window control), способный принимать ввод с клавиатуры. Вы можете использовать эту технику чтобы включать клавиши NumLock, CapsLock и ScrollLock под Windows NT. Та же техника работает и под Windows 95 для CapsLock и ScrollLock но не работает для клавиши NumLock.

Обратите внимание, что приведены четыре поцедуры: SimulateKeyDown() - эмулировать нажатие клавиши (без отпускания) SimulateKeyUp() - эмулировать отпускание клавиши SimulateKeystroke() - эмулировать удар по клавише (нажатие и отпускание) и SendKeys(), позволяющие Вам гибко контролировать посылаемые сообщения клавиатуры.

SimulateKeyDown(), SimulateKeyUp() и SimulateKeystroke() получают коды виртуальных клавиш (virtural key) (вроде VK_F1). Процедура SimulateKeystroke() получает дополнительный параметр, полезный при эмуляции нажатия PrintScreen. Когда этот параметр равен нулю весь экран будет скопирован в буфер обмена (clipboard). Если дополнительный параметр равен 1 будет скопированно только активное окно.

Четыре метода "button click" демонстрируют использование: ButtonClick1 - включает capslock ButtonClick2 - перехватывает весь экран в буфер обмена (clipboard). ButtonClick3 - перехватывает активное окно в буфер обмена (clipboard). ButtonClick4 - устанавливает фокус в Edit и отправляет в него строку.

procedure SimulateKeyDown(Key: byte);
begin
  keybd_event(Key, 0, 0, 0);
end;
procedure SimulateKeyUp(Key: byte);
begin
  keybd_event(Key, 0, KEYEVENTF_KEYUP, 0);
end;
procedure SimulateKeystroke(Key: byte; extra: DWORD);
begin
  keybd_event(Key, extra, 0, 0);
  keybd_event(Key, extra, KEYEVENTF_KEYUP, 0);
end;
procedure SendKeys(s: string);
var
  i: integer;
  flag: bool;
  w: word;
begin
  {Get the state of the caps lock key}
  flag := not GetKeyState(VK_CAPITAL) and 1 = 0;
  {If the caps lock key is on then turn it off}
  if flag then
    SimulateKeystroke(VK_CAPITAL, 0);
  for i := 1 to Length(s) do
  begin
    w := VkKeyScan(s[i]);
    {If there is not an error in the key translation}
    if ((HiByte(w) $FF) and (LoByte(w) $FF)) then
    begin
      {If the key requires the shift key down - hold it down}
      if HiByte(w) and 1 = 1 then
        SimulateKeyDown(VK_SHIFT);
      {Send the VK_KEY}
      SimulateKeystroke(LoByte(w), 0);
      {If the key required the shift key down - release it}
      if HiByte(w) and 1 = 1 then
        SimulateKeyUp(VK_SHIFT);
    end;
  end;
  {if the caps lock key was on at start, turn it back on}
  if flag then
    SimulateKeystroke(VK_CAPITAL, 0);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  {Toggle the cap lock}
  SimulateKeystroke(VK_CAPITAL, 0);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  {Capture the entire screen to the clipboard}
  {by simulating pressing the PrintScreen key}
  SimulateKeystroke(VK_SNAPSHOT, 0);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
  {Capture the active window to the clipboard}
  {by simulating pressing the PrintScreen key}
  SimulateKeystroke(VK_SNAPSHOT, 1);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
  {Set the focus to a window (edit control) and send it a string}
  Application.ProcessMessages;
  Edit1.SetFocus;
  SendKeys('Delphi World is REALY BEST');
end;

Послать нажатие клавиш.

Автор: Xavier Pacheco

unit Main;
interface
uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Menus;
type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    Button2: TButton;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Open1: TMenuItem;
    Exit1: TMenuItem;
    Button4: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Open1Click(Sender: TObject);
    procedure Exit1Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
uses SendKey, KeyDefs;
procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.SetFocus; // focus Edit1
  SendKeys('^{DELETE}I love...'); // send keys to Edit1
  WaitForHook; // let keys playback
  Perform(WM_NEXTDLGCTL, 0, 0); // move to Edit2
  SendKeys('~delphi ~developer''s ~guide!'); // send keys to Edit2
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  H: hWnd;
  PI: TProcessInformation;
  SI: TStartupInfo;
begin
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  { Invoke notepad }
  if CreateProcess(nil, 'notepad', nil, nil, False, 0, nil, nil, SI,
    PI) then
  begin
    { wait until notepad is ready to receive keystrokes }
    WaitForInputIdle(PI.hProcess, INFINITE);
    { find new notepad window }
    H := FindWindow('Notepad', 'Untitled - Notepad');
    if SetForegroundWindow(H) then // bring it to front
      SendKeys('Hello from the Delphi Developer''s Guide SendKeys ' +
        'example!{ENTER}'); // send keys!
  end
  else
    MessageDlg(Format('Failed to invoke Notepad.  Error code %d',
      [GetLastError]), mtError, [mbOk], 0);
end;
procedure TForm1.Open1Click(Sender: TObject);
begin
  ShowMessage('Open');
end;
procedure TForm1.Exit1Click(Sender: TObject);
begin
  Close;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
  WaitForInputIdle(GetCurrentProcess, INFINITE);
  SendKeys('@fx');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  WaitForHook;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
  WaitForInputIdle(GetCurrentProcess, INFINITE);
  SendKeys('@fo');
end;
end.
Скачать пример

Эмуляция нажатия клавиши для любого активного приложения.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Эмуляция нажатия клавиши
Функция SendKeys этого юнита, эмулиреут нажатие клавиши для лююого активного приложения
Для активизации приложения ивпользуйте функцию AppActivate
Зависимости: SysUtils, Windows, messages
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   Автор неизвестен
Дата:        19 июня 2002 г.
***************************************************** }
unit SKUnit;
interface
uses SysUtils, Windows, messages;
function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;
function AppActivate(WindowName: PChar): boolean;
const
  WorkBufLen = 40;
var
  WorkBuf: array[0..WorkBufLen] of Char;
implementation
type
  THKeys = array[0..pred(MaxLongInt)] of byte;
var
  AllocationSize: integer;
  (*
  Converts a string of characters and key names to keyboard events and
  passes them to Windows.
  Example syntax:
  SendKeys('abc123{left}{left}{left}def{end}456{left 6}ghi{end}789', True);
  *)
function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;
type
  WBytes = array[0..pred(SizeOf(Word))] of Byte;
  TSendKey = record
    Name: ShortString;
    VKey: Byte;
  end;
const
  {Array of keys that SendKeys recognizes.
  If you add to this list, you must be sure to keep it sorted alphabetically
  by Name because a binary search routine is used to scan it.}
  MaxSendKeyRecs = 41;
  SendKeyRecs: array[1..MaxSendKeyRecs] of TSendKey =
  (
    (Name: 'BKSP'; VKey: VK_BACK),
    (Name: 'BS'; VKey: VK_BACK),
    (Name: 'BACKSPACE'; VKey: VK_BACK),
    (Name: 'BREAK'; VKey: VK_CANCEL),
    (Name: 'CAPSLOCK'; VKey: VK_CAPITAL),
    (Name: 'CLEAR'; VKey: VK_CLEAR),
    (Name: 'DEL'; VKey: VK_DELETE),
    (Name: 'DELETE'; VKey: VK_DELETE),
    (Name: 'DOWN'; VKey: VK_DOWN),
    (Name: 'END'; VKey: VK_END),
    (Name: 'ENTER'; VKey: VK_RETURN),
    (Name: 'ESC'; VKey: VK_ESCAPE),
    (Name: 'ESCAPE'; VKey: VK_ESCAPE),
    (Name: 'F1'; VKey: VK_F1),
    (Name: 'F10'; VKey: VK_F10),
    (Name: 'F11'; VKey: VK_F11),
    (Name: 'F12'; VKey: VK_F12),
    (Name: 'F13'; VKey: VK_F13),
    (Name: 'F14'; VKey: VK_F14),
    (Name: 'F15'; VKey: VK_F15),
    (Name: 'F16'; VKey: VK_F16),
    (Name: 'F2'; VKey: VK_F2),
    (Name: 'F3'; VKey: VK_F3),
    (Name: 'F4'; VKey: VK_F4),
    (Name: 'F5'; VKey: VK_F5),
    (Name: 'F6'; VKey: VK_F6),
    (Name: 'F7'; VKey: VK_F7),
    (Name: 'F8'; VKey: VK_F8),
    (Name: 'F9'; VKey: VK_F9),
    (Name: 'HELP'; VKey: VK_HELP),
    (Name: 'HOME'; VKey: VK_HOME),
    (Name: 'INS'; VKey: VK_INSERT),
    (Name: 'LEFT'; VKey: VK_LEFT),
    (Name: 'NUMLOCK'; VKey: VK_NUMLOCK),
    (Name: 'PGDN'; VKey: VK_NEXT),
    (Name: 'PGUP'; VKey: VK_PRIOR),
    (Name: 'PRTSC'; VKey: VK_PRINT),
    (Name: 'RIGHT'; VKey: VK_RIGHT),
    (Name: 'SCROLLLOCK'; VKey: VK_SCROLL),
    (Name: 'TAB'; VKey: VK_TAB),
    (Name: 'UP'; VKey: VK_UP)
    );
  {Extra VK constants missing from Delphi's Windows API interface}
  VK_NULL = 0;
  VK_SemiColon = 186;
  VK_Equal = 187;
  VK_Comma = 188;
  VK_Minus = 189;
  VK_Period = 190;
  VK_Slash = 191;
  VK_BackQuote = 192;
  VK_LeftBracket = 219;
  VK_BackSlash = 220;
  VK_RightBracket = 221;
  VK_Quote = 222;
  VK_Last = VK_Quote;
  ExtendedVKeys: set of byte =
  [VK_Up,
    VK_Down,
    VK_Left,
    VK_Right,
    VK_Home,
    VK_End,
    VK_Prior, {PgUp}
  VK_Next, {PgDn}
  VK_Insert,
    VK_Delete];
const
  INVALIDKEY = $FFFF {Unsigned -1};
  VKKEYSCANSHIFTON = $01;
  VKKEYSCANCTRLON = $02;
  VKKEYSCANALTON = $04;
  UNITNAME = 'SendKeys';
var
  UsingParens, ShiftDown, ControlDown, AltDown, FoundClose: Boolean;
  PosSpace: Byte;
  I, L: Integer;
  NumTimes, MKey: Word;
  KeyString: string[20];
  procedure DisplayMessage(Message: PChar);
  begin
    MessageBox(0, Message, UNITNAME, 0);
  end;
  function BitSet(BitTable, BitMask: Byte): Boolean;
  begin
    Result := ByteBool(BitTable and BitMask);
  end;
  procedure SetBit(var BitTable: Byte; BitMask: Byte);
  begin
    BitTable := BitTable or Bitmask;
  end;
  procedure KeyboardEvent(VKey, ScanCode: Byte; Flags: Longint);
  var
    KeyboardMsg: TMsg;
  begin
    keybd_event(VKey, ScanCode, Flags, 0);
    if (Wait) then
      while (PeekMessage(KeyboardMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do
      begin
        TranslateMessage(KeyboardMsg);
        DispatchMessage(KeyboardMsg);
      end;
  end;
  procedure SendKeyDown(VKey: Byte; NumTimes: Word; GenUpMsg: Boolean);
  var
    Cnt: Word;
    ScanCode: Byte;
    NumState: Boolean;
    KeyBoardState: TKeyboardState;
  begin
    if (VKey = VK_NUMLOCK) then
    begin
      NumState := ByteBool(GetKeyState(VK_NUMLOCK) and 1);
      GetKeyBoardState(KeyBoardState);
      if NumState then
        KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] and not 1)
      else
        KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] or 1);
      SetKeyBoardState(KeyBoardState);
      exit;
    end;
    ScanCode := Lo(MapVirtualKey(VKey, 0));
    for Cnt := 1 to NumTimes do
      if (VKey in ExtendedVKeys) then
      begin
        KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
        if (GenUpMsg) then
          KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
      end
      else
      begin
        KeyboardEvent(VKey, ScanCode, 0);
        if (GenUpMsg) then
          KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
      end;
  end;
  procedure SendKeyUp(VKey: Byte);
  var
    ScanCode: Byte;
  begin
    ScanCode := Lo(MapVirtualKey(VKey, 0));
    if (VKey in ExtendedVKeys) then
      KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)
    else
      KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
  end;
  procedure SendKey(MKey: Word; NumTimes: Word; GenDownMsg: Boolean);
  begin
    if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then
      SendKeyDown(VK_SHIFT, 1, False);
    if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then
      SendKeyDown(VK_CONTROL, 1, False);
    if (BitSet(Hi(MKey), VKKEYSCANALTON)) then
      SendKeyDown(VK_MENU, 1, False);
    SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
    if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then
      SendKeyUp(VK_SHIFT);
    if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then
      SendKeyUp(VK_CONTROL);
    if (BitSet(Hi(MKey), VKKEYSCANALTON)) then
      SendKeyUp(VK_MENU);
  end;
  {Implements a simple binary search to locate special key name strings}
  function StringToVKey(KeyString: ShortString): Word;
  var
    Found, Collided: Boolean;
    Bottom, Top, Middle: Byte;
  begin
    Result := INVALIDKEY;
    Bottom := 1;
    Top := MaxSendKeyRecs;
    Found := false;
    Middle := (Bottom + Top) div 2;
    repeat
      Collided := ((Bottom = Middle) or (Top = Middle));
      if (KeyString = SendKeyRecs[Middle].Name) then
      begin
        Found := True;
        Result := SendKeyRecs[Middle].VKey;
      end
      else
      begin
        if (KeyString > SendKeyRecs[Middle].Name) then
          Bottom := Middle
        else
          Top := Middle;
        Middle := (Succ(Bottom + Top)) div 2;
      end;
    until (Found or Collided);
    if (Result = INVALIDKEY) then
      DisplayMessage('Invalid Key Name');
  end;
  procedure PopUpShiftKeys;
  begin
    if (not UsingParens) then
    begin
      if ShiftDown then
        SendKeyUp(VK_SHIFT);
      if ControlDown then
        SendKeyUp(VK_CONTROL);
      if AltDown then
        SendKeyUp(VK_MENU);
      ShiftDown := false;
      ControlDown := false;
      AltDown := false;
    end;
  end;
begin
  AllocationSize := MaxInt;
  Result := false;
  UsingParens := false;
  ShiftDown := false;
  ControlDown := false;
  AltDown := false;
  I := 0;
  L := StrLen(SendKeysString);
  if (L > AllocationSize) then
    L := AllocationSize;
  if (L = 0) then
    Exit;
  case SendKeysString[I] of
    '(':
      begin
        UsingParens := True;
        Inc(I);
      end;
    ')':
      begin
        UsingParens := False;
        PopUpShiftKeys;
        Inc(I);
      end;
    '%':
      begin
        AltDown := True;
        SendKeyDown(VK_MENU, 1, False);
        Inc(I);
      end;
    '+':
      begin
        ShiftDown := True;
        SendKeyDown(VK_SHIFT, 1, False);
        Inc(I);
      end;
    '^':
      begin
        ControlDown := True;
        SendKeyDown(VK_CONTROL, 1, False);
        Inc(I);
      end;
    '{':
      begin
        NumTimes := 1;
        if (SendKeysString[Succ(I)] = '{') then
        begin
          MKey := VK_LEFTBRACKET;
          SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);
          SendKey(MKey, 1, True);
          PopUpShiftKeys;
          Inc(I, 3);
          // Continue;
        end;
        KeyString := '';
        FoundClose := False;
        while (I <= L) do
        begin
          Inc(I);
          if (SendKeysString[I] = '}') then
          begin
            FoundClose := True;
            Inc(I);
            Break;
          end;
          KeyString := KeyString + Upcase(SendKeysString[I]);
        end;
        if (not FoundClose) then
        begin
          DisplayMessage('No Close');
          Exit;
        end;
        if (SendKeysString[I] = '}') then
        begin
          MKey := VK_RIGHTBRACKET;
          SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);
          SendKey(MKey, 1, True);
          PopUpShiftKeys;
          Inc(I);
          // Continue;
        end;
        PosSpace := Pos(' ', KeyString);
        if (PosSpace <> 0) then
        begin
          NumTimes := StrToInt(Copy(KeyString, Succ(PosSpace), Length(KeyString)
            - PosSpace));
          KeyString := Copy(KeyString, 1, Pred(PosSpace));
        end;
        if (Length(KeyString) = 1) then
          MKey := vkKeyScan(KeyString[1])
        else
          MKey := StringToVKey(KeyString);
        if (MKey <> INVALIDKEY) then
        begin
          SendKey(MKey, NumTimes, True);
          PopUpShiftKeys;
          // Continue;
        end;
      end;
    '~':
      begin
        SendKeyDown(VK_RETURN, 1, True);
        PopUpShiftKeys;
        Inc(I);
      end;
  else
    begin
      MKey := vkKeyScan(SendKeysString[I]);
      if (MKey <> INVALIDKEY) then
      begin
        SendKey(MKey, 1, True);
        PopUpShiftKeys;
      end
      else
        DisplayMessage('Invalid KeyName');
      Inc(I);
    end;
  end;
  Result := true;
  PopUpShiftKeys;
end;
{AppActivate
This is used to set the current input focus to a given window using its
name. This is especially useful for ensuring a window is active before
sending it input messages using the SendKeys function. You can specify
a window's name in its entirety, or only portion of it, beginning from
the left.
}
var
  WindowHandle: HWND;
function EnumWindowsProc(WHandle: HWND; lParam: LPARAM): BOOL; export; stdcall;
const
  MAX_WINDOW_NAME_LEN = 80;
var
  WindowName: array[0..MAX_WINDOW_NAME_LEN] of char;
begin
  {Can't test GetWindowText's return value since some windows don't have a title}
  GetWindowText(WHandle, WindowName, MAX_WINDOW_NAME_LEN);
  Result := (StrLIComp(WindowName, PChar(lParam), StrLen(PChar(lParam))) <> 0);
  if (not Result) then
    WindowHandle := WHandle;
end;
function AppActivate(WindowName: PChar): boolean;
begin
  try
    Result := true;
    WindowHandle := FindWindow(nil, WindowName);
    if (WindowHandle = 0) then
      EnumWindows(@EnumWindowsProc, Integer(PChar(WindowName)));
    if (WindowHandle <> 0) then
    begin
      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_HOTKEY, WindowHandle);
      SendMessage(WindowHandle, WM_SYSCOMMAND, SC_RESTORE, WindowHandle);
    end
    else
      Result := false;
  except
    on Exception do
      Result := false;
  end;
end;
end.

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

SendKeys('A', False); 

Посылаем нажатия клавиш другому приложению.

Автор: Gert v.d. Venis WEB-сайт: http://www.sources.ru

Компонент Sendkeys:

unit SendKeys;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
  TSendKeys = class(TComponent)
  private
    fhandle: HWND;
    L: Longint;
    fchild: boolean;
    fChildText: string;
    procedure SetIsChildWindow(const Value: boolean);
    procedure SetChildText(const Value: string);
    procedure SetWindowHandle(const Value: HWND);
  protected
  public
  published
    procedure GetWindowHandle(Text: string);
    procedure SendKeys(buffer: string);
    property WindowHandle: HWND read fhandle write SetWindowHandle;
    property IsChildWindow: boolean read fchild write SetIsChildWindow;
    property ChildWindowText: string read fChildText write SetChildText;
  end;
procedure Register;
implementation
var
  temps: string; {й utilizado para ser acessivel pelas funcs q sao
  utilizadas como callbacks}
  HTemp: Hwnd;
  ChildText: string;
  ChildWindow: boolean;
procedure Register;
begin
  RegisterComponents('Standard', [TSendKeys]);
end;
{ TSendKeys }
function PRVGetChildHandle(H: HWND; L: Integer): LongBool;
var
  p: pchar;
  I: integer;
  s: string;
begin
  I := length(ChildText) + 2;
  GetMem(p, i + 1);
  SendMessage(H, WM_GetText, i, integer(p));
  s := strpcopy(p, s);
  if pos(ChildText, s) <> 0 then
  begin
    HTemp := H;
    Result := False
  end
  else
    Result := True;
  FreeMem(p);
end;
function PRVSendKeys(H: HWND; L: Integer): LongBool; stdcall;
var
  s: string;
  i: integer;
begin
  i := length(temps);
  if i <> 0 then
  begin
    SetLength(s, i + 2);
    GetWindowText(H, pchar(s), i + 2);
    if Pos(temps, string(s)) <> 0 then
    begin
      Result := false;
      if ChildWindow then
        EnumChildWindows(H, @PRVGetChildHandle, L)
      else
        HTemp := H;
    end
    else
      Result := True;
  end
  else
    Result := False;
end;
procedure TSendKeys.GetWindowHandle(Text: string);
begin
  temps := Text;
  ChildText := fChildText;
  ChildWindow := fChild;
  EnumWindows(@PRVSendKeys, L);
  fHandle := HTemp;
end;
procedure TSendKeys.SendKeys(buffer: string);
var
  i: integer;
  w: word;
  D: DWORD;
  P: ^DWORD;
begin
  P := @D;
  SystemParametersInfo(//get flashing timeout on win98
    SPI_GETFOREGROUNDLOCKTIMEOUT,
    0,
    P,
    0);
  SetForeGroundWindow(fHandle);
  for i := 1 to length(buffer) do
  begin
    w := VkKeyScan(buffer[i]);
    keybd_event(w, 0, 0, 0);
    keybd_event(w, 0, KEYEVENTF_KEYUP, 0);
  end;
  SystemParametersInfo(//set flashing TimeOut=0
    SPI_SETFOREGROUNDLOCKTIMEOUT,
    0,
    nil,
    0);
  SetForegroundWindow(TWinControl(TComponent(Self).Owner).Handle);
  //->typecast working...
  SystemParametersInfo(//set flashing TimeOut=previous value
    SPI_SETFOREGROUNDLOCKTIMEOUT,
    D,
    nil,
    0);
end;
procedure TSendKeys.SetChildText(const Value: string);
begin
  fChildText := Value;
end;
procedure TSendKeys.SetIsChildWindow(const Value: boolean);
begin
  fchild := Value;
end;
procedure TSendKeys.SetWindowHandle(const Value: HWND);
begin
  fHandle := WindowHandle;
end;
end.
Описание: Данный компонент получает хэндл(handle) любого запущенного окна и даёт возможность отправить по указанному хэндлу любые комбинации нажатия клавиш. Совместимость: Все версии Delphi Собственно сам исходничек:

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  // Запускаем Notepad, и ему мы будем посылать нажатия клавиш
  WinExec('NotePad.exe', SW_SHOW);
  // В параметре процедуры GetWindowHandle помещаем
  // текст заголовка окна Nоtераd'а.
  SendKeys1.GetWindowHandle('Untitled - Notepad');
  // Если хэндл окна получен успешно, то отправляем ему текст
  if SendKeys1.WindowHandle <> 0 then
    SendKeys1.SendKeys('This is a test');
  // Так же можно отправить код любой кнопки типа
  // RETURN, используя следующий код:
  // SendKeys1.SendKeys(Chr(13));
end;

Неправда ли весело :).

Переслать сообщение с помощью SendMessage.

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

{ 
  This is useful if you want to send a message from your DLL back 
  to the calling application. 
}
 const
   MY_MESSAGE = WM_USER + 4242;
   type
   TForm1 = class(TForm)
     Button1: TButton;
     procedure Button1Click(Sender: TObject);
     // Handler that receive the Message 
    procedure MessageReceiver(var msg: TMessage); message MY_MESSAGE;
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TForm1.Button1Click(Sender: TObject);
 var
   txt: string;
 begin
   txt := 'Hello World';
   SendMessage(Form1.Handle, MY_MESSAGE, 0, DWORD(PChar(txt)));
 end;
 // To receive this custom Message that is addressed to form1.handle 
// you need a message handler. 
procedure TForm1.MessageReceiver(var msg: TMessage);
 var
   txt: PChar;
 begin
   txt := PChar(msg.lParam);
   msg.Result := 1;
   ShowMessage(txt);
 end;
 end.

Отправление сообщения сразу всем элементам управления формы.

Вы слишком много работаете за компьютером, если: - Вспоминаете про антивирусные пакеты всякий раз, когда открываете консервы, книги, двери - Вспоминаете про архивирование, когда: - идете с тяжелыми сумками домой - несете свою жену на руках - Вспоминаете операцию "удалить", когда: - видите тещу - инспектора ГИБДД - Подсознательно ненавидите женщин с именами - Ася - Клава - Вспоминаете про разархивирование, когда получаете зарплату. Вы слишком мало работаете за компьютером, если: - позволяете себе заниматься сексом без предохранения, даже с надежным партнером - позволяете себе заниматься сексом - позволяете себе.

Можно использовать.

Screen.Forms[i].BroadCast(msg);

Где [i] - индекс той формы, которой Вы хотите переслать сообщение. BroadCast работает со всеми компонентами, потомками TWinControls и отправляет сообщение всем дочерним компонентам из массива Controls. Если один из дочерних компонентов обрабатывает это сообщение и устанавливает Msg.Result в ненулевое значение - дальнейшая рассылка сообщения останавливается.

Посылка Raw IP-пакетов.

Автор: E.J.Molendijk
Дочь просит И-нетчика: - Папа, посмотри пожалуйста, какая на улице погода. - Сейчас,-ответил тот и набрал в браузере "http://weather.cnn.com".

Используя данный исходник можно конструировать собственные пакеты содержащие внутри всё, что угодно. Можно самостоятельно указывать в пакете IP-адрес получателя и отправителя, порт назначения и т.д. Если Вы не знаете, что это такое, то лучше не эксперементировать. Единственный недостаток, то, что скорее всего данный пример будет работать только в Windows 2000. Так же исходник позволяет произвести SYN flood и IP spoofing.

Необходимо зайти в систему под Администратором.

{
Raw Packet Sender
using: Delphi + Winsock 2
Copyright (c) 2000 by E.J.Molendijk (xes@dds.nl)
------------------------------------------------
Перед использованием измените значения
SrcIP+SrcPort+DestIP+DestPort на нужные!
------------------------------------------------
}
unit main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, OleCtrls, Registry;
const
  SrcIP = '123.123.123.1';
  SrcPort = 1234;
  DestIP = '123.123.123.2';
  DestPort = 4321;
  Max_Message = 4068;
  Max_Packet = 4096;
type
  TPacketBuffer = array[0..Max_Packet-1] of byte;
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure SendIt;
end;
// Заголовок IP пакета
type
  T_IP_Header = record
    ip_verlen : Byte;
    ip_tos : Byte;
    ip_totallength : Word;
    ip_id : Word;
    ip_offset : Word;
    ip_ttl : Byte;
    ip_protocol : Byte;
    ip_checksum : Word;
    ip_srcaddr : LongWord;
    ip_destaddr : LongWord;
end;
// Заголовок UDP пакета
type
  T_UDP_Header = record
    src_portno : Word;
    dst_portno : Word;
    udp_length : Word;
    udp_checksum : Word;
end;
// Некоторые объявления типов для Winsock 2
u_char = Char;
u_short = Word;
u_int = Integer;
u_long = Longint;
SunB = packed record
  s_b1, s_b2, s_b3, s_b4: u_char;
end;
SunW = packed record
  s_w1, s_w2: u_short;
end;
in_addr = record
  case integer of
    0: (S_un_b: SunB);
    1: (S_un_w: SunW);
    2: (S_addr: u_long);
  end;
  TInAddr = in_addr;
Sockaddr_in = record
  case Integer of
    0: (sin_family: u_short;
    sin_port: u_short;
    sin_addr: TInAddr;
    sin_zero: array[0..7] of Char);
    1: (sa_family: u_short;
    sa_data: array[0..13] of Char)
  end;
TSockAddr = Sockaddr_in;
TSocket = u_int;
const
  WSADESCRIPTION_LEN = 256;
  WSASYS_STATUS_LEN = 128;
type
  PWSAData = ^TWSAData;
  WSAData = record // !!! also WSDATA
    wVersion: Word;
    wHighVersion: Word;
    szDescription: array[0..WSADESCRIPTION_LEN] of Char;
    szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char;
    iMaxSockets: Word;
    iMaxUdpDg: Word;
    lpVendorInfo: PChar;
end;
TWSAData = WSAData;
// Определяем необходимые функции winsock 2
function closesocket(s: TSocket): Integer; stdcall;
function socket(af, Struct, protocol: Integer): TSocket; stdcall;
function sendto(s: TSocket; var Buf; len, flags: Integer; var addrto: TSockAddr;
tolen: Integer): Integer; stdcall;{}
function setsockopt(s: TSocket; level, optname: Integer; optval: PChar;
optlen: Integer): Integer; stdcall;
function inet_addr(cp: PChar): u_long; stdcall; {PInAddr;} { TInAddr }
function htons(hostshort: u_short): u_short; stdcall;
function WSAGetLastError: Integer; stdcall;
function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;
function WSACleanup: Integer; stdcall;
const
  AF_INET = 2; // internetwork: UDP, TCP, etc.
  IP_HDRINCL = 2; // включаем заголовок IP пакета
  SOCK_RAW = 3; // интерфейс raw-протокола
  IPPROTO_IP = 0; // dummy for IP
  IPPROTO_TCP = 6; // tcp
  IPPROTO_UDP = 17; // user datagram protocol
  IPPROTO_RAW = 255; // raw IP пакет
  INVALID_SOCKET = TSocket(not(0));
  SOCKET_ERROR = -1;
var
  Form1: TForm1;
implementation
// Импортируем функции Winsock 2
const WinSocket = 'WS2_32.DLL';
function closesocket; external winsocket name 'closesocket';
function socket; external winsocket name 'socket';
function sendto; external winsocket name 'sendto';
function setsockopt; external winsocket name 'setsockopt';
function inet_addr; external winsocket name 'inet_addr';
function htons; external winsocket name 'htons';
function WSAGetLastError; external winsocket name 'WSAGetLastError';
function WSAStartup; external winsocket name 'WSAStartup';
function WSACleanup; external winsocket name 'WSACleanup';
{$R *.DFM}
//
// Function: checksum
//
// Description:
// This function calculates the 16-bit one's complement sum
// for the supplied buffer
//
function CheckSum(var Buffer; Size : integer) : Word;
type
  TWordArray = array[0..1] of Word;
var
  ChkSum : LongWord;
  i : Integer;
begin
  ChkSum := 0;
  i := 0;
  while Size > 1 do
  begin
    ChkSum := ChkSum + TWordArray(Buffer)[i];
    inc(i);
    Size := Size - SizeOf(Word);
  end;
  if Size=1 then
    ChkSum := ChkSum + Byte(TWordArray(Buffer)[i]);
  ChkSum := (ChkSum shr 16) + (ChkSum and $FFFF);
  ChkSum := ChkSum + (Chksum shr 16);
  Result := Word(ChkSum);
end;
procedure BuildHeaders(FromIP : string; iFromPort : Word; ToIP : string;
iToPort : Word; StrMessage : string; var Buf : TPacketBuffer;
var remote : TSockAddr; var iTotalSize: Word);
var
  dwFromIP : LongWord;
  dwToIP : LongWord;
  iIPVersion : Word;
  iIPSize : Word;
  ipHdr : T_IP_Header;
  udpHdr : T_UDP_Header;
  iUdpSize : Word;
  iUdpChecksumSize : Word;
  cksum : Word;
  Ptr : ^Byte;
  procedure IncPtr(Value : Integer);
  begin
    ptr := pointer(integer(ptr) + Value);
  end;
begin
  // преобразуем ip адреса
  dwFromIP := inet_Addr(PChar(FromIP));
  dwToIP := inet_Addr(PChar(ToIP));
  // Инициализируем заголовок IP пакета
  //
  iTotalSize := sizeof(ipHdr) + sizeof(udpHdr) + length(strMessage);
  iIPVersion := 4;
  iIPSize := sizeof(ipHdr) div sizeof(LongWord);
  //
  // IP version goes in the high order 4 bits of ip_verlen. The
  // IP header length (in 32-bit words) goes in the lower 4 bits.
  //
  ipHdr.ip_verlen := (iIPVersion shl 4) or iIPSize;
  ipHdr.ip_tos := 0; // IP type of service
  ipHdr.ip_totallength := htons(iTotalSize); // Total packet len
  ipHdr.ip_id := 0; // Unique identifier: set to 0
  ipHdr.ip_offset := 0; // Fragment offset field
  ipHdr.ip_ttl := 128; // время жизни пакета
  ipHdr.ip_protocol := $11; // Protocol(UDP)
  ipHdr.ip_checksum := 0 ; // IP checksum
  ipHdr.ip_srcaddr := dwFromIP; // Source address
  ipHdr.ip_destaddr := dwToIP; // Destination address
  //
  // Инициализируем заголовок UDP пакета
  //
  iUdpSize := sizeof(udpHdr) + length(strMessage);
  udpHdr.src_portno := htons(iFromPort) ;
  udpHdr.dst_portno := htons(iToPort) ;
  udpHdr.udp_length := htons(iUdpSize) ;
  udpHdr.udp_checksum := 0 ;
  //
  // Build the UDP pseudo-header for calculating the UDP checksum.
  // The pseudo-header consists of the 32-bit source IP address,
  // the 32-bit destination IP address, a zero byte, the 8-bit
  // IP protocol field, the 16-bit UDP length, and the UDP
  // header itself along with its data (padded with a 0 if
  // the data is odd length).
  //
  iUdpChecksumSize := 0;
  ptr := @buf[0];
  FillChar(Buf, SizeOf(Buf), 0);
  Move(ipHdr.ip_srcaddr, ptr^, SizeOf(ipHdr.ip_srcaddr));
  IncPtr(SizeOf(ipHdr.ip_srcaddr));
  iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_srcaddr);
  Move(ipHdr.ip_destaddr, ptr^, SizeOf(ipHdr.ip_destaddr));
  IncPtr(SizeOf(ipHdr.ip_destaddr));
  iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_destaddr);
  IncPtr(1);
  Inc(iUdpChecksumSize);
  Move(ipHdr.ip_protocol, ptr^, sizeof(ipHdr.ip_protocol));
  IncPtr(sizeof(ipHdr.ip_protocol));
  iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_protocol);
  Move(udpHdr.udp_length, ptr^, sizeof(udpHdr.udp_length));
  IncPtr(sizeof(udpHdr.udp_length));
  iUdpChecksumSize := iUdpChecksumSize + sizeof(udpHdr.udp_length);
  move(udpHdr, ptr^, sizeof(udpHdr));
  IncPtr(sizeof(udpHdr));
  iUdpChecksumSize := iUdpCheckSumSize + sizeof(udpHdr);
  Move(StrMessage[1], ptr^, Length(strMessage));
  IncPtr(Length(StrMessage));
  iUdpChecksumSize := iUdpChecksumSize + length(strMessage);
  cksum := checksum(buf, iUdpChecksumSize);
  udpHdr.udp_checksum := cksum;
  //
  // Now assemble the IP and UDP headers along with the data
  // so we can send it
  //
  FillChar(Buf, SizeOf(Buf), 0);
  Ptr := @Buf[0];
  Move(ipHdr, ptr^, SizeOf(ipHdr)); IncPtr(SizeOf(ipHdr));
  Move(udpHdr, ptr^, SizeOf(udpHdr)); IncPtr(SizeOf(udpHdr));
  Move(StrMessage[1], ptr^, length(StrMessage));
  // Apparently, this SOCKADDR_IN structure makes no difference.
  // Whatever we put as the destination IP addr in the IP header
  // is what goes. Specifying a different destination in remote
  // will be ignored.
  //
  remote.sin_family := AF_INET;
  remote.sin_port := htons(iToPort);
  remote.sin_addr.s_addr := dwToIP;
end;
procedure TForm1.SendIt;
var
  sh : TSocket;
  bOpt : Integer;
  ret : Integer;
  Buf : TPacketBuffer;
  Remote : TSockAddr;
  Local : TSockAddr;
  iTotalSize : Word;
  wsdata : TWSAdata;
begin
  // Startup Winsock 2
  ret := WSAStartup($0002, wsdata);
  if ret<>0 then
  begin
    memo1.lines.add('WSA Startup failed.');
    exit;
  end;
  with memo1.lines do
  begin
    add('WSA Startup:');
    add('Desc.: '+wsData.szDescription);
    add('Status: '+wsData.szSystemStatus);
  end;
  try
    // Создаём сокет
    sh := Socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    if (sh = INVALID_SOCKET) then
    begin
      memo1.lines.add('Socket() failed: '+IntToStr(WSAGetLastError));
      exit;
    end;
    Memo1.lines.add('Socket Handle = '+IntToStr(sh));
    // Option: Header Include
    bOpt := 1;
    ret := SetSockOpt(sh, IPPROTO_IP, IP_HDRINCL, @bOpt, SizeOf(bOpt));
    if ret = SOCKET_ERROR then
    begin
      Memo1.lines.add('setsockopt(IP_HDRINCL) failed: '+IntToStr(WSAGetLastError));
      exit;
    end;
    // строим пакет
    BuildHeaders( SrcIP, SrcPort,
    DestIP, DestPort,
    'THIS IS A TEST PACKET',
    Buf, Remote, iTotalSize );
    // Отправляем пакет
    ret := SendTo(sh, buf, iTotalSize, 0, Remote, SizeOf(Remote));
    if ret = SOCKET_ERROR then
      Memo1.Lines.Add('sendto() failed: '+IntToStr(WSAGetLastError))
    else
      Memo1.Lines.Add('send '+IntToStr(ret)+' bytes.');
    // Закрываем сокет
    CloseSocket(sh);
  finally
    // Закрываем Winsock 2
    WSACleanup;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  SendIt;
end; 
end.

Передать строки, картинки (streams) между процессами.

{ 
  The WM_COPYDATA messages makes it possible to transfer information 
  between processes. It does this by passing the data through the kernel. 
  Space is allocated in the receiving process to hold the information that is copied, 
  by the kernel, from the source process to the target process. 
  The sender passes a pointer to a COPYDATASTRUCT, which is defined as a structure 
  of the following: 
} 
type 
  TCopyDataStruct = packed record 
    dwData: DWORD;   // anwendungsspezifischer Wert 
    cbData: DWORD;   // Byte-Lдngе der zu ььегtгаgеndеn Daten 
    lpData: Pointer; // Adresse der Daten 
  end; 
 
{ Sender Application } 
unit SenderApp; 
interface 
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  StdCtrls, ExtCtrls; 
type 
  TForm1 = class(TForm) 
    Button1: TButton; 
    Edit1: TEdit; 
    Button2: TButton; 
    Image1: TImage; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
  private 
    procedure SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct); 
  public 
  end; 
var 
  Form1: TForm1; 
implementation 
{$R *.DFM} 
// Sender: Send data 
procedure TForm1.SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct); 
begin 
  if hTargetWnd <> 0 then 
    SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@ACopyDataStruct)) 
  else 
    ShowMessage('No Recipient found!'); 
end; 
// Send Text from Edit1 to other app 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  MyCopyDataStruct: TCopyDataStruct; 
  hTargetWnd: HWND; 
begin 
  // Set up a COPYDATASTRUCT structure for use with WM_COPYDATA 
  // TCopyDataStruct mit den Sende-Daten Infos аиsfь11еn 
  with MyCopyDataStruct do 
  begin 
    dwData := 0; // may use a value do identify content of message 
    cbData := StrLen(PChar(Edit1.Text)) + 1;  //Need to transfer terminating #0 as well 
    lpData := PChar(Edit1.Text) 
  end; 
  // Find the destination window for the WM_COPYDATA message 
  // Еmрfдngег Fenster anhand des Titelzeilentextes suchen 
  hTargetWnd := FindWindow(nil,PChar('Message Receiver')); 
  // send the structure to the receiver 
  // Die Struktur an den Еmрfдngег schicken 
  SendCopyData(hTargetWnd, MyCopyDataStruct); 
end; 
// Send Image1 to other app 
procedure TForm1.Button2Click(Sender: TObject); 
var 
  ms: TMemoryStream; 
  MyCopyDataStruct: TCopyDataStruct; 
  hTargetWnd: HWND; 
begin 
  ms := TMemoryStream.Create; 
  try 
    image1.Picture.Bitmap.SaveToStream(ms); 
    with MyCopyDataStruct do 
    begin 
      dwData := 1; 
      cbData := ms.Size; 
      lpData := ms.Memory; 
    end; 
    // Search window by the window title 
    // Еmрfдngег Fenster anhand des Titelzeilentextes suchen 
    hTargetWnd := FindWindow(nil,PChar('Message Receiver')); 
    // Send the Data 
    // Daten Senden 
    SendCopyData(hTargetWnd,MyCopyDataStruct); 
  finally 
    ms.Free; 
  end; 
end; 
end. 
{*********************************************************************} 
{ Receiver Application } 
unit ReceiverApp; 
interface 
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  ExtCtrls, StdCtrls; 
type 
  TForm1 = class(TForm) 
    Image1: TImage; 
    Label1: TLabel; 
  private 
    procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; 
    { Private-Deklarationen } 
  public 
    { Public-Deklarationen } 
  end; 
var 
  Form1: TForm1; 
implementation 
{$R *.DFM} 
procedure TForm1.WMCopyData(var Msg: TWMCopyData); 
var 
  sText: array[0..99] of Char; 
  ms: TMemoryStream; 
begin 
  case Msg.CopyDataStruct.dwData of 
    0: { Receive Text, Text empfangen} 
      begin 
        StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData); 
        label1.Caption := sText; 
      end; 
    1: { Receive Image, Bild empfangen} 
      begin 
        ms := TMemoryStream.Create; 
        try 
          with Msg.CopyDataStruct^ do 
           ms.Write(lpdata^, cbdata); 
           ms.Position := 0; 
          image1.Picture.Bitmap.LoadFromStream(ms); 
        finally 
          ms.Free; 
        end; 
      end; 
  end; 
end; 
end.

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

Получатель:

procedure ReceiveMessage (var Msg: TMessage);
message WM_COPYDATA;
...
procedure TFormReceive.ReceiveMessage;
var
  pcd: PCopyDataStruct;
begin
  pcd := PCopyDataStruct(Msg.LParam);
  Caption := PChar(pcd.lpData);
end;

Отправитель:

procedure TFormXXX.Button1Click(Sender: TObject);
var
  cd: TCopyDataStruct;
begin
  cd.cbData := Length(Edit1.Text) + 1;
  cd.lpData := PChar(Edit1.Text);
  SendMessage(FindWindow('TFormReceive', nil), WM_COPYDATA, 0, LParam(@cd));
end;

Передача спецсимволов в документ Word.

Автор: Andrey

// Омега 937 - юникод нужного символа
WordApp.Selection.TypeText(WideString('#937'));

Переслать текст в другую программу.

Автор: Xavier Pacheco

unit Readmain;
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Menus, StdCtrls;
{ The WM_COPYDATA Windows message is not defined in the 16-bit Messages }
{ unit, although it is available to 16-bit applications running under   }
{ Windows 95 or NT.  This message is discussed in the Win32 API online  }
{ help. }
const
  WM_COPYDATA = $004A;
type
  TMainForm = class(TForm)
    ReadMemo: TMemo;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Exit1: TMenuItem;
    Help1: TMenuItem;
    About1: TMenuItem;
    procedure Exit1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure About1Click(Sender: TObject);
  private
    procedure OnAppMessage(var M: TMsg; var Handled: Boolean);
    procedure WMCopyData(var M: TMessage); message WM_COPYDATA;
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
uses RegMsg, AboutU;
type
  { The TCopyDataStruct record type is not defined in WinTypes unit, }
  { although it is available in the 16-bit Windows API when running  }
  { under Windows 95 and NT. The lParam of the WM_COPYDATA message   }
  { points to one of these. }
  PCopyDataStruct = ^TCopyDataStruct;
  TCopyDataStruct = record
    dwData: DWORD;
    cbData: DWORD;
    lpData: Pointer;
  end;
procedure TMainForm.OnAppMessage(var M: TMsg; var Handled: Boolean);
{ OnMessage handler for Application object. }
begin
  { The DDGM_HandshakeMessage message is received as a broadcast to     }
  { all applications.  The wParam of this message contains the handle   }
  { of the window which broadcasted the message.  We respond by posting }
  { the same message back to the sender, with our handle in the wParam. }
  if M.Message = DDGM_HandshakeMessage then
  begin
    PostMessage(M.wParam, DDGM_HandshakeMessage, Handle, 0);
    Handled := True;
  end;
end;
procedure TMainForm.WMCopyData(var M: TMessage);
{ Handler for WM_COPYDATA message }
begin
  { Check wParam to ensure we know WHO sent us the WM_COPYDATA message }
  if PCopyDataStruct(M.lParam)^.dwData = DDGM_HandshakeMessage then
    { When WM_COPYDATA message is received, the lParam points to}
    ReadMemo.SetTextBuf(PChar(PCopyDataStruct(M.lParam)^.lpData));
end;
procedure TMainForm.Exit1Click(Sender: TObject);
begin
  Close;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnMessage := OnAppMessage;
end;
procedure TMainForm.About1Click(Sender: TObject);
begin
  AboutBox;
end;
end.
unit CopyMain;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Menus;
type
  TMainForm = class(TForm)
    DataMemo: TMemo;
    BottomPnl: TPanel;
    BtnPnl: TPanel;
    CloseBtn: TButton;
    CopyBtn: TButton;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    CopyData1: TMenuItem;
    N1: TMenuItem;
    Exit1: TMenuItem;
    Help1: TMenuItem;
    About1: TMenuItem;
    procedure CloseBtnClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure About1Click(Sender: TObject);
    procedure CopyBtnClick(Sender: TObject);
  private
    { Private declarations }
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
uses AboutU, RegMsg;
// The following declaration is necessary because of an error in
// the declaration of BroadcastSystemMessage() in the Windows unit
function BroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD;
  uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
  external 'user32.dll';
var
  Recipients: DWORD = BSM_APPLICATIONS;
procedure TMainForm.WndProc(var Message: TMessage);
var
  DataBuffer: TCopyDataStruct;
  Buf: PChar;
  BufSize: Integer;
begin
  if Message.Msg = DDGM_HandshakeMessage then
  begin
    { Allocate buffer }
    BufSize := DataMemo.GetTextLen + (1 * SizeOf(Char));
    Buf := AllocMem(BufSize);
    { Copy memo to buffer }
    DataMemo.GetTextBuf(Buf, BufSize);
    try
      with DataBuffer do
      begin
        { Fill dwData with registered message as safety check }
        dwData := DDGM_HandshakeMessage;
        cbData := BufSize;
        lpData := Buf;
      end;
      { NOTE: WM_COPYDATA message must be *sent* }
      SendMessage(Message.wParam, WM_COPYDATA, Handle,
        Longint(@DataBuffer));
    finally
      FreeMem(Buf, BufSize);
    end;
  end
  else
    inherited WndProc(Message);
end;
procedure TMainForm.CloseBtnClick(Sender: TObject);
begin
  Close;
end;
procedure TMainForm.FormResize(Sender: TObject);
begin
  BtnPnl.Left := BottomPnl.Width div 2 - BtnPnl.Width div 2;
end;
procedure TMainForm.About1Click(Sender: TObject);
begin
  AboutBox;
end;
procedure TMainForm.CopyBtnClick(Sender: TObject);
begin
  { Call for any listening apps }
  BroadcastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
    @Recipients, DDGM_HandshakeMessage, Handle, 0);
end;
end.
Скачать весь проект

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

Под Win16 Вы можете использовать функцию SpoolFile, или Passthrough escape, если принтер поддерживает последнее. Под Win32 Вы можете использовать WritePrinter.

Ниже пример открытия принтера и записи чистого потока данных в принтер. Учтите, что Вы должны передать корректное имя принтера, такое, как "HP LaserJet 5MP", чтобы функция сработала успешно.

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

uses WinSpool;
procedure WriteRawStringToPrinter(PrinterName:string; S:string);
var
  Handle: THandle;
  N: DWORD;
  DocInfo1: TDocInfo1;
begin
  if not OpenPrinter(PChar(PrinterName), Handle, nil) then
  begin
    ShowMessage('Error ' + IntToStr(GetLastError));
    Exit;
  end;
  with DocInfo1 do
  begin
    pDocName := PChar('test doc');
    pOutputFile := nil;
    pDataType := 'RAW';
  end;
  StartDocPrinter(Handle, 1, @DocInfo1);
  StartPagePrinter(Handle);
  WritePrinter(Handle, PChar(S), Length(S), N);
  EndPagePrinter(Handle);
  EndDocPrinter(Handle);
  ClosePrinter(Handle);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  WriteRawStringToPrinter('HP', 'Test This');
end;

Посмотри и доделай как тебе надо:

unit TextPrinter;
interface
uses
  Windows, Controls, Forms, Dialogs;
type
  TTextPrinter = class(TObject)
    FNumberOfBytesWritten: Integer;
    FHandle: THandle;
    FPrinterOpen: Boolean;
    FErrorString: PChar;
    procedure SetErrorString;
  public
    constructor Create;
    procedure write(const Str: string);
    procedure WriteLn(const Str: string);
    destructor Destroy; override;
  published
    property NumberOfBytesWritten: Integer read FNumberOfBytesWritten;
end;
implementation
{TTextPrinter}
constructor TTextPrinter.Create;
begin
  FHandle := CreateFile('LPT1', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ
  or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if FHandle = INVALID_HANDLE_VALUE then
  begin
    SetErrorString;
    raise Exception.Create(FErrorString);
  end
  else
    FPrinterOpen := True;
end;
procedure TTextPrinter.SetErrorString;
begin
  if FErrorString <> nil then
  LocalFree(Integer(FErrorString));
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,
  nil, GetLastError(), LANG_USER_DEFAULT, @FErrorString, 0, nil);
end;
procedure TTextPrinter.write(const Str: string);
var
  OEMStr: PChar;
  NumberOfBytesToWrite: Integer;
begin
  if not FPrinterOpen then
    Exit;
  NumberOfBytesToWrite := Length(Str);
  OEMStr := PChar(LocalAlloc(LMEM_FIXED, NumberOfBytesToWrite + 1));
  try
    CharToOem(PChar(Str), OEMStr);
    if not WriteFile(FHandle, OEMStr^, NumberOfBytesToWrite, FNumberOfBytesWritten, nil) then
    begin
      SetErrorString;
      raise Exception.Create(FErrorString);
    end;
  finally
    LocalFree(Integer(OEMStr));
  end;
end;
procedure TTextPrinter.WriteLn(const Str: string);
begin
  Self.write(Str);
  Self.write(#10);
end;
destructor TTextPrinter.Destroy;
begin
  CloseHandle(FHandle);
  if FErrorString <> nil then
    LocalFree(Integer(FErrorString));
end;
end.

P.S. В принципе, вместо LPT1 может стоять что угодно, даже сетевой сервер печати (\\server\prn) - все равно печатает. Можно и параметр в конструктор вставить и т.д.

Как передать UserName и Password в удаленный модуль данных.

В Удаленный Модуль Данных бросьте компонент TDatabase, затем добавьте процедуру автоматизации (пункт главного меню Edit | Add To Interface) для Login.

Убедитесь, что свойство HandleShared компонента TDatabase установлено в True.

procedure Login(UserName, Password: WideString);
begin
  { DB = TDatabase }
  { Something unique between clients }
  DB.DatabaseName := UserName + 'DB';
  DB.Params.Values['USER NAME'] := UserName;
  DB.Params.Values['PASSWORD'] := Password;
  DB.Open;
end;

После того, как Вы создали этот метод автоматизации, Вы можете вызывать его с помощью:

RemoteServer1.AppServer.Login('USERNAME','PASSWORD'); 

Как отправить вебформу на сервер при помощи TClientSocket (напрямую и через прокси).

Посетитель у провайдера. - Ой, что это у вас так крякнуло. - Сервер наверное...
{
Присоедините следующие события к Вашему ClientSocket:
procedure T...Form.ClientSocket1Write;
procedure T...Form.ClientSocket1Read;
procedure T...Form.ClientSocket1Disconnect;
procedure T...Form.ClientSocket1Error;
Так же пример показывает, как направлять передачу через прокси-сервер.
Для отправки на вебсервер используется следующий формат:
Напрямую:
  'POST ' + PostAddr + 'HTTP/1.0' + HTTP_Data + Content
Через проксю:
  'POST http://' Webserver + PostAddr + 'HTTP/1.0' + HTTP_Data + Content
}
const
  WebServer = 'www.somehost.com';
  WebPort = 80;
  PostAddr = '/cgi-bin/form';
  { Следующие переменные используются только для вебсервера: }
  ProxyServer ='proxy.somewhere.com';
  ProxyPort = 3128;
  // В заголовке post необходимы некоторые данные
  HTTP_Data =
  'Content-Type: application/x-www-form-urlencoded'#10+
  'User-Agent: Delphi/5.0 ()'#10+ { Отрекламируем Delphi 5! }
  'Host: somewhere.com'#10+
  'Connection: Keep-Alive'#10;
type
  T...Form = class(TForm)
    ...
  private
    { Private declarations }
    HTTP_POST : string;
    FContent : string;
    // Эта переменная будет содержать ответ сервера
    FResult : string;
  public
    { Public declarations }
end;
{ Эти функции сделают некоторое url-кодирование }
{ Например. 'John Smith' => 'John+Smith' }
function HTTPTran(St: string): string;
var
  i: Integer;
begin
  Result:='';
  for i:=1 to length(St) do
    if St[i] in ['a'..'z','A'..'Z','0','1'..'9'] then
      Result:=Result+St[i]
    else
      if St[i]=' ' then
        Result:=Result+'+'
      else
        Result:=Result+'%'+IntToHex(Byte(St[i]),2);
end;
procedure T...Form.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
begin
  // Постим данные
  Socket.SendText(HTTP_POST+FContent);
end;
procedure T...Form.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
begin
  // Получаем результат
  FResult:=FResult+Socket.ReceiveText;
end;
procedure T...Form.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
  // ЗДЕСЬ МОЖНО ОБРАБОТАТЬ FResult //
end;
procedure T...Form.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
  ErrorCode := 0; // Игнорируем ошибки
end;
{ А эта подпрограмма, которую можно
использовать для постинга данных формы. }
procedure T...Form.PostTheForm;
begin
  // Очищаем результаты
  FResult:='';
  // Вы можете ввести поля формы, которые необходимы
  // Вот некоторые примеры:
  FContent:=
  'Name='+ HTTPTran('John Smith') +'&'+
  'Address='+ HTTPTran('1 Waystreet') +'&'+
  'Email='+ HTTPTran('jsmith@somewhere.com') +'&'+
  'B1=Submit' +  #10;
  // Вычисляем длину содержимого
  FContent:=
  'Content-Length: '+IntToStr(Length(FContent))+#10+#10+FContent;
  {-- Начало прокси ---}
  { если Вы используете прокси, то раскоментируйте этот код }
  ClientSocket1.Host := ProxyServer;
  ClientSocket1.Port := ProxyPort;
  HTTP_POST := 'POST http:  //'+WebServer+PostAddr+' HTTP/1.0'#10;
  {--- Конец прокси ---}
  {--- Начало соединения напрямую --- }
  { удалите этот код, еслы Вы будете использовать прокси }
  ClientSocket1.Host := WebServer;
  ClientSocket1.Port := WebPort;
  HTTP_POST := 'POST '+PostAddr+' HTTP/1.0'#10;
  {--- Конец соединения напрямую ---}
  // Соединяем заголовок
  HTTP_Post := HTTP_Post + HTTP_Data;
  // Пытаемся открыть соединение
  ClientSocket1.Open;
end;

Как разделить обработку OnClick и OnDblClick.

Ведь OnClick будет вызываться всегда, и перед DblClick.

Именно так и происходит в Windows - посылаются оба сообщения. Для того чтобы обработать только какое-то одно событие необходимо чуть " задержать" выполнение OnClick. Сделать это можно следующим способом:

procedure TForm1.ListBox1Click(Sender: TObject);
var
  Msg: TMsg;
  TargetTime: Longint;
begin
  { get the maximum time to wait for a double-click message }
  TargetTime := GetTickCount + GetDoubleClickTime;
  { cycle until DblClick received or wait time run out }
  while GetTickCount <  TargetTime do
    if PeekMessage(Msg, ListBox1.Handle, WM_LBUTTONDBLCLK,
    WM_LBUTTONDBLCLK, WM_NOREMOVE)
    then Exit; { Double click }
  MessageDlg('Single clicked', mtInformation, [mbOK], 0);
end;

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

Диск С: выполнил недопустимую операцию и будет закрыт
CONST
  MaxSize : Longint = 1440000;  //байт
function ExtractFileNames(FileNames:string):string; 
var 
  S:string; 
begin 
  S:=''; 
  while Pos('.', FileNames) > 0 do 
  begin 
    S:=S+Copy(FileNames,1,Pos('.',FileNames)-1); 
    Delete(FileNames,1,Pos('.',FileNames)); 
  end; 
  result:=S; 
end; 
procedure TForm1.SpeedButton1Click(Sender: TObject); 
var 
  InFile,OutFile: FILE; 
  CopyBuffer : POINTER; { buffer for copying } 
  iRecsOK, iRecsWr, index: Integer; 
  sFileName,sFileExt,sFileFullName:string; 
  fFileSize: file of Byte; 
  Size:LongInt; 
begin 
  sFileFullName:='C:\1\1.mp3'; 
  sFileName:=ExtractFileName(sFileFullName); 
  sFileExt:=ExtractFileExt(sFileName); 
  sFileName:=ExtractFileNames(sFileName); 
  ShowMessage(sFileFullName+#13+sFilename+#13+sFileExt);
  if FileExists(sFileFullName) then 
  begin 
    AssignFile(fFileSize, sFileFullName); 
    FileMode := 0;  {Set file access to read only } 
    Reset(fFileSize); 
    Size := FileSize(fFileSize); {Get File Size} 
    CloseFile(fFileSize); 
    ShowMessage(IntToStr(Size)); 
    if Size>MaxSize then 
    begin {Divide} 
      Getmem(CopyBuffer, MaxSize); { allocate the buffer } 
      Assignfile(inFile,sFileFullName );//+ '.ZIP'); 
      Reset(inFile,1); 
      index := 1; 
      repeat 
        AssignFile(outFile,sFileName + '-'+IntToStr(index) + sFileExt); 
        Rewrite(OutFile,1); 
        inc(index); 
        BlockRead(InFile, CopyBuffer^, MaxSize, iRecsOK); 
        BlockWrite(OutFile, CopyBuffer^, iRecsOK, iRecsWr); 
        CloseFile(OutFile); 
      until (iRecsOK < MaxSize); 
      CloseFile(InFile); 
      FreeMem(CopyBuffer, MaxSize); { free the buffer } 
      ShowMessage('Done..!'); 
    end
    else
    begin
      ShowMessage('Do nothing..!');
    end;
  end
  else
    ShowMessage('File: '+sFileFullName+' not found');
end;
// Чтобы склеить файлы обратно в один,
// можно воспользоваться DOS функцией copy;
// copy /b file-1.xxx+file-2.xxx+file-3.xxx file.mp3

Дилемма доступа к данным.

Оформил: DeeCo

Автор: Марко Кэнту (Marco Cantu) (Перевод - Юрия Елкина) Оригинал можно прочесть здесь

По традиции, приложения Delphi использовали технологию BDE для доступа к данным. Но с появлением Delhi 5 появилась новая возможность - ADO. С момента появления Delphi 5, который включает технологию ADOExpress ( она входит в Delphi Enterprise или продается как отдельная опция к Delphi Professional), программисты встретились с выбором: что использовать BDE или ADO для доступа к БД ? Типичным ответом на вопрос "Какую технологию Баз Данных использовать ?" будет - "Это зависит от ..." Технология BDE ( http://www.borland.com/bde ) имеет длинную и славную историю. Она появилась в Delphi 1 в качестве механизма для доступа к таблицам Paradox и, позднее, была частью ISAPI - инициативы поддерживаемой IBM, Novell и WordPerfect. Несмотря на некоторые проблемы, BDE является одной из причин успеха Delphi на арене Баз Данных и достигнув версии 5 она является достаточно зрелой технологией. И хотя есть несколько проблем в процессе программирования и установки готовых программ, Delphi - программисты научились жить с BDE.

Несколько белых пятен.

Так зачем уходить от BDE ? Разворачивание ее на арендуемых Internet серверах зачастую невозможно из-за неодобрения ISР'ом запуска сервисов системного уровня на своих серверах. И хотя BDE была усилена для поддержки таких возможностей, как обьектно-реляционная модель Oracle 8, некоторые из ее возможностей все еще сдерживаются ее Paradox корнями. Например, способ сопоставления числовых полей Базы Данных и типов переменных создает проблемы совместимости при работе Delphi с различными SQL серверами. Другая проблема в том, что BDE полностью включает ядро, используемое Paradox и dBase для доступа к данным. Нет возможности поставлять облегченную версию BDE исключив поддержку Раrаdох'а, если вы работаете только с SQL серверами. (С другой стороны, некоторые из этих проблем, такие как работа SQL сервера на ISP сервере, актуальны и для использования ADO.) В конце концов, с приходом Kylix (технология Delphi и C++Builder для Linux), мы увидим версию Delphi которая не сможет использовать BDE, но очевидно будет базироваться на новом облегченном ядре. Если для BDE нет места в будущем Delphi для Linux, она врядли может считаться будущим и для Windows. Borland продолжает утверждать, что BDE остается, но как мы видим за последние пару лет разрабатывается очень мало. Технология ActiveX Data Objects (ADO) это разработка Microsoft. Она предоставляет упрощенный способ для доступа к данным основанный на OLE DB, сама же мощная лошадка остается за сценой. Программировать непосредственно на OLE DB уровне достаточно сложно, поэтому Microsoft предоставляет более простое решение. Включая технологию ADOExpress в Delphi Borland принял ADO в качестве общей технологии и признал Microsoft Access, как широко распространенный механизм доступа к БД (Прим.перев.: с полгода назад Microsoft заключил серию соглашений с Inprise- Microsoft заплатил в общей сложности порядка 250 млн. долларов за поддержку его технологий в продуктах Inprise). Не смотря на то, что в последнее время вы уже могли использовать БД Access через BDE, возможности все же были ограничены. Microsoft также установил несколько препятствий на дороге - таких как сокрытие новой версии DAO, фронтэнд Access'a обычно используемый в Visual Basic - чтобы подтолкнуть других поставщиков средств разработки для Windows в сторону ADO. Так или иначе, ADO это достаточно интересная технология. Она оптимально "легковесная", достаточно мощная, предоставляет доступ к данным вне реляционных БД, и Microsoft пытается продвинуть ее в качестве открытого стандарта. Вы даже можете написать OLE DB провайдера для ADO в Delphi.

Потеря управления.

Хотя ADO и умное решение, есть одна вещь, которая мне не нравится: вы теряете контроль, который предоставляет BDE. BDE ограничена в своей возможности обновлять результат запроса, поэтому чтобы использовать живые запросы (live queries) в Delphi вы часто используете компонент UpdateSql, а с SQL предложениями для замены, вставки и удаления - компонент Query. Это может показаться неудобным, но на самом деле позволяет сделать значительную тонкую настройку вашего приложения. ADO с другой стороны, делает много работы, чтобы обновлять результат запроса без какого либо дополнительного кодирования, но у вас нет контроля над SQL-кодом посылаемым серверам, если только Вы не разместили собственное решение отбросив живые запросы. Некоторая тонкая настройка доступна в ADO через использование DataSet'a, курсора, и блокировочных опций, но эти возможности ведут себя по разному на разных SQL серверах и, как оказывается, тонко настраиваются только для Microsoft SQL Server и Access. Рассмотрим последнее подробнее: Несмотря на то, что использование ADO для доступа к нескольким базам данных является хорошей идеей, есть несколько возможностей в ADO , которые похоже связаны только с Access. Например, Access является единственной, из всех что я знаю, базой данных, в которой вы имеете возможность блокирования записей при чтении на случай, если вы захотите редактировать их в будущем. Как и BDE включает в себя некоторые Paradox-специфичные возможности, так и ADO включает несколько возможностей, которые более Access-ориентированы, чем должно было бы предоставлять универсальное средство доступа к данным. Некоторые из наиболее интересных возможностей ADO связаны с использованием курсоров со стороны клиента. Вы можете скопировать полностью набор данных на клиентский компьютер и выполнить ряд операций на этом кэше, включая сортировку, фильтрацию и редактирование данных. Вы можете даже скопировать часть данных в локальный файл и работать с ним в режиме offline. Примеры, как это сделать, есть в Delphi 5/demos и на моем сайте . BDE тоже выполняет кэширование на локальном компьютере, но не позволит вам работать с кэшем. Однако, довольно много Delphi программистов научились использовать компонент ClientDataSet для работы с кэшированными данными. Я исследовал этот предмет и пришел к заключению, что ClientDataSet делает все, что делает клиентский (client-side) курсор ADO, включая сортировку, фильтрацию и локальное кэширование выборки из набора данных (local snapshots). Обе технологии позволяют вам создать локальный набор данных наложенный (mapped to) на файл в удаленной базе данных без соединения с ней. Обе имеют поддержку для XML - хотя и различными путями. Обе могут быть использованы для построения трехуровневых приложений. И обе предоставляют вложенные (nested) таблицы для создания master-detail представлений данных. В дополнение, компонент ClientDataSet предоставляет некоторые продвинутые возможности не поддерживаемые в ADO: группирование, аггрегирование, вычисляемые поля и поддержка абстрактных типов данных ( вот пример ). ClientDataSet и связанный с ним компонент провайдера предоставляет намного больше возможностей управления, чем в ADO. Возможно не только точно указать как будет выпоняться запись данных, но у вас имеются большие возможности для обработки конфликтов записи.

Обилие опций.

ADO и BDE не единственная альтернатива для доступа к данным в Delphi. Есть и другие компоненты наборов данных производимых Borland и третьими фирмами для непосредственного доступа к SQL серверам, таким как Oracle и Interbase. Есть также альтернативы для ADO - это разработки, которые предоставляют прямой доступ к DAO, и другие альтернативные технологии поддерживающие от Btrieve до AS/400. Но я все еще не нашел решения для прямого OLE DB доступа. BDE больше не является общим знаменателем для доступа к базам данных в Delphi. Я даже написал пару dаtаsеt'ов для доступа к данным не из баз данных. Вы, разумеется, не нуждаетесь в ADO и специальных OLE DB провайдерах для этого. Oracle, один из предпочтительных back-end серверов для Delphi приложений, наслаждается таким широкораспространенным использованием (колоритная фраза, я не стал ее "русифицировать"- прим.перев.), что я подозреваю, что успех ADO стратегии в значительной степени зависит от поддержки Oracle - по крайней мере с точки зрения перспективы для Delphi разработчиков. Очевидно, что поставляемый Мiсrоsоft'ом OLE DB провайдер для Oracle на сегодня не является ни живучим, ни полным решением. Если Oracle не подтолкнет ADO, то будущее этой технологии не видится столь ярким. А поскольку Microsoft наращивает ADO в направлении к собственным реализациям баз данных, то становится понятным сопративление Oracle'a продвижению ADO. Самое большое преимущество технологии ADO в том, что она широко распространена в Windows и продвигается фирмой Microsoft. Если вам нужен доступ к базам данных Microsoft SQL Server или Access, то вы вероятно предпочтете использовать ADO. Если вы используете Paradox или Interbase, тогда вам вероятно лучше поставить на BDE - если только вы не погрузились на корабль "InterBase Express". С таким большим количеством вопросов в программном бизнесе, ответом на "Какую технологию мне надо бы использовать ?" будет : "Это зависит от ..."

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

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

function AddThousandSeparator(S: string; Chr: Char): string;
 var
    I: Integer;
 begin
   Result := S;
   I := Length(S) - 2;
   while I > 1 do
    begin
     Insert(Chr, Result, I);
     I := I - 3;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   Edit1.Text := AddThousandSeparator('400500210', '''');
   // -- 400'500'210 
end;
 // Other possibility / andere Мцg1iсhкеit: ThousandSeparator := ''''; 
 label1.Caption := FormatFloat('#,###,###.###', 23453452);
 // --> 23'453'452 

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

Автор: Владимир Каталов Эту статейку написал скорее всего Владимир Каталов на тему защиты shareware-программ, но я думаю что она будет полезна не только программистам, но и крэкерам тоже. При хорошем знании ассемблера очень просто обойти защиту типа имя пользователя-регкод: когда пользователь вводит свое имя и регистрационный код, программа генерирует "правильный" код и сравнивает его с тем, который он ввел, поэтому "перехватить" этот код (просто считать его из памяти) не составлет особого труда. В большинстве случаев достаточно просто поставить breakpoint на функцию lstrcmp -- и все дела... Упаковка программы, анти-дебаггерные и анти-дизассемблерные "примочки" помогают слабо. А вот метод фиксированного ключа может быть реализован достаточно неплохо. Хранить "правильные" ключи в программе совсем не обязательно - можно подчинить их некоторым правилам; что-то подобное делает Microsoft со своими CD-keys, но у них все слишком просто. Алгоритм проверки может быть длинным и запутанным, так что его дизассемблирование (и "разборка", что же он делает) причинит немало головной боли. Именно первый метод я и применил для защиты своей программы (Advanced Disk Catalog - старые версии), но и он был "сломан" (хотя, как мне написал ломавший его хакер, подборка всего двух правильных ключей отняла у него много времени). Тогда мне в голову пришла идея: а что, если ключи хранить внутри программы, но зашифрованными? Я "сгенерил" некоторое количество ключей (абсолютно случайным образом), зашифровал их (по отдельности) 128-битным ключом по алгоритму RSA и прошил в программу в виде ресурса. Когда пользователь вводит ключ, он шифруется по тому же алгоритму и сравнивается с правильными. Так как система с открытым ключом не позволяет произвести обратное преобразование, базируясь только на открытом ключе (а закрытого нет даже у меня - расшифровывать-то не надо), то подобрать ключи невозможно даже теоретически. Есть, впрочем, еще одна проблема: хакер (или "крэкер", если угодно) может заменить 'je' на 'jne' (или что-то в этом роде) там, где происходит последняя проверка, и функция "IsValidKey(...)" будет всегда возвращать TRUE. Останется лишь написать маленький patch... Чтобы защититься и от этого, я вычисляю CRC своего exe-файла и сравниваю его с правильный, прошитым тоже внутри программы (естественно, при вычислении эта часть файла -- где лежит правильный CRC -- исключается; а "прошивается" он после компиляции). Кстати, это еще и защита от вирусов. Вообще-то, проверку CRC тоже можно локализовать и "запатчить", но это уже немного сложнее, особенно если программа вызывает функции чтения/записи и для других целей. Кроме того, не стоит в случае несовпадения CRC сразу об этом сообщать, иначе можно будет поставить hardware breakpoint и найти место, где он вычисляется. И последнее. Если хочется защитить программы совсем уж "круто", томожно несколько функций в своей программе (те, которые должны вызываться только в зарегистрированной версии), зашифровать по тому же алгоритму с открытым ключом. При этом, естественно, часть серийного номера (отсылаемого зарегистрировавшимся) надо сделать "статическим", т.е. неизменным для всех пользователей. На основе этой части после регистрации генерируется полный закрытый ключ, который далее используется для расшифровки указанных функций. Таким образом, даже если будет написан patch, позволяющий "зарегистрироваться" с любым (произвольным) кодом, расшифровка пройдет неправильно, и вместо нормального кода будет выполняться "мусор". Этот способ я применил в другой своей программе (Advanced ZIP Password Recovery), и ее пока не вскрыли. Всего наилучшего !

Комментарий от Bad_guy: действительно, если некоторые нужные функции зашифрованы, расшифровать их практически невозможно, и я бы сказал, что для защиты программы этого метода с лихвой достаточно, но никогда не надо забывать, что есть такой способ взлома как кража правильного кода (хотя такой метод противоречит крэкерской этике, да и не крэкерство это вовсе). На мой же взгляд программа - это сейф, содержимое которого вам известно, и прежде чем начинать взламывать этот сейф надо подумать: так ли вам нужно его содержимое ? Автор вот говорит - никто не взломает Advanced ZIP Password Recovery, ну и пускай спит спокойно, а я пока поищу бесплатный аналог (если припрёт).

Читать и писать в последовательный порт.

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

function OpenCOMPort: Boolean;
 var
   DeviceName: array[0..80] of Char;
   ComFile: THandle;
 begin
    { First step is to open the communications device for read/write. 
     This is achieved using the Win32 'CreateFile' function. 
     If it fails, the function returns false. 
     Wir versuchen, COM1 zu offnen. 
     Sollte dies fehlschlagen, gibt die Funktion false zuruck. 
   }
   StrPCopy(DeviceName, 'COM1:');
   ComFile := CreateFile(DeviceName,
     GENERIC_READ or GENERIC_WRITE,
     0,
     nil,
     OPEN_EXISTING,
     FILE_ATTRIBUTE_NORMAL,
     0);
   if ComFile = INVALID_HANDLE_VALUE then
     Result := False
   else
     Result := True;
 end;
 function SetupCOMPort: Boolean;
 const
   RxBufferSize = 256;
   TxBufferSize = 256;
 var
   DCB: TDCB;
   Config: string;
   CommTimeouts: TCommTimeouts;
 begin
    { We assume that the setup to configure the setup works fine. 
     Otherwise the function returns false. 
     wir gehen davon aus das das Einstellen des COM Ports funktioniert. 
     sollte dies fehlschlagen wird der Ruckgabewert auf "FALSE" gesetzt. 
   }
   Result := True;
   if not SetupComm(ComFile, RxBufferSize, TxBufferSize) then
     Result := False;
   if not GetCommState(ComFile, DCB) then
     Result := False;
   // define the baudrate, parity,... 
  // hier die Baudrate, Paritat usw. konfigurieren 
      Config := 'baud=9600 parity=n data=8 stop=1';
   if not BuildCommDCB(@Config[1], DCB) then
     Result := False;
   if not SetCommState(ComFile, DCB) then
     Result := False;
   with CommTimeouts do
   begin
     ReadIntervalTimeout         := 0;
     ReadTotalTimeoutMultiplier  := 0;
     ReadTotalTimeoutConstant    := 1000;
     WriteTotalTimeoutMultiplier := 0;
     WriteTotalTimeoutConstant   := 1000;
   end;
   if not SetCommTimeouts(ComFile, CommTimeouts) then
     Result := False;
 end;
 { 
  The following is an example of using the 'WriteFile' function 
  to write data to the serial port. 
  Folgendes Beispiel verwendet die 'WriteFile' Funktion, um Daten 
  auf den seriellen Port zu schreiben. 
}
 procedure SendText(s: string);
 var
   BytesWritten: DWORD;
 begin
    { 
     Add a word-wrap (#13 + #10) to the string 
     An den ubergebenen String einen Zeilenumbruch (#13 + #10) hangen 
   }
   s := s + #13 + #10;
   WriteFile(ComFile, s[1], Length(s), BytesWritten, nil);
 end;
 { 
  The following is an example of using the 'ReadFile' function to read 
  data from the serial port. 
   
  Folgendes Beispiel verwendet die 'ReadFile' Funktion, um Daten 
  vom seriellen Port zu lesen. 
}
 procedure ReadText: string;
 var
   d: array[1..80] of Char;
   s: string;
   BytesRead, i: Integer;
 begin
   Result := '';
   if not ReadFile(ComFile, d, SizeOf(d), BytesRead, nil) then
   begin
     { Raise an exception }
   end;
   s := '';
   for i := 1 to BytesRead do s := s + d[I];
   Result := s;
 end;
 procedure CloseCOMPort;
 begin
   // finally close the COM Port! 
  // nicht vergessen den COM Port wieder zu schliessen! 
  CloseHandle(ComFile);
 end;

Инсталляция и удаление сервисов под НТ.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Инсталляция/удаление сервисов под НТ.
Функции для создавания и удаления NT Services.
Можно создать NT Service от текущее приложение. Параметры:
1. CreateNTService(ExecutablePath,ServiceName: String)
ExecutablePath - Полный путь к изполнимого файла от которого создавается NT Service
ServiceName - Имя сервиза которое отобразится в Service Control Manager
Результат:
true - если операциая завершена успешно
false - если есть ошибка. Можно произвести call то
GetLastError чтобы информироваться об естество ошибки
2. DeleteNTService(ServiceName: String):boolean;
ServiceName - имя сервиза подлежающии удаления
Результат:
true - если операциая завершена успешно
false - если есть ошибка. Можно произвести call то GetLastError чтобы
информироваться об естество ошибки
Зависимости: WinSVC, Windows
Автор:       Alex Kantchev, stoma@bitex.bg
Copyright:   Собственное написание
Дата:        19 июня 2002 г.
***************************************************** }
// CreateNTService(ExecutablePath,ServiceName: String)
// ExecutablePath - Полный путь к изполнимого файла от
// которого создавается NT Service
// ServiceName - Имя сервиза которое отобразится
// в Service Control Manager Результат:
//Результат:
// true - если операциая завершена успешно
// false - если есть ошибка. Можно произвести
// call то GetLastError чтобы информироваться об
// естество ошибки
function CreateNTService(ExecutablePath, ServiceName: string): boolean;
var
  hNewService, hSCMgr: SC_HANDLE;
  // Rights: DWORD;
  FuncRetVal: Boolean;
begin
  FuncRetVal := False;
  hSCMgr := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
  if (hSCMgr <> 0) then
  begin
    //Custom service access rights may be built here
    //we use GENERIC_EXECUTE which is combination of
    //STANDARD_RIGHTS_EXECUTE, SERVICE_START, SERVICE_STOP,
    //SERVICE_PAUSE_CONTINUE, and SERVICE_USER_DEFINED_CONTROL
    //You can create own rights and use them as shown in the
    //commented line below.
    //Rights := STANDARD_RIGHTS_REQUIRED or SERVICE_START or SERVICE_STOP
    // or SERVICE_QUERY_STATUS or SERVICE_PAUSE_CONTINUE or
    // SERVICE_INTERROGATE;
    hNewService := CreateService(hSCMgr, PChar(ServiceName), PChar(ServiceName),
      STANDARD_RIGHTS_REQUIRED, SERVICE_WIN32_OWN_PROCESS,
      SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
      PChar(ExecutablePath), nil, nil, nil, nil, nil);
    CloseServiceHandle(hSCMgr);
    if (hNewService <> 0) then
      FuncRetVal := true
    else
      FuncRetVal := false;
  end;
  CreateNTService := FuncRetVal;
end;
// ***
//DeleteNTService(ServiceName: String):boolean;
// ServiceName - имя сервиза подлежающии удаления
//Результат:
// true - если операциая завершена успешно
// false - если есть ошибка. Можно произвести call то GetLastError чтобы
// информироваться об естество ошибки
function DeleteNTService(ServiceName: string): boolean;
var
  hServiceToDelete, hSCMgr: SC_HANDLE;
  RetVal: LongBool;
  FunctRetVal: Boolean;
begin
  FunctRetVal := false;
  hSCMgr := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
  if (hSCMgr <> 0) then
  begin
    hServiceToDelete := OpenService(hSCMgr, PChar(ServiceName),
      SERVICE_ALL_ACCESS);
    RetVal := DeleteService(hServiceToDelete);
    CloseServiceHandle(hSCMgr);
    FunctRetVal := RetVal;
  end;
  DeleteNTService := FunctRetVal;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  tmpS: string;
begin
  tmpS := 'Delphi_Service_' + Application.Title;
  if (CreateNTService(Application.ExeName, tmpS)) then
    MessageDlg('Service ' + tmpS + ' has been successfully created!',
      mtInformation, [mbOK], 0)
  else
    MessageDlg('Unable to create service ' + tmpS + ' Win32 Error code: ' +
      IntToStr(GetLastError), mtWarning, [mbOK], 0);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  tmpS: string;
begin
  tmpS := 'Delphi_Service_' + Application.Title + '1';
  if (DeleteNTService(tmpS)) then
    MessageDlg('Service ' + tmpS + ' has been successfully deleted!',
      mtInformation, [mbOK], 0)
  else
    MessageDlg('Unable to delete service ' + tmpS + ' Win32 Error code: ' +
      IntToStr(GetLastError), mtWarning, [mbOK], 0);
end;

Как заказать сервисный процесс.

Автор: Admin WEB-сайт: http://forum.vingrad.ru

unit Stealth;
interface
uses
  WinTypes, WinProcs, Classes, Forms, SysUtils, Controls, Messages;
type
  TStealth = class(TComponent)
  private
    fHideApp: Boolean;
    procedure SetHideApp(Value: Boolean);
  protected
    { Protected declarations }
    procedure HideApplication;
    procedure ShowApplication;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    // procedure Loaded; override;
  published
    { Published declarations }
    property HideApp: Boolean read fHideApp write SetHideApp default false;
  end;
function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer; stdcall;
  external 'KERNEL32.DLL';
procedure Register;
implementation
destructor TStealth.Destroy;
begin
  ShowApplication;
  inherited destroy;
end;
constructor TStealth.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  // fHideform := true;
end;
procedure TStealth.SetHideApp(Value: Boolean);
begin
  fHideApp := Value;
  if Value then
    HideApplication
  else
    ShowApplication;
end;
procedure TStealth.HideApplication;
begin
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(GetCurrentProcessID, 1);
end;
procedure TStealth.ShowApplication;
begin
  if not (csDesigning in ComponentState) then
    RegisterServiceProcess(GetCurrentProcessID, 0);
end;
procedure Register;
begin
  RegisterComponents('My', [TStealth]);
end;
end.

SetFocus в TStringGrid.

Grid.Row := YourRowNumber;
Grid.TopRow := YourFirstVisibleRow;
Grid.LeftCol := YourFirstVisibleColumn; 

SetFocus в Edit на OnExit.

Встречаются как то НАШ Российский хакер и ихний БИЛ ГЕЙТС! ГЕЙТС: Слушай у тебя случайно Wind(Ы)2000 нет, а то Рождество на Носу, а подарить друзьям нечего? Все деньги жена забирает! ХАКЕР: Есть! Что за вопросы! А ТЕБЕ КАКУЮ, Русскую или Английскую?
Я пробую выполнить editbox.SetFocus и/или editbox.Clear, но но это не дает никакого эффекта (по крайней мере видимого). Что я делаю неправильно? Вы посылаете команду на изменение фокуса внутри обработчика, который сам устанавливает фокус, этим вы получаете банальную рекурсию. Я избегаю этого путем отправления собственного сообщения в обработчике OnExit, после чего в обработчике моего сообщения выставляю логический флажок, предохраняющий код от рекурсии, поскольку данный флажок контролируется в обработчике OnExit.

Следующие строки содержат необходимый код:

interface
........
const
  WM_MyExitRtn = WM_USER + 1001;
  ........
  type
  TForm1 = class(TForm)
    .........
    private
    bExitInProgress: Boolean; {предохраняемся от рекурсий сообщений}
  public
    procedure WMMyExitRtn(var msg: TMessage); message WM_MyExitRtn;
  end;
  .........
  implementation
.........
procedure TForm1.DBEdit1Exit(Sender: TObject);
begin
  if not bExitInProgress then
    PostMessage(Handle, WM_MyExitRtn, 0, LongInt(Sender));
end;
.........
procedure TForm1.WMMyExitRtn(var msg: TMessage);
begin
  bExitInProgress := True; { предохраняемся от рекурсивного вызова }
  {здесь содержится необходимый код }
  bExitInProgress := False; { сбрасываем флаг }
end;

Правила для SetRange.

Автор: Josh Я, похоже, обнаружил неплохое решение ранжирования данных - вам необходимо только установить различные начальный и конечный диапазоны последнего поля, определенного вашим индексом, после чего введенное в любое поле индекса значение будет проигнорировано. Также вы не сможете покинуть ранжируемое поле, если редактируемая запись пуста.

Попытаюсь изложить все попроще... Скажем, у меня есть индекс Field1; Field2; Field3, затем;

SetRangeStart;
Table1Field1.Value := x1;
Table1Field2.Value := y1;
Table1Field3.Value := z1;
SetRangeEnd;
Table1Field1.Value := x2;
Table1Field2.Value := y2;
Table1Field3.Value := z2;
ApplyRange;
Правила...

X1 должен равняться x2, если y или z определен.

Y1 должен равняться y2, если z определен.

X должен быть определен, если y или z определены.

Y должен быть определен, если x определен.

Если x1 = x2 и никаких других критериев не определено, тогда y1 и y2 должны быть соответственно min/max значениями y.

Если x1 = x2 и y1 = y2 и никаких других критериев не определено, тогда z1 и z2 должны быть соответственно min/max значениями z.

Я не знаю, поняли вы это или нет, но надеюсь это поможет...

Пример SetText, GetText строкового поля.

Автор: Mike Orriss

Ниже приведен простой пример поля ID, содержащего A,B,C, для вывода (и обновления) *всех* элементов управления как 1,2,3:

procedure TForm1.Table1IDGetText(Sender: TField;
  var Text: OpenString; DisplayText: Boolean);
var
  s: string;
begin
  s := (Sender as TStringField).Value;
  case s[1] of
    'A': Text := '1';
    'B': Text := '2';
    'C': Text := '3';
  else
    Text := '9'
  end;
end;
procedure TForm1.Table1IDSetText(Sender: TField; const Text: string);
var
  s: string;
begin
  case Text[1] of
    '1': s := 'A';
    '2': s := 'B';
    '3': s := 'C';
  else
    s := 'Z'
  end;
  (Sender as TStringField).Value := s;
end;

Установка привилегий.

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

Не забуду мать родную - ZX Spectrum навсегда!
{ 
  For some functions you need to get the right privileges 
  on a Windows NT machine. 
  (e.g: To shut down or restart windows with ExitWindowsEx or 
  to change the system time) 
  The following code provides a procedure to adjust the privileges. 
  The AdjustTokenPrivileges() function enables or disables privileges 
  in the specified access token. 
} 
// NT Defined Privileges from winnt.h 
const 
  SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege'; 
  SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege'; 
  SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege'; 
  SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege'; 
  SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege'; 
  SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege'; 
  SE_TCB_NAME = 'SeTcbPrivilege'; 
  SE_SECURITY_NAME = 'SeSecurityPrivilege'; 
  SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege'; 
  SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege'; 
  SE_SYSTEM_PROFILE_NAME = 'SeSystemProfilePrivilege'; 
  SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege'; 
  SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege'; 
  SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege'; 
  SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege'; 
  SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege'; 
  SE_BACKUP_NAME = 'SeBackupPrivilege'; 
  SE_RESTORE_NAME = 'SeRestorePrivilege'; 
  SE_SHUTDOWN_NAME = 'SeShutdownPrivilege'; 
  SE_DEBUG_NAME = 'SeDebugPrivilege'; 
  SE_AUDIT_NAME = 'SeAuditPrivilege'; 
  SE_SYSTEM_ENVIRONMENT_NAME = 'SeSystemEnvironmentPrivilege'; 
  SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege'; 
  SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege'; 
  SE_UNDOCK_NAME = 'SeUndockPrivilege'; 
  SE_SYNC_AGENT_NAME = 'SeSyncAgentPrivilege'; 
  SE_ENABLE_DELEGATION_NAME = 'SeEnableDelegationPrivilege'; 
  SE_MANAGE_VOLUME_NAME = 'SeManageVolumePrivilege'; 
// Enables or disables privileges debending on the bEnabled 
function NTSetPrivilege(sPrivilege: string; bEnabled: Boolean): Boolean; 
var 
  hToken: THandle; 
  TokenPriv: TOKEN_PRIVILEGES; 
  PrevTokenPriv: TOKEN_PRIVILEGES; 
  ReturnLength: Cardinal; 
begin 
  Result := True; 
  // Only for Windows NT/2000/XP and later. 
  if not (Win32Platform = VER_PLATFORM_WIN32_NT) then Exit; 
  Result := False; 
  // obtain the processes token 
  if OpenProcessToken(GetCurrentProcess(), 
    TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then 
  begin 
    try 
      // Get the locally unique identifier (LUID) . 
      if LookupPrivilegeValue(nil, PChar(sPrivilege), 
        TokenPriv.Privileges[0].Luid) then 
      begin 
        TokenPriv.PrivilegeCount := 1; // one privilege to set 
        case bEnabled of 
          True: TokenPriv.Privileges[0].Attributes  := SE_PRIVILEGE_ENABLED; 
          False: TokenPriv.Privileges[0].Attributes := 0; 
        end; 
        ReturnLength := 0; // replaces a var parameter 
        PrevTokenPriv := TokenPriv; 
        // enable or disable the privilege 
        AdjustTokenPrivileges(hToken, False, TokenPriv, SizeOf(PrevTokenPriv), 
          PrevTokenPriv, ReturnLength); 
      end; 
    finally 
      CloseHandle(hToken); 
    end; 
  end; 
  // test the return value of AdjustTokenPrivileges. 
  Result := GetLastError = ERROR_SUCCESS; 
  if not Result then 
    raise Exception.Create(SysErrorMessage(GetLastError)); 
end;

SetVolume почему-то не дает никакого эффекта.

Новая компьютерная игра МУ-МУ. Герасим использует Му-му по умолчанию.

В модуле MMSystem есть функции.

waveOutSetVolume(WAVE_MAPPER, AVolume) midiOutSetVolume(MIDI_MAPPER, AVolume)

Которые регулируют громкость в зависимости от формата музыки.

AVolume: Longint - старший разряд - правый канал, младший - левый

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

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

procedure TForm1.SetProperties(ClassName, SomeProperty: string; Value: Integer);
 var
   i: integer;
   PropInfo: PPropInfo;
   Component: TComponent;
 begin
   for i := 0 to ComponentCount - 1 do
   begin
     Component := Components[i];
     if (Component is TControl) and ((Component.ClassName = ClassName)
       or (ClassName = 'AllComponents')) then
     begin
       PropInfo := GetPropInfo(Component.ClassInfo, SomeProperty);
       if Assigned(PropInfo) then
         SetOrdProp(Component, PropInfo, Integer(Value));
     end;
   end;
 end;
 // Examples, Beispiele: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   // Make all components readonly 
  SetProperties('AllComponents', 'readonly', 1);
   // Make all components of Class TEdit invisible 
  SetProperties('TEdit', 'visible', 0);
   // Set the Height Property of all TLabels 
  SetProperties('TLabel', 'Height', 30);
   // Set ShowHint to false for all components 
  SetProperties('AllComponents', 'ShowHint', 0);
   // Disable all TMemo Components 
  SetProperties('TMemo', 'Enabled', 0);
   // Set Autosize to true for all TLabels 
  SetProperties('TLabel', 'Autosize', 1);
 end;

Установка режима бинарного файла.

Автор: Steve

Попробуйте вызвать приведенную ниже процедуру немедленно после перезаписи выходного файла и перед началом записи в него:

procedure SetBinaryMode(var F: file); assembler;
asm
  mov ax,$4400
  les di,F
  mov bx,word ptr es:[di]
  int $21
  or dl,$20
  xor dh,dh
  mov ax,$4401
  int $21
end;

Установка определенного бита в слове.

Автор: Григорий Ситнин WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Установка определенного бита в слове
Возвращает AWord с установленным битом номер ABit (c 0 по 15) в значение 1,
если AState = TRUE или 0, если AState = FALSE.
Стоит заметить, что в функцию передается именно номер бита, а не маска.
Проверка на правильный номер не производится.
Зависимости: System
Автор:       Григорий Ситнин, gregor@gregor.ru, Москва
Copyright:   Григорий Ситнин, 2003
Дата:        8 июля 2003 г.
***************************************************** }
function SetBit(AWord: word; ABit: byte; AState: boolean = true): word;
begin
  if AState then
    Result := AWord or (1 shl ABit)
  else
    Result := AWord and (not (1 shl ABit));
end;

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

{$APPTYPE CONSOLE}
program test;
uses Bits; // В модуле Bits описана функция SetBit
var
  z: word;
  i: integer;
  s: string;
begin
  z := 0;
  z := setbit(z, 0); // 0000000000000001
  z := setbit(z, 2); // 0000000000000101
  z := setbit(z, 9); // 0000001000000101
  z := setbit(z, 14); // 0100001000000101
  z := setbit(z, 15); // 1100001000000101
  s := '';
  for i := 15 downto 0 do
    s := s + inttostr(Ord(CheckBit(z, i)));
  writeln('value: ', z, ' dec = 1100001000000101');
  writeln('result: ', s);
  z := $FFFF;
  z := setbit(z, 0, false); // 1111111111111110
  z := setbit(z, 2, false); // 1111111111111010
  z := setbit(z, 9, false); // 1111110111111010
  z := setbit(z, 14, false); // 1011110111111010
  z := setbit(z, 15, false); // 0011110111111010
  s := '';
  for i := 15 downto 0 do
    s := s + inttostr(Ord(CheckBit(z, i)));
  writeln('value: ', z, ' dec = 0011110111111010');
  writeln('result: ', s);
end.

Установка времени компиляции программы в диалоговом окне О программе.

Я подразумаваю, что проблема состоит в получении времени компиляции?

Var
  F: Integer;
  S: String;
Begin
  F:=FileOpen(ExpandFileName(Application.ExeName), 0);
  S:=TimeToStr(FileDateToDateTime(FileGetDate(F)));
  FileClose(F);
End;
Взгляните на описание функции DateTime... в файле помощи. Вероятно, существует лучший способ получения времени без использования функции FileOpen.

Также можно использовать время файла (File Time) в качестве номера версии, так, время 6:02 обозначало бы версию 6.02, и устанавливать его чем-то типа Touch.

Установить флаг DTR и RTS в активное состояние для определённого COM-порта.

Автор: Slava V

После открытия com-порта через CreateFile() необходимо с помощью GetCommState() и SetCommState() установить параметры порта (в частности убрать автоуправление RTS и DTR). Затем для установки соответствующих сигналов используется функция EscapeCommFunction().

Procedure ControlRTS;
Var
  S: String;
  lDCB: TDCB;
  fHandle: THandle;
Begin
  S:='COM1';
  // open port
  fHandle:=CreateFile( Pchar(S), GENERIC_READ Or GENERIC_WRITE, 0,
  Nil, OPEN_EXISTING, 0, 0);
  If fHandle=INVALID_HANDLE_VALUE Then
  Begin
    // can't open....
    Exit;
  End;
  // read settings
  If Not GetCommState( fHandle, lDCB) Then
  Begin
    // can't read
    Exit;
  End;
  // Fill dcb
  lDCB.BaudRate:=CBR_2400;
  lDCB.ByteSize:=8;
  lDCB.Parity:=NOPARITY;
  lDCB.StopBits:=ONESTOPBIT;
  // !!! we will manage RTS ourself !!!
  ldcb.Flags:=(ldcb.Flags And $FFFFC0FF) Or $00000100;
  // set comm state
  SetCommState( fHandle, ldcb);
  // Here we can manage
  // Reset RTS
  EscapeCommFunction( fHandle, CLRRTS);
  // Set RTS
  EscapeCommFunction( fHandle, SETRTS);
  // Close port
  CloseHandle(fHandle);
  fHandle:=0;
End;

P.S. Надо заметить, что все выходы com-порта являются инверсными. А это значит, что активным сосотоянием порта является 0, а неактивным 1.

Как программно установить конфигурацию COM-порта.

procedure TForm1.Button1Click(Sender: TObject);
var
  CommPort: string;
  hCommFile: THandle;
  Buffer: PCommConfig;
  size: DWORD;
begin
  CommPort := 'COM1';
  {Открываем Com-порт}
  hCommFile := CreateFile(PChar(CommPort),
  GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if hCommFile = INVALID_HANDLE_VALUE then
  begin
    ShowMessage('Unable to open ' + CommPort);
    exit;
  end;
  {Выделяем временный буфер}
  GetMem(Buffer, sizeof(TCommConfig));
  {Получаем размер структуры CommConfig}
  size := 0;
  GetCommConfig(hCommFile, Buffer^, size);
  {Освобождаем временный буфер}
  FreeMem(Buffer, sizeof(TCommConfig));
  {Выделяем память для структуры CommConfig}
  GetMem(Buffer, size);
  GetCommConfig(hCommFile, Buffer^, size);
  {Изменяем скорость передачи}
  Buffer^.dcb.BaudRate := 1200;
  {Устанавливаем новую конфигурацию для COM-порта}
  SetCommConfig(hCommFile, Buffer^, size);
  {Освобождаем буфер}
  FreeMem(Buffer, size);
  {Закрываем COM-порт}
  CloseHandle(hCommFile);
end;

Установка или снятие Debug привелегии у текущего процесса.

Автор: Мироводин Дмитрий WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Установка/снятие Debug привелегии у текущего процесса
Функция устанавливает/снимает отладочные привелегии у текущего процесса
(можно выбрать и другой, изменив GetCurrentProcess на нужный Handle ).
Актуально для совместного использования с ToolHelp -
т.е. получения информации о процессах.
Зависимости: Windows
Автор:       Мироводин Дмитрий (адаптация), mirovodin@mail.ru
Copyright:   2000 Jeffrey Richter
Дата:        20 октября 2003 г.
***************************************************** }
function EnableDebugPrivilege(const Value: Boolean): Boolean;
const
  SE_DEBUG_NAME = 'SeDebugPrivilege';
var
  hToken: THandle;
  tp: TOKEN_PRIVILEGES;
  d: DWORD;
begin
  Result := False;
  if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken) then
  begin
    tp.PrivilegeCount := 1;
    LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid);
    if Value then
      tp.Privileges[0].Attributes := $00000002
    else
      tp.Privileges[0].Attributes := $80000000;
    AdjustTokenPrivileges(hToken, False, tp, SizeOf(TOKEN_PRIVILEGES), nil, d);
    if GetLastError = ERROR_SUCCESS then
    begin
      Result := True;
    end;
    CloseHandle(hToken);
  end;
end;

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

// После этого можно смотреть информацию о таких системных
// модулях как: winlogon.exe и servises.exe и д.р.
EnableDebugPrivilege(True); // вкрючить
EnableDebugPrivilege(False); // выключить

Вывести список установленных принтеров и установить принтер по умолчанию.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    cbPrinters: TComboBox;
    lblPrinter: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure cbPrintersChange(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses IniFiles, Printers;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
  { Copy the printer names to the combobox and set the combobox to
    show the currently selected default printer }
  cbPrinters.Items.Assign(Printer.Printers);
  cbPrinters.Text := Printer.Printers[Printer.PrinterIndex];
  // Update the label to reflect the default printer
  lblPrinter.Caption := Printer.Printers[Printer.PrinterIndex];
end;
procedure TMainForm.cbPrintersChange(Sender: TObject);
var
  IniFile: TIniFile;
  TempStr1, TempStr2: string;
  S: array[0..64] of char;
begin
  with Printer do
  begin
    // Set the new printer based on the ComboBox's selected printer
    PrinterIndex := cbPrinters.ItemIndex;
    // Store the printer name into a temporary string
    TempStr1 := Printers[PrinterIndex];
    // Delete the unnecessary portion of the printer name
    System.Delete(TempStr1, Pos(' on ', TempStr1), Length(TempStr1));
    // Create a TIniFile class
    IniFile := TIniFile.Create('WIN.INI');
    try
      // Retrieve the device name of the selected printer
      TempStr2 := IniFile.ReadString('Devices', TempStr1, '');
      // Change the default printer to that chosen by the user
      IniFile.WriteString('windows', 'device', TempStr1 + ',' + TempStr2);
      // Tell all windows that the default printer changed.
      StrCopy(S, 'windows');
      SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, LongInt(@S));
    finally
      IniFile.Free;
    end;
  end;
  // Update the label to reflect the new printer selection
  lblPrinter.Caption := Printer.Printers[Printer.PrinterIndex];
end;
end.

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

Автор: Сергей.

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

function GetDesktopListViewHandle: THandle;
var
  S: String;
begin
  Result := FindWindow('ProgMan', nil);
  Result := GetWindow(Result, GW_CHILD);
  Result := GetWindow(Result, GW_CHILD);
  SetLength(S, 40);
  GetClassName(Result, PChar(S), 39);
  if PChar(S) <> 'SysListView32' then 
    Result := 0;
end;
После того, как Вы взяли тот хэндл, Вы можете использовать API этого ListView, определенный в модуле CommCtrl, для того, чтобы манипулировать рабочим столом. Смотрите тему "LVM_xxxx messages" в оперативной справке по Win32.

К примеру, следующая строка кода:

{Не забудьте в uses добавить CommCtrl}
ListView_SetItemPosition(GetDesktopListViewHandle,i,x,y);
ярлыку с индексом i, задаст координаты (x,y). К примеру Мой компьютер имеет индекс 0, т.е i:=0; С наилучшими пожеланиями, Сергей Nomadic дополняет:

К примеру, следующая строка кода:

SendMessage( GetDesktopListViewHandle, LVM_ALIGN, LVA_ALIGNLEFT, 0 ); 

Разместит иконки рабочего стола по левой стороне рабочего стола Windows.

Изменение даты создания каталога.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Изменение даты создания каталога
Две перегруженные функции, изменяющие дату и время создания каталога.
Первая принимает непосредственные значения года, месяца, дня, часа,
минут и секунд. Во второй дата и время указываются в формате TDateTime.
См. также: http://delphibase.endimus.com/?action=viewfunc&topic=fileattr&id=10496
Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.com, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        4 февраля 2004 г.
***************************************************** }
function SetDirTime(const Dir: string; Year, Month, Day, Hour, Minute, Second:
  Word): Boolean; overload;
var
  H: Integer;
  F: TFileTime;
  S: TSystemTime;
begin
  H := CreateFile(PChar(Dir), $0100, 0, nil, OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS, 0);
  if H <> -1 then
  begin
    S.wYear := Year;
    S.wMonth := Month;
    S.wDay := Day;
    S.wHour := Hour;
    S.wMinute := Minute;
    S.wSecond := Second;
    SystemTimeToFileTime(S, F);
    LocalFileTimeToFileTime(F, F);
    Result := Boolean(SetFileTime(H, @F, @F, @F));
    CloseHandle(H);
  end
  else
    Result := False;
end;
function SetDirTime(const Dir: string; DateTime: TDateTime): Boolean; overload;
var
  H: Integer;
  F: TFileTime;
  S: TSystemTime;
begin
  H := CreateFile(PChar(Dir), $0100, 0, nil, OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS, 0);
  if H <> -1 then
  begin
    DateTimeToSystemTime(DateTime, S);
    SystemTimeToFileTime(S, F);
    LocalFileTimeToFileTime(F, F);
    Result := Boolean(SetFileTime(H, @F, @F, @F));
    CloseHandle(H);
  end
  else
    Result := False;
end;

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

if not SetDirTime('c:\Program Files', 2004, 2, 4, 12, 0, 0) then
  ShowMessage('Error!');

Как установить переменные окружения.

- Чем Windows похожи на презерватив? - Никому не нравится, но все используют.

Следующая простая подпрограмма создаёт новые значения в переменных окружения. Если переменной окружения не существует, то она создаётся. Если переменной окружения установить значение пустой строки, то переменная удаляется. Функция возвращает 0, если значение переменной установлено или переменная создана успешно, либо возвратит значение ошибки Windows вслучае неудачи. Обратите внимание, что размер пространства доступного для переменных окружения ограничен.

function SetEnvVarValue(const VarName, VarValue: string): Integer;
begin
  // Просто вызываем API функцию
  if Windows.SetEnvironmentVariable(PChar(VarName), PChar(VarValue)) then
    Result := 0
  else
    Result := GetLastError;
end;

ЗАМЕЧАНИЕ: данный способ позволяет делать изменения в переменных окружения только для текущего процесса либо для дочерних процессов, порождённых текущим.

Для того, чтобы передать какую-либо переменную окружения в дочерний процесс просто:

Создайте новую переменную окружения при помощи SetDOSEnvVar. Запустите новую программу.

А вот как выглядит пример передачи текущих переменных окружения + переменной FOO=Bar в дочерний процесс:

var
  ErrCode: Integer;
begin
  ErrCode := SetEnvVarValue('FOO', 'Bar');
  if ErrCode = 0 then
    WinExec('MyChildProg.exe', SW_SHOWNORMAL);
  else
    ShowMessage(SysErrorMessage(ErrCode));
end;

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

Как установить значение строкового или целого поля если оно присутствует.

Автор: Xavier Pacheco I am building a routine that checks our forms for validity before deploying them. I would like to use some kind of structure that tests if a component type has access to a certain property, something like: " if (self.Controls[b] has Tag) then ...". Can anyone offer suggestions?

Here's an example of setting a string property for a component if it exists and another for an integer property:

procedure SetStringPropertyIfExists(AComp: TComponent; APropName: string;
  AValue: string);
var
  PropInfo: PPropInfo;
  TK: TTypeKind;
begin
  PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    TK := PropInfo^.PropType^.Kind;
    if (TK = tkString) or (TK = tkLString) or (TK = tkWString) then
      SetStrProp(AComp, PropInfo, AValue);
  end;
end;
procedure SetIntegerPropertyIfExists(AComp: TComponent; APropName: string;
  AValue: Integer);
var
  PropInfo: PPropInfo;
begin
  PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    if PropInfo^.PropType^.Kind = tkInteger then
      SetOrdProp(AComp, PropInfo, AValue);
  end;
end;

Устанавливаем дату создания файла.

Function SetFileDate(
  Const FileName : String;
  Const FileDate : TDateTime): Boolean;
var
 FileHandle        : THandle;
 FileSetDateResult : Integer;
begin
 try
  try
   FileHandle := FileOpen
      (FileName,
       fmOpenWrite OR fmShareDenyNone);
   if FileHandle > 0 Then  begin
    FileSetDateResult :=
      FileSetDate(
        FileHandle,
        DateTimeToFileDate(FileDate));
      result := (FileSetDateResult = 0);
    end;
  except
   Result := False;
  end;
 finally
  FileClose (FileHandle);
 end;
end;
{Использование:}
SetFileDate('c:\mydir\myfile.ext', Now)

Выставить дату для файла.

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

function SetFileDateTime(FileName: string; NewDateTime: TDateTime): Boolean;
 var
   FileHandle: Integer;
   FileTime: TFileTime;
   LFT: TFileTime;
   LST: TSystemTime;
 begin
   Result := False;
   try
     DecodeDate(NewDateTime, LST.wYear, LST.wMonth, LST.wDay);
     DecodeTime(NewDateTime, LST.wHour, LST.wMinute, LST.wSecond, LST.wMilliSeconds);
     if SystemTimeToFileTime(LST, LFT) then
     begin
       if LocalFileTimeToFileTime(LFT, FileTime) then
       begin
         FileHandle := FileOpen(FileName, fmOpenReadWrite or
           fmShareExclusive);
         if SetFileTime(FileHandle, nil, nil, @FileTime) then
           Result := True;
       end;
     end;
   finally
     FileClose(FileHandle);
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if OpenDialog1.Execute then
     if SetFileDateTime(OpenDialog1.FileName, now) then
       ShowMessage('Date set to now !');
 end;

Установка времени и даты файла.

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

var
  f: file;
begin
  Assign(f, DirInfo.Name);
  Reset(f);
  SetFTime(f, Time);
  Close(f);
end;

Установка ловушек в Windows.

Если вы хотите чаще встречаться с понравившейся девушкой установите ей Windows'95
Сегодня мы поговорим об установке hоок'ов (ловушек) в Windows. Hook - это механизм перехвата сообщений, путем установки специальной функции на верх стека hook-функций системы. Без установки таких ловушек практически невозможно обойтись при написании различных средств удаленного администрирования, шпионов и других программ в той или иной степени осуществляющих контроль за пользователем, использующем ОС Windows. Ноок'и бывают глобальные (на всю систему) и локальные (на какой-либо поток). Установить в систему hook можно при помощи функции SetWindowsHookEx(), со следующим заголовком:
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
Если ты плохо воспринимаешь Си-шный код, на Delphi заголовок выглядит так:
	SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc;
    			     hmod: HINST; dwThreadId: DWORD): HHOOK;
Функция SetWindowsHookEx() в случае установки hook'a возвращает его дескриптор, в случае ошибки возвращает 0. Разберем подробней все входящие параметры этой функции: 1. idHook - константа, определяет типа устанавливаемого hоок'а. Может принимать одно из ниже перечисленных значений: WH_CALLWNDPROC - Следит за сообщениями до отправки в оконную функцию и вызывается, когда процедуре окна посылается сообщение. Ловушка срабатывает при каждом вызове функции SendMessage. WH_CALLWNDPROCRET - Контролирует сообщения после их отправки в оконную функцию. WH_CBT - Вызывается перед обработкой большинства сообщений окон, мыши и клавиатуры (созданием окон, активация окон, уничтожением окон, сменой размера окон, перед установкой фокуса и.т.п.) WH_DEBUG - Вызывается перед любой другой ловушкой. Полезно для отладки hоок'ов. WH_GETMESSAGE - Вызывается, когда из очереди приложения считывается сообщение. WH_HARDWARE - Вызывается, когда из очереди приложения считывается сообщение установленного на компьютере оборудования. WH_JOURNALPLAYBACK - Вызывается, когда из очереди системы считывается сообщение. Применяется для добавления в очередь системных событий. WH_JOURNALRECORD - Вызывается, когда из очереди системы запрашивается какое-нибудь событие. Применяется для регистрации системных событий. WH_KEYBOARD - Вызывается, когда из очереди приложения считывается сообщения WM_Keydown или WM_Keyup. Одна из самых распространенных ловушек -). WH_MOUSE - Вызывается, когда из очереди приложения считывается сообщение мыши. WH_MSGFILTER - Вызывается, когда сообщение должно быть обработано диалоговым окном приложения, меню или окном приложения. WH_SHELL - Вызывается, когда создаются и разрушаются окна верхнего уровня или когда приложению-оболочке требуется стать активным. 2. lpfn - указатель на саму hook функцию. Ее заголовок:
function HOOKFUNCTION(code: Integer; wparam: WPARAM;
    lparam: LPARAM): LRESULT stdcall;
Значения входящих параметров зависят от типа hook'a. Если ставится глобальный hook, эта функция должна обязательно находиться в dll. 3. hmod - принимает значение hInstance или дескриптор DLL (в глобальных ловушках). 4. dwThreadId - идентифицирует поток, в который вставляется ловушка. В глобальных hоок'ах этот параметр должен быть равен 0. Для удаления установленной ловушки существует функция UnhookWindowsHookEx(). В качестве параметра нужно использовать указатель (дескриптор) на hook функцию (значение, которое возвращает функция SetWindowsHookEx()). Ну вот и все, с основами мы ознакомлены. Теперь напишем маленькую шуточную программу, ставящую hook на считывания сообщений мыши (WH_MOUSE). Сделаем так, чтобы при нажатии на правую кнопку мыши скрывалась кнопка "Пуск", при нажатии на левую - появлялась, среднею - изменялся заголовок активного окна. Сама hook функция будет находиться в dll. Кроме того, в dll будут находиться две процедуры - sethook() и removehook(), соответственно устанавливающие и удаляющие ловушку.

Привожу код dll библиотеки:

library lib;
uses
  windows,messages;
var
  H : THandle;
{Hook-функция}
function hook(c0de, wParam, lParam : integer): Lresult; stdcall;
{Объявления переменных}
var
  w : THandle;
  hw : hwnd;
begin
{Если c0de не меньше 0, все в порядке, продолжаем}
if c0de >= 0 then
begin
  { Если wParam = WM_RBUTTONUP, т.е. нажата правая кнопка мыши, получаем
  хендл (handle) кнопки "Пуск" и скрываем ее }
  case wParam of
WM_RBUTTONUP :
begin
  W:= FindWindow('Shell_TrayWnd', nil);
  W:= FindWindowEx(W, HWND(0),'Button', nil);
  ShowWindow(W, SW_hide);
end;
{ Если wParam = WM_LBUTTONUP, т.е. нажата левая кнопка мыши, получаем
хендл кнопки пуск и показываем ее }
WM_LBUTTONUP:
begin
  W:= FindWindow('Shell_TrayWnd', nil);
  W:= FindWindowEx(W, HWND(0),'Button', nil);
  ShowWindow(W, SW_SHOW);
end;
{ Если wParam = WM_MBUTTONUP, т.е. нажата средняя кнопка мыши, получаем
указатель на заголовок активного окна и изменяем его }
WM_MBUTTONUP:
begin
  hw:=GetForegroundWindow;
  SetWindowText(hw,'EXAMPLE OF WINDOWS HOOK (WH_MOUSE) - by Dark Lord 
<darklord@smtp.ru>');
end;
end;
end else
{Если c0de меньше 0}
begin
  {Вызываем следующую ловушку в цепочке ловушек Windows и выходим из процедуры}
  result := CallNextHookEx(H, c0de, wParam, lParam);
  exit;
end;
{Вызываем следующую ловушку в цепочке ловушек Windows}
result := CallNextHookEx(H, c0de, wParam, lParam);
End;
{ Процедура установки ловушки, если не удалось
установить - выводим сообщение об ошибке }
procedure sethook;
begin
H:= SetWindowsHookEx(WH_MOUSE, @hook, hInstance, 0);
if H = 0 then
  messagebox(0,'hmmm..','ERROR',mb_iconhand);
end;
{ Процедура удаления ловушки }
procedure removehook;
begin
  UnhookWindowsHookEx(H);
end;
{ Экспорт процедур установки и удаления hook'a }
exports
sethook index 1  name 'sethook',
removehook index 2 name 'removehook';
end.

В самой программе ловушка будет устанавливаться вызовом из dll процедуры sethook, удаляться - вызовом процедуры removehook. Пример установки и удаления hоок'а и исходник dll библиотеки есть в прилагающемся исходнике. В данной статье были рассмотрены только основы установки ловушек. Есть немалое количество нюансов и возникающих проблем при установке нескольких hоок'ов, установки hоок'ов в разных ОС (особенно это касается глобальных ловушек). Для получения подробностей по этим вопросам рекомендую использовать Win32 API Reference или MSDN (если нет диска с MSDN - прогуляйтесь на http://msdn.microsoft.com, здесь правда не вся информация, но большая ее часть).

Как присвоить значение свойству Selected в ListBox.

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

Свойство "selected" компонента ТListВох может быть использованно только если свойство MultiSelect установленно в True. Если Вы работаете с ListВох'ом у которого MultiSelect=false то используйте свойство ItemIndex.

procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('1');
  ListBox1.Items.Add('2');
  {This will fail on a single selection ListBox}
  // ListBox1.Selected[1] := true;
  ListBox1.ItemIndex := 1; {This is ok}
end;

Включить или отключить Caps, Num или Scroll Lock.

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

type
   TKeyType = (ktCapsLock, ktNumLock, ktScrollLock);
 procedure SetLedState(KeyCode: TKeyType; bOn: Boolean);
 var
   KBState: TKeyboardState;
   Code: Byte;
 begin
   case KeyCode of
     ktScrollLock: Code := VK_SCROLL;
     ktCapsLock: Code := VK_CAPITAL;
     ktNumLock: Code := VK_NUMLOCK;
   end;
   GetKeyboardState(KBState);
   if (Win32Platform = VER_PLATFORM_WIN32_NT) then
   begin
     if Boolean(KBState[Code]) <> bOn then
     begin
       keybd_event(Code,
                   MapVirtualKey(Code, 0),
                   KEYEVENTF_EXTENDEDKEY,
                   0);
       keybd_event(Code,
                   MapVirtualKey(Code, 0),
                   KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP,
                   0);
     end;
   end
   else
   begin
     KBState[Code] := Ord(bOn);
     SetKeyboardState(KBState);
   end;
 end;
 // Example Call: 
// Beispielaufruf: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   SetLedState(ktCapsLock, True);  // CapsLock on 
  SetLedState(ktNumLock, True);  // NumLock on 
  SetLedState(ktScrollLock, True);  // ScrollLock on 
end;

Как перевести монитор в режим stand-by.

Автор: Kecvin S. Gallagher
В офисе два программиста беседуют: Первый: - У меня самый матовый монитор!!! Второй: - Это потому, что его неоднократно покрывали матом!!!

Если монитор поддерживает режим Stand by, то его можно программно перевести в этот режим. Данная возможность доступна на Windows95 и выше.

Чтобы перевести монитор в режим Stand by:

SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, 0);

Чтобы вывести его из этого режима:

SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, -1);
А теперь более полный пример кода: На новую форму поместите кнопку, таймер и ListBox. Timer (use Object Inspector): Enabled := False Interval := 15000

Добавьте следующее событие таймеру:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  ListBox1.Items.Add(FormatDateTime('h:mm:ss AM/PM', Time));
  SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, -1);
end;

Command Button:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('--> ' + FormatDateTime('h:mm:ss AM/PM', Time));
  Timer1.Enabled := not Timer1.Enabled;
  SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, 0);
end;
После запуска откомпилированного приложения и нажатия на кнопку, экран погаснет на 15 секунд.

ЗАМЕЧАНИЕ: Удостоверьтесь, что во первых компьютер поддерживает режимы энергосбережения, а вовторых, эти функции не запрещены на данном компьютере.

В TRichEdit или TMemo при нажатии Enter позиционирование курсора в позицию как в предыдущей строке.

Вопрос в службу поддержки: Что делать если я довел мышку до края коврика, а курсор не дошел до края экрана?
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ComCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    RichEdit1: TRichEdit;
    procedure RichEdit1KeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
  public
    { Public declarations }
end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
uses
  richedit;
procedure TForm1.RichEdit1KeyPress(Sender: TObject; var Key: Char);
var
  line, col, indent: integer;
  S: string;
begin
  if key = #13 then
  begin
    key := #0;
    with sender as TRichEdit do
    begin
      {figure out line and column position of caret}
      line := PerForm( EM_EXLINEFROMCHAR, 0, SelStart );
      Col := SelStart - Perform( EM_LINEINDEX, line, 0 );
      {get part of current line in front of caret}
      S:= Copy( lines[ line ], 1, col );
      {count blanks and tabs in this string}
      indent := 0;
      while (indent and (S[indent + 1] in [' ', #9]) do
        Inc( indent );
      {insert a linebreak followed by the substring of blanks and tabs}
      SelText := #13#10 + Copy(S, 1, indent);
    end;
  end;
end;
end.

Получить или установить имя компьютера.

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

{ Retrieve the computer name }
 function GetComputerName: string;
 var
   buffer: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char;
   Size: Cardinal;
 begin
   Size := MAX_COMPUTERNAME_LENGTH + 1;
   Windows.GetComputerName(@buffer, Size);
   Result := StrPas(buffer);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowMessage(GetComputerName);
 end;
 { Set the computer name }
 function SetComputerName(AComputerName: string): Boolean;
 var
   ComputerName: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char;
   Size: Cardinal;
 begin
   StrPCopy(ComputerName, AComputerName);
   Result := Windows.SetComputerName(ComputerName);
 end;
 procedure TForm1.Button2Click(Sender: TObject);
 begin
   if SetComputerName('NewComputerName') then
     ShowMessage('Computer Name Reset Setting will be used at next startup.')
   else
      ShowMessage('Computer Name Not Reset');
 end;

Как установить количество точек на дюйм в TPrinter.

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

Не делайте изменений после того, как принтер начал печатать.

Два примера:

uses Printers;
var
  MyFile: TextFile;
begin
  Printer.PrinterIndex := 2;
  AssignPrn(MyFile);
  Rewrite(MyFile);
  Printer.Canvas.Font.Name := 'Courier New';
  Printer.Canvas.Font.Style := [fsBold];
  Printer.Canvas.Font.PixelsPerInch :=
    GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY);
  Writeln(MyFile, 'Print this text');
  System.CloseFile(MyFile);
end;
uses Printers;
begin
  Printer.PrinterIndex := 2;
  Printer.BeginDoc;
  Printer.Canvas.Font.Name := 'Courier New';
  Printer.Canvas.Font.Style := [fsBold];
  Printer.Canvas.Font.PixelsPerInch :=
    GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY);
  Printer.Canvas.Textout(10, 10, 'Print this text');
  Printer.EndDoc;
end;

Публикование свойств-наборов.

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

myClass = class(TComponent)
  private { !! вы пропустили эту директиву защиты }
    fMySetProperty : myset;
  published
    MySetProperty : myset read fMySetProperty write fMySetProperty;
end;

Выставить значения свойств.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;
type
  TMainForm = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Panel1: TPanel;
    Button5: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Panel1Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses TypInfo;
{$R *.DFM}
procedure SetIntegerPropertyIfExists(AComp: TComponent; APropName: string;
  AValue: Integer);
var
  PropInfo: PPropInfo;
begin
  PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    if PropInfo^.PropType^.Kind = tkInteger then
      SetOrdProp(AComp, PropInfo, AValue);
  end;
end;
procedure SetObjectPropertyIfExists(AComponent: TComponent; APropName: string;
  AValue: TObject);
var
  PropInfo: PPropInfo;
begin
  PropInfo := GetPropInfo(AComponent.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    if PropInfo^.PropType^.Kind = tkClass then
      SetObjectProp(AComponent, PropInfo, AValue);
  end;
end;
procedure SetBooleanPropertyIfExists(AComp: TComponent; APropName: string;
  AValue: Boolean);
var
  PropInfo: PPropInfo;
begin
  PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    if PropInfo^.PropType^.Kind = tkEnumeration then
      SetOrdProp(AComp, PropInfo, Integer(AValue));
  end;
end;
procedure SetStringPropertyIfExists(AComp: TComponent; APropName: string;
  AValue: string);
var
  PropInfo: PPropInfo;
  TK: TTypeKind;
begin
  PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    TK := PropInfo^.PropType^.Kind;
    if (TK = tkString) or (TK = tkLString) or (TK = tkWString) then
      SetStrProp(AComp, PropInfo, AValue);
  end;
end;
procedure SetMethodPropertyIfExists(AComp: TComponent; APropName: string;
  AMethod: TMethod);
var
  PropInfo: PPropInfo;
begin
  PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
  if PropInfo <> nil then
  begin
    if PropInfo^.PropType^.Kind = tkMethod then
      SetMethodProp(AComp, PropInfo, AMethod);
  end;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
  SetStringPropertyIfExists(Button1, 'Caption', 'Yahoo');
end;
procedure TMainForm.Button2Click(Sender: TObject);
begin
  SetIntegerPropertyIfExists(Button2, 'Width', 50);
end;
procedure TMainForm.Button3Click(Sender: TObject);
begin
  SetBooleanPropertyIfExists(Button3, 'Enabled', False);
end;
procedure TMainForm.Button4Click(Sender: TObject);
var
  F: TFont;
begin
  F := TFont.Create;
  F.Name := 'Arial';
  F.Size := 24;
  F.Color := clRed;
  SetObjectPropertyIfExists(Panel1, 'Font', F);
end;
procedure TMainForm.Button5Click(Sender: TObject);
begin
  SetMethodPropertyIfExists(Button5, 'OnClick',
    GetMethodProp(Panel1, 'OnClick'));
end;
procedure TMainForm.Panel1Click(Sender: TObject);
begin
  ShowMessage(Button5.Caption);
end;
end.

Как установить значение свойства компонента по имени.

Автор: Jeff Overcash WEB-сайт: http://www.lmc-mediaagentur.de

You can use RTTI to do this. Here is how to change a particular component:

procedure TForm1.BtnClick(Sender: TObject);
var
  p: PPropInfo;
  f: TFont;
begin
  f := TFont.Create;
  {Setup the font properties}
  f.Name := 'Arial';
  p := GetPropInfo(Sender.ClassInfo, 'Font');
  if Assigned(p) then
    SetOrdProp(Sender, p, Integer(f));
  f.Free;
end;

To get at all the forms loop through the Screen global variable. For each form loop through its Components list calling the above procedure (or something close). If you only create your components at design time that is it. If you create some at runtime and the owner is not the form, then for each component loop through its Components list recursively to get at all the owned components.

Как указать максимальный размер текста для RichEdit Control.

У этого компонента есть свойство MaxLength, которое работает некорректно. Поэтому лучше пользоваться.

RichEdit.Perform(EM_LIMITTEXT, нужный размер, 0);

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

Если Вы передаете в качестве размера 0, то ОС ограничивает размер OS Specific Default Value. Реально, по результатам моих экспериментов, поставить можно размер, чуть меньший доступной виртуальной памяти. Я ограничился 90% от свободной виртуалки.

Для того, чтобы не повторять этот вызов (EM_LIMITTEXT), можно воспользоваться сообщением EM_EXLIMITTEXT.

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

Билл Гейтс собрал Microsoft на конференцию и говорит: - Господа, мы установили, что половина людей использующих Windows имеет возраст больше медианного! Другая потрясающая новость - это то, что другая половина людей имеет возраст ниже медианного! - Простите, что такое медианный возраст? - Этого мы еще не установили.

Следующие несколько строк кода позволяют установить системную дату и время без использования панели управления. Дата и время устанавливаются двумя раздельными компонентами TDateTimePicker. Дата и время декодируются и передаются в API функцию.

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

procedure TfmTime.btnTimeClick(Sender: TObject);
var
  vsys: _SYSTEMTIME;
  vYear, vMonth, vDay, vHour, vMin, vSec, vMm: Word;
begin
  DecodeDate( Trunc(dtpDate.Date), vYear, vMonth, vDay );
  DecodeTime( dtpTime.Time, vHour, vMin, vSec, vMm );
  vMm := 0;
  vsys.wYear := vYear;
  vsys.wMonth := vMonth;
  vsys.wDay := vDay;
  vsys.wHour := ( vHour - 2 );
  vsys.wMinute := vMin;
  vsys.wSecond := vSec;
  vsys.wMilliseconds := vMm;
  vsys.wDayOfWeek := DayOfWeek( Trunc(dtpDate.Date) );
  SetSystemTime( vsys );
end;

Выставить системные переменные среды.

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

{*********************************************}
 { Set Global Environment Function             }
 { Coder : Kingron,2002.8.6                    }
 { Bug Report : Kingron@163.net                }
 { Test OK For Windows 2000 Advance Server     }
 { Parameter:                                  }
 { Name : environment variable name            }
 { Value: environment variable Value           }
 { Ex: SetGlobalEnvironment('MyVar','OK')      }
 {*********************************************}
 function SetGlobalEnvironment(const Name, Value: string;
   const User: Boolean = True): Boolean;
 resourcestring
   REG_MACHINE_LOCATION = 'System\CurrentControlSet\Control\Session Manager\Environment';
   REG_USER_LOCATION = 'Environment';
 begin
   with TRegistry.Create do
     try
       if User then { User Environment Variable }
         Result := OpenKey(REG_USER_LOCATION, True)
       else { System Environment Variable }
       begin
         RootKey := HKEY_LOCAL_MACHINE;
         Result  := OpenKey(REG_MACHINE_LOCATION, True);
       end;
       if Result then
       begin
         WriteString(Name, Value); { Write Registry for Global Environment }
         { Update Current Process Environment Variable }
         SetEnvironmentVariable(PChar(Name), PChar(Value));
         { Send Message To All Top Window for Refresh }
         SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment')));
       end;
     finally
       Free;
     end;
 end; { SetGlobalEnvironment }

Установка шрифтов для системного меню.

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

procedure TForm1.Button1Click(Sender: TObject);
 var
   ncm: TNonClientMetrics;
 begin
   ncm.cbSize := SizeOf(TNonClientMetrics);
   {get old non client metrics}
   SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SizeOf(TNonClientMetrics), @ncm, 0);
   { 
  lfCaptionFont - regular captions 
  lfSmCaptionFont - small captions 
  lfMenuFont - menus 
  lfStatusFont - status bars 
  lfMessageFont - message boxes 
  }
   ncm.lfMessageFont.lfFaceName := 'Tahoma';
   {set new non client metrics}
   SystemParametersInfo(SPI_SETNONCLIENTMETRICS, SizeOf(TNonClientMetrics), @ncm, 0);
 end;

Назначение прав пользователей на таблицу.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Назначение прав пользователей на таблицу
Какое может быть описание? И так все понятно. Кто делает эти формы?
Зависимости: uses IBQuery,IBDataBase,SysUtils
Автор:       Dracula, dracula@krruda.dp.ua, Krivoy Rog
Copyright:   Dracula
Дата:        28 января 2003 г.
***************************************************** }
unit IBRights;
interface
uses IBQuery, IBDataBase, SysUtils;
type
  TIBUserRights = record
    sel: Boolean;
    ins: Boolean;
    upd: Boolean;
    del: Boolean;
    exe: Boolean;
  end;
function GetUserRights(UserName, Relation: string; IBDb: TIBDataBase):
  TIBUserRights;
var
  Rights: TIBUserRights;
implementation
function GetUserRights(UserName, Relation: string; IBDb: TIBDataBase):
  TIBUserRights;
var
  Qr: TIBQuery;
  Tr: TIBTransaction;
begin
  if Assigned(IBDb) then
  begin
    Tr := TIBTransaction.Create(nil);
    try
      Tr.DefaultDatabase := IBDb;
      Qr := TIBQuery.Create(nil);
      Qr.Database := IBDb;
      Tr.StartTransaction;
      Qr.Close;
      Qr.Sql.Clear;
      Qr.Sql.Add('select RDB$USER,RDB$PRIVILEGE,RDB$RELATION_NAME ' +
        'from RDB$USER_PRIVILEGES ' +
        'where upper(RDB$USER)=:AUser ' +
        'and upper(RDB$RELATION_NAME)=:ARelation');
      Qr.Prepare;
      Qr.Params.ParamValues['AUser'] := AnsiUpperCase(UserName);
      Qr.Params.ParamValues['ARelation'] := AnsiUpperCase(Relation);
      Qr.Open;
      while not Qr.eof do
      begin
        if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'S' then
          Rights.sel := true
        else
          Rights.sel := false;
        if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'I' then
          Rights.ins := true
        else
          Rights.ins := false;
        if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'U' then
          Rights.upd := true
        else
          Rights.upd := false;
        if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'D' then
          Rights.del := true
        else
          Rights.del := false;
        if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'X' then
          Rights.exe := true
        else
          Rights.exe := false;
        if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'R' then
          //Rights.ref:=true else Rights.ref:=false;
        begin
          Rights.sel := true;
          Rights.ins := true;
          Rights.upd := true;
          Rights.del := true;
          Rights.exe := true;
        end;
        Qr.Next;
      end;
      Qr.Close;
      Tr.Commit;
      Qr.Free;
    finally
      Tr.Free;
    end;
  end;
  Result := Rights;
end;
end.

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

uses....., IBRights;
......
var
  rights: TIBUserRights;
implementation
.....
begin
  ...
  rights := GetUserRights(DllLogin, NameTable, IBDataBase);
  .....
end;

Переместить курсор в TRichEdit на нужную позицию.

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

// You can move the caret in a TRichEdit component by using this code : 
procedure RichEdit_MoveTo(RichEdit: TRichEdit; LineNumber, CharNumber: Word);
 begin
   RichEdit.SelStart := RichEdit.Perform(EM_LINEINDEX, LineNumber, 0) + CharNumber);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   RichEdit_MoveTo(RichEdit1,2,5);
   Application.ProcessMessages;
   RichEdit1.SetFocus;
 end;

Выставление и сброс битов.

SetWord - слово, которое необходимо установить. BitNum - номер бита, который необходимо выставить согласно определениям в секции const (Bit0, Bit1 и др...). GetBitStat возвращает Истину, если бит установлен и Ложь в противном случае.

{ Слово записывается следующим образом...            }
{ Слово      -  0  0  0  0  0  0 0 0 0 0 0 0 0 0 0 0 }
{ Номер бита - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 }
const
  Bit0 = 1;
  Bit1 = 2;
  Bit2 = 4;
  Bit3 = 8;
  Bit4 = 16;
  Bit5 = 32;
  Bit6 = 64;
  Bit7 = 128;
  Bit8 = 256;
  Bit9 = 512;
  Bit10 = 1024;
  Bit11 = 2048;
  Bit12 = 4096;
  Bit13 = 8192;
  Bit14 = 16384;
  Bit15 = 32768;
procedure SetBit(SetWord, BitNum: Word);
begin
  SetWord := SetWord or BitNum; { Устанавливаем бит }
end;
procedure ClearBit(SetWord, BitNum: Word);
begin
  SetWord := SetWord or BitNum; { Устанавливаем бит }
  SetWord := SetWord xor BitNum; { Переключаем бит   }
end;
procedure ToggleBit(SetWord, BitNum: Word);
begin
  SetWord := SetWord xor BitNum; { Переключаем бит   }
end;
function GetBitStat(SetWord, BitNum: Word): Boolean;
begin
  GetBitStat := SetWord and BitNum = BitNum { Если бит установлен }
end;

Установить метку тома файловой системы.

BOOL SetVolumeLabel(
    LPCTSTR lpRootPathName,     // адрес имени корневой директории тома
    LPCTSTR lpVolumeName        // метка тома
   );
Параметры lpRootPathName – указывает на завершающуюся нулем строку, определяющую корневую директорию тома файловой системы, метка которого будет устанавливаться. Если значение этого параметра равно NULL, используется корень текущей директории. lpVolumeName – указывает на строку, определяющую метку тома. Если значение этого параметра равно NULL, то функция удаляет метку указанного тома. Возвращаемые значения В случае успеха возвращается ненулевое значение. В случае неудачи возвращается нуль. Для получения дополнительной информации об ошибке вызовите функцию GetLastError.

См. также: GetVolumeInformation.

Перевернуть текст в TStringGrid на 90 градусов.

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

uses
   {...} Grids;
 type
   TForm1 = class(TForm)
     StringGrid1: TStringGrid;
     procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
       Rect: TRect; State: TGridDrawState);
   end;
 {...}
 implementation
 {...}
 // Display text vertically in StringGrid cells 
// Vertikale Textausgabe in den Zellen eines StringGrid 
procedure StringGridRotateTextOut(Grid: TStringGrid; ARow, ACol: Integer; Rect: TRect;
   Schriftart: string; Size: Integer; Color: TColor; Alignment: TAlignment);
 var
   lf: TLogFont;
   tf: TFont;
 begin
   // if the font is to big, resize it 
  // wenn Schrift zu gro? dann anpassen 
  if (Size > Grid.ColWidths[ACol] div 2) then
     Size := Grid.ColWidths[ACol] div 2;
   with Grid.Canvas do
   begin
     // Replace the font 
    // Font setzen 
    Font.Name := Schriftart;
     Font.Size := Size;
     Font.Color := Color;
     tf := TFont.Create;
     try
       tf.Assign(Font);
       GetObject(tf.Handle, SizeOf(lf), @lf);
       lf.lfEscapement  := 900;
       lf.lfOrientation := 0;
       tf.Handle := CreateFontIndirect(lf);
       Font.Assign(tf);
     finally
       tf.Free;
     end;
     // fill the rectangle 
    // Rechteck fullen 
    FillRect(Rect);
     // Align text and write it 
    // Text nach Ausrichtung ausgeben 
    if Alignment = taLeftJustify then
       TextRect(Rect, Rect.Left + 2,Rect.Bottom - 2,Grid.Cells[ACol, ARow]);
     if Alignment = taCenter then
       TextRect(Rect, Rect.Left + Grid.ColWidths[ACol] div 2 - Size +
         Size div 3,Rect.Bottom - 2,Grid.Cells[ACol, ARow]);
     if Alignment = taRightJustify then
       TextRect(Rect, Rect.Right - Size - Size div 2 - 2,Rect.Bottom -
         2,Grid.Cells[ACol, ARow]);
   end;
 end;
 // 2. Alternative: Display text vertically in StringGrid cells 
// 2. Variante: Vertikale Textausgabe in den Zellen eines StringGrid 
procedure StringGridRotateTextOut2(Grid:TStringGrid;ARow,ACol:Integer;Rect:TRect;
           Schriftart:String;Size:Integer;Color:TColor;Alignment:TAlignment);
 var
     NewFont, OldFont : Integer;
     FontStyle, FontItalic, FontUnderline, FontStrikeout: Integer;
 begin
    // if the font is to big, resize it 
   // wenn Schrift zu gro? dann anpassen 
   If (Size > Grid.ColWidths[ACol] DIV 2) Then
        Size := Grid.ColWidths[ACol] DIV 2;
    with Grid.Canvas do
    begin
        // Set font 
       // Font setzen 
       If (fsBold IN Font.Style) Then
           FontStyle := FW_BOLD
        Else
           FontStyle := FW_NORMAL;
        If (fsItalic IN Font.Style) Then
           FontItalic := 1
        Else
           FontItalic := 0;
        If (fsUnderline IN Font.Style) Then
           FontUnderline := 1
        Else
           FontUnderline := 0;
        If (fsStrikeOut IN Font.Style) Then
           FontStrikeout:=1
        Else
           FontStrikeout:=0;
        Font.Color := Color;
        NewFont := CreateFont(Size, 0, 900, 0, FontStyle, FontItalic,
                              FontUnderline, FontStrikeout, DEFAULT_CHARSET,
                              OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                              DEFAULT_PITCH, PChar(Schriftart));
        OldFont := SelectObject(Handle, NewFont);
        // fill the rectangle 
       // Rechteck fullen 
       FillRect(Rect);
        // Write text depending on the alignment 
       // Text nach Ausrichtung ausgeben 
       If Alignment = taLeftJustify Then
           TextRect(Rect,Rect.Left+2,Rect.Bottom-2,Grid.Cells[ACol,ARow]);
        If Alignment = taCenter Then
           TextRect(Rect,Rect.Left+Grid.ColWidths[ACol] DIV 2 - Size + Size DIV 3,
             Rect.Bottom-2,Grid.Cells[ACol,ARow]);
        If Alignment = taRightJustify Then
           TextRect(Rect,Rect.Right-Size - Size DIV 2 - 2,Rect.Bottom-2,Grid.Cells[ACol,ARow]);
        // Recreate reference to the old font 
       // Referenz auf alten Font wiederherstellen 
       SelectObject(Handle, OldFont);
        // Recreate reference to the new font 
       // Referenz auf neuen Font loschen 
       DeleteObject(NewFont);
    end;
 end;
 // Call the method in OnDrawCell 
// Methode im OnDrawCell aufrufen 
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol,
   ARow: Integer; Rect: TRect; State: TGridDrawState);
 begin
   // In the second column: Rotate Text by 90° and left align the text 
  // Text um 90°gedreht ausgeben, linksbundig in der zweiten Spalte 
  if ACol = 1 then
     StringGridRotateTextOut(StringGrid1, ARow, ACol, Rect, 'ARIAL',
       12,clRed, taLeftJustify);
   // In the third column: Center the text 
  // Ausgabe zentriert in der dritten Spalte 
  if ACol = 2 then
     StringGridRotateTextOut(StringGrid1, ARow, ACol, Rect, 'ARIAL', 12, clBlue, taCenter);
   // In all other columns third row: right align the text 
  // Ausgabe rechtsbundig in den restlichen Spalten 
  if ACol > 2 then
     StringGridRotateTextOut(StringGrid1, ARow, ACol, Rect, 'ARIAL', 12,clGreen,
       taRightJustify);
 end;
 end.

Добавление колонки в StringGrid.

Автор: http://www.sources.ru

procedure GridAddColumn(StrGrid: TStringGrid; NewColumn: Integer);
var
  Column: Integer;
begin
  StrGrid.ColCount := StrGrid.ColCount + 1;
  for Column := StrGrid.ColCount - 1 downto NewColumn do
    StrGrid.Cols[Column].Assign(StrGrid.Cols[Column - 1]);
  StrGrid.Cols[NewColumn - 1].Text := '';
end;

Добавление колонки в StringGrid 2.

Автор: http://www.sources.ru

procedure AddColumn(SG: TStringGrid; AtColNumber: integer;
  ColWidth: integer = 0);
var
  Column: integer;
  Wdth: integer;
begin
  AtColNumber := abs(AtColNumber);
  SG.ColCount := SG.ColCount + 1;
  if abs(ColWidth) = 0 then
    Wdth := SG.DefaultColWidth
  else
    Wdth := ColWidth;
  if AtColNumber <= SG.ColCount then
  begin
    for Column := SG.ColCount - 1 downto AtColNumber + 1 do
    begin
      SG.Cols[Column].Assign(SG.Cols[Column - 1]);
      SG.Colwidths[Column] := SG.Colwidths[Column - 1];
    end;
    SG.Cols[AtColNumber].Text := '';
    SG.Colwidths[AtColNumber] := Wdth;
  end;
end;

Получить содержимое TStringGrid или TDrawGrid в виде строки.

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

{ 
 This copies the contents of a TstringGrid/TDrawGrid (only Text!!) into a string. 
 Tabs are inserted between the columns, CR+LF between rows. 
}
 use
   Grids;
 {...}
 { we need this Cracker Class because the Col/RowCount property 
  is not public in TCustomGrid }
 type
   TGridHack = class(TCustomGrid);
 function GetstringGridText(_Grid: TCustomGrid): string;
 var
   Grid: TGridHack;
   Row, Col: Integer;
   s: string;
 begin
   // Cast the paramter to a TGridHack, so we can access protected properties 
  Grid   := TGridHack(_Grid);
   Result := '';
   // for all rows, then for all columns 
  for Row := 0 to Grid.RowCount - 1 do
   begin
     for Col := 0 to Grid.ColCount - 1 do
     begin
       // the first column does not need the tab 
      if Col > 0 then
         Result := Result + #9;
       Result := Result + Grid.GetEditText(Col, Row);
     end;
     Result := Result + #13#10;
 end;
 end;

Автоматический подбор ширины колонок для TStringGrid.

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

 procedure SetGridColumnWidths(Grid: TStringGrid;
   const Columns: array of Integer);
 { 
  When you double-Click on a Column-Header the Column 
  autosizes to fit its content 
  Bei Doppelklick auf eine fixierte Spalte passt sich 
  die Spaltenbreite der Textgrosse an 
}
   procedure AutoSizeGridColumn(Grid: TStringGrid; column, min, max: Integer);
     { Set for max and min some minimal/maximial Values}
     { Bei max and min kann eine Minimal- resp. Maximalbreite angegeben werden}
   var
     i: Integer;
     temp: Integer;
     tempmax: Integer;
   begin
     tempmax := 0;
     for i := 0 to (Grid.RowCount - 1) do
     begin
       temp := Grid.Canvas.TextWidth(Grid.cells[column, i]);
       if temp > tempmax then tempmax := temp;
       if tempmax > max then
       begin
         tempmax := max;
         break;
       end;
     end;
     if tempmax < min then tempmax := min;
     Grid.ColWidths[column] := tempmax + Grid.GridLineWidth + 3;
   end;
   procedure TForm1.StringGrid1DblClick(Sender: TObject);
   var
     P: TPoint;
     iColumn, iRow: Longint;
   begin
     GetCursorPos(P);
     with StringGrid1 do
     begin
       P := ScreenToClient(P);
       MouseToCell(P.X, P.Y, iColumn, iRow);
       if P.Y < DefaultRowHeight then
         AutoSizeGridColumn(StringGrid1, iColumn, 40, 100);
     end;
   end;

Автоматический подбор ширины колонок для TStringGrid 2.

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

   procedure TForm1.Button1Click(Sender: TObject);
   { by P. Below }
   const
     DEFBORDER = 8;
   var
     max, temp, i, n: Integer;
   begin
     with Grid do
     begin
       Canvas.Font := Font;
       for n := Low(Columns) to High(Columns) do
       begin
         max := 0;
         for i := 0 to RowCount - 1 do
         begin
           temp := Canvas.TextWidth(Cells[Columns[n], i]) + DEFBORDER;
           if temp > max then
             max := temp;
         end; { For }
         if max > 0 then
           ColWidths[Columns[n]] := max;
       end; { For }
     end; { With }
   end; {SetGridColumnWidths  }

Выравнивание ячеек в TStringGrid.

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

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
   Rect: TRect; State: TGridDrawState);
   procedure WriteText(StringGrid: TStringGrid; ACanvas: TCanvas; const ARect: TRect;
     const Text: string; Format: Word);
   const
     DX = 2;
     DY = 2;
   var
     S: array[0..255] of Char;
     B, R: TRect;
   begin
     with Stringgrid, ACanvas, ARect do
     begin
       case Format of
         DT_LEFT: ExtTextOut(Handle, Left + DX, Top + DY,
             ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text), Length(Text), nil);
         DT_RIGHT: ExtTextOut(Handle, Right - TextWidth(Text) - 3, Top + DY,
             ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text),
             Length(Text), nil);
         DT_CENTER: ExtTextOut(Handle, Left + (Right - Left - TextWidth(Text)) div 2,
             Top + DY, ETO_OPAQUE or ETO_CLIPPED, @ARect,
             StrPCopy(S, Text), Length(Text), nil);
       end;
     end;
   end;
   procedure Display(StringGrid: TStringGrid; const S: string; Alignment: TAlignment);
   const
     Formats: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
   begin
     WriteText(StringGrid, StringGrid.Canvas, Rect, S, Formats[Alignment]);
   end;
 begin
   // Right-justify columns 0-2 
  // Spalten 0-2 rechts ausrichten. 
  if ACol in [0..2] then
     Display(StringGrid1, StringGrid1.Cells[ACol, ARow], taRightJustify)
     // Center the first row 
    // Erste zeile zentrieren 
    if ARow = 0 then
       Display(StringGrid1, StringGrid1.Cells[ACol, ARow], taCenter)
   end;

Очистить ячейки в TStringGrid.

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

 procedure TForm1.Button1Click(Sender: TObject);
 var
   i, k: Integer;
 begin
   with StringGrid1 do
     for i := 0 to ColCount - 1 do
       for k := 0 to RowCount - 1 do
         Cells[i, k] := '';
 end;

Очистить ячейки в TStringGrid 2.

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

 // Many times faster! 
// Um einiges schneller! 
procedure TForm1.Button2Click(Sender: TObject);
 var
   I: Integer;
 begin
   for I := 0 to StringGrid1.RowCount - 1 do
     StringGrid1.Rows[I].Clear();
 end;

Автоматический подбор ширины колонок для TStringGrid или TDrawGrid.

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

{   This will resize the columns of a TStringGrid / TDrawGrid (text 
    only!) so the text is completely visble. To save some time, 
    it uses the first 10 rows only, but that should be easy to fix, 
    if you need more. }
 // we need this to access protected methods 
type
   TGridHack = class(TCustomGrid);
 procedure ResizeStringGrid(_Grid: TCustomGrid);
 var
   Col, Row: integer;
   Grid: TGridHack;
   MaxWidth: integer;
   ColWidth: integer;
   ColText: string;
   MaxRow: integer;
   ColWidths: array of integer;
 begin
   Grid := TGridHack(_Grid);
   SetLength(ColWidths, Grid.ColCount);
   MaxRow := 10;
   if MaxRow > Grid.RowCount then
     MaxRow := Grid.RowCount;
   for Col := 0 to Grid.ColCount - 1 do
   begin
     MaxWidth := 0;
     for Row := 0 to MaxRow - 1 do
     begin
       ColText  := Grid.GetEditText(Col, Row);
       ColWidth := Grid.Canvas.TextWidth(ColText);
       if ColWidth > MaxWidth then
         MaxWidth := ColWidth;
     end;
     if goVertLine in Grid.Options then
       Inc(MaxWidth, Grid.GridLineWidth);
     ColWidths[Col]      := MaxWidth + 4;
     Grid.ColWidths[Col] := ColWidths[Col];
   end;
 end;

Реализация события OnColumnClick из TListView для TStringGrid.

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

{ 
 There are two routines to implement the OnColumnClick Methods for a TStringGrid. 
 Set the first row as fixed and the Defaultdrawing to True. 
 Mit folgenden zwei Routinen kann man in einem TStringgrid 
 die Methode OnColumnClick eines TListView erzeugen (visuell). 
 Reihe 0 mu? fixiert sein undDefaultDrawing = True 
}
 type
   TForm1 = class(TForm)
     StringGrid1: TStringGrid;
     procedure StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
     procedure StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
   private
     zelle: TRect; // cell 
    acol, arow: Integer;
   public
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
   Shift: TShiftState; X, Y: Integer);
 var
   Text: string;
 begin
   with stringgrid1 do
   begin
     MouseRoCell(x, y, acol, arow);
     if (arow = 0) and (button = mbleft) then
       case acol of
         0..2:
           begin
             // Draws a 3D Effect (Push) 
            // Zeichnet 3D-Effekt (Push) 
            zelle := CellRect(acol, arow);
             Text := Cells[acol, arow];
             Canvas.Font := Font;
             Canvas.Brush.Color := clBtnFace;
             Canvas.FillRect(zelle);
             Canvas.TextRect(zelle, zelle.Left + 2, zelle.Top + 2, Text);
             DrawEdge(Canvas.Handle, zelle, 10, 2 or 4 or 8);
             DrawEdge(Canvas.Handle, zelle, 2 or 4, 1);
           end;
       end;
   end;
 end;
 procedure TForm1.StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
   Shift: TShiftState; X, Y: Integer);
 var
   Text: string;
 begin
   with StringGrid1 do
   begin
     // Draws a 3D-Effect (Up) 
    // Zeichnet 3D-Effekt (Up) 
    Text := Cells[acol, arow];
     if arow = 0 then
     begin
       Canvas.Font := Font;
       Canvas.Brush.Color := clBtnFace;
       Canvas.FillRect(zelle);
       Canvas.TextRect(zelle, zelle.Left + 2, zelle.Top + 2, Text);
       DrawEdge(Canvas.Handle, zelle, 4, 4 or 8);
       DrawEdge(Canvas.Handle, zelle, 4, 1 or 2);
       MouseToCell(zelle.Left, zelle.Top, acol, arow);
     end;
   end;
   if (arow = 0) and (Button = mbleft) then
     case acol of
       0..2:
         begin
           // Code to be executed... 
          // Programmcode der ausgefuhrt werden soll 
          ShowMessage('Column ' + IntToStr(acol));
           zelle := stringgrid1.CellRect(1, 1);
         end;
     end;
 end;
 end.

Обратиться к колонке или строке StringGrid по заголовку.

В следующем примере приведены две функции: GetGridColumnByName() и GetGridRowByName(), которые возвращают колонку или строку, имеющую заданный заголовок (caption).

Пример:

procedure TForm1.FormCreate(Sender: TObject);
begin
  StringGrid1.Rows[1].Strings[0] := 'This Row';
  StringGrid1.Cols[1].Strings[0] := 'This Column';
end;
function GetGridColumnByName(Grid: TStringGrid; ColName: string): integer;
var
  i: integer;
begin
  for i := 0 to Grid.ColCount - 1 do
    if Grid.Rows[0].Strings[i] = ColName then
    begin
      Result := i;
      exit;
    end;
  Result := -1;
end;
function GetGridRowByName(Grid: TStringGrid; RowName: string): integer;
var
  i: integer;
begin
  for i := 0 to Grid.RowCount - 1 do
    if Grid.Cols[0].Strings[i] = RowName then
    begin
      Result := i;
      exit;
    end;
  Result := -1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Column: integer;
  Row: integer;
begin
  Column := GetGridColumnByName(StringGrid1, 'This Column');
  if Column = -1 then
    ShowMessage('Column not found')
  else
    ShowMessage('Column found at ' + IntToStr(Column));
  Row := GetGridRowByName(StringGrid1, 'This Row');
  if Row = -1 then
    ShowMessage('Row not found')
  else
    ShowMessage('Row found at ' + IntToStr(Row));
end;

Сортировать TStringGrid по колонке.

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

type
   TMoveSG = class(TCustomGrid); // reveals protected MoveRow procedure 
{...}
 procedure SortGridByCols(Grid: TStringGrid; ColOrder: array of Integer);
 var
   i, j:   Integer;
   Sorted: Boolean;
 function Sort(Row1, Row2: Integer): Integer;
 var
   C: Integer;
 begin
   C      := 0;
   Result := AnsiCompareStr(Grid.Cols[ColOrder[C]][Row1], Grid.Cols[ColOrder[C]][Row2]);
   if Result = 0 then
   begin
     Inc(C);
     while (C <= High(ColOrder)) and (Result = 0) do
     begin
       Result := AnsiCompareStr(Grid.Cols[ColOrder[C]][Row1],
         Grid.Cols[ColOrder[C]][Row2]);
       Inc(C);
     end;
   end;
 end;
 begin
   if SizeOf(ColOrder) div SizeOf(i) <> Grid.ColCount then Exit;
   for i := 0 to High(ColOrder) do
     if (ColOrder[i] < 0) or (ColOrder[i] >= Grid.ColCount) then Exit;
   j := 0;
   Sorted := False;
   repeat
     Inc(j);
     with Grid do
       for i := 0 to RowCount - 2 do
         if Sort(i, i + 1) > 0 then
         begin
           TMoveSG(Grid).MoveRow(i + 1, i);
           Sorted := False;
         end;
   until Sorted or (j = 1000);
   Grid.Repaint;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   { Sort rows based on the contents of two or more columns. 
    Sorts first by column 1. If there are duplicate values 
    in column 1, the next sort column is column 2 and so on...}
   SortGridByCols(StringGrid1, [1, 2, 0, 3, 4]);
 end;

Копировать и вставлять ячейки TStringGrid в буфер обмена.

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

uses
   Clipbrd;
 //Copy 
procedure TForm1.Button1Click(Sender: TObject);
 var
   S: string;
   GRect: TGridRect;
   C, R: Integer;
 begin
   GRect := StringGrid1.Selection;
   S  := '';
   for R := GRect.Top to GRect.Bottom do
   begin
     for C := GRect.Left to GRect.Right do
     begin
       if C = GRect.Right then  S := S + (StringGrid1.Cells[C, R])
       else
         S := S + StringGrid1.Cells[C, R] + #9;
     end;
     S := S + #13#10;
 end;
   ClipBoard.AsText := S;
 end;
 // Paste 
procedure TForm1.Button2Click(Sender: TObject);
 var
   Grect: TGridRect;
   S, CS, F: string;
   L, R, C: Byte;
 begin
   GRect := StringGrid1.Selection;
   L := GRect.Left;
   R := GRect.Top;
   S := ClipBoard.AsText;
   R := R - 1;
   while Pos(#13, S) > 0 do
   begin
     R  := R + 1;
     C  := L - 1;
     CS := Copy(S, 1,Pos(#13, S));
     while Pos(#9, CS) > 0 do
     begin
       C := C + 1;
       if (C <= StringGrid1.ColCount - 1) and (R <= StringGrid1.RowCount - 1) then
         StringGrid1.Cells[C, R] := Copy(CS, 1,Pos(#9, CS) - 1);
       F := Copy(CS, 1,Pos(#9, CS) - 1);
       Delete(CS, 1,Pos(#9, CS));
     end;
     if (C <= StringGrid1.ColCount - 1) and (R <= StringGrid1.RowCount - 1) then
       StringGrid1.Cells[C + 1,R] := Copy(CS, 1,Pos(#13, CS) - 1);
     Delete(S, 1,Pos(#13, S));
     if Copy(S, 1,1) = #10 then
       Delete(S, 1,1);
   end;
 end;

Удаление колонки в StringGrid.

Автор: http://www.sources.ru

procedure GridRemoveColumn(StrGrid: TStringGrid; DelColumn: Integer);
var
  Column: Integer;
begin
  if DelColumn <= StrGrid.ColCount then
  begin
    for Column := DelColumn to StrGrid.ColCount - 1 do
      StrGrid.Cols[Column - 1].Assign(StrGrid.Cols[Column]);
    StrGrid.ColCount := StrGrid.ColCount - 1;
  end;
end;

Удаление колонки в StringGrid 2.

Автор: http://www.sources.ru

procedure RemoveColumn(SG: TStringGrid; ColNumber: integer);
var
  Column: integer;
begin
  ColNumber := abs(ColNumber);
  if ColNumber <= SG.ColCount then
  begin
    for Column := ColNumber to SG.ColCount - 2 do
    begin
      SG.Cols[Column].Assign(SG.Cols[Column + 1]);
      SG.Colwidths[Column] := SG.Colwidths[Column + 1];
    end;
    SG.ColCount := SG.ColCount - 1;
  end;
end;

Удаление колонки в TStringGrid.

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

type
   TStringGridHack = class(TStringGrid)
   public
     procedure DeleteCol(ACol: Longint);
   end;
 var
   Form1: TForm1;
 implementation
 procedure TStringGridHack.DeleteCol(ACol: Longint);
 begin
   if ACol = FixedCols then if ACol = (ColCount - 1) then
     begin
       Cols[ACol].Clear;
       if ColCount(FixedCols + 1) then ColCount := (ColCount - 1);
     end
     else
     begin
       Cols[ACol] := Cols[ACol + 1];
       DeleteCol(ACol + 1);
     end;
 end;

Удалить строку из TStringGrid.

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

procedure GridDeleteRow(RowNumber: Integer; Grid: TstringGrid);
 var
   i: Integer;
 begin
   Grid.Row := RowNumber;
   if (Grid.Row = Grid.RowCount - 1) then
     { On the last row}
     Grid.RowCount := Grid.RowCount - 1
   else
   begin
     { Not the last row}
     for i := RowNumber to Grid.RowCount - 1 do
       Grid.Rows[i] := Grid.Rows[i + 1];
     Grid.RowCount := Grid.RowCount - 1;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   GridDeleteRow(3, stringGrid1);
 end;

Переход по ячейкам в TStringGrid по клавиши Enter как по Tab.

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

procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
 begin
   if Key = #13 then
     with StringGrid1 do
       if Col then {next column}
         Col := Col + 1
     else if Row then
     begin {next Row}
       Row := Row + 1;
       Col := 1;
     end
      else
     begin {End of Grid- Go to Top again}
       Row := 1;
       Col := 1;
     end;
 end;

Вставка и удаление строк из TStringGrid.

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

// For this tip you need a StringGrid1 and a Button1. 
// Fur diesen Tip braucht man ein StringGrid1 und einen Button1. 
{...}
 type
   TForm1 = class(TForm)
     StringGrid1: TStringGrid;
     Button1: TButton;
     procedure Button1Click(Sender: TObject);
   private
     {...}
   public
     {...}
   end;
 type
   TStringGridHack = class(TStringGrid)
   protected
     procedure DeleteRow(ARow: Longint); reintroduce;
     procedure InsertRow(ARow: Longint);
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.DFM}
 procedure TStringGridHack.DeleteRow(ARow: Longint);
 var
   GemRow: Integer;
 begin
   GemRow := Row;
   if RowCount > FixedRows + 1 then
     inherited DeleteRow(ARow)
   else
     Rows[ARow].Clear;
   if GemRow < RowCount then Row := GemRow;
 end;
 procedure TStringGridHack.InsertRow(ARow: Longint);
 var
   GemRow: Integer;
 begin
   GemRow := Row;
   while ARow < FixedRows do Inc(ARow);
   RowCount := RowCount + 1;
   MoveRow(RowCount - 1, ARow);
   Row := GemRow;
   Rows[Row].Clear;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   // Insert Row, Zeile hinzufugen 
  TStringGridHack(StringGrid1).InsertRow(1);
   // Remove Row, Zeile entfernen 
  TStringGridHack(StringGrid1).DeleteRow(2);
 end;
 end.

Определить, отображаются ли полосы прокрутки для TStringGrid.

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

if (GetWindowlong(Stringgrid1.Handle, GWL_STYLE) and WS_VSCROLL) <> 0 then
   ShowMessage('Vertical scrollbar is visible!');
 if (GetWindowlong(Stringgrid1.Handle, GWL_STYLE) and WS_HSCROLL) <> 0 then
   ShowMessage('Horizontal scrollbar is visible!');

Многострочный TStringGrid 4.

Автор: Пётр Соболь.

Была необходимость в использовании многострочного TStringGrida. Не один из трех способов создания не сработал, Переработал статью о подобной проблеме с ТDВGгidом. Получилось нечто очень компактное, чем и решил поделиться. Обработка того же события прорисовки, в uses надо добавить WinProcs:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow:
  Integer;
  Rect: TRect; State: TGridDrawState);
var
  Format: Word;
  C: array[0..255] of Char;
begin
  Format := DT_LEFT or DT_WORDBREAK;
  (Sender as TStringGrid).Canvas.FillRect(Rect);
  StrPCopy(C, (Sender as TStringGrid).Cells[ACol, ARow]);
  WinProcs.DrawText((Sender as TStringGrid).Canvas.Handle, C,
    StrLen(C), Rect, Format);
end;

Увеличение ячейки TStringGrid при увеличении числа строк.

Автор: Пётр Соболь.

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow:
  Integer; Rect: TRect; State: TGridDrawState);
var
  Format: Word;
  C: array[0..255] of Char;
  r: integer;
begin
  C := '';
  Format := DT_LEFT or DT_WORDBREAK;
  (Sender as TStringGrid).Canvas.FillRect(Rect);
  StrPCopy(C, (Sender as TStringGrid).Cells[ACol, ARow]);
  if c <> '' then //если есть значения
  begin
    r := WinProcs.DrawText((Sender as TStringGrid).Canvas.Handle, C,
      StrLen(C), Rect, Format);
    if r > (Sender as TStringGrid).RowHeights[Arow] then
      //если высота колонки меньше
      (Sender as TStringGrid).RowHeights[Arow] := r;
  end;
end;

Как перемещать строки и колонки в StringGrid.

В процессе социологического опроса, проведенного в корпорации "Мелкий-Мягкий", выяснено, что: 99% сотрудников корпорации любят все большое и негибкое.

Пользователь может перемещать строки и колонки StringGrid при помощи мышки. Можно ли это сделать программно? В описании TCustomGrid можно увидеть методы MoveColumn и MoveRow, однако они скрыты в TStringGrid. Но нам ничего не мешает просабклассить TStringGrid и объявить эти методы как public:

type
  TStringGridX = class(TStringGrid)
  public
    procedure MoveColumn(FromIndex, ToIndex: Longint);
    procedure MoveRow(FromIndex, ToIndex: Longint);
  end;

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

procedure TStringGridX.MoveColumn(FromIndex, ToIndex: Integer);
begin
  inherited;
end;
procedure TStringGridX.MoveRow(FromIndex, ToIndex: Integer);
begin
  inherited;
end;

Этот компонент не нужно регистрировать в палитре компонентов. Просто используйте потомка TStringGrid или любого TCustomGrid, и вызывайте его методы:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TStringGridX(StringGrid1).MoveColumn(1, 3);
end;

Как перемещать строки и колонки в StringGrid 2.

Автор: Vit

type
  TFake = class(TStringGrid);
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  TFake(StringGrid1).MoveColumn(1, 3);
end;

Многострочный TStringGrid 3.

Автор: http://www.sources.ru

Сперва необходимо установить свойство DefaultDrawing в False. Далее, необходимо вставить следующий код в обработчик события OnDrawCell:

procedure TForm1.StringGrid1DrawCell(Sender: TObject;
  Col, Row: Longint;
  Rect: TRect;
  State: TGridDrawState);
var
  Line1: string;
  Line2: string;
  ptr: integer;
  padding: integer;
  hGrid: TStringGrid;
begin
  hGrid := (Sender as TStringGrid);
  ptr := Pos(';', hGrid.Cells[Col, Row]);
  if ptr > 0 then
  begin
    Line1 := Copy(hGrid.Cells[Col, Row], 1, ptr - 1);
    Line2 := Copy(hGrid.Cells[Col, Row], ptr + 1,
      Length(hGrid1.Cells[Col, Row]) - ptr);
  end
  else
    Line1 := hGrid.Cells[Col, Row];
  hGrid.Canvas.FillRect(Rect);
  hGrid.Canvas.TextOut(Rect.Left, Rect.Top + 2, Line1);
  if ptr > 0 then
    hGrid.Canvas.TextOut(Rect.Left, Rect.Top -
      hGrid.Canvas.Font.Height + 3, Line2);
end;

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

StringGrid1.RowHeights[0] := StringGrid1.DefaultRowHeight * 2;

Распечатать TStringGrid 2.

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

uses
   printers;
 //StringGrid Inhalt ausdrucken 
procedure PrintStringGrid(Grid: TStringGrid; Title: string;
   Orientation: TPrinterOrientation);
 var
   P, I, J, YPos, XPos, HorzSize, VertSize: Integer;
   AnzSeiten, Seite, Zeilen, HeaderSize, FooterSize, ZeilenSize, FontHeight: Integer;
   mmx, mmy: Extended;
   Footer: string;
 begin
   //Kopfzeile, Fu?zeile, Zeilenabstand, Schriftgro?e festlegen 
  HeaderSize := 100;
   FooterSize := 200;
   ZeilenSize := 36;
   FontHeight := 36;
   //Printer initializieren 
  Printer.Orientation := Orientation;
   Printer.Title  := Title;
   Printer.BeginDoc;
   //Druck auf mm einstellen 
  mmx := GetDeviceCaps(Printer.Canvas.Handle, PHYSICALWIDTH) /
     GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSX) * 25.4;
   mmy := GetDeviceCaps(Printer.Canvas.Handle, PHYSICALHEIGHT) /
     GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY) * 25.4;
   VertSize := Trunc(mmy) * 10;
   HorzSize := Trunc(mmx) * 10;
   SetMapMode(Printer.Canvas.Handle, MM_LOMETRIC);
   //Zeilenanzahl festlegen 
  Zeilen := (VertSize - HeaderSize - FooterSize) div ZeilenSize;
   //Seitenanzahl ermitteln 
  if Grid.RowCount mod Zeilen <> 0 then
     AnzSeiten := Grid.RowCount div Zeilen + 1
   else
     AnzSeiten := Grid.RowCount div Zeilen;
   Seite := 1;
   //Grid Drucken 
  for P := 1 to AnzSeiten do
   begin
     //Kopfzeile 
    Printer.Canvas.Font.Height := 48;
     Printer.Canvas.TextOut((HorzSize div 2 - (Printer.Canvas.TextWidth(Title) div 2)),
       - 20,Title);
     Printer.Canvas.Pen.Width := 5;
     Printer.Canvas.MoveTo(0, - HeaderSize);
     Printer.Canvas.LineTo(HorzSize, - HeaderSize);
     //Fu?zeile 
    Printer.Canvas.MoveTo(0, - VertSize + FooterSize);
     Printer.Canvas.LineTo(HorzSize, - VertSize + FooterSize);
     Printer.Canvas.Font.Height := 36;
     Footer := 'Seite: ' + IntToStr(Seite) + ' von ' + IntToStr(AnzSeiten);
     Printer.Canvas.TextOut((HorzSize div 2 - (Printer.Canvas.TextWidth(Footer) div 2)),
       - VertSize + 150,Footer);
     //Zeilen drucken 
    Printer.Canvas.Font.Height := FontHeight;
     YPos := HeaderSize + 10;
     for I := 1 to Zeilen do
     begin
       if Grid.RowCount >= I + (Seite - 1) * Zeilen then
       begin
         XPos := 0;
         for J := 0 to Grid.ColCount - 1 do
         begin
           Printer.Canvas.TextOut(XPos, - YPos,
             Grid.Cells[J, I + (Seite - 1) * Zeilen - 1]);
           XPos := XPos + Grid.ColWidths[J] * 3;
         end;
         YPos := YPos + ZeilenSize;
       end;
     end;
     //Seite hinzufugen 
    Inc(Seite);
     if Seite <= AnzSeiten then Printer.NewPage;
   end;
   Printer.EndDoc;
 end;
 //Example 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   //Drucken im Querformat 
  PrintStringGrid(Grid, 'StringGrid Print Landscape', poLandscape);
   //Drucken im Hochformat 
  PrintStringGrid(Grid, 'StringGrid Print Portrait', poPortrait);
 end;

Изменение размеров колонок в StringGrid.

Оформил: DeeCo

Ниже приведён примен кода, который позволяет автоматически подогнать размер колонки в компененте TStringGrid, под размер самой длинной строки текста в колонке:

procedure AutoSizeGridColumn(Grid: TStringGrid;
  column: integer);
var
  i: integer;
  temp: integer;
  max: integer;
begin
  max := 0;
  for i := 0 to (Grid.RowCount - 1) do
  begin
    temp := Grid.Canvas.TextWidth(grid.cells[column, i]);
    if temp > max then
      max := temp;
  end;
  Grid.ColWidths[column] := Max + Grid.GridLineWidth + 3;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  AutoSizeGridColumn(StringGrid1, 1);
end;

Обмен строк TStringGrid.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Обмен строк StringGrid
Обмен содержимого указанных строк StringGrid.
Варианты без копирования связанных с ячейками объектов и вместе с ними.
Зависимости: Grids
Автор:       Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright:   MBo
Дата:        27 апреля 2002 г.
***************************************************** }
procedure SGExchangeRows(SG: TStringGrid; Row1, Row2: Integer);
var
  TempString: string;
begin
  if (Row1 in [0..SG.RowCount - 1]) and (Row2 in [0..SG.RowCount - 1]) then
  begin
    TempString := SG.Rows[Row1].Text;
    SG.Rows[Row1].Assign(SG.Rows[Row2]);
    SG.Rows[Row2].Text := TempString;
  end;
end;
procedure SGExchRowsWithObj(SG: TStringGrid; Row1, Row2: Integer);
var
  TempList: TStringList;
begin
  with SG do
    if (Row1 in [0..RowCount - 1]) and (Row2 in [0..RowCount - 1]) then
    begin
      TempList := TStringList.Create;
      TempList.Assign(Rows[Row1]);
      Rows[Row1].Assign(Rows[Row2]);
      Rows[Row2].Assign(TempList);
      TempList.Free;
    end;
end;

Удаление строки из TStringGrid.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Удаление строки из StringGrid
Удаляет из StringGrid указанную строку, сдвигая остальные.
Зависимости: Grids
Автор:       Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright:   MBo
Дата:        27 апреля 2002 г.
***************************************************** }
procedure SGDeleteRow(SG: TStringGrid; RowToDelete: Integer);
var
  i: Integer;
begin
  with SG do
  begin
    if (RowToDelete >= 0) and (RowToDelete < RowCount) then
    begin
      for i := RowToDelete to RowCount - 2 do
        Rows[i].Assign(Rows[i + 1]);
      RowCount := RowCount - 1;
    end;
  end;
end;

Вставка строки в TStringGrid.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вставка строки в StringGrid
Вставляет новую строку в указанную позицию StringGrid
Зависимости: Grids
Автор:       Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright:   MBo
Дата:        27 апреля 2002 г.
***************************************************** }
procedure SGInsertRow(SG: TStringGrid; NewRow: Integer);
var
  i: Integer;
begin
  if NewRow < 0 then
    NewRow := 0; // либо 1, задайте нужное вам поведение
  with SG do
  begin
    RowCount := RowCount + 1;
    if NewRow < RowCount - 1 then
    begin
      for i := RowCount - 1 downto NewRow + 1 do
        Rows[i].Assign(Rows[i - 1]);
    end;
    Rows[NewRow].Clear;
  end;
end;

Перестановка строки в TStringGrid в другую позицию.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Перестановка строки в StringGrid в другую позицию
Передвигает строку StringGrid из позиции FromRow в позицию ToRow, сдвигая остальные
Зависимости: Grids
Автор:       Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright:   MBo
Дата:        28 апреля 2002 г.
***************************************************** }
procedure SGMoveRow(SG: TStringGrid; FromRow, ToRow: Integer);
var
  TempList: TStringList;
  i: Integer;
begin
  with SG do
    if (FromRow in [0..RowCount - 1]) and (ToRow in [0..RowCount - 1]) then
    begin
      TempList := TStringList.Create;
      TempList.Assign(Rows[FromRow]);
      if FromRow > ToRow then
        for i := FromRow downto ToRow + 1 do
          Rows[i].Assign(Rows[i - 1])
      else
        for i := FromRow to ToRow - 1 do
          Rows[i].Assign(Rows[i + 1]);
      Rows[ToRow].Assign(TempList);
      TempList.Free;
    end;
end;

Сохранить TStringGrid в файл, загрузить из файла.

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

// Save a TStringGrid to a file 
procedure SaveStringGrid(StringGrid: TStringGrid; const FileName: TFileName);
 var
   f:    TextFile;
   i, k: Integer;
 begin
   AssignFile(f, FileName);
   Rewrite(f);
   with StringGrid do
   begin
     // Write number of Columns/Rows 
    Writeln(f, ColCount);
     Writeln(f, RowCount);
     // loop through cells 
    for i := 0 to ColCount - 1 do
       for k := 0 to RowCount - 1 do
         Writeln(F, Cells[i, k]);
   end;
   CloseFile(F);
 end;
 // Load a TStringGrid from a file 
procedure LoadStringGrid(StringGrid: TStringGrid; const FileName: TFileName);
 var
   f:          TextFile;
   iTmp, i, k: Integer;
   strTemp:    String;
 begin
   AssignFile(f, FileName);
   Reset(f);
   with StringGrid do
   begin
     // Get number of columns 
    Readln(f, iTmp);
     ColCount := iTmp;
     // Get number of rows 
    Readln(f, iTmp);
     RowCount := iTmp;
     // loop through cells & fill in values 
    for i := 0 to ColCount - 1 do
       for k := 0 to RowCount - 1 do
       begin
         Readln(f, strTemp);
         Cells[i, k] := strTemp;
       end;
   end;
   CloseFile(f);
 end;
 // Save StringGrid1 to 'c:\temp.txt': 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   SaveStringGrid(StringGrid1, 'c:\temp.txt');
 end;
 // Load StringGrid1 from 'c:\temp.txt': 
procedure TForm1.Button2Click(Sender: TObject);
 begin
   LoadStringGrid(StringGrid1, 'c:\temp.txt');
 end;

Позиционирование каретки в TStringGrid.

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

{ 
  The following code allows you to position the caret 
  in a cell (InplaceEditor) of a StringGrid. 
  We need a Cracker class to access the InplaceEditor. 
  Mit folgendem Code kann man den Cursor in einer Zelle 
  (InplaceEditor) eines StringGrids positionieren. 
  Hierfur brauchen wir eine "Cracker" Klasse, weil der 
  InplaceEditor "protected" ist. 
}
 type
   TGridCracker = class(TStringGrid);
    {...}
 implementation
 {...}
 procedure SetCaretPosition(Grid: TStringGrid; col, row, x_pos: Integer);
 begin
   Grid.Col := Col;
   Grid.Row := Row;
   with TGridCracker(Grid) do
     InplaceEditor.SelStart := x_pos;
 end;
 // Get the Caret position from the focussed cell 
// Ermittelt die Caret-Position der aktuellen Zelle 
function GetCaretPosition(Grid: TStringGrid): Integer;
 begin
   with TGridCracker(Grid) do
     Result := InplaceEditor.SelStart;
 end;
 // Example / Beispiel: 
// Set the focus on col 1, row 3 and position the caret at position 5 
// Fokusiert die Zelle(1,3) und setzt den Cursor auf Position 5 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   StringGrid1.SetFocus;
   SetCaretPosition(StringGrid1, 1, 3, 5);
 end;

Сортировка столбцов в StringGrid.

Автор: http://www.sources.ru

procedure GridSort(StrGrid: TStringGrid; NoColumn: Integer);
var
  Line, PosActual: Integer;
  Row: TStrings;
begin
  Renglon := TStringList.Create;
  for Line := 1 to StrGrid.RowCount - 1 do
  begin
    PosActual := Line;
    Row.Assign(TStringlist(StrGrid.Rows[PosActual]));
    while True do
    begin
      if (PosActual = 0) or (StrToInt(Row.Strings[NoColumn - 1]) >=
        StrToInt(StrGrid.Cells[NoColumn - 1, PosActual - 1])) then
        Break;
      StrGrid.Rows[PosActual] := StrGrid.Rows[PosActual - 1];
      Dec(PosActual);
    end;
    if StrToInt(Row.Strings[NoColumn - 1]) < StrToInt(StrGrid.Cells[NoColumn -
      1, PosActual]) then
      StrGrid.Rows[PosActual] := Row;
  end;
  Renglon.Free;
end;

Сортировка столбцов в StringGrid 2.

Автор: http://www.sources.ru

type
  TStringGridExSortType = (srtAlpha, srtInteger, srtDouble);
procedure GridSort(SG: TStringGrid; ByColNumber, FromRow, ToRow: integer;
  SortType: TStringGridExSortType = srtAlpha);
var
  Temp: TStringList;
  function SortStr(Line: string): string;
  var
    RetVar: string;
  begin
    case SortType of
      srtAlpha: Retvar := Line;
      srtInteger: Retvar := FormatFloat('000000000', StrToIntDef(trim(Line),
        0));
      srtDouble:
        try
          Retvar := FormatFloat('000000000.000000', StrToFloat(trim(Line)));
        except
          RetVar := '0.00';
        end;
    end;
    Result := RetVar;
  end;
  // Рекурсивный QuickSort
  procedure QuickSort(Lo, Hi: integer; CC: TStrings);
    procedure Sort(l, r: integer);
    var
      i, j: integer;
      x: string;
    begin
      i := l;
      j := r;
      x := SortStr(CC[(l + r) div 2]);
      repeat
        while SortStr(CC[i]) < x do
          inc(i);
        while x < SortStr(CC[j]) do
          dec(j);
        if i <= j then
        begin
          Temp.Assign(SG.Rows[j]); // Меняем местами 2 строки
          SG.Rows[j].Assign(SG.Rows[i]);
          SG.Rows[i].Assign(Temp);
          inc(i);
          dec(j);
        end;
      until i > j;
      if l < j then
        sort(l, j);
      if i < r then
        sort(i, r);
    end;
  begin {quicksort}
    ;
    Sort(Lo, Hi);
  end;
begin
  Temp := TStringList.Create;
  QuickSort(FromRow, ToRow, SG.Cols[ByColNumber]);
  Temp.Free;
end;

Сортировка TStringGrid.

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

procedure SortStringGrid(var GenStrGrid: TStringGrid; ThatCol: Integer);
 const
   // Define the Separator 
  TheSeparator = '@';
 var
   CountItem, I, J, K, ThePosition: integer;
   MyList: TStringList;
   MyString, TempString: string;
 begin
   // Give the number of rows in the StringGrid 
  CountItem := GenStrGrid.RowCount;
   //Create the List 
  MyList        := TStringList.Create;
   MyList.Sorted := False;
   try
     begin
       for I := 1 to (CountItem - 1) do
         MyList.Add(GenStrGrid.Rows[I].Strings[ThatCol] + TheSeparator +
           GenStrGrid.Rows[I].Text);
       //Sort the List 
      Mylist.Sort;
       for K := 1 to Mylist.Count do
       begin
         //Take the String of the line (K – 1) 
        MyString := MyList.Strings[(K - 1)];
         //Find the position of the Separator in the String 
        ThePosition := Pos(TheSeparator, MyString);
         TempString  := '';
         {Eliminate the Text of the column on which we have sorted the StringGrid}
         TempString := Copy(MyString, (ThePosition + 1), Length(MyString));
         MyList.Strings[(K - 1)] := '';
         MyList.Strings[(K - 1)] := TempString;
       end;
       // Refill the StringGrid 
      for J := 1 to (CountItem - 1) do
         GenStrGrid.Rows[J].Text := MyList.Strings[(J - 1)];
     end;
   finally
     //Free the List 
    MyList.Free;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   // Sort the StringGrid1 on the second Column 
  // StringGrid1 nach der 1. Spalte sortieren 
  SortStringGrid(StringGrid1, 1);
 end;

Копирование содержимого TStringGrid в буфер обмена.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Копирование содержимого StringGrid в буфер обмена
Копирует содержимое ячеек StringGrid в ClipBoard в формате, позволяющем
вставку, например, в Word или Excel. При CopySel=True копирует выделение,
иначе всю таблицу или указанный диапазон (CL- левый столбец и т.д.).
Зависимости: Grids
Автор:       Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright:   MBo
Дата:        30 апреля 2002 г.
***************************************************** }
procedure SGCopyToCLP(SG: TStringGrid; CopySel: Boolean; CL: integer = -1;
  RT: integer = -1; CR: integer = -1; RB: integer = -1);
var
  i, j: Integer;
  s: string;
begin
  s := '';
  with SG do
  begin
    if CopySel then
    begin
      CL := Selection.Left;
      CR := Selection.Right;
      RT := Selection.Top;
      RB := Selection.Bottom;
    end;
    //при необходимости FixedRows и FixedCols можно заменить на 0
    if (CL < FixedCols) or (CL > CR) or (CL >= ColCount) then
      CL := FixedCols;
    if (CR < FixedCols) or (CL > CR) or (CR >= ColCount) then
      CR := ColCount - 1;
    if (RT < FixedRows) or (RT > RB) or (RT >= RowCount) then
      RT := FixedRows;
    if (RB < FixedCols) or (RT > RB) or (RB >= RowCount) then
      RB := RowCount - 1;
    for i := RT to RB do
    begin
      for j := CL to CR do
      begin
        s := s + Cells[j, i];
        if j < CR then
          s := s + #9;
      end;
      s := s + #13#10;
  end;
  end;
  ClipBoard.AsText := s;
end;
// Пример использования:
SGCopyToCLP(StringGrid1, True); //выделение
SGCopyToCLP(StringGrid1, False); //все ячейки
SGCopyToCLP(StringGrid1, False, 1, 1, 3, 2); //диапазон, 6 ячеек

Экспортировать TStringGrid в Excel-файл.

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

{ With OLE Automation }
 uses
   ComObj;
 function RefToCell(ARow, ACol: Integer): string;
 begin
   Result := Chr(Ord('A') + ACol - 1) + IntToStr(ARow);
 end;
 function SaveAsExcelFile(AGrid: TStringGrid; ASheetName, AFileName: string): Boolean;
 const
   xlWBATWorksheet = -4167;
 var
   Row, Col: Integer;
   GridPrevFile: string;
   XLApp, Sheet, Data: OLEVariant;
   i, j: Integer;
 begin
   // Prepare Data 
  Data := VarArrayCreate([1, AGrid.RowCount, 1, AGrid.ColCount], varVariant);
   for i := 0 to AGrid.ColCount - 1 do
     for j := 0 to AGrid.RowCount - 1 do
       Data[j + 1, i + 1] := AGrid.Cells[i, j];
   // Create Excel-OLE Object 
  Result := False;
   XLApp := CreateOleObject('Excel.Application');
   try
     // Hide Excel 
    XLApp.Visible := False;
     // Add new Workbook 
    XLApp.Workbooks.Add(xlWBatWorkSheet);
     Sheet := XLApp.Workbooks[1].WorkSheets[1];
     Sheet.Name := ASheetName;
     // Fill up the sheet 
    Sheet.Range[RefToCell(1, 1), RefToCell(AGrid.RowCount,
       AGrid.ColCount)].Value := Data;
     // Save Excel Worksheet 
    try
       XLApp.Workbooks[1].SaveAs(AFileName);
       Result := True;
     except
       // Error ? 
    end;
   finally
     // Quit Excel 
    if not VarIsEmpty(XLApp) then
     begin
       XLApp.DisplayAlerts := False;
       XLApp.Quit;
       XLAPP := Unassigned;
       Sheet := Unassigned;
     end;
   end;
 end;
 // Example: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   if SaveAsExcelFile(stringGrid1, 'My Stringgrid Data', 'c:\MyExcelFile.xls') then
     ShowMessage('StringGrid saved!');
 end;

Экспортировать TStringGrid в Excel-файл 2.

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

 { Without OLE }
 procedure XlsWriteCellLabel(XlsStream: TStream; const ACol, ARow: Word;
   const AValue: string);
 var
   L: Word;
 const
   {$J+}
   CXlsLabel: array[0..5] of Word = ($204, 0, 0, 0, 0, 0);
   {$J-}
 begin
   L := Length(AValue);
   CXlsLabel[1] := 8 + L;
   CXlsLabel[2] := ARow;
   CXlsLabel[3] := ACol;
   CXlsLabel[5] := L;
   XlsStream.WriteBuffer(CXlsLabel, SizeOf(CXlsLabel));
   XlsStream.WriteBuffer(Pointer(AValue)^, L);
 end;
 function SaveAsExcelFile(AGrid: TStringGrid; AFileName: string): Boolean;
 const
   {$J+} CXlsBof: array[0..5] of Word = ($809, 8, 00, $10, 0, 0); {$J-}
   CXlsEof: array[0..1] of Word = ($0A, 00);
 var
   FStream: TFileStream;
   I, J: Integer;
 begin
   Result := False;
   FStream := TFileStream.Create(PChar(AFileName), fmCreate or fmOpenWrite);
   try
     CXlsBof[4] := 0;
     FStream.WriteBuffer(CXlsBof, SizeOf(CXlsBof));
     for i := 0 to AGrid.ColCount - 1 do
       for j := 0 to AGrid.RowCount - 1 do
         XlsWriteCellLabel(FStream, I, J, AGrid.cells[i, j]);
     FStream.WriteBuffer(CXlsEof, SizeOf(CXlsEof));
     Result := True;
   finally
     FStream.Free;
   end;
 end;
 // Example: 
procedure TForm1.Button2Click(Sender: TObject);
 begin
   if SaveAsExcelFile(StringGrid1, 'c:\MyExcelFile.xls') then
     ShowMessage('StringGrid saved!');
 end;

Экспортировать TStringGrid в Excel-файл 3.

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

 { Code by Reinhard Schatzl }
 uses
   ComObj;
 // Hilfsfunktion fur StringGridToExcelSheet 
// Helper function for StringGridToExcelSheet 
function RefToCell(RowID, ColID: Integer): string;
 var
   ACount, APos: Integer;
 begin
   ACount := ColID div 26;
   APos := ColID mod 26;
   if APos = 0 then
   begin
     ACount := ACount - 1;
     APos := 26;
   end;
   if ACount = 0 then
     Result := Chr(Ord('A') + ColID - 1) + IntToStr(RowID);
   if ACount = 1 then
     Result := 'A' + Chr(Ord('A') + APos - 1) + IntToStr(RowID);
   if ACount > 1 then
     Result := Chr(Ord('A') + ACount - 1) + Chr(Ord('A') + APos - 1) + IntToStr(RowID);
 end;
 // StringGrid Inhalt in Excel exportieren 
// Export StringGrid contents to Excel 
function StringGridToExcelSheet(Grid: TStringGrid; SheetName, FileName: string;
   ShowExcel: Boolean): Boolean;
 const
   xlWBATWorksheet = -4167;
 var
   SheetCount, SheetColCount, SheetRowCount, BookCount: Integer;
   XLApp, Sheet, Data: OLEVariant;
   I, J, N, M: Integer;
   SaveFileName: string;
 begin
   //notwendige Sheetanzahl feststellen 
  SheetCount := (Grid.ColCount div 256) + 1;
   if Grid.ColCount mod 256 = 0 then
     SheetCount := SheetCount - 1;
   //notwendige Bookanzahl feststellen 
  BookCount := (Grid.RowCount div 65536) + 1;
   if Grid.RowCount mod 65536 = 0 then
     BookCount := BookCount - 1;
   //Create Excel-OLE Object 
  Result := False;
   XLApp  := CreateOleObject('Excel.Application');
   try
     //Excelsheet anzeigen 
    if ShowExcel = False then
       XLApp.Visible := False
     else
       XLApp.Visible := True;
     //Workbook hinzufugen 
    for M := 1 to BookCount do
     begin
       XLApp.Workbooks.Add(xlWBATWorksheet);
       //Sheets anlegen 
      for N := 1 to SheetCount - 1 do
       begin
         XLApp.Worksheets.Add;
       end;
     end;
     //Sheet ColAnzahl feststellen 
    if Grid.ColCount <= 256 then
       SheetColCount := Grid.ColCount
     else
       SheetColCount := 256;
     //Sheet RowAnzahl feststellen 
    if Grid.RowCount <= 65536 then
       SheetRowCount := Grid.RowCount
     else
       SheetRowCount := 65536;
     //Sheets befullen 
    for M := 1 to BookCount do
     begin
       for N := 1 to SheetCount do
       begin
         //Daten aus Grid holen 
        Data := VarArrayCreate([1, Grid.RowCount, 1, SheetColCount], varVariant);
         for I := 0 to SheetColCount - 1 do
           for J := 0 to SheetRowCount - 1 do
             if ((I + 256 * (N - 1)) <= Grid.ColCount) and
               ((J + 65536 * (M - 1)) <= Grid.RowCount) then
               Data[J + 1, I + 1] := Grid.Cells[I + 256 * (N - 1), J + 65536 * (M - 1)];
         //------------------------- 
        XLApp.Worksheets[N].Select;
         XLApp.Workbooks[M].Worksheets[N].Name := SheetName + IntToStr(N);
         //Zellen als String Formatieren 
        XLApp.Workbooks[M].Worksheets[N].Range[RefToCell(1, 1),
           RefToCell(SheetRowCount, SheetColCount)].Select;
         XLApp.Selection.NumberFormat := '@';
         XLApp.Workbooks[M].Worksheets[N].Range['A1'].Select;
         //Daten dem Excelsheet ubergeben 
        Sheet := XLApp.Workbooks[M].WorkSheets[N];
         Sheet.Range[RefToCell(1, 1), RefToCell(SheetRowCount, SheetColCount)].Value :=
           Data;
       end;
     end;
     //Save Excel Worksheet 
    try
       for M := 1 to BookCount do
       begin
         SaveFileName := Copy(FileName, 1,Pos('.', FileName) - 1) + IntToStr(M) +
           Copy(FileName, Pos('.', FileName),
           Length(FileName) - Pos('.', FileName) + 1);
         XLApp.Workbooks[M].SaveAs(SaveFileName);
       end;
       Result := True;
     except
       // Error ? 
    end;
   finally
     //Excel Beenden 
    if (not VarIsEmpty(XLApp)) and (ShowExcel = False) then
     begin
       XLApp.DisplayAlerts := False;
       XLApp.Quit;
       XLAPP := Unassigned;
       Sheet := Unassigned;
     end;
   end;
 end;
 //Example 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   //StringGrid inhalt in Excel exportieren 
  //Grid : stringGrid, SheetName : stringgrid Print, Pfad : c:\Test\ExcelFile.xls, Excelsheet anzeigen 
  StringGridToExcelSheet(StringGrid, 'Stringgrid Print', 'c:\Test\ExcelFile.xls', True);
 end;

Перевести TStringGrid в HTML.

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

{ The SGridToHtml() function converts a Stringgrid to a HTML Code. 
  Parameters: SG: TStringGrid to convert 
              Dest: TMemo to show the HTML Code 
              BorderSize: Size of border (0 = show no border) 
{ 
  Mit der Funktion SGridToHtml() konnen StringGrids in HTML Code umgewandelt werden. 
  SG bezieht sich auf das TStringGrid, welches codiert werden soll. 
  in Dest (vom Typ TMemo) wird der HTML Code erzeugt. 
  BorderSize bestimmt, wie dick der Tabellenrahmen sein soll. 0 = kein Rahmen 
}
 procedure SGridToHtml(SG: TStringgrid; Dest: TMemo; BorderSize: Integer);
 var
    i, p: integer;
   SStyle1, SStyle2, Text: string;
 begin
   Dest.Clear;
   Dest.Lines.Add('<html>');
   Dest.Lines.Add('<body>');
   Dest.Lines.Add('  <table border="' + IntToStr(BorderSize) + '" width="' +
     IntToStr(SG.Width) + '" height="' + IntToStr(SG.Width) + '">');
   for i := 0 to SG.RowCount - 1 do
   begin
     Dest.Lines.Add('  <tr>');
     for p := 0 to SG.ColCount - 1 do
     begin
       SStyle1 := '';
       SStyle2 := '';
       if fsbold in SG.Font.Style then
       begin
         SStyle1 := SStyle1 + '<b>';
         SStyle2 := SStyle2 + '</b>';
       end;
       if fsitalic in SG.Font.Style then
       begin
         SStyle1 := SStyle1 + '<i>';
         SStyle2 := SStyle2 + '</i>';
       end;
       if fsunderline in SG.Font.Style then
       begin
         SStyle1 := SStyle1 + '<u>';
         SStyle2 := SStyle2 + '</u>';
       end;
       Text := sg.Cells[p, i];
       if Text = '' then Text := ' ';
       Dest.Lines.Add('    <td width="' + IntToStr(sg.ColWidths[p]) +
         '" height="' + IntToStr(sg.RowHeights[p]) +
         '"><font color="#' + IntToHex(sg.Font.Color, 6) +
         '" face="' + SG.Font.Name + '">' + SStyle1 +
         Text + SStyle2 + '</font></td>');
     end;
     Dest.Lines.Add('  </tr>');
   end;
   Dest.Lines.Add('  </table>');
   Dest.Lines.Add('</body>');;
   Dest.Lines.Add('</html>');
 end;
 // Example, Beispiel 
procedure TFormCSVInport.Button6Click(Sender: TObject);
 begin
   SGridToHtml(StringGrid1, Memo1, 1);
   Memo1.Lines.SaveToFile('c:\test.html');
 end;

Как экспортировать StringGrid в MS Word таблицу.

Автор: http://www.swissdelphicenter.ch
uses
  ComObj;
procedure TForm1.Button1Click(Sender: TObject);
var
  WordApp, NewDoc, WordTable: OLEVariant;
  iRows, iCols, iGridRows, jGridCols: Integer;
begin
  try
    // Create a Word Instance
    // Word Instanz erzeugen
    WordApp := CreateOleObject('Word.Application');
  except
    // Error...
    // Fehler....
    Exit;
  end;
  // Show Word
  // Word anzeigen
  WordApp.Visible := True;
  // Add a new Doc
  // Neues Dok einfugen
  NewDoc := WordApp.Documents.Add;
  // Get number of columns, rows
  // Spalten, Reihen ermitteln
  iCols := StringGrid1.ColCount;
  iRows := StringGrid1.RowCount;
  // Add a Table
  // Tabelle einfugen
  WordTable := NewDoc.Tables.Add(WordApp.Selection.Range, iCols, iRows);
  // Fill up the word table with the Stringgrid contents
  // Tabelle ausfullen mit Stringgrid Daten
  for iGridRows := 1 to iRows do
    for jGridCols := 1 to iCols do
      WordTable.Cell(iGridRows, jGridCols).Range.Text :=
        StringGrid1.Cells[jGridCols - 1, iGridRows - 1];
  // Here you might want to Save the Doc, quit Word...
  // Hier evtl Word Doc speichern, beenden...
  // ...
  // Cleanup...
  WordApp := Unassigned;
  NewDoc := Unassigned;
  WordTable := Unassigned;
end;

Затенить компонент.

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

procedure ShadeIt(f: TForm; c: TControl; Width: Integer; Color: TColor); 
var 
  rect: TRect; 
  old: TColor; 
begin 
  if (c.Visible) then 
  begin 
    rect := c.BoundsRect; 
    rect.Left := rect.Left + Width; 
    rect.Top := rect.Top + Width; 
    rect.Right := rect.Right + Width; 
    rect.Bottom := rect.Bottom + Width; 
    old := f.Canvas.Brush.Color; 
    f.Canvas.Brush.Color := Color; 
    f.Canvas.fillrect(rect); 
    f.Canvas.Brush.Color := old; 
  end; 
end; 
procedure TForm1.FormPaint(Sender: TObject); 
var 
  i: Integer; 
begin 
  for i := 0 to Self.ControlCount - 1 do 
    ShadeIt(Self, Self.Controls[i], 3, clBlack); 
end;

Как получить эффект тени для Hint.

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

type
  TXPHintWindow = class(THintWindow)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure WMNCPaint(var msg: TMessage); message WM_NCPAINT;
  end;
function IsWinXP: Boolean;
begin
  Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and
    (Win32MajorVersion >= 5) and (Win32MinorVersion >= 1);
end;
procedure TXPHintWindow.CreateParams(var Params: TCreateParams);
const
  CS_DROPSHADOW = $00020000;
begin
  inherited;
  if IsWinXP then
    Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
procedure TXPHintWindow.WMNCPaint(var msg: TMessage);
var
  R: TRect;
  DC: HDC;
begin
  DC := GetWindowDC(Handle);
  try
    R := Rect(0, 0, Width, Height);
    DrawEdge(DC, R, EDGE_ETCHED, BF_RECT or BF_MONO);
  finally
    ReleaseDC(Handle, DC);
  end;
end;
initialization
  HintWindowClass := TXPHintWindow;
  Application.ShowHint := False;
  Application.ShowHint := True;
end.

Перетасовка экрана.

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

Системные и прикладные программисты едут на конференцию. Встречаются у касс вокзала, где и те и другие берут билеты. Прикладники покупают по билету на нос, системщики берут один билет. Удивленные прикладники спрашивают: - У вас че только один человек едет? - Да нет. Все. - А как же вы? - А это наши трудности. В поезде прикладники занимают места согласно купленных билетов за полчаса до отправления. За 45 сек. До отхода появляется стая системщиков. С криками вся толпа врывается в вагон закрывается в туалете. Поезд трогается. Контроллер подходит к туалету и стучит. Высовывается рука, протягивает билет. Через некоторое время системщики, как тараканы, расползаются по поезду. Едут обратно. Опять встречаются на вокзале. Прикладники, укравшие копирайт берут один билет. Системщики билета не берут. За 45 сек до отхода врывается толпа прикладников и запирается в туалете. Поезд трогается. Стук в дверь туалета. Выосвывается рука, протягивает билет. Системщик хватает билет и бежит в другой туалет.

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

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

Как это всё осуществить

Создайте новый проект Delphi с чистой формой. Установите свойство Name в 'Shuffler'. Добавьте на форму компоненты Image (Image1) и Timer (Timer1). Image будет содержать в себе изображение десктопа (разобранное), а Timer будет вызывать процедуру рисования. Свойство Interval компонента Timer определяет, как часто будет происходить перемешивание (значение 1000 эквивалентно одной секунде, 2000 - двум секундам).

Так же для проекта потребуется несколько глобальных переменных. Поместите следующий код перед секцией implementation в модуле формы:

var
  Shuffler: TShuffler; //это было добавлено самим Delphi
  DesktopBitmap : TBitmap;
  gx, gy : Integer;
  redRect : TBitmap;
  rW, rH : Integer;
const
  DELTA = 8; //должно быть 2^n

Значение константы (integer) DELTA определяет, на сколько частей будет разбит экран (строк и колонок). Число DELTA должно быть в виде 2^n, где n - целое (integer) число со знаком. Большое значение DELTA приводит к маленьким размерам блоков. Например, если DELTA равна 16 и разрешение экрана 1024 x 768, то экран будет поделён на 256 частей размером 64x48.

DesktopBitmap это битмап, который хранит в себе захваченное текущее изображение десктопа - мы будем получать это изображение делая скриншот. redRect это битмап картинка, которая заменяет перемещённую часть картинки. redRect создаётся в событии формы OnCreate. gx, gy содержат текущие координаты x и y (Left, Top) redRect внутри разобранного изображения. rW, rH это ширина и высота прямоугольного блока. Для 1024x768 и DELTA=16, rW будет равно 64 а rH = 48.

Проект начинает выполняться с обработчика события OnCreate:

procedure TShuffler.FormCreate(Sender: TObject);
begin
  rW := Screen.Width div DELTA;
  rH := Screen.Height div DELTA;
  redRect:=TBitmap.Create;
  with redRect do
  begin
    Width := rW;
    Height := rH;
    Canvas.Brush.Color := clRed;
    Canvas.Brush.Style := bssolid;
    Canvas.Rectangle(0,0,rW,rH);
    Canvas.Font.Color := clNavy;
    Canvas.Font.Style := Canvas.Font.Style + [fsBold];
    Canvas.TextOut(2,2,'About');
    Canvas.Font.Style := Canvas.Font.Style - [fsBold];
    Canvas.TextOut(2,17,'Delphi');
    Canvas.TextOut(2,32,'Programming');
  end;
  Timer1.Enabled := False;
  Image1.Align := alClient;
  Visible := False;
  BorderStyle := bsNone;
  Top := 0;
  Left := 0;
  Width := Screen.Width;
  Height := Screen.Height;
  InitScreen;
  // SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE + SWP_NOMOVE);
  Visible := True;
  Timer1.Interval := 10; // меньше := быстрее
  Timer1.Enabled := True; // Запускаем вызов DrawScreen
end;

Во-первых, значения rW и rH определяются значением DELTA. Как уже объяснялось, если разрешение экрана 800x600 и DELTA равна 8, изображение экрана будет разделено на 8x8 частей размером 100x75 (rW = 100, rH = 75).

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

Наконец, устанавливается ширина и высота формы как у экрана. Вызов (закомментированный) API функции SetWindowPos можно использовать, чтобы установить форму всегда на переднем плане (OnTop), не перемещаемую и не изменяемую. Вызывается процедура InitScreen. Устанавливает интервал таймера и начинает выполняться обработчик события OnTimer, запуская процедуру DrawScreen.

InitScreen - Скриншот

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

Чтобы получить скриншот десктопа, используется GetDC для GetDesktopWindow. API функция BitBt используется для передачи картинки десктопа в DesktopBitmap. GetDC(GetDesktopWindow) получает дескриптор контекста устройства дисплея для указанного окна - окна возвращённого функцией GetDesktopWindow. В заключении DesktopBitmap ассоциируется с компонентой Image1. Если что-то не ясно, то советую заглянуть справичные файлы по Delphi.

Начальная позиция redRect выбирается случайным образом. Trunc(Random * DELTA) возвращает целое число от 0 до DELTA. Далее, redRect рисуется в точке gx, gy, используя функцию CopyRect объекта Canvas. Опять же, если Вы не знакомы с алгоритмом рисования Delphi, то советую порыться в справке.

В конце, при помощи MoveTo и LineTo рисуется сетка. Сетка необязательна и используется только для того, чтобы лучше различать границы блоков.

procedure InitScreen;
var
  i, j: integer;
begin
  //получаем битмап десктопа
  DesktopBitmap := TBitmap.Create;
  with DesktopBitmap do
  begin
    Width := Screen.Width;
    Height := Screen.Height;
  end;
  BitBlt(DesktopBitmap.Canvas.Handle,
  0,0,Screen.Width,Screen.Height,
  GetDC(GetDesktopWindow),0,0,SrcCopy);
  Shuffler.Image1.Picture.Bitmap := DesktopBitmap;
  //изначальные координаты redRect
  Randomize;
  gx := Trunc(Random * DELTA);
  gy := Trunc(Random * DELTA);
  Shuffler.Image1.Canvas.CopyRect(
  Rect(rW * gx, rH * gy, rW * gx + rW, rH * gy + rH),
  redRect.Canvas,
  Rect(0,0,rW,rH));
  //рисуем сетку
  for i:=0 to DELTA-1 do
  begin
    Shuffler.Image1.Canvas.MoveTo(rW * i,0);
    Shuffler.Image1.Canvas.LineTo(rW * i,Screen.Height);
    Shuffler.Image1.Canvas.MoveTo(0, rH * i);
    Shuffler.Image1.Canvas.LineTo(Screen.Width, rH * i);
  end;
end;

Draw Screen

Основной код находится в процедуре DrawScreen. Эта процедура вызывается внутри события OnTimer компонента Timer.

procedure DrawScreen;
var
  r1, r2: TRect;
  Direction: integer;
begin
  r1:=Rect(rW * gx , rH * gy, rW * gx + rW , rH * gy + rH);
  Direction := Trunc(Random*4);
  case Direction of
    0: gx := Abs((gx + 1) mod DELTA); //право
    1: gx := Abs((gx - 1) mod DELTA); //лево
    2: gy := Abs((gy + 1) mod DELTA); //низ
    3: gy := Abs((gy - 1) mod DELTA); //верх
  end; //case
  r2 := Rect(rW * gx , rH * gy, rW * gx + rW , rH * gy + rH);
  with Shuffler.Image1.Canvas do
  begin
    CopyRect(r1, Shuffler.Image1.Canvas, r2);
    CopyRect(r2, redRect.Canvas, redRect.Canvas.ClipRect);
  end;
end;

Несмотря на кажущуюся сложность кода, он очень прост в использовании. Менять местами можно только части смежные с redRect, поэтому доступны только 4 возможных направления. Прямоугольник r1 содержит текущию позицию redRect, r2 указывает на прямоугольник с блоком, который был перемещён. CopyRect используется для перемещения выбранного блока на место redRect и рисования redRect его в новом месте - таким образом осуществляется обмен этих двух блоков.

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

Не забудьте по событию OnTimer для компонента Timer написать:

DrawScreen;

Перетасовка строк в списке.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Перетасовка строк в списке
Процедура тасует строки в списке.
List - Список строк
MoveCount - необязательный параметр, задающий количество перестановок,
если этот параметр опущен, либо меньше или равен нулю, его значение
принимается равным C*C-C, где C - количество строк в списке.
Зависимости: SysUtils, Classes
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        18 ноября 2002 г.
***************************************************** }
procedure ShuffleStrings(List: TStrings; MoveCount: Integer = 0);
var
  i, N, C: Integer;
  Index1, Index2: Integer;
  Str: string;
  Obj: TObject;
begin
  List.BeginUpdate;
  try
    C := List.Count;
    N := MoveCount;
    if N <= 0 then
      N := C * C - C;
    for i := 0 to N do
    begin
      Index1 := Random(C);
      Index2 := Random(C);
      Str := List.Strings[Index1];
      Obj := List.Objects[Index1];
      List.Strings[Index1] := List.Strings[Index2];
      List.Objects[Index1] := List.Objects[Index2];
      List.Strings[Index2] := Str;
      List.Objects[Index2] := Obj;
    end;
  finally
    List.EndUpdate;
  end;
end;

Разделяемые элементы управления для Tabbednotebook.

Вы могли бы получить тот же эффект, устанавливая элементы управления на панели или в другом контейнере, который не является дочерним по отношению к Notebook, но находится наверху. Поскольку элементы не являются дочерними по отношению к страницам, то их никак не затронет изменение страниц, и вы можете управлять ими из кода. Видимость же такова, что кажется, что каждая страница компонента содержит тот же самый набор компонентов. Что делаю я -- располагаю разделяемые элементы управления на форме, а не на конкретной странице TTabbedNotebook. Для того, чтобы сделать это, вам необходимо установить свойство TTabbedNotebook Align на что-то другое, отличное от alClient -- например, если разделяемые элементы управления расположены в нижней части формы, используйте для TTabbedNotebook значение alTop, и отодвиньте нижнюю границу чуть повыше, чтобы в образовавшейся области без труда разместились бы разделяемые элементы управления. Если вы хотите сделать так, чтобы "разделяемые" элементы управления имитировали свое нахождение на каждой странице TTabbedNotebook, оставьте их на форме, но поместите их на передний план, и передвиньте TTabbedNotebook снова в нижнюю часть формы.

У меня это работает. Я еще не испытывал нужду в разделяемых элементах управления для _набора_ страниц, но первая идея, которая приходит мне в голову, заключается в установке свойства Visible для каждого компонента в true/false в обработчике события TTabbedNotebook OnChange.

Разделяемые элементы управления для Tabbednotebook 2.

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

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

Разделяемые элементы управления для Tabbednotebook 3.

Компоненты, которые вы хотите показывать на всех страницах, должны принадлежать родителю TTabbedNotebook (в общем случае TForm, на которой расположен компонент), и, очевидно, они должны быть размещены ПОЗАДИ TTabbedNotebook. Самым простым способом сделать это, как я выяснил, является следующий способ: разместите на форме TTabbedNotebook, но оставьте возможность задать ему родителя (например, пока не устанавливайте ему свойство .Align), затем разместите кнопки (или что там у вас) в области родителя, затем установите свойство .Align у TTabbedNotebook, после чего элементы управления, которые вы разместили, окажутся позади TTabbedNotebook, и будут видны на всех его страницах (действительно, они будут располагаться "наверху" TTabbedNotebook. Если вы уже разместили компоненты, то лучшим решением будет использование для TTabbedNotebook команды меню "Edit/Send to Back", при котором нижние компоненты "всплывут" наверх. Также, возможно непосредственно отредактировать файл .DFM и убедиться в том, что родительский порядок компонентов собледен в полной мере.

Защита программ перекрытием кода.

Автор: Den is Com
Хакеры взломали 3-х минутную демо-версию Виагры. Теперь она trial-версия на 70 дней.
Не секрет, что совершенной защиты не существует, как некоторые авторы и фирмы не пыжатся , но они не правы. Хорошая защита должна обеспечить такой уровень, чтобы на вскрытие защиты нужно было затратить усилия сравнимые с самостоятельным написанием программы. Разумеется она должна быть многоуровневой и перекрывающейся (т.е уровни должны работать независимо). Не забывайте, что хорошие взломщики неплохо знают ассемблер и высокоуровневе ухищрения от них не спасают. Следовательно защищаться нужно тоже используя ассемблер.Не считайте, что это уже не модно или тяжело. Хороший программист не брезгует ассемблером и высшей математикой.

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

temp_string :='Den is Com';
asm
  mov  ax,$05EB
  @as: jmp @as-2
end;
ShowMessage('Сообщение');
На первый взгляд это может сконфузить вас, но все это очень просто. Первая инструкция заносит "левое" значение в AX. Вторая делает переход на значение операнда команды MOV AX. '05EB' переводится как 'jmp$+5' (помните, что слова хранятся задом наперед) Этот переход перепрыгивает первый JMP и продолжает дальше по коду.Вероятно, не будет достаточно для защиты, но продемонстрирует технику. Взгляните на это как пример.

Присваивание temp_string :='Den is Com' существенной роли не играет :), но может использоваться при отладке программы - хорошо присматривается при использовании дизассемблера. Скорее всего ваши первые шаги будут приводить к частому зависанию компьютера, но не отчаивайтесь - это того стоит. Попробуйте разработать свой способ сравнения строк (чаше всего ловятся именно эти инструкции), попробуйте замаскировать инструкции зависания компьютера и т.д.

Ошибка совместного доступа к базе данных.

Автор: Vit WEB-сайт: http://forum.vingrad.ru Подскажите как на дельфи создаются базы данных с которыми можно работать на нескольких компьютерах одновременно (таблицы Paradox). Как буфиризировать таблицы Paradox? Использую компонент DataBase со стандарным драйвером, указываю сетевой путь к БД, но при обращении сразу нескольких пользователей возникает сообщение что каталог заблокирован другим .NET файлом. Решение: Control Panel->BDE Administrator->закладка Configuration->Drivers->Native->Paradox

Сделайте где нибудь в сетке каталог доступный со всех компьютеров, Опция Net Dir должна указывать на всех компьютерах должна указывать на этот каталог - в нём будут хранится файлы которые обеспечивают раздельный доступ.

Как расшарить диск.

Автор: Repairman
Программист, глядя на только что отформатированный вирусом винчестер: "Хмм... кажется здесь кто-то поработал зубной щеткой Reach Interdental от Johnson&Johnson..."

Это модуль для Share любого диска или папки как на локальном, так и на удаленном компьютере (если, конечно у Вас права администратора и на компе разрешено удаленное администрирование, для локальной машины это не обязательно... ;-)).

Следует отметить, что под NT некоторые процедуры находятся в других DLL...

Функция SetShareOnDisk - ставит шару, RemoveShareFromDisk - снимает ее.

//(c)2002  repairman@uzel.ru
unit Share;
interface
type
  TPassw = string[8];
  TNetName = string[12];
  function SetShareOnDisk(HostName: string; { имя компьютера }
  LocalPath: string; { папка которую надо открыть для доступа }
  NetName: TNetName; { имя расшаренной папки в сети }
  Remark: string;    { комментарий, видимый в сети }
  Access: word;      { доступ }
  RO_Passw: TPassw;  { пароль на чтение }
  RW_Passw: TPassw   { пароль на полный доступ }
  ): boolean;
  function RemoveShareFromDisk(HostName: string; { имя компьютера }
  NetName: string;  { сетевое имя папки которую надо закрыть }
  LocalPath: string { локальный путь папки }
  ): boolean;
var
  ShareResult: word;
implementation
uses
  SysUtils, Windows, ShlObj;
{ указатель на имя компьютера,
например '\\Server'#0, если свой, то можно nil }
function NetShareAdd(ServerName: PChar;
Level: Word;       { уровень структуры Share_info, здесь 50 }
PShareInfo: PChar; { указатель на структуру Share_Info }
ParmErr: DWord)    { указатель на ??? }
{ svrapi для Win9X, NetApi32 для NT }
: dword; stdcall; external 'svrapi.dll';
function NetShareDel(ServerName: PChar; NetName: PChar;
Reserved: DWord): dword; stdcall; external 'svrapi.dll';
type
  _share_info_50 = record { структура Share уровня 50 }
  NetName: array[1..13] of char; { Как будет называться диск в сети }
  SType: byte;   { тип =0 (STYPE_DISKTREE) - шарить диски }
  Flags: word;   { флаги $0191,$0192,$0193....(доступ из сети) }
  Remark: PChar; { указатель на комментарий, видимый из сети }
  Path: PChar;   { указатель на имя ресурса, например 'c:\'#0 }
  { пароль для полного доступа, если не нужен =#0 }
  RW_Password: array [1..9] of char;
  { пароль для доступа на чтение, если не нужен =#0 }
  RO_Password: array [1..9] of char;
end;
function SetShareOnDisk(HostName, LocalPath: string; NetName: TNetName;
Remark: string; Access: word; RO_Passw, RW_Passw: TPassw): boolean;
var
  ShareInfo: _Share_Info_50;
begin
  Result:=false;
  StrPCopy(@ShareInfo.NetName, NetName);
  ShareInfo.SType:=0;
  ShareInfo.Flags:=Access;
  ShareInfo.Remark:=PChar(Remark);
  ShareInfo.Path:=PChar(LocalPath);
  StrPCopy(@ShareInfo.RO_Password, RO_Passw);
  StrPCopy(@ShareInfo.RW_Password, RW_Passw);
  { вызываем Share }
  ShareResult:=NetShareAdd(PChar(HostName), 50,@ShareInfo,$0000002a);
  if ShareResult<>0 then { расшарить неудалось }
    Exit;
    { сказать шеллу об изменениях }
  SHChangeNotify(SHCNE_NETSHARE, SHCNF_PATH, PChar(LocalPath), nil);
  Result:=true;
end;
function RemoveShareFromDisk(HostName, NetName, LocalPath: string): boolean;
begin
  Result:=false;
  { удалить шару }
  ShareResult:=NetShareDel(PChar(HostName), PChar(NetName), 0);
  if ShareResult<>0 then
    Exit;
    { сказать шеллу об изменениях }
  SHChangeNotify(SHCNE_NETUNSHARE, SHCNF_PATH,PChar(LocalPath),nil);
  Result:=true;
end;
end.

Расшаренная DLL.

Автор: Xavier Pacheco

{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
library ShareLib;
uses
  ShareMem,
  Windows,
  SysUtils,
  Classes;
const
  cMMFileName: PChar = 'SharedMapData';
{$I DLLDATA.INC}
var
  GlobalData: PGlobalDLLData;
  MapHandle: THandle;
  { GetDLLData will be the exported DLL function }
procedure GetDLLData(var AGlobalData: PGlobalDLLData); stdcall;
begin
  { Point AGlobalData to the same memory address referred to by GlobalData. }
  AGlobalData := GlobalData;
end;
procedure OpenSharedData;
var
  Size: Integer;
begin
  { Get the size of the data to be mapped. }
  Size := SizeOf(TGlobalDLLData);
  { Now get a memory-mapped file object. Note the first parameter passes
    the value $FFFFFFFF or DWord(-1) so that space is allocated from the system's
    paging file. This requires that a name for the memory-mapped
    object get passed as the last parameter. }
  MapHandle := CreateFileMapping(DWord(-1), nil, PAGE_READWRITE, 0, Size,
    cMMFileName);
  if MapHandle = 0 then
    RaiseLastWin32Error;
  { Now map the data to the calling process's address space and get a
    pointer to the beginning of this address }
  GlobalData := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
  { Initialize this data }
  GlobalData^.S := 'ShareLib';
  GlobalData^.I := 1;
  if GlobalData = nil then
  begin
    CloseHandle(MapHandle);
    RaiseLastWin32Error;
  end;
end;
procedure CloseSharedData;
{ This procedure un-maps the memory-mapped file and releases the memory-mapped
  file handle }
begin
  UnmapViewOfFile(GlobalData);
  CloseHandle(MapHandle);
end;
procedure DLLEntryPoint(dwReason: DWord);
begin
  case dwReason of
    DLL_PROCESS_ATTACH: OpenSharedData;
    DLL_PROCESS_DETACH: CloseSharedData;
  end;
end;
exports
  GetDLLData;
begin
  { First, assign the procedure to the DLLProc variable }
  DllProc := @DLLEntryPoint;
  { Now invoke the procedure to reflect that the DLL is attaching
    to the process }
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
Скачать весь проект

Общий доступ к памяти, распределенной DLL.

Общий доступ к области отображения файлов (Sharing Memory Mapped Files)... Проверьте нижеследующий код:

var
  HMapping: THandle;
  PMapData: Pointer;
const
  MAPFILESIZE = 1000;
procedure OpenMap;
var
  llInit: Boolean;
  lInt: Integer;
begin
  HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, MAPFILESIZE,
    pchar('ИМЯ ОБЛАСТИ ОТОБРАЖЕНИЯ'));
  // Проверка наличия
  llInit := (GetLastError() <> ERROR_ALREADY_EXISTS);
  if (hMapping = 0) then
  begin
    ShowMessage('Невозможно создать объект отображения файла');
    Application.Terminate;
    exit;
  end;
  PMapData := MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  if PMapData = nil then
  begin
    CloseHandle(HMapping);
    ShowMessage('Невозможно отобразить блок памяти');
    Application.Terminate;
    exit;
  end;
  if (llInit) then
  begin
    // Если объект отображения создан, инициализируем блок символами #0
    memset(PMapData, #0, MAPFILESIZE);
  end;
end;
procedure CloseMap;
begin
  if PMapData <> nil then
  begin
    UnMapViewOfFile(PMapData);
  end;
  if HMapping <> 0 then
  begin
    CloseHandle(HMapping);
  end;
end;
{ Таким образом любые два или более приложения или DLL могут получить указатели к
  одному и тому же блоку памяти.В данном примере PMapData указывает на 1000 -
  байтный буфер, инициализированный вначале символами #0.Однако существует одна
  потенциальная проблема - синхронизация доступа к памяти.Решить эту проблему можно
  с помощью мьютексов.Вот пример их использования:
  Вызовите LockMap перед записью (и чтением?) объекта отображения файла.
Не забывайте после каждого обновления немедленно вызывать UnlockMap. }
var
  HMapMutex: THandle;
const
  REQUEST_TIMEOUT = 1000;
function LockMap: Boolean;
begin
  Result := true;
  HMapMutex := CreateMutex(nil, false, pchar('ИМЯ ВАШЕГО МЬЮТЕКСА'));
  if HMixMutex = 0 then
  begin
    ShowMessage('Не могу создать мьютекс');
    Result := false;
  end
  else
  begin
    if WaitForSingleObject(HMapMutex, REQUEST_TIMEOUT) = WAIT_FAILED then
    begin
      // время ожидания
      ShowMessage('Невозможно заблокировать объект отображения файла');
      Result := false;
    end;
  end;
end;
procedure UnlockMap;
begin
  ReleaseMutex(HMixMutex);
  CloseHandle(HMixMutex);
end;

Как использовать Shell API SHBrowseForFolder.

Как использовать функцию Shell API SHBrowseForFolder чтобы позволить пользователю выбрать каталог?

uses ShellAPI, ShlObj;
procedure TForm1.Button1Click(Sender: TObject);
var
  TitleName: string;
  lpItemID: PItemIDList;
  BrowseInfo: TBrowseInfo;
  DisplayName: array[0..MAX_PATH] of char;
  TempPath: array[0..MAX_PATH] of char;
begin
  FillChar(BrowseInfo, sizeof(TBrowseInfo), #0);
  BrowseInfo.hwndOwner := Form1.Handle;
  BrowseInfo.pszDisplayName := @DisplayName;
  TitleName := 'Please specify a directory';
  BrowseInfo.lpszTitle := PChar(TitleName);
  BrowseInfo.ulFlags := BIF_RETURNONLYFSDIRS;
  lpItemID := SHBrowseForFolder(BrowseInfo);
  if lpItemId <> nil then
  begin
    SHGetPathFromIDList(lpItemID, TempPath);
    ShowMessage(TempPath);
    GlobalFreePtr(lpItemID);
  end;
end;

Как использовать Shell API SHBrowseForFolder 2.

Автор: Анатолий.

threadvar
  myDir: string;
function BrowseCallbackProc(hwnd: HWND; uMsg: UINT; lParam: LPARAM; lpData:
  LPARAM): integer; stdcall;
begin
  Result := 0;
  if uMsg = BFFM_INITIALIZED then
    SendMessage(hwnd, BFFM_SETSELECTION, 1, LongInt(PChar(myDir)))
end;
function SelectDirectory(const Caption: string; const Root: WideString;
  var Directory: string): Boolean;
var
  WindowList: Pointer;
  BrowseInf!
  o: TBrowseInfo;
  Buffer: PChar;
  RootItemIDList, ItemIDList: PItemIDList;
  ShellMalloc: IMalloc;
  IDesktopFolder: IShellFolder;
  Eaten, Flags: LongWord;
begin
  myDir := Directory;
  Result := False;
  FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
  if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
  begin
    Buffer := ShellMalloc.Alloc(MAX_PATH);
    try
      RootItemIDList := nil;
      if Root <> '' then
      begin
        SHGetDesktopFolder(IDesktopFolder);
        IDesktopFolder.ParseDisplayName(Application.Handle, nil,
          POleStr(Root), Eaten, RootItemIDList, Flags);
      end;
      with BrowseInfo do
      begin
        hwndOwner := Application.Handle;
        pidlRoot := RootItemIDList;
        pszDisplayName := Buffer;
        lpfn := @BrowseCallbackProc;
        lParam := Integer(PChar(Directory));
        lpszTitle := PChar(Caption);
        ulFlags := BIF_RETURNONLYFSDIRS or $0040 or BIF_EDITBOX or
        BIF_STATUSTEXT;
      end;
      WindowList := DisableTaskWindows(0);
      try
        ItemIDList := ShBrowseForFolder(BrowseInfo);
      finally
        EnableTaskWindows(WindowList);
      end;
      Result := ItemIDList <> nil;
      if Result then
      begin
        ShGetPathFromIDList(ItemIDList!
          , Buffer);
        ShellMalloc.Free(ItemIDList);
        Directory := Buffer;
      end;
    finally
      ShellMalloc.Free(Buffer);
    end;
  end;
end;

Запуск внешней программы. Как послать E-mail и сделать ссылку на сайт.

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

Для запуска внешней программы, для посылки письма или для создания ссылки на сайт вам понадобиться всего одна функция ShellExecute, которая описывается в модуле ShellAPI - не забудьте подключить его в uses.

Этой функции нужно указать несколько параметров:

Дескриптор родительского окна Выполняемое действие. Этот параметр может принимать следующие значения "open", "print", "explore" - соответственно открытие, печать или исследование. Можно указывать nil - тогда будет выполняться действие по умолчанию - "open". Имя файла или папки, или e-mail, или URL Параметры Каталог по умолчанию Способ вывода окна. В качестве значения можно указать: SW_HIDE Скрывает окно и активизирует другое. SW_MAXIMIZE Разворачивает окно. SW_MINIMIZE Сворачивает окно. SW_RESTORE Активизирует и выводит окно. Если окно было развёрнуто или свёрнуто - восстанавливает исходный размер и позицию. SW_SHOW Активизирует и выводит окно с его оригинальным размером и положением. SW_SHOWDEFAULT Активизирует с установками, заданными в структуре STARTUPINFO, которая была передана при создании процесса приложением запускающим нужную программу. SW_SHOWMAXIMIZED Выводит окно в развёрнутом виде. SW_SHOWMINIMIZED Выводит окно в виде пиктограммы на панели задач. SW_SHOWMINNOACTIVE Выводит окно в свёрнутом виде на панели задач и не передаёт ему фокус ввода, т.е. окно, которое до этого было активно остаётся активно по прежнему. SW_SHOWNA Отображает окно в его текущем состоянии. Активное окно остаётся активным по прежнему. SW_SHOWNOACTIVATE Выводит окно в его последнем положении и с последними используемыми размерами. Активное окно остаётся активным по прежнему. SW_SHOWNORMAL Выводит окно. Если оно было свёрнуто или развёрнуто - восстанавливает его оригинальные размеры и позицию

Примеры:

// Запуск файла
ShellExecute(Handle, nil, 'c:\windows\calc.exe', nil, nil, SW_SHOW);
// Просмотр каталога
ShellExecute(Handle, nil, 'c:\windows', nil, nil, SW_SHOW);
// Ссылка на сайт
ShellExecute(Handle, nil, 'http://www.site.ru', nil, nil, SW_SHOW);
// Послать E-mail
ShellExecute(Handle, nil, 'mailto:DelphiWorld@mail.ru', nil, nil, SW_SHOW);

Сортировка Шелла.

Сортировка Шелла. Это еще одна модификация пузырьковой сор- тировки. Суть ее состоит в том, что здесь выполняется сравнение ключей, отстоящих один от другого на некотором расстоянии d. Ис- ходный размер d обычно выбирается соизмеримым с половиной общего размера сортируемой последовательности. Выполняется пузырьковая сортировка с интервалом сравнения d. Затем величина d уменьшается вдвое и вновь выполняется пузырьковая сортировка, далее d умень- шается еще вдвое и т.д. Последняя пузырьковая сортировка выполня- ется при d=1. Качественный порядок сортировки Шелла остается O(N^2), среднее же число сравнений, определенное эмпирическим пу- тем - log2(N)^2*N. Ускорение достигается за счет того, что выяв- ленные "не на месте" элементы при d>1, быстрее "всплывают" на свои места.

Пример иллюстрирует сортировку Шелла.

{===== Программный пример =====}
 { Сортировка Шелла }
 Procedure Sort( var a : seq);
 Var d, i, t : integer;
    k : boolean; { признак перестановки }
   begin
   d:=N div 2;  { начальное значение интервала }
   while d>0 do begin { цикл с уменьшением интервала до 1 }
     { пузырьковая сортировка с интервалом d }
     k:=true;
     while k do begin  { цикл, пока есть перестановки }
       k:=false; i:=1;
       for i:=1 to N-d do begin
         { сравнение эл-тов на интервале d }
         if a[i]>a[i+d] then begin
           t:=a[i]; a[i]:=a[i+d]; a[i+d]:=t; { перестановка }
           k:=true;  { признак перестановки }
           end; { if ... }
         end; { for ... }
       end; { while k }
     d:=d div 2;  { уменьшение интервала }
     end;  { while d>0 }
 end;

Результаты трассировки программного примера 3.9 представлены в таблице.

----------T---T-------------------------------------------------¬
¦   шаг   ¦ d ¦    содержимое массива a                         ¦
+---------+---+-------------------------------------------------+
¦исходный ¦   ¦ 76 22  4 17 13 49  4 18 32 40 96 57 77 20  1 52 ¦
¦   1     ¦ 8 ¦ 32 22  4 17 13 20  1 18 76 40 96 57 77 49  4 52 ¦
¦   2     ¦ 8 ¦ 32 22  4 17 13 20  1 18 76 40 96 57 77 49  4 52 ¦
¦   3     ¦ 4 ¦ 13 20  1 17 32 22  4 18 76 40  4 52 77 49 96 57 ¦
¦   4     ¦ 4 ¦ 13 20  1 17 32 22  4 18 76 40  4 52 77 49 96 57 ¦
¦   5     ¦ 2 ¦  1 17 13 20  4 18 32 22  4 40 76 49 77 52 96 57 ¦
¦   6     ¦ 2 ¦  1 17  4 18 13 20  4 22 32 40 76 49 77 52 96 57 ¦
¦   7     ¦ 2 ¦  1 17  4 18  4 20 13 22 32 40 76 49 77 52 96 57 ¦
¦   8     ¦ 2 ¦  1 17  4 18  4 20 13 22 32 40 76 49 77 52 96 57 ¦
¦   9     ¦ 1 ¦  1  4 17  4 18 13 20 22 32 40 49 76 52 77 57 96 ¦
¦  10     ¦ 1 ¦  1  4  4 17 13 18 20 22 32 40 49 52 76 57 77 96 ¦
¦  11     ¦ 1 ¦  1  4  4 13 17 18 20 22 32 40 49 52 57 76 77 96 ¦
¦  12     ¦ 1 ¦  1  4  4 13 17 18 20 22 32 40 49 52 57 76 77 96 ¦
¦результат¦   ¦  1  4  4 13 17 18 20 22 32 40 49 52 57 76 77 96 ¦
L---------+---+--------------------------------------------------

Shell своими руками - System Tray.

Автор: Богдан Минич Иконку в трей помещают с помощью Shell_NotifyIconW. Интересено посмотреть на этот процесс с другой точки зрения. Цитата с сайта delphi.mastak.ru: Shell_NotifyIconW просто ищет окно с классом "Shell_TrayWnd" и посылает в него сообщение WM_COPYDATA. в качестве данных выступает простая структура TNIDMessage. возвращаясь к топику: если создать свое окно с классом "Shell_TrayWnd" и обрабатывать входящие сообщения WM_COPYDATA, то можно написать полный аналог system tray! ... (с) paul_shmakov ...чем и займемся. В первую очередь немаловажное замечание: сообщение посылается только одному окну, то есть наше приложение должно грузится первым. Разные там ехрlоrеr'ы и другие подобные будут мешать. Шаг первый:

Создаем окно "Shell_TrayWnd".

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WinClassName := 'Shell_TrayWnd';
end;
Все. Окно класса "Shell_TrayWnd" имеем. Шаг второй:

Ловим WM_COPYDATA.

procedure TFORM1.WMCOPYDATA(var Msg: Tmessage);
var
  pcd: PCopyDataStruct;
  NID: PNotifyIconData;
begin
  pcd := PCOPYDATASTRUCT(msg.lParam);
  if pcd^.dwData = 1 then
  begin
    NID := pointer(integer(pcd.lpData) + 8);
    case integer(pointer(integer(pcd.lpData) + 4)^) of
      NIM_ADD: Msg.Result := NewTrayIcon(NID); // добавить иконку
      NIM_DELETE: Msg.Result := DeleteTrayIcon(NID); // удалить иконку
      NIM_MODIFY: Msg.Result := ModifyTrayIcon(NID);
        // изменить иконку (или подсказку)
    end;
    exit;
  end;
end;

Обратите внимание на Msg.Result. Желательно чтобы NewTrayIcon, DeleteTrayIcon, ModifyTrayIcon возвращали Integer(True) или Integer(False) в зависимости от помещения/удаления иконки. Некоторые приложения не проверяют этот результат, но если начнут проверять - то причины "глючного" поведения иконки того же AVP Monitor можно искать долго и безуспешно.

Шаг третий:

Поймали, и че с ним теперь делать?

А мы имеем очень интересную структуру - NID.cbSize - размер записи, в принципе не интересен; NID.Wnd - хендл окна (владельца иконки); NID.uID - идентификатор иконки (если их в приложении несколько), для данной задачи нужен для отсылки обратного сообщения; NID.uFlags - определяет, какие поля используются в сообщении. Параметр может быть любой комбинацией из флагов (0 - uCallbackMessage, 2 - hIcon, 4 - czTip); NID.uCallbackMessage - номер сообщения, которое посылается окну, определяемому полем NID.Wnd (владельцу). lParam отсылаемого сообщения дожен равняться NID.uID, а wParam сообщение от мыши. Пример: PostMessage(NID.Wnd, NID.uCallBack, NID.uID, MOUSE_EVENT) где MOUSE_EVENT может принимать значения WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK и подобные для других кнопок мыши. NID.hIcon - хендл иконки, которую собственно и предполагается отображать; NID.szTip - строка, оканчивающаяся нулевым символом, содержит подсказку, которая должна выводится при наведении курсора на иконку.

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

procedure TForm1.FormCreate(Sender: TObject);
var
  WM_TASKBARCREATED: UINT;
begin
  WM_TASKBARCREATED := RegisterWindowMessage('TaskbarCreated');
  PostMessage(HWND_BROADCAST, WM_TASKBARCREATED, 0, 0);
end;
Не все приложения реагируют на такое сообщение. Скачать демо проект DemoTray.zip (9.8K) И несколько слов о демо проекте. ВНИМАНИЕ!!! Следуйте данным инструкциям только в том случае, если Вы ясно понимаете смысл действий!!! Повторюсь: Shell_NotifyIconW сообщение посылает только одному окну. Поэтому чтобы увидеть результаты работы демопроекта, загружать его надо без или вместо ехрlоrеr'а. Первый вариант (для Win9x): Пример: файл %windir%\system.ini изменить следующим образом:

Найти строчку:

shell=explorer.exe

Заменить на (предполагается что демопроект находится в C:\Demotray\ ) :

;shell=explorer.exe
 shell=c:\demotray\demotray.exe
Перегрузите Windows Для возврата explorer'a раскомментируйте первую строчку, закомментируйте или удалите вторую.

Второй вариант: Лично я использую Far для выгрузки Explorer.exe.

1) Загружаем IDE Delphi и демопроект 2) Загружаем Far, F11->Process list-> выбираем EXPLORER.EXE->F8->OK 3) Отлаживаем проект 4) Для появления Explorer'a просто запустите его.

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

Инструкции по закрытию EXPLORER.EXE действительны для Win9x.

Если у Вас NT - разберитесь сами. Если не сможете разобратся - то за такие проекты Вам браться рановато.

Гревные ругательства "А у меня после ... ничего не работает!" не принимаются. Благодарности: Paul Shmakov - реверсинг Shell_NotifyIcon, моральная поддержка. Стив Тейксейра & Ксавье Пачеко - литература. Особая благодарность обоим использованым алфавитам.

Клавиши-акселераторы для TPageControl.

Приходит программист на работу с красными глазами, злой, не в настроении. У него же и спрашивают: - Слышь, Вась, что ты такой невеселый? - Да я тут всю ночь программу писал. - И что не работает? - Работает. - Может с глюками какими? - Нет, без. - Так что ты злой такой? - Да я, бл@, на клавише Backspace заснул.
TPageControl, расположенный на закладке Win95 палитры компонентов, в настоящий момент не может работать с акселераторами. Тем не менее, в наших силах создать потомок TPageControl, поддерживающий вышеназванную характеристику.

В приведенном ниже коде показана реализация такого компонента. Наследник TPageControl осуществляет захват и обработку сообщения CM_DIALOGCHAR. Это позволяет перехватывать комбинации клавиш, которые могут быть акселератороми для данной формы. Обработчик со бытия CMDialogChar использует функцию IsAccel, которая позволяет определить, имеет ли отношение перехваченный код клавиш к акселератору одной из страниц TPageControl. В этом случае делаем страницу активной и передаем ей фокус.

unit tapage;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics,
  Controls, Forms, Dialogs, ComCtrls;
type
  TAPageControl = class(TPageControl)
  private
    procedure CMDialogChar(var Msg: TCMDialogChar); message CM_DIALOGCHAR;
  end;
procedure Register;
implementation
procedure TAPageControl.CMDialogChar(var Msg: TCMDialogChar);
var
  i: Integer;
  S: string;
begin
  if Enabled then
    for I := 0 to PageCount - 1 do
      if IsAccel(Msg.CharCode, Pages[i].Caption) and
        Pages[I].TabVisible then
      begin
        Msg.Result := 1;
        ActivePage := Pages[I];
        Change;
        Exit; // выход из цикла.
      end;
  inherited;
end;
procedure Register;
begin
  RegisterComponents('Test', [TAPageControl]);
end;
end.

Как сделать клавишу-акселератор (keyboard shortcut) компонету, у которого нет заголовка.

Статьи по Delphi (015)…
...Ну! И кто открывал бутылку о кнопку "F8"?!

Возможный вариант - присвоить ссылку на этот компонент свойству FocusControl ТLаbеl'а. В примере используется невидимый Label для создания "быстрой" клавиши (Alt+M) компонента Memo. Чтобы использовать пример, разместите на форме компонент TMemo, Label и несколько других компонентов, которые могут принимать фокус ввода. Запустите программу, переведите фокус ввода куда-нибудь вне Memo и нажмите Alt+M - фокус ввода вернется в Memo.

procedure TForm1.FormCreate(Sender: TObject);
begin 
  Label1.Visible := false;
  Label1.Caption := '&M';
  Label1.FocusControl := Memo1;
end;

Чтобы клавиши shortcut пунктов главного меню главной формы действовали только в этой форме (но не в модальных окнах).

Знакомая проблема. Лечится так:

function WindowHook(var Message: TMessage): Boolean;
...
procedure.FormCreate(Sender: TObject);
begin
  // MainForm
  Application.HookMainWindow(WindowHook);
end;
function.WindowHook;
begin
  Result := False;
  with Message do
    case Msg of
      CM_APPKEYDOWN, CM_APPSYSCOMMAND: Msg := WM_NULL;
end;

Сокращенное имя каталога.

Есть имя каталога: c:\windows\media\temp\abc\sound\chime.wav Мне необходима сокращенная запись, такая как c:\windows\..\sound\chime.wav а не полный путь к файлу. Есть ли простой способ для этого?

Я разработал процедуру, которая делает нечто похожее. Она сокращает имя каталога, когда он и текущий путь имеют в корне тот же диск и/или часть пути, совпадающие вначале. Полученный результат (сокращенная запись) действительно легче для восприятия. Я производил кодирование в шестнадцатиричном редакторе в Borland Pascal и некоторое время не использовал. Я не думаю, что данный код может быть несовместим с последними версиями Delphi.

function shortenfilename(s: string): string;
var
  drive, curdrive: string[2];
  dir, curdir: string[80];
  name: string[20];
  ext: string[5];
  i: byte;
begin
  for i := 1 to length(s) do
    s[i] := upcase(s[i]);
  s := fexpand(s);
  fsplit(s, dir, name, ext);
  drive := copy(dir, 1, 2);
  dir := copy(dir, 4, length(dir) - 3);
  getdir(0, curdir);
  curdrive := copy(curdir, 1, 2);
  curdir := copy(curdir, 4, length(curdir) - 3) + '\';
  if drive = curdrive then
  begin
    if copy(dir, 1, length(curdir)) = curdir then
    begin
      i := length(curdir);
      if length(dir) <> i then
        dir := dir + '\';
      shortenfilename := copy(dir, i + 1, length(dir) - i - 1) + name + ext;
    end
    else
      shortenfilename := copy(s, 3, length(s) - 2);
  end
  else
    shortenfilename := s;
end;

Как использовать функцию ShowMessageFmt.

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessageFmt('This is %s.'#13'Handle: %.8x '#13'WindowProc: %p',
    [Caption, Handle, @WindowProc]);
end;

%s — вместо нее подставляется первый параметр из [], приведенный к типу String %.8x — подставляется целое, переведенное в 16-ричную систему, дополненное слева нулями до 8-ми цифр %p — подставляется указатель.

Отобразить, присоединить сетевые диски.

Вовочка прибегает домой 1-го сентября после уроков, подходит к папе-фидошнику и говорит: - Пап, мы сегодня писали сочинение на тему "Как я провёл лето", а мне влепили тройбан. Сам помотри - за что? Батя отрывается от монитора, долго всматривается в сочинение и отвечает: - А ты что хотел?! Оффтопик, оверквотинг...
function ConnectDrive(_drvLetter: string; _netPath: string; _showError: Boolean; 
  _reconnect: Boolean): DWORD; 
var 
  nRes: TNetResource; 
  errCode: DWORD; 
  dwFlags: DWORD; 
begin 
  { Fill NetRessource with #0 to provide uninitialized values } 
  { NetRessource mit #0 fullen => Keine unitialisierte Werte } 
  FillChar(NRes, SizeOf(NRes), #0); 
  nRes.dwType := RESOURCETYPE_DISK; 
  { Set Driveletter and Networkpath } 
  { Laufwerkbuchstabe und Netzwerkpfad setzen } 
  nRes.lpLocalName  := PChar(_drvLetter); 
  nRes.lpRemoteName := PChar(_netPath); { Example: \\Test\C } 
  { Check if it should be saved for use after restart and set flags } 
  { Uberprufung, ob gespeichert werden soll } 
  if _reconnect then 
    dwFlags := CONNECT_UPDATE_PROFILE and CONNECT_INTERACTIVE 
  else 
    dwFlags := CONNECT_INTERACTIVE; 
  errCode := WNetAddConnection3(Form1.Handle, nRes, nil, nil, dwFlags); 
  { Show Errormessage, if flag is set } 
  { Fehlernachricht aneigen } 
  if (errCode <> NO_ERROR) and (_showError) then 
  begin 
    Application.MessageBox(PChar('An error occured while connecting:' + #13#10 + 
      SysErrorMessage(GetLastError)), 
      'Error while connecting!', 
      MB_OK); 
  end; 
  Result := errCode; { NO_ERROR } 
end; 
function ConnectPrinterDevice(_lptPort: string; _netPath: string; _showError: Boolean; 
  _reconnect: Boolean): DWORD; 
var 
  nRes: TNetResource; 
  errCode: DWORD; 
  dwFlags: DWORD; 
begin 
  { Fill NetRessource with #0 to provide uninitialized values } 
  { NetRessource mit #0 fullen => Keine unitialisierte Werte } 
  FillChar(NRes, SizeOf(NRes), #0); 
  nRes.dwType := RESOURCETYPE_PRINT; 
  { Set Printername and Networkpath } 
  { Druckername und Netzwerkpfad setzen } 
  nRes.lpLocalName  := PChar(_lptPort); 
  nRes.lpRemoteName := PChar(_netPath); { Example: \\Test\Printer1 } 
  { Check if it should be saved for use after restart and set flags } 
  { Uberprufung, ob gespeichert werden soll } 
  if _reconnect then 
    dwFlags := CONNECT_UPDATE_PROFILE and CONNECT_INTERACTIVE 
  else 
    dwFlags := CONNECT_INTERACTIVE; 
  errCode := WNetAddConnection3(Form1.Handle, nRes, nil, nil, dwFlags); 
  { Show Errormessage, if flag is set } 
  { Fehlernachricht aneigen } 
  if (errCode <> NO_ERROR) and (_showError) then 
  begin 
    Application.MessageBox(PChar('An error occured while connecting:' + #13#10 + 
      SysErrorMessage(GetLastError)), 
      'Error while connecting!', 
      MB_OK); 
  end; 
  Result := errCode; { NO_ERROR } 
end; 
function DisconnectNetDrive(_locDrive: string; _showError: Boolean; _force: Boolean; 
  _save: Boolean): DWORD; 
var 
  dwFlags: DWORD; 
  errCode: DWORD; 
begin 
  { Set dwFlags, if necessary } 
  { Setze dwFlags auf gewunschten Wert } 
  if _save then 
    dwFlags := CONNECT_UPDATE_PROFILE 
  else 
    dwFlags := 0; 
  { Cancel the connection see also at http://www.swissdelphicenter.ch/en/showcode.php?id=391 } 
  { Siehe auch oben genannten Link (Netzlaufwerke anzeigen) } 
  errCode := WNetCancelConnection2(PChar(_locDrive), dwFlags, _force); 
  { Show Errormessage, if flag is set } 
  { Fehlernachricht anzeigen } 
  if (errCode <> NO_ERROR) and (_showError) then  
  begin 
    Application.MessageBox(PChar('An error occured while disconnecting:' + #13#10 + 
      SysErrorMessage(GetLastError)), 
      'Error while disconnecting', 
      MB_OK); 
  end; 
  Result := errCode; { NO_ERROR } 
end; 
{Beispiel / Example:} 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ConnectDrive('h:', '\\Servername\C', True, True); 
end; 
procedure TForm1.Button2Click(Sender: TObject); 
begin 
  DisconnectNetDrive('h:', True, True, True); 
end;

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

Автор: Serge Buzadzhy

procedure ShowAlmostModal(FormModal:TForm);
begin
  NavigatorForm.Enabled := false;
  FormModal.ShowModal
end;

И вот это привесь на OnShow почти модальной формы.

procedure FormShow(Sender:Tobject);
begin
  NavigatorForm.Enabled := true;
end;

Считать двоичный файл и отобразить значение байт как ASCII.

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

type
   TDisplayProc = procedure(const s: string) of object;
 procedure ShowBinary(var Data; Count: Cardinal; DispProc: TDisplayProc);
 implementation
 procedure ShowBinary(var Data; Count: Cardinal; DispProc: TDisplayProc);
 var
   line: string[80];
   i: Cardinal;
   p: PChar;
   nStr: string[4];
 const
   posStart = 1;
   binStart = 7;
   ascStart = 57;
   HexChars: PChar = '0123456789ABCDEF';
 begin
   p    := @Data;
   line := '';
   for i := 0 to Count - 1 do
   begin
     if (i mod 16) = 0 then
     begin
       if Length(line) > 0 then
         DispProc(line);
       FillChar(line, SizeOf(line), ' ');
       line[0] := Chr(72);
       nStr    := Format('%4.4X', [i]);
       Move(nStr[1], line[posStart], Length(nStr));
       line[posStart + 4] := ':';
     end;
     if p[i] >= ' ' then
       line[i mod 16 + ascStart] := p[i]
     else
       line[i mod 16 + ascStart] := '.';
     line[binStart + 3 * (i mod 16)]     := HexChars[(Ord(p[i]) shr 4) and $F];
     line[binStart + 3 * (i mod 16) + 1] := HexChars[Ord(p[i]) and $F];
   end;
   DispProc(line);
 end;
 procedure TForm1.Display(const S: string);
 begin
   Memo1.Lines.Add(S);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   ms: TMemoryStream;
 begin
   if Opendialog1.Execute then
   begin
     ms := TMemoryStream.Create;
     try
       ms.LoadFromfile(OpenDialog1.FileName);
       ShowBinary(ms.Memory^, ms.Size, Display);
     finally
       ms.Free
     end;
   end;
 end;

Просмотр буфера обмена.

unit ClipboardViewer;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs;
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FNextViewerHandle: THandle;
    procedure WMDrawClipboard(var message: TMessage);
      message WM_DRAWCLIPBOARD;
    procedure WMChangeCBCHain(var message: TMessage);
      message WM_CHANGECBCHAIN;
  public
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
  // Проверяем работоспособность функции.
  // При невозможности просмотра буфера обмена
  // функция возвратит значение Nil.
  FNextViewerHandle := SetClipboardViewer(Handle);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  // Восстанавливаем цепочки.
  ChangeClipboardChain(Handle, FNextViewerHandle);
end;
procedure TForm1.WMDrawClipboard(var message: TMessage);
begin
  // Вызывается при любом изменении содержимого буфера обмена
  message.Result := SendMessage(WM_DRAWCLIPBOARD, FNextViewerHandle, 0, 0);
end;
procedure TForm1.WMChangeCBCHain(var message: TMessage);
begin
  // Вызывается при любом изменении цепочек буфера обмена.
  if message.wParam = FNextViewerHandle then
  begin
    // Удаляем следующую цепочку просмотра. Корректируем внутреннюю переменную.
    FNextViewerHandle := message.lParam;
    // Возвращаем 0 чтобы указать, что сообщение было обработано
    message.Result := 0;
  end
  else
  begin
    // Передаем сообщение следующему окну в цепочке.
    message.Result := SendMessage(FNextViewerHandle, WM_CHANGECBCHAIN,
      message.wParam, message.lParam);
  end;
end;
end.

Просмотр буфера обмена 2.

Автор: Neil

Просмотр буфера обмена в Delphi совсем не сложен. Вот участок кода программы, вешающий цепочки в буфере обмена и просто отображающий его текст. Расположите компонент Memo на главной форме нового проекта, присвойстве свойству Align значение alClient, добавьте необходимые private-поля и методы и создайте их реализацию следующим образом:

...
private
  { Private declarations }
  PrevHwnd: Hwnd;
  procedure WMChangeCBChain(var Msg: TWMChangeCBChain);
    message WM_CHANGECBCHAIN;
  procedure WMDrawClipboard(var Msg: TWMDrawClipboard);
    message WM_DRAWCLIPBOARD;
...
procedure TForm1.WMChangeCBChain(var Msg: TWMChangeCBChain);
begin
  if PrevHWnd = Msg.Remove then
    PrevHWnd := Msg.Next;
  if Msg.Remove <> Handle then
    SendMessage(PrevHWnd, WM_CHANGECBCHAIN, Msg.Remove, Msg.Next);
end;
procedure TForm1.WMDrawClipboard(var Msg: TWMDrawClipboard);
var
  P: PChar;
  H: THandle;
begin
  SendMessage(PrevHWnd, WM_DRAWCLIPBOARD, 0, 0);
  if Clipboard.HasFormat(CF_TEXT) then
  begin
    H := Clipboard.GetAsHandle(CF_TEXT);
    Len := GlobalSize(H) + 1;
    P := GlobalLock(H);
    Memo1.SetTextBuf(P);
    GlobalUnlock(H);
  end;
  Msg.Result := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  PrevHwnd := SetClipboardViewer(Handle);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  ChangeClipboardChain(Handle, PrevHwnd);
end;

Обращаю ваше внимание на то, что у меня не было никакой конкретной идеи прежде, чем я это сделал; я просто внимательно прочел файлы помощи по SetClipboardViewer и во всех связанных темах.

Открытие сокращённого или полного диалога выбора цвета.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Открытие сокращённого или полного диалога выбора цвета
Вид диалога зависит от того, можно ли показать начальный цвет
(C : TColor) в сокращённом диалоге или нужно раскрывать его полностью.
Возвращает выбранный пользователем цвет.
Зависимости: Windows, Messages, SysUtils, Classes, DIALOGS;
Автор:       Igor Kovalevsky, pc-ambulance@mail.ru, Владикавказ
Copyright:   Igor Kovalevsky
Дата:        1 июня 2002 г.
***************************************************** }
function SelectColor(C: TColor): TColor;
const
  BasicColors = [$00, $40, $80, $A0, $C0, $FF];
begin
  with TColorDialog.Create(Application) do
  begin
    Color := C;
    if (GetRValue(Color) in BasicColors) and
      (GetGValue(Color) in BasicColors) and
      (GetBValue(Color) in BasicColors) then
    begin
      Options := Options - [cdFullOpen];
    end
    else
    begin
      Options := Options + [cdFullOpen];
    end;
    if Execute then
    begin
      Result := Color
    end
    else
    begin
      Result := clNone;
    end;
    Free;
  end;
end;

Отображение ломанной линии.

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

Автор: Jim Gunkel (Nevrona Designs) Как мне вывести ломанную линию на холсте, если я не знаю размер массива (количество точек) до момента запуска программы??? По-моему, это невозможно. Просветите меня!

Недавно я решал аналогичную проблему при кодировании ReportPrinter и не нашел хорошего решения для создания открытого массива параметров заданного размера. Решение, которое я, наконец, использовал, заключалось в хитрой комбинации функции polyline и polygon с ассемблерным кодом. Я публикую исходный код, поскольку думаю что он будет полезен, пока Borland не создаст стандартные и простые методы для работы с массивами заданного размера.

type
  PPointArr = ^TPointArr;
  TPointArr = array[0..16380] of TPoint;
var
  I1: integer;
  Elements: word;
  PointArr: PPointArr;
begin
  GetMem(PointArr, (Elements + 1) * SizeOf(TPoint));
  try
    for I1 := 0 to Elements do
    begin
      PointArr^[I1].X := ReadNextXValue;
      PointArr^[I1].Y := ReadNextYValue;
    end; { for }
    { Вызов Polygon(PointArr^), но только с Elements+1-элеменами
      в открытом массиве }
    asm
      les di,PointArr { Помещаем указатель на PointArr }
      push es
      push di
      push Elements { Помещаем High(PointArr^) }
      les di,self { Помещаем указатель self }
      push es
      push di
      les di,es:[di] { Вызов self.Polygon }
      call Polygon
    end; { asm }
  finally
    FreeMem(PointArr, (Elements + 1) * SizeOf(TPoint));
  end; { try }
end;

Вывод пояснения о текущей операции.

Автор: Роман Василенко WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вывод пояснения о текущей операции.
При вызове этой функции на экране появляется табличка с указанной
в параметре msg надписью. В параметрах TxColor, BgColor и BvColor
определяются цвета текста, заливки и рамки, соответственно.
Возвращает функция ссылку на объект типа TForm, который необходимо
освободить, когда табличка уже не нужна.
Вероятная проблема: Если у Вас слишком много текста в табличке,
она может не уместиться на экране и вылезет краями за его предел.
Зависимости: Forms, StdCtrls, ExtCtrls, Controls, Graphics, Classes;
Автор:       Роман Василенко, romix@nm.ru, Пятигорск
Copyright:   Роман Василенко
Дата:        23 сентября 2002 г.
***************************************************** }
//ИМХО, нечего комментировать здесь.
function ShowWaitMsg(msg: string; TxColor, BgColor, BvColor: tcolor): tform;
var
  frm: tform;
  lb: tlabel;
  sh: tshape;
begin
  frm := tform.CreateNew(Application);
  with frm do
  begin
    BorderIcons := [];
    borderstyle := bsNone;
    position := poScreenCenter;
    sh := tshape.create(frm);
    sh.parent := frm;
    sh.Align := alClient;
    sh.brush.color := BgColor;
    sh.pen.color := BvColor;
    sh.pen.style := pssolid;
    sh.Pen.Width := 2;
    lb := tlabel.Create(frm);
    lb.parent := frm;
    lb.Left := 10;
    lb.top := 5;
    lb.Caption := msg;
    lb.Font.Size := 14;
    lb.Font.Style := [fsbold];
    lb.Alignment := taCenter;
    lb.AutoSize := true;
    lb.Transparent := true;
    lb.WordWrap := true;
    clientwidth := lb.Canvas.TextWidth(msg) + 20;
    clientheight := lb.Canvas.TextHeight(msg) + 10;
    lb.Font.color := TxColor;
    show;
  end;
  application.processmessages;
  result := frm;
end;

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

// Например, заводим переменную:
var
  f: tform;
// Для появления таблички, делаем:
f := ShowWaitMsg('Привет! Это программа Васи Пупкина!',
  clWhite, ClNavy, clBlack);
// Для того, чтобы потом избавиться от нее:
f.free;

Показ удаленных записей в таблице dBASE.

В таблицах dBASE записи не удаляются до тех пор, пока таблица не будет упакована. Пока же это не произойдет, удаленные записи остаются в таблице, только имеют при этом флажок "к удалению". Для того, чтобы показать эти существующие, но не отображаемые записи, существует функция ShowDeleted(), которая использует функцию BDE API DbiSetProp(), показывающая записи, помеченные к удалению. При использовании этой функции нет необходимости закрывать и вновь открывать таблицу. ShowDeleted() в качестве параметров передается TTable и логическое значение. Логический параметр указывает на необходимость показа удаленных записей.

Демонстрационный проект:

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, DB, DBTables;
type
  TForm1 = class(TForm)
    Table1: TTable;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    CheckBox1: TCheckBox;
    procedure CheckBox1Click(Sender: TObject);
  public
    procedure ShowDeleted(Table: TTable; ShowDeleted: Boolean);
  end;
var
  Form1: TForm1;
implementation
uses DBITYPES, DBIERRS, DBIPROCS;
{$R *.DFM}
procedure TForm1.ShowDeleted(Table: TTable; ShowDeleted: Boolean);
var
  rslt: DBIResult;
  szErrMsg: DBIMSG;
begin
  Table.DisableControls;
  try
    Check(DbiSetProp(hDBIObj(Table.Handle), curSOFTDELETEON,
      LongInt(ShowDeleted)));
  finally
    Table.EnableControls;
  end;
  Table.Refresh;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  ShowDeleted(Table1, CheckBox1.Checked);
end;
end.

Показать удаленные записи таблицы.

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

procedure DeletedRecords(Table: TTable; SioNo: Boolean);
 begin
   Table.DisableControls;
   try
     Check(DbiSetProp(hDBIObj(Table.Handle), curSOFTDELETEON, Longint(SioNo)));
   finally
     Table.EnableControls;
   end;
   Table.Refresh;
 end;

Как заставить приложение показывать различные иконки при различных разрешениях дисплея.

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

Поместите следующий код в файл проекта (.DPR) Вашего приложения:

Application.Initialize;
Application.CreateForm(TForm1, Form1);
case GetDeviceCaps(GetDC(Form1.Handle), HORZRES) of
   640 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON640');
   800 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON800');
  1024 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON1024');
  1280 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON1280');
end;
Application.Run;

Как вывести сведения о диске (метка тома, серийный номер, файловая система и т.д.).

Статьи по Delphi (015)…
Звонок в службу технической поддержки: - У меня компьютер не работает! - После чего это произошло? - Я его включил - загрузился Нортон. Смотрю - у меня слева диск С: и справа диск С:. Я подумал - нафиг мне два диска С:? И стер правый к чертовой матери.
procedure TForm1.Button2Click(Sender: TObject);
var
  VolumeName,
  FileSystemName : array [0..MAX_PATH-1] of Char;
  VolumeSerialNo : DWord;
  MaxComponentLength,FileSystemFlags: Cardinal;
begin
  GetVolumeInformation('C:\',VolumeName,MAX_PATH,@VolumeSerialNo,
  MaxComponentLength,FileSystemFlags, FileSystemName,MAX_PATH);
  Memo1.Lines.Add('VolumeName = '+VolumeName);
  Memo1.Lines.Add('SerialNo = $'+IntToHex(VolumeSerialNo,8));
  Memo1.Lines.Add('CompLen = '+IntToStr(MaxComponentLength));
  Memo1.Lines.Add('Flags = $'+IntToHex(FileSystemFlags,4));
  Memo1.Lines.Add('FSName = '+FileSystemName);
end;

Как программно вывести окно свойств экрана.

Автор: Nomadic.

ShellExecute(Application.Handle, 'open', 'desk.cpl', nil, nil, sw_ShowNormal); 

Показать диалог выбора домена.

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

{ 
  If you are developing network software for Windows NT, 
  you usually need to ask the user to select a computer or domain 
  he wants to connect/login. 
}
 const
   FOCUSDLG_DOMAINS_ONLY = 1;
   FOCUSDLG_SERVERS_ONLY = 2;
   FOCUSDLG_SERVERS_DOMAINS = 3;
   FOCUSDLG_BROWSE_LOGON_DOMAIN = $00010000;
   FOCUSDLG_BROWSE_WKSTA_DOMAIN = $00020000;
   FOCUSDLG_BROWSE_OTHER_DOMAINS = $00040000;
   FOCUSDLG_BROWSE_TRUSTING_DOMAINS = $00080000;
   FOCUSDLG_BROWSE_WORKGROUP_DOMAINS = $00100000;
   FOCUSDLG_BROWSE_ALL_DOMAINS = FOCUSDLG_BROWSE_LOGON_DOMAIN or
     FOCUSDLG_BROWSE_WKSTA_DOMAIN or FOCUSDLG_BROWSE_OTHER_DOMAINS or
     FOCUSDLG_BROWSE_TRUSTING_DOMAINS or FOCUSDLG_BROWSE_WORKGROUP_DOMAINS;
 function SystemFocusDialog(hwndOwner: HWND; dwSelectionFlag: UINT;
   wszName: PWideChar; dwBufSize: DWORD; var bOKPressed: Boolean;
   wszHelpFile: PWideChar; dwContextHelpId: DWORD): DWORD; stdcall;
   external 'ntlanman.dll' Name 'I_SystemFocusDialog';
 function ComputerBrowser(hWndParent: HWND; wCompName: PWideChar;
   dwBufLen: DWORD): Boolean;
 var
   dwError: DWORD;
 begin
   Result := False;
   dwError := SystemFocusDialog(hWndParent, FOCUSDLG_SERVERS_DOMAINS or
     FOCUSDLG_BROWSE_ALL_DOMAINS,
     wCompName, dwBufLen, Result, nil, 0);
   if dwError <> NO_ERROR then Exit;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   wCompName: array [0..MAX_COMPUTERNAME_LENGTH + 1] of WideChar;
 begin
   if ComputerBrowser(0, wCompName, MAX_COMPUTERNAME_LENGTH + 1) then
     ShowMessage(wCompName)
   else
     ShowMessage('no computer selected');
 end;
 {***************************}
 // Show the ServerBrowseDialogA0 Dialog 
type
   TServerBrowseDialogA0 = function(hwnd: HWND; pchBuffer: Pointer;
     cchBufSize: DWORD): bool;
   stdcall;
 function ShowServerDialog(AHandle: THandle): string;
 var
   ServerBrowseDialogA0: TServerBrowseDialogA0;
   LANMAN_DLL: DWORD;
   buffer: array[0..1024] of char;
   bLoadLib: Boolean;
 begin
   LANMAN_DLL := GetModuleHandle('NTLANMAN.DLL');
   if LANMAN_DLL = 0 then
   begin
     LANMAN_DLL := LoadLibrary('NTLANMAN.DLL');
     bLoadLib := True;
   end;
   if LANMAN_DLL <> 0 then
   begin @ServerBrowseDialogA0 := GetProcAddress(LANMAN_DLL, 'ServerBrowseDialogA0');
     DialogBox(HInstance, MAKEINTRESOURCE(101), AHandle, nil);
     ServerBrowseDialogA0(AHandle, @buffer, 1024);
     if buffer[0] = '\' then
     begin
       Result := buffer;
     end;
     if bLoadLib then
       FreeLibrary(LANMAN_DLL);
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   label1.Caption := ShowServerDialog(Form1.Handle);
 end;

Отображение отформатированных сообщений об ошибках.

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

 function GetLastErrorText(): string;
 var
   dwSize: DWORD;
   lpszTemp: PAnsiChar;
 begin
   dwSize := 512;
   lpszTemp := nil;
   try
     GetMem(lpszTemp, dwSize);
     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY,
       nil,
       GetLastError(),
       LANG_NEUTRAL,
       lpszTemp,
       dwSize,
       nil)
   finally
     Result := lpszTemp;
     FreeMem(lpszTemp)
   end
 end;

Отображение отформатированных сообщений об ошибках 2.

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

 function SystemErrorMessage: string;
 var
   P: PChar;
 begin
   if FormatMessage(Format_Message_Allocate_Buffer + Format_Message_From_System,
                    nil,
                    GetLastError,
                    0,
                    @P,
                    0,
                    nil) <> 0 then
   begin
     Result := P;
     LocalFree(Integer(P))
   end
   else
     Result := '';
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShowMessage(SystemErrorMessage);
 end;

Показать файлы с ассоциированными с ними иконками в TListView.

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

{ 
  The following example shows how to show all files and their 
  associated icons of a folder in a TListView. 
  To test the code, you need a ListView1 and a ImageList1 where the icons are stored. 
  Im folgenden Beispiel werden alle Dateien & zugehorigen 
  Icons eines Verzeichnisses in einer TListView angezeigt. 
  Um das Beispiel zu testen, braucht man eine ListView1 und eine ImageList1 
  Komponente, wo die Icons gespeichert werden. 
}
 uses
   ShellApi;
 procedure LV_InsertFiles(strPath: string; ListView: TListView; ImageList: TImageList);
 var
   i: Integer;
   Icon: TIcon;
   SearchRec: TSearchRec;
   ListItem: TListItem;
   FileInfo: SHFILEINFO;
 begin
   // Create a temporary TIcon 
  Icon := TIcon.Create;
   ListView.Items.BeginUpdate;
   try
     // search for the first file 
    i := FindFirst(strPath + '*.*', faAnyFile, SearchRec);
     while i = 0 do
     begin
       with ListView do
       begin
         // On directories and volumes 
        if ((SearchRec.Attr and FaDirectory <> FaDirectory) and
           (SearchRec.Attr and FaVolumeId <> FaVolumeID)) then
         begin
           ListItem := ListView.Items.Add;
           //Get The DisplayName 
          SHGetFileInfo(PChar(strPath + SearchRec.Name), 0, FileInfo,
             SizeOf(FileInfo), SHGFI_DISPLAYNAME);
           Listitem.Caption := FileInfo.szDisplayName;
           // Get The TypeName 
          SHGetFileInfo(PChar(strPath + SearchRec.Name), 0, FileInfo,
             SizeOf(FileInfo), SHGFI_TYPENAME);
           ListItem.SubItems.Add(FileInfo.szTypeName);
           //Get The Icon That Represents The File 
          SHGetFileInfo(PChar(strPath + SearchRec.Name), 0, FileInfo,
             SizeOf(FileInfo), SHGFI_ICON or SHGFI_SMALLICON);
           icon.Handle := FileInfo.hIcon;
           ListItem.ImageIndex := ImageList.AddIcon(Icon);
           // Destroy the Icon 
          DestroyIcon(FileInfo.hIcon);
         end;
       end;
       i := FindNext(SearchRec);
     end;
   finally
     Icon.Free;
     ListView.Items.EndUpdate;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   // Assign a Imagelist to the ListView 
  ListView1.SmallImages := ImageList1;
   // Show Listview in Report Style and add 2 Columns 
  ListView1.ViewStyle := vsReport;
   ListView1.Columns.Add;
   ListView1.Columns.Add;
   LV_InsertFiles('C:\Windows\', ListView1, ImageList1);
 end;

Показать свойства файла.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, SoCtrls, SoSpecEdit, ShellAPI;
type
  TForm1 = class(TForm)
    Button1: TButton;
    sohoFilenameEdit1: TsohoFilenameEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure ShowPropertiesDialog(FName: string);
var
  SExInfo: TSHELLEXECUTEINFO;
  Error: LongInt;
begin
  ZeroMemory(Addr(SExInfo),SizeOf(SExInfo));
  SExInfo.cbSize := SizeOf(SExInfo);
  SExInfo.lpFile := PChar(FName);
  SExInfo.lpVerb := 'properties';
  SExInfo.fMask  := SEE_MASK_INVOKEIDLIST;
  ShellExecuteEx(Addr(SExInfo));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowPropertiesDialog(sohoFilenameEdit1.FileName);
end;
end.

Как показать иконку, ассоциированную с данным типом файла.

Статьи по Delphi (015)…
Приходит программист в магазин. Там продавщица - полная такая тетенька. Программист (меланхолично оглядевшись): - Бутылку пива и пачку презервативов. Продавщица: - Это ты девушку охмурить бутылкой пива собрался? Вино надо дорогое покупать.... Программист (отрешенно): - Да нет. Пиво я выпью, когда она уйдет.
uses
  ShellAPI;
...
procedure TForm1.Button1Click(Sender: TObject);
var
  Icon: hIcon;
  IconIndex: word;
begin
  IconIndex := 1;
  Icon := ExtractAssociatedIcon(HInstance,
    Application.ExeName, IconIndex);
  DrawIcon(Canvas.Handle, 10, 10, Icon);
end;

Показать первый кадр AVI-файла.

Следом за акцией "Кликни Деда Мороза!" компания Coca-Cola планирует с Нового года запустить акции "Бэкапни Деда Мороза" и "Форматни Снегурочку"
procedure TForm1.Button1Click(Sender: TObject);
begin
  Application.ProcessMessages;
  MediaPlayer1.Open;
  Application.ProcessMessages;
  MediaPlayer1.Step;
  Application.ProcessMessages;
  MediaPlayer1.Previous;
end;

Просмотреть текст формы из запущенной программы.

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

procedure TForm1.Button1Click(Sender: TObject);
 var
   rs: TResourceStream;
   ms: TMemoryStream;
 begin
   rs := TResourceStream.Create(HInstance, ClassName, RT_RCDATA);
   try
     ms := TMemoryStream.Create;
     try
       ObjectBinaryToText(rs, ms);
       ms.Seek(0, 0);
       memo1.Lines.LoadFromStream(ms);
     finally
       ms.Free;
     end;
   finally
     rs.Free;
   end;
 end;

Показ формы без фокуса.

ShowWindow(theWindowHandle, SW_SHOWNOACTIVE); 

Отображение полных строк списка при перемещении мыши по списку.

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

Автор: Serzs
Внимание! Вы изменили положение мыши. Для того, чтобы изменения вступили в силу, необходимо перезагрузить компьютер.

Текст формы примера:

object MainForm: TMainForm
Left = 7
Top = 121
Width = 200
Height = 157
Hint = '34534535'
Caption = 'Long hints'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.name = 'MS Sans Serif'
Font.Style = []
ShowHint = True
OnCreate = FormCreate
PixelsPerInch = 120
TextHeight = 16
object ListBox1: TListBox
Left = 12
Top = 12
Width = 165
Height = 97
Hint = '1|2'
ItemHeight = 16
Items.Strings = (
'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'A1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'1234567890'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'ABCD')
ParentShowHint = False
ShowHint = True
TabOrder = 0
OnMouseMove = ListBox1MouseMove
end
end

Текст модуля:

unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TMainForm = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Integer);
  private
    { Private declarations }
    FHintRow : Integer; // Номер строки в списке, на которую указывает мышь
  public
    { Public declarations }
    // Обработчик подсказок
    procedure OnShowHint(var HintStr: string; var CanShow: Boolean;
    var HintInfo: THintInfo);
end;
var
  MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
  FHintRow := -1;
  Application.OnShowHint := OnShowHint; // Установка обработчика
end;
procedure TMainForm.OnShowHint(var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
var
  Pos: TPoint;
begin
  with HintInfo do
    // Проверка на нужный объект
    if HintControl is TListBox then
      with HintControl as TListBox do
      begin
        Pos.X := 0;
        Pos.Y := ListBox1.Tag;
        HintPos := ListBox1.ClientToScreen(Pos);
        HintStr := ListBox1.Hint;
      end;
end;
procedure TMainForm.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
  MousePos : TPoint;
  ItemPos : TRect;
  RowWidth,
  ItemNum : Integer;
  FHint : string;
begin
  MousePos.X := X;
  MousePos.Y := Y;
  // Определение номера строки в списке
  ItemNum := ListBox1.ItemAtPos(MousePos, True);
  // Проверка на перемещение мыши на другую строку
  if (ItemNum <> FHintRow) then
  begin
    FHintRow := ItemNum;
    // Проверка на наличие элементов в списке
    if ItemNum <> -1 then
    begin
      ItemPos := ListBox1.ItemRect(ItemNum);
      Application.CancelHint; // Снять текущую подсказку
      ListBox1.Tag := ItemPos.Top; // Запоминаем позицию строки по вертикали
      FHint := ListBox1.Items[ItemNum];
      // Проверка на ширину строки
      RowWidth := ListBox1.Canvas.TextWidth( FHint );
      if (RowWidth > ListBox1.ClientWidth) then
        FHint := FHint + '|'
      else
        FHint := '';
      ListBox1.Hint := FHint;
    end
    else
    begin
      ListBox1.Hint := '';
      Application.CancelHint;
      ListBox1.Tag := -1;
    end;
  end
end;
end.

Текст проекта:

program PrjHint;
uses
  Forms,
  Main in 'Main.pas' {MainForm};
{$R *.RES}
begin
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  Application.ShowHint := True;
  Application.HintPause := 100;
  Application.HintHidePause := 999999;
  Application.Run;
end.

Как вывести главное окно справочной системы.

В тюремной камере: - Тебе сколько дали? - 15 лет. - А за что? - Окно разбил. - Да где ж ты работаешь? - В Майкрософте...

В 16-битных версиях справочной системы необходимо было вызывать начальное (главное) окно помощи с параметром HELP_CONTENTS в комманде HelpCommand. В 32-битном варианте это осуществляется следующим образом:

Application.HelpCommand(HELP_FINDER, 0);

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

Вывод справочной информации.

Чтобы вывести справочную информацию, которая находится в СНМ-файле, нужно воспользоваться ActiveX-компонентом (элементом управления) ньореп, который входит в состав Windows и представляет собой специальную динамическую библиотеку (файл Hhopen.ocx). Первое, что следует сделать, — установить компонент Hhopen на одну из вкладок палитры компонентов. Для этого надо из меню Component выбрать команду Import ActiveX Control. На экране появится окно Import ActiveX, в котором будут перечислены все зарегистрированные в реестре Windows компоненты. В окне Import ActiveX, в списке зарегистрированных компонентов, нужно выбрать строку hhopen OLE Control module и щелкнуть на кнопке Install. В результате этого на экране появится диалоговое окно Install, в котором программист может выбрать пакет (packege — пакет, библиотека компонентов), в который будет добавлен устанавливаемый компонент. Компоненты, добавляемые программистом, "по умолчанию" добавляются в пакет dciusr. В результате щелчка на кнопке ОК выбранный компонент добавляется в пакет, и на экране появляется окно Package и запрос подтверждения процесса перекомпиляции пакета . По завершении процесса компиляции на экране появится окно, информирующее о том, что компонент добавлен в пакет и зарегистрирован . Значок компонента ньореп будет добавлен на вкладку ActiveX. В процессе компиляции будет создан файл представления компонента -- модуль HHOPENLib_TLIB.pas, который содержит описание методов, свойств и событий компонента.

Модуль представления можно увидеть, загрузив его в редактор кода из каталога \Delphi 7\Lib. Пролистав в окне редактора кода модуль HHOPENLib_TLIB.pas, который представляет собой интерфейс для доступа к элементу управления, можно найти описание класса THhopen.

THhopen = class(Telecontrol)
private
  FIntf: _DHhopen;
  function GetControlInterface: _DHhopen;
protected
  procedure CreateControl;
  procedure InitControlData; override;
public
  function OpenHelp(const HelpFile: WideString;
    const HelpSection: WideString): Integer;
  procedure CloseHelp;
  property ControlInterface: _DHhopen read GetControlInterface;
  property DefaultInterface: _DHhopen read GetControlInterface;
published
  property isHelpOpened: WordBool index 1
  read GetWordBoolProp
  write SetWordBoolProp
  stored False;
end;
Класс тньореп предоставляет два метода: OpenHelp и CloseHelp. Метод OpenHeip обеспечивает вывод справочной информации, метод close-Help — закрывает окно справочной системы. У метода openHeip два параметра — имя файла справочной информации и имя раздела, содержимое которого будет выведено. В качестве имени раздела надо использовать имя HTML-файла, который применялся программой HTML Help Workshop в процессе создания СНМ-файла. Следует обратить внимание на то, что оба параметра должны быть строками widechar.

Следующая программа, ее диалоговое окно приведено на рис. 14.10, а текст — в листинге 14.2, демонстрирует использование ActiveX-компонента Hhopen для вывода справочной информации. Компонент нпореп добавляется в форму обычным образом. Так как во время работы программы он не отображается, то его можно поместить в любое место формы.

interface
uses
  Windows, Messages, SysUtils,
  Classes, Graphics, Controls,
  Forms, Dialogs, OleCtrls, HHOPENLibJTLB, StdCtrls;
type
  TForm1 = class(TForm)Label1: TLabel;
    Editl: TEdit; // файл справки
    Edit2: TEdit; //раздел справки (имя HTML-файла)
    Button1: TButton; // кнопка Справка
    Hhopen1: THhopen; // ActiveX-компонент Hhopen
    Label2: TLabel;
    Label3: TLabel;
    procedure ButtonlClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
// щелчок на кнопке Справка
procedure TForm1.ButtonlClick(Sender: TObject);
var
  HelpFile: string; // файл справки
  HelpTopic: string; // раздел справки
  pwHelpFile: PWideChar;
  // файл справки (указатель на строку WideChar)
  pwHelpTopic: PWideChar;
  // раздел (указатель на строку WideChar)
begin
  HelpFile := Edit1.Text;
  HelpTopic := Edit2.Text;
  // выделить память для строк WideChar
  GetMemfpwHelpFile, Length(HelpFile) * 2);
  GetMemfpwHelpTopic, Length(HelpTopic) * 2);
  // преобразовать Ansi-строку в WideString-строку
  pwHelpFile := StringToWideChar(HelpFile, pwHelpFile, MAX_PATH * 2)
  pwHelpTopic := StringToWideChar(HelpTopic, pwHelpTopic, 32);
  // вывести справочную информацию
  Form1.Hhopen1.OpenHelp(pwHelpFile, pwHelpTopic);
end;
end.

Вывод справочной информации выполняет процедура обработки события Onciic на кнопке Справка. Так как параметры метода OpenHelp должны быть строками widechar, то сначала выполняется преобразование ANSI-строки В строку WideChar.

Как скрыть или показать иконки на рабочем столе.

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  // скрыть иконки
  ShowWindow(FindWindow(nil, 'Program Manager'), SW_HIDE);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  // показать иконки
  ShowWindow(FindWindow(nil, 'Program Manager'), SW_SHOW);
end;

Как скрыть или показать иконки на рабочем столе 2.

procedure ShowDesktop(const YesNo : boolean);
var h : THandle;
begin
 h := FindWindow('ProgMan', nil);
 h := GetWindow(h, GW_CHILD);
 if YesNo = True then
   ShowWindow(h, SW_SHOW)
 else
   ShowWindow(h, SW_HIDE);
end;
{Использование:}
{Скрыть иконки на рабочем столе}
ShowDesktop(False);
{Показать иконки на рабочем столе}
ShowDesktop(true);

Показать или спрятать окно.

Автор: Осипов Евгений WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Показать/спрятать окно
SW_HIDE - скрывает окно и убирает из панели задачь, например нужно
при использовании иконки программы в системном трее при сворачивании
окна убирать его из панели задачь
SW_RESTORE - восстанавливает окно, при использовании иконки в
системном трее при нажатии на иконку, показать скрытое SW_HIDE-ом окно.
Другие параметры смотрите в справке Delphi по ShowWindow
Зависимости: Uses Windows
Автор:       ОсиповЕвгений Анатольевич, ICQ:114205759, Бузулук
Copyright:   Delphi Help, Windows API
Дата:        31 мая 2002 г.
***************************************************** }
ShowWindow(Application.Handle, SW_RESTORE); //Восстановить
ShowWindow(Application.Handle, SW_HIDE); //скрыть

Показ и скрытие дочерних MDI-окон.

Автор: Neil

...да, я понял: необходим гарантированный показ или скрытие MDI-окна. Гарантированно скрыть можно вызовом ShowWindow(theHandle, SW_HIDE), но в этом случае при показе *НЕ* используется ShowWindow. Вместо это сделайте так:

SetWindowPos(theHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE OR
SWP_NOSIZE OR SWP_SHOWWINDOW);

Далее я обнаружил, что дочерние окна не скрывались/показывались, *ЕСЛИ* для осуществления этих функций использовались оконные компоненты (например, кнопки). Для решения проблемы добавьте следующую строку после вызова SetWindowPos:

WinProcs.SetFocus(TheHandle);

*НЕ* используйте метод SetFocus; SetFocus здесь - Windows API функция.

Дополнение

По моему,все-таки, вызов ShowWindow(theHandle, SW_HIDE)проще, короче и, по крайней мере, в Delphi5 нормально работает с оконными компонентами.

procedure TMainForm.Button1Click(Sender: TObject);
begin
  //кнопка для показа/скрытия дочернего MDI-окна
  If ShowWindow(form1MDI.Handle, SW_HIDE)=False then
    ShowWindow(form1MDI.Handle, SW_SHOW);
end;

С уважением Михаил Шпанер.

Спрятать или показать кнопку ПУСК.

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

По нажатию на кнопку пишем следующее.

Startbutton(false);

...круто!!!...а чтобы сделать ПУСК опять видимым:

Startbutton(true);

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

procedure Startbutton(visi:boolean);
var
  Tray, Child : hWnd;
  C : array[0..127] of Char;
  S : string;
begin
  Tray := FindWindow('Shell_TrayWnd', nil);
  Child := GetWindow(Tray, GW_CHILD);
  while Child <> 0 do
  begin
    if GetClassName(Child, C, SizeOf(C)) > 0 then
    begin
      S := StrPAS(C);
      if UpperCase(S) = 'BUTTON' then
      begin
        if Visi then
          ShowWindow(Child, 1)
        else
          ShowWindow(Child, 0);
      end;
    end;
    Child := GetWindow(Child, GW_HWNDNEXT);
  end;
end;

Как программно спрятать или показать заголовок окна TitleBar.

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

procedure TForm1.HideTitlebar;
var
  Save: Longint;
begin
  if BorderStyle=bsNone then
    Exit;
  Save:=GetWindowLong(Handle, GWL_STYLE);
  if (Save and WS_CAPTION)=WS_CAPTION then
  begin
    case BorderStyle of
      bsSingle, bsSizeable:
        SetWindowLong(Handle, GWL_STYLE, Save and (not WS_CAPTION) or WS_BORDER);
      bsDialog:
        SetWindowLong(Handle, GWL_STYLE, Save and
        (not WS_CAPTION) or DS_MODALFRAME or WS_DLGFRAME);
    end;
    Height:=Height-GetSystemMetrics(SM_CYCAPTION);
    Refresh;
  end;
end;
procedure TForm1.ShowTitlebar;
var
  Save: Longint;
begin
  if BorderStyle=bsNone then
    Exit;
  Save:=GetWindowLong(Handle, GWL_STYLE);
  if (Save and WS_CAPTION)<>WS_CAPTION then
  begin
    case BorderStyle of
      bsSingle, bsSizeable:
        SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or WS_BORDER);
      bsDialog:
        SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or
        DS_MODALFRAME or WS_DLGFRAME);
    end;
    Height:=Height+getSystemMetrics(SM_CYCAPTION);
    Refresh;
  end;
end;

Заставляем появиться Hint в нужный момент.

Сделаем это по нажатию на первую кнопку, а по нажатию на вторую кнопку будем скрывать окно hint'a:

public
  { Public declarations }
  h: THintWindow;
procedure TForm1.Button1Click(Sender: TObject);
begin
  if h<>nil then
    H.ReleaseHandle;
  H:=THintWindow.Create(Form1);
  H.ActivateHint(Form1.ClientRect, 'Это всплывающая подсказка');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  if h<>nil then
    H.ReleaseHandle;
end;

Как вывести hint, когда курсор мышки над определённым контролом.

var  hintWnd: THintWindow;
procedure TForm1.ActivateHintNOW( x,y: Integer);
var
  rect: TRect;
begin
  HintTxt := 'qq';
  if hintTxt <> '' then
  begin
    rect := hintWnd.CalcHintRect( Screen.Width, hinttxt, nil);
    rect.Left := rect.Left + x;
    rect.Right := rect.Right + x;
    rect.Top := rect.Top + y;
    rect.Bottom := rect.Bottom + y;
    hintWnd.ActivateHint( rect, hinttxt);
  end;
end;
// Замечание: Не забудьте каждый раз создавать hintWnd:
//   hintwnd:= THintWindow.create(self);
// а затем освобождать его:
//   hintwnd.releasehandle;

Показ изображений в DBGrid.

Муж-программист (М) с женой (Ж) сидят в цирке на выступлении иллюзиониста. Во время очередного номера из небольшого ящика выходят много девушек. Ж: - Как они могли поместится в таком маленьком ящике?... М: - Ерунда! Если бы он использовал WinRAR он бы еще больше их туда запихнул...
После небольшого раздумья я решил прислать своего потомка DBGrid, который позволяет выводить изображения, благо добавочного кодирования не так много.

Код компонента:

{
// DBPICGRD.PAS (C)
//      ВСЕ ПРАВА ЗАЩИЩЕНЫ.
//
// ОПИСАНИЕ:
//      Компонент DBGrid, способный
//      отображать графику в ячейках.
}
unit DBPicGrd;
interface
uses
  DBGrids, DB, DBTables, Grids, WinTypes, Classes, Graphics;
type
  TDBPicGrid = class(TDBGrid)
  protected
    procedure DrawDataCell(const Rect: TRect;
      Field: TField; State: TGridDrawState); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property DefaultDrawing default False;
  end;
procedure Register;
implementation
constructor TDBPicGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  DefaultDrawing := False;
end;
procedure TDBPicGrid.DrawDataCell(const Rect: TRect; Field: TField;
  State: TGridDrawState);
var
  bmp: TBitmap;
begin
  with Canvas do
  begin
    FillRect(Rect);
    if Field is TGraphicField then
    try
      bmp := TBitmap.Create;
      bmp.Assign(Field);
      Draw(Rect.Left, Rect.Top, bmp);
    finally
      bmp.Free;
    end
    else
      TextOut(Rect.Left, Rect.Top, Field.Text);
  end;
end;
procedure Register;
begin
  RegisterComponents('Custom', [TDBPicGrid]);
end;
end.

Подскажите как правильно показать на экране и сохранить в базе картинку формата JPEG.

if Picture.Graphic is TJPegImage then
begin
  bs:=TBlobStream.Create(TBlobField(Field),bmWrite);
  Picture.Graphic.SaveToStream(bs);
  bs.Free;
end
else
if Picture.Graphic is TBitmap then
begin
  Jpg:=TJPegImage.Create;
  Jpg.CompressionQuality:=...;
  Jpg.PixelFormat:=...;
  Jpg.Assign(Picture.Graphic);
  Jpg.JPEGNeeded;
  bs:=TBlobStream.Create(TBlobField(Field),bmWrite);
  Jpg.SaveToStream(bs);
  bs.Free;
  Jpg.Free;
end
else
  Field.Clear;

Вывести информацию о пакете.

Автор: Xavier Pacheco

program PkgInfo;
uses
  Forms,
  Dialogs,
  SysUtils,
  PkgMain in 'PkgMain.pas' {PackInfoForm};
{$R *.RES}
var
  OpenDialog: TOpenDialog;
begin
  if (ParamCount > 0) and FileExists(ParamStr(1)) then
    PkgName := ParamStr(1)
  else
  begin
    OpenDialog := TOpenDialog.Create(Application);
    OpenDialog.DefaultExt := '*.bpl';
    OpenDialog.Filter := 'Packages (*.bpl)|*.bpl|Delphi 3 Packages ' +
      '(*.dpl)|*.dpl';
    if OpenDialog.Execute then
      PkgName := OpenDialog.FileName;
  end;
  if PkgName <> '' then
  begin
    Application.Initialize;
    Application.CreateForm(TPackInfoForm, PackInfoForm);
    Application.Run;
  end;
end.
unit PkgMain;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;
type
  TPackInfoForm = class(TForm)
    GroupBox1: TGroupBox;
    DsgnPkg: TCheckBox;
    RunPkg: TCheckBox;
    BuildCtl: TRadioGroup;
    GroupBox2: TGroupBox;
    GroupBox3: TGroupBox;
    Button1: TButton;
    Label1: TLabel;
    DescEd: TEdit;
    memContains: TMemo;
    memRequires: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  end;
var
  PackInfoForm: TPackInfoForm;
  PkgName: string; // This is assigned in project file
implementation
{$R *.DFM}
procedure PackageInfoCallback(const Name: string; NameType: TNameType;
  Flags: Byte; Param: Pointer);
var
  AddName: string;
  Memo: TMemo;
begin
  Assert(Param <> nil);
  AddName := Name;
  case NameType of
    ntContainsUnit: Memo := TPackInfoForm(Param).memContains;
    ntRequiresPackage: Memo := TPackInfoForm(Param).memRequires;
  else
    Memo := nil;
  end;
  if (Memo <> nil) then
  begin
    if Memo.Text <> '' then
      AddName := ', ' + AddName;
    Memo.Text := Memo.Text + AddName;
  end;
end;
procedure TPackInfoForm.FormCreate(Sender: TObject);
var
  PackMod: HMODULE;
  Flags: Integer;
begin
  // Since we only need to get into the package's resources,
  // LoadLibraryEx with LOAD_LIBRARY_AS_DATAFILE provides a speed-
  // efficient means for loading the package.
  PackMod := LoadLibraryEx(PChar(PkgName), 0, LOAD_LIBRARY_AS_DATAFILE);
  if PackMod = 0 then
    Exit;
  try
    GetPackageInfo(PackMod, Pointer(Self), Flags, PackageInfoCallback);
  finally
    FreeLibrary(PackMod);
  end;
  Caption := 'Package Info: ' + ExtractFileName(PkgName);
  DsgnPkg.Checked := Flags and pfDesignOnly <> 0;
  RunPkg.Checked := Flags and pfRunOnly <> 0;
  if Flags and pfNeverBuild <> 0 then
    BuildCtl.ItemIndex := 1;
  DescEd.Text := GetPackageDescription(PChar(PkgName));
end;
procedure TPackInfoForm.Button1Click(Sender: TObject);
begin
  Close;
end;
end.

Показывать номер страницы.

 Sub ShowViewPageNo()
    ActiveDocument.Repaginate
    varTotalPages = Selection.Information(wdNumberOfPagesInDocument)
    varVertScrollPercent = ActiveDocument.ActiveWindow _
       .VerticalPercentScrolled
    varViewPage = Int((varVertScrollPercent / 100) * varTotalPages) + 1
    varIPPage = Selection.Information(wdActiveEndPageNumber)
    MsgBox "Your insertion point is on page [" & varIPPage & "]." + _
       "You are looking at page [" & varViewPage & "].""
 End Sub

Как показать окно свойств экрана.

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

Я сижу за монитором нажимаю на мыша и смотрю погасшим взором, как стреляет П.П.Ш.

Для этого воспользуемся 'Rundll32.exe' и запустим её в 'shellexecute'. Не забудьте добавить 'shellapi' в Ваш список uses.

//Эта функция совместима со всеми версиями Windows
function GetSystemDir: TFileName;
var
  SysDir: array [0..MAX_PATH-1] of char;
begin
  SetString(Result, SysDir, GetSystemDirectory(SysDir, MAX_PATH));
  if Result = '' then
    raise Exception.Create(SysErrorMessage(GetLastError));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  x: Tfilename;
begin
  x := getsystemdir;
  ShellExecute(Form11.Handle, 'open', Pchar('rundll32.exe'),
  'shell32.dll,Control_RunDLL Desk.cpl,@0,3', Pchar(X), SW_normal);
end;

Показать диалог выбора компьютера.

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

- Дорогой, ты кого больше любишь? Меня или эту дурацкую штуку, за которой ты проводить целый, св#лочь!!! - Ну, киса, как ты можешь сравнивать неодушевленный предмет с компьютером?!
{ 
  The "Choose Computer" is a dialog provided by network services 
  (NTLANMAN.DLL) for Windows 2k/NT/XP 
  to display the servers and their computers. 
} 
type 
  TServerBrowseDialogA0 = function(hwnd: HWND; pchBuffer: Pointer; cchBufSize: DWORD): bool;  
  stdcall; 
function ShowServerDialog(AHandle: THandle): string; 
var 
  ServerBrowseDialogA0: TServerBrowseDialogA0; 
  LANMAN_DLL: DWORD; 
  buffer: array[0..1024] of char; 
  bLoadLib: Boolean; 
begin 
  LANMAN_DLL := GetModuleHandle('NTLANMAN.DLL'); 
  if LANMAN_DLL = 0 then 
  begin 
    LANMAN_DLL := LoadLibrary('NTLANMAN.DLL'); 
    bLoadLib := True; 
  end; 
  if LANMAN_DLL <> 0 then 
  begin @ServerBrowseDialogA0 := GetProcAddress(LANMAN_DLL, 'ServerBrowseDialogA0'); 
    DialogBox(HInstance, MAKEINTRESOURCE(101), AHandle, nil); 
    ServerBrowseDialogA0(AHandle, @buffer, 1024); 
    if buffer[0] = '\' then 
    begin 
      Result := buffer; 
    end; 
    if bLoadLib then 
      FreeLibrary(LANMAN_DLL); 
  end; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  label1.Caption := ShowServerDialog(Form1.Handle); 
end;

Как показать число, разделив каждые три цифры запятыми.

Автор: Олег Кулабухов.

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  i := 12345678;
  Memo1.Lines.Add(FormatFloat('#,', i));
end;

Показ логотипа при запуске приложения.

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

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

Затем в вашем файле PROJECT.DPR где-то сразу после ключевого слова begin напишите примерно следующее:

logoform := TLogoform.Create(nil);
{ ВНИМАНИЕ! show!   НЕ showmodal }
logoform.Show;
{ Здесь может размещаться код инициализации приложения,
  например, открытие базы данных... }
{ После блока кода, создающего все ваши формы и перед
  строчкой Application.Run напишите: }
logoform.Hide;
logoform.Release;

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

Показ логотипа при запуске приложения 2.

Автор: Howard Harkness
Юзер - это человек, который иногда наступает на грабли. Чайник - это человек, который всегда наступает на грабли. Ламер - это человек, который считает, что не наступает на грабли, но на самом деле наступает на них чаще, чем чайник. Программист - это человек, который изобретает новые грабли. Билл Гейтс - некое мифическое существо, покровителей граблей. МикроСофт - завод по производству граблей. Ассемблер - язык программирования, в котором программист наступает на грабли миллион раз в секунду.
Мне также понадобился логотип для одного из моих клиентов, поэтому я загрузил файл из публичной библиотеки и включил в свое приложение модуль, позволяющий выводить логотип при загрузке программы. В этом модуле было около 150 строк кода, но у меня свое понятие относительно количества необходимого для этого дела кода, поэтому я просто удалил лишнее, для которого просто не нашел применения. После моего хирургического вмешательства осталось всего пять или шесть строк кода, вплетенных в скелет формы. После сравнения моего кода с демо-проектом, поставляемым с Delphi (MastApp), я понял, что мой код много проще. Я не хочу обвинять автора в том, что он плохо сделал свою работу. Без его помощи этого кода просто бы не существовало. Я нахожу, что изучение чужого кода всегда существенно расширяет и дополняет багаж программиста. Код производит впечатление, что создание окошка с логотипом в Delphi плевое дело. Ну за дело: сначала, с помощью редактора, создайте форму (лучшим решением будет создание нового проекта в отдельном каталоге) и установите лучшие, на ваш художественный взгляд, значения таких свойств, как позиция, размеры и границы. Затем создайте обработчик события OnDeactivate и добавьте единственную строчку кода, в которой мы освобождаем форму. И, последнее, добавьте секцию initialization с тремя строчками кода, которые создают, выводят и обновляют форму. Для того, чтобы использовать новое окошко с логотипом, скопируйте получившийся .PAS-файл в каталог с проектом, в котором вы намереваетесь его использовать, и вставьте ссылку на модуль в самую верхнюю часть секции uses. НЕ добавляйте форму к проекту.

Ниже приведен пример кода (форма имеет имя SplashForm, модуль обозван как SPLASH). Мой код добавлен между закомментаренных блоков {>>вставить} и {<<конец вставки}, весь остальной код генерируется Delphi.

type
  TSplashForm = class(TForm)
    [...labels, bitmaps, и пр., добавляется редактором Delphi...]
    procedure FormDeactivate(Sender: TObject);
  end;
var
  SplashForm: TSplashForm;
implementation
{$R *.DFM}
{ Это шаманское место. Application.Run в нашем случае
вызывает программу деактивации.}
procedure TSplashForm.FormDeactivate(Sender: TObject);
begin
  {>>вставить}
  Free;
  {<<конец вставки}
end;
{>>вставить}
initialization
  begin
    SplashForm := TSplashForm.Create(nil);
    SplashForm.Show;
    { Я не уверен, но причина наличия здесь Update в том,
    что, как мне кажется, приложение пока не может работать
    c очередью своих сообщений}
    SplashForm.Update;
  end;
  {<<конец вставки}
end.

В головном модуле после строчки USES (.DPR-файл) просто добавьте "SPLASH," (не заключайте это в кавычки). Это все. Никакой головной боли с таймерами, никаких запусков отдельных приложений. Логотип быстро появляется и остается до тех пор, пока приложение не начнет свою работу.

Показ логотипа при запуске приложения 3.

program Project1;
uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};
  ULogo in 'ULogo.pas' {LogoForm};
{$R *.RES}
begin
  Application.Initialize; {до этого момента никаких изменений}
  with TLogoForm.Create(Application) do
  try
    Show;
    Update;
    Application.CreateForm(TForm1, Form1);
    {GProgress.AddProgress(1); - здесь можно двигать прогресс, если TGauge
    или TProgressBar лежат на ТLоgоFоrm'е}
    {если есть еще формы, то Application.CreateForm(TForm2, Form2); и т.д.}
  finally
    Free;
  end;
  Application.Run;
end.

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

Автор: Nomadic Для этого существует утилита DB2LOOK. Она находится в SQLLIB\MISC. Пример использования:
CONNECT TO SAMPLE USER xxx USING yyy
DB2LOOK -d SAMPLE -u xxx -e -t employee
Вывод может быть перенаправлен в файл. Полный синтаксис выдаётся по команде:
DB2LOOK ?

Просмотреть текст формы.

Статьи по Delphi (015)…
unit FRForm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls;
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
  ResStr: TResourceStream;
  MemStr: TMemoryStream;
begin
  ResStr := TResourceStream.Create(
    hInstance, 'TFORM1', RT_RCDATA);
  try
    MemStr := TMemoryStream.Create;
    ResStr.Position := 0;
    ObjectBinaryToText (ResStr, MemStr);
    MemStr.Position := 0;
    Memo1.Lines.LoadFromStream (
      MemStr);
  finally
    ResStr.Free
  end;
end;
end.

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

Показ окна без главной формы.

Как мне сделать так, чтобы видимой была только выбранная форма? (то есть без главной формы)

Попробуйте этот код в любом вторичном окне, которое вы НЕ хотите сопровождать главным окном:

...
private {Это включается в объявления формы.}
  { Private declarations }
  procedure CreateParams(VAR Params: TCreateParams); override;
...
procedure TForm2.CreateParams(VAR Params: TCreateParams);
begin
  Inherited CreateParams(Params);
  Params.WndParent := GetDesktopWindow;
end;

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

Показать корзину Windows.

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

uses
   ActiveX, ShlObj, ShellApi;
 procedure FreePidl(pidl: PItemIDList);
 var
    allocator: IMalloc;
 begin
   if Succeeded(shlobj.SHGetMalloc(allocator)) then
    begin
     allocator.Free(pidl);
     {$IFDEF VER90}
     allocator.Release;
     {$ENDIF}
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   exInfo: TShellExecuteInfo;
 begin
   // initialize all fields to 0 
  FillChar(exInfo, SizeOf(exInfo), 0);
   with exInfo do
   begin
     cbSize := SizeOf(exInfo); // required! 
    fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_IDLIST;
     Wnd := Handle;
     nShow := SW_SHOWNORMAL;
     lpVerb := 'open';
     ShGetSpecialFolderLocation(Handle, CSIDL_BITBUCKET, PItemIDLIst(lpIDList));
   end;
   ShellExecuteEx(@exInfo);
   FreePIDL(exinfo.lpIDList);
 end;

Исследование Текстового перекодировщика Штирлиц версия 4.0.

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

Инструменты: 0) SoftIce 1) WDAsm 2) Hex editor (HView). И вновь я говорю вам здрасте, дорогие мои читатели. Вот попалась мне в руки чудесная программка, которая настолько хитро перекодирует текстовые файлы, что вы не запутаетесь в различных русских кодировках. Мне она понравилась, если бы не огромный баннер в правом нижнем углу, который существует в течении минуты с момента создания и всячески мешает мне работать. Непорядок. Я взял файл Shtirlitz.exe и дизассемблировал его в WDAsm. Затем зашёл в String Reference и начал искать подозрительный текст... И нашёл!!! строку : "Shtirlitz.Banner" Дважды кликаю на неё и попадаю сюда:
:00401924 FF1594954400            Call dword ptr [00449594]
:0040192A 50                      push eax
:0040192B 6800080000              push 00000800
* Possible StringData Ref from Data Obj ->"Shtirlitz.Banner"
                                  |
:00401930 68D0004400              push 004400D0
:00401935 B928764400              mov ecx, 00447628
:0040193A E836530000              call 00406C75
Судя по вызываемым функциям (GetTickCOunt, SetTimer) мы в процедуре обработки баннерного состояния :) Теперь посмотрим чуть выше:
* Referenced by a CALL at Address:
|:0040182A   			<-- Вот из этого адреса вызывается процедура
|
:00401911 55                      push ebp
:00401912 8BEC                    mov ebp, esp
:00401914 83EC1C                  sub esp, 0000001C
Идём по адресу 0040182A, с которого вызывается эта функция и видим:
:0040182A E8E2000000              call 00401911 <--вызываем процедуру обработки баннера
:0040182F 84C0                    test al, al
:00401831 750E                    jne 00401841  <--если все ОК - прыгаем
Вот то что нам надо, а что если jne 00401841 на jne 00401833 и тогда программа будет всегда думать, что время баннера истекло. так и есть, я заменил в HView jne 00401841 на jne 00401833, т.е. теперь программа не будет в любом случае прыгать на процедуру вывода баннерного окна. Но программа при запуске начала ругаться, что исполняемый модуль был изменён. Значит в программе есть CRC проверка. Будем копать дальше. Запустим программу с помощью SymbolLoader'a из пакета SoftIce, чтобы оказаться в самом начале программы, затем скролим (не заходя в функции) до этого места:
:00435CE3 FF151C974400            Call dword ptr [0044971C]
:00435CE9 50                      push eax
:00435CEA E83D0CFDFF              call 0040692C		<-- Подозрительная функция
:00435CEF 8945A0                  mov dword ptr [ebp-60], eax
:00435CF2 50                      push eax
:00435CF3 E808FDFFFF              call 00435A00		<--это конец программе
:00435CF8 EB21                    jmp 00435D1B
Из того, что функция call 00435A00 делает полный каюк программе я заключил, что перед ней функция call 0040692C что-то представляет из себя! Я зашёл в неё (F8) и оказался тут:
:0040692C 8B0D88734400            mov ecx, dword ptr [00447388]
:00406932 85C9                    test ecx, ecx
:00406934 7414                    je 0040694A
:00406936 FF742410                push [esp+10]
:0040693A 8B01                    mov eax, dword ptr [ecx]
:0040693C FF742410                push [esp+10]
:00406940 FF74240C                push [esp+0C]
:00406944 FF10                    call dword ptr [eax]	<--что-то скрывают?
Их того, что больше функций нет, я предположил, что функция call dword ptr [eax] тоже очень странна! Я опять зашёл в неё и оказался тут: Это оказалось самое главное:
:0041FC4E 55                      push ebp
:0041FC4F 8BEC                    mov ebp, esp
:0041FC51 83EC1C                  sub esp, 0000001C
:0041FC54 53                      push ebx
:0041FC55 56                      push esi
:0041FC56 57                      push edi
:0041FC57 8BF9                    mov edi, ecx
:0041FC59 FF7510                  push [ebp+10]
:0041FC5C FF750C                  push [ebp+0C]
:0041FC5F FF7508                  push [ebp+08]
:0041FC62 E84D6EFEFF              call 00406AB4
:0041FC67 84C0                    test al, al
:0041FC69 745C                    je 0041FCC7
:0041FC6B 8BCF                    mov ecx, edi
:0041FC6D E8E9060000              call 0042035B
:0041FC72 8BCF                    mov ecx, edi
:0041FC74 E80F070000              call 00420388
:0041FC79 84C0                    test al, al
:0041FC7B 754A                    jne 0041FCC7
:0041FC7D 8BCF                    mov ecx, edi
:0041FC7F E80B030000              call 0041FF8F
:0041FC84 84C0                    test al, al
:0041FC86 743F                    je 0041FCC7
:0041FC88 68A9010000              push 000001A9
:0041FC8D E8A2040000              call 00420134
:0041FC92 59                      pop ecx
:0041FC93 50                      push eax
:0041FC94 E8074B0100              call 004347A0
:0041FC99 59                      pop ecx
:0041FC9A 8BF0                    mov esi, eax
:0041FC9C 8BCF                    mov ecx, edi
:0041FC9E E8B9030000              call 0042005C	<--Процедура генерации CRC
:0041FCA3 663BC6                  cmp ax, si	<--в ax - сгенерированный CRC, а в si - правильный (0x8560)
:0041FCA6 7426                    je 0041FCCE	<--если совпадают - прыгаем через сообщение о неверном CRC
Здесь необходимо заменить je 0041FCCE на jmp 0041FCCE, чтобы программа всегда думала, что CRC правильный. Я сделал все необходимые изменения, но при запуске программы главное окно появилось и тут же закрылось, получив всего 5 сообщений (WM_???). Так, Так, Так! Значит программа где-то ещё делает проверку! Я в этом месте немного помучался, но потом пошел старым и проверенным способом: запустил программу в SymbolLoader'e и начиная с адреса 0041FC4E прошёлся по НЕ изменнённой программе отладчиком, записав все условные переходы. Затем то же самое проделал с изменённой программой, сравнивая условные переходы с неизменённой программой. И очень скоро по адресу 0041FE9B я натолкнулся на расхождение:
:0041FE9B 750B                    jne 0041FEA8			<--кракнутая программа здесь прыгала, в то время как НЕ кракнутая - НЕ прыгала
:0041FE9D 8B45FC                  mov eax, dword ptr [ebp-04]
:0041FEA0 3B05E0314400            cmp eax, dword ptr [004431E0]
:0041FEA6 7412                    je 0041FEBA			<--кракнутая - не прыгает, не кракнутая - прыгает!
Ура! Я нашёл вторую проверку на некракнутость! Теперь я просто заменил jne 0041FEA8 на jne 0041FE9D, а je 0041FEBA на jmp 0041FEBA и........Все заработало! Ура! Баннера больше нет! И ни на что программа не жалуется!!! ВсЁ! А для самых маленьких крак:
----------Начало  крака----------------
Shtirlitz v4.0 Cracker
by vallkor (vallkor@chat.ru)
Shtirl~1.exe
00000C32: 0E 00
0001F0A6: 74 EB
0001F29C: 0B 00
0001F2A6: 74 EB
---------- Конец Крака ----------------
Исследовал и наваял туториал: vallkor//PTDS E-mail: vallkor@chat.ru Page : http://vallkor.chat.ru

Как правильно завершить некое приложение.

Автор: Nomadic.

Если не принудительно, то можно послать на его Instance сообщение WM_QUIT. Если же необходимо принудительно терминировать приложение, то смотрите ниже - под Windows NT процесс можно терминировать через специально предназначенный для этого хэндл. Иначе гарантии нет. Предположим, что процесс создаем мы, ожидая его завершения в течение maxworktime. Тогда -

var
  dwResult: Longint; // This example was converted from C source.
begin
  // Not tested. Some 'nil' assignments must be applied
  // as zero assignments in Pascal. Some vars need to
  // be declared (maxworktime, si, pi). AA.
  if CreateProcess(nil, CmdStr, nil, nil, FALSE,
    CREATE_NEW_CONSOLE, nil, nil, si, pi) then
  begin
    CloseHandle(pi.hThread);
    dwResult := WaitForSingleObject(pi.hProcess, maxworktime * 1000 * 60);
    CloseHandle(pi.hProcess);
    if dwResult <> WAIT_OBJECT_0 then
    begin
      pi.hProcess := OpenProcess(PROCESS_TERMINATE, FALSE, pi.dwProcessId);
      if pi.hProcess <> nil then
      begin
        TerminateProcess(pi.hProcess, 0);
        CloseHandle(pi.hProcess);
      end;
    end;
  end;
end;

Как правильно завершить некое приложение 2.

Автор: Панферов Андрей
Если бы колбаса продавалась так же как программное обеспечение, то под прозрачной оберткой у нее было бы следующее соглашение: 1) изготовитель не гарантирует совместимость данного продукта с организмом и не несет никаких последствий за его употребление; 2) потребителю запрещается исследовать содержимое этой колбасы (например, на наличие крысиных хвостов); 3) покупатель может сьесть колбасу, но все равно не станет ее владельцем; 4) употребить колбасу может только один человек; 5) покупатель не может продать колбасу кому-либо; 6)производитель не гарантирует, что данный продукт свободен от ошибок (например, от примесей цианистого калия); 7) возможная ответственность за здоровье покупателя не может превысить стоимость колбасы; 8) разрывая обертку, покупатель принимает условия данного соглашения.
var
  Form1: TForm1;
  // Глобальные переменные:
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  StartEn: Boolean = False;
implementation
{$R *.DFM}
// Запуск процесса
procedure TForm1.StartButtonClick(Sender: TObject);
var
  CmdStr: PChar; // Командная строка для запуска приложения
begin
  CmdStr := PChar(FilenameEdit1.FileName); // Определение командной строки
  StartEn := False; // Запущено ли приложение
  // Без этого приложение не стартует =<
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := SW_NORMAL; // Состояние окна запущенного приложения
  // <= Без этого приложение не стартует
  // Запускаем процесс
  StartEn := CreateProcess(nil,
    CmdStr, { указатель командной строки }
    nil, { указатель на процесс атрибутов безопасности }
    nil, { указатель на поток атрибутов безопасности }
    False, { флаг родительского обработчика }
    CREATE_NEW_CONSOLE or { флаг создания }
    NORMAL_PRIORITY_CLASS,
    nil, { указатель на новую среду процесса }
    nil, { указатель на имя текущей директории }
    StartupInfo, { указатель на STARTUPINFO }
    ProcessInfo); { указатель на PROCESS_INF }
  EndButton.Enabled := StartEn;
end;
// Терминация процесса
procedure TForm1.EndButtonClick(Sender: TObject);
begin
  if StartEn then
  begin
    ProcessInfo.hProcess := OpenProcess(PROCESS_TERMINATE, False,
      ProcessInfo.dwProcessId);
    if ProcessInfo.hProcess <> Null then
    begin
      TerminateProcess(ProcessInfo.hProcess, 0);
      CloseHandle(ProcessInfo.hThread);
      CloseHandle(ProcessInfo.hProcess);
    end;
  end;
  StartEn := False;
  EndButton.Enabled := StartEn;
end;

Как выключить компьютер с любой версией Windows.

Статьи по Delphi (015)…
Сообщение перед выключением компьютера: - Вашу жену зовут Наташа, детей-Юля и Павлик. Вы все еще хотите выйти из Windows?!
function GetWinVersion: string;
var
  VersionInfo: TOSVersionInfo;
  OSName: string;
begin
  // устанавливаем размер записи
  VersionInfo.dwOSVersionInfoSize := SizeOf( TOSVersionInfo );
  if Windows.GetVersionEx( VersionInfo ) then
  begin
    with VersionInfo do
    begin
      case dwPlatformId of
        VER_PLATFORM_WIN32s: OSName := 'Win32s';
        VER_PLATFORM_WIN32_WINDOWS: OSName := 'Windows 95';
        VER_PLATFORM_WIN32_NT: OSName := 'Windows NT';
      end; // case dwPlatformId
      Result := OSName + ' Version ' + IntToStr( dwMajorVersion ) + '.' + IntToStr( dwMinorVersion ) +
      #13#10' (Build ' + IntToStr( dwBuildNumber ) + ': ' + szCSDVersion + ')';
    end; // with VersionInfo
  end // if GetVersionEx
  else
  Result := '';
end;
procedure ShutDown;
const
  SE_SHUTDOWN_NAME = 'SeShutdownPrivilege'; // Borland forgot this declaration
var
  hToken: THandle;
  tkp: TTokenPrivileges;
  tkpo: TTokenPrivileges;
  zero: DWORD;
begin
  if Pos('Windows NT', GetWinVersion) = 1 then // we've got to do a whole buch of things
  begin
    zero := 0;
    if not OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
    begin
      MessageBox(0, 'Exit Error', 'OpenProcessToken() Failed', MB_OK);
      Exit;
    end; // if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
    if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
    begin
      MessageBox(0, 'Exit Error', 'OpenProcessToken() Failed', MB_OK);
      Exit;
    end; // if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
    // SE_SHUTDOWN_NAME
    if not LookupPrivilegeValue( nil, 'SeShutdownPrivilege' , tkp.Privileges[0].Luid ) then
    begin
      MessageBox(0, 'Exit Error', 'LookupPrivilegeValue() Failed', MB_OK);
      Exit;
    end; // if not LookupPrivilegeValue( nil, 'SeShutdownPrivilege' , tkp.Privileges[0].Luid )
    tkp.PrivilegeCount := 1;
    tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
    AdjustTokenPrivileges(hToken, False, tkp, SizeOf( TTokenPrivileges ), tkpo, zero);
    if Boolean(GetLastError()) then
    begin
      MessageBox(0, 'Exit Error', 'AdjustTokenPrivileges() Failed', MB_OK);
      Exit;
    end // if Boolean( GetLastError() )
    else
      ExitWindowsEx( EWX_FORCE or EWX_SHUTDOWN, 0 );
  end // if OSVersion = 'Windows NT'
  else
  begin // just shut the machine down
    ExitWindowsEx( EWX_FORCE or EWX_SHUTDOWN, 0 );
  end; // else
end;
Содержание.