memopad 備忘録

step3:完成品のフィードバック対応

v0.1.0 を実際に使って出たフィードバックを 1 件ずつ記録し、対応版をリリースしていく。フィードバックが 0 件になった時点を v1.0 とする。着手日:2026-09-07

目次
  1. フィードバック一覧と対応バージョン
  2. FB-1:IME 入力のレスポンスが悪い(v0.2.0)
  3. FB-2:上部の高さと配置をメモ帳に合わせる(v0.3.0)
  4. FB-3:「背景色」「文字色」を「色変更」に統合(v0.4.0)
  5. FB-4:目に優しい配色 16 パターン(v0.5.0)
  6. FB-5:タブをクリックしても切り替わらない(v0.5.1)
  7. FB-6〜8:表示の細かな崩れ 3 件(v0.5.2)
  8. FB-11:起動がメモ帳よりワンテンポ遅い(v0.7.0)
  9. Windows Forms への作り直しと MemoPad への改名(v0.8.0)
  10. FB-13:タイトル行でウィンドウを動かせない(v0.9.0)
  11. FB-14・FB-15:メニューの見た目(v0.9.0/v0.9.1)
  12. FB-16:ドラッグ&ドロップで開くと編集済みになる(v0.9.1)
  13. FB-17:ホイールで最後の行まで届かない(v0.9.2)
  14. FB-18:折り返しなしで横にスクロールできない(v0.9.2)
  15. FB-19:大きなファイルで固まる(v0.10.0 で本文を Scintilla に変更)
  16. FB-21:開いた直後だけウィンドウ移動がカクつく(原因の特定)
  17. FB-21 の対策:移動・リサイズ中は折り返しの計算を止める(v0.10.1)
  18. FB-22:端末を起動して最初の起動だけ遅い(v0.10.2)
  19. FB-23:行頭のカーソルが見づらい(v0.10.2)
  20. FB-24:メニューのアクセス キーが効かない(v0.10.2)
  21. FB-25:それでも最初の起動に「ひと呼吸」ある(見送り)
  22. FB-26:配色パターンの画面が 1920×1080 で切れる(v0.10.3)
  23. FB-27:インストーラのアイコンを v0.5 までのものに戻す(v0.10.3)
  24. FB-28:ボタンの名前が読み上げられない(v0.10.3)
  25. FB-29:「プログラムから開く」の候補を .txt 以外にも広げる(v0.10.4)
  26. FB-30:Ctrl+Z で取り消す単位がおかしい(v0.10.5)
  27. FB-31・32:右クリックの「MemoPad で開く」と .bat(v0.10.5)、署名の申請と最初の階層の見送り
  28. つまずいた点のメモ

1. フィードバック一覧と対応バージョン

初回フィードバック(2026-09-07 受領)は 4 件。種類ごとにバージョンを分けて対応する。

番号種類内容対応版状態
FB-1改善IME 使用時にキーを打ってから一呼吸置いて文字が表示される。メモ帳と比べて明らかに遅いv0.2.0対応済み
FB-2修正ウィンドウ上部からエディタまでの高さをメモ帳と同等にし、上からタブ→メニュー→エディタの順に並べる。+ボタンはタブ名の近くに。フッターの高さもメモ帳に合わせるv0.3.0対応済み
FB-3変更「背景色」「文字色」を「色変更」メニューに統合し、1 つのウィンドウで両方を変えられるようにする。16 色は PICO-8 パレットに置き替えるv0.4.0対応済み
FB-4機能追加背景色と文字色を一度に変える「目に優しい配色」16 パターンを、プレビューを見て選べる方式で追加するv0.5.0対応済み

2 回目のフィードバック(2026-09-07、v0.5.0 のインストール確認時)。IME の体感速度、配色パターン、色変更、ドラッグ&ドロップは OK との確認をもらった。

番号種類内容対応版状態
FB-5不具合タブをクリックしても切り替わらない(メニューと文字入力は動く)v0.5.1対応済み

3 回目のフィードバック(2026-09-07、v0.5.1 で「タブ切り替えは改善された」との確認とあわせて)。

番号種類内容対応版状態
FB-6不具合検索・置換バーの閉じるボタン(×)が欠けて見えるv0.5.2対応済み
FB-7不具合配色パターンのプレビュー枠で英語の行が見えないv0.5.2対応済み
FB-8不具合右上の閉じる/最小化/最大化ボタンにマウスを載せると「□□□」と文字化けして見えるv0.5.2対応済み

4 回目のフィードバック(2026-09-07、v0.5.2 は OK との確認とあわせて)。

番号種類内容対応版状態
FB-9変更アプリ アイコンをユーザー提供のデザイン(緑の文書に白い罫線、16〜256 px の 7 サイズ入り .ico)に変更するv0.6.0対応済み
FB-10公開GitHub Pages の公開、README.md の追加、アプリの紹介ページの作成v0.6.0対応済み

5 回目のフィードバック(2026-09-08)。

番号種類内容対応版状態
FB-11性能メモ帳に比べて起動がワンテンポ遅い。軽いエディタとしての使い勝手に関わるので、メモ帳並みに短縮したいv0.7.0 → v0.8.0対応済み
FB-12変更FB-11 が達成できたら、名称を memopad から MemoPad に改め、アイコンをユーザー提供の新デザインに差し替えるv0.8.0対応済み

FB-11 はまず WPF のままできる対策を v0.7.0 で入れたが、約 520 ms → 505 ms にしかならず「メモ帳並み」には届かなかった。空の WPF ウィンドウでも約 400 ms かかることが計測で分かったため、メイン ウィンドウを Windows Forms に作り直して v0.8.0 で 221 ms(メモ帳は 211 ms)にした(詳細は 8 節と9 節)。これを受けて FB-12 の改名とアイコン差し替えも v0.8.0 で行った。

6 回目のフィードバック(2026-09-08、v0.8.0 のインストール確認時)。起動の速さは「体感で速くなった、OK」、大きなファイルを読み込んだときの動作も「以前は緩慢だったが現バージョンは軽快。ここは重要なので変えないでほしい」との確認をもらった。

番号種類内容対応版状態
FB-13不具合ウィンドウ上部のドラッグでウィンドウを動かせない。上部をダブルクリックしても最大化/元のサイズに戻らない(v0.7.0 まではできていた)v0.9.0対応済み
FB-14確認ヘルプの「MemoPad について(A)」の (A) は何か—変更なし
FB-15改善サブメニューの文字がやや上付きで、字下げが全角 2 文字分ほどあり右に寄りすぎている。全体的に整えてほしいv0.9.0対応済み

FB-13 は Windows Forms への作り直し(v0.8.0)で持ち込んだ不具合で、調べるとウィンドウ四辺のリサイズも同じ原因で効かなくなっていた(10 節)。FB-14 の (A) はメモ帳と同じアクセス キー表記なのでそのままにし、FB-15 のメニューはメモ帳と同じ位置に整えた(11 節)。「軽快さを変えないでほしい」という要望に対しては、本文の読み込みと描画には手を入れていないことに加え、13.4 MB(12 万行)のファイルを 0.42 秒で開けることを再計測して確かめた。

7 回目のフィードバック(2026-09-08、v0.9.0 の確認時)。メニューの文字の上下位置は OK との確認をもらった。

番号種類内容対応版状態
FB-15(続き)改善チェックの付かないメニューも、メモ帳では少しだけ字下げされている。アイコンが無いメニューは少し字下げするのが基本v0.9.1対応済み
FB-16不具合既存のファイルをドラッグ&ドロップして開くと、最初から編集された状態(タイトルの * とタブの●)になるv0.9.1対応済み

v0.9.0 では字下げを浅くしすぎて、文字がポップアップの左端に寄っていた。メモ帳と同じ控えめな字下げに直した(11 節)。FB-16 は RichEdit が自分でもドロップを処理してしまうのが原因で、ドロップ先を自前のものに差し替えて直した(12 節)。

8 回目のフィードバック(2026-09-11、v0.9.1 の確認時)。メニューの字下げと、ドラッグ&ドロップで開いた直後の状態は OK との確認をもらった。

番号種類内容対応版状態
FB-17不具合長いファイルをマウス ホイールでスクロールすると、最後の改行まで届かない(メモ帳では届く)。些細なことだが気になるv0.9.2対応済み
FB-18不具合(FB-17 の確認中に発見)「右端で折り返す」をオフにしても横スクロールバーが出ず、長い行の右端から先が見えないv0.9.2対応済み

FB-17 は Windows 標準の RichEdit のホイール処理の癖が原因で、ホイールだけ MemoPad が自前で送るようにした(13 節)。FB-18 は確認の途中で見つけた v0.2.0 からの不具合で、了承をもらって同じ版で直した(14 節)。「起動時間が増えないか」という確認の依頼には、修正前後の起動時間と大きなファイルの読み込み時間を実測して、変わらないことを示した(13.4)。

9 回目のフィードバック(2026-09-12、v0.9.2 を試用して)。FB-17・FB-18 の修正自体は問題なかったが、大きなファイルで固まる現象がそれ以上に重い問題として上がった。

番号種類内容対応版状態
FB-19不具合10 MB/12 万行の日本語ファイルを開くと、「右端で折り返す」のオン・オフに関わらず非常に長い時間固まる(一応は開く)。さらに一度固まったあとは、新規タブ・ウィンドウ移動・最大化など操作の種別に関わらずそのつど固まるようになるv0.10.0対応済み
FB-20対処固まる現象を抱えたままの版はエディタとして機能を満たしていないため、公開中のインストーラの配布を一旦停止する(サイトの公開は継続)—対応済み

FB-19 は 8 回目のフィードバックの確認中にこちらでも把握していたもので、そのときは「折り返しオンで約 44 秒、オフなら約 3 秒」と報告していた。実機ではオフでも長時間かかり、報告した数字が実際と違っていた。対処として先に配布を止め、そのうえで原因を調べた。原因は本文に使っていた RichEdit が、日本語と英字の混ざった行の文字幅を GDI に何度も問い合わせることだった。RichEdit の設定でも、Win32 の Edit コントロールでも、Windows App SDK の WinUI でも避けられなかったため、v0.10.0 で本文のエディタを Scintilla に差し替えた。最長の無応答は 43,456 ms → 34 ms になった(15 節)。

10 回目のフィードバック(2026-09-13、v0.10.0 の確認時)。FB-19 の「開く速度」と「その後も固まる」はどちらも解消したとの確認をもらい、インストーラの公開も可となった(同日、v0.10.0 で配布を再開)。あわせて 1 件の確認を受けた。

番号種類内容対応版状態
FB-21確認→不具合大きなファイルを開いた直後だけ、すぐにウィンドウを移動させるとカクカクする(3 秒ほど待てばスムーズ)。遅延読み込みの処理をそちらに回しているためではないか。まず原因が知りたい(すぐの修正は不要)v0.10.1対応済み

推測のとおりだった。Scintilla は折り返し位置の計算を 10 ms ずつに区切って空き時間に進めるため、その約 4 秒の間はウィンドウの移動が計算と順番待ちになる。別プロセスからの計測で、忙しい区間(0.4〜4.2 秒)の応答時間の中央値が 9.7 ms=ちょうど 1 回分の計算時間であること、折り返しオフではこの区間が存在しないことを確かめた(15.9)。そのうえで対策 4 案を入力レスポンス・起動時間と合わせて実測し、「ウィンドウの移動・リサイズ中だけ計算を止める」案を v0.10.1 で採用した(ドラッグ中の位置の更新間隔 71.4 ms → 16.7 ms。入力・起動・見た目は変化なし。16 節)。

11 回目のフィードバック(2026-10-28、v0.10.1 をしばらく使って)。2 件を受け、確認の途中で 1 件を見つけた。

番号種類内容対応版状態
FB-22性能端末を起動して最初にアプリを起動するときだけ時間がかかる。一度起動すれば、終了して再度起動してもすぐに起動する。いくつかの端末で同じv0.10.2対応済み(速くなったことを確認。残りは FB-25)
FB-23改善カーソル(キャレット)が画面の左端にあると見づらく、使っている途中で何度も位置を見失う。単純にカーソルの太さを変えればよいのかv0.10.2対応済み(2026-10-31 に確認)
FB-24不具合(確認中に発見)本文にフォーカスがあると、メニューを開いてもアクセス キー(Alt+O → F など)が効かず、メニューが閉じて本文に文字が入る。v0.10.0 からv0.10.2対応済み(2026-10-31 に確認)

FB-22 は、起動時に使わない WPF 本体(約 23 MB)を読み込んでいたことと、Scintilla の DLL の電子署名を毎回検証していたことが重なっていた。どちらもやめて、起動時に読む DLL を 76.8 MB → 50.9 MB に減らした。ディスク キャッシュに載っていない状態からの起動は 902 → 737 ms(ウィンドウ表示)になった(17 節)。FB-23 は太さではなく左の余白が灰色で塗られていたのが主因で、濃い配色だと行頭のキャレットが灰色の帯の縁にまぎれていた。余白を背景色で塗り、幅もメモ帳に合わせ、Windows の「テキスト カーソルの太さ」にも従うようにした(18 節)。FB-24 は FB-22 の確認中に見つけた。IME が押した文字を本文へ直接送ってしまうためで、メニューを開いている間だけ本文の IME を切り離して直した(19 節)。IME は Meltype と Microsoft IME の両方で確かめた。

12 回目のフィードバック(2026-10-30、v0.10.2 をいくつかの端末で確かめて)。FB-22 は「体感的には速くなった」との確認をもらったうえで、3 件を受けた。確認の途中で 1 件を見つけた。

番号種類内容対応版状態
FB-25性能速くはなったが、クリックしてからひと呼吸おいて起動する感じは残り、メモ帳と比べると初回起動の遅さが気になる。ただし、改善で今の機能やほかの項目に悪影響が出るなら、このままでもよい(最低限は許容できる)—見送り(現状で許容)
FB-26不具合1920×1080 の液晶の端末では、配色パターンの画面の下が切れてボタンが押せないv0.10.3対応済み(2026-10-31 に確認)
FB-27変更インストーラのアイコンは、v0.5.0 までのアイコンのほうがインストーラだと分かるので元に戻したいv0.10.3対応済み(2026-10-31 に確認)
FB-28不具合(確認中に発見)アクセス キー付きのボタン 8 個(「その他の色(M)...」「保存しない(N)」など)の名前が、読み上げソフトや UI Automation から空に見える。v0.4.0 からv0.10.3対応済み(2026-10-31 に確認)

FB-25 は、起動時に読む 51.0 MB のうち 41.6 MB(82 %)が .NET ランタイムと Windows Forms 本体で、アプリの作りを変えずに削れる分は 4 % ほどしか残っていなかった。残る手は「サインイン時に先読みしておく」だが、サインインのたびに裏で仕事をさせることになる。ユーザーと相談し、今のままにした(20 節)。FB-26 は、1920×1080・表示スケール 150 % だと画面の作業領域が論理 672 px しかないのに、配色パターンの画面が 752 px あったのが原因。カードと余白を詰めて 611 px にし、さらに低い画面ではパターンの一覧だけがスクロールするようにした(21 節)。FB-27 はインストーラにアプリのアイコンを付ける指定を外し、Inno Setup 標準のアイコンに戻した(22 節)。FB-28 は FB-26 の確認中に見つけた。アクセス キーのために使っている AccessText が原因で、名前を明示して直した(23 節)。

13 回目のフィードバック(2026-10-30、v0.10.3 を確かめている途中の質問から)。インストーラの「「プログラムから開く」の候補に MemoPad を追加する」は .txt だけが対象かと聞かれ、.txt だけだと答えたところ、ほかの拡張子にも広げたいとの要望を受けた。

