Grammar

xiaoxiao2021-03-06  75

Grammar

Delphi is equivalent to this two aspects. In the early version of Delphi, Turbo Pascal has a class that has been specifically used for object-based data management in Turbo Pascal. In Delphi, these functions have been greatly enhanced. Delphi classifies the object data management class into a Stream Object (Filer) and applies them to all aspects of the visual component library (VCL). They not only provide the function of managed objects in memory, deposit, and Windows resources, but also provide the functionality of objects in the database blob field.

In this chapter, the implementation principle, application method, and application in the super-media system will be described in this chapter. This is important for using delphi development of advanced applications.

20.1 Implementation principle and application of stream object

Stream object, also known as a streaming object, is a collective that TSTREAM, THANDLESTREAM, TFileStream, TMemoryStream, TResourceReam, and Tblobstream, etc. They represent the ability to store data on various media, which abstracts the management operations of various data types (including objects and components) in memory, existence, and database fields, and makes full use of object-oriented technology Advantages, applications can be quite easily copied in various Stream objects.

The data and methods and methods of use of various objects are described below.

20.1.1 TSTREAM Object

The TSTREAM object is an abstract object that can store the object of binary data in various media. Objects inherited from TSTREAM objects are used to store data in media such as memory, Windows resource files, disk files, and database fields.

Two properties are defined in TSTream: Size and Position. They represent the stream of streams and the current pointer position in bytes in bytes. The method defined in TSTream is used to read, write, and mutually copy secondary data in various streams. Because all stream objects are inherited from TSTream, the domains and methods defined in TSTream can be called and accessed by the Stream object. In addition, the TSTREAM provides a unified interface for the application of various streams, simplifies the use of streams; different stream objects are abstracts the operation of different storage media, so The need for TSTREAM provides the most simple means for data copies between different media.

20.1.1.1 Attributes and Methods of TSTREAM

Position attribute

Disclaimer: Property Position: longint;

The position attribute specifies the current offset read and written in the stream.

2. size attribute

Disclaimer: Property Size: longint;

The size property indicates the size of the stream in bytes, it is read-only.

3. CopyFrom method

Disclaimer: Function CopyFrom (Source: TSTREAM; Count: longint): Longint

CopyFrom Copy Count bytes from the stream specified by Source to the current stream, move the pointer from the current location, the number of characters of the function return is the number of bytes actually copied.

4. Read method

