Garden に戻る

Windows画面に直接落書きできるアプリ作った話 - Desktop Ink

オンライン会議で画面共有中にマークできる、.NET 10で作った軽量な画面オーバーレイアプリの実装記録

公開日 読了目安 7 分

皆さんこんにちは。最近、オンライン会議で画面共有しながら説明するときに「あれ、ここのどこ指してるか分かります?」って言われることが増えました。マウスカーソルだけだと分かりにくいんですよね。

そこで、Windows画面に直接描画できる Desktop Ink というアプリを作りました。既存のツールもあるんですが、.NET Frameworkの古いバージョンでインストールが面倒だったり、機能が多すぎて使いづらかったり。今回は必要最小限の機能に絞って、.NET 10で作り直してみました。

リポジトリとダウンロード

Desktop Ink の使用例

どんな機能があるか

このアプリの基本コンセプトは「常時最前面の透過オーバーレイに自由に描画できる」です。マルチモニター環境にも対応していて、各モニターごとに透明な全画面ウィンドウを作成します。

2つの描画モード

描画モードは2つあります。

Permanent Draw Mode(恒久描画モード)

これは Win+Shift+D でトグルできる通常の描画モードです。オンにすると画面上の任意の場所に線を引けるようになって、オフにするまで消えません。プレゼン中に強調したい箇所をマークするときとか、メモ書きを残しておきたいときに便利。

Temporary Draw Mode(一時描画モード)

こっちが個人的にお気に入りの機能。Altキーをダブルクリックして押し続けると、その間だけ描画できます。キーを離すと自動的に全部消える。会議中に「今ここ説明してます」って一瞬だけマークしたいときに最高。トグルする必要もないし、消し忘れもない。

最初はマウスジェスチャーとかも考えたんですが、既存アプリとの相性とか、誤検知の問題があって結局Altダブルクリックに落ち着きました。シンプルな実装で、意外と自然に使える。

Control Palette(コントロールパレット)

小さな浮遊ウィンドウで、描画モードのトグル、全消去、終了ボタンが並んでます。マウスで操作したい人向け。このパレット自体もドラッグで移動できるようにしてあります。半透明にして邪魔にならないようにはしてるけど、正直キーボードショートカットの方が使いやすいかも。

ホットキーは以下の通り。

ホットキー 動作
Win+Shift+D 描画/透過モードのトグル
Win+Shift+C 全モニターのインクストロークをクリア
Win+Shift+Q アプリ終了

アーキテクチャの話

WPFで作っています。構成はシンプルで、以下の要素から成り立っている。

  • Overlay Windows: モニターごとに1つずつ作る全画面透過ウィンドウ
  • Control Window: ドラッグ可能なコントロールパレット
  • Keyboard Hook Manager: グローバルホットキーと一時モード検出用の低レベルキーボードフック
  • Overlay Manager: マルチモニター環境でのオーバーレイのライフサイクル管理

透過とヒットテストの仕組み

一番面白かったのがウィンドウの透過処理です。WPFでは WindowStyle="None" と AllowsTransparency="True" を使えば透過ウィンドウは作れるんですが、問題は「描画モードじゃないときにマウスイベントを下のウィンドウに完全に透過させる」こと。

最初 Background="Transparent" だけ設定したら、透過はするけどクリックイベントは拾っちゃう。これだと下のアプリが操作できない。で、IsHitTestVisible="False" を試したら今度は描画モード時にもマウスイベントを受け取れなくなりました。

結局 Win32 API を使って WS_EX_TRANSPARENT スタイルを動的に切り替えることにしました。

// Pass-through mode時
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);

// Draw mode時
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);

このスタイル変更をモード切り替えのたびに実行しています。ちょっと泥臭いけど、これが一番シンプルで確実。

マルチモニター対応

System.Windows.Forms.Screen.AllScreens を使って全モニター情報を取得しています。WPFには標準でマルチモニター管理のAPIがないので、ここだけ WinForms に依存。各モニターごとに位置とサイズを計算して、全画面オーバーレイウィンドウを配置します。

foreach (var screen in Screen.AllScreens)
{
    var overlay = new OverlayWindow
    {
        Left = screen.Bounds.Left,
        Top = screen.Bounds.Top,
        Width = screen.Bounds.Width,
        Height = screen.Bounds.Height
    };
    overlay.Show();
}

モニター構成が変わったときの再配置は、実はまだ実装していません。アプリ起動時のモニター配置を保持したまま動きます。ディスプレイ設定変えたら再起動が必要。まあ、使ってる最中にモニター構成変える人はそんなにいないと思うので、優先度低め。

低レベルキーボードフックの実装

グローバルホットキーとAltダブルクリック検出のために、Win32の SetWindowsHookEx を使っています。WPFのイベントだとアプリがフォーカスされていないときに動かないので、どうしても低レベルフックが必要でした。

Altダブルクリックの検出は意外と面倒。システムのダブルクリック時間を GetDoubleClickTime() で取得して、Alt押下のタイミングを記録します。

private DateTime _lastAltPressTime;

if (keyPressed && vkCode == VK_ALT)
{
    var now = DateTime.Now;
    var interval = (now - _lastAltPressTime).TotalMilliseconds;
    
    if (interval < GetDoubleClickTime())
    {
        // Altダブルクリック検出
        _isTemporaryDrawMode = true;
    }
    _lastAltPressTime = now;
}

Altキーを離したときの検出も同じフックで行います。_isTemporaryDrawMode フラグを落として、全オーバーレイのインクストロークをクリア。

この辺のコード、実は最初バグっていました。Altキーを「押しっぱなし」にしてると連続でキーイベントが飛んでくるので、ダブルクリック判定が誤発火する。キーリピートを無視する処理を追加して、初回押下だけカウントするようにして解決。

既存ツールとの比較

似たようなツールだと Epic Pen とか ZoomIt が有名です。これらは機能豊富で、色変更やブラシサイズの調整、スクリーンショット機能とかもあります。ただ、Epic Pen は .NET Framework 4.5.2 を要求するので、最近の Windows 環境だと追加インストールが必要。ZoomIt はポータブルだけど、機能が多すぎて個人的には使いづらかった。

Desktop Ink は「画面共有時に一時的にマークする」用途に特化しています。色は赤一色、ブラシサイズも固定。その代わり、.NET 10なので追加ランタイムのインストール不要(Windows 11ならプリインストールされている)。ビルドも速い。

機能を削ぎ落とした分、実装がシンプルになって保守しやすいのが副次的な利点。コードベース全体で1000行ちょっとしかないです。

使ってみて気づいたこと

実際に何度か使ってみて、Temporary Draw Mode が想像以上に便利だと分かりました。プレゼン中に「Alt押しながら囲む → 離す → 消える」の流れが自然。トグル不要なので、画面に線が残り続ける心配もない。

Control Palette は正直そこまで使っていません。キーボードショートカットに慣れると、マウス操作に持ち替えるのが面倒になります。まあ、ホットキー覚えるのが苦手な人もいるので、あって損はないかと。

マルチモニター対応も実装したけど、実際に3モニター環境で試したら思ったより動く。モニター間でストロークが途切れないか心配だったんですが、各オーバーレイが独立してるので問題なしでした。

まだやってないこと

色変更とブラシサイズの調整は実装していません。「赤一色、固定サイズ」で割り切っています。今後、要望があれば追加するかもですが、優先度は低め。

あと、ストロークの保存機能もない。アプリを閉じたら全部消えます。永続化は考えてないので、メモ用途には向いてません。あくまで一時的なマーキングツールとして使ってください。

消しゴム機能も未実装。全消去(Win+Shift+C)はあるけど、部分的に消すことはできません。InkCanvas の標準機能使えば実装自体は簡単なんですが、用途的に不要かなと思っています。

おわりに

Desktop Ink は「必要最小限の機能で軽快に動く」を目指して作りました。既存ツールが重くて使いにくいと感じてる人には試してもらいたいです。

リポジトリは MIT ライセンスで公開してるので、機能追加したい人はプルリクエスト歓迎です。自分の用途に合わせてフォークして改造するのもあり。

ちなみに、Altダブルクリックのアイデアは別のツールから拝借しました(具体的にどれかは忘れた)。最初は自分で思いついたと思ってたけど、記憶を辿ったら既視感あった。まあ、良いアイデアは広まるべきなので問題ないかと。

とりあえず、オンライン会議で使ってみてください。画面共有がちょっとだけ快適になるはず。