الحديث عن تطبيق "flow" في برمجة الدلفي
تشن جينجتاو
ما هو الدفق؟ Stream، ببساطة، هي أداة معالجة بيانات مجردة تعتمد على الكائنات الموجهة. يتم في الدفق تحديد بعض العمليات الأساسية لمعالجة البيانات، مثل قراءة البيانات وكتابة البيانات وغيرها. ويقوم المبرمج بتنفيذ جميع العمليات على الدفق دون الاهتمام باتجاه التدفق الفعلي للبيانات في الطرف الآخر من الدفق. لا يمكن للتدفقات معالجة الملفات فحسب، بل يمكنها أيضًا معالجة الذاكرة الديناميكية وبيانات الشبكة ونماذج البيانات الأخرى. إذا كنت ماهرًا جدًا في عمليات البث واستخدمت سهولة التدفقات في برنامجك، فسيتم تحسين كفاءة كتابة البرامج بشكل كبير.
أدناه، يستخدم المؤلف أربعة أمثلة: أداة تشفير ملفات EXE، وبطاقة التهنئة الإلكترونية، وOICQ ذاتية الصنع، ونقل شاشة الشبكة لتوضيح استخدام "الدفق" في برمجة دلفي. كانت بعض التقنيات المذكورة في هذه الأمثلة بمثابة أسرار للعديد من البرامج ولم تكن متاحة للعامة، والآن يمكن للجميع اقتباس الكود مباشرةً مجانًا.
"المباني الشاهقة ترتفع من الأرض." قبل تحليل الأمثلة، دعونا أولاً نفهم المفاهيم والوظائف الأساسية للتدفق، فقط بعد فهم هذه الأشياء الأساسية يمكننا الانتقال إلى الخطوة التالية. يرجى التأكد من فهم هذه الطرق الأساسية بعناية. وبطبيعة الحال، إذا كنت على دراية بها بالفعل، يمكنك تخطي هذه الخطوة.
1. المفاهيم الأساسية وإعلانات الوظائف للتدفقات في دلفي
في دلفي، الفئة الأساسية لجميع كائنات التدفق هي فئة TStream، التي تحدد الخصائص والأساليب المشتركة لجميع التدفقات.
يتم تقديم الخصائص المحددة في فئة TStream على النحو التالي:
1. الحجم: تُرجع هذه الخاصية حجم البيانات الموجودة في الدفق بالبايت.
2. الموضع: تتحكم هذه السمة في موضع مؤشر الوصول في التدفق.
هناك أربع طرق افتراضية محددة في Tstream:
1. القراءة: تقوم هذه الطريقة بقراءة البيانات من الدفق. النموذج الأولي للوظيفة هو:
قراءة الدالة (var Buffer;Count:Longint):Longint;virtual;abstract;
المعلمة Buffer هي المخزن المؤقت الذي يتم وضعه عند قراءة البيانات، وهو عدد بايتات البيانات المراد قراءتها. القيمة المرجعة لهذه الطريقة هي العدد الفعلي للبايتات المقروءة، والتي يمكن أن تكون أقل من أو تساوي القيمة المحددة عدد.
2. الكتابة: تقوم هذه الطريقة بكتابة البيانات في الدفق. النموذج الأولي للوظيفة هو:
كتابة الوظيفة (var Buffer;Count:Longint):Longint;virtual;abstract;
المعلمة Buffer هي المخزن المؤقت للبيانات المراد كتابتها في الدفق، والعدد هو طول البيانات بالبايت، والقيمة المرجعة لهذه الطريقة هي عدد البايتات المكتوبة بالفعل في الدفق.
3. البحث: تنفذ هذه الطريقة حركة مؤشر القراءة في الدفق. النموذج الأولي للوظيفة هو:
البحث عن الوظيفة (الإزاحة: Longint؛ الأصل: Word): Longint؛ Virtual؛ Abstract؛
المعلمة Offset هي عدد بايتات الإزاحة، وتشير المعلمة Origin إلى المعنى الفعلي للإزاحة، وقيمها المحتملة هي كما يلي:
soFromBeginning:Offset هو موضع المؤشر من بداية البيانات بعد الحركة. في هذا الوقت يجب أن يكون الإزاحة أكبر من أو يساوي الصفر.
soFromCurrent:Offset هو الموضع النسبي للمؤشر بعد الحركة والمؤشر الحالي.
soFromEnd:Offset هو موضع المؤشر من نهاية البيانات بعد الحركة. في هذا الوقت يجب أن يكون الإزاحة أقل من أو يساوي الصفر. القيمة المرجعة لهذه الطريقة هي موضع المؤشر بعد الحركة.
4. حجم المجموعة: تعمل هذه الطريقة على تغيير حجم البيانات. النموذج الأولي للوظيفة هو:
حجم مجموعة الوظائف (NewSize: Longint)؛
بالإضافة إلى ذلك، يتم تعريف العديد من الطرق الثابتة أيضًا في فئة TStream:
1. ReadBuffer: وظيفة هذه الطريقة هي قراءة البيانات من الموضع الحالي في الدفق. النموذج الأولي للوظيفة هو:
PROcedure ReadBuffer(var Buffer;Count:Longint);
تعريف المعلمات هو نفس ما ورد أعلاه. ملاحظة: عندما لا يكون عدد بايتات البيانات المقروءة هو نفس عدد البايتات التي تحتاج إلى القراءة، سيتم إنشاء استثناء EReadError.
2. WriteBuffer: وظيفة هذه الطريقة هي كتابة البيانات إلى الدفق في الموضع الحالي. النموذج الأولي للوظيفة هو:
الإجراء WriteBuffer(var Buffer;Count:Longint);
تعريف المعلمات هو نفس ما ورد أعلاه. ملاحظة: عندما لا يكون عدد بايتات البيانات المكتوبة هو نفس عدد البايتات التي يجب كتابتها، سيتم إنشاء استثناء EWriteError.
3. CopyFrom: تُستخدم هذه الطريقة لنسخ تدفقات البيانات من التدفقات الأخرى. النموذج الأولي للوظيفة هو:
وظيفة CopyFrom(Source:TStream;Count:Longint):Longint;
المعلمة المصدر هي الدفق الذي يوفر البيانات، والعدد هو عدد بايتات البيانات المنسوخة. عندما يكون Count أكبر من 0، يقوم CopyFrom بنسخ عدد وحدات البايت من البيانات من الموضع الحالي للمعلمة المصدر؛ وعندما يكون Count يساوي 0، يقوم CopyFrom بتعيين خاصية الموضع للمعلمة المصدر على 0، ثم ينسخ كافة بيانات المصدر. ;
لدى TStream فئات مشتقة أخرى، وأكثرها استخدامًا هي فئة TFileStream. لاستخدام فئة TFileStream للوصول إلى الملفات، يجب عليك أولاً إنشاء مثيل. البيان هو كما يلي:
إنشاء المنشئ (اسم ملف const: سلسلة؛ الوضع: Word)؛
اسم الملف هو اسم الملف (بما في ذلك المسار)، والمعلمة Mode هي طريقة فتح الملف، والتي تتضمن وضع فتح الملف ووضع المشاركة، والقيم والمعاني المحتملة هي كما يلي:
الوضع المفتوح:
fmCreate: قم بإنشاء ملف باسم الملف المحدد، أو افتح الملف إذا كان موجودًا بالفعل.
fmOpenRead: فتح الملف المحدد في وضع القراءة فقط
fmOpenWrite: فتح الملف المحدد في وضع الكتابة فقط
fmOpenReadWrite: فتح الملف المحدد للكتابة
وضع المشاركة:
fmShareCompat: وضع المشاركة متوافق مع FCBs
fmShareExclusive: لا تسمح للبرامج الأخرى بفتح الملف بأي شكل من الأشكال
fmShareDenyWrite: لا تسمح للبرامج الأخرى بفتح الملف للكتابة
fmShareDenyRead: لا تسمح للبرامج الأخرى بفتح الملف في وضع القراءة
fmShareDenyNone: يمكن للبرامج الأخرى فتح الملف بأي طريقة
يحتوي TStream أيضًا على فئة مشتقة، TMemoryStream، والتي يتم استخدامها بشكل متكرر جدًا في التطبيقات الفعلية. يطلق عليه دفق الذاكرة، وهو ما يعني إنشاء كائن دفق في الذاكرة. أساليبها ووظائفها الأساسية هي نفسها المذكورة أعلاه.
حسنًا، مع وضع الأساس المذكور أعلاه، يمكننا أن نبدأ رحلة البرمجة الخاصة بنا.
-------------------------------------------------- --------------------------
2. التطبيق العملي الأول: استخدام التدفقات لإنشاء برامج تشفير ملفات EXE والحزم وملفات الاستخراج الذاتي وبرامج التثبيت
لنتحدث أولاً عن كيفية إنشاء برنامج تشفير لملفات EXE.
مبدأ تشفير ملفات EXE: قم بإنشاء ملفين، يستخدم أحدهما لإضافة الموارد إلى ملف EXE الآخر، وهو ما يسمى برنامج الوظيفة الإضافية. يسمى ملف EXE الآخر الذي تتم إضافته بملف الرأس. وظيفة هذا البرنامج هي قراءة الملفات المضافة إلى نفسه. تعد بنية ملف EXE في نظام التشغيل Windows معقدة نسبيًا، وتحتوي بعض البرامج أيضًا على مجاميع اختبارية، وعندما تجد أنه تم تغييرها، فإنها ستعتقد أنها مصابة بفيروس وترفض التنفيذ. لذلك نضيف الملف إلى برنامجنا الخاص حتى لا تتغير بنية الملف الأصلي. لنكتب أولاً وظيفة إضافة. وظيفة هذه الوظيفة هي إضافة ملف كدفق إلى نهاية ملف آخر. الوظيفة هي كما يلي:
الوظيفة Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
فار
الهدف، المصدر: TFileStream؛
MyFileSize:integer;
يبدأ
يحاول
المصدر:=TFileStream.Create(SourceFile,fmOpenRead أو fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite أو fmShareExclusive);
يحاول
Target.Seek(0,soFromEnd);// أضف الموارد إلى النهاية
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);// احسب حجم المورد واكتبه في نهاية العملية المساعدة
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
أخيراً
Target.Free;
المصدر.مجاني؛
نهاية؛
يستثني
النتيجة:=خطأ؛
مخرج؛
نهاية؛
النتيجة:=صحيح؛
نهاية؛
مع الأساس المذكور أعلاه، يجب أن نفهم هذه الوظيفة بسهولة. المعلمة SourceFile هي الملف المراد إضافته، والمعلمة TargetFile هي الملف الهدف المراد إضافته. على سبيل المثال، لإضافة a.exe إلى b.exe: Cjt_AddtoFile('a.exe',b.exe'); إذا نجحت الإضافة، فسيتم إرجاع True، وإلا فسيتم إرجاع False.
بناءً على الوظيفة المذكورة أعلاه يمكننا كتابة وظيفة القراءة المعاكسة:
الوظيفة Cjt_LoadFromFile(SourceFile,TargetFile:string):Boolean;
فار
المصدر:TFileStream;
الهدف: TMemoryStream؛
MyFileSize:integer;
يبدأ
يحاول
الهدف:=TMemoryStream.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
يحاول
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));// قراءة حجم المورد
Source.Seek(-MyFileSize,soFromEnd);// حدد موقع المورد
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//إزالة الموارد
Target.SaveToFile(TargetFile);//حفظ في الملف
أخيراً
Target.Free;
المصدر.مجاني؛
نهاية؛
يستثني
النتيجة:=خطأ؛
مخرج؛
نهاية؛
النتيجة:=صحيح؛
نهاية؛
المعلمة SourceFile هي اسم الملف الذي تمت إضافة الملف إليه، والمعلمة TargetFile هي اسم الملف الهدف المحفوظ بعد إخراج الملف. على سبيل المثال، يقوم Cjt_LoadFromFile('b.exe','a.txt'); بإخراج الملف في b.exe وحفظه بتنسيق a.txt. إذا نجح الاستخراج، فسيتم إرجاع True وإلا فسيتم إرجاع False.
افتح دلفي، وأنشئ مشروعًا جديدًا، ثم ضع عنصر التحكم Edit1 وزرين على النافذة: Button1 وButton2. تم تعيين خاصية التسمية التوضيحية للزر على "موافق" و"إلغاء" على التوالي. اكتب الكود في حدث Click لـ Button1:
فار S: سلسلة؛
يبدأ
S:=ChangeFileExt(application.ExeName,'.Cjt');
إذا Edit1.Text='790617' إذن
يبدأ
Cjt_LoadFromFile(Application.ExeName,S);
{أخرج الملف واحفظه في المسار الحالي وأطلق عليه اسم "Original file.Cjt"}
Winexec(pchar(S),SW_Show);{تشغيل "الملف الأصلي.Cjt"}
التطبيق.إنهاء؛{الخروج من البرنامج}
نهاية
آخر
Application.MessageBox('كلمة المرور غير صحيحة، يرجى إعادة الإدخال!'، 'كلمة المرور غير صحيحة'، MB_ICONERROR+MB_OK);
قم بتجميع هذا البرنامج وإعادة تسمية ملف EXE إلى head.exe. قم بإنشاء ملف نصي جديد head.rc بالمحتوى: head exefile head.exe، ثم انسخه إلى دليل BIN الخاص بدلفي، وقم بتنفيذ أمر Dos Brcc32.exe head.rc، وسيتم إنشاء ملف head.res الملف هو الاحتفاظ بملفات الموارد التي نريدها أولاً.
تم إنشاء ملف الرأس الخاص بنا، فلنقم بإنشاء برنامج الوظيفة الإضافية.
أنشئ مشروعًا جديدًا وضع عناصر التحكم التالية: تم تعيين تحرير وOpendialog وخصائص التسمية التوضيحية للزرين 1 على "تحديد ملف" و"تشفير" على التوالي. أضف جملة في البرنامج المصدر: {$R head.res} وانسخ ملف head.res إلى الدليل الحالي للبرنامج. بهذه الطريقة، يتم الآن تجميع ملف head.exe مع البرنامج.
اكتب الكود في حدث Cilck الخاص بـ Button1:
إذا كان OpenDialog1.Execute ثم Edit1.Text:=OpenDialog1.FileName؛
اكتب الكود في حدث Cilck الخاص بـ Button2:
فار S: سلسلة؛
يبدأ
S:=ExtractFilePath(Edit1.Text);
إذا كان ExtractRes('exefile','head',S+'head.exe') إذن
إذا Cjt_AddtoFile(Edit1.Text,S+'head.exe') ثم
إذا حذف ملف (Edit1.Text) ثم
إذا RenameFile(S+'head.exe',Edit1.Text) ثم
Application.MessageBox('تم تشفير الملف بنجاح!','رسالة',MB_ICONINFORMATION+MB_OK)
آخر
يبدأ
إذا كان FileExists(S+'head.exe') ثم حذف File(S+'head.exe');
Application.MessageBox('فشل تشفير الملف!','رسالة',MB_ICONINFORMATION+MB_OK)
نهاية؛
نهاية؛
من بينها، ExtractRes هي وظيفة مخصصة تُستخدم لاستخراج head.exe من ملف المورد.
الدالة ExtractRes(ResType, ResName, ResNewName: String): boolean;
فار
الدقة: تريسورسستريم؛
يبدأ
يحاول
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
يحاول
Res.SavetoFile(ResNewName);
النتيجة:=صحيح؛
أخيراً
الدقة.مجاني؛
نهاية؛
يستثني
النتيجة:=خطأ؛
نهاية؛
نهاية؛
ملحوظة: تقوم وظيفتنا أعلاه ببساطة بإلحاق ملف واحد بنهاية ملف آخر. في التطبيقات الفعلية، يمكن تغييره لإضافة ملفات متعددة، طالما تم تحديد عنوان الإزاحة وفقًا للحجم والرقم الفعليين. على سبيل المثال، يقوم مجمّع الملفات بإضافة برنامجين أو أكثر إلى ملف الرأس. مبادئ برامج الاستخراج الذاتي والمثبتات هي نفسها، ولكن مع المزيد من الضغط. على سبيل المثال، يمكننا الرجوع إلى وحدة LAH، وضغط الدفق ثم إضافتها، بحيث يصبح الملف أصغر. فقط قم بفك الضغط عنها قبل قراءتها. بالإضافة إلى ذلك، فإن مثال تشفير EXE الموجود في المقالة لا يزال به العديد من العيوب، على سبيل المثال، كلمة المرور ثابتة على "790617"، وبعد إخراج EXE وتشغيله، يجب حذفه بعد انتهاء تشغيله، إلخ. ويمكن للقراء تعديله بأنفسهم.
-------------------------------------------------- -------------------
3. التطبيق العملي 2: استخدام التدفقات لإنشاء بطاقات تهنئة إلكترونية قابلة للتنفيذ
غالبًا ما نرى بعض برامج إنتاج بطاقات التهنئة الإلكترونية التي تسمح لك بتحديد الصور بنفسك، وبعد ذلك سيقوم بإنشاء ملف EXE قابل للتنفيذ لك. عند فتح بطاقة التهنئة، سيتم عرض الصورة أثناء تشغيل الموسيقى. الآن بعد أن تعلمنا عمليات الدفق، يمكننا أيضًا إنشاء واحدة.
في عملية إضافة الصور، يمكننا استخدام Cjt_AddtoFile السابق مباشرة، وما يتعين علينا القيام به الآن هو كيفية قراءة الصور وعرضها. يمكننا استخدام Cjt_LoadFromFile السابق لقراءة الصورة أولاً وحفظها كملف ثم تحميلها. ومع ذلك، هناك طريقة أبسط وهي قراءة دفق الملف مباشرة وعرضه باستخدام أداة الدفق القوية ، يصبح كل شيء بسيطًا.
الصور الأكثر شيوعًا في الوقت الحاضر هي تنسيق BMP وتنسيق JPG. سنكتب الآن وظائف القراءة والعرض لهذين النوعين من الصور.
الوظيفة Cjt_BmpLoad(ImgBmp:TIImage;SourceFile:String):Boolean;
فار
المصدر:TFileStream;
MyFileSize:integer;
يبدأ
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
يحاول
يحاول
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));// قراءة الموارد
Source.Seek(-MyFileSize,soFromEnd);// حدد موقع البداية للمورد
ImgBmp.Picture.Bitmap.LoadFromStream(Source);
أخيراً
المصدر.مجاني؛
نهاية؛
يستثني
النتيجة:=خطأ؛
مخرج؛
نهاية؛
النتيجة:=صحيح؛
نهاية؛
ما ورد أعلاه هو وظيفة لقراءة صور BMP، وما يلي هو وظيفة لقراءة صور JPG. نظرًا لاستخدام وحدة JPG، يجب إضافة جملة: يستخدم jpeg إلى البرنامج.
الوظيفة Cjt_JpgLoad(JpgImg:Timage;SourceFile:String):Boolean;
فار
المصدر:TFileStream;
MyFileSize:integer;
Myjpg: TJpegImage;
يبدأ
يحاول
Myjpg:= TJpegImage.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
يحاول
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));
Source.Seek(-MyFileSize,soFromEnd);
Myjpg.LoadFromStream(Source);
JpgImg.Picture.Bitmap.Assign(Myjpg);
أخيراً
المصدر.مجاني؛
Myjpg.free;
نهاية؛
يستثني
النتيجة:=خطأ؛
مخرج؛
نهاية؛
النتيجة:=صحيح؛
نهاية؛
باستخدام هاتين الوظيفتين، يمكننا إنشاء برنامج قراءة. لنأخذ صور BMP كمثال:
قم بتشغيل دلفي، وأنشئ مشروعًا جديدًا، ثم ضع عنصر التحكم في عرض الصور Image1. ما عليك سوى كتابة الجملة التالية في حدث الإنشاء بالنافذة:
Cjt_BmpLoad(Image1,Application.ExeName);
هذا هو ملف الرأس، ثم نستخدم الطريقة السابقة لإنشاء ملف مورد head.res.
الآن يمكننا البدء في إنشاء برنامجنا الإضافي. الكود بأكمله هو كما يلي:
الوحدة 1؛
واجهة
الاستخدامات
Windows، الرسائل، SysUtils، الفئات، الرسومات، عناصر التحكم، النماذج، مربعات الحوار،
ExtCtrls، StdCtrls، ExtDlgs؛
يكتب
TForm1 = الفئة (TForm)
تحرير 1: تحرير؛
Button1: TButton؛
Button2: TButton؛
OpenPictureDialog1: TOpenPictureDialog;
الإجراء FormCreate(Sender: TObject);
الإجراء Button1Click(Sender: TObject);
الإجراء Button2Click(Sender: TObject);
خاص
الدالة ExtractRes(ResType, ResName, ResNewName: String): boolean;
الوظيفة Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
{تصريحات خاصة}
عام
{التصريحات العامة}
نهاية؛
فار
Form1: TForm1؛
تطبيق
{$R *.DFM}
الوظيفة TForm1.ExtractRes(ResType, ResName, ResNewName : String): boolean;
فار
الدقة: تريسورسستريم؛
يبدأ
يحاول
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
يحاول
Res.SavetoFile(ResNewName);
النتيجة:=صحيح؛
أخيراً
الدقة.مجاني؛
نهاية؛
يستثني
النتيجة:=خطأ؛
نهاية؛
نهاية؛
الدالة TForm1.Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
فار
الهدف، المصدر: TFileStream؛
MyFileSize:integer;
يبدأ
يحاول
المصدر:=TFileStream.Create(SourceFile,fmOpenRead أو fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite أو fmShareExclusive);
يحاول
Target.Seek(0,soFromEnd);// أضف الموارد إلى النهاية
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);// احسب حجم المورد واكتبه في نهاية العملية المساعدة
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
أخيراً
Target.Free;
المصدر.مجاني؛
نهاية؛
يستثني
النتيجة:=خطأ؛
مخرج؛
نهاية؛
النتيجة:=صحيح؛
نهاية؛
الإجراء TForm1.FormCreate(Sender: TObject);
يبدأ
Caption:='برنامج العرض التوضيحي Bmp2Exe المؤلف: Chen Jingtao';
تحرير1.النص:='';
OpenPictureDialog1.DefaultExt := GraphicExtension(TBitmap);
OpenPictureDialog1.Filter := GraphicFilter(TBitmap);
Button1.Caption:='اختر صورة BMP';
Button2.Caption:='إنشاء EXE';
نهاية؛
الإجراء TForm1.Button1Click(Sender: TObject);
يبدأ
إذا OpenPictureDialog1.Execute بعد ذلك
Edit1.Text:=OpenPictureDialog1.FileName;
نهاية؛
الإجراء TForm1.Button2Click(Sender: TObject);
فار
HeadTemp:String;
يبدأ
إذا لم يكن FileExists(Edit1.Text) ثم
يبدأ
Application.MessageBox('ملف صورة BMP غير موجود، يرجى التحديد مرة أخرى!','Message',MB_ICONINFORMATION+MB_OK)
مخرج؛
نهاية؛
HeadTemp:=ChangeFileExt(Edit1.Text,'.exe');
إذا كان ExtractRes('exefile','head',HeadTemp) إذن
إذا Cjt_AddtoFile(Edit1.Text,HeadTemp) ثم
Application.MessageBox('تم إنشاء ملف EXE بنجاح!','رسالة',MB_ICONINFORMATION+MB_OK)
آخر
يبدأ
إذا FileExists(HeadTemp) ثم حذف File(HeadTemp);
Application.MessageBox('فشل إنشاء ملف EXE!','رسالة',MB_ICONINFORMATION+MB_OK)
نهاية؛
نهاية؛
نهاية.
ماذا عن ذلك؟ إنه لأمر مدهش :) اجعل واجهة البرنامج أكثر جمالا وأضف بعض الوظائف، وستجد أنها ليست أقل شأنا بكثير من تلك البرامج التي تتطلب التسجيل.
-------------------------------------------------- --------------------------
التطبيق العملي الثالث: استخدم التدفقات لإنشاء OICQ الخاص بك
OICQ هو برنامج اتصال فوري عبر الإنترنت تم تطويره بواسطة شركة Shenzhen Tencent ولديه قاعدة مستخدمين كبيرة في الصين. ومع ذلك، يجب أن يكون OICQ متصلاً بالإنترنت وتسجيل الدخول إلى خادم Tencent قبل استخدامه. حتى نتمكن من كتابة واحد بأنفسنا واستخدامه في الشبكة المحلية.
يستخدم OICQ بروتوكول UDP، وهو بروتوكول بدون اتصال، أي أنه يمكن لأطراف الاتصال إرسال المعلومات دون إنشاء اتصال، وبالتالي فإن الكفاءة عالية نسبيًا. إن تحكم NMUDP الخاص بـ FastNET الذي يأتي مع دلفي نفسها هو تحكم في مخطط بيانات المستخدم لبروتوكول UDP. ومع ذلك، تجدر الإشارة إلى أنه في حالة استخدام عنصر التحكم هذا، يجب عليك الخروج من البرنامج قبل إيقاف تشغيل الكمبيوتر، لأن عنصر التحكم TNMXXX به خطأ. يستخدم ThreadTimer الذي يستخدمه PowerSocket، وهو أساس جميع عناصر التحكم نانومتر، نافذة مخفية (فئة TmrWindowClass) للتعامل مع العيوب.
ما الخطأ الذي حدث:
بسوك::TThreadTimer::WndProc(var msg:TMessage)
إذا msg.message=WM_TIMER ثم
يتولى الأمر بنفسه
نتيجة الرسالة:=0
آخر
msg.result:=DefWindowProc(0,....)
نهاية
تكمن المشكلة في أنه عند استدعاء DefWindowProc، تكون معلمة HWND المرسلة في الواقع ثابتة 0، لذلك في الواقع لا يمكن لـ DefWindowProc أن يعمل. يؤدي استدعاء أي رسالة إدخال إلى إرجاع 0، بما في ذلك WM_QUERYENDsession، لذا لا يمكن للنوافذ الخروج. بسبب الاستدعاء غير الطبيعي لـ DefWindowProc، في الواقع، باستثناء WM_TIMER، فإن الرسائل الأخرى التي تتم معالجتها بواسطة DefWindowProc غير صالحة.
الحل موجود في PSock.pas
ضمن TThreadTimer.Wndproc
النتيجة := DefWindowProc( 0, Msg, WPARAM, LPARAM );
التغيير إلى:
النتيجة := DefWindowProc( FWindowHandle, Msg, WPARAM, LPARAM );
واجهت الإصدارات المبكرة ذات المستوى المنخفض من OICQ هذه المشكلة أيضًا إذا لم يتم إيقاف تشغيل OICQ، فستومض الشاشة للحظة ثم تعود عند إيقاف تشغيل الكمبيوتر.
حسنًا، دون مزيد من اللغط، دعنا نكتب OICQ الخاص بنا، وهذا في الواقع مثال يأتي مع دلفي :).
أنشئ مشروعًا جديدًا، واسحب عنصر تحكم NMUDP من لوحة FASTNET إلى النافذة، ثم ضع ثلاثة تعديلات تسمى Editip وEditPort وEditMyTxt وثلاثة أزرار BtSend وBtClear وBtSave وMEMOMemoReceive وSaveDialog وشريط الحالة. عندما ينقر المستخدم على BtSend، يتم إنشاء كائن دفق ذاكرة، وتتم كتابة المعلومات النصية المراد إرسالها في دفق الذاكرة، ثم يرسل NMUDP الدفق للخارج. عندما يتلقى NMUDP البيانات، يتم تشغيل حدث DataReceived الخاص به. وهنا نقوم بتحويل الدفق المستلم إلى معلومات شخصية ثم نعرضه.
ملاحظة: تذكر تحرير جميع كائنات الدفق (مجانًا) بعد إنشائها واستخدامها. في الواقع، يجب أن يكون مدمرها هو Destroy. ومع ذلك، إذا فشل إنشاء الدفق، فسيؤدي استخدام Destroy إلى إنشاء استثناء سيتحقق أولاً مما إذا لم يتم إنشاء الدفق بنجاح، فلن يتم إصداره إلا إذا تم إنشاؤه، لذلك يكون الاستخدام المجاني أكثر أمانًا.
نستخدم في هذا البرنامج التحكم NMUDP الذي له عدة خصائص مهمة. يمثل RemoteHost عنوان IP أو اسم الكمبيوتر للكمبيوتر البعيد، وLocalPort هو المنفذ المحلي، الذي يراقب بشكل أساسي ما إذا كانت هناك بيانات واردة. يعد RemotePort منفذًا بعيدًا، ويتم إرسال البيانات من خلال هذا المنفذ عند إرسال البيانات. إن فهم هذه الأشياء يمكن أن يفهم برنامجنا بالفعل.
الكود بأكمله هو كما يلي:
الوحدة 1؛
واجهة
الاستخدامات
Windows، الرسائل، SysUtils، الفئات، الرسومات، عناصر التحكم، النماذج، مربعات الحوار، StdCtrls، ComCtrls، NMUDP؛
يكتب
TForm1 = الفئة (TForm)
NMUDP1: TNMUDP؛
تحرير: تيديت؛
منفذ التحرير: TEdit؛
EditMyTxt: TEdit;
MemoReceive: TMemo؛
بتسيند: تيبوتون؛
بت كلير: تي بوتون؛
بتساف: تيبوتون؛
StatusBar1: TStatusBar;
SaveDialog1: TSaveDialog;
الإجراء BtSendClick(Sender: TObject);
الإجراء NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer;
فرومIP: سلسلة؛
الإجراء NMUDP1InvalidHost(var Handled: Boolean);
الإجراء NMUDP1DataSend(Sender: TObject);
الإجراء FormCreate(Sender: TObject);
الإجراء BtClearClick(Sender: TObject);
الإجراء BtSaveClick(Sender: TObject);
الإجراء EditMyTxtKeyPress(Sender: TObject; var Key: Char);
خاص
{تصريحات خاصة}
عام
{التصريحات العامة}
نهاية؛
فار
Form1: TForm1؛
تطبيق
{$R *.DFM}
الإجراء TForm1.BtSendClick(Sender: TObject);
فار
MyStream: TMemoryStream؛
MySendTxt: سلسلة؛
إيبورت، رمز: عدد صحيح؛
يبدأ
Val(EditPort.Text,Iport,icode);
إذا رمز<>0 ثم
يبدأ
Application.MessageBox('يجب أن يكون المنفذ رقمًا، يرجى إعادة الإدخال!','Message',MB_ICONINFORMATION+MB_OK);
مخرج؛
نهاية؛
NMUDP1.RemoteHost := EditIP.Text {remote host};
NMUDP1.LocalPort:=Iport {local port}
NMUDP1.RemotePort := Iport {remote port}
MySendTxt := EditMyTxt.Text;
MyStream := TMemoryStream.Create {إنشاء دفق}؛
يحاول
MyStream.Write(MySendTxt[1], Length(EditMyTxt.Text));{اكتب البيانات}
NMUDP1.SendStream(MyStream);
أخيراً
MyStream.Free؛
نهاية؛
نهاية؛
الإجراء TForm1.NMUDP1DataReceived(Sender: TComponent;
NumberBytes: عدد صحيح؛ FromIP: سلسلة؛
فار
MyStream: TMemoryStream؛
MyReciveTxt: سلسلة؛
يبدأ
MyStream := TMemoryStream.Create {إنشاء دفق}؛
يحاول
NMUDP1.ReadStream(MyStream);{تلقي الدفق}
SetLength(MyReciveTxt,NumberBytes);{NumberBytes هو عدد البايتات المستلمة}
MyStream.Read(MyReciveTxt[1],NumberBytes);{قراءة البيانات}
MemoReceive.Lines.Add('تم تلقي المعلومات من المضيف '+FromIP+':'+MyReciveTxt);
أخيراً
MyStream.Free؛
نهاية؛
نهاية؛
الإجراء TForm1.NMUDP1InvalidHost(var Handled: Boolean);
يبدأ
Application.MessageBox('عنوان IP الخاص بالطرف الآخر غير صحيح، يرجى إعادة الإدخال!','Message',MB_ICONINFORMATION+MB_OK);
نهاية؛
الإجراء TForm1.NMUDP1DataSend(Sender: TObject);
يبدأ
StatusBar1.SimpleText:='تم إرسال الرسالة بنجاح!';
نهاية؛
الإجراء TForm1.FormCreate(Sender: TObject);
يبدأ
EditIP.Text:='127.0.0.1';
EditPort.Text:='8868';
BtSend.Caption:='إرسال';
BtClear.Caption:='مسح سجل الدردشة';
BtSave.Caption:='حفظ سجل الدردشة';
MemoReceive.ScrollBars:=ssBoth;
MemoReceive.Clear;
EditMyTxt.Text:='أدخل المعلومات هنا وانقر فوق إرسال.';
StatusBar1.SimplePanel:=true;
نهاية؛
الإجراء TForm1.BtClearClick(Sender: TObject);
يبدأ
MemoReceive.Clear;
نهاية؛
الإجراء TForm1.BtSaveClick(Sender: TObject);
يبدأ
إذا SaveDialog1.Execute ثم MemoReceive.Lines.SaveToFile(SaveDialog1.FileName);
نهاية؛
الإجراء TForm1.EditMyTxtKeyPress(Sender: TObject; var Key: Char);
يبدأ
إذا كان المفتاح=#13 ثم BtSend.Click;
نهاية؛
نهاية.
من المؤكد أن البرنامج المذكور أعلاه يتخلف كثيرًا عن OICQ، لأن OICQ يستخدم طريقة الاتصال المقبس 5. عندما يتصل بالإنترنت، يقوم أولاً باسترداد معلومات الصديق وحالة الاتصال من الخادم. عند انتهاء مهلة الإرسال، سيحفظ المعلومات على الخادم أولاً، وينتظر حتى يتصل الطرف الآخر بالإنترنت في المرة القادمة، ثم يرسلها ثم يحذفها. النسخ الاحتياطي للخادم. يمكنك تحسين هذا البرنامج بناءً على المفاهيم التي تعلمتها مسبقًا. على سبيل المثال، قم بإضافة عنصر تحكم NMUDP لإدارة حالة الاتصال بالإنترنت. يتم أولاً تحويل المعلومات المرسلة إلى رمز ASCII لعملية AND وإضافة معلومات الرأس بعد ذلك تلقي المعلومات سواء كانت رأسية المعلومات صحيحة أم لا، سيتم فك تشفير المعلومات وعرضها فقط إذا كانت صحيحة، وبالتالي تحسين الأمان والسرية.
بالإضافة إلى ذلك، هناك ميزة أخرى رائعة لبروتوكول UDP وهي أنه يمكنه البث، مما يعني أن أي شخص في نفس قطاع الشبكة يمكنه تلقي المعلومات دون تحديد عنوان IP محدد. يتم تقسيم قطاعات الشبكة عمومًا إلى ثلاث فئات: A وB وC.
1~126.XXX.XXX.XXX (شبكة من الفئة A): عنوان البث هو XXX.255.255.255
128~191.XXX.XXX.XXX (شبكة من الفئة B): عنوان البث هو XXX.XXX.255.255
192~254.XXX.XXX.XXX (شبكة الفئة C): عنوان البث هو XXX.XXX.XXX.255
على سبيل المثال، إذا كانت ثلاثة أجهزة كمبيوتر هي 192.168.0.1 و192.168.0.10 و192.168.0.18، فعند إرسال المعلومات، تحتاج فقط إلى تحديد عنوان IP 192.168.0.255 لتحقيق البث. يوجد أدناه وظيفة تقوم بتحويل IP إلى بث IP، استخدمها لتحسين OICQ^-^.
الدالة Trun_ip(S:string):string;
فار s1,s2,s3,ss,sss,Head: string;
ن،م:عدد صحيح؛
يبدأ
سسس:=س;
n:=pos('.',s);
s1:=نسخة(s,1,n);
م:=الطول(s1);
حذف (ق،1،م)؛
الرأس:=نسخة(s1,1,(الطول(s1)-1));
n:=pos('.',s);
s2:=نسخة(s,1,n);
م:=الطول(s2);
حذف (ق،1،م)؛
n:=pos('.',s);
s3:=copy(s,1,n);
م:=الطول(s3);
حذف (ق،1،م)؛
سس:=سسس;
إذا strtoint(Head) في [1..126] ثم ss:=s1+'255.255.255'; //1~126.255.255.255 (شبكة من الفئة أ)
إذا strtoint(Head) في [128..191] ثم ss:=s1+s2+'255.255';//128~191.XXX.255.255 (شبكة من الفئة B)
إذا strtoint(Head) في [192..254] ثم ss:=s1+s2+s3+'255'; //192~254.XXX.XXX.255 (شبكة الفئة)
النتيجة:=SS;
نهاية؛
-------------------------------------------------- --------------------------
5. التطبيق العملي 4: استخدام التدفقات لنقل صور الشاشة عبر الشبكة
يجب أن تكون قد شاهدت العديد من برامج إدارة الشبكة. إحدى وظائف هذه البرامج هي مراقبة شاشة الكمبيوتر البعيد. في الواقع، يتم تحقيق ذلك أيضًا باستخدام عمليات الدفق. وفيما يلي نعطي مثالاً، وينقسم هذا المثال إلى برنامجين، أحدهما هو الخادم والآخر هو العميل. بعد تجميع البرنامج، يمكن استخدامه مباشرة على جهاز واحد أو شبكة محلية أو الإنترنت. وقد أعطيت التعليقات المقابلة في البرنامج. سنقوم بإجراء تحليل مفصل في وقت لاحق.
أنشئ مشروعًا جديدًا واسحب عنصر تحكم ServerSocket إلى النافذة الموجودة على لوحة الإنترنت. يُستخدم عنصر التحكم هذا بشكل أساسي لمراقبة العميل وإنشاء اتصالات واتصالات مع العميل. بعد ضبط منفذ الاستماع، اتصل بالطريقة Open أو Active:=True لبدء العمل. ملاحظة: على عكس NMUDP السابق، بمجرد أن يبدأ المقبس في الاستماع، لا يمكن تغيير المنفذ الخاص به. إذا كنت تريد تغييره، فيجب عليك أولاً استدعاء Close أو تعيين Active على False، وإلا سيحدث استثناء. بالإضافة إلى ذلك، إذا كان المنفذ مفتوحًا بالفعل، فلا يمكن استخدام هذا المنفذ بعد الآن. لذلك، لا يمكنك تشغيل البرنامج مرة أخرى قبل الخروج، وإلا سيحدث استثناء، أي ستظهر نافذة خطأ. وفي التطبيقات العملية يمكن تجنب الأخطاء من خلال تحديد ما إذا كان البرنامج قد تم تشغيله والخروج منه إذا كان قيد التشغيل بالفعل.
عندما يتم تمرير البيانات من العميل، سيتم تشغيل حدث ServerSocket1ClientRead، ويمكننا معالجة البيانات المستلمة هنا. في هذا البرنامج، يتلقى بشكل أساسي معلومات الشخصية التي يرسلها العميل وينفذ العمليات المقابلة وفقًا للاتفاق المسبق.
رمز البرنامج بأكمله هو كما يلي:
الوحدة 1؛ {برنامج الخادم}
واجهة
الاستخدامات
Windows، الرسائل، SysUtils، الفئات، الرسومات، عناصر التحكم، النماذج، مربعات الحوار، JPEG، ExtCtrls، ScktComp؛
يكتب
TForm1 = الفئة (TForm)
ServerSocket1: TServerSocket؛
الإجراء ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);
الإجراء FormCreate(Sender: TObject);
الإجراء FormClose(Sender: TObject; var Action: TCloseAction);
خاص
الإجراء Cjt_GetScreen(var Mybmp: TBitmap; DrawCur: Boolean);
{وظيفة التقاط الشاشة المخصصة، يشير DrawCur إلى ما إذا كان سيتم التقاط صورة الماوس}
{تصريحات خاصة}
عام
{التصريحات العامة}
نهاية؛
فار
Form1: TForm1؛
MyStream: TMemorystream؛ {كائن دفق الذاكرة}
تطبيق
{$R *.DFM}
الإجراء TForm1.Cjt_GetScreen(var Mybmp: TBitmap; DrawCur: Boolean);
فار
Cursorx، Cursory: عدد صحيح؛
العاصمة: هدك؛
Mycan: Tcanvas؛
ص: تريكت؛
DrawPos: نقطة؛
MyCursor: TIcon؛
عقد: هوند؛
خيط: كلمة مزدوجة؛
النائب: نقطة؛
pIconInfo: TIconInfo;
يبدأ
Mybmp := Tbitmap.Create {إنشاء BMPMAP}
Mycan := TCanvas.Create {لقطة شاشة}؛
dc := GetWindowDC(0);
يحاول
Mycan.Handle := dc;
R := Rect(0, 0, screen.Width, screen.Height);
Mybmp.Width := R.Right;
Mybmp.Height := R.Bottom;
Mybmp.Canvas.CopyRect(R, Mycan, R);
أخيراً
ReleaseDC(0, DC);
نهاية؛
Mycan.Handle := 0;
Mycan.Free;
إذا DrawCur ثم {ارسم صورة الماوس}
يبدأ
GetCursorPos(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);
سريع := DrawPos.y - round(pIconInfo.yHotspot);
Mybmp.Canvas.Draw(cursorx, cursory, MyCursor);
حذفObject(pIconInfo.hbmColor);{يقوم GetIconInfo بإنشاء كائنين نقطيين عند استخدام هذين الكائنين يجب تحريرهما يدويًا}
حذفObject(pIconInfo.hbmMask); {وإلا، بعد الاتصال به، سيتم إنشاء صورة نقطية، وستؤدي الاستدعاءات المتعددة إلى إنشاء عدة مكالمات حتى استنفاد الموارد}
Mycursor.ReleaseHandle {تحرير ذاكرة المصفوفة}
MyCursor.Free؛ {حرر مؤشر الماوس}
نهاية؛
نهاية؛
الإجراء TForm1.FormCreate(Sender: TObject);
يبدأ
ServerSocket1.Port := 3000 {منفذ}
ServerSocket1.Open {يبدأ المقبس في الاستماع}
نهاية؛
الإجراء TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
يبدأ
إذا كان ServerSocket1.Active ثم ServerSocket1.Close {إغلاق المقبس}؛
نهاية؛
الإجراء TForm1.ServerSocket1ClientRead(Sender: TObject;
المقبس: TCustomWinSocket)؛
فار
S، S1: سلسلة؛
MyBmp: TBitmap؛
Myjpg: TJpegimage;
يبدأ
S :=Socket.ReceiveText;
إذا كان S = 'cap'، فعندئذ {يُصدر العميل أمر التقاط الشاشة}
يبدأ
يحاول
MyStream := TMemorystream.Create;{إنشاء دفق ذاكرة}
MyBmp := TBitmap.Create;
Myjpg := TJpegimage.Create;
Cjt_GetScreen(MyBmp, True); {صحيح يعني التقاط صورة الماوس}
Myjpg.Assign(MyBmp); {تحويل صور BMP إلى تنسيق JPG لسهولة النقل على الإنترنت}
Myjpg.CompressionQuality := 10; {إعداد النسبة المئوية لضغط ملف JPG، كلما زاد العدد، كانت الصورة أكثر وضوحًا، ولكن كلما كانت البيانات أكبر}
Myjpg.SaveToStream(MyStream); {اكتب صورة JPG للبث}
Myjpg.free;
MyStream.Position := 0;{ملاحظة: يجب إضافة هذه الجملة}
s1 := inttostr(MyStream.size);{حجم الدفق}
المقبس.sendtext(s1); {إرسال حجم الدفق}
أخيراً
MyBmp.free;
نهاية؛
نهاية؛
إذا كان s = 'ready' إذن {العميل جاهز لتلقي الصور}
يبدأ
MyStream.Position := 0;
المقبس.SendStream(MyStream); {أرسل الدفق}
نهاية؛
نهاية؛
نهاية.
ما ورد أعلاه هو الخادم، دعنا نكتب برنامج العميل أدناه. أنشئ مشروعًا جديدًا وأضف عنصر التحكم في المقبس ClientSocket، وعنصر التحكم في عرض الصورة، واللوحة، والتحرير، وزرين، وعنصر التحكم في شريط الحالة StatusBar1. ملاحظة: ضع Edit1 وزرين فوق Panel1. تتشابه سمات ClientSocket مع ServerSocket، ولكن هناك سمة عنوان إضافية تشير إلى عنوان IP الخاص بالخادم المراد توصيله. بعد ملء عنوان IP، انقر فوق "اتصال" لتأسيس اتصال ببرنامج الخادم، وفي حالة النجاح، يمكن بدء الاتصال. سيؤدي النقر فوق "التقاط الشاشة" إلى إرسال الأحرف إلى الخادم. نظرًا لأن البرنامج يستخدم وحدات صور JPEG، يجب إضافة Jpeg إلى الاستخدامات.
الكود بأكمله هو كما يلي:
وحدة الوحدة 2 {العميل}؛
واجهة
الاستخدامات
Windows، الرسائل، SysUtils، الفئات، الرسومات، عناصر التحكم، النماذج، مربعات الحوار، StdCtrls، ScktComp، ExtCtrls، Jpeg، ComCtrls؛
يكتب
TForm1 = الفئة (TForm)
ClientSocket1: TClientSocket;
Image1: تيماج؛
StatusBar1: TStatusBar;
اللوحة 1: لوحة T؛
تحرير 1: تحرير؛
Button1: TButton؛
Button2: TButton؛
الإجراء Button1Click(Sender: TObject);
الإجراء ClientSocket1Connect(Sender: TObject;
المقبس: TCustomWinSocket)؛
الإجراء Button2Click(Sender: TObject);
الإجراء ClientSocket1Error(Sender: TObject; المقبس: TCustomWinSocket;
ErrorEvent: TERrorEvent; var ErrorCode: Integer);
الإجراء ClientSocket1Read(Sender: TObject; المقبس: TCustomWinSocket);
الإجراء FormCreate(Sender: TObject);
الإجراء FormClose(Sender: TObject; var Action: TCloseAction);
الإجراء ClientSocket1Disconnect(Sender: TObject;
المقبس: TCustomWinSocket)؛
خاص
{تصريحات خاصة}
عام
{التصريحات العامة}
نهاية؛
فار
Form1: TForm1؛
ماي سايز: لونجينت؛
MyStream: TMemorystream؛ {كائن دفق الذاكرة}
تطبيق
{$R *.DFM}
الإجراء TForm1.FormCreate(Sender: TObject);
يبدأ
{-------- فيما يلي تعيين خصائص المظهر لعنصر التحكم في النافذة------------- }
{ملاحظة: ضع الزر 1 والزر 2 والتحرير 1 أعلى اللوحة 1}
Edit1.Text := '127.0.0.1';
Button1.Caption := 'الاتصال بالمضيف';
Button2.Caption := 'التقاط الشاشة'؛
Button2.Enabled := خطأ؛
Panel1.Align := alTop;
Image1.Align := alClient;
Image1.Stretch := صحيح؛
StatusBar1.Align:=alBottom;
StatusBar1.SimplePanel := صحيح؛
{----------------------------------------------------------------- --------- }
MyStream := TMemorystream.Create {إنشاء كائن دفق الذاكرة}
حجمي := 0;
نهاية؛
الإجراء TForm1.Button1Click(Sender: TObject);
يبدأ
إن لم يكن ClientSocket1.Active بعد ذلك
يبدأ
ClientSocket1.Address:= Edit1.Text; {عنوان IP البعيد}
ClientSocket1.Port := 3000 {منفذ المقبس}
ClientSocket1.Open؛ {إنشاء اتصال}
نهاية؛
نهاية؛
الإجراء TForm1.Button2Click(Sender: TObject);
يبدأ
Clientsocket1.Socket.SendText('cap'); {أرسل أمرًا لإعلام الخادم لالتقاط صورة الشاشة}
Button2.Enabled := خطأ؛
نهاية؛
الإجراء TForm1.ClientSocket1Connect(Sender: TObject;
المقبس: TCustomWinSocket)؛
يبدأ
StatusBar1.SimpleText := 'مع المضيف' + ClientSocket1.Address + 'تم إنشاء الاتصال بنجاح!';
Button2.Enabled := صحيح؛
نهاية؛
الإجراء TForm1.ClientSocket1Error(Sender: TObject;
المقبس: TCustomWinSocket؛ ErrorEvent: TERrorEvent;
varErrorCode: Integer);
يبدأ
رمز الخطأ: = 0 {لا تظهر نافذة الخطأ}
StatusBar1.SimpleText := 'غير قادر على الاتصال بالمضيف' + ClientSocket1.Address + 'تم إنشاء الاتصال!';
نهاية؛
الإجراء TForm1.ClientSocket1Disconnect(Sender: TObject;
المقبس: TCustomWinSocket)؛
يبدأ
StatusBar1.SimpleText := 'مع المضيف' + ClientSocket1.Address + 'قطع الاتصال!';
Button2.Enabled := خطأ؛
نهاية؛
الإجراء TForm1.ClientSocket1Read(Sender: TObject;
المقبس: TCustomWinSocket)؛
فار
MyBuffer: صفيف [0..10000] من البايت {تعيين المخزن المؤقت للاستقبال}
MyReceviceLength: integer;
S: سلسلة؛
MyBmp: TBitmap؛
MyJpg: TJpegimage;
يبدأ
StatusBar1.SimpleText := 'يتم الآن استلام البيانات...';
إذا كان MySize = 0، {MySize هو عدد البايتات التي يرسلها الخادم إذا كان 0، فهذا يعني أن استقبال الصورة لم يبدأ بعد}
يبدأ
S :=Socket.ReceiveText;
MySize := Strtoint(S); {اضبط عدد البايتات التي سيتم استلامها}
Clientsocket1.Socket.SendText('ready'); {أرسل أمرًا لإعلام الخادم ببدء إرسال الصور}
نهاية
آخر
ابدأ {في ما يلي جزء استلام بيانات الصورة}
MyReceviceLength := المقبس.ReceiveLength {قراءة طول الحزمة}
StatusBar1.SimpleText := 'يتم استلام البيانات، حجم البيانات هو:' + inttostr(MySize);
المقبس.ReceiveBuf(MyBuffer, MyReceviceLength); {استلم حزمة البيانات واقرأها في المخزن المؤقت}
MyStream.Write(MyBuffer, MyReceviceLength); {اكتب البيانات في الدفق}
إذا كان MyStream.Size >= MySize ثم {إذا كان طول الدفق أكبر من عدد البايتات التي سيتم استلامها، فقد اكتمل الاستقبال}
يبدأ
MyStream.Position := 0;
MyBmp := tbitmap.Create;
MyJpg := tjpegimage.Create;
يحاول
MyJpg.LoadFromStream(MyStream); {اقرأ البيانات الموجودة في الدفق في كائن صورة JPG}
MyBmp.Assign(MyJpg); {تحويل JPG إلى BMP}
StatusBar1.SimpleText := 'عرض الصورة';
Image1.Picture.Bitmap.Assign(MyBmp);
أخيرًا {في ما يلي أعمال التنظيف}
MyBmp.free;
MyJpg.free;
Button2.Enabled := صحيح؛
{Socket.SendText('cap');أضف هذه الجملة لالتقاط الشاشة بشكل مستمر}
MyStream.Clear;
حجمي := 0;
نهاية؛
نهاية؛
نهاية؛
نهاية؛
الإجراء TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
يبدأ
MyStream.Free {حرر كائن دفق الذاكرة}
إذا كان ClientSocket1.Active ثم ClientSocket1.Close {إغلاق اتصال المقبس}؛
نهاية؛
نهاية.
مبدأ البرنامج: قم بتشغيل الخادم لبدء الاستماع، ثم قم بتشغيل العميل، وأدخل عنوان IP الخاص بالخادم لإنشاء اتصال، ثم أرسل حرفًا لإعلام الخادم بالتقاط الشاشة. يستدعي الخادم الوظيفة المخصصة Cjt_GetScreen لالتقاط الشاشة وحفظها بتنسيق BMP، وتحويل BMP إلى JPG، وكتابة JPG في دفق الذاكرة، ثم إرسال الدفق إلى العميل. بعد استلام الدفق، يقوم العميل بتنفيذ العملية المعاكسة، حيث يقوم بتحويل الدفق إلى JPG ثم إلى BMP ومن ثم عرضه.
ملاحظة: نظرًا لقيود المقبس، لا يمكن إرسال البيانات الكبيرة جدًا في وقت واحد، ولكن يمكن إرسالها عدة مرات فقط. لذلك، في البرنامج، بعد تحويل لقطة الشاشة إلى دفق، يرسل الخادم أولاً حجم الدفق لإعلام العميل بمدى حجم الدفق. يستخدم العميل هذا الرقم لتحديد ما إذا كان قد تم استلام الدفق أم لا هو كذلك، سيتم تحويله وعرضه.
يستخدم هذا البرنامج وOICQ السابق الذي تم إنشاؤه ذاتيًا كائن دفق الذاكرة TMemoryStream. في الواقع، يعد كائن الدفق هذا هو الأكثر استخدامًا في البرمجة، حيث يمكنه تحسين قدرات القراءة والكتابة للإدخال/الإخراج، وإذا كنت ترغب في تشغيل عدة أنواع مختلفة من التدفقات في نفس الوقت وتبادل البيانات مع بعضها البعض، فاستخدمه ككائن. "الوسيط" هو الأفضل. على سبيل المثال، إذا قمت بضغط دفق أو إلغاء ضغطه، فعليك أولاً إنشاء كائن TMemoryStream، ثم نسخ البيانات الأخرى إليه، ثم تنفيذ العمليات المقابلة. ولأنه يعمل مباشرة في الذاكرة، فإن كفاءته عالية جدًا. في بعض الأحيان لن تلاحظ أي تأخير.
مجالات التحسين في البرنامج: بالطبع يمكنك إضافة وحدة ضغط للضغط قبل الإرسال ثم الإرسال. ملحوظة: هناك أيضًا خدعة هنا، وهي ضغط BMP مباشرةً بدلاً من تحويله إلى JPG ثم ضغطه. أظهرت التجارب أن حجم الصورة في البرنامج أعلاه يبلغ حوالي 40-50 كيلو بايت. إذا تمت معالجتها باستخدام خوارزمية ضغط LAH، فسيكون حجمها 8-12 كيلو بايت فقط، مما يجعل النقل أسرع. إذا كنت تريد التحرك بشكل أسرع، يمكنك استخدام هذه الطريقة: قم أولاً بالتقاط الصورة الأولى وإرسالها، ثم ابدأ من الصورة الثانية وأرسل فقط الصور في مناطق مختلفة عن الصورة السابقة. يوجد برنامج أجنبي يسمى Remote Administrator يستخدم هذه الطريقة. البيانات التي اختبروها هي كما يلي: 100-500 إطار في الثانية على الشبكة المحلية، و5-10 إطارات في الثانية على الإنترنت عندما تكون سرعة الشبكة منخفضة للغاية. هذه الاستطرادات تريد فقط توضيح حقيقة واحدة: عند التفكير في مشكلة ما، خاصة عند كتابة برنامج، وخاصة البرنامج الذي يبدو معقدًا للغاية، لا تنشغل بها كثيرًا في بعض الأحيان، قد تفكر في الأمر من زاوية أخرى . البرنامج مات والموهبة حية. وبطبيعة الحال، لا يمكن الاعتماد إلا على تراكم الخبرة. لكن تكوين عادات جيدة منذ البداية سيؤتي ثماره طوال حياتك!