Overview:
This document describes how to implement data encryption, data compression, and self-decompression for a single file. You can also compress multiple files or folders with slight modifications.
Keywords: Encrypted compression, zlib, stream, resource file
Reference:
In our daily life, we must have used well-known compression software such as WinZip and WinRAR, which is a problem of data encryption and Data Compression when developing software! I will discuss this technical issue in this article, and I would like to thank you for your skills. Every time I face the problem and want to solve it, the skills you have worked hard to find will always make me feel clear, solve the problem. This article mainly uses Delph's powerful stream processing techniques to implement data encryption and compression, and is used in actual software program development, write down my personal experiences and development experiences and share them with you.
1. system functions
1) Data Compression
Use two stream classes (tcompressionstream and tdecompressionstream) provided by Delphi to compress and decompress data.
2) Data Encryption and Compression
Data Encryption is implemented through the "stream" Application in Delphi programming. tfilestream and tmemorystream are the two Derived classes of tstream. The data compression part adopts the 1) implementation method.
3) double-click the compressed file to automatically associate and decompress it.
By changing the registry, the implementation extension is associated with the program file, mainly using Tregistry; and the API function shchangenoworkflow is used to display the registration effect immediately.
4). Generate a self-decompressed file.
Self-decompressed files implement data compression 1) and Data Encryption compression 2) automatically decompress; and, through the use of resource files, merge executable self-decompressed files with data files, to decompress the data.
2. System implementation
2.1 Working Principle
2.2 Key Technologies
(1) zlib
1) base class tcustomzlibstream: it is the base class of the class tcompressionstream and tdecompressionstream. It mainly has an attribute: onprogress, which occurs when the class is compressed or decompressed.
Format: Procedure onprogress (Sender: tobject); dynamic;
2) compression class tcompressionstream: In addition to inheriting the onprogress attribute of the base class, a property is added: compressionrate, which is defined as follows:
Property compressionrate: single read getcompressionrate;
The compression ratio can be obtained through this attribute.
Its important methods are defined as follows:
Constructor tcompressionstream. Create (compressionlevel: tcompressionlevel; DEST: tstream );
Specifically: tcompressionlevel (compression type), which is defined by the following:
1) clnone: No data compression;
2) clfastest: Fast compression at the cost of compression efficiency;
3) cldefault: perform normal compression;
4) CLmax: To maximize compression and sacrifice speed;
DeST: Destination stream, used to store compressed data.
Function tcompressionstream. Write (const buffer; count: longint): longint;
Buffer: The data to be compressed;
Count: the number of bytes of data to be compressed;
The number of bytes written to the stream.
Note: The data of the compression class tcompressionstream can only be written. If you try to read data from its internal system, an "error" exception will occur. The data to be compressed is written to the stream through the write method. The data is compressed during the write process and stored in the memory stream (tmemorystream) provided by the constructor. The onprocess event is triggered at the same time.
3) Decompression class tdecompressionstream: Unlike compression class tcompressionstream, its data can only be read. If you try to write data to it, an "error" exception will occur.
Its several important methods are defined as follows:
Constructor create (Source: tstream );
Where: source is the stream that stores compressed data;
Function read (VAR buffer; count: longint): longint;
Data Reading function, buffer: memory data buffer; count: buffer size;
The number of bytes read by the function. During data reading, the data is decompressed and the onprocess event is triggered.
(2) stream
In Delphi, the base class of all stream objects is the tstream class, which defines the common attributes and methods of all streams.
The attributes defined in the tstream class are as follows:
1). Size: This attribute returns the data size in the stream in bytes.
2) position: the position of the access pointer in the control flow.
There are four virtual methods defined in tstream:
1) read: This method reads data from the stream. The returned value is the number of bytes actually read. It can be smaller than or equal to the specified value.
2) Write: This method writes data into the stream, and the returned value is the number of bytes actually written into the stream.
3). Seek: This method moves the read pointer in the stream. The returned value is the position of the pointer after moving.
Function prototype: function seek (offset: longint; origint: Word): longint; virtual; abstract;
The offset parameter is the number of offset bytes. The origint parameter indicates the actual meaning of the Offset. The possible values are as follows:
Sofrombeginning: Offset indicates the position from which the pointer starts from the data. The offset value must be greater than or equal to zero.
Sofromcurrent: Offset indicates the relative position of the pointer to the current pointer after moving.
Sofromend: offset is the position at which the pointer is moved from the end of the data. The offset value must be less than or equal to zero.
4) setsize: This method changes the data size.
In addition, the tstream class also defines several static methods:
1) readbuffer: This method is used to read data from the current position of the stream, which is the same as the preceding read.
Note: When the number of data bytes to be read is different from the number of bytes to be read, an ereaderror error occurs.
2) writebuffer: This method is used to write data to the stream at the current position, which is the same as the preceding write method.
Note: When the number of written data bytes is different from the number of bytes to be written, an ewriteerror error occurs.
3) copyfrom: This method is used to copy data streams from other streams.
Function prototype: function copyfrom (Source: tstream; count: longint): longint;
The source parameter is the stream that provides data, and the count parameter is the number of copied data bytes. When count is greater than 0, copyfrom copies the data of Count bytes from the current position of the Source parameter; when count is equal to 0, copyfrom sets the position attribute of the Source parameter to 0, then copy all data from the source;
Common tstream Derived classes:
Tfilestream (file stream access)
Tstringstream (processing string data in memory)
Tmemorystream (data processing for working memory areas)
Tblobstream (processing BLOB fields)
Twinsocketstream (socket read/write processing)
Tolestream (data processing for the COM interface)
Tresourcestream (processing resource file streams)
The most common class is tfilestream. To use the tfilestream class to access files, you must first create an instance. The statement is as follows:
Constructor create (const filename: string; Mode: Word );
Filename is the file name (including the path)
Mode is the method to open a file. It includes the open mode and sharing mode of the file. Its possible values and meanings are as follows:
Open Mode:
Fmcreate: Creates a file with the specified file name. If the file already exists, open it.
Fmopenread: open a specified file in read-only mode
Fmopenwrite: open a specified file in write-only mode
Fmopenreadwrite: open a specified file in write mode
Sharing Mode:
Fm1_compat: The Shared Mode is compatible with FCBs.
Fm1_exclusive: do not allow other programs to open the file in any way
Fmsharedenywrite: do not allow other programs to open the file in write mode.
Fmsharedenyread: other programs are not allowed to open the file in Read mode.
Fmsharedenynone: other programs can open this file in any way
(3) resource files
1) create a resource file
First, create a. RC plain text file.
Format: Resource Identifier keyword resource file name
Resource Identifier: the special identifier used to call resources in a program;
Keyword: identifies the resource file type;
Wave: The resource file is a sound file;
Rcdata: JPEG file;
Avi: AVI animation;
Icon: icon file;
Bitmap: a bitmap file;
Cursor: The cursor file;
Exefile: EXE file
Resource file name: the full name of the file stored on the disk for the resource file
For example:
Myzjy exefile zjy.exe
2) Compile the resource file
Use brcc32.exe to compile the resource file. RC in the/Bind directory of Delphi. Of course, you can also copy brcc32 to the program documentation directory separately.
For example:
Brcc32 wnhoo_reg.rc
3) resource file reference
...
Implementation
{$ R *. DFM}
{$ R wnhoo_reg.res}
...
4) Call resource files
(1) Access bitmap in the resource file)
Image. Picture. bitmap. Handle: = loadbitmap (hinstance, 'Resource identifier ');
Note: If the bitmap is not loaded successfully, the program still runs, but the image will no longer display the image. You can determine whether the load is successful Based on the return value of the loadbitmap function. If the load is successful, the return value is non-0, and if the load fails, the return value is 0.
Another method for accessing the display bitmap is as follows:
Image. Picture. bitmap. loadfromresourcename (hinstance, 'Resource identifier ');
(2) access the cursor in the resource file
Screen. cursors [] is a cursor array. With the cursor file, we can add the custom cursor to this attribute. Because the default cursor index value in the array is 0, it is best to set the custom cursor index value to 1 unless you want to replace the default cursor.
Screen. cursors [1]: = loadcursor (hinstance, 'Resource identifier ');
Image. cursor: = 1;
(3) Access the icon in the resource file
Place the icon in the resource file to dynamically change the application icon.
Application. Icon. Handle: = loadicon (hinstance, 'Resource identifier ');
(4) Access avi from resource files
Animate. resname: = 'myavi'; // Resource Identifier
Animate. Active: = true;
(5) access the JPEG in the resource file
Add the JPEG unit to the uses unit.
VaR
Fjpg: tsf-image;
Fstream: tresourcestream;
Begin
Fjpg: = tsf-image. Create;
// Use tresourcestream
Fstream: = tresourcestream. Create (hinstance, 'Resource identifier', resource type );
Fjpg. loadfromstream (fstream );
Image. Picture. bitmap. Assign (fjpg );
(6) access the wave in the resource file
Add mmsystem to uses Unit
Playsound (pchar ('mywav '), hinstance, snd_async or snd_memory or snd_resource );
(4) INI File Operations
(1) Structure of the INI file:
; This is a comment on the INI File
[Node]
Keyword = Value
...
The INI file can have multiple nodes, and each node can have multiple keywords. "=" is followed by the value of this keyword (there are three types: String, integer, and boolean values. The strings are stored in the INI file without quotation marks. The Boolean true value is represented by 1, and the Boolean false value is represented by 0 ). The comment starts with a semicolon.
(2) INI File Operations
1. Add inifiles in the uses section of the interface;
2. Add one line in the VaR variable definition section: INIFILE: Tinifile. Then, you can create, open, read, and write the variable myinifile.
3. Open the INI file: INIFILE: = Tinifile. Create ('tmp. ini ');
4. Read the value of the Keyword:
A: = INIFILE. readstring ('node', 'key', default value); // string type
B: = INIFILE. readinteger ('node', 'key', default value); // Integer type
C: = INIFILE. readbool ('node', 'key', default value); // Boolean Type
[Default value] indicates the default value returned when the keyword does not exist in the INI file.
5. Write the INI file:
INIFILE. writestring ('node', 'keyword ', variable or string value );
INIFILE. writeinteger ('node', 'keyword ', variable or integer value );
INIFILE. writebool ('node', 'key', variable or true or false );
If the node of the INI file does not exist, the preceding statement will automatically create the INI file.
6. Delete keywords:
INIFILE. deletekey ('node', 'key'); // delete a keyword.
INIFILE. erasesection ('node'); // delete a node
7. node operations:
INIFILE. readsection ('node', tstrings variable); // you can read all the keyword names in the specified section to a string list variable;
INIFILE. readsections (tstrings variable); // you can read all the section names in the INI file to a string list variable.
INIFILE. readsectionvalues ('node', tstrings variable); // you can read all rows (including keywords, =, and values) in the specified section of the INI file to a string list variable.
8. Release: INIFILE. distory; or INIFILE. Free;
(5) File Association
Uses
Registry, shlobj;
// Implement associated registration
Procedure tmyzip. regzzz;
VaR
Reg: Tregistry;
Begin
Reg: = Tregistry. Create;
Reg. rootkey: = hkey_classes_root;
Reg. openkey ('. zzz', true );
Reg. writestring ('', 'myzip ');
Reg. closekey;
Reg. openkey ('myzip/Shell/Open/command', true );
// Executable program used to open the. Zzz File
Reg. writestring ('', '"' + application. exename + '"" % 1 "');
Reg. closekey;
Reg. openkey ('myzip/defaultcon ', true );
// Retrieve the icon of the currently executable program as the. Zzz File
Reg. writestring ('','' + application. exename + ', 0 ');
Reg. Free;
// Refresh now
Shchangenovel (shcn_assocchanged, shcnf_idlist, nil, nil );
End;
2.3 Implementation of encrypted Compression
1. Generate the INI temporary Encryption File
The temporary file format of the INI used for encryption:
[File1] // node. You can use file1. N in the software to encrypt multiple files.
Filename = compressed file name
Password = extract the password
Filesize = File Size
Filedate = creation date
Isjm = Password required for decompression
To store information of multiple files and folders, you can store the password keyword under a general node. This article only implements encryption for a single file, so you only need to use the above format.
2. Merge data files with the INI files used for encryption, which can be implemented in the form of file streams.
Structure of the encrypted file:
Figure (1)
Figure (2)
The above two forms can be used according to the actual situation. This article uses the structure shown in figure (1.
3. Use zlib technology to compress and store encrypted data and generate new compressed files.
2.4. For the implementation of File Association, see section 2.2 (V)
2.5 self-extract implementation
1. Create an executable program file dedicated to extraction
2. Generate a resource file from the file created in 1.
3. Place the resource file in the program of the compression tool in this article for compilation.
4. Combine the resource file with the compressed file to generate a self-extracting file.
Structure of the Self-extracting file:
5. Self-extracting: The data is decomposed by encrypting and compressing the data in the file, and then the encrypted and compressed data is decompressed again and the real data file is decomposed.
2.6 System Program Design
This is all the code about the core part of the software implementation. Here we will detail all the technical details of this software.
// Wnhoo_zzz.pas
Unit wnhoo_zzz;
Interface
Uses
Windows, forms, sysutils, classes, zlib, registry, inifiles, dialogs, shlobj;
Type
Pass = string [20];
Type
Tmyzip = Class
Private
{Private declarations here}
Protected
{Protected declarations here}
Public
Procedure regzzz;
Procedure ys_file (infilename, outfilename: string; Password: pass; isjm: Boolean; ysbz: integer );
Function jy_file (infilename: string; Password: Pass = ''): Boolean;
Procedure zjywj (VAR filename: string );
Constructor create;
Destructor destroy; override;
{Public declarations here}
Published
{Published declarations here}
End;
Implementation
Constructor tmyzip. Create;
Begin
Inherited create; // initialize the inherited part
End;
//###################################### ###############
// Encrypt the original file
Procedure jm_file (vfile: string; var target: tmemorystream; Password: pass; isjm: Boolean );
{
Vfile: Encrypted File
Target: output target stream after Encryption
Password: Password
Isjm: whether to encrypt
-------------------------------------------------------------
Size of the encrypted file = size of the original file + size of the [ini encrypted compressed information file] + size of the Data Type for storing the [ini encrypted compressed information file]
---------------------------------------------------------------
}
VaR
Tmpstream, inistream: tfilestream;
Filesize: integer;
INIFILE: Tinifile;
Filename: string;
Begin
// Open the [encrypted compressed file]
Tmpstream: = tfilestream. Create (vfile, fmopenread or fm1_exclusive );
Try
// Write [original file stream] To the end of [temporary encrypted compressed file stream]
Target. Seek (0, sofromend );
Target. copyfrom (tmpstream, 0 );
// Obtain the file path and generate the [ini encrypted and compressed information file]
Filename: = extractfilepath (paramstr (0) + 'tmp. In _';
INIFILE: = Tinifile. Create (filename );
INIFILE. writestring ('file1', 'filename', extractfilename (vfile ));
INIFILE. writestring ('file1', 'Password', password );
INIFILE. writeinteger ('file1', 'filesize', target. size );
INIFILE. writedatetime ('file1', 'filedate', now ());
INIFILE. writebool ('file1', 'isjm ', isjm );
INIFILE. Free;
// Read the [ini encrypted compressed information file stream]
Inistream: = tfilestream. Create (filename, fmopenread or fm1_exclusive );
Try
// Continue adding [ini encrypted compressed information file] To the end of [temporary encrypted compressed file stream]
Inistream. Position: = 0;
Target. Seek (0, sofromend );
Target. copyfrom (inistream, 0 );
// Calculate the size of the current [ini encrypted and compressed information file]
Filesize: = inistream. size;
// Add the size information of the [ini encrypted compressed information file] to the end of the [temporary encrypted file]
Target. writebuffer (filesize, sizeof (filesize ));
Finally
Inistream. Free;
Deletefile (filename );
End;
Finally
Tmpstream. Free;
End;
End;
//************************************** ************************
// Stream compression
Procedure ys_stream (instream, outstream: tstream; ysbz: integer );
{
Instream: the encrypted file stream to be compressed
Output file stream after outstream Compression
Ysbz: compression standard
}
VaR
YS: tcompressionstream;
Begin
// The stream Pointer Points to the header
Instream. Position: = 0;
// Compression standard selection
Case ysbz
1: YS: = tcompressionstream. Create (clnone, outstream); // do not compress
2: YS: = tcompressionstream. Create (clfastest, outstream); // Fast Compression
3: YS: = tcompressionstream. Create (cldefault, outstream); // standard Compression
4: YS: = tcompressionstream. Create (CLmax, outstream); // maximum compression
Else
YS: = tcompressionstream. Create (clfastest, outstream );
End;
Try
// Compressed stream
Ys. copyfrom (instream, 0 );
Finally
Ys. Free;
End;
End;
//************************************** ***************************
// Extract the stream
Procedure jy_stream (instream, outstream: tstream );
{
Instream: original compressed stream File
Outstream: extract the stream File
}
VaR
Jyl: tdecompressionstream;
Buf: array [1 .. 512] of byte;
Sjread: integer;
Begin
Instream. Position: = 0;
Jyl: = tdecompressionstream. Create (instream );
Try
Repeat
// Read the actual size
Sjread: = jyl. Read (BUF, sizeof (BUF ));
If sjread> 0 then
Outstream. Write (BUF, sjread );
Until (sjread = 0 );
Finally
Jyl. Free;
End;
End;
//************************************** ************************
// Implement associated registration
Procedure tmyzip. regzzz;
VaR
Reg: Tregistry;
Begin
Reg: = Tregistry. Create;
Reg. rootkey: = hkey_classes_root;
Reg. openkey ('. zzz', true );
Reg. writestring ('', 'myzip ');
Reg. closekey;
Reg. openkey ('myzip/Shell/Open/command', true );
// Executable program used to open the. Zzz File
Reg. writestring ('', '"' + application. exename + '"" % 1 "');
Reg. closekey;
Reg. openkey ('myzip/defaultcon ', true );
// Retrieve the icon of the currently executable program as the. Zzz File
Reg. writestring ('','' + application. exename + ', 0 ');
Reg. Free;
// Refresh now
Shchangenovel (shcn_assocchanged, shcnf_idlist, nil, nil );
End;
// Compress the file
Procedure tmyzip. ys_file (infilename, outfilename: string; Password: pass; isjm: Boolean; ysbz: integer );
{
Infilename: // the file to be compressed and encrypted
Outfilename: // compressed and encrypted files
Password: // extract the password
Ysbz: // compression standard
}
VaR
Instream: tmemorystream; // temporary stream after file encryption
Outstream: tfilestream; // compress the output file stream
Begin
// Create a [temporary stream after file encryption]
Instream: = tmemorystream. Create;
// File encryption
Jm_file (infilename, instream, password, isjm );
// Create a compressed output file stream
Outstream: = tfilestream. Create (outfilename, fmcreate );
Try
// [Temporary stream after file encryption] Compression
Ys_stream (instream, outstream, ysbz );
Finally
Outstream. Free;
Instream. Free;
End;
End;
// Decompress the file
Function tmyzip. jy_file (infilename: string; Password: Pass = ''): Boolean;
VaR
Instream, inistream, filestream_ OK: tfilestream;
{
Instream: // extract the file name
Inistream: // ini temporary file stream
Filestream_ OK: // decompress the OK File
}
Outstream: tmemorystream; // temporary memory stream
INIFILE: Tinifile; // temporary INI File
Filesize: integer; // the size of the password file
Resultvalue: Boolean; // Return Value
Begin
Try
Instream: = tfilestream. Create (infilename, fmopenread );
Try
Outstream: = tmemorystream. Create;
Try
Jy_stream (instream, outstream );
// Generate a temporary INI File
Inistream: = tfilestream. Create (extractfilepath (paramstr (0) + 'tmp. In _ ', fmcreate );
Try
// Point to the location of the integer variable that stores the decoding information
Outstream. Seek (-sizeof (filesize), sofromend );
// Read variable information
Outstream. readbuffer (filesize, sizeof (filesize ));
// Point to the location of the decoded Information
Outstream. Seek (-(filesize + sizeof (filesize), sofromend );
// Read the decoded information into the INI stream
Inistream. copyfrom (outstream, filesize );
// Release the INI file stream
Inistream. Free;
// Read INI File Information
INIFILE: = Tinifile. Create (extractfilepath (paramstr (0) + 'tmp. In _');
Resultvalue: = INIFILE. readbool ('file1', 'isjm ', false );
If resultvalue then
Begin
If INIFILE. readstring ('file1', 'Password', '') = trim (password) then
Resultvalue: = true
Else
Resultvalue: = false;
End
Else
Resultvalue: = true;
If resultvalue then
Begin
Filestream_ OK: = tfilestream. Create (extractfilepath (paramstr (1) + INIFILE. readstring ('file1', 'filename', 'wnhoo. zzz'), fmcreate );
Try
Outstream. Position: = 0;
Filestream_ OK .copyfrom (outstream, INIFILE. readinteger ('file1', 'filesize', 0 ));
Finally
Filestream_ OK .free;
End;
End;
INIFILE. Free;
Finally
// Delete the temporary INI File
Deletefile (extractfilepath (paramstr (0) + 'tmp. In _');
End;
//
Finally
Outstream. Free;
End;
Finally
Instream. Free;
End;
Except
Resultvalue: = false;
End;
Result: = resultvalue;
End;
// Self-extract to create
Procedure tmyzip. zjywj (VAR filename: string );
VaR
Myres: tresourcestream; // temporarily stores self-extracted EXE files
Myfile: tfilestream; // original file stream
Xfilename: string; // temporary file name
File_ OK: tmemorystream; // memory stream for generating files
Filesize: integer; // the size of the original file.
Begin
If fileexists (filename) then
Begin
// Create a memory stream
File_ OK: = tmemorystream. Create;
// Release the resource file-Self-decompress the EXE file
Myres: = tresourcestream. Create (hinstance, 'myzjy', pchar ('expifile '));
// Read the original file into the memory
Myfile: = tfilestream. Create (filename, fmopenread );
Try
Myres. Position: = 0;
File_ OK .copyfrom (Myres, 0 );
File_ OK .seek (0, sofromend );
Myfile. Position: = 0;
File_ OK .copyfrom (myfile, 0 );
File_ OK .seek (0, sofromend );
Filesize: = myfile. size;
File_ OK .writebuffer (filesize, sizeof (filesize ));
File_ OK .position: = 0;
Xfilename: registrchangefileext(filename,'.exe ');
File_ OK .savetofile (xfilename );
Finally
Myfile. Free;
Myres. Free;
File_ OK .free;
End;
Deletefile (filename );
Filename: = xfilename;
End;
End;
//###################################### ###############
Destructor tmyzip. Destroy;
Begin
Inherited destroy;
End;
End.
3. Conclusion
Delphi's new visual programming environment provides us with a convenient and quick windows application development tool. For program developers, using Delphi to develop application software will undoubtedly greatly improve programming efficiency. In Delphi, you can easily use stream to implement File Processing, dynamic memory processing, network data processing, and other data forms. Writing programs also greatly improves the efficiency.
References:
1. Delphi system help
2. Feng Zhiqiang. Application of compressed stream and decompressed stream in Delphi
3. Chen Jingtao: "stream" in Delphi programming"