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

質問
-
お世話になります。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つの試案をテストしてみましたが,うまく行きませんでした。
しかし,私のテスト方法が悪かったかもしれないし,他にいいアイディアが
あるかもしれないと思ってこのフォーラムに投稿させていただきました。
もしも返信いただければ幸いです。宜しくお願い致します。
回答
-
詳しいやり方まで踏め込めない意見で申し訳ありません。
クリップボードには基本的に一度にまとめて登録しなければなりません。
Win32API のクリップボード操作関連のコードと、.NET Framework のクリップボード操作関連のコードの二つがある場合、「一度にまとめて登録」が実現できません。
従って、どちらか片方のやり方にまとめるコードを考えていく必要があります。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
×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).aspxRegisterClipboardFormat に任意の文字列を渡して登録し、それに対応する数値(ID)を取得してください。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
すべての返信
-
詳しいやり方まで踏め込めない意見で申し訳ありません。
クリップボードには基本的に一度にまとめて登録しなければなりません。
Win32API のクリップボード操作関連のコードと、.NET Framework のクリップボード操作関連のコードの二つがある場合、「一度にまとめて登録」が実現できません。
従って、どちらか片方のやり方にまとめるコードを考えていく必要があります。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
×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).aspxRegisterClipboardFormat に任意の文字列を渡して登録し、それに対応する数値(ID)を取得してください。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
Azulean様、お返事ありがとうございます。ご助言を参考にして以下のコードでメタファイル,PNG,JPEGを一括してクリップボードに転送できました。でも,私がVB2008の知識が少ないせいで愚かなことをやっております。SetClipboardDataにハンドルを渡す際,MemoryStreamのハンドルを直接取得する方法が分からなかったので,IntPtrでメモリを確保してそこにコピーしているのです。もっとスマートなやり方があったらどうかご教示願います。'----------Public Declare Function OpenClipboard Lib "user32" Alias "OpenClipboard" (ByVal hwnd As IntPtr) As IntegerPublic Declare Function EmptyClipboard Lib "user32" Alias "EmptyClipboard" () As BooleanPublic Declare Function SetClipboardData Lib "user32" (ByVal wFormat As Integer, ByVal hMem As IntPtr) As IntPtrPublic Declare Function RegisterClipboardFormat Lib "user32.dll" Alias "RegisterClipboardFormatA" (ByVal lpszFormat As String) As IntegerPublic Declare Function CloseClipboard Lib "user32" Alias "CloseClipboard" () As BooleanPublic Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtrPublic 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.MemoryStreamDim 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.LengthDim 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.MemoryStreamDim 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.LengthDim h2 As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(l2)System.Runtime.InteropServices.Marshal.Copy(ms2.GetBuffer, 0, h2, l2)
Dim hEMF, hEMF2 As IntPtrhEMF = meta.GetHenhmetafile() ' invalidates mfIf Not hEMF.Equals(New IntPtr(0)) ThenhEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))If Not hEMF2.Equals(New IntPtr(0)) ThenIf OpenClipboard(Me.Handle) ThenIf EmptyClipboard() ThenDim hRes As IntPtrhRes = SetClipboardData(14, hEMF2) ' 14 == CF_ENHMETAFILEhRes = SetClipboardData(i1, h1) ' PNGhRes = SetClipboardData(i2, h2) ' JPEGCloseClipboard()End IfEnd IfEnd IfDeleteEnhMetaFile(hEMF)End If
System.Runtime.InteropServices.Marshal.FreeHGlobal(h1)System.Runtime.InteropServices.Marshal.FreeHGlobal(h2)meta.Dispose()
End Sub'----------
-
気になる点がいくつかあります。
ただし、私自身も説明できる状態にないものを含みますので、ご自身でも調べてみてください。・MemoryStream が Dispose されていません。
・SetClipboard に渡したメモリオブジェクトは write/free してはいけないと記載があります。
・SetClipboard に渡すメモリオブジェクトは GMEM_MOVEABLE フラグを指定して確保しなければならないと書いてあるが、AllocHGlobal で確保したポインタをそのまま渡して良いか不明。
http://msdn.microsoft.com/en-us/library/ms649051(VS.85).aspx
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
こんにちは、monger_monger さん。
MSDN フォーラムのご利用ありがとうございます。フォーラム オペレーターの 山本です。
Azulean さんからのアドバイスが参考になったようでしたので、勝手ながら私のほうで回答としてマークさせていただきました。
Azulean さん、アドバイスありがとうございます。
問題解決に役立った有効な情報、参考になった情報には、回答としてマークすることで、今後、同じ問題でこのスレッドを参照される方にも、有効な情報がわかり易くなるかと思いますので、ご協力よろしくお願いいたします。
他にも何か情報をお持ちの方がいらっしゃいましたら、是非投稿をお願いいたします。
今後とも、MSDN フォーラムをよろしくお願いいたします。それでは。
_________________________________________
マイクロソフト株式会社 フォーラム オペレーター 山本 春海