番号種類内容対応版状態
FB-29変更「プログラムから開く」の候補に、.txt 以外(.log .ini .csv .md .c .cpp .py .js .ts .json .cs .php .html .css .xhtml と拡張子なし、ほかにプログラマがテキスト形式で使いそうな拡張子)も入れたいv0.10.4対応済み(2026-10-31 に確認)

FB-29 は、インストーラが候補に入れる拡張子を 1 種類から 83 種類(拡張子なしを含む)に増やした。既定のアプリは変えない(24 節)。v0.10.4 は FB-26〜28 の修正もそのまま含む。

14 回目のフィードバック(2026-10-31、v0.10.4 の確認と合わせて)。FB-23・24・26〜29 は確認が済み、v0.10.4 を公開した。そのうえで次の 3 件を受けた。v0.10.5 で対応して確認が済み、v0.10.5 も公開した。

番号種類内容対応版状態
FB-30不具合Ctrl+Z の取り消しがおかしい。既存のファイルでは 1 文字ずつ取り消されるのに、新規のファイルでは入力したものが全部消えるv0.10.5対応済み(2026-10-31 に確認)
FB-31変更.bat も「プログラムから開く」で MemoPad を選べるようにしたいv0.10.5対応済み(2026-10-31 に確認)
FB-32変更ファイルを右クリックしたとき、メモ帳の「メモ帳で編集」や VS Code の「Code で開く」のように、最初のメニューに「MemoPad で開く」があると使いやすいv0.10.5対応済み(2026-10-31 に確認。「その他のオプション」の中に出す。最初の階層は署名が得られず見送り)

FB-30 は、Scintilla が続けて打った文字を 1 つの単位にまとめるためで、メモ帳と同じく「単語とその後ろの空白」ごと、日本語は確定ごとに区切るようにした(25 節)。FB-31 は、.bat は v0.10.4 で候補に入れていたが、Windows が .bat の右クリックに「プログラムから開く」自体を出さなかった。FB-32 の「MemoPad で開く」で開けるようにした。Windows 11 で最初の階層に出すには信頼された証明書での署名が要るので、まず「その他のオプションを確認」の中に出し、並行して無料のコード署名(SignPath Foundation)を申請した。審査は知名度の条件で通らず、ユーザーと相談のうえ、最初の階層は見送った(26 節)。

FB-9 は src/memopad/memopad.ico を差し替え(アイコンを描いていた docs/tools/make-icon.ps1 は不要になったので削除)、インストーラにも SetupIconFile で同じアイコンを付けた。タイトル行の左端のアイコンは exe から取り出しているので自動で変わる。FB-10 はリポジトリ直下に README.md、docs/index.html に紹介ページを置き、GitHub Pages で main ブランチの /docs を公開した(紹介ページ:mrgarita.github.io/memopad、備忘録はその下の site/)。インストーラは GitHub Releases に置く。

2. FB-1:IME 入力のレスポンスが悪い(v0.2.0)

2.1 現象

「メモ帳に比べ、キータッチしたときに一呼吸置いてからエディタ部分に表示される。キー入力に表示が追い付いていない。シンプルなメモ帳でこのレスポンスの悪さは致命的」というフィードバック。IME(日本語入力)のときに特に気になるとのこと。

2.2 調べ方

体感を数値にするため、2 段階で計測した。

  1. アプリ内部の処理時間。環境変数 MEMOPAD_PERF=1 で有効になる診断ログ(Services/PerfLog.cs)を組み込み、キー押下(KeyDown)→本文変更(TextChanged)→描画(CompositionTarget.Rendering)の各時刻と、ウィンドウ メッセージ・Dispatcher の処理時間を %TEMP%\memopad-perf.log に記録した。
  2. 入力から画面に出るまでの時間。PowerShell から keybd_event でキーを 1 つ送り、画面上部をキャプチャし続けてピクセルが変わるまでの時間を測るスクリプトを書いた。IME は 半角/全角 キーを送って ON にし、「nihongo…」とローマ字を 1 文字ずつ送る。同じ手順をメモ帳にも当てて比べる。キャプチャ 1 回に約 18 ms かかるので、精度はその程度。

2.3 結果

アプリ内部は速かった。キー押下から本文変更まで 3〜5 ms、描画要求まで +2 ms で、115 KB のファイルでも変わらない。ステータスバー更新などの自前処理は 0.01 ms。つまり アプリのコードが遅いのではなかった。

一方、画面に出るまでの時間はメモ帳の 2.5 倍あった。WPF の TextBox は、UI スレッドが描画内容を作ってから別の描画スレッドが DirectX で描き、それを DWM(デスクトップ合成)が画面に出す、という 3 段階を経る。その途中で垂直同期に合わせて待つため、1〜2 フレーム(16〜33 ms)分の遅れが構造的に乗る。テーマ(Fluent)を外しても、ソフトウェア描画に切り替えても変わらなかった。

エディタIME 入力(ひらがな合成中)半角入力
メモ帳(Windows 11 Store 版)約 19 ms約 17 ms
memopad v0.1.0(WPF TextBox)約 50 ms約 31〜49 ms
WPF TextBox+Fluent テーマ無効約 50 ms—
WPF TextBox+ソフトウェア描画約 45 ms約 41 ms
Win32 RichEdit(Windows Forms の RichTextBox)約 22 ms約 20 ms
Win32 Edit(Windows Forms の TextBox)約 28 ms約 20 ms
memopad v0.2.0(RichEdit に置き換え)約 21 ms約 16 ms
キー送信から画面に文字が出るまで(IME 入力、単位 ms、短いほど良い) メモ帳 19 memopad v0.1.0(WPF TextBox) 50 RichEdit 単体(試作) 22 memopad v0.2.0(RichEdit) 21 016335060 ※ 16 ms = 60 Hz の 1 フレーム。キャプチャの分解能が約 18 ms なので目安の値
図 2-1:入力から表示までの遅延。v0.2.0 でメモ帳と同等になった

2.4 原因のまとめ

v0.1.0:WPF TextBox キー入力IME → WPF UI スレッドレイアウト・描画命令 描画スレッド垂直同期を待って DirectX 描画 DWM 合成次のフレームで表示 画面約 50 ms v0.2.0:Win32 RichEdit(WindowsFormsHost で埋め込み) キー入力IME → RichEdit UI スレッドで GDI 描画その場でウィンドウに描く DWM 合成次のフレームで表示 画面約 21 ms 黄色の箱が余分に待つ段階。WPF は描画を別スレッドに渡し、垂直同期に合わせて提示するため、メモ帳より 1〜2 フレーム遅れる
図 2-2:描画経路の違い。アプリのコードではなく WPF の表示パイプラインが原因だった

2.5 対策:エディタを RichEdit に置き換える

本文の編集部分だけを Windows 標準の RichEdit(msftedit.dll)にした。Windows Forms の RichTextBox を継承した Views/PlainTextEdit.cs を作り、WindowsFormsHost で WPF の画面に埋め込む。メニュー・タブ・検索バー・ダイアログはこれまでどおり WPF なので、見た目とテーマはそのまま。

項目やったこと
プレーン テキスト化EM_SETTEXTMODE で TM_PLAINTEXT にする(本文が空のうちに送る必要がある)。貼り付けても書式が付かず、フォント・色は全体で 1 つになる
元に戻すTM_MULTILEVELUNDO+EM_SETUNDOLIMIT=1000。Ctrl+Z/Ctrl+Y は RichEdit が処理し、Ctrl+Y だけアプリ側で Redo を呼ぶ
折り返しWindows Forms の WordWrap はハンドルを作り直して履歴が消えるので、EM_SETTARGETDEVICE を直接送る
ズームZoomFactor(EM_SETZOOM)。Ctrl+ホイールは WM_MOUSEWHEEL を横取りしてアプリのズームに回し、ステータスバーの % と連動させる
ショートカットWindowsFormsHost の中では WPF の InputBinding が効かない。ProcessCmdKey で押されたキーを Commands.All の KeyGesture と突き合わせて実行する。Alt+F などのアクセス キーも同じ経路でメニューを開く
右クリックWM_CONTEXTMENU を受けて WPF の ContextMenu(元に戻す〜すべて選択、検索)を出す
ファイルのドロップRichEdit 自身の OLE ドロップを止め(EnableAutoDragDrop=false)、DragDrop でファイルを開く
余白EM_SETRECT で左に 8 px の余白。サイズ変更で戻るので都度設定
ダーク テーマスクロールバーに SetWindowTheme(hwnd, "DarkMode_Explorer")
ステータスバー更新全文を読む処理は連続入力中に毎回動かさず、入力が途切れたとき(Dispatcher の Background)に 1 回だけ行う

2.6 置き換えで起きた問題と対処

RichEdit の文字が透けて見える状態
図 2-3:置き換え直後。本文が薄く透け、背後のウィンドウまで見えている
不透明化した v0.2.0 のメイン画面
図 2-4:ウィンドウを不透明にし、日本語フォントの自動選択を有効にした後

2.7 確認

背景色を紺、文字色を黄にしたところ
図 2-5:背景色・文字色の変更も RichEdit で動く(BackColor/ForeColor)
ダーク テーマで検索した状態
図 2-6:ダーク テーマ。検索の一致箇所が選択され、スクロールバーもダーク用

step2 の撮影スクリプト(docs/tools/capture-memopad.ps1)を回帰確認に使い、メニュー・検索/置換・行へ移動・フォント・色・テーマ・タブ・未保存確認・ページ設定・バージョン情報がすべて動くことを確認した。遅延の再計測は表 2-3 の最終行のとおり。

診断ログの使い方:レスポンスの問題が再発したときは、環境変数 MEMOPAD_PERF=1 を設定して memopad を起動すると %TEMP%\memopad-perf.log にキー押下と本文変更の時刻が記録される。通常起動では何も書かない。

3. FB-2:上部の高さと配置をメモ帳に合わせる(v0.3.0)

memopad v0.1.0(左)とメモ帳(右)の比較
図 3-1:フィードバックで示された比較。左が memopad v0.1.0、右がメモ帳。上部の高さと並び順、フッターの高さが違う

3.1 現象と目標

v0.1.0 は「タイトル バー→メニュー→タブ→本文」の順で、本文が始まるまでがメモ帳より 50 px 以上低い。メモ帳は「タブ(タイトル バーに同居)→メニュー→本文」の順で、+ボタンは最後のタブのすぐ右にある。フッター(ステータスバー)も memopad のほうが高い。

同じ大きさ(1000×720 物理 px、表示スケール 150 %)で並べて撮り、上端から本文までとステータスバーの高さを画像から測った。

項目(論理 px)メモ帳memopad v0.1.0memopad v0.3.0
上端から本文まで7512774
 タイトル行(タブを含む)4130(標準のタイトル バー)+ 49(メニュー)+ 48(タブ)42(タブを含む)
 メニュー行32—32
ステータスバー314732
v0.1.0、v0.3.0、メモ帳の上部を並べた比較
図 3-2:左から v0.1.0、v0.3.0、メモ帳(上部 260 px を切り出し)。v0.3.0 は本文の開始位置がメモ帳と 1 px 差

3.2 やったこと

項目内容
タイトル バーを自前で描くWPF の WindowChrome(CaptionHeight=42、UseAeroCaptionButtons=False)で標準のタイトル バーを外し、タイトル行にアイコン・タブ・+ボタン・最小化/最大化/閉じるボタンを置いた。ボタンのグリフは Segoe Fluent Icons。ダブルクリックで最大化、ドラッグで移動、右クリックでシステム メニューは WindowChrome がそのまま面倒を見る
ガラス領域は使わないGlassFrameThickness=0。ガラス領域があると RichEdit が透ける(2.6 節)。角丸は DWMWA_WINDOW_CORNER_PREFERENCE で DWM に頼む
タブの見せ方選択中のタブとメニュー行を同じ色にしてつなげ、タイトル行は少し濃くする(メモ帳と同じ)。Fluent の ListBoxItem は余白が大きいので、タブ用の簡素なテンプレートに差し替えた
メニュー行を 32 px にFluent の MenuItem は高さ約 58 px あり、Menu の高さを固定すると項目が描画されなくなる。トップ レベルの項目だけ簡素なテンプレート(サブメニューの Popup 込み)にし、サブメニューの項目は Fluent のまま
ステータスバーを 32 px にStatusBarItem の Padding と文字サイズを詰めた
最大化時の余白WindowChrome は最大化するとリサイズ枠の分だけ画面外にはみ出すので、最大化中はルートに 7 px の余白を付ける
v0.3.0 のメイン画面
図 3-3:v0.3.0。タイトル行にタブと+、その下にメニュー、本文の順
v0.3.0 のダーク テーマでタブを 2 つ開いたところ
図 3-4:ダーク テーマ。未保存のタブには●が付く
v0.3.0 のファイル メニュー
図 3-5:メニュー行を詰めても、サブメニューは Fluent の見た目のまま
計測の落とし穴:最初の計測では本文の開始位置が 7 px ずれて見えた。設定ファイルに「最大化」が保存されていたため、最大化中の余白が写り込んでいた。撮影スクリプト(capture-memopad.ps1)は設定を退避してから撮るので、そちらの値を採用した。

4. FB-3:「背景色」「文字色」を「色変更」に統合(v0.4.0)

4.1 現象

v0.1.0 では「書式」メニューに「背景色」「文字色」のサブメニューが別々にあり、どちらも同じ 16 色+「その他の色」が並んでいた。片方を選ぶたびにメニューを開き直すので使い勝手が悪い、というフィードバック。あわせて 16 色は Windows の基本 16 色から、参照ファイルの PICO-8 パレットに置き替える。

4.2 やったこと

番号名前色番号名前色
0黒 #0000008赤 #FF004D
1濃い青 #1D2B539オレンジ #FFA300
2濃い紫 #7E255310黄 #FFEC27
3濃い緑 #00875111緑 #00E436
4茶 #AB523612青 #29ADFF
5濃い灰 #5F574F13藍 #83769C
6明るい灰 #C2C3C714ピンク #FF77A8
7白 #FFF1E815桃 #FFCCAA
v0.4.0 の書式メニュー
図 4-1:書式メニュー。「背景色」「文字色」が「色変更...」にまとまった
色変更ダイアログで背景色に濃い青、文字色に黄を選んだところ
図 4-2:色変更ダイアログ。背景色と文字色を並べて選び、下のプレビューで確かめる
濃い青の背景に黄の文字
図 4-3:OK を押した後の本文
その他の色から開く色の設定ダイアログ
図 4-4:「その他の色...」は Windows 標準の色の設定ダイアログ(変更なし)

5. FB-4:目に優しい配色 16 パターン(v0.5.0)

5.1 要望

テーマのように全体の配色をまとめて変える機能として、目に優しい配色 16 パターン(参照ファイル「テキストエディタ配色パターン」)を用意し、プレビューを見て選べる方式にしてほしい、というフィードバック。背景色と文字色を一度に変える。

5.2 やったこと

16 パターン(HTML で再現したもの。名前はアクセント色、色コードは背景 / 文字):

生成り
あいうえお Aa Bb 0123
#F7F3E9 / #3B372E
ソフトグレー
あいうえお Aa Bb 0123
#F2F2F0 / #333333
セピア
あいうえお Aa Bb 0123
#F4ECD8 / #4A3F35
ミント
あいうえお Aa Bb 0123
#EAF4F0 / #2C3E3A
ラベンダー
あいうえお Aa Bb 0123
#F1EEF7 / #35313F
ピーチ
あいうえお Aa Bb 0123
#FBEEE6 / #4A3226
スカイ
あいうえお Aa Bb 0123
#EAF2F8 / #2A3B4A
サンド
あいうえお Aa Bb 0123
#F5EFE6 / #453D33
チャコール
あいうえお Aa Bb 0123
#2B2B2B / #DCDCDC
森
あいうえお Aa Bb 0123
#1F2B24 / #D6E4DA
夜
あいうえお Aa Bb 0123
#1B2430 / #D7DEE8
墨
あいうえお Aa Bb 0123
#232323 / #E0DCD3
ダークパープル
あいうえお Aa Bb 0123
#241F2E / #E1DCE8
コーヒー
あいうえお Aa Bb 0123
#2A211B / #E5DACB
スレート
あいうえお Aa Bb 0123
#262B30 / #DCE1E5
ワイン
あいうえお Aa Bb 0123
#2A1E22 / #E5D9DB
v0.5.0 の書式メニュー
図 5-1:書式メニューに「配色パターン...」が加わった
配色パターン ダイアログで「夜」を選んだところ
図 5-2:配色パターン ダイアログ。カードを選ぶと下のプレビューが変わる
配色パターン「夜」を反映した本文
図 5-3:「夜」を反映した本文

6. FB-5:タブをクリックしても切り替わらない(v0.5.1)

6.1 現象

v0.5.0 をインストールして確認中、タブをクリックしても切り替わらない。メニューと文字入力は動く。Ctrl+Tab やプログラムからの切り替え(新しいタブを開いたとき)は動いていたため、撮影スクリプトの確認では気づけなかった。

6.2 原因

v0.3.0 でタブ用に差し替えた ListBoxItem のスタイルに Focusable=False を付けていた。WPF の ListBoxItem はマウスで押されたとき、まず自分にフォーカスを移し、それが成功したときだけ選択処理を行う。フォーカスを取れない設定にしたことで、クリックしても選択されなくなっていた(フォーカスが本文から離れないように、という意図だったが逆効果)。

6.3 対処と確認

操作v0.5.0(インストール済み)v0.5.1
Ctrl+N でタブ追加*タイトルなし 2 に切り替わる同じ
1 つ目のタブをクリック切り替わらない(*タイトルなし 2 のまま)タイトルなし に切り替わる
2 つ目のタブをクリック—*タイトルなし 2 に切り替わる
クリック後にキー入力本文に入る本文に入る(abc → abcxyz)

7. FB-6〜8:表示の細かな崩れ 3 件(v0.5.2)

欠けて見える検索バーの閉じるボタン
図 7-1:FB-6。×の文字グリフが幅 32 px のボタンに収まっていない
修正後の検索バー
図 7-2:v0.5.2。Segoe Fluent Icons の閉じるグリフにし、幅と高さを隣のボタンに揃えた
英語の行が切れている配色パターンのプレビュー
図 7-3:FB-7。プレビュー枠の高さを 88 px に固定していたため、フォントが大きいと 2 行目が隠れる
修正後の配色パターン ダイアログ
図 7-4:v0.5.2。最小高さに変え、フォントの大きさに合わせて枠が伸びる(色変更ダイアログも同様)
□□□と表示されるツールチップ
図 7-5:FB-8。キャプション ボタンのスタイルに付けたアイコン フォント(Segoe Fluent Icons)をツールチップが継承し、日本語が□になっていた
修正後のツールチップ「最小化」
図 7-6:v0.5.2。アイコン フォントはグリフを描く部分だけに付け、ツールチップは通常のフォントで出る
確認のしかたを変えた点:3 件ともユーザーの環境(表示スケール 150 %、大きめのフォント設定)で見えた崩れで、既定設定で撮る撮影スクリプトでは出なかった。確認用の撮影は、設定を退避しない版(ユーザーの設定のまま起動して撮る)も併用する。また撮影スクリプトは、前面ウィンドウが自分で起動した memopad でなければキーを送らずに中断するようにした(ユーザーが操作中に別アプリへキーが飛ぶのを防ぐ)。

8. FB-11:起動がメモ帳よりワンテンポ遅い(v0.7.0)

8.1 現象と測り方

メモ帳を起動するとすぐウィンドウが出るのに、memopad は一呼吸置いてから出る。FB-1 と同じく、まず数値にした。docs/tools/measure-startup.ps1 は対象を起動し、別プロセスから 1 ms 間隔で (1) 対象プロセスの可視ウィンドウが現れるまで、(2) 画面のピクセル(本文中央とタイトル行)の色が変わらなくなるまで、を測る。8 回起動して 1 回目(コールド)を除いた中央値を採る。

対象ウィンドウ表示まで描画完了まで
メモ帳(Windows 11 の Store 版)約 200 ms約 300 ms
memopad v0.6.0約 520〜550 ms約 650 ms

ウィンドウが出るまでで 2.5 倍以上、300 ms 以上の差がある。これが「ワンテンポ」の正体。

8.2 起動処理の内訳

環境変数 MEMOPAD_PERF=1 で有効になる診断ログ(Services/PerfLog.cs)に、プロセス起動からの経過時間を段階ごとに記録する Mark を足した。温まった状態(2 回目以降)の v0.6.0 の内訳は次のとおり。

v0.6.0 の起動処理の内訳 0100200300 400500600700800 ms .NET ランタイム起動(60 ms) WPF Application の初期化(81 ms) 設定 JSON の読み込み(31 ms)→ v0.7.0 で 18 ms Fluent テーマの適用(47 ms) MainWindow の XAML 読み込み(106 ms) 最初のタブ:Windows Forms と RichEdit の生成(57 ms) レイアウトとテンプレート適用(50 ms) WindowChrome の初期化(42 ms) Direct3D デバイス生成と表示(80 ms)→ v0.7.0 で 4 ms Show の完了(69 ms) 最初の描画(66 ms) ウィンドウ表示(約 536 ms)
図 8-1:v0.6.0 の起動処理の内訳。橙は v0.7.0 で短縮できた段階、青は Windows Forms 側、灰は WPF と .NET の固定費

特定の 1 か所が遅いのではなく、WPF の固定費が薄く広く積み上がっている。dotnet-trace で JIT イベントを数えると、起動中に 1,523 メソッドが JIT コンパイルされていた(P/Invoke の呼び出しスタブ約 560、ジェネリック型の実体化約 470、memopad 自身のコード約 150)。同梱の .NET ランタイムは事前コンパイル(ReadyToRun)済みだが、スタブと実体化は実行時に作られる。

8.3 試したことと効果

試したこと効果(ウィンドウ表示まで)採否
アプリ本体の ReadyToRun 発行(PublishReadyToRun)約 10 ms 短縮採用(無害)
フレームワークまで一体で事前コンパイルする複合 ReadyToRun(PublishReadyToRunComposite)温まった状態で約 40 ms 短縮。ただしインストール直後の初回起動が 1.2 秒 → 4.3 秒に悪化(100 MB 超の一体イメージをウイルス対策がスキャンする)、容量 +20 MB不採用
Fluent テーマを外す(旧来の Aero2 テーマ)約 60 ms 短縮。見た目(ダーク テーマ、Windows 11 風のメニュー)を失う不採用
ランタイムのオプション(TieredPGO 無効、並行 GC 無効)差なし—
WPF の描画をソフトウェアにする(RenderMode.SoftwareOnly)Direct3D デバイスの生成(約 80 ms)が消え、描画完了が約 50 ms 早まる。memopad の WPF 部分はタイトル行・メニュー・ステータスバーだけで本文は GDI の RichEdit なので、見た目・速度に差は出ない採用
設定 JSON をソース ジェネレーターで変換(JsonSerializerContext)31 ms → 18 ms採用
Windows Forms/msftedit.dll/System.Drawing/Fluent のアセンブリを別スレッドで先読み(Services/StartupWarmup.cs)約 10 ms 短縮(CPU の空いているコアで並列に読み込む)採用
フォント(Yu Gothic UI、Segoe Fluent Icons)の別スレッド先読み差なし不採用
メニュー項目の遅延生成メニュー部分の XAML 読み込みは 11 ms しかなく、作り込む価値なし不採用

8.4 v0.7.0 の結果

対象ウィンドウ表示まで(中央値)描画完了まで(中央値)
メモ帳205 ms約 300 ms
memopad v0.6.0521 ms669 ms
memopad v0.7.0505 ms619 ms

描画完了は 50 ms 早くなったが、ウィンドウが出るまでは 16 ms しか縮まらず、体感はほぼ変わらない。WPF のまま「メモ帳並み」にはできなかった。

8.5 WPF の下限と Windows Forms との比較

どこまで縮められるかを知るため、最小構成のアプリを同じ条件(self-contained 発行、同じ端末)で測った。

ウィンドウが表示されるまでの時間の比較 メモ帳 最小構成の Windows Forms(RichEdit+メニュー+ステータスバー) 最小構成の WPF(メニュー+テキスト ボックスだけ) memopad v0.7.0 memopad v0.6.0 205 ms 162 ms 398 ms 505 ms 521 ms
図 8-2:ウィンドウが表示されるまでの時間(2 回目以降の中央値)。緑の点線がメモ帳の水準
選択と結果:(A) v0.7.0 の水準(約 500 ms)で受け入れる、(B) メイン ウィンドウを Windows Forms で作り直す、の 2 案を出し、ユーザーは (B) を選んだ。作り直した v0.8.0 でウィンドウ表示は 221 ms になり、メモ帳(211 ms)と並んだ。作業の中身は次の 9 節に書く。

9. Windows Forms への作り直しと MemoPad への改名(v0.8.0)

9.1 方針:本文はそのまま、ガワだけ載せ替える

本文の編集領域はもともと Windows Forms のコントロール(RichEdit を継承した Views/PlainTextEdit.cs)で、v0.2.0 以降それを WPF の WindowsFormsHost に埋めて使っていた。つまり いちばん重要な部分は最初から Windows Forms で、WPF はその周り(タイトル行・メニュー・検索バー・ステータスバー)を描くためだけに 400 ms を払っていたことになる。そこで周りだけを Windows Forms に載せ替えた。

v0.7.0 と v0.8.0 の構成の違い v0.7.0(WPF がガワ) WPF Window(WindowChrome) タイトル行(ListBox のタブ+ボタン) Menu(Fluent テーマ) WindowsFormsHost RichEdit(PlainTextEdit) StatusBar v0.8.0(Windows Forms がガワ) Form(WM_NCCALCSIZE で枠を自前化) TitleBar(TabButton+ボタン) MenuStrip(FluentMenuRenderer) RichEdit(PlainTextEdit、そのまま流用) StatusBarPanel 赤=WPF、青=Windows Forms。ダイアログ(フォント・色変更・配色パターン・行へ移動・ページ設定・保存確認・バージョン情報)は WPF のまま残し、初めて開くときに読み込む。
図 9-1:ガワを WPF から Windows Forms に載せ替えた。本文の RichEdit は v0.2.0 のまま使い回している

9.2 作ったもの

ファイル役割
Program.cs入口。App.xaml をやめ、WPF に触れずに設定を読んでフォームを出す
Views/MainForm.csメイン ウィンドウ。WM_NCCALCSIZE で標準のタイトル バーと枠を外し、WM_NCHITTEST でリサイズとドラッグ移動を自分で答える
Views/TitleBar.csタイトル行。タブ(TabButton)と+・最小化・最大化・閉じるを並べる
Views/FluentMenuRenderer.csMenuStrip を Windows 11 風に描く(角丸のホバー、薄い枠線、チェックと右矢印は Segoe Fluent Icons)
Views/StatusBarPanel.csステータスバー。改行コードとエンコードはクリックでメニューを出す
Views/MainForm.Menu.cs / MainForm.Find.csメニューと右クリック メニュー/検索・置換バー
Services/WpfHost.csWPF のダイアログを遅延初期化する。起動時は読み込まず、最初にダイアログを開くときだけ WPF を立ち上げる
Services/ThemeService.csFluent テーマ(ThemeMode)の代わりに、ライト/ダークの配色を ThemePalette として自前で持つ

消したのは App.xaml、MainWindow.xaml(と分離コード)、EditorView.xaml、Commands.cs、値コンバーター 2 つ。ダイアログ 7 種は WPF のまま、オーナーだけ Window からウィンドウ ハンドルに変えて流用した。

v0.8.0 のメイン画面(ライト)
図 9-2:v0.8.0 のメイン画面。タブ→メニュー→本文の並びと寸法は v0.3.0 のまま
v0.8.0 のダーク テーマ
図 9-3:ダーク テーマ。Fluent テーマの代わりに配色を自前で持つ
2 つのタブと未保存の印
図 9-4:タブは Button を継承したコントロール。未保存は●、右端に閉じる×
MemoPad についてダイアログ
図 9-5:ダイアログは WPF のまま。名称とアイコンを新しくした(FB-12)

9.3 つまずいた点

症状原因と対処
タブが支援技術(スクリーン リーダー)から見えない最初はタイトル行を 1 枚のコントロールに自前描画し、AccessibleObject の子として公開したが、UI Automation には名前の無い枠としてしか出なかった(Windows Forms の素の Control は子要素を UI Automation へ渡さない)。タブとボタンを Button を継承した実体のあるコントロールに変え、AccessibleName と AccessibleRole を付けたら、名前・位置・クリックがそのまま伝わるようになった。FB-5 で作ったタブ クリックの確認スクリプトもこれで動く
選択中のタブだけ×が消えるタブの背景を×より後に描いていた。タブをすべて描いてから×を描く順に変えた
ステータスバーのメニューを閉じると落ちるClosed イベントの中で ContextMenuStrip を Dispose していた。後始末が破棄済みのオブジェクトに触れて例外になる。BeginInvoke でメッセージを処理し終えてから捨てるようにした
エンコードのメニューが画面からはみ出す項目の左端を基準に右へ開いていた。右端を基準に左へ開くようにした
最大化するとウィンドウが画面外へはみ出すWM_NCCALCSIZE で枠を外すと、最大化時は見えない枠の分だけ外へ出る。GetSystemMetricsForDpi で枠の太さを取り、その分だけ内側に寄せた
撮影スクリプトでメニューの ESC が効かないWindows Forms のメニューは ESC 1 回ではメニュー モードを抜けない(1 回目でドロップダウン、2 回目で選択解除)。スクリプト側を ESC 2 回に直した。あわせて、撮影前にマウス カーソルを画面の隅へ寄せてツールチップの写り込みを防ぐようにした

9.4 結果

対象ウィンドウ表示まで(中央値)描画完了まで(中央値)
メモ帳(Windows 11 の Store 版)211 ms467 ms
MemoPad v0.6.0(WPF)521 ms669 ms
MemoPad v0.7.0(WPF+短縮策)505 ms619 ms
MemoPad v0.8.0(Windows Forms)221 ms332 ms

ウィンドウが出るまでは 505 ms → 221 ms と 2.3 倍速くなり、メモ帳(211 ms)に並んだ。本文が描き終わるまでは 332 ms で、メモ帳(467 ms)より速い。step1 で洗い出した機能はすべて動くことを、撮影スクリプト(docs/tools/capture-memopad.ps1)とタブ クリックの確認スクリプトで確かめている。

ウィンドウが表示されるまでの時間の推移 メモ帳 MemoPad v0.8.0(Windows Forms) MemoPad v0.7.0(WPF+短縮策) MemoPad v0.6.0(WPF) 211 ms 221 ms 505 ms 521 ms 同じ端末で 10 回ずつ起動し、1 回目(コールド)を除いた中央値。緑の点線がメモ帳の水準
図 9-6:ウィンドウが表示されるまでの時間。v0.8.0 でメモ帳の水準に並んだ

9.5 FB-12:MemoPad への改名と新しいアイコン

FB-11 が達成できたので、名称を memopad から MemoPad に改め、アイコンをユーザー提供の新デザイン(クリーム色の文書に緑の罫線)に差し替えた。

対象変更
実行ファイルmemopad.exe → MemoPad.exe(AssemblyName)
画面の表示タイトル バー、メニューの「MemoPad について」、各ダイアログの見出しとメッセージ
設定の保存先%APPDATA%\MemoPad\settings.json(Windows のパスは大文字小文字を区別しないので、これまでの設定はそのまま引き継がれる)
インストーラアプリ名・ショートカット・関連付けの表示名、出力ファイル名 MemoPad-setup-0.8.0.exe
アイコンsrc/memopad/memopad.ico(16〜256 px の 6 サイズ)と、紹介ページ用の PNG・favicon

リポジトリ名・フォルダー名・名前空間(Memopad)はそのままにした。GitHub の URL が変わると既存のリンクが切れるうえ、識別子は英語のままという方針に沿うため。

10. FB-13:タイトル行でウィンドウを動かせない(v0.9.0)

10.1 現象

v0.8.0 をインストールしたユーザーから「ウィンドウ上部のドラッグでの移動ができなくなっている。ウィンドウ上部をダブルクリックした際のウィンドウ サイズ変更もできない」というフィードバックを受けた。v0.7.0(WPF)までは普通にできていた操作で、Windows Forms への作り直しで持ち込んだ不具合。手元で調べたところ、ウィンドウの四辺をドラッグしてのリサイズも同じ原因で効かなくなっていた(フィードバックには挙がっていなかったが、あわせて直した)。

10.2 原因:当たり判定を子コントロールが横取りしていた

MemoPad は標準のタイトル バーを WM_NCCALCSIZE で外し、「ここはタイトル行(HTCAPTION)」「ここは左の縁(HTLEFT)」といった判定を MainForm が WM_NCHITTEST で答えている。ところが Windows は、その座標にある いちばん手前の子ウィンドウ に当たり判定を尋ねる。v0.7.0 までの WPF はウィンドウ全体が 1 つの HWND だったので親がそのまま答えられたが、Windows Forms はコントロールごとに HWND があるため、タイトル行を覆う TitleBar、四辺を覆う RichEdit やステータスバーが「ここは普通のクライアント領域(HTCLIENT)」と答えてしまい、親の答えは使われない。

当たり判定の流れ(v0.8.0 と v0.9.0) v0.8.0(動かせない) タイトル行の空きをドラッグ TitleBar(子ウィンドウ) 「ここは普通のクライアント(HTCLIENT)」 Form(MainForm) HTCAPTION と答えるが、ここまで来ない → 移動もダブルクリックの最大化もリサイズもできない v0.9.0(動く) タイトル行の空きをドラッグ TitleBar(ChromeHitTest) 「自分は透明(HTTRANSPARENT)」 Form(MainForm) 「ここはタイトル行(HTCAPTION)」 → 移動・最大化・スナップ・リサイズは Windows が処理
図 10-1:子ウィンドウが当たり判定を横取りしていた。HTTRANSPARENT を返すと判定が親へ落ちる

どのウィンドウにマウス入力が届くかは WindowFromPoint で調べられる。v0.8.0 では、フォーム自身は正しく答えているのに、入力先はすべて子コントロールだった。

位置フォームの答え入力が届く先(v0.8.0)子の答え(v0.9.0)
タイトル行の空きHTCAPTION(2)TitleBarHTTRANSPARENT(-1)
左右の縁HTLEFT / HTRIGHT(10 / 11)RichEditHTTRANSPARENT(-1)
下の縁HTBOTTOM(15)ステータスバーHTTRANSPARENT(-1)
タブ・ボタンの上HTCLIENT(1)TabButtonHTCLIENT(1)=今までどおり

10.3 直し方:子から HTTRANSPARENT を返す

Views/ChromeHitTest.cs を追加した。子コントロールが WM_NCHITTEST を受け取ったら、その座標を親フォームに聞き返し(MainForm.IsWindowFrameAt)、タイトル行の空きかウィンドウの縁だったら HTTRANSPARENT(自分は透明。下のウィンドウに聞いてほしい、という意味)を返す。すると判定が親へ落ち、ドラッグ移動・ダブルクリックでの最大化/復元・スナップ・右クリックのシステム メニュー・四辺のリサイズを、いつもどおり Windows が処理してくれる。タブや+ボタンの上は今までどおり子が受け取るので、クリックの動作は変わらない。

直したコントロール覆っていた場所
TitleBar(タブとキャプション ボタンを含む)タイトル行全体、上端と右上隅
PlainTextEdit(RichEdit)本文が届く左右の縁
StatusBarPanel下の縁と左右の下隅
ChromeMenuStrip / ChromePanel(メニュー・検索バー・本文の入れ物)左右の縁

10.4 確かめ方

UI Automation では「マウスで掴んで動かす」操作を再現できないので、mouse_event で実際にドラッグとダブルクリックを行い、ウィンドウの位置とサイズが期待どおり変わるかを確認した(タブのクリック不具合〈FB-5〉を撮影スクリプトで見逃した反省から、v0.5.1 以降は実操作での確認を必ず 1 つ用意している)。

操作期待v0.9.0 の結果
タイトル行の空きをドラッグウィンドウが同じ量だけ動く(+120, +60) の操作で (+120, +60) 移動
タイトル行をダブルクリック最大化する最大化した
最大化中にもう一度ダブルクリック元のサイズに戻る戻った
右の縁を +100 ドラッグ幅が 100 増える1000 → 1100
下の縁を +80 ドラッグ高さが 80 増える700 → 780

11. FB-14・FB-15:メニューの見た目(v0.9.0/v0.9.1)

11.1 FB-14:「MemoPad について(A)」の (A)

(A) は About(バージョン情報)の頭文字を割り当てたアクセス キーの表記で、日本語版のメモ帳も「メモ帳について(A)」と出す昔からの決まり。Alt→H→A でダイアログが開く。他の項目(終了(X)、保存(S) など)とも揃っているので、そのままにした。

11.2 FB-15:字下げが広すぎる/文字が上付き

「サブメニューの文字列がやや上付きでバランスが悪い」「メニューからサブメニューが字下げで表示されるが、字下げの幅が文字 2 文字分ほどあり右寄りすぎる」というフィードバック。メモ帳のメニューと並べて、ポップアップの左端から文字までの距離と、項目の中心に対する文字の上下位置を実測した(表示スケール 150 %、単位は実ピクセル)。

項目メモ帳MemoPad v0.8.0MemoPad v0.9.0MemoPad v0.9.1
字下げ(チェックの無いメニュー)約 21 px54 px17 px25 px
字下げ(チェックの付くメニュー)約 58 px54 px50 px62 px
項目の中心と文字の中心のずれほぼ 05.5 px 上1.5 px 下1.5 px 下
項目の高さ50 px48 px48 px48 px

原因は 2 つあった。

v0.9.0 ではアイコン欄を外した分そのままにしたので、今度は文字がポップアップの左端に近づきすぎた(17 px)。 「アイコンが無いメニューも少し字下げするのが基本」というフィードバックを受け、v0.9.1 で表題だけを 5 論理 px 右へ寄せてメモ帳と同じ見え方にした(25 px)。項目の余白(ToolStripMenuItem.Padding)でもポップアップの 余白(ToolStripDropDownMenu.Padding)でも左右の位置は動かせないので、FluentMenuRenderer で 文字を描く矩形をずらしている。ショートカットの表示(右寄せ)と、メニュー バーの項目はずらさない。

v0.8.0 のファイル メニュー。文字が右に寄っている
図 11-1:v0.8.0。使っていないアイコン欄の分だけ字下げが深く、文字も少し上に寄っている
v0.9.0 のファイル メニュー。字下げが浅くなった
図 11-2:v0.9.1。メモ帳と同じ位置に文字が来る(チェックの付く表示メニューだけは従来どおり深く字下げする)

12. FB-16:ドラッグ&ドロップで開くと編集済みになる(v0.9.1)

12.1 現象

エクスプローラーから既存のテキスト ファイルを本文の上にドラッグ&ドロップして開くと、開いた直後なのに タイトルに *、タブに●が付いて「編集済み」の状態になる。本文の内容と文字数は正しく、そのまま閉じようとすると 保存の確認ダイアログが出てしまう。メニューの「開く」やエクスプローラーからの「プログラムから開く」では起きない。

ドロップ直後にタブへ●が付いている
図 12-1:v0.9.0 まで。開いた直後なのにタブが●(編集済み)になる
ドロップ直後でも編集済みにならない
図 12-2:v0.9.1。ファイルを開くだけの状態になる

12.2 原因:ドロップを RichEdit にも処理させていた

本文の RichEdit は OLE のドロップ先を自分で登録していて、Windows Forms はそこに相乗りする形で DragDrop イベントを出す。MemoPad はそのイベントでファイルを開いていたが、イベントを出したあとに RichEdit 自身のドロップ処理が続く。プレーン テキスト モードでは実際には何も貼り付けられないものの、 変更通知(EN_CHANGE)だけが上がるため、直前に読み込んだばかりの文書が「編集済み」と判定されていた。

ドロップ先のタブが空の「タイトルなし」だと、そのタブを使い回してファイルを読み込む(メモ帳と同じ挙動)ので、 読み込んだ文書そのものに●が付く。本文に何か書いてある状態で落としたときは別のタブが作られるため、 新しいタブは●にならず、現象が見えにくかった。

12.3 直し方:ドロップ先を自前のものに差し替える

Views/FileDropTarget.cs に IDropTarget の実装を追加し、RichEdit のハンドルができたところで RevokeDragDrop→RegisterDragDrop と差し替える。受け付けるのはファイル(CF_HDROP)だけで、 ドロップされたデータは RichEdit に渡さない。これで「ファイルを開くだけ」の動作になる。 ファイルを開く処理自体は BeginInvoke で後回しにして、ドロップの処理中にドラッグ元を待たせないようにした。

落とす場所受け取るものv0.9.1 の動き
本文(RichEdit)FileDropTarget(自前)開くだけ。編集済みにならない
タイトル行・メニュー・ステータスバーフォームの DragDrop(従来どおり)開くだけ
ファイル以外(文字列など)—受け付けない(従来どおり)

12.4 確かめ方

エクスプローラーを開いてファイルの項目の位置を UI Automation で取り、mouse_event で実際にドラッグして MemoPad に落とすスクリプトで確認した。自前のウィンドウから DoDragDrop で送る合成のドロップでは、 RichEdit に拒否されて再現しなかった(エクスプローラーが渡すデータには複数の形式が入っているため挙動が違う)。 再現には本物のドラッグが要る、という点も含めての確認になった。

手順v0.9.0v0.9.1
空のタブに本文へドロップ*dd_sample.txt(編集済み)dd_sample.txt
入力済みのタブがある状態で本文へドロップ新しいタブで開く(編集済みにはならない)同じ
タイトル行へドロップdd_sample.txt同じ

13. FB-17:ホイールで最後の行まで届かない(v0.9.2)

13.1 現象

「長いファイルをマウス ホイールでスクロールしたとき、最後の改行までスクロールされなかった。メモ帳では問題なく最後の改行まで届く。些細なことだが気になる」というフィードバック(再現用のファイルも受け取った)。 ファイルの末尾が改行で終わっていると、その改行のあとに空の行が 1 行あり、カーソルもそこへ置ける。メモ帳はホイールでこの空行が見える位置まで送れるが、 MemoPad は最後の文字の行が画面の下端に来たところで止まり、それ以上回しても動かなかった。

ホイールで送り切っても 45 行目が下端に来たところで止まっている
図 13-1:v0.9.1。ホイールを回し続けても 45 行目(最後の行)が下端で止まり、その下の空行は画面の外のまま
45 行目の下に空行が見えるところまで送れている
図 13-2:v0.9.2。45 行目の下の空行まで送れる(メモ帳と同じ)

13.2 原因:RichEdit のホイール処理は末尾の空行を数えない

ホイールで一番下まで送ったときの位置を、スクロールバーの ▼(1 行ずつ送る)や「一番下へ」(SB_BOTTOM)と比べた。 値はスクロール位置(px。表示スケール 150 %、1 行 26 px)。ファイルの末尾の形を 3 通りに変えると、ホイールだけが、最後の改行のあとの空行 1 行分手前で止まることが分かった。

ファイルの末尾ホイール(40 ノッチ)▼ で 1 行ずつ一番下へ(SB_BOTTOM)
改行 1 つ(受け取ったファイル)901(26 px 手前)927927
改行なし901901901
改行 2 つ927(26 px 手前)953953

MemoPad の本文は Windows 標準の RichEdit(msftedit.dll、この端末では 10.0.26100)で、ホイールの処理も RichEdit 自身が行っていた (1 ノッチで 3 行を約 0.3 秒かけて動かす、なめらかスクロール)。メモ帳の本文も RichEdit 系の部品だが、読み込んでいるモジュールを調べると、 アプリに同梱した新しい RichEdit(riched20.dll 16.0、Office 系)だった。こちらではこの癖が直っている。 Office の DLL は再配布できないので、MemoPad の側で対処する。

ホイールで送り切ったときに止まる位置(v0.9.1 と v0.9.2) v0.9.1(RichEdit 自身のホイール処理) 42 行目 … 43 行目 … 44 行目 … 45 行目(最後の文字の行) (末尾の改行のあとの空行) 画面 画面の外 → 最後の文字の行が下端に来たところで止まる (▼ や PageDown なら空行まで届く) v0.9.2(MemoPad が自前で送る) 42 行目 … 43 行目 … 44 行目 … 45 行目(最後の文字の行) (末尾の改行のあとの空行) 画面 → スクロールバーで一番下へ送ったときと  同じ位置(nMax − nPage)まで届く
図 13-3:RichEdit 自身のホイール処理は、末尾の空行を 1 行分数えずに止まる。v0.9.2 ではスクロールバーと同じ上限まで送る

13.3 直し方:ホイールだけ MemoPad が自前でなめらかに送る

Views/SmoothWheelScroller.cs を追加し、PlainTextEdit が WM_MOUSEWHEEL(Ctrl・Shift を押していないもの)を RichEdit に渡さず、このクラスに回すようにした。Ctrl+ホイールのズームと Shift+ホイールは今までどおり。

項目v0.9.1(RichEdit 自身)v0.9.2(SmoothWheelScroller)
1 ノッチの量3 行(78 px)3 行(78 px)
動き始め約 50 ms 後約 15〜30 ms 後
動き終わり約 0.32 秒(等速、約 30 コマ/秒)約 0.25 秒(止まり際ゆるやか、約 60 コマ/秒)
末尾が改行のファイル空行の 1 行手前で止まる空行まで届く
上へ送り切ったとき先頭から 1〜2 px ずれて止まることがある先頭(0)で止まる
送っている途中で Ctrl+Home先頭へ戻ったあとも残りの動きが続き、113 px の位置で止まる先頭で止まる
タッチパッド相当(delta −1 を 120 回)—78 px(1 ノッチと同じ)

13.4 確かめ方

実際のマウス入力(mouse_event)でホイールを回し、別プロセスから GetScrollInfo でスクロール位置を読んで確かめた。 表の値は「ホイールで送り切った位置 / スクロールバーの一番下の位置」。

確認v0.9.1v0.9.2
受け取ったファイル(末尾に改行 1 つ)901 / 927927 / 927
末尾に改行なし/改行 2 つ901 / 901、927 / 953901 / 901、953 / 953
折り返しなし・長い行のファイル(横スクロールバーは v0.9.2 だけ出る、14 節)1,525 / 1,5511,577 / 1,577
12 万行:一番下から上へ 3、下へ 10 ノッチ—3,119,471 / 3,119,471
12 万行:先頭から 40 ノッチ—3,120 px(40 × 78 px どおり)
ズームを 1 段拡大してから 1 ノッチ84 px84 px
Ctrl+ホイール(ズーム)、Shift+ホイール修正前後で同じ動き

