Click here to Skip to main content
15,887,676 members
Articles / Programming Languages / Visual Basic
Tip/Trick

Getting and Setting Windows System Audio Volume

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
4 Apr 2023CPOL 5.7K   1   1
Demonstrates two functions to retrieve the current Windows System Audio Volume and set Audio Volume.
Get or Set the Windows System audio volume with a simple method call.

Introduction

Currently, there are no .NET classes that provide methods to retrieve or set the system output volume. Using interfaces to the Windows Multi-Media Device APIs, this article provides a solution.

Background

I got the idea for this article from this web page. I refined it a bit so that it can be dropped into your VB .NET or C# source code and used immediately.

Using the Code

To retrieve the current system audio volume setting value as an integer (0 - 100), call the GetVolume() function.

VB.NET
Dim currentVolume As Integer = GetVolume()
C#
int currentVolume = SystemAudio.WindowsSystemAudio.GetVolume();

To set the current system audio volume setting to a new value (0 - 100), call the SetVolume() function.

VB.NET
SetVolume(30)
C#
SystemAudio.WindowsSystemAudio.SetVolume(30);

The following is the source code for the VB.NET and C# methods that call the Windows Multi-Media APIs to either get or set the Windows System audio volume.

VB.NET
Option Strict Off
Imports System
Imports System.Runtime.InteropServices

Module WindowsSystemAudio
    <ComImport>
    <Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), _
     InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    Private Interface IMMDeviceEnumerator
#Disable Warning IDE1006 ' Naming Styles
        Sub lpVtbl()
#Enable Warning IDE1006 ' Naming Styles
        Function GetDefaultAudioEndpoint(ByVal dataFlow As Integer, _
        ByVal role As Integer, <Out> ByRef ppDevice As IMMDevice) As Integer
    End Interface

    Private NotInheritable Class MMDeviceEnumeratorFactory
        Public Shared Function CreateInstance() As IMMDeviceEnumerator
            Return CType(Activator.CreateInstance_
            (Type.GetTypeFromCLSID(New Guid_
            ("BCDE0395-E52F-467C-8E3D-C4579291692E"))), _
            IMMDeviceEnumerator) ' a MMDeviceEnumerator
        End Function
    End Class

    <Guid("D666063F-1587-4E43-81F1-B948E807363F"), _
     InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    Private Interface IMMDevice
        Function Activate(
    <MarshalAs(UnmanagedType.LPStruct)> ByVal iid As Guid, _
     ByVal dwClsCtx As Integer, ByVal pActivationParams As IntPtr, <Out>
    <MarshalAs(UnmanagedType.IUnknown)> ByRef ppInterface As Object) As Integer
    End Interface

    <Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), _
     InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
    Public Interface IAudioEndpointVolume
        Function RegisterControlChangeNotify(ByVal pNotify As IntPtr) As Integer
        Function UnregisterControlChangeNotify(ByVal pNotify As IntPtr) As Integer
        Function GetChannelCount(ByRef pnChannelCount As UInteger) As Integer
        Function SetMasterVolumeLevel(ByVal fLevelDB As Single, _
        ByVal pguidEventContext As Guid) As Integer
        Function SetMasterVolumeLevelScalar_
        (ByVal fLevel As Single, ByVal pguidEventContext As Guid) As Integer
        Function GetMasterVolumeLevel(ByRef pfLevelDB As Single) As Integer
        Function GetMasterVolumeLevelScalar(ByRef pfLevel As Single) As Integer
    End Interface

    Friend Sub SetVolume(ByVal Level As Integer)
        Try
            Dim deviceEnumerator As IMMDeviceEnumerator = _
                                 MMDeviceEnumeratorFactory.CreateInstance()
            Dim speakers As IMMDevice = Nothing
            Dim res As Integer
            Const eRender = 0
            Const eMultimedia = 1
            deviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, speakers)
            Dim Audio_EndPointVolume As IAudioEndpointVolume = Nothing
            speakers.Activate(GetType(IAudioEndpointVolume).GUID, 0, _
                              IntPtr.Zero, Audio_EndPointVolume)
            Dim ZeroGuid As New Guid()
            res = Audio_EndPointVolume.SetMasterVolumeLevelScalar_
                  (Level / 100.0F, ZeroGuid)
        Catch ex As Exception
        End Try
    End Sub

    Friend Function GetVolume() As Integer
        Try
            Dim currentLevel As Single = 0 ' Expressed as a decimal value
            Dim deviceEnumerator As IMMDeviceEnumerator = _
                                 MMDeviceEnumeratorFactory.CreateInstance()
            Dim speakers As IMMDevice = Nothing
            Dim res As Integer
            Const eRender = 0
            Const eMultimedia = 1
            deviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, speakers)
            Dim Audio_EndPointVolume As IAudioEndpointVolume = Nothing
            speakers.Activate(GetType(IAudioEndpointVolume).GUID, _
                              0, IntPtr.Zero, Audio_EndPointVolume)
            res = Audio_EndPointVolume.GetMasterVolumeLevelScalar(currentLevel)
            Return CInt(100 * currentLevel)  ' Returned as an Integer 0 - 100
        Catch ex As Exception
            Return -1
        End Try
    End Function
End Module 
C#
using System;
using System.Runtime.InteropServices;

namespace SystemAudio
{
    internal static class WindowsSystemAudio
    {
        [ComImport]
        [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), 
               InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IMMDeviceEnumerator
        {
            void Vtbl();
            int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice ppDevice);
        }
        private static class MMDeviceEnumeratorFactory
        {
            public static IMMDeviceEnumerator CreateInstance()
            {
                return (IMMDeviceEnumerator)Activator.CreateInstance
                (Type.GetTypeFromCLSID(new Guid
                ("BCDE0395-E52F-467C-8E3D-C4579291692E")));
            }
        }
        [Guid("D666063F-1587-4E43-81F1-B948E807363F"), 
               InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IMMDevice
        {
            int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, 
                          int dwClsCtx, IntPtr pActivationParams, 
                          [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
        }

        [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);
        }

        internal static void SetVolume(int level)
        {
            try
            {
                IMMDeviceEnumerator deviceEnumerator = 
                                    MMDeviceEnumeratorFactory.CreateInstance();
                IMMDevice speakers;
                const int eRender = 0;
                const int eMultimedia = 1;
                deviceEnumerator.GetDefaultAudioEndpoint
                                 (eRender, eMultimedia, out speakers);
                object aepv_obj;
                speakers.Activate(typeof(IAudioEndpointVolume).GUID, 
                                  0, IntPtr.Zero, out aepv_obj);
                IAudioEndpointVolume aepv = (IAudioEndpointVolume)aepv_obj;
                Guid ZeroGuid = new();
                int res = aepv.SetMasterVolumeLevelScalar(level / 100f, ZeroGuid);
            }
            catch (Exception ex) 
            { 
            }
        }

        internal static int GetVolume()
        {
            try
            {
                float currentLevel = 0f;
                IMMDeviceEnumerator deviceEnumerator = 
                         MMDeviceEnumeratorFactory.CreateInstance();
                IMMDevice speakers;
                const int eRender = 0;
                const int eMultimedia = 1;
                deviceEnumerator.GetDefaultAudioEndpoint
                                 (eRender, eMultimedia, out speakers);
                object aepv_obj;
                speakers.Activate(typeof(IAudioEndpointVolume).GUID, 
                                  0, IntPtr.Zero, out aepv_obj);
                IAudioEndpointVolume aepv = (IAudioEndpointVolume)aepv_obj;
                int res = aepv.GetMasterVolumeLevelScalar(ref currentLevel);
                return (int)(100 * currentLevel);  // Returned as an Integer 0 - 100
            }
            catch (Exception ex) 
            { 
                return -1;
            }
        }
    }
}

Points of Interest

Loads of credit and a big thank you goes to Eske Rahn for the large amount of research that he did to gather the information and produce an example.

History

  • 4th April, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
United States United States
I’m retired. When I started my career, programming projects consisted of plugging wires into plug boards to create punch card processing applications to be run on electrical accounting machine like the IBM 402, 407, 085, 088, 514, 519, etc. From there, I moved to writing SPS and Autocoder applications on an IBM 1401 with 4K of memory eventually upgraded to 16K of memory. After many years of migrating my skills to various languages on various hardware platforms, I became an Information Technology Director where I didn’t need to program anymore. So, starting in 1996, I volunteered my time with a local community cable television organization and built some applications to help them run their operations. Originally in Clipper Summer 1987 and later Clipper 5.2, I migrated and enhanced those applications to VB .NET 2003 in 2003. I retired from my full-time job in 2010. Since then, I have continued to support the local community cable tv organization's applications. In 2013, I migrated the VB .NET 2003 Solution to VB .NET 2012 so that it can run on 64-bit computers and interact with Microsoft Office 2010. The upgrade went smoothly. In mid 2013, I developed a VB .NET 2012 application for them to download election results data from the Secretary of State's web site, format the results and send them to a VizRT character generator for on-air display.

Comments and Discussions

 
-- No messages could be retrieved (timeout) --