What is a stream? Stream, simply put, is an abstract processing data based on object-oriented
tools. In the stream, some basic operations for processing data are defined, such as reading data, writing data, etc.
The programmer performs all operations on the stream without caring about the actual flow direction of the data at the other end of the stream. Flow not
But it can process files, dynamic memory, network data and other data forms. if you are right
The operation of streams is very proficient. Using the convenience of streams in programs will greatly improve the efficiency of writing programs.
Below, the author uses four examples: EXE file encryptor, electronic greeting card, homemade OICQ and network screen
Transmission to illustrate the use of "stream" in Delphi programming. Some of the techniques in these examples used to be a lot of soft
The secret of the file is not open to the public, and now everyone can directly quote the code free of charge.
"Tall buildings rise from the ground." Before analyzing the examples, let's first understand the basic concepts and concepts of flow.
Functions, only after understanding these basic things can we proceed to the next step. Please understand carefully
these basic methods. Of course, if you are already familiar with them, you can skip this step.
1. Basic concepts and function declarations of streams in Delphi
In Delphi, the base class of all stream objects is the TStream class, which defines the common properties of all streams.
and methods.
The properties defined in the TStream class are introduced as follows:
1. Size: This property returns the size of the data in the stream in bytes.
2. Position: This attribute controls the position of the access pointer in the flow.
There are four virtual methods defined in Tstream:
1. Read: This method reads data from the stream. The function prototype is:
Function Read(var Buffer;Count:Longint):Longint;virtual;abstract;
The parameter Buffer is the buffer placed when reading data, Count is the number of bytes of data to be read,
The return value of this method is the actual number of bytes read, which can be less than or equal to the value specified in Count.
2. Write: This method writes data into the stream. The function prototype is:
Function Write(var Buffer;Count:Longint):Longint;virtual;abstract;
The parameter Buffer is the buffer of the data to be written into the stream, Count is the length of the data in bytes,
The return value of this method is the number of bytes actually written to the stream.
3. Seek: This method implements the movement of the read pointer in the stream. The function prototype is:
Function Seek(Offset:Longint;Origint:Word):Longint;virtual;abstract;
The parameter Offset is the number of offset bytes, and the parameter Origin points out the actual meaning of Offset and its possible values.
as follows:
soFromBeginning:Offset is the position of the pointer from the beginning of the data after movement. At this time Offset must
Greater than or equal to zero.
soFromCurrent:Offset is the relative position of the pointer after movement and the current pointer.
soFromEnd:Offset is the position of the pointer from the beginning of the data after movement. At this time Offset must
Less than or equal to zero.
The return value of this method is the position of the pointer after movement.
4. Setsize: This method changes the size of the data. The function prototype is:
Function Setsize(NewSize:Longint);virtual;
In addition, several static methods are also defined in the TStream class:
1. ReadBuffer: The function of this method is to read data from the current position in the stream. The function prototype is:
PRocedure ReadBuffer(var Buffer;Count:Longint);
The definition of parameters is the same as Read above. Note: When the number of data bytes read is different from the bytes that need to be read
When the numbers are different, an EReadError exception will be generated.
2. WriteBuffer: The function of this method is to write data to the stream at the current position. The function prototype is:
Procedure WriteBuffer(var Buffer;Count:Longint);
The definition of parameters is the same as Write above. Note: When the number of data bytes written is different from the bytes that need to be written
When the numbers are different, an EWriteError exception will be generated.
3. CopyFrom: This method is used to copy data streams from other streams. The function prototype is:
Function CopyFrom(Source:TStream;Count:Longint):Longint;
The parameter Source is the stream that provides data, and Count is the number of data bytes copied. When Count is greater than 0,
CopyFrom copies Count bytes of data from the current position of the Source parameter; when Count equals 0,
CopyFrom sets the Position property of the Source parameter to 0, and then copies all the data of the Source;
TStream has other derived classes, the most commonly used of which is the TFileStream class. Using TFileStream
To access files using a class, you must first create an instance. The statement is as follows:
constructor Create(const Filename:string;Mode:Word);
Filename is the file name (including path), and the parameter Mode is the way to open the file, which includes how to open the file.
Open mode and shared mode, their possible values and meanings are as follows:
Open mode:
fmCreate: Create a file with the specified file name, or open the file if it already exists.
fmOpenRead: Open the specified file in read-only mode
fmOpenWrite: Open the specified file in write-only mode
fmOpenReadWrite: Open the specified file for writing
Sharing mode:
fmShareCompat: Share mode is compatible with FCBs
fmShareExclusive: Do not allow other programs to open the file in any way
fmShareDenyWrite: Do not allow other programs to open the file for writing
fmShareDenyRead: Do not allow other programs to open the file in read mode
fmShareDenyNone: Other programs can open the file in any way
TStream also has a derived class TMemoryStream, which is used very often in actual applications.
Very frequently. It's called a memory stream, which means creating a stream object in memory. Its basic methods and functions follow
Same as above.
Well, with the above foundation in place, we can start our programming journey.
-------------------------------------------------- --------------------------
2. Practical application one: using streams to create EXE file encryptors, bundles, self-extracting files and installation programs
Let’s first talk about how to make an EXE file encryptor.
The principle of EXE file encryptor: create two files, one is used to add resources to the other EXE file
Inside, it's called an add-in program. Another EXE file that is added is called a header file. The function of this program is
Read the files added to itself.
The structure of EXE files under Windows is relatively complex. Some programs also have checksums. When you find that they have been changed
Later, they will think that they are infected by the virus and refuse to execute it. So we add the file to our program,
This will not change the original file structure. Let's first write an add function. The function of this function is to add
One file is appended to the end of another file as a stream. The function is as follows:
Function Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Target,Source:TFileStream;
MyFileSize:integer;
begin
try
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite or fmShareExclusive);
try
Target.Seek(0,soFromEnd);//Add resources to the end
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//Calculate the resource size and write it to the end of the auxiliary process
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
finally
Target.Free;
Source.Free;
end;
except
Result:=False;
Exit;
end;
Result:=True;
end;
With the above foundation, we should easily understand this function. where the parameter SourceFile is
The file to be added, the parameter TargetFile is the target file to be added. For example, add a.exe to
In b.exe you can: Cjt_AddtoFile('a.exe',b.exe'); if the addition is successful, return True otherwise
Return false.
Based on the above function we can write the opposite read function:
Function Cjt_LoadFromFile(SourceFile,TargetFile:string):Boolean;
var
Source:TFileStream;
Target:TMemoryStream;
MyFileSize:integer;
begin
try
Target:=TMemoryStream.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//Read resource size
Source.Seek(-MyFileSize,soFromEnd);//Locate the resource location
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//Remove resources
Target.SaveToFile(TargetFile);//Save to file
finally
Target.Free;
Source.Free;
end;
except
Result:=false;
Exit;
end;
Result:=true;
end;
The parameter SourceFile is the file name of the added file, and the parameter TargetFile is the file name taken out.
The target file name to be saved after the file. For example, Cjt_LoadFromFile('b.exe','a.txt'); in b.exe
Take out the file and save it as a.txt. If the extraction is successful, it returns True otherwise it returns False.
Open Delphi, create a new project, and put an Edit control Edit1 and two Buttons on the window:
Button1 and Button2. Button's Caption property is set to "OK" and "Cancel" respectively. exist
Write code in the Click event of Button1:
var S: string;
begin
S:=ChangeFileExt(application.ExeName,'.Cjt');
if Edit1.Text='790617' then
begin
Cjt_LoadFromFile(Application.ExeName,S);
{Take out the file and save it in the current path and name it "original file.Cjt"}
Winexec(pchar(S),SW_Show);{Run "original file.Cjt"}
Application.Terminate;{Exit program}
end
else
Application.MessageBox('Password is incorrect, please re-enter!', 'Password is incorrect', MB_ICONERROR+MB_OK);
Compile this program and rename the EXE file to head.exe. Create a new text file head.rc,
The content is: head exefile head.exe, then copy them to the BIN directory of Delphi and execute
The Dos command Brcc32.exe head.rc will generate a head.res file. This file is what we want
Resource files, keep them first.
Our header file has been created, let's create the add-in program.
Create a new project and put the following controls: one Edit, one Opendialog, and two Button1
The Caption properties are set to "Select File" and "Encrypted" respectively.
Add a sentence in the source program: {$R head.res} and copy the head.res file to the current directory of the program.
In this way, the head.exe just now is compiled together with the program.
Write the code in the Cilck event of Button1:
if OpenDialog1.Execute then Edit1.Text:=OpenDialog1.FileName;
Write the code in the Cilck event of Button2:
var S:String;
begin
S:=ExtractFilePath(Edit1.Text);
if ExtractRes('exefile','head',S+'head.exe') then
if Cjt_AddtoFile(Edit1.Text,S+'head.exe') then
if DeleteFile(Edit1.Text) then
if RenameFile(S+'head.exe',Edit1.Text) then
Application.MessageBox('File encryption successful!','Message',MB_ICONINFORMATION+MB_OK)
else
begin
if FileExists(S+'head.exe') then DeleteFile(S+'head.exe');
Application.MessageBox('File encryption failed!','Message',MB_ICONINFORMATION+MB_OK)
end;
end;
Among them, ExtractRes is a custom function, which is used to extract head.exe from the resource file.
Function ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
begin
try
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
try
Res.SavetoFile(ResNewName);
Result:=true;
finally
Res.Free;
end;
except
Result:=false;
end;
end;
Note: Our function above simply appends one file to the end of another file.
In actual applications, it can be changed to add multiple files, as long as the offset is defined according to the actual size and number
The address will do. For example, a file bundler adds two or more programs to a header file.
in. The principles of self-extracting programs and installers are the same, but with more compression.
For example, we can reference a LAH unit, compress the stream and then add it, so that the file will become smaller.
Just decompress it before reading it out.
In addition, the example of the EXE encryptor in the article still has many imperfections. For example, the password is fixed as
"790617", after taking out the EXE and running it, you should wait for it to finish running and then delete it, etc. Readers can modify it by themselves.