Clipboard.SetDataObject(data, true)

特定の環境で System.Windows.Forms.Clipboard.SetDataObject() を copy == true で呼び出すと、コピー操作の成功失敗に関わらず CLIPBRD_E_CANT_OPEN を保持する ExternalException が発生する、という症状がある。結果的にクリップボードが期待通りになっているにもかかわらず、例外が発生してしまう原因について。
Win32 のクリップボードには、所有権とか遅延レンダリングとかいった要素が存在するが、System.Windows.Forms.Clipboard クラスは基本的にそのような機能を表面的に提供していないため、独自のクリップボード操作クラスを作成しなければ、この問題は解決できない。
まず、前提条件となる特定の環境だが、

という場合に発生する。つまり、遅延レンダリングクリップボードに配置されたデータを、即時レンダリングされた場合に問題が発生する。Clipboard クラスは、

  • ウィンドウハンドルに関連するものを要求しない
  • 遅延レンダリングが自動化されている
  • SetDataObject() と GetDataObject() が IDataObject を利用している
  • 例外発生時の ExternalException.ErrorCode (HRESULT) の値

という観点から、Clipboard クラスは OLE/COM の OleSetClipboard API を利用しているであろうことが推測されるし、実際にこの API 利用しているようなのだが、この API は常に遅延レンダリングクリップボードデータが設定されるという特徴がある。
Clipboard.SetDataObject(object, true) が呼び出された場合、Clipboard クラスは OleSetClipboard API を利用してデータオブジェクトを遅延レンダリングクリップボードへ登録したあと、OleFlashClipboard API を呼び出してレンダリングを実施しなければならないが、前述のような環境では OleSetClipboard API を完了した時点で別のアプリケーションがクリップボードデータのレンダリングを要求しており、Clipboard クラスからの OleFlashClipboard APIクリップボードの所有権を取得できずに CLIPBRD_E_CANT_OPEN を返すことになる、というわけだ。
この問題をまっとうに解消するためには、Clipboard.SetDataObject() の呼び出し全体で、クリップボードの所有権を保持しつづける必要があるのだが、OleSetClipboard が内部で利用している所有権やレンダリングエンジンを備えるウィンドウハンドルを安全に取得する方法がないため、所有権を維持することは難しいだろう。というわけで、

という方法を選択しなければならない。さて、遅延レンダリングを行う場合だが、オブジェクトを複製せずに配置する場合はすこし注意が必要になる。遅延レンダリングで Clipboard.SetDataObject() に渡した参照型は、DataObject クラスや Clipboard クラスによって自動的にシリアライズされるが、このシリアライズレンダリング要求のたびに発生するため、複製でない参照型の状態を更新すると、通常の「コピー/貼り付け」の操作にそぐわない動作をしてしまう。