суббота, 19 мая 2018 г.

HtmlDocumentFactory library - Working with HTML documents in .NET

Sometimes there's a need to create or parse HTML documents programmatically. Windows Forms framework in .NET has a nice HtmlDocument class for HTML manupulation. However, it can only work with documents from WebBrowser control, and there's no public constructor to create new HtmlDocument instance from scratch or from arbitrary HTML content. However, it can be done by accessing MSHTML COM interfaces directly and invoking private HtmlDocument constructor via reflection. I have implemented this approach in HtmlDocumentFactory library.

Source code: https://github.com/MSDN-WhiteKnight/HtmlDocumentFactory

Download binaries: https://yadi.sk/d/lPk5bGov3WCXsD

Usage

The library consists of a single static class HtmlLib.HtmlDocumentFactory that provides helper methods for creating and destroying HTML documents and converting them to strings. Use CreateHtmlDocument method to create a new document, modify its content via HtmlDocument methods, use HtmlDocumentToString method to obtain whole HTML string, then call ReleaseHtmlDocument when you no longer need it.


среда, 27 сентября 2017 г.

Получение информации об устройствах вывода звука в Windows (с#)

Иногда возникает необходимость определения присутствующих в системе устройств вывода звука, и какие из них активны в текущий момент. Для этих целей можно использовать Windows Multimedia Device API. Его интерфейс IMMDeviceEnumerator позволяет получить список устройств вывода звука (audio endpoints), их тип и различную информацию о них. Однако, в современных звуковых картах часто динамики и наушники представлены одним устройством с точки зрения системы, и выбор, на какое устройство выводить звук, осуществляется в настройках специальной утилиты производителя (или автоматически). В таком случае для определения путей вывода звука в кокретной ситуации можно использовать Device Topology API . С помощью него можно получить информацию о разъемах звукового адаптера и их состоянии.

Для доступа к этим API из c# нам понадобятся декларации неуправляемых структур, интерфейсов и т.п. из библиотек Mmdeviceapi.h и Devicetopology.h, а также COM-объекта Property Store:

using System;

using System.Collections.Generic;

using System.Text;

using System.Runtime.InteropServices;

using System.IO;

using System.Runtime.CompilerServices;

 

namespace com_test

{

    class Native

    {

        [DllImport("ole32.Dll")]

        static public extern uint CoCreateInstance(ref Guid clsid,

        [MarshalAs(UnmanagedType.IUnknown)] object inner, uint context, ref Guid uuid,

        [MarshalAs(UnmanagedType.IUnknown)] out object rReturnedComObject);

 

        //************************************************************************

        [DllImport("ole32.dll")]

        private extern static int PropVariantClear(ref PropVariant pvar);

 

        public const int DEVICE_STATE_ACTIVE = 0x00000001;

        public const int DEVICE_STATE_DISABLE = 0x00000002;

        public const int DEVICE_STATE_NOTPRESENT = 0x00000004;

        public const int DEVICE_STATE_UNPLUGGED = 0x00000008;

        public const int DEVICE_STATEMASK_ALL = 0x0000000f;

               

 

        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);

 

    }

 

    enum EndpointFormFactor

    {

        RemoteNetworkDevice = 0,

        Speakers = ( RemoteNetworkDevice + 1 ) ,

        LineLevel           = ( Speakers + 1 ) ,

        Headphones       = ( LineLevel + 1 ) ,

        Microphone        = ( Headphones + 1 ) ,

        Headset  = ( Microphone + 1 ) ,

        Handset  = ( Headset + 1 ) ,

        UnknownDigitalPassthrough    = ( Handset + 1 ) ,

        SPDIF    = ( UnknownDigitalPassthrough + 1 ) ,

        DigitalAudioDisplayDevice      = ( SPDIF + 1 ) ,

        UnknownFormFactor    = ( DigitalAudioDisplayDevice + 1 ) ,

        EndpointFormFactor_enum_count       = ( UnknownFormFactor + 1 )

    };

 

    enum EPcxConnectionType

    {

        eConnTypeUnknown=0,

        eConnType3Point5mm,

        eConnTypeQuarter,

        eConnTypeAtapiInternal,

        eConnTypeRCA,

        eConnTypeOptical,

        eConnTypeOtherDigital,

        eConnTypeOtherAnalog,

        eConnTypeMultichannelAnalogDIN,

        eConnTypeXlrProfessional,

        eConnTypeRJ11Modem,

        eConnTypeCombination

    } ;

 

    enum EPcxGeoLocation

    {

        eGeoLocRear = 0x1,

        eGeoLocFront,

        eGeoLocLeft,

        eGeoLocRight,

        eGeoLocTop,

        eGeoLocBottom,

        eGeoLocRearPanel,

        eGeoLocRiser,

        eGeoLocInsideMobileLid,

        eGeoLocDrivebay,

        eGeoLocHDMI,

        eGeoLocOutsideMobileLid,

        eGeoLocATAPI,

        eGeoLocNotApplicable,

        eGeoLocReserved6,

        EPcxGeoLocation_enum_count

    } ;

 

    public enum EDataFlow

    {

        eRender,

        eCapture,

        eAll,

        EDataFlow_enum_count

    }

 

    public enum ERole

    {

        eConsole,

        eMultimedia,

        eCommunications,

        ERole_enum_count

    }

 

    public enum CLSCTX

    {

        CLSCTX_INPROC_SERVER = 0x1,

        CLSCTX_INPROC_HANDLER = 0x2,

        CLSCTX_LOCAL_SERVER = 0x4,

        CLSCTX_REMOTE_SERVER = 0x10,

        CLSCTX_SERVER = (CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER |

                                                 CLSCTX_REMOTE_SERVER),

        CLSCTX_ALL = (CLSCTX_INPROC_HANDLER | CLSCTX_SERVER)

    };

 

    //Windows Core Audio API declarations

    //Источник: http://www.java2s.com/Code/CSharp/Windows/SoundUtils.htm

 

    [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"),

        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

    public interface IMMDeviceCollection

    {      

        int GetCount(ref uint pcDevices);

        int Item(uint nDevice, [Out, MarshalAs(UnmanagedType.Interface)] out object ppDevice);

    }

 

    [Guid("D666063F-1587-4E43-81F1-B948E807363F"),

    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

    public interface IMMDevice

    {

        int Activate(ref Guid iid, uint dwClsCtx, IntPtr pActivationParams,

            [Out, MarshalAs(UnmanagedType.Interface)] out object ppInterface);

 

        int OpenPropertyStore(int stgmAccess, [Out, MarshalAs(UnmanagedType.Interface)] out object ppProperties);

 

        int GetId(ref StringBuilder ppstrId);

 

        int GetState(ref int pdwState);

    }

 

    [ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]

    class MMDeviceEnumerator

    {

    }

 

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"),    

    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

    public interface IMMDeviceEnumerator

    {

 

         int EnumAudioEndpoints(EDataFlow dataFlow, int dwStateMask,

             [Out, MarshalAs(UnmanagedType.Interface)] out object ppDevices);

 

         int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role,

             [Out, MarshalAs(UnmanagedType.Interface)] out object ppEndpoint);

               

         int  GetDevice(string pwstrId, ref IntPtr ppDevice);

 

         int  RegisterEndpointNotificationCallback(IntPtr pClient);

 

         int  UnregisterEndpointNotificationCallback(IntPtr pClient);

    }

     

 

    //*********** Property store *****************************

 

    //Источник: https://blogs.msdn.microsoft.com/adamroot/2008/04/11/interop-with-propvariants-in-net/

 

    [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)

        {

        }

    }

   

    [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_I2:

                        return iVal;

                    case VarEnum.VT_I4:

                    case VarEnum.VT_INT:

                        return lVal;

                    case VarEnum.VT_UI4:

                    case VarEnum.VT_I8:

                        return hVal;

                    case VarEnum.VT_R4:

                        return fltVal;

                    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)

                        {

                            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("0x"+vt.ToString("X4") + " type not supported");

                }

            }

        }

 

    }

    //*****************************************************

       

    //Device Topology declarations

 

    [Guid("2A07407E-6497-4A18-9787-32F79BD0D98F"),

    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

    public interface IDeviceTopology

    {

        int GetConnectorCount([Out] out int pConnectorCount);

        int GetConnector(int nIndex, out IConnector ppConnector);

        int GetSubunitCount([Out] out int pCount);

        int GetSubunit(int nIndex, out /*ISubunit*/object ppSubunit);

        int GetPartById(int nId, out IPart ppPart);

        int GetDeviceId([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppwstrDeviceId);

        int GetSignalPath(IPart pIPartFrom, IPart pIPartTo, bool bRejectMixedPaths, /*IPartsList*/object ppParts);

    }

 

   

    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown),

    Guid("9c2c4058-23f5-41de-877a-df3af236a09e")]

    public interface IConnector

    {

        int GetType(out int pType);

        int GetDataFlow(out EDataFlow dataFlow);

        int ConnectTo([In] IConnector connector);

        int Disconnect();

        int IsConnected(out bool pbConnected);       

        int GetConnectedTo([MarshalAs(UnmanagedType.Interface)] out object ppConTo);

        int GetConnectorIdConnectedTo(out string ppwstrConnectorId);

        int GetDeviceIdConnectedTo(out string ppwstrDeviceId);

    }

 

    [Guid("AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9"),

    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

    public interface IPart

    {

        int GetName(out StringBuilder ppwstrName);

        int GetLocalId(out int pnId);

        int GetGlobalId(out StringBuilder ppwstrGlobalId);

        int GetPartType(out int pPartType);

        int GetSubType(out Guid pSubType);

        int GetControlInterfaceCount(out uint pCount);

 

        int GetControlInterface(int nIndex, out /*IControlInterface*/ object ppFunction);

        int EnumPartsIncoming(out /*IPartsList[]*/object ppParts);

        int EnumPartsOutgoing(out /*IPartsList[]*/object ppParts);

              

        int GetTopologyObject([Out,MarshalAs(UnmanagedType.Interface)] out object ppTopology);

 

        int Activate(uint dwClsContext,

                 ref Guid refiid,

                 [MarshalAs(UnmanagedType.Interface)]

             out object interfacePointer);

        int RegisterControlChangeCallback(ref Guid riid, /*IControlChangeNotify*/object pNofity);

        int UnregisterControlChangeCallback(/*IControlChangeNotify*/object pNotify);

    }

 

    [ComVisible(false)]

    [ComImport,

    InterfaceType(ComInterfaceType.InterfaceIsIUnknown),

    Guid("4509F757-2D46-4637-8E62-CE7DB944F57B")]

    public interface IKsJackDescription

    {

        int GetJackCount(out uint jacks);

        int GetJackDescription(uint jack, ref KSJACK_DESCRIPTION pDescription);

    }

 

    [StructLayout(LayoutKind.Sequential)]

    public struct KSJACK_DESCRIPTION

    {

        public uint ChannelMapping;

        public uint Color;

        public uint ConnectionType;

        public uint GeoLocation;

        public uint GenLocation;

        public uint PortConnection;

        public uint IsConnected;

    } ;

 

}

 

Для вывода информации создадим форму в приложении WinForms с элементами TextBox и FlowLayoutPanel.

 

Создадим COM-объект MMDeviceEnumerator и обяъвим необходимые интерфейсы:

 

MMDeviceEnumerator devenum = new MMDeviceEnumerator();// CoClass

IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)devenum;

IMMDeviceCollection deviceCollection = null;

IMMDevice dev = null;

IMMDevice defDevice=null;

IPropertyStore propertyStore=null;

 

Устройство выода звука по умолчанию и его FriendlyName можно получить так:

 

deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole, out o);

defDevice = o as IMMDevice;

 

defDevice.OpenPropertyStore(0/*STGM_READ*/, out o);               

propertyStore = o as IPropertyStore;

PropVariant name = new PropVariant();

propertyStore.GetValue(Native.PKEY_Device_FriendlyName, out name);

 

Для получения списка всех устройств используем следующий код:

 

deviceEnumerator.EnumAudioEndpoints(EDataFlow.eRender, 0x0000000F /*DEVICE_STATEMASK_ALL*/, out o);               

deviceCollection = o as IMMDeviceCollection;

uint deviceCount = 0;

deviceCollection.GetCount(ref deviceCount);

 

Для вывода информации по устройству создадим метод PrintDevice

 

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);

            StringBuilder id = new StringBuilder(500);

            object o = null;

           

            int state = 0;

            uint con_count = 0;

 

            FlowLayoutPanel pan = new FlowLayoutPanel();

           

            pan.Width = 390;

            pan.Height = 100;

            pan.FlowDirection = FlowDirection.TopDown;

            pan.WrapContents = false;

 

            try

            {

 

                //*********

                id = new StringBuilder(500);

                dev.GetId(ref id);               

                //*********

 

                //имя устройства

                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());

 

                TextBox tb = new TextBox();

                tb.Text = friendlyName.Value.ToString();

                tb.Width = pan.Width - 10;

                tb.ReadOnly = true;

                pan.Controls.Add(tb);

 

                Label lab;

                lab = new Label(); lab.AutoSize = true;

                lab.Text = "ID: "+id.ToString(); pan.Controls.Add(lab);

 

                //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());

 

                lab = new Label(); lab.AutoSize = true;

                lab.Text = "Form factor: " + f.ToString();pan.Controls.Add(lab);

 

                //состояние устройства

                dev.GetState(ref state);               

 

                string str="";

                switch (state)

                {

                    case Native.DEVICE_STATE_ACTIVE: str=("State: Active"); break;

                    case Native.DEVICE_STATE_DISABLE: str = ("State: Disabled"); break;

                    case Native.DEVICE_STATE_NOTPRESENT: str = ("State: Not present"); break;

                    case Native.DEVICE_STATE_UNPLUGGED: str = ("State: Unplugged"); break;

                }

                res.AppendLine(str);

 

                lab = new Label(); lab.AutoSize = true;

                lab.Text = str; pan.Controls.Add(lab);

 

                /* 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, iidKsJackDescription, out o);                   

                    pJackDesc = (IKsJackDescription)o;

 

                    if (pJackDesc != null)

                    {

                        con_count = 0;

                        pJackDesc.GetJackCount(out con_count);

                        if (con_count > 0)

                        {

                            StringBuilder sb ;

                            Bitmap bmp;

 

                            //отобразить информацию о разъемах

                            for (uint i = 0; i < con_count; i++)

                            {

                                pJackDesc.GetJackDescription(i, ref desc);                               

                                                            

                                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;

                                }

                                if (desc.IsConnected == 0) sb.Append(" (Отключен)");

 

                                res.AppendLine(sb.ToString());

                                bmp = new Bitmap(pan.Width , 20);

                               

                                //генерация изображения

                                Graphics g = Graphics.FromImage(bmp);

                                using (g)

                                {

                                    //цвет получается сочетанием desc.Color и 0xFF для альфа-канала

                                    Pen pen = new Pen(Color.FromArgb((int)(desc.Color | 0xFF000000)));

                                    g.FillRectangle(SystemBrushes.Control, 0.0f, 0.0f, bmp.Width, bmp.Height);

                                    using (pen)

                                    {

                                        pen.Width = 5;

                                        g.DrawEllipse(pen, 2, 2, 15, 15);//нарисовать круг, используя цвет разъема

                                        Font fnt = new System.Drawing.Font(new FontFamily("Arial"), 9.0f);

                                        g.DrawString(sb.ToString(),fnt, Brushes.Black, 21, 0,StringFormat.GenericDefault);

                                    }

                                }

 

                                PictureBox pb = new PictureBox();

                                pb.Image = bmp;

                                pb.SizeMode = PictureBoxSizeMode.AutoSize;

                                pan.Controls.Add(pb);

                               

                            }//end for

                        }

                        else

                        {

                            res.AppendLine("* Нет разъемов");

                            lab = new Label(); lab.Text = "* Нет разъемов";

                            lab.AutoSize = true;

                            pan.Controls.Add(lab);

                        }

 

                    }

                    else

                    {

                        res.AppendLine("* Не удалось получить информацию о разъемах");

                        lab = new Label(); lab.Text = "* Не удалось получить информацию о разъемах";

                        lab.AutoSize = true;

                        pan.Controls.Add(lab);

                    }

                    ;

 

 

                }

                catch (COMException ex)

                {

                    if ((uint)ex.HResult == 0x80070490 /*E_NOTFOUND*/)

                    {

                        res.AppendLine("Не подключен");

                        lab = new Label(); lab.Text = "* Не подключен";

                        lab.AutoSize = true;

                        pan.Controls.Add(lab);

                    }

                    else

                    {

                        res.AppendLine("COM error: " + ex.Message);

                        lab = new Label(); lab.Text = "COM error: " + ex.Message;

                        lab.AutoSize = true;

                        pan.Controls.Add(lab);

                    }

                }

                catch (Exception ex)

                {

                    res.AppendLine("Не удалось получить информацию о разъемах: " + ex.Message);

                    lab = new Label(); lab.Text = "Не удалось получить информацию о разъемах: " + ex.Message;

                    lab.AutoSize = true;

                    pan.Controls.Add(lab);

                }               

 

            }

            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; }               

            }

 

            int h = 0;

            foreach (Control c in pan.Controls){if (c.Bottom > h) h = c.Bottom;}

            pan.Height = h+10;

 

            flowLayoutPanel1.Controls.Add(pan);          

            return res.ToString();

}

 

