コード例 #1
0
ファイル: BluesoleilFactory.cs プロジェクト: zhubin-12/32feet
            //----
            internal void SdkInit()
            {
                bool init = _api.Btsdk_IsSDKInitialized();
                bool conn = _api.Btsdk_IsServerConnected(); // Ok to call if '!init'?

                if (init && conn)
                {
                    return;
                }
                BtSdkError ret = _api.Btsdk_Init();

                //Debug.WriteLine("BlueSoleil Btsdk_Init: " + BluesoleilUtils.BtSdkErrorToString(ret));
                BluesoleilUtils.CheckAndThrow(ret, "Btsdk_Init");
#if DEBUG
                // When Radio is Off, Init succeeds when called but IsSDKInitialized
                // still returns false!  That persists until the radio's turned on.
                bool initAfter = _api.Btsdk_IsSDKInitialized();
                bool connAfter = _api.Btsdk_IsServerConnected(); // Ok to call if '!init'?
                if (!initAfter || !connAfter)
                {
                    bool ready = _api.Btsdk_IsBluetoothReady();
                    Debug.Assert(!ready, "Not init&&conn, but NOT ready...");
                    Debug.Assert(!init, "Not init&&conn, but NOT !init...");
                    Debug.Assert(conn, "Not init&&conn, but NOT conn...");
                }
#endif
                _needsDispose = true;
            }
コード例 #2
0
        //----
        internal static BluesoleilDeviceInfo CreateFromGivenAddress(BluetoothAddress addr, BluesoleilFactory factory)
        {
            UInt32 hDev;

            byte[] bd_addr = BluesoleilUtils.FromBluetoothAddress(addr);
            bool   remembered;

            hDev = factory.Api.Btsdk_GetRemoteDeviceHandle(bd_addr);
            if (hDev != StackConsts.BTSDK_INVALID_HANDLE)
            {
                remembered = true;
            }
            else
            {
                // Presumably this means that the device isn't known.
                // Does this add it forever? -- no???
                hDev = factory.Api.Btsdk_AddRemoteDevice(bd_addr);
                if (hDev == StackConsts.BTSDK_INVALID_HANDLE)
                {
                    BluesoleilUtils.CheckAndThrow(BtSdkError.SDK_UNINIT, "Btsdk_Get/AddRemoteDevice");
                }
                remembered = false;
            }
            var result = new BluesoleilDeviceInfo(hDev, factory);

            result._remembered = remembered;
            Debug.Assert(addr.Equals(result.DeviceAddress), "Address changed in create! was: "
                         + addr + ", now: " + result.DeviceAddress);
            return(result);
        }
コード例 #3
0
ファイル: BluesoleilFactory.cs プロジェクト: zhubin-12/32feet
            internal void CloseAnyLiveConnections()
            {
                ICollection <BTCONNHDL> hConnList;

                lock (_liveConns) {
                    hConnList = _liveConns.Keys;
                }
                int count = 0, okCount = 0;;

                foreach (var hConn in hConnList)
                {
                    var ret = _api.Btsdk_Disconnect(hConn);
                    Debug.WriteLineIf(ret != BtSdkError.OK, "KillAllConnections: Disconnect: "
                                      + BluesoleilUtils.BtSdkErrorToString(ret));
                    ++count;
                    if (ret == BtSdkError.OK)
                    {
                        ++okCount;
                    }
                }//for
                try {
                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                  "BlueSoleil CloseAnyLiveConnections: count: {0}, okCount: {1}",
                                                  count, okCount));
                } catch (ObjectDisposedException) {
                }
            }
コード例 #4
0
ファイル: BluesoleilClient.cs プロジェクト: zhubin-12/32feet
        //----
        void ConnectRfcommPreAllocateComPort(UInt16 svcClass16, UInt32 hDev,
                                             out UInt32 hConn, out byte channel, out int comPort
                                             //, out UInt32 comSerialNum
                                             )
        {
            UInt32 comSerialNum;
            //
            UInt32     comSerialNum0;
            BtSdkError ret;
            UInt32     osComPort;
            //
            const UInt32 UsageTypeConst = 1;
            const StackConsts.COMM_SET flags
                = StackConsts.COMM_SET.Record | StackConsts.COMM_SET.UsageType;
            const UInt16 BTSDK_CLS_SERIAL_PORT = 0x1101;
            const int    Timeout = 2200;

            //
            comSerialNum0 = _factory.Api.Btsdk_GetASerialNum();
            comSerialNum  = comSerialNum0;
            Debug.Assert(comSerialNum != 0, "INFO comSerialNum == 0 wierd maybe???");
            bool success = _factory.Api.Btsdk_PlugInVComm(comSerialNum, out osComPort,
                                                          UsageTypeConst, flags, Timeout);

            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                          "Btsdk_GetASerialNum comSerialNum: {0}, Btsdk_PlugInVComm success: {1}, osComPort: {2}",
                                          comSerialNum0, success, osComPort));
            if (!success)
            {
                BluesoleilUtils.CheckAndThrow(BtSdkError.OPERATION_FAILURE, "Btsdk_PlugInVComm");
            }
            comPort = checked ((int)osComPort);
            ret     = _factory.Api.Btsdk_InitCommObj(checked ((byte)osComPort),
                                                     BTSDK_CLS_SERIAL_PORT);
            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                          "Btsdk_InitCommObj ret: {0}", ret));
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_InitCommObj");
            //
            bool connSuccess = false;

            try {
                var sppStru = new Structs.BtSdkSPPConnParamStru(osComPort);
                ret = _factory.Api.Btsdk_ConnectEx(hDev, svcClass16,
                                                   ref sppStru, out hConn);
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                              "ret: {0}, hConn: 0x{1:X}", ret, hConn));
                BluesoleilUtils.CheckAndThrow(ret, "Btsdk_ConnectEx");
                _hDev = hDev;
                //
                channel = 0; // unknown
                Console.WriteLine("Connect remote SPP Service with local COM{0}\n", osComPort);
                connSuccess = true;
            } finally {
                if (!connSuccess)
                {
                    FreeComIndex(_factory, comPort, comSerialNum0);
                }
            }
        }
コード例 #5
0
ファイル: BluesoleilClient.cs プロジェクト: zhubin-12/32feet
        //----
        private void ConnectRfcomm(BluetoothEndPoint remoteEP, UInt32 hDev,
                                   out UInt32 hConn, out byte channel, out int comPort)
        {
            Structs.BtSdkAppExtSPPAttrStru sppAttr = new Structs.BtSdkAppExtSPPAttrStru(remoteEP);
            //
            Debug.WriteLine("Gonna Btsdk_ConnectAppExtSPPService with: " + sppAttr.ToString());
            BtSdkError ret = _factory.Api.Btsdk_ConnectAppExtSPPService(hDev, ref sppAttr, out hConn);

            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                          "ret: {0}, hConn: 0x{1:X}, with: {2}", ret, hConn, sppAttr.ToString()));
            _hDev = hDev;
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_ConnectAppExtSPPService");
            //
            var comNumGetClientPort = _factory.Api.Btsdk_GetClientPort(hConn);

            Debug.WriteLine("comNumGetClientPort: " + comNumGetClientPort);
            //
            if (sppAttr.rf_svr_chnl != 0)
            {
                Debug.Assert(sppAttr.com_index != 0, "Argghhhh com_index is zero! (When rf_svr_chnl isn't).");
                channel = sppAttr.rf_svr_chnl;
                comPort = sppAttr.com_index;
            }
            else
            {
                // Some profiles are handled specifically OBEX, etc etc
                // so they don't create a COM port when that
                Debug.Assert(sppAttr.com_index == 0, "Don't expect a COM port to be created in this (fail) case.");
                //
                // Connecting to SPP 0x1101 also returns no com port in the
                // struct but a COM port is connected for it. Btsdk_GetClientPort
                // DOES return the correct port see whether we're in that case.
                if (comNumGetClientPort != 0)
                {
                    comPort = comNumGetClientPort;
                    channel = 0; // Unknown!
                }
                else
                {
                    // Highly likely an OPP/etc connection was made, and not a RFCOMM
                    // connection, and thus no COM port we can use. :-(  So fail!
                    Trace.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                  "BlueSoleil seems no RFCOMM connection made, closing. (channel: {0}, COM: {1})",
                                                  sppAttr.rf_svr_chnl, sppAttr.com_index));
                    // (Note: Add a dummy record so RemoveLiveConnection works ok).
                    int liveCountB = _factory.AddConnection(hConn, NullBluesoleilConnection.Instance);
                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                  "BlueSoleilClient.Connect non-RFCOMM LiveConns count: {0}.", liveCountB));
                    var retD = _factory.Api.Btsdk_Disconnect(hConn);
                    BluesoleilUtils.Assert(retD, "Close non-RFCOMM connection");
                    throw BluesoleilUtils.ErrorConnectIsNonRfcomm();
                }
            }
            //System.Windows.Forms.MessageBox.Show("BlueSoleil created port: '" + comPort + "'.");
        }
コード例 #6
0
        internal BluesoleilRadio(BluesoleilFactory fcty)
        {
            _fcty = fcty;
            _fcty.SdkInit();
            BtSdkError retAddr, ret;

            byte[] bd_addr = new byte[StackConsts.BTSDK_BDADDR_LEN];
            ret = retAddr = _fcty.Api.Btsdk_GetLocalDeviceAddress(bd_addr);
            BluesoleilUtils.Assert(retAddr, "Btsdk_GetLocalDeviceAddress");
            if (ret == BtSdkError.OK)
            {
                _addr = BluesoleilUtils.ToBluetoothAddress(bd_addr);
            }
            else
            {
                _addr = BluetoothAddress.None;
            }
            //
            byte[] arr = new byte[500];
            UInt16 len = checked ((UInt16)arr.Length);

            ret = _fcty.Api.Btsdk_GetLocalName(arr, ref len);
            if (retAddr == BtSdkError.OK)
            {
                BluesoleilUtils.Assert(ret, "Btsdk_GetLocalName");
            }
            if (ret == BtSdkError.OK)
            {
                _name = BluesoleilUtils.FromNameString(arr, len);
            }
            else
            {
                _name = string.Empty;
            }
            //
            uint cod;

            ret = _fcty.Api.Btsdk_GetLocalDeviceClass(out cod);
            //BluesoleilUtils.CheckAndThrow(ret);
            if (retAddr == BtSdkError.OK)
            {
                Debug.Assert(ret == BtSdkError.OK, "Btsdk_GetLocalDeviceClass ret: " + ret);
            }
            _cod = new ClassOfDevice(cod);
            //
            _lmp = new Structs.BtSdkLocalLMPInfoStru(HciVersion.Unknown);
            ret  = _fcty.Api.Btsdk_GetLocalLMPInfo(ref _lmp);
            if (retAddr == BtSdkError.OK)
            {
                BluesoleilUtils.Assert(ret, "Btsdk_GetLocalLMPInfo");
            }
        }
コード例 #7
0
ファイル: BluesoleilClient.cs プロジェクト: zhubin-12/32feet
        internal static void FreeComIndex(BluesoleilFactory factory, int comNum, uint comSerialNum)
        {
            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                          "BluesoleilClient.FreeComIndex IN: comNum: {0}, comSerialNum: {1}",
                                          comNum, comSerialNum));
            BtSdkError ret;
            var        comNum8 = checked ((byte)comNum);

            ret = factory.Api.Btsdk_DeinitCommObj(comNum8);
            BluesoleilUtils.Assert(ret, "Btsdk_DeinitCommObj");
            factory.Api.Btsdk_PlugOutVComm(comSerialNum, StackConsts.COMM_SET.Record);
            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                          "BluesoleilClient.FreeComIndex OUT: Btsdk_DeinitCommObj ret: {0}", ret));
        }
コード例 #8
0
ファイル: BluesoleilFactory.cs プロジェクト: zhubin-12/32feet
            internal void Dispose(bool disposing)
            {
                if (!_needsDispose)
                {
                    return;
                }
                //
                CloseAnyLiveConnections();
                //
                _needsDispose = false;
                BtSdkError ret = _api.Btsdk_Done();

                BluesoleilUtils.Assert(ret, "Btsdk_Done");
            }
コード例 #9
0
ファイル: BluesoleilFactory.cs プロジェクト: zhubin-12/32feet
        internal void RegisterCallbacksOnce()
        {
            if (_inquiryResultIndFunc != null)
            {
                return;
            }
            BtSdkError ret;

            Structs.BtSdkCallbackStru val;
            //
            _inquiryResultIndFunc = _inquiryHandler.HandleInquiryResultInd;
            val = new Structs.BtSdkCallbackStru(_inquiryResultIndFunc);
            Debug.Assert(val._type == StackConsts.CallbackType.INQUIRY_RESULT_IND);
            ret = Api.Btsdk_RegisterCallback4ThirdParty(ref val);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_RegisterCallback4ThirdParty");
            //
            _inquiryCompleteIndFunc = HandleInquiryComplete;
            val = new Structs.BtSdkCallbackStru(_inquiryCompleteIndFunc);
            Debug.Assert(val._type == StackConsts.CallbackType.INQUIRY_COMPLETE_IND);
            ret = Api.Btsdk_RegisterCallback4ThirdParty(ref val);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_RegisterCallback4ThirdParty");
            //
            //----
            _pinReqIndFunc = _sec.HandlePinReqInd;
            val            = new Structs.BtSdkCallbackStru(_pinReqIndFunc);
            Debug.Assert(val._type == StackConsts.CallbackType.PIN_CODE_IND);
            ret = Api.Btsdk_RegisterCallback4ThirdParty(ref val);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_RegisterCallback4ThirdParty");
            //
            //----
            _connectionEventIndFunc = _records.HandleConnectionEventInd;
            val = new Structs.BtSdkCallbackStru(_connectionEventIndFunc);
            Debug.Assert(val._type == StackConsts.CallbackType.CONNECTION_EVENT_IND);
            ret = Api.Btsdk_RegisterCallback4ThirdParty(ref val);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_RegisterCallback4ThirdParty");
            //
            _statusCallback = HandleReceiveBluetoothStatusInfo;
            ret             = Api.Btsdk_RegisterGetStatusInfoCB4ThirdParty(ref _statusCallback);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_RegisterGetStatusInfoCB4ThirdParty");
            ret = Api.Btsdk_SetStatusInfoFlag(StackConsts.BTSDK_BLUETOOTH_STATUS_FLAG);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_SetStatusInfoFlag");
        }
コード例 #10
0
ファイル: BluesoleilFactory.cs プロジェクト: zhubin-12/32feet
            internal IAsyncResult BeginInquiry(int maxDevices, TimeSpan inquiryLength,
                                               AsyncCallback callback, object state,
                                               BluetoothClient.LiveDiscoveryCallback liveDiscoHandler, object liveDiscoState,
                                               DiscoDevsParams args)
            {
                _fcty.SdkInit();
                _fcty.RegisterCallbacksOnce();
                //
                byte maxDurations;
                byte maxNum;

                CommonDiscoveryBluetoothClient.ConvertBthInquiryParams(maxDevices, inquiryLength, out maxNum, out maxDurations);
                return(BeginInquiry(maxDevices, inquiryLength,
                                    callback, state,
                                    liveDiscoHandler, liveDiscoState,
                                    delegate() {
                    BtSdkError ret = _fcty.Api.Btsdk_StartDeviceDiscovery(
                        AnyClass, maxNum, maxDurations);
                    BluesoleilUtils.CheckAndThrow(ret, "Btsdk_StartDeviceDiscovery");
                }, args));
            }
コード例 #11
0
        void GetInfo(ref BluetoothAddress addr)
        {
            BtSdkError ret;
            var        props = new Structs.BtSdkRemoteDevicePropertyStru();

            ret = _factory.Api.Btsdk_GetRemoteDeviceProperty(_hDev, out props);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_GetRemoteDeviceProperty");
            //
            if ((props.mask & Structs.BtSdkRemoteDevicePropertyStru.Mask.Handle) != 0)
            {
                Debug.Assert(_hDev == props.dev_hdl, "hDev");
            }
            else
            {
                Debug.Fail("Handle unknown?!?");
            }
            if ((props.mask & Structs.BtSdkRemoteDevicePropertyStru.Mask.Address) != 0)
            {
                addr = BluesoleilUtils.ToBluetoothAddress(props.bd_addr);
            }
            if ((props.mask & Structs.BtSdkRemoteDevicePropertyStru.Mask.Class) != 0)
            {
                _cod = new ClassOfDevice(props.dev_class);
            }
            if ((props.mask & Structs.BtSdkRemoteDevicePropertyStru.Mask.Name) != 0)
            {
                Debug.Assert(props.name.Length == StackConsts.BTSDK_DEVNAME_LEN,
                             "props.name.Length: " + props.name.Length + ", BTSDK_DEVNAME_LEN: " + StackConsts.BTSDK_DEVNAME_LEN);
                string name = BluesoleilUtils.FromNameString(props.name);
                _cachedName = name;
            }
            if ((props.mask & Structs.BtSdkRemoteDevicePropertyStru.Mask.LmpInfo) != 0)
            {
                var fs = (LmpFeatures)BitConverter.ToInt64(props.lmp_info.lmp_feature, 0);
                var v  = new RadioVersions(props.lmp_info.lmp_version, props.lmp_info.lmp_subversion,
                                           fs, props.lmp_info.manuf_name);
                _versions = v;
            }
            //props.
        }
コード例 #12
0
        public void SetMode(bool?connectable, bool?discoverable)
        {
            BtSdkError ret;

            ret = _fcty.Api.Btsdk_StartBluetooth();
            BluesoleilUtils.Assert(ret, "Radio.set_Mode Start");
            StackConsts.DiscoveryMode mask;
            // Always need to get, to get pairable etc.
            ret = _fcty.Api.Btsdk_GetDiscoveryMode(out mask);
            BluesoleilUtils.Assert(ret, "Radio.set_Mode Get");
            if (ret != BtSdkError.OK)
            {
                mask = StackConsts.BTSDK_DISCOVERY_DEFAULT_MODE;
            }
            //
            switch (connectable)
            {
            case true:
                mask |= StackConsts.DiscoveryMode.BTSDK_CONNECTABLE;
                break;

            case false:
                mask &= ~StackConsts.DiscoveryMode.BTSDK_CONNECTABLE;
                break;
                // null NOP
            }
            switch (discoverable)
            {
            case true:
                mask |= StackConsts.DiscoveryMode.BTSDK_GENERAL_DISCOVERABLE;
                break;

            case false:
                mask &= ~StackConsts.DiscoveryMode.BTSDK_GENERAL_DISCOVERABLE;
                break;
                // null NOP
            }
            ret = _fcty.Api.Btsdk_SetDiscoveryMode(mask);
            BluesoleilUtils.Assert(ret, "Radio.set_Mode Set");
        }
コード例 #13
0
        private static string ParseServiceName(ref Structs.BtSdkRemoteServiceAttrStru attrs)
        {
            string nameUtf8 = BluesoleilUtils.FixedLengthArrayToStringUtf8(attrs.svc_name);
            string nameUL   = Encoding.Unicode.GetString(attrs.svc_name);

            nameUL = nameUL.Trim('\0');
#if DEBUG
            string nameUB = Encoding.BigEndianUnicode.GetString(attrs.svc_name);
            nameUB = nameUB.Trim('\0');
            Debug.Assert(nameUL.Length == nameUB.Length,
                         "Our simple check of length doesn't work for Unicode little-endian vs big-endian, the lengths are the same."
                         + "  BUT different here!!  nameUL.Length: " + nameUL.Length + ", nameUB.Length: " + nameUB.Length);
#endif
            //
            string name = nameUtf8;
            if (nameUL.Length > name.Length)
            {
                name = nameUL;
            }
            // As described in DEBUG above, can't do this!!   if (nameUB.Length > name.Length) name = nameUB;
            return(name);
        }
コード例 #14
0
        public ServiceRecord[] GetServiceRecords(Guid service)
        {
            BtSdkError ret;
            var        search = new Structs.BtSdkSDPSearchPatternStru[1];

            search[0] = new Structs.BtSdkSDPSearchPatternStru(service);
            UInt32[] recordHandles = new uint[MaxServiceRecordsLookup];
            int      num           = recordHandles.Length;
            // Fetch the matching records (the handles of).
            int numSearch = search.Length;

            ret = _factory.Api.Btsdk_BrowseRemoteServicesEx(_hDev, search, numSearch, recordHandles, ref num);
            if (ret == BtSdkError.NO_SERVICE)   // None
            {
                return(new ServiceRecord[0]);
            }
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_BrowseRemoteServicesEx");
            // Fetch the records' attributes and convert to our format.
            var results = new List <ServiceRecord>();

            for (int i = 0; i < num; ++i)
            {
                var hRcd  = recordHandles[i];
                var attrs = new Structs.BtSdkRemoteServiceAttrStru(
                    StackConsts.AttributeLookup.ServiceName | StackConsts.AttributeLookup.ExtAttributes);
                // Use 'Get' here and not 'Refresh' as the 'Browse' call above
                // should have retrieved the attributes.
                ret = _factory.Api.Btsdk_GetRemoteServiceAttributes(hRcd, ref attrs);
                BluesoleilUtils.CheckAndThrow(ret, "Btsdk_RefreshRemoteServiceAttributes");
                Debug.Assert(attrs.dev_hdl == _hDev);
                //
                IBluesoleilApi hackApi = _factory.Api;
                ServiceRecord  sr      = CreateServiceRecord(ref attrs, hackApi);
                results.Add(sr);
            }
            return(results.ToArray());
        }
コード例 #15
0
        private BluesoleilDeviceInfo(UInt32 hDev, BluesoleilFactory factory)
        {
            if (factory == null)
            {
                throw new ArgumentNullException("factory");
            }
            _factory = factory;
            BtSdkError ret;

            _hDev = hDev;
            //
            byte[] bd_addr = new byte[StackConsts.BTSDK_BDADDR_LEN];
            ret = factory.Api.Btsdk_GetRemoteDeviceAddress(_hDev, bd_addr);
            BluesoleilUtils.CheckAndThrow(ret, "Btsdk_GetRemoteDeviceAddress");
            if (ret != BtSdkError.OK)   // Not known/present(?).
            {
                return;
            }
            _addr = BluesoleilUtils.ToBluetoothAddress(bd_addr);
            //
            uint cod;

            ret = factory.Api.Btsdk_GetRemoteDeviceClass(_hDev, out cod);
            if (ret != BtSdkError.OK)   // Not known/present(?).
            {
                return;
            }
            _cod = new ClassOfDevice(cod);
            //
            ret = _factory.Api.Btsdk_IsDevicePaired(_hDev, out _paired);
            Debug.Assert(ret == BtSdkError.OK, "Btsdk_IsDevicePaired ret: " + ret);
            //
            _connected = _factory.Api.Btsdk_IsDeviceConnected(_hDev);
            //
            GetInfo(ref _addr);
        }
コード例 #16
0
        internal static ServiceRecord CreateServiceRecord(ref Structs.BtSdkRemoteServiceAttrStru attrs, IBluesoleilApi api)
        {
            ServiceRecordBuilder bldr = new ServiceRecordBuilder();
            //--
            Guid sc = BluetoothService.CreateBluetoothUuid(attrs.svc_class);

            bldr.AddServiceClass(sc);
            //--
            string name = ParseServiceName(ref attrs);

            if (name.Length != 0)
            {
                bldr.ServiceName = name;
            }
            //
            byte?port   = null;
            var  extras = new List <ServiceAttribute>();

            Debug.Assert(attrs.status == 0, "attrs.status: 0x" + attrs.status.ToString("X"));
            if (attrs.ext_attributes != IntPtr.Zero)
            {
                if (sc == BluetoothService.HumanInterfaceDevice)
                {
                    var hidInfo = (Structs.BtSdkRmtHidSvcExtAttrStru_HACK)Marshal.PtrToStructure(
                        attrs.ext_attributes, typeof(Structs.BtSdkRmtHidSvcExtAttrStru_HACK));
                    Debug.Assert(Marshal.SizeOf(typeof(Structs.BtSdkRmtHidSvcExtAttrStru_HACK))
                                 == Marshal.SizeOf(hidInfo), "SizeOf x2");
                    Debug.Assert(hidInfo.size == Marshal.SizeOf(typeof(Structs.BtSdkRmtHidSvcExtAttrStru_HACK))
                                 + Structs.BtSdkRmtHidSvcExtAttrStru_HACK.StackMiscountsPaddingSize,
                                 "Different sizes!  hidInfo.size: " + hidInfo.size + ", SizeOf(): " + Marshal.SizeOf(typeof(Structs.BtSdkRmtHidSvcExtAttrStru_HACK)));
                    // TO-DO Human Interface (HID) record: Use "mask" field, it's undocumented, check for real life values
                    // With test SdpCreateAHumanInputDeviceRecordsAllTwoOfThree
                    // which adds two out of three of {DeviceReleaseNumber,DeviceSubclass,CountryCode}
                    // mask==0.  So mask apparently applies to other fields!
                    // So we check these three values for zero
                    // and discard them if so!
                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                  "HID.mask: {0:X}", hidInfo.mask));
                    var list = new List <ServiceAttribute>();
                    if (hidInfo.deviceReleaseNumber != 0)
                    {
                        list.Add(
                            new ServiceAttribute(HidProfileAttributeId.DeviceReleaseNumber,
                                                 new ServiceElement(ElementType.UInt16, hidInfo.deviceReleaseNumber)));
                    }
                    if (hidInfo.deviceSubclass != 0)
                    {
                        list.Add(
                            new ServiceAttribute(HidProfileAttributeId.DeviceSubclass,
                                                 new ServiceElement(ElementType.UInt8, hidInfo.deviceSubclass)));
                    }
                    if (hidInfo.countryCode != 0)
                    {
                        list.Add(
                            new ServiceAttribute(HidProfileAttributeId.CountryCode,
                                                 new ServiceElement(ElementType.UInt8, hidInfo.countryCode)));
                    }
                    // TO-DO HID other...
                    extras.AddRange(list);
                }
                else if (sc == BluetoothService.PnPInformation)
                {
                    var deviceInfo = (Structs.BtSdkRmtDISvcExtAttrStru)Marshal.PtrToStructure(
                        attrs.ext_attributes, typeof(Structs.BtSdkRmtDISvcExtAttrStru));
                    Debug.Assert(Marshal.SizeOf(typeof(Structs.BtSdkRmtDISvcExtAttrStru))
                                 == Marshal.SizeOf(deviceInfo), "SizeOf x2");
                    Debug.Assert(deviceInfo.size == Marshal.SizeOf(typeof(Structs.BtSdkRmtDISvcExtAttrStru))
                                 + Structs.BtSdkRmtDISvcExtAttrStru.StackMiscountsPaddingSize,
                                 "Different sizes!  deviceInfo.size: " + deviceInfo.size + ", Marshal.SizeOf: " + Marshal.SizeOf(typeof(Structs.BtSdkRmtDISvcExtAttrStru)));
                    // TO-DO Device Info (PnP) record: Use "mask" field, it's undocumented, check for real life values
                    //Debug.Assert(deviceInfo.mask == 0, "Is mask field in BtSdkRmtDISvcExtAttrStru ever set!!!, is here:" + deviceInfo.mask);
                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                  "PnP/DI.mask: {0:X}", deviceInfo.mask));
                    // Like above (PnP) we see mask==0 for the fields we handle
                    // here (six of).  So we check these values
                    // for zero and discard them if so!
                    var list = new List <ServiceAttribute>();
                    if (deviceInfo.spec_id != 0)
                    {
                        list.Add(
                            new ServiceAttribute(DeviceIdProfileAttributeId.SpecificationId,
                                                 new ServiceElement(ElementType.UInt16, deviceInfo.spec_id)));
                    }
                    if (deviceInfo.vendor_id != 0)
                    {
                        list.Add(
                            new ServiceAttribute(DeviceIdProfileAttributeId.VendorId,
                                                 new ServiceElement(ElementType.UInt16, deviceInfo.vendor_id)));
                    }
                    if (deviceInfo.product_id != 0)
                    {
                        list.Add(
                            new ServiceAttribute(DeviceIdProfileAttributeId.ProductId,
                                                 new ServiceElement(ElementType.UInt16, deviceInfo.product_id)));
                    }
                    if (deviceInfo.version != 0)
                    {
                        list.Add(
                            new ServiceAttribute(DeviceIdProfileAttributeId.Version,
                                                 new ServiceElement(ElementType.UInt16, deviceInfo.version)));
                    }
                    if (true /* Zero means False here!! */)
                    {
                        list.Add(
                            new ServiceAttribute(DeviceIdProfileAttributeId.PrimaryRecord,
                                                 new ServiceElement(ElementType.Boolean, deviceInfo.primary_record)));
                    }
                    if (deviceInfo.vendor_id_source != 0)
                    {
                        list.Add(
                            new ServiceAttribute(DeviceIdProfileAttributeId.VendorIdSource,
                                                 new ServiceElement(ElementType.UInt16, deviceInfo.vendor_id_source)));
                    }
                    // TO-DO URLs...
                    extras.AddRange(list);
                }
                else
                {
                    // On testing we see this never working!  For one device
                    // with an ImagingResponder record the size of 0x18 and
                    // not 0x8 as per definition, and the port value is wrong.
                    // And for its PhonebookAccessPse record the size is
                    // correctly 0x8, but again the port value is wrong!
                    //
                    var sppInfo = (Structs.BtSdkRmtSPPSvcExtAttrStru)Marshal.PtrToStructure(
                        attrs.ext_attributes, typeof(Structs.BtSdkRmtSPPSvcExtAttrStru));
                    Debug.Assert(sppInfo.size == Marshal.SizeOf(typeof(Structs.BtSdkRmtSPPSvcExtAttrStru)),
                                 "Different sizes!");
                    port = sppInfo.server_channel;
                }
                api.Btsdk_FreeMemory(attrs.ext_attributes);
            }//if (attrs.ext_attributes != NULL)
            // Use a different API to try and get the RFCOMM port number as
            // the previous API is quite rubbish at doing that!!
            var svcB   = new Structs.BtSdkAppExtSPPAttrStru(sc);
            var retSpp = api.Btsdk_SearchAppExtSPPService(attrs.dev_hdl, ref svcB);

            if (retSpp == BtSdkError.NO_SERVICE)   // error
            {
            }
            else if (retSpp != BtSdkError.OK)     // error
            {
                Debug.WriteLine("GetSvcRcds Btsdk_SearchAppExtSPPService ret: "
                                + BluesoleilUtils.BtSdkErrorToString(retSpp));
            }
            else     // success
            {
                if (svcB.rf_svr_chnl != 0)
                {
                    byte newPort = svcB.rf_svr_chnl;
                    if (port.HasValue)
                    {
                        Debug.Assert(port.Value == newPort, "port: " + port.Value + ", newPort: " + newPort);
                    }
                    else
                    {
                        port = newPort;
                    }
                }
                if (svcB.sdp_record_handle != 0)
                {
                    bldr.AddCustomAttribute(new ServiceAttribute(
                                                UniversalAttributeId.ServiceRecordHandle,
                                                ServiceElement.CreateNumericalServiceElement(ElementType.UInt32, svcB.sdp_record_handle)));
                }
#if DEBUG
                Debug.Assert(svcB.service_class_128 == sc, "svcSpp.service_class_128: " + svcB.service_class_128 + ", sc: " + sc);
                var snSpp = BluesoleilUtils.FromNameString(svcB.svc_name, StackConsts.BTSDK_SERVICENAME_MAXLENGTH);
                if (snSpp == null)
                {
                    Debug.Assert(name == null || name.Length == 0, "svcSpp.svc_name: null" + ", name: " + name);
                }
                else if (snSpp.Length == 1)
                {
                    // SearchAppExtSPPService doesn't handle Unicode
                    // but Btsdk_BrowseRemoteServicesEx etc does.
                    Debug.Assert(snSpp[0] == name[0], "svcSpp.svc_name: " + snSpp + ", name: " + name);
                }
                else
                {
                    Debug.Assert(snSpp == name, "svcSpp.svc_name: " + snSpp + ", bldr.ServiceName: " + name);
                }
#endif
            }
            //
            if (port.HasValue)
            {
            }
            else
            {
                bldr.ProtocolType = BluetoothProtocolDescriptorType.None;
            }
            if (extras.Count != 0)
            {
                bldr.AddCustomAttributes(extras);
            }
            //
            const ServiceAttributeId FakeDescr = (ServiceAttributeId)(-1);
            bldr.AddCustomAttribute(new ServiceAttribute(FakeDescr,
                                                         new ServiceElement(ElementType.TextString,
                                                                            "<partial BlueSoleil decode>")));
            ServiceRecord sr = bldr.ServiceRecord;
            if (port.HasValue)
            {
                Debug.Assert(bldr.ProtocolType == BluetoothProtocolDescriptorType.Rfcomm,
                             "type=" + bldr.ProtocolType);
                ServiceRecordHelper.SetRfcommChannelNumber(sr, port.Value);
            }
            else
            {
                bldr.ProtocolType = BluetoothProtocolDescriptorType.None;
            }
            return(sr);
        }