Zurück zur Homepage

Twain-Schnittstelle

Twain ist ein Standard zum Austausch von Daten zwischen Bildeingabegeräten und Programmen.

Die Bezeichnung ist lt. Wiki eine Abkürzung für Toolkit (Technology) Without An (Any) Important (Interesting), zu Deutsch etwa Werkzeugsatz ohne einen wichtigen Namen, es wird dort aber auch Transmit Windows Advanced Interface genannt. Wie man sieht, gibt es keine offizielle Erklärung, was der Name überhaupt aussagen soll. Wichtiger als das ist aber, dass man mit Twain recht einfach an Daten von Kameras und Scannern kommt, wobei der Begriff “Einfach“ natürlich auch relativ ist.

Twain

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. 144 KB: Twain.xlsm oder Twain.xls

Das Klassenmodul des Tabellenblatts Twain

Ein Klick auf die Schaltfläche cmdGetTwainData oder cmdSaveTwain löst die entsprechende Ereignisprozedur aus. Die beiden Ereignisprozeduren unterscheiden sich nur durch die letzte Zeile, in der einen wird dem Steuerelement Image1 das gelieferte Bild zugewiesen, in der anderen wird das Bild abgespeichert. Deshalb wird auch nur eine der beiden Prozeduren erklärt.

Option Explicit

Private Sub cmdGetTwainData_Click()
   Dim objTwain   As New clsTwain
   Dim objPic     As IPictureDisp
   On Error Resume Next
   
   With objTwain
      .Left = Me.Range("B11")
      .Top = Me.Range("B12")
      .Right = Me.Range("B13")
      .Bottom = Me.Range("B14")
      .ResolutionDPI = Me.Range("B15")
      Select Case Me.Range("B16")
         Case 1
            .ColorTypeSetRGB
         Case 2
            .ColorTypeSetGrey
         Case 3
            .ColorTypeSetBlackWhite
      End Select
      .Manufacturer = Me.Range("B18")
      .ProductFamily = Me.Range("B19")
      .ProductName = Me.Range("B20")
      .TimeoutSec = Me.Range("B21")
      .HideDialog = Not Me.OLEObjects("chkDialog").Object.Value
      .SetSettings = Me.OLEObjects("chkSettings").Object.Value
       Set objPic = .GetTwainPicture()
   End With
   Me.OLEObjects("Image1").Object.Picture = objPic

End Sub

Private Sub cmdSaveTwain_Click()
   Dim objTwain   As New clsTwain
   Dim objPic     As IPictureDisp
   On Error Resume Next
   With objTwain
      .Left = Me.Range("B11")
      .Top = Me.Range("B12")
      .Right = Me.Range("B13")
      .Bottom = Me.Range("B14")
      .ResolutionDPI = Me.Range("B15")
      Select Case Me.Range("B16")
         Case 1
            .ColorTypeSetRGB
         Case 2
            .ColorTypeSetGrey
         Case 3
            .ColorTypeSetBlackWhite
      End Select
      .Manufacturer = Me.Range("B18")
      .ProductFamily = Me.Range("B19")
      .ProductName = Me.Range("B20")
      .TimeoutSec = Me.Range("B21")
      .HideDialog = Not Me.OLEObjects("chkDialog").Object.Value
      .SetSettings = Me.OLEObjects("chkSettings").Object.Value
      .SaveTwainPicture Me.Range("B10")
   End With

End Sub

Private Sub cmdSource_Click()
   Dim objTwain   As New clsTwain
   objTwain.ShowSourceDialog
End Sub

Die Ereignisprozeduren cmdGetTwainData_Click und cmdSaveTwain_Click

Die Prozedur cmdGetTwainData entnimmt einige Einstellungen dem Tabellenblatt und setzt die Eigenschaften der Klasse clsTwain. Die Eigenschaft GetTwainPicture liefert das Bild und setzt die Picture-Eigenschaft des Bildsteuerelements, die Methode SaveTwainPicture speichert unter dem übergebenen Pfad.

Die Ereignisprozedur cmdSource_Click

Die Prozedur cmdSource_Click öffnet den Dialog zur Auswahl einer Twain-Quelle, indem die Methode ShowSourceDialog der Klasse clsTwain gesetzt wird.

Die Klasse clsTwain

Die Klasse clsTwain ist dafür da, den Zugriff auf die Twainschnittstelle zu kapseln. Dadurch erhält man ein Objekt, welches man über das einfache Setzen und Auslesen von Eigenschaften, sowie durch den Aufruf von Methoden steuern kann, ohne sich um die Einzelheiten kümmern zu müssen. Diese Einzelheiten sind nicht immer leicht zu durchschauen, da die Funktion DSM_Entry nicht für die Programmiersprache VB(A) geschrieben wurde, auch die verwendeten Datentypen passen nicht unbedingt harmonisch zusammen.

Ein großer Teil des Codes besteht aus dem Definieren von Konstanten und benutzerdefinierten Typen, der Deklaration von Klassenweit gültigen Variablen und dem Bekanntmachen von API-Funktionen über die Declare-Anweisung.

Die Twainfunktion DSM_Entry

Die API-Funktion DSM_Entry ist die Schlüsselfunktion und steckt in der Bibliothek Twain_32.dll. Über diese Funktion laufen alle Aufträge, wobei der fünfte Parameter (MSG) den Auftrag beschreibt, der sechste die Daten dazu liefert oder zurückgibt. Die ersten zwei Parameter sind vom Typ TW_IDENTITY (enthält auch Daten vom Typ TW_VERSION). Was das für Daten im Einzelnen sind, kann man den Typdeklarationen entnehmen.

Option Explicit
'################################################
'### Klassenweit gültige Datentypen ###
'### Siehe TWAIN.h (TWAIN Working Group) ###
'################################################
Private Type POINTAPI
   x As Long
   y As Long
End Type
Private Type TW_VERSION
   MajorNum                As Integer
   MinorNum                As Integer
   Language                As Integer 'TWLG_ Konstanten
   Country                 As Integer 'TWCY_ Konstanten
   Info(1 To 34)           As Byte 'Text
End Type
Private Type TW_IDENTITY
   Id                      As Long
   Version                 As TW_VERSION
   ProtocolMajor           As Integer 'TWON_PROTOCOLMAJOR 1
   ProtocolMinor           As Integer 'TWON_PROTOCOLMINOR 9
   SupportedGroups1        As Integer 'DG_ Konstanten
   SupportedGroups2        As Integer
   Manufacturer(1 To 34)   As Byte 'Text
   ProductFamily(1 To 34)  As Byte 'Text
   ProductName(1 To 34)    As Byte 'Text
End Type
Private Type TW_USERINTERFACE
   ShowUI   As Integer
   ModalUI  As Integer 'For Mac only
   hParent  As Long
End Type
Private Type TW_PENDINGXFERS
   Count       As Integer
   Reserved1   As Integer
   Reserved2   As Integer
End Type
Private Type TW_ONEVALUE
   ItemType As Integer
   Item1    As Integer
   Item2    As Integer
End Type
Private Type TW_CAPABILITY
   Cap          As Integer
   ConType      As Integer
   hContainer   As Long
End Type
Private Type TW_FIX32
   Whole   As Integer
   Frac    As Integer
End Type
Private Type TW_FRAME
   Left     As TW_FIX32
   Top      As TW_FIX32
   Right    As TW_FIX32
   Bottom   As TW_FIX32
End Type
Private Type TW_IMAGELAYOUT
   Frame            As TW_FRAME
   DocumentNumber   As Long
   PageNumber       As Long
   FrameNumber      As Long
End Type
Private Type TW_EVENT
   pEvent      As Long
   TWMessage   As Integer
End Type
Private Type MSG
   hWnd    As Long
   message As Long
   wParam  As Long
   lParam  As Long
   time    As Long
   pt      As POINTAPI
End Type
'################################################
'### Klassenweit gültige Konstanten ###
'### Siehe TWAIN.h (TWAIN Working Group) ###
'################################################
Private Const DG_CONTROL            As Long = 1
Private Const DG_IMAGE              As Long = 2

Private Const MSG_GET               As Long = 1
Private Const MSG_SET               As Long = 6
Private Const MSG_XFERREADY         As Long = 257
Private Const MSG_CLOSEDSREQ        As Long = 258
Private Const MSG_OPENDSM           As Long = 769
Private Const MSG_CLOSEDSM          As Long = 770
Private Const MSG_OPENDS            As Long = 1025
Private Const MSG_CLOSEDS           As Long = 1026
Private Const MSG_USERSELECT        As Long = 1027
Private Const MSG_DISABLEDS         As Long = 1281
Private Const MSG_ENABLEDS          As Long = 1282
Private Const MSG_PROCESSEVENT      As Long = 1537
Private Const MSG_ENDXFER           As Long = 1793

Private Const DAT_CAPABILITY        As Long = 1
Private Const DAT_EVENT             As Long = 2
Private Const DAT_IDENTITY          As Long = 3
Private Const DAT_PARENT            As Long = 4
Private Const DAT_PENDINGXFERS      As Long = 5
Private Const DAT_USERINTERFACE     As Long = 9
Private Const DAT_IMAGELAYOUT       As Long = 258
Private Const DAT_IMAGENATIVEXFER   As Long = 260

Private Const TWRC_SUCCESS          As Long = 0
Private Const TWRC_CHECKSTATUS      As Long = 2
Private Const TWRC_DSEVENT          As Long = 4
Private Const TWRC_NOTDSEVENT       As Long = 5
Private Const TWRC_XFERDONE         As Long = 6

Private Const TWLG_GER              As Long = 6
Private Const TWCY_GERMANY          As Long = 49
Private Const TWON_PROTOCOLMAJOR    As Long = 1
Private Const TWON_PROTOCOLMINOR    As Long = 9
Private Const TWON_ONEVALUE         As Long = 5

Private Const BW                    As Long = 0
Private Const GREY                  As Long = 1
Private Const RGB                   As Long = 2

Private Const XFERCOUNT             As Long = 1
Private Const PIXELTYPE             As Long = 257
Private Const INDICATORS            As Long = 4107
Private Const UICONTROLLABLE        As Long = 4110
Private Const PHYSICALWIDTH         As Long = 4369
Private Const PHYSICALHEIGHT        As Long = 4370
Private Const XRESOLUTION           As Long = 4376
Private Const YRESOLUTION           As Long = 4377
Private Const BITDEPTH              As Long = 4395

Private Const INT16                 As Long = 1
Private Const UINT16                As Long = 4
Private Const BOOL                  As Long = 6
Private Const FIX32                 As Long = 7
Private Const GHND                  As Long = 66


'################################################
'### API-Funktionen ###
'################################################
Private Declare Function DSM_Entry _
   Lib "Twain_32.dll" ( _
   pOrigin As Any, _
   pDest As Any, _
   ByVal DG As Long, _
   ByVal DAT As Integer, _
   ByVal MSG As Integer, _
   pData As Any _
   ) As Integer
Private Declare Sub CopyMemory _
   Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
   ByVal pDest As Long, _
   ByVal pSource As Long, _
   ByVal Length As Long)
Private Declare Sub ZeroMemory _
   Lib "kernel32.dll" Alias "RtlZeroMemory" ( _
   ByVal pDest As Long, _
   ByVal Length As Long)
Private Declare Function GlobalLock _
   Lib "kernel32.dll" ( _
   ByVal hMem As Long _
   ) As Long
Private Declare Function GlobalUnlock _
   Lib "kernel32.dll" ( _
   ByVal hMem As Long _
   ) As Long
Private Declare Function GlobalAlloc _
   Lib "kernel32.dll" ( _
   ByVal wFlags As Long, _
   ByVal dwBytes As Long _
   ) As Long
Private Declare Function GlobalFree _
   Lib "kernel32.dll" ( _
   ByVal hMem As Long _
   ) As Long
Private Declare Function GetMessage _
   Lib "user32.dll" Alias "GetMessageA" ( _
   lpMsg As MSG, _
   ByVal hWnd As Long, _
   ByVal wMsgFilterMin As Long, _
   ByVal wMsgFilterMax As Long _
   ) As Long
Private Declare Function TranslateMessage _
   Lib "user32.dll" ( _
   lpMsg As MSG _
   ) As Long
Private Declare Function DispatchMessage _
   Lib "user32.dll" Alias "DispatchMessageA" ( _
   lpMsg As MSG _
   ) As Long
Private Declare Function CreateWindowEx _
   Lib "user32.dll" Alias "CreateWindowExA" ( _
   ByVal dwExStyle As Long, _
   ByVal lpClassName As String, _
   ByVal lpWindowName As String, _
   ByVal dwStyle As Long, _
   ByVal x As Long, _
   ByVal y As Long, _
   ByVal nWidth As Long, _
   ByVal nHeight As Long, _
   ByVal hWndParent As Long, _
   ByVal hMenu As Long, _
   ByVal hInstance As Long, _
   ByVal lpParam As Long _
   ) As Long
Private Declare Function DestroyWindow _
   Lib "user32.dll" ( _
   ByVal hWnd As Long _
   ) As Long


'################################################
'### Umwandeln ins Format IPictureDisp ###
'################################################
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 OpenClipboard _
   Lib "user32" ( _
   ByVal hWnd As Long _
   ) As Long
Private Declare Function CloseClipboard _
   Lib "user32" () As Long
Private Declare Function GetClipboardData _
   Lib "user32" ( _
   ByVal wFormat As Long _
   ) As Long
Private Declare Function IsClipboardFormatAvailable _
   Lib "user32" ( _
   ByVal wFormat As Long _
   ) As Long
