Browse Source

Merge branch 'release/0.1.0'

liuyuqi-dellpc 2 months ago
parent
commit
c247510d5c
11 changed files with 3028 additions and 12 deletions
  1. 3 1
      .gitignore
  2. 256 0
      BLETool/BLEInfo.cs
  3. 22 0
      BLETool/BLETool.csproj
  4. 451 0
      BLETool/ClassSerialization.cs
  5. 79 0
      BLETool/ListviewSort.cs
  6. 1090 9
      BLETool/MainForm.Designer.cs
  7. 964 2
      BLETool/MainForm.cs
  8. 149 0
      BLETool/MainForm.resx
  9. BIN
      BLETool/bt.ico
  10. 10 0
      README.md
  11. 4 0
      docs/index.md

+ 3 - 1
.gitignore

@@ -1,4 +1,6 @@
 /.vs/BLETool/v17
 /.vs/BLETool/FileContentIndex
 BLETool/.vs/
-BLETool/obj/
+BLETool/obj/
+BLETool/bin/
+BLETool/ble2/windows.winmd

+ 256 - 0
BLETool/BLEInfo.cs

@@ -0,0 +1,256 @@
+using System;
+using System.Text;
+using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Security.Cryptography;
+using Windows.Storage.Streams;
+
+namespace BLEComm
+{
+    class BLEInfo
+    {
+
+        /// <summary>
+        ///     This enum assists in finding a string representation of a BT SIG assigned value for Service UUIDS
+        ///     Reference: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx
+        /// </summary>
+        public enum GattNativeServiceUuid : ushort
+        {
+            None = 0,
+            AlertNotification = 0x1811,
+            Battery = 0x180F,
+            BloodPressure = 0x1810,
+            CurrentTimeService = 0x1805,
+            CyclingSpeedandCadence = 0x1816,
+            DeviceInformation = 0x180A,
+            GenericAccess = 0x1800,
+            GenericAttribute = 0x1801,
+            Glucose = 0x1808,
+            HealthThermometer = 0x1809,
+            HeartRate = 0x180D,
+            HumanInterfaceDevice = 0x1812,
+            ImmediateAlert = 0x1802,
+            LinkLoss = 0x1803,
+            NextDSTChange = 0x1807,
+            PhoneAlertStatus = 0x180E,
+            ReferenceTimeUpdateService = 0x1806,
+            RunningSpeedandCadence = 0x1814,
+            ScanParameters = 0x1813,
+            TxPower = 0x1804,
+            SimpleKeyService = 0xFFE0
+        }
+
+        /// <summary>
+        ///     This enum is nice for finding a string representation of a BT SIG assigned value for Characteristic UUIDs
+        ///     Reference: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx
+        /// </summary>
+        public enum GattNativeCharacteristicUuid : ushort
+        {
+            None = 0,
+            AlertCategoryID = 0x2A43,
+            AlertCategoryIDBitMask = 0x2A42,
+            AlertLevel = 0x2A06,
+            AlertNotificationControlPoint = 0x2A44,
+            AlertStatus = 0x2A3F,
+            Appearance = 0x2A01,
+            BatteryLevel = 0x2A19,
+            BloodPressureFeature = 0x2A49,
+            BloodPressureMeasurement = 0x2A35,
+            BodySensorLocation = 0x2A38,
+            BootKeyboardInputReport = 0x2A22,
+            BootKeyboardOutputReport = 0x2A32,
+            BootMouseInputReport = 0x2A33,
+            CSCFeature = 0x2A5C,
+            CSCMeasurement = 0x2A5B,
+            CurrentTime = 0x2A2B,
+            DateTime = 0x2A08,
+            DayDateTime = 0x2A0A,
+            DayofWeek = 0x2A09,
+            DeviceName = 0x2A00,
+            DSTOffset = 0x2A0D,
+            ExactTime256 = 0x2A0C,
+            FirmwareRevisionString = 0x2A26,
+            GlucoseFeature = 0x2A51,
+            GlucoseMeasurement = 0x2A18,
+            GlucoseMeasurementContext = 0x2A34,
+            HardwareRevisionString = 0x2A27,
+            HeartRateControlPoint = 0x2A39,
+            HeartRateMeasurement = 0x2A37,
+            HIDControlPoint = 0x2A4C,
+            HIDInformation = 0x2A4A,
+            IEEE11073_20601RegulatoryCertificationDataList = 0x2A2A,
+            IntermediateCuffPressure = 0x2A36,
+            IntermediateTemperature = 0x2A1E,
+            LocalTimeInformation = 0x2A0F,
+            ManufacturerNameString = 0x2A29,
+            MeasurementInterval = 0x2A21,
+            ModelNumberString = 0x2A24,
+            NewAlert = 0x2A46,
+            PeripheralPreferredConnectionParameters = 0x2A04,
+            PeripheralPrivacyFlag = 0x2A02,
+            PnPID = 0x2A50,
+            ProtocolMode = 0x2A4E,
+            ReconnectionAddress = 0x2A03,
+            RecordAccessControlPoint = 0x2A52,
+            ReferenceTimeInformation = 0x2A14,
+            Report = 0x2A4D,
+            ReportMap = 0x2A4B,
+            RingerControlPoint = 0x2A40,
+            RingerSetting = 0x2A41,
+            RSCFeature = 0x2A54,
+            RSCMeasurement = 0x2A53,
+            SCControlPoint = 0x2A55,
+            ScanIntervalWindow = 0x2A4F,
+            ScanRefresh = 0x2A31,
+            SensorLocation = 0x2A5D,
+            SerialNumberString = 0x2A25,
+            ServiceChanged = 0x2A05,
+            SoftwareRevisionString = 0x2A28,
+            SupportedNewAlertCategory = 0x2A47,
+            SupportedUnreadAlertCategory = 0x2A48,
+            SystemID = 0x2A23,
+            TemperatureMeasurement = 0x2A1C,
+            TemperatureType = 0x2A1D,
+            TimeAccuracy = 0x2A12,
+            TimeSource = 0x2A13,
+            TimeUpdateControlPoint = 0x2A16,
+            TimeUpdateState = 0x2A17,
+            TimewithDST = 0x2A11,
+            TimeZone = 0x2A0E,
+            TxPowerLevel = 0x2A07,
+            UnreadAlertStatus = 0x2A45,
+            AggregateInput = 0x2A5A,
+            AnalogInput = 0x2A58,
+            AnalogOutput = 0x2A59,
+            CyclingPowerControlPoint = 0x2A66,
+            CyclingPowerFeature = 0x2A65,
+            CyclingPowerMeasurement = 0x2A63,
+            CyclingPowerVector = 0x2A64,
+            DigitalInput = 0x2A56,
+            DigitalOutput = 0x2A57,
+            ExactTime100 = 0x2A0B,
+            LNControlPoint = 0x2A6B,
+            LNFeature = 0x2A6A,
+            LocationandSpeed = 0x2A67,
+            Navigation = 0x2A68,
+            NetworkAvailability = 0x2A3E,
+            PositionQuality = 0x2A69,
+            ScientificTemperatureinCelsius = 0x2A3C,
+            SecondaryTimeZone = 0x2A10,
+            String = 0x2A3D,
+            TemperatureinCelsius = 0x2A1F,
+            TemperatureinFahrenheit = 0x2A20,
+            TimeBroadcast = 0x2A15,
+            BatteryLevelState = 0x2A1B,
+            BatteryPowerState = 0x2A1A,
+            PulseOximetryContinuousMeasurement = 0x2A5F,
+            PulseOximetryControlPoint = 0x2A62,
+            PulseOximetryFeatures = 0x2A61,
+            PulseOximetryPulsatileEvent = 0x2A60,
+            SimpleKeyState = 0xFFE1
+        }
+
+        /// <summary>
+        ///     This enum assists in finding a string representation of a BT SIG assigned value for Descriptor UUIDs
+        ///     Reference: https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorsHomePage.aspx
+        /// </summary>
+        public enum GattNativeDescriptorUuid : ushort
+        {
+            CharacteristicExtendedProperties = 0x2900,
+            CharacteristicUserDescription = 0x2901,
+            ClientCharacteristicConfiguration = 0x2902,
+            ServerCharacteristicConfiguration = 0x2903,
+            CharacteristicPresentationFormat = 0x2904,
+            CharacteristicAggregateFormat = 0x2905,
+            ValidRange = 0x2906,
+            ExternalReportReference = 0x2907,
+            ReportReference = 0x2908
+        }
+        public static ushort ConvertUuidToShortId(Guid uuid)
+        {
+            // Get the short Uuid
+            var bytes = uuid.ToByteArray();
+            var shortUuid = (ushort)(bytes[0] | (bytes[1] << 8));
+            return shortUuid;
+        }
+        public static string GetServiceName(GattDeviceService service)
+        {
+            BLEInfo.GattNativeServiceUuid serviceName;
+            if (Enum.TryParse(ConvertUuidToShortId(service.Uuid).ToString(), out serviceName))
+            {
+                return serviceName.ToString();
+            }
+            return "Custom Service: " + service.Uuid;
+        }
+        public static string GetCharacteristicName(GattCharacteristic characteristic)
+        {
+
+            GattNativeCharacteristicUuid characteristicName;
+            if (Enum.TryParse(ConvertUuidToShortId(characteristic.Uuid).ToString(),
+                out characteristicName))
+            {
+                return characteristicName.ToString();
+            }
+
+            if (!string.IsNullOrEmpty(characteristic.UserDescription))
+            {
+                return characteristic.UserDescription;
+            }
+
+            else
+            {
+                return "Custom Characteristic: " + characteristic.Uuid;
+            }
+        }
+        public static string FormatValueByPresentation(IBuffer buffer, GattPresentationFormat format, out string datatype)
+        {
+            // BT_Code: For the purpose of this sample, this function converts only UInt32 and
+            // UTF-8 buffers to readable text. It can be extended to support other formats if your app needs them.
+            byte[] data;
+            CryptographicBuffer.CopyToByteArray(buffer, out data);
+            if (format != null)
+            {
+                if (format.FormatType == GattPresentationFormatTypes.UInt32 && data.Length >= 4)
+                {
+                    datatype = "Decimal";
+                    return BitConverter.ToInt32(data, 0).ToString();
+                }
+                else if (format.FormatType == GattPresentationFormatTypes.UInt16 && data.Length >= 2)
+                {
+                    datatype = "Decimal";
+                    return BitConverter.ToInt16(data, 0).ToString();
+                }
+                else if (format.FormatType == GattPresentationFormatTypes.Utf8)
+                {
+                    try
+                    {
+                        datatype = "UTF8";
+                        return Encoding.UTF8.GetString(data);
+                    }
+                    catch (ArgumentException)
+                    {
+                        datatype = "known";
+                        return "(Invalid UTF-8 string)";
+                    }
+                }
+                else
+                {
+                    datatype = "HEX";
+                    // Add support for other format types as needed.
+                    return CryptographicBuffer.EncodeToHexString(buffer);
+                }
+            }
+            else if (data != null)
+            {
+                datatype = "HEX";
+                return CryptographicBuffer.EncodeToHexString(buffer);
+            }
+            else
+            {
+                datatype = "HEX";
+                return "Empty data received";
+            }
+        }
+
+
+    }
+}

+ 22 - 0
BLETool/BLETool.csproj

@@ -32,9 +32,16 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>bt.ico</ApplicationIcon>
+  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>ble2\System.Runtime.WindowsRuntime.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -44,8 +51,14 @@
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml" />
+    <Reference Include="windows">
+      <HintPath>.\ble2\windows.winmd</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="BLEInfo.cs" />
+    <Compile Include="ClassSerialization.cs" />
+    <Compile Include="ListviewSort.cs" />
     <Compile Include="MainForm.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -61,6 +74,9 @@
     <Compile Include="Views\AboutUs.Designer.cs">
       <DependentUpon>AboutUs.cs</DependentUpon>
     </Compile>
+    <EmbeddedResource Include="MainForm.resx">
+      <DependentUpon>MainForm.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.resx">
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
@@ -90,5 +106,11 @@
     <Folder Include="Service\" />
     <Folder Include="Utils\" />
   </ItemGroup>
+  <ItemGroup>
+    <Content Include="bt.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Connected Services\" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>

+ 451 - 0
BLETool/ClassSerialization.cs

@@ -0,0 +1,451 @@
+using System;
+using System.Windows.Forms;
+using System.Xml.Serialization;
+using System.IO;
+
+namespace MSerialization
+
+{
+
+    public class S_TEXT
+    {
+        public string Text { get; set; }
+    }
+    public class S_CHECKED
+    {
+        public bool Checked { get; set; }
+    }
+    public class S_ITEMS
+    {
+        public string[] items { get; set; }
+    }
+    public enum SAVETYPE
+    {
+        TEXT = 1,
+        CHECKED = 2,
+        ITEMS = 3
+    }
+    public class MSaveControl
+    {
+        //保存,恢复 控件的如下属性
+        //Support 
+        //存储状态      Checkbox RadioButton 
+        //存储文本      TextBox ComboBox
+        //存储ITEMS数组 ListBox
+
+        public static int Save_All_SupportedControls(System.Windows.Forms.Control.ControlCollection controls)
+        {
+
+            try
+            {
+                foreach (System.Windows.Forms.Control control in controls)
+                {
+                    if (!control.HasChildren)
+                    {
+                        if (control.Name != "")
+                        {
+                            if (control is System.Windows.Forms.CheckBox)
+                            {
+                                SaveControl(controls, control.Name, SAVETYPE.CHECKED);
+                            }
+                            if (control is System.Windows.Forms.RadioButton)
+                            {
+                                SaveControl(controls, control.Name, SAVETYPE.CHECKED);
+                            }
+                            if (control is System.Windows.Forms.TextBox)
+                            {
+                                SaveControl(controls, control.Name, SAVETYPE.TEXT);
+                            }
+                            if (control is System.Windows.Forms.ComboBox)
+                            {
+                                SaveControl(controls, control.Name, SAVETYPE.ITEMS);
+                            }
+                            if (control is System.Windows.Forms.ListBox)
+                            {
+                                SaveControl(controls, control.Name, SAVETYPE.ITEMS);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        Save_All_SupportedControls(control.Controls);
+                    }
+                }
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int Load_All_SupportedControls(System.Windows.Forms.Control.ControlCollection controls)
+        {
+            try
+            {
+
+                foreach (System.Windows.Forms.Control control in controls)
+                {
+                    if (!control.HasChildren)
+                    {
+                        if (control.Name != "")
+                        {
+                            if (control is System.Windows.Forms.CheckBox)
+                            {
+                                LoadControl(controls, control.Name, SAVETYPE.CHECKED);
+                            }
+                            if (control is System.Windows.Forms.RadioButton)
+                            {
+                                LoadControl(controls, control.Name, SAVETYPE.CHECKED);
+                            }
+                            if (control is System.Windows.Forms.TextBox)
+                            {
+                                LoadControl(controls, control.Name, SAVETYPE.TEXT);
+                            }
+                            if (control is System.Windows.Forms.ComboBox)
+                            {
+                                LoadControl(controls, control.Name, SAVETYPE.ITEMS);
+                            }
+                            if (control is System.Windows.Forms.ListBox)
+                            {
+                                LoadControl(controls, control.Name, SAVETYPE.ITEMS);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        Load_All_SupportedControls(control.Controls);
+                    }
+                }
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int SaveControl(System.Windows.Forms.Control.ControlCollection controls, string s_control, SAVETYPE savetype)
+        {
+            try
+            {
+                object control = controls.Find(s_control, true)[0];
+                //如果要保存的是Text属性,可以直接用基类Control保存,无需识别控件类型
+                if (savetype == SAVETYPE.TEXT)
+                {
+                    SavetoFile(Application.StartupPath + "\\control_" + s_control + ".xml", (System.Windows.Forms.Control)control);
+                }
+                //如果要保存的是CHECKED属性,需要先判别控件具体类型,做强制转换后再处理
+                else if (savetype == SAVETYPE.CHECKED)
+                {
+                    if (control is System.Windows.Forms.CheckBox)
+                    {
+                        SavetoFile(Application.StartupPath + "\\control_" + s_control + ".xml", (System.Windows.Forms.CheckBox)control);
+                    }
+                    else if (control is System.Windows.Forms.RadioButton)
+                    {
+                        SavetoFile(Application.StartupPath + "\\control_" + s_control + ".xml", (System.Windows.Forms.RadioButton)control);
+                    }
+                }
+                //如果要保存的是ITEMS属性,需要先判别控件具体类型,做强制转换后再处理
+                else if (savetype == SAVETYPE.ITEMS)
+                {
+                    if (control is System.Windows.Forms.ListBox)
+                    {
+                        SavetoFile(Application.StartupPath + "\\control_" + s_control + ".xml", (System.Windows.Forms.ListBox)control);
+                    }
+                    else if (control is System.Windows.Forms.ComboBox)
+                    {
+                        SavetoFile(Application.StartupPath + "\\control_" + s_control + ".xml", (System.Windows.Forms.ComboBox)control);
+                    }
+
+                }
+
+            }
+            catch (Exception ee)
+            {
+               // MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int LoadControl(System.Windows.Forms.Control.ControlCollection controls, string s_control, SAVETYPE savetype)
+        {
+            try
+            {
+
+                if (savetype == SAVETYPE.TEXT)
+                {
+                    S_TEXT sptr;
+
+                    XmlSerializer serializer = new XmlSerializer(typeof(S_TEXT));
+                    TextReader reader = new StreamReader(Application.StartupPath + "\\control_" + s_control + ".xml");
+                    sptr = (S_TEXT)(serializer.Deserialize(reader));
+                    reader.Close();
+                    System.Windows.Forms.Control control = controls.Find(s_control, true)[0];
+                    control.Text = sptr.Text;
+                }
+                else if (savetype == SAVETYPE.CHECKED)
+                {
+                    object control = controls.Find(s_control, true)[0];
+                    if (control is System.Windows.Forms.CheckBox)
+                    {
+                        System.Windows.Forms.CheckBox acontrol = (System.Windows.Forms.CheckBox)control;
+                        LoadfromFile(Application.StartupPath + "\\control_" + s_control + ".xml", ref acontrol);
+                    }
+                    else if (control is System.Windows.Forms.RadioButton)
+                    {
+                        System.Windows.Forms.RadioButton acontrol = (System.Windows.Forms.RadioButton)control;
+                        LoadfromFile(Application.StartupPath + "\\control_" + s_control + ".xml", ref acontrol);
+                    }
+                }
+                else if (savetype == SAVETYPE.ITEMS)
+                {
+                    object control = controls.Find(s_control, true)[0];
+                    if (control is System.Windows.Forms.ListBox)
+                    {
+                        System.Windows.Forms.ListBox acontrol = (System.Windows.Forms.ListBox)control;
+                        LoadfromFile(Application.StartupPath + "\\control_" + s_control + ".xml", ref acontrol);
+                    }
+                    else if (control is System.Windows.Forms.ComboBox)
+                    {
+                        System.Windows.Forms.ComboBox acontrol = (System.Windows.Forms.ComboBox)control;
+                        LoadfromFile(Application.StartupPath + "\\control_" + s_control + ".xml", ref acontrol);
+                    }
+
+                }
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+
+        public static int SavetoFile(String filename, System.Windows.Forms.Control control)
+        {
+            S_TEXT stxt = new S_TEXT();
+            stxt.Text = control.Text;
+
+            try
+            {
+                XmlSerializer serializer = new XmlSerializer(typeof(S_TEXT));
+                TextWriter writer = new StreamWriter(filename);
+                serializer.Serialize(writer, stxt);
+                writer.Close();
+            }
+
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+
+            return 1;
+        }
+        public static int SavetoFile(String filename, System.Windows.Forms.CheckBox checkbox)
+        {
+            S_CHECKED s_Checked = new S_CHECKED();
+            s_Checked.Checked = checkbox.Checked;
+
+            try
+            {
+                XmlSerializer serializer = new XmlSerializer(typeof(S_CHECKED));
+                TextWriter writer = new StreamWriter(filename);
+                serializer.Serialize(writer, s_Checked);
+                writer.Close();
+            }
+
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+
+            return 1;
+        }
+        public static int SavetoFile(String filename, System.Windows.Forms.RadioButton radbox)
+        {
+            S_CHECKED s_Checked = new S_CHECKED();
+            s_Checked.Checked = radbox.Checked;
+
+            try
+            {
+                XmlSerializer serializer = new XmlSerializer(typeof(S_CHECKED));
+                TextWriter writer = new StreamWriter(filename);
+                serializer.Serialize(writer, s_Checked);
+                writer.Close();
+            }
+
+            catch (Exception ee)
+            {
+               // MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+
+            return 1;
+        }
+        public static int SavetoFile(String filename, System.Windows.Forms.ListBox listbox)
+        {
+            S_ITEMS s_items = new S_ITEMS();
+            s_items.items = new string[listbox.Items.Count];
+
+            for (int i = 0; i < listbox.Items.Count; i++)
+            {
+                s_items.items[i] = (string)listbox.Items[i];
+            }
+
+            try
+            {
+                XmlSerializer serializer = new XmlSerializer(typeof(S_ITEMS));
+                TextWriter writer = new StreamWriter(filename);
+                serializer.Serialize(writer, s_items);
+                writer.Close();
+            }
+
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+
+            return 1;
+        }
+        public static int SavetoFile(String filename, System.Windows.Forms.ComboBox combobox)
+        {
+            S_ITEMS s_items = new S_ITEMS();
+            s_items.items = new string[combobox.Items.Count];
+
+            for (int i = 0; i < combobox.Items.Count; i++)
+            {
+                s_items.items[i] = (string)combobox.Items[i];
+            }
+
+            try
+            {
+                XmlSerializer serializer = new XmlSerializer(typeof(S_ITEMS));
+                TextWriter writer = new StreamWriter(filename);
+                serializer.Serialize(writer, s_items);
+                writer.Close();
+            }
+
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+
+            return 1;
+        }
+        public static int LoadfromFile(String filename, ref System.Windows.Forms.ListBox listbox)
+        {
+            try
+            {
+                S_ITEMS sptr;
+
+                XmlSerializer serializer = new XmlSerializer(typeof(S_ITEMS));
+                TextReader reader = new StreamReader(filename);
+                sptr = (S_ITEMS)(serializer.Deserialize(reader));
+                reader.Close();
+                listbox.Items.Clear();
+                for (int i = 0; i < sptr.items.Length; i++)
+                {
+                    listbox.Items.Add(sptr.items[i]);
+                }
+
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int LoadfromFile(String filename, ref System.Windows.Forms.ComboBox combobox)
+        {
+            try
+            {
+                S_ITEMS sptr;
+
+                XmlSerializer serializer = new XmlSerializer(typeof(S_ITEMS));
+                TextReader reader = new StreamReader(filename);
+                sptr = (S_ITEMS)(serializer.Deserialize(reader));
+                reader.Close();
+                combobox.Items.Clear();
+                for (int i = 0; i < sptr.items.Length; i++)
+                {
+                    combobox.Items.Add(sptr.items[i]);
+                }
+
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int LoadfromFile(String filename, ref System.Windows.Forms.Control control)
+        {
+            try
+            {
+                S_TEXT sptr;
+
+                XmlSerializer serializer = new XmlSerializer(typeof(S_TEXT));
+                TextReader reader = new StreamReader(filename);
+                sptr = (S_TEXT)(serializer.Deserialize(reader));
+                reader.Close();
+                control.Text = sptr.Text;
+
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int LoadfromFile(String filename, ref System.Windows.Forms.CheckBox control)
+        {
+
+            try
+            {
+                S_CHECKED sptr;
+
+                XmlSerializer serializer = new XmlSerializer(typeof(S_CHECKED));
+                TextReader reader = new StreamReader(filename);
+                sptr = (S_CHECKED)(serializer.Deserialize(reader));
+                reader.Close();
+                control.Checked = sptr.Checked;
+
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+        public static int LoadfromFile(String filename, ref System.Windows.Forms.RadioButton control)
+        {
+            try
+            {
+                S_CHECKED sptr;
+
+                XmlSerializer serializer = new XmlSerializer(typeof(S_CHECKED));
+                TextReader reader = new StreamReader(filename);
+                sptr = (S_CHECKED)(serializer.Deserialize(reader));
+                reader.Close();
+                control.Checked = sptr.Checked;
+
+            }
+            catch (Exception ee)
+            {
+                //MessageBox.Show(ee.StackTrace, ee.Message);
+                return 0;
+            }
+            return 1;
+        }
+    }
+}

+ 79 - 0
BLETool/ListviewSort.cs

@@ -0,0 +1,79 @@
+using System.Collections;
+using System.Windows.Forms;
+
+namespace BLETool
+{
+    public class ListViewColumnSorter : IComparer
+    {
+        private int ColumnToSort;// 指定按照哪个列排序      
+        private SortOrder OrderOfSort;// 指定排序的方式               
+        private CaseInsensitiveComparer ObjectCompare;// 声明CaseInsensitiveComparer类对象,
+        public ListViewColumnSorter()// 构造函数
+        {
+            ColumnToSort = 0;// 默认按第一列排序            
+            OrderOfSort = SortOrder.None;// 排序方式为不排序            
+            ObjectCompare = new CaseInsensitiveComparer();// 初始化CaseInsensitiveComparer类对象
+        }
+
+
+        // 重写IComparer接口.        
+        // <returns>比较的结果.如果相等返回0,如果x大于y返回1,如果x小于y返回-1</returns>
+        public int Compare(object x, object y)
+        {
+            int compareResult = 1;
+            ListViewItem listviewX, listviewY;
+            // 将比较对象转换为ListViewItem对象
+            listviewX = (ListViewItem)x;
+            listviewY = (ListViewItem)y;
+
+            int val1 = 0, val2 = 0;
+            if (int.TryParse(listviewX.SubItems[ColumnToSort].Text, out val1) && int.TryParse(listviewY.SubItems[ColumnToSort].Text, out val2))
+            {
+                compareResult = ObjectCompare.Compare(val1, val2);
+            }
+            else
+            {
+                // 比较
+                compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
+            }
+            // 根据上面的比较结果返回正确的比较结果
+            if (OrderOfSort == SortOrder.Ascending)
+            {   // 因为是正序排序,所以直接返回结果
+                return compareResult;
+            }
+            else if (OrderOfSort == SortOrder.Descending)
+            {  // 如果是反序排序,所以要取负值再返回
+                return (-compareResult);
+            }
+            else
+            {
+                // 如果相等返回0
+                return 0;
+            }
+        }
+        /// 获取或设置按照哪一列排序.        
+        public int SortColumn
+        {
+            set
+            {
+                ColumnToSort = value;
+            }
+            get
+            {
+                return ColumnToSort;
+            }
+        }
+        /// 获取或设置排序方式.    
+        public SortOrder Order
+        {
+            set
+            {
+                OrderOfSort = value;
+            }
+            get
+            {
+                return OrderOfSort;
+            }
+        }
+    }
+}

+ 1090 - 9
BLETool/MainForm.Designer.cs

@@ -3,14 +3,14 @@
     partial class MainForm
     {
         /// <summary>
-        /// Required designer variable.
+        /// 必需的设计器变量。
         /// </summary>
         private System.ComponentModel.IContainer components = null;
 
         /// <summary>
-        /// Clean up any resources being used.
+        /// 清理所有正在使用的资源。
         /// </summary>
-        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
         protected override void Dispose(bool disposing)
         {
             if (disposing && (components != null))
@@ -20,21 +20,1102 @@
             base.Dispose(disposing);
         }
 
-        #region Windows Form Designer generated code
+        #region Windows 窗体设计器生成的代码
 
         /// <summary>
-        /// Required method for Designer support - do not modify
-        /// the contents of this method with the code editor.
+        /// 设计器支持所需的方法 - 不要修改
+        /// 使用代码编辑器修改此方法的内容。
         /// </summary>
         private void InitializeComponent()
         {
-            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
+            this.ss_bottom = new System.Windows.Forms.StatusStrip();
+            this.btn_Stop = new System.Windows.Forms.Button();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.lv_device = new System.Windows.Forms.ListView();
+            this.DevName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+            this.ID = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+            this.CanPaired = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+            this.IsPaired = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+            this.RSSI = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+            this.panel1 = new System.Windows.Forms.Panel();
+            this.pan_search = new System.Windows.Forms.Panel();
+            this.chb_ShowUnconnectableDevice = new System.Windows.Forms.CheckBox();
+            this.chb_ShowHidden = new System.Windows.Forms.CheckBox();
+            this.panel8 = new System.Windows.Forms.Panel();
+            this.btn_Refresh = new System.Windows.Forms.Button();
+            this.panel5 = new System.Windows.Forms.Panel();
+            this.panel6 = new System.Windows.Forms.Panel();
+            this.btn_Search = new System.Windows.Forms.Button();
+            this.panel4 = new System.Windows.Forms.Panel();
+            this.panel7 = new System.Windows.Forms.Panel();
+            this.groupBox3 = new System.Windows.Forms.GroupBox();
+            this.textBox3 = new System.Windows.Forms.TextBox();
+            this.textBox2 = new System.Windows.Forms.TextBox();
+            this.textBox1 = new System.Windows.Forms.TextBox();
+            this.button3 = new System.Windows.Forms.Button();
+            this.button2 = new System.Windows.Forms.Button();
+            this.button1 = new System.Windows.Forms.Button();
+            this.groupBox4 = new System.Windows.Forms.GroupBox();
+            this.btn_getData = new System.Windows.Forms.Button();
+            this.btn_refreshCharacteristic = new System.Windows.Forms.Button();
+            this.cmb_service = new System.Windows.Forms.ComboBox();
+            this.lab_servicecount = new System.Windows.Forms.Label();
+            this.cmb_characteristic = new System.Windows.Forms.ComboBox();
+            this.lab_charcount = new System.Windows.Forms.Label();
+            this.groupBox5 = new System.Windows.Forms.GroupBox();
+            this.rtb_debug = new System.Windows.Forms.RichTextBox();
+            this.groupBox6 = new System.Windows.Forms.GroupBox();
+            this.groupBox9 = new System.Windows.Forms.GroupBox();
+            this.txt_callback_service = new System.Windows.Forms.TextBox();
+            this.cb_display_callbackData = new System.Windows.Forms.CheckBox();
+            this.label5 = new System.Windows.Forms.Label();
+            this.label4 = new System.Windows.Forms.Label();
+            this.txt_callback_characteristic = new System.Windows.Forms.TextBox();
+            this.pan_read = new System.Windows.Forms.Panel();
+            this.btn_Read = new System.Windows.Forms.Button();
+            this.lab_Indicate = new System.Windows.Forms.Label();
+            this.lab_Notify = new System.Windows.Forms.Label();
+            this.panel18 = new System.Windows.Forms.Panel();
+            this.panel16 = new System.Windows.Forms.Panel();
+            this.pan_hex = new System.Windows.Forms.Panel();
+            this.txt_HexResult = new System.Windows.Forms.TextBox();
+            this.rad_hex = new System.Windows.Forms.RadioButton();
+            this.panel15 = new System.Windows.Forms.Panel();
+            this.pan_utf8 = new System.Windows.Forms.Panel();
+            this.txt_UTF8Result = new System.Windows.Forms.TextBox();
+            this.rad_utf8 = new System.Windows.Forms.RadioButton();
+            this.panel17 = new System.Windows.Forms.Panel();
+            this.pan_dec = new System.Windows.Forms.Panel();
+            this.txt_DecResult = new System.Windows.Forms.TextBox();
+            this.rad_dec = new System.Windows.Forms.RadioButton();
+            this.groupBox7 = new System.Windows.Forms.GroupBox();
+            this.pan_write = new System.Windows.Forms.Panel();
+            this.txt_write = new System.Windows.Forms.ComboBox();
+            this.panel9 = new System.Windows.Forms.Panel();
+            this.rad_writeUTF8 = new System.Windows.Forms.RadioButton();
+            this.rad_writeHex = new System.Windows.Forms.RadioButton();
+            this.rad_writeDec = new System.Windows.Forms.RadioButton();
+            this.btn_Write = new System.Windows.Forms.Button();
+            this.panel10 = new System.Windows.Forms.Panel();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.groupBox8 = new System.Windows.Forms.GroupBox();
+            this.pan_connection = new System.Windows.Forms.Panel();
+            this.txt_deviceID = new System.Windows.Forms.TextBox();
+            this.label3 = new System.Windows.Forms.Label();
+            this.txt_rssi = new System.Windows.Forms.TextBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.txt_devicename = new System.Windows.Forms.TextBox();
+            this.panel13 = new System.Windows.Forms.Panel();
+            this.label1 = new System.Windows.Forms.Label();
+            this.lab_connection = new System.Windows.Forms.Label();
+            this.panel12 = new System.Windows.Forms.Panel();
+            this.btn_pair = new System.Windows.Forms.Button();
+            this.panel2 = new System.Windows.Forms.Panel();
+            this.groupBox2.SuspendLayout();
+            this.panel1.SuspendLayout();
+            this.pan_search.SuspendLayout();
+            this.groupBox3.SuspendLayout();
+            this.groupBox4.SuspendLayout();
+            this.groupBox5.SuspendLayout();
+            this.groupBox6.SuspendLayout();
+            this.groupBox9.SuspendLayout();
+            this.pan_read.SuspendLayout();
+            this.pan_hex.SuspendLayout();
+            this.pan_utf8.SuspendLayout();
+            this.pan_dec.SuspendLayout();
+            this.groupBox7.SuspendLayout();
+            this.pan_write.SuspendLayout();
+            this.groupBox1.SuspendLayout();
+            this.groupBox8.SuspendLayout();
+            this.pan_connection.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // ss_bottom
+            // 
+            this.ss_bottom.Location = new System.Drawing.Point(0, 727);
+            this.ss_bottom.Name = "ss_bottom";
+            this.ss_bottom.Size = new System.Drawing.Size(858, 22);
+            this.ss_bottom.TabIndex = 15;
+            this.ss_bottom.Text = "statusStrip1";
+            // 
+            // btn_Stop
+            // 
+            this.btn_Stop.Dock = System.Windows.Forms.DockStyle.Left;
+            this.btn_Stop.Location = new System.Drawing.Point(111, 0);
+            this.btn_Stop.Name = "btn_Stop";
+            this.btn_Stop.Size = new System.Drawing.Size(91, 26);
+            this.btn_Stop.TabIndex = 20;
+            this.btn_Stop.Text = "停止";
+            this.btn_Stop.UseVisualStyleBackColor = true;
+            this.btn_Stop.Click += new System.EventHandler(this.btn_Stop_Click);
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Controls.Add(this.lv_device);
+            this.groupBox2.Controls.Add(this.panel1);
+            this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.groupBox2.Location = new System.Drawing.Point(0, 0);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(858, 139);
+            this.groupBox2.TabIndex = 22;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "BLE设备列表";
+            // 
+            // lv_device
+            // 
+            this.lv_device.BackColor = System.Drawing.Color.White;
+            this.lv_device.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.DevName,
+            this.ID,
+            this.CanPaired,
+            this.IsPaired,
+            this.RSSI});
+            this.lv_device.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.lv_device.FullRowSelect = true;
+            this.lv_device.GridLines = true;
+            this.lv_device.HideSelection = false;
+            this.lv_device.Location = new System.Drawing.Point(3, 17);
+            this.lv_device.Name = "lv_device";
+            this.lv_device.Size = new System.Drawing.Size(852, 77);
+            this.lv_device.TabIndex = 23;
+            this.lv_device.UseCompatibleStateImageBehavior = false;
+            this.lv_device.View = System.Windows.Forms.View.Details;
+            this.lv_device.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lv_device_ColumnClick);
+            this.lv_device.SelectedIndexChanged += new System.EventHandler(this.lv_device_SelectedIndexChanged);
+            this.lv_device.SizeChanged += new System.EventHandler(this.lv_device_SizeChanged);
+            this.lv_device.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lv_device_MouseDoubleClick);
+            // 
+            // DevName
+            // 
+            this.DevName.Text = "DevName";
+            this.DevName.Width = 200;
+            // 
+            // ID
+            // 
+            this.ID.Text = "ID";
+            this.ID.Width = 360;
+            // 
+            // CanPaired
+            // 
+            this.CanPaired.Text = "Can Paired";
+            this.CanPaired.Width = 100;
+            // 
+            // IsPaired
+            // 
+            this.IsPaired.Text = "Is Paired";
+            this.IsPaired.Width = 100;
+            // 
+            // RSSI
+            // 
+            this.RSSI.Text = "RSSI";
+            // 
+            // panel1
+            // 
+            this.panel1.Controls.Add(this.pan_search);
+            this.panel1.Controls.Add(this.panel7);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.panel1.Location = new System.Drawing.Point(3, 94);
+            this.panel1.Name = "panel1";
+            this.panel1.Size = new System.Drawing.Size(852, 42);
+            this.panel1.TabIndex = 24;
+            // 
+            // pan_search
+            // 
+            this.pan_search.Controls.Add(this.chb_ShowUnconnectableDevice);
+            this.pan_search.Controls.Add(this.chb_ShowHidden);
+            this.pan_search.Controls.Add(this.panel8);
+            this.pan_search.Controls.Add(this.btn_Refresh);
+            this.pan_search.Controls.Add(this.panel5);
+            this.pan_search.Controls.Add(this.btn_Stop);
+            this.pan_search.Controls.Add(this.panel6);
+            this.pan_search.Controls.Add(this.btn_Search);
+            this.pan_search.Controls.Add(this.panel4);
+            this.pan_search.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.pan_search.Location = new System.Drawing.Point(0, 9);
+            this.pan_search.Name = "pan_search";
+            this.pan_search.Size = new System.Drawing.Size(852, 26);
+            this.pan_search.TabIndex = 23;
+            // 
+            // chb_ShowUnconnectableDevice
+            // 
+            this.chb_ShowUnconnectableDevice.AutoSize = true;
+            this.chb_ShowUnconnectableDevice.Dock = System.Windows.Forms.DockStyle.Left;
+            this.chb_ShowUnconnectableDevice.Location = new System.Drawing.Point(308, 0);
+            this.chb_ShowUnconnectableDevice.Name = "chb_ShowUnconnectableDevice";
+            this.chb_ShowUnconnectableDevice.Size = new System.Drawing.Size(96, 26);
+            this.chb_ShowUnconnectableDevice.TabIndex = 31;
+            this.chb_ShowUnconnectableDevice.Text = "显示离线设备";
+            this.chb_ShowUnconnectableDevice.UseVisualStyleBackColor = true;
+            this.chb_ShowUnconnectableDevice.CheckedChanged += new System.EventHandler(this.chb_ShowUnconnectableDevice_CheckedChanged);
+            // 
+            // chb_ShowHidden
+            // 
+            this.chb_ShowHidden.AutoSize = true;
+            this.chb_ShowHidden.Dock = System.Windows.Forms.DockStyle.Left;
+            this.chb_ShowHidden.Location = new System.Drawing.Point(212, 0);
+            this.chb_ShowHidden.Name = "chb_ShowHidden";
+            this.chb_ShowHidden.Size = new System.Drawing.Size(96, 26);
+            this.chb_ShowHidden.TabIndex = 30;
+            this.chb_ShowHidden.Text = "显示隐藏设备";
+            this.chb_ShowHidden.UseVisualStyleBackColor = true;
+            // 
+            // panel8
+            // 
+            this.panel8.Dock = System.Windows.Forms.DockStyle.Left;
+            this.panel8.Location = new System.Drawing.Point(202, 0);
+            this.panel8.Name = "panel8";
+            this.panel8.Size = new System.Drawing.Size(10, 26);
+            this.panel8.TabIndex = 29;
+            // 
+            // btn_Refresh
+            // 
+            this.btn_Refresh.Dock = System.Windows.Forms.DockStyle.Right;
+            this.btn_Refresh.Location = new System.Drawing.Point(751, 0);
+            this.btn_Refresh.Name = "btn_Refresh";
+            this.btn_Refresh.Size = new System.Drawing.Size(91, 26);
+            this.btn_Refresh.TabIndex = 27;
+            this.btn_Refresh.Text = "读取信号强度";
+            this.btn_Refresh.UseVisualStyleBackColor = true;
+            this.btn_Refresh.Click += new System.EventHandler(this.btn_Refresh_Click);
+            // 
+            // panel5
+            // 
+            this.panel5.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel5.Location = new System.Drawing.Point(842, 0);
+            this.panel5.Name = "panel5";
+            this.panel5.Size = new System.Drawing.Size(10, 26);
+            this.panel5.TabIndex = 28;
+            // 
+            // panel6
+            // 
+            this.panel6.Dock = System.Windows.Forms.DockStyle.Left;
+            this.panel6.Location = new System.Drawing.Point(101, 0);
+            this.panel6.Name = "panel6";
+            this.panel6.Size = new System.Drawing.Size(10, 26);
+            this.panel6.TabIndex = 26;
+            // 
+            // btn_Search
+            // 
+            this.btn_Search.Dock = System.Windows.Forms.DockStyle.Left;
+            this.btn_Search.Location = new System.Drawing.Point(10, 0);
+            this.btn_Search.Name = "btn_Search";
+            this.btn_Search.Size = new System.Drawing.Size(91, 26);
+            this.btn_Search.TabIndex = 19;
+            this.btn_Search.Text = "搜索";
+            this.btn_Search.UseVisualStyleBackColor = true;
+            this.btn_Search.Click += new System.EventHandler(this.btn_Search_Click);
+            // 
+            // panel4
+            // 
+            this.panel4.Dock = System.Windows.Forms.DockStyle.Left;
+            this.panel4.Location = new System.Drawing.Point(0, 0);
+            this.panel4.Name = "panel4";
+            this.panel4.Size = new System.Drawing.Size(10, 26);
+            this.panel4.TabIndex = 24;
+            // 
+            // panel7
+            // 
+            this.panel7.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.panel7.Location = new System.Drawing.Point(0, 35);
+            this.panel7.Name = "panel7";
+            this.panel7.Size = new System.Drawing.Size(852, 7);
+            this.panel7.TabIndex = 24;
+            // 
+            // groupBox3
+            // 
+            this.groupBox3.Controls.Add(this.textBox3);
+            this.groupBox3.Controls.Add(this.textBox2);
+            this.groupBox3.Controls.Add(this.textBox1);
+            this.groupBox3.Controls.Add(this.button3);
+            this.groupBox3.Controls.Add(this.button2);
+            this.groupBox3.Controls.Add(this.button1);
+            this.groupBox3.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.groupBox3.Location = new System.Drawing.Point(0, 627);
+            this.groupBox3.Name = "groupBox3";
+            this.groupBox3.Size = new System.Drawing.Size(858, 100);
+            this.groupBox3.TabIndex = 23;
+            this.groupBox3.TabStop = false;
+            this.groupBox3.Text = "Control";
+            this.groupBox3.Visible = false;
+            // 
+            // textBox3
+            // 
+            this.textBox3.Location = new System.Drawing.Point(562, 20);
+            this.textBox3.Name = "textBox3";
+            this.textBox3.Size = new System.Drawing.Size(71, 21);
+            this.textBox3.TabIndex = 24;
+            this.textBox3.Text = "180";
+            this.textBox3.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+            // 
+            // textBox2
+            // 
+            this.textBox2.Location = new System.Drawing.Point(347, 20);
+            this.textBox2.Name = "textBox2";
+            this.textBox2.Size = new System.Drawing.Size(71, 21);
+            this.textBox2.TabIndex = 23;
+            this.textBox2.Text = "90";
+            this.textBox2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+            // 
+            // textBox1
+            // 
+            this.textBox1.Location = new System.Drawing.Point(155, 20);
+            this.textBox1.Name = "textBox1";
+            this.textBox1.Size = new System.Drawing.Size(71, 21);
+            this.textBox1.TabIndex = 22;
+            this.textBox1.Text = "0";
+            this.textBox1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+            // 
+            // button3
+            // 
+            this.button3.Location = new System.Drawing.Point(521, 47);
+            this.button3.Name = "button3";
+            this.button3.Size = new System.Drawing.Size(143, 38);
+            this.button3.TabIndex = 21;
+            this.button3.Text = "Do not disturb";
+            this.button3.UseVisualStyleBackColor = true;
+            this.button3.Click += new System.EventHandler(this.button3_Click);
+            // 
+            // button2
+            // 
+            this.button2.Location = new System.Drawing.Point(319, 47);
+            this.button2.Name = "button2";
+            this.button2.Size = new System.Drawing.Size(143, 38);
+            this.button2.TabIndex = 20;
+            this.button2.Text = "Working";
+            this.button2.UseVisualStyleBackColor = true;
+            this.button2.Click += new System.EventHandler(this.button2_Click);
+            // 
+            // button1
+            // 
+            this.button1.Location = new System.Drawing.Point(119, 47);
+            this.button1.Name = "button1";
+            this.button1.Size = new System.Drawing.Size(143, 38);
+            this.button1.TabIndex = 19;
+            this.button1.Text = "Free";
+            this.button1.UseVisualStyleBackColor = true;
+            this.button1.Click += new System.EventHandler(this.button1_Click);
+            // 
+            // groupBox4
+            // 
+            this.groupBox4.Controls.Add(this.btn_getData);
+            this.groupBox4.Controls.Add(this.btn_refreshCharacteristic);
+            this.groupBox4.Controls.Add(this.cmb_service);
+            this.groupBox4.Controls.Add(this.lab_servicecount);
+            this.groupBox4.Controls.Add(this.cmb_characteristic);
+            this.groupBox4.Controls.Add(this.lab_charcount);
+            this.groupBox4.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.groupBox4.Location = new System.Drawing.Point(0, 189);
+            this.groupBox4.Name = "groupBox4";
+            this.groupBox4.Size = new System.Drawing.Size(858, 48);
+            this.groupBox4.TabIndex = 24;
+            this.groupBox4.TabStop = false;
+            this.groupBox4.Enter += new System.EventHandler(this.groupBox4_Enter);
+            // 
+            // btn_getData
+            // 
+            this.btn_getData.Location = new System.Drawing.Point(788, 17);
+            this.btn_getData.Name = "btn_getData";
+            this.btn_getData.Size = new System.Drawing.Size(59, 24);
+            this.btn_getData.TabIndex = 29;
+            this.btn_getData.Text = "读取";
+            this.btn_getData.UseVisualStyleBackColor = true;
+            this.btn_getData.Click += new System.EventHandler(this.btn_getData_Click);
+            // 
+            // btn_refreshCharacteristic
+            // 
+            this.btn_refreshCharacteristic.Location = new System.Drawing.Point(344, 17);
+            this.btn_refreshCharacteristic.Name = "btn_refreshCharacteristic";
+            this.btn_refreshCharacteristic.Size = new System.Drawing.Size(59, 24);
+            this.btn_refreshCharacteristic.TabIndex = 28;
+            this.btn_refreshCharacteristic.Text = "读取";
+            this.btn_refreshCharacteristic.UseVisualStyleBackColor = true;
+            this.btn_refreshCharacteristic.Click += new System.EventHandler(this.btn_refreshCharacteristic_Click);
+            // 
+            // cmb_service
+            // 
+            this.cmb_service.Font = new System.Drawing.Font("SimSun", 12F);
+            this.cmb_service.FormattingEnabled = true;
+            this.cmb_service.Location = new System.Drawing.Point(71, 17);
+            this.cmb_service.Name = "cmb_service";
+            this.cmb_service.Size = new System.Drawing.Size(267, 24);
+            this.cmb_service.TabIndex = 4;
+            this.cmb_service.SelectedIndexChanged += new System.EventHandler(this.cmb_service_SelectedIndexChanged);
+            // 
+            // lab_servicecount
+            // 
+            this.lab_servicecount.AutoSize = true;
+            this.lab_servicecount.Location = new System.Drawing.Point(12, 23);
+            this.lab_servicecount.Name = "lab_servicecount";
+            this.lab_servicecount.Size = new System.Drawing.Size(29, 12);
+            this.lab_servicecount.TabIndex = 3;
+            this.lab_servicecount.Text = "服务";
+            // 
+            // cmb_characteristic
+            // 
+            this.cmb_characteristic.Font = new System.Drawing.Font("SimSun", 12F);
+            this.cmb_characteristic.FormattingEnabled = true;
+            this.cmb_characteristic.Location = new System.Drawing.Point(475, 17);
+            this.cmb_characteristic.Name = "cmb_characteristic";
+            this.cmb_characteristic.Size = new System.Drawing.Size(307, 24);
+            this.cmb_characteristic.TabIndex = 2;
+            this.cmb_characteristic.SelectedIndexChanged += new System.EventHandler(this.cmb_characteristic_SelectedIndexChanged);
+            // 
+            // lab_charcount
+            // 
+            this.lab_charcount.AutoSize = true;
+            this.lab_charcount.Location = new System.Drawing.Point(420, 24);
+            this.lab_charcount.Name = "lab_charcount";
+            this.lab_charcount.Size = new System.Drawing.Size(29, 12);
+            this.lab_charcount.TabIndex = 1;
+            this.lab_charcount.Text = "特征";
+            // 
+            // groupBox5
+            // 
+            this.groupBox5.Controls.Add(this.rtb_debug);
+            this.groupBox5.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.groupBox5.Location = new System.Drawing.Point(0, 521);
+            this.groupBox5.Name = "groupBox5";
+            this.groupBox5.Size = new System.Drawing.Size(858, 106);
+            this.groupBox5.TabIndex = 25;
+            this.groupBox5.TabStop = false;
+            this.groupBox5.Text = "Log记录";
+            // 
+            // rtb_debug
+            // 
+            this.rtb_debug.BackColor = System.Drawing.SystemColors.Control;
+            this.rtb_debug.BorderStyle = System.Windows.Forms.BorderStyle.None;
+            this.rtb_debug.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.rtb_debug.Location = new System.Drawing.Point(3, 17);
+            this.rtb_debug.Name = "rtb_debug";
+            this.rtb_debug.ReadOnly = true;
+            this.rtb_debug.Size = new System.Drawing.Size(852, 86);
+            this.rtb_debug.TabIndex = 11;
+            this.rtb_debug.Text = "";
+            // 
+            // groupBox6
+            // 
+            this.groupBox6.Controls.Add(this.groupBox9);
+            this.groupBox6.Controls.Add(this.pan_read);
+            this.groupBox6.Controls.Add(this.panel16);
+            this.groupBox6.Controls.Add(this.pan_hex);
+            this.groupBox6.Controls.Add(this.panel15);
+            this.groupBox6.Controls.Add(this.pan_utf8);
+            this.groupBox6.Controls.Add(this.panel17);
+            this.groupBox6.Controls.Add(this.pan_dec);
+            this.groupBox6.Dock = System.Windows.Forms.DockStyle.Top;
+            this.groupBox6.Location = new System.Drawing.Point(3, 17);
+            this.groupBox6.Name = "groupBox6";
+            this.groupBox6.Size = new System.Drawing.Size(852, 206);
+            this.groupBox6.TabIndex = 26;
+            this.groupBox6.TabStop = false;
+            this.groupBox6.Text = "读取";
+            // 
+            // groupBox9
+            // 
+            this.groupBox9.Controls.Add(this.txt_callback_service);
+            this.groupBox9.Controls.Add(this.cb_display_callbackData);
+            this.groupBox9.Controls.Add(this.label5);
+            this.groupBox9.Controls.Add(this.label4);
+            this.groupBox9.Controls.Add(this.txt_callback_characteristic);
+            this.groupBox9.Dock = System.Windows.Forms.DockStyle.Top;
+            this.groupBox9.Location = new System.Drawing.Point(3, 155);
+            this.groupBox9.Name = "groupBox9";
+            this.groupBox9.Size = new System.Drawing.Size(846, 50);
+            this.groupBox9.TabIndex = 45;
+            this.groupBox9.TabStop = false;
+            this.groupBox9.Text = "订阅特征";
+            // 
+            // txt_callback_service
+            // 
+            this.txt_callback_service.Location = new System.Drawing.Point(41, 20);
+            this.txt_callback_service.Name = "txt_callback_service";
+            this.txt_callback_service.ReadOnly = true;
+            this.txt_callback_service.Size = new System.Drawing.Size(272, 21);
+            this.txt_callback_service.TabIndex = 6;
+            // 
+            // cb_display_callbackData
+            // 
+            this.cb_display_callbackData.AutoSize = true;
+            this.cb_display_callbackData.Location = new System.Drawing.Point(656, 20);
+            this.cb_display_callbackData.Name = "cb_display_callbackData";
+            this.cb_display_callbackData.Size = new System.Drawing.Size(180, 16);
+            this.cb_display_callbackData.TabIndex = 44;
+            this.cb_display_callbackData.Text = "在读取栏显示订阅特征的数据";
+            this.cb_display_callbackData.UseVisualStyleBackColor = true;
+            this.cb_display_callbackData.Click += new System.EventHandler(this.cb_display_callbackData_Click);
+            // 
+            // label5
+            // 
+            this.label5.AutoSize = true;
+            this.label5.Location = new System.Drawing.Point(328, 24);
+            this.label5.Name = "label5";
+            this.label5.Size = new System.Drawing.Size(29, 12);
+            this.label5.TabIndex = 4;
+            this.label5.Text = "特征";
+            // 
+            // label4
+            // 
+            this.label4.AutoSize = true;
+            this.label4.Location = new System.Drawing.Point(10, 24);
+            this.label4.Name = "label4";
+            this.label4.Size = new System.Drawing.Size(29, 12);
+            this.label4.TabIndex = 5;
+            this.label4.Text = "服务";
+            // 
+            // txt_callback_characteristic
+            // 
+            this.txt_callback_characteristic.Location = new System.Drawing.Point(363, 20);
+            this.txt_callback_characteristic.Name = "txt_callback_characteristic";
+            this.txt_callback_characteristic.ReadOnly = true;
+            this.txt_callback_characteristic.Size = new System.Drawing.Size(272, 21);
+            this.txt_callback_characteristic.TabIndex = 7;
+            // 
+            // pan_read
+            // 
+            this.pan_read.Controls.Add(this.btn_Read);
+            this.pan_read.Controls.Add(this.lab_Indicate);
+            this.pan_read.Controls.Add(this.lab_Notify);
+            this.pan_read.Controls.Add(this.panel18);
+            this.pan_read.Dock = System.Windows.Forms.DockStyle.Top;
+            this.pan_read.Location = new System.Drawing.Point(3, 120);
+            this.pan_read.Name = "pan_read";
+            this.pan_read.Size = new System.Drawing.Size(846, 35);
+            this.pan_read.TabIndex = 39;
+            // 
+            // btn_Read
+            // 
+            this.btn_Read.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.btn_Read.Location = new System.Drawing.Point(226, 0);
+            this.btn_Read.Name = "btn_Read";
+            this.btn_Read.Size = new System.Drawing.Size(101, 30);
+            this.btn_Read.TabIndex = 43;
+            this.btn_Read.Text = "Read";
+            this.btn_Read.UseVisualStyleBackColor = true;
+            this.btn_Read.Click += new System.EventHandler(this.btn_Read_Click);
+            // 
+            // lab_Indicate
+            // 
+            this.lab_Indicate.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+            this.lab_Indicate.Location = new System.Drawing.Point(12, 0);
+            this.lab_Indicate.Name = "lab_Indicate";
+            this.lab_Indicate.Size = new System.Drawing.Size(101, 29);
+            this.lab_Indicate.TabIndex = 40;
+            this.lab_Indicate.Text = "Indicate";
+            this.lab_Indicate.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+            // 
+            // lab_Notify
+            // 
+            this.lab_Notify.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+            this.lab_Notify.Location = new System.Drawing.Point(119, 0);
+            this.lab_Notify.Name = "lab_Notify";
+            this.lab_Notify.Size = new System.Drawing.Size(101, 29);
+            this.lab_Notify.TabIndex = 41;
+            this.lab_Notify.Text = "Notify";
+            this.lab_Notify.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+            // 
+            // panel18
+            // 
+            this.panel18.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel18.Location = new System.Drawing.Point(835, 0);
+            this.panel18.Name = "panel18";
+            this.panel18.Size = new System.Drawing.Size(11, 35);
+            this.panel18.TabIndex = 39;
+            // 
+            // panel16
+            // 
+            this.panel16.Dock = System.Windows.Forms.DockStyle.Top;
+            this.panel16.Location = new System.Drawing.Point(3, 110);
+            this.panel16.Name = "panel16";
+            this.panel16.Size = new System.Drawing.Size(846, 10);
+            this.panel16.TabIndex = 37;
+            // 
+            // pan_hex
+            // 
+            this.pan_hex.Controls.Add(this.txt_HexResult);
+            this.pan_hex.Controls.Add(this.rad_hex);
+            this.pan_hex.Dock = System.Windows.Forms.DockStyle.Top;
+            this.pan_hex.Location = new System.Drawing.Point(3, 81);
+            this.pan_hex.Name = "pan_hex";
+            this.pan_hex.Size = new System.Drawing.Size(846, 29);
+            this.pan_hex.TabIndex = 34;
+            // 
+            // txt_HexResult
+            // 
+            this.txt_HexResult.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.txt_HexResult.Font = new System.Drawing.Font("SimSun", 12F);
+            this.txt_HexResult.Location = new System.Drawing.Point(65, 0);
+            this.txt_HexResult.Name = "txt_HexResult";
+            this.txt_HexResult.Size = new System.Drawing.Size(781, 26);
+            this.txt_HexResult.TabIndex = 14;
+            // 
+            // rad_hex
+            // 
+            this.rad_hex.AutoSize = true;
+            this.rad_hex.Dock = System.Windows.Forms.DockStyle.Left;
+            this.rad_hex.Location = new System.Drawing.Point(0, 0);
+            this.rad_hex.Name = "rad_hex";
+            this.rad_hex.Size = new System.Drawing.Size(65, 29);
+            this.rad_hex.TabIndex = 20;
+            this.rad_hex.TabStop = true;
+            this.rad_hex.Text = "Hex    ";
+            this.rad_hex.UseVisualStyleBackColor = true;
+            // 
+            // panel15
+            // 
+            this.panel15.Dock = System.Windows.Forms.DockStyle.Top;
+            this.panel15.Location = new System.Drawing.Point(3, 76);
+            this.panel15.Name = "panel15";
+            this.panel15.Size = new System.Drawing.Size(846, 5);
+            this.panel15.TabIndex = 35;
+            // 
+            // pan_utf8
+            // 
+            this.pan_utf8.Controls.Add(this.txt_UTF8Result);
+            this.pan_utf8.Controls.Add(this.rad_utf8);
+            this.pan_utf8.Dock = System.Windows.Forms.DockStyle.Top;
+            this.pan_utf8.Location = new System.Drawing.Point(3, 48);
+            this.pan_utf8.Name = "pan_utf8";
+            this.pan_utf8.Size = new System.Drawing.Size(846, 28);
+            this.pan_utf8.TabIndex = 33;
+            // 
+            // txt_UTF8Result
+            // 
+            this.txt_UTF8Result.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.txt_UTF8Result.Font = new System.Drawing.Font("SimSun", 12F);
+            this.txt_UTF8Result.Location = new System.Drawing.Point(65, 0);
+            this.txt_UTF8Result.Name = "txt_UTF8Result";
+            this.txt_UTF8Result.Size = new System.Drawing.Size(781, 26);
+            this.txt_UTF8Result.TabIndex = 31;
+            // 
+            // rad_utf8
+            // 
+            this.rad_utf8.AutoSize = true;
+            this.rad_utf8.Dock = System.Windows.Forms.DockStyle.Left;
+            this.rad_utf8.Location = new System.Drawing.Point(0, 0);
+            this.rad_utf8.Name = "rad_utf8";
+            this.rad_utf8.Size = new System.Drawing.Size(65, 28);
+            this.rad_utf8.TabIndex = 21;
+            this.rad_utf8.TabStop = true;
+            this.rad_utf8.Text = "UTF8   ";
+            this.rad_utf8.UseVisualStyleBackColor = true;
+            // 
+            // panel17
+            // 
+            this.panel17.Dock = System.Windows.Forms.DockStyle.Top;
+            this.panel17.Location = new System.Drawing.Point(3, 43);
+            this.panel17.Name = "panel17";
+            this.panel17.Size = new System.Drawing.Size(846, 5);
+            this.panel17.TabIndex = 36;
+            // 
+            // pan_dec
+            // 
+            this.pan_dec.Controls.Add(this.txt_DecResult);
+            this.pan_dec.Controls.Add(this.rad_dec);
+            this.pan_dec.Dock = System.Windows.Forms.DockStyle.Top;
+            this.pan_dec.Location = new System.Drawing.Point(3, 17);
+            this.pan_dec.Name = "pan_dec";
+            this.pan_dec.Size = new System.Drawing.Size(846, 26);
+            this.pan_dec.TabIndex = 32;
+            // 
+            // txt_DecResult
+            // 
+            this.txt_DecResult.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.txt_DecResult.Font = new System.Drawing.Font("SimSun", 12F);
+            this.txt_DecResult.Location = new System.Drawing.Point(65, 0);
+            this.txt_DecResult.Name = "txt_DecResult";
+            this.txt_DecResult.Size = new System.Drawing.Size(781, 26);
+            this.txt_DecResult.TabIndex = 30;
+            // 
+            // rad_dec
+            // 
+            this.rad_dec.AutoSize = true;
+            this.rad_dec.Checked = true;
+            this.rad_dec.Dock = System.Windows.Forms.DockStyle.Left;
+            this.rad_dec.Location = new System.Drawing.Point(0, 0);
+            this.rad_dec.Name = "rad_dec";
+            this.rad_dec.Size = new System.Drawing.Size(65, 26);
+            this.rad_dec.TabIndex = 19;
+            this.rad_dec.TabStop = true;
+            this.rad_dec.Text = "Integer";
+            this.rad_dec.UseVisualStyleBackColor = true;
+            // 
+            // groupBox7
+            // 
+            this.groupBox7.Controls.Add(this.pan_write);
+            this.groupBox7.Dock = System.Windows.Forms.DockStyle.Top;
+            this.groupBox7.Location = new System.Drawing.Point(3, 223);
+            this.groupBox7.Name = "groupBox7";
+            this.groupBox7.Size = new System.Drawing.Size(852, 87);
+            this.groupBox7.TabIndex = 32;
+            this.groupBox7.TabStop = false;
+            this.groupBox7.Text = "写入";
+            // 
+            // pan_write
+            // 
+            this.pan_write.Controls.Add(this.txt_write);
+            this.pan_write.Controls.Add(this.panel9);
+            this.pan_write.Controls.Add(this.rad_writeUTF8);
+            this.pan_write.Controls.Add(this.rad_writeHex);
+            this.pan_write.Controls.Add(this.rad_writeDec);
+            this.pan_write.Controls.Add(this.btn_Write);
+            this.pan_write.Controls.Add(this.panel10);
+            this.pan_write.Dock = System.Windows.Forms.DockStyle.Top;
+            this.pan_write.Location = new System.Drawing.Point(3, 17);
+            this.pan_write.Name = "pan_write";
+            this.pan_write.Size = new System.Drawing.Size(846, 30);
+            this.pan_write.TabIndex = 0;
+            // 
+            // txt_write
+            // 
+            this.txt_write.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.txt_write.Font = new System.Drawing.Font("SimSun", 15F);
+            this.txt_write.FormattingEnabled = true;
+            this.txt_write.Location = new System.Drawing.Point(0, 0);
+            this.txt_write.Name = "txt_write";
+            this.txt_write.Size = new System.Drawing.Size(578, 28);
+            this.txt_write.TabIndex = 32;
+            // 
+            // panel9
+            // 
+            this.panel9.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel9.Location = new System.Drawing.Point(578, 0);
+            this.panel9.Name = "panel9";
+            this.panel9.Size = new System.Drawing.Size(10, 30);
+            this.panel9.TabIndex = 27;
+            // 
+            // rad_writeUTF8
+            // 
+            this.rad_writeUTF8.AutoSize = true;
+            this.rad_writeUTF8.Checked = true;
+            this.rad_writeUTF8.Dock = System.Windows.Forms.DockStyle.Right;
+            this.rad_writeUTF8.Location = new System.Drawing.Point(588, 0);
+            this.rad_writeUTF8.Name = "rad_writeUTF8";
+            this.rad_writeUTF8.Size = new System.Drawing.Size(47, 30);
+            this.rad_writeUTF8.TabIndex = 31;
+            this.rad_writeUTF8.TabStop = true;
+            this.rad_writeUTF8.Text = "UTF8";
+            this.rad_writeUTF8.UseVisualStyleBackColor = true;
+            // 
+            // rad_writeHex
+            // 
+            this.rad_writeHex.AutoSize = true;
+            this.rad_writeHex.Dock = System.Windows.Forms.DockStyle.Right;
+            this.rad_writeHex.Location = new System.Drawing.Point(635, 0);
+            this.rad_writeHex.Name = "rad_writeHex";
+            this.rad_writeHex.Size = new System.Drawing.Size(47, 30);
+            this.rad_writeHex.TabIndex = 30;
+            this.rad_writeHex.Text = "Hex ";
+            this.rad_writeHex.UseVisualStyleBackColor = true;
+            // 
+            // rad_writeDec
+            // 
+            this.rad_writeDec.AutoSize = true;
+            this.rad_writeDec.Dock = System.Windows.Forms.DockStyle.Right;
+            this.rad_writeDec.Location = new System.Drawing.Point(682, 0);
+            this.rad_writeDec.Name = "rad_writeDec";
+            this.rad_writeDec.Size = new System.Drawing.Size(53, 30);
+            this.rad_writeDec.TabIndex = 29;
+            this.rad_writeDec.Text = "Int32";
+            this.rad_writeDec.UseVisualStyleBackColor = true;
+            // 
+            // btn_Write
+            // 
+            this.btn_Write.Dock = System.Windows.Forms.DockStyle.Right;
+            this.btn_Write.Location = new System.Drawing.Point(735, 0);
+            this.btn_Write.Name = "btn_Write";
+            this.btn_Write.Size = new System.Drawing.Size(101, 30);
+            this.btn_Write.TabIndex = 22;
+            this.btn_Write.Text = "写入";
+            this.btn_Write.UseVisualStyleBackColor = true;
+            this.btn_Write.Click += new System.EventHandler(this.btn_Write_Click);
+            // 
+            // panel10
+            // 
+            this.panel10.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel10.Location = new System.Drawing.Point(836, 0);
+            this.panel10.Name = "panel10";
+            this.panel10.Size = new System.Drawing.Size(10, 30);
+            this.panel10.TabIndex = 26;
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.groupBox7);
+            this.groupBox1.Controls.Add(this.groupBox6);
+            this.groupBox1.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.groupBox1.Location = new System.Drawing.Point(0, 237);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(858, 284);
+            this.groupBox1.TabIndex = 21;
+            this.groupBox1.TabStop = false;
+            // 
+            // groupBox8
+            // 
+            this.groupBox8.Controls.Add(this.pan_connection);
+            this.groupBox8.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.groupBox8.Location = new System.Drawing.Point(0, 139);
+            this.groupBox8.Name = "groupBox8";
+            this.groupBox8.Size = new System.Drawing.Size(858, 50);
+            this.groupBox8.TabIndex = 26;
+            this.groupBox8.TabStop = false;
+            // 
+            // pan_connection
+            // 
+            this.pan_connection.Controls.Add(this.txt_deviceID);
+            this.pan_connection.Controls.Add(this.label3);
+            this.pan_connection.Controls.Add(this.txt_rssi);
+            this.pan_connection.Controls.Add(this.label2);
+            this.pan_connection.Controls.Add(this.txt_devicename);
+            this.pan_connection.Controls.Add(this.panel13);
+            this.pan_connection.Controls.Add(this.label1);
+            this.pan_connection.Controls.Add(this.lab_connection);
+            this.pan_connection.Controls.Add(this.panel12);
+            this.pan_connection.Controls.Add(this.btn_pair);
+            this.pan_connection.Controls.Add(this.panel2);
+            this.pan_connection.Dock = System.Windows.Forms.DockStyle.Top;
+            this.pan_connection.Location = new System.Drawing.Point(3, 17);
+            this.pan_connection.Name = "pan_connection";
+            this.pan_connection.Size = new System.Drawing.Size(852, 21);
+            this.pan_connection.TabIndex = 44;
+            // 
+            // txt_deviceID
+            // 
+            this.txt_deviceID.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.txt_deviceID.Font = new System.Drawing.Font("SimSun", 9F);
+            this.txt_deviceID.Location = new System.Drawing.Point(249, 0);
+            this.txt_deviceID.Name = "txt_deviceID";
+            this.txt_deviceID.Size = new System.Drawing.Size(275, 21);
+            this.txt_deviceID.TabIndex = 50;
+            // 
+            // label3
+            // 
+            this.label3.Dock = System.Windows.Forms.DockStyle.Right;
+            this.label3.Location = new System.Drawing.Point(524, 0);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(64, 21);
+            this.label3.TabIndex = 52;
+            this.label3.Text = " 信号强度";
+            this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // txt_rssi
+            // 
+            this.txt_rssi.Dock = System.Windows.Forms.DockStyle.Right;
+            this.txt_rssi.Font = new System.Drawing.Font("SimSun", 9F);
+            this.txt_rssi.Location = new System.Drawing.Point(588, 0);
+            this.txt_rssi.Name = "txt_rssi";
+            this.txt_rssi.Size = new System.Drawing.Size(29, 21);
+            this.txt_rssi.TabIndex = 53;
+            // 
+            // label2
+            // 
+            this.label2.Dock = System.Windows.Forms.DockStyle.Left;
+            this.label2.Location = new System.Drawing.Point(202, 0);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(47, 21);
+            this.label2.TabIndex = 51;
+            this.label2.Text = "设备ID";
+            this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // txt_devicename
+            // 
+            this.txt_devicename.Dock = System.Windows.Forms.DockStyle.Left;
+            this.txt_devicename.Font = new System.Drawing.Font("SimSun", 9F);
+            this.txt_devicename.Location = new System.Drawing.Point(49, 0);
+            this.txt_devicename.Name = "txt_devicename";
+            this.txt_devicename.Size = new System.Drawing.Size(153, 21);
+            this.txt_devicename.TabIndex = 47;
+            // 
+            // panel13
+            // 
+            this.panel13.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel13.Location = new System.Drawing.Point(617, 0);
+            this.panel13.Name = "panel13";
+            this.panel13.Size = new System.Drawing.Size(21, 21);
+            this.panel13.TabIndex = 48;
+            // 
+            // label1
+            // 
+            this.label1.Dock = System.Windows.Forms.DockStyle.Left;
+            this.label1.Location = new System.Drawing.Point(0, 0);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(49, 21);
+            this.label1.TabIndex = 46;
+            this.label1.Text = "设备名";
+            this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+            // 
+            // lab_connection
+            // 
+            this.lab_connection.BackColor = System.Drawing.SystemColors.Control;
+            this.lab_connection.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+            this.lab_connection.Dock = System.Windows.Forms.DockStyle.Right;
+            this.lab_connection.ForeColor = System.Drawing.Color.White;
+            this.lab_connection.Location = new System.Drawing.Point(638, 0);
+            this.lab_connection.Name = "lab_connection";
+            this.lab_connection.Size = new System.Drawing.Size(92, 21);
+            this.lab_connection.TabIndex = 44;
+            this.lab_connection.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+            // 
+            // panel12
+            // 
+            this.panel12.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel12.Location = new System.Drawing.Point(730, 0);
+            this.panel12.Name = "panel12";
+            this.panel12.Size = new System.Drawing.Size(21, 21);
+            this.panel12.TabIndex = 45;
+            // 
+            // btn_pair
+            // 
+            this.btn_pair.BackColor = System.Drawing.SystemColors.ControlLight;
+            this.btn_pair.Dock = System.Windows.Forms.DockStyle.Right;
+            this.btn_pair.Location = new System.Drawing.Point(751, 0);
+            this.btn_pair.Name = "btn_pair";
+            this.btn_pair.Size = new System.Drawing.Size(91, 21);
+            this.btn_pair.TabIndex = 54;
+            this.btn_pair.Text = "配对";
+            this.btn_pair.UseVisualStyleBackColor = false;
+            this.btn_pair.Click += new System.EventHandler(this.btn_pair_Click);
+            // 
+            // panel2
+            // 
+            this.panel2.Dock = System.Windows.Forms.DockStyle.Right;
+            this.panel2.Location = new System.Drawing.Point(842, 0);
+            this.panel2.Name = "panel2";
+            this.panel2.Size = new System.Drawing.Size(10, 21);
+            this.panel2.TabIndex = 55;
+            // 
+            // MainForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-            this.ClientSize = new System.Drawing.Size(800, 450);
-            this.Text = "Form1";
+            this.ClientSize = new System.Drawing.Size(858, 749);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.groupBox8);
+            this.Controls.Add(this.groupBox4);
+            this.Controls.Add(this.groupBox1);
+            this.Controls.Add(this.groupBox5);
+            this.Controls.Add(this.groupBox3);
+            this.Controls.Add(this.ss_bottom);
+            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+            this.Name = "MainForm";
+            this.Text = "BLETool";
+            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
+            this.Load += new System.EventHandler(this.Form1_Load);
+            this.Shown += new System.EventHandler(this.Form1_Shown);
+            this.groupBox2.ResumeLayout(false);
+            this.panel1.ResumeLayout(false);
+            this.pan_search.ResumeLayout(false);
+            this.pan_search.PerformLayout();
+            this.groupBox3.ResumeLayout(false);
+            this.groupBox3.PerformLayout();
+            this.groupBox4.ResumeLayout(false);
+            this.groupBox4.PerformLayout();
+            this.groupBox5.ResumeLayout(false);
+            this.groupBox6.ResumeLayout(false);
+            this.groupBox9.ResumeLayout(false);
+            this.groupBox9.PerformLayout();
+            this.pan_read.ResumeLayout(false);
+            this.pan_hex.ResumeLayout(false);
+            this.pan_hex.PerformLayout();
+            this.pan_utf8.ResumeLayout(false);
+            this.pan_utf8.PerformLayout();
+            this.pan_dec.ResumeLayout(false);
+            this.pan_dec.PerformLayout();
+            this.groupBox7.ResumeLayout(false);
+            this.pan_write.ResumeLayout(false);
+            this.pan_write.PerformLayout();
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox8.ResumeLayout(false);
+            this.pan_connection.ResumeLayout(false);
+            this.pan_connection.PerformLayout();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
         }
 
         #endregion
+        private System.Windows.Forms.StatusStrip ss_bottom;
+        private System.Windows.Forms.Button btn_Stop;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.ListView lv_device;
+        private System.Windows.Forms.GroupBox groupBox3;
+        private System.Windows.Forms.TextBox textBox3;
+        private System.Windows.Forms.TextBox textBox2;
+        private System.Windows.Forms.TextBox textBox1;
+        private System.Windows.Forms.Button button3;
+        private System.Windows.Forms.Button button2;
+        private System.Windows.Forms.Button button1;
+        private System.Windows.Forms.GroupBox groupBox4;
+        private System.Windows.Forms.ComboBox cmb_service;
+        private System.Windows.Forms.Label lab_servicecount;
+        private System.Windows.Forms.ComboBox cmb_characteristic;
+        private System.Windows.Forms.Label lab_charcount;
+        private System.Windows.Forms.ColumnHeader DevName;
+        private System.Windows.Forms.ColumnHeader ID;
+        private System.Windows.Forms.ColumnHeader CanPaired;
+        private System.Windows.Forms.ColumnHeader IsPaired;
+        private System.Windows.Forms.Panel panel1;
+        private System.Windows.Forms.Button btn_Search;
+        private System.Windows.Forms.Panel pan_search;
+        private System.Windows.Forms.Panel panel6;
+        private System.Windows.Forms.Panel panel4;
+        private System.Windows.Forms.Panel panel7;
+        private System.Windows.Forms.ColumnHeader RSSI;
+        private System.Windows.Forms.Button btn_Refresh;
+        private System.Windows.Forms.Panel panel5;
+        private System.Windows.Forms.CheckBox chb_ShowHidden;
+        private System.Windows.Forms.Panel panel8;
+        private System.Windows.Forms.CheckBox chb_ShowUnconnectableDevice;
+        private System.Windows.Forms.GroupBox groupBox5;
+        private System.Windows.Forms.RichTextBox rtb_debug;
+        private System.Windows.Forms.Button btn_refreshCharacteristic;
+        private System.Windows.Forms.Button btn_getData;
+        private System.Windows.Forms.GroupBox groupBox6;
+        private System.Windows.Forms.Panel pan_read;
+        private System.Windows.Forms.Label lab_Indicate;
+        private System.Windows.Forms.Label lab_Notify;
+        private System.Windows.Forms.Panel panel18;
+        private System.Windows.Forms.Panel panel16;
+        private System.Windows.Forms.Panel pan_hex;
+        private System.Windows.Forms.TextBox txt_HexResult;
+        private System.Windows.Forms.RadioButton rad_hex;
+        private System.Windows.Forms.Panel panel15;
+        private System.Windows.Forms.Panel pan_utf8;
+        private System.Windows.Forms.TextBox txt_UTF8Result;
+        private System.Windows.Forms.RadioButton rad_utf8;
+        private System.Windows.Forms.Panel panel17;
+        private System.Windows.Forms.Panel pan_dec;
+        private System.Windows.Forms.TextBox txt_DecResult;
+        private System.Windows.Forms.RadioButton rad_dec;
+        private System.Windows.Forms.GroupBox groupBox7;
+        private System.Windows.Forms.Panel pan_write;
+        private System.Windows.Forms.Panel panel9;
+        private System.Windows.Forms.Button btn_Write;
+        private System.Windows.Forms.Panel panel10;
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.RadioButton rad_writeDec;
+        private System.Windows.Forms.RadioButton rad_writeUTF8;
+        private System.Windows.Forms.RadioButton rad_writeHex;
+        private System.Windows.Forms.ComboBox txt_write;
+        private System.Windows.Forms.Button btn_Read;
+        private System.Windows.Forms.GroupBox groupBox8;
+        private System.Windows.Forms.Panel pan_connection;
+        private System.Windows.Forms.TextBox txt_devicename;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.Label lab_connection;
+        private System.Windows.Forms.Panel panel13;
+        private System.Windows.Forms.Panel panel12;
+        private System.Windows.Forms.TextBox txt_deviceID;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.TextBox txt_rssi;
+        private System.Windows.Forms.Button btn_pair;
+        private System.Windows.Forms.Panel panel2;
+        private System.Windows.Forms.CheckBox cb_display_callbackData;
+        private System.Windows.Forms.TextBox txt_callback_characteristic;
+        private System.Windows.Forms.TextBox txt_callback_service;
+        private System.Windows.Forms.Label label4;
+        private System.Windows.Forms.Label label5;
+        private System.Windows.Forms.GroupBox groupBox9;
     }
 }
 

+ 964 - 2
BLETool/MainForm.cs

@@ -1,20 +1,982 @@
 using System;
 using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Forms;
+using System.Diagnostics;
+using Windows.Devices.Bluetooth;
+using Windows.Devices.Enumeration;
+using Windows.Devices.Bluetooth.Advertisement;
+using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Security.Cryptography;
+using Windows.Storage.Streams;
+using MSerialization;
+using BLEComm;
+using System.Runtime.InteropServices.WindowsRuntime;
 
 namespace BLETool
 {
+
     public partial class MainForm : Form
     {
+        readonly uint E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED = 0x80650003;
+        readonly uint E_BLUETOOTH_ATT_INVALID_PDU = 0x80650004;
+        readonly uint E_ACCESSDENIED = 0x80070005;
+        readonly uint E_DEVICE_NOT_AVAILABLE = 0x800710df; // HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_AVAILABLE)
+
+        //找到的设备
+        private Dictionary<string, DeviceInformation> devices = new Dictionary<string, DeviceInformation>();
+        private DeviceWatcher deviceWatcher;
+        ListViewColumnSorter lvwColumnSorter;
+        //当前选中的BLE设备
+        BluetoothLEDevice ble_Device;
+        //上一个BLE设备
+        BluetoothLEDevice last_ble_Device;
+        //当前设备提供的服务组
+        IReadOnlyList<GattDeviceService> ble_Services = null;
+        //当前选中的BLE服务
+        GattDeviceService ble_Service = null;
+        //当前选中的BLE属性组
+        IReadOnlyList<GattCharacteristic> ble_Characteristics = null;
+        //当前选中的BLE属性
+        GattCharacteristic ble_Characteristic = null;
+        //上一个BLE属性
+        GattCharacteristic last_ble_Characteristic = null;
+        //订阅的Notify或IndicateBLE属性
+        GattCharacteristic callback_Characteristic = null;
+        //数据格式
+        GattPresentationFormat presentationFormat;
+        //数据结果
+        IBuffer ble_result;
         public MainForm()
         {
             InitializeComponent();
+            lvwColumnSorter = new ListViewColumnSorter();
+            this.lv_device.ListViewItemSorter = lvwColumnSorter;
+            this.ss_bottom.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.HorizontalStackWithOverflow;
+
+        }
+        //发现新设备
+        private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
+        {
+            try
+            {
+                this?.Invoke(new Action(() =>
+                {
+                    if (sender == deviceWatcher)
+                    {
+                        // Make sure device isn't already present in the list.
+                        if (!devices.ContainsKey(deviceInfo.Id))
+                        {
+                            if (deviceInfo.Name != "" || chb_ShowHidden.Checked == true)
+                            {
+                                string id = deviceInfo.Id;
+
+                                devices.Add(deviceInfo.Id, deviceInfo);
+                                //添加到列表
+                                ListViewItem lvi = new ListViewItem();
+                                lvi.Text = deviceInfo.Name;
+                                lvi.Name = id;
+                                lvi.SubItems.Add(id);
+                                lvi.SubItems.Add(deviceInfo.Pairing.CanPair.ToString());
+                                lvi.SubItems.Add(deviceInfo.Pairing.IsPaired.ToString());
+                                lvi.SubItems.Add("");
+                                lv_device.Items.Add(lvi);
+                            }
+                        }
+                    }
+                }));
+            }
+            catch { }
+        }
+
+        //设备信息被更新
+        private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
+        {
+            try
+            {
+                this?.Invoke(new Action(() =>
+                {
+
+                    // Protect against race condition if the task runs after the app stopped the deviceWatcher.
+                    if (sender == deviceWatcher)
+                    {
+                        //存在该设备
+                        if (devices.Keys.Contains(deviceInfoUpdate.Id) && devices[deviceInfoUpdate.Id] != null)
+                        {
+                            string id = deviceInfoUpdate.Id;
+                            devices[deviceInfoUpdate.Id].Update(deviceInfoUpdate);
+                            lv_device.Items[id].SubItems[0].Text = devices[deviceInfoUpdate.Id].Name;
+                            lv_device.Items[id].SubItems[2].Text = devices[deviceInfoUpdate.Id].Pairing.CanPair.ToString();
+                            lv_device.Items[id].SubItems[3].Text = devices[deviceInfoUpdate.Id].Pairing.IsPaired.ToString();
+                        }
+                    }
+
+                }));
+            }
+            catch { }
+        }
+        //从设备列表移除设备
+        private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
+        {
+            this?.Invoke(new Action(() =>
+            {
+                if (sender == deviceWatcher)
+                {
+                    string id = deviceInfoUpdate.Id;
+                    if (chb_ShowUnconnectableDevice.Checked != true)
+                    {
+                        devices.Remove(deviceInfoUpdate.Id);
+                        lv_device.Items.RemoveByKey(id);
+                    }
+                }
+            }));
+        }
+
+        //设备枚举完成
+        private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object e)
+        {
+            this?.Invoke(new Action(() =>
+            {
+                log("设备搜索完毕");
+            }));
+        }
+
+        //终止设备搜索
+        private void DeviceWatcher_Stopped(DeviceWatcher sender, object e)
+        {
+            this?.Invoke(new Action(() =>
+            {
+                log("搜索被终止");
+            }));
+        }
+        private void Form1_Load(object sender, EventArgs e)
+        {
+
+        }
+        private void StartBleDeviceWatcher()
+        {
+            //BLE device watch
+            string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected", "System.Devices.Aep.Bluetooth.Le.IsConnectable" };
+            string aqsAllBluetoothLEDevices = "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";
+
+            deviceWatcher =
+                    DeviceInformation.CreateWatcher(
+                        aqsAllBluetoothLEDevices,
+                        requestedProperties,
+                        DeviceInformationKind.AssociationEndpoint);
+
+            // Register event handlers before starting the watcher.
+            deviceWatcher.Added += DeviceWatcher_Added;
+            deviceWatcher.Updated += DeviceWatcher_Updated;
+            deviceWatcher.Removed += DeviceWatcher_Removed;
+            deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
+            deviceWatcher.Stopped += DeviceWatcher_Stopped;
+            deviceWatcher.Start();
+
+        }
+        private void StartBleAdvertisementWatcher()
+        {
+            //BLE advertisement watcher
+            var watcher = new BluetoothLEAdvertisementWatcher();
+            watcher.Received += Watcher_Received;
+            watcher.Start();
+        }
+        private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
+        {
+            string bleaddr = args.BluetoothAddress.ToString("x");
+            bleaddr = string.Join(":", System.Text.RegularExpressions.Regex.Split(bleaddr, "(?<=\\G.{2})(?!$)"));
+            this?.Invoke(new Action(() =>
+            {
+                foreach (ListViewItem lvi in lv_device.Items)
+                {
+                    if (lvi.Name.Contains(bleaddr))
+                    {
+                        Console.WriteLine(args.RawSignalStrengthInDBm.ToString());
+                        lvi.SubItems[4].Text = args.RawSignalStrengthInDBm.ToString();
+                    }
+                }
+                if (txt_deviceID.Text.Contains(bleaddr))
+                {
+                    txt_rssi.Text = args.RawSignalStrengthInDBm.ToString();
+                }
+            }));
+        }
+        private void StopBleDeviceWatcher()
+        {
+            if (deviceWatcher != null)
+            {
+                deviceWatcher.Stop();
+                // Unregister the event handlers.
+                deviceWatcher.Added -= DeviceWatcher_Added;
+                deviceWatcher.Updated -= DeviceWatcher_Updated;
+                deviceWatcher.Removed -= DeviceWatcher_Removed;
+                deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
+                deviceWatcher.Stopped -= DeviceWatcher_Stopped;
+                deviceWatcher = null;
+            }
+        }
+        private void btn_Search_Click(object sender, EventArgs e)
+        {
+            //清除设备列表
+            devices.Clear();
+            lv_device.Items.Clear();
+            log("正在搜索设备...");
+            //启动设备搜索
+            StartBleDeviceWatcher();
+        }
+
+        private void lv_device_ColumnClick(object sender, ColumnClickEventArgs e)
+        {
+            // 检查点击的列是不是现在的排序列.
+            if (e.Column == lvwColumnSorter.SortColumn)
+            {
+                // 重新设置此列的排序方法.
+                if (lvwColumnSorter.Order == SortOrder.Ascending)
+                {
+                    lvwColumnSorter.Order = SortOrder.Descending;
+                }
+                else
+                {
+                    lvwColumnSorter.Order = SortOrder.Ascending;
+                }
+            }
+            else
+            {
+                // 设置排序列,默认为正向排序
+                lvwColumnSorter.SortColumn = e.Column;
+                lvwColumnSorter.Order = SortOrder.Ascending;
+            }
+            // 用新的排序方法对ListView排序
+            this.lv_device.Sort();
+        }
+
+        private void lv_device_SelectedIndexChanged(object sender, EventArgs e)
+        {
+
+        }
+
+        private void btn_Stop_Click(object sender, EventArgs e)
+        {
+            StopBleDeviceWatcher();
+        }
+
+
+        private void btn_Refresh_Click(object sender, EventArgs e)
+        {
+            StartBleAdvertisementWatcher();
+            log("正在获取信号强度(RSSI)");
+        }
+
+        private void btn_pair_Click(object sender, EventArgs e)
+        {
+            //解除上一个设备的回调
+            UnhookNotify();
+            if (last_ble_Device != null)
+            {
+                last_ble_Device.ConnectionStatusChanged -= CurrentDevice_ConnectionStatusChanged;
+            }
+            GetDeviceServices();
+        }
+
+        private void chb_ShowUnconnectableDevice_CheckedChanged(object sender, EventArgs e)
+        {
+
+        }
+        private void log(string text)
+        {
+
+        }
+
+        private void cmb_service_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            cmb_characteristic.Items.Clear();
+            cmb_characteristic.Text = "";
+            lab_charcount.Text = "特征[0]";
+            //getCharacteristic();
+        }
+        private async void GetDeviceServices()
+        {
+            log("获取服务");
+            lab_servicecount.Text = "服务[0]";
+            cmb_service.Text = "";
+            lab_charcount.Text = "特征[0]";
+            cmb_characteristic.Items.Clear();
+            cmb_characteristic.Text = "";
+            txt_rssi.Text = "";
+            lab_connection.BackColor = Color.White;
+            BluetoothLEDevice bledevice = null;
+            try
+            {
+                if (lv_device.Items.Count <= 0 || lv_device.SelectedItems.Count <= 0) return;
+                lab_connection.Text = "Connecting...";
+                lab_connection.ForeColor = Color.Black;
+                string id = lv_device.SelectedItems[0].Name;
+                string name = lv_device.SelectedItems[0].Text;
+                txt_devicename.Text = name;
+                txt_deviceID.Text = id;
+
+                if (id != null)
+                {
+                    log("配对设备 " + id + "(" + name + ")");
+                    bledevice = await BluetoothLEDevice.FromIdAsync(id);
+                    DevicePairingResult result = await bledevice.DeviceInformation.Pairing.PairAsync();
+                    //log(result.Status.ToString());
+                    if (bledevice == null)
+                    {
+                        lab_connection.BackColor = Color.Red;
+                        lab_connection.ForeColor = Color.White;
+                        lab_connection.Text = "Disconnected...";
+                        log("设备连接失败");
+                    }
+
+                }
+            }
+            catch (Exception ex) when (ex.HResult == E_DEVICE_NOT_AVAILABLE)
+            {
+                log("蓝牙设备不可用");
+            }
+            if (bledevice != null && bledevice != last_ble_Device)
+            {
+                last_ble_Device = ble_Device;
+                ble_Device = bledevice;
+                ble_Device.ConnectionStatusChanged -= CurrentDevice_ConnectionStatusChanged;
+                ble_Device.ConnectionStatusChanged += CurrentDevice_ConnectionStatusChanged;
+                getService();
+            }
+        }
+        private async void getService()
+        {
+            GattDeviceServicesResult result;
+            try
+            {
+                result = await ble_Device.GetGattServicesAsync(BluetoothCacheMode.Uncached);
+            }
+            catch
+            {
+                return;
+            }
+            if (result.Status == GattCommunicationStatus.Success)
+            {
+                lab_connection.BackColor = Color.DarkGreen;
+                lab_connection.Text = "Connected";
+                lab_connection.ForeColor = Color.White;
+                var services = result.Services;
+                log(String.Format("找到 {0} 个服务", services.Count));
+                lab_servicecount.Text = "服务[" + services.Count + "]";
+                cmb_service.Items.Clear();
+                ble_Services = services;
+                foreach (var service in services)
+                {
+                    string servicename = BLEInfo.GetServiceName(service);
+                    cmb_service.Items.Add(servicename);
+                }
+                if (services.Count > 0)
+                {
+                    cmb_service.Text = cmb_service.Items[0].ToString();
+                    ble_Service = services[0];
+                }
+
+            }
+            else
+            {
+                log("设备不可访问");
+
+            }
+        }
+        private async void getCharacteristic()
+        {
+            log("获取当前服务特征");
+
+            IReadOnlyList<GattCharacteristic> characteristics = null;
+            if (cmb_service.SelectedIndex >= 0)
+                try
+                {
+                    ble_Service = ble_Services[cmb_service.SelectedIndex];
+                    // Ensure we have access to the device.
+                    var accessStatus = await ble_Service.RequestAccessAsync();
+                    if (accessStatus == DeviceAccessStatus.Allowed)
+                    {
+                        // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only 
+                        // and the new Async functions to get the characteristics of unpaired devices as well. 
+                        var result = await ble_Service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);
+                        if (result.Status == GattCommunicationStatus.Success)
+                        {
+                            characteristics = result.Characteristics;
+                            log(String.Format("找到 {0} 个服务特征", characteristics.Count));
+                            lab_charcount.Text = "特征[" + characteristics.Count + "]";
+                            ble_Characteristics = characteristics;
+                            foreach (var characteristic in characteristics)
+                            {
+                                string characteristicname = BLEInfo.GetCharacteristicName(characteristic);
+                                cmb_characteristic.Items.Add(characteristicname);
+                            }
+                            if (ble_Characteristics.Count > 0)
+                            {
+                                cmb_characteristic.Text = cmb_characteristic.Items[0].ToString();
+                                ble_Characteristic = ble_Characteristics[0];
+                            }
+                        }
+                        else
+                        {
+                            //log(result.Status.ToString());
+
+                            // On error, act as if there are no characteristics.
+                            characteristics = new List<GattCharacteristic>();
+                            lab_charcount.Text = "特征[0]";
+                        }
+                    }
+                    else
+                    {
+                        // Not granted access
+                        log("该服务访问失败");
+
+                        // On error, act as if there are no characteristics.
+                        ble_Characteristics = new List<GattCharacteristic>();
+                        lab_charcount.Text = "特征[0]";
+
+                    }
+                }
+                catch (Exception ex)
+                {
+                    log("该服务特征禁止操作: " + ex.Message);
+                    // On error, act as if there are no characteristics.
+                    characteristics = new List<GattCharacteristic>();
+                    lab_charcount.Text = "特征[0]";
+                }
+
+        }
+
+        //控件颜色闪烁
+        private void BlinkControl(Control control)
+        {
+
+            control.BackColor = Color.Blue;
+            System.Timers.Timer timer = new System.Timers.Timer();
+            timer.Interval = 200;
+            timer.AutoReset = false;
+            timer.Elapsed += delegate
+            {
+                control.BackColor = SystemColors.Control;
+            };
+            timer.Start();
+
+
+        }
+
+        private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
+        {
+            if (callback_Characteristic == null) return;
+            // BT_Code: An Indicate or Notify reported that the value has changed.
+            // Display the new value with a timestamp.
+            GattCharacteristicProperties properties = callback_Characteristic.CharacteristicProperties;
+            if (properties.HasFlag(GattCharacteristicProperties.Indicate))
+            {
+                log("收到Indicate通知");
+                BlinkControl(this.lab_Indicate);
+            }
+            if (properties.HasFlag(GattCharacteristicProperties.Notify))
+            {
+                log("收到Notify通知");
+                BlinkControl(this.lab_Notify);
+            }
+            if (ble_Characteristic == null) return;
+            this.Invoke(new Action(() =>
+            {
+                txt_DecResult.Text = "";
+                txt_HexResult.Text = "";
+                txt_UTF8Result.Text = "";
+                var result = args.CharacteristicValue;
+                string type = "";
+                string formattedResult = BLEInfo.FormatValueByPresentation(result, presentationFormat, out type);
+                txt_UTF8Result.Text = formattedResult;
+                if (type == "HEX") { rad_hex.Checked = true; rad_dec.Checked = false; rad_utf8.Checked = false; }
+                if (type == "Decimal") { rad_hex.Checked = false; rad_dec.Checked = true; rad_utf8.Checked = false; }
+                if (type == "UTF8") { rad_hex.Checked = false; rad_dec.Checked = false; rad_utf8.Checked = true; }
+                try
+                {
+                    byte[] data;
+                    CryptographicBuffer.CopyToByteArray(result, out data);
+                    txt_UTF8Result.Text = Encoding.UTF8.GetString(data);
+                }
+                catch
+                {
+                    txt_UTF8Result.Text = "";
+                }
+                try
+                {
+                    byte[] data;
+                    CryptographicBuffer.CopyToByteArray(result, out data);
+                    txt_HexResult.Text = BitConverter.ToString(data, 0).Replace("-", " ").ToUpper();
+                }
+                catch
+                {
+                    txt_HexResult.Text = "";
+                }
+                try
+                {
+                    byte[] data;
+                    CryptographicBuffer.CopyToByteArray(result, out data);
+                    if (data.Length == 1) txt_DecResult.Text = data[0].ToString();
+                    if (data.Length == 2) txt_DecResult.Text = BitConverter.ToInt16(data, 0).ToString();
+                    if (data.Length == 4) txt_DecResult.Text = BitConverter.ToInt32(data, 0).ToString();
+                    if (data.Length == 8) txt_DecResult.Text = BitConverter.ToInt64(data, 0).ToString();
+
+                }
+                catch
+                {
+                    txt_DecResult.Text = "";
+                }
+            }));
+
+        }
+        private void btn_refreshCharacteristic_Click(object sender, EventArgs e)
+        {
+            cmb_characteristic.Items.Clear();
+            cmb_characteristic.Text = "";
+            lab_charcount.Text = "特征[0]";
+            getCharacteristic();
+        }
+
+
+        private void cmb_characteristic_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            //GetData();
+            if (cmb_characteristic.SelectedIndex >= 0)
+            {
+                last_ble_Characteristic = ble_Characteristic;
+                ble_Characteristic = ble_Characteristics[cmb_characteristic.SelectedIndex];
+            }
+        }
+        private async void UnhookNotify()
+        {
+            if (callback_Characteristic == null) return;
+            GattCharacteristicProperties callback_properties = callback_Characteristic.CharacteristicProperties;
+            //特征支持Nodify或者Indicate属性
+            if (callback_properties.HasFlag(GattCharacteristicProperties.Notify) || callback_properties.HasFlag(GattCharacteristicProperties.Indicate))
+            {
+                //解除上一个属性的Notify和Indicate
+                try
+                {
+                    // BT_Code: Must write the CCCD in order for server to send notifications.
+                    // We receive them in the ValueChanged event handler.
+                    // Note that this sample configures either Indicate or Notify, but not both.
+                    var result = await
+                            callback_Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
+                                GattClientCharacteristicConfigurationDescriptorValue.None);
+                    if (result == GattCommunicationStatus.Success)
+                    {
+                        callback_Characteristic.ValueChanged -= Characteristic_ValueChanged;
+                    }
+                    callback_Characteristic = null;
+                }
+                catch (Exception ex)
+                {
+                    // This usually happens when a device reports that it support notify, but it actually doesn't.
+                    log(ex.Message);
+                }
+            }
+        }
+        //为蓝牙的characteristic设置回调使能和回调函数
+        private async void HookNotify()
+        {
+            if (ble_Characteristic == null) return;
+            GattCharacteristicProperties properties = ble_Characteristic.CharacteristicProperties;
+            //特征支持Nodify或者Indicate属性
+            if (properties.HasFlag(GattCharacteristicProperties.Notify) || properties.HasFlag(GattCharacteristicProperties.Indicate))
+            {
+                callback_Characteristic = ble_Characteristic;
+                txt_callback_service.Text = cmb_service.Text;
+                txt_callback_characteristic.Text = cmb_characteristic.Text;
+                cb_display_callbackData.Checked = true;
+
+                // initialize status
+                GattCommunicationStatus status = GattCommunicationStatus.Unreachable;
+                var cccdValue = GattClientCharacteristicConfigurationDescriptorValue.None;
+                if (properties.HasFlag(GattCharacteristicProperties.Indicate))
+                {
+                    cccdValue = GattClientCharacteristicConfigurationDescriptorValue.Indicate;
+                }
+
+                else if (properties.HasFlag(GattCharacteristicProperties.Notify))
+                {
+                    cccdValue = GattClientCharacteristicConfigurationDescriptorValue.Notify;
+                }
+
+                try
+                {
+                    //设置回调使能
+                    // BT_Code: Must write the CCCD in order for server to send indications.
+                    status = await callback_Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(cccdValue);
+
+                    if (status == GattCommunicationStatus.Success)
+                    {
+                        //设置回调函数
+                        ble_Characteristic.ValueChanged += Characteristic_ValueChanged;
+                        log("成功订阅了服务特征变化的通知");
+                    }
+                    else
+                    {
+                        log($"未成功订阅服务特征变化的通知: {status}");
+                    }
+                }
+                catch (Exception ex)
+                {
+                    // This usually happens when a device reports that it support indicate, but it actually doesn't.
+                    log(ex.Message);
+                }
+            }
+        }
+        //读数characteristic的类型,如果支持read属性则读取值
+        private async void btn_getData_Click(object sender, EventArgs e)
+        {
+            log("正在读取特征的数据类别");
+            txt_DecResult.Text = "";
+            txt_HexResult.Text = "";
+            txt_UTF8Result.Text = "";
+            if (ble_Characteristic != null)
+            {
+
+                try
+                {
+                    // Get all the child descriptors of a characteristics. 
+                    var result = await ble_Characteristic.GetDescriptorsAsync(BluetoothCacheMode.Uncached);
+                    if (result.Status != GattCommunicationStatus.Success)
+                    {
+                        log("Descriptor read failure: " + result.Status.ToString());
+                    }
+                    // BT_Code: There's no need to access presentation format unless there's at least one. 
+                    presentationFormat = null;
+                    if (ble_Characteristic.PresentationFormats.Count > 0)
+                    {
+
+                        if (ble_Characteristic.PresentationFormats.Count.Equals(1))
+                        {
+                            // Get the presentation format since there's only one way of presenting it
+                            presentationFormat = ble_Characteristic.PresentationFormats[0];
+                        }
+                        else
+                        {
+                            // It's difficult to figure out how to split up a characteristic and encode its different parts properly.
+                            // In this case, we'll just encode the whole thing to a string to make it easy to print out.
+                        }
+                    }
+                    log("特征类别获取成功.");
+                    GattCharacteristicProperties properties = ble_Characteristic.CharacteristicProperties;
+                    btn_Read.Enabled = properties.HasFlag(GattCharacteristicProperties.Read);
+                    btn_Write.Enabled = properties.HasFlag(GattCharacteristicProperties.Write);
+                    lab_Indicate.Enabled = properties.HasFlag(GattCharacteristicProperties.Indicate);
+                    lab_Notify.Enabled = properties.HasFlag(GattCharacteristicProperties.Notify);
+                    log("该特征支持操作属性 {" + properties.ToString() + "}");
+                    //特征支持读属性
+                    if (btn_Read.Enabled == true)
+                    {
+                        txt_DecResult.Enabled = true;
+                        txt_HexResult.Enabled = true;
+                        txt_UTF8Result.Enabled = true;
+                        btn_Read_Click(null, null);
+                    }
+                    else
+                    {
+                        txt_DecResult.Text = "Read function is not supported for the characteristic";
+                        txt_HexResult.Text = "Read function is not supported for the characteristic";
+                        txt_UTF8Result.Text = "Read function is not supported for the characteristic";
+                        txt_DecResult.Enabled = false;
+                        txt_HexResult.Enabled = false;
+                        txt_UTF8Result.Enabled = false;
+                    }
+                    HookNotify();
+                }
+                catch (Exception ee)
+                {
+                    log("设备暂时不可达");
+                }
+
+            }
+        }
+        //读取charactistic的值
+        private async void btn_Read_Click(object sender, EventArgs e)
+        {
+            BlinkControl(this.btn_Read);
+            log("正在读取数据");
+            if (ble_Characteristic == null) return;
+            txt_DecResult.Text = "";
+            txt_HexResult.Text = "";
+            txt_UTF8Result.Text = "";
+            // BT_Code: Read the actual value from the device by using Uncached.
+            GattReadResult result = await ble_Characteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
+            if (result.Status == GattCommunicationStatus.Success)
+            {
+                ble_result = result.Value;
+                string type = "";
+                string formattedResult = BLEInfo.FormatValueByPresentation(result.Value, presentationFormat, out type);
+                txt_UTF8Result.Text = formattedResult;
+                if (type == "HEX") { rad_hex.Checked = true; rad_dec.Checked = false; rad_utf8.Checked = false; }
+                if (type == "Decimal") { rad_hex.Checked = false; rad_dec.Checked = true; rad_utf8.Checked = false; }
+                if (type == "UTF8") { rad_hex.Checked = false; rad_dec.Checked = false; rad_utf8.Checked = true; }
+                log("数据读取成功");
+                try
+                {
+                    byte[] data;
+                    CryptographicBuffer.CopyToByteArray(ble_result, out data);
+                    txt_UTF8Result.Text = Encoding.UTF8.GetString(data);
+                }
+                catch
+                {
+                    txt_UTF8Result.Text = "";
+                }
+                try
+                {
+                    byte[] data;
+                    CryptographicBuffer.CopyToByteArray(ble_result, out data);
+                    txt_HexResult.Text = BitConverter.ToString(data, 0).Replace("-", " ").ToUpper();
+                }
+                catch
+                {
+                    txt_HexResult.Text = "";
+                }
+                try
+                {
+                    byte[] data;
+                    CryptographicBuffer.CopyToByteArray(ble_result, out data);
+                    if (data == null)
+                    {
+                        log("不存在要被读取的数据");
+                        return;
+                    }
+                    if (data.Length == 1) txt_DecResult.Text = data[0].ToString();
+                    if (data.Length == 2) txt_DecResult.Text = BitConverter.ToInt16(data, 0).ToString();
+                    if (data.Length == 4) txt_DecResult.Text = BitConverter.ToInt32(data, 0).ToString();
+                    if (data.Length == 8) txt_DecResult.Text = BitConverter.ToInt64(data, 0).ToString();
+
+                }
+                catch
+                {
+                    txt_DecResult.Text = "";
+                }
+            }
+            else
+            {
+                log("读操作失败,系统错误: " + result.Status.ToString());
+            }
+
+        }
+
+        //写入characteristic的值
+        private async void btn_Write_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                //存储发送过的指令
+                bool foundinlist = false;
+                string txt = txt_write.Text;
+                foreach (string cmbitem in txt_write.Items)
+                {
+                    if (cmbitem == txt) foundinlist = true;
+                }
+                if (!foundinlist) txt_write.Items.Add(txt);
+
+                log("正在写入数据");
+                if (!String.IsNullOrEmpty(txt_write.Text))
+                {
+                    bool writeSuccessful = false;
+                    if (rad_writeUTF8.Checked == true)
+                    {
+                        var writeBuffer = CryptographicBuffer.ConvertStringToBinary(txt_write.Text, BinaryStringEncoding.Utf8);
+                        writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(writeBuffer);
+                    }
+                    else if (rad_writeDec.Checked == true)
+                    {
+                        var isValidValue = Int32.TryParse(txt_write.Text, out int readValue);
+                        if (isValidValue)
+                        {
+                            var writer = new DataWriter();
+                            writer.ByteOrder = ByteOrder.LittleEndian;
+                            writer.WriteInt32(readValue);
+
+                            writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(writer.DetachBuffer());
+                        }
+                        else
+                        {
+                            log("数据类型不是整数");
+                        }
+                    }
+                    else
+                    {
+                        try
+                        {
+                            string s = txt_write.Text.Replace(" ", "");
+                            byte[] buffer = new byte[s.Length / 2];
+                            for (int i = 0; i < s.Length; i += 2)
+                            {
+                                buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
+                            }
+                            writeSuccessful = await WriteBufferToSelectedCharacteristicAsync(WindowsRuntimeBufferExtensions.AsBuffer(buffer, 0, buffer.Length));
+                        }
+                        catch { }
+                    }
+                    //if (writeSuccessful) log("Write operation ok"); else log("Write operation failed");
+                }
+                else
+                {
+                    log("数据为空");
+                }
+            }
+            catch
+            {
+                log("写入失败");
+            }
+        }
+        //写入值到当前激活的charactistic
+        private async Task<bool> WriteBufferToSelectedCharacteristicAsync(IBuffer buffer)
+        {
+            try
+            {
+                if (ble_Characteristic == null) return false;
+                // BT_Code: Writes the value from the buffer to the characteristic.
+
+                var result = await ble_Characteristic.WriteValueWithResultAsync(buffer);
+
+                if (result.Status == GattCommunicationStatus.Success)
+                {
+                    log("写入成功");
+                    return true;
+                }
+                else
+                {
+                    log($"写入失败: {result.Status}");
+                    return false;
+                }
+            }
+            catch (Exception ex) when (ex.HResult == E_BLUETOOTH_ATT_INVALID_PDU)
+            {
+                log("系统错误 " + ex.Message);
+                return false;
+            }
+            catch (Exception ex) when (ex.HResult == E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED || ex.HResult == E_ACCESSDENIED)
+            {
+                // This usually happens when a device reports that it support writing, but it actually doesn't.
+                log("系统错误 " + ex.Message);
+                return false;
+            }
+        }
+
+
+        private void lv_device_SizeChanged(object sender, EventArgs e)
+        {
+            //设备列表的尺寸发生变更则按比例改变列宽
+            float factor = (float)this.Width / 874;
+            lv_device.Columns[0].Width = (int)(200 * factor);
+            lv_device.Columns[1].Width = (int)(360 * factor);
+            lv_device.Columns[2].Width = (int)(100 * factor);
+            lv_device.Columns[3].Width = (int)(100 * factor);
+            lv_device.Columns[4].Width = (int)(60 * factor);
+        }
+
+        private void lv_device_MouseDoubleClick(object sender, MouseEventArgs e)
+        {
+            //尝试配对设备
+            btn_pair_Click(sender, e);
+        }
+
+
+
+        private void Form1_Shown(object sender, EventArgs e)
+        {
+            //MSerialization.MSaveControl.Load_All_SupportedControls(this.Controls);
+            txt_deviceID.Text = "";
+            txt_devicename.Text = "";
+            txt_callback_service.Text = "";
+            txt_callback_characteristic.Text = "";
+            txt_rssi.Text = "";
+            lab_connection.ForeColor = Color.Gray;
+        }
+
+        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            MSaveControl.Save_All_SupportedControls(this.Controls);
+        }
+        /// <summary>
+        /// 设备连接状态发生变化的回调函数
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="args"></param>
+        private void CurrentDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
+        {
+            if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected && ble_Device.DeviceId != null)
+            {
+                //log("Device disconnected");
+                this.Invoke(new Action(() => {
+                    lab_connection.BackColor = Color.Red;
+                    lab_connection.ForeColor = Color.White;
+                    lab_connection.Text = "Disconnected...";
+                }));
+
+            }
+            else
+            {
+                //log("Device connected");
+                this.Invoke(new Action(() => {
+                    lab_connection.BackColor = Color.Green;
+                    lab_connection.ForeColor = Color.White;
+                    lab_connection.Text = "Connected";
+                    ble_Device = sender;
+
+                }));
+            }
+        }
+        /// <summary>
+        /// 用户定制功能
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void button1_Click(object sender, EventArgs e)
+        {
+            txt_write.Text = "steer," + textBox1.Text;
+            btn_Write_Click(sender, e);
+        }
+
+        private void button2_Click(object sender, EventArgs e)
+        {
+            txt_write.Text = "steer," + textBox2.Text;
+            btn_Write_Click(sender, e);
+        }
+
+        private void button3_Click(object sender, EventArgs e)
+        {
+            txt_write.Text = "steer," + textBox3.Text;
+            btn_Write_Click(sender, e);
+        }
+
+        private void toolStripStatusLabel1_Click(object sender, EventArgs e)
+        {
+            Process.Start("https://shop319667793.taobao.com/index.htm?spm=2013.1.w5002-23636016701.2.82507181okoQR7");
+        }
+
+        private void status_text_Click(object sender, EventArgs e)
+        {
+            Process.Start("https://www.miuser.net/385.html");
+        }
+
+        private void groupBox4_Enter(object sender, EventArgs e)
+        {
+
+        }
+
+        private void cb_display_callbackData_Click(object sender, EventArgs e)
+        {
+            if (cb_display_callbackData.Checked)
+            {
+                HookNotify();
+            }
+            else
+            {
+                UnhookNotify();
+            }
         }
     }
+
 }
+

+ 149 - 0
BLETool/MainForm.resx

@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="ss_bottom.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>72</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        AAABAAIAEBAQAAAABAAoAQAAJgAAACAgEAAAAAQA6AIAAE4BAAAoAAAAEAAAACAAAAABAAQAAAAAAIAA
+        AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/
+        AAAA//8A/wAAAP8A/wD//wAA////AMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMz////MzP
+        /MzP/Mz/zM/8zM/8zP/Mz/zMz/zM/8zP/MzP///8zM/8zM/8zP/Mz/zMz/zM/8zP/MzP/Mz/zM/8zM//
+        //zP///8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAgAAAAQAAAAAEABAAAAAAAAAIAAAAA
+        AAAAAAAAEAAAABAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD/
+        /wD/AAAA/wD/AP//AAD///8AzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM
+        zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM
+        zMzMzMzMzMzMzMz//////8zMzMzP//zMzMzM///////8zMzMz//8zMzMzP///////8zMzM///MzMzMz/
+        /8zM///8zMzP//zMzMzM///MzM///MzMz//8zMzMzP//zMzP//zMzM///MzMzMz//8zMz//8zMzP//zM
+        zMzM///MzP///MzMz//8zMzMzP///////8zMzM///MzMzMz///////zMzMzP//zMzMzM///////MzMzM
+        z//8zMzMzP//zM///MzMzM///MzMzMz//8zM///MzMzP//zMzMzM///MzP//zMzMz//8zMzMzP//zM//
+        /8zMzM///MzMzMz////////M/////////8zM///////8zP/////////MzP//////zMz/////////zMzM
+        zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM
+        zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMwAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
+</value>
+  </data>
+</root>

BIN
BLETool/bt.ico


+ 10 - 0
README.md

@@ -0,0 +1,10 @@
+# BLETool
+
+蓝牙工具,基于windows C# .net 4.7,蓝牙搜索,配对,写入,读取等
+
+## License
+
+
+## Reference
+
+- 参考了[miuser00/blecomm](https://gitee.com/miuser00/blecomm)

+ 4 - 0
docs/index.md

@@ -0,0 +1,4 @@
+# 开发文档
+
+
+