Re: Bitmap aus Zwischenablage lesen

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance

From: Arne Janning (spam.me-here.arnolo_at_msn.com)
Date: 06/13/04


Date: Sun, 13 Jun 2004 06:15:39 +0200

Christoph Redl wrote:
> Ich will ein Bitmap aus der System-Zwischenablage in mein
> Programm kopieren. Letztendlich will ich ein Bitmap-Objekt
> in meinem Programm haben.
> Um festzustellen ob ein Bitmap in der Zwischenablage ist
> benutze ich folgenden Code:
>
> Clipboard.GetDataObject().GetDataPresent
> (System.Windows.Forms.DataFormats.Dib, True)
>
> Unter WinXP funktioniert es auch, wenn ich das Format
> System.Windows.Forms.DataFormats.Bitmap überprüfe.
> Unter Win98 und WinME erkennt er mir das Bitmap aber nur
> als System.Windows.Forms.DataFormats.Dib.
>
> Daher wollte ich wissen, wie ich als Dib gespeicherte
> Daten in ein Bitmap-Objekt bekomme.
> Wenn ich versuche es ganz normal als Bitmap einzulesen
> erhalte ich einen Fehler.
> Wenn ich versuche es als Dib einzulesen und einem Bitmap-
> Objekt zuzuweisen erhalte ich ebenfalls einen Fehler.

Hallo Christoph,

das Problem ist, dass es in .NET zwar einen Wrapper für die
GDI+-Bitmap-Funktion GdipCreateBitmapFromHBITMAP gibt (das ist
Image.FromHbitmap(IntPtr hbitmap)), es aber tatsächlich keinen Wrapper
für GdipCreateBitmapFromGdiDib gibt. Scheinbar ist das einfach vergessen
worden. Referenz der GDI+-Bitmap-Funktionen ist hier:
http://msdn.microsoft.com/library/?url=/library/en-us/gdicpp/GDIPlus/GDIPlusReference/FlatBitmap.asp

Thomas Scheidegger hat einen einen Workaround dafür geschrieben:
http://dnetmaster.net/source/dibtoimage.zip

Du kannst seinen Code (C#) verwenden oder nur den, der für Dich relevant
ist und den ich nach VB.NET portiert habe und unten anhänge.

Die Benutzung ist in beiden Fällen dieselbe:

Imports System.Runtime.InteropServices

'Clipboard-Daten holen
Dim obj As Object =
Clipboard.GetDataObject().GetDataPresent(DataFormats.Dib)
'In einen MemoryStream casten
Dim stream As System.IO.MemoryStream = CType(obj, System.IO.MemoryStream)
Dim img As Byte() = stream.ToArray()
'Reserviert einen Speicherblock
Dim p As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(img(0)) *
img.Length)
'Kopiert die Daten in einen nicht verwalteten Speicherzeiger
Marshal.Copy(img, 0, p, img.Length)
Try
   'DibToImage.WithStream(p) liefert ein Bitmap-Objekt
   pictureBox1.Image = DibToImage.WithStream(p)
Catch ex As Exception
   'Fehlerbehandlung
Finally
   'Speicherblock wieder freigeben
   Marshal.FreeCoTaskMem(p)
End Try

Hier der DibToImage-Quelltext:
'DibToImage.vb

Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO
Imports System.Runtime.InteropServices

