2010-02-09 6 views
4

Windows Vista/7에서 마스터 볼륨이 변경 될 때마다 알림을 받으려고합니다. 내가 말하는 메시지 상자를 얻기 위해 기다리고 있었다Vista/7에서 볼륨 변경 알림 받기 (C++)

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

시스템 볼륨 변경,하지만 난 메시지 상자를 결코 할 때 "권이 변경"이것은 내가 사용하고 코드입니다.

나는 내가 뭘 잘못 http://msdn.microsoft.com/en-us/library/dd374921(VS.85).aspx

에서 대부분의 코드를 가지고?

편집 : 나는 내 응용 프로그램 (감사 @nobugz)의 볼륨을 변경할 때 알림을받을 수 있나요. 그러나 마스터 볼륨을 변경할 때 알림이 필요합니다. 래리 오스터 비스타/7의 볼륨에 대한 블로그 게시물의 시리즈가 있습니다

편집 2 (Win7에의 볼륨 믹서 대화 상자에서 "장치"로 표시). 이것은 특히 흥미 롭습니다 : http://blogs.msdn.com/larryosterman/archive/2007/03/22/fun-with-the-endpoint-volume-interfaces-closing-the-loop.aspx 나는 나가 나가 원하는 것을 얻을 수 있는지보기 위하여 내일 부호를 시도 할 것이다. 이제는 침대가 있습니다.

EDIT 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 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 :이 래리의 코드의 버전을 박탈입니다.

#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; 
} 

아직 SDK 예제를 살펴 보지 않았습니다. 이제는이 응용 프로그램이 내 응용 프로그램에 필요한 것을 추가 할만큼 잘 작동하는 방법을 알게되었습니다. everyones 도움에 감사드립니다!

+1

메인의 오류 코드를 확인하지 않습니다. 다른 오류가없는 것이 확실합니까? – Michael

+0

http://msdn.microsoft.com/en-us/library/windows/desktop/dd370839(v=vs.85).aspx – rogerdpack

답변

1

시도해도 문제가 없습니다. 볼륨 컨트롤 트레이 아이콘을 클릭하고 믹서를 클릭 한 다음 의 볼륨을 응용 프로그램으로 수정하십시오.

+0

흠. 마스터 볼륨이 변경 될 때 응용 프로그램의 볼륨뿐만 아니라 알림을 받고 싶습니다. 무엇을 바꾸어야합니까? – Tobbe

+0

IAudioEndpointVolume이 필요하다고 생각합니다. –

+0

IAudioEndpointVolumeCallback은 다음을 살펴볼 수 있습니다. – Tobbe

1

Windows SDK "OSD" sample은 내가 블로그에 게시 한 것보다 하드웨어 볼륨을 모니터링하는 훨씬 간단한 예제입니다 (해당 게시물의 모든 계량 정크가 필요하지는 않습니다).

+0

그래, 사용 종료 된 코드에서 해당 항목을 제거했습니다. 아직 제거되지 않은 코드를 게시하지 않으려 고합니다. – Tobbe