From 673d298633b61e7764ad28eaac93fa36041ac58f Mon Sep 17 00:00:00 2001 From: Alexis Couvreur Date: Tue, 17 Mar 2026 14:15:11 -0400 Subject: [PATCH] feat(windows): add error code from async operation --- adapter_windows.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 2 ++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/adapter_windows.go b/adapter_windows.go index 726a45e0..e5df9952 100644 --- a/adapter_windows.go +++ b/adapter_windows.go @@ -3,6 +3,8 @@ package bluetooth import ( "errors" "fmt" + "syscall" + "unsafe" "github.com/go-ole/go-ole" "github.com/saltosystems/winrt-go" @@ -55,11 +57,57 @@ func awaitAsyncOperation(asyncOperation *foundation.IAsyncOperation, genericPara <-waitChan if status != foundation.AsyncStatusCompleted { + if err := getAsyncError(asyncOperation); err != nil { + return fmt.Errorf("async operation failed with status %d: %w", status, err) + } return fmt.Errorf("async operation failed with status %d", status) } return nil } +// getAsyncError queries IAsyncInfo from an IAsyncOperation to retrieve +// the error code of a failed async operation. If the HRESULT corresponds +// to a Bluetooth ATT error (facility 0x65), it returns an AttributeProtocolError. +func getAsyncError(asyncOperation *foundation.IAsyncOperation) error { + iid := ole.NewGUID(foundation.GUIDIAsyncInfo) + + var asyncInfo *foundation.IAsyncInfo + hr, _, _ := syscall.SyscallN( + asyncOperation.VTable().QueryInterface, + uintptr(unsafe.Pointer(asyncOperation)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&asyncInfo)), + ) + if hr != 0 { + return nil + } + defer asyncInfo.Release() + + result, err := asyncInfo.GetErrorCode() + if err != nil { + return err + } + if result.Value == 0 { + return nil + } + + return hresultToError(uint32(result.Value)) +} + +// hresultToError converts an HRESULT to an appropriate error type. +// For Bluetooth ATT errors (facility 0x65), it returns an error with the ATT error code. +// For other errors, it returns a generic error with the HRESULT value. +func hresultToError(hr uint32) error { + facility := (hr >> 16) & 0x1FFF + code := hr & 0xFFFF + + if facility == 0x65 { // FACILITY_BLUETOOTH_ATT + return fmt.Errorf("Bluetooth ATT error (code 0x%04X)", code) + } + + return fmt.Errorf("HRESULT 0x%08X", hr) +} + func (a *Adapter) Address() (MACAddress, error) { // TODO: get mac address return MACAddress{}, errors.New("not implemented") diff --git a/go.mod b/go.mod index 6b79071d..cffcd88c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.1 require ( github.com/go-ole/go-ole v1.2.6 github.com/godbus/dbus/v5 v5.1.0 - github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b + github.com/saltosystems/winrt-go v0.0.0-20260317170058-9c2fec580d96 github.com/soypat/cyw43439 v0.0.0-20250505012923-830110c8f4af github.com/tinygo-org/cbgo v0.0.4 golang.org/x/crypto v0.12.0 diff --git a/go.sum b/go.sum index b7eeec2d..84dd9ab3 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b h1:du3zG5fd8snsFN6RBoLA7fpaYV9ZQIsyH9snlk2Zvik= github.com/saltosystems/winrt-go v0.0.0-20240509164145-4f7860a3bd2b/go.mod h1:CIltaIm7qaANUIvzr0Vmz71lmQMAIbGJ7cvgzX7FMfA= +github.com/saltosystems/winrt-go v0.0.0-20260317170058-9c2fec580d96 h1:IXxzj3yjfDNXZJ35foY+RpFShqPsZZ81hhCckgfh5PI= +github.com/saltosystems/winrt-go v0.0.0-20260317170058-9c2fec580d96/go.mod h1:CIltaIm7qaANUIvzr0Vmz71lmQMAIbGJ7cvgzX7FMfA= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=