Other fun things to do with the EndpointVolume interfaces

Last week I posted a code snippet that showed how to change the master volume in Vista.  That snippet doesn't really show the coolness that exists within the IAudioEndpointVolume interface.

One of my favorite features is the support for OSD's (On Screen Displays).  To show it off, I write a tiny little OSD program that shows off just two of the APIs in question - VolumeStepUp and VolumeStepDown. 

If you hit '+' and '-' it'll increase and decrease the volume, I also included a cheesy OSD so you can see the effects of the APIs.

Before people complain about how much code is here, this is the complete source file, and there's a lot more code to deal with the console than there is to deal with the volume. 

#include "stdafx.h"
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <strsafe.h>

int _tmain(int argc, _TCHAR* argv[])
    HRESULT hr;

    IMMDeviceEnumerator *deviceEnumerator = NULL;;
    HANDLE      handle;

    //    Clear the screen.  Code stolen from: http://support.microsoft.com/kb/319257.
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD writtenChars = 0;
    CONSOLE_SCREEN_BUFFER_INFO consoleInfo;            
    COORD home;
    home.X = home.Y = 0;
    GetConsoleScreenBufferInfo(handle, &consoleInfo);
    FillConsoleOutputCharacter(handle, L' ', consoleInfo.dwSize.X * consoleInfo.dwSize.Y, home, &writtenChars);
    SetConsoleCursorPosition(handle, home);

    //    Set the console to raw mode. 
    DWORD oldMode;
    GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldMode);
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

    //    Instantiate an endpoint volume object.

    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
    IMMDevice *defaultDevice = NULL;

    hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
    deviceEnumerator = NULL;

    IAudioEndpointVolume *endpointVolume = NULL;
    hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
    defaultDevice = NULL;

    wchar_t inputChar = '\0';
    while (inputChar != '\r')
        UINT currentStep, stepCount;
        DWORD read, written;
        COORD writeCoord;
        wchar_t outputString[256];
        StringCbCopy(outputString, sizeof(outputString), L"Volume: ");

        //    Calculate the cheesy OSD.

        endpointVolume->GetVolumeStepInfo(&currentStep, &stepCount);
        for (size_t i = 0 ; i < stepCount ; i += 1)
            if (i <= currentStep)
                StringCbCat(outputString, sizeof(outputString), L"=");
                StringCbCat(outputString, sizeof(outputString), L"-");
        writeCoord.X = 0;
        writeCoord.Y = 0;
        WriteConsoleOutputCharacter(handle, outputString, (DWORD)wcslen(outputString), writeCoord, &written);

        ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
        if (inputChar == '+')
        else if (inputChar == '-')

    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode);

    return 0;

This program's pretty simple right now, the next step is to add a bit of magic to it (metering).

Comments (12)

  1. Luciano says:

    There are still someone who can code in a proper language at Microsoft 😉

  2. I code in the language that best fits my task at hand, usually it’s C++.

  3. John says:

    Too bad it won’t compile in ASCII builds.  No use of the _T* types or macros?  For shame, Larry.  For shame.

  4. John, why on earth would  want this to compile in 8 bit character mode?  The compiler’s default character set is now Unicode, and 8bit character sets carry with them a mountainous set of baggage.

    If you want it to work in non unicode, the conversion is simple.

  5. John says:

    Just for the sake of completeness.

    Will the Windows SDK headers ever ship without definitions for ASCII structs / functions?  As you say there is not really any compelling use for ASCII these days aside from backwards compatibility, but how long is that going to be maintained?

  6. John, the A functions won’t go away, but all new APIs are unicode only.

  7. Anony Moose says:

    Many of the new APIs are Vista-only and 9x is officially way way past its lifetime. (I’m ok with that, some things need to die.) So, if you want to use these new APIs in actual products, you’ld better hope your customers have all upgraded.   😉

  8. Michael says:

    Larry –

                Could you please show an example of implementing IAudioEndpointVolumeCallback? Not a full app, just the lines of code to create the interface and supply the OnNotify function, so I’ve got something to hand to RegisterControlChangeNotify? I will praise your name fulsomely.



    (mrgriffin@gmail.com if you want to comment without posting in your space)

  9. Yesterday I posted a quick&amp;dirty OSD (complete with cheesy text graphics). Today I’m going to add

  10. Tanveer Badar says:

    Nice to see your code is color highlighted.

  11. Nike says:

    Hi Larry,i want to change the Mute status.

    if (inputChar == ‘+’)  


       BOOL *mute_status=FALSE;                  


       if (mute_status)





    No matter the Mute is on or off,the mute_status is 0,what is worry with this?


  12. Nike says:

    Larry,oh,it is my worry.

    I can set the muting state .

Skip to main content