Delphi中流的應用!

來源:互聯網
上載者:User

 什麼是流?流,簡單來說就是建立在物件導向基礎上的一種抽象的處理資料的工具。在流中,定義了一些處理資料的基本操作,如讀取資料,寫入資料等,程式員是對流進行所有操作的,而不用關心流的另一頭資料的真正流向。流不但可以處理檔案,還可以處理動態記憶體、網路資料等多種資料形式。如果你對流的操作非常熟練,在程式中利用流的方便性,寫起程式會大大提高效率的。  
      下面,筆者通過四個執行個體:EXE檔案加密器、電子賀卡、自製OICQ和網路螢幕傳輸來說明Delphi編程中“流”的利用。這些例子中的一些技巧曾經是很多軟體的秘密而不公開的,現在大家可以無償的直接引用其中的代碼了。  
      “萬丈高樓平地起”,在分析執行個體之前,我們先來瞭解一下流的基本概念和函數,只有在理解了這些基本的東西後我們才能進行下一步。請務必認真領會這些基本方法。當然,如果你對它們已經很熟悉了,則可以跳過這一步。  
    
  一、Delphi中流的基本概念及函式宣告  
  在Delphi中,所有流對象的基類為TStream類,其中定義了所有流的共同屬性和方法。  
  TStream類中定義的屬性介紹如下:  
  1、Size:此屬性以位元組返迴流中資料大小。  
  2、Position:此屬性控制流程中存取指標的位置。  
  Tstream中定義的虛方法有四個:  
  1、Read:此方法實現將資料從流中讀出。函數原形為:  
  Function   Read(var   Buffer;Count:Longint):Longint;virtual;abstract;  
  參數Buffer為資料讀出時放置的緩衝區,Count為需要讀出的資料的位元組數,該方法傳回值為實際讀出的位元組數,它可以小於或等於Count中指定的值。  
  2、Write:此方法實現將資料寫入流中。函數原形為:  
  Function   Write(var   Buffer;Count:Longint):Longint;virtual;abstract;  
  參數Buffer為將要寫入流中的資料的緩衝區,Count為資料的長度位元組數,該方法傳回值為實際寫入流中的位元組數。  
  3、Seek:此方法實現流中讀取指標的移動。函數原形為:  
  Function   Seek(Offset:Longint;Origint:Word):Longint;virtual;abstract;  
  參數Offset為位移位元組數,參數Origint指出Offset的實際意義,其可能的取值如下:  
  soFromBeginning:Offset為移動後指標距離資料開始的位置。此時Offset必須大於或者等於零。  
  soFromCurrent:Offset為移動後指標與當前指標的相對位置。  
  soFromEnd:Offset為移動後指標距離資料結束的位置。此時Offset必須小於或者等於零。該方法傳回值為移動後指標的位置。  
  4、Setsize:此方法實現改變資料的大小。函數原形為:  
  Function   Setsize(NewSize:Longint);virtual;  
  另外,TStream類中還定義了幾個靜態方法:  
  1、ReadBuffer:此方法的作用是從流中當前位置讀取資料。函數原形為:  
  Procedure   ReadBuffer(var   Buffer;Count:Longint);  
  參數的定義跟上面的Read相同。注意:當讀取的資料位元組數與需要讀取的位元組數不相同時,將產生EReadError異常。  
  2、WriteBuffer:此方法的作用是在當前位置向流寫入資料。函數原形為:  
  Procedure   WriteBuffer(var   Buffer;Count:Longint);  
  參數的定義跟上面的Write相同。注意:當寫入的資料位元組數與需要寫入的位元組數不相同時,將產生EWriteError異常。  
  3、CopyFrom:此方法的作用是從其它流中拷貝資料流。函數原形為:  
  Function   CopyFrom(Source:TStream;Count:Longint):Longint;  
  參數Source為提供資料的流,Count為拷貝的資料位元組數。當Count大於0時,CopyFrom從Source參數的當前位置拷貝Count個位元組的資料;當Count等於0時,CopyFrom設定Source參數的Position屬性為0,然後拷貝Source的所有資料;  
  TStream還有其它衍生類別,其中最常用的是TFileStream類。使用TFileStream類來存取檔案,首先要建立一個執行個體。聲明如下:  
  constructor   Create(const   Filename:string;Mode:Word);  
  Filename為檔案名稱(包括路徑),參數Mode為開啟檔案的方式,它包括檔案的開啟模式和共用模式,其可能的取值和意義如下:  
   
  開啟模式:  
  fmCreate   :用指定的檔案名稱建立檔案,如果檔案已經存在則開啟它。  
  fmOpenRead   :以唯讀方式開啟指定檔案  
  fmOpenWrite   :以唯寫方式開啟指定檔案  
  fmOpenReadWrite:以寫寫方式開啟指定檔案  
  共用模式:  
  fmShareCompat   :共用模式與FCBs相容  
  fmShareExclusive:不允許別的程式以任何方式開啟該檔案  
  fmShareDenyWrite:不允許別的程式以寫方式開啟該檔案  
  fmShareDenyRead   :不允許別的程式以讀方式開啟該檔案  
  fmShareDenyNone   :別的程式可以以任何方式開啟該檔案  
   
  TStream還有一個衍生類別TMemoryStream,實際應用中用的次數也非常頻繁。它叫記憶體流,就是說在記憶體中建立一個流對象。它的基本方法和函數跟上面是一樣的。  
  好了,有了上面的基礎後,我們就可以開始我們的編程之行了。  
  -----------------------------------------------------------------------  
  二、實際應用之一:利用流製作EXE檔案加密器、捆綁、自解壓檔案及安裝程式  
   
      我們先來說一下如何製作一個EXE檔案加密器吧。    
      EXE檔案加密器的原理:建立兩個檔案,一個用來添加資源到另外一個EXE檔案裡面,稱為添加程式。另外一個被添加的EXE檔案稱為標頭檔。該程式的功能是把添加到自己裡面的檔案讀出來。Windows下的EXE檔案結構比較複雜,有的程式還有校正和,當發現自己被改變後會認為自己被病毒感染而拒絕執行。所以我們把檔案添加到自己的程式裡面,這樣就不會改變原來的檔案結構了。我們先寫一個添加函數,該函數的功能是把一個檔案當作一個流添加到另外一個檔案的尾部。函數如下:   
Function Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;<br /> var<br /> Target,Source:TFileStream;<br /> MyFileSize:integer;<br /> begin<br /> try<br /> Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareExclusive);<br /> Target:=TFileStream.Create(TargetFile,fmOpenWrite or fmShareExclusive);<br /> try<br /> Target.Seek(0,soFromEnd);//往尾部添加資源<br /> Target.CopyFrom(Source,0);<br /> MyFileSize:=Source.Size+Sizeof(MyFileSize);//計算資源大小,並寫入輔程尾部<br /> Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));<br /> finally<br /> Target.Free;<br /> Source.Free;<br /> end;<br /> except<br /> Result:=False;<br /> Exit;<br /> end;<br /> Result:=True;<br /> end;<br />   

有了上面的基礎,我們應該很容易看得懂這個函數。其中參數SourceFile是要添加的檔案,參數TargetFile是被添加到的目標檔案。比如說把a.exe添加到b.exe裡面可以:Cjt_AddtoFile('a.exe',b.exe');如果添加成功就返回True否則返回假。  
  根據上面的函數我們可以寫出相反的讀出函數:

Function Cjt_LoadFromFile(SourceFile,TargetFile :string):Boolean;<br /> var<br /> Source:TFileStream;<br /> Target:TMemoryStream;<br /> MyFileSize:integer;<br /> begin<br /> try<br /> Target:=TMemoryStream.Create;<br /> Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);<br /> try<br /> Source.Seek(-sizeof(MyFileSize),soFromEnd);<br /> Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//讀出資源大小<br /> Source.Seek(-MyFileSize,soFromEnd);//定位到資源位置<br /> Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//取出資源<br /> Target.SaveToFile(TargetFile);//存放到檔案<br /> finally<br /> Target.Free;<br /> Source.Free;<br /> end;<br /> except<br /> Result:=false;<br /> Exit;<br /> end;<br /> Result:=true;<br /> end;<br />  

其中參數SourceFile是已經添加了檔案的檔案名稱,參數TargetFile是取出檔案後儲存的目標檔案名。比如說Cjt_LoadFromFile('b.exe','a.txt');在b.exe中取出檔案儲存為a.txt。如果取出成功就返回True否則返回假。  
  開啟Delphi,建立一個工程,在視窗上放上一個Edit控制項Edit1和兩個Button:Button1和Button2。Button的Caption屬性分別設定為“確定”和“取消”。在Button1的Click事件中寫代碼:

var S:string;<br /> begin<br /> S:=ChangeFileExt(Application.ExeName,'.Cjt');<br /> if Edit1.Text='790617' then<br /> begin<br /> Cjt_LoadFromFile(Application.ExeName,S);<br /> {取出檔案儲存在當前路徑下並命名"原檔案.Cjt"}<br /> Winexec(pchar(S),SW_Show);{運行"原檔案.Cjt"}<br /> Application.Terminate;{退出程式}<br /> end<br /> else<br /> Application.MessageBox('密碼不對,請重新輸入!','密碼錯誤',MB_ICONERROR+MB_OK);   

 編譯這個程式,並把EXE檔案改名為head.exe。建立一個文字檔head.rc,內容為:   head   exefile   head.exe,然後把它們拷貝到Delphi的BIN目錄下,執行Dos命令Brcc32.exe   head.rc,將產生一個head.res的檔案,這個檔案就是我們要的資源檔,先留著。  
      我們的標頭檔已經建立了,下面我們來建立添加程式。  
      建立一個工程,放上以下控制項:一個Edit,一個Opendialog,兩個Button1的Caption屬性分別設定為"選擇檔案"和"加密"。在來源程式中添加一句:{$R   head.res}並把head.res檔案拷貝到程式目前的目錄下。這樣一來就把剛才的head.exe跟程式一起編譯了。  
      在Button1的Cilck事件裡面寫下代碼:   
   if OpenDialog1.Execute then Edit1.Text:=OpenDialog1.FileName;<br /> 在Button2的Cilck事件裡面寫下代碼:<br /> var S:String;<br /> begin<br /> S:=ExtractFilePath(Edit1.Text);<br /> if ExtractRes('exefile','head',S+'head.exe') then<br /> if Cjt_AddtoFile(Edit1.Text,S+'head.exe') then<br /> if DeleteFile(Edit1.Text) then<br /> if RenameFile(S+'head.exe',Edit1.Text) then<br /> Application.MessageBox('檔案加密成功!','資訊',MB_ICONINFORMATION+MB_OK)<br /> else<br /> begin<br /> if FileExists(S+'head.exe') then DeleteFile(S+'head.exe');<br /> Application.MessageBox('檔案加密失敗!','資訊',MB_ICONINFORMATION+MB_OK)<br /> end;<br /> end;<br />

其中ExtractRes為自訂函數,它的作用是把head.exe從資源檔中取出來。   

Function ExtractRes(ResType, ResName, ResNewName : String):boolean;<br /> var<br /> Res : TResour 

下面這個例子更貼近你的想法:  
  三、實際應用之二:利用流製作可執行電子賀卡  
   
      我們經常看到一些電子賀卡之類的製作軟體,可以讓你自己選擇圖片,然後它會產生一個EXE可執行檔給你。開啟賀卡時就會一邊放音樂一邊顯示出圖片來。現在學了流操作之後,我們也可以做一個了。    
      添加圖片過程我們可以直接用前面的Cjt_AddtoFile,而現在要做的是如何把映像讀出並顯示。我們用前面的Cjt_LoadFromFile先把圖片讀出來儲存為檔案再調入也是可以的,但是還有更簡單的方法,就是直接把檔案流讀出來顯示,有了流這個利器,一切都變的簡單了。  
      現在的圖片比較流行的是BMP格式和JPG格式。我們現在就針對這兩種圖片寫出讀取並顯示函數。

Function Cjt_BmpLoad(ImgBmp:TImage;SourceFile:String):Boolean;<br /> var<br /> Source:TFileStream;<br /> MyFileSize:integer;<br /> begin<br /> Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);<br /> try<br /> try<br /> Source.Seek(-sizeof(MyFileSize),soFromEnd);<br /> Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//讀出資源<br /> Source.Seek(-MyFileSize,soFromEnd);//定位到資源開始位置<br /> ImgBmp.Picture.Bitmap.LoadFromStream(Source);<br /> finally<br /> Source.Free;<br /> end;<br /> except<br /> Result:=False;<br /> Exit;<br /> end;<br /> Result:=True;<br /> end;<br />  

 上面是讀出BMP圖片的,下面的是讀出JPG圖片的函數,因為要用到JPG單元,所以要在程式中添加一句:uses   jpeg。 

Function Cjt_JpgLoad(JpgImg:Timage;SourceFile:String):Boolean;<br /> var<br /> Source:TFileStream;<br /> MyFileSize:integer;<br /> Myjpg: TJpegImage;<br /> begin<br /> try<br /> Myjpg:= TJpegImage.Create;<br /> Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);<br /> try<br /> Source.Seek(-sizeof(MyFileSize),soFromEnd);<br /> Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));<br /> Source.Seek(-MyFileSize,soFromEnd);<br /> Myjpg.LoadFromStream(Source);<br /> JpgImg.Picture.Bitmap.Assign(Myjpg);<br /> finally<br /> Source.Free;<br /> Myjpg.free;<br /> end;<br /> except<br /> Result:=false;<br /> Exit;<br /> end;<br /> Result:=true;<br /> end;<br />  

有了這兩個函數,我們就可以製作讀出程式了。下面我們以BMP圖片為例:  
      運行Delphi,建立一個工程,放上一個顯示影像控制Image1。在視窗的Create事件中寫上一句就可以了:  
  Cjt_BmpLoad(Image1,Application.ExeName);  
      這個就是標頭檔了,然後我們用前面的方法產生一個head.res資源檔。  
  下面就可以開始製作我們的添加程式了。全部代碼如下:   
unit Unit1; </p><p> interface </p><p> uses<br /> Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,<br /> ExtCtrls, StdCtrls, ExtDlgs; </p><p> type<br /> TForm1 = class(TForm)<br /> Edit1: TEdit;<br /> Button1: TButton;<br /> Button2: TButton;<br /> OpenPictureDialog1: TOpenPictureDialog;<br /> procedure FormCreate(Sender: TObject);<br /> procedure Button1Click(Sender: TObject);<br /> procedure Button2Click(Sender: TObject);<br /> private<br /> Function ExtractRes(ResType, ResName, ResNewName : String):boolean;<br /> Function Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;<br /> { Private declarations }<br /> public<br /> { Public declarations }<br /> end; </p><p> var<br /> Form1: TForm1; </p><p> implementation </p><p> {$R *.DFM}<br /> Function TForm1.ExtractRes(ResType, ResName, ResNewName : String):boolean;<br /> var<br /> Res : TResourceStream;<br /> begin<br /> try<br /> Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));<br /> try<br /> Res.SavetoFile(ResNewName);<br /> Result:=true;<br /> finally<br /> Res.Free;<br /> end;<br /> except<br /> Result:=false;<br /> end;<br /> end;<br /> Function TForm1.Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;<br /> var<br /> Target,Source:TFileStream;<br /> MyFileSize:integer;<br /> begin<br /> try<br /> Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareExclusive);<br /> Target:=TFileStream.Create(TargetFile,fmOpenWrite or fmShareExclusive);<br /> try<br /> Target.Seek(0,soFromEnd);//往尾部添加資源<br /> Target.CopyFrom(Source,0);<br /> MyFileSize:=Source.Size+Sizeof(MyFileSize);//計算資源大小,並寫入輔程尾部<br /> Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));<br /> finally<br /> Target.Free;<br /> Source.Free;<br /> end;<br /> except<br /> Result:=False;<br /> Exit;<br /> end;<br /> Result:=True;<br /> end;<br /> procedure TForm1.FormCreate(Sender: TObject);<br /> begin<br /> Caption:='Bmp2Exe示範程式.作者:陳經韜';<br /> Edit1.Text:='';<br /> OpenPictureDialog1.DefaultExt := GraphicExtension(TBitmap);<br /> OpenPictureDialog1.Filter := GraphicFilter(TBitmap); </p><p> Button1.Caption:='選擇BMP圖片';<br /> Button2.Caption:='產生EXE';<br /> end; </p><p> procedure TForm1.Button1Click(Sender: TObject);<br /> begin<br /> if OpenPictureDialog1.Execute then<br /> Edit1.Text:=OpenPictureDialog1.FileName;<br /> end; </p><p> procedure TForm1.Button2Click(Sender: TObject);<br /> var<br /> HeadTemp:String;<br /> begin<br /> if Not FileExists(Edit1.Text) then<br /> begin<br /> Application.MessageBox('BMP圖片檔案不存在,請重新選擇!','資訊',MB_ICONINFORMATION+MB_OK)<br /> Exit;<br /> end;<br /> HeadTemp:=ChangeFileExt(Edit1.Text,'.exe');<br /> if ExtractRes('exefile','head',HeadTemp) then<br /> if Cjt_AddtoFile(Edit1.Text,HeadTemp) then<br /> Application.MessageBox('EXE檔案產生成功!','資訊',MB_ICONINFORMATION+MB_OK)<br /> else<br /> begin<br /> if FileExists(HeadTemp) then DeleteFile(HeadTemp);<br /> Application.MessageBox('EXE檔案產生失敗!','資訊',MB_ICONINFORMATION+MB_OK)<br /> end;<br /> end;<br /> end.<br />  

怎麼樣?很神奇吧:)把程式介面弄的漂亮點,再添加一些功能,你會發現比起那些要註冊的軟體來也不會遜多少吧。  

  

  
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.