Private Declare Function EmptyClipboard _
   Lib "user32.dll" () As Long
Private Declare Function SetClipboardData _
   Lib "user32.dll" ( _
   ByVal wFormat As Long, _
   ByVal hMem 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 Const vbPicTypeNone      As Long = 0
Private Const vbPicTypeBitmap    As Long = 1
Private Const vbPicTypeMetafile  As Long = 2
Private Const vbPicTypeIcon      As Long = 3
Private Const vbPicTypeEMetafile As Long = 4
Private Const CF_BITMAP          As Long = 2
Private Const CF_DIB             As Long = &H8
Private Const CF_ENHMETAFILE     As Long = 14

'################################################
'### Klassenweit gültige Variablen ###
'################################################
Private mudtAppID                As TW_IDENTITY
Private mudtScrID                As TW_IDENTITY
Private mlngWindow               As Long
Private mlngColorType            As Long
Private mdblLeft                 As Double
Private mdblTop                  As Double
Private mdblRight                As Double
Private mdblBottom               As Double
Private mlngRes                  As Long
Private mlngTimeoutSec           As Long
Private mstrManufacturer         As String * 34
Private mstrProductFamily        As String * 34
Private mstrProductName          As String * 34
Private mblnSetSettings          As Boolean
Private mblnHideDialog           As Boolean

'################################################
'### Eigenschaften und Methoden der Klasse ###
'################################################
Public Property Let Left(ByVal vNewValue As Double)
   mdblLeft = vNewValue
End Property
Public Property Let Top(ByVal vNewValue As Double)
   mdblTop = vNewValue
End Property
Public Property Let Right(ByVal vNewValue As Double)
   mdblRight = vNewValue
End Property
Public Property Let Bottom(ByVal vNewValue As Double)
   mdblBottom = vNewValue
End Property
Public Property Get Left() As Double
   Left = mdblLeft
End Property
Public Property Get Top() As Double
   Top = mdblTop
End Property
Public Property Get Right() As Double
   Right = mdblRight
End Property
Public Property Get Bottom() As Double
   Bottom = mdblBottom
End Property

Public Property Let Manufacturer(ByVal vNewValue As String)
   mstrManufacturer = vNewValue
End Property
Public Property Get Manufacturer() As String
   Manufacturer = mstrManufacturer
End Property

Public Property Let ProductFamily(ByVal vNewValue As String)
   mstrProductFamily = vNewValue
End Property
Public Property Get ProductFamily() As String
   ProductFamily = mstrProductFamily
End Property

Public Property Let ProductName(ByVal vNewValue As String)
   mstrProductName = vNewValue
End Property
Public Property Get ProductName() As String
   ProductName = mstrProductName
End Property

Public Property Let TimeoutSec(ByVal vNewValue As Long)
   mlngTimeoutSec = vNewValue
End Property
Public Property Get TimeoutSec() As Long
   TimeoutSec = mlngTimeoutSec
End Property

Public Property Let ResolutionDPI(ByVal vNewValue As Long)
   mlngRes = vNewValue
End Property
Public Property Get ResolutionDPI() As Long
   ResolutionDPI = mlngRes
End Property

Public Property Let SetSettings(ByVal vNewValue As Boolean)
   mblnSetSettings = vNewValue
End Property
Public Property Get SetSettings() As Boolean
   SetSettings = mblnSetSettings
End Property

Public Property Let HideDialog(ByVal vNewValue As Boolean)
   mblnHideDialog = vNewValue
End Property
Public Property Get HideDialog() As Boolean
   HideDialog = mblnHideDialog
End Property

Public Sub ColorTypeSetRGB()
   mlngColorType = RGB
End Sub
Public Sub ColorTypeSetBlackWhite()
   mlngColorType = BW
End Sub
Public Sub ColorTypeSetGrey()
   mlngColorType = GREY
End Sub
Public Function GetUserdefinedColorType() As Long
   GetUserdefinedColorType = mlngColorType
End Function

Private Sub Class_Initialize()
   mlngColorType = RGB
   mlngRes = 200
   mlngTimeoutSec = 120
   mstrManufacturer = Environ("Username")
   mstrProductFamily = "First Twain"
   mstrProductName = "Office Twain"
 End Sub
 
Public Function GetTwainPicture(Optional HideDialog As BooleanAs IPictureDisp
   On Error Resume Next
   Set GetTwainPicture = LoadTwainPic()
End Function

Public Function SaveTwainPicture( _
   strDestination As String _
   ) As Boolean
   Dim objPic     As IPictureDisp
   
   On Error Resume Next
   
   Err.Clear
   
   Set objPic = LoadTwainPic()
   
   ' Zieldatei löschen, falls vorhanden
   If Dir(strDestination) <> "" Then Kill strDestination
   
   ' Bild speichern unter
   SavePicture objPic, strDestination
   
   If Err.Number = 0 Then SaveTwainPicture = True
   
End Function

Public Function ShowSourceDialog() As Boolean
   Dim lngRet     As Long
   
   On Error Goto Errorhandler
   
   ' Dialog Manager anzeigen
   DataSourceManagerOpen
   
   ' Dialog überwachen und auswerten
   lngRet = DSM_Entry( _
     mudtAppID, _
     ByVal 0&, _
     DG_CONTROL, _
     DAT_IDENTITY, _
     MSG_USERSELECT, _
     mudtScrID)
   
   ' Dialog Manager schließen
   DataSourceManagerClose
   
   If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
   
   ' Erfolg zurückgeben
   ShowSourceDialog = True
   
   Exit Function
Errorhandler:
End Function

'################################################
'### Interne Funktionen/Prozeduren der Klasse ###
'################################################
Private Function LoadTwainPic() As IPictureDisp
   Dim lngRet        As Long
   Dim blnOpen       As Boolean
   Dim lngDIB        As Long
   ' Öffnet den Datenquellenmanager und die Datenquelle.
Bei Bedarf
   ' werden Voreinstellungen gesetzt, wenn kein Dialog gewünscht ist.
   ' Zurückgegeben wird ein Ole-Bild vom Typ IPictureDisp
   On Error Goto Errorhandler
   
   ' Datenquellenmanager öffnen
   DataSourceManagerOpen
   
   ' Datenquelle öffnen
   DataSourceOpen
   
   ' Voreinstellungen benutzen, wenn gewünscht
   If mblnSetSettings Then SetUserDefinedValues
   
   ' Daten auslesen
   lngDIB = GetTwainData(Not mblnHideDialog)
   
   ' Datenquelle schließen
   DataSourceClose
   
   ' Datenquellenmanager schließen
   DataSourceManagerClose
   
   ' Bild zurückgeben
   Set LoadTwainPic = GetPictureFromDib(lngDIB)
   
   ' Speicher freigeben
   GlobalFree lngDIB
   
   Exit Function
Errorhandler:
   DataSourceClose
   DataSourceManagerClose
   GlobalFree lngDIB
   MsgBox "Fehler beim Auslesen eines Bildes"
End Function

Private Function GetTwainData( _
   blShowUI As Boolean _
   ) As Long
   Dim udtUserinterface    As TW_USERINTERFACE
   Dim udtPending          As TW_PENDINGXFERS
   Dim udtEvent            As TW_EVENT
   Dim udtMSG              As MSG
   Dim lngDIB              As Long
   Dim lngRet              As Long
   Dim blnReady            As Boolean
   Dim dteTimeout          As Date
   ' Startet die Datenübertragung, zeigt bei Bedarf einen Dialog an
   ' Wenn die Message MSG_XFERREADY abgesetzt wird, ist die
   ' Übertragung abgeschlossen. Zurückgegeben wird ein Bild vom Typ
   ' DIB (Device Intependend Picture)
   ' Nach einer eingestellten Timeoutzeit (Voreinstellung 120 Sek.)
   ' wird abgebrochen
   On Error Goto Errorhandler
   
   With udtUserinterface
      ' Dialog anzeigen
      .ShowUI = blShowUI 'Abs(CLng(blShowUI))
      .ModalUI = 1
      .hParent = mlngWindow
   End With
   
   lngRet = DSM_Entry( _
      mudtAppID, _
      mudtScrID, _
      DG_CONTROL, _
      DAT_USERINTERFACE, _
      MSG_ENABLEDS, _
      udtUserinterface)
      
   If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
   
   ' Notausgang
   dteTimeout = Now() + TimeSerial(0, 0, mlngTimeoutSec)
   
   Do While GetMessage(udtMSG, 0&, 0&, 0&)
   
      If Now() > dteTimeout Then Exit Do
      
      ' Speicherinhalt auf Null setzen
      ZeroMemory VarPtr(udtEvent), Len(udtEvent)
      
      ' Art des Events festlegen
      udtEvent.pEvent = VarPtr(udtMSG)
      
      ' Event holen
      lngRet = DSM_Entry( _
         mudtAppID, _
         mudtScrID, _
         DG_CONTROL, _
         DAT_EVENT, _
         MSG_PROCESSEVENT, _
         udtEvent)
         
      Select Case udtEvent.TWMessage
         Case MSG_XFERREADY
            ' Bereit zum Übertragen
            blnReady = True
            Exit Do
         Case MSG_CLOSEDSREQ
            ' Schließen
            blnReady = False
            Exit Do
      End Select
      TranslateMessage udtMSG
      DispatchMessage udtMSG
    Loop
    
   If blnReady Then
   
      ' PIC-Handle (DIB) holen
      lngRet = DSM_Entry( _
         mudtAppID, _
         mudtScrID, _
         DG_IMAGE, _
         DAT_IMAGENATIVEXFER, _
         MSG_GET, _
         lngDIB)
   End If
   
   ' Transfer beenden
   lngRet = DSM_Entry( _
      mudtAppID, _
      mudtScrID, _
      DG_CONTROL, _
      DAT_PENDINGXFERS, _
      MSG_ENDXFER, _
      udtPending)
         
   ' Dialog beenden
   lngRet = DSM_Entry( _
      mudtAppID, _
      mudtScrID, _
      DG_CONTROL, _
      DAT_USERINTERFACE, _
      MSG_DISABLEDS, _
      udtUserinterface)
   
   ' Ausgelesenes DIB zurückgeben
   GetTwainData = lngDIB
   
   Exit Function
Errorhandler:
End Function

Private Function GetPictureFromDib(lngDIB As LongAs IPictureDisp
   Dim udtPicdesc          As PICTDESC
   Dim IID_IDispatch       As GUID
   Dim objPic              As IPictureDisp
   Dim hImage              As Long
   Dim lngRet              As Long
   Dim lngGraphicTypeClip  As Long
   Dim lngVbPicType        As Long
   ' Wandelt ein Bild vom Typ DIB in eins vom Typ IPictureDisp um
   ' Dazu wird aber eine Bitmap benötigt. Wird ein DIB in die
   ' Zwischenablage übertragen, steht es dort auch als Bitmap oder
   ' Metafile zur Verfügung. Dieses wird ausgelesen und mit der
   ' API OleCreatePictureIndirect umgewandelt
   On Error Goto Errorhandler
   
   If lngDIB = 0 Then Exit Function
   ' Schnittstellenkennung kPictureIID (GUID)
   With IID_IDispatch
      .Data1 = &H20400
      .Data4(0) = &HC0
      .Data4(7) = &H46
   End With
   
   ' DIB ins Clipboard stellen
   OpenClipboard 0&
   EmptyClipboard
   SetClipboardData CF_DIB, lngDIB
   CloseClipboard
   
   ' Bitmap oder Metafile aus Clipboard holen
   OpenClipboard 0&
   If IsClipboardFormatAvailable(CF_BITMAP) <> 0 Then
      lngGraphicTypeClip = CF_BITMAP
      lngVbPicType = vbPicTypeBitmap
   ElseIf IsClipboardFormatAvailable(CF_ENHMETAFILE) <> 0 Then
      lngGraphicTypeClip = CF_ENHMETAFILE
      lngVbPicType = vbPicTypeEMetafile
   End If
   
   ' Überprüfen, ob eine Grafik im Clipboard ist
   If lngGraphicTypeClip <> 0 Then
   
      ' Handle auf Grafik im Clipboard
      hImage = GetClipboardData(lngGraphicTypeClip)
      
      With udtPicdesc
         ' Struktur Picdesc ausfüllen
         .cbSize = Len(udtPicdesc)
         .picType = lngVbPicType
         .hImage = hImage
       End With

      ' Picture-Objekt erzeugen
      lngRet = OleCreatePictureIndirect( _
         udtPicdesc, IID_IDispatch, 1&, objPic)
         
      ' Picture-Objekt zurückgeben
      If lngRet = 0 Then Set GetPictureFromDib = objPic
      
   End If
   
' Die Fehlerbehandlung soll sicherstellen, dass
' das Clipboard auch bei Fehlern geschlossen wird
Errorhandler:
   CloseClipboard
End Function

Private Function DataSourceManagerOpen() As Boolean
   Dim strManufacturer     As String
   Dim strProductFamily    As String
   Dim strProductName      As String
   Dim lngRet              As Long
   Dim udtDummy            As TW_IDENTITY

   On Error Goto Errorhandler
      
   ' Benutzerdefinierte Variable zurücksetzen
   mudtAppID = udtDummy

   strManufacturer = Trim(mstrManufacturer)
   strProductFamily = Trim(mstrProductFamily)
   strProductName = Trim(mstrProductName)
    
   ' Messagefenster erzeugen
   mlngWindow = CreateWindowEx( _
      0&, _
      "#32770", _
      "TWAIN_MSG_WINDOW", _
      0&, _
      10&, 10&, 150&, 50&, _
      0&, 0&, 0&, 0&)
         
   With mudtAppID
      ' Eigene Versionsinformationen eintragen
      .Version.MajorNum = 1
      .Version.Language = TWLG_GER
      .Version.Country = TWCY_GERMANY
      .ProtocolMajor = TWON_PROTOCOLMAJOR
      .ProtocolMinor = TWON_PROTOCOLMINOR
      .SupportedGro
ups1 = DG_CONTROL Or DG_IMAGE
      CopyMemory _
         VarPtr(.Manufacturer(1)), _
         StrPtr(StrConv(strManufacturer, vbFromUnicode)), _
         Len(strManufacturer)
      CopyMemory _
         VarPtr(.ProductFamily(1)), _
         StrPtr(StrConv(strProductFamily, vbFromUnicode)), _
         Len(strProductFamily)
      CopyMemory _
         VarPtr(.ProductName(1)), _
         StrPtr(StrConv(strProductName, vbFromUnicode)), _
         Len(strProductName)
   End With
    
   ' Messagefenster setzen
   lngRet = DSM_Entry( _
      mudtAppID, _
      ByVal 0&, _
      DG_CONTROL, _
      DAT_PARENT, _
      MSG_OPENDSM, _
      mlngWindow)
      
   If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
    
   ' Erfolg zurückgeben
   DataSourceManagerOpen = True
   
   Exit Function
Errorhandler:
   DestroyWindow mlngWindow
End Function

Private Function DataSourceManagerClose() As Boolean
   Dim lngRet As Long
   On Error Goto Errorhandler
   
   ' Dialog schließen
   lngRet = DSM_Entry( _
      mudtAppID, _
      ByVal 0&, _
      DG_CONTROL, _
      DAT_PARENT, _
      MSG_CLOSEDSM, _
      mlngWindow)
      
   If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
   
   ' Fenster Auswahldialog zerstören
   DestroyWindow mlngWindow
    
   ' Ergebnis zurückgeben
   DataSourceManagerClose = True
   
   Exit Function
Errorhandler:
   ' Fenster Auswahldialog zerstören
   DestroyWindow mlngWindow
End Function

Private Function DataSourceOpen() As Boolean
   Dim lngRet     As Long
   Dim udtDummy   As TW_IDENTITY

   On Error Goto Errorhandler
   
   ' Benutzerdefinierte Variable zurücksetzen
   mudtScrID= udtDummy

   lngRet = DSM_Entry( _
      mudtAppID, _
      ByVal 0&, _
      DG_CONTROL, _
      DAT_IDENTITY, _
      MSG_OPENDS, _
      mudtScrID)
      
   If lngRet <> TWRC_SUCCESS Then Goto Errorhandler

   DataSourceOpen = True
   Exit Function
Errorhandler:
End Function

Private Function DataSourceClose() As Boolean
   Dim lngRet     As Long
   On Error Goto Errorhandler
   
   ' Auswahl Datenquelle schließen
   lngRet = DSM_Entry( _
      mudtAppID, _
      ByVal 0&, _
      DG_CONTROL, _
      DAT_IDENTITY, _
      MSG_CLOSEDS, _
      mudtScrID)
      
   If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
    
   ' Ergebnis zurückgeben
   DataSourceClose = True
   
   Exit Function
Errorhandler:
End Function

Private Function GetTwainValue(ByVal lngCapability As LongAs Variant
   Dim udtCapability       As TW_CAPABILITY
   Dim udtOneValue         As TW_ONEVALUE
   Dim udtFix32            As TW_FIX32
   Dim lngPosValue         As Long
   Dim lngRet              As Long
   On Error Goto Errorhandler
   
   With udtCapability
      .ConType = TWON_ONEVALUE
      .Cap = lngCapability
      
      ' Einen Wert auslesen
      lngRet = DSM_Entry( _
         mudtAppID, _
         mudtScrID, _
         DG_CONTROL, _
         DAT_CAPABILITY, _
         MSG_GET, _
         udtCapability)
                       
      If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
      
      ' Speicher sperren und Adresse ermitteln
      lngPosValue = GlobalLock(.hContainer)
      
      ' Speicherinhalt kopieren
      CopyMemory VarPtr(udtOneValue), lngPosValue, Len(udtOneValue)
      
      ' Sperrung aufheben und Speicher freigeben
      GlobalUnlock .hContainer
      GlobalFree .hContainer
      
   End With
   
   With udtOneValue
      ' Wert je nach geforderten Datentyp zurückgeben
      Select Case .ItemType
         Case INT16 ' 16 Bit vorzeichenbehaftet
            GetTwainValue = .Item1
         Case UINT16, BOOL ' 16 Bit vorzeichenlos
            GetTwainValue = FromUnsignedShort(.Item1)
         Case FIX32 ' 32 Bit vorzeichenlos
            CopyMemory VarPtr(udtFix32), VarPtr(.Item1), 4&
            GetTwainValue = Fix32ToDbl(udtFix32)
      End Select
   End With
   
   Exit Function
Errorhandler:
End Function

Private Function SetTwainValue( _
   lngCapability As Long, _
   lngType As Long, _
   varItem As Variant _
   ) As Boolean
   Dim udtCapability    As TW_CAPABILITY
   Dim udtOneValue      As TW_ONEVALUE
   Dim udtFix32         As TW_FIX32
   Dim lngHandle        As Long
   Dim lngPosValue      As Long
   Dim lngRet           As Long
   Dim intDummy         As Integer
   On Error Goto Errorhandler
   
   With udtCapability
      .ConType = TWON_ONEVALUE
      .Cap = lngCapability
      
      udtOneValue.ItemType = lngType
      
      Select Case lngType
         Case INT16
            udtOneValue.Item1 = CInt(varItem)
         Case UINT16, BOOL
            intDummy = ToUnsignedInt(CLng(varItem))
            CopyMemory VarPtr(udtOneValue.Item1), VarPtr(intDummy), 2&
         Case FIX32
            udtFix32 = DblToFix32(CDbl(varItem))
            CopyMemory VarPtr(udtOneValue.Item1), VarPtr(udtFix32), 4&
      End Select
   
      ' Speicher reservieren, Handle darauf holen
      lngHandle = GlobalAlloc(GHND, Len(udtOneValue))
      
      ' Speicher sperren, Adresse auslesen
      lngPosValue = GlobalLock(lngHandle)
      
      ' Inhalt udtOneValue an die Adresse lngPosValue kopieren
      CopyMemory lngPosValue, VarPtr(udtOneValue), Len(udtOneValue)
      
      ' Sperrung aufheben
      GlobalUnlock lngHandle
      
      ' Handle auf den reservierten Speicher
      .hContainer = lngHandle
      
      ' Wert setzen
      lngRet = DSM_Entry( _
         mudtAppID, _
         mudtScrID, _
         DG_CONTROL, _
         DAT_CAPABILITY, _
         MSG_SET, _
         udtCapability)
                       
      ' Speicher freigeben
      GlobalFree lngHandle
      
      If lngRet <> TWRC_SUCCESS Then Goto Errorhandler
         
   End With
   SetTwainValue = True
   Exit Function
Errorhandler:
End Function

Private Function SetUserDefinedValues() As Boolean
   Dim lngRet           As Long
   Dim lngDIB           As Long
   Dim udtLayout        As TW_IMAGELAYOUT
   On Error Goto Errorhandler
   
   ' Wenn Null übergeben wird, dann gesamte Breite oder Höhe
   If mdblRight = 0 Then mdblRight = GetTwainValue(PHYSICALWIDTH)
   If mdblBottom = 0 Then mdblBottom = GetTwainValue(PHYSICALHEIGHT)
   
   With udtLayout.Frame
      ' Struktur Bildgröße ausfüllen
      .Left = DblToFix32(mdblLeft)
      .Top = DblToFix32(mdblTop)
      .Right = DblToFix32(mdblRight)
      .Bottom = DblToFix32(mdblBottom)
   End With
   
   ' Bildgröße festlegen
   lngRet = DSM_Entry( _
      mudtAppID, _
      mudtScrID, _
      DG_IMAGE, _
      DAT_IMAGELAYOUT, _
      MSG_SET, _
      udtLayout)
      
   ' Auflösung und Farbtiefe festlegen
   SetTwainValue XRESOLUTION, FIX32, mlngRes
   SetTwainValue YRESOLUTION, FIX32, mlngRes
   SetTwainValue PIXELTYPE, UINT16, mlngColorType
   
   ' Farbtiefe in Bits per Pixel bei RGB festlegen (24 Bits/Pixel)
   If mlngColorType = RGB Then SetTwainValue BITDEPTH, UINT16, 24

   ' Ergebnis zurückgeben
   SetUserDefinedValues = True
   
   Exit Function
Errorhandler:
End Function

Private Function ToUnsignedInt(lngValue As LongAs Integer
   ToUnsignedInt = "&H" & Hex(lngValue)
End Function
Private Function FromUnsignedShort(intValue As IntegerAs Long
   FromUnsignedShort = "&H" & Hex(intValue)
End Function
Private Function ToUnsignedLong(dblValue As DoubleAs Long
   ToUnsignedLong = IIf(dblValue > 2147483647, dblValue - 4294967296#, dblValue)
End Function
Private Function FromUnsignedLong(lngValue As LongAs Double
   FromUnsignedLong = IIf(lngValue < 0, lngValue + 4294967296#, lngValue)
End Function
Private Function Fix32ToDbl(udtFix32 As TW_FIX32) As Double
   Fix32ToDbl = udtFix32.Whole + CDbl("&H" & Hex(udtFix32.Frac)) / 65536
End Function
Private Function DblToFix32(dblValue As DoubleAs TW_FIX32
   With DblToFix32
      .Whole = CInt(Fix(dblValue))
      .Frac = "&H" & Hex(CLng((dblValue - .Whole) * 65536))
   End With
End Function

Die Eigenschaften Left, Top, Right, Bottom

Diese Eigenschaften legen den Scanbereich in Inch fest.

Die Eigenschaften Manufacturer, ProductFamily, ProductName

Diese Eigenschaften können einen beliebigen Text aufnehmen, sind aber für die Funktion selber unerheblich. In der Initialisierungsroutine wird Manufacturer auf den aktuellen Benutzernamen, ProductFamily auf "First Twain", ProductName auf "Office Twain" gesetzt.

Die Eigenschaft TimeoutSec

Diese Eigenschaft legt die Timeoutzeit in Sekunden fest, nach der abgebrochen wird. In der Initialisierungsroutine wird die Zeit auf den Wert 120 Sekunden gesetzt.

Die Eigenschaft ResolutionDPI

Diese Eigenschaft legt die Auflösung fest, in der gescannt wird. In der Initialisierungsroutine wird die Auflösung auf den Wert 200 DpI gesetzt.

Die Eigenschaft HideDialog

Diese Eigenschaft legt fest, ob das Dialogfenster ausgeblendet wird.

Die Eigenschaften ColorTypeSetRGB, ColorTypeSetBlackWhite, ColorTypeSetGrey, GetUserdefinedColorType

Die ersten drei Eigenschaften legen den Farbtyp fest, der beim Scannen eingesetzt wird. Die letzte Eigenschaft liefert den aktuellen Farbtyp als Longwert. In der Initialisierungsroutine wird der Farbtyp auf RGB gesetzt.

Die Eigenschaft SetSettings

Diese Eigenschaft legt fest, ob Voreinstellungen benutzt werden.

Die öffentliche Funktion GetTwainPicture

Diese Funktion liefert das gescannte Bild als Objekt vom Typ IPictureDisp. Dazu wird die interne Funktion LoadTwainPic aufgerufen.

Die öffentliche Funktion SaveTwainPicture

Diese Funktion  nimmt einen Dateipfad inklusive Dateinamen entgegen, speichert dort das gescannte Bild und liefert einen Wahrheitswert, der Auskunft darüber gibt, ob die Aktion erfolgreich war. Dazu wird die interne Funktion LoadTwainPic aufgerufen und mit der SavePicture-Methode das Bild gespeichert.

Die öffentliche Funktion ShowSourceDialog

Diese Funktion öffnet den Dialog zur Auswahl einer Twainquelle und liefert einen Wahrheitswert, der Auskunft darüber gibt, ob die Aktion erfolgreich war. Dazu wird die interne Funktion DataSourceManagerOpen ausgeführt, die den Data Source Manager öffnet. Danach wird die Twain-API-Funktion DSM_Entry aufgerufen, welche den eigentlichen Dialog erscheinen lässt. Anschließend ruft man die interne Funktion DataSourceManagerClose auf, welche den Data Source Manager wieder schließt. Das Öffnen und letztendlich das Schließen des Managers ist Grundbestandteil jeder Operation.

Die interne Funktion LoadTwainPicture

Diese Funktion öffnet den Datenquellenmanager und die Datenquelle. Bei Bedarf werden Voreinstellungen gesetzt, wenn kein Dialog gewünscht ist. Zurückgegeben wird ein Ole-Bild vom Typ IPictureDisp.

Der Datenquellenmanager wird über die interne Funktion DataSourceManagerOpen und die Datenquelle mit der internen Funktion DataSourceOpen geöffnet. Sollen Voreinstellungen gesetzt werden, ruft man die interne Funktion SetUserDefinedValues auf. Anschließend ruft man die interne Funktion GetTwainData auf und übergibt einen Wahrheitswert, der darüber entscheidet, ob ein Dialog angezeigt wird. Nun werden noch über die internen Funktionen DataSourceManagerClose und DataSourceClose der Datenquellenmanager und die Datenquelle geschlossen.

Jetzt besitzt man ein Handle auf eine DIB (Device Intependent Bitmap), welches an die Funktion GetPictureFromDib übergeben wird, die ein Bild vom Typ IPictureDisp zurückliefert. Danach kann der für die DIB reservierte Speicherbereich freigegeben werden.

Die interne Funktion DataSourceManagerOpen

Diese Funktion, also das öffnen des Data Source Managers wird bei nahezu bei jeder Operation, welche über die Schnittstelle läuft, aufgerufen. Zu Beginn wird die Variable mudtAppID des benutzerdefinierten Datentyps TW_IDENTITY bzw. TW_VERSION mit den in der Initialisierungsroutine vordefinierten oder an die Klasse übergebenen Daten gefüllt.

Die Kommunikation mit der DLL erfolgt, wie in der Welt von Microsoft üblich, über die Nachrichtenschlange (Messagequeue) von Fenstern, es wird dazu also auch ein Fenster benötigt. Solch ein unsichtbares Dummy-Fenster beliebiger Größe legt man mit der API CreateWindowEx an. Das Handle dieses erzeugten Fensters wird in der klassenweit gültigen Variablen mlngWindow gespeichert. Die DLL Twain_32.dll benötigt natürlich auch dieses Handle, damit sie eben Nachrichten an das Fenster schicken kann. Übergeben wird das Fensterhandle als sechster Parameter mit der Allround-Funktion DSM_Entry., wobei der fünfte Parameter auf MSG_OPENDSM gesetzt wird.

Auslesen kann man diese Nachrichten mit der API GetMessage, welche die vom einem Fenster empfangenen Nachrichten auslesen kann. Man sollte aber auch nicht vergessen, bei einem Fehler oder spätestens beim Entladen der Klasse das erzeugte Fenster mit der API DestroyWindow auch wieder zu zerstören.

Die interne Funktion DataSourceManagerClose

Diese Funktion schließt mit der Funktion DSM_Entry den Data Source Manager. Übergeben wird das Fensterhandle als sechster Parameter, der fünfte Parameter wird auf MSG_CloseDSM gesetzt.

Die interne Funktion DataSourceOpen

Diese Funktion öffnet die Datenquelle mit der Funktion DSM_Entry. Übergeben wird die Variable mudtScrID des benutzerdefinierten Datentyps TW_IDENTITY bzw. TW_VERSION als sechster Parameter, der fünfte Parameter wird auf MSG_OPENDS gesetzt. Die Variable mudtScrID wird dabei mit Informationen gefüllt.

Die interne Funktion DataSourceClose

Diese Funktion schließt mit der Funktion DSM_Entry die Datenquelle. Übergeben wird die Variable mudtScrID des benutzerdefinierten Datentyps TW_IDENTITY bzw. TW_VERSION als sechster Parameter, der fünfte Parameter wird auf MSG_CLOSEDS gesetzt.

Die interne Funktion SetUserDefinedValues

Diese Funktion dient dazu, Voreinstellungen zu setzen. Hat man über die Eigenschaften für die Breite oder Höhe keinen Wert vorgegeben, wird mit der internen Funktion GetTwainValue die Breite oder Höhe des gesamten Scanbereichs in Inch (Zoll) ermittelt. Zurückgeliefert wird dabei ein Double-Wert, die Funktion selbst kann je nach Anforderung aber auch VBA-Integer oder -Long (Vorzeichenbehaftet) zurückliefern.

Zum Setzen der Scangröße wird eine ausgefüllte Variable vom Typ TW_IMAGELAYOUT benötigt. Das darin enthaltene Element vom Typ Frame nimmt schließlich den gewünschten Scanbereich auf. Dessen Elemente (Left, Top, Right, Bottom) sind vom Typ TW_FIX32 und bestehen aus je zwei (16 Bit) Integer-Elementen, wobei das erste Element den Ganzzahlenanteil, das zweite den Nachkommaanteil aufnimmt. Die Umwandlung von Double in diesen Datentyp übernimmt die Funktion DblToFix32.

Zum Setzen der Scangröße wird wieder die Funktion DSM_Entry bemüht, die Kombination der Parameter drei, vier und fünf gibt den Auftrag vor, der sechste Parameter vom Typ TW_IMAGELAYOUT enthält die Daten.

Mit Hilfe der internen Funktion SetTwainValue werden die weiteren Einstellungen vorgenommen.

Um die Auflösung in X-, bzw. Y-Richtung festzulegen, übergibt man im ersten Parameter einmal die Konstante XRESOLUTION und für die andere Richtung die Konstante YRESOLUTION. Um der Funktion mitzuteilen, um was für einen Datentyp es sich bei der Auflösung handelt, wird im zweiten Parameter die Konstante FIX32 übergeben. Der dritte Parameter enthält schließlich den zu setzenden Wert.

Die interne Funktion SetTwainValue wird auch dazu benutzt, festzulegen, ob im RGB-Farbraum, mit Grautönen oder schwarzweiß gescannt wird. In der Initialisierungsroutine wurde RGB festgelegt. Der zum gewünschten Farbraum zugehörige Wert wird als dritter Parameter übergeben, als Datentyp (zweiter Parameter) wird UINT16 verwendet und im ersten Parameter übergibt man die Konstante PIXELTYPE. Wurde RGB eingestellt, muss auch noch die Farbtiefe gesetzt werden. Dazu übergibt man an SetTwainValue die Parameter BITDEPTH, UINT16 und den Wert 24 für eine Farbtiefe von 24 Bit.

Die interne Funktion GetTwainValue

Diese Funktion dient dazu, Einstellungen der Twainquelle auszulesen. Als Parameter wird eine Kennung des gewünschten Wertes übergeben.

Zwei Elemente einer Variablen des Datentyps TW_CAPABILITY werden zum Auslesen ausgefüllt, einmal ConType mit der Konstanten TWON_ONEVALUE und zum Anderen Cap mit der Kennung, welcher Wert gelesen werden soll. Diese Variable wird als sechster Parameter an die Funktion DSM_Entry übergeben, die Kombination der Parameter drei, vier und fünf gibt den Auftrag vor. Der Rückgabewert der Funktion DSM_Entry gibt Auskunft über den Erfolg. Wird der Wert der Konstanten TWRC_SUCCESS zurückgeliefert, war alles in Ordnung, im anderen Fall wird die Funktion abgebrochen.

An den Inhalt des gesuchten Wertes kommt man aber nicht direkt. Das Element hContainer des Datentyps TW_CAPABILITY enthält nach der Rückkehr auch nicht die Adresse des Speicherbereichs, sondern als Wert einen Zeiger auf 4 Bytes, an der die Adresse zu finden ist. GlobalLock erwartet solch einen Zeiger, sperrt dabei den Speicherbereich und liefert die Adresse als Wert. Der Datentyp, welcher an der Speicheradresse steht, ist vom Typ TW_ONEVALUE. Mit CopyMemory wird der Speicherinhalt in eine Variable vom Typ TW_ONEVALUE kopiert. Jetzt muss man noch die Sperrung des Speichers aufheben und den Speicher wieder freigeben. Dazu dienen die API-Funktionen GlobalUnlock und GlobalFree.

Mit Hilfe des im Element ItemType der Variablen udtOneValue vom Typ TW_ONEVALUE kann man erkennen, welcher Datentyp in den nächsten zwei Integerwerten steckt. Diese werden bei Bedarf mit Hilfe der internen Funktionen FromUnsignedShort oder Fix32ToDbl in VBA-Werte umgewandelt und als Funktionsergebnis zurückgegeben.

Die interne Funktion SetTwainValue

Diese Funktion dient dazu, Einstellungen der Twainquelle zu setzen. Als erster Parameter wird eine Kennung des gewünschten Wertes übergeben, als zweiter der Datentyp und als dritter die Daten selbst.

Zwei Elemente der Variablen udtCapability des Datentyps TW_CAPABILITY werden zu Beginn ausgefüllt, einmal ConType mit der Konstanten TWON_ONEVALUE und zum Anderen Cap mit der Kennung, welcher Wert gesetzt werden soll.

Mit Hilfe des im Element ItemType der Variablen udtOneValue vom Typ TW_ONEVALUE wird festgelegt, welcher Datentyp in den nächsten zwei Integerwerten steckt. Der zu setzende Wert wird mit Hilfe der internen Funktionen ToUnsignedInt oder DblToFix32 in einen Longwert umgewandelt, der Inhalt dann mit CopyMemory in die Variable udtOneValue kopiert.

Mit dieser Variablen selbst kann man aber noch nicht viel anfangen. Wie bereits bei GetTwainValue beschrieben, enthält das Element hContainer des Datentyps TW_CAPABILITY nicht die Adresse des Speicherbereichs, sondern einen Zeiger auf 4 Bytes, an der die Adresse des Speicherbereichs der Daten zu finden ist. Also reserviert man sich mit GlobalAlloc so viel Speicher, dass der Typ TW_ONEVALUE komplett hineinpasst und sperrt diesen mit GlobalLock. Nun besitzt man die Adresse dieses Speichers und kann den Inhalt der Variablen vom Typ TW_ONEVALUE mit CopyMemory dort hineinkopieren. Jetzt kann die Sperrung aufgehoben werden und dem Element hContainer die Adresse des Speicherbereichs übergeben werden.

Als sechster Parameter wird nun die Variable udtCapability an die Funktion DSM_Entry übergeben, die Kombination der Parameter drei, vier und fünf gibt den Auftrag vor. Der Rückgabewert der Funktion DSM_Entry gibt dabei Auskunft über den Erfolg. Die gewünschte Einstellung sollte erfolgt sein, wenn der Rückgabewert dem Wert der Konstanten TWRC_SUCCESS entspricht. Zuvor muss aber noch der reservierte Speicher wieder freigegeben werden.

Die interne Funktion GetTwainData

Diese Funktion startet die Datenübertragung, im Übergabeparameter wird festgelegt, ob ein Dialog angezeigt wird. Zurückgegeben wird ein Bild vom Typ DIB (Device Intependend Picture). Nach einer eingestellten Timeoutzeit (Voreinstellung 120 Sek.) wird abgebrochen..

Zu Beginn wird die Variable udtUserinterface vom Datentyp TW_USERINTERFACE ausgefüllt. Das darin enthaltene Element ShowUI entscheidet darüber, ob ein Dialog angezeigt wird. Als sechster Parameter wird nun die Variable udtUserinterface an die Funktion DSM_Entry übergeben, die Kombination der Parameter drei, vier und fünf gibt den Auftrag vor. Der Rückgabewert der Funktion DSM_Entry gibt Auskunft über den Erfolg.

Der Scanvorgang selbst ist bei der Rückkehr aber nicht abgeschlossen, Auskunft darüber erteilt die API GetMessage, welche die Fensternachrichten an den aktuellen Thread abfragt, wenn der zweite Parameter Null ist. Bei jedem Schleifendurchlauf wird diese API einmal aufgerufen, dabei wird die Variable udtMSG des Typs MSG ausgefüllt. Diese wird in eine Struktur vom Typ udtEvent als Element pEvent eingebunden und als sechster Parameter an die Funktion DSM_Entry übergeben, die Kombination der Parameter drei, vier und fünf gibt den Auftrag vor.

Nach der Rückkehr wird das Element TWMessage ausgewertet. Handelt es sich um den Wert der Konstanten MSG_XFERREADY wird die Variable blnReady auf Wahr, bei einem Wert von MSG_CLOSEDSREQ wird blnReady auf Falsch gesetzt. Bei beiden Werten wird die Schleife verlassen. Die Schleife wird auch verlassen, wenn die Timeoutzeit abgelaufen ist. Mit den Funktionen DispatchMessage und TranslateMessage werden anschließend die Meldungen weitergeleitet.

Ist anschließend die Variable blnReady auf Wahr gesetzt, holt man sich mit der Funktion DSM_Entry ein Handle auf ein Bild vom Typ DIB (Device Intependend Picture). Indem man beim Aufrufen der Funktion DSM_Entry den fünften Parameter auf MSG_ENDXFER setzt, wird der Transfer beendet, mit dem Setzen auf MSG_DISABLEDS der Dialog beendet.

Die interne Funktion GetPictureFromDib

Diese Funktion dient dazu, aus dem DIB ein Objekt vom Typ StdPicture (IPictureDisp) machen. Ich habe aber bisher keine Möglichkeit gefunden, das direkt zu erledigen, aber mit einer Bitmap oder einem Metafile funktioniert es. Wird ein DIB in die Zwischenablage übertragen, steht sie dort auch als Bitmap oder Metafile zur Verfügung. Also öffnet man mit OpenClipboard die Zwischenablage, leert diese mit EmptyClipboard, stellt die DIB mit SetClipboardData in die Zwischenablage und schließt diese wieder mit CloseClipboard.

Nun öffnet man mit OpenClipboard die Zwischenablage, fragt mit IsClipboardFormatAvailable nach, ob dort eine Bitmap oder ein Metafile vorhanden ist. Dieses Grafikhandle wird mit GetClipboardData ausgelesen und am Ende schließt man mit CloseClipboard die Zwischenablage.

Der API-Funktion OleCreatePictureIndirect übergibt man den ausgefüllten Datentyp PICTDESC. Dort werden das Bitmap- bzw. Metafilehandle 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.

Die internen Funktionen ToUnsignedInt, FromUnsignedShort, ToUnsignedLong, FromUnsignedLong, Fix32ToDbl, DblToFix32

Diese Funktion wandeln Zahlen in Formate um, die von der Funktion DSM_Entry verstanden werden und umgekehrt.