//TODO PairRequest XmlDocs for XP and CE pre 5.0.
        /// <summary>
        /// Intiates pairing for a remote device.
        /// </summary>
        /// <param name="device">Remote device with which to pair.</param>
        /// <param name="pin">Chosen PIN code, must be between 1 and 16 ASCII characters.</param>
        /// <remarks><para>On Windows CE platforms this calls <c>BthPairRequest</c>,
        /// its MSDN remarks say:
        /// </para>
        /// <para>&#x201C;BthPairRequest passes the parameters to the <c>BthSetPIN</c>
        /// function and creates an ACL connection. Once the connection is established,
        /// it calls the <c>BthAuthenticate</c> function to authenticate the device.&#x201D;
        /// </para>
        /// <para>On Windows XP/Vista platforms this calls <c>BluetoothAuthenticateDevice</c>,
        /// if the pin argument is set to null a Wizard is displayed to accept a PIN from the user,
        /// otherwise the function executes in transparent mode.
        /// </para>
        /// <para>See also
        /// <see cref="M:InTheHand.Net.Bluetooth.BluetoothSecurity.SetPin(InTheHand.Net.BluetoothAddress,System.String)"/>
        /// </para>
        /// </remarks>
        /// <returns>Whether the operation was successful.</returns>
        public bool PairRequest(BluetoothAddress device, string pin)
        {
            if (device == null)
            {
                throw new ArgumentNullException("device");
            }
            if (device.ToInt64() == 0)
            {
                throw new ArgumentNullException("device", "A non-blank address must be specified.");
            }
#if NETCF
            if (pin == null)
            {
                throw new ArgumentNullException("pin");
            }

            bool success             = false;
            IBluetoothDeviceInfo bdi = new WindowsBluetoothDeviceInfo(device);

            if (System.Environment.OSVersion.Version.Major >= 5)
            {
                byte[] pinbytes = System.Text.Encoding.ASCII.GetBytes(pin);
                int    len      = pin.Length;
                int    result   = NativeMethods.BthPairRequest(device.ToByteArray(), len, pinbytes);
                if (result == 0)
                {
                    success = true;
                }
            }
            else
            {
                //BthPairRequest is CE 5.0 onwards so we will do it with individual steps

                //preset outgoing pin
                success = SetPin(device, pin);

                if (success)
                {
                    int    hresult;
                    ushort handle = 0;

                    //connect to device
                    try
                    {
                        hresult = NativeMethods.BthCreateACLConnection(device.ToByteArray(), out handle);
                        if (hresult != 0)
                        {
                            success = false;
                        }
                        else
                        {
                            //force authentication
                            hresult = NativeMethods.BthAuthenticate(device.ToByteArray());
                            if (hresult != 0)
                            {
                                success = false;
                            }
                        }
                    }
                    finally
                    {
                        if (handle != 0)
                        {
                            //close connection
                            hresult = NativeMethods.BthCloseConnection(handle);
                        }
                    }
                }
            }

            if (success)
            {
                //setup UI pairing (registry)
                RegistryKey rkDevices   = Registry.LocalMachine.CreateSubKey(NativeMethods.ceRegistryRoot + "\\Device");
                RegistryKey rkNewDevice = rkDevices.CreateSubKey(device.ToString());
                rkNewDevice.SetValue("name", bdi.DeviceName);
                rkNewDevice.SetValue("trusted", 1);
                rkNewDevice.SetValue("class", bdi.ClassOfDevice.GetHashCode());

//#if V2
                RegistryKey     rkServices = rkNewDevice.CreateSubKey("Services");
                ServiceRecord[] recs       = bdi.GetServiceRecords(BluetoothService.SerialPort);
                //byte[][] recs = bdi.GetServiceRecordsUnparsedWindowsRaw(BluetoothService.SerialPort);

                if (recs.Length > 0)
                {
                    byte[]      servRecord = recs[0].SourceBytes;
                    RegistryKey rkSerial   = rkServices.CreateSubKey(BluetoothService.SerialPort.ToString());
                    rkSerial.SetValue("sdprecord", servRecord);
                    rkSerial.SetValue("Name", "Serial Port");
                    rkSerial.SetValue("enabled", 1);
                    int channel = ServiceRecordHelper.GetRfcommChannelNumber(recs[0]);
                    if (channel != -1)
                    {
                        rkSerial.SetValue("channel", 0x14b0000 + channel);
                    }
                    else
                    {
                        System.Diagnostics.Debug.Fail("PairRequest CE, peer SPP record missing channel.");
                    }
                    rkSerial.Close();
                }
                rkServices.Close();
//#endif

                rkNewDevice.Close();
                rkDevices.Close();
            }

            return(success);
#else
            //use other constructor to ensure struct size is set
            BLUETOOTH_DEVICE_INFO bdi = new BLUETOOTH_DEVICE_INFO(device.ToInt64());

            //string length, but allow for null pins for UI
            int length = 0;
            if (pin != null)
            {
                length = pin.Length;
            }
            int result = NativeMethods.BluetoothAuthenticateDevice(IntPtr.Zero, IntPtr.Zero, ref bdi, pin, length);

            if (result != 0)
            {
                //determine error cause from "result"...
                // ERROR_INVALID_PARAMETER      87
                // WAIT_TIMEOUT                258
                // ERROR_NOT_AUTHENTICATED    1244
                Debug.WriteLine("PairRequest/BAD failed with: " + result);
                return(false);
            }
            return(true);
#endif
        }
Example #2
0
        IBluetoothDeviceInfo[] DoDiscoverDevices(int maxDevices,
                                                 bool authenticated, bool remembered, bool unknown, bool discoverableOnly,
                                                 InTheHand.Net.Sockets.BluetoothClient.LiveDiscoveryCallback liveDiscoHandler, object liveDiscoState)
        {
            WqsOffset.AssertCheckLayout();
            CsaddrInfoOffsets.AssertCheckLayout();
            //
#if !NETCF
            Debug.Assert(liveDiscoHandler == null, "Don't use the NETCF live-disco feature on Win32!");
#endif
#if WinXP
            const bool Win32DiscoverableOnlyIncludesAllDevices = false;
#endif
            var prototype                = new BluetoothEndPoint(BluetoothAddress.None, BluetoothService.Empty);
            int discoveredDevices        = 0;
            List_IBluetoothDeviceInfo al = null;

            IntPtr handle       = IntPtr.Zero;
            int    lookupresult = 0;

            /* Windows XP SP3
             * Begin takes the full Inquiry time.
             *          12:09:15.7968750: Begin
             *  10.265s 12:09:26.0625000: Begin complete
             *          12:09:26.0625000: Next
             *          12:09:26.0625000: Next Complete
             *          ...
             *          12:09:26.1718750: Next
             *          12:09:26.1718750: Next Complete
             *          12:09:26.1718750: End
             *          12:09:26.1718750: End Complete
             */
            /* WM 6 SP
             * Begin is quick, Next blocks until a device is found.
             *         13:46:47.1760000: Begin
             *         13:46:47.2350000: Begin complete
             *         13:46:47.2360000: Next
             * 10.537s	13:46:57.7730000: Next Complete
             *         13:46:57.8910000: Next
             *         13:46:57.8940000: Next Complete
             *         13:46:57.8950000: Next
             *         13:46:57.8960000: Next Complete
             *         13:46:57.8960000: End
             *         13:46:57.8990000: End Complete
             *
             */
            System.Text.StringBuilder timings = null;
#if DEBUG
            //      timings = new System.Text.StringBuilder();
#endif
            Action <string> markTime = delegate(string name) {
                if (timings != null)
                {
                    timings.AppendFormat(CultureInfo.InvariantCulture,
                                         "{1}: {0}\r\n", name, DateTime.UtcNow.TimeOfDay);
                }
            };

#if WinXP
            if (discoverableOnly && !Win32DiscoverableOnlyIncludesAllDevices)
            {
                // No way to separate out the devices-in-range on Win32. :-(
                return(new IBluetoothDeviceInfo[0]);
            }
#endif
#if NETCF
            DateTime discoTime = DateTime.UtcNow;
            if (unknown || discoverableOnly)
            {
#endif
            al = new List_IBluetoothDeviceInfo();
            byte[] buffer = new byte[1024];
            BitConverter.GetBytes(WqsOffset.StructLength_60).CopyTo(buffer, WqsOffset.dwSize_0);
            BitConverter.GetBytes(WqsOffset.NsBth_16).CopyTo(buffer, WqsOffset.dwNameSpace_20);

            int bufferlen = buffer.Length;


            BTHNS_INQUIRYBLOB bib = new BTHNS_INQUIRYBLOB();
            bib.LAP = iac;// 0x9E8B33;

#if NETCF
            bib.length        = Convert.ToByte(inquiryLength.TotalSeconds / 1.28);
            bib.num_responses = Convert.ToByte(maxDevices);
#else
            bib.length = Convert.ToByte(inquiryLength.TotalSeconds);
#endif
            GCHandle hBib = GCHandle.Alloc(bib, GCHandleType.Pinned);
            IntPtr pBib   = hBib.AddrOfPinnedObject();

            BLOB b = new BLOB(8, pBib);


            GCHandle hBlob = GCHandle.Alloc(b, GCHandleType.Pinned);

            Marshal32.WriteIntPtr(buffer, WqsOffset.lpBlob_56, hBlob.AddrOfPinnedObject());


            //start looking for Bluetooth devices
            LookupFlags flags = LookupFlags.Containers;

#if WinXP
            //ensure cache is cleared on XP when looking for new devices
            if (unknown || discoverableOnly)
            {
                flags |= LookupFlags.FlushCache;
            }
#endif
            markTime("Begin");
            lookupresult = NativeMethods.WSALookupServiceBegin(buffer, flags, out handle);
            markTime("Begin complete");

            hBlob.Free();
            hBib.Free();

            // TODO ?Change "while(...maxDevices)" usage on WIN32?
            while (discoveredDevices < maxDevices && lookupresult != -1)
            {
                markTime("Next");
#if NETCF
                lookupresult = NativeMethods.WSALookupServiceNext(handle, LookupFlags.ReturnAddr | LookupFlags.ReturnBlob, ref bufferlen, buffer);
#else
                LookupFlags flagsNext = LookupFlags.ReturnAddr;
#if WIN32_READ_BTH_DEVICE_INFO
                flagsNext |= LookupFlags.ReturnBlob;
#endif
                lookupresult = NativeMethods.WSALookupServiceNext(handle, flagsNext, ref bufferlen, buffer);
#endif
                markTime("Next Complete");

                if (lookupresult != -1)
                {
                    //increment found count
                    discoveredDevices++;


                    //status
#if WinXP
                    BTHNS_RESULT status   = (BTHNS_RESULT)BitConverter.ToInt32(buffer, WqsOffset.dwOutputFlags_52);
                    bool         devAuthd = ((status & BTHNS_RESULT.Authenticated) == BTHNS_RESULT.Authenticated);
                    bool         devRembd = ((status & BTHNS_RESULT.Remembered) == BTHNS_RESULT.Remembered);
                    if (devAuthd && !devRembd)
                    {
                        System.Diagnostics.Debug.WriteLine("Win32 BT disco: Auth'd but NOT Remembered.");
                    }
                    bool devUnkwn = !devRembd && !devAuthd;
                    bool include  = (authenticated && devAuthd) || (remembered && devRembd) || (unknown && devUnkwn);
                    Debug.Assert(!discoverableOnly, "Expected short circuit for Win32 unsupported discoverableOnly!");
                    if (include)
#else
                    if (true)
#endif
                    {
#if NETCF
                        IntPtr           lpBlob = (IntPtr)BitConverter.ToInt32(buffer, 56);
                        BLOB             ib     = (BLOB)Marshal.PtrToStructure(lpBlob, typeof(BLOB));
                        BthInquiryResult bir    = (BthInquiryResult)Marshal.PtrToStructure(ib.pBlobData, typeof(BthInquiryResult));
#endif
                        //struct CSADDR_INFO {
                        //    SOCKET_ADDRESS LocalAddr;
                        //    SOCKET_ADDRESS RemoteAddr;
                        //    INT iSocketType;
                        //    INT iProtocol;
                        //}
                        //struct SOCKET_ADDRESS {
                        //    LPSOCKADDR lpSockaddr;
                        //    INT iSockaddrLength;
                        //}
                        //pointer to outputbuffer
                        IntPtr bufferptr = Marshal32.ReadIntPtr(buffer, WqsOffset.lpcsaBuffer_48);
                        //remote socket address
                        IntPtr sockaddrptr = Marshal32.ReadIntPtr(bufferptr, CsaddrInfoOffsets.OffsetRemoteAddr_lpSockaddr_8);
                        //remote socket len
                        int sockaddrlen = Marshal.ReadInt32(bufferptr, CsaddrInfoOffsets.OffsetRemoteAddr_iSockaddrLength_12);


                        SocketAddress btsa = new SocketAddress(AddressFamily32.Bluetooth, sockaddrlen);

                        for (int sockbyte = 0; sockbyte < sockaddrlen; sockbyte++)
                        {
                            btsa[sockbyte] = Marshal.ReadByte(sockaddrptr, sockbyte);
                        }

                        var bep = (BluetoothEndPoint)prototype.Create(btsa);

                        //new deviceinfo
                        IBluetoothDeviceInfo newdevice;

#if NETCF
                        newdevice = new WindowsBluetoothDeviceInfo(bep.Address, bir.cod);
                        // Built-in to Win32 so only do on NETCF
                        newdevice.SetDiscoveryTime(discoTime);
#else
                        newdevice = new WindowsBluetoothDeviceInfo(bep.Address);
#if WIN32_READ_BTH_DEVICE_INFO
                        ReadBlobBTH_DEVICE_INFO(buffer, newdevice);
#endif
#endif
                        //add to discovered list
                        al.Add(newdevice);
                        if (liveDiscoHandler != null)
                        {
                            liveDiscoHandler(newdevice, liveDiscoState);
                        }
                    }
                }
            }//while
#if NETCF
        }
#endif

            //stop looking
            if (handle != IntPtr.Zero)
            {
                markTime("End");
                lookupresult = NativeMethods.WSALookupServiceEnd(handle);
                markTime("End Complete");
            }
            if (timings != null)
            {
                Debug.WriteLine(timings);
#if !NETCF
                Console.WriteLine(timings);
#endif
            }

#if NETCF
            List_IBluetoothDeviceInfo known = WinCEReadKnownDevicesFromRegistry();
            al = BluetoothClient.DiscoverDevicesMerge(authenticated, remembered, unknown,
                                                      known, al, discoverableOnly, discoTime);
#endif


            //return results
            if (al.Count == 0)
            {
                //special case for empty collection
                return(new IBluetoothDeviceInfo[0] {
                });
            }

            return((IBluetoothDeviceInfo[])al.ToArray(
#if V1
                       typeof(IBluetoothDeviceInfo)
#endif
                       ));
        }