DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

DOBON.NET

Microsoft Officeでコピーした図をPictureBoxに表示する

クリップボードのメタファイルを取得する

Microsoft Office Word、Excel、Power Pointなどで作成した図をクリップボードにコピーした時、このデータを拡張メタファイル形式として以下のような方法でデータを取得しようとしてもうまくいきません。

[VB.NET]
Dim data As IDataObject = Clipboard.GetDataObject()
'クリップボードにあるデータが拡張メタファイル形式か調べる
If data.GetDataPresent(DataFormats.EnhancedMetafile) Then
    '拡張メタファイル形式でデータを取得する
    PictureBox1.Image = _
        CType(data.GetData(DataFormats.EnhancedMetafile), Image)
End If
[C#]
IDataObject data = Clipboard.GetDataObject();
//クリップボードにあるデータが拡張メタファイル形式か調べる
if (data.GetDataPresent(DataFormats.EnhancedMetafile))
{
    //拡張メタファイル形式でデータを取得する
    PictureBox1.Image =
        (Image) data.GetData(DataFormats.EnhancedMetafile);
}

.NET Frameworkの機能だけでは残念ながらクリップボードのメタファイルデータは取得できないようです(.NET Framework 1.1現在)。しかし、Microsoft Officeからクリップボードにコピーされたデータは、EnhancedMetafile形式の他に、Office Drawing Shape Format、MetaFilePict、PNG+Office Art、JFIF+Office Art、GIF+Office Art、PNG、JFIF、GIF、ActiveClipboard、HTML Format形式に関連付けられており、このうち、HTML Format以外はMemoryStreamオブジェクトとしてデータを取得できます。つまり、Microsoft Officeでコピーした図を、PNG形式等でデータを取得することは可能です。

次のサンプルでは、フォームにピクチャボックス"PictureBox1"があり、このPictureBox1にクリップボードにコピーされた図を表示しています。

[VB.NET]
Dim data As IDataObject = Clipboard.GetDataObject()
'クリップボードにあるデータがPNG形式か調べる
If data.GetDataPresent("PNG") Then
    'PNG形式でデータをMemoryStreamとして取得する
    Dim ms As MemoryStream = CType(data.GetData("PNG"), MemoryStream)
    'Imageオブジェクトに変換する
    PictureBox1.Image = Image.FromStream(ms)
End If
[C#]
IDataObject data = Clipboard.GetDataObject();
//クリップボードにあるデータがPNG形式か調べる
if (data.GetDataPresent("PNG"))
{
    //PNG形式でデータをMemoryStreamとして取得する
    MemoryStream ms = (MemoryStream) data.GetData("PNG");
    //Imageオブジェクトに変換する
    PictureBox1.Image = Image.FromStream(ms);
}

"PNG"以外の形式を試してみたところ、上記のコードで"PNG"の部分を以下の文字列に変更した場合にのみ成功するようです。

"PNG+Office Art"
"JFIF+Office Art"
"GIF+Office Art"
"JFIF"
"GIF"

クリップボードのメタファイルを取得する

クリップボードのメタファイルデータをメタファイルとして取得するには、Win32 APIを使うしかないようです(.NET Framework 1.1現在)。この方法は、「Windows Forms FAQ - 37.5 How can I programmatically create a bitmap?」で紹介されています。

以下にWin32 APIを使用してクリップボードのメタファイルデータを取得するコードを紹介します。

[VB.NET]
'Imports System.Runtime.InteropServices

Const CF_ENHMETAFILE As Integer = 14

<DllImport("user32.dll")> _
Private Shared Function OpenClipboard( _
    ByVal hWndNewOwner As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function IsClipboardFormatAvailable( _
    ByVal wFormat As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function GetClipboardData( _
    ByVal wFormat As Integer) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function CloseClipboard() As Integer
End Function

''' <summary>
''' クリップボードのメタファイル形式のデータを取得する
''' </summary>
''' <param name="hWnd">ウィンドウのハンドル</param>
''' <returns>取得したデータ</returns>
Public Shared Function GetEnhMetafileOnClipboard( _
    ByVal hWnd As IntPtr) As System.Drawing.Imaging.Metafile
    Dim meta As System.Drawing.Imaging.Metafile = Nothing
    If OpenClipboard(hWnd) Then
        Try
            If IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0 Then
                Dim hmeta As IntPtr = GetClipboardData(CF_ENHMETAFILE)
                meta = New System.Drawing.Imaging.Metafile(hmeta, True)
            End If
        Finally
            CloseClipboard()
        End Try
    End If

    Return meta
End Function
[C#]
//using System.Runtime.InteropServices;

private const int CF_ENHMETAFILE = 14;

[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern int IsClipboardFormatAvailable(int wFormat);
[DllImport("user32.dll")]
private static extern IntPtr GetClipboardData(int wFormat);
[DllImport("user32.dll")]
private static extern int CloseClipboard();

/// <summary>
/// クリップボードのメタファイル形式のデータを取得する
/// </summary>
/// <param name="hWnd">ウィンドウのハンドル</param>
/// <returns>取得したデータ</returns>
public static System.Drawing.Imaging.Metafile
    GetEnhMetafileOnClipboard(IntPtr hWnd)
{
    System.Drawing.Imaging.Metafile meta = null;
    if (OpenClipboard(hWnd))
    {
        try
        {
            if (IsClipboardFormatAvailable(CF_ENHMETAFILE) != 0)
            {
                IntPtr hmeta = GetClipboardData(CF_ENHMETAFILE);
                meta = new System.Drawing.Imaging.Metafile(hmeta, true);
            }
        }
        finally
        {
            CloseClipboard();
        }
    }

    return meta;
}

このGetEnhMetafileOnClipboardメソッドを使うには、C#では

PictureBox1.Image = GetEnhMetafileOnClipboard(this.Handle);

VB.NETでは

PictureBox1.Image = GetEnhMetafileOnClipboard(Me.Handle)

のようにします。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。