1. 程式人生 > >利用鍵盤鉤子開發按鍵發音程式

利用鍵盤鉤子開發按鍵發音程式

一、前言
一日,看見我媽正在用電腦練習打字,頻頻低頭看鍵盤,我想:要是鍵盤能發音的話,不就可以方便她養成"盲打"的好習慣嗎?光想不做可不行,開始行動(您可千萬別急著去拿工具箱啊^_^)...
按鍵能發音,其關鍵就是讓程式能夠知道當前鍵盤上是哪個鍵被按下,並播放相應的聲音,自己的程式當然不在話下,那麼其它程式當前按下哪個鍵如何得知呢?利用鍵盤鉤子便可以很好地解決。

下載本文的全部原始碼 大小:552K

二、掛鉤(HOOK)的基本原理
WINDOWS呼叫掛接的回撥函式時首先會呼叫位於函式鏈首的函式,我們只要將自己的回撥函式置於鏈首,該回調函式就會首先被呼叫。那麼如何將我們自己的回撥函式置於函式鏈的鏈首呢?函式SetWindowsHookEx()實現的就是該功能。我們首先來看一下SetWindowsHookEx函式的原型:

HHOOK SetWindowsHookEx(
  int idHook,       
  HOOKPROC lpfn,     
  HINSTANCE hMod,    
  DWORD dwThreadId  
); 

第一個引數:指定鉤子的型別,有WH_MOUSE、WH_KEYBOARD等十多種(具體參見MSDN)
第二個引數:標識鉤子函式的入口地址
第三個引數:鉤子函式所在模組的控制代碼;
第四個引數:鉤子相關函式的ID用以指定想讓鉤子去鉤哪個執行緒,為0時則攔截整個系統的訊息。

另外需要注意的是為了捕獲所有事件,掛鉤函式應該放在動態連結庫DLL中。

三、具體實現

理論的話就不多說了,執行VC++6.0,新建一個MFC AppWizard(dll)工程,命名為Hook,使用預設的建立DLL型別的選項,也就是使用共享MFC DLL,點選完成後開始編寫程式碼:

(1)在Hook.h中定義全域性函式

BOOL installhook(); //鉤子安裝函式
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);//掛鉤函式


(2)在Hook.cpp檔案的#endif下新增定義全域性變數Hook的程式碼:

static HHOOK hkb=NULL;
HINSTANCE hins; //鉤子函式所在模組的控制代碼

(3)新增核心程式碼

BOOL installhook()
{
    hkb=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hins,0);
    return TRUE;
}

第一個引數指定鉤子的型別,因為我們只用到鍵盤操作所以設定為WH_KEYBOARD;第二個引數將鉤子函式的入口地址指定為KeyboardProc,當鉤子鉤到任何訊息後便呼叫這個函式,即當不管系統的哪個視窗有鍵盤輸入馬上會引起KeyboardProc的動作;第三個引數是鉤子函式所在模組的控制代碼;最後一個引數是鉤子相關函式的ID用以指定想讓鉤子去鉤哪個執行緒,為0時則攔截整個系統的訊息;
現在,就開始定義當鍵盤上的鍵按下時程式要做什麼了~
KeyboardProc動作:

LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{ 
    if(((DWORD)lParam&0x40000000) && (HC_ACTION==nCode))
    {
       switch(wParam) //鍵盤按鍵標識
        {
        case ''1'':sndPlaySound("1.wav",SND_ASYNC);break; //當數字鍵1被按下
         case ''2'':sndPlaySound("2.wav",SND_ASYNC);break;
        case ''3'':sndPlaySound("3.wav",SND_ASYNC);break;
        case ''4'':sndPlaySound("4.wav",SND_ASYNC);break;
        ....
        case ''A'':sndPlaySound("a.wav",SND_ASYNC);break; //當字母鍵A被按下
         case ''B'':sndPlaySound("b.wav",SND_ASYNC);break;
        case ''C'':sndPlaySound("c.wav",SND_ASYNC);break;
        case ''D'':sndPlaySound("d.wav",SND_ASYNC);break;
        ....
        }
     }
     LRESULT RetVal = CallNextHookEx( hkb, nCode, wParam, lParam ); 
     return RetVal;
}

