Статьи по Delphi (015)…
Дублирование компонентов и их потомков во время выполнения приложения 2.
| Автор: Xavier |
| Три подруги: одна вышла замуж за сотрудника компании IBM, вторая за сотрудника компании APPALE, третья за сотрудника Microsoft... После первой брачной ночи расказывают: Первая: Класс.. Такая надежность, такая устойчивость, такое время наработки на отказ... Вторая: Кайф просто... Такой дружественный интерфейс, такая совместимость, такая производительность... Третья: А мой всю ночь расказывал как это будет хорошо... |
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBTables, Outline;
function Replicator(C: TComponent): TComponent;
implementation
{ Следующая процедура "клонирует" свойства C1 и записывает их в C2.
C1 и C2 должны иметь один и тот же тип. Используйте данный метод для
компонентов, не имеющих метода Assign. }
procedure CloneComponent(C1: TComponent; C2: TComponent);
var
S: TMemoryStream;
begin
if C1.ClassType <> C2.ClassType then
raise EComponentError.Create('Типы объектов не совместимы');
if C1 is TWinControl then
TWinControl(C2).Parent := TWinControl(C1).Parent;
S := TMemoryStream.Create; { создаем поток для работы с памятью }
with S do
begin
WriteComponent(C1); { пишем свойства C1 в поток }
Seek(0, 0); { перемещаемся в начало потока }
ReadComponent(C2); { читаем свойства из потока в C2 }
Free; { освобождаем поток }
end;
end;
{ Следующая функция "реплицирует" компонент C и возвращает новый
компонент типа и со свойствами компонента C. }
function Replicator(C: TComponent): TComponent;
begin
Result := TComponentClass(C.ClassType).Create(C.Owner); { создаем компонент }
CloneComponent(C, Result); { клонируем его }
end;
end.
|
Вот как это использовать:
var
BitBtn: TBitBtn;
begin
{ Если BitBtn1 уже существует }
TComponent(BitBtn) := Replicator(BitBtn1);
end;
|
Как создать базу данных Access во время выполнения.
procedure CreateMSAccessDB(filename : String);
var
DBEngine, Workspace: Variant;
const
{Important to use the following constant as is}
dbLangGeneral = ';LANGID=0x0409;CP=1252;COUNTRY=0';
dbVersion30 = 32;
begin
DBEngine := CreateOleObject('DAO.DBEngine');
{DBEngine := CreateOleObject('DAO.DBEngine.35'); For DAO 3.5}
Workspace := DBEngine.Workspaces[0];
try
Workspace.CreateDatabase(filename, dbLangGeneral, dbVersion30);
except on EOleException do
ShowMessage('Database already exists');
end;
end;
|
Создание события во время выполнения приложения.
На примере переопределения события в Memo:
memo.onchange:=memo1Change; procedure TForm1.Memo1Change(Sender: TObject); begin panel1.caption:='Содержимое было изменено'; end; |
Ошибки времени выполнения.
| Виндоват - испраглюсь! |
type
str31 = string[31];
function ErrMeaning (ResultCode: Integer): str31;
{----------------------------------------------------------}
{ Возвращает строковое значение по коду ошибки. }
{----------------------------------------------------------}
const
NumOfEntries = 108;
type
ErrorEntry = record
Code: Integer;
Meaning: str31;
end;
ErrorMeaningsArray = array [1..NumOfEntries] of ErrorEntry;
const
MeaningsArray: ErrorMeaningsArray =
{Ошибки DOS}
((Code: 1; Meaning: 'Неверный номер DOS-функции' {Invalid DOS function number}),
(Code: 2; Meaning: 'Файл не найден' {File not found}),
(Code: 3; Meaning: 'Путь не найден' {Path not found}),
(Code: 4; Meaning: 'Слишком много открытых файлов' {Too many open files}),
(Code: 5; Meaning: 'В доступе к файлу отказано' {File access denied}),
(Code: 6; Meaning: 'Неверный дескриптор файла' {Invalid file handle}),
(Code: 7; Meaning: 'Разрушены управляющие блоки памяти' {Memory control blocks destroyed}),
(Code: 8; Meaning: 'Недостаточно памяти DOS' {Insufficient DOS memory}),
(Code: 9; Meaning: 'Неверный адрес блока памяти' {Invalid memory block address}),
(Code: 10; Meaning: 'Неверное окружение DOS' {Invalid DOS environment}),
(Code: 11; Meaning: 'Неверный формат (DOS)' {Invalid format (DOS)}),
(Code: 12; Meaning: 'Неверный код доступа к файлу' {Invalid file access code}),
(Code: 13; Meaning: 'Неверные данные (DOS)' {Invalid data (DOS)}),
(Code: 15; Meaning: 'Неверный номер устройства' {Invalid drive number}),
(Code: 16; Meaning: 'Невозможно удалить текущую директорию' {Cannot remove current directory}),
(Code: 17; Meaning: 'Невозможно переименовать драйвер' {Cannot rename across drives}),
(Code: 18; Meaning: 'Файлов больше нет' {No more files}),
(Code: 19; Meaning: 'Диск защищен от записи' {Disk write-protected}),
(Code: 20; Meaning: 'Неизвестное устройство (DOS)' {Unknown unit (DOS)}),
(Code: 21; Meaning: 'Устройство не готово' {Drive not ready}),
(Code: 22; Meaning: 'Неизвестная команда DOS' {Unknown DOS command}),
(Code: 23; Meaning: 'CRC-ошибка' {CRC error}),
(Code: 24; Meaning: 'Плохой запрос длины структуры' {Bad request structure length}),
(Code: 25; Meaning: 'Ошибка поиска' {Seek error}),
(Code: 26; Meaning: 'Неизвестный тип носителя' {Unknown media type}),
(Code: 27; Meaning: 'Сектор диска не найден' {Disk sector not found}),
(Code: 28; Meaning: 'Недостаточно бумаги' {Out of paper}),
(Code: 29; Meaning: 'Ошибка записи' {Write fault}),
(Code: 30; Meaning: 'Ошибка чтения' {Read fault}),
(Code: 31; Meaning: 'Общий сбой' {General failure}),
(Code: 32; Meaning: 'Нарушение общего доступа к файлу' {File sharing violation}),
(Code: 33; Meaning: 'Нарушение блокировки файла' {File lock violation}),
(Code: 34; Meaning: 'Неверная смена диска' {Invalid disk change}),
(Code: 35; Meaning: 'Управляющий блок файла недоступен' {File control block unavailable}),
(Code: 36; Meaning: 'Переполнение разделяемого буфера' {Sharing buffer overflow}),
(Code: 37; Meaning: 'Несовпадение кодовой страницы' {Code page mismatch}),
(Code: 38; Meaning: 'Ошибка обработки EOF' {Error handling EOF}),
(Code: 39; Meaning: 'Дескриптор диска полон' {Handle disk full}),
(Code: 50; Meaning: 'Сетевой запрос не поддерживается' {Network request not supported}),
(Code: 51; Meaning: 'Удаленный компьютер недоступен' {Remote computer not listening}),
(Code: 52; Meaning: 'Дубликат имени в сети' {Duplicate name on network}),
(Code: 53; Meaning: 'Сетевое имя не найдено' {Network name not found}),
(Code: 54; Meaning: 'Сеть занята' {Network busy}),
(Code: 55; Meaning: 'Сетевое устройство больше не существует' {Network device no longer exists}),
(Code: 56; Meaning: 'Превышен лимит команды NetBIOS' {NetBIOS command limit exceeded}),
(Code: 57; Meaning: 'Ошибка сетевого адаптера' {Network adaptor error}),
(Code: 58; Meaning: 'Неверный сетевой ответ' {Incorrect network response}),
(Code: 59; Meaning: 'Неожиданная ошибка сети' {Unexpected network error}),
(Code: 60; Meaning: 'Несовместимый удаленный адаптер' {Incompatible remote adaptor}),
(Code: 61; Meaning: 'Очередь на печать переполнена' {Print queue full}),
(Code: 62; Meaning: 'Нет достаточного места для файла печати' {Not enough space for print file}),
(Code: 63; Meaning: 'Файл печати удален' {Print file deleted}),
(Code: 64; Meaning: 'Сетевое имя удалено' {Network name deleted}),
(Code: 65; Meaning: 'Доступ запрещен' {Access denied}),
(Code: 66; Meaning: 'Неверный тип сетевого устройства' {Network device type incorrect}),
(Code: 67; Meaning: 'Сетевое имя не найдено' {Network name not found}),
(Code: 68; Meaning: 'Превышен предел сетевого имени' {Network name limit exceeded}),
(Code: 69; Meaning: 'Превышен предел сеансов NetBIOS' {NetBIOS session limit exceeded}),
(Code: 70; Meaning: 'Временная пауза' {Temporarily paused}),
(Code: 71; Meaning: 'Сетевой запрос не принят' {Network request not accepted}),
(Code: 72; Meaning: 'Пауза переадресации печати/диска' {Print/disk redirection paused}),
(Code: 80; Meaning: 'Файл уже существует' {File already exists}),
(Code: 82; Meaning: 'Невозможно создать каталог' {Cannot make directory entry}),
(Code: 83; Meaning: 'Ошибка прерывания 24' {Fail on interrupt 24}),
(Code: 84; Meaning: 'Слишком много переадресаций' {Too many redirections}),
(Code: 85; Meaning: 'Дубликат переадресации' {Duplicate redirection}),
(Code: 86; Meaning: 'Неверный пароль' {Invalid password}),
(Code: 87; Meaning: 'Неверный параметр' {Invalid parameter}),
(Code: 88; Meaning: 'Ошибка данных сети' {Network data fault}),
{Ошибки ввода/вывода (I/O errors)}
(Code: 100; Meaning: 'Ошибка чтения диска' {Disk read error}),
(Code: 101; Meaning: 'Ошибка записи диска' {Disk write error}),
(Code: 102; Meaning: 'Файл не назначен' {File not assigned}),
(Code: 103; Meaning: 'Файл не открыт' {File not open}),
(Code: 104; Meaning: 'Не открыт файл для приема' {File not open for input}),
(Code: 105; Meaning: 'Не открыт файл для выдачи' {File not open for output}),
(Code: 106; Meaning: 'Неверный числовой формат' {Invalid numeric format}),
{Критические ошибки (Только для реального или защищенного режима)}
(Code: 150; Meaning: 'Диск защищен от записи' {Disk is write protected}),
(Code: 151; Meaning: 'Неизвестное устройство' {Unknown unit}),
(Code: 152; Meaning: 'Устройство не готово' {Drive not ready}),
(Code: 153; Meaning: 'Неизвестная команда DOS' {Unknown DOS command}),
(Code: 154; Meaning: 'Ошибка CRC в данных' {CRC error in data}),
(Code: 155; Meaning: 'Плохой запрос длины структуры устройства' {Bad drive request struct length}),
(Code: 156; Meaning: 'Ошибка позиционирования диска' {Disk seek error}),
(Code: 157; Meaning: 'Неизвестный тип носителя' {Unknown media type}),
(Code: 158; Meaning: 'Сектор не найден' {Sector not found}),
(Code: 159; Meaning: 'Недостаточно бумаги в принтере' {Printer out of paper}),
(Code: 160; Meaning: 'Ошибка записи устройства' {Device write fault}),
(Code: 161; Meaning: 'Ошибка чтения устройства' {Device read fault}),
(Code: 162; Meaning: 'Аппаратный сбой' {Hardware failure}),
{Фатальные ошибки}
(Code: 200; Meaning: 'Деление на ноль' {Division by zero}),
(Code: 201; Meaning: 'Ошибка проверки диапазона' {Range check error}),
(Code: 202; Meaning: 'Ошибка переполнения стека' {Stack overflow error}),
(Code: 203; Meaning: 'Ошибка переполнения кучи' {Heap overflow error}),
(Code: 204; Meaning: 'Неверная операция с указателем' {Invalid pointer operation}),
(Code: 205; Meaning: 'Переполнение числа с плавающей точкой' {Floating point overflow}),
(Code: 206; Meaning: 'Потеря значимости числа с плавающей точкой' {Floating point underflow}),
(Code: 207; Meaning: 'Неверная операция с числом с плавающей точкой' {Invalid floating pt. operation}),
(Code: 208; Meaning: 'Не установлен оверлей-менеджер' {Overlay manager not installed}),
(Code: 209; Meaning: 'Ошибка чтения оверлей-файла' {Overlay file read error}),
(Code: 210; Meaning: 'Объект не инициализирован' {Object not initialised}),
(Code: 211; Meaning: 'Вызов абстрактного метода' {Call to abstract method}),
(Code: 212; Meaning: 'Ошибка регистрации потока' {Stream registration error}),
(Code: 213; Meaning: 'Индекс TCollection вышел за границы диапазона' {TCollection index out of range}),
(Code: 214; Meaning: 'Ошибка переполнения TCollection' {TCollection overflow error}),
(Code: 215; Meaning: 'Ошибка арифметического переполнения' {Arithmetic overflow error}),
(Code: 216; Meaning: 'Общая ошибка защиты' {General Protection Fault}),
(Code: 217; Meaning: 'Необработанное исключение' {Unhandled exception}),
(Code: 219; Meaning: 'Неверное приведение типа' {Invalid typecast}));
var
Low, High, Mid, Diff: Integer;
begin
Low := 1;
High := NumOfEntries;
while Low <= High do
begin
Mid := (Low + High) div 2;
Diff := MeaningsArray[Mid].Code - ResultCode;
if Diff < 0 then Low := Mid + 1 else
if Diff > 0 then High := Mid - 1 else
begin {нашли это}
ErrMeaning := MeaningsArray[Mid].Meaning;
Exit; {ErrMeaning}
end;
end; {while}
ErrMeaning := 'Ошибка ' + IntToStr(ResultCode) +
' (неизвестное значение)';
end; {ErrMeaning}
|
Ещё...
var
i: Integer;
begin
for i := 1 to NumOfEntries do
if MeaningsArray[i].Code < ResultCode then
Continue {до следующей итерации цикла FOR loop}
else
begin
if MeaningsArray[i].Code = ResultCode then
begin
ErrMeaning := MeaningsArray[i].Meaning;
Exit; {ErrMeaning}
end
else {Code in array > ResultCode}
Break; {выход из цикла FOR}
end;
ErrMeaning := 'Ошибка ' + IntToStr(ResultCode) +
' (неизвестное значение)';
end; {ErrMeaning}
|
Функции редактора полей во время выполнения программы.
Автор: Lindsay Reichmann Возможен ли вызов функций редактора полей (Fields Editor) во время выполнения программы? Да. Если вы определили поля во время разработки приложения, то во время выполнения можно менять их свойства (например, Size).Например, следующий код изменяет каждый размер поля TField.Size так, чтобы соответствовать фактическому размеру поля открываемого набора данных:
procedure SetupFieldsAndOpenDataset(DataSet: TDataSet);
var
FieldNum, DefNum: Integer;
begin
with DataSet do
begin
if Active then
Close;
FieldDefs.Update; {набор данных должен быть закрыт}
{ищем каждое предопределенное TField в DataSet.FieldDefs:}
for FieldNum := FieldCount - 1 downto 0 do
with Fields[FieldNum] do
begin
DefNum := FieldDefs.IndexOf(FieldName);
if DefNum < 0 then
raise Exception.CreateFmt(
'Поле "%s" не найдено в наборе данных "%s"',
[FieldName, Dataset.Name]);
{устанавливаем свойство size:}
Size := FieldDefs[DefNum].Size;
end;
Open;
end;
end;
|
RTTI в действии.
| Интернетчик проснулся ночью. Смотрит - вокруг темно, фонари не горят, ничего не видно. "Network is unreachable", - подумал Интернетчик. |
Бывает, что надо всем контролам на форме поменять OnMouseMove Event. Или же нужно сбросить свойство Hint у всех элементов, у которых это свойство есть. Контролы могут быть самые разные TEdit, TLabel, TListView и т.п. Такаие возможности в Delphi есть, более того, на них построена вся среда визуальной разработки IDE Delphi.
Для справки:
Run-time type information (RTTI) - это специальный механизм определения типа объекта во время выполнения. Чаще всего это актуально для определения типа объекта по указателю. Многие каркасные библиотеки самостоятельно поддерживают этот мехзанизм. В том числе и VCL.
Приведем пример использования данного механизма в Delphi. Для начала рассмотрим работу с методами, а затем - со свойствами.
Следующий пример устанавливает у всех компонентов на форме обработчик OnMouseMove на собственную процедуру.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
//...эта ф-ия обязательно должна быть описана здесь, а не в одном из
// следующих разделов, иначе MethodAddress() ее не видит.
procedure CommonMouseMove (Sender: TObject;
Shift: TShiftState; X, Y: Integer);
private
public
procedure SetEventMethodToAllComponentsInForm( FormX: TForm );
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
TypInfo;
procedure TForm1.SetEventMethodToAllComponentsInForm( FormX: TForm );
var
PropInfo: PPropInfo;
i: integer;
CommonMethod: TMethod;
begin
for i:=0 to FormX.ComponentCount-1 do
begin
PropInfo := GetPropInfo( FormX.Components[i].ClassInfo, 'OnMouseMove');
if PropInfo <> nil then
begin
CommonMethod.Data := FormX.Components[i];
CommonMethod.Code := Form1.MethodAddress('CommonMouseMove');
SetMethodProp( FormX.Components[i], PropInfo, CommonMethod );
end;
end;
end;
procedure TForm1.CommonMouseMove (Sender: TObject;
Shift: TShiftState; X,Y: Integer);
begin
ShowMessage('Delphi World is COOL!');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//...передадим для пробы ссылку на эту же форму
SetEventMethodToAllComponentsInForm( self );
end;
|
Это что касается методов. Свойства тоже не обделены такими возможностями. Для из изменения нужно использовать процедуру procedure SetOrdProp(Instance: TObject; PropInfo: PPropInfo; Value: Longint);
// procedure SetOrdProp(Instance: TObject;
// PropInfo: PPropInfo; Value: Longint);
// Например следующий код устанавливает
// свойство Color у всех компонентов формы,
// которые этим свойством располагают:
for i:=0 to FormX.ComponentCount-1 do
begin
PropInfo := GetPropInfo( FormX.Components[i].ClassInfo, 'Color');
if PropInfo <> nil then
SetOrdProp( FormX.Components[i], PropInfo, clGreen );
end;
|
Обратите внимание, что используемые здесть процедуры для работы с RTTI определены в стандартном модуле TypInfo.
Жизнь и смерть в режиме run-time.
Оформил: DeeCo
Автор: Елена Филиппова
Лирическое вступление.
Конец ноября в этом году выдался довольно забавный, и погода в Москве была неестественная для этого времени года и респондентов Круглого стола как прорвало. Если раньше можно было выделить десяток основных животрепещущих вопросов, например как выдвинуть CD-Rom, поместить иконку в Tray и обязательно добавить в Caption формы непрямоугольную кнопку, то теперь появился новый и явный лидер. И в личной почте и в вопросах на Круглый стол, и даже по Аське, несмотря на отсутствие авторизации, я получила за неделю десяток практически дословно повторяющихся вопросов от совершенно разных и незнакомых мне людей. "Как создать кнопку во время работы? Как назначить ей обработчики событий? Почему нельзя написать (Edit + IntToStr(i)).Text:='' и как же тогда можно писать? Не перебирать же все Еdit'ы у меня их на форме 100 штук! Ну Дельфя и дает!.."И так далее с небольшими вариациями. Может курсовые задачи во всех ВУЗах одинаковые или это магнитные бури с солнечной активностью... А может "Дельфя" дает далеко не всем.Итак, зачетной сессии посвящается...
Материал предназначен для начинающих программистов, умеющих работать с компонентами Delphi в режиме design-time но уже не считающих, что программировать это значит "накликать мышкой форму". Никаких интересных моментов для профи статья не содержит, это исключительно учебный материал. Цель статьи ответить на вопросы и показать : Каким образом можно создавать и располагать на форме визуальные компоненты? Каким образом обрабатывать группы компонент, меняя их свойства "одним махом"? Как назначать обработчики для разных событий? Зачем нужно свойство Tag? Как можно двигать компоненты по форме? И все это - во время работы программы (режим run-time).Пояснения к проекту:
Скачать проект (откомпилировано в Delphi 5) : Исходники + Exe-файл RunTime.zip (200 K) Только исходники RunTime_Source.zip (8 K) Проект показывает сразу все перечисленные возможности работы с компонентами, он представляет собой форму , на которой есть панель (PanelTest : TPanel), управляющие компоненты и меню. Все наши компоненты, которые мы будем создавать, будут располагаться на панели PanelTest. Чтобы не путать тестовые компоненты с управляющими. Создавать будем TEdit , TButton , TCheckBox и TLabel по выбору. Управляющие элементы формы: rgComponents : TRadioGroup - выбор типа компонента ColorGrid : TColorGrid - выбор цвета фонта для меню "Изменить цвет" MainMenu : TMainMenu - действия с группами компонент pmComponent : TPopupMenu - изменения свойств конкретного компонента и его удаление. Некоторые элементы MainMenu и pmComponent будут достраиваться и изменять свои свойства в run-time. Создаем компонент. Это очень просто, главное - знать какой именно это компонент и где он будет лежать. Например, создадим TButton на нашей панели PanelTest.var New: TButton; begin // Создаем НОВЫЙ экземпляр класса TButton // и кладем ссылку на него в переменную New New := TButton.Create(PanelTest); // Координаты левого верхнего угла новой кнопки на панели New.Top := ...; New.Left := ...; New.Name := 'Button'; // А вот эта процедура будет вызываться // при нажатии на новенькую кнопку // то есть - обработка событи OnClick New.OnClick := OnClickButton; // Оп! И делаем кнопку видимой на PanelTest New.Parent := PanelTest; end;Комментарии к коду. Хотя код короткий и довольно простой, для "новичков" стоит дать некоторые пояснения. Определение переменной Var New : TButton НЕ СОЗДАЕТ новой кнопки, а только говорит о том, что в переменной New будет лежать ссылка на экземпляр класса TButton. Этот момент надо понять очень четко. Извиняюсь перед продвинутыми начинающими за прописные истины, но получаемые мной многочисленные письма заставляют обращать внимание на эти мелочи. Это довольно важные мелочи. Примечание: Советую прочесть статью Максима Игнатьева "Путешествуя по TObject. Или как оно работает." Итак, создаем новый экземпляр класса TButton, а попросту говоря, нашу кнопку:
New := TButton.Create(PanelTest);Конструктор имеет входной параметр
TButton.Create(AOwner: TObject);где указан Owner , то есть "хозяин", создаваемого объекта. Хозяин компонента отвечает за его корректное удаление и освобождение памяти. В качестве хозяина мы передаем ему панель PanelTest, это означает, что при удалении PanelTest будет удалена и наша кнопка. Если в качестве параметра указать nil, то и заботиться об удалении кнопки придется самим. Какие комментарии можно добавить к прозрачным командам New.Top:= ... ? Ну вот, например такие: если мы создаем компонент, для которого требуется задать не только координаты левого верхнего угла, но и его ширину и высоту, то вместо четырех присвоений используйте метод TControl.SetBounds . Назначение имени для динамических компонентов совершенно не обязательно, они прекрасно могут существовать и без имен. Только тогда невозможно будет найти их методом FindComponent, что, впрочем, не всегда необходимо. В нашем случае я назначаю имя всем компонентам исключительно от лени - присваивание New.Name:='Button' автоматически заполнит поле Text для TEdit и поле Caption для TLabel, TButton и TCheckBox и об этом можно не заботиться. Созданная кнопка почти готова к самостоятельно жизни, за одним маленьким исключением... ее пока не видно. Посмотрите повнимательнее Help на тему TControl.Parent, это то самое свойство, которое нам нужно. Parent - это визуальный родитель контрола. Присваивание New.Parent:=PanelTest помещает кнопку New в массив дочерних контролов для ранели PanelTest.
property Controls[Index: Integer]: TControl;Теперь кнопка будет не просто видна на панели, а будет принадлежать этой панели. То есть при возможном перемещении PanelTest или изменении ее размеров , наша кнопка будет оставаться внутри PanelTest. Но и это еще не все. Если кнопка нам нужна не для украшения формы, то хотелось бы, чтобы она адекватно реагировала на некоторые события, ну хотя бы на свое собственное нажатие. Читаем help :
property OnClick: TNotifyEvent;Это свойство определяет реакцию на событие, которое возникает при клике мышки или нажатии Enter на контрол. Лирическое отступление:Смотрим help по теме "Procedural types" , то есть "процедурные типы". Например тип :
type TMyProcedure = procedure(I: Integer);это процедурный тип, который является ссылкой(указателем) на адрес процедуры с определенным списком параметров.
var MyProcedure: TMyProcedure; procedure X(I: Integer); begin ... end; ... MyProcedure := X;Переменная MyProcedure содержит адрес соответствущей процедуры. Таким образом процедуру можно передавать как параметр в другие процедуры и функции.
type TNotifyEvent = procedure(Sender: TObject) of object;Тип TNotifyEvent это не просто указатель на процедуру, это ссылка на метод, об этом говорит директива "of object". Чем отличается ссылка на процедуру от ссылки на метод? На самом деле тип "ссылка на метод" реально содержит две ссылки - непосредственно адрес метода(процедуры) и ссылку на сам объект, которому этот метод принадлежит. Для того, чтобы назначить свой собственный обработчик события, мы должны создать процедуру с параметром Sender: TObject , назовем ее OnClickButton. Процедура эта будет незамысловатая - выдается сообщение о том, какая именно кнопка нажата. Для того, чтобы понять какая кнопка нажата, обратимся к параметру Sender, который передается нам в процедуру. Sender это тот компонент, который инициирует событие, то есть в нашем случае - нажатая кнопка.
procedure TForm1.OnClickButton(Sender: TObject);
var
Value: string;
begin
MessageDlg('Нажата кнопка ' + TControl(Sender).Name, mtInformation, [mbOk],
0);
end;
И назначим эту процедуру в качестве обработчика события OnClick созданной кнопки: New.OnClick := OnClickButton;
Теперь наша кнопка прям как настоящая!
Лень, как источник вдохновения. Итак, начнем. Компоненты будем создавать при двойном клике мышкой на панели PanelTest. В реальных примерах в run-time приходится добавлять на форму РАЗНЫЕ компоненты. В нашем примере будем создавать не только кнопки (TButton) , а еще и TLabel, TEdit и TCheckBox. Радиокнопки rgComponents как раз указывают, что именно мы собираемся создавать. Можно пойти самым простым путем, воспользуемся уже разобранным кодом, добавим CASE на все наши случаи и для каждого класса перепишем этот код, изменяя тип создаваемого компонента. Самый простой путь не всегда самый правильный. Во-первых, если Вам понадобится добавить еще один тип, придется еще дописать кусок кода. А во-вторых, лениво повторять один и тот же код сто раз. :о) То, что не красиво выглядит, с большой степенью вероятности, не совсем верно реализовано. Настоящий программист, человек довольно ленивый... Именно это заставляет его оптимизировать процесс разработки. Именно по этой причине появился первый компилятор :о) По этой же причине мы пойдем другим путем.Ссылка на класс.
Хорошо бы передать Delphi тип компонента, экземпляр которого мы хотим создать. Тогда бы задача упростилась. Формируем массивчик типов, передаем очередной элемент массива и Delphi сама вызывает нужные методы именно того класса, который мы ей дали! А ведь мы можем это сделать : для этой цели существуют ссылки на класс - class references. Посмотрите help по этой теме, там довольно подробно объясняется это понятие и приводятся примеры. Для примера: class of TObject это ссылка на класс TObject.type TClass = class of TObject;Переменная типа TClass не может содержать экземпляр TObject, это только ссылка на определенный класс. Мы можем определить свой собственный тип, как ссылку на класс, который является родительским по отношению ко ВСЕМ нашим типам создаваемых компонент. Нам нужна ссылка на TControl. В Delphi есть уже "готовые" типы ссылок на классы. Кроме TClass есть еще несколько , в том числе и TControlClass.
type TControlClass = class of TControl;Воспользуемся им. Нам понадобится список классов, с которыми будм работать :
type TListClass = array[0..3] of TControlClass; const ListClass: TListClass = (TEdit, TButton, TCheckBox, TLabel);На событие OnDblClick панели PanelTest разбираем, что именно мы должны создать и создаем это...
procedure TForm1.PanelTestDblClick(Sender: TObject);
const
No: integer = 0;
var
TypeClass: TControlClass;
New: TControl;
Point: TPoint;
begin
if (rgComponents.ItemIndex >= 0) and
(rgComponents.ItemIndex < rgComponents.Items.Count) then
begin
// Получаем ссылку на выбранный класс
TypeClass := TControlClass(ListClass[rgComponents.ItemIndex]);
Inc(No); // увеличиваем счетчик компонент
// Создаем компонент - вызываем конструктор выбранного класса
New := TypeClass.Create(PanelTest);
Point := PanelTest.ScreenToClient(Mouse.CursorPos);
New.Top := Point.y;
New.Left := Point.x;
// Имя = название класса + номер нового компонента
New.Name := New.ClassName + IntToStr(No);
New.Tag := 1;
// Навешиваем меню по правой кнопке
TEdit(New).PopupMenu := pmComponent;
// Если это кнопка - назначим обработчик
if TypeClass = TButton then
TButton(New).OnClick := OnClickButton;
// И помещаем новенького на панель
New.Parent := PanelTest;
end;
end;
Все! И никаких САSЕ'ов :о)
Комментарий:
Point := PanelTest.ScreenToClient(Mouse.CursorPos);Привязываем положение нового компонента к той точке, в которой кликнули мышкой. Процедра ScreenToClient пересчитывает абсолютные (экранные) координаты глобального объекта Mouse в координаты относительно левого верхнего угла панели. Изменение свойств группы компонент. Вы обратили внимание на строку New.Tag:=1 ? Что это за свойство и в чем его смысл? Это целое число типа LongInt(длиной 4 байта), которое является полноценным свойством компонента и никак не учитывается средой. Delphi на него не обращает внимания. Можете там что-нибудь сохранить. На всякий случай. Итак, присвоим нашим новеньким Tag равный 1. Строка
TEdit(New).PopupMenu := pmComponent;связывает наши компоненты с popup-меню pmComponent, которое теперь будет вызываться при нажатии правой кнопки на компоненте. Не смущайтесь приведением к типу TEdit, дело в том, что свойство PopupMenu у класса TControl определено как protected и может быть использовано только его потомками. Чтобы не перебирать все наши типы, выбираем один и используем его для проникновения к нужному нам свойству. Так как PopupMenu есть у всех наших классов и оно то самое, которое наследовано от TControl.PopupMenu , то присвоение будет верным. Меню pmComponent содержит три опции :
Tag = 1 Tag = 2 ------ //Удалить компонентПервая устанавливает свойство Tag равным 1, вторая меняет его на 2. И последняя опция удаляет компонент по нашему желанию. Обратите внимание на основное меню окна, а именно на его пункт "Измененить цвет" Будем изменять цвет шрифта наших компонентов по выбранному условию. Все три пункта меню "Измененить цвет" используют один и тот же обработчик:
procedure TForm1.AllColorClick(Sender: TObject);
var
i: Integer;
begin
if TControl(Sender).Tag = 0 then
for i := 0 to PanelTest.ControlCount - 1 do
TEdit(PanelTest.Controls[i]).Font.Color := ColorGrid.ForegroundColor
else
for i := 0 to PanelTest.ControlCount - 1 do
if TWinControl(PanelTest.Controls[i]).Tag = TControl(Sender).Tag then
TEdit(PanelTest.Controls[i]).Font.Color := ColorGrid.ForegroundColor
end;
Sender в этой процедуре - выбранный пункт меню. Обратите внимание в самом проекте, как расставлено свойство Tag у пунктов меню "Измененить цвет".
Перебираем в цикле массив Controls панели PanelTest, он содержит наши компоненты, и изменяем цвет шрифта по условию - если значение Tag выбранного пункта меню совпадает со значением Tag очередного компонента, это наш клиент!
Здесь мы изменяли свойство у всех компонентов, незавсимо от их типа. А в меню "Очистить" проводится почти такая же операция, но условием выступает принадлежность к определенному типу. Если мы хотим очистить TEdit (то есть очистить его свойство Text ), то перебор списка Controls будет выглядеть так:
for i := 0 to PanelTest.ControlCount - 1 do
begin
if (PanelTest.Controls[i] is TEdit) then
TEdit(PanelTest.Controls[i]).Text := ''
end;
В удалении компонента нет никаких сложностей. В нашем примере только надо понять, что именно удаляется.
Воспользуемся свойством TPopupMenu.PopupComponent : TComponent , определяющим компонент, над которым в данный момент нажали правую кнопку мыши и вызвали это меню. pmComponent.PopUpComponent.Free;
На рисунке показан момент работы проекта:
Таинственное свойство Tag.
Было бы обидно, если Delphi разрешала бы программистам хранить для своих нужд только целочисленный признак. А мало ли какие у кого нужды?
Если внимательно прочесть Help, то в самой последней строке нам откроется истина : в свойстве Tag можно хранить что угодно длинной 4 байта ( any 32-bit value such as a component reference or a pointer ) !
Это может быть целое число , а может быть ссылка (pointer). Например, ссылка на компонент. Чувствуете, какие открываются перспективы?!
Пункт "Отметить CheckBox" основного меню в design-time имеет только один пункт (полоску считать не будем). По смыслу задачи в этот пункт должны добавляться подпункты со списком созданных к этому моменту ТСhескВох'ов. При нажатии на нужный пункт, соответствующий TCheckBox на панели будет отмечаться (Checked:=True). Чудное(ударение на первый слог) свойство Tag поможет очень изящно решить эту задачу. Достраиваться меню будет в момент его вызова (событие OnClick), ибо зачем нам в другие моменты эта информация?
procedure TForm1.miCheckBoxClick(Sender: TObject);
var
Item: TMenuItem;
i: Integer;
begin
// очищаем список
for i := TMenuItem(Sender).Count - 1 downto 2 do
TMenuItem(Sender).Delete(i);
// Формируем свежий вариант меню по текущему списку СhескВох'ов
for i := 0 to PanelTest.ControlCount - 1 do
if (PanelTest.Controls[i] is TCheckBox) then
begin
Item := NewItem(TCheckBox(PanelTest.Controls[i]).Caption, 0, False, True,
miCheckedClick, 0, '');
TMenuItem(Sender).Add(Item);
Item.Tag := LongInt(PanelTest.Controls[i]);
end;
end;
Не ищите функцию NewItem в проекте, ее там нет. Это функция из VCL модуля Menus. Посмотрите его, найдете еще много-много интересного.
Вообще читайте исходники почаще. Это самый лучший учебный материал.
Итак, для каждого TCheckBox на нашей панели создаем новый пункт в меню и... вот оно! Item.Tag := LongInt(PanelTest.Controls[i]);Приведение типов к LongInt необходимо для компилятора, иначе он не разрешит нам это присваивание. В Delphi класс сам по себе является ссылкой, то есть PanelTest.Controls[i] содержит не сам компонент, а его адрес. Свойству Tag присваем адрес соответствующего компонента. Собственно и все. Вы обратили внимание на то, что мы сразу при создании пункта меню привязали к нему обработчик на событие OnClick? Вот этот обработчик:
procedure TForm1.miCheckedClick(Sender: TObject);
var
i: Integer;
begin
//Функция Ptr конвертирует 4 байта в тип Pointer.
//function Ptr(Address: Integer): Pointer
if TMenuItem(Sender).Tag = 0 then
begin
for i := 0 to PanelTest.ControlCount - 1 do
if PanelTest.Controls[i] is TCheckBox then
TCheckBox(PanelTest.Controls[i]).Checked := True
end
else
TCheckBox(Ptr(TMenuItem(Sender).Tag)).Checked := True;
end;
IF TMenuItem(Sender).Tag = 0 - проверка на пункт "Отметить все", здесь мы пользуемся старым способом перебора массива PanelTest.Controls.
А вот если это пункт для единичного ТСhескВох'а ... используем свойство Tag иным манером :о)
В принципе неплохо бы еще и проверять, что возвращает Ptr. Примерно так
var
TagPtr: Ponter;
...
TagPtr := Ptr(TMenuItem(Sender).Tag);
if TagPtr <> nil then
if TagPtr is TCheckBox then
TCheckBox(TagPtr).Checked := True;
Это общий случай - защита от ошибочных ситуаций.
В нашем примере в TMenuItem(Sender).Tag ничего иного быть и не может, так что проверять не обязательно...
Хотя, с проверкой надежнее :о)
Что бы еще такого придумать интересненького?..
А вот, например, давайте для каждой кнопки менять в run-time текст выводимого ею сообщения (помните нашу процедуру OnClickButton ? ).
Где хранить этот текст для каждой кнопки? Создать массив или динамический список строк и синхронизировать его со списком компонент?
Можно конечно... но зачем так сложно?
Ведь в этом замечательном свойстве Tag можно хранить и строки.
Изменим немного код создания компонент:
var
...
MessTag: PChar;
...
if TypeClass = TButton then
begin
TButton(New).OnClick := OnClickButton;
// выделяем память под строку
GetMem(MessTag, Length('Нажата кнопка №' + IntToStr(No)) + 1);
StrCopy(MessTag, PChar('Нажата кнопка №' + IntToStr(No)));
TButton(New).Tag := LongInt(MessTag);
end
else
New.Tag := 1;
...
И процедура OnClickButton изменится соответствующим образом: procedure TForm1.OnClickButton(Sender: TObject); var Value: string; begin Value := PChar(Ptr(TButton(Sender).Tag)); MessageDlg(Value, mtInformation, [mbOk], 0); end;Изменение строки привяжем к меню pmComponent, добавив туда еще один пункт. Обратите внимание, что пункт этот имеет смысл только для кнопок. Поэтому перед показом popup-меню, на событие OnPopup, делаем соответствующий пункт видимым или невидимым.
procedure TForm1.pmComponentPopup(Sender: TObject); begin pmTag1.Checked := TPopupMenu(Sender).PopupComponent.Tag = 1; pmTag2.Checked := not pmTag1.Checked; pmLine.Visible := TPopupMenu(Sender).PopupComponent is TButton; pmNewMessage.Visible := pmLine.Visible; end;Да! Перед тем, как записать адрес новой строки в Tag, освободим память от предыдущей, ведь она нам больше не нужна.
procedure TForm1.pmNewMessageClick(Sender: TObject);
var
Value: string;
MessTag: PChar;
begin
MessTag := PChar(TButton(pmComponent.PopupComponent).Tag);
Value := MessTag;
if InputQuery('Сменить сообщение',
'Для кнопки ' + TButton(pmComponent.PopupComponent).Name, Value) then
begin
FreeMem(MessTag);
GetMem(MessTag, Length(Value) + 1);
StrCopy(MessTag, PChar(Value));
TButton(pmComponent.PopupComponent).Tag := LongInt(MessTag);
end;
end;
При удалении компонента нам теперь надо учесть этот момент - если удаляется кнопка, надо удалить ее строку.
Движение - это жизнь
Напоследок, для полного оживления картины, сделаем наши компоненты подвижными. Разрешим им свободно перемещаться по панели, таская их мышкой.
Это будет не drag & drop, а совершенно обычное перемещение, как в режиме design-time. Для его реализации нам понадобится обработать самим два события для передвигаемого компонента: OnMouseDown - если нажата левая кнопка мышки, запомним ее текущее положение в переменной DragPoint и и будем считать эти координаты точкой отсчета. OnMouseMove - если нажата левая кнопка мышки, передвигаем компонент по новым координатам, сдвигая его так же, как сдвинулась мышка относительно нашей точки отсчета. Тем самым компонент будет плавно передвигаться вслед за мышкой. Пишем эти процедуры сами, список параметров должен соответствовать описаниям этих событий ( для этого снова к Неlр'у ). В принципе можно посмотреть, как создает обработчики этих событий сама Delphi и оттуда переписать параметры.
procedure TForm1.ControlMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
DragPoint := Point(X, Y);
end;
procedure TForm1.ControlMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (ssLeft in Shift) then
begin
TControl(Sender).Left := TControl(Sender).Left + x - DragPoint.X;
TControl(Sender).Top := TControl(Sender).Top + y - DragPoint.y;
end;
end;
Естественно, что при создании компонент необходимо добавить назначение этих процедур в качестве обработчиков нужных нам событий. ... New.Top := Point.y; New.Left := Point.x; TLabel(New).OnMouseDown := ControlMouseDown; TLabel(New).OnMouseMove := ControlMouseMove; ...Ну, а теперь, мышку в руки и смело вперед!
Итого...
В статье я довольно подробно разобрала некоторые вопросы, некоторые затронула вскользь. Не дала прямого ответа на вопрос, почему нельзя писать "(Edit + IntToStr(i)).Text:=''". В самом проекте оставлено несколько забавных нюансов, от которых бы надо избавиться (например, при перетаскивании компонентов срабатывает событие OnClick, что делает кнопки особенно навязчивыми). Это не от лени, в этом была стратегическая задумка... Никакой Интернет со всеми его конференциями, статьями и примерами никогда не заменит программисту собственного опыта. Только то, что добыто (разобрано и понято) своими силами, запоминается надолго и приносит пользу. Опыта надо набираться обязательно. Читайте help, ищите ответ в исходниках, экспериментируйте с проектом сколько душе угодно, но только обязательно сами... Удачи! Скачать проект (откомпилировано в Delphi 5) : Исходники + Exe-файл RunTime.zip (200 K) Только исходники RunTime_Source.zip (8 K)Менять главную форму во время выполнения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure SetAsMainForm(aForm:TForm);
var
P:Pointer;
begin
P := @Application.Mainform;
Pointer(P^) := aForm;
end;
{************************************}
// Example of Usage:
{
Question:
If my application is main.exe and the main form is form1, form1 displays
when it runs. I would like to display other forms based on the parameter
passed.
main.exe param1 will display form2 as the first form
main.exe param2 with display form3 as the first form
Answer:
}
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2},
Unit3 in 'Unit3.pas' {Form3};
{$R *.res}
procedure SetAsMainForm(aForm:TForm);
var
P:Pointer;
begin
P := @Application.Mainform;
Pointer(P^) := aForm;
end;
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.CreateForm(TForm3, Form3);
if Paramstr(1) = 'Param1' then
SetAsMainForm(Form2);
if Paramstr(1) = 'Param2' then
SetAsMainForm(Form3);
Application.Run;
end.
Имя свойства в течение выполнения программы.
| Если бы строители строили здания так же, как программисты пишут программы, первый залетевший дятел разрушил бы цивилизацию. |
If ( Sender is TLabel ) then.. |
Или, если вам заранее известен тип объекта, то можно так:
TLabel( Sender ).Caption |
Чтобы получить доступ к имени, воспользуйтесь одним из приведенных ниже примеров. Пример:
formname:=(myform as tobject).name; |
Или.
formname:=(myform as tcontrol).name; |
Если вы используете свойство ClassName подобно этому:
with Sender as TForm do Label1.Caption := copy(ClassName,2,length(ClassName)-1); |
По крайней мере, от этого вы можете защититься сами, заключив в скобки вызов с приведением типа, и сделав предварительно простую проверку на нужный тип:
If Sender is TForm then Label1.Caption := (Sender as TForm).Name ; |
Это в случае, если вы пытаетесь выполнить следующее:
Label1.Caption := Form1.Name ; |
Это целый сонм проблем. Я читал, что при исполнении этого кода или не доступно свойство Form (во время выполнения программы), или, что чаще, возникает заблуждение относительно инициализируемого класса (вернее, его имени, Form1). Если вы тщательно читали документацию по Delphi, то наверняка заметили тот факт, что установка свойства в Инспекторе Объектов (Object Inspector) НЕ означает его автоматическую установку во время выполнения программы. Чтобы с честью выбраться из этой ситуации, необходимо явно установить свойство (в нашем случае свойство .Name) в методе фомы .Create. Это может выглядеть приблизительно так:
procedure TForm1.Create( Sender : TObject ) ; begin Form1.Name := 'Form1' ; end ; procedure TForm1.Button1Click( Sender : TObject ) ; begin Label1.Caption := Form1.Name ; end ; |
var TC: TComponent; begin TC := label1.Owner; label1.Caption := TC.ClassName; end; |
К своей форме я добавил кнопку, и в обработчике нажатия на нее я записал следующий код:
name := 'AName'; |
Затем, после щелчка на кнопке, я мог бы щелкнуть на форме, и заголовок метки изменился бы на 'AName'. Решением может служить определение свойства Name в обработчике события создания формы. Т.е., если вы назвали форму именем MyForm, то в обработчике события OnCreate вы должны написать следующее:
name := 'MyForm'; |
Это решит вашу проблему, а я закругляюсь.
Заменить элемент управления во время выполнения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
(* Neue Komponente mit eigenen Eigenschaften ersetzt erst
zur Programmlaufzeit das Verhalten von TPanel *)
(* The new component replace at runtime TPanel *)
TExtPanel = class(TCustomControl)
protected
procedure WMSize(var Message: TWMSize); message WM_Size;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TExtPanel.WMSize;
begin
Caption := 'Width: ' + IntToStr(Width) +
', Height: ' + IntToStr(Height);
end;
procedure ReplaceParentClass(DelphiClass,
OldParent,
NewParent: TClass);
var
aClassPointer: ^Byte;
pVCL, pNew: ^Pointer;
dwProtect: DWORD;
begin
if Assigned(NewParent) then
if Assigned(DelphiClass) then
begin
// ClassParent-Zeiger suchen
//Search ClassParent Pointer
while (DelphiClass.ClassParent <> OldParent) do
begin
with DelphiClass do
begin
if (ClassParent = nil) or
(ClassParent = NewParent) then Exit;
// DelphiClass.ClassParent zuweisen
// Set DelphiClass.ClassParent
DelphiClass := ClassParent;
end
end;
// Parent-Zeiger der originalen VCL-Klasse suchen
// Search Parent-Pointer of original VCL-Class
aClassPointer := Pointer(DelphiClass);
Inc(aClassPointer, vmtParent);
pVCL := Pointer(aClassPointer);
aClassPointer := Pointer(NewParent);
Inc(aClassPointer, vmtSelfPtr);
pNew := Pointer(aClassPointer);
// Schreibberechtigung holen, Zeigerwerte tauschen
// Set write permission, swap pointers
VirtualProtect(pVCL, SizeOf(Pointer), PAGE_READWRITE, @dwProtect);
pVCL^ := pNEW;
// alte Zugriffsrechte aktivieren
// avtivate old access right
VirtualProtect(pVCL, SizeOf(Pointer), dwProtect, @dwProtect);
end
end;
initialization
ReplaceParentClass(TPanel, TCustomControl, TExtPanel);
finalization
ReplaceParentClass(TPanel, TExtPanel, TCustomControl);
end.
Назначение события во время выполнения программы.
Автор: Mike Scott Вот процедура, назначающая событие компонента обработчику события другого (или того же самого) компонента, где во время выполнения программа само событие и его обработчик заданы в виде строки. В случае неверных имен события или его обработчика процедура возбуждает исключительную ситуацию. Вы можете "очищать" событие, передавая компоненту NIL с обработчиком или нулевое имя самого обработчика. Для демонстрации того, как это можно использовать, я включил в пример пару маленьких событий для кнопок. Во время работы, вы могли бы потестировать это с парой областей редактирования, имеющих другой тип и другие имена обработчиков.Почуствуйте мощь RTTI (runtime type information)!
procedure SetEvent(ComponentWithEvent: TComponent;
const Event: string;
ComponentWithHandler: TComponent;
const Handler: string);
var
PropInfo: PPropInfo;
Method: TMethod;
begin
PropInfo := GetPropInfo(ComponentWithEvent.ClassInfo, Event);
if PropInfo = nil then
raise Exception.CreateFmt('Событие %s не найдено в классе %s',
[Event, ComponentWithEvent.ClassName]);
Method.Code := nil;
if Assigned(ComponentWithHandler) and (Handler <> '') then
begin
Method.Code := ComponentWithHandler.MethodAddress(Handler);
if Method.Code = nil then
raise Exception.CreateFmt('Класс %s не имеет метода с именем %s',
[ComponentWithHandler.ClassName,
Handler]);
end;
Method.Data := ComponentWithHandler;
SetMethodProp(ComponentWithEvent, PropInfo, Method);
end;
{ примеры, показывающие как использовать SetEvent }
procedure TForm1.SetBtnClick(Sender: TObject);
begin
SetEvent(MenuItem, 'OnClick', Self, 'Test1Click');
end;
procedure TForm1.ClearBtnClick(Sender: TObject);
begin
SetEvent(MenuItem, 'OnClick', nil, '');
end;
|
Пример реализации резиновой нити.
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, ComCtrls;
type
TMainForm = class(TForm)
ColorDlg: TColorDialog;
StatusBar: TStatusBar;
Timer: TTimer;
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure TimerTimer(Sender: TObject);
private
MouseRect: TRect;
IsDown: Boolean;
RectColor: TColor;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
with MouseRect do
begin
IsDown := True;
Left := X;
Top := Y;
Right := X;
Bottom := Y;
Canvas.Pen.Color := RectColor;
end;
if (Button = mbRight) and ColorDlg.Execute
then RectColor := ColorDlg.Color;
end;
procedure TMainForm.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
IsDown := False;
Canvas.Pen.Color := Color;
with MouseRect do
Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right, Bottom),
Point(Left, Bottom), Point(Left, Top)]);
with StatusBar do
begin
Panels[4].Text := '';
Panels[5].Text := '';
end;
end;
procedure TMainForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
with StatusBar do
begin
Panels[2].Text := 'X: ' + IntToStr(X);
Panels[3].Text := 'Y: ' + IntToStr(Y);
end;
if not IsDown then Exit;
Canvas.Pen.Color := Color;
with mouserect do
begin
Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right, Bottom),
Point(Left, Bottom), Point(Left, Top)]);
Right := X;
Bottom := Y;
Canvas.Pen.Color := RectColor;
Canvas.Polyline([Point(Left, Top), Point(Right, Top), Point(Right, Bottom),
Point(Left, Bottom), Point(Left, Top)]);
end;
with StatusBar do
begin
Panels[4].Text := 'Ширина: ' + IntToStr(Abs(MouseRect.Right - MouseRect.Left));
Panels[5].Text := 'Высота: ' + IntToStr(Abs(MouseRect.Bottom - MouseRect.Top));
end;
end;
procedure TMainForm.TimerTimer(Sender: TObject);
begin
with StatusBar do
begin
Panels[0].Text := 'Дата: ' + DateToStr(Now);
Panels[1].Text := 'Время: ' + TimeToStr(Now);
end;
end;
end.
Скачать весь проект
Использование функций из RunDll32.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
Call functions from Rundll always like that: Aufruf von Funktionen aus Rundll stets nach dem Schema aufrufen: WinExec(PChar('rundll32 ...'),SW_SHOWNORMAL); where rundll32... should be replaced by one of the functions below. wobei rundll32... nаtьг1iсh durch eine der unten stehenden Aufrufe ersetzt werden muss. * Shutdown Windows: Windows beenden: Rundll32.exe user,ExitWindows * Install a Screensaver: Installation eines Bildschirmschoners: rundll32.exe desk.cpl,InstallScreenSaver c:\windows\system\ssaver.scr * Call the Standard newsreader: Standard Newsreader aufrufen: rundll32.exe url.dll,NewsProtocolHandler borland.public.delphi.objectpascal * Call the Standard Mailprogram: Standard Mailprogramm aufrufen: rundll32.exe url.dll,MailToProtocolHandler info@swissdelphicenter.ch * Make a Telnet Connection: Eine Telnetverbindung aufbauen: rundll32.exe url.dll,TelnetProtocolHandler 172.134.77.119 * Dialog: Copy Disk: Dialog: Diskette kopieren aufrufen: rundll32.exe diskcopy.dll,DiskCopyRunDll * Dialog: Format Disk: Dialog: Diskette formatieren: rundll32.exe shell32.dll,SHFormatDrive * Call a Connection with a configured Connection: Verbinden mit Dialog einer konfigurierten Verbindung aufrufen: rundll32.exe rnaui.dll,RnaDial Internetzugang * Call Assistants of a Ras-Connection without previous Explanation: Assistenten fьг eine DFЬ-Verbindung ohne vorherige Егк1дгиng aufrufen: rundll32.exe rnaui.dll,RnaWizard @1 * The same with Explanations: Das selbe mit Егк1дгиng: rundll32.exe rnaui.dll,RnaWizard * Print a Testpage on a printer: Eine Testseite auf dem Drucker ausdrucken: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL PrintTestPage * Call Assistants to install a printer: Assistenten zur Installation eines Druckers aufrufen: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter * Call Add/Remove Software: Eigenschaften Software Нinzиfьgеn/Еntfеrnеn aufrufen: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl * Show Properties for Modems: Eigenschaften fьг Modems anzeigen: rundll32.exe shell32.dll,Control_RunDLL modem.cpl * Call Assistants to configure a Modem: Assistent zum Einrichten eines neuen Modems aufrufen: rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add * Show the Network configuration: Netzwerkkonfiguration anzeigen: rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl * Assistant to install hardware Assistenten zur Hardwareinstallation aufrufen: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1 * Properties for the Display: Eigenschften von Anzeige aufrufen: -Background -Hintergrund: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0 -Screensaver -Bildschirmschoner rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1 -Display -Darstellung rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2 -Settings -Einstellungen rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3 * Open a file with its associated application : eine Datei mit assoziierter Anwendung aufrufen: rundll32.exe url.dll,FileProtocolHandler mydocument.docКоличество запущенных копий программы.
| Землетрясение в Штатах сорвало выступление Билла Гейтса. Наконец-то старый спор всех пользователей компьютеров: "кто главнее и круче - Билл или Бог?" полностью разрешен в пользу последнего. Билли посрамлен. |
program CrtApp;
uses
SysUtils, WinTypes, WinProcs, WinCrt;
var
NumInstances, SavePrevInst: word;
procedure GetInstanceData(hInst, Offset, Size: Word); far; external 'KERNEL';
begin
SavePrevInst := hPrevInst;
NumInstances := 0;
while hPrevInst <> 0 do
begin
GetInstanceData(hPrevInst, Ofs(hPrevInst), SizeOf(hPrevInst));
Inc(NumInstances);
end;
Writeln('Уже запущено ', NumInstances, ' копий программы');
hPrevInst := SavePrevInst;
end.
|
Создание компонент во время выполнения программы и обработка их событий.
| "Се, грядет с облаками, и узрит Его всякое око. И возрыдают перед Ним все племена земные. Ей, аминь." - так или примерно так древний прозаик Иоанн по прозвищу Богослов пророчествует в своих "Откровениях" о грядущем ужасном WINDOWS. |
В данном примере по созданию окна создаются кнопки, по нажатию каждой выводится сообщение, в котором значится заголовок кнопки, вызвавшей событие. Сначала вынесите на форму компонент TButton и по его нажатию напишите такой код:
procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage((Sender as TButton).Caption); end; |
А основное действие свершится по созданию окна:
procedure TForm1.FormCreate(Sender: TObject);
var
Button1: TButton;
i: integer;
begin
for i:=0 to 4 do
begin
Button1:=TButton.Create(Form1);
with Button1 do
begin
Parent := Form1;
Caption := 'Кнопка ' + IntToStr(i + 1);
Height := 25;
Width := 75;
Top := i * 25 + 25;
Left := 50;
OnClick := Button1Click;
end;
end;
end;
|
Как перемещать компоненты во время работы программы.
| Фидошник едет в троллейбусе и орет: - Сакс, сакс!!!! Бабулька рядом говорит ему: - Hy что все сакс, да сакс... Ты еще какие-нибудь слова знаешь? - Да, Windows... - А что это? - САКС!!!!! |
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure Button1MouseUp(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer);
private
{Private declarations}
public
{Public declarations}
MouseDownPoint : TPoint;
Moving : bool;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if ssCtrl in Shift then
begin
SetCapture(Button1.Handle);
Moving := true;
MouseDownPoint.X := x;
MouseDownPoint.Y := Y;
end;
end;
procedure TForm1.Button1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Moving then
begin
Button1.Left := Button1.Left - (MouseDownPoint.x - x);
Button1.Top := Button1.Top - (MouseDownPoint.y - y);
end;
end;
procedure TForm1.Button1MouseUp(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Moving then
begin
ReleaseCapture;
Moving := false;
Button1.Left := Button1.Left - (MouseDownPoint.x - x);
Button1.Top := Button1.Top - (MouseDownPoint.y - y);
end;
end;
|
Показ свойств во время выполнения программы.
| Автор: Thomas von Stetten |
| Новость: фирма MICROSOFT, для увеличения надёжности работы своего сервера, установила на него операционную систему UNIX. |
Я написал компонент-отладчик, выводящий в дереве все компоненты. Попробуйте этот код. Вызывайте функцию DisplayProperties как показано ниже:
DisplayProperties(Form1, {Вы можете использовать любой компонент}
Outline1.Lines, {Допускается любой TStrings-объект}
0); {0 - "стартовый", корневой уровень}
|
DisplayProperties(AObj: TObject; AList: TStrings; iIndentLevel: Integer);
var
Indent: string;
ATypeInfo: PTypeInfo;
ATypeData: PTypeData;
APropTypeData: PTypeData;
APropInfo: PPropInfo;
APropList: PPropList;
iProp: Integer;
iCnt: Integer;
iCntProperties: SmallInt;
ASecondObj: TObject;
procedure AddLine(sLine: string);
begin
AList.Add(Indent + #160 + IntToStr(iProp) + ': ' + APropInfo^.Name
+ ' (' + APropInfo^.PropType^.Name + ')' + sLine);
end;
begin
try
Indent := GetIndentSpace(iIndentLevel);
ATypeInfo := AObj.ClassInfo;
ATypeData := GetTypeData(ATypeInfo);
iCntProperties := ATypeData^.PropCount;
GetMem(APropList, SizeOf(TPropInfo) * iCntProperties);
GetPropInfos(ATypeInfo, APropList);
for iProp := 0 to ATypeData^.PropCount - 1 do
begin
APropInfo := APropList^[iProp];
case APropInfo^.PropType^.Kind of
tkInteger:
AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)));
tkChar:
AddLine(' := ' + chr(GetOrdProp(AObj, APropInfo)));
tkEnumeration:
begin
APropTypeData := GetTypeData(APropInfo^.PropType);
if APropTypeData^.BaseType^.Name <> APropInfo^.PropType^.Name then
AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)))
else
AddLine(' := ' + APropTypeData^.NameList);
end;
tkFloat:
AddLine(' := ' + FloatToStr(GetFloatProp(AObj, APropInfo)));
tkString:
AddLine(' := "' + GetStrProp(AObj, APropInfo) + '"');
tkSet:
begin
AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)));
end;
tkClass:
begin
ASecondObj := TObject(GetOrdProp(AObj, APropInfo));
if ASecondObj = nil then
AddLine(' := NIL')
else
begin
AddLine('');
DisplayProperties(ASecondObj, AList, iIndentLevel + 1);
end;
end;
tkMethod:
begin
AddLine('');
end;
else
AddLine(' := >>НЕИЗВЕСТНО<<');
end;
end;
except {Выводим исключение и продолжаем дальше}
on e: Exception do
ShowMessage(e.Message);
end;
FreeMem(APropList, SizeOf(TPropInfo) * iCntProperties);
end;
function GetIndentSpace(iIndentLevel: Integer): string;
var
iCnt: Integer;
begin
Result := '';
for iCnt := 0 to iIndentLevel - 1 do
Result := Result + #9;
end;
|
Запуск программы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ Open a file or starts a programm (without parameters) }
procedure OpenFile(FileName: string);
var
c: array[0..800] of Char;
begin
StrPCopy(c, FileName);
ShellExecute(Application.Handle, 'open', c, nil, nil, SW_NORMAL);
end;
{ Starts a programm with commandline parameters }
procedure OpenProgram(prog, params: string);
var
c, p: array[0..800] of Char;
begin
StrPCopy(c, prog);
StrPCopy(p, params);
ShellExecute(Application.Handle, 'open', c, p, nil, SW_NORMAL);
end;
{ Starts a program and wait until its terminated:
WindowState is of the SW_xxx constants }
function ExecAndWait(const FileName, Params: string;
WindowState: Word): Boolean;
var
SUInfo: TStartupInfo;
ProcInfo: TProcessInformation;
CmdLine: string;
begin
{ Enclose filename in quotes to take care of
long filenames with spaces. }
CmdLine := '"' + FileName + '"' + Params;
FillChar(SUInfo, SizeOf(SUInfo), #0);
with SUInfo do
begin
cb := SizeOf(SUInfo);
dwFlags := STARTF_USESHOWWINDOW;
wShowWindow := WindowState;
end;
Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
CREATE_NEW_CONSOLE or
NORMAL_PRIORITY_CLASS, nil,
PChar(ExtractFilePath(FileName)),
SUInfo, ProcInfo);
{ Wait for it to finish. }
if Result then
WaitForSingleObject(ProcInfo.hProcess, INFINITE);
end;
{ Execute a complete shell command line and waits until terminated. }
function ExecCmdLineAndWait(const CmdLine: string;
WindowState: Word): Boolean;
var
SUInfo: TStartupInfo;
ProcInfo: TProcessInformation;
begin
{ Enclose filename in quotes to take care of
long filenames with spaces. }
FillChar(SUInfo, SizeOf(SUInfo), #0);
with SUInfo do
begin
cb := SizeOf(SUInfo);
dwFlags := STARTF_USESHOWWINDOW;
wShowWindow := WindowState;
end;
Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
CREATE_NEW_CONSOLE or
NORMAL_PRIORITY_CLASS, nil,
nil {PChar(ExtractFilePath(Filename))},
SUInfo, ProcInfo);
{ Wait for it to finish. }
if Result then
WaitForSingleObject(ProcInfo.hProcess, INFINITE);
end;
{ Execute a complete shell command line without waiting. }
function OpenCmdLine(const CmdLine: string;
WindowState: Word): Boolean;
var
SUInfo: TStartupInfo;
ProcInfo: TProcessInformation;
begin
{ Enclose filename in quotes to take care of
long filenames with spaces. }
FillChar(SUInfo, SizeOf(SUInfo), #0);
with SUInfo do
begin
cb := SizeOf(SUInfo);
dwFlags := STARTF_USESHOWWINDOW;
wShowWindow := WindowState;
end;
Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
CREATE_NEW_CONSOLE or
NORMAL_PRIORITY_CLASS, nil,
nil {PChar(ExtractFilePath(Filename))},
SUInfo, ProcInfo);
end;
Как запустить апплет панели управления.

Запустить аплет Панели управления можно вызвав функцию WinExec, для выполнения файла control.exe, которому передано имя аплета. Обычно аплеты панели управления расположены в каталоге System Windows и имеют расширение .cpl.
procedure TForm1.Button1Click(Sender: TObject);
begin
WinExec('C:\WINDOWS\CONTROL.EXE TIMEDATE.CPL', sw_ShowNormal);
end;
|
Запускать можете следующие апплеты:
Desk.cpl - свойства экрана Inetcpl.cpl - свойства Internet Intl.cpl - свойства "Язык и Стандарты" Joy.cpl - игровые устройства Mmsys.cpl - свойства мультимедиа Modem.cpl - свойства модемы Netcpl.cpl - сеть Odbccp32.cpl - ODBC Data Source Administrator Password.cpl - свойства пароли Powercfg.cpl - свойства "Управление электропитанием" Access.cpl - свойства "Специальные возможности" Sticpl.cpl - свойства "Сканеры м камеры" Sysdm.cpl - свойства системы Telephon.cpl - параметры набора номера Appwiz.cpl - установка и удаление программ Main.cpl - мышь Timedate.cpl - свойства "Дата и время" dtccfg.cpl - настройка клиента MS DTC Mlcfg32.cpl - свойства Microsoft Outlook Findfast.cpl - Microsoft FrontPage bdeadmin.cpl - BDE Administrator ibmgr.cpl - Interbase managerОткрыть меню кнопки ПУСК или запустить диалог запуска программ.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// Show the Startmenu:
keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), 0, 0);
keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), KEYEVENTF_KEYUP, 0)
//Show the Run-Dialog:
keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), 0, 0);
keybd_event(Ord('R'), MapVirtualKey(Ord('R'), 0), 0, 0);
keybd_event(Ord('R'), MapVirtualKey(Ord('R'), 0), KEYEVENTF_KEYUP, 0);
keybd_event(VK_LWIN, MapVirtualKey(VK_LWIN, 0), KEYEVENTF_KEYUP, 0)
Как запускать внешнюю программу сразу с высоким приоритетом.
Автор: Shadow Предлагаю Вашему вниманию пример, который изменяет приоритет приложения. Изменение приоритета следует использовать с осторожностью, так как присвоение слишком высокого приоритета может привести к медленной работе остальных программ и системы в целом. См. Win32 help for SetThreadPriority() function.Пример:
procedure TForm1.Button1Click(Sender: TObject); var ProcessID: DWORD; ProcessHandle: THandle; ThreadHandle: THandle; begin ProcessID := GetCurrentProcessID; ProcessHandle := OpenProcess(PROCESS_SET_INFORMATION, false, ProcessID); SetPriorityClass(ProcessHandle, REALTIME_PRIORITY_CLASS); ThreadHandle := GetCurrentThread; SetThreadPriority(ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); end; |
Запустить консольное приложение и получить его стандартный вывод.
| - Что делать, если мышь уже дошла до края коврика, а курсор еще не дошел до края экрана? Возможные ответы: - перестать пользоватся мышью - выбросить такой маленький коврик - купить новый "экран" - осторожно придерживая большим пальцем курсор мыши, двигать монитор - прогнать драйвер мыши под отладчиком - Windows СУКС, этот Билли даже мышь нормальную не может сделать, Linux forever! - запустить антивирус! - да у тебя просто руки кривые! - сколько раз можно задавать один и тот же вопрос, предлагаю внести его в FAQ - Е...! про мышей - это не к нам |
{
This function runs a program (console or batch) and adds its output
to Memo1
}
{....}
private
function RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean;
{....}
function TForm1.RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean;
var
start: TStartupInfo;
procInfo: TProcessInformation;
tmpName: string;
tmp: Windows.THandle;
tmpSec: TSecurityAttributes;
res: TStringList;
return: Cardinal;
begin
Result := False;
try
{ Setze ein Temporares File }
{ Set a temporary file }
tmpName := 'Test.tmp';
FillChar(tmpSec, SizeOf(tmpSec), #0);
tmpSec.nLength := SizeOf(tmpSec);
tmpSec.bInheritHandle := True;
tmp := Windows.CreateFile(PChar(tmpName),
Generic_Write, File_Share_Write,
@tmpSec, Create_Always, File_Attribute_Normal, 0);
try
FillChar(start, SizeOf(start), #0);
start.cb := SizeOf(start);
start.hStdOutput := tmp;
start.dwFlags := StartF_UseStdHandles or StartF_UseShowWindow;
start.wShowWindow := SW_Minimize;
{ Starte das Programm }
{ Start the program }
if CreateProcess(nil, PChar(_exeName + ' ' + _cmdLine), nil, nil, True,
0, nil, PChar(_dirName), start, procInfo) then
begin
SetPriorityClass(procInfo.hProcess, Idle_Priority_Class);
WaitForSingleObject(procInfo.hProcess, Infinite);
GetExitCodeProcess(procInfo.hProcess, return);
Result := (return = 0);
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
Windows.CloseHandle(tmp);
{ Die Ausgaben hinzufugen }
{ Add the output }
res := TStringList.Create;
try
res.LoadFromFile(tmpName);
Memo1.Lines.AddStrings(res);
finally
res.Free;
end;
Windows.DeleteFile(PChar(tmpName));
end
else
begin
Application.MessageBox(PChar(SysErrorMessage(GetLastError())),
'RunCaptured Error', MB_OK);
end;
except
Windows.CloseHandle(tmp);
Windows.DeleteFile(PChar(tmpName));
raise;
end;
finally
end;
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
RunCaptured('C:\', 'cmd.exe', '/c dir');
end;
|
Как запустить текущий ScreenSaver.
| В зоопарке ребенок, возбужденно тыча пальцем на клетку с приматами (обезьянами :), кричит: - Мама ! Мама ! Смотри - программисты ! - Почему ты так решил ? - Они как папа ! - не мытые, лохматые и мозоль на попе !!! |
SendMessage(Application.Handle, WM_SYSCOMMAND, SC_SCREENSAVE, 0); |
Как запустить текущий ScreenSaver 2.
| О, Гюльчатай, сними же свой скринсейвер! |
Сначала мы проверяем, установлен ли Screen Saver, если нет - возвращаемся с отрицательным ответом, в противном случае - запускаем его и возвращаем true.
function RunScreenSaver: bool;
var
b: boolean;
begin
result := false;
if SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, @b, 0) <> true then
exit;
if not b then
exit;
PostMessage(GetDesktopWindow, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
result := true;
end;
|
Показать диалог для запуска приложения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
ComObj;
procedure TForm1.FormCreate(Sender: TObject);
var
ShellApplication: Variant;
begin
ShellApplication := CreateOleObject('Shell.Application');
ShellApplication.FileRun;
end;
Показать диалог для запуска приложения 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ This code uses the undocumented RunFileDlg function to show the "run" dialog }
{ Dieser Code verwendet die undokumentierte RunFileDlg Funktion, um den Ausfuhren Dialog anzuzeigen }
// For Win NT
procedure RunFileDlgW(OwnerWnd: HWND; Icon: HICON; lpstrDirectory: PWideChar;
lpstrTitle: PWideChar; lpstrDescription: PWideChar; Flags: Longint); stdcall;
external 'Shell32.dll' Index 61;
// For Win 9x (Win NT to show standard captions )
procedure RunFileDlg(OwnerWnd: HWND; Icon: HICON; lpstrDirectory: PChar;
lpstrTitle: PChar; lpstrDescription: PChar; Flags: Longint); stdcall;
external 'Shell32.dll' Index 61;
const
RFF_NOBROWSE = 1; //Removes the browse button.
RFF_NODEFAULT = 2; // No default item selected.
RFF_CALCDIRECTORY = 4; // Calculates the working directory from the file name.
RFF_NOLABEL = 8; // Removes the edit box label.
RFF_NOSEPARATEMEM = 14; // Removes the Separate Memory Space check box (Windows NT only).
function ShowRunFileDialg(OwnerWnd: HWND; InitialDir, Title, Description: PChar;
flags: Integer; StandardCaptions: Boolean): Boolean;
var
HideBrowseButton: Boolean;
TitleWideChar, InitialDirWideChar, DescriptionWideChar: PWideChar;
Size: Integer;
begin
if (Win32Platform = VER_PLATFORM_WIN32_NT) and not StandardCaptions then
begin
Size := SizeOf(WideChar) * MAX_PATH;
InitialDirWideChar := nil;
TitleWideChar := nil;
DescriptionWideChar := nil;
GetMem(InitialDirWideChar, Size);
GetMem(TitleWideChar, Size);
GetMem(DescriptionWideChar, Size);
StringToWideChar(InitialDir, InitialDirWideChar, MAX_PATH);
StringToWideChar(Title, TitleWideChar, MAX_PATH);
StringToWideChar(Description, DescriptionWideChar, MAX_PATH);
try
RunFileDlgW(OwnerWnd, 0, InitialDirWideChar, TitleWideChar, DescriptionWideChar, Flags);
finally
FreeMem(InitialDirWideChar);
FreeMem(TitleWideChar);
FreeMem(DescriptionWideChar);
end;
end else
RunFileDlg(OwnerWnd, 0, PChar(InitialDir), PChar(Title), PChar(Description), Flags);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowRunFileDialg(FindWindow('Shell_TrayWnd', nil), nil, nil, nil, RFF_NOBROWSE, True);
end;
Как написать DLL, которую можно было-бы выполнить с помощью RunDll, RunDll32.
Автор: Nomadic Вы должны определить в программе вызываемую снаружи функцию. Функция должна быть __stdcall (или WINAPI, что то же самое ;)) и иметь четыре аргумента. Первый - HWND окна, порождаемого rundll32 (можно использовать в качестве оwnеr'а своих dialog bох'ов), второй - HINSTANCE задачи, третий - остаток командной строки (LPCSTR, даже под NT), четвертый - не знаю ;). Например -int __stdcall __declspec(dllexport) Test
(
HWND hWnd,
HINSTANCE hInstance,
LPCSTR lpCmdLine,
DWORD dummy
)
{
MessageBox(hWnd, lpCmdLine, "Command Line", MB_OK);
return 0;
}
Исполняем таким образом - rundll32 test.dll,_Test@16 this is a command lineвыдаст message box со строкой "this is a command line".
На Паскале -
function Test( hWnd: Integer; hInstance: Integer; lpCmdLine: PChar; dummy: Longint ): Integer; stdcall; export; begin Windows.MessageBox(hWnd, lpCmdLine, 'Command Line', MB_OK); Result := 0; end; |
int WINAPI RunDll( HWND hWnd, HINSTANCE hInstance, LPCSTR lpszCmdLine, DWORD dummy ); ...... int WINAPI RunDllW( HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, DWORD dummy ); ......и .def-файл примерно такого вида:
EXPORTS RunDll RunDllA=RunDll RunDllW
То rundll32 становится разборчивой -- под NT вызывает UNICODE-версию. Под 95, разумеется, ANSI.
Запустить на выполнение файл от имени любого пользователя.
Автор: Диюк Анрей.
unit HSAdvApi;
interface
uses
Windows;
procedure MyCreateProcess(ConstCommandLine: string);
function CreateProcessWithLogonW(const lpUsername: PWideChar;
const lpDomain: PWideChar; const lpPassword: PWideChar;
dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
lpCommandLine: PWideChar; dwCreationFlags: DWORD;
lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
lpStartupInfo: PStartupInfo;
lpProcessInfo: PProcessInformation): Boolean; stdcall;
const
LOGON_WITH_PROFILE = $00000001;
LOGON_NETCREDENTIALS_ONLY = $00000002;
LOGON_ZERO_PASSWORD_BUFFER = $80000000;
implementation
uses
SysUtils;
{$WARN SYMBOL_DEPRECATED OFF}
{ ADVAPI32.DLL functions }
type
TCreateProcessWithLogonW =
function(const lpUsername: PWideChar;
const lpDomain: PWideChar; const lpPassword: PWideChar;
dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
lpCommandLine: PWideChar; dwCreationFlags: DWORD;
lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
lpStartupInfo: PStartupInfo;
lpProcessInfo: PProcessInformation): Boolean; stdcall;
const
DllName = 'advapi32.dll';
var
DllHandle: THandle;
_CreateProcessWithLogonW: TCreateProcessWithLogonW;
function InitLib: Boolean;
begin
if DllHandle = 0 then
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin
DllHandle := LoadLibrary(DllName);
if DllHandle <> 0 then
begin
@_CreateProcessWithLogonW := GetProcAddress(DllHandle,
'CreateProcessWithLogonW');
end;
end;
Result := (DllHandle <> 0);
end;
function NotImplementedBool: Boolean;
begin
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
Result := false;
end;
function CreateProcessWithLogonW(const lpUsername: PWideChar;
const lpDomain: PWideChar; const lpPassword: PWideChar;
dwLogonFlags: DWORD; const lpApplicationName: PWideChar;
lpCommandLine: PWideChar; dwCreationFlags: DWORD;
lpEnvironment: Pointer; const lpCurrentDirectory: PWideChar;
lpStartupInfo: PStartupInfo;
lpProcessInfo: PProcessInformation): Boolean; stdcall;
begin
if InitLib and Assigned(_CreateProcessWithLogonW) then
Result := _CreateProcessWithLogonW(lpUsername, lpDomain, lpPassword,
dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags,
lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInfo)
else
Result := NotImplementedBool;
end;
procedure MyCreateProcess(ConstCommandLine: string);
const
UserName: WideString = 'ADMIN';
Password: WideString = 'creyc';
//ConstCommandLine : String = 'MONIC_S.EXE ';
Title: WideString = 'SERVISE';
Domain: WideString = 'SOKAL';
var
MyStartupInfo: STARTUPINFO;
ProcessInfo: PROCESS_INFORMATION;
CommandLine: array[0..512] of WideChar;
begin
FillChar(MyStartupInfo, SizeOf(MyStartupInfo), 0);
MyStartupInfo.cb := SizeOf(MyStartupInfo);
StringToWideChar(ConstCommandLine, CommandLine,
Sizeof(CommandLine) div SizeOf(WideChar));
MyStartupInfo.lpTitle := PWideChar(Title);
if not CreateProcessWithLogonW(PWideChar(UserName), PWideChar(Domain),
PWideChar(Password), LOGON_WITH_PROFILE, nil,
CommandLine, 0, nil, nil, @MyStartupInfo, @ProcessInfo) then
RaiseLastWin32Error()
else
begin
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;
end;
initialization
finalization
if DllHandle <> 0 then
FreeLibrary(DllHandle);
end.
Запустить процесс в защищенной области другого пользователя.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
JwaWinBase; ( http://members.chello.nl/m.vanbrakel2/ )
//...
procedure TForm1.Button1Click(Sender: TObject);
var
si: STARTUPINFOW;
pif: PROCESS_INFORMATION;
res: Bool;
s: string;
begin
//erstmal die StartUpInfoW setzen
//set StartUpInfoW first
si.cb := SizeOf(startupinfow);
si.dwFlags := STARTF_USESHOWWINDOW;
si.wShowWindow := SW_SHOWDEFAULT;
si.lpReserved := nil;
si.lpDesktop := nil;
si.lpTitle := 'Konsole';
// dann CreateProcessWithLogonW аиsfьhгеn...
// run CreateProcessWithLogonW...
res := CreateProcessWithLogonW('Security', 'ArViCor', 'test', LOGON_WITH_PROFILE,
'c:\win2kas\system32\regedt32.exe', nil
, CREATE_DEFAULT_ERROR_MODE, nil, nil, si, pif);
if booltostr(res) = '0' then
begin
//wenn ein Fehler auftritt, soll der Fehlercode ausgegeben werden
//ььег 'net helpmsg ' in der Kommandoeingabeaufforderung
//kann dieser entziffert werden
//if an error occures, show the error-code
//this code can be 'translated' with 'net helpmsg ' on command-prompt
str(GetLastError, s);
ShowMessage('CreateProcessWithLogonResult: ' + booltostr(res) + #10 +
'GetLastError: ' + s);
end;
end;
// Verbesserungen gerne als Mail an mich!
Как можно запустить lnk.
| Автор: Олег Кулабухов |
| Заходит старушка в трамвай, предупреждая всех что у нее в сумке яйца. Проходя мимо молодого человека, задевает его маленький мешочек. ОН: - Ах..., острожнее, тв@ю мать. ОНА, слегка ударяя по мешку ножкой: - А что у тебя там? ОН, ударяя ногой по ее сумке со всей дури и разбивая к чертям собачьим все яйца. - ДУРА! Там же ВИНЧЕСТЕР! |
uses ShellApi;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShellExecute(0, nil, 'C:\WINDOWS\START MENU\DELPHI\Delphi3.lnk',
nil, nil, SW_SHOWNORMAL);
end;
|
Как заставить запускаться из определенной папки.
Автор: Vit WEB-сайт: http://forum.vingrad.ruПриведенный пример кода проверяет из какой папки запущена программа, если она запущена не из корневой - то переносит себя в корень и запускается оттуда.
program Project1;
uses
Forms, classes, windows, Sysutils, ShellApi,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
var
f: textFile;
FileName: string;
begin
if paramstr(1) <> '/runasis' then
begin
CopyFile(PChar(Paramstr(0)), PChar('c:\' + extractFilename(paramstr(0))),
True);
shellexecute(0, 'Open', PChar(extractFilename(paramstr(0))), '/runasis',
'c:\', sw_restore);
FileName := changefileext(paramstr(0), '.bat');
assignFile(f, FileName);
rewrite(f);
writeln(f, ':1');
writeln(f, format('Erase "%s"', [paramstr(0)]));
writeln(f, format('If exist "%s" Goto 1', [paramstr(0)]));
writeln(f, format('Erase "%s"', [FileName]));
closefile(f);
ShellExecute(0, 'Open', PChar(FileName), nil, nil, sw_hide);
end
else
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
Как запустить и остановить сервис (или получить его статус).

| - Почему программисты не заливают в машину 95-й бензин? - Боятся, что повиснет. |
Здесь представлены две функции ServiceStart и ServiceStop, которые показывают, как пользоваться API функциями OpenSCManager, OpenService и т.д.:
function ServiceStart(aMachine, aServiceName: string ): boolean;
// aMachine это UNC путь, либо локальный компьютер если пусто
var
h_manager,h_svc: SC_Handle;
svc_status: TServiceStatus;
Temp: PChar;
dwCheckPoint: DWord;
begin
svc_status.dwCurrentState := 1;
h_manager := OpenSCManager(PChar(aMachine), nil, SC_MANAGER_CONNECT);
if h_manager > 0 then
begin
h_svc := OpenService(h_manager, PChar(aServiceName),
SERVICE_START or SERVICE_QUERY_STATUS);
if h_svc > 0 then
begin
temp := nil;
if (StartService(h_svc,0,temp)) then
if (QueryServiceStatus(h_svc,svc_status)) then
begin
while (SERVICE_RUNNING <> svc_status.dwCurrentState) do
begin
dwCheckPoint := svc_status.dwCheckPoint;
Sleep(svc_status.dwWaitHint);
if (not QueryServiceStatus(h_svc,svc_status)) then
break;
if (svc_status.dwCheckPoint then
begin
// QueryServiceStatus не увеличивает dwCheckPoint
break;
end;
end;
end;
CloseServiceHandle(h_svc);
end;
CloseServiceHandle(h_manager);
end;
Result := SERVICE_RUNNING = svc_status.dwCurrentState;
end;
function ServiceStop(aMachine,aServiceName: string ): boolean;
// aMachine это UNC путь, либо локальный компьютер если пусто
var
h_manager, h_svc: SC_Handle;
svc_status: TServiceStatus;
dwCheckPoint: DWord;
begin
h_manager:=OpenSCManager(PChar(aMachine),nil, SC_MANAGER_CONNECT);
if h_manager > 0 then
begin
h_svc := OpenService(h_manager,PChar(aServiceName),
SERVICE_STOP or SERVICE_QUERY_STATUS);
if h_svc > 0 then
begin
if(ControlService(h_svc,SERVICE_CONTROL_STOP, svc_status))then
begin
if(QueryServiceStatus(h_svc,svc_status))then
begin
while(SERVICE_STOPPED <> svc_status.dwCurrentState)do
begin
dwCheckPoint := svc_status.dwCheckPoint;
Sleep(svc_status.dwWaitHint);
if(not QueryServiceStatus(h_svc,svc_status))then
begin
// couldn't check status
break;
end;
if(svc_status.dwCheckPoint then
break;
end;
end;
end;
CloseServiceHandle(h_svc);
end;
CloseServiceHandle(h_manager);
end;
Result := SERVICE_STOPPED = svc_status.dwCurrentState;
end;
|
Чтобы узнать состояние сервиса, используйте следующую функцию:
function ServiceGetStatus(sMachine, sService: string ): DWord;
var
h_manager, h_service: SC_Handle;
service_status: TServiceStatus;
hStat: DWord;
begin
hStat := 1;
h_manager := OpenSCManager(PChar(sMachine) ,nil, SC_MANAGER_CONNECT);
if h_manager > 0 then
begin
h_svc := OpenService(h_manager,PChar(sService), SERVICE_QUERY_STATUS);
if h_svc > 0 then
begin
if(QueryServiceStatus(h_svc, service_status)) then
hStat := service_status.dwCurrentState;
CloseServiceHandle(h_svc);
end;
CloseServiceHandle(h_manager);
end;
Result := hStat;
end;
|
Она возвращает одну из следующих констант:
SERVICE_STOPPED SERVICE_RUNNING SERVICE_PAUSED SERVICE_START_PENDING SERVICE_STOP_PENDING SERVICE_CONTINUE_PENDING SERVICE_PAUSE_PENDINGВсё что, что Вам нужно, это unit WinSvc!
Другой способ запуска чужого приложения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
libc;
procedure TForm1.Button1Click(Sender: TObject);
var
iPrg: Integer;
begin
//Execute kcalc - A calculator for KDE
iPrg := libc.system('kcalc');
if iPrg = -1 then
ShowMessage('Error executing your program');
end;
Просмотрщик запущенных процессов.
Автор: Василий WEB-сайт: http://www.sources.ru Программа не видна по Ctrl+Alt+Del, и сама оттуда же может спрятать любой из процессов(правда, не все с самого начала "светятся" по Ctrl+Alt+Del) или завершить его. Простой пример для знакомства с ToolHelp32.В исходном коде есть недоработки, например, процедура Delproc получает в качестве параметра строку, затем переводит ее в целочисленный тип(integer), хотя можно передавать сразу число. Заморочка была в проверке числа-индекса на подлинность, а так как я выдрал часть кода из более ранней своей проги, я не стал это менять, а просто подогнал до рабочей версии. Оптимизацией кода вы можете заняться сами по желанию(вы можете, если хотите, а если не хотите, то вы не обязаны, вы посто могли бы... да... :))) Программа не работала в WinNT 4.0, но в Win9x работать должна.
unit main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, tlhelp32, StdCtrls, ComCtrls, Buttons;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Button2: TButton;
Button4: TButton;
Button5: TButton;
StatusBar1: TStatusBar;
Button6: TButton;
procedure Button4Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
private
{ Private declarations }
procedure ListProcesses;
procedure Delproc(numb: string);
public
{ Public declarations }
end;
var
Form1: TForm1;
processID: array[1..50] of integer;
function RegisterServiceProcess(dwProcessID, dwType: integer): integer; stdcall;
external 'kernel32.dll';
implementation
{$R *.DFM}
procedure TForm1.delproc(numb: string);
var
c1: Cardinal;
pe: TProcessEntry32;
s1, s2: string;
x: integer;
begin
x := 0;
try
Strtoint(numb);
except
Statusbar1.SimpleText := 'Invalid number';
exit;
end;
c1 := CreateToolHelp32Snapshot(TH32CS_SnapProcess, 0);
if c1 = INVALID_HANDLE_VALUE then
begin
Statusbar1.SimpleText := 'Process listing failed';
exit;
end;
try
pe.dwSize := sizeof(pe);
if Process32First(c1, pe) then
repeat
inc(x);
s1 := ExtractFileName(pe.szExeFile);
s2 := ExtractFileExt(s1);
Delete(s1, length(s1) + 1 - length(s2), maxInt);
if x = strtoint(numb) then
if terminateprocess(OpenProcess(PROCESS_ALL_ACCESS, false,
pe.th32ProcessID), 1) then
begin
Statusbar1.SimpleText := 'Process ' + s1 + ' terminated.';
end
else
Statusbar1.SimpleText := ('Couldnt terminate process' +
pe.szExeFile);
until not Process32Next(c1, pe);
finally CloseHandle(c1);
end;
end;
procedure Tform1.ListProcesses;
var
c1: Cardinal;
pe: TProcessEntry32;
s1, s2: string;
x: integer;
begin
X := 0;
c1 := CreateToolHelp32Snapshot(TH32CS_SnapProcess, 0);
if c1 = INVALID_HANDLE_VALUE then
begin
Statusbar1.SimpleText := ('Информация о процессах закрыта.');
exit;
end;
try
pe.dwSize := sizeof(pe);
if Process32First(c1, pe) then
repeat
inc(x);
s1 := ExtractFileName(pe.szExeFile);
s2 := ExtractFileExt(s1);
Delete(s1, length(s1) + 1 - length(s2), maxInt);
Listbox1.Items.Add(Inttostr(x) + ' ' + s1 + ' : ' + pe.szExeFile);
ProcessId[x] := pe.th32ProcessID;
//ListBox1.Items.Add(inttostr(pe.th32ProcessID));
until not Process32Next(c1, pe);
finally CloseHandle(c1);
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Enabled := false;
Button5.Enabled := false;
Button6.Enabled := false;
ListProcesses;
if not (csDesigning in ComponentState) then
RegisterServiceProcess(GetCurrentProcessID, 1);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Listbox1.Clear;
ListProcesses;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
p: integer;
begin
//hide
with Listbox1 do
p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
if not (csDesigning in ComponentState) then
RegisterServiceProcess(ProcessID[p], 1);
with Listbox1 do
StatusBar1.SimpleText := (Listbox1.items[itemindex] + ' hidden');
end;
procedure TForm1.Button5Click(Sender: TObject);
var
p: integer;
begin
//show
with Listbox1 do
p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
if not (csDesigning in ComponentState) then
RegisterServiceProcess(ProcessID[p], 0);
with Listbox1 do
StatusBar1.SimpleText := (Listbox1.items[itemindex] + ' shown');
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
Button1.Enabled := true;
Button5.Enabled := true;
Button6.Enabled := true;
end;
procedure TForm1.Button6Click(Sender: TObject);
var
p: integer;
begin
with Listbox1 do
p := Listbox1.Items.IndexOf(Listbox1.items[itemindex]) + 1;
delproc(inttostr(p));
end;
end.
Как узнать, запущен ли процесс в Win9x.
function IsRunning(sName: string): boolean;
var
han: THandle;
ProcStruct: PROCESSENTRY32; // from "tlhelp32" in uses clause
sID: string;
begin
Result := false;
// Get a snapshot of the system
han := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
if han = 0 then
exit;
// Loop thru the processes until we find it or hit the end
ProcStruct.dwSize := sizeof(PROCESSENTRY32);
if Process32First(han, ProcStruct) then
begin
repeat
sID := ExtractFileName(ProcStruct.szExeFile);
// Check only against the portion of the name supplied, ignoring case
if uppercase(copy(sId, 1, length(sName))) = uppercase(sName) then
begin
// Report we found it
Result := true;
Break;
end;
until not Process32Next(han, ProcStruct);
end;
// clean-up
CloseHandle(han);
end;
|
Бегущая строка.

procedure TForm1.Timer1Timer(Sender: TObject);
const
LengthGoString = 10;
Gostring = 'Этот код был взят с проекта Delphi World'+
' Выпуск 2002 - 2003! Этот код б';
// Повторить столько символов - сколько в LengthGoString
const
i: Integer = 1;
begin
Label1.Caption := Copy(GoString, i, LengthGoString);
Inc(i);
if Length(GoString) - LengthGostring < i then
i:=1;
end;
|
Создание компонент в Run-Time.
| Надпись на могиле программера: Причина смерти: Run-time error at 18:12:97 Причина рождения: GPF at: 18:12:97 |
Итак, рассмотрим на первый взгляд сложный вопрос о создании компонентов в Run_time (то есть во время работы программы). Но на самом деле этот вопрос довольно просто решается.
Давайте вместе попробуем написать код, чтобы при нажатии на кнопку на форме появлялось текстовое поле с каким-нибудь текстом. Для этого надо проделать следующие действия: объявить переменную необходимого вам типа компонента, в нашем случае TEdit, затем необходимо описать процедуру Create для нашей переменной, задать обязательное свойство Parent (то есть где появится наш компонент), ну и установить необязательные параметры типа высоты, ширины и т.д. Лучше разбираться на конкретном примере, так что смотрите какой у меня получился код:
procedure TForm1.Button1Click(Sender: TObject); var x: TEdit; // объявляем переменную типа TEdit begin x:=TEdit.create(self);// создаем экземпляр компонента x.parent:=form1;// текстовое поле появится на форме x.left:=10; x.top:=10; x.Width:=250; x.Text:='Delphi World is (:-0) !!!'; end; |
Как вы видите все очень просто. Поупражняйтесь с различными параметрами.
Процессы создания визуальных и невизуальных компонентов несколько отличаются (последний попроще будет). Поскольку Edit мы уже создали (а это как раз визуальный компонент), теперь будем создавать невизуальный компонент, например FontDialog. Поставьте на форму еще кнопку. Обработчик события OnClick может выглядеть так:
procedure TForm1.Button2Click(Sender: TObject); var y: TFontDialog; // объявляем переменную типа TFontDialog begin y:=TFontdialog.Create(self); y.Execute; //только для демонстрации. Показать что работает. end; |
Вы свободно можете изменять любые свойства компонента, созданного во время работы приложения.
Теперь мы попробуем вместе проделать такую вещь: мы отсортируем находящиеся в Memo данные. Для этого поставьте на форму Memo и напишите в ней 4 строки, например:
| Memo4 Memo1 Memo3 Memo2 |
Поскольку само Memo сортировать данные не умеет, то нам придется во время работы программы создать какую-нибудь переменную абстрактного класса TStringList (этот класс сортировать умеет), затем присвоить этому классу строки из Memo, отсортировать их и присвоить их обратно Memo. Поставим еще одну кнопку на форму, которая будет запускать процесс. Код получится примерно таким:
procedure TForm1.Button3Click(Sender: TObject); var t: TStringList; begin t:=TStringList.Create; //создаем t.AddStrings(memo1.lines); //присваиваем переменной t строки из Memo t.Sort; // сортируем memo1.Clear; memo1.Lines.AddStrings(t); // присваиваем memo уже отсортированные строки end; |
Теперь рассмотрим еще один вопрос, касающийся создания компонентов в Run-time. Допустим вам надо создать 20 полей для ввода текста (Edit) и еще десять меток (Label), не будете же вы 20 раз писать одно и тоже для каждого edit'a. В этой ситуации есть очень элегантный выход: воспользоваться массивом компонентов. В общем виде объявление массива компонентов может выглядеть так: имя_переменной:array[нижний_индекс..верхний_индекс] of тип_компонента. Теперь поупражняемся в этом. Создайте новое приложение. На форму поместите только кнопку. Обработчик события OnClick которой у меня получился таким:
procedure TForm1.Button1Click(Sender: TObject);
var
a: array [1..20] of TEdit; // массив элементов Edit
b: array [1..10] of TLabel; //массив меток
i, j: integer;
begin
for i:=1 to 20 do
begin
a[i]:=TEdit.create(self);
a[i].parent:=form1;
a[i].left:=10;
a[i].text:='элемент # '+inttostr(i);
a[i].top:=i*20;
end;
for j:=1 to 10 do
begin
b[j]:=TLabel.create(self);
b[j].parent:=form1;
b[j].left:=200;
b[j].Caption:='элемент # '+inttostr(j);
b[j].top:=j*30;
end;
end;
|
Ну как?! Иногда воспользоваться массивом компонентов очень даже удобно, потому что в массиве обращение к его элементам происходит по индексу, а в обычной ситуации обращение происходит по имени компонента, что не всегда удобно.
Как упаковать базу (DBase, Paradox) в RunTime.
Для dBase:
uses
DbiProcs;
with Table do
begin
OldState := Active;
Close;
Exclusive := True;
Open;
DbiPackTable(DBHandle, Handle, nil, nil, True);
{^ здесь можно добавить check()}
Close;
Exclusive := False;
Active := OldState;
{ при желании можно сохранить закладку }
end;
|
Для Paradox:
DbiDoRestructure(DBHandle, 1, ?, nil, nil, nil, False); { см. dbiProcs.int }
|
Вместо ? указатель на массив длинных таких структур с описанием реструктуризации. Кто делал на Px, я думаю, дадут пример.
Это насчет упаковки db (может что-то и лишнее, что-то можно сделать по-другому, но работает ).
Пример для Paradox:
uses
BDE; // for D3, для D2 непомню (что-то типа DbiProc и еще что-то)
// для примера
tLog: TTable; // таблица юзающая d:\db\log.db
var
TblDesc: CRTblDesc;
rslt: DBIResult;
Dir: string; //имеется в виду huge string т.е. {$H+}
hDb: hDbiDb;
begin
tLog.Active:=False; //деактивируем TTable
SetLength(Dir, dbiMaxNameLen + 1);
DbiGetDirectory(tLog.DBHandle, False, PChar(Dir));
SetLength(Dir, StrLen(PChar(Dir)));
DbiOpenDatabase(nil, nil, dbiReadWrite, dbiOpenExcl,
nil, 0, nil, nil, hDb);
DbiSetDirectory(hDb, PChar(Dir));
FillChar(TblDesc, sizeof(CRTblDesc), 0);
StrPCopy(TblDesc.szTblName, 'd:\db\log.db');
// здесь должно быть полное имя файла
// которое можно:
// а) ввести руками;
// б) вытащить из пропертей таблицы;
// в) вытащить из алиаса;
// г) см. FAQ
StrCopy(TblDesc.szTblType, szParadox);
//BTW тут может и szDBase стоять
TblDesc.bPack := TRUE;
DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False);
DbiCloseDatabase(hDb);
end;
|
Как запустить приложение и подождать пока оно отработает 7.
Автор: RhinoFC WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск программы и ожидание ее завершения.
Функция запускает внешнюю программу, заданную параметром Path, и ждет ее завершения.
Во время выполнения внешней программы, текущее приложение скрывается (
т.к. не может перерисовать главное окно, что некрасиво), поэтому фактически
происходит переключение с текущей программы на внешнюю и,
после завершения внешней программы, обратно.
В качестве внешней программы может выступать любой выполняемый файл (EXE, COM, BAT, SCR).
Зависимости: Windows, Forms
Автор: Евгений Валяев (RhinoFC), rhinofc@sniiggims.ru, ICQ:55263922, Новосибирск
Copyright: RhinoFC
Дата: 5 июня 2002 г.
***************************************************** }
function SwitchToProg(const Path: string): Boolean;
var
SI: TStartupInfo;
PI: TProcessInformation;
ExitCode: Cardinal;
begin
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
Result := CreateProcess(nil, PChar(Path), nil, nil, False, 0, nil, nil,
SI, PI);
if Result then
begin
Application.MainForm.Hide;
while GetExitCodeProcess(PI.hProcess, ExitCode) and
(ExitCode = STILL_ACTIVE) do
; // ждем завершения, пустой цикл
Application.MainForm.Show;
end;
end;
Как запустить приложение и подождать пока оно отработает 8.
Автор: Elena_tark WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Дождаться завершение внешнего приложения
Дожидается завершения внешнего процесса
Зависимости: shellAPI
Автор: elena_tark, elena_tark@freemail.ru, Калуга
Copyright: Собственное
Дата: 1 августа 2003 г.
***************************************************** }
procedure ShellExec(Sender: TObject);
var
ProcInfo: PShellExecuteInfo;
begin
(Sender as TControl).Enabled := False;
GetMem(ProcInfo, SizeOf(ProcInfo^));
with ProcInfo^ do
begin
Wnd := Handle;
cbSize := SizeOf(ProcInfo^);
lpFile := PChar('notepad.exe');
lpParameters := nil;
lpVerb := 'open';
nShow := SW_SHOW;
fMask := SEE_MASK_DOENVSUBST or SEE_MASK_NOCLOSEPROCESS;
end;
try
Win32check(ShellExecuteEx(ProcInfo));
while not Application.Terminated and
(WaitForSingleObject(ProcInfo.hProcess, 100) = WAIT_TIMEOUT) do
Application.ProcessMessages;
finally
if ProcInfo.hProcess <> 0 then
CloseHandle(ProcInfo.hProcess);
Dispose(ProcInfo);
(Sender as TControl).Enabled := True;
end;
end;
Запуск программы и ожидание завершения определенное время.
Автор: Kostas WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск программы и ожидание завершения опр.время
Запускает внешнюю программу и ожидает его завершения определённое время,
если истекает время то, программа продолжает работу не зависимо от
состояния внешней программы (На случай подвисания вн. прог-мы, особенно DOS)
Зависимости: Windows
Автор: Kostas, kostas@tut.by, РБ Гомельская обл. г. Речица
Copyright: вроде delphi.mastak.ru (слегка доработанно)
Дата: 03 мая 2002 г.
***************************************************** }
function ExecAndWait(aCmd: string; WaitTimeOut: cardinal = INFINITE): Cardinal;
var
si: STARTUPINFO;
pi: PROCESS_INFORMATION;
res: BOOL;
r: cardinal;
begin
with si do
begin
cb := sizeof(si);
lpReserved := nil;
lpDesktop := nil;
lpTitle := PChar('External program "' + aCmd + '"');
dwFlags := 0;
cbReserved2 := 0;
lpReserved2 := nil;
end;
res := CreateProcess(nil, PChar(aCmd), nil, nil, FALSE, 0, nil, nil, si, pi);
if res then
WaitForSingleObject(pi.hProcess, WaitTimeOut);
GetExitCodeProcess(pi.hProcess, r);
result := r;
end;
// Пример использования:
procedure Exec(CommandStr, WaitTime);
begin
if ExecAndWait(CommandStr, WaitTime) = WAIT_FAILED then
ShowMessage('Истекло время ожидания внешней программы ' + CommandStr);
end;
Запуск программы и ожидание ее окончания, принудительное завершение если Timeout.
Автор: August WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Запуск программы и ожидание ее окончания, принудительное завершение если timeout
Функция создавалась для запуска и сопровождения приложения в указаном интервале времени.
Я лично для наблюдения за работой приложения включил функцию в нить,
которая (обрывалась) по завершении процесса.
Зависимости: Classes, SysUtils, Windows, strUtils, Consts;
Автор: August, august@torba.com, ICQ:176381214
Copyright: doctorAugust
Дата: 5 февраля 2003 г.
***************************************************** }
function ExecuteFileTm(FileName: string; Params: string; Dsk: boolean; TimeOut:
cardinal): boolean;
var
Res: LongBool;
PIn: PROCESS_INFORMATION;
SII: STARTUPINFO;
RetCode: cardinal;
TmOut: cardinal; //In seconds
i: cardinal;
CreationsFlags: cardinal;
begin
TmOut := TimeOut;
try
if Dsk then
begin
CreationsFlags := CREATE_DEFAULT_ERROR_MODE or NORMAL_PRIORITY_CLASS
or CREATE_UNICODE_ENVIRONMENT;
end
else
begin
CreationsFlags := CREATE_DEFAULT_ERROR_MODE or CREATE_NO_WINDOW
or NORMAL_PRIORITY_CLASS
or CREATE_UNICODE_ENVIRONMENT;
end;
getstartupinfo(SII);
Res := createprocess(
pchar(FileName),
pchar(Params),
nil,
nil,
True,
CreationsFlags,
nil,
pchar(extractfilepath(FileName)),
SII,
PIn);
except
CloseHandle(Pin.hProcess);
ExecuteFileTm := False;
exit;
end;
if not Res then
begin
CloseHandle(Pin.hProcess);
ExecuteFileTm := False;
exit;
end;
i := 0;
RetCode := STILL_ACTIVE;
while (i <= TmOut) and (RetCode = STILL_ACTIVE) do
begin
try
GetExitCodeProcess(Pin.hProcess, RetCode);
except
CloseHandle(Pin.hProcess);
ExecuteFileTm := False;
exit;
end;
Sleep(1000);
inc(i);
end;
if (i > TmOut) and (RetCode = STILL_ACTIVE) then
begin
TerminateProcess(Pin.hProcess, RetCode);
CloseHandle(Pin.hProcess);
// ExecuteFilePrExt:=false;
ExecuteFileTm := false; // ION T
exit;
end;
CloseHandle(Pin.hProcess);
ExecuteFileTm := True;
end;
Пример запуска макроса в MS Word.

| Сдает новый русский компьютер по гарантии. Смотрят - у монитора здоровенная дыра пробита в правом нижнем углу. - Как же это так? - Да вы чё мне продали!!! Я полгорода держу!!! Передо мною вся братва строится!!! А тут какая-то скрепка мне пальцами да у виска крутит!!! |
...
vvWord:= CreateOleObject('Word.Application.8');
vvWord.Application.Visible:=true;
vvWord.Documents.Open( TempFileName );
vvWord.ActiveDocument.SaveAs( FileName, 1 ); // as .DOC
vvWord.Application.Run( 'Macros Name' );
...
|
Русификация консольных приложений в Delphi.
| Автор: Eugene Kasnerik |
| Приказ министра обороны по русификации сообщений WINDOWS. Yes - Есть! No - Никак нет! OK - Так точно! Pause - Можно оправиться и закурить. Stop - Смирно! Continue - Вольно! Abort - Расстрелять! Retry - Кругом! Ignore - Выполнить любой ценой! Login - Стой, кто идет? Password - Предъявите пропуск! Shutdown - Отбой! Access denied - Не положено! Message - Рапорт. Exception - Нештатная ситуация. А вместо слова WINDOW, приказываю использовать - АМБРАЗУРА. |
С периодичностью раз в месяц-полтора конференция RU.DELPHI оглашается стонами на тему “Консоль не поет по-русски”, за которыми стоит вывод текста в консольных приложениях в кодировке OEM (Delphi IDE, как и все GUI, работает в ANSI).
С точки зрения набора символов эти кодовых таблицы не совпадают: позиции символов кириллицы в них различны (отсюда и неприятные эффекты), кроме того, в ANSI присутствуют диакритические символы, которых нет в OEM, но в последней имеются символы псевдографики, незаменимые при изображении таблиц (интересно, это еще кем-то востребовано? На ум приходит только FAR). Впрочем, возможности для вывода текстовой информации у этих таблиц одинаковы, что в нашем случае позволяет говорить о взаимозаменяемости.
Рассмотрим некоторые способы, которыми можно решить возникающие проблемы (три из них встречаются в различных FAQ, последний менее тривиален, но, видимо, в наибольшей степень отвечает задаче).
Работа в OEM-редакторе. Вместо борьбы с выводом «не тех» символов можно пойти иным путем – изначально готовить текст программы (точнее те его части, которые критичны к кодовой таблице) в редакторе, работающем в кодировке OEM. Решение простенькое, но на удивление эффективное, особенно при написании каких-либо локальных утилит, в которых, тем не менее, сильно востребован вывод информации.
К недостаткам можно отнести работу вне привычного IDE с его облегчающими жизнь наворотами (кодирование, компиляция и отладка в одном флаконе), а также определенные сложности при разрастании проекта, когда начинают использоваться сторонние строковые ресурсы, созданные с применением кодировки ANSI.
Если же проект не содержит hard-coded (включенных непосредственно в код) строк, то возможно организовать вынесение всех строковых ресурсов в отдельные модули с последующей их локализацией под требуемую кодировку – утилит, меняющих кодировку файлов существует предостаточно.
Использование фильтрующих процедур. Windows API содержит функции для преобразования между кодировками OEM и ANSI OemToChar, CharToOem, которые и предлагается использовать при выводе текста, заменяя фрагменты.
Writeln('Delphi World - это круто!!!');
|
На:
procedure MyWriteln(const S: string);
var
NewStr: string;
begin
SetLengtn(NewStr, Length(S));
CharToOem(PChar(S), PChar(NewStr));
Writeln(NewStr);
end;
...
MyWriteln('Delphi World - это круто!!!');
|
К недостаткам можно отнести невозможность использования расширенного синтаксиса Write, а также некоторое захламление текста программы явным вызовом фильтрующих процедур. Задача становится совсем неприятной, когда предстоит «русифицировать» уже готовое приложение со многими обращениями к Write, поэтому применять его стоит с определенными оговорками.
Изменение кодовой страницы консоли. В принципе, для решения задачи есть документированный способ – изменение кодовой страницы консоли средствами Windows API. Проблема лишь в том, что в Win95/98 функция не работает. Впрочем, если приложение будет работать только в Windows NT, можно воспользоваться функцией SetConsoleOutputCP(866).
Перекрытие процедур вывода в RTL. Вывод в Pascal (еще в версиях от Borland для DOS) через Write-процедуры осуществляется посредством передачи выводимой информации в файл Output, который вполне можно подвергнуть легкой модификации с целью упростить себе жизнь.
Известно, что Write/Writeln без указания файла осуществляет вывод в файл Output. Output имеет тип TextFile, он же TTextRec, содержимое которого описано в SysUtils.pas. Есть там и поля, содержащие адреса процедур, в которые приходит на обработку поток выводимых приложением данных (в случае вывода). Не вдаваясь в подробности (желающие могут посмотреть устройство механизмов вывода в исходниках RTL), покажем, что происходит в процедуре, отвечающей за вывод (TTextRec.InOutFunc):
{ Реконструкция TextOut из Assign.asm }
function TextOut(var Text: TTextRec): Integer;
var
Dummy: Cardinal;
SavePos: Integer;
begin
SavePos := Text.BufPos;
if SavePos > 0 then
begin
Text.BufPos := 0;
if WriteFile(Text.Handle, Text.BufPtr^, SavePos, Dummy, nil) then
Result := 0
else
Result := GetLastError;
end
else
Result := 0;
end;
|
Теперь видно, что нужно сделать для вывода символов в нужной кодовой таблице – перед выводом в файл средствами ОС модифицировать данные в выходном буфере структуры Text, вписав следующую строку:
CharToOemBuff(Text.BufPtr, Text.BufPtr, SavePos); |
Модифицировать буфер можно, т.к. после операции записи в файл содержимое буфера фактически сбрасывается (когда в Text.BufPos записывается 0 – именно столько актуальных данных остается в буфере). Если не завязываться на эту особенность реализации, можно распределить буфер и модифицировать данные уже в нем. Впрочем, решение в любом случае достаточно сильно опирается на особенности реализации, поэтому проверить его пригодность при смене версии Delphi рекомендуется в любом случае. С другой стороны, вероятность отхода Borland от наработанного решения крайне мала.
Заметим, что кроме InOutFunc вывод в файл ОС происходит и в FlushFunc, которая в файле Output указывает на ту же функцию, что и InOutFunc. С учетом всего вышесказанного модуль, осуществляющий «русификацию» консольных приложений «на лету» будет совсем небольшим:
{
Модуль “русификации“ консольных приложений
(c) Eugene Kasnerik, 1999
e-mail: eugene1975@mail.ru
}
unit EsConsole;
interface
implementation
uses
Windows;
{
Описание структуры приведено здесь с единственной целью –
не подключать SysUtils и, соответственно, код инициализации
этого модуля. Консольные приложения обычно малы и 25К кода
обработки исключений – несколько высокая плата за описание
единственной структуры.
}
type
TTextRec = record
Handle: Integer;
Mode: Integer;
BufSize: Cardinal;
BufPos: Cardinal;
BufEnd: Cardinal;
BufPtr: PChar;
OpenFunc: Pointer;
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: array[1..32] of Byte;
name: array[0..259] of Char;
Buffer: array[0..127] of Char;
end;
function ConOutFunc(var Text: TTextRec): Integer;
var
Dummy: Cardinal;
SavePos: Integer;
begin
SavePos := Text.BufPos;
if SavePos > 0 then
begin
Text.BufPos := 0;
CharToOemBuff(Text.BufPtr, Text.BufPtr, SavePos);
if WriteFile(Text.Handle, Text.BufPtr^, SavePos, Dummy, nil) then
Result := 0
else
Result := GetLastError;
end
else
Result := 0;
end;
initialization
Rewrite(Output); // Проводим инициализацию файла
{ И подменяем обработчики. Есть в этом что-то от
хака, но цель оправдывает средства }
TTextRec(Output).InOutFunc := @ConOutFunc;
TTextRec(Output).FlushFunc := @ConOutFunc;
end.
|
Для русификации приложения достаточно лишь подключить вышеуказанный модуль в любом месте программы (как правило, в проекте), после чего вывод ANSI-символов будет осуществлен в ожидаемом виде. Однако следует иметь в виду, что не будут доступны символы псевдографики (для них нет аналогов в ANSI, т.е. в редакторе их не введешь) и часть диакритических знаков (для них нет аналогов в OEM). Впрочем, для модификации значений выводимых символов не обязательно использовать системные функции, что открывает простор для консольного вывода в самых разных кодировках.
Выравнивание текста по ширине с автоматическим переносом русских слов.
Оформил: DeeCo
Автор: Антон Григорьев.
Краткое описание :
Для переноса слов программа не использует словарь, а анализирует каждое слово с точки зрения правил русского языка. Правила эти достаточно сложны, поэтому иногда программа выбирает не самое изящное, хотя и не неправильное решение. Прежде всего это касается тех слов, в которых три или более согласных идут подряд. Программа может вставить перенос между любыми из них, хотя есть дополнительные правила, которые при этом игнорируются. Например, то, что приставка должна оставаться целой. Слишком муторно было бы заставлять компьютер учить все приставки. Да ещё и объяснять, что, например, в слове "президент" "пре" - это не приставка, а в слове "предлог" приставка не "пре", а "пред". Тогда уж лучше и в самом деле словарь. Впрочем, демонстрационная программа и есть демонстрационная программа, кто захочет, посмотрит, поймёт основные принципы и сделает лучше. Из улучшений, которые так и напрашиваются, стоит назвать красную строку и разбивку текста на несколько абзацев. Но это делается настолько элементарно, что мне даже не хочется на этом останавливаться. Можно ввести перенос английских слов (сейчас программа их переносить не умеет, потому что я не слишком хорошо знаком с английскими правилами переноса), можно ввести запрет на перенос слов из прописных букв - возможностей для творчества хватает, было бы желание. Я же реализовал простейший вариант - при запуске программа ищет файл ByWidth.txt, мешает все слова в одну кучу, удаляет все лишние пробелы и разбивает на строки нужной длины, перенося при необходимости слова. Предполагается, что в исходном тексте нет переносов, в том числе и тех слов, которые пишутся через дефис. Таким образом, исходный текст может быть написан хоть в одну строку, хоть по одному слову на строку - разницы не будет. И можно вставлять пустые строки - они всё равно игнорируются.Примечание:
Защита от дурака в программе отсутствует, поэтому если файл ByWidth.txt не будет содержать никакого текста, никаких вежливых предупреждений не будет. Тот, кто захочет приспособить этот пример для чего-то полезного, без проблем добавит эту защиту, а загромождать демонстрацинную программу вряд ли стоит. Скачать проект : ByWidth.zip файлы проекта + exe (189 K) ByWidthPr.zip только файлы проекта (16,5 K)Вставка рисунка в TRxRichEdit.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
RichEdit;
// Stream Callback function
type
TEditStreamCallBack = function(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): DWORD;
stdcall;
TEditStream = record
dwCookie: Longint;
dwError: Longint;
pfnCallback: TEditStreamCallBack;
end;
// RichEdit Type
type
TMyRichEdit = TRxRichEdit;
// EditStreamInCallback callback function
function EditStreamInCallback(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): DWORD; stdcall;
// by P. Below
var
theStream: TStream;
dataAvail: LongInt;
begin
theStream := TStream(dwCookie);
with theStream do
begin
dataAvail := Size - Position;
Result := 0;
if dataAvail <= cb then
begin
pcb := read(pbBuff^, dataAvail);
if pcb <> dataAvail then
Result := UINT(E_FAIL);
end
else
begin
pcb := read(pbBuff^, cb);
if pcb <> cb then
Result := UINT(E_FAIL);
end;
end;
end;
// Insert Stream into RichEdit
procedure PutRTFSelection(RichEdit: TMyRichEdit; SourceStream: TStream);
// by P. Below
var
EditStream: TEditStream;
begin
with EditStream do
begin
dwCookie := Longint(SourceStream);
dwError := 0;
pfnCallback := EditStreamInCallBack;
end;
RichEdit.Perform(EM_STREAMIN, SF_RTF or SFF_SELECTION, Longint(@EditStream));
end;
// Convert Bitmap to RTF Code
function BitmapToRTF(pict: TBitmap): string;
// by D3k
var
bi, bb, rtf: string;
bis, bbs: Cardinal;
achar: ShortString;
hexpict: string;
I: Integer;
begin
GetDIBSizes(pict.Handle, bis, bbs);
SetLength(bi, bis);
SetLength(bb, bbs);
GetDIB(pict.Handle, pict.Palette, PChar(bi)^, PChar(bb)^);
rtf := '{\rtf1 {\pict\dibitmap ';
SetLength(hexpict, (Length(bb) + Length(bi)) * 2);
I := 2;
for bis := 1 to Length(bi) do
begin
achar := Format('%x', [Integer(bi[bis])]);
if Length(achar) = 1 then
achar := '0' + achar;
hexpict[I - 1] := achar[1];
hexpict[I] := achar[2];
Inc(I, 2);
end;
for bbs := 1 to Length(bb) do
begin
achar := Format('%x', [Integer(bb[bbs])]);
if Length(achar) = 1 then
achar := '0' + achar;
hexpict[I - 1] := achar[1];
hexpict[I] := achar[2];
Inc(I, 2);
end;
rtf := rtf + hexpict + ' }}';
Result := rtf;
end;
// Example to insert image from Image1 into RxRichEdit1
procedure TForm1.Button1Click(Sender: TObject);
var
SS: TStringStream;
BMP: TBitmap;
begin
BMP := TBitmap.Create;
BMP := Image1.Picture.Bitmap;
SS := TStringStream.Create(BitmapToRTF(BMP));
try
PutRTFSelection(RxRichEdit1, SS);
finally
SS.Free;
end;
end;
Сохранение типа списка объектов.
unit Dates;
interface
uses
SysUtils, Classes;
type
TDate = class (TComponent)
private
FMonth, FDay, FYear: Integer;
FOnChange: TNotifyEvent;
protected
function DaysInMonth: Integer;
procedure SetMonth (Value: Integer);
procedure SetYear (Value: Integer);
procedure SetDay (Value: Integer);
procedure DoChange; virtual;
public
constructor Create (AOwner: TComponent); override;
constructor Init (m, d, y: Integer);
procedure SetValue (m, d, y: Integer);
function LeapYear: Boolean;
procedure Increase;
procedure Decrease;
procedure Add (NumberOfDays: Integer);
procedure Subtract (NumberOfDays: Integer);
function GetText: string;
// properties:
property Text: string read GetText;
published
property Day: Integer read FDay write SetDay;
property Month: Integer read FMonth write SetMonth;
property Year: Integer read FYear write SetYear;
// event:
property OnChange: TNotifyEvent
read FonChange write FOnChange;
end;
// dates exception
type
EDateOutOfRange = class (Exception);
procedure Register;
implementation
constructor TDate.Create (AOwner: TComponent);
var
Y, D, M: Word;
begin
inherited Create (AOwner);
// today...
DecodeDate (Now, Y, M, D);
FYear := Y;
FMonth := M;
FDay := D;
end;
constructor TDate.Init (m, d, y: Integer);
begin
SetValue (m, d, y);
end;
procedure TDate.DoChange;
begin
if Assigned (FOnChange) then
FOnChange (self);
end;
procedure TDate.SetValue (m, d, y: Integer);
var
OldY, OldM: Integer;
begin
// store the old value
OldY := FYear;
OldM := FMonth;
// assing the new value
try
FYear := y;
// check the ranges
SetMonth (m);
SetDay (d);
DoChange;
except
on EDateOutOfRange do
begin
// reset the values
FYear := OldY;
FMonth := OldM;
// let the error show up
raise;
end;
end;
end;
procedure TDate.SetMonth (Value: Integer);
begin
if (Value >= 1) and (Value <= 12) then
begin
FMonth := Value;
DoChange;
end
else
raise EDateOutOfRange.Create ('Month out of range');
end;
procedure TDate.SetYear (Value: Integer);
begin
FYear := Value;
DoChange;
end;
procedure TDate.SetDay (Value: Integer);
begin
if (Value >= 1) and (Value <= DaysInMonth) then
begin
FDay := Value;
DoChange;
end
else
raise EDateOutOfRange.Create ('Day out of range');
end;
function TDate.LeapYear: Boolean;
begin
// compute leap years, considering "exceptions"
if (FYear mod 4 <> 0) then
LeapYear := False
else if (FYear mod 100 <> 0) then
LeapYear := True
else if (FYear mod 400 <> 0) then
LeapYear := False
else
LeapYear := True;
end;
function TDate.DaysInMonth: Integer;
begin
case FMonth of
1, 3, 5, 7, 8, 10, 12:
DaysInMonth := 31;
4, 6, 9, 11:
DaysInMonth := 30;
2:
if (LeapYear) then
DaysInMonth := 29
else
DaysInMonth := 28;
else
// if the month is not correct
DaysInMonth := 0;
end;
end;
procedure TDate.Increase;
begin
// if this day is not the last of the month
if FDay < DaysInMonth then
Inc (FDay) // increase the value by 1
else
// if it is not in December
if FMonth < 12 then
begin
// Day 1 of next month
Inc (FMonth);
FDay := 1;
end
else
begin
// else it is next year New Year's Day
Inc (FYear);
FMonth := 1;
FDay := 1;
end;
DoChange;
end;
// exactly the reverse of the Increase method
procedure TDate.Decrease;
begin
if FDay > 1 then
Dec (FDay) // decrease the value by 1
else
// it is the first of a month
if FMonth > 1 then
begin
// assign last day of previous month
Dec (FMonth);
FDay := DaysInMOnth;
end
else
// it is the first of January
begin
// assign last day of previous year
Dec (FYear);
FMonth := 12;
FDay := DaysInMOnth;
end;
DoChange;
end;
function TDate.GetText: string;
begin
GetText := Format ('%s %d, %d',
[LongMonthNames[Month], Day, Year]);
end;
procedure TDate.Add (NumberOfDays: Integer);
var
N: Integer;
begin
// increase the day n times
for N := 1 to NumberOfDays do
Increase;
end;
procedure TDate.Subtract (NumberOfDays: Integer);
var
N: Integer;
begin
// decrease the day n times
for N := 1 to NumberOfDays do
Decrease;
end;
procedure Register;
begin
RegisterComponents ('Md3', [TDate]);
end;
end.
|
unit SList;
interface
uses
Classes;
type
TSafeList = class
private
LType: TClass;
FList: TList;
function Get (Index: Integer): TObject;
procedure Put (Index: Integer; Item: TObject);
function GetCount: Integer;
public
constructor Create (CType: TClass);
destructor Destroy; override;
function Add (Item: TObject): Integer;
function Equals(List: TSafeList): Boolean;
property Count: Integer read GetCount;
property Items [Index: Integer]: TObject
read Get write Put; default;
end;
implementation
uses
SysUtils;
constructor TSafeList.Create (CType: TClass);
begin
FList := TList.Create;
LType := CType;
end;
destructor TSafeList.Destroy;
begin
FList.Free;
inherited Destroy;
end;
function TSafeList.Get(Index: Integer): TObject;
begin
Result := FList [Index];
end;
function TSafeList.Add (Item: TObject): Integer;
var
Test: Boolean;
begin
try
Test := Item is LType;
except
on Exception do
raise EInvalidCast.Create (Format (
'SafeList: Cannot add a non-object to a list of %s objects',
[LType.ClassName]));
end;
if Test then
Result := FList.Add (Item)
else
raise EInvalidCast.Create (Format (
'SafeList: Cannot add a %s object to a list of %s objects',
[Item.ClassName, LType.ClassName]));
end;
procedure TSafeList.Put(Index: Integer; Item: TObject);
var
Test: Boolean;
begin
try
Test := Item is LType;
except on Exception do
raise EInvalidCast.Create (Format (
'SafeList: Cannot put a non-object into a list of %s objects',
[LType.ClassName]));
end;
if Test then
FList [Index] := Item
else
raise EInvalidCast.Create (Format (
'SafeList: Cannot put a %s object into a list of %s objects',
[TObject(Item).ClassName, LType.ClassName]));
end;
function TSafeList.GetCount: Integer;
begin
Result := FList.Count;
end;
function TSafeList.Equals(List: TSafeList): Boolean;
var
I: Integer;
begin
Result := False;
if List.Count <> FList.Count then
Exit;
for I := 0 to List.Count - 1 do
if List[I] <> FList[I] then
Exit;
Result := True;
end;
end.
|
unit SafeForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, SList;
type
TForm1 = class(TForm)
ButtonAddDates: TButton;
ButtonAddButton: TButton;
ButtonAddPointer: TButton;
ButtonNewDate: TButton;
ListBox1: TListBox;
ButtonNewButton: TButton;
ButtonNewPointer: TButton;
procedure ButtonAddDatesClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ButtonAddButtonClick(Sender: TObject);
procedure ButtonAddPointerClick(Sender: TObject);
procedure ButtonNewDateClick(Sender: TObject);
procedure ButtonNewButtonClick(Sender: TObject);
procedure ButtonNewPointerClick(Sender: TObject);
private
List: TSafeList;
public
procedure UpdateList;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
Dates;
procedure TForm1.ButtonAddDatesClick(Sender: TObject);
var
I: Integer;
begin
Randomize;
try
for I := 1 to 10 do
List.Add (TDate.Init (
1 + Random (12), 1 + Random (28),
1900 + Random (200)));
finally
UpdateList;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
List := TSafeList.Create (TDate);
end;
procedure TForm1.ButtonAddButtonClick(Sender: TObject);
begin
List.Add (Sender);
UpdateList;
end;
procedure TForm1.ButtonAddPointerClick(Sender: TObject);
var
P: Pointer;
begin
P := @Form1;
List.Add (P);
UpdateList;
end;
procedure TForm1.UpdateList;
var
I: Integer;
begin
ListBox1.Clear;
for I := 0 to List.Count - 1 do
Listbox1.Items.Add ((
TDate(List [I]).GetText));
end;
procedure TForm1.ButtonNewDateClick(Sender: TObject);
begin
List [1] := TDate.Create (self);
UpdateList;
end;
procedure TForm1.ButtonNewButtonClick(Sender: TObject);
begin
List [1] := Sender;
UpdateList;
end;
procedure TForm1.ButtonNewPointerClick(Sender: TObject);
var
S: String;
begin
S := 'Hi';
List [1] := Pointer(S);
UpdateList;
end;
end.
|
Загрузить весь проект
Как определить, что два слова имеют схожее произношение.
Автор: Lloyd Функция Soundex определяет схожесть звучания двух слов. Алгоритм Soundex опубликован в одной из статей журнала PC Magazine и предназначен для работы с английским языком (может кто-нибудь портирует для работы с нашим могучим? Пишите). Функции передается строка. Возвращаемое Soundex значение также имеет тип строки. Эта величина может сохраняться в базе данных или сравниваться с другим значением Soundex. Если два слова имеют одинаковое значение Soundex, можно предположить, что звучат они одинаково (более или менее). Вы должны иметь в виду, что алгоритм Soundex игнорирует первую букву слова. Таким образом, "won" и "one" будут иметь различное значение Soundex, а "Won" и "Wunn" - одинаковое.Soundex будет особенно полезен в базах данных, когда пользователь затрудняется с правописанием имен и фамилий.
function Soundex(OriginalWord: string): string;
var
Tempstring1, Tempstring2: string;
Count: integer;
begin
Tempstring1 := '';
Tempstring2 := '';
OriginalWord := Uppercase(OriginalWord);
{Переводим исходное слово в верхний регистр}
Appendstr(Tempstring1, OriginalWord[1]); {Используем первую букву слова}
for Count := 2 to length(OriginalWord) do
{Назначаем числовое значение каждой букве, за исключением первой}
case OriginalWord[Count] of
'B', 'F', 'P', 'V': Appendstr(Tempstring1, '1');
'C', 'G', 'J', 'K', 'Q', 'S', 'X', 'Z': Appendstr(Tempstring1, '2');
'D', 'T': Appendstr(Tempstring1, '3');
'L': Appendstr(Tempstring1, '4');
'M', 'N': Appendstr(Tempstring1, '5');
'R': Appendstr(Tempstring1, '6');
{Все другие буквы, цифры и знаки пунктуации игнорируются}
end;
Appendstr(Tempstring2, OriginalWord[1]);
{Удаляем из результата все последовательно повторяющиеся цифры.}
for Count := 2 to length(Tempstring1) do
if Tempstring1[Count - 1] <> Tempstring1[Count] then
Appendstr(Tempstring2, Tempstring1[Count]);
Soundex := Tempstring2; {Это - значение soundex}
end;
|
SoundAlike - функция, проверяющая схожесть звучания двух слов. При схожести звучания она возвратит значение True и значение False в противном случае. Она демонстрирует пример использования функции Soundex.
function SoundAlike(Word1, Word2: string): boolean;
begin
if (Word1 = '') and (Word2 = '') then
result := True
else if (Word1 = '') or (Word2 = '') then
result := False
else if (Soundex(Word1) = Soundex(Word2)) then
result := True
else
result := False;
end;
|
Андрей vs. Андрей - 6
ндрей vs. Андрей - 5
Анрей vs. Андрей - 5
Андрей vs. Александр - 4
Андрей vs. Иннокентий - 2
АнХрей vs. Андрей - 3, но в то же время с другими словами результат
будет на уровне 0..2
Andrew V. Fionik
O сохранении иконок 32хЗ2 в 256-цветном формате.
Автор: Владимир WEB-сайт: http://forum.vingrad.ru Суть вопроса: я столкнулся с проблемой сохранения полноцветных иконок, когда понадобилось немного изменить имеющиеся у меня для своих программ. Ни родной Image Editor от Delphi6, ни другие редакторы не смогли мне помочь. Могли это делать платные редакторы, но они не для нас. Начав разбираться, я обнаружил, что созданная функцией CreateIconIndirect иконка нормально выглядит, если после создания ее кинуть на форму, однако после записи Icon.SaveToFile иконка обезображивается. Это происходит на стадии записи иконки. Поискав информацию, облазив форумы, я понял, что либо этой проблемой никто не занимался, либо с ней все мирятся и заниматься не хотят, хотя интересующиеся имеются. Став заниматься проблемой вплотную, я решил использовать нетипизированные файлы, которым все равно, что в них записывают. Эти файлы хорошо подходят для работы с массивами байт и, если создать массив полностью соответствующий структуре иконки, то записав массив в нетипизированный файл, получим искомую иконку. Пришлось изучить структуру файла ICO и вот результат. Для использования процедуры нет необходимости изучать файл ICO, если только вы не хотите ничего изменять, например, размеры иконки. В принципе, можно сделать универсальную процедуру, которая бы определяла размер иконки и, в соответствии с размерами, создавала иконку. Все адреса и размеры, указанные в процедуре, являются абсолютными и изменению не подлежат, так как именно от них зависит, будет ли соответствовать создаваемый массив файлу ICO. В процедуру введено автоопределение количества цветов палитры, от этого зависит структура файла. Вам нет нужды указывать количество используемых цветов.Кроме того, введено определение размера рисунка и если он не 32хЗ2, то процедура прекратит работу. Это произойдет и в случае если кому-то захочется использовать более 256 цветов. Тем, кто хочет узнать о структуре файлов ICO, читать после кода процедуры. Используя этот метод можно работать с любыми файлами, например, для открытия и побайтного изменения EXE-файлов. Для работы этой процедуры нужен рисунок 32хЗ2 на канве (у меня Image2), диалоговое окно SaveDialog1 (но не SavePictureDialog1) и все. Если нужно, измените имя канвы, оно встречается в двух строчках кода, и имя диалогового окна. Процедура с комментариями ниже:
//Записываем картинку как иконку (файл .ico)
{Так как стандартные функции не дают возможности создавать 256 цветные
иконки, мы пойдем другим путем, хоть он и медленнее и длиннее.
Создадим массив байт, который запишем как файл, это и будет
искомая иконка. Для этого создадим промежуточные массивы, иначе процедура
станет слишком навороченной, затем проверим количество используемых цветов
и, в зависимости от этого, создадим окончательный массив.}
procedure TForm1.ToolButton6Click(Sender: TObject);
var
MAnd, MXor, MOsn, MCol: array of Byte;
MPix, MOrg: array of TColor;
a, b, c, n, m: integer;
Bt, Bu, Indicator: Byte;
p: TColor;
k: boolean;
F: file;
LenOsn, LenXor, LenCol: integer;
begin
if (Image2.Picture.Height <> 32) or (Image2.Picture.Width <> 32) then
begin
ShowMessage('Исходный рисунок не 32хЗ2');
exit;
end;
{Создание массивов: MAnd - массив маски AND, MXor - массив маски Xor,
основного массива MOsn - куда будет собираться вся информация,
массив MPix - массив пикселов рисунка начиная с левого нижнего угла,
MCol - массив таблицы цветов - палитры, MOrg - массив оригинальных цветов}
{Заполнение массивов MPix и MAnd}
{Активизация массивов}
SetLength(MPix, 1024);
SetLength(MAnd, 128);
{Установка счетчиков}
b := 0; //Счетчик битов
a := 0; //Счетчик пикселов картинки
c := 0; //Счетчик байтов маски MAnd
Bt := 0; //Формируемый байт маски MAnd
{Преобразование картинки в удобный формат, с началом от 0,
и перенос ее в таком виде в массив MPix, меняя по пути белый -
будущий прозрачный цвет - на черный, заодно заполняем массив MAnd}
for m := 31 downto 0 do
for n := 0 to 31 do
begin
MPix[a] := Image2.Canvas.Pixels[n, m];
p := MPix[a];
if p = RGB(255, 255, 255) then
begin
MPix[a] := RGB(0, 0, 0);
if b = 0 then
Bt := Bt + 128;
if b = 1 then
Bt := Bt + 64;
if b = 2 then
Bt := Bt + 32;
if b = 3 then
Bt := Bt + 16;
if b = 4 then
Bt := Bt + 8;
if b = 5 then
Bt := Bt + 4;
if b = 6 then
Bt := Bt + 2;
if b = 7 then
Bt := Bt + 1;
end;
b := b + 1;
if b = 8 then
begin
MAnd[c] := Bt;
Bt := 0;
c := c + 1;
b := 0;
end;
a := a + 1;
end;
{Заполнение маски MOrg нулями, предполагая 256 цветов}
SetLength(MOrg, 256);
for n := 0 to 255 do
MOrg[n] := RGB(255, 255, 255);
{Оцениваем количество цветов и от результата переделываем MOrg}
a := 0; //Счетчик количества цветов
for n := 0 to 1023 do
begin
p := MPix[n];
k := false;
for m := 0 to 255 do
begin
if k = false then
if p = MOrg[m] then
k := true;
end;
if k = false then
begin
if a > 255 then
begin
ShowMessage('Ваш рисунок имеет более 256 цветов');
MOrg := nil;
MPix := nil;
MAnd := nil;
exit;
end;
MOrg[a] := p;
a := a + 1;
end;
end;
LenOsn := 2238;
LenXor := 1024;
LenCol := 1024;
Indicator := 1;
if a < 15 then
begin
LenOsn := 766;
LenXor := 512;
Indicator := 0;
end;
{Заполняем маски нулями}
SetLength(MXor, LenXor);
SetLength(MCol, LenCol);
for n := 0 to LenXor - 1 do
begin
MXor[n] := 0;
end;
for n := 0 to LenCol - 1 do
begin
MCol[n] := 0;
end;
{Заполнение массива MXor согласно массива MPix}
Bu := 0;
b := 0;
c := 0;
for n := 0 to 1023 do
begin
p := MPix[n];
k := false;
for Bt := 0 to 255 do
begin
if k = false then
if p = MOrg[Bt] then
begin
k := true;
if indicator = 1 then
MXor[n] := Bt
else if b = 1 then
begin
Bu := 16 * Bu + Bt;
MXor[c] := Bu;
c := c + 1;
b := 0;
end
else
begin
Bu := Bt;
b := 1;
end;
end;
end;
end;
{Заполняем массив MCol согласно массива MOrg}
begin
a := 0;
for n := 0 to 255 do
begin
p := MOrg[n];
MCol[a] := GetBValue(p);
MCol[a + 1] := GetGValue(p);
MCol[a + 2] := GetRValue(p);
a := a + 4;
end;
end;
{Заполняем MOsn нулями}
SetLength(MOsn, LenOsn);
for n := 0 to LenOsn - 1 do
MOsn[n] := 0;
{Заполняем массив MOsn для 256 цветной иконки}
if indicator = 1 then
begin
MOsn[2] := 1;
MOsn[4] := 1;
MOsn[6] := 32;
MOsn[7] := 32;
MOsn[14] := $A8;
MOsn[15] := 8;
MOsn[18] := $16;
MOsn[22] := $28;
MOsn[26] := 32;
MOsn[30] := $40;
MOsn[34] := 1;
MOsn[36] := 8;
MOsn[42] := $80;
MOsn[43] := 4;
MOsn[55] := 1;
m := 0;
for n := 62 to 1085 do
begin
MOsn[n] := MCol[m];
m := m + 1;
end;
m := 0;
for n := 1086 to 2109 do
begin
MOsn[n] := MXor[m];
m := m + 1;
end;
m := 0;
for n := 2110 to 2237 do
begin
MOsn[n] := MAnd[m];
m := m + 1;
end;
end;
{Заполняем массив MOsn для 16-цветной}
if indicator = 0 then
begin
MOsn[2] := 1;
MOsn[4] := 1;
MOsn[6] := 32;
MOsn[7] := 32;
MOsn[8] := 4;
MOsn[14] := $E8;
MOsn[15] := 2;
MOsn[18] := $16;
MOsn[22] := $28;
MOsn[26] := 32;
MOsn[30] := 64;
MOsn[34] := 1;
MOsn[36] := 4;
MOsn[43] := 2;
MOsn[54] := 16;
m := 0;
for n := 62 to 125 do
begin
MOsn[n] := MCol[m];
m := m + 1;
end;
m := 0;
for n := 126 to 637 do
begin
MOsn[n] := MXor[m];
m := m + 1;
end;
m := 0;
for n := 638 to 765 do
begin
MOsn[n] := MAnd[m];
m := m + 1;
end;
end;
{Закрываем все массивы, кроме MOsn}
MAnd := nil;
MXor := nil;
MOrg := nil;
MCol := nil;
MPix := nil;
{Записываем массив MOsn в файл, для этого создаем нетипизированный файл,
активизируем его и побайтно пишем в него данные из массива MOsn}
if SaveDialog1.Execute then
begin
AssignFile(f, SaveDialog1.FileName + '.ico');
Rewrite(f, 1);
for n := 0 to LenOsn - 1 do
begin
Bt := MOsn[n];
BlockWrite(f, Bt, 1);
end;
CloseFile(f);
MOsn := nil;
end;
end;
Существуют иконки более сложной структуры. Они могут содержать несколько рисунков. Я с ними не разбирался.
Может быть кто-то из профессионалов скажет - примитивщина, нагородил. Ну что ж, может быть есть пути проще и лучше, но когда я спрашивал на форумах никто не смог помочь. Главное, работает без проблем. Я всего месяц как познакомился с Delphi и Паскалем, думаю можно кое-что и простить.
О структуре иконки. Информации немного в интернете, но кое-что я раскопал. Пришлось просмотреть коды разных иконок, чтобы иметь о них представление. Файл ICO очень похож на BMP: в начале файла заголовок BITMAPFILEHEADER, размером обычно 22 байта. В нем содержится информация о файле. За ним идет BITMAPINFOHEADER, 40 байт, содержит информацию о ширине и высоте иконки в пикселах, количество бит на пиксел в структуре картинки, занимаемое место таблицей цветов и другая информация. Для примера оба заголовка для 16-цветной иконки. В квадратных скобках номер байта в файле, далее эго значение.
[2] := 1; // Всегда 1 [4] := 1; // Всегда 1 [6] := 32; // Ширина и [7] := 32; // высота в пикселах? [8] := 4 // Не знаю [14] := $E8; // Младший байт размера файла минус 22(размер BITMAPFILEHEADER) [15] := 2; // Старший байт размера файла [18] := 22; // Размер BITMAPFILEHEADER [22] := 40; // Размер заголовка BITMAPINFOHEADER [26] := 32; // Ширина в пикселах [30] := 64; // Высота в пикселах обоих масок в сумме [34] := 1; // Число плоскостей [36] := 4; // бит/пиксел для таблицы пикселов [43] := 2; // Старший байт размера таблицы пикселов (т.е.$200=512) [54] := 16; // Число используемых цветов (не обязательно)Остальные не указанные байты и до адреса 62 все 0. После заголовков находится таблица цветов. Каждый цвет занимает 4 байта. По порядку - синий, зеленый, красный и нулевой байты. Первые 3 байта образуют цвет пиксела. То есть, каждый пиксел на иконке может иметь 256х256х256 оттенков. При 16 цветной иконке таблица цветов занимает 16хЧ=64 байта, при 256 цветной - 1024 байта. По идее, при записи иконки любая программа должна просканировать иконку и все найденные цвета записать в таблицу.Почему этого не происходит я не знаю. Windows эти цвета при записи игнорирует и устанавливает свои. Если же при 16 цветной палитре используются только, допустим, 6 цветов, то остальные 10 цветов не нужны, однако место для них остается, но оно не используется. Следующим блоком в файле идет таблица пикселов. Первый пиксел это самый нижний в левом углу. И переписываются они в таблицу построчно. Основной параметр, по которому таблица организуется, это количество бит на пиксел. Каждый элемент этой таблицы - это номер цвета в таблице цветов. При выводе на экран иконки берется очередной элемент, то есть номер цвета в таблице цветов, берет по номеру цвет из таблицы цветов и этот цвет выводится на экран. Так как для определения номера в таблице из 16 цветов достаточно 4 бит, то каждый байт таблицы пикселов определяет 2 пиксела. Причем, младшие 4 разряда относятся к первому, а старшие - к следующему пикселам. Для 256 цветов необходим полный байт. Поэтому, таблица пикселов занимает соответственно 512 и 1024 байтов. Таблица пикселов это образ маски XOR иконки, цвет, который должен быть прозрачным, заменен черным, в остальном же это рисунок, который вы создали в редакторе. И последним идет таблица маски AND. Это двухцветная маска вашего рисунка, имеет только белый цвет (бит включен) и черный цвет (бит равен 0). Белый цвет находится там, где будет прозрачный цвет, черный скрывает все остальное. При наложении двух масок друг на друга тот пиксел, у которого на маске AND белый, а на маске XOR черный цвет, будет невидим. Так как маска AND монохромная, то один байт содержит информацию о 8 пикселах, а размер маски будет 1024/8=128 байт. Имеем: для 16-цветной иконки размер 22+40+64+512+128=766 байт, для 256 - 22+40+1024+1024+128=2238 байт.
Создав массив из соответствующим образом пребразованного рисунка, мы можем записать его байты в нетипизированный файл, которому все равно, что в нем находится, добавив к имени расширение .ico, мы получим то, что было нужно. Конечно, есть разные варианты размеров иконок, у них могут меняться размеры заголовков, использоваться другая информация, но структуры эти должны присутствовать. Существуют иконки гораздо большие размером, но я с такими не разбирался.
Сохранение и выдёргивание ресурсов в DLL или EXE.
| В трамвае: - Девушка! А девушка! А вы наверно программистка? - Да, но как вы догадались?!! - У вас очень глупое лицо! - ДУРАК!!! - Да, я тоже программист... |
Иногда возникает необходимость вшить ресурсы в исполняемый файл Вашего приложения (например чтобы предотвратить их случайное удаление пользователем, либо, чтобы защитить их от изменений). Данный пример показывает как вшить любой файл как ресурс в EXE-шнике.
Далее рассмотрим, как создать файл ресурсов, содержащий корию какого-либо файла. После создания такого файла его можно легко прицепить к Вашему проекту директивой {$R}. Файл ресурсов, который мы будем создавать имеет следующий формат:
заголовок заголовок для нашего RCDATA ресурса собственно данные - RCDATA ресурсВ данном примере будет показано, как сохранить в файле ресурсов только один файл, но думаю, что так же легко Вы сможете сохранить и несколько файлов.
Заголовок ресурса выглядит следующим образом:
TResHeader = record DataSize: DWORD; // размер данных HeaderSize: DWORD; // размер этой записи ResType: DWORD; // нижнее слово = $FFFF => ordinal ResId: DWORD; // нижнее слово = $FFFF => ordinal DataVersion: DWORD; // * MemoryFlags: WORD; LanguageId: WORD; // * Version: DWORD; // * Characteristics: DWORD; // * end; |
Поля помеченны звёздочкой Мы не будем использовать.
Приведённый код создаёт файл ресурсов и копирует его в данный файл:
procedure CreateResourceFile(
DataFile, ResFile: string; // имена файлов
ResID: Integer // id ресурсов
);
var
FS, RS: TFileStream;
FileHeader, ResHeader: TResHeader;
Padding: array [0..SizeOf(DWORD)-1] of Byte;
begin
{ Open input file and create resource file }
FS := TFileStream.Create( // для чтения данных из файла
DataFile, fmOpenRead);
RS := TFileStream.Create( // для записи файла ресурсов
ResFile, fmCreate);
{ Создаём заголовок файла ресурсов - все нули, за исключением
HeaderSize, ResType и ResID }
FillChar(FileHeader, SizeOf(FileHeader), #0);
FileHeader.HeaderSize := SizeOf(FileHeader);
FileHeader.ResId := $0000FFFF;
FileHeader.ResType := $0000FFFF;
{ Создаём заголовок данных для RC_DATA файла
Внимание: для создания более одного ресурса необходимо
повторить следующий процесс, используя каждый раз различные
ID ресурсов }
FillChar(ResHeader, SizeOf(ResHeader), #0);
ResHeader.HeaderSize := SizeOf(ResHeader);
// id ресурса - FFFF означает "не строка!"
ResHeader.ResId := $0000FFFF or (ResId shl 16);
// тип ресурса - RT_RCDATA (from Windows unit)
ResHeader.ResType := $0000FFFF
or (WORD(RT_RCDATA) shl 16);
// размер данных - есть размер файла
ResHeader.DataSize := FS.Size;
// Устанавливаем необходимые флаги памяти
ResHeader.MemoryFlags := $0030;
{ Записываем заголовки в файл ресурсов }
RS.WriteBuffer(FileHeader, sizeof(FileHeader));
RS.WriteBuffer(ResHeader, sizeof(ResHeader));
{ Копируем файл в ресурс }
RS.CopyFrom(FS, FS.Size);
{ Pad data out to DWORD boundary - any old
rubbish will do!}
if FS.Size mod SizeOf(DWORD) <> 0 then
RS.WriteBuffer(Padding, SizeOf(DWORD) -
FS.Size mod SizeOf(DWORD));
{ закрываем файлы }
FS.Free;
RS.Free;
end;
|
Данный код не совсем красив, и отсутствует обработка ошибок. Правильнее будет создать класс, включающий в себя данный пример.
Извлечение ресурсов из EXE
Теперь рассмотрим пример, показывающий, как извлекать ресурсы из исполняемого модуля.
Вся процедура заключается в создании потока ресурса, создании файлового потока и копировании из потока ресурса в поток файла.
procedure ExtractToFile(Instance:THandle; ResID:Integer; ResType, FileName:string);
var
ResStream: TResourceStream;
FileStream: TFileStream;
begin
try
ResStream := TResourceStream.CreateFromID(Instance, ResID, pChar(ResType));
try
//if FileExists(FileName) then
//DeleteFile(pChar(FileName));
FileStream := TFileStream.Create(FileName, fmCreate);
try
FileStream.CopyFrom(ResStream, 0);
finally
FileStream.Free;
end;
finally
ResStream.Free;
end;
except
on E:Exception do
begin
DeleteFile(FileName);
raise;
end;
end;
end;
|
Всё, что требуется, это получить Instance exe-шника или dll (у Вашего приложения это Application.Instance или Application.Handle, для dll Вам придётся получить его самостоятельно :).
ResID тот же самый ID , который был присвоен ресурсу ResType: WAVEFILE, BITMAP, CURSOR, CUSTOM это типы ресурсов, с которыми возможно работать, но у меня получилось успешно проделать процедуру только с CUSTOM FileName это имя файла, который мы хотим создать из ресурсаЗапись массива на диск.
| Автор: Steve Schafer |
| Format винту не товарищ. |
Скажем, ваша структура данных выглядит следующим образом:
type
TMyRec = record
SomeField: Integer;
SomeOtherField: Double;
TheRest: array[0..99] of Single;
end;
|
И TBlobField имеет имя MyBlobField. TMyRec назван как MyRec. Для копирования содержимого MyRec в MyBlobField необходимо сделать следующее:
var Stream: TBlobStream; begin Stream := TBlobStream.Create(MyBlobField, bmWrite); Stream.Write(MyRec, SizeOf(MyRec)); Stream.Free; end; |
Есть другой путь:
var Stream: TBlobStream; begin Stream := TBlobStream.Create(MyBlobField, bmRead); Stream.Read(MyRec, SizeOf(MyRec)); Stream.Free; end; |
Сохранение массива c изображениями.
Автор: Michael Vincze Я решил проблему записи массива TBitmap в файл и его повторного чтения. Идея заключается в загрузке каждого TBitmap во временный TMemoryStream. Член TMemoryStream.Size информирует о размере данных, которые нужно сохранить на диске. Затем мы пишем размер и сопровождаем его данными типа TFileStream. Эту манипуляцию мы проделываем для каждого TBitmap в массиве. Для процедуры чтения сначала мы должны считать из потока размер данных TBitmap. Затем мы распределяем область для типа TMemoryStream полученного размера и считываем данные. Затем переписываем из TFileStream в TMemoryStream. И, наконец, мы читает из TMemoryStream сам TBitmap. Эту манипуляцию мы проделываем для каждого TBitmap в массиве. Ниже я привел код, который я реально использовал. Код из игры Bingo, которую я разрабатываю, имеет сетку 5x5, чьи ячейки содержат изображение.Реализация алгоритма весьма медленна, поэтому если вы имеете или найдете более быстрый алгоритм, пожалуйста, уведомите меня об этом. Если у вас есть любые вопросы, пожалуйста, свяжитесь со мной.
procedure TMainForm.SaveBoard;
var
MemoryStream: TMemoryStream;
FileStream: TFileStream;
Writer: TWriter;
Buffer: Pointer;
Size: Longint;
Column: Integer;
Row: Integer;
begin
MemoryStream := TMemoryStream.Create;
FileStream := TFileStream.Create(SaveFilename, fmCreate);
Writer := TWriter.Create(FileStream, $1000);
try
for Column := 0 to 4 do
for Row := 0 to 4 do
begin
MemoryStream.Clear;
Bitmaps[Column, Row].SaveToStream(MemoryStream);
Buffer := MemoryStream.Memory;
Size := MemoryStream.Size;
Writer.WriteInteger(Size);
Writer.Write(Buffer^, Size);
end;
finally
Writer.Free;
FileStream.Free;
MemoryStream.Free;
end;
end;
procedure TMainForm.Open1Click(Sender: TObject);
var
MemoryStream: TMemoryStream;
FileStream: TFileStream;
Buffer: Pointer;
Reader: TReader;
Column: Integer;
Row: Integer;
Size: Longint;
begin
OpenDialog2.Filename := SaveFilename;
if not OpenDialog2.Execute then
Exit;
MemoryStream := TMemoryStream.Create;
FileStream := TFileStream.Create(OpenDialog2.Filename, fmOpenRead);
Reader := TReader.Create(FileStream, $1000);
try
for Column := 0 to 4 do
for Row := 0 to 4 do
begin
Size := Reader.ReadInteger;
MemoryStream.SetSize(Size);
Buffer := MemoryStream.Memory;
Reader.Read(Buffer^, Size);
Bitmaps[Column, Row].LoadFromStream(MemoryStream);
end;
finally
Reader.Free;
FileStream.Free;
MemoryStream.Free;
end;
DrawGrid1.Repaint;
SaveFilename := OpenDialog2.Filename;
Caption := 'Bingo-создатель - ' + ExtractFilename(SaveFilename);
end;
|
Сохранить содержимое буфера обмена или потока как картинку.
Напрямую, конечно, нет. вставь в richedit и так:
procedure TForm1.Button1Click(Sender: TObject);
var
b : tbitmap;
fr: TFormatRange;
r : TRect;
begin
b:=tbitmap.create;
b.width:=rxrichedit1.width;
b.height:=rxrichedit1.height;
r:=rect(0,0,RXRichEdit1.Width*screen.Pixelsperinch,
RXRichEdit1.Height*screen.Pixelsperinch);
fr.hdc:=b.Canvas.handle;
fr.hdctarget:=b.Canvas.handle;
fr.rc:=r;
fr.rcpage:=r;
fr.chrg.cpMin:=0;
fr.chrg.cpMax:=-1;
Sendmessage(RXRichEdit1.handle,EM_FORMATRANGE,1,longint(@fr));
image1.Picture.assign(b);
b.free;
end;
|
Сохранение компонента в файле.
Как мне сохранить а потом загрузить целый TListView (ViewStyle=vsReport) со всеми ячейками в файле?
var f: TFileStream;
begin
f := TFileStream.Create('c:\stream.vcl', fmcreate);
f.WriteComponent(lb);
f.Free;
end;
...
var
f: TFileStream;
begin
f := TFileStream.Create('c:\stream.vcl', fmOpenRead);
f.ReadComponent(lvFiles);
f.Free;
|
Сохранение состояния ВСЕХ компонентов.
Автор: Святослав WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохраняет состояние !!!ВСЕХ!!! компонентов (втч и вложенных, втч TYesOrNoDialog)
на форме в реестр. Знает кучу классов. Очень удобен, напримр, для руссификации приложения
(Создаете *.reg файл, с переведенными property text, caption и т.д.)
TStateSaver.RegistryPath:String - то мето в реестре, куда сохранять.
.WriteTop, .WriteLeft, .WriteHeight, ... :Boolean - сохранять ли соответствующее
Property (нет динамического списка, который можно было бы реализовать, используя
GetFieldAddress, по причине невозможности определить состояние ReadOnly :( )
property OnNewComponentSaving:TSaverEvent read _ONNC write _ONNC;
property OnNewComponentLoading:TSaverEvent read _ONNCL write _ONNCL; - еvеnt'ы
вызывающиеся при сохранении/загрузки состояния какого-то компонента.
Параметр DoIt:Boolean - сохранить/загрузить или нет.
procedure SaveComponentState(C:TComponent; preffix, postfix:String);
procedure LoadComponentState(C:TComponent; preffix, postfix:String);
-сохранить/загрузить состояние всех под-компонентов компонента C.
preffix и postfic - префикс и постфикс имени при сохранении в реестр.
Зависимости: Windows, Messages, SysUtils, Classes, Registry, Dialogs, Controls,
StdCtrls, ExtCtrls, Buttons, UBPFD.YesOrNoDialog, Menus;
Автор: Святослав, lisin@asicdesign.ru, ICQ:138752432, Saint Petersburg
Copyright: (C) NetBreaker666[AWD]<SP666>@Svjatoslav_Lisin - т.е. я сам
Дата: 11 августа 2002 г.
***************************************************** }
unit StateSaver;
interface
uses
Windows, Messages, SysUtils, Classes, Registry, Dialogs, Controls, StdCtrls,
ExtCtrls, Buttons, YesOrNoDialog, Menus;
type
TSaverEvent = procedure(Sender: TObject; Target: TComponent; var DoIt: Boolean)
of object;
TStateSaver = class(TComponent)
private
{ Private declarations }
RegPath: string;
RegRoot: string;
RegRootHKEY: HKEY;
WTOP, WLEFT, WWIDTH, WHEIGHT, WTAG: Boolean;
WCAPTION, WTEXT, WCOLOR: Boolean;
WEnabled, WVisible, WChecked: Boolean;
_ONNC, _ONNCL: TSaverEvent;
procedure SetRegRoot(S: string);
procedure SetRegRootHKEY(HK: HKEY);
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property RegistryRoot: string read RegROOT write SetRegROOT;
property RegistryRootHKEY: HKey read RegRootHKEY write SetRegRootHKEY default
HKEY_CURRENT_USER;
property RegistryPath: string read RegPath write RegPath;
property WriteTop: Boolean read WTOP write WTOP;
property WriteLeft: Boolean read WLeft write WLeft;
property WriteWidth: Boolean read WWIDTH write WWidth;
property WriteHeight: Boolean read WHEIGHT write WHeight;
property WriteCaption: Boolean read WCaption write WCaption;
property WriteText: Boolean read WText write WText;
property WriteColor: boolean read WColor write WColor;
property WriteTag: Boolean read WTAG write WTag;
property WriteEnabled: Boolean read WEnabled write WEnabled;
property WriteVisible: Boolean read WVisible write WVisible;
property WriteChecked: Boolean read WChecked write WChecked;
property OnNewComponentSaving: TSaverEvent read _ONNC write _ONNC;
property OnNewComponentLoading: TSaverEvent read _ONNCL write _ONNCL;
procedure SaveComponentState(C: TComponent; preffix, postfix: string);
procedure LoadComponentState(C: TComponent; preffix, postfix: string);
end;
TUPC = class(TControl)
public
property Color;
property Caption;
property Text;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('NetBreakers', [TStateSaver]);
end;
procedure TStateSaver.SetRegRoot(S: string);
begin
S := UpperCase(S);
if S = 'HKEY_LOCAL_MACHINE' then
begin
RegRootHKEY := HKEY_LOCAL_MACHINE;
RegRoot := S;
Exit;
end;
if S = 'HKEY_CURRENT_USER' then
begin
RegRootHKEY := HKEY_CURRENT_USER;
RegRoot := S;
Exit;
end;
if S = 'HKEY_CLASSES_ROOT' then
begin
RegRootHKEY := HKEY_CLASSES_ROOT;
RegRoot := S;
Exit;
end;
if S = 'HKEY_USERS' then
begin
RegRootHKEY := HKEY_USERS;
RegRoot := S;
Exit;
end;
if S = 'HKEY_PERFORMANCE_DATA' then
begin
RegRootHKEY := HKEY_PERFORMANCE_DATA;
RegRoot := S;
Exit;
end;
if S = 'HKEY_CURRENT_CONFIG' then
begin
RegRootHKEY := HKEY_CURRENT_CONFIG;
RegRoot := S;
Exit;
end;
if S = 'HKEY_DYN_DATA' then
begin
RegRootHKEY := HKEY_DYN_DATA;
RegRoot := S;
Exit;
end;
ShowMessage('Invalid registry key.');
end;
procedure TStateSaver.SetRegRootHKEY(HK: HKEY);
begin
case HK of
HKEY_LOCAL_MACHINE:
begin
RegRoot := 'HKEY_LOCAL_MACHINE';
end;
HKEY_CURRENT_USER:
begin
RegRoot := 'HKEY_CURRENT_USER';
end;
HKEY_CLASSES_ROOT:
begin
RegRoot := 'HKEY_CLASSES_ROOT';
end;
HKEY_USERS:
begin
RegRoot := 'HKEY_USERS';
end;
HKEY_PERFORMANCE_DATA:
begin
RegRoot := 'HKEY_PERFORMANCE_DATA';
end;
HKEY_CURRENT_CONFIG:
begin
RegRoot := 'HKEY_CURRENT_CONFIG';
end;
HKEY_DYN_DATA:
begin
RegRoot := 'HKEY_DYN_DATA';
end;
else
begin
ShowMessage('Unknown registry key.');
Exit;
end;
end;
RegRootHKEY := HK;
end;
procedure TStateSaver.SaveComponentState(C: TComponent; preffix, postfix:
string);
var
T: TControl;
R: TRegistry;
I: Integer;
CC: Boolean;
begin
CC := True;
if Assigned(_ONNC) then
_ONNC(self, C, CC);
if CC then
begin
if C is TControl then
begin
T := C as TControl;
R := TRegistry.Create;
R.RootKey := RegRootHKEY;
if R.OpenKey(RegPath, True) then
begin
try
if WTOP then
R.WriteInteger(preffix + C.GetNamePath + '.TOP' + postfix, T.Top);
except
end;
try
if WEnabled then
R.WriteBool(preffix + C.GetNamePath + '.Enabled' + postfix,
T.Enabled);
except
end;
try
if WVisible then
R.WriteBool(preffix + C.GetNamePath + '.TOP' + postfix, T.Visible);
except
end;
try
if WLEFT then
R.WriteInteger(preffix + C.GetNamePath + '.LEFT' + postfix, T.Left);
except
end;
try
if WTAG then
R.WriteInteger(preffix + C.GetNamePath + '.TAG' + postfix, T.Tag);
except
end;
try
if WHEIGHT then
R.WriteInteger(preffix + C.GetNamePath + '.HEIGHT' + postfix,
T.Height);
except
end;
try
if WWIDTH then
R.WriteInteger(preffix + C.GetNamePath + '.WIDTH' + postfix,
T.Width);
except
end;
if WTEXT then
begin
try
R.WriteString(preffix + C.GetNamePath + '.Text' + postfix,
TUPC(T).Text);
except
try
if T is TCustomEdit then
R.WriteString(preffix + C.GetNamePath + '.Text' + postfix,
TCustomEdit(T).Text);
except
end;
end;
end;
if WCOLOR then
begin
try
R.WriteInteger(preffix + C.GetNamePath + '.Color' + postfix,
Integer(TUPC(T).color));
except
end;
end;
if WCAPTION then
begin
try
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TUPC(T).caption);
except
try
if T is TButton then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TButton(T).Caption);
if T is TCustomLabel then
R.WriteString(preffix + C.GetNamePath + '.caption' + postfix,
TCustomLabel(T).Caption);
if T is TCheckBox then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TCheckBox(T).Caption);
if T is TRadioButton then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TRadioButton(T).Caption);
if T is TGroupBox then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TGroupBox(T).Caption);
if T is TRadioGroup then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TRadioGroup(T).Caption);
if T is TPanel then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TPanel(T).Caption);
if T is TSpeedButton then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TSpeedButton(T).Caption);
if T is TStaticText then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TStaticText(T).Caption);
except
end;
end;
end;
end
else
begin
//ShowMessage('Couldn''t open key "'+RegPath+'".');
Exit;
end;
R.Free;
end
else
begin
if C is TYesOrNoDialog then
begin
R := TRegistry.Create;
R.RootKey := RegRootHKEY;
if R.OpenKey(RegPath, True) then
begin
if WCaption then
R.WriteString(preffix + C.GetNamePath + '.Caption' + postfix,
TYesOrNoDialog(C).caption);
if WText then
R.WriteString(preffix + C.GetNamePath + '.Text' + postfix,
TYesOrNoDialog(C).Text);
end;
R.Free;
end;
if C is TPopupMenu then
begin
R := TRegistry.Create;
R.RootKey := RegRootHKEY;
if R.OpenKey(RegPath, True) then
begin
for I := 0 to TPopupMenu(C).Items.Count - 1 do
begin
if WCaption then
R.WriteString(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Caption' + postfix, TPopupMenu(C).Items[I].caption);
if WEnabled then
R.WriteBool(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Enabled' + postfix, TPopupMenu(C).Items[I].Enabled);
if WVisible then
R.WriteBool(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Visible' + postfix, TPopupMenu(C).Items[I].Visible);
if WChecked then
R.WriteBool(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Checked' + postfix, TPopupMenu(C).Items[I].Checked);
end;
end;
R.Free;
end;
end;
end;
for I := 0 to C.ComponentCount - 1 do
SaveComponentState(C.Components[i], preffix + C.GetNamePath + '.', postfix);
end;
procedure TStateSaver.LoadComponentState(C: TComponent; preffix, postfix:
string);
var
T: TControl;
R: TRegistry;
I: Integer;
CC: Boolean;
begin
CC := True;
if Assigned(_ONNCL) then
_ONNCL(self, C, CC);
if CC then
begin
if C is TControl then
begin
T := C as TControl;
R := TRegistry.Create;
R.RootKey := RegRootHKEY;
if R.OpenKey(RegPath, False) then
begin
try
if WTOP then
if R.ValueExists(preffix + C.GetNamePath + '.TOP' + postfix) then
T.Top := R.ReadInteger(preffix + C.GetNamePath + '.TOP' +
postfix);
except
end;
try
if WEnabled then
if R.ValueExists(preffix + C.GetNamePath + '.Enabled' + postfix)
then
T.Enabled := R.ReadBool(preffix + C.GetNamePath + '.Enabled' +
postfix);
except
end;
try
if WVisible then
if R.ValueExists(preffix + C.GetNamePath + '.TOP' + postfix) then
T.Visible := R.ReadBool(preffix + C.GetNamePath + '.TOP' +
postfix);
except
end;
try
if WLEFT then
if R.ValueExists(preffix + C.GetNamePath + '.LEFT' + postfix) then
T.Left := R.ReadInteger(preffix + C.GetNamePath + '.LEFT' +
postfix);
except
end;
try
if WTAG then
if R.ValueExists(preffix + C.GetNamePath + '.TAG' + postfix) then
T.Tag := R.ReadInteger(preffix + C.GetNamePath + '.TAG' +
postfix);
except
end;
try
if WHEIGHT then
if R.ValueExists(preffix + C.GetNamePath + '.HEIGHT' + postfix) then
T.Height := R.ReadInteger(preffix + C.GetNamePath + '.HEIGHT' +
postfix);
except
end;
try
if WWIDTH then
if R.ValueExists(preffix + C.GetNamePath + '.WIDTH' + postfix) then
T.Width := R.ReadInteger(preffix + C.GetNamePath + '.WIDTH' +
postfix);
except
end;
if WTEXT then
if R.ValueExists(preffix + C.GetNamePath + '.Text' + postfix) then
begin
try
TUPC(T).Text := R.ReadString(preffix + C.GetNamePath + '.Text' +
postfix);
except
try
if T is TCustomEdit then
TCustomEdit(T).Text := R.ReadString(preffix + C.GetNamePath +
'.Text' + postfix);
except
end;
end;
end;
if WCOLOR then
if R.ValueExists(preffix + C.GetNamePath + '.Color' + postfix) then
begin
try
TUPC(T).Color := R.ReadInteger(preffix + C.GetNamePath + '.Color'
+ postfix);
except
end;
end;
if WCaption then
if R.ValueExists(preffix + C.GetNamePath + '.Caption' + postfix) then
begin
try
TUPC(T).Caption := R.ReadString(preffix + C.GetNamePath +
'.Caption' + postfix);
except
try
if T is TButton then
TButton(T).Caption := R.ReadString(preffix + C.GetNamePath +
'.Caption' + postfix);
if T is TCustomLabel then
TCustomLabel(T).Caption := R.ReadString(preffix + C.GetNamePath
+ '.caption' + postfix);
if T is TCheckBox then
TCheckBox(T).Caption := R.ReadString(preffix + C.GetNamePath +
'.Caption' + postfix);
if T is TRadioButton then
TRadioButton(T).Caption := R.ReadString(preffix + C.GetNamePath
+ '.Caption' + postfix);
if T is TGroupBox then
TGroupBox(T).Caption := R.ReadString(preffix + C.GetNamePath +
'.Caption' + postfix);
if T is TRadioGroup then
TRadioGroup(T).Caption := R.ReadString(preffix + C.GetNamePath
+ '.Caption' + postfix);
if T is TPanel then
TPanel(T).Caption := R.ReadString(preffix + C.GetNamePath +
'.Caption' + postfix);
if T is TSpeedButton then
TSpeedButton(T).Caption := R.ReadString(preffix + C.GetNamePath
+ '.Caption' + postfix);
if T is TStaticText then
TStaticText(T).Caption := R.ReadString(preffix + C.GetNamePath
+ '.Caption' + postfix);
except
end;
end;
end;
end;
R.Free;
end
else
begin
if C is TYesOrNoDialog then
begin
R := TRegistry.Create;
R.RootKey := RegRootHKEY;
if R.OpenKey(RegPath, False) then
begin
if WCaption then
if R.ValueExists(preffix + C.GetNamePath + '.Caption' + postfix)
then
TYesOrNoDialog(C).caption := R.ReadString(preffix + C.GetNamePath +
'.Caption' + postfix);
if WText then
if R.ValueExists(preffix + C.GetNamePath + '.Text' + postfix) then
TYesOrNoDialog(C).text := R.ReadString(preffix + C.GetNamePath +
'.Text' + postfix);
end;
R.Free;
end;
if C is TPopupMenu then
begin
R := TRegistry.Create;
R.RootKey := RegRootHKEY;
if R.OpenKey(RegPath, True) then
begin
for I := 0 to TPopupMenu(C).Items.Count - 1 do
begin
if WCaption then
if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Caption' + postfix) then
TPopupMenu(C).Items[I].caption := R.ReadString(preffix +
C.GetNamePath + '.Item[' + IntToStr(I) + '].Caption' + postfix);
if WEnabled then
if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Enabled' + postfix) then
TPopupMenu(C).Items[I].Enabled := R.ReadBool(preffix +
C.GetNamePath + '.Item[' + IntToStr(I) + '].Enabled' + postfix);
if WVisible then
if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Visible' + postfix) then
TPopupMenu(C).Items[I].Visible := R.ReadBool(preffix +
C.GetNamePath + '.Item[' + IntToStr(I) + '].Visible' + postfix);
if WChecked then
if R.ValueExists(preffix + C.GetNamePath + '.Item[' + IntToStr(I) +
'].Checked' + postfix) then
TPopupMenu(C).Items[I].Checked := R.ReadBool(preffix +
C.GetNamePath + '.Item[' + IntToStr(I) + '].Checked' + postfix);
end;
end;
R.Free;
end;
end;
end;
for I := 0 to C.ComponentCount - 1 do
LoadComponentState(C.Components[i], preffix + C.GetNamePath + '.', postfix);
end;
end.
Сохранение компонента полностью со всеми свойствами в БД.
Автор: Z-man WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение компонента полностью, со всеми свойствами в БД.
Данная функция использовалась и используется для сохранения отчетов,
формируемые пользователем, в базу данных. БД используется: Informix и Oracle,
под другие БД не пробовал. Отчеты строились в ReportBuilder5.5.
Для других компонентов не пробывал использовать
Зависимости: стандартные
Автор: Z-man, nik7777@land.ru, ICQ:160236098, Astana
Copyright: Z-man ®
Дата: 28 мая 2002 г.
***************************************************** }
function ComponentToString(Component: TComponent): string;
var
BinStream: TMemoryStream;
StrStream: TStringStream;
s: string;
begin
BinStream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
BinStream.WriteComponent(Component);
BinStream.Seek(0, soFromBeginning);
ObjectBinaryToText(BinStream, StrStream);
StrStream.Seek(0, soFromBeginning);
Result := StrStream.DataString;
finally
StrStream.Free;
end;
finally
BinStream.Free
end;
end;
Пример использования:
OraTReport.FieldByName('report').AsString := ComponentToString(rbReport);
//* где: *//
//* rbReport - ReportBuilder5 (желтый отчет)*//
//* OraTReport - TOraTable из пакета компонентов ODAC (визуальные *//
//* компоненты для прямого доступа к Oracle)*//
Сохранение данных в Clipboard.
Автор: Vladimir Timonin [Q:]Мне нужно использовать clipboard для сохранения данных в собственном формате и я хочу для этого написать набор процедур ввода/вывода с использованием потоков (streams). Возможно ли создать объект TMemoryStream, эаполнить его и поместить в Clipboard?[A:]Не только возможно, именно так поступают функции Clipboard.GetComponent и Clipboard.SetComponent. Сначала вы должны зарегистрировать свой собственный формат данных для Clipboard с помощью функции RegisterClipboardFormat:
CF_MYFORMAT := RegisterClipboardFormat('My Format Description');
Далее вы должны выполнить шаги:
1. Создать поток (memory stream) и записать туда данные.
2. Создать глобальный буфер в памяти и скопировать поток туда.
3. Вызвать Clipboard.SetAsHandle(), чтобы поместить буфер в Clipboard.
Пример:
var
hBuf: THandle;
Bufptr: Pointer;
MStream: TMemoryStream;
begin
MStream := TMemoryStream.Create;
try
{ write your data to the stream }
hBuf := GlobalAlloc(GMEM_MOVEABLE, MStream.Size);
try
BufPtr := GlobalLock(hBuf);
try
Move(MStream.Memory^, BufPtr^, MStream.Size);
Clipboard.SetAsHandle(CF_MYFORMAT, hBuf);
finally
GlobalUnlock(hBuf);
end;
except
GlobalFree(hBuf);
raise;
end;
finally
MStream.Free;
end;
end;
|
Для обратного получения потока и данных, сделайте что-нибудь вроде этого:
var
hBuf: THandle;
BufPtr: Pointer;
MStream: TMemoryStream;
begin
hBuf := Clipboard.GetAsHandle(CF_MYFORMAT);
if hBuf <> 0 then
begin
BufPtr := GlobalLock(hBuf);
if BufPtr <> nil then
try
MStream := TMemoryStream.Create;
try
MStream.WriteBuffer(BufPtr^, GlobalSize(hBuf));
MStream.Position := 0;
{ read your data from the stream }
finally
MStream.Free;
end;
finally
GlobalUnlock(hBuf);
end;
end;
end;
|
Сохранить порядок колонок в DBGrid, после того как пользовать их перестроил.
| РЕКЛАМА!!! Телефонный звонок в 2 часа ночи. Сонный мужик подходит к телефону: - АЛЛО?!!! Тишина. Кладет трубку. Ровно через 45 секунд опять звонок. - АЛЛО?!!!! Тишина. И так повторяентся 74 раза с регулярностью 45 секунд. Мужик: - ПИИИИИИИи (длинный продолжительный писк, которым на телевидении заглушают матные слова) На другом конце провода довольный СисОп с лыбой во всю широту натуры, обнимающий небольшую коробочку. Голос за кадром: - Делай всё, что тебе нравится вместе с модемом ZyXel!!! |
procedure TMainForm.NewIni(const NomeIni: string);
var F: System.Text;
i: Byte;
begin
System.Assign(F, NomeIni);
System.ReWrite(F);
System.WriteLn(F, '[Campi_Ordine]');
for i:=1 to Table1.FieldCount do
System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].FieldName);
System.WriteLn(F, '');
System.WriteLn(F, '[Campi_Size]');
for i:=1 to Table1.FieldCount do
System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].DisplayWidth);
System.Close(F);
end;
procedure TMainForm.SaveIni(const FN: String);
var Ini: TIniFile;
i: Integer;
begin
NewIni(FN);
Ini := TIniFile.Create(FN);
with Ini do
begin
for i:=1 to Table1.FieldCount do
begin
S:= Table1.Fields[i-1].FieldName;
WriteString('Campi_Ordine', 'Campo'+IntToStr(i),
Table1.Fields[i-1].FieldName);
WriteInteger('Campi_Size', 'Campo'+IntToStr(i),
Table1.Fields[i-1].DisplayWidth);
end;
end;
Ini.Free;
end;
procedure TMainForm.LoadIni(const FN: String);
var Ini: TIniFile;
i: Integer;
j: Longint;
S: String;
function MyReadInteger(const Section, Ident: string): Longint;
begin
result := Ini.ReadInteger(Section, Ident, -1);
if result=-1 then
raise Exception.Create('Errore nel file di configurazione.');
end;
function MyReadString(const Section, Ident: string): String;
begin
result := Ini.ReadString(Section, Ident, '');
if result='' then
raise Exception.Create('Errore nel file di configurazione.');
end;
begin
Ini := TIniFile.Create(FN);
try
with Ini do
begin
for i:=1 to Table1.FieldCount do
begin
S:= MyReadString('Campi_Ordine', 'Campo'+IntToStr(i));
j:= MyReadInteger('Campi_Size', 'Campo'+IntToStr(i));
Table1.FieldByName(S).Index := i-1;
Table1.FieldByName(S).DisplayWidth := j;
end;
end;
finally
Ini.Free;
end;
end;
|
Сохранить Excel файл как текстовый.
uses
ComObj;
function ExcelSaveAsText(ExcelFile, TextFile: TFileName): Boolean;
const
xlText = -4158;
var
ExcelApp: OleVariant;
vTemp1, vTemp2, vTemp3: OLEVariant;
begin
Result := False;
try
ExcelApp := CreateOleObject('Excel.Application');
except
// Fehler beim цffnеn von Excel...
// Error occured...
Exit;
end;
try
ExcelApp.Workbooks.Open(ExcelFile);
ExcelApp.DisplayAlerts := False;
vTemp3 := False;
vTemp2 := xlText;
vTemp1 := TextFile;
ExcelApp.ActiveWorkbook.SaveAs(vTemp1, vTemp2, vTemp3);
Result := True;
finally
ExcelApp.Quit;
ExcelApp := Unassigned;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ExcelSaveAsText('C:\YouExcelFile.xls','C:\YourTextFile.txt');
end;
|
Сохранение размеров, позиции и состояния окна.
Приведу код WindowRestorer - восстановителя размера и состояния окна. ОПИСАНИЕ: Вы наверное замечали, что профессионально написанные программы "запоминают" состояние и позицию окон с момента их последнего показа? А большинство RAD-приложений это игнорируют? Вы можете исправить эту ошибку, взяв на вооружение этот модуль. Он позволяет сохранять позицию, размер и состояние окна, поэтому пользователь, открыв его вновь, увидит его в том же состоянии, в котором он видел его в последний раз. ИСПОЛЬЗОВАНИЕ: Поместите WINRSTOR в список используемых модулей главной или любой другой формы, состояние, размер и позицию которой вы хотите сохранить. (Если вы сэкономить время и для восстановления дочерних форм использовать WinSaveChildren и WinRestoreChildren из главной формы, вы должны объявить этот модуль только в главной форме.)В MainForm.Create, инициализируйте глобальный объект WinRestorer следующим образом (он должен предварительно быть объявлен, но еще не инициализирован):
GlobalWinRestorer := TWinRestorer.create( Application, TRUE, WHATSAVE_ALL); |
Или так:
GlobalWinRestorer := TWinRestorer.create( Application, TRUE, [location, size, state]); |
Затем в MainForm.Destroy вы должны разрушить глобальный объект WinRestorer следующим образом:
GlobalWinRestorer.free; |
Хорошее место для сохранения статуса формы - в обработчике события queryclose или в специально созданной кнопке или пункте меню. Я обычно создаю этот пункт в меню "Файл" с именем '&Сохранение рабочей области' и обрабатываю следующим образом:
GlobalWinRestorer.SaveChildren(Self, [default]); |
И при закрытии основной формы необходимо сделать следующее:
GlobalWinRestorer.SaveWin(Self, [WHATSAVE_ALL]); |
Восстановить состояние дочерних форм можно следующим образом:
GlobalWinRestorer.RestoreWin(Self, [default]); |
Я же переместил данный код в обработчик события show моей главной формы:
GlobalWinRestorer.RestoreWin(Self, [default]); GlobalWinRestorer.RestoreChildren(Self, [default]); |
Подсказки: Если вы установили TForm.Position в poScreenCenter или что-то подобное, данный модуль вам не поможет. poDesigned кажется, работает как положено. Можно добавить обработку исключения, если вы пытаетесь установить верхнюю или левую позицию при значении формы poScreenCentere, но при этом вы должны быть осторожными при использовании WinRestoreChildren. Я не проверял это со значениями координат (позиции) и оставил это на усмотрение разработчиков.
unit WinRstor;
interface
uses SysUtils, Forms;
type
{=============================================================}
{------------------------------------------------------------------
Восстановитель окон классовых объектов и связанных типов.
-------------------------------------------------------------------}
EWinRestorer = class(Exception);
TWhatSave = (default, size, location, state);
STWhatSave = set of TWhatSave;
TWinRestorer = class(TObject)
protected
mIniFile: string;
mIniSect: string[80];
mIsInitialized: boolean;
mDefaultWhat: STWhatSave;
public
constructor Create(TheApp: TApplication;
LocalDir: boolean; DefaultWhatSave: STWhatSave);
{Если localDir = true, каталог ini = каталогу приложения.
Else, ini dir is the windows dir.}
procedure SaveWin(TheForm: TForm; What: STWhatSave);
procedure SaveChildren(TheMDIForm: TForm; What: STWhatSave);
procedure RestoreWin(TheForm: TForm; What: STWhatSave);
procedure RestoreChildren(TheMDIForm: TForm; What: STWhatSave);
property IniFileName: string read mIniFile;
end;
const
WHATSAVE_ALL = [size, location, state];
var
GlobalWinRestorer: TWinRestorer;
implementation
uses IniFiles;
constructor TWinRestorer.create;
var
fname, path: string[100];
begin
inherited create;
{Получаем имя ini-файла}
if default in DefaultWhatSave then
raise EWinRestorer.create(
'Попытка инициализации параметров с позицией окна по умолчанию ' +
' с установленным элементом [default]. ' +
'Параметры по умолчанию могут содержать только установленные элементы -
[size, location, state]. ')
else
mDefaultWhat := DefaultWhatSave;
fname := ChangeFileExt(ExtractFileName(TheApp.exeName), '.INI');
if LocalDir then
begin {вычисляем путь и добавляем к нему имя файла}
path := ExtractFilePath(TheApp.exeName);
if path[length(path)] <> '\' then
path := path + '\';
fname := path + fname;
end;
{заполняем поля объекта}
mIniFile := fname;
mIniSect := 'WindowsRestorer';
{Для культуры напишем некоторое примечание
в секцию с именем [WinRestorer Notes]}
end;
procedure TWinRestorer.RestoreWin;
var
FormNm, SectionNm: string[80];
ini: TIniFile;
n, l, t, w, h: integer; {Left, Top Width, Height}
begin
ini := TIniFile.create(mIniFile);
try
SectionNm := mIniSect;
FormNm := TheForm.classname;
if default in What then
What := mDefaultWhat;
{При необходимости обновляем состояние окна}
if state in What then
n := ini.ReadInteger(SectionNm, FormNm + '_WindowState', 0);
case n of
1: TheForm.WindowState := wsMinimized;
2: TheForm.WindowState := wsNormal;
3: TheForm.WindowState := wsMaximized;
end;
{При необходимости обновляем размеры и позицию.}
with TheForm do
begin
l := left;
t := top;
h := height;
w := width;
end; {Сохраняем текущие значения.}
if size in What then
begin
w := ini.ReadInteger(SectionNm, FormNm + '_Width', w);
h := ini.ReadInteger(SectionNm, FormNm + '_Height', h);
end;
if location in What then
begin
t := ini.ReadInteger(SectionNm, FormNm + '_Top', t);
l := ini.ReadInteger(SectionNm, FormNm + '_Left', l);
end;
TheForm.SetBounds(l, t, w, h);
finally
ini.free;
end;
end;
procedure TWinRestorer.RestoreChildren;
var
i: integer;
begin
if TheMDIForm.formstyle <> fsMDIForm then
raise
EWinRestorer.create('Попытка сохранения размеров дочернего
окна для не-MDI окна родителя.')
else
for i := 0 to TheMDIForm.MDIChildCount - 1 do
RestoreWin(TheMDIForm.MDIChildren[i], what);
end;
procedure TWinRestorer.SaveWin;
var
FormNm, SectionNm: string[80];
w: STWhatsave;
ini: TIniFile;
begin
ini := TIniFile.create(mIniFile);
try
SectionNm := mIniSect;
FormNm := TheForm.ClassName;
if default in What then
w := mDefaultWhat
else
w := mDefaultWhat;
if size in w then
begin
ini.WriteInteger(SectionNm, FormNm + '_Width', TheForm.Width);
ini.WriteInteger(SectionNm, FormNm + '_Height', TheForm.Height);
end;
if location in w then
begin
ini.WriteInteger(SectionNm, FormNm + '_Top', TheForm.Top);
ini.WriteInteger(SectionNm, FormNm + '_Left', TheForm.Left);
end;
if state in w then
case TheForm.WindowState of
wsMinimized: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 1);
wsNormal: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 2);
wsMaximized: ini.WriteInteger(SectionNm, FormNm + '_WindowState', 3);
end;
finally
ini.free;
end;
end;
procedure TWinRestorer.SaveChildren;
var
i: integer;
begin
if TheMDIForm.formstyle <> fsMDIForm then
raise
EWinRestorer.create('Попытка восстановления размеров дочернего
окна для не-MDI окна родителя.')
else
for i := 0 to TheMDIForm.MDIChildCount - 1 do
SaveWin(TheMDIForm.MDIChildren[i], what);
end;
initialization
end.
|
Сохранение в файле и загрузка из файла формы с компонентами.
unit Cref_f;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls,
ExtCtrls, Menus;
type
CRefType = class of TControl;
TForm1 = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Exit1: TMenuItem;
N1: TMenuItem;
SaveAs1: TMenuItem;
Open1: TMenuItem;
New1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
Panel1: TPanel;
RadioRadioButton: TRadioButton;
ButtonRadioButton: TRadioButton;
EditRadioButton: TRadioButton;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
SaveAs2: TMenuItem;
N2: TMenuItem;
SaveDialog2: TSaveDialog;
MemoRadioButton: TRadioButton;
procedure RadioButtonRadioClick(Sender: TObject);
procedure RadioButtonButtonClick(Sender: TObject);
procedure RadioButtonEditClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure New1Click(Sender: TObject);
procedure Open1Click(Sender: TObject);
procedure SaveAs1Click(Sender: TObject);
procedure About1Click(Sender: TObject);
procedure Exit1Click(Sender: TObject);
procedure SaveAs2Click(Sender: TObject);
procedure MemoRadioButtonClick(Sender: TObject);
private
ClassRef: CRefType;
Counter: Integer;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.RadioButtonRadioClick(Sender: TObject);
begin
ClassRef := TRadioButton;
end;
procedure TForm1.RadioButtonButtonClick(Sender: TObject);
begin
ClassRef := TButton;
end;
procedure TForm1.RadioButtonEditClick(Sender: TObject);
begin
ClassRef := TEdit;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ClassRef := TRadioButton;
Counter := 0;
end;
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
MyObj: TControl;
MyName: String;
begin
{create an object using the current class reference}
MyObj := ClassRef.Create (self);
MyObj.Visible := False;
MyObj.Parent := self;
MyObj.Left := X;
MyObj.Top := Y;
Inc (Counter);
{define the name using the class name, without the
initial T, and the number of the Counter}
MyName := ClassRef.ClassName + IntToStr (Counter);
Delete (MyName, 1, 1);
MyObj.Name := MyName;
MyObj.Visible := True;
end;
procedure TForm1.New1Click(Sender: TObject);
var
I: Integer;
begin
{delete all existing components, except the panel}
for I := ControlCount - 1 downto 0 do
if Controls[I].ClassName <> 'TPanel' then
Controls[I].Free;
Counter := 0;
end;
procedure TForm1.Open1Click(Sender: TObject);
var
S: TFileStream;
New: TComponent;
Reader1: TReader; // new line
begin
if OpenDialog1.Execute then
begin
{remove existing controls}
New1Click (self);
{open the stream}
S := TFileStream.Create (OpenDialog1.FileName,
fmOpenRead);
try
while S.Position < S.Size do
begin
{read a component and add it to the form}
// old code:
// New := S.ReadComponent (nil);
// InsertControl (New as TControl);
// new code:
Reader1 := TReader.Create (S, 4096);
try
Reader1.Parent := self;
New := Reader1.ReadRootComponent (nil);
InsertComponent (New);
finally
Reader1.Free;
end;
Inc (Counter);
end;
finally
S.Free;
end;
end;
end;
procedure TForm1.SaveAs1Click(Sender: TObject);
var
S: TFileStream;
I: Integer;
begin
if SaveDialog1.Execute then
begin
{open or create the stream file}
S := TFileStream.Create (SaveDialog1.FileName,
fmOpenWrite or fmCreate);
try
{save each component except the panel}
for I := 0 to ControlCount - 1 do
if Controls[I].ClassName <> 'TPanel' then
S.WriteComponent (Controls[I]);
finally
S.Free;
end;
end;
end;
procedure TForm1.About1Click(Sender: TObject);
begin
MessageDlg ('CREF2 Example: Save components to file' +
Chr(13) + 'From "Mastering Delphi", by Marco Саntщ',
mtInformation, [mbOk], 0);
end;
procedure TForm1.Exit1Click(Sender: TObject);
begin
Close;
end;
{save form file}
procedure TForm1.SaveAs2Click(Sender: TObject);
begin
if SaveDialog2.Execute then
WriteComponentResFile
(SaveDialog2.Filename, self);
end;
procedure TForm1.MemoRadioButtonClick(Sender: TObject);
begin
ClassRef := TMemo;
end;
initialization
{register the classes of the components; this code is
required by the stream loader}
RegisterClasses ([TRadioButton, TEdit, TButton, TMemo]);
end.
|
Загрузить весь проект
Сохранить изображение в формате JPEG.
| Пришел програмист фотки печатать дал негатив служащим а они его спрашивают: - В каком формате печатать? - В "jpg" конечно |
В комплект поставки Delphi входит модуль JPEG. Он позволяет работать с изображениями в формате JPEG. Эта программа сохраняет изображение экрана в файле C:\Screen.jpg.
uses Jpeg;
procedure TForm1.Button1Click(Sender: TObject);
var
JpegIm: TJpegImage;
bm: TBitMap;
begin
bm := TBitMap.Create;
bm.Width := Screen.Width;
bm.Height := Screen.Height;
BitBlt(bm.Canvas.Handle, 0, 0,
bm.Width, bm.Height,
GetDC(0), 0, 0, SRCCOPY);
JpegIm := TJpegImage.Create;
JpegIm.Assign(bm);
JpegIm.CompressionQuality := 20;
JpegIm.Compress;
JpegIm.SaveToFile('C:\Screen.jpg');
bm.Destroy;
JpegIm.Destroy;
end;
|
Сохранение и загрузка данных в объекты на примере коллекций.
Если в Вашей программе используются классы для описания объектов некоторой предметной области, то данные, их инициализирующие, можно хранить и в базе данных. Но можно выбрать гораздо более продуктивный подход, который доступен в Delphi/C++ Builder. Среда разработки Delphi/C++ Builder хранит ресурсы всех форм в двоичных или текстовых файлах и эта возможность доступна и для разрабатываемых с ее помощью программ. В данном случае, для оценки удобств такого подхода лучше всего рассмотреть конкретный пример.
Необходимо реализовать хранение информации о некоей службе рассылки и ее подписчиках. Будем хранить данные о почтовом сервере и список подписчиков. Каждая запись о подписчике хранит его личные данные и адрес, а также список тем(или каталогов), на которые он подписан. Как большие поклонники Гради Буча (Grady Booch), а также будучи заинтересованы в удобной организации кода, мы организуем информацию о подписчиках в виде объектов. В Delphi для данной задачи идеально подходит класс TCollection, реализующий всю необходимую функциональность для работы со списками типизированных объектов. Для этого мы наследуемся от TCollection, называя новый класс TMailList, а также создаем наследника от TCollectionItem - TMailClient. Последний будет содержать все необходимые данные о подписчике, а также реализовывать необходимые функции для работы с ним.
Начнем с TMailClient.
type
TMailClient = class(TCollectionItem)
private
FName: string;
FAddress: string;
FEnabled: boolean;
FFolders: TStringList;
public
Files: TStringList;
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure PickFiles;
published
property name: string read FName write FName;
property Address: string read FAddress write FAddress;
property Enabled: boolean read FEnabled write FEnabled default true;
property Folders: TStringList read FFolders write FFolders;
end;
|
Класс содержит сведения о имени клиента, его адресе, его статусе(Enabled), а также список каталогов, на которые он подписан. Процедура PickFiles составляет список файлов к отправке и сохраняет его в свойстве Files. Класс TMailList, хранящий объекты класса TMailClient, приведен ниже.
TMailList = class(TCollection) public function GetMailClient(index: Integer): TMailClient; procedure SetMailClient(index: Integer; Value: TMailClient); public function Add: TMailClient; property Items[index: Integer]: TMailClient read GetMailClient write SetMailClient; default; end; |
Теперь поместим класс TMailList в класс TMailer, содержащий также и данные о параметрах доступа к почтовому серверу для отправки почты.
TMailer = class(TComponent)
private
name: string;
EMail: string;
FMailList: TMailList;
FLastActivated: TDateTime;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property MailList: TMailList read FMailList write FMailList;
property LastActivated: TDateTime read FLastActivated write FLastActivated;
end;
|
В данном случае мы наследуемся от класса TComponent, для того, чтобы была возможности записи данных объекта в файл. Свойство MailList содержит уже объект класса TMailList, а свойство LastActivated - дату последнего запуска программы рассылки.
Реализация всех приведенных классов приведена ниже.
constructor TMailClient.Create(Collection: TCollection);
begin
inherited;
Folders := TStringList.Create;
Files := TStringList.Create;
FEnabled := true;
end;
destructor TMailClient.Destroy;
begin
Folders.Free;
Files.Free;
inherited;
end;
procedure TMailClient.PickFiles;
var
i: integer;
begin
for i := 0 to Folders.Count - 1 do
CreateFileList(Files, Folders[i]);
end;
function TMailList.GetMailClient(index: Integer): TMailClient;
begin
Result := TMailClient(inherited Items[index]);
end;
procedure TMailList.SetMailClient(index: Integer; Value: TMailClient);
begin
Items[index].Assign(Value);
end;
function TMailList.Add: TMailClient;
begin
Result := TMailClient(inherited Add);
end;
constructor TMailer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
MailList := TMailList.Create(TMailClient);
FLastActivated := now;
end;
destructor TMailer.Destroy;
begin
MailList.Free;
inherited;
end;
|
Функция CreateFileList создает по каким-либо правилам список файлов на основе переданного ей списка каталогов, обходя их рекурсивно.
procedure CreateFileList(sl: TStringList; FilePath: string);
var
sr: TSearchRec;
procedure ProcessFile;
begin
if (sr.name = '.') or(sr.name = '..') then
exit;
if sr.Attr <> faDirectory then
sl.Add(FilePath + '\' + sr.name);
if sr.Attr = faDirectory then
CreateFileList(sl, FilePath + '\' + sr.name);
end;
begin
if not DirectoryExists(FilePath) then
exit;
if FindFirst(FilePath + '\' + Settings.IncludeFileMasks,
faAnyFile , sr) = 0 then
ProcessFile;
while FindNext(sr) = 0 do
ProcessFile;
FindClose(sr);
end;
|
В итоге мы располагаем классом TMailer, содержащим всю необходимую нам информацию. Теперь перейдем к созданию объекта, их сохранению и загрузке.
var
sDataFile: string;
MLN: TMailLateNight;
begin
MLN := TMailLateNight.Create(nil);
sDataFile := ExtractFilePath(ParamStr(0)) + 'users.dat';
//...загрузка данных из файла
if FileExists(sDatsFile) then
LoadUsersFromTextFile(MLN, sDatsFile);
...
//...работа с объектами
for i:=0 to MLN.MailList.Count-1 do
begin
s := MLN.MailList[i].name;
s := MLN.MailList[i].Address;
MLN.MailList[i].PickFiles;
for j:=0 to MLN.Files.Count-1 do
begin
s := MLN.MailList[i].Files[j];
...
//...сохранение данных в файл
SaveComponentToTextFile(MLN, sDataFile);
|
Хранение данных в файле позволяет оказаться от использования БД, если объем данных не слишком велик и нет необходимости в совместном доступе к данным.
Самое главное - мы организуем все данные в виде набора удобных для работы классов и не тратим время на их сохранение и инициализацию из БД.
Далее приведен код функций для сохранения/чтения компонента.
//...процедура удаляет все дочерние компоненты из формы
procedure DeleteComponents(Form: TForm);
var
i: integer;
begin
for i := Form.ComponentCount - 1 downto 0 do
Form.Components[i].Free;
end;
// ...процедура загружает(инициализирует)
// компонент из текстового файла с ресурсом
procedure LoadComponentFromTextFile(Component: TComponent;
FileName: string);
var
ms: TMemoryStream;
fs: TFileStream;
begin
ms := TMemoryStream.Create;
fs := TFileStream.Create(FileName, fmOpenRead);
try
ObjectTextToBinary(fs, ms);
ms.position := 0;
ms.ReadComponent(Component);
finally
ms.Free;
fs.free;
end;
end;
//...процедура сохраняет компонент в текстовый файл
procedure SaveComponentToTextFile(Component: TComponent;
FileName: string);
var
ms: TMemoryStream;
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmCreate or fmOpenWrite);
ms := TMemoryStream.Create;
try
ms.WriteComponent(Component);
ms.position := 0;
ObjectBinaryToText(ms, fs);
finally
ms.Free;
fs.free;
end;
end;
|
Сохранение и чтение TStringGrid.
procedure SaveGrid;
var
f: textfile;
x, y: integer;
begin
assignfile(f, 'Filename');
rewrite(f);
writeln(f, stringgrid.colcount);
writeln(f, stringgrid.rowcount);
for X := 0 to stringgrid.colcount - 1 do
for y := 0 to stringgrid.rowcount - 1 do
writeln(F, stringgrid.cells[x, y]);
closefile(f);
end;
procedure LoadGrid;
var
f: textfile;
temp, x, y: integer;
tempstr: string;
begin
assignfile(f, 'Filename');
reset(f);
readln(f, temp);
stringgrid.colcount := temp;
readln(f, temp);
stringgrid.rowcount := temp;
for X := 0 to stringgrid.colcount - 1 do
for y := 0 to stringgrid.rowcount - 1 do
begin
readln(F, tempstr);
stringgrid.cells[x, y] := tempstr;
end;
closefile(f);
end;
|
При чтении почты POP3 отделять Attachment и сохранять в файл.
| Привет, дорогая моя! Наконец-то я добилась от этого придурка разрешения пользоваться компьютером! Он даже сделал мне собственный почтовый ящик, так что теперь мы сможем общаться не только по телефону. Пиши мне на адрес ***@JIZNI.NET |
Почту читаю, используя компонент TNMPOP3 (стандартный в поставке D5), еще пробовал использовать библитеку Indy, но не помогло. А проблема заключается в том, что после прочтения письма, невозможно отделить аттачмент от тела. Но это происходит не со всеми письмами. Если я отправлю письмо с аттачем, то я могу его нормально читать и разбирать, а если отправляет Заказчик, то получается то, что я описал. Причем, Outlook и theBat, эти письма нормально читают и аттач МОЖНО сохранить.
for intIndex := 0 to Pred(Msg.MessageParts.Count) do
begin
if (Msg.MessageParts.Items[intIndex] is TIdAttachment) then
begin //general attachment
TIdAttachment(Msg.MessageParts.Items[intIndex]).SaveToFile(
TIdAttachment(Msg.MessageParts.Items[intIndex]).Filename);
TIdAttachment.Create(Msg1.MessageParts,
TIdAttachment(Msg.MessageParts.Items[intIndex]).Filename);
end
else
begin //body text
if Msg.MessageParts.Items[intIndex] is TIdText then
begin
Memo1.Lines.Clear;
Memo1.Lines.AddStrings(TIdText(Msg.MessageParts.Items[intIndex]).Body);
end
end;
end;
|
Сохранить несколько контролов в один файл.
{
-> Question:
How can I store some TRichEdit components and TEdit Components in a single file?
-> Answer:
Use a filestream and a reader or writer object.
These ease the tasks of writing strings to a binary stream and reading them back.
}
{
-> Frage:
Wie kann ich den Inhalt einiger TRichEdit und TEdit Komponenten in einer einzigen
Datei speichern?
-> Antwort:
Verwende einen FileStraem und ein Reader/Writer Objekt.
Damit kann man leicht strings in einen ьinдгеn Stream speichern und zигьск1еsеn.
}
// Save routine
// Speichern Routine
procedure SaveEditcontrols(const FileName: string; const Controls: array of TCustomEdit);
var
fs: TFilestream;
writer: TWriter;
i: Integer;
ss: TStringstream;
begin
fs := TFilestream.Create(FileName, fmCreate);
try
writer := TWriter.Create(fs, 4096);
try
for i := Low(Controls) to High(Controls) do
if Controls[i] is TCustomRichedit then
begin
ss := TStringstream.Create(EmptyStr);
try
with TRichedit(Controls[i]) do
begin
Plaintext := False;
Lines.SaveToStream(ss);
end;
writer.WriteString(ss.Datastring);
finally
ss.Free;
end;
end
else
writer.WriteString(Controls[i].Text);
finally
writer.Free;
end;
finally
fs.Free;
end;
end;
// Load routine
// Lade Routine
procedure LoadEditcontrols(const FileName: string; const Controls: array of TCustomEdit);
var
fs: TFilestream;
reader: Treader;
i: Integer;
ss: TStringstream;
begin
fs := TFilestream.Create(FileName, fmOpenread or fmShareDenyWrite);
try
reader := Treader.Create(fs, 4096);
try
for i := Low(Controls) to High(Controls) do
if Controls[i] is TCustomRichedit then
begin
ss := TStringstream.Create(reader.ReadString);
try
with TRichedit(Controls[i]) do
begin
Plaintext := False;
Lines.LoadfromStream(ss);
end;
finally
ss.Free;
end;
end
else
Controls[i].Text := reader.ReadString;
finally
reader.Free;
end;
finally
fs.Free;
end;
end;
// Example to store 2 TRichEdits and 3 Edit Controls to one file
// Beispiel, um 3 TRichEdits und 3 TEdit Controls in einer Datei zu speichern
procedure TForm1.Button1Click(Sender: TObject);
begin
SaveEditControls('C:\temp\temp.dat',
[richedit1, richedit2, edit1, edit2, edit3]);
end;
|
Как хранить настройки для программы. Работа с INI-файлами.
| Разговор двух украинских программистов. - Слышь Микола, как москали КД-ПЗУ называют? - Как? - CD-ROM! - Поубывал бы. |
var
IniFile: TIniFile;
//конкретные переменные - мои - храню их в инишке
IniPortItem: integer;
IniRTSChecked: boolean;
const
//значения по умолчанию
DefPortItem = 1;
DefRTSChecked = false;
procedure ReadIni;
var
IniPath: string;
FileName: string;
begin
GetDir(0,IniPath);
FileName:=IniPath+'\muk.ini';
IniFile:=TIniFile.Create(FileName);
IniPortItem:=IniFile.ReadInteger('Port','Number',DefPortItem);
IniRTSChecked:=IniFile.ReadBool('Port','RTS/CTS',DefRTSChecked);
IniFile.Free;
end;
procedure SaveIni;
var
IniPath: string;
FileName: string;
begin
GetDir(0,IniPath);
FileName:=IniPath+'\muk.ini';
IniFile:=TIniFile.Create(FileName);
IniFile.WriteInteger('Port','Number',IniPortItem);
IniFile.WriteBool('Port','RTS/CTS',IniRTSChecked);
IniFile.Free;
end;
|
Сохранять настройки в ini-файле.
Этот пример показывает, как можно сохранять настройки с помощью ini-файла. Ini-файлы удобны тем, что после удаления программы без деинсталяции в реестре не остается "следов". Еще это удобно тем, что для "сброса" настроек программы достаточно удалить ini-файл.
Эта программа сохраняет позицию и размер окна, содержимое поля ввода Edit1, число, введенное в Edit2, флажок CheckBox1, а также содержимое ListBox1 (добавить в него строчку можно, нажав на кнопку).
uses IniFiles;
procedure TForm1.FormCreate(Sender: TObject);
var
F: TIniFile;
names: TStringList;
i: integer;
begin
F := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'window.ini');
Form1.Left := F.ReadInteger('position', 'left', 0);
Form1.Width := F.ReadInteger('position', 'width', 200);
Form1.Top := F.ReadInteger('position', 'top', 0);
Form1.Height := F.ReadInteger('position', 'height', 200);
Edit1.Text := F.ReadString('tools', 'edit1', 'no text');
Edit2.Text := IntToStr(F.ReadInteger('tools', 'koef', 0));
CheckBox1.Checked := F.ReadBool('tools', 'check', true);
names := TStringList.Create;
F.ReadSection('files', names);
for i := 0 to Names.Count - 1 do
ListBox1.Items.Add(F.ReadString('files', names.Strings[i], ''));
names.Destroy;
F.Destroy;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
F: TIniFile;
i: integer;
begin
F := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'window.ini');
F.WriteInteger('position', 'left', Form1.Left);
F.WriteInteger('position', 'width', Form1.Width);
F.WriteInteger('position', 'top', Form1.Top);
F.WriteInteger('position', 'height', Form1.Height);
F.WriteString('tools', 'edit1', Edit1.Text);
F.WriteInteger('tools', 'koef', StrToIntDef(Edit2.Text, 0));
F.WriteBool('tools', 'check', CheckBox1.Checked);
for i := 0 to ListBox1.Items.Count - 1 do
F.WriteString('files', 'file' + IntToStr(i+1), ListBox1.Items.Strings[i]);
F.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add(Edit1.Text);
end;
|
Сохранение прикреплений Outlook.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Won’t some backups of your outlook attachments are filtered
some incoming log files? Here's the function.
}
uses
ComObj;
{...}
function ManageAttachments(SendersName, AttachmentPath: string;
MailDelete: Boolean): Boolean;
var
oApp: Variant;
oNs: Variant;
oFolder: Variant;
oMsg: Variant;
AtC: Variant;
AttFilename: Variant;
FileName: string;
CheckSender: string;
Counter: integer;
MailCounter: integer;
begin
try
oApp := CreateOLEObject('outlook.application');
try
oNs := oApp.GetNamespace('MAPI');
ofolder := oNS.GetDefaultFolder(6); // FolderTypeEnum (olFolderInbox)
MailCounter := 1;
// If there is any email in the Inbox
if ofolder.Items.Count > 0 then
begin
repeat
// Get the first Email
oMsg := ofolder.Items(MailCounter);
// Check the name or Email
// Use CheckSender := oMsg.subject to search on Subject;
CheckSender := oMsg.sendername;
if CheckSender = SendersName then
// Remove this line to backup all your attachments.
begin
// Check how many attachments
atc := oMsg.Attachments.Count;
if atc > 0 then
begin
// Get all the attachments and save them
for Counter := 1 to atc do
begin
AttFilename := oMsg.Attachments.item(Counter).FileName;
//filename := IncludeTrailingBackslash(AttachmentPath)+AttFilename; {Use this line for D5)}
FileName := AttachmentPath + '\' + AttFilename;
oMsg.Attachments.Item(Counter).SaveAsFile(FileName);
end;
end;
if MailDelete then
begin
oMsg.Delete;
// There's 1 Email less, so MailCounter - 1
Dec(MailCounter);
end;
end;
// Get the next Email
Inc(MailCounter);
// Do until there is no more Email to check
until MailCounter > ofolder.Items.Count;
end;
finally
oApp.quit;
end;
except
Result := False;
Exit;
end;
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// ManageAttachments(Email or name, Backup directory, MailDelete yes or no)
ManageAttachments('info@cleys.com', 'F:\test', False);
end;
{
Warning!
All your selected Email will be deleted if MailDelete = true
Achtung!
Alle E-Mails werden geloscht, wenn MailDelete = true ist.
}
Как сохранить значение свойства в поток.
Автор: Rick Rogers WEB-сайт: http://www.lmc-mediaagentur.de How can I save properties of a TList to a stream? I need the entire list to be saved as a whole and not as individual objects.A TList doesn't have any intrinsic streaming capability built into it, but it is very easy to stream anything that you want with a little elbow grease. Think about it: a stream is data. Classes have properties, whose values are data. It isn't too hard to write property data to a stream. Here's a simple example to get you going. This is but just one of many possible approaches to saving object property data to a stream:
unit uStreamableExample;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Contnrs;
type
TStreamableObject = class(TPersistent)
protected
function ReadString(Stream: TStream): string;
function ReadLongInt(Stream: TStream): LongInt;
function ReadDateTime(Stream: TStream): TDateTime;
function ReadCurrency(Stream: TStream): Currency;
function ReadClassName(Stream: TStream): ShortString;
procedure WriteString(Stream: TStream; const Value: string);
procedure WriteLongInt(Stream: TStream; const Value: LongInt);
procedure WriteDateTime(Stream: TStream; const Value: TDateTime);
procedure WriteCurrency(Stream: TStream; const Value: Currency);
procedure WriteClassName(Stream: TStream; const Value: ShortString);
public
constructor CreateFromStream(Stream: TStream);
procedure LoadFromStream(Stream: TStream); virtual; abstract;
procedure SaveToStream(Stream: TStream); virtual; abstract;
end;
TStreamableObjectClass = class of TStreamableObject;
TPerson = class(TStreamableObject)
private
FName: string;
FBirthDate: TDateTime;
public
constructor Create(const AName: string; ABirthDate: TDateTime);
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
property Name: string read FName write FName;
property BirthDate: TDateTime read FBirthDate write FBirthDate;
end;
TCompany = class(TStreamableObject)
private
FName: string;
FRevenues: Currency;
FEmployeeCount: LongInt;
public
constructor Create(const AName: string; ARevenues: Currency; AEmployeeCount:
LongInt);
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
property Name: string read FName write FName;
property Revenues: Currency read FRevenues write FRevenues;
property EmployeeCount: LongInt read FEmployeeCount write FEmployeeCount;
end;
TStreamableList = class(TStreamableObject)
private
FItems: TObjectList;
function Get_Count: LongInt;
function Get_Objects(Index: LongInt): TStreamableObject;
public
constructor Create;
destructor Destroy; override;
function FindClass(const AClassName: string): TStreamableObjectClass;
procedure Add(Item: TStreamableObject);
procedure Delete(Index: LongInt);
procedure Clear;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
property Objects[Index: LongInt]: TStreamableObject read Get_Objects;
default;
property Count: LongInt read Get_Count;
end;
TForm1 = class(TForm)
SaveButton: TButton;
LoadButton: TButton;
procedure SaveButtonClick(Sender: TObject);
procedure LoadButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
Path: string;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
resourcestring
DEFAULT_FILENAME = 'test.dat';
procedure TForm1.SaveButtonClick(Sender: TObject);
var
List: TStreamableList;
Stream: TStream;
begin
List := TStreamableList.Create;
try
List.Add(TPerson.Create('Rick Rogers', StrToDate('05/20/68')));
List.Add(TCompany.Create('Fenestra', 1000000, 7));
Stream := TFileStream.Create(Path + DEFAULT_FILENAME, fmCreate);
try
List.SaveToStream(Stream);
finally
Stream.Free;
end;
finally
List.Free;
end;
end;
{ TPerson }
constructor TPerson.Create(const AName: string; ABirthDate: TDateTime);
begin
inherited Create;
FName := AName;
FBirthDate := ABirthDate;
end;
procedure TPerson.LoadFromStream(Stream: TStream);
begin
FName := ReadString(Stream);
FBirthDate := ReadDateTime(Stream);
end;
procedure TPerson.SaveToStream(Stream: TStream);
begin
WriteString(Stream, FName);
WriteDateTime(Stream, FBirthDate);
end;
{ TStreamableList }
procedure TStreamableList.Add(Item: TStreamableObject);
begin
FItems.Add(Item);
end;
procedure TStreamableList.Clear;
begin
FItems.Clear;
end;
constructor TStreamableList.Create;
begin
FItems := TObjectList.Create;
end;
procedure TStreamableList.Delete(Index: LongInt);
begin
FItems.Delete(Index);
end;
destructor TStreamableList.Destroy;
begin
FItems.Free;
inherited;
end;
function TStreamableList.FindClass(const AClassName: string):
TStreamableObjectClass;
begin
Result := TStreamableObjectClass(Classes.FindClass(AClassName));
end;
function TStreamableList.Get_Count: LongInt;
begin
Result := FItems.Count;
end;
function TStreamableList.Get_Objects(Index: LongInt): TStreamableObject;
begin
Result := FItems[Index] as TStreamableObject;
end;
procedure TStreamableList.LoadFromStream(Stream: TStream);
var
StreamCount: LongInt;
I: Integer;
S: string;
ClassRef: TStreamableObjectClass;
begin
StreamCount := ReadLongInt(Stream);
for I := 0 to StreamCount - 1 do
begin
S := ReadClassName(Stream);
ClassRef := FindClass(S);
Add(ClassRef.CreateFromStream(Stream));
end;
end;
procedure TStreamableList.SaveToStream(Stream: TStream);
var
I: Integer;
begin
WriteLongInt(Stream, Count);
for I := 0 to Count - 1 do
begin
WriteClassName(Stream, Objects[I].ClassName);
Objects[I].SaveToStream(Stream);
end;
end;
{ TStreamableObject }
constructor TStreamableObject.CreateFromStream(Stream: TStream);
begin
inherited Create;
LoadFromStream(Stream);
end;
function TStreamableObject.ReadClassName(Stream: TStream): ShortString;
begin
Result := ReadString(Stream);
end;
function TStreamableObject.ReadCurrency(Stream: TStream): Currency;
begin
Stream.Read(Result, SizeOf(Currency));
end;
function TStreamableObject.ReadDateTime(Stream: TStream): TDateTime;
begin
Stream.Read(Result, SizeOf(TDateTime));
end;
function TStreamableObject.ReadLongInt(Stream: TStream): LongInt;
begin
Stream.Read(Result, SizeOf(LongInt));
end;
function TStreamableObject.ReadString(Stream: TStream): string;
var
L: LongInt;
begin
L := ReadLongInt(Stream);
SetLength(Result, L);
Stream.Read(Result[1], L);
end;
procedure TStreamableObject.WriteClassName(Stream: TStream; const Value:
ShortString);
begin
WriteString(Stream, Value);
end;
procedure TStreamableObject.WriteCurrency(Stream: TStream; const Value:
Currency);
begin
Stream.Write(Value, SizeOf(Currency));
end;
procedure TStreamableObject.WriteDateTime(Stream: TStream; const Value:
TDateTime);
begin
Stream.Write(Value, SizeOf(TDateTime));
end;
procedure TStreamableObject.WriteLongInt(Stream: TStream; const Value: LongInt);
begin
Stream.Write(Value, SizeOf(LongInt));
end;
procedure TStreamableObject.WriteString(Stream: TStream; const Value: string);
var
L: LongInt;
begin
L := Length(Value);
WriteLongInt(Stream, L);
Stream.Write(Value[1], L);
end;
{ TCompany }
constructor TCompany.Create(const AName: string; ARevenues: Currency;
AEmployeeCount: Integer);
begin
FName := AName;
FRevenues := ARevenues;
FEmployeeCount := AEmployeeCount;
end;
procedure TCompany.LoadFromStream(Stream: TStream);
begin
FName := ReadString(Stream);
FRevenues := ReadCurrency(Stream);
FEmployeeCount := ReadLongInt(Stream);
end;
procedure TCompany.SaveToStream(Stream: TStream);
begin
WriteString(Stream, FName);
WriteCurrency(Stream, FRevenues);
WriteLongInt(Stream, FEmployeeCount);
end;
procedure TForm1.LoadButtonClick(Sender: TObject);
var
List: TStreamableList;
Stream: TStream;
Instance: TStreamableObject;
I: Integer;
begin
Stream := TFileStream.Create(Path + DEFAULT_FILENAME, fmOpenRead);
try
List := TStreamableList.Create;
try
List.LoadFromStream(Stream);
for I := 0 to List.Count - 1 do
begin
Instance := List[I];
if Instance is TPerson then
ShowMessage(TPerson(Instance).Name);
if Instance is TCompany then
ShowMessage(TCompany(Instance).Name);
end;
finally
List.Free;
end;
finally
Stream.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Path := ExtractFilePath(Application.ExeName);
end;
initialization
RegisterClasses([TPerson, TCompany]);
end.
Как сохранить значение свойства в поток 2.
Автор: Yoav WEB-сайт: http://www.lmc-mediaagentur.de The solution above will work, but it forces you to implement streaming support for each of the TStreamableObject objects. Delphi has already implemented this mechanism in for the TPersistent class and the TComponent class, and you can use this mechanism. The class I include here does the job. It holds classes that inherit from TUmbCollectionItem (which in turn inherits from Delphi TCollectionItem), and handles all the streaming of the items. As the items are written with the Delphi mechanism, all published data is streamed.Notes: This class does not support working within the delphi IDE like TCollection. All objects inheriting from TUmbCollectionItem must be registered using the Classes.RegisterClass function. All objects inheriting from TUmbCollectionItem must implement the assign function. By default, the TUmbCollection owns its items (frees them when the collection is freed), but this functionality can be changed.
unit UmbCollection;
interface
uses
Windows, Messages, SysUtils, Classes, contnrs;
type
TUmbCollectionItemClass = class of TUmbCollectionItem;
TUmbCollectionItem = class(TCollectionItem)
private
FPosition: Integer;
public
{when overriding this method, you must call the inherited assign.}
procedure Assign(Source: TPersistent); override;
published
{the position property is used by the streaming mechanism to place the object in the
right position when reading the items. do not use this property.}
property Position: Integer read FPosition write FPosition;
end;
TUmbCollection = class(TObjectList)
private
procedure SetItems(Index: Integer; Value: TUmbCollectionItem);
function GetItems(Index: Integer): TUmbCollectionItem;
public
function Add(AObject: TUmbCollectionItem): Integer;
function Remove(AObject: TUmbCollectionItem): Integer;
function IndexOf(AObject: TUmbCollectionItem): Integer;
function FindInstanceOf(AClass: TUmbCollectionItemClass; AExact: Boolean =
True;
AStartAt: Integer = 0): Integer;
procedure Insert(Index: Integer; AObject: TUmbCollectionItem);
procedure WriteToStream(AStream: TStream); virtual;
procedure ReadFromStream(AStream: TStream); virtual;
property Items[Index: Integer]: TUmbCollectionItem read GetItems write
SetItems; default;
published
property OwnsObjects;
end;
implementation
{ TUmbCollection }
function ItemsCompare(Item1, Item2: Pointer): Integer;
begin
Result := TUmbCollectionItem(Item1).Position -
TUmbCollectionItem(Item2).Position;
end;
function TUmbCollection.Add(AObject: TUmbCollectionItem): Integer;
begin
Result := inherited Add(AObject);
end;
function TUmbCollection.FindInstanceOf(AClass: TUmbCollectionItemClass;
AExact: Boolean; AStartAt: Integer): Integer;
begin
Result := inherited FindInstanceOf(AClass, AExact, AStartAt);
end;
function TUmbCollection.GetItems(Index: Integer): TUmbCollectionItem;
begin
Result := inherited Items[Index] as TUmbCollectionItem;
end;
function TUmbCollection.IndexOf(AObject: TUmbCollectionItem): Integer;
begin
Result := inherited IndexOf(AObject);
end;
procedure TUmbCollection.Insert(Index: Integer; AObject: TUmbCollectionItem);
begin
inherited Insert(Index, AObject);
end;
procedure TUmbCollection.ReadFromStream(AStream: TStream);
var
Reader: TReader;
Collection: TCollection;
ItemClassName: string;
ItemClass: TUmbCollectionItemClass;
Item: TUmbCollectionItem;
i: Integer;
begin
Clear;
Reader := TReader.Create(AStream, 1024);
try
Reader.ReadListBegin;
while not Reader.EndOfList do
begin
ItemClassName := Reader.ReadString;
ItemClass := TUmbCollectionItemClass(FindClass(ItemClassName));
Collection := TCollection.Create(ItemClass);
try
Reader.ReadValue;
Reader.ReadCollection(Collection);
for i := 0 to Collection.Count - 1 do
begin
item := ItemClass.Create(nil);
item.Assign(Collection.Items[i]);
Add(Item);
end;
finally
Collection.Free;
end;
end;
Sort(ItemsCompare);
Reader.ReadListEnd;
finally
Reader.Free;
end;
end;
function TUmbCollection.Remove(AObject: TUmbCollectionItem): Integer;
begin
Result := inherited Remove(AObject);
end;
procedure TUmbCollection.SetItems(Index: Integer; Value: TUmbCollectionItem);
begin
inherited Items[Index] := Value;
end;
procedure TUmbCollection.WriteToStream(AStream: TStream);
var
Writer: TWriter;
CollectionList: TObjectList;
Collection: TCollection;
ItemClass: TUmbCollectionItemClass;
ObjectWritten: array of Boolean;
i, j: Integer;
begin
Writer := TWriter.Create(AStream, 1024);
CollectionList := TObjectList.Create(True);
try
Writer.WriteListBegin;
{init the flag array and the position property of the TCollectionItem objects.}
SetLength(ObjectWritten, Count);
for i := 0 to Count - 1 do
begin
ObjectWritten[i] := False;
Items[i].Position := i;
end;
{write the TCollectionItem objects. we write first the name of the objects class,
then write all the object of the same class.}
for i := 0 to Count - 1 do
begin
if ObjectWritten[i] then
Continue;
ItemClass := TUmbCollectionItemClass(Items[i].ClassType);
Collection := TCollection.Create(ItemClass);
CollectionList.Add(Collection);
{write the items class name}
Writer.WriteString(Items[i].ClassName);
{insert the items to the collection}
for j := i to Count - 1 do
if ItemClass = Items[j].ClassType then
begin
ObjectWritten[j] := True;
(Collection.Add as ItemClass).Assign(Items[j]);
end;
{write the collection}
Writer.WriteCollection(Collection);
end;
finally
CollectionList.Free;
Writer.WriteListEnd;
Writer.Free;
end;
end;
{ TUmbCollectionItem }
procedure TUmbCollectionItem.Assign(Source: TPersistent);
begin
if Source is TUmbCollectionItem then
Position := (Source as TUmbCollectionItem).Position
else
inherited;
end;
end.
Сохранение точных размеров при печати.
Приведенный ниже модуль демонстрирует принцип использования GetDeviceCaps для получения исчерпывающей информации о вашем принтере, включая HORZRES и VERTRES (горизонтальное и вертикальное разрешение в пикселах) на дюйм бумаги. Используя значения LOGPIXELSX и LOGPIXELSY, вы можете откалибровать принтер для точного задания количества точек на дюйм в горизонтальном и вертикальном направлениях. В дополнение к информации о принтере, приведенный модуль демонстрирует способ печати, при котором изображение выводится на принтер "естественного" размера; также показана возможность размещения изображения на бумаге в определенной позиции и определенного размера (т.е. с применением масштабирования). Я думаю это должно вам помочь в деле управления принтером.Пример также демонстрирует вывод на печать синусной кривой в конкретной позиции печати с заданными в дюймах размерами. Я думаю вы без труда разберетесь как перейти на метрическую систему размеров.
unit Tstpr2fm;
{Пример использования объекта Printer из модуля TPrinter.
Приведен избыточный стиль программирования для облегчения
восприятия материала.
Демонстрация величин, возвращаемых функцией Windows API GetDeviceCaps.}
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Print: TButton;
Image1: TImage;
procedure PrintClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
Printers;
{Константы WINAPI GetDeviceCaps получены из C++ windows.h и wingdi.h}
{Отдельные константы здесь приведены только для информации о их наличии}
const
DRIVERVERSION = 0;
TECHNOLOGY = 2; {Смотри windows.h для значения маски}
HORZSIZE = 4;
VERTSIZE = 6;
HORZRES = 8;
VERTRES = 10;
BITSPIXEL = 12;
PLANES = 14;
NUMBRUSHES = 16;
NUMPENS = 18;
NUMMARKERS = 20;
NUMFONTS = 22;
NUMCOLORS = 24;
PDEVICESIZE = 26;
CURVECAPS = 28; {Смотри windows.h для значения маски}
LINECAPS = 30; {Смотри windows.h для значения маски}
POLYGONALCAPS = 32; {Смотри windows.h для значения маски}
TEXTCAPS = 34; {Смотри windows.h для значения маски}
CLIPCAPS = 36; {Смотри windows.h для значения маски}
RASTERCAPS = 38; {Смотри windows.h для значения маски}
ASPECTX = 40;
ASPECTY = 42;
ASPECTXY = 44;
LOGPIXELSX = 88;
LOGPIXELSY = 90;
SIZEPALETTE = 104;
NUMRESERVED = 106;
COLORRES = 108;
PHYSICALWIDTH = 110; {Смотри определение в windows.h}
PHYSICALHEIGHT = 111; {Смотри определение в windows.h}
PHYSICALOFFSETX = 112; {Смотри определение в windows.h}
PHYSICALOFFSETY = 113; {Смотри определение в windows.h}
SCALINGFACTORX = 114; {Смотри определение в windows.h}
SCALINGFACTORY = 115; {Смотри определение в windows.h}
DeviceCapsString: array[1..34] of string =
('DRIVERVERSION', 'TECHNOLOGY', 'HORZSIZE',
'VERTSIZE', 'HORZRES', 'VERTRES',
'BITSPIXEL', 'PLANES', 'NUMBRUSHES',
'NUMPENS', 'NUMMARKERS', 'NUMFONTS',
'NUMCOLORS', 'PDEVICESIZE', 'CURVECAPS',
'LINECAPS', 'POLYGONALCAPS', 'TEXTCAPS',
'CLIPCAPS', 'RASTERCAPS', 'ASPECTX',
'ASPECTY', 'ASPECTXY', 'LOGPIXELSX',
'LOGPIXELSY', 'SIZEPALETTE', 'NUMRESERVED',
'COLORRES', 'PHYSICALWIDTH', 'PHYSICALHEIGHT',
'PHYSICALOFFSETX', 'PHYSICALOFFSETY', 'SCALINGFACTORX',
'SCALINGFACTORY');
DeviceCapsIndex: array[1..34] of INTEGER =
(0, 2, 4, 6, 8, 10, 12, 14, 16, 18,
20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
40, 42, 44, 88, 90, 104, 106, 108, 110, 111,
112, 113, 114, 115);
{$R *.DFM}
function iPosition(const i: INTEGER): INTEGER;
begin
RESULT := Integer(i * LongInt(Printer.PageWidth) div 1000)
end {iPosition};
function jPosition(const j: INTEGER): INTEGER;
begin
RESULT := Integer(j * LongInt(Printer.PageHeight) div 1000)
end {jPosition};
procedure TForm1.PrintClick(Sender: TObject);
var
DestinationRectangle: TRect;
GraphicAspectRatio: DOUBLE;
i: INTEGER;
j: INTEGER;
iBase: INTEGER;
iPixelsPerInch: WORD;
jBase: INTEGER;
jDelta: INTEGER;
jPixelsPerInch: WORD;
OffScreen: TBitMap;
PixelAspectRatio: DOUBLE;
SourceRectangle: TRect;
TargetRectangle: TRect;
value: INTEGER;
x: DOUBLE;
y: DOUBLE;
begin
Printer.Orientation := poLandscape;
Printer.BeginDoc;
{Делаем прямоугольник для показа полей}
Printer.Canvas.Rectangle(0, 0, Printer.PageWidth, Printer.PageHeight);
{Свойства принтера и страницы}
Printer.Canvas.Font.Name := 'Times New Roman';
Printer.Canvas.Font.Size := 12;
Printer.Canvas.Font.Style := [fsBold];
Printer.Canvas.TextOut(iPosition(50), jPosition(40),
'Свойства принтера и страницы');
Printer.Canvas.Font.Style := [];
Printer.Canvas.Font.Size := 10;
iBase := iPosition(50);
jBase := 60;
jDelta := 18;
Printer.Canvas.TextOut(iPosition(50), jPosition(jBase),
Printer.Printers.Strings[Printer.PrinterIndex]);
INC(jBase, jDelta);
Printer.Canvas.TextOut(iBase, jPosition(jBase),
'Пикселей: ' + IntToStr(Printer.PageWidth) + ' X ' +
IntToStr(Printer.PageHeight));
INC(jBase, jDelta);
Printer.Canvas.TextOut(iBase, jPosition(jBase),
'Дюймов: ' + FormatFloat('0.000',
Printer.PageWidth / Printer.Canvas.Font.PixelsPerInch) + ' X ' +
FormatFloat('0.000',
Printer.PageHeight / Printer.Canvas.Font.PixelsPerInch));
INC(jBase, 2 * jDelta);
Printer.Canvas.TextOut(iBase, jPosition(jBase),
'Шрифт: ' + Printer.Canvas.Font.Name + ' Размер: ' +
IntToStr(Printer.Canvas.Font.Size));
INC(jBase, jDelta);
Printer.Canvas.TextOut(iBase, jPosition(jBase),
'Пикселей в дюйме: ' + IntToStr(Printer.Canvas.Font.PixelsPerInch));
INC(jBase, jDelta);
Printer.Canvas.TextOut(iBase, jPosition(jBase),
'''ТЕКСТ'': ' + IntToStr(Printer.Canvas.TextWidth('ТЕКСТ')) + ' X ' +
IntToStr(Printer.Canvas.TextHeight('ТЕКСТ')) + '
пикселей');
{Значения GetDeviceCaps}
INC(jBase, 2 * jDelta);
Printer.Canvas.Font.Size := 12;
Printer.Canvas.Font.Style := [fsBold];
Printer.Canvas.TextOut(iBase, jPosition(jBase), 'GetDeviceCaps');
INC(jBase, jDelta);
Printer.Canvas.Font.Size := 10;
Printer.Canvas.Font.Style := [];
for j := LOW(DeviceCapsIndex) to HIGH(DeviceCapsIndex) do
begin
value := GetDeviceCaps(Printer.Handle, DeviceCapsIndex[j]);
Printer.Canvas.TextOut(iBase, jPosition(jBase), DeviceCapsString[j]);
if (DeviceCapsIndex[j] < 28) or (DeviceCapsIndex[j] > 38) then
Printer.Canvas.TextOut(iPosition(250), jPosition(jBase), Format('%-8d',
[value]))
else
Printer.Canvas.TextOut(iPosition(250), jPosition(jBase), Format('%.4x',
[value]));
INC(jBase, jDelta);
end;
{Помещаем изображение в левый нижний угол}
Printer.Canvas.Draw(iPosition(300), jPosition(100),
Form1.Image1.Picture.Graphic);
{Помещаем то же изображение, имеющее ширину 1" и пропорциональную
высоту в позиции 4"-правее и 1"-ниже верхнего левого угла}
GraphicAspectRatio := Form1.Image1.Picture.Height /
Form1.Image1.Picture.Width;
iPixelsPerInch := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
jPixelsPerInch := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
PixelAspectRatio := jPixelsPerInch / iPixelsPerInch;
TargetRectangle := Rect(4 * iPixelsPerInch, {4"}
jPixelsPerInch, {1"}
6 * iPixelsPerInch, {6" -- 2" ширина}
jPixelsPerInch +
TRUNC(2 * iPixelsPerInch * GraphicAspectRatio *
PixelAspectRatio));
Printer.Canvas.TextOut(4 * iPixelsPerInch, jPixelsPerInch -
Printer.Canvas.TextHeight('X'),
'2" ширина от (4", 1")');
Printer.Canvas.StretchDraw(TargetRectangle, Form1.Image1.Picture.Graphic);
{Создаем изображение в памяти и затем копируем его на холст принтера}
SourceRectangle := Rect(0, 0, 3 * iPixelsPerInch - 1, 2 * jPixelsPerInch -
1);
{Это не должно работать! Rectangle = Left, Top, Right, Bottom
Top и Bottom считаются зарезервированными?}
DestinationRectangle := Rect(4 * iPixelsPerInch, 6 * jPixelsPerInch,
7 * iPixelsPerInch - 1, 4 * jPixelsPerinch - 1);
Printer.Canvas.TextOut(4 * iPixelsPerInch, 4 * jPixelsPerInch -
Printer.Canvas.TextHeight('X'),
IntToStr(3 * iPixelsPerInch) + ' пикселей на ' +
IntToStr(2 * jPixelsPerInch) + ' пикселей -- ' +
'3"-на-2" в (4",4")');
OffScreen := TBitMap.Create;
try
OffScreen.Width := SourceRectangle.Right + 1;
OffScreen.Height := SourceRectangle.Bottom + 1;
with OffScreen.Canvas do
begin
Pen.Color := clBlack;
Brush.Color := clWhite;
Rectangle(0, 0, 3 * iPixelsPerInch - 1, 2 * jPixelsPerInch - 1);
Brush.Color := clRed;
MoveTo(0, 0);
LineTo(3 * iPixelsPerInch - 1, 2 * jPixelsPerInch - 1);
Brush.Color := clBlue;
MoveTo(0, 0);
for i := 0 to 3 * iPixelsPerInch - 1 do
begin
x := 12 * PI * (i / (3 * iPixelsPerInch - 1));
y := jPixelsPerInch + jPixelsPerInch * SIN(x);
LineTo(i, TRUNC(y));
end
end;
Printer.Canvas.CopyRect(DestinationRectangle, OffScreen.Canvas,
SourceRectangle);
finally
OffScreen.Free
end;
{Список шрифтов для данного принтера}
iBase := iPosition(750);
Printer.Canvas.Font.Name := 'Times New Roman';
Printer.Canvas.Font.Size := 12;
Printer.Canvas.Font.Style := [fsBold];
Printer.Canvas.TextOut(iBase, jPosition(40), 'Шрифты');
Printer.Canvas.Font.Style := [];
Printer.Canvas.Font.Size := 10;
jDelta := 16;
for j := 0 to Printer.Fonts.Count - 1 do
begin
Printer.Canvas.TextOut(iBase, jPosition(60 + jDelta * j),
Printer.Fonts.Strings[j])
end;
Printer.EndDoc;
end;
end.
|
Сохранение пользовательских настроек.
| Автор: Mike Scott |
| Сын системного администратора перед сном просит папу: - Па-па... Расскажи сказку про умного, толкового, доброго, смелого юзера... |
Вы можете легко это сделать, передавая форму при закрытии в поток для записи ее в отдельный файл. Затем, когда это создано, проверяйте наличие файла и организуйте его чтение. Вот как можно это сделать:
const
FileName = 'Form1.stm';
constructor TForm1.Create(AOwner: TComponent);
begin
if FileExists(FileName) then
begin
CreateNew(AOwner);
ReadComponentResFile(FileName, Self);
end
else
inherited Create(AOwner);
{ поместите здесь код в стиле 'OnCreate' }
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
WriteComponentResFile(FileName, Self);
end;
|
Имейте в виду, что это не вызывает OnCreate. В действительности, вам OnCreate и не нужен, поскольку сюда вы можете поместить код, необходимый в обработчике OnCreate; данный код следует поместить после конструктора Create, как и указано в комментарии.
Как сохранить исходник HTML из TWebBrowser.Document на диск.
| Серьезная организация возьмет на высокооплачиваемую должность опытного хакера. Резюме просим размещать на главной странице сайта www.microsoft.com. |
TWebBrowser.Document включает в себя IPersistStreamInit который содержит метод Save(). Всё, что нам нужно знать, это как использовать данный метод с объектом, который включён в IStream. Для этого мы просто воспользуемся TStreamAdapter.
Обратите внимание, что интерфейсы IPersistStreamInit и IStream объявлены внутри ActiveX unit.
Итак, вот так это выглядит.
procedure TForm1.SaveHTMLSourceToFile(const FileName: string;
WB: TWebBrowser);
var
PersistStream: IPersistStreamInit;
FileStream: TFileStream;
Stream: IStream;
SaveResult: HRESULT;
begin
PersistStream := WB.Document as IPersistStreamInit;
FileStream := TFileStream.Create(FileName, fmCreate);
try
Stream := TStreamAdapter.Create(FileStream, soReference) as IStream;
SaveResult := PersistStream.Save(Stream, True);
if FAILED(SaveResult) then
MessageBox(Handle, 'Fail to save HTML source', 'Error', 0);
finally
{ В ответ на уничтожение объекта TFileStream, передаём
soReference в конструктор TStreamAdapter. }
FileStream.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if SaveDialog1.Execute then
SaveHTMLSourceToFile(SaveDialog1.FileName, WebBrowser1);
end;
|
А как сохранить вместе с исходником все файлы (.CSS, JPG, GIF и т.д...) ?
try
WebBrowser1.ExecWB(4, 0);
except
on E: Exception do
msError := true;
end;
|
Сохранение и восстановление положения и размеров окон.
Автор: Desdechado WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Сохранение и восстановление положения и размеров окон
Процедуры SaveWndPos и SetWndPos работают в паре.
Сохраняют для конкретного пользователя Windows в реестре местоположение,
размер окон (и прочих Соntrоl'ов при явном указании),
а также ширины и порядок расположения колонок в DВGrid'ах на форме.
Не дает "потерять" колонки совсем.
Возможные проблемы:
1. С записью в реестр без прав (в некую левую ветку).
2. При изменении имен и количества полей в DВGrid'е.
3. С окнами неизменяемого размера, если его в Design-time поменяли.
Комментарии:
1. Статус колонки DBGrid (visible, readonly) не сохраняется, но сделать
это несложно.
2. Для формы, которая используются для разных целей, но хотелось бы
сохранить ее характеристики для каждой цели отдельно, можно явно
указывать УНИКАЛЬНЫЙ класс.
Важное уточнение:
Сохранение параметров DBGrid происходит ТОЛЬКО при статически
описанных колонках в самом гриде.
Еще комментарий:
параметр lSetSize в SetWndPos применяется только к размерам формы, а
не к ширине колонок. Но можно и это учесть.
Зависимости: Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls, Buttons, Registry, Math, DB, DbTables, dbGrids
Автор: Александр Хабаров aka Desdechado, x_a_u@mail.ru, Феодосия
Copyright: Александр Хабаров
Дата: 13 мая 2002 г.
***************************************************** }
const
cRegKey = '\Sоftwаrе\СооlSоft\Настройки\';
procedure SaveWndPos(frm: TControl; cClass: string = '');
var
rct: TRect;
oReg: TRegistry;
grid: TDBGrid;
i, j: Integer;
begin
{--- определить параметры ---}
rct := frm.BoundsRect;
if (cClass = '') then
cClass := frm.ClassName;
{--- сохранить в реестре ---}
oReg := TRegistry.Create();
with (oReg) do
try
RootKey := HKEY_CURRENT_USER;
{--- ... размер окна ---}
if (OpenKey(cRegKey + cClass, True)) then
begin
if (frm is TForm) then
WriteInteger('Show', Ord((frm as TForm).WindowState));
if (not (frm is TForm) or
((frm as TForm).WindowState = wsNormal)) then
begin
WriteInteger('Left', rct.Left);
WriteInteger('Top', rct.Top);
WriteInteger('Right', rct.Right);
WriteInteger('Bottom', rct.Bottom);
end;
end;
CloseKey();
{--- ... размеры и положение колонок в Grid'ах ---}
for i := 0 to frm.ComponentCount - 1 do
if (frm.Components[i].ClassName = D
if (frm.Components[i].ClassName = 'TDBGrid') then
begin
grid := TDBGrid(frm.Components[i]);
for j := 0 to grid.Columns.Count - 1 do
begin
if (OpenKey(cRegKey + cClass + '\' + grid.Name + '\' +
IntToStr(j), True)) then
begin
WriteString('Name', grid.Columns[j].FieldName);
WriteInteger('Width', grid.Columns[j].Width);
end;
CloseKey();
end; // for( j )
end;
finally
CloseKey();
Free;
end;
end;
procedure SetWndPos(frm: TControl; lSetSize: Boolean = True;
cClass: string = '');
var
rct: TRect;
oReg: TRegistry;
nShow, i, j, k: Integer;
grid: TDBGrid;
cName: string;
begin
nShow := Ord(wsNormal);
{--- текущие параметры (на случай, если нет в реестре) ---}
rct := frm.BoundsRect;
if (cClass = '') then
cClass := frm.ClassName;
if (frm is TForm) then
nShow := Ord((frm as TForm).WindowState);
{--- считать из реестра ---}
oReg := TRegistry.Create();
with (oReg) do
try
RootKey := HKEY_CURRENT_USER;
{--- ... размер окна ---}
if (OpenKeyReadOnly(cRegKey + cClass)) then
begin
if (frm is TForm) then
nShow := ReadInteger('Show');
if (ValueExists('Left')) then
rct.Left := ReadInteger('Left');
if (ValueExists('Top')) then
rct.Top := ReadInteger('Top');
if (ValueExists('Right')) then
rct.Right := ReadInteger('Right');
if (ValueExists('Bottom')) then
rct.Bottom := ReadInteger('Bottom');
end;
CloseKey();
{--- ... размеры и положение колонок в Grid'ах ---}
for i := 0 to frm.ComponentCount - 1 do
if (frm.Components[i].ClassName = 'TDBGrid') then
begin
grid := TDBGrid(frm.Components[i]);
for j := 0 to grid.Columns.Count - 1 do
begin
if (OpenKeyReadOnly(cRegKey + cClass + '\' + grid.Name + '\' +
IntToStr(j))) then
begin
cName := ReadString('Name');
for k := 0 to grid.Columns.Count - 1 do
if (grid.Columns[k].FieldName = cName) then
begin
grid.Columns[k].Index := j;
break;
end;
grid.Columns[j].Width := ReadInteger('Width');
end;
CloseKey();
{--- экстремальные ширины - нормировать ---}
grid.Columns[j].Width := Max(grid.Columns[j].Width, 3);
grid.Columns[j].Width := Min(grid.Columns[j].Width,
grid.ClientWidth - 3);
end; // for( j )
end;
finally
CloseKey();
Free;
end;
{--- применить считанное к окну ---}
if (lSetSize) then
frm.BoundsRect := rct
else
begin
frm.Top := rct.Top;
frm.Left := rct.Left;
end;
if ((frm is TForm) and (nShow = Ord(wsMaximized))) then
(frm as TForm).WindowState := wsMaximized;
end;
procedure FitDeskTop(frm: TControl);
var
rct: TRect;
begin
rct := frm.BoundsRect;
if (rct.Top < 0) then
rct.Top := 0
else if (rct.Bottom >= Screen.DeskTopHeight) then
rct.Top := rct.Top + Screen.DeskTopHeight - rct.Bottom - 1;
if (rct.Left < 0) then
rct.Left := 0
else if (rct.Right >= Screen.DeskTopWidth) then
rct.Left := rct.Left + Screen.DeskTopWidth - rct.Right - 1;
rct.Bottom := rct.Top + frm.Height;
rct.Right := rct.Left + frm.Width;
frm.BoundsRect := rct;
end;
Пример использования:
{
0. не забудьте поменять константу для СВОЕЙ ветки в реестре
1. после создания окна перед Show
2. перед frm.Release (удалением) надо вызывать SaveWndPos
}
procedure TfrmCard.FormCreate(Sender: TObject);
begin
// прочий код здесь
SetWndPos(Self);
end;
procedure TfrmCard.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// прочий код здесь
SaveWndPos(Self);
Action := caFree;
end;
Сохранить документ Word как RTF.
uses
ComObj;
function ConvertDoc2Rtf(var FileName: string) : Boolean;
var
oWord: OleVariant;
oDoc: OleVariant;
begin
Result := False;
try
oWord := GetActiveOleObject('Word.Application');
except
oWord := CreateOleObject('Word.Application');
end;
oWord.Documents.Open(FileName);
oDoc := oWord.ActiveDocument;
FileName := ChangeFileExt(FileName, '.rtf');
oDoc.SaveAs(FileName);
oWord.ActiveDocument.Close(wdDoNotSaveChanges, EmptyParam, EmptyParam);
oWord.Quit(EmptyParam, EmptyParam, EmptyParam);
oDoc := VarNull;
oWord := VarNull;
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
FileName = 'c:\Document.doc';
begin
if ConvertDoc2Rtf(FileName) then
begin
ShowMessage('Word document has been converted to .rtf');
RichEdit1.Lines.LoadFromFile(FileName);
end;
end;
|
Изменить шрифт TStatusBar.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
To use this example, you need a TStatusBar and at least one Panel.
Then change the style of StatusBar1.Panels[0] to
psOwnerDraw and add the code below to the OnDrawPanel handler.
Es wird eine TStatusBar fur dieses Beispiel benotigt mit
mindestens einem Panel.
Der Style von StatusBar1.Panels[0] muss zu psOwnerDraw
geandert werden.
Fuge dann folgenden Code im OnDrawPanel Ereignis hinzu:
}
procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);
var
SomeText: string;
begin
if Panel = StatusBar1.Panels[0] then
begin
SomeText := 'Hello!';
Panel.Width := Trunc(StatusBar1.Canvas.TextWidth(SomeText) * 1.5);
with StatusBar.Canvas do
begin
Brush.Color := clWhite;
FillRect(Rect);
Font.Name := 'Arial';
Font.Color := clRed;
Font.Style := Font.Style + [fsItalic, fsBold];
TextRect(Rect, Rect.Left + 1, Rect.Top, SomeText);
end;
end;
end;
Из ресурсов поочередно загружать глифы для кнопок SpeedButton.
Автор: Dennis Passmore Могу ли я из ресурсов поочередно загружать глифы для кнопок speedbutton и, если да, то как это сделать?Например, если в вашем проекте используется TDBGrid, то иконки кнопок компонента DBNavigator могут линковаться вашей программой, и их можно загрузить для использования в ваших speedbutton следующим образом:
SpeedButton.Caption := ''; SpeedButton1.Glyph.LoadFromResourcename(HInstance,'DBN_REFRESH'); SpeedButton1.NumGlyphs := 2; |
Все имена должны использовать верхний регистр.
Прокрутка TScrollBox с помощью стрелок вверх и вниз.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
private
procedure CMDialogKey(var Msg: TCMDialogKey); message CM_DIALOGKEY;
end;
implementation
procedure TForm1.CMDialogkey;
begin
with Scrollbox1.VertScrollBar do
begin
case Msg.CharCode of
// Delphi takes care of Position < 0 or Position > Range
VK_DWON: Position := Position + Increment; // down arrow key
VK_Up: Position := Position - Increment; // up arrow key
else
inherited;
end;
end;
end;
Масштабирование окна.
Автор: Andrey Kozlov.
implementation
const
ScreenWidth: Integer = 800; {Я разрабатывал свою форму в режиме 800x600.}
ScreenHeight: Integer = 600;
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
x, y: LongInt; {Тип Integer не достаточно большой для наших значений.}
begin
form1.scaled := true;
x := getSystemMetrics(SM_CXSCREEN);
y := getSystemMetrics(SM_CYSCREEN);
if (x <> ScreenHeight) or (y <> ScreenWidth) then
begin
form1.height := form1.height * x div ScreenWidth;
form1.width := form1.width * y div ScreenHeight;
end;
if x <> ScreenWidth then
scaleBy(x, ScreenWidth);
end;
|
Файл DELSEQ07.FAQ содержит код примера отображения форм в различных разрешениях. К сожалению, он не учитывал ширину границы окна. Я публикую изменение, масштабирующее компоненты вне зависимости от разрешения экрана и ширины границ окон. Включите нижеследующий модуль в секцию uses каждого модуля и вызывайте ScaleForm в обработчике формы OnCreate, передавая в качестве параметра имя формы. Я надеюсь что помог тем, кто столкнулся с данной проблемой.
unit scale;
interface
uses
Forms, WinTypes, WinProcs, SysUtils;
procedure ScaleForm(Sender: TObject);
implementation
procedure ScaleForm(Sender: TObject);
const
{измените это так, чтобы это соответствовало
режиму разрешения во время разработки}
DesignScrY: LongInt = 480;
DesignScrX: LongInt = 640;
DesignBorder: LongInt = 4; {значение в Панели Управления + 1}
var
SystemScrY: LongInt;
SystemScrX: LongInt;
SystemBorder: LongInt;
OldHeight: LongInt;
OldWidth: LongInt;
begin
SystemScrY := GetSystemMetrics(SM_CYSCREEN);
SystemScrX := GetSystemMetrics(SM_CXSCREEN);
SystemBorder := GetSystemMetrics(SM_CYFRAME);
with Sender as TForm do
begin
Scaled := True;
AutoScroll := False;
Top := Top * SystemScrX div DesignScrX;
Left := Left * SystemScrX div DesignScrX;
OldHeight := Height + (DesignBorder - SystemBorder) * 2;
OldWidth := Width + (DesignBorder - SystemBorder) * 2;
ScaleBy((OldWidth * SystemScrX div DesignScrX - SystemBorder * 2),
(OldWidth - DesignBorder * 2));
{
Для форм не имеющих границ измените предшествующие
три строки следующим способом:
OldHeight := Height;
OldWidth := Width;
ScaleBy(SystemScrX, DesignScrX);
}
Height := OldHeight * SystemScrY div DesignScrY;
Width := OldWidth * SystemScrX div DesignScrX;
end;
end;
begin
end.
|
Масштабирование окна 2.
Вывод формы с различными разрешениями экрана.При проектировании форм часто бывает полезным предусмотреть ситуацию, когда форма и находящиеся на ней элементы управления должны иметь одинаковый размер вне зависимости от текущего разрешения экрана. Вот небольшой пример как можно это сделать:
implementation
const
ScreenWidth: LongInt = 800; {Я разрабатывал мою форму в режиме 800x600.}
ScreenHeight: LongInt = 600;
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
scaled := true;
if (screen.width <> ScreenWidth) then
begin
height := longint(height) * longint(screen.height) div ScreenHeight;
width := longint(width) * longint(screen.width) div ScreenWidth;
scaleBy(screen.width, ScreenWidth);
end;
end;
|
Затем, вероятно, вы захотите иметь нечто, проверяющее размер шрифтов, OK. Прежде, чем вы измените размер шрифта, вам необходимо убедиться, что объект имеет свойство font. Это может быть сделано следующим образом:
uses typinfo;
var
i: integer;
begin
for i := componentCount - 1 downtto 0 do
with components[i] do
begin
if GetPropInfo(ClassInfo, 'font') <> nil then
font.size := (NewFormWidth DIV OldFormWidth) * font.size;
end;
end;
|
Масштабирование размера формы и размера шрифтов.
Когда мои программы работают на системах с установленными маленькими шрифтами, я получаю странный вид моей формы. К примеру, расположенные на форме компоненты Label становятся малы для размещения указанного теста, обрезая его в правой или нижней части. StringGrid не осуществляет положенного выравнивания и т.д.Попробуй следующий код. Он масштабирует как размер формы, так и размер шрифтов. Вызывай его в Form.FormCreate. Надеюсь это поможет.
unit geScale;
interface
uses Forms, Controls;
procedure geAutoScale(MForm: TForm);
implementation
type
TFooClass = class(TControl); { необходимо выяснить защищенность }
{ свойства Font }
procedure geAutoScale(MForm: TForm);
const
cScreenWidth: integer = 800;
cScreenHeight: integer = 600;
cPixelsPerInch: integer = 96;
cFontHeight: integer = -11; {В режиме проектирование значение из Font.Height}
var
i: integer;
begin
{
ВАЖНО!! : Установите в Инспекторе Объектов свойство Scaled TForm в FALSE.
Следующая программа масштабирует форму так, чтобы она выглядела одинаково
внезависимости от размера экрана и пикселей на дюйм. Расположенный ниже
участок кода проверяет, отличается ли размер экрана во время выполнения
от размера во время проектирования. Если да, Scaled устанавливается в True
и компоненты снова масштабируются так, чтобы они выводились в той же
позиции экрана, что и во время проектирования.
}
if (Screen.width &; lt > cScreenWidth) or (Screen.PixelsPerInch <>
cPixelsPerInch) then
begin
MForm.scaled := TRUE;
MForm.height := MForm.height * screen.Height div cScreenHeight;
MForm.width := MForm.width * screen.width div cScreenWidth;
MForm.ScaleBy(screen.width, cScreenWidth);
end;
{
Этот код проверяет, отличается ли размер шрифта во времы выполнения от
размера во время проектирования. Если во время выполнения pixelsperinch
формы отличается от pixelsperinch во время проектирования, шрифты снова
масштабируются так, чтобы форма не отличалась от той, которая была во
время разработки. Масштабирование производится исходя из коэффициента,
получаемого путем деления значения font.height во время проектирования
на font.height во время выполнения. Font.size в этом случае работать не
будет, так как это может дать результат больший, чем текущие размеры
компонентов, при этом текст может оказаться за границами области компонента.
Например, форма создана при размерах экрана 800x600 с установленными
маленькими шрифтами, имеющими размер font.size = 8. Когда вы запускаете
в системе с 800x600 и большими шрифтами, font.size также будет равен 8,
но текст будет бОльшим чем при работе в системе с маленькими шрифтами.
Данное масштабирование позволяет иметь один и тот же размер шрифтов
при различных установках системы.
}
if (Screen.PixelsPerInch <> cPixelsPerInch) then
begin
for i := MForm.ControlCount - 1 downto 0 do
TFooClass(MForm.Controls[i]).Font.Height :=
(MForm.Font.Height div cFontHeight) *
TFooClass(MForm.Controls[i]).Font.Height;
end;
end;
end.
|
Технология без интересного имени или как работать со сканером.
Автор: Павел В настоящее время в конференциях то и дело встречаются вопросы типа: как мне получить изображение со сканера, с web камеры и т.д.. При том, что и интернете практически полностью отсутствуют материалы по этим вопросам на русском языке и при достаточном разнообразии их на английском. Эта статья должна помочь начинающему программисту на Delphi разобраться в них. В статье подробно, с примерами описана работа со сканером с использованием популярной библиотеки Easy TWAIN. Введение В отличие от принтеров сканеры изначально не поддерживались ОС Windows и не имеют API для работы с ними. В начале своего появления сканеры взаимодействовали с программами посредством уникального для каждой модели сканера интерфейса, что серьезно затрудняло включение поддержки работы со сканером в прикладные программы. Для решения этой проблемы был разработан TWAIN - индустриальный стандарт интерфейса программного обеспечения для передачи изображений из различных устройств в Windows и Macintosh. Стандарт издан и поддерживается TWAIN рабочей группой - официальный сайт www.twain.org. Стандарт издан в 1992 г. В настоящее время действует версия 1.9 от января 2000 г. Абревеатура TWAIN изначально не имела какого-то определенного смысла хотя позже была придумана расшифровка: (Technology Without An Interesting Name - Технология без интересного имени). TWAIN - не протокол аппаратного уровня, он требует драйвера (названного Data Source или DS) для каждого устройства. К настоящему времени (май 2000 г.) TWAIN доступен для Windows 3.1 и выше (Intel и совместимые процессоры), Macintosh и OS/2. Для Linux самый близкий стандарт - SANE. Менеджер TWAIN (DSM) - действует как координатор между приложениями и Источником Данных (Data Source). DSM имеет минимальный пользовательский интерфейс - только выбор DS. Все взаимодействие с пользователем вне прикладной программы осуществляется по средствам DS. Каждый источник данных разрабатывается непосредственно производителем соответствующих устройств. И их поддержка стандарта TWAIN осуществляется на добровольной основе. Использование TWAIN DSM и DS это DLLs загружаемые в адресное пространство приложения и работают как подпрограммы приложения. DSM использует межпроцессную связь, что бы координировать действия со своими копиями, когда больше чем одна программа использует TWAIN.Упрощенная схема действия приложения использующего TWAIN:
Открыть диалог настройки соответствующего устройства (диалог отображает DS) и задать соответствующие настройки. Приложение ожидает сообщение от DS, что изображение готово. Во время ожидания все зарегистрированные сообщения будут направляться через TWAIN. Если это не будет выполняться, то приложение не получит сообщения о готовности изображения. Приложение принимает изображение от DS. TWAIN определяет три типа передачи изображения: Native - в Windows это DIB в памяти Memory - как блоки пикселей в буферах памяти File - DS записывает изображение непосредственно в файл (не обязательно поддерживается) Приложение закрывает DS. Использование EZTWAIN Данная библиотека была разработана, что бы упростить разработку программ использующих TWAIN предоставляя разработчику упрощенную версию TWAIN API. EZTWAN обеспечивает передачу всех windows сообщений через TWAIN и ожидает сообщения о готовности изображения. Библиотека EZTWAIN является свободно распространяемой библиотекой с открытыми исходными кодами. В настоящее время выпущена версия 1.12. Библиотеку можно свободно скачать с сайта: www.dosadi.com, библиотека написана на C и предназначена для использования как DLL, необходимый для ее использования с Delphi модуль так же можно скачать с сайта. Кроме нее у меня с сайта можно скачать модификацию данной библиотеки, предназначенную для статической компоновки с программой на Delphi. Указанная версия (MultiTWAIN for Delphi) не требует наличия библиотеки EZTW32.DLL. Структура программы Используемые функции. Перед вызовом функций сканирования необходимо вызвать функцию: TWAIN_SelectImageSource(hwnd: HWND): Integer; Данная функция позволяет выбрать источник получения данных из списка TWAIN совместимых устройств, в качестве параметра она получает хендл основного окна прикладной программы. Следует заменить, что если в системе имеется одно TWAIN совместимое устройство, то вызывать функцию не обязательно. Для получения изображения служит функция: TWAIN_AcquireNative(hwnd: HWND; pixmask: Integer): HBitmap; где: hwnd - хендел основного окна прикладной программы (допускается указывать 0); pixmask - режим сканирования ( необходимо задавать 0 - указание другого режима может приводить к ошибке); hBitmap - указатель на область памяти, содержащей полученные данные в DIB формате. По окончании работы с DIB данными их необходимо удалить вызвав процедуру: TWAIN_FreeNative(hDIB: HBitmap); где: hDIB - указатель, полученный при вызове функции TWAIN_AcquireNative.Для облегчения обработки полученных DIB данных в библиотеке имеется несколько сервисных функций:
TWAIN_DibWidth(hDib: HBitmap): Integer; // Получает ширину изображения в пикселях TWAIN_DibHeight(hDib: HBitmap): Integer; // Получает высоту изображения в пикселях TWAIN_CreateDibPalette(hdib: HBitmap): Integer; // Получает цветовую палитру изображения TWAIN_DrawDibToDC(hDC: HDC; dx, dy, w, h: Integer; hDib: HBitmap; sx, sy: Integer); // Передает DIB данные в формате совместимым // с указанным контекстом устройства.Пример программы
Полный текст примера можно взять отсюда. Мы рассмотрим только функцию получения данных с TWAIN устройства:
procedure TForm1.Accquire1Click(Sender: TObject);
var
dat: hBitMap;
PInfo: PBitMapInfoHeader;
Height, Width: integer;
{Функция возведения 2 в степень s}
function stp2(s: byte): longint;
var
m: longint;
i: byte;
begin
m := 2;
for i := 2 to s do
m := m * 2;
stp2 := m;
end;
begin
{Получаем указатель на графические данные}
dat := TWAIN_AcquireNative(Handle, 0);
if dat <> 0 then
begin
{Получаем указатель на область памяти содержащей DIB
данные и блокируем область памяти}
PInfo := GlobalLock(dat);
{Анализируем полученные данные}
Height := PInfo.biHeight;
Width := PInfo.biWidth;
{Узнаем размер полученного изображения в сантиметрах}
Wcm.Caption := floatToStrF(100 / PInfo.biXPelsPerMeter * Width, ffNumber, 8,
3)
+ ' cm';
Hcm.Caption := floatToStrF(100 / PInfo.biYPelsPerMeter * Height, ffNumber,
8, 3)
+ ' cm';
{Определяем число цветов в изображении}
Colors.Caption := floatToStrF(stp2(PInfo.biBitCount), ffNumber, 8, 0) +
' цветов';
{Разблокируем память}
GlobalUnlock(dat);
{Передаем в битовую матрицу графические данные}
{И устанавливаем перехват ошибок}
try
MyBitMap.Palette := TWAIN_CreateDibPalette(dat);
MyBitMap.Width := Width;
MyBitMap.Height := Height;
TWAIN_DrawDibToDC(MyBitMap.Canvas.Handle, 0, 0, Width, Height, dat, 0, 0);
except
// Обрабатываем наиболее вероятную ошибку связанную
// с не хваткой ресурсов для загрузки изображения
on EOutOFResources do
MessageDlg('TBitMap: Нет ресурсов для загрузки изображения!',
mtError, [mbOk], 0);
end;
{Отображаем графические данные}
Image1.Picture.Graphic := MyBitMap;
{Освобождаем память занятую графическими данными}
TWAIN_FreeNative(dat);
end;
end;
Обработка ошибок необходима, так как объект TBitMap имеет серьезные ограничения на размер создаваемого изображения. При этом производится обработка наиболее вероятной ошибки, в случае возникновения другой ошибки, ее обработка будет передана обработчику по умолчанию. Обработка ошибки в данном случае заключается в выдаче диагностического сообщения, в прикладной программе можно реализовать выполнение любых необходимых действий, например, произвести уменьшение разрешения и повторно подать на загрузку в TBitMap.
Заключение
Приведенный здесь пример тестировался на сканере Umax 2000P с драйвером VistaScan32 V3.52. При получении изображений следует помнить, что максимальный размер блока памяти, который может распределить Windows, составляет 2 Гб и при попытке сканировании страниц формата АЧ с высоким разрешением можно превысить этот предел. Кроме того, достаточно простой в обращении объект TBitMap имеет куда более серьезные ограничения на размер загружаемых изображений, что требует непосредственной работы с DIB данными. Но это уже тема для отдельной статьи. Если у Вас появились вопросы или предложения пишите мне: speclab@4unet.ru.
Сканирование версии структуры базы данных.
Спасибо за идеи, высказанные в группах новостей и присланные по электронной почте. Я думаю, что нашел лучшее решение.Очевидно, BDE содержит номер версии структуры, по крайней мере для файлов Paradox. (Я не могу поручиться за dBase и другие форматы.) Всякий раз при изменении структуры (например, в Database Desktop) BDE увеличивает номер версии. Следующий модуль содержит функцию, которая возвращает версию структуры базы данных:
(*****************************************************************************
* DbUtils.pas
*
* Утилита для работы с базами данных
*
* Создана 09/20/96
*****************************************************************************)
unit Dbutils;
(****************************************************************************)
(****************************************************************************)
interface
(****************************************************************************)
(****************************************************************************)
uses DbTables;
function DbGetVersion(table: TTable): LongInt;
(****************************************************************************)
(****************************************************************************)
implementation
(****************************************************************************)
(****************************************************************************)
uses
Db, DbiProcs, DbiTypes, {DbiErrs,}
SysUtils;
{---------------------------------------------------------------------------}
(*
* Цель: определение номера версии структуры таблицы
* Параметры: table (I) - интересующая нас таблица
* Возвращаемая величина: номер версии
* Исключительная ситуация: EDatabaseError
*)
function DbGetVersion(table: TTable): LongInt;
var
hCursor: hDBICur;
tableDesc: TBLFullDesc;
cName: array[0..255] of Char;
begin
{ копируем имя таблицы в строку 'с' }
StrPCopy(cName, table.TableName);
{ просим BDE создать запись, содержащую информацию об определенной таблице }
Check(DbiOpenTableList(table.DBHandle, True, False, cName, hCursor));
{ получаем запись, содержащую информацию о структуре }
Check(DbiGetNextRecord(hCursor, dbiNOLOCK, @tableDesc, nil));
{ возвращаем поле записи, содержащее номер версии структуры нашей таблицы }
Result := tableDesc.tblExt.iRestrVersion;
Check(DbiCloseCursor(hCursor));
end;
end.
|
Рекурсивный проход дерева каталогов.
Автор: Vit WEB-сайт: http://forum.vingrad.ru
procedure ScanDir(Dir: string);
var
SearchRec: TSearchRec;
begin
if Dir <> '' then
if Dir[length(Dir)] <> '\' then
Dir := Dir + '\';
if FindFirst(Dir + '*.*', faAnyFile, SearchRec) = 0 then
repeat
if (SearchRec.name = '.') or (SearchRec.name = '..') then
continue;
if (SearchRec.Attr and faDirectory) <> 0 then
ScanDir(Dir + SearchRec.name)
//we found Directory: "Dir+SearchRec.name"
else
Showmessage(Dir + SearchRec.name);
//we found File: "Dir+SearchRec.name"
until
FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ScanDir('c:');
end;
Как получить список файлов со всеми подкаталогами.
Оформил: DeeCo
Автор: Анатолий Подгорецкий.
procedure ScanDir(StartDir: string; Mask: string; List: TStrings);
var
SearchRec: TSearchRec;
begin
if Mask = '' then
Mask := '*.*';
if StartDir[Length(StartDir)] <> '\' then
StartDir := StartDir + '\';
if FindFirst(StartDir + Mask, faAnyFile, SearchRec) = 0 then
begin
repeat Application.ProcessMessages;
if (SearchRec.Attr and faDirectory) <> faDirectory then
List.Add(StartDir + SearchRec.Name)
else if (SearchRec.Name <> '..') and (SearchRec.Name <> '.')thenbegin
List.Add(StartDir + SearchRec.Name + '\');
ScanDir(StartDir + SearchRec.Name + '\', Mask, List);
end;
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
end;
Пример вызова, параметры 1. имя папки 2. маска, по умолчанию *.* 3. хранилище для резульатат, любой наследник от TString, например TStringList
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear;
ScanDir('c:', '', ListBox1.Items);
Label1.Caption := IntToStr(ListBox1.Items.Count);
end;
Сканируем файл в поисках текста.
function ScanFile(const FileName: string;
const forString: string;
caseSensitive: Boolean): Longint;
{ returns position of string in file or -1, if not found }
const
BufferSize = $8001; { 32K+1 bytes }
var
pBuf, pEnd, pScan, pPos: PChar;
filesize: LongInt;
bytesRemaining: LongInt;
bytesToRead: Word;
F: file;
SearchFor: PChar;
oldMode: Word;
begin
Result := -1; { assume failure }
if (Length(forString) = 0) or (Length(FileName) = 0) then Exit;
SearchFor := nil;
pBuf := nil;
{ open file as binary, 1 byte recordsize }
AssignFile(F, FileName);
oldMode := FileMode;
FileMode := 0; { read-only access }
Reset(F, 1);
FileMode := oldMode;
try { allocate memory for buffer and pchar search string }
SearchFor := StrAlloc(Length(forString) + 1);
StrPCopy(SearchFor, forString);
if not caseSensitive then { convert to upper case }
AnsiUpper(SearchFor);
GetMem(pBuf, BufferSize);
filesize := System.Filesize(F);
bytesRemaining := filesize;
pPos := nil;
while bytesRemaining > 0 do
begin
{ calc how many bytes to read this round }
if bytesRemaining >= BufferSize then
bytesToRead := Pred(BufferSize)
else
bytesToRead := bytesRemaining;
{ read a buffer full and zero-terminate the buffer }
BlockRead(F, pBuf^, bytesToRead, bytesToRead);
pEnd := @pBuf[bytesToRead];
pEnd^ := #0;
{ scan the buffer. Problem: buffer may contain #0 chars! So we
treat it as a concatenation of zero-terminated strings. }
pScan := pBuf;
while pScan < pEnd do
begin
if not caseSensitive then { convert to upper case }
AnsiUpper(pScan);
pPos := StrPos(pScan, SearchFor); { search for substring }
if pPos <> nil then
begin { Found it! }
Result := FileSize - bytesRemaining +
Longint(pPos) - Longint(pBuf);
Break;
end;
pScan := StrEnd(pScan);
Inc(pScan);
end;
if pPos <> nil then Break;
bytesRemaining := bytesRemaining - bytesToRead;
if bytesRemaining > 0 then
begin
{ no luck in this buffers load. We need to handle the case of
the search string spanning two chunks of file now. We simply
go back a bit in the file and read from there, thus inspecting
some characters twice
}
Seek(F, FilePos(F) - Length(forString));
bytesRemaining := bytesRemaining + Length(forString);
end;
end; { While }
finally
CloseFile(F);
if SearchFor <> nil then StrDispose(SearchFor);
if pBuf <> nil then FreeMem(pBuf, BufferSize);
end;
end; { ScanFile }
|
Сканирование shared-ресурсов.
Оформил: DeeCo
В результате экспериментов с NetBIOS был реализован метод сканирования Internet в поисках открытых для записи разделяемых (shared) ресурсов. Подобная идея уже применялась в Legion, но простое дополнение этой схемы простейшим алгоритмом подбора паролей позволило добиться неожиданно "хороших" результатов.
Алгоритм сканированияСканирование выполняется параллельно для каждого из заданных адресов (при наличии достаточных ресурсов у атакующего). Сканируется сеть класса C, кроме 1.2.3.0 и 1.2.3.255.
Первая стадия - сбор данных.
Послать запрос ping. Если нет ответа, то пропустить этот адрес (и не атаковать эту машину на второй стадии). Определить имя DNS (на всякий случай, оно нигде не используется) Попробовать установить null session(1). Попробовать получить список shared resources. Если null session установить не удалось, то закончить первую стадию для этого адреса. Попробовать получить список имен пользователей. Попробовать получить информацию об операционной системе, а именно Платформу (Windows 9x, Windows NT) Версию LAN Manager (Для NT: 4.00 => NT4, 5.00 => Windows 2000) Роль системы в сети: флаги Master Browser, Backup Browser, PDC, BDC, RAS dialin server.Вторая стадия - подбор паролей. Все имена пользователей, полученные на первой стадии, объединяются в один список, что позволяет учесть доменную архитектуру систем на базе Windows NT. На этой стадии проверяются соединения типа net use \\1.2.3.4\IPC$ password /user:username. Если соединение успешно, то проверяются все возможность подключения ко всем shared resources, и, когда подключение успешно, возможность записи.
Проверить подключение со случайным именем (используется результат GetTickCount() в десятичном представлении) и пустым паролем. Если подключение успешно, то считать, что разрешено подключение Guest, и дальнейшие проверки паролей будут бессмысленны(2). Проверить shared resources и больше не подбирать пароли на эту машину. Проверить подключения со всеми именами, собранными на первой стадии. Проверяется пустой пароль и пароль, совпадающий с именем пользователя. Если подключение успешно, то проверить shared resources.(1) Null session - анонимное соединение (с пустым именем и паролем). То же, что и net use \\1.2.3.4\IPC$ "" /user:"" (2) Это не совсем верно. Правильным вариантом было бы все равно проверять все имена, и каждый раз отслеживать изменения прав доступа к shared resources.
ЗащитаАтака может быть предотвращена следующими методами:
отключением NetBIOS over TCP/IP запретом доступа к портам 135 и 139 при помощи firewall запретом анонимного доступа к системе путем установки в 1 значения ключа HKLM\System\CurrentControlSet\Control\Lsa\RestrictAnonymous выбором устойчивых паролей запретом пользователя Guest или введением для него login station restrictions специальными программными средствамиВведение account lockout смысла, скорее всего, не имеет, поскольку проводятся всего две попытки угадать пароль.
Стандартное протоколирование доступа методами NT не позволяет установить IP атакующего (только NetBIOS имя машины и домен, которые могут быть произвольно изменены). Однако протокол позволяет идентифицировать прошедшую атаку: сначала идет одна запись об успешном входе в систему пользователя ANONYMOUS, а потом (после паузы около минуты) серия записей о неуспешных попытках входа с разными именами пользователей (в том числе и тех, которых нет на этой машине; по две записи на имя). Если среди них есть хотя бы одна запись об успешном входе, систему следует считать скомпрометированной.
РеализацияДля проверки метода была написана программа, реализующая вышеописанный алгоритм (это заняло около 6 часов вместе с тестированием и перерывами на покурить). Тестовые сканирования проводились на Dual Celeron 400MHz 96Mb RAM с одновременным запуском до 128 потоков.
СтатистикаБыл проведен ряд экспериментальных сканирований. В качестве источника адресов подсетей был использован Intruders list, полученный при помощи BlackICE, однако разработка алгорима быстрого определения "населенных" подсетей не представляет никакой сложности. Сканирование 254 адресов занимает от двух до десяти минут в зависимости от качества связи и общего количества имен пользователей в сети.
Кратко сформулированный результат такой:
примерно 10% паролей пользователей Windows может быть угадано с двух попыток примерно 40% Windows-машин, доступных по NetBIOS over TCP/IP, выставляют ресурсы в общее пользование без (достаточной) защиты примерно каждая третья share, доступная из Internet, разрешает записьКроме того: за два часа не очень напряженной работы можно найти три машины с Windows NT, к которым можно подключиться по сети с правами администратора (CONNECT: Administrator : <NO PASSWORD> :).
Детальная статистика:
Всего адресов просканировано 1524 100% На ping ответило 442 29% Null session установлено с 102 6.7% Guest разрешен(1) 57 3.7% Уязвимых машин(2) 45 3.0%
(1) Т.е. возможно подключение к IPC$ со случайным именем (2) Уязвимыми считались все машины, которые позволяли подключить разделяемый ресурс хотя бы на чтение.
Распределение платформ. Всего успешных запросов 102 100% Windows 95/98 58 57% Windows NT или SAMBA 44 43%
Пользователи. Всего имен проверено 177 100% С пустым паролем 10 6% С паролем, совпадающим с именем 10 6%
Shared resources. Всего директорий 278 100% Доступно на чтение 171 62% Доступно на запись 91 33%
Перспективы примененияВо-первых, эта атака (после незначительного усовершенствования) может быть использована для быстрого заселения обширных территорий, например, BackOrifice или программами для проведения DDoS-атак.
Во-вторых, на этом алгоритме может быть реализован сетевой червь, даже заниженная оценка скорости распространения которого производит тяжелое впечатление.
Как получить Регистрационный Номер для Sceleton ScreenSaver.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Взять программу можно тут: http://psy.plex.ru или тут: http://www.bik.donetsk.ua/pc_club/ в разделе software) tutorial by vallkor (vallkor@chat.ru) В руки мне попался этот скринсевер и, впринципе, мне понравился. На самом деле, это скорее всего обысная модель из 3dStudioMax, ну да ладно, не об этом я. Так вот, скачал я эту программу и хотел на нее посмотреть, но жестоко обломался, когда увидел надпись на экране: "Зарегестрировать можно на psy.plex.ru". Ну, думаю, люди ваще офигели: даже в скринсейвера встраивают регистрацию. Как оказалось - регистрация бесплатна, но для этого нужно лезть в инет и искать что-то, т.к. у меня с инетом напряг, то лучше я её ломану :) Я открыл свойства рабочего стола, потом "Заставка", выбрал в меню заставок "Sceleton" и потом выбрал "Настройки", затем нажимаю "Register" и что же я вижу: "После регистрации текст с экрана исчезнет" - очень приятно Ж) Рег. имя: Рег. номер: Вводим vallkor/123321 окрываем СофтАйс (далее айс) и ставим брекпоинт bpx GetWindowTextA и нажимаем OK. брык! Выскочили в айсе, так, значит нажимаем F5, чтобы программа и наш регистрационный номер считала. Опа! Опять выскочили в айсе. Теперь нажимаем F11 чтобы выйти из вызова функции. И попадаем сюда:lea edx,[ebp-08] lea eax,[ebp-020C] ;в eax введенный нами РН call 0040791C mov edx,[ebp-08] ;в edx введенный нами РН mov eax,[ebp-04] ;в eax введенное нами имя call 0043A7F4 ;вызов функции генерации и проверки РН Зайдем в эту функцию (F8) и вывалимся тут: push ebp mov ebp, esp add esp, -10 push ebx xor ecx, ecxЭто не то всё! Поэтому идем вниз по программе (F10) пока не наткнемся на это место:
call 0043A6FC ;здесь и "куют" нужный нам РН mov eax,[ebp-0C] ;здесь нужный нам РН! нажимай d eax и он твой! (1) mov edx,[ebp-08] ;а это введенный нами (2) call 00403BB0 ;а это проверка наших РН'овИтак, как уже было сказано и показано в строке (1) в eax записывается сгенерированный РН, поэтому в строке (2) вводим d eax и в окне памяти в айсе мы видим нужный РН, пишем его на бумагу, закрываем айс, вводим его вместо "123321" и можно пользовать. ЗЫ: как оказалось впоследствие сгенерированный РН не затирается даже после сообщения о неверном РН, что ООЧень глупо! Поэтому можно было сделать так:
s ds:0 l ffffffff "123321" и рядом в памяти валялся бы нужный нам номер!ЗЗЫ: спасибо автору icedump'a за встроенный тетрис :) Все пожелания и оскорбления присылайте сюда: vallkor@chat.ru
Выключение компьютера в заданное время.
Оформил: DeeCo
Автор: Олег Агранович.
Программа для выключения компьютера в заданное время. Если запустить с параметром, указав время, то программа запустится скрытно и выключит компьютер в указанное время. Проверенно на Windows XP. Для выключения используется процедура:
procedure ShutdownComputer;
var
ph: THandle;
tp, prevst: TTokenPrivileges;
rl: DWORD;
begin
OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or
TOKEN_QUERY, ph);
LookupPrivilegeValue(nil, 'SeShutdownPrivilege', tp.Privileges[0].Luid);
tp.PrivilegeCount := 1;
tp.Privileges[0].Attributes := 2;
AdjustTokenPrivileges(ph, FALSE, tp, SizeOf(prevst), prevst, rl);
ExitWindowsEx(EWX_SHUTDOWN or EWX_POWEROFF, 0);
end;
Скачать (обновление от 01.07.02): Исполняемый файл Sleeper.zip (161 K) Исходные коды Sleeper_sources.zip (6 K)
ScreenMate.
| Новая игра. Казахский DOOM! Никаких тебе лабиринтов! Голая степь! |
P.s К сожалению вам надо позаботиться о кадрах анимации этого персонажа самим т.к рисунки я послать немогу...
{*******************************************************}
{ }
{ Delphi VCL Extensions (RX) }
{ }
{ Copyright (c) 1995, 1996 AO ROSNO }
{ Copyright (c) 1997, 1998 Master-Bank }
{ }
{*******************************************************}
unit Animate;
interface
{$I RX.INC}
uses Messages, {$IFDEF WIN32}Windows, {$ELSE}WinTypes, WinProcs,
{$ENDIF}
SysUtils, Classes, Graphics, Controls, Forms, StdCtrls, Menus,
ExtCtrls;
type
TGlyphOrientation = (goHorizontal, goVertical);
{ TRxImageControl }
TRxImageControl = class(TGraphicControl)
private
FDrawing: Boolean;
protected
FGraphic: TGraphic;
function DoPaletteChange: Boolean;
procedure DoPaintImage; virtual; abstract;
procedure PaintDesignRect;
procedure PaintImage;
procedure PictureChanged;
public
constructor Create(AOwner: TComponent); override;
end;
{ TAnimatedImage }
TAnimatedImage = class(TRxImageControl)
private
{ Private declarations }
FActive: Boolean;
FAutoSize: Boolean;
FGlyph: TBitmap;
FImageWidth: Integer;
FImageHeight: Integer;
FInactiveGlyph: Integer;
FOrientation: TGlyphOrientation;
FTimer: TTimer;
FNumGlyphs: Integer;
FGlyphNum: Integer;
FStretch: Boolean;
FTransparentColor: TColor;
FOpaque: Boolean;
FTimerRepaint: Boolean;
FOnFrameChanged: TNotifyEvent;
FOnStart: TNotifyEvent;
FOnStop: TNotifyEvent;
procedure DefineBitmapSize;
procedure ResetImageBounds;
procedure AdjustBounds;
function GetInterval: Cardinal;
procedure SetAutoSize(Value: Boolean);
procedure SetInterval(Value: Cardinal);
procedure SetActive(Value: Boolean);
procedure SetOrientation(Value: TGlyphOrientation);
procedure SetGlyph(Value: TBitmap);
procedure SetGlyphNum(Value: Integer);
procedure SetInactiveGlyph(Value: Integer);
procedure SetNumGlyphs(Value: Integer);
procedure SetStretch(Value: Boolean);
procedure SetTransparentColor(Value: TColor);
procedure SetOpaque(Value: Boolean);
procedure ImageChanged(Sender: TObject);
procedure UpdateInactive;
procedure TimerExpired(Sender: TObject);
function TransparentStored: Boolean;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
protected
{ Protected declarations }
function GetPalette: HPALETTE; override;
procedure Loaded; override;
procedure Paint; override;
procedure DoPaintImage; override;
procedure FrameChanged; dynamic;
procedure Start; dynamic;
procedure Stop; dynamic;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure DoPaintImageOn(Mycanvas: Tcanvas; x, y: integer);
virtual;
published
{ Published declarations }
property Active: Boolean read FActive write SetActive default
False;
property Align;
property AutoSize: Boolean read FAutoSize write SetAutoSize
default True;
property Orientation: TGlyphOrientation read FOrientation write
SetOrientation
default goHorizontal;
property Glyph: TBitmap read FGlyph write SetGlyph;
property GlyphNum: Integer read FGlyphNum write SetGlyphNum
default 0;
property Interval: Cardinal read GetInterval write SetInterval
default 100;
property NumGlyphs: Integer read FNumGlyphs write SetNumGlyphs
default 1;
property InactiveGlyph: Integer read FInactiveGlyph write
SetInactiveGlyph default -1;
property TransparentColor: TColor read FTransparentColor write
SetTransparentColor
stored TransparentStored;
property Opaque: Boolean read FOpaque write SetOpaque default
False;
property Color;
property Cursor;
property DragCursor;
property DragMode;
property ParentColor default True;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property Stretch: Boolean read FStretch write SetStretch default
True;
property Visible;
property OnClick;
property OnDblClick;
property OnMouseMove;
property OnMouseDown;
property OnMouseUp;
property OnDragOver;
property OnDragDrop;
property OnEndDrag;
{$IFDEF WIN32}
property OnStartDrag;
{$ENDIF}
property OnFrameChanged: TNotifyEvent read FOnFrameChanged write
FOnFrameChanged;
property OnStart: TNotifyEvent read FOnStart write FOnStart;
property OnStop: TNotifyEvent read FOnStop write FOnStop;
end;
implementation
uses RxConst, VCLUtils;
{ TRxImageControl }
constructor TRxImageControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := [csClickEvents, csCaptureMouse, csOpaque,
{$IFDEF WIN32}csReplicatable, {$ENDIF}csDoubleClicks];
Height := 105;
Width := 105;
ParentColor := True;
end;
procedure TRxImageControl.PaintImage;
var
Save: Boolean;
begin
Save := FDrawing;
FDrawing := True;
try
DoPaintImage;
finally
FDrawing := Save;
end;
end;
procedure TRxImageControl.PaintDesignRect;
begin
if csDesigning in ComponentState then
with Canvas do
begin
Pen.Style := psDash;
Brush.Style := bsClear;
Rectangle(0, 0, Width, Height);
end;
end;
function TRxImageControl.DoPaletteChange: Boolean;
var
ParentForm: TCustomForm;
Tmp: TGraphic;
begin
Result := False;
Tmp := FGraphic;
if Visible and (not (csLoading in ComponentState)) and (Tmp <>
nil)
{$IFDEF RX_D3} and (Tmp.PaletteModified){$ENDIF} then
begin
if (GetPalette <> 0) then
begin
ParentForm := GetParentForm(Self);
if Assigned(ParentForm) and ParentForm.Active and
Parentform.HandleAllocated then
begin
if FDrawing then
ParentForm.Perform(WM_QUERYNEWPALETTE, 0, 0)
else
PostMessage(ParentForm.Handle, WM_QUERYNEWPALETTE, 0, 0);
Result := True;
{$IFDEF RX_D3}
Tmp.PaletteModified := False;
{$ENDIF}
end;
end
{$IFDEF RX_D3}
else
begin
Tmp.PaletteModified := False;
end;
{$ENDIF}
end;
end;
procedure TRxImageControl.PictureChanged;
begin
if (FGraphic <> nil) then
if DoPaletteChange and FDrawing then
Update;
if not FDrawing then
Invalidate;
end;
{ TAnimatedImage }
constructor TAnimatedImage.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTimer := TTimer.Create(Self);
Interval := 100;
FGlyph := TBitmap.Create;
FGraphic := FGlyph;
FGlyph.OnChange := ImageChanged;
FGlyphNum := 0;
FNumGlyphs := 1;
FInactiveGlyph := -1;
FTransparentColor := clNone;
FOrientation := goHorizontal;
FAutoSize := True;
FStretch := True;
Width := 32;
Height := 32;
end;
destructor TAnimatedImage.Destroy;
begin
FOnFrameChanged := nil;
FOnStart := nil;
FOnStop := nil;
FGlyph.OnChange := nil;
Active := False;
FGlyph.Free;
inherited Destroy;
end;
procedure TAnimatedImage.Loaded;
begin
inherited Loaded;
ResetImageBounds;
UpdateInactive;
end;
function TAnimatedImage.GetPalette: HPALETTE;
begin
Result := 0;
if not FGlyph.Empty then
Result := FGlyph.Palette;
end;
procedure TAnimatedImage.ImageChanged(Sender: TObject);
begin
FTransparentColor := FGlyph.TransparentColor and not PaletteMask;
DefineBitmapSize;
AdjustBounds;
PictureChanged;
end;
procedure TAnimatedImage.UpdateInactive;
begin
if (not Active) and (FInactiveGlyph >= 0) and
(FInactiveGlyph < FNumGlyphs) and (FGlyphNum <> FInactiveGlyph) then
begin
FGlyphNum := FInactiveGlyph;
end;
end;
function TAnimatedImage.TransparentStored: Boolean;
begin
Result := (FGlyph.Empty and (FTransparentColor <> clNone)) or
((FGlyph.TransparentColor and not PaletteMask) <>
FTransparentColor);
end;
procedure TAnimatedImage.SetOpaque(Value: Boolean);
begin
if Value <> FOpaque then
begin
FOpaque := Value;
PictureChanged;
end;
end;
procedure TAnimatedImage.SetTransparentColor(Value: TColor);
begin
if Value <> TransparentColor then
begin
FTransparentColor := Value;
PictureChanged;
end;
end;
procedure TAnimatedImage.SetOrientation(Value: TGlyphOrientation);
begin
if FOrientation <> Value then
begin
FOrientation := Value;
DefineBitmapSize;
AdjustBounds;
Invalidate;
end;
end;
procedure TAnimatedImage.SetGlyph(Value: TBitmap);
begin
FGlyph.Assign(Value);
end;
procedure TAnimatedImage.SetStretch(Value: Boolean);
begin
if Value <> FStretch then
begin
FStretch := Value;
PictureChanged;
if Active then
Repaint;
end;
end;
procedure TAnimatedImage.SetGlyphNum(Value: Integer);
begin
if Value <> FGlyphNum then
begin
if (Value < FNumGlyphs) and (Value >= 0) then
begin
FGlyphNum := Value;
UpdateInactive;
FrameChanged;
PictureChanged;
end;
end;
end;
procedure TAnimatedImage.SetInactiveGlyph(Value: Integer);
begin
if Value < 0 then
Value := -1;
if Value <> FInactiveGlyph then
begin
if (Value < FNumGlyphs) or (csLoading in ComponentState) then
begin
FInactiveGlyph := Value;
UpdateInactive;
FrameChanged;
PictureChanged;
end;
end;
end;
procedure TAnimatedImage.SetNumGlyphs(Value: Integer);
begin
FNumGlyphs := Value;
if FInactiveGlyph >= FNumGlyphs then
begin
FInactiveGlyph := -1;
FGlyphNum := 0;
end
else
UpdateInactive;
FrameChanged;
ResetImageBounds;
AdjustBounds;
PictureChanged;
end;
procedure TAnimatedImage.DefineBitmapSize;
begin
FNumGlyphs := 1;
FGlyphNum := 0;
FImageWidth := 0;
FImageHeight := 0;
if (FOrientation = goHorizontal) and (FGlyph.Height > 0) and
(FGlyph.Width mod FGlyph.Height = 0) then
FNumGlyphs := FGlyph.Width div FGlyph.Height
else if (FOrientation = goVertical) and (FGlyph.Width > 0) and
(FGlyph.Height mod FGlyph.Width = 0) then
FNumGlyphs := FGlyph.Height div FGlyph.Width;
ResetImageBounds;
end;
procedure TAnimatedImage.ResetImageBounds;
begin
if FNumGlyphs < 1 then
FNumGlyphs := 1;
if FOrientation = goHorizontal then
begin
FImageHeight := FGlyph.Height;
FImageWidth := FGlyph.Width div FNumGlyphs;
end
else {if Orientation = goVertical then}
begin
FImageWidth := FGlyph.Width;
FImageHeight := FGlyph.Height div FNumGlyphs;
end;
end;
procedure TAnimatedImage.AdjustBounds;
begin
if not (csReading in ComponentState) then
begin
if FAutoSize and (FImageWidth > 0) and (FImageHeight > 0) then
SetBounds(Left, Top, FImageWidth, FImageHeight);
end;
end;
type
TParentControl = class(TWinControl);
|
Создание хранителя экрана (Screen Saver).
Время от времени я наблюдаю вопросы с просьбой рассказать о процессе создания с помощью Delphi хранителя экрана, которого можно было бы выбрать с помощью панели управления (Control Panel / Desktop). После того, как я увидел несколько общих ответов, частично отвечавших на заданный вопрос, я решил создать данный совет и полностью посвятить вас в технологию создания хранителя экрана Windows от начала до конца. Приведенный ниже код позволяет создать предмет нашего разговора, а именно - простой хранитель экрана Windows. Полный исходный код данного хранителя приведен в конце совета. Ну а теперь обсудим подробности создания этого типа программ, являющихся ровесниками первого компьютера. Вступление Хранитель экрана Windows является обыкновенным исполнимым приложением Windows, имеющим в отличие от стандартных программ расширение .SCR. Тем не менее, для корректного связывания с панелью управления, хранитель должен соблюдать определенные требования. В общих чертах программа должна: поддерживать опции настройки содержать свое описание различать состояния активного режима и режима конфигурации недопускать запуск своей второй копии осуществлять выход, если пользователь нажал клавишу или переместил мышьНиже я попытаюсь показать, как каждое из этих требований может быть удовлетворено с помощью Delphi.
С самого началаХранитель экрана, который мы собираемся создать, во время очередного простоя компьютера очищает экран и рисует затененные сферы в произвольных местах экрана, периодически их стирая и начиная заново. Пользователь может определить максимальное количество выводимых на экран сфер и скорость их рисования.
Прежде всего создайте новый, пустой проект, выбрав пункт New Project из меню File. (Если вы находитесь в репозитарии, выберите "Blank project".).
Конфигурационная формаПервое, что видит большинство людей при запуске хранителя экрана - диалог настройки. В нем пользователь может определить значения для специфических опций хранителя экрана. Для того, чтобы создать такую форму, измените свойства Form1 (создается автоматически при создании нового проекта) как показано ниже:
BorderIcons [biSystemMenu] biSystemMenu True biMinimize False biMaximize False BorderStyle bsDialog Caption Configuration Height 162 Name CfgFrm Position poScreenCenter Visible False Width 266
Нам необходимо предоставить возможность изменять максимальное количество сфер, выводимых на экране, их размер и скорость рисования. Для того, чтобы это сделать, добавьте следующие три компонента Label (из палитры Standard) и компонент SpinEdit (из палитры Samples): (Примечание: Для быстрого размещения этих компонентов на форме скопируйте этот текст в буфер обмена и замените текст описания формы, выводимый при нажатии на пункт меню "View as Text" контекстного меню формы.).
object Label1: TLabel Left = 16 Top = 19 Width = 58 Height = 16 Alignment = taRightJustify Caption = 'Сфер:' end object Label2: TLabel Left = 41 Top = 59 Width = 33 Height = 16 Alignment = taRightJustify Caption = 'Размер:' end object Label3: TLabel Left = 29 Top = 99 Width = 45 Height = 16 Alignment = taRightJustify Caption = 'Скорость:' end object spnSpheres: TSpinEdit Left = 84 Top = 15 Width = 53 Height = 26 MaxValue = 500 MinValue = 1 TabOrder = 0 Value = 50 end object spnSize: TSpinEdit Left = 84 Top = 55 Width = 53 Height = 26 MaxValue = 250 MinValue = 50 TabOrder = 1 Value = 100 end object spnSpeed: TSpinEdit Left = 84 Top = 95 Width = 53 Height = 26 MaxValue = 10 MinValue = 1 TabOrder = 2 Value = 10 end |
Наконец, нам необходимы три кнопки -- OK, Отмена и Тест. Кнопка Тест не является стандартной для диалогов настройки хранителей экрана, но она позволяет удобно и легко проверить сделанные изменения. Добавьте следующие три кнопки, используя компоненты BitBtn из палитры "Additional":
object btnOK: TBitBtn Left = 153 Top = 11 Width = 89 Height = 34 TabOrder = 3 Kind = bkOK end object btnCancel: TBitBtn Left = 153 Top = 51 Width = 89 Height = 34 TabOrder = 4 Kind = bkCancel end object btnTest: TBitBtn Left = 153 Top = 91 Width = 89 Height = 34 Caption = 'Тест...' TabOrder = 5 Kind = bkIgnore end |
Перейдите к редактору кода модуля Setup и добавьте следующее объявление в секции используемых модулей:
uses IniFiles; |
Затем добавьте следующие объявления процедур в секцию private модуля TCfgFrm:
procedure LoadConfig; procedure SaveConfig; |
Теперь создайте код самих процедур после секции uses в разреле реализации:
const
CfgFile = 'SPHERES.INI';
procedure TCfgFrm.LoadConfig;
var
inifile : TIniFile;
begin
inifile := TIniFile.Create(CfgFile);
try
with inifile do begin
spnSpheres.Value := ReadInteger('Config', 'Spheres', 50);
spnSize.Value := ReadInteger('Config', 'Size', 100);
spnSpeed.Value := ReadInteger('Config', 'Speed', 10);
end;
finally
inifile.Free;
end;
end; {TCfgFrm.LoadConfig}
procedure TCfgFrm.SaveConfig;
var
inifile : TIniFile;
begin
inifile := TIniFile.Create(CfgFile);
try
with inifile do begin
WriteInteger('Config', 'Spheres', spnSpheres.Value);
WriteInteger('Config', 'Size', spnSize.Value);
WriteInteger('Config', 'Speed', spnSpeed.Value);
end;
finally
inifile.Free;
end;
end; {TCfgFrm.SaveConfig}
|
Для обеспечения необходимой функциональности нашей формы нам необходимо создать обработчики некоторых событий, чтобы правильно и в нужный момент загрузить или сохранить конфигурацию. Сначала, в момент запуска программы, нам необходимо автоматически загрузить сохраненную конфигурацию. Для этого нам нужно обработать событие событие OnCreate нашей конфигурационной формы. Дважды щелкните на событии OnCreate в Инспекторе Объектов и введите следующий код:
procedure TCfgFrm.FormCreate(Sender: TObject);
begin
LoadConfig;
end; {TCfgFrm.FormCreate}
|
Далее дважды щелкните на кнопке OK. Нам необходимо сохранять текущую конфигурацию и закрывать окно при каждом нажатии данной кнопки, поэтому введите следующий код:
procedure TCfgFrm.btnOKClick(Sender: TObject);
begin
SaveConfig;
Close;
end; {TCfgFrm.btnOKClick}
|
Для того чтобы закрыть форму, не сохраняя результаты, напишите следующий обработчик для кнопки Отмена (дважды щелкните по ней):
procedure TCfgFrm.btnCancelClick(Sender: TObject);
begin
Close;
end; {TCfgFrm.btnCancelClick}
|
Наконец, чтобы протестировать хранителя экрана, мы должны вывести на экран его форму (которую мы еще не создали). Дважды щелкните на кнопке Тест и введите следующий код:
procedure TCfgFrm.btnTestClick(Sender: TObject);
begin
ScrnFrm.Show;
end; {TCfgFrm.btnTestClick}
|
Затем добавьте "Scrn" в список используемых модулей в секции реализации. Scrn - это модуль формы хранителя экрана, который мы создадим в следующем шаге. Ну а пока сохраните созданный нами модуль с именем "Cfg", выбрав пункт "Save File As" в меню "File".
Форма хранителя экранаСам хранитель экрана будет просто большой, черной формой без заголовка, занимающей весь экран, на котором и будет разворачиваться наше представление. Для того, чтобы создать вторую форму, выберите пункт New Form в меню File или "Blank form" в репозитарии.
BorderIcons [] biSystemMenu False biMinimize False biMaximize False BorderStyle bsNone Color clBlack FormStyle fsStayOnTop Name ScrnFrm Visible False
Расположите на форме единственный Delphi компонент - таймер из палитры System. Установите его свойства как указано ниже:
object tmrTick: TTimer Enabled = False OnTimer = tmrTickTimer Left = 199 Top = 122 end |
Больше никаких компонентов для этой формы не потребуется. Тем не менее мы должны добавить некоторый код, осуществляющий рисование затененных сфер. Переключитесь в редактор кода на модуль формы ScrnFrm. В секции private TScrnFrm добавьте следующее объявление процедуры:
procedure DrawSphere(x, y, size : integer; color : TColor); |
Теперь, в секции реализации модуля, добавьте код для данной процедуры:
procedure TScrnFrm.DrawSphere(x, y, size : integer; color : TColor);
var
i, dw : integer;
cx, cy : integer;
xy1, xy2 : integer;
r, g, b : byte;
begin
with Canvas do begin
{Заполняем установки карандаша и кисти.}
Pen.Style := psClear;
Brush.Style := bsSolid;
Brush.Color := color;
{Подготовим цвета для сфер.}
r := GetRValue(color);
g := GetGValue(color);
b := GetBValue(color);
{Рисуем сферу.}
dw := size div 16;
for i := 0 to 15 do begin
xy1 := (i * dw) div 2;
xy2 := size - xy1;
Brush.Color := RGB(Min(r + (i * 8), 255), Min(g + (i * 8), 255),
Min(b + (i * 8), 255));
Ellipse(x + xy1, y + xy1, x + xy2, y + xy2);
end;
end;
end; {TScrnFrm.DrawSphere}
|
Как вы можете увидеть из кода, мы задаем координаты (x,y) верхней части, левый угол сферы, а также диаметр и базовый цвет. Затем, для того, чтобы нарисовать сферу, мы создаем градиент от базового до самого яркого цвета. Изменяя шаг за шагом цвет кисти, мы рисуем и заполняем каждый раз концентрический круг меньшего диаметра.
Наверняка вы также обратили внимание на то, что функция периодически обращается другой функции, именуемой Min(). Так как это функция не является стандартной функцией Delphi, мы должны добавить ее объявление к данному модулю, выше объявления DrawSphere().
function Min(a, b : integer) : integer;
begin
if b < a then
Result := b
else
Result := a;
end; {Min}
|
Для периодического вызова функции DrawSphere() мы должны реагировать на событие OnTimer компонента Timer, который мы добавили к форме ScrnFrm. Дважды щелкните на этом компоненте и заполните автоматически созданный скелет процедуры следующим кодом:
procedure TScrnFrm.tmrTickTimer(Sender: TObject);
const
sphcount : integer = 0;
var
x, y : integer;
size : integer;
r, g, b : byte;
color : TColor;
begin
if sphcount > CfgFrm.spnSpheres.Value then begin
Refresh;
sphcount := 0;
end;
Inc(sphcount);
x := Random(ClientWidth);
y := Random(ClientHeight);
size := CfgFrm.spnSize.Value + Random(50) - 25;
x := x - size div 2;
y := y - size div 2;
r := Random($80);
g := Random($80);
b := Random($80);
DrawSphere(x, y, size, RGB(r, g, b));
end; {TScrnFrm.tmrTickTimer}
|
Данная процедура осуществляет подсчет рисуемых сфер и осуществляет восстановление (стирание) экрана при достижении максимального числа сфер. Кроме этого, она вычисляет случайную позицию, размер и цвет следующей выводимой сферы. (Примечание: диапазон цветов ограничен только первой половиной спектра яркости для обеспечения большей глубины тени.).
Возможно вы уже обратили внимание, что процедура tmrTickTimer() обращается к форме CfgFrm для получения текущих значений конфигурации. Для того, чтобы эта ссылка работала, добавьте в секцию используемых модулей следующие строчки:
uses Cfg; |
Затем нам необходим способ деактивирования хранителя экрана при нажатии любой клавиши, передвижении мыши или потери фокуса. Реализация этого возможна только с помощью обработчика события Application.OnMessage, которое может реагировать на необходимые для выхода их хранителя экрана сообщения.
Для начала добавьте следующее объявление переменной в секции реализации модуля:
var crs : TPoint; |
Эта переменная необходима для хранения оригинальной позиции курсора мыши для ее последующего сравнения. Теперь добавьте следующее объявление в секции private модуля TScrnFrm:
procedure DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean); |
Добавьте соответствующий код в секцию реализации модуля:
procedure TScrnFrm.DeactivateScrnSaver(var Msg : TMsg; var Handled : boolean);
var
done : boolean;
begin
if Msg.message = WM_MOUSEMOVE then
done := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or
(Abs(HIWORD(Msg.lParam) - crs.y) > 5)
else
done := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_ACTIVATE) or
(Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_NCACTIVATE);
if done then
Close;
end; {TScrnFrm.DeactivateScrnSaver}
|
При получении системного сообщения WM_MOUSEMOVE мы сравниваем новые координаты мыши с оригинальными. Если перемещение мыши превысило допустимое значение (в нашем случае порог составляет 5 пикселей), закрываем хранитель экрана. Далее проверяем нажатие клавиши или передачу фокуса другому окну или диалогу, и в этом случае также закрываем хранитель.
Чтобы процедура работала, нам нужно передать обработчику события Application.OnMessage оригинальную позицию курсора мыши. Лучшее место для получения координат курсора находится в обработчике события формы OnShow:
procedure TScrnFrm.FormShow(Sender: TObject);
begin
GetCursorPos(crs);
tmrTick.Interval := 1000 - CfgFrm.spnSpeed.Value * 90;
tmrTick.Enabled := true;
Application.OnMessage := DeactivateScrnSaver;
ShowCursor(false);
end; {TScrnFrm.FormShow}
|
В данном участке кода мы также задаем периодичность срабатывания таймера и активизируем его, а также прячем курсор мыши. Следующий код не является обязательным, но тем не менее мы включим его в обработчик события OnHide:
procedure TScrnFrm.FormHide(Sender: TObject);
begin
Application.OnMessage := nil;
tmrTick.Enabled := false;
ShowCursor(true);
end; {TScrnFrm.FormHide}
|
И, наконец, нам необходимо убедиться, что при запуске форма хранителя экрана занимает целый экран. Для этого добавьте следующий код в обработчик события формы OnActivate:
procedure TScrnFrm.FormActivate(Sender: TObject);
begin
WindowState := wsMaximized;
end; {TScrnFrm.FormActivate}
|
Сохраните созданный нами модуль ScrnFrm под именем "SCRN.PAS", выбрав пункт Save File в меню File.
Описание хранителя экранаВы можете определить текст, который появится в списке хранителей экранов в Control Panel / Desktop, добавив директиву {$D текст} к исходному файлу проекта. Директива $D вставляет текст в описание модуля выполняемого файла. Чтобы Панель Управления "поняла" этот текст и принадлежность файла к хранителям экрана, описание должно начинаться с зарезервированного слова "SCRNSAVE".
Выберите пункт Project Source из меню View для редактирования исходного кода проекта. Под директивой "{$R *.RES}" добавьте следующую строчку:
{$D SCRNSAVE Spheres Screen Saver}
|
Теперь при выводе списка доступных хранителей экранов в Контрольной Панели вы увидите текст "Spheres Screen Saver", позволяющий выбрать ваш маленький шедевр.
Активный режим и режим конфигурацииWindows запускает программу хранителя экрана при двух возможных условиях: 1) при активизации хранителя экрана, и 2) когда необходимо изменить его настройки. В обоих случаях Windows запускает одну и ту же программу. Для запуска программы в одном из двух режимов предусмотрен параметр командной строки - "/s" для активного режима и "/c" для режима конфигурации. Для корректной работы с Панелью управления наш хранитель экрана должен проверять командную строку на предмет наличия одного из ключа.
Активный режимКогда хранитель экрана стартует в активном режиме (/s), нам необходимо создать и показать именно форму хранителя экрана. Но при этом нам также необходимо создать и форму конфигурации, поскольку она содержит все конфигурационные настройки. При закрытии формы хранителя экрана программа завершает свою работу. В этом режиме форма хранителя экрана является главной формой приложения - она запускается при старте приложения и при ее закрытии приложение завершается.
Режим конфигурацииКогда хранитель экрана стартует в режиме конфигурации (/c), нам необходимо создать и вывести на экран конфигурационную форму. Но при этом нам также необходимо создать и форму хранителя экрана, поскольку пользователь может захотеть протестировать выбранные настройки. Тем не менее, при закрытии конфигурационной формы приложение также должно завершить свою работу. В этом случае мы определяем конфигурационную форму как главную форму приложения.
Определение главной формыВ нашем случае нам необходимо идентифицировать ScrnFrm как главную форму приложения, если в командной строке присутствовал параметр /s, в противном случае главной формой приложения должна быть форма CfgFrm. Чтобы это сделать, необходимо знать одну недокументированную характеристику VCL объекта TApplication: главной формой приложения становится первая форма, создаваемая вызовом Application.CreateForm(). Таким образом, для определения главной формы приложения согласно параметрам, передаваемым во время выполнения программы, следует отредактировать исходный код проекта следующим образом:
begin
if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin
{ScrnFrm должна быть главной формой.}
Application.CreateForm(TScrnFrm, ScrnFrm);
Application.CreateForm(TCfgFrm, CfgFrm);
end else begin
{CfgFrm должна быть главной формой.}
Application.CreateForm(TCfgFrm, CfgFrm);
Application.CreateForm(TScrnFrm, ScrnFrm);
end;
Application.Run;
end.
|
Просто изменяя порядок создания форм, мы автоматически устанавливаем главную форму приложения. Кроме того, основная форма будет показана автоматически, несмотря на то, что для обоих форм мы установили значение свойства Visible равным False. В результате мы получаем желаемый эффект с использованием минимального кода.
(Примечание: для обеспечения показанной выше функциональности необходимо выключить опцию "Complete boolean eval" в пункте меню Options | Project | Compiler settings. В противном случае при отсутствии параметров командной строки будет возникать ошибка.).
Для использования Delphi функции UpperCase(), необходимо включить модуль SysUtils в список используемых модулей исходного кода проекта как показано ниже:
uses
Forms, SysUtils,
Scrn in 'SCRN.PAS' {ScrnFrm},
Cfg in 'CFG.PAS' {CfgFrm};
|
При разработке хранителя экрана необходимо учитывать один ньюанс - недопущение запуска второй копии хранителя. В противном случае Windows будет запускать хранитель экрана каждый раз при наступлении времени его активизации, даже в случае, когда он запущен.
Для того, чтобы недопустить запуск второй копии нашего хранителя, отредактируйте исходный код проекта следующим образом:
begin
{Возможен запуск только одной копии.}
if hPrevInst = 0 then begin
if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then begin
...
end;
Application.Run;
end;
end;
|
Переменная hPrevInst является глобальной переменной, определенной в Delphi для ссылки на запущенные копиии текущей программы. Ее значение будет равно нулю, если хранитель экрана еще не был запущен.
Теперь сохраните файл проекта с именем "SPHERES.DPR" и скомпилируйте программу. Вот мы и получили хранитель экрана, полностью удовлетворяющий всем нашим требованиям. В случае отсутствия параметров в командной строке программа переходит в режим конфигурации, заданный по умолчанию. Вы можете протестировать активный режим, передавая в командной строке первым параметр "/s". (Смотри Run | Parameters...).
Установка хранителя экранаПосле отладки и проверки хранителя экрана вы уже можете его установить и использовать в системе. Для этого скопируйте исполняемый файл (SPHERES.EXE) в директорию Windows и измените его расширение на .SCR, в результате чего вы получите файл с именем SPHERES.SCR. Затем, войдя в Панель Управления, дважды щелкните на иконке Desktop, и выберите Screen Saver | Name. Вы должны увидеть "Spheres Screen Saver" в списке доступных хранителей экрана. Выбрав его и нажав на кнопку ОК, вы тем самым сделаете его активным системным хранителем.
Полный исходный код проекта хранителя экранаCfg.dfm
// Cfg.dfm
object CfgFrm: TCfgFrm
Left = 196
Top = 124
BorderIcons = [biSystemMenu]
BorderStyle = bsDialog
Caption = 'Конфигурация'
ClientHeight = 135
ClientWidth = 258
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'System'
Font.Style = []
PixelsPerInch = 96
Position = poScreenCenter
OnCreate = FormCreate
TextHeight = 16
object Label1: TLabel
Left = 16
Top = 19
Width = 58
Height = 16
Alignment = taRightJustify
Caption = 'Сфер:'
end
object Label2: TLabel
Left = 41
Top = 59
Width = 33
Height = 16
Alignment = taRightJustify
Caption = 'Размер:'
end
object Label3: TLabel
Left = 29
Top = 99
Width = 45
Height = 16
Alignment = taRightJustify
Caption = 'Скорость:'
end
object spnSpheres: TSpinEdit
Left = 84
Top = 15
Width = 53
Height = 26
MaxValue = 500
MinValue = 1
TabOrder = 0
Value = 50
end
object spnSize: TSpinEdit
Left = 84
Top = 55
Width = 53
Height = 26
MaxValue = 250
MinValue = 50
TabOrder = 1
Value = 100
end
object spnSpeed: TSpinEdit
Left = 84
Top = 95
Width = 53
Height = 26
MaxValue = 10
MinValue = 1
TabOrder = 2
Value = 10
end
object btnOK: TBitBtn
Left = 153
Top = 11
Width = 89
Height = 34
TabOrder = 3
OnClick = btnOKClick
Kind = bkOK
end
object btnCancel: TBitBtn
Left = 153
Top = 51
Width = 89
Height = 34
TabOrder = 4
OnClick = btnCancelClick
Kind = bkCancel
end
object btnTest: TBitBtn
Left = 153
Top = 91
Width = 89
Height = 34
Caption = 'Тест...'
TabOrder = 5
OnClick = btnTestClick
Kind = bkIgnore
end
end
// Cfg.pas
unit Cfg;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Buttons, Spin;
type
TCfgFrm = class(TForm)
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
spnSpheres: TSpinEdit;
spnSize: TSpinEdit;
spnSpeed: TSpinEdit;
btnOK: TBitBtn;
btnCancel: TBitBtn;
btnTest: TBitBtn;
procedure FormCreate(Sender: TObject);
procedure btnOKClick(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure btnTestClick(Sender: TObject);
private
{ Private declarations }
procedure LoadConfig;
procedure SaveConfig;
public
{ Public declarations }
end;
var
CfgFrm: TCfgFrm;
implementation
{$R *.DFM}
uses
Scrn,
IniFiles;
const
CfgFile = 'SPHERES.INI';
procedure TCfgFrm.LoadConfig;
var
inifile: TIniFile;
begin
inifile := TIniFile.Create(CfgFile);
try
with inifile do
begin
spnSpheres.Value := ReadInteger('Config', 'Spheres', 50);
spnSize.Value := ReadInteger('Config', 'Size', 100);
spnSpeed.Value := ReadInteger('Config', 'Speed', 10);
end;
finally
inifile.Free;
end;
end; {TCfgFrm.LoadConfig}
procedure TCfgFrm.SaveConfig;
var
inifile: TIniFile;
begin
inifile := TIniFile.Create(CfgFile);
try
with inifile do
begin
WriteInteger('Config', 'Spheres', spnSpheres.Value);
WriteInteger('Config', 'Size', spnSize.Value);
WriteInteger('Config', 'Speed', spnSpeed.Value);
end;
finally
inifile.Free;
end;
end; {TCfgFrm.SaveConfig}
procedure TCfgFrm.FormCreate(Sender: TObject);
begin
LoadConfig;
end; {TCfgFrm.FormCreate}
procedure TCfgFrm.btnOKClick(Sender: TObject);
begin
SaveConfig;
Close;
end; {TCfgFrm.btnOKClick}
procedure TCfgFrm.btnCancelClick(Sender: TObject);
begin
Close;
end; {TCfgFrm.btnCancelClick}
procedure TCfgFrm.btnTestClick(Sender: TObject);
begin
ScrnFrm.Show;
end; {TCfgFrm.btnTestClick}
end.
// SCRN.dfm
object ScrnFrm: TScrnFrm
Left = 196
Top = 98
BorderIcons = []
BorderStyle = bsNone
Caption = 'ScrnFrm'
ClientHeight = 101
ClientWidth = 259
Color = clBlack
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'System'
Font.Style = []
FormStyle = fsStayOnTop
OldCreateOrder = True
OnActivate = FormActivate
OnHide = FormHide
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 16
object tmrTick: TTimer
Enabled = False
OnTimer = tmrTickTimer
Left = 65535
Top = 2
end
end
// SCRN.pas
unit Scrn;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls;
type
TScrnFrm = class(TForm)
tmrTick: TTimer;
procedure tmrTickTimer(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormHide(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
procedure DrawSphere(x, y, size: integer; color: TColor);
procedure DeactivateScrnSaver(var Msg: TMsg; var Handled: boolean);
public
{ Public declarations }
end;
var
ScrnFrm: TScrnFrm;
implementation
{$R *.DFM}
uses
Cfg;
var
crs: TPoint; {Оригинальная позиция курсора мыши.}
function Min(a, b: integer): integer;
begin
if b < a then
Result := b
else
Result := a;
end; {Min}
procedure TScrnFrm.DrawSphere(x, y, size: integer; color: TColor);
var
i, dw: integer;
cx, cy: integer;
xy1, xy2: integer;
r, g, b: byte;
begin
with Canvas do
begin
{Заполняем установки карандаша и кисти.}
Pen.Style := psClear;
Brush.Style := bsSolid;
Brush.Color := color;
{Подготовим цвета для сфер.}
r := GetRValue(color);
g := GetGValue(color);
b := GetBValue(color);
{Рисуем сферу.}
dw := size div 16;
for i := 0 to 15 do
begin
xy1 := (i * dw) div 2;
xy2 := size - xy1;
Brush.Color := RGB(Min(r + (i * 8), 255), Min(g + (i * 8), 255),
Min(b + (i * 8), 255));
Ellipse(x + xy1, y + xy1, x + xy2, y + xy2);
end;
end;
end; {TScrnFrm.DrawSphere}
procedure TScrnFrm.DeactivateScrnSaver(var Msg: TMsg; var Handled: boolean);
var
done: boolean;
begin
if Msg.message = WM_MOUSEMOVE then
done := (Abs(LOWORD(Msg.lParam) - crs.x) > 5) or
(Abs(HIWORD(Msg.lParam) - crs.y) > 5)
else
done := (Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) or
(Msg.message = WM_SYSKEYDOWN) or (Msg.message = WM_SYSKEYUP) or
(Msg.message = WM_ACTIVATE) or (Msg.message = WM_NCACTIVATE) or
(Msg.message = WM_ACTIVATEAPP) or (Msg.message = WM_LBUTTONDOWN) or
(Msg.message = WM_RBUTTONDOWN) or (Msg.message = WM_MBUTTONDOWN);
if done then
Close;
end; {TScrnFrm.DeactivateScrnSaver}
procedure TScrnFrm.tmrTickTimer(Sender: TObject);
const
sphcount: integer = 0;
var
x, y: integer;
size: integer;
r, g, b: byte;
color: TColor;
begin
if sphcount > CfgFrm.spnSpheres.Value then
begin
Refresh;
sphcount := 0;
end;
Inc(sphcount);
x := Random(ClientWidth);
y := Random(ClientHeight);
size := CfgFrm.spnSize.Value + Random(50) - 25;
x := x - size div 2;
y := y - size div 2;
r := Random($80);
g := Random($80);
b := Random($80);
DrawSphere(x, y, size, RGB(r, g, b));
end; {TScrnFrm.tmrTickTimer}
procedure TScrnFrm.FormShow(Sender: TObject);
begin
GetCursorPos(crs);
tmrTick.Interval := 1000 - CfgFrm.spnSpeed.Value * 90;
tmrTick.Enabled := true;
Application.OnMessage := DeactivateScrnSaver;
ShowCursor(false);
end; {TScrnFrm.FormShow}
procedure TScrnFrm.FormHide(Sender: TObject);
begin
Application.OnMessage := nil;
tmrTick.Enabled := false;
ShowCursor(true);
end; {TScrnFrm.FormHide}
procedure TScrnFrm.FormActivate(Sender: TObject);
begin
WindowState := wsMaximized;
end; {TScrnFrm.FormActivate}
end.
// Spheres.dpr
program Spheres;
uses
Forms, SysUtils,
Scrn in 'SCRN.PAS' {ScrnFrm},
Cfg in 'CFG.PAS' {CfgFrm};
{$R *.RES}
{$D SCRNSAVE Spheres Screen Saver}
begin
{Возможен запуск только одной копии.}
if hPrevInst = 0 then
begin
if (ParamCount > 0) and (UpperCase(ParamStr(1)) = '/S') then
begin
{ScrnFrm должна быть главной формой.}
Application.CreateForm(TScrnFrm, ScrnFrm);
Application.CreateForm(TCfgFrm, CfgFrm);
end
else
begin
{CfgFrm должна быть главной формой.}
Application.CreateForm(TCfgFrm, CfgFrm);
Application.CreateForm(TScrnFrm, ScrnFrm);
end;
Application.Run;
end;
end.
// Spheres.opt
[Compiler]
A = 1
B = 0
D = 1
F = 0
I = 1
K = 1
L = 1
P = 1
Q = 0
R = 0
S = 1
T = 0
U = 1
V = 1
W = 0
X = 1
Y = 1
[Linker]
MapFile = 0
LinkBuffer = 0
DebugInfo = 0
OptimizeExe = 1
StackSize = 16384
HeapSize = 8192
[Directories]
OutputDir =
SearchPath =
Conditionals =
[Parameters]
RunParams = / s
|
Снимок экрана.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
Graphics;
// Capture the entire screen
// Screenshot des gesamten Bildschirms
procedure ScreenShot(Bild: TBitMap);
var
c: TCanvas;
r: TRect;
begin
c := TCanvas.Create;
c.Handle := GetWindowDC(GetDesktopWindow);
try
r := Rect(0, 0, Screen.Width, Screen.Height);
Bild.Width := Screen.Width;
Bild.Height := Screen.Height;
Bild.Canvas.CopyRect(r, c, r);
finally
ReleaseDC(0, c.Handle);
c.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Form1.Visible := False;
Sleep(750); // some delay, ein wenig Zeit geben
ScreenShot(Image1.Picture.BitMap);
Form1.Visible := True;
end;
// Only active window
// Screenshot des aktiven Fensters
procedure ScreenShotActiveWindow(Bild: TBitMap);
var
c: TCanvas;
r, t: TRect;
h: THandle;
begin
c := TCanvas.Create;
c.Handle := GetWindowDC(GetDesktopWindow);
h := GetForeGroundWindow;
if h <> 0 then
GetWindowRect(h, t);
try
r := Rect(0, 0, t.Right - t.Left, t.Bottom - t.Top);
Bild.Width := t.Right - t.Left;
Bild.Height := t.Bottom - t.Top;
Bild.Canvas.CopyRect(r, c, t);
finally
ReleaseDC(0, c.Handle);
c.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Form1.Visible := False;
Sleep(750); //some delay,ein wenig Zeit geben
ScreenShotActiveWindow(Image1.Picture.BitMap);
Form1.Visible := True;
end;
{**********************************************}
// Another print screen function by Xavier P:
// Capture the entire screen
procedure ScreenShot(x: Integer;
y: Integer; //(x, y) = Left-top coordinate
Width: Integer;
Height: Integer; //(Width-Height) = Bottom-Right coordinate
bm: TBitMap); //Destination
var
dc: HDC;
lpPal: PLOGPALETTE;
begin
{test width and height}
if ((Width = 0) or
(Height = 0)) then
Exit;
bm.Width := Width;
bm.Height := Height;
{get the screen dc}
dc := GetDc(0);
if (dc = 0) then
Exit;
{do we have a palette device?}
if (GetDeviceCaps(dc, RASTERCAPS) and
RC_PALETTE = RC_PALETTE) then
begin
{allocate memory for a logical palette}
GetMem(lpPal,
SizeOf(TLOGPALETTE) +
(255 * SizeOf(TPALETTEENTRY)));
{zero it out to be neat}
FillChar(lpPal^,
SizeOf(TLOGPALETTE) +
(255 * SizeOf(TPALETTEENTRY)),
#0);
{fill in the palette version}
lpPal^.palVersion := $300;
{grab the system palette entries}
lpPal^.palNumEntries :=
GetSystemPaletteEntries(dc,
0,
256,
lpPal^.palPalEntry);
if (lpPal^.PalNumEntries <> 0) then
{create the palette}
bm.Palette := CreatePalette(lpPal^);
FreeMem(lpPal, SizeOf(TLOGPALETTE) +
(255 * SizeOf(TPALETTEENTRY)));
end;
{copy from the screen to the bitmap}
BitBlt(bm.Canvas.Handle,
0,
0,
Width,
Height,
Dc,
x,
y,
SRCCOPY);
{release the screen dc}
ReleaseDc(0, dc);
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
ScreenShot(0,0,Screen.Width, Screen.Height, Image1.Picture.Bitmap);
end;
{**********************************************}
// Capture a window
procedure ScreenShot(hWindow: HWND; bm: TBitmap);
var
Left, Top, Width, Height: Word;
R: TRect;
dc: HDC;
lpPal: PLOGPALETTE;
begin
{Check if valid window handle}
if not IsWindow(hWindow) then Exit;
{Retrieves the rectangular coordinates of the specified window}
GetWindowRect(hWindow, R);
Left := R.Left;
Top := R.Top;
Width := R.Right - R.Left;
Height := R.Bottom - R.Top;
bm.Width := Width;
bm.Height := Height;
{get the screen dc}
dc := GetDc(0);
if (dc = 0) then
begin
Exit;
end;
{do we have a palette device?}
if (GetDeviceCaps(dc, RASTERCAPS) and
RC_PALETTE = RC_PALETTE) then
begin
{allocate memory for a logical palette}
GetMem(lpPal,
SizeOf(TLOGPALETTE) +
(255 * SizeOf(TPALETTEENTRY)));
{zero it out to be neat}
FillChar(lpPal^,
SizeOf(TLOGPALETTE) +
(255 * SizeOf(TPALETTEENTRY)),
#0);
{fill in the palette version}
lpPal^.palVersion := $300;
{grab the system palette entries}
lpPal^.palNumEntries :=
GetSystemPaletteEntries(dc,
0,
256,
lpPal^.palPalEntry);
if (lpPal^.PalNumEntries <> 0) then
begin
{create the palette}
bm.Palette := CreatePalette(lpPal^);
end;
FreeMem(lpPal, SizeOf(TLOGPALETTE) +
(255 * SizeOf(TPALETTEENTRY)));
end;
{copy from the screen to the bitmap}
BitBlt(bm.Canvas.Handle,
0,
0,
Width,
Height,
Dc,
Left,
Top,
SRCCOPY);
{release the screen dc}
ReleaseDc(0, dc);
end;
// Example: Capture the foreground window:
procedure TForm1.Button1Click(Sender: TObject);
begin
ScreenShot(GetForeGroundWindow, Image1.Picture.Bitmap);
end;
{**********************************************}
// by Daniel Wischnewski
Sometimes you want to take a screen shot,
however often Windows has trouble with big data amounts and becomes very slow.
The simple solution is to make many small screen shots and paste the result together.
It''s not light speed, however often faster than taking the whole screen at once.
const
cTileSize = 50;
function TForm1.GetSCREENSHOT: TBitmap;
var
Locked: Boolean;
X, Y, XS, YS: Integer;
Canvas: TCanvas;
R: TRect;
begin
Result := TBitmap.Create;
Result.Width := Screen.Width;
Result.Height := Screen.Height;
Canvas := TCanvas.Create;
Canvas.Handle := GetDC(0);
Locked := Canvas.TryLock;
try
XS := Pred(Screen.Width div cTileSize);
if Screen.Width mod cTileSize > 0 then
Inc(XS);
YS := Pred(Screen.Height div cTileSize);
if Screen.Height mod cTileSize > 0 then
Inc(YS);
for X := 0 to XS do
for Y := 0 to YS do
begin
R := Rect(
X * cTileSize, Y * cTileSize, Succ(X) * cTileSize,
Succ(Y) * cTileSize);
Result.Canvas.CopyRect(R, Canvas, R);
end;
finally
if Locked then
Canvas.Unlock;
ReleaseDC(0, Canvas.Handle);
Canvas.Free;
end;
end;
Как скопировать экран (или его часть) в TBitmap.
Например, с помощью WinAPI так -
var
bmp: TBitmap;
DC: HDC;
begin
bmp:=TBitmap.Create;
bmp.Height:=Screen.Height;
bmp.Width:=Screen.Width;
DC:=GetDC(0); //Дескриптор экрана
bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height,
DC, 0, 0, SRCCOPY);
bmp.SaveToFile('Screen.bmp');
ReleaseDC(0, DC);
end;
|
Объект Screen[.width,height] - размеры.
Var Desktop: TCanvas ; BitMap: TBitMap; begin DesktopCanvas:=TCanvas.Create; DesktopCanvas.Handle:=GetDC(Hwnd_Desktop); BitMap := TBitMap.Create; BitMap.Width := Screen.Width; BitMap.Height:=Screen.Height; Bitmap.Canvas.CopyRect(Bitmap.Canvas.ClipRect, DesktopCanvas, DesktopCanvas.ClipRect); ........ end; |
Как сохранить содержимое экрана в файл.
| 10 стадий утреннего похмелья для программиста (заметки монитора). 1. Созерцательная. Смотрит в монитор чистыми прозрачными глазами, ничего не предпринимает, никуда не нажимает, изредка в изумлении произносит, глядя в экран: "Вот это да, кто бы мог подумать!" Компьютер выключен из розетки. 2. Деятельная. С подозрением и прищуром смотрит на клавиатуру, раскачиваясь всем телом, потом резким движением выбрасывает руку, пытаясь нажать на искомую кнопку. В случае удачного попадания кричит "Е-ху!!!", в случае неудачного попадания сносит монитор к едрене фене, при этом добродушно ворчит "ну вот третий раз за сегодня" 3. Угрюмая. Понуро сидит перед компьтером, пытаясь не совершать резких движений. Набирает текст на клавиатуре языком головы и двумя руками держится за мышку, чтобы не опрокинуться с кресла... Как оказалось, напрасно. 4. Автомобильная. Пытается завести компьютер автомобильными ключами, присобачить магнитолу на панель компьютера, и включить дворники, потому что "ни хр@на не видно, а ехать надо". 5. Развлекательная. Играет в Quake, причем из всех видов оружия предпочитает собственные руки и дерется с монитором, угрожает компьютеру вернуться завтра с друзьями-каратистами, и отп*здить его так, что "мама" не распознает. 6. Террористическая. После очередной попытки разлепить глаза, в ужасе кричит, что компьютер заминирован, потому что в правом нижнем углу тикают часы, и ныряет под стол. 7. Государственная. На предложение "Введите свой пароль" орет в компьютер: "Ах сука, ты меня, что ли не узнаешь?", показывает монитору язык, неприличные жесты и удостоверение помощника депутата Государственной Думы. 8. Оптимистическая. Весел, игрив, обращается с компьютером на равных. Прочитав страничку BK.list.ru, также вспоминает пару забавных историй и рассказывает их монитору. Потом они вместе пьют пиво, компьютер отхлебывает дисководом. 9. Униксовая. ...выключает компьютер, две минуты пьет пиво, снова включает, дожидается заставки и в тоске произносит "Опять винды, ненавижу Гейтса", выключает компьютер, две минуты пьет пиво, снова включает... 10. Успокоительная. Долго по слогам читает с экрана "Теперь питание компьютера можно отключить", после чего, мягко улыбаясь, говорит "Спасибо" и засыпает на клавиатуре. |
procedure TForm1.Button1Click(Sender: TObject);
var
DC: HDC;
Canva: TCanvas;
B: TBitmap;
begin
Canva := TCanvas.Create;
B := TBitmap.Create;
DC := GetDC(0);
try
Canva.Handle := DC;
with Screen do
begin
B.Width := Width;
B.Height := Height;
B.Canvas.CopyRect(Rect(0, 0, Width, Height),
Canva, Rect(0, 0, Width, Height));
B.SaveToFile('c:\Мои документы\sсrееntоfilе.bmp');
end
finally
ReleaseDC(0, DC);
B.Free;
Canva.Free
end
end;
|
Как копировать образ экрана в файл.
Автор: Vit WEB-сайт: http://forum.vingrad.ruНа форме у меня стоит TImage (его можно сделать невидимым).
var
i: TJPEGImage;
begin
try
i := TJPEGImage.create;
try
i.CompressionQuality := 100;
image.Width := screen.width;
image.height := screen.height;
DWH := GetDesktopWindow;
GetWindowRect(DWH, DRect);
DescDC := GetDeviceContext(DWH);
Canv.Handle := DescDC;
DRect.Left := 0;
DRect.Top := 0;
DRect.Right := screen.Width;
DRect.Bottom := screen.Height;
Image.Canvas.CopyRect(DRect, Canv, DRect);
i.assign(Image.Picture.Bitmap);
I.SaveToFile('M:\MyFile.jpg');
finally
i.free;
end;
except
end;
Типы использованных переменных:
Dwh : HWND; DRect: TRect; DescDC: HDC; Canv: TCanvas;
Экранный вирус.
Никогда не видели экранного вируса? Представьте, что Ваш экран заболел и покрылся красными пятнами :) А если эта болезнь нападёт на какое-нибудь окно ? Всё, что нам надо, это получить контекст устройства при помощи API функции GetWindowDC и рисовать, что душе угодно.
К исходному коду особых комментариев не требуется, скажу лишь только то, что основная часть кода находится в обработчике события OnTimer:
type
TScreenVirus = class(TComponent)
private
FTimer: TTimer;
FInterval: Cardinal;
FColor: TColor;
FRadius: Integer;
protected
procedure OnTimer(Sender: TObject);
procedure SetInterval(Value: Cardinal);
public
constructor Create(AOwner: TComponent); override;
procedure StartInfection;
published
property Interval: Cardinal
read FInterval write SetInterval;
property Color: TColor
read FColor write FColor default clRed;
property Radius: Integer
read FRadius write FRadius default 10;
end;
constructor TScreenVirus.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTimer := TTimer.Create(Owner);
FInterval := FTimer.Interval;
FTimer.Enabled := False;
FTimer.OnTimer := OnTimer;
FColor := clRed;
FRadius := 10;
end;
procedure TScreenVirus.StartInfection;
begin
if Assigned(FTimer) then
FTimer.Enabled := True;
end;
procedure TScreenVirus.SetInterval(Value: Cardinal);
begin
if Value <> FInterval then
begin
FInterval := Value;
FTimer.Interval := Interval;
end;
end;
procedure TScreenVirus.OnTimer(Sender: TObject);
var
hdcDesk: THandle;
Brush: TBrush;
X, Y: Integer;
begin
hdcDesk := GetWindowDC(GetDesktopWindow);
Brush := TBrush.Create;
Brush.Color := FColor;
SelectObject(hdcDesk, Brush.Handle);
X := Random(Screen.Width);
Y := Random(Screen.Height);
Ellipse(hdcDesk, X - FRadius, Y - FRadius,
X + FRadius, Y + FRadius);
ReleaseDC(hdcDesk, GetDesktopWindow);
Brush.Free;
end;
Прокрутка Memo.
Var
ScrollMessage:TWMVScroll;
begin
ScrollMessage.Msg:=WM_VScroll;
for i := Memo1.Lines.Count DownTo 0 do
begin
ScrollMessage.ScrollCode:=sb_LineUp;
ScrollMessage.Pos:=0;
Memo1.Dispatch(ScrollMessage);
end;
end;
|
Как прокрутить TRichEdit в конец.
Существует множество способов, включая и:
with MainFrm.RichEdit1 do begin perform (WM_VSCROLL, SB_BOTTOM, 0); perform (WM_VSCROLL, SB_PAGEUP, 0); end; |
Вышеприведённый пример работает отлично в 9x и NT4, но не работает в Windows 2000. Поэетому предлагаю воспользоваться следующим примером:
with MainFrm.RichEdit1 do begin SelStart := Length(Text); Perform(EM_SCROLLCARET, 0, 0); end; |
Или.
SendMessage(RichEdit1.Handle, EM_SCROLL, SB_LINEDOWN, 0); |
Прокрутка таблицы - хитрость PeekMessage.
На днях я решил поиграть с API-функцией PeekMessage(). Функция работает, но ловить ее нужно следующим образом. Я прокручиваю таблицу, связанную с набором данных. "Поиск" в наборе данных замедляет процесс скролирования (условимся называть "поиском" синхронное перемещение табличного курсора в процессе скроллирования, при котором текущей записью становится запись, ближайшая к нажимаемой кнопке полосы прокрутки). Возникла задача: необходимо отменить "поиск" (процесс слежения) и переместить указатель на необходимую запись только в случае остановки пользователем процесса скроллирования, другими словами - пока пользователь осуществляет скроллирование, "поиск" необходимо отменить. Итак, ко мне в голову пришла мысль, что с помощью PeekMessage() можно выловить определенное сообщение и тем самым отменить поиск во время прокрутки. Звучит просто, но на самом деле все оказалось наоборот. Я установил фильтр поиска сообщений на WM_MOUSEFIRST/LAST. Ситуация: пользователь непрерывно прокручивает DBGrid вниз, т.е. держит нажатой нижнюю кнопку скроллирования. В результате PeekMessage() возвращает False - нас это не устраивает, это не то, что мы хотим. Положительный результат можно получить только в случае сверхскоростных манипуляций мышью. Если в фильтре использовать 0 и 0, чтобы поймать любое сообщение, результат всегда будет True. Причина, очевидно в том, что любой щелчок мыши в области DBGrid никак не обойдется без последствий, генерация системой сообщения PAINT яркий тому пример, поэтому PeekMessage может возвратить True в любое время, что тоже не может нам помочь. Было бы хорошо, если бы дескриптор DBGrid получал событие OnMouseUp() во время его скроллирования. Обидно, но OnMouseUp() работает только с DBGrid, а не с полосами прокрутки. OnMouseUp() с TForm при KeyPreview:=true не работает, я проверял. После пришла идея опросить состояние кнопок мыши с помощью функции GetKeyState(). Пока кнопка нажата (DOWN), "поиск" запрещен, и наоборот. UP (кнопка отжата) свидетельствует об окончании процесса скроллирования. Данный способ работы с окном во время манипуляций с его полосой прокрутки заработал без проблем. Теперь все в порядке: поиска во время прокрутки не происходит и табличный курсор также никуда не перемещается.Рассмотренная тема имеет отношение к полосам прокрутки, а события OnKeyUp() и OnMouseUp() могут применяться где-нибудь еще.
Как в Memo прокрутить текст.
| Все, конечно же, в курсе, что Аська (ICQ-98, например) при получении сообщения говорит второй любимый американский звук "О-а!" (первый лю- бимый - "Упс!"). Так вот, как-то одна моя знакомая девушка, увидев что- то то ли в окне, то ли по телевизору, произнесла это самое "О-а!". На что я воскликнул: "Ну ты прям как Аська". После чего минут десять объяснял, что я не имел ввиду никакие "какаськи". :)) |
В поле ввода вводите на какую строку нужно сойти, и по нажатию на кнопку эта строка будет попадать о зону видимости:
procedure TForm1.Button1Click(Sender: TObject); var line: integer; begin line := StrToIntDef(Edit1.Text,1); Memo1.SelStart := Memo1.Perform(EM_LINEINDEX, line, 0); Memo1.Perform(EM_SCROLLCARET, 0, 0); end; |
Прокручивать TWebBrowser своими кнопками.
// Scroll up procedure TForm1.Button1Click(Sender: TObject); begin WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(0, -100); end; // Scroll left procedure TForm1.Button3Click(Sender: TObject); begin WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(-100, 0); end; // Scroll down procedure TForm1.Button2Click(Sender: TObject); begin WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(0, +100); end; // Scroll right procedure TForm1.Button4Click(Sender: TObject); begin WebBrowser1.OleObject.Document.ParentWindow.ScrollBy(+100, 0); end; |
Читаем CSV текстовый файл в StringGrid.
procedure ReadTabFile(FN: TFileName; FieldSeparator:
Char; SG: TStringGrid);
var
i: Integer;
S: string;
T: string;
Colonne, ligne: Integer;
Les_Strings: TStringList;
CountCols: Integer;
CountLines: Integer;
TabPos: Integer;
StartPos: Integer;
InitialCol: Integer;
begin
Les_Strings := TStringList.Create;
try
// Load the file, Datei laden
Les_Strings.LoadFromFile(FN);
// Get the number of rows, Anzahl der Zeilen ermitteln
CountLines := Les_Strings.Count + SG.FixedRows;
// Get the number of columns, Anzahl der Spalten ermitteln
T := Les_Strings[0];
for i := 0 to Length(T) - 1 do Inc(CountCols,
Ord(IsDelimiter(FieldSeparator, T, i)));
Inc(CountCols, 1 + SG.FixedCols);
// Adjust Grid dimensions, Anpassung der Grid-Gro?e
if CountLines > SG.RowCount then SG.RowCount := CountLines;
if CountCols > SG.ColCount then SG.ColCount := CountCols;
// Initialisierung
InitialCol := SG.FixedCols - 1;
Ligne := SG.FixedRows - 1;
// Iterate through all rows of the table
// Schleife durch allen Zeilen der Tabelle
for i := 0 to Les_Strings.Count - 1 do
begin
Colonne := InitialCol;
Inc(Ligne);
StartPos := 1;
S := Les_Strings[i];
TabPos := Pos(FieldSeparator, S);
repeat
Inc(Colonne);
SG.Cells[Colonne, Ligne] := Copy(S, StartPos, TabPos - 1);
S := Copy(S, TabPos + 1, 999);
TabPos := Pos(FieldSeparator, S);
until TabPos = 0;
end;
finally
Les_Strings.Free;
end;
end;
// Example, Beispiel:
procedure TForm1.Button1Click(Sender: TObject);
begin
Screen.Cursor := crHourGlass;
// Open tab-delimited files
ReadTabFile('C:\TEST.TXT', #9, StringGrid1);
Screen.Cursor := crDefault;
end;
|
Алгоритмы поиска.
Оформил: DeeCo
Автор: Елена Филиппова.
Введение.
Данная статья не претендует на полное описание всех существующих методов поиска и посвящена именно обзору классических решений, довольно простой на вид, поисковой задачи. Мне хотелось рассказать об алгоритмах поиска, о том что это такое и каким образом их следует применять в конкретных задачах. Я постаралась минимизировать количество математических выкладок и сухих определений, чтобы облегчить восприятие именно логики алгоритмов, не требуя при этом специальной математической подготовки. Говоря о поиске, мы будем иметь в виду некий массив данных, а искать будем определенный элемент в этом массиве. Оптимальность поиска для простоты определим очень конкретно - это скорость работы алгоритма. Казалось бы, к чему плодить много алгоритмов, давайте найдем один, самый оптимальный и успокоимся на этом. Это ошибочное мнение. Найти оптимальный алгоритм, не привязываясь при его выборе к условию задачи - это иллюзия. Какой алгоритм , из множества известных сейчас, самый быстрый? Ответа на этот вопрос не существует. Нет самого оптимального алгоритма в абстрактном смысле. Выбор его очень сильно зависит от условия задачи, которую нам придется решать. Именно на этот факт мне и хотелось бы обратить особенное внимание. Маленькое лирическое отступление... Судя по литературе, подъем интереса к математическим исследованиям методов поиска начался в 60-х годах. Очевидно, что на заре развития вычислительной техники, небольшой ( по нашим меркам просто никакой ) объем оперативной памяти и наличие только внешних устройств последовательного доступа (магнитная лента), делало задачу поиска или слишком тривиальной или абсолютно нерешаемой.Общая классификация алгоритмов поиска.
Все методы можно разделить на статические и динамические. При статическом поиске массив значений не меняется во время работы алгоритма. Во время динамического поиска массив может перестраиваться или изменять размерность. Представьте себе, что Вы ищете слово в словаре. Как бы Вы его не искали, Ваш агоритм обязательно будет статическим, так как сам словарь ( массив слов ) не будет изменяться во время поиска. Если конечно не выдирать из него страницы. Примером динамического поиска может служить попытка найти определенную карту в колоде. Откладывая в сторону ненужные карты, Вы облегчаете себе задачу поиска, уменьшая количества оставшихся карт в колоде, которые еще нужно перебрать, тем самым перестраивая массив значений во время поиска! Так же методы поиска можно разделить на методы, использующие истинные ключи и на методы, работающие по преобразованным ключам. В данном случае "ключем" называют то значение, которое мы ищем. Поиск в колоде карт - поиск по истинным ключам, то есть имеете дело с тем, что есть. Поиск в словаре - поиск по преобразованным ключам, так как все слова отсортированы в алфавитном порядке, то есть массив значений изменен перед началом поиска. Этот порядок создан специально для облегчения поиска и вовсе не является естественным для списка слов! Кстати, привычный нам алфавитный порядок слов, не всегда казался людям естественным и является изобретением не очень древним. Ну и еще вариант классификации - методы основанные на сравнении самих значений и методы, основанные на их цифровых свойствах. Так называемые методы хэширования. Рассматриваемые в данном обзоре алгоритмы являются комбинациями первых двух способов классификации. О методе хэширования я лишь упомяну исключительно из академического интереса, так как он не входит в круг рассматриваемых здесь задач. Представьте себе, что нам надо найти определенную карту в колоде. Для этого мы выбрали довольно нестандартный способ, мы придумали такую функцию, которая вычисляет номер нужной карты в колоде. Напримерf("крестовая дама") = 5 , а f("туз пик")=12
То есть, подставив в функцию искомое значение, мы получаем местоположение этого значения в нашем массиве. Вот здорово! Это же самый что ни на есть ПРЯМОЙ доступ , по сравнению с ПОСЛЕДОВАТЕЛЬНЫМ перебором всех ( или некоторых ) значений в искомом массиве.
Этот метод и называется методом хэширования. Все бы хорошо, но вот беда, функция f(К) должна определять ОДНОЗНАЧНОЕ соответствие для каждого значения в массиве, а находить подобные функции довольно сложно.
Подробное рассмотрение алгоритмов.
Попытаемся решить простейшую задачу - найти нужное значение в массиве. На этом первом примере мне хочется показать, как оптимальность решения зависит от того, в каком массиве мы будем искать. Перед Вами колода игральных карт, в ней , как правило, 54 карты ( ну или 36 ). Карты лежат в беспорядке. Необходимо найти определенную карту, например пикового короля. Что Вы предпримете? Думаю, что догадаться нетрудно, первое, что приходит в голову, это последовательно перебирать все карты, до тех пор пока нужная не найдется. При самом плохом стечении обстоятельств Вам придется перебрать все карты в колоде , что на самом деле не так долго, если честно. Попробуем немного изменить условие. Искать нам надо не карту, а конверт с определенным адресом. Конвертов этих перед Вами лежит немерянное количество, если окинуть взглядом заваленные столы, ну несколько сотен как минимум. Нет, нет, не пожимайте удивленно плечами, задача не надуманная, а самая что ни на есть реальная. Если вдруг кто-нибудь ( а мир так удивительно тесен! ) знаком с Константиновым Николаем Николаевичем, тот скорее всего знаком с этой задачей не понаслышке. Итак, я отвлеклась, перед нами несколько сотен конвертов с именами, нужно найти вполне определенный конверт. Способ, только что опробованный на колоде карт, очень скоро утомит. Вряд ли кто в этом сомневается, не правда ли? :о) А вот если предварительно все эти конверты разложить в алфавитном порядке, то дальнейшая работа по нахождению вполне определенного имени уже не представляется такой ужасной! Конечно, на сортировку необходимо затратить вполне определенные силы и время, но они окупятся при многоразовом поиске, вот что самое главное Вспомнив пример с картами, отметим, что в том случае тратить время на предварительную сортировку колоды вряд ли стоит... Итак, мы можем сделать первый и очень важный вывод - оптимальность метода поиска зависит от размера массива, в котором мы ищем! Прямой перебор всех значений прост в понимании, легок в исполнении и достаточно быстр на малых массивах данных. Насчет легкости и простоты , это не лирика, это весомый аргумент, ведь Вам после того, как будет выбран определенный метод, необходимо еще формализовать его и закодировать. В итоге программиста инетересует код. Не будем об этом забывать. Итак, все это в совокупности позволяет сказать, что метод прямого перебора оптимален для малых массивов. Если же массив данных достаточно велик, то оптимальность поиска достигается предварительной сортировкой значений в массиве. Ну что же, с задачей поиска при малом количестве значений мы разобрались, она достаточно тривиальна. Давайте займемся поиском в о-о-очень длинном массиве... Это гораздо интереснее. Итак, приняв все вышесказанное за истину, мы отсортировали наш массив. Все значения лежат в некотором порядке. Можно искать! И, собственно, что?... Как искать-то? Существует несколько классических способов поиска значения в отсортированном массиве. Мы рассмотрим три способа. Они были предложены в 60-х годах и мало изменились в наше время. Один из них очень известный, ведь Вам наверняка приходилось слышать такие названия как "метод дихотомии", "бинарного дерева" или "метод половинного деления", что собственно одно и тоже. Вот с него и начнем.1. Алгоритм поиска по бинарному дереву.
Суть этого алгоритма достаточно проста. Представим себе, что у нас есть набор карточек с телефонными номерами и адресами людей. Карточки отсортированы в порядке возрастания телефонных номеров. Необходимо найти адрес человека, если мы знаем, что его номер телефона 222-22-22. Наши действия? Берем карточку из середины пачки, номер карточки 444-44-44. Сравнивая его с искомым номером, мы видим, что наш меньше и , значит, находится точно в первой половине пачки. Смело откладываем вторую часть пачки в сторону, она нам не нужна, массив поиска мы сузили ровно в два раза. Теперь берем карточку из середины оставшейся пачки, на ней номер 123-45-67. Из чего следует, что нужная нам карточка лежит во второй половине оставшейся пачки... Таким образом , при каждом сравнении , мы уменьшаем зону поиска в два раза. Отсюда и название метода - половинного деления или дихотомии. Не буду приводить математического доказательства, так как это не является целью данной статьи, а просто отмечу, что скорость сходимости этого алгоритма пропорциональна Log(2)N ¹ . Это означает буквально то, что не более, чем через Log(2)N сравнений , мы либо найдем нужное значение, либо убедимся в его отсутствии. Другое название этого алгоритма – «метод бинарного дерева» происходит из представления "пути" поиска в виде дерева (у которого каждая следующая ветвь разделяется на две, по одной из которых мы и движемся в дальнейшем).. Способ очень распространенный в наше время, возможно по причине его эффективности вкупе с простотой программирования этого алгоритма. Именно бинарный поиск используется при поиске в индексах таблиц. Есть еще один алгоритм, основанный на делении искомого массива на части, аналогичный предыдущему. Я упомяну о нем скорее из-за некоторой его экзотичности.2. Поиск по "дереву Фибоначчи".
Рассмотрим его исключительно из академического интереса. Для тех, кто пожелает разобраться в методе более детально, список литературы в конце статьи. Трудноватый для восприятия метод, но эффективность его немного выше, чем у поиска по Бинарному дереву, хотя так же пропорциональна Log(2)N. В дереве Фибоначчи числа в дочерних узлах, отличаются от числа в родительском узле на одну и ту же величину, а именно на число Фибоначчи. Суть метода в том, что сравнивая наше искомое значение с очередным значением в массиве , мы не делим пополам новую зону поиска , как в бинарном поиске, а отступаем от предыдущего значения, с которым сравнивали, в нужную сторону на число Фибоначчи. Почему этот способ считается более эффективным, чем предыдущий? Ответ достаточно неожиданный и боюсь , что нам с Вами оценить его должным образом вряд ли удастся. Дело в том, что метод Фибоначчи включает в себя только такие арифметические операции, как сложение и вычитание, нет необходимости в делении на 2, тем самым экономится процессорное время! Хочется напомнить, что речь идет о том времени, когда был изобретен метод, а именно - о 60-х годах. В то время процессорное время можно было экономить на подобных подходах.3. Метод экстраполяций.
Переходя к следующему методу, давайте представим, что Вам срочно понадобилось узнать, как переводится на русский язык английское слово treasure. То есть перед нами задача - найти это слово в словаре. По сути все наши дальнейшие действия будут ничем иным, как реализацией некоторого алгоритма поиска. Массив значений - это словарь, все значения в нем отсортированы по алфавиту. Искомое слово нам известно. Значит сейчас мы будем искать в отсортированном массиве. Если честно, то я с трудом представляю себе, что Вы станете делить все страницы книги пополам, смотреть что там в середине и отлистывать в одну или другую сторону ровно ¼ страниц словаря и так далее... То есть метод дихотомии Вы проигнорируете. И уж совсем неожиданно предположить, что Вы воспользуетесь деревом Фибоначчи. Вы просто возьмете и ... и найдете нужное слово, не правда ли? А теперь остановимся и еще раз повторим поиск , при этом очень внимательно проанализируем наши действия. Итак, искомое слово начинается на букву T , открываем словарь немного дальше, чем на середине. Нам попалась буква R, ясно , что искать надо во второй части словаря, а на сколько отступить? На половину? "Ну зачем же на половину, это больше, чем надо", скажете Вы и будете правы. Ведь нам не просто известно, в какой части массива искомое значение, мы владеем еще и информацией о том, насколько далеко надо шагнуть! Вот мы и подошли к сути рассматриваемого метода. В отличии от первых двух , он не просто определяет зону нового поиска, но и оценивает величину нового шага. Алгоритм носит название экстраполяционного метода и имеет скорость сходимости большую, чем первые два метода. Примечание: Если при поиске по бинарному дереву за каждый шаг массив поиска уменьшался с N значений до N/2 , то при этом методе за каждый шаг зона поиска уменьшается с N значений до корня квадратного из N. Если К лежит между Kn и Km , то следующий шаг делаем от n на величину(n - m)*(K - Kn)/(Km - Kn)Скорость экстраполяционнго метода начинает существенно превышать скорость метода половинного деления при больших значениях N. Итак, мы рассмотрели конкретные алгоритмы поиска в отсортированном массиве. Таким образом можно подвести итоги: Если необходимо искать в небольшом массиве и искать нужно нечасто, проще воспользоваться методом перебора всех значений массива; Если массив значений большой и искать нужно часто, отсортируйте массив и воспользуйтесь методом половинного деления или интерполяционным методом. Каким из них именно, выбирайте исходя из того, насколько велик массив и какой метод Вам самим легче реализовать в коде. При предложенном выборе возникает резонный вопрос , а "большой массив" это какой? При оценке времени, которое затрачивает алгорит на поиск имеют значение две величины. Во-первых, это размерность массива (количество элементов) и, во-вторых, это количество обращений (то есть, сколько раз нам нужно в нем искать).
Итак, имеем массив размерностью N и проводим М раз в нем поиск.
Количество операций, которые выполнит алгоритм прямого перебора, пропорционально M*N. Метод дихотомии совершит 2М*Log(2)N операции , да еще и время на сортировкку надо прибавить - N*Log(2)N
Итак, имеем два выражения : T1 = M*N; T2 = 2М*Log(2)N + N*Log(2)NПри Т1 = Т2 мы находимся на границе, на которой одинаково оптимальны оба алгоритма. Из этого равенства можно получить области в системе координат "Количество обращений - Размерность массива", определяющие оптимальность одного алгоритма относительно другого (см. рисунок).
Способы коррекции алгоритмов поиска.
Основным критерием выбора оптимального метода был размер массива, в котором мы ищем. А более конкретные условия задачи могут существенно повысить скорость уже выбранного метода. До сих пор мы считали, что каждое значение массива с одинаковой вероятностью может оказаться искомым. Но это утверждение не для всех задач верно. Предположим, что у нас есть конверты с адресами учеников. Конвертов немного и сортировать мы их не стали. Ученики эти принимали участие в различных конкурсах и заняли там призовые места. Причем один человек мог отличиться не в одном , а во многих конкурсах. Перед нами задача: по спискам каждого конкурса найти отличившихся и вложить в его конверт соответствующее уведомление. Естественно, что уведомлений будет столько , сколько состязаний ученик выиграл. Через некоторое время, вкладывая очередное n-ое уведомление в конверт очередного юного гения и в глубине души догадываясь, что это не последний выигранный им конкурс, Вам вдруг захочется, чтобы его конверт лежал где-то в начале пачки.... И совершенно не важно, каким методом поиска Вы пользуетесь, Важно другое - исходя из условий конкретной задачи этот метод нуждается в коррекции. Вот тут нам поможет довольно простая схема динамического метода коррекции поиска или метод "Самоорганизующегося" списка. Идея метода проста - когда мы находим нужный конверт, мы кладем его в начало пачки. То есть перемещаем значение в начало массива, помните перестраиваемый массив при динамическом поиске? В итоге, часто используемые элементы будут расположены довольно близко к началу массива. Таким образом, количество сравнений до удачного поиска будет минимизироваться "само-собой". И через несколько иттераций в начале пачки автоматически окажутся конверты будущих ломоносовых. Математически это можно объяснить так: предположим , что значение Ki будет разыскиваться с вероятностью Pi, причемP1 + P2 + ... Pn = 1Время удачного поиска пропорционально С , среднее значение которого :
С = P1 + 2*P2 + .... N*Pn .Минимального значения С достигнет при :
P1 > P2 > ... > PnТо есть тогда, когда наиболее часто используемые записи находятся в НАЧАЛЕ таблицы. Данный способ, естественно, стоит применять в случае наибольшей вероятности именно удачного поиска. Не будем загромождать статью математическими выкладками, ибо тема несколько иная, но в результате оказывается, что оптимальное ( с точки зрения частоты обращений) расположение значений экономит около трети поискового времени! Возможно этот метод кому-то покажется экзотическим ( опять же, вероятности какие-то, это вам не Ctrl-C & Ctrl-V , ужас просто) , но, в зависимости от условия задачи, метод может быть очень эффективным. Менее экзотический, но тоже сильно зависящий от конкретных условий задачи, динамический метод "очищаемого" списка. Это не самостоятельный метод поиска, а именно коррекция одного из методов описанных выше. Когда при удачном поиске значения, запись ему соответствующая, удаляется из массива, уменьшая такми образом его размерность. И еще один момент. До сих пор мы считали, что в рассматриваемых задачах вероятность удачного поиска достаточно велика. Представим себе, что поиск поисходит в неком массиве, про который мы точно знаем, что искомые значения скорее всего там не содержатся! Чем же нам это помогает? А вот чем: мы можем минимизировать количество сравнений, заранее отсекая неудачный поиск, то есть значения ,которых нет в массиве. Предположим, что у нас есть большой массив отсортированных значений. Достаточно перед началом поиска очередного значения проверить, а попадает ли оно в принципе в массив? Это совершенно просто :
если (Ki < K1) или (Ki > Kn), то , собственно говоря, чего суетиться-то? Но применять этот способ коррекции алгоритма можно только в том случае, если вероятность неудачного поиска достаточно велика. Потому как при большой размерности массива еще и прибавлять сравнение на каждом шаге можно только в том случае, если очередное сравнение СКОРЕЕ ВСЕГО заменит нам очередной виток поиска. Оценим, какова должна быть вероятность неудачного поиска, то есть отсутствие искомого значения в массиве, чтобы было выгодно применять эту модификацию алгоритма. (Приведенная ниже оценка вероятности представлена Николаевым А.) Пусть Р - вероятность того, что искомое значение отсутствует между минимальным и максимальным значением массива. Тогда мы, отсекая сверхэкстремальные значения, в среднем не производим Р*(С - 2) операций, где С - среднее число операций при проведении немодифицированного поиска. В то же время мы вынуждены впустую совершить 2*(1 - Р) операций для сравнения значения с минимальным и максимальным в массиве. Так как мы должны быть в выигрыше, то
Р*(С - 2) > 2*(1 - Р) , следовательно PC -2P + 2P - 2 > 0
РС > 2 , ну и наконец : P > 2/CТаким образом, при P > 2/C мы будем в выигрыше. Например, для поиска по бинарному дереву в случае массива с 32 элементами достаточно один раз из четырех задавать сверхэкстремальное значение для поиска, чтобы модифицированный алгоритм работал быстрее. Вот, пожалуй, и все.
Историческая справка:
Бинарный поиск впервые упомянут Джоном Мочли в 1946г. Экстраполяционный поиск предложен У.Петерсоном 1957. Фибоначчиев поиск изобретен Д.Фергюсоном в 1960.Для тех, кто интересуется математической основой упомянутых методов:
Д.Кнут "Искусство программирования для ЭВМ", 1978г, издательство "МИР", том 3 "Сортировка и поиск". Популярные лекции по математике, выпуск №6. 1969г. Издательство "Наука" Н.Н. Воробьев. "Числа Фибоначчи" Р.Альсведе, И.Вегенер "Задачи поиска" , 1982г, Издательство "Мир"Поиск и фильтрация данных в Delphi.
Автор: Storm Введение Базы данных являются удобным средством хранения структурированной информации. Однако само по себе накопление и хранение информации делает базу данных большой кучей мусора. Удобно организованный поиск и отбор информации способен резко повысить эффективность ее использования. В данной статье мы попытаемся разобраться в методах поиска информации, реализованных в Delphi. Предполагается, что Вы в общих чертах знакомы с теорией реляционных баз данных и имеете некоторый опыт программирования на Delphi. Общие положения Для понимания реализации методов поиска Delphi необходимо сделать краткое отступление относительно идеологии построения баз данных. Итак, всю систему работы с БД можно представить в виде трех слоев или модулей (рис.1). Хранилище данных отвечает за хранение информации и обеспечение ее целостности и непротиворечивости. Бизнес логика реализует набор правил предметной области приложения, т.е правил изменения и дополнения информации. Презентационный слой выводит данные в форме доступной пользователю и обеспечивает интерфейс для взаимодействия с бизнес логикой. Очевидно, что нас будет интересовать взаимодействие двух верхних модулей с хранилищем данных. В зависимости от специфики реализации базы данных данные слои могут объединяться или реализовываться как отдельные независимые модули. Для нашей задачи системы управления базами данных можно классифицировать по следующим признакам: По типу хранилища данных: - файловые - серверные Файловые хранилища представляют собой набор файлов таблиц БД, сосредоточенных, как правило, в одной директории. Клиентское приложение имеет доступ к таблицам на уровне файловой системы. Серверные - хранилище данных представляет собой программу, извлекающую информацию из файла(ов) БД по запросам клиентских приложений. При этом не на ПК клиента не требуется иметь непосредственный доступ к файловой системе сервера. По количеству слоев: - Двухуровневые - Многослойные Двухуровневые СУБД совмещают в клиентском приложении бизнес логику и презентационные функции. Примером работы такой схемы является классическое приложение СУБД, где клиентское приложение осуществляет отображение данных пользователю и реализует функции проверки, контроля и отправки информации в хранилище данных. Многослойные (как правило 3-х) приложения кроме клиентской программы имеют специальную программу - сервер приложений. Сервер приложений реализует функции доступа к хранилищу данных и передает запрошенные данные клиентской программе, проверяя правильность запросов. Поиск данных Универсальные методы класса TDataSetКласс TDataSet является базовым классом компонента, реализующим функции доступа к БД. Многие из его методов являются абстрактными и реализуются в потомках. Он имеет два метода для поиска данных: Locate и Lookup. Данные методы ищут запись, удовлетворяющую заданным условиям.
function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): Boolean; virtual; function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant; virtual;Разница между ними в том, что функция Locate при поиске записи позиционирует курсор на найденную запись, а Lookup не делает этого. Если поля указанные для поиска индексированы, то поиск производится с использованием индекса, что значительно ускоряет поиск. В качестве примера рассмотрим использование данных методов на примере TTable в двухуровневом приложении. Итак, начнем с генерации нового приложения File/New Application. Мы получим проект приложения с главной формой. На данную форму поместим компонент Table с закладки Data Access, по умолчанию он будет иметь имя Table1. Теперь настроим компонент Table1 на взаимодействие с таблицей country из базы данных DBDEMOS. Для этого необходимо установить следующие свойства Table1 в следующей последовательности: 1. DatabaseName ==> DBDEMOS 2. TableName ==> country.db
Теперь можно активизировать компонент Table1, установив его свойство Active в значение true. Далее разместим на форме компонент DataSource, позволяющий сделать данные Table1 доступными для визуальных компонентов отображения данных. Свяжем DataSource1 c Table1, установив св-во DataSet компонента DataSource1 равным Table1. Разместим визуальные компоненты отображения данных со страницы Data Controls: DBGrid и DBNavigator. Установим св-во DataSource обоих равным DataSource1. В результате мы получим форму, изображенную на рис. 2. Откомпилировав и запустив данный проект, мы сможем просматривать и редактировать данные таблицы country. Остается добавить элементы для организации поиска в таблице. Разместим на форме компонент Edit1 и две кнопки. Св-во Caption кнопки Button1 установим равным "Locate", а Button2 "Lookup". Далее в обработчике события OnClick кнопки Button1 организуем вызов метода Locate. Код обработчика события приведен ниже:
Рис. 1.
Рис. 2.
TForm1.Button1Click(Sender: TObject);
begin
if not Table1.Locate('Name', Edit1.Text, []) then
ShowMessage('Запись не найдена');
end;
Разберем код более подробно. Строка Table1.Locate организует поиск записи в таблице Country. Первый параметр этой функции - поля, значения которых нужно проверять. В данном случае мы ищем запись по одному полю Name. Второй параметр, что шаблон поиска и третий опции поиска. Функция возвращает значение типа boolean, указывающее на успешность поиска.
Теперь пришло время протестировать наш пример. Запустим программу на выполнение, в строке ввода пишем Cuba и нажимаем кнопку Locate. Курсор в DBGrid1 должен переместиться на запись, имеющую в поле Name введенное значение (рис.3).
Однако наш пример имеет пока один недостаток, в строку редактирования необходимо вводить полное имя c учетом регистра, т.е если мы вместо Cuba введем, например Cu или cuba, то наш поиск будет безрезультатным. Естественно это не может нас не устраивать. Поэтому пришло время рассмотреть более подробно опции поиска. Данный параметр имеет тип TlocateOptions и позволяет задавать набор из двух параметров поиска: loCaseInsensitive и loPartialKey. Установка первого из них отменяет чувствительность к регистру в текстовых полях, а второй позволяет искать запись частично соответствующие заданному условию. С учетом вышесказанного код обработчика событий будет выглядеть следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Table1.Locate('Name', Edit1.Text, [loCaseInsensitive,
loPartialKey]) then
ShowMessage('Запись не найдена');
end;
Следующей проблемой является поиск записи по нескольким полям. Для организации поиска по имени страны и континенту добавим на форму еще один компонент Edit2. Код обработчика события нажатия на кнопку Locate изменим следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Table1.Locate(
'Continent;Name',
VarArrayOf([Edit2.Text, Edit1.Text]),
[loCaseInsensitive, loPartialKey]) then
ShowMessage('Запись не найдена');
end;
Как видно, при поиске по нескольким полям, все они перечисляются в параметре функции Locate
Запустив приложение, в строке поиска континента пишем South america, а в строке "страна" - C. Нажимаем кнопку Lookup - результат поиска - установка курсора в DBGrid на запись Chile.
В ходе написания этой статьи выяснилась одна особенность. Частичный поиск при поиске по нескольким полям работает лишь для последнего поля, указанного в списке.
Модифицируем наш пример для использования функции Lookup. Ниже приведен код обработчика события нажатия на кнопку Lookup:
procedure TForm1.Button2Click(Sender: TObject);
var
Res: Variant;
begin
Res := Table1.Lookup('Continent;Name', VarArrayOf([Edit2.Text,
Edit1.Text]), 'Area');
if Res <> Null then
ShowMessage('Area ' + string(Res));
end;
Как видно из кода, мы проводим поиск по полям Continent, Name. При нахождении записи мы выдаем сообщение о площади страны, при это курсор на найденную запись не перемещается. К сожалению в параметрах функции отсутствует LocateOptions.
Исходный код данного примера расположен в файле.
Итак, подведем некоторые итоги. Функции Locate и Lookup предназначены для поиска в базе данных одной записи, удовлетворяющей заданным условиям. Данные методы определены в классе TDataSet как виртуальные и могут быть переопределены в классах потомках. Метод Locate устанавливает курсор на обнаруженную запись, Lookup этого не делает. Метод Locate, хотя и с некоторыми оговорками, может искать записи по частично заданному ключу.
Необходимо отметить, что очень часто необходимо найти сразу несколько записей. Для решения такого рода задач нужно применять методы, предоставляемые классом Ttable, Tquery …, либо проводить фильтрацию. Как это сделать описано ниже.
Методы класса TTable
Компонент TTable предназначен для работы с таблицей база данных. Данный компонент используется в двухуровневых приложениях баз данных, либо в сервере приложений в трехуровневой БД. Как правило, использование TTable для работы с клиент-серверной БД оказывается менее эффективным, чем TQuery, поскольку TTable извлекает сразу ВСЕ записи из таблицы, а в TQuery лишь удовлетворяющие условиям запроса.
Компонент имеет несколько специфических методов для поиска данных. Данные методы используются для поиска только по индексированным полям (для dBase и Paradox как минимум). Все они делятся на две группы:
- Методы поиска одной записи
- Методы поиска диапазона записей.
Рассмотрим сначала первую группу методов. К ним относятся GotoKey, FindKey, GotoNearest, FindNearest. Первые два метода используются для поиска строго соответствия, другие ищут частичное соответствие.
В качестве примера создадим новое приложение, на главной форме разместим компоненты Table, DataSource, DBGrid, DBNavigator. Установим св-ва данных компонентов в соответствии с таблицей 1. Отметим, что таблица country.db имеет индексированное поле Name, по которому мы далее организуем поиск.
Таблица 1
| Table1 | |
| DatabaseName | BCDEMOS |
| TableName | Country.db |
| Active | true |
| DataSource1 | |
| DataSet | Table1 |
| DBNavigator1, DBGrid1 | |
| DataSource | DataSource1 |
Далее разместим на форме компонент Edit и четыре кнопки, установив их св-во Caption в GotoKey, GotoNearest, FindKey, FindNearest. Ниже приведен код обработчиков событий нажатия на эти кнопки.
//Использование GotoKey
procedure TForm1.Button1Click(Sender: TObject);
begin
with Table1 do
begin
EditKey;
FieldByName('Name').AsString := Edit1.Text;
if not GotoKey then
ShowMessage('Not found');
end;
end;
// Использование GotoNearest
procedure TForm1.Button2Click(Sender: TObject);
begin
with Table1 do
begin
EditKey;
FieldByName('Name').AsString := Edit1.Text;
GotoNearest;
end;
end;
// Использование FindKey
procedure TForm1.Button3Click(Sender: TObject);
begin
with Table1 do
begin
if not FindKey([Edit1.Text]) then
ShowMessage('Not found');
end;
end;
// Использование FindNearest
procedure TForm1.Button4Click(Sender: TObject);
begin
with Table1 do
begin
FindNearest([Edit1.Text]);
end;
end;
Прокомментируем вышеприведенный код. Методы GotoKey, FindKey проводят поиск на точное соответствие заданному ключу. Они возвращают значение типа boolean, говорящее об успехе поиска.
Методы GotoNearest, FindNearest проводят поиск первой записи хотя бы частично соответствующей ключу. Они не возвращают значений, т.к такой вид поиска всегда будет успешным.
Перед вызовом методов GotoKey, GotoNearest необходимо вызывать метод EditKey или SetKey, чтобы перевести компонент Table в режим редактирования ключа поиска.
Код данного примера содержится в файле.
Методы поиска диапазона записей позволяют отобразить записи таблицы, лежащие в указанном диапазоне значений поля. Для таблиц Paradox и dBase данные методы работают только для индексированных полей. К данным методам относятся SetRangeStart, SetRangeEnd, EditRangeStart, EditRangeEnd, ApplyRange, CancelRange. Техника использования данных функций очень проста.
Сначала необходимо установить начало и конец диапазона вызовом функций SetRangeStart, SetRangeEnd, EditRangeStart, EditRangeEnd, указывая при этом значения полей формирования диапазона. Затем вызовом ApplyRange применить указанный диапазон. Сброс установленного диапазона позволяет вновь отобразить все записи таблицы. Он выполняется вызовом функции CancelRange.
Для иллюстрации вышесказанного создадим новый проект, на главную форму поместим компоненты Table, DataSource, DBGrid, DBNavigator. Установим их свойства как указано в таблице 1. Далее разместим две строки ввода и две кнопки SetRange и CancelRange. Обработчики событий нажатия этих кнопок приведены ниже.
procedure TForm1.SetRangeClick(Sender: TObject);
begin
with Table1 do
begin
SetRangeStart; {Входим в режим установки начала диапазона}
FieldByName('Name').AsString := Edit1.Text; {значение начала диапазона}
SetRangeEnd; { Входим в режим установки конца диапазона }
FieldByName('Name').AsString := Edit2.Text; {значение конца диапазона}
ApplyRange; { Применяем указанный диапазон}
end;
end;
procedure TForm1.CancelRangeClick(Sender: TObject);
begin
Table1.CancelRange; {Очистка диапазона}
end;
Код данного примера содержится в файле.
Использование TQuery
Компонент TQuery предназначен для извлечения данных с помощью языка SQL (Structured query language). Компонент используется в двухуровневых приложениях работы с базами данных и в сервере приложений в трехуровневых. Он, как правило, применяется для работы с клиент-серверными базами данных.
Сам компонент TQuery не имеет специальных методов поиска записей, однако, возможности поиска заложены в языке SQL.Ограничения на простые данные, получаемые предложением select, реализуются с помощью предложения where.
В качестве иллюстрации приведем следующий пример. Создадим новое приложение, разместим на его главной форме компоненты: Query, DataSource, DBGrid, DBNavigator, Memo, две кнопки Button1 и Button2.
Установим свойства компонентов, как это указано в таблице 2.
Таблица 2
| Query1 | |
| DatabaseName | BCDEMOS |
| SQL | SELECT Name, Capital, Continent, Area, PopulationFROM "country.db" CountryWHERE (Area > 50000) AND (Population > 20000000) |
| Active | true |
| DataSource1 | |
| DataSet | Query1 |
| DBNavigator1, DBGrid1 | |
| DataSource | DataSource1 |
| Button1 | |
| Caption | Применить |
| Button2 | |
| Caption | Отменить |
Вид главной формы на этапе разработки приведен на рис. 4.
Рис. 3.
В обработчике события OnCreate формы реализуем копирование текста запроса из Query1, код приведен ниже.
procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Text := Query1.SQL.Text; end;
Кнопка Button1 при нажатии будет переносить текст запроса, исправленный пользователем, из Memo1 обратно в Query1. Обработчик нажатия Button1 будет выглядеть так:
procedure TForm1.Button1Click(Sender: TObject); begin Query1.Active := false; Query1.SQL.Text := Memo1.Text; Query1.Active := true; end;Обработчик события нажатия Button2 установим, указывающим на FormCreate. Откомпилируем и запустим нашу программу. Убедимся в том, что при изменении текста запроса (площади и населения) происходит изменение записей в DBGrid1.
Данный подход имеет ряд существенных недостатков, затрудняющих его практическое использование.
Пользователь приложения обязан разбираться в языке SQL; Отсутствуют средства автоматического контроля синтаксиса запроса; При изменении текста запроса, он отправляется на сервер, где каждый раз анализируется и компилируется, что снижает быстродействие поиска. Для генерации запроса пользователю необходимо знать имена таблиц и полей базы данных. По указанным выше причинам, данный подход применяется редко. Как правило, применение данного подхода оправдана при написании приложений для администраторов БД или разработчиков отчетов.Код данного примера содержится в файле.
Более приемлемым является применение параметризированных запросов. В этом случае в тексте запроса содержатся параметры для дальнейшей подстановки конкретных значений на этапе выполнения. Параметры в тексте запроса начинаются с двоеточия. Итак, создадим новое приложение, разместим на главной форме компоненты Query, DataSource, DBGrid, DBNavigator, кноку Button1. Установим их свойства аналогично предыдущему примеру. Далее текст св-ва SQL компонента установим равным:SELECT Name, Capital, Continent, Area, Population FROM "country.db" Country WHERE (Name = :Name_param)В последней строке мы определили параметр запроса Name_param. Теперь открыв в инспекторе объектов редактор св-ва Params компонента Query1, мы увидим элемент, соответствующий определенному параметру. Выбрав данный элемент отредактируем его свойства. DataType - тип значения параметра (по умолчанию ftUnknown) установим в ftString. ParamType - тип параметра (по умолчанию ptUnknown) установим в ptInput.
Обработчик события нажатия на кнопку будет выглядеть следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
begin
Query1.Close;
if not Query1.Prepared then
Query1.Prepare;
Query1.ParamByName('Name_param').Value := Edit1.Text;
Query1.Open;
end;
Для увеличения скорости поиска в строках 4 и 5 осуществляется проверка подготовки запроса. Если запрос не подготовлен (что случается при старте программы) вызывается метод Prepare. При этом сервер анализирует и компилирует запрос, сохраняя его на сервере. Далее серверу необходимо отсылать лишь значения параметров запроса.
Скомилируем и запустим на выполнение созданное приложение. В строке поиска введем значение Canada и нажмем кнопку "Применить". Результатом поиска будет вывод в строки в DBGrid1, как это показано на рис. 5.
Рис. 4.
Код данного примера содержится в файле.
Для поиска записей частично соответствующих введенному критерию изменим текст запроса компонента Query1 следующим образом:
SELECT Name, Capital, Continent, Area, Population FROM "country.db" Country WHERE (Name Like :Name_param)Как видно мы заменили оператор строго равенства (=) на LIKE. Оператор Like позволяет искать текстовые значения нестрого соответствующие заданному критерию. При этом в критерии поиска можно использовать знаки подстановки. % - любые символы, _ - один символ. Например, критерий поиска С% в нашем случае даст нам вывод четырех записей (Canada, Chile, Colombia, Cuba). В одном запросе можно комбинировать несколько условий поиска с помощью операторов OR, AND итд. Конечно, мы осветили лишь небольшую часть возможностей SQL по поиску данных. Если Вы хотите получить более подробную информацию, рекомендую приобрести какую-либо книгу по SQL. Фильтрация В отличие от методов поиска, предполагающих извлечение данных из хранилища данных фильтрация предполагает отбор уже извлеченных данных в клиентском приложении. Для реализации данного подхода в Delphi в компонентах доступа к данным введены два свойства Filter и Filtered. Установка свойства Filtered типа boolean в true переводит компонент в режим фильтрации. В свойстве Filter при этом можно определить значение фильтра для отбора записей. Построение фильтра во многом похоже на построение условия where в SQL запросе. Основное отличие в том, что слово where не пишется, используются другие знаки подстановки, в тексте фильтра нельзя после знаков сравнения вставлять имена полей для локальных таблиц. Если имя поля содержит пробелы, то оно заключается в квадратные скобки, например [Home directory] Свойство FilterOptions позволяет установить дополнительные параметры фильтрации, а именно foCaseInsensitive - нечувствительность к регистру в текстовых полях; foNoPartialCompare - отсутствие поиска по частичному условию, при установке данной опции знак * воспринимается как литера, а не как знак подстановки любые символы. Пример фильтрации можно найти в проекте Demos\DB\Filter из примеров, поставляемых с Delphi.
Все вышесказанное позволяет реализовывать фильтрацию данных по достаточно простым условиям. В случае, если необходимо реализовать более сложный нестандартный фильтр можно написать обработчик события OnFilterRecord. Тип события определен как.
type TFilterRecordEvent = procedure(DataSet: TDataSet; var Accept: Boolean) of object;
Другими словами в обработчике события можно изменять переменную Accept, указывая будет ли отображаться каждая конкретная запись. Например обработчик может выглядеть так:
Accept := DataSet['DateOfPayment'] > DataSet['DateOfPurchase'] + 30;Не забывайте, что обработчик события дополняет, а не замещает свойство Filter, т.е если включена фильтрация (Filtered := true) и св-во фильтр содержит значение фильтра, то в обработчике события и фильтр связаны логическим отношением "AND". Заключение
Конечно, невозможно изложить в статье все приемы и хитрости поиска данных, а тем более в такой гибкой и мощной среде разработки как Delphi. Мы рассмотрели лишь общие положения. В частности, за рамками статьи остались такие темы, как поиск в трехуровневых приложениях баз данных.
Поиск звуковой платы.
| На уроке литературы в компьютерном лицее: - Герасим был это... этим... Ну, звуковой карты у него не было. |
uses MMsystem;
begin
if WaveOutGetNumDevs>0 then
Result:='Yes'
else
Result:='No';
end;
|
Процедура поиска закладок и перехода по ним.
// процедура поиска закладок и перехода по ней procedure WordGotoBookmark(rBookmark: string); var Whatr: OLEVariant; // OleEnum; // OLEVariant; // What :TWordDocument; Which: OLEVariant; Count: OLEVariant; Name: OLEVariant; begin Whatr := wdGoToBookmark; Which := unAssigned; Count := unAssigned; Name := rBookmark; Form1.WrdApp.Selection.GoTo_(Whatr, Which, Count, Name); end; |
Произвести поиск байта или слова в строке.
Автор: http://sunsb.dax.ru У семейства x86 есть группа специализированных строковых инструкций, одна из которых - scasb/scasw - производит поиск байта/слова в строке. Использовать преимущества этой инструкции в Delphi позволяют длинные строки, которых в старых паскалях не было.Никаких сложностей с пониманием, на мой взгляд, быть не должно. Единственное это смена режима открытия файла (FileMode := 0), которая позволит открыть файлы заблокированные ядром Windows и сдвиг указателя файла при чтении нового блока влево на длину искомой строки. Сдвиг делается на случай разрезания искомой строки на части при чтении файла. Полный текст проверенной программы:
program search;
{$APPTYPE CONSOLE}
uses SysUtils;
const buffSize = 16384;
var F : File;
var buff : AnsiString;
var oldFileMode : integer;
var SearchString: shortString='SunSB';
var SearchPos : integer = -1;
var readed : integer;
var blockStart: integer;
begin
SetLength( buff, buffSize);
assignFile( F, 'Speedometer2.exe');
oldFileMode := FileMode;
FileMode := 0;
reset( F,1);
while not eof( F ) do begin
blockStart := filePos( F );
blockRead( F, buff[1], buffSize, readed);
SearchPos:=Pos( SearchString, buff );
if SearchPos > 0 then begin
WriteLn( 'Substr found at pos ',
blockStart+SearchPos );
break;
end;
if readed=buffSize then
seek( F, ( filePos( F ) -
length( SearchString )));
end;
closeFile( F );
FileMode := oldFileMode;
SetLength( buff, 0 );
if SearchPos = 0 then
WriteLn( 'Substr not found.');
readLn;
end.
|
Поиск класса.
Автор: Mike Scott Могу ли я во время выполнения приложения определить, существует ли класс с именем Tlog? Используйте в модуле TLog RegisterClass( TLog ) или потомка TLog, затем FindClass( 'TLog' ) или FindClass( 'TLogSubclass' ) для получения ссылки на класс в вызывающем модуле, позволяя тем самым пользоваться объектами данного класса. Вы также могли бы добавить классовый метод, возвращающий существующий экземпляр или NIL, или который создает и возвращает новый экземпляр при отсутствии текущего. Самое необходимое, что нужно сделать, это создать абстрактный, чисто виртуальный базовый класс TLog, и TLogSubclass, реально наполненный функциональным назначением. Вызывающему оператору необходимо знать всего лишь о TLog, а не о TLogSubclass, чтобы получить доступ к его методам и свойствам.Тем не менее, я считаю не лучшим решением технологию поиска класса по его имени. Вот что предлагаю я:
unit LogUnit;
interface
type
TLog = class
public
constructor Create;
procedure LogMessage(const Message: string); virtual; abstract;
end;
var
Log: TLog;
implementation
constructor TLog.Create;
begin
Log := Self;
end;
procedure TidyUp; far;
begin
Log.Free;
end;
initialization
AddExitProc(TidyUp);
end.
|
unit LogImpl;
interface
implementation
uses Log;
type
TLogImplementation = class(TLog)
public
procedure LogMessage(const Message: string); override;
end;
procedure TLogImplementation.LogMessage(const Message: string);
begin
{ записываем сообщение в журнальный файл }
end;
initialization
TLogImplementation.Create;
end.
|
Кроме того, вы могли бы иметь ничего не делающий TLog.LogMessage. Затем вы могли бы создать экземпляр TLog в секции инициализации модуля LogUnit.pas, и освобождать его перед созданием экземпляра TLogImplementation в LogImpl.pas. Таким образом, для подключения к приложению класса, нужно просто добавить к проекту модуль LogImpl.
Поиск DOS-окна.
| Как отладить Windows. Заходите в DOS, набираете "format c:", а затем нажимаете "y". По другому он не понимает. |
При поиске окон, как отмечалось, нужен класс и имя, так вот - если Вы ищите DOS-окно, то его класс всегда = 'tty'.
Поиск файлов.
| Новая русскоязычная поисковая система "Иван Сусанин" |
Не правда ли, знакомая ситуация? Необходимо сделать так, чтобы программа искала какой-либо файл... Все, хорошо, если у Вас для этого есть специальная компонента (кстати, не входящая в стандартный набор). А если ее нет? Здесь придется писать алгоритм поиска файла.
В Delphi существует две функции для поиска файлов. Это:
function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer; function FindNext(var F: TSearchRec): Integer; |
Разберемся, что же означают эти функции. Для начала возьмем первую - FindFirst. Разберемся сначала с переменными.
Path Это переменная, как видно из названия, показывающая путь к директории, где будет производиться поиск файла. Кроме этого, в эту переменную входит также и имя файла (файлов), которые должны быть найдены. Причем, в названии файла можно пользоваться такими символами: * (звездочка) и ? (знак вопроса). Значения этих символов стандартны: знак вопроса - любой допустимый символ, звездочка - комбинация любых допустимых символов. Под допустимыми символами я понимаю символы, которые могут использоваться в операционной системе для обозначения имен файлов.Пример использования переменной Path:
{поиск файлов с любым именем в корне диска C:}
Path:='c:\*.*';
{поиск файлов в директории E:\AUDIO с именем, которое
начинается на song3, с расширением .wav}
Path:='e:\audio\song3?.wav';
|
Обратите внимание: Недопустимо использовать символы * и ? в названии директории. Эти символы могут использоваться только в имени файла.
Attr Переменная задает тип файлов, которые будут найдены. Тип переменной - Integer. Чтобы не мучаться с запоминанием цифр, рекомендую Вам запомнить такие слова: faReadOnly - файлы, у которых установлен аттрибут "Только для чтения". faHidden - файлы, у которых установлен атрибут "Скрытые". faSysFile - файлы, у которых установлен атрибут "Системный". faArchive - файлы, у которых установлен атрибут "Архивный". faDirectory - директория. То есть поиск поддиректорий в директории. faAnyFile - любой файл (в том числе и faDirectory, и faVolumeID).Теперь с этими словами можно обращаться как с цифрами - складывать их и вычитать. Например:
{поиск Скрытых и Системных файлов}
Attr := faHidden + faSysFile;
{поиск всех файлов, кроме файлов,
имеющих атрибут "Только для чтения"}
Attr := faAnyFile - faReadOnly;
|
Теперь разберемся, что же выдает функция. Функция возвращает 0, если была выполнена успешно, или, в противном случае, код ошибки. Кроме того, если функция нашла файл, удовлетворяющий и условиям переменной Path, и условиям переменной Attr, то она записывает результат в переменную F (типа TSearchRec), которая, естественно, должна быть объявлена командой Var.
Тип TSearchRec можно представить как:
type TSearchRec = record Time: Integer; Size: Integer; Attr: Integer; name: TFileName; ExcludeAttr: Integer; FindHandle: THandle; FindData: TWin32FindData; end; |
Теперь разберемся, что означает функция FindNext.
Если команда FindFirst нашла какой-либо файл, то, возможно, если имя файла задано с символами * и/или ?, есть еще один или несколько файлов, удовлетворяющих условию поиска. В этом случае и используется команда FindNext. Функция также возвращает 0, если была выполнена успешно, или, в противном случае, код ошибки. И также записывает данные в переменную F.
Теперь, зная эти две команды, можно составить и алгоритм поиска заданного файла.
Простейший алгоритм:
var
F: TSearchRec;
Path: string;
Attr: Integer;
begin
{Искать все файлы в заданной директории с расширение .wav}
Path := 'e:\audio\album31\*.wav';
{которые имеют атрибуты "Только для чтения" и "Архивный"}
Attr := faReadOnly + faArchive;
FindFirst(Path, Attr, F);
{Если хотя бы один файл найден, то продолжить поиск}
if F.name <> '' then
begin
ListBox1.Items.Add(F.name); {Добавление в TListBox имени найденного файла}
while FindNext(F) = 0 do
ListBox1.Items.Add(F.name);
end;
FindClose(F);
end.
|
Обратите внимание на процедуру FindClose. Она освобождает память, которую заняли функции FindFirst и FindNext.
Как запустить диалог поиска файла.
| Автор: Олег Кулабухов |
| У программиста потерялся файл, и он его никак не может найти. После пяти минут поисков он громко объявляет: - Так, потерялся файл. Просьба никому не выходить из комнаты! |
procedure TForm1.Button1Click(Sender: TObject);
begin
with TDDEClientConv.Create(Self) do
begin
ConnectMode := ddeManual;
ServiceApplication := 'explorer.exe';
SetLink('Folders', 'AppProperties');
OpenLink;
ExecuteMacro('[FindFolder(, C:\DOWNLOAD)]', False);
CloseLink;
Free;
end;
end;
|
Поиск файлов по маске в заданной директории.
Автор: Xavier Pacheco
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, FileCtrl, Grids, Outline, DirOutln;
type
TMainForm = class(TForm)
dcbDrives: TDriveComboBox;
edtFileMask: TEdit;
lblFileMask: TLabel;
btnSearchForFiles: TButton;
lbFiles: TListBox;
dolDirectories: TDirectoryOutline;
procedure btnSearchForFilesClick(Sender: TObject);
procedure dcbDrivesChange(Sender: TObject);
private
FFileName: string;
function GetDirectoryName(Dir: string): string;
procedure FindFiles(APath: string);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
function TMainForm.GetDirectoryName(Dir: string): string;
{ This function formats the directory name so that it is a valid
directory containing the back-slash (\) as the last character. }
begin
if Dir[Length(Dir)] <> '\' then
Result := Dir + '\'
else
Result := Dir;
end;
procedure TMainForm.FindFiles(APath: string);
{ This is a procedure which is called recursively so that it finds the
file with a specified mask through the current directory and its
sub-directories. }
var
FSearchRec,
DSearchRec: TSearchRec;
FindResult: integer;
function IsDirNotation(ADirName: string): Boolean;
begin
Result := (ADirName = '.') or (ADirName = '..');
end;
begin
APath := GetDirectoryName(APath); // Obtain a valid directory name
{ Find the first occurrence of the specified file name }
FindResult := FindFirst(APath + FFileName, faAnyFile + faHidden +
faSysFile + faReadOnly, FSearchRec);
try
{ Continue to search for the files according to the specified
mask. If found, add the files and their paths to the listbox.}
while FindResult = 0 do
begin
lbFiles.Items.Add(LowerCase(APath + FSearchRec.Name));
FindResult := FindNext(FSearchRec);
end;
{ Now search the sub-directories of this current directory. Do this
by using FindFirst to loop through each subdirectory, then call
FindFiles (this function) again. This recursive process will
continue until all sub-directories have been searched. }
FindResult := FindFirst(APath + '*.*', faDirectory, DSearchRec);
while FindResult = 0 do
begin
if ((DSearchRec.Attr and faDirectory) = faDirectory) and not
IsDirNotation(DSearchRec.Name) then
FindFiles(APath + DSearchRec.Name); // Recursion here
FindResult := FindNext(DSearchRec);
end;
finally
FindClose(FSearchRec);
end;
end;
procedure TMainForm.btnSearchForFilesClick(Sender: TObject);
{ This method starts the searching process. It first changes the cursor
to an hourglass since the process may take awhile. It then clears the
listbox and calls the FindFiles() function which will be called
recursively to search through sub-directories }
begin
Screen.Cursor := crHourGlass;
try
lbFiles.Items.Clear;
FFileName := edtFileMask.Text;
FindFiles(dolDirectories.Directory);
finally
Screen.Cursor := crDefault;
end;
end;
procedure TMainForm.dcbDrivesChange(Sender: TObject);
begin
dolDirectories.Drive := dcbDrives.Drive;
end;
end.
Поиск файлов по маске по всему дереву каталогов, начиная с указанного.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Поиск файлов по маске по всему дереву каталогов, начиная с указанного
Продедура производит фалов по маске в указанном и во всех его вложенных
каталогах.
Параметр StartFolder указывает на начальный каталог поиска,
параметр Mask указывает маску поиска (например '*.pas'),
параметр List указывает на список строк, в которых будут записаны
результаты поиска,
Необязательный параметр ScanSubFolders указывает на необходимость
поиска во вложенных подкаталогах.
Зависимости: Windows, SysUtils, Classes, Masks
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 29 апреля 2002 г.
***************************************************** }
procedure FindFiles(StartFolder, Mask: string; List: TStrings;
ScanSubFolders: Boolean = True);
var
SearchRec: TSearchRec;
FindResult: Integer;
begin
List.BeginUpdate;
try
StartFolder := IncludeTrailingBackslash(StartFolder);
FindResult := FindFirst(StartFolder + '*.*', faAnyFile, SearchRec);
try
while FindResult = 0 do
with SearchRec do
begin
if (Attr and faDirectory) <> 0 then
begin
if ScanSubFolders and (Name <> '.') and (Name <> '..') then
FindFiles(StartFolder + Name, Mask, List, ScanSubFolders);
end
else
begin
if MatchesMask(Name, Mask) then
List.Add(StartFolder + Name);
end;
FindResult := FindNext(SearchRec);
end;
finally
FindClose(SearchRec);
end;
finally
List.EndUpdate;
end;
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
FindFiles('C:\Program Files\', '*.exe', memo1.Lines, true);
end;
Поиск в базе данных ADO.
Самая распространённая задача, которую решают приложения работающие с базами данных - это поиск необходимых записей по заданному критерию. В Delphi, компоненты ADOExpress включают в себя методы поиска записей, аналогичные тем, которые используются в BDE. В данной статье будут рассмотрены различные способы поиска данных разработке ADO-приложений в Delphi Обычно алгоритм поиска строится по следующей схеме: начинаем поиск с начала таблицы, проверяем поле в каждой строке на предмет удовлетворения нашему критерию, останавливаем цикл на выбранной записи. Давайте рассмотрим несколько способов расположения данных, полученных из БД посредствам компонента ADODataset (для Таблицы и для Запроса). LocateЭтот универсальный метод поиска устанавливает текущую запись как первую строку, удовлетворяющую набору критериев поиска. Используя метод Locate мы можем искать значения одного или более полей, расположенных в массиве переменных. В приведённом ниже коде, метод Locate ищет первую запись, содержащую строку 'Zoom' в поле 'Name'. Если вызов Locate возвращает True - то запись найдена и установлена как текущая.
AdoTable1.Locate('Name','Zoom',[]);
{...или...}
var ffield, fvalue: string;
opts : TLocateOptions;
ffield := 'Name';
fvalue := 'zoom';
opts := [loCaseInsensitive];
if not AdoTable1.Locate(ffield, fvalue, opts) then
ShowMessage(fvalue + ' not found in ' + ffield);
|
Следующий пример заполняет заполняет массив переменных LookupRes.
var LookupRes: Variant;
LookupRes := ADOTable1.Lookup
('Name', 'Zoom', 'Author; Description');
if not VarIsNull(LookupRes) then
ShowMessage(VarToStr(LookupRes[0])) //имя автора
|
Метод GetIndexNames в компоненте TADOTable возвращает список (например: ячеек combo box) доступных индексов для таблицы.
ADOTable1.GetIndexNames(ComboBox1.Items); |
Метод Seek имеет следующее определение:
function Seek(const KeyValues: Variant; SeekOption: TSeekOption = soFirstEQ): Boolean; |
SeekOption - Назначение.
soFirstEQ Указатель на запись позиционируется в первую удовлетворяющую требованиям запись, если она найдена, либо в конец таблицы, если не найдена soLastEQ Указатель на запись позиционируется на последнюю удовлетворяющую требованиям запись если она найдена, либо в конец таблицы если нет. soAfterEQ Указатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо сразу после той, которая была найдена. soAfter Указатель на запись позиционируется сразу после той, которая была найдена. soBeforeEQ Указатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо перед той, которая была найдена. soBefore Указатель на запись позиционируется перед той записью, которая была найдена. Примечание 1: метод Seek поддерживает курсоры только на стороне сервера (server-side). Seek не будет работать, если значение свойства CursorLocation равно clUseClient. Для этого используется метод Supports для определения основного провайдера, поддерживающего Seek. Примечание 2: когда Вы используйте метод Seek для нескольких полей, то Seek поля должны быть в том же самом порядке как поля в основной таблице. Если это не так, то метод Seek выдаст ошибку. Примечание 3: Вы не сможете использовать метод Seek в компоненте TADOQuery.Чтобы определять, была ли соответствующая запись найдена, мы используем свойства BOF или EOF (в зависимости от направления поиска). Следующий код использует индекс, указанный в ComboBox, чтобы найти значение, содержащееся в окне редактирования Edit1.
var strIndex: string;
strIndex := ComboBox1.Text; //из примера выше
if ADOTable1.Supports(coSeek) then begin
with ADOTable1 do begin
Close;
IndexName := strIndex;
CursorLocation := clUseServer;
Open;
Seek (Edit1.Text, soFirstEQ);
end;
if ADOTable1.EOF then
ShowMessage ('Record value NOT found');
end;
|
Поиск записи в больших таблицах.
Автор: Александр КупринПрочитал Ваши "Советы по Delphi" - спасибо большое: понравилось и, главное, помогло - никак не получалось нарисовать круглую форму. В свою очередь хочу предложить на Ваш суд небольшую процедуру, которая мне очень помогла. Процедура позволяет переходить на любую из записей в таблице (формат Paradox или DBase). Необходимость в ней возникла, когда мне пришлось работать с таблицей размером в 10 и более тысяч записей у которой было несколько калькулируемых полей и полей подлинкованных из объектов TQuery. При использовании метода TTable.MoveBy программа медленно и печально замолкала (вообще-то она работала, но как?!). Встретил я этот пример в технической документации Borland (2656), где сравнивались функции Paradox Engine и BDE. Пример был написан на C. Вот его интерпретация на Delphi:
uses BDE;
...
procedure MoveToRec(RecNo: longint; taSingle: TDBDataSet);
// переход на логическую запись
var
ErrorCode: DBIResult;
CursorProps: CurProps;
begin
ErrorCode := DbiGetCursorProps(taSingle.Handle, CursorProps);
if ErrorCode = DBIERR_NONE then
begin
case TTable(taSingle).TableType of
ttParadox: ErrorCode := DbiSetToSeqNo(taSingle.Handle, RecNo);
ttDBase: ErrorCode := DbiSetToRecordNo(taSingle.Handle, RecNo);
end; { case..}
taSingle.Resync([rmCenter]);
end { if..}
end; { procedure MoveToRec }
|
Поиск в базе данных.

| - Я спросил y Рэмблера: "где моя любимая?" - Ты б еще у Яндекса, идиот, спросил! |
Самая распространённая задача, которую решают приложения работающие с базами данных - это поиск необходимых записей по заданному критерию. В Delphi, компоненты ADOExpress включают в себя методы поиска записей, аналогичные тем, которые используются в BDE.
В данной статье будут рассмотрены различные способы поиска данных разработке ADO-приложений в Delphi.
Обычно алгоритм поиска строится по следующей схеме: начинаем поиск с начала таблицы, проверяем поле в каждой строке на предмет удовлетворения нашему критерию, останавливаем цикл на выбранной записи.
Давайте рассмотрим несколько способов расположения данных, полученных из БД посредствам компонента ADODataset (для Таблицы и для Запроса).
Locate
Этот универсальный метод поиска устанавливает текущую запись как первую строку, удовлетворяющую набору критериев поиска. Используя метод Locate мы можем искать значения одного или более полей, расположенных в массиве переменных. В приведённом ниже коде, метод Locate ищет первую запись, содержащую строку 'Zoom' в поле 'Name'. Если вызов Locate возвращает True - то запись найдена и установлена как текущая.
AdoTable1.Locate('Name','Zoom',[]);
{...или...}
var
ffield, fvalue: string;
opts : TLocateOptions;
ffield := 'Name';
fvalue := 'zoom';
opts := [loCaseInsensitive];
if not AdoTable1.Locate(ffield, fvalue, opts) then
ShowMessage(fvalue + ' not found in ' + ffield);
|
Lookup
Метод Lookup не перемещает курсор в соответствующую строку, а только возвращает её значение. Lookup возвращает массив переменных, содержащих значения из полей, указанных в разделённом точкой с запятой списке имён, значения которых должны быть возвращены из интересующей нас строки. Если соответствующих нашему запросу строк не найдено, то Lookup вернёт пустую (Null) переменную.
Следующий пример заполняет заполняет массив переменных LookupRes.
var
LookupRes: Variant;
LookupRes := ADOTable1.Lookup
('Name', 'Zoom', 'Author; Description');
if not VarIsNull(LookupRes) then
ShowMessage(VarToStr(LookupRes[0])) //имя автора
|
Одно из преимуществ методов Locate и Lookup, состоит в том, что они не требуют, чтобы таблица была проиндексирована. Однако, функция Locate будет работать намного быстрее, если таблица будет проиндексирована.
Индексирование
Индексирование помогает находить и сортировать записи намного быстрее. Вы можете создавать индексы основанные на одном поле либо на нескольких полях. Индексирование нескольких полей позволяет Вам различать записи, в которых первое поле может иметь то же самое значение. В большинстве случаев при частом поиске/сортировке желательно индексировать поля. Например, если Вы ищете определённый тип приложения в поле Type, то Вы можете создать индекс на это поле для ускорения поиска по типу. Следует упомянуть, что первичный ключ таблицы автоматически проиндексирован, а так же Вы не можете индексировать поля с типом данных OLE Object. И ещё, обратите внимание, что если многие из значений в поле те же самые, то индексирование в данном случае не ускорит процесс получения данных из БД.
BDE (не ADO) Delphi предоставляет нам определённые функции для работы с таблицами базы данных, которые позволяют нам производить поиск необходимых значений. Вот некоторые из них Goto, GoToKey, GoToNearest, Find, FindKey, Find Nearest, и т.д. Для более полной справки по этим методам, Вам следует посмотреть в справке Delphi, в разделе: Searching for records based on indexed fields. ADO напротив не поддерживает эти методы. Вместо этого он представляет метод Seek.
Seek
В ADO метод Seek использует индекс для поиска данных. Наример, при поиске в базе данных Access, если не задать индекс, то база данных будет использовать Первичный индексный ключ.
Seek используется для поиска записей с указанным значением (или значениями) в поле (либо полях) на которых основан текущий индекс. Если Seek не находит желаемую строку, то никакой ошибки не выдаётся, а курсор устанавливается в конец данных. Seek возвращает значение boolean, указывающее на успешность поиска: True если запись была найдена либо False если записей удовлетворяющих нашим требований не было найдено.
Метод GetIndexNames в компоненте TADOTable возвращает список (например: ячеек combo box) доступных индексов для таблицы.
ADOTable1.GetIndexNames(ComboBox1.Items); |
Этот же список доступен в режиме разработки в свойстве IndexName компонента TADOTable. Свойство IndexFieldNames может использоваться как альтернативный метод для определения индекса используемого в таблице. В IndexFieldNames, мы указываем имя каждого поля для использования в таблице.
Метод Seek имеет следующее определение:
function Seek(const KeyValues: Variant; SeekOption: TSeekOption = soFirstEQ): Boolean; |
Значения SeekOption:
sоFirstЕQУказатель на запись позиционируется в первую удовлетворяющую требованиям запись, если она найдена, либо в конец таблицы, если не найдена sоLаstЕQУказатель на запись позиционируется на последнюю удовлетворяющую требованиям запись если она найдена, либо в конец таблицы если нет. sоАftеrЕQУказатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо сразу после той, которая была найдена. sоАftеrУказатель на запись позиционируется сразу после той, которая была найдена. sоВеfоrеЕQУказатель на запись позиционируется на удовлетворяющую требованиям запись, если таковая найдена, либо перед той, которая была найдена. sоВеfоrеУказатель на запись позиционируется перед той записью, которая была найдена.Примечание 1: метод Seek поддерживает курсоры только на стороне сервера (server-side). Seek не будет работать, если значение свойства CursorLocation равно clUseClient. Для этого используется метод Supports для определения основного провайдера, поддерживающего Seek.
Примечание 2: когда Вы используйте метод Seek для нескольких полей, то Seek поля должны быть в том же самом порядке как поля в основной таблице. Если это не так, то метод Seek выдаст ошибку.
Примечание 3: Вы не сможете использовать метод Seek в компоненте TADOQuery.
Чтобы определять, была ли соответствующая запись найдена, мы используем свойства BOF или EOF (в зависимости от направления поиска). Следующий код использует индекс, указанный в ComboBox, чтобы найти значение, содержащееся в окне редактирования Edit1.
var
strIndex: string;
strIndex := ComboBox1.Text; //из примера выше
if ADOTable1.Supports(coSeek) then
begin
with ADOTable1 do
begin
Close;
IndexName := strIndex;
CursorLocation := clUseServer;
Open;
Seek (Edit1.Text, soFirstEQ);
end;
if ADOTable1.EOF then
ShowMessage ('Record value NOT found');
end;
|
Поиск в DBGride без ввода искомого значения в поле ввода.
| Твой Windows свопиться украдкой На твой заполненый хард драйв В него тихонько кинь гранаткой Чтоб больше места он не взял. |
Можно обрабатывать событие OnKeyPress компонента DBGrid:
procedure TSubscriber_frm.RxDBGrid1KeyPress(Sender: TObject;
var Key: Char);
begin
// если набор номера (численного значения)
if ((Ord(key)>=48) and (Ord(key)or ((Ord(key)>=96) and (Ord(key)then
begin
edit1.Visible:=True;
edit1.SetFocus;
edit1.Text:=key;
edit1.SelStart:=1;
end;
// если набор текста
if (Ord(key)>=192) and (Ord(key)then
begin
edit2.Visible:=True;
edit2.SetFocus;
edit2.Text:=key;
edit2.SelStart:=1;
end;
end;
|
А в обработке события OnChange edit1 и edit2 применить ф-цию FindNearest по нужному полю. Прятать edit-ы , после удачного поиска, либо по нажатии пользователем стрелки, Enter-a и т.п.
Простейший сканер диска.
| Первое правило сисадина: "Лежаший на столе винчестер с данными выглядит точно так же как и винчестер без данных". |
Вот пример, который ищет мпЗ файлы на жестком диске...
unit Audit1;
interface
uses windos;
var
dest: string;
procedure dorecurse(dir: string);
implementation
{$R *.DFM}
procedure Process(dir: string; Searchrec: tsearchrec);
begin
showmessage(Searchrec.name);
case Searchrec.attr of
$10:
if (searchrec.name <> '.') and (searchrec.name <> '..') then
begin
dorecurse(dir + '\' + searchrec.name);
writeln(dir);
end;
end;
end;
procedure Dorecurse(dir: string);
var
Searchrec: Tsearchrec;
pc: array[0..79] of Char;
begin
StrPCopy(pc, dir + '\*.mp3');
FindFirst(pc, FaAnyfile, SearchRec);
Process(dir, SearchRec);
while FindNext(SearchRec) <> -18 do
begin
Process(dir, SearchRec);
end;
end;
procedure startsearch;
begin
dorecurse(paramstr(1));
end;
begin
startsearch;
end.
|
Поиск кратчайшего пути.
Обычно задача поиска пути на графе формулируется следующим образом: найти наилучший маршрут. Под наилучшим маршрутом, как правило, понимают кратчайший. Найти кратчайший маршрут можно выбором из всех найденных. Однако совсем не обязательно искать все маршруты. Можно поступить иначе: во время выбора очередной точки проверить, не превысит ли длина формируемого маршрута длину уже найденного пути, если эта точка будет включена в маршрут; если превысит, то эту точку следует пропустить и выбрать другую. Таким образом, после того как будет найден первый маршрут, программа будет вести поиск только по тем ветвям графа, которые могут улучшить найденное решение, отсекая пути, делающие формируемый маршрут длиннее уже найденного.В листинге приведена процедура, которая использует процедуру step, выполняющую выбор очередной точки маршрута таким образом, что обеспечивается поиск пути минимальной длины.
procedure TForm1.Button1Click(Sender: TObject);
const
N = 10; { кол-во вершин графа}
var
map: array[1..N, 1..N] of integer;
// Карта.map[i,j] не 0,если
// точки i и j соединены
road: array[1..N] of integer;
// Дорога — номера точек карты
incl: array[1..N] of boolean; // incl[1]равен TRUE,если точка
// с номером i включена в road
start, finish: integer;
// Начальная и конечная точки
found: boolean; len: integer; // длина найденного (минимального) маршрута
c_len: integer; // длина текущего (формируемого) маршрута
i, j: integer;
// выбор очередной точки
procedure step(s, f, p: integer);
var
с: integer; { Номер точки, в которую делаем очередной шаг }
i: integer;
begin
if s = f then
begin
len := c_len; { сохраним длину найденного маршрута }
{ вывод найденного маршрута }
for i := 1 to p - 1 do
Label1.caption := Label1.caption + ' ' + IntToStr(road[i]);
Label1.caption := Label1.caption
+ ', длина:' + IntToStr(len) + #13;
end
else
{ выбираем очередную точку }
for c := l to N do { проверяем все вершины }
if (map[s, c] <> 0) and (not incite])
and ((len = 0) or (c_len + map[s, c] < len)) then
begin
// точка соединена с текущей, но не включена в маршрут
roadtp] := c; { добавим вершину в путь }
incl[c] := TRUE; { пометим вершину как включенную }
c_len := c_len + map[s, с];
step(c, f, p + l);
incite := FALSE;
roadtp := 0;
c_len := c_len - map[s, с];
end;
end;
{ конец процедуры step }
begin
Labell.caption := '';
{ инициализация массивов }
for i: = 1 to N do
road[i]: = 0;
for i := l to N do
incl[i] := FALSE;
{ ввод описания карты из SrtingGrid.Cells}
for i := l to N do
for j := 1 to N do
if StringGridl.Cells[i, j] <> '' then
mapti, j] := StrToInt(StringGridl.Cells[i, j])
else
mapti, j] := 0;
len := 0; // длина найденного (минимального) маршрута
len := 0, // длина текущего (формируемого) маршрута
start := StrToInt(Edit1.text);
finish := StrToInt(Edit2.text);
road[1] := start; { внесем точку в маршрут }
incl[start] := TRUE; { пометим ее как включенную }
step(start, finish, 2); {ищем вторую точку маршрута }
// проверим, найден ли хотя бы один путь
if not found then
Label1.caption := 'Указанные точки не соединены!';
end;
Диалоговое окно программы поиска кратчайшего пути и процедура обработки события OnActivate ничем не отличаются от диалогового окна и соответствующей процедуры OnActivate программы поиска всех возможных маршрутов, рассмотренной в предыдущем разделе.
Поиск пути.
unit road_;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids;
type
TForml = class(TForm)
StringGridl: TStringGrid;
Edit1: TEdit;
Edit2: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
Label4: TLabel;
procedure FormActivate(Sender: TObject);
procedure ButtonlClickfSender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForml.FormActivate(Sender: TObject);
var
i: integer;
begin
// нумерация строк
for i := 1 to 10 do
StringGridl.Cells[0, i] := IntToStr(i); // нумерация колонок
for i := l to 10 do
StringGridl.Cells[1, 0] := IntToStr(i);
// описание предопределенной карты
StringGridl.Cells[1,2]:='1'
StringGridl.Cells[2,l]:='1'
StringGridl.Cells[1, 3] := '1'
StringGridl.Cells[3, 1] := '1'
StringGridl.Cells[1, 4] := '1'
StringGridl.Cells[4, 1] := '1'
StringGridl.Cells[3, 7] := '1'
StringGridl.Cells[7, 3] := '1'
StringGridl.Cells[4, 6] := '1'
StringGridl.Cells[6, 4] := '1'
StringGridl.Cells[5, 6] := '1'
StringGridl.Cells[6, 5] := '1'
StringGridl.Cells[5, 7] := '1'
StringGridl.Cells[7, 5] := '1'
StringGridl.Cells[6, 7] := '1'
StringGridl.Cells[7, 6] := '1'
end;
procedure TForml.ButtonlClick(Sender: TObject);
const
N = 10; // кол-во вершин графа var
map: array[1..N, 1..N] of integer; // Карта.map[i,j]ne 0,
// если точки i и j соединены
road: array[1..N] of integer;
// Дорога - номера точек карты
incl: array[1..N] of boolean; // incl[1]равен TRUE, если точка
// с номером i включена в road
start, finish: integer; // Начальная и конечная точки
found: boolean; i, j: integer;
procedure step(s, f, p: integer);
var
с: integer; // Номер точки, в которую делаем очередной шаг
i: integer;
begin
if s = f then
begin
// Точки s и f совпали !
found := TRUE;
Labell.caption := Labell.caption + #13 + 'Путь:';
for i := l to p - 1 do
Labell.caption := Labell.caption + ' '
+ IntToStr(road[i]);
end
else
begin
// выбираем очередную точку
for c:=l to N do
begin // проверяем все вершины
// точка соединена с текущей и не включена в маршрут
if (map[s, c] <> 0) and (not incite1) then
begin
road[p] := c; // добавим вершину в путь
incl[c] := TRUE; // пометим вершину как включенную
step(c, f, p + l); incite] := FALSE;
road[p] := 0;
end;
end;
end;
end; // конец процедуры step
begin
Label1.caption: = ' ';
// инициализация массивов
for i := l to N do
road[i] := 0;
for i := l to N do
incl[i] := FALSE;
// ввод описания карты из SrtingGrid.Cells
for i := l to N do
for j := 1 to N do
if StringGrid1.Cells[i, j] <> '' then
map[i, j] := StrToInt(StringGridl.Cells[i, j];
else
map[i, j] := 0;
start := StrToInt(Editl.text);
finish := StrToInt(Edit2.text);
road[l] := start; // внесем точку в маршрут
incl[start] := TRUE; // пометим ее как включенную
step(start, finish, 2); //ищем вторую точку маршрута
// проверим, найден ли хотя бы один путь
if not found then
Labell.caption := 'Указанные точки не соединены!';
end;
end.
При запуске программы в момент активизации формы приложения происходит событие onActivate, процедура обработки которого заполняет массив StringGridl.cells значениями, представляющими описание карты. Этаже процедура нумерует строки и столбцы таблицы, заполняя зафиксированные ячейки первого столбца и первой строки StringGridl.
Поиск маршрута инициирует процедура TFormi.Buttoniciick, которая запускается щелчком на кнопке Поиск. Данная процедура для поиска точки, соединенной с исходной точкой, вызывает процедуру step, которая после выбора первой точки, соединенной с начальной, и включения ее в маршрут вызывает сама себя. При этом в качестве начальной точки задается уже не исходная, а текущая, только что включенная в маршрут.
Поиск в отдельном потоке фразы в файлах.
Автор: Xavier Pacheco
unit Main;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, Menus, SrchIni,
SrchU, ComCtrls, AppEvnts;
type
TMainForm = class(TForm)
lbFiles: TListBox;
StatusBar: TStatusBar;
pnlControls: TPanel;
PopupMenu: TPopupMenu;
FontDialog: TFontDialog;
pnlOptions: TPanel;
gbParams: TGroupBox;
LFileSpec: TLabel;
LToken: TLabel;
lPathName: TLabel;
edtFileSpec: TEdit;
edtToken: TEdit;
btnPath: TButton;
edtPathName: TEdit;
gbOptions: TGroupBox;
cbCaseSensitive: TCheckBox;
cbFileNamesOnly: TCheckBox;
cbRecurse: TCheckBox;
cbRunFromAss: TCheckBox;
pnlButtons: TPanel;
btnSearch: TBitBtn;
btnClose: TBitBtn;
btnPrint: TBitBtn;
btnPriority: TBitBtn;
Font1: TMenuItem;
Clear1: TMenuItem;
Print1: TMenuItem;
N1: TMenuItem;
Exit1: TMenuItem;
ApplicationEvents: TApplicationEvents;
procedure btnSearchClick(Sender: TObject);
procedure btnPathClick(Sender: TObject);
procedure lbFilesDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure Font1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnPrintClick(Sender: TObject);
procedure btnCloseClick(Sender: TObject);
procedure lbFilesDblClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure btnPriorityClick(Sender: TObject);
procedure edtTokenChange(Sender: TObject);
procedure Clear1Click(Sender: TObject);
procedure ApplicationEventsHint(Sender: TObject);
private
procedure ReadIni;
procedure WriteIni;
public
Running: Boolean;
SearchPri: Integer;
SearchThread: TSearchThread;
procedure EnableSearchControls(Enable: Boolean);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
uses Printers, ShellAPI, StrUtils, FileCtrl, PriU;
procedure PrintStrings(Strings: TStrings);
{ This procedure prints all of the strings in the Strings parameter }
var
Prn: TextFile;
I: Integer;
begin
if Strings.Count = 0 then // Are there strings?
raise Exception.Create('No text to print!');
AssignPrn(Prn); // assign Prn to printer
try
Rewrite(Prn); // open printer
try
for I := 0 to Strings.Count - 1 do // iterate over all strings
WriteLn(Prn, Strings.Strings[I]); // write to printer
finally
CloseFile(Prn); // close printer
end;
except
on EInOutError do
MessageDlg('Error Printing text.', mtError, [mbOk], 0);
end;
end;
procedure TMainForm.EnableSearchControls(Enable: Boolean);
{ Enables or disables certain controls so options can't be modified }
{ while search is executing. }
begin
btnSearch.Enabled := Enable; // enable/disable proper controls
cbRecurse.Enabled := Enable;
cbFileNamesOnly.Enabled := Enable;
cbCaseSensitive.Enabled := Enable;
btnPath.Enabled := Enable;
edtPathName.Enabled := Enable;
edtFileSpec.Enabled := Enable;
edtToken.Enabled := Enable;
Running := not Enable; // set Running flag
edtTokenChange(nil);
with btnClose do
begin
if Enable then
begin // set props of Close/Stop button
Caption := '&Close';
Hint := 'Close Application';
end
else
begin
Caption := '&Stop';
Hint := 'Stop Searching';
end;
end;
end;
procedure TMainForm.btnSearchClick(Sender: TObject);
{ Called when Search button is clicked. Invokes search thread. }
begin
EnableSearchControls(False); // disable controls
lbFiles.Clear; // clear listbox
{ start thread }
SearchThread := TSearchThread.Create(cbCaseSensitive.Checked,
cbFileNamesOnly.Checked, cbRecurse.Checked, edtToken.Text,
edtPathName.Text, edtFileSpec.Text);
end;
procedure TMainForm.edtTokenChange(Sender: TObject);
begin
btnSearch.Enabled := not Running and (edtToken.Text <> '');
end;
procedure TMainForm.btnPathClick(Sender: TObject);
{ Called when Path button is clicked. Allows user to choose new path. }
var
ShowDir: string;
begin
ShowDir := edtPathName.Text;
if SelectDirectory('Choose a search path...', '', ShowDir) then
edtPathName.Text := ShowDir;
end;
procedure TMainForm.lbFilesDrawItem(Control: TWinControl;
Index: Integer; Rect: TRect; State: TOwnerDrawState);
{ Called in order to owner draw listbox. }
var
CurStr: string;
begin
with lbFiles do
begin
CurStr := Items.Strings[Index];
Canvas.FillRect(Rect); // clear out rect
if not cbFileNamesOnly.Checked then // if not filename only...
{ if current line is filename... }
if (Pos('File ', CurStr) = 1) and
(CurStr[Length(CurStr)] = ':') then
with Canvas.Font do
begin
Style := [fsUnderline]; // underline font
Color := clRed; // paint red
end
else
Rect.Left := Rect.Left + 15; // otherwise, indent
DrawText(Canvas.Handle, PChar(CurStr), Length(CurStr), Rect,
DT_SINGLELINE);
end;
end;
procedure TMainForm.Font1Click(Sender: TObject);
{ Allows user to pick new font for listbox }
begin
{ Pick new listbox font }
if FontDialog.Execute then
lbFiles.Font := FontDialog.Font;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
{ OnDestroy event handler for form }
begin
WriteIni;
end;
procedure TMainForm.FormCreate(Sender: TObject);
{ OnCreate event handler for form }
begin
ReadIni; // read INI file
end;
procedure TMainForm.btnPrintClick(Sender: TObject);
{ Called when Print button is clicked. }
begin
if MessageDlg('Send search results to printer?', mtConfirmation,
[mbYes, mbNo], 0) = mrYes then
PrintStrings(lbFiles.Items);
end;
procedure TMainForm.btnCloseClick(Sender: TObject);
{ Called to stop thread or close application }
begin
// if thread is running then terminate thread
if Running then
SearchThread.Terminate
// otherwise close app
else
Close;
end;
procedure TMainForm.lbFilesDblClick(Sender: TObject);
{ Called when user double-clicks in listbox. Invokes viewer for }
{ highlighted file. }
var
ProgramStr, FileStr: string;
RetVal: THandle;
begin
{ if user clicked on a file.. }
if (Pos('File ', lbFiles.Items[lbFiles.ItemIndex]) = 1) then
begin
{ load text editor from INI file. Notepad is default. }
ProgramStr := SrchIniFile.ReadString('Defaults', 'Editor', 'notepad');
FileStr := lbFiles.Items[lbFiles.ItemIndex]; // Get selected file
FileStr := Copy(FileStr, 6, Length(FileStr) - 5); // Remove prefix
if FileStr[Length(FileStr)] = ':' then // Remove ":"
DecStrLen(FileStr, 1);
if cbRunFromAss.Checked then
{ Run file from shell association }
RetVal := ShellExecute(Handle, 'open', PChar(FileStr), nil, nil,
SW_SHOWNORMAL)
else
{ View file using text editor }
RetVal := ShellExecute(Handle, 'open', PChar(ProgramStr),
PChar(FileStr), nil, SW_SHOWNORMAL);
{ Check for error }
if RetVal < 32 then
RaiseLastWin32Error;
end;
end;
procedure TMainForm.FormResize(Sender: TObject);
{ OnResize event handler. Centers controls in form. }
begin
{ divide status bar into two panels with a 1/3 - 2/3 split }
with StatusBar do
begin
Panels[0].Width := Width div 3;
Panels[1].Width := Width * 2 div 3;
end;
end;
procedure TMainForm.btnPriorityClick(Sender: TObject);
{ Show thread priority form }
begin
ThreadPriWin.Show;
end;
procedure TMainForm.ReadIni;
{ Reads default values from Registry }
begin
with SrchIniFile do
begin
edtPathName.Text := ReadString('Defaults', 'LastPath', 'C:\');
edtFileSpec.Text := ReadString('Defaults', 'LastFileSpec', '*.*');
edtToken.Text := ReadString('Defaults', 'LastToken', '');
cbFileNamesOnly.Checked := ReadBool('Defaults', 'FNamesOnly', False);
cbCaseSensitive.Checked := ReadBool('Defaults', 'CaseSens', False);
cbRecurse.Checked := ReadBool('Defaults', 'Recurse', False);
cbRunFromAss.Checked := ReadBool('Defaults', 'RunFromAss', False);
Left := ReadInteger('Position', 'Left', Left);
Top := ReadInteger('Position', 'Top', Top);
Width := ReadInteger('Position', 'Width', Width);
Height := ReadInteger('Position', 'Height', Height);
end;
end;
procedure TMainForm.WriteIni;
{ writes current settings back to Registry }
begin
with SrchIniFile do
begin
WriteString('Defaults', 'LastPath', edtPathName.Text);
WriteString('Defaults', 'LastFileSpec', edtFileSpec.Text);
WriteString('Defaults', 'LastToken', edtToken.Text);
WriteBool('Defaults', 'CaseSens', cbCaseSensitive.Checked);
WriteBool('Defaults', 'FNamesOnly', cbFileNamesOnly.Checked);
WriteBool('Defaults', 'Recurse', cbRecurse.Checked);
WriteBool('Defaults', 'RunFromAss', cbRunFromAss.Checked);
WriteInteger('Position', 'Left', Left);
WriteInteger('Position', 'Top', Top);
WriteInteger('Position', 'Width', Width);
WriteInteger('Position', 'Height', Height);
end;
end;
procedure TMainForm.Clear1Click(Sender: TObject);
begin
lbFiles.Items.Clear;
end;
procedure TMainForm.ApplicationEventsHint(Sender: TObject);
{ OnHint event handler for Application }
begin
{ Display application hints on status bar }
StatusBar.Panels[0].Text := Application.Hint;
end;
end.
Скачать весь проект
Поиск свойств.
Я хотел бы во время выполнения приложения получить, если возможно, список всех свойств компонента. (Список и тип каждого свойства)Хороший материал по этому вопросу вы найдете в typinfo.pas (каталог Docs); нижеследующий код заполнит компонент Memo именами свойств:
Uses TypInfo;
procedure TForm1.Button1Click(Sender: TObject);
var
PropList: pPropList;
J, I: Integer;
begin
Memo1.Lines.Clear;
getMem(PropList, sizeof(tPropList));
J := GetPropList(TypeInfo(tButton), tkProperties, PropList);
for I := 0 to J - 1 do
Memo1.Lines.Add(PropList^[I]^.Name);
FreeMem(PropList, sizeof(tPropList));
end;
|
Поиск строки текста в наследниках TCustomEdit.
Автор: AlekseyПришло мне письмо от Алексея. На этот раз он прислал (цитирую): "юнит для поиска строки(текста) в TEdit, TMemo, или других компонентах (дочерних ТСustоmЕdit'у)." Так как тескт "авторский" (более того, здесь также присутствует наследование), помещаю его здесь в том виде, в котором он был прислан, т.е. без перевода. В случае каких-либо вопросов и недоразумений обращайтесь по вышеуказанносу адресу электронной почты.
{ПРИМЕР :
[...]
implementation
uses Search;}
{$R *.DFM}
{procedure TForm1.Button1Click(Sender: TObject);
begin
SearchMemo(RichEdit1, 'Найди меня', [frDown]);
end;
В опции поиска можно подключать, отключать, комбинировать следующие
параметры:
frDown - указывает на то, что происходит поиск вниз по тексту от курсора(при
отключенном frDоwn'е будет происходит поиск вверх по тексту).
frMatchCase - указывает на то, что следует проводить поиск с учетом
регистра.
frWholeWord - указывает на то, что следует искать только слово целиком.
[...]
Авторские права на этот юнит пренадлежат неизвесно кому.
В каком виде этот юнит попал мне, практически в этом же
виде я отдаю его вам. Пользуйтесь и благодарите неизвесного
героя.}
unit Search;
interface
uses
WinProcs, SysUtils, StdCtrls, Dialogs;
const
{****************************************************************************
* Default word delimiters are any character except the core alphanumerics. *
****************************************************************************}
WordDelimiters: set of Char = [#0..#255] - ['a'..'z', 'A'..'Z', '1'..'9',
'0'];
{******************************************************************************
* SearchMemo scans the text of a TEdit, TMemo, or other TCustomEdit-derived *
* component for a given search string. The search starts at the current *
* caret position in the control. The Options parameter determines whether *
* the search runs forward (frDown) or backward from the caret position, *
* whether or not the text comparison is case sensitive, and whether the *
* matching string must be a whole word. If text is already selected in the *
* control, the search starts at the 'far end' of the selection (SelStart if *
* searching backwards, SelEnd if searching forwards). If a match is found, *
* the control's text selection is changed to select the found text and the *
* function returns True. If no match is found, the function returns False. *
******************************************************************************}
function SearchMemo(Memo: TCustomEdit;
const SearchString: string;
Options: TFindOptions): Boolean;
{******************************************************************************
* SearchBuf is a lower-level search routine for arbitrary text buffers. *
* Same rules as SearchMemo above. If a match is found, the function returns *
* a pointer to the start of the matching string in the buffer. If no match, *
* the function returns nil. *
******************************************************************************}
function SearchBuf(Buf: PChar; BufLen: Integer;
SelStart, SelLength: Integer;
SearchString: string;
Options: TFindOptions): PChar;
implementation
function SearchMemo(Memo: TCustomEdit;
const SearchString: string;
Options: TFindOptions): Boolean;
var
Buffer, P: PChar;
Size: Word;
begin
Result := False;
if (Length(SearchString) = 0) then
Exit;
Size := Memo.GetTextLen;
if Size = 0 then
Exit;
Buffer := StrAlloc(Size + 1);
try
Memo.GetTextBuf(Buffer, Size + 1);
P := SearchBuf(Buffer, Size, Memo.SelStart, Memo.SelLength, SearchString,
Options);
if P <> nil then
begin
Memo.SelStart := P - Buffer;
Memo.SelLength := Length(SearchString);
Result := True;
end;
finally
StrDispose(Buffer);
end;
end;
function SearchBuf(Buf: PChar; BufLen: Integer;
SelStart, SelLength: Integer;
SearchString: string;
Options: TFindOptions): PChar;
var
SearchCount, I: Integer;
C: Char;
Direction: Shortint;
CharMap: array[Char] of Char;
function FindNextWordStart(var BufPtr: PChar): Boolean;
begin { (True XOR N) is equivalent to (not N) }
// Result := False; { (False XOR N) is equivalent to (N) }
{ When Direction is forward (1), skip non delimiters, then skip delimiters. }
{ When Direction is backward (-1), skip delims, then skip non delims }
while (SearchCount > 0) and
((Direction = 1) xor
(BufPtr^ in WordDelimiters)) do
begin
Inc(BufPtr, Direction);
Dec(SearchCount);
end;
while (SearchCount > 0) and
((Direction = -1) xor
(BufPtr^ in WordDelimiters)) do
begin
Inc(BufPtr, Direction);
Dec(SearchCount);
end;
Result := SearchCount > 0;
if Direction = -1 then
begin {back up one char, to leave ptr on first non delim}
Dec(BufPtr, Direction);
Inc(SearchCount);
end;
end;
begin
Result := nil;
if BufLen <= 0 then
Exit;
if frDown in Options then
begin {if frDown...}
Direction := 1;
Inc(SelStart, SelLength); { start search past end of selection }
SearchCount := BufLen - SelStart - Length(SearchString);
if SearchCount < 0 then
Exit;
if Longint(SelStart) + SearchCount > BufLen then
Exit;
end {if frDown...}
else
begin {else}
Direction := -1;
Dec(SelStart, Length(SearchString));
SearchCount := SelStart;
end; {else}
if (SelStart < 0) or (SelStart > BufLen) then
Exit;
Result := @Buf[SelStart];
{ Using a Char map array is faster than calling AnsiUpper on every character }
for C := Low(CharMap) to High(CharMap) do
CharMap[C] := C;
if not (frMatchCase in Options) then
begin {if not (frMatchCase}
AnsiUpperBuff(PChar(@CharMap), sizeof(CharMap));
AnsiUpperBuff(@SearchString[1], Length(SearchString));
end; {if not (frMatchCase}
while SearchCount > 0 do
begin {while SearchCount}
if frWholeWord in Options then
begin
if not FindNextWordStart(Result) then
Break;
end;
I := 0;
while (CharMap[Result[I]] = SearchString[I + 1]) do
begin {while (CharMap...}
Inc(I);
if I >= Length(SearchString) then
begin {if I >=...}
if (not (frWholeWord in Options)) or
(SearchCount = 0) or
(Result[I] in WordDelimiters) then
Exit;
Break;
end; {if I >=...}
end; {while (CharMap...}
Inc(Result, Direction);
Dec(SearchCount);
end; {while SearchCount}
Result := nil;
end;
end.
|
Алгоритмы поиска в тексте.
Оформил: DeeCo
Автор: Андрей Боровский.
Наверное, каждому, кто много работает за компьютером, знакома подобная ситуация: перелистывая страницы книги в поисках нужного фрагмента, невольно начинаешь думать о том, как вызвать команду «поиск по всему тексту». Действительно, современные программы обработки текста приучили нас к такой удобной возможности, как поиск и замена фрагментов, и если вы разрабатываете подобную программу, пользователь вправе ожидать, что вы предоставите в его распоряжение соответствующие команды. Эту проблему нельзя эффективно решить при помощи стандартных функций Delphi/Kylix, поскольку большинство средств разработки включает только малоэффективные средства. Во-первых, в стандартных функциях не всегда используются самые эффективные алгоритмы, а во-вторых, вполне возможно, что вам понадобится изменить стандартное поведение этих функций (например, предусмотреть возможность поиска по шаблону).
В этой статье рассматриваются два варианта наиболее эффективного алгоритма поиска в тексте – алгоритма Бойера-Мура. Мы также рассмотрим некоторые полезные расширения этого алгоритма.
Алгоритм грубой силы и простой вариант алгоритма Бойера-Мура.Прежде, чем приступить к рассмотрению алгоритма Бойера-Мура, приведем простейший (и самый медленный) алгоритм поиска, называемый «методом грубой силы».
function Find(const S, P: string): Integer;
var
i, j: Integer;
begin
Result := 0;
if Length(P) > Length(S) then
Exit;
for i := 1 to Length(S) - Length(P) + 1 do
for j := 1 to Length(P) do
if P[j] <> S[i + j - 1] then
Break
else if j = Length(P) then
begin
Result := i;
Exit;
end;
end;
Функция Find ищет подстроку P в строке S и возвращает индекс первого символа подстроки или 0, если подстрока не найдена. Хотя в общем случае этот метод, как и большинство методов грубой силы, малоэффективен, в некоторых ситуациях он вполне приемлем. Мы сами воспользуемся похожим алгоритмом при построении таблицы смещений для метода Бойера-Мура.
Алгоритм Бойера-Мура, разработанный двумя учеными – Бойером (Robert S. Boyer) и Муром (J. Strother Moore), считается наиболее быстрым среди алгоритмов общего назначения, предназначенных для поиска подстроки в строке. Прежде чем рассмотреть работу этого алгоритма, уточним некоторые термины. Под строкой мы будем понимать всю последовательность символов текста. Собственно говоря, речь не обязательно должна идти именно о тексте. В общем случае строка – это любая последовательность байтов. Поиск подстроки в строке осуществляется по заданному образцу, т. е. некоторой последовательности байтов, длина которой не превышает длину строки. Наша задача заключается в том, чтобы определить, содержит ли строка заданный образец.
Простейший вариант алгоритма Бойера-Мура состоит из следующих шагов. На первом шаге мы строим таблицу смещений для искомого образца. Процесс построения таблицы будет описан ниже. Далее мы совмещаем начало строки и образца и начинаем проверку с последнего символа образца. Если последний символ образца и соответствующий ему при наложении символ строки не совпадают, образец сдвигается относительно строки на величину, полученную из таблицы смещений, и снова проводится сравнение, начиная с последнего символа образца. Если же символы совпадают, производится сравнение предпоследнего символа образца и т. д. Если все символы образца совпали с наложенными символами строки, значит мы нашли подстроку и поиск окончен. Если же какой-то (не последний) символ образца не совпадает с соответствующим символом строки, мы сдвигаем образец на один символ вправо и снова начинаем проверку с последнего символа. Весь алгоритм выполняется до тех пор, пока либо не будет найдено вхождение искомого образца, либо не будет достигнут конец строки.
Величина сдвига в случае несовпадения последнего символа вычисляется исходя из следующих соображений: сдвиг образца должен быть минимальным, таким, чтобы не пропустить вхождение образца в строке. Если данный символ строки встречается в образце, мы смещаем образец таким образом, чтобы символ строки совпал с самым правым вхождением этого символа в образце. Если же образец вообще не содержит этого символа, мы сдвигаем образец на величину, равную его длине, так что первый символ образца накладывается на следующий за проверявшимся символ строки.
Величина смещения для каждого символа образца зависит только от порядка символов в образце, поэтому смещения удобно вычислить заранее и хранить в виде одномерного массива, где каждому символу алфавита соответствует смещение относительно последнего символа образца. Поясним все вышесказанное на простом примере. Пусть у нас есть набор символов из пяти символов: a, b, c, d, e и мы хотим найти вхождение образца “abbad” в строке “abeccacbadbabbad”. Следующие схемы иллюстрируют все этапы выполнения алгоритма:
Таблица смещений для образца “abbad”.
Начало поиска. Последний символ образца не совпадает с наложенным символом строки. Сдвигаем образец вправо на 5 позиций:
Три символа образца совпали, а четвертый – нет. Сдвигаем образец вправо на одну позицию:
Последний символ снова не совпадает с символом строки. В соответствии с таблицей смещений сдвигаем образец на 2 позиции:
Еще раз сдвигаем образец на 2 позиции:
Теперь, в соответствии с таблицей, сдвигаем образец на одну позицию, и получаем искомое вхождение образца:
Реализуем указанный алгоритм на языке ObjectPascal. Прежде всего следует определить тип данных «таблица смещений». Для кодовой таблицы, состоящей из 256 символов, определение этого типа будет выглядеть так:
type TBMTable = array[0..255] of Integer;
Далее приводится процедура, вычисляющая таблицу смещений для образца P.
procedure MakeBMTable(var BMT: TBMTable; const P: string);
var
i: Integer;
begin
for i := 0 to 255 do
BMT[i] := Length(P);
for i := Length(P) downto 1 do
if BMT[Byte(P[i])] = Length(P) then
BMT[Byte(P[i])] := Length(P)– i;
end;
Теперь напишем функцию, осуществляющую поиск.
function BMSearch(StartPos: Integer; const S, P: string; const BMT: TBMTable):
Integer;
var
Pos, lp, i: Integer;
begin
lp := Length(P);
Pos := StartPos + lp –1;
while Pos do
if P[lp] <> S[Pos] then
Pos := Pos + BMT[S[Pos]]
else
for i := lp - 1 downto 1 do
if P[i] <> S[Pos – lp + i] then
begin
Inc(Pos);
Break;
end
else if i = 1 then
begin
Result := Pos – lp + 1;
Exit;
end;
Result := 0;
end;
Функция BMSearch возвращает позицию первого символа первого вхождения образца P в строке S. Если последовательность P в S не найдена, функция возвращает 0 (напомню, что в ObjectPascal нумерация символов в строке String начинается с 1). Параметр StartPos позволяет указать позицию в строке S, с которой следует начинать поиск. Это может быть полезно в том случае, если вы захотите найти все вхождения P в S. Для поиска с самого начала строки следует задать StartPos равным 1. Если результат поиска не равен нулю, то для того, чтобы найти следующее вхождение P в S, нужно задать StartPos равным значению «предыдущий результат плюс длина образца».
Более эффективный вариантХотя рассмотренный упрощенный алгоритм вполне пригоден с практической точки зрения (и часто применяется), нельзя не заметить, что результаты сравнений используются недостаточно эффективно. Действительно, на втором шаге, когда у нас совпали три символа, мы, зная, что последовательность “bad” встречается в образце только один раз, могли бы сразу сместить образец на всю его длину, а не на один символ. Теперь мы рассмотрим другой, немного более сложный вариант алгоритма Бойера-Мура, позволяющий учесть результаты предыдущих сравнений в случае частичного совпадения образца и подстроки. Прежде всего изменим принцип построения таблицы смещений. В этом варианте алгоритма таблица – двумерная, каждому символу образца соответствует один столбец таблицы, а каждой букве алфавита – одна строка. В ячейках таблицы содержатся значения смещений, на которые нужно сдвинуть образец, если при проверке данного символа образца обнаружено несовпадение и вместо искомого символа получен символ алфавита, соответствующий некоторой строке в таблице. Например, таблица последовательности “abdab” для нашего пятибуквенного алфавита будет выглядеть следующим образом:
Заполнение таблицы начинается с последнего столбца. Самый правый столбец таблицы фактически соответствует массиву, который используется в упрощенном варианте алгоритма. Следующие столбцы содержат значения сдвига для образца при условии, что предыдущие (при движении справа налево) символы совпали. Проводя поиск при помощи этой таблицы, мы последовательно просматриваем значения ячеек, лежащих на пересечении столбца, соответствующего символу образца и строки, соответствующей символу текста. Просмотр начинается с последнего столбца. Если в каждом столбце выбранная ячейка содержит 0, значит, подстрока найдена. Если значение ячейки отличается от нуля, мы сдвигаем образец на соответствующее значение.
Определенные сложности могут возникнуть при работе с кодировкой Unicode. Очевидно, что таблица, число строк которой равно числу символов двухбайтовой кодировки, будет слишком громоздкой. К счастью, в такой таблице нет необходимости, ведь в случае двухбайтовой кодировки любой образец содержит лишь небольшую часть символов алфавита. Для всех символов, не содержащихся в образце, значения смещения в каждом столбце таблицы будут одинаковыми. Эта особенность позволяет разработать сокращенные варианты таблицы для Unicode. Конечно, Unicode-строку можно рассматривать как последовательность байтов, где каждый Unicode-символ представлен двумя байтами. Однако этот подход менее эффективен и не позволяет воспользоваться теми дополнительными возможностями алгоритма, о которых будет сказано ниже.
Далее приводятся функции и определения, реализующие алгоритм для 256-символьного набора.
type
TIntVect = array[0..255] of Integer;
TBMTable = array[0..0] of TIntVect;
PBMTable = ^TBMTable;
function FindRightmost(const S, P: string; n: Integer): Integer;
var
i, j, lp: Integer;
begin
Result := 0;
lp := Length(P);
if lp > n then
Exit;
for i := n - lp + 1 downto 1 do
for j := 1 to lp do
if P[j] <> S[i + j - 1] then
Break
else if j = lp then
begin
Result := i;
Exit;
end;
end;
procedure MakeBMTable(var BMT: PBMTable; const P: string);
var
i, j, lp, MaxShift, CurShift, SufPos: Integer;
Suffix: string;
begin
lp := Length(P);
GetMem(BMT, SizeOf(TIntVect) * lp);
for i := 0 to 255 do
BMT^[lp - 1][i] := lp;
for i := lp downto 1 do
if BMT^[lp - 1][Byte(P[i])] = lp then
BMT^[lp - 1][Byte(P[i])] := lp - i;
MaxShift := lp;
for i := lp - 1 downto 1 do
begin
SetLength(Suffix, lp - i);
Move(P[i + 1], Suffix[1], lp - i);
if Pos(Suffix, P) = 1 then
MaxShift := i;
for j := 0 to 255 do
begin
CurShift := MaxShift;
SetLength(Suffix, lp - i + 1);
Suffix[1] := Char(j);
Move(P[i + 1], Suffix[2], lp - i);
SufPos := FindRightmost(P, Suffix, lp - 1);
if SufPos <> 0 then
CurShift := i - SufPos;
BMT^[i - 1][j] := CurShift;
end;
BMT^[i - 1][Byte(P[i])] := 0;
end;
end;
function BMSearch(StartPos, lp: Integer; const S: string; BMT: PBMTable):
Integer;
var
Pos, i: Integer;
begin
Pos := StartPos + lp - 1;
while Pos do
for i := lp downto 1 do
if BMT^[i - 1][Byte(S[Pos - lp + i])] <> 0 then
begin
Pos := Pos + BMT^[i - 1][Byte(S[Pos - lp + i])];
Break;
end
else if i = 1 then
begin
Result := Pos - lp + 1;
Exit;
end;
Result := 0;
end;
Служебная функция FindRightmost возвращает самое последнее вхождение образца P среди n первых символов строки S. Формат вызова функции BMSearch отличается от предыдущего. В параметре lp передается длина строки образца, сама же строка не нужна, так как таблица смещений однозначно описывает образец. Следует также учесть, что функция MakeBMTable динамически выделяет память для таблицы смещений, и после окончания использования функции BMSearch эту память необходимо освободить при помощи функции FreeMem. Следующий фрагмент кода иллюстрирует поиск всех вхождений образца P в строке S.
MakeBMTable(BMT, P); PatPos := BMSearch(1, Length(P), S, BMT); while PatPos <> 0 dobegin...PatPos := BMSearch(PatPos + 1, Length(P), S, BMT); end; FreeMem(BMT);
Дополнительным преимуществом данного варианта алгоритма является возможность организовать «регистронезависимый» поиск, т. е. поиск слова вне зависимости от регистра букв. Для этого достаточно в таблице смещений сопоставить одинаковые строки одним и тем же буквам разного регистра. Можно даже ввести поиск по шаблону, содержащему подстановочные символы. Ниже приводятся функции формирования таблицы смещений для шаблонов, в которых символ «?» соответствует любому символу используемого набора.
function WCBeginsWith(const P, S: string): Boolean;
var
i, lp: Integer;
begin
Result := False;
lp := Length(P);
if lp > Length(S) then
Exit;
for i := 1 to lp do
if (P[i] <> S[i]) and (P[i] <> '?') and (S[i] <> '?') then
Exit;
Result := True;
end;
function WCFindRightmost(const S, P: string; l: Integer): Integer;
var
i, j, lp: Integer;
begin
Result := 0;
lp := Length(P);
if lp > l then
Exit;
for i := l - lp + 1 downto 1 do
for j := 1 to lp do
if (P[j] <> S[i + j - 1]) and (P[j] <> '?') and (S[i + j - 1] <> '?') then
Break
else if j = lp then
begin
Result := i;
Exit;
end;
end;
procedure WCMakeBMTable(var BMT: PBMTable; const P: string);
var
i, j, lp, MaxShift, CurShift, SufPos: Integer;
Suffix: string;
begin
lp := Length(P);
GetMem(BMT, SizeOf(TIntVect) * lp);
if P[lp] = '?' then
for i := 0 to 255 do
BMT^[lp - 1][i] := 0
else
begin
for i := 0 to 255 do
BMT^[lp - 1][i] := lp;
for i := lp downto 1 do
if BMT^[lp - 1][Byte(P[i])] = lp then
BMT^[lp - 1][Byte(P[i])] := lp - i;
end;
MaxShift := lp;
for i := lp - 1 downto 1 do
begin
SetLength(Suffix, lp - i);
Move(P[i + 1], Suffix[1], lp - i);
if WCBeginsWith(Suffix, P) then
MaxShift := i;
if P[i] = '?' then
for j := 0 to 255 do
BMT^[i - 1][j] := 0
else
for j := 0 to 255 do
begin
CurShift := MaxShift;
SetLength(Suffix, lp - i + 1);
Suffix[1] := Char(j);
Move(P[i + 1], Suffix[2], lp - i);
SufPos := WCFindRightmost(P, Suffix, lp - 1);
if SufPos < >
0 then
CurShift := i - SufPos;
BMT^[i - 1][j] := CurShift;
end;
BMT^[i - 1][Byte(P[i])] := 0;
end;
end;
Например, если в качестве шаблона функции WCMakeBMTable передать строку «брос?ть», и передать полученную таблицу функции BMSearch, в тексте S будут найдены все вхождения слов «бросать» и «бросить» (а также «забросать», «забросить», «бросаться» и т.п., так как не указаны предваряющий и завершающий пробелы).
В заключение рассмотрим вопрос о том, всегда ли следует применять алгоритм Бойера-Мура, и какой вариант этого алгоритма лучше выбрать. Превосходство алгоритма Бойера-Мура перед методом грубой силы наиболее ощутимо проявляется с увеличением длины образца. Хотя алгоритм Бойера-Мура производит меньше сравнений, чем примитивный алгоритм при длине образца более двух символов, большая сложность этого алгоритма и необходимость заранее вычислять таблицу смещений может свести на нет его преимущества, если поиск проводится в коротком тексте, и длина образца невелика.
Преимущество в быстродействии более сложного варианта алгоритма Бойера-Мура перед более простым вариантом сказывается, только если длина образца велика, и в тексте часто встречаются отдельные последовательности символов, содержащиеся в образце. Главное же достоинство более сложного варианта алгоритма заключается в возможности реализации регистронезависимого поиска и поиска по шаблону.
Поиск текста в текстовом файле.
unit BMSearch;
(* -------------------------------------------------------------------
Поиск строки методом Boyer-Moore.
Это - один из самых быстрых алгоритмов поиска строки.
See a description in:
R. Boyer и S. Moore.
Быстрый алгоритм поиска строки.
Communications of the ACM 20, 1977, страницы 762-772
------------------------------------------------------------------- *)
interface
type
{$IFDEF WINDOWS}
size_t = Word;
{$ELSE}
size_t = LongInt;
{$ENDIF}
type
TTranslationTable = array[char] of char; { таблица перевода }
TSearchBM = class(TObject)
private
FTranslate: TTranslationTable; { таблица перевода }
FJumpTable: array[char] of Byte; { таблица переходов }
FShift_1: integer;
FPattern: pchar;
FPatternLen: size_t;
public
procedure Prepare(Pattern: pchar; PatternLen: size_t; IgnoreCase: Boolean);
procedure PrepareStr(const Pattern: string; IgnoreCase: Boolean);
function Search(Text: pchar; TextLen: size_t): pchar;
function Pos(const S: string): integer;
end;
implementation
uses SysUtils;
(* -------------------------------------------------------------------
Игнорируем регистр таблицы перевода
------------------------------------------------------------------- *)
procedure CreateTranslationTable(var T: TTranslationTable; IgnoreCase: Boolean);
var
c: char;
begin
for c := #0 to #255 do
T[c] := c;
if not IgnoreCase then
exit;
for c := 'a' to 'z' do
T[c] := UpCase(c);
{ Связываем все нижние символы с их эквивалентом верхнего регистра }
T['Б'] := 'A';
T['А'] := 'A';
T['Д'] := 'A';
T['В'] := 'A';
T['б'] := 'A';
T['а'] := 'A';
T['д'] := 'A';
T['в'] := 'A';
T['Й'] := 'E';
T['И'] := 'E';
T['Л'] := 'E';
T['К'] := 'E';
T['й'] := 'E';
T['и'] := 'E';
T['л'] := 'E';
T['к'] := 'E';
T['Н'] := 'I';
T['М'] := 'I';
T['П'] := 'I';
T['О'] := 'I';
T['н'] := 'I';
T['м'] := 'I';
T['п'] := 'I';
T['о'] := 'I';
T['У'] := 'O';
T['Т'] := 'O';
T['Ц'] := 'O';
T['Ф'] := 'O';
T['у'] := 'O';
T['т'] := 'O';
T['ц'] := 'O';
T['ф'] := 'O';
T['Ъ'] := 'U';
T['Щ'] := 'U';
T['Ь'] := 'U';
T['Ы'] := 'U';
T['ъ'] := 'U';
T['щ'] := 'U';
T['ь'] := 'U';
T['ы'] := 'U';
T['с'] := 'С';
end;
(* -------------------------------------------------------------------
Подготовка таблицы переходов
------------------------------------------------------------------- *)
procedure TSearchBM.Prepare(Pattern: pchar; PatternLen: size_t;
IgnoreCase: Boolean);
var
i: integer;
c, lastc: char;
begin
FPattern := Pattern;
FPatternLen := PatternLen;
if FPatternLen < 1 then
FPatternLen := strlen(FPattern);
{ Данный алгоритм базируется на наборе из 256 символов }
if FPatternLen > 256 then
exit;
{ 1. Подготовка таблицы перевода }
CreateTranslationTable(FTranslate, IgnoreCase);
{ 2. Подготовка таблицы переходов }
for c := #0 to #255 do
FJumpTable[c] := FPatternLen;
for i := FPatternLen - 1 downto 0 do
begin
c := FTranslate[FPattern[i]];
if FJumpTable[c] >= FPatternLen - 1 then
FJumpTable[c] := FPatternLen - 1 - i;
end;
FShift_1 := FPatternLen - 1;
lastc := FTranslate[Pattern[FPatternLen - 1]];
for i := FPatternLen - 2 downto 0 do
if FTranslate[FPattern[i]] = lastc then
begin
FShift_1 := FPatternLen - 1 - i;
break;
end;
if FShift_1 = 0 then
FShift_1 := 1;
end;
procedure TSearchBM.PrepareStr(const Pattern: string; IgnoreCase: Boolean);
var
str: pchar;
begin
if Pattern <> '' then
begin
{$IFDEF Windows}
str := @Pattern[1];
{$ELSE}
str := pchar(Pattern);
{$ENDIF}
Prepare(str, Length(Pattern), IgnoreCase);
end;
end;
{ Поиск последнего символа & просмотр справа налево }
function TSearchBM.Search(Text: pchar; TextLen: size_t): pchar;
var
shift, m1, j: integer;
jumps: size_t;
begin
result := nil;
if FPatternLen > 256 then
exit;
if TextLen < 1 then
TextLen := strlen(Text);
m1 := FPatternLen - 1;
shift := 0;
jumps := 0;
{ Поиск последнего символа }
while jumps <= TextLen do
begin
Inc(Text, shift);
shift := FJumpTable[FTranslate[Text^]];
while shift <> 0 do
begin
Inc(jumps, shift);
if jumps > TextLen then
exit;
Inc(Text, shift);
shift := FJumpTable[FTranslate[Text^]];
end;
{ Сравниваем справа налево FPatternLen - 1 символов }
if jumps >= m1 then
begin
j := 0;
while FTranslate[FPattern[m1 - j]] = FTranslate[(Text - j)^] do
begin
Inc(j);
if j = FPatternLen then
begin
result := Text - m1;
exit;
end;
end;
end;
shift := FShift_1;
Inc(jumps, shift);
end;
end;
function TSearchBM.Pos(const S: string): integer;
var
str, p: pchar;
begin
result := 0;
if S <> '' then
begin
{$IFDEF Windows}
str := @S[1];
{$ELSE}
str := pchar(S);
{$ENDIF}
p := Search(str, Length(S));
if p <> nil then
result := 1 + p - str;
end;
end;
end.
|
Как найти директорию TEMP в Windows.
function c_GetTempPath: string; var Buffer: array[0..1023] of Char; begin SetString(Result, Buffer, GetTempPath(Sizeof(Buffer) - 1, Buffer)); end; |
Этот код так же можно использовать для:
GetCurrentDirectory GetSystemDirectory GetWindowsDirectoryКак найти каталог Windows.

| Преподаватель в театральном вузе - студенту-компьютерщику: - Нежность надо изобразить! Нежность! А не эту идиотскую ухмылку! Ну, представьте, что вы смотрите на монитор с OS/2. Хорошо. Вот так лучше. А теперь представьте на мониторе Юникс. Браво! Отлично! Сам Станиславский был бы доволен. А теперь изобразите чувство негодования. Опять не получается. Хорошо. Представьте на мониторе Windows 95.Я просил негодование, а не взрыв бешенства. Ладно. Тогда представьте на мониторе Windows 3.11. Это скепсис, а не негодование. Представьте тогда Windows NT. Мне не нужна ласковая улыбка! Мне, нужно негодование! Ну, давайте вообразим на мониторе Internet Ехр1огег. О! Негодование сыграно отлично. Ну, и наконец, сами изобразите мне какое-нибудь чувство, представив на экране Лексикон. Молодой человек! Не надо блевать в аудитории! |
public
{ Public declarations }
Windir: string;
WindirP: PChar;
Res: Cardinal;
procedure TForm1.Button1Click(Sender: TObject);
begin
WinDirP := StrAlloc(MAX_PATH);
Res := GetWindowsDirectory(WinDirP, MAX_PATH);
if Res > 0 then
WinDir := StrPas(WinDirP);
Label1.Caption := WinDir;
end;
|
Как найти каталог Windows 2.
| Win95 как самолет - тошнит, а выйти некуда! |
{$IFNDEF WIN32}
const MAX_PATH = 144;
{$ENDIF}
...
implementation
...
var
a : array[0..MAX_PATH] of char;
begin
GetWindowsDirectory(a, sizeof(a));
ShowMessage(StrPas(a));
GetSystemDirectory(a, sizeof(a));
ShowMessage(StrPas(a));
|
Как найти каталог Windows 3.
| Продается стиральная машина "Windows-95". Стирает все. |
%WinDir - что бы это значило?
Часто можно видеть что-то похожее на это: "файл располагается в директории %WinDir\file.ext, где %WinDir - системная директория Windows." Так что бы это значило - %WinDir?
Если Вы устанавливали Windows, принимая все по умолчанию, то Windows у Вас установлен в директорию C:\WINDOWS. Однако иногда люди по каким-либо побуждениям устанавливают Windows в другую директорию, например, C:\WIN0000.
Иногда нужно бывает обратиться к какому-либо файлу, который находится как раз в этой %WinDir директории. Можно, конечно, написать C:\WINDOWS, но, тогда с уверенностью можно сказать, что на части компьютеров эта программа не пройдет.
Предлагаю Вам такую процедуру:
procedure TForm1.Button2Click(Sender: TObject);
var
F: TextFile;
St, Res: string;
begin
AssignFile(F,'c:\msdos.sys');
Reset(F);
while not Eof(F) do
begin
ReadLn(F, St);
if Copy(St, 1, 6) = 'WinDir' then
Break;
end;
CloseFile(F);
Res := Copy(St, 8, Length(St) - 7);
Edit1.Text := res;
end;
|
После выполнения этой процедуры в переменную Res записывается значение %WinDir.
Перевести секунды в формат времени.
| В одной старой-старой стране, в старом-старом городе, в старом-старом замке, в старой-старой комнате, сидит, окутанный старой-старой паутиной, старый-старый скелет. Перед ним монитор, а на мониторе надпись: - Windows is now loading. Please wait! |
const SecPerDay = 86400; SecPerHour = 3600; SecPerMinute = 60; function SecondToTime(const Seconds: Cardinal): Double; var ms, ss, mm, hh, dd: Cardinal; begin dd := Seconds div SecPerDay; hh := (Seconds mod SecPerDay) div SecPerHour; mm := ((Seconds mod SecPerDay) mod SecPerHour) div SecPerMinute; ss := ((Seconds mod SecPerDay) mod SecPerHour) mod SecPerMinute; ms := 0; Result := dd + EncodeTime(hh, mm, ss, ms); end; procedure TForm1.Button1Click(Sender: TObject); begin label1.Caption := DateTimeToStr(Date + SecondToTime(86543)); end; |
Преобразование количества секунд в формат TTIME.
Автор: VID WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Преобразование количества секунд в формат TTIME (ЧЧ:ММ:СС).
Преобразование количества секунд в формат TTIME (ЧЧ:ММ:СС).
На выходе функции, получаем TTIME
Зависимости: system, sysutils
Автор: VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright: VID
Дата: 14 июня 2002 г.
***************************************************** }
function SecToTime(Sec: Integer): TTime;
var
H, M, S: INTEGER;
HS, MS, SS: string;
begin
S := Sec;
M := Round(INT(S / 60));
S := S - M * 60; //Seconds
H := Round(INT(M / 60)); //Hours
M := M - H * 60; //Minutes
if H < 10 then
HS := '0' + Inttostr(H)
else
HS := inttostr(H);
if M < 10 then
MS := '0' + Inttostr(M)
else
MS := inttostr(M);
if S < 10 then
SS := '0' + inttostr(S)
else
SS := inttostr(S);
RESULT := StrToTime(HS + ':' + MS + ':' + SS);
end;
Пример использования:
ShowMessage(TimeToStr(SecToTime(50))); //получаем сообщение: "00:00:50 "
Получение второго цвета заголовков форм.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение «второго» цвета заголовков форм
Данная фунция возвращает значение цвета, использующегося для
отображения плавного перехода цветов в заголоках форм Windows 98, ME и 2000
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 21 мая 2002 г.
***************************************************** }
function clGradientActiveCaption: Integer;
var
B: BOOL;
begin
SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, @B, 0);
if B then
Result := GetSysColor(COLOR_GRADIENTACTIVECAPTION)
else
Result := GetSysColor(COLOR_ACTIVECAPTION);
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject); begin self.Color := clGradientActiveCaption; end;
Сворачивание вторичной формы на панель задач.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TForm = class(TForm)
{...}
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
implementation
{...}
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
Params.WndParent := GetDesktopWindow;
end;
Определить, установлен ли Macromedia Flash.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
program SeekFlash;
uses
Forms, Windows, INIFiles, SysUtils, Dialogs, ShellApi;
{$R *.RES}
type
TVersionInfo = record
dwSignature,
dwStrucVersion,
dwFileVersionMS,
dwFileVersionLS,
dwProductVersionMS,
dwProductVersionLS,
dwFileFlagsMask,
dwFileFlags,
dwFileOS,
dwFileType,
dwFileSubtype,
dwFileDateMS,
dwFileDateLS: DWORD;
end;
var
//Reg:TRegistry;
Ini: TIniFile;
Text: array[1..4] of string;
AppPath, IniFile, MySec: string;
SetupFile, RunFile, SetupClass: string;
VersionStr, BrowserStr: string;
{RegSubKey,} OpenBrowser, PlugInName, UseExt: string;
MainVersion: Word;
// FoundOne :Boolean;
PVer, DPchar, POpenBrowser: PChar;
MyPoint: Pointer;
PLen: Cardinal;
version: ^TVersionInfo;
DumD: DWORD;
LWord, HWord: Word;
CheckHandle: Hwnd;
//---------------------------
//Gets the Plugin (file-) Version
//---------------------------
procedure GetVersion(pluginName: string);
begin
DPChar := StrAlloc(255);
DPchar := PChar(PluginName);
PVer := StrAlloc(getFileVersionInfoSize(DPchar, Plen));
getFileVersionInfo(DPChar, 0, 255, PVer);
VerQueryValue(Pver, '\', MyPoint, Plen);
Version := myPoint;
dumD := Version.dwFileVersionMS;
hword := dumD shr 16;
lword := dumD and 255;
MainVersion := hword;
VersionStr := IntToStr(Hword) + '.' + IntToStr(LWord);
dumD := Version.dwFileVersionLs;
hword := dumD shr 16;
lword := dumD and 255;
versionStr := versionStr + '.' + IntToStr(Hword) + '.' + IntToStr(lWord);
end;
begin
appPath := extractFileDir(Application.exeName);
// runFile:= 'test.htm';
//runFile must be a html File to determin wich browser(NC or IE) is used
POpenBrowser := StrAlloc(255);
FindExecutable(PChar(extractFileName(runFile)),
PChar(extractFileDir(runFile)), POpenBrowser);
OpenBrowser := POpenBrowser;
if not Fileexists(openBrowser) then
begin
MessageDlg(Text[4], mtInformation, [mbOK], 0);
halt;
end;
//set the FileLocations for Netscape or IE
if Pos('NETSCAPE.EXE', uppercase(trim(OpenBrowser))) <> 0 then
begin //found Netscape
BrowserStr := 'Netscape Comunicator';
PlugInName := Copy(OpenBrowser, 1, Pos('NETSCAPE.EXE',
uppercase(trim(OpenBrowser))) - 1);
PluginName := PlugInName + 'Plugins\NPSWF32.dll';
end
else
//found IEExplorer
begin
BrowserStr := 'Internet Explorer';
DPChar := StrAlloc(255);
GetSystemDirectory(DPChar, 255);
PluginName := DPChar + '\Macromed\Flash\swflash.ocx';
DPChar := nil;
end;
GetVersion(pluginName);
//returned Version Number, may be checked
while mainVersion < 4 do
begin
if messagedlg(Text[2], mtInformation, [mbYes, mbNo], 0) = 6 then
begin
//installFlash;
end
// if message
else
halt;
getVersion(pluginName);
end; // while mainVer
end.
Как захватить весь вывод в консоли.
unit consoleoutput;
interface
uses
Controls, Windows, SysUtils, Forms;
function GetDosOutput(const CommandLine: string): string;
implementation
function GetDosOutput(const CommandLine: string): string;
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of Char;
BytesRead: Cardinal;
WorkDir, Line: string;
begin
Application.ProcessMessages;
with SA do
begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
// созда¸м пайп для перенаправления стандартного вывода
CreatePipe(StdOutPipeRead, // дескриптор чтения
StdOutPipeWrite, // дескриптор записи
@SA, // аттрибуты безопасности
0 // количество байт принятых для пайпа - 0 по умолчанию
);
try
// Созда¸м дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
// а так же проверяем, чтобы он не показывался на экране.
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
// Запускаем компилятор из командной строки
WorkDir := ExtractFilePath(CommandLine);
WasOK := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
// Теперь, когда дескриптор получен, для безопасности закрываем запись.
// Нам не нужно, чтобы произошло случайное чтение или запись.
CloseHandle(StdOutPipeWrite);
// если процесс может быть создан, то дескриптор, это его вывод
if not WasOK then
raise Exception.Create('Could not execute command line!')
else
try
// получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
Line := '';
repeat
// читаем блок символов (могут содержать возвраты каретки и переводы строки)
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
// есть ли что-нибудь ещ¸ для чтения?
if BytesRead > 0 then
begin
// завершаем буфер PChar-ом
Buffer[BytesRead] := #0;
// добавляем буфер в общий вывод
Line := Line + Buffer;
end;
until not WasOK or (BytesRead = 0);
// жд¸м, пока завершится консольное приложение
WaitForSingleObject(PI.hProcess, INFINITE);
finally
// Закрываем все оставшиеся дескрипторы
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
finally
result := Line;
CloseHandle(StdOutPipeRead);
end;
end;
end.
|
Как скопировать выбранные в DBGrid записи в клипборд.
Автор: Тенцер А.Л.
const
FIELD_DELIMITER = #9;
RECORD_DELIMITER = #10;
procedure CopyDBGridToClipboard( Grid : TDBGrid );
var
BM : String;
S : String;
S1: String;
I : Integer;
begin
with Grid do begin
if Assigned( DataSource ) and
Assigned( DataSource.DataSet ) and
DataSource.DataSet.Active then
with DataSource.DataSet do begin
S := '';
DisableControls;
BM := BookMark;
for I := 0 to Pred( Columns.Count ) do begin
if Assigned(Columns.Items[I].Field) then
S := S + Columns.Items[I].Title.Caption + FIELD_DELIMITER;
end;
S[ Length( S ) ] := RECORD_DELIMITER;
First;
while not Eof do begin
S1 := '';
for I := 0 to Pred( Columns.Count ) do begin
if Assigned(Columns.Items[I].Field) then
S1 := S1 + FieldByName( Columns[I].FieldName ).AsString +
FIELD_DELIMITER;
end;
S1[ Length( S1 ) ] := RECORD_DELIMITER;
S := S + S1;
Next;
end;
BookMark := BM;
EnableControls;
// Clipboard.SetTextBuf( PChar( S ) );
SendToClipboard( S );
end;
end;
end;
|
Как выделить кнопку в DBNavigator программно.
Автор: Vit WEB-сайт: http://forum.vingrad.ru
type TFake = class(TDBNavigator); procedure TForm1.Button1Click(Sender: TObject); begin TFake(DBNavigator1).buttons[nbNext].Perform(WM_RBUTTONDOWN, 0, 0); TFake(DBNavigator1).buttons[nbNext].Perform(WM_RBUTTONUP, 0, 0); end;
Каким образом выбирать размер шрифта.
Автор: Nomadic Каким образом выбирать размер шрифта, т.к. все мои страдания по выборупараметров шрифта в CreateFont() никак не отражались на его размере. Все что я придумал, это юзать glScale(), но в этом случае получаем плохое качество (по сравнению с той-же Вордой) при малом размере символовВот часть работающего примера на Си (переведенного мною на Паскаль (АА)).
procedure GLSetupRC(pData: Pointer)
//void GLSetupRC(void *pData)
//{
var
// HDC hDC;
hDC: HDC;
// HFONT hFont;
hFont: HFONT;
// GLYPHMETRICSFLOAT agmf[128];
agmf: array[0..127] of GLYPHMETRICSFLOAT;
// LOGFONT logfont;
logfont: LOGFONT;
begin
logfont.lfHeight := -10;
logfont.lfWidth := 0;
logfont.lfEscapement := 0;
logfont.lfOrientation := 0;
logfont.lfWeight := FW_BOLD;
logfont.lfItalic := FALSE;
logfont.lfUnderline := FALSE;
logfont.lfStrikeOut := FALSE;
logfont.lfCharSet := ANSI_CHARSET;
logfont.lfOutPrecision := OUT_DEFAULT_PRECIS;
logfont.lfClipPrecision := CLIP_DEFAULT_PRECIS;
logfont.lfQuality := DEFAULT_QUALITY;
logfont.lfPitchAndFamily := DEFAULT_PITCH;
//strcpy(logfont.lfFaceName,"Arial");
// strcpy(logfont.lfFaceName,"Decor");
StrPCopy(logfont.lfFaceName, 'Decor');
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST); // Hidden surface removal
glFrontFace(GL_CCW); // Counter clock-wise polygons face out
glEnable(GL_CULL_FACE); // Do not calculate insides
glShadeModel(GL_SMOOTH); // Smooth shading
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glEnable(GL_COLOR_MATERIAL);
glClearColor(0.0, 0.0, 0.0, 1.0);
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glEnable(GL_LIGHT0);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glMateriali(GL_FRONT, GL_SHININESS, 100);
// Blue 3D Text
glRGB(0, 0, 255);
// Select the font into the DC
hDC := (HDC)pData;
// hFont = CreateFontIndirect(&logfont);
hFont := CreateFontIndirect(Addr(logfont));
SelectObject(hDC, hFont);
//create display lists for glyphs 0 through 255 with 0.3 extrusion
// and default deviation. The display list numbering starts at 1000
// (it could be any number).
// if(!wglUseFontOutlines(hDC, 0, 128, 1000, 0., 0.3,
// WGL_FONT_POLYGONS, agmf))
if not wglUseFontOutlines(hDC, 0, 128, 1000, 0., 0.3,
//> ``` - это тебе поможет
//> Выводить текст можно в любым масштабе
WGL_FONT_POLYGONS, agmf) then
Windows.MessageBox(nil, 'Could not create Font Outlines',
'Error', MB_OK or MB_ICONSTOP);
// Delete the font now that we are done
DeleteObject(hFont);
//}
end;
// void GLRenderScene(void *pData)
procedure GLRenderScene(pData: Pointer);
begin
(* ... *)
// Draw 3D text
glListBase(1000);
glPushMatrix();
// Set up transformation to draw the string.
glTranslatef(-35.0, 0.0, -5.0);
glScalef(60.0, 60.0, 60.0);
glCallLists(3, GL_UNSIGNED_BYTE, 'Decor');
glPopMatrix(); // Clear the window with current clearing color
(* ... *)
end;
|
Как выделить в RichEdit 4 строки.
| Год назад. Раздается в нашей конторе звонок. Поднимает трубку человек, там кого-то просят, кто в другой комнате ... человек оглядывает присутствующих "Как в соседню комнату перевести? Какай там адрес? www а дальше что?" |
Строки нумеруются с 0!
with richedit1 do begin selstart:=FindText(lines[3],0,length(text), [stWholeWord]); sellength:=length(lines[3]); selattributes.color:=clBlue; end; |
Работает, если строка уникальная, иначе можно - вычислять начало.
sstart := 0; for i := 0 to numstr - 1 do sstart := sstart + length(lines[i]) + 2; // numstr=3 selstart := sstart; // для 4 строки |
Выделить строку в Memo.
| Возвращаюсь вечером с работы домой, устал естессно, причём день выдался сплошь да рядом связаный с аккаунтами... подхожу к двери подъезда, вставляю ключ, дёргаю дверь... дверь не открывается - первая мысль "БЛИН , ОПЯТЬ ПАРОЛЬ СМЕНИЛИ"...(на самом деле ключ не до конца вставил) |
procedure TfrmMain.Memo1Click(Sender: TObject);
var
Line: Integer;
begin
with (Sender as TMemo) do
begin
Line := Perform(EM_LINEFROMCHAR, SelStart, 0);
SelStart := Perform(EM_LINEINDEX, Line, 0);
SelLength := Length(Lines[Line]);
end;
end;
|
Выделение меню.

| - Как размножается Microsoft? - Делением. |
Function SelectMenu(winname,item,subitem:string):boolean; // winname - имя окна, item - имя пункта меню, subitem - имя подпункта var winhandle,menuhandle,submenuhandle,i,j,res:integer; itemname,subitemname:pchar; begin res:=-1; winhandle:=FindWindow(nil,pchar(winname)); menuhandle:=getmenu(winhandle); getmem(itemname,255); getmem(subitemname,255); for i:=0 to getmenuitemcount(menuhandle)-1 do begin getmenustring(menuhandle,i,itemname,255,MF_BYPOSITION); if string(itemname)=item then begin submenuhandle:=getsubmenu(menuhandle,i); for j:=0 to getmenuitemcount(submenuhandle)-1 do begin getmenustring(submenuhandle,j,subitemname,255,MF_BYPOSITION); if string(subitemname)=subitem then res:=SendMessage(winhandle,WM_COMMAND,makelong(getmenuitemid(submenuhandle,j),0 ),0); end; end; end; freemem(itemname); freemem(subitemname); if res=0 then result:=true else result:=false; end; |
Как реализовать выделение резиновым прямоугольником.

| Автор: Даниил Карапетян WEB сайт: http://program.dax.ru |
| - Как три программиста могут организовать бизнес? - Один пишет вирусы, а другой антивирусы. - А третий? - Операционные системы, под которыми это все работает. |
В качестве объекта я взял Label, меняющий цвет в зависимости от того, выделен он или нет. При нажатии мышью на форме в FirstPoint кладутся координата курсора. При дальнейшем движении мыши координаты прямоугольника будут высчитываться по FirstPoint и текущим координатам курсора. Причем, чтобы программа нормально отрабатывала случай, когда высота или ширина прямоугольника отрицательная (это произойдет, если увести мышь левее или выше начальной точки), создана процедура NormalRect. NormalRect устанавливает ко Скачать необходимые для компиляции файлы проекта можно на program.dax.ru/subscribe/. http://program.dax.ru/subscribe/index.htm.
uses stdctrls;
var
Selecting: boolean = false;
FirstPoint: TPoint;
sel: TRect;
procedure DrawRect;
begin
with Form1.Canvas do begin
Pen.Style := psDot;
Pen.Color := clGray;
Pen.Mode := pmXor;
Brush.Style := bsClear;
Rectangle(sel.Left, sel.Top, sel.Right, sel.Bottom);
end;
end;
procedure NormalRect(p1, p2: TPoint);
begin
if p1.x < p2.x then begin
sel.Left := p1.x;
sel.Right := p2.x;
end else begin
sel.Left := p2.x;
sel.Right := p1.x;
end;
if p1.y < p2.y then begin
sel.Top := p1.y;
sel.Bottom := p2.y;
end else begin
sel.Top := p2.y;
sel.Bottom := p1.y;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
randomize;
for i := 1 to random(5) + 5 do begin
with TLabel.Create(Form1) do begin
Caption := 'Label' + IntToStr(i);
Left := random(Form1.ClientWidth - Width);
Top := random(Form1.ClientHeight - Height);
Visible := true;
Parent := Form1;
end;
end;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if selecting or (Button < > mbLeft) then Exit;
SetCapture(Form1.Handle);
Selecting := true;
FirstPoint := Point(X, Y);
sel := Bounds(X, Y, 0, 0);
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure SelectLebel(lb: TLabel; r: TRect);
var
select: boolean;
res: TRect;
begin
select := IntersectRect(res, lb.BoundsRect, r);
if select and (lb.Color = clNavy) then Exit;
if select then begin
lb.Color := clNavy;
lb.Font.Color := clWhite;
end else begin
lb.Color := clBtnFace;
lb.Font.Color := clBlack;
end;
end;
var
i: integer;
begin
if not Selecting then Exit;
DrawRect;
NormalRect(FirstPoint, Point(X, Y));
for i := 0 to Form1.ComponentCount - 1 do
if (Form1.Components[i] is TLabel) then
SelectLebel(Form1.Components[i] as TLabel, sel);
Application.ProcessMessages;
DrawRect;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (not Selecting) or (Button < > mbLeft) then Exit;
NormalRect(FirstPoint, Point(X, Y));
DrawRect;
ReleaseCapture;
Selecting := false;
end;
{
Программа была протестирована:
Дмитрий Бажин mailto:bsdriver@mail.ru
Юрий Кравченко mailto:ykravchenko@ukr.net
Все советы и замечания, пожалуйста,
присылайте на subscribe@program.dax.ru
Всего доброго,
Даниил Карапетян.
}
|
Что из себя представляет Self.
Автор: Vit WEB-сайт: http://forum.vingrad.ru Self - это явное задание экземпляра класса в его методе.Например для твоей формы это указание на саму форму:
procedure TForm1.Button1Click(Sender: TObject); begin showmessage(self.classname + #13#10 + self.name); end;
Если например это MDI форма то это будет указатель именно на тот экземпляр для которого выполняется этот код. На практике Self обычно применяется при написании своих классов, когда ты пишешь класс или компонент, то у тебя нет переменной с экземпляром этого компонента, следовательно чтобы обратится к экземпляру (который появится только в коде конечного пользователя, который будет использовать компонент) класса нужна переменная - вот она и берётся за self.
Что из себя представляет Self 2.
Автор: Fantasist WEB-сайт: http://forum.vingrad.ruЧтобы понять, что такое self надо понять что такое метод класса. Метод класса - это просто функция(процедура) который имеет дополнительный неявный параметр - указатель на экземпляр класса. То есть:
TMy = class x: integer; procedure Proc(val: integer); end; procedure TMy.Proc(val: integer); begin x := val; end;
После компиляции это будет практически то же самое, что:
procedure Proc(self: TMy; val: integer); begin self.x := val; end;
То есть на самом деле в методе Proc обращаясь к x мы на самом деле обращаемся к self.x, просто переменная self опускается. В скомпилированном коде нет такого понятия как классы - есть только код и память. Все методы классов превращаются в обыкновенные функции, в которым качестве первого параметра передается указатель на область памяти где лежит созданный пользователем экземпляр класса, который они и используют для чтения или записи(а так же для вызова) того, что мы называем членами класса.
var m1, m2: TMy; begin ... m1.Proc(4); // Proc(m1,4) m2.Proc(4); // Proc(m2,4) end;
Создание самоуничтожающегося объекта.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
ISelfDestroy = interface;
//forget about GUID, if you are not using COM
TSelfDestroy = class(TInterfacedObject, ISelfDestroy)
private
FObject: TObject;
public
constructor Create(AObject: TObject);
destructor Destroy; override;
end;
implementation
constructor TSelfDestroy.Create(AObject: TObject);
begin
FObject := AObject;
end;
destructor TSelfDestroy.Destroy;
begin
FreeAndNil(FObject);
inherited;
end;
// So when you use, just do like this...
procedure TForm1.Button1Click(Sender: TObject);
var
MyObject: TMyObject;
SelfDestroy: TSelfDestroy;
begin
MyObject := TMyObject.Create;
SelfDestroy := TSelfDestroy.Create(MyObject);
// The MyObject will free automatically as soon as TSelfDestroy
// goes out of scope.
// Carry on your code here...
end;
Диалог для выбора компьютера.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
ShlObj, ActiveX;
function BrowseComputer(DialogTitle: string; var CompName: string;
bNewStyle: Boolean): Boolean;
// bNewStyle: If True, this code will try to use the "new"
// BrowseForFolders UI on Windows 2000/XP
const
BIF_USENEWUI = 28;
var
BrowseInfo: TBrowseInfo;
ItemIDList: PItemIDList;
ComputerName: array[0..MAX_PATH] of Char;
Title: string;
WindowList: Pointer;
ShellMalloc: IMalloc;
begin
if Failed(SHGetSpecialFolderLocation(Application.Handle, CSIDL_NETWORK, ItemIDList)) then
raise Exception.Create('Unable open browse computer dialog');
try
FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
BrowseInfo.hwndOwner := Application.Handle;
BrowseInfo.pidlRoot := ItemIDList;
BrowseInfo.pszDisplayName := ComputerName;
Title := DialogTitle;
BrowseInfo.lpszTitle := PChar(Pointer(Title));
if bNewStyle then
BrowseInfo.ulFlags := BIF_BROWSEFORCOMPUTER or BIF_USENEWUI
else
BrowseInfo.ulFlags := BIF_BROWSEFORCOMPUTER;
WindowList := DisableTaskWindows(0);
try
Result := SHBrowseForFolder(BrowseInfo) <> nil;
finally
EnableTaskWindows(WindowList);
end;
if Result then CompName := ComputerName;
finally
if Succeeded(SHGetMalloc(ShellMalloc)) then
ShellMalloc.Free(ItemIDList);
end;
end;
// Example
procedure TForm1.Button1Click(Sender: TObject);
var
Computer: string;
begin
BrowseComputer('...', Computer, True);
label1.Caption := Computer;
end;
Показать диалог выбора директории с кнопкой для создания новой.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{....}
uses
ShlObj, ActiveX;
{....}
{
This code shows the SelectDirectory dialog with additional expansions:
- an edit box, where the user can type the path name,
- also files can appear in the list,
- a button to create new directories.
Dieser Code zeigt den SelectDirectory-Dialog mit zusatzlichen Erweiterungen:
- eine Edit-Box, wo der Benutzer den Verzeichnisnamen eingeben kann,
- auch Dateien konnen in der Liste angezeigt werden,
- eine Schaltflache zum Erstellen neuer Verzeichnisse.
}
function AdvSelectDirectory(const Caption: string; const Root: WideString;
var Directory: string; EditBox: Boolean = False; ShowFiles: Boolean = False;
AllowCreateDirs: Boolean = True): Boolean;
// callback function that is called when the dialog has been initialized
//or a new directory has been selected
// Callback-Funktion, die aufgerufen wird, wenn der Dialog initialisiert oder
//ein neues Verzeichnis selektiert wurde
function SelectDirCB(Wnd: HWND; uMsg: UINT; lParam, lpData: lParam): Integer;
stdcall;
var
PathName: array[0..MAX_PATH] of Char;
begin
case uMsg of
BFFM_INITIALIZED: SendMessage(Wnd, BFFM_SETSELECTION, Ord(True), Integer(lpData));
// include the following comment into your code if you want to react on the
//event that is called when a new directory has been selected
// binde den folgenden Kommentar in deinen Code ein, wenn du auf das Ereignis
//reagieren willst, das aufgerufen wird, wenn ein neues Verzeichnis selektiert wurde
{BFFM_SELCHANGED:
begin
SHGetPathFromIDList(PItemIDList(lParam), @PathName);
// the directory "PathName" has been selected
// das Verzeichnis "PathName" wurde selektiert
end;}
end;
Result := 0;
end;
var
WindowList: Pointer;
BrowseInfo: TBrowseInfo;
Buffer: PChar;
RootItemIDList, ItemIDList: PItemIDList;
ShellMalloc: IMalloc;
IDesktopFolder: IShellFolder;
Eaten, Flags: LongWord;
const
// necessary for some of the additional expansions
// notwendig fur einige der zusatzlichen Erweiterungen
BIF_USENEWUI = $0040;
BIF_NOCREATEDIRS = $0200;
begin
Result := False;
if not DirectoryExists(Directory) then
Directory := '';
FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
begin
Buffer := ShellMalloc.Alloc(MAX_PATH);
try
RootItemIDList := nil;
if Root <> '' then
begin
SHGetDesktopFolder(IDesktopFolder);
IDesktopFolder.ParseDisplayName(Application.Handle, nil,
POleStr(Root), Eaten, RootItemIDList, Flags);
end;
OleInitialize(nil);
with BrowseInfo do
begin
hwndOwner := Application.Handle;
pidlRoot := RootItemIDList;
pszDisplayName := Buffer;
lpszTitle := PChar(Caption);
// defines how the dialog will appear:
// legt fest, wie der Dialog erscheint:
ulFlags := BIF_RETURNONLYFSDIRS or BIF_USENEWUI or
BIF_EDITBOX * Ord(EditBox) or BIF_BROWSEINCLUDEFILES * Ord(ShowFiles) or
BIF_NOCREATEDIRS * Ord(not AllowCreateDirs);
lpfn := @SelectDirCB;
if Directory <> '' then
lParam := Integer(PChar(Directory));
end;
WindowList := DisableTaskWindows(0);
try
ItemIDList := ShBrowseForFolder(BrowseInfo);
finally
EnableTaskWindows(WindowList);
end;
Result := ItemIDList <> nil;
if Result then
begin
ShGetPathFromIDList(ItemIDList, Buffer);
ShellMalloc.Free(ItemIDList);
Directory := Buffer;
end;
finally
ShellMalloc.Free(Buffer);
end;
end;
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
var
dir: string;
begin
AdvSelectDirectory('Caption', 'c:\', dir, False, False, True);
Label1.Caption := dir;
end;
Почти полный аналог метода SendKeys из VB.
Автор: Ken Henderson.
(*
SendKeys routine for 32-bit Delphi.
Written by Ken Henderson
Copyright (c) 1995 Ken Henderson email:khen@compuserve.com
This unit includes two routines that simulate popular Visual Basic
routines: Sendkeys and AppActivate. SendKeys takes a PChar
as its first parameter and a boolean as its second, like so:
SendKeys('KeyString', Wait);
where KeyString is a string of key names and modifiers that you want
to send to the current input focus and Wait is a boolean variable or value
that indicates whether SendKeys should wait for each key message to be
processed before proceeding. See the table below for more information.
AppActivate also takes a PChar as its only parameter, like so:
AppActivate('WindowName');
where WindowName is the name of the window that you want to make the
current input focus.
SendKeys supports the Visual Basic SendKeys syntax, as documented below.
Supported modifiers:
+ = Shift
^ = Control
% = Alt
Surround sequences of characters or key names with parentheses in order to
modify them as a group. For example, '+abc' shifts only 'a', while '+(abc)' shifts
all three characters.
Supported special characters
~ = Enter
( = begin modifier group (see above)
) = end modifier group (see above)
{ = begin key name text (see below)
} = end key name text (see below)
Supported characters:
Any character that can be typed is supported. Surround the modifier keys
listed above with braces in order to send as normal text.
Supported key names (surround these with braces):
BKSP, BS, BACKSPACE
BREAK
CAPSLOCK
CLEAR
DEL
DELETE
DOWN
END
ENTER
ESC
ESCAPE
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
F16
HELP
HOME
INS
LEFT
NUMLOCK
PGDN
PGUP
PRTSC
RIGHT
SCROLLLOCK
TAB
UP
Follow the keyname with a space and a number to send the specified key a
given number of times (e.g., {left 6}).
*)
unit sndkey32;
interface
Uses SysUtils, Windows, Messages;
Function SendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;
function AppActivate(WindowName : PChar) : boolean;
{Buffer for working with PChar's}
const
WorkBufLen = 40;
var
WorkBuf : array[0..WorkBufLen] of Char;
implementation
type
THKeys = array[0..pred(MaxLongInt)] of byte;
var
AllocationSize : integer;
(*
Converts a string of characters and key names to keyboard events and
passes them to Windows.
Example syntax:
SendKeys('abc123{left}{left}{left}def{end}456{left 6}ghi{end}789', True);
*)
Function SendKeys(SendKeysString : PChar; Wait : Boolean) : Boolean;
type
WBytes = array[0..pred(SizeOf(Word))] of Byte;
TSendKey = record
Name : ShortString;
VKey : Byte;
end;
const
{Array of keys that SendKeys recognizes.
If you add to this list, you must be sure to keep it sorted alphabetically
by Name because a binary search routine is used to scan it.}
MaxSendKeyRecs = 41;
SendKeyRecs : array[1..MaxSendKeyRecs] of TSendKey =
(
(Name:'BKSP'; VKey:VK_BACK),
(Name:'BS'; VKey:VK_BACK),
(Name:'BACKSPACE'; VKey:VK_BACK),
(Name:'BREAK'; VKey:VK_CANCEL),
(Name:'CAPSLOCK'; VKey:VK_CAPITAL),
(Name:'CLEAR'; VKey:VK_CLEAR),
(Name:'DEL'; VKey:VK_DELETE),
(Name:'DELETE'; VKey:VK_DELETE),
(Name:'DOWN'; VKey:VK_DOWN),
(Name:'END'; VKey:VK_END),
(Name:'ENTER'; VKey:VK_RETURN),
(Name:'ESC'; VKey:VK_ESCAPE),
(Name:'ESCAPE'; VKey:VK_ESCAPE),
(Name:'F1'; VKey:VK_F1),
(Name:'F10'; VKey:VK_F10),
(Name:'F11'; VKey:VK_F11),
(Name:'F12'; VKey:VK_F12),
(Name:'F13'; VKey:VK_F13),
(Name:'F14'; VKey:VK_F14),
(Name:'F15'; VKey:VK_F15),
(Name:'F16'; VKey:VK_F16),
(Name:'F2'; VKey:VK_F2),
(Name:'F3'; VKey:VK_F3),
(Name:'F4'; VKey:VK_F4),
(Name:'F5'; VKey:VK_F5),
(Name:'F6'; VKey:VK_F6),
(Name:'F7'; VKey:VK_F7),
(Name:'F8'; VKey:VK_F8),
(Name:'F9'; VKey:VK_F9),
(Name:'HELP'; VKey:VK_HELP),
(Name:'HOME'; VKey:VK_HOME),
(Name:'INS'; VKey:VK_INSERT),
(Name:'LEFT'; VKey:VK_LEFT),
(Name:'NUMLOCK'; VKey:VK_NUMLOCK),
(Name:'PGDN'; VKey:VK_NEXT),
(Name:'PGUP'; VKey:VK_PRIOR),
(Name:'PRTSC'; VKey:VK_PRINT),
(Name:'RIGHT'; VKey:VK_RIGHT),
(Name:'SCROLLLOCK'; VKey:VK_SCROLL),
(Name:'TAB'; VKey:VK_TAB),
(Name:'UP'; VKey:VK_UP)
);
{Extra VK constants missing from Delphi's Windows API interface}
VK_NULL=0;
VK_SemiColon=186;
VK_Equal=187;
VK_Comma=188;
VK_Minus=189;
VK_Period=190;
VK_Slash=191;
VK_BackQuote=192;
VK_LeftBracket=219;
VK_BackSlash=220;
VK_RightBracket=221;
VK_Quote=222;
VK_Last=VK_Quote;
ExtendedVKeys : set of byte =
[VK_Up,
VK_Down,
VK_Left,
VK_Right,
VK_Home,
VK_End,
VK_Prior, {PgUp}
VK_Next, {PgDn}
VK_Insert,
VK_Delete];
const
INVALIDKEY = $FFFF {Unsigned -1};
VKKEYSCANSHIFTON = $01;
VKKEYSCANCTRLON = $02;
VKKEYSCANALTON = $04;
UNITNAME = 'SendKeys';
var
UsingParens, ShiftDown, ControlDown, AltDown, FoundClose : Boolean;
PosSpace : Byte;
I, L : Integer;
NumTimes, MKey : Word;
KeyString : String[20];
procedure DisplayMessage(Message : PChar);
begin
MessageBox(0,Message,UNITNAME,0);
end;
function BitSet(BitTable, BitMask : Byte) : Boolean;
begin
Result:=ByteBool(BitTable and BitMask);
end;
procedure SetBit(var BitTable : Byte; BitMask : Byte);
begin
BitTable:=BitTable or Bitmask;
end;
procedure KeyboardEvent(VKey, ScanCode : Byte; Flags : Longint);
var
KeyboardMsg : TMsg;
begin
keybd_event(VKey, ScanCode, Flags,0);
If (Wait) then While (PeekMessage(KeyboardMsg,0,WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do begin
TranslateMessage(KeyboardMsg);
DispatchMessage(KeyboardMsg);
end;
end;
procedure SendKeyDown(VKey: Byte; NumTimes : Word; GenUpMsg : Boolean);
var
Cnt : Word;
ScanCode : Byte;
NumState : Boolean;
KeyBoardState : TKeyboardState;
begin
If (VKey=VK_NUMLOCK) then begin
NumState:=ByteBool(GetKeyState(VK_NUMLOCK) and 1);
GetKeyBoardState(KeyBoardState);
If NumState then KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] and not 1)
else KeyBoardState[VK_NUMLOCK]:=(KeyBoardState[VK_NUMLOCK] or 1);
SetKeyBoardState(KeyBoardState);
exit;
end;
ScanCode:=Lo(MapVirtualKey(VKey,0));
For Cnt:=1 to NumTimes do
If (VKey in ExtendedVKeys)then begin
KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
If (GenUpMsg) then
KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
end else begin
KeyboardEvent(VKey, ScanCode, 0);
If (GenUpMsg) then KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end;
end;
procedure SendKeyUp(VKey: Byte);
var
ScanCode : Byte;
begin
ScanCode:=Lo(MapVirtualKey(VKey,0));
If (VKey in ExtendedVKeys)then
KeyboardEvent(VKey, Sca
else KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end;
procedure SendKey(MKey: Word; NumTimes : Word; GenDownMsg : Boolean);
begin
If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyDown(VK_SHIFT,1,False);
If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyDown(VK_CONTROL,1,False);
If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyDown(VK_MENU,1,False);
SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
If (BitSet(Hi(MKey),VKKEYSCANSHIFTON)) then SendKeyUp(VK_SHIFT);
If (BitSet(Hi(MKey),VKKEYSCANCTRLON)) then SendKeyUp(VK_CONTROL);
If (BitSet(Hi(MKey),VKKEYSCANALTON)) then SendKeyUp(VK_MENU);
end;
{Implements a simple binary search to locate special key name strings}
function StringToVKey(KeyString : ShortString) : Word;
var
Found, Collided : Boolean;
Bottom, Top, Middle : Byte;
begin
Result:=INVALIDKEY;
Bottom:=1;
Top:=MaxSendKeyRecs;
Found:=false;
Middle:=(Bottom+Top) div 2;
Repeat
Collided:=((Bottom=Middle) or (Top=Middle));
If (KeyString=SendKeyRecs[Middle].Name) then begin
Found:=True;
Result:=SendKeyRecs[Middle].VKey;
end else begin
If (KeyString>SendKeyRecs[Middle].Name) then Bottom:=Middle
else Top:=Middle;
Middle:=(Succ(Bottom+Top)) div 2;
end;
Until (Found or Collided);
If (Result=INVALIDKEY) then DisplayMessage('Invalid Key Name');
end;
procedure PopUpShiftKeys;
begin
If (not UsingParens) then begin
If ShiftDown then SendKeyUp(VK_SHIFT);
If ControlDown then SendKeyUp(VK_CONTROL);
If AltDown then SendKeyUp(VK_MENU);
ShiftDown:=false;
ControlDown:=false;
AltDown:=false;
end;
end;
begin
AllocationSize:=MaxInt;
Result:=false;
UsingParens:=false;
ShiftDown:=false;
ControlDown:=false;
AltDown:=false;
I:=0;
L:=StrLen(SendKeysString);
If (L>AllocationSize) then L:=AllocationSize;
If (L=0) then Exit;
while (Ibegin
case SendKeysString[I] of
'(' : begin
UsingParens:=True;
Inc(I);
end;
')' : begin
UsingParens:=False;
PopUpShiftKeys;
Inc(I);
end;
'%' : begin
AltDown:=True;
SendKeyDown(VK_MENU,1,False);
Inc(I);
end;
'+' : begin
ShiftDown:=True;
SendKeyDown(VK_SHIFT,1,False);
Inc(I);
end;
'^' : begin
ControlDown:=True;
SendKeyDown(VK_CONTROL,1,False);
Inc(I);
end;
'{' : begin
NumTimes:=1;
If (SendKeysString[Succ(I)]='{') then begin
MKey:=VK_LEFTBRACKET;
SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
SendKey(MKey,1,True);
PopUpShiftKeys;
Inc(I,3);
Continue;
end;
KeyString:='';
FoundClose:=False;
while (I<=L) do begin
Inc(I);
If (SendKeysString[I]='}') then begin
FoundClose:=True;
Inc(I);
Break;
end;
KeyString:=KeyString+Upcase(SendKeysString[I]);
end;
If (Not FoundClose) then begin
DisplayMessage('No Close');
Exit;
end;
If (SendKeysString[I]='}') then begin
MKey:=VK_RIGHTBRACKET;
SetBit(Wbytes(MKey)[1],VKKEYSCANSHIFTON);
SendKey(MKey,1,True);
PopUpShiftKeys;
Inc(I);
Continue;
end;
PosSpace:=Pos(' ',KeyString);
If (PosSpace<>0) then begin
NumTimes:=StrToInt(Copy(KeyString,Succ(PosSpace),Length(KeyString)-PosSpace));
KeyString:=Copy(KeyString,1,Pred(PosSpace));
end;
If (Length(KeyString)=1) then MKey:=vkKeyScan(KeyString[1])
else MKey:=StringToVKey(KeyString);
If (MKey<>INVALIDKEY) then begin
SendKey(MKey,NumTimes,True);
PopUpShiftKeys;
Continue;
end;
end;
'~' : begin
SendKeyDown(VK_RETURN,1,True);
PopUpShiftKeys;
Inc(I);
end;
else begin
MKey:=vkKeyScan(SendKeysString[I]);
If (MKey<>INVALIDKEY) then begin
SendKey(MKey,1,True);
PopUpShiftKeys;
end else DisplayMessage('Invalid KeyName');
Inc(I);
end;
end;
end;
Result:=true;
PopUpShiftKeys;
end;
{AppActivate
This is used to set the current input focus to a given window using its
name. This is especially useful for ensuring a window is active before
sending it input messages using the SendKeys function. You can specify
a window's name in its entirety, or only portion of it, beginning from
the left.
}
var
WindowHandle : HWND;
function EnumWindowsProc(WHandle: HWND; lParam: LPARAM): BOOL; export; stdcall;
const
MAX_WINDOW_NAME_LEN = 80;
var
WindowName : array[0..MAX_WINDOW_NAME_LEN] of char;
begin
{Can't test GetWindowText's return value since some windows don't have a title}
GetWindowText(WHandle,WindowName,MAX_WINDOW_NAME_LEN);
Result := (StrLIComp(WindowName,PChar(lParam), StrLen(PChar(lParam))) <> 0);
If (not Result) then WindowHandle:=WHandle;
end;
function AppActivate(WindowName : PChar) : boolean;
begin
try
Result:=true;
WindowHandle:=FindWindow(nil,WindowName);
If (WindowHandle=0) then EnumWindows(@EnumWindowsProc,Integer(PChar(WindowName)));
If (WindowHandle<>0) then begin
SendMessage(WindowHandle, WM_SYSCOMMAND, SC_HOTKEY, WindowHandle);
SendMessage(WindowHandle, WM_SYSCOMMAND, SC_RESTORE, WindowHandle);
end else Result:=false;
except
on Exception do Result:=false;
end;
end;
end.
|
Как отправить нажатие клавиши с кодом 255 в элемент управления Windows.
| Ученые исследовательского подразделения MicroSoft подсчитали точное количество клавиш на клавиатуре... |
Функция keybd_event() принимает значения до 244 - как мне отправить нажатие клавиши с кодом #255 в элемент управления Windows? Это может понадобится для иностранных языков или для специальных символов. (например, в русских шрифтах символ с кодом #255 - я прописное). Приведенный в примере метод, не стоит использовать в случае если символ может быть передан обычным способом (функцией keybd_event()).
procedure TForm1.Button1Click(Sender: TObject);
var
KeyData : packed record
RepeatCount : word;
ScanCode : byte;
Bits : byte;
end;
begin
{Let the button repaint}
Application.ProcessMessages;
{Set the focus to the window}
Edit1.SetFocus;
{Send a right so the char is added to the end of the line}
// SimulateKeyStroke(VK_RIGHT, 0);
keybd_event(VK_RIGHT, 0,0,0);
{Let the app get the message}
Application.ProcessMessages;
FillChar(KeyData, sizeof(KeyData), #0);
KeyData.ScanCode := 255;
KeyData.RepeatCount := 1;
SendMessage(Edit1.Handle, WM_KEYDOWN, 255,LongInt(KeyData));
KeyData.Bits := KeyData.Bits or (1 shl 30);
KeyData.Bits := KeyData.Bits or (1 shl 31);
SendMessage(Edit1.Handle, WM_KEYUP, 255, LongInt(KeyData));
KeyData.Bits := KeyData.Bits and not (1 shl 30);
KeyData.Bits := KeyData.Bits and not (1 shl 31);
SendMessage(Edit1.Handle, WM_CHAR, 255, LongInt(KeyData));
Application.ProcessMessages;
end;
|
Отправить письмо на E-mail так, чтобы пользователь не подозревал об отправке.
| У одной провайдерской фирмы спросили: - Почему Вы так активно создаёте сервисы бесплатного e-mail? - Ну, как Вам сказать... А Вы читали когда-нибудь чужую почту? |
unit Email;
interface
uses
Windows, SusUtils, Classes;
function SendEmail(const RecipName, RecipAddress,
Subject, Attachment: string): Boolean;
function IsOnline: Boolean;
implementation
uses Mapi;
function SendEmail(const RecipName, RecipAddress,
Subject, Attachment: string): Boolean;
var
MapiMessage: TMapiMessage;
MapiFileDesc: TMapiFileDesc;
MapiRecipDesc: TMapiRecipDesc;
i: integer;
s: string;
begin
with MapiRecipDesc do
begin
ulRecerved:= 0;
ulRecipClass:= MAPI_TO;
lpszName:= PChar(RecipName);
lpszAddress:= PChar(RecipAddress);
ulEIDSize:= 0;
lpEntryID:= nil;
end;
with MapiFileDesc do
begin
ulReserved:= 0;
flFlags:= 0;
nPosition:= 0;
lpszPathName:= PChar(Attachment);
lpszFileName:= nil;
lpFileType:= nil;
end;
with MapiMessage do
begin
ulReserved := 0;
lpszSubject := nil;
lpszNoteText := PChar(Subject);
lpszMessageType := nil;
lpszDateReceived := nil;
lpszConversationID := nil;
flFlags := 0;
lpOriginator := nil;
nRecipCount := 1;
lpRecips := @MapiRecipDesc;
if length(Attachment) > 0 then
begin
nFileCount:= 1;
lpFiles := @MapiFileDesc;
end
else
begin
nFileCount:= 0;
lpFiles:= nil;
end;
end;
Result:= MapiSendMail(0, 0, MapiMessage, MAPI_DIALOG
or MAPI_LOGON_UI or MAPI_NEW_SESSION, 0) = SUCCESS_SUCCESS;
end;
function IsOnline: Boolean;
var
RASConn: TRASConn;
dwSize,dwCount: DWORD;
begin
RASConns.dwSize:= SizeOf(TRASConn);
dwSize:= SizeOf(RASConns);
Res:=RASEnumConnectionsA(@RASConns, @dwSize, @dwCount);
Result:= (Res = 0) and (dwCount > 0);
end;
end.
|
Как отправить бинарные данные из CGI приложения.
| Интернетчик является утром домой. Жена спрашивает: - Где был?! - У любовницы... - Врешь, опять всю ночь в Интернете просидел! |
Не для кого не секрет, как просто можно получать данные различного типа из CGI приложения. Однако, иногда необходимо, чтобы данные сохранялись в виде файла с определённым именем, типа "Test.ZIP". Для этого необходимо добавить в заголовок HTTP пункт "Content-Disposition".
В Delphi для этого используется свойство CustomHeaders. В это TStrings свойство можно добавлять пункты в виде "name=value" - так как HTTP синтакс name:value здесь не используется.
procedure TWebModule1.WebModule1CHECKSTATUSAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
s: TFileStream;
begin
s := nil;
if request.query='download' then
try
response.Title := 'Download Test.ZIP';
response.CustomHeaders.Add ('Content-Disposition=filename=Test.zip');
response.ContentType := 'application/zip';
s := TFileStream.Create (fmOpenRead+fmShareDenyNone,'Test.zip');
response.contentstream := s;
response.sendresponse;
finally
s.Free;
end;
end;
|
Как послать широковещательный UDP пакет.
| Смотрит ламер на папку "Удаленный доступ к сети", и думает: "Как он ещё тут, когда его уже удалили?" |
procedure TMainForm.FormCreate(Sender: TObject); var Init: TWSAData; SockOpt: BOOL; Sock: TSocket; Target: TSockAddrIn; begin WSAStartup($101,Init); Sock:=Socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); SockOpt:=TRUE; SetSockOpt(Sock,SOL_SOCKET,SO_BROADCAST, PChar(@SockOpt),SizeOf(SockOpt)) ; Target.sin_port:=htons(8167); //номер порта Target.sin_addr.S_addr:=INADDR_BROADCAST; Target.sa_family:=AF_INET; SendTo(Sock,Data,DataBytes,0,Target,SizeOf(Target)); WSACleanup; end; |
Переслать командную строку из второго экземпляра программы в первый.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
I set up a file type (.myfile) to be run by my program, and everything works
okay. The program opens it fine. The problem is, if I double-click some file
on my HD, it opens a new instance of my app, rather than sending the file to
the already opened one. How can I fix this?
}
{
Example for using DDE to open files from Explorer:
http://codecentral.borland.com/codecentral/ccweb.exe/finder
and search for submission ID 17787
}
{That is the comprehensive solution <g>. There are simpler ones but they tend
to get problems if more than one file is opened at once from Explorer. Those
problems can be handled, but it gets a bit complex to do that.
Basically you proceed like this:
In your programs main block (DPR file) you create a named global kernel
object, e.g. a Mutex or memory-mapped file, using a unique name (e.g. a GUID).
The first instance will create this object successfully and hold on to the
objects handle until it closes. All further instances will detect that the
object already exists, so know that they are not supposed to show up to the
user. But they have to pass over any command-line they may have been handed
from Explorer. They do that by sending a WM_COPYDATA message with the command
line contents to the first instances main window. For that they have to find
the windows handle, for which they use FindWindow with the main forms
classname (which should definitely be somewhat more unique than "Form1"!). The
problem is that the first instance may not have gotten around to creating its
main window yet (if more than one file has been opened from Explorer and the
program was not already running). So the second instance may have to wait in a
loop (using Sleep to suspend itself for a little bit) until the first
instances window shows up.
Bits and pieces of this process have been posted many times on the groups in
the past, but i don't remember if a solution covering all bases was among
them. So since it is Sunday and i'm a wee bit bored let's try for a generic
solution. The meat is in the PBOnceOnly unit given further down. I explain its
usage first. Note that this has been tested on Windows 2000 only, and not very
extensively either.
In the projects DPR file you have code looking like this:
}
program OneInstanceDemo;
uses
Forms,
Unit1 in 'Unit1.pas' {OneInstanceDemoMainform},
PBOnceOnly;
{$R *.res}
const
ProcessName = '{53F0DF5B-B69D-40B7-9B2C-A9E515CCFC80}';
begin
if AlreadyRunning(ProcessName, TOneInstanceDemoMainform) then
Exit;
Application.Initialize;
Application.CreateForm(TOneInstanceDemoMainform, OneInstanceDemoMainform);
Application.Run;
end.
{You can create a GUID for the processname via Ctrl-Shift-G in the IDE, just
remove the enclosing square brackets.
The main form needs a message handler for WM_COPYDATA, and also a method to
handle a command-line parameter. The example form only shows the passed
parameter in a memo.}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, Buttons, StdCtrls;
type
TOneInstanceDemoMainform = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
procedure WMCopyData(var msg: TWMCopyData); message WM_COPYDATA;
procedure HandleParameter(const param: string);
public
{ Public declarations }
end;
var
OneInstanceDemoMainform: TOneInstanceDemoMainform;
implementation
uses PBOnceOnly;
{$R *.DFM}
procedure TOneInstanceDemoMainform.FormCreate(Sender: TObject);
begin
memo1.Text := Format('Thread ID: %x'#13#10, [GetCurrentThreadID]);
HandleCommandline(HandleParameter);
end;
procedure TOneInstanceDemoMainform.HandleParameter(const param: string);
begin
memo1.Lines.Add(param);
end;
procedure TOneInstanceDemoMainform.WMCopyData(var msg: TWMCopyData);
begin
HandleSendCommandline(msg.CopyDataStruct^, HandleParameter);
end;
end.
{The work of dissecting the passed commandline is left to the PBOnceOnly unit,
since it "knows" how it packaged the parameters in the other instance. The
technique used by the unit is rather simple: the first instance creates a
memory mapped file and stores its main threads thread ID into this file. It
cannot store the main forms handle since the form has not been created yet
when AlreadyRunning is called. It would be a bad idea anyway since a forms
handle can change over the form objects lifetime. The second instance gets
this handle, uses EnumThreadWindows to find the first instances main form
handle (doing this way avoids problems with the IDE designers form instance
during development), packages the command line and sends it over to the found
window. The second instance will then terminate since AlreadyRunning returns
true in it. It never creates any of the autocreated forms or datamodules and
never enters its message loop.}
{== PBOnceOnly ========================================================}
{: Implements a function to detect a running instance of the program and
(optionally) pass over any command line to the first instances main
window.
@author Dr. Peter Below
@desc Version 1.0 created 2003-02-23<BR>
Last modified 2003-02-23<P>
If a command line has to be passed over we need the window handle of the
first instances main window, to send a WM_COPYDATA message to it. Since
the first instance may not have gotten around to creating its main
form window handle yet we retry a couple of times and wait a bit in
between. This process can be configured by setting the MAX_RETRIES and
RETRIES_INTERVAL variables before calling AlreadyRunning. }
{======================================================================}
{$BOOLEVAL OFF} {Unit depends on shortcut boolean evaluation}
unit PBOnceOnly;
interface
uses Windows;
var
{: Specifies how often we retry to find the first instances main
window. }
MAX_RETRIES: Integer = 10;
{: Specifies how long, in milliseconds, we sleep between retries. }
RETRIES_INTERVAL: Integer = 1000;
{-- AlreadyRunning ----------------------------------------------------}
{: Checks for another instance of the program and optionally passes over
this instances command line.
@Param aProcessName is a unique name to be used to identify this program.
@Param aMainformClass is the programs main form class, can be nil.
@Param passCommandline indicates whether to pass the command line, true
by default.
@Param allowMultiuserInstances indicates whether to allow other
instances of the program to run in another user context. Only applies
to Windows terminal server or XP. True by default.
@Returns true if there is another instance running, false if not.
@Precondition The function has not been called already. It must only
be called once per program run.
@Desc Creates a memory mapped file with the passed process name,
optionally with an added 'Global' prefix. If the MMF already existed
we know that this is a second instance. The first instance stores its
main thread ID into the MMF, the second one uses that with
EnumThreadWindows to find the first instances main window and sends
the command line via WM_COPYDATA to this window, if requested.
@Raises Exception if creation of the MMF fails for some reason.
}{ Created 2003-02-23 by P. Below
-----------------------------------------------------------------------}
function AlreadyRunning(const aProcessName: string;
aMainformClass: TClass = nil;
passCommandline: Boolean = true;
allowMultiuserInstances: Boolean = true): Boolean;
type
{: Callback type used by HandleSendCommandline. The callback will
be handed one parameter at a time. }
TParameterEvent = procedure(const aParam: string) of object;
{-- HandleSendCommandline ---------------------------------------------}
{: Dissect a command line passed via WM_COPYDATA from another instance
@Param data contains the data received via WM_COPYDATA.
@Param onParameter is a callback that will be called with every passed
parameter in turn.
@Precondition onParameter <> nil
}{ Created 2003-02-23 by P. Below
-----------------------------------------------------------------------}
procedure HandleSendCommandline(const data: TCopyDataStruct;
onParameter: TParameterEvent);
{-- HandleCommandline -------------------------------------------------}
{: This is a convenience procedure that allows handling of this
instances command line parameters to be done the same way as
a command line send over from another instance.
@Param onParameter will be called for every command line parameter in turn.
@Precondition onParameter <> nil
}{ Created 2003-02-23 by P. Below
-----------------------------------------------------------------------}
procedure HandleCommandline(onParameter: TParameterEvent);
implementation
uses Messages, Classes, Sysutils;
{ The THandledObject and TShareMem classes come from the D6 IPCDemos
demo project. }
type
THandledObject = class(TObject)
protected
FHandle: THandle;
public
destructor Destroy; override;
property Handle: THandle read FHandle;
end;
{ This class simplifies the process of creating a region of shared memory.
In Win32, this is accomplished by using the CreateFileMapping and
MapViewOfFile functions. }
TSharedMem = class(THandledObject)
private
FName: string;
FSize: Integer;
FCreated: Boolean;
FFileView: Pointer;
public
constructor Create(const Name: string; Size: Integer);
destructor Destroy; override;
property Name: string read FName;
property Size: Integer read FSize;
property Buffer: Pointer read FFileView;
property Created: Boolean read FCreated;
end;
procedure Error(const Msg: string);
begin
raise Exception.Create(Msg);
end;
{ THandledObject }
destructor THandledObject.Destroy;
begin
if FHandle <> 0 then
CloseHandle(FHandle);
end;
{ TSharedMem }
constructor TSharedMem.Create(const Name: string; Size: Integer);
begin
try
FName := Name;
FSize := Size;
{ CreateFileMapping, when called with $FFFFFFFF for the handle value,
creates a region of shared memory }
FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
Size, PChar(Name));
if FHandle = 0 then abort;
FCreated := GetLastError = 0;
{ We still need to map a pointer to the handle of the shared memory region
}
FFileView := MapViewOfFile(FHandle, FILE_MAP_WRITE, 0, 0, Size);
if FFileView = nil then abort;
except
Error(Format('Error creating shared memory %s (%d)', [Name,
GetLastError]));
end;
end;
destructor TSharedMem.Destroy;
begin
if FFileView <> nil then
UnmapViewOfFile(FFileView);
inherited Destroy;
end;
var
{ This object is destroyed by the unit finalization }
ProcessInfo: TSharedMem = nil;
{ Check if we are running in a terminal client session }
function IsRemoteSession: Boolean;
const
sm_RemoteSession = $1000; { from WinUser.h }
begin
Result := GetSystemMetrics(sm_RemoteSession) <> 0;
end;
{ Check if we are running on XP or a newer version. XP is Windows NT 5.1 }
function IsXP: Boolean;
begin
Result :=
(Sysutils.Win32Platform = VER_PLATFORM_WIN32_NT)
and
((Sysutils.Win32MajorVersion > 5)
or
((Sysutils.Win32MajorVersion = 5)
and
(Sysutils.Win32MinorVersion > 0)
)
);
end;
{ Check if we are running in a Windows terminal client session or on
Windows XP. }
function IsWTSOrXP: Boolean;
begin
Result := IsRemoteSession or IsXP
end;
type
{ Helper class to hold classname and found window handle for
EnumThreadWindows }
TEnumhelper = class
public
FClassname: string;
FWnd: HWND;
constructor Create(const aClassname: string);
function Matches(wnd: HWND): Boolean;
end;
constructor TEnumhelper.Create(const aClassname: string);
begin
inherited Create;
FClassname := aClassname;
end;
function TEnumhelper.Matches(wnd: HWND): Boolean;
var
classname: array[0..127] of Char;
begin
classname[0] := #0;
Windows.GetClassname(wnd, classname, sizeof(classname));
Result := AnsiSametext(Fclassname, classname);
if result then
FWnd := wnd;
end;
function EnumProc(wnd: HWND; helper: TEnumHelper): BOOL; stdcall;
begin
Result := not helper.Matches(wnd);
end;
function FindFirstInstanceMainform(const aClassname: string): HWND;
var
threadID: DWORD;
helper: TEnumHelper;
begin
threadID := PDWORD(Processinfo.FFileView)^;
helper := TEnumHelper.Create(aclassname);
try
EnumThreadWindows(threadID, @EnumProc, Integer(helper));
Result := helper.FWnd;
finally
helper.Free;
end;
end;
function AlreadyRunning(const aProcessName: string;
aMainformClass: TClass = nil;
passCommandline: Boolean = true;
allowMultiuserInstances: Boolean = true): Boolean;
function Processname: string;
begin
if not allowMultiuserInstances and IsWTSorXP then
Result := 'Global\' + aProcessName
else
Result := aProcessName;
end;
procedure StoreThreadID;
begin
PDWORD(ProcessInfo.FFileView)^ := GetCurrentThreadID;
end;
function GetCommandline: string;
var
sl: TStringlist;
i: Integer;
begin
if ParamCount = 1 then
Result := ParamStr(1)
else begin
sl := TStringlist.Create;
try
for i := 1 to ParamCount do
sl.Add(ParamStr(i));
Result := sl.Text;
finally
sl.free;
end; { Finally }
end;
end;
procedure DoPassCommandline;
var
wnd: HWND;
S: string;
copydata: TCopyDataStruct;
retries: Integer;
begin
retries := 0;
repeat
wnd := FindFirstInstanceMainform(aMainformclass.Classname);
if wnd <> 0 then
begin
S := GetCommandline;
copydata.dwData := Paramcount;
copydata.cbData := Length(S) + 1;
copydata.lpData := PChar(S);
SendMessage(wnd, WM_COPYDATA, 0, integer(@copydata));
end
else begin
Inc(retries);
Sleep(RETRIES_INTERVAL);
end;
until (wnd <> 0) or (retries > MAX_RETRIES);
end;
begin
Assert(not Assigned(ProcessInfo),
'Do not call AlreadyRunning more than once!');
ProcessInfo := TSharedMem.Create(Processname, Sizeof(DWORD));
Result := not ProcessInfo.Created;
if Result then
begin
if passCommandline and Assigned(aMainformClass) and (ParamCount > 0) then
DoPassCommandline;
end
else
StoreThreadID;
end;
procedure HandleSendCommandline(const data: TCopyDataStruct;
onParameter: TParameterEvent);
var
i: Integer;
sl: TStringlist;
begin
Assert(Assigned(onParameter), 'OnParameter cannot be nil');
if data.dwData = 1 then
onParameter(PChar(data.lpData))
else
begin
sl := TStringlist.Create;
try
sl.Text := PChar(data.lpData);
for i := 0 to sl.Count - 1 do
onParameter(sl[i]);
finally
sl.Free;
end; { Finally }
end;
end;
procedure HandleCommandline(onParameter: TParameterEvent);
var
i: Integer;
begin
Assert(Assigned(onParameter), 'OnParameter cannot be nil');
for i := 1 to ParamCount do
onParameter(ParamStr(i));
end;
initialization
finalization
ProcessInfo.Free;
end.
Пересылка данных в ячейки Excel.
| Автор: Mikhail Andronov |
| Новые компьютерные вирусы: "Виагра" - делает из вашей старой гибкой дискеты - жёсткий диск. "Монка Левински" - высасывает из вашего жёсткого диска информацию и тут же сообщает всем по сети о случившемся. "Рональд Рейган" - сохраняет все ваши данные, но забывает, где они находятся. "Борис Ельцин" - выставляет в биосе, что ваш 486 - это Р-III, обьясняет медленную скорость работы тем, что подцепил легкий вирус, постоянно обновляет системный регистр и драйвера. Проблемы 2000 для него не существует. Его дочерние версии могут тайком перекачивать деньги на зарубежные счета. "Майк Тайсон" - вырубает ваш компьютер с первых двух байтов. "Арнольд Шварцнеггер" - Terminate all programs and say -I'LL BE BACK!!! "Титаник" - показывает вам физиономию Ди-Каприо до тех пор, пока вы не утопите свой PC в ванной со льдом. |
Привожу полностью все файлы проекта:
// *-*-*-*-*-*-*-*
// SelectToExcel.dpr
// *-*-*-*-*-*-*-*
program SelectToExcel;
uses
Forms,
Main in 'Main.pas' {Form1};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
// *-*-*-*-*-*-*-*
// Main.dfm
// *-*-*-*-*-*-*-*
object Form1: TForm1
Left = 267
Top = 137
AutoScroll = False
Caption = 'Экспорт результатов SELECT в Excel'
ClientHeight = 277
ClientWidth = 519
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 8
Top = 4
Width = 114
Height = 13
Caption = 'Предложение SELECT'
end
object Label2: TLabel
Left = 8
Top = 224
Width = 91
Height = 13
Caption = 'Имя базы данных'
end
object btnExport: TButton
Left = 436
Top = 20
Width = 75
Height = 25
Caption = 'Экспорт'
TabOrder = 0
OnClick = btnExportClick
end
object memSelect: TMemo
Left = 8
Top = 20
Width = 417
Height = 197
TabOrder = 1
end
object edtDatabaseName: TEdit
Left = 8
Top = 240
Width = 413
Height = 21
TabOrder = 2
end
object queSelect: TQuery
Left = 24
Top = 20
end
end
// *-*-*-*-*-*-*-*
// Main.pas
// *-*-*-*-*-*-*-*
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Db, DBTables;
type
TForm1 = class(TForm)
queSelect: TQuery;
btnExport: TButton;
memSelect: TMemo;
edtDatabaseName: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure btnExportClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
ComObj;
{$R *.DFM}
procedure TForm1.btnExportClick(Sender: TObject);
var
XL, // Приложение Excel
TableVals: Variant; // Врем. массив для переноса значений в Excel
i, LineCounter, // Счетчик строк для переноса записей в Excel
queSelectRecCount,
queSelectFieldsCount: Integer;
begin
inherited;
try
Application.ProcessMessages;
Screen.Cursor := crSQLWait;
with queSelect do
begin
SQL.Assign(memSelect.Lines);
DatabaseName := edtDatabaseName.Text;
Open;
{AMA: Экспорт в Excel}
queSelectRecCount := RecordCount;
queSelectFieldsCount := FieldCount;
TableVals := VarArrayCreate([0, queSelectRecCount - 1, //кол-во строк
0, queSelectFieldsCount - 1], // кол-во столбцов
varOleStr);
First;
LineCounter := 0;
while not EOF do
begin
for i := 0 to queSelectFieldsCount - 1 do
if not Fields[i].IsNull then
TableVals[LineCounter, i] := Fields[i].AsString
else
TableVals[LineCounter, i] := '';
LineCounter := LineCounter + 1;
Next;
end;
Close;
end;
try
try
XL := GetActiveOleObject('Excel.Application');
except
XL := CreateOleObject('Excel.Application');
end;
except
raise Exception.Create('Не могу запустить Excel');
end;
XL.Visible := True;
XL.Workbooks.Add;
XL.Range[XL.Cells[1, 1],
XL.Cells[queSelectRecCount,
queSelectFieldsCount]].Value := TableVals;
XL.Range[XL.Cells[1, 1],
XL.Cells[queSelectRecCount,
queSelectFieldsCount]].Borders.Weight := 2;
finally
Screen.Cursor := crDefault;
end;
end;
end.
|
Отправить E-mail.
| BATMAN - человек-почтовый ящик. |
procedure TForm1.Button1Click(Sender: TObject);
begin
NMSMTP1.Host := 'mail.host.com';
NMSMTP1.UserID := 'Username';
NMSMTP1.Connect;
NMSMTP1.PostMessage.FromAddress := 'your_mail@host.com';
NMSMTP1.PostMessage.ToAddress.Text := 'delphiworld@mail.ru';
NMSMTP1.PostMessage.ToCarbonCopy.Text := 'your_mail@host.com';
NMSMTP1.PostMessage.ToBlindCarbonCopy.Text := 'second_your_mail@host.com';
NMSMTP1.PostMessage.Body.Text := 'This is the message from Delphi World project';
NMSMTP1.PostMessage.Attachments.Text := 'c:\File.txt';
NMSMTP1.PostMessage.Subject := 'Delphi World is Great!';
NMSMTP1.SendMail;
ShowMessage('Mail sent !');
NMSMTP1.Disconnect;
end;
|
Отправлять электронную почту.
| Приходит хакер к психиатру: - Док, помоги, у меня раздвоение виртуальной личности. - Не понял, это как? - Я со своего второго ника на третий E-Маi1ы начал получать. |
Алгоритм простой (для сторонних компонентов он принципиально ничем не отличается):
необходимо заполнить структуру PostMessage; отправить эту структуру при помощи метода SendMail.Структура PostMessage:
(*) FromName: string - имя отправителя; FromAddress: string - Почтовый адресс отправителя ReplyToAddress: string - Адрес, по которому следует ответить на письмо; Organization: string LocalProgram: string - Имя почтовой программы отправителя; (*) ToAddress: TStringList - Почтовый адрес главного получателя (ей) ToCC: TStringList - Список получателей Carbon Copy ToBCC: TSringList - Список получателей Blind Carbon Copy Attachments: TStringList - Список вложений Body: TStringList - Собственно текст письма Subject: string - Строка темы письма. (*) - помечены обязательные поля. С остальным не сложно разобраться самому. Также это можно сделать при помощи MAPI. Т.е. письмо будет отправлено средствами _почтовой_программы_по_умолчанию_ - со всеми недостатками :) (например письмо будет помещено в папку "отправленные"). Зато без всяких там компонент и просто :)Ниже готовая функция, в которой:
From - от кого Dest - кому Subject - тема Text - текст письма FileName - путь к вложению (если '', то соответственно ничего и не вкладываем) Outlook - показывать или нет письмо перед отправкой (в твоём случае False)
Uses MAPI;
...
function SendMail(const From, Dest, Subject, Text, FileName: PChar;
Outlook: boolean):Integer;
var
Message: TMapiMessage;
Recipient, Sender: TMapiRecipDesc;
File_Attachment: TMapiFileDesc;
function MakeMessage: TMapiMessage;
begin
FillChar(Sender, SizeOf(Sender), 0);
Sender.ulRecipClass := MAPI_ORIG;
Sender.lpszAddress := From;
FillChar(Recipient, SizeOf(Recipient), 0);
Recipient.ulRecipClass := MAPI_TO;
Recipient.lpszAddress := Dest;
FillChar(File_Attachment, SizeOf(File_Attachment), 0);
File_Attachment.nPosition := Cardinal(-1);
File_Attachment.lpszPathName := FileName;
FillChar(Result, SizeOf(Result), 0);
with Message do begin
lpszSubject := Subject;
lpszNoteText := Text;
lpOriginator := @Sender;
nRecipCount := 1;
lpRecips := @Recipient;
nFileCount := 1;
lpFiles := @File_Attachment;
end;
end;
var
SM: TFNMapiSendMail;
MAPIModule: HModule;
MAPI_FLAG: Cardinal;
begin
if Outlook then
MAPI_FLAG:=MAPI_DIALOG
else
MAPI_FLAG:=0;
MAPIModule := LoadLibrary(PChar(MAPIDLL));
if MAPIModule = 0 then
Result := -1
else
try
@SM := GetProcAddress(MAPIModule, 'MAPISendMail');
if @SM <> nil then begin
MakeMessage;
Result := SM(0, Application.Handle, Message, MAPI_FLAG, 0);
end else Result := 1;
finally
FreeLibrary(MAPIModule);
end;
end;
|
Отправить письмо, чтобы пользователь об этом не знал (D7).
Автор: Прокопец Антон.
// Кинуть на форму компонент IdSMTP1
...
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdMessageClient, IdSMTP, StdCtrls, idMessage, IdEMailAddress;
...
procedure TForm1.Button1Click(Sender: TObject);
var
M: TIdMessage;
begin
M := TIdMessage.Create(Form1);
M.Body.Add('Типа первая строчка собщения');
M.Body.Add('Типа вторая строчка собщения');
M.From.Text := '"Рустик" <boris@uacity.com>'; //отправитель
M.Recipients.Add;
M.Recipients.Items[0].Text := '"Тест" <antohha@ukr.net>'; //Получатель
M.Subject := 'Ну чо? потестим? =)'; //Тема письма
IdSMTP1.AuthenticationType := atLogin;
IdSMTP1.Host := 'uacity.com';
IdSMTP1.Username := 'boris';
IdSMTP1.Password := '1234'; // - ну свой пароль я те не выдам :-Р
IdSMTP1.Connect();
if IdSMTP1.Connected then
begin
IdSMTP1.Send(M);
end;
IdSMTP1.Disconnect;
end;
Отправка E-mail через MAPI.
Автор: Eugene Mayevski.
unit MapiControl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs;
type
{ Вводим новый тип события для получения Errorcode }
TMapiErrEvent = procedure(Sender: TObject; ErrCode: Integer) of object;
TMapiControl = class(TComponent)
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
private
{ Private-объявления }
FSubject: string;
FMailtext: string;
FFromName: string;
FFromAdress: string;
FTOAdr: TStrings;
FCCAdr: TStrings;
FBCCAdr: TStrings;
FAttachedFileName: TStrings;
FDisplayFileName: TStrings;
FShowDialog: Boolean;
FUseAppHandle: Boolean;
{ Error Events: }
FOnUserAbort: TNotifyEvent;
FOnMapiError: TMapiErrEvent;
FOnSuccess: TNotifyEvent;
{ +> Изменения, внесённые Eugene Mayevski [mailto:Mayevski@eldos.org]}
procedure SetToAddr(newValue : TStrings);
procedure SetCCAddr(newValue : TStrings);
procedure SetBCCAddr(newValue : TStrings);
procedure SetAttachedFileName(newValue : TStrings);
{ +
protected
{ Protected-объявления }
public
{ Public-объявления }
ApplicationHandle: THandle;
procedure Sendmail();
procedure Reset();
published
{ Published-объявления }
property Subject: string read FSubject write FSubject;
property Body: string read FMailText write FMailText;
property FromName: string read FFromName write FFromName;
property FromAdress: string read FFromAdress write FFromAdress;
property Recipients: TStrings read FTOAdr write SetTOAddr;
property CopyTo: TStrings read FCCAdr write SetCCAddr;
property BlindCopyTo: TStrings read FBCCAdr write SetBCCAddr;
property AttachedFiles: TStrings read FAttachedFileName write SetAttachedFileName;
property DisplayFileName: TStrings read FDisplayFileName;
property ShowDialog: Boolean read FShowDialog write FShowDialog;
property UseAppHandle: Boolean read FUseAppHandle write FUseAppHandle;
{ события: }
property OnUserAbort: TNotifyEvent read FOnUserAbort write FOnUserAbort;
property OnMapiError: TMapiErrEvent read FOnMapiError write FOnMapiError;
property OnSuccess: TNotifyEvent read FOnSuccess write FOnSuccess;
end;
procedure register;
implementation
uses Mapi;
{ регистрируем компонент: }
procedure register;
begin
RegisterComponents('expectIT', [TMapiControl]);
end;
{ TMapiControl }
constructor TMapiControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FOnUserAbort := nil;
FOnMapiError := nil;
FOnSuccess := nil;
FSubject := '';
FMailtext := '';
FFromName := '';
FFromAdress := '';
FTOAdr := TStringList.Create;
FCCAdr := TStringList.Create;
FBCCAdr := TStringList.Create;
FAttachedFileName := TStringList.Create;
FDisplayFileName := TStringList.Create;
FShowDialog := False;
ApplicationHandle := Application.Handle;
end;
{ +> Изменения, внесённые Eugene Mayevski [mailto:Mayevski@eldos.org]}
procedure TMapiControl.SetToAddr(newValue : TStrings);
begin
FToAdr.Assign(newValue);
end;
procedure TMapiControl.SetCCAddr(newValue : TStrings);
begin
FCCAdr.Assign(newValue);
end;
procedure TMapiControl.SetBCCAddr(newValue : TStrings);
begin
FBCCAdr.Assign(newValue);
end;
procedure TMapiControl.SetAttachedFileName(newValue : TStrings);
begin
FAttachedFileName.Assign(newValue);
end;
{ +
destructor TMapiControl.Destroy;
begin
FTOAdr.Free;
FCCAdr.Free;
FBCCAdr.Free;
FAttachedFileName.Free;
FDisplayFileName.Free;
inherited destroy;
end;
{ Сбрасываем все используемые поля}
procedure TMapiControl.Reset;
begin
FSubject := '';
FMailtext := '';
FFromName := '';
FFromAdress := '';
FTOAdr.Clear;
FCCAdr.Clear;
FBCCAdr.Clear;
FAttachedFileName.Clear;
FDisplayFileName.Clear;
end;
{ Эта процедура составляет и отправляет Email }
procedure TMapiControl.Sendmail;
var
MapiMessage: TMapiMessage;
MError: Cardinal;
Sender: TMapiRecipDesc;
PRecip, Recipients: PMapiRecipDesc;
PFiles, Attachments: PMapiFileDesc;
i: Integer;
AppHandle: THandle;
begin
{ Перво-наперво сохраняем Handle приложения, if not
the Component might fail to send the Email or
your calling Program gets locked up. }
AppHandle := Application.Handle;
{ Нам нужно зарезервировать память для всех получателей }
MapiMessage.nRecipCount := FTOAdr.Count + FCCAdr.Count + FBCCAdr.Count;
GetMem(Recipients, MapiMessage.nRecipCount * sizeof(TMapiRecipDesc));
try
with MapiMessage do
begin
ulReserved := 0;
{ Устанавливаем поле Subject: }
lpszSubject := PChar(Self.FSubject);
{ ... Body: }
lpszNoteText := PChar(FMailText);
lpszMessageType := nil;
lpszDateReceived := nil;
lpszConversationID := nil;
flFlags := 0;
{ и отправителя: (MAPI_ORIG) }
Sender.ulReserved := 0;
Sender.ulRecipClass := MAPI_ORIG;
Sender.lpszName := PChar(FromName);
Sender.lpszAddress := PChar(FromAdress);
Sender.ulEIDSize := 0;
Sender.lpEntryID := nil;
lpOriginator := @Sender;
PRecip := Recipients;
{ У нас много получателей письма: (MAPI_TO)
установим для каждого: }
if nRecipCount > 0 then
begin
for i := 1 to FTOAdr.Count do
begin
PRecip^.ulReserved := 0;
PRecip^.ulRecipClass := MAPI_TO;
{ lpszName should carry the Name like in the
contacts or the adress book, I will take the
email adress to keep it short: }
PRecip^.lpszName := PChar(FTOAdr.Strings[i - 1]);
{ Если Вы используете этот компонент совместно с Outlook97 или 2000
(не Express версии) , то Вам прийдётся добавить
'SMTP:' в начало каждого (email-) адреса.
}
PRecip^.lpszAddress := PChar('SMTP:' + FTOAdr.Strings[i - 1]);
PRecip^.ulEIDSize := 0;
PRecip^.lpEntryID := nil;
Inc(PRecip);
end;
{ То же самое проделываем с получателями копии письма: (CC, MAPI_CC) }
for i := 1 to FCCAdr.Count do
begin
PRecip^.ulReserved := 0;
PRecip^.ulRecipClass := MAPI_CC;
PRecip^.lpszName := PChar(FCCAdr.Strings[i - 1]);
PRecip^.lpszAddress := PChar('SMTP:' + FCCAdr.Strings[i - 1]);
PRecip^.ulEIDSize := 0;
PRecip^.lpEntryID := nil;
Inc(PRecip);
end;
{ ... тоже самое для Bcc: (BCC, MAPI_BCC) }
for i := 1 to FBCCAdr.Count do
begin
PRecip^.ulReserved := 0;
PRecip^.ulRecipClass := MAPI_BCC;
PRecip^.lpszName := PChar(FBCCAdr.Strings[i - 1]);
PRecip^.lpszAddress := PChar('SMTP:' + FBCCAdr.Strings[i - 1]);
PRecip^.ulEIDSize := 0;
PRecip^.lpEntryID := nil;
Inc(PRecip);
end;
end;
lpRecips := Recipients;
{ Теперь обработаем прикреплённые к письму файлы: }
if FAttachedFileName.Count > 0 then
begin
nFileCount := FAttachedFileName.Count;
GetMem(Attachments, MapiMessage.nFileCount * sizeof(TMapiFileDesc));
PFiles := Attachments;
{ Во первых установим отображаемые на экране имена файлов (без пути): }
FDisplayFileName.Clear;
for i := 0 to FAttachedFileName.Count - 1 do
FDisplayFileName.Add(ExtractFileName(FAttachedFileName[i]));
if nFileCount > 0 then
begin
{ Теперь составим структурку для прикреплённого файла: }
for i := 1 to FAttachedFileName.Count do
begin
{ Устанавливаем полный путь }
Attachments^.lpszPathName := PChar(FAttachedFileName.Strings[i - 1]);
{ ... и имя, отображаемое на дисплее: }
Attachments^.lpszFileName := PChar(FDisplayFileName.Strings[i - 1]);
Attachments^.ulReserved := 0;
Attachments^.flFlags := 0;
{ Положение должно быть -1, за разьяснениями обращайтесь в WinApi Help. }
Attachments^.nPosition := Cardinal(-1);
Attachments^.lpFileType := nil;
Inc(Attachments);
end;
end;
lpFiles := PFiles;
end
else
begin
nFileCount := 0;
lpFiles := nil;
end;
end;
{
Send the Mail, silent or verbose:
Verbose means in Express a Mail is composed and shown as setup.
In non-Express versions we show the Login-Dialog for a new
session and after we have choosen the profile to use, the
composed email is shown before sending
Silent does currently not work for non-Express version. We have
no Session, no Login Dialog so the system refuses to compose a
new email. In Express Versions the email is sent in the
background.
}
if FShowDialog then
MError := MapiSendMail(0, AppHandle, MapiMessage, MAPI_DIALOG
or MAPI_LOGON_UI or MAPI_NEW_SESSION, 0)
else
MError := MapiSendMail(0, AppHandle, MapiMessage, 0, 0);
{
Теперь обработаем сообщения об ошибках. В MAPI их присутствует достаточное.
количество. В этом примере я обрабатываю только два из них: USER_ABORT и SUCCESS,
относящиеся к специальным.
Сообщения, не относящиеся к специальным:
MAPI_E_AMBIGUOUS_RECIPIENT,
MAPI_E_ATTACHMENT_NOT_FOUND,
MAPI_E_ATTACHMENT_OPEN_FAILURE,
MAPI_E_BAD_RECIPTYPE,
MAPI_E_FAILURE,
MAPI_E_INSUFFICIENT_MEMORY,
MAPI_E_LOGIN_FAILURE,
MAPI_E_TEXT_TOO_LARGE,
MAPI_E_TOO_MANY_FILES,
MAPI_E_TOO_MANY_RECIPIENTS,
MAPI_E_UNKNOWN_RECIPIENT:
}
case MError of
MAPI_E_USER_ABORT:
begin
if Assigned(FOnUserAbort) then
FOnUserAbort(Self);
end;
SUCCESS_SUCCESS:
begin
if Assigned(FOnSuccess) then
FOnSuccess(Self);
end
else
begin
if Assigned(FOnMapiError) then
FOnMapiError(Self, MError);
end;
end;
finally
{ В заключение освобождаем память }
FreeMem(Recipients, MapiMessage.nRecipCount * sizeof(TMapiRecipDesc));
end;
end;
{ Вопросы и замечания присылайте Автору. }
end.
|
Передать файл через Socket соединение.
| - Ну, ты молодец! Вчера сделал больше чем за целый месяц!! - Да просто у меня Интернет не работал! |
Для данной цели можно воспользоваться компонентами TNMStrmServ и TNMStrm, которые предназначены для обмена потоками данных.
Обработчик для клиента:
var
MyStream: TMemoryStream;
begin
MyStream := TMemoryStream.Create;
MyStream.LoadFromFile('c:\windоws\рабочий стол\DеlрhiWоrld.txt');
NMStrm1.PostIt(MyStream);
MyStream.Free;
end;
|
Обработчик для сервера:
procedure TForm1.NMStrmServ1MSG(Sender: TComponent;
const sFrom: string; strm: TStream);
var
MyStream: TMemoryStream;
begin
MyStream := TMemoryStream.Create;
MyStream.CopyFrom(strm, NMStrmServ1.BytesTotal);
MyStream.SaveToFile('c:\windоws\рабочий стол\DеlрhiWоrld2.txt');
end;
|
Переслать файл в Корзину.
| - С 25 - Ранен. - С 26 - Ранен. - С 27 - Файл win. com на диске С: убит! |
Сначала нужно в директиве uses подключить модуль ShellAPI, чтобы мы смогли воспользоваться API-функцией SHFileOperation(). А затем уже можно использовать такой вот процедурой:
procedure SendToPomoyka(FileName: string);
var
SHF: TSHFileOpStruct;
begin
with SHF do
begin
Wnd := Application.Handle;
wFunc := FO_DELETE;
pFrom := PChar(FileName);
fFlags := FOF_SILENT or FOF_ALLOWUNDO;
end;
SHFileOperation(SHF);
end;
|
Как передать фокус следующему контролу.
Perform(WM_NEXTDLGCTL, 0, 0); |
Отправить сообщение на ICQ.
| Обменяю 7-значный UIN ICQ на два 6-значных. |
{
You need 3 TEdits, 1 TMemo und 1 TClientSocket.
Set the TClientsocket's Port to 80 and the Host to wwp.mirabilis.com.
}
var
Form1: TForm1;
csend: string;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
cSend := 'POST http://wwp.icq.com/scripts/WWPMsg.dll HTTP/2.0' + chr(13) + chr(10);
cSend := cSend + 'Referer: http://wwp.mirabilis.com' + chr(13) + chr(10);
cSend := cSend + 'User-Agent: Mozilla/4.06 (Win95; I)' + chr(13) + chr(10);
cSend := cSend + 'Connection: Keep-Alive' + chr(13) + chr(10);
cSend := cSend + 'Host: wwp.mirabilis.com:80' + chr(13) + chr(10);
cSend := cSend + 'Content-type: application/x-www-form-urlencoded' + chr(13) + chr(10);
cSend := cSend + 'Content-length:8000' + chr(13) + chr(10);
cSend := cSend + 'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*' +
chr(13) + chr(10) + chr(13) + chr(10);
cSend := cSend + 'from=' + edit1.Text + ' &fromemail=' + edit2.Text +
' &fromicq:110206786' + ' &body=' + memo1.Text + ' &to=' + edit3.Text + '&Send=';
clientsocket1.Active := True;
end;
procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
begin
clientsocket1.Socket.SendText(csend);
clientsocket1.Active := False;
end;
|
Посылка кода клавиши или текста в окно.
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var Key: Char);
private
AppInst: THandle;
AppWind: THandle;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses ShellAPI;
procedure SendShift(H: HWnd; Down: Boolean);
var
vKey, ScanCode, wParam: Word;
lParam: longint;
begin
vKey := $10;
ScanCode := MapVirtualKey(vKey, 0);
wParam := vKey or ScanCode shl 8;
lParam := longint(ScanCode) shl 16 or 1;
if not (Down) then
lParam := lParam or $C0000000;
SendMessage(H, WM_KEYDOWN, vKey, lParam);
end;
procedure SendCtrl(H: HWnd; Down: Boolean);
var
vKey, ScanCode, wParam: Word;
lParam: longint;
begin
vKey := $11;
ScanCode := MapVirtualKey(vKey, 0);
wParam := vKey or ScanCode shl 8;
lParam := longint(ScanCode) shl 16 or 1;
if not (Down) then
lParam := lParam or $C0000000;
SendMessage(H, WM_KEYDOWN, vKey, lParam);
end;
procedure SendKey(H: Hwnd; Key: char);
var
vKey, ScanCode, wParam: Word;
lParam, ConvKey: longint;
Shift, Ctrl: boolean;
begin
ConvKey := OemKeyScan(ord(Key));
Shift := (ConvKey and $00020000) <> 0;
Ctrl := (ConvKey and $00040000) <> 0;
ScanCode := ConvKey and $000000FF or $FF00;
vKey := ord(Key);
wParam := vKey;
lParam := longint(ScanCode) shl 16 or 1;
if Shift then
SendShift(H, true);
if Ctrl then
SendCtrl(H, true);
SendMessage(H, WM_KEYDOWN, vKey, lParam);
SendMessage(H, WM_CHAR, vKey, lParam);
lParam := lParam or $C0000000;
SendMessage(H, WM_KEYUP, vKey, lParam);
if Shift then
SendShift(H, false);
if Ctrl then
SendCtrl(H, false);
end;
function EnumFunc(Handle: HWnd; TF: TForm1): Bool; far;
begin
TF.AppWind := 0;
if GetWindowWord(Handle, GWW_HINSTANCE) = TF.AppInst then
TF.AppWind := Handle;
result := (TF.AppWind = 0);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Text: array[0..255] of char;
begin
AppInst := ShellExecute(Handle, 'open', 'notepad.exe', nil, '', SW_NORMAL);
EnumWindows(@EnumFunc, longint(self));
AppWind := GetWindow(AppWind, GW_CHILD);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
SendKey(AppWind, 'T');
SendKey(AppWind, 'e');
SendKey(AppWind, 's');
SendKey(AppWind, 't');
end;
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
if AppWind <> 0 then
SendKey(AppWind, Key);
end;
end.
|
Как посылать нажатие клавиш в элемент управления.
| Фирма Microsoft выпустила "Windows for woman" Основное отличие продукта в том, что две кнопки- "Yes" и "No" заменены одной- "Maybe..." |
Ниже приведена процедура, позволяющаю отправлять нажатия в любой элемент управления (window control), способный принимать ввод с клавиатуры. Вы можете использовать эту технику чтобы включать клавиши NumLock, CapsLock и ScrollLock под Windows NT. Та же техника работает и под Windows 95 для CapsLock и ScrollLock но не работает для клавиши NumLock.
Обратите внимание, что приведены четыре поцедуры: SimulateKeyDown() - эмулировать нажатие клавиши (без отпускания) SimulateKeyUp() - эмулировать отпускание клавиши SimulateKeystroke() - эмулировать удар по клавише (нажатие и отпускание) и SendKeys(), позволяющие Вам гибко контролировать посылаемые сообщения клавиатуры.
SimulateKeyDown(), SimulateKeyUp() и SimulateKeystroke() получают коды виртуальных клавиш (virtural key) (вроде VK_F1). Процедура SimulateKeystroke() получает дополнительный параметр, полезный при эмуляции нажатия PrintScreen. Когда этот параметр равен нулю весь экран будет скопирован в буфер обмена (clipboard). Если дополнительный параметр равен 1 будет скопированно только активное окно.
Четыре метода "button click" демонстрируют использование: ButtonClick1 - включает capslock ButtonClick2 - перехватывает весь экран в буфер обмена (clipboard). ButtonClick3 - перехватывает активное окно в буфер обмена (clipboard). ButtonClick4 - устанавливает фокус в Edit и отправляет в него строку.
procedure SimulateKeyDown(Key: byte);
begin
keybd_event(Key, 0, 0, 0);
end;
procedure SimulateKeyUp(Key: byte);
begin
keybd_event(Key, 0, KEYEVENTF_KEYUP, 0);
end;
procedure SimulateKeystroke(Key: byte; extra: DWORD);
begin
keybd_event(Key, extra, 0, 0);
keybd_event(Key, extra, KEYEVENTF_KEYUP, 0);
end;
procedure SendKeys(s: string);
var
i: integer;
flag: bool;
w: word;
begin
{Get the state of the caps lock key}
flag := not GetKeyState(VK_CAPITAL) and 1 = 0;
{If the caps lock key is on then turn it off}
if flag then
SimulateKeystroke(VK_CAPITAL, 0);
for i := 1 to Length(s) do
begin
w := VkKeyScan(s[i]);
{If there is not an error in the key translation}
if ((HiByte(w) $FF) and (LoByte(w) $FF)) then
begin
{If the key requires the shift key down - hold it down}
if HiByte(w) and 1 = 1 then
SimulateKeyDown(VK_SHIFT);
{Send the VK_KEY}
SimulateKeystroke(LoByte(w), 0);
{If the key required the shift key down - release it}
if HiByte(w) and 1 = 1 then
SimulateKeyUp(VK_SHIFT);
end;
end;
{if the caps lock key was on at start, turn it back on}
if flag then
SimulateKeystroke(VK_CAPITAL, 0);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{Toggle the cap lock}
SimulateKeystroke(VK_CAPITAL, 0);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
{Capture the entire screen to the clipboard}
{by simulating pressing the PrintScreen key}
SimulateKeystroke(VK_SNAPSHOT, 0);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
{Capture the active window to the clipboard}
{by simulating pressing the PrintScreen key}
SimulateKeystroke(VK_SNAPSHOT, 1);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
{Set the focus to a window (edit control) and send it a string}
Application.ProcessMessages;
Edit1.SetFocus;
SendKeys('Delphi World is REALY BEST');
end;
|
Послать нажатие клавиш.
Автор: Xavier Pacheco
unit Main;
interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Menus;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
Button2: TButton;
MainMenu1: TMainMenu;
File1: TMenuItem;
Open1: TMenuItem;
Exit1: TMenuItem;
Button4: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Open1Click(Sender: TObject);
procedure Exit1Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses SendKey, KeyDefs;
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.SetFocus; // focus Edit1
SendKeys('^{DELETE}I love...'); // send keys to Edit1
WaitForHook; // let keys playback
Perform(WM_NEXTDLGCTL, 0, 0); // move to Edit2
SendKeys('~delphi ~developer''s ~guide!'); // send keys to Edit2
end;
procedure TForm1.Button2Click(Sender: TObject);
var
H: hWnd;
PI: TProcessInformation;
SI: TStartupInfo;
begin
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
{ Invoke notepad }
if CreateProcess(nil, 'notepad', nil, nil, False, 0, nil, nil, SI,
PI) then
begin
{ wait until notepad is ready to receive keystrokes }
WaitForInputIdle(PI.hProcess, INFINITE);
{ find new notepad window }
H := FindWindow('Notepad', 'Untitled - Notepad');
if SetForegroundWindow(H) then // bring it to front
SendKeys('Hello from the Delphi Developer''s Guide SendKeys ' +
'example!{ENTER}'); // send keys!
end
else
MessageDlg(Format('Failed to invoke Notepad. Error code %d',
[GetLastError]), mtError, [mbOk], 0);
end;
procedure TForm1.Open1Click(Sender: TObject);
begin
ShowMessage('Open');
end;
procedure TForm1.Exit1Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
WaitForInputIdle(GetCurrentProcess, INFINITE);
SendKeys('@fx');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
WaitForHook;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
WaitForInputIdle(GetCurrentProcess, INFINITE);
SendKeys('@fo');
end;
end.
Скачать пример
Эмуляция нажатия клавиши для любого активного приложения.
Автор: VID WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Эмуляция нажатия клавиши
Функция SendKeys этого юнита, эмулиреут нажатие клавиши для лююого активного приложения
Для активизации приложения ивпользуйте функцию AppActivate
Зависимости: SysUtils, Windows, messages
Автор: VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright: Автор неизвестен
Дата: 19 июня 2002 г.
***************************************************** }
unit SKUnit;
interface
uses SysUtils, Windows, messages;
function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;
function AppActivate(WindowName: PChar): boolean;
const
WorkBufLen = 40;
var
WorkBuf: array[0..WorkBufLen] of Char;
implementation
type
THKeys = array[0..pred(MaxLongInt)] of byte;
var
AllocationSize: integer;
(*
Converts a string of characters and key names to keyboard events and
passes them to Windows.
Example syntax:
SendKeys('abc123{left}{left}{left}def{end}456{left 6}ghi{end}789', True);
*)
function SendKeys(SendKeysString: PChar; Wait: Boolean): Boolean;
type
WBytes = array[0..pred(SizeOf(Word))] of Byte;
TSendKey = record
Name: ShortString;
VKey: Byte;
end;
const
{Array of keys that SendKeys recognizes.
If you add to this list, you must be sure to keep it sorted alphabetically
by Name because a binary search routine is used to scan it.}
MaxSendKeyRecs = 41;
SendKeyRecs: array[1..MaxSendKeyRecs] of TSendKey =
(
(Name: 'BKSP'; VKey: VK_BACK),
(Name: 'BS'; VKey: VK_BACK),
(Name: 'BACKSPACE'; VKey: VK_BACK),
(Name: 'BREAK'; VKey: VK_CANCEL),
(Name: 'CAPSLOCK'; VKey: VK_CAPITAL),
(Name: 'CLEAR'; VKey: VK_CLEAR),
(Name: 'DEL'; VKey: VK_DELETE),
(Name: 'DELETE'; VKey: VK_DELETE),
(Name: 'DOWN'; VKey: VK_DOWN),
(Name: 'END'; VKey: VK_END),
(Name: 'ENTER'; VKey: VK_RETURN),
(Name: 'ESC'; VKey: VK_ESCAPE),
(Name: 'ESCAPE'; VKey: VK_ESCAPE),
(Name: 'F1'; VKey: VK_F1),
(Name: 'F10'; VKey: VK_F10),
(Name: 'F11'; VKey: VK_F11),
(Name: 'F12'; VKey: VK_F12),
(Name: 'F13'; VKey: VK_F13),
(Name: 'F14'; VKey: VK_F14),
(Name: 'F15'; VKey: VK_F15),
(Name: 'F16'; VKey: VK_F16),
(Name: 'F2'; VKey: VK_F2),
(Name: 'F3'; VKey: VK_F3),
(Name: 'F4'; VKey: VK_F4),
(Name: 'F5'; VKey: VK_F5),
(Name: 'F6'; VKey: VK_F6),
(Name: 'F7'; VKey: VK_F7),
(Name: 'F8'; VKey: VK_F8),
(Name: 'F9'; VKey: VK_F9),
(Name: 'HELP'; VKey: VK_HELP),
(Name: 'HOME'; VKey: VK_HOME),
(Name: 'INS'; VKey: VK_INSERT),
(Name: 'LEFT'; VKey: VK_LEFT),
(Name: 'NUMLOCK'; VKey: VK_NUMLOCK),
(Name: 'PGDN'; VKey: VK_NEXT),
(Name: 'PGUP'; VKey: VK_PRIOR),
(Name: 'PRTSC'; VKey: VK_PRINT),
(Name: 'RIGHT'; VKey: VK_RIGHT),
(Name: 'SCROLLLOCK'; VKey: VK_SCROLL),
(Name: 'TAB'; VKey: VK_TAB),
(Name: 'UP'; VKey: VK_UP)
);
{Extra VK constants missing from Delphi's Windows API interface}
VK_NULL = 0;
VK_SemiColon = 186;
VK_Equal = 187;
VK_Comma = 188;
VK_Minus = 189;
VK_Period = 190;
VK_Slash = 191;
VK_BackQuote = 192;
VK_LeftBracket = 219;
VK_BackSlash = 220;
VK_RightBracket = 221;
VK_Quote = 222;
VK_Last = VK_Quote;
ExtendedVKeys: set of byte =
[VK_Up,
VK_Down,
VK_Left,
VK_Right,
VK_Home,
VK_End,
VK_Prior, {PgUp}
VK_Next, {PgDn}
VK_Insert,
VK_Delete];
const
INVALIDKEY = $FFFF {Unsigned -1};
VKKEYSCANSHIFTON = $01;
VKKEYSCANCTRLON = $02;
VKKEYSCANALTON = $04;
UNITNAME = 'SendKeys';
var
UsingParens, ShiftDown, ControlDown, AltDown, FoundClose: Boolean;
PosSpace: Byte;
I, L: Integer;
NumTimes, MKey: Word;
KeyString: string[20];
procedure DisplayMessage(Message: PChar);
begin
MessageBox(0, Message, UNITNAME, 0);
end;
function BitSet(BitTable, BitMask: Byte): Boolean;
begin
Result := ByteBool(BitTable and BitMask);
end;
procedure SetBit(var BitTable: Byte; BitMask: Byte);
begin
BitTable := BitTable or Bitmask;
end;
procedure KeyboardEvent(VKey, ScanCode: Byte; Flags: Longint);
var
KeyboardMsg: TMsg;
begin
keybd_event(VKey, ScanCode, Flags, 0);
if (Wait) then
while (PeekMessage(KeyboardMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) do
begin
TranslateMessage(KeyboardMsg);
DispatchMessage(KeyboardMsg);
end;
end;
procedure SendKeyDown(VKey: Byte; NumTimes: Word; GenUpMsg: Boolean);
var
Cnt: Word;
ScanCode: Byte;
NumState: Boolean;
KeyBoardState: TKeyboardState;
begin
if (VKey = VK_NUMLOCK) then
begin
NumState := ByteBool(GetKeyState(VK_NUMLOCK) and 1);
GetKeyBoardState(KeyBoardState);
if NumState then
KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] and not 1)
else
KeyBoardState[VK_NUMLOCK] := (KeyBoardState[VK_NUMLOCK] or 1);
SetKeyBoardState(KeyBoardState);
exit;
end;
ScanCode := Lo(MapVirtualKey(VKey, 0));
for Cnt := 1 to NumTimes do
if (VKey in ExtendedVKeys) then
begin
KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY);
if (GenUpMsg) then
KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP)
end
else
begin
KeyboardEvent(VKey, ScanCode, 0);
if (GenUpMsg) then
KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end;
end;
procedure SendKeyUp(VKey: Byte);
var
ScanCode: Byte;
begin
ScanCode := Lo(MapVirtualKey(VKey, 0));
if (VKey in ExtendedVKeys) then
KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP)
else
KeyboardEvent(VKey, ScanCode, KEYEVENTF_KEYUP);
end;
procedure SendKey(MKey: Word; NumTimes: Word; GenDownMsg: Boolean);
begin
if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then
SendKeyDown(VK_SHIFT, 1, False);
if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then
SendKeyDown(VK_CONTROL, 1, False);
if (BitSet(Hi(MKey), VKKEYSCANALTON)) then
SendKeyDown(VK_MENU, 1, False);
SendKeyDown(Lo(MKey), NumTimes, GenDownMsg);
if (BitSet(Hi(MKey), VKKEYSCANSHIFTON)) then
SendKeyUp(VK_SHIFT);
if (BitSet(Hi(MKey), VKKEYSCANCTRLON)) then
SendKeyUp(VK_CONTROL);
if (BitSet(Hi(MKey), VKKEYSCANALTON)) then
SendKeyUp(VK_MENU);
end;
{Implements a simple binary search to locate special key name strings}
function StringToVKey(KeyString: ShortString): Word;
var
Found, Collided: Boolean;
Bottom, Top, Middle: Byte;
begin
Result := INVALIDKEY;
Bottom := 1;
Top := MaxSendKeyRecs;
Found := false;
Middle := (Bottom + Top) div 2;
repeat
Collided := ((Bottom = Middle) or (Top = Middle));
if (KeyString = SendKeyRecs[Middle].Name) then
begin
Found := True;
Result := SendKeyRecs[Middle].VKey;
end
else
begin
if (KeyString > SendKeyRecs[Middle].Name) then
Bottom := Middle
else
Top := Middle;
Middle := (Succ(Bottom + Top)) div 2;
end;
until (Found or Collided);
if (Result = INVALIDKEY) then
DisplayMessage('Invalid Key Name');
end;
procedure PopUpShiftKeys;
begin
if (not UsingParens) then
begin
if ShiftDown then
SendKeyUp(VK_SHIFT);
if ControlDown then
SendKeyUp(VK_CONTROL);
if AltDown then
SendKeyUp(VK_MENU);
ShiftDown := false;
ControlDown := false;
AltDown := false;
end;
end;
begin
AllocationSize := MaxInt;
Result := false;
UsingParens := false;
ShiftDown := false;
ControlDown := false;
AltDown := false;
I := 0;
L := StrLen(SendKeysString);
if (L > AllocationSize) then
L := AllocationSize;
if (L = 0) then
Exit;
case SendKeysString[I] of
'(':
begin
UsingParens := True;
Inc(I);
end;
')':
begin
UsingParens := False;
PopUpShiftKeys;
Inc(I);
end;
'%':
begin
AltDown := True;
SendKeyDown(VK_MENU, 1, False);
Inc(I);
end;
'+':
begin
ShiftDown := True;
SendKeyDown(VK_SHIFT, 1, False);
Inc(I);
end;
'^':
begin
ControlDown := True;
SendKeyDown(VK_CONTROL, 1, False);
Inc(I);
end;
'{':
begin
NumTimes := 1;
if (SendKeysString[Succ(I)] = '{') then
begin
MKey := VK_LEFTBRACKET;
SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);
SendKey(MKey, 1, True);
PopUpShiftKeys;
Inc(I, 3);
// Continue;
end;
KeyString := '';
FoundClose := False;
while (I <= L) do
begin
Inc(I);
if (SendKeysString[I] = '}') then
begin
FoundClose := True;
Inc(I);
Break;
end;
KeyString := KeyString + Upcase(SendKeysString[I]);
end;
if (not FoundClose) then
begin
DisplayMessage('No Close');
Exit;
end;
if (SendKeysString[I] = '}') then
begin
MKey := VK_RIGHTBRACKET;
SetBit(Wbytes(MKey)[1], VKKEYSCANSHIFTON);
SendKey(MKey, 1, True);
PopUpShiftKeys;
Inc(I);
// Continue;
end;
PosSpace := Pos(' ', KeyString);
if (PosSpace <> 0) then
begin
NumTimes := StrToInt(Copy(KeyString, Succ(PosSpace), Length(KeyString)
- PosSpace));
KeyString := Copy(KeyString, 1, Pred(PosSpace));
end;
if (Length(KeyString) = 1) then
MKey := vkKeyScan(KeyString[1])
else
MKey := StringToVKey(KeyString);
if (MKey <> INVALIDKEY) then
begin
SendKey(MKey, NumTimes, True);
PopUpShiftKeys;
// Continue;
end;
end;
'~':
begin
SendKeyDown(VK_RETURN, 1, True);
PopUpShiftKeys;
Inc(I);
end;
else
begin
MKey := vkKeyScan(SendKeysString[I]);
if (MKey <> INVALIDKEY) then
begin
SendKey(MKey, 1, True);
PopUpShiftKeys;
end
else
DisplayMessage('Invalid KeyName');
Inc(I);
end;
end;
Result := true;
PopUpShiftKeys;
end;
{AppActivate
This is used to set the current input focus to a given window using its
name. This is especially useful for ensuring a window is active before
sending it input messages using the SendKeys function. You can specify
a window's name in its entirety, or only portion of it, beginning from
the left.
}
var
WindowHandle: HWND;
function EnumWindowsProc(WHandle: HWND; lParam: LPARAM): BOOL; export; stdcall;
const
MAX_WINDOW_NAME_LEN = 80;
var
WindowName: array[0..MAX_WINDOW_NAME_LEN] of char;
begin
{Can't test GetWindowText's return value since some windows don't have a title}
GetWindowText(WHandle, WindowName, MAX_WINDOW_NAME_LEN);
Result := (StrLIComp(WindowName, PChar(lParam), StrLen(PChar(lParam))) <> 0);
if (not Result) then
WindowHandle := WHandle;
end;
function AppActivate(WindowName: PChar): boolean;
begin
try
Result := true;
WindowHandle := FindWindow(nil, WindowName);
if (WindowHandle = 0) then
EnumWindows(@EnumWindowsProc, Integer(PChar(WindowName)));
if (WindowHandle <> 0) then
begin
SendMessage(WindowHandle, WM_SYSCOMMAND, SC_HOTKEY, WindowHandle);
SendMessage(WindowHandle, WM_SYSCOMMAND, SC_RESTORE, WindowHandle);
end
else
Result := false;
except
on Exception do
Result := false;
end;
end;
end.
Пример использования:
SendKeys('A', False);
Посылаем нажатия клавиш другому приложению.
Автор: Gert v.d. Venis WEB-сайт: http://www.sources.ruКомпонент Sendkeys:
unit SendKeys;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TSendKeys = class(TComponent)
private
fhandle: HWND;
L: Longint;
fchild: boolean;
fChildText: string;
procedure SetIsChildWindow(const Value: boolean);
procedure SetChildText(const Value: string);
procedure SetWindowHandle(const Value: HWND);
protected
public
published
procedure GetWindowHandle(Text: string);
procedure SendKeys(buffer: string);
property WindowHandle: HWND read fhandle write SetWindowHandle;
property IsChildWindow: boolean read fchild write SetIsChildWindow;
property ChildWindowText: string read fChildText write SetChildText;
end;
procedure Register;
implementation
var
temps: string; {й utilizado para ser acessivel pelas funcs q sao
utilizadas como callbacks}
HTemp: Hwnd;
ChildText: string;
ChildWindow: boolean;
procedure Register;
begin
RegisterComponents('Standard', [TSendKeys]);
end;
{ TSendKeys }
function PRVGetChildHandle(H: HWND; L: Integer): LongBool;
var
p: pchar;
I: integer;
s: string;
begin
I := length(ChildText) + 2;
GetMem(p, i + 1);
SendMessage(H, WM_GetText, i, integer(p));
s := strpcopy(p, s);
if pos(ChildText, s) <> 0 then
begin
HTemp := H;
Result := False
end
else
Result := True;
FreeMem(p);
end;
function PRVSendKeys(H: HWND; L: Integer): LongBool; stdcall;
var
s: string;
i: integer;
begin
i := length(temps);
if i <> 0 then
begin
SetLength(s, i + 2);
GetWindowText(H, pchar(s), i + 2);
if Pos(temps, string(s)) <> 0 then
begin
Result := false;
if ChildWindow then
EnumChildWindows(H, @PRVGetChildHandle, L)
else
HTemp := H;
end
else
Result := True;
end
else
Result := False;
end;
procedure TSendKeys.GetWindowHandle(Text: string);
begin
temps := Text;
ChildText := fChildText;
ChildWindow := fChild;
EnumWindows(@PRVSendKeys, L);
fHandle := HTemp;
end;
procedure TSendKeys.SendKeys(buffer: string);
var
i: integer;
w: word;
D: DWORD;
P: ^DWORD;
begin
P := @D;
SystemParametersInfo(//get flashing timeout on win98
SPI_GETFOREGROUNDLOCKTIMEOUT,
0,
P,
0);
SetForeGroundWindow(fHandle);
for i := 1 to length(buffer) do
begin
w := VkKeyScan(buffer[i]);
keybd_event(w, 0, 0, 0);
keybd_event(w, 0, KEYEVENTF_KEYUP, 0);
end;
SystemParametersInfo(//set flashing TimeOut=0
SPI_SETFOREGROUNDLOCKTIMEOUT,
0,
nil,
0);
SetForegroundWindow(TWinControl(TComponent(Self).Owner).Handle);
//->typecast working...
SystemParametersInfo(//set flashing TimeOut=previous value
SPI_SETFOREGROUNDLOCKTIMEOUT,
D,
nil,
0);
end;
procedure TSendKeys.SetChildText(const Value: string);
begin
fChildText := Value;
end;
procedure TSendKeys.SetIsChildWindow(const Value: boolean);
begin
fchild := Value;
end;
procedure TSendKeys.SetWindowHandle(const Value: HWND);
begin
fHandle := WindowHandle;
end;
end.
Описание:
Данный компонент получает хэндл(handle) любого запущенного окна и даёт возможность отправить по указанному хэндлу любые комбинации нажатия клавиш.
Совместимость: Все версии Delphi
Собственно сам исходничек:
После того, как проинсталируете этот компонент, создайте новое приложение и поместите на форму кнопку и сам компонент SendKeys. Добавьте следующий код в обработчик события OnClick кнопки:
procedure TForm1.Button1Click(Sender: TObject);
begin
// Запускаем Notepad, и ему мы будем посылать нажатия клавиш
WinExec('NotePad.exe', SW_SHOW);
// В параметре процедуры GetWindowHandle помещаем
// текст заголовка окна Nоtераd'а.
SendKeys1.GetWindowHandle('Untitled - Notepad');
// Если хэндл окна получен успешно, то отправляем ему текст
if SendKeys1.WindowHandle <> 0 then
SendKeys1.SendKeys('This is a test');
// Так же можно отправить код любой кнопки типа
// RETURN, используя следующий код:
// SendKeys1.SendKeys(Chr(13));
end;
Неправда ли весело :).
Переслать сообщение с помощью SendMessage.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This is useful if you want to send a message from your DLL back
to the calling application.
}
const
MY_MESSAGE = WM_USER + 4242;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
// Handler that receive the Message
procedure MessageReceiver(var msg: TMessage); message MY_MESSAGE;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
txt: string;
begin
txt := 'Hello World';
SendMessage(Form1.Handle, MY_MESSAGE, 0, DWORD(PChar(txt)));
end;
// To receive this custom Message that is addressed to form1.handle
// you need a message handler.
procedure TForm1.MessageReceiver(var msg: TMessage);
var
txt: PChar;
begin
txt := PChar(msg.lParam);
msg.Result := 1;
ShowMessage(txt);
end;
end.
Отправление сообщения сразу всем элементам управления формы.
| Вы слишком много работаете за компьютером, если: - Вспоминаете про антивирусные пакеты всякий раз, когда открываете консервы, книги, двери - Вспоминаете про архивирование, когда: - идете с тяжелыми сумками домой - несете свою жену на руках - Вспоминаете операцию "удалить", когда: - видите тещу - инспектора ГИБДД - Подсознательно ненавидите женщин с именами - Ася - Клава - Вспоминаете про разархивирование, когда получаете зарплату. Вы слишком мало работаете за компьютером, если: - позволяете себе заниматься сексом без предохранения, даже с надежным партнером - позволяете себе заниматься сексом - позволяете себе. |
Можно использовать.
Screen.Forms[i].BroadCast(msg); |
Где [i] - индекс той формы, которой Вы хотите переслать сообщение. BroadCast работает со всеми компонентами, потомками TWinControls и отправляет сообщение всем дочерним компонентам из массива Controls. Если один из дочерних компонентов обрабатывает это сообщение и устанавливает Msg.Result в ненулевое значение - дальнейшая рассылка сообщения останавливается.
Посылка Raw IP-пакетов.
| Автор: E.J.Molendijk |
| Дочь просит И-нетчика: - Папа, посмотри пожалуйста, какая на улице погода. - Сейчас,-ответил тот и набрал в браузере "http://weather.cnn.com". |
Используя данный исходник можно конструировать собственные пакеты содержащие внутри всё, что угодно. Можно самостоятельно указывать в пакете IP-адрес получателя и отправителя, порт назначения и т.д. Если Вы не знаете, что это такое, то лучше не эксперементировать. Единственный недостаток, то, что скорее всего данный пример будет работать только в Windows 2000. Так же исходник позволяет произвести SYN flood и IP spoofing.
Необходимо зайти в систему под Администратором.
{
Raw Packet Sender
using: Delphi + Winsock 2
Copyright (c) 2000 by E.J.Molendijk (xes@dds.nl)
------------------------------------------------
Перед использованием измените значения
SrcIP+SrcPort+DestIP+DestPort на нужные!
------------------------------------------------
}
unit main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, OleCtrls, Registry;
const
SrcIP = '123.123.123.1';
SrcPort = 1234;
DestIP = '123.123.123.2';
DestPort = 4321;
Max_Message = 4068;
Max_Packet = 4096;
type
TPacketBuffer = array[0..Max_Packet-1] of byte;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure SendIt;
end;
// Заголовок IP пакета
type
T_IP_Header = record
ip_verlen : Byte;
ip_tos : Byte;
ip_totallength : Word;
ip_id : Word;
ip_offset : Word;
ip_ttl : Byte;
ip_protocol : Byte;
ip_checksum : Word;
ip_srcaddr : LongWord;
ip_destaddr : LongWord;
end;
// Заголовок UDP пакета
type
T_UDP_Header = record
src_portno : Word;
dst_portno : Word;
udp_length : Word;
udp_checksum : Word;
end;
// Некоторые объявления типов для Winsock 2
u_char = Char;
u_short = Word;
u_int = Integer;
u_long = Longint;
SunB = packed record
s_b1, s_b2, s_b3, s_b4: u_char;
end;
SunW = packed record
s_w1, s_w2: u_short;
end;
in_addr = record
case integer of
0: (S_un_b: SunB);
1: (S_un_w: SunW);
2: (S_addr: u_long);
end;
TInAddr = in_addr;
Sockaddr_in = record
case Integer of
0: (sin_family: u_short;
sin_port: u_short;
sin_addr: TInAddr;
sin_zero: array[0..7] of Char);
1: (sa_family: u_short;
sa_data: array[0..13] of Char)
end;
TSockAddr = Sockaddr_in;
TSocket = u_int;
const
WSADESCRIPTION_LEN = 256;
WSASYS_STATUS_LEN = 128;
type
PWSAData = ^TWSAData;
WSAData = record // !!! also WSDATA
wVersion: Word;
wHighVersion: Word;
szDescription: array[0..WSADESCRIPTION_LEN] of Char;
szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char;
iMaxSockets: Word;
iMaxUdpDg: Word;
lpVendorInfo: PChar;
end;
TWSAData = WSAData;
// Определяем необходимые функции winsock 2
function closesocket(s: TSocket): Integer; stdcall;
function socket(af, Struct, protocol: Integer): TSocket; stdcall;
function sendto(s: TSocket; var Buf; len, flags: Integer; var addrto: TSockAddr;
tolen: Integer): Integer; stdcall;{}
function setsockopt(s: TSocket; level, optname: Integer; optval: PChar;
optlen: Integer): Integer; stdcall;
function inet_addr(cp: PChar): u_long; stdcall; {PInAddr;} { TInAddr }
function htons(hostshort: u_short): u_short; stdcall;
function WSAGetLastError: Integer; stdcall;
function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;
function WSACleanup: Integer; stdcall;
const
AF_INET = 2; // internetwork: UDP, TCP, etc.
IP_HDRINCL = 2; // включаем заголовок IP пакета
SOCK_RAW = 3; // интерфейс raw-протокола
IPPROTO_IP = 0; // dummy for IP
IPPROTO_TCP = 6; // tcp
IPPROTO_UDP = 17; // user datagram protocol
IPPROTO_RAW = 255; // raw IP пакет
INVALID_SOCKET = TSocket(not(0));
SOCKET_ERROR = -1;
var
Form1: TForm1;
implementation
// Импортируем функции Winsock 2
const WinSocket = 'WS2_32.DLL';
function closesocket; external winsocket name 'closesocket';
function socket; external winsocket name 'socket';
function sendto; external winsocket name 'sendto';
function setsockopt; external winsocket name 'setsockopt';
function inet_addr; external winsocket name 'inet_addr';
function htons; external winsocket name 'htons';
function WSAGetLastError; external winsocket name 'WSAGetLastError';
function WSAStartup; external winsocket name 'WSAStartup';
function WSACleanup; external winsocket name 'WSACleanup';
{$R *.DFM}
//
// Function: checksum
//
// Description:
// This function calculates the 16-bit one's complement sum
// for the supplied buffer
//
function CheckSum(var Buffer; Size : integer) : Word;
type
TWordArray = array[0..1] of Word;
var
ChkSum : LongWord;
i : Integer;
begin
ChkSum := 0;
i := 0;
while Size > 1 do
begin
ChkSum := ChkSum + TWordArray(Buffer)[i];
inc(i);
Size := Size - SizeOf(Word);
end;
if Size=1 then
ChkSum := ChkSum + Byte(TWordArray(Buffer)[i]);
ChkSum := (ChkSum shr 16) + (ChkSum and $FFFF);
ChkSum := ChkSum + (Chksum shr 16);
Result := Word(ChkSum);
end;
procedure BuildHeaders(FromIP : string; iFromPort : Word; ToIP : string;
iToPort : Word; StrMessage : string; var Buf : TPacketBuffer;
var remote : TSockAddr; var iTotalSize: Word);
var
dwFromIP : LongWord;
dwToIP : LongWord;
iIPVersion : Word;
iIPSize : Word;
ipHdr : T_IP_Header;
udpHdr : T_UDP_Header;
iUdpSize : Word;
iUdpChecksumSize : Word;
cksum : Word;
Ptr : ^Byte;
procedure IncPtr(Value : Integer);
begin
ptr := pointer(integer(ptr) + Value);
end;
begin
// преобразуем ip адреса
dwFromIP := inet_Addr(PChar(FromIP));
dwToIP := inet_Addr(PChar(ToIP));
// Инициализируем заголовок IP пакета
//
iTotalSize := sizeof(ipHdr) + sizeof(udpHdr) + length(strMessage);
iIPVersion := 4;
iIPSize := sizeof(ipHdr) div sizeof(LongWord);
//
// IP version goes in the high order 4 bits of ip_verlen. The
// IP header length (in 32-bit words) goes in the lower 4 bits.
//
ipHdr.ip_verlen := (iIPVersion shl 4) or iIPSize;
ipHdr.ip_tos := 0; // IP type of service
ipHdr.ip_totallength := htons(iTotalSize); // Total packet len
ipHdr.ip_id := 0; // Unique identifier: set to 0
ipHdr.ip_offset := 0; // Fragment offset field
ipHdr.ip_ttl := 128; // время жизни пакета
ipHdr.ip_protocol := $11; // Protocol(UDP)
ipHdr.ip_checksum := 0 ; // IP checksum
ipHdr.ip_srcaddr := dwFromIP; // Source address
ipHdr.ip_destaddr := dwToIP; // Destination address
//
// Инициализируем заголовок UDP пакета
//
iUdpSize := sizeof(udpHdr) + length(strMessage);
udpHdr.src_portno := htons(iFromPort) ;
udpHdr.dst_portno := htons(iToPort) ;
udpHdr.udp_length := htons(iUdpSize) ;
udpHdr.udp_checksum := 0 ;
//
// Build the UDP pseudo-header for calculating the UDP checksum.
// The pseudo-header consists of the 32-bit source IP address,
// the 32-bit destination IP address, a zero byte, the 8-bit
// IP protocol field, the 16-bit UDP length, and the UDP
// header itself along with its data (padded with a 0 if
// the data is odd length).
//
iUdpChecksumSize := 0;
ptr := @buf[0];
FillChar(Buf, SizeOf(Buf), 0);
Move(ipHdr.ip_srcaddr, ptr^, SizeOf(ipHdr.ip_srcaddr));
IncPtr(SizeOf(ipHdr.ip_srcaddr));
iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_srcaddr);
Move(ipHdr.ip_destaddr, ptr^, SizeOf(ipHdr.ip_destaddr));
IncPtr(SizeOf(ipHdr.ip_destaddr));
iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_destaddr);
IncPtr(1);
Inc(iUdpChecksumSize);
Move(ipHdr.ip_protocol, ptr^, sizeof(ipHdr.ip_protocol));
IncPtr(sizeof(ipHdr.ip_protocol));
iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_protocol);
Move(udpHdr.udp_length, ptr^, sizeof(udpHdr.udp_length));
IncPtr(sizeof(udpHdr.udp_length));
iUdpChecksumSize := iUdpChecksumSize + sizeof(udpHdr.udp_length);
move(udpHdr, ptr^, sizeof(udpHdr));
IncPtr(sizeof(udpHdr));
iUdpChecksumSize := iUdpCheckSumSize + sizeof(udpHdr);
Move(StrMessage[1], ptr^, Length(strMessage));
IncPtr(Length(StrMessage));
iUdpChecksumSize := iUdpChecksumSize + length(strMessage);
cksum := checksum(buf, iUdpChecksumSize);
udpHdr.udp_checksum := cksum;
//
// Now assemble the IP and UDP headers along with the data
// so we can send it
//
FillChar(Buf, SizeOf(Buf), 0);
Ptr := @Buf[0];
Move(ipHdr, ptr^, SizeOf(ipHdr)); IncPtr(SizeOf(ipHdr));
Move(udpHdr, ptr^, SizeOf(udpHdr)); IncPtr(SizeOf(udpHdr));
Move(StrMessage[1], ptr^, length(StrMessage));
// Apparently, this SOCKADDR_IN structure makes no difference.
// Whatever we put as the destination IP addr in the IP header
// is what goes. Specifying a different destination in remote
// will be ignored.
//
remote.sin_family := AF_INET;
remote.sin_port := htons(iToPort);
remote.sin_addr.s_addr := dwToIP;
end;
procedure TForm1.SendIt;
var
sh : TSocket;
bOpt : Integer;
ret : Integer;
Buf : TPacketBuffer;
Remote : TSockAddr;
Local : TSockAddr;
iTotalSize : Word;
wsdata : TWSAdata;
begin
// Startup Winsock 2
ret := WSAStartup($0002, wsdata);
if ret<>0 then
begin
memo1.lines.add('WSA Startup failed.');
exit;
end;
with memo1.lines do
begin
add('WSA Startup:');
add('Desc.: '+wsData.szDescription);
add('Status: '+wsData.szSystemStatus);
end;
try
// Создаём сокет
sh := Socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if (sh = INVALID_SOCKET) then
begin
memo1.lines.add('Socket() failed: '+IntToStr(WSAGetLastError));
exit;
end;
Memo1.lines.add('Socket Handle = '+IntToStr(sh));
// Option: Header Include
bOpt := 1;
ret := SetSockOpt(sh, IPPROTO_IP, IP_HDRINCL, @bOpt, SizeOf(bOpt));
if ret = SOCKET_ERROR then
begin
Memo1.lines.add('setsockopt(IP_HDRINCL) failed: '+IntToStr(WSAGetLastError));
exit;
end;
// строим пакет
BuildHeaders( SrcIP, SrcPort,
DestIP, DestPort,
'THIS IS A TEST PACKET',
Buf, Remote, iTotalSize );
// Отправляем пакет
ret := SendTo(sh, buf, iTotalSize, 0, Remote, SizeOf(Remote));
if ret = SOCKET_ERROR then
Memo1.Lines.Add('sendto() failed: '+IntToStr(WSAGetLastError))
else
Memo1.Lines.Add('send '+IntToStr(ret)+' bytes.');
// Закрываем сокет
CloseSocket(sh);
finally
// Закрываем Winsock 2
WSACleanup;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SendIt;
end;
end.
|
Передать строки, картинки (streams) между процессами.
{
The WM_COPYDATA messages makes it possible to transfer information
between processes. It does this by passing the data through the kernel.
Space is allocated in the receiving process to hold the information that is copied,
by the kernel, from the source process to the target process.
The sender passes a pointer to a COPYDATASTRUCT, which is defined as a structure
of the following:
}
type
TCopyDataStruct = packed record
dwData: DWORD; // anwendungsspezifischer Wert
cbData: DWORD; // Byte-Lдngе der zu ььегtгаgеndеn Daten
lpData: Pointer; // Adresse der Daten
end;
{ Sender Application }
unit SenderApp;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Button2: TButton;
Image1: TImage;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
procedure SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct);
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// Sender: Send data
procedure TForm1.SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct);
begin
if hTargetWnd <> 0 then
SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@ACopyDataStruct))
else
ShowMessage('No Recipient found!');
end;
// Send Text from Edit1 to other app
procedure TForm1.Button1Click(Sender: TObject);
var
MyCopyDataStruct: TCopyDataStruct;
hTargetWnd: HWND;
begin
// Set up a COPYDATASTRUCT structure for use with WM_COPYDATA
// TCopyDataStruct mit den Sende-Daten Infos аиsfь11еn
with MyCopyDataStruct do
begin
dwData := 0; // may use a value do identify content of message
cbData := StrLen(PChar(Edit1.Text)) + 1; //Need to transfer terminating #0 as well
lpData := PChar(Edit1.Text)
end;
// Find the destination window for the WM_COPYDATA message
// Еmрfдngег Fenster anhand des Titelzeilentextes suchen
hTargetWnd := FindWindow(nil,PChar('Message Receiver'));
// send the structure to the receiver
// Die Struktur an den Еmрfдngег schicken
SendCopyData(hTargetWnd, MyCopyDataStruct);
end;
// Send Image1 to other app
procedure TForm1.Button2Click(Sender: TObject);
var
ms: TMemoryStream;
MyCopyDataStruct: TCopyDataStruct;
hTargetWnd: HWND;
begin
ms := TMemoryStream.Create;
try
image1.Picture.Bitmap.SaveToStream(ms);
with MyCopyDataStruct do
begin
dwData := 1;
cbData := ms.Size;
lpData := ms.Memory;
end;
// Search window by the window title
// Еmрfдngег Fenster anhand des Titelzeilentextes suchen
hTargetWnd := FindWindow(nil,PChar('Message Receiver'));
// Send the Data
// Daten Senden
SendCopyData(hTargetWnd,MyCopyDataStruct);
finally
ms.Free;
end;
end;
end.
{*********************************************************************}
{ Receiver Application }
unit ReceiverApp;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Label1: TLabel;
private
procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var
sText: array[0..99] of Char;
ms: TMemoryStream;
begin
case Msg.CopyDataStruct.dwData of
0: { Receive Text, Text empfangen}
begin
StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
label1.Caption := sText;
end;
1: { Receive Image, Bild empfangen}
begin
ms := TMemoryStream.Create;
try
with Msg.CopyDataStruct^ do
ms.Write(lpdata^, cbdata);
ms.Position := 0;
image1.Picture.Bitmap.LoadFromStream(ms);
finally
ms.Free;
end;
end;
end;
end;
end.
|
Как передать строку другому приложению.
Получатель:
procedure ReceiveMessage (var Msg: TMessage); message WM_COPYDATA; ... procedure TFormReceive.ReceiveMessage; var pcd: PCopyDataStruct; begin pcd := PCopyDataStruct(Msg.LParam); Caption := PChar(pcd.lpData); end; |
Отправитель:
procedure TFormXXX.Button1Click(Sender: TObject);
var
cd: TCopyDataStruct;
begin
cd.cbData := Length(Edit1.Text) + 1;
cd.lpData := PChar(Edit1.Text);
SendMessage(FindWindow('TFormReceive', nil), WM_COPYDATA, 0, LParam(@cd));
end;
|
Передача спецсимволов в документ Word.
Автор: Andrey
// Омега 937 - юникод нужного символа
WordApp.Selection.TypeText(WideString('#937'));
Переслать текст в другую программу.
Автор: Xavier Pacheco
unit Readmain;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Menus, StdCtrls;
{ The WM_COPYDATA Windows message is not defined in the 16-bit Messages }
{ unit, although it is available to 16-bit applications running under }
{ Windows 95 or NT. This message is discussed in the Win32 API online }
{ help. }
const
WM_COPYDATA = $004A;
type
TMainForm = class(TForm)
ReadMemo: TMemo;
MainMenu1: TMainMenu;
File1: TMenuItem;
Exit1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
procedure Exit1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure About1Click(Sender: TObject);
private
procedure OnAppMessage(var M: TMsg; var Handled: Boolean);
procedure WMCopyData(var M: TMessage); message WM_COPYDATA;
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
uses RegMsg, AboutU;
type
{ The TCopyDataStruct record type is not defined in WinTypes unit, }
{ although it is available in the 16-bit Windows API when running }
{ under Windows 95 and NT. The lParam of the WM_COPYDATA message }
{ points to one of these. }
PCopyDataStruct = ^TCopyDataStruct;
TCopyDataStruct = record
dwData: DWORD;
cbData: DWORD;
lpData: Pointer;
end;
procedure TMainForm.OnAppMessage(var M: TMsg; var Handled: Boolean);
{ OnMessage handler for Application object. }
begin
{ The DDGM_HandshakeMessage message is received as a broadcast to }
{ all applications. The wParam of this message contains the handle }
{ of the window which broadcasted the message. We respond by posting }
{ the same message back to the sender, with our handle in the wParam. }
if M.Message = DDGM_HandshakeMessage then
begin
PostMessage(M.wParam, DDGM_HandshakeMessage, Handle, 0);
Handled := True;
end;
end;
procedure TMainForm.WMCopyData(var M: TMessage);
{ Handler for WM_COPYDATA message }
begin
{ Check wParam to ensure we know WHO sent us the WM_COPYDATA message }
if PCopyDataStruct(M.lParam)^.dwData = DDGM_HandshakeMessage then
{ When WM_COPYDATA message is received, the lParam points to}
ReadMemo.SetTextBuf(PChar(PCopyDataStruct(M.lParam)^.lpData));
end;
procedure TMainForm.Exit1Click(Sender: TObject);
begin
Close;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Application.OnMessage := OnAppMessage;
end;
procedure TMainForm.About1Click(Sender: TObject);
begin
AboutBox;
end;
end.
unit CopyMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Menus;
type
TMainForm = class(TForm)
DataMemo: TMemo;
BottomPnl: TPanel;
BtnPnl: TPanel;
CloseBtn: TButton;
CopyBtn: TButton;
MainMenu1: TMainMenu;
File1: TMenuItem;
CopyData1: TMenuItem;
N1: TMenuItem;
Exit1: TMenuItem;
Help1: TMenuItem;
About1: TMenuItem;
procedure CloseBtnClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure About1Click(Sender: TObject);
procedure CopyBtnClick(Sender: TObject);
private
{ Private declarations }
protected
procedure WndProc(var Message: TMessage); override;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
uses AboutU, RegMsg;
// The following declaration is necessary because of an error in
// the declaration of BroadcastSystemMessage() in the Windows unit
function BroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD;
uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
external 'user32.dll';
var
Recipients: DWORD = BSM_APPLICATIONS;
procedure TMainForm.WndProc(var Message: TMessage);
var
DataBuffer: TCopyDataStruct;
Buf: PChar;
BufSize: Integer;
begin
if Message.Msg = DDGM_HandshakeMessage then
begin
{ Allocate buffer }
BufSize := DataMemo.GetTextLen + (1 * SizeOf(Char));
Buf := AllocMem(BufSize);
{ Copy memo to buffer }
DataMemo.GetTextBuf(Buf, BufSize);
try
with DataBuffer do
begin
{ Fill dwData with registered message as safety check }
dwData := DDGM_HandshakeMessage;
cbData := BufSize;
lpData := Buf;
end;
{ NOTE: WM_COPYDATA message must be *sent* }
SendMessage(Message.wParam, WM_COPYDATA, Handle,
Longint(@DataBuffer));
finally
FreeMem(Buf, BufSize);
end;
end
else
inherited WndProc(Message);
end;
procedure TMainForm.CloseBtnClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.FormResize(Sender: TObject);
begin
BtnPnl.Left := BottomPnl.Width div 2 - BtnPnl.Width div 2;
end;
procedure TMainForm.About1Click(Sender: TObject);
begin
AboutBox;
end;
procedure TMainForm.CopyBtnClick(Sender: TObject);
begin
{ Call for any listening apps }
BroadcastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
@Recipients, DDGM_HandshakeMessage, Handle, 0);
end;
end.
Скачать весь проект
Как отправить на принтер чистый поток данных.
Под Win16 Вы можете использовать функцию SpoolFile, или Passthrough escape, если принтер поддерживает последнее. Под Win32 Вы можете использовать WritePrinter.
Ниже пример открытия принтера и записи чистого потока данных в принтер. Учтите, что Вы должны передать корректное имя принтера, такое, как "HP LaserJet 5MP", чтобы функция сработала успешно.
Конечно, Вы можете включать в поток данных любые необходимые управляющие коды, которые могут потребоваться.
uses WinSpool;
procedure WriteRawStringToPrinter(PrinterName:string; S:string);
var
Handle: THandle;
N: DWORD;
DocInfo1: TDocInfo1;
begin
if not OpenPrinter(PChar(PrinterName), Handle, nil) then
begin
ShowMessage('Error ' + IntToStr(GetLastError));
Exit;
end;
with DocInfo1 do
begin
pDocName := PChar('test doc');
pOutputFile := nil;
pDataType := 'RAW';
end;
StartDocPrinter(Handle, 1, @DocInfo1);
StartPagePrinter(Handle);
WritePrinter(Handle, PChar(S), Length(S), N);
EndPagePrinter(Handle);
EndDocPrinter(Handle);
ClosePrinter(Handle);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
WriteRawStringToPrinter('HP', 'Test This');
end;
|
Посмотри и доделай как тебе надо:
unit TextPrinter;
interface
uses
Windows, Controls, Forms, Dialogs;
type
TTextPrinter = class(TObject)
FNumberOfBytesWritten: Integer;
FHandle: THandle;
FPrinterOpen: Boolean;
FErrorString: PChar;
procedure SetErrorString;
public
constructor Create;
procedure write(const Str: string);
procedure WriteLn(const Str: string);
destructor Destroy; override;
published
property NumberOfBytesWritten: Integer read FNumberOfBytesWritten;
end;
implementation
{TTextPrinter}
constructor TTextPrinter.Create;
begin
FHandle := CreateFile('LPT1', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ
or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if FHandle = INVALID_HANDLE_VALUE then
begin
SetErrorString;
raise Exception.Create(FErrorString);
end
else
FPrinterOpen := True;
end;
procedure TTextPrinter.SetErrorString;
begin
if FErrorString <> nil then
LocalFree(Integer(FErrorString));
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,
nil, GetLastError(), LANG_USER_DEFAULT, @FErrorString, 0, nil);
end;
procedure TTextPrinter.write(const Str: string);
var
OEMStr: PChar;
NumberOfBytesToWrite: Integer;
begin
if not FPrinterOpen then
Exit;
NumberOfBytesToWrite := Length(Str);
OEMStr := PChar(LocalAlloc(LMEM_FIXED, NumberOfBytesToWrite + 1));
try
CharToOem(PChar(Str), OEMStr);
if not WriteFile(FHandle, OEMStr^, NumberOfBytesToWrite, FNumberOfBytesWritten, nil) then
begin
SetErrorString;
raise Exception.Create(FErrorString);
end;
finally
LocalFree(Integer(OEMStr));
end;
end;
procedure TTextPrinter.WriteLn(const Str: string);
begin
Self.write(Str);
Self.write(#10);
end;
destructor TTextPrinter.Destroy;
begin
CloseHandle(FHandle);
if FErrorString <> nil then
LocalFree(Integer(FErrorString));
end;
end.
|
P.S. В принципе, вместо LPT1 может стоять что угодно, даже сетевой сервер печати (\\server\prn) - все равно печатает. Можно и параметр в конструктор вставить и т.д.
Как передать UserName и Password в удаленный модуль данных.
В Удаленный Модуль Данных бросьте компонент TDatabase, затем добавьте процедуру автоматизации (пункт главного меню Edit | Add To Interface) для Login.
Убедитесь, что свойство HandleShared компонента TDatabase установлено в True.
procedure Login(UserName, Password: WideString);
begin
{ DB = TDatabase }
{ Something unique between clients }
DB.DatabaseName := UserName + 'DB';
DB.Params.Values['USER NAME'] := UserName;
DB.Params.Values['PASSWORD'] := Password;
DB.Open;
end;
|
После того, как Вы создали этот метод автоматизации, Вы можете вызывать его с помощью:
RemoteServer1.AppServer.Login('USERNAME','PASSWORD');
|
Как отправить вебформу на сервер при помощи TClientSocket (напрямую и через прокси).
| Посетитель у провайдера. - Ой, что это у вас так крякнуло. - Сервер наверное... |
{
Присоедините следующие события к Вашему ClientSocket:
procedure T...Form.ClientSocket1Write;
procedure T...Form.ClientSocket1Read;
procedure T...Form.ClientSocket1Disconnect;
procedure T...Form.ClientSocket1Error;
Так же пример показывает, как направлять передачу через прокси-сервер.
Для отправки на вебсервер используется следующий формат:
Напрямую:
'POST ' + PostAddr + 'HTTP/1.0' + HTTP_Data + Content
Через проксю:
'POST http://' Webserver + PostAddr + 'HTTP/1.0' + HTTP_Data + Content
}
const
WebServer = 'www.somehost.com';
WebPort = 80;
PostAddr = '/cgi-bin/form';
{ Следующие переменные используются только для вебсервера: }
ProxyServer ='proxy.somewhere.com';
ProxyPort = 3128;
// В заголовке post необходимы некоторые данные
HTTP_Data =
'Content-Type: application/x-www-form-urlencoded'#10+
'User-Agent: Delphi/5.0 ()'#10+ { Отрекламируем Delphi 5! }
'Host: somewhere.com'#10+
'Connection: Keep-Alive'#10;
type
T...Form = class(TForm)
...
private
{ Private declarations }
HTTP_POST : string;
FContent : string;
// Эта переменная будет содержать ответ сервера
FResult : string;
public
{ Public declarations }
end;
{ Эти функции сделают некоторое url-кодирование }
{ Например. 'John Smith' => 'John+Smith' }
function HTTPTran(St: string): string;
var
i: Integer;
begin
Result:='';
for i:=1 to length(St) do
if St[i] in ['a'..'z','A'..'Z','0','1'..'9'] then
Result:=Result+St[i]
else
if St[i]=' ' then
Result:=Result+'+'
else
Result:=Result+'%'+IntToHex(Byte(St[i]),2);
end;
procedure T...Form.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
begin
// Постим данные
Socket.SendText(HTTP_POST+FContent);
end;
procedure T...Form.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
begin
// Получаем результат
FResult:=FResult+Socket.ReceiveText;
end;
procedure T...Form.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
// ЗДЕСЬ МОЖНО ОБРАБОТАТЬ FResult //
end;
procedure T...Form.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ErrorCode := 0; // Игнорируем ошибки
end;
{ А эта подпрограмма, которую можно
использовать для постинга данных формы. }
procedure T...Form.PostTheForm;
begin
// Очищаем результаты
FResult:='';
// Вы можете ввести поля формы, которые необходимы
// Вот некоторые примеры:
FContent:=
'Name='+ HTTPTran('John Smith') +'&'+
'Address='+ HTTPTran('1 Waystreet') +'&'+
'Email='+ HTTPTran('jsmith@somewhere.com') +'&'+
'B1=Submit' + #10;
// Вычисляем длину содержимого
FContent:=
'Content-Length: '+IntToStr(Length(FContent))+#10+#10+FContent;
{-- Начало прокси ---}
{ если Вы используете прокси, то раскоментируйте этот код }
ClientSocket1.Host := ProxyServer;
ClientSocket1.Port := ProxyPort;
HTTP_POST := 'POST http: //'+WebServer+PostAddr+' HTTP/1.0'#10;
{--- Конец прокси ---}
{--- Начало соединения напрямую --- }
{ удалите этот код, еслы Вы будете использовать прокси }
ClientSocket1.Host := WebServer;
ClientSocket1.Port := WebPort;
HTTP_POST := 'POST '+PostAddr+' HTTP/1.0'#10;
{--- Конец соединения напрямую ---}
// Соединяем заголовок
HTTP_Post := HTTP_Post + HTTP_Data;
// Пытаемся открыть соединение
ClientSocket1.Open;
end;
|
Как разделить обработку OnClick и OnDblClick.
Ведь OnClick будет вызываться всегда, и перед DblClick.Именно так и происходит в Windows - посылаются оба сообщения. Для того чтобы обработать только какое-то одно событие необходимо чуть " задержать" выполнение OnClick. Сделать это можно следующим способом:
procedure TForm1.ListBox1Click(Sender: TObject);
var
Msg: TMsg;
TargetTime: Longint;
begin
{ get the maximum time to wait for a double-click message }
TargetTime := GetTickCount + GetDoubleClickTime;
{ cycle until DblClick received or wait time run out }
while GetTickCount < TargetTime do
if PeekMessage(Msg, ListBox1.Handle, WM_LBUTTONDBLCLK,
WM_LBUTTONDBLCLK, WM_NOREMOVE)
then Exit; { Double click }
MessageDlg('Single clicked', mtInformation, [mbOK], 0);
end;
|
Как разделить файл на куски и склеить их потом.
| Диск С: выполнил недопустимую операцию и будет закрыт |
CONST
MaxSize : Longint = 1440000; //байт
function ExtractFileNames(FileNames:string):string;
var
S:string;
begin
S:='';
while Pos('.', FileNames) > 0 do
begin
S:=S+Copy(FileNames,1,Pos('.',FileNames)-1);
Delete(FileNames,1,Pos('.',FileNames));
end;
result:=S;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
InFile,OutFile: FILE;
CopyBuffer : POINTER; { buffer for copying }
iRecsOK, iRecsWr, index: Integer;
sFileName,sFileExt,sFileFullName:string;
fFileSize: file of Byte;
Size:LongInt;
begin
sFileFullName:='C:\1\1.mp3';
sFileName:=ExtractFileName(sFileFullName);
sFileExt:=ExtractFileExt(sFileName);
sFileName:=ExtractFileNames(sFileName);
ShowMessage(sFileFullName+#13+sFilename+#13+sFileExt);
if FileExists(sFileFullName) then
begin
AssignFile(fFileSize, sFileFullName);
FileMode := 0; {Set file access to read only }
Reset(fFileSize);
Size := FileSize(fFileSize); {Get File Size}
CloseFile(fFileSize);
ShowMessage(IntToStr(Size));
if Size>MaxSize then
begin {Divide}
Getmem(CopyBuffer, MaxSize); { allocate the buffer }
Assignfile(inFile,sFileFullName );//+ '.ZIP');
Reset(inFile,1);
index := 1;
repeat
AssignFile(outFile,sFileName + '-'+IntToStr(index) + sFileExt);
Rewrite(OutFile,1);
inc(index);
BlockRead(InFile, CopyBuffer^, MaxSize, iRecsOK);
BlockWrite(OutFile, CopyBuffer^, iRecsOK, iRecsWr);
CloseFile(OutFile);
until (iRecsOK < MaxSize);
CloseFile(InFile);
FreeMem(CopyBuffer, MaxSize); { free the buffer }
ShowMessage('Done..!');
end
else
begin
ShowMessage('Do nothing..!');
end;
end
else
ShowMessage('File: '+sFileFullName+' not found');
end;
// Чтобы склеить файлы обратно в один,
// можно воспользоваться DOS функцией copy;
// copy /b file-1.xxx+file-2.xxx+file-3.xxx file.mp3
|
Дилемма доступа к данным.
Оформил: DeeCo
Автор: Марко Кэнту (Marco Cantu) (Перевод - Юрия Елкина) Оригинал можно прочесть здесь
По традиции, приложения Delphi использовали технологию BDE для доступа к данным. Но с появлением Delhi 5 появилась новая возможность - ADO. С момента появления Delphi 5, который включает технологию ADOExpress ( она входит в Delphi Enterprise или продается как отдельная опция к Delphi Professional), программисты встретились с выбором: что использовать BDE или ADO для доступа к БД ? Типичным ответом на вопрос "Какую технологию Баз Данных использовать ?" будет - "Это зависит от ..." Технология BDE ( http://www.borland.com/bde ) имеет длинную и славную историю. Она появилась в Delphi 1 в качестве механизма для доступа к таблицам Paradox и, позднее, была частью ISAPI - инициативы поддерживаемой IBM, Novell и WordPerfect. Несмотря на некоторые проблемы, BDE является одной из причин успеха Delphi на арене Баз Данных и достигнув версии 5 она является достаточно зрелой технологией. И хотя есть несколько проблем в процессе программирования и установки готовых программ, Delphi - программисты научились жить с BDE.Несколько белых пятен.
Так зачем уходить от BDE ? Разворачивание ее на арендуемых Internet серверах зачастую невозможно из-за неодобрения ISР'ом запуска сервисов системного уровня на своих серверах. И хотя BDE была усилена для поддержки таких возможностей, как обьектно-реляционная модель Oracle 8, некоторые из ее возможностей все еще сдерживаются ее Paradox корнями. Например, способ сопоставления числовых полей Базы Данных и типов переменных создает проблемы совместимости при работе Delphi с различными SQL серверами. Другая проблема в том, что BDE полностью включает ядро, используемое Paradox и dBase для доступа к данным. Нет возможности поставлять облегченную версию BDE исключив поддержку Раrаdох'а, если вы работаете только с SQL серверами. (С другой стороны, некоторые из этих проблем, такие как работа SQL сервера на ISP сервере, актуальны и для использования ADO.) В конце концов, с приходом Kylix (технология Delphi и C++Builder для Linux), мы увидим версию Delphi которая не сможет использовать BDE, но очевидно будет базироваться на новом облегченном ядре. Если для BDE нет места в будущем Delphi для Linux, она врядли может считаться будущим и для Windows. Borland продолжает утверждать, что BDE остается, но как мы видим за последние пару лет разрабатывается очень мало. Технология ActiveX Data Objects (ADO) это разработка Microsoft. Она предоставляет упрощенный способ для доступа к данным основанный на OLE DB, сама же мощная лошадка остается за сценой. Программировать непосредственно на OLE DB уровне достаточно сложно, поэтому Microsoft предоставляет более простое решение. Включая технологию ADOExpress в Delphi Borland принял ADO в качестве общей технологии и признал Microsoft Access, как широко распространенный механизм доступа к БД (Прим.перев.: с полгода назад Microsoft заключил серию соглашений с Inprise- Microsoft заплатил в общей сложности порядка 250 млн. долларов за поддержку его технологий в продуктах Inprise). Не смотря на то, что в последнее время вы уже могли использовать БД Access через BDE, возможности все же были ограничены. Microsoft также установил несколько препятствий на дороге - таких как сокрытие новой версии DAO, фронтэнд Access'a обычно используемый в Visual Basic - чтобы подтолкнуть других поставщиков средств разработки для Windows в сторону ADO. Так или иначе, ADO это достаточно интересная технология. Она оптимально "легковесная", достаточно мощная, предоставляет доступ к данным вне реляционных БД, и Microsoft пытается продвинуть ее в качестве открытого стандарта. Вы даже можете написать OLE DB провайдера для ADO в Delphi.Потеря управления.
Хотя ADO и умное решение, есть одна вещь, которая мне не нравится: вы теряете контроль, который предоставляет BDE. BDE ограничена в своей возможности обновлять результат запроса, поэтому чтобы использовать живые запросы (live queries) в Delphi вы часто используете компонент UpdateSql, а с SQL предложениями для замены, вставки и удаления - компонент Query. Это может показаться неудобным, но на самом деле позволяет сделать значительную тонкую настройку вашего приложения. ADO с другой стороны, делает много работы, чтобы обновлять результат запроса без какого либо дополнительного кодирования, но у вас нет контроля над SQL-кодом посылаемым серверам, если только Вы не разместили собственное решение отбросив живые запросы. Некоторая тонкая настройка доступна в ADO через использование DataSet'a, курсора, и блокировочных опций, но эти возможности ведут себя по разному на разных SQL серверах и, как оказывается, тонко настраиваются только для Microsoft SQL Server и Access. Рассмотрим последнее подробнее: Несмотря на то, что использование ADO для доступа к нескольким базам данных является хорошей идеей, есть несколько возможностей в ADO , которые похоже связаны только с Access. Например, Access является единственной, из всех что я знаю, базой данных, в которой вы имеете возможность блокирования записей при чтении на случай, если вы захотите редактировать их в будущем. Как и BDE включает в себя некоторые Paradox-специфичные возможности, так и ADO включает несколько возможностей, которые более Access-ориентированы, чем должно было бы предоставлять универсальное средство доступа к данным. Некоторые из наиболее интересных возможностей ADO связаны с использованием курсоров со стороны клиента. Вы можете скопировать полностью набор данных на клиентский компьютер и выполнить ряд операций на этом кэше, включая сортировку, фильтрацию и редактирование данных. Вы можете даже скопировать часть данных в локальный файл и работать с ним в режиме offline. Примеры, как это сделать, есть в Delphi 5/demos и на моем сайте . BDE тоже выполняет кэширование на локальном компьютере, но не позволит вам работать с кэшем. Однако, довольно много Delphi программистов научились использовать компонент ClientDataSet для работы с кэшированными данными. Я исследовал этот предмет и пришел к заключению, что ClientDataSet делает все, что делает клиентский (client-side) курсор ADO, включая сортировку, фильтрацию и локальное кэширование выборки из набора данных (local snapshots). Обе технологии позволяют вам создать локальный набор данных наложенный (mapped to) на файл в удаленной базе данных без соединения с ней. Обе имеют поддержку для XML - хотя и различными путями. Обе могут быть использованы для построения трехуровневых приложений. И обе предоставляют вложенные (nested) таблицы для создания master-detail представлений данных. В дополнение, компонент ClientDataSet предоставляет некоторые продвинутые возможности не поддерживаемые в ADO: группирование, аггрегирование, вычисляемые поля и поддержка абстрактных типов данных ( вот пример ). ClientDataSet и связанный с ним компонент провайдера предоставляет намного больше возможностей управления, чем в ADO. Возможно не только точно указать как будет выпоняться запись данных, но у вас имеются большие возможности для обработки конфликтов записи.Обилие опций.
ADO и BDE не единственная альтернатива для доступа к данным в Delphi. Есть и другие компоненты наборов данных производимых Borland и третьими фирмами для непосредственного доступа к SQL серверам, таким как Oracle и Interbase. Есть также альтернативы для ADO - это разработки, которые предоставляют прямой доступ к DAO, и другие альтернативные технологии поддерживающие от Btrieve до AS/400. Но я все еще не нашел решения для прямого OLE DB доступа. BDE больше не является общим знаменателем для доступа к базам данных в Delphi. Я даже написал пару dаtаsеt'ов для доступа к данным не из баз данных. Вы, разумеется, не нуждаетесь в ADO и специальных OLE DB провайдерах для этого. Oracle, один из предпочтительных back-end серверов для Delphi приложений, наслаждается таким широкораспространенным использованием (колоритная фраза, я не стал ее "русифицировать"- прим.перев.), что я подозреваю, что успех ADO стратегии в значительной степени зависит от поддержки Oracle - по крайней мере с точки зрения перспективы для Delphi разработчиков. Очевидно, что поставляемый Мiсrоsоft'ом OLE DB провайдер для Oracle на сегодня не является ни живучим, ни полным решением. Если Oracle не подтолкнет ADO, то будущее этой технологии не видится столь ярким. А поскольку Microsoft наращивает ADO в направлении к собственным реализациям баз данных, то становится понятным сопративление Oracle'a продвижению ADO. Самое большое преимущество технологии ADO в том, что она широко распространена в Windows и продвигается фирмой Microsoft. Если вам нужен доступ к базам данных Microsoft SQL Server или Access, то вы вероятно предпочтете использовать ADO. Если вы используете Paradox или Interbase, тогда вам вероятно лучше поставить на BDE - если только вы не погрузились на корабль "InterBase Express". С таким большим количеством вопросов в программном бизнесе, ответом на "Какую технологию мне надо бы использовать ?" будет : "Это зависит от ..."Показать число с разделителями тысячных знаков.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function AddThousandSeparator(S: string; Chr: Char): string;
var
I: Integer;
begin
Result := S;
I := Length(S) - 2;
while I > 1 do
begin
Insert(Chr, Result, I);
I := I - 3;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := AddThousandSeparator('400500210', '''');
// -- 400'500'210
end;
// Other possibility / andere Мцg1iсhкеit: ThousandSeparator := '''';
label1.Caption := FormatFloat('#,###,###.###', 23453452);
// --> 23'453'452
Регистрация основанная на серийных ключах.
Автор: Владимир Каталов Эту статейку написал скорее всего Владимир Каталов на тему защиты shareware-программ, но я думаю что она будет полезна не только программистам, но и крэкерам тоже. При хорошем знании ассемблера очень просто обойти защиту типа имя пользователя-регкод: когда пользователь вводит свое имя и регистрационный код, программа генерирует "правильный" код и сравнивает его с тем, который он ввел, поэтому "перехватить" этот код (просто считать его из памяти) не составлет особого труда. В большинстве случаев достаточно просто поставить breakpoint на функцию lstrcmp -- и все дела... Упаковка программы, анти-дебаггерные и анти-дизассемблерные "примочки" помогают слабо. А вот метод фиксированного ключа может быть реализован достаточно неплохо. Хранить "правильные" ключи в программе совсем не обязательно - можно подчинить их некоторым правилам; что-то подобное делает Microsoft со своими CD-keys, но у них все слишком просто. Алгоритм проверки может быть длинным и запутанным, так что его дизассемблирование (и "разборка", что же он делает) причинит немало головной боли. Именно первый метод я и применил для защиты своей программы (Advanced Disk Catalog - старые версии), но и он был "сломан" (хотя, как мне написал ломавший его хакер, подборка всего двух правильных ключей отняла у него много времени). Тогда мне в голову пришла идея: а что, если ключи хранить внутри программы, но зашифрованными? Я "сгенерил" некоторое количество ключей (абсолютно случайным образом), зашифровал их (по отдельности) 128-битным ключом по алгоритму RSA и прошил в программу в виде ресурса. Когда пользователь вводит ключ, он шифруется по тому же алгоритму и сравнивается с правильными. Так как система с открытым ключом не позволяет произвести обратное преобразование, базируясь только на открытом ключе (а закрытого нет даже у меня - расшифровывать-то не надо), то подобрать ключи невозможно даже теоретически. Есть, впрочем, еще одна проблема: хакер (или "крэкер", если угодно) может заменить 'je' на 'jne' (или что-то в этом роде) там, где происходит последняя проверка, и функция "IsValidKey(...)" будет всегда возвращать TRUE. Останется лишь написать маленький patch... Чтобы защититься и от этого, я вычисляю CRC своего exe-файла и сравниваю его с правильный, прошитым тоже внутри программы (естественно, при вычислении эта часть файла -- где лежит правильный CRC -- исключается; а "прошивается" он после компиляции). Кстати, это еще и защита от вирусов. Вообще-то, проверку CRC тоже можно локализовать и "запатчить", но это уже немного сложнее, особенно если программа вызывает функции чтения/записи и для других целей. Кроме того, не стоит в случае несовпадения CRC сразу об этом сообщать, иначе можно будет поставить hardware breakpoint и найти место, где он вычисляется. И последнее. Если хочется защитить программы совсем уж "круто", томожно несколько функций в своей программе (те, которые должны вызываться только в зарегистрированной версии), зашифровать по тому же алгоритму с открытым ключом. При этом, естественно, часть серийного номера (отсылаемого зарегистрировавшимся) надо сделать "статическим", т.е. неизменным для всех пользователей. На основе этой части после регистрации генерируется полный закрытый ключ, который далее используется для расшифровки указанных функций. Таким образом, даже если будет написан patch, позволяющий "зарегистрироваться" с любым (произвольным) кодом, расшифровка пройдет неправильно, и вместо нормального кода будет выполняться "мусор". Этот способ я применил в другой своей программе (Advanced ZIP Password Recovery), и ее пока не вскрыли. Всего наилучшего !Комментарий от Bad_guy: действительно, если некоторые нужные функции зашифрованы, расшифровать их практически невозможно, и я бы сказал, что для защиты программы этого метода с лихвой достаточно, но никогда не надо забывать, что есть такой способ взлома как кража правильного кода (хотя такой метод противоречит крэкерской этике, да и не крэкерство это вовсе). На мой же взгляд программа - это сейф, содержимое которого вам известно, и прежде чем начинать взламывать этот сейф надо подумать: так ли вам нужно его содержимое ? Автор вот говорит - никто не взломает Advanced ZIP Password Recovery, ну и пускай спит спокойно, а я пока поищу бесплатный аналог (если припрёт).
Читать и писать в последовательный порт.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function OpenCOMPort: Boolean;
var
DeviceName: array[0..80] of Char;
ComFile: THandle;
begin
{ First step is to open the communications device for read/write.
This is achieved using the Win32 'CreateFile' function.
If it fails, the function returns false.
Wir versuchen, COM1 zu offnen.
Sollte dies fehlschlagen, gibt die Funktion false zuruck.
}
StrPCopy(DeviceName, 'COM1:');
ComFile := CreateFile(DeviceName,
GENERIC_READ or GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if ComFile = INVALID_HANDLE_VALUE then
Result := False
else
Result := True;
end;
function SetupCOMPort: Boolean;
const
RxBufferSize = 256;
TxBufferSize = 256;
var
DCB: TDCB;
Config: string;
CommTimeouts: TCommTimeouts;
begin
{ We assume that the setup to configure the setup works fine.
Otherwise the function returns false.
wir gehen davon aus das das Einstellen des COM Ports funktioniert.
sollte dies fehlschlagen wird der Ruckgabewert auf "FALSE" gesetzt.
}
Result := True;
if not SetupComm(ComFile, RxBufferSize, TxBufferSize) then
Result := False;
if not GetCommState(ComFile, DCB) then
Result := False;
// define the baudrate, parity,...
// hier die Baudrate, Paritat usw. konfigurieren
Config := 'baud=9600 parity=n data=8 stop=1';
if not BuildCommDCB(@Config[1], DCB) then
Result := False;
if not SetCommState(ComFile, DCB) then
Result := False;
with CommTimeouts do
begin
ReadIntervalTimeout := 0;
ReadTotalTimeoutMultiplier := 0;
ReadTotalTimeoutConstant := 1000;
WriteTotalTimeoutMultiplier := 0;
WriteTotalTimeoutConstant := 1000;
end;
if not SetCommTimeouts(ComFile, CommTimeouts) then
Result := False;
end;
{
The following is an example of using the 'WriteFile' function
to write data to the serial port.
Folgendes Beispiel verwendet die 'WriteFile' Funktion, um Daten
auf den seriellen Port zu schreiben.
}
procedure SendText(s: string);
var
BytesWritten: DWORD;
begin
{
Add a word-wrap (#13 + #10) to the string
An den ubergebenen String einen Zeilenumbruch (#13 + #10) hangen
}
s := s + #13 + #10;
WriteFile(ComFile, s[1], Length(s), BytesWritten, nil);
end;
{
The following is an example of using the 'ReadFile' function to read
data from the serial port.
Folgendes Beispiel verwendet die 'ReadFile' Funktion, um Daten
vom seriellen Port zu lesen.
}
procedure ReadText: string;
var
d: array[1..80] of Char;
s: string;
BytesRead, i: Integer;
begin
Result := '';
if not ReadFile(ComFile, d, SizeOf(d), BytesRead, nil) then
begin
{ Raise an exception }
end;
s := '';
for i := 1 to BytesRead do s := s + d[I];
Result := s;
end;
procedure CloseCOMPort;
begin
// finally close the COM Port!
// nicht vergessen den COM Port wieder zu schliessen!
CloseHandle(ComFile);
end;
Инсталляция и удаление сервисов под НТ.
Автор: Alex Kantchev WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Инсталляция/удаление сервисов под НТ.
Функции для создавания и удаления NT Services.
Можно создать NT Service от текущее приложение. Параметры:
1. CreateNTService(ExecutablePath,ServiceName: String)
ExecutablePath - Полный путь к изполнимого файла от которого создавается NT Service
ServiceName - Имя сервиза которое отобразится в Service Control Manager
Результат:
true - если операциая завершена успешно
false - если есть ошибка. Можно произвести call то
GetLastError чтобы информироваться об естество ошибки
2. DeleteNTService(ServiceName: String):boolean;
ServiceName - имя сервиза подлежающии удаления
Результат:
true - если операциая завершена успешно
false - если есть ошибка. Можно произвести call то GetLastError чтобы
информироваться об естество ошибки
Зависимости: WinSVC, Windows
Автор: Alex Kantchev, stoma@bitex.bg
Copyright: Собственное написание
Дата: 19 июня 2002 г.
***************************************************** }
// CreateNTService(ExecutablePath,ServiceName: String)
// ExecutablePath - Полный путь к изполнимого файла от
// которого создавается NT Service
// ServiceName - Имя сервиза которое отобразится
// в Service Control Manager Результат:
//Результат:
// true - если операциая завершена успешно
// false - если есть ошибка. Можно произвести
// call то GetLastError чтобы информироваться об
// естество ошибки
function CreateNTService(ExecutablePath, ServiceName: string): boolean;
var
hNewService, hSCMgr: SC_HANDLE;
// Rights: DWORD;
FuncRetVal: Boolean;
begin
FuncRetVal := False;
hSCMgr := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
if (hSCMgr <> 0) then
begin
//Custom service access rights may be built here
//we use GENERIC_EXECUTE which is combination of
//STANDARD_RIGHTS_EXECUTE, SERVICE_START, SERVICE_STOP,
//SERVICE_PAUSE_CONTINUE, and SERVICE_USER_DEFINED_CONTROL
//You can create own rights and use them as shown in the
//commented line below.
//Rights := STANDARD_RIGHTS_REQUIRED or SERVICE_START or SERVICE_STOP
// or SERVICE_QUERY_STATUS or SERVICE_PAUSE_CONTINUE or
// SERVICE_INTERROGATE;
hNewService := CreateService(hSCMgr, PChar(ServiceName), PChar(ServiceName),
STANDARD_RIGHTS_REQUIRED, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
PChar(ExecutablePath), nil, nil, nil, nil, nil);
CloseServiceHandle(hSCMgr);
if (hNewService <> 0) then
FuncRetVal := true
else
FuncRetVal := false;
end;
CreateNTService := FuncRetVal;
end;
// ***
//DeleteNTService(ServiceName: String):boolean;
// ServiceName - имя сервиза подлежающии удаления
//Результат:
// true - если операциая завершена успешно
// false - если есть ошибка. Можно произвести call то GetLastError чтобы
// информироваться об естество ошибки
function DeleteNTService(ServiceName: string): boolean;
var
hServiceToDelete, hSCMgr: SC_HANDLE;
RetVal: LongBool;
FunctRetVal: Boolean;
begin
FunctRetVal := false;
hSCMgr := OpenSCManager(nil, nil, SC_MANAGER_CREATE_SERVICE);
if (hSCMgr <> 0) then
begin
hServiceToDelete := OpenService(hSCMgr, PChar(ServiceName),
SERVICE_ALL_ACCESS);
RetVal := DeleteService(hServiceToDelete);
CloseServiceHandle(hSCMgr);
FunctRetVal := RetVal;
end;
DeleteNTService := FunctRetVal;
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var
tmpS: string;
begin
tmpS := 'Delphi_Service_' + Application.Title;
if (CreateNTService(Application.ExeName, tmpS)) then
MessageDlg('Service ' + tmpS + ' has been successfully created!',
mtInformation, [mbOK], 0)
else
MessageDlg('Unable to create service ' + tmpS + ' Win32 Error code: ' +
IntToStr(GetLastError), mtWarning, [mbOK], 0);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
tmpS: string;
begin
tmpS := 'Delphi_Service_' + Application.Title + '1';
if (DeleteNTService(tmpS)) then
MessageDlg('Service ' + tmpS + ' has been successfully deleted!',
mtInformation, [mbOK], 0)
else
MessageDlg('Unable to delete service ' + tmpS + ' Win32 Error code: ' +
IntToStr(GetLastError), mtWarning, [mbOK], 0);
end;
Как заказать сервисный процесс.
Автор: Admin WEB-сайт: http://forum.vingrad.ru
unit Stealth;
interface
uses
WinTypes, WinProcs, Classes, Forms, SysUtils, Controls, Messages;
type
TStealth = class(TComponent)
private
fHideApp: Boolean;
procedure SetHideApp(Value: Boolean);
protected
{ Protected declarations }
procedure HideApplication;
procedure ShowApplication;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// procedure Loaded; override;
published
{ Published declarations }
property HideApp: Boolean read fHideApp write SetHideApp default false;
end;
function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer; stdcall;
external 'KERNEL32.DLL';
procedure Register;
implementation
destructor TStealth.Destroy;
begin
ShowApplication;
inherited destroy;
end;
constructor TStealth.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
// fHideform := true;
end;
procedure TStealth.SetHideApp(Value: Boolean);
begin
fHideApp := Value;
if Value then
HideApplication
else
ShowApplication;
end;
procedure TStealth.HideApplication;
begin
if not (csDesigning in ComponentState) then
RegisterServiceProcess(GetCurrentProcessID, 1);
end;
procedure TStealth.ShowApplication;
begin
if not (csDesigning in ComponentState) then
RegisterServiceProcess(GetCurrentProcessID, 0);
end;
procedure Register;
begin
RegisterComponents('My', [TStealth]);
end;
end.
SetFocus в TStringGrid.
Grid.Row := YourRowNumber; Grid.TopRow := YourFirstVisibleRow; Grid.LeftCol := YourFirstVisibleColumn; |
SetFocus в Edit на OnExit.
| Встречаются как то НАШ Российский хакер и ихний БИЛ ГЕЙТС! ГЕЙТС: Слушай у тебя случайно Wind(Ы)2000 нет, а то Рождество на Носу, а подарить друзьям нечего? Все деньги жена забирает! ХАКЕР: Есть! Что за вопросы! А ТЕБЕ КАКУЮ, Русскую или Английскую? |
Следующие строки содержат необходимый код:
interface
........
const
WM_MyExitRtn = WM_USER + 1001;
........
type
TForm1 = class(TForm)
.........
private
bExitInProgress: Boolean; {предохраняемся от рекурсий сообщений}
public
procedure WMMyExitRtn(var msg: TMessage); message WM_MyExitRtn;
end;
.........
implementation
.........
procedure TForm1.DBEdit1Exit(Sender: TObject);
begin
if not bExitInProgress then
PostMessage(Handle, WM_MyExitRtn, 0, LongInt(Sender));
end;
.........
procedure TForm1.WMMyExitRtn(var msg: TMessage);
begin
bExitInProgress := True; { предохраняемся от рекурсивного вызова }
{здесь содержится необходимый код }
bExitInProgress := False; { сбрасываем флаг }
end;
|
Правила для SetRange.
Автор: Josh Я, похоже, обнаружил неплохое решение ранжирования данных - вам необходимо только установить различные начальный и конечный диапазоны последнего поля, определенного вашим индексом, после чего введенное в любое поле индекса значение будет проигнорировано. Также вы не сможете покинуть ранжируемое поле, если редактируемая запись пуста.Попытаюсь изложить все попроще... Скажем, у меня есть индекс Field1; Field2; Field3, затем;
SetRangeStart; Table1Field1.Value := x1; Table1Field2.Value := y1; Table1Field3.Value := z1; SetRangeEnd; Table1Field1.Value := x2; Table1Field2.Value := y2; Table1Field3.Value := z2; ApplyRange; |
X1 должен равняться x2, если y или z определен.
Y1 должен равняться y2, если z определен.
X должен быть определен, если y или z определены.
Y должен быть определен, если x определен.
Если x1 = x2 и никаких других критериев не определено, тогда y1 и y2 должны быть соответственно min/max значениями y.
Если x1 = x2 и y1 = y2 и никаких других критериев не определено, тогда z1 и z2 должны быть соответственно min/max значениями z.
Я не знаю, поняли вы это или нет, но надеюсь это поможет...
Пример SetText, GetText строкового поля.
Автор: Mike OrrissНиже приведен простой пример поля ID, содержащего A,B,C, для вывода (и обновления) *всех* элементов управления как 1,2,3:
procedure TForm1.Table1IDGetText(Sender: TField;
var Text: OpenString; DisplayText: Boolean);
var
s: string;
begin
s := (Sender as TStringField).Value;
case s[1] of
'A': Text := '1';
'B': Text := '2';
'C': Text := '3';
else
Text := '9'
end;
end;
procedure TForm1.Table1IDSetText(Sender: TField; const Text: string);
var
s: string;
begin
case Text[1] of
'1': s := 'A';
'2': s := 'B';
'3': s := 'C';
else
s := 'Z'
end;
(Sender as TStringField).Value := s;
end;
|
Установка привилегий.

| Не забуду мать родную - ZX Spectrum навсегда! |
{
For some functions you need to get the right privileges
on a Windows NT machine.
(e.g: To shut down or restart windows with ExitWindowsEx or
to change the system time)
The following code provides a procedure to adjust the privileges.
The AdjustTokenPrivileges() function enables or disables privileges
in the specified access token.
}
// NT Defined Privileges from winnt.h
const
SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege';
SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege';
SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege';
SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege';
SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege';
SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege';
SE_TCB_NAME = 'SeTcbPrivilege';
SE_SECURITY_NAME = 'SeSecurityPrivilege';
SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege';
SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege';
SE_SYSTEM_PROFILE_NAME = 'SeSystemProfilePrivilege';
SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege';
SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege';
SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege';
SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege';
SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege';
SE_BACKUP_NAME = 'SeBackupPrivilege';
SE_RESTORE_NAME = 'SeRestorePrivilege';
SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
SE_DEBUG_NAME = 'SeDebugPrivilege';
SE_AUDIT_NAME = 'SeAuditPrivilege';
SE_SYSTEM_ENVIRONMENT_NAME = 'SeSystemEnvironmentPrivilege';
SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege';
SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege';
SE_UNDOCK_NAME = 'SeUndockPrivilege';
SE_SYNC_AGENT_NAME = 'SeSyncAgentPrivilege';
SE_ENABLE_DELEGATION_NAME = 'SeEnableDelegationPrivilege';
SE_MANAGE_VOLUME_NAME = 'SeManageVolumePrivilege';
// Enables or disables privileges debending on the bEnabled
function NTSetPrivilege(sPrivilege: string; bEnabled: Boolean): Boolean;
var
hToken: THandle;
TokenPriv: TOKEN_PRIVILEGES;
PrevTokenPriv: TOKEN_PRIVILEGES;
ReturnLength: Cardinal;
begin
Result := True;
// Only for Windows NT/2000/XP and later.
if not (Win32Platform = VER_PLATFORM_WIN32_NT) then Exit;
Result := False;
// obtain the processes token
if OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
begin
try
// Get the locally unique identifier (LUID) .
if LookupPrivilegeValue(nil, PChar(sPrivilege),
TokenPriv.Privileges[0].Luid) then
begin
TokenPriv.PrivilegeCount := 1; // one privilege to set
case bEnabled of
True: TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
False: TokenPriv.Privileges[0].Attributes := 0;
end;
ReturnLength := 0; // replaces a var parameter
PrevTokenPriv := TokenPriv;
// enable or disable the privilege
AdjustTokenPrivileges(hToken, False, TokenPriv, SizeOf(PrevTokenPriv),
PrevTokenPriv, ReturnLength);
end;
finally
CloseHandle(hToken);
end;
end;
// test the return value of AdjustTokenPrivileges.
Result := GetLastError = ERROR_SUCCESS;
if not Result then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
|
SetVolume почему-то не дает никакого эффекта.
| Новая компьютерная игра МУ-МУ. Герасим использует Му-му по умолчанию. |
В модуле MMSystem есть функции.
waveOutSetVolume(WAVE_MAPPER, AVolume) midiOutSetVolume(MIDI_MAPPER, AVolume)Которые регулируют громкость в зависимости от формата музыки.
AVolume: Longint - старший разряд - правый канал, младший - левыйВыставить значение для определенного свойства всех компонент формы или всех компонент нужного класса.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.SetProperties(ClassName, SomeProperty: string; Value: Integer);
var
i: integer;
PropInfo: PPropInfo;
Component: TComponent;
begin
for i := 0 to ComponentCount - 1 do
begin
Component := Components[i];
if (Component is TControl) and ((Component.ClassName = ClassName)
or (ClassName = 'AllComponents')) then
begin
PropInfo := GetPropInfo(Component.ClassInfo, SomeProperty);
if Assigned(PropInfo) then
SetOrdProp(Component, PropInfo, Integer(Value));
end;
end;
end;
// Examples, Beispiele:
procedure TForm1.Button1Click(Sender: TObject);
begin
// Make all components readonly
SetProperties('AllComponents', 'readonly', 1);
// Make all components of Class TEdit invisible
SetProperties('TEdit', 'visible', 0);
// Set the Height Property of all TLabels
SetProperties('TLabel', 'Height', 30);
// Set ShowHint to false for all components
SetProperties('AllComponents', 'ShowHint', 0);
// Disable all TMemo Components
SetProperties('TMemo', 'Enabled', 0);
// Set Autosize to true for all TLabels
SetProperties('TLabel', 'Autosize', 1);
end;
Установка режима бинарного файла.
Автор: SteveПопробуйте вызвать приведенную ниже процедуру немедленно после перезаписи выходного файла и перед началом записи в него:
procedure SetBinaryMode(var F: file); assembler; asm mov ax,$4400 les di,F mov bx,word ptr es:[di] int $21 or dl,$20 xor dh,dh mov ax,$4401 int $21 end; |
Установка определенного бита в слове.
Автор: Григорий Ситнин WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Установка определенного бита в слове
Возвращает AWord с установленным битом номер ABit (c 0 по 15) в значение 1,
если AState = TRUE или 0, если AState = FALSE.
Стоит заметить, что в функцию передается именно номер бита, а не маска.
Проверка на правильный номер не производится.
Зависимости: System
Автор: Григорий Ситнин, gregor@gregor.ru, Москва
Copyright: Григорий Ситнин, 2003
Дата: 8 июля 2003 г.
***************************************************** }
function SetBit(AWord: word; ABit: byte; AState: boolean = true): word;
begin
if AState then
Result := AWord or (1 shl ABit)
else
Result := AWord and (not (1 shl ABit));
end;
Пример использования:
{$APPTYPE CONSOLE}
program test;
uses Bits; // В модуле Bits описана функция SetBit
var
z: word;
i: integer;
s: string;
begin
z := 0;
z := setbit(z, 0); // 0000000000000001
z := setbit(z, 2); // 0000000000000101
z := setbit(z, 9); // 0000001000000101
z := setbit(z, 14); // 0100001000000101
z := setbit(z, 15); // 1100001000000101
s := '';
for i := 15 downto 0 do
s := s + inttostr(Ord(CheckBit(z, i)));
writeln('value: ', z, ' dec = 1100001000000101');
writeln('result: ', s);
z := $FFFF;
z := setbit(z, 0, false); // 1111111111111110
z := setbit(z, 2, false); // 1111111111111010
z := setbit(z, 9, false); // 1111110111111010
z := setbit(z, 14, false); // 1011110111111010
z := setbit(z, 15, false); // 0011110111111010
s := '';
for i := 15 downto 0 do
s := s + inttostr(Ord(CheckBit(z, i)));
writeln('value: ', z, ' dec = 0011110111111010');
writeln('result: ', s);
end.
Установка времени компиляции программы в диалоговом окне О программе.
Я подразумаваю, что проблема состоит в получении времени компиляции?
Var F: Integer; S: String; Begin F:=FileOpen(ExpandFileName(Application.ExeName), 0); S:=TimeToStr(FileDateToDateTime(FileGetDate(F))); FileClose(F); End; |
Также можно использовать время файла (File Time) в качестве номера версии, так, время 6:02 обозначало бы версию 6.02, и устанавливать его чем-то типа Touch.
Установить флаг DTR и RTS в активное состояние для определённого COM-порта.
Автор: Slava VПосле открытия com-порта через CreateFile() необходимо с помощью GetCommState() и SetCommState() установить параметры порта (в частности убрать автоуправление RTS и DTR). Затем для установки соответствующих сигналов используется функция EscapeCommFunction().
Procedure ControlRTS;
Var
S: String;
lDCB: TDCB;
fHandle: THandle;
Begin
S:='COM1';
// open port
fHandle:=CreateFile( Pchar(S), GENERIC_READ Or GENERIC_WRITE, 0,
Nil, OPEN_EXISTING, 0, 0);
If fHandle=INVALID_HANDLE_VALUE Then
Begin
// can't open....
Exit;
End;
// read settings
If Not GetCommState( fHandle, lDCB) Then
Begin
// can't read
Exit;
End;
// Fill dcb
lDCB.BaudRate:=CBR_2400;
lDCB.ByteSize:=8;
lDCB.Parity:=NOPARITY;
lDCB.StopBits:=ONESTOPBIT;
// !!! we will manage RTS ourself !!!
ldcb.Flags:=(ldcb.Flags And $FFFFC0FF) Or $00000100;
// set comm state
SetCommState( fHandle, ldcb);
// Here we can manage
// Reset RTS
EscapeCommFunction( fHandle, CLRRTS);
// Set RTS
EscapeCommFunction( fHandle, SETRTS);
// Close port
CloseHandle(fHandle);
fHandle:=0;
End;
|
P.S. Надо заметить, что все выходы com-порта являются инверсными. А это значит, что активным сосотоянием порта является 0, а неактивным 1.
Как программно установить конфигурацию COM-порта.
procedure TForm1.Button1Click(Sender: TObject);
var
CommPort: string;
hCommFile: THandle;
Buffer: PCommConfig;
size: DWORD;
begin
CommPort := 'COM1';
{Открываем Com-порт}
hCommFile := CreateFile(PChar(CommPort),
GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hCommFile = INVALID_HANDLE_VALUE then
begin
ShowMessage('Unable to open ' + CommPort);
exit;
end;
{Выделяем временный буфер}
GetMem(Buffer, sizeof(TCommConfig));
{Получаем размер структуры CommConfig}
size := 0;
GetCommConfig(hCommFile, Buffer^, size);
{Освобождаем временный буфер}
FreeMem(Buffer, sizeof(TCommConfig));
{Выделяем память для структуры CommConfig}
GetMem(Buffer, size);
GetCommConfig(hCommFile, Buffer^, size);
{Изменяем скорость передачи}
Buffer^.dcb.BaudRate := 1200;
{Устанавливаем новую конфигурацию для COM-порта}
SetCommConfig(hCommFile, Buffer^, size);
{Освобождаем буфер}
FreeMem(Buffer, size);
{Закрываем COM-порт}
CloseHandle(hCommFile);
end;
|
Установка или снятие Debug привелегии у текущего процесса.
Автор: Мироводин Дмитрий WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Установка/снятие Debug привелегии у текущего процесса
Функция устанавливает/снимает отладочные привелегии у текущего процесса
(можно выбрать и другой, изменив GetCurrentProcess на нужный Handle ).
Актуально для совместного использования с ToolHelp -
т.е. получения информации о процессах.
Зависимости: Windows
Автор: Мироводин Дмитрий (адаптация), mirovodin@mail.ru
Copyright: 2000 Jeffrey Richter
Дата: 20 октября 2003 г.
***************************************************** }
function EnableDebugPrivilege(const Value: Boolean): Boolean;
const
SE_DEBUG_NAME = 'SeDebugPrivilege';
var
hToken: THandle;
tp: TOKEN_PRIVILEGES;
d: DWORD;
begin
Result := False;
if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken) then
begin
tp.PrivilegeCount := 1;
LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid);
if Value then
tp.Privileges[0].Attributes := $00000002
else
tp.Privileges[0].Attributes := $80000000;
AdjustTokenPrivileges(hToken, False, tp, SizeOf(TOKEN_PRIVILEGES), nil, d);
if GetLastError = ERROR_SUCCESS then
begin
Result := True;
end;
CloseHandle(hToken);
end;
end;
Пример использования:
// После этого можно смотреть информацию о таких системных // модулях как: winlogon.exe и servises.exe и д.р. EnableDebugPrivilege(True); // вкрючить EnableDebugPrivilege(False); // выключить
Вывести список установленных принтеров и установить принтер по умолчанию.
Автор: Xavier Pacheco
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TMainForm = class(TForm)
cbPrinters: TComboBox;
lblPrinter: TLabel;
procedure FormCreate(Sender: TObject);
procedure cbPrintersChange(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses IniFiles, Printers;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Copy the printer names to the combobox and set the combobox to
show the currently selected default printer }
cbPrinters.Items.Assign(Printer.Printers);
cbPrinters.Text := Printer.Printers[Printer.PrinterIndex];
// Update the label to reflect the default printer
lblPrinter.Caption := Printer.Printers[Printer.PrinterIndex];
end;
procedure TMainForm.cbPrintersChange(Sender: TObject);
var
IniFile: TIniFile;
TempStr1, TempStr2: string;
S: array[0..64] of char;
begin
with Printer do
begin
// Set the new printer based on the ComboBox's selected printer
PrinterIndex := cbPrinters.ItemIndex;
// Store the printer name into a temporary string
TempStr1 := Printers[PrinterIndex];
// Delete the unnecessary portion of the printer name
System.Delete(TempStr1, Pos(' on ', TempStr1), Length(TempStr1));
// Create a TIniFile class
IniFile := TIniFile.Create('WIN.INI');
try
// Retrieve the device name of the selected printer
TempStr2 := IniFile.ReadString('Devices', TempStr1, '');
// Change the default printer to that chosen by the user
IniFile.WriteString('windows', 'device', TempStr1 + ',' + TempStr2);
// Tell all windows that the default printer changed.
StrCopy(S, 'windows');
SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, LongInt(@S));
finally
IniFile.Free;
end;
end;
// Update the label to reflect the new printer selection
lblPrinter.Caption := Printer.Printers[Printer.PrinterIndex];
end;
end.
Как програмным путем задавать координаты ярлыкам на рабочем столе.
Автор: Сергей.
Рабочий стол перекрыт сверху компонентом ListView. Вам просто необходимо взять хэндл этого органа управления. Пример:
function GetDesktopListViewHandle: THandle;
var
S: String;
begin
Result := FindWindow('ProgMan', nil);
Result := GetWindow(Result, GW_CHILD);
Result := GetWindow(Result, GW_CHILD);
SetLength(S, 40);
GetClassName(Result, PChar(S), 39);
if PChar(S) <> 'SysListView32' then
Result := 0;
end;
|
К примеру, следующая строка кода:
{Не забудьте в uses добавить CommCtrl}
ListView_SetItemPosition(GetDesktopListViewHandle,i,x,y);
|
К примеру, следующая строка кода:
SendMessage( GetDesktopListViewHandle, LVM_ALIGN, LVA_ALIGNLEFT, 0 ); |
Разместит иконки рабочего стола по левой стороне рабочего стола Windows.
Изменение даты создания каталога.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Изменение даты создания каталога
Две перегруженные функции, изменяющие дату и время создания каталога.
Первая принимает непосредственные значения года, месяца, дня, часа,
минут и секунд. Во второй дата и время указываются в формате TDateTime.
См. также: http://delphibase.endimus.com/?action=viewfunc&topic=fileattr&id=10496
Зависимости: Windows
Автор: Dimka Maslov, mainbox@endimus.com, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 4 февраля 2004 г.
***************************************************** }
function SetDirTime(const Dir: string; Year, Month, Day, Hour, Minute, Second:
Word): Boolean; overload;
var
H: Integer;
F: TFileTime;
S: TSystemTime;
begin
H := CreateFile(PChar(Dir), $0100, 0, nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0);
if H <> -1 then
begin
S.wYear := Year;
S.wMonth := Month;
S.wDay := Day;
S.wHour := Hour;
S.wMinute := Minute;
S.wSecond := Second;
SystemTimeToFileTime(S, F);
LocalFileTimeToFileTime(F, F);
Result := Boolean(SetFileTime(H, @F, @F, @F));
CloseHandle(H);
end
else
Result := False;
end;
function SetDirTime(const Dir: string; DateTime: TDateTime): Boolean; overload;
var
H: Integer;
F: TFileTime;
S: TSystemTime;
begin
H := CreateFile(PChar(Dir), $0100, 0, nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0);
if H <> -1 then
begin
DateTimeToSystemTime(DateTime, S);
SystemTimeToFileTime(S, F);
LocalFileTimeToFileTime(F, F);
Result := Boolean(SetFileTime(H, @F, @F, @F));
CloseHandle(H);
end
else
Result := False;
end;
Пример использования:
if not SetDirTime('c:\Program Files', 2004, 2, 4, 12, 0, 0) then
ShowMessage('Error!');
Как установить переменные окружения.
| - Чем Windows похожи на презерватив? - Никому не нравится, но все используют. |
Следующая простая подпрограмма создаёт новые значения в переменных окружения. Если переменной окружения не существует, то она создаётся. Если переменной окружения установить значение пустой строки, то переменная удаляется. Функция возвращает 0, если значение переменной установлено или переменная создана успешно, либо возвратит значение ошибки Windows вслучае неудачи. Обратите внимание, что размер пространства доступного для переменных окружения ограничен.
function SetEnvVarValue(const VarName, VarValue: string): Integer;
begin
// Просто вызываем API функцию
if Windows.SetEnvironmentVariable(PChar(VarName), PChar(VarValue)) then
Result := 0
else
Result := GetLastError;
end;
|
ЗАМЕЧАНИЕ: данный способ позволяет делать изменения в переменных окружения только для текущего процесса либо для дочерних процессов, порождённых текущим.
Для того, чтобы передать какую-либо переменную окружения в дочерний процесс просто:
Создайте новую переменную окружения при помощи SetDOSEnvVar. Запустите новую программу.А вот как выглядит пример передачи текущих переменных окружения + переменной FOO=Bar в дочерний процесс:
var
ErrCode: Integer;
begin
ErrCode := SetEnvVarValue('FOO', 'Bar');
if ErrCode = 0 then
WinExec('MyChildProg.exe', SW_SHOWNORMAL);
else
ShowMessage(SysErrorMessage(ErrCode));
end;
|
Так же можно воспользоваться примером, содержащем различные примеры работы с переменными окружения, который можно скачать здесь
Как установить значение строкового или целого поля если оно присутствует.
Автор: Xavier Pacheco I am building a routine that checks our forms for validity before deploying them. I would like to use some kind of structure that tests if a component type has access to a certain property, something like: " if (self.Controls[b] has Tag) then ...". Can anyone offer suggestions?Here's an example of setting a string property for a component if it exists and another for an integer property:
procedure SetStringPropertyIfExists(AComp: TComponent; APropName: string;
AValue: string);
var
PropInfo: PPropInfo;
TK: TTypeKind;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
TK := PropInfo^.PropType^.Kind;
if (TK = tkString) or (TK = tkLString) or (TK = tkWString) then
SetStrProp(AComp, PropInfo, AValue);
end;
end;
procedure SetIntegerPropertyIfExists(AComp: TComponent; APropName: string;
AValue: Integer);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkInteger then
SetOrdProp(AComp, PropInfo, AValue);
end;
end;
Устанавливаем дату создания файла.
Function SetFileDate(
Const FileName : String;
Const FileDate : TDateTime): Boolean;
var
FileHandle : THandle;
FileSetDateResult : Integer;
begin
try
try
FileHandle := FileOpen
(FileName,
fmOpenWrite OR fmShareDenyNone);
if FileHandle > 0 Then begin
FileSetDateResult :=
FileSetDate(
FileHandle,
DateTimeToFileDate(FileDate));
result := (FileSetDateResult = 0);
end;
except
Result := False;
end;
finally
FileClose (FileHandle);
end;
end;
{Использование:}
SetFileDate('c:\mydir\myfile.ext', Now)
|
Выставить дату для файла.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function SetFileDateTime(FileName: string; NewDateTime: TDateTime): Boolean;
var
FileHandle: Integer;
FileTime: TFileTime;
LFT: TFileTime;
LST: TSystemTime;
begin
Result := False;
try
DecodeDate(NewDateTime, LST.wYear, LST.wMonth, LST.wDay);
DecodeTime(NewDateTime, LST.wHour, LST.wMinute, LST.wSecond, LST.wMilliSeconds);
if SystemTimeToFileTime(LST, LFT) then
begin
if LocalFileTimeToFileTime(LFT, FileTime) then
begin
FileHandle := FileOpen(FileName, fmOpenReadWrite or
fmShareExclusive);
if SetFileTime(FileHandle, nil, nil, @FileTime) then
Result := True;
end;
end;
finally
FileClose(FileHandle);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
if SetFileDateTime(OpenDialog1.FileName, now) then
ShowMessage('Date set to now !');
end;
Установка времени и даты файла.
...вы были на правильном пути, когда заговорили о функции SetFTime. Ниже несколько строчек кода, которые изменяют значения даты & времени файла:
var f: file; begin Assign(f, DirInfo.Name); Reset(f); SetFTime(f, Time); Close(f); end; |
Установка ловушек в Windows.
| Если вы хотите чаще встречаться с понравившейся девушкой установите ей Windows'95 |
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);Если ты плохо воспринимаешь Си-шный код, на Delphi заголовок выглядит так:
SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc;
hmod: HINST; dwThreadId: DWORD): HHOOK;
Функция SetWindowsHookEx() в случае установки hook'a возвращает его дескриптор, в случае ошибки возвращает 0.
Разберем подробней все входящие параметры этой функции:
1. idHook - константа, определяет типа устанавливаемого hоок'а. Может принимать одно из ниже перечисленных значений:
WH_CALLWNDPROC - Следит за сообщениями до отправки в оконную функцию и вызывается, когда процедуре окна посылается сообщение. Ловушка срабатывает при каждом вызове функции SendMessage.
WH_CALLWNDPROCRET - Контролирует сообщения после их отправки в оконную функцию.
WH_CBT - Вызывается перед обработкой большинства сообщений окон, мыши и клавиатуры (созданием окон, активация окон, уничтожением окон, сменой размера окон, перед установкой фокуса и.т.п.)
WH_DEBUG - Вызывается перед любой другой ловушкой. Полезно для отладки hоок'ов.
WH_GETMESSAGE - Вызывается, когда из очереди приложения считывается сообщение.
WH_HARDWARE - Вызывается, когда из очереди приложения считывается сообщение установленного на компьютере оборудования.
WH_JOURNALPLAYBACK - Вызывается, когда из очереди системы считывается сообщение. Применяется для добавления в очередь системных событий.
WH_JOURNALRECORD - Вызывается, когда из очереди системы запрашивается какое-нибудь событие. Применяется для регистрации системных событий.
WH_KEYBOARD - Вызывается, когда из очереди приложения считывается сообщения WM_Keydown или WM_Keyup. Одна из самых распространенных ловушек -).
WH_MOUSE - Вызывается, когда из очереди приложения считывается сообщение мыши.
WH_MSGFILTER - Вызывается, когда сообщение должно быть обработано диалоговым окном приложения, меню или окном приложения.
WH_SHELL - Вызывается, когда создаются и разрушаются окна верхнего уровня или когда приложению-оболочке требуется стать активным.
2. lpfn - указатель на саму hook функцию. Ее заголовок:
function HOOKFUNCTION(code: Integer; wparam: WPARAM;
lparam: LPARAM): LRESULT stdcall;
Значения входящих параметров зависят от типа hook'a. Если ставится глобальный hook, эта функция должна обязательно находиться в dll.
3. hmod - принимает значение hInstance или дескриптор DLL (в глобальных ловушках).
4. dwThreadId - идентифицирует поток, в который вставляется ловушка. В глобальных hоок'ах этот параметр должен быть равен 0.
Для удаления установленной ловушки существует функция UnhookWindowsHookEx(). В качестве параметра нужно использовать указатель (дескриптор) на hook функцию (значение, которое возвращает функция SetWindowsHookEx()).
Ну вот и все, с основами мы ознакомлены. Теперь напишем маленькую шуточную программу, ставящую hook на считывания сообщений мыши (WH_MOUSE). Сделаем так, чтобы при нажатии на правую кнопку мыши скрывалась кнопка "Пуск", при нажатии на левую - появлялась, среднею - изменялся заголовок активного окна. Сама hook функция будет находиться в dll. Кроме того, в dll будут находиться две процедуры - sethook() и removehook(), соответственно устанавливающие и удаляющие ловушку.
Привожу код dll библиотеки:
library lib;
uses
windows,messages;
var
H : THandle;
{Hook-функция}
function hook(c0de, wParam, lParam : integer): Lresult; stdcall;
{Объявления переменных}
var
w : THandle;
hw : hwnd;
begin
{Если c0de не меньше 0, все в порядке, продолжаем}
if c0de >= 0 then
begin
{ Если wParam = WM_RBUTTONUP, т.е. нажата правая кнопка мыши, получаем
хендл (handle) кнопки "Пуск" и скрываем ее }
case wParam of
WM_RBUTTONUP :
begin
W:= FindWindow('Shell_TrayWnd', nil);
W:= FindWindowEx(W, HWND(0),'Button', nil);
ShowWindow(W, SW_hide);
end;
{ Если wParam = WM_LBUTTONUP, т.е. нажата левая кнопка мыши, получаем
хендл кнопки пуск и показываем ее }
WM_LBUTTONUP:
begin
W:= FindWindow('Shell_TrayWnd', nil);
W:= FindWindowEx(W, HWND(0),'Button', nil);
ShowWindow(W, SW_SHOW);
end;
{ Если wParam = WM_MBUTTONUP, т.е. нажата средняя кнопка мыши, получаем
указатель на заголовок активного окна и изменяем его }
WM_MBUTTONUP:
begin
hw:=GetForegroundWindow;
SetWindowText(hw,'EXAMPLE OF WINDOWS HOOK (WH_MOUSE) - by Dark Lord
<darklord@smtp.ru>');
end;
end;
end else
{Если c0de меньше 0}
begin
{Вызываем следующую ловушку в цепочке ловушек Windows и выходим из процедуры}
result := CallNextHookEx(H, c0de, wParam, lParam);
exit;
end;
{Вызываем следующую ловушку в цепочке ловушек Windows}
result := CallNextHookEx(H, c0de, wParam, lParam);
End;
{ Процедура установки ловушки, если не удалось
установить - выводим сообщение об ошибке }
procedure sethook;
begin
H:= SetWindowsHookEx(WH_MOUSE, @hook, hInstance, 0);
if H = 0 then
messagebox(0,'hmmm..','ERROR',mb_iconhand);
end;
{ Процедура удаления ловушки }
procedure removehook;
begin
UnhookWindowsHookEx(H);
end;
{ Экспорт процедур установки и удаления hook'a }
exports
sethook index 1 name 'sethook',
removehook index 2 name 'removehook';
end.
|
В самой программе ловушка будет устанавливаться вызовом из dll процедуры sethook, удаляться - вызовом процедуры removehook. Пример установки и удаления hоок'а и исходник dll библиотеки есть в прилагающемся исходнике. В данной статье были рассмотрены только основы установки ловушек. Есть немалое количество нюансов и возникающих проблем при установке нескольких hоок'ов, установки hоок'ов в разных ОС (особенно это касается глобальных ловушек). Для получения подробностей по этим вопросам рекомендую использовать Win32 API Reference или MSDN (если нет диска с MSDN - прогуляйтесь на http://msdn.microsoft.com, здесь правда не вся информация, но большая ее часть).
Как присвоить значение свойству Selected в ListBox.

Свойство "selected" компонента ТListВох может быть использованно только если свойство MultiSelect установленно в True. Если Вы работаете с ListВох'ом у которого MultiSelect=false то используйте свойство ItemIndex.
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add('1');
ListBox1.Items.Add('2');
{This will fail on a single selection ListBox}
// ListBox1.Selected[1] := true;
ListBox1.ItemIndex := 1; {This is ok}
end;
|
Включить или отключить Caps, Num или Scroll Lock.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TKeyType = (ktCapsLock, ktNumLock, ktScrollLock);
procedure SetLedState(KeyCode: TKeyType; bOn: Boolean);
var
KBState: TKeyboardState;
Code: Byte;
begin
case KeyCode of
ktScrollLock: Code := VK_SCROLL;
ktCapsLock: Code := VK_CAPITAL;
ktNumLock: Code := VK_NUMLOCK;
end;
GetKeyboardState(KBState);
if (Win32Platform = VER_PLATFORM_WIN32_NT) then
begin
if Boolean(KBState[Code]) <> bOn then
begin
keybd_event(Code,
MapVirtualKey(Code, 0),
KEYEVENTF_EXTENDEDKEY,
0);
keybd_event(Code,
MapVirtualKey(Code, 0),
KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP,
0);
end;
end
else
begin
KBState[Code] := Ord(bOn);
SetKeyboardState(KBState);
end;
end;
// Example Call:
// Beispielaufruf:
procedure TForm1.Button1Click(Sender: TObject);
begin
SetLedState(ktCapsLock, True); // CapsLock on
SetLedState(ktNumLock, True); // NumLock on
SetLedState(ktScrollLock, True); // ScrollLock on
end;
Как перевести монитор в режим stand-by.
| Автор: Kecvin S. Gallagher |
| В офисе два программиста беседуют: Первый: - У меня самый матовый монитор!!! Второй: - Это потому, что его неоднократно покрывали матом!!! |
Если монитор поддерживает режим Stand by, то его можно программно перевести в этот режим. Данная возможность доступна на Windows95 и выше.
Чтобы перевести монитор в режим Stand by:
SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, 0); |
Чтобы вывести его из этого режима:
SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, -1); |
Добавьте следующее событие таймеру:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
ListBox1.Items.Add(FormatDateTime('h:mm:ss AM/PM', Time));
SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, -1);
end;
|
Command Button:
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add('--> ' + FormatDateTime('h:mm:ss AM/PM', Time));
Timer1.Enabled := not Timer1.Enabled;
SendMessage(Application.Handle, wm_SysCommand, SC_MonitorPower, 0);
end;
|
ЗАМЕЧАНИЕ: Удостоверьтесь, что во первых компьютер поддерживает режимы энергосбережения, а вовторых, эти функции не запрещены на данном компьютере.
В TRichEdit или TMemo при нажатии Enter позиционирование курсора в позицию как в предыдущей строке.
| Вопрос в службу поддержки: Что делать если я довел мышку до края коврика, а курсор не дошел до края экрана? |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
RichEdit1: TRichEdit;
procedure RichEdit1KeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
richedit;
procedure TForm1.RichEdit1KeyPress(Sender: TObject; var Key: Char);
var
line, col, indent: integer;
S: string;
begin
if key = #13 then
begin
key := #0;
with sender as TRichEdit do
begin
{figure out line and column position of caret}
line := PerForm( EM_EXLINEFROMCHAR, 0, SelStart );
Col := SelStart - Perform( EM_LINEINDEX, line, 0 );
{get part of current line in front of caret}
S:= Copy( lines[ line ], 1, col );
{count blanks and tabs in this string}
indent := 0;
while (indent and (S[indent + 1] in [' ', #9]) do
Inc( indent );
{insert a linebreak followed by the substring of blanks and tabs}
SelText := #13#10 + Copy(S, 1, indent);
end;
end;
end;
end.
|
Получить или установить имя компьютера.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ Retrieve the computer name }
function GetComputerName: string;
var
buffer: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char;
Size: Cardinal;
begin
Size := MAX_COMPUTERNAME_LENGTH + 1;
Windows.GetComputerName(@buffer, Size);
Result := StrPas(buffer);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(GetComputerName);
end;
{ Set the computer name }
function SetComputerName(AComputerName: string): Boolean;
var
ComputerName: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char;
Size: Cardinal;
begin
StrPCopy(ComputerName, AComputerName);
Result := Windows.SetComputerName(ComputerName);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if SetComputerName('NewComputerName') then
ShowMessage('Computer Name Reset Setting will be used at next startup.')
else
ShowMessage('Computer Name Not Reset');
end;
Как установить количество точек на дюйм в TPrinter.
При смене принтера, размер шрифтов может отмасштабироваться не правильно. Чтобы сделать правильное масштабирование, необходимо установить свойство PixelsPerInch шрифта.
Не делайте изменений после того, как принтер начал печатать.
Два примера:
uses Printers;
var
MyFile: TextFile;
begin
Printer.PrinterIndex := 2;
AssignPrn(MyFile);
Rewrite(MyFile);
Printer.Canvas.Font.Name := 'Courier New';
Printer.Canvas.Font.Style := [fsBold];
Printer.Canvas.Font.PixelsPerInch :=
GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY);
Writeln(MyFile, 'Print this text');
System.CloseFile(MyFile);
end;
|
uses Printers;
begin
Printer.PrinterIndex := 2;
Printer.BeginDoc;
Printer.Canvas.Font.Name := 'Courier New';
Printer.Canvas.Font.Style := [fsBold];
Printer.Canvas.Font.PixelsPerInch :=
GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY);
Printer.Canvas.Textout(10, 10, 'Print this text');
Printer.EndDoc;
end;
|
Публикование свойств-наборов.
...я думаю, что ваша проблема заключается в том, что любые поля или методы, которые явно не отнесены ни к одному из типов защиты, автоматически принимают тип published. Я подозреваю, что ваш код должен выглядеть примерно так:
myClass = class(TComponent)
private { !! вы пропустили эту директиву защиты }
fMySetProperty : myset;
published
MySetProperty : myset read fMySetProperty write fMySetProperty;
end;
|
Выставить значения свойств.
Автор: Xavier Pacheco
unit MainFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TMainForm = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Panel1: TPanel;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Panel1Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses TypInfo;
{$R *.DFM}
procedure SetIntegerPropertyIfExists(AComp: TComponent; APropName: string;
AValue: Integer);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkInteger then
SetOrdProp(AComp, PropInfo, AValue);
end;
end;
procedure SetObjectPropertyIfExists(AComponent: TComponent; APropName: string;
AValue: TObject);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComponent.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkClass then
SetObjectProp(AComponent, PropInfo, AValue);
end;
end;
procedure SetBooleanPropertyIfExists(AComp: TComponent; APropName: string;
AValue: Boolean);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkEnumeration then
SetOrdProp(AComp, PropInfo, Integer(AValue));
end;
end;
procedure SetStringPropertyIfExists(AComp: TComponent; APropName: string;
AValue: string);
var
PropInfo: PPropInfo;
TK: TTypeKind;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
TK := PropInfo^.PropType^.Kind;
if (TK = tkString) or (TK = tkLString) or (TK = tkWString) then
SetStrProp(AComp, PropInfo, AValue);
end;
end;
procedure SetMethodPropertyIfExists(AComp: TComponent; APropName: string;
AMethod: TMethod);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkMethod then
SetMethodProp(AComp, PropInfo, AMethod);
end;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
SetStringPropertyIfExists(Button1, 'Caption', 'Yahoo');
end;
procedure TMainForm.Button2Click(Sender: TObject);
begin
SetIntegerPropertyIfExists(Button2, 'Width', 50);
end;
procedure TMainForm.Button3Click(Sender: TObject);
begin
SetBooleanPropertyIfExists(Button3, 'Enabled', False);
end;
procedure TMainForm.Button4Click(Sender: TObject);
var
F: TFont;
begin
F := TFont.Create;
F.Name := 'Arial';
F.Size := 24;
F.Color := clRed;
SetObjectPropertyIfExists(Panel1, 'Font', F);
end;
procedure TMainForm.Button5Click(Sender: TObject);
begin
SetMethodPropertyIfExists(Button5, 'OnClick',
GetMethodProp(Panel1, 'OnClick'));
end;
procedure TMainForm.Panel1Click(Sender: TObject);
begin
ShowMessage(Button5.Caption);
end;
end.
Как установить значение свойства компонента по имени.
Автор: Jeff Overcash WEB-сайт: http://www.lmc-mediaagentur.deYou can use RTTI to do this. Here is how to change a particular component:
procedure TForm1.BtnClick(Sender: TObject);
var
p: PPropInfo;
f: TFont;
begin
f := TFont.Create;
{Setup the font properties}
f.Name := 'Arial';
p := GetPropInfo(Sender.ClassInfo, 'Font');
if Assigned(p) then
SetOrdProp(Sender, p, Integer(f));
f.Free;
end;
To get at all the forms loop through the Screen global variable. For each form loop through its Components list calling the above procedure (or something close). If you only create your components at design time that is it. If you create some at runtime and the owner is not the form, then for each component loop through its Components list recursively to get at all the owned components.
Как указать максимальный размер текста для RichEdit Control.
У этого компонента есть свойство MaxLength, которое работает некорректно. Поэтому лучше пользоваться.
RichEdit.Perform(EM_LIMITTEXT, нужный размер, 0); |
Причем перед каждом открытии файла это действие необходимо повторять.
Если Вы передаете в качестве размера 0, то ОС ограничивает размер OS Specific Default Value. Реально, по результатам моих экспериментов, поставить можно размер, чуть меньший доступной виртуальной памяти. Я ограничился 90% от свободной виртуалки.
Для того, чтобы не повторять этот вызов (EM_LIMITTEXT), можно воспользоваться сообщением EM_EXLIMITTEXT.
Как установить системную дату и время программно.
| Билл Гейтс собрал Microsoft на конференцию и говорит: - Господа, мы установили, что половина людей использующих Windows имеет возраст больше медианного! Другая потрясающая новость - это то, что другая половина людей имеет возраст ниже медианного! - Простите, что такое медианный возраст? - Этого мы еще не установили. |
Следующие несколько строк кода позволяют установить системную дату и время без использования панели управления. Дата и время устанавливаются двумя раздельными компонентами TDateTimePicker. Дата и время декодируются и передаются в API функцию.
Из значения часа вычитается 2 для установки правильного времени.
procedure TfmTime.btnTimeClick(Sender: TObject); var vsys: _SYSTEMTIME; vYear, vMonth, vDay, vHour, vMin, vSec, vMm: Word; begin DecodeDate( Trunc(dtpDate.Date), vYear, vMonth, vDay ); DecodeTime( dtpTime.Time, vHour, vMin, vSec, vMm ); vMm := 0; vsys.wYear := vYear; vsys.wMonth := vMonth; vsys.wDay := vDay; vsys.wHour := ( vHour - 2 ); vsys.wMinute := vMin; vsys.wSecond := vSec; vsys.wMilliseconds := vMm; vsys.wDayOfWeek := DayOfWeek( Trunc(dtpDate.Date) ); SetSystemTime( vsys ); end; |
Выставить системные переменные среды.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{*********************************************}
{ Set Global Environment Function }
{ Coder : Kingron,2002.8.6 }
{ Bug Report : Kingron@163.net }
{ Test OK For Windows 2000 Advance Server }
{ Parameter: }
{ Name : environment variable name }
{ Value: environment variable Value }
{ Ex: SetGlobalEnvironment('MyVar','OK') }
{*********************************************}
function SetGlobalEnvironment(const Name, Value: string;
const User: Boolean = True): Boolean;
resourcestring
REG_MACHINE_LOCATION = 'System\CurrentControlSet\Control\Session Manager\Environment';
REG_USER_LOCATION = 'Environment';
begin
with TRegistry.Create do
try
if User then { User Environment Variable }
Result := OpenKey(REG_USER_LOCATION, True)
else { System Environment Variable }
begin
RootKey := HKEY_LOCAL_MACHINE;
Result := OpenKey(REG_MACHINE_LOCATION, True);
end;
if Result then
begin
WriteString(Name, Value); { Write Registry for Global Environment }
{ Update Current Process Environment Variable }
SetEnvironmentVariable(PChar(Name), PChar(Value));
{ Send Message To All Top Window for Refresh }
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment')));
end;
finally
Free;
end;
end; { SetGlobalEnvironment }
Установка шрифтов для системного меню.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
ncm: TNonClientMetrics;
begin
ncm.cbSize := SizeOf(TNonClientMetrics);
{get old non client metrics}
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SizeOf(TNonClientMetrics), @ncm, 0);
{
lfCaptionFont - regular captions
lfSmCaptionFont - small captions
lfMenuFont - menus
lfStatusFont - status bars
lfMessageFont - message boxes
}
ncm.lfMessageFont.lfFaceName := 'Tahoma';
{set new non client metrics}
SystemParametersInfo(SPI_SETNONCLIENTMETRICS, SizeOf(TNonClientMetrics), @ncm, 0);
end;
Назначение прав пользователей на таблицу.
Автор: Dracula WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Назначение прав пользователей на таблицу
Какое может быть описание? И так все понятно. Кто делает эти формы?
Зависимости: uses IBQuery,IBDataBase,SysUtils
Автор: Dracula, dracula@krruda.dp.ua, Krivoy Rog
Copyright: Dracula
Дата: 28 января 2003 г.
***************************************************** }
unit IBRights;
interface
uses IBQuery, IBDataBase, SysUtils;
type
TIBUserRights = record
sel: Boolean;
ins: Boolean;
upd: Boolean;
del: Boolean;
exe: Boolean;
end;
function GetUserRights(UserName, Relation: string; IBDb: TIBDataBase):
TIBUserRights;
var
Rights: TIBUserRights;
implementation
function GetUserRights(UserName, Relation: string; IBDb: TIBDataBase):
TIBUserRights;
var
Qr: TIBQuery;
Tr: TIBTransaction;
begin
if Assigned(IBDb) then
begin
Tr := TIBTransaction.Create(nil);
try
Tr.DefaultDatabase := IBDb;
Qr := TIBQuery.Create(nil);
Qr.Database := IBDb;
Tr.StartTransaction;
Qr.Close;
Qr.Sql.Clear;
Qr.Sql.Add('select RDB$USER,RDB$PRIVILEGE,RDB$RELATION_NAME ' +
'from RDB$USER_PRIVILEGES ' +
'where upper(RDB$USER)=:AUser ' +
'and upper(RDB$RELATION_NAME)=:ARelation');
Qr.Prepare;
Qr.Params.ParamValues['AUser'] := AnsiUpperCase(UserName);
Qr.Params.ParamValues['ARelation'] := AnsiUpperCase(Relation);
Qr.Open;
while not Qr.eof do
begin
if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'S' then
Rights.sel := true
else
Rights.sel := false;
if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'I' then
Rights.ins := true
else
Rights.ins := false;
if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'U' then
Rights.upd := true
else
Rights.upd := false;
if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'D' then
Rights.del := true
else
Rights.del := false;
if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'X' then
Rights.exe := true
else
Rights.exe := false;
if Copy(Trim(Qr.FieldByName('RDB$PRIVILEGE').AsString), 1, 1) = 'R' then
//Rights.ref:=true else Rights.ref:=false;
begin
Rights.sel := true;
Rights.ins := true;
Rights.upd := true;
Rights.del := true;
Rights.exe := true;
end;
Qr.Next;
end;
Qr.Close;
Tr.Commit;
Qr.Free;
finally
Tr.Free;
end;
end;
Result := Rights;
end;
end.
Пример использования:
uses....., IBRights; ...... var rights: TIBUserRights; implementation ..... begin ... rights := GetUserRights(DllLogin, NameTable, IBDataBase); ..... end;
Переместить курсор в TRichEdit на нужную позицию.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// You can move the caret in a TRichEdit component by using this code : procedure RichEdit_MoveTo(RichEdit: TRichEdit; LineNumber, CharNumber: Word); begin RichEdit.SelStart := RichEdit.Perform(EM_LINEINDEX, LineNumber, 0) + CharNumber); end; procedure TForm1.Button1Click(Sender: TObject); begin RichEdit_MoveTo(RichEdit1,2,5); Application.ProcessMessages; RichEdit1.SetFocus; end;
Выставление и сброс битов.
SetWord - слово, которое необходимо установить. BitNum - номер бита, который необходимо выставить согласно определениям в секции const (Bit0, Bit1 и др...). GetBitStat возвращает Истину, если бит установлен и Ложь в противном случае.
{ Слово записывается следующим образом... }
{ Слово - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 }
{ Номер бита - 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 }
const
Bit0 = 1;
Bit1 = 2;
Bit2 = 4;
Bit3 = 8;
Bit4 = 16;
Bit5 = 32;
Bit6 = 64;
Bit7 = 128;
Bit8 = 256;
Bit9 = 512;
Bit10 = 1024;
Bit11 = 2048;
Bit12 = 4096;
Bit13 = 8192;
Bit14 = 16384;
Bit15 = 32768;
procedure SetBit(SetWord, BitNum: Word);
begin
SetWord := SetWord or BitNum; { Устанавливаем бит }
end;
procedure ClearBit(SetWord, BitNum: Word);
begin
SetWord := SetWord or BitNum; { Устанавливаем бит }
SetWord := SetWord xor BitNum; { Переключаем бит }
end;
procedure ToggleBit(SetWord, BitNum: Word);
begin
SetWord := SetWord xor BitNum; { Переключаем бит }
end;
function GetBitStat(SetWord, BitNum: Word): Boolean;
begin
GetBitStat := SetWord and BitNum = BitNum { Если бит установлен }
end;
|
Установить метку тома файловой системы.
BOOL SetVolumeLabel(
LPCTSTR lpRootPathName, // адрес имени корневой директории тома
LPCTSTR lpVolumeName // метка тома
);
|
См. также: GetVolumeInformation.
Перевернуть текст в TStringGrid на 90 градусов.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
{...} Grids;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
end;
{...}
implementation
{...}
// Display text vertically in StringGrid cells
// Vertikale Textausgabe in den Zellen eines StringGrid
procedure StringGridRotateTextOut(Grid: TStringGrid; ARow, ACol: Integer; Rect: TRect;
Schriftart: string; Size: Integer; Color: TColor; Alignment: TAlignment);
var
lf: TLogFont;
tf: TFont;
begin
// if the font is to big, resize it
// wenn Schrift zu gro? dann anpassen
if (Size > Grid.ColWidths[ACol] div 2) then
Size := Grid.ColWidths[ACol] div 2;
with Grid.Canvas do
begin
// Replace the font
// Font setzen
Font.Name := Schriftart;
Font.Size := Size;
Font.Color := Color;
tf := TFont.Create;
try
tf.Assign(Font);
GetObject(tf.Handle, SizeOf(lf), @lf);
lf.lfEscapement := 900;
lf.lfOrientation := 0;
tf.Handle := CreateFontIndirect(lf);
Font.Assign(tf);
finally
tf.Free;
end;
// fill the rectangle
// Rechteck fullen
FillRect(Rect);
// Align text and write it
// Text nach Ausrichtung ausgeben
if Alignment = taLeftJustify then
TextRect(Rect, Rect.Left + 2,Rect.Bottom - 2,Grid.Cells[ACol, ARow]);
if Alignment = taCenter then
TextRect(Rect, Rect.Left + Grid.ColWidths[ACol] div 2 - Size +
Size div 3,Rect.Bottom - 2,Grid.Cells[ACol, ARow]);
if Alignment = taRightJustify then
TextRect(Rect, Rect.Right - Size - Size div 2 - 2,Rect.Bottom -
2,Grid.Cells[ACol, ARow]);
end;
end;
// 2. Alternative: Display text vertically in StringGrid cells
// 2. Variante: Vertikale Textausgabe in den Zellen eines StringGrid
procedure StringGridRotateTextOut2(Grid:TStringGrid;ARow,ACol:Integer;Rect:TRect;
Schriftart:String;Size:Integer;Color:TColor;Alignment:TAlignment);
var
NewFont, OldFont : Integer;
FontStyle, FontItalic, FontUnderline, FontStrikeout: Integer;
begin
// if the font is to big, resize it
// wenn Schrift zu gro? dann anpassen
If (Size > Grid.ColWidths[ACol] DIV 2) Then
Size := Grid.ColWidths[ACol] DIV 2;
with Grid.Canvas do
begin
// Set font
// Font setzen
If (fsBold IN Font.Style) Then
FontStyle := FW_BOLD
Else
FontStyle := FW_NORMAL;
If (fsItalic IN Font.Style) Then
FontItalic := 1
Else
FontItalic := 0;
If (fsUnderline IN Font.Style) Then
FontUnderline := 1
Else
FontUnderline := 0;
If (fsStrikeOut IN Font.Style) Then
FontStrikeout:=1
Else
FontStrikeout:=0;
Font.Color := Color;
NewFont := CreateFont(Size, 0, 900, 0, FontStyle, FontItalic,
FontUnderline, FontStrikeout, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH, PChar(Schriftart));
OldFont := SelectObject(Handle, NewFont);
// fill the rectangle
// Rechteck fullen
FillRect(Rect);
// Write text depending on the alignment
// Text nach Ausrichtung ausgeben
If Alignment = taLeftJustify Then
TextRect(Rect,Rect.Left+2,Rect.Bottom-2,Grid.Cells[ACol,ARow]);
If Alignment = taCenter Then
TextRect(Rect,Rect.Left+Grid.ColWidths[ACol] DIV 2 - Size + Size DIV 3,
Rect.Bottom-2,Grid.Cells[ACol,ARow]);
If Alignment = taRightJustify Then
TextRect(Rect,Rect.Right-Size - Size DIV 2 - 2,Rect.Bottom-2,Grid.Cells[ACol,ARow]);
// Recreate reference to the old font
// Referenz auf alten Font wiederherstellen
SelectObject(Handle, OldFont);
// Recreate reference to the new font
// Referenz auf neuen Font loschen
DeleteObject(NewFont);
end;
end;
// Call the method in OnDrawCell
// Methode im OnDrawCell aufrufen
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol,
ARow: Integer; Rect: TRect; State: TGridDrawState);
begin
// In the second column: Rotate Text by 90° and left align the text
// Text um 90°gedreht ausgeben, linksbundig in der zweiten Spalte
if ACol = 1 then
StringGridRotateTextOut(StringGrid1, ARow, ACol, Rect, 'ARIAL',
12,clRed, taLeftJustify);
// In the third column: Center the text
// Ausgabe zentriert in der dritten Spalte
if ACol = 2 then
StringGridRotateTextOut(StringGrid1, ARow, ACol, Rect, 'ARIAL', 12, clBlue, taCenter);
// In all other columns third row: right align the text
// Ausgabe rechtsbundig in den restlichen Spalten
if ACol > 2 then
StringGridRotateTextOut(StringGrid1, ARow, ACol, Rect, 'ARIAL', 12,clGreen,
taRightJustify);
end;
end.
Добавление колонки в StringGrid.
Автор: http://www.sources.ru
procedure GridAddColumn(StrGrid: TStringGrid; NewColumn: Integer);
var
Column: Integer;
begin
StrGrid.ColCount := StrGrid.ColCount + 1;
for Column := StrGrid.ColCount - 1 downto NewColumn do
StrGrid.Cols[Column].Assign(StrGrid.Cols[Column - 1]);
StrGrid.Cols[NewColumn - 1].Text := '';
end;
Добавление колонки в StringGrid 2.
Автор: http://www.sources.ru
procedure AddColumn(SG: TStringGrid; AtColNumber: integer;
ColWidth: integer = 0);
var
Column: integer;
Wdth: integer;
begin
AtColNumber := abs(AtColNumber);
SG.ColCount := SG.ColCount + 1;
if abs(ColWidth) = 0 then
Wdth := SG.DefaultColWidth
else
Wdth := ColWidth;
if AtColNumber <= SG.ColCount then
begin
for Column := SG.ColCount - 1 downto AtColNumber + 1 do
begin
SG.Cols[Column].Assign(SG.Cols[Column - 1]);
SG.Colwidths[Column] := SG.Colwidths[Column - 1];
end;
SG.Cols[AtColNumber].Text := '';
SG.Colwidths[AtColNumber] := Wdth;
end;
end;
Получить содержимое TStringGrid или TDrawGrid в виде строки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This copies the contents of a TstringGrid/TDrawGrid (only Text!!) into a string.
Tabs are inserted between the columns, CR+LF between rows.
}
use
Grids;
{...}
{ we need this Cracker Class because the Col/RowCount property
is not public in TCustomGrid }
type
TGridHack = class(TCustomGrid);
function GetstringGridText(_Grid: TCustomGrid): string;
var
Grid: TGridHack;
Row, Col: Integer;
s: string;
begin
// Cast the paramter to a TGridHack, so we can access protected properties
Grid := TGridHack(_Grid);
Result := '';
// for all rows, then for all columns
for Row := 0 to Grid.RowCount - 1 do
begin
for Col := 0 to Grid.ColCount - 1 do
begin
// the first column does not need the tab
if Col > 0 then
Result := Result + #9;
Result := Result + Grid.GetEditText(Col, Row);
end;
Result := Result + #13#10;
end;
end;
Автоматический подбор ширины колонок для TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure SetGridColumnWidths(Grid: TStringGrid;
const Columns: array of Integer);
{
When you double-Click on a Column-Header the Column
autosizes to fit its content
Bei Doppelklick auf eine fixierte Spalte passt sich
die Spaltenbreite der Textgrosse an
}
procedure AutoSizeGridColumn(Grid: TStringGrid; column, min, max: Integer);
{ Set for max and min some minimal/maximial Values}
{ Bei max and min kann eine Minimal- resp. Maximalbreite angegeben werden}
var
i: Integer;
temp: Integer;
tempmax: Integer;
begin
tempmax := 0;
for i := 0 to (Grid.RowCount - 1) do
begin
temp := Grid.Canvas.TextWidth(Grid.cells[column, i]);
if temp > tempmax then tempmax := temp;
if tempmax > max then
begin
tempmax := max;
break;
end;
end;
if tempmax < min then tempmax := min;
Grid.ColWidths[column] := tempmax + Grid.GridLineWidth + 3;
end;
procedure TForm1.StringGrid1DblClick(Sender: TObject);
var
P: TPoint;
iColumn, iRow: Longint;
begin
GetCursorPos(P);
with StringGrid1 do
begin
P := ScreenToClient(P);
MouseToCell(P.X, P.Y, iColumn, iRow);
if P.Y < DefaultRowHeight then
AutoSizeGridColumn(StringGrid1, iColumn, 40, 100);
end;
end;
Автоматический подбор ширины колонок для TStringGrid 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
{ by P. Below }
const
DEFBORDER = 8;
var
max, temp, i, n: Integer;
begin
with Grid do
begin
Canvas.Font := Font;
for n := Low(Columns) to High(Columns) do
begin
max := 0;
for i := 0 to RowCount - 1 do
begin
temp := Canvas.TextWidth(Cells[Columns[n], i]) + DEFBORDER;
if temp > max then
max := temp;
end; { For }
if max > 0 then
ColWidths[Columns[n]] := max;
end; { For }
end; { With }
end; {SetGridColumnWidths }
Выравнивание ячеек в TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure WriteText(StringGrid: TStringGrid; ACanvas: TCanvas; const ARect: TRect;
const Text: string; Format: Word);
const
DX = 2;
DY = 2;
var
S: array[0..255] of Char;
B, R: TRect;
begin
with Stringgrid, ACanvas, ARect do
begin
case Format of
DT_LEFT: ExtTextOut(Handle, Left + DX, Top + DY,
ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text), Length(Text), nil);
DT_RIGHT: ExtTextOut(Handle, Right - TextWidth(Text) - 3, Top + DY,
ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text),
Length(Text), nil);
DT_CENTER: ExtTextOut(Handle, Left + (Right - Left - TextWidth(Text)) div 2,
Top + DY, ETO_OPAQUE or ETO_CLIPPED, @ARect,
StrPCopy(S, Text), Length(Text), nil);
end;
end;
end;
procedure Display(StringGrid: TStringGrid; const S: string; Alignment: TAlignment);
const
Formats: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
begin
WriteText(StringGrid, StringGrid.Canvas, Rect, S, Formats[Alignment]);
end;
begin
// Right-justify columns 0-2
// Spalten 0-2 rechts ausrichten.
if ACol in [0..2] then
Display(StringGrid1, StringGrid1.Cells[ACol, ARow], taRightJustify)
// Center the first row
// Erste zeile zentrieren
if ARow = 0 then
Display(StringGrid1, StringGrid1.Cells[ACol, ARow], taCenter)
end;
Очистить ячейки в TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
i, k: Integer;
begin
with StringGrid1 do
for i := 0 to ColCount - 1 do
for k := 0 to RowCount - 1 do
Cells[i, k] := '';
end;
Очистить ячейки в TStringGrid 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// Many times faster!
// Um einiges schneller!
procedure TForm1.Button2Click(Sender: TObject);
var
I: Integer;
begin
for I := 0 to StringGrid1.RowCount - 1 do
StringGrid1.Rows[I].Clear();
end;
Автоматический подбор ширины колонок для TStringGrid или TDrawGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ This will resize the columns of a TStringGrid / TDrawGrid (text
only!) so the text is completely visble. To save some time,
it uses the first 10 rows only, but that should be easy to fix,
if you need more. }
// we need this to access protected methods
type
TGridHack = class(TCustomGrid);
procedure ResizeStringGrid(_Grid: TCustomGrid);
var
Col, Row: integer;
Grid: TGridHack;
MaxWidth: integer;
ColWidth: integer;
ColText: string;
MaxRow: integer;
ColWidths: array of integer;
begin
Grid := TGridHack(_Grid);
SetLength(ColWidths, Grid.ColCount);
MaxRow := 10;
if MaxRow > Grid.RowCount then
MaxRow := Grid.RowCount;
for Col := 0 to Grid.ColCount - 1 do
begin
MaxWidth := 0;
for Row := 0 to MaxRow - 1 do
begin
ColText := Grid.GetEditText(Col, Row);
ColWidth := Grid.Canvas.TextWidth(ColText);
if ColWidth > MaxWidth then
MaxWidth := ColWidth;
end;
if goVertLine in Grid.Options then
Inc(MaxWidth, Grid.GridLineWidth);
ColWidths[Col] := MaxWidth + 4;
Grid.ColWidths[Col] := ColWidths[Col];
end;
end;
Реализация события OnColumnClick из TListView для TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
There are two routines to implement the OnColumnClick Methods for a TStringGrid.
Set the first row as fixed and the Defaultdrawing to True.
Mit folgenden zwei Routinen kann man in einem TStringgrid
die Methode OnColumnClick eines TListView erzeugen (visuell).
Reihe 0 mu? fixiert sein undDefaultDrawing = True
}
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
procedure StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
zelle: TRect; // cell
acol, arow: Integer;
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Text: string;
begin
with stringgrid1 do
begin
MouseRoCell(x, y, acol, arow);
if (arow = 0) and (button = mbleft) then
case acol of
0..2:
begin
// Draws a 3D Effect (Push)
// Zeichnet 3D-Effekt (Push)
zelle := CellRect(acol, arow);
Text := Cells[acol, arow];
Canvas.Font := Font;
Canvas.Brush.Color := clBtnFace;
Canvas.FillRect(zelle);
Canvas.TextRect(zelle, zelle.Left + 2, zelle.Top + 2, Text);
DrawEdge(Canvas.Handle, zelle, 10, 2 or 4 or 8);
DrawEdge(Canvas.Handle, zelle, 2 or 4, 1);
end;
end;
end;
end;
procedure TForm1.StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Text: string;
begin
with StringGrid1 do
begin
// Draws a 3D-Effect (Up)
// Zeichnet 3D-Effekt (Up)
Text := Cells[acol, arow];
if arow = 0 then
begin
Canvas.Font := Font;
Canvas.Brush.Color := clBtnFace;
Canvas.FillRect(zelle);
Canvas.TextRect(zelle, zelle.Left + 2, zelle.Top + 2, Text);
DrawEdge(Canvas.Handle, zelle, 4, 4 or 8);
DrawEdge(Canvas.Handle, zelle, 4, 1 or 2);
MouseToCell(zelle.Left, zelle.Top, acol, arow);
end;
end;
if (arow = 0) and (Button = mbleft) then
case acol of
0..2:
begin
// Code to be executed...
// Programmcode der ausgefuhrt werden soll
ShowMessage('Column ' + IntToStr(acol));
zelle := stringgrid1.CellRect(1, 1);
end;
end;
end;
end.
Обратиться к колонке или строке StringGrid по заголовку.
В следующем примере приведены две функции: GetGridColumnByName() и GetGridRowByName(), которые возвращают колонку или строку, имеющую заданный заголовок (caption).Пример:
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1.Rows[1].Strings[0] := 'This Row';
StringGrid1.Cols[1].Strings[0] := 'This Column';
end;
function GetGridColumnByName(Grid: TStringGrid; ColName: string): integer;
var
i: integer;
begin
for i := 0 to Grid.ColCount - 1 do
if Grid.Rows[0].Strings[i] = ColName then
begin
Result := i;
exit;
end;
Result := -1;
end;
function GetGridRowByName(Grid: TStringGrid; RowName: string): integer;
var
i: integer;
begin
for i := 0 to Grid.RowCount - 1 do
if Grid.Cols[0].Strings[i] = RowName then
begin
Result := i;
exit;
end;
Result := -1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Column: integer;
Row: integer;
begin
Column := GetGridColumnByName(StringGrid1, 'This Column');
if Column = -1 then
ShowMessage('Column not found')
else
ShowMessage('Column found at ' + IntToStr(Column));
Row := GetGridRowByName(StringGrid1, 'This Row');
if Row = -1 then
ShowMessage('Row not found')
else
ShowMessage('Row found at ' + IntToStr(Row));
end;
|
Сортировать TStringGrid по колонке.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TMoveSG = class(TCustomGrid); // reveals protected MoveRow procedure
{...}
procedure SortGridByCols(Grid: TStringGrid; ColOrder: array of Integer);
var
i, j: Integer;
Sorted: Boolean;
function Sort(Row1, Row2: Integer): Integer;
var
C: Integer;
begin
C := 0;
Result := AnsiCompareStr(Grid.Cols[ColOrder[C]][Row1], Grid.Cols[ColOrder[C]][Row2]);
if Result = 0 then
begin
Inc(C);
while (C <= High(ColOrder)) and (Result = 0) do
begin
Result := AnsiCompareStr(Grid.Cols[ColOrder[C]][Row1],
Grid.Cols[ColOrder[C]][Row2]);
Inc(C);
end;
end;
end;
begin
if SizeOf(ColOrder) div SizeOf(i) <> Grid.ColCount then Exit;
for i := 0 to High(ColOrder) do
if (ColOrder[i] < 0) or (ColOrder[i] >= Grid.ColCount) then Exit;
j := 0;
Sorted := False;
repeat
Inc(j);
with Grid do
for i := 0 to RowCount - 2 do
if Sort(i, i + 1) > 0 then
begin
TMoveSG(Grid).MoveRow(i + 1, i);
Sorted := False;
end;
until Sorted or (j = 1000);
Grid.Repaint;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{ Sort rows based on the contents of two or more columns.
Sorts first by column 1. If there are duplicate values
in column 1, the next sort column is column 2 and so on...}
SortGridByCols(StringGrid1, [1, 2, 0, 3, 4]);
end;
Копировать и вставлять ячейки TStringGrid в буфер обмена.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
Clipbrd;
//Copy
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
GRect: TGridRect;
C, R: Integer;
begin
GRect := StringGrid1.Selection;
S := '';
for R := GRect.Top to GRect.Bottom do
begin
for C := GRect.Left to GRect.Right do
begin
if C = GRect.Right then S := S + (StringGrid1.Cells[C, R])
else
S := S + StringGrid1.Cells[C, R] + #9;
end;
S := S + #13#10;
end;
ClipBoard.AsText := S;
end;
// Paste
procedure TForm1.Button2Click(Sender: TObject);
var
Grect: TGridRect;
S, CS, F: string;
L, R, C: Byte;
begin
GRect := StringGrid1.Selection;
L := GRect.Left;
R := GRect.Top;
S := ClipBoard.AsText;
R := R - 1;
while Pos(#13, S) > 0 do
begin
R := R + 1;
C := L - 1;
CS := Copy(S, 1,Pos(#13, S));
while Pos(#9, CS) > 0 do
begin
C := C + 1;
if (C <= StringGrid1.ColCount - 1) and (R <= StringGrid1.RowCount - 1) then
StringGrid1.Cells[C, R] := Copy(CS, 1,Pos(#9, CS) - 1);
F := Copy(CS, 1,Pos(#9, CS) - 1);
Delete(CS, 1,Pos(#9, CS));
end;
if (C <= StringGrid1.ColCount - 1) and (R <= StringGrid1.RowCount - 1) then
StringGrid1.Cells[C + 1,R] := Copy(CS, 1,Pos(#13, CS) - 1);
Delete(S, 1,Pos(#13, S));
if Copy(S, 1,1) = #10 then
Delete(S, 1,1);
end;
end;
Удаление колонки в StringGrid.
Автор: http://www.sources.ru
procedure GridRemoveColumn(StrGrid: TStringGrid; DelColumn: Integer);
var
Column: Integer;
begin
if DelColumn <= StrGrid.ColCount then
begin
for Column := DelColumn to StrGrid.ColCount - 1 do
StrGrid.Cols[Column - 1].Assign(StrGrid.Cols[Column]);
StrGrid.ColCount := StrGrid.ColCount - 1;
end;
end;
Удаление колонки в StringGrid 2.
Автор: http://www.sources.ru
procedure RemoveColumn(SG: TStringGrid; ColNumber: integer);
var
Column: integer;
begin
ColNumber := abs(ColNumber);
if ColNumber <= SG.ColCount then
begin
for Column := ColNumber to SG.ColCount - 2 do
begin
SG.Cols[Column].Assign(SG.Cols[Column + 1]);
SG.Colwidths[Column] := SG.Colwidths[Column + 1];
end;
SG.ColCount := SG.ColCount - 1;
end;
end;
Удаление колонки в TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TStringGridHack = class(TStringGrid)
public
procedure DeleteCol(ACol: Longint);
end;
var
Form1: TForm1;
implementation
procedure TStringGridHack.DeleteCol(ACol: Longint);
begin
if ACol = FixedCols then if ACol = (ColCount - 1) then
begin
Cols[ACol].Clear;
if ColCount(FixedCols + 1) then ColCount := (ColCount - 1);
end
else
begin
Cols[ACol] := Cols[ACol + 1];
DeleteCol(ACol + 1);
end;
end;
Удалить строку из TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure GridDeleteRow(RowNumber: Integer; Grid: TstringGrid);
var
i: Integer;
begin
Grid.Row := RowNumber;
if (Grid.Row = Grid.RowCount - 1) then
{ On the last row}
Grid.RowCount := Grid.RowCount - 1
else
begin
{ Not the last row}
for i := RowNumber to Grid.RowCount - 1 do
Grid.Rows[i] := Grid.Rows[i + 1];
Grid.RowCount := Grid.RowCount - 1;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
GridDeleteRow(3, stringGrid1);
end;
Переход по ячейкам в TStringGrid по клавиши Enter как по Tab.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.StringGrid1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
with StringGrid1 do
if Col then {next column}
Col := Col + 1
else if Row then
begin {next Row}
Row := Row + 1;
Col := 1;
end
else
begin {End of Grid- Go to Top again}
Row := 1;
Col := 1;
end;
end;
Вставка и удаление строк из TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// For this tip you need a StringGrid1 and a Button1.
// Fur diesen Tip braucht man ein StringGrid1 und einen Button1.
{...}
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{...}
public
{...}
end;
type
TStringGridHack = class(TStringGrid)
protected
procedure DeleteRow(ARow: Longint); reintroduce;
procedure InsertRow(ARow: Longint);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TStringGridHack.DeleteRow(ARow: Longint);
var
GemRow: Integer;
begin
GemRow := Row;
if RowCount > FixedRows + 1 then
inherited DeleteRow(ARow)
else
Rows[ARow].Clear;
if GemRow < RowCount then Row := GemRow;
end;
procedure TStringGridHack.InsertRow(ARow: Longint);
var
GemRow: Integer;
begin
GemRow := Row;
while ARow < FixedRows do Inc(ARow);
RowCount := RowCount + 1;
MoveRow(RowCount - 1, ARow);
Row := GemRow;
Rows[Row].Clear;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Insert Row, Zeile hinzufugen
TStringGridHack(StringGrid1).InsertRow(1);
// Remove Row, Zeile entfernen
TStringGridHack(StringGrid1).DeleteRow(2);
end;
end.
Определить, отображаются ли полосы прокрутки для TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
if (GetWindowlong(Stringgrid1.Handle, GWL_STYLE) and WS_VSCROLL) <> 0 then
ShowMessage('Vertical scrollbar is visible!');
if (GetWindowlong(Stringgrid1.Handle, GWL_STYLE) and WS_HSCROLL) <> 0 then
ShowMessage('Horizontal scrollbar is visible!');
Многострочный TStringGrid 4.
Автор: Пётр Соболь.
Была необходимость в использовании многострочного TStringGrida. Не один из трех способов создания не сработал, Переработал статью о подобной проблеме с ТDВGгidом. Получилось нечто очень компактное, чем и решил поделиться. Обработка того же события прорисовки, в uses надо добавить WinProcs:
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow:
Integer;
Rect: TRect; State: TGridDrawState);
var
Format: Word;
C: array[0..255] of Char;
begin
Format := DT_LEFT or DT_WORDBREAK;
(Sender as TStringGrid).Canvas.FillRect(Rect);
StrPCopy(C, (Sender as TStringGrid).Cells[ACol, ARow]);
WinProcs.DrawText((Sender as TStringGrid).Canvas.Handle, C,
StrLen(C), Rect, Format);
end;
Увеличение ячейки TStringGrid при увеличении числа строк.
Автор: Пётр Соболь.
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow:
Integer; Rect: TRect; State: TGridDrawState);
var
Format: Word;
C: array[0..255] of Char;
r: integer;
begin
C := '';
Format := DT_LEFT or DT_WORDBREAK;
(Sender as TStringGrid).Canvas.FillRect(Rect);
StrPCopy(C, (Sender as TStringGrid).Cells[ACol, ARow]);
if c <> '' then //если есть значения
begin
r := WinProcs.DrawText((Sender as TStringGrid).Canvas.Handle, C,
StrLen(C), Rect, Format);
if r > (Sender as TStringGrid).RowHeights[Arow] then
//если высота колонки меньше
(Sender as TStringGrid).RowHeights[Arow] := r;
end;
end;
Как перемещать строки и колонки в StringGrid.
| В процессе социологического опроса, проведенного в корпорации "Мелкий-Мягкий", выяснено, что: 99% сотрудников корпорации любят все большое и негибкое. |
Пользователь может перемещать строки и колонки StringGrid при помощи мышки. Можно ли это сделать программно? В описании TCustomGrid можно увидеть методы MoveColumn и MoveRow, однако они скрыты в TStringGrid. Но нам ничего не мешает просабклассить TStringGrid и объявить эти методы как public:
type
TStringGridX = class(TStringGrid)
public
procedure MoveColumn(FromIndex, ToIndex: Longint);
procedure MoveRow(FromIndex, ToIndex: Longint);
end;
Чтобы воспользоваться этими методами, достаточно вызвать соответствующий метод предка:
procedure TStringGridX.MoveColumn(FromIndex, ToIndex: Integer); begin inherited; end; procedure TStringGridX.MoveRow(FromIndex, ToIndex: Integer); begin inherited; end;
Этот компонент не нужно регистрировать в палитре компонентов. Просто используйте потомка TStringGrid или любого TCustomGrid, и вызывайте его методы:
procedure TForm1.Button1Click(Sender: TObject); begin TStringGridX(StringGrid1).MoveColumn(1, 3); end;
Как перемещать строки и колонки в StringGrid 2.
Автор: Vit
type TFake = class(TStringGrid); ... procedure TForm1.Button1Click(Sender: TObject); begin TFake(StringGrid1).MoveColumn(1, 3); end;
Многострочный TStringGrid 3.
Автор: http://www.sources.ruСперва необходимо установить свойство DefaultDrawing в False. Далее, необходимо вставить следующий код в обработчик события OnDrawCell:
procedure TForm1.StringGrid1DrawCell(Sender: TObject;
Col, Row: Longint;
Rect: TRect;
State: TGridDrawState);
var
Line1: string;
Line2: string;
ptr: integer;
padding: integer;
hGrid: TStringGrid;
begin
hGrid := (Sender as TStringGrid);
ptr := Pos(';', hGrid.Cells[Col, Row]);
if ptr > 0 then
begin
Line1 := Copy(hGrid.Cells[Col, Row], 1, ptr - 1);
Line2 := Copy(hGrid.Cells[Col, Row], ptr + 1,
Length(hGrid1.Cells[Col, Row]) - ptr);
end
else
Line1 := hGrid.Cells[Col, Row];
hGrid.Canvas.FillRect(Rect);
hGrid.Canvas.TextOut(Rect.Left, Rect.Top + 2, Line1);
if ptr > 0 then
hGrid.Canvas.TextOut(Rect.Left, Rect.Top -
hGrid.Canvas.Font.Height + 3, Line2);
end;
Теперь достаточно для переноса строки вставить в неё точку с запятой. Так же не забудьте изменить высоту строки так, чтобы переносы строки поместились в ячейку:
StringGrid1.RowHeights[0] := StringGrid1.DefaultRowHeight * 2;
Распечатать TStringGrid 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
printers;
//StringGrid Inhalt ausdrucken
procedure PrintStringGrid(Grid: TStringGrid; Title: string;
Orientation: TPrinterOrientation);
var
P, I, J, YPos, XPos, HorzSize, VertSize: Integer;
AnzSeiten, Seite, Zeilen, HeaderSize, FooterSize, ZeilenSize, FontHeight: Integer;
mmx, mmy: Extended;
Footer: string;
begin
//Kopfzeile, Fu?zeile, Zeilenabstand, Schriftgro?e festlegen
HeaderSize := 100;
FooterSize := 200;
ZeilenSize := 36;
FontHeight := 36;
//Printer initializieren
Printer.Orientation := Orientation;
Printer.Title := Title;
Printer.BeginDoc;
//Druck auf mm einstellen
mmx := GetDeviceCaps(Printer.Canvas.Handle, PHYSICALWIDTH) /
GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSX) * 25.4;
mmy := GetDeviceCaps(Printer.Canvas.Handle, PHYSICALHEIGHT) /
GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY) * 25.4;
VertSize := Trunc(mmy) * 10;
HorzSize := Trunc(mmx) * 10;
SetMapMode(Printer.Canvas.Handle, MM_LOMETRIC);
//Zeilenanzahl festlegen
Zeilen := (VertSize - HeaderSize - FooterSize) div ZeilenSize;
//Seitenanzahl ermitteln
if Grid.RowCount mod Zeilen <> 0 then
AnzSeiten := Grid.RowCount div Zeilen + 1
else
AnzSeiten := Grid.RowCount div Zeilen;
Seite := 1;
//Grid Drucken
for P := 1 to AnzSeiten do
begin
//Kopfzeile
Printer.Canvas.Font.Height := 48;
Printer.Canvas.TextOut((HorzSize div 2 - (Printer.Canvas.TextWidth(Title) div 2)),
- 20,Title);
Printer.Canvas.Pen.Width := 5;
Printer.Canvas.MoveTo(0, - HeaderSize);
Printer.Canvas.LineTo(HorzSize, - HeaderSize);
//Fu?zeile
Printer.Canvas.MoveTo(0, - VertSize + FooterSize);
Printer.Canvas.LineTo(HorzSize, - VertSize + FooterSize);
Printer.Canvas.Font.Height := 36;
Footer := 'Seite: ' + IntToStr(Seite) + ' von ' + IntToStr(AnzSeiten);
Printer.Canvas.TextOut((HorzSize div 2 - (Printer.Canvas.TextWidth(Footer) div 2)),
- VertSize + 150,Footer);
//Zeilen drucken
Printer.Canvas.Font.Height := FontHeight;
YPos := HeaderSize + 10;
for I := 1 to Zeilen do
begin
if Grid.RowCount >= I + (Seite - 1) * Zeilen then
begin
XPos := 0;
for J := 0 to Grid.ColCount - 1 do
begin
Printer.Canvas.TextOut(XPos, - YPos,
Grid.Cells[J, I + (Seite - 1) * Zeilen - 1]);
XPos := XPos + Grid.ColWidths[J] * 3;
end;
YPos := YPos + ZeilenSize;
end;
end;
//Seite hinzufugen
Inc(Seite);
if Seite <= AnzSeiten then Printer.NewPage;
end;
Printer.EndDoc;
end;
//Example
procedure TForm1.Button1Click(Sender: TObject);
begin
//Drucken im Querformat
PrintStringGrid(Grid, 'StringGrid Print Landscape', poLandscape);
//Drucken im Hochformat
PrintStringGrid(Grid, 'StringGrid Print Portrait', poPortrait);
end;
Изменение размеров колонок в StringGrid.
Оформил: DeeCo
Ниже приведён примен кода, который позволяет автоматически подогнать размер колонки в компененте TStringGrid, под размер самой длинной строки текста в колонке:
procedure AutoSizeGridColumn(Grid: TStringGrid;
column: integer);
var
i: integer;
temp: integer;
max: integer;
begin
max := 0;
for i := 0 to (Grid.RowCount - 1) do
begin
temp := Grid.Canvas.TextWidth(grid.cells[column, i]);
if temp > max then
max := temp;
end;
Grid.ColWidths[column] := Max + Grid.GridLineWidth + 3;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AutoSizeGridColumn(StringGrid1, 1);
end;
Обмен строк TStringGrid.
Автор: MBo WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Обмен строк StringGrid
Обмен содержимого указанных строк StringGrid.
Варианты без копирования связанных с ячейками объектов и вместе с ними.
Зависимости: Grids
Автор: Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright: MBo
Дата: 27 апреля 2002 г.
***************************************************** }
procedure SGExchangeRows(SG: TStringGrid; Row1, Row2: Integer);
var
TempString: string;
begin
if (Row1 in [0..SG.RowCount - 1]) and (Row2 in [0..SG.RowCount - 1]) then
begin
TempString := SG.Rows[Row1].Text;
SG.Rows[Row1].Assign(SG.Rows[Row2]);
SG.Rows[Row2].Text := TempString;
end;
end;
procedure SGExchRowsWithObj(SG: TStringGrid; Row1, Row2: Integer);
var
TempList: TStringList;
begin
with SG do
if (Row1 in [0..RowCount - 1]) and (Row2 in [0..RowCount - 1]) then
begin
TempList := TStringList.Create;
TempList.Assign(Rows[Row1]);
Rows[Row1].Assign(Rows[Row2]);
Rows[Row2].Assign(TempList);
TempList.Free;
end;
end;
Удаление строки из TStringGrid.
Автор: MBo WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Удаление строки из StringGrid
Удаляет из StringGrid указанную строку, сдвигая остальные.
Зависимости: Grids
Автор: Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright: MBo
Дата: 27 апреля 2002 г.
***************************************************** }
procedure SGDeleteRow(SG: TStringGrid; RowToDelete: Integer);
var
i: Integer;
begin
with SG do
begin
if (RowToDelete >= 0) and (RowToDelete < RowCount) then
begin
for i := RowToDelete to RowCount - 2 do
Rows[i].Assign(Rows[i + 1]);
RowCount := RowCount - 1;
end;
end;
end;
Вставка строки в TStringGrid.
Автор: MBo WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вставка строки в StringGrid
Вставляет новую строку в указанную позицию StringGrid
Зависимости: Grids
Автор: Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright: MBo
Дата: 27 апреля 2002 г.
***************************************************** }
procedure SGInsertRow(SG: TStringGrid; NewRow: Integer);
var
i: Integer;
begin
if NewRow < 0 then
NewRow := 0; // либо 1, задайте нужное вам поведение
with SG do
begin
RowCount := RowCount + 1;
if NewRow < RowCount - 1 then
begin
for i := RowCount - 1 downto NewRow + 1 do
Rows[i].Assign(Rows[i - 1]);
end;
Rows[NewRow].Clear;
end;
end;
Перестановка строки в TStringGrid в другую позицию.
Автор: MBo WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Перестановка строки в StringGrid в другую позицию
Передвигает строку StringGrid из позиции FromRow в позицию ToRow, сдвигая остальные
Зависимости: Grids
Автор: Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright: MBo
Дата: 28 апреля 2002 г.
***************************************************** }
procedure SGMoveRow(SG: TStringGrid; FromRow, ToRow: Integer);
var
TempList: TStringList;
i: Integer;
begin
with SG do
if (FromRow in [0..RowCount - 1]) and (ToRow in [0..RowCount - 1]) then
begin
TempList := TStringList.Create;
TempList.Assign(Rows[FromRow]);
if FromRow > ToRow then
for i := FromRow downto ToRow + 1 do
Rows[i].Assign(Rows[i - 1])
else
for i := FromRow to ToRow - 1 do
Rows[i].Assign(Rows[i + 1]);
Rows[ToRow].Assign(TempList);
TempList.Free;
end;
end;
Сохранить TStringGrid в файл, загрузить из файла.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// Save a TStringGrid to a file
procedure SaveStringGrid(StringGrid: TStringGrid; const FileName: TFileName);
var
f: TextFile;
i, k: Integer;
begin
AssignFile(f, FileName);
Rewrite(f);
with StringGrid do
begin
// Write number of Columns/Rows
Writeln(f, ColCount);
Writeln(f, RowCount);
// loop through cells
for i := 0 to ColCount - 1 do
for k := 0 to RowCount - 1 do
Writeln(F, Cells[i, k]);
end;
CloseFile(F);
end;
// Load a TStringGrid from a file
procedure LoadStringGrid(StringGrid: TStringGrid; const FileName: TFileName);
var
f: TextFile;
iTmp, i, k: Integer;
strTemp: String;
begin
AssignFile(f, FileName);
Reset(f);
with StringGrid do
begin
// Get number of columns
Readln(f, iTmp);
ColCount := iTmp;
// Get number of rows
Readln(f, iTmp);
RowCount := iTmp;
// loop through cells & fill in values
for i := 0 to ColCount - 1 do
for k := 0 to RowCount - 1 do
begin
Readln(f, strTemp);
Cells[i, k] := strTemp;
end;
end;
CloseFile(f);
end;
// Save StringGrid1 to 'c:\temp.txt':
procedure TForm1.Button1Click(Sender: TObject);
begin
SaveStringGrid(StringGrid1, 'c:\temp.txt');
end;
// Load StringGrid1 from 'c:\temp.txt':
procedure TForm1.Button2Click(Sender: TObject);
begin
LoadStringGrid(StringGrid1, 'c:\temp.txt');
end;
Позиционирование каретки в TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
The following code allows you to position the caret
in a cell (InplaceEditor) of a StringGrid.
We need a Cracker class to access the InplaceEditor.
Mit folgendem Code kann man den Cursor in einer Zelle
(InplaceEditor) eines StringGrids positionieren.
Hierfur brauchen wir eine "Cracker" Klasse, weil der
InplaceEditor "protected" ist.
}
type
TGridCracker = class(TStringGrid);
{...}
implementation
{...}
procedure SetCaretPosition(Grid: TStringGrid; col, row, x_pos: Integer);
begin
Grid.Col := Col;
Grid.Row := Row;
with TGridCracker(Grid) do
InplaceEditor.SelStart := x_pos;
end;
// Get the Caret position from the focussed cell
// Ermittelt die Caret-Position der aktuellen Zelle
function GetCaretPosition(Grid: TStringGrid): Integer;
begin
with TGridCracker(Grid) do
Result := InplaceEditor.SelStart;
end;
// Example / Beispiel:
// Set the focus on col 1, row 3 and position the caret at position 5
// Fokusiert die Zelle(1,3) und setzt den Cursor auf Position 5
procedure TForm1.Button1Click(Sender: TObject);
begin
StringGrid1.SetFocus;
SetCaretPosition(StringGrid1, 1, 3, 5);
end;
Сортировка столбцов в StringGrid.
Автор: http://www.sources.ru
procedure GridSort(StrGrid: TStringGrid; NoColumn: Integer);
var
Line, PosActual: Integer;
Row: TStrings;
begin
Renglon := TStringList.Create;
for Line := 1 to StrGrid.RowCount - 1 do
begin
PosActual := Line;
Row.Assign(TStringlist(StrGrid.Rows[PosActual]));
while True do
begin
if (PosActual = 0) or (StrToInt(Row.Strings[NoColumn - 1]) >=
StrToInt(StrGrid.Cells[NoColumn - 1, PosActual - 1])) then
Break;
StrGrid.Rows[PosActual] := StrGrid.Rows[PosActual - 1];
Dec(PosActual);
end;
if StrToInt(Row.Strings[NoColumn - 1]) < StrToInt(StrGrid.Cells[NoColumn -
1, PosActual]) then
StrGrid.Rows[PosActual] := Row;
end;
Renglon.Free;
end;
Сортировка столбцов в StringGrid 2.
Автор: http://www.sources.ru
type
TStringGridExSortType = (srtAlpha, srtInteger, srtDouble);
procedure GridSort(SG: TStringGrid; ByColNumber, FromRow, ToRow: integer;
SortType: TStringGridExSortType = srtAlpha);
var
Temp: TStringList;
function SortStr(Line: string): string;
var
RetVar: string;
begin
case SortType of
srtAlpha: Retvar := Line;
srtInteger: Retvar := FormatFloat('000000000', StrToIntDef(trim(Line),
0));
srtDouble:
try
Retvar := FormatFloat('000000000.000000', StrToFloat(trim(Line)));
except
RetVar := '0.00';
end;
end;
Result := RetVar;
end;
// Рекурсивный QuickSort
procedure QuickSort(Lo, Hi: integer; CC: TStrings);
procedure Sort(l, r: integer);
var
i, j: integer;
x: string;
begin
i := l;
j := r;
x := SortStr(CC[(l + r) div 2]);
repeat
while SortStr(CC[i]) < x do
inc(i);
while x < SortStr(CC[j]) do
dec(j);
if i <= j then
begin
Temp.Assign(SG.Rows[j]); // Меняем местами 2 строки
SG.Rows[j].Assign(SG.Rows[i]);
SG.Rows[i].Assign(Temp);
inc(i);
dec(j);
end;
until i > j;
if l < j then
sort(l, j);
if i < r then
sort(i, r);
end;
begin {quicksort}
;
Sort(Lo, Hi);
end;
begin
Temp := TStringList.Create;
QuickSort(FromRow, ToRow, SG.Cols[ByColNumber]);
Temp.Free;
end;
Сортировка TStringGrid.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure SortStringGrid(var GenStrGrid: TStringGrid; ThatCol: Integer);
const
// Define the Separator
TheSeparator = '@';
var
CountItem, I, J, K, ThePosition: integer;
MyList: TStringList;
MyString, TempString: string;
begin
// Give the number of rows in the StringGrid
CountItem := GenStrGrid.RowCount;
//Create the List
MyList := TStringList.Create;
MyList.Sorted := False;
try
begin
for I := 1 to (CountItem - 1) do
MyList.Add(GenStrGrid.Rows[I].Strings[ThatCol] + TheSeparator +
GenStrGrid.Rows[I].Text);
//Sort the List
Mylist.Sort;
for K := 1 to Mylist.Count do
begin
//Take the String of the line (K – 1)
MyString := MyList.Strings[(K - 1)];
//Find the position of the Separator in the String
ThePosition := Pos(TheSeparator, MyString);
TempString := '';
{Eliminate the Text of the column on which we have sorted the StringGrid}
TempString := Copy(MyString, (ThePosition + 1), Length(MyString));
MyList.Strings[(K - 1)] := '';
MyList.Strings[(K - 1)] := TempString;
end;
// Refill the StringGrid
for J := 1 to (CountItem - 1) do
GenStrGrid.Rows[J].Text := MyList.Strings[(J - 1)];
end;
finally
//Free the List
MyList.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Sort the StringGrid1 on the second Column
// StringGrid1 nach der 1. Spalte sortieren
SortStringGrid(StringGrid1, 1);
end;
Копирование содержимого TStringGrid в буфер обмена.
Автор: MBo WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Копирование содержимого StringGrid в буфер обмена
Копирует содержимое ячеек StringGrid в ClipBoard в формате, позволяющем
вставку, например, в Word или Excel. При CopySel=True копирует выделение,
иначе всю таблицу или указанный диапазон (CL- левый столбец и т.д.).
Зависимости: Grids
Автор: Борис Новгородов (MBo), mbo@mail.ru, Новосибирск
Copyright: MBo
Дата: 30 апреля 2002 г.
***************************************************** }
procedure SGCopyToCLP(SG: TStringGrid; CopySel: Boolean; CL: integer = -1;
RT: integer = -1; CR: integer = -1; RB: integer = -1);
var
i, j: Integer;
s: string;
begin
s := '';
with SG do
begin
if CopySel then
begin
CL := Selection.Left;
CR := Selection.Right;
RT := Selection.Top;
RB := Selection.Bottom;
end;
//при необходимости FixedRows и FixedCols можно заменить на 0
if (CL < FixedCols) or (CL > CR) or (CL >= ColCount) then
CL := FixedCols;
if (CR < FixedCols) or (CL > CR) or (CR >= ColCount) then
CR := ColCount - 1;
if (RT < FixedRows) or (RT > RB) or (RT >= RowCount) then
RT := FixedRows;
if (RB < FixedCols) or (RT > RB) or (RB >= RowCount) then
RB := RowCount - 1;
for i := RT to RB do
begin
for j := CL to CR do
begin
s := s + Cells[j, i];
if j < CR then
s := s + #9;
end;
s := s + #13#10;
end;
end;
ClipBoard.AsText := s;
end;
// Пример использования:
SGCopyToCLP(StringGrid1, True); //выделение
SGCopyToCLP(StringGrid1, False); //все ячейки
SGCopyToCLP(StringGrid1, False, 1, 1, 3, 2); //диапазон, 6 ячеек
Экспортировать TStringGrid в Excel-файл.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ With OLE Automation }
uses
ComObj;
function RefToCell(ARow, ACol: Integer): string;
begin
Result := Chr(Ord('A') + ACol - 1) + IntToStr(ARow);
end;
function SaveAsExcelFile(AGrid: TStringGrid; ASheetName, AFileName: string): Boolean;
const
xlWBATWorksheet = -4167;
var
Row, Col: Integer;
GridPrevFile: string;
XLApp, Sheet, Data: OLEVariant;
i, j: Integer;
begin
// Prepare Data
Data := VarArrayCreate([1, AGrid.RowCount, 1, AGrid.ColCount], varVariant);
for i := 0 to AGrid.ColCount - 1 do
for j := 0 to AGrid.RowCount - 1 do
Data[j + 1, i + 1] := AGrid.Cells[i, j];
// Create Excel-OLE Object
Result := False;
XLApp := CreateOleObject('Excel.Application');
try
// Hide Excel
XLApp.Visible := False;
// Add new Workbook
XLApp.Workbooks.Add(xlWBatWorkSheet);
Sheet := XLApp.Workbooks[1].WorkSheets[1];
Sheet.Name := ASheetName;
// Fill up the sheet
Sheet.Range[RefToCell(1, 1), RefToCell(AGrid.RowCount,
AGrid.ColCount)].Value := Data;
// Save Excel Worksheet
try
XLApp.Workbooks[1].SaveAs(AFileName);
Result := True;
except
// Error ?
end;
finally
// Quit Excel
if not VarIsEmpty(XLApp) then
begin
XLApp.DisplayAlerts := False;
XLApp.Quit;
XLAPP := Unassigned;
Sheet := Unassigned;
end;
end;
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
if SaveAsExcelFile(stringGrid1, 'My Stringgrid Data', 'c:\MyExcelFile.xls') then
ShowMessage('StringGrid saved!');
end;
Экспортировать TStringGrid в Excel-файл 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ Without OLE }
procedure XlsWriteCellLabel(XlsStream: TStream; const ACol, ARow: Word;
const AValue: string);
var
L: Word;
const
{$J+}
CXlsLabel: array[0..5] of Word = ($204, 0, 0, 0, 0, 0);
{$J-}
begin
L := Length(AValue);
CXlsLabel[1] := 8 + L;
CXlsLabel[2] := ARow;
CXlsLabel[3] := ACol;
CXlsLabel[5] := L;
XlsStream.WriteBuffer(CXlsLabel, SizeOf(CXlsLabel));
XlsStream.WriteBuffer(Pointer(AValue)^, L);
end;
function SaveAsExcelFile(AGrid: TStringGrid; AFileName: string): Boolean;
const
{$J+} CXlsBof: array[0..5] of Word = ($809, 8, 00, $10, 0, 0); {$J-}
CXlsEof: array[0..1] of Word = ($0A, 00);
var
FStream: TFileStream;
I, J: Integer;
begin
Result := False;
FStream := TFileStream.Create(PChar(AFileName), fmCreate or fmOpenWrite);
try
CXlsBof[4] := 0;
FStream.WriteBuffer(CXlsBof, SizeOf(CXlsBof));
for i := 0 to AGrid.ColCount - 1 do
for j := 0 to AGrid.RowCount - 1 do
XlsWriteCellLabel(FStream, I, J, AGrid.cells[i, j]);
FStream.WriteBuffer(CXlsEof, SizeOf(CXlsEof));
Result := True;
finally
FStream.Free;
end;
end;
// Example:
procedure TForm1.Button2Click(Sender: TObject);
begin
if SaveAsExcelFile(StringGrid1, 'c:\MyExcelFile.xls') then
ShowMessage('StringGrid saved!');
end;
Экспортировать TStringGrid в Excel-файл 3.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ Code by Reinhard Schatzl }
uses
ComObj;
// Hilfsfunktion fur StringGridToExcelSheet
// Helper function for StringGridToExcelSheet
function RefToCell(RowID, ColID: Integer): string;
var
ACount, APos: Integer;
begin
ACount := ColID div 26;
APos := ColID mod 26;
if APos = 0 then
begin
ACount := ACount - 1;
APos := 26;
end;
if ACount = 0 then
Result := Chr(Ord('A') + ColID - 1) + IntToStr(RowID);
if ACount = 1 then
Result := 'A' + Chr(Ord('A') + APos - 1) + IntToStr(RowID);
if ACount > 1 then
Result := Chr(Ord('A') + ACount - 1) + Chr(Ord('A') + APos - 1) + IntToStr(RowID);
end;
// StringGrid Inhalt in Excel exportieren
// Export StringGrid contents to Excel
function StringGridToExcelSheet(Grid: TStringGrid; SheetName, FileName: string;
ShowExcel: Boolean): Boolean;
const
xlWBATWorksheet = -4167;
var
SheetCount, SheetColCount, SheetRowCount, BookCount: Integer;
XLApp, Sheet, Data: OLEVariant;
I, J, N, M: Integer;
SaveFileName: string;
begin
//notwendige Sheetanzahl feststellen
SheetCount := (Grid.ColCount div 256) + 1;
if Grid.ColCount mod 256 = 0 then
SheetCount := SheetCount - 1;
//notwendige Bookanzahl feststellen
BookCount := (Grid.RowCount div 65536) + 1;
if Grid.RowCount mod 65536 = 0 then
BookCount := BookCount - 1;
//Create Excel-OLE Object
Result := False;
XLApp := CreateOleObject('Excel.Application');
try
//Excelsheet anzeigen
if ShowExcel = False then
XLApp.Visible := False
else
XLApp.Visible := True;
//Workbook hinzufugen
for M := 1 to BookCount do
begin
XLApp.Workbooks.Add(xlWBATWorksheet);
//Sheets anlegen
for N := 1 to SheetCount - 1 do
begin
XLApp.Worksheets.Add;
end;
end;
//Sheet ColAnzahl feststellen
if Grid.ColCount <= 256 then
SheetColCount := Grid.ColCount
else
SheetColCount := 256;
//Sheet RowAnzahl feststellen
if Grid.RowCount <= 65536 then
SheetRowCount := Grid.RowCount
else
SheetRowCount := 65536;
//Sheets befullen
for M := 1 to BookCount do
begin
for N := 1 to SheetCount do
begin
//Daten aus Grid holen
Data := VarArrayCreate([1, Grid.RowCount, 1, SheetColCount], varVariant);
for I := 0 to SheetColCount - 1 do
for J := 0 to SheetRowCount - 1 do
if ((I + 256 * (N - 1)) <= Grid.ColCount) and
((J + 65536 * (M - 1)) <= Grid.RowCount) then
Data[J + 1, I + 1] := Grid.Cells[I + 256 * (N - 1), J + 65536 * (M - 1)];
//-------------------------
XLApp.Worksheets[N].Select;
XLApp.Workbooks[M].Worksheets[N].Name := SheetName + IntToStr(N);
//Zellen als String Formatieren
XLApp.Workbooks[M].Worksheets[N].Range[RefToCell(1, 1),
RefToCell(SheetRowCount, SheetColCount)].Select;
XLApp.Selection.NumberFormat := '@';
XLApp.Workbooks[M].Worksheets[N].Range['A1'].Select;
//Daten dem Excelsheet ubergeben
Sheet := XLApp.Workbooks[M].WorkSheets[N];
Sheet.Range[RefToCell(1, 1), RefToCell(SheetRowCount, SheetColCount)].Value :=
Data;
end;
end;
//Save Excel Worksheet
try
for M := 1 to BookCount do
begin
SaveFileName := Copy(FileName, 1,Pos('.', FileName) - 1) + IntToStr(M) +
Copy(FileName, Pos('.', FileName),
Length(FileName) - Pos('.', FileName) + 1);
XLApp.Workbooks[M].SaveAs(SaveFileName);
end;
Result := True;
except
// Error ?
end;
finally
//Excel Beenden
if (not VarIsEmpty(XLApp)) and (ShowExcel = False) then
begin
XLApp.DisplayAlerts := False;
XLApp.Quit;
XLAPP := Unassigned;
Sheet := Unassigned;
end;
end;
end;
//Example
procedure TForm1.Button1Click(Sender: TObject);
begin
//StringGrid inhalt in Excel exportieren
//Grid : stringGrid, SheetName : stringgrid Print, Pfad : c:\Test\ExcelFile.xls, Excelsheet anzeigen
StringGridToExcelSheet(StringGrid, 'Stringgrid Print', 'c:\Test\ExcelFile.xls', True);
end;
Перевести TStringGrid в HTML.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ The SGridToHtml() function converts a Stringgrid to a HTML Code.
Parameters: SG: TStringGrid to convert
Dest: TMemo to show the HTML Code
BorderSize: Size of border (0 = show no border)
{
Mit der Funktion SGridToHtml() konnen StringGrids in HTML Code umgewandelt werden.
SG bezieht sich auf das TStringGrid, welches codiert werden soll.
in Dest (vom Typ TMemo) wird der HTML Code erzeugt.
BorderSize bestimmt, wie dick der Tabellenrahmen sein soll. 0 = kein Rahmen
}
procedure SGridToHtml(SG: TStringgrid; Dest: TMemo; BorderSize: Integer);
var
i, p: integer;
SStyle1, SStyle2, Text: string;
begin
Dest.Clear;
Dest.Lines.Add('<html>');
Dest.Lines.Add('<body>');
Dest.Lines.Add(' <table border="' + IntToStr(BorderSize) + '" width="' +
IntToStr(SG.Width) + '" height="' + IntToStr(SG.Width) + '">');
for i := 0 to SG.RowCount - 1 do
begin
Dest.Lines.Add(' <tr>');
for p := 0 to SG.ColCount - 1 do
begin
SStyle1 := '';
SStyle2 := '';
if fsbold in SG.Font.Style then
begin
SStyle1 := SStyle1 + '<b>';
SStyle2 := SStyle2 + '</b>';
end;
if fsitalic in SG.Font.Style then
begin
SStyle1 := SStyle1 + '<i>';
SStyle2 := SStyle2 + '</i>';
end;
if fsunderline in SG.Font.Style then
begin
SStyle1 := SStyle1 + '<u>';
SStyle2 := SStyle2 + '</u>';
end;
Text := sg.Cells[p, i];
if Text = '' then Text := ' ';
Dest.Lines.Add(' <td width="' + IntToStr(sg.ColWidths[p]) +
'" height="' + IntToStr(sg.RowHeights[p]) +
'"><font color="#' + IntToHex(sg.Font.Color, 6) +
'" face="' + SG.Font.Name + '">' + SStyle1 +
Text + SStyle2 + '</font></td>');
end;
Dest.Lines.Add(' </tr>');
end;
Dest.Lines.Add(' </table>');
Dest.Lines.Add('</body>');;
Dest.Lines.Add('</html>');
end;
// Example, Beispiel
procedure TFormCSVInport.Button6Click(Sender: TObject);
begin
SGridToHtml(StringGrid1, Memo1, 1);
Memo1.Lines.SaveToFile('c:\test.html');
end;
Как экспортировать StringGrid в MS Word таблицу.
Автор: http://www.swissdelphicenter.ch
uses
ComObj;
procedure TForm1.Button1Click(Sender: TObject);
var
WordApp, NewDoc, WordTable: OLEVariant;
iRows, iCols, iGridRows, jGridCols: Integer;
begin
try
// Create a Word Instance
// Word Instanz erzeugen
WordApp := CreateOleObject('Word.Application');
except
// Error...
// Fehler....
Exit;
end;
// Show Word
// Word anzeigen
WordApp.Visible := True;
// Add a new Doc
// Neues Dok einfugen
NewDoc := WordApp.Documents.Add;
// Get number of columns, rows
// Spalten, Reihen ermitteln
iCols := StringGrid1.ColCount;
iRows := StringGrid1.RowCount;
// Add a Table
// Tabelle einfugen
WordTable := NewDoc.Tables.Add(WordApp.Selection.Range, iCols, iRows);
// Fill up the word table with the Stringgrid contents
// Tabelle ausfullen mit Stringgrid Daten
for iGridRows := 1 to iRows do
for jGridCols := 1 to iCols do
WordTable.Cell(iGridRows, jGridCols).Range.Text :=
StringGrid1.Cells[jGridCols - 1, iGridRows - 1];
// Here you might want to Save the Doc, quit Word...
// Hier evtl Word Doc speichern, beenden...
// ...
// Cleanup...
WordApp := Unassigned;
NewDoc := Unassigned;
WordTable := Unassigned;
end;
Затенить компонент.

procedure ShadeIt(f: TForm; c: TControl; Width: Integer; Color: TColor);
var
rect: TRect;
old: TColor;
begin
if (c.Visible) then
begin
rect := c.BoundsRect;
rect.Left := rect.Left + Width;
rect.Top := rect.Top + Width;
rect.Right := rect.Right + Width;
rect.Bottom := rect.Bottom + Width;
old := f.Canvas.Brush.Color;
f.Canvas.Brush.Color := Color;
f.Canvas.fillrect(rect);
f.Canvas.Brush.Color := old;
end;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
i: Integer;
begin
for i := 0 to Self.ControlCount - 1 do
ShadeIt(Self, Self.Controls[i], 3, clBlack);
end;
|
Как получить эффект тени для Hint.
Автор: http://www.swissdelphicenter.ch
type
TXPHintWindow = class(THintWindow)
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WMNCPaint(var msg: TMessage); message WM_NCPAINT;
end;
function IsWinXP: Boolean;
begin
Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and
(Win32MajorVersion >= 5) and (Win32MinorVersion >= 1);
end;
procedure TXPHintWindow.CreateParams(var Params: TCreateParams);
const
CS_DROPSHADOW = $00020000;
begin
inherited;
if IsWinXP then
Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
procedure TXPHintWindow.WMNCPaint(var msg: TMessage);
var
R: TRect;
DC: HDC;
begin
DC := GetWindowDC(Handle);
try
R := Rect(0, 0, Width, Height);
DrawEdge(DC, R, EDGE_ETCHED, BF_RECT or BF_MONO);
finally
ReleaseDC(Handle, DC);
end;
end;
initialization
HintWindowClass := TXPHintWindow;
Application.ShowHint := False;
Application.ShowHint := True;
end.
Перетасовка экрана.

| Системные и прикладные программисты едут на конференцию. Встречаются у касс вокзала, где и те и другие берут билеты. Прикладники покупают по билету на нос, системщики берут один билет. Удивленные прикладники спрашивают: - У вас че только один человек едет? - Да нет. Все. - А как же вы? - А это наши трудности. В поезде прикладники занимают места согласно купленных билетов за полчаса до отправления. За 45 сек. До отхода появляется стая системщиков. С криками вся толпа врывается в вагон закрывается в туалете. Поезд трогается. Контроллер подходит к туалету и стучит. Высовывается рука, протягивает билет. Через некоторое время системщики, как тараканы, расползаются по поезду. Едут обратно. Опять встречаются на вокзале. Прикладники, укравшие копирайт берут один билет. Системщики билета не берут. За 45 сек до отхода врывается толпа прикладников и запирается в туалете. Поезд трогается. Стук в дверь туалета. Выосвывается рука, протягивает билет. Системщик хватает билет и бежит в другой туалет. |
В статье описывается пример, который позволяет разделить экран на блоки, а затем поменять эти блоки местами. Так же можно менять размеры блоков и скорость их перемещения. На мой взгляд неплохое начало для создания логической игрушки либо экранной заставки.
Уверен, что каждый из Вас уже хоть раз видел что-то подобное в действии. При запуске, программа берёт изображение десктопа и разделяет его на определённое количество прямоугольных частей (одинакового размера). После этого часть блоков случайным образом перемещается со своего первоначального места.
Как это всё осуществить
Создайте новый проект Delphi с чистой формой. Установите свойство Name в 'Shuffler'. Добавьте на форму компоненты Image (Image1) и Timer (Timer1). Image будет содержать в себе изображение десктопа (разобранное), а Timer будет вызывать процедуру рисования. Свойство Interval компонента Timer определяет, как часто будет происходить перемешивание (значение 1000 эквивалентно одной секунде, 2000 - двум секундам).
Так же для проекта потребуется несколько глобальных переменных. Поместите следующий код перед секцией implementation в модуле формы:
var Shuffler: TShuffler; //это было добавлено самим Delphi DesktopBitmap : TBitmap; gx, gy : Integer; redRect : TBitmap; rW, rH : Integer; const DELTA = 8; //должно быть 2^n |
Значение константы (integer) DELTA определяет, на сколько частей будет разбит экран (строк и колонок). Число DELTA должно быть в виде 2^n, где n - целое (integer) число со знаком. Большое значение DELTA приводит к маленьким размерам блоков. Например, если DELTA равна 16 и разрешение экрана 1024 x 768, то экран будет поделён на 256 частей размером 64x48.
DesktopBitmap это битмап, который хранит в себе захваченное текущее изображение десктопа - мы будем получать это изображение делая скриншот. redRect это битмап картинка, которая заменяет перемещённую часть картинки. redRect создаётся в событии формы OnCreate. gx, gy содержат текущие координаты x и y (Left, Top) redRect внутри разобранного изображения. rW, rH это ширина и высота прямоугольного блока. Для 1024x768 и DELTA=16, rW будет равно 64 а rH = 48.Проект начинает выполняться с обработчика события OnCreate:
procedure TShuffler.FormCreate(Sender: TObject);
begin
rW := Screen.Width div DELTA;
rH := Screen.Height div DELTA;
redRect:=TBitmap.Create;
with redRect do
begin
Width := rW;
Height := rH;
Canvas.Brush.Color := clRed;
Canvas.Brush.Style := bssolid;
Canvas.Rectangle(0,0,rW,rH);
Canvas.Font.Color := clNavy;
Canvas.Font.Style := Canvas.Font.Style + [fsBold];
Canvas.TextOut(2,2,'About');
Canvas.Font.Style := Canvas.Font.Style - [fsBold];
Canvas.TextOut(2,17,'Delphi');
Canvas.TextOut(2,32,'Programming');
end;
Timer1.Enabled := False;
Image1.Align := alClient;
Visible := False;
BorderStyle := bsNone;
Top := 0;
Left := 0;
Width := Screen.Width;
Height := Screen.Height;
InitScreen;
// SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE + SWP_NOMOVE);
Visible := True;
Timer1.Interval := 10; // меньше := быстрее
Timer1.Enabled := True; // Запускаем вызов DrawScreen
end;
|
Во-первых, значения rW и rH определяются значением DELTA. Как уже объяснялось, если разрешение экрана 800x600 и DELTA равна 8, изображение экрана будет разделено на 8x8 частей размером 100x75 (rW = 100, rH = 75).
Во-вторых, созданный битмап redRect, будет размещён внутри картинки, с той целью, чтобы заменить перемещённый блок. redRect является простым красным прямоугольником с текстом (синим) внутри него. Так же для этого можно использовать готовую эмблему или что-то ещё.
Наконец, устанавливается ширина и высота формы как у экрана. Вызов (закомментированный) API функции SetWindowPos можно использовать, чтобы установить форму всегда на переднем плане (OnTop), не перемещаемую и не изменяемую. Вызывается процедура InitScreen. Устанавливает интервал таймера и начинает выполняться обработчик события OnTimer, запуская процедуру DrawScreen.
InitScreen - Скриншот
Процедура InitScreen, вызываемая из обработчика события OnCreate, используется для получения скриншота текущего изображения десктопа, устанавливая начальную позицию redRect и рисуя сетку. Код, который будет рисовать сетку необязателен.
Чтобы получить скриншот десктопа, используется GetDC для GetDesktopWindow. API функция BitBt используется для передачи картинки десктопа в DesktopBitmap. GetDC(GetDesktopWindow) получает дескриптор контекста устройства дисплея для указанного окна - окна возвращённого функцией GetDesktopWindow. В заключении DesktopBitmap ассоциируется с компонентой Image1. Если что-то не ясно, то советую заглянуть справичные файлы по Delphi.
Начальная позиция redRect выбирается случайным образом. Trunc(Random * DELTA) возвращает целое число от 0 до DELTA. Далее, redRect рисуется в точке gx, gy, используя функцию CopyRect объекта Canvas. Опять же, если Вы не знакомы с алгоритмом рисования Delphi, то советую порыться в справке.
В конце, при помощи MoveTo и LineTo рисуется сетка. Сетка необязательна и используется только для того, чтобы лучше различать границы блоков.
procedure InitScreen;
var
i, j: integer;
begin
//получаем битмап десктопа
DesktopBitmap := TBitmap.Create;
with DesktopBitmap do
begin
Width := Screen.Width;
Height := Screen.Height;
end;
BitBlt(DesktopBitmap.Canvas.Handle,
0,0,Screen.Width,Screen.Height,
GetDC(GetDesktopWindow),0,0,SrcCopy);
Shuffler.Image1.Picture.Bitmap := DesktopBitmap;
//изначальные координаты redRect
Randomize;
gx := Trunc(Random * DELTA);
gy := Trunc(Random * DELTA);
Shuffler.Image1.Canvas.CopyRect(
Rect(rW * gx, rH * gy, rW * gx + rW, rH * gy + rH),
redRect.Canvas,
Rect(0,0,rW,rH));
//рисуем сетку
for i:=0 to DELTA-1 do
begin
Shuffler.Image1.Canvas.MoveTo(rW * i,0);
Shuffler.Image1.Canvas.LineTo(rW * i,Screen.Height);
Shuffler.Image1.Canvas.MoveTo(0, rH * i);
Shuffler.Image1.Canvas.LineTo(Screen.Width, rH * i);
end;
end;
|
Draw Screen
Основной код находится в процедуре DrawScreen. Эта процедура вызывается внутри события OnTimer компонента Timer.
procedure DrawScreen;
var
r1, r2: TRect;
Direction: integer;
begin
r1:=Rect(rW * gx , rH * gy, rW * gx + rW , rH * gy + rH);
Direction := Trunc(Random*4);
case Direction of
0: gx := Abs((gx + 1) mod DELTA); //право
1: gx := Abs((gx - 1) mod DELTA); //лево
2: gy := Abs((gy + 1) mod DELTA); //низ
3: gy := Abs((gy - 1) mod DELTA); //верх
end; //case
r2 := Rect(rW * gx , rH * gy, rW * gx + rW , rH * gy + rH);
with Shuffler.Image1.Canvas do
begin
CopyRect(r1, Shuffler.Image1.Canvas, r2);
CopyRect(r2, redRect.Canvas, redRect.Canvas.ClipRect);
end;
end;
|
Несмотря на кажущуюся сложность кода, он очень прост в использовании. Менять местами можно только части смежные с redRect, поэтому доступны только 4 возможных направления. Прямоугольник r1 содержит текущию позицию redRect, r2 указывает на прямоугольник с блоком, который был перемещён. CopyRect используется для перемещения выбранного блока на место redRect и рисования redRect его в новом месте - таким образом осуществляется обмен этих двух блоков.
Было бы приятней наблюдать анимированный обмен блоков, но я оставлю эту задачу для самостоятельного решения.
Не забудьте по событию OnTimer для компонента Timer написать:
DrawScreen; |
Перетасовка строк в списке.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Перетасовка строк в списке
Процедура тасует строки в списке.
List - Список строк
MoveCount - необязательный параметр, задающий количество перестановок,
если этот параметр опущен, либо меньше или равен нулю, его значение
принимается равным C*C-C, где C - количество строк в списке.
Зависимости: SysUtils, Classes
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 18 ноября 2002 г.
***************************************************** }
procedure ShuffleStrings(List: TStrings; MoveCount: Integer = 0);
var
i, N, C: Integer;
Index1, Index2: Integer;
Str: string;
Obj: TObject;
begin
List.BeginUpdate;
try
C := List.Count;
N := MoveCount;
if N <= 0 then
N := C * C - C;
for i := 0 to N do
begin
Index1 := Random(C);
Index2 := Random(C);
Str := List.Strings[Index1];
Obj := List.Objects[Index1];
List.Strings[Index1] := List.Strings[Index2];
List.Objects[Index1] := List.Objects[Index2];
List.Strings[Index2] := Str;
List.Objects[Index2] := Obj;
end;
finally
List.EndUpdate;
end;
end;
Разделяемые элементы управления для Tabbednotebook.
Вы могли бы получить тот же эффект, устанавливая элементы управления на панели или в другом контейнере, который не является дочерним по отношению к Notebook, но находится наверху. Поскольку элементы не являются дочерними по отношению к страницам, то их никак не затронет изменение страниц, и вы можете управлять ими из кода. Видимость же такова, что кажется, что каждая страница компонента содержит тот же самый набор компонентов. Что делаю я -- располагаю разделяемые элементы управления на форме, а не на конкретной странице TTabbedNotebook. Для того, чтобы сделать это, вам необходимо установить свойство TTabbedNotebook Align на что-то другое, отличное от alClient -- например, если разделяемые элементы управления расположены в нижней части формы, используйте для TTabbedNotebook значение alTop, и отодвиньте нижнюю границу чуть повыше, чтобы в образовавшейся области без труда разместились бы разделяемые элементы управления. Если вы хотите сделать так, чтобы "разделяемые" элементы управления имитировали свое нахождение на каждой странице TTabbedNotebook, оставьте их на форме, но поместите их на передний план, и передвиньте TTabbedNotebook снова в нижнюю часть формы.У меня это работает. Я еще не испытывал нужду в разделяемых элементах управления для _набора_ страниц, но первая идея, которая приходит мне в голову, заключается в установке свойства Visible для каждого компонента в true/false в обработчике события TTabbedNotebook OnChange.
Разделяемые элементы управления для Tabbednotebook 2.
Вероятно, самым лучшим решением будет размещение разделяемых компонентов непосредственно на форме, и дальнейшее их перемещение в коде на TabbedNotebook, тем самым делая их видимыми на нескольких страницах компонента.Для любых закладок, где разделяемые компоненты НЕ должны появляться, установите их свойство Visible в False, сделать это лучше всего в обработчике события TabbedNotebook OnChange.
Разделяемые элементы управления для Tabbednotebook 3.
Компоненты, которые вы хотите показывать на всех страницах, должны принадлежать родителю TTabbedNotebook (в общем случае TForm, на которой расположен компонент), и, очевидно, они должны быть размещены ПОЗАДИ TTabbedNotebook. Самым простым способом сделать это, как я выяснил, является следующий способ: разместите на форме TTabbedNotebook, но оставьте возможность задать ему родителя (например, пока не устанавливайте ему свойство .Align), затем разместите кнопки (или что там у вас) в области родителя, затем установите свойство .Align у TTabbedNotebook, после чего элементы управления, которые вы разместили, окажутся позади TTabbedNotebook, и будут видны на всех его страницах (действительно, они будут располагаться "наверху" TTabbedNotebook. Если вы уже разместили компоненты, то лучшим решением будет использование для TTabbedNotebook команды меню "Edit/Send to Back", при котором нижние компоненты "всплывут" наверх. Также, возможно непосредственно отредактировать файл .DFM и убедиться в том, что родительский порядок компонентов собледен в полной мере.
Защита программ перекрытием кода.
| Автор: Den is Com |
| Хакеры взломали 3-х минутную демо-версию Виагры. Теперь она trial-версия на 70 дней. |
Мой любимый метод для наколки - это перекрывающийся код. Он может показаться немного сложным для большинства из нас, но зная нескольо HEX-значений инструкций процессора,вы тоже сможете сделать небольшой по размеру перекрывающийся код. Перекрывающийся код можно сделать сколь угодно многоуровневым, просто здесь я покажу только в каком направлении надо "копать".
temp_string :='Den is Com';
asm
mov ax,$05EB
@as: jmp @as-2
end;
ShowMessage('Сообщение');
|
Присваивание temp_string :='Den is Com' существенной роли не играет :), но может использоваться при отладке программы - хорошо присматривается при использовании дизассемблера. Скорее всего ваши первые шаги будут приводить к частому зависанию компьютера, но не отчаивайтесь - это того стоит. Попробуйте разработать свой способ сравнения строк (чаше всего ловятся именно эти инструкции), попробуйте замаскировать инструкции зависания компьютера и т.д.
Ошибка совместного доступа к базе данных.
Автор: Vit WEB-сайт: http://forum.vingrad.ru Подскажите как на дельфи создаются базы данных с которыми можно работать на нескольких компьютерах одновременно (таблицы Paradox). Как буфиризировать таблицы Paradox? Использую компонент DataBase со стандарным драйвером, указываю сетевой путь к БД, но при обращении сразу нескольких пользователей возникает сообщение что каталог заблокирован другим .NET файлом. Решение: Control Panel->BDE Administrator->закладка Configuration->Drivers->Native->ParadoxСделайте где нибудь в сетке каталог доступный со всех компьютеров, Опция Net Dir должна указывать на всех компьютерах должна указывать на этот каталог - в нём будут хранится файлы которые обеспечивают раздельный доступ.
Как расшарить диск.
| Автор: Repairman |
| Программист, глядя на только что отформатированный вирусом винчестер: "Хмм... кажется здесь кто-то поработал зубной щеткой Reach Interdental от Johnson&Johnson..." |
Это модуль для Share любого диска или папки как на локальном, так и на удаленном компьютере (если, конечно у Вас права администратора и на компе разрешено удаленное администрирование, для локальной машины это не обязательно... ;-)).
Следует отметить, что под NT некоторые процедуры находятся в других DLL...
Функция SetShareOnDisk - ставит шару, RemoveShareFromDisk - снимает ее.
//(c)2002 repairman@uzel.ru
unit Share;
interface
type
TPassw = string[8];
TNetName = string[12];
function SetShareOnDisk(HostName: string; { имя компьютера }
LocalPath: string; { папка которую надо открыть для доступа }
NetName: TNetName; { имя расшаренной папки в сети }
Remark: string; { комментарий, видимый в сети }
Access: word; { доступ }
RO_Passw: TPassw; { пароль на чтение }
RW_Passw: TPassw { пароль на полный доступ }
): boolean;
function RemoveShareFromDisk(HostName: string; { имя компьютера }
NetName: string; { сетевое имя папки которую надо закрыть }
LocalPath: string { локальный путь папки }
): boolean;
var
ShareResult: word;
implementation
uses
SysUtils, Windows, ShlObj;
{ указатель на имя компьютера,
например '\\Server'#0, если свой, то можно nil }
function NetShareAdd(ServerName: PChar;
Level: Word; { уровень структуры Share_info, здесь 50 }
PShareInfo: PChar; { указатель на структуру Share_Info }
ParmErr: DWord) { указатель на ??? }
{ svrapi для Win9X, NetApi32 для NT }
: dword; stdcall; external 'svrapi.dll';
function NetShareDel(ServerName: PChar; NetName: PChar;
Reserved: DWord): dword; stdcall; external 'svrapi.dll';
type
_share_info_50 = record { структура Share уровня 50 }
NetName: array[1..13] of char; { Как будет называться диск в сети }
SType: byte; { тип =0 (STYPE_DISKTREE) - шарить диски }
Flags: word; { флаги $0191,$0192,$0193....(доступ из сети) }
Remark: PChar; { указатель на комментарий, видимый из сети }
Path: PChar; { указатель на имя ресурса, например 'c:\'#0 }
{ пароль для полного доступа, если не нужен =#0 }
RW_Password: array [1..9] of char;
{ пароль для доступа на чтение, если не нужен =#0 }
RO_Password: array [1..9] of char;
end;
function SetShareOnDisk(HostName, LocalPath: string; NetName: TNetName;
Remark: string; Access: word; RO_Passw, RW_Passw: TPassw): boolean;
var
ShareInfo: _Share_Info_50;
begin
Result:=false;
StrPCopy(@ShareInfo.NetName, NetName);
ShareInfo.SType:=0;
ShareInfo.Flags:=Access;
ShareInfo.Remark:=PChar(Remark);
ShareInfo.Path:=PChar(LocalPath);
StrPCopy(@ShareInfo.RO_Password, RO_Passw);
StrPCopy(@ShareInfo.RW_Password, RW_Passw);
{ вызываем Share }
ShareResult:=NetShareAdd(PChar(HostName), 50,@ShareInfo,$0000002a);
if ShareResult<>0 then { расшарить неудалось }
Exit;
{ сказать шеллу об изменениях }
SHChangeNotify(SHCNE_NETSHARE, SHCNF_PATH, PChar(LocalPath), nil);
Result:=true;
end;
function RemoveShareFromDisk(HostName, NetName, LocalPath: string): boolean;
begin
Result:=false;
{ удалить шару }
ShareResult:=NetShareDel(PChar(HostName), PChar(NetName), 0);
if ShareResult<>0 then
Exit;
{ сказать шеллу об изменениях }
SHChangeNotify(SHCNE_NETUNSHARE, SHCNF_PATH,PChar(LocalPath),nil);
Result:=true;
end;
end.
|
Расшаренная DLL.
Автор: Xavier Pacheco
{
Copyright © 1999 by Delphi 5 Developer's Guide - Xavier Pacheco and Steve Teixeira
}
library ShareLib;
uses
ShareMem,
Windows,
SysUtils,
Classes;
const
cMMFileName: PChar = 'SharedMapData';
{$I DLLDATA.INC}
var
GlobalData: PGlobalDLLData;
MapHandle: THandle;
{ GetDLLData will be the exported DLL function }
procedure GetDLLData(var AGlobalData: PGlobalDLLData); stdcall;
begin
{ Point AGlobalData to the same memory address referred to by GlobalData. }
AGlobalData := GlobalData;
end;
procedure OpenSharedData;
var
Size: Integer;
begin
{ Get the size of the data to be mapped. }
Size := SizeOf(TGlobalDLLData);
{ Now get a memory-mapped file object. Note the first parameter passes
the value $FFFFFFFF or DWord(-1) so that space is allocated from the system's
paging file. This requires that a name for the memory-mapped
object get passed as the last parameter. }
MapHandle := CreateFileMapping(DWord(-1), nil, PAGE_READWRITE, 0, Size,
cMMFileName);
if MapHandle = 0 then
RaiseLastWin32Error;
{ Now map the data to the calling process's address space and get a
pointer to the beginning of this address }
GlobalData := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
{ Initialize this data }
GlobalData^.S := 'ShareLib';
GlobalData^.I := 1;
if GlobalData = nil then
begin
CloseHandle(MapHandle);
RaiseLastWin32Error;
end;
end;
procedure CloseSharedData;
{ This procedure un-maps the memory-mapped file and releases the memory-mapped
file handle }
begin
UnmapViewOfFile(GlobalData);
CloseHandle(MapHandle);
end;
procedure DLLEntryPoint(dwReason: DWord);
begin
case dwReason of
DLL_PROCESS_ATTACH: OpenSharedData;
DLL_PROCESS_DETACH: CloseSharedData;
end;
end;
exports
GetDLLData;
begin
{ First, assign the procedure to the DLLProc variable }
DllProc := @DLLEntryPoint;
{ Now invoke the procedure to reflect that the DLL is attaching
to the process }
DLLEntryPoint(DLL_PROCESS_ATTACH);
end.
Скачать весь проект
Общий доступ к памяти, распределенной DLL.
Общий доступ к области отображения файлов (Sharing Memory Mapped Files)... Проверьте нижеследующий код:
var
HMapping: THandle;
PMapData: Pointer;
const
MAPFILESIZE = 1000;
procedure OpenMap;
var
llInit: Boolean;
lInt: Integer;
begin
HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, MAPFILESIZE,
pchar('ИМЯ ОБЛАСТИ ОТОБРАЖЕНИЯ'));
// Проверка наличия
llInit := (GetLastError() <> ERROR_ALREADY_EXISTS);
if (hMapping = 0) then
begin
ShowMessage('Невозможно создать объект отображения файла');
Application.Terminate;
exit;
end;
PMapData := MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if PMapData = nil then
begin
CloseHandle(HMapping);
ShowMessage('Невозможно отобразить блок памяти');
Application.Terminate;
exit;
end;
if (llInit) then
begin
// Если объект отображения создан, инициализируем блок символами #0
memset(PMapData, #0, MAPFILESIZE);
end;
end;
procedure CloseMap;
begin
if PMapData <> nil then
begin
UnMapViewOfFile(PMapData);
end;
if HMapping <> 0 then
begin
CloseHandle(HMapping);
end;
end;
{ Таким образом любые два или более приложения или DLL могут получить указатели к
одному и тому же блоку памяти.В данном примере PMapData указывает на 1000 -
байтный буфер, инициализированный вначале символами #0.Однако существует одна
потенциальная проблема - синхронизация доступа к памяти.Решить эту проблему можно
с помощью мьютексов.Вот пример их использования:
Вызовите LockMap перед записью (и чтением?) объекта отображения файла.
Не забывайте после каждого обновления немедленно вызывать UnlockMap. }
var
HMapMutex: THandle;
const
REQUEST_TIMEOUT = 1000;
function LockMap: Boolean;
begin
Result := true;
HMapMutex := CreateMutex(nil, false, pchar('ИМЯ ВАШЕГО МЬЮТЕКСА'));
if HMixMutex = 0 then
begin
ShowMessage('Не могу создать мьютекс');
Result := false;
end
else
begin
if WaitForSingleObject(HMapMutex, REQUEST_TIMEOUT) = WAIT_FAILED then
begin
// время ожидания
ShowMessage('Невозможно заблокировать объект отображения файла');
Result := false;
end;
end;
end;
procedure UnlockMap;
begin
ReleaseMutex(HMixMutex);
CloseHandle(HMixMutex);
end;
|
Как использовать Shell API SHBrowseForFolder.
Как использовать функцию Shell API SHBrowseForFolder чтобы позволить пользователю выбрать каталог?
uses ShellAPI, ShlObj;
procedure TForm1.Button1Click(Sender: TObject);
var
TitleName: string;
lpItemID: PItemIDList;
BrowseInfo: TBrowseInfo;
DisplayName: array[0..MAX_PATH] of char;
TempPath: array[0..MAX_PATH] of char;
begin
FillChar(BrowseInfo, sizeof(TBrowseInfo), #0);
BrowseInfo.hwndOwner := Form1.Handle;
BrowseInfo.pszDisplayName := @DisplayName;
TitleName := 'Please specify a directory';
BrowseInfo.lpszTitle := PChar(TitleName);
BrowseInfo.ulFlags := BIF_RETURNONLYFSDIRS;
lpItemID := SHBrowseForFolder(BrowseInfo);
if lpItemId <> nil then
begin
SHGetPathFromIDList(lpItemID, TempPath);
ShowMessage(TempPath);
GlobalFreePtr(lpItemID);
end;
end;
Как использовать Shell API SHBrowseForFolder 2.
Автор: Анатолий.
threadvar
myDir: string;
function BrowseCallbackProc(hwnd: HWND; uMsg: UINT; lParam: LPARAM; lpData:
LPARAM): integer; stdcall;
begin
Result := 0;
if uMsg = BFFM_INITIALIZED then
SendMessage(hwnd, BFFM_SETSELECTION, 1, LongInt(PChar(myDir)))
end;
function SelectDirectory(const Caption: string; const Root: WideString;
var Directory: string): Boolean;
var
WindowList: Pointer;
BrowseInf!
o: TBrowseInfo;
Buffer: PChar;
RootItemIDList, ItemIDList: PItemIDList;
ShellMalloc: IMalloc;
IDesktopFolder: IShellFolder;
Eaten, Flags: LongWord;
begin
myDir := Directory;
Result := False;
FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
begin
Buffer := ShellMalloc.Alloc(MAX_PATH);
try
RootItemIDList := nil;
if Root <> '' then
begin
SHGetDesktopFolder(IDesktopFolder);
IDesktopFolder.ParseDisplayName(Application.Handle, nil,
POleStr(Root), Eaten, RootItemIDList, Flags);
end;
with BrowseInfo do
begin
hwndOwner := Application.Handle;
pidlRoot := RootItemIDList;
pszDisplayName := Buffer;
lpfn := @BrowseCallbackProc;
lParam := Integer(PChar(Directory));
lpszTitle := PChar(Caption);
ulFlags := BIF_RETURNONLYFSDIRS or $0040 or BIF_EDITBOX or
BIF_STATUSTEXT;
end;
WindowList := DisableTaskWindows(0);
try
ItemIDList := ShBrowseForFolder(BrowseInfo);
finally
EnableTaskWindows(WindowList);
end;
Result := ItemIDList <> nil;
if Result then
begin
ShGetPathFromIDList(ItemIDList!
, Buffer);
ShellMalloc.Free(ItemIDList);
Directory := Buffer;
end;
finally
ShellMalloc.Free(Buffer);
end;
end;
end;
Запуск внешней программы. Как послать E-mail и сделать ссылку на сайт.
Для запуска внешней программы, для посылки письма или для создания ссылки на сайт вам понадобиться всего одна функция ShellExecute, которая описывается в модуле ShellAPI - не забудьте подключить его в uses.
Этой функции нужно указать несколько параметров:
Дескриптор родительского окна Выполняемое действие. Этот параметр может принимать следующие значения "open", "print", "explore" - соответственно открытие, печать или исследование. Можно указывать nil - тогда будет выполняться действие по умолчанию - "open". Имя файла или папки, или e-mail, или URL Параметры Каталог по умолчанию Способ вывода окна. В качестве значения можно указать: SW_HIDE Скрывает окно и активизирует другое. SW_MAXIMIZE Разворачивает окно. SW_MINIMIZE Сворачивает окно. SW_RESTORE Активизирует и выводит окно. Если окно было развёрнуто или свёрнуто - восстанавливает исходный размер и позицию. SW_SHOW Активизирует и выводит окно с его оригинальным размером и положением. SW_SHOWDEFAULT Активизирует с установками, заданными в структуре STARTUPINFO, которая была передана при создании процесса приложением запускающим нужную программу. SW_SHOWMAXIMIZED Выводит окно в развёрнутом виде. SW_SHOWMINIMIZED Выводит окно в виде пиктограммы на панели задач. SW_SHOWMINNOACTIVE Выводит окно в свёрнутом виде на панели задач и не передаёт ему фокус ввода, т.е. окно, которое до этого было активно остаётся активно по прежнему. SW_SHOWNA Отображает окно в его текущем состоянии. Активное окно остаётся активным по прежнему. SW_SHOWNOACTIVATE Выводит окно в его последнем положении и с последними используемыми размерами. Активное окно остаётся активным по прежнему. SW_SHOWNORMAL Выводит окно. Если оно было свёрнуто или развёрнуто - восстанавливает его оригинальные размеры и позициюПримеры:
// Запуск файла ShellExecute(Handle, nil, 'c:\windows\calc.exe', nil, nil, SW_SHOW); // Просмотр каталога ShellExecute(Handle, nil, 'c:\windows', nil, nil, SW_SHOW); // Ссылка на сайт ShellExecute(Handle, nil, 'http://www.site.ru', nil, nil, SW_SHOW); // Послать E-mail ShellExecute(Handle, nil, 'mailto:DelphiWorld@mail.ru', nil, nil, SW_SHOW); |
Сортировка Шелла.
Сортировка Шелла. Это еще одна модификация пузырьковой сор- тировки. Суть ее состоит в том, что здесь выполняется сравнение ключей, отстоящих один от другого на некотором расстоянии d. Ис- ходный размер d обычно выбирается соизмеримым с половиной общего размера сортируемой последовательности. Выполняется пузырьковая сортировка с интервалом сравнения d. Затем величина d уменьшается вдвое и вновь выполняется пузырьковая сортировка, далее d умень- шается еще вдвое и т.д. Последняя пузырьковая сортировка выполня- ется при d=1. Качественный порядок сортировки Шелла остается O(N^2), среднее же число сравнений, определенное эмпирическим пу- тем - log2(N)^2*N. Ускорение достигается за счет того, что выяв- ленные "не на месте" элементы при d>1, быстрее "всплывают" на свои места.Пример иллюстрирует сортировку Шелла.
{===== Программный пример =====}
{ Сортировка Шелла }
Procedure Sort( var a : seq);
Var d, i, t : integer;
k : boolean; { признак перестановки }
begin
d:=N div 2; { начальное значение интервала }
while d>0 do begin { цикл с уменьшением интервала до 1 }
{ пузырьковая сортировка с интервалом d }
k:=true;
while k do begin { цикл, пока есть перестановки }
k:=false; i:=1;
for i:=1 to N-d do begin
{ сравнение эл-тов на интервале d }
if a[i]>a[i+d] then begin
t:=a[i]; a[i]:=a[i+d]; a[i+d]:=t; { перестановка }
k:=true; { признак перестановки }
end; { if ... }
end; { for ... }
end; { while k }
d:=d div 2; { уменьшение интервала }
end; { while d>0 }
end;
|
Результаты трассировки программного примера 3.9 представлены в таблице.
----------T---T-------------------------------------------------¬ ¦ шаг ¦ d ¦ содержимое массива a ¦ +---------+---+-------------------------------------------------+ ¦исходный ¦ ¦ 76 22 4 17 13 49 4 18 32 40 96 57 77 20 1 52 ¦ ¦ 1 ¦ 8 ¦ 32 22 4 17 13 20 1 18 76 40 96 57 77 49 4 52 ¦ ¦ 2 ¦ 8 ¦ 32 22 4 17 13 20 1 18 76 40 96 57 77 49 4 52 ¦ ¦ 3 ¦ 4 ¦ 13 20 1 17 32 22 4 18 76 40 4 52 77 49 96 57 ¦ ¦ 4 ¦ 4 ¦ 13 20 1 17 32 22 4 18 76 40 4 52 77 49 96 57 ¦ ¦ 5 ¦ 2 ¦ 1 17 13 20 4 18 32 22 4 40 76 49 77 52 96 57 ¦ ¦ 6 ¦ 2 ¦ 1 17 4 18 13 20 4 22 32 40 76 49 77 52 96 57 ¦ ¦ 7 ¦ 2 ¦ 1 17 4 18 4 20 13 22 32 40 76 49 77 52 96 57 ¦ ¦ 8 ¦ 2 ¦ 1 17 4 18 4 20 13 22 32 40 76 49 77 52 96 57 ¦ ¦ 9 ¦ 1 ¦ 1 4 17 4 18 13 20 22 32 40 49 76 52 77 57 96 ¦ ¦ 10 ¦ 1 ¦ 1 4 4 17 13 18 20 22 32 40 49 52 76 57 77 96 ¦ ¦ 11 ¦ 1 ¦ 1 4 4 13 17 18 20 22 32 40 49 52 57 76 77 96 ¦ ¦ 12 ¦ 1 ¦ 1 4 4 13 17 18 20 22 32 40 49 52 57 76 77 96 ¦ ¦результат¦ ¦ 1 4 4 13 17 18 20 22 32 40 49 52 57 76 77 96 ¦ L---------+---+--------------------------------------------------
Shell своими руками - System Tray.
Автор: Богдан Минич Иконку в трей помещают с помощью Shell_NotifyIconW. Интересено посмотреть на этот процесс с другой точки зрения. Цитата с сайта delphi.mastak.ru: Shell_NotifyIconW просто ищет окно с классом "Shell_TrayWnd" и посылает в него сообщение WM_COPYDATA. в качестве данных выступает простая структура TNIDMessage. возвращаясь к топику: если создать свое окно с классом "Shell_TrayWnd" и обрабатывать входящие сообщения WM_COPYDATA, то можно написать полный аналог system tray! ... (с) paul_shmakov ...чем и займемся. В первую очередь немаловажное замечание: сообщение посылается только одному окну, то есть наше приложение должно грузится первым. Разные там ехрlоrеr'ы и другие подобные будут мешать. Шаг первый:Создаем окно "Shell_TrayWnd".
procedure TForm1.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); Params.WinClassName := 'Shell_TrayWnd'; end; |
Ловим WM_COPYDATA.
procedure TFORM1.WMCOPYDATA(var Msg: Tmessage);
var
pcd: PCopyDataStruct;
NID: PNotifyIconData;
begin
pcd := PCOPYDATASTRUCT(msg.lParam);
if pcd^.dwData = 1 then
begin
NID := pointer(integer(pcd.lpData) + 8);
case integer(pointer(integer(pcd.lpData) + 4)^) of
NIM_ADD: Msg.Result := NewTrayIcon(NID); // добавить иконку
NIM_DELETE: Msg.Result := DeleteTrayIcon(NID); // удалить иконку
NIM_MODIFY: Msg.Result := ModifyTrayIcon(NID);
// изменить иконку (или подсказку)
end;
exit;
end;
end;
|
Обратите внимание на Msg.Result. Желательно чтобы NewTrayIcon, DeleteTrayIcon, ModifyTrayIcon возвращали Integer(True) или Integer(False) в зависимости от помещения/удаления иконки. Некоторые приложения не проверяют этот результат, но если начнут проверять - то причины "глючного" поведения иконки того же AVP Monitor можно искать долго и безуспешно.
Шаг третий:Поймали, и че с ним теперь делать?
А мы имеем очень интересную структуру - NID.cbSize - размер записи, в принципе не интересен; NID.Wnd - хендл окна (владельца иконки); NID.uID - идентификатор иконки (если их в приложении несколько), для данной задачи нужен для отсылки обратного сообщения; NID.uFlags - определяет, какие поля используются в сообщении. Параметр может быть любой комбинацией из флагов (0 - uCallbackMessage, 2 - hIcon, 4 - czTip); NID.uCallbackMessage - номер сообщения, которое посылается окну, определяемому полем NID.Wnd (владельцу). lParam отсылаемого сообщения дожен равняться NID.uID, а wParam сообщение от мыши. Пример: PostMessage(NID.Wnd, NID.uCallBack, NID.uID, MOUSE_EVENT) где MOUSE_EVENT может принимать значения WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK и подобные для других кнопок мыши. NID.hIcon - хендл иконки, которую собственно и предполагается отображать; NID.szTip - строка, оканчивающаяся нулевым символом, содержит подсказку, которая должна выводится при наведении курсора на иконку.В случаях ошибки нужно информировать приложения про необходимость поместить иконки обратно. Для этого послужат такие действия:
procedure TForm1.FormCreate(Sender: TObject);
var
WM_TASKBARCREATED: UINT;
begin
WM_TASKBARCREATED := RegisterWindowMessage('TaskbarCreated');
PostMessage(HWND_BROADCAST, WM_TASKBARCREATED, 0, 0);
end;
|
Найти строчку:
shell=explorer.exe |
Заменить на (предполагается что демопроект находится в C:\Demotray\ ) :
;shell=explorer.exe shell=c:\demotray\demotray.exe |
Второй вариант: Лично я использую Far для выгрузки Explorer.exe.
1) Загружаем IDE Delphi и демопроект 2) Загружаем Far, F11->Process list-> выбираем EXPLORER.EXE->F8->OK 3) Отлаживаем проект 4) Для появления Explorer'a просто запустите его.Используйте эти инструкции на свой страх и риск. Прочитайте их дважды. Внимательно изучите исходники. Трижды.
Инструкции по закрытию EXPLORER.EXE действительны для Win9x.Если у Вас NT - разберитесь сами. Если не сможете разобратся - то за такие проекты Вам браться рановато.
Гревные ругательства "А у меня после ... ничего не работает!" не принимаются. Благодарности: Paul Shmakov - реверсинг Shell_NotifyIcon, моральная поддержка. Стив Тейксейра & Ксавье Пачеко - литература. Особая благодарность обоим использованым алфавитам.Клавиши-акселераторы для TPageControl.
| Приходит программист на работу с красными глазами, злой, не в настроении. У него же и спрашивают: - Слышь, Вась, что ты такой невеселый? - Да я тут всю ночь программу писал. - И что не работает? - Работает. - Может с глюками какими? - Нет, без. - Так что ты злой такой? - Да я, бл@, на клавише Backspace заснул. |
В приведенном ниже коде показана реализация такого компонента. Наследник TPageControl осуществляет захват и обработку сообщения CM_DIALOGCHAR. Это позволяет перехватывать комбинации клавиш, которые могут быть акселератороми для данной формы. Обработчик со бытия CMDialogChar использует функцию IsAccel, которая позволяет определить, имеет ли отношение перехваченный код клавиш к акселератору одной из страниц TPageControl. В этом случае делаем страницу активной и передаем ей фокус.
unit tapage;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, ComCtrls;
type
TAPageControl = class(TPageControl)
private
procedure CMDialogChar(var Msg: TCMDialogChar); message CM_DIALOGCHAR;
end;
procedure Register;
implementation
procedure TAPageControl.CMDialogChar(var Msg: TCMDialogChar);
var
i: Integer;
S: string;
begin
if Enabled then
for I := 0 to PageCount - 1 do
if IsAccel(Msg.CharCode, Pages[i].Caption) and
Pages[I].TabVisible then
begin
Msg.Result := 1;
ActivePage := Pages[I];
Change;
Exit; // выход из цикла.
end;
inherited;
end;
procedure Register;
begin
RegisterComponents('Test', [TAPageControl]);
end;
end.
|
Как сделать клавишу-акселератор (keyboard shortcut) компонету, у которого нет заголовка.
| ...Ну! И кто открывал бутылку о кнопку "F8"?! |
Возможный вариант - присвоить ссылку на этот компонент свойству FocusControl ТLаbеl'а. В примере используется невидимый Label для создания "быстрой" клавиши (Alt+M) компонента Memo. Чтобы использовать пример, разместите на форме компонент TMemo, Label и несколько других компонентов, которые могут принимать фокус ввода. Запустите программу, переведите фокус ввода куда-нибудь вне Memo и нажмите Alt+M - фокус ввода вернется в Memo.
procedure TForm1.FormCreate(Sender: TObject); begin Label1.Visible := false; Label1.Caption := '&M'; Label1.FocusControl := Memo1; end; |
Чтобы клавиши shortcut пунктов главного меню главной формы действовали только в этой форме (но не в модальных окнах).
Знакомая проблема. Лечится так:
function WindowHook(var Message: TMessage): Boolean;
...
procedure.FormCreate(Sender: TObject);
begin
// MainForm
Application.HookMainWindow(WindowHook);
end;
function.WindowHook;
begin
Result := False;
with Message do
case Msg of
CM_APPKEYDOWN, CM_APPSYSCOMMAND: Msg := WM_NULL;
end;
|
Сокращенное имя каталога.
Есть имя каталога: c:\windows\media\temp\abc\sound\chime.wav Мне необходима сокращенная запись, такая как c:\windows\..\sound\chime.wav а не полный путь к файлу. Есть ли простой способ для этого?Я разработал процедуру, которая делает нечто похожее. Она сокращает имя каталога, когда он и текущий путь имеют в корне тот же диск и/или часть пути, совпадающие вначале. Полученный результат (сокращенная запись) действительно легче для восприятия. Я производил кодирование в шестнадцатиричном редакторе в Borland Pascal и некоторое время не использовал. Я не думаю, что данный код может быть несовместим с последними версиями Delphi.
function shortenfilename(s: string): string;
var
drive, curdrive: string[2];
dir, curdir: string[80];
name: string[20];
ext: string[5];
i: byte;
begin
for i := 1 to length(s) do
s[i] := upcase(s[i]);
s := fexpand(s);
fsplit(s, dir, name, ext);
drive := copy(dir, 1, 2);
dir := copy(dir, 4, length(dir) - 3);
getdir(0, curdir);
curdrive := copy(curdir, 1, 2);
curdir := copy(curdir, 4, length(curdir) - 3) + '\';
if drive = curdrive then
begin
if copy(dir, 1, length(curdir)) = curdir then
begin
i := length(curdir);
if length(dir) <> i then
dir := dir + '\';
shortenfilename := copy(dir, i + 1, length(dir) - i - 1) + name + ext;
end
else
shortenfilename := copy(s, 3, length(s) - 2);
end
else
shortenfilename := s;
end;
|
Как использовать функцию ShowMessageFmt.
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessageFmt('This is %s.'#13'Handle: %.8x '#13'WindowProc: %p',
[Caption, Handle, @WindowProc]);
end;
|
%s — вместо нее подставляется первый параметр из [], приведенный к типу String %.8x — подставляется целое, переведенное в 16-ричную систему, дополненное слева нулями до 8-ми цифр %p — подставляется указатель.
Отобразить, присоединить сетевые диски.
| Вовочка прибегает домой 1-го сентября после уроков, подходит к папе-фидошнику и говорит: - Пап, мы сегодня писали сочинение на тему "Как я провёл лето", а мне влепили тройбан. Сам помотри - за что? Батя отрывается от монитора, долго всматривается в сочинение и отвечает: - А ты что хотел?! Оффтопик, оверквотинг... |
function ConnectDrive(_drvLetter: string; _netPath: string; _showError: Boolean;
_reconnect: Boolean): DWORD;
var
nRes: TNetResource;
errCode: DWORD;
dwFlags: DWORD;
begin
{ Fill NetRessource with #0 to provide uninitialized values }
{ NetRessource mit #0 fullen => Keine unitialisierte Werte }
FillChar(NRes, SizeOf(NRes), #0);
nRes.dwType := RESOURCETYPE_DISK;
{ Set Driveletter and Networkpath }
{ Laufwerkbuchstabe und Netzwerkpfad setzen }
nRes.lpLocalName := PChar(_drvLetter);
nRes.lpRemoteName := PChar(_netPath); { Example: \\Test\C }
{ Check if it should be saved for use after restart and set flags }
{ Uberprufung, ob gespeichert werden soll }
if _reconnect then
dwFlags := CONNECT_UPDATE_PROFILE and CONNECT_INTERACTIVE
else
dwFlags := CONNECT_INTERACTIVE;
errCode := WNetAddConnection3(Form1.Handle, nRes, nil, nil, dwFlags);
{ Show Errormessage, if flag is set }
{ Fehlernachricht aneigen }
if (errCode <> NO_ERROR) and (_showError) then
begin
Application.MessageBox(PChar('An error occured while connecting:' + #13#10 +
SysErrorMessage(GetLastError)),
'Error while connecting!',
MB_OK);
end;
Result := errCode; { NO_ERROR }
end;
function ConnectPrinterDevice(_lptPort: string; _netPath: string; _showError: Boolean;
_reconnect: Boolean): DWORD;
var
nRes: TNetResource;
errCode: DWORD;
dwFlags: DWORD;
begin
{ Fill NetRessource with #0 to provide uninitialized values }
{ NetRessource mit #0 fullen => Keine unitialisierte Werte }
FillChar(NRes, SizeOf(NRes), #0);
nRes.dwType := RESOURCETYPE_PRINT;
{ Set Printername and Networkpath }
{ Druckername und Netzwerkpfad setzen }
nRes.lpLocalName := PChar(_lptPort);
nRes.lpRemoteName := PChar(_netPath); { Example: \\Test\Printer1 }
{ Check if it should be saved for use after restart and set flags }
{ Uberprufung, ob gespeichert werden soll }
if _reconnect then
dwFlags := CONNECT_UPDATE_PROFILE and CONNECT_INTERACTIVE
else
dwFlags := CONNECT_INTERACTIVE;
errCode := WNetAddConnection3(Form1.Handle, nRes, nil, nil, dwFlags);
{ Show Errormessage, if flag is set }
{ Fehlernachricht aneigen }
if (errCode <> NO_ERROR) and (_showError) then
begin
Application.MessageBox(PChar('An error occured while connecting:' + #13#10 +
SysErrorMessage(GetLastError)),
'Error while connecting!',
MB_OK);
end;
Result := errCode; { NO_ERROR }
end;
function DisconnectNetDrive(_locDrive: string; _showError: Boolean; _force: Boolean;
_save: Boolean): DWORD;
var
dwFlags: DWORD;
errCode: DWORD;
begin
{ Set dwFlags, if necessary }
{ Setze dwFlags auf gewunschten Wert }
if _save then
dwFlags := CONNECT_UPDATE_PROFILE
else
dwFlags := 0;
{ Cancel the connection see also at http://www.swissdelphicenter.ch/en/showcode.php?id=391 }
{ Siehe auch oben genannten Link (Netzlaufwerke anzeigen) }
errCode := WNetCancelConnection2(PChar(_locDrive), dwFlags, _force);
{ Show Errormessage, if flag is set }
{ Fehlernachricht anzeigen }
if (errCode <> NO_ERROR) and (_showError) then
begin
Application.MessageBox(PChar('An error occured while disconnecting:' + #13#10 +
SysErrorMessage(GetLastError)),
'Error while disconnecting',
MB_OK);
end;
Result := errCode; { NO_ERROR }
end;
{Beispiel / Example:}
procedure TForm1.Button1Click(Sender: TObject);
begin
ConnectDrive('h:', '\\Servername\C', True, True);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
DisconnectNetDrive('h:', True, True, True);
end;
|
Как вывести окно модальное для всех окон кроме одного.
Автор: Serge Buzadzhy
procedure ShowAlmostModal(FormModal:TForm); begin NavigatorForm.Enabled := false; FormModal.ShowModal end; |
И вот это привесь на OnShow почти модальной формы.
procedure FormShow(Sender:Tobject); begin NavigatorForm.Enabled := true; end; |
Считать двоичный файл и отобразить значение байт как ASCII.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TDisplayProc = procedure(const s: string) of object;
procedure ShowBinary(var Data; Count: Cardinal; DispProc: TDisplayProc);
implementation
procedure ShowBinary(var Data; Count: Cardinal; DispProc: TDisplayProc);
var
line: string[80];
i: Cardinal;
p: PChar;
nStr: string[4];
const
posStart = 1;
binStart = 7;
ascStart = 57;
HexChars: PChar = '0123456789ABCDEF';
begin
p := @Data;
line := '';
for i := 0 to Count - 1 do
begin
if (i mod 16) = 0 then
begin
if Length(line) > 0 then
DispProc(line);
FillChar(line, SizeOf(line), ' ');
line[0] := Chr(72);
nStr := Format('%4.4X', [i]);
Move(nStr[1], line[posStart], Length(nStr));
line[posStart + 4] := ':';
end;
if p[i] >= ' ' then
line[i mod 16 + ascStart] := p[i]
else
line[i mod 16 + ascStart] := '.';
line[binStart + 3 * (i mod 16)] := HexChars[(Ord(p[i]) shr 4) and $F];
line[binStart + 3 * (i mod 16) + 1] := HexChars[Ord(p[i]) and $F];
end;
DispProc(line);
end;
procedure TForm1.Display(const S: string);
begin
Memo1.Lines.Add(S);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
ms: TMemoryStream;
begin
if Opendialog1.Execute then
begin
ms := TMemoryStream.Create;
try
ms.LoadFromfile(OpenDialog1.FileName);
ShowBinary(ms.Memory^, ms.Size, Display);
finally
ms.Free
end;
end;
end;
Просмотр буфера обмена.
unit ClipboardViewer;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FNextViewerHandle: THandle;
procedure WMDrawClipboard(var message: TMessage);
message WM_DRAWCLIPBOARD;
procedure WMChangeCBCHain(var message: TMessage);
message WM_CHANGECBCHAIN;
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
// Проверяем работоспособность функции.
// При невозможности просмотра буфера обмена
// функция возвратит значение Nil.
FNextViewerHandle := SetClipboardViewer(Handle);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// Восстанавливаем цепочки.
ChangeClipboardChain(Handle, FNextViewerHandle);
end;
procedure TForm1.WMDrawClipboard(var message: TMessage);
begin
// Вызывается при любом изменении содержимого буфера обмена
message.Result := SendMessage(WM_DRAWCLIPBOARD, FNextViewerHandle, 0, 0);
end;
procedure TForm1.WMChangeCBCHain(var message: TMessage);
begin
// Вызывается при любом изменении цепочек буфера обмена.
if message.wParam = FNextViewerHandle then
begin
// Удаляем следующую цепочку просмотра. Корректируем внутреннюю переменную.
FNextViewerHandle := message.lParam;
// Возвращаем 0 чтобы указать, что сообщение было обработано
message.Result := 0;
end
else
begin
// Передаем сообщение следующему окну в цепочке.
message.Result := SendMessage(FNextViewerHandle, WM_CHANGECBCHAIN,
message.wParam, message.lParam);
end;
end;
end.
|
Просмотр буфера обмена 2.
Автор: NeilПросмотр буфера обмена в Delphi совсем не сложен. Вот участок кода программы, вешающий цепочки в буфере обмена и просто отображающий его текст. Расположите компонент Memo на главной форме нового проекта, присвойстве свойству Align значение alClient, добавьте необходимые private-поля и методы и создайте их реализацию следующим образом:
...
private
{ Private declarations }
PrevHwnd: Hwnd;
procedure WMChangeCBChain(var Msg: TWMChangeCBChain);
message WM_CHANGECBCHAIN;
procedure WMDrawClipboard(var Msg: TWMDrawClipboard);
message WM_DRAWCLIPBOARD;
...
procedure TForm1.WMChangeCBChain(var Msg: TWMChangeCBChain);
begin
if PrevHWnd = Msg.Remove then
PrevHWnd := Msg.Next;
if Msg.Remove <> Handle then
SendMessage(PrevHWnd, WM_CHANGECBCHAIN, Msg.Remove, Msg.Next);
end;
procedure TForm1.WMDrawClipboard(var Msg: TWMDrawClipboard);
var
P: PChar;
H: THandle;
begin
SendMessage(PrevHWnd, WM_DRAWCLIPBOARD, 0, 0);
if Clipboard.HasFormat(CF_TEXT) then
begin
H := Clipboard.GetAsHandle(CF_TEXT);
Len := GlobalSize(H) + 1;
P := GlobalLock(H);
Memo1.SetTextBuf(P);
GlobalUnlock(H);
end;
Msg.Result := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
PrevHwnd := SetClipboardViewer(Handle);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
ChangeClipboardChain(Handle, PrevHwnd);
end;
|
Обращаю ваше внимание на то, что у меня не было никакой конкретной идеи прежде, чем я это сделал; я просто внимательно прочел файлы помощи по SetClipboardViewer и во всех связанных темах.
Открытие сокращённого или полного диалога выбора цвета.
Автор: Igor Kovalevsky WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Открытие сокращённого или полного диалога выбора цвета
Вид диалога зависит от того, можно ли показать начальный цвет
(C : TColor) в сокращённом диалоге или нужно раскрывать его полностью.
Возвращает выбранный пользователем цвет.
Зависимости: Windows, Messages, SysUtils, Classes, DIALOGS;
Автор: Igor Kovalevsky, pc-ambulance@mail.ru, Владикавказ
Copyright: Igor Kovalevsky
Дата: 1 июня 2002 г.
***************************************************** }
function SelectColor(C: TColor): TColor;
const
BasicColors = [$00, $40, $80, $A0, $C0, $FF];
begin
with TColorDialog.Create(Application) do
begin
Color := C;
if (GetRValue(Color) in BasicColors) and
(GetGValue(Color) in BasicColors) and
(GetBValue(Color) in BasicColors) then
begin
Options := Options - [cdFullOpen];
end
else
begin
Options := Options + [cdFullOpen];
end;
if Execute then
begin
Result := Color
end
else
begin
Result := clNone;
end;
Free;
end;
end;
Отображение ломанной линии.

Недавно я решал аналогичную проблему при кодировании ReportPrinter и не нашел хорошего решения для создания открытого массива параметров заданного размера. Решение, которое я, наконец, использовал, заключалось в хитрой комбинации функции polyline и polygon с ассемблерным кодом. Я публикую исходный код, поскольку думаю что он будет полезен, пока Borland не создаст стандартные и простые методы для работы с массивами заданного размера.
type
PPointArr = ^TPointArr;
TPointArr = array[0..16380] of TPoint;
var
I1: integer;
Elements: word;
PointArr: PPointArr;
begin
GetMem(PointArr, (Elements + 1) * SizeOf(TPoint));
try
for I1 := 0 to Elements do
begin
PointArr^[I1].X := ReadNextXValue;
PointArr^[I1].Y := ReadNextYValue;
end; { for }
{ Вызов Polygon(PointArr^), но только с Elements+1-элеменами
в открытом массиве }
asm
les di,PointArr { Помещаем указатель на PointArr }
push es
push di
push Elements { Помещаем High(PointArr^) }
les di,self { Помещаем указатель self }
push es
push di
les di,es:[di] { Вызов self.Polygon }
call Polygon
end; { asm }
finally
FreeMem(PointArr, (Elements + 1) * SizeOf(TPoint));
end; { try }
end;
|
Вывод пояснения о текущей операции.
Автор: Роман Василенко WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Вывод пояснения о текущей операции.
При вызове этой функции на экране появляется табличка с указанной
в параметре msg надписью. В параметрах TxColor, BgColor и BvColor
определяются цвета текста, заливки и рамки, соответственно.
Возвращает функция ссылку на объект типа TForm, который необходимо
освободить, когда табличка уже не нужна.
Вероятная проблема: Если у Вас слишком много текста в табличке,
она может не уместиться на экране и вылезет краями за его предел.
Зависимости: Forms, StdCtrls, ExtCtrls, Controls, Graphics, Classes;
Автор: Роман Василенко, romix@nm.ru, Пятигорск
Copyright: Роман Василенко
Дата: 23 сентября 2002 г.
***************************************************** }
//ИМХО, нечего комментировать здесь.
function ShowWaitMsg(msg: string; TxColor, BgColor, BvColor: tcolor): tform;
var
frm: tform;
lb: tlabel;
sh: tshape;
begin
frm := tform.CreateNew(Application);
with frm do
begin
BorderIcons := [];
borderstyle := bsNone;
position := poScreenCenter;
sh := tshape.create(frm);
sh.parent := frm;
sh.Align := alClient;
sh.brush.color := BgColor;
sh.pen.color := BvColor;
sh.pen.style := pssolid;
sh.Pen.Width := 2;
lb := tlabel.Create(frm);
lb.parent := frm;
lb.Left := 10;
lb.top := 5;
lb.Caption := msg;
lb.Font.Size := 14;
lb.Font.Style := [fsbold];
lb.Alignment := taCenter;
lb.AutoSize := true;
lb.Transparent := true;
lb.WordWrap := true;
clientwidth := lb.Canvas.TextWidth(msg) + 20;
clientheight := lb.Canvas.TextHeight(msg) + 10;
lb.Font.color := TxColor;
show;
end;
application.processmessages;
result := frm;
end;
Пример использования:
// Например, заводим переменную:
var
f: tform;
// Для появления таблички, делаем:
f := ShowWaitMsg('Привет! Это программа Васи Пупкина!',
clWhite, ClNavy, clBlack);
// Для того, чтобы потом избавиться от нее:
f.free;
Показ удаленных записей в таблице dBASE.
В таблицах dBASE записи не удаляются до тех пор, пока таблица не будет упакована. Пока же это не произойдет, удаленные записи остаются в таблице, только имеют при этом флажок "к удалению". Для того, чтобы показать эти существующие, но не отображаемые записи, существует функция ShowDeleted(), которая использует функцию BDE API DbiSetProp(), показывающая записи, помеченные к удалению. При использовании этой функции нет необходимости закрывать и вновь открывать таблицу. ShowDeleted() в качестве параметров передается TTable и логическое значение. Логический параметр указывает на необходимость показа удаленных записей.Демонстрационный проект:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, DB, DBTables;
type
TForm1 = class(TForm)
Table1: TTable;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
CheckBox1: TCheckBox;
procedure CheckBox1Click(Sender: TObject);
public
procedure ShowDeleted(Table: TTable; ShowDeleted: Boolean);
end;
var
Form1: TForm1;
implementation
uses DBITYPES, DBIERRS, DBIPROCS;
{$R *.DFM}
procedure TForm1.ShowDeleted(Table: TTable; ShowDeleted: Boolean);
var
rslt: DBIResult;
szErrMsg: DBIMSG;
begin
Table.DisableControls;
try
Check(DbiSetProp(hDBIObj(Table.Handle), curSOFTDELETEON,
LongInt(ShowDeleted)));
finally
Table.EnableControls;
end;
Table.Refresh;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
ShowDeleted(Table1, CheckBox1.Checked);
end;
end.
|
Показать удаленные записи таблицы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure DeletedRecords(Table: TTable; SioNo: Boolean);
begin
Table.DisableControls;
try
Check(DbiSetProp(hDBIObj(Table.Handle), curSOFTDELETEON, Longint(SioNo)));
finally
Table.EnableControls;
end;
Table.Refresh;
end;
Как заставить приложение показывать различные иконки при различных разрешениях дисплея.
Для этого достаточно текущее разрешение экрана и в соответствии с ним изменить дескриптор иконки приложения. Естественно, что Вам придется создать в ресурсах новые иконки.
Поместите следующий код в файл проекта (.DPR) Вашего приложения:
Application.Initialize; Application.CreateForm(TForm1, Form1); case GetDeviceCaps(GetDC(Form1.Handle), HORZRES) of 640 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON640'); 800 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON800'); 1024 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON1024'); 1280 : Application.Icon.Handle := LoadIcon (hInstance, 'ICON1280'); end; Application.Run; |
Как вывести сведения о диске (метка тома, серийный номер, файловая система и т.д.).
| Звонок в службу технической поддержки: - У меня компьютер не работает! - После чего это произошло? - Я его включил - загрузился Нортон. Смотрю - у меня слева диск С: и справа диск С:. Я подумал - нафиг мне два диска С:? И стер правый к чертовой матери. |
procedure TForm1.Button2Click(Sender: TObject);
var
VolumeName,
FileSystemName : array [0..MAX_PATH-1] of Char;
VolumeSerialNo : DWord;
MaxComponentLength,FileSystemFlags: Cardinal;
begin
GetVolumeInformation('C:\',VolumeName,MAX_PATH,@VolumeSerialNo,
MaxComponentLength,FileSystemFlags, FileSystemName,MAX_PATH);
Memo1.Lines.Add('VolumeName = '+VolumeName);
Memo1.Lines.Add('SerialNo = $'+IntToHex(VolumeSerialNo,8));
Memo1.Lines.Add('CompLen = '+IntToStr(MaxComponentLength));
Memo1.Lines.Add('Flags = $'+IntToHex(FileSystemFlags,4));
Memo1.Lines.Add('FSName = '+FileSystemName);
end;
|
Как программно вывести окно свойств экрана.
Автор: Nomadic.
ShellExecute(Application.Handle, 'open', 'desk.cpl', nil, nil, sw_ShowNormal); |
Показать диалог выбора домена.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
If you are developing network software for Windows NT,
you usually need to ask the user to select a computer or domain
he wants to connect/login.
}
const
FOCUSDLG_DOMAINS_ONLY = 1;
FOCUSDLG_SERVERS_ONLY = 2;
FOCUSDLG_SERVERS_DOMAINS = 3;
FOCUSDLG_BROWSE_LOGON_DOMAIN = $00010000;
FOCUSDLG_BROWSE_WKSTA_DOMAIN = $00020000;
FOCUSDLG_BROWSE_OTHER_DOMAINS = $00040000;
FOCUSDLG_BROWSE_TRUSTING_DOMAINS = $00080000;
FOCUSDLG_BROWSE_WORKGROUP_DOMAINS = $00100000;
FOCUSDLG_BROWSE_ALL_DOMAINS = FOCUSDLG_BROWSE_LOGON_DOMAIN or
FOCUSDLG_BROWSE_WKSTA_DOMAIN or FOCUSDLG_BROWSE_OTHER_DOMAINS or
FOCUSDLG_BROWSE_TRUSTING_DOMAINS or FOCUSDLG_BROWSE_WORKGROUP_DOMAINS;
function SystemFocusDialog(hwndOwner: HWND; dwSelectionFlag: UINT;
wszName: PWideChar; dwBufSize: DWORD; var bOKPressed: Boolean;
wszHelpFile: PWideChar; dwContextHelpId: DWORD): DWORD; stdcall;
external 'ntlanman.dll' Name 'I_SystemFocusDialog';
function ComputerBrowser(hWndParent: HWND; wCompName: PWideChar;
dwBufLen: DWORD): Boolean;
var
dwError: DWORD;
begin
Result := False;
dwError := SystemFocusDialog(hWndParent, FOCUSDLG_SERVERS_DOMAINS or
FOCUSDLG_BROWSE_ALL_DOMAINS,
wCompName, dwBufLen, Result, nil, 0);
if dwError <> NO_ERROR then Exit;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
wCompName: array [0..MAX_COMPUTERNAME_LENGTH + 1] of WideChar;
begin
if ComputerBrowser(0, wCompName, MAX_COMPUTERNAME_LENGTH + 1) then
ShowMessage(wCompName)
else
ShowMessage('no computer selected');
end;
{***************************}
// Show the ServerBrowseDialogA0 Dialog
type
TServerBrowseDialogA0 = function(hwnd: HWND; pchBuffer: Pointer;
cchBufSize: DWORD): bool;
stdcall;
function ShowServerDialog(AHandle: THandle): string;
var
ServerBrowseDialogA0: TServerBrowseDialogA0;
LANMAN_DLL: DWORD;
buffer: array[0..1024] of char;
bLoadLib: Boolean;
begin
LANMAN_DLL := GetModuleHandle('NTLANMAN.DLL');
if LANMAN_DLL = 0 then
begin
LANMAN_DLL := LoadLibrary('NTLANMAN.DLL');
bLoadLib := True;
end;
if LANMAN_DLL <> 0 then
begin @ServerBrowseDialogA0 := GetProcAddress(LANMAN_DLL, 'ServerBrowseDialogA0');
DialogBox(HInstance, MAKEINTRESOURCE(101), AHandle, nil);
ServerBrowseDialogA0(AHandle, @buffer, 1024);
if buffer[0] = '\' then
begin
Result := buffer;
end;
if bLoadLib then
FreeLibrary(LANMAN_DLL);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
label1.Caption := ShowServerDialog(Form1.Handle);
end;
Отображение отформатированных сообщений об ошибках.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function GetLastErrorText(): string;
var
dwSize: DWORD;
lpszTemp: PAnsiChar;
begin
dwSize := 512;
lpszTemp := nil;
try
GetMem(lpszTemp, dwSize);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY,
nil,
GetLastError(),
LANG_NEUTRAL,
lpszTemp,
dwSize,
nil)
finally
Result := lpszTemp;
FreeMem(lpszTemp)
end
end;
Отображение отформатированных сообщений об ошибках 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function SystemErrorMessage: string;
var
P: PChar;
begin
if FormatMessage(Format_Message_Allocate_Buffer + Format_Message_From_System,
nil,
GetLastError,
0,
@P,
0,
nil) <> 0 then
begin
Result := P;
LocalFree(Integer(P))
end
else
Result := '';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(SystemErrorMessage);
end;
Показать файлы с ассоциированными с ними иконками в TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
The following example shows how to show all files and their
associated icons of a folder in a TListView.
To test the code, you need a ListView1 and a ImageList1 where the icons are stored.
Im folgenden Beispiel werden alle Dateien & zugehorigen
Icons eines Verzeichnisses in einer TListView angezeigt.
Um das Beispiel zu testen, braucht man eine ListView1 und eine ImageList1
Komponente, wo die Icons gespeichert werden.
}
uses
ShellApi;
procedure LV_InsertFiles(strPath: string; ListView: TListView; ImageList: TImageList);
var
i: Integer;
Icon: TIcon;
SearchRec: TSearchRec;
ListItem: TListItem;
FileInfo: SHFILEINFO;
begin
// Create a temporary TIcon
Icon := TIcon.Create;
ListView.Items.BeginUpdate;
try
// search for the first file
i := FindFirst(strPath + '*.*', faAnyFile, SearchRec);
while i = 0 do
begin
with ListView do
begin
// On directories and volumes
if ((SearchRec.Attr and FaDirectory <> FaDirectory) and
(SearchRec.Attr and FaVolumeId <> FaVolumeID)) then
begin
ListItem := ListView.Items.Add;
//Get The DisplayName
SHGetFileInfo(PChar(strPath + SearchRec.Name), 0, FileInfo,
SizeOf(FileInfo), SHGFI_DISPLAYNAME);
Listitem.Caption := FileInfo.szDisplayName;
// Get The TypeName
SHGetFileInfo(PChar(strPath + SearchRec.Name), 0, FileInfo,
SizeOf(FileInfo), SHGFI_TYPENAME);
ListItem.SubItems.Add(FileInfo.szTypeName);
//Get The Icon That Represents The File
SHGetFileInfo(PChar(strPath + SearchRec.Name), 0, FileInfo,
SizeOf(FileInfo), SHGFI_ICON or SHGFI_SMALLICON);
icon.Handle := FileInfo.hIcon;
ListItem.ImageIndex := ImageList.AddIcon(Icon);
// Destroy the Icon
DestroyIcon(FileInfo.hIcon);
end;
end;
i := FindNext(SearchRec);
end;
finally
Icon.Free;
ListView.Items.EndUpdate;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// Assign a Imagelist to the ListView
ListView1.SmallImages := ImageList1;
// Show Listview in Report Style and add 2 Columns
ListView1.ViewStyle := vsReport;
ListView1.Columns.Add;
ListView1.Columns.Add;
LV_InsertFiles('C:\Windows\', ListView1, ImageList1);
end;
Показать свойства файла.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, SoCtrls, SoSpecEdit, ShellAPI;
type
TForm1 = class(TForm)
Button1: TButton;
sohoFilenameEdit1: TsohoFilenameEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure ShowPropertiesDialog(FName: string);
var
SExInfo: TSHELLEXECUTEINFO;
Error: LongInt;
begin
ZeroMemory(Addr(SExInfo),SizeOf(SExInfo));
SExInfo.cbSize := SizeOf(SExInfo);
SExInfo.lpFile := PChar(FName);
SExInfo.lpVerb := 'properties';
SExInfo.fMask := SEE_MASK_INVOKEIDLIST;
ShellExecuteEx(Addr(SExInfo));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowPropertiesDialog(sohoFilenameEdit1.FileName);
end;
end.
|
Как показать иконку, ассоциированную с данным типом файла.
| Приходит программист в магазин. Там продавщица - полная такая тетенька. Программист (меланхолично оглядевшись): - Бутылку пива и пачку презервативов. Продавщица: - Это ты девушку охмурить бутылкой пива собрался? Вино надо дорогое покупать.... Программист (отрешенно): - Да нет. Пиво я выпью, когда она уйдет. |
uses
ShellAPI;
...
procedure TForm1.Button1Click(Sender: TObject);
var
Icon: hIcon;
IconIndex: word;
begin
IconIndex := 1;
Icon := ExtractAssociatedIcon(HInstance,
Application.ExeName, IconIndex);
DrawIcon(Canvas.Handle, 10, 10, Icon);
end;
|
Показать первый кадр AVI-файла.
| Следом за акцией "Кликни Деда Мороза!" компания Coca-Cola планирует с Нового года запустить акции "Бэкапни Деда Мороза" и "Форматни Снегурочку" |
procedure TForm1.Button1Click(Sender: TObject); begin Application.ProcessMessages; MediaPlayer1.Open; Application.ProcessMessages; MediaPlayer1.Step; Application.ProcessMessages; MediaPlayer1.Previous; end; |
Просмотреть текст формы из запущенной программы.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
rs: TResourceStream;
ms: TMemoryStream;
begin
rs := TResourceStream.Create(HInstance, ClassName, RT_RCDATA);
try
ms := TMemoryStream.Create;
try
ObjectBinaryToText(rs, ms);
ms.Seek(0, 0);
memo1.Lines.LoadFromStream(ms);
finally
ms.Free;
end;
finally
rs.Free;
end;
end;
Показ формы без фокуса.
ShowWindow(theWindowHandle, SW_SHOWNOACTIVE); |
Отображение полных строк списка при перемещении мыши по списку.

| Автор: Serzs |
| Внимание! Вы изменили положение мыши. Для того, чтобы изменения вступили в силу, необходимо перезагрузить компьютер. |
Текст формы примера:
object MainForm: TMainForm Left = 7 Top = 121 Width = 200 Height = 157 Hint = '34534535' Caption = 'Long hints' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -13 Font.name = 'MS Sans Serif' Font.Style = [] ShowHint = True OnCreate = FormCreate PixelsPerInch = 120 TextHeight = 16 object ListBox1: TListBox Left = 12 Top = 12 Width = 165 Height = 97 Hint = '1|2' ItemHeight = 16 Items.Strings = ( '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'A1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ' '1234567890' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'ABCD') ParentShowHint = False ShowHint = True TabOrder = 0 OnMouseMove = ListBox1MouseMove end end |
Текст модуля:
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TMainForm = class(TForm)
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
procedure ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
private
{ Private declarations }
FHintRow : Integer; // Номер строки в списке, на которую указывает мышь
public
{ Public declarations }
// Обработчик подсказок
procedure OnShowHint(var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
end;
var
MainForm: TMainForm;
implementation
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
FHintRow := -1;
Application.OnShowHint := OnShowHint; // Установка обработчика
end;
procedure TMainForm.OnShowHint(var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
var
Pos: TPoint;
begin
with HintInfo do
// Проверка на нужный объект
if HintControl is TListBox then
with HintControl as TListBox do
begin
Pos.X := 0;
Pos.Y := ListBox1.Tag;
HintPos := ListBox1.ClientToScreen(Pos);
HintStr := ListBox1.Hint;
end;
end;
procedure TMainForm.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
MousePos : TPoint;
ItemPos : TRect;
RowWidth,
ItemNum : Integer;
FHint : string;
begin
MousePos.X := X;
MousePos.Y := Y;
// Определение номера строки в списке
ItemNum := ListBox1.ItemAtPos(MousePos, True);
// Проверка на перемещение мыши на другую строку
if (ItemNum <> FHintRow) then
begin
FHintRow := ItemNum;
// Проверка на наличие элементов в списке
if ItemNum <> -1 then
begin
ItemPos := ListBox1.ItemRect(ItemNum);
Application.CancelHint; // Снять текущую подсказку
ListBox1.Tag := ItemPos.Top; // Запоминаем позицию строки по вертикали
FHint := ListBox1.Items[ItemNum];
// Проверка на ширину строки
RowWidth := ListBox1.Canvas.TextWidth( FHint );
if (RowWidth > ListBox1.ClientWidth) then
FHint := FHint + '|'
else
FHint := '';
ListBox1.Hint := FHint;
end
else
begin
ListBox1.Hint := '';
Application.CancelHint;
ListBox1.Tag := -1;
end;
end
end;
end.
|
Текст проекта:
program PrjHint;
uses
Forms,
Main in 'Main.pas' {MainForm};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.ShowHint := True;
Application.HintPause := 100;
Application.HintHidePause := 999999;
Application.Run;
end.
|
Как вывести главное окно справочной системы.
| В тюремной камере: - Тебе сколько дали? - 15 лет. - А за что? - Окно разбил. - Да где ж ты работаешь? - В Майкрософте... |
В 16-битных версиях справочной системы необходимо было вызывать начальное (главное) окно помощи с параметром HELP_CONTENTS в комманде HelpCommand. В 32-битном варианте это осуществляется следующим образом:
Application.HelpCommand(HELP_FINDER, 0); |
Примечание: Для того, чтобы показывались "книжки" (или главные темы) - необходимо создать .CNT-файл.
Вывод справочной информации.
Чтобы вывести справочную информацию, которая находится в СНМ-файле, нужно воспользоваться ActiveX-компонентом (элементом управления) ньореп, который входит в состав Windows и представляет собой специальную динамическую библиотеку (файл Hhopen.ocx). Первое, что следует сделать, — установить компонент Hhopen на одну из вкладок палитры компонентов. Для этого надо из меню Component выбрать команду Import ActiveX Control. На экране появится окно Import ActiveX, в котором будут перечислены все зарегистрированные в реестре Windows компоненты. В окне Import ActiveX, в списке зарегистрированных компонентов, нужно выбрать строку hhopen OLE Control module и щелкнуть на кнопке Install. В результате этого на экране появится диалоговое окно Install, в котором программист может выбрать пакет (packege — пакет, библиотека компонентов), в который будет добавлен устанавливаемый компонент. Компоненты, добавляемые программистом, "по умолчанию" добавляются в пакет dciusr. В результате щелчка на кнопке ОК выбранный компонент добавляется в пакет, и на экране появляется окно Package и запрос подтверждения процесса перекомпиляции пакета . По завершении процесса компиляции на экране появится окно, информирующее о том, что компонент добавлен в пакет и зарегистрирован . Значок компонента ньореп будет добавлен на вкладку ActiveX. В процессе компиляции будет создан файл представления компонента -- модуль HHOPENLib_TLIB.pas, который содержит описание методов, свойств и событий компонента.Модуль представления можно увидеть, загрузив его в редактор кода из каталога \Delphi 7\Lib. Пролистав в окне редактора кода модуль HHOPENLib_TLIB.pas, который представляет собой интерфейс для доступа к элементу управления, можно найти описание класса THhopen.
THhopen = class(Telecontrol)
private
FIntf: _DHhopen;
function GetControlInterface: _DHhopen;
protected
procedure CreateControl;
procedure InitControlData; override;
public
function OpenHelp(const HelpFile: WideString;
const HelpSection: WideString): Integer;
procedure CloseHelp;
property ControlInterface: _DHhopen read GetControlInterface;
property DefaultInterface: _DHhopen read GetControlInterface;
published
property isHelpOpened: WordBool index 1
read GetWordBoolProp
write SetWordBoolProp
stored False;
end;
Класс тньореп предоставляет два метода: OpenHelp и CloseHelp.
Метод OpenHeip обеспечивает вывод справочной информации, метод close-Help — закрывает окно справочной системы.
У метода openHeip два параметра — имя файла справочной информации и имя раздела, содержимое которого будет выведено. В качестве имени раздела надо использовать имя HTML-файла, который применялся программой HTML Help Workshop в процессе создания СНМ-файла. Следует обратить внимание на то, что оба параметра должны быть строками widechar.
Следующая программа, ее диалоговое окно приведено на рис. 14.10, а текст — в листинге 14.2, демонстрирует использование ActiveX-компонента Hhopen для вывода справочной информации. Компонент нпореп добавляется в форму обычным образом. Так как во время работы программы он не отображается, то его можно поместить в любое место формы.
interface
uses
Windows, Messages, SysUtils,
Classes, Graphics, Controls,
Forms, Dialogs, OleCtrls, HHOPENLibJTLB, StdCtrls;
type
TForm1 = class(TForm)Label1: TLabel;
Editl: TEdit; // файл справки
Edit2: TEdit; //раздел справки (имя HTML-файла)
Button1: TButton; // кнопка Справка
Hhopen1: THhopen; // ActiveX-компонент Hhopen
Label2: TLabel;
Label3: TLabel;
procedure ButtonlClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// щелчок на кнопке Справка
procedure TForm1.ButtonlClick(Sender: TObject);
var
HelpFile: string; // файл справки
HelpTopic: string; // раздел справки
pwHelpFile: PWideChar;
// файл справки (указатель на строку WideChar)
pwHelpTopic: PWideChar;
// раздел (указатель на строку WideChar)
begin
HelpFile := Edit1.Text;
HelpTopic := Edit2.Text;
// выделить память для строк WideChar
GetMemfpwHelpFile, Length(HelpFile) * 2);
GetMemfpwHelpTopic, Length(HelpTopic) * 2);
// преобразовать Ansi-строку в WideString-строку
pwHelpFile := StringToWideChar(HelpFile, pwHelpFile, MAX_PATH * 2)
pwHelpTopic := StringToWideChar(HelpTopic, pwHelpTopic, 32);
// вывести справочную информацию
Form1.Hhopen1.OpenHelp(pwHelpFile, pwHelpTopic);
end;
end.
Вывод справочной информации выполняет процедура обработки события Onciic на кнопке Справка. Так как параметры метода OpenHelp должны быть строками widechar, то сначала выполняется преобразование ANSI-строки В строку WideChar.
Как скрыть или показать иконки на рабочем столе.

procedure TForm1.Button1Click(Sender: TObject); begin // скрыть иконки ShowWindow(FindWindow(nil, 'Program Manager'), SW_HIDE); end; procedure TForm1.Button2Click(Sender: TObject); begin // показать иконки ShowWindow(FindWindow(nil, 'Program Manager'), SW_SHOW); end; |
Как скрыть или показать иконки на рабочем столе 2.
procedure ShowDesktop(const YesNo : boolean);
var h : THandle;
begin
h := FindWindow('ProgMan', nil);
h := GetWindow(h, GW_CHILD);
if YesNo = True then
ShowWindow(h, SW_SHOW)
else
ShowWindow(h, SW_HIDE);
end;
{Использование:}
{Скрыть иконки на рабочем столе}
ShowDesktop(False);
{Показать иконки на рабочем столе}
ShowDesktop(true);
|
Показать или спрятать окно.
Автор: Осипов Евгений WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Показать/спрятать окно
SW_HIDE - скрывает окно и убирает из панели задачь, например нужно
при использовании иконки программы в системном трее при сворачивании
окна убирать его из панели задачь
SW_RESTORE - восстанавливает окно, при использовании иконки в
системном трее при нажатии на иконку, показать скрытое SW_HIDE-ом окно.
Другие параметры смотрите в справке Delphi по ShowWindow
Зависимости: Uses Windows
Автор: ОсиповЕвгений Анатольевич, ICQ:114205759, Бузулук
Copyright: Delphi Help, Windows API
Дата: 31 мая 2002 г.
***************************************************** }
ShowWindow(Application.Handle, SW_RESTORE); //Восстановить
ShowWindow(Application.Handle, SW_HIDE); //скрыть
Показ и скрытие дочерних MDI-окон.
Автор: Neil...да, я понял: необходим гарантированный показ или скрытие MDI-окна. Гарантированно скрыть можно вызовом ShowWindow(theHandle, SW_HIDE), но в этом случае при показе *НЕ* используется ShowWindow. Вместо это сделайте так:
SetWindowPos(theHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE OR SWP_NOSIZE OR SWP_SHOWWINDOW); |
Далее я обнаружил, что дочерние окна не скрывались/показывались, *ЕСЛИ* для осуществления этих функций использовались оконные компоненты (например, кнопки). Для решения проблемы добавьте следующую строку после вызова SetWindowPos:
WinProcs.SetFocus(TheHandle); |
*НЕ* используйте метод SetFocus; SetFocus здесь - Windows API функция.
ДополнениеПо моему,все-таки, вызов ShowWindow(theHandle, SW_HIDE)проще, короче и, по крайней мере, в Delphi5 нормально работает с оконными компонентами.
procedure TMainForm.Button1Click(Sender: TObject);
begin
//кнопка для показа/скрытия дочернего MDI-окна
If ShowWindow(form1MDI.Handle, SW_HIDE)=False then
ShowWindow(form1MDI.Handle, SW_SHOW);
end;
|
С уважением Михаил Шпанер.
Спрятать или показать кнопку ПУСК.
По нажатию на кнопку пишем следующее.
Startbutton(false); |
...круто!!!...а чтобы сделать ПУСК опять видимым:
Startbutton(true); |
Если вы думаете, что на этом дело и закончится, хочу поспешить вас разубедить, ведь вам ещё нужно реализовать процедуру Startbutton:
procedure Startbutton(visi:boolean);
var
Tray, Child : hWnd;
C : array[0..127] of Char;
S : string;
begin
Tray := FindWindow('Shell_TrayWnd', nil);
Child := GetWindow(Tray, GW_CHILD);
while Child <> 0 do
begin
if GetClassName(Child, C, SizeOf(C)) > 0 then
begin
S := StrPAS(C);
if UpperCase(S) = 'BUTTON' then
begin
if Visi then
ShowWindow(Child, 1)
else
ShowWindow(Child, 0);
end;
end;
Child := GetWindow(Child, GW_HWNDNEXT);
end;
end;
|
Как программно спрятать или показать заголовок окна TitleBar.

procedure TForm1.HideTitlebar;
var
Save: Longint;
begin
if BorderStyle=bsNone then
Exit;
Save:=GetWindowLong(Handle, GWL_STYLE);
if (Save and WS_CAPTION)=WS_CAPTION then
begin
case BorderStyle of
bsSingle, bsSizeable:
SetWindowLong(Handle, GWL_STYLE, Save and (not WS_CAPTION) or WS_BORDER);
bsDialog:
SetWindowLong(Handle, GWL_STYLE, Save and
(not WS_CAPTION) or DS_MODALFRAME or WS_DLGFRAME);
end;
Height:=Height-GetSystemMetrics(SM_CYCAPTION);
Refresh;
end;
end;
procedure TForm1.ShowTitlebar;
var
Save: Longint;
begin
if BorderStyle=bsNone then
Exit;
Save:=GetWindowLong(Handle, GWL_STYLE);
if (Save and WS_CAPTION)<>WS_CAPTION then
begin
case BorderStyle of
bsSingle, bsSizeable:
SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or WS_BORDER);
bsDialog:
SetWindowLong(Handle, GWL_STYLE, Save or WS_CAPTION or
DS_MODALFRAME or WS_DLGFRAME);
end;
Height:=Height+getSystemMetrics(SM_CYCAPTION);
Refresh;
end;
end;
|
Заставляем появиться Hint в нужный момент.
Сделаем это по нажатию на первую кнопку, а по нажатию на вторую кнопку будем скрывать окно hint'a:
public
{ Public declarations }
h: THintWindow;
procedure TForm1.Button1Click(Sender: TObject);
begin
if h<>nil then
H.ReleaseHandle;
H:=THintWindow.Create(Form1);
H.ActivateHint(Form1.ClientRect, 'Это всплывающая подсказка');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if h<>nil then
H.ReleaseHandle;
end;
|
Как вывести hint, когда курсор мышки над определённым контролом.
var hintWnd: THintWindow;
procedure TForm1.ActivateHintNOW( x,y: Integer);
var
rect: TRect;
begin
HintTxt := 'qq';
if hintTxt <> '' then
begin
rect := hintWnd.CalcHintRect( Screen.Width, hinttxt, nil);
rect.Left := rect.Left + x;
rect.Right := rect.Right + x;
rect.Top := rect.Top + y;
rect.Bottom := rect.Bottom + y;
hintWnd.ActivateHint( rect, hinttxt);
end;
end;
// Замечание: Не забудьте каждый раз создавать hintWnd:
// hintwnd:= THintWindow.create(self);
// а затем освобождать его:
// hintwnd.releasehandle;
|
Показ изображений в DBGrid.
| Муж-программист (М) с женой (Ж) сидят в цирке на выступлении иллюзиониста. Во время очередного номера из небольшого ящика выходят много девушек. Ж: - Как они могли поместится в таком маленьком ящике?... М: - Ерунда! Если бы он использовал WinRAR он бы еще больше их туда запихнул... |
Код компонента:
{
// DBPICGRD.PAS (C)
// ВСЕ ПРАВА ЗАЩИЩЕНЫ.
//
// ОПИСАНИЕ:
// Компонент DBGrid, способный
// отображать графику в ячейках.
}
unit DBPicGrd;
interface
uses
DBGrids, DB, DBTables, Grids, WinTypes, Classes, Graphics;
type
TDBPicGrid = class(TDBGrid)
protected
procedure DrawDataCell(const Rect: TRect;
Field: TField; State: TGridDrawState); override;
public
constructor Create(AOwner: TComponent); override;
published
property DefaultDrawing default False;
end;
procedure Register;
implementation
constructor TDBPicGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
DefaultDrawing := False;
end;
procedure TDBPicGrid.DrawDataCell(const Rect: TRect; Field: TField;
State: TGridDrawState);
var
bmp: TBitmap;
begin
with Canvas do
begin
FillRect(Rect);
if Field is TGraphicField then
try
bmp := TBitmap.Create;
bmp.Assign(Field);
Draw(Rect.Left, Rect.Top, bmp);
finally
bmp.Free;
end
else
TextOut(Rect.Left, Rect.Top, Field.Text);
end;
end;
procedure Register;
begin
RegisterComponents('Custom', [TDBPicGrid]);
end;
end.
|
Подскажите как правильно показать на экране и сохранить в базе картинку формата JPEG.
if Picture.Graphic is TJPegImage then begin bs:=TBlobStream.Create(TBlobField(Field),bmWrite); Picture.Graphic.SaveToStream(bs); bs.Free; end else if Picture.Graphic is TBitmap then begin Jpg:=TJPegImage.Create; Jpg.CompressionQuality:=...; Jpg.PixelFormat:=...; Jpg.Assign(Picture.Graphic); Jpg.JPEGNeeded; bs:=TBlobStream.Create(TBlobField(Field),bmWrite); Jpg.SaveToStream(bs); bs.Free; Jpg.Free; end else Field.Clear; |
Вывести информацию о пакете.
Автор: Xavier Pacheco
program PkgInfo;
uses
Forms,
Dialogs,
SysUtils,
PkgMain in 'PkgMain.pas' {PackInfoForm};
{$R *.RES}
var
OpenDialog: TOpenDialog;
begin
if (ParamCount > 0) and FileExists(ParamStr(1)) then
PkgName := ParamStr(1)
else
begin
OpenDialog := TOpenDialog.Create(Application);
OpenDialog.DefaultExt := '*.bpl';
OpenDialog.Filter := 'Packages (*.bpl)|*.bpl|Delphi 3 Packages ' +
'(*.dpl)|*.dpl';
if OpenDialog.Execute then
PkgName := OpenDialog.FileName;
end;
if PkgName <> '' then
begin
Application.Initialize;
Application.CreateForm(TPackInfoForm, PackInfoForm);
Application.Run;
end;
end.
unit PkgMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TPackInfoForm = class(TForm)
GroupBox1: TGroupBox;
DsgnPkg: TCheckBox;
RunPkg: TCheckBox;
BuildCtl: TRadioGroup;
GroupBox2: TGroupBox;
GroupBox3: TGroupBox;
Button1: TButton;
Label1: TLabel;
DescEd: TEdit;
memContains: TMemo;
memRequires: TMemo;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
end;
var
PackInfoForm: TPackInfoForm;
PkgName: string; // This is assigned in project file
implementation
{$R *.DFM}
procedure PackageInfoCallback(const Name: string; NameType: TNameType;
Flags: Byte; Param: Pointer);
var
AddName: string;
Memo: TMemo;
begin
Assert(Param <> nil);
AddName := Name;
case NameType of
ntContainsUnit: Memo := TPackInfoForm(Param).memContains;
ntRequiresPackage: Memo := TPackInfoForm(Param).memRequires;
else
Memo := nil;
end;
if (Memo <> nil) then
begin
if Memo.Text <> '' then
AddName := ', ' + AddName;
Memo.Text := Memo.Text + AddName;
end;
end;
procedure TPackInfoForm.FormCreate(Sender: TObject);
var
PackMod: HMODULE;
Flags: Integer;
begin
// Since we only need to get into the package's resources,
// LoadLibraryEx with LOAD_LIBRARY_AS_DATAFILE provides a speed-
// efficient means for loading the package.
PackMod := LoadLibraryEx(PChar(PkgName), 0, LOAD_LIBRARY_AS_DATAFILE);
if PackMod = 0 then
Exit;
try
GetPackageInfo(PackMod, Pointer(Self), Flags, PackageInfoCallback);
finally
FreeLibrary(PackMod);
end;
Caption := 'Package Info: ' + ExtractFileName(PkgName);
DsgnPkg.Checked := Flags and pfDesignOnly <> 0;
RunPkg.Checked := Flags and pfRunOnly <> 0;
if Flags and pfNeverBuild <> 0 then
BuildCtl.ItemIndex := 1;
DescEd.Text := GetPackageDescription(PChar(PkgName));
end;
procedure TPackInfoForm.Button1Click(Sender: TObject);
begin
Close;
end;
end.
Показывать номер страницы.
Sub ShowViewPageNo()
ActiveDocument.Repaginate
varTotalPages = Selection.Information(wdNumberOfPagesInDocument)
varVertScrollPercent = ActiveDocument.ActiveWindow _
.VerticalPercentScrolled
varViewPage = Int((varVertScrollPercent / 100) * varTotalPages) + 1
varIPPage = Selection.Information(wdActiveEndPageNumber)
MsgBox "Your insertion point is on page [" & varIPPage & "]." + _
"You are looking at page [" & varViewPage & "].""
End Sub
|
Как показать окно свойств экрана.

| Я сижу за монитором нажимаю на мыша и смотрю погасшим взором, как стреляет П.П.Ш. |
Для этого воспользуемся 'Rundll32.exe' и запустим её в 'shellexecute'. Не забудьте добавить 'shellapi' в Ваш список uses.
//Эта функция совместима со всеми версиями Windows
function GetSystemDir: TFileName;
var
SysDir: array [0..MAX_PATH-1] of char;
begin
SetString(Result, SysDir, GetSystemDirectory(SysDir, MAX_PATH));
if Result = '' then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
x: Tfilename;
begin
x := getsystemdir;
ShellExecute(Form11.Handle, 'open', Pchar('rundll32.exe'),
'shell32.dll,Control_RunDLL Desk.cpl,@0,3', Pchar(X), SW_normal);
end;
|
Показать диалог выбора компьютера.

| - Дорогой, ты кого больше любишь? Меня или эту дурацкую штуку, за которой ты проводить целый, св#лочь!!! - Ну, киса, как ты можешь сравнивать неодушевленный предмет с компьютером?! |
{
The "Choose Computer" is a dialog provided by network services
(NTLANMAN.DLL) for Windows 2k/NT/XP
to display the servers and their computers.
}
type
TServerBrowseDialogA0 = function(hwnd: HWND; pchBuffer: Pointer; cchBufSize: DWORD): bool;
stdcall;
function ShowServerDialog(AHandle: THandle): string;
var
ServerBrowseDialogA0: TServerBrowseDialogA0;
LANMAN_DLL: DWORD;
buffer: array[0..1024] of char;
bLoadLib: Boolean;
begin
LANMAN_DLL := GetModuleHandle('NTLANMAN.DLL');
if LANMAN_DLL = 0 then
begin
LANMAN_DLL := LoadLibrary('NTLANMAN.DLL');
bLoadLib := True;
end;
if LANMAN_DLL <> 0 then
begin @ServerBrowseDialogA0 := GetProcAddress(LANMAN_DLL, 'ServerBrowseDialogA0');
DialogBox(HInstance, MAKEINTRESOURCE(101), AHandle, nil);
ServerBrowseDialogA0(AHandle, @buffer, 1024);
if buffer[0] = '\' then
begin
Result := buffer;
end;
if bLoadLib then
FreeLibrary(LANMAN_DLL);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
label1.Caption := ShowServerDialog(Form1.Handle);
end;
|
Как показать число, разделив каждые три цифры запятыми.
Автор: Олег Кулабухов.
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
i := 12345678;
Memo1.Lines.Add(FormatFloat('#,', i));
end;
|
Показ логотипа при запуске приложения.

| Автор: Алексей |
| Компания Макросос разработала новый продвинутый бюстгальтер СиСи++ с открывающимися окнами и возможностью удаленного доступа. Бета версия рассылается для тестирования всем желающим (но без застежек). |
Затем в вашем файле PROJECT.DPR где-то сразу после ключевого слова begin напишите примерно следующее:
logoform := TLogoform.Create(nil);
{ ВНИМАНИЕ! show! НЕ showmodal }
logoform.Show;
{ Здесь может размещаться код инициализации приложения,
например, открытие базы данных... }
{ После блока кода, создающего все ваши формы и перед
строчкой Application.Run напишите: }
logoform.Hide;
logoform.Release;
|
Это будет показывать форму с логотипом до тех пор, пока приложение не выполнит инициализационный код и окончательно не запустится.
Показ логотипа при запуске приложения 2.
| Автор: Howard Harkness |
| Юзер - это человек, который иногда наступает на грабли. Чайник - это человек, который всегда наступает на грабли. Ламер - это человек, который считает, что не наступает на грабли, но на самом деле наступает на них чаще, чем чайник. Программист - это человек, который изобретает новые грабли. Билл Гейтс - некое мифическое существо, покровителей граблей. МикроСофт - завод по производству граблей. Ассемблер - язык программирования, в котором программист наступает на грабли миллион раз в секунду. |
Ниже приведен пример кода (форма имеет имя SplashForm, модуль обозван как SPLASH). Мой код добавлен между закомментаренных блоков {>>вставить} и {<<конец вставки}, весь остальной код генерируется Delphi.
type
TSplashForm = class(TForm)
[...labels, bitmaps, и пр., добавляется редактором Delphi...]
procedure FormDeactivate(Sender: TObject);
end;
var
SplashForm: TSplashForm;
implementation
{$R *.DFM}
{ Это шаманское место. Application.Run в нашем случае
вызывает программу деактивации.}
procedure TSplashForm.FormDeactivate(Sender: TObject);
begin
{>>вставить}
Free;
{<<конец вставки}
end;
{>>вставить}
initialization
begin
SplashForm := TSplashForm.Create(nil);
SplashForm.Show;
{ Я не уверен, но причина наличия здесь Update в том,
что, как мне кажется, приложение пока не может работать
c очередью своих сообщений}
SplashForm.Update;
end;
{<<конец вставки}
end.
|
В головном модуле после строчки USES (.DPR-файл) просто добавьте "SPLASH," (не заключайте это в кавычки). Это все. Никакой головной боли с таймерами, никаких запусков отдельных приложений. Логотип быстро появляется и остается до тех пор, пока приложение не начнет свою работу.
Показ логотипа при запуске приложения 3.
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
ULogo in 'ULogo.pas' {LogoForm};
{$R *.RES}
begin
Application.Initialize; {до этого момента никаких изменений}
with TLogoForm.Create(Application) do
try
Show;
Update;
Application.CreateForm(TForm1, Form1);
{GProgress.AddProgress(1); - здесь можно двигать прогресс, если TGauge
или TProgressBar лежат на ТLоgоFоrm'е}
{если есть еще формы, то Application.CreateForm(TForm2, Form2); и т.д.}
finally
Free;
end;
Application.Run;
end.
|
Существует ли средство для вывода определения структуры таблицы.
Автор: Nomadic Для этого существует утилита DB2LOOK. Она находится в SQLLIB\MISC. Пример использования:CONNECT TO SAMPLE USER xxx USING yyy DB2LOOK -d SAMPLE -u xxx -e -t employeeВывод может быть перенаправлен в файл. Полный синтаксис выдаётся по команде:
DB2LOOK ?
Просмотреть текст формы.
unit FRForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
ResStr: TResourceStream;
MemStr: TMemoryStream;
begin
ResStr := TResourceStream.Create(
hInstance, 'TFORM1', RT_RCDATA);
try
MemStr := TMemoryStream.Create;
ResStr.Position := 0;
ObjectBinaryToText (ResStr, MemStr);
MemStr.Position := 0;
Memo1.Lines.LoadFromStream (
MemStr);
finally
ResStr.Free
end;
end;
end.
|
Загрузить весь проект
Показ окна без главной формы.
Как мне сделать так, чтобы видимой была только выбранная форма? (то есть без главной формы)Попробуйте этот код в любом вторичном окне, которое вы НЕ хотите сопровождать главным окном:
...
private {Это включается в объявления формы.}
{ Private declarations }
procedure CreateParams(VAR Params: TCreateParams); override;
...
procedure TForm2.CreateParams(VAR Params: TCreateParams);
begin
Inherited CreateParams(Params);
Params.WndParent := GetDesktopWindow;
end;
|
Присваивая дескриптор окна родительской формы Рабочему столу, вы удаляете ту связь, которая, в нормальной ситуации, при выводе окна на самый верхний уровень, заставляет переместиться туда также целиком все приложение.
Показать корзину Windows.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
ActiveX, ShlObj, ShellApi;
procedure FreePidl(pidl: PItemIDList);
var
allocator: IMalloc;
begin
if Succeeded(shlobj.SHGetMalloc(allocator)) then
begin
allocator.Free(pidl);
{$IFDEF VER90}
allocator.Release;
{$ENDIF}
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
exInfo: TShellExecuteInfo;
begin
// initialize all fields to 0
FillChar(exInfo, SizeOf(exInfo), 0);
with exInfo do
begin
cbSize := SizeOf(exInfo); // required!
fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_IDLIST;
Wnd := Handle;
nShow := SW_SHOWNORMAL;
lpVerb := 'open';
ShGetSpecialFolderLocation(Handle, CSIDL_BITBUCKET, PItemIDLIst(lpIDList));
end;
ShellExecuteEx(@exInfo);
FreePIDL(exinfo.lpIDList);
end;
Исследование Текстового перекодировщика Штирлиц версия 4.0.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Инструменты: 0) SoftIce 1) WDAsm 2) Hex editor (HView). И вновь я говорю вам здрасте, дорогие мои читатели. Вот попалась мне в руки чудесная программка, которая настолько хитро перекодирует текстовые файлы, что вы не запутаетесь в различных русских кодировках. Мне она понравилась, если бы не огромный баннер в правом нижнем углу, который существует в течении минуты с момента создания и всячески мешает мне работать. Непорядок. Я взял файл Shtirlitz.exe и дизассемблировал его в WDAsm. Затем зашёл в String Reference и начал искать подозрительный текст... И нашёл!!! строку : "Shtirlitz.Banner" Дважды кликаю на неё и попадаю сюда:
:00401924 FF1594954400 Call dword ptr [00449594]
:0040192A 50 push eax
:0040192B 6800080000 push 00000800
* Possible StringData Ref from Data Obj ->"Shtirlitz.Banner"
|
:00401930 68D0004400 push 004400D0
:00401935 B928764400 mov ecx, 00447628
:0040193A E836530000 call 00406C75
Судя по вызываемым функциям (GetTickCOunt, SetTimer) мы в процедуре обработки баннерного состояния :)
Теперь посмотрим чуть выше: * Referenced by a CALL at Address: |:0040182A <-- Вот из этого адреса вызывается процедура | :00401911 55 push ebp :00401912 8BEC mov ebp, esp :00401914 83EC1C sub esp, 0000001CИдём по адресу 0040182A, с которого вызывается эта функция и видим:
:0040182A E8E2000000 call 00401911 <--вызываем процедуру обработки баннера :0040182F 84C0 test al, al :00401831 750E jne 00401841 <--если все ОК - прыгаемВот то что нам надо, а что если jne 00401841 на jne 00401833 и тогда программа будет всегда думать, что время баннера истекло. так и есть, я заменил в HView jne 00401841 на jne 00401833, т.е. теперь программа не будет в любом случае прыгать на процедуру вывода баннерного окна. Но программа при запуске начала ругаться, что исполняемый модуль был изменён. Значит в программе есть CRC проверка. Будем копать дальше. Запустим программу с помощью SymbolLoader'a из пакета SoftIce, чтобы оказаться в самом начале программы, затем скролим (не заходя в функции) до этого места:
:00435CE3 FF151C974400 Call dword ptr [0044971C] :00435CE9 50 push eax :00435CEA E83D0CFDFF call 0040692C <-- Подозрительная функция :00435CEF 8945A0 mov dword ptr [ebp-60], eax :00435CF2 50 push eax :00435CF3 E808FDFFFF call 00435A00 <--это конец программе :00435CF8 EB21 jmp 00435D1BИз того, что функция call 00435A00 делает полный каюк программе я заключил, что перед ней функция call 0040692C что-то представляет из себя! Я зашёл в неё (F8) и оказался тут:
:0040692C 8B0D88734400 mov ecx, dword ptr [00447388] :00406932 85C9 test ecx, ecx :00406934 7414 je 0040694A :00406936 FF742410 push [esp+10] :0040693A 8B01 mov eax, dword ptr [ecx] :0040693C FF742410 push [esp+10] :00406940 FF74240C push [esp+0C] :00406944 FF10 call dword ptr [eax] <--что-то скрывают?Их того, что больше функций нет, я предположил, что функция call dword ptr [eax] тоже очень странна! Я опять зашёл в неё и оказался тут: Это оказалось самое главное:
:0041FC4E 55 push ebp :0041FC4F 8BEC mov ebp, esp :0041FC51 83EC1C sub esp, 0000001C :0041FC54 53 push ebx :0041FC55 56 push esi :0041FC56 57 push edi :0041FC57 8BF9 mov edi, ecx :0041FC59 FF7510 push [ebp+10] :0041FC5C FF750C push [ebp+0C] :0041FC5F FF7508 push [ebp+08] :0041FC62 E84D6EFEFF call 00406AB4 :0041FC67 84C0 test al, al :0041FC69 745C je 0041FCC7 :0041FC6B 8BCF mov ecx, edi :0041FC6D E8E9060000 call 0042035B :0041FC72 8BCF mov ecx, edi :0041FC74 E80F070000 call 00420388 :0041FC79 84C0 test al, al :0041FC7B 754A jne 0041FCC7 :0041FC7D 8BCF mov ecx, edi :0041FC7F E80B030000 call 0041FF8F :0041FC84 84C0 test al, al :0041FC86 743F je 0041FCC7 :0041FC88 68A9010000 push 000001A9 :0041FC8D E8A2040000 call 00420134 :0041FC92 59 pop ecx :0041FC93 50 push eax :0041FC94 E8074B0100 call 004347A0 :0041FC99 59 pop ecx :0041FC9A 8BF0 mov esi, eax :0041FC9C 8BCF mov ecx, edi :0041FC9E E8B9030000 call 0042005C <--Процедура генерации CRC :0041FCA3 663BC6 cmp ax, si <--в ax - сгенерированный CRC, а в si - правильный (0x8560) :0041FCA6 7426 je 0041FCCE <--если совпадают - прыгаем через сообщение о неверном CRCЗдесь необходимо заменить je 0041FCCE на jmp 0041FCCE, чтобы программа всегда думала, что CRC правильный. Я сделал все необходимые изменения, но при запуске программы главное окно появилось и тут же закрылось, получив всего 5 сообщений (WM_???). Так, Так, Так! Значит программа где-то ещё делает проверку! Я в этом месте немного помучался, но потом пошел старым и проверенным способом: запустил программу в SymbolLoader'e и начиная с адреса 0041FC4E прошёлся по НЕ изменнённой программе отладчиком, записав все условные переходы. Затем то же самое проделал с изменённой программой, сравнивая условные переходы с неизменённой программой. И очень скоро по адресу 0041FE9B я натолкнулся на расхождение:
:0041FE9B 750B jne 0041FEA8 <--кракнутая программа здесь прыгала, в то время как НЕ кракнутая - НЕ прыгала :0041FE9D 8B45FC mov eax, dword ptr [ebp-04] :0041FEA0 3B05E0314400 cmp eax, dword ptr [004431E0] :0041FEA6 7412 je 0041FEBA <--кракнутая - не прыгает, не кракнутая - прыгает!Ура! Я нашёл вторую проверку на некракнутость! Теперь я просто заменил jne 0041FEA8 на jne 0041FE9D, а je 0041FEBA на jmp 0041FEBA и........Все заработало! Ура! Баннера больше нет! И ни на что программа не жалуется!!! ВсЁ! А для самых маленьких крак:
----------Начало крака---------------- Shtirlitz v4.0 Cracker by vallkor (vallkor@chat.ru) Shtirl~1.exe 00000C32: 0E 00 0001F0A6: 74 EB 0001F29C: 0B 00 0001F2A6: 74 EB ---------- Конец Крака ----------------Исследовал и наваял туториал: vallkor//PTDS E-mail: vallkor@chat.ru Page : http://vallkor.chat.ru
Как правильно завершить некое приложение.
Автор: Nomadic.
Если не принудительно, то можно послать на его Instance сообщение WM_QUIT. Если же необходимо принудительно терминировать приложение, то смотрите ниже - под Windows NT процесс можно терминировать через специально предназначенный для этого хэндл. Иначе гарантии нет. Предположим, что процесс создаем мы, ожидая его завершения в течение maxworktime. Тогда -
var
dwResult: Longint; // This example was converted from C source.
begin
// Not tested. Some 'nil' assignments must be applied
// as zero assignments in Pascal. Some vars need to
// be declared (maxworktime, si, pi). AA.
if CreateProcess(nil, CmdStr, nil, nil, FALSE,
CREATE_NEW_CONSOLE, nil, nil, si, pi) then
begin
CloseHandle(pi.hThread);
dwResult := WaitForSingleObject(pi.hProcess, maxworktime * 1000 * 60);
CloseHandle(pi.hProcess);
if dwResult <> WAIT_OBJECT_0 then
begin
pi.hProcess := OpenProcess(PROCESS_TERMINATE, FALSE, pi.dwProcessId);
if pi.hProcess <> nil then
begin
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
end;
end;
end;
end;
|
Как правильно завершить некое приложение 2.
| Автор: Панферов Андрей |
| Если бы колбаса продавалась так же как программное обеспечение, то под прозрачной оберткой у нее было бы следующее соглашение: 1) изготовитель не гарантирует совместимость данного продукта с организмом и не несет никаких последствий за его употребление; 2) потребителю запрещается исследовать содержимое этой колбасы (например, на наличие крысиных хвостов); 3) покупатель может сьесть колбасу, но все равно не станет ее владельцем; 4) употребить колбасу может только один человек; 5) покупатель не может продать колбасу кому-либо; 6)производитель не гарантирует, что данный продукт свободен от ошибок (например, от примесей цианистого калия); 7) возможная ответственность за здоровье покупателя не может превысить стоимость колбасы; 8) разрывая обертку, покупатель принимает условия данного соглашения. |
var
Form1: TForm1;
// Глобальные переменные:
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
StartEn: Boolean = False;
implementation
{$R *.DFM}
// Запуск процесса
procedure TForm1.StartButtonClick(Sender: TObject);
var
CmdStr: PChar; // Командная строка для запуска приложения
begin
CmdStr := PChar(FilenameEdit1.FileName); // Определение командной строки
StartEn := False; // Запущено ли приложение
// Без этого приложение не стартует =<
FillChar(StartupInfo, Sizeof(StartupInfo), #0);
StartupInfo.cb := Sizeof(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_NORMAL; // Состояние окна запущенного приложения
// <= Без этого приложение не стартует
// Запускаем процесс
StartEn := CreateProcess(nil,
CmdStr, { указатель командной строки }
nil, { указатель на процесс атрибутов безопасности }
nil, { указатель на поток атрибутов безопасности }
False, { флаг родительского обработчика }
CREATE_NEW_CONSOLE or { флаг создания }
NORMAL_PRIORITY_CLASS,
nil, { указатель на новую среду процесса }
nil, { указатель на имя текущей директории }
StartupInfo, { указатель на STARTUPINFO }
ProcessInfo); { указатель на PROCESS_INF }
EndButton.Enabled := StartEn;
end;
// Терминация процесса
procedure TForm1.EndButtonClick(Sender: TObject);
begin
if StartEn then
begin
ProcessInfo.hProcess := OpenProcess(PROCESS_TERMINATE, False,
ProcessInfo.dwProcessId);
if ProcessInfo.hProcess <> Null then
begin
TerminateProcess(ProcessInfo.hProcess, 0);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
end;
StartEn := False;
EndButton.Enabled := StartEn;
end;
|
Как выключить компьютер с любой версией Windows.
| Сообщение перед выключением компьютера: - Вашу жену зовут Наташа, детей-Юля и Павлик. Вы все еще хотите выйти из Windows?! |
function GetWinVersion: string;
var
VersionInfo: TOSVersionInfo;
OSName: string;
begin
// устанавливаем размер записи
VersionInfo.dwOSVersionInfoSize := SizeOf( TOSVersionInfo );
if Windows.GetVersionEx( VersionInfo ) then
begin
with VersionInfo do
begin
case dwPlatformId of
VER_PLATFORM_WIN32s: OSName := 'Win32s';
VER_PLATFORM_WIN32_WINDOWS: OSName := 'Windows 95';
VER_PLATFORM_WIN32_NT: OSName := 'Windows NT';
end; // case dwPlatformId
Result := OSName + ' Version ' + IntToStr( dwMajorVersion ) + '.' + IntToStr( dwMinorVersion ) +
#13#10' (Build ' + IntToStr( dwBuildNumber ) + ': ' + szCSDVersion + ')';
end; // with VersionInfo
end // if GetVersionEx
else
Result := '';
end;
procedure ShutDown;
const
SE_SHUTDOWN_NAME = 'SeShutdownPrivilege'; // Borland forgot this declaration
var
hToken: THandle;
tkp: TTokenPrivileges;
tkpo: TTokenPrivileges;
zero: DWORD;
begin
if Pos('Windows NT', GetWinVersion) = 1 then // we've got to do a whole buch of things
begin
zero := 0;
if not OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
begin
MessageBox(0, 'Exit Error', 'OpenProcessToken() Failed', MB_OK);
Exit;
end; // if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
begin
MessageBox(0, 'Exit Error', 'OpenProcessToken() Failed', MB_OK);
Exit;
end; // if not OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)
// SE_SHUTDOWN_NAME
if not LookupPrivilegeValue( nil, 'SeShutdownPrivilege' , tkp.Privileges[0].Luid ) then
begin
MessageBox(0, 'Exit Error', 'LookupPrivilegeValue() Failed', MB_OK);
Exit;
end; // if not LookupPrivilegeValue( nil, 'SeShutdownPrivilege' , tkp.Privileges[0].Luid )
tkp.PrivilegeCount := 1;
tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, False, tkp, SizeOf( TTokenPrivileges ), tkpo, zero);
if Boolean(GetLastError()) then
begin
MessageBox(0, 'Exit Error', 'AdjustTokenPrivileges() Failed', MB_OK);
Exit;
end // if Boolean( GetLastError() )
else
ExitWindowsEx( EWX_FORCE or EWX_SHUTDOWN, 0 );
end // if OSVersion = 'Windows NT'
else
begin // just shut the machine down
ExitWindowsEx( EWX_FORCE or EWX_SHUTDOWN, 0 );
end; // else
end;
|
Содержание.
- Дублирование компонентов и их потомков во время выполнения приложения 2.
- Как создать базу данных Access во время выполнения.
- Создание события во время выполнения приложения.
- Ошибки времени выполнения.
- Функции редактора полей во время выполнения программы.
- RTTI в действии.
- Жизнь и смерть в режиме run-time.
- Менять главную форму во время выполнения.
- Имя свойства в течение выполнения программы.
- Заменить элемент управления во время выполнения.
- Назначение события во время выполнения программы.
- Пример реализации резиновой нити.
- Использование функций из RunDll32.
- Количество запущенных копий программы.
- Создание компонент во время выполнения программы и обработка их событий.
- Как перемещать компоненты во время работы программы.
- Показ свойств во время выполнения программы.
- Запуск программы.
- Как запустить апплет панели управления.
- Открыть меню кнопки ПУСК или запустить диалог запуска программ.
- Как запускать внешнюю программу сразу с высоким приоритетом.
- Запустить консольное приложение и получить его стандартный вывод.
- Как запустить текущий ScreenSaver.
- Как запустить текущий ScreenSaver 2.
- Показать диалог для запуска приложения.
- Показать диалог для запуска приложения 2.
- Как написать DLL, которую можно было-бы выполнить с помощью RunDll, RunDll32.
- Запустить на выполнение файл от имени любого пользователя.
- Запустить процесс в защищенной области другого пользователя.
- Как можно запустить lnk.
- Как заставить запускаться из определенной папки.
- Как запустить и остановить сервис (или получить его статус).
- Другой способ запуска чужого приложения.
- Просмотрщик запущенных процессов.
- Как узнать, запущен ли процесс в Win9x.
- Бегущая строка.
- Создание компонент в Run-Time.
- Как упаковать базу (DBase, Paradox) в RunTime.
- Как запустить приложение и подождать пока оно отработает 7.
- Как запустить приложение и подождать пока оно отработает 8.
- Запуск программы и ожидание завершения определенное время.
- Запуск программы и ожидание ее окончания, принудительное завершение если Timeout.
- Пример запуска макроса в MS Word.
- Русификация консольных приложений в Delphi.
- Выравнивание текста по ширине с автоматическим переносом русских слов.
- Вставка рисунка в TRxRichEdit.
- Сохранение типа списка объектов.
- Как определить, что два слова имеют схожее произношение.
- O сохранении иконок 32хЗ2 в 256-цветном формате.
- Сохранение и выдёргивание ресурсов в DLL или EXE.
- Запись массива на диск.
- Сохранение массива c изображениями.
- Сохранить содержимое буфера обмена или потока как картинку.
- Сохранение компонента в файле.
- Сохранение состояния ВСЕХ компонентов.
- Сохранение компонента полностью со всеми свойствами в БД.
- Сохранение данных в Clipboard.
- Сохранить порядок колонок в DBGrid, после того как пользовать их перестроил.
- Сохранить Excel файл как текстовый.
- Сохранение размеров, позиции и состояния окна.
- Сохранение в файле и загрузка из файла формы с компонентами.
- Сохранить изображение в формате JPEG.
- Сохранение и загрузка данных в объекты на примере коллекций.
- Сохранение и чтение TStringGrid.
- При чтении почты POP3 отделять Attachment и сохранять в файл.
- Сохранить несколько контролов в один файл.
- Как хранить настройки для программы. Работа с INI-файлами.
- Сохранять настройки в ini-файле.
- Сохранение прикреплений Outlook.
- Как сохранить значение свойства в поток.
- Как сохранить значение свойства в поток 2.
- Сохранение точных размеров при печати.
- Сохранение пользовательских настроек.
- Как сохранить исходник HTML из TWebBrowser.Document на диск.
- Сохранение и восстановление положения и размеров окон.
- Сохранить документ Word как RTF.
- Изменить шрифт TStatusBar.
- Из ресурсов поочередно загружать глифы для кнопок SpeedButton.
- Прокрутка TScrollBox с помощью стрелок вверх и вниз.
- Масштабирование окна.
- Масштабирование окна 2.
- Масштабирование размера формы и размера шрифтов.
- Технология без интересного имени или как работать со сканером.
- Сканирование версии структуры базы данных.
- Рекурсивный проход дерева каталогов.
- Как получить список файлов со всеми подкаталогами.
- Сканируем файл в поисках текста.
- Сканирование shared-ресурсов.
- Как получить Регистрационный Номер для Sceleton ScreenSaver.
- Выключение компьютера в заданное время.
- ScreenMate.
- Создание хранителя экрана (Screen Saver).
- Снимок экрана.
- Как скопировать экран (или его часть) в TBitmap.
- Как сохранить содержимое экрана в файл.
- Как копировать образ экрана в файл.
- Экранный вирус.
- Прокрутка Memo.
- Как прокрутить TRichEdit в конец.
- Прокрутка таблицы - хитрость PeekMessage.
- Как в Memo прокрутить текст.
- Прокручивать TWebBrowser своими кнопками.
- Читаем CSV текстовый файл в StringGrid.
- Алгоритмы поиска.
- Поиск и фильтрация данных в Delphi.
- Поиск звуковой платы.
- Процедура поиска закладок и перехода по ним.
- Произвести поиск байта или слова в строке.
- Поиск класса.
- Поиск DOS-окна.
- Поиск файлов.
- Как запустить диалог поиска файла.
- Поиск файлов по маске в заданной директории.
- Поиск файлов по маске по всему дереву каталогов, начиная с указанного.
- Поиск в базе данных ADO.
- Поиск записи в больших таблицах.
- Поиск в базе данных.
- Поиск в DBGride без ввода искомого значения в поле ввода.
- Простейший сканер диска.
- Поиск кратчайшего пути.
- Поиск пути.
- Поиск в отдельном потоке фразы в файлах.
- Поиск свойств.
- Поиск строки текста в наследниках TCustomEdit.
- Алгоритмы поиска в тексте.
- Поиск текста в текстовом файле.
- Как найти директорию TEMP в Windows.
- Как найти каталог Windows.
- Как найти каталог Windows 2.
- Как найти каталог Windows 3.
- Перевести секунды в формат времени.
- Преобразование количества секунд в формат TTIME.
- Получение второго цвета заголовков форм.
- Сворачивание вторичной формы на панель задач.
- Определить, установлен ли Macromedia Flash.
- Как захватить весь вывод в консоли.
- Как скопировать выбранные в DBGrid записи в клипборд.
- Как выделить кнопку в DBNavigator программно.
- Каким образом выбирать размер шрифта.
- Как выделить в RichEdit 4 строки.
- Выделить строку в Memo.
- Выделение меню.
- Как реализовать выделение резиновым прямоугольником.
- Что из себя представляет Self.
- Что из себя представляет Self 2.
- Создание самоуничтожающегося объекта.
- Диалог для выбора компьютера.
- Показать диалог выбора директории с кнопкой для создания новой.
- Почти полный аналог метода SendKeys из VB.
- Как отправить нажатие клавиши с кодом 255 в элемент управления Windows.
- Отправить письмо на E-mail так, чтобы пользователь не подозревал об отправке.
- Как отправить бинарные данные из CGI приложения.
- Как послать широковещательный UDP пакет.
- Переслать командную строку из второго экземпляра программы в первый.
- Пересылка данных в ячейки Excel.
- Отправить E-mail.
- Отправлять электронную почту.
- Отправить письмо, чтобы пользователь об этом не знал (D7).
- Отправка E-mail через MAPI.
- Передать файл через Socket соединение.
- Переслать файл в Корзину.
- Как передать фокус следующему контролу.
- Отправить сообщение на ICQ.
- Посылка кода клавиши или текста в окно.
- Как посылать нажатие клавиш в элемент управления.
- Послать нажатие клавиш.
- Эмуляция нажатия клавиши для любого активного приложения.
- Посылаем нажатия клавиш другому приложению.
- Переслать сообщение с помощью SendMessage.
- Отправление сообщения сразу всем элементам управления формы.
- Посылка Raw IP-пакетов.
- Передать строки, картинки (streams) между процессами.
- Как передать строку другому приложению.
- Передача спецсимволов в документ Word.
- Переслать текст в другую программу.
- Как отправить на принтер чистый поток данных.
- Как передать UserName и Password в удаленный модуль данных.
- Как отправить вебформу на сервер при помощи TClientSocket (напрямую и через прокси).
- Как разделить обработку OnClick и OnDblClick.
- Как разделить файл на куски и склеить их потом.
- Дилемма доступа к данным.
- Показать число с разделителями тысячных знаков.
- Регистрация основанная на серийных ключах.
- Читать и писать в последовательный порт.
- Инсталляция и удаление сервисов под НТ.
- Как заказать сервисный процесс.
- SetFocus в TStringGrid.
- SetFocus в Edit на OnExit.
- Правила для SetRange.
- Пример SetText, GetText строкового поля.
- Установка привилегий.
- SetVolume почему-то не дает никакого эффекта.
- Выставить значение для определенного свойства всех компонент формы или всех компонент нужного класса.
- Установка режима бинарного файла.
- Установка определенного бита в слове.
- Установка времени компиляции программы в диалоговом окне О программе.
- Установить флаг DTR и RTS в активное состояние для определённого COM-порта.
- Как программно установить конфигурацию COM-порта.
- Установка или снятие Debug привелегии у текущего процесса.
- Вывести список установленных принтеров и установить принтер по умолчанию.
- Как програмным путем задавать координаты ярлыкам на рабочем столе.
- Изменение даты создания каталога.
- Как установить переменные окружения.
- Как установить значение строкового или целого поля если оно присутствует.
- Устанавливаем дату создания файла.
- Выставить дату для файла.
- Установка времени и даты файла.
- Установка ловушек в Windows.
- Как присвоить значение свойству Selected в ListBox.
- Включить или отключить Caps, Num или Scroll Lock.
- Как перевести монитор в режим stand-by.
- В TRichEdit или TMemo при нажатии Enter позиционирование курсора в позицию как в предыдущей строке.
- Получить или установить имя компьютера.
- Как установить количество точек на дюйм в TPrinter.
- Публикование свойств-наборов.
- Выставить значения свойств.
- Как установить значение свойства компонента по имени.
- Как указать максимальный размер текста для RichEdit Control.
- Как установить системную дату и время программно.
- Выставить системные переменные среды.
- Установка шрифтов для системного меню.
- Назначение прав пользователей на таблицу.
- Переместить курсор в TRichEdit на нужную позицию.
- Выставление и сброс битов.
- Установить метку тома файловой системы.
- Перевернуть текст в TStringGrid на 90 градусов.
- Добавление колонки в StringGrid.
- Добавление колонки в StringGrid 2.
- Получить содержимое TStringGrid или TDrawGrid в виде строки.
- Автоматический подбор ширины колонок для TStringGrid.
- Автоматический подбор ширины колонок для TStringGrid 2.
- Выравнивание ячеек в TStringGrid.
- Очистить ячейки в TStringGrid.
- Очистить ячейки в TStringGrid 2.
- Автоматический подбор ширины колонок для TStringGrid или TDrawGrid.
- Реализация события OnColumnClick из TListView для TStringGrid.
- Обратиться к колонке или строке StringGrid по заголовку.
- Сортировать TStringGrid по колонке.
- Копировать и вставлять ячейки TStringGrid в буфер обмена.
- Удаление колонки в StringGrid.
- Удаление колонки в StringGrid 2.
- Удаление колонки в TStringGrid.
- Удалить строку из TStringGrid.
- Переход по ячейкам в TStringGrid по клавиши Enter как по Tab.
- Вставка и удаление строк из TStringGrid.
- Определить, отображаются ли полосы прокрутки для TStringGrid.
- Многострочный TStringGrid 4.
- Увеличение ячейки TStringGrid при увеличении числа строк.
- Как перемещать строки и колонки в StringGrid.
- Как перемещать строки и колонки в StringGrid 2.
- Многострочный TStringGrid 3.
- Распечатать TStringGrid 2.
- Изменение размеров колонок в StringGrid.
- Обмен строк TStringGrid.
- Удаление строки из TStringGrid.
- Вставка строки в TStringGrid.
- Перестановка строки в TStringGrid в другую позицию.
- Сохранить TStringGrid в файл, загрузить из файла.
- Позиционирование каретки в TStringGrid.
- Сортировка столбцов в StringGrid.
- Сортировка столбцов в StringGrid 2.
- Сортировка TStringGrid.
- Копирование содержимого TStringGrid в буфер обмена.
- Экспортировать TStringGrid в Excel-файл.
- Экспортировать TStringGrid в Excel-файл 2.
- Экспортировать TStringGrid в Excel-файл 3.
- Перевести TStringGrid в HTML.
- Как экспортировать StringGrid в MS Word таблицу.
- Затенить компонент.
- Как получить эффект тени для Hint.
- Перетасовка экрана.
- Перетасовка строк в списке.
- Разделяемые элементы управления для Tabbednotebook.
- Разделяемые элементы управления для Tabbednotebook 2.
- Разделяемые элементы управления для Tabbednotebook 3.
- Защита программ перекрытием кода.
- Ошибка совместного доступа к базе данных.
- Как расшарить диск.
- Расшаренная DLL.
- Общий доступ к памяти, распределенной DLL.
- Как использовать Shell API SHBrowseForFolder.
- Как использовать Shell API SHBrowseForFolder 2.
- Запуск внешней программы. Как послать E-mail и сделать ссылку на сайт.
- Сортировка Шелла.
- Shell своими руками - System Tray.
- Клавиши-акселераторы для TPageControl.
- Как сделать клавишу-акселератор (keyboard shortcut) компонету, у которого нет заголовка.
- Чтобы клавиши shortcut пунктов главного меню главной формы действовали только в этой форме (но не в модальных окнах).
- Сокращенное имя каталога.
- Как использовать функцию ShowMessageFmt.
- Отобразить, присоединить сетевые диски.
- Как вывести окно модальное для всех окон кроме одного.
- Считать двоичный файл и отобразить значение байт как ASCII.
- Просмотр буфера обмена.
- Просмотр буфера обмена 2.
- Открытие сокращённого или полного диалога выбора цвета.
- Отображение ломанной линии.
- Вывод пояснения о текущей операции.
- Показ удаленных записей в таблице dBASE.
- Показать удаленные записи таблицы.
- Как заставить приложение показывать различные иконки при различных разрешениях дисплея.
- Как вывести сведения о диске (метка тома, серийный номер, файловая система и т.д.).
- Как программно вывести окно свойств экрана.
- Показать диалог выбора домена.
- Отображение отформатированных сообщений об ошибках.
- Отображение отформатированных сообщений об ошибках 2.
- Показать файлы с ассоциированными с ними иконками в TListView.
- Показать свойства файла.
- Как показать иконку, ассоциированную с данным типом файла.
- Показать первый кадр AVI-файла.
- Просмотреть текст формы из запущенной программы.
- Показ формы без фокуса.
- Отображение полных строк списка при перемещении мыши по списку.
- Как вывести главное окно справочной системы.
- Вывод справочной информации.
- Как скрыть или показать иконки на рабочем столе.
- Как скрыть или показать иконки на рабочем столе 2.
- Показать или спрятать окно.
- Показ и скрытие дочерних MDI-окон.
- Спрятать или показать кнопку ПУСК.
- Как программно спрятать или показать заголовок окна TitleBar.
- Заставляем появиться Hint в нужный момент.
- Как вывести hint, когда курсор мышки над определённым контролом.
- Показ изображений в DBGrid.
- Подскажите как правильно показать на экране и сохранить в базе картинку формата JPEG.
- Вывести информацию о пакете.
- Показывать номер страницы.
- Как показать окно свойств экрана.
- Показать диалог выбора компьютера.
- Как показать число, разделив каждые три цифры запятыми.
- Показ логотипа при запуске приложения.
- Показ логотипа при запуске приложения 2.
- Показ логотипа при запуске приложения 3.
- Существует ли средство для вывода определения структуры таблицы.
- Просмотреть текст формы.
- Показ окна без главной формы.
- Показать корзину Windows.
- Исследование Текстового перекодировщика Штирлиц версия 4.0.
- Как правильно завершить некое приложение.
- Как правильно завершить некое приложение 2.
- Как выключить компьютер с любой версией Windows.