الجانب المظلم للتطبيق. ProcessMessages

المادة المقدمة من ماركوس Junglas

عند برمجة معالج الأحداث في دلفي (مثل عند النقر حدث TButton) ، هناك وقت يحتاج فيه التطبيق إلى أن يكون مشغولا لفترة من الوقت ، على سبيل المثال يحتاج الرمز إلى كتابة ملف كبير أو ضغط بعض البيانات.

إذا قمت بذلك فسوف تلاحظ ذلك يبدو أن طلبك مؤمن. لا يمكن نقل النموذج بعد الآن ولا تظهر الأزرار أي علامة على الحياة. يبدو أن تحطمت.

السبب هو أن تطبيق Delpi مترابط واحد. يمثل الرمز الذي تكتبه مجموعة من الإجراءات التي يطلق عليها مؤشر دلفي الرئيسي كلما حدث. ما تبقى من الوقت مؤشر الترابط الرئيسي هو معالجة رسائل النظام وأشياء أخرى مثل وظائف التعامل مع النموذج والمكونات.

لذلك ، إذا لم تنهِ معالجة حدثك عن طريق القيام ببعض الأعمال المطولة ، فستمنع التطبيق من التعامل مع هذه الرسائل.

حل شائع لمثل هذا النوع من المشاكل هو استدعاء "التطبيق. عملية الرسائل ". "التطبيق" هو ​​كائن عالمي من فئة TApplication.

تطبيق. تتعامل رسائل المعالجة مع جميع رسائل الانتظار مثل حركات النوافذ ونقرات الأزرار وما إلى ذلك. يستخدم بشكل شائع كحل بسيط للحفاظ على التطبيق الخاص بك "يعمل".

لسوء الحظ ، فإن الآلية الكامنة وراء "ProcessMessages" لها خصائصها الخاصة ، والتي قد تسبب ارتباكًا كبيرًا!

instagram viewer

ماذا ProcessMessages؟

يعالج PprocessMessages جميع رسائل نظام الانتظار في قائمة انتظار رسائل التطبيقات. يستخدم Windows رسائل "للتحدث" مع جميع التطبيقات قيد التشغيل. يتم إحضار تفاعل المستخدم إلى النموذج عبر الرسائل ويقوم "ProcessMessages" بمعالجتها.

إذا كان الماوس ينزل على TButton ، على سبيل المثال ، فإن ProgressMessages يقوم بكل ما يجب أن يحدث في هذا الحدث مثل إعادة طلاء الزر لحالة "مضغوطة" وبالطبع استدعاء لإجراء معالجة OnClick () إذا قمت بتعيين واحد.

هذه هي المشكلة: قد تحتوي أي مكالمة إلى ProcessMessages على مكالمة متكررة لأي معالج حدث مرة أخرى. إليك مثال:

استخدم الكود التالي لمعالج OnClick حتى للزر ("work"). يحاكي for-statement مهمة معالجة طويلة مع بعض المكالمات إلى ProcessMessages بين الحين والآخر.

تم تبسيط هذا لتحسين قابلية القراءة:

{في MyForm:}
مستوى العمل: عدد صحيح
{OnCreate:}
مستوى العمل: = 0 ؛
إجراء TForm1.WorkBtnClick (Sender: TObject)؛
فار
دورة: عدد صحيح.
ابدأ
inc (مستوى العمل) ؛
إلى عن على دورة: = 1 إلى 5 فعل
ابدأ
خطوط Memo1.Lines. إضافة ('- العمل' + IntToStr (WorkLevel) + '، Cycle' + IntToStr (دورة) ؛
تطبيق. ProcessMessages
النوم (1000) ؛ // أو بعض الأعمال الأخرى
النهاية;
خطوط Memo1.Lines. أضف ("العمل" + IntToStr (WorkLevel) + "انتهى")) ؛
ديسمبر (مستوى العمل) ؛
النهاية;

دون "ProcessMessages" ، تتم كتابة الأسطر التالية على المذكرة ، إذا تم الضغط على الزر مرتين في وقت قصير:

 - العمل 1 ، الدورة 1
- العمل 1 ، الدورة 2
- العمل 1 ، الدورة 3
- العمل 1 ، الدورة 4
- العمل 1 ، الدورة 5
انتهى العمل 1.
- العمل 1 ، الدورة 1
- العمل 1 ، الدورة 2
- العمل 1 ، الدورة 3
- العمل 1 ، الدورة 4
- العمل 1 ، الدورة 5
انتهى العمل 1.

على الرغم من أن الإجراء مشغول ، إلا أن النموذج لا يظهر أي رد فعل ، ولكن تم وضع النقر الثاني في قائمة انتظار الرسائل بواسطة Windows. مباشرة بعد انتهاء "OnClick" ، سيتم الاتصال به مرة أخرى.

