六月婷婷综合激情-六月婷婷综合-六月婷婷在线观看-六月婷婷在线-亚洲黄色在线网站-亚洲黄色在线观看网站

明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

使用鍵盤鉤子開發(fā)按鍵發(fā)音程序

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

下載本文的全部源代碼 大小:552K

二、掛鉤(HOOK)的基本原理
WINDOWS調(diào)用掛接的回調(diào)函數(shù)時首先會調(diào)用位于函數(shù)鏈首的函數(shù),我們只要將自己的回調(diào)函數(shù)置于鏈首,該回調(diào)函數(shù)就會首先被調(diào)用。那么如何將我們自己的回調(diào)函數(shù)置于函數(shù)鏈的鏈首呢?函數(shù)SetWindowsHookEx()實現(xiàn)的就是該功能。我們首先來看一下SetWindowsHookEx函數(shù)的原型:


HHOOK SetWindowsHookEx(
int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
第一個參數(shù):指定鉤子的類型,有WH_MOUSE、WH_KEYBOARD等十多種(具體參見MSDN)
第二個參數(shù):標識鉤子函數(shù)的入口地址
第三個參數(shù):鉤子函數(shù)所在模塊的句柄;
第四個參數(shù):鉤子相關函數(shù)的ID用以指定想讓鉤子去鉤哪個線程,為0時則攔截整個系統(tǒng)的消息。

另外需要注意的是為了捕獲所有事件,掛鉤函數(shù)應該放在動態(tài)鏈接庫DLL中。

三、具體實現(xiàn)
理論的話就不多說了,運行VC++6.0,新建一個MFC AppWizard(dll)工程,命名為Hook,使用默認的創(chuàng)建DLL類型的選項,也就是使用共享MFC DLL,點擊完成后開始編寫代碼:

(1)在Hook.h中定義全局函數(shù)
BOOL installhook(); //鉤子安裝函數(shù)
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);//掛鉤函數(shù)

(2)在Hook.cpp文件的#endif下添加定義全局變量Hook的代碼:
static HHOOK hkb=NULL;
HINSTANCE hins; //鉤子函數(shù)所在模塊的句柄
(3)添加核心代碼
BOOL installhook()
{
hkb=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hins,0);
return TRUE;
}
第一個參數(shù)指定鉤子的類型,因為我們只用到鍵盤操作所以設定為WH_KEYBOARD;第二個參數(shù)將鉤子函數(shù)的入口地址指定為KeyboardProc,當鉤子鉤到任何消息后便調(diào)用這個函數(shù),即當不管系統(tǒng)的哪個窗口有鍵盤輸入馬上會引起KeyboardProc的動作;第三個參數(shù)是鉤子函數(shù)所在模塊的句柄;最后一個參數(shù)是鉤子相關函數(shù)的ID用以指定想讓鉤子去鉤哪個線程,為0時則攔截整個系統(tǒng)的消息;
現(xiàn)在,就開始定義當鍵盤上的鍵按下時程序要做什么了~
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; //當數(shù)字鍵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函數(shù)sndPlaySound的第一個參數(shù)定義的聲音文件的絕對路徑(比如要播放C盤下的a.wav,就定義成"C:\\a.wav");第二參數(shù)定義播放模式,SND_ASYNC模式可以及時地釋放正在播放的聲音文件,立刻停止當前聲音的播放轉去播放新的聲音,這樣在我們連續(xù)擊鍵時就不會有阻塞感了.

(4)添加輸出標識
在Hook.def的末尾添加
installhook
KeyboardProc
短短的四步,鍵盤鉤子的制作算是完成了,編譯生成后的DLL文件就可以自由的用別的程序來調(diào)用了.
在程序中如何調(diào)用DLL呢?那就簡單了.再用VC++6.0新建一個MFC AppWizard(exe)工程,命名為KeySound,點擊"確定"后選擇程序類型為對話框,直接點擊確定即可.
在KeySoundDlg.cpp文件中的OnInitDialog()初始化函數(shù)的CDialog::OnInitDialog();下面添加:

//阻止程序反復駐留內(nèi)存,也為了防止有兩個程序同時讀取DLL而發(fā)生錯誤.


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后打開記事本或?qū)懽职澹w驗一下系統(tǒng)為您即時快速地朗讀您按下的每一個鍵的快感吧^-^

有一點必須說明,標準鍵盤有101個鍵,您想讓多少鍵發(fā)聲音,就必須在上面的KeyboardProc動作里定義多少個鍵,常用的10個數(shù)字鍵和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);
}
那么當程序窗口顯示在面前時按下某個鍵,就會彈出一個消息顯示該鍵的名稱,然后用''包起來就可以了,比如逗號句號,就是','和'.',簡單吧:)
到此就全部完成了按鍵發(fā)音程序的編寫,通過改變聲音文件的名稱而不用改動程序本身就可以達到更換按鍵聲音的目的了,只是有個遺憾,聲音文件在硬盤中的位置不能變更,從C盤換移動D盤程序就不能播放了,怎么樣才能靈活的讀取聲音文件呢?可以用API函數(shù)GetModuleFileName來得到程序所在的目錄,具體實現(xiàn)方法如下:
(1)在Hook.h的public:下面添加:


BOOL InitInstance(); //初始化函數(shù)
(2)在Hook.cpp的#endif下添加定義全局變量的代碼:


char szBuf[256];
char *p;
CString msg;
(3)在Hook.cpp中適當位置添加:


BOOL CHookApp::InitInstance ()
{
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文件夾放在同一個文件夾下,以后只要搬動整個文件夾就能實現(xiàn)聲音文件的任意移動了。

調(diào)試時需要注意:將Hook.dll、Sound目錄放在KeySound.exe的執(zhí)行目錄下。假如編譯鏈接的時候出現(xiàn)unresolved external symbol __imp__sndPlaySoundA@8 這樣的信息,請在Project Settings中加入Winmm.lib 。 


主站蜘蛛池模板: 夜夜夜夜夜夜夜工噜噜噜 | 亚洲天堂高清 | 色综合久久久高清综合久久久 | 青草视频网站在线观看 | 日韩中文字幕在线有码视频网 | 人碰人操| 欧美性乱 | 日韩爱 | 日本免费完整版观看 | 日本a在线天堂 | 天天躁日日躁狠狠躁一级毛片 | 亚洲综合精品一二三区在线 | 日日噜噜噜噜人人爽亚洲精品 | 亚洲日本天堂在线 | 最新国产在线 | 色偷偷亚洲| 日本在线视频免费 | 午夜三级黄色片 | 亚洲四虎在线 | 天天色天天插 | 日本一区二区视频免费播放 | 青草视频在线观看完整版 | 日韩一级在线播放免费观看 | 青草青青产国视频在线 | 亚洲成av人片在线观看天堂无码 | 婷婷丁香在线视频 | 中文字幕精品在线视频 | 在线久综合色手机在线播放 | 欧美在线观看一区二区 | 五月婷婷丁香久久 | 欧美综合图区亚欧综合图区 | 欧美午夜一区二区福利视频 | 日本成人片在线观看 | 中文字幕天堂最新版在线网 | 四虎看片| 四虎精品成人免费观看 | 亚洲另类电击调教在线观看 | 天天搞天天 | 日本免费高清 | 日韩精品欧美亚洲高清有无 | 中日韩欧美风情视频 |