суббота, 16 мая 2020 г.

WinForms Excel Library v1.2 update

Download WinForms Excel library 1.2 on Github releases

  • Fix null reference when trying to access active workbook
  • Fix exceptions when trying to delete temp file
  • Disable code that accesses Worksheet.ProtectContents in Excel 2016+
  • Disable APIs that add, remove or rename worksheets and open files in Excel 2016+ due to UI freezes (DisplayStatusBar, DisplayFormulaBar properties - can't be set after Excel is initialized; DeleteSheet, AddSheet, SetSheetName, MoveSheet, NewEmptyWorkbook, OpenFile, AddChart methods now throw NotSupportedException)
  • Fix SetActiveSheet and SaveIntoFile in Excel 2016+
  • Add new properties InitialSheetCount and SourceFile that can be set only before Excel is initialized
  • Add new method AddEmbeddedChart

WinForms Excel Library now works with Excel 2016, but with limited functionality:

  • You cannot add or remove sheets after InitializeExcel is called, but you can set InitialSheetCount before initialization to control sheet count.
  • You cannot open workbook files after InitializeExcel is called, but you can set SourceFile property before initialization to select workbook to open. If this property is empty string, control will be created with new empty workbook
  • You cannot add charts to new sheets, but you can add charts embedded to existing sheet with AddEmbeddedChart method
  • You cannot modify content of protected sheets (on older Excel versions library attempts to unprotect sheet)

суббота, 27 июля 2019 г.

CilBytecodeParser 1.5 released

Changes in new version:

  • Moved CilInstruction.opcodes initialization from static constructor to first FindOpCode call, so it will be only loaded when needed.
  • Added support for instructions referencing 32-bit target branch operands
  • Added special characters escaping when displaying string literals
  • Added CilGraph.EmitTo method with jumps support as well as the corresponding IlGenerator extension method

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

Source code: CilBytecodeParser on GitHub

воскресенье, 24 марта 2019 г.

CilBytecodeParser 1.3 released

CilBytecodeParser 1.3

License: BSD 2.0

Requirements: .NET Framework 3.5+

CilBytecodeParser is a library that reads .NET methods' Common Intermediate Language (CIL) bytecode and converts it into high-level objects or textual CIL representation so they can be easily studied and programmatically processed.


  • Retreving the collection of instructions that form method body
  • Retreiving graph that represents a flow of control between method's instructions
  • Converting CIL code into text
  • Convenient extension method syntax

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

Source code: CilBytecodeParser on GitHub

суббота, 1 сентября 2018 г.

ErrLib 1.0 released

ErrLib is a library to assist in dealing with exceptions and errors in C/C++ Windows Applications

Author: MSDN.WhiteKnight
License: BSD 3-clause
Requirements: Windows Vista (or newer), Visual Studio 2010 (or newer)

  • Simple way to get exception code, error message and stack trace in the handler block for SEH exceptions (as well as unhandled C++ exceptions which cause them)
  • Executing user-defined callback function on unhandled SEH exception
  • Helper macros for converting Win32/COM errors into exceptions
  • Configurable logging: can write diagnostic information into log file, stderr stream, Windows Event Log or other targets
  • Multithreaded: all functionality can be used from different threads independently
  • All string processing in Unicode (wide characters)

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

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

суббота, 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


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



        static public extern uint CoCreateInstance(ref Guid clsid,

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

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




        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














    } ;


    enum EPcxGeoLocation


        eGeoLocRear = 0x1,
















    } ;


    public enum EDataFlow








    public enum ERole








    public enum CLSCTX




        CLSCTX_LOCAL_SERVER = 0x4,

        CLSCTX_REMOTE_SERVER = 0x10,






    //Windows Core Audio API declarations

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




    public interface IMMDeviceCollection


        int GetCount(ref uint pcDevices);

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





    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






    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)






    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




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



                            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;


                        throw new NotSupportedException("0x"+vt.ToString("X4") + " type not supported");








    //Device Topology declarations




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






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





    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,


             out object interfacePointer);

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

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







    public interface IKsJackDescription


        int GetJackCount(out uint jacks);

        int GetJackDescription(uint jack, ref KSJACK_DESCRIPTION pDescription);




    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;



            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;






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



                TextBox tb = new TextBox();

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

                tb.Width = pan.Width - 10;

                tb.ReadOnly = true;



                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;




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




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


                                    case EPcxConnectionType.eConnTypeAtapiInternal: sb.Append("Разъем ATAPI ");


                                    case EPcxConnectionType.eConnTypeRCA: sb.Append("Разъем RCA ");


                                    case EPcxConnectionType.eConnTypeQuarter: sb.Append("Разъем 1/2 дюйма ");


                                    case EPcxConnectionType.eConnTypeOtherAnalog: sb.Append("Аналоговый разъем ");


                                    case EPcxConnectionType.eConnTypeOtherDigital: sb.Append("Цифровой разъем ");


                                    default: sb.Append(con_type.ToString()+" ");




                                switch (loc)


                                    case EPcxGeoLocation.eGeoLocFront: sb.Append("- Передняя панель");


                                    case EPcxGeoLocation.eGeoLocRear: sb.Append("- Задняя панель");


                                    case EPcxGeoLocation.eGeoLocHDMI: sb.Append("- HDMI");


                                    case EPcxGeoLocation.eGeoLocNotApplicable: sb.Append("- Расположение не определено");


                                    default: sb.Append("- "+loc.ToString());



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



                                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;



                            }//end for




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

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

                            lab.AutoSize = true;







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

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

                        lab.AutoSize = true;







                catch (COMException ex)


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


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

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

                        lab.AutoSize = true;





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

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

                        lab.AutoSize = true;




                catch (Exception ex)


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

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

                    lab.AutoSize = true;







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



            return res.ToString();



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


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


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



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


        deviceCollection.Item(i, out o);

        dev = o as IMMDevice;




        //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-системах