クリップボード

Clipboardクラスを使用するにはMainメソッドにSTAThreadAttribute属性を付け、Single Thread Apartment (STA) モードにする必要があります。さもなくば「OLE が呼び出される前に、現在のスレッドが Single Thread Apartment (STA) モードに設定されていなければなりません。Main 関数に STAThreadAttribute が設定されていることを確認してください。」としてThreadStateExceptionが投げられます。

クリップボードへデータを追加

string text = "TEST";
Clipboard.SetDataObject(text);

Clipboard.SetDataObject()

クリップボードを消去し、そこへデータを追加できます。

MainメソッドにSTAThreadAttribute属性を付けていないと、「OLE が呼び出される前に、現在のスレッドが Single Thread Apartment (STA) モードに設定されていなければなりません。Main 関数に STAThreadAttribute が設定されていることを確認してください。」としてThreadStateExceptionが投げられます。

public static void SetDataObject (object data, bool copy);
SetDataObject(Object, Boolean) - Clipboard.SetDataObject Method (System.Windows.Forms) | Microsoft Learn

アプリケーション終了後もデータを残しておくならば、copyにtrueを指定します。データが文字列などの単純なデータ型ならば、trueとして残すようにします。一方でfalseとするとアプリケーション終了後にデータは消去され、これはこの引数を省略した構文と同じ挙動となります。

要求されたクリップボード操作に成功しませんでした。(Requested Clipboard operation did not succeed.)」としてExternalException例外によりコピーに失敗するならば、試行回数を指定します。このような失敗は、他のスレッドやアプリケーションによりクリップボードが使用されているときに発生します。

public static void SetDataObject (object data, bool copy, int retryTimes, int retryDelay);

retryTimesで試行回数を、retryDelayでその間隔をミリ秒で指定します。なおこれを省略した場合は、10回、100ミリ秒間隔で試行されます。SetDataObject - Clipboard.cs

失敗の詳細はExternalException.ErrorCodeで確認できます。

定数 内容
0x800401D0 CLIPBRD_E_CANT_OPEN OpenClipboard Failed
0x800401D1 CLIPBRD_E_CANT_EMPTY EmptyClipboard Failed
0x800401D2 CLIPBRD_E_CANT_SET SetClipboard Failed
0x800401D3 CLIPBRD_E_BAD_DATA Data on clipboard is invalid
0x800401D4 CLIPBRD_E_CANT_CLOSE CloseClipboard Failed
0x8007000E E_OUTOFMEMORY Ran out of memory
COM エラー コード (汎用) (Winerror.h) - Win32 apps | Microsoft Learn

複数のフォーマット

DataObjectのインスタンスを作成し、そこにSetData()で異なるフォーマットのデータを追加します。そしてSetDataObject()でそれをクリップボードへ追加します。

DataObject data = new DataObject();
data.SetData(DataFormats.Html, htmlData);
data.SetData(DataFormats.Text, textData);

Clipboard.SetDataObject(data);

SetData()の構文は次のようになっており、第1引数でフォーマットを指定します。そのときに有効なフォーマットの文字列はDataFormatsクラスで定義されており、DataFormats.Textならば"Text"、DataFormats.Htmlならば"HTML Format"のように取得できます。

public virtual void SetData (string format, object data);
SetData(String, Object) - DataObject.SetData Method (System.Windows.Forms) | Microsoft Learn

単純なテキスト形式ならばstring型のオブジェクトを渡すだけで、System.String、UnicodeText、Textの3つのフォーマットで格納されます。

string text = "TEST";
Clipboard.SetDataObject(text, true);

IDataObject iData = Clipboard.GetDataObject();
string[] formats = iData.GetFormats();

// formats {string[3]}
// [0] "System.String"
// [1] "UnicodeText"
// [2] "Text"

データをクリップボードから取得

IDataObject iData = Clipboard.GetDataObject(); // システムのクリップボードにあるデータを表す、IDataObjectを取得
if (iData.GetDataPresent(DataFormats.UnicodeText))
{
    string text = (string)iData.GetData(DataFormats.UnicodeText);
}

Clipboard.GetDataObject()

システムのクリップボードにあるデータを表す、IDataObjectを得られます。

MainメソッドにSTAThreadAttribute属性を付けていないと、つねにnullが返されます。

public static System.Windows.Forms.IDataObject GetDataObject ();
Clipboard.GetDataObject Method (System.Windows.Forms) | Microsoft Learn

クリップボードにデータがないとnullが返されます。

IDataObject.GetDataPresent()

このインスタンスに格納されたデータが指定の型に関連付けられ、それへ変換可能なときtrueが返されます。

public bool GetDataPresent (Type format);
GetDataPresent(Type) - IDataObject.GetDataPresent Method (System.Windows.Forms) | Microsoft Learn

formatは、DataFormatsクラスで定義されている文字列の形式でも指定できます。

IDataObject.GetData()

指定の型に関連付けられたデータを得られます。

public object GetData (Type format);
GetData(Type) - IDataObject.GetData Method (System.Windows.Forms) | Microsoft Learn

指定の書式に関連付けられたデータを得られないとき、GetData()はnullを返します。

public object GetData (string format);

formatは"Text"、またはDataFormatsクラスのフィールドを用いてDataFormats.Textのように指定します。

いずれの構文でも、指定の書式のデータを見つけられないときはその書式への変換が試みられますが、autoConvertを引数に取る構文ではこれをfalseとすることで、この変換を無効にできます。

public object GetData (string format, bool autoConvert);

たとえばクリップボードのデータにANSIテキスト形式に対応しないUnicode文字が含まれているとき、

string text = (string)iData.GetData(DataFormats.Text);

のように取得すると、それらの文字は「?」に置換されます。

IDataObject.GetFormats()

このインスタンスに格納されたデータに関連付けられている書式の一覧を得られます。

public string[] GetFormats ();
GetFormats() - IDataObject.GetFormats Method (System.Windows.Forms) | Microsoft Learn
IDataObject iData = Clipboard.GetDataObject();

string[] formats = iData.GetFormats();
// [0] "System.String" string
// [1] "UnicodeText" string
// [2] "Text" string
// [3] "Rich Text Format" string

特定の形式

特定の形式のデータを扱うならば、Clipboardクラスにそれ専用の静的メソッドが用意されています。

対象 追加 取得 判定
任意のデータ SetData() GetData() ContainsData()
テキスト SetText() GetText() ContainsText()
画像 SetImage() GetImage() ContainsImage()
音声 SetAudio() GetAudioStream() ContainsAudio()
ファイル SetFileDropList() GetFileDropList() ContainsFileDropList()
Methods - Clipboard Class (System.Windows.Forms) | Microsoft Learn

SetData()

SetData()では指定の書式でDataObjectに格納した後、Clipboard.SetDataObject()を呼び出しています。

public static void SetData(string format, object data) {
    //Note: We delegate argument checking to IDataObject.SetData, if it wants to do so.
    IDataObject dataObject = new DataObject();
    dataObject.SetData(format, data);
    Clipboard.SetDataObject(dataObject, true);
}
SetData - Clipboard.cs

SetText()

SetText()ではOSのバージョンによってTextかUnicodeTextをDataObjectに格納し、Clipboard.SetDataObject()を呼び出しています。

public static void SetText(string text) {
    // Pass in Text format for Win98...
    if (Environment.OSVersion.Platform != System.PlatformID.Win32NT ||
        Environment.OSVersion.Version.Major < 5)
    {
        SetText(text, TextDataFormat.Text);
    }
    else {
        SetText(text, TextDataFormat.UnicodeText);
    }
}

public static void SetText(string text, TextDataFormat format) {
    if (String.IsNullOrEmpty(text)) {
        throw new ArgumentNullException("text");
    }

    //valid values are 0x0 to 0x4
    if (!ClientUtils.IsEnumValid(format, (int)format, (int)TextDataFormat.Text, (int)TextDataFormat.CommaSeparatedValue))
    {
        throw new InvalidEnumArgumentException("format", (int)format, typeof(TextDataFormat));
    }

    IDataObject dataObject = new DataObject();
    dataObject.SetData(ConvertToDataFormats(format), false, text);
    Clipboard.SetDataObject(dataObject, true);
}
SetText - Clipboard.cs

