最新公司项目中需要实时监测U盘、TYPE-C、移动硬盘等磁盘存储设备的插入与拔出,以及蓝牙连接或断开,查询了网上的很多资料,总结一下
首先订阅和接收相关的设备消息需要在服务程序下来完成,所以是需要先完成一个服务程序的编写,本文主要重点在订阅和接收相关的设备消息,服务程序就不介绍了
服务程序启动最开始要注册用于处理扩展服务控制请求的函数。RegisterServiceCtrlHandlerExA,此函数主要是用于注册接收订阅消息的函数,SvcCtrlHandler函数实现就是接收消息
SERVICE_STATUS_HANDLE gSvcStatusHandle = RegisterServiceCtrlHandlerEx(
SVCNAME,
SvcCtrlHandler,
NULL);
监控外设需要用到RegisterDeviceNotification来订阅卷设备消息,如果外设(磁盘,光盘,usb)都要监控,则只需要订阅卷设备GUID_DEVINTERFACE_VOLUME即可,如果只关注其中一种,则分别订阅磁盘GUID_DEVINTERFACE_DISK,光盘GUID_DEVINTERFACE_CDROM,USB设备GUID_DEVINTERFACE_USB_DEVICE;订阅蓝牙可使用GUID_BLUETOOTH_HCI_EVENT
这里我封装了下RegisterDeviceNotification,方便调用
BOOL DoRegisterDeviceInterface(
GUID InterfaceClassGuid,
DWORD dwDeviceType,
HANDLE hHandle,
HDEVNOTIFY *hDevNotify
)
{
if (dwDeviceType == DBT_DEVTYP_HANDLE)
{
if (NULL == hHandle)
{
CString strTemp;
strTemp.Format(L"[ServiceTest] RegisterDeviceNotification 句柄为空,设备类型:[%ld]注册失败", dwDeviceType);
OutputDebugStringW(strTemp);
}
DEV_BROADCAST_HANDLE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
NotificationFilter.dbch_devicetype = dwDeviceType;
NotificationFilter.dbch_handle = hHandle;
NotificationFilter.dbch_eventguid = InterfaceClassGuid;
*hDevNotify = RegisterDeviceNotification(
gSvcStatusHandle,
&NotificationFilter,
DEVICE_NOTIFY_SERVICE_HANDLE
);
}
else
{
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = dwDeviceType;
NotificationFilter.dbcc_classguid = InterfaceClassGuid;
*hDevNotify = RegisterDeviceNotification(
gSvcStatusHandle,
&NotificationFilter,
DEVICE_NOTIFY_SERVICE_HANDLE
);
}
CString strTemp;
if(NULL == *hDevNotify)
{
strTemp.Format(L"[ServiceTest] RegisterDeviceNotification 设备类型:[%ld]注册失败,err:%ld", dwDeviceType, GetLastError());
OutputDebugStringW(strTemp);
return FALSE;
}
strTemp.Format(L"[ServiceTest] RegisterDeviceNotification 设备类型:%ld 注册成功", dwDeviceType);
OutputDebugStringW(strTemp);
return TRUE;
}
订阅外设用法
HDEVNOTIFY hDevNotify;
//订阅卷设备
if (DoRegisterDeviceInterface(GUID_DEVINTERFACE_VOLUME, DBT_DEVTYP_DEVICEINTERFACE, NULL, &hDevNotify))
{
vecDevNotify.push_back(hDevNotify);
}
//如果订阅了卷设备就不用单独订阅磁盘和光盘了,如果都订阅了会接收到两次一样的消息
//订阅磁盘消息
//DoRegisterDeviceInterface(GUID_DEVINTERFACE_DISK, DBT_DEVTYP_DEVICEINTERFACE, NULL, &hDevNotify);
//订阅光盘消息
//DoRegisterDeviceInterface(GUID_DEVINTERFACE_CDROM, DBT_DEVTYP_DEVICEINTERFACE, NULL, &hDevNotify);
//订阅usb消息
//DoRegisterDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE, DBT_DEVTYP_DEVICEINTERFACE, NULL, &hDevNotify);
订阅蓝牙用法,在网上找了一下都是通过BluetoothFindFirstRadio api的方式开始枚举本地的蓝牙无线电,对枚举到的句柄订阅,但是测试中发现订阅蓝牙消息在winserver中bthprops.cpl缺失的问题,所以改成用GetProcAddress的方式https://stackoverflow.com/questions/22386730/alternative-to-bluetoothgetradioinfo-on-windows-server-2008-r2,使用方式如下:
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
//订阅蓝牙消息
//HBLUETOOTH_RADIO_FIND hbf = NULL;
//HANDLE hbr = NULL;
//BLUETOOTH_FIND_RADIO_PARAMS btfrp = { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
//hbf = BluetoothFindFirstRadio(&btfrp, &hbr);
//bool brfind = false;
//if (hbf != NULL)
//{
// brfind = TRUE;
//}
//while (brfind)
//{
// if (DoRegisterDeviceInterface(GUID_BLUETOOTH_HCI_EVENT, DBT_DEVTYP_HANDLE, hbr, &hDevNotify))
// {
// vecDevNotify.push_back(hDevNotify);
// }
// brfind = BluetoothFindNextRadio(hbf, &hbr);
//}
//BluetoothFindRadioClose(hbf);
//由于订阅蓝牙消息在winserver发现了bthprops.cpl缺失的问题改成用GetProcAddress的方式
//https://stackoverflow.com/questions/22386730/alternative-to-bluetoothgetradioinfo-on-windows-server-2008-r2
CString strTemp;
do
{
HMODULE hModule = LoadLibrary(L"BluetoothAPIs.dll");
if (hModule == NULL)
{
strTemp.Format(L"[ServiceTest] 加载获取BluetoothAPIs.dll失败,订阅蓝牙设备消息失败");
OutputDebugStringW(strTemp);
}
FARPROC pFuncFirstRadio = GetProcAddress(hModule, "BluetoothFindFirstRadio");
FARPROC pFuncNextRadio = GetProcAddress(hModule, "BluetoothFindNextRadio");
FARPROC pFuncRadioClose = GetProcAddress(hModule, "BluetoothFindRadioClose");
if (pFuncFirstRadio == NULL || pFuncNextRadio == NULL || pFuncRadioClose == NULL)
{
strTemp.Format(L"[ServiceTest] 获取蓝牙设备的导出函数失败,订阅蓝牙设备消息失败");
OutputDebugStringW(strTemp);
FreeLibrary(hModule);
break;
}
typedef HANDLE(WINAPI* LPBluetoothFindFirstRadio)(BLUETOOTH_FIND_RADIO_PARAMS*, HANDLE*);
LPBluetoothFindFirstRadio pBluetoothFindFirstRadio = (LPBluetoothFindFirstRadio)pFuncFirstRadio;
typedef BOOL(WINAPI* LPBluetoothFindNextRadio)(HANDLE, HANDLE*);
LPBluetoothFindNextRadio pBluetoothFindNextRadio = (LPBluetoothFindNextRadio)pFuncNextRadio;
typedef BOOL(WINAPI* LPBluetoothFindRadioClose)(HANDLE);
LPBluetoothFindRadioClose pBluetoothFindRadioClose = (LPBluetoothFindRadioClose)pFuncRadioClose;
HBLUETOOTH_RADIO_FIND hbf = NULL;
HANDLE hbr = NULL;
BLUETOOTH_FIND_RADIO_PARAMS btfrp = { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
hbf = pBluetoothFindFirstRadio(&btfrp, &hbr);
bool brfind = false;
if (hbf != NULL)
{
brfind = TRUE;
}
while (brfind)
{
if (DoRegisterDeviceInterface(GUID_BLUETOOTH_HCI_EVENT, DBT_DEVTYP_HANDLE, hbr, &hDevNotify))
{
vecDevNotify.push_back(hDevNotify);
}
brfind = pBluetoothFindNextRadio(hbf, &hbr);
}
pBluetoothFindRadioClose(hbf);
FreeLibrary(hModule);
} while (FALSE);
订阅完成后就可以启动服务,上面说到的SvcCtrlHandler函数实现就是接收消息
DWORD WINAPI SvcCtrlHandler(DWORD dwCtrl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
// Handle the requested control code.
CString strHandle;
strHandle.Format(L"[ServiceTest] SvcCtrlHandler dwCtrl = %lu, dwEventType = %lu", dwCtrl, dwEventType);
OutputDebugString(strHandle);
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
// Signal the service to stop.
SetEvent(ghSvcStopEvent);
return NO_ERROR;
case SERVICE_CONTROL_DEVICEEVENT:
if (DBT_DEVICEARRIVAL == dwEventType || DBT_CUSTOMEVENT == dwEventType)
{
DEV_BROADCAST_HDR * pHdr = (DEV_BROADCAST_HDR *)lpEventData;
//strHandle.Format(L"[ServiceTest] detected a new device dbch_devicetype = %lu", pHdr->dbch_devicetype);
//OutputDebugString(strHandle);
LPOLESTR pGuid = NULL;
WCHAR szTempPath[516] = { 0 };
switch(pHdr->dbch_devicetype)
{
case DBT_DEVTYP_DEVICEINTERFACE:
PDEV_BROADCAST_DEVICEINTERFACE pDevInf;
pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
StringFromCLSID(pDevInf->dbcc_classguid, &pGuid);
strHandle.Format(L"[ServiceTest] new deviceDevName = %s Device CLSID = %s", pDevInf->dbcc_name, pGuid);
OutputDebugString(strHandle);
//接收到外设插拔的消息
CoTaskMemFree(pGuid);
break;
case DBT_DEVTYP_HANDLE:
PDEV_BROADCAST_HANDLE pDevHnd;
pDevHnd = (PDEV_BROADCAST_HANDLE)pHdr;
//StringFromCLSID(pDevHnd->dbch_eventguid, &pGuid);
if (GUID_BLUETOOTH_HCI_EVENT == pDevHnd->dbch_eventguid)
{
//接收到蓝牙断开或连接的消息
}
break;
case DBT_DEVTYP_OEM:
PDEV_BROADCAST_OEM pDevOem;
pDevOem = (PDEV_BROADCAST_OEM)pHdr;
break;
case DBT_DEVTYP_PORT:
PDEV_BROADCAST_PORT pDevPort;
pDevPort = (PDEV_BROADCAST_PORT)pHdr;
break;
case DBT_DEVTYP_VOLUME:
PDEV_BROADCAST_VOLUME pDevVolume;
pDevVolume = (PDEV_BROADCAST_VOLUME)pHdr;
break;
}
}
break;
case SERVICE_CONTROL_INTERROGATE:
// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
return NO_ERROR;
}
接收到消息时,就可以枚举外设或蓝牙就可以判断出新增的外设或蓝牙了
最后再说一下枚举蓝牙设备的实现方式,我这里主要是检查移动设备的接入,比如电脑或手机蓝牙连接电脑,类型主要是这个结构ulClassofDevice 可以参考https://blog.youkuaiyun.com/guoqx/article/details/129396192
BOOL EnumBluetooth()
{
BOOL bFindConnect = FALSE;
HBLUETOOTH_RADIO_FIND hbf = NULL;
HANDLE hbr = NULL;
HBLUETOOTH_DEVICE_FIND hbdf = NULL;
BLUETOOTH_FIND_RADIO_PARAMS btfrp = { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
BLUETOOTH_RADIO_INFO bri = { sizeof(BLUETOOTH_RADIO_INFO)};
BLUETOOTH_DEVICE_SEARCH_PARAMS btsp = { sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS) };
BLUETOOTH_DEVICE_INFO btdi = { sizeof(BLUETOOTH_DEVICE_INFO) };
hbf=BluetoothFindFirstRadio(&btfrp, &hbr);
bool brfind = hbf != NULL;
while (brfind)
{
if (BluetoothGetRadioInfo(hbr, &bri) == ERROR_SUCCESS)
{
btsp.hRadio = hbr;
btsp.fReturnAuthenticated = TRUE;
btsp.fReturnConnected = TRUE;
btsp.fReturnRemembered = TRUE;
btsp.fReturnUnknown = TRUE;
btsp.cTimeoutMultiplier = 30;
hbdf=BluetoothFindFirstDevice(&btsp, &btdi);
bool bfind = hbdf != NULL;
while (bfind)
{
ULONG ulMajorDeviceClasses = btdi.ulClassofDevice & 0xF00;
if (btdi.fConnected && (ulMajorDeviceClasses == 512 || ulMajorDeviceClasses == 256))
{
CString strTemp;
strTemp.Format(L"[ServiceTest] 已连接蓝牙:%s", btdi.szName);
OutputDebugStringW(strTemp);
bFindConnect = TRUE;
}
bfind=BluetoothFindNextDevice(hbdf, &btdi);
}
BluetoothFindDeviceClose(hbdf);
}
CloseHandle(hbr);
brfind=BluetoothFindNextRadio(hbf, &hbr);
}
BluetoothFindRadioClose(hbf);
return bFindConnect;
}