Skip to content
rk0exnPublic

About

Binary Editor for Windows

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Repository files navigation

BinEdit

Win32、Direct3D 11、Direct2D 1.1、DirectWrite で描画する、外部ランタイム不要のバイナリエディターです。

開発時のアーキテクチャ、不変条件、変更時チェックリストは CODEX.md を参照してください。ソースコード内の開発者向けコメントは英語で統一しています。

English README is Here.

ビルド

Visual Studio 2026 で BinEdit.slnx を開き、x64 の Debug または Release をビルドします。プロジェクトは v145 ツールセットを使用します。

& 'C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe' BinEdit.slnx /m /p:Configuration=Release /p:Platform=x64

テスト

ビルド後、以下のテストで各機能を検証できます(PowerShellスクリプトはすべて -Configuration Release を付与)。

テスト 検証内容
x64\Release\BinEdit.Core.Tests.exe コアのファイルI/O、部分保存、外部競合、検索、エンコード、Undo/Redo、20 KiBのランダム編集シナリオ、メインウィンドウ最終タブの「閉じる/切り離す」可否ポリシー(未変更の無題、変更済み無題、名前付き、複数タブ、切り離しウィンドウ、不正インデックスをUI非依存で検証)
tests\AsyncFileIoUiTests.ps1 384 MiBファイルのメモリマップ読み込み/非同期保存、処理中のUI応答、メニュー無効化、入力拒否、4 MiB境界をまたぐ挿入後のデータ整合性
tests\EditorUiStress.ps1 実ウィンドウへ大量の16進入力、ランダム位置のBackspace/Delete/挿入/上書きを送り、保存結果を全バイト照合
tests\ScrollBarUiTests.ps1 D2Dスクロールバーのサムドラッグ、トラックのページ移動、ネイティブスクロールスタイルの不在
tests\MiddleScrollUiTests.ps1 中クリックの上下加速スクロール
tests\BitToolUiTests.ps1 空ファイルでのビット処理ツール起動と入力値/オペランド編集
tests\ExternalChangeUiTests.ps1 外部から原子的に置換されたファイルの自動再読み込み、未保存時の「再読み込み/このまま続行」、各選択後の保存内容
tests\TabDockingUiTests.ps1 複数ファイルのタブ分離、メイン/サブのメニュー差、フォーカス、再ドッキング、メイン終了時の連動終了
tests\TabStripScrollUiTests.ps1 大量タブのShift+ホイール横スクロールとヒットテスト
tests\UntitledCloseUiTests.ps1 変更済み無題タブの保存確認、最後の名前付きファイルをCtrl+Wで閉じて無題へ戻る動作
tests\MemoryReleaseUiTests.ps1 96 MiBファイルがprivateバッファへ複製されないこと、タブを閉じると対象ファイルのマップ領域が全解放されること
tests\MemoryRegionStressUiTests.ps1 サブウィンドウの作成/終了を反復して仮想メモリ領域、Private Bytes、ハンドル、GDI/USER資源を採取。大容量サブ文書のマップ解放、Explorer/DWMの継続、タスクバーUIスレッドの応答性も検証
tests\SearchAsyncUiTests.ps1 非同期検索のワーカースレッド、検索中のUI応答、終了時キャンセル、F3の選択位置
tests\RecoveryUiTests.ps1 メインとサブの未保存ファイルを持つ実プロセスを強制終了し、次回起動時にメインのタブへ集約して復元した内容を全バイト照合

