In meiner Freizeit habe ich mich gefragt, ob ich eine Anwendung mit folgenden Anforderungen erstellen kann:
- zumindest einige nützliche Anwendungsfunktionen (dh kein Dummy)
- das Vorhandensein einer Fensterschnittstelle
- Größe weniger als 1 Kb
Im Allgemeinen ist der Anwendungsstandard mit einer Größe von bis zu 1 KB 1k Intro, eine Art Demoszene. Meistens wird dies in der Assembler-Initialisierung von OpenGL geschrieben und dann einem Shader zugeführt, der die Hauptaufgabe erledigt (zeichnet einige farbige Fraktale). All dies wird vom Packer (zum Beispiel Crinkler) geerntet.
Diese Anwendungen werden buchstäblich auf ein Byte geleckt, ihre Entwicklung dauert Wochen und sogar Monate.
Dieser Ansatz ist zu hart, ich habe beschlossen, nicht über die übliche Anwendungsprogrammierung unter WinAPI hinauszugehen.
Zu dieser Zeit begann ein Sommerverkauf bei Steam, bei dem Erwachsene eingeladen wurden, die Außerirdischen mit einer Maus im Browser mit Bedacht zu wackeln (ja, Valve macht wieder Spiele, wie Gabe versprochen hatte).
Dies beleidigte mich bis ins Mark und ich beschloss, den einfachsten Autoklicker mit minimalen Einstellungen zu implementieren.
Erforderlich für 1 KB:
- Schnittstellenerstellung und -initialisierung
- Fensterfunktion mit Ereignishandlern
- Hauptanwendungslogik (basierend auf den Funktionen GetAsyncKeyState und SendInput)
Die Anwendung wird in MSVC in reinem C ohne Assembler und SMS erstellt und dann mit dem Crinkler Packer komprimiert. Es sollte beachtet werden, dass Crinkler Daten (insbesondere verdünnte) viel effizienter komprimiert als Code (ungefähr zweimal). Daher werden wir versuchen, das Maximum an Funktionalität in die Daten zu übertragen.
Beginnend mit dem klassischen CreateWindow für jedes Fensterelement wurde mir klar, dass ich nicht in die erforderliche Größe passen konnte.
Ich musste nach einer Alternative suchen. Und sie wurde zur Funktion CreateDialogIndirect und erstellte einen Dialog aus der gefüllten DLGTEMPLATE-Struktur (bestehend aus einem Heap DLGITEMTEMPLATE).
Zum bequemen Erstellen und Füllen der Struktur habe ich einige Makros wie diese gestartet:
#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}
Jetzt können Sie die Struktur deklarieren und mit den Elementen des zukünftigen Fensters füllen:
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), };
Wir füttern die Struktur der CreateDialogIndirect-Funktion, und hier ist das resultierende Fenster:

Da wir in 1 KB passen, haben wir nicht wie alles andere ein Manifest und daher gibt es auch keine visuellen Stile. Alles ist grau und quadratisch wie in der Jugend.
Aber wir weichen immer noch aus, ziehen das Manifest aus shell32.dll und wenden den darauf basierenden Kontext auf unsere Anwendung an:
static ACTCTX actCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID|ACTCTX_FLAG_SET_PROCESS_DEFAULT|ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID, "shell32.dll", 0, 0, tmp, (LPCTSTR)124, 0, 0}; GetSystemDirectory(tmp,sizeof(tmp)); LoadLibrary("shell32.dll"); ActivateActCtx(CreateActCtx(&actCtx),(ULONG_PTR*)&tmp);
Es ist schon stilvoll, modisch:

Wir haben es geschafft, die Fensterfunktion auf eine ziemlich kompakte zu verkleinern:
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);
Und dann dachte ich, dass es schön wäre, die Verarbeitung von Befehlszeilenargumenten hinzuzufügen, damit der Benutzer die Möglichkeit hat, mit den erforderlichen Einstellungen zu beginnen.
Zum Beispiel:
input.exe /L /T /P:20 /K:9 - 20 , / Tab
Verschieben Sie den Teil der Funktionalität in die Daten und erhalten Sie Folgendes:
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++)=='/')
Der Handler kam sehr klein heraus. Natürlich keine Überprüfungen und Schutzmaßnahmen gegen falsche Eingaben, nur das notwendige Minimum.
Nun die Hauptfunktionalität (ich habe sie in einen separaten Stream gestellt):
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); }
Wir drücken die resultierende obj-Datei crinkler'om - die Ausgabe ist 973 Bytes.
Davon belegen Daten 163 Bytes (Komprimierungsrate 33,1%), Code 517 Bytes (Komprimierungsrate 68,9%).
Es ist möglich zu schrumpfen und stärker zu werden (weitere 50 Bytes), aber das Ziel wurde bereits erreicht. Sogar 51 freie Bytes blieben übrig.
Die anfänglichen Anforderungen auf dem Weg wurden hinzugefügt:
- Befehlszeilenargumente verarbeiten
- Anzeigen eines Fensters mit visuellen Stilen
An einigen Stellen sieht der Code sehr schief aus. Das liegt daran, dass ich es schief geschrieben habe. Und an einigen Stellen konnte dadurch Platz gespart werden.
Sicherlich können Sie ein paar weitere Möglichkeiten finden, um die Größe der Anwendung zu reduzieren, aber nicht dramatisch (ich denke, das Byte ist 50).
Ergebnis:
Jetzt können Sie Aliens im automatischen Modus mit wildem Schaden pro Sekunde rufen.Es ist möglich, ultrakompakte Anwendungen mit tatsächlich verwendeten nützlichen Funktionen und einer Fensterschnittstelle zu erstellen.
Neuheit:
Null Ich habe eine Reihe von Techniken / Entwicklungen zusammengestellt.
Zweckmäßigkeit:
Nutzlos, Spaß für.
QuellcodeBinärAkzeptierte Kritik, Wünsche, Vorschläge, Bewunderung, Empörung.