VC++实时检测耳机的插入与拔出(附源码)

       网上不少人问如何通过代码去检测耳机的插入与拔出,但网上基本找不到有效的、完整的代码去实现对耳机的检测。最近我们的项目中也遇到这样的需求,所以在网上搜集了大量的资料,综合了多篇文章的内容,找到了一种实时检测耳机插拔的有效方法。在此和大家分享一下相关的代码,供大家参考,如果有更好的办法,可以在评论区留言。

VC++实时检测耳机的插入与拔出(附源码)_第1张图片

1、实现继承于IMMNotificationClient接口类的CMMNotificationClient类,实时感知音频设备变化的通知事件

       我们讲的这种耳机(扬声器)是插入电脑3.5mm(毫米)插孔的耳机,本文中也是对3.5mm插孔插入的耳机进行实时检测。      

       首先我们要编写一个继承于IMMNotificationClient音视频设备通知接口类的类CMMNotificationClient,用来实时感知音视频设备变化的通知事件。 由于 IMMNotificationClient 继承自 IUnknown纯虚接口类,所以在CMMNotificationClient 类中要实现IUnknown接口类的AddRef、 Release 和 QueryInterface 等接口。   

       此外,CMMNotificationClient类中还要实现特定于 IMMNotificationClient 接口类的OnDefaultDeviceChanged 、OnDeviceAdded、OnDeviceRemoved、OnDeviceStateChanged和OnPropertyValueChanged等接口。这些接口的含义在MSDN在线网站上有详细的说明:

设备事件 (核心音频 Api)https://docs.microsoft.com/zh-cn/windows/win32/coreaudio/device-events?redirectedfrom=MSDN在MSDN该网页上,也给出了继承于接口类IMMNotificationClient的CMMNotificationClient类的实现,但MSDN上给出的示例代码是有问题的,代码中并没有初始化音视频COM组件的操作。

       下面给出CMMNotificationClient类的完整实现。CMMNotificationClient类的头文件如下:

#pragma once

#define SAFE_RELEASE(punk)  \
	if ((punk) != NULL)  \
{ (punk)->Release(); (punk) = NULL; }

#include  
#include "iostream" 
using namespace std;
#include "Functiondiscoverykeys_devpkey.h"

//-----------------------------------------------------------
// Example implementation of IMMNotificationClient interface.
// When the status of audio endpoint devices change, the
// MMDevice module calls these methods to notify the client.
//-----------------------------------------------------------

class CMMNotificationClient : public IMMNotificationClient
{
	LONG _cRef;
	IMMDeviceEnumerator *_pEnumerator;
	BOOL m_bHave;

	 Private function to print device-friendly name
	//HRESULT _PrintDeviceName(LPCWSTR  pwstrId);

public:
	CMMNotificationClient() :
	  _cRef(1),
		  _pEnumerator(NULL),
		m_bHave(FALSE)
	  {
		  //初始化COM 
		  ::CoInitialize(NULL);
		  HRESULT hr = S_OK;

		  //创建接口 
		  hr = CoCreateInstance(
			  __uuidof(MMDeviceEnumerator), NULL,
			  CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
			  (void**)&_pEnumerator);

		  if (hr == S_OK)
		  {
			  cout << "接口创建成功" << endl;
		  }
		  else
		  {
			  cout << "接口创建失败" << endl;
		  }
		  //注册事件 
		  hr = _pEnumerator->RegisterEndpointNotificationCallback((IMMNotificationClient*)this);
		  if (hr == S_OK)
		  {
			  cout << "注册成功" << endl;
		  }
		  else
		  {
			  cout << "注册失败" << endl;
		  }
	  }

	  ~CMMNotificationClient()
	  {
		  SAFE_RELEASE(_pEnumerator)
	  }

	  // IUnknown methods -- AddRef, Release, and QueryInterface

	  ULONG STDMETHODCALLTYPE AddRef()
	  {
		  return InterlockedIncrement(&_cRef);
	  }

	  ULONG STDMETHODCALLTYPE Release()
	  {
		  ULONG ulRef = InterlockedDecrement(&_cRef);
		  if (0 == ulRef)
		  {
			  delete this;
		  }
		  return ulRef;
	  }

	  HRESULT STDMETHODCALLTYPE QueryInterface(
		  REFIID riid, VOID **ppvInterface)
	  {
		  if (IID_IUnknown == riid)
		  {
			  AddRef();
			  *ppvInterface = (IUnknown*)this;
		  }
		  else if (__uuidof(IMMNotificationClient) == riid)
		  {
			  AddRef();
			  *ppvInterface = (IMMNotificationClient*)this;
		  }
		  else
		  {
			  *ppvInterface = NULL;
			  return E_NOINTERFACE;
		  }
		  return S_OK;
	  }

	  // Callback methods for device-event notifications.

	  HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
		  EDataFlow flow, ERole role,
		  LPCWSTR pwstrDeviceId)
	  {
		  //char  *pszFlow = "?????";
		  //char  *pszRole = "?????";

		  //CString strName;
		  //_PrintDeviceName(pwstrDeviceId, strName);
		  //CString strLog = _T("[OnDefaultDeviceChanged]");
		  //strLog += strName;
		  AfxMessageBox( strLog );

		  //switch (flow)
		  //{
		  //case eRender:
			 // pszFlow = "eRender";
			 // break;
		  //case eCapture:
			 // pszFlow = "eCapture";
			 // break;
		  //}

		  //switch (role)
		  //{
		  //case eConsole:
			 // pszRole = "eConsole";
			 // break;
		  //case eMultimedia:
			 // pszRole = "eMultimedia";
			 // break;
		  //case eCommunications:
			 // pszRole = "eCommunications";
			 // break;
		  //}

		  //printf("  -->New default device: flow = %s, role = %s\n",
			 // pszFlow, pszRole);
		  return S_OK;
	  }

	  HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
	  {
		  //CString strName;
		  //_PrintDeviceName(pwstrDeviceId, strName);
		  //CString strLog = _T("[OnDeviceAdded]");
		  //strLog += strName;
		  AfxMessageBox( strLog );

		  //printf("  -->Added device\n");
		  return S_OK;
	  };

	  HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
	  {
		 // CString strName;
		 // _PrintDeviceName(pwstrDeviceId, strName);
		 // CString strLog = _T("[OnDeviceRemoved]");
		 // strLog += strName;
		  AfxMessageBox( strLog );

		 // printf("  -->Removed device\n");
		  return S_OK;
	  }

	  HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
		  LPCWSTR pwstrDeviceId,
		  DWORD dwNewState)
	  {
		  //WCHAR* pszState = L"???????????????";

		  //CString strName;
		  //_PrintDeviceName(pwstrDeviceId, strName);
		  //CString strLog = _T("[OnDeviceStateChanged]");
		  //strLog += strName;
		  //

		  //switch (dwNewState)
		  //{
		  //case DEVICE_STATE_ACTIVE:
			 // pszState = L"ACTIVE";
			 // break;
		  //case DEVICE_STATE_DISABLED:
			 // pszState = L"DISABLED";
			 // break;
		  //case DEVICE_STATE_NOTPRESENT:
			 // pszState = L"NOTPRESENT";
			 // break;
		  //case DEVICE_STATE_UNPLUGGED:
			 // pszState = L"UNPLUGGED";
			 // break;
		  //}

		  //strLog += pszState;
		  AfxMessageBox( strLog );

		  printf("  -->New device state is DEVICE_STATE_%s (0x%8.8x)\n",
			  pszState, dwNewState);

		  return S_OK;
	  }
	  
	  HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
		  LPCWSTR pwstrDeviceId,
		  const PROPERTYKEY key);

	  BOOL IsCurInsertEarPhone(CUIString& strDevId);

	   Given an endpoint ID string, print the friendly device name.
  //HRESULT _PrintDeviceName(LPCWSTR pwstrId, CString& strName )
  //{
	 // HRESULT hr = S_OK;
	 // IMMDevice *pDevice = NULL;
	 // IPropertyStore *pProps = NULL;
	 // PROPVARIANT varString;

	 // CoInitialize(NULL);
	 // PropVariantInit(&varString);

	 // if (_pEnumerator == NULL)
	 // {
		//  // Get enumerator for audio endpoint devices.
		//  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
		//	  NULL, CLSCTX_INPROC_SERVER,
		//	  __uuidof(IMMDeviceEnumerator),
		//	  (void**)&_pEnumerator);
	 // }
	 // if (hr == S_OK)
	 // {
		//  hr = _pEnumerator->GetDevice(pwstrId, &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_Device_FriendlyName, &varString);
	 // }

	 // //CString str;
	 // strName.Format(_T("**Device name: %s\n Endpoint ID string: %s\n"),
		//  (hr == S_OK) ? varString.pwszVal : L"null device",
		//  (pwstrId != NULL) ? pwstrId : L"null ID");
	 // PROPVARIANT varString2;
	 // PropVariantInit(&varString2);
	 // hr = pProps->GetValue(PKEY_Device_DevType, &varString2);
	 // CString strType;
	 // strType.Format(_T("(nType = %d)"), varString2.ulVal );
	 // strName += strType;

	 // PropVariantClear(&varString2);
	 // PropVariantClear(&varString);

	 // SAFE_RELEASE(pProps)
		//  SAFE_RELEASE(pDevice)
		//  CoUninitialize();
	 // return hr;
  //}
};

