Buchempfehlung
Windows System Programming
Windows System Programming
Das Kompendium liefert viele interessante Informationen zur Windows-Programmierung auf Englisch. [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 10 von 12

Drag&Drop mit virtuellen Dateien

Als letzte Variante behandeln wir noch den Umgang mit virtuellen Dateien, d.h. welche von denen wir keinen aktuellen Pfad angeben können, weil sie zum Beispiel in einer Datenbank sind oder zuerst noch entschlüsselt werden müssen. Das Clipboardformat dafür ist CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS und tritt immer als solches Paar auf. Da es nicht vordefiniert ist, müssen wir uns eine ID dafür holen:

    With divfmtetc(2)
        .cfFormat   = RegisterClipboardFormat(@"Filegroupdescriptor") 'CFSTR_FILEDESCRIPTOR
        .tymed      = TYMED_HGLOBAL
        .dwAspect   = DVASPECT_CONTENT
        .ptd        = NULL
        .lindex     = -1
    End With
    With divfmtetc(3)
        .cfFormat   = RegisterClipboardFormat(@"Filecontents")      'CFSTR_FILECONTENTS
        .tymed      = TYMED_HGLOBAL
        .dwAspect   = DVASPECT_CONTENT
        .ptd        = NULL
    End With
    With divfmtetc(4)
        .cfFormat   = divfmtetc(3).cfFormat
        .tymed      = TYMED_ISTREAM
        .dwAspect   = DVASPECT_CONTENT
        .ptd        = NULL
    End With

An der Stelle ist ein wenig Verwechselungsgefahr: Zu registrieren ist Filegroupdescriptor, was auch der TYPE ist, den wir dafür in HGLOBAL kopieren, das Format heißt in der MSDN aber CFSTR_FILEDESCRIPTOR. Das hier ist ein Teil meines Arrays der unterstützten Formate in meiner *.bi, damit ich diese Definitionen nicht immer wieder neu tippen muss. Der Filegroupdescriptor besteht wiederum aus zwei Strukturen: Einmal einem Filegroupdescriptor - der eigentlich nur ein UINT mit der Anzahl der enthaltenen Dateien ist - gefolgt von einem Filedescriptor für jede Datei. Dieser Filedescriptor enthält die ganzen Informationen über die Datei wie Dateiname, Attribute, die Erstellungs- und Änderungsdaten usw. Damit bekommt dann auch das lindex-Member von FORMATETC eine Bedeutung: In der Reihenfolge der Filedescriptoren werden auch die FORMATETC-Strukturen damit durchnummeriert, die erste Datei hat also .lindex = 0, die zweite .lindex=1 ... - wir haben damit in unserem Dataobject für jede Datei ein Format plus den Beschreiber. Ein ganz nützliches Tool ist übrigens der Dataobjectviewer, den es hier gibt:
Externer Link!dataobjview.zip
Dieser zeigt einem an, welche Datenobjekte in einem Drag&Drop enthalten sind - von dieser Seite stammt auch ein guter Teil der Informationen, die hier eingeflossen sind.

Für die ganze Struktur gibt es leider keinen richtigen TYPE auch wenn einer in der shlobj.bi definiert ist, denn der würde ein variables Array erfordern das mit Windows kompatibel ist und das geht mit FB leider nicht. Man kann sich das aber einfach manuell zusammenbasteln indem man den UINT und die FILEDESCRIPTOR Strukturen einfach nacheinander in Global Memory kopiert.

Dann gibt es noch zwei Möglichkeiten, den Inhalt der virtuellen Datei zu übertragen: einmal als HGLOBAL, was aber nur bis zu einer gewissen Dateigröße praktikabel ist, da wir den Dateiinhalt Byte für Byte in den globalen Speicher kopieren müssen. Dann gibt es das ISTREAM-Interface, das vereinfacht gesagt sich den Dateiinhalt scheibchenweise in einen vom Zielprogramm angegebenen Pointer kopieren lässt.

Doch zunächst füllen wir das CFSTR_FILEDESCRIPTOR-Format:

    Dim stats(0 To nFiles-1) As FILEDESCRIPTOR
    Dim pGlob2 As HGLOBAL
    If (cFlags And C_CONTGLOBAL) Or (cFlags And C_CONTSTREAM) Then
        numFormats += nFiles + 1
        'get FileAttributes and copy
        Dim fstats As  WIN32_FILE_ATTRIBUTE_DATA
        Dim filename As ZString * MAX_PATH
        numFormats += nFiles + 1            'one each for the content plus one for the descriptor
            filename = filepath(index) & filetitle(index)
            GetFileAttributesEx(@filename, GetFileExInfoStandard, @fstats )
            stats(index).dwFileAttributes = fstats.dwFileAttributes
            stats(index).ftCreationTime     = fstats.ftCreationTime
            stats(index).ftLastAccessTime = fstats.ftLastAccessTime
            stats(index).ftLastWriteTime    = fstats.ftLastWriteTime
            stats(index).nFileSizeHigh  = fstats.nFileSizeHigh
            stats(index).nFileSizeLow       = fstats.nFileSizeLow
            stats(index).cFileName          = filetitle(index)
            stats(index).dwFlags                = FD_ATTRIBUTES Or FD_CREATETIME Or FD_ACCESSTIME Or FD_WRITESTIME Or FD_FILESIZE
        Next index

        'Create FILEGROUPDESCRIPTOR = (UINT) nFiles & FILEDESCRIPTOR(1 to nfiles)
        'and copy to global
        Dim pFGD As UINT Ptr
        Dim pFD As FILEDESCRIPTOR Ptr
        pGlob2 = GlobalAlloc(GPTR_ ,SizeOf(UINT) + SizeOf(FILEDESCRIPTOR)*(nFiles) )
        pFGD = GlobalLock(pGlob2)
        *pFGD = nFiles
        pFD = Cast(FILEDESCRIPTOR Ptr,pFGD+1)
        For index = 0 To nFiles-1
            pFD[index] = stats(index)
        Next index
        GlobalUnlock(pGlob2)
    EndIf

Der Code macht jetzt genau das: von jeder Datei - vorher schon ausgelesen mit filepath und filetitle - mit GetFileAttributesEX die Informationen holen, in FILEDESCRIPTOR umwandeln und diese im Global Memory speichern, wobei am Anfang dieses Speicherbereichs zuerst die Anzahl der Beschreiber steht, fertig ist das Format. Die Datei speichern wir komplett in einem anderen Global Memory:

    Dim pContent(0 To nFiles-1) As HGLOBAL
    If (cFlags And C_TEXT) Or (CFlags And C_CONTGLOBAL) Then
        Dim filecontent As String
        For index = 0 To nFiles - 1
            Open (filepath(index) & filetitle(index)) For Binary As #1
            filecontent = String(Lof(1)+1, 0)   '0-terminated String für CF_TEXT
            Get #1,,filecontent
            Close #1
            pContent(index) = GlobalAlloc(GPTR_,Len(filecontent))
            memcpy(GlobalLock(pContent(index)),StrPtr(filecontent),Len(filecontent))
            GlobalUnlock(pContent(index))
            'for CF_TEXT, copy only the first file
            If (cFlags And C_TEXT) And Not (cFlags And C_CONTGLOBAL) Then
                numFormats += 1
                Exit For
            EndIf
        Next index
    EndIf

Wobei das das der gleiche Code wie für CF_TEXT bei ganzen Dateien ist, da wird nämlich die ganze Datei in einen Textstring umgewandelt und ebenso gespeichert. Dann das FORMATETC/STGMEDIUM Paar für das Formatpaar erstellen:

    If (cFlags And C_CONTGLOBAL) Or (cFlags And C_CONTSTREAM) Then
        'Format FILEDESCRIPTOR
        fmtetc(index) = divfmtetc(2)
        With stgmed(index)
            .tymed           = TYMED_HGLOBAL
            .hGlobal         = pGlob2
            .pUnkForRelease = NULL
        End With
        index += 1
    EndIf

    If (cFlags And C_CONTGLOBAL) Then
        'Format FILECONTENT als HGLOBAL
        Dim i As Integer
        For i=0 To nFiles-1
            fmtetc(index+i) = divfmtetc(3)
            fmtetc(index+i).lindex = i
            With stgmed(index+i)
                .tymed = TYMED_HGLOBAL
                .hGlobal = pContent(i)
                .pUnkForRelease = NULL
            End With
        Next i
    EndIf

Damit wäre der Teil schon fertig. Der Code in dem Beispielprojekt ist so geschrieben, dass man der DataTransfer - Funktion mit den Flags übergibt, welche Format(e) man will, die IF-Schleifen erstellen diese dann und übergeben ein Array mit den gewünschen Formaten dann an unser IDataObject. Wir machen aber jetzt gleich weiter und nehmen die ISTREAM-Variante des Formats. Das IStream-Interface ermöglicht es uns, die Datei als Datenstrom und nicht als kompletten GlobalMemory-Block zu übergeben, es werden also keine Resourcen belegt.

 

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