Zurück zur Homepage

Bilderschau, Skalieren von Bildern

Eine Bilderschau zu implementieren, ist eigentlich recht einfach. Man liest ein Verzeichnis samt seinen Unterverzeichnissen aus und zeigt die darin gefundenen Bilddateien auf einem Tabellenblatt an. Zu jedem Bild wird noch der Pfad ausgegeben, am besten als Hyperlink, damit man dieses Bild auch direkt öffnen kann. In einem der vorherigen Beispiele wurde gezeigt, wie sich so etwas mit Kommentaren verwirklichen lässt, wobei als Kommentarhintergrund das Bild benutzt wird.

Bilderschau #2

Das funktioniert auch recht gut, bei vielen, größeren Bildern wird aber rasch der Speicherplatz knapp, bzw. bläht sich die Dateigröße enorm auf. Das liegt daran, dass die Bilder als Objekte vom Typ StdPicture (IPictureDisp) irgendwo in der Arbeitsmappe gespeichert werden müssen und bei der geöffneten Mappe in den Arbeitsspeicher geladen werden. Wenn man jetzt noch weiß, dass diese Bilder intern immer als Bitmaps in der Originalgröße vorliegen, egal welches platzsparende Bildformat als Quelldatei benutzt wurde oder wie groß das Bild nachher in der Mappe dargestellt wird, kann man vielleicht den tatsächlichen Speicherbedarf ermessen.

Dem kann man abhelfen, indem man die Bildgröße verringert, in der Originalgröße werden die Bilder ja auch in den wenigsten Fällen angezeigt. Eine Halbierung der Größe verringert dann den Speicherbedarf auf ein Viertel, bei einer Skalierung von 10:1 beträgt der Speicherbedarf sogar nur noch ein Hundertstel des ursprünglichen. Nun wäre es aber zu beschwerlich, das Skalieren  per Hand mit einem Bildbearbeitungsprogramm durchzuführen. Auch das Benutzen des fremden Programmes mittels OLE setzt voraus, dass dieses auf allen Rechnern, auf denen eine Bilderschau erstellt werden soll, auch korrekt installiert ist.

Setzt man ein paar API-Funktionen ein, lässt sich das gleiche aber auch mit Bordmitteln verwirklichen. Der Vorteil dabei ist der, dass die verwendeten API-Funktionen fest in den Betriebssystemen von Microsoft verankert sind und somit ohne eine zusätzliche Installation auskommen. Den meisten API-Funktionen ist es auch egal, ob das installierte System Windows 95, Windows 2000, Windows NT, Windows XP oder Vista heißt.

Auch bei diesem Beispiel gilt, dass man den Code frei benutzen kann. Eine Veröffentlichung des Codes oder Teilen davon, womöglich noch unter anderem Namen, sollte aber unterbleiben.

Excel-Dateien zum Download ca. 700 KB: Vorschau.xlsm oder Vorschau.xls

Das Modul mdlPictureShow

Beginnen wir mit dem etwas einfacheren Teil, nämlich dem Auslesen und eigentlichen Darstellen der Bilder eines Verzeichnisses.

Option Explicit
Private Declare Function GetShortPathName _
   Lib "kernel32" Alias "GetShortPathNameA" ( _
   ByVal lpszLongPath As String, _
   ByVal lpszShortPath As String, _
   ByVal lBuffer As Long _
   ) As Long
   
Private Const BIF_RETURNONLYFSDIRS     As Long = &H1
Private Const BIF_DONTGOBELOWDOMAIN    As Long = &H2
Private Const BIF_STATUSTEXT           As Long = &H4
Private Const BIF_RETURNFSANCESTORS    As Long = &H8
Private Const BIF_EDITBOX              As Long = &H10
Private Const BIF_VALIDATE             As Long = &H20
Private Const BIF_NEWDIALOGSTYLE       As Long = &H40
Private Const BIF_BROWSEINCLUDEURLS    As Long = &H80
Private Const BIF_BROWSEFORCOMPUTER    As Long = &H1000
Private Const BIF_BROWSEFORPRINTER     As Long = &H2000
Private Const BIF_BROWSEINCLUDEFILES   As Long = &H4000
Private Const BIF_SHAREABLE            As Long = &H8000
Private Const BIF_SHOWALLOBJECTS       As Long = &H8
'Arbeitsplatz
Public Const ssfDRIVES                 As Long = &H11
   
Public Sub PictureShow()
   Dim strPath    As String
   Dim strFile    As String
   Dim lngRow     As Long
   Dim dblRate    As Double
   Dim objComment As Comment
   Dim colFiles   As New Collection
   Dim varFiles   As Variant
   Dim ShortPath  As String
   Dim objScale   As New clsPicScale
   Dim objPic     As IPictureDisp
   Dim lngHeight  As Long
   Dim lngWidth   As Long
   Dim strTemp    As String
   
   lngHeight = 75
   
   strTemp = Environ("Temp") & "\aaaaaaa.bmp"
   
   ' Ordner über Dialog wählen
   strPath = ShellGetFolder()
   
   ' Verlassen, wenn nichts gewählt
   If strPath = "" Then Exit Sub
   
   ' Nach .JPG Files suchen
   myFilesearch strPath, colFiles, "jpg"
   
   With Worksheets("Bilderschau")
   
      ' Altes im Blatt Löschen
      .Range("A4:D65535").Clear
      
      lngRow = 3
      
      Application.ScreenUpdating = False
      
      ' Alle .JPG Files nacheinander durchlaufen
      For Each varFiles In colFiles
         
         ' Bild kleiner machen
         With objScale
            .Height = lngHeight
            .Picture = LoadPicture(CStr(varFiles))
            Set objPic = .ScalePicture
            lngWidth = .Width
         End With
         
         ' Ev. vorhandenes Bild im Temp-Verzeichnis löschen
         If Dir(strTemp) <> "" Then Kill strTemp
         
         ' Eingedampftes Bild im Temp-Verzeichnis speichern
         SavePicture objPic, strTemp
         
         If lngWidth <> 0 Then
         
            ' Kurzen Dateipfad ermitteln
            ShortPath = String(251, 0)
            GetShortPathName varFiles, ShortPath, 250
            ShortPath = _
               Left(ShortPath, InStr(1, ShortPath, Chr(0)) - 1)
            
            ' Dateiname und Pfad eintragen
            lngRow = lngRow + 1
            .Cells(lngRow, 1) = Dir(varFiles)
            .Cells(lngRow, 2) = varFiles
            
            dblRate = lngWidth / lngHeight
            
            ' Einen Hyperlink auf die Datei setzen
            ActiveSheet.Hyperlinks.Add Anchor:=.Cells(lngRow, 1), _
               Address:=ShortPath
               
            ' Kommentar einfügen
            Set objComment = .Cells(lngRow, 1).AddComment
            
            ' In den Kommentar das Bild in der Höhe 100
            ' maßstabsgerecht einfügen
            With objComment
               .Shape.Fill.UserPicture strTemp
               .Shape.Height = 100
               .Shape.Width = .Shape.Height * dblRate
            End With
            
         End If
         
      Next
      
   End With
   
   Application.ScreenUpdating = True
   
End Sub


 Private Sub myFilesearch( _
   ByVal strStart As String, _
   ByRef colList As Collection, _
   Optional ByVal strFilter As String)
   
   Dim astrFolder()  As String
   Dim i             As Long
   Dim strCurFolder  As String
   Dim strFile       As String

   'Erst einmal 100 Unterverzeichnisse annehmen
   ReDim astrFolder(1 To 100)
   
   If Left(strFilter, 1) <> "*" Then strFilter = "*" & strFilter
   
   If Right$(strStart, 1) <> "\" Then
   
       'Nachschauen, ob übergebener Pfad auch einen
       'Backslash enthält.
Wenn nicht, dann anhängen
       strStart = strStart & "\"
       
   End If
   
   strCurFolder = strStart
   
   ' Alle Dateien liefern
   strStart = strStart & "*"
   
   ' Suche mit Dir initialisieren
   strFile = Dir(strStart, vbSystem Or _
      vbHidden Or vbDirectory Or vbNormal)
      
   Do While strFile <> ""
   ' Do lange durchlaufen, wie
   ' durch Dir etwas geliefert wird
   
       If GetAttr(strCurFolder & strFile) And vbDirectory Then
       'wenn Datei ein Verzeichnis ist
           If Right$(strFile, 1) <> "." Then
               ' und zwar ein untergeordnetes,
               
               i = i + 1
               
               If i > UBound(astrFolder) Then
               
                  'Wenn Array zu klein ist, anpassen
                   ReDim Preserve astrFolder(1 To i + 1)
                   
               End If
               
               'dann ein Array mit Verzeichnissen füllen.
               astrFolder(i) = strFile
               
           End If
      Else
      'Handelt es sich um eine Datei,
      
         If LCase(strFile) Like LCase(strFilter) Then
         'und entspricht sie noch den strFilterbedingungen,
         
            'dann den Pfad an die Collection colList hängen.
            colList.Add strCurFolder & strFile, _
               strCurFolder & strFile
            
         End If
      End If
      strFile = Dir$()
   Loop
   
   ' Keine Unterverzeichnisse vorhanden, dann beenden
   If i = 0 Then Exit Sub
   
   ' Array anpassen
   ReDim Preserve astrFolder(1 To i)
   
   'Jetzt erst werden die Unterverzeichnisse abgearbeitet,
   'weil Dir$ mit Rekursionen nicht klarkommt.
   For i = 1 To UBound(astrFolder)
   
      'Jetzt ruft sich diese Prozedur noch einmal auf.
      
myFilesearch strCurFolder & astrFolder(i), colList, strFilter
      
   Next
   
End Sub


Public Function ShellGetFolder( _
   Optional Start As Variant = ssfDRIVES, _
   Optional Caption As String = "Browse Folder")
   On Error Resume Next
   
   Dim objShell      As Object
   Dim objBrowse     As Object
   Dim lngOptions    As Long
   
   ' Eigenschaften des Dialoges setzen
   lngOptions = BIF_RETURNONLYFSDIRS Or _
                BIF_EDITBOX Or _
                BIF_VALIDATE Or _
                BIF_SHOWALLOBJECTS Or _
                BIF_NEWDIALOGSTYLE Or _
                BIF_STATUSTEXT Or _
                BIF_SHOWALLOBJECTS
   
   Set objShell = CreateObject("Shell.Application")
   If IsNumeric(Start) Then
      ' Anfangspfad als Konstante
      Set objBrowse = objShell.BrowseForFolder( _
         &H0, Caption, lngOptions, CLng(Start))
   Else
      ' Anfangspfad als String
      Set objBrowse = objShell.BrowseForFolder( _
         &H0, Caption, lngOptions, Start & Chr(0))
   End If
   
   ' Dialog starten und Pfad zurückgeben
   ShellGetFolder = objBrowse.ParentFolder.ParseName( _
      objBrowse.Title).Path

End Function

PictureShow

Die Prozedur PictureShow ermittelt über die Umgebungsvariable Temp den Pfad zu den temporären Dateien des angemeldeten Benutzers und speichert diesen in einer Variablen. Anschließend wird die Funktion ShellGetFolder aufgerufen, um das Anfangsverzeichnis zu erfragen, ab dem die Suche nach Bilddateien beginnen soll. Mit der aufgerufenen Prozedur myFileSearch wird die übergebene Collection colFiles mit den Pfaden der gefundenen Bilddateien gefüllt. Der Anfangspfad wird als erstes, die Collection als zweites und der Dateifilter als drittes Argument übergeben.

Anschließend wird der Zielbereich im Tabellenblatt Bilderschau gelöscht und nacheinander jedes Element der Collection mit den Bilderpfaden durchlaufen. Die Klasse clsPicScale übernimmt die gewünschte Zielhöhe in der Property (Eigenschaft) Height. Das zu skalierende Bild, welches mit LoadPicture geladen wird, übergibt man an die Eigenschaft Picture und erhält über die Funktion ScalePicture das skalierte Bild als Objekt zurück. Die Breite des skalierten Bildes kann man über die Eigenschaft Width auslesen.

Da mir keine Möglichkeit bekannt ist, einem Kommentarhintergrund direkt ein Bild vom Typ StdPicture (IPictureDisp) zuzuweisen, muss man den Weg über das Temp-Verzeichnis gehen, dort hinein wird erst einmal das skalierte Bild kopiert. Nun kann man einen Kommentar einfügen und diesem anschließend über die Fill.UserPicture-Eigenschaft das temporär gespeicherte Bild zuweisen. In die gleiche Zelle wird anschließend der Dateiname und ein Hyperlink auf die Originaldatei eingefügt, die rechts daneben liegende Zelle nimmt als Text den kompletten Pfad auf.

myFilesearch

