<< | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Demo "ExtractMSOfficeFiles" | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
OLE2-Objekte in OLE-Feldern von Microsoft Access | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(Demo-MDB) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
In dieser Demo-MDB werden Microsoft Office Dateien entpackt, die als OLE-Objekte in OLE-Feldern gespeichert wurden. Das Beispiel zeigt, wie OLE2-Objekte aufgebaut sind, wo sie im OLE1Stream abgelegt werden und wie man auf sie zugreifen kann.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Diese Dokumentation behandelt die Microsoft Object Linking and Embedding (OLE) Data Structures nach OLE2 und das Microsoft Compound Binary File Format. Diese Formate sind hervorragend von Microsoft dokumentiert. Um eine vollständige Darstellung dieser Formate zu erhalten, ist es ratsam, sich mit der originalen Dokumentation von Microsoft zu beschäftigen. Diese Dokumentation hier dient nur dem besseren Verständnis der Dateninhalte der OLE-Felder von Microsoft Access.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bei meinen Erklärungen gehe ich davon aus, dass die Informationen, die meine Beispiele DocfileViewer (Compound File) und ReduceToPresentationPicture (OLE1-Objekte) vermitteln, bereits bekannt sind.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
OLE2-Objekte verwenden das Compound File Format. Die Daten der OLE-Objekte werden dabei als Streams in einem Compound File gespeichert. Es gibt dort prinzipiell dieselben Bereiche wie bei den OLE1-Objekten. Jeder Bereich erhält (min.) einen eigenen Stream. Das gilt auf jeden Fall für die Bereiche Container-Application-Data, Creating-Application-Data und Presentation-Data. Ob es einen einzelnen Stream gibt, der dem Bereich der nativen Daten entspricht, ist vor allem abhängig von der Ausgangssituation. Verschiedene Programme speichern ihre Daten selber im Compound File Format. Wird eine derartige Datei als OLE-Objekt eingefügt, dann werden die zusätzlichen OLE-Streams in das bereits bestehende Compound File eingetragen. Wenn aber eine Datei eingefügt wird, die nicht auf dem Compound File Format basiert, dann muss für das OLE2-Objekt ein eigenes Compound File angelegt werden. Dort könnten dann die nativen Daten in einem einzelnen Stream abgelegt sein, der dem Bereich der nativen Daten der OLE1-Objekte entspricht. Wie dieser Stream aufgebaut ist und welche Daten er enthält, wird aber von der Ersteller-Anwendung vorgegeben. Nur für den Fall, dass OLE1-Objekte zu OLE2-Objekten konvertiert werden müssen, gibt es einen speziellen Stream im OLE-Format, der direkt dem Bereich der nativen Daten der OLE1-Objekte entspricht.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Snapshot- und PDF-Dateien basieren z.B. nicht auf dem Compound File Format. Werden solche Dateien als OLE2-Objekte gespeichert, dann wird ein Stream erstellt, der als native Daten den Inhalt der eingefügten Datei enthält. Bei komprimierten Grafikformaten kann man dagegen nicht davon ausgehen, dass als native Daten der Inhalt der eingefügten Datei gespeichert wird. Komprimierte Grafikformate existieren nicht auf dem Bildschirm, dort gibt es immer nur Bitmaps. Eine JPG-Datei ist im Prinzip ein Bitmap, das sich in einem Zip-Archiv befindet. So etwas kann man gut speichern oder transferieren, aber man kann es sich nicht ansehen. Bevor man das Bitmap ansehen kann, muss es erst wieder entpackt werden. Wenn ein Grafikprogramm ein OLE-Objekt bearbeiten soll, dann ist das ein Vorgang der immer auf dem Bildschirm stattfindet. Da man dort kein komprimiertes Format gebrauchen kann, wird in den OLE-Objekten meistens ein unkomprimiertes Format verwendet. Wenn man z.B. eine JPG-Datei mit Hilfe der OLE-Schnittstelle des Microsoft Photo Editor als OLE-Objekt speichert, dann wird im Stream der nativen Daten nicht der Inhalt der JPG-Datei abgelegt, sondern man findet dort die Grafik als Bitmap vor.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Die Programme Word, Excel und Power Point verwenden selber das Compound File Format zum Speichern ihrer Daten. Als native Daten der Ersteller-Anwendung wird das gesamte Compound File übergeben, in das dann die zusätzlichen Streams des OLE-Objekts eingetragen werden. Der Aufbau der nativen Daten in solchen OLE2-Objekten entspricht dem speziellen Format der jeweiligen Ersteller-Anwendung. Ggf. kann man dort auch weitere OLE2-Objekte vorfinden, die aber zu den nativen Daten gehören.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Möchte man eine Datei entpacken, die nicht auf dem Compound File Format basiert, dann muss man den Stream der nativern Daten isolieren und ihn als Datei speichern. Möchte man dagegen eine Datei entpacken, die selber das Compound File Format verwendet, dann muss man die zusätzlichen OLE-Streams entfernen und den Rest als Datei speichern. Da die Streams durch ihre Namen identifiziert werden, müssen diese natürlich bekannt sein. Teilweise werden die Namen durch das OLE-Format vorgegeben. Da aber die Ersteller-Anwendung und die Container-Anwendung ihre eigenen Streams in das Compound File des OLE2-Objekts eintragen, muss auch immer der spezielle Aufbau jeder einzelnen Klasse bekannt sein. Am wichtigsten sind aber natürlich die Standardbereiche des OLE-Formats.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dem Bereich der Container-Application-Data entspricht der Stream mit dem Namen chr(3) & "AccessObjSiteData". Der Stream besteht aus 56 Bytes. Der Inhalt scheint immer gleich (uninteressant) zu sein.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dem Bereich der Creating-Application-Data entspricht der Stream mit dem Namen chr(1) & "Ole". In diesem Stream wird angegeben, ob das Objekt verknüpft oder eingebettet ist. Bei eingebetteten Objekten werden dort keine weiteren Informationen gespeichert. Eine Größenangabe zu den nativen Daten macht für ein Compound File an dieser Stelle keinen Sinn und der Klassenname wird als CLSID im Root-Directory-Entry des Compound Files hinterlegt. Bei verknüpften Objekten enthält der Stream dagegen alle Informationen über das verknüpfte Objekt.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dem Bereich der Presentation-Data entsprechen die Streams mit den Namen chr(2) & "OlePres000" und chr(2) & "OlePres001". Gemäß dem OLE-Format wären bis zu 999 dieser OlePresentationStreams zulässig. In den OLE-Feldern von Access habe ich aber bisher nur diese beiden Varianten vorgefunden. Der Stream "OlePres000" enthält das Präsentationsbild im Windows Metafile Format. Der Stream "OlePres001" enthält das Präsentationsbild im Enhanced Metafile Format. Bei den meisten Klassen sind beide Streams vorhanden.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dem Bereich der Nativen Daten entspricht eigentlich nur der Stream mit dem Namen chr(1) & "Ole10Native", den es aber nur für die Konvertierung von OLE1- zu OLE2-Objekten gibt. In allen anderen Fällen werden die nativen Daten von der Ersteller-Anwendung in einem oder mehreren Streams im Compound File abgelegt. Werden Dateien eingefügt, die nicht auf dem Compound File Format basieren, dann werden die nativen Daten z.B. in einem Stream mit dem Namen "CONTENTS" gespeichert. Bei Dateien, die selber auf dem Compound File Format basieren, muss man wissen welche Streams zu den nativen Daten gehören. Ein Stream mit dem Namen chr(1) & "CompObj" könnte z.B. sowohl zu den OLE-Objekt-Daten, als auch zu den nativen Daten der eingefügten Datei gehören. Man muss auch beachten, dass solche Dateien möglicherweise selber OLE-Objekte beinhalten. Die zusätzlichen Streams eines OLE-Objekts befinden sich immer im Root-Verzeichnis des Compound Files.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dieses Demo zeigt, wie man verschiedene Microsoft Office Dateien entpacken kann. Word, Excel und Power Point Dateien basieren (bis Office 2003) auf dem Compound File Format und werden als OLE2-Objekte ausgetauscht. Access speichert OLE-Objekte im OLE1-Format, OLE2-Objekte werden dabei im Bereich der nativen Daten des OLE1Streams eingebettet. Zuerst muss man also das OLE2-Objekt aus diesem Bereich isolieren. Das OLE2-Objekt ist dann ein Compound File (Structured Storage), in dem sich neben den ursprünglichen Streams der eingefügten Datei die zusätzlichen Streams des OLE2-Objekts befinden. Die zusätzlichen Streams müssen gelöscht werden, was letztlich bedeutet, dass man mit den restlichen Streams ein neues Compound File erstellt. Das reduzierte Compound File entspricht dann wieder der eingefügten Datei und kann gespeichert werden. Es müssen die Streams "\1Ole", "\2OlePres000", "\2OlePres001" und "\3AccessObjSiteData" entfernt werden, die sich im Root-Verzeichnis des OLE2-Objekts befinden.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Die Vorgänge im ersten Teil des Codes wurden bereits in meinen Beispielen DocfileViewer und ReduceToPresentationPicture angesprochen. An Stelle der Presentation-Data werden hier jetzt die nativen Daten des OLE1-Objekts als Bytestream geladen. Der Bytestream beinhaltet damit das Compound File des OLE2-Objekts auf dessen Inhalt dann zugegriffen wird. Während im DocfileViewer-Demo gezeigt wurde, wie Compound Files aufgebaut sind und wie man sie auslesen kann, wird in diesem Demo nun gezeigt, wie man selber ein Compound File erstellen kann. Die verwendeten Strukturen wurden bereits in den beiden anderen Beispielen beschrieben, der benötigte Deklarationsbereich sieht wie folgt aus.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Die nachfolgende Funktion isoliert das Compound File eines OLE2-Objekts aus einem OLE1Stream, der in einem OLE-Feld gespeichert wurde. Als Parameter wird das OLE-Feld mit dem OLE-Objekt übergeben. Außerdem wird der Dateiname erwartet, unter dem die entpackte Office Datei gespeichert werden soll.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Mit der folgenden Funktion werden die Container-Application-Data von Microsoft Access geladen, wenn sie vorhanden sind.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bevor ich zur eigentlichen Funktion zum Wiederherstellen der Office Dateien komme, möchte ich zuerst noch eine Unterfunktion ansprechen. Bei der Wiederherstellung wird das Verzeichnis-Array des Compound Files des OLE2-Objekts eingelesen, wobei die Einträge der zusätzlichen OLE-Streams ignoriert werden. Das Verzeichnis-Array enthält danach alle Einträge, die in das neue Compound File übernommen werden sollen. Da jetzt aber einige der ursprünglichen Einträge fehlen, sind Lücken in der Binärbaum-Verkettung entstanden, mit deren Hilfe die Verzeichnisstruktur codiert wurde. Bevor das neue Verzeichnis-Array verwendet werden kann, muss erst noch die Binärbaum-Verkettung reorganisiert werden. Das Thema "Binärbaum" ist sehr interessant und umfangreich, wer sich damit beschäftigen will, sollte sich an anderer Stelle informieren. Ich behandle das Ganze hier nur sehr spartanisch. Die Verkettung der Einträge erfolgt gemäß dem Compound File Format als Red-Black-Tree. Ich ignoriere die Farbwerte und setze sie beim Schreiben auf black. Das ist zulässig und entspricht der Behandlung eines einfachen Binärbaums.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Für die Verkettung der Verzeichnis-Array-Einträge stehen 3 Variablen pro Eintrag zur Verfügung. Zwei dieser Variablen verweisen auf Geschwister-Einträge, die dritte Variable verweist auf einen Kind-Eintrag. Die Verzeichnis-Array-Einträge (die ich behandle) stehen entweder für einen Stream oder für ein Verzeichnis. Der erste Eintrag im Verzeichnis-Array steht für das Root-Verzeichnis des Compound Files. Das Root-Verzeichnis ist die Wurzel der Baumstruktur und besitzt selber keine Geschwister, kann aber Kinder beinhalten. In der Kind-Variablen des Root-Eintrags wird der Index des ersten Kind-Eintrags gespeichert, der dem Root-Verzeichnis untergeordnet ist. Natürlich kann das Root-Verzeichnis auch mehrere Kinder enthalten. Alle weiteren Kinder sind dann Geschwister des ersten Kindes. Besitzt ein Kind-Eintrag Geschwister, dann wird der Index des nächsten Geschwister-Eintrags in einer seiner beiden Geschwister-Variablen abgelegt. Einem Eintrag kann ein linker und ein rechter Geschwister-Eintrag zugeordnet werden. Der rechte Eintrag ist dabei immer größer und der linke Eintrag immer kleiner. Die Größe der Einträge ergibt sich aus ihren Namen. Zuerst ist dafür die Anzahl der Zeichen entscheidend, bei gleicher Länge werden die Namen dann binär verglichen. In einem Verzeichnis dürfen Namen nicht doppelt vorkommen.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Zusammenfassend kann man sagen, alle Kind-Einträge die sich in einem Verzeichnis befinden sind über ihre Geschwister-Variablen miteinander verkettet. Ein Verzeichnis-Eintrag kennt immer nur einen seiner Kind-Einträge und ist mit ihm über seine Kind-Variable verkettet. Die Verkettung der Ebenen erfolgt also über die Kind-Variablen und die Verkettung der Elemente einer Ebene über die Geschwister-Variablen. Sucht man nach einem Namen in einem Verzeichnis, dann fängt man mit dem Eintrag an, auf den die Kind-Variable des Verzeichnis-Eintrags verweist. Entspricht dieser Eintrag nicht dem gesuchten Namen, dann geht man zum nächsten Geschwister-Eintrag weiter. Da man ja weiß, ob der Name den man sucht, größer oder kleiner ist als der Name des aktuellen Eintrages, weiß man auch, ob man mit dem linken oder mit dem rechten Geschwister-Eintrag weitermachen muss. Man wiederholt dann das Ganze so lange, bis man den gesuchten Namen gefunden hat. Gibt es keinen Geschwister-Eintrag mehr, dann ist die entsprechende Geschwister-Variable als EndOfChain gekennzeichnet. Möchte man einen neuen Eintrag in den Binärbaum aufnehmen, dann sucht man auf dieselbe Weise nach dem ersten freien Platz in der Kette. Man fängt also mit dem ersten Kind-Eintrag an. Da es keine doppelten Namen geben darf, ist der Name des Kind-Eintrags entweder größer oder kleiner als der Name des neuen Eintrags und man weiß damit, ob es mit dem linken oder rechten Geschwister-Eintrag weiter gehen muss. Man folgt der Kette auf diesem Weg so lange, bis man zu einem Eintrag kommt, dessen entsprechende Geschwister-Variable als EndOfChain gekennzeichnet ist. Dort wird dann der Index des neuen Eintrags eingetragen.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Den Index des Verzeichnis-Arrays nennt man SID, das englische Wort für Geschwister ist Siblings und für Kind natürlich Child. Daraus ergeben sich die Namen der Variablen des Binärbaums in den Verzeichnis-Array-Einträgen. 'sidLeftSib' und 'sidRightSib' sind die Geschwister-Variablen und 'sidChild' ist die Kind-Variable.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Da ich die Binärbaum-Verkettung nicht besonders vorteilhaft finde, wenn es darum geht gezielt auf Elemente einer Ebenen zuzugreifen, bilde ich das Verzeichnis-Array nochmal in einem zweidimensionalen Typen-Array ab. Die erste Dimension beinhaltet dabei Informationen zu den Verzeichnissen und die zweite Dimension zu den Elementen der Verzeichnisse.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Zum Einlesen des Binärbaums benutze ich zwei rekursive Prozeduren. Die erste Prozedur liest die Informationen zu den Verzeichnissen ein und die zweite die Informationen zu den Elementen. ReadStorageDirectory() wird zuerst mit dem SID 0 für das Root-Verzeichnis aufgerufen. Das Verzeichnis wird in der 1.Dimension registriert und danach die Prozedur ReadDirectoryNode() aufgerufen, wobei der SID des ersten Kind-Eintrags übergeben wird. Dort wird dann der Kind-Eintrag in der 2.Dimension registriert, wenn er nicht zu den Einträgen gehört, die ignoriert werden sollen. Anschließend wird überprüft, ob der Eintrag Geschwister-Einträge besitzt. Existieren weitere Geschwister-Einträge, dann ruft sich die Prozedur selber auf und übergibt dabei den SID des Geschwister-Eintrags. Der Vorgang wiederholt sich, bis alle Einträge des aktuellen Verzeichnisses eingelesen wurden. Danach geht es wieder in ReadStorageDirectory() weiter. Dort wird nun das Array mit den Einträgen durchlaufen. Handelt es sich bei einem der Einträge um ein Verzeichnis, dann ruft sich die Prozedur erneut selber auf und übergibt dabei den SID dieses Eintrags. Die Vorgänge wiederholen sich so lange, bis die gesamte Baumstruktur durchlaufen wurde.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Mit der folgenden Prozedur wird das neue Verzeichnis-Array erstellt und die Einträge im Binärbaum verkettet. Als Kind-Eintrag verwende ich jeweils das erste Element im Array, die restlichen Einträge werden dann entsprechend ihrer Größe an diesen angehängt. Man könnte natürlich auch die optimale Baumstruktur der Einträge ermitteln, was dann der Fall wäre, wenn so viele Einträge wie möglich je zwei Geschwister-Einträge besitzen würden. Ich nehme hier aber die Einträge so wie sie kommen, das ist für die Office-Dateien ausreichend. Wenn alle Geschwister-Einträge verkettet wurden, werden die SIDs der 1.Kind-Einträge in den Verzeichnis-Array-Einträgen der Ebenen (Verzeichnisse) hinterlegt.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Nun komme ich also zu der Funktion mit der die Office-Dateien wiederhergestellt werden. Das Compound File des OLE2-Objekts wird übergeben und das Verzeichnis-Array wird unter Ausschluss der zusätzlichen OLE-Streams eingelesen und reorganisiert. Dann wird das neue Verzeichnis-Array durchlaufen, alle benötigten Streams aus dem OLE2-Objekt ausgelesen und entsprechend ihrer Größen entweder an den neuen Ministream oder den neuen Datenstream angehängt. Da sich die Sektorengröße nicht geändert hat, können die Streams so wie sie sind verwendet werden. Durch diesen Vorgang werden die Streams auch gleich defragmentiert. Danach wird die benötigte MiniFAT bereitgestellt und der Ministream an die Sektorengröße des Compound Files angepasst. Anschließend wird die FAT erstellt und entsprechend der FAT die DIF. Da die FAT auch die FAT-Sektoren und die zusätzlichen DIF-Sektoren beinhaltet, muss ihre richtige Größe schrittweise ermittelt werden. Das gilt entsprechend für die DIF. Nachdem diese Einheiten alle in der richtigen Größe bereitgestellt wurden, wird mit der Verkettung begonnen. Zuerst wird die DIF verkettet, dann kommt die FAT dran. In die FAT muss die DIF, die FAT selber, das Verzeichnis-Array, die MiniFAT und der Ministream registriert werden. Abschließend werden dann die einzelnen Streams, entweder in der FAT oder der MiniFAT verkettet und jeweils der erste Sektor im zugehörigen Verzeichnis-Array-Eintrag hinterlegt. Die Einzelteile des neuen Compound Files stehen danach in korrekter Form bereit und können in einer Datei gespeichert werden.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Mit der nachfolgenden Unterfunktion wird ein Stream aus seinen Sektoren zusammengesetzt. Die Größe des Streams entscheidet, ob die Sektoren im Ministream liegen oder ob es sich um Sektoren des Compound File handelt.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Damit bin ich am Ende angekommen und hoffe, dass ich mit meinem Beispiel diesem komplexen Thema einigermaßen gerecht werden konnte. Auch wenn es hier um eine Funktion für Microsoft Access geht, stehen doch im Wesentlichen die OLE-Objekte und das Compound File Format im Vordergrund. Sinnvollerweise verwende ich bei der Neuerstellung des Compound Files verschiedene Daten direkt aus dem Compound File des OLE2-Objekts. Wenn man alle Daten selber bereitstellt, kann man auf dieselbe Weise auch Compound Files (Struktured Storage) mit beliebigem Inhalt erzeugen. Beim Wiederherstellen der eingebetteten Microsoft Office Dateien werden diese automatisch defragmentiert. Bei den Excel-Dateien muss man noch die Mappe einblenden, dafür suche ich die entsprechende Struktur im Workbook-Stream und setze dort ein entsprechendes Bit auf 0. Diese Funktion und der Workbook-Stream gehören aber nicht zu dieser Dokumentation. Sie können sich das vollständige Demo herunterladen, dann steht Ihnen ein lauffähiges Beispiel zur Verfügung.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Download für Access 2002/2003 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ExtractMSOfficeFiles-Demo (Access 2002) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Download für Access 2000 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ExtractMSOfficeFiles-Demo (Access 2000) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(downgrade) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Download für Access 97 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ExtractMSOfficeFiles-Demo (Access 97) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(downgrade) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Installation: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Einfach runterladen und anschauen. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|