Bild aus Zwischenablage in GUI kopieren

  • Hallo liebe Community,

    ich habe vor kurzem angefangen mich mit den GDIPlus-Funktionen von AutoIt vertraut zu machen, da ich einige Bild-Analysen programmieren möchte. Dabei geht es nicht so sehr um das Tempo, sondern mehr um die Präsentation der Analyse.

    Und leider scheitere ich schon an der wichtigsten Grundfunktion: Das holen einer Grafik aus der Zwischenablage nach AutoIt. Da die kleinen Tools später einmal in einer Umgebung ohne Schreibzugriff auf Datenträger laufen sollen kann ich nicht den Umweg Zwischenablage => temporäre Datei => Tool gehen... das wäre ja nicht so das Problem.
    Ich muss das irgendwie ohne temporäre Datei lösen.

    Das folgende Script ist meine Versuchsbasis für dieses Problem: Es lädt eine Grafik aus einer Datei und stellt diese in der linken Hälfte der GUI dar, anschließend soll eine Grafik aus der Zwischenablage geholt und in der rechten Hälfte der GUI angezeigt werden. Teil 1 (Grafik aus Datei) funktioniert wie gesagt problemlos, aber die rechte Hälfte bleibt immer leer... ;(


    Hat jemand hier auch schon mal so eine Funktion geschrieben oder hat eine Idee, was da bei mir nicht stimmt?


    Hier mal das Script:

    [autoit]


    #include <GDIPlus.au3>
    #include <Clipboard.au3>
    #include <GUIConstantsEx.au3>

    [/autoit][autoit][/autoit][autoit]

    ; Folgende Typ-Kennzeichen werden verwendet:
    ; $s... = Variable vom Typ STRING
    ; $hwnd_... = Handle eines Fensters
    ; $hgro_... = Handle eines Graphics-Objekts
    ; $himo_... = Handle eines Image-Objekts
    ; $hbmp_... = Handle eines Bitmaps
    ; $hbmo_... = Handle eines Bitmap-Objekts

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    ; Vorbereitende Aktionen:
    $sFileName = "Sterne.bmp" ; Name der Bitmap-Datei...
    $hwnd_GUI = GUICreate("GUI", 201, 100)
    GUISetState()
    _GDIPlus_Startup()

    [/autoit][autoit][/autoit][autoit]

    ; Handle auf das Graphics-Objekt des GUI-Fensters
    $hgro_GUI = _GDIPlus_GraphicsCreateFromHWND($hwnd_GUI)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ; +++ Schritt 1: Die Bitmap-Datei laden und in der linken Hälfte des GUI-Fensters anzeigen +++
    ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    [/autoit][autoit][/autoit][autoit]

    ; Handle auf das Image-Objekt der geladenen Bitmap-Datei...
    $himo_BitmapFile = _GDIPlus_ImageLoadFromFile($sFileName)
    ConsoleWrite("Größe der geladenen Bitmap-Grafik: " & _GDIPlus_ImageGetWidth($himo_BitmapFile) & " x " & _GDIPlus_ImageGetHeight($himo_BitmapFile) & " (BxH)" & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    ; Das geladene Bild in der linken Hälfte des GUI-Fensters anzeigen. Es wird immer auf 100x100 Pixel skaliert
    _GDIPlus_GraphicsDrawImageRect($hgro_GUI, $himo_BitmapFile, 0, 0, 100, 100)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ; +++ Schritt 2: Die sich in der Zwischenablage befindliche Grafik laden und in der rechten Hälfe des GUI-Fensters anzeigen +++
    ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    [/autoit][autoit][/autoit][autoit]

    $hbmp_ClipboardBitmap = _ClipBoard_GetData($CF_BITMAP) ; Laut Hilfe liefert diese Funktion ein Handle des Bitmaps in der Zwischenablage
    $hbmo_ClipboardBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbmp_ClipboardBitmap) ; und so sollte aus dem HBITMAP ein Bitmap-Objekt werden

    [/autoit][autoit][/autoit][autoit]

    ;
    $himo_ClipboardBitmap = _GDIPlus_ImageGetGraphicsContext($hbmp_ClipboardBitmap) ; In der Hilfe zu _GDIPlus_ImageGetGraphicsContext wird gezeigt, das ein HBITMAP so in ein Image umgewandelt werden kann,
    ; dort wird per _ScreenCapture_Capture ein HBITMAP zurückgeliefert...
    _GDIPlus_GraphicsDrawImageRect($hgro_GUI, $himo_ClipboardBitmap, 101, 0, 100, 100)

    [/autoit][autoit][/autoit][autoit]

    ; Jetzt noch warten bis das GUI-Fenster geschlossen wird, dann endet das Script.
    While 1
    $msg = GUIGetMsg()
    If $msg = $GUI_EVENT_CLOSE Then ExitLoop
    WEnd

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func OnAutoItExit()
    _GDIPlus_Shutdown()
    EndFunc

    [/autoit]

    Vielen Dank schon mal im voraus.

    Ach ja, die Datei Sterne.bmp hab ich mal als Dateianhang beigefügt.
    Gruss Thorsten

  • Hallo AutoBert,

    diesen Beitrag hatte ich mir auch schon angeschaut. Mein Problem daran ist nur, das ich ja keine Datei speichern kann.

    Wenn ich das jetzt richtig verstehe, dann stehen am Ende in der Variable die kompletten Bitmap-Daten. Wie mach ich dann daraus ein Bitmap-Objekt? Darum hab ich den Beitrag auch nicht mehr weiter verfolgt.

    Kannst du mir da mal auf die Sprünge helfen?

    Gruss Thorsten

  • Zitat

    Mein Problem daran ist nur, das ich ja keine Datei speichern kann.

    Wieso? Das wäre doch das einfachste....mit _GDIPlus_BitmapCreateFromFile () hat man dann das Handle. Aber es geht auch anders....

    Zitat

    Wie mach ich dann daraus ein Bitmap-Objekt?

    das ist sehr einfach, du erstellst ein Bitmapobjekt und "kopierst" diese Daten einfach dort hinein....
    Ich hatte dazu folgende Funktion erstellt mit dem Ziel, sämtliche relevanten Zugriffe auf Bitmaps mit einem Schlag "abzuwatschen". Das heisst, ein DeviceContext zum blitten, ein Pointer auf die Bitmapdaten für direkte Manipulation der Farben bzw Pixel, und natürlich das Handle für Bearbeitung per GDI. Mit einfachen Abänderungen kann man auch Monochrom-, 8-Bit, 24-Bit usw Bitmaps erstellen.

    [autoit]

    Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe $HDC und $ptr und handle auf die Bitmapdaten
    $hcdc = _WinAPI_CreateCompatibleDC(0) ;Desktop-Kompatiblen DeviceContext erstellen lassen
    $tBMI = DllStructCreate($tagBITMAPINFO) ;Struktur der Bitmapinfo erstellen und Daten eintragen
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4);Structgröße abzüglich der Daten für die Palette
    DllStructSetData($tBMI, "Width", $iwidth)
    DllStructSetData($tBMI, "Height", -$iheight) ;minus =standard = bottomup
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32) ;32 Bit = 4 Bytes => AABBGGRR
    $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', $DIB_RGB_COLORS, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hbmp = $adib[0] ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden
    $ptr = $adib[4] ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet
    ;_arraydisplay($adib)
    _WinAPI_SelectObject($hcdc, $hbmp) ;objekt hbitmap in DC
    Return $hcdc ;DC der Bitmap zurückgeben
    EndFunc ;==>_CreateNewBmp32

    [/autoit]

    Für dasTopic heisst das, aus den Bitmapdaten der Zwischenablage die relevanten Daten (Höhe, Breite, Bitcount usw) auslesen und damit die "neue" Bitmap erstellen. Dann die Bitmapdaten (die "Pixel") aus der Zwischenablage an die Position des Pointers (Anfang der Bitmapdaten) kopieren, und per Handle weiterverarbeiten.

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (27. April 2010 um 13:15)

  • Hi Andy,

    Danke erst mal für die Infos. Ich hab mal angefangen und versucht die Daten, die von _ClipBoard_GetData($CF_BITMAP) kommen zu untersuchen. Dabei hab ich festgestellt, das die Funktion mit Fehler beendet (@error = -4). Nach genauerer Untersuchung konnte ich feststellen, das ja intern _ClipBoard_GetDataEx aufgerufen und das Ergebnis weiter verarbeitet wird. Und da scheint bei mir das Problem zu liegen:

    Hier mal der Anfang der UDF-Funktion, die ich mit WinAPI_GetLastError erweitert hab (damit du weißt wovon ich spreche):

    [autoit]


    Func _ClipBoard_GetData($iFormat = 1)
    ConsoleWrite('@@ (259) :(' & @MIN & ':' & @SEC & ') _ClipBoard_GetData()' & @CR) ;### Function Trace
    If Not _ClipBoard_IsFormatAvailable($iFormat) Then Return SetError(-1, 0, 0)
    If Not _ClipBoard_Open(0) Then Return SetError(-2, 0, 0)
    Local $hMemory = _ClipBoard_GetDataEx($iFormat)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hMemory = ' & $hMemory & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    ;_ClipBoard_Close(); moved to end: traditionally done *after* copying over the memory
    If $hMemory=0 Then
    _ClipBoard_Close()
    Return SetError(-3, 0, 0)
    EndIf

    [/autoit][autoit][/autoit][autoit]

    Local $aResult = DllCall("kernel32.dll", "ptr", "GlobalLock", "handle", $hMemory)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $aResult[0] = ' & $aResult[0] & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    Local $iLastError = _WinAPI_GetLastError()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iLastError = ' & $iLastError & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    Local $sLastError = _WinAPI_GetLastErrorMessage()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sLastError = ' & $sLastError & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    Local $pMemoryBlock=$aResult[0]

    [/autoit][autoit][/autoit][autoit]

    If $pMemoryBlock=0 Then
    _ClipBoard_Close()
    Return SetError(-4,0,0)
    EndIf

    [/autoit]

    In Zeile 14ff steht ja ursprünglich ein UDF-Aufruf _MemGlobalLock, den hab ich mal direkt in diese Funktion kopiert und um eine Abfrage von WinAPI_GetLastError erweitert.
    Und da kommt dann raus: Fehlercode: 6 => Das Handle ist ungültig.

    Der DLL-Call um einen Pointer zu erhalten schlägt also fehl. Hast du evtl. eine Ahnung warum?

    Was ich bisher vergessen habe zu erwähnen ist: Ich arbeite hier auf Win7Ultimate 64Bit - könnte das die Ursache meiner Probleme sein?

  • Was ich bisher vergessen habe zu erwähnen ist: Ich arbeite hier auf Win7Ultimate 64Bit - könnte das die Ursache meiner Probleme sein?

    also das folgende klappt bei mir XP32 einwandfrei, ich rechtsklicke Autoberts Avatarbild ^^, klicke auf "Bild kopieren", starte das folgende Script und das Bild erscheint in der GUI

    Spoiler anzeigen
    [autoit]

    #include <Clipboard.au3>
    #include <WinAPIError.au3>
    #include <WinAPI.au3>
    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_Startup()
    $a=_ClipBoard_GetDatax(2)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    $hgui=guicreate("")
    guisetstate()
    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hGraphic = ' & $hGraphic & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    $h=_GDIPlus_BitmapCreateFromHBITMAP($a)
    $b=_GDIPlus_GraphicsDrawImage($hGraphic, $h, 0, 0)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $b = ' & $b & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    while sleep(50)
    wend

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _ClipBoard_GetDatax($iFormat = 1)
    ConsoleWrite('@@ (259) :(' & @MIN & ':' & @SEC & ') _ClipBoard_GetData()' & @CR) ;### Function Trace
    If Not _ClipBoard_IsFormatAvailable($iFormat) Then Return SetError(-1, 0, 0)
    If Not _ClipBoard_Open(0) Then Return SetError(-2, 0, 0)
    Local $hMemory = _ClipBoard_GetDataEx($iFormat)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hMemory = ' & $hMemory & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    return $hMemory
    ;_ClipBoard_Close(); moved to end: traditionally done *after* copying over the memory
    If $hMemory=0 Then
    _ClipBoard_Close()
    Return SetError(-3, 0, 0)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Local $aResult = DllCall("kernel32.dll", "ptr", "GlobalLock", "handle", $hMemory)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $aResult[0] = ' & $aResult[0] & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    Local $iLastError = _WinAPI_GetLastError()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iLastError = ' & $iLastError & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    Local $sLastError = _WinAPI_GetLastErrorMessage()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sLastError = ' & $sLastError & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    Local $pMemoryBlock=$aResult[0]
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $pMemoryBlock = ' & $pMemoryBlock & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    If $pMemoryBlock=0 Then
    _ClipBoard_Close()
    Return SetError(-4,0,0)
    EndIf
    return $pMemoryBlock
    endfunc

    [/autoit]
  • kann das nur bestätigen: es läuft!
    EDIT\\
    code etwas geändert (dann kann man es leichter schließen =) )

    Spoiler anzeigen
    [autoit]

    #include <Clipboard.au3>
    #include <WinAPIError.au3>
    #include <WinAPI.au3>
    #include <GDIPlus.au3>
    #include <GUIConstantsEx.au3>
    #include <WindowsConstants.au3>
    _GDIPlus_Startup()
    $a=_ClipBoard_GetDatax(2)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    $hgui=guicreate("")
    guisetstate()
    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hGraphic = ' & $hGraphic & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    $h=_GDIPlus_BitmapCreateFromHBITMAP($a)
    $b=_GDIPlus_GraphicsDrawImage($hGraphic, $h, 0, 0)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $b = ' & $b & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
    Case $GUI_EVENT_CLOSE
    Exit
    EndSwitch
    WEnd
    Func _ClipBoard_GetDatax($iFormat = 1)
    ConsoleWrite('@@ (259) :(' & @MIN & ':' & @SEC & ') _ClipBoard_GetData()' & @CR) ;### Function Trace
    If Not _ClipBoard_IsFormatAvailable($iFormat) Then Return SetError(-1, 0, 0)
    If Not _ClipBoard_Open(0) Then Return SetError(-2, 0, 0)
    Local $hMemory = _ClipBoard_GetDataEx($iFormat)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hMemory = ' & $hMemory & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    return $hMemory
    ;_ClipBoard_Close(); moved to end: traditionally done *after* copying over the memory
    If $hMemory=0 Then
    _ClipBoard_Close()
    Return SetError(-3, 0, 0)
    EndIf
    Local $aResult = DllCall("kernel32.dll", "ptr", "GlobalLock", "handle", $hMemory)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $aResult[0] = ' & $aResult[0] & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    Local $iLastError = _WinAPI_GetLastError()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iLastError = ' & $iLastError & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    Local $sLastError = _WinAPI_GetLastErrorMessage()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sLastError = ' & $sLastError & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    Local $pMemoryBlock=$aResult[0]
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $pMemoryBlock = ' & $pMemoryBlock & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    If $pMemoryBlock=0 Then
    _ClipBoard_Close()
    Return SetError(-4,0,0)
    EndIf
    return $pMemoryBlock
    endfunc

    [/autoit]

    Einmal editiert, zuletzt von Alizame (29. April 2010 um 16:24)

  • Hi Andy!

    Also das funktioniert jetzt bei mir auch... will erst mal garnicht wissen warum :)

    Jedenfalls vielen Dank für die funktionierende Funktion, damit kann ich jetzt endlich Teil 2 meines Probs angehen, Pixel und Linien in das Bild zu malen und Pixel auszulesen...

    Aber das bekomme ich schon irgendwie hin..... das hat ja vorher auch schon zum Teil funktioniert. :rolleyes:

    Ansonsten gibt es hier ja noch ein Super Forum :rock: , wo einem wirklich gut geholfen wird. Vielen Dank noch mal allen, die mich hier bei unterstützt haben. Danke schön.

    Btw: Wie bekommt man diese platzsparenden Spoiler hin? Ist das kompliziert?