المتابعون للمدونة

الاثنين، 7 يناير 2013

هل يمكن كتابة كود لتعطيل بعض الأزرار الخاصة عن العمل؟


س: هل يمكن كتابة كود لتعطيل بعض الأزرار الخاصة عن العمل مثل زر الويندوز و 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( );

هناك تعليقان (2):

  1. ما شاء الله تبارك الله
    جزاكم الله خيراً علي هذا العلم النافع
    نفعك الله به في الدنيا والأخرة
    أيه أخبار كتب الويب !!

    ردحذف
  2. شكرا لتقديرك أ. عبد الرحمن..
    حاليا أعمل في كتاب عن WPF
    ولكن الأحداتث الساخنة في مصر تعطلني عن إنجازه..
    بعد ذلك سأكتب عن ASP.NET إن شاء الله
    تحياتي

    ردحذف

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.

صفحة الشاعر