Was ist ein Stream? Stream ist, einfach ausgedrückt, eine abstrakte, objektorientierte Datenverarbeitung
Werkzeuge. Im Stream sind einige grundlegende Vorgänge zur Verarbeitung von Daten definiert, z. B. das Lesen von Daten, das Schreiben von Daten usw.
Der Programmierer führt alle Operationen am Stream aus, ohne sich um die tatsächliche Flussrichtung der Daten am anderen Ende des Streams zu kümmern. Fließen nicht
Es kann jedoch Dateien, dynamischen Speicher, Netzwerkdaten und andere Datenformen verarbeiten. wenn du recht hast
Die Verwendung von Streams in Programmen wird die Effizienz beim Schreiben von Programmen erheblich verbessern.
Im Folgenden verwendet der Autor vier Beispiele: EXE-Dateiverschlüsseler, elektronische Grußkarte, hausgemachtes OICQ und Netzwerkbildschirm
Übertragung zur Veranschaulichung der Verwendung von „Stream“ in der Delphi-Programmierung. Einige der Techniken in diesen Beispielen waren früher sehr weich
Das Geheimnis der Datei ist nicht öffentlich zugänglich, und jetzt kann jeder den Code direkt und kostenlos zitieren.
„Hohe Gebäude ragen aus dem Boden.“ Bevor wir die Beispiele analysieren, wollen wir zunächst die grundlegenden Konzepte und Konzepte der Strömung verstehen.
Funktionen: Erst nachdem wir diese grundlegenden Dinge verstanden haben, können wir mit dem nächsten Schritt fortfahren. Bitte verstehen Sie sorgfältig
diese grundlegenden Methoden. Wenn Sie bereits damit vertraut sind, können Sie diesen Schritt natürlich überspringen.
1. Grundkonzepte und Funktionsdeklarationen von Streams in Delphi
In Delphi ist die Basisklasse aller Stream-Objekte die TStream-Klasse, die die gemeinsamen Eigenschaften aller Streams definiert.
und Methoden.
Die in der TStream-Klasse definierten Eigenschaften werden wie folgt eingeführt:
1. Größe: Diese Eigenschaft gibt die Größe der Daten im Stream in Bytes zurück.
2. Position: Dieses Attribut steuert die Position des Zugriffszeigers im Fluss.
In Tstream sind vier virtuelle Methoden definiert:
1. Lesen: Diese Methode liest Daten aus dem Stream. Der Funktionsprototyp ist:
Funktion Read(var Buffer;Count:Longint):Longint;virtual;abstract;
Der Parameter Buffer ist der Puffer, der beim Lesen von Daten platziert wird. Count ist die Anzahl der zu lesenden Datenbytes.
Der Rückgabewert dieser Methode ist die tatsächliche Anzahl der gelesenen Bytes, die kleiner oder gleich dem in Count angegebenen Wert sein kann.
2. Schreiben: Diese Methode schreibt Daten in den Stream. Der Funktionsprototyp ist:
Funktion Write(var Buffer;Count:Longint):Longint;virtual;abstract;
Der Parameter Buffer ist der Puffer der Daten, die in den Stream geschrieben werden sollen, Count ist die Länge der Daten in Bytes,
Der Rückgabewert dieser Methode ist die Anzahl der tatsächlich in den Stream geschriebenen Bytes.
3. Suchen: Diese Methode implementiert die Bewegung des Lesezeigers im Stream. Der Funktionsprototyp ist:
Function Seek(Offset:Longint;Origint:Word):Longint;virtual;abstract;
Der Parameter Offset ist die Anzahl der Offset-Bytes und der Parameter Origin zeigt die tatsächliche Bedeutung von Offset und seine möglichen Werte an.
wie folgt:
soFromBeginning:Offset ist die Position des Zeigers vom Anfang der Daten nach der Verschiebung. Zu diesem Zeitpunkt muss der Offset erfolgen
Größer oder gleich Null.
soFromCurrent:Offset ist die relative Position des Zeigers nach der Bewegung und des aktuellen Zeigers.
soFromEnd:Offset ist die Position des Zeigers vom Anfang der Daten nach der Verschiebung. Zu diesem Zeitpunkt muss der Offset erfolgen
Kleiner oder gleich Null.
Der Rückgabewert dieser Methode ist die Position des Zeigers nach der Bewegung.
4. Setsize: Diese Methode ändert die Größe der Daten. Der Funktionsprototyp ist:
Funktion Setsize(NewSize:Longint);virtual;
Darüber hinaus sind in der TStream-Klasse auch mehrere statische Methoden definiert:
1. ReadBuffer: Die Funktion dieser Methode besteht darin, Daten von der aktuellen Position im Stream zu lesen. Der Funktionsprototyp ist:
PROcedure ReadBuffer(var Buffer;Count:Longint);
Die Definition der Parameter ist die gleiche wie oben gelesen. Hinweis: Wenn die Anzahl der gelesenen Datenbytes von der Anzahl der zu lesenden Bytes abweicht
Wenn die Zahlen unterschiedlich sind, wird eine EReadError-Ausnahme generiert.
2. WriteBuffer: Die Funktion dieser Methode besteht darin, Daten an der aktuellen Position in den Stream zu schreiben. Der Funktionsprototyp ist:
Prozedur WriteBuffer(var Buffer;Count:Longint);
Die Definition der Parameter ist die gleiche wie bei Write oben. Hinweis: Wenn die Anzahl der geschriebenen Datenbytes von der Anzahl der zu schreibenden Bytes abweicht
Wenn die Zahlen unterschiedlich sind, wird eine EWriteError-Ausnahme generiert.
3. CopyFrom: Diese Methode wird verwendet, um Datenströme aus anderen Streams zu kopieren. Der Funktionsprototyp ist:
Function CopyFrom(Source:TStream;Count:Longint):Longint;
Der Parameter Source ist der Stream, der Daten bereitstellt, und Count ist die Anzahl der kopierten Datenbytes. Wenn Count größer als 0 ist,
CopyFrom kopiert Count-Bytes an Daten von der aktuellen Position des Source-Parameters, wenn Count gleich 0 ist.
CopyFrom setzt die Position-Eigenschaft des Source-Parameters auf 0 und kopiert dann alle Daten der Source;
TStream verfügt über weitere abgeleitete Klassen, von denen die TFileStream-Klasse am häufigsten verwendet wird. Verwenden von TFileStream
Um mithilfe einer Klasse auf Dateien zuzugreifen, müssen Sie zunächst eine Instanz erstellen. Die Aussage lautet wie folgt:
Konstruktor Create(const Filename:string;Mode:Word);
Dateiname ist der Dateiname (einschließlich Pfad) und der Parameter Modus ist die Art und Weise, wie die Datei geöffnet wird, einschließlich der Art und Weise, wie die Datei geöffnet wird.
Offener Modus und gemeinsamer Modus. Ihre möglichen Werte und Bedeutungen sind wie folgt:
Offener Modus:
fmCreate: Erstellen Sie eine Datei mit dem angegebenen Dateinamen oder öffnen Sie die Datei, falls sie bereits vorhanden ist.
fmOpenRead: Öffnen Sie die angegebene Datei im schreibgeschützten Modus
fmOpenWrite: Öffnen Sie die angegebene Datei im schreibgeschützten Modus
fmOpenReadWrite: Öffnen Sie die angegebene Datei zum Schreiben
Freigabemodus:
fmShareCompat: Der Freigabemodus ist mit FCBs kompatibel
fmShareExclusive: Erlauben Sie anderen Programmen nicht, die Datei auf irgendeine Weise zu öffnen
fmShareDenyWrite: Anderen Programmen nicht erlauben, die Datei zum Schreiben zu öffnen
fmShareDenyRead: Anderen Programmen nicht erlauben, die Datei im Lesemodus zu öffnen
fmShareDenyNone: Andere Programme können die Datei auf beliebige Weise öffnen
TStream verfügt außerdem über eine abgeleitete Klasse TMemoryStream, die in tatsächlichen Anwendungen sehr häufig verwendet wird.
Sehr häufig. Es wird als Speicherstrom bezeichnet, was bedeutet, dass ein Stream-Objekt im Speicher erstellt wird. Es folgen die grundlegenden Methoden und Funktionen
Das Gleiche wie oben.
Nun, wenn die oben genannte Grundlage geschaffen ist, können wir unsere Programmierreise beginnen.
-------------------------------------------------- --------------------------
2. Praktische Anwendung eins: Verwenden von Streams zum Erstellen von EXE-Dateiverschlüsselern, Bundles, selbstextrahierenden Dateien und Installationsprogrammen
Lassen Sie uns zunächst darüber sprechen, wie man einen EXE-Dateiverschlüsseler erstellt.
Das Prinzip des EXE-Dateiverschlüsselers: Erstellen Sie zwei Dateien, eine dient zum Hinzufügen von Ressourcen zur anderen EXE-Datei
Im Inneren heißt es Add-In-Programm. Eine weitere hinzugefügte EXE-Datei wird Header-Datei genannt. Die Funktion dieses Programms ist
Lesen Sie die zu sich selbst hinzugefügten Dateien.
Die Struktur von EXE-Dateien unter Windows ist relativ komplex. Einige Programme verfügen auch über Prüfsummen, wenn Sie feststellen, dass diese geändert wurden
Später denken sie, dass sie mit dem Virus infiziert sind und weigern sich, ihn auszuführen. Also fügen wir die Datei zu unserem Programm hinzu,
Die ursprüngliche Dateistruktur wird dadurch nicht verändert. Schreiben wir zunächst eine Additionsfunktion. Die Funktion dieser Funktion besteht darin, hinzuzufügen
Eine Datei wird als Stream an das Ende einer anderen Datei angehängt. Die Funktion ist wie folgt:
Funktion Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Ziel,Quelle:TFileStream;
MyFileSize:integer;
beginnen
versuchen
Source:=TFileStream.Create(SourceFile,fmOpenRead oder fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite oder fmShareExclusive);
versuchen
Target.Seek(0,soFromEnd);//Ressourcen am Ende hinzufügen
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);// Berechnen Sie die Ressourcengröße und schreiben Sie sie an das Ende des Hilfsprozesses
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
Endlich
Ziel.Frei;
Quelle.Frei;
Ende;
außer
Ergebnis:=Falsch;
Ausfahrt;
Ende;
Ergebnis:=True;
Ende;
Mit der oben genannten Grundlage sollten wir diese Funktion leicht verstehen. wo der Parameter SourceFile ist
Die hinzuzufügende Datei, der Parameter TargetFile ist die hinzuzufügende Zieldatei. Fügen Sie beispielsweise eine.exe hinzu
In b.exe können Sie Folgendes tun: Cjt_AddtoFile('a.exe',b.exe'); wenn das Hinzufügen erfolgreich ist, geben Sie andernfalls True zurück
Gibt false zurück.
Basierend auf der obigen Funktion können wir die entgegengesetzte Lesefunktion schreiben:
Funktion Cjt_LoadFromFile(SourceFile,TargetFile:string):Boolean;
var
Quelle:TFileStream;
Ziel:TMemoryStream;
MyFileSize:integer;
beginnen
versuchen
Target:=TMemoryStream.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead oder fmShareDenyNone);
versuchen
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//Ressourcengröße lesen
Source.Seek(-MyFileSize,soFromEnd);//Lokalisieren Sie den Ressourcenspeicherort
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//Ressourcen entfernen
Target.SaveToFile(TargetFile);//In Datei speichern
Endlich
Ziel.Frei;
Quelle.Frei;
Ende;
außer
Ergebnis:=false;
Ausfahrt;
Ende;
Ergebnis:=true;
Ende;
Der Parameter SourceFile ist der Dateiname der hinzugefügten Datei und der Parameter TargetFile ist der herausgenommene Dateiname.
Der Zieldateiname, der nach der Datei gespeichert werden soll. Beispiel: Cjt_LoadFromFile('b.exe','a.txt'); in b.exe
Nehmen Sie die Datei heraus und speichern Sie sie als.txt. Wenn die Extraktion erfolgreich ist, wird True zurückgegeben, andernfalls wird False zurückgegeben.
Öffnen Sie Delphi, erstellen Sie ein neues Projekt und fügen Sie ein Bearbeitungssteuerelement Edit1 und zwei Schaltflächen in das Fenster ein:
Button1 und Button2. Die Caption-Eigenschaft der Schaltfläche ist auf „OK“ bzw. „Abbrechen“ gesetzt. existieren
Schreiben Sie Code in das Click-Ereignis von Button1:
var S: string;
beginnen
S:=ChangeFileExt(application.ExeName,'.Cjt');
wenn Edit1.Text='790617' dann
beginnen
Cjt_LoadFromFile(Application.ExeName,S);
{Nehmen Sie die Datei heraus, speichern Sie sie im aktuellen Pfad und nennen Sie sie „Originaldatei.Cjt“}
Winexec(pchar(S),SW_Show);{Run „original file.Cjt“}
Application.Terminate;{Programm beenden}
Ende
anders
Application.MessageBox('Passwort ist falsch, bitte erneut eingeben!', 'Passwort ist falsch', MB_ICONERROR+MB_OK);
Kompilieren Sie dieses Programm und benennen Sie die EXE-Datei in head.exe um. Erstellen Sie eine neue Textdatei head.rc,
Der Inhalt lautet: head exefile head.exe, dann kopieren Sie sie in das BIN-Verzeichnis von Delphi und führen Sie sie aus
Der Dos-Befehl Brcc32.exe head.rc generiert eine head.res-Datei. Diese Datei ist das, was wir wollen
Bewahren Sie Ressourcendateien zuerst auf.
Unsere Header-Datei wurde erstellt. Lassen Sie uns das Add-In-Programm erstellen.
Erstellen Sie ein neues Projekt und fügen Sie die folgenden Steuerelemente ein: ein Bearbeiten, ein Opendialog und zwei Button1
Die Beschriftungseigenschaften sind auf „Datei auswählen“ bzw. „Verschlüsselt“ eingestellt.
Fügen Sie im Quellprogramm einen Satz hinzu: {$R head.res} und kopieren Sie die Datei head.res in das aktuelle Verzeichnis des Programms.
Auf diese Weise wird die head.exe gerade zusammen mit dem Programm kompiliert.
Schreiben Sie den Code in das Cilck-Ereignis von Button1:
if OpenDialog1.Execute then Edit1.Text:=OpenDialog1.FileName;
Schreiben Sie den Code in das Cilck-Ereignis von Button2:
var S:String;
beginnen
S:=ExtractFilePath(Edit1.Text);
if ExtractRes('exefile','head',S+'head.exe') dann
if Cjt_AddtoFile(Edit1.Text,S+'head.exe') dann
wenn DeleteFile(Edit1.Text) dann
if RenameFile(S+'head.exe',Edit1.Text) dann
Application.MessageBox('Dateiverschlüsselung erfolgreich!','Message',MB_ICONINFORMATION+MB_OK)
anders
beginnen
if FileExists(S+'head.exe') then DeleteFile(S+'head.exe');
Application.MessageBox('Dateiverschlüsselung fehlgeschlagen!','Message',MB_ICONINFORMATION+MB_OK)
Ende;
Ende;
Unter diesen ist ExtractRes eine benutzerdefinierte Funktion, die zum Extrahieren von head.exe aus der Ressourcendatei verwendet wird.
Funktion ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res: TResourceStream;
beginnen
versuchen
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
versuchen
Res.SavetoFile(ResNewName);
Ergebnis:=true;
Endlich
Res.Frei;
Ende;
außer
Ergebnis:=false;
Ende;
Ende;
Hinweis: Unsere obige Funktion hängt einfach eine Datei an das Ende einer anderen Datei an.
In tatsächlichen Anwendungen kann das Hinzufügen mehrerer Dateien geändert werden, sofern der Versatz entsprechend der tatsächlichen Größe und Anzahl definiert wird
Die Adresse reicht aus. Ein Datei-Bundler fügt beispielsweise zwei oder mehr Programme zu einer Header-Datei hinzu.
In. Die Prinzipien selbstextrahierender Programme und Installationsprogramme sind dieselben, jedoch mit stärkerer Komprimierung.
Wir können beispielsweise auf eine LAH-Einheit verweisen, den Stream komprimieren und ihn dann hinzufügen, sodass die Datei kleiner wird.
Dekomprimieren Sie es einfach, bevor Sie es vorlesen.
Darüber hinaus weist das Beispiel des EXE-Verschlüsselungsprogramms im Artikel noch viele Mängel auf. Beispielsweise ist das Passwort behoben
„790617“, nachdem Sie die EXE-Datei herausgenommen und ausgeführt haben, sollten Sie warten, bis die Ausführung abgeschlossen ist, und sie dann löschen usw. Leser können sie selbst ändern.