上面的程式碼中我們用播放聲音做為按鍵被按下後的動作,API函式sndPlaySound的第一個引數定義的聲音檔案的絕對路徑(比如要播放C盤下的a.wav,就定義成"C://a.wav");第二引數定義播放模式,SND_ASYNC模式可以及時地釋放正在播放的聲音檔案,立刻停止當前聲音的播放轉去播放新的聲音,這樣在我們連續擊鍵時就不會有阻塞感了.為了執行sndPlaySound函式,必須在Hook.cpp的檔案頭加上:

 #include "mmsystem.h"

並且點選VC++選單上的“工程”-“設定”進入Link屬性頁,在L物件/庫模組下輸入:winmm.lib後確定即可.

(4)新增輸出標識
在Hook.def的末尾新增

installhook
KeyboardProc

短短的四步,鍵盤鉤子的製作算是完成了,編譯生成後的DLL檔案就可以自由的用別的程式來呼叫了.
在程式中如何呼叫DLL呢?那就簡單了.再用VC++6.0新建一個MFC AppWizard(exe)工程,命名為KeySound,點選"確定"後選擇程式型別為對話方塊,直接點選確定即可.
在KeySoundDlg.cpp檔案中的OnInitDialog()初始化函式的CDialog::OnInitDialog();下面新增:

//阻止程式反覆駐留記憶體,也為了防止有兩個程式同時讀取DLL而發生錯誤.

CreateMutex(NULL, FALSE, "KeySound");
if(GetLastError()==ERROR_ALREADY_EXISTS)
   OnOK();

//讀取DLL
static HINSTANCE hinstDLL; 
typedef BOOL (CALLBACK *inshook)(); 
inshook instkbhook;
if(hinstDLL=LoadLibrary((LPCTSTR)"Hook.dll"))
{
    instkbhook=(inshook)GetProcAddress(hinstDLL,"installhook");  
    instkbhook();
}
else
{
    MessageBox("當前目錄找不到Hook.dll檔案,程式初始化失敗");
    OnOK();
}

將編譯生成後的KeySound.exe和Hook.dll放在同一目錄下,定義好聲音檔案,執行KeySound.exe後開啟記事本或寫字板,體驗一下系統為您即時快速地朗讀您按下的每一個鍵的快感吧^-^

有一點必須說明,標準鍵盤有101個鍵,您想讓多少鍵發聲音,就必須在上面的KeyboardProc動作裡定義多少個鍵,常用的10個數字鍵和26個英文字母不會給您帶來太大的困難,只要相應的''A''對應A鍵,''1''對應1鍵就可以,但如果您希望能讓更多的鍵都有各種特色音樂的話,很可能會遇到一些鍵盤編碼上的麻煩,比如ESC鍵就不能簡單的用''ESC''來搞定了,得用VK_ESCAPE,又比如Alt鍵得用VK_MENU來定義,沒有個鍵盤編碼表的話會令人相當頭疼,這裡我介紹一種讓程式來告訴您鍵盤按鍵名稱的方法:
為一個工程新增PreTranslateMessage對映,新增如下程式碼:

char KeyName[50];
ZeroMemory(KeyName,50);
if(pMsg -> message == WM_KEYDOWN)
{ 
   GetKeyNameText(pMsg->lParam,KeyName,50);
   MessageBox(KeyName);
} 

那麼當程式視窗顯示在面前時按下某個鍵,就會彈出一個訊息顯示該鍵的名稱,然後用''''包起來就可以了,比如逗號句號,就是'',''和''.'',簡單吧:)

到此就全部完成了按鍵發音程式的編寫,通過改變聲音檔案的名稱而不用改動程式本身就可以達到更換按鍵聲音的目的了,只是有個遺憾,聲音檔案在硬碟中的位置不能變更,從C盤換移動D盤程式就不能播放了,怎麼樣才能靈活的讀取聲音檔案呢?可以用API函式GetModuleFileName來得到程式所在的目錄,具體實現方法如下:
(1)在Hook.h的public:下面新增:

BOOL InitInstance(); //初始化函式

(2)在Hook.cpp的#endif下新增定義全域性變數的程式碼:

char szBuf[256];
char *p;
CString msg;

(3)在Hook.cpp中適當位置新增:

BOOL CHookApp::InitInstance ()
{ 
   hins=AfxGetInstanceHandle();
   GetModuleFileName(AfxGetInstanceHandle( ),szBuf,sizeof(szBuf));
   p = szBuf;
   while(strchr(p,''//'')) 
   { 
        p = strchr(p,''//''); 
        p++; 
   }
   *p = ''/0''; 
   msg=szBuf;
   return TRUE; 
}

(4)新建一個資料夾並命名為Sound;

(5)改變聲音檔案物理位置定義方式

case ''1'':sndPlaySound(msg+"sound//1.wav",SND_ASYNC);break; 

msg是得到程式當前所在目錄,加上後面的程式碼就是指播放當前目錄下的Sound目錄裡的1.wav檔案,這樣就將聲音檔案的絕對路徑改成了靈活的相對路徑.您只要把KeySound.exe,Hook.dll和Sound資料夾放在同一個資料夾下,以後只要搬動整個資料夾就能實現聲音檔案的任意移動了。

除錯時需要注意:將Hook.dll、Sound目錄放在KeySound.exe的執行目錄下。假如編譯連結的時候出現unresolved external symbol [email protected] 這樣的資訊,請在Project Settings中加入Winmm.lib 。

相關推薦

利用鍵盤鉤子開發按鍵發音程式

一、前言一日,看見我媽正在用電腦練習打字,頻頻低頭看鍵盤,我想:要是鍵盤能發音的話,不就可以方便她養成"盲打"的好習慣嗎?光想不做可不行,開始行動(您可千萬別急著去拿工具箱啊^_^)...按鍵能發音,其關鍵就是讓程式能夠知道當前鍵盤上是哪個鍵被按下,並播放相應的聲音,自己的程

WPF 利用鍵盤鉤子來捕獲鍵盤,做一些不為人知的事情...完整實例

程序猿 通過 不知道 tro color launcher 聲明 leg win 原文:WPF 利用鍵盤鉤子來捕獲鍵盤,做一些不為人知的事情...完整實例鍵盤鉤子是一種可以監控鍵盤操作的指令。 看到這句話是不是覺得其實鍵盤鉤子可以做很多事情. 場景 當你的程序需

利用鍵盤鉤子捕獲Windows鍵盤動作

轉自:http://www.yesky.com/328/1890328.shtml         引言   在科研生產中對研製、除錯操作的記錄是非常有必要而且是有很重要價值的。通過對記錄資訊的分析,可以在事故發生後準確的分析出事故的起因、操作是否存在失誤等許多重要線索。

C# 在程式焦點之外捕捉按鍵操作(鍵盤鉤子的使用)

在程式中捕捉使用者的按鍵行為很容易,但是假如程式最小化了或者隱藏到系統托盤了,這時因為程式已經失去焦點了我們想捕捉按鍵行為就不是那麼容易了。怎麼辦呢?這就要使用鍵盤鉤子了。 關於鍵盤鉤子,下面這個網友描述的很詳盡,我就不班門弄斧了,轉載過來以備不時之需。原文:http://

按鍵】短按,長按,按鍵釋放,三種模式的按鍵掃描程式(軟體消抖動)--- 矩陣鍵盤

請先閱讀上篇: 短按,長按,按鍵釋放,三種模式的按鍵掃描程式(軟體消抖動) 上面的程式適用於單個按鍵,那是不是也可以適用於矩陣鍵盤呢? 答案是肯定的。 接下來在這裡做一個簡單的擴充套件,具體框架不用改變,所以具體的框架內容和思路在這裡不詳述了,自行參考上篇文章,這裡就說說擴充套件

輸入子系統------鍵盤按鍵驅動程式 13.Linux鍵盤按鍵驅動 (詳解)

由上一節的輸入子系統的框架分析可知,其分三層:裝置驅動層,核心層,事件驅動層 我們在為某種裝置的編寫驅動層,只需要關心裝置驅動層,即如何驅動裝置並獲得硬體資料(如按下的按鍵資料),然後呼叫核心層提供的介面,核心層就會自動把資料提交給事件處理層。在輸入子系統中,事件驅動是標準的,適用於所有輸入類的。

手把手教你用nginx開發自己的伺服器------利用nginx開發一個helloWorld程式(三)

之前兩篇文章已經說明了過程,今天稍微把過程說細一點,畢竟知其然還要知其所以然嘛,整個呼叫的邏輯是怎完整的呢?其實上兩篇文章看似簡單的將nginx處理一個請求的過程說出來了,但實際過程一點也不簡單,一個連線處理的過程,主要是複雜在準備階段(也就是各種回撥函式的掛載,上下文的準備

手把手教你用nginx開發自己的伺服器------利用nginx開發一個helloWorld程式(一)

能開始學習nginx的你,肯定也擼了不少程式碼了,相信你學習程式碼都是從helloWorld開始的,那麼,今天我們就用nginx開發一個helloWorld,我們將要實現的功能就是當瀏覽器來訪問你的伺服器時,你的終端列印一個helloWorld。先別急著開始擼程式碼,先聊一聊

手把手教你用nginx開發自己的伺服器------利用nginx開發一個helloWorld程式(二)

現在我們正式開始編寫nginx的helloWorld功能,該從哪下手呢?別急,我們在上一篇文章中提到了事件驅動對吧。nginx是怎麼樣事件驅動的呢?我們來看看ngx_worker_process_cycle()這個函式的一部分for ( ;; ) { if

利用C#開發web應用程式時,對登錄檔進行操作提示沒有許可權的解決辦法

因為公司專案需要對web程式新增一套限制客戶惡意傳播的方案。沒辦法,東西放在客戶的伺服器或者電腦裡面。鑑於本人菜鳥一個,也就能想到利用兩種方案,具體的實現的方式,將會在之後的博文中寫出。 我寫這篇文章

如何讓你的程式避開全域性鍵盤鉤子的監視

      一直以來有個疑問,就是如果別人在你的電腦上安裝了鍵盤鉤子來監視你的鍵盤按鍵動作,我的程式怎麼才能避開這些全域性鍵盤鉤子(system-wide hook)的監視.正好最近一段時間因為工作關係在研究鉤子,順便研究了一下這個問題,今天算是找到了一個解決辦法.解決辦法:

嵌入式Linux裝置驅動開發之:按鍵驅動程式例項

11.6  按鍵驅動程式例項 11.6.1  按鍵工作原理 高電平和低電平相接怎麼會變成低電平呢 就像你把電源正極的負極相連一樣會把電壓拉低。大電流會從高電平引腳流向低電平引腳,把高電平引腳拉低。 LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部

初識heX,利用javascript開發桌面應用程式

最近看了一個開源的新技術,就是利用js和html開發桌面應用,就像寫web頁面一樣,它就是heX,一個利用Node,js和HTML5來實現開發的技術。 而且維護這個開源技術的竟然是網易有道的一個團隊,真是不錯,這說明國內的技術真是提升了啊,不過目前好像只支援windows

利用SSH框架開發時遇到的各種Bug及解決方法

for manage unmap 獲取 extension cfg.xml request rem soci 1、hibernate自動生成的配置文件 hibernate.cfg.xml 有時候是有問題的,會出現 org.hibernate.HibernateExcept

Modbus庫開發筆記之九:利用協議棧開發Modbus TCP Server應用

數根 網絡 received ant getc multiple 利用 這不 tar 前面我們已經完成了Modbus協議棧的開發,但這不是我們的目的。我們開發它的目的當然是要使用它來解決我們的實際問題。接下來我們就使用剛開發的Modbus協議棧開發一個Modbus TCP

全局鍵盤鉤子

call ++ http void export eof one rtu 資料 記得幾年前學習windows開發的時候,為了一個鍵盤鉤子在網上找了無數的資料也沒幾個能用的。 最近又需要用到全局鍵盤鉤子記錄鍵盤消息,發現一篇不錯的文章。 http://blog.csdn.ne

web移動端和PC端利用chrome同步開發調試

orm 暫時 ng-click flow fill idt style ini nts web移動端和PC端利用chrome同步開發調試

c++中鍵盤的單個按鍵的刷新

字符 控制臺 運行 pac 運行環境 作用 char 鍵盤 spa 運行環境Vs2013 為了實現小遊戲中按鍵的使用。 在遊戲中常常把主要的行為放在while()中所以常用的鍵盤輸入函數就不再使用,因為有其暫停作用。而這在遊戲中是不允許的。 而且遊戲使用的往往是少數幾個

在WPF中快速實現鍵盤鉤子

nag Dll調用 bin != 攔截 class key and view 原文:在WPF中快速實現鍵盤鉤子大部分的時候,當我們需要鍵盤事件的時候,可以通過在主窗口註冊KeyBinding來實現,不過,有的時候我們需要的是全局鍵盤事件,想在任何一個地方都能使用,最開始的時

019-直接利用Socket/TCP開發網絡遊戲二

字符 i++ 字節數組 parameter host lag lse encoding static 今天我們繼續開始學習網絡部分的知識。今天的部分是分為兩個部分的一個數據傳送,一個是MySQL的開頭。廢話不多說我們開始今天的內容。 我們其實知道在vs中是有粘包與分包的機制