在Vista / 7(C ++)上获取卷更改通知 [英] Getting volume change notifications on Vista/7 (C++)

查看:313
本文介绍了在Vista / 7(C ++)上获取卷更改通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在Windows Vista / 7上的主音量变化时收到通知。这是我使用的代码:

  #include< audiopolicy.h> 
#include< audioclient.h>
#include< mmdeviceapi.h>
#include< endpointvolume.h>
#include< windows.h>
#include< shlwapi.h>
#include< iostream>
#include< Tchar.h>

static const GUID AudioSessionVolumeCtx = {0x2715279f,0x4139,0x4ba0,{0x9c,0xb1,0xb3,0x51,0xf1,0xb5,0x8a,0x4a}};

template< class T> void SafeRelease(T ** ppT)
{
if(* ppT)
{
(* ppT) - &
* ppT = NULL;
}
}

class CAudioSessionVolume:public IAudioSessionEvents
{
public:
static HRESULT CreateInstance(UINT uNotificationMessage,HWND hwndNotification,CAudioSessionVolume * * ppAudioSessionVolume)
{
CAudioSessionVolume * pAudioSessionVolume = new(std :: nothrow)
CAudioSessionVolume(uNotificationMessage,hwndNotification);

if(pAudioSessionVolume == NULL)
{
return E_OUTOFMEMORY;
}

HRESULT hr = pAudioSessionVolume-> Initialize();
if(SUCCEEDED(hr))
{
* ppAudioSessionVolume = pAudioSessionVolume;
}
else
{
pAudioSessionVolume-> Release();
}

return hr;
}

// IUnknown方法。
STDMETHODIMP QueryInterface(REFIID riid,void ** ppv)
{
static const QITAB qit [] =
{
QITABENT(CAudioSessionVolume,IAudioSessionEvents),
{0},
};
return QISearch(this,qit,riid,ppv);
}

STDMETHODIMP_(ULONG)AddRef()
{
return InterlockedIncrement(& m_cRef);
}

STDMETHODIMP_(ULONG)Release()
{
LONG cRef = InterlockedDecrement(& m_cRef);
if(cRef == 0)
{
delete this;
}
return cRef;
}

STDMETHODIMP OnSimpleVolumeChanged(float NewVolume,BOOL NewMute,LPCGUID EventContext)
{
MessageBox(NULL,_T(vol changed),_T ),MB_OK);
return S_OK;
}

//剩余的音频会话事件不需要任何操作。
STDMETHODIMP OnDisplayNameChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}

STDMETHODIMP OnIconPathChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}

STDMETHODIMP OnChannelVolumeChanged(DWORD,float [],DWORD,LPCGUID)
{
return S_OK;
}

STDMETHODIMP OnGroupingParamChanged(LPCGUID,LPCGUID)
{
return S_OK;
}

STDMETHODIMP OnStateChanged(AudioSessionState)
{
return S_OK;
}

STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason)
{
return S_OK;
}

//其他方法
HRESULT启用通知(BOOL bEnable)
{
HRESULT hr = S_OK;

if(bEnable)
{
hr = m_pAudioSession-> RegisterAudioSessionNotification(this);
}
else
{
hr = m_pAudioSession-> UnregisterAudioSessionNotification(this);
}

return hr;
}

HRESULT SetDisplayName(const WCHAR * wszName)
{
if(m_pAudioSession == NULL)
{
return E_FAIL;
}
else
{
return m_pAudioSession-> SetDisplayName(wszName,NULL);
}
}

protected:
CAudioSessionVolume(UINT uNotificationMessage,HWND hwndNotification):
m_cRef(1),m_pAudioSession(NULL),m_pSimpleAudioVolume )
{
}

〜CAudioSessionVolume()
{
EnableNotifications(FALSE);

SafeRelease(& m_pAudioSession);
SafeRelease(& m_pSimpleAudioVolume);
}

HRESULT初始化()
{
HRESULT hr = S_OK;

IMMDeviceEnumerator * pDeviceEnumerator = NULL;
IMMDevice * pDevice = NULL;
IAudioSessionManager * pAudioSessionManager = NULL;

//获取音频端点设备的枚举。
hr = CoCreateInstance(__ uuidof(MMDeviceEnumerator),NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(& pDeviceEnumerator));

if(FAILED(hr))
{
goto done;
}

//获取SAR将使用的默认音频端点。
hr = pDeviceEnumerator-> GetDefaultAudioEndpoint(eRender,eConsole,
& pDevice);

if(FAILED(hr))
{
goto done;
}

//获取此设备的会话管理器。
hr = pDevice-> Activate(__ uuidof(IAudioSessionManager),
CLSCTX_INPROC_SERVER,NULL,(void **)& pAudioSessionManager);

if(FAILED(hr))
{
goto done;
}

//获取音频会话。
hr = pAudioSessionManager-> GetAudioSessionControl(
& GUID_NULL,//获取默认的音频会话
FALSE,//会话不是跨进程的
& m_pAudioSession);


if(FAILED(hr))
{
goto done;
}

hr = pAudioSessionManager-> GetSimpleAudioVolume(& GUID_NULL,0,
& m_pSimpleAudioVolume);

done:
SafeRelease(& pDeviceEnumerator);
SafeRelease(& pDevice);
SafeRelease(& pAudioSessionManager);
return hr;
}

private:
LONG m_cRef;

IAudioSessionControl * m_pAudioSession;
ISimpleAudioVolume * m_pSimpleAudioVolume;
};

int main()
{
CoInitialize(NULL);
CAudioSessionVolume * asv;
CAudioSessionVolume :: CreateInstance(0,NULL,& asv);
asv-> EnableNotifications(true);

char s;
gets(& s);
}



我希望在系统显示vol changed的消息框



我得到了大部分的代码从 http://msdn.microsoft.com/en-us/library/dd374921(VS.85).aspx

我做错了什么?



EDIT:卷为我的应用程序(感谢@nobugz)。但我想更改主音量通知。 (在Win7的音量调音台对话框中列为设备)



编辑2: Larry Osterman有一系列关于Vista / 7。这一点特别有趣: http://blogs.msdn.com/larryosterman/archive/2007/03/22/fun-with-the-endpoint-volume-interfaces-closing-the-loop.aspx 我会明天试试代码,看看我能得到我想要的。



编辑3:这是Larry的博客文章,包括上面链接的完整代码。它做我需要的,然后一些。我将尝试修剪我不需要的功能。

  #include< windows.h> 
#include< mmdeviceapi.h>
#include< endpointvolume.h>
#include< Tchar.h>
#include< strsafe.h>

class CVolumeNotification:public IAudioEndpointVolumeCallback
{
LONG m_RefCount;
〜CVolumeNotification(void){};
public:
CVolumeNotification(void):m_RefCount(1)
{
}
STDMETHODIMP_(ULONG)AddRef(){return InterlockedIncrement(& m_RefCount); }
STDMETHODIMP_(ULONG)Release()
{
LONG ref = InterlockedDecrement(& m_RefCount);
if(ref == 0)
delete this;
return ref;
}
STDMETHODIMP QueryInterface(REFIID IID,void ** ReturnValue)
{
if(IID == IID_IUnknown || IID == __uuidof(IAudioEndpointVolumeCallback))
{
* ReturnValue = static_cast< IUnknown *>(this);
Addref();
return S_OK;
}
* ReturnValue = NULL;
return E_NOINTERFACE;
}

STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData)
{
wchar_t outputString [256];
DWORD写入;
COORD writeCoord;
StringCbPrintf(outputString,sizeof(outputString),LVolume Changed:%f,NotificationData-> fMasterVolume);

writeCoord.X = 0;
writeCoord.Y = 3,
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE),outputString,(DWORD)wcslen(outputString),writeCoord,& written);
return S_OK;
}
};

struct TimerContext
{
IAudioMeterInformation * _Meter;
};

const int TimerPeriodicityMS = 100;
void CALLBACK TimerMeterCallback(PTP_CALLBACK_INSTANCE CallbackInstance,PVOID Context,PTP_TIMER Timer)
{
TimerContext * timerContext =(TimerContext *)Context;
wchar_t outputString [256];
float peakValue;
DWORD写入;
COORD writeCoord;
StringCbCopy(outputString,sizeof(outputString),LMeter:);

timerContext-> _Meter-> GetPeakValue(& peakValue);
for(size_t i = 0; i {
StringCbCat(outputString,sizeof(outputString),L*);
}
for(size_t i =(size_t)(peakValue * 100); i <100; i + = 1)
{
StringCbCat(outputString,sizeof(outputString) ,L。
}
writeCoord.X = 0;
writeCoord.Y = 1;
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE),outputString,(DWORD)wcslen(outputString),writeCoord,& written);
}

int _tmain(int argc,_TCHAR * argv [])
{
CoInitialize(NULL)
HRESULT hr;
IMMDeviceEnumerator * deviceEnumerator = NULL ;;
HANDLE句柄;
TP_CALLBACK_ENVIRON callbackEnvironment;
PTP_CLEANUP_GROUP cleanupGroup;
PTP_TIMER timer;
TimerContext上下文;

InitializeThreadpoolEnvironment(& callbackEnvironment);
cleanupGroup = CreateThreadpoolCleanupGroup();
SetThreadpoolCallbackCleanupGroup(& callbackEnvironment,cleanupGroup,NULL);

timer = CreateThreadpoolTimer(TimerMeterCallback,& context,& callbackEnvironment);

//
//清除屏幕。代码被盗:http://support.microsoft.com/kb/319257。
//
handle = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD writtenChars = 0;
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
COORD首页;
home.X = home.Y = 0;
GetConsoleScreenBufferInfo(handle,& consoleInfo);
FillConsoleOutputCharacter(handle,L'',consoleInfo.dwSize.X * consoleInfo.dwSize.Y,home,& writtenChars);
SetConsoleCursorPosition(handle,home);

//
//将控制台设置为原始模式。
//
DWORD oldMode;
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),& oldMode);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),oldMode&〜(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

//
//实例化端点卷对象。
//
hr = CoCreateInstance(__ uuidof(MMDeviceEnumerator),NULL,CLSCTX_INPROC_SERVER,__uuidof(IMMDeviceEnumerator),(LPVOID *)& deviceEnumerator);
IMMDevice * defaultDevice = NULL;

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

IAudioEndpointVolume * endpointVolume = NULL;
hr = defaultDevice-> Activate(__ uuidof(IAudioEndpointVolume),CLSCTX_INPROC_SERVER,NULL,(LPVOID *)& endpointVolume);
CVolumeNotification * volumeNotification = new CVolumeNotification();
hr = endpointVolume-> RegisterControlChangeNotify(volumeNotification);
hr = defaultDevice-> Activate(__ uuidof(IAudioMeterInformation),CLSCTX_INPROC_SERVER,NULL,(LPVOID *)& context._Meter);
defaultDevice-> Release();
defaultDevice = NULL;
//设置100毫秒定时器。
LARGE_INTEGER timerPeriodicityLI;
FILETIME timerPeriodicity;
timerPeriodicityLI.QuadPart = -1 *(TimerPeriodicityMS * 10000);

timerPeriodicity.dwLowDateTime = timerPeriodicityLI.LowPart;
timerPeriodicity.dwHighDateTime = timerPeriodicityLI.HighPart;

SetThreadpoolTimer(timer,& timerPeriodicity,TimerPeriodicityMS,10);

wchar_t inputChar ='\0';
while(inputChar!='\r')
{
UINT currentStep,stepCount;
DWORD读,写;
COORD writeCoord;
wchar_t outputString [256];
StringCbCopy(outputString,sizeof(outputString),LVolume:);

//
//计算cheesy OSD。
//
endpointVolume-> GetVolumeStepInfo(& currentStep,& stepCount);
for(size_t i = 0; i {
if(i <= currentStep)
{
StringCbCat ,sizeof(outputString),L=);
}
else
{
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 =='+')
{
endpointVolume-> VolumeStepUp(NULL);
}
else if(inputChar ==' - ')
{
endpointVolume-> VolumeStepDown(NULL);
}
}

//
//删除我们的通知。
//
endpointVolume-> UnregisterControlChangeNotify(volumeNotification);

endpointVolume-> Release();
volumeNotification-> Release();
SetConsoleMode(getStdHandle(STD_INPUT_HANDLE),oldMode);

CloseThreadpoolCleanupGroupMembers(cleanupGroup,FALSE,NULL);
CloseThreadpoolCleanupGroup(cleanupGroup);
cleanupGroup = NULL;
DestroyThreadpoolEnvironment(& callbackEnvironment);

context._Meter-> Release();

CoUninitialize();

return 0;
}

EDIT 4:

  #include< windows.h> 
#include< mmdeviceapi.h>
#include< endpointvolume.h>
#include< iostream>
#include< Tchar.h>

class CVolumeNotification:public IAudioEndpointVolumeCallback
{
LONG m_RefCount;
〜CVolumeNotification(void){};
public:
CVolumeNotification(void):m_RefCount(1)
{
}
STDMETHODIMP_(ULONG)AddRef(){return InterlockedIncrement(& m_RefCount); }
STDMETHODIMP_(ULONG)Release()
{
LONG ref = InterlockedDecrement(& m_RefCount);
if(ref == 0)
delete this;
return ref;
}
STDMETHODIMP QueryInterface(REFIID IID,void ** ReturnValue)
{
if(IID == IID_IUnknown || IID == __uuidof(IAudioEndpointVolumeCallback))
{
* ReturnValue = static_cast< IUnknown *>(this);
AddRef();
return S_OK;
}
* ReturnValue = NULL;
return E_NOINTERFACE;
}

STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData)
{
std :: cout< NotificationData-> fMasterVolume<< _T();

return S_OK;
}
};

int _tmain(int argc,_TCHAR * argv [])
{
CoInitialize(NULL);
HRESULT hr;
IMMDeviceEnumerator * deviceEnumerator = NULL ;;

//
//实例化端点卷对象。
//
hr = CoCreateInstance(__ uuidof(MMDeviceEnumerator),NULL,CLSCTX_INPROC_SERVER,__uuidof(IMMDeviceEnumerator),(LPVOID *)& deviceEnumerator);
IMMDevice * defaultDevice = NULL;

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

IAudioEndpointVolume * endpointVolume = NULL;
hr = defaultDevice-> Activate(__ uuidof(IAudioEndpointVolume),CLSCTX_INPROC_SERVER,NULL,(LPVOID *)& endpointVolume);
CVolumeNotification * volumeNotification = new CVolumeNotification();
hr = endpointVolume-> RegisterControlChangeNotify(volumeNotification);
defaultDevice-> Release();
defaultDevice = NULL;

wchar_t inputChar ='\0'
while(inputChar!='\r')
{
DWORD read;
ReadConsole(GetStdHandle(STD_INPUT_HANDLE),& inputChar,1,& read,NULL);
}

//
//删除我们的通知。
//
endpointVolume-> UnregisterControlChangeNotify(volumeNotification);
endpointVolume-> Release();
volumeNotification-> Release();

CoUninitialize();

return 0;
}

我还没有看过SDK示例。我现在知道这个东西如何工作,以至于添加我需要我的应用程序。感谢所有人的帮助!

解决方案

当我尝试它时工作正常。请务必点击音量控制盘图标,点击混音器并修改您的应用的音量。


I'm trying to get notifications whenever the master volume changes on Windows Vista/7. This is the code I'm using:

#include <audiopolicy.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <windows.h>
#include <shlwapi.h>
#include <iostream>
#include <Tchar.h>

static const GUID AudioSessionVolumeCtx = { 0x2715279f, 0x4139, 0x4ba0, { 0x9c, 0xb1, 0xb3, 0x51, 0xf1, 0xb5, 0x8a, 0x4a } };

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

class CAudioSessionVolume : public IAudioSessionEvents
{
public:
    static HRESULT CreateInstance( UINT uNotificationMessage, HWND hwndNotification, CAudioSessionVolume **ppAudioSessionVolume )
    {
        CAudioSessionVolume *pAudioSessionVolume = new (std::nothrow) 
            CAudioSessionVolume(uNotificationMessage, hwndNotification);

        if (pAudioSessionVolume == NULL)
        {
            return E_OUTOFMEMORY;
        }

        HRESULT hr = pAudioSessionVolume->Initialize();
        if (SUCCEEDED(hr))
        {
            *ppAudioSessionVolume = pAudioSessionVolume;
        }
        else
        {
            pAudioSessionVolume->Release();
        }

        return hr;
    }

    // IUnknown methods.
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        static const QITAB qit[] = 
        {
            QITABENT(CAudioSessionVolume, IAudioSessionEvents),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }

    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHODIMP_(ULONG) Release()
    {
        LONG cRef = InterlockedDecrement( &m_cRef );
        if (cRef == 0)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHODIMP OnSimpleVolumeChanged( float NewVolume, BOOL NewMute, LPCGUID EventContext )
    {
        MessageBox(NULL, _T("vol changed"), _T("test"), MB_OK);
        return S_OK;
    }

    // The remaining audio session events do not require any action.
    STDMETHODIMP OnDisplayNameChanged(LPCWSTR,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnIconPathChanged(LPCWSTR,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnChannelVolumeChanged(DWORD,float[],DWORD,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnGroupingParamChanged(LPCGUID,LPCGUID)
    {
        return S_OK;
    }

    STDMETHODIMP OnStateChanged(AudioSessionState)
    {
        return S_OK;
    }

    STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason)
    {
        return S_OK;
    }

    // Other methods
    HRESULT EnableNotifications(BOOL bEnable)
    {
        HRESULT hr = S_OK;

        if (bEnable)
        {
            hr = m_pAudioSession->RegisterAudioSessionNotification(this);
        }
        else
        {
            hr = m_pAudioSession->UnregisterAudioSessionNotification(this);
        }

        return hr;
    }

    HRESULT SetDisplayName(const WCHAR *wszName)
    {
        if (m_pAudioSession == NULL)
        {
            return E_FAIL;
        }
        else
        {
            return m_pAudioSession->SetDisplayName(wszName, NULL);
        }
    }

protected:
    CAudioSessionVolume( UINT uNotificationMessage, HWND hwndNotification ) :
        m_cRef(1), m_pAudioSession(NULL), m_pSimpleAudioVolume(NULL)
    {
    }

    ~CAudioSessionVolume()
    {
        EnableNotifications(FALSE);

        SafeRelease(&m_pAudioSession);
        SafeRelease(&m_pSimpleAudioVolume);
    }

    HRESULT Initialize()
    {
        HRESULT hr = S_OK;

        IMMDeviceEnumerator *pDeviceEnumerator = NULL;
        IMMDevice *pDevice = NULL;
        IAudioSessionManager *pAudioSessionManager = NULL;

        // Get the enumerator for the audio endpoint devices.
        hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnumerator));

        if (FAILED(hr)) 
        { 
            goto done; 
        }

        // Get the default audio endpoint that the SAR will use.
        hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
            &pDevice);

        if (FAILED(hr)) 
        { 
            goto done; 
        }

        // Get the session manager for this device.
        hr = pDevice->Activate(__uuidof(IAudioSessionManager), 
            CLSCTX_INPROC_SERVER, NULL, (void**) &pAudioSessionManager);

        if (FAILED(hr)) 
        { 
            goto done; 
        }

        // Get the audio session. 
        hr = pAudioSessionManager->GetAudioSessionControl( 
            &GUID_NULL,     // Get the default audio session. 
            FALSE,          // The session is not cross-process.
            &m_pAudioSession);


        if (FAILED(hr)) 
        { 
            goto done; 
        }

        hr = pAudioSessionManager->GetSimpleAudioVolume(&GUID_NULL, 0,
            &m_pSimpleAudioVolume);

    done:
        SafeRelease(&pDeviceEnumerator);
        SafeRelease(&pDevice);
        SafeRelease(&pAudioSessionManager);
        return hr;
    }

private:
    LONG m_cRef;

    IAudioSessionControl    *m_pAudioSession;
    ISimpleAudioVolume      *m_pSimpleAudioVolume;
};

int main()
{
    CoInitialize(NULL);
    CAudioSessionVolume *asv;
    CAudioSessionVolume::CreateInstance(0, NULL, &asv);
    asv->EnableNotifications(true);

    char s;
    gets(&s);
}

I was expecting to get a message box saying "vol changed" when the system volume changes, but I never get a message box.

I got most of the code from http://msdn.microsoft.com/en-us/library/dd374921(VS.85).aspx

What am I doing wrong?

EDIT: I do get notifications when changing the volume for my application (thanks @nobugz). But I want notification when changing the master volume. (Listed as "Device" in Win7's Volume Mixers dialog)

EDIT 2: Larry Osterman has a series of blog posts about the volume in Vista/7. This one in particular is interesting: http://blogs.msdn.com/larryosterman/archive/2007/03/22/fun-with-the-endpoint-volume-interfaces-closing-the-loop.aspx I'll try the code out tomorrow to see if I can get what I want. Now it's time for bed.

EDIT 3: This is the complete code from Larry's blog posts up to and including the link posted above. It does what I need and then some. I'll try to trim the features I don't need.

#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <Tchar.h>
#include <strsafe.h>

class CVolumeNotification : public IAudioEndpointVolumeCallback 
{ 
    LONG m_RefCount; 
    ~CVolumeNotification(void) {}; 
public: 
    CVolumeNotification(void) : m_RefCount(1) 
    { 
    } 
    STDMETHODIMP_(ULONG)AddRef() { return InterlockedIncrement(&m_RefCount); } 
    STDMETHODIMP_(ULONG)Release()  
    { 
        LONG ref = InterlockedDecrement(&m_RefCount);  
        if (ref == 0) 
            delete this; 
        return ref; 
    } 
    STDMETHODIMP QueryInterface(REFIID IID, void **ReturnValue) 
    { 
        if (IID == IID_IUnknown || IID== __uuidof(IAudioEndpointVolumeCallback))  
        { 
            *ReturnValue = static_cast<IUnknown*>(this); 
            AddRef(); 
            return S_OK; 
        } 
        *ReturnValue = NULL; 
        return E_NOINTERFACE; 
    } 

    STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData) 
    { 
        wchar_t outputString[256]; 
        DWORD written; 
        COORD writeCoord; 
        StringCbPrintf(outputString, sizeof(outputString), L"Volume Changed: %f", NotificationData->fMasterVolume); 

        writeCoord.X = 0; 
        writeCoord.Y = 3; 
        WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), outputString, (DWORD)wcslen(outputString), writeCoord, &written); 
        return S_OK; 
    } 
}; 

struct TimerContext 
{ 
    IAudioMeterInformation *_Meter; 
}; 

const int TimerPeriodicityMS = 100; 
void CALLBACK TimerMeterCallback(PTP_CALLBACK_INSTANCE CallbackInstance, PVOID Context, PTP_TIMER Timer) 
{
    TimerContext *timerContext = (TimerContext *)Context; 
    wchar_t outputString[256]; 
    float peakValue; 
    DWORD written; 
    COORD writeCoord; 
    StringCbCopy(outputString, sizeof(outputString), L"Meter: "); 

    timerContext->_Meter->GetPeakValue(&peakValue); 
    for (size_t i = 0 ; i < peakValue*100; i += 1) 
    { 
        StringCbCat(outputString, sizeof(outputString), L"*"); 
    } 
    for (size_t i = (size_t)(peakValue*100) ; i < 100; i += 1) 
    { 
        StringCbCat(outputString, sizeof(outputString), L"."); 
    } 
    writeCoord.X = 0; 
    writeCoord.Y = 1; 
    WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), outputString, (DWORD)wcslen(outputString), writeCoord, &written); 
} 

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
    HRESULT hr;
    IMMDeviceEnumerator *deviceEnumerator = NULL;;
    HANDLE handle; 
    TP_CALLBACK_ENVIRON callbackEnvironment; 
    PTP_CLEANUP_GROUP cleanupGroup; 
    PTP_TIMER timer; 
    TimerContext context; 

    InitializeThreadpoolEnvironment(&callbackEnvironment); 
    cleanupGroup = CreateThreadpoolCleanupGroup(); 
    SetThreadpoolCallbackCleanupGroup(&callbackEnvironment, cleanupGroup, NULL); 

    timer = CreateThreadpoolTimer(TimerMeterCallback, &context, &callbackEnvironment); 

    //
    //    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->Release();
    deviceEnumerator = NULL;

    IAudioEndpointVolume *endpointVolume = NULL;
    hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
    CVolumeNotification *volumeNotification = new CVolumeNotification(); 
    hr = endpointVolume->RegisterControlChangeNotify(volumeNotification); 
    hr = defaultDevice->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&context._Meter); 
    defaultDevice->Release(); 
    defaultDevice = NULL; 
    // Set a 100 millisecond timer. 
    LARGE_INTEGER timerPeriodicityLI; 
    FILETIME timerPeriodicity; 
    timerPeriodicityLI.QuadPart = -1*(TimerPeriodicityMS* 10000 ); 

    timerPeriodicity.dwLowDateTime = timerPeriodicityLI.LowPart; 
    timerPeriodicity.dwHighDateTime = timerPeriodicityLI.HighPart; 

    SetThreadpoolTimer(timer, &timerPeriodicity, TimerPeriodicityMS, 10); 

    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"=");
            }
            else
            {
                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 == '+')
        {
            endpointVolume->VolumeStepUp(NULL);
        }
        else if (inputChar == '-')
        {
            endpointVolume->VolumeStepDown(NULL);
        }
    }

    // 
    //    Remove our notification. 
    // 
    endpointVolume->UnregisterControlChangeNotify(volumeNotification); 

    endpointVolume->Release(); 
    volumeNotification->Release(); 
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode);

    CloseThreadpoolCleanupGroupMembers(cleanupGroup, FALSE, NULL); 
    CloseThreadpoolCleanupGroup(cleanupGroup); 
    cleanupGroup = NULL; 
    DestroyThreadpoolEnvironment(&callbackEnvironment); 

    context._Meter->Release(); 

    CoUninitialize();

    return 0;
}

EDIT 4: This is a stripped down version of Larry's code.

#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <iostream>
#include <Tchar.h>

class CVolumeNotification : public IAudioEndpointVolumeCallback 
{ 
    LONG m_RefCount; 
    ~CVolumeNotification(void) {}; 
public: 
    CVolumeNotification(void) : m_RefCount(1) 
    { 
    } 
    STDMETHODIMP_(ULONG)AddRef() { return InterlockedIncrement(&m_RefCount); } 
    STDMETHODIMP_(ULONG)Release()  
    { 
        LONG ref = InterlockedDecrement(&m_RefCount);  
        if (ref == 0) 
            delete this; 
        return ref; 
    } 
    STDMETHODIMP QueryInterface(REFIID IID, void **ReturnValue) 
    { 
        if (IID == IID_IUnknown || IID== __uuidof(IAudioEndpointVolumeCallback))  
        { 
            *ReturnValue = static_cast<IUnknown*>(this); 
            AddRef(); 
            return S_OK; 
        } 
        *ReturnValue = NULL; 
        return E_NOINTERFACE; 
    } 

    STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData) 
    { 
        std::cout << NotificationData->fMasterVolume << _T(" ");

        return S_OK; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
    HRESULT hr;
    IMMDeviceEnumerator *deviceEnumerator = NULL;;

    // 
    //    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->Release();
    deviceEnumerator = NULL;

    IAudioEndpointVolume *endpointVolume = NULL;
    hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
    CVolumeNotification *volumeNotification = new CVolumeNotification(); 
    hr = endpointVolume->RegisterControlChangeNotify(volumeNotification); 
    defaultDevice->Release(); 
    defaultDevice = NULL; 

    wchar_t inputChar = '\0';
    while (inputChar != '\r')
    {
        DWORD read;
        ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
    }

    // 
    //    Remove our notification. 
    // 
    endpointVolume->UnregisterControlChangeNotify(volumeNotification); 
    endpointVolume->Release(); 
    volumeNotification->Release(); 

    CoUninitialize();

    return 0;
}

I still haven't looked at the SDK example. I now know how this stuff works well enough to add what I need to my application. Thanks for everyones help!

解决方案

It works fine when I try it. Be sure to click on the volume control tray icon, click Mixer and modify the volume for your app.

这篇关于在Vista / 7(C ++)上获取卷更改通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