1. 开头
最一开始写这个的目的是为了控制播放和输出设备的音量,开启静音等。但是,在使用C#调用Com的时候发现了一个神奇的事情。
// IMMDeviceEnumerator 为接口
IMMDeviceEnumerator mMDeviceEnumerator = new IMMDeviceEnumerator();
没错,可以实例化一个接口?神奇的操作😃。
所以,这篇的主要目的是写为什么可以实例化一个接口这么神奇的操作。接下来会写怎么操作音量,还有怎么选择默认设备😋
想看实现获取音频设备列表 代码的直接看 下面, 想看为什么能实例化一个接口的可以直接看最后。
2. 功能代码实现
Com接口的相关P/Invoke 封装看附录
static PROPERTYKEY PKEY_DeviceInterface_FriendlyName = new PROPERTYKEY() {
fmtid = new Guid(0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22), pid = 2 }; //PKEY_DeviceInterface_FriendlyName
static PROPERTYKEY PKEY_Device_FriendlyName = new PROPERTYKEY() {
fmtid = new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), pid = 14 }; //PKEY_Device_FriendlyName
static void Main(string[] args)
{
IMMDeviceEnumerator mMDeviceEnumerator = new IMMDeviceEnumerator();
IMMDeviceCollection deviceCollection;
mMDeviceEnumerator.EnumAudioEndpoints(EDataFlow.eRender, DeviceState.All, out deviceCollection);
uint count = 0;
deviceCollection.GetCount(out count);
Console.WriteLine(count);
for (uint i = 0; i < count; i++)
{
deviceCollection.Item(i, out IMMDevice device);
device.GetId(out string id);
Console.WriteLine(id);
device.GetState(out uint state);
Console.WriteLine(state);
device.OpenPropertyStore(StorageAccessMode.Read, out IPropertyStore property);
PROPVARIANT pROPVARIANT = new PROPVARIANT();
property.GetValue(ref PKEY_Device_FriendlyName, out pROPVARIANT);
Console.WriteLine(pROPVARIANT.Value);
}
}
3 关于神奇操作的分析
最简单的一句话 就是 CoClass 这个attribute 使用这个之后 编译器会将
IMMDeviceEnumerator mMDeviceEnumerator = new IMMDeviceEnumerator();
理解为
IMMDeviceEnumerator mMDeviceEnumerator = (IMMDeviceEnumerator) Activator.CreateInstance(Type.GetTypeFromCLSID
(new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")));
如果,这篇文章的理解有错误,欢迎指正。
相关解析可以参考:
How does the C# compiler detect COM types?。
FAKING COM TO FOOL THE C# COMPILER。
附录
相关P/Invoke封装
#region Struct
public struct WAVEFORMATEX
{
ushort wFormatTag; /* format type */
ushort nChannels; /* number of channels (i.e. mono, stereo...) */
uint nSamplesPerSec; /* sample rate */
uint nAvgBytesPerSec; /* for buffer estimation */
ushort nBlockAlign; /* block size of data */
ushort wBitsPerSample; /* number of bits per sample of mono data */
ushort cbSize; /* the count in bytes of the size of */
/* extra information (after cbSize) */
}
[StructLayout(LayoutKind.Sequential)]
public struct PROPERTYKEY
{
public Guid fmtid;
public uint pid;
}
public struct BLOB
{
public uint cbSize;
/* [size_is] */
public IntPtr/* BYTE* */ pBlobData;
}
[StructLayout(LayoutKind.Explicit)]
public struct PROPVARIANT
{
/// <summary>
/// Value type tag.
/// </summary>
[FieldOffset(0)] public short vt;
/// <summary>
/// Reserved1.
/// </summary>
[FieldOffset(2)] public short wReserved1;