10 puntos por GN⁺ 2025-05-12 | 3 comentarios | Compartir por WhatsApp
  • Simple Todo es una app nativa de Todo para Windows hecha con C y la API de Win32, y es un proyecto que muestra programación de GUI en Windows y funciones de integración con el sistema
  • Sus funciones principales son crear, editar y eliminar tareas, marcarlas como completadas, minimizar a la bandeja del sistema, opción de inicio automático con Windows y búsqueda en tiempo real sin distinción entre mayúsculas y minúsculas
  • Los datos se guardan como archivo binario en %APPDATA%\TodoApp\todos.dat, contienen la lista completa de tareas y admiten hasta 100 tareas
  • La compilación en Windows requiere MinGW-w64 y el SDK de Windows; después de clonar el repositorio, se compila con .\build.bat
  • La estructura se divide en main.c, todo.c, gui.c y utils/search.c, y la GUI usa la API de Win32, Common Controls y UXTheme

Naturaleza del proyecto

  • Simple Todo es una aplicación nativa de Todo para Windows escrita en C y la API de Win32
  • Apunta a ser un ejecutable nativo ligero y usa el look and feel predeterminado de Windows junto con los estilos visuales modernos de Windows
  • La GUI está basada en la API de Win32 e incluye integración con la bandeja del sistema y persistencia de datos mediante entrada/salida de archivos

Funciones de Todo

  • Se pueden crear, editar y eliminar elementos Todo
  • Las tareas se pueden marcar como completadas
  • Se puede establecer una prioridad para cada Todo
  • La función de búsqueda opera en tiempo real y admite coincidencia sin distinción entre mayúsculas y minúsculas
  • La app se puede minimizar a la bandeja del sistema
  • Ofrece una opción para ejecutarse automáticamente al iniciar Windows

Método de almacenamiento de datos

  • Los datos de Todo se guardan en %APPDATA%\TodoApp\todos.dat
  • El formato de almacenamiento es un archivo binario que contiene la lista completa de tareas
  • La capacidad de almacenamiento está limitada a un máximo de 100 tareas

Compilación y ejecución

  • Requisitos
    • Windows OS
    • Compilador GCC de MinGW-w64
    • SDK de Windows incluido en MinGW
  • Proceso de compilación
    • Instalar MinGW-w64 y agregar el directorio bin al PATH del sistema
    • Clonar el repositorio y luego moverse al directorio simple-todo-c
    • Ejecutar .\build.bat para compilar
  • La ejecución se inicia con bin/todo.exe

Estructura de la implementación

  • La estructura del proyecto se divide en archivos C por función
    • src/main.c: punto de entrada de la aplicación
    • src/todo.c, src/todo.h: lógica de gestión de Todo y estructura de datos
    • src/gui.c: implementación de la GUI
    • src/utils/search.c, src/utils/search.h: implementación y declaración de la función de búsqueda
    • src/app.manifest: manifiesto para los estilos visuales de Windows
  • Los componentes principales son la API de Win32, Common Controls, UXTheme y File I/O
  • Para depuración, ofrece una guía DEBUGGING.md que puede usarse en Visual Studio, VS Code, etc.

Licencia y contribuciones

  • La licencia es la MIT License
  • Se aceptan contribuciones mediante Pull Request

3 comentarios

 
aer0700 2025-05-13

Tiene su encanto.

 
GN⁺ 2025-05-12
Opiniones de Hacker News
  • La programación de GUI con Win32 tiene algo curiosamente atractivo.
    Es un poco peculiar, pero si lees el blog de Raymond Chen, se entiende por qué.
    La API de Win32 tiene raíces en la época del procesador 8088, así que hay trasfondos como ahorrar 40 bytes de código o usar un registro menos si se hace de cierta manera.
    Antes hice muchas apps GUI de juguete con mingw y el libro de Petzold, y cosas como escribir controles personalizados, dibujar gráficos y texto, manejar scroll y hacer hit testing eran bastante divertidas.
    Veo que la app usa strcpy y sprintf, pero para programación seria habría que usar variantes con verificación de longitud. Me sorprende que el compilador no haya arrojado un montón de advertencias.
    La API de Win32 tiene muchas funciones que reemplazan cosas de la biblioteca estándar de C. Si de verdad quieres reducir el tamaño del ejecutable, puedes intentar escribir la app sin la biblioteca estándar de C. Está ZeroMemory() en lugar de memset(), y CopyMemory() en lugar de memcpy().
    En algún momento escribir código C puro a mano se vuelve doloroso, pero creo que hacerlo en C puro las primeras veces es lo mejor para aprender. Si administras los detalles directamente, mientras aprendes te haces una buena idea de lo que ocurre por dentro.
    Si quieres seguir probando la programación GUI con Win32, recomiendo ver WTL (Windows Template Library). Es un wrapper de C++ sobre la API de Win32, así que hace mucho más fácil entender el comportamiento.

    • Uso una excelente app para PC llamada AlomWare Toolbox, que es casi el arquetipo del diseño Win32 (https://www.alomware.com/images/tab-automation.png).
      Aunque tiene muchísimas funciones, gracias a eso pesa apenas unos 3 MB. Sin frameworks y con un único ejecutable; ojalá todo el software siguiera siendo así.
    • Hoy en día, si no usas al menos strncpy en vez de strcpy, tienes que aguantar que tanto personas como herramientas de IA te digan una y otra vez que no deberías hacerlo.
      Personalmente, una de las principales razones por las que uso Zig es que reduce mucho estas trampas comunes. Claro que C también está bien.
    • Se mencionó ZeroMemory() en lugar de memset() y CopyMemory() en lugar de memcpy(), pero los intrinsics de MSVC probablemente usen instrucciones rep stos/movs.
      Podrían ser más pequeños que una llamada a función, porque una llamada a función también incluye el tamaño de la entrada en la tabla de importaciones.
    • Hice mucho de ese tipo de trabajo y, sinceramente, extraño la capacidad de desarrollar UI nativas con código nativo.
    • También usa fopen/fread/fclose, pero parece que no usa APIs de Win32 como CreateFile/ReadFile/WriteFile.
  • En vez de llamar laboriosamente a CreateWindow() para cada control, tradicionalmente se colocaban recursos de cuadro de diálogo en un archivo .rc (Visual Studio todavía tiene un editor de diálogos para hacerlo visualmente) y se usaba CreateDialog() en lugar de CreateWindow().
    Así se crean automáticamente todos los controles. Si agregas un manifiesto de aplicación, también obtienes estilos de UI modernos y soporte para alta DPI.

    • Con este método también obtienes automáticamente el cambio de foco con Tab entre controles y algunos atajos de teclado.
      Eso sí, si quieres redimensionamiento, todavía tienes que manejarlo manualmente, pero normalmente es fácil y el código no pasa de unos pocos cientos de bytes.
    • En la práctica, la superposición entre la biblioteca estándar de C y la biblioteca del sistema operativo existe casi solo en UNIX, y en 1985, cuando salió Windows 1.0, todavía no había algo que pudiera llamarse estándar.
      Existía K&R C, pero fuera de UNIX cada sistema operativo elegía por su cuenta qué funciones ofrecer.
      Además, fuera de los sistemas tipo UNIX, existía la tradición de que la biblioteca estándar fuera responsabilidad del compilador de C, no de la plataforma.
      Por eso las bibliotecas de C las proporcionaban Borland, Watcom, Symantec, Microsoft, Green Hills, Zortech y otros.
      Mac OS fue igual hasta que apareció MPW, y lo mismo pasaba con IBM y Unisys, microcomputadoras y mainframes, y VMS antes de OpenVMS.
      Desde Windows 10 también existe Universal C Runtime.
    • Este enfoque se puede trasladar fácilmente a lenguajes que tengan una buena interfaz de funciones externas (FFI), y no se necesita nada más.
      No hace falta un compilador de recursos ni un linker para crear una DLL de recursos.
      Los archivos de recursos y su formato binario no son una buena API.
      Si en un lenguaje de nivel razonablemente alto tienes llamadas a CreateWindow, parece posible crear mediante metaprogramación un DSL parecido a recursos que se integre bien con el lenguaje.
  • Hace tiempo hice algo parecido en ensamblador en Linux en menos de 2 KiB: https://gaultier.github.io/blog/x11_x64.html
    Como dijeron otros, si lo haces en C puro y usas enlace dinámico, al menos en Linux es fácil mantenerlo por debajo de 20 KiB. En Windows quizá sea todavía más fácil, porque el sistema operativo trae muchas más cosas incluidas por defecto.
    En cualquier caso, apoyo el intento. Probar las opciones de enlace que dejé al final de mi artículo debería ayudar a reducir el tamaño.

    • Un programa TUI TODO basado en ncurses un poco más ampliado pesa 15K. Es en Linux y no está enlazado estáticamente.
      Todavía no he logrado compilar ncurses con musl.
  • Al leer “look and feel nativo de Windows”, antes de ejecutarlo esperaba que la vista de lista tuviera un menú contextual y que al hacer doble clic en ciertos campos apareciera un menú desplegable casi en línea o un cuadro de texto.
    Ya no sé si ahora la gente no sabe diseñar programas según la UI/UX de Win32, o si simplemente yo estoy demasiado viejo.

  • Decían “sin frameworks”, pero con el escalado de DPI las fuentes se ven borrosas, no hay soporte para Tab, en los campos de texto Ctrl-A no selecciona todo, faltan varias funciones que incluso ofrecían frameworks premodernos, y al agregar filas también aparecen errores
    No sé qué significa “moderno”

    • Aquí hay un ejemplo de configuración de reconocimiento de DPI: https://github.com/Dwedit/GameStretcher/blob/master/Stretche...
      Este código verifica dinámicamente cuál está disponible entre user32:SetProcessDpiAwarenessContext, shcore:SetProcessDpiAwareness y user32:SetProcessDPIAware, y lo llama
      Si la versión de Windows es tan antigua que no implementa ninguno de ellos (anterior a Windows XP), no llama a nada
    • Parece que “moderno” significa que es mucho más grande de lo necesario y que le faltan muchas funciones
      La mayoría de las funciones que mencioné como faltantes, en especial cosas como moverse entre controles con Tab, son muy fáciles de agregar
  • El programador de 6502 que llevo dentro se está muriendo por dentro al ver que 278KB ahora se considera liviano

    • Intenté reproducir este binario para ver en qué se usan los 278KB
      El primer obstáculo que encontré fue que, si git está configurado con core.autocrlf=false, el archivo build.bat no funciona. Al cambiarlo a core.autocrlf=true y clonar de nuevo, pude compilar
      Usé el toolchain x86_64-15.1.0-release-win32-seh-msvcrt-rt_v12-rev0.7z de https://github.com/niXman/mingw-builds-binaries/releases/tag...
      En este caso sale un archivo .exe de 102KB. De entrada es mucho mejor que los 278KB afirmados. Puede que el autor use otro toolchain u otra configuración, así que estaría bien tener pasos exactos para reproducirlo
      Pasándole algunos flags a GCC se puede reducir más
      gcc -Os => 100KB
      gcc -Oz => 99KB
      gcc -flto => 101KB
      gcc -s => 51KB
      gcc -s -Oz -flto => 47KB
      Si lo único que importa es un .exe pequeño, todavía hay mucho margen de mejora
    • Una buena parte se debe a la plataforma y el formato del ejecutable
      Sin cosas como información de stack trace, infraestructura de enlazado dinámico y tablas de manejo de excepciones, podría ser mucho más liviano. Las tablas de manejo de excepciones son necesarias incluso en C, porque una excepción puede atravesar funciones C
    • También podríamos pedir que en las competencias de demoscene creen una categoría de app TODO de 64KB
    • Sinceramente me sorprende que sea tan grande. Esperaba que fuera más chico, o que más o menos la mitad la ocupara el ícono de la app
      Recuerdo que cuando escribí algo así hace tiempo era más pequeño. ¿Será por MinGW?
    • De pronto me acordé de la época en que programar Win32 en assembler se volvió bastante hip
      Probablemente fue una reacción a la tendencia de que las descargas de shareware fueran cada vez más grandes. Eran los años oscuros de MFC
      Junto con la programación temprana para Palm Pilot 68k, lo recuerdo como el último gran momento del assembler fuera de la retrocomputación
  • Amigos, es simplemente una app hecha para probar y divertirse. Los comentarios tienen razón, y algo así probablemente habría sido más sensato hacerlo en C++ u otro lenguaje

    • Hace unos 30 años, cuando hice mis primeros programas para Windows, aprendí exactamente de esta manera. Solo que seguramente usé un compilador de C++
      No sé bien por qué, pero creo que la documentación de la API de Windows guiaba a escribir código estilo C usando un compilador de C++. Parece que Microsoft veía C++ como un superconjunto mejorado de C, y consideraba correcto usarlo incluso para código estilo C
    • No es broma: preferiría usar esta app antes que la app TODO básica de Windows 11
    • Así es exactamente como debe hacerse
      Si vas a usar la API de Win32, otro lenguaje no cambia nada fundamentalmente, y de hecho puede hacerlo más confuso
      La gente suele caer en la mentalidad de C++, y para alguien que no está familiarizado con la API de Win32 eso habría convertido todo en un amasijo aún más confuso
      Es un intento muy lindo, y más allá de lo que piensen otros, la familiaridad con la API de Win32 es una buena habilidad base
    • https://github.com/Efeckc17/YoutubeGO Como referencia, me haría muy feliz que también revisaran o le echaran un vistazo a esta aplicación
    • Este tipo de proyectos de UI nativa limpia fue lo que me motivó a aprender programación. Genial
  • Veo aquí a varias personas burlándose bastante que hacen software o sitios web que cargan varios MB de JS o C# para mandar 278KB de telemetría cada vez que el usuario mueve el mouse

    • Una app similar hecha con C# + WinForms ocupa menos de 10KB en disco y usa 6MB de RAM
      Esta app usa 1.5MB de RAM, y ambas arrancan al instante
  • Al desarrollar apps para Windows, siempre resulta agradable saber que se puede hacer todo en C
    Simplemente se siente natural construir desde los elementos básicos de más bajo nivel
    En cambio, en macOS todo es un objeto Objective-C, así que aunque se puede escribir una app en C puro, es como bajar un nivel por debajo de la abstracción que Apple pretendía, y termina siendo casi 1000 veces más verboso. Para hacer algo, en la práctica hay que manipular como marionetas la jerarquía de clases de Objective-C, y eso se siente realmente incómodo
    No entiendo por qué no se puede volver a montar la API basada en clases de Objective-C encima de una API procedural básica al estilo Win32. Técnicamente Win32 también está “basado en clases”, pero es mínimo
    Por eso, por ahora no me dan muchas ganas de portar mi código C a macOS

    • Curiosamente, Windows tiene COM y también WinRT, que es su propia API/ABI orientada a objetos construida sobre COM
      Pero el compilador MIDL para interfaces COM genera headers que permiten usar e implementar interfaces desde C: https://www.codeproject.com/KB/com/com_in_c1.aspx
      Hasta donde sé, hoy sigue funcionando también con WinRT. Eso sí, los structs y nombres de funciones generados tienen que incluir todo el namespace, así que quedan muy feos: https://stackoverflow.com/a/7437006/111335
    • Sí existía una API procedural, y se llamaba Carbon
      Era un reemplazo casi directo de la API Macintosh Toolbox, que user32.dll copió descaradamente
      Pero, aparte de la calidez que pueda sentir el programador, la UI realmente quiere orientación a objetos. Un árbol de distintos objetos que comparten parte del comportamiento común describe casi tal cual el 99% del código de UI
      Toolbox y Carbon carecían de esa orientación a objetos, así que estaban bastante forzados. Una idea realmente más original que agregó Windows fueron precisamente las clases de ventana
      Personalmente, la rareza que se siente al manipular clases Objective-C directamente desde C es la misma que siento cada vez que tengo que definir clases de ventana o procedimientos de ventana en código de user32.dll[0]
      Así como la UI quiere orientación a objetos, la orientación a objetos quiere funcionalidades dedicadas del lenguaje. Se puede construir sin ellas, pero la usabilidad se vuelve 2000 veces peor
      [0] O cualquier cosa relacionada con GTK/GObject
  • Parece que está enlazando contra una biblioteca estática
    No debería enlazar contra una biblioteca estática, sino contra una DLL, y así el tamaño de la aplicación se reduciría bastante

    • Eso parece al revés
      Si de todos modos hay que distribuir la DLL junto con el programa (porque no forma parte del sistema operativo), la DLL contiene toda la funcionalidad, y cada DLL incluso puede tener su propio runtime de C
      Si se compila todo estáticamente en un único EXE, hay un solo runtime de C y es fácil eliminar las funciones que no se usan
      Una DLL solo reduce el tamaño cuando ese código se comparte entre varios programas
    • No, enlazar contra la versión estática del CRT es una buena elección. Se puede recortar el código que no se usa
      Si enlazas dinámicamente contra MSVCRxx/VCRUNTIME, el usuario tiene que descargar exactamente esa DLL desde Microsoft
      Si enlazas dinámicamente contra MSVCRT, no tienes ese problema, pero hacerlo en Visual Studio es muy difícil
      El único caso en que enlazar estáticamente contra el CRT realmente podría estar mal sería algo como cumplir con la LGPL
 
roxie 2025-05-16

Se siente como si el aliento nasal de los panas llegara hasta aquí...