您的位置:首页 > 其它

如何检测耳机接入Windows系统

2016-03-09 19:16 615 查看
[align=center]author:Leen[/align]
[align=center]
[/align]
[align=center]
[/align]
[align=center]
[/align]
[align=left]对于这个问题,网上很多人说处理WM_DEVICECHANGE消息即可。[/align]
[align=left]于是快速建立个MFC小程序测试一下:[/align]
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;
}

[align=left]实测发现有很多局限性,对于那种大个头的USB耳机来说会收的到事件,但是对于平时用的那种小插孔耳机完全没效果。。[/align]
[align=left]难道Windows就无法检测耳机的插入么?[/align]
[align=left]后来发现网上有人说要用 IMMNotificationClient接口[/align]

Minimum supported client

Windows Vista [desktop apps only]
[align=left]
[/align]
[align=left]但是这个接口怎么用呢?[/align]
[align=left]在MSDN上找到个例子,可以监测耳机的声音,[/align]
// 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();
};

[align=left]
[/align]
[align=left]这个例子原本并没有实现HRESULT CVolumeMonitor::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key)这个接口[/align]
[align=left]但是经过实测这个接口是可以收到耳机插拔事件的[/align]
[align=left]
[/align]
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;
}


[align=left]但是这种方法有个弊端,无法知道是插入还是拔出,因为插入跟拔出都会收到一大堆的事件、。[/align]
// 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;
}


代码下载链接
[align=left]点击打开链接
[/align]
[align=left]
[/align]
[align=left]
[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: