Windows系統通過GDI(圖形設備接口)將系統的硬件和用戶可以操作的編程接口相分離,以保證系統的穩定型和安全性。當某一個設備的硬件配置發生變化時,Windows發送廣播消息WM_DEVICECHANGE給相關的應用和設備驅動程序,此時在應用程序中可以截獲該消息並分析其中的消息參數,先分辨當前的消息內容,然後調用不同的事件處理程序。本文中主要考慮的是光驅的彈出和送入事件,因此程序設計時只需對邏輯驅動器進行掃描,判斷是哪個驅動器號發生變化即可。一般的外設(包括軟、硬盤驅動器、光驅等)在Windows系統中按照邏輯上的驅動器名稱進行管理,這樣就屏蔽了用戶和計算機硬件直接打交道。Windows中用掩碼數字0代表驅動器“A”,1代表驅動器“B”,依此類推。其中每個邏輯驅動器又有0和1兩種狀態變化,如果驅動器一直未發生變化,則此值為0,否則置為1,一個邏輯驅動器狀態可以響應多種事件,如打開、關閉、新添加、刪除等事件,甚至可以響應用戶自定義的事件。
本文中的程序主要是監測光驅的彈出和送入的狀態改變,當應用程序啟動後,彈出一個對話框,說明正在等待光驅事件的發生,此時如果將光驅彈出,應用程序會提示此時光盤驅動器已經彈出,在送入光驅之後,並且光驅中有CDROM碟片時,應用會提示光驅已經就緒。
二、程序實現
從Visual C++的IDE中的File菜單中選擇New對話框,在Project屬性頁中選擇Win32 Application,建立一個空的Win32應用程序,將StdAfx.h和StdAfx.cpp包含進來。建立一個新的對話框資源,在對話框上寫上一句靜態文本,“正在等待光驅事件”。下面實現監測光驅狀態變化的主程序,在主程序cdchange.cpp中實現了三個函數。
第一個函數是chFirstDriveFromMask(ULONG unitmask),該函數的作用是將響應WM_DEVICECHANGE消息事件的內容(即驅動器掩碼)作為輸入,和系統定義的掩碼相比較,從而返回發生變化事件的驅動器的邏輯名稱,如“E盤”、“F盤”等。函數的源代碼如下:
char chFirstDriveFromMask (ULONG unitmask)
{
char i;
for (i = 0; i < 26; ++i) //假設不會超過26個邏輯驅動器
{
if (unitmask & 0x1) //看該驅動器的狀態是否發生了變化
break;
unitmask = unitmask >> 1;
}
return (i + 'A');
}
第二個函數是關鍵,它是對話框的事件處理函數,同時也是用來截獲並處理Windows的WM_DEVICECHANGE事件。在該函數中首先聲明了一個 PDEV_BROADCAST_HDR類型的結構變量lpdb,該結構裡存儲了當WM_DEVICECHANGE消息產生時的設備事件信息,它的聲明在 VC98目錄下面的Include目錄中的dbt.h中。接著,進入事件和消息處理程序,當WM_DEVICECHANGE事件出現時,程序再判斷該消息的附加消息參數以判斷CDROM的事件類型。當一個設備被插入並變得可用時,系統會發送廣播事件DBT_DEVICEARRIVAL,而當一個設備被除去並變得不可用時,系統會發送廣播事件DBT_DEVICEREMOVECOMPLETE,根據這兩種消息可以判斷當前的光驅是否是開著的。處理完以上事件之後,還要檢查一下光驅中是否由CDROM碟片,如有才彈出對話框表明光驅已經彈出或成功送入。同時為了防止於其他的自動識別光驅狀態的應用產生沖突,本例中將暫時禁止光驅的自動播放功能。函數的源代碼如下:
BOOL WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL fRet = TRUE; // 返回值
//通過響應WM_DEVICECHANGE消息得到的設備事件信息結構
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
//對話框消息處理
switch (uMsg)
{
case WM_INITDIALOG:
fRet = TRUE;
break;
//對 WM_DEVICECHANGE 消息進行處理
case WM_DEVICECHANGE:
char szMsg[80]; // 對話框中要表示的字符串
switch (wParam)
{
//當一個設備變得被插入並變得可用時,
//系統會發送廣播事件DBT_DEVICEARRIVAL
case DBT_DEVICEARRIVAL:
// 判斷CDROM碟片是否已經插入到光驅中
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME) {
PDEV_BROADCAST_VOLUME lpdbv=(PDEV_BROADCAST_VOLUME) lpdb;
//判斷是否有CDROM碟片
if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
// 顯示消息,獲取光驅的邏輯驅動器號
wsprintf (szMsg, "驅動器 %c: 已經可用\n",chFirstDriveFromMask(lpdbv ->dbcv_unitmask));
MessageBox (hwnd, szMsg, "光驅自動監測", MB_OK |MB_ICONINFORMATION);
}
}
break;
//當一個設備變得被移走並變得不可用時,
//系統會發送廣播事件DBT_ DEVICEREMOVECOMPLETE
case DBT_DEVICEREMOVECOMPLETE:
// 判斷CDROM碟片是否從光驅中移走
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME) {
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
//顯示消息,獲取光驅的邏輯驅動器號
wsprintf (szMsg, "驅動器 %c: 已經彈出\n",chFirstDriveFromMask(lpdbv ->dbcv_unitmask));
MessageBox (hwnd, szMsg, "光驅自動監測", MB_OK| MB_ICONINFORMATION);
}
}
break;
}
//處理其他Windows消息
case WM_COMMAND:
int wmId, wmEvent;
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case IDOK:
EndDialog(hwnd, 0);
break;
}
default:
fRet = FALSE;
break;
}
// 禁止光驅的AutoPlay功能
static UINT uMsgQueryCancelAutoPlay=RegisterWindowMessage("QueryCancelAutoPlay");
if (uMsg==uMsgQueryCancelAutoPlay)
{
int n = MessageBox(hwnd, "你想禁止AutoPlay功能嗎?", NULL,MB_YESNO | MB_ICONQUESTION);
// 1代表取消 AutoPlay
// 0 t代表允許AutoPlay
SetDlgMsgResult(hwnd, uMsg, (n == IDYES) ? 1 : 0);
fRet = (n == IDYES) ? 1 : 0;
}
return(fRet);
}
第三個函數非常簡單,產生一個模式對話框。代碼如下:
int APIENTRY WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
//從對話框模版資源中創建一個模式對話框
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1),NULL, DlgProc);
return 0;
}