WinFormsのForm
へのクリップボード監視機能の追加、コンポーネントとNativeWindow
で思ったより簡単に実装できました。NativeWindow
の理解が浅いので問題が残っているかもしれませんが、とりあえず動きはします。動作確認はC# 12 (.NET 8.0)ですが、名前空間などの書き方を変えれば以前の.NETでも使えると思います。
プロジェクトにClipboardWatcher.cs
等の名前で次のコードを追加して一度ビルドします。後はツールボックスからForm
に追加できます。
#pragma warning disable SYSLIB1054 using System.ComponentModel; using System.Runtime.InteropServices; using System.Runtime.Versioning; namespace Utility.Windows.Forms; /// <summary> /// クリップボードの変更を監視します。 /// </summary> [SupportedOSPlatform("windows")] [DefaultEvent(nameof(ClipboardUpdated))] sealed class ClipboardWatcher : Component { /// <summary> /// クリップボードが変更されました。 /// </summary> public event EventHandler? ClipboardUpdated; private sealed class ClipboardWatcherNativeWindow : NativeWindow { private readonly ClipboardWatcher _owner; public ClipboardWatcherNativeWindow(ClipboardWatcher owner) { _owner = owner; CreateHandle(new CreateParams()); } protected override void WndProc(ref Message m) { const int WM_CLIPBOARDUPDATE = 0x031D; switch (m.Msg) { case WM_CLIPBOARDUPDATE: _owner.OnClipboardUpdated(_owner, new()); break; } base.WndProc(ref m); } protected override void OnHandleChange() { base.OnHandleChange(); NativeMethods.AddClipboardFormatListener(Handle); } public override void ReleaseHandle() { NativeMethods.RemoveClipboardFormatListener(Handle); base.ReleaseHandle(); } private static class NativeMethods { [DllImport("user32.dll")] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool AddClipboardFormatListener(nint hwnd); [DllImport("user32.dll")] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool RemoveClipboardFormatListener(nint hwnd); } } private ClipboardWatcherNativeWindow? _native; public ClipboardWatcher() { if (!DesignMode) _native = new(this); } protected override void Dispose(bool disposing) { if (disposing) { _native?.ReleaseHandle(); _native = null; } base.Dispose(disposing); } /// <summary> /// <code>ClipboardUpdated</code>イベントを呼び出すかを取得または設定します。 /// </summary> [DefaultValue(true)] [Category("Behavior")] public bool Enabled { get; set; } = true; private void OnClipboardUpdated(object? sender, EventArgs e) { if (Enabled) ClipboardUpdated?.Invoke(sender, e); } }
作成中に学んだこと。
- 低レベルのWindowsネイティブウィンドウ操作は
NativeWindow
クラスの派生クラスを作成する。 NativeWindow
は自動でウィンドウを作らないので、コンストラクタなどでCreateHandle
を呼び出す。CreateHandle
の引数でクラス名を指定するとサブクラス化ウィンドウが作成できる。System.Windows.Forms.Button
等はこの機能でサブクラス化している。
参考
- WinForms - GihHub (Microsoft)(変な操作を防ぐためにアクセス時GitHubログアウト推奨)