Страницы

Поиск по вопросам

понедельник, 8 июля 2019 г.

Получение полного имени устройства аудиозахвата

ICreateDevEnum devEnum; IEnumMoniker enumMoniker; IMoniker[] monikers = new IMoniker[1]; IPropertyBag propBag;
devEnum = (ICreateDevEnum)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_SystemDeviceEnum)); devEnum.CreateClassEnumerator(CLSID_AudioInputDeviceCategory, out enumMoniker, 0);
while (enumMoniker.Next(1, monikers, IntPtr.Zero) == 0) {
object variant; object propBagObj;
monikers[0].BindToStorage(null, null, ref IPropertyBagGuid, out propBagObj); propBag = (IPropertyBag)propBagObj;
if (propBag.Read("FriendlyName", out variant, null) == 0) System.Windows.MessageBox.Show(variant.ToString() + " (" + variant.ToString().Length + ")"); }
Под Windows 10 этот код у меня работает прекрасно и выводит полное имя микрофона (44 символа). На Windows 7 имя выводится не полное, а лишь первые 31 символ.
Подскажите, что может быть причиной такого поведения и как это починить (возможно, аналогичный более универсальный код)?
На устройствах, сразу скажу, стоят разные версии DirectX (12 (где всё работает) и 11).


Ответ

Попробуйте сделать это через 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.
Ссылки по теме:
Windows Multimedia Device API
Описание понятия Endpoint ID
Взаимодействие с Wave Audio API

Комментариев нет:

Отправить комментарий