操作

  • Ctrl+O / Ctrl+S: 開く / 保存
  • Ctrl+N: 新しい無題タブを作成
  • Ctrl+W: 現在のタブを閉じる(未保存時は保存確認。メインウィンドウの最後の名前付きファイルまたは変更済み無題を閉じた後は、新しい無題タブへ戻る。サブウィンドウの最後のタブを閉じた場合は、そのサブウィンドウを閉じる)
  • Ctrl+Shift+S: 現在のタブを名前を付けて保存
  • Alt+F4: 現在のウィンドウを終了
  • Ctrl+Tab / Ctrl+Shift+Tab: 次/前のタブへ切り替え
  • タブが横幅に収まらない場合は、Shift+マウスホイールでタブ列を左右へスクロール
  • タブ幅に収まらないファイル名は末尾を省略表示し、タブ名へマウスを置くとD3D11/D2D1ツールチップで省略前のファイル名を表示
  • タブを左右へドラッグ: 同一ウィンドウ内で順序を変更
  • タブをタブ列の外へドラッグ: 編集状態とUndo/Redo履歴を保持したまま新規ウィンドウへ切り離し(元ウィンドウに2タブ以上ある場合のみ)
  • 切り離したタブを別のBinEditウィンドウのタブ列へドラッグ: その位置へ再ドッキング
  • 複数ファイルをコマンドラインへ渡すか、既存ファイルを編集中に「開く」を実行すると、ファイルごとにタブを作成
  • 通常の二重起動は名前付きMutex MX_BinEdit_F80F で抑止し、コマンドラインのファイルを既存プロセスへ転送して新しいタブで開く
  • Ctrl+F / F3: バイナリまたは文字列検索 / 次を検索。検索種別はマウス、Space/矢印キー、Alt+B/Alt+Tで切り替え
  • Ctrl+Z / Ctrl+Y: 元に戻す / やり直す
  • 「ツール」→「ビット処理ツール」: AND、OR、XOR、反転、左右シフト/ローテート、加減乗除を入力値とオペランドから8ビット単位で計算
  • クリック、Shift+矢印: 選択。16進ペインでは16進数字、文字ペインでは文字を入力して編集
  • Insert: 挿入/上書きモードを切り替え。起動時は挿入モードで、現在のモードはステータスに表示
  • Tab: 16進/文字入力ペインを切り替え。アクティブ側はヘッダーのアクセント線とステータスの「入力」で確認可能
  • 末尾まで入力すると次の仮想EOFセルへ進み、続けて入力した場合だけファイルを拡張
  • Backspace: 直前の入力単位を削除。16進ペインは1バイト、文字ペインは選択中の文字形式に基づく1文字
  • Delete: 挿入モードでは現在位置を前方削除。上書きモードでは対象バイトを 00 に置換
  • 右端のD3D11/D2D1スクロールバーをドラッグ、トラックをクリック、またはマウスホイール、Page Up / Page Down: スクロール
  • データ領域を中クリック: オートスクロールを開始。停止帯/上方向/下方向はそれぞれ専用カーソルで表示し、開始点から上下へ離すほど加速。再度の中クリックまたは通常のクリック/キー/ホイール操作で終了
  • 「編集」→「選択範囲をマーク」で範囲マーク。キャレットが仮想EOFセルにある場合は無効
  • 「色」メニューでマーク色、および現在位置のバイト値に対する色を設定。キャレットが仮想EOFセルにある場合、現在バイト値の設定/解除は無効

配色・言語設定

配色とテーマ、文字形式は %LOCALAPPDATA%\BinEdit\profile.json に保存されます。選択、プライマリボタン、フォーカス、キャレットのキーカラーは、現在の Windows ユーザーが設定した UI アクセントカラーに追従します。ハイコントラスト有効時はアクセントカラーを使用せず、Windows の高コントラスト用システム色を優先します。

