本文由xyz8808貢獻
2009-11-26 12:49//定義變量
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0;
HookProc KeyboardHookProcedure;
/*************************
* 聲明API函數
* ***********************/
// 安裝鉤子 (using System.Runtime.InteropServices;)
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int SetWindowsHookEx(int idHook,HookProc lpfn, IntPtr hInstance, int threadId);
// 卸載鉤子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
// 繼續下一個鉤子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得當前線程編號(線程鉤子需要用到)
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
//鉤子子程:就是鉤子所要做的事情
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (nCode >= 0)
{
/****************
//線程鍵盤鉤子判斷是否按下鍵
Keys keyData = (Keys)wParam;
if(lParam.ToInt32() > 0)
{
// 鍵盤按下
}
if(lParam.ToInt32() < 0)
{
// 鍵盤抬起
}
****************/
/****************
//全局鍵盤鉤子判斷是否按下鍵
wParam = = 0x100 // 鍵盤按下
wParam = = 0x101 // 鍵盤抬起
****************/
KeyMSG m = (KeyMSG) Marshal.PtrToStructure(lParam, typeof(KeyMSG));//鍵盤
// 在這里添加你想要做是事情(比如把鍵盤nCode記錄下來,搞個郵件發送程序發到自己的郵箱去)
return 0;//如果返回1,則結束消息,這個消息到此為止,不再傳遞。如果返回0或調用CallNextHookEx函數則消息出了這個鉤子繼續往下傳遞,也就是傳給消息真正的接受者
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
//鍵盤結構
public struct KeyMSG
{
public int vkCode; //鍵值
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
// 安裝鉤子
public void HookStart()
{
if(hKeyboardHook == 0)
{
// 創建HookProc實例
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
// 設置線程鉤子
hKeyboardHook = SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//************************************
//鍵盤線程鉤子
//SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId()); //GetCurrentThreadId()為要監視的線程ID,你完全可以自己寫個方法獲取QQ的線程哦
//鍵盤全局鉤子,需要引用空間(using System.Reflection;)
//SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//
//關于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函數將鉤子加入到鉤子鏈表中,說明一下四個參數:
//idHook 鉤子類型,即確定鉤子監聽何種消息,上面的代碼中設為2,即監聽鍵盤消息并且是線程鉤子,如果是全局鉤子監聽鍵盤消息應設為13,
//線程鉤子監聽鼠標消息設為7,全局鉤子監聽鼠標消息設為14。
//
//lpfn 鉤子子程的地址指針。如果dwThreadId參數為0 或是一個由別的進程創建的線程的標識,lpfn必須指向DLL中的鉤子子程。 除此以外,lpfn可
//以指向當前進程的一段鉤子子程代碼。鉤子函數的入口地址,當鉤子鉤到任何消息后便調用這個函數。
//
//hInstance應用程序實例的句柄。標識包含lpfn所指的子程的DLL。如果threadId 標識當前進程創建的一個線程,而且子程代碼位于當前
//進程,hInstance必須為NULL。可以很簡單的設定其為本應用程序的實例句柄。
//
//threadedId 與安裝的鉤子子程相關聯的線程的標識符。如果為0,鉤子子程與所有的線程關聯,即為全局鉤子。
//************************************
// 如果設置鉤子失敗
if(hKeyboardHook == 0 )
{
HookStop();
throw new Exception("SetWindowsHookEx failed.");
}
}
}
// 卸載鉤子
public void HookStop()
{
bool retKeyboard = true;
if(hKeyboardHook != 0)
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = 0;
}
if (!( retKeyboard))
throw new Exception("UnhookWindowsHookEx failed.");
}
//*****************************
試用了一下,不是很穩定,改回自帶的 KeyUp函數了,記錄以備查
//自定義類型
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
//接口調用
[DllImport( "User32.DLL ")]
public static extern int SendMessage(IntPtr hWnd, uint Msg,int wParam,int lParam);
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport("User32.Dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport( "user32",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(HookType idHook,HOOKPROC lpfn,int hmod,int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode,int wParam,int lParam);
//函數實現
public void SetHook()
{
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
IntPtr ParenthWnd = new IntPtr(0);
bool isPressed = (((lParam & 0x80000000) == 0) && (nCode==0));
CallNextHookEx(m_HookHandle, nCode, wParam, lParam); //先把消息發給系統默認處理隊列
global.lastKeyValue = wParam;
ParenthWnd = FindWindow( null,"零售業務");
switch(wParam)
{
case 112: //修改數量 F1
if(isPressed)
{
inputType = global.ST_NUM_STP1;
doKeyWork();
}
break;
case 113: //備注 F2
if(isPressed)
{
inputType = global.ST_REMARK;
textBoxBZ.Focus();
textBoxBZ.SelectAll();
}
break;
case 114: //摘要 F3
if(isPressed)
{
inputType = global.ST_ABST;
textBoxZY.Focus();
textBoxZY.SelectAll();
}
break;
case 115: //支付 F4
if(isPressed)
if((global.saleList == null) || (global.saleList.Length == 0))
break;
else
{
showProductInfo();
labelCPZS.Text = global.sAllNum.ToString() + " 件";
labelYSJE.Text = global.sAllValue.ToString() + " 元";
labelYHJE.Text = global.sPriValue.ToString() + " 元";
labelSSJE.Text = global.sDueValue.ToString() + " 元";
inputType = global.ST_GATHER_STP1;
textBoxSK.Focus();
textBoxSK.SelectAll();
}
break;
case 116: //輸入產品 F5
if(isPressed)
{
inputType = global.ST_PRODUCT;
textBoxSPMC.Focus();
textBoxSPMC.SelectAll();
}
break;
case 117: //開始新訂單 F6
if(isPressed)
{
resetSale(); //銷售界面復位
}
break;
default:
//CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
break;
}
return(0);
}
///
/// 清理所有正在使用的資源。
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
C#中鍵盤鉤子的使用
默認分類 2008-07-05 17:33 閱讀53 評論0 字號: 大大 中中 小小 public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//在這里放置你的處理代碼 return 0;
}
}
使用方法
可以在Form的構造函數里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();
C#+低級Windows API鉤子攔截鍵盤輸入
www.diybl.com 時間:2010-01-14 作者:匿名 編輯:Smi1e 點擊: 9 [評論]
-
-
摘要 在家里,嬰兒和其它動物可能會重擊你的計算機鍵盤,致使出現各種無法預言的結果。本文中的這個C#示例應用程序將向你展示如何基于Windows鉤子API來實現在擊鍵造成任何危害之前捕獲它們。
一. 簡介
貓和嬰兒有很多共同之處。他們都喜歡吃家中養植的植物,都非常討厭關門。他們也都愛玩弄你的鍵盤,結果是,你正發送給你的老板的電子郵件可能是以半截句子發送出去的,你的Excel帳戶也被加入了一些亂七八糟的內容,并且你還沒有注意到,當打開Windows資源管理器時,若干文件已經被移到了回收站!
其解決方案是,開發一個應用程序實現如下功能:只要鍵盤處于"威脅狀態"你就可以進行切換,并確保任何鍵盤輸入活動都不會造成危害。本文想展示如何使用一種低級Windows API鉤子在一個C#應用程序中實現鍵盤"控制"。下圖是本文示例程序的一個運行快照。
二. 背景
其實,已經存在許多有關于Windows鉤子的文章和示例代碼,并且已經有人編寫過與本文幾乎一樣的C++示例程序。然而,當我搜索相應的C#應用程序的源碼時,卻找到極少的.NET示例,而且沒有一個程序能夠提供一個方便的自包含的C#類。
.NET框架能夠使你以托管方式來存取你最常使用的鍵盤事件(通過KeyPress,KeyUp和KeyDown)。遺憾的是,這些事件都不能被用來停止Windows組合鍵(如Alt+Tab或Windows"開始"鍵),從而允許用戶"遠離"某一個應用程序。
本文的想法在操作系統級上捕獲鍵盤事件而不是通過框架級來實現。為此,應用程序需要使用Windows API函數來把它自身添加到應用程序"鉤子鏈"中以監聽來自操作系統的鍵盤消息。當它收到這種類型的消息時,該應用程序能夠選擇性地傳遞消息,或者進行正常處理,或者"鎮壓"它以便不再有其它應用程序(包括Windows)來影響它。本文正是想解釋其實現機理。
然而,請注意,本文中的代碼僅適用于基于NT版本的Windows(NT,2000和XP),并且無法使用這個方法來停用Ctrl+Alt+Delete。有關于如何實現這一點,你可以參考MSDN有關資料。
三. 使用代碼
為了易于使用,我在本文中提供了兩個獨立的zip文件。一個僅包含KeyboardHook類,這是本文介紹的重點。另一個是一個完整的微軟Visual C# 2005 Express Edition應用程序工程,名叫"Baby Keyboard Bash",它實現顯示擊鍵的名字或彩色的形狀以響應于擊鍵。
四. 實例化類
鍵盤鉤子是通過keyboard.cs中的KeyboardHook類來建立和管理的。這個類實現了IDisposable接口,因此,實例化它的最簡單的方法是在應用程序的Main()方法中使用using關鍵字來封裝Application.Run()調用。這將確保只要該應用程序開始即建立鉤子并且,更重要的是,當該應用程序結束時立即使這個鉤子失效。
這個類引發一個事件來警告應用程序已經有鍵被按下,因此主表單能夠存取在Main()方法中創建的KeyboardHook實例就顯得非常重要;最簡單的方法是把這個實例存儲在一個公共成員變量中。
KeyboardHook提供了三種構造器來啟用或禁用某些設置:
· KeyboardHook():捕獲所有擊鍵,沒有任何內容傳遞到Windows或另外的應用程序。
· KeyboardHook(string param):把參數串轉換為Parameters枚舉中的值之一,然后調用下面的構造器:
· KeyboardHook(KeyboardHook.Parameters enum):根據從Parameters枚舉中選擇的值的不同,分別啟動下列設置:
o Parameters.AllowAltTab:允許用戶使用Alt+Tab切換到另外的應用程序。
o Parameters.AllowWindowsKey:允許用戶使用Ctrl+Esc或一種Windows鍵存取任務欄和開始菜單。
o Parameters.AllowAltTabAndWindows:啟用Alt+Tab,Ctrl+Esc和Windows鍵。
o Parameters.PassAllKeysToNextApp:如果該參數為true,那么所有的擊鍵將被傳遞給任何其它監聽應用程序(包括Windows)。
當擊鍵繼續被鍵盤鉤子捕獲時,啟用Alt+Tab和/或Windows鍵允許實際使用該計算機者切換到另一個應用程序并且使用鼠標與之交互。PassAllKeysToNextApp設置有效地禁用了擊鍵捕獲;這個類也是建立一個低級鍵盤鉤子并且引發它的KeyIntercepted事件,但是它還負責把鍵盤事件傳遞到另一個監聽程序。
因此,實例化該類以捕獲所有擊鍵的方法如下:
public static KeyboardHook kh;
[STAThread]
static void Main()
{
//其它代碼
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}
五. 處理KeyIntercepted事件
當一外鍵被按下時,這個KeyboardHook類激活一個包含一些KeyboardHookEventArgs的KeyIntercepted事件。這是通過一個KeyboardHookEventHandler類型的方法使用以下方式來實現的:
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
這個KeyboardHookEventArgs返回關于被按下鍵的下列信息:
· KeyName:鍵名,通過把捕獲的鍵代碼強制轉換為System.Windows.Forms.Keys而獲得。
· KeyCode:由鍵盤鉤子返回的原來的鍵代碼
· PassThrough:指出是否這個KeyboardHook實例被配置以允許該擊鍵傳遞到其它應用程序。如果你想允許一用戶使用Alt+Tab或 Ctrl+Esc/Windows鍵切換到其它的應用程序的話,那么對之進行檢查是很有用的。
然后,使用一個具有適當簽名的方法來執行擊鍵所調用的任何任務。下面是一個示例片斷:
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
//檢查是否這個鍵擊事件被傳遞到其它應用程序并且停用TopMost,以防他們需要調到前端
if (e.PassThrough)
{
this.TopMost = false;
}
ds.Draw(e.KeyName);
}
本文的剩下部分將解釋低級鍵盤鉤子是如何在KeyboardHook中實現的。
六. 實現一個低級Windows API鍵盤鉤子
在user32.dll中,Windows API包含三個方法來實現此目的:
· SetWindowsHookEx,它負責建立鍵盤鉤子
· UnhookWindowsHookEx,它負責移去鍵盤鉤子
· CallNextHookEx,它負責把擊鍵信息傳遞到下一個監聽鍵盤事件的應用程序
創建一個能夠攔截鍵盤的應用程序的關鍵是,實現前面兩個方法,而"放棄"第三個。結果是,任何擊鍵都只能傳遞到這個應用程序中。
為了實現這一目標,第一步是包括System.Runtime.InteropServices命名空間并且導入API方法,首先是SetWindowsHookEx:
using System.Runtime.InteropServices
……
//在類內部:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
導入UnhookWindowsHookEx和CallNextHookEx的代碼請見后面的討論。
下一步是調用SetWindowsHookEx來建立鉤子,這時需要傳遞下列四個參數:
· idHook:
這個數字決定了要建立的鉤子的類型。例如,SetWindowsHookEx可以被用于鉤住鼠標事件(當然還有其它事件)。在本文情況下,我們僅對13有興趣,這是鍵盤鉤子的id。為了使代碼更易讀些,我們把它賦值給一個常數WH_KEYBOARD_LL。
· Lpfn:
這是一個指向函數的長指針,該函數將負責處理鍵盤事件。在C#中,"指針"是通過傳遞一個代理類型的實例而獲得的,從而使之引用一個適當的方法。這是我們在每次使用鉤子時所調用的方法。
這里值得注意的是,這個代理實例需要被存儲于這個類的一個成員變量中。這是為了防止一旦第一個方法調用結束它會被作為垃圾回收。
· hMod:
建立鉤子的應用程序的一個實例句柄。我找到的絕大多數實例僅把它設置為IntPtr.Zero,理由是不大可能存在該應用程序的多個實例。然而,這部分代碼使用了來自于kernel32.dll的GetModuleHandle來標識準確的實例從而使這個類更具靈活性。
· dwThreadId:
當前進程的id。把它設置為0可以使這個鉤子成為全局構子,這是相應于一個低級鍵盤鉤子的正確設置。
SetWindowsHookEx返回一個鉤子id,這個id將被用于當應用程序結束時從鉤子鏈中脫鉤,因此它需要存儲在一個成員變量中以備將來使用。KeyboardHook類中的相關代碼如下:
private HookHandlerDelegate proc;
private IntPtr hookID = IntPtr.Zero;
private const int WH_KEYBOARD_LL = 13;
public KeyboardHook()
{
proc = new HookHandlerDelegate(HookCallback);
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
hookID = SetWindowsHookEx(WH_KEYBOARD_LL, proc,GetModuleHandle(curModule.ModuleName), 0);
}
}
七. 處理鍵盤事件
如前面所提及,SetWindowsHookEx需要一個到被用來處理鍵盤事件的回調函數的指針。它期望有一個使用如下簽名的函數:
LRESULT CALLBACK LowLevelKeyboardProc( int nCode,WPARAM wParam,LPARAM lParam);
其實,建立一個函數指針的C#方法使用了一個代理,因此,向SetWindowsHookEx指出它需要的內容的第一步是使用正確的簽名來聲明一個代理:
private delegate IntPtr HookHandlerDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
然后,使用相同的簽名編寫一個回調方法;這個方法將包含實際上處理鍵盤事件的所有代碼。在KeyboardHook的情況下,它檢查是否擊鍵應該被傳遞給其它應用程序并且接下來激發KeyIntercepted事件。下面是一個簡化版本的不帶有擊鍵處理代碼的情況:
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
//僅為KeyDown事件過濾wParam,否則該代碼將再次執行-對于每一次擊鍵(也就是,相應于KeyDown和KeyUp)
//WM_SYSKEYDOWN是捕獲Alt相關組合鍵所必需的
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
//激發事件
OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
//返回一個"啞"值以捕獲擊鍵
return (System.IntPtr)1;
}
//事件沒有被處理,把它傳遞給下一個應用程序
return CallNextHookEx(hookID, nCode, wParam, ref lParam);
}
接下來,一個到HookCallback的參考被指派給HookHandlerDelegate的一個實例并且被傳遞到SetWindowsHookEx的調用,正如前一節所展示的。
無論何時一個鍵盤事件發生,下列參數將被傳遞給HookCallBack:
· nCode:
根據MSDN文檔,回調函數應該返回CallNextHookEx的結果,如果這個值小于零的話。正常的鍵盤事件將返回一個大于或等于零的nCode值。
· wParam:
這個值指示發生了什么類型的事件:鍵被按下還是松開,以及是否按下的鍵是一個系統鍵(左邊或右邊的Alt鍵)。
· lParam:
這是一個存儲精確擊鍵信息的結構,例如被按鍵的代碼。在KeyboardHook中聲明的這個結構如下:
private struct KBDLLHOOKSTRUCT
{
public int vkCode;
int scanCode;
public int flags;
int time;
int dwExtraInfo;
}
其中的這兩個公共參數是在KeyboardHook中的回調方法所使用的僅有的兩個參數。vkCoke返回虛擬鍵代碼,它能夠被強制轉換為System.Windows.Forms.Keys以獲得鍵名,而flags顯示是否這是一個擴展鍵(例如,Windows Start鍵)或是否同時按下了Alt鍵。有關于Hook回調方法的完整代碼展示在每一種情況下要檢查哪些flags值。
如果flags提供的信息和KBDLLHOOKSTRUCT的其它組成元素不需要,那么這個回調方法和代碼的簽名可以按如下進行修改:
private delegate IntPtr HookHandlerDelegate(
int nCode, IntPtr wParam, IntPtr lParam);
在這種情況中,lParam將僅返回vkCode。
八. 把擊鍵傳遞到下一個應用程序
一個良好的鍵盤鉤子回調方法應該以調用CallNextHookEx函數并且返回它的結果結束。這可以確保其它應用程序能夠有機會處理針對于它們的擊鍵。
然而,KeyboardHook類的主要功能在于,阻止擊鍵被傳播到任何其它更多的應用程序。因此它無論在何時處理一次擊鍵,HookCallback都將返回一個啞值:
return (System.IntPtr)1;
另一方面,它確實調用CallNextHookEx-如果它不處理該事件,或如果重載的構造器中的使用KeyboardHook傳遞的參數允許某些組合鍵通過。
CallNextHookEx被啟用-通過從user32.dll導入該函數,如下列代碼所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, ref KeyInfoStruct lParam);
然后,被導入的方法被HookCallMethod所調用,這可以確保所有的通過鉤子接收到的參數被繼續傳遞到下一個應用程序中:
CallNextHookEx(hookID, nCode, wParam, ref lParam);
如前面所提及,如果在lParam中的flags是不相關的,那么可以修改導入的CallNextHookEx的簽名以把lParam定義為System.IntPtr。
九. 移去鉤子
處理鉤子的最后一步是使用從user32.dll中導入的UnhookWindowsHookEx函數移去它(當破壞KeyboardHook類的實例時),如下所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
既然KeyboardHook實現IDisposable,那么這可以在Dispose方法中完成。
public void Dispose()
{
UnhookWindowsHookEx(hookID);
}
hookID是構造器在調用SetWindowsHookEx所返回的id。這將從鉤子鏈中刪除應用程序。
c#鍵盤鉤子
作者: lzh 類別: C#/VB 日期: 2003-5-27 22:22:19
You can use system hook to hook any messages send to the application.
Hooking the keyboard (WH_KEYBOARD) and mouse (WH_MOUSE) messages can be
used to determine if the user interactive with the computer. You also can
hook all the messages (WH_GETMESSAGE). The hook can be set with this code:
public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_GETMESSAGE = 3
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_GETMESSAGE,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//Perform your process
return 0;
}
}
Then you can install the hook procedure by the following code:
Win32Hook hook = new Win32Hook();
hook.SetHook();
用鉤子(hook)實現C#的屏幕鍵盤效果
-
SVN技術網 www.svn8.com 2010-01-20 08:38:02 來源:bbs.svn8.com 作者:佚名 點擊:158次
文章摘要:用鉤子(hook)實現C#的屏幕鍵盤效果 要實現一個屏幕鍵盤,需要監聽所有鍵盤事件,無論窗體是否被激活。因此需要一個全局的鉤子,也就 是系統范圍的鉤子。 什么是鉤子(Hook) 鉤子(Hook)是Windows提供的一種消息處理機制平臺,是指在程序正常運行中接受信息之前預先啟動的函數,用來檢查和修改傳給該程序的信息,(鉤子)
-
使用完鉤子后,要進行卸載,這個可以寫在析構函數中。
12 public void Stop() {3 this.Stop(true, true, true);4 }5 6 public void Stop(bool uninstallMouseHook, bool uninstallKeyboardHook, 7 bool throwExceptions) {8 // if mouse hook set and must be uninstalled9 if (hMouseHook != IntPtr.Zero && uninstallMouseHook) {10 // uninstall hook11 bool retMouse = UnhookWindowsHookEx(hMouseHook);12 // reset invalid handle13 hMouseHook = IntPtr.Zero;14 // if failed and exception must be thrown15 if (retMouse == false && throwExceptions) {16 // Returns the error code returned by the last unmanaged function 17 // called using platform invoke that has the DllImportAttribute.18 // SetLastError flag set. 19 int errorCode = Marshal.GetLastWin32Error();20 // Initializes and throws a new instance of the Win32Exception class 21 // with the specified error. 22 throw new Win32Exception(errorCode);23 }24 }2526 // if keyboard hook set and must be uninstalled27 if (hKeyboardHook != IntPtr.Zero && uninstallKeyboardHook) {28 // uninstall hook29 bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook);30 // reset invalid handle31 hKeyboardHook = IntPtr.Zero;32 // if failed and exception must be thrown33 if (retKeyboard == false && throwExceptions) {34 // Returns the error code returned by the last unmanaged function 35 // called using platform invoke that has the DllImportAttribute.36 // SetLastError flag set. 37 int errorCode = Marshal.GetLastWin32Error();38 // Initializes and throws a new instance of the Win32Exception class 39 // with the specified error. 40 throw new Win32Exception(errorCode);41 }42 }43 }44
將這個文件編譯成一個dll,即可在應用程序中調用。通過它提供的事件,便可監聽所有的鍵盤事件。
但是,這只能監聽鍵盤事件,沒有鍵盤的情況下,怎么會有鍵盤事件?其實很簡單,通過SendInput
API函數提供虛擬鍵盤代碼的調用即可模擬鍵盤輸入。下面的代碼模擬一個 KeyDown 和 KeyUp 過程,
把他們連接起來就是一次按鍵過程。
1 private void SendKeyDown(short key) {2 Input[] input = new Input[1];3 input[0].type = INPUT.KEYBOARD;4 input[0].ki.wVk = key;5 input[0].ki.time = NativeMethods.GetTickCount();67 if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0])) 8 < input.Length) {9 throw new Win32Exception(Marshal.GetLastWin32Error());10 }11 }1213 private void SendKeyUp(short key) {14 Input[] input = new Input[1];15 input[0].type = INPUT.KEYBOARD;16 input[0].ki.wVk = key;17 input[0].ki.dwFlags = KeyboardConstaint.KEYEVENTF_KEYUP;18 input[0].ki.time = NativeMethods.GetTickCount();1920 if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0]))21 < input.Length) {22 throw new Win32Exception(Marshal.GetLastWin32Error());23 }24 }
自己實現一個 KeyBoardButton 控件用作按鈕,用 Visual Studio 或者 SharpDevelop 為屏幕鍵盤設計 UI,然后
在這些 Button 的 Click 事件里面模擬一個按鍵過程。
12 private void ButtonOnClick(object sender, EventArgs e) {3 KeyboardButton btnKey = sender as KeyboardButton;4 if (btnKey == null) {5 return;6 }78 SendKeyCommand(btnKey);9 }10 11 private void SendKeyCommand(KeyboardButton keyButton) {12 short key = keyButton.VKCode;13 if (combinationVKButtonsMap.ContainsKey(key)) {14 if (keyButton.Checked) {15 SendKeyUp(key);16 } else {17 SendKeyDown(key);18 }19 } else {20 SendKeyDown(key);21 SendKeyUp(key);22 }23 }
其中 combinationVKButtonsMap 是一個 IDictionary>, key 存儲的是VK_SHIFT, VK_CONTROL 等組合鍵的鍵盤碼。左右兩個按鈕對應同一個鍵盤碼,因此需要放在一個 List 里。
標準鍵盤上的每一個鍵都有虛擬鍵碼( VK_CODE)與之對應。還有一些其他的常量,
把它寫在一個靜態 class 里吧。
1 // KeyboardConstaint.cs2 internal static class KeyboardConstaint {3 internal static readonly short VK_F1 = 0x70;4 internal static readonly short VK_F2 = 0x71;5 internal static readonly short VK_F3 = 0x72;6 internal static readonly short VK_F4 = 0x73;7 internal static readonly short VK_F5 = 0x74;8 internal static readonly short VK_F6 = 0x75;9 internal static readonly short VK_F7 = 0x76;10 internal static readonly short VK_F8 = 0x77;11 internal static readonly short VK_F9 = 0x78;12 internal static readonly short VK_F10 = 0x79;13 internal static readonly short VK_F11 = 0x7A;14 internal static readonly short VK_F12 = 0x7B;1516 internal static readonly short VK_LEFT = 0x25;17 internal static readonly short VK_UP = 0x26;18 internal static readonly short VK_RIGHT = 0x27;19 internal static readonly short VK_DOWN = 0x28;2021 internal static readonly short VK_NONE = 0x00;22 internal static readonly short VK_ESCAPE = 0x1B;23 internal static readonly short VK_EXECUTE = 0x2B;24 internal static readonly short VK_CANCEL = 0x03;25 internal static readonly short VK_RETURN = 0x0D;26 internal static readonly short VK_ACCEPT = 0x1E;27 internal static readonly short VK_BACK = 0x08;28 internal static readonly short VK_TAB = 0x09;29 internal static readonly short VK_DELETE = 0x2E;30 internal static readonly short VK_CAPITAL = 0x14;31 internal static readonly short VK_NUMLOCK = 0x90;32 internal static readonly short VK_SPACE = 0x20;33 internal static readonly short VK_DECIMAL = 0x6E;34 internal static readonly short VK_SUBTRACT = 0x6D;3536 internal static readonly short VK_ADD = 0x6B;37 internal static readonly short VK_DIVIDE = 0x6F;38 internal static readonly short VK_MULTIPLY = 0x6A;39 internal static readonly short VK_INSERT = 0x2D;4041 internal static readonly short VK_OEM_1 = 0xBA; // ';:' for US42 internal static readonly short VK_OEM_PLUS = 0xBB; // '+' any country4344 internal static readonly short VK_OEM_MINUS = 0xBD; // '-' any country4546 internal static readonly short VK_OEM_2 = 0xBF; // '/?' for US47 internal static readonly short VK_OEM_3 = 0xC0; // '`~' for US48 internal static readonly short VK_OEM_4 = 0xDB; // '[{' for US49 internal static readonly short VK_OEM_5 = 0xDC; // '\|' for US50 internal static readonly short VK_OEM_6 = 0xDD; // ']}' for US51 internal static readonly short VK_OEM_7 = 0xDE; // ''"' for US52 internal static readonly short VK_OEM_PERIOD = 0xBE; // '.>' any country53 internal static readonly short VK_OEM_COMMA = 0xBC; // ',<' any country54 internal static readonly short VK_SHIFT = 0x10;55 internal static readonly short VK_CONTROL = 0x11;56 internal static readonly short VK_MENU = 0x12;57 internal static readonly short VK_LWIN = 0x5B;58 internal static readonly short VK_RWIN = 0x5C;59 internal static readonly short VK_APPS = 0x5D;6061 internal static readonly short VK_LSHIFT = 0xA0;62 internal static readonly short VK_RSHIFT = 0xA1;63 internal static readonly short VK_LCONTROL = 0xA2;64 internal static readonly short VK_RCONTROL = 0xA3;65 internal static readonly short VK_LMENU = 0xA4;66 internal static readonly short VK_RMENU = 0xA5;6768 internal static readonly short VK_SNAPSHOT = 0x2C;69 internal static readonly short VK_SCROLL = 0x91;70 internal static readonly short VK_PAUSE = 0x13;71 internal static readonly short VK_HOME = 0x24;7273 internal static readonly short VK_NEXT = 0x22;74 internal static readonly short VK_PRIOR = 0x21;75 internal static readonly short VK_END = 0x23;7677 internal static readonly short VK_NUMPAD0 = 0x60;78 internal static readonly short VK_NUMPAD1 = 0x61;79 internal static readonly short VK_NUMPAD2 = 0x62;80 internal static readonly short VK_NUMPAD3 = 0x63;81 internal static readonly short VK_NUMPAD4 = 0x64;82 internal static readonly short VK_NUMPAD5 = 0x65;83 internal static readonly short VK_NUMPAD5NOTHING = 0x0C;84 internal static readonly short VK_NUMPAD6 = 0x66;85 internal static readonly short VK_NUMPAD7 = 0x67;86 internal static readonly short VK_NUMPAD8 = 0x68;87 internal static readonly short VK_NUMPAD9 = 0x69;8889 internal static readonly short KEYEVENTF_EXTENDEDKEY = 0x0001;90 internal static readonly short KEYEVENTF_KEYUP = 0x0002;9192 internal static readonly int GWL_EXSTYLE = -20;93 internal static readonly int WS_DISABLED = 0X8000000;94 internal static readonly int WM_SETFOCUS = 0X0007;95 }
文章來自[SVN中文技術網]轉發請保留本站地址:http://www.svn8.com/dotnet/Csharp/2010012018328_2.html
C#中鍵盤鉤子的使用(轉)
public class Win32Hook
{
[DllImport("kernel32")]
public static extern int GetCurrentThreadId();
[DllImport( "user32",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HOOKPROC lpfn,
int hmod,
int dwThreadId);
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
public void SetHook()
{
// set the keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD,
new HOOKPROC(this.MyKeyboardProc),
0,
GetCurrentThreadId());
}
public int MyKeyboardProc(int nCode, int wParam, int lParam)
{
//在這里放置你的處理代碼 return 0;
}
}
使用方法
可以在Form的構造函數里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();