Windows-rs: Obtaining IMMDeviceEnumerator from MMDeviceEnumerator

Created on 1 Feb 2021  路  8Comments  路  Source: microsoft/windows-rs

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.

question

Most helpful comment

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.

All 8 comments

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 __uuidof operator, 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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ZCWorks picture ZCWorks  路  3Comments

rylev picture rylev  路  4Comments

kennykerr picture kennykerr  路  5Comments

rylev picture rylev  路  5Comments

06393993 picture 06393993  路  3Comments