/// <summary>
        /// Opens device in persistent mode (reopens after device removal-reconnection)
        /// </summary>
        /// <param name="in_path"></param>
        /// <returns></returns>
        public bool Open(string in_path)
        {
            bool success = true;

            // store devcie path
            m_device_path = in_path;

            // open device
            m_device_handle = USBNativeMethods.CreateFile(in_path, USBNativeMethods.GENERIC_READ | USBNativeMethods.GENERIC_WRITE, USBNativeMethods.FILE_SHARE_READ | USBNativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, USBNativeMethods.OPEN_EXISTING, USBNativeMethods.FILE_FLAG_OVERLAPPED, 0);

            if (m_device_handle.IsInvalid)
            {
                success = false;
            }

            // get report length
            if (success)
            {
                IntPtr preparsed_data_pointer = new IntPtr();
                if (USBNativeMethods.HidD_GetPreparsedData(m_device_handle, ref preparsed_data_pointer))
                {
                    USBNativeMethods.HIDP_CAPS capabilities = new USBNativeMethods.HIDP_CAPS();

                    // Get report lengths
                    USBNativeMethods.HidP_GetCaps(preparsed_data_pointer, ref capabilities);

                    // Store report length
                    m_input_report_length   = capabilities.InputReportByteLength;
                    m_output_report_length  = capabilities.OutputReportByteLength;
                    m_feature_report_length = capabilities.FeatureReportByteLength;

                    // No need for PreparsedData any more, so free the memory it's using.
                    USBNativeMethods.HidD_FreePreparsedData(ref preparsed_data_pointer);

                    // check report size
                    if (m_input_report_length != USBNativeMethods.HID_MAX_REPORT_SIZE || m_output_report_length != USBNativeMethods.HID_MAX_REPORT_SIZE)
                    {
                        success = false;
                    }
                }
                else
                {
                    success = false;
                }
            }

            if (success)
            {
                // determine buffer size (use the biggest report size, normally they must be same)
                int buffer_size = m_input_report_length;

                if (m_output_report_length > buffer_size)
                {
                    buffer_size = m_output_report_length;
                }

                if (m_feature_report_length > buffer_size)
                {
                    buffer_size = m_feature_report_length;
                }

                // get file stream
                m_file_stream = new FileStream(m_device_handle, FileAccess.ReadWrite, buffer_size, true);
            }

            return(success);
        }
        /// <summary>
        /// Gets the list of the dvices
        /// </summary>
        /// <param name="in_vid">Vendor ID</param>
        /// <param name="in_pid">Product ID</param>
        /// <returns></returns>
        public List <DeviceInfo> EnumerateDevices()
        {
            bool result;
            int  device_index = 0;
            List <DeviceInfo> device_info_collection = new List <DeviceInfo>();

            // init
            Guid hid_guid = Guid.Empty;

            //Obtain the device interface GUID for the HID class
            USBNativeMethods.HidD_GetHidGuid(ref hid_guid);

            // Requesting a pointer to a device information set
            IntPtr device_info_set = USBNativeMethods.SetupDiGetClassDevs(ref hid_guid, IntPtr.Zero, IntPtr.Zero, USBNativeMethods.DIGCF_PRESENT | USBNativeMethods.DIGCF_INTERFACEDEVICE);

            // The cbSize element of the MyDeviceInterfaceData structure must be set to
            // the structure's size in bytes.
            USBNativeMethods.SP_DEVICE_INTERFACE_DATA device_interface_data = new USBNativeMethods.SP_DEVICE_INTERFACE_DATA();
            device_interface_data.Size = Marshal.SizeOf(device_interface_data);

            device_index = 0;
            while (true)
            {
                // Begin with 0 and increment through the device information set until
                // no more devices are available.
                result = USBNativeMethods.SetupDiEnumDeviceInterfaces(device_info_set, IntPtr.Zero, ref hid_guid, device_index, ref device_interface_data);
                if (!result)
                {
                    // If it fails, that means we've reached the end of the list.
                    break;
                }

                // A device is present.
                // Find out how big of a buffer is needed.
                Int32 buffer_size = 0;
                result = USBNativeMethods.SetupDiGetDeviceInterfaceDetail(device_info_set, ref device_interface_data, IntPtr.Zero, 0, ref buffer_size, IntPtr.Zero);
                if (result)
                {
                    // This success is unexpected! We wanted to get an error, with the attendant
                    // information of how big to make the buffer for a successful call.
                    break;
                }

                // Allocate memory for the SP_DEVICE_INTERFACE_DETAIL_DATA structure, using the returned Length.
                IntPtr detail_data_buffer = Marshal.AllocHGlobal(buffer_size);

                // Store cbSize in the first bytes of the array. The number of bytes varies with 32-bit and 64-bit systems.
                Marshal.WriteInt32(detail_data_buffer, (IntPtr.Size == 4) ? (IntPtr.Size + Marshal.SystemDefaultCharSize) : 8);

                // Call SetupDiGetDeviceInterfaceDetail again.
                // This time, pass a pointer to DetailDataBuffer
                // and the returned required buffer size.
                result = USBNativeMethods.SetupDiGetDeviceInterfaceDetail(device_info_set, ref device_interface_data, detail_data_buffer, buffer_size, ref buffer_size, IntPtr.Zero);
                if (result)
                {
                    // Skip over cbsize (4 bytes) to get the address of the devicePathName.
                    IntPtr device_pathname_pointer = new IntPtr(detail_data_buffer.ToInt64() + sizeof(Int32));

                    // Get the String containing the devicePathName.
                    string device_pathname = Marshal.PtrToStringAuto(device_pathname_pointer);

                    // Open a handle to the device.
                    SafeFileHandle device_handle;
                    device_handle = USBNativeMethods.CreateFile(device_pathname,
                                                                USBNativeMethods.GENERIC_READ | USBNativeMethods.GENERIC_WRITE,
                                                                USBNativeMethods.FILE_SHARE_READ | USBNativeMethods.FILE_SHARE_WRITE,
                                                                IntPtr.Zero,
                                                                USBNativeMethods.OPEN_EXISTING,
                                                                0,
                                                                0);

                    if (!device_handle.IsInvalid)
                    {
                        USBNativeMethods.HIDD_ATTRIBUTES attributes = new USBNativeMethods.HIDD_ATTRIBUTES();

                        // Set the Size to the number of bytes in the structure.
                        attributes.Size = Marshal.SizeOf(attributes);

                        // Requests information from the device.
                        result = USBNativeMethods.HidD_GetAttributes(device_handle, ref attributes);

                        // check vendor and product id
                        if (attributes.VendorID == VID && attributes.ProductID == PID)
                        {
                            StringBuilder serial_number = new StringBuilder(1024);

                            if (USBNativeMethods.HidD_GetSerialNumberString(device_handle, serial_number, serial_number.Capacity))
                            {
                                DeviceInfo device_info = new DeviceInfo();

                                device_info.DevicePath   = device_pathname;
                                device_info.SerialNumber = serial_number.ToString();

                                device_info_collection.Add(device_info);
                            }
                        }

                        // close handle
                        device_handle.Close();
                    }
                }

                // Free the memory used by the detailData structure (no longer needed).
                Marshal.FreeHGlobal(detail_data_buffer);

                // next device
                device_index++;
            }

            // free device info set
            if (device_info_set != IntPtr.Zero)
            {
                USBNativeMethods.SetupDiDestroyDeviceInfoList(device_info_set);
            }

            return(device_info_collection);
        }