تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
Overriding WndProc اعتراض رسائل النظام
#1
كاتب الموضوع : samerselo


يرسل نظام التشغيل – الويندوز – جميع أنواع الرسائل للتطبيقات والتي تخبرها عن التغييرات في بيئة النوافذ. وهذه الرسائل تخبر النموذج بالقيام بعدة أشياء كإعادة الرسم، النقل، إعادة التحجيم، الإخفاء، التصغير، والإغلاق بالإضافة إلى الاستجابة للتغييرات في بيئة النوافذ أو القيام بأي شئ آخر متعلق بالنوافذ. ويكون لجميع تطبيقات النوافذ إجراء يتم إطلاقه للاستجابة لتلك الرسائل ويدعى هذا الإجراء في العادة WindowProc ويعالج فيجول بايزيك دوت نيت هذه الرسائل في إجراء يدعى WndProc الذي يمكنك تجاوزه لعمل أشياء مخصصة عند استقبال برنامجك لرسائل معينة.
فمثلا الكود التالي يظهر كيف يضمن البرنامج إبقاء النافذة لنفس نسب قياسات الواجهة بحيث نقوم بتجاوز الإجراء WndProc الخاص بالنموذج والبحث عن الرسالة WM_SIZING التي تستقبل محددات تتضمن الحافة التي يقوم المستخدم بجرها لتغيير حجم النموذج وتركيب من النوع Rect يعطي للنموذج مكانه وحجمه الجديدين ويبدأ الكود بتحديد التركيب Rect ثم يعلن عن إجراء تجاوز للإجراء WndProc والذي يحدد بعض الثوابت والمتغيرات الساكنة التي تحمل القيم الأساسية للنموذج ثم يحدد الإجراء WndProc نوع الرسالة التي يقوم بمعالجتها فإن كانت WM_SIZING تستخدم الوظيفة PtrToStructure لنسخ m.LParam إلى التركيب Rect الذي يتم استخدامه لحساب العرض والطول الجديدين للنموذج والنسبة بينهما. فإن كانت هذه هي المرة الأولى التي يتم تنفيذ WndProc فيها فتكون بذلك قيمة المتغير الساكن fixed_aspect_ratio مساوية للصفر وعندما يرى أن قيمته مساوية للصفر يقوم بتخزين نسبة الطول والعرض الحاليين للنموذج في ذلك المتغير ثم يقوم WndProc بتحديد فيما إذا كانت النسبة الخاصة بالنموذج مختلفة عن القيمة الأصلية فإن تم تغييرها يقرر أي بعد (طول أو عرض) سيتم حفظه فإن كان المستخدم يقوم بالسحب من إحدى الزوايا يقوم الإجراء بحساب أي البعدين (طول أو عرض) هو الأكبر ثم يقوم بحساب قيمة البعد الآخر الذي يحقق نسبة الطول للعرض الخاصة بالنموذج. وإن كان المستخدم يقوم بسحب أحد الأطراف يقوم البرنامج بالحفاظ على العرض الجديد ويقوم بحساب الارتفاع المناسب حسب النسبة وكذلك إن كان يقوم بالسحب باستخدام إحدى الحافتين العلوية أو السفلية يقوم بتثبيت الارتفاع ويقوم بحساب العرض الملائم حسب النسبة ثم يقوم البرنامج بتقرير فيما إذا كان يجب عليه نقل النموذج بحيث يحرك الطرف الذي يقوم المستخدم بسحبه فمثلا إن كان المستخدم يسحب الزاوية اليسارية السفلى فالبرنامج يغير قيم اليسار والأسفل بحيث تبقى الزاوية العلوية اليمنى ثابتة ثم يقوم WndProc باستدعاء Marshal.StructureToPtr لينسخ التركيب Rect مجددا إلى m.LParam وأخيرا يقوم WndProc باستدعاء MyBase.WndProc ليترك المجال للإجراء WndProc الأب لاستخدام القيم الجديدة لتغيير حجم النموذج. واستدعاء الإجراء WndProc الخاص بالأب هام جدا فإن لم يقم البرنامح باستدعائه لكل رسالة لم يتم معالجتها بشكل كامل فإن تلك الرسالة لن يتم معالجتها وبالنتيجة فالنافذة لن تقوم بمعالجة جميع الرسائل الخاصة بها مما ينتج عن ذلك عدة مشاكل مختلفة كعدم قدرتها على إعادة رسم نفسها أو التحريك أو إظهار القوائم أو غيرها من الأمور الخاصة بالنموذج

كود :
Imports System.Runtime.InteropServices

Public Class Form1

Public Structure Rect
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_SIZING As Long = &H214
Const WMSZ_LEFT As Integer = 1
Const WMSZ_RIGHT As Integer = 2
Const WMSZ_TOP As Integer = 3
Const WMSZ_TOPLEFT As Integer = 4
Const WMSZ_TOPRIGHT As Integer = 5
Const WMSZ_BOTTOM As Integer = 6
Const WMSZ_BOTTOMLEFT As Integer = 7
Const WMSZ_BOTTOMRIGHT As Integer = 8
Static fixed_aspect_ratio As Double = 0
Dim new_aspect_ratio As Double
If m.Msg = WM_SIZING And m.HWnd.Equals(Me.Handle) Then
' Turn the message’s lParam into a Rect.
Dim r As Rect
r = DirectCast( _
Marshal.PtrToStructure(m.LParam, GetType(Rect)), _
Rect)
' Get the current dimensions.
Dim wid As Double = r.right - r.left
Dim hgt As Double = r.bottom - r.top
' Get the new aspect ratio.
new_aspect_ratio = hgt / wid
' The first time, save the form’s aspect ratio.
If fixed_aspect_ratio = 0 Then
fixed_aspect_ratio = new_aspect_ratio
End If
' See if the aspect ratio is changing.
If fixed_aspect_ratio <> new_aspect_ratio Then
' To decide which dimension we should preserve,
' see what border the user is dragging.
If m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
m.WParam.ToInt32 = WMSZ_TOPRIGHT Or _
m.WParam.ToInt32 = WMSZ_BOTTOMLEFT Or _
m.WParam.ToInt32 = WMSZ_BOTTOMRIGHT Then

