What is the flow? Flow, simply, is a tool to establish an abstract processing data on an object-oriented basis. In the stream, some basic operations of processing data are defined, such as reading data, write data, etc., programmers are convection all operations without having to care about the true flow of the other data of the stream. The flow can not only handle files, but also process dynamic memory, network data, etc. If your convection operation is very skilled, it utilizes the convenience of flow in the program, and the writing process will greatly improve efficiency. Below, the author uses four examples: EXE file encrypse, electronic greeting card, self-made OICQ, and network screen transfer to illustrate the utilization of Delphi programming. Some skills in these examples have been the secrets of many software, and now everyone can directly quote the code. "Wanzhang high rise is flat", before analyzing example, let's first understand the basic concepts and functions of the stream, and we can carry it next after understanding these basic things. Be sure to carefully understand these basic methods. Of course, if you are already familiar with them, you can skip this step. 1. The basic concepts and function declarations in Delphi in Delphi, the base class of all current objects is a TSTREAM class, which defines the common attributes and methods of all streams. The properties defined in the TSTream class are described below: 1. Size: This property is in the data size in the stream. 2, Position: This property controls the location of the pointer in the stream. The virtual method defined in TSTream has four: 1, read: This method implements the data from the stream. The function is prototype: function read (var buffer; count: long): longint; virtual; abstract; parameter buffer is a buffer placed when the data is read, and Count is the number of bytes of data that needs to be read, and the method returns the value. The number of bytes read out, it can be less than or equal to the value specified in Count. 2, WRITE: This method implements writing data to the stream. The function of the function is: function write (var buffer; count: long): longint; virtual; abstract; parameter buffer is the buffer of data to be written into the stream, and count is the length byte number of data, the method returns value is actual Write the number of bytes in the stream. 3. SEEK: This method implements the movement of the pointer in the stream. The function is: function seek (offset: longint; origint: word): line; virtual; abstract; parameter OFFSET is the number of offset bytes, the parameter origint points out the actual meaning of the OFFSET, which may be as follows: SOFROMBEGINNING: OFFSET is Move the position of the pointer to the start of the data. At this time, the OFFSET must be greater than or equal to zero. SOFROMCURRENT: OFFSET is the relative position of the movement pointer and the current pointer. SofroMend: OFFSET is the position of the end of the pointer distance data. At this time, OFFSET must be less than or equal to zero. The method returns the value of the moving pointer. 4. Setsize: This method implements the size of the data. The function is: function setsize (newsize: longint); Virtual; further, several static methods are also defined in the TSTREAM class: 1, ReadBuffer: This method is to read data from the current location in the stream. The function is: Procedure Readbuffer (var buffer; count: longint); the definition of the parameter is the same as the Read above. Note: An EREADERROR exception is generated when the number of data bytes read is different from the number of bytes you need to read. 2, WriteBuffer: The role of this method is to write data in the current location. The function is: Procedure WriteBuffer (var buffer; count: longint); the definition of the parameter is the same as the WRITE above.
Note: EWRITEERROR exception will be generated when the number of data bytes written is different from the number of bytes that needs to be written. 3, CopyFrom: The role of this method is to copy the data stream from other streams. The function is: function CopyFrom (Source: TSTREAM; Count: longint): longint; parameter source is the number of data bytes that provide data. When Count is greater than 0, CopyFrom is copied from the current location of the Source parameter. When count is equal to 0, the copyFrom sets the position attribute of the Source parameter to 0, then copy all the data of the Source; TSTREAM has other derived classes The most commonly used TFileStream class. Use the TFileStream class to access files, first create an instance. The declaration is as follows: constructor create (const filename: String; Mode: Word); filename is a file name (including path), parameter mode is open file, which includes file open mode and sharing mode, which may take value and meaning As follows: Open mode: fmcreate: Take it with the specified file name, if the file already exists, open it. FmopenRead: Open the specified file in a read-only mode: Open the specified file with only Write mode: Open the specified file sharing mode with a write mode: FMShareCompat: Share mode is compatible with FMShareExClusive: Do not allow other programs to open this file in any way FMSharednywrite: Does not allow other programs to open this file fmsharedenyread: Do not allow other programs to open this file fmsharednynnenone: other programs can open this file in any way. TSTREAM has a derived class TMEMORYSTREAM, actual application The number of times is very frequent. It is called memory flow, that is, establish a stream object in memory. Its basic methods and functions are the same below. Ok, after I have the above foundation, we can start our programming. -------------------------------------------------- --------------------- Second, one of the practical applications: Use flow to make EXE file encryprators, bundle, self-extracting files and installer let us first say how Make an EXE file encrypler. The principle of the EXE file encrypler: Create two files, one to add resources to another EXE file, called add programs. Another added EXE file is called a header file. The program's function is to read the file added to yourself. The EXE file structure under Windows is more complicated, and some programs also have checks, and they will refuse to perform by viral infection after they are changed. So we add files to your own program so that we will not change the original file structure. Let's write a function, the function of the function is to add a file as a stream to the end of another file.
Function is as follows: Function Cjt_AddtoFile (SourceFile, TargetFile: string): Boolean; varTarget, Source: TFileStream; MyFileSize: integer; begintrySource: = TFileStream.Create (SourceFile, fmOpenRead or fmShareExclusive); Target: = TFileStream.Create (TargetFile, fmOpenWrite or FMShareExClusive; trytarget.seek (0, SofroMend); // Add resource Target.copyFrom (Source, 0) to the tail, 0); MyFileSize: = source.size sizeof (MyFileSize); // Calculate resource size, and write access Target.writebuffer (MyFilesize); FinallyTarget.Free; Source.Free; End; ExceptResult: = false; exit; end; result: = true; end; With the above foundation, we should be easy to see I have to understand this function. The parameter sourcefile is the file to be added, and the parameter TargetFile is the target file being added. For example, add A.EXE to B.EXE: CJT_ADDTOFILE ('a.exe', b.exe '); returns true if you add success or return. According to the above function, we can write the opposite readout function: Function Cjt_LoadFromFile (SourceFile, TargetFile: string): Boolean; varSource: TFileStream; Target: TMemoryStream; MyFileSize: integer; begintryTarget: = TMemoryStream.Create; Source: = TFileStream. Create (SourceFile, fmOpenRead or fmShareDenyNone); trySource.Seek (-sizeof (MyFileSize), soFromEnd); Source.ReadBuffer (MyFileSize, sizeof (MyFileSize)); // read the resource size Source.Seek (-MyFileSize, soFromEnd); // Position to the resource location target.copyfrom (source, myfilesize-size); // Remove the resource target.savetofile (targetfile); // Store FinallyTarget.Free; Source.Free; End; ExceptResult: = FALSE EXIT; END: = true; END; where the parameter sourcefile is the file name that has already been added, the parameter targetfile is the target file name saved after the file is removed. For example, CJT_LoadFromFile ('B.exe', 'a.txt'); Take out files in B.EXE to be saved as A.txt. Returns True if you take a success, or return false. Open Delphi, create a new project, put an edit control EDIT1 and two Button: button1 and button2 on the window. Button's CAPTION attribute is set to "OK" and "Cancel".
Write code in Button1 Click event: var s: string; begins: = changefileext (application.exename, '. CJT'); if edit1.text = '790617' TENGINCJT_LOADFROMFILE (Application.exename, s); {Remove file Save under the current path and name "Original file .cjt"} Winexec (PCHAR (S), SW_SHOW); {Run "Original file .cjt"} Application.Terminate; {Exit Program} endelse Application.MessageBox ('Password is wrong, Please re-enter! ',' Password Error ', MB_ICONERROR MB_OK; compile this program and rename the EXE file to Head.exe. Create a text file head.rc, the content is: Head exefile head.exe, then copy them into the bin directory of Delphi, execute the DOS command brcc32.exe head.rc, will generate a head.res file, this file is The resources we want will stay first. Our header file has been established, let's establish an adding program. Create a new project, put the following controls: an Edit, an OpenDialog, two Button1 CAPTION attributes are set to "Select File" and "Encryption". Add a sentence in the source program: {$ R Head.RES} and copy the head.res file to the current directory of the program. This will compile the Head.exe with the program. Write the code in the CILCK event of Button1: if OpenDialog1.execute1 Edit1.text: = Opendialog1.FileName; write code in the cilck event of Button2: var s: string; begins: = extractFilepath (edit1.text); if Extractres ('EXEFILE', 'Head', S 'Head.exe') Thenif CJT_ADDTOFILE (Edit1.Text, S 'Head.exe') Thenif Deletefile (Edit1.Text) Thenif Renamefile (S 'Head.exe', Edit1. Text) thenApplication.MessageBox ( 'file encryption success!', 'information', MB_ICONINFORMATION MB_OK) elsebeginif FileExists (S 'head.exe') then DeleteFile (S 'head.exe'); Application.MessageBox ( 'file encryption failed ! ',' Information ', MB_ICONITION MB_OK) end; end; where extractres is a custom function, its role is to take Head.exe from the resource file.
Function ExtractRes (ResType, ResName, ResNewName: String): boolean; varRes: TResourceStream; begintryRes: = TResourceStream.Create (Hinstance, Resname, Pchar (ResType)); tryRes.SavetoFile (ResNewName); Result: = true; finallyRes.Free End; ExcePtResult: = false; end; end; Note: We have the above functions only simply add a file to the end of another file. Active application can be changed to add multiple files, as long as the offset address is defined according to the actual size and number of numbers. For example, the file bundle is added to one or more programs to a header file. The principles of the self-demired procedures and installation procedures are also the same, but more compression. For example, we can reference a LAH unit and add the stream compressed, so that the file will become small. When you read it, you can decompress it. In addition, there are many ways to exist in the text, such as the password is fixed to "790617". After removing the EXE operation, it should wait until it is running, and the reader can modify itself. -------------------------------------------------- ------------------ Third, the actual application 2: Use flow to make executive E-greetings, we often see some electronic greeting software, you can let yourself Select a picture, then it will generate an Exe executable to you. When you open your greeting card, you will display a picture while playing music. Now, after learning the flow of flow, we can do one. Add a picture process We can use the front CJT_ADDTOFILE directly, and now how to read the image and display it. We use the previous CJT_LOADFROMFILE first to save the picture to save as files. It is also possible, but there is a simpler method. It is to directly read the files directly. With this tool, everything is simple. The current picture is popular in BMP format and JPG format. We now write and display a function for both images. Function Cjt_BmpLoad (ImgBmp: TImage; SourceFile: String): Boolean; varSource: TFileStream; MyFileSize: integer; beginSource: = TFileStream.Create (SourceFile, fmOpenRead or fmShareDenyNone); trytrySource.Seek (-sizeof (MyFileSize), soFromEnd); Source .Readbuffer (MyFileSize)); // reads out resources Source.seek (-myfilesize, sofroMend); // Located to resource start position IMGBMP.Picture.bitmap.LoadFromstream (source); finallysource.free; end; EXCEPTRESULT: = false; End; End; Result: = true; end; The above is to read the BMP image, below is a function of reading the JPG image, because you want to use the JPG unit, so you have to add a sentence in the program: USES JPEG.
Function Cjt_JpgLoad (JpgImg: Timage; SourceFile: String): Boolean; varSource: TFileStream; MyFileSize: integer; Myjpg: TJpegImage; begintryMyjpg: = TJpegImage.Create; Source: = TFileStream.Create (SourceFile, fmOpenRead or fmShareDenyNone); trySource.Seek (-sizeof (MyFileSize), soFromEnd); Source.ReadBuffer (MyFileSize, sizeof (MyFileSize)); Source.Seek (-MyFileSize, soFromEnd); Myjpg.LoadFromStream (Source); JpgImg.Picture.Bitmap.Assign (Myjpg); FinalLysource.Free; myjpg.free; end; exceptresult: = false; exit; end; result: = true; end; With these two functions, we can make a reader. Below we take BMP pictures as an example: run Delphi, create a new project, put a display image control image1. Write a sentence in the CREATE event in the window: CJT_BMPLOAD (Image1, Application.exename); this is the header file, and then we generate a head.RES resource file in front of the method. Below you can start making our additions.
All code is as follows: unit Unit1; interfaceusesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, ExtDlgs; typeTForm1 = class (TForm) Edit1: TEdit; Button1: TButton; Button2: TButton; OpenPictureDialog1: TOpenPictureDialog ; procedure FormCreate (Sender: TObject); procedure Button1Click (Sender: TObject); procedure Button2Click (Sender: TObject); privateFunction ExtractRes (ResType, ResName, ResNewName: String): boolean; Function Cjt_AddtoFile (SourceFile, TargetFile: string): Boolean ; {Private declarations} public {public declarations} end; varForm1: TForm1; implementation {$ R * .DFM} Function TForm1.ExtractRes (ResType, ResName, ResNewName: String): boolean; varRes: TResourceStream; begintryRes: = TResourceStream.Create (hinstance, Resname, Pchar (ResType)); tryRes.SavetoFile (ResNewName); Result: = true; finallyRes.Free; end; exceptResult: = false; end; end; Function TForm1.Cjt_AddtoFile (SourceFile, TargetFile: string): Boolean; VARTARGET, SOURCE: TFileStream; MyFileSize: Integer; BeginTrySource: = TFileStrea m.Create (SourceFile, fmOpenRead or fmShareExclusive); Target: = TFileStream.Create (TargetFile, fmOpenWrite or fmShareExclusive); tryTarget.Seek (0, soFromEnd); // add resources to the tail Target.CopyFrom (Source, 0); MyFileSize : = Source.size sizeof (MyFileSize); // Calculate the resource size, and write the tail Target.WriteBuffer (MyFileSize); finallyTarget.Free; Source.Free; End; ExceptResult: = false; Exit; end; Result: = True; end; procedure TForm1.FormCreate (Sender: TObject); beginCaption: = '. Bmp2Exe demo author: Chen Jingtao'; Edit1.Text: = ''; OpenPictureDialog1.DefaultExt: = GraphicExtension (TBitmap ); Openpicturedialog1.filter: = graphicfilter (Tbitmap); button1.caption: = 'Select BMP image'; button2.caption: = 'Generate EXE';
procedure TForm1.Button1Click (Sender: TObject); beginif OpenPictureDialog1.Execute thenEdit1.Text: = OpenPictureDialog1.FileName; end; procedure TForm1.Button2Click (Sender: TObject); varHeadTemp: String; beginif Not FileExists (Edit1.Text) thenbeginApplication.MessageBox ('BMP image file does not exist, please reselect!', 'Information', MB_ICONIONFORMATION MB_OK) EXIT; End; HeadTemp: = ChangeFileExt (Edit1.Text, '. EXE'); if extractres ('Exefile', 'Head ', HeadTemp) thenif Cjt_AddtoFile (Edit1.Text, HeadTemp) thenApplication.MessageBox (' EXE file generating success ',' information! ', MB_ICONINFORMATION MB_OK) elsebeginif FileExists (HeadTemp) then DeleteFile (HeadTemp); Application.MessageBox (' EXE File generation failure! ',' Information ', MB_ICONITIONFORMATION MB_OK) end; end; end. How? Very magical :) Put the program in the program, add some functions, you will find that it will not be inferior to the software you want to register. -------------------------------------------------- --------------------- Actual application 3: Use flow to make their own OICQ OICQ is a network real-time communication software of Shenzhen Tencent, a large number of users in China. group. But OICQ must be connected to the Internet to Tencent's servers to use. So we can write one yourself in the local network. OICQ uses the UDP protocol, which is a connectionless protocol, that is, communication can send information without establishing a connection, so the efficiency is relatively high. Delphi's own NMUDP control of the FastNET company is an UDP protocol user data reporter. However, pay attention is that if you use this control to exit the program to turn off the computer because the TNMXXX control has bugs. All NM controls The foundation PowerSocket is used by Threadtimer, which is a hidden window (class TMRWINDOWCLASS). Places for problems: PSock :: Tthreadtimer :: WndProc (var msg: tMESSAG) if msg.MESSAGE = WM_TIMER THEN THEN THEN Hello Hello Msg.Result: = 0ELSEMSG.RESULT: = DefWindowProc (0, ....) END IND When you call DEFWINDOWPROC, the transferred HWND parameter is actually 0, so that DEFWINDOWPROC does not work, return 0 for any input messages, including WM_QueryEndSession, so you can't quit Windows. Due to the abnormal call of DEFWINDOWPROC, in addition to WM_TIMER, other messages are ineffective by defWindowProc processing.
The solution is in psock.pas in tthreadtimer.wndproc result: = defWindowProc (0, MSG, WPARAM, LPARAM); change to: Result: = DefWindowProc (FwindowHandle, MSG, WPARAM, LPARAM); Early Low versions OICQ also have This problem, if you don't close the OICQ, turn off your computer and return it again. Ok, nonsense, let us write our OICQ, this is actually the example of Delphi comes with :) New project, drag a nmudp control to the window at the Fastnet surface version, then put the three edit, The names are Editip, Editport, EditmyTXT, three buttons Btsend, BTCLEAR, BTSAVE, an MemMemoreMorecept, a Savedialog, and a status bar statusbar1. When the user clicks on Btsend, establish a memory flow object, write the text information you want to send into the memory, then the NMUDP sends the stream. When NMUDP has data reception, trigger its DataReceiveD event, we will convert the received stream into character information here, then display it. Note: After all the stream objects are established, remember release (free), in fact, its interpretation function should be Destroy, but if you build a stream loss, use Destroy to produce an exception, and use Free's words will check There is no successful establishment, and if it is released, it is safe to use free. In this program we use NMUDP controls, it has several important properties. Remotehost represents the IP or computer name of the remote computer. LocalPort is a local port, mainly listening to data incoming. RemotePort is a remote port, and the data is sent out through this port when transmitting data. Understanding these already understanding our programs.
All code is as follows: unit Unit1; interfaceusesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, NMUDP; typeTForm1 = class (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); procedure NMUDP1DataReceived (Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer); procedure NMUDP1InvalidHost (var handled: Boolean); procedure NMUDP1DataSend (Sender: TObject); procedure FormCreate (Sender: TObject); procedure BtClearClick (Sender: TObject); procedure BtSaveClick (Sender: TObject); procedure EditMyTxtKeyPress (Sender: TObject ; var Key: Char); private {Private declarations} public {public declarations} end; varForm1: TForm1; implementation {$ R * .DFM} procedure TForm1.BtSendClick (Sender: TObject); varMyStream: TMemoryStream; MySendTxt: String; Iport , ICODE: INTEGER; Beginval (Editport.Text, Iport, iCode); if IC Ode <> 0 thenbeginApplication.MessageBox ('port must be a number, please re-enter!', 'information', MB_ICONIONFORMATION MB_OK); EXIT; END; nmudp1.remotehost: = editip.text; {remote host} nmudp1.localPort: = Iport; {local port} NMUDP1.RemotePort: = Iport; {remote port} MySendTxt:; = EditMyTxt.Text MyStream: = TMemoryStream.Create; {establish flow} tryMyStream.Write (MySendTxt [1], Length (EditMyTxt.Text )); write data {} NMUDP1.SendStream (MyStream); {transmission streams} finallyMyStream.Free; {release flow} end; end; procedure TForm1.NMUDP1DataReceived (Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer ); varMyStream: TMemoryStream; MyReciveTxt: String; beginMyStream: = TMemoryStream.Create; {establish flow} tryNMUDP1.ReadStream (MyStream); {receiving stream} SetLength (MyReciveTxt, NumberBytes);
{Numberbytes for the received bytes} MyStream.read (MyReciveTxt [1], NumberBytes); {read data} Memoreceive.Lines.Add ('received information from host' fromip ':' myRecivetxt); FinallyMystream .Free; {Release stream} end; end; procedure tform1.nmudp1invalidhost (var handled: boolean); beginApplication.MESSAGEBOX ('"The other IP address is incorrect, please re-enter!', 'Information', MB_ICONIONFORMATION MB_OK); END; procedure TForm1.NMUDP1DataSend (Sender: TObject); beginStatusBar1.SimpleText: = 'success information sent!'; end; procedure TForm1.FormCreate (Sender: TObject); beginEditIP.Text: = '127.0.0.1'; EditPort.Text: = '8868'; btsend.caption: = 'Send'; btclear.caption: = 'Clear chat record'; btsave.caption: = 'Save chat record'; memoreceive.scrollbars: = ssboth; memoreceive.clear; editmytxt.text: = 'where input information, and click send.'; StatusBar1.SimplePanel: = true; end; procedure TForm1.BtClearClick (Sender: TObject); beginMemoReceive.Clear; end; procedure TForm1.BtSaveClick (Sender: TObject); beginif SaveDialog1 .Execute then MemoReceive.Lines.SaveToFile (SaveDialog1.FileName); end; procedure TForm1.EditMyTxtKeyPress (Sender: TObject; var Key: Char);. beginif Key = # 13 then BtSend.Click; end; end of the above process The order is very far from OICQ, because OICQ uses Socket5 communication mode. When it goes online, take a friend information from the server and online state. Send timeout will save the information first in the server, wait for the other party to send it later, then send the server backup. You can improve this program based on the concept of the previous studies, such as adding an NMUDP control to manage online status, the sending information first converts to the ASCII code to perform and run and add a header information, and then determine the information after receiving information The information head is correct or not, if the information is decrypted correctly, so it improves security confidentiality. In addition, the UDP protocol has a great advantage that it can be broadcast, that is, it can receive information in a network segment without having to specify the specific IP address. The network segment is generally divided into three categories of A, B, C, 1 ~ 126.xxx.xxx.xxx (Class A): Broadcast address is xxx.255.255.255128 ~ 191.xxx.xxx.xxx (Class B): Broadcast The address is xxx.xxx.255.255192 ~ 254.xxx.xxx.xxx (Class C): The broadcast address is xxx.xxx.xxx.255, such as three computers 192.168.0.1, 192.168.0.0, 192.168.0.18, send information As long as the specified IP address is 192.168.0.255, it can be broadcast.
The following is given a function of converting IP for broadcast IP, getting to improve your own OICQ ^ - ^. Function trun_ip (s: string): string; varing; varing; ss, sss, head: string; n , M: Integer; beginsss: = S; N: = POS ('.', s); S1: = COPY (S, 1, N); M: = Length (S1); Delete (s, 1, m) HEAD: = COPY (S1, 1)); N: = POS ('.', S); S2: = COPY (S, 1, N); M: = Length (S2 ); delete (s, 1, m); N: = POS ('.', s); S3: = COPY (S, 1, N); M: = Length (S3); delete (s, 1, m ); ss: = sss; if strand (head) in [1..126] THEN SS: = S1 '255.255.255'; //1~126.255.255.255 (Class A) IF Strtoint (Head) in [128 ........................................................................ '255'; //192~254.xxx.xxx.255(C network) Result: = ss; end; ----------------------- ------------------------------------------------Fives, Actual application 4: Using the stream to implement network transmission screen images You should have seen many network management procedures, and one of these programs is to monitor the screen of the remote computer. In fact, this is also achieved by flow operation. Here we give an example, this example is divided into two programs, one server, one is the client. After the program is compiled, you can use it directly on a single machine, a local network or an internet. The corresponding comments have been given in the program. Later, let's make specific analysis. Create a new project, drag a ServerSocket control to the window on the Internetboard, which is mainly used to listen to the client to establish connection and communication with the client. Set the Monitor port After calling the method Open or Active: = true. Note: Unlike the previous NMUDP, when the socket begins to listen, it cannot change its port. If you want to change, you must call a close or set Active to false, otherwise an exception will occur. Also, if the port has been opened, you can't use this port. So the program runs no exits if this program cannot be running, otherwise an exception is generated, i.e., the error window pops up. In practical applications, it can be used by judging whether the program has been running, and if you have run, you have already run it to avoid an error. When the client has data incoming, the ServerSocket1ClientRead event will be triggered, and we can process the received data here. In this program, it is mainly to receive the character information sent by the client and perform the corresponding operations according to the prior agreement.
The full code for the following: unit Unit1; {server program} interfaceusesWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, JPEG, ExtCtrls, ScktComp; typeTForm1 = class (TForm) ServerSocket1: TServerSocket; procedure ServerSocket1ClientRead (Sender: TObject; Socket: TCustomWinSocket); procedure FormCreate (Sender: TObject); procedure FormClose (Sender: TObject; var Action: TCloseAction); privateprocedure Cjt_GetScreen (var Mybmp: TBitmap; DrawCur: Boolean); {custom screen capture function, DrawCur represents image or not catch mouse} {Private declarations} public {public declarations} end; varForm1: TForm1; MyStream: TMemorystream; {stream object memory} implementation {$ R * .DFM} procedure TForm1.Cjt_GetScreen (var Mybmp: TBitmap; DrawCur: Boolean); Varcursorx, Cursory: Integer; DC: HDC; Mycan: Tcanvas; R: TRECT; Drawpos: Tpoint; Mycursor: Ticon; HLD: hwnd; thread1d: dword; mp: tpoint; piconinfo: tibitmap. Create; {Establish bmpMap} mycan: = tcanvas.create; {screen interception} DC: = getWindowdc (0); trymycan.handle: = DC; r: = RECT (0, 0, Screen.Width, Screen.Height); Mybmp.width: = r.right; myb mp.height: = r.Bottom; MyBMP.canvas.copyRect (R, Mycan, R); FinallyReleaseDC (0, DC); end; mycan.handle: = 0; mycan.free; if drawcur dam, {Painting mouse diagram like} beginGetCursorPos (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); cursory: = DrawPos.y - round (pIconInfo.yHotspot) Mybmp.canvas.draw (Cursorx, Cursory, Mycursor); {Painting Mouse} deleteObject (PiconInfo.hbmcolor);
{GeticonInfo created two Bitmap objects. You need to manually release these two objects} deleteObject (piconinfo.hbmmask; {Otherwise, after calling him, he will create a bitmap, multiple calls will generate multiple, until resource consumption do} Mycursor.ReleaseHandle; {deallocate an array} MyCursor.Free; {release the mouse pointer} end; end; procedure TForm1.FormCreate (Sender: TObject); beginServerSocket1.Port: = 3000; {port} ServerSocket1.Open; {Socket begins listening} end; procedure TForm1.FormClose (Sender: TObject; var Action: TCloseAction); beginif ServerSocket1.Active then ServerSocket1.Close; {Close Socket} end; procedure TForm1.ServerSocket1ClientRead (Sender: TObject; Socket: TCustomWinSocket); varS, S1: string; MyBmp: TBitmap; Myjpg: TJpegimage; beginS: = Socket.ReceiveText; if S = 'cap' then {grip client sends screen commands} begintryMyStream: = TMemorystream.Create; {creating a memory flow} MyBmp: = Tbitmap.create; myjpg: = tjpegimage.create; cjt_getscreen (mybmp, true); {TRUE represents grasping mouse image} myjpg.assign (mybmp); {Transfer BMP image to JPG format, easy to transfer on the Internet} myjpg .CompressionQuality: = 10; {JPG file compression percentage setting, the larger the number, the more clear the image, the larger the data, the larger the data, and the data is called; {write the JPG image to the stream} myjpg.free; MyStream.Position : = 0; {Note: This sentence must be added} S1: = INTOSTR (MyStream.size); {Flow size} S Ocket.sendtext (S1); {Send stream size} finallymybmp.free; end; end; if s = 'ready' Then {Client already ready to receive image} beginmystream.position: = 0; socket.sendstream (MyStream) {Send the stream to} end; end; end. The above is the server, let's write the client program. Create a new project, add a Socket Control ClientSocket, Image Display Control Image, a Panel, an Edit, Two Button and a Status Control STATUSBAR1. Note: Place Edit1 and Two Button on the panel1. ClientSocket properties are similar to Serversocket, but there is more address property, indicating the server IP address to be connected. Fill in the IP address After "Connection" will establish a connection with the server program, and if you succeed, you can communicate. Click "Screen" to send characters to the server.
Because the program uses the JPEG image unit, you have to add JPEG in the USES. All code is as follows: Unit unit2 {client}; Interfaceuseswindows, Messages, Sysutils, Classes, Graphics, Controls, Forms, Dialogs, Stdctrls, Scktcomp, ExtCtrls, JPEG , ComCtrls; typeTForm1 = class (TForm) ClientSocket1: TClientSocket; Image1: 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); private {Private declarations} public {public declarations} end; varForm1: TForm1; MySize: Longint ; {Memory} {Memory} {$ r * .d FM} procedure tform1.formcreate (sender: TOBJECT); begin {-------- The following is the appearance of the window control -------------} {Note: Put Button1, Button2 and edit1 are placed on the top of Panel1} edit1.text: = '127.0.0.1'; button1.caption: = 'connection host'; button2.caption: = 'catching screen'; Button2.Enabled: = false; panel1.align: = altop; image1.align: = alclient; image1.stretch: = true; statusbar1.align: = albottom; statusbar1.simpanel: = true; {------------------------------------------------------------------------------------------------------------------------------------ -----------------------------} MyStream: = TMEMORYSTREAM.CREATE; {Establishing a memory flow object} mysize: = 0; {initialization } end; procedure tform1.button1click (sender: TOBJECT); beginif not clientsocket1.active kilinclientSocket1.address: = edit1.text; {remote IP address} clientsocket1.port: = 3000;
{Socket port} clientsocket1.open; {establishment connection} end; end; procedure tform1.button2click (sender: TOBJECT); beginclientSocket1.socket.sendtext ('CAP'); {Send instruction notification server capture screen image} Button2 .Enabled: = False; end; procedure TForm1.ClientSocket1Connect (Sender: TObject; Socket: TCustomWinSocket); beginStatusBar1.SimpleText: = 'host' ClientSocket1.Address 'connection is successfully!'; Button2.Enabled: = True; end; procedure TForm1.ClientSocket1Error (Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); beginErrorcode: = 0; {not eject the error window} StatusBar1.SimpleText: = 'Unable to host' ClientSocket1.Address 'to establish a connection!'; end; procedure TForm1.ClientSocket1Disconnect (Sender: TObject; Socket: TCustomWinSocket); beginStatusBar1.SimpleText: = 'host' ClientSocket1.Address 'disconnect!'; Button2.Enabled: = False ; end; procedure TForm1.ClientSocket1Read (Sender: TObject; Socket: TCustomWinSocket); varMyBuffer: array [0..10000] of byte; {set receive buffer} MyReceviceLength: integer; S: string; MyBmp: TBitmap; MyJpg: TJpegimage BeginStatusbar1.SIMPleText: = 'is receiving data. ..... '; if mysize = 0 THEN {mysize is the number of bytes sent by the server, if 0 is expressed as the image reception of the image:}: = Socket.RecEtext; mysize: = start (s); { Set the number of bytes to receive} clientSocket1.socket.sendtext ('ready'); {send instruction notification server starts sending image} endelsebegin {The following is an image data receiving part} myrecelength: = socket.Recelength; {Read Package length} statusbar1.simpletext: = 'is receiving data, data size is:' INTOSTR (MySize); socket.ReceiveBuf (MyBuffer, MyReceLength); {Receive packets and read into buffer} MyStream.write (MyBuffer, MyReceLength; {write data into the stream} if myStream.size> = mysize the {If the flow length is greater than the number of bytes required to receive, the received is completed} beginmystream.position: = 0; mybmp: = tbitmap.create; myjpg : =
TJPEGIMAGE.CREATE; trymyjpg.loadFromstream (MyStream); {read data in the stream to the JPG image object} mybmp.assign (myjpg); {turn JPG to bmp} statusbar1.simpletext: = 'is displaying image'; image1 .Picture.bitmap.assign (MyBMP); {Assignment to Image1 Element} Finally {The following is a clear job} mybmp.free; button2.enabled: = true; {socket.sendtext ('CAP'); Add this The screep can be continuously gripped} myStream.clear; mysize: = 0; end; end; end; end; procedure tform1.formclose (Sender: Tobject; var Action: tclosection); beginmystream.free; {Release memory flow object} IF ClientSocket1.Close; {Close Socket connection} end; end. Principle: Run server start listening, run the client, enter the server IP address to establish a connection, and send a character notification service to the screen. The server calls the custom function CJT_GETSCREEN crashes the screen to BMP, convert BMP to JPG, write the JPG write å memory stream, and send the stream to the client. The client receives the flow after making the opposite operation, convert the stream to jpg and then converted to BMP and display it. Note: Because of the limit of socket, you cannot send too much data at once, you can only send several times. So the server-side grip conversion is converted to the stream into the size of the stream, inform the client, how large this flow is disclosed, the client determines whether the completion has been received according to this number, if the reception is converted and displayed, This program uses the previous homemade OICQ to utilize memory flow objects TMemoryStream. In fact, this streaming object is the most common in programming, it can improve the reading and writing skills of I / O, and if you want to operate several different types of streams at the same time, use it to "intermedians" It is the best. For example, if you put a stream compressed or unpack, build a TMEMORYSTREAM object first, then copy other data, and then perform the corresponding operation. Because it works directly in memory, the efficiency is very high. Sometimes you don't feel any delay. The program has to be improved: Of course, one compression unit can be added, and then compress and send before sending. Note: There is also a skill here, which is directly to compress BMP instead of being converted into JPG. Experiment proves: The above program is about 40-50KB, if it is handled with a LAH compression algorithm, it is more fast so that it is transmitted. If you want to be faster, you can use this method: first grab the first image to send, then start with the second image of the previously different regions. The foreign country has a program called Remote Administrator, which is the use of such a method. The data they tested is as follows: Local network 1 seconds 100-500 horns, on the Internet, in the case of extremely low network speed, transfer 5-10 in a second. Say these questions, just want to explain a truth: thinking, especially writing procedures, especially looking very complicated procedures, don't drill the horns, sometimes you want to change your angle. The program is dead and talent is alive. Of course, these can only rely on experience.