Я очень рад, что кодинг вызвала такие бурные эмоции у наших читателей. Благодаря тебе, мне пришлось в прошлом номере отойти от плана, за что получил от главреда пару ударов под дышло, лишился средней почки, получил перелом правого желудочка и искривление прямой кишки :). Поэтому я сегодня исправляюсь и перевыполняю план за прошлый и за этот месяц.
Очень многие считают, что визуальность очень плохо влияет на результирующий код. Я ничего не могу сказать против. Очень часто оно так и есть, но не в случае с Delphi. Сегодня мы напишем прогу, которую нельзя увидеть по Ctrl+Alt+Del, воспользуемся встроенным ассемблером, обратимся к WinAPI, ну и как всегда поприкалываемся. А самое главное, что скомпилированный файл будет занимать всего 8192 байта!!! Не веришь? Да, все защитники языков низкого уровня утверждают, что в Delphi это невозможно. Сегодня тебе предстоит убедиться в обратном.
Сейчас я расскажу о том, как написать прогу меняющую заголовки всех окон на надпись "Я за тобой наблюдаю". На рисунке 1 показано окно Delphi под влиянием нашего сегодняшнего примера.
Рисунок 1. Delphi после запуска нашей проги
Сегодняшняя прога будет использовать Win API (Windows Application Program Interface) - интерфейс прикладных программ для кодера. Попросту говоря, это функции и константы которыми оперирует сам Windows. Рядовой Delphi-кодер всего этого может и не знать, так как у нас есть VCL (честь ему и хвала). VCL - это надстройка над WinAPI. Можно сказать больше - это инструменты, которые позволяют упростить кодинг и скрыть все сложности этого нелегкого труда.
В С++ тоже есть нечто подобное - MFC. Так что эта часть населения не сильно обделена, хотя MFC более сложная, абсолютно не визуальная, и выигрыш от ее использования в 10 раз меньше, чем от VCL.
Надстройка - это хорошо, но она дает удобный доступ только к части возможностей Windows. Эта часть наиболее часто используемая при кодинге. И все же, иногда приходится использовать то, что в VCL недоступно. В этих случаях нужно прямое обращения к API функциям Windows.
НА СТАРТ!!!:
Запусти Delphi. Если он у тебя уже запущен, то создай новый проект. Как всегда перед тобой появится пустая форма. Она нам сегодня не понадобится, поэтому удалим ее. Для этого из меню "Project" выбери пункт меню "Remove from Project...". Перед тобой появится окно, в котором ты должен выделить имя формы и нажать "ОК". Тебя попросят подтвердить удаление, на что нажми "ОК".
Рисунок 2. Удаление из проекта главной формы
Теперь нужно войти в исходник самого проекта. Для этого выбери из меню "Project" пункт "View Source". Здесь можно удалять все, кроме строки:
program Project1;
Напиши там все, что написано в листинге 1. Все, наша прога готова. Теперь можно создать запускной файл (нажми Ctrl+F9 или выбери из меню "Project" пункт "Compile") или даже запустить прогу.
Рисунок 3. Меню Project
Я НЕ ТОРМО, Я МЕДЛЕННЫЙ ГАЗ!!! :
Для того, чтобы ты не выглядел тупорылой коровой, давай разберем по клавишам сегодняшний пример. Желательно знать, что ты делаешь, а не безмозгло повторять за другими. Тем более что тут много всего интересного.
Ты уже знаешь, что после ключевого слова uses пишут подключаемые модули. У нас их всего два: windows и messages. В этих двух модулях идет описание основных WinAPI функций и сообщений. Из этих модулей Delphi узнает о существовании WinAPI и как с ним работать.
Далее идет описание функции registerserviceprocess, которая позволяет прятать прогу от магических клавиш Ctrl+Alt+Del:
procedure registerserviceprocess; external 'kernel32.dll' name 'RegisterServiceProcess';
Подобным образом мы уже описывали в прошлый раз функцию из созданной нами библиотеки DLL. Здесь я показываю, что в библиотеке kernel32.dll (это ядро Windows) есть функция registerserviceprocess.
Дальше идет функция EnumWindowsProc. О ней мы поговорим немного позже, а сейчас перейду на начало программы.
ВСТРОЕННЫЙ АССЕМБЛЕР :
В Delphi есть встроенный ассемблер. Ты можешь прямо среди кода на паскале писать код на ассемблере. Вот я так и делаю.
С помощью директивы asm я открываю блок кода на ассемблере. Как только ассемблер мне уже не нужен, я ставлю end и снова могу писать на паскале. Я написал на этом чудо языке три строчки кода. Первые две заносят в стек числа 1 и 0 (это параметры функции registerserviceprocess). После этого я вызываю саму функцию с помощью call registerserviceprocess. То же самое можно было бы сделать и с помощью вызова на паскале registerserviceprocess(0, 1), но я захотел показать тебе, как работает встроенный ассемблер.
ЛОВЛЯ НА ЖИВЦА :
Все программа спрятана, теперь пора переходить к главным обязанностям - поменять заголовки всех окон. Для этого я запускаю цикл:
while условие do
begin
end
Рисунок 4. Код
Цикл while ..do означает - выполнять, пока не будет выполнено условие. У меня в качестве условия указано "true". Это значит, что цикл будет выполняться бесконечно.
Внутри цикла я вызываю функцию- EnumWindows (перечислить окна). В качестве единственного параметра ей нужно передать адрес другой функции, которая будет вызываться каждый раз, когда найдено какое-нибудь запущенное окно. Для этого у меня служит функция EnumWindowsProc. Так что каждый раз, когда EnumWindows найдет окно, будет выполняться код, написанный в EnumWindowsProc . Этот код выглядит вот так:
function EnumWindowsProc(h: hwnd): BOOL; stdcall;
begin
SendMessage(h,WM_SETTEXT,0,
lparam(PChar('Я за тобой наблюдаю...')));
end;
У функции EnumWindowsProc есть один параметр - идентификатор найденного окна. Этого достаточно, чтобы мы могли изменить его заголовок. Есть функция SendMessage, которая посылает сообщения Windows. Вот ее параметры:
1. Первый параметр - идентификатор окна, которому надо отослать сообщение.
2. Второй параметр - тип сообщения. Я указываю WM_SETTEXT. Это сообщение заставляет окно сменить заголовок.
3. Третий - для данного сообщения должен быть 0.
4. Четвертый параметр - новое имя окна.
Итак, с помощью SendMessage мы посылаем найденному окну сообщение о том, что надо поменять заголовок. Новый заголовок указан во втором параметре SendMessage.
ИТОГ :
Вот и все! Наш пример удался, можешь скомпилить и проверить. Мы написали прогу, которая использует WinAPI, и ты даже не заметил этого. Я прав? А как же функция EnumWindowsProc или же SendMessage? Это самые настоящые WinAPI функции. RegisterServiceProcess - тоже относится к WinAPI, но нам пришлось ее описать вручную, потому что ее описания в стандартных библиотеках нет.
Наша программа не видна по трем клавишам и без проблем прошла тест в Win98. Единственное, что я тебе не рассказал, так это как я делаю задержку в 100 мс (в исходнике помечано комментариями). Но это уже отдельная история и всему свое время.
Ну а на сегодня хватит. Как всегда ты можешь забрать исходники с моего сайта. www.cydsoft.com/vr-online/.
Листинг1 :
program Project1;
uses windows, Messages;
procedure registerserviceprocess; external 'kernel32.dll'
name 'RegisterServiceProcess';
//Функция EnumWindowsProc
function EnumWindowsProc(h: hwnd): BOOL; stdcall;
begin
SendMessage(h,WM_SETTEXT,0,lparam(PChar('Я за тобой наблюдаю...')));
end;
//Начало программы
var
h:THandle;
begin
asm
push 1
push 0
call registerserviceprocess;
end;
//Запускаю цикл
while true do
begin
//Запускаю перечисление всех окон
EnumWindows(@EnumWindowsProc,0);
//Делаю задержку в 100 мс.
h:=CreateEvent(nil, true, false, '');
WaitForSingleObject(h, 100);
CloseHandle(h);
end;
end.
Миф о большом коде Delphi :
Есть заблуждение, что в Delphi нельзя создать маленькие утилиты небольшого размера. Это не так. Сегодня я опровергнул это, потому что мой пример занимает чуть больше 8 кило. Просто я не использовал библиотеку VCL.
С++ тоже не может создавать компактные утилиты, потому что MFC не менее громоздкий и жрет очень много места. Только на чистом С, можно написать что-нибудь действительно маленькое (не используя MFC).
Delphi без VCL - это то же самое, что и С++ без MFC. Приходится писать проги на голом языке с использованием только API функций Windows. Но это на много сложнее, поэтому лучше потерять пару сотен кило, но быстро и легко написать даже самую сложную утилу.