Buchempfehlung
Visual Basic 6 Kochbuch
Visual Basic 6 Kochbuch
Viele praktische Tipps zum Programmieren mit Visual Basic 6, die sich oft auch auf FB übertragen lassen. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

Windows Drag und Drop Tutorial

von MitgliedstephanbrunkerSeite 3 von 12

IUnknown Interface

Im FreeBasic Package ist unter FreeBASIC\Examples\Win32\COM\DropTarget\ zwar eine Implementation des DropTarget Interfaces vorhanden, aber die ist leider nicht besonders gut gemacht und praktisch gar nicht erklärt. Außerdem wurde sie zu einer Zeit geschrieben, in der FreeBasic noch keine virtuellen Methoden unterstützte und deshalb wurde dafür ein Workaround benutzt.

Das grundlegende Interface für die Windows COM ist das IUnknown Interface. Die Interfaces funktionieren in der Praxis als TYPE, dem verschiedene Funktionen zugeordnet sind. Zur Kommunikation wird nun eine Variable dieses Typs erzeugt und der Pointer dazu übergeben. Das Ziel ruft dann die Funktionen auf die damit zur Verfügung gestellt werden. Vom IUnknown Interface aus kommen zusätzlich die Vererbungen ins Spiel, denn jedes Kind dieses Interfaces besitzt die gleichen grundlegenden Funktionen plus seine eigenen. Da aber jedes Kind seine eigenen Versionen dieser ererbten Funktionen verwenden kann, sind diese in einer Tabelle virtueller Methoden (VTable) als Pointer hinterlegt, die auf die gültigen Methoden zeigen.

Das IUnknown Interface wird als TYPE IUnknown EXTENDS OBJECT definiert um die Vererbung in Freebasic zu aktivieren und die VTable im Hintergrund zu erzeugen. Die Methoden werden als VIRTUAL definiert um vom Kindtyp überschrieben werden zu können. Praktischerweise funktioniert hier Freebasic jetzt gleichermaßen wie Windows (C+), denn wenn ein fremdes Interface als Pointer übergeben wird, ist die Struktur mit der VTable dahinter die gleiche wie in Freebasic. Wir können den Pointer also als entsprechenden Dateityp CASTen und der Zugriff funktioniert dann mit dem -> Operator genauso wie erwartet. Das ist jetzt alles viel einfacher als in dem Beispiel im Package und in den in den Windows-Header-Dateien vorgesehenen TYPES für die Interfaces. Dort war nämlich die VTable manuell als erster Record im TYPE vorgesehen und der Zugriff erfolgte dann über (TYPE)->lpVtbl->Methode , wofür es einen extra TYPE, zum Beispiel IDropTargetVtbl gab. Einziger Nachteil wenn wir das jetzt "richtig" machen: Wir müssen die vorhandenen TYPES UNDEFen, um unsere eigenen, mit Vererbung programmierten verwenden zu können.

IUnknown hat die Methoden QueryInterface, AddRef und Release. Diese drei Methoden besitzen die Basisfunktionalitäten: Mit QueryInterface kann abgefragt werden, ob es sich um das gewünschte Interface handelt. Jede Kindfunktion erzeugt hier seine eigene Methode, denn IUnknown kann ja nicht wissen, um welches Kind es sich handelt. AddRef und Release verwalten, wie viele Programme im Moment auf das Interface zugreifen, indem sich jedes Programm hier registriert und den Zähler damit erhöht. Wenn wir zum Beispiel unser Interface als Drop-Ziel registrieren, erhöht sich der Zähler um 1. Genauso wird der Zähler mit Release erniedrigt und wenn es nicht mehr gebraucht wird, zerstört sich das Interface von selbst. Theoretisch bräuchten wir diese Methoden bei den Kind-Typen nicht mehr zu deklarieren und würden diese einfach vererben. Praktisch gilt das nur für das m_lRefCount Member und vielleicht AddRef, denn das QueryInterface muss ja zurückgeben, um welches Kind es sich genau handelt und Release muss die Kindtype löschen und nicht nur IUnknown und außerdem ggf. reservierten Speicher freigeben. Am einfachsten ist es dann, auf die Vererbung zu verzichten und diese Funktion in den abgeleiteten Typen zu überschreiben. Außerdem können wir so eine Print-Anweisung in den Code integrieren, der uns beim Debuggen sagt, was das Interface zur Zeit macht, denn es wird ja von Windows aufgerufen und nicht von uns selbst. Hier also der Code dafür, relativ wenig und einfach und deshalb wesentlich verständlicher als das Beispiel im Package:

#Undef IUnknown     'undef the default implementation

Type IUnknown EXTENDS OBJECT    'IUnknown Interface

        Declare Constructor()
        Declare Destructor()

        'Methods:
        'IUnknown Interface:
        Declare Virtual Function QueryInterface (ByVal iid As REFIID, ByVal ppvObject As Any Ptr Ptr) As HRESULT
        Declare Virtual Function AddRef () As ULong
        Declare Virtual Function Release () As ULong

        'member variables:
        As Long      m_lRefCount

End Type

Constructor IUnknown
    Print "IUnknown::Constructor"

    'Initialize Reference Count
    m_lRefCount  = 1

End Constructor

Destructor IUnknown()
    Print "IUnknown::Destructor"
End Destructor

'IUnknown::QueryInterface
Function IUnknown.QueryInterface (ByVal iid As REFIID, ByVal ppvObject As Any Ptr Ptr) As HRESULT
   'if this interface is what you want, then return a pointer
   Print "IUnknown::QueryInterface"
   If IsEqualIID ( iid, @IID_IUnknown) Then
        AddRef()
        *ppvObject = @this
        Return S_OK
    Else
        *ppvObject = NULL
        Return E_NOINTERFACE
    End If

End Function

'IUnknown::AddRef
Function IUnknown.AddRef () As ULong
    'register a new access
    m_lRefCount += 1
    Print "IUnknown::AddRef - Count: "; m_lRefCount
    Return m_lRefCount
End Function

'IUnknown::Release
Function IUnknown.Release () As ULong
    'unregister an access and destroy if not needed anymore
    m_lRefCount -= 1
    Print "IUnknown::Release - Count: "; m_lRefCount
    If(m_lRefCount = 0) Then
        Delete @this
        Return 0
    Else
        Return m_lRefCount
    End If
End Function

Wie sich mit dem Schlüsselwort Delete vermuten lässt, erzeugen wir unser Interface dann logischerweise mit NEW IUnknown . Der Constructor gibt uns einen RefCount, den wir nach Gebrauch mit ->Release() wieder freigeben, selbst wenn ein anderes Programm noch auf das Interface zugreift. Ein schönes Beispiel dafür ist die Zwischenablage, die gefüllt bleibt auch wenn unser Programm beendet wird.

 

Gehe zu Seite Gehe zu Seite  1  2  3  4  5  6  7  8  9  10  11  12  
Zusätzliche Informationen und Funktionen
  Bearbeiten Bearbeiten  

  Versionen Versionen