In der Prozedur myFilesearch wird die Suche mit Dir so initialisiert, dass alle Dateien inklusive der Verzeichnisse eines im Prozedurkopf übergebenen Verzeichnisses ausgelesen werden. Zu den Dateien, die anschließend geliefert werden, gehört unter anderen auch eine Datei, welche das übergeordnete Verzeichnis und eine andere, welche das gleiche Verzeichnis repräsentiert. Eine Datei mit dem Namen “.“ Punkt steht dabei für das gleiche- und die mit Doppelpunkt “..“ für das übergeordnete Verzeichnis.

Möchte man Unterverzeichnisse mit in die Suche einbeziehen, hat man aber ein Problem. Dir lässt sich nämlich nicht rekursiv einsetzen, da man keine neue Suche starten kann, ohne die alten Einstellungen zu überschreiben. Ein verändertes Suchmuster, beispielsweise durch die Angabe eines neuen Pfades, beginnt eine neue Suche. Man hat keine Möglichkeit mehr, die vorherige Suche an der Position fortzusetzen, an der man mit den neuen Parametern begonnen hat.

Um dem abzuhelfen, werden gefundene Unterverzeichnisse in einem Array zwischengespeichert, Dateien, die dem Suchkriterium entsprechen, werden sofort in einer im Prozedurkopf übergebenen Collection gespeichert. Nachdem die Suche nach Dateien in dieser Verzeichnisebene beendet ist, wird für jedes im angesprochenen Array  gespeicherte Unterverzeichnis die gleiche Prozedur noch einmal aufgerufen, aber mit dem um das Unterverzeichnis erweiterten Suchpfad. Die einmal aufgerufene Prozedur wird erst dann beendet, wenn alle ihre Unterverzeichnisse abgearbeitet sind.

ShellGetFolder

Die Windows Shell bietet einen Dialog zur Verzeichnisauswahl, dieser wird in der Funktion ShellGetFolder benutzt. Zum Festlegen eines Pfades kann man einen vordefinierten oder einen benutzerdefinierten benutzen. Die vorgegebenen Pfade werden als numerische Longwerte übergeben und stecken in Konstanten mit dem Präfix (Vorsilbe) ssf. Benutzerdefinierte Pfade werden als String übergeben.

Die Klasse clsPicScale

Mit Hilfe dieser Klasse wird das übergebene Bild skaliert und als neues Objekt vom Typ StdPicture (IPictureDisp) zurückgegeben.

Option Explicit
Private Type POINTAPI
   x As Long
   y As Long
End Type
Private Type BITMAP
   bmType As Long
   bmWidth As Long
   bmHeight As Long
   bmWidthBytes As Long
   bmPlanes As Integer
   bmBitsPixel As Integer
   bmBits As Long
End Type
Private Type GUID
  Data1 As Long
  Data2 As Integer
  Data3 As Integer
  Data4(7) As Byte
End Type
Private Type PICTDESC
   cbSize As Long
   picType As Long
   hImage As Long
   Data1 As Long
   Data2 As Long
End Type
Private Declare Function SetStretchBltMode _
   Lib "gdi32" ( _
   ByVal hdc As Long, _
   ByVal nStretchMode As Long _
   ) As Long
Private Declare Function StretchBlt _
   Lib "gdi32.dll" ( _
   ByVal hdc As Long, _
   ByVal x As LongByVal y As Long, _
   ByVal nWidth As LongByVal nHeight As Long, _
   ByVal hSrcDC As Long, _
   ByVal xSrc As LongByVal ySrc As Long, _
   ByVal nSrcWidth As LongByVal nSrcHeight As Long, _
   ByVal dwRop As Long _
   ) As Long
Private Declare Function OleCreatePictureIndirect _
   Lib "olepro32.dll" ( _
   pPictDesc As PICTDESC, _
   RefIID As GUID, _
   ByVal fPictureOwnsHandle As Long, _
   ppvObj As IPicture _
   ) As Long
Private Declare Function DeleteDC _
   Lib "gdi32" ( _
   ByVal hdc As Long _
   ) As Long
Private Declare Function CreateCompatibleDC _
   Lib "gdi32" ( _
   ByVal hdc As Long _
   ) As Long
Private Declare Function SelectObject _
   Lib "gdi32" ( _
   ByVal hdc As Long, _
   ByVal hObject As Long _
   ) As Long
Private Declare Function CreateCompatibleBitmap _
   Lib "gdi32" ( _
   ByVal hdc As Long, _
   ByVal nWidth As Long, _
   ByVal nHeight As Long _
   ) As Long
Private Declare Function GetObjectAPI _
   Lib "gdi32" Alias "GetObjectA" ( _
   ByVal hObject As Long, _
   ByVal nCount As Long, _
   lpObject As Any _
   ) As Long
Private Const SRCCOPY            As Long = &HCC0020
Private Const vbPicTypeBitmap    As Long = 1
Private Const cspHalftone        As Long = 4

Private mobjPicture              As IPictureDisp
Private mlngHeight               As Long
Private mlngWidth                As Long

Public Function ScalePicture() As IPictureDisp
   Dim objPic              As IPictureDisp
   Dim x                   As Long
   Dim y                   As Long
   Dim lngSourceDC         As Long
   Dim lngDestDC           As Long
   Dim udtBMP              As BITMAP
   Dim lngObjOldSource     As Long
   Dim lngObjOldDest       As Long
   Dim lngDestBMP          As Long
   Dim udtPicdesc          As PICTDESC
   Dim IID_IDispatch       As GUID
   Dim hImage              As Long
   Dim lngRet              As Long

   If mobjPicture Is Nothing Then
      MsgBox "Kein gültiges Bild!"
      Exit Function
   End If

   ' DummyDC zum Blitten vom Quellbild erzeugen
   lngSourceDC = CreateCompatibleDC(0&)

   ' Bitmapstruktur des Bildes ausfüllen lassen, damit
   ' Größe ausgelesen werden kann
   GetObjectAPI mobjPicture.Handle, Len(udtBMP), udtBMP

   ' Größe auslesen
   x = udtBMP.bmWidth
   y = udtBMP.bmHeight

   ' Bitmap ins QuellDC stellen, altes Objekt merken
   lngObjOldSource = SelectObject(lngSourceDC, mobjPicture.Handle)

   ' Zielgröße der Bitmap ermitteln
   If mlngHeight > 0 Then
      mlngWidth = x * (mlngHeight / y)
   Else
      mlngHeight = y * (mlngWidth / x)
   End If

   ' Bitmap in der gewünschten Größe erzeugen
   lngDestBMP = CreateCompatibleBitmap( _
      lngSourceDC, mlngWidth, mlngHeight)

   ' DummyDC zum Blitten ins Zielbild erzeugen
   lngDestDC = CreateCompatibleDC(0&)

   ' Bitmap ins ZielDC stellen, altes Objekt merken
   lngObjOldDest = SelectObject(lngDestDC, lngDestBMP)

   ' Qualität erhöhen
   SetStretchBltMode lngDestDC, cspHalftone

   ' Bild in den ZielDC blitten
   StretchBlt lngDestDC, 0, 0, mlngWidth, mlngHeight, _
      lngSourceDC, 0, 0, x, y, SRCCOPY

   ' Schnittstellenkennung kPictureIID (GUID)
   With IID_IDispatch
      .Data1 = &H20400
      .Data4(0) = &HC0
      .Data4(7) = &H46
   End With

   With udtPicdesc
      ' Struktur Picdesc ausfüllen
      .cbSize = Len(udtPicdesc)
      .picType = vbPicTypeBitmap
      .hImage = lngDestBMP
    End With

   ' Picture-Objekt erzeugen
   lngRet = OleCreatePictureIndirect( _
      udtPicdesc, IID_IDispatch, 1&, objPic)

   ' Picture-Objekt zurückgeben
   If lngRet = 0 Then Set ScalePicture = objPic

   SelectObject lngSourceDC, lngObjOldSource
   DeleteDC lngSourceDC

   SelectObject lngDestDC, lngObjOldDest
   DeleteDC lngDestDC
End Function

Public Property Let Picture(ByVal vNewValue As IPictureDisp)
   Set mobjPicture = vNewValue
End Property

Public Property Let Height(ByVal vNewValue As Long)
   mlngHeight = vNewValue
   mlngWidth = 0
End Property
Public Property Get Height() As Long
   Height = mlngHeight
End Property

Public Property Let Width(ByVal vNewValue As Long)
   mlngHeight = 0
   mlngWidth = vNewValue
End Property
Public Property Get Width() As Long
   Width = mlngWidth
End Property

Private Sub Class_Initialize()
   mlngHeight = 100
End Sub

Picture-Eigenschaft

An diese Eigenschaft wird ein Bild vom Typ StdPicture (IPictureDisp) übergeben. Solch ein Bild kann mit LoadPicture direkt aus einer Datei erzeugt werden, aber auch Bilder im Tabellenblatt oder Bildhintergründe von Steuerelementen sind eine mögliche Quelle.

Width/Height-Eigenschaft

Mit diesen Eigenschaften kann man die Zielgröße eines Bildes festlegen. Dabei wird in den Let-Prozeduren entweder die gewünschte Höhe oder die gewünschte Breite angegeben. Der jeweils andere Wert wird beim Skalieren berechnet und zwar so, dass das Seitenverhältnis des Originalbildes beibehalten wird. Nachdem ein Bild skaliert wurde, kann man den bis dahin unbekannten Wert über die Get-Prozeduren auslesen.

OriginHeight/OriginWidth

Diese zwei Eigenschaften liefern die Abmessungen des Originalbildes in Pixeln. Dazu wird mit GetObjectApi die Bitmapstruktur udtBMP ausgefüllt und von dort die entsprechenden Werte ausgelesen.

ScalePicture

Die Funktion ScalePicture liefert ein Bild vom Typ StdPicture (IPictureDisp), sofern ein Bild dieses Typs an die Klasse übergeben wurde.

Dazu wird zunächst einmal ein DC (Gerätekontext) angelegt, der das Quellbild aufnehmen soll. Solch ein DC ist im Prinzip eine Zeichenfläche, auf der grafische Operationen ausgeführt werden können. Da man nicht unbedingt mit dem Gerätekontext eines Druckers oder eines ähnlichen grafischen Gerätes arbeiten möchte, erzeugt man mit CreateCompatibleDC einen DC, der kompatibel zum Bildschirm ist. Darauf werden im Allgemeinen ja auch die Bilder ausgegeben. Besitzt man nun einen solchen DC, stellt man mit der API SelectObject das Originalbild dort hinein. An diese API wird das Handle des DCs und das Handle des Quellbildes (mobjPicture.Handle) übergeben. Zuvor lässt man sich noch mit Hilfe der API GetObjectAPI die Struktur BITMAP ausfüllen, die anschließend verschiedene Informationen, wie beispielsweise die Abmessungen und Farbtiefe des Bildes enthält.

Danach erzeugt man mit der API CreateCompatibleBitmap eine leere, aber mit dem Quellbild kompatible Bitmap in den gewünschten Abmessungen des skalierten Bildes. Dieses vorerst leere Bild wird in einen neu erzeugten DC gestellt. Mit der API StretchBlt wird nun das Quellbild aus dem Quell-DC in das Ziel-DC “geblittet“ und zwar so, dass es das Zielbild vollkommen ausfüllt. Das heißt, an diese Funktion muss man die linke obere Position, sowie die Breite und Höhe des Quell- bzw. Zielrechtecks übergeben. Das letzte Argument legt fest, auf welche Art die einzelnen Pixel im Zielbereich mit den eventuell dort vorhandenen kombiniert werden, in diesem Fall wird aber ein einfaches Überschreiben verwendet.

Um die Qualität zu verbessern und sehr unschöne Farbfehler zu vermeiden, setzt man zuvor mit der API SetStretchBltMode den Blittingmodus auf cspHalftone, was aber anschließend das Blitten etwas verlangsamt. Nun muss man nur noch aus dem Zielbitmap ein Objekt vom Typ StdPicture (IPictureDisp) machen. Der API-Funktion OleCreatePictureIndirect übergibt man dazu den ausgefüllten Datentyp PICTDESC. Dort werden das Bitmaphandle und der Typ der Grafik eingetragen. Außerdem wird noch die Schnittstellenkennung in Form des ausgefüllten Datentyps GUID benötigt, die als Variable mit dem Namen IID_IDispatch übergeben wird. Die Objektvariable objPic vom Typ IPictureDisp nimmt bei Erfolg das Bild auf und wird als Funktionsergebnis zurückgegeben.

Als Aufräumarbeit müssen anschließend die Objekte, die vor dem ersten Aufruf von SelectObjekt im jeweiligen DC gewesen sind, auch wieder dorthin zurückbefördert werden. Ist das mit Hilfe der API SelectObject geschehen, kann man mit DeleteDC die erzeugten DCs löschen.