2010-02-09 5 views
4

J'essaie d'obtenir des notifications chaque fois que le volume principal change sous Windows Vista/7. Voici le code que je utilise:Obtenir des notifications de changement de volume sous 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); 
} 

Je me attendais à obtenir une boîte de message « vol modifié » lorsque les variations de volume du système, mais je ne me une boîte de message.

J'ai eu la plupart du code de http://msdn.microsoft.com/en-us/library/dd374921(VS.85).aspx

Qu'est-ce que je fais mal?

EDIT: Je reçois des notifications lorsque je change le volume de mon application (merci @nobugz). Mais je veux une notification lors du changement du volume principal. (Répertorié "Device" dans la boîte de dialogue Volume Mixers de Win7)

EDIT 2: Larry Osterman publie une série d'articles sur le volume dans Vista/7. Celui-ci en particulier est intéressant: http://blogs.msdn.com/larryosterman/archive/2007/03/22/fun-with-the-endpoint-volume-interfaces-closing-the-loop.aspx Je vais essayer le code demain pour voir si je peux obtenir ce que je veux. Maintenant c'est l'heure du coucher.

EDIT 3: Ceci est le code complet du blog de Larry, y compris le lien posté ci-dessus. Il fait ce dont j'ai besoin et ensuite. Je vais essayer de couper les fonctionnalités dont je n'ai pas besoin.

#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: Ceci est une version dépouillée du code de Larry.

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

Je n'ai toujours pas examiné l'exemple SDK. Je sais maintenant comment cela fonctionne assez bien pour ajouter ce dont j'ai besoin à mon application. Merci pour l'aide de tout le monde!

+1

Vous ne vérifiez aucun code d'erreur dans le main - êtes-vous sûr qu'il n'y a pas d'autres échecs? – Michael

+0

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

Répondre

1

Cela fonctionne très bien quand je l'essaie. Assurez-vous de cliquer sur l'icône de la barre de contrôle du volume, cliquez sur Mixer et modifiez le volume pour votre application.

+0

Hmm. Je veux être notifié quand le volume principal change, pas seulement le volume pour mon application. De quoi ai-je besoin pour changer? – Tobbe

+0

Je pense que vous avez besoin de IAudioEndpointVolume. –

+0

IAudioEndpointVolumeCallback est peut-être quelque chose à regarder :) – Tobbe

1

Le SDK Windows "OSD" sample est un exemple beaucoup plus simple de surveillance du volume matériel que celui que j'ai posté sur le blog (vous n'avez pas besoin de toute la jauge de comptage dans ce post).

+0

Oui, j'ai enlevé ce truc du code que j'ai fini par utiliser, mais je n'ai pas encore eu l'occasion de publier le code dépouillé ici pour le moment. – Tobbe

Questions connexes