OneNoteのアドイン(dll)を開発中、クリップボードに文字列を設定できず嵌った。
導入
[C#][.NET] クリップボードに文字列を設定する
を見ながらコーディングしたがうまくいかない。
別のプロジェクトにミニマムを作って同じコードで動かすとうまくいく。ググった感じSTAThread属性が付いていないと上手く値が反映されないらしい。
STAThread属性とは
WinFormsを作成した時に勝手にできてるあれです。
[STAThread]
static void Main(string[] args) {
}
いつも何も考えずにつけてるので完全にノーマークでした。
STAはSingle Thread Apartmentの略で、逆はMTAでMulti Thread Apartmentの略です。2つに違いはオブジェクトが単一スレッドを想定しているか、マルチスレッドを想定しているかとのこと。
なぜClipboardが反映されなかったのか?
まず、ClipboardはCOMを利用しています。COMはオブジェクトやスレッドを、アパートメントと呼ばれるCOMがその境界を越えて呼び出しをマーシャリングする領域に割り当てられます。このCOMは作成したスレッドと別のスレッドで動かすことができないらしい。なので、コードを動かすスレッドのメソッドに属性としてSTAThreadを付けないと動かない見たいです。
COMを使っている代表例に、
- ドラッグ&ドロップ機能
- クリップボード関係の機能
- FileDialogの派生クラスなど
- WebBrowserコントロール
- IMEの使用
があるんだそうな。
じゃあ、どうするか?
using System.Windows.Forms;
public void SetClipBoard(IRibbonControl control) {
//スレッドの中にシングルスレッドにしたいメソッドを入れる
Thread t = new Thread(ClipBoardSetter);
t.SetApartmentState(Apartment.STA);
t.Start();
}
public void ClipBoardSetter(){
Clipboard.SetData(DataFormat.Text, "コピー成功(´∀`)");
}
こんな感じで書いたらとりあえず動きました。
最後に
色々調べながら書いてたけど間違ってる気しかしない。タスケテ。。。
ところで、今回一番驚いたのはMACでC#のClipboardを利用できることでしたとさ。