I'm trying to port some C# code using the Core Audio Library to Rust, specifically, this StackOverflow answer. I can't figure out how to obtain an IMMDeviceEnumerator. This is the line from the original source code:
IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
I can construct an MMDeviceEnumerator with Default::default(), but I don't know how to cast it to the interface type. I've searched the documentation for a way to convert them, but there is no function that accepts a MMDeviceEnumerator or returns an IMMDeviceEnumerator, and there is no trait implemented by either type that would satisfy e.g. a generic function to convert from one type to the other.
The documentation for this API indicates that you must create the object using the CoCreateInstance function.
https://docs.microsoft.com/en-us/windows/win32/coreaudio/mmdevice-api
You can use this same function from Rust. Here's a comprehensive COM example that illustrates this:
https://github.com/kennykerr/samples-rs/tree/master/clock
Separately, I will probably add a wrapper for CoCreateInstance to the windows crate directly as this is a common pattern. That should make this a lot easier.
I was just about to say the same as @kennykerr . This is an example of how you could use it:
Example
Build:
fn main() {
windows::build!(
windows::win32::core_audio::{MMDeviceEnumerator, IMMDeviceEnumerator},
windows::win32::com::{CoCreateInstance, CoInitialize, CLSCTX},
);
}
Main:
use bindings::windows::win32::core_audio::{MMDeviceEnumerator, IMMDeviceEnumerator, EDataFlow};
use bindings::windows::{IUnknown, Interface};
use bindings::windows::win32::com::{CoCreateInstance, CoInitialize, CLSCTX};
use std::ptr;
const DEVICE_STATE_ACTIVE: u32 = 0x1;
fn main() {
let hr = unsafe { CoInitialize(ptr::null_mut()) };
println!("CoInitialize: {:?}", hr);
let mut enumerator: Option<IMMDeviceEnumerator> = None;
let hr = unsafe {
CoCreateInstance(
&MMDeviceEnumerator,
None,
(CLSCTX::CLSCTX_INPROC_SERVER.0 | CLSCTX::CLSCTX_LOCAL_SERVER.0 | CLSCTX::CLSCTX_REMOTE_SERVER.0) as u32,
&IMMDeviceEnumerator::IID,
&mut enumerator as *mut _ as _)
};
println!("CoCreateInstance: {:?}", hr);
if let Some(enumerator) = enumerator {
let mut devices = None;
let hr = enumerator.EnumAudioEndpoints(EDataFlow::eAll, DEVICE_STATE_ACTIVE, &mut devices);
println!("EnumAudioEndpoints: {:?}", hr);
if let Some(devices) = devices {
let mut count = 0;
let hr = devices.GetCount(&mut count);
println!("GetCount: {:?}", hr);
println!("Found {:?} active devices.", count);
}
}
}
I'm using the windows-rs verison form GitHub. I beleive the MMDeviceEnumerator is different in the 0.2.1 version. You only need to pass the GUID to CoCreateInstance.
@Nerixyz thanks for sharing an example!
After #496 you will be able to simplify this further.
Thank you for the example - a follow-up question though, where can I find the class ID for MMDeviceEnumerator? I've searched for a bit - a C++ example that I found over in microsoft docs uses the __uuidof operator, but I didn't see that in the Rust bindings, if it can even be bound. And there is no CLSID associated constant like the IID for interfaces.
that I found over in microsoft docs uses the
__uuidofoperator, but I didn't see that in the Rust bindings
In the Rust bindings you'd use ::IID. But for some reason, the Guid for MMDeviceEnumerator isn't emitted at all. Only if you use the latest version from GitHub, the MMDeviceEnumerator is a Guid.
If you use the 0.2.1 version, you have to use Guid::from("bcde0395-e52f-467c-8e3d-c4579291692e") as the Guid.
::IID is used for interface identifier guids of the particular interface they are attached to while class identifiers (a.k.a. CLSIDs) are just stand alone constants. Indeed, MMDeviceEnumerator was not part of the metadata when version 0.2.1 was released. We need to release a new version, but until then you can just use the crate directly from GitHub which should include MMDeviceEnumerator.
Most helpful comment
The documentation for this API indicates that you must create the object using the
CoCreateInstancefunction.https://docs.microsoft.com/en-us/windows/win32/coreaudio/mmdevice-api
You can use this same function from Rust. Here's a comprehensive COM example that illustrates this:
https://github.com/kennykerr/samples-rs/tree/master/clock
Separately, I will probably add a wrapper for
CoCreateInstanceto thewindowscrate directly as this is a common pattern. That should make this a lot easier.