using System;
using System.Collections.Generic;

using System.Runtime.InteropServices;
using System.Security;
using Microsoft.Win32.SafeHandles;

namespace DS4Windows 
{
    [SuppressUnmanagedCodeSecurity]
    public class ScpDevice
    {
        public virtual Boolean IsActive 
        {
            get { return m_IsActive; }
        }

        public virtual String Path 
        {
            get { return m_Path; }
        }

        public ScpDevice(String Class) 
        {
            this.m_Class = new Guid(Class);
        }


        public virtual Boolean Open(Int32 Instance = 0) 
        {
            String DevicePath = String.Empty;
            m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE;

            if (Find(m_Class, ref DevicePath, Instance))
            {
                Open(DevicePath);
            }

            return m_IsActive;
        }

        public virtual Boolean Open(String DevicePath)  
        {
            m_Path = DevicePath.ToUpper();
            m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE;

            if (GetDeviceHandle(m_Path))
            {
                if (WinUsb_Initialize(m_FileHandle, ref m_WinUsbHandle))
                {
                    if (InitializeDevice())
                    {
                        m_IsActive = true;
                    }
                    else
                    {
                        WinUsb_Free(m_WinUsbHandle);
                        m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE;
                    }
                }
                else
                {
                    m_FileHandle.Close();
                }
            }

            return m_IsActive;
        }

        public virtual Boolean Start() 
        {
            return m_IsActive;
        }

        public virtual Boolean Stop()  
        {
            m_IsActive = false;

            if (!(m_WinUsbHandle == (IntPtr) INVALID_HANDLE_VALUE))
            {
                WinUsb_AbortPipe(m_WinUsbHandle, m_IntIn);
                WinUsb_AbortPipe(m_WinUsbHandle, m_BulkIn);
                WinUsb_AbortPipe(m_WinUsbHandle, m_BulkOut);

                WinUsb_Free(m_WinUsbHandle);
                m_WinUsbHandle = (IntPtr) INVALID_HANDLE_VALUE;
            }

            if (m_FileHandle != null && !m_FileHandle.IsInvalid && !m_FileHandle.IsClosed)
            {
                m_FileHandle.Close();
                m_FileHandle = null;
            }

            return true;
        }

        public virtual Boolean Close() 
        {
            return Stop();
        }


        public virtual Boolean ReadIntPipe  (Byte[] Buffer, Int32 Length, ref Int32 Transfered) 
        {
            if (!m_IsActive) return false;

            return WinUsb_ReadPipe(m_WinUsbHandle, m_IntIn, Buffer, Length, ref Transfered, IntPtr.Zero);
        }

        public virtual Boolean ReadBulkPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) 
        {
            if (!m_IsActive) return false;

            return WinUsb_ReadPipe(m_WinUsbHandle, m_BulkIn, Buffer, Length, ref Transfered, IntPtr.Zero);
        }

        public virtual Boolean WriteIntPipe (Byte[] Buffer, Int32 Length, ref Int32 Transfered) 
        {
            if (!m_IsActive) return false;

            return WinUsb_WritePipe(m_WinUsbHandle, m_IntOut, Buffer, Length, ref Transfered, IntPtr.Zero);
        }

        public virtual Boolean WriteBulkPipe(Byte[] Buffer, Int32 Length, ref Int32 Transfered) 
        {
            if (!m_IsActive) return false;

            return WinUsb_WritePipe(m_WinUsbHandle, m_BulkOut, Buffer, Length, ref Transfered, IntPtr.Zero);
        }


        public virtual Boolean SendTransfer(Byte RequestType, Byte Request, UInt16 Value, Byte[] Buffer, ref Int32 Transfered) 
        {
            if (!m_IsActive) return false;

            WINUSB_SETUP_PACKET Setup = new WINUSB_SETUP_PACKET();

            Setup.RequestType = RequestType;
            Setup.Request     = Request;
            Setup.Value       = Value;
            Setup.Index       = 0;
            Setup.Length      = (UInt16) Buffer.Length;

            return WinUsb_ControlTransfer(m_WinUsbHandle, Setup, Buffer, Buffer.Length, ref Transfered, IntPtr.Zero);
        }


        #region Constant and Structure Definitions
        public const Int32 SERVICE_CONTROL_STOP                 = 0x00000001;
        public const Int32 SERVICE_CONTROL_SHUTDOWN             = 0x00000005;
        public const Int32 SERVICE_CONTROL_DEVICEEVENT          = 0x0000000B;
        public const Int32 SERVICE_CONTROL_POWEREVENT           = 0x0000000D;

        public const Int32 DBT_DEVICEARRIVAL                    = 0x8000;
        public const Int32 DBT_DEVICEQUERYREMOVE                = 0x8001;
        public const Int32 DBT_DEVICEREMOVECOMPLETE             = 0x8004;
        public const Int32 DBT_DEVTYP_DEVICEINTERFACE           = 0x0005;
        public const Int32 DBT_DEVTYP_HANDLE                    = 0x0006;

        public const Int32 PBT_APMRESUMEAUTOMATIC               = 0x0012;
        public const Int32 PBT_APMSUSPEND                       = 0x0004;

        public const Int32 DEVICE_NOTIFY_WINDOW_HANDLE          = 0x0000;
        public const Int32 DEVICE_NOTIFY_SERVICE_HANDLE         = 0x0001;
        public const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES  = 0x0004;

        public const Int32 WM_CREATE                            = 0x0001;
        public const Int32 WM_DEVICECHANGE                      = 0x0219;

        public const Int32 DIGCF_PRESENT                        = 0x0002;
        public const Int32 DIGCF_DEVICEINTERFACE                = 0x0010;

        public delegate Int32 ServiceControlHandlerEx(Int32 Control, Int32 Type, IntPtr Data, IntPtr Context);

        [StructLayout(LayoutKind.Sequential)]
        public class DEV_BROADCAST_DEVICEINTERFACE 
        {
            internal Int32 dbcc_size;
            internal Int32 dbcc_devicetype;
            internal Int32 dbcc_reserved;
            internal Guid  dbcc_classguid;
            internal Int16 dbcc_name;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class DEV_BROADCAST_DEVICEINTERFACE_M 
        {
            public Int32 dbcc_size;
            public Int32 dbcc_devicetype;
            public Int32 dbcc_reserved;

            [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)]
            public Byte[] dbcc_classguid;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
            public Char[] dbcc_name;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class DEV_BROADCAST_HDR 
        {
            public Int32 dbch_size;
            public Int32 dbch_devicetype;
            public Int32 dbch_reserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct SP_DEVICE_INTERFACE_DATA 
        {
            internal Int32  cbSize;
            internal Guid   InterfaceClassGuid;
            internal Int32  Flags;
            internal IntPtr Reserved;
        }

        protected const UInt32 FILE_ATTRIBUTE_NORMAL        = 0x80;
        protected const UInt32 FILE_FLAG_OVERLAPPED         = 0x40000000;
        protected const UInt32 FILE_SHARE_READ              = 1;
        protected const UInt32 FILE_SHARE_WRITE             = 2;
        protected const UInt32 GENERIC_READ                 = 0x80000000;
        protected const UInt32 GENERIC_WRITE                = 0x40000000;
        protected const  Int32 INVALID_HANDLE_VALUE         = -1;
        protected const UInt32 OPEN_EXISTING                = 3;
        protected const UInt32 DEVICE_SPEED                 = 1;
        protected const Byte   USB_ENDPOINT_DIRECTION_MASK  = 0x80;

        protected enum POLICY_TYPE 
        {
            SHORT_PACKET_TERMINATE = 1,
            AUTO_CLEAR_STALL       = 2,
            PIPE_TRANSFER_TIMEOUT  = 3,
            IGNORE_SHORT_PACKETS   = 4,
            ALLOW_PARTIAL_READS    = 5,
            AUTO_FLUSH             = 6,
            RAW_IO                 = 7,
        }

        protected enum USBD_PIPE_TYPE 
        {
            UsbdPipeTypeControl     = 0,
            UsbdPipeTypeIsochronous = 1,
            UsbdPipeTypeBulk        = 2,
            UsbdPipeTypeInterrupt   = 3,
        }

        protected enum USB_DEVICE_SPEED 
        {
            UsbLowSpeed  = 1,
            UsbFullSpeed = 2,
            UsbHighSpeed = 3,
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct USB_CONFIGURATION_DESCRIPTOR 
        {
            internal Byte   bLength;
            internal Byte   bDescriptorType;
            internal UInt16 wTotalLength;
            internal Byte   bNumInterfaces;
            internal Byte   bConfigurationValue;
            internal Byte   iConfiguration;
            internal Byte   bmAttributes;
            internal Byte   MaxPower;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct USB_INTERFACE_DESCRIPTOR 
        {
            internal Byte bLength;
            internal Byte bDescriptorType;
            internal Byte bInterfaceNumber;
            internal Byte bAlternateSetting;
            internal Byte bNumEndpoints;
            internal Byte bInterfaceClass;
            internal Byte bInterfaceSubClass;
            internal Byte bInterfaceProtocol;
            internal Byte iInterface;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct WINUSB_PIPE_INFORMATION 
        {
            internal USBD_PIPE_TYPE PipeType;
            internal Byte           PipeId;
            internal UInt16         MaximumPacketSize;
            internal Byte           Interval;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        protected struct WINUSB_SETUP_PACKET 
        {
            internal Byte   RequestType;
            internal Byte   Request;
            internal UInt16 Value;
            internal UInt16 Index;
            internal UInt16 Length;
        }

        protected const Int32 DIF_PROPERTYCHANGE = 0x12;
        protected const Int32 DICS_ENABLE        = 1;
        protected const Int32 DICS_DISABLE       = 2;
        protected const Int32 DICS_PROPCHANGE    = 3;
        protected const Int32 DICS_FLAG_GLOBAL   = 1;

        [StructLayout(LayoutKind.Sequential)]
        protected struct SP_CLASSINSTALL_HEADER 
        {
            internal Int32 cbSize;
            internal Int32 InstallFunction;
        }

        [StructLayout(LayoutKind.Sequential)]
        protected struct SP_PROPCHANGE_PARAMS 
        {
            internal SP_CLASSINSTALL_HEADER ClassInstallHeader;
            internal Int32 StateChange;
            internal Int32 Scope;
            internal Int32 HwProfile;
        }
        #endregion

        #region Protected Data Members
        protected Guid   m_Class = Guid.Empty;
        protected String m_Path  = String.Empty;

        protected SafeFileHandle m_FileHandle = null;
        protected IntPtr m_WinUsbHandle = IntPtr.Zero;

        protected Byte m_IntIn   = 0xFF;
        protected Byte m_IntOut  = 0xFF;
        protected Byte m_BulkIn  = 0xFF;
        protected Byte m_BulkOut = 0xFF;

        protected Boolean m_IsActive = false;
        #endregion

        #region Static Helper Methods
        public enum Notified { Ignore = 0x0000, Arrival = 0x8000, QueryRemove = 0x8001, Removal = 0x8004 };

        public static Boolean RegisterNotify(IntPtr Form, Guid Class, ref IntPtr Handle, Boolean Window = true) 
        {
            IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero;

            try
            {
                DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE();
                Int32 Size = Marshal.SizeOf(devBroadcastDeviceInterface);

                devBroadcastDeviceInterface.dbcc_size       = Size;
                devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
                devBroadcastDeviceInterface.dbcc_reserved   = 0;
                devBroadcastDeviceInterface.dbcc_classguid  = Class;

                devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(Size);
                Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true);

                Handle = RegisterDeviceNotification(Form, devBroadcastDeviceInterfaceBuffer, Window ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE);

                Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface);

                return Handle != IntPtr.Zero;
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message);
                throw;
            }
            finally
            {
                if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer);
                }
            }
        }

        public static Boolean UnregisterNotify(IntPtr Handle) 
        {
            try
            {
                return UnregisterDeviceNotification(Handle);
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message);
                throw;
            }
        }
        #endregion

        #region Protected Methods
        protected virtual Boolean Find(Guid Target, ref String Path, Int32 Instance = 0) 
        {
            IntPtr detailDataBuffer = IntPtr.Zero;
            IntPtr deviceInfoSet    = IntPtr.Zero;

            try
            {
                SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA();
                Int32 bufferSize = 0, memberIndex = 0;

                deviceInfoSet = SetupDiGetClassDevs(ref Target, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

                DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData);

                while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref Target, memberIndex, ref DeviceInterfaceData))
                {
                    SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da);
                    {
                        detailDataBuffer = Marshal.AllocHGlobal(bufferSize);

                        Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);

                        if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da))
                        {
                            IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4: detailDataBuffer.ToInt64() + 4);

                            Path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper();
                            Marshal.FreeHGlobal(detailDataBuffer);

                            if (memberIndex == Instance) return true;
                        }
                        else Marshal.FreeHGlobal(detailDataBuffer);
                    }

                    memberIndex++;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message);
                throw;
            }
            finally
            {
                if (deviceInfoSet != IntPtr.Zero)
                {
                    SetupDiDestroyDeviceInfoList(deviceInfoSet);
                }
            }

            return false;
        }

        protected virtual Boolean GetDeviceInstance(ref String Instance) 
        {
            IntPtr detailDataBuffer = IntPtr.Zero;
            IntPtr deviceInfoSet    = IntPtr.Zero;

            try
            {
                SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(), da = new SP_DEVICE_INTERFACE_DATA();
                Int32 bufferSize = 0, memberIndex = 0;

                deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

                DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData);

                while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref m_Class, memberIndex, ref DeviceInterfaceData))
                {
                    SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da);
                    {
                        detailDataBuffer = Marshal.AllocHGlobal(bufferSize);

                        Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);

                        if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da))
                        {
                            IntPtr pDevicePathName = new IntPtr(IntPtr.Size == 4 ? detailDataBuffer.ToInt32() + 4 : detailDataBuffer.ToInt64() + 4);

                            String Current = Marshal.PtrToStringAuto(pDevicePathName).ToUpper();
                            Marshal.FreeHGlobal(detailDataBuffer);

                            if (Current == Path)
                            {
                                Int32  nBytes = 256;
                                IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes);

                                CM_Get_Device_ID(da.Flags, ptrInstanceBuf, nBytes, 0);
                                Instance = Marshal.PtrToStringAuto(ptrInstanceBuf).ToUpper();

                                Marshal.FreeHGlobal(ptrInstanceBuf);
                                return true;
                            }
                        }
                        else Marshal.FreeHGlobal(detailDataBuffer);
                    }

                    memberIndex++;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message);
                throw;
            }
            finally
            {
                if (deviceInfoSet != IntPtr.Zero)
                {
                    SetupDiDestroyDeviceInfoList(deviceInfoSet);
                }
            }

            return false;
        }

        protected virtual Boolean GetDeviceHandle(String Path) 
        {
            m_FileHandle = CreateFile(Path, (GENERIC_WRITE | GENERIC_READ), FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | 0x20000000 | 0x80000000, 0);

            return !m_FileHandle.IsInvalid;
        }

        protected virtual Boolean UsbEndpointDirectionIn(Int32 addr) 
        {
            return (addr & 0x80) == 0x80;
        }

        protected virtual Boolean UsbEndpointDirectionOut(Int32 addr) 
        {
            return (addr & 0x80) == 0x00;
        }

        protected virtual Boolean InitializeDevice() 
        {
            try
            {
                USB_INTERFACE_DESCRIPTOR ifaceDescriptor = new USB_INTERFACE_DESCRIPTOR();
                WINUSB_PIPE_INFORMATION  pipeInfo = new WINUSB_PIPE_INFORMATION();

                if (WinUsb_QueryInterfaceSettings(m_WinUsbHandle, 0, ref ifaceDescriptor))
                {
                    for (Int32 i = 0; i < ifaceDescriptor.bNumEndpoints; i++)
                    {
                        WinUsb_QueryPipe(m_WinUsbHandle, 0, System.Convert.ToByte(i), ref pipeInfo);

                        if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionIn(pipeInfo.PipeId)))
                        {
                            m_BulkIn = pipeInfo.PipeId;
                            WinUsb_FlushPipe(m_WinUsbHandle, m_BulkIn);
                        }
                        else if (((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeBulk) & UsbEndpointDirectionOut(pipeInfo.PipeId)))
                        {
                            m_BulkOut = pipeInfo.PipeId;
                            WinUsb_FlushPipe(m_WinUsbHandle, m_BulkOut);
                        }
                        else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionIn(pipeInfo.PipeId))
                        {
                            m_IntIn = pipeInfo.PipeId;
                            WinUsb_FlushPipe(m_WinUsbHandle, m_IntIn);
                        }
                        else if ((pipeInfo.PipeType == USBD_PIPE_TYPE.UsbdPipeTypeInterrupt) & UsbEndpointDirectionOut(pipeInfo.PipeId))
                        {
                            m_IntOut = pipeInfo.PipeId;
                            WinUsb_FlushPipe(m_WinUsbHandle, m_IntOut);
                        }
                    }

                    return true;
                }

                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message);
                throw;
            }
        }

        protected virtual Boolean RestartDevice(String InstanceId) 
        {
            IntPtr deviceInfoSet = IntPtr.Zero;

            try
            {
                SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();

                deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData);
                deviceInfoSet = SetupDiGetClassDevs(ref m_Class, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

                if (SetupDiOpenDeviceInfo(deviceInfoSet, InstanceId, IntPtr.Zero, 0, ref deviceInterfaceData))
                {
                    SP_PROPCHANGE_PARAMS props = new SP_PROPCHANGE_PARAMS();

                    props.ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
                    props.ClassInstallHeader.cbSize = Marshal.SizeOf(props.ClassInstallHeader);
                    props.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;

                    props.Scope       = DICS_FLAG_GLOBAL;
                    props.StateChange = DICS_PROPCHANGE;
                    props.HwProfile   = 0x00;

                    if (SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInterfaceData, ref props, Marshal.SizeOf(props)))
                    {
                        return SetupDiChangeState(deviceInfoSet, ref deviceInterfaceData);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} {1}", ex.HelpLink, ex.Message);
                throw;
            }
            finally
            {
                if (deviceInfoSet != IntPtr.Zero)
                {
                    SetupDiDestroyDeviceInfoList(deviceInfoSet);
                }
            }

            return false;
        }
        #endregion

        #region Interop Definitions
        [DllImport("setupapi.dll", SetLastError = true)]
        protected static extern Int32 SetupDiCreateDeviceInfoList(ref System.Guid ClassGuid, Int32 hwndParent);

        [DllImport("setupapi.dll", SetLastError = true)]
        protected static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

        [DllImport("setupapi.dll", SetLastError = true)]
        protected static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        protected static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags);

        [DllImport("user32.dll", SetLastError = true)]
        protected static extern Boolean UnregisterDeviceNotification(IntPtr Handle);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UInt32 hTemplateFile);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_Initialize(SafeFileHandle DeviceHandle, ref IntPtr InterfaceHandle);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_QueryInterfaceSettings(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, ref USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_QueryPipe(IntPtr InterfaceHandle, Byte AlternateInterfaceNumber, Byte PipeIndex, ref WINUSB_PIPE_INFORMATION PipeInformation);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_AbortPipe(IntPtr InterfaceHandle, Byte PipeID);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_FlushPipe(IntPtr InterfaceHandle, Byte PipeID);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_ReadPipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_WritePipe(IntPtr InterfaceHandle, Byte PipeID, Byte[] Buffer, Int32 BufferLength, ref Int32 LengthTransferred, IntPtr Overlapped);

        [DllImport("winusb.dll", SetLastError = true)]
        protected static extern Boolean WinUsb_Free(IntPtr InterfaceHandle);

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern IntPtr RegisterServiceCtrlHandlerEx(String ServiceName, ServiceControlHandlerEx Callback, IntPtr Context);

        [DllImport("kernel32.dll", SetLastError = true)]
        protected static extern Boolean DeviceIoControl(SafeFileHandle DeviceHandle, Int32 IoControlCode, Byte[] InBuffer, Int32 InBufferSize, Byte[] OutBuffer, Int32 OutBufferSize, ref Int32 BytesReturned, IntPtr Overlapped);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern Int32 CM_Get_Device_ID(Int32 dnDevInst, IntPtr Buffer, Int32 BufferLen, Int32 ulFlags);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern Boolean SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, String DeviceInstanceId, IntPtr hwndParent, Int32 Flags, ref SP_DEVICE_INTERFACE_DATA DeviceInfoData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern Boolean SetupDiChangeState(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        protected static extern Boolean SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_PROPCHANGE_PARAMS ClassInstallParams, Int32 ClassInstallParamsSize);
        #endregion
    }
}