HTMLクリップボード形式 (HTML Clipboard format)

HTMLをクリップボードを介して処理するには、次のような書式に従う必要があります。

Version:1.0
StartHTML:00000097
EndHTML:00000186
StartFragment:00000133
EndFragment:00000150
<HTML>
<BODY>
<!--StartFragment-->
<P>SAMPLE</P>
<!--EndFragment-->
</BODY>
</HTML>
Overview of CF_HTML - HTML Clipboard Format (Internet Explorer) | Microsoft Learn

実際の書式は、

IDataObject iData = Clipboard.GetDataObject();
object html = iData.GetData(DataFormats.Html);

のようにして用例を確認できます。

改行文字が含まれる文字列をExcelに貼り付けると、2つのセルに分割されてしまいます。これをセル内で改行されるようにするには、HTMLのstyle要素

br {mso-data-placement:same-cell;}

を指定するか、改行文字を"<br style='mso-data-placement:same-cell'>"の文字列に置換します。.net - How to insert programmatically a new line in an Excel cell in C#? - Stack Overflow

クリップボードの変更監視

SetClipboardViewer()を呼ぶことでウィンドウをクリップボード ビューアー チェーン (clipboard viewer chain) に追加することで、クリップボードの内容が変更されたときにWM_DRAWCLIPBOARDメッセージを受け取れるようになります。Adding a Window to the Clipboard Viewer Chain - Using the Clipboard - Win32 apps | Microsoft Learn

HWND SetClipboardViewer(
  [in] HWND hWndNewViewer
);
SetClipboardViewer function (winuser.h) - Win32 apps | Microsoft Learn
internal class ClipboardWatcher : NativeWindow
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

    [DllImport("user32.dll")]
    private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam);


    private IntPtr hWndNextViewer;
    public event EventHandler<TextEventArgs> ClipboardChanged;


    public ClipboardWatcher(Control control)
    {
        control.HandleCreated += delegate
        {
            AssignHandle(control.Handle);
            this.hWndNextViewer = SetClipboardViewer(this.Handle);
        };

        control.HandleDestroyed += delegate
        {
            ChangeClipboardChain(this.Handle, this.hWndNextViewer);
            ReleaseHandle();
        };
    }

    protected override void WndProc(ref Message m)
    {
        const int WM_DRAWCLIPBOARD = 0x0308;
        const int WM_CHANGECBCHAIN = 0x030D;

        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD: // クリップボードの内容が変更された
                if (Clipboard.ContainsText())
                {
                    if (ClipboardChanged != null) ClipboardChanged(this, new TextEventArgs(Clipboard.GetText()));
                }

                if (this.hWndNextViewer != IntPtr.Zero)
                {
                    // クリップボード ビューアー チェーンにある次のウィンドウへメッセージを渡す
                    SendMessage(new HandleRef(this, this.hWndNextViewer), m.Msg, m.WParam, m.LParam);
                }
                break;

            case WM_CHANGECBCHAIN: // 他のウィンドウが、クリップボード ビューアー チェーンから除去された
                if (m.WParam == this.hWndNextViewer)
                {
                    // 次のウィンドウが閉じているならば、チェーンを訂正する
                    this.hWndNextViewer = m.LParam;
                }
                else if (this.hWndNextViewer != IntPtr.Zero)
                {
                    // さもなくば、次のリンクへメッセージを渡す
                    SendMessage(new HandleRef(this, this.hWndNextViewer), m.Msg, m.WParam, m.LParam);
                }
                break;
        }
        base.WndProc(ref m);
    }
}

public class TextEventArgs : EventArgs
{
    private string text;

    public string Text
    {
        get { return this.text; }
    }

    public TextEventArgs(string text)
    {
        this.text = text;
    }
}
Microsoft Learnから検索