C++ wondows 外设蓝牙监控

最新公司项目中需要实时监测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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值