Why IAudioEndpointVolume::SetMute doesn't work with C# Interop?

Issue

I am trying to call Windows Core Audio IAudioEndpointVolume::SetMute from C# using Interop.

Using a C++ implementation, it works fine. But when I try to do the same calls from a C# app, nothing changes.
What is weird with the C# app is that if I call SetMute nothing happens but if I call SetMasterVolumeLevelScalar, it effectively changes the volume.

The implementation I have is this :

using System;
using System.Runtime.InteropServices;

namespace App
{
    [ComImport]
    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IMMDeviceEnumerator
    {
        void _VtblGap1_1();
        int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice ppDevice);
    }

    [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IMMDevice
    {
        int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
        int OpenPropertyStore([MarshalAs(UnmanagedType.U4)] UInt32 accessMode, [MarshalAs(UnmanagedType.Interface)] out object properties);
        int GetId([MarshalAs(UnmanagedType.LPWStr)] out string strId);
    }

    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IAudioEndpointVolume
    {
        int RegisterControlChangeNotify(IntPtr pNotify);
        int UnregisterControlChangeNotify(IntPtr pNotify);
        int GetChannelCount(ref uint pnChannelCount);
        int SetMasterVolumeLevel(float fLevelDB, Guid pguidEventContext);
        int SetMasterVolumeLevelScalar(float fLevel, Guid pguidEventContext);
        int GetMasterVolumeLevel(ref float pfLevelDB);
        int GetMasterVolumeLevelScalar(ref float pfLevel);
        int GetChannelVolumeLevel(ref float pfLevelDB);
        int GetChannelVolumeLevelScalar(ref float pfLevel);
        int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, Guid pguidEventContext);
        int GetMute([MarshalAs(UnmanagedType.Bool)] ref bool pbMute);
        int GetVolumeStepInfo(out UInt32 step, out UInt32 stepCount);
        int VolumeStepUp([MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        int VolumeStepDown([MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
        int QueryHardwareSupport(out UInt32 hardwareSupportMask);
        int GetVolumeRange(out float volumeMin, out float volumeMax, out float volumeStep);
    }

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")));
                IMMDevice speakers;
                const int eRender = 0;
                const int eMultimedia = 1;
                deviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, out speakers);

                string id;
                speakers.GetId(out id);
                object aepv_obj;
                speakers.Activate(typeof(IAudioEndpointVolume).GUID, 1, IntPtr.Zero, out aepv_obj);
                IAudioEndpointVolume aepv = (IAudioEndpointVolume)aepv_obj;

                var zeroGuid = new Guid();
                int res = aepv.SetMute(true, zeroGuid);
                Console.WriteLine($"Got {res}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"**Error** {ex.Message}");
            }
        }
    }

Is there something wrong with the Interop implementation ?

Thanks

Solution

I tried hard and eventually succeded, these "_VtblGaps_" are the key… Just copy-paste all into a new C# script and use from your program script:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace MMDeviceAPI
{
public class MMDeviceController
{
    [ComImport]
    [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
    private class MMDeviceEnumerator
    {
    }

    [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDeviceEnumerator
    {
        void _VtblGap1_1();
        //void _VtblGap1_5(); /// (Alternative)
        [PreserveSig]
        int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice ppDevice);
    }
    [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IMMDevice
    {
        [PreserveSig]
        int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
        void _VtblGap1_1();
        int GetId([MarshalAs(UnmanagedType.LPWStr)] out string strId);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IAudioEndpointVolume
    {
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IGetChannelCount : IAudioEndpointVolume
    {
        void _VtblGap1_2();
        int GetChannelCount(ref uint pnChannelCount);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IGetChannelVolumeLevelScalar : IAudioEndpointVolume
    {
        void _VtblGap1_10();
        int GetChannelVolumeLevelScalar(uint nChannel, ref float pfLevel);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IGetMasterVolumeLevelScalar : IAudioEndpointVolume
    {
        void _VtblGap1_6();
        int GetMasterVolumeLevelScalar(ref float pfLevel);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface IGetMute : IAudioEndpointVolume
    {
        void _VtblGap1_12();
        int GetMute([MarshalAs(UnmanagedType.Bool)] ref bool pbMute);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface ISetChannelVolumeLevelScalar : IAudioEndpointVolume
    {
        void _VtblGap1_8();
        int SetChannelVolumeLevelScalar(uint nChannel, float fLevel, [MarshalAs(UnmanagedType.LPStruct)] Guid pguidEventContext);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface ISetMasterVolumeLevelScalar : IAudioEndpointVolume
    {
        void _VtblGap1_4();
        int SetMasterVolumeLevelScalar(float fLevel, [MarshalAs(UnmanagedType.LPStruct)] Guid pguidEventContext);
    }
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    private interface ISetMute : IAudioEndpointVolume
    {
        void _VtblGap1_11();
        int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, [MarshalAs(UnmanagedType.LPStruct)] Guid pguidEventContext);
    }

    public enum EDataFlow { eRender, eCapture, eAll };
    public enum ERole { eConsole, eMultimedia, eCommunications };

    ///====================================================================================================

    private static IAudioEndpointVolume GetMMDeviceAudioEndpointVolume(EDataFlow dataFlow, ERole role)
    {
        IMMDeviceEnumerator MMDeviceEnumerator = (IMMDeviceEnumerator)(new MMDeviceEnumerator());
        MMDeviceEnumerator.GetDefaultAudioEndpoint(dataFlow, role, out IMMDevice MMDevice);
        MMDevice.Activate(typeof(IAudioEndpointVolume).GUID, 0, IntPtr.Zero, out object o);
        IAudioEndpointVolume AudioEndpointVolume = (IAudioEndpointVolume)o;

        Marshal.ReleaseComObject(MMDevice);
        Marshal.ReleaseComObject(MMDeviceEnumerator);

        return AudioEndpointVolume;
    }

    public static uint GetChannelCount(EDataFlow dataFlow, ERole role)
    {
        IGetChannelCount AudioEndpointVolume = (IGetChannelCount)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        uint returnValue = 0;
        AudioEndpointVolume.GetChannelCount(ref returnValue);

        Marshal.ReleaseComObject(AudioEndpointVolume);
        return returnValue;
    }

    public static float GetChannelVolumeLevelScalar(EDataFlow dataFlow, ERole role, uint channel)
    {
        IGetChannelVolumeLevelScalar AudioEndpointVolume = (IGetChannelVolumeLevelScalar)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        float returnValue = 0;
        AudioEndpointVolume.GetChannelVolumeLevelScalar(channel, ref returnValue);

        Marshal.ReleaseComObject(AudioEndpointVolume);
        return returnValue;
    }

    public static float GetMasterVolumeLevelScalar(EDataFlow dataFlow, ERole role)
    {
        IGetMasterVolumeLevelScalar AudioEndpointVolume = (IGetMasterVolumeLevelScalar)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        float returnValue = 0f;
        AudioEndpointVolume.GetMasterVolumeLevelScalar(ref returnValue);

        Marshal.ReleaseComObject(AudioEndpointVolume);
        return returnValue;
    }

    public static bool GetMute(EDataFlow dataFlow, ERole role)
    {
        IGetMute AudioEndpointVolume = (IGetMute)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        bool returnValue = false;
        AudioEndpointVolume.GetMute(ref returnValue);

        Marshal.ReleaseComObject(AudioEndpointVolume);
        return returnValue;
    }

    public static void SetChannelVolumeLevelScalar(EDataFlow dataFlow, ERole role, uint channel, float volume)
    {
        ISetChannelVolumeLevelScalar AudioEndpointVolume = (ISetChannelVolumeLevelScalar)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        AudioEndpointVolume.SetChannelVolumeLevelScalar(channel, volume, Guid.Empty);

        Marshal.ReleaseComObject(AudioEndpointVolume);
    }

    public static void SetMasterVolumeLevelScalar(EDataFlow dataFlow, ERole role, float volume)
    {
        ISetMasterVolumeLevelScalar AudioEndpointVolume = (ISetMasterVolumeLevelScalar)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        AudioEndpointVolume.SetMasterVolumeLevelScalar(volume, Guid.Empty);

        Marshal.ReleaseComObject(AudioEndpointVolume);
    }

    public static void SetMute(EDataFlow dataFlow, ERole role, bool mute)
    {
        ISetMute AudioEndpointVolume = (ISetMute)GetMMDeviceAudioEndpointVolume(dataFlow, role);

        AudioEndpointVolume.SetMute(mute, Guid.Empty);

        Marshal.ReleaseComObject(AudioEndpointVolume);
    }

    ///====================================================================================================

    public static void TestFunctionality()
    {
        int device = 0;
        EDataFlow dataFlow;
        ERole eRole;

        while (true)
        {
            device++;

            switch (device)
            {
                case 1:
                dataFlow = EDataFlow.eRender;
                eRole = ERole.eMultimedia;
                break;
                case 2:
                dataFlow = EDataFlow.eCapture;
                eRole = ERole.eMultimedia;
                break;
                default:
                return;
            }

            MessageBox.Show($"[EDataFlow & ERole]:  {dataFlow.ToString()}_{eRole.ToString()}");
            uint channels = GetChannelCount(dataFlow, eRole); MessageBox.Show("[GetChannelCount]:  " + channels);
            MessageBox.Show("[GetChannelVolumeLevelScalar #0]:  " + GetChannelVolumeLevelScalar(dataFlow, eRole, 0).ToString());
            if (channels > 1) { MessageBox.Show("[GetChannelVolumeLevelScalar #1]:  " + GetChannelVolumeLevelScalar(dataFlow, eRole, 1).ToString()); }
            MessageBox.Show("[GetMasterVolumeLevelScalar]:  " + GetMasterVolumeLevelScalar(dataFlow, eRole).ToString());
            MessageBox.Show("[GetMute]:  " + GetMute(dataFlow, eRole).ToString());
            MessageBox.Show("[SetChannelVolumeLevelScalar #0]:  0.44"); SetChannelVolumeLevelScalar(dataFlow, eRole, 0, 0.44f);
            if (channels > 1) { MessageBox.Show("[SetChannelVolumeLevelScalar #1]:  0.88"); SetChannelVolumeLevelScalar(dataFlow, eRole, 1, 0.88f); }
            MessageBox.Show("[SetMasterVolumeLevelScalar]:  0.55"); SetMasterVolumeLevelScalar(dataFlow, eRole, 0.55f);
            MessageBox.Show("[SetMute]:  false"); SetMute(dataFlow, eRole, false);
        }
    }

    ///====================================================================================================
}
}

Answered By – Aerrixus

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published