Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use BluetoothLEAdvertisement::SetFlags API #2310

Closed
anayw2001 opened this issue Jan 26, 2023 · 13 comments
Closed

How to use BluetoothLEAdvertisement::SetFlags API #2310

anayw2001 opened this issue Jan 26, 2023 · 13 comments
Labels
question Further information is requested

Comments

@anayw2001
Copy link

Hi,
I've been trying to figure out how to use the SetFlags API for creating a General Discoverability BluetoothLEAdvertisement, but I cannot figure out how to correctly type the parameter so the conversions are completed automatically.

I have tried:
BluetoothLEAdvertisementFlags::GeneralDiscoverabilityMode()
&BluetoothLEAdvertisementFlags::GeneralDiscoverabilityMode().into()
(&BluetoothLEAdvertisementFlags::GeneralDiscoverabilityMode()).into()

Would be much appreciated if I could receive some guidance on how to create the required InParam<IReference> type.

@kennykerr
Copy link
Collaborator

Can you share a minimal repro? Sorry, I'm not familiar with this API.

Generally, when an API expects a IReference<T> parameter it means it's a nullable value type. So, it can accept either a null pointer or a boxed value (it's a C# thing). I don't yet provide a convenient way to call such APIs in Rust, but it should map to an Option<T> in Rust (#292). If you're desperate, you can implement IReference<T> with the implement macro but I should be able to convert such methods to expect an Option<T> directly.

@kennykerr
Copy link
Collaborator

For completeness, here's what the metadata looks like:

image

The T? syntax is C# syntax but ends up as IReference<T> on the ABI, which is what you get in Rust.

@kennykerr kennykerr added the enhancement New feature or request label Jan 26, 2023
@anayw2001
Copy link
Author

anayw2001 commented Jan 26, 2023

Would it end up looking something like in Rust then?

Screenshot 2023-01-26 at 5 45 44 PM

I still get

error[E0277]: the trait bound `InParam<IReference<BluetoothLEAdvertisementFlags>>: From<std::option::Option<BluetoothLEAdvertisementFlags>>` is not satisfied                                                                            
   --> presence\np_cli\src\platform\windows.rs:51:23                                                                                                                                                                                     
    |                                                                                                                                                                                                                                    
51  |             .SetFlags(Some(BluetoothLEAdvertisementFlags::GeneralDiscoverableMode))                                                                                                                                                
    |              -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<std::option::Option<BluetoothLEAdvertisementFlags>>` is not implemented for `InParam<IReference<BluetoothLEAdvertisementFlags>>`
    |              |                                                                                                                                                                                                                     
    |              required by a bound introduced by this call                                                                                                                                                                           
    |
    = help: the following other types implement trait `From<T>`:
              <InParam<PCSTR> as From<PCSTR>>
              <InParam<PCWSTR> as From<&HSTRING>>
              <InParam<PCWSTR> as From<PCWSTR>>
              <InParam<T> as From<std::option::Option<&'a T>>>
              <InParam<U> as From<&'a T>>
              <InParam<bool> as From<bool>>
              <InParam<f32> as From<f32>>
              <InParam<f64> as From<f64>>
            and 10 others
    = note: required for `std::option::Option<BluetoothLEAdvertisementFlags>` to implement `Into<InParam<IReference<BluetoothLEAdvertisementFlags>>>`
    = note: required for `InParam<IReference<BluetoothLEAdvertisementFlags>>` to implement `TryFrom<std::option::Option<BluetoothLEAdvertisementFlags>>`
note: required by a bound in `BluetoothLEAdvertisement::SetFlags`
   --> C:\Users\awadhera\.cargo\registry\src\github.com-1ecc6299db9ec823\windows-0.44.0\src\Windows\Devices\Bluetooth\Advertisement\mod.rs:503:139
    |
503 |         P0: ::std::convert::TryInto<::windows::core::InParam<super::super::super::Foundation::IReference<BluetoothLEAdvertisementFlags>>, Error = E0>,
    |                                                                                                                                           ^^^^^^^^^^ required by this bound in `BluetoothLEAdvertisement::SetFlags`

@kennykerr
Copy link
Collaborator

Yes, when the code gen changes are done you can just use None or Some(value) for such parameters. Until then, you need to provide an IReference<T> implementation yourself, but that's hard. Thanks for the reminder - I'll try to get to this shortly.

@anayw2001
Copy link
Author

Gotcha, thanks! How would I go about providing an IReference<T> implementation for now until the code gen changes are done?

@kennykerr
Copy link
Collaborator

As I said, this should all be automatic but for the moment here's what you'll have to do to implement IReference<T> yourself:

[dependencies.windows]
version = "0.44"
features = [
    "Devices_Bluetooth_Advertisement",
    "Foundation", 
    "implement", 
]
use windows::{core::*, Devices::Bluetooth::Advertisement::*, Foundation::*};

fn main() -> Result<()> {
    let blue = BluetoothLEAdvertisement::new()?;

    // Clear flags
    blue.SetFlags(None)?;

    // Read cleared flags
    assert!(blue.Flags().unwrap_err().code().is_ok());

    // Set boxed flags
    blue.SetFlags(&Reference::box_value(
        BluetoothLEAdvertisementFlags::LimitedDiscoverableMode,
    ))?;

    // Read unboxed flags
    assert!(blue.Flags()?.Value()? == BluetoothLEAdvertisementFlags::LimitedDiscoverableMode);

    Ok(())
}

#[implement(IReference<T>)]
struct Reference<T>(T)
where
    T: RuntimeType;

impl<T: RuntimeType> Reference<T> {
    fn box_value(value: T) -> IReference<T> {
        Self(value).into()
    }
}

impl<T: RuntimeType + 'static> IReference_Impl<T> for Reference<T> {
    fn Value(&self) -> Result<T> {
        Ok(self.0.clone())
    }
}

impl<T: RuntimeType + 'static> IPropertyValue_Impl for Reference<T> {
    fn Type(&self) -> Result<PropertyType> {
        todo!()
    }
    fn IsNumericScalar(&self) -> Result<bool> {
        todo!()
    }
    fn GetUInt8(&self) -> Result<u8> {
        todo!()
    }
    fn GetRect(&self) -> Result<Rect> {
        todo!()
    }
    fn GetUInt8Array(&self, _: &mut Array<u8>) -> Result<()> {
        todo!()
    }
    fn GetInt16Array(&self, _: &mut Array<i16>) -> Result<()> {
        todo!()
    }
    fn GetUInt16Array(&self, _: &mut Array<u16>) -> Result<()> {
        todo!()
    }
    fn GetInt32Array(&self, _: &mut Array<i32>) -> Result<()> {
        todo!()
    }
    fn GetUInt32Array(&self, _: &mut Array<u32>) -> Result<()> {
        todo!()
    }
    fn GetInt64Array(&self, _: &mut Array<i64>) -> Result<()> {
        todo!()
    }
    fn GetUInt64Array(&self, _: &mut Array<u64>) -> Result<()> {
        todo!()
    }
    fn GetSingleArray(&self, _: &mut Array<f32>) -> Result<()> {
        todo!()
    }
    fn GetDoubleArray(&self, _: &mut Array<f64>) -> Result<()> {
        todo!()
    }
    fn GetChar16Array(&self, _: &mut Array<u16>) -> Result<()> {
        todo!()
    }
    fn GetBooleanArray(&self, _: &mut Array<bool>) -> Result<()> {
        todo!()
    }
    fn GetStringArray(&self, _: &mut Array<HSTRING>) -> Result<()> {
        todo!()
    }
    fn GetInspectableArray(&self, _: &mut Array<IInspectable>) -> Result<()> {
        todo!()
    }
    fn GetGuidArray(&self, _: &mut Array<GUID>) -> Result<()> {
        todo!()
    }
    fn GetDateTimeArray(&self, _: &mut Array<DateTime>) -> Result<()> {
        todo!()
    }
    fn GetTimeSpanArray(&self, _: &mut Array<TimeSpan>) -> Result<()> {
        todo!()
    }
    fn GetPointArray(&self, _: &mut Array<Point>) -> Result<()> {
        todo!()
    }
    fn GetSizeArray(&self, _: &mut Array<Size>) -> Result<()> {
        todo!()
    }
    fn GetRectArray(&self, _: &mut Array<Rect>) -> Result<()> {
        todo!()
    }
    fn GetInt16(&self) -> Result<i16> {
        todo!()
    }
    fn GetUInt16(&self) -> Result<u16> {
        todo!()
    }
    fn GetInt32(&self) -> Result<i32> {
        todo!()
    }
    fn GetUInt32(&self) -> Result<u32> {
        todo!()
    }
    fn GetInt64(&self) -> Result<i64> {
        todo!()
    }
    fn GetUInt64(&self) -> Result<u64> {
        todo!()
    }
    fn GetSingle(&self) -> Result<f32> {
        todo!()
    }
    fn GetDouble(&self) -> Result<f64> {
        todo!()
    }
    fn GetChar16(&self) -> Result<u16> {
        todo!()
    }
    fn GetBoolean(&self) -> Result<bool> {
        todo!()
    }
    fn GetString(&self) -> Result<HSTRING> {
        todo!()
    }
    fn GetGuid(&self) -> Result<GUID> {
        todo!()
    }
    fn GetDateTime(&self) -> Result<DateTime> {
        todo!()
    }
    fn GetTimeSpan(&self) -> Result<TimeSpan> {
        todo!()
    }
    fn GetPoint(&self) -> Result<Point> {
        todo!()
    }
    fn GetSize(&self) -> Result<Size> {
        todo!()
    }
}

@anayw2001
Copy link
Author

Awesome, thank you so much! Will close this issue as #292 seems to be the main issue.

@anayw2001
Copy link
Author

Reopening as use of this API results in

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { code: HRESULT(0x80070057), message: "The parameter is incorrect." }', src\main.rs:23:23

main.rs:

fn advertise() {
    let advertisement = BluetoothLEAdvertisement::new().unwrap();
    advertisement.SetFlags(&reference_impl::Reference::box_value(BluetoothLEAdvertisementFlags::GeneralDiscoverableMode)).unwrap();
    let sections = advertisement.DataSections().unwrap();
    let new_section = BluetoothLEAdvertisementDataSection::new().unwrap();
    new_section.SetDataType(BluetoothLEAdvertisementDataTypes::ServiceData16BitUuids().unwrap()).unwrap();
    let data_writer =  DataWriter::new().unwrap();
    data_writer.WriteUInt16(0xF1FCu16).unwrap();
    data_writer.WriteBytes(b"hello").unwrap();
    new_section.SetData(&data_writer.DetachBuffer().unwrap()).unwrap();
    println!("# of data sections: {}", sections.Size().unwrap());
    advertisement.DataSections().unwrap().Append(&new_section).unwrap();
    println!("# of data sections: {}", sections.Size().unwrap());
    let publisher = BluetoothLEAdvertisementPublisher::Create(&advertisement).unwrap();
    publisher.Start().unwrap(); --> L23
    println!("Started advertising {:?}!", advertisement);
    sleep(Duration::from_secs(20));
    publisher.Stop().unwrap();
    println!("Stopped advertising {:?}!", advertisement);
}

@anayw2001 anayw2001 reopened this Jan 28, 2023
@anayw2001
Copy link
Author

Same issue with the SetLocalName API

        advertisement
            .SetLocalName(&if let Some(local_name) = name {
                HSTRING::from(local_name)
            } else {
                HSTRING::default()
            })
            .unwrap()

@anayw2001
Copy link
Author

edit: Just checked the docs and it seems it's a system-reserved API so should maybe be removed instead.

@kennykerr
Copy link
Collaborator

The minimal, reproducible example I provided #2310 (comment) works without error, at least on my machine. If you think you've found an issue with windows-rs then please provide a minimal, reproducible example. Otherwise, if you have questions about how to use the API you're better off heading over to https://stackoverflow.com/ as I don't have any particular insights on how this API is meant to work in practice.

@kennykerr kennykerr added question Further information is requested and removed enhancement New feature or request labels Jan 28, 2023
@anayw2001
Copy link
Author

anayw2001 commented Jan 28, 2023

Gotcha, the problem is still reproducible, I just added

    let publisher = BluetoothLEAdvertisementPublisher::Create(&blue)?;
    publisher.Start()?;

so it looks like:

fn main() -> Result<()> {
    let blue = BluetoothLEAdvertisement::new()?;

    // Clear flags
    blue.SetFlags(None)?;

    // Read cleared flags
    assert!(blue.Flags().unwrap_err().code().is_ok());

    // Set boxed flags
    blue.SetFlags(&Reference::box_value(
        BluetoothLEAdvertisementFlags::LimitedDiscoverableMode,
    ))?;

    // Read unboxed flags
    assert!(blue.Flags()?.Value()? == BluetoothLEAdvertisementFlags::LimitedDiscoverableMode);

    let publisher = BluetoothLEAdvertisementPublisher::Create(&blue)?;
    publisher.Start()?;

    Ok(())
}

to your code snippet above and it errors out with
Error: Error { code: HRESULT(0x80070057), message: "The parameter is incorrect." }

@riverar
Copy link
Collaborator

riverar commented Jan 28, 2023

Hi @anayw2001, not seeing any issues here either. This is very likely related to your Bluetooth software or hardware stack--try updating your drivers perhaps.

We can't help with Bluetooth issues here. I recommend posting on Microsoft Q&A--there are engineers that answer questions there. Specifically, the UWP area/tag seems apt. (https://learn.microsoft.com/en-us/answers/tags/105/windows-uwp)

Collecting Bluetooth logs may help you gain additional insights too (https://github.com/Microsoft/busiotools/tree/master/bluetooth/tracing).

Closing as this is unrelated to the Rust crate.

@riverar riverar closed this as not planned Won't fix, can't repro, duplicate, stale Jan 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants