using System; using System.Diagnostics; using System.Runtime.InteropServices; using InTheHand.Net.Bluetooth.Msft; using InTheHand.Win32; using System.Diagnostics.CodeAnalysis; namespace InTheHand.Net.Bluetooth { /// /// Provides Bluetooth authentication services on desktop Windows. /// /// - /// /// This class is supported on desktop Windows and with the Microsoft /// stack only. /// /// This class can be used in one of two ways. Firstly /// an instance can be created specifying one device that is being connected /// to and the PIN string to use for it. (That form is used internally by /// to support /// its method). /// /// Secondly it can also be used a mode where a user supplied /// callback will be called when any device requires authentication, /// the callback includes a parameter of type /// . /// Various authentication methods are available in Bluetooth version /// 2.1 and later. Which one is being used is indicated by the /// /// property. /// If it is /// then the callback method should set the /// /// property. /// /// /// For the other authentication methods /// e.g. /// or /// the callback method should use one or more of the other properties and /// methods e.g. /// , /// , /// , /// /// etc. /// /// /// See the example below for a 'Legacy' method handler. /// The callback mode can be configured to do a callback after the /// ‘send PIN’ action, this allows one to see if it was successful /// etc. An example sequence where the PIN was incorrect is as follows. /// /// ///Authenticate one device -- with wrong passcode here the first two times. ///Passcode respectively: 'BAD-x', 'BAD-y', '9876' ///Making PC discoverable ///Hit Return to complete ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 0, Last error code 0 /// Sending "BAD-x" ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 1, Last error code 1244 /// Sending "BAD-y" ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 2, Last error code 1167 /// Sending "9876" ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 3, Last error code 1167 ///etc /// /// /// That is we see the error code of 1244=NativeErrorNotAuthenticated /// once, and then the peer device disappears (1167=NativeErrorDeviceNotConnected). /// I suppose that's a security feature -- its stops an attacker /// from trying again and again with different passcodes. /// /// Anyway the result of that is that is it not worth repeating /// the callback after the device disappears. The code now enforces this. With /// /// set to true, if the result of the previous attempt was ‘success’ /// or ‘device not connected’ then any new PIN set in the callback /// won’t be used and thus the callback won’t be called again /// for that authentication attempt. /// /// A successful authentication process can thus be detected by checking if /// e.PreviousNativeErrorCode == NativeErrorSuccess && e.AttemptNumber != 0 /// /// /// /// The instance will continue receiving authentication requests /// until it is disposed or garbage collected, so keep a reference to it /// whilst it should be active and call /// /// when you’re finished. /// /// /// - /// /// If one wants to respond to PIN requests for one device with a known PIN then /// use the simple form which is initialized with an address and PIN. /// /// BluetoothWin32Authentication authenticator /// = new BluetoothWin32Authentication(remoteEP.Address, m_pin); /// // when the peer is expected to require pairing, perhaps do some work. /// authenticator.Dispose(); /// /// /// If one wants to see the PIN request, perhaps to be able to check the type /// of the peer by its address then use the form here which requests callbacks. /// (Note that this code assumes that 'Legacy' PIN-based pairing is being /// used; setting the Pin property will presumably have no effect if the /// authentication method being used is one of the v2.1 SSP forms). /// /// Using pairer As New BluetoothWin32Authentication(AddressOf Win32AuthCallbackHandler) /// Console.WriteLine("Hit Return to stop authenticating") /// Console.ReadLine() /// End Using /// ... /// /// Sub Win32AuthCallbackHandler(ByVal sender As Object, ByVal e As InTheHand.Net.Bluetooth.BluetoothWin32AuthenticationEventArgs) /// ' Note we assume here that 'Legacy' pairing is being used, /// ' and thus we only set the Pin property! /// Dim address As String = e.Device.DeviceAddress.ToString() /// Console.WriteLine("Received an authentication request from address " + address) /// /// ' compare the first 8 hex numbers, this is just a special case because in the /// ' used scenario the model of the devices can be identified by the first 8 hex /// ' numbers, the last 4 numbers being the device specific part. /// If address.Substring(0, 8).Equals("0099880D") OrElse _ /// address.Substring(0, 8).Equals("0099880E") Then /// ' send authentication response /// e.Pin = "5276" /// ElseIf (address.Substring(0, 8).Equals("00997788")) Then /// ' send authentication response /// e.Pin = "ásdfghjkl" /// End If /// End Sub /// /// //[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, UnmanagedCode = true)] public partial class BluetoothWin32Authentication : IDisposable { /// /// Windows’ ERROR_SUCCESS /// /// /// public const int NativeErrorSuccess = 0; /// /// Windows’ ERROR_NOT_AUTHENTICATED /// /// /// public const int NativeErrorNotAuthenticated = (int)Win32Error.ERROR_NOT_AUTHENTICATED; /// /// Windows’ ERROR_DEVICE_NOT_CONNECTED /// /// /// public const int NativeErrorDeviceNotConnected = (int)Win32Error.ERROR_DEVICE_NOT_CONNECTED; // This class is XP only, but XmlDocs generation is from CF2 only, so we need // to compile on that platform too. So, leave the skeleton with no innards. #if WinXP // bool m_hasKB942567 = true; // "Windows Vista Feature Pack for Wireless" etc. readonly IntPtr m_radioHandle = IntPtr.Zero; BluetoothAuthenticationRegistrationHandle m_regHandle; NativeMethods.BluetoothAuthenticationCallback m_callback; // Stop gc of callback thunk. NativeMethods.BluetoothAuthenticationCallbackEx m_callbackEx; // Stop gc of callback thunk. // readonly BluetoothAddress m_remoteAddress; readonly String m_pin; readonly EventHandler m_userCallback; #endif //-------------------------------------------------------------- /// /// Initializes a new instance of the class. /// /// - /// /// Initializes a new instance of the class, /// to respond to a specific address with a specific PIN string. /// /// - /// /// The instance will continue receiving authentication requests /// until it is disposed or garbage collected, so keep a reference to it /// whilst it should be active, and call /// /// when you’re finished. /// /// /// - /// The address of the device to authenticate, /// as a . /// /// The PIN string to use for authentication, as a /// . /// public BluetoothWin32Authentication(BluetoothAddress remoteAddress, String pin) { #if ! WinXP throw new PlatformNotSupportedException("BluetoothWin32Authentication is Win32 only."); #else if (remoteAddress == null) { throw new ArgumentNullException("remoteAddress"); } if (remoteAddress.ToInt64() == 0) { throw new ArgumentNullException("remoteAddress", "A non-blank address must be specified."); } if (pin == null) { throw new ArgumentNullException("pin"); } m_remoteAddress = remoteAddress; m_pin = pin; Register(remoteAddress); #endif } /// /// Initializes a new instance of the class, /// to call a specified handler when any device requires authentication. /// /// - /// /// See the example below. /// /// The callback mode can be configured to do a callback after the /// ‘send PIN’action, this allows one to see if it was successful /// etc. An example sequence where the PIN was incorrect is as follows. /// /// ///Authenticate one device -- with wrong passcode here the first two times. ///Passcode respectively: 'BAD-x', 'BAD-y', '9876' ///Making PC discoverable ///Hit Return to complete ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 0, Last error code 0 /// Sending "BAD-x" ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 1, Last error code 1244 /// Sending "BAD-y" ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 2, Last error code 1167 /// Sending "9876" ///Authenticating 0017E464CF1E wm_alan1 /// Attempt# 3, Last error code 1167 ///etc /// /// /// That is we see the error code of 1244=NativeErrorNotAuthenticated /// once, and then the peer device disappears (1167=NativeErrorDeviceNotConnected). /// I suppose that's a security feature -- its stops an attacker /// from trying again and again with different passcodes. /// /// Anyway the result of that is that is it not worth repeating /// the callback after the device disappears. The code now enforces this. With /// /// set to true, if the result of the previous attempt was ‘success’ /// or ‘device not connected’ then any new PIN set in the callback /// won’t be used and thus the callback won’t be called again /// for that authentication attempt. /// /// A successful authentication process can thus be detected by setting /// CallbackWithResult=true and checking in the callback if /// e.PreviousNativeErrorCode == NativeErrorSuccess && e.AttemptNumber != 0 /// /// /// /// The instance will continue receiving authentication requests /// until it is disposed or garbage collected, so keep a reference to it /// whilst it should be active, and call /// /// when you’re finished. /// /// /// - /// A reference to a handler function that can respond /// to authentication requests. /// /// - /// /// /// Using pairer As New BluetoothWin32Authentication(AddressOf Win32AuthCallbackHandler) /// Console.WriteLine("Hit Return to stop authenticating") /// Console.ReadLine() /// End Using /// ... /// /// Sub Win32AuthCallbackHandler(ByVal sender As Object, ByVal e As InTheHand.Net.Bluetooth.BluetoothWin32AuthenticationEventArgs) /// Dim address As String = e.Device.DeviceAddress.ToString() /// Console.WriteLine("Received an authentication request from address " + address) /// /// ' compare the first 8 hex numbers, this is just a special case because in the /// ' used scenario the model of the devices can be identified by the first 8 hex /// ' numbers, the last 4 numbers being the device specific part. /// If address.Substring(0, 8).Equals("0099880D") OrElse _ /// address.Substring(0, 8).Equals("0099880E") Then /// ' send authentication response /// e.Pin = "5276" /// ElseIf (address.Substring(0, 8).Equals("00997788")) Then /// ' send authentication response /// e.Pin = "ásdfghjkl" /// End If /// End Sub /// /// public BluetoothWin32Authentication(EventHandler handler) { #if ! WinXP throw new PlatformNotSupportedException("BluetoothWin32Authentication is Win32 only."); #else m_userCallback = handler; Register(new BluetoothAddress(0)); // All devices #endif } #if WinXP private void Register(BluetoothAddress remoteAddress) { System.Diagnostics.Debug.Assert(m_pin == null ^ m_userCallback == null); // m_callback = new NativeMethods.BluetoothAuthenticationCallback(NativeCallback); m_callbackEx = new NativeMethods.BluetoothAuthenticationCallbackEx(NativeCallback); BLUETOOTH_DEVICE_INFO bdi = new BLUETOOTH_DEVICE_INFO(remoteAddress); UInt32 ret; if (m_hasKB942567) { try { ret = NativeMethods.BluetoothRegisterForAuthenticationEx( ref bdi, out m_regHandle, m_callbackEx, IntPtr.Zero); } catch (EntryPointNotFoundException) { m_hasKB942567 = false; ret = NativeMethods.BluetoothRegisterForAuthentication( ref bdi, out m_regHandle, m_callback, IntPtr.Zero); } } else { ret = NativeMethods.BluetoothRegisterForAuthentication( ref bdi, out m_regHandle, m_callback, IntPtr.Zero); } int gle = Marshal.GetLastWin32Error(); System.Diagnostics.Debug.Assert(ret == NativeErrorSuccess, "BluetoothRegisterForAuthentication failed, GLE=" + gle.ToString() + "=0x" + gle.ToString("X")); if (ret != NativeErrorSuccess) { throw new System.ComponentModel.Win32Exception(gle); } m_regHandle.SetObjectToKeepAlive(m_callback, m_callbackEx); } //-------------------------------------------------------------- private bool NativeCallback(IntPtr param, ref BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams) { Debug.WriteLine("BtRegForAuthEx Callback:"); Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "authParams: addr: 0x{0:X12},\n meth: '{1}'\n capa: '{2}'\n requ: '{3}'\n n/pk: 0x{4:X}={4}", pAuthCallbackParams.deviceInfo.Address, pAuthCallbackParams.authenticationMethod, pAuthCallbackParams.ioCapability, pAuthCallbackParams.authenticationRequirements, pAuthCallbackParams.Numeric_Value_Passkey)); //if (pAuthCallbackParams.authenticationMethod // == BluetoothAuthenticationMethod.Legacy) { BLUETOOTH_DEVICE_INFO bdi = pAuthCallbackParams.deviceInfo; BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS? refParams = pAuthCallbackParams; NativeCallback(pAuthCallbackParams.authenticationMethod, param, ref bdi, true, ref refParams); //} return false; } private bool NativeCallback(IntPtr param, ref BLUETOOTH_DEVICE_INFO bdi) { Debug.WriteLine("BtRegForAuth[NO-Ex] Callback."); BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS? nullRefParams = null; return NativeCallback(BluetoothAuthenticationMethod.Legacy, param, ref bdi, false, ref nullRefParams); } [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "param")] private bool NativeCallback(BluetoothAuthenticationMethod method, IntPtr param, ref BLUETOOTH_DEVICE_INFO bdi, bool versionEx, ref BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS? pAuthCallbackParams) { System.Diagnostics.Debug.Assert(m_pin == null ^ m_userCallback == null); // System.Diagnostics.Debug.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, "AuthenticateResponder callback (for {0}): 0x{1:X} 0x{2:X}", m_remoteAddress, param, bdi.Address)); // String pin; Int32 ret; if (m_pin != null) { // Pre-specified case. System.Diagnostics.Debug.Assert(bdi.Address == m_remoteAddress.ToInt64(), "Should only get callback for the single device."); //TODO if (bdi.Address != m_remoteAddress.ToInt64()) { // return false; //} pin = m_pin; if (versionEx) { // TODO Want to send a positive response here to NumericComparison?? ret = BluetoothSendAuthenticationResponseExPin(ref bdi, pin); } else { ret = NativeMethods.BluetoothSendAuthenticationResponse( m_radioHandle, ref bdi, pin); } } else if (method == BluetoothAuthenticationMethod.Legacy) { // Callback case. System.Diagnostics.Debug.Assert(m_userCallback != null); BluetoothWin32AuthenticationEventArgs e = new BluetoothWin32AuthenticationEventArgs(bdi); while (true) { // Callback the user code OnAuthentication(e); // Don't proceed if no (null) passcode given, or // if the last attempt was successful, or // the decvice has disppeared. if (e.Pin == null) { ret = NativeErrorSuccess; break; } if (e.PreviousNativeErrorCode == NativeErrorSuccess && e.AttemptNumber != 0) { Debug.Assert(e.CallbackWithResult, "NOT CbWR but here (A#)!!"); ret = NativeErrorSuccess; break; } if (e.PreviousNativeErrorCode == NativeErrorDeviceNotConnected) { Debug.Assert(e.CallbackWithResult, "NOT CbWR but here (DNC)!!"); // When I try this (against Win2k+Belkin and iPaq hx2190, // both apparently with Broadcom) I see: //[[ //Authenticate one device -- with wrong passcode here the first two times. //Passcode respectively: 'BAD-x', 'BAD-y', '9876' //Making PC discoverable //Hit Return to complete //Authenticating 0017E464CF1E wm_alan1 // Attempt# 0, Last error code 0 //Using '0.23672947484847' //Authenticating 0017E464CF1E wm_alan1 // Attempt# 1, Last error code 1244 //Using '0.54782851764365' //Authenticating 0017E464CF1E wm_alan1 // Attempt# 2, Last error code 1167 //Using '9876' //Authenticating 0017E464CF1E wm_alan1 // Attempt# 3, Last error code 1167 //etc //]] // That is we see the error code of 1244=ErrorNotAuthenticated // once, and then the peer device disappears (1167=ErrorDeviceNotConnected). // I suppose that's a security feature -- its stops an attacker // from trying again and again with different passcodes. // // Anyway the result of that is that is it NOT worth repeating // the callback after the device disappears. ret = NativeErrorSuccess; break; } pin = e.Pin; System.Diagnostics.Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "BW32Auth SendAuthRsp pin {0}", pin)); if (versionEx) { ret = BluetoothSendAuthenticationResponseExPin(ref bdi, pin); } else { ret = NativeMethods.BluetoothSendAuthenticationResponse( m_radioHandle, ref bdi, pin); } if (ret != NativeErrorSuccess) { System.Diagnostics.Trace.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, " BluetoothSendAuthenticationResponse failed: {0}=0x{0:X}", ret)); } // Have to callback the user code after the attempt? BluetoothWin32AuthenticationEventArgs lastEa = e; if (!lastEa.CallbackWithResult) { break; } e = new BluetoothWin32AuthenticationEventArgs(ret, lastEa); } } else if (method == BluetoothAuthenticationMethod.NumericComparison || method == BluetoothAuthenticationMethod.Passkey || method == BluetoothAuthenticationMethod.PasskeyNotification || method == BluetoothAuthenticationMethod.OutOfBand ) { // Callback case. System.Diagnostics.Debug.Assert(m_userCallback != null); BluetoothWin32AuthenticationEventArgs e = new BluetoothWin32AuthenticationEventArgs(bdi, ref pAuthCallbackParams); while (true) { // Callback the user code OnAuthentication(e); // Check if after e.CallbackWithResult... if (e.PreviousNativeErrorCode == NativeErrorSuccess && e.AttemptNumber != 0) { Debug.Assert(e.CallbackWithResult, "NOT CbWR but here (A#)!!"); ret = NativeErrorSuccess; break; } if (e.PreviousNativeErrorCode == NativeErrorDeviceNotConnected) { Debug.Assert(e.CallbackWithResult, "NOT CbWR but here (DNC)!!"); ret = NativeErrorSuccess; break; } bool? confirm = e.Confirm; System.Diagnostics.Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "BW32Auth SendAuthRspEx-NumComparison {0}", confirm)); if (confirm == null) { ret = NativeErrorSuccess; break; } if (method != BluetoothAuthenticationMethod.OutOfBand) { ret = BluetoothSendAuthenticationResponseExNumCompPasskey(ref bdi, confirm, e); } else { ret = BluetoothSendAuthenticationResponseExOob(ref bdi, confirm, e); } if (ret != NativeErrorSuccess) { System.Diagnostics.Trace.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, " BluetoothSendAuthenticationResponseEx failed: {0}=0x{0:X}", ret)); } // Have to callback the user code after the attempt? BluetoothWin32AuthenticationEventArgs lastEa = e; if (!lastEa.CallbackWithResult) { break; } e = new BluetoothWin32AuthenticationEventArgs(ret, lastEa); } } else { Debug.Fail("Unsupported auth method: " + method); ret = NativeErrorSuccess; } // if (ret != NativeErrorSuccess) { System.Diagnostics.Trace.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, "BluetoothSendAuthenticationResponse failed: {0}=0x{0:X}", ret)); } return true; // "The return value from this function is ignored by the system." } private int BluetoothSendAuthenticationResponseExPin(ref BLUETOOTH_DEVICE_INFO bdi, String pin) { Int32 ret; BLUETOOTH_AUTHENTICATE_RESPONSE__PIN_INFO rsp = new BLUETOOTH_AUTHENTICATE_RESPONSE__PIN_INFO(); rsp.authMethod = BluetoothAuthenticationMethod.Legacy; rsp.bthAddressRemote = bdi.Address; rsp.pinInfo.pin = new byte[BLUETOOTH_PIN_INFO.BTH_MAX_PIN_SIZE]; byte[] buf = System.Text.Encoding.UTF8.GetBytes(pin); int len = Math.Min(BLUETOOTH_PIN_INFO.BTH_MAX_PIN_SIZE, buf.Length); Array.Copy(buf, rsp.pinInfo.pin, len); rsp.pinInfo.pinLength = len; ret = NativeMethods.BluetoothSendAuthenticationResponseEx( m_radioHandle, ref rsp); return ret; } private int BluetoothSendAuthenticationResponseExNumCompPasskey(ref BLUETOOTH_DEVICE_INFO bdi, bool? confirm, BluetoothWin32AuthenticationEventArgs e) { if (!confirm.HasValue) { return NativeErrorSuccess; } Int32 ret; var rsp = new BLUETOOTH_AUTHENTICATE_RESPONSE__NUMERIC_COMPARISON_PASSKEY_INFO(); rsp.negativeResponse = 1; // Default to NEGATIVE, really set below. rsp.authMethod = e.AuthenticationMethod; if (!(e.AuthenticationMethod == BluetoothAuthenticationMethod.NumericComparison || e.AuthenticationMethod == BluetoothAuthenticationMethod.Passkey || e.AuthenticationMethod == BluetoothAuthenticationMethod.PasskeyNotification)) { Debug.Fail("Bad call!!! method is: " + e.AuthenticationMethod); return NativeErrorNotAuthenticated; } rsp.bthAddressRemote = bdi.Address; switch (confirm) { case true: rsp.negativeResponse = 0; // Set the response number/passcode value if (e.ResponseNumberOrPasskey.HasValue) { rsp.numericComp_passkey = checked((uint)e.ResponseNumberOrPasskey.Value); } break; case false: case null: Debug.Assert(confirm != null, "Should have exited above when non-response."); rsp.negativeResponse = 1; break; } ret = NativeMethods.BluetoothSendAuthenticationResponseEx( m_radioHandle, ref rsp); return ret; } private int BluetoothSendAuthenticationResponseExOob(ref BLUETOOTH_DEVICE_INFO bdi, bool? confirm, BluetoothWin32AuthenticationEventArgs e) { if (!confirm.HasValue) { return NativeErrorSuccess; } Int32 ret; var rsp = new BLUETOOTH_AUTHENTICATE_RESPONSE__OOB_DATA_INFO(); rsp.negativeResponse = 1; // Default to NEGATIVE, really set below. rsp.authMethod = e.AuthenticationMethod; if (!(e.AuthenticationMethod == BluetoothAuthenticationMethod.OutOfBand)) { Debug.Fail("Bad call!!! method is: " + e.AuthenticationMethod); return NativeErrorNotAuthenticated; } rsp.bthAddressRemote = bdi.Address; switch (confirm) { case true: rsp.negativeResponse = 0; // Set the oob values // (Testing shows that P/Invoke disallowed only incorrect // lengthinline arrays, null arrays are ok). if (e.OobC != null) rsp.oobInfo.C = e.OobC; if (e.OobR != null) rsp.oobInfo.R = e.OobR; break; case false: case null: Debug.Assert(confirm != null, "Should have exited above when non-response."); rsp.negativeResponse = 1; break; } ret = NativeMethods.BluetoothSendAuthenticationResponseEx( m_radioHandle, ref rsp); return ret; } /// /// Calls the authentication callback handler. /// /// - /// An instance of /// containing the details of the authentication callback. /// protected virtual void OnAuthentication(BluetoothWin32AuthenticationEventArgs e) { EventHandler callback = m_userCallback; if (callback != null) { m_userCallback(this, e); } } #endif //-------------------------------------------------------------- #region IDisposable Members /// /// Release the unmanaged resources used by the . /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Release the unmanaged resources used by the , /// and optionally disposes of the managed resources. /// protected virtual void Dispose(bool disposing) { #if WinXP if (disposing) { if (m_regHandle != null) { m_regHandle.Dispose(); } } #endif } #endregion }//class #if WinXP // Or should we derive from Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid? sealed internal class BluetoothAuthenticationRegistrationHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { object m_objectToKeepAlive, m_objectToKeepAlive2; #if DEBUG WeakReference m_weakRef, m_weakRef2; #endif public BluetoothAuthenticationRegistrationHandle() : base(true) { } protected override bool ReleaseHandle() { #if false // was: DEBUG -- fails in Finalizer case IIRC System.Diagnostics.Debug.Assert((m_weakRef == null) == (m_objectToKeepAlive == null), "Only one of the two objects set."); if (m_weakRef != null) { bool isAlive = m_weakRef.IsAlive; try { System.Diagnostics.Debug.Assert(isAlive || AppDomain.CurrentDomain.IsFinalizingForUnload(), "m_weakRef.IsAlive"); } catch (ObjectDisposedException) { } // SafeHandle->CriticalFinalizer } #endif bool success = NativeMethods.BluetoothUnregisterAuthentication(handle); int gle = Marshal.GetLastWin32Error(); System.Diagnostics.Debug.Assert(success, "BluetoothUnregisterAuthentication returned false, GLE=" + gle.ToString() + "=0x" + gle.ToString("X")); return success; } internal void SetObjectToKeepAlive(object objectToKeepAlive, object objectToKeepAlive2) { m_objectToKeepAlive = objectToKeepAlive; m_objectToKeepAlive2 = objectToKeepAlive2; #if DEBUG m_weakRef = new WeakReference(m_objectToKeepAlive); m_weakRef2 = new WeakReference(m_objectToKeepAlive); #endif } }//class #endif }