Сначала мы с помощью IMMDevice и IPropertyStore получаем общие свойства устройств. Затем получаем IDeviceTopology и находим объект IConnector, который символизирует разъемы нашего устройства. Из него получаем IKsJackDescription, который выдает список разъемов. Для разъемов можно узнать тип порта, его расположение (даже его цвет), а также статус (подключен/не подключен). Следует отметить, что не у всех разъемов есть электрический детектор подключения - для них статус всегда будет "Подключено". Используя эти данные, генерируем картинки разъемов и вставляем во FlowLayoutPanel.

 

Используем данную функцию для вывода всех устройств:

 

textBox1.Text = "Устройство по умолчанию : " + name.Value.ToString();

flowLayoutPanel1.Controls.Clear();

 

for (uint i = 0; i < deviceCount; i++)//вывести информацию о устройствах

{

        deviceCollection.Item(i, out o);

        dev = o as IMMDevice;

 

        PrintDevice(dev);                  

 

        //clean up resources

        if (dev != null) { Marshal.ReleaseComObject(dev); dev = null; }                                       

}

 

Окончательная очистка ресурсов COM:

 

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 (defDevice != null) Marshal.ReleaseComObject(defDevice);

if (propertyStore != null) Marshal.ReleaseComObject(propertyStore);

 

Результат работы программы представлен на рисунке

 

 

Полный проект (VS2012)

Обновлено 28.09.2017: исправлена ошибка, приводящая к Access Violation на x86-системах

вторник, 19 сентября 2017 г.

Обновление Small Media Player - v2.0

Вышла новая версия проигрывателя Small Media Player.

Список изменений

  • Добавлена поддержка Windows 10
  • Изменен интерфейс, теперь список воспроизведения и обложка автоматически изменяют размер при изменении размеров главного окна
  • В главном окне добавлена краткая информация о форматах аудио и видео в воспроизводимом файле
  • Добавлено выделение стрелкой текущего файла в списке воспроизведения
  • Опция отображения фонового рисунка заменена на аналогичную опцию отображения рисунка при отсутствии обложки
  • Настройки программы и список воспроизведения теперь хранятся в папке "Мои документы"
  • Если включено запоминание позиции, запоминается также громкость.
  • m4a добавлен в список обрабатываемых расширений
  • Программа теперь собрана с помощью Visual C++ 2012


Скачать Small Media Player 2.0