應用程式常需即時感應 U 盤、移動硬碟等可移除裝置的插入與拔出。Windows 透過 WM_DEVICECHANGE 訊息通知應用層,配合 DEV_BROADCAST_VOLUME 結構即可識別磁碟機代號並執行對應邏輯 (如自動備份、病毒掃描、UI 更新)。
// 在 CWnd 衍生類中覆寫 OnDeviceChange
BOOL CMyWnd::OnDeviceChange(UINT nEventType, DWORD dwData)
{
PDEV_BROADCAST_HDR hdr = (PDEV_BROADCAST_HDR)dwData;
if (!hdr) return TRUE;
// 僅處理磁碟機裝置 (DBT_DEVTYP_VOLUME)
if (hdr->dbch_devicetype != DBT_DEVTYP_VOLUME) return TRUE;
PDEV_BROADCAST_VOLUME vol = (PDEV_BROADCAST_VOLUME)hdr;
DWORD unitMask = vol->dbcv_unitmask; // 位遮罩,bit 0=A:, bit 1=B: ...
int driveLetter = -1;
// 找到第一個置位的 bit
for (int i = 0; i < 26; ++i) {
if (unitMask & (1 << i)) { driveLetter = i; break; }
}
if (driveLetter == -1) return TRUE;
CString driveRoot;
driveRoot.Format(_T("%c:\\"), _T('A') + driveLetter);
switch (nEventType) {
case DBT_DEVICEARRIVAL: // 裝置插入
if (GetDriveType(driveRoot) == DRIVE_REMOVABLE) {
// 確認是可移除磁碟 (U 盤等)
OnUsbInserted(driveRoot); // 自訂處理:掃描、備份、提示...
}
break;
case DBT_DEVICEREMOVECOMPLETE: // 裝置移除完成
if (GetDriveType(driveRoot) == DRIVE_REMOVABLE) {
OnUsbRemoved(driveRoot); // 自訂處理:清理暫存、UI 更新...
}
break;
case DBT_DEVNODES_CHANGED:
// 設備拓撲變更,可選處理
break;
}
return TRUE;
}
typedef struct _DEV_BROADCAST_VOLUME {
DWORD dbcv_size; // 結構大小
DWORD dbcv_devicetype; // DBT_DEVTYP_VOLUME (0x00000002)
DWORD dbcv_reserved;
DWORD dbcv_unitmask; // 位遮罩,對應磁碟機代號
WORD dbcv_flags; // DBTF_MEDIA (媒體變更) / DBTF_NET (網路磁碟)
} DEV_BROADCAST_VOLUME, *PDEV_BROADCAST_VOLUME;
ON_WM_DEVICECHANGE() (MFC 自動映射虛擬函數 OnDeviceChange)。dbcv_unitmask 可能有多個 bit 置位,實際專案應迴圈處理每個磁碟機。dbcv_flags & DBTF_MEDIA 表示磁碟機已存在但媒體更換 (如換光碟);DBT_DEVICEARRIVAL/REMOVECOMPLETE 針對裝置本身。PostMessage 到工作執行緒或 AfxBeginThread。若需針對特定裝置介面 GUID (如特定廠商 U 盤) 通知,可用 RegisterDeviceNotification 註冊 DEV_BROADCAST_DEVICEINTERFACE,取消註冊用 UnregisterDeviceNotification。但對通用 U 盤偵測,WM_DEVICECHANGE + DBT_DEVTYP_VOLUME 最簡便。
參考 MSDN 範例:Detecting Media Insertion or Removal。