Windowsのクリップボード
同じデスクトップ(ウィンドウステーション)内のアプリで共有される領域。同じ内容を複数の形式で同時に置ける。
OLEドラッグ&ドロップ
IDataObject・IDropSource・IDropTargetとDoDragDropのループで構成される、COMベースのデータ受け渡し。
クリップボード形式
クリップボードに置かれたデータの表現形式。CF_TEXT・CF_UNICODETEXT・CF_HDROP・CF_DIBなどの標準形式と、RegisterClipboardFormatで登録する形式がある。
CF_TEXT
ANSIコードページ依存のテキスト用クリップボード形式。環境によって解釈が変わり文字化けの原因になるため、テキストにはCF_UNICODETEXTを使う。
CF_HDROP
DROPFILES構造体と二重NUL終端のフルパス文字列配列で構成される、ファイルの受け渡し用のクリップボード形式。
HTML Format(登録形式)
Version・StartHTML・EndHTML・StartFragment・EndFragmentというバイトオフセットのヘッダーを持つUTF-8テキストとして書式付きテキストを運ぶ登録形式。
.NETのClipboardクラス
WinForms/WPFが提供するクリップボード操作のラッパー。呼び出し元スレッドがSTAであることを要求する。
OleInitialize(STA)
OLEを使うためのCOM初期化。RegisterDragDropはこれで初期化していないとE_OUTOFMEMORYで失敗する。
遅延レンダリング
データ本体の代わりに「要求されたら作る」という約束だけを登録し、要求時にWM_RENDERFORMATで生成する仕組み。
OleFlushClipboard
クリップボードが保持しているデータオブジェクトへのポインターから、データをクリップボード上に実体化して終了後も貼り付け可能にする関数。
UIスレッドのコンテキスト
WinForms/WPFなどデスクトップアプリで、UIの応答性を保つために画面更新や重い処理の実行場所として意識されるコンテキスト。
UIイベントハンドラ
IDataObject
クリップボードにもドラッグ&ドロップにも共通して使われる、複数形式のデータを運ぶCOMインターフェース。
信頼できない入力としての検証
外部から受け取るデータの形式・サイズ・内容を確認してから使うこと。クリップボードやドラッグ&ドロップで受け取るデータも対象になる。
AddClipboardFormatListener / WM_CLIPBOARDUPDATE
クリップボードの変更をウィンドウメッセージで受け取る、現在推奨される監視方式。
クリップボード履歴・デバイス間同期のポリシー
Experience/AllowClipboardHistoryとPrivacy/AllowCrossDeviceClipboardで、履歴(Win+V)とクラウド同期を制御する設定。
グループポリシー
Active Directoryドメイン環境でWindowsの設定を集中構成する仕組み。
Microsoft Intune
クラウドからPC・モバイルデバイスを管理するMicrosoftのMDMサービス。
クリップボード履歴・デバイス間同期
コピーした内容を複数件保持してWin+Vで呼び出せるWindowsの履歴機能と、Microsoftアカウントを介したデバイス間のクリップボード同期。
RDPのクリップボードリダイレクト
別セッションとの間でクリップボードを橋渡しする機能と、それを制御するポリシー。
UIPI(User Interface Privilege Isolation)
低い整合性レベルのプロセスから高い整合性レベルのウィンドウへのメッセージを既定で遮断する仕組み。
管理者権限
コンピューター全体の設定を変更できる権限。
昇格
DragDropEffects(Copy / Move / Link)
ドラッグ元が許可する効果の集合を宣言し、ドロップ先が実際の効果を選ぶ契約。Moveが成立するとドラッグ元がデータを削除する。
ハイドレーション
プレースホルダーの中身を同期プロバイダーから取り寄せること。逆にローカルの実体を捨ててプレースホルダーへ戻すのがデハイドレーション。
デハイドレーション
ウィンドウは同じ情報を複数のクリップボード形式で置ける。貼り付け先が理解できる形式を選ぶため、同じコピーでも貼り付け先によって結果が変わる。RegisterClipboardFormatで独自の形式も登録できる。
CF_TEXTはANSIでコードページ依存なので日本語環境では文字化けの温床になる。CF_UNICODETEXTとの間はシステムが暗黙変換するが、正はUnicode側である。
CF_HDROPはDROPFILES構造体と二重NUL終端のフルパス文字列配列で構成され、ファイルの受け渡しに使われる。
登録名は「HTML Format」で、Version・StartHTML・EndHTML・StartFragment・EndFragmentというバイトオフセットのヘッダーを持つUTF-8テキストという独特の構造をしている。
.NETのClipboardクラスはSTAスレッドからしか使えず、MTAのバックグラウンドスレッドから触ると失敗する。WinForms/WPFのUIスレッドはSTAなので通常は問題なく、専用のSTAスレッドに逃がせばUIスレッド以外からも扱える。ネイティブのOpenClipboardにはこの制約はない。
SetClipboardDataのデータハンドルにNULLを渡すと「要求されたら作る」という約束だけが登録され、誰かがその形式を要求した時点でWM_RENDERFORMATが届く。コピー元は終了前にWM_RENDERALLFORMATSを受けて未レンダリングの全形式を実体化する責務があり、怠るとその形式は失われる。
OleSetClipboardではクリップボードが保持するのはデータオブジェクトへのポインターだけで、アプリ終了時にOleFlushClipboardを呼ぶとデータが実体化され、終了後も貼り付け可能になる。
レンダリング要求はメッセージ処理中に同期的に走るため、生成に時間がかかるデータではUIが固まるというトレードオフがある。
クリップボードから取得したデータは信頼できないため、注意深くパースすべきだと明記されている。貼り付けるデータは外部由来の入力として扱う。
監視方式にはビューアーウィンドウ・シーケンス番号・フォーマットリスナーの3つがあり、現在の正解はAddClipboardFormatListenerとWM_CLIPBOARDUPDATEである。
Experience/AllowClipboardHistoryで履歴(Win+V)を、Privacy/AllowCrossDeviceClipboardでデバイス間のクラウド同期を制御できる。
同じPolicy CSPの設定はIntuneからも配布できる。
ポリシーで履歴やデバイス間同期を無効化できるほか、機密を扱うコピー機能ではExcludeClipboardContentFromMonitorProcessing等の登録形式で自分のコピーを履歴・同期の対象外にできる。通常のクリップボード経由の貼り付け自体は引き続き機能する。
RDPなど別セッションとの間は本来別のクリップボードで、リダイレクト機能が橋渡ししている。TS_CLIENT_CLIPBOARDでリダイレクトを制御できる。
IDataObjectが運ばれるデータ本体で、IDropSourceがドラッグ継続/中止の判断とカーソルフィードバック、IDropTargetがDragEnter/DragOver/DragLeave/Dropでの受け入れ可否の表明と受け取りを担う。クリップボードのコピー&ペーストとまったく同じ機能を提供するため、コピペを実装済みのアプリなら追加はわずかとされている。
ドロップ先ウィンドウはRegisterDragDropで登録するが、CoInitialize/CoInitializeExで初期化しているとE_OUTOFMEMORYで必ず失敗し、OleInitializeでなければならない。呼び出しスレッドがメッセージポンプを回していることも必須で、怠るとドラッグ中の他アプリがハングする。
UIPIは低い整合性レベルの送信元からのメッセージを遮断するため、通常権限のエクスプローラーから昇格したアプリへはドロップの通知が届かない。ChangeWindowMessageFilterExで通せるのは旧式のWM_DROPFILESであって、OLE D&D全体が解決するわけではない。
アプリを常時昇格で動かすと、通常権限のエクスプローラーからのドロップがUIPIに遮られる。昇格が必要な処理だけを別プロセスに分離すれば、UI本体は通常権限のままドロップを受けられる。
Copy/Move/Linkはドラッグ元とドロップ先の間の契約で、ドラッグ元がDoDragDropで許可する効果の集合を宣言し、ドロップ先が実際の効果を選ぶ。Moveが成立するとドラッグ元がデータを削除するため、受け側が深く考えずMoveを返すと元のファイルが消える事故になる。
CF_HDROPで渡るのはパスだけなので、パスが存在してもファイル本体がローカルにないオンデマンドファイルの場合、開いた瞬間にダウンロードが走り、オフラインなら失敗する。