You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WPF(C#)语音录制程序中实现无第三方API的音频输入选择ComboBox

不依赖第三方库实现WPF麦克风选择方案

没问题!完全可以通过Windows原生的Core Audio APIs来实现,不需要任何第三方库。下面是一套完整的思路和可直接复用的代码示例:

核心思路

Windows从Vista开始提供了Core Audio API,其中的MMDeviceEnumerator组件可以枚举系统中的音频设备,我们可以通过它筛选出所有活动的麦克风捕获设备,然后将设备信息绑定到ComboBox供用户选择。

步骤1:定义Core Audio API互操作接口

因为Core Audio是COM API,我们需要手动定义必要的接口、枚举和结构体(也可以直接添加COM引用,但手动定义更灵活,避免版本依赖问题):

using System;
using System.Runtime.InteropServices;

// Core Audio API接口定义
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDeviceEnumerator
{
    void NotImpl1();
    int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);
    int EnumAudioEndpoints(EDataFlow dataFlow, EDeviceState stateMask, out IMMDeviceCollection ppDevices);
}

[Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDeviceCollection
{
    int GetCount(out uint pcDevices);
    int Item(uint nDevice, out IMMDevice ppDevice);
}

[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDevice
{
    int GetId([MarshalAs(UnmanagedType.LPWStr)] out string ppstrId);
    int GetState(out uint pdwState);
    int GetPropertyStore(int stgmAccess, out IPropertyStore ppProperties);
}

[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPropertyStore
{
    int GetCount(out uint cProps);
    int GetAt(uint iProp, out PropertyKey pkey);
    int GetValue(ref PropertyKey key, out PropVariant pv);
}

// 音频数据流方向枚举
internal enum EDataFlow
{
    eRender,    // 播放设备
    eCapture,   // 捕获设备(麦克风)
    eAll
}

// 设备角色枚举
internal enum ERole
{
    eConsole,
    eMultimedia,
    eCommunications
}

// 设备状态枚举
internal enum EDeviceState
{
    Active = 0x00000001,       // 活动设备
    Disabled = 0x00000002,
    NotPresent = 0x00000004,
    Unplugged = 0x00000008,
    All = 0x0000000F
}

// 属性键结构体
[StructLayout(LayoutKind.Sequential)]
internal struct PropertyKey
{
    public Guid fmtid;
    public uint pid;
}

// 属性值变体结构体
[StructLayout(LayoutKind.Sequential)]
internal struct PropVariant
{
    public ushort vt;
    public ushort wReserved1;
    public ushort wReserved2;
    public ushort wReserved3;
    public IntPtr pwszVal; // 存储字符串类型的值
}

// Core Audio互操作工具类
internal static class CoreAudioInterop
{
    [DllImport("ole32.dll")]
    public static extern int CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter, uint dwClsContext, ref Guid riid, out IMMDeviceEnumerator ppv);

    // MMDeviceEnumerator类ID
    public static readonly Guid MMDeviceEnumeratorClassId = new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E");
    // IMMDeviceEnumerator接口ID
    public static readonly Guid IID_IMMDeviceEnumerator = new Guid("A95664D2-9614-4F35-A746-DE8DB63617E6");
    // 设备友好名称的属性键
    public static readonly PropertyKey PKEY_Device_FriendlyName = new PropertyKey
    {
        fmtid = new Guid("A45C254E-DF1C-4EFD-8020-67D146A850E0"),
        pid = 14
    };
}

步骤2:枚举可用麦克风设备

编写方法来获取所有活动的麦克风设备,封装成便于绑定的实体类:

using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

// 麦克风设备实体类
public class MicrophoneDevice
{
    // 设备唯一ID(用于后续录制初始化)
    public string DeviceId { get; set; }
    // 设备友好名称(显示在ComboBox中)
    public string FriendlyName { get; set; }
}

public static class MicrophoneEnumerator
{
    public static List<MicrophoneDevice> GetActiveMicrophones()
    {
        var microphones = new List<MicrophoneDevice>();

        // 创建MMDeviceEnumerator实例
        int hr = CoreAudioInterop.CoCreateInstance(
            ref CoreAudioInterop.MMDeviceEnumeratorClassId,
            IntPtr.Zero,
            1, // CLSCTX_INPROC_SERVER
            ref CoreAudioInterop.IID_IMMDeviceEnumerator,
            out IMMDeviceEnumerator enumerator);

        if (hr != 0)
            return microphones;

        // 枚举所有活动的捕获设备
        hr = enumerator.EnumAudioEndpoints(EDataFlow.eCapture, EDeviceState.Active, out IMMDeviceCollection deviceCollection);
        if (hr != 0)
        {
            Marshal.ReleaseComObject(enumerator);
            return microphones;
        }

        deviceCollection.GetCount(out uint deviceCount);
        for (uint i = 0; i < deviceCount; i++)
        {
            deviceCollection.Item(i, out IMMDevice device);
            // 获取设备ID
            device.GetId(out string deviceId);
            // 获取设备属性存储
            device.GetPropertyStore(0, out IPropertyStore propStore);
            // 获取设备友好名称
            propStore.GetValue(ref CoreAudioInterop.PKEY_Device_FriendlyName, out PropVariant propVariant);
            string friendlyName = Marshal.PtrToStringUni(propVariant.pwszVal);

            microphones.Add(new MicrophoneDevice
            {
                DeviceId = deviceId,
                FriendlyName = friendlyName
            });

            // 释放COM资源,避免内存泄漏
            Marshal.ReleaseComObject(propVariant.pwszVal);
            Marshal.ReleaseComObject(propStore);
            Marshal.ReleaseComObject(device);
        }

        // 释放剩余COM资源
        Marshal.ReleaseComObject(deviceCollection);
        Marshal.ReleaseComObject(enumerator);

        return microphones;
    }
}

步骤3:绑定到WPF的ComboBox

在WPF窗口的代码后台或ViewModel中获取麦克风列表并绑定到ComboBox:

XAML部分:

<Window x:Class="WpfMicrophoneSelector.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="麦克风选择器" Height="200" Width="300">
    <Grid>
        <ComboBox x:Name="micComboBox" 
                  Width="250" 
                  Margin="20"
                  DisplayMemberPath="FriendlyName"
                  SelectedValuePath="DeviceId"/>
    </Grid>
</Window>

C#代码后台:

public MainWindow()
{
    InitializeComponent();
    // 加载麦克风列表
    var microphones = MicrophoneEnumerator.GetActiveMicrophones();
    micComboBox.ItemsSource = microphones;
    // 默认选中第一个设备(如果有的话)
    if (microphones.Any())
        micComboBox.SelectedIndex = 0;
}

后续录制的扩展思路

当用户选择麦克风后,你可以通过选中的DeviceId,继续使用Core Audio的IAudioClient接口来初始化音频捕获会话,实现无第三方库的语音录制。如果需要这部分的代码,可以再问我!

注意事项

  • 该方案支持Windows Vista及以上所有版本,兼容性良好;
  • 务必记得释放所有COM对象,避免内存泄漏;
  • 如果设备列表需要动态更新(比如插拔麦克风),可以监听Core Audio的设备通知事件,不过这部分相对复杂,普通场景下定期刷新列表即可。

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:58:17