C#遮蔽系統熱鍵Ctrl+Alt+Delete的程式碼嘗試。
最近在做一款小軟體,需要鎖定使用者的輸入,包括系統熱鍵(Ctrl+Alt+Delete),在網路中尋找良久,發現這些不錯的程式碼。
經過仔細整理,現將這些程式碼公佈,有興趣的拿去用用。
蒐集的過程有些艱辛,從VB6,到VB.NET,到我熟悉的C#。
一、思路分析。
1. 在窗體類Form中,過載訊息迴圈WndProc來實現功能按鍵的遮蔽
protected override void WndProc(ref Message m)
{
//ToDo:根據m.Msg來處理你要的按鍵
base.WndProc(ref m);
}
優點:可以遮蔽大部分按鍵。使用簡單。
缺點:不能註冊全域性熱鍵,對系統級的熱鍵無效,比如:a. Ctrl+Esc, b. Ctrl+Shift+Esc,
c. Alt+Tab, d. Alt+Esc, e. Ctrl+Alt+Delete
類似的方法有System.Windows.Forms.Control的KeyPress,KeyDown事件,
2.利用鉤子技術,這個可以捕獲大多數鍵盤訊息,但對Ctrl+Alt+Delete仍然無效。
3.最後發現一份VB6寫的程式碼,提到這個方案:
鎖定 Ctrl+Alt+Del 使用遠端執行緒、程式碼注入及子類化技術。
普通鍵盤訊息,使用普通鉤子技術”
雖然這些我都還不太懂,但總算把程式碼移植到了.NET環境下,功能是實現了。
二、程式碼分析
第一步:包裝Win32 API
User32.dll,
1 [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] 2 publicstaticextern IntPtr SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr pInstance, int threadId); 3 4 [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] 5 publicstaticexternbool UnhookWindowsHookEx(IntPtr pHookHandle);6 7 [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] 8 publicstaticexternint CallNextHookEx(IntPtr pHookHandle, int nCode, int wParam, IntPtr lParam); 9 10 [DllImport("user32.dll")] 11 publicstaticexternbool BlockInput(bool fBlockIt);
Kernel32.dll,
1 [DllImport("kernel32.dll")] 2 publicstaticextern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, 3 [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 4 int dwProcessId); 5 [DllImport("kernel32.dll", SetLastError =true)] 6 publicstaticexternbool ReadProcessMemory( 7 IntPtr hProcess, 8 IntPtr lpBaseAddress, 9 [Out] byte[] lpBuffer, 10 int dwSize, 11 outint lpNumberOfBytesRead 12 ); 13 [DllImport("kernel32.dll", SetLastError =true)] 14 publicstaticexternbool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, uint[] lpBuffer, uint nSize, outint lpNumberOfBytesWritten); 15 16 17 [DllImport("kernel32.dll", SetLastError =true, CharSet = CharSet.Auto)] 18 publicstaticexternushort GlobalAddAtom(string lpString); 19 [DllImport("kernel32.dll", SetLastError =true, ExactSpelling =true)] 20 publicstaticexternushort GlobalDeleteAtom(ushort nAtom); 21 [DllImport("kernel32.dll", SetLastError =true, CharSet = CharSet.Auto, EntryPoint ="GlobalFindAtomW")] 22 publicstaticexternushort GlobalFindAtom(string lpString); 23 24 25 [DllImport("kernel32.dll", SetLastError =true)] 26 publicstaticextern IntPtr CreateToolhelp32Snapshot(SnapshotFlags dwFlags, uint th32ProcessID); 27 [DllImport("kernel32.dll", EntryPoint ="Process32FirstW")] 28 publicstaticexternbool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe); 29 [DllImport("kernel32.dll", EntryPoint ="Process32NextW")] 30 publicstaticexternbool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe); 31 32 33 [DllImport("kernel32.dll", SetLastError =true)] 34 [return: MarshalAs(UnmanagedType.Bool)] 35 publicstaticexternbool CloseHandle(IntPtr hObject); 36 [DllImport("kernel32.dll")] 37 publicstaticextern IntPtr GetCurrentProcess(); 38 39 40 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 41 publicstaticextern IntPtr GetModuleHandle(string lpModuleName); 42 [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling =true, SetLastError =true)] 43 publicstaticextern UIntPtr GetProcAddress(IntPtr hModule, string procName); 44 [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError =true, ExactSpelling =true)] 45 publicstaticextern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect); 46 [DllImport("kernel32.dll", SetLastError =true, ExactSpelling =true)] 47 publicstaticexternbool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType); 48 49 50 [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError =true, ExactSpelling =true)] 51 publicstaticextern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, int lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId); 52 [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError =true, ExactSpelling =true)] 53 publicstaticextern UInt32 WaitForSingleObject(IntPtr hHandle, Int32 dwMilliseconds); 54 [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError =true, ExactSpelling =true)] 55 publicstaticexternbool GetExitCodeThread(IntPtr hThread, outint lpExitCode); 56 57 [DllImport("Kernel32.dll", EntryPoint ="RtlMoveMemory", SetLastError =false)] 58 publicstaticexternvoid MoveMemory(object dest, IntPtr src, int size);
advapi32.dll
View Code第二步,建立一個輔助類,HookHelper
以下是兩個公開的方法:
View Code第三,提供一下原始碼的地址
總結:第一次發部落格,希望大家支援。
------------------------------《《《《《《《《《
2011年08月09日,程式出錯的BUG修正,感謝朋友的指出。
------------------------------《《《《《《《《《
錯誤描述:
在多次切換鎖定和解鎖功能,並狂按Alt鍵時程式會關閉,經除錯,出現如下錯誤:
檢測到 CallbackOnCollectedDelegate
Message: 對“Demo!SomeNamespace.SomeClass+SomeDelegate::Invoke”型別的已垃圾回收委託進行了回撥。這可能會導致應用程式崩潰、損壞和資料丟失。向非託管程式碼傳遞委託時,託管應用程式必須讓這些委託保持活動狀態,直到確信不會再次呼叫它們。
錯誤原因:
如果非託管程式碼需要多次呼叫託管程式碼中的回撥,請將委託的引用儲存為成員變數。否則會出現類似上面的異常:
。。。。
如果不用成員變數,而用區域性變數引用被new出來的委託,那麼非託管程式碼可能剛開始的幾次回撥是OK的,但是接下來就會出現上面所說的異常,
原因就在於GC將區域性變數和區域性變數引用的委託物件都銷燬了,非託管程式碼再去訪問那個函式指標時發現指標指向的地址已經無效。
解決辦法:
//1.把委託拿到外面定義private HookProc m_HookProc; //2.在建構函式裡賦值這個委託{ m_HookProc =new HookProc(LowLevelKeyboardProc); } //3.在呼叫方法中使用這個定義過的委託。{ //安裝鉤子 m_lHookID = Win32Lib.SetWindowsHookEx( (int)HookType.WH_KEYBOARD_LL, m_HookProc, pInstance, 0); //這裡註釋掉的是以前的程式碼 //m_lHookID = Win32Lib.SetWindowsHookEx( // (int)HookType.WH_KEYBOARD_LL, // new HookProc(LowLevelKeyboardProc), // pInstance, // 0);}
再次感謝大家的支援。
http://www.cnblogs.com/robnetcn/archive/2011/08/05/LockSysKey.html