10 Punkte von GN⁺ 2025-05-12 | 3 Kommentare | Auf WhatsApp teilen
  • Dieses Open-Source-Projekt ist eine leichtgewichtige native Windows-Todo-Anwendung, die ausschließlich mit C und der Win32 API erstellt wurde
  • Es läuft ohne Abhängigkeit von Frameworks mit minimaler Größe (maximal 26,5 KB) und implementiert fortgeschrittene Windows-GUI- und Systemintegration direkt
  • Es bietet nicht nur Grundfunktionen wie Todo-Einträge hinzufügen, bearbeiten, löschen und als erledigt markieren, sondern auch praxisnahe Produktivitätsfunktionen wie System-Tray-Integration und eine Autostart-Option
  • Die Speicherung erfolgt dauerhaft in einer Binärdatei; im AppData-Ordner können bis zu 100 Aufgabenlisten gespeichert werden
  • Zu den Stärken zählen klassisches, eng am Betriebssystem orientiertes Programmieren ohne große Frameworks und eine leichtgewichtige Laufzeitumgebung

🌟 Simple Todo (C / WinAPI)

Projektüberblick

  • Dieses Projekt erstellt eine moderne native Windows-Todo-App ausschließlich mit C und der Win32 API
  • Es demonstriert fortgeschrittene Fähigkeiten in Windows-GUI-Programmierung und Systemintegration
  • Das Projekt ist sehr klein (maximal 26,5 KB) und behält das originale Erscheinungsbild von Windows bei

✨ Hauptfunktionen

  • Todo-Einträge erstellen, bearbeiten und löschen
  • Aufgaben können als erledigt markiert werden
  • Dauerhafte Speicherung in AppData, sodass die Daten immer erhalten bleiben
  • Integration in das System-Tray, sodass die App beim Minimieren ins Tray verschoben wird
  • Ein Erscheinungsbild im nativen Windows-Stil
  • Eine Option für den Autostart beim Windows-Start

🛠️ Technische Details

  • Vollständig in reinem C geschrieben
  • Für die GUI wird ausschließlich die Win32 API verwendet
  • Winzige Größe der ausführbaren Datei (26,5 KB mit UPX-Komprimierung)
  • Integration in das System-Tray
  • Moderne visuelle Styles über ein Manifest

💾 Datenspeicherung

  • Alle Todos werden in einer einzigen Binärdatei gespeichert
  • Speicherpfad: %APPDATA%\\TodoApp\\todos.dat
  • Binärformat mit Unterstützung für bis zu 100 Einträge

📋 Voraussetzungen

  • Eine Windows-Betriebssystem-Umgebung ist erforderlich
  • MinGW-w64 (GCC-Compiler) sowie das Windows SDK werden benötigt

🎮 Verwendung

  • Nach dem Start von bin/todo.exe können über die Oberfläche folgende Aktionen ausgeführt werden
  • Mit dem Button "Add" ein neues Todo hinzufügen
  • Einen Eintrag auswählen und mit "Edit" bearbeiten
  • Mit "Delete" einen Eintrag löschen
  • Mit "Complete" als erledigt markieren
  • Für jeden Eintrag kann eine Priorität festgelegt werden

🏗️ Projektstruktur

  • Im Ordner src/ befinden sich Haupteinstiegspunkt (main.c), Todo-Verwaltungslogik (todo.c), Strukturdeklarationen (todo.h) und GUI-Implementierung (gui.c)
  • In bin/ liegt die kompilierte ausführbare Datei
  • Enthält das Build-Skript (build.bat) sowie die Projektdokumentation

🔧 Entwicklungselemente

  • Win32 API: Implementierung von Fensterverwaltung und der gesamten GUI
  • Common Controls: Verwendung moderner UI-Elemente
  • UXTheme: Unterstützung für visuelle Windows-Styles
  • File I/O: Realisierung dauerhafter Datenspeicherung

📝 Lizenz

  • Frei nutzbar und modifizierbar unter der MIT-Lizenz

🤝 Hinweise zur Mitarbeit

  • Pull Requests willkommen
  • Jede Person kann am Projekt mitwirken

📫 Kontakt und Links

3 Kommentare

 
aer0700 2025-05-13

