windows 摄像头,麦克风热插拔功能

发布于:2025-09-11 ⋅ 阅读:(20) ⋅ 点赞:(0)

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;
}

网站公告

今日签到

点亮在社区的每一天
去签到