none
EMF形式を含む多形式(PNG,JPG)の画像をクリップボードに転送する方法 RRS feed

  • 質問

  •  

     

    お世話になります。WinXP+VB2008で開発しております。

    EMF形式を含む多形式(PNG,JPG)の画像をクリップボードに転送する方法を探しております。

    EMF形式のみでのクリップボード転送は http://support.microsoft.com/kb/323530/ja の方法を用いて実現できました。

    '----------

        Public Declare Function OpenClipboard Lib "user32" Alias "OpenClipboard" (ByVal hwnd As IntPtr) As Integer

        Public Declare Function EmptyClipboard Lib "user32" Alias "EmptyClipboard" () As Boolean

        Public Declare Function SetClipboardData Lib "user32" (ByVal wFormat As Integer, ByVal hMem As IntPtr) As IntPtr

        Public Declare Function CloseClipboard Lib "user32" Alias "CloseClipboard" () As Boolean

        Public Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtr

        Public Declare Function DeleteEnhMetaFile Lib "gdi32" Alias "DeleteEnhMetaFile" (ByVal hemf As IntPtr) As Boolean

    '----------

        Public Shared Function PutEnhMetafileOnClipboard(ByVal hWnd As IntPtr, ByVal mf As System.Drawing.Imaging.Metafile) As Boolean

            Dim bResult As New Boolean()

            bResult = False

            Dim hEMF, hEMF2 As IntPtr

            hEMF = mf.GetHenhmetafile() ' invalidates mf

            If Not hEMF.Equals(New IntPtr(0)) Then

                hEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))

                If Not hEMF2.Equals(New IntPtr(0)) Then

                    If OpenClipboard(hWnd) Then

                        If EmptyClipboard() Then

                            Dim hRes As IntPtr

                            hRes = SetClipboardData(14, hEMF2) ' 14 == CF_ENHMETAFILE

                            bResult = hRes.Equals(hEMF2)

                            CloseClipboard()

                        End If

                    End If

                End If

                DeleteEnhMetaFile(hEMF)

            End If

            Return bResult

        End Function

    '----------

        Dim meta As Drawing.Imaging.Metafile = New System.Drawing.Imaging.Metafile("tempmetafile.emf")

        PutEnhMetafileOnClipboard(Me.Handle, meta)

    '----------

    また,EMF以外の形式でのクリップボード転送も以下の方法で実現しました。

    '----------

        Dim ms1 As IO.MemoryStream = New IO.MemoryStream

        Dim img1 As Image = Image.FromFile("tempbmpfile.bmp")

        img1.Save(ms1, System.Drawing.Imaging.ImageFormat.Png)

        img1.Dispose()

     

        Dim ms2 As IO.MemoryStream = New IO.MemoryStream

        Dim img2 As Image = Image.FromFile("tempbmpfile.bmp")

        img2.Save(ms2, System.Drawing.Imaging.ImageFormat.Jpeg)

        img2.Dispose()

     

        Dim dataobject As IDataObject = New DataObject()

        dataobject.SetData("PNG", False, ms1)

        dataobject.SetData("JFIF", False, ms2)

        System.Windows.Forms.Clipboard.SetDataObject(dataobject, True)

    '----------

    しかし,この両者を同時にクリップボードへ転送しようとするとうまく行きません。

    '----------

    試案1:

    PNG,JPEGをクリップボードにコピーしたあと,EMFをクリップボードにコピーする。

    ×PutEnhMetafileOnClipboard内のEmptyClipboard()でPNG,JPEGが消える

      ↓EmptyClipboard()をコメントアウトする。

        ×Excel2003には貼りつけられるが,何故かWord2003に貼り付けられなくなる。

    '----------

    試案2:

    EMFをクリップボードにコピーしたあと,PNG,JPEGををクリップボードにコピーする。

    ×Clipboard.SetDataObject(dataobject, True)するとEMFが消える

      ↓EMF転送直後にdataobject = Clipboard.GetDataObjectする。

        ×貼付け時にEMFの名前はあるが実際には貼り付けられない

      ↓EMF転送直後にdataobject = Clipboard.GetData("EnhancedMetafile")する。

        ×dataobject=Nothingとなる

    '----------

    試案3:

    PutEnhMetafileOnClipboard内でPNG,JPEGのコピーもする

    ×SetClipboardDataの第一引数の定数にPNG,JPEGが居ない。

    '----------

    以上,3つの試案をテストしてみましたが,うまく行きませんでした。

    しかし,私のテスト方法が悪かったかもしれないし,他にいいアイディアが

    あるかもしれないと思ってこのフォーラムに投稿させていただきました。

    もしも返信いただければ幸いです。宜しくお願い致します。

     

     

    2010年11月22日 6:19

回答

  • 詳しいやり方まで踏め込めない意見で申し訳ありません。

    クリップボードには基本的に一度にまとめて登録しなければなりません。
    Win32API のクリップボード操作関連のコードと、.NET Framework のクリップボード操作関連のコードの二つがある場合、「一度にまとめて登録」が実現できません。
    従って、どちらか片方のやり方にまとめるコードを考えていく必要があります。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答の候補に設定 山本春海 2010年11月29日 1:10
    • 回答としてマーク 山本春海 2010年12月8日 5:27
    2010年11月22日 11:33
    モデレータ
  • ×SetClipboardDataの第一引数の定数にPNG,JPEGが居ない。

    SetClipboardData の第 1 引数に "Registered Clipboard Formats" というリンクがあります。
    http://msdn.microsoft.com/en-us/library/ms649051(VS.85).aspx
    http://msdn.microsoft.com/en-us/library/ms649049(VS.85).aspx

    RegisterClipboardFormat に任意の文字列を渡して登録し、それに対応する数値(ID)を取得してください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答の候補に設定 山本春海 2010年12月1日 8:56
    • 回答としてマーク 山本春海 2010年12月8日 5:27
    2010年11月22日 11:36
    モデレータ

すべての返信

  • 詳しいやり方まで踏め込めない意見で申し訳ありません。

    クリップボードには基本的に一度にまとめて登録しなければなりません。
    Win32API のクリップボード操作関連のコードと、.NET Framework のクリップボード操作関連のコードの二つがある場合、「一度にまとめて登録」が実現できません。
    従って、どちらか片方のやり方にまとめるコードを考えていく必要があります。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答の候補に設定 山本春海 2010年11月29日 1:10
    • 回答としてマーク 山本春海 2010年12月8日 5:27
    2010年11月22日 11:33
    モデレータ
  • ×SetClipboardDataの第一引数の定数にPNG,JPEGが居ない。

    SetClipboardData の第 1 引数に "Registered Clipboard Formats" というリンクがあります。
    http://msdn.microsoft.com/en-us/library/ms649051(VS.85).aspx
    http://msdn.microsoft.com/en-us/library/ms649049(VS.85).aspx

    RegisterClipboardFormat に任意の文字列を渡して登録し、それに対応する数値(ID)を取得してください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答の候補に設定 山本春海 2010年12月1日 8:56
    • 回答としてマーク 山本春海 2010年12月8日 5:27
    2010年11月22日 11:36
    モデレータ
  • Azulean様、お返事ありがとうございます。
    ご助言を参考にして以下のコードでメタファイル,PNG,JPEGを一括してクリップボードに転送できました。
    でも,私がVB2008の知識が少ないせいで愚かなことをやっております。
    SetClipboardDataにハンドルを渡す際,
    MemoryStreamのハンドルを直接取得する方法が分からなかったので,
    IntPtrでメモリを確保してそこにコピーしているのです。
    もっとスマートなやり方があったらどうかご教示願います。
    '----------
    Public Declare Function OpenClipboard Lib "user32" Alias "OpenClipboard" (ByVal hwnd As IntPtr) As Integer
    Public Declare Function EmptyClipboard Lib "user32" Alias "EmptyClipboard" () As Boolean
    Public Declare Function SetClipboardData Lib "user32" (ByVal wFormat As Integer, ByVal hMem As IntPtr) As IntPtr
    Public Declare Function RegisterClipboardFormat Lib "user32.dll" Alias "RegisterClipboardFormatA" (ByVal lpszFormat As String) As Integer
    Public Declare Function CloseClipboard Lib "user32" Alias "CloseClipboard" () As Boolean
    Public Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtr
    Public Declare Function DeleteEnhMetaFile Lib "gdi32" Alias "DeleteEnhMetaFile" (ByVal hemf As IntPtr) As Boolean
    '----------
    Sub Hoge()

        'メタファイル取得
        Dim meta As Drawing.Imaging.Metafile = New System.Drawing.Imaging.Metafile("tempmetafile.emf")

        'PNG取得
        Dim ms1 As IO.MemoryStream = New IO.MemoryStream
        Dim img1 As Image = Image.FromFile("tempbmpfile.bmp")
        img1.Save(ms1, System.Drawing.Imaging.ImageFormat.Png)
        img1.Dispose()
        Dim i1 As Integer = RegisterClipboardFormat("PNG")
        Dim l1 As Integer = ms1.Length
        Dim h1 As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(l1)
        System.Runtime.InteropServices.Marshal.Copy(ms1.GetBuffer, 0, h1, l1)

        'JPEG取得
        Dim ms2 As IO.MemoryStream = New IO.MemoryStream
        Dim img2 As Image = Image.FromFile("tempbmpfile.bmp")
        img2.Save(ms2, System.Drawing.Imaging.ImageFormat.Jpeg)
        img2.Dispose()
        Dim i2 As Integer = RegisterClipboardFormat("JFIF")
        Dim l2 As Integer = ms2.Length
        Dim h2 As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(l2)
        System.Runtime.InteropServices.Marshal.Copy(ms2.GetBuffer, 0, h2, l2)

        Dim hEMF, hEMF2 As IntPtr
        hEMF = meta.GetHenhmetafile() ' invalidates mf
        If Not hEMF.Equals(New IntPtr(0)) Then
            hEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))
            If Not hEMF2.Equals(New IntPtr(0)) Then
                If OpenClipboard(Me.Handle) Then
                    If EmptyClipboard() Then
                        Dim hRes As IntPtr
                        hRes = SetClipboardData(14, hEMF2)  ' 14 == CF_ENHMETAFILE
                        hRes = SetClipboardData(i1, h1)     ' PNG
                        hRes = SetClipboardData(i2, h2)     ' JPEG
                        CloseClipboard()
                    End If
                End If
            End If
            DeleteEnhMetaFile(hEMF)
        End If

        System.Runtime.InteropServices.Marshal.FreeHGlobal(h1)
        System.Runtime.InteropServices.Marshal.FreeHGlobal(h2)
        meta.Dispose()

    End Sub
    '----------

    2010年11月24日 2:02
  • 気になる点がいくつかあります。
    ただし、私自身も説明できる状態にないものを含みますので、ご自身でも調べてみてください。

    ・MemoryStream が Dispose されていません。
    ・SetClipboard に渡したメモリオブジェクトは write/free してはいけないと記載があります。
    ・SetClipboard に渡すメモリオブジェクトは GMEM_MOVEABLE フラグを指定して確保しなければならないと書いてあるが、AllocHGlobal で確保したポインタをそのまま渡して良いか不明。
    http://msdn.microsoft.com/en-us/library/ms649051(VS.85).aspx


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年11月27日 23:08
    モデレータ
  • こんにちは、monger_monger さん。

    MSDN フォーラムのご利用ありがとうございます。フォーラム オペレーターの 山本です。

    Azulean さんからのアドバイスが参考になったようでしたので、勝手ながら私のほうで回答としてマークさせていただきました。
    Azulean さん、アドバイスありがとうございます。

    問題解決に役立った有効な情報、参考になった情報には、回答としてマークすることで、今後、同じ問題でこのスレッドを参照される方にも、有効な情報がわかり易くなるかと思いますので、ご協力よろしくお願いいたします。

    他にも何か情報をお持ちの方がいらっしゃいましたら、是非投稿をお願いいたします。
    今後とも、MSDN フォーラムをよろしくお願いいたします。それでは。
    _________________________________________
    マイクロソフト株式会社 フォーラム オペレーター 山本 春海

    2010年12月8日 5:27