?? jiurl鍵盤驅動 2.htm
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0058)http://jiurl.nease.net/cn/document/KbdDriver/JiurlKbd2.htm -->
<HTML><HEAD><TITLE>JIURL鍵盤驅動 2</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<STYLE type=text/css>.title {
FONT-WEIGHT: bold; FONT-SIZE: 21px; LINE-HEIGHT: 48px; FONT-FAMILY: "黑體", Arial, sans-serif; TEXT-DECORATION: none
}
.author {
FONT-SIZE: 12px; LINE-HEIGHT: 16px; FONT-FAMILY: "宋體"
}
.content {
FONT-SIZE: 14px; LINE-HEIGHT: 20px
}
</STYLE>
<META content="MSHTML 6.00.2900.2668" name=GENERATOR></HEAD>
<BODY bgColor=#f7f7f7 topMargin=5>
<DIV align=center>
<CENTER>
<TABLE height=29 cellSpacing=0 cellPadding=0 width="96%" border=0>
<TBODY>
<TR>
<TD class=title width="100%" height=41>
<P align=center><FONT face=宋體>JIURL鍵盤驅動 2</FONT></P></TD></TR></CENTER>
<TR>
<TD class=author width="100%" height=9>
<P align=center><FONT face=宋體>作者: <A
href="mailto:jiurl@mail.china.com">JIURL</A> </FONT></P></TD></TR>
<TR>
<TD class=author width="100%" height=6>
<P align=center><FONT
face=宋體>
主頁: <A href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A>
</FONT></P></TD></TR>
<TR>
<TD class=author width="100%" height=2>
<P align=center><FONT face=宋體> 日期: 2003-12-13</FONT>
</P></TD></TR></TBODY></TABLE></DIV>
<DIV align=center>
<CENTER>
<TABLE height=1 cellSpacing=0 cellPadding=0 width="96%" border=0>
<TBODY>
<TR>
<TD width="100%" height=1>
<HR color=#396da5 SIZE=3>
</TD></TR></TBODY></TABLE></CENTER></DIV>
<DIV align=center>
<TABLE class=content height=200 cellSpacing=0 cellPadding=0 width="96%"
border=0>
<TBODY>
<TR>
<TD vAlign=top width="131%" height=200>
<P><B>2 應用層基礎知識</B><BR><BR> 在討論使用鍵盤的應用程序這個問題之前,我們首先介紹一下
Windows 中,應用程序使用驅動,應用程序與驅動通信的一些問題。<BR><BR>2.1
應用程序如何使用驅動<BR><BR> 應用程序中使用
CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle
來指示驅動程序完成某種任務。比如我們在應用程序中使用 ReadFile 來讓驅動讀取硬件設備,我們在應用程序中使用 WriteFile
來讓驅動寫硬件設備,我們在應用程序中使用 DeviceIoContorl 來讓驅動完成某些驅動支持的功能。而 ReadFile,
WriteFile, DeviceIoControl 這三個 api 都需要一個句柄作為參數,以確定他們是要哪個驅動來完成他們的請求。這個句柄是通過
CreateFile 獲得的。使用 CloseHandle 關閉這個句柄。簡單的說就是,應用程序中,首先要通過 CreateFile
獲得一個句柄,之后應用程序可以以這個句柄為參數,使用 ReadFile,WriteFile,DeviceIoControl
讓驅動程序執行某種操作。當不再使用時,通過 CloseHandle 關閉這個句柄。<BR><BR> 這幾個
api 都位于 KERNEL32.DLL 中,他們最終會通過系統服務(int 2e)調用內核中的相應的函數,如
NtCreateFile,NtReadFile 等。而 NtCreateFile,NtReadFile 等函數中,會創建一個
IRP,并用傳入的參數初始化這個 IRP,然后將這個 IRP 發給驅動,讓驅動做處理。相應的 NtCreateFile 產生
IRP_MJ_CREATE 的 IRP ,NtReadFile 產生 IRP_MJ_READ 的 IRP。驅動得到這些 IRP
,根據情況做處理,對于 IRP_MJ_READ ,會調用驅動中處理 IRP_MJ_READ
的部分,可能最后引起讀硬件的操作。<BR><BR><BR>2.2 獲得指定驅動的句柄<BR><BR>
對于希望被應用程序使用的驅動,會在初始化的過程中,把能找到它設備對象的一個 SymbolicLink 放在對象管理器命名空間(Object
Manager Namespace)的 \??\ 下。這樣用 "\\\\.\\那個SymbolicLink的名字" 作為 CreateFile 的
lpFileName 參數,調用 CreateFile ,得到的句柄就可以找到相應的驅動的那個設備對象(\\.\ 會被轉換成
\??\)。之后以這個句柄為參數使用 ReadFile,WriteFile,DeviceIoControl,產生的 IRP
就被發到相應的設備對象。也就是說只要驅動把 設備對象的 SymbolicLink 放在 \??\ 下,并且應用程序知道這個 SymbolicLink
的名字,就可以使用 CreateFile 得到相應的句柄。<BR><BR>HANDLE CreateFile(<BR>LPCTSTR
lpFileName, // file name<BR>DWORD dwDesiredAccess, // access mode<BR>DWORD
dwShareMode, // share mode<BR>LPSECURITY_ATTRIBUTES lpSecurityAttributes,
// SD<BR>DWORD dwCreationDisposition, // how to create<BR>DWORD
dwFlagsAndAttributes, // file attributes<BR>HANDLE hTemplateFile // handle
to template file<BR>);<BR><BR><BR>可以使用工具 WinObj 來查看 對象管理器命名空間(Object
Manager Namespace) 。WinObj 可以從 http://www.sysinternals.com
獲得。關于內核對象和命名地址空間的詳細介紹,可以參考《 JIURL玩玩Win2k 對象
》,這篇文章可以在我的主頁上找到。<BR><BR>在驅動的初始化過程中,會通過調用 IoCreateDevice
創建設備對象,可以指定一個設備名作為IoCreateDevice 的參數(也可以不指定,那樣這個設備對象就沒有名字)。這樣這個設備對象會被放在
對象管理器命名空間(Object Manager Namespace)的 \Device\ 下。不過應用程序只能訪問命名空間的 \??\
,所以如果驅動希望把設備對象暴露給應用程序的話,會為設備對象創建一個 SymbolicLink 放在 \??\ 下。對于放在 \Device\
下的有名字的設備,其他驅動程序如果知道它的名字,就可以使用 IoGetDeviceObjectPointer
得到這個設備對象的指針。<BR><BR>驅動可以通過 IoCreateSymbolicLink ,在 \??\ 下建立設備對象的
SymbolicLink 。這樣應用程序必須要也知道該 SymbolicLink 的名字,然后就可以以這個符號鏈接名做參數使用 CreateFile
,得到句柄。從 IoCreateSymbolicLink 的參數,我們可以知道,只能使用 IoCreateSymbolicLink
為有名字的設備對象建立 SymbolicLink。<BR><BR>另一種方法是,使用一個 GUID 來標識一個設備接口。驅動使用標識設備的 GUID
做參數調用IoRegisterDeviceInterface ,然后使用 IoSetDeviceInterfaceState ,就會為設備對象在
\??\ 下產生一個符號鏈接(SymbolicLink)。應用程序使用同一個 GUID 做參數,使用API:
SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces,
SetupDiGetDeviceInterfaceDetail 就可以得到創建的 \??\ 下的符號鏈接名,就可以以這個符號鏈接名做參數使用
CreateFile ,得到句柄。<BR><BR>
句柄簡介。每個進程都有一個自己的句柄表,句柄表中放著內核對象的指針,句柄就是內核對象在句柄表中的索引。通過句柄就可以在進程的句柄表中找到對應的內核對象的指針。關于句柄的詳細介紹,可以參考
《 JIURL玩玩Win2k 進程線程篇 HANDLE_TABLE 》,這篇文章可以在我的主頁上找到。<BR><BR>2.3
一些結論<BR><BR> SymbolicLink 對象可以找到相應的 設備對象。SymbolicLink
對象中保存著相應的 設備對象的地址。設備對象不保存它的 SymbolicLink 對象的任何信息。SymbolicLink
對象的地址保存在對象管理器命名空間(Object Manager Namespace)中。也就是說只要知道 SymbolicLink
的名字,就可以在對象管理器命名空間中找到它。應用程序 CreateFile 得到的句柄,通過這個句柄在進程的句柄表中找到的是一個文件對象(File
Object)。文件對象 對應的 設備對象 中不保存這個文件對象的任何信息。對應的 SymbolicLink
對象中也不保存這個文件對象的任何信息。這個文件對象的地址,保存在應用程序的句柄表中,應用程序通過句柄可以找到這個文件對象。這個 文件對象
中保存著對應的 設備對象 的地址。可以猜到,應用程序在用 CreateFile 創建的時候,會根據參數中的 SymolicLink 名字,找到
SymolicLink 對象,進而找到該對象中保存的 設備對象 的地址,然后直接把找到的 設備對象 的地址保存在文件對象中。文件對象的 +04
struct _DEVICE_OBJECT *DeviceObject
處,保存著對應的設備對象的地址。<BR><BR> 對于需要暴露接口給應用程序的驅動。首先,驅動中需要在
對象管理器命名空間的 \??\ 下,為設備對象建立一個 SymbolicLink ,不管采取何種方式。之后,應用程序要知道這個
SymbolicLink 的名字,不管采取何種方式。然后應用程序以 "\\\\.\\那個SymbolicLink的名字" 為參數使用
CreateFile 得到一個句柄。這樣,之后的 DeviceIoControl(),WriteFile(),ReadFile() 使用前面用
CreateFile 得到的句柄作為參數,他們可以通過這個句柄,找到對應的文件對象,而這個文件對象中保存有對應的 設備對象 的指針,這樣就可以將
IRP 發到這些設備。 <BR><BR>
上面的結論是通過對一個小例子進行觀察得到的。<BR><BR>2.4 IRP 將被發往設備棧的棧頂<BR><BR>
IRP
將無論如何被發往設備棧的頂。CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle。他們最終都會產生一個
IRP,發給一個設備對象。對于 CreateFile 來說通過 SymbolicLink的名字
來找到一個設備對象。對于其他的幾個函數,通過句柄,找到一個文件對象,文件對象中保存有設備對象的指針。不過產生的 IRP
并不一定發給找到的這個設備對象,而是發給找到的這個設備對象所在設備棧的最頂上的一個設備對象。<BR><BR>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -