Написание экстра-маленьких Win32 приложений на С++ от 1 КБ используя лишь API, на примере программы Windows Hider
Введение
Натыкаясь в Интернете на довольно интересные программы, я часто не решался их
закачивать после того, как узнавал их размер. Какую ни возьми - все огромные. Да
и ресурсов системных потребляют немало. В этой статье я расскажу о том, как
сделать программу в среднем в 10 - 100 раз меньше размером, чем попадаются
аналогичные.
Цель
Написать очень быструю и маленькую программу, скрывающую по CTRL+F12 заданные
окна. При нажатии комбинации CTRL+F10 она должна показать спрятанные окна.
Входные данные:
Если будут найдены окна, содержащие в своем заголовке указанные строки, они
будут спрятаны.
В вышеуказанном примере будут спрятаны все окна IE, окно
Microsoft Visual C++, окно почтовой программы "The Bat!" и все окна, в
заголовках которых содержится комбинация символов "911".
Итак, писать будем на чистом Win32 API. Создадим окно, привяжем к нему
горячие клавиши. По требованию будем осуществлять перебор видимых окон в системе
и в заголовке каждого будем искать заданные комбинации символов.
Опции линкера
Если ничего не предпринимать, то нам не удастся получить в итоге файл менее
32 КБ(примерно). Поэтому пишем:
На что теперь стоит обратить особое внимание? Обычно точка входа в программу
выглядит так:
(кстати, для Win32 приложений второй параметр всегда NULL)
Но(!)... Так как мы отключили "Runtime library", нам теперь передается в этих
параметрах разный мусор. Поэтому называем точку входа не WinMain а New_WinMain,
которую объявим, как void New_WinMain(void), чтобы не забыть о том, что нам
ничего не передается. А параметр HINSTANCE получаем функцией
GetModuleHandle(NULL). Ах да, и выходить из программы будем функцией
ExitProcess.
Теперь если собрать нашу пустую программку, которая ничего делать не будет,
размер ее будет 1 Кб. Но нам нужно еще дописать 3 Кб кода. Продолжим.
Чтобы все дальнейшее было понятно даже новичку в программировании под
Windows, я прокомментирую все.
Объявим кое-какие константы
Это понадобится для регистрации "горячих" клавиш функцией RegisterHotKey.
Размер буффера, куда будет считываться заголовок окна функцией GetWindowText.
Размер буфера, куда будет считываться файл со стоками фильтрации
(используется в объявлении char FilterStrings[MAXFIL];)
(Примечание: При желании можно сделать и выделение памяти динамически - найти
файл, узнать его размер и выделить блок. Приблизительный пример:
Массив хендлов окон (вряд ли будет у нас более 300 окон)
Кол-во инициализированных элементов в этом массиве
Дескрипторы окон - главное и два дочерних - кнопка "Hide" и кнопка "Edit
filter strings"
Тут будет что-то типа "c:\programs\winhider\winhider.settings.txt"
Соответственно, хендл файла с именем "что-то типа"
А это место, куда будем считывать все из этого файла
Функции
Обработка сообщений главного окна
Функция, которая будет вызываться для каждого окна при переборе всех окон
Проверка наличия строки str2 в str1
Скрывание с экрана очередного окна
Возврат всех спрятанных окон на экран
Пройдемся по главным строкам функции NewWinMain
* Получим INSTANCE модуля. Это нам нужно для регистрации оконного класса
* Зарегистрируем оконный класс
* Создаем главное окно приложения
И помещаем на него две кнопки. Как видим, кнопки имеют класс "BUTTON". Они
являются дочерними окну hwndMain.
Наконец, показываем главное окно
Примечание: Так как кто-то этого может не знать, хочу отметить, что в языке
С++ есть "операция следования" - запятая. Т.е. просто последовательно выполнятся
обе функции ShowWindow и UpdateWindow (как отдельный блок). В вышеуказанной
строке можно было бы и просто поставить ";", а вообще иногда это помогает
избавиться от огромного количества фигурных скобок {}, в тексте программы.
* Затем регистрируем в системе HotKeys. Они будут привязаны к главному окну,
которому будут передаватся сообщения WM_HOTKEY.
* Затем считываем настройки из файла и запускаем главный цикл обработки
оконных сообщений для текущего процесса.
Оконная процедура
Рассмотрим функцию my_EnumWindowsProc
Пропустим все невидимые окна
Получим TITLE очередного окна
Затем перебираем все стоки из файла настроек
Продолжаем дальнейший перебор окон
(Если бы было return FALSE, перебор бы закончился.)
В остальных функциях особо описывать нечего.
FAQ, возникший в результате множества заданных мне вопросов.
Q: Почему программа не линкуется?
A: Попробуйте собрать не debug, а release версию. А если вам нужна
возможность отладки, воспользуйтесь обычными #define. И все-таки есть еще одни
вариант. В отладочной версии линкер не может собрать файл потому, что не находит
"__chkesp", которая содержится в "CHKESP.OBJ". Что мы можем сделать? Да взять и
заменить тот obj на свой, который будет меньше размером и не будет содержать
ненужный нам код.
Q: Как теперь получить переданную командную строку?
A: Ну тут все просто. Пользуйтесь стандартными API. То же самое и для
Instance приложения. Вот они - GetCommandLine, GetModuleHandle.
Q: А какой минимальный align возможен?
A: Для того, чтобы ваша программа запускалась нормально в любой версии
Windows, используйте 512 байт.
Q: А можно ли делать такими маленькими DLL?
A: Да. Назначьте свою точку входа вместо _DllMainCRTStartup.
Q: А почему пропали функции strcmp, strlen и т.п.?
A: Так как они были реализованы в RTL, теперь вы не можете их использовать.
Но это не беда. В модуле kernel есть отличная замена этим функциям. Названия те
же, но с буквой "l" вначале. Например - lstrlen, lstrcmp, lstrcat.
Q: А теперь стали недоступны функции работы с памятью - memset,
CopyMemory?
A: RTL сам предоставляет интефейс для работы с памятью. Во-первых, чтобы
соблюдать снандарт, во-вторых, чтобы упростить работу с памятью в среде Win32.
Вот посмотрите на функцию CopyMemory - она на самом деле не является настоящей
API функцией. Попробуйте слинковать проект без RTL, в котором используется эта
функция. Результат - неудачная попытка линковки - ссылка на _memcpy. Еще один
пример - функция new. В среде Win32 вы должы воспользоваться функциями
GlobalAlloc, GlobalLoc и т.п. Однако вы можете просто заменить RTL функции
своими. В файле add.txt вы можете взять уже готовые функции, если не хотите
писать их сами.
Q: У меня есть один вопрос, которого нет в этом FAQ, что делать?
A: В таком случае вы можете задать вопрос на нашем форуме.
Комментарии |
отсутствуют |
Добавление комментария |