Disclaimer: Function Read (VAR Buffer; Count: longint; virtual; abstentract;

The READ method copies the contents of the COUNT bytes into the buffer from the current location in the current stream, and moves the current pointer to the number of bytes, and the function return value is the number of bytes actually read. If the return value is less than Count, this means that the read operation is reached the end of the stream before reading the number of bytes of bytes.

The read method is an abstract method. Each subsequent Stream object must override this method based on your own unique read operations on a particular storage medium. Moreover, all other read data of the stream (such as: ReadBuffer, ReadComponent, etc.) call the Read method when the actual read operation is completed. The advantage of object-oriented dynamic keratual is reflected here. Because after successive Stream objects simply override the Read method, other read operations (such as readbuffer, readcomponent, etc.) do not need to be redefined, and TSTREAM also provides a unified interface. 5. Readbuffer method

Disclaimer: Procedure Readput (VAR Buffer; Count: longint);

The READBUFFER method is copied from the stream to the buffer and moves the current pointer to the rear byte. If the reading operation exceeds the end of the stream, the ReadBuffer method causes an EREADERROR exception event.

6. Readcomponent method

Disclaimer: Function Readcomponent: tComponent;

The READComponent method reads the components specified by Instance in the current stream, and the function returns the component read. ReadComponent creates a Reader object when reading instance and all objects it owns and calls its ReadrootComponent method.

If Instance is a NIL, the readcomponent method creates a component based on the component type information described in the stream and returns the newly created part.

7. Readcomponentres method

Disclaimer: Function Readcomponentres (Instance: Tcomponent): Tcomponent

The READComponentRES method reads the components specified in the stream, but the current location of the stream must be the location of the part written by the WriteComponentRES method.

ReadComponentres first calls the ReadresHeader method to read the resource header from the stream, and then call the ReadComponent method to read instance. If the current location of the stream does not contain a resource header. ReadresHeader will trigger an einvalidImage event. In the Classes library unit, there is also a function called ReadComponentRes, which performs the same operation, but it creates its own stream based on the resources contained in the application.

8. ReadresHeader method

Disclaimer: Procedure Readreshead;

The Readresheader method reads the Windows resource file header from the current location of the stream and moves the current position pointer to the tail of the file head. If the stream does not contain a valid resource file, ReadresHeader will trigger an EinValidImage anomalous event.

The READComponentRES method of the stream automatically calls the Readresheader method before reading the part from the resource file, so usually the programmer usually does not need to call it.

9. SEEK method

Disclaimer: Function seek (Offset: longint; origin: word): long;

The SEEK method moves the current pointer of the stream to the offset bytes, and the starting point of the byte move is specified by Origin. If Offset is a negative number, the Seek method will move from the beginning of the described starting point. The different values ​​of Origin and their meaning are listed in the following table:

Table 20.1 Value of the parameters of the function seek

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Value of the starting point OFFSET of the constant value Seek

─────────────────────────────────────── ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─

SOFROMCURRENT 1 flow current position positive or negative number

SofroMend 2 stream end negative

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

10. Write method

There are two types of objects in the target of Delphi objects, are called Write: Stream objects and Filer objects. The WRITE method of the Stream object writes the data into the stream. The Filer object passes the relevant streaming data, which is described later.

The WRITE method of the Stream object is declared as follows:

Function Write (CONST BUFFER): Longint; Virtual; ABSTRACT

Write Method writes in the stream in the buffer and moves the COUNT byte to the tail of the current position pointer, and the function returns the number of bytes written.

The WRITE method of TSTREAM is abstract, and each inherited Stream object must provide a particular method of writing data to a specific storage (memory, disk file, etc.) by overwriting the method. All other write data of the stream (such as WriteBuffer, WriteComponent) call WRITE to actually write.

11. WriteBuffer method

Disclaimer: Procedure WriteBuffer (CONST BUFFER); CONGINT

WriteBuffer features Similar to Write. The WriteBuffer method calls Write to perform the actual write operation. If the stream can write all bytes, WriteBuffer triggers an EWRITEERROR an exception event.

12. WriteComponent method

There are methods called WriteComponents at Stream objects and Filer objects. The WriteComponent method of the Stream object writes the components specified by Instance and all parts therefrom. Writer objects' WriteComponent writes the attribute value of the specified part to the flow of the Writer object.

The WriteComponent method of the Stream object is this:

Procedure WriteComponent (Instance: tcomponent);

WriteComponent creates a Writer object and calls Writer's WriterootComponent method writes instance and its objects.

13. WriteComponentres method

Disclaimer: WriteComponentres (const resName: String; Instance: tcomponent);

The WriteComponentRES method first writes the standard Windows resource file header in the stream and writes the components specified by Instance into the stream. To read the components written by WriteComponentres, you must call the Readcomponentres method.

WriteComponentRes uses the resName incoming string as the resource name of the resource file, and then calls the WriteComponent method to write instance and the parts it owns.

14. WriteDescendant method

Disclaimer: Procedure WriteScendant (Instance Ancestor: Tcomponent);

The WriteDescendant method of the Stream object creates a Writer object and then the WriteDescendant method that is transferred to the object will write the Instance part into the stream. The Instance can be a form inherited from the ANCESTOR component, or may be a component corresponding to the Ancestor component in the ancestor form in a form inherited from the ancestral form.

15. WriteDescendantres method declaration: Procedure WriteScendantres (Const ResName: String)

Instance, Ancestor: Tcomponent;

WriteDescendantres method writes the Windows resource file header and uses the ResName role resource name, then call the WriteDescendant method to write the instance.

20.1.1.2 Principle of TSTREAM

TSTREAM objects are the basic class of Stream objects, which is the basis of the stream object. In order to store data objects on different media, the subsequent Stream object is mainly improved in the Read and Write methods. Therefore, it is learned that TSTREAM is the core of mastering Stream object management. Although Borland provides documentation of the STREAM object, it is not mentioned for its implementation and application methods, and the author is a streaming object technology from the source code and some examples provided from Borland Delphi 2.0 Client / Server Suite.

The following is the implementation of the properties and methods of TSTREAM.

1. Implementation of TSTREAM properties

As mentioned earlier, TSTREAM has two properties of Position and Size, as an abstract data type, which abstracts the domain that reads and write data in various storage media. So how do they achieve?

The read and write control in the definition of component properties is described in this chapter in the custom component. Position and size have also made read and write controls. Defined as follows:

Property Position: Longint Read getPosition Write setPosition;

Property Size: Longint Read GetSize;

It is known that Position is a readable write property, and size is read-only.

The implementation of the position attribute is reflected in getPosition and setPosition. When in the program running, anything that reads the Position value and the operation assigned to the Position will automatically trigger a private method getPosition and setPosition. The declarations of the two methods are as follows:

Function TSTREAM.GETPSITION: longint;

Begin

Result: = SEEK (0, 1);

END;

Procedure TStream.SetPosition (POS: longint);

Begin

Seek (POS, 0);

END;

When setting the position, the Delphi compilation mechanism will automatically pass the position to POS.

The first parameter is the mobile offset, the second parameter is the starting point of the mobile, and the return value is the moving pointer position.

The implementation of the Size property is only read control, and the write operation is completely shielded. The read control method getsize is implemented as follows:

Function TSTREAM.GETSIZE: Longint

VAR

POS: longint;

Begin

POS: = SEEK (0, 1);

Result: = SEEK (0, 2);

Seek (POS, 0);

END;

2. Implementation of TSTREAM method

(1) CopyFrom method

CopyFrom is a useful method in the Stream object that is used to copy data in different storage media. For example, between memory and external files, memory and database fields, etc. It simplifies a number of memory allocations, file open, and read and write, etc., unifies all copies to the Stream object.

As mentioned earlier: CopyFrom method with two parameters of Source and Count and returns long. This method copies the content of the count from Source to the current stream, if the count value is 0, copy all the data. Function TSTREAM.COPYFROM (Source: TSTREAM; Count: longint): Longint

Const

Maxbufsize = $ f000;

VAR

BUFSIZE, N: Integer;

Buffer: pchar;

Begin

If count = 0 THEN

Begin

Source.position: = 0;

Coung = "en-cn"> When the part in the resource file is called, the programmer does not need to be called. If the read is not a resource file Readresheader, an exception event will be triggered.

Procedure TSTREAM.READRESHEADER;

VAR

Readcount: longint;

HEADER: Array [0..79] of char;

Begin

Fillchar (Header, Sizeof (Header), 0)

Readcount: = Read (HEADER, SIZEOF (HEADER) - 1);

IF (Byte (@Header [0]) ^) = $ ff) and (Word ((@Header [1]) ^) = 10) THEN

