س: هل يمكن كتابة كود لتعطيل بعض الأزرار
الخاصة عن العمل مثل زر الويندوز و Alt+Tab
و Ctrl+Esc؟
ج: لا يمكن فعل
هذا باستخدام أحداث لوحة المفاتيح KeyBown
و KeyUp و KeyPress.. صحيح أنها تستجيب لضغط بعض هذه
الأزرار الخاصة، ولكن بعد أن يكون نظام الويندوز قد نفذ وظائفها بالفعل.. على سبيل
المثال: ضغط Alt+Tab
يؤدي إلى التنقل بين البرامج المفتوحة في الويندوز.. وبالتالي لن يستطيع برنامجك
الاستجابة لها، لأن برنامجا آخر سيكون قد تم تفعيله وعرض نافذته في تلك اللحظة.
ولحل هذه
المشكلة، يجب استخدام تقنية تعرف بتصيد لوحة المفاتيح Keyboard Hooking، وذلك باستخدام
ثلاثة دوال API
هي SetWindowsHookEx
و CallNextHookEx
و UnhookWindowsHookEx،
حيث:
SetWindowsHookEx:
تطلب هذه الدالة
من الويندوز إرسال الرسائل منخفصة المستوى Low Level الخاصة بلوحة المفاتيح إلى دالة تسمى
دالة الاستجابة Callback
Function تقوم بتعريفها في البرنامج الخاص بك، وبهذا تستطيع
أن تعالج هذه الأزرار مبكرا قبل أن ينفذها الويندوز.
CallNextHookEx:
تستخدم هذه
الدالة لتمرير رسائل لوحة المفاتيح من برنامجك إلى أي برنامج آخر يقتنص لوحة
المفاتيح.. فإذا أردت أن تمنع تنفيذ أي أزرار، فلا تقم باستدعاء هذه الدالة من
داخل دالة الاستجابة Callback
Function الخاصة بك.
UnhookWindowsHookEx:
يؤدي استدعاء
هذه الدالة إلى توقف برنامجك عن تصيد لوحة المفاتيح.
وقد رأيت وضع
الكود اللازم لإيقاف عمل أزرار الويندوز و Alt+Tab و Ctrl+Esc
في فئة خاصة أسميتها SpecialChars،
وسأضع لكم نسختين منها، واحدة بفيجيوال بيزيك والثانية بسي شارب:
' كود فيجيوال بيزيك
Imports System
Imports System.Diagnostics
Imports
System.Runtime.InteropServices
Imports System.Windows.Forms
Class SpecialChars
Private Shared Sp As SpecialChars
Public Shared Sub DisableSpChrs()
Sp = New SpecialChars()
Dim objCurrentModule As ProcessModule = Process.GetCurrentProcess().MainModule
objKeyboardProcess = New LowLevelKeyboardProc(AddressOf Sp.captureKey)
Sp.ptrHook =
SetWindowsHookEx(13, objKeyboardProcess,
GetModuleHandle(objCurrentModule.ModuleName), 0)
End Sub
Public Shared Sub EnableSpChrs()
UnhookWindowsHookEx(Sp.ptrHook)
End Sub
' Structure contain information about low-level keyboard input event
<StructLayout(LayoutKind.Sequential)> _
Private Structure KBDLLHOOKSTRUCT
Public key As Keys
Public scanCode As Integer
Public flags As Integer
Public time As Integer
Public extra As IntPtr
End Structure
'System level functions to be used for hook and unhook keyboard
input
Private Delegate Function LowLevelKeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function SetWindowsHookEx(ByVal id As Integer, ByVal callback As LowLevelKeyboardProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInteger) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function UnhookWindowsHookEx(ByVal hook As IntPtr) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function CallNextHookEx(ByVal hook As IntPtr, ByVal nCode As Integer, ByVal wp As IntPtr, ByVal lp As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetModuleHandle(ByVal name As String) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetAsyncKeyState(ByVal key As Keys) As Short
End Function
'Declaring Global objects
Private ptrHook As IntPtr
Private Shared objKeyboardProcess As LowLevelKeyboardProc
Private Function captureKey(ByVal nCode As Integer, ByVal wp As IntPtr, ByVal lp As IntPtr) As IntPtr
If nCode >= 0 Then
Dim objKeyInfo As KBDLLHOOKSTRUCT = CType(Marshal.PtrToStructure(lp, GetType(KBDLLHOOKSTRUCT)), KBDLLHOOKSTRUCT)
' Disabling Windows keys
If objKeyInfo.key = Keys.RWin OrElse objKeyInfo.key = Keys.LWin OrElse objKeyInfo.key = Keys.Tab AndAlso HasAltModifier(objKeyInfo.flags)
OrElse objKeyInfo.key = Keys.Escape AndAlso (Control.ModifierKeys And Keys.Control) = Keys.Control Then
Return New IntPtr(1) ' if 0 is returned then All the above
keys will be enabled
End If
End If
Return CallNextHookEx(ptrHook,
nCode, wp, lp)
End Function
Private Function HasAltModifier(ByVal flags As Integer) As Boolean
Return (flags And &H20) = &H20
End Function
End Class
// كود سي شارب
using System;
using System.Diagnostics;
using
System.Runtime.InteropServices;
using System.Windows.Forms;
class SpecialChars
{
private static SpecialChars Sp;
public static void DisableSpChrs( )
{
Sp = new SpecialChars();
ProcessModule objCurrentModule = Process.GetCurrentProcess().MainModule;
objKeyboardProcess = new LowLevelKeyboardProc(Sp.captureKey);
Sp.ptrHook =
SetWindowsHookEx(13, objKeyboardProcess, GetModuleHandle(objCurrentModule.ModuleName),
0);
}
public static void EnableSpChrs( )
{
UnhookWindowsHookEx(Sp.ptrHook);
}
// Structure contain information about low-level keyboard input event
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public Keys key;
public int scanCode;
public int flags;
public int time;
public IntPtr extra;
}
//System level functions to be used for hook and unhook keyboard
input
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hook);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern short GetAsyncKeyState(Keys key);
//Declaring Global objects
private IntPtr ptrHook;
private static LowLevelKeyboardProc objKeyboardProcess;
private IntPtr captureKey(int nCode, IntPtr wp, IntPtr lp)
{
if (nCode >= 0)
{
KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lp, typeof(KBDLLHOOKSTRUCT));
// Disabling Windows keys
if (objKeyInfo.key == Keys.RWin ||
objKeyInfo.key
== Keys.LWin ||
objKeyInfo.key
== Keys.Tab
&& HasAltModifier(objKeyInfo.flags) ||
objKeyInfo.key
== Keys.Escape
&& (Control.ModifierKeys & Keys.Control) == Keys.Control )
{
return (IntPtr)1; // if 0 is returned then All the above keys will be enabled
}
}
return CallNextHookEx(ptrHook,
nCode, wp, lp);
}
bool HasAltModifier(int flags)
{
return (flags & 0x20) == 0x20;
}
}
وأهم ما يعنينا
في الفئة SpecialChars هو الوسيلة DisableSpChrs التي تقوم
بتعطيل عمل الأزرار الخاصة، والوسيلة EnableSpChrs التي تقوم بإعادة تفعيل عمل
الأزرار الخاصة.. وقد جعلت هاتين الوسيلتين ثابتتين Static (مشتركتين Shared في فيجوال بيزيك) حتى يمكن استدعاؤهما
مباشرة من اسم الفئة بدون الحاجة لتعريف متغير منها.
ولو وضعت مربع
اختيار CheckBox على النموذج
وأسميته chk_DisableSpecialKeys،
فيمكنك أن تستخدم الكود التالي في الحدث CheckedChanged الخاص به لتعطيل وتفعيل عمل
الأزرار الخاصة:
' كود فيجوال بيزيك
If chk_DisableSpecialKeys.Checked Then
SpecialChars.DisableSpChrs( )
Else
SpecialChars.EnableSpChrs( )
End If
// كود سي شارب
if (chk_DisableSpecialKeys.Checked)
SpecialChars.DisableSpChrs( );
else
SpecialChars.EnableSpChrs( );
ما شاء الله تبارك الله
ردحذفجزاكم الله خيراً علي هذا العلم النافع
نفعك الله به في الدنيا والأخرة
أيه أخبار كتب الويب !!
شكرا لتقديرك أ. عبد الرحمن..
ردحذفحاليا أعمل في كتاب عن WPF
ولكن الأحداتث الساخنة في مصر تعطلني عن إنجازه..
بعد ذلك سأكتب عن ASP.NET إن شاء الله
تحياتي