「今回の修正で起動時間が増えないか」という確認の依頼があったので、修正前(v0.9.1)と修正後を配布版と同じ条件(self-contained・ReadyToRun)で発行し、 交互に計測した。どちらも差は計測のばらつきの範囲で、増えていない。大きなファイルの読み込みも変わらない。

計測v0.9.1v0.9.2
起動:ウィンドウ表示(10 回の中央値、2 巡)226.5 ms、223.5 ms223 ms、219.5 ms
起動:描画完了(同上)348 ms、333.5 ms335.5 ms、336 ms
英数字 11.8 MB・12 万行を開き、UI が応答できるまで(5 回の中央値)2,011 ms2,017 ms
別に見つかった課題:日本語を含む 10.4 MB・12 万行のファイルを「右端で折り返す」オンで開くと、UI が約 44 秒応答しなくなる。 v0.9.1 でも同じで、今回の修正とは関係がない。このときは「折り返しなしなら約 3 秒」と報告したが、 実機で試すと折り返しのオン・オフに関わらず長時間かかることが分かり、FB-19 として扱うことになった。

14. FB-18:折り返しなしで横にスクロールできない(v0.9.2)

14.1 現象

FB-17 を確かめる途中で見つけた。「右端で折り返す」をオフにすると、長い行は右端で切れたまま横スクロールバーが出ない。 End でカーソルを行末(306 列目)へ動かしても表示が横に動かず、右端から先の文字を見る手段がなかった。 メモ帳は折り返しなしのとき横スクロールバーを出し、カーソルに合わせて横にも送る。本文を RichEdit に置き換えた v0.2.0 からの不具合で、 了承をもらって FB-18 として v0.9.2 で直した。

カーソルは 306 列目にあるが、表示は行頭のままで横スクロールバーもない
図 14-1:v0.9.1。ステータスバーは「列 306」なのに表示は行頭のまま。横スクロールバーも出ない
カーソルの位置まで横に送られ、下に横スクロールバーが出ている
図 14-2:v0.9.2。カーソルの位置まで横に送られ、下に横スクロールバー(右寄りのつまみ)が出る

14.2 原因:Windows Forms の WordWrap が true のままだった

Windows Forms の RichTextBox は、自分の WordWrap プロパティが true(既定)だと、ウィンドウを作るときに 横スクロールバー(WS_HSCROLL)と、カーソルに合わせて横に送る指定(ES_AUTOHSCROLL)を付けない。 MemoPad は折り返しを切り替えても元に戻す履歴が消えないよう、WordWrap を使わず RichEdit に直接 EM_SETTARGETDEVICE を送って切り替えている (WordWrap を変えるとウィンドウが作り直され、履歴が消える)。そのため WordWrap は既定の true のまま残り、 折り返しを外しても横に送る手段がなかった。本文のウィンドウのスタイルを調べると、実際に 2 つとも付いていなかった。

14.3 直し方

14.4 確かめ方

横スクロールバーの有無は、本文の欄の高さ(出ていれば 1 本分低くなる)と縦のスクロール範囲で確かめた(150 %、単位は px)。 長い行は 300 文字 × 80 行、短い行は 1 行 15 文字前後 × 60 行の検証用ファイル。

状態横スクロールバー本文の欄の高さ縦の一番下
長い行・折り返しありで起動なし5619,871
→ 表示メニューで折り返しなしへあり5351,577
→ 折り返しありへ戻すなし5619,871(EM_SHOWSCROLLBAR を入れる前は 9,897 にずれていた)
長い行・折り返しなしで起動あり5351,577
短い行だけ・折り返しなし/ありなし5611,031

15. FB-19:大きなファイルで固まる(v0.10.0 で本文を Scintilla に変更)

15.1 現象

v0.9.2 を実際に使ってもらったところ、次の報告を受けた(2026-09-12)。

2 つ目の「一度固まるとその後も固まる」は、最初の読み込みだけの問題ではないことを示している。 レイアウトの計算結果が持ち越されず、画面を作り直すたびに大きな文書の計算をやり直している可能性が高い。

15.2 対処:先に配布を止める

ユーザーの判断は「固まるという現象を抱えた v0.9.2(以前のバージョンも含め)は、エディタとしての機能を満たしていない」というもの。 これを受けて、原因調査より先に配布の停止を行った。

対象対処
GitHub Releases のインストーラ(v0.6.0〜v0.9.1 の MemoPad-setup-*.exe)すべて削除。リリース ページ自体と自動生成のソース zip は残し、本文に停止の理由を明記
紹介ページ・READMEダウンロードのボタンと手順をお知らせに差し替え
備忘録サイト(このページを含む)公開を継続。経過はこの節に追記していく
ソース コード公開を継続

修正が済んで同じファイルが実用的な時間で開けることを確かめたら、あらためて配布を再開する。 → 2026-09-13 に v0.10.0 で配布を再開した(15.9)。

15.3 測り直し:報告した数字が違っていた

まず実機と同じ手順(アプリにファイルのパスを渡して起動する)で測り直した。docs/tools/measure-openfile.ps1 を作り、 ウィンドウが出てから WM_NULL に答えられるようになるまでを外から測る。

操作MemoPad v0.9.2 折り返しオン同 折り返しオフメモ帳
開く(応答が戻るまで)43.5 秒87.3 秒0.41 秒
最大化28.6 秒55.7 秒0 秒
元のサイズへ28.6 秒55.7 秒0 秒

13.4 で「折り返しなしなら約 3 秒」と書いたのは誤りで、折り返しなしのほうが倍遅い。 前回はアプリの外から測る手順ではなく、別の条件で測った数字をそのまま載せてしまっていた。 また、1 回のリサイズで 28.6 秒=全文のレイアウト 2 回分かかっている。余白を付ける EM_SETRECT を OnResize のたびに呼んでおり、WM_SIZE による再計算に加えてもう 1 回走っていた。

15.4 原因:日本語と英字が混ざった行の「文字幅の問い合わせ」

本文のスレッドの命令ポインタを 2 ms ごとに拾い、どのモジュールで時間を使っているかを集計した (SuspendThread→GetThreadContext→ResumeThread。win32u.dll のエクスポート表を解析して関数名まで対応付けた)。 30,000 行の読み込み中に 432 サンプル。

関数割合内容
NtGdiGetWidthTable55.3 %GDI の文字幅テーブルの取得
NtGdiGetCharWidthW28.0 %1 文字ずつの幅の取得
NtGdiGetCharSet6.0 %文字セットの問い合わせ
MsftEdit.dll 自身7.2 %RichEdit 本体の処理

つまり 時間のほとんどは RichEdit ではなく、GDI へ文字幅を尋ねるシステム コールだった。 どういうときに遅いのかを、行の中身を変えながら 30,000 行で測って絞り込んだ。

1 行の中身(30,000 行)読み込み
英数字だけ147 ms
日本語だけ228 ms
日本語+英字の混在7,224 ms

日本語と英字が同じ行に混ざると、GDI の文字幅キャッシュが効かなくなって 30〜50 倍遅くなる。 行数に比例して増える(7,500 行 1.8 秒 → 120,000 行 29 秒)。今回のファイルは「空白+行番号+日本語+英字」で、 日本語の実文書ではごくありふれた形なので、まともに直撃していた。

RichEdit 側の設定で回避できないか、次をすべて試したが効果はなかった。

15.5 メモ帳が速い理由と、Windows App SDK を抱える案

メモ帳の本文は RichEditD2DPT というウィンドウ クラスだった。これは Windows App SDK の WinUIEdit.dll(DirectWrite 描画の新しい RichEdit)が提供するもので、msftedit.dll には無い。 「Windows App SDK を同梱すれば使えるのでは」と考えて実際に試した。

試したこと結果
WinUIEdit.dll を LoadLibrary読み込めるが RichEditD2DPT は登録されない
DllGetVersion を呼ぶ/エクスポート 24 個を確認クラスを登録する関数は無い
CreateWindowEx("RichEditD2DPT", …)失敗(err=1407 クラスが存在しない)
Windows App SDK 1.8 を参照し、XAML Islands で WinUI を初期化初期化後も登録されない。WinUI の TextBox/RichEditBox を置いても、その名前の子ウィンドウはできない

メモ帳の速さは NotepadTextBox というメモ帳独自の実装によるもので、公開された部品ではなかった。 しかも、同じ 10.4 MB のファイルを WinUI のコントロールに入れると次のとおりで、そもそも速くならない。

方式10.4 MB・12 万行
WinUI 3 TextBox28.4 秒(時間の 92 % は WinUIEdit.dll の中)
WinUI 3 RichEditBox52.4 秒
現行 RichEdit(比較)29.0 秒

配布物も 153.6 MB → 251.8 MB(ファイル数 265 → 585)に増え、WinUI の初期化で起動も約 190 ms 延びる。 FB-11 で 505 ms → 221 ms にした成果を失ううえ、肝心の問題が直らないので、この案は捨てた。

15.6 見方を変える:問題は「合計時間」ではなく「止まっている時間」

メモ帳をよく観察すると、縦スクロールの範囲が「行数 × 1 行の高さ」ちょうど(120,001 × 42 = 5,040,042)で、 開いた直後から最後まで変わらなかった。つまりメモ帳は全行を測らずに見積もり、画面に出た行だけ測っている。 だから折り返しがオンでも一瞬で開ける。

これまで(RichEdit / Edit / WinUI):一度に全部計算する レイアウト計算(この間ずっと操作できない) 操作できる → 43 秒の無応答。リサイズのたびに再発 v0.10.0(Scintilla):空き時間に細切れで計算する 操作できる → 合計時間は同じでも、途切れずに操作できる(最長の無応答 34 ms)
図 15-1:同じ計算でも、まとめて行うか空き時間に分けて行うかで「固まる/固まらない」が変わる

そこで計測の指標を「合計時間」から「操作を受け付けない時間の最長」に変え、 docs/tools/measure-freeze.ps1 で測り直した。この指標で候補を比べると次のようになる。

本文のコントロール読み込み中の最長の無応答最大化元のサイズ
メモ帳(参考)0 ms0 ms0 ms
Scintilla0〜22 ms0 ms0 ms
Win32 の Edit コントロール3,800〜28,000 ms同左同左
RichEdit(v0.9.2)43,456 ms19,944 ms20,006 ms

Win32 の Edit コントロールも候補だった(折り返しオフなら 0.2 秒で開ける)が、折り返しオンだと フォント次第で 3.8〜28 秒の無応答が残り、多段階の「元に戻す」も自前で作る必要があった。 Scintilla は折り返しの計算自体には時間がかかる(Consolas で 11〜33 秒)ものの、それを空き時間に分けて進めるので 一度も画面が止まらない。

15.7 v0.10.0:本文を Scintilla に差し替えた

採用前に、これまでのフィードバックで作り込んだ 2 つを実測して確かめた。

確認RichEdit(現行)EditScintilla
起動:ウィンドウ表示(同一条件・中央値)148.5 ms119.5 ms167 ms
入力→表示(FB-1。20 回の中央値)16.9 ms16.8 ms16.7 ms

入力レスポンスは 3 つとも計測の下限(画面の 1 フレーム=16.7 ms)に張り付いており、差は無い。 起動は配布版で 229 ms → 256 ms(同じ端末のメモ帳は 202 ms)で、約 30 ms の増加に収まった。

実装で押さえた点は次のとおり。

10.4 MB・12 万行の日本語ファイルを開いた MemoPad v0.10.0 の画面
図 15-2:v0.10.0 で 10.4 MB・12 万行(504 万文字)の日本語ファイルを開いたところ。待たされずに操作できる

15.8 確かめ方

確認v0.9.2v0.10.0
10.4 MB・12 万行を開く:最長の無応答43,456 ms34 ms
同:最大化/元のサイズ19,944 / 20,006 ms0 / 0 ms
起動:ウィンドウ表示(配布版・10 回の中央値)229 ms256 ms
ホイールで最後の空行まで(docs/tools/test-scroll.ps1)OKOK
折り返しオフの横スクロール(同上)OKOK
多段階の元に戻す/やり直し(docs/tools/test-editing.ps1)OKOK
保存の往復(CRLF/LF の維持、日本語)OKOK(10 項目すべて)
タブのクリック切り替え(docs/tools/test-tabclick.ps1)OKOK
インストーラの大きさ51.2 MB54.3 MB(+3.1 MB)
残った課題:本文が UI Automation に出なくなった(ControlType.Pane・パターンなし)。 RichEdit は ControlType.Document + TextPattern/ValuePattern で読めていたので、 スクリーン リーダーなどの支援技術に対しては後退している。Scintilla の SCI_SETACCESSIBILITY は GTK 版だけの機能で Windows では効かない。独自のアクセシブル オブジェクトを返す・WM_GETOBJECT を 自前で処理する・UI Automation の問い合わせに答えず MSAA へ橋渡しさせる、の 3 つを試したが出せなかった。 UI Automation プロバイダー(IRawElementProviderSimple/ITextProvider)の自前実装が要るため、 別の課題として扱う。

15.9 v0.10.0 の確認と、開いた直後だけ操作がカクつく件

v0.10.0 を使ってもらい、2026-09-13 に次の返事を受けた。

大きなファイル(big-ja-120k.txt)の開く速度とその後の「固まる」部分は解消した。折り返しあり・なしに関わらずストレスなく起動できるようになった。インストーラの公開は可とする。

これを受けて 15.2 の配布停止を解除し、v0.10.0 のインストーラを GitHub Releases に置いて配布を再開した。 不具合が残る v0.6.0〜v0.9.1 のインストーラは取り下げたままにする。

あわせて、次の確認を受けた。

大きなファイルを開いた時だけ、すぐにウィンドウを移動させたりするとカクカクする。3 秒ほど待ってからウィンドウ移動する分には問題なくスムーズになる。これは遅延読み込みにおける処理をそちらにまわしているから生じることだと推測するがどうか?

推測のとおりだった。Scintilla が折り返し位置の計算を「空き時間に少しずつ」進めている間だけ、ウィンドウの移動がその計算と順番待ちになる。以下は確かめ方と数字。

測り方

アプリにファイルのパスを渡して起動し、別プロセスから 12 ms ごとに次の 3 つを記録した(マウス・キーボードは一切触らない)。

結果

条件忙しい区間その間の CPUその間の往復時間収まったあと
折り返しオン(開いた直後)ウィンドウが出た 0.4 秒後〜4.2 秒後1 コアをほぼ占有(500 ms/500 ms)中央値 9.7 ms(90 % 点 10.0 ms・最大 12.4 ms)CPU 0・往復 0.1 ms
折り返しオフ(開いた直後)なしほぼ 00.3 ms同左
折り返しオン・ウィンドウ幅を変えた直後変更から 4.6 秒1 コアをほぼ占有中央値 10.7 msCPU 0・往復 0.3 ms
10.4 MB・12 万行を開いた直後、UI スレッドが返事をするまでの時間(12 ms ごとに外から計測) Scintilla が折り返しを計算している約 3.8 秒 1 回分の計算=約 10 ms ↑ 開いた瞬間の読み込み(85 ms)。この 1 回だけは縦軸の上限を超える 0 8 16 ms 0 2 秒 4 秒 6 秒 8 秒 折り返しオン(この間はウィンドウ移動がカクつく) 折り返しオフ(計算が要らないので最初から静か) 縦軸は WM_NULL の往復時間。ウィンドウの移動もこれと同じ列に並ぶので、待たされた分だけ動きが飛ぶ
図 15-3:開いた直後の UI スレッドの応答。折り返しオンでは約 4 秒のあいだ「約 10 ms 待たされる」状態が続き、そのあと 0.1 ms に戻る

なぜカクつくのか

Scintilla は折り返し位置の計算を一度にやらず、10 ms ずつに区切って空き時間に進める(Windows 版では 10 ms 間隔のタイマーで 1 回分を回す。タイマーのメッセージは順番待ちの列が空のときだけ届く低優先度のもの)。 計測した往復時間の中央値 9.7 ms・90 % 点 10.0 ms は、この 1 回分の長さがそのまま出たもの。