CMMNotificationClient类的cpp源文件如下:

#include "stdafx.h"
#include "mmnotifyclient.h"
#include "mainlogic.h"
#include "devicetopology.h"


HRESULT STDMETHODCALLTYPE CMMNotificationClient::OnPropertyValueChanged(
	LPCWSTR pwstrDeviceId,
	const PROPERTYKEY key)
{
	// CString strName;
	// _PrintDeviceName(pwstrDeviceId, strName);
	// CString strLog = _T("[OnPropertyValueChanged]");
	// strLog += strName;
	 AfxMessageBox( strLog );

	 //printf("  -->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);

	if ( !GetChatPtr()->IsMainUISwitchSuccess() )
	{
		return S_OK;
	}

	CString strDevId;
	BOOL bHave = IsCurInsertEarPhone(strDevId);
	CUIString strLog;
	strLog.Format(_T("[CMMNotificationClient::OnPropertyValueChanged] m_bHave:%d,bHave:%d ."), m_bHave, bHave);
	WriteLog(strLog);

	if (!m_bHave && bHave)
	{
		MessageBox(NULL, _T("耳机插入"), _T("提示"), MB_OK);
	}
	else if (m_bHave && !bHave)
	{
		MessageBox(NULL, _T("耳机拔出"), _T("提示"), MB_OK);
	}

	m_bHave = bHave;

	//CStringA str;
	//str.Format("pwstrDeviceId: %d");
	//MessageBoxA(NULL, str, "Tip", MB_OK);
	return S_OK;
}

BOOL CMMNotificationClient::IsCurInsertEarPhone(CString& strDevId)
{
	IKsJackDescription *pJackDesc = NULL;
	HRESULT hr = S_OK;

	IMMDevice *pDevice = NULL;
	IPropertyStore *pProps = NULL;
	PROPVARIANT varString;

	IDeviceTopology *pDeviceTopology = NULL;
	IConnector *pConnFrom = NULL;
	IConnector *pConnTo = NULL;
	IPart *pPart = NULL;

	//CoInitialize(NULL);
	PropVariantInit(&varString);

	if (_pEnumerator == NULL)
	{
		// Get enumerator for audio endpoint devices.
		hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
			NULL, CLSCTX_INPROC_SERVER,
			__uuidof(IMMDeviceEnumerator),
			(void**)&_pEnumerator);
	}

	IMMDeviceCollection *pMultiDevice = NULL;
	hr = _pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pMultiDevice);
	if (FAILED(hr))
		return FALSE;

	UINT deviceCount = 0;
	hr = pMultiDevice->GetCount(&deviceCount);
	if (FAILED(hr))
		return FALSE;

	for (UINT ii = 0; ii < deviceCount; ii++)
	{
		pDevice = NULL;
		hr = pMultiDevice->Item(ii, &pDevice);
		if (FAILED(hr))
		{
			continue;
		}

		hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL,
			NULL, (void**)&pDeviceTopology);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pDevice)
			continue;
		}

		// The device topology for an endpoint device always
