Tutorial
Windows Drag und Drop Tutorial
von stephanbrunker | Seite 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.
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|
|