ウィンドウをドラッグしている間、Windows はアプリと同じスレッドの中で移動用のループを回している。 マウスの移動と折り返しの計算が同じ列に交互に並ぶので、1 コマ描くたびに最大 10 ms 余分に待つ。 画面の 1 コマは 16.7 ms(60 Hz)なので、半分以上を計算に持っていかれる計算になり、動きが飛んで見える。 全行の計算が終わるとタイマーは止まり、往復は 0.1 ms に戻る――ユーザーの言う「3 秒ほど待つとスムーズ」はこれ。

これは FB-19 で RichEdit から Scintilla に替えたときの仕組みそのものでもある。 同じ量の計算を、RichEdit は一度にやって 43 秒固まり、Scintilla は 10 ms ずつに割るので固まらない。 ただし割って進めている間は、他の操作と CPU を分け合うことになる。トレードオフの表と裏の関係にある。

今回のファイルは 1 行 41 文字で、このウィンドウ幅では実際には 1 行も折り返さない(縦スクロールの範囲は 120,000 行のまま変わらない)。 それでも「折り返す必要がない」と判断するには 120,000 行すべての幅を測る必要があるので、同じ約 4 秒がかかる。 折り返しオフでこの区間がまったく無いのは、Scintilla が画面に出ている行の幅しか測らないため。

ここまでで分かったこと

直す場合の候補は次の 4 つ。いずれも入力レスポンス(FB-1)と起動時間(FB-11)に 響きうるため、ユーザーの判断で実測してから決めることにした(結果と採用した案は 16 節)。

案ねらい
ⓐ 移動・リサイズ中だけ計算を止めるドラッグしている間は計算を止め、放したら再開する。計算の総量は変えない
ⓑ DirectWrite + レイアウトの別スレッド化(SCI_SETLAYOUTTHREADS)計算そのものを別スレッドへ逃がす。Scintilla 5.3 以降の機能(同梱は 5.6.3)
ⓒ 文字幅キャッシュを増やす(SCI_SETPOSITIONCACHE)同じ文字の幅を測り直す回数を減らす
ⓓ DirectWrite だけにする文字幅の問い合わせ経路を GDI から変える

16. FB-21:開いた直後のウィンドウ移動がカクつく(v0.10.1)

原因(15.9)が分かったところで、対策の 4 案を切り替えられるビルドを作り、 ドラッグの滑らかさ・入力レスポンス・起動時間・大きなファイルの読み込みを同じ端末で測って比べた。

16.1 ドラッグの滑らかさ

大きなファイルを開いた直後(=折り返しの計算中)にタイトル行を 3 秒ドラッグし、マウスを 8 ms ごとに動かしながら、 ウィンドウの位置が実際に更新される間隔を測った。

条件3 秒間の更新回数間隔の中央値90 % 点最大
参考:小さいファイル(計算が無い状態)16116.7 ms32.4 ms34.1 ms
現行 v0.10.04171.4 ms81.4 ms92.1 ms
ⓐ 移動・リサイズ中だけ計算を止める15716.7 ms32.3 ms35.3 ms
ⓑ DirectWrite + 別スレッド11516.9 ms62.3 ms72.5 ms
ⓐ+ⓑ16116.7 ms32.1 ms34.2 ms
ⓒ 文字幅キャッシュ増4271.4 ms81.8 ms82.2 ms
ⓓ DirectWrite だけ4171.7 ms81.4 ms131.4 ms
ドラッグ中にウィンドウの位置が更新される間隔(中央値・ms。短いほどなめらか) 参考:小さいファイル(計算なし) 16.7 現行 v0.10.0 71.4 ⓐ 移動中だけ計算を止める 16.7 ⓑ DirectWrite + 別スレッド 16.9 ⓒ 文字幅キャッシュ増 71.4 ⓓ DirectWrite だけ 71.7 016.7335067 ※ 16.7 ms = 画面の 1 コマ。3 秒間ドラッグし、マウスは 8 ms ごとに動かして計測
図 16-1:ⓐ は「計算が無い状態」と同じ 16.7 ms まで戻る。ⓒ・ⓓ は効果がない

16.2 入力レスポンス・起動時間・読み込み

「軽快さを損なわない」ことが第一なので、残りの 3 つも測った。入力レスポンスは docs/tools/measure-input.ps1(今回追加。キーを送ってから画面のピクセルが変わるまでを外から測る)、 起動は measure-startup.ps1、読み込みは measure-openfile.ps1。

案入力→表示
(空文書・20 回の中央値)
起動:ウィンドウ表示/描画完了
(8 回の中央値)
大きなファイルを開く
(応答が戻るまで)
折り返しの計算が
終わるまで
現行 v0.10.014.9 ms267 / 479 ms256 ms4.1 秒
ⓐ15.5 ms266 / 496 ms273 ms4.1 秒
ⓑ15.4 ms266 / 483 ms462 ms0.13 秒
ⓐ+ⓑ16.6 ms273 / 469 ms446 ms0.13 秒

入力→表示は 4 つとも画面の 1 コマ(16.7 ms)以内で、差は計測のばらつきの範囲。起動時間も差が無い。 ⓑ だけは大きなファイルを開く時間が 256 ms → 462 ms に増える(DirectWrite の初期化の分)。

ⓑ の副作用:文字の見た目が変わる。同じ文章を GDI と DirectWrite で描いて画素を比べると、 文字の形と位置はほぼ同じだが、にじみ(アンチエイリアス)の出方が違う(サンプルした画素の 10.3 % で差、最大の色差 185)。 メモ帳と同じ見た目を保つ方針なので、これは採用のハードルになる。

16.3 採用:ⓐ(移動・リサイズ中だけ計算を止める)

ドラッグは「計算が無い状態」と同じ数値まで完全に戻り、入力・起動・見た目・読み込み時間はどれも変わらない。 変更も小さく副作用が読みやすいので ⓐ を採用した。ⓑ は「計算そのものを 4.1 秒 → 0.13 秒に縮める」魅力があるが、 描画エンジンが変わる・読み込みが 0.2 秒増える・ドラッグは ⓐ ほど完全にならない、の 3 点で見送った。

実装は 2 か所だけ。Scintilla の空き時間の処理は 10 ms 間隔のタイマー(番号 2)で回っているので、 そのタイマーだけを通さないようにする。

// Views/PlainTextEdit.cs
case WM_TIMER when PauseIdleWork && (int)m.WParam == ScintillaIdleTimerId:
    return;                       // 折り返しの計算を通さない(止めている間だけ)

// Views/MainForm.cs
case WM_ENTERSIZEMOVE:            // 移動・リサイズのループに入った
    foreach (var t in _tabs) t.Editor.Edit.PauseIdleWork = true;
    break;
case WM_EXITSIZEMOVE:             // 放した
    foreach (var t in _tabs) t.Editor.Edit.PauseIdleWork = false;
    break;

画面に見えている範囲の折り返しは描画のときに計算されるので、止めていても表示は正しい。 実際に右端をドラッグして幅を 1500 → 630 px に縮めても、本文は新しい幅で折り返されて表示された。 放した時点で計算は再開し、最後まで終わることも確かめた(3.6 秒で完了)。

16.4 確かめ方

確認v0.10.0v0.10.1
開いた直後のドラッグ:位置の更新間隔(中央値)71.4 ms16.7 ms
同:3 秒間の更新回数41 回159 回
ドラッグ中に計算が止まっているか(WM_NULL の往復)0.74 ms0.01 ms
放したあとに計算が再開して終わるか—OK(3.6 秒で完了)
入力→表示(20 回の中央値)14.9 ms15.9 ms
起動:ウィンドウ表示/描画完了(8 回の中央値)267 / 479 ms260 / 445 ms
10.4 MB・12 万行を開く(応答が戻るまで)256 ms363 ms
多段階の元に戻す/やり直し・保存の往復(test-editing.ps1)OKOK(10 件すべて)
ホイールで最後の空行まで・折り返しオフの横スクロール(test-scroll.ps1)OKOK
タブのクリック切り替え(test-tabclick.ps1)OKOK
残った課題:折り返しの計算中に文字を打つと、1 文字出るのに 117〜133 ms かかる(計算が終われば 15.7 ms に戻る)。 ⓑ でも 118 ms で改善しなかった。レイアウトの結果が出るまで描画を待つ必要があるためで、別の課題として扱う。

17. FB-22:端末を起動して最初の起動だけ遅い(v0.10.2)

17.1 見立て

2 回目以降が速いのは、1 回目に読んだ DLL が Windows のファイル キャッシュ(メモリ)に残るため。端末を起動した直後はキャッシュが空なので、 DLL をディスクから読み、Defender の初回の検査も受ける。MemoPad は .NET ランタイムを同梱しているので、使う DLL はすべて MemoPad のフォルダーにあり、ほかのアプリと共有されない(メモ帳の部品は Windows 自身がすでに読み込んでいる)。 つまり起動時に触るファイルの量が、そのまま初回の時間になる。

17.2 起動時に何を読んでいるか

起動 3 秒後の MemoPad が読み込んでいる DLL を数えると、アプリのフォルダーから 48 個・76.8 MB。その中に、ダイアログを開くまで 要らないはずの WPF 本体と、使っていないはずの暗号化ライブラリが入っていた。診断ログ(MEMOPAD_PERF=1)に 「アセンブリの読み込みと、その呼び出し元のスタック」を記録する処理を足して、きっかけを突き止めた。

読み込まれていた DLL大きさきっかけ
PresentationFramework.dll15.4 MBAssemblyInfo.cs の [assembly: ThemeInfo(...)](WPF のプロジェクト テンプレートの残り物)。Windows Forms は初期化のときに exe の属性(TargetFrameworkAttribute)を探し、その途中でほかの属性の型も解決するため、ThemeInfo のある WPF 本体まで読み込まれる
PresentationCore.dll8.2 MBScintilla5.NET のパッケージが全 CPU 向けの Scintilla.dll/Lexilla.dll を Content として足し、WPF のビルドが Content ごとに AssemblyAssociatedContentFile 属性(PresentationCore の型)を exe に付ける。上と同じ経路で読み込まれる
System.Security.Cryptography.dll2.0 MBScintilla.NET が最初のエディタを作るとき、Scintilla.dll と Lexilla.dll の電子署名(Authenticode)を WinVerifyTrust で検証し、発行元を X509Certificate2 で確かめている

電子署名の検証は、証明書チェーンの組み立てと証明書ストアの読み込みを伴う。温まった状態でも約 0.1 秒かかっていた(診断ログで、 暗号化ライブラリの読み込みから検証の終わりまで 126 ms)。端末の起動直後は、この処理に関わるファイルもキャッシュに無い。

17.3 直し方

7.1.1 で Scintilla 本体は 5.6.3 → 5.6.7 になる。変更履歴に Windows 版の動きを変える項目は無く、FB-21 の対策が頼っている 「折り返しの計算はタイマー番号 2」も変わっていないことをソースで確かめた。

17.4 確かめ方:キャッシュに無い状態を作る

端末の再起動はこちらではできないので、発行フォルダーを毎回 robocopy /J(バッファーを使わないコピー)で新しい場所へ写してから起動した。 この方法で書いたファイルはファイル キャッシュに載らず、Defender にとっても初めてのファイルになるので、「再起動直後」に近い(やや悪い側の)状態になる。

確認v0.10.1v0.10.2
起動時に読む DLL(アプリのフォルダー)48 個・76.8 MB43 個・50.9 MB
キャッシュ無し:ウィンドウ表示/描画完了(5 回の中央値)902 / 1,102 ms737 / 967 ms
温まった状態:同(10 回の中央値)263 / 477 ms218 / 434 ms
10.4 MB・12 万行を開く(応答が戻るまで、3 回の中央値)397 ms347 ms
インストールされるファイル274 個・163.3 MB268 個・156.6 MB
起動してウィンドウが表示されるまで(中央値・ms。短いほど速い) キャッシュ無し v0.10.1 902 キャッシュ無し v0.10.2 737 温まった状態 v0.10.1 263 温まった状態 v0.10.2 218 02505007501000 ※ キャッシュ無し=毎回バッファーを使わずに新しい場所へコピーしてから起動(5 回)。温まった状態は 10 回
図 17-1:キャッシュ無しで 165 ms、温まった状態でも 45 ms 縮んだ

キャッシュ無しの時間の内訳も見た。v0.10.2 で、温まった状態は約 220 ms、「キャッシュには載っているが Defender には新しいファイル」 (普通にコピーして全ファイルを読んでから起動)で約 430 ms、キャッシュにも無いと約 700 ms。Defender の初回の検査とディスクの読み込みがほぼ半分ずつで、 どちらも読むファイルの量に比例する。

単一ファイル化は逆効果だった。DLL を exe 1 つ(157 MB)にまとめる PublishSingleFile も試したが、 新しいコピーからの 1 回目が 3,456 ms、2 回目でも 1,357 ms かかった。Defender が大きな exe を丸ごと検査するためで、採用しなかった (温まった状態は 221 ms で差は無い)。複合 ReadyToRun(8 節)と同じく、初回起動は「新しいコピー」で測ってから判断する。
端末を再起動した直後の実測は、ユーザーに確認をお願いしている。こちらの数字は近似で、再起動の直後は Windows 自身の 初期化と並行して読むぶん、さらに時間がかかりうる。残りの大半は .NET ランタイムと Windows Forms 本体(合わせて約 40 MB)の読み込みで、 これ以上縮めるには「サインイン時に先読みしておく」など別の手段が要る。

18. FB-23:行頭のカーソルが見づらい(v0.10.2)

18.1 原因:左の余白が灰色で塗られていた