Public Class DibToImage
     Public Shared Function WithStream( _
         ByVal dibPtr As IntPtr) As Bitmap
         Dim fh As BITMAPFILEHEADER = New BITMAPFILEHEADER
         Dim bmiTyp As Type = GetType(BITMAPINFOHEADER)
         Dim bmi As BITMAPINFOHEADER = _
             CType(Marshal.PtrToStructure( _
                 dibPtr, bmiTyp), BITMAPINFOHEADER)
         If bmi.biSizeImage = 0 Then
             bmi.biSizeImage = _
             ((((bmi.biWidth * bmi.biBitCount) + 31) _
             And -32) >> 3) * Math.Abs(bmi.biHeight)
         End If

         If bmi.biClrUsed = 0 AndAlso bmi.biBitCount < 16 Then
             bmi.biClrUsed = 1 << bmi.biBitCount
         End If

         Dim fhSize As Int32 = _
             Marshal.SizeOf(GetType(BITMAPFILEHEADER))
         Dim dibSize As Int32 = _
         bmi.biSize + (bmi.biClrUsed * 4) + bmi.biSizeImage
         fh.Type = New Char() {CChar("B"), CChar("M")}
         fh.Size = fhSize + dibSize
         fh.OffBits = fhSize + _
             bmi.biSize + (bmi.biClrUsed * 4)
         Dim data(fh.Size) As Byte
         RawSerializeInto(fh, data)
         Marshal.Copy(dibPtr, data, fhSize, dibSize)
         Dim stream As MemoryStream = _
             New MemoryStream(data)
         Dim tmp As Bitmap = New Bitmap(stream)
         Dim result As Bitmap = New Bitmap(tmp)
         tmp.Dispose()
         tmp = Nothing
         stream.Close()
         stream = Nothing
         data = Nothing
         Return result
     End Function

     Private Shared Sub RawSerializeInto( _
         ByVal anything As Object, ByVal datas As Byte())
         Dim rawsize As Int32 = Marshal.SizeOf(anything)
         If rawsize > datas.Length Then
             Throw New ArgumentException( _
                 "buffer too small", "byte() datas")
         End If
         Dim handle As GCHandle = GCHandle.Alloc( _
             datas, GCHandleType.Pinned)
         Dim buffer As IntPtr = handle.AddrOfPinnedObject()
         Marshal.StructureToPtr(anything, buffer, False)
         handle.Free()
     End Sub

     <StructLayout(LayoutKind.Sequential, _
         CharSet:=CharSet.Ansi, Pack:=1)> _
     Private Class BITMAPFILEHEADER
         <MarshalAs(UnmanagedType.ByValArray, _
             SizeConst:=2)> _
         Public Type As Char()
         Public Size As Int32
         Public reserved1 As Int16
         Public reserved2 As Int16
         Public OffBits As Int32
     End Class

     <StructLayout(LayoutKind.Sequential, Pack:=2)> _
     Private Class BITMAPINFOHEADER
         Public biSize As Int32
         Public biWidth As Int32
         Public biHeight As Int32
         Public biPlanes As Int16
         Public biBitCount As Int16
         Public biCompression As Int32
         Public biSizeImage As Int32
         Public biXPelsPerMeter As Int32
         Public biYPelsPerMeter As Int32
         Public biClrUsed As Int32
         Public biClrImportant As Int32
     End Class
End Class

Gruß

Arne Janning



Relevant Pages

  • =?Windows-1252?Q?Re:_Generelle_Einstellungen_f=FCr_img-Tag?=
    ... Imports System.Web.UI ... Namespace SFW.Controls ... Public Class Image ... End Sub ...
    (microsoft.public.de.german.entwickler.dotnet.asp)
  • Re: Anweisungen per Dienst ausführen
    ... Hier mein VB-Code: ... Imports System.Runtime.Remoting.Channels.Tcp ... Public Class Form1 ... End Function ...
    (microsoft.public.de.german.entwickler.dotnet.vb)
  • Re: VB.NET vs. C#
    ... beide Sprachen haben ihre Vor und Nachteile ... schneller zu tippen als endIf end for etc. ... > public class Foo: Goo, IBaz, IBla ... > Dim a As Byte = ... ...
    (microsoft.public.de.german.entwickler.dotnet.vb)
  • ayuda con asignacion de objetos
    ... TLista = class ... constructor NewLista; {Crea una nueva lista vacia y retorna ... PNodo); ...
    (borland.public.delphi.non-technical)
  • Re: MainForm ansprechen
    ... > Aus einem MDI kann ich auf das MainForm mittels ParentForm zugreifen. ... Public Class Form1 ... Private Sub Form1_Load(ByVal sender As System.Object, ... End Class ...
    (microsoft.public.de.german.entwickler.dotnet.vb)