C#からClipboardを操作する

最近 C# から Clipboard をゴニョゴニョするプログラムを作る必要があったため,いろいろと調べてみました(VS2005sp1 + .NET Framework 2.0).


.NET Framework では,Clipboard クラス (System.Windows.Forms) として Clipboard に対する操作がまとめられており,簡単にデータの設定/取得を行うことができます.
Clipboard クラスを使用したサンプルは,DOBON.NET: VB.NET, C#, 無料ソフトウェア... にて紹介されています.
例:クリップボードにファイルをコピーまたは切り取りをする、クリップボードからファイルを取得する、貼り付ける: .NET Tips: C#, VB.NET


しかし,Clipboard に対するイベントは Clipboard クラスに定義されていないため,Clipboard の監視を行いたい場合は,API を使って自分で何とかするしかありません.調べてみると,なんと Clipboardの監視はできない? − Insider.NET − @IT にコードが載っています.ありがたや(^_^;

また,英語ですが Using the Clipboard の "Creating a Clipboard Viewer Window" という項目でも,API ベースではありますが Clipboard Viewer のサンプルコードが紹介されており,参考になります(MSDN ライブラリの方では日本語資料があるのかな?).

今回は,テキストデータや画像データだけではなく,Officeアプリケーション上でコピーした内容も扱えるようにしようと考えました.


string[] IDataObject.GetFormats(); で,変換可能な形式を取得してみると...
Power Point の描画オブジェクトやクリップアートをコピーすると,

Office Drawing Shape Format
PNG+Office Art
JFIF+Office Art
GIF+Office Art
Object Descriptor
PNG
JFIF
GIF
MetaFilePict
EnhancedMetafile
PowerPoint 8.0 Internal Slides
ActiveClipboard
System.Drawing.Bitmap
Bitmap

Power Point のスライドをコピーすると,

Embedded Object
Object Descriptor
EnhancedMetafile
MetaFilePict
PNG
JFIF
GIF
Link Source
Link Source Descriptor
PowerPoint 8.0 Internal Slides
ActiveClipboard

Excelのセルをコピーすると,

1回目:Clipboardを空にしてくる
2回目:
EnhancedMetafile
MetaFilePict
System.Drawing.Bitmap
Bitmap
Biff8
Biff5
BIFF4
Biff3
Biff
SymbolicLink
Wk1
DataInterchangeFormat
XML Spreadsheet
HTML Format
System.String
UnicodeText
Text
Csv
Rich Text Format
Embed Source
Object Descriptor
Link Source
Link Source Descriptor
Link
Format129

Excelのグラフをコピーすると,

EnhancedMetafile
Embed Source
Object Descriptor
Link Source
Link Source Descriptor
Link
(上記の内容で,2回更新される)

(なんだか,見慣れない名前のフォーマットがいくつかあります)


例えば,Clipboardの内容をOffice描画オブジェクトとして扱いたいのであれば,

IDataObject data = Clipboard.GetDataObject();
if (data.GetDataPresent("Office Drawing Shape Format"))
{
    MemoryStream memory = (MemoryStream)data.GetData("Office Drawing Shape Format");    // 取得

    Clipboard.SetText("ダミーテキスト");

    Clipboard.SetData("Office Drawing Shape Format", memory);    // 設定
}

のように,MemoryStreamとして取り扱うことができます(キャストする型はGetTypeで調べてください).


さて,これらをどう扱っていこうかなぁ...( ´Д`)

ただ,問題がありまして,DataFormats.EnhancedMetafile を指定して GetData すると null が返ってきます(Microsoft Power Point のオブジェクトをコピーしたとき等).場合によっては,例外が発生します.

IDataObject data = Clipboard.GetDataObject();
if (data.GetDataPresent(DataFormats.EnhancedMetafile))
{
    object obj = data.GetData(DataFormats.EnhancedMetafile);    // nullが返ってくる
}


MSDN 曰く,

クリップボードでメタファイル形式を使用する場合には、特別な配慮が必要な場合があります。DataObject クラスの現在の実装における制限により、.NET Framework で使用されるメタファイル形式は、旧メタファイル形式を使用するアプリケーションでは認識されない場合があります。この場合は、Win32 クリップボード アプリケーション プログラミング インターフェイス (API: Application Programming Interface) で相互運用する必要があります。詳細については、http://support.microsoft.com にある Microsoft サポート技術情報の文書 323530 (「Metafiles on Clipboard Are Not Visible to All Applications」) を参照してください。

だそうな.

この件については,先ほどの DOBON.NET: VB.NET, C#, 無料ソフトウェア... でも紹介されています.


ならば,ということで,上記2つのページを参考に,Clipboard への Metafile の設定/取得メソッドを実装してみたところ,"取得"のほうは(おそらく)うまく行きましたが(試しに Metafile.GetThumbnailImage を用いてサムネイルを表示させると,正しく表示される,ぐらいしか確認はしていませんが),"設定"のほうはうまく行きませんでした.


まず,PRB: Metafiles on Clipboard Are Not Visible to All Applications に載っているサンプル通りに実装すると,

    hEMF2 = CopyEnhMetaFile(hEMF, new IntPtr(0));

が,"hEMF2 == IntPtr.Zero" を返してきます.
また,"hEMF" をダイレクトに(hEMF2 = hEMF; みたいにして)SetClipboardData 関数へ渡すと,今度は SetClipboardData 関数が NULL を返してきます.


さらに,Clipboard クラスの SetData メソッドを使用しても,Officeアプリケーション側からは"認識できないファイル",または"Windows メタファイル"とか名前が出ても実際は貼り付けられない等の状態になってしまいます.

Metafile.Saveメソッドを用いて,MemoryStreamに格納しようとしても,encoder が null だよと例外が飛んでくる.

ぬぬぬ...(`皿´)