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 11 von 12

Das IStream-Interface

Nachdem wir ja vererbte Interfaces verwenden, haben wir es bei dem IStream-Interface eigentlich mit zwei Interfaces zu tun: Von IUnknown erbt erst einmal ISequentialStream mit den Methoden Read und Write, und von diesem erbt dann IStream mit den Methoden Seek und ein paar anderen, die wir aber nicht implementieren müssen.

Nachdem wir das Prinzip ja schon hatten, braucht der Typ eigentlich kaum Erklärung:

Type ISequentialStream EXTENDS IUnknown     'Custom IISequentialStream Interface, requires Buxfix for IsEqualIID!

    Declare Constructor ()
    Declare Constructor (ByRef filepath As String)
    Declare Destructor ()

    'Methods:
    'IUnknown Interface:
    Declare Virtual Function QueryInterface ( ByVal iid As REFIID, ByVal ppvObject As PVOID Ptr) As HRESULT

    'ISequential Stream Interface:
    Declare Virtual Function Readx Alias "Read" ( ByVal pv As Any Ptr, ByVal cb As ULong, ByVal pcbread As ULong Ptr) As HRESULT
    Declare Virtual Function Writex Alias "Write" ( ByVal pv As Any Ptr, ByVal cb As ULong, ByVal pdbWritten As ULong Ptr) As HRESULT

    'member variables:
    As String   m_Filepath
    As String   m_Filecontent
    As ULongInt m_Seekpointer

End Type

Constructor ISequentialStream
    Print "ISequentialStream::Constructor()"

    'initalize member variables
    m_Seekpointer = 0
    'data buffer, because it's read in heaps
    m_Filecontent = ""

End Constructor

Constructor ISequentialStream (ByRef filepath As String)
    Print "ISequentialStream::Constructor"

    'initalize member variables
    m_Seekpointer = 0
    'data source:
    m_Filepath = filepath
    'data buffer, because it's read in heaps
    m_Filecontent = ""

End Constructor

Destructor ISequentialStream()
    Print "ISequentialStream::Destructor"
End Destructor

'IUnknown::QueryInterface
Function ISequentialStream.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 "ISequentialStream::QueryInterface"
   If IsEqualIID ( iid, @IID_ISequentialStream) Or IsEqualIID ( iid, @IID_IUnknown) Then
        AddRef()
        *ppvObject = @This
        Return S_OK
    Else
        *ppvObject = NULL
        Return E_NOINTERFACE
    End If

End Function



Bei AddRef und Release habe ich einfach die Elternfunktionen belassen, denn in den vorherigen Interfaces ging es mir darum, die Referenzvergabe zu dokumentieren und da brauchte ich die passende Print-Anweisung. QueryInterface muss natürlich überschrieben werden um auf die Anfrage richtig antworten zu können. Constructor und Destructor brauchen wir auch unbedingt sowie einen Parameterlosen Default-Constructor für die Vererbung zum nächsten Interface. Den Destructor bräuchten wir zwar theoretisch nicht, aber ein Virtueller Destructor bei IUnknown macht erst mal Probleme, also definieren wir ihn einfach, steht eh nix drin ...

Write ist ebenso nicht implementiert, interessant wird dann Read, das ist dann tatsächlich wo unsere Daten gelesen werden wollen:

Function ISequentialStream.Readx Alias "Read" ( ByVal pv As Any Ptr, ByVal cb As ULong, ByVal pcbread As ULong Ptr) As HRESULT
    Print "ISequentialStream::Read [";cb;" Bytes @ Seekpointer "; m_Seekpointer;" ]"
    'Caller requests cb Bytes of Data put at memory *pv, control value is pcbread (copied bytes)

    'Copy Filecontent into buffer (Replace with helper function to implement in actual program)
    If m_Filecontent = "" Then
        Open m_Filepath For Binary As #1
        m_Filecontent = Space(Lof(1))
        Get #1,,m_Filecontent
        Close #1
    End If

    Dim bytesleft As LongInt
    Dim toread As ULong
    'calculate remaining data from seekpointer to eof
    bytesleft = FileLen(m_Filepath) - m_Seekpointer
    If bytesleft <= 0 Then   'no more data
        *pcbRead = 0
        Return S_FALSE
    ElseIf bytesleft < cb Then   'as many as requested
        toread = bytesleft
    Else
        toread = cb             'less than requested
    EndIf

    Dim i As ULong
    Dim pp As UByte Ptr = pv
    'copy data to pointer recieved from caller
    For i = 0 To toread -1
        pp[i] = m_Filecontent[i + m_Seekpointer]
    Next i

    'update Seekpointer
    m_Seekpointer += i

    'give feedback if as many data is copied than requested
    If cb > toread Then
        *pcbRead = toread
        Return S_FALSE
    Else
        *pcbRead = cb
        Return S_OK
    EndIf

End Function

'ISequentialStream::Write
Function ISequentialStream.Writex ( ByVal pv As Any Ptr, ByVal cb As ULong, ByVal pcbWritten As ULong Ptr) As HRESULT
    Print "ISequentialStream::Write"
    'request for writing data *pv in the stream
    Return E_NOTIMPL
End Function

Was ich jetzt hier gemacht habe ist zwar eigentlich Blödsinn, aber es war die beste Möglichkeit der Demonstration. Ich lese nämlich den Dateiinhalt beim Aufruf komplett in einen String ein, was zwar funktioniert, aber eigentlich dient das Interface ja dazu, eben die Datei nur häppchenweise zu lesen und Speicher zu belegen. Jedenfalls hat das Interface einen Seekpointer genauso wie das beim Dateizugriff in Freebasic mit GET und PUT auch ist. Man übergibt der Read-Methode jetzt die Anweisung, cb Bytes ab dem Seekpointer zu lesen und den Inhalt am Pointer pv zu speichern. Zur Kontrolle gibt es den Wert pcbread, der angibt, wie viele Bytes tatsächlich gelesen wurden, wenn zum Beispiel über das Dateiende hinaus zu lesen versucht wurde. In ISequentialStream kann der Seekpointer nicht bewegt werden, dafür brauchen wir dann IStream.

Type IStream EXTENDS ISequentialStream

    Declare Constructor (ByRef filepath As String)
    Declare Destructor ()

    'Methods:
    'IUnknown Interface:
    Declare Virtual Function QueryInterface ( ByVal iid As REFIID, ByVal ppvObject As PVOID Ptr) As HRESULT

    'IStream Interface:
    Declare Virtual Function Seekx Alias "Seek" ( ByVal dilbMove As LARGE_INTEGER, ByVal dwOrigin As DWORD, ByVal plibNewPosition As ULARGE_INTEGER Ptr) As HRESULT
    Declare Virtual Function SetSize ( ByVal libNewSize As ULARGE_INTEGER) As HRESULT
    Declare Virtual Function CopyTo ( ByVal pstm As IStream Ptr, ByVal cb As ULARGE_INTEGER, ByVal pcbRead As ULARGE_INTEGER Ptr, ByVal pcbWritten As ULARGE_INTEGER Ptr) As HRESULT
    Declare Virtual Function Commit ( ByVal grfCommitFlags As DWORD) As HRESULT
    Declare Virtual Function Revert () As HRESULT
    Declare Virtual Function LockRegion ( ByVal libOffset As ULARGE_INTEGER, ByVal cb As ULARGE_INTEGER, ByVal dwLockType As DWORD) As HRESULT
    Declare Virtual Function UnlockRegion ( ByVal libOffset As ULARGE_INTEGER, ByVal cb As ULARGE_INTEGER, ByVal dwLockType As DWORD) As HRESULT
    Declare Virtual Function Stat ( ByVal pstatstg As STATSTG Ptr, ByVal grfStatFlag As DWORD) As HRESULT
    Declare Virtual Function Clone ( ByVal ppstm As ISTREAM Ptr) As HRESULT

End Type

Constructor IStream (ByRef filepath As String)
    Print "IStream::Constructor"
    'data source:
    m_Filepath = filepath

End Constructor

Wiederholungen gefallen nicht, wir brauchen nur Seek, auf den Rest antworden wir mit E_NOTIMPL.

'ISequentialStream::Seek
Function IStream.Seekx ( ByVal dlibMove As LARGE_INTEGER, ByVal dwOrigin As DWORD, ByVal plibNewPosition As ULARGE_INTEGER Ptr) As HRESULT
    'moves the Seekpointer
    Print "IStream::Seek -> ";

    Select Case dwOrigin
        Case STREAM_SEEK_SET
            Print "STREAM_SEEK_SET : Move ";
            m_Seekpointer = 0 + dlibMove.QuadPart
        Case STREAM_SEEK_CUR
            Print "STREAM_SEEK_CUR : Move ";
            m_Seekpointer += dlibMove.QuadPart
        Case STREAM_SEEK_END
            Print "STREAM_SEEK_END : Move ";
            m_Seekpointer = FileLen(m_Filepath) + dlibMove.QuadPart
    End Select
    Print dlibMove.QuadPart
    plibNewPosition->QuadPart = m_Seekpointer
    Return S_OK
End Function

Da Dateien größer als 2GB sein können, reicht hier eine Integer-Variable für die Dateilänge und den Seekpointer nicht aus. Der LARGE_INTEGER von Windows ist ein Type mit zwei Integerwerten und einem Quadpart, mit dem wir dann wie mit einem Freebasic Longint rechnen können. dwOrigin gibt an, von wo aus wir verschieben sollen, vom Anfang, vom Ende, oder von der aktuellen Position. Wenn wir also nicht die aktuelle Datei mit abspeichern, so müssen wir zumindest die Länge mit unterbringen um auf die Anfrage nach dem Ende korrekt anworten zu können.

 

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