Первым делом разберёмся с двумя приёмами программирования, которые мы будем использовать и в последующих статьях данного цикла:
1) Как работать с "горячими" клавишами?
2) Как добавить свой значок на Системную Панель (SysTray, можно узнать по характерным для неё часикам)?
На первый вопрос я уже дал частичный ответ в статье Сделай сам: "Вскрывалка паролей", но нам нужно копнуть немного глубже, а второй вопрос мы ещё вообще не затрагивали.
Работа с "горячими" клавишами
Для работы с "горячими" клавишами вполне достаточно использования лишь двух функций:
1) Регистрирует "горячие" клавиши:
BOOL RegisterHotKey(
HWND hWnd, // этому окну придёт уведомление о нажатии комбинации клавиш
int id, // идентификатор "горячих" клавиш
UINT fsModifiers, // должны ли быть нажаты клавиши Ctrl, Shift, Alt или Win
UINT vk // код клавиши, на которую мы будем реагировать
);
2) Удаляет "горячие" клавиши
BOOL UnregisterHotKey(
HWND hWnd, // окно, ассоциированное с "горячими" клавишами
int id // идентификатор "горячих" клавиш
);
Всё бы ничего, да вот при работе с "горячими" клавишами из DLL (динамически подключаемой библиотеки) могут возникнуть проблемы с идентификаторами. Чтобы этого избежать следует использовать функцию GlobalAddAtom, которая возвратит нам уникальный идентификатор (атом) требуемого формата. После завершения работы с "горячими" клавишами атом нужно удалить при помощи функции GlobalDeleteAtom. Работу с атомами мы рассмотрим в одной из следующих статей, где будем использовать DLL.
Помещение своей иконки в SysTray
Бывают такие программы, которые постоянно должны быть активны в системе. Они довольно часто помещают свой значок на Системную панель, чтобы не надоедать пользователю присутствием своего окна и в то же время оставить возможность "достучаться" до программы. Для управления иконкой на Системной панели используется функция
Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL;
dwMessage – сообщение, которое мы посылаем Панели задач, может принимать следующие значения: NIM_ADD – добавить новую иконку, NIM_DELETE – удалить иконку, NIM_MODIFY – изменить иконку.
lpData – указатель на структуру NotifyIconData, которая состоит из следующих полей:
cbSize: DWORD;
Wnd: HWND;
uID: UINT;
uFlags: UINT;
uCallbackMessage: UINT;
hIcon: HICON;
szTip: array [0..63] of AnsiChar;
Пояснения:
cbSize – размер структуры;
Wnd – идентификатор окна, которое будет получать сообщения, ассоциированные с иконкой;
uID – идентификатор иконки, который мы сами ей назначаем;
uFlags – комбинация из трёх флагов, которая обозначает, какие поля структуры мы хотим заполнить: NIF_ICON (хотим заполнить поле hIcon), NIF_MESSAGE (поле uCallbackMessage), NIF_TIP (поле szTip).
uCallbackMessage – определяемый нами идентификатор сообщения, ассоциированного с иконкой;
hIcon – идентификатор иконки;
szTip – текст подсказки к иконке, которая появляется при наведении на неё курсора мышки.
Пример использования функции Shell_NotifyIcon вы найдёте в коде нашей сегодняшней программы.
Настала пора приступить к созданию программы. Она будет выполнять две полезные функции:
1) Закрытие всех окон IE;
2) Переход "Вперёд\Назад" в коллекции некоторых объектов. Всё дело в том, что часто при просмотре какого-либо форума или галереи картинок, ссылки имеют вид "http://anysite/forum.php?page=2". Т.е мы находимся на второй странице форума. Если мы заменим цифру на '1', то попадём на предыдущую страницу, на '3' – на следующую. Примерно по такому принципу мы организуем Переход "Вперёд\Назад".
Закрытие всех окон IE
Предлагаю два способа реализации функции закрытия всех окон IE.
Способ I
Последовательно ищем окна Internet Explorer'a и закрываем их:
procedure CloseAllIE_1;
var
ie: HWND;
begin
//ищем окно IE
ie := FindWindow('IEFrame', nil);
//пока найдено окно IE…
while (ie<>0) do
begin
//…закрываем его
postmessage(ie, WM_CLOSE, 0, 0);
//ищем следующее
ie := FindWindow('IEFrame', nil);
end;
end;
Способ II
Перебираем все родительские окна в системе и, если увидели окно IE, то закрываем его. Таким образом, мы совершаем только один проход по окнам, а значит, работает этот способ намного быстрее предыдущего.
procedure CloseAllIE_2;
//эта функция будет применяться к каждому окну
functionCloseIE (Wnd: HWND): boolean; stdcall;
var
winclass: array [0..255] of char;
begin
//получаем класс окна
GetClassName (Wnd, WinClass, sizeof(WinClass));
//если у нас окно IE, то закрываем его
if (WinClass='IEFrame') then
PostMessage (Wnd, WM_CLOSE, 0, 0);
//принимаем следующее окно
CloseIE := true;
end;
begin
//перебираем все родительские окна, указывая,
//какой функцией будем их обрабатывать
EnumWindows(@CloseIE, 0);
end;
Переход "Вперёд\Назад"
Разделим нашу задачу на три составляющие:
1) Получить адрес (URL) текущей странички IE;
2) Увеличить (уменьшить) последнее число адреса на единицу;
3) Записать изменённый адрес в окно IE и заставить браузер перейти по нему.
Пункт 1: получить адрес текущей странички
Окно IE, как и любое порядочное окно, содержит множество дочерних окон (всякие там кнопочки, поля ввода, надписи и т.д.). В одном из таких окон содержится URL странички. После недолгого изучения иерархии окон в IE при помощи специальной программы, добраться до адреса совсем несложно. Следующая функция возвращает идентификатор поля ввода (класс 'Edit'), в котором содержится URL:
function FindEdit(var h:hwnd):boolean;
var
wclass: array[0..255] of char;
begin
//получаем идентификатор и класс "верхнего" окна
h := getforegroundwindow;
getclassname(h, wclass, sizeof(wclass));
//если это окно IE, то "распутываем" иерархию дочерних окон
if (wclass='IEFrame') then
begin
h := findwindowex(h, 0, 'WorkerA', nil);
h := findwindowex(h, 0, 'ReBarWindow32', nil);
h := findwindowex(h, 0, 'ComboBoxEx32', nil);
h := findwindowex(h, 0, 'ComboBox', nil);
h := findwindowex(h, 0, 'Edit', nil);
result := true;
end else
result := false;
end;
После того, как мы добрались до поля ввода, в котором хранится адрес странички, извлечём его при помощи следующей функции:
function GetText(WindowHandle: hwnd): String;
var
txtLength: Integer;
buffer: String;
begin
//Узнаём длину текста
TxtLength := SendMessage(WindowHandle, WM_GETTEXTLENGTH, 0, 0);
if (txtlength>0) then
begin
txtlength := txtlength + 1;
setlength (buffer, txtlength);
//записываем текст окна в buffer
sendmessage(WindowHandle, wm_gettext, txtlength, longint(@buffer[1]));
result := buffer;
end else result := '';
end;
Таким образом, чтобы узнать текущий адрес в IE, достаточно применить эти две функции:
…
FindEdit(h);
Adress := GetText(h);
…
Пункт 2: Увеличить (уменьшить) последнее число адреса на единицу
Здесь нам потребуется только умение оперировать со строками и знание основ арифметики.
//увеличение последнего числа в строке s
function IncURL(s:string): String;
const
num = ['0'..'9'];
var
i, j, cnt: word;
plus, ch: byte;
code: integer;
s1: string;
begin
i := length(s);
plus := 0;
//ищем первую цифру с конца
while (i>0) and not(s[i] in num) do
dec(i);
//если нашли…
if (i<>0) then
begin
//считаем количество девяток, которыми кончается число
cnt := 0;
while (i>0) and (s[i]='9') do
begin
dec(i);
inc(cnt);
end;
if (i=0) or not( s[i] in num) then
begin
ch := 0;
{если число сплошь состоит из девяток, то мы должны первой цифрой сделать '1' и заменить все девятки на нули}
if not(s[i] in num) then
plus := 1;
end else
val(s[i], ch, code);
inc(ch);
str(ch, s1);
if (cnt>0) then
//заменяем последние девятки на нули
for j := 1 to cnt do
s1 := s1 + '0';
//составляем итоговую строку
s := copy(s, 1, i-1+plus)+s1+copy(s, i+cnt+1, length(s)-i-cnt);
end;
result := s;
end;
//уменьшение последнего числа в строке s
function DecURL(s:string):string;
const
num = ['0'..'9'];
var
i, j, cnt: word;
minus, ch: byte;
code: integer;
s1: string;
begin
i := length(s);
minus := 0;
//ищем последнюю цифру в строке
while (i>0) and not (s[i] in num) do
dec(i);
if (i<>0) then
begin
cnt := 0;
//считаем количество нулей
while (i>0) and(s[i]='0') do
begin
dec(i);
inc(cnt);
end;
if (i>0) and (s[i] in num) then
begin
val(s[i],ch,code);
dec(ch);
if (ch=0) and (i>0) and not(s[i-1] in num) then
s1 := ''
else
str(ch, s1);
if (cnt>0) then
//заменяем последние нули на девятки
for j := 1 to cnt do
s1 := s1 + '9';
//составляем итоговый URL
s := copy(s, 1, i-1-minus) + s1 + copy(s, i+cnt+1, length(s)-i-cnt);
end;
end;
result := s;
end;
Пункт 3: записать изменённый адрес в окно IE и заставить браузер перейти по нему.
Для выполнения третьей части задачи нужно просто послать полю ввода два сообщения:
…
//"вводим" изменённый адрес
SendMessage(h, WM_SETTEXT, 0, longint(Pchar(s)));
//"нажимаем" клавишу "Ввод" (Enter)
SendMessage(h, WM_KEYDOWN, VK_RETURN, 0);
…
Теперь соберём эти три пункта в нужные нам процедуры:
//переход "Вперёд"
procedure NextPage;
var
s: String;
h: HWnd;
begin
if FindEdit(h) then
begin
//получаем URL
s := gettext(h);
delete(s, length(s), 1);
//увеличиваем последнее число на единицу
s := IncURL(s);
//записываем новый URL
SendMessage(h, WM_SETTEXT, 0, longint(Pchar(s)));
//нажимаем клавишу "Ввод"
SendMessage(h, WM_KEYDOWN, VK_RETURN, 0);
end;
end;
//переход "Вперёд"
procedure PrevPage;
var
s: String;
h: HWnd;
begin
if FindEdit(h) then
begin
s := string(gettext(h));
delete(s, length(s), 1);
s := DecURL(s);
SendMessage(h, WM_SETTEXT, 0, longint(Pchar(s)));
SendMessage(h, WM_KEYDOWN, VK_RETURN, 0);
end;
end;
Все основные процедуры нашей программы готовы, теперь настало время сделать для них оболочку. Создайте новый проект и поместите на форму один-единственный компонент – PopupMenu1:TPopupMenu1. В нём должен быть один пункт – "Выход". Процедура при выборе этого пункта:
procedure TForm1.ExitClick(Sender: TObject);
begin
//завершаем работу программы
halt;
end;
Добавьте к списку подключаемых модулей (раздел Uses) модуль ShellApi. Объявите константу
const
WM_ICONTRAY = wm_user + 1;
Этим мы определили сообщение, которое будем получать от иконки в SysTray.
В раздел Private добавьте три строчки:
//информация о нашей иконке
TrayIconData: TNotifyIconData;
//эта процедура будет обрабатывать поступающие от иконки сообщения
procedure TrayMessage (var Msg: TMessage); message WM_ICONTRAY;
//процедура обработки нажатия "горячих" клавиш
procedure hotykey(var msg:TWMHotkey); message WM_HOTKEY;
Запишите процедуру для события OnCreate формы:
procedure TForm1.FormCreate(Sender: TObject);
begin
//заполняем информацию об иконке
with TrayIconData do
begin
cbSize := SizeOf(TrayIconData);
Wnd := Handle;
uID := 0;
uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
uCallbackMessage := WM_ICONTRAY;
hIcon := application.icon.handle;
szTip := 'My TrayIcon!';
end;
//добавляем иконку в SysTray
Shell_NotifyIcon(NIM_ADD, @TrayIconData);
//регистрируем "горячие" клавиши Alt+P
RegisterHotKey(handle, 1,.MOD_Alt, 80);
//регистрируем "горячие" клавиши Alt+Q
RegisterHotKey(handle, 2,.MOD_Alt, 81);
//регистрируем "горячие" клавиши Alt+R
RegisterHotKey(handle, 3,.MOD_Alt, 82);
end;
Теперь процедура при уничтожении формы:
procedure TForm1.FormDestroy(Sender: TObject);
begin
//удаляем иконку
ShellApi.Shell_NotifyIcon(NIM_DELETE, @TrayIconData);
//удаляем "горячие" клавиши
UnRegisterHotKey(handle, 1);
UnRegisterHotKey(handle, 2);
UnRegisterHotKey(handle, 3);
end;
//обработка сообщений от иконки
procedure TForm1.TrayMessage(var Msg: TMessage);
var
p: TPoint;
begin
case Msg.lParam of
//если нажата правая кнопка мыши,
WM_RBUTTONDown, WM_RBUTTONDBLCLK:
begin
SetForegroundWindow(handle);
GetCursorPos(p);
//то показываем наше меню в текущей позиции курсора
popupmenu1.Popup(p.x,p.y);
end;
end;
end;
//нажатие на "горячие" клавиши
procedure TForm1.hotykey(var msg:TWMHotkey);
var
n: Integer;
begin
n := msg.HotKey;
//если нажали Alt+P, то закрываем все окна IE
if (n=1) then CloseAllIE_2;
else
//если нажали Alt+Q, то переход "Вперёд"
if (n=2) then NextPage
else
//если нажали Alt+R, то переход "Назад"
if (n=3) then PrevPage;
end;
На этом создание программы, которая расширяет функциональность браузера Internet Explorer, завершено. Мы научились:
1) работать с "горячими" клавишами;
2) помещать иконку на Системную панель;
3) работать с сообщениями и окнами.
На сегодня всё.
Иван Ширко
ishyrko@gmail.com
Читать дальше >>