DOBON.NET DOBON.NETプログラミング掲示板過去ログ

クリップボードからの画像取得について

環境/言語:[Vista/VB]
分類:[.NET]

ある特定のソフト(LANクリップボード)からの取得したイメージを
VBから取得できません。

以下のプログラムで確認すると「Nothing」となります。
ペイントなどで貼り付けはできるのですが、どのような形式となっ
ているか確認する方法は、ありますでしょうか?

Dim Image As System.Drawing.Image
If Clipboard.ContainsImage() Then
MsgBox("Image")
Image = Clipboard.GetImage()
If Image Is Nothing Then
MsgBox("Nothing")

End If
End If

以上、よろしくお願いいたします。
2009/05/11(Mon) 04:06:19 編集(投稿者)

> ペイントなどで貼り付けはできるのですが、どのような形式となっ
> ているか確認する方法は、ありますでしょうか?

IDataObject.GetFormatsメソッドでできます。

For Each f As String In Clipboard.GetDataObject().GetFormats()
Console.WriteLine(f)
Next
ありがとうございます。
IDataObject.GetFormatsでうまく取得できました。

通常は、
System.Drawing.Bitmap
Bitmap
DeviceIndependentBitmap
Format17

取得できないのは、
DeviceIndependentBitmap
System.Drawing.Bitmap
Bitmap
Format17

でした。ここからデータをとってPictureBoxにもってこようとおもう
のですがClipboard.GetImage()ではもってこれませんでした。

たびたびもうしわけございませんがご存知でしたらご教授いただけれ
ばと思います。

■No24527に返信(管理人さんの記事)
> 2009/05/11(Mon) 04:06:19 編集(投稿者)
>
>>ペイントなどで貼り付けはできるのですが、どのような形式となっ
>>ているか確認する方法は、ありますでしょうか?
>
> IDataObject.GetFormatsメソッドでできます。
>
> For Each f As String In Clipboard.GetDataObject().GetFormats()
> Console.WriteLine(f)
> Next
■No24533に返信(ぺんぺんさんの記事)
> 取得できないのは、
> DeviceIndependentBitmap
> System.Drawing.Bitmap
> Bitmap
> Format17
> でした。ここからデータをとってPictureBoxにもってこようとおもう
> のですがClipboard.GetImage()ではもってこれませんでした。

Clipboard.GetData(String) で取得してみてください。

System.Drawing.Bitmap/Bitmap では Bitmap クラスのインスタンスが、
DeviceIndependentBitmap/Format17 では、MemoryStream クラスのインスタンスが
取得できるかと思います。
度々ありがとうございます。

以下の方法で取得を試みたのですがうまく取得できません。

方法1
Dim ms As MemoryStream = CType(Clipboard.GetData("DeviceIndependentBitmap"), MemoryStream)
PictureBox1.Image = Bitmap.FromStream(ms)

方法2
Dim bmap As Bitmap = CType(Clipboard.GetData("System.Drawing.Bitmap"), Bitmap)
PictureBox1.Image = bmap

方法3
PictureBox1.Image = Clipboard.GetData("System.Drawing.Bitmap")

方法4
PictureBox1.Image = Clipboard.GetData("DeviceIndependentBitmap")

お手数をおかけしますがご教授いただければと思います。


■No24534に返信(魔界の仮面弁士さんの記事)
> ■No24533に返信(ぺんぺんさんの記事)
>>取得できないのは、
>>DeviceIndependentBitmap
>>System.Drawing.Bitmap
>>Bitmap
>>Format17
>>でした。ここからデータをとってPictureBoxにもってこようとおもう
>>のですがClipboard.GetImage()ではもってこれませんでした。
>
> Clipboard.GetData(String) で取得してみてください。
>
> System.Drawing.Bitmap/Bitmap では Bitmap クラスのインスタンスが、
> DeviceIndependentBitmap/Format17 では、MemoryStream クラスのインスタンスが
> 取得できるかと思います。
■No24546に返信(ぺんぺんさんの記事)
> 以下の方法で取得を試みたのですがうまく取得できません。

取得はできたけれど、期待する結果では無かった、という意味でしょうか?


まず、GetData(String) の結果を Object 型変数に受け取った上で、
 If o Is Nothing Then
  TextBox1.Text = "Nothing"
 Else
  TextBox1.Text = o.GetType().FullName
 End If
を実行し、どのような型が返されているのか確認してみてください。


もし、Nothing になってしまう、あるいは、Bitmap 型ではあるが、
PictureBox 等に表示すると別の画像に見えるという事なのであれば、
クリップボード共有ソフトによって、操作が阻害されているのかも知れませんので、
確認のため、一時的にそのソフトを終了してみてください。
度々のご回答ありがとうございます。

早速ですが、それぞれの方法で取得した際は、以下のようになりました。
方法1
Message="使用されたパラメータが有効ではありません。"
Source="System.Drawing"

方法2
 反応なし(イメージ取得されず)

方法3
 反応なし(イメージ取得されず)

方法4
 型 'System.IO.MemoryStream' のオブジェクトを型 'System.Drawing.Image' にキャストできません。

また、o.GetType().FullNameでは、System.Windows.Forms.DataObjectと返されました。
クリップボード共有ソフトを終了しても同様にPictureBox には貼り付けられませんでした。

お手数をおかけしますがご教授いただければ幸いです。

■No24547に返信(魔界の仮面弁士さんの記事)
> ■No24546に返信(ぺんぺんさんの記事)
>>以下の方法で取得を試みたのですがうまく取得できません。
>
> 取得はできたけれど、期待する結果では無かった、という意味でしょうか?
>
>
> まず、GetData(String) の結果を Object 型変数に受け取った上で、
>  If o Is Nothing Then
>   TextBox1.Text = "Nothing"
>  Else
>   TextBox1.Text = o.GetType().FullName
>  End If
> を実行し、どのような型が返されているのか確認してみてください。
>
>
> もし、Nothing になってしまう、あるいは、Bitmap 型ではあるが、
> PictureBox 等に表示すると別の画像に見えるという事なのであれば、
> クリップボード共有ソフトによって、操作が阻害されているのかも知れませんので、
> 確認のため、一時的にそのソフトを終了してみてください。
No24533 のご投稿を拝見させていただくと、「通常は」と「取得できないのは」で列挙されているフォーマットは同じで、順番が違うだけのようですが、順番が違うだけで取得できなくなるものでしょうか?私はあまり詳しくないので何とも言えませんが、ちょっと変なような気がします。

ちなみにIrfanViewという画像ビューアで画像をクリップボードにコピーして、IDataObject.GetFormatsでフォーマットを調べたところ、「取得できないのは」と全く同じフォーマットになりましたが、Clipboard.GetImageで取得できました。
2009/05/14(Thu) 16:23:08 編集(投稿者)

>> まず、GetData(String) の結果を Object 型変数に受け取った上で、
>> TextBox1.Text = o.GetType().FullName
>> を実行し、どのような型が返されているのか確認してみてください。
> また、o.GetType().FullNameでは、System.Windows.Forms.DataObjectと返されました。

引数にどのフォーマットを指定しても、すべて System.Windows.Forms.DataObject と
返されてしまう、という事なのでしょうか。

もしかして、変数 o への取得部分が、GetObject(String) ではなく、
GetDataObject になってしまっていませんか? 再確認をお願いします。

もし、本当に DataObject が返されたのであれば、メッセージは、
> 型 'System.IO.MemoryStream' のオブジェクトを型 'System.Drawing.Image' にキャストできません。
ではなく、
 型 'System.Windows.Forms.DataObject' のオブジェクトを型 'System.Drawing.Image' にキャストできません。
になりそうなものなのですけれども。


また、型変換エラーが出たという事は、 No24546 のコードになぞらえて
 PictureBox1.Image = o
あるいは
 PictureBox1.Image = DirectCast(o, Image)
などといったコードを書いているという事ですよね。

もし、上記のようにしているのだとしたら、それができるのは、
o.GetType() が Bitmap 等の Image 継承クラスを返してくる場合だけです。

少なくとも、型が DataObject や MemoryStream だと分かった時点で、
Image へと直接キャストする事は意味がありません。DataObject や MemoryStream など、
それぞれの型にキャストした上で、その中身を取り出すコードに修正する必要があります。


■No24556に返信(管理人さんの記事)
> 順番が違うだけで取得できなくなるものでしょうか?
クリップボードからの貼り付け時に複数のフォーマットがサポートされる
アプリケーション(たとえば、ワードパッドなど)の場合、その実装として
以下の 2 パターンが考えられます。(以下、VB.NET 的表現)

 '★パターン A★
 For Each fmt In アプリがサポートする形式一覧
  If Clipboard.ContainsData(fmt) Then
   貼り付け処理
   Exit For
  End If
 Next

 '★パターン B★
 For Each fmt In Clipboard.GetDataObject.GetFormats()
  If アプリがサポートする形式一覧.Contains(fmt) Then
   貼り付け処理
   Exit For
  End If
 Next

後者で実装されたアプリの場合、列挙順が変わると動作が変化する事はありそうです。

列挙順の変更が、今回の問題と関係しているかどうかまでは分かりませんけれども、
クリップボード内に複数の Image 型が内包されていた場合、列挙順によっては
No24526 のような
 If Clipboard.ContainsImage() Then
  Image = Clipboard.GetImage()
というコードにおいて、GetImage が返す結果が変化する可能性はあるかもしれません。
2009/05/14(Thu) 16:18:23 編集(投稿者)

■No24558に追記(魔界の仮面弁士の記事)
> 少なくとも、型が DataObject や MemoryStream だと分かった時点で、
> Image へと直接キャストする事は意味がありません。DataObject や MemoryStream など、
> それぞれの型にキャストした上で、その中身を取り出すコードに修正する必要があります。

"DeviceIndependentBitmap" で得られる MemoryStream については、
そのままでは Image.FromStream 等では取り出せないようですね。

ヘッダを付与してやると、Bitmap として読み込めるようになりました。


以下、VB2005 での手抜き実装。


'----- Form1.vb -----
Imports System.IO
Public Class Form1
 Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
  Dim dataObject As IDataObject = Clipboard.GetDataObject()
  If dataObject.GetDataPresent(DataFormats.Dib, False) Then
   Me.BackgroundImage = CreateBitmapFromDIB(TryCast(dataObject.GetData(DataFormats.Dib, False), MemoryStream))
  End If
 End Sub
End Class


'----- Module1.vb -----
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Text
Imports System.Runtime.InteropServices

Module Module1
 Public Function CreateBitmapFromDIB(ByVal dib As MemoryStream) As Bitmap
  If dib Is Nothing Then
   Return Nothing
  End If

  Const BITMAPFILEHEADER_SIZE As Integer = 14
  Dim bin() As Byte = dib.ToArray()
  Dim headerSize As Integer = BitConverter.ToInt32(bin, 0)
  Dim pixelSize As Integer = bin.Length - headerSize
  Dim fileSize As Integer = BITMAPFILEHEADER_SIZE + bin.Length

  Dim bmpStm As New MemoryStream(fileSize)
  Dim writer As New BinaryWriter(bmpStm)
  '--- Bitmap File Header ---
  writer.Write(Encoding.ASCII.GetBytes("BM")) 'Type
  writer.Write(fileSize) 'Size
  writer.Write(0UI) 'reserved1,reserved2
  writer.Write(BITMAPFILEHEADER_SIZE + headerSize) 'OffBits
  '--- DIB ---
  writer.Write(bin)
  writer.Flush()
  bmpStm.Seek(0, SeekOrigin.Begin)
  Return New Bitmap(bmpStm, False)
 End Function
End Module
No 24562 のコードでうまくいきました!
ありがとうございました。

>もしかして、変数 o への取得部分が、GetObject(String) ではなく、
>GetDataObject になってしまっていませんか? 再確認をお願いします。
GetDataObject になっていました。
GetObjectに直して再度実行したところSystem.Drawing.Bitmapはnothing
でDeviceIndependentBitmapは、System.IO.MemoryStreamとなりました。

VBSからはじめてVBに来たので型の定義への理解がいまいちなので勉強
しようとおもいます。

本当にありがとございました。

■No24562に返信(魔界の仮面弁士さんの記事)
> 2009/05/14(Thu) 16:18:23 編集(投稿者)
>
> ■No24558に追記(魔界の仮面弁士の記事)
>>少なくとも、型が DataObject や MemoryStream だと分かった時点で、
>>Image へと直接キャストする事は意味がありません。DataObject や MemoryStream など、
>>それぞれの型にキャストした上で、その中身を取り出すコードに修正する必要があります。
>
> "DeviceIndependentBitmap" で得られる MemoryStream については、
> そのままでは Image.FromStream 等では取り出せないようですね。
>
> ヘッダを付与してやると、Bitmap として読み込めるようになりました。
>
>
> 以下、VB2005 での手抜き実装。
>
>
> '----- Form1.vb -----
> Imports System.IO
> Public Class Form1
>  Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
>   Dim dataObject As IDataObject = Clipboard.GetDataObject()
>   If dataObject.GetDataPresent(DataFormats.Dib, False) Then
>    Me.BackgroundImage = CreateBitmapFromDIB(TryCast(dataObject.GetData(DataFormats.Dib, False), MemoryStream))
>   End If
>  End Sub
> End Class
>
>
> '----- Module1.vb -----
> Imports System
> Imports System.IO
> Imports System.Drawing
> Imports System.Text
> Imports System.Runtime.InteropServices
>
> Module Module1
>  Public Function CreateBitmapFromDIB(ByVal dib As MemoryStream) As Bitmap
>   If dib Is Nothing Then
>    Return Nothing
>   End If
>
>   Const BITMAPFILEHEADER_SIZE As Integer = 14
>   Dim bin() As Byte = dib.ToArray()
>   Dim headerSize As Integer = BitConverter.ToInt32(bin, 0)
>   Dim pixelSize As Integer = bin.Length - headerSize
>   Dim fileSize As Integer = BITMAPFILEHEADER_SIZE + bin.Length
>
>   Dim bmpStm As New MemoryStream(fileSize)
>   Dim writer As New BinaryWriter(bmpStm)
>   '--- Bitmap File Header ---
>   writer.Write(Encoding.ASCII.GetBytes("BM")) 'Type
>   writer.Write(fileSize) 'Size
>   writer.Write(0UI) 'reserved1,reserved2
>   writer.Write(BITMAPFILEHEADER_SIZE + headerSize) 'OffBits
>   '--- DIB ---
>   writer.Write(bin)
>   writer.Flush()
>   bmpStm.Seek(0, SeekOrigin.Begin)
>   Return New Bitmap(bmpStm, False)
>  End Function
> End Module
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板