// contains just one connector (connector number 0).
		hr = pDeviceTopology->GetConnector(0, &pConnFrom);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		// Step across the connection to the jack on the adapter.
		hr = pConnFrom->GetConnectedTo(&pConnTo);
		if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
		{
			// The adapter device is not currently active.
			hr = E_NOINTERFACE;
		}
		if (FAILED(hr))
		{
			SAFE_RELEASE(pConnFrom)
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		// Get the connector's IPart interface.
		hr = pConnTo->QueryInterface(__uuidof(IPart), (void**)&pPart);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pConnTo)
			SAFE_RELEASE(pConnFrom)
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		//获取描述外接设备信息
		hr = pPart->Activate(CLSCTX_INPROC_SERVER, __uuidof(IKsJackDescription), (void**)&pJackDesc);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pPart)
			SAFE_RELEASE(pConnTo)
			SAFE_RELEASE(pConnFrom)
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		UINT num = 0;
		pJackDesc->GetJackCount(&num);
		//wprintf_s(_T("GetJackCount: %d\n"), num);
		for (UINT j = 0; j < num; ++j)
		{
			KSJACK_DESCRIPTION ksjack_desc = { 0 };

			hr = pJackDesc->GetJackDescription(j, &ksjack_desc);
			if (hr == S_OK)
			{
				//wprintf_s(_T("Jack%d PortConnection: %d\n"), j + 1, ksjack_desc.PortConnection);  //端口类型
				//wprintf_s(_T("Jack%d ConnectionType: %d\n"), j + 1, ksjack_desc.ConnectionType); //连接类型
				//wprintf_s(_T("Jack%d IsConnected: %d\n\n"), j + 1, ksjack_desc.IsConnected); //连接状态
				if (ksjack_desc.IsConnected)
				{
					if (ksjack_desc.ConnectionType == eConnType3Point5mm)//连接类型
					{
						WCHAR achId[512] = { 0 };
						WCHAR*  pId = achId;
						pDevice->GetId(&pId);
                        strDevId = pId;

						SAFE_RELEASE(pJackDesc)
						SAFE_RELEASE(pPart)
						SAFE_RELEASE(pConnTo)
						SAFE_RELEASE(pConnFrom)
						SAFE_RELEASE(pDeviceTopology)
						SAFE_RELEASE(pDevice)
						SAFE_RELEASE(pMultiDevice)
						return 1;
					}
				}
			}
		}

		SAFE_RELEASE(pJackDesc)
		SAFE_RELEASE(pPart)
		SAFE_RELEASE(pConnTo)
		SAFE_RELEASE(pConnFrom)
		SAFE_RELEASE(pDeviceTopology)
		SAFE_RELEASE(pDevice)
	}

	SAFE_RELEASE(pJackDesc)
	SAFE_RELEASE(pPart)
	SAFE_RELEASE(pConnTo)
	SAFE_RELEASE(pConnFrom)
	SAFE_RELEASE(pDeviceTopology)
	SAFE_RELEASE(pDevice)
	SAFE_RELEASE(pMultiDevice)
	return 0;
}

       经过反复的实验和测试,当插入或拔出耳机时,在OnPropertyValueChanged接口中始终都有回调,所以就是通过该接口的通知去实现检测的。

       上述代码中包含了实时检测耳机插拔的完整代码,可以直接拿去使用。下面我们就来针对代码中的细节作详细的说明。

2、在CMMNotificationClient的构造函数中初始化多媒体设备COM接口,设置回调类指针

      CMMNotificationClient类的构造函数如下:

	CMMNotificationClient() :
	  _cRef(1),
		  _pEnumerator(NULL),
		m_bHave(FALSE)
	  {
		  //初始化COM 
		  ::CoInitialize(NULL);
		  HRESULT hr = S_OK;

		  //创建接口 
		  hr = CoCreateInstance(
			  __uuidof(MMDeviceEnumerator), NULL,
			  CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
			  (void**)&_pEnumerator);

		  if (hr == S_OK)
		  {
			  cout << "接口创建成功" << endl;
		  }
		  else
		  {
			  cout << "接口创建失败" << endl;
		  }
		  //注册事件 
		  hr = _pEnumerator->RegisterEndpointNotificationCallback((IMMNotificationClient*)this);
		  if (hr == S_OK)
		  {
			  cout << "注册成功" << endl;
		  }
		  else
		  {
			  cout << "注册失败" << endl;
		  }
	  }

