10 ポイント 投稿者 GN⁺ 2025-05-12 | 3件のコメント | WhatsAppで共有
  • Simple Todoは、CとWin32 APIで作られたネイティブWindows Todoアプリで、Windows GUIプログラミングとシステム統合機能を示すプロジェクト
  • 主な機能は、Todoの作成・編集・削除、完了表示、システムトレイへの最小化、Windows自動起動オプション、大文字小文字を区別しないリアルタイム検索
  • データは%APPDATA%\TodoApp\todos.datバイナリファイルとして保存され、Todo一覧全体を保持し、最大100件のTodoをサポート
  • ビルドにはWindowsでMinGW-w64とWindows SDKが必要で、リポジトリをクローンした後に.\build.batでコンパイル
  • 構成はmain.ctodo.cgui.cutils/search.cに分かれており、GUIはWin32 API・Common Controls・UXThemeを使用

プロジェクトの性格

  • Simple Todoは、CとWin32 APIで書かれたネイティブWindows Todoアプリケーション
  • 軽量なネイティブ実行ファイルを目指しており、Windowsの標準的なルック&フィールと最新のWindowsビジュアルスタイルを使用
  • GUIはWin32 APIベースで、システムトレイ統合とファイル入出力によるデータ永続化を含む

Todo機能

  • Todo項目を作成、編集、削除できる
  • タスクを完了状態として表示できる
  • 各Todoに優先度を設定できる
  • 検索機能はリアルタイムで動作し、大文字小文字を区別しないマッチングをサポート
  • アプリをシステムトレイに最小化できる
  • Windows起動時に自動実行するオプションを提供

データ保存方式

  • Todoデータは%APPDATA%\TodoApp\todos.datに保存される
  • 保存形式は、Todo一覧全体を保持するバイナリファイル
  • 保存容量は最大100件のTodoに制限される

ビルドと実行

  • 要件
    • Windows OS
    • MinGW-w64 GCCコンパイラ
    • MinGWに含まれるWindows SDK
  • ビルド手順
    • MinGW-w64をインストールし、binディレクトリをシステムPATHに追加
    • リポジトリをクローンした後、simple-todo-cディレクトリに移動
    • .\build.batを実行してビルド
  • 実行はbin/todo.exeで開始

実装構成

  • プロジェクト構造は機能別のCファイルに分かれる
    • src/main.c: アプリケーションのエントリポイント
    • src/todo.c, src/todo.h: Todo管理ロジックとデータ構造
    • src/gui.c: GUI実装
    • src/utils/search.c, src/utils/search.h: 検索機能の実装と宣言
    • src/app.manifest: Windowsビジュアルスタイル用のマニフェスト
  • 中核構成要素はWin32 API、Common Controls、UXTheme、File I/O
  • デバッグについては、Visual StudioやVS Codeなどで利用できるDEBUGGING.mdガイドを提供

ライセンスと貢献

  • ライセンスはMIT License
  • Pull Request形式での貢献を受け付ける

3件のコメント

 
aer0700 2025-05-13

