Sprechen wir über die Anwendung von „Flow“ in der Delphi-Programmierung
Chen Jingtao
Was ist ein Stream? Einfach ausgedrückt ist Stream ein abstraktes Datenverarbeitungstool, das auf objektorientierter Datenverarbeitung basiert. 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 Vorgänge am Stream aus, ohne sich um die tatsächliche Flussrichtung der Daten am anderen Ende des Streams zu kümmern. Streams können nicht nur Dateien, sondern auch dynamischen Speicher, Netzwerkdaten und andere Datenformen verarbeiten. Wenn Sie sich mit Stream-Operationen sehr gut auskennen und die Vorteile von Streams in Ihrem Programm nutzen, wird die Effizienz beim Schreiben von Programmen erheblich verbessert.
Im Folgenden verwendet der Autor vier Beispiele: EXE-Dateiverschlüsseler, elektronische Grußkarte, selbst erstelltes OICQ und Netzwerkbildschirmübertragung, um die Verwendung von „Stream“ in der Delphi-Programmierung zu veranschaulichen. Einige der Techniken in diesen Beispielen waren einst Geheimnisse vieler Software und nicht für die Öffentlichkeit zugänglich. 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 Funktionen des Flusses verstehen. Erst nachdem wir diese grundlegenden Dinge verstanden haben, können wir mit dem nächsten Schritt fortfahren. Bitte stellen Sie sicher, dass Sie diese grundlegenden Methoden sorgfältig verstehen. 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 und Methoden aller Streams definiert.
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. Der Rückgabewert dieser Methode ist die tatsächliche Anzahl der gelesenen Bytes, die kleiner oder gleich dem in angegebenen Wert sein kann Zählen.
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 und 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 gibt die tatsächliche Bedeutung von Offset an. Seine möglichen Werte sind wie folgt:
soFromBeginning:Offset ist die Position des Zeigers vom Anfang der Daten nach der Verschiebung. Zu diesem Zeitpunkt muss der Offset größer oder gleich Null sein.
soFromCurrent:Offset ist die relative Position des Zeigers nach der Bewegung und des aktuellen Zeigers.
soFromEnd:Offset ist die Position des Zeigers vom Ende der Daten nach der Verschiebung. Zu diesem Zeitpunkt muss der Offset kleiner oder gleich Null sein. 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 nicht mit der Anzahl der zu lesenden Bytes übereinstimmt, 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 nicht mit der Anzahl der zu schreibenden Bytes übereinstimmt, 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, kopiert CopyFrom Count-Datenbytes von der aktuellen Position des Source-Parameters; wenn Count gleich 0 ist, setzt CopyFrom 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. Um die TFileStream-Klasse für den Zugriff auf Dateien zu verwenden, 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 des Dateiöffnungsmodus und des Freigabemodus. Seine 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. Es wird als Speicherstrom bezeichnet, was bedeutet, dass ein Stream-Objekt im Speicher erstellt wird. Die grundlegenden Methoden und Funktionen sind dieselben 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 wird zum Hinzufügen von Ressourcen zur anderen EXE-Datei verwendet, die als Add-In-Programm bezeichnet wird. Eine weitere hinzugefügte EXE-Datei wird Header-Datei genannt. Die Funktion dieses Programms besteht darin, die ihm hinzugefügten Dateien zu lesen. Die EXE-Dateistruktur unter Windows ist relativ komplex, und einige Programme verfügen auch über Prüfsummen. Wenn sie feststellen, dass sie geändert wurden, denken sie, dass sie mit einem Virus infiziert sind, und verweigern die Ausführung. Daher fügen wir die Datei zu unserem eigenen Programm hinzu, sodass die ursprüngliche Dateistruktur nicht geändert wird. Schreiben wir zunächst eine Additionsfunktion. Die Funktion dieser Funktion besteht darin, eine Datei als Stream am Ende einer anderen Datei hinzuzufügen. 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. Der Parameter SourceFile ist die hinzuzufügende Datei und der Parameter TargetFile ist die hinzuzufügende Zieldatei. Um beispielsweise a.exe zu b.exe hinzuzufügen: Cjt_AddtoFile('a.exe',b.exe'); Wenn das Hinzufügen erfolgreich ist, wird True zurückgegeben, andernfalls wird False zurückgegeben.
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 Name der Datei, zu der die Datei hinzugefügt wurde, und der Parameter TargetFile ist der Name der Zieldatei, die nach dem Herausnehmen der Datei gespeichert wird. Beispielsweise entnimmt Cjt_LoadFromFile('b.exe','a.txt'); die Datei in b.exe und speichert sie als a.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. 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 mit dem Inhalt: head exefile head.exe, kopieren Sie sie dann in das BIN-Verzeichnis von Delphi, führen Sie den Dos-Befehl Brcc32.exe head.rc aus und es wird eine head.res-Datei generiert file ist Behalten Sie zuerst die gewünschten Ressourcendateien bei.
Unsere Header-Datei wurde erstellt. Lassen Sie uns das Add-In-Programm erstellen.
Erstellen Sie ein neues Projekt und platzieren Sie die folgenden Steuerelemente: ein Edit- und ein Opendialog-Element, und die Caption-Eigenschaften der beiden Button1s sind auf „Select File“ bzw. „Encryption“ festgelegt. 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 die Offset-Adresse entsprechend der tatsächlichen Größe und Anzahl definiert wird. Ein Datei-Bundler fügt beispielsweise zwei oder mehr Programme zu einer Header-Datei hinzu. 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 immer noch viele Mängel auf. Beispielsweise ist das Kennwort auf „790617“ festgelegt, und nachdem die EXE-Datei herausgenommen und ausgeführt wurde, sollte sie nach Abschluss der Ausführung gelöscht werden usw . Leser können es selbst ändern.
-------------------------------------------------- -------------------
3. Praktische Anwendung 2: Verwenden von Streams zum Erstellen ausführbarer E-Grußkarten
Wir sehen oft eine Software zur Erstellung von E-Grußkarten, die es Ihnen ermöglicht, Bilder selbst auszuwählen und dann eine ausführbare EXE-Datei für Sie zu generieren. Wenn Sie die Grußkarte öffnen, wird das Bild angezeigt, während Musik abgespielt wird. Nachdem wir nun Stream-Operationen gelernt haben, können wir auch eine erstellen.
Beim Hinzufügen von Bildern können wir die vorherige Cjt_AddtoFile direkt verwenden. Jetzt müssen wir nur noch die Bilder auslesen und anzeigen. Wir können das vorherige Cjt_LoadFromFile verwenden, um das Bild zuerst auszulesen, es als Datei zu speichern und es dann zu laden. Es gibt jedoch eine einfachere Möglichkeit, den Dateistream direkt zu lesen und mit dem leistungsstarken Tool Stream anzuzeigen , alles wird einfach.
Die beliebtesten Bilder sind heutzutage das BMP-Format und das JPG-Format. Wir werden nun Lese- und Anzeigefunktionen für diese beiden Arten von Bildern schreiben.
Funktion Cjt_BmpLoad(ImgBmp:TImage;SourceFile:String):Boolean;
var
Quelle:TFileStream;
MyFileSize:integer;
beginnen
Source:=TFileStream.Create(SourceFile,fmOpenRead oder fmShareDenyNone);
versuchen
versuchen
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//Ressourcen lesen
Source.Seek(-MyFileSize,soFromEnd);//Suchen Sie die Startposition der Ressource
ImgBmp.Picture.Bitmap.LoadFromStream(Source);
Endlich
Quelle.Frei;
Ende;
außer
Ergebnis:=Falsch;
Ausfahrt;
Ende;
Ergebnis:=True;
Ende;
Das Obige ist eine Funktion zum Lesen von BMP-Bildern und das Folgende ist eine Funktion zum Lesen von JPG-Bildern. Da die JPG-Einheit verwendet wird, muss dem Programm der Satz „uses jpeg“ hinzugefügt werden.
Funktion Cjt_JpgLoad(JpgImg:Timage;SourceFile:String):Boolean;
var
Quelle:TFileStream;
MyFileSize:integer;
Myjpg: TJpegImage;
beginnen
versuchen
Myjpg:= TJpegImage.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead oder fmShareDenyNone);
versuchen
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));
Source.Seek(-MyFileSize,soFromEnd);
Myjpg.LoadFromStream(Source);
JpgImg.Picture.Bitmap.Assign(Myjpg);
Endlich
Quelle.Frei;
Myjpg.free;
Ende;
außer
Ergebnis:=false;
Ausfahrt;
Ende;
Ergebnis:=true;
Ende;
Mit diesen beiden Funktionen können wir ein Ausleseprogramm erstellen. Nehmen wir als Beispiel BMP-Bilder:
Führen Sie Delphi aus, erstellen Sie ein neues Projekt und platzieren Sie ein Bildanzeigesteuerelement Image1. Schreiben Sie einfach den folgenden Satz in das Create-Ereignis des Fensters:
Cjt_BmpLoad(Image1,Application.ExeName);
Dies ist die Header-Datei, und dann verwenden wir die vorherige Methode, um eine head.res-Ressourcendatei zu generieren.
Jetzt können wir mit der Erstellung unseres Zusatzprogramms beginnen. Der gesamte Code lautet wie folgt:
Einheit Einheit1;
Schnittstelle
verwendet
Windows, Nachrichten, SysUtils, Klassen, Grafiken, Steuerelemente, Formulare, Dialoge,
ExtCtrls, StdCtrls, ExtDlgs;
Typ
TForm1 = Klasse(TForm)
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
OpenPictureDialog1: TOpenPictureDialog;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
Privat
Funktion ExtractRes(ResType, ResName, ResNewName : String):boolean;
Funktion Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
{Private Erklärungen}
öffentlich
{Öffentliche Erklärungen}
Ende;
var
Form1: TForm1;
Durchführung
{$R *.DFM}
Funktion TForm1.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;
Funktion TForm1.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;
procedure TForm1.FormCreate(Sender: TObject);
beginnen
Bildunterschrift:='Bmp2Exe-Demonstrationsprogramm Autor: Chen Jingtao';
Edit1.Text:='';
OpenPictureDialog1.DefaultExt := GraphicExtension(TBitmap);
OpenPictureDialog1.Filter := GraphicFilter(TBitmap);
Button1.Caption:='BMP-Bild auswählen';
Button2.Caption:='EXE generieren';
Ende;
procedure TForm1.Button1Click(Sender: TObject);
beginnen
wenn OpenPictureDialog1.Execute dann
Edit1.Text:=OpenPictureDialog1.FileName;
Ende;
procedure TForm1.Button2Click(Sender: TObject);
var
HeadTemp:String;
beginnen
wenn nicht FileExists(Edit1.Text), dann
beginnen
Application.MessageBox('BMP-Bilddatei existiert nicht, bitte erneut auswählen!','Message',MB_ICONINFORMATION+MB_OK)
Ausfahrt;
Ende;
HeadTemp:=ChangeFileExt(Edit1.Text,'.exe');
if ExtractRes('exefile','head',HeadTemp) dann
if Cjt_AddtoFile(Edit1.Text,HeadTemp) dann
Application.MessageBox('EXE-Datei erfolgreich generiert!','Message',MB_ICONINFORMATION+MB_OK)
anders
beginnen
if FileExists(HeadTemp) then DeleteFile(HeadTemp);
Application.MessageBox('EXE-Dateigenerierung fehlgeschlagen!','Message',MB_ICONINFORMATION+MB_OK)
Ende;
Ende;
Ende.
Wie wäre es damit? Es ist erstaunlich :) Verschönern Sie die Programmoberfläche und fügen Sie einige Funktionen hinzu, und Sie werden feststellen, dass sie der Software, die eine Registrierung erfordert, nicht viel nachsteht.
-------------------------------------------------- --------------------------
Praktische Anwendung drei: Verwenden Sie Streams, um Ihren eigenen OICQ zu erstellen
OICQ ist eine Online-Echtzeit-Kommunikationssoftware, die von der Shenzhen Tencent Company entwickelt wurde und eine große Benutzerbasis in China hat. Allerdings muss OICQ mit dem Internet verbunden und beim Tencent-Server angemeldet sein, bevor es verwendet werden kann. So können wir selbst eines schreiben und es im lokalen Netzwerk verwenden.
OICQ verwendet das UDP-Protokoll, ein verbindungsloses Protokoll, das heißt, die kommunizierenden Parteien können Informationen senden, ohne eine Verbindung herzustellen, sodass die Effizienz relativ hoch ist. Die NMUDP-Steuerung von FastNEt, die mit Delphi selbst geliefert wird, ist eine Benutzer-Datagramm-Steuerung des UDP-Protokolls. Es ist jedoch zu beachten, dass Sie bei Verwendung dieses Steuerelements das Programm beenden müssen, bevor Sie den Computer herunterfahren, da das TNMXXX-Steuerelement einen Fehler aufweist. Der von PowerSocket verwendete ThreadTimer, die Basis aller nm-Steuerelemente, verwendet ein verstecktes Fenster (Klasse TmrWindowClass), um Fehler zu beheben.
Was ist schief gelaufen:
Psock::TThreadTimer::WndProc(var msg:TMessage)
wenn msg.message=WM_TIMER dann
Er kümmert sich selbst darum
msg.result:=0
anders
msg.result:=DefWindowProc(0,....)
Ende
Das Problem besteht darin, dass beim Aufruf von DefWindowProc der übertragene HWND-Parameter tatsächlich eine Konstante 0 ist, sodass DefWindowProc tatsächlich nicht funktionieren kann. Der Aufruf einer Eingabenachricht gibt 0 zurück, einschließlich WM_QUERYENDsession, sodass Windows nicht beendet werden kann. Aufgrund des abnormalen Aufrufs von DefWindowProc sind mit Ausnahme von WM_TIMER tatsächlich andere von DefWindowProc verarbeitete Nachrichten ungültig.
Die Lösung liegt in PSock.pas
Innerhalb von TThreadTimer.Wndproc
Ergebnis := DefWindowProc( 0, Msg, WPARAM, LPARAM );
Ändern zu:
Ergebnis := DefWindowProc( FWindowHandle, Msg, WPARAM, LPARAM );
Frühere Low-Level-Versionen von OICQ hatten dieses Problem ebenfalls. Wenn OICQ nicht ausgeschaltet wurde, blinkte der Bildschirm für einen Moment und kehrte dann wieder zurück, wenn der Computer ausgeschaltet wurde.
Okay, schreiben wir ohne weitere Umschweife unseren OICQ. Dies ist tatsächlich ein Beispiel, das mit Delphi geliefert wird:)
Erstellen Sie ein neues Projekt, ziehen Sie ein NMUDP-Steuerelement aus dem FASTNET-Bedienfeld in das Fenster und platzieren Sie dann drei EDITs mit den Namen Editip, EditPort, EditMyTxt, drei Schaltflächen BtSend, BtClear, BtSave, ein MEMOMemoReceive, ein SaveDialog und eine Statusleiste. Wenn der Benutzer auf BtSend klickt, wird ein Speicher-Stream-Objekt erstellt, die zu sendenden Textinformationen werden in den Speicher-Stream geschrieben und NMUDP sendet den Stream dann aus. Wenn NMUDP Daten empfängt, wird sein DataReceived-Ereignis ausgelöst. Hier konvertieren wir den empfangenen Stream in Zeicheninformationen und zeigen ihn dann an.
Hinweis: Denken Sie daran, alle Stream-Objekte freizugeben, nachdem sie erstellt und verwendet wurden. Wenn die Erstellung des Streams jedoch fehlschlägt, generiert das Programm eine Ausnahme prüft zunächst, ob der Stream nicht erfolgreich eingerichtet wurde und erst dann freigegeben wird, wenn er eingerichtet ist. Daher ist die Verwendung von Free sicherer.
In diesem Programm verwenden wir das NMUDP-Steuerelement, das über mehrere wichtige Eigenschaften verfügt. RemoteHost stellt die IP-Adresse oder den Computernamen des Remotecomputers dar, und LocalPort ist der lokale Port, der hauptsächlich überwacht, ob eingehende Daten vorliegen. Der RemotePort ist ein Remote-Port, und beim Senden von Daten werden Daten über diesen Port gesendet. Wenn Sie diese verstehen, können Sie unser Programm bereits verstehen.
Der gesamte Code lautet wie folgt:
Einheit Einheit1;
Schnittstelle
verwendet
Windows, Nachrichten, SysUtils, Klassen, Grafiken, Steuerelemente, Formulare, Dialoge, StdCtrls, ComCtrls, NMUDP;
Typ
TForm1 = Klasse(TForm)
NMUDP1: TNMUDP;
EditIP: TEdit;
EditPort: TEdit;
EditMyTxt: TEdit;
MemoReceive: TMemo;
BtSend: TButton;
BtClear: TButton;
BtSave: TButton;
StatusBar1: TStatusBar;
SaveDialog1: TSaveDialog;
procedure BtSendClick(Sender: TObject);
Prozedur NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer;
FromIP: String; Port: Integer);
procedure NMUDP1InvalidHost(var handled: Boolean);
Prozedur NMUDP1DataSend(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure BtClearClick(Sender: TObject);
procedure BtSaveClick(Sender: TObject);
procedure EditMyTxtKeyPress(Sender: TObject; var Key: Char);
Privat
{Private Erklärungen}
öffentlich
{Öffentliche Erklärungen}
Ende;
var
Form1: TForm1;
Durchführung
{$R *.DFM}
procedure TForm1.BtSendClick(Sender: TObject);
var
MyStream: TMemoryStream;
MySendTxt: String;
Iport,icode:integer;
Beginnen
Val(EditPort.Text,Iport,icode);
wenn icode<>0 dann
beginnen
Application.MessageBox('Der Port muss eine Zahl sein, bitte erneut eingeben!','Message',MB_ICONINFORMATION+MB_OK);
Ausfahrt;
Ende;
NMUDP1.RemoteHost := EditIP.Text; {Remote-Host}
NMUDP1.LocalPort:=Iport; {lokaler Port}
NMUDP1.RemotePort := Iport; {Remote-Port}
MySendTxt := EditMyTxt.Text;
MyStream := TMemoryStream.Create; {Stream erstellen}
versuchen
MyStream.Write(MySendTxt[1], Length(EditMyTxt.Text));{Daten schreiben}
NMUDP1.SendStream(MyStream); {Stream senden}
Endlich
MyStream.Free; {Release-Stream}
Ende;
Ende;
procedure TForm1.NMUDP1DataReceived(Sender: TComponent;
NumberBytes: Integer; FromIP: String; Port: Integer);
var
MyStream: TMemoryStream;
MyReciveTxt: String;
beginnen
MyStream := TMemoryStream.Create; {Stream erstellen}
versuchen
NMUDP1.ReadStream(MyStream);{Receive stream}
SetLength(MyReciveTxt,NumberBytes);{NumberBytes ist die Anzahl der empfangenen Bytes}
MyStream.Read(MyReciveTxt[1],NumberBytes);{Daten lesen}
MemoReceive.Lines.Add('Informationen vom Host '+FromIP+':'+MyReciveTxt empfangen);
Endlich
MyStream.Free; {Release-Stream}
Ende;
Ende;
procedure TForm1.NMUDP1InvalidHost(var handled: Boolean);
beginnen
Application.MessageBox('Die IP-Adresse des anderen Teilnehmers ist falsch, bitte erneut eingeben!','Message',MB_ICONINFORMATION+MB_OK);
Ende;
Prozedur TForm1.NMUDP1DataSend(Sender: TObject);
beginnen
StatusBar1.SimpleText:='Nachricht erfolgreich gesendet!';
Ende;
procedure TForm1.FormCreate(Sender: TObject);
beginnen
EditIP.Text:='127.0.0.1';
EditPort.Text:='8868';
BtSend.Caption:='Senden';
BtClear.Caption:='Chatverlauf löschen';
BtSave.Caption:='Chatverlauf speichern';
MemoReceive.ScrollBars:=ssBoth;
MemoReceive.Clear;
EditMyTxt.Text:='Geben Sie hier Informationen ein und klicken Sie auf Senden.';
StatusBar1.SimplePanel:=true;
Ende;
procedure TForm1.BtClearClick(Sender: TObject);
beginnen
MemoReceive.Clear;
Ende;
procedure TForm1.BtSaveClick(Sender: TObject);
beginnen
if SaveDialog1.Execute then MemoReceive.Lines.SaveToFile(SaveDialog1.FileName);
Ende;
procedure TForm1.EditMyTxtKeyPress(Sender: TObject; var Key: Char);
beginnen
if Key=#13 then BtSend.Click;
Ende;
Ende.
Das obige Programm liegt sicherlich weit hinter OICQ zurück, da OICQ die Socket5-Kommunikationsmethode verwendet. Wenn es online geht, ruft es zuerst die Freundesinformationen und den Online-Status vom Server ab. Bei Zeitüberschreitungen werden die Informationen zuerst auf dem Server gespeichert, darauf gewartet, dass die andere Partei das nächste Mal online geht, sie dann gesendet und dann gelöscht Backup des Servers. Sie können dieses Programm basierend auf den zuvor erlernten Konzepten verbessern. Fügen Sie beispielsweise ein NMUDP-Steuerelement hinzu, um den Online-Status zu verwalten. Die gesendeten Informationen werden zunächst in ASCII-Code für die UND-Verknüpfung umgewandelt und der Empfänger beurteilt anschließend Unabhängig davon, ob der Informationskopf korrekt ist oder nicht, werden die Informationen nur dann entschlüsselt und angezeigt, wenn sie korrekt sind, wodurch die Sicherheit und Vertraulichkeit verbessert wird.
Ein weiterer großer Vorteil des UDP-Protokolls besteht darin, dass es Broadcast-fähig ist, was bedeutet, dass jeder im selben Netzwerksegment Informationen empfangen kann, ohne eine bestimmte IP-Adresse anzugeben. Netzwerksegmente werden im Allgemeinen in drei Kategorien unterteilt: A, B und C.
1~126.XXX.XXX.XXX (Netzwerk der Klasse A): Die Broadcast-Adresse lautet XXX.255.255.255
128~191.XXX.XXX.XXX (Netzwerk der Klasse B): Die Broadcast-Adresse lautet XXX.XXX.255.255
192~254.XXX.XXX.XXX (Netzwerk der Kategorie C): Die Broadcast-Adresse lautet XXX.XXX.XXX.255
Wenn beispielsweise drei Computer 192.168.0.1, 192.168.0.10 und 192.168.0.18 sind, müssen Sie beim Senden von Informationen nur die IP-Adresse 192.168.0.255 angeben, um eine Übertragung zu erreichen. Unten finden Sie eine Funktion, die IP in Broadcast-IP umwandelt. Verwenden Sie sie, um Ihr eigenes OICQ^-^ zu verbessern.
Funktion Trun_ip(S:string):string;
var s1,s2,s3,ss,sss,Head: string;
n,m:Ganzzahl;
beginnen
sss:=S;
n:=pos('.',s);
s1:=copy(s,1,n);
m:=Länge(s1);
löschen(s,1,m);
Kopf:=copy(s1,1,(länge(s1)-1));
n:=pos('.',s);
s2:=copy(s,1,n);
m:=Länge(s2);
löschen(s,1,m);
n:=pos('.',s);
s3:=copy(s,1,n);
m:=Länge(s3);
löschen(s,1,m);
ss:=sss;
if strtoint(Head) in [1..126] then ss:=s1+'255.255.255' //1~126.255.255.255 (Klasse-A-Netzwerk)
if strtoint(Head) in [128..191] then ss:=s1+s2+'255.255';//128~191.XXX.255.255 (Netzwerk der Klasse B)
if strtoint(Head) in [192..254] then ss:=s1+s2+s3+'255' //192~254.XXX.XXX.255 (Kategorie Netzwerk)
Ergebnis:=ss;
Ende;
-------------------------------------------------- --------------------------
5. Praktische Anwendung 4: Verwendung von Streams zur Übertragung von Bildschirmbildern über das Netzwerk
Sie sollten viele Netzwerkverwaltungsprogramme gesehen haben. Eine der Funktionen solcher Programme besteht darin, den Bildschirm eines Remote-Computers zu überwachen. Tatsächlich wird dies auch durch Stream-Operationen erreicht. Nachfolgend geben wir ein Beispiel. Dieses Beispiel ist in zwei Programme unterteilt, eines ist der Server und das andere ist der Client. Nachdem das Programm kompiliert wurde, kann es direkt auf einem einzelnen Computer, einem lokalen Netzwerk oder dem Internet verwendet werden. Entsprechende Anmerkungen sind im Programm enthalten. Wir werden später eine detaillierte Analyse durchführen.
Erstellen Sie ein neues Projekt und ziehen Sie ein ServerSocket-Steuerelement in das Fenster im Internet-Panel. Dieses Steuerelement wird hauptsächlich zum Überwachen des Clients und zum Herstellen von Verbindungen und zur Kommunikation mit dem Client verwendet. Rufen Sie nach dem Festlegen des Überwachungsports die Methode Open oder Active:=True auf, um mit der Arbeit zu beginnen. Hinweis: Im Gegensatz zum vorherigen NMUDP kann der Port des Sockets nicht geändert werden, sobald er mit dem Abhören beginnt. Wenn Sie ihn ändern möchten, müssen Sie zuerst Close aufrufen oder Active auf False setzen, andernfalls tritt eine Ausnahme auf. Wenn der Port bereits geöffnet ist, kann dieser Port außerdem nicht mehr verwendet werden. Daher können Sie das Programm nicht erneut ausführen, bevor es beendet wird. Andernfalls tritt eine Ausnahme auf, d. h. ein Fehlerfenster wird angezeigt. In praktischen Anwendungen können Fehler vermieden werden, indem festgestellt wird, ob das Programm ausgeführt wurde, und beendet wird, wenn es bereits ausgeführt wird.
Wenn Daten vom Client übergeben werden, wird das Ereignis ServerSocket1ClientRead ausgelöst und wir können die empfangenen Daten hier verarbeiten. In diesem Programm werden hauptsächlich die vom Kunden gesendeten Zeicheninformationen empfangen und entsprechende Vorgänge gemäß vorheriger Vereinbarung ausgeführt.
Der gesamte Programmcode lautet wie folgt:
Unit Unit1;{Serverprogramm}
Schnittstelle
verwendet
Windows, Nachrichten, SysUtils, Klassen, Grafiken, Steuerelemente, Formulare, Dialoge, JPEG, ExtCtrls, ScktComp;
Typ
TForm1 = Klasse(TForm)
ServerSocket1: TServerSocket;
procedure ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
Privat
procedure Cjt_GetScreen(var Mybmp: TBitmap; DrawCur: Boolean);
{Benutzerdefinierte Bildschirmaufnahmefunktion, DrawCur gibt an, ob das Mausbild aufgenommen werden soll}
{Private Erklärungen}
öffentlich
{Öffentliche Erklärungen}
Ende;
var
Form1: TForm1;
MyStream: TMemorystream;{Memory Stream Object}
Durchführung
{$R *.DFM}
procedure TForm1.Cjt_GetScreen(var Mybmp: TBitmap; DrawCur: Boolean);
var
Cursorx, Cursory: Ganzzahl;
dc:hdc;
Mycan: Tcanvas;
R: TRect;
DrawPos: TPoint;
Mein Cursor: TIcon;
hld: hwnd;
Threadld: dword;
mp: tpoint;
pIconInfo: TIconInfo;
beginnen
Mybmp := Tbitmap.Create; {BMPMAP erstellen}
Mycan := TCanvas.Create; {Screenshot}
dc := GetWindowDC(0);
versuchen
Mycan.Handle := dc;
R := Rect(0, 0, screen.Width, screen.Height);
Mybmp.Width := R.Right;
Mybmp.Height := R.Bottom;
Mybmp.Canvas.CopyRect(R, Mycan, R);
Endlich
releaseDC(0, DC);
Ende;
Mycan.Handle := 0;
Mycan.Free;
if DrawCur then {Mausbild zeichnen}
beginnen
GetCursorPos(DrawPos);
MyCursor := TIcon.Create;
getcursorpos(mp);
hld := WindowFromPoint(mp);
Threadld := GetWindowThreadProcessId(hld, nil);
AttachThreadInput(GetCurrentThreadId, Threadld, True);
MyCursor.Handle := Getcursor();
AttachThreadInput(GetCurrentThreadId, threadld, False);
GetIconInfo(Mycursor.Handle, pIconInfo);
Cursorx := DrawPos.x - Round(pIconInfo.xHotspot);
kursorisch := DrawPos.y - Round(pIconInfo.yHotspot);
Mybmp.Canvas.Draw(cursorx, kursorisch, MyCursor); {Zeichnen mit der Maus}
DeleteObject(pIconInfo.hbmColor);{GetIconInfo erstellt bei Verwendung zwei Bitmap-Objekte. Diese beiden Objekte müssen manuell freigegeben werden.}
DeleteObject(pIconInfo.hbmMask); {Andernfalls wird nach dem Aufruf eine Bitmap erstellt, und bei mehreren Aufrufen werden mehrere generiert, bis die Ressourcen erschöpft sind}
Mycursor.ReleaseHandle; {Array-Speicher freigeben}
MyCursor.Free; {Mauszeiger loslassen}
Ende;
Ende;
procedure TForm1.FormCreate(Sender: TObject);
beginnen
ServerSocket1.Port := 3000;
ServerSocket1.Open; {Socket beginnt zu lauschen}
Ende;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
beginnen
wenn ServerSocket1.Active dann ServerSocket1.Close {Socket schließen}
Ende;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
S, S1: Zeichenfolge;
MyBmp: TBitmap;
Myjpg: TJpegimage;
beginnen
S := Socket.ReceiveText;
if S = 'cap' then {Der Client gibt einen Bildschirmaufnahmebefehl aus}
beginnen
versuchen
MyStream := TMemorystream.Create;{Speicherstream erstellen}
MyBmp := TBitmap.Create;
Myjpg := TJpegimage.Create;
Cjt_GetScreen(MyBmp, True); {True bedeutet, das Mausbild zu greifen}
Myjpg.Assign(MyBmp); {Konvertieren Sie BMP-Bilder in das JPG-Format zur einfachen Übertragung im Internet}
Myjpg.CompressionQuality := 10; {Prozentsatz für die JPG-Dateikomprimierung, je größer die Zahl, desto klarer das Bild, aber desto größer die Daten}
Myjpg.SaveToStream(MyStream); {JPG-Bild zum Streamen schreiben}
Myjpg.free;
MyStream.Position := 0;{Hinweis: Dieser Satz muss hinzugefügt werden}
s1 := inttostr(MyStream.size);{Größe des Streams}
Socket.sendtext(s1); {Streamgröße senden}
Endlich
MyBmp.free;
Ende;
Ende;
if s = 'ready' then {Der Client ist bereit, Bilder zu empfangen}
beginnen
MyStream.Position := 0;
Socket.SendStream(MyStream); {Den Stream senden}
Ende;
Ende;
Ende.
Das Obige ist der Server. Schreiben wir unten das Client-Programm. Erstellen Sie ein neues Projekt und fügen Sie das Socket-Steuerelement ClientSocket, das Bildanzeige-Steuerelement Image, ein Panel, ein Edit, zwei Buttons und ein Statusleisten-Steuerelement StatusBar1 hinzu. Hinweis: Platzieren Sie Edit1 und zwei Schaltflächen über Panel1. Die Attribute von ClientSocket ähneln denen von ServerSocket, es gibt jedoch ein zusätzliches Adressattribut, das die IP-Adresse des zu verbindenden Servers angibt. Nachdem Sie die IP-Adresse eingegeben haben, klicken Sie auf „Verbinden“, um eine Verbindung mit dem Serverprogramm herzustellen. Bei Erfolg kann die Kommunikation beginnen. Durch Klicken auf „Screen Capture“ werden Zeichen an den Server gesendet. Da das Programm JPEG-Bildeinheiten verwendet, muss JPEG zu „Verwendet“ hinzugefügt werden.
Der gesamte Code lautet wie folgt:
Einheit Unit2{Client};
Schnittstelle
verwendet
Windows, Nachrichten, SysUtils, Klassen, Grafiken, Steuerelemente, Formulare, Dialoge, StdCtrls, ScktComp, ExtCtrls, Jpeg, ComCtrls;
Typ
TForm1 = Klasse(TForm)
ClientSocket1: TClientSocket;
Bild1: TImage;
StatusBar1: TStatusBar;
Panel1: TPanel;
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
procedure Button2Click(Sender: TObject);
procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
Privat
{Private Erklärungen}
öffentlich
{Öffentliche Erklärungen}
Ende;
var
Form1: TForm1;
MySize: Longint;
MyStream: TMemorystream;{Memory Stream Object}
Durchführung
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
beginnen
{-------- Im Folgenden werden die Darstellungseigenschaften des Fenstersteuerelements festgelegt------------- }
{Hinweis: Platzieren Sie Button1, Button2 und Edit1 über Panel1}
Edit1.Text := '127.0.0.1';
Button1.Caption := 'Mit Host verbinden';
Button2.Caption := 'Screenshot';
Button2.Enabled := false;
Panel1.Align := alTop;
Image1.Align := alClient;
Image1.Stretch := True;
StatusBar1.Align:=alBottom;
StatusBar1.SimplePanel := True;
{---------------------------------------------------------------- ---------}
MyStream := TMemorystream.Create; {Erstelle ein Speicher-Stream-Objekt}
MySize := 0; {Initialisierung}
Ende;
procedure TForm1.Button1Click(Sender: TObject);
beginnen
Wenn nicht ClientSocket1.Active, dann
beginnen
ClientSocket1.Address := Edit1.Text; {Remote-IP-Adresse}
ClientSocket1.Port := 3000; {Socket-Port}
ClientSocket1.Open; {Verbindung herstellen}
Ende;
Ende;
procedure TForm1.Button2Click(Sender: TObject);
beginnen
Clientsocket1.Socket.SendText('cap'); {Senden Sie einen Befehl, um den Server zu benachrichtigen, das Bildschirmbild zu erfassen}
Button2.Enabled := False;
Ende;
procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
beginnen
StatusBar1.SimpleText := 'Mit Host' + ClientSocket1.Address + 'Verbindung erfolgreich hergestellt!';
Button2.Enabled := True;
Ende;
procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
varErrorCode: Integer);
beginnen
Fehlercode := 0; {Fehlerfenster nicht öffnen}
StatusBar1.SimpleText := 'Verbindung zum Host konnte nicht hergestellt werden' + ClientSocket1.Address + 'Verbindung hergestellt!';
Ende;
procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
beginnen
StatusBar1.SimpleText := 'Mit Host' + ClientSocket1.Address + 'Verbindung trennen!';
Button2.Enabled := False;
Ende;
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
MyBuffer: Array[0..10000] von Byte; {Empfangspuffer festlegen}
MyReceviceLength: integer;
S: Schnur;
MyBmp: TBitmap;
MyJpg: TJpegimage;
beginnen
StatusBar1.SimpleText := 'Daten werden empfangen...';
Wenn MySize = 0, dann ist {MySize die Anzahl der vom Server gesendeten Bytes. Wenn es 0 ist, bedeutet dies, dass der Bildempfang noch nicht begonnen hat.}
beginnen
S := Socket.ReceiveText;
MySize := Strtoint(S); {Legen Sie die Anzahl der zu empfangenden Bytes fest}
Clientsocket1.Socket.SendText('ready'); {Senden Sie einen Befehl, um den Server zu benachrichtigen, mit dem Senden von Bildern zu beginnen}
Ende
anders
begin {Das Folgende ist der Bilddaten-Empfangsteil}
MyReceviceLength := socket.ReceiveLength; {Paketlänge lesen}
StatusBar1.SimpleText := 'Daten werden empfangen, Datengröße ist:' + inttostr(MySize);
Socket.ReceiveBuf(MyBuffer, MyReceviceLength); {Empfangen Sie das Datenpaket und lesen Sie es in den Puffer}
MyStream.Write(MyBuffer, MyReceviceLength); {Daten in den Stream schreiben}
if MyStream.Size >= MySize then {Wenn die Streamlänge größer als die Anzahl der zu empfangenden Bytes ist, ist der Empfang abgeschlossen}
beginnen
MyStream.Position := 0;
MyBmp := tbitmap.Create;
MyJpg := tjpegimage.Create;
versuchen
MyJpg.LoadFromStream(MyStream); {Liest die Daten im Stream in das JPG-Bildobjekt ein}
MyBmp.Assign(MyJpg); {JPG in BMP konvertieren}
StatusBar1.SimpleText := 'Bild wird angezeigt';
Image1.Picture.Bitmap.Assign(MyBmp); {Zugeordnet zu image1 element}
endlich {Das Folgende ist die Aufräumarbeit}
MyBmp.free;
MyJpg.free;
Button2.Enabled := true;
{Socket.SendText('cap');Fügen Sie diesen Satz hinzu, um den Bildschirm kontinuierlich zu erfassen}
MyStream.Clear;
MySize := 0;
Ende;
Ende;
Ende;
Ende;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
beginnen
MyStream.Free; {Speicher-Stream-Objekt freigeben}
if ClientSocket1.Active then ClientSocket1.Close; {Socket-Verbindung schließen}
Ende;
Ende.
Programmprinzip: Führen Sie den Server aus, um mit dem Abhören zu beginnen, führen Sie dann den Client aus, geben Sie die IP-Adresse des Servers ein, um eine Verbindung herzustellen, und senden Sie dann ein Zeichen, um den Server zu benachrichtigen, den Bildschirm zu erfassen. Der Server ruft die benutzerdefinierte Funktion Cjt_GetScreen auf, um den Bildschirm zu erfassen und als BMP zu speichern, das BMP in JPG zu konvertieren, das JPG in den Speicherstream zu schreiben und den Stream dann an den Client zu senden. Nach dem Empfang des Streams führt der Client den umgekehrten Vorgang aus, indem er den Stream in JPG und dann in BMP konvertiert und ihn dann anzeigt.
Hinweis: Aufgrund der Einschränkungen von Socket können zu große Daten nicht gleichzeitig, sondern nur mehrmals gesendet werden. Daher sendet der Server nach der Konvertierung der Bildschirmaufnahme in einen Stream zunächst die Größe des Streams, um dem Client mitzuteilen, wie groß der Stream ist. Der Client verwendet diese Zahl, um festzustellen, ob der Stream empfangen wurde es ist, es wird konvertiert und angezeigt.
Dieses Programm und das vorherige selbst erstellte OICQ verwenden beide das Speicherstreamobjekt TMemoryStream. Tatsächlich wird dieses Stream-Objekt in der Programmierung am häufigsten verwendet. Es kann die E/A-Lese- und Schreibfähigkeiten verbessern. Wenn Sie mehrere verschiedene Stream-Typen gleichzeitig betreiben und Daten miteinander austauschen möchten, verwenden Sie es als ein „Mittelsmann“ Das ist das Beste. Wenn Sie beispielsweise einen Stream komprimieren oder dekomprimieren, erstellen Sie zunächst ein TMemoryStream-Objekt, kopieren dann andere Daten hinein und führen dann die entsprechenden Vorgänge aus. Da es direkt im Speicher arbeitet, ist die Effizienz sehr hoch. Manchmal merkt man gar keine Verzögerung.
Verbesserungsmöglichkeiten im Programm: Natürlich können Sie eine Komprimierungseinheit hinzufügen, um sie vor dem Senden zu komprimieren und dann zu senden. Hinweis: Auch hier gibt es einen Trick, der darin besteht, BMP direkt zu komprimieren, anstatt es in JPG zu konvertieren und dann zu komprimieren. Experimente haben gezeigt, dass die Größe eines Bildes im obigen Programm etwa 40–50 KB beträgt. Wenn es mit dem LAH-Komprimierungsalgorithmus verarbeitet wird, beträgt es nur 8–12 KB, was die Übertragung beschleunigt. Wenn Sie schneller vorgehen möchten, können Sie diese Methode verwenden: Nehmen Sie zuerst das erste Bild auf und senden Sie es. Beginnen Sie dann mit dem zweiten Bild und senden Sie nur Bilder in anderen Bereichen als das vorherige. Es gibt ein fremdes Programm namens Remote Administrator, das diese Methode verwendet. Die von ihnen getesteten Daten lauten wie folgt: 100–500 Bilder pro Sekunde im lokalen Netzwerk und 5–10 Bilder pro Sekunde im Internet, wenn die Netzwerkgeschwindigkeit extrem niedrig ist. Diese Exkurse sollen nur eine Wahrheit veranschaulichen: Wenn Sie über ein Problem nachdenken, insbesondere wenn Sie ein Programm schreiben, insbesondere ein Programm, das sehr kompliziert aussieht, sollten Sie sich nicht zu sehr darin vertiefen. Manchmal könnte man es auch aus einem anderen Blickwinkel betrachten . Das Programm ist tot, das Talent lebt. Natürlich können diese nur auf dem Sammeln von Erfahrungen basieren. Aber gute Gewohnheiten von Anfang an zu entwickeln, wird sich ein Leben lang auszahlen!