Aplicación GUI menor a 1 kb

Durante mi tiempo libre, me pregunté la posibilidad de crear una aplicación con los siguientes requisitos:

  • al menos alguna función de aplicación útil (es decir, no es un ficticio)
  • la presencia de una interfaz de ventana
  • tamaño inferior a 1 Kb

En general, el estándar de la aplicación con un tamaño de hasta 1 Kb es 1k intro, que es una especie de demoscene. La mayoría de las veces, esto se escribe en la inicialización del ensamblador de OpenGL y luego se le da un sombreador, que hace el trabajo principal (dibuja algunos fractales de colores). Además, todo esto es cosechado por el empacador (por ejemplo, arrugador).
Estas aplicaciones están literalmente lamidas a un byte, su desarrollo lleva semanas e incluso meses.

Este enfoque es demasiado duro, decidí no ir más allá de la programación de aplicaciones habitual en WinAPI.

En este momento, comenzó una venta de verano en Steam, donde los adultos fueron invitados a mover a los alienígenas sabiamente con un mouse en el navegador (sí, Valve vuelve a hacer juegos, como prometió Gabe).
Esto me ofendió hasta el núcleo, y decidí intentar implementar el autoclicker más simple con una configuración mínima.

Requerido para caber en 1 KB:

  • creación e inicialización de interfaz
  • función de ventana con manejadores de eventos
  • lógica de aplicación principal (construida sobre las funciones GetAsyncKeyState y SendInput)

La aplicación se creará en MSVC en C puro sin ensamblador y SMS, y luego se comprimirá con el empaquetador de arrugas. Cabe señalar que el analizador comprime los datos (especialmente los enrarecidos) de manera mucho más eficiente que el código (aproximadamente dos veces). Por lo tanto, intentaremos transferir la máxima funcionalidad a los datos.

Comenzando con el clásico CreateWindow para cada elemento de ventana, me di cuenta de que no podía caber en el tamaño requerido.

Tuve que buscar una alternativa. Y se convirtió en la función CreateDialogIndirect, creando un diálogo a partir de la estructura DLGTEMPLATE llena (que consiste en un montón DLGITEMTEMPLATE)

Para la creación y el llenado convenientes de la estructura, comencé algunas macros como estas:

#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} 

Ahora puede declarar y llenar la estructura con los elementos de la ventana futura:

 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), }; 

Alimentamos la estructura de la función CreateDialogIndirect, y aquí está la ventana resultante:



Como encajamos en 1 kb, no tenemos un manifiesto, como todo lo demás, y por lo tanto tampoco hay estilos visuales. Todo es gris y cuadrado, como en la juventud.

Pero seguimos esquivando, sacando el manifiesto de shell32.dll y aplicando el contexto basado en él a nuestra aplicación:

 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); 

Ya es elegante, de moda:



Logramos reducir la función de ventana a una función bastante compacta:

 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); 

Y luego pensé que sería bueno agregar el procesamiento de los argumentos de la línea de comandos para que el usuario tuviera la oportunidad de comenzar con la configuración necesaria.

Por ejemplo:

input.exe /L /T /P:20 /K:9 - 20 , / Tab

Mueva la parte de la funcionalidad dentro de los datos y obtenga algo como:

 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; } } 

El manejador salió muy pequeño. Naturalmente, no hay controles y protecciones contra entradas incorrectas, solo el mínimo necesario.

Ahora la funcionalidad principal (lo puse en una secuencia separada):

 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); } 


Presionamos el obj-file crinkler'om resultante - la salida es de 973 bytes.

De estos, los datos ocupan 163 bytes (relación de compresión 33.1%), código - 517 bytes (relación de compresión 68.9%).

Es posible reducir y fortalecer (otros 50 bytes), pero el objetivo ya se ha logrado. Incluso quedaban 51 bytes de reserva.

Se agregaron los requisitos iniciales en el camino:

  • procesamiento de argumentos de línea de comando
  • mostrando una ventana con estilos visuales

En algunos lugares, el código se ve muy torcido. Esto es porque lo escribí torcidamente. Y en algunos lugares esto permitió ahorrar espacio.

Seguramente puede encontrar un par de formas más de reducir el tamaño de la aplicación, pero no de manera espectacular (creo que el byte es 50).

Resultado:

Ahora puedes llamar a los extraterrestres en modo automático con daño salvaje por segundo.

Es posible crear aplicaciones ultracompactas con una funcionalidad útil realmente utilizada y una interfaz de ventana.

Novedad:
Cero Reuní un montón de técnicas / desarrollos.

Expediencia
Inútil, divertido para.

Código fuente
Binario

Aceptada crítica, deseos, sugerencias, admiración, indignación.

Source: https://habr.com/ru/post/es416135/


All Articles