ロマンがありますね

 
GN⁺ 2025-05-12
Hacker News のコメント
  • Win32 GUI プログラミングには、どこか不思議と気に入ってしまうところがある
    少し変わってはいるが、Raymond Chen のブログを読むと理由が見えてくる
    Win32 API は 8088 プロセッサの時代にルーツがあり、特定のやり方をするとコードを 40 バイト節約できたり、レジスタを 1 つ少なく使えたりする、といった背景がある
    昔 mingw と Petzold の本でおもちゃの GUI アプリをたくさん作ったが、カスタムコントロールの作成、グラフィックやテキストの描画、スクロール処理、ヒットテストなどはどれもかなり楽しかった
    アプリ内で strcpysprintf を使っているのが見えるが、真面目なプログラミングなら長さチェック付きの派生版を使うべきだ。コンパイラが警告を山ほど出していないのが不思議なくらいだ
    Win32 API には C 標準ライブラリにあるものを置き換える関数が多い。実行ファイルサイズを本当に削りたいなら、C 標準ライブラリなしでアプリを書くこともできる。memset() の代わりに ZeroMemory()memcpy() の代わりに CopyMemory() がある
    ある時点から純粋な C コードを直接書くのはつらくなるが、最初の数回は純粋な C でやってみるのが学習には一番だと思う。細部を自分で管理すると、学んでいる間に内部で何が起きているのか感覚をつかみやすい
    Win32 GUI プログラミングをもっとやってみたいなら、WTL(Windows Template Library) を見ることを勧める。Win32 API を包んだ C++ ラッパーなので、動作をはるかに理解しやすくなる

    • AlomWare Toolbox という素晴らしい PC アプリを使っているが、Win32 設計の典型に近い(https://www.alomware.com/images/tab-automation.png
      機能が非常に多いのに、そのおかげでサイズは約 3MB しかない。フレームワークもなく、単一の実行ファイル 1 つだけなので、すべてのソフトウェアが今でもこんなふうならいいのにと思う
    • 最近は少なくとも strcpy の代わりに strncpy を使わないと、人間はもちろん AI ツールにまで、そんなことを続けるなと言われるのを覚悟しなければならない
      個人的に Zig を使う主な理由の 1 つは、こうしたよくある落とし穴をかなり減らしてくれる点だ。もちろん C も悪くない
    • memset() の代わりに ZeroMemory()memcpy() の代わりに CopyMemory() があると言ったが、MSVC intrinsicrep stos/movs 命令を使いそうだ
      関数呼び出しより小さくなる可能性がある。関数呼び出しにはインポートテーブル項目のサイズも含まれるためだ
    • そういう作業をたくさんやったし、正直、ネイティブコードでネイティブ UI を開発できた能力が懐かしい
    • fopen/fread/fclose も使っているが、CreateFile/ReadFile/WriteFile のような Win32 API は使っていないようだ
  • コントロールごとに苦労して CreateWindow() を呼ぶ代わりに、伝統的には .rc ファイルに ダイアログリソースを配置し(Visual Studio には今でもこれを視覚的に行うダイアログエディタがある)、CreateWindow() の代わりに CreateDialog() を使っていた
    こうすればすべてのコントロールを自動で作ってくれる。アプリケーションマニフェストを追加すれば、モダンな UI スタイルと高 DPI 対応も得られる

    • この方式なら、コントロール間の Tab 移動といくつかのキーボードショートカットも自動で得られる
      ただしサイズ変更をしたい場合は依然として手動で処理する必要があるが、たいてい簡単で、コードも数百バイトを超えない
    • C 標準ライブラリと OS ライブラリが重なっているのは、実質的には UNIX くらいで、Windows 1.0 が出た 1985 年には標準と呼べるものもまだなかった
      K&R C はあったが、UNIX 以外の各 OS は提供する機能をそれぞれ好きに選んでいた
      さらに UNIX 系でないところでは、標準ライブラリはプラットフォームではなく C コンパイラが責任を持つという伝統があった
      そのため C ライブラリは Borland、Watcom、Symantec、Microsoft、Green Hills、Zortech などが提供していた
      Mac OS も MPW が出るまでは同様で、IBM や Unisys、マイクロコンピュータとメインフレーム、OpenVMS 以前の VMS もそうだった
      Windows 10 以降には Universal C Runtime もある
    • このアプローチは、まともな 外部関数インターフェース(FFI) を持つ言語へ簡単に移植でき、それ以外は何も必要ない
      リソースコンパイラも、リソース DLL を作るリンカも不要だ
      リソースファイルとそのバイナリ形式は良い API ではない
      そこそこ高水準の言語で CreateWindow 呼び出しを持っているなら、その言語になじむリソース風の DSL をメタプログラミングで作れそうだ
  • 以前 Linux で、似たようなものをアセンブリで 2KiB 未満に収めて作ったことがある: https://gaultier.github.io/blog/x11_x64.html
    他の人たちが言っているように、純粋な C で作って動的リンクを使えば、少なくとも Linux では 20KiB 未満に簡単に収められる。Windows は OS に標準で含まれているものがずっと多いので、さらに簡単かもしれない
    いずれにせよ、試み自体は応援したい。私の記事の最後に書いておいたリンクオプションを試すと、サイズ削減の助けになるはずだ

    • 少し拡張した ncurses ベースの TUI TODO プログラムが 15K だ。Linux で、静的リンクではない
      まだ musl で ncurses をビルドできてはいない
  • 「ネイティブな Windows のルック&フィール」と言うので、実際に実行するまでは、リストビューに コンテキストメニューがあり、特定のフィールドをダブルクリックするとインラインに近いドロップダウンメニューやテキストボックスが出るものだと期待していた
    今では Win32 UI/UX に合わせてプログラムを設計する方法を知らないのか、それとも自分が年を取りすぎただけなのか分からない

  • 「フレームワークなし」と言っていたのに、DPIスケーリングでフォントがぼやけ、Tabサポートもなく、テキストフィールドでCtrl-Aによる全選択もできず、現代以前のフレームワークでさえ提供していた多くの機能が欠けていて、行追加時にもエラーが出る
    「モダン」とはどういう意味なのか分からない

    • DPI認識設定の例はここにある: https://github.com/Dwedit/GameStretcher/blob/master/Stretche...
      このコードは user32:SetProcessDpiAwarenessContextshcore:SetProcessDpiAwarenessuser32:SetProcessDPIAware のうち利用可能なものを動的に確認して呼び出す
      Windowsのバージョンが非常に古く、これらのどれも実装していない場合(Windows XP以前)は、何も呼び出さない
    • 「モダン」というのは、必要以上にずっと大きいのに機能はたくさん抜けている、という意味らしい
      抜けていると指摘した機能の大半、特にコントロール間のTab移動などは、追加するのがとても簡単
  • 私の中の6502プログラマーは、278KBが今では軽量と見なされるのを見て、内心死にかけている

    • このバイナリを再現して、278KBがどこに使われているのか見ようとした
      最初にぶつかった障害は、gitが core.autocrlf=false に設定されていると build.bat ファイルが動作しないことだった。core.autocrlf=true に変えて再クローンしたらビルドできた
      ツールチェーンは https://github.com/niXman/mingw-builds-binaries/releases/tag...x86_64-15.1.0-release-win32-seh-msvcrt-rt_v12-rev0.7z を使った
      この場合、102KBの .exe ファイルができる。主張されている278KBより出だしからずっと良い。作者が別のツールチェーンや設定を使っている可能性もあるので、正確な再現手順があるとありがたい
      GCCにいくつかスイッチを渡すとさらに小さくできる
      gcc -Os => 100KB
      gcc -Oz => 99KB
      gcc -flto => 101KB
      gcc -s => 51KB
      gcc -s -Oz -flto => 47KB
      小さな .exe サイズだけが関心事なら、まだ改善の余地は大きい
    • かなりの部分はプラットフォームと実行ファイル形式のせい
      スタックトレース情報、動的リンクのインフラ、例外処理テーブルのようなものがなければ、はるかに軽くできる。例外処理テーブルは、例外がC関数をまたいで通過する可能性があるため、Cでも必要になる
    • デモシーンの大会に64KB TODOアプリ部門を作ってほしいと請願してみてもいいかもしれない
    • 正直、それほど大きいのは驚き。もっと小さいか、半分くらいはアプリアイコンが占めていると思っていた
      以前こういうものを書いたときは、もっと小さかった記憶がある。もしかしてMinGWのせいだろうか?
    • ふと、Win32をアセンブリでプログラミングするのがかなりヒップだった時代を思い出した
      おそらく、シェアウェアのダウンロードサイズが大きくなっていく流れへの反応だったのだろう。MFCの暗黒時代だった
      初期のPalm Pilot 68kプログラミングと並んで、レトロコンピューティングではないアセンブリの最後の歓声として記憶している
  • みんな、これはただ一度やってみて、面白半分で作ったアプリだ。コメントの指摘は正しいし、こういうものはC++や他の言語で作ったほうがもっと合理的だったと思う

    • 約30年前に初めてWindowsプログラムを作ったとき、私もまさにこういうやり方で学んだ。ただしC++コンパイラを使っていたはず
      理由はよく分からないが、Windows APIのドキュメントはCスタイルのコードをC++コンパイラで使うよう案内していたように思う。MicrosoftはC++をCの改良された上位集合と見なし、Cスタイルのコードにも使うのが正しいと考えていたのだろう
    • 冗談ではなく、標準のWindows 11 TODOアプリよりこのアプリを使いたい
    • これはまさにそうあるべきやり方だ
      Win32 APIを使うなら、他の言語が根本的に変えることはなく、むしろさらに分かりにくくすることがある
      人々はC++的な考え方に陥りがちで、Win32 APIに慣れていない人にとっては、全体をさらに混乱した塊にしてしまっただろう
      とてもかわいい試みだし、他の人がどう思おうと、Win32 APIへの慣れは悪くない基礎スキルだ
    • https://github.com/Efeckc17/YoutubeGO 参考までに、このアプリケーションもレビューしたり見てくれたりすると本当にうれしい
    • こういうすっきりしたネイティブUIプロジェクトが、私にプログラミングを学ばせた動機だった。素晴らしい
  • ここでは、ユーザーがマウスを動かすたびに278KBのテレメトリを送るために数MBのJSやC#をロードするソフトウェアやWebサイトを作っている人たちが、かなり皮肉を言っているのが見える

    • C# + WinFormsで作った同様のアプリはディスク上では10KB未満で、RAMは6MB使う
      このアプリはRAMを1.5MB使い、どちらも即座に起動する
  • Windows アプリを開発するとき、すべてを C でできるという事実はいつも気分を良くしてくれる
    最も低レベルな基本要素から積み上げていくやり方が、ただ自然に感じられる
    一方 macOS ではすべてが Objective-C オブジェクトなので、純粋な C でアプリを書くことはできるものの、Apple が意図した抽象化より一段下に降りることになり、ほとんど 1000 倍冗長になる。何かをするには、事実上 Objective-C のクラス階層を操り人形のように操作しなければならず、本当に気持ち悪い
    なぜ Objective-C のクラスベース API を、基本的な Win32 風の手続き型 API の上に改めて載せられないのか分からない。技術的には Win32 も「クラスベース」ではあるが、少なくとも最小限だ
    だから当面、自分の C コードを macOS に移植しようという気にはあまりならない

    • 興味深いことに、Windows には COM、そして COM の上に作られた WinRT という独自の オブジェクト指向 API/ABI がある
      ただし COM インターフェース用の MIDL コンパイラは、C でインターフェースを使い実装できるヘッダーを生成してくれる: https://www.codeproject.com/KB/com/com_in_c1.aspx
      自分の知る限り、WinRT でも現在なお動作する。ただし生成される C の構造体と関数名は名前空間全体を含める必要があるため、非常に不格好になる: https://stackoverflow.com/a/7437006/111335
    • 手続き型 API は存在していて、名前は Carbon だった
      user32.dll が露骨に真似た Macintosh Toolbox API の、ほぼドロップイン置換だった
      ただしプログラマーが感じる温かみを除けば、UI は本当にオブジェクト指向を求める。共通の振る舞いを一部共有する異なるオブジェクトのツリーは、UI コードの 99% をほぼそのまま説明している
      Toolbox と Carbon はそうしたオブジェクト指向性が不足していたため、かなり無理があった。Windows が実際に加えた独創的な考えの一つが、まさにウィンドウクラスだった
      個人的には、C から Objective-C クラスを直接扱うときに感じる違和感を、user32.dll コードでウィンドウクラスやウィンドウプロシージャを定義しなければならないたびに感じる[0]
      UI がオブジェクト指向を求めるように、オブジェクト指向は専用の言語機能を求める。なくても作ることはできるが、使い勝手は 2000 倍悪くなる
      [0] あるいは GTK/GObject に関するすべて
  • 静的ライブラリにリンクしているように見える
    静的ライブラリではなく DLL にリンクすべきで、そうすればアプリケーションサイズは大きく減る

    • それは逆に見える
      いずれにせよ DLL をプログラムと一緒に配布しなければならないなら(OS の一部ではないので)、DLL には全機能が入っていて、各 DLL が独自の C ランタイムまで持つ可能性もある
      すべてを単一の EXE に静的コンパイルすれば、C ランタイムは一つだけで、使っていない関数も簡単に削除できる
      DLL は、そのコードが複数のプログラム間で共有される場合にだけサイズを減らしてくれる
    • いや、CRT の静的版にリンクするのは良い選択だ。使っていないコードを削れる
      MSVCRxx/VCRUNTIME に動的リンクすると、ユーザーが Microsoft からまさにその DLL をダウンロードする必要がある
      MSVCRT に動的リンクすればその問題はないが、Visual Studio では非常にやりにくい
      CRT に静的リンクすると本当に問題になるのは、LGPL 準拠くらいだと思う
 
roxie 2025-05-16

兄貴たちの鼻息がここまで伝わってくるような感じ…