Clipboardクラスを使用するにはMainメソッドにSTAThreadAttribute属性を付け、Single Thread Apartment (STA) モードにする必要があります。さもなくば「OLE が呼び出される前に、現在のスレッドが Single Thread Apartment (STA) モードに設定されていなければなりません。Main 関数に STAThreadAttribute が設定されていることを確認してください。」としてThreadStateExceptionが投げられます。
string text = "TEST"; Clipboard.SetDataObject(text);
クリップボードを消去し、そこへデータを追加できます。
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 |
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);
}
システムのクリップボードにあるデータを表す、IDataObjectを得られます。
MainメソッドにSTAThreadAttribute属性を付けていないと、つねにnullが返されます。
public static System.Windows.Forms.IDataObject GetDataObject ();Clipboard.GetDataObject Method (System.Windows.Forms) | Microsoft Learn
クリップボードにデータがないとnullが返されます。
このインスタンスに格納されたデータが指定の型に関連付けられ、それへ変換可能なときtrueが返されます。
public bool GetDataPresent (Type format);GetDataPresent(Type) - IDataObject.GetDataPresent Method (System.Windows.Forms) | Microsoft Learn
formatは、DataFormatsクラスで定義されている文字列の形式でも指定できます。
指定の型に関連付けられたデータを得られます。
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);
のように取得すると、それらの文字は「?」に置換されます。
このインスタンスに格納されたデータに関連付けられている書式の一覧を得られます。
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() |
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()では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をクリップボードを介して処理するには、次のような書式に従う必要があります。
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(Handle); }; control.HandleDestroyed += delegate { ChangeClipboardChain(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; } }