' The user is dragging a corner.
' Preserve the bigger dimension.
If new_aspect_ratio > fixed_aspect_ratio Then
' It’s too tall and thin. Make it wider.
wid = hgt / fixed_aspect_ratio
Else
' It’s too short and wide. Make it taller.
hgt = wid * fixed_aspect_ratio
End If
ElseIf m.WParam.ToInt32 = WMSZ_LEFT Or _
m.WParam.ToInt32 = WMSZ_RIGHT Then

' The user is dragging a side.
' Preserve the width.
hgt = wid * fixed_aspect_ratio
ElseIf m.WParam.ToInt32 = WMSZ_TOP Or _
m.WParam.ToInt32 = WMSZ_BOTTOM Then

' The user is dragging the top or bottom.
' Preserve the height.
wid = hgt / fixed_aspect_ratio
End If
' Figure out whether to reset the top/bottom
' and left/right.
' See if the user is dragging the top edge.
If m.WParam.ToInt32 = WMSZ_TOP Or _
m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
m.WParam.ToInt32 = WMSZ_TOPRIGHT Then

' Reset the top.
r.top = r.bottom - CInt(hgt)
Else
' Reset the bottom.
r.bottom = r.top + CInt(hgt)
End If
' See if the user is dragging the left edge.
If m.WParam.ToInt32 = WMSZ_LEFT Or _
m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
m.WParam.ToInt32 = WMSZ_BOTTOMLEFT Then

' Reset the left.
r.left = r.right - CInt(wid)
Else
' Reset the right.
r.right = r.left + CInt(wid)
End If
' Update the Message object’s LParam field.
Marshal.StructureToPtr(r, m.LParam, True)
End If
End If
MyBase.WndProc(m)
End Sub

End Class
في فيجول بايزيك 6 والنسخ السابقة له يمكن للبرنامج تثبيت إجراء WindowProc خاص لعمل نفس العمليات بصعوبة وتدعى هذه العملية بـ subclassing ويعتبر الاسم غير موفق لأن البرمجة غرضية التوجه تستخدم نفس الاسم subclassing للإشارة إلى إنشاء فئة من أخرى كما نفعل عند استخدامنا للعبارة Inherits وتكون عملية تجاوز WndProc في فيجول بايزيك دوت نيت أسهل بكثير وأكثر أمنا من الطريقة المستخدمة في فيجول بايزيك 6 كما تلاحظ في المثال ولكنك مازلت بحاجة لبعض الخدع لتحويل IntPtr المخزن في m.LParam من وإلى تراكيب مناسبة. كما يكون عليك تقرير أي من الرسائل ستقوم باعتراضها وأي من قيم m.LParam و m.WParam تأخذ وكيف يمكنك التأثير عليهم بأمان. وإحدى الطرق لتعلم هذه الرسائل هو إدراج الإجراء WndProc التالي ثم القيام بالعمل الذي تريد دراسته (تغيير حجم النموذج على سبيل المثال)

كود :
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Debug.Print(m.ToString)
MyBase.WndProc(m)
End Sub
والسطر التالي يبين النتيجة من أجل الرسالة WM_SIZING المرسلة إلى النموذج عندما يقوم المستخدم بتغيير حجمه وعلى الأقل يظهر اسم الرسالة WM_SIZING وقيمتها الست عشرية 0x214

كود :
msg=0x214 (WM_SIZING) hwnd=0x30b8c wparam=0x2 lparam=0x590e29c result=0x0
والبحث عن اسم الرسالة في موقع مايكروسوفت ومواقع البرمجة الأخرى يعطيك في العادة معلومات أخرى تحتاج لمعرفتها مثل معنى قيم m.WParam و m.LParam كما يجدر ملاحظة أن Form class يرث الإجراء WndProc من Control class والتي ترثه بقية التحكمات على النموذج بدورها و الذي يعني أنك تستطيع تجاوز الإجراء WndProc بأي تحكم منها وبذلك يمكنك التحكم بتصرفاتها. فمثلا ترينا قطعة الكود التالية كيف تعمل الفئة NoCtxMnuTextBox والمشتقة من التحكم TextBox بحيث يقوم الإجراء WndProc الخاص بها بتفحص الرسالة WM_CONTEXTMENU ثم يستدعي الإجراء الخاص بالفئة الأب من أجل جميع الرسائل الأخرى وبالقيام بإفشال معالجة الرسالة WM_CONTEXTMENU نمنع إظهار القائمة المنبثقة الخاصة بالتحكم والتي تضم أوامر مثل النسخ واللصق عندما تنقر بزر الماوس اليميني عليها

كود :
Public Class NoCtxMnuTextBox
Inherits System.Windows.Forms.TextBox
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_CONTEXTMENU As Integer = &H7B
If m.Msg <> WM_CONTEXTMENU Then
MyBase.WndProc(m)
End If
End Sub
End Class
}}}
تم الشكر بواسطة:



التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم