mirror of
https://github.com/inthehand/32feet.git
synced 2026-10-29 15:11:18 +08:00
1239 lines
46 KiB
C#
1239 lines
46 KiB
C#
// 32feet.NET - Personal Area Networking for .NET
|
|
//
|
|
// InTheHand.Net.Bluetooth.Msft.BluetoothClient
|
|
//
|
|
// Copyright (c) 2003-2008 In The Hand Ltd, All rights reserved.
|
|
// This source code is licensed under the MIT License
|
|
|
|
//#define WIN32_READ_BTH_DEVICE_INFO
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.InteropServices;
|
|
using Microsoft.Win32;
|
|
using InTheHand.Net.Bluetooth.Factory;
|
|
using InTheHand.Net.Sockets;
|
|
using InTheHand.Runtime.InteropServices;
|
|
using AsyncResult_BeginDiscoverDevices = InTheHand.Net.AsyncResult<
|
|
InTheHand.Net.Bluetooth.Factory.IBluetoothDeviceInfo[],
|
|
InTheHand.Net.Bluetooth.Msft.SocketBluetoothClient.DiscoDevsParamsWithLive>;
|
|
using List_IBluetoothDeviceInfo = System.Collections.Generic.List<InTheHand.Net.Bluetooth.Factory.IBluetoothDeviceInfo>;
|
|
using InTheHand.SystemCore;
|
|
|
|
namespace InTheHand.Net.Bluetooth.Msft
|
|
{
|
|
class SocketBluetoothClient : IBluetoothClient
|
|
{
|
|
readonly BluetoothFactory _fcty;
|
|
private bool cleanedUp = false;
|
|
private ISocketOptionHelper m_optionHelper;
|
|
#if WinXP
|
|
// If SetPin(String) is called before connect we need to know the remote
|
|
// address to start the BluetoothWin32Authenticator for, so store this
|
|
// so we can start the authenticator at connect-time.
|
|
string m_pinForConnect;
|
|
#endif
|
|
|
|
#region Constructor
|
|
#if NETCF
|
|
static SocketBluetoothClient()
|
|
{
|
|
InTheHand.Net.PlatformVerification.ThrowException();
|
|
}
|
|
#endif
|
|
|
|
internal SocketBluetoothClient(BluetoothFactory fcty)
|
|
{
|
|
Debug.Assert(fcty != null, "ArgNull");
|
|
_fcty = fcty;
|
|
try {
|
|
this.Client = CreateSocket();
|
|
} catch (SocketException se) {
|
|
throw new PlatformNotSupportedException("32feet.NET does not support the Bluetooth stack on this device.", se);
|
|
}
|
|
m_optionHelper = CreateSocketOptionHelper(this.Client);
|
|
}
|
|
internal SocketBluetoothClient(BluetoothFactory fcty, BluetoothEndPoint localEP)
|
|
: this(fcty)
|
|
{
|
|
if (localEP == null) {
|
|
throw new ArgumentNullException("localEP");
|
|
}
|
|
|
|
//bind to specific local endpoint
|
|
var bindEP = PrepareBindEndPoint(localEP);
|
|
this.Client.Bind(bindEP);
|
|
}
|
|
|
|
internal SocketBluetoothClient(BluetoothFactory fcty, Socket acceptedSocket)
|
|
{
|
|
Debug.Assert(fcty != null, "ArgNull");
|
|
_fcty = fcty;
|
|
this.Client = acceptedSocket;
|
|
active = true;
|
|
m_optionHelper = CreateSocketOptionHelper(this.Client);
|
|
}
|
|
|
|
|
|
protected virtual Socket CreateSocket()
|
|
{
|
|
return new Socket(BluetoothAddressFamily, SocketType.Stream, BluetoothProtocolType.RFComm);
|
|
}
|
|
|
|
protected virtual AddressFamily BluetoothAddressFamily
|
|
{
|
|
get { return AddressFamily32.Bluetooth; }
|
|
}
|
|
|
|
protected virtual BluetoothEndPoint PrepareConnectEndPoint(BluetoothEndPoint serverEP)
|
|
{
|
|
return serverEP;
|
|
}
|
|
|
|
protected virtual BluetoothEndPoint PrepareBindEndPoint(BluetoothEndPoint serverEP)
|
|
{
|
|
return serverEP;
|
|
}
|
|
|
|
protected virtual ISocketOptionHelper CreateSocketOptionHelper(Socket socket)
|
|
{
|
|
return new MsftSocketOptionHelper(socket);
|
|
}
|
|
#endregion
|
|
|
|
#region InquiryAccessCode
|
|
private int iac = BluetoothAddress.Giac; //0x9E8B33;
|
|
#if NETCF
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public int InquiryAccessCode
|
|
{
|
|
get
|
|
{
|
|
return iac;
|
|
}
|
|
set
|
|
{
|
|
iac = value;
|
|
}
|
|
}
|
|
#else
|
|
public int InquiryAccessCode
|
|
{
|
|
get { throw new NotSupportedException(); }
|
|
set { throw new NotSupportedException(); }
|
|
}
|
|
#endif
|
|
#endregion
|
|
|
|
#region Query Length
|
|
|
|
//length of time for query
|
|
private TimeSpan inquiryLength = new TimeSpan(0, 0, 10);
|
|
|
|
/// <summary>
|
|
/// Amount of time allowed to perform the query.
|
|
/// </summary>
|
|
/// <remarks>On Windows CE the actual value used is expressed in units of 1.28 seconds, so will be the nearest match for the value supplied.
|
|
/// The default value is 10 seconds. The maximum is 60 seconds.</remarks>
|
|
public TimeSpan InquiryLength
|
|
{
|
|
get
|
|
{
|
|
return inquiryLength;
|
|
}
|
|
set
|
|
{
|
|
if ((value.TotalSeconds > 0) && (value.TotalSeconds <= 60)) {
|
|
inquiryLength = value;
|
|
} else {
|
|
throw new ArgumentOutOfRangeException("value",
|
|
"QueryLength must be a positive timespan between 0 and 60 seconds.");
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Discover Devices
|
|
/// <summary>
|
|
/// Discovers accessible Bluetooth devices and returns their names and addresses.
|
|
/// </summary>
|
|
/// <param name="maxDevices">The maximum number of devices to get information about.</param>
|
|
/// <param name="authenticated">True to return previously authenticated/paired devices.</param>
|
|
/// <param name="remembered">True to return remembered devices.</param>
|
|
/// <param name="unknown">True to return previously unknown devices.</param>
|
|
/// <param name="discoverableOnly">True to return only discoverable devices
|
|
/// (where both in range and in discoverable mode).
|
|
/// When <see langword="true"/> all other flags are ignored.
|
|
/// <strong>Note: Does NOT work on Win32 with the Microsoft stack.</strong>
|
|
/// </param>
|
|
/// <returns>An array of BluetoothDeviceInfo objects describing the devices discovered.</returns>
|
|
/// -
|
|
/// <remarks>
|
|
/// <para>The <see paramref="discoverableOnly"/> flag will discover only
|
|
/// the devices that are in range and are in discoverable mode. This works
|
|
/// only on WM/CE with the Microsoft stack, or on any platform with the
|
|
/// Widcomm stack.
|
|
/// </para>
|
|
/// <para>
|
|
/// It does not work on desktop Windows with the Microsoft
|
|
/// stack, where the in range and remembered devices are returned already
|
|
/// merged! There simple all devices will be returned. Even the
|
|
/// <see cref="InTheHand.Net.Sockets.BluetoothDeviceInfo.LastSeen">BluetoothDeviceInfo.LastSeen</see>
|
|
/// property is of no use there: on XP and Vista at least the value provided
|
|
/// is always simply the current time.
|
|
/// </para>
|
|
/// </remarks>
|
|
IBluetoothDeviceInfo[] IBluetoothClient.DiscoverDevices(int maxDevices,
|
|
bool authenticated, bool remembered, bool unknown, bool discoverableOnly)
|
|
{
|
|
return DiscoverDevices(maxDevices,
|
|
authenticated, remembered, unknown, discoverableOnly,
|
|
null, null);
|
|
}
|
|
|
|
public virtual IBluetoothDeviceInfo[] DiscoverDevices(int maxDevices,
|
|
bool authenticated, bool remembered, bool unknown, bool discoverableOnly,
|
|
InTheHand.Net.Sockets.BluetoothClient.LiveDiscoveryCallback liveDiscoHandler, object liveDiscoState)
|
|
{
|
|
#if !NETCF
|
|
// In the MSFT+Win32 API there's no simple way to get only the
|
|
// in-range-discoverable devices, and thus it can't provide
|
|
// 'discoverableOnly' nor live discovery. So, we register for the
|
|
// native event whilst we run a normal discovery. We use the
|
|
// native event to raise our live event and to gather the devices
|
|
// to be included in the discoverableOnly result.
|
|
// Note the event seems NOT be raised on WinXP in this case.
|
|
// The event (on Win7 at least) raises lots of apparently duplicate
|
|
// events, so we need to do some filtering for both results.
|
|
var realLiveDiscoHandler = liveDiscoHandler;
|
|
//if (realLiveDiscoHandler != null) Debug.Assert(discoverableOnly || unknown, "You want live events from 'remembered'?!?");
|
|
bool keepDiscoOnly = false;
|
|
if (discoverableOnly) {
|
|
keepDiscoOnly = true;
|
|
unknown = authenticated = remembered = true;
|
|
discoverableOnly = false;
|
|
}
|
|
BluetoothWin32Events events;
|
|
EventHandler<BluetoothWin32RadioInRangeEventArgs> radioInRangeHandler;
|
|
List<IBluetoothDeviceInfo> seenDevices;
|
|
if (keepDiscoOnly || realLiveDiscoHandler != null) {
|
|
events = BluetoothWin32Events.GetInstance();
|
|
seenDevices = new List<IBluetoothDeviceInfo>();
|
|
radioInRangeHandler = delegate(object sender, BluetoothWin32RadioInRangeEventArgs e) {
|
|
var newdevice = e.DeviceWindows;
|
|
Debug.WriteLine("Radio in range: " + newdevice.DeviceAddress);
|
|
bool unique = BluetoothDeviceInfo.AddUniqueDevice(seenDevices, newdevice);
|
|
if (unique && realLiveDiscoHandler != null) {
|
|
Debug.WriteLine("Live IS unique.");
|
|
realLiveDiscoHandler(newdevice, liveDiscoState);
|
|
} else { //COVERAGE
|
|
Debug.WriteLine("Live is NOT unique ("
|
|
+ unique + "," + realLiveDiscoHandler != null + ").");
|
|
}
|
|
};
|
|
events.InRange += radioInRangeHandler;
|
|
} else {
|
|
events = null;
|
|
radioInRangeHandler = null;
|
|
seenDevices = null;
|
|
}
|
|
// Don't use the WM live-disco functionality.
|
|
liveDiscoHandler = null;
|
|
#endif
|
|
IBluetoothDeviceInfo[] result;
|
|
try {
|
|
result = DoDiscoverDevices(maxDevices,
|
|
authenticated, remembered, unknown, discoverableOnly,
|
|
liveDiscoHandler, liveDiscoState);
|
|
} finally {
|
|
#if !NETCF
|
|
if (events != null && radioInRangeHandler != null) {
|
|
events.InRange -= radioInRangeHandler;
|
|
}
|
|
#endif
|
|
}
|
|
#if !NETCF
|
|
if (keepDiscoOnly) {
|
|
Debug.Assert(seenDevices != null, "Should have created list 'seenDevices' above!");
|
|
Debug.WriteLine("Disco result: " + result.Length + ", liveDevice: " + seenDevices.Count);
|
|
result = BluetoothDeviceInfo.Intersect(result, seenDevices);
|
|
Debug.WriteLine("Result result: " + result.Length);
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
#if WinXP
|
|
private void ReadBlobBTH_DEVICE_INFO(byte[] buffer, IBluetoothDeviceInfo dev)
|
|
{
|
|
// XXXX - Testing only, at least delete the "Console.WriteLine" before use. - XXXX
|
|
IntPtr pBlob = Marshal32.ReadIntPtr(buffer, WqsOffset.lpBlob_56);
|
|
if (pBlob != IntPtr.Zero) {
|
|
BLOB blob = (BLOB)Marshal.PtrToStructure(pBlob, typeof(BLOB));
|
|
if (blob.pBlobData != IntPtr.Zero) {
|
|
BTH_DEVICE_INFO bdi = (BTH_DEVICE_INFO)Marshal.PtrToStructure(blob.pBlobData, typeof(BTH_DEVICE_INFO));
|
|
BluetoothDeviceInfoProperties flags = bdi.flags;
|
|
Debug.WriteLine(String.Format(CultureInfo.InvariantCulture, "[BTH_DEVICE_INFO: {0:X12}, '{1}' = 0x{2:X4}]", bdi.address, flags, bdi.flags));
|
|
Trace.Assert((flags & ~BDIFMasks.AllOrig) == 0, "*Are* new flags there!");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if NETCF
|
|
static class DevicesValueNames
|
|
{
|
|
internal const string DeviceName = "name";
|
|
internal const string ClassOfDevice = "class";
|
|
}
|
|
|
|
private static List_IBluetoothDeviceInfo WinCEReadKnownDevicesFromRegistry()
|
|
{
|
|
Func<BluetoothAddress, string, uint, bool, IBluetoothDeviceInfo> makeDev
|
|
= (address, name, classOfDevice, authd)
|
|
=> new WindowsBluetoothDeviceInfo(address, name, classOfDevice, authd);
|
|
var deviceListPath = "Software\\Microsoft\\Bluetooth\\Device";
|
|
return ReadKnownDevicesFromRegistry(deviceListPath, makeDev);
|
|
}
|
|
|
|
internal static List_IBluetoothDeviceInfo ReadKnownDevicesFromRegistry(
|
|
string deviceListPath, Func<BluetoothAddress, string, uint, bool, IBluetoothDeviceInfo> makeDev)
|
|
{
|
|
List_IBluetoothDeviceInfo known = new List_IBluetoothDeviceInfo();
|
|
|
|
//open bluetooth device key
|
|
RegistryKey devkey = Registry.LocalMachine.OpenSubKey(deviceListPath);
|
|
//bool addFromRegistry = authenticated || remembered;
|
|
|
|
if (devkey != null) {
|
|
|
|
//enumerate the keys
|
|
foreach (string devid in devkey.GetSubKeyNames()) {
|
|
BluetoothAddress address;
|
|
|
|
if (BluetoothAddress.TryParse(devid, out address)) {
|
|
//get friendly name
|
|
RegistryKey thisdevkey = devkey.OpenSubKey(devid);
|
|
string name = thisdevkey.GetValue(DevicesValueNames.DeviceName, "").ToString();
|
|
uint classOfDevice = Convert.ToUInt32(thisdevkey.GetValue(DevicesValueNames.ClassOfDevice, 0));
|
|
thisdevkey.Close();
|
|
|
|
//add to collection
|
|
IBluetoothDeviceInfo thisdevice = makeDev(address, name, classOfDevice, true);
|
|
known.Add(thisdevice);
|
|
}
|
|
}
|
|
|
|
devkey.Close();
|
|
}
|
|
return known;
|
|
}
|
|
#endif
|
|
|
|
#if !V1
|
|
IAsyncResult IBluetoothClient.BeginDiscoverDevices(int maxDevices,
|
|
bool authenticated, bool remembered, bool unknown, bool discoverableOnly,
|
|
AsyncCallback callback, object state)
|
|
{
|
|
return ((IBluetoothClient)this).BeginDiscoverDevices(maxDevices,
|
|
authenticated, remembered, unknown, discoverableOnly,
|
|
callback, state,
|
|
null, null);
|
|
}
|
|
|
|
IAsyncResult IBluetoothClient.BeginDiscoverDevices(int maxDevices,
|
|
bool authenticated, bool remembered, bool unknown, bool discoverableOnly,
|
|
AsyncCallback callback, object state,
|
|
InTheHand.Net.Sockets.BluetoothClient.LiveDiscoveryCallback liveDiscoHandler, object liveDiscoState)
|
|
{
|
|
var args = new DiscoDevsParamsWithLive(maxDevices,
|
|
authenticated, remembered, unknown, discoverableOnly,
|
|
DateTime.MinValue,
|
|
liveDiscoHandler, liveDiscoState);
|
|
AsyncResult_BeginDiscoverDevices ar = new AsyncResult_BeginDiscoverDevices(
|
|
callback, state, args);
|
|
System.Threading.ThreadPool.QueueUserWorkItem(BeginDiscoverDevices_Runner, ar);
|
|
return ar;
|
|
}
|
|
|
|
internal class DiscoDevsParamsWithLive : DiscoDevsParams
|
|
{
|
|
internal readonly InTheHand.Net.Sockets.BluetoothClient.LiveDiscoveryCallback _liveDiscoHandler;
|
|
internal readonly object _liveDiscoState;
|
|
|
|
public DiscoDevsParamsWithLive(int maxDevices,
|
|
bool authenticated, bool remembered, bool unknown, bool discoverableOnly,
|
|
DateTime discoTime,
|
|
InTheHand.Net.Sockets.BluetoothClient.LiveDiscoveryCallback liveDiscoHandler, object liveDiscoState)
|
|
: base(maxDevices,
|
|
authenticated, remembered, unknown, discoverableOnly,
|
|
discoTime)
|
|
{
|
|
_liveDiscoHandler = liveDiscoHandler;
|
|
_liveDiscoState = liveDiscoState;
|
|
}
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
|
|
void BeginDiscoverDevices_Runner(object state)
|
|
{
|
|
AsyncResult_BeginDiscoverDevices ar = (AsyncResult_BeginDiscoverDevices)state;
|
|
ar.SetAsCompletedWithResultOf(() => DiscoverDevices(ar.BeginParameters.maxDevices,
|
|
ar.BeginParameters.authenticated, ar.BeginParameters.remembered,
|
|
ar.BeginParameters.unknown, ar.BeginParameters.discoverableOnly,
|
|
ar.BeginParameters._liveDiscoHandler, ar.BeginParameters._liveDiscoState), false);
|
|
}
|
|
|
|
IBluetoothDeviceInfo[] IBluetoothClient.EndDiscoverDevices(IAsyncResult asyncResult)
|
|
{
|
|
AsyncResult_BeginDiscoverDevices ar = (AsyncResult_BeginDiscoverDevices)asyncResult;
|
|
return ar.EndInvoke();
|
|
}
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
|
|
#region Active
|
|
private bool active = false;
|
|
|
|
/// <summary>
|
|
/// Gets or set a value that indicates whether a connection has been made.
|
|
/// </summary>
|
|
protected bool Active
|
|
{
|
|
get
|
|
{
|
|
return active;
|
|
}
|
|
set
|
|
{
|
|
active = value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Available
|
|
public int Available
|
|
{
|
|
get
|
|
{
|
|
EnsureNotDisposed();
|
|
return clientSocket.Available;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Client
|
|
|
|
private Socket clientSocket;
|
|
|
|
public Socket Client
|
|
{
|
|
get { return clientSocket; }
|
|
set { this.clientSocket = value; }
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Connect
|
|
/// <summary>
|
|
/// Connects a client to a specified endpoint.
|
|
/// </summary>
|
|
/// <param name="remoteEP">A <see cref="BluetoothEndPoint"/> that represents the remote device.</param>
|
|
public virtual void Connect(BluetoothEndPoint remoteEP)
|
|
{
|
|
EnsureNotDisposed();
|
|
if (remoteEP == null) {
|
|
throw new ArgumentNullException("remoteEP");
|
|
}
|
|
|
|
Connect_StartAuthenticator(remoteEP);
|
|
try {
|
|
var connEP = PrepareConnectEndPoint(remoteEP);
|
|
clientSocket.Connect(connEP);
|
|
active = true;
|
|
} finally {
|
|
Connect_StopAuthenticator();
|
|
}
|
|
}
|
|
|
|
#region Begin Connect
|
|
/// <summary>
|
|
/// Begins an asynchronous request for a remote host connection.
|
|
/// The remote host is specified by a <see cref="BluetoothEndPoint"/>.
|
|
/// </summary>
|
|
/// <param name="remoteEP">A <see cref="BluetoothEndPoint"/> containing the
|
|
/// address and UUID of the remote service.</param>
|
|
/// <param name="requestCallback">An AsyncCallback delegate that references the method to invoke when the operation is complete.</param>
|
|
/// <param name="state">A user-defined object that contains information about the connect operation.
|
|
/// This object is passed to the requestCallback delegate when the operation is complete.</param>
|
|
/// <returns></returns>
|
|
public virtual IAsyncResult BeginConnect(BluetoothEndPoint remoteEP, AsyncCallback requestCallback, object state)
|
|
{
|
|
EnsureNotDisposed();
|
|
Connect_StartAuthenticator(remoteEP);
|
|
var connEP = PrepareConnectEndPoint(remoteEP);
|
|
return this.Client.BeginConnect(connEP, requestCallback, state);
|
|
}
|
|
#endregion
|
|
|
|
#region End Connect
|
|
/// <summary>
|
|
/// Asynchronously accepts an incoming connection attempt.
|
|
/// </summary>
|
|
/// <param name="asyncResult">An <see cref="IAsyncResult"/> object returned by a call to
|
|
/// <see cref="M:BeginConnect(InTheHand.Net.Sockets.BluetoothEndPoint,System.AsyncCallback,System.Object)"/>
|
|
/// / <see cref="M:BeginConnect(InTheHand.Net.Sockets.BluetoothAddress,System.Guid,System.AsyncCallback,System.Object)"/>.
|
|
/// </param>
|
|
public virtual void EndConnect(IAsyncResult asyncResult)
|
|
{
|
|
try {
|
|
Socket sock = this.Client;
|
|
if (sock == null) {
|
|
Debug.Assert(cleanedUp, "!cleanedUp");
|
|
throw new ObjectDisposedException("BluetoothClient");
|
|
} else {
|
|
sock.EndConnect(asyncResult);
|
|
}
|
|
this.active = true;
|
|
} finally {
|
|
Connect_StopAuthenticator();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Connected
|
|
public bool Connected
|
|
{
|
|
get
|
|
{
|
|
if (clientSocket == null)
|
|
return false;
|
|
return clientSocket.Connected;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Get Stream
|
|
|
|
private NetworkStream dataStream;
|
|
|
|
protected virtual NetworkStream MakeStream(Socket sock)
|
|
{
|
|
return new NetworkStream(this.Client, true);
|
|
}
|
|
|
|
|
|
public NetworkStream GetStream()
|
|
{
|
|
EnsureNotDisposed();
|
|
if (!this.Client.Connected) {
|
|
throw new InvalidOperationException("The operation is not allowed on non-connected sockets.");
|
|
}
|
|
|
|
if (dataStream == null) {
|
|
dataStream = MakeStream(this.Client);
|
|
}
|
|
|
|
return dataStream;
|
|
}
|
|
|
|
#if TEST_EARLY
|
|
public Stream GetStream2()
|
|
{
|
|
return GetStream();
|
|
}
|
|
#endif
|
|
#endregion
|
|
|
|
public LingerOption LingerState
|
|
{
|
|
#if !NETCF
|
|
get { return Client.LingerState; }
|
|
set { Client.LingerState = value; }
|
|
#else
|
|
get
|
|
{
|
|
return (LingerOption)Client.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger);
|
|
}
|
|
set
|
|
{
|
|
Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, value);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#region Authenticate
|
|
/// <summary>
|
|
/// Gets or sets the authentication state of the current connect or behaviour to use when connection is established.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// For disconnected sockets, specifies that authentication is required in order for a connect or accept operation to complete successfully.
|
|
/// Setting this option actively initiates authentication during connection establishment, if the two Bluetooth devices were not previously authenticated.
|
|
/// The user interface for passkey exchange, if necessary, is provided by the operating system outside the application context.
|
|
/// For outgoing connections that require authentication, the connect operation fails with WSAEACCES if authentication is not successful.
|
|
/// In response, the application may prompt the user to authenticate the two Bluetooth devices before connection.
|
|
/// For incoming connections, the connection is rejected if authentication cannot be established and returns a WSAEHOSTDOWN error.
|
|
/// </remarks>
|
|
public bool Authenticate
|
|
{
|
|
get { return m_optionHelper.Authenticate; }
|
|
set { m_optionHelper.Authenticate = value; }
|
|
}
|
|
#endregion
|
|
|
|
#region Encrypt
|
|
/// <summary>
|
|
/// On unconnected sockets, enforces encryption to establish a connection.
|
|
/// Encryption is only available for authenticated connections.
|
|
/// For incoming connections, a connection for which encryption cannot be established is automatically rejected and returns WSAEHOSTDOWN as the error.
|
|
/// For outgoing connections, the connect function fails with WSAEACCES if encryption cannot be established.
|
|
/// In response, the application may prompt the user to authenticate the two Bluetooth devices before connection.
|
|
/// </summary>
|
|
public bool Encrypt
|
|
{
|
|
get { return m_optionHelper.Encrypt; }
|
|
set { m_optionHelper.Encrypt = value; }
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Link Key
|
|
/// <summary>
|
|
/// Returns link key associated with peer Bluetooth device.
|
|
/// </summary>
|
|
public Guid LinkKey
|
|
{
|
|
get
|
|
{
|
|
EnsureNotDisposed();
|
|
byte[] bytes = new byte[16];
|
|
#if NETCF
|
|
byte[] link = clientSocket.GetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.GetLink, 32);
|
|
Buffer.BlockCopy(link, 16, bytes, 0, 16);
|
|
#endif
|
|
|
|
return new Guid(bytes);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Link Policy
|
|
/// <summary>
|
|
/// Returns the Link Policy of the current connection.
|
|
/// </summary>
|
|
public LinkPolicy LinkPolicy
|
|
{
|
|
get
|
|
{
|
|
EnsureNotDisposed();
|
|
#if NETCF
|
|
byte[] policy = clientSocket.GetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.GetLinkPolicy, 4);
|
|
return (LinkPolicy)BitConverter.ToInt32(policy, 0);
|
|
#else
|
|
return LinkPolicy.Disabled;
|
|
#endif
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Set PIN
|
|
/// <summary>
|
|
/// Sets the PIN associated with the currently connected device.
|
|
/// </summary>
|
|
/// <param name="pin">PIN which must be composed of 1 to 16 ASCII characters.</param>
|
|
/// <remarks>Assigning null (Nothing in VB) or an empty String will revoke the PIN.</remarks>
|
|
public void SetPin(string pin)
|
|
{
|
|
if (!Connected) {
|
|
#if WinXP
|
|
m_pinForConnect = pin;
|
|
#else
|
|
SetPin(null, pin);
|
|
#endif
|
|
} else {
|
|
EndPoint rep = clientSocket.RemoteEndPoint;
|
|
BluetoothAddress addr = null;
|
|
if (rep != null)
|
|
addr = ((BluetoothEndPoint)rep).Address;
|
|
if (addr == null)
|
|
throw new InvalidOperationException(
|
|
"The socket needs to be connected to detect the remote device"
|
|
+ ", use the other SetPin method..");
|
|
SetPin(addr, pin);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set or change the PIN to be used with a specific remote device.
|
|
/// </summary>
|
|
/// <param name="device">Address of Bluetooth device.</param>
|
|
/// <param name="pin">PIN string consisting of 1 to 16 ASCII characters.</param>
|
|
/// <remarks>Assigning null (Nothing in VB) or an empty String will revoke the PIN.</remarks>
|
|
public void SetPin(BluetoothAddress device, string pin)
|
|
{
|
|
m_optionHelper.SetPin(device, pin);
|
|
}
|
|
|
|
private void Connect_StartAuthenticator(BluetoothEndPoint remoteEP)
|
|
{
|
|
#if WinXP
|
|
if (m_pinForConnect != null) {
|
|
SetPin(remoteEP.Address, m_pinForConnect);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void Connect_StopAuthenticator()
|
|
{
|
|
#if WinXP
|
|
if (m_pinForConnect != null) {
|
|
SetPin(null, null);
|
|
}
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region Remote Machine Name
|
|
public BluetoothEndPoint RemoteEndPoint
|
|
{
|
|
get
|
|
{
|
|
EnsureNotDisposed();
|
|
return (BluetoothEndPoint)clientSocket.RemoteEndPoint;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of the remote device.
|
|
/// </summary>
|
|
public string RemoteMachineName
|
|
{
|
|
get
|
|
{
|
|
EnsureNotDisposed();
|
|
return GetRemoteMachineName(clientSocket);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of the specified remote device.
|
|
/// </summary>
|
|
/// <param name="a">Address of remote device.</param>
|
|
/// <returns>Friendly name of specified device.</returns>
|
|
public string GetRemoteMachineName(BluetoothAddress a)
|
|
{
|
|
#if WinXP
|
|
var bdi = _fcty.DoGetBluetoothDeviceInfo(a);
|
|
return bdi.DeviceName;
|
|
#else
|
|
byte[] buffer = new byte[504];
|
|
//copy remote device address to buffer
|
|
Buffer.BlockCopy(a.ToByteArray(), 0, buffer, 0, 6);
|
|
|
|
try
|
|
{
|
|
clientSocket.SetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.ReadRemoteName, buffer);
|
|
string name = string.Empty;
|
|
name = System.Text.Encoding.Unicode.GetString(buffer, 8, 496);
|
|
|
|
int offset = name.IndexOf('\0');
|
|
if(offset > -1)
|
|
{
|
|
name = name.Substring(0, offset);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
catch(SocketException ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine("BluetoothClient GetRemoteMachineName(addr) ReadRemoteName failed: " + ex.Message);
|
|
return null;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of a device by a specified socket.
|
|
/// </summary>
|
|
/// <param name="s"> A <see cref="Socket"/>.</param>
|
|
/// <returns>Returns a string value of the computer or device name.</returns>
|
|
public static string GetRemoteMachineName(Socket s)
|
|
{
|
|
#if WinXP
|
|
// HACK was new WindowsBluetoothDeviceInfo
|
|
var bdi = new BluetoothDeviceInfo(((BluetoothEndPoint)s.RemoteEndPoint).Address);
|
|
return bdi.DeviceName;
|
|
#else
|
|
byte[] buffer = new byte[504];
|
|
//copy remote device address to buffer
|
|
Buffer.BlockCopy(((BluetoothEndPoint)s.RemoteEndPoint).Address.ToByteArray(), 0, buffer, 0, 6);
|
|
|
|
string name = "";
|
|
|
|
try
|
|
{
|
|
s.SetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.ReadRemoteName, buffer);
|
|
name = System.Text.Encoding.Unicode.GetString(buffer, 8, 496);
|
|
|
|
int offset = name.IndexOf('\0');
|
|
if(offset > -1)
|
|
{
|
|
name = name.Substring(0, offset);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
catch(SocketException ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine("BluetoothClient GetRemoteMachineName(socket) ReadRemoteName failed: " + ex.Message);
|
|
return null;
|
|
}
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
/// <summary>
|
|
/// Releases the unmanaged resources used by the BluetoothClient and optionally releases the managed resources.
|
|
/// </summary>
|
|
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!cleanedUp) {
|
|
if (disposing) {
|
|
IDisposable idStream = dataStream;
|
|
if (idStream != null) {
|
|
//dispose the stream which will also close the socket
|
|
idStream.Dispose();
|
|
} else {
|
|
if (this.Client != null) {
|
|
this.Client.Close();
|
|
this.Client = null;
|
|
}
|
|
}
|
|
// TODO ??? m_optionHelper.Dispose();
|
|
}
|
|
|
|
cleanedUp = true;
|
|
}
|
|
}
|
|
|
|
private void EnsureNotDisposed()
|
|
{
|
|
Debug.Assert(cleanedUp == (clientSocket == null), "always consistent!! ("
|
|
+ cleanedUp + " != " + (clientSocket == null) + ")");
|
|
if (cleanedUp || (clientSocket == null))
|
|
throw new ObjectDisposedException("BluetoothClient");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes the <see cref="BluetoothClient"/> and the underlying connection.
|
|
/// </summary>
|
|
/// -
|
|
/// <seealso cref="M:InTheHand.Net.Sockets.BluetoothClient.Close"/>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Frees resources used by the <see cref="BluetoothClient"/> class.
|
|
/// </summary>
|
|
~SocketBluetoothClient()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Throw SocketException For HR
|
|
internal static void ThrowSocketExceptionForHR(int errorCode)
|
|
{
|
|
if (errorCode < 0) {
|
|
int socketerror = 0;
|
|
socketerror = Marshal.GetLastWin32Error();
|
|
|
|
throw new SocketException(socketerror);
|
|
}
|
|
}
|
|
|
|
internal static void ThrowSocketExceptionForHrExceptFor(int errorCode, params int[] nonErrorCodes)
|
|
{
|
|
if (errorCode < 0) {
|
|
int socketerror = 0;
|
|
socketerror = Marshal.GetLastWin32Error();
|
|
if (-1 != Array.IndexOf(nonErrorCodes, socketerror, 0, nonErrorCodes.Length)) {
|
|
return;
|
|
}
|
|
throw new SocketException(socketerror);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
internal class MsftSocketOptionHelper : ISocketOptionHelper
|
|
{
|
|
readonly Socket m_socket;
|
|
#if !WinCE
|
|
private bool authenticate = false;
|
|
private BluetoothWin32Authentication m_authenticator;
|
|
#endif
|
|
private bool encrypt = false;
|
|
|
|
internal MsftSocketOptionHelper(Socket socket)
|
|
{
|
|
m_socket = socket;
|
|
}
|
|
|
|
#region Authenticate
|
|
/// <summary>
|
|
/// Gets or sets the authentication state of the current connect or behaviour to use when connection is established.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// For disconnected sockets, specifies that authentication is required in order for a connect or accept operation to complete successfully.
|
|
/// Setting this option actively initiates authentication during connection establishment, if the two Bluetooth devices were not previously authenticated.
|
|
/// The user interface for passkey exchange, if necessary, is provided by the operating system outside the application context.
|
|
/// For outgoing connections that require authentication, the connect operation fails with WSAEACCES if authentication is not successful.
|
|
/// In response, the application may prompt the user to authenticate the two Bluetooth devices before connection.
|
|
/// For incoming connections, the connection is rejected if authentication cannot be established and returns a WSAEHOSTDOWN error.
|
|
/// </remarks>
|
|
public bool Authenticate
|
|
{
|
|
get
|
|
{
|
|
#if NETCF
|
|
byte[] authbytes = m_socket.GetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.GetAuthenticationEnabled, 4);
|
|
int auth = BitConverter.ToInt32(authbytes, 0);
|
|
return (auth==0) ? false : true;
|
|
#else
|
|
return authenticate;
|
|
#endif
|
|
}
|
|
set
|
|
{
|
|
#if NETCF
|
|
m_socket.SetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.SetAuthenticationEnabled, (int)(value ? 1 : 0));
|
|
#else
|
|
m_socket.SetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.Authenticate, value);
|
|
authenticate = value;
|
|
#endif
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Encrypt
|
|
/// <summary>
|
|
/// On unconnected sockets, enforces encryption to establish a connection.
|
|
/// Encryption is only available for authenticated connections.
|
|
/// For incoming connections, a connection for which encryption cannot be established is automatically rejected and returns WSAEHOSTDOWN as the error.
|
|
/// For outgoing connections, the connect function fails with WSAEACCES if encryption cannot be established.
|
|
/// In response, the application may prompt the user to authenticate the two Bluetooth devices before connection.
|
|
/// </summary>
|
|
public bool Encrypt
|
|
{
|
|
get { return encrypt; }
|
|
set
|
|
{
|
|
m_socket.SetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.Encrypt, (int)(value ? 1 : 0));
|
|
encrypt = value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Set Pin
|
|
public void SetPin(BluetoothAddress device, string pin)
|
|
{
|
|
#if WinXP
|
|
if (pin != null) {
|
|
m_authenticator = new BluetoothWin32Authentication(device, pin);
|
|
} else {
|
|
if (m_authenticator != null) {
|
|
m_authenticator.Dispose();
|
|
}
|
|
}
|
|
#else
|
|
byte[] link = new byte[32];
|
|
|
|
//copy remote device address
|
|
if (device != null)
|
|
{
|
|
Buffer.BlockCopy(device.ToByteArray(), 0, link, 8, 6);
|
|
}
|
|
|
|
//copy PIN
|
|
if (pin != null & pin.Length > 0)
|
|
{
|
|
if (pin.Length > 16)
|
|
{
|
|
throw new ArgumentOutOfRangeException("PIN must be between 1 and 16 ASCII characters");
|
|
}
|
|
//copy pin bytes
|
|
byte[] pinbytes = System.Text.Encoding.ASCII.GetBytes(pin);
|
|
Buffer.BlockCopy(pinbytes, 0, link, 16, pin.Length);
|
|
BitConverter.GetBytes(pin.Length).CopyTo(link, 0);
|
|
}
|
|
|
|
m_socket.SetSocketOption(BluetoothSocketOptionLevel.RFComm, BluetoothSocketOptionName.SetPin, link);
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
}//class--SocketOptionHelper
|
|
|
|
}//class--BluetoothClient
|
|
|
|
}
|