計算機是如何管理自身所存放著(zhù)大量的信息的呢? windows 的磁盤(pán)管理程序為我們提供了一套嚴密而又高效的信息組織開(kāi)工 --- 硬盤(pán)上的信息是以文件的形式被管理的。
面向存儲的文件技術(shù)
什么是文件? 計算機中,一篇文章、一幅圖畫(huà)、一個(gè)程序、一首歌曲等都是以文件的形式存儲在磁盤(pán)上的,每個(gè)文件都有一個(gè)文件名。計算機就是對文件按名存取的。文件名的格式如下:主文件名 . 擴展名
文件名由主文件名和擴展名兩部分組成,中間用小圓點(diǎn)隔開(kāi),其中擴展名可以省略。而擴展名是用來(lái)區分文件類(lèi)型的。 Windows 為了區分文件的類(lèi)型,一些軟件系統會(huì )自動(dòng)給文件加上“ .wps ”擴展名;畫(huà)圖程序畫(huà)的圖像文件一般為“ .bmp ”等。
在 windows 中,主文件名可以由英文字符、漢字、數字以及一些符號等組成,但不能使用 +<>*?\ 等符號。
什么是文件夾?
在計算機中存放著(zhù)數以萬(wàn)計的文件,為了便于管理這些文件,就像我們把文件分類(lèi)放到不同的抽屜中便于查閱一樣,在計算機中也有像抽屜的東西,它就是文件夾。文件夾也要有一個(gè)名字,取名的原則與文件的取名類(lèi)似,只是不用再區分文件夾的類(lèi)型,當文件夾多了以后,還可以把某些文件夾歸到一個(gè)大文件夾中去。久而久之,就構成了計算機中龐大的磁盤(pán)文件結構。
為什么要在程序中使用文件?
通常,程序中的數據在程序運行結束后,就會(huì )從內存中清除,再次運行程序時(shí)不會(huì )自動(dòng)出現。在編制程序的過(guò)程中不可避免地會(huì )遇到將某些數據永久保存的問(wèn)題,當關(guān)閉程序后,依然可以使用這些數據,這時(shí)就需要進(jìn)行文件操作。
文件類(lèi)型
Visual c++ 處理的文件通常分為兩種:
文本文件: 只可被任意文本編輯器讀取 ASCII 文本。
二進(jìn)制文件: 指對包含任意格式或無(wú)格式數據的文件的統稱(chēng)。
這里只介紹文本文件的讀寫(xiě), INI 文件也屬于文本文件的范疇,且 INI 文件的結構和用途與普通的文本文件不同,所以會(huì )單獨介紹。
第一部分:文本文件
文本文件的讀寫(xiě)
認識 CFile 類(lèi);認識文本文件;能夠正確靈活應用文本文件存取信息;避免文本文件讀寫(xiě)的常見(jiàn)誤區。
CFile 是 MFC 的文件操作基本類(lèi),它直接支持無(wú)緩沖的二進(jìn)制磁盤(pán) I/O 操作,并通過(guò)其派生類(lèi)支持文本文件、內存文件和 socket 文件。
客戶(hù)操作記錄實(shí)例功能預覽及關(guān)鍵知識點(diǎn)
許多系統,出于安全或其他原因,常常要求隨時(shí)對鍵盤(pán)進(jìn)行監控,利用 Hook (鉤子)技術(shù)編寫(xiě)的應用程序能夠很好地達到這個(gè)目的。本軟件就制作了一個(gè)客戶(hù)操作記錄軟件,即在軟件運行過(guò)程中,用戶(hù)在鍵盤(pán)上的按鍵操作會(huì )被記錄下來(lái),這樣對維護軟件的正常運行非常有利。
只要啟動(dòng)客戶(hù)操作記錄軟件后,不管輸入焦點(diǎn)是否在本軟件上,按鍵都會(huì )被記錄下來(lái)。我們需要的是鍵盤(pán)的系統監控,只要本軟件在運行,無(wú)論當前計算機在做什么,都能監測到用戶(hù)按鍵的行為并做出反應,這就要用到 Hook 技術(shù)。
Hook 技術(shù)在很多特殊軟件中廣泛應用,如,金山詞霸的“取詞”功能,就用到了 Hook 計技術(shù)。
鉤子的本質(zhì)是一段用以處理系統消息的程序,通過(guò)系統調用,將其掛入系統。鉤子的種類(lèi)很多,每種鉤子可以截獲并處理相應的消息,每當特定的消息發(fā)出,在到達目的窗口之前,鉤子程序先行截獲該消息、得到對此消息的控制權。此時(shí)在鉤子函數中就可以對截獲的消息進(jìn)行加工處理,甚至可以強制結束消息的傳遞。
從鉤子的本質(zhì)來(lái)看,可以?xún)?yōu)先截獲操作系統的各種消息進(jìn)行處理,所以它幾乎無(wú)所不能,因為 windows 的應用程序都是基于消息驅動(dòng)的,應用程序的操作都依賴(lài)于它所得到的消息的類(lèi)型及內容。
如果 Hook 過(guò)程在應用程序中實(shí)現,若應用程序不是當前窗口時(shí),該 Hook 就補齊作用;如果 Hook 在 DLL 中實(shí)現,程序在運行中動(dòng)態(tài)調用它,它能實(shí)時(shí)對系統進(jìn)行監控。根據需要,我們采用的是在 DLL 中實(shí)現 Hook 的方式。
(應用程序 exe? 和 DLL 的區別所在)
文本文件存儲原理
字符被計算機處理時(shí)都是以二進(jìn)制代碼的形式出現的,即一個(gè)字符對應一個(gè) 8 位二進(jìn)制數,這種二進(jìn)制碼的集合就是所謂的 ASCII 碼。
基本的 ASCII 碼有 128 個(gè),最高位都是 0 ,對應的十進(jìn)制數是 0-127 。鍵盤(pán)上的字符,如英文字母、數字和一些常用符號,使用基本 ASCII 部分。如數字“ 0 ”的 ASCII 碼用二進(jìn)制數表示就是 00110000 (即十進(jìn)制數 48 )。
擴展的 ASCII 碼有 128 個(gè),最高位是 1 ,對應的十進(jìn)制數是 128-255 。一些制表符和其他符號使用擴展的 ASCII 碼部分。
為解決漢字的存儲和顯示問(wèn)題,我國制定了國際 GB2312 。據此規定,一個(gè)漢字由 2 個(gè)擴展的 ASCII 碼組成,這種高位為 1 的雙字節漢字編碼就是漢字的機內碼,簡(jiǎn)稱(chēng)為內碼。例如,漢字“學(xué)”的機內碼用二進(jìn)制數表示就是 11010001 10100111 (即十進(jìn)制數 206 和 167 ),用十進(jìn)制表示就是 53671 ( 206*256+167 )。對于字符,文本文件存儲的是它的 ASCII 碼,對于漢字,文本文件存儲的是它的內碼,即兩位 ASCII 碼,如字符串“ 0 學(xué) 0 ”,在文本文件中存儲的內容是 00110000 11010001 10100111 00110000
正確的文本文件讀寫(xiě)過(guò)程
1. 定義文件變量; 2. 打開(kāi)指定的文件; 3. 向從文本文件中寫(xiě)入信息; 4. 從文本文件中讀取信息; 5. 關(guān)閉文件
下面具體介紹如何實(shí)現這些過(guò)程。
1. 定義文件變量
定義文件變量格式: CStdioFile 文件變量;
例如,定義一個(gè)名稱(chēng)為 f1 的文件變量,語(yǔ)句如下: CStdioFile f1;
2. 打開(kāi)指定文件
可以直接通過(guò) CStdioFile 的構造函數來(lái)打開(kāi)磁盤(pán)文件,同時(shí)可以用標志位指定打開(kāi)方式(只讀、只寫(xiě)、讀寫(xiě)等):
CStdioFile(LPCTSTR lpszFileName,UINT nOpenFlags);
其中, lpszFileName 表示要打開(kāi)的文件名,可以是相對路徑或絕對路徑
nOpenFlags 設置文件打開(kāi)方式標志位,可以指定用“ | ”連接多個(gè)標志位。下面是常用的打開(kāi)標志:
CFile::typeText :以文本文件的形式打開(kāi)文件
CFile::typeBinary :以二進(jìn)制文件的形式打開(kāi)文件
CFile::modeCreate :如果指定文件名的文件不存在,則新建文件;如果文件存在并且沒(méi)有設置 CFile::modeNoTruncate 標志,則清空文件。
CFile::modeNoTruncate :如果文件存在,不把它的長(cháng)度刪除為 0 (即不清空文件中的數據)。
CFile::modeRead :以只讀方式打開(kāi)文件
CFile::modeReadWrite :以可讀可寫(xiě)方式打開(kāi)文件
CFile::modeWrite :以只寫(xiě)方式打開(kāi)文件
CFile::shareDenyNone :文件打開(kāi)后,不禁止其他進(jìn)程對文件的讀寫(xiě)操作
CFile::shareExclusive :文件打開(kāi)后,禁止其他進(jìn)程對文件的讀寫(xiě)操作
CFile::shareDenyRead :文件打開(kāi)后,禁止其他進(jìn)程對文件的讀操作
CFile::shareDenyWrite :文件打開(kāi)后,禁止其他進(jìn)程對文件的寫(xiě)操作
此外,可以不在構造函數中打開(kāi)文件,而僅僅調用空的構造函數 CStidoFile (),然后用 CStdioFile::Open() 打開(kāi)文件。 Open 函數的前兩個(gè)參數和非空構造函數的參數相同,其聲明如下:
BOOL Open(LPCTSTR lpszFileName,UINT nOpenFlags,CFileException* pError=NULL);
第 3 個(gè)參數與打開(kāi)失敗時(shí)的異常處理有關(guān)。
實(shí)例 1 :以只讀方式打開(kāi)一個(gè)文件
步驟:
使用 AppWizard 創(chuàng )建一個(gè)對話(huà)框應用程序,刪除其自動(dòng)產(chǎn)生的所有控件,添加一個(gè) Button 控件。雙擊控件,在相應的函數里添加代碼:
char * pszFileName="C:\\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::typeText|CFile::modeRead),&fileException)
{
TRACE("Can't open file %s, error = %u\n",pszFileName,fileException.m_cause);
}
運行結果:如果 C:\ 下沒(méi)有 myfile.txt 文件,則新生成該文件。
3. 向從文本文件中寫(xiě)入信息
CStdioFile 提供了函數 WriteString 來(lái)向文本文件中寫(xiě)入文本, WriteString 函數的格式如下:
void WriteString(LPCTSTR lpsz);
WriteString 的參數 lpsz 是一個(gè)以 ”\0” 字符結束的字符串,要把這個(gè)字符串的內容寫(xiě)入文件。
提示 :使用 WriteString 函數時(shí),如果希望每執行一次 WriteString ,文本文件中的內容就會(huì )自動(dòng)換行一次,那么就需要在需要換行的地方輸出“ \n ”:
myFile.WriteString(“ 第 1 行 \n”) ;
實(shí)例 2 :向文件中寫(xiě)入文本
建立 MFC 基于對話(huà)框的程序,刪除自動(dòng)添加的所有控件,添加一個(gè)“確定”按鈕,雙擊按鈕,按默認添加事件函數,雙擊按鈕,在相應的函數處添加如下代碼:
char* pszFileName="C:\\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(pszFileName,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException)
{
myFile.WriteString(" 第 1 行 \n");
CString strOrder;
strOrder.Format("%d,%.3f",66,88.88);
myFile.WriteString(strOrder);
}
else
{
TRACE("Can't open file %s,error=%u\n",pszFileName,fileException.m_cause);
}
程序運行結果: C:\myfile.txt 文件中內容如下:
第 1 行
66,88.880
4. 從文本文件中讀取信息
CStidoFile 提供了函數 ReadString 來(lái)讀取文本, ReadString 有兩種形式,一種為:
virtual LPTSTR ReadString(LPTSTR lpsz, UINIT nMax);
ReadString 函數的參數如下:
lpsz :是用戶(hù)提供的一個(gè)指向字符串的指針,它用來(lái)接受從文件讀出的文本,以 ”\0” 結束。
nMax 是本次所允許讀入的文本字符個(gè)數,不計“ \0” 字符,也就是說(shuō)最多能讀入 nMax-1 個(gè)文本字符。
ReadString 的返回值是一個(gè) LPTSTR 類(lèi)型的指針,它指向從文件讀出的文本字符串,如果到達文件尾,則返回 NULL 。
ReadString 的另一種形式為:
BOOL ReadString(CString& rString);
參數 rString 用來(lái)容納從文件讀出的文本。
CString 版本忽略回車(chē)換行符,返回值是一個(gè)布爾值。如果返回值為 FALSE ,表示因到達文件尾而沒(méi)有讀到任何字符。
提示: 每執行一次 ReadString ,就會(huì )自動(dòng)從文本文件中讀取一行數據,同時(shí)文件操作指針會(huì )自動(dòng)跳轉到下一行。
實(shí)例 3 :從文件中讀取文本信息
步驟:創(chuàng )建基于對話(huà)框的 MFC 程序,刪除所有自動(dòng)添加的控件,添加按鈕控件,為按鈕添加事件,并在相應的函數處,添加如下代碼:
char* pszFileName="C:\\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(pszFileName,CFile::typeText|CFile::modeReadWrite),&fileException)
{
myFile.SeekToBegin();
CString str1;
myFile.ReadString(str1);
CString str2;
myFile.ReadString(str2);
AfxMessageBox(str1+str2);
}
else
{
TRACE("Can't open file %s,error=%u\n",pszFileName,fileException.m_cause);
}
myFile.Close();
程序運行結果:為程序 F9 設置斷點(diǎn),然后 F5 單步執行,結果如下:
5. 關(guān)閉文件
對文件的操作完成后,使用 CloseFile 關(guān)閉文件。
函數 CStdioFile::Close 關(guān)閉一個(gè)文件,一般一個(gè)文件使用完畢就應該關(guān)閉它:
myFile.Close();
錯誤的文本文件讀寫(xiě)過(guò)程
在讀寫(xiě)文本文件的時(shí)候,最常見(jiàn)的錯誤是 --- 操作文件不存在。這種錯誤產(chǎn)生的典型原因有:
1. 路徑錯誤
char * pszFileName="C:\\Windows\\MyFile.txt";
CStdioFile myFile;
CFileException fileException;
if(!myFile.Open(pszFileName,CFile::modeCreate|CFile::typeText|CFile::modeReadWrite),&fileException)
{
// 文件操作代碼
}
else
{
TRACE("Can't open file %s, error = %u\n",pszFileName,fileException.m_cause);
}
myFile.Close();
由于將文件變量與一個(gè)絕對路徑的文件名關(guān)聯(lián),而程序的數據通常存儲在相對路徑下,所以一旦相對路徑和相對路徑不一致時(shí),就會(huì )出錯。
舉例而言,上一段程序本意是想從 windows 的安裝目錄下面的 MyTextFile.txt 文件中讀取一行數據,但是如果操作系統安裝的路徑不是 C:\Windwos ,而是 C:\Winnt, 那么這段程序就會(huì )出錯。
解決方法是在程序中使用相對路徑,改正后的程序如下:
// 獲取 windows 路徑
LPTSTR lpBuffer=new char[MAX_PATH];
::GetWindowsDirectory(lpBuffer,MAX_PATH);
strcat(lpBuffer,"\\MyFile.txt");
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(lpBuffer,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException)
{
// 文件操作代碼
}
else
{
TRACE("Can't open file %s, error = %u\n",pszFileName,fileException.m_cause);
}
CString strFileTitle="MyFile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(strFileTitle,CFile::typeText|CFile::modeReadWrite),&fileException)
{
// 文件操作代碼
myFile.WriteString(" 測試! ");
}
else
{
TRACE("Can't open file %s, error = %u\n",pszFileName,fileException.m_cause);
}
myFile.Close();
2. 操作文件不存在
如果應用程序所有路徑下面不存在 MyFile.txt 文件,那么在 WriteString 寫(xiě)入信息時(shí)就會(huì )出錯。
解決辦法就是在程序中打開(kāi)文件前要檢查是否存在此文件。如下程序:
CString strFileTitle="MyFile.txt";
CFileFind finder;
if(finder.FindFile(strFileTitle))
{
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(lpBuffer,CFile::typeText|CFile::modeCreate|CFile::modeReadWrite),&fileException)
{
// 文件操作代碼
}
else
{
TRACE("Can't open file %s, error = %u\n",pszFileName,fileException.m_cause);
}
}
else
{
TRACE("Can't find file %s\n",strFileTitle);
}
myFile.Close();
3. 超越文件權限進(jìn)行讀寫(xiě)操作
在打開(kāi)文件的過(guò)程中,通過(guò)參數指定了文件的讀寫(xiě)權限,如果讀寫(xiě)的操作沒(méi)有和相應的權限對應,就會(huì )出現錯誤。
下面的程序就是典型的忽略了文件操作權限的例子:
CString strFileTitle="MyFile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(strFileTitle,CFile::typeText|CFile::modeCreate|CFile::NoTruncate|CFile::modeRead),&fileException)
{
// 文件操作代碼
myFile.WriteString(" 測試 !");
}
else
{
TRACE("Can't open file %s,error=%u\n",strFileTitle,fileException.m_cause);
}
myFile.Close();
支招兒 :
1. 準確定位文件的路徑
操作文件的過(guò)程中,經(jīng)常需要將文本文件放在程序自身的目錄中,但是如果僅僅在程序中使用不指定任何路徑信息的相對路徑,如:
myFile.Open("MyFile.txt",CFile::modeCreate|CFile::typeText|CFile::modeReadWrite);
那么就有可能出現不能正確定位的情況,準確定位文件位置的方法是獲得可執行程序自身的絕對路徑,如:
TCHAR FilePath[MAX_PATH];
GetModuleFileName(NULL,FilePath,MAX_PATH);
(_tcstchr(FilePath,'\\'))[1]=0;
lstrcat(FilePath,_T("MyFile.txt"));
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(FilePath,CFile::modeCreate|CFile::typeText|CFile::modeReadWrite),&fileException)
{
// 文件操作代碼
}
else
{
TRACE("Can't open file %s,error=%u\n",FilePath,fileException.m_cause);
}
myFile.Close();
2. 讀文本文件指定的一行,并得到文本文件的總行數。
讀文本文件指定的一行,并得到文本文件的總行數
要統計文本文件的總行數,可以從頭逐行讀,直到文件尾,程序:
CStdioFile myFile;
CFileException fileException;
if(myFile.Open("MyFile.txt",CFile::modeCreate|CFile::modeNoTruncate|CFile::typeText|CFile::modeReadWrite),&fileException)
{
CString strContent;
int order=1;
while(myFile.ReadString(strContent))
{
if(2==order)
{
AfxMessageBox(strContent);
}
order=order+1;
}
}
else
{
TRACE("Can't open file");
}
myFile.Close();
實(shí)例演示文件操作過(guò)程
客戶(hù)操作記錄實(shí)例
本軟件分為兩個(gè)部分,一部分是 DLL 模塊,里面利用 Hook 技術(shù)完成鍵盤(pán)監控和寫(xiě)入文件的功能;另一部分是界面部分,調用 DLL 啟動(dòng)和停止客戶(hù)操作記錄功能。
第 1 步:創(chuàng )建 MFC DLL 項目
第 2 步:創(chuàng )建 TestHook.h 文件
第 3 步:加入全局共享數據變量
第 4 步:保存 DLL 實(shí)例句柄
第 5 步:類(lèi) CKeyboradHook 的成員函數
第 6 步:創(chuàng )建鉤子可執行程序
第 1 步:創(chuàng )建 MFC DLL 項目
創(chuàng )建一個(gè)名為 HookTest 的 project , project 的類(lèi)型為選擇 MFC AppWizard(DLL),DLL 類(lèi)型為 MFC Extension DLL(using shared MFC DLL)
注意: 選擇 File->New 菜單項,在彈出對話(huà)框的左邊的列表框中選擇 MFC AppWizard(DLL).
在 project name 文本框中輸入項目名稱(chēng), HookTest ; location 中輸入項目的存盤(pán)路徑;選中 Create new workspace ;在 platForms 列表中選擇 Win32 選項。
單擊 OK 按鈕繼續下一步,在彈出的對話(huà)框中設置 DLL 類(lèi)型為 MFC Extension DLL ( using shared MFC DLL ) .
在 IDE 中,選擇 FileView 選項卡,在其中就會(huì )發(fā)現其中有 HookTest.cpp 文件,卻沒(méi)有 HookTest.h 文件,這是因為 visual C++6.0 中沒(méi)有現成的鉤子類(lèi),所以要自己動(dòng)手創(chuàng )建 TestHook.h 文件,在其中建立鉤子類(lèi)。
第 2 步:創(chuàng )建 TestHook.h 文件
選擇 File 菜單,再選擇 New 菜單項,將彈出 New 對話(huà)框。選擇 files 選項卡,并且選擇其中的 C/C++ Header File.
選中 add to project ,并且在對應的下拉列表中選擇項目名稱(chēng) HookTest ;在 location 文本框中輸入項目的存盤(pán)路徑,或單擊右邊的按鈕選擇相應的路徑;在 file 對應的文本框中輸入文件名 HookTest.h ;單擊 OK 按鈕,在 IDE 中自動(dòng)打開(kāi) Hooktest.h 文件供編輯代碼用;
TestHook.h 文件:
#if _MSC_VER>1000
#pragma once
#endif //_MSC_VER>1000
class AFX_EXT_CLASS CHookTest:public CObject
{
public:
CHookTest();
~CHookTest();
BOOL StartHook(); //StartHook() 函數實(shí)現安裝鉤子
BOOL StopHook(); //StopHook() 函數實(shí)現卸載鉤子
};
第 3 步:加入全局共享數據變量
HookTest.cpp 文件中添加:
// 存儲各個(gè)鍵賭贏(yíng)的字符
CString cskey[TOTAL_KEYS]=
{
"BACKSPACE",
"TAB",
……
"F12",
};
// 存儲各個(gè)鍵對應的鍵值
int nkey[TOTAL_KEYS]=
{
0X08, //"BACKSPACE",
0X09, //"TAB",
…….
0x7b,//"F12",
};
#pragma data_seg("mydata")
// 安裝的鍵盤(pán)鉤子子句柄
HHOOK glhTestHook=NULL;
//DLL 實(shí)例句柄
HINSTANCE glhkInstance=NULL;
#pragma data_seg()
第 4 步:保存 DLL 實(shí)例句柄
DllMain 函數中添加如下代碼:
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("HOOKTEST.DLL Initializing!\n");
// 擴展 DLL 僅初始化一次
if (!AfxInitExtensionModule(HookTestDLL, hInstance))
return 0;
//DLL 加入動(dòng)態(tài) MFC 類(lèi)庫中
new CDynLinkLibrary(HookTestDLL);
// 保存 DLL 實(shí)例句柄
glhkInstance=hInstance;
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("HOOKTEST.DLL Terminating!\n");
// 終止這個(gè)鏈接庫前調用它
AfxTermExtensionModule(HookTestDLL);
}
return 1; // ok
第 5 步:類(lèi) CKeyboradHook 的成員函數
//KeyboradProc 函數
LRESULT WINAPI KeyboradProc(int nCode,WPARAM wParam,LPARAM lParam)
{
for(int i=0;i<TOTAL_KEYS;i++)
{
if(nkey[i]==(int)wParam)
{
int nKeyStatus=lParam &0x80000000;
// 根據用戶(hù)按鍵播放對應的聲音文件
switch(nKeyStatus)
case 0: //WM_KEYUP
//case 0x80000000://WM_KEYUP
{
char* pszFileName="C:\\myfile.txt";
CStdioFile myFile;
CFileException fileException;
if(myFile.Open(pszFileName,CFile::typeText|CFile::modeCreate|CFile::modeNoTruncate|CFile::modeReadWrite),&fileException)
{
myFile.SeekToEnd();
// 將文件指針移動(dòng)到文件末尾準備進(jìn)行追加文本的操作
// 此處可以編寫(xiě)追加文本的操作
myFile.WriteString(cskey[i]);
}
else
{
TRACE("Can't open file %s,error=%u\n",pszFileName,fileException.m_cause);
}
}
}
}
// 調用 CallNextHookEx 函數把鉤子信息傳遞給鉤子鏈的下一個(gè)鉤子函數
return CallNextHookEx(glhTestHook,nCode,wParam,lParam);
}
第 6 步:創(chuàng )建鉤子可執行程序
//****************************
BOOL CHookTest::StartHook()
{
glhTestHook=SetWindowsHookEx(WH_KEYBOARD,KeyboradProc,glhkInstance,0);
if(glhTestHook!=NULL)
return TRUE;
return FALSE;
}
//****************************
/*
HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,INSTANCE hMod,DWORD dwThreadId)
idHook: 鉤子類(lèi)型,它是和鉤子函數類(lèi)型一一對應的,例如, WH_KEYBOARD 表示安裝的是鍵盤(pán)鉤子, WH_MOUSE 表示的是鼠標鉤子等。
lpfn :鉤子函數的地址
hMod: 鉤子函數所在的實(shí)例的句柄,對于線(xiàn)程鉤子,該參數為 NULL ;對于系統鉤子,該參數為鉤子函數的 DLL 句柄
dwThreadId: 指定鉤子所監視的線(xiàn)程的線(xiàn)程號,對于全局鉤子,該參數為 NULL.
SetWindowsHookEx 返回所安裝的鉤子句柄。
調用 StartHook 函數后,所有鍵盤(pán)的消息都會(huì )轉移到 KeyboradProc 函數中,通過(guò)數組 nkey 的值與 wParam 參數相比較,可以知道用戶(hù)按下的是哪個(gè)鍵,通過(guò)對 IParam 值的判斷,可以知道是按下鍵還是釋放鍵,然后播放鍵對應的聲音文件即可。
*/
//****************************
// 卸載鉤子
BOOL CHookTest::StopHook()
{
BOOL bResult=FALSE;
if(glhTestHook)
{
bResult=UnhookWindowsHookEx(glhTestHook);
if(bResult)
{
glhTestHook=NULL;
}
}
return bResult;
}
聯(lián)系客服
微信登錄中...
請勿關(guān)閉此頁(yè)面
