MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Отобразить подключенное звуковое устройство"
Answer 724444
Введение
Раз уж подняли тему звуковых устройств и отображения их имени, имеет смысл написать ответ и здесь. Если динамики и наушники представлены в системе отдельными конечными устройствами (Endpoint), задача решается довольно просто с помощью любого аудио-API. С помощью того-же BASS.NET это делается примерно так:
BASS_DEVICEINFO[] devices=Bass.BASS_GetDeviceInfos(); foreach(BASS_DEVICEINFO dev in devices) { if(dev.IsDefault)textBox1.Text=("Текущее устройство вывода звука: "+dev.name+" - "+dev.type.ToString()); }
Если нужно реагировать на изменения в устройствах в реальном времени, можно воспользоваться техникой, описанной здесь, только вместо DBT_DEVICEARRIVAL обрабатывать DBT_DEVNODES_CHANGED (0x0007).
Однако, это не всегда работает. Проблема в том, что в современных аудио-адаптерах обычно все аналоговые выходы представлены для системы одним конечным устройством, и переключение между динамиками и наушниками осуществляется либо автоматически драйвером, либо через утилиту производителя. В этом случае код выше бесполезен, так как он всегда будет выдавать одно и то же. Надежного решения тут нет, так как не все разъемы обладают системой детектирования кабеля, но можно попробовать использовать для определения активных разъемов Multimedia API и Device Topology API.
Используемые API
Для решения задачи нам понадобится Windows Multimedia Device API - для получения текущего конечного устройства вывода, и Device Topology API - для получения информации о разъемах. Необходимые классы определены в файлах
Objbase.h
,Shobjidl.h
,MMDeviceAPI.h
иDevicetopology.h
. Поскольку это все COM-объекты, нам понадобятся декларации для использования их в управляемом коде. Их можно взять в данной статье, или получить Interop Assemblies самостоятельно с помощью средств Windows SDK. Для этого используются примерно такие команды:midl /out c:\tmp /header "C:\Program Files (x86)\Microsoft SDKs...\Include\mmdeviceapi.h" "C:\Program Files (x86)\Microsoft SDKs...\Include\mmdeviceapi.idl"
tlbimp /out:MMDevAPI.Interop.dll mmdeviceapi.tlb
Первая получает файл IDL, а вторая - сборку для подключения непосредственно к коду .NET.
Пример программы
Для вывода текущего устройства и его разъемов можно использовать такой код:
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 { //вывод информации об устройстве string PrintDevice(IMMDevice dev) { IPropertyStore propertyStore = null; IDeviceTopology pDeviceTopology = null; IConnector pConnFrom = null; IConnector pConnTo = null; IPart pPart = null; IKsJackDescription pJackDesc = null; KSJACK_DESCRIPTION desc = new KSJACK_DESCRIPTION(); StringBuilder res = new StringBuilder(300); object o = null; int state = 0; uint con_count = 0; try { //имя устройства dev.OpenPropertyStore(0/*STGM_READ*/, out o); propertyStore = o as IPropertyStore; PropVariant friendlyName = new PropVariant(); propertyStore.GetValue(Native.PKEY_Device_FriendlyName, out friendlyName); res.AppendLine(friendlyName.Value.ToString()); //form factor PropVariant FormFactor = new PropVariant(); propertyStore.GetValue(Native.PKEY_AudioEndpoint_FormFactor, out FormFactor); EndpointFormFactor f=EndpointFormFactor.UnknownFormFactor; Enum.TryParse<EndpointFormFactor>(FormFactor.Value.ToString(), out f); res.AppendLine("Form factor: " + f.ToString()); //состояние устройства dev.GetState(ref state); string str=""; switch (state) { case Native.DEVICE_STATE_DISABLE: str = ("Отключено"); break; case Native.DEVICE_STATE_NOTPRESENT: str = ("Отсутствует"); break; case Native.DEVICE_STATE_UNPLUGGED: str = ("Отключено"); break; } if(str!="")res.AppendLine(str); /* DEVICE TOPOLOGY */ Guid iidDeviceTopology = new Guid("2A07407E-6497-4A18-9787-32F79BD0D98F"); dev.Activate(ref iidDeviceTopology, (uint)CLSCTX.CLSCTX_ALL, IntPtr.Zero, out o); pDeviceTopology = o as IDeviceTopology; pDeviceTopology.GetConnector(0, out pConnFrom); try { o = null; pConnFrom.GetConnectedTo(out o); pConnTo = o as IConnector; pPart = (IPart)pConnTo;//QueryInterface Guid iidKsJackDescription = new Guid("4509F757-2D46-4637-8E62-CE7DB944F57B"); pPart.Activate((uint)CLSCTX.CLSCTX_INPROC_SERVER, ref iidKsJackDescription, out o); pJackDesc = (IKsJackDescription)o; if (pJackDesc != null) { con_count = 0; pJackDesc.GetJackCount(out con_count); if (con_count > 0) { StringBuilder sb ; //отобразить информацию о разъемах for (uint i = 0; i < con_count; i++) { pJackDesc.GetJackDescription(i, ref desc); if (desc.IsConnected == 0) continue; sb= new StringBuilder(100); EPcxConnectionType con_type = (EPcxConnectionType)desc.ConnectionType; EPcxGeoLocation loc = (EPcxGeoLocation)desc.GeoLocation; res.Append("* "); switch (con_type) { case EPcxConnectionType.eConnType3Point5mm: sb.Append("Разъем 3.5 мм "); break; case EPcxConnectionType.eConnTypeAtapiInternal: sb.Append("Разъем ATAPI "); break; case EPcxConnectionType.eConnTypeRCA: sb.Append("Разъем RCA "); break; case EPcxConnectionType.eConnTypeQuarter: sb.Append("Разъем 1/2 дюйма "); break; case EPcxConnectionType.eConnTypeOtherAnalog: sb.Append("Аналоговый разъем "); break; case EPcxConnectionType.eConnTypeOtherDigital: sb.Append("Цифровой разъем "); break; default: sb.Append(con_type.ToString()+" "); break; } switch (loc) { case EPcxGeoLocation.eGeoLocFront: sb.Append("- Передняя панель"); break; case EPcxGeoLocation.eGeoLocRear: sb.Append("- Задняя панель"); break; case EPcxGeoLocation.eGeoLocHDMI: sb.Append("- HDMI"); break; case EPcxGeoLocation.eGeoLocNotApplicable: sb.Append("- Расположение не определено"); break; default: sb.Append("- "+loc.ToString()); break; } res.AppendLine(sb.ToString()); }//end for } else { res.AppendLine("* Нет разъемов"); } } else { res.AppendLine("* Не удалось получить информацию о разъемах"); } ; } catch (COMException ex) { if ((uint)ex.HResult == 0x80070490 /*E_NOTFOUND*/) { res.AppendLine("Не подключен"); } else { res.AppendLine("COM error: " + ex.Message); } } catch (Exception ex) { res.AppendLine("Не удалось получить информацию о разъемах: " + ex.Message); } } finally { //clean up resources if (dev != null) Marshal.ReleaseComObject(dev); if (propertyStore != null) Marshal.ReleaseComObject(propertyStore); if (pDeviceTopology != null) { Marshal.ReleaseComObject(pDeviceTopology); pDeviceTopology = null; } if (pConnFrom != null) { Marshal.ReleaseComObject(pConnFrom); pConnFrom = null; } if (pConnTo != null) { Marshal.ReleaseComObject(pConnTo); pConnTo = null; } if (pPart != null) { Marshal.ReleaseComObject(pPart); pPart = null; } if (pJackDesc != null) { Marshal.ReleaseComObject(pJackDesc); pJackDesc = null; } } return res.ToString(); } public Form1() { InitializeComponent(); } private void button_Click(object sender, EventArgs e) { MMDeviceEnumerator devenum = new MMDeviceEnumerator();//Create CoClass IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)devenum; IMMDevice defDevice = null; IPropertyStore propertyStore = null; try { object o = null; // * default device * deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole, out o); defDevice = o as IMMDevice; textBox1.Text = "Текущее устройство вывода звука: " + Environment.NewLine + Environment.NewLine; textBox1.Text += PrintDevice(defDevice); textBox1.Select(0, 0); } catch (Exception ex) { MessageBox.Show(ex.ToString()); } finally { //clean up resources if (devenum != null) Marshal.ReleaseComObject(devenum); if (deviceEnumerator != null) Marshal.ReleaseComObject(deviceEnumerator); if (defDevice != null) Marshal.ReleaseComObject(defDevice); if (propertyStore != null) Marshal.ReleaseComObject(propertyStore); } } } }
Здесь интерфейс
IMMDeviceEnumerator
используется для определения текущего устройства вывода. Затем делается попытка для него получитьIKsJackDescription
и вывести для всех активных разъемов тип и место размещения. Понятно, невозможно достоверно отличить динамики от наушников (потому что разъемы у них совершенно одинаковые), но какие-то выводы из этого можно извлечь.
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.