// 32feet.NET - Personal Area Networking for .NET
//
// InTheHand.Net.Bluetooth.WindowsBluetoothRadio
//
// Copyright (c) 2003-2010 In The Hand Ltd, All rights reserved.
// This source code is licensed under the In The Hand Community License - see License.txt
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Win32;
using InTheHand.Net.Bluetooth.Factory;
#if WinXP
namespace InTheHand.Net.Bluetooth{
sealed class WindowsBluetoothRadio : IBluetoothRadio //: IDisposable
{
public string Remote { get { return null; } }
#region IsSupported
/// <summary>
/// Gets a value that indicates whether the 32feet.NET library can be used with the current device.
/// </summary>
internal static bool IsPlatformSupported
{
get
{
return (AllRadios.Length > 0);
}
}
#endregion
private BLUETOOTH_RADIO_INFO radio;
private IntPtr handle;
#region Constructor
internal WindowsBluetoothRadio(IntPtr handle)
{
radio = new BLUETOOTH_RADIO_INFO();
radio.dwSize = 520;
System.Diagnostics.Debug.Assert(System.Runtime.InteropServices.Marshal.SizeOf(radio) == radio.dwSize, "BLUETOOTH_RADIO_INFO SizeOf == dwSize");
int hresult = NativeMethods.BluetoothGetRadioInfo(handle, ref radio);
if(hresult!=0)
{
throw new System.ComponentModel.Win32Exception(hresult, "Error retrieving Radio information.");
}
this.handle = handle;
}
#endregion
#region Primary Radio
internal static IBluetoothRadio PrimaryRadio
{
get
{
//get a single radio
IntPtr handle = IntPtr.Zero;
IntPtr findhandle = IntPtr.Zero;
BLUETOOTH_FIND_RADIO_PARAMS bfrp;
bfrp.dwSize = 4;
System.Diagnostics.Debug.Assert(System.Runtime.InteropServices.Marshal.SizeOf(bfrp) == bfrp.dwSize, "BLUETOOTH_FIND_RADIO_PARAMS SizeOf == dwSize");
findhandle = NativeMethods.BluetoothFindFirstRadio(ref bfrp, ref handle);
if (findhandle != IntPtr.Zero)
{
NativeMethods.BluetoothFindRadioClose(findhandle);
}
if (handle != IntPtr.Zero)
{
return new WindowsBluetoothRadio(handle);
}
return null;
}
}
#endregion
#region All Radios
internal static IBluetoothRadio[] AllRadios
{
get
{
IntPtr handle = IntPtr.Zero;
IntPtr findhandle = IntPtr.Zero;
BLUETOOTH_FIND_RADIO_PARAMS bfrp;
bfrp.dwSize = 4;
System.Diagnostics.Debug.Assert(System.Runtime.InteropServices.Marshal.SizeOf(bfrp) == bfrp.dwSize, "BLUETOOTH_FIND_RADIO_PARAMS SizeOf == dwSize");
List<IntPtr> radiocollection = new List<IntPtr>();
findhandle = NativeMethods.BluetoothFindFirstRadio(ref bfrp, ref handle);
if(findhandle!=IntPtr.Zero)
{
//add first handle
radiocollection.Add(handle);
while(NativeMethods.BluetoothFindNextRadio(findhandle, ref handle))
{
//add subsequent handle
radiocollection.Add(handle);
}
//close findhandle
NativeMethods.BluetoothFindRadioClose(findhandle);
}
//populate results array
IBluetoothRadio[] results = new IBluetoothRadio[radiocollection.Count];
for(int radioindex = 0; radioindex < results.Length; radioindex++)
{
results[radioindex] = new WindowsBluetoothRadio(radiocollection[radioindex]);
}
return results;
}
}
#endregion
#region Handle
public IntPtr Handle
{
get
{
return this.handle;
}
}
#endregion
#region Hardware Status
public HardwareStatus HardwareStatus
{
get
{
return HardwareStatus.Unknown;
}
}
#endregion
#region Mode
public RadioMode Mode
{
get
{
if (NativeMethods.BluetoothIsDiscoverable(handle))
{
return RadioMode.Discoverable;
}
if (NativeMethods.BluetoothIsConnectable(handle))
{
return RadioMode.Connectable;
}
return RadioMode.PowerOff;
}
set
{
switch (value)
{
case RadioMode.Discoverable:
if (Mode == RadioMode.PowerOff)
{
NativeMethods.BluetoothEnableIncomingConnections(handle, true);
}
NativeMethods.BluetoothEnableDiscovery(handle, true);
break;
case RadioMode.Connectable:
if (Mode == RadioMode.Discoverable)
{
NativeMethods.BluetoothEnableDiscovery(handle, false);
}
else
{
NativeMethods.BluetoothEnableIncomingConnections(handle, true);
}
break;
case RadioMode.PowerOff:
if (Mode == RadioMode.Discoverable)
{
NativeMethods.BluetoothEnableDiscovery(handle, false);
}
NativeMethods.BluetoothEnableIncomingConnections(handle, false);
break;
}
}
}
#endregion
#region Local Address
public BluetoothAddress LocalAddress
{
get
{
return new BluetoothAddress(radio.address);
}
}
#endregion
#region Name
public string Name
{
get
{
return radio.szName;
}
set
{
// Based on code submission by Todd M Stafney
// figure out what subkey in the registry the name lives in
string regKey = GetRadioRegKey(this.LocalAddress);
if (null == regKey) return; // should only get this if there are no active BT devices?
string fullRegKey = String.Format("SYSTEM\\CurrentControlSet\\Enum\\{0}\\Device Parameters", regKey);
// now try and slam dunk our new name in there - this requires the app to be running as Administrator or equivalent
System.Security.Permissions.RegistryPermission rp = new System.Security.Permissions.RegistryPermission(System.Security.Permissions.PermissionState.Unrestricted);
rp.Demand();
Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(fullRegKey, true);
if (null == rk) return; // ??? this shouldn't happen!
// let's provide a handy way to "reset" the name...
if (null == value) value = System.Environment.MachineName;
// the name is stored as a binary... of ascii chars...
System.Collections.Generic.List<byte> bits = new System.Collections.Generic.List<byte>(System.Text.ASCIIEncoding.ASCII.GetBytes(value));
// but we need to null terminate him
bits.Add(0);
// do it...
rk.SetValue("Local Name", bits.ToArray(), Microsoft.Win32.RegistryValueKind.Binary);
rk.Close();
// This check decides what IO control code to use based on if we're in XP or Vista.
// The XP one I had thanks to the Mark article. But that wasn't valid on Vista. So?
// I had to unpack these CTL_CODE generated values from the old driver kit (thank you
// dusty old CD's in my office) and then figure out what it was and then go searching
// through the new driver kit to match it up. Ok so for those of you following along at home
// 0x411008 in Vista CTL_CODE() parlance is:
// DeviceType == 0x41 == FILE_DEVICE_BLUETOOTH
// Access == 0x00 == FILE_ANY_ACCESS
// Function == 0x402 == some sort of device control function
// Method == 0x00 == METHOD_BUFFERED
uint ctlCode = (uint)(6 > System.Environment.OSVersion.Version.Major ? 0x220fd4 : 0x411008);
long foo = 4; // tells the control function to reset or reload or similar...
int bytes = 0; // merely a placeholder
if (!NativeMethods.DeviceIoControl(this.Handle, ctlCode, ref foo, 4, IntPtr.Zero, 0, ref bytes, IntPtr.Zero))
{
throw new Win32Exception();
}
}
}
private static string GetRadioRegKey(InTheHand.Net.BluetoothAddress radioAddress)
{
Guid btguid = NativeMethods.GUID_DEVCLASS_BLUETOOTH;
IntPtr hDevInfo = NativeMethods.SetupDiGetClassDevs(ref btguid, null, IntPtr.Zero, NativeMethods.DIGCF.PRESENT | NativeMethods.DIGCF.PROFILE);
if (IntPtr.Zero.Equals(hDevInfo)) throw new Win32Exception();
try
{
NativeMethods.SP_DEVINFO_DATA data = new NativeMethods.SP_DEVINFO_DATA();
// On 32bit platforms, all SetupApi structures are 1-Byte packed.
// On 64bit platforms the SetupApi structures are 8-byte packed.
// i.e. for 32 bit SP_DEVINFO_DATA.cbSize=28, for 64Bit SP_DEVINFO_DATA.cbSize=(28+4)=32.
data.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(data);
uint idx = 0;
while (NativeMethods.SetupDiEnumDeviceInfo(hDevInfo, idx++, ref data))
{
// grab the instance id
string instanceId = DevInstanceId(hDevInfo, data);
//the last part of the instanceId will be the unique bluetooth address of the radio
int lastSlashIndex = instanceId.LastIndexOf("\\");
BluetoothAddress address;
InTheHand.Net.BluetoothAddress.TryParse(instanceId.Substring(lastSlashIndex + 1, instanceId.Length - lastSlashIndex - 1), out address);
//InTheHand.Net.BluetoothAddress address = InTheHand.Net.BluetoothAddress.Parse(instanceId.Substring(lastSlashIndex + 1, instanceId.Length - lastSlashIndex - 1));
if (address != null && radioAddress == address)
{
return instanceId;
}
}
}
finally
{
// make sure we clean up after ourselves!
NativeMethods.SetupDiDestroyDeviceInfoList(hDevInfo);
}
// hmmm, we didn't find any BT radios?
return null;
}
private static string DevInstanceId(IntPtr hDevInfo, NativeMethods.SP_DEVINFO_DATA data)
{
int rc = NativeMethods.ERROR_SUCCESS;
int requiredSize = 0;
System.Text.StringBuilder sb = null;
// we call twice, first to get the required buffer size...
for (int ii = 0; ii < 2; ii++)
{
rc = NativeMethods.ERROR_SUCCESS;
if (!NativeMethods.SetupDiGetDeviceInstanceId(hDevInfo, ref data, sb, requiredSize, out requiredSize))
{
rc = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (rc != NativeMethods.ERROR_INSUFFICIENT_BUFFER) break;
sb = new System.Text.StringBuilder(requiredSize + 2);
}
}
if (NativeMethods.ERROR_SUCCESS != rc) throw new Win32Exception(rc);
return sb.ToString();
}
#endregion
#region Class Of Device
public ClassOfDevice ClassOfDevice
{
get
{
return new ClassOfDevice(radio.ulClassofDevice);
}
}
#endregion
#region Manufacturer
public Manufacturer Manufacturer
{
get
{
return radio.manufacturer;
}
}
#endregion
#region Lmp Subversion
public int LmpSubversion
{
get
{
return radio.lmpSubversion;
}
}
#endregion
#region Stack
public Manufacturer SoftwareManufacturer
{
get
{
return Manufacturer.Microsoft;
}
}
#endregion
/*private static NativeMethods.BluetoothMessageFilter bmf = new NativeMethods.BluetoothMessageFilter();
public event EventHandler RadioInRange
{
add
{
NativeMethods.DEV_BROADCAST_HANDLE dbh = new NativeMethods.DEV_BROADCAST_HANDLE();
dbh.dbch_size = System.Runtime.InteropServices.Marshal.SizeOf(dbh);
dbh.dbch_devicetype = NativeMethods.DBT_DEVTYP_HANDLE;
dbh.dbch_handle = this.handle;
dbh.dbch_eventguid = NativeMethods.GUID_BLUETOOTH_RADIO_IN_RANGE;
System.Windows.Forms.Application.AddMessageFilter(bmf);
int result = NativeMethods.RegisterDeviceNotification(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle, ref dbh, 0);
}
remove
{
}
}*/
}
}
#endif
|