MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Получение полного имени устройства аудиозахвата"
Answer 724245
Попробуйте сделать это через Multimedia API. У меня нормально выводит FriendlyName больше 40 символов.
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.IO; using System.Runtime.CompilerServices; namespace com_test { /*Объявления COM-интерфейсов*/ class Native { public static PROPERTYKEY PKEY_Device_FriendlyName = new PROPERTYKEY(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); public static PROPERTYKEY PKEY_AudioEndpoint_FormFactor = new PROPERTYKEY(0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, 0); } //http://www.java2s.com/Code/CSharp/Windows/SoundUtils.htm [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IMMDeviceCollection { //HRESULT GetCount([out, annotation("__out")] UINT* pcDevices); int GetCount(ref uint pcDevices); //HRESULT Item([in, annotation("__in")]UINT nDevice, [out, annotation("__out")] IMMDevice** ppDevice); int Item(uint nDevice, [Out, MarshalAs(UnmanagedType.Interface)] out object ppDevice); } [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IMMDevice { //HRESULT Activate([in, annotation("__in")] REFIID iid, [in, annotation("__in")] DWORD dwClsCtx, [in,unique, annotation("__in_opt")] PROPVARIANT* pActivationParams, [out,iid_is(iid), annotation("__out")] void** ppInterface); int Activate(ref Guid iid, uint dwClsCtx, IntPtr pActivationParams, [Out, MarshalAs(UnmanagedType.Interface)] out object ppInterface); //HRESULT OpenPropertyStore([in, annotation("__in")] DWORD stgmAccess, [out, annotation("__out")] IPropertyStore** ppProperties); int OpenPropertyStore(int stgmAccess, [Out, MarshalAs(UnmanagedType.Interface)] out object ppProperties); //HRESULT GetId([out,annotation("__deref_out")] LPWSTR* ppstrId); int GetId(ref StringBuilder ppstrId); //HRESULT GetState([out, annotation("__out")] DWORD* pdwState); int GetState(ref int pdwState); } [ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumerator { } [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), //[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IMMDeviceEnumerator { //HRESULT EnumAudioEndpoints([in, annotation("__in")] EDataFlow dataFlow, [in, annotation("__in")] DWORD dwStateMask, [out, annotation("__out")] IMMDeviceCollection** ppDevices); int EnumAudioEndpoints(EDataFlow dataFlow, int dwStateMask, [Out, MarshalAs(UnmanagedType.Interface)] out object ppDevices); //HRESULT GetDefaultAudioEndpoint([in, annotation("__in")] EDataFlow dataFlow, [in, annotation("__in")] ERole role, [out, annotation("__out")] IMMDevice** ppEndpoint); int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, [Out, MarshalAs(UnmanagedType.Interface)] out object ppEndpoint); //HRESULT GetDevice([, annotation("__in")]LPCWSTR pwstrId, [out, annotation("__out")] IMMDevice** ppDevice); int GetDevice(string pwstrId, ref IntPtr ppDevice); //HRESULT RegisterEndpointNotificationCallback([in, annotation("__in")] IMMNotificationClient* pClient); int RegisterEndpointNotificationCallback(IntPtr pClient); //HRESULT UnregisterEndpointNotificationCallback([in, annotation("__in")] IMMNotificationClient* pClient); int UnregisterEndpointNotificationCallback(IntPtr pClient); } public enum EDataFlow { eRender, eCapture, eAll, EDataFlow_enum_count } public enum ERole { eConsole, eMultimedia, eCommunications, ERole_enum_count } //*********** Property store ***************************** [ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IPropertyStore { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetCount([Out] out uint cProps); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void GetAt([In] uint iProp, out PROPERTYKEY pkey); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] int GetValue([In] ref PROPERTYKEY key, out PropVariant pv); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void SetValue([In] ref PROPERTYKEY key, [In] ref object pv); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void Commit(); } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct PROPERTYKEY { public Guid fmtid; public uint pid; public PROPERTYKEY(Guid guid, int propertyId) { this.fmtid = guid; this.pid = (uint)propertyId; } public PROPERTYKEY(string formatId, int propertyId) : this(new Guid(formatId), propertyId) { } public PROPERTYKEY(uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h, uint i, uint j, uint k, int propertyId) : this(new Guid((uint)a, (ushort)b, (ushort)c, (byte)d, (byte)e, (byte)f, (byte)g, (byte)h, (byte)i, (byte)j, (byte)k), propertyId) { } } //https://blogs.msdn.microsoft.com/adamroot/2008/04/11/interop-with-propvariants-in-net/ [StructLayout(LayoutKind.Sequential)] public struct PropVariant { ushort vt; ushort wReserved1; ushort wReserved2; ushort wReserved3; IntPtr p; int p2; private byte[] GetDataBytes() { byte[] ret = new byte[IntPtr.Size + sizeof(int)]; if (IntPtr.Size == 4) BitConverter.GetBytes(p.ToInt32()).CopyTo(ret, 0); else if (IntPtr.Size == 8) BitConverter.GetBytes(p.ToInt64()).CopyTo(ret, 0); BitConverter.GetBytes(p2).CopyTo(ret, IntPtr.Size); return ret; } sbyte cVal // CHAR cVal; { get { return (sbyte)GetDataBytes()[0]; } } short iVal // SHORT iVal; { get { return BitConverter.ToInt16(GetDataBytes(), 0); } } int lVal // LONG lVal; { get { return BitConverter.ToInt32(GetDataBytes(), 0); } } long hVal // LARGE_INTEGER hVal; { get { return BitConverter.ToInt64(GetDataBytes(), 0); } } float fltVal // FLOAT fltVal; { get { return BitConverter.ToSingle(GetDataBytes(), 0); } } public object Value { get { switch ((VarEnum)vt) { case VarEnum.VT_I1: return cVal; //case VarEnum.VT_UI1:return bVal; case VarEnum.VT_I2: return iVal; //case VarEnum.VT_UI2: return uiVal; case VarEnum.VT_I4: case VarEnum.VT_INT: return lVal; case VarEnum.VT_UI4: //case VarEnum.VT_UINT: return ulVal; case VarEnum.VT_I8: return hVal; // case VarEnum.VT_UI8: return uhVal; case VarEnum.VT_R4: return fltVal; /*case VarEnum.VT_R8: return dblVal; case VarEnum.VT_BOOL: return boolVal; case VarEnum.VT_ERROR: return scode; case VarEnum.VT_CY: return cyVal; case VarEnum.VT_DATE: return date;*/ case VarEnum.VT_FILETIME: return DateTime.FromFileTime(hVal); case VarEnum.VT_BSTR: return Marshal.PtrToStringBSTR(p); case VarEnum.VT_BLOB: byte[] blobData = new byte[lVal]; IntPtr pBlobData; if (IntPtr.Size == 4) { pBlobData = new IntPtr(p2); } else if (IntPtr.Size == 8) { // In this case, we need to derive a pointer at offset 12, // because the size of the blob is represented as a 4-byte int // but the pointer is immediately after that. pBlobData = new IntPtr(BitConverter.ToInt64(GetDataBytes(), sizeof(int))); } else throw new NotSupportedException(); Marshal.Copy(pBlobData, blobData, 0, lVal); return blobData; case VarEnum.VT_LPSTR: return Marshal.PtrToStringAnsi(p); case VarEnum.VT_LPWSTR: return Marshal.PtrToStringUni(p); case VarEnum.VT_UNKNOWN: return Marshal.GetObjectForIUnknown(p); case VarEnum.VT_DISPATCH: return p; default: throw new NotSupportedException("The type of this variable is not support ('" + vt.ToString() + "')"); } } } } //***************************************************** }
using System; using System.Collections.Generic; using System.Data; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.ComponentModel; namespace com_test { public partial class Form1 : Form { public Form1() { InitializeComponent(); MMDeviceEnumerator devenum = new MMDeviceEnumerator();//Create CoClass IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)devenum; IMMDeviceCollection deviceCollection = null; IMMDevice dev = null; IPropertyStore propertyStore=null; try { object o = null; int hr; hr = deviceEnumerator.EnumAudioEndpoints(EDataFlow.eCapture, 0x0000000F /*DEVICE_STATEMASK_ALL*/, out o); deviceCollection = o as IMMDeviceCollection; uint deviceCount = 0; hr = deviceCollection.GetCount(ref deviceCount); textBox1.Text = ""; for (uint i = 0; i < deviceCount; i++)//print all devices { hr = deviceCollection.Item(i, out o); dev = o as IMMDevice; //get device name hr = dev.OpenPropertyStore(0/*STGM_READ*/, out o); propertyStore = o as IPropertyStore; PropVariant friendlyName=new PropVariant(); hr = propertyStore.GetValue(Native.PKEY_Device_FriendlyName, out friendlyName); //output data textBox1.Text += "#" + (i + 1).ToString() + ": " + friendlyName.Value.ToString(); textBox1.Text += Environment.NewLine; //clean up resources if (dev != null) { Marshal.ReleaseComObject(dev); dev = null; } if (propertyStore != null) { Marshal.ReleaseComObject(propertyStore); propertyStore = null; } } textBox1.Select(0, 0); } finally { //clean up resources if (devenum != null) Marshal.ReleaseComObject(devenum); if (deviceEnumerator != null) Marshal.ReleaseComObject(deviceEnumerator); if (deviceCollection != null) Marshal.ReleaseComObject(deviceCollection); if (dev != null) Marshal.ReleaseComObject(dev); if (propertyStore != null) Marshal.ReleaseComObject(propertyStore); } } } }
Дополнение
Для получения WaveInID устройства, нужно получить Endpoint ID всех WaveIn-устройств и сравнить его с результатом
device.GetID
:class DShow { [DllImport("winmm.dll")] public static extern uint waveInGetNumDevs(); public const uint DRV_RESERVED = 0x0800; public const uint DRV_QUERYFUNCTIONINSTANCEID = (DRV_RESERVED + 17); public const uint DRV_QUERYFUNCTIONINSTANCEIDSIZE = (DRV_RESERVED + 18); public const uint DRV_QUERYDEVICEINTERFACE = (DRV_RESERVED + 12); public const uint DRV_QUERYDEVICEINTERFACESIZE = (DRV_RESERVED + 13); [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "waveInMessage")] public static extern int waveInMessage1(uint hWaveIn, uint msg, ref uint dw1, uint dw2); [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "waveInMessage")] public static extern int waveInMessage2(uint hWaveIn, uint msg, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder dw1, uint dw2); public static uint GetWaveInID(string endpid) { uint num = DShow.waveInGetNumDevs(); StringBuilder sb; uint size = 0; for (uint i = 0; i < num; i++) { size = 0; DShow.waveInMessage1(i, DShow.DRV_QUERYFUNCTIONINSTANCEIDSIZE, ref size, 0); sb = new StringBuilder((int)size + 10); DShow.waveInMessage2(i, DShow.DRV_QUERYFUNCTIONINSTANCEID, sb, size); if (sb.ToString() == endpid) return i; } throw new EntryPointNotFoundException(endpid+" device was not found!"); } }
Дополнение 2
Ну и объяснение первоначальной проблемы: обрезка Friendly name в DirectShow вызвана ограничением в Wave Audio API, который он внутренне использует в Windows 7. В структуре WAVEINCAPS поле под имя устройства имеет фиксированный размер в 32 байта. В Windows 10 видимо принцип работы изменили, так что эта структура не задействована. Также следует отметить, что Wave Audio API начиная с висты объявлен устаревшим, и новые приложения должны вместо него использовать соответствующие функции из Multimedia Device API, WASAPI, Windows Media Foundation или DirectShow.
Ссылки по теме:
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.