Главная » Хабрахабр » [Из песочницы] GUI-приложение размером менее 1 Кб

[Из песочницы] GUI-приложение размером менее 1 Кб

На досуге задался вопросом возможности создания приложения, со следующими требованиями:

  • хоть сколько-нибудь полезная прикладная функция (то есть не пустышка)
  • наличие оконного интерфейса
  • размер менее 1 Кб

Вообще, эталон приложения с размером до 1 Кб — это 1k intro, являющееся разновидностью демосцен. Чаще всего это написанная на ассемблере инициализация OpenGL с последующим скармливанием ему шейдера, который и выполняет основную работу (отрисовывает какие-нибудь раскрашенные фракталы). Плюс всё это пожато упаковщиком (например crinkler)
Эти приложения в буквальном смысле вылизаны до байта, их разработка занимает недели и даже месяцы.

Такой подход слишком суров, я решил не выходить за рамки обычного прикладного программирования на WinAPI.

В это время в Steam началась летняя распродажа, где взрослым людям предлагалось дичайше затыкивать инопланетян мышой в браузере (да-да, Valve снова делает игры, как и обещал Гейб).
Это оскорбило меня до глубины души, и я решил попробовать реализовать простейший автокликер с минимальными настройками.

Требовалось уместить в 1 Кб:

  • создание и инициализацию интерфейса
  • оконную функцию с обработчиками событий
  • основную логику приложения (построенную на функциях GetAsyncKeyState и SendInput)

Приложение будет создаваться в MSVC на чистом C без ассемблера и СМС, а затем сжиматься пакером crinkler. Следует отметить, что данные (особенно разреженные) crinkler сжимает гораздо эффективнее, чем код (раза эдак в два). Поэтому будем стараться максимум функционала переносить внутрь данных.

Начав с классических CreateWindow для каждоко элемента окна, я понял, что в требуемый размер я никак не влезу.

И ей стала функция CreateDialogIndirect, создающая диалог из заполненной структуры DLGTEMPLATE (состоящей из кучи DLGITEMTEMPLATE) Пришлось искать альтернативу.

Для удобного создания и заполнения структуры я завёл немножко макросов вроде таких:

#define NUMCHARS(p) (sizeof(p)/sizeof((p)[0])) #define DLGCTL(a) struct{DWORD style; DWORD exStyle; short x; short y; short cx;
short cy; WORD id; WORD sysClass; WORD idClass; WCHAR wszTitle[NUMCHARS(a)]; WORD cbCreationData;} #define RADIO(x,y,cx,cy,id,title) {WS_VISIBLE|WS_CHILD|BS_RADIOBUTTON, 0, (x)/2, (y)/2,\n
(cx)/2,(cy)/2, id, 0xFFFF, 0x0080, title, 0}

Теперь можно объявить и заполнить структуру элементами будущего окна:

static struct
{ DWORD style; DWORD dwExtendedStyle; WORD ccontrols; short x; short y; short cx; short cy; WORD menu; WORD windowClass; DLGCTL(LBL_BTN_LEFT) button_left; DLGCTL(LBL_BTN_MIDDLE) button_middle; DLGCTL(LBL_BTN_RIGHT) button_right;
} Dlg = { WS_VISIBLE|WS_POPUP|WS_BORDER, 0, TOTAL_CONTROLS, 50/2, 50/2, WND_CX/2, WND_CY/2, 0, 0, RADIO(10, 0, 80, 30, IDC_BTN_LEFT, LBL_BTN_LEFT), RADIO(100, 0, 80, 30, IDC_BTN_MIDDLE, LBL_BTN_MIDDLE), RADIO(190, 0, 68, 30, IDC_BTN_RIGHT, LBL_BTN_RIGHT),
};

Скармливаем структуру функции CreateDialogIndirect, и вот получившееся окно:

Всё серое и квадратное, как в молодости. Так как мы умещаемся в 1 кб, то манифеста, как и всего прочего, у нас нет, а значит и визуальных стилей тоже.

Но мы всё-таки извернёмся, дёрнув манифест из shell32.dll и применив контекст на его основе к нашему приложению:

static ACTCTX actCtx = ; GetSystemDirectory(tmp,sizeof(tmp)); LoadLibrary("shell32.dll"); ActivateActCtx(CreateActCtx(&actCtx),(ULONG_PTR*)&tmp);

Вот уже стильно, модно:

Оконную функцию удалось ужать до довольно компактной:


switch(msg)
{ case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_BTN_LEFT: case IDC_BTN_MIDDLE: case IDC_BTN_RIGHT: input[0].mi.dwFlags = wParam; input[1].mi.dwFlags = (wParam<<1); CheckRadioButton(hWnd,IDC_BTN_LEFT,IDC_BTN_MIDDLE,wParam); break; case IDC_BTN_HOLD: case IDC_BTN_TRIGGER: trigger_mode = (wParam==IDC_BTN_TRIGGER); CheckRadioButton(hWnd,IDC_BTN_HOLD,IDC_BTN_TRIGGER,wParam); break; case IDC_EDIT_PERIOD: period = GetDlgItemInt(hWnd,IDC_EDIT_PERIOD,(BOOL*)&tmp[0],0); break; case IDC_BTN_EXIT: exit(0); } break; } return DefWindowProc(hWnd,msg,wParam,lParam);

И тут я подумал, что хорошо бы добавить обработку аргументов командной строки, дабы пользователь имел возможность запускаться с нужными настройками.

Например эдак:

input.exe /L /T /P:20 /K:9 - кликать левой кнопкой мыши каждые 20 мсек, режим включается/выключается клавишей Tab

Перенесём часть функционала внутрь данных и получим что-то вроде:

static unsigned int arg_to_cmd[] = {IDC_BTN_HOLD, 0, 0, IDC_EDIT_KEY, IDC_BTN_LEFT, IDC_BTN_MIDDLE, 0, 0, IDC_EDIT_PERIOD, 0, IDC_BTN_RIGHT, 0, IDC_BTN_TRIGGER}; i = (char*)GetCommandLine();
while(*i)
{ if (*(i++)=='/')//looking for argument switch(*i) { case 'L': case 'M': case 'R': case 'H': case 'T': SendMessage(hWnd,WM_COMMAND,arg_to_cmd[*i-'H'],0);//send button command break; case 'P': case 'K': t = atoi(i+2); SetDlgItemInt(hWnd,arg_to_cmd[*i-'H'],t,0); if(*i=='P') period = t; else key = t; break; } }

Обработчик вышел весьма небольшим. Естественно, никаких проверок и защит от неверного ввода, только необходимый минимум.

Теперь основной функционал (его я вынес в отдельный поток):

while(1)
{ key_state = (GetAsyncKeyState(key)>>1); if (trigger_mode) { if ((key_state)&&(key_state!=prev_key_state)) active^= 1; prev_key_state = key_state; } else active = key_state; if (active) SendInput(2,(LPINPUT)&input,sizeof(INPUT)); Sleep(period);
}

Жмём получившийся obj-файл crinkler'ом — на выходе 973 байта.

1%), код — 517 байт (степень сжатия 68. Из них данные занимают 163 байта (степень сжатия 33. 9%).

Даже остался 51 запасной байт. Можно ужать и сильнее (ещё байт на 50), но цель и так достигнута.

К первоначальнми требованиям по ходу добавились:

  • обработка аргументов командной строки
  • отображение окна с визуальными стилями

Местами код выглядят весьма криво. Это потому, что я его криво написал. А ещё кое-где это позволило сэкономить место.

Наверняка можно придумать ещё пару-тройку способов уменьшить размер приложения, но не кардинально (я думаю, байт на 50).

Результат:

Теперь можно закликивать инопланетян в автоматическом режиме с диким уроном в секунду.

Вполне возможно создавать сверхкомпактные приложения с реально используемым полезным функционалом и оконным интерфейсом.

Собрал в кучу несколько приёмов\наработок. Новизна:
Нулевая.

Целесообразность:
Бесполезно, забавы для.

Исходник
Бинарник

Принимаются критика, пожелания, предложения, восхищения, возмущения.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Орден куколки

Прошу считать пятничным постом! Это как бы научная фантастика, но правда важный дисклеймер 1. Научная фантастика понятие очень широкое. Этот рассказ-эссе я смею предложить только тем, кто нормально относится к углу . Простите меня, если в вашем представлении научная фантастика ...

Трудоголизм — болезненное состояние, о котором не принято говорить

Тему профессионального выгорания не пинал на Хабре только ленивый. Были истории и о том, как с этим бороться, и рассказы людей, которые лично столкнулись с этой проблемой, и статьи вида «как избежать выгорания». На самом деле тема важная и нужная. ...