BEGIN_MESSAGE_MAP(CVolumeHelperDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_DEVICECHANGE() //}}AFX_MSG_MAP END_MESSAGE_MAP()
BOOL CVolumeHelperDlg::OnDeviceChange(UINT nEventType,DWORD_PTR dwData) { DEV_BROADCAST_HDR* pdbh= NULL; switch(nEventType) { case DBT_DEVICEARRIVAL: pdbh = (DEV_BROADCAST_HDR*)(dwData); if(pdbh->dbch_devicetype == DBT_DEVTYP_VOLUME) { AfxMessageBox(_T("DBT_DEVTYP_VOLUME")); } break; case DBT_DEVICEREMOVECOMPLETE: AfxMessageBox(_T("DBT_DEVICEREMOVECOMPLETE")); break; default: break; } return TRUE; }
Minimum supported client |
Windows Vista [desktop apps only] |
---|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #pragma once class CVolumeMonitor : IMMNotificationClient, IAudioEndpointVolumeCallback { private: BOOL m_bRegisteredForEndpointNotifications; BOOL m_bRegisteredForVolumeNotifications; CComPtr<IMMDeviceEnumerator> m_spEnumerator; CComPtr<IMMDevice> m_spAudioEndpoint; CComPtr<IAudioEndpointVolume> m_spVolumeControl; CCriticalSection m_csEndpoint; long m_cRef; ~CVolumeMonitor(); // refcounted object... make the destructor private HRESULT AttachToDefaultEndpoint(); void DetachFromEndpoint(); // IMMNotificationClient (only need to really implement OnDefaultDeviceChanged) IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*pwstrDeviceId*/, DWORD /*dwNewState*/);// { return S_OK; } IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*pwstrDeviceId*/);// { return S_OK; } IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*pwstrDeviceId*/) { return S_OK; } IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId); // **** IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/);// { return S_OK; } IFACEMETHODIMP OnDeviceQueryRemove() { return S_OK; } IFACEMETHODIMP OnDeviceQueryRemoveFailed() { return S_OK; } IFACEMETHODIMP OnDeviceRemovePending() { return S_OK; } // IAudioEndpointVolumeCallback IFACEMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify); // IUnknown IFACEMETHODIMP QueryInterface(const IID& iid, void** ppUnk); public: CVolumeMonitor(); HRESULT Initialize(); void Dispose(); HRESULT GetLevelInfo(VOLUME_INFO* pInfo); void ChangeEndpoint(); HRESULT PrintDeviceName(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); // IUnknown IFACEMETHODIMP_(ULONG) AddRef(); IFACEMETHODIMP_(ULONG) Release(); };
HRESULT CVolumeMonitor::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { PrintDeviceName(pwstrDeviceId, key); if (g_hwndOSD != NULL) PostMessage(g_hwndOSD, WM_ENDPOINTPROPERTYCHANGE, key.pid, 0); TCHAR szkey[2048] = { 0 }; StringCbPrintf(szkey, RTL_NUMBER_OF(szkey),_T("-->Changed device property {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d\n"), key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3, key.fmtid.Data4[0], key.fmtid.Data4[1], key.fmtid.Data4[2], key.fmtid.Data4[3], key.fmtid.Data4[4], key.fmtid.Data4[5], key.fmtid.Data4[6], key.fmtid.Data4[7], key.pid); OutputDebugString(szkey); return S_OK; }
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "stdafx.h" #include "osd.h" #include "endpointMonitor.h" #include <functiondiscoverykeys.h> #include <strsafe.h> CVolumeMonitor::CVolumeMonitor() : m_bRegisteredForEndpointNotifications(FALSE), m_bRegisteredForVolumeNotifications(FALSE), m_cRef(1) { } CVolumeMonitor::~CVolumeMonitor() { } // ---------------------------------------------------------------------- // Call when the app is done with this object before calling release. // This detaches from the endpoint and releases all audio service references. // // ---------------------------------------------------------------------- void CVolumeMonitor::Dispose() { DetachFromEndpoint(); if (m_bRegisteredForEndpointNotifications) { m_spEnumerator->UnregisterEndpointNotificationCallback(this); m_bRegisteredForEndpointNotifications = FALSE; } } // ---------------------------------------------------------------------- // Initialize this object. Call after constructor. // // ---------------------------------------------------------------------- HRESULT CVolumeMonitor::Initialize() { HRESULT hr; // create enumerator hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); if (SUCCEEDED(hr)) { hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); if (SUCCEEDED(hr)) { hr = AttachToDefaultEndpoint(); } } return hr; } // ---------------------------------------------------------------------- // Called from the UI thread when the volume is changed (see OSD.cpp // WM_VOLUMECHANGE handler) // // ---------------------------------------------------------------------- HRESULT CVolumeMonitor::GetLevelInfo(VOLUME_INFO* pInfo) { HRESULT hr = E_FAIL; m_csEndpoint.Enter(); if (m_spVolumeControl != NULL) { hr = m_spVolumeControl->GetMute(&pInfo->bMuted); if (SUCCEEDED(hr)) { hr = m_spVolumeControl->GetVolumeStepInfo(&pInfo->nStep, &pInfo->cSteps); } } m_csEndpoint.Leave(); return hr; } // ---------------------------------------------------------------------- // Start monitoring the current default device // // ---------------------------------------------------------------------- HRESULT CVolumeMonitor::AttachToDefaultEndpoint() { m_csEndpoint.Enter(); // get the default music & movies playback device HRESULT hr = m_spEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &m_spAudioEndpoint); if (SUCCEEDED(hr)) { // get the volume control for it hr = m_spAudioEndpoint->Activate(__uuidof(m_spVolumeControl), CLSCTX_INPROC_SERVER, NULL, (void**)&m_spVolumeControl); if (SUCCEEDED(hr)) { // register for callbacks hr = m_spVolumeControl->RegisterControlChangeNotify(this); m_bRegisteredForVolumeNotifications = SUCCEEDED(hr); } } m_csEndpoint.Leave(); return hr; } // ---------------------------------------------------------------------- // Stop monitoring the device and release all associated references // // ---------------------------------------------------------------------- void CVolumeMonitor::DetachFromEndpoint() { m_csEndpoint.Enter(); if (m_spVolumeControl != NULL) { // be sure to unregister... if (m_bRegisteredForVolumeNotifications) { m_spVolumeControl->UnregisterControlChangeNotify(this); m_bRegisteredForVolumeNotifications = FALSE; } m_spVolumeControl.Release(); } if (m_spAudioEndpoint != NULL) { m_spAudioEndpoint.Release(); } m_csEndpoint.Leave(); } // ---------------------------------------------------------------------- // Call this from the UI thread when the default device changes // // ---------------------------------------------------------------------- void CVolumeMonitor::ChangeEndpoint() { DetachFromEndpoint(); AttachToDefaultEndpoint(); } // ---------------------------------------------------------------------- // Implementation of IMMNotificationClient::OnDefaultDeviceChanged // // When the user changes the default output device we want to stop monitoring the // former default and start monitoring the new default // // ---------------------------------------------------------------------- HRESULT CVolumeMonitor::OnDefaultDeviceChanged ( EDataFlow flow, ERole /*role*/, LPCWSTR /*pwstrDefaultDeviceId*/ ) { if (flow == eRender) { if (g_hwndOSD != NULL) PostMessage(g_hwndOSD, WM_ENDPOINTCHANGE, 0, 0); } // return value of this callback is ignored return S_OK; } // ---------------------------------------------------------------------- // Implementation of IAudioEndpointVolumeCallback::OnNotify // // This is called by the audio core when anyone in any process changes the volume or // mute state for the endpoint we are monitoring // // ---------------------------------------------------------------------- HRESULT CVolumeMonitor::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA /*pNotify*/) { if (g_hwndOSD != NULL) PostMessage(g_hwndOSD, WM_VOLUMECHANGE, 0, 0); return S_OK; } HRESULT CVolumeMonitor::OnDeviceStateChanged(LPCWSTR /*pwstrDeviceId*/, DWORD /*dwNewState*/) { return S_OK; } HRESULT CVolumeMonitor::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { PrintDeviceName(pwstrDeviceId, key); if (g_hwndOSD != NULL) PostMessage(g_hwndOSD, WM_ENDPOINTPROPERTYCHANGE, key.pid, 0); TCHAR szkey[2048] = { 0 }; StringCbPrintf(szkey, RTL_NUMBER_OF(szkey),_T("-->Changed device property {%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d\n"), key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3, key.fmtid.Data4[0], key.fmtid.Data4[1], key.fmtid.Data4[2], key.fmtid.Data4[3], key.fmtid.Data4[4], key.fmtid.Data4[5], key.fmtid.Data4[6], key.fmtid.Data4[7], key.pid); OutputDebugString(szkey); return S_OK; } HRESULT CVolumeMonitor::OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; } HRESULT CVolumeMonitor::PrintDeviceName(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { HRESULT hr = S_OK; CComPtr<IMMDevice> pDevice = NULL; CComPtr<IPropertyStore> pProps = NULL; PROPVARIANT varString; CoInitialize(NULL); PropVariantInit(&varString); if (m_spEnumerator == NULL) { // Get enumerator for audio endpoint devices. hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); } if (hr == S_OK) { hr = m_spEnumerator->GetDevice(pwstrDeviceId, &pDevice); } if (hr == S_OK) { hr = pDevice->OpenPropertyStore(STGM_READ, &pProps); } if (hr == S_OK) { // Get the endpoint device's friendly-name property. hr = pProps->GetValue(PKEY_DeviceInterface_FriendlyName, &varString); } /*printf("----------------------\nDevice name: \"%S\"\n" " Endpoint ID string: \"%S\"\n", (hr == S_OK) ? varString.pwszVal : L"null device", (pwstrDeviceId != NULL) ? pwstrDeviceId : L"null ID");*/ PropVariantClear(&varString); return hr; } // IUnknown methods HRESULT CVolumeMonitor::QueryInterface(REFIID iid, void** ppUnk) { if ((iid == __uuidof(IUnknown)) || (iid == __uuidof(IMMNotificationClient))) { *ppUnk = static_cast<IMMNotificationClient*>(this); } else if (iid == __uuidof(IAudioEndpointVolumeCallback)) { *ppUnk = static_cast<IAudioEndpointVolumeCallback*>(this); } else { *ppUnk = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } ULONG CVolumeMonitor::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG CVolumeMonitor::Release() { long lRef = InterlockedDecrement(&m_cRef); if (lRef == 0) { delete this; } return lRef; }最后只得投机取巧来实现接入耳机时放声音,拔出是静音。
case WM_ENDPOINTPROPERTYCHANGE: { DWORD time = GetTickCount(); if ((time - g_Time) > 3000) { g_Time = time; } else { break; } switch (wParam) { case 0: VolumOffWithoutOsd(); break; default: VolumeOnWithoutOsd(); break; } return 0; }
// Entry to the app int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR /*lpCmdLine*/, int /*nCmdShow*/) { g_hInstance = hInstance; // Mark that this process is DPI aware. SetProcessDPIAware(); // Init COM and double-buffered painting HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (SUCCEEDED(hr)) { hr = BufferedPaintInit(); g_bDblBuffered = SUCCEEDED(hr); // Init volume monitor g_pVolumeMonitor = new (std::nothrow) CVolumeMonitor(); if (g_pVolumeMonitor) { hr = g_pVolumeMonitor->Initialize(); if (SUCCEEDED(hr)) { // Get initial volume level so that we can figure out a good window size g_pVolumeMonitor->GetLevelInfo(&g_currentVolume); WNDCLASSEX wcex = { sizeof(wcex) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.hInstance = g_hInstance; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszClassName = g_szWindowClass; RegisterClassEx(&wcex); // Create the (only) window DWORD const dwStyle = WS_POPUP; // no border or title bar DWORD const dwStyleEx = WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE; // transparent, topmost, with no taskbar item g_hwndOSD = CreateWindowEx(dwStyleEx, g_szWindowClass, NULL, dwStyle, 0, 0, 0, 0, NULL, NULL, g_hInstance, NULL); if (g_hwndOSD) { // Hide the window ShowWindow(g_hwndOSD, SW_HIDE); // Main message loop MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } if (g_bDblBuffered) BufferedPaintUnInit(); g_pVolumeMonitor->Dispose(); g_pVolumeMonitor->Release(); } } CoUninitialize(); } return 0; }