在CMMNotificationClient类的构造函数中,先创建IMMDeviceEnumerator COM接口,并把当前的CMMNotificationClient类作为回调类设置到系统中,这样系统中有音视频设备变化时,就会回调CMMNotificationClient类中的OnDefaultDeviceChanged 、OnDeviceAdded、OnDeviceRemoved、OnDeviceStateChanged和OnPropertyValueChanged等接口了。

       我们在将CMMNotificationClient类引入到代码后,只需要在代码中定义一个CMMNotificationClient类对象即可。

3、通过获取音频设备接口下外设的KSJACK_DESCRIPTION 信息判断耳机的连接状态

       经实际测试发现,当插入或拔出耳机时,会多次回调OnPropertyValueChanged接口,回调接口中回调的信息中只包含设备id,根本没有设备类型的信息,也是没法判断哪个消息是和耳机插入与拔出有关系的。

       我们通过搜集资料得知,可以通过遍历系统的音频接口上外设信息,就能知道在3.5mm的接口上插入耳机了。获取存放外设信息的结构体KSJACK_DESCRIPTION,其定义如下:

typedef /* [public][public] */ struct __MIDL___MIDL_itf_devicetopology_0000_0000_0009
    {
    DWORD ChannelMapping;
    COLORREF Color;
    EPcxConnectionType ConnectionType; // 设备类型
    EPcxGeoLocation GeoLocation;
    EPcxGenLocation GenLocation;
    EPxcPortConnection PortConnection;
    BOOL IsConnected;                  // 设备是否连接上
    } 	KSJACK_DESCRIPTION;

通过上述结构体中的ConnectionType字段判断当前外设是不是有3.5mm插孔耳机,通过IsConnected字段判断耳机有没有连接上,相关代码如下所示:

BOOL CMMNotificationClient::IsCurInsertEarPhone(CString& strDevId)
{
	IKsJackDescription *pJackDesc = NULL;
	HRESULT hr = S_OK;

	IMMDevice *pDevice = NULL;
	IPropertyStore *pProps = NULL;
	PROPVARIANT varString;

	IDeviceTopology *pDeviceTopology = NULL;
	IConnector *pConnFrom = NULL;
	IConnector *pConnTo = NULL;
	IPart *pPart = NULL;

	//CoInitialize(NULL);
	PropVariantInit(&varString);

	if (_pEnumerator == NULL)
	{
		// Get enumerator for audio endpoint devices.
		hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
			NULL, CLSCTX_INPROC_SERVER,
			__uuidof(IMMDeviceEnumerator),
			(void**)&_pEnumerator);
	}

	IMMDeviceCollection *pMultiDevice = NULL;
	hr = _pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pMultiDevice);
	if (FAILED(hr))
		return FALSE;

	UINT deviceCount = 0;
	hr = pMultiDevice->GetCount(&deviceCount);
	if (FAILED(hr))
		return FALSE;

	for (UINT ii = 0; ii < deviceCount; ii++)
	{
		pDevice = NULL;
		hr = pMultiDevice->Item(ii, &pDevice);
		if (FAILED(hr))
		{
			continue;
		}

		hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL,
			NULL, (void**)&pDeviceTopology);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pDevice)
			continue;
		}

		// The device topology for an endpoint device always
// contains just one connector (connector number 0).
		hr = pDeviceTopology->GetConnector(0, &pConnFrom);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		// Step across the connection to the jack on the adapter.
		hr = pConnFrom->GetConnectedTo(&pConnTo);
		if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
		{
			// The adapter device is not currently active.
			hr = E_NOINTERFACE;
		}
		if (FAILED(hr))
		{
			SAFE_RELEASE(pConnFrom)
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		// Get the connector's IPart interface.
		hr = pConnTo->QueryInterface(__uuidof(IPart), (void**)&pPart);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pConnTo)
			SAFE_RELEASE(pConnFrom)
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		//获取描述外接设备信息
		hr = pPart->Activate(CLSCTX_INPROC_SERVER, __uuidof(IKsJackDescription), (void**)&pJackDesc);
		if (FAILED(hr))
		{
			SAFE_RELEASE(pPart)
			SAFE_RELEASE(pConnTo)
			SAFE_RELEASE(pConnFrom)
			SAFE_RELEASE(pDeviceTopology)
			SAFE_RELEASE(pDevice)
			continue;
		}

		UINT num = 0;
		pJackDesc->GetJackCount(&num);
		//wprintf_s(_T("GetJackCount: %d\n"), num);
		for (UINT j = 0; j < num; ++j)
		{
			KSJACK_DESCRIPTION ksjack_desc = { 0 };

			hr = pJackDesc->GetJackDescription(j, &ksjack_desc);
			if (hr == S_OK)
			{
				//wprintf_s(_T("Jack%d PortConnection: %d\n"), j + 1, ksjack_desc.PortConnection);  //端口类型
				//wprintf_s(_T("Jack%d ConnectionType: %d\n"), j + 1, ksjack_desc.ConnectionType); //连接类型
				//wprintf_s(_T("Jack%d IsConnected: %d\n\n"), j + 1, ksjack_desc.IsConnected); //连接状态
				if (ksjack_desc.IsConnected)
				{
					if (ksjack_desc.ConnectionType == eConnType3Point5mm)//连接类型
					{
						WCHAR achId[512] = { 0 };
						WCHAR*  pId = achId;
						pDevice->GetId(&pId);
                        strDevId = pId;

						SAFE_RELEASE(pJackDesc)
						SAFE_RELEASE(pPart)
						SAFE_RELEASE(pConnTo)
						SAFE_RELEASE(pConnFrom)
						SAFE_RELEASE(pDeviceTopology)
						SAFE_RELEASE(pDevice)
						SAFE_RELEASE(pMultiDevice)
						return 1;
					}
				}
			}
		}

		SAFE_RELEASE(pJackDesc)
		SAFE_RELEASE(pPart)
		SAFE_RELEASE(pConnTo)
		SAFE_RELEASE(pConnFrom)
		SAFE_RELEASE(pDeviceTopology)
		SAFE_RELEASE(pDevice)
	}

	SAFE_RELEASE(pJackDesc)
	SAFE_RELEASE(pPart)
	SAFE_RELEASE(pConnTo)
	SAFE_RELEASE(pConnFrom)
	SAFE_RELEASE(pDeviceTopology)
	SAFE_RELEASE(pDevice)
	SAFE_RELEASE(pMultiDevice)
	return 0;
}

4、通过记录耳机的连接状态,判断是插入耳机还是拔出耳机

       CMMNotificationClient::IsCurInsertEarPhone接口只能判断耳机是否连入到系统中,但无法判断耳机是插入还是拔出了,所以我们要记录耳机的连接状态,如果是从未连接切换到连接状态,则表示是插入耳机;如果是从连接状态切换到未连接状态,则表示是拔出耳机。相关代码如下:

	CString strDevId;
	BOOL bHave = IsCurInsertEarPhone(strDevId);
	CUIString strLog;
	strLog.Format(_T("[CMMNotificationClient::OnPropertyValueChanged] m_bHave:%d,bHave:%d ."), m_bHave, bHave);
	WriteLog(strLog);

	if (!m_bHave && bHave)
	{
		MessageBox(NULL, _T("耳机插入"), _T("提示"), MB_OK);
	}
	else if (m_bHave && !bHave)
	{
		MessageBox(NULL, _T("耳机拔出"), _T("提示"), MB_OK);
	}

5、大体思路总结

      当耳机插入或者拔出时,都会触发OnPropertyValueChanged接口的回调,我们就在这个接口中调用IsCurInsertEarPhone接口查看耳机的来连接状态,然后根据状态的记录,判断是插入耳机还是拔出耳机。

       其实目前这种检测方法,其实是有点牵强的,因为插入或者拔出一次耳机时会触发OnPropertyValueChanged函数的多次调用,我们只能在该函数中以轮询的方式去调用IsCurInsertEarPhone接口去进行耳机插拔的检测的,所以目前这种检测效率也要稍差一点的。

你可能感兴趣的:(VC++常用功能代码封装,3.5mm插孔,耳机,插入与拔出,实时检测)