Das hat einfach Charme.

 
GN⁺ 2025-05-12
Hacker-News-Kommentare
  • An der win32-GUI-Programmierung gibt es Dinge, die ich mag, auch wenn sie etwas eigen ist. Wenn man Raymond Chens Blog liest, versteht man warum: Die win32-API stammt noch aus der Zeit des 8088-Prozessors, und manches ist darauf ausgelegt, auf bestimmte Weise 40 Bytes Code zu sparen oder ein Register weniger zu benutzen. Früher habe ich mir mit mingw und dem Petzold-Buch viele einfache GUI-Apps selbst geschrieben. Es hat wirklich Spaß gemacht, alles selbst zu machen: Custom Controls, Grafik-/Textausgabe, Scroll-Verarbeitung, Hit-Testing und so weiter. In deiner App habe ich gesehen, dass du strcpy und sprintf verwendest; wenn man ernsthaft programmiert, sollte man unbedingt Varianten mit Längenprüfung verwenden. Ich bin überrascht, dass der Compiler nicht sofort gewarnt hat. In der win32-API gibt es viele Funktionen, die Standardfunktionen der C-Bibliothek ersetzen. Wenn du die EXE noch kleiner machen willst, würde ich empfehlen, nur <Windows.h> zu verwenden und ohne cstdlib zu schreiben. Statt memset kann man ZeroMemory, statt memcpy CopyMemory verwenden. Natürlich wird rohes C-Coding irgendwann ziemlich schmerzhaft, aber die ersten paar Male hilft es beim Lernen am meisten, alles direkt in purem C zu machen. Man entwickelt dadurch ein Gefühl für solche Details. Wenn du mehr win32-GUI-Programmierung ausprobieren willst, würde ich auch WTL (Windows Template Library) empfehlen. Sie kapselt die win32-API in C++ und macht es viel leichter, die Funktionsweise zu verstehen.
    • Heutzutage sollte man wenigstens strcpy durch strncpy ersetzen, sonst wird das jeder ständig anmerken. Einer der großen Gründe, Zig zu verwenden, ist, dass solche häufigen Fehler seltener werden. Aber natürlich ist auch C okay.
    • Zu der Aussage, statt memset ZeroMemory und statt memcpy CopyMemory zu verwenden: MSVC-Intrinsics nutzen die rep stos/movs-Instruktionen, wodurch der Code kleiner wird als ein Funktionsaufruf und auch die Importtabelle schrumpft.
    • Das habe ich früher auch viel gemacht, und ehrlich gesagt vermisse ich die Fähigkeit, native UIs mit nativem Code zu entwickeln.
    • Frage dazu, warum es statt memset und memcpy überhaupt ZeroMemory und CopyMemory gibt: Warum hat man so etwas extra gebaut, statt einfach die vorhandene C-Standardbibliothek zu nutzen?
  • Früher hat man statt CreateWindow jedes Mal mühsam aufzurufen oft Dialog-Ressourcen in einer .rc-Datei beschrieben (Visual Studio hatte dafür auch einen Dialog-Editor) und dann CreateDialog verwendet. Dann werden alle Controls auf einmal erzeugt. Wenn man nur ein Application-Manifest hinzufügt, bekommt man auch modernen UI-Stil und Unterstützung für hohe DPI.
    • Mit dieser Methode bekommt man auch Tastaturfunktionen wie Tab-Navigation zwischen Controls automatisch. Größenänderungen muss man zwar weiterhin selbst behandeln, aber den Code dafür kann man schnell ergänzen und es ist nicht schwer.
    • Diese Methode kommt auch im Petzold-Buch vor, daher würde ich empfehlen, sie sich einmal anzusehen.
  • Ich habe früher etwas Ähnliches für Linux in unter 2 KiB Assembler umgesetzt. Wenn man es in C schreibt und dynamisch linkt, kommt man unter Linux leicht auf unter 20 KiB. Bei Windows dürfte es dank vieler eingebauter Funktionen sogar noch einfacher sein. Deshalb möchte ich solche Versuche ausdrücklich unterstützen. Wenn man sich die Linker-Optionen am Ende des Artikels ansieht, lässt sich die Größe vermutlich noch weiter reduzieren.
  • Wenn man es ohne Framework baut, hat man bei DPI-Skalierung unscharfe Schrift, keine Tab-Unterstützung, keine grundlegenden Features vormoderner Frameworks wie Ctrl-A zum Markieren im Textfeld, Fehler beim Hinzufügen von Zeilen und so weiter. Inwiefern das in irgendeinem Sinn "modern" sein soll, ist mir unklar.
    • Hier ist ein Beispiel für DPI-Awareness-Konfiguration: Der Code versucht je nach Version, die DPI-Erkennung über verschiedene Windows-Funktionen zu setzen (user32:SetProcessDpiAwarenessContext, shcore:SetProcessDpiAwareness, user32:SetProcessDPIAware). Bei wirklich alten Versionen wird gar nichts aufgerufen.
    • Das Wort modern passt wirklich nicht, denn es ist nur groß, aber funktionsarm (Tab-Navigation zwischen Controls lässt sich übrigens leicht selbst implementieren).
  • Als jemand, der auf dem 6502 programmiert hat, tut es weh, dass heute sogar 278 KB schon als leichtgewichtig gelten.
    • Ich habe mir einmal die Binärgröße angesehen, und das erste Hindernis war, dass build.bat mit core.autocrlf=false nicht korrekt funktionierte. Nachdem ich das auf core.autocrlf=true geändert und das Repo neu geklont hatte, ließ es sich bauen. Ein bestimmtes mingw-Toolchain erzeugt eine .exe von 102 KB, also deutlich effizienter als 278 KB. Wenn man noch weiter reduzieren will, kann man GCC zusätzliche Flags geben: Mit gcc -s -Oz -flto sind sogar 47 KB möglich. Wenn es nur um Binärgröße geht, gibt es noch viel Spielraum.
    • Dass die Größe in diese Region kommt, liegt an der Plattform und am ausführbaren Dateiformat. Dinge wie Stack-Trace-Informationen, Infrastruktur für dynamisches Linken und Exception-Handling-Tabellen belegen eben Platz.
    • Ich würde gern beantragen, bei Demoparty-Wettbewerben eine Kategorie „64KB TODO-App“ einzuführen.
    • Ehrlich gesagt überrascht mich die Größe. Ich erinnere mich, dass so etwas früher kleiner war, wenn man die Icons herausrechnet. Vielleicht liegt es an MinGW?
    • 6502? Das ist Luxus. Zu meiner Zeit gab es oft gar keine CPU.
    • Das erinnert mich an die Zeit, als win32-Assemblerprogrammierung plötzlich populär wurde, besonders als Shareware-Downloads immer größer wurden. Ich muss auch an frühe Palm-Pilot-68k-Programmierung denken. Es war wie das letzte Aufflackern nicht-retroartiger Assemblerprogrammierung.
    • Jemand hat sogar eine quickrun.exe mit 15 KB gebaut, nur mit C und der puren Win32-API. Kein besonderer Trick zur Größenreduktion, kompiliert mit Mingw32; es ist eine GUI-App, die Programme per Alias schnell startet.
    • Heute Abend debugge ich meinen Emulator, der ein Z80-System mit 64 KB RAM emuliert. Manchmal merkt man erst dann wieder, wie sehr sich Zeit und Umgebungen verändert haben. Andererseits haben wir im Gegenzug zum Größenwachstum auch enorme Fortschritte gemacht.
    • Allein ein Adresszeiger ist von 8-Bit- zu 64-Bit-Architekturen achtmal größer geworden. Also nicht klagen, sondern diese Veränderung als Kunst würdigen.
    • 278 KB sind gerade klein genug, um auf eine 5 1/4-Zoll-Diskette zu passen.
  • Diese App wurde einfach aus Spaß direkt selbst gebaut. Wie in den Kommentaren erwähnt, wäre es vernünftiger gewesen, C++ oder eine andere Sprache zu verwenden, aber mir ging es einfach darum, es auszuprobieren.
    • Vor etwa 30 Jahren habe ich mein erstes Windows-Programm fast genauso gebaut. Der Unterschied war nur, dass ich einen C++-Compiler benutzt habe. Damals war es in der offiziellen Dokumentation sogar die empfohlene Methode, Code im C-Stil mit einem C++-Compiler zu schreiben. C++ galt als Obermenge von C, und Microsoft tendierte auch in diese Richtung.
    • Ehrlich gesagt würde ich deine App viel lieber benutzen als die standardmäßige To-do-App von Windows 11.
    • Wenn man die win32-API verwendet, ändert sich das Wesentliche unabhängig von der Sprache kaum. Ein Sprachwechsel kann eher noch mehr Verwirrung stiften. Wenn man zu sehr auf C++-Stil fixiert ist, kann das für jemanden, der die win32-API gerade erst lernt, eher zusätzliche Verwirrung schaffen. Sich mit einem so einfachen/netten Nebenprojekt an die win32-API zu gewöhnen, halte ich als Entwickler für gute Grundlagenarbeit.
    • Ich habe übrigens noch eine andere App namens YoutubeGO; ich würde mich freuen, wenn du sie dir auch ansiehst.
    • Solche sauberen Projekte mit nativer UI waren auch für mich der Grund, warum ich überhaupt Programmieren lernen wollte, daher kann ich das gut nachvollziehen und nur loben.
  • In Web- und Softwareprojekten lädt man oft Megabytes an JS oder C#, nur um 278 KB Telemetrie zu verschicken; so ein Versuch ist da erfrischend.
    • Eine ähnliche App in C# + WinForms ist auf der Festplatte kleiner als 10 KB und braucht nur 6 MB RAM. Diese App hier braucht 1,5 MB RAM. Beide starten sofort.
  • Im Moment sieht es so aus, als wäre eine statische Bibliothek gelinkt worden. Wenn man stattdessen gegen DLLs linkt, könnte man die App-Größe drastisch verringern.
    • Das stimmt eher nicht. Wenn man die DLLs mitliefern muss (weil sie nicht im OS enthalten sind), bringt jede DLL ihre eigene C-Runtime mit und bläht alles eher auf. Wenn man alles statisch in eine EXE packt, hat man die C-Runtime nur einmal, und ungenutzte Funktionen lassen sich leicht entfernen. DLLs sparen nur dann Platz, wenn mehrere Programme dieselbe DLL gemeinsam nutzen.
    • Die CRT (Runtime Library) statisch zu linken hilft sogar eher dabei, unnötigen Code zu vermeiden. Wenn man DLLs dynamisch linkt, muss man unter Umständen zusätzlich die VCRUNTIME-DLLs von Microsoft installieren. In Visual Studio ist dynamisches Linken gegen MSVCRT auch nicht unbedingt einfach. Eine Ausnahme ist natürlich, wenn man LGPL-Konformität erfüllen muss.
  • Das erinnert mich an den kürzlich erschienenen schnellen Datei-Explorer File Pilot, der in C geschrieben wurde und nur 1,8 MB groß ist.
  • Es wird als „moderne“, „native“ Windows-Todo-App bezeichnet, aber ich frage mich wirklich, was daran modern sein soll. Und wenn man es in C++ schreibt, könnte man viele Probleme vermeiden und auch globale Variablen loswerden. Mit std::string, std::array, std::list, anonymen Namespaces und ohne malloc würde der Code halb so lang und weniger fehleranfällig werden.
    • Die globalen Variablen haben in einer App mit knapp 500 Zeilen kaum Auswirkungen, und ihr Zweck ist klar. Zu glauben, dass beim Ersetzen durch std::string oder std::list am Ende dieselbe Assemblerausgabe herauskommt, zeigt, dass man die internen Abläufe nicht wirklich versteht.
    • Sogar die neueste Ausgabe des Petzold-Buchs wird im C++-Modus mit Visual C++ gebaut und empfiehlt gemeinsame C/C++-Syntax. Schon zu Windows-95-Zeiten haben nur noch wenige Leute fast ausschließlich in C geschrieben; Mainstream-Sprachen waren da längst VB, Delphi und C++.
    • Zu dem Vorschlag, Standard-string oder array/list zu verwenden: Wenn man schon direkt mit der winapi arbeitet, dann passt LPWSTR (Wide String) besser zur API als std::string und ist eher zu empfehlen. Jedenfalls eher als altmodische char[]-Methoden. Ich glaube nicht, dass std::array oder list den Code wirklich besser machen würden.
    • Modern ist es nicht, aber Programmierung näher an den Grundlagen hilft dabei, die eigentlichen Abläufe richtig zu verstehen. Die Probleme sind außerdem einfach genug, um leicht verständlich zu sein. Ich halte das für ein gutes Lernprojekt. Mich würde auch interessieren, wie eine Assembler-Version einer solchen App aussehen würde.
 
roxie 2025-05-16

Leute, ich habe das Gefühl, als würde ich den Atem der alten Hasen bis hierher spüren ...