بما في ذلك "ProcessMessages" ، قد يكون الإخراج مختلفًا جدًا:

 - العمل 1 ، الدورة 1
- العمل 1 ، الدورة 2
- العمل 1 ، الدورة 3
- العمل 2 ، الدورة 1
- العمل 2 ، الدورة 2
- العمل 2 ، الدورة 3
- العمل 2 ، الدورة 4
- العمل 2 ، الدورة 5
انتهى العمل 2.
- العمل 1 ، الدورة 4
- العمل 1 ، الدورة 5
انتهى العمل 1.

هذه المرة يبدو أن النموذج يعمل مرة أخرى ويقبل أي تفاعل من جانب المستخدم. لذلك يتم الضغط على الزر في منتصف الطريق خلال أول وظيفة "للعامل" الخاصة بك مرة أخرى ، والتي سيتم التعامل معها على الفور. يتم التعامل مع جميع الأحداث القادمة مثل أي استدعاء وظيفة أخرى.

من الناحية النظرية ، خلال كل مكالمة إلى "ProgressMessages" قد يحدث أي عدد من النقرات ورسائل المستخدم "في مكان".

لذا كن حذرا مع التعليمات البرمجية الخاصة بك!

مثال مختلف (في كود زائف بسيط!):

إجراء OnClickFileWrite () ؛
فار myfile: = TFileStream؛
ابدأ
myfile: = TFileStream.create ('myOutput.txt') ؛
محاولة
في حين BytesReady> 0 فعل
ابدأ
ملفي. اكتب (DataBlock) ؛
dec (BytesReady ، sizeof (DataBlock)) ؛
DataBlock [2]: = # 13؛ {خط الاختبار 1}
تطبيق. ProcessMessages
DataBlock [2]: = # 13؛ {خط الاختبار 2}
النهاية;
أخيرا
myfile.free؛
النهاية;
النهاية;

تقوم هذه الوظيفة بكتابة كمية كبيرة من البيانات وتحاول "إلغاء قفل" التطبيق باستخدام "ProcessMessages" في كل مرة يتم فيها كتابة كتلة من البيانات.

إذا نقر المستخدم على الزر مرة أخرى ، فسيتم تنفيذ نفس الرمز أثناء استمرار كتابة الملف. لذلك لا يمكن فتح الملف مرة ثانية وفشل الإجراء.

ربما سيقوم التطبيق الخاص بك ببعض الاسترداد خطأ مثل تحرير المخازن المؤقتة.

كنتيجة محتملة ، سيتم تحرير "Datablock" والرمز الأول سيؤدي "فجأة" إلى رفع "انتهاك الوصول" عندما يصل إليه. في هذه الحالة: سيعمل سطر الاختبار 1 ، سيتعطل سطر الاختبار 2.

الطريقة الأفضل:

لتسهيل ذلك ، يمكنك تعيين النموذج بالكامل "ممكّنة: = خطأ" ، والذي يحظر جميع مدخلات المستخدم ، لكنه لا يعرض ذلك للمستخدم (جميع الأزرار غير رمادية).

هناك طريقة أفضل تتمثل في تعيين جميع الأزرار على "معطل" ، ولكن هذا قد يكون معقدًا إذا كنت تريد الاحتفاظ بزر "إلغاء" واحد على سبيل المثال. ستحتاج أيضًا إلى استعراض كافة المكونات لتعطيلها وعندما يتم تمكينها مرة أخرى ، تحتاج إلى التحقق مما إذا كان يجب أن يكون هناك بعض ما تبقى في حالة تعطيل.

يمكنك تعطيل عناصر تحكم تابعة لحاوية عند تغيير الخاصية Enabled.

كما يوحي اسم الفئة "TNotifyEvent" ، يجب استخدامه فقط لردود الفعل على المدى القصير للحدث. أفضل طريقة هي IMHO لوضع كل الكود "البطيء" في خيط خاص.

فيما يتعلق بمشاكل "PrecessMessages" و / أو تمكين المكونات وتعطيلها ، استخدام الخيط الثاني يبدو أن ليست معقدة للغاية على الإطلاق.

تذكر أنه حتى الأسطر البسيطة والسريعة من التعليمات البرمجية قد تتوقف لمدة ثوانٍ ، على سبيل المثال قد يضطر فتح ملف على محرك أقراص إلى الانتظار حتى ينتهي محرك الدوران. لا يبدو جيدًا جدًا إذا بدا أن التطبيق قد تعطل لأن محرك الأقراص بطيء جدًا.

هذا هو. في المرة القادمة التي تضيف "التطبيق. ProcessMessages "، فكر مرتين ؛)

instagram story viewer