windows 摄像头,麦克风热插拔功能
一 背景
1 在pc视频会议中我们需要实时更新设备源,以前我们是以遍历的方式实现的,基于性能和内存方面的考虑, 我们需要研究以通知方式实现更新设备源。
2 研发时网上有很多代码,但有些无效,有些需要csdn会员,因此写一个博客做一个记录和分享。
二 代码
main.cpp
#include "DeviceObserverWin.h"
int main()
{
DeviceObserverWin deviceObserver;
deviceObserver.Start();
getchar();
return 0;
}
DeviceObserverWin.h
#ifndef DEVICE_OBSERVER_WIN_H
#define DEVICE_OBSERVER_WIN_H
#include <windows.h>
#include <atomic>
#include <vector>
// 设备信息结构
struct DeviceInfo {
std::string devicePath; // 设备路径
std::string description; // 设备描述
std::string manufacturer; // 制造商
std::string instanceId; // 设备实例ID
};
class DeviceObserverWin {
public:
DeviceObserverWin();
~DeviceObserverWin();
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static DeviceInfo GetDeviceInfo(const char* devicePath, GUID& guid);
void Start();
void Stop();
protected:
void createObserver();
protected:
HWND m_window;
std::vector<HDEVNOTIFY> m_notifiers;
std::atomic<bool> m_runFlag;
};
#endif // DEVICE_OBSERVER_WIN_H
DeviceObserverWin.cpp
#include "DeviceObserverWin.h"
#include <windows.h>
#include <dbt.h>
#include <iostream>
#include <vector>
#include <windows.h>
#include <setupapi.h> // 确保包含正确头文件
#include <atlstr.h>
#include <devguid.h>
#include <initguid.h>
#include <thread>
#include <cfgmgr32.h>
using namespace std;
static const GUID GUID_CAMERA = { 0x65E8773D,0x8F56,0x11D0,{0xA3,0xB9,0x00,0xA0,0xC9,0x22,0x31,0x96} };
static const GUID GUID_MICROPHONE = { 0x2EEF81BE,0x96A8,0x4A9C,{0x8E,0x9D,0x8F,0x3C,0x9D,0xCC,0x8D,0x8F} };
DeviceObserverWin::DeviceObserverWin() {
m_window = nullptr;
m_runFlag.store(true);
}
DeviceObserverWin::~DeviceObserverWin() {
for (auto hNotify : m_notifiers) {
if (hNotify) UnregisterDeviceNotification(hNotify);
}
if (m_window) {
DestroyWindow(m_window);
}
}
void DeviceObserverWin::Start() {
std::thread mthead([&]() {
createObserver();
});
mthead.detach();
}
void DeviceObserverWin::createObserver() {
//new windows
WNDCLASS WC = { 0 };
WC.hInstance = GetModuleHandle(NULL);
WC.lpfnWndProc = WndProc;
WC.lpszClassName = "DeviceNotify";
if (RegisterClass(&WC) == 0)
{
printf("RegisterClass fail \n");
return;
}
//create window
m_window = CreateWindowEx(0, "DeviceNotify", "yoho the is write to where", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
GetModuleHandle(NULL), // Instance handle
NULL // 指向窗口创建的数据
);
if (m_window == NULL) //创建窗口失败
{
printf("CreateWindowEx fail \n");
return;
}
// 多重GUID注册
m_notifiers.clear();
const GUID* guids[] = { &GUID_CAMERA, &GUID_MICROPHONE };
DEV_BROADCAST_DEVICEINTERFACE filter;
ZeroMemory(&filter, sizeof(filter));
filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
for (const auto& guid : guids) {
filter.dbcc_classguid = *guid;
m_notifiers.push_back(RegisterDeviceNotification(m_window, &filter, DEVICE_NOTIFY_WINDOW_HANDLE));
}
MSG msg;
while (m_runFlag) {
GetMessage(&msg, NULL, 0, 0);
}
}
void DeviceObserverWin::Stop() {
m_runFlag.store(false); // 原子写入停止标志
}
LRESULT CALLBACK DeviceObserverWin::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) //窗口回调函数
{
switch (message)
{
case WM_CREATE:
{
CREATESTRUCTA* Info = (CREATESTRUCTA*)lParam; //CREATESTRUCTA是窗口结构体
printf("create windows data \n");
break;
}
case WM_DEVICECHANGE:
{
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
char* szDevId = pDevInf->dbcc_name + 4;
if (DBT_DEVICEARRIVAL == wParam)
{
DeviceInfo deviceInfo = GetDeviceInfo(pDevInf->dbcc_name, pDevInf->dbcc_classguid);
printf("deviceInfo.description:%s \n", deviceInfo.description.c_str());
printf("deviceInfo.devicePath:%s \n", deviceInfo.devicePath.c_str());
printf("deviceInfo.instanceId:%s \n", deviceInfo.instanceId.c_str());
printf("deviceInfo.manufacturer:%s \n", deviceInfo.manufacturer.c_str());
printf("add device:%s \n", szDevId);
break;
}
else if (DBT_DEVICEREMOVECOMPLETE == wParam)
{
DeviceInfo deviceInfo = GetDeviceInfo(pDevInf->dbcc_name, pDevInf->dbcc_classguid);
printf("deviceInfo.description:%s \n", deviceInfo.description.c_str());
printf("deviceInfo.devicePath:%s \n", deviceInfo.devicePath.c_str());
printf("deviceInfo.instanceId:%s \n", deviceInfo.instanceId.c_str());
printf("deviceInfo.manufacturer:%s \n", deviceInfo.manufacturer.c_str());
printf("delete device:%s \n", szDevId);
break;
}
break;
}
default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
// 获取设备详细信息
DeviceInfo DeviceObserverWin::GetDeviceInfo(const char* devicePath, GUID& guid){
DeviceInfo info;
info.devicePath = devicePath;
// 获取设备信息集句柄
HDEVINFO hDevInfo = SetupDiGetClassDevs(
&guid,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
);
if (hDevInfo == INVALID_HANDLE_VALUE) {
return info;
}
SP_DEVICE_INTERFACE_DATA devInterfaceData = { 0 };
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
DWORD memberIndex = 0;
while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guid, memberIndex, &devInterfaceData)) {
DWORD requiredSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, NULL, 0, &requiredSize, NULL);
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
if (pDetail == NULL) {
memberIndex++;
continue;
}
pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SP_DEVINFO_DATA devInfoData = { 0 };
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, pDetail, requiredSize, NULL, &devInfoData)) {
if (_stricmp(pDetail->DevicePath, devicePath) == 0) {
// 获取设备描述
CHAR desc[256] = { 0 };
if (SetupDiGetDeviceRegistryProperty(
hDevInfo, &devInfoData, SPDRP_DEVICEDESC, NULL,
(PBYTE)desc, sizeof(desc), NULL))
{
info.description = desc;
}
// 获取制造商
CHAR mfg[256] = { 0 };
if (SetupDiGetDeviceRegistryProperty(
hDevInfo, &devInfoData, SPDRP_MFG, NULL,
(PBYTE)mfg, sizeof(mfg), NULL))
{
info.manufacturer = mfg;
}
// 获取设备实例ID
CHAR instanceId[256] = { 0 };
if (CM_Get_Device_ID(devInfoData.DevInst, instanceId, sizeof(instanceId) / sizeof(WCHAR), 0) == CR_SUCCESS) {
info.instanceId = instanceId;
}
free(pDetail);
break;
}
}
free(pDetail);
memberIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return info;
}