C# 如何将USB耳机麦克风与耳机/扬声器相关联

C# 如何将USB耳机麦克风与耳机/扬声器相关联,c#,.net,wmi,microphone,headset,C#,.net,Wmi,Microphone,Headset,背景 我需要在软电话应用程序中显示连接的耳机列表,包括麦克风和耳机。对于测试,我有以下设备: 贾布拉专业9470 Plantronics BT300M 罗技G430游戏耳机 用户应该能够从组合框中选择耳机,而无需分别选择麦克风和耳机 信息 我知道在Windows中哪里可以找到有关麦克风和耳机的信息,但我无法使用WMI或MMDevice API获取这些信息 要查找信息,请右键单击任务栏右侧的声音扬声器图标,然后选择播放设备 通过双击或单击“属性”打开“属性”窗口。 单击“属性”窗口上的“属性”按钮

背景

我需要在软电话应用程序中显示连接的耳机列表,包括麦克风和耳机。对于测试,我有以下设备:

贾布拉专业9470 Plantronics BT300M 罗技G430游戏耳机 用户应该能够从组合框中选择耳机,而无需分别选择麦克风和耳机

信息

我知道在Windows中哪里可以找到有关麦克风和耳机的信息,但我无法使用WMI或MMDevice API获取这些信息

要查找信息,请右键单击任务栏右侧的声音扬声器图标,然后选择播放设备

通过双击或单击“属性”打开“属性”窗口。 单击“属性”窗口上的“属性”按钮。 单击“详细信息”选项卡并在组合框中找到“子对象”属性。 这将为我提供以下信息:

SWD\MMDEVAPI\{0.0.0.00000000}.{f2e09e37-8389-46c4-8b2b-53e08b874399}
SWD\MMDEVAPI\{0.0.1.00000000}.{3402ee6e-d862-47ca-8ab8-bb8254216032}
第一行与我的耳机Jabra PRO 9470和第二个耳机麦克风Jabra PRO 9470相匹配

为了在C中获得相同的信息,我在类中循环并输出包含mmpi的所有值。在我的电脑上,它将返回6个值3个麦克风,3个耳机

foreach (var device in new ManagementObjectSearcher("SELECT * FROM Win32_USBControllerDevice").Get())
{
    foreach (var property in device.Properties)
    {
        // Gets the value of the property on the device.
        var value = property.Value == null ? string.Empty : property.Value.ToString();

        if (value.IndexOf("mmdevapi", StringComparison.OrdinalIgnoreCase) > -1)
        {
            // Output connected USB microphones and earphones.
            Console.WriteLine(property.Value);
        }
    }
}
作为参考,上述代码将输出:

\\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\\MMDEVAPI\\{0.0.0.00000000}.{F2E09E37-8389-46C4-8B2B-53E08B874399}"
\\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\\MMDEVAPI\\{0.0.1.00000000}.{3402EE6E-D862-47CA-8AB8-BB8254216032}"
\\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\\MMDEVAPI\\{0.0.0.00000000}.{985F2B5C-2EE2-4733-BBD6-48BFDE2D5582}"
\\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\\MMDEVAPI\\{0.0.1.00000000}.{71D824EA-DAE9-4F0D-B673-4425385E3777}"
\\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\\MMDEVAPI\\{0.0.0.00000000}.{D29C0970-D515-4F91-9924-F0063CF1A196}"
\\PC9018\root\cimv2:Win32_PnPEntity.DeviceID="SWD\\MMDEVAPI\\{0.0.1.00000000}.{C4B331E2-C56B-4D9B-A486-2ED6C11FDB8C}"
问题

我现在最大的问题是,如何将正确的耳机、麦克风和耳机关联到耳机对象中

尝试

我曾尝试在Google和StackOverflow中搜索答案或提示,但我无法使用WMI或MMDevice API在麦克风和耳机之间找到任何共同点或关系

如果有一种方法可以创建一个字典,其中密钥是物理设备或USB端口所特有的,并且该值是关联Win32_PnPEntity.DeviceID的列表,那么我找不到它


本着几天后星球大战日的精神:帮帮我,斯塔克。你是我唯一的希望。

在和Windows注册表的帮助下,我似乎自己找到了答案。问题中的代码仍然是正确的,但是NAudio使它变得更容易一些

我在Windows注册表中的以下位置找到了问题的答案:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render
每个设备都有一个名为{b3f8fa53-0004-438e-9003-51a46e139bfc}的属性,2的值与此{1}类似。USB\VID_047F&PID_0416&MI_00\7&21995D75&0&0000

这看起来是一个唯一的硬件ID,由耳机麦克风和耳机共享,与问题中的儿童属性相同

解决方案

一种接口,使用单一方法定位并返回耳机列表

public interface IHeadsetLocator
{
    /// <summary>
    /// Locate all connected audio devices based on the given state.
    /// </summary>
    /// <param name="deviceState"></param>
    /// <returns></returns>
    IReadOnlyCollection<Headset> LocateConnectedAudioDevices(DeviceState deviceState = DeviceState.Active);
}
我希望这将有助于其他类似情况的人

快乐编码

public class HeadsetLocator : IHeadsetLocator
{
    /// <summary>
    /// Locate all connected audio devices based on the given state.
    /// </summary>
    /// <param name="deviceState"></param>
    /// <returns></returns>
    public IReadOnlyCollection<Headset> LocateConnectedAudioDevices(DeviceState deviceState = DeviceState.Active)
    {
        var enumerator = new MMDeviceEnumerator();
        var relatedAudioDevices = new ConcurrentDictionary<string, List<MMDevice>>();
        var headsets = new List<Headset>();

        // Locate all connected audio devices.
        foreach (var device in enumerator.EnumerateAudioEndPoints(DataFlow.All, deviceState))
        {
            // Gets the DataFlow and DeviceID from the connected audio device.
            var index = device.ID.LastIndexOf('.');
            var dataFlow = device.ID.Substring(0, index).Contains("0.0.0") ? DataFlow.Render : DataFlow.Capture;
            var deviceId = device.ID.Substring(index + 1);

            // Gets the unique hardware token.
            var hardwareToken = GetHardwareToken(dataFlow, deviceId);

            var audioDevice = relatedAudioDevices.GetOrAdd(hardwareToken, o => new List<MMDevice>());
            audioDevice.Add(device);
        }

        // Combines the related devices into a headset object.
        foreach (var devicePair in relatedAudioDevices)
        {
            var capture = devicePair.Value.FirstOrDefault(o => o.ID.Contains("0.0.1"));
            var render = devicePair.Value.FirstOrDefault(o => o.ID.Contains("0.0.0"));

            if (capture != null && render != null)
            {
                headsets.Add(new Headset("Headset", render.DeviceFriendlyName, capture, render));
            }
        }

        return new ReadOnlyCollection<Headset>(headsets);
    }

    /// <summary>
    /// Gets the token of the USB device.
    /// </summary>
    /// <param name="dataFlow"></param>
    /// <param name="audioDeviceId"></param>
    /// <returns></returns>
    public string GetHardwareToken(DataFlow dataFlow, string audioDeviceId)
    {
        using (var registryKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
        {
            var captureKey = registryKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\" + dataFlow + @"\" + audioDeviceId + @"\Properties");
            if (captureKey != null)
            {
                return captureKey.GetValue("{b3f8fa53-0004-438e-9003-51a46e139bfc},2") as string;
            }
        }

        return null;
    }
}