กระแสคืออะไร? พูดง่ายๆ ก็คือ Stream คือข้อมูลการประมวลผลเชิงนามธรรมที่อิงตามเชิงวัตถุ
เครื่องมือ ในสตรีม มีการกำหนดการดำเนินการพื้นฐานบางอย่างสำหรับการประมวลผลข้อมูล เช่น การอ่านข้อมูล การเขียนข้อมูล เป็นต้น
โปรแกรมเมอร์ดำเนินการทั้งหมดบนสตรีมโดยไม่สนใจทิศทางการไหลที่แท้จริงของข้อมูลที่ปลายอีกด้านของสตรีม ไหลไม่
แต่สามารถประมวลผลไฟล์ หน่วยความจำแบบไดนามิก ข้อมูลเครือข่าย และรูปแบบข้อมูลอื่นๆ ถ้าคุณพูดถูก
การทำงานของสตรีมมีความชำนาญมาก การใช้ความสะดวกของสตรีมในโปรแกรมจะช่วยปรับปรุงประสิทธิภาพของการเขียนโปรแกรมได้อย่างมาก
ด้านล่างผู้เขียนใช้สี่ตัวอย่าง: ตัวเข้ารหัสไฟล์ EXE, บัตรอวยพรอิเล็กทรอนิกส์, OICQ แบบโฮมเมด และหน้าจอเครือข่าย
การส่งสัญญาณเพื่อแสดงการใช้ "สตรีม" ในการเขียนโปรแกรม Delphi เทคนิคบางอย่างในตัวอย่างนี้เคยเป็นเทคนิคที่นุ่มนวลมาก
ความลับของไฟล์ไม่ได้เปิดเผยต่อสาธารณะ และตอนนี้ทุกคนสามารถอ้างอิงโค้ดได้โดยตรงโดยไม่เสียค่าใช้จ่าย
"อาคารสูงลอยขึ้นมาจากพื้นดิน" ก่อนที่จะวิเคราะห์ตัวอย่าง เรามาทำความเข้าใจแนวคิดพื้นฐานและแนวคิดเรื่องการไหลกันก่อน
ฟังก์ชั่น หลังจากเข้าใจสิ่งพื้นฐานเหล่านี้แล้วเท่านั้นที่เราจะไปยังขั้นตอนต่อไปได้ โปรดเข้าใจอย่างรอบคอบ
วิธีการพื้นฐานเหล่านี้ แน่นอน หากคุณคุ้นเคยกับสิ่งเหล่านี้แล้ว คุณสามารถข้ามขั้นตอนนี้ได้
1. แนวคิดพื้นฐานและการประกาศฟังก์ชันของสตรีมใน Delphi
ใน Delphi คลาสพื้นฐานของสตรีมออบเจ็กต์ทั้งหมดคือคลาส TStream ซึ่งกำหนดคุณสมบัติทั่วไปของสตรีมทั้งหมด
และวิธีการ
คุณสมบัติที่กำหนดไว้ในคลาส TStream มีการแนะนำดังต่อไปนี้:
1. ขนาด: คุณสมบัตินี้ส่งคืนขนาดของข้อมูลในสตรีมเป็นไบต์
2. ตำแหน่ง: คุณลักษณะนี้ควบคุมตำแหน่งของตัวชี้การเข้าถึงในโฟลว์
มีสี่วิธีเสมือนที่กำหนดไว้ใน Tstream:
1. อ่าน: วิธีการนี้จะอ่านข้อมูลจากสตรีม ต้นแบบฟังก์ชันคือ:
ฟังก์ชั่นอ่าน (var Buffer;Count:Longint):Longint;virtual;abstract;
พารามิเตอร์ Buffer คือบัฟเฟอร์ที่วางไว้เมื่ออ่านข้อมูล Count คือจำนวนไบต์ของข้อมูลที่จะอ่าน
ค่าที่ส่งคืนของเมธอดนี้คือจำนวนไบต์ที่อ่านจริง ซึ่งอาจน้อยกว่าหรือเท่ากับค่าที่ระบุใน Count
2. เขียน: วิธีการนี้จะเขียนข้อมูลลงในสตรีม ต้นแบบฟังก์ชันคือ:
ฟังก์ชั่นเขียน (var Buffer;Count:Longint):Longint;virtual;abstract;
พารามิเตอร์ Buffer คือบัฟเฟอร์ของข้อมูลที่จะเขียนลงในสตรีม Count คือความยาวของข้อมูลเป็นไบต์
ค่าที่ส่งคืนของเมธอดนี้คือจำนวนไบต์ที่เขียนลงในสตรีมจริงๆ
3. ค้นหา: วิธีการนี้ใช้การเคลื่อนไหวของตัวชี้การอ่านในสตรีม ต้นแบบฟังก์ชันคือ:
ฟังก์ชั่นค้นหา (ออฟเซ็ต: Longint; ต้นกำเนิด: Word): Longint; เสมือน; นามธรรม;
พารามิเตอร์ Offset คือจำนวนไบต์ออฟเซ็ต และพารามิเตอร์ Origin ชี้ให้เห็นความหมายที่แท้จริงของ Offset และค่าที่เป็นไปได้
ดังต่อไปนี้:
soFromBeginning:Offset คือตำแหน่งของตัวชี้จากจุดเริ่มต้นของข้อมูลหลังการเคลื่อนไหว ในเวลานี้ออฟเซตต้อง
มากกว่าหรือเท่ากับศูนย์
soFromCurrent:Offset คือตำแหน่งสัมพัทธ์ของตัวชี้หลังการเคลื่อนไหวและตัวชี้ปัจจุบัน
soFromEnd:Offset คือตำแหน่งของตัวชี้จากจุดเริ่มต้นของข้อมูลหลังการเคลื่อนไหว ในเวลานี้ออฟเซตต้อง
น้อยกว่าหรือเท่ากับศูนย์
ค่าที่ส่งคืนของวิธีนี้คือตำแหน่งของตัวชี้หลังการเคลื่อนไหว
4. Setsize: วิธีการนี้จะเปลี่ยนขนาดของข้อมูล ต้นแบบฟังก์ชันคือ:
ฟังก์ชั่น Setsize (NewSize: Longint); เสมือน;
นอกจากนี้ ยังมีการกำหนดวิธีการคงที่หลายวิธีในคลาส TStream:
1. ReadBuffer: ฟังก์ชั่นของวิธีนี้คือการอ่านข้อมูลจากตำแหน่งปัจจุบันในสตรีม ต้นแบบฟังก์ชันคือ:
กระบวนการ ReadBuffer (บัฟเฟอร์ var; นับ: Longint);
คำจำกัดความของพารามิเตอร์เหมือนกับที่อ่านด้านบน หมายเหตุ: เมื่อจำนวนไบต์ข้อมูลที่อ่านแตกต่างจากไบต์ที่ต้องอ่าน
เมื่อตัวเลขแตกต่างกัน EReadError ข้อยกเว้นจะถูกสร้างขึ้น
2. WriteBuffer: หน้าที่ของวิธีนี้คือการเขียนข้อมูลไปยังสตรีมที่ตำแหน่งปัจจุบัน ต้นแบบฟังก์ชันคือ:
ขั้นตอน WriteBuffer(var Buffer;Count:Longint);
คำจำกัดความของพารามิเตอร์เหมือนกับเขียนด้านบน หมายเหตุ: เมื่อจำนวนไบต์ข้อมูลที่เขียนแตกต่างจากไบต์ที่ต้องเขียน
เมื่อตัวเลขต่างกัน ระบบจะสร้างข้อยกเว้น EWriteError
3. CopyFrom: วิธีการนี้ใช้เพื่อคัดลอกสตรีมข้อมูลจากสตรีมอื่น ต้นแบบฟังก์ชันคือ:
ฟังก์ชั่น CopyFrom (ที่มา: TStream; Count: Longint): Longint;
พารามิเตอร์ Source คือสตรีมที่ให้ข้อมูล และ Count คือจำนวนไบต์ข้อมูลที่คัดลอก เมื่อนับมากกว่า 0
CopyFrom คัดลอกจำนวนไบต์ของข้อมูลจากตำแหน่งปัจจุบันของพารามิเตอร์ Source เมื่อ Count เท่ากับ 0
CopyFrom ตั้งค่าคุณสมบัติ Position ของพารามิเตอร์ Source เป็น 0 จากนั้นคัดลอกข้อมูลทั้งหมดของ Source
TStream มีคลาสที่ได้รับมาอื่นๆ ซึ่งคลาสที่ใช้กันมากที่สุดคือคลาส TFileStream การใช้ TFileStream
หากต้องการเข้าถึงไฟล์โดยใช้คลาส คุณต้องสร้างอินสแตนซ์ก่อน คำชี้แจงมีดังนี้:
ตัวสร้างสร้าง (const Filename:string;Mode:Word);
ชื่อไฟล์คือชื่อไฟล์ (รวมถึงพาธ) และพารามิเตอร์ Mode คือวิธีการเปิดไฟล์ ซึ่งรวมถึงวิธีการเปิดไฟล์ด้วย
โหมดเปิดและโหมดแชร์ ค่าและความหมายที่เป็นไปได้มีดังนี้:
โหมดเปิด:
fmCreate: สร้างไฟล์ตามชื่อไฟล์ที่ระบุ หรือเปิดไฟล์หากมีอยู่แล้ว
fmOpenRead: เปิดไฟล์ที่ระบุในโหมดอ่านอย่างเดียว
fmOpenWrite: เปิดไฟล์ที่ระบุในโหมดเขียนอย่างเดียว
fmOpenReadWrite: เปิดไฟล์ที่ระบุเพื่อเขียน
โหมดการแชร์:
fmShareCompat: โหมดแชร์เข้ากันได้กับ FCB
fmShareExclusive: ไม่อนุญาตให้โปรแกรมอื่นเปิดไฟล์ไม่ว่าด้วยวิธีใดก็ตาม
fmShareDenyWrite: ไม่อนุญาตให้โปรแกรมอื่นเปิดไฟล์เพื่อเขียน
fmShareDenyRead: ไม่อนุญาตให้โปรแกรมอื่นเปิดไฟล์ในโหมดอ่าน
fmShareDenyNone: โปรแกรมอื่นสามารถเปิดไฟล์ได้ทุกทาง
TStream ยังมีคลาสที่ได้รับ TMemoryStream ซึ่งใช้บ่อยมากในแอปพลิเคชันจริง
บ่อยมาก. เรียกว่าสตรีมหน่วยความจำ ซึ่งหมายถึงการสร้างวัตถุสตรีมในหน่วยความจำ โดยมีวิธีการและฟังก์ชันพื้นฐานดังนี้
เช่นเดียวกับข้างต้น
ด้วยรากฐานข้างต้น เราก็สามารถเริ่มต้นเส้นทางการเขียนโปรแกรมของเราได้
-------------------------------------------------- -------------------------------------------------- -------------------------------
2. การใช้งานจริงประการที่หนึ่ง: การใช้สตรีมเพื่อสร้างตัวเข้ารหัสไฟล์ EXE, บันเดิล, ไฟล์ที่ขยายในตัว และโปรแกรมการติดตั้ง
ก่อนอื่นเรามาพูดถึงวิธีสร้างตัวเข้ารหัสไฟล์ EXE กันก่อน
หลักการของตัวเข้ารหัสไฟล์ EXE: สร้างไฟล์สองไฟล์ โดยไฟล์หนึ่งใช้เพื่อเพิ่มทรัพยากรให้กับไฟล์ EXE อื่น
ข้างในเรียกว่าโปรแกรมเสริม ไฟล์ EXE อื่นที่เพิ่มเข้ามาเรียกว่าไฟล์ส่วนหัว หน้าที่ของโปรแกรมนี้ก็คือ
อ่านไฟล์ที่เพิ่มเข้าไปในตัวมันเอง
โครงสร้างของไฟล์ EXE ใน Windows ค่อนข้างซับซ้อน บางโปรแกรมก็มี Checksum เช่นกัน เมื่อคุณพบว่ามีการเปลี่ยนแปลง
ต่อมาพวกเขาจะคิดว่าตนเองติดไวรัสและปฏิเสธที่จะดำเนินการ ดังนั้นเราจึงเพิ่มไฟล์ลงในโปรแกรมของเรา
สิ่งนี้จะไม่เปลี่ยนโครงสร้างไฟล์ดั้งเดิม ก่อนอื่นมาเขียนฟังก์ชันเพิ่มกันก่อน ฟังก์ชันของฟังก์ชันนี้คือการเพิ่ม
ไฟล์หนึ่งถูกต่อท้ายไฟล์อื่นเป็นสตรีม ฟังก์ชั่นมีดังนี้:
ฟังก์ชัน Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
เป้าหมายแหล่งที่มา: TFileStream;
MyFileSize:จำนวนเต็ม;
เริ่ม
พยายาม
ที่มา:=TFileStream.Create(SourceFile,fmOpenRead หรือ fmShareExclusive);
เป้าหมาย:=TFileStream.Create(TargetFile,fmOpenWrite หรือ fmShareExclusive);
พยายาม
Target.Seek(0,soFromEnd);//เพิ่มทรัพยากรต่อท้าย
Target.CopyFrom(แหล่งที่มา,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//คำนวณขนาดทรัพยากรและเขียนไว้ที่ส่วนท้ายของกระบวนการเสริม
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
ในที่สุด
เป้าหมายฟรี;
แหล่งที่มาฟรี;
จบ;
ยกเว้น
ผลลัพธ์:=เท็จ;
ออก;
จบ;
ผลลัพธ์:=จริง;
จบ;
ด้วยพื้นฐานข้างต้น เราควรเข้าใจฟังก์ชันนี้ได้อย่างง่ายดาย โดยที่พารามิเตอร์ SourceFile อยู่
ไฟล์ที่จะเพิ่ม พารามิเตอร์ TargetFile คือไฟล์เป้าหมายที่จะเพิ่ม เช่น เพิ่ม a.exe ลงใน
ใน b.exe คุณสามารถ: Cjt_AddtoFile('a.exe',b.exe'); หากการเพิ่มสำเร็จ ให้คืนค่า True เป็นอย่างอื่น
กลับเท็จ
จากฟังก์ชันข้างต้น เราสามารถเขียนฟังก์ชัน read ที่ตรงกันข้ามได้:
ฟังก์ชัน Cjt_LoadFromFile(SourceFile,TargetFile:string):Boolean;
var
ที่มา:TFileStream;
เป้าหมาย: TMemoryStream;
MyFileSize:จำนวนเต็ม;
เริ่ม
พยายาม
เป้าหมาย:=TMemoryStream.Create;
ที่มา:=TFileStream.Create(SourceFile,fmOpenRead หรือ fmShareDenyNone);
พยายาม
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//อ่านขนาดทรัพยากร
Source.Seek(-MyFileSize,soFromEnd);//ค้นหาตำแหน่งของทรัพยากร
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//ลบทรัพยากร
Target.SaveToFile(TargetFile);//บันทึกลงไฟล์
ในที่สุด
เป้าหมายฟรี;
แหล่งที่มาฟรี;
จบ;
ยกเว้น
ผลลัพธ์:=เท็จ;
ออก;
จบ;
ผลลัพธ์:=จริง;
จบ;
พารามิเตอร์ SourceFile คือชื่อไฟล์ของไฟล์ที่เพิ่ม และพารามิเตอร์ TargetFile คือชื่อไฟล์ที่นำออก
ชื่อไฟล์เป้าหมายที่จะบันทึกหลังไฟล์ ตัวอย่างเช่น Cjt_LoadFromFile('b.exe','a.txt'); ใน b.exe
นำไฟล์ออกมาแล้วบันทึกเป็น a.txt หากการแยกข้อมูลสำเร็จ จะคืนค่า True มิฉะนั้นจะส่งคืนค่า False
เปิด Delphi สร้างโครงการใหม่ และใส่ตัวควบคุม Edit1 และปุ่มสองปุ่มบนหน้าต่าง:
ปุ่ม 1 และปุ่ม 2 คุณสมบัติคำอธิบายภาพของปุ่มถูกตั้งค่าเป็น "ตกลง" และ "ยกเลิก" ตามลำดับ มีอยู่
เขียนโค้ดในเหตุการณ์ Click ของ Button1:
var S: สตริง;
เริ่ม
S:=ChangeFileExt(application.ExeName,'.Cjt');
ถ้า Edit1.Text='790617' แล้ว
เริ่ม
Cjt_LoadFromFile(Application.ExeName,S);
{นำไฟล์ออกและบันทึกไว้ในเส้นทางปัจจุบันแล้วตั้งชื่อเป็น "ไฟล์ต้นฉบับ.Cjt"}
Winexec(pchar(S),SW_Show); {เรียกใช้ "ไฟล์ต้นฉบับ.Cjt"}
Application.Terminate;{ออกจากโปรแกรม}
จบ
อื่น
Application.MessageBox('รหัสผ่านไม่ถูกต้อง กรุณากรอกใหม่!', 'รหัสผ่านไม่ถูกต้อง', MB_ICONERROR+MB_OK);
คอมไพล์โปรแกรมนี้และเปลี่ยนชื่อไฟล์ EXE เป็น head.exe สร้างไฟล์ข้อความใหม่ head.rc
เนื้อหาคือ: head exefile head.exe จากนั้นคัดลอกไปยังไดเร็กทอรี BIN ของ Delphi และดำเนินการ
คำสั่ง Dos Brcc32.exe head.rc จะสร้างไฟล์ head.res นี่คือไฟล์ที่เราต้องการ
ไฟล์ทรัพยากร ให้เก็บไว้ก่อน
ไฟล์ส่วนหัวของเราถูกสร้างขึ้นแล้ว มาสร้างโปรแกรม Add-in กัน
สร้างโปรเจ็กต์ใหม่และใส่การควบคุมต่อไปนี้: หนึ่งแก้ไข หนึ่ง Opendialog และสอง Button1
คุณสมบัติคำบรรยายถูกตั้งค่าเป็น "เลือกไฟล์" และ "เข้ารหัส" ตามลำดับ
เพิ่มประโยคในซอร์สโปรแกรม: {$R head.res} และคัดลอกไฟล์ head.res ไปยังไดเร็กทอรีปัจจุบันของโปรแกรม
ด้วยวิธีนี้ head.exe ในตอนนี้จะถูกคอมไพล์พร้อมกับโปรแกรม
เขียนโค้ดในเหตุการณ์ Cilck ของ Button1:
ถ้า OpenDialog1.Execute ให้แก้ไข1.Text:=OpenDialog1.FileName;
เขียนโค้ดในเหตุการณ์ Cilck ของ Button2:
var S:สตริง;
เริ่ม
S:=ExtractFilePath(แก้ไข1.ข้อความ);
ถ้า ExtractRes('exefile','head',S+'head.exe') แล้ว
ถ้า Cjt_AddtoFile(Edit1.Text,S+'head.exe') แล้ว
ถ้า DeleteFile(Edit1.Text) แล้ว
ถ้า RenameFile(S+'head.exe',Edit1.Text) แล้ว
Application.MessageBox('การเข้ารหัสไฟล์สำเร็จ!','ข้อความ',MB_ICONINFORMATION+MB_OK)
อื่น
เริ่ม
ถ้า FileExists(S+'head.exe') แล้วก็ DeleteFile(S+'head.exe');
Application.MessageBox('การเข้ารหัสไฟล์ล้มเหลว!','ข้อความ',MB_ICONINFORMATION+MB_OK)
จบ;
จบ;
ในหมู่พวกเขา ExtractRes เป็นฟังก์ชันที่กำหนดเองซึ่งใช้ในการแยก head.exe ออกจากไฟล์ทรัพยากร
ฟังก์ชัน ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
เริ่ม
พยายาม
Res := TResourceStream.Create (คำแนะนำ, Resname, Pchar (ResType));
พยายาม
Res.SavetoFile(ResNewName);
ผลลัพธ์:=จริง;
ในที่สุด
Res.ฟรี;
จบ;
ยกเว้น
ผลลัพธ์:=เท็จ;
จบ;
จบ;
หมายเหตุ: ฟังก์ชั่นของเราข้างต้นเพียงเพิ่มไฟล์หนึ่งต่อท้ายไฟล์อื่น
ในแอปพลิเคชันจริงสามารถเปลี่ยนเพื่อเพิ่มหลายไฟล์ได้ ตราบใดที่ออฟเซ็ตถูกกำหนดตามขนาดและจำนวนจริง
ที่อยู่ก็จะทำ ตัวอย่างเช่น ตัวรวมไฟล์จะเพิ่มโปรแกรมตั้งแต่สองโปรแกรมขึ้นไปลงในไฟล์ส่วนหัว
ใน. หลักการของโปรแกรมและตัวติดตั้งแบบขยายตัวเองจะเหมือนกัน แต่มีการบีบอัดที่มากกว่า
ตัวอย่างเช่น เราสามารถอ้างอิงหน่วย LAH บีบอัดสตรีมแล้วเพิ่มเข้าไป เพื่อให้ไฟล์มีขนาดเล็กลง
เพียงขยายขนาดก่อนที่จะอ่านออก
นอกจากนี้ ตัวอย่างของตัวเข้ารหัส EXE ในบทความยังคงมีข้อบกพร่องหลายประการ เช่น รหัสผ่านได้รับการแก้ไขเป็น
"790617" หลังจากนำ EXE ออกแล้วรัน ควรรอให้รันเสร็จจึงลบออก ฯลฯ ผู้อ่านสามารถแก้ไขได้ด้วยตนเอง