クリップボードのデータってどうなっているの?
非Windows系プログラマ向けの簡単な記事です。
クリップボードにそれらしい名前を付けるとすると、 型付きのデータ転送用共通メモリ機構 です。
本記事では、現行の Windows 環境で実際に利用されている型付きのデータ転送用共通メモリ機構でのデータ管理方法を整理します。
Windows でのクリップボードの基本構造
クリップボードでは、アプリケーション間でデータを共有するためのメモリ領域をOSレベルで確保しています。Ctrl+Cを入力すると、そのメモリ領域内にユーザーの選択したデータが記録されます。
そのデータのプロパティは以下の通りです。
- フォーマットID(UINT) — データの種類(テキスト、画像など)を識別
- データハンドル(HGLOBAL) — 実際のデータを保持する共有メモリへのポインタ
注意点としては、1度のCtrl+Cで1組のフォーマットID/データハンドルではなく複数組のフォーマットID/データハンドルが同時に登録される可能性があります。
ユーザーから見ると複数のフォーマットが同時に登録されているようには見えませんが、内部では色々な場面での貼り付けに対応できるよう、複数のフォーマットが同時に登録されます。例えば、色付きの文字列・色なしの文字列の両方のデータが同時に登録されていたりします。この辺りは開発力のあるアプリケーションであるほど色々と対応されているはずです。
続いては、フォーマットIDの具体例を見ていきます。データハンドルについては、その実体はメモリに置かれたビット列にすぎず、クリップボード理解の本質から外れる気がするので、ここでお別れです。
フォーマットID(UINT)
テキスト系フォーマット
| フォーマットID | 実体 | 説明 | 
|---|---|---|
| CF_UNICODETEXT | wchar_t[](UTF-16, NULL終端) | 現行標準。多言語対応のテキスト。Word、メモ帳、ブラウザなどで使用され、UTF-16エンコーディングで保存される。 | 
| CF_TEXT | char[](ANSIコード, NULL終端) | 旧来の8ビット文字列。ローカルのコードページ(日本語OSではShift-JISなど)を使う。古いアプリやWin32 API互換用。 | 
| CF_LOCALE | DWORD(LCID値) | テキストがどの言語ロケールで作られたかを示す補助情報。 使われる場面: アプリが CF_TEXTを登録する際、同時にCF_LOCALEを登録して「このテキストはどの言語設定(例:日本語、英語)でエンコードされたか」を明示する。これにより、他のアプリが文字コード変換を行う際の判断材料になる。(例:英語OSに日本語テキストを貼り付けた際、CF_LOCALE=0x0411 があるとShift-JISとして正しく処理できる) | 
関連する話題として、Microsoft PowerToysの例をご紹介します。Microsoft PowerToysでは「プレーンテキストとして直接貼り付ける」という機能が提供されています。

この機能では、貼り付け時にCF_UNICODETEXT以外のテキスト関係のフォーマットを削除しています。
実装からも確認できます。
EmptyClipboard()でクリップボードを空にしてから、SetClipboardData(CF_UNICODETEXT, h_clipboard_data)でCF_UNICODETEXTを登録しています。順番前後していますが、wcscpy_s(pch_data, clipboard_text.length() + 1, clipboard_text.c_str())ではデータハンドルを登録しています。
            wcscpy_s(pch_data, clipboard_text.length() + 1, clipboard_text.c_str());
            EmptyClipboard();
            if (NULL == SetClipboardData(CF_UNICODETEXT, h_clipboard_data))
            {
                DWORD errorCode = GetLastError();
                auto errorMessage = get_last_error_message(errorCode);
                Logger::error(L"Couldn't set the clipboard data to the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L"");
                GlobalUnlock(h_clipboard_data);
                GlobalFree(h_clipboard_data);
                CloseClipboard();
                Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.SetClipboardData");
                return;
            }
🖼️ 画像系フォーマット
| フォーマットID | 実体 | 説明 | 
|---|---|---|
| CF_BITMAP | HBITMAP(GDIオブジェクト) | Windows内部のビットマップ。描画API(GDI)で直接扱える形式。スクリーンキャプチャやペイントなどで使用。 | 
| CF_DIB | BITMAPINFOHEADER+ ピクセルデータ | BMPファイルに近い構造で、ピクセル情報を直接持つ。デバイスに依存しない画像形式。画像編集ソフトや印刷アプリで使用。 | 
| CF_DIBV5 | BITMAPV5HEADER+ ピクセルデータ | DIBの拡張版。αチャンネル(透明度)や色空間情報を保持でき、PNGに近い表現力を持つ。Office製品・ブラウザなどで広く使用。 | 
📂 ファイル・オブジェクト系フォーマット
| フォーマット | 実体 | 説明 | 
|---|---|---|
| CF_HDROP | DROPFILES構造体 + ファイルパス群 | エクスプローラでファイルをコピーやドラッグ&ドロップしたときに使用。 ファイルパスがNULL区切りで並ぶ(例: C:\test1.txt\0C:\test2.png\0\0)。実際のファイル内容は含まず、パスのリストのみを渡す。 | 
| CF_OWNERDISPLAY | 遅延レンダリング(Lazy Rendering)用トリガ | SetClipboardData(format, NULL)を使って登録し、実データを即時には渡さず、「貼り付け要求が来た時点」で生成する。ExcelやPhotoshopなどで大量データをコピーする際、処理を遅延させてレスポンスを改善するために使われる。 OSは貼り付け要求時に WM_RENDERFORMATメッセージを送信し、アプリがその場で実データを返す。 | 
🔊 メディア系フォーマット
| フォーマットID | 実体 | 説明 | 
|---|---|---|
| CF_WAVE | WAVファイル形式(RIFF構造) | 音声データ。録音ソフトやメディアエディタ間でのコピー・貼り付けに使用される。 | 
📄 図形・レイアウト系(ベクター)
| フォーマットID | 実体 | 説明 | 
|---|---|---|
| CF_ENHMETAFILE | HENHMETAFILE(拡張メタファイル) | ベクター形式の図形データ(線・円・文字などの描画命令列)。解像度に依存せず、WordやPowerPoint間の図形貼り付けで使用される。 | 
PowerPointでポスター印刷をするときとかに意識するやつです。

カスタムフォーマット(RegisterClipboardFormat)
アプリケーション独自のフォーマットは RegisterClipboardFormat() により登録されます。
登録名を文字列で指定し、同じ名前を使うアプリ間では同一フォーマットIDが共有されます。
- 
ID割り当て範囲:
 0xC000(=49152)〜0xFFFF はアプリケーション定義領域であり、OS標準フォーマットと衝突しない。
 カスタムフォーマット例: Microsoft Word の "HTML Format"
WordやOutlookがコピー時に登録する代表的カスタムフォーマットが"HTML Format"です。
書式付きテキストをHTMLとして転送するために使われます。
- 
フォーマット名: "HTML Format"
- 
データ内容: HTML断片+オフセット情報ヘッダ
 (Version:1.0\nStartHTML:...EndFragment:...)
- 利用アプリ: Word, Outlook, Edge, Chrome
- 
目的:
 プレーンテキストだけでなく、リッチな装飾(色、フォント、リンク)を含むテキストを
 他アプリへ貼り付け可能にする。
実際のクリップボード内のフォーマットを見てみる。
最後に実際のクリップボード内のフォーマットを見てみます。
VSCodeでコードをコピーしてクリップボードに記録されたフォーマットを見てみると、いろいろな情報が入っています。ID: 13はこちらの通り、CF_UNICODETEXTです。それ以外にも色々なフォーマットが記録されおり、例えばHTML Formatでは色情報なども記録されます。このお陰でVSCodeでコピーテキストをPower Pointに貼り付けるといった場面でも、元の文字色が反映されるのです。
ID: 49416 / 名前: HTML Format
ID: 13 / 名前: (標準フォーマット)
ID: 50013 / 名前: Chromium Web Custom MIME Data Format
ID: 50150 / 名前: Chromium internal source RFH token
ID: 50151 / 名前: Chromium internal source URL
ID: 16 / 名前: (標準フォーマット)
ID: 1 / 名前: (標準フォーマット)
ID: 7 / 名前: (標準フォーマット)

ちなみに、Microsoft PowerToysの「プレーンテキストとして直接貼り付ける」を使うと、以下のフォーマットに書き換わります。HTML Formatが削除され、プレーンテキストの13と管理用フォーマットのみが残っています。
ID: 13 / 名前: (標準フォーマット)
ID: 50001 / 名前: ExcludeClipboardContentFromMonitorProcessing
ID: 16 / 名前: (標準フォーマット)
ID: 1 / 名前: (標準フォーマット)
ID: 7 / 名前: (標準フォーマット)
クリップボード内のフォーマットは以下のスクリプトで確認しています。
import win32clipboard
win32clipboard.OpenClipboard()
format = 0
print("=== クリップボードフォーマット一覧 ===")
while True:
    format = win32clipboard.EnumClipboardFormats(format)
    if not format:
        break
    try:
        name = win32clipboard.GetClipboardFormatName(format)
    except:
        name = "(標準フォーマット)"
    print(f"ID: {format} / 名前: {name}")
win32clipboard.CloseClipboard()
おわり
以上です!
今回は省略しましたが、実際にクリップボードを操作するときにはデータハンドルをゴニョゴニョする感じです。
OSレイヤの機能って身近な割にはあまり実装まで意識しないので、深掘りすると面白いですよね。
またWin32のコードベースを見るたびにWindows系プログラマって凄いなーと。。




Discussion