行頭にあるキャレット(文字入力のカーソル)を、点滅の前後で画面を連写して差分を取り、位置と太さを測った。ユーザーの配色 (背景 #1F2B24・文字 #D6E4DA)では、本文の左の余白(150 % 表示で 12 px)が背景色ではなく 明るい灰色 #F0F0F0 で塗られており、1 px のキャレットがその帯のすぐ右隣に描かれていた。 明るいキャレットが明るい帯の縁に接しているので、縦線があるのか帯の縁なのか見分けがつかない。

左:v0.10.1。濃い緑の本文の左に灰色の帯があり、3 行目の行頭のキャレットが帯の縁に接している。右:v0.10.2。帯が無く、キャレットが縁から離れて背景の上に見える
図 18-1:3 行目(空行)の行頭にキャレットがある状態(2 倍に拡大)。左の v0.10.1 はキャレットが灰色の帯に張り付いて見分けにくい

余白は Scintilla の 0 番の欄(マージン)で作っている。欄の種類が「記号(Symbol)」だと行番号用のスタイルの背景色で塗られ、 その色は全スタイルをそろえる StyleClearAll の中で、わざわざシステムの灰色(COLOR_3DFACE)に戻される。 白い背景では #F0F0F0 と白の差がほとんど目立たないので、v0.10.0 から気づかなかった。欄の種類を 「本文の背景色(MarginType.BackColor)」に変えて直した。

18.2 太さ:メモ帳も 1 px

「太さを変えればよいか」という相談について:太さはメモ帳も MemoPad も 1 px で同じだった(Windows の「テキスト カーソルの太さ」が既定の 1 のとき)。 見づらさの主因は 18.1 の帯なので、太さは変えずに、Windows の設定(アクセシビリティ → テキスト カーソル → テキスト カーソルの太さ)に従うようにした。 Scintilla はこの設定を読まないので、v0.10.1 までは設定で太くしても MemoPad だけ 1 px のままだった。起動時に設定を読み、 設定が変わったときの通知(WM_SETTINGCHANGE)でも読み直す。起動中に 1 → 4 → 1 と変えて、キャレットが 1 → 4 → 1 px に追従することを確かめた (Scintilla.NET 7.0.0 は幅を 3 px までに制限していたが、7.1.1 で Scintilla 本来の 20 px まで使える)。

18.3 余白の幅もメモ帳に合わせた

見える左端から行頭のキャレットまでを測ると、メモ帳は 26 px(論理 17.3 px)、MemoPad は 13 px(論理 8.7 px)と半分しかなかった(150 % 表示)。 RichEdit 時代に決めた 8 px のままだったためで、最大化すると画面の縁から 13 px の位置に 1 px の線が立つことになる。 ユーザーと相談して余白を 8 → 17 px(96 dpi 換算)に広げ、メモ帳と同じ 26 px にした。

確認(150 % 表示)v0.10.1v0.10.2メモ帳
左の余白の色#F0F0F0(灰色)本文の背景色背景色
見える左端から行頭のキャレットまで13 px26 px26 px
キャレットの太さ(設定が 1 のとき)1 px1 px1 px
Windows の「テキスト カーソルの太さ」効かない従う(起動中の変更にも追従)—

19. FB-24:メニューのアクセス キーが効かない(v0.10.2)

19.1 見つけた経緯と現象

FB-22 の確認で撮影スクリプト(capture-memopad.ps1)を流すと、フォント・色変更などのダイアログが 1 つも開かなかった。 修正前の v0.10.1 でも同じだった。キー入力を再現して調べると、Alt+O で書式メニューは開くが、続けて F を押すと メニューが閉じて本文に「f」が入る(IME がオンなら未確定の文字になる)。矢印キーと Enter では選べる。 フォーカスが検索バーの入力欄にあるときは正しく開いた。IME は Meltype と Microsoft IME のどちらでも同じだった。

19.2 原因:IME の確定文字はメニューを素通りする

Windows Forms のメニューは、開いている間もフォーカスを本文に残したまま、キー入力のメッセージ(WM_KEYDOWN・WM_CHAR)を メッセージ ループの段階でメニューへ付け替えて、アクセス キーを受け取っている。本文(Scintilla)に届いたメッセージを記録すると、 F を押したときに届いていたのは WM_KEYDOWN ではなく、IME からの WM_IME_STARTCOMPOSITION → WM_IME_COMPOSITION(確定文字「f」)→ WM_IME_ENDCOMPOSITION だった。IME の確定文字はフォーカスのあるウィンドウへ直接送られるので、 付け替えを通らない。Windows 標準のメニューはメニューの間 IME を止めるが、Windows Forms のメニューは止めない。

キー F メニューの間は IME を切り離す(v0.10.2)/入力欄にフォーカスがあるとき WM_KEYDOWN / WM_CHAR メッセージ ループメニューへ付け替え メニュー「フォント」を実行 v0.10.1:本文にフォーカスがあり、IME が有効なとき IME WM_IME_COMPOSITION「f」本文へ直接送られる 本文に「f」メニューは閉じる
図 19-1:IME を通った文字はメッセージ ループの付け替えを通らずに本文へ届く

RichEdit 時代(v0.9.x)の撮影スクリプトではダイアログを撮れていたので、本文を Scintilla にした v0.10.0 から起きていたとみられる。

19.3 直し方

メニューが開いている間(メニュー バーは MenuActivate〜MenuDeactivate、右クリック メニューなどは Opened〜Closed)だけ、フォーカスのある部品から IME を切り離し(ImmAssociateContext に NULL)、 閉じたら元に戻す(MainForm.Menu.cs の SuspendImeForMenu/ResumeImeAfterMenu)。Windows 標準のメニューと同じ振る舞いになる。

19.4 確かめ方

本物のキー入力と同じ経路(keybd_event)でキーを送り、ダイアログが開くか、検索バーにフォーカスが移るかを見た。

操作(本文にフォーカスがある状態から)v0.10.1v0.10.2
Alt+O → F(フォント)NGOK
Alt+F → U(ページ設定)NGOK
Alt+H → A(MemoPad について)NGOK
Shift+F10(右クリック メニュー)→ F(検索)NGOK
上の 4 つを IME オン/IME を閉じた状態の両方で8 件とも NG8 件とも OK
メニューやダイアログを閉じたあと、IME で「あ」と入力できるかOKOK

あわせて、IME がオンだと送った英字が未確定になって「編集すると * が付く」が誤って NG になる test-editing.ps1 を、 入力の前に IME を閉じるように直した。

20. FB-25:それでも最初の起動に「ひと呼吸」ある(見送り)

20.1 残っている 51 MB の内訳

v0.10.2 の発行版を起動し、3 秒後に読み込まれている DLL のうち、アプリのフォルダーにあるものを数えた(43 個・51.0 MB)。 端末を起動した直後は、これをディスクから読み、Defender の初回の検査を受ける(17 節)。

起動時に読む DLL 51.0 MB の内訳(1 MB=12 px) .NET ランタイム 21.7 MB Windows Forms 19.9 MB ここまで 41.6 MB(82 %)。アプリの作りを変えない限り減らせない CoreLib 14.6・coreclr 4.6・clrjit 1.9・起動部 0.7 本体 13.1・Primitives 3.8・描画など 2.9 Scintilla 3.0(うち Lexilla 1.3) 設定の JSON 1.8 MemoPad 本体 0.6 その他 3.9(文字コード表 0.8 など)
図 20-1:.NET ランタイムと Windows Forms 本体で 8 割を占める

20.2 作りを変えずに削れるのは 4 % ほど

最初の表示までに使っていないのは、Lexilla.dll(構文の色分け用。プレーン テキストでは使わない。1.3 MB)と、Shift_JIS などの文字コード表(ファイルを開くまで要らない。0.8 MB)くらいだった。 合わせて 2.1 MB で全体の約 4 %、時間にすると 30 ms 程度の見込みで、「ひと呼吸」の体感は変わらない。手を入れる危険に見合わないので見送った。 大きく減らすには配り方そのものを変えるしかなく、どれも別の悪影響がある。

配り方を変える案悪影響
Native AOT(機械語に事前変換して .NET ランタイムを持たない)Windows Forms は .NET 9 では対応していない
トリミング(使わないコードを DLL から削る)Windows Forms は対応していない。動いても、実行時にしか分からない欠落が起こりうる
.NET ランタイムを同梱せず、Windows にインストールされたものを使う利用者に .NET デスクトップ ランタイムを別に入れてもらう必要がある。再起動直後はそれもキャッシュに無いので、効果も限られる
単一ファイル化・複合 ReadyToRun試したが初回はむしろ遅くなった(17.4・8 節)

20.3 残る手:サインイン時に先読みしておく

2 回目以降の起動が速い(ウィンドウ表示まで約 220 ms)のは、1 回目に読んだ DLL がキャッシュに残り、Defender の検査も済んでいるから。 そこで、Windows にサインインしたときに MemoPad を画面に出さずに起動し、DLL を読むだけ読んで終了させておけば、利用者が最初に起動するときも 2 回目と同じ状態になる。 Microsoft Edge の「スタートアップ ブースト」や Office の起動ブーストと同じ考え方で、Edge や Office と違って常駐はしない。

入れる場合はインストーラで選べる項目にするつもりで、「入れる(既定はオフ)」「入れる(既定はオン)」「今のまま」の 3 つでユーザーに相談した。 ユーザーの方針は「改善で今の機能やほかの項目に悪影響が出るなら、このままでよい」で、サインインのたびに裏で仕事をさせるのはその悪影響にあたるため、 今のままにした(v0.10.2 の初回起動を、最低限許容できる速さとする)。

21. FB-26:配色パターンの画面が 1920×1080 で切れる(v0.10.3)

21.1 原因:作業領域は「解像度 ÷ 表示スケール − タスク バー」

受け取ったスクリーンショットではタスク バーの高さが 72 px あり、1920×1080 の端末は表示スケール 150 % で使われている。アプリから見た画面は 1280×720(論理 px)で、 タスク バーの 48 px を除くと、ウィンドウを置ける高さ(作業領域)は 672 px しかない。配色パターンの画面は 16 個のカードを 4 列×4 段に並べていて、高さが 752 px あった。画面より 80 px 高いので、上端が画面の上にそろえられ、下の説明文の途中から OK・キャンセルまでがタスク バーの裏に隠れた (同じ 1920×1080 でも 125 % なら作業領域は 816 px あり、収まっていた)。 開発に使っている端末は 2560×1440・150 %(作業領域 912 px)で、収まっていたので気づかなかった。

ほかのダイアログも同じ方法(UI Automation でウィンドウの大きさを読み、1.5 で割る)で測った。作業領域を超えていたのは配色パターンだけだった。

ダイアログ高さ(論理 px)作業領域 672 px に
配色パターン752 → 611収まらない → 収まる
フォント520収まる
色変更502収まる
ページ設定471収まる
MemoPad について290収まる
行に移動204収まる
保存の確認148収まる

21.2 直し方:詰めて収め、それでも入らない画面ではスクロール

左:修正前の配色パターン画面。赤い破線(作業領域の下端)より下に説明文と OK・キャンセルがはみ出している。中央:修正後。破線より上に収まっている。右:高さ 480 で試した例。パターンの一覧にスクロール バーが出て、OK・キャンセルは見えている
図 21-1:赤い破線が 1920×1080・150 % の作業領域の下端。修正前(左)は OK・キャンセルが画面の外に出ていた

21.3 確かめ方

手元の画面は作業領域が 912 px あるので、高さの上限を一時的に 480 px に下げたビルドで、一覧がスクロールし OK・キャンセルが見えることを確かめた(図 21-1 の右)。 発行版(v0.10.3)では高さ 611 px で、「夜」を選んで OK を押すと背景色・文字色が変わることも撮影スクリプトで確かめた。 1920×1080 の端末での見え方は、ユーザーに確認をお願いしている。

22. FB-27:インストーラのアイコンを v0.5 までのものに戻す(v0.10.3)

v0.5.2 までのインストーラは、Inno Setup 標準のアイコン(箱に下向きの矢印)だった。v0.6.0 でアプリのアイコンを新しくしたときに、 インストーラ定義(installer/memopad.iss)の SetupIconFile でインストーラにも同じアイコンを付けた。 アプリと同じ文書の絵では、ダウンロードしたファイルがインストーラなのかアプリ本体なのか見分けにくい。SetupIconFile の指定を外して標準に戻した。

版インストーラのアイコン
v0.1.0〜v0.5.2Inno Setup 標準(箱に下向きの矢印)
v0.6.0〜v0.10.2アプリと同じ(クリーム色の文書に緑の罫線)
v0.10.3Inno Setup 標準に戻した(アプリ本体・スタート メニュー・アンインストールの一覧のアイコンは今までどおりアプリのもの)

作ったインストーラからアイコンを取り出し、v0.5.2 のものと同じ絵になっていることを確かめた。

23. FB-28:ボタンの名前が読み上げられない(v0.10.3)

FB-26 の確認で撮影スクリプトを流すと、「色変更」の画面の「その他の色(M)...」ボタンが見つからなかった。UI Automation でボタンの名前を一覧すると、名前が空だった。 Fluent テーマのボタンは Content="保存しない(_N)" のような文字列の _ をアクセス キーとして扱わないので、 アクセス キーの要るボタンは中身を AccessText にしている。この書き方だと、ボタンの名前(読み上げソフトが読む文字)が空になる。 中身が普通の文字列の「OK」「キャンセル」は名前が付いていた。

ダイアログ名前が空だったボタン
色変更その他の色(M)...・その他の色(O)...・既定の色に戻す(D)
保存の確認保存(S)・保存しない(N)
ページ設定縦(O)・横(A)(ラジオ ボタン)
行に移動移動(G)

8 個に AutomationProperties.Name で見たままの名前を付けた。全ダイアログのボタンの名前を一覧し直し、名前の無いものが残っていないことを確かめた (「行に移動」の入力欄にある消去ボタンだけは Fluent テーマが用意する部品で、記号の名前が付いている)。 見た目と操作は変わらない。読み上げソフトで「ボタン」としか読まれなかったものが、ボタンの名前で読まれるようになる。

24. FB-29:「プログラムから開く」の候補を .txt 以外にも広げる(v0.10.4)

インストーラの「追加タスクの選択」には、v0.1.0 から「「プログラムから開く」の候補に MemoPad を追加する」がある(既定でオン)。 ところが候補に入れていたのは .txt だけで、.log や .py などを MemoPad で開くには、「別のアプリを選択」→「PC でアプリを選択する」で MemoPad.exe を探す必要があった。 一度選べば Windows が覚えるので、この端末でも .md・.gitignore・.html・.php・.py には以前に手で選んだ MemoPad が残っていた。

24.1 仕組み:候補に入れるだけで、既定のアプリは変えない

チェックを入れると、インストーラはレジストリの Software\Classes に 2 種類のキーを書く(ユーザー単位のインストールなら HKCU、全ユーザー向けなら HKLM)。 1 つは MemoPad をアプリとして登録するキーで、表示名と開くときのコマンドを持つ。もう 1 つが拡張子ごとの OpenWithList で、ここに MemoPad.exe があると、その拡張子の右クリックの「プログラムから開く」に MemoPad が並ぶ。 ダブルクリックで開くアプリ(拡張子の既定値と、利用者が「常にこのアプリを使う」で選んだ UserChoice)には触らない。

Applications\MemoPad.exe FriendlyAppName = MemoPad(表示名) shell\open\command = "…\MemoPad.exe" "%1" .txt .log .md .py .cs … .(拡張子なし) └ OpenWithList\MemoPad.exe v0.10.3 までは .txt だけ → v0.10.4 で 83 種類 既定のアプリ(ダブルクリックで開くアプリ) 各拡張子の既定値・UserChoice には触らない 表示名・開き方 どの拡張子で出すか 右クリック →「プログラムから開く」 メモ帳 Visual Studio Code MemoPad 別のアプリを選択
図 24-1:右の一覧に MemoPad が並ぶかは拡張子ごとの OpenWithList で決まる。ダブルクリックで開くアプリは変えない

24.2 入れた拡張子(83 種類)

太字はユーザーが挙げたもの。ほかは、プログラムを書くときにテキストとして開くことが多いものを足した。.bat や .ps1 のように実行できる形式も、候補に並べるだけでダブルクリックの動作(実行)は変わらない。

分類拡張子
テキスト・文書.txt(v0.1 から) .log .md .markdown .rst .tex .diff .patch
設定.ini .cfg .conf .config .inf .properties .toml .yaml .yml .env .reg .editorconfig .gitignore .gitattributes
データ.csv .tsv .json .jsonc .xml .xsl .xslt .sql .svg
Web.html .htm .xhtml .css .scss .less .js .mjs .cjs .jsx .ts .tsx .vue .php
C 系・コンパイル言語.c .h .cpp .hpp .cc .cxx .cs .java .kt .go .rs .swift .vb .bas
スクリプト.py .rb .pl .lua .sh .bash .ps1 .psm1 .psd1 .bat .cmd .vbs
ビルド・プロジェクト.sln .csproj .vbproj .vcxproj .props .targets .xaml .manifest .iss .cmake .gradle
拡張子なしMakefile・Dockerfile・LICENSE・README など(レジストリでは . という名前のキー)

インストーラ定義(installer/memopad.iss)では、拡張子を #dim OpenWithExts[83] の 1 か所に並べ、Inno Setup の前処理(#sub/#for)で拡張子ごとの行を出している。 増やすときは配列に足して要素数を直すだけでよい。アンインストールでは、MemoPad.exe のキーを消したあと、空になった OpenWithList と拡張子のキーも消す(uninsdeletekeyifempty)。 ほかのアプリが同じキーに書いた内容があれば残る。チェックボックスの名前(txtassoc)は .txt だけだった頃のままにした。変えると、更新のインストールで前回の選択が引き継がれなくなるため。

MemoPad バージョン 0.10.4 セットアップの「追加タスクの選択」ページ。「関連付け(.txt・.log・.md・ソース コードなど。既定のアプリは変えません):」の下に、チェック済みの「「プログラムから開く」の候補に MemoPad を追加する(O)」がある
図 24-2:インストーラの「追加タスクの選択」。見出しに対象と「既定のアプリは変えない」ことを書いた

24.3 確かめたこと

「プログラムから開く」の画面をスクリプトから開くと、この端末ではすぐに閉じてしまった。そこで、Explorer が右クリックの一覧を作るのと同じ API(SHAssocEnumHandlers、ファイル単位では IShellItem の BHID_EnumAssocHandlers)で候補を取り出して確かめた。この端末にユーザー単位で上書きインストール → アンインストール → 入れ直しの順に試した。

確かめたこと結果
候補に MemoPad が並ぶ拡張子入れる前 6/83(.txt と、以前に手で選んだ 5 種類)→ 入れた後 83/83
実際のファイル(Makefile・Dockerfile・LICENSE・.gitignore・.env・sample.log・app.config・main.rs)すべて候補に MemoPad が並ぶ
種類の名前・既定のアプリ・ダブルクリックで実行されるコマンド83 種類とも変化なし
アンインストールMemoPad が作ったキーはすべて消え、空のキーも残らない。もとからあったキー(VS Code の OpenWithProgids など)はそのまま
入れ直し1 回目のインストール直後と、レジストリ・関連付けとも一致

なお、「常にこのアプリを使う」で MemoPad を既定にしていた拡張子(この端末では .txt と .php)は、アンインストールすると MemoPad.exe が無くなるので既定から外れ、入れ直すと戻った。これは v0.10.3 以前も同じ動きで、今回の変更とは関係ない。

25. FB-30:Ctrl+Z で取り消す単位がおかしい(v0.10.5)

「既存のファイルでは 1 文字ずつ取り消されるのに、新規のファイルでは入力したものが全部消える」との報告。既存のファイルでは日本語を入力していた。 v0.10.4 で、入力のしかたを変えながら「入力 → Ctrl+Z を 1 回ずつ押す → そのたびに本文を取り出す」を自動で繰り返して調べた(本文は UI Automation から読めないので、Ctrl+A → Ctrl+C で取り出した)。

25.1 調べたこと:どの入力でも「全部まとめて 1 回」だった

入力(v0.10.4)新規のタブ既存のファイル
英字「abc def ghi」(IME オフ、1 文字 60 ms 間隔)1 回で全部消える1 回で全部消える
英字「abcd」(IME オフ、1 文字 700 ms 間隔)—1 回で全部消える
日本語「日本語」「です」を 2 回に分けて確定(Meltype)1 回で全部消える1 回で全部消える
日本語を確定する前に Ctrl+Z—1 回目は確定だけ(Ctrl+Z を IME が受け取る)、2 回目で確定した全体が消える

新規と既存で違いは出ず、どの入力でも「続けて入力した分が 1 回の Ctrl+Z で全部消える」だった。報告の「1 文字ずつ」は再現できなかった。 1 文字ずつになるのは、Insert キーで上書きモードにしたとき(Scintilla が 1 文字ごとに区切る。上書きモードはタブごとに切り替わる)だけ見つかった。 いずれにしても、メモ帳とは単位が違う。メモ帳で同じように試すと、「単語とその後ろの空白」ごとに取り消され(空白の次に文字を打つと新しい単位になり、「.」では区切らない)、日本語は確定ごとだった。 ユーザーと相談し、取り消す単位をメモ帳と同じにすることにした。単位が決まっていれば、入力のしかたによらず同じ動きになる。

25.2 原因:Scintilla は続けて打った文字を 1 つにまとめる

Scintilla は、編集を 1 回ずつ「元に戻す履歴」に積むとき、直前の操作と同じ種類(挿入なら挿入)で位置が続いていれば、前の操作と 1 つの単位にまとめる。 単語の切れ目でも IME の確定でも区切らないので、カーソルを動かさずに打ち続けた分はすべて 1 つになっていた。区切られるのは、カーソルを動かして別の場所に打ったとき、削除をはさんだとき、貼り付けなどの別の操作をしたときだけ。

25.3 対策:文字が入る前に、メモ帳の切れ目で区切る

Scintilla には「ここから先は前の操作とまとめない」と直接指示する API は無いが、空の BeginUndoAction/EndUndoAction を 1 組呼ぶと、直前の操作に「まとめない」印が付く。 PlainTextEdit の WndProc で、文字が本文に入る前のメッセージ(WM_CHAR、Enter・Tab の WM_KEYDOWN、IME の確定 WM_IME_COMPOSITION)を受け、次のときにこの印を付ける。

選択範囲を置き換える入力と上書きモードは、Scintilla が自分で区切るので何もしない。貼り付け・切り取り・削除の単位も今までどおり。

英字「abc def␣␣ghi.jkl」を続けて打ったあと(␣ は空白) v0.10.4 まで abc␣def␣␣ghi.jkl 1 Ctrl+Z 1 回で全部消える v0.10.5(メモ帳と同じ) abc␣ 3 def␣␣ 2 ghi.jkl 1 単語+後ろの空白ごと(「.」では区切らない) 日本語を「日本語」「です」の 2 回に分けて確定したあと v0.10.4 まで 日本語です 1 Ctrl+Z 1 回で全部消える v0.10.5(メモ帳と同じ) 日本語 2 です 1 確定ごと
図 25-1:Ctrl+Z 1 回で取り消される単位。丸の数字は取り消される順番

25.4 確かめたこと

入力(v0.10.5)Ctrl+Z を 1 回ずつ押したときに消える順
英字「abc def␣␣ghi.jkl」(新規・既存とも)「ghi.jkl」→「def␣␣」→「abc␣」
英字「abc」Enter「def」Enter Enter「ghi」「ghi」→「def」と改行 2 つ →「abc」と改行
日本語「日本語」「です」を 2 回に分けて確定(Meltype)「です」→「日本語」

新規のタブと既存のファイルで同じ動きになった。既存の編集テスト(docs\tools\test-editing.ps1。多段階の元に戻す・やり直し、未保存の印、改行コードと日本語の保存)も 10 件すべて OK。

26. FB-31・32:右クリックの「MemoPad で開く」と .bat(v0.10.5)

FB-31 は「.bat も『プログラムから開く』に」、FB-32 は「右クリックの最初のメニューに『MemoPad で開く』を」という要望。ユーザーのスクリーンショットでは、.bat の右クリックの最初の階層に「Code で開く」「メモ帳で編集」が並び、「プログラムから開く」は無かった。

26.1 .bat には「プログラムから開く」自体が出ない

.bat は v0.10.4 で候補(.bat\OpenWithList\MemoPad.exe)に入れていて、SHAssocEnumHandlers でも MemoPad が候補に並んでいた。ところが、Windows は .bat の右クリックに「プログラムから開く」の項目そのものを出さない。 候補に入れても選ぶ場所が無いので、FB-31 は FB-32 の「MemoPad で開く」でまとめて対応することにした。

26.2 Windows 11 の右クリック メニューは 2 階層

Windows 11 の右クリック メニューの最初の階層に、ほかのアプリが項目を足せるのは、パッケージ(ID を持つアプリ)が登録した項目だけ。項目の中身は IExplorerCommand を実装した DLL で、パッケージのマニフェストの windows.fileExplorerContextMenus で登録する。 メモ帳は Microsoft Store のアプリなので、もともとパッケージを持っている。VS Code はインストール先に「外部の場所を使うパッケージ」(スパース パッケージ)を置き、その中で「Code で開く」を登録している(この端末の VS Code のマニフェストで確かめた。署名は Microsoft)。 これまでのようにレジストリの shell の下に書く項目は、「その他のオプションを確認」(Shift+F10)で開く従来のメニューの中にしか出ない。この端末に入っている、ほかのエディタの「〜で開く」も従来の方式で、最初の階層には出ていなかった。

右クリック(最初の階層) 開く プログラムから開く(.bat には出ない) … Code で開く メモ帳で編集 ← パッケージの項目 MemoPad で開く(署名が得られず見送り) その他のオプションを確認 その他のオプション(従来のメニュー。Shift+F10) 開く 編集 … MemoPad で開く ← v0.10.5 ほかのエディタの「〜で開く」 レジストリの *\shell に書いた項目 (Windows 10 では最初の階層に出る) 最初の階層に出す条件:ID を持つパッケージ + IExplorerCommand の DLL + Windows が信頼する証明書での署名 メモ帳は Store アプリ、VS Code は Microsoft の署名付きのスパース パッケージで満たしている
図 26-1:Windows 11 の右クリック メニューと、項目が出る場所

26.3 最初の階層に出すための署名をどうするか

パッケージは、Windows が信頼する証明書で署名していないと登録できない。コストをかけずに済ませる方法を比べた。

方法出る場所費用・手間判断
A. レジストリの *\shell に書く(従来の方式)「その他のオプションを確認」の中(Windows 10 は最初の階層)無料・すぐできるv0.10.5 で採用
B. SignPath Foundation の無料コード署名(オープンソース向け)最初の階層無料。審査があり、期間は読めず、通るとも限らない。ビルドを GitHub Actions に移す必要がある。発行元は「SignPath Foundation」と表示される。インストーラにも署名できるので SmartScreen の警告も出にくくなる並行して申請する(2026-11-01 に不承認。26.7 節)
C. 自作の証明書を、インストール時に PC の「信頼する発行元」へ入れる最初の階層無料。ただしインストールに管理者権限が要り、利用者の PC に信頼する証明書を増やすことになる一般向けの配布には使わない
D. 有料の署名サービス・証明書最初の階層月額または年額の費用コスト 0 の方針で除外

ユーザーと相談し、v0.10.5 では A で「その他のオプションを確認」の中に出しておき、並行して B を申請することにした。審査が通ったら、IExplorerCommand の DLL とスパース パッケージを作って最初の階層に出す予定だった(審査は通らず、最初の階層は見送った。26.7 節)。

26.4 実装:すべてのファイルに「MemoPad で開く」

インストーラの「追加タスクの選択」に「右クリック メニューに「MemoPad で開く」を追加する」を足した(既定でオン。タスク名 contextmenu)。チェックすると、Software\Classes\*\shell\MemoPad に表示名・アイコン・開くときのコマンドを書く。 * はすべてのファイルを表すので、VS Code の「Code で開く」と同じく、どのファイルの右クリックにも出る。既定のアプリやダブルクリックの動作(.bat なら実行)は変えない。

キー値
*\shell\MemoPad(既定)= MemoPad で開く、Icon = "…\MemoPad.exe",0
*\shell\MemoPad\command(既定)= "…\MemoPad.exe" "%1"

ユーザー単位のインストールでは、HKCU の Software\Classes\* と *\shell をここで初めて作ることがある。アンインストールでは、MemoPad のキーを消したあと、空になったこの 2 つも消す(ほかのアプリのキーがあれば残す)。

26.5 確かめたこと

確かめたこと結果
従来のメニューの項目(Shell.Application の Verbs()。.txt・.bat・.cmd・.py・.reg・.ini)6 種類すべてに「MemoPad で開く」が出る
.bat を「MemoPad で開く」で開くMemoPad に「t.bat - MemoPad」として開き、バッチは実行されない
v0.10.4 からの上書きインストール(タスクの指定なし)新しいタスクは既定どおりオンになり、キーが作られる
アンインストール*\shell\MemoPad と、空になった *\shell が消える。* はほかのアプリのキー(OpenWithProgids・shellex)があるので残る
入れ直しキーが元どおりに作られる

26.6 SignPath Foundation への申請準備

SignPath Foundation は、オープンソースのプロジェクトに無料でコード署名の証明書を提供している。条件(signpath.org/terms)のうち、MemoPad で手当てが要ったものと、用意したものは次のとおり。

条件MemoPad の状態・用意したもの
OSI 承認のライセンス、公開リポジトリ、マルウェアでないことMIT ライセンス、GitHub の公開リポジトリ。通信する機能は無い
すでにリリースしていて、機能をダウンロード ページに書いていることGitHub Releases でインストーラを配布し、紹介ページと README に機能を書いている
ソースから自動化された手順でビルドすること(GitHub がホストするランナー)GitHub Actions のワークフロー .github/workflows/build.yml を作った。発行 → 本体に署名 → インストーラを作成 → インストーラに署名、の順。署名の手順は、リポジトリに SignPath の設定(変数とシークレット)を入れるまで飛ばす
署名するファイルの製品名・製品バージョンをそろえることexe と dll の製品バージョンに付いていたコミット番号(0.10.5+c80201d…)を外し、インストーラにも版情報を入れた。3 つとも製品名 MemoPad・製品バージョンは版番号だけになる
自分たちのファイルだけに署名すること署名するのはインストーラと MemoPad.exe・MemoPad.dll だけ。.NET ランタイムと Scintilla には署名しない。SignPath に登録する設定の控えを .signpath/ に置いた
トップ・ダウンロード ページに「Code signing policy」を載せること(帰属の文、役割、プライバシー)Code signing policy のページを作り、紹介ページ・README・リリースの説明からリンクした
担当者全員が 2 段階認証を使うことGitHub の 2 段階認証はユーザーが確かめる(API からは見えなかった)
インストーラにはアンインストールの手段があり、システムの変更は事前に示すことInno Setup のアンインストーラ。関連付けと右クリック メニューは「追加タスクの選択」で内容を示し、外せる

2026-10-31 にユーザーのアカウントで申請した。すぐに受付の返信があり、数営業日のうちに次の手順か追加の質問を連絡するとのこと。審査が通れば、SignPath 側でプロジェクトと署名ポリシーを作り、GitHub の連携と、リポジトリの変数・シークレットを設定して、ワークフローの署名の手順を動かす予定だった。 なお、Inno Setup はインストーラの版情報の文字列を固定長で書き込むので、製品名が「MemoPad」の後ろに空白の詰まった 60 文字になる。SignPath の製品名の照合で空白が問題にならないかは、再申請して設定するときに実物で確かめる。

26.7 審査の結果と、最初の階層の見送り

申請の翌日(2026-11-01)に、SignPath Foundation から不承認の連絡が来た。理由は品質ではなく、公開の場での知名度が足りないこと。Foundation の証明書は SignPath Foundation の名前で発行するので、すでに広く信頼されているプロジェクトかどうかを、次のような外からの手がかりで判断するという。

知名度が上がれば、再申請すれば審査し直すとのこと。有料のプランも案内されたが、コスト 0 の方針なので使わない。あわせて、26.3 節の比較になかった方法を 1 つ調べた。

方法出る場所費用・手間判断
E. Microsoft Store に MSIX で出す最初の階層(Store から入れた人だけ)無料(個人の開発者の登録は 2025 年 9 月から無料。審査のあとで Microsoft が署名し直すので、証明書は要らない)。ただし MSIX の作成、右クリック用の DLL、Store の掲載情報と審査が要り、リリースのたびに GitHub と Store の 2 つの経路を保つことになる。「MemoPad」の名前が Store で使えるかも分からない。GitHub の Inno Setup 版は従来のメニューのまま見送り

E なら無料で最初の階層に出せるが、そうなるのは Store から入れた人だけで、作業と保守の手間に見合わない。ユーザーと相談のうえ、最初の階層は見送りとし、FB-32 は v0.10.5 の「その他のオプションを確認」の中に出す形で完了とした。 GitHub Actions のワークフローと .signpath/ の控えは、再申請のときのために残す。Code signing policy のページは、リリースの説明からリンクしているので、「今は署名していない」ことを書き足して残した。

27. つまずいた点のメモ