本文由xyz8808貢獻(xiàn)
2009-11-26 12:49//定義變量
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
static int hKeyboardHook = 0;
HookProc KeyboardHookProcedure;
/*************************
* 聲明API函數(shù)
* ***********************/
// 安裝鉤子 (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);
// 繼續(xù)下一個(gè)鉤子
[DllImport("user32.dll",CharSet=CharSet.Auto, CallingC.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
// 取得當(dāng)前線程編號(hào)(線程鉤子需要用到)
[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記錄下來(lái),搞個(gè)郵件發(fā)送程序發(fā)到自己的郵箱去)
return 0;//如果返回1,則結(jié)束消息,這個(gè)消息到此為止,不再傳遞。如果返回0或調(diào)用CallNextHookEx函數(shù)則消息出了這個(gè)鉤子繼續(xù)往下傳遞,也就是傳給消息真正的接受者
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
//鍵盤結(jié)構(gòu)
public struct KeyMSG
{
public int vkCode; //鍵值
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
// 安裝鉤子
public void HookStart()
{
if(hKeyboardHook == 0)
{
// 創(chuàng)建HookProc實(shí)例
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
// 設(shè)置線程鉤子
hKeyboardHook = SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//************************************
//鍵盤線程鉤子
//SetWindowsHookEx( 2,KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId()); //GetCurrentThreadId()為要監(jiān)視的線程ID,你完全可以自己寫個(gè)方法獲取QQ的線程哦
//鍵盤全局鉤子,需要引用空間(using System.Reflection;)
//SetWindowsHookEx( 13,KeyboardHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);
//
//關(guān)于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函數(shù)將鉤子加入到鉤子鏈表中,說(shuō)明一下四個(gè)參數(shù):
//idHook 鉤子類型,即確定鉤子監(jiān)聽(tīng)何種消息,上面的代碼中設(shè)為2,即監(jiān)聽(tīng)鍵盤消息并且是線程鉤子,如果是全局鉤子監(jiān)聽(tīng)鍵盤消息應(yīng)設(shè)為13,
//線程鉤子監(jiān)聽(tīng)鼠標(biāo)消息設(shè)為7,全局鉤子監(jiān)聽(tīng)鼠標(biāo)消息設(shè)為14。
//
//lpfn 鉤子子程的地址指針。如果dwThreadId參數(shù)為0 或是一個(gè)由別的進(jìn)程創(chuàng)建的線程的標(biāo)識(shí),lpfn必須指向DLL中的鉤子子程。 除此以外,lpfn可
//以指向當(dāng)前進(jìn)程的一段鉤子子程代碼。鉤子函數(shù)的入口地址,當(dāng)鉤子鉤到任何消息后便調(diào)用這個(gè)函數(shù)。
//
//hInstance應(yīng)用程序?qū)嵗木浔?biāo)識(shí)包含lpfn所指的子程的DLL。如果threadId 標(biāo)識(shí)當(dāng)前進(jìn)程創(chuàng)建的一個(gè)線程,而且子程代碼位于當(dāng)前
//進(jìn)程,hInstance必須為NULL。可以很簡(jiǎn)單的設(shè)定其為本應(yīng)用程序的實(shí)例句柄。
//
//threadedId 與安裝的鉤子子程相關(guān)聯(lián)的線程的標(biāo)識(shí)符。如果為0,鉤子子程與所有的線程關(guān)聯(lián),即為全局鉤子。
//************************************
// 如果設(shè)置鉤子失敗
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.");
}
//*****************************
試用了一下,不是很穩(wěn)定,改回自帶的 KeyUp函數(shù)了,記錄以備查
//自定義類型
public enum HookType
{
WH_KEYBOARD = 2
}
public delegate int HOOKPROC(int nCode, int wParam, int lParam);
//接口調(diào)用
[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);
//函數(shù)實(shí)現(xiàn)
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); //先把消息發(fā)給系統(tǒng)默認(rèn)處理隊(duì)列
global.lastKeyValue = wParam;
ParenthWnd = FindWindow( null,"零售業(yè)務(wù)");
switch(wParam)
{
case 112: //修改數(shù)量 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: //輸入產(chǎn)品 F5
if(isPressed)
{
inputType = global.ST_PRODUCT;
textBoxSPMC.Focus();
textBoxSPMC.SelectAll();
}
break;
case 117: //開(kāi)始新訂單 F6
if(isPressed)
{
resetSale(); //銷售界面復(fù)位
}
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#中鍵盤鉤子的使用
默認(rèn)分類 2008-07-05 17:33 閱讀53 評(píng)論0 字號(hào): 大大 中中 小小 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的構(gòu)造函數(shù)里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();
C#+低級(jí)Windows API鉤子攔截鍵盤輸入
www.diybl.com 時(shí)間:2010-01-14 作者:匿名 編輯:Smi1e 點(diǎn)擊: 9 [評(píng)論]
-
-
摘要 在家里,嬰兒和其它動(dòng)物可能會(huì)重?fù)裟愕挠?jì)算機(jī)鍵盤,致使出現(xiàn)各種無(wú)法預(yù)言的結(jié)果。本文中的這個(gè)C#示例應(yīng)用程序?qū)⑾蚰阏故救绾位赪indows鉤子API來(lái)實(shí)現(xiàn)在擊鍵造成任何危害之前捕獲它們。
一. 簡(jiǎn)介
貓和嬰兒有很多共同之處。他們都喜歡吃家中養(yǎng)植的植物,都非常討厭關(guān)門。他們也都愛(ài)玩弄你的鍵盤,結(jié)果是,你正發(fā)送給你的老板的電子郵件可能是以半截句子發(fā)送出去的,你的Excel帳戶也被加入了一些亂七八糟的內(nèi)容,并且你還沒(méi)有注意到,當(dāng)打開(kāi)Windows資源管理器時(shí),若干文件已經(jīng)被移到了回收站!
其解決方案是,開(kāi)發(fā)一個(gè)應(yīng)用程序?qū)崿F(xiàn)如下功能:只要鍵盤處于"威脅狀態(tài)"你就可以進(jìn)行切換,并確保任何鍵盤輸入活動(dòng)都不會(huì)造成危害。本文想展示如何使用一種低級(jí)Windows API鉤子在一個(gè)C#應(yīng)用程序中實(shí)現(xiàn)鍵盤"控制"。下圖是本文示例程序的一個(gè)運(yùn)行快照。
二. 背景
其實(shí),已經(jīng)存在許多有關(guān)于Windows鉤子的文章和示例代碼,并且已經(jīng)有人編寫過(guò)與本文幾乎一樣的C++示例程序。然而,當(dāng)我搜索相應(yīng)的C#應(yīng)用程序的源碼時(shí),卻找到極少的.NET示例,而且沒(méi)有一個(gè)程序能夠提供一個(gè)方便的自包含的C#類。
.NET框架能夠使你以托管方式來(lái)存取你最常使用的鍵盤事件(通過(guò)KeyPress,KeyUp和KeyDown)。遺憾的是,這些事件都不能被用來(lái)停止Windows組合鍵(如Alt+Tab或Windows"開(kāi)始"鍵),從而允許用戶"遠(yuǎn)離"某一個(gè)應(yīng)用程序。
本文的想法在操作系統(tǒng)級(jí)上捕獲鍵盤事件而不是通過(guò)框架級(jí)來(lái)實(shí)現(xiàn)。為此,應(yīng)用程序需要使用Windows API函數(shù)來(lái)把它自身添加到應(yīng)用程序"鉤子鏈"中以監(jiān)聽(tīng)來(lái)自操作系統(tǒng)的鍵盤消息。當(dāng)它收到這種類型的消息時(shí),該應(yīng)用程序能夠選擇性地傳遞消息,或者進(jìn)行正常處理,或者"鎮(zhèn)壓"它以便不再有其它應(yīng)用程序(包括Windows)來(lái)影響它。本文正是想解釋其實(shí)現(xiàn)機(jī)理。
然而,請(qǐng)注意,本文中的代碼僅適用于基于NT版本的Windows(NT,2000和XP),并且無(wú)法使用這個(gè)方法來(lái)停用Ctrl+Alt+Delete。有關(guān)于如何實(shí)現(xiàn)這一點(diǎn),你可以參考MSDN有關(guān)資料。
三. 使用代碼
為了易于使用,我在本文中提供了兩個(gè)獨(dú)立的zip文件。一個(gè)僅包含KeyboardHook類,這是本文介紹的重點(diǎn)。另一個(gè)是一個(gè)完整的微軟Visual C# 2005 Express Edition應(yīng)用程序工程,名叫"Baby Keyboard Bash",它實(shí)現(xiàn)顯示擊鍵的名字或彩色的形狀以響應(yīng)于擊鍵。
四. 實(shí)例化類
鍵盤鉤子是通過(guò)keyboard.cs中的KeyboardHook類來(lái)建立和管理的。這個(gè)類實(shí)現(xiàn)了IDisposable接口,因此,實(shí)例化它的最簡(jiǎn)單的方法是在應(yīng)用程序的Main()方法中使用using關(guān)鍵字來(lái)封裝Application.Run()調(diào)用。這將確保只要該應(yīng)用程序開(kāi)始即建立鉤子并且,更重要的是,當(dāng)該應(yīng)用程序結(jié)束時(shí)立即使這個(gè)鉤子失效。
這個(gè)類引發(fā)一個(gè)事件來(lái)警告應(yīng)用程序已經(jīng)有鍵被按下,因此主表單能夠存取在Main()方法中創(chuàng)建的KeyboardHook實(shí)例就顯得非常重要;最簡(jiǎn)單的方法是把這個(gè)實(shí)例存儲(chǔ)在一個(gè)公共成員變量中。
KeyboardHook提供了三種構(gòu)造器來(lái)啟用或禁用某些設(shè)置:
· KeyboardHook():捕獲所有擊鍵,沒(méi)有任何內(nèi)容傳遞到Windows或另外的應(yīng)用程序。
· KeyboardHook(string param):把參數(shù)串轉(zhuǎn)換為Parameters枚舉中的值之一,然后調(diào)用下面的構(gòu)造器:
· KeyboardHook(KeyboardHook.Parameters enum):根據(jù)從Parameters枚舉中選擇的值的不同,分別啟動(dòng)下列設(shè)置:
o Parameters.AllowAltTab:允許用戶使用Alt+Tab切換到另外的應(yīng)用程序。
o Parameters.AllowWindowsKey:允許用戶使用Ctrl+Esc或一種Windows鍵存取任務(wù)欄和開(kāi)始菜單。
o Parameters.AllowAltTabAndWindows:?jiǎn)⒂肁lt+Tab,Ctrl+Esc和Windows鍵。
o Parameters.PassAllKeysToNextApp:如果該參數(shù)為true,那么所有的擊鍵將被傳遞給任何其它監(jiān)聽(tīng)?wèi)?yīng)用程序(包括Windows)。
當(dāng)擊鍵繼續(xù)被鍵盤鉤子捕獲時(shí),啟用Alt+Tab和/或Windows鍵允許實(shí)際使用該計(jì)算機(jī)者切換到另一個(gè)應(yīng)用程序并且使用鼠標(biāo)與之交互。PassAllKeysToNextApp設(shè)置有效地禁用了擊鍵捕獲;這個(gè)類也是建立一個(gè)低級(jí)鍵盤鉤子并且引發(fā)它的KeyIntercepted事件,但是它還負(fù)責(zé)把鍵盤事件傳遞到另一個(gè)監(jiān)聽(tīng)程序。
因此,實(shí)例化該類以捕獲所有擊鍵的方法如下:
public static KeyboardHook kh;
[STAThread]
static void Main()
{
//其它代碼
using (kh = new KeyboardHook())
{
Application.Run(new Form1());
}
五. 處理KeyIntercepted事件
當(dāng)一外鍵被按下時(shí),這個(gè)KeyboardHook類激活一個(gè)包含一些KeyboardHookEventArgs的KeyIntercepted事件。這是通過(guò)一個(gè)KeyboardHookEventHandler類型的方法使用以下方式來(lái)實(shí)現(xiàn)的:
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
這個(gè)KeyboardHookEventArgs返回關(guān)于被按下鍵的下列信息:
· KeyName:鍵名,通過(guò)把捕獲的鍵代碼強(qiáng)制轉(zhuǎn)換為System.Windows.Forms.Keys而獲得。
· KeyCode:由鍵盤鉤子返回的原來(lái)的鍵代碼
· PassThrough:指出是否這個(gè)KeyboardHook實(shí)例被配置以允許該擊鍵傳遞到其它應(yīng)用程序。如果你想允許一用戶使用Alt+Tab或 Ctrl+Esc/Windows鍵切換到其它的應(yīng)用程序的話,那么對(duì)之進(jìn)行檢查是很有用的。
然后,使用一個(gè)具有適當(dāng)簽名的方法來(lái)執(zhí)行擊鍵所調(diào)用的任何任務(wù)。下面是一個(gè)示例片斷:
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
//檢查是否這個(gè)鍵擊事件被傳遞到其它應(yīng)用程序并且停用TopMost,以防他們需要調(diào)到前端
if (e.PassThrough)
{
this.TopMost = false;
}
ds.Draw(e.KeyName);
}
本文的剩下部分將解釋低級(jí)鍵盤鉤子是如何在KeyboardHook中實(shí)現(xiàn)的。
六. 實(shí)現(xiàn)一個(gè)低級(jí)Windows API鍵盤鉤子
在user32.dll中,Windows API包含三個(gè)方法來(lái)實(shí)現(xiàn)此目的:
· SetWindowsHookEx,它負(fù)責(zé)建立鍵盤鉤子
· UnhookWindowsHookEx,它負(fù)責(zé)移去鍵盤鉤子
· CallNextHookEx,它負(fù)責(zé)把擊鍵信息傳遞到下一個(gè)監(jiān)聽(tīng)鍵盤事件的應(yīng)用程序
創(chuàng)建一個(gè)能夠攔截鍵盤的應(yīng)用程序的關(guān)鍵是,實(shí)現(xiàn)前面兩個(gè)方法,而"放棄"第三個(gè)。結(jié)果是,任何擊鍵都只能傳遞到這個(gè)應(yīng)用程序中。
為了實(shí)現(xiàn)這一目標(biāo),第一步是包括System.Runtime.InteropServices命名空間并且導(dǎo)入API方法,首先是SetWindowsHookEx:
using System.Runtime.InteropServices
……
//在類內(nèi)部:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
導(dǎo)入U(xiǎn)nhookWindowsHookEx和CallNextHookEx的代碼請(qǐng)見(jiàn)后面的討論。
下一步是調(diào)用SetWindowsHookEx來(lái)建立鉤子,這時(shí)需要傳遞下列四個(gè)參數(shù):
· idHook:
這個(gè)數(shù)字決定了要建立的鉤子的類型。例如,SetWindowsHookEx可以被用于鉤住鼠標(biāo)事件(當(dāng)然還有其它事件)。在本文情況下,我們僅對(duì)13有興趣,這是鍵盤鉤子的id。為了使代碼更易讀些,我們把它賦值給一個(gè)常數(shù)WH_KEYBOARD_LL。
· Lpfn:
這是一個(gè)指向函數(shù)的長(zhǎng)指針,該函數(shù)將負(fù)責(zé)處理鍵盤事件。在C#中,"指針"是通過(guò)傳遞一個(gè)代理類型的實(shí)例而獲得的,從而使之引用一個(gè)適當(dāng)?shù)姆椒ā_@是我們?cè)诿看问褂勉^子時(shí)所調(diào)用的方法。
這里值得注意的是,這個(gè)代理實(shí)例需要被存儲(chǔ)于這個(gè)類的一個(gè)成員變量中。這是為了防止一旦第一個(gè)方法調(diào)用結(jié)束它會(huì)被作為垃圾回收。
· hMod:
建立鉤子的應(yīng)用程序的一個(gè)實(shí)例句柄。我找到的絕大多數(shù)實(shí)例僅把它設(shè)置為IntPtr.Zero,理由是不大可能存在該應(yīng)用程序的多個(gè)實(shí)例。然而,這部分代碼使用了來(lái)自于kernel32.dll的GetModuleHandle來(lái)標(biāo)識(shí)準(zhǔn)確的實(shí)例從而使這個(gè)類更具靈活性。
· dwThreadId:
當(dāng)前進(jìn)程的id。把它設(shè)置為0可以使這個(gè)鉤子成為全局構(gòu)子,這是相應(yīng)于一個(gè)低級(jí)鍵盤鉤子的正確設(shè)置。
SetWindowsHookEx返回一個(gè)鉤子id,這個(gè)id將被用于當(dāng)應(yīng)用程序結(jié)束時(shí)從鉤子鏈中脫鉤,因此它需要存儲(chǔ)在一個(gè)成員變量中以備將來(lái)使用。KeyboardHook類中的相關(guān)代碼如下:
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需要一個(gè)到被用來(lái)處理鍵盤事件的回調(diào)函數(shù)的指針。它期望有一個(gè)使用如下簽名的函數(shù):
LRESULT CALLBACK LowLevelKeyboardProc( int nCode,WPARAM wParam,LPARAM lParam);
其實(shí),建立一個(gè)函數(shù)指針的C#方法使用了一個(gè)代理,因此,向SetWindowsHookEx指出它需要的內(nèi)容的第一步是使用正確的簽名來(lái)聲明一個(gè)代理:
private delegate IntPtr HookHandlerDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
然后,使用相同的簽名編寫一個(gè)回調(diào)方法;這個(gè)方法將包含實(shí)際上處理鍵盤事件的所有代碼。在KeyboardHook的情況下,它檢查是否擊鍵應(yīng)該被傳遞給其它應(yīng)用程序并且接下來(lái)激發(fā)KeyIntercepted事件。下面是一個(gè)簡(jiǎn)化版本的不帶有擊鍵處理代碼的情況:
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
//僅為KeyDown事件過(guò)濾wParam,否則該代碼將再次執(zhí)行-對(duì)于每一次擊鍵(也就是,相應(yīng)于KeyDown和KeyUp)
//WM_SYSKEYDOWN是捕獲Alt相關(guān)組合鍵所必需的
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
//激發(fā)事件
OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
//返回一個(gè)"啞"值以捕獲擊鍵
return (System.IntPtr)1;
}
//事件沒(méi)有被處理,把它傳遞給下一個(gè)應(yīng)用程序
return CallNextHookEx(hookID, nCode, wParam, ref lParam);
}
接下來(lái),一個(gè)到HookCallback的參考被指派給HookHandlerDelegate的一個(gè)實(shí)例并且被傳遞到SetWindowsHookEx的調(diào)用,正如前一節(jié)所展示的。
無(wú)論何時(shí)一個(gè)鍵盤事件發(fā)生,下列參數(shù)將被傳遞給HookCallBack:
· nCode:
根據(jù)MSDN文檔,回調(diào)函數(shù)應(yīng)該返回CallNextHookEx的結(jié)果,如果這個(gè)值小于零的話。正常的鍵盤事件將返回一個(gè)大于或等于零的nCode值。
· wParam:
這個(gè)值指示發(fā)生了什么類型的事件:鍵被按下還是松開(kāi),以及是否按下的鍵是一個(gè)系統(tǒng)鍵(左邊或右邊的Alt鍵)。
· lParam:
這是一個(gè)存儲(chǔ)精確擊鍵信息的結(jié)構(gòu),例如被按鍵的代碼。在KeyboardHook中聲明的這個(gè)結(jié)構(gòu)如下:
private struct KBDLLHOOKSTRUCT
{
public int vkCode;
int scanCode;
public int flags;
int time;
int dwExtraInfo;
}
其中的這兩個(gè)公共參數(shù)是在KeyboardHook中的回調(diào)方法所使用的僅有的兩個(gè)參數(shù)。vkCoke返回虛擬鍵代碼,它能夠被強(qiáng)制轉(zhuǎn)換為System.Windows.Forms.Keys以獲得鍵名,而flags顯示是否這是一個(gè)擴(kuò)展鍵(例如,Windows Start鍵)或是否同時(shí)按下了Alt鍵。有關(guān)于Hook回調(diào)方法的完整代碼展示在每一種情況下要檢查哪些flags值。
如果flags提供的信息和KBDLLHOOKSTRUCT的其它組成元素不需要,那么這個(gè)回調(diào)方法和代碼的簽名可以按如下進(jìn)行修改:
private delegate IntPtr HookHandlerDelegate(
int nCode, IntPtr wParam, IntPtr lParam);
在這種情況中,lParam將僅返回vkCode。
八. 把擊鍵傳遞到下一個(gè)應(yīng)用程序
一個(gè)良好的鍵盤鉤子回調(diào)方法應(yīng)該以調(diào)用CallNextHookEx函數(shù)并且返回它的結(jié)果結(jié)束。這可以確保其它應(yīng)用程序能夠有機(jī)會(huì)處理針對(duì)于它們的擊鍵。
然而,KeyboardHook類的主要功能在于,阻止擊鍵被傳播到任何其它更多的應(yīng)用程序。因此它無(wú)論在何時(shí)處理一次擊鍵,HookCallback都將返回一個(gè)啞值:
return (System.IntPtr)1;
另一方面,它確實(shí)調(diào)用CallNextHookEx-如果它不處理該事件,或如果重載的構(gòu)造器中的使用KeyboardHook傳遞的參數(shù)允許某些組合鍵通過(guò)。
CallNextHookEx被啟用-通過(guò)從user32.dll導(dǎo)入該函數(shù),如下列代碼所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, ref KeyInfoStruct lParam);
然后,被導(dǎo)入的方法被HookCallMethod所調(diào)用,這可以確保所有的通過(guò)鉤子接收到的參數(shù)被繼續(xù)傳遞到下一個(gè)應(yīng)用程序中:
CallNextHookEx(hookID, nCode, wParam, ref lParam);
如前面所提及,如果在lParam中的flags是不相關(guān)的,那么可以修改導(dǎo)入的CallNextHookEx的簽名以把lParam定義為System.IntPtr。
九. 移去鉤子
處理鉤子的最后一步是使用從user32.dll中導(dǎo)入的UnhookWindowsHookEx函數(shù)移去它(當(dāng)破壞KeyboardHook類的實(shí)例時(shí)),如下所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
既然KeyboardHook實(shí)現(xiàn)IDisposable,那么這可以在Dispose方法中完成。
public void Dispose()
{
UnhookWindowsHookEx(hookID);
}
hookID是構(gòu)造器在調(diào)用SetWindowsHookEx所返回的id。這將從鉤子鏈中刪除應(yīng)用程序。
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)實(shí)現(xiàn)C#的屏幕鍵盤效果
-
SVN技術(shù)網(wǎng) www.svn8.com 2010-01-20 08:38:02 來(lái)源:bbs.svn8.com 作者:佚名 點(diǎn)擊:158次
文章摘要:用鉤子(hook)實(shí)現(xiàn)C#的屏幕鍵盤效果 要實(shí)現(xiàn)一個(gè)屏幕鍵盤,需要監(jiān)聽(tīng)所有鍵盤事件,無(wú)論窗體是否被激活。因此需要一個(gè)全局的鉤子,也就 是系統(tǒng)范圍的鉤子。 什么是鉤子(Hook) 鉤子(Hook)是Windows提供的一種消息處理機(jī)制平臺(tái),是指在程序正常運(yùn)行中接受信息之前預(yù)先啟動(dòng)的函數(shù),用來(lái)檢查和修改傳給該程序的信息,(鉤子)
-
使用完鉤子后,要進(jìn)行卸載,這個(gè)可以寫在析構(gòu)函數(shù)中。
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
將這個(gè)文件編譯成一個(gè)dll,即可在應(yīng)用程序中調(diào)用。通過(guò)它提供的事件,便可監(jiān)聽(tīng)所有的鍵盤事件。
但是,這只能監(jiān)聽(tīng)鍵盤事件,沒(méi)有鍵盤的情況下,怎么會(huì)有鍵盤事件?其實(shí)很簡(jiǎn)單,通過(guò)SendInput
API函數(shù)提供虛擬鍵盤代碼的調(diào)用即可模擬鍵盤輸入。下面的代碼模擬一個(gè) KeyDown 和 KeyUp 過(guò)程,
把他們連接起來(lái)就是一次按鍵過(guò)程。
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 }
自己實(shí)現(xiàn)一個(gè) KeyBoardButton 控件用作按鈕,用 Visual Studio 或者 SharpDevelop 為屏幕鍵盤設(shè)計(jì) UI,然后
在這些 Button 的 Click 事件里面模擬一個(gè)按鍵過(guò)程。
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 是一個(gè) IDictionary>, key 存儲(chǔ)的是VK_SHIFT, VK_CONTROL 等組合鍵的鍵盤碼。左右兩個(gè)按鈕對(duì)應(yīng)同一個(gè)鍵盤碼,因此需要放在一個(gè) List 里。
標(biāo)準(zhǔn)鍵盤上的每一個(gè)鍵都有虛擬鍵碼( VK_CODE)與之對(duì)應(yīng)。還有一些其他的常量,
把它寫在一個(gè)靜態(tài) 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 }
文章來(lái)自[SVN中文技術(shù)網(wǎng)]轉(zhuǎn)發(fā)請(qǐng)保留本站地址:http://www.svn8.com/dotnet/Csharp/2010012018328_2.html
C#中鍵盤鉤子的使用(轉(zhuǎn))
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的構(gòu)造函數(shù)里放入
Win32Hook hook = new Win32Hook();
hook.SetHook();