Seek (Strlen (HEADER 3) 10 - Readcount, 1)

Else

Raise EinvalidImage.createres (SINVALIMAGE);

END;

ReadComponentres reads the part in the Windows resource file, in order to determine if it is a resource file, it first calls the ReadresHeader method, then call the ReadComponent method to read the component specified by Instance. Below is its implementation:

Function TSTREAM.Readcomponentres (Instance: Tcomponent): tComponent

Begin

Readresheader;

Result: = Readcomponent (Instance);

END;

Writing method with readcomponentres is WriteComponentres, Delphi calls these two ways to read and write form files (DFM files), which will be used to read the DFM files in the back book.

⑷ WriteComponent and WriteDescendant methods

The WriteDescendant method of the Stream object creates a Twriter object during the implementation, and then writes Instances into the stream with the WriteDescendant method of TWRITER. The WriteComponent method simply calls the WriteDescendant method to write instance to the stream. Their implementations are as follows:

Procedure TStream.writeComponent (Instance: Tcomponent);

Begin

WriteScendnt (Instance, NIL);

END;

Procedure TStream.writeDescend (Instance, Ancestor: Tcomponent);

VAR

Writer: twriter;

Begin

Writer: = twriter.create (Self, 4096);

Try

Writer.writeDescend (Instance, Ancestor);

Finally

Writer.free;

END;

END;

⑸ WriteScendantres and WriteComponentres methods WriteScendantres methods are used to write parts to Windows resource files; and WriteComponentres methods simply call the WriteDescendantres method, and their implementations are as follows:

Procedure TStream.writeComponentres (const resname: String; Instance:

Tcomponent;

Begin

WriteDescendentres (RESNAME, INSTANCE, NIL);

END;

Procedure TStream.writeDescendentres (const resname: String; Instance,

Ancestor: tComponent;

VAR

Headersize: integer;

Origin, Imagesize: longint;

HEADER: Array [0..79] of char;

Begin

Byte (@Header [0]) ^): = $ ff;

Word ((@Header [1]) ^): = 10;

Headersize: = Strlen (Strupper (Struplcopy (@Header [3], ResName, 63))) 10;

Word ((@Header [Headersize - 6]) ^): = 1030;

Longint ((@Header [Headersize - 4]) ^): = 0;

Writebuffer (header, headersize);

ORIGIN: = position;

WriteDescendnt (Instance, Ancestor);

Imagesize: = POSITION - Origin

Position: = Origin - 4;

WriteBuffer (ImageSize);

Position: = Origin Imagesize;

END;

WriteCompnentres is a corresponding object written with ReadComponentres, which mutually cooperates with each other to read Delphi's DFM file, which utilizes the functionality of the Delphi system.

20.1.2 THANDLESTREAM object

The behavior of the ThandleStream object is particularly like the FileStream object, which is different from the data that has been created instead of the file name to store data.

The THANDLESTREAM object defines the Handle property that provides read-only access to the file handle, and the handle property can be used as the parameters of the RTL file management function of Delphi, using the file class function to read and write data. The THANDLESTREAM covers the constructor crete, which specifies the file handle associated with the THANDLESTREAM object.

20.1.2.1 Method for properties of THANDLESTREAM:

Handle property

Disclaimer: Property Handle: Integer;

The Handle property provides only read-only access to the file handle, which is incorporated by the THANDLESTREAM constructor. Therefore, in addition to the methods provided with THANDLESTREAM, you can also operate with the handle with the file management function. In fact, the THANDLESTREAM method is also implemented using the file management function to actually read and write operations.

2. CREATE method

Disclaimer: Constructor Creger (AHANDLE: Integer);

The CREATE method creates a THANDLESTREAM object associated with a specific file handle using the incoming Handle parameter and assigns the AHANDLE. 3. Read, WRITE and SEEK methods

These three methods are the true method of TSTREAM, but these three methods are overwritten in THANDLESTREAM to achieve data access of a specific medium-file. The implementation of these three methods will be described later.

20.1.2.2 THANDLESTREAM implementation principle

ThandleStream is inherited from TSTREAM, so you can share the properties and most methods of TSTREAM. The ThandleStream is now mainly increasing a attribute handle and overcomes four methods of Create, Read, Write, and Seek.

1. Implementation of attributes

The implementation of the Handle property is as realized by most of the Delphi attribute, first declares a variable fhaandle stored in the object-defined private section, and then declares the attribute handle in the defined public section, where the read and write control section defined by the property is read-only Control, read control only reads the value of the FHANDLE variable directly, as follows:

THANDLESTREAM = Class (TSTREAM)

Private

FHANDE: Integer;

public

...

Property Handle: Integer Read Fhandle;

END;

2. Implementation

ThandleStream's Create method, as a parameter in AHANDLE, just simply assigns the value of AHANDLE to FHANDLE, as follows:

Constructor ThandleStream.create (AHANDLE: Integer);

Begin

FHANDE: = ahandle;

END;

To implement data object storage for files, the THANDLESTREAM's READ, WRITE, and SEEK methods overwrite the corresponding methods in TSTREAM. They have allocated Windows file management functions.

The READ method calls the FileRead function to implement the file read operation, which is implemented as follows:

Function ThandleStream.read (VAR Buffer; Count: longint): longint

Begin

Result: = FileRead (Fhandle, Buffer, Count);

if Result = -1 Then Result: = 0;

END;

The Write method calls the FileWrite function to implement the file write operation, which is implemented as follows:

Function ThandleStream.write (CONST BUFFER): Longint;

Begin

Result: = FileWrite (Fhandle, Buffer, Count);

if Result = -1 Then Result: = 0;

END;

The SEEK method calls the Fileseek function to move the mobile file pointer, which is implemented as follows:

Function ThandleStream.seek (Offset: longint; Origin: Word): Longint

Begin

Result: = Fileseek (Fhandle, Offset, Origin);

END;

20.1.3 TFileStream object

The TFileStream object is a stream object that stores data on disk files. TFileStream is inherited from THANDLESTREAM, and it is the access operation of the file and the THANDLESTREAM. Differences are the THANDLESTREAM access files with handles, and TFileStream uses a file name to access the file. In fact, TFileStream is a layer of packaging on THANDLESTREAM, and its kernel is the properties and methods of THANDLESTREAM. New properties and methods are not added in TFileStream. It is only overwritten constructor CREATE and sectoral methods. Bring two parameters FileName and MODE in the Create method. FileName describes file names to create or open, while Mode description file patterns such as FMCReate, FmopenRead, and FmopenWrite. The CREATE method first creates or opens a file called FileName using the FileCreate or FileOpen function, and then the resulting file handle is assigned to Fhaandle. TFileStream's file read and write operations are written from ThandleStream inherited.

VAR

Stream: TSTREAM;

Begin

Stream: = TfileStream.create (filename, fmcreate);

Try

SaveTostream (stream);

Finally

Stream.free;

END;

END;

There are similar nested structures in many objects of Delphi's many objects, LoadFromstream and LoadFire methods.

20.1.5 TmemoryStream object

The TMEMORYSTREAM object is a Stream object that manages data in dynamic memory. It is inherited from the TCUSTomMemoryStream. In addition to the attributes and methods inherited from the TCUSTomMemoryStream, it also increases and covers some of them to be used by disk files and other annotations. Method of reading data. It also provides a dynamic memory management method that writes to eliminate memory content. The following describes these properties and methods.

20.1.5.1 Attributes and Methods of TmemoryStream

Capacity property

Disclaimer: Property Copacity: longint;

The Capacity property determines the size of the memory pool assigned to the memory stream. This is somewhat different from the size property. The Size property is the size of the data in the stream. In the program, you can set the maximum memory required by the value of Capacity, which avoids frequent reassignment.

2. Realloc method

Disclaimer: Function Realloc (VAR Newcapacity: longint): Pointer; Virtual;

The Realloc method, allocates dynamic memory in units of 8K, and the size of the memory is specified by newcapacity, and the function returns a pointer to allocated memory.

3. Setsize method

The setsize method eliminates the data contained in the memory stream and set the size of the memory pool in the memory stream to a size byte. If size is zero, it is a SetSize method to release the existing memory pool and set the MEMORY property to nil; otherwise, the setsize method adjusts the memory pool size to size.

4. Clear method

Declaration: procedure clear;

The CLEAR method releases the memory pool in the memory and sets the MEMORY attribute to NIL. After calling the Clear method, the size and position properties are 0.

5. LoadFromstream method

Disclaimer: Procedure LoadFromstream (Stream: TStream);

LoadFromstream method copies all the contents in the stream specified to MemoryStream, and the replication process will replace existing content, making MemoryStream as a copy of Stream. 6. LoadFromFile method

Disclaimer: Procedure LoadFromFile (count filename: string);

LoadFromFile method copies all the contents of the filename to MemoryStream and replaces existing content. After calling the LoadFromFile method, MemoryStream will become a complete copy of the file content in memory.

20.1.5.2 Implementation principle of TMEMORYSTREAM object

TMEMORYSTREAM is directly inherited from the TCUSTomMemoryStream object, so you can enjoy the properties and methods of TCUSTomMemReam. As mentioned earlum, TCUSTomMemoryStream is an abstract object for data operations in memory. It provides a framework for the implementation of the MemoryStream object, and the contents of the frame must be filled by the specific MemoryStream object. The TMEMORYSTREAM object is the specific content in the framework that needs to populate the framework by dynamic memory management. The implementation of the TMEMORYSTREAM object will be described below.

1. Implementation of TMEMORYSTREAM properties

TMEMORYSTREAM adds a Capacity property in its protected section that determines the size of the MemoryStream accounts for the size of the dynamic memory. TMEMORYSTREAM first declares the Fcapacity variable as a data domain that stores the Capacity property value, then declares that this property is declared at the Protected section. On the read control section of the attribute declaration, simply read the value of Fcapacity, and call the method setcapacity at the write control. The method is required to modify the Capacity property in addition to the Fcapacity property, such as status changes.

The following is the implementation of the property:

TmemoryStream = Class (TCUSTomMemoryStream)

Private

Fcapacity: longint;

Procedure setCapacity (NewCapacity: longint);

protected

...

Property Capacity: Longint Read Fcapacity Write setCapacity;

public

...

END;

The implementation of the write control method setcapacity is like this:

Procedure TmemoryStream.SetCapacity (NewCapacity: longint);

Begin

SetPointer (Realloc (Newcapacity), fsize);

Fcapacity: = newcapacity;

END;

In the SetCapacity method, first call Realloc to reassign the memory, then assign the Fcapacity with the value of NewCapacity. The Realloc method changes to some object states.

2. Implementation of the TMEMORYSTREAM object method

(1) Realloc method

The Realloc method is the core of the TMemoryStream dynamic memory allocation. Its setsize, setcapacity and other methods are ultimately called Realloc to allocate and initialize work. Its implementation is as follows:

Const

MemoryDelta = $ 2000;

Function TmemoryStream.realloc (VAR NewCapacity: longint): Pointer;

Begin

IF newcapacity> 0 THEN

NEWCAPACITY: = (Newcapacity (MemoryDelta - 1)) and not (MemoryDelta - 1); result: = Memory

IF newcapacity <> fcapacity then

Begin

IF newcapacity = 0 THEN

Begin

GlobalFreePtr (Memory);

Result: = NIL;

END ELSE

Begin

if Capacity = 0 THEN

Result: = GlobalAllocptr (HeapAllocflags, Newcapacity)

Else

Result: = GlobalReallocptr (Memory, Newcapacity, HeapAllocflags);

If Result = nil the raise estreamerror.createres (SMEMORYSTREAMERROR);

END;

END;

END;

The REALLOC method is assigned dynamic memory in 8K units, and the first sentence IF statement in the method is to perform this operation. If the incoming Newcapacity parameter value is 0, the memory in the stream is released. The realloc method releases memory with the Global FreePtr function, allocates memory with GlobalAllocptr, and use GlobalReallocptr to redistribute. If the original Capacity property value is 0, the Globa | Allocptr may call GlobalReallocptr. Finally, if the result is NIL, the memory flow error is triggered, otherwise returns a pointer to the assigned memory.

(2) WRITE method

Write method is written to binary data from the current location of the internal buffer pool of the memory stream. It is achieved as follows:

Function TMemoryStream.write (CONST BUFFER): Longint

VAR

POS: longint;

Begin

IF (fposition> = 0) and (count> = 0) THEN

Begin

POS: = fposition count;

IF POS> 0 THEN

Begin

IF Pos> fsize kil

Begin

IF Pos> Fcapacity Then

SetCapacity (POS);

Fsize: = POS;

END;

System.move (Buffer, Pointer (long (fmemory fposition) ^, count)

FPSITION: = POS;

RESULT: = Count;

EXIT;

END;

END;

Result: = 0;

END;

In the buffer, you want to write binary data to write the stream. If the byte of the data to be written beyond the size of the flow memory pool, call the SetCapacity method to allocate the memory, then use the memory replication function to copy data in the buffer to FMEMory. in. Then move the position pointer and return the number of bytes written to the data. Analysis of this program can know that the value of Fcapacity and the value of fsize are different.

(3) Clear method

The CLEAR method eliminates the data in the memory stream, set the Memory property to NIL, and set the value of fsize and fposition to 0. It is achieved as follows:

Procedure tmemorystream.clear;

Begin

SetCapacity (0);

Fsize: = 0;

FPSITION: = 0;

END;

⑷ LoadFromstream and LoadFromFile method LoadFromstream method First redistribute dynamic memory according to the incoming STREAM, then call the Stream's ReadputBuffer method to copy data into the FMemory, and the full content of Stream has a complete copy in memory. It is achieved as follows:

Procedure TmemoryStream.LoadFromstream (Stream: TStream);

VAR

Count: longint;

Begin

Stream.position: = 0;

Count: = stream.size;

Setsize (count);

If Count <> 0 Then Stream.Readbuffer (FMemory ^, Count);

END;

LoadFromFile and LoadFromstream are a pair method. LoadFromFile first creates a TFileStream object, then call the LoadFromstream method to write data in the FileStream file stream into MemoryStream.

转载请注明原文地址:https://www.9cbs.com/read-120926.html

New Post(0)