色の設定にはWindows標準カラーダイアログを使わず、BinEdit独自のD3D11/D2D1カラーピッカーを使用します。彩度/明度面、色相バー、現在色と新色の比較、RGB(0~255)とHEX(#RRGGBB)の直接入力、マウスドラッグ、クリップボード、キーボード微調整、Per-Monitor V2 DPI変更に対応します。

UI は日本語と英語に対応しています。「言語 / Language」メニューの「自動」は Windows のユーザー UI 言語に従い、明示的な選択は profile.json の language に保存されます。

ウィンドウ構成

起動時に最初に作られるメインウィンドウは、プロセス内で唯一の BinEdit.MainWindow クラスです。タブから切り離したトップレベルウィンドウは BinEdit.DetachedTabWindow クラスのサブウィンドウとして扱われ、「テーマ」「言語」および「ファイル」→「終了」を表示しません。サブはメインをWin32 ownerとするため、独立した通常フレーム、移動、サイズ変更、入力フォーカスを維持しながら、切り離し/終了の反復でタスクバーのサムネイル資源を増殖させません。設定変更はメインから全サブへ反映されます。メインを閉じると全ウィンドウの未保存確認を行い、成功した場合はサブも連動してすべて閉じます。

検索画面、ビット処理ツール、「BinEdit について」画面もネイティブ子コントロールを使わず、D3D11/D2D1/DirectWrite で描画します。ライト、ダーク、ハイコントラストの各 Palette、DPI変更、キーボード操作に対応します。ビット処理ツールは入力値とオペランドを編集して結果を表示します。算術オーバーフローは下位8ビットへ折り返し、シフト/ローテートは0~7、除算は0以外のオペランドを受け付けます。

検索の非同期処理

検索は編集用データから作成した共有所有の不変スナップショットに対して非同期実行されます。候補範囲をハードウェア並列数に合わせて最大16ワーカーへ分割し、重複一致、最大10,000件の強調表示、F3の折り返しを維持します。パターンはワイルドカードでリテラル断片に分割し、全ワイルドカードのパターンは候補位置を直接列挙します。リテラルを含む場合は、文書全体から採取したバイト頻度サンプルで最も出現の少ないバイト位置をアンカーに選び、2バイトのSSE2 SIMD seedで16バイト単位に走査して、候補位置だけを全パターンで検証します。強調表示上限とF3の位置が確定したワーカーは残りの範囲を走査せずに終了するため、頻出パターンの全件走査を回避できます。検索中もUIスレッドとR2PR描画はブロックされず、編集・タブ移動・ファイル切り替え・ウィンドウ終了時は旧世代の検索を協調キャンセルします。連続入力による再検索は150 msに集約されます。スナップショットの寿命はコーディネータと全ワーカーの完了まで保持されるため、呼び出し元のバイト列を借用したままバックグラウンド処理することはありません。

ファイルI/O

ファイルの読み込み、通常保存、名前を付けて保存、外部変更による再読み込み、および管理者再起動用の一時退避は、所有されたファイルI/Oワーカーで実行します。処理中もUIスレッドは描画、DPI変更、サイズ変更、システムテーマ変更へ応答し、D2Dのインジケーターと日本語/英語の進捗テキストを表示します。その間は編集、メニュー、タブ操作、終了を無効化し、ワーカーが借用している文書データをUI側から参照しません。追加で届いた単一起動IPC/ドラッグ&ドロップのパスは最大256件まで所有キューへ退避し、現在のI/O完了後に順次開きます。

空でないファイルは CreateFileMappingW(PAGE_READONLY) と MapViewOfFile(FILE_MAP_READ) で読み取り専用マップし、OSのデマンドページングに任せます。ファイル全体をprivateな std::vector へ複製しません。編集内容は永続的なpiece tableとして、変更されていないマップ範囲と小さな所有ブロックを組み合わせて表現するため、1バイトの編集や挿入で大容量ファイル全体をRAMへ展開しません。描画は表示中のバイトだけを参照し、検索はワーカーごとに最大1 MiB、保存と回復記録は最大4 MiBの固定チャンクでストリームします。ファイルまたはタブを閉じると共有スナップショットの最終所有者がビューを UnmapViewOfFile し、マップ領域を解放します。

ダイアログ描画

保存確認、警告、エラーは MessageBox を使わず、独立した GDI ベースのモーダルダイアログで表示します。このダイアログは D3D11/D2D1 に依存しないため、GPU描画の初期化に失敗した場合にも使用できます。情報、警告、エラー、質問の記号はフォント字形ではなく、テーマ色を使った4倍解像度のGDIパスを縮小合成して滑らかに描画し、色付きバッジ内に十分な余白を取った控えめな大きさに統一します。本文とボタン文字はテーマ背景へ透過描画され、Tab/Shift+Tabでボタンフォーカスを順方向/逆方向へ移動できます。ボタンにはD3D系ダイアログと同じ14 DIPのSegoe UI Variable Textを使い、プライマリはSemibold、セカンダリはNormalで描画します。96 DPI(100%)を論理レイアウト基準とし、Per-Monitor V2 のDPI変更時は移動先DPIで本文を再計測して、ウィンドウ、フォント、余白、ボタン、ヒットテスト領域を同時に再スケーリングします。

外観

クライアント領域は WinUI 3 / Fluent を意識した外観です。Segoe UI Variableによる文字階層、単一レイヤーの編集サーフェス、角丸の選択表示とコントロール、控えめな境界線、Windowsユーザーのアクセントカラーを使用します。高DPIの中幅表示では密度を自動調整し、さらに狭い場合は文字ペイン全体を隠して16進ペインを優先します。これは既存のWin32/D3D11/D2D1実装による視覚設計であり、Windows App SDKやXAMLへの移行ではありません。

縦スクロールバーも非クライアント領域のWin32コントロールではなく、D3D11/D2D1で描画します。サムとトラックの寸法はPer-Monitor V2 DPIへ追従し、ライト/ダークではWinUI風の控えめな表示、ハイコントラストではWindowsシステム色を使用します。行位置はSCROLLINFOの32ビット範囲へ縮小せず、ファイル全体のsize_t行数から直接計算します。

中クリックのオートスクロール開始位置にはD2Dの上下方向マーカーを表示します。マーカー付近には停止帯を設け、Windowsのオートパン用カーソルを停止/上/下で切り替えながら、距離に応じた二次加速で大容量ファイルを高速移動します。専用タイマーは操作中だけ有効になり、行位置が変化した場合だけデータ領域とスクロールバーをR2PR更新します。

ダークテーマ時のトップレベルメニューバーは UAH 描画メッセージを利用して背景・ホバー・選択・無効状態を描き分けます。ドロップダウン内のメニュー項目も、選択したシステム/ライト/ダーク設定に連動して Windows のネイティブメニューテーマを更新します。UAH と利用している uxtheme.dll エクスポートは Windows の公開 API ではないため、利用できない環境とハイコントラスト時は Windows 標準描画へフォールバックします。

管理者権限での再起動

保護されたファイルの保存でアクセスが拒否された場合は、現在の編集内容をCNG生成の一時ファイルへ退避し、UAC の同意後に管理者権限で BinEdit を再起動して内容を引き継ぎます。内部コマンドラインの一時ファイル、保存先、回復IDは CommandLineToArgvW の逆変換規則に従って引用し、空文字、空白、引用符直前および末尾のバックスラッシュを安全に保持します。昇格後にも保存先の絶対パスを独自GDIダイアログで再確認し、一時ファイルの名前、実体パス、reparse属性、保存先種別を検証してから原子的に保存します。

終了時の保護

未保存の編集がある間は ShutdownBlockReasonCreate で理由を登録します。ファイルI/O中は処理内容を終了阻止理由として登録し、WM_QUERYENDSESSION ではモーダルUIを出さず終了を拒否します。未保存状態では最新回復世代の非同期更新を開始し、保存後またはセッション終了の取り消し/完了時に登録状態を同期します。

安全性と異常終了からの回復

部分保存と原子的置換

通常の固定長上書き編集は変更された連続範囲だけを元ファイルへ書き込みます。挿入/削除のように長さまたはオフセット構造が変わる編集は、マップ中のストリームを SetEndOfFile するとWindowsが ERROR_USER_MAPPED_FILE で拒否し、マップ元を順方向に書き換えると未コピーの後続データを破壊し得るため、最大4 MiBの固定バッファでCNGランダム名の同一ディレクトリ一時ファイルへストリームし、原子的に置換します。全内容をRAMへ展開することはありません。

非原子的な部分保存を始める前に、保存対象と同一リビジョンの完全な回復スナップショットをflush+原子的置換で確定します。回復記録を確定できない場合は元ファイルを部分更新せず、完全な原子的保存へ切り替えます。離散した変更範囲が4,096件を超えた場合も、巨大な範囲へ結合せず完全な原子的保存へ切り替えます。これにより強制終了が部分保存の途中で起きても次回起動時に保存直前の全内容を復元できます。

保存時の検証

保存するたびに対象の現在のFILE_ATTRIBUTE_READONLYを再取得し、読み取り専用なら管理者権限への再起動を提案せず保存を拒否します。読み込み時の属性は保持しないため、読み込み後に属性が設定/解除された場合も保存時点の状態が使われます。保存トランザクション中は他プロセスの書き込み/置換を拒否し、完了前に FlushFileBuffers を行います。名前を付けて保存、クラッシュ回復データ、外部競合で「このまま続行」を選んだ後は、変更範囲の前提が成立しないため完全スナップショットへフォールバックします。この完全保存では同一ディレクトリのCNGランダム名一時ファイルへ全バイトを書き、既存ファイルは ReplaceFileW、新規ファイルは MoveFileExW で原子的に公開します。プロファイルも最大1 MiBに制限し、同じくflush+原子的置換で保存します。

外部変更の監視

開いているファイルの親ディレクトリはWin32変更通知で監視し、通知時にボリューム/ファイルID、最終更新時刻、サイズを照合します。未保存変更がなければ外部版を自動的に再読み込みします。未保存変更がある場合は、日本語/英語対応の独自GDIダイアログで「再読み込み」または「このまま続行」を選択できます。続行時は現在の外部版を新しい競合基準として記録し、次回保存を完全スナップショットに切り替えます。外部で削除/移動された場合は、メモリ上の内容を未保存として保持するため、誤って閉じても内容が失われません。保存直前にも同じファイル識別情報を再検査するため、監視通知と保存操作の間に発生した更新を無確認で上書きしません。

回復記録

未保存タブは %LOCALAPPDATA%\BinEdit\Recovery へ回復記録を作成します。記録はユーザーとSYSTEMだけの保護DACL、SHA-256完全性検査、CNG生成128ビットID、flush+原子的置換を使用し、対応ボリュームではEFSも有効化します。最初の変更は直ちに、入力停止後は500 ms、連続入力中は最大2秒間隔で更新します。中断された一時記録は次回起動時に安全な名前を検証して除去し、完成していない/改変された記録は復元しません。異常終了時に複数ウィンドウへ未保存ファイルが分かれていても、承認した回復記録は次回起動したメインウィンドウのタブへすべて集約され、サブウィンドウは自動再生成しません。

リソース解放

ファイルを閉じるか別のファイルへ置き換える際は、バイト本体、Undo/Redo履歴、変更範囲、検索結果、マークアップの確保済み容量を空コンテナへ交換し、長寿命のメインウィンドウへ保持しません。単なるclear()による再利用用容量の保持は行いません。最後のタブとともにサブウィンドウを閉じる場合は、WM_DESTROYでD3D/DXGIデバイス資源を即時解放し、ウィンドウプロシージャ復帰後にメインのメッセージキューでサブコントローラーを破棄します。これにより閉じたサブの文書、履歴、検索状態、レンダラーを所有配列へ残しません。

強制終了への対応

taskkill /F や TerminateProcess では対象プロセス内の後処理コードは実行できませんが、Windowsがそのプロセスの全スレッドを終了するためプロセス外に孤立スレッドは残りません。通常終了ではすべての std::jthread に停止要求を送りjoinし、強制終了ではプロセス外の原子的な回復記録を次回起動時に使用します。クラッシュ/ハング時は RegisterApplicationRestart も登録します(強制終了自体は自動再起動の対象外です)。

セキュリティ緩和策

実行ファイルは /GS、/sdl、Spectre対策、CFG、EH continuation metadata、CET互換、DEP、ASLR、High Entropy VAを有効化します。起動時には安全なDLL検索、heap corruption termination、strict HANDLE checking、legacy extension point禁止、remote/low-integrity image load禁止を設定します。Win32コールバックからC++例外を外へ出さずfail-fastし、次回の回復へ委ねます。単一起動IPCは転送先と送信元HWNDのセッション、ユーザーSID、整合性レベル、および転送サイズ、UTF-16境界、全パスを副作用前に検証します。さらにHWND、PID、プロセス生成時刻の組を取得し、送信直前またはファイルを開く直前に同じ組と信頼条件を再検証して、検査後のウィンドウ再利用を拒否します。Mutexを安全に作成できない場合は、多重起動へフォールバックせず起動を中止します。

R2PR

この実装では Require-To-Partial-Re-Render (R2PR) を damage tracking として扱います。編集、キャレット移動、検索、スクロールなどが更新領域を Require し、同一メッセージサイクル中の要求を1つへ集約します。描画時は同じ矩形を Direct2D のクリップと IDXGISwapChain1::Present1 の dirty rect に渡します。変更がなければ描画も Present も行わず、表示時は垂直同期するため 240 Hz ディスプレイにも対応します。

データ領域はヘッダーとステータスの間へ完全に収まる行だけを表示します。最終行の下に残る端数領域は描画・クリック対象にしないため、DPIやウィンドウサイズによって次のオフセット行だけが孤立表示されることはありません。表示行数はスクロール範囲とページ移動でも共通して使用します。

Source layout

ファイル 内容
src/App.* / src/TabPolicy.h Win32メッセージ処理、エディター状態、UI非依存のタブコマンド可否判定
src/Document.* バイトデータ、部分/原子的保存、外部変更監視、並列検索、履歴
src/Renderer.* メイン画面のD3D11/D2D1描画とR2PR
src/DialogSurface.* カスタムダイアログ共通のGPU描画基盤
src/ColorPickerDialog.* / src/ColorMath.* 独自カラーピッカーとHSV/RGB変換
src/MessageDialog.* D3D非依存のGDI確認・警告・エラーダイアログ
src/SearchDialog.* / src/BitToolDialog.* / src/AboutDialog.* フルテーマ対応画面
src/BitOperations.* UI非依存の8ビット演算と入力制約
src/Profile.* / src/Localization.* 設定、配色、日本語/英語
src/RecoveryStore.* 未保存スナップショットの保護、検証、原子的保存、復元
src/Security.* プロセス緩和策、IPC送信元検証、Win32例外境界のfail-fast
src/CommandLineEscaping.h Win32コマンドライン引数の厳密な引用と組み立て
src/ThemeMenu.* Win32メニューバーとドロップダウン項目のテーマ連動
tests/CoreTests.cpp UI非依存コアの実ファイルテスト
tests/*.ps1(各種) 実HWND経由の非同期ファイルI/O、編集、スクロールバー、中クリック高速スクロール、ビット処理ツール、外部変更、ファイル/サブウィンドウ終了時のメモリ・GUI資源解放とタスクバー応答性、非同期検索、メイン/サブ、タブ列横スクロール、最終タブの終了、複数ウィンドウ強制終了復元テスト(テスト名は上記「テスト」表を参照)

About

Binary Editor for Windows

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages