You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
10 KiB
365 lines
10 KiB
#include "hnotif.h"
|
|
#include "hwdev.h"
|
|
|
|
#include "misc.h"
|
|
#include "mischlpr.h"
|
|
#include "dbg.h"
|
|
#include "tfids.h"
|
|
|
|
#include <ioevent.h>
|
|
#include <dbt.h>
|
|
|
|
#define ARRAYSIZE(a) (sizeof((a))/sizeof((a)[0]))
|
|
|
|
HRESULT CHandleNotif::Init(LPCWSTR pszElemName)
|
|
{
|
|
return _SetName(pszElemName);
|
|
}
|
|
|
|
HRESULT CHandleNotif::InitNotif(CHandleNotifTarget* phnt)
|
|
{
|
|
ASSERT(!_phnt);
|
|
|
|
_phnt = phnt;
|
|
|
|
return _Register();
|
|
}
|
|
|
|
HDEVNOTIFY CHandleNotif::GetDeviceNotifyHandle()
|
|
{
|
|
return _hdevnotify;
|
|
}
|
|
|
|
CHandleNotifTarget* CHandleNotif::GetHandleNotifTarget()
|
|
{
|
|
return _phnt;
|
|
}
|
|
|
|
//
|
|
// Application gets DBT_DEVICEQUERYREMOVE message with the handle of the device
|
|
// that's being removed. It should just close the handle to the device.
|
|
//
|
|
// If everything goes OK it gets DBT_DEVICEREMOVEPENDING to notify that remove
|
|
// is complete. Here it unregisters the notification that it did on the handle.
|
|
//
|
|
// If query-remove fails because somebody else in the system vetoed it, it gets
|
|
// DBT_QUERYREMOVEFAILED. Here it should first unregister the notification and
|
|
// reopen the device (if it's still interested) and register again for device
|
|
// change notification (DBT_DEVTYP_HANDLE) on the new handle.
|
|
//
|
|
HRESULT CHandleNotif::HNHandleEvent(DEV_BROADCAST_HANDLE* UNREF_PARAM(pdbh),
|
|
DWORD dwEventType, BOOL* pfSurpriseRemoval)
|
|
{
|
|
HRESULT hres;
|
|
|
|
*pfSurpriseRemoval = FALSE;
|
|
|
|
switch (dwEventType)
|
|
{
|
|
case DBT_DEVICEQUERYREMOVE:
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("DBT_DEVICEQUERYREMOVE for '%s'"), _pszElemName);
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case DBT_DEVICEREMOVEPENDING:
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("DBT_DEVICEREMOVEPENDING for '%s'"), _pszElemName);
|
|
_fSurpriseRemoval = FALSE;
|
|
hres = _Unregister();
|
|
break;
|
|
|
|
case DBT_DEVICEQUERYREMOVEFAILED:
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("DBT_DEVICEQUERYREMOVEFAILED for '%s'"), _pszElemName);
|
|
_fSurpriseRemoval = TRUE;
|
|
hres = _Unregister();
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = _Register();
|
|
}
|
|
break;
|
|
|
|
case DBT_DEVICEREMOVECOMPLETE:
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("DBT_DEVICEREMOVECOMPLETE for '%s'"), _pszElemName);
|
|
if (_fSurpriseRemoval)
|
|
{
|
|
*pfSurpriseRemoval = TRUE;
|
|
hres = _Unregister();
|
|
}
|
|
|
|
hres = S_FALSE;
|
|
|
|
break;
|
|
|
|
default:
|
|
hres = S_FALSE;
|
|
break;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//static
|
|
HRESULT CHandleNotif::_HandleDeviceArrivalRemoval(
|
|
DEV_BROADCAST_HANDLE* UNREF_PARAM(pdbh), DWORD UNREF_PARAM(dwEventType),
|
|
CNamedElem* UNREF_PARAM(pelem))
|
|
{
|
|
#ifdef ENABLE_SURPRISEREMOVAL
|
|
HRESULT hres = S_OK;
|
|
|
|
BOOL fSurpriseRemoval = FALSE;
|
|
CHandleNotif* phn = (CHandleNotif*)pelem;
|
|
CHandleNotifTarget* phnt = phn->GetHandleNotifTarget();
|
|
|
|
if (phnt)
|
|
{
|
|
if (fSurpriseRemoval && phnt->HNTIsSurpriseRemovalAware())
|
|
{
|
|
// Use me!
|
|
DWORD cchReq;
|
|
WCHAR szDeviceIntfID[MAX_DEVICEID];
|
|
WCHAR szFriendlyName[30];
|
|
|
|
hres = pelem->GetName(szDeviceIntfID,
|
|
ARRAYSIZE(szDeviceIntfID), &cchReq);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
CHWDeviceInst* phwdevinst;
|
|
CNamedElem* pelemToRelease;
|
|
hres = _GetHWDeviceInstFromDeviceOrVolumeIntfID(
|
|
szDeviceIntfID, &phwdevinst, &pelemToRelease);
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
hres = phwdevinst->GetFriendlyName(szFriendlyName,
|
|
ARRAYSIZE(szFriendlyName));
|
|
|
|
if (SUCCEEDED(hres) && (S_FALSE != hres))
|
|
{
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("! ! ! Surprise removal for: '%s' ! ! !"),
|
|
szFriendlyName);
|
|
}
|
|
else
|
|
{
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("! ! ! Surprise removal for (no FriendlyName): '%s' ! ! !"),
|
|
szDeviceIntfID);
|
|
}
|
|
|
|
pelemToRelease->RCRelease();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//static
|
|
HRESULT CHandleNotif::_HandleDeviceLockUnlock(DEV_BROADCAST_HANDLE* pdbh,
|
|
DWORD, CNamedElem* pelem)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CHandleNotif* phn = (CHandleNotif*)pelem;
|
|
|
|
if (GUID_IO_VOLUME_LOCK == pdbh->dbch_eventguid)
|
|
{
|
|
#ifdef DEBUG
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("DBT_CUSTOMEVENT + GUID_IO_VOLUME_LOCK for '%s'"),
|
|
pelem->DbgGetName());
|
|
#endif
|
|
|
|
// nothing to do
|
|
++(phn->_cLockAttempts);
|
|
}
|
|
else
|
|
{
|
|
if (GUID_IO_VOLUME_LOCK_FAILED == pdbh->dbch_eventguid)
|
|
{
|
|
#ifdef DEBUG
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("DBT_CUSTOMEVENT + GUID_IO_VOLUME_LOCK_FAILED for '%s'"),
|
|
pelem->DbgGetName());
|
|
#endif
|
|
|
|
--(phn->_cLockAttempts);
|
|
|
|
if (0 == (phn->_cLockAttempts))
|
|
{
|
|
hres = phn->_Unregister();
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = phn->_Register();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GUID_IO_VOLUME_UNLOCK == pdbh->dbch_eventguid)
|
|
{
|
|
#ifdef DEBUG
|
|
TRACE(TF_SHHWDTCTDTCT,
|
|
TEXT("DBT_CUSTOMEVENT + GUID_IO_VOLUME_UNLOCK for '%s'"),
|
|
pelem->DbgGetName());
|
|
#endif
|
|
|
|
// Play it safe...
|
|
(phn->_cLockAttempts) = 0;
|
|
|
|
hres = phn->_Unregister();
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = phn->_Register();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
//static
|
|
HRESULT CHandleNotif::HandleBroadcastHandleEvent(DEV_BROADCAST_HANDLE* pdbh,
|
|
DWORD dwEventType)
|
|
{
|
|
CNamedElemList* pnel;
|
|
HRESULT hres = CHWEventDetectorHelper::GetList(HWEDLIST_HANDLENOTIF,
|
|
&pnel);
|
|
|
|
if (S_OK == hres)
|
|
{
|
|
// Find the elem in the list
|
|
CNamedElemEnum* penum;
|
|
|
|
hres = pnel->GetEnum(&penum);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
CNamedElem* pelem;
|
|
BOOL fFoundIt = FALSE;
|
|
|
|
while (!fFoundIt && SUCCEEDED(hres = penum->Next(&pelem)) &&
|
|
(S_FALSE != hres))
|
|
{
|
|
CHandleNotif* phn = (CHandleNotif*)pelem;
|
|
|
|
if (phn->GetDeviceNotifyHandle() == pdbh->dbch_hdevnotify)
|
|
{
|
|
// Found it!
|
|
BOOL fSurpriseRemoval;
|
|
CHandleNotifTarget* phnt = phn->GetHandleNotifTarget();
|
|
|
|
hres = phn->HNHandleEvent(pdbh, dwEventType, &fSurpriseRemoval);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if ((GUID_IO_MEDIA_ARRIVAL == pdbh->dbch_eventguid) ||
|
|
(GUID_IO_MEDIA_REMOVAL == pdbh->dbch_eventguid))
|
|
{
|
|
hres = _HandleDeviceArrivalRemoval(pdbh, dwEventType,
|
|
pelem);
|
|
}
|
|
else
|
|
{
|
|
if ((GUID_IO_VOLUME_LOCK == pdbh->dbch_eventguid) ||
|
|
(GUID_IO_VOLUME_LOCK_FAILED == pdbh->dbch_eventguid) ||
|
|
(GUID_IO_VOLUME_UNLOCK == pdbh->dbch_eventguid))
|
|
{
|
|
hres = _HandleDeviceLockUnlock(pdbh, dwEventType,
|
|
pelem);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = phnt->HNTHandleEvent(pdbh, dwEventType);
|
|
|
|
// phnt has the same life span as pelem, no need to
|
|
// RCAddRef/RCRelease
|
|
}
|
|
}
|
|
|
|
fFoundIt = TRUE;
|
|
}
|
|
|
|
pelem->RCRelease();
|
|
}
|
|
|
|
penum->RCRelease();
|
|
}
|
|
|
|
pnel->RCRelease();
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//static
|
|
HRESULT CHandleNotif::Create(CNamedElem** ppelem)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
*ppelem = new CHandleNotif();
|
|
|
|
if (!(*ppelem))
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
HRESULT CHandleNotif::_Register()
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
DEV_BROADCAST_HANDLE dbhNotifFilter = {0};
|
|
|
|
dbhNotifFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
|
dbhNotifFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
|
|
dbhNotifFilter.dbch_handle = _GetDeviceHandle(_pszElemName,
|
|
FILE_READ_ATTRIBUTES);
|
|
|
|
if (INVALID_HANDLE_VALUE != dbhNotifFilter.dbch_handle)
|
|
{
|
|
hres = CHWEventDetectorHelper::RegisterDeviceNotification(
|
|
&dbhNotifFilter, &_hdevnotify, FALSE);
|
|
|
|
_CloseDeviceHandle(dbhNotifFilter.dbch_handle);
|
|
}
|
|
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("--- Registered for '%s'"), _pszElemName);
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CHandleNotif::_Unregister()
|
|
{
|
|
if (_hdevnotify)
|
|
{
|
|
UnregisterDeviceNotification(_hdevnotify);
|
|
_hdevnotify = NULL;
|
|
|
|
TRACE(TF_SHHWDTCTDTCT, TEXT("--- UNRegistered for '%s'"), _pszElemName);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
CHandleNotif::CHandleNotif() : _hdevnotify(NULL), _phnt(NULL),
|
|
_fSurpriseRemoval(TRUE), _cLockAttempts(0)
|
|
{}
|
|
|
|
CHandleNotif::~CHandleNotif()
|
|
{
|
|
_Unregister();
|
|
}
|