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;
}
}