/******************************************************************************* * * (C) COPYRIGHT MICROSOFT CORP., 1999 * * TITLE: WiaLock.Cpp * * VERSION: 1.0 * * AUTHOR: ByronC * * DATE: 15 November, 1999 * * DESCRIPTION: * Implementation of Lock Manager. * *******************************************************************************/ #include "precomp.h" #include "stiexe.h" // // Include Headers // #include #include "device.h" #include "wiamonk.h" #include "wiapriv.h" #include "stiusd.h" #define DECLARE_LOCKMGR #include "lockmgr.h" /**************************************************************************\ * StiLockMgr * * Constructor for the lock manager. * * Arguments: * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/ StiLockMgr::StiLockMgr() { DBG_FN(StiLockMgr::StiLockMgr); m_cRef = 0; m_dwCookie = 0; m_bSched = FALSE; m_lSchedWaitTime = 0; } /**************************************************************************\ * Initialize * * Initializes the lock manager and registers this instance in the ROT. * * Arguments: * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::Initialize() { DBG_FN(StiLockMgr::Initialize); HRESULT hr = S_OK; // // Artificially set ref count too high to prevent destruction from inside Release() // m_cRef = 2; #ifdef USE_ROT CWiaInstMonk *pInstMonk = new CWiaInstMonk(); CHAR szCookieName[MAX_PATH]; IUnknown *pUnk; IMoniker *pIMoniker; IBindCtx *pCtx; IRunningObjectTable *pTable; USES_CONVERSION; // // Make up a cookie name. This will be stored in the registry. This // name uniquely identifies our lock manager on the system. // srand( (unsigned)time( NULL ) ); sprintf(szCookieName, "%d_LockMgr_%d", rand(), rand()); // // Get our IUnknown interface // hr = QueryInterface(IID_IUnknown, (VOID**) &pUnk); if (SUCCEEDED(hr)) { // // We need to register this object in the ROT, so that any STI clients // will connect to this Lock Manager. This way, we maintain consistent // device lock information amongst multiple STI and WIA clients. // // First create an instance Moniker. Then get a pointer to the ROT. // Register this named moniker with our Lock Manager. Store the // cookie so we can unregister upon destruction of the Lock Manager. // if (pInstMonk) { hr = pInstMonk->Initialize(A2W(szCookieName)); if (SUCCEEDED(hr)) { hr = pInstMonk->QueryInterface(IID_IMoniker, (VOID**) &pIMoniker); } } else { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr)) { hr = CreateBindCtx(0, &pCtx); if (SUCCEEDED(hr)) { hr = pCtx->GetRunningObjectTable(&pTable); if (SUCCEEDED(hr)) { // // Register ourselves in the ROT // hr = pTable->Register(ROTFLAGS_ALLOWANYCLIENT, pUnk, pIMoniker, &m_dwCookie); ASSERT(hr == S_OK); // // Write the cookie name to the registry, so clients will know // the name of our lock manager. // if (hr == S_OK) { hr = WriteCookieNameToRegistry(szCookieName); } else { DBG_ERR(("StiLockMgr::Initialize, could not register Moniker")); hr = (SUCCEEDED(hr)) ? E_FAIL : hr; } pTable->Release(); } else { DBG_ERR(("StiLockMgr::Initialize, could not get Running Object Table")); } pCtx->Release(); } else { DBG_ERR(("StiLockMgr::Initialize, could not create bind context")); } } else { DBG_ERR(("StiLockMgr::Initialize, problem creating Moniker")); } if (pInstMonk) { pInstMonk->Release(); } #endif return hr; } /**************************************************************************\ * ~StiLockMgr * * Destructor - removes instance from the ROT that was resgistered in * Initialize. * * Arguments: * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/ StiLockMgr::~StiLockMgr() { DBG_FN(StiLockMgr::~StiLockMgr); m_cRef = 0; #ifdef USE_ROT if (m_dwCookie) { HRESULT hr; IBindCtx *pCtx; IRunningObjectTable *pTable; hr = CreateBindCtx(0, &pCtx); if (SUCCEEDED(hr)) { hr = pCtx->GetRunningObjectTable(&pTable); if (SUCCEEDED(hr)) { hr = pTable->Revoke(m_dwCookie); } } DeleteCookieFromRegistry(); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::~StiLockMgr, could not Unregister Moniker")); } m_dwCookie = 0; } #endif m_bSched = FALSE; m_lSchedWaitTime = 0; } /**************************************************************************\ * IUnknown methods * * QueryInterface * AddRef * Release * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT _stdcall StiLockMgr::QueryInterface( const IID& iid, void** ppv) { if (iid == IID_IUnknown) { *ppv = (IUnknown*) this; } else if (iid == IID_IStiLockMgr) { *ppv = (IStiLockMgr*) this; } else { return E_NOINTERFACE; } AddRef(); return S_OK; } ULONG _stdcall StiLockMgr::AddRef(void) { InterlockedIncrement(&m_cRef); return m_cRef; } ULONG _stdcall StiLockMgr::Release(void) { LONG ref; InterlockedDecrement(&m_cRef); ref = m_cRef; if (ref == 0) { delete this; } return ref; } /**************************************************************************\ * RequestLock * * Attempt to acquire a device lock. NOTE: Do not attempt this call from * inside an ACTIVE_DEVICE - it may lead to dealock. Use * RequestLock(ACTIVE_DEVICE, ...) instead. * * Arguments: * * pszDeviceName - The STI internal name of the device (same as WIA * device ID) * ulTimeout - The max. amount of time to wait for a lock * bInServerProcess- Indicates whether we're being called from within the * server's process. * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT _stdcall StiLockMgr::RequestLock(BSTR pszDeviceName, ULONG ulTimeout, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestLock); HRESULT hr = E_FAIL; ACTIVE_DEVICE *pDevice = NULL; USES_CONVERSION; // // Get the device specified by pszDeviceName // pDevice = g_pDevMan->IsInList(DEV_MAN_IN_LIST_DEV_ID, pszDeviceName); if(pDevice) { hr = RequestLockHelper(pDevice, ulTimeout, bInServerProcess, dwClientThreadId); // // Release the device due to the AddRef made by the call to // IsInList // pDevice->Release(); } else { // // Device not found, log error // DBG_ERR(("StiLockMgr::RequestLock, device name was not found")); hr = STIERR_INVALID_DEVICE_NAME; } return hr; } /**************************************************************************\ * RequestLock * * Attempt to acquire a device lock. This method is always called from * the server. * * Arguments: * * pDevice - The STI ACTIVE_DEVICE object * ulTimeout - The max. amount of time to wait for a lock * * Return Value: * * Status * * History: * * 12/06/1999 Original Version * \**************************************************************************/ HRESULT _stdcall StiLockMgr::RequestLock(ACTIVE_DEVICE *pDevice, ULONG ulTimeout, BOOL bOpenPort) { DBG_FN(StiLockMgr::RequestLock); return RequestLockHelper(pDevice, ulTimeout, bOpenPort, GetCurrentThreadId()); } /**************************************************************************\ * RequestLockHelper * * Helper used to acquire a device lock. It fills out the appropriate * lock information stored with the device. * * Arguments: * * pDevice - A pointer to the STI device * ulTimeout - The max. amount of time to wait for a lock * bInServerProcess- Indicates whether we're in the server process * * Return Value: * * Status * * History: * * 12/06/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::RequestLockHelper(ACTIVE_DEVICE *pDevice, ULONG ulTimeout, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestLockHelper); HRESULT hr = S_OK; DWORD dwWait = 0; LockInfo *pLockInfo = NULL; DWORD dwCurThread = 0; hr = CheckDeviceInfo(pDevice); if (FAILED(hr)) { return hr; } pLockInfo = (LockInfo*) pDevice->m_pLockInfo; // // Check whether this is the same thread re-acquiring an active lock. // If not, we must wait for device to become free. // dwCurThread = dwClientThreadId; if (InterlockedCompareExchange((LONG*)&pLockInfo->dwThreadId, dwCurThread, dwCurThread) == (LONG) dwCurThread) { pLockInfo->lInUse++; pLockInfo->lTimeLeft = pLockInfo->lHoldingTime; } else { dwWait = WaitForSingleObject(pLockInfo->hDeviceIsFree, ulTimeout); if (dwWait == WAIT_OBJECT_0) { // // Check whether the driver is still loaded. // if (pDevice->m_DrvWrapper.IsDriverLoaded()) { // // Update lock information // InterlockedExchange((LONG*) &pLockInfo->dwThreadId, dwCurThread); pLockInfo->lTimeLeft = pLockInfo->lHoldingTime; pLockInfo->lInUse++; // // Only ask USD to open port if we're in the server process. // if (bInServerProcess) { hr = LockDevice(pDevice); } else { pLockInfo->bDeviceIsLocked = TRUE; } } else { // // Driver not loaded, so clear the lock info. This is the // case where an application was sitting on a request to // lock the device, but the service's control thread // was busy unloading it. We want to stop it here, so // we don't make the bogus call down to the driver // which we know is not loaded. // ClearLockInfo(pLockInfo); hr = WIA_ERROR_OFFLINE; } } else { DBG_ERR(("StiLockMgr::RequestLockHelper, device is busy")); hr = WIA_ERROR_BUSY; } } return hr; } /**************************************************************************\ * RequestUnlock * * Attempt to unlock a device. NOTE: Do not attempt this call from * inside an ACTIVE_DEVICE - it may lead to dealock. Use * RequestUnlock(ACTIVE_DEVICE, ...) instead. * * Arguments: * * pszDeviceName - The STI internal name of the device (same as WIA * device ID) * bInServerProcess- Indicates whether we're in the server process * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT _stdcall StiLockMgr::RequestUnlock(BSTR bstrDeviceName, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestUnlock); HRESULT hr = E_FAIL; ACTIVE_DEVICE *pDevice = NULL; USES_CONVERSION; // // Get the device specified by pszDeviceName // pDevice = g_pDevMan->IsInList(DEV_MAN_IN_LIST_DEV_ID, bstrDeviceName); if(pDevice) { hr = RequestUnlockHelper(pDevice, bInServerProcess, dwClientThreadId); // // Release the device due to the AddRef made by the call to // IsInList // pDevice->Release(); } else { // // Device not found, log error // DBG_ERR(("StiLockMgr::RequestUnlock, device name was not found")); hr = STIERR_INVALID_DEVICE_NAME; } return hr; } /**************************************************************************\ * RequestUnlock * * Attempt to unlock a device. This method is always called from within * the server. * * Arguments: * * pDevice - The STI ACTIVE_DEVICE object * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT _stdcall StiLockMgr::RequestUnlock(ACTIVE_DEVICE *pDevice, BOOL bClosePort) { DBG_FN(StiLockMgr::RequestUnlock); return RequestUnlockHelper(pDevice, bClosePort, GetCurrentThreadId()); } /**************************************************************************\ * RequestUnlockHelper * * Helper used to unlock a device lock. It clears the appropriate * lock information stored with the device. * * Arguments: * * pDevice - A pointer to the STI device * ulTimeout - The max. amount of time to wait for a lock * bInServerProcess- Indicates whether we're in the server process * * Return Value: * * Status * * History: * * 12/06/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::RequestUnlockHelper(ACTIVE_DEVICE *pDevice, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestUnlockHelper); HRESULT hr = S_OK; LockInfo *pLockInfo = NULL; BOOL bDidNotUnlock = TRUE; hr = CheckDeviceInfo(pDevice); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::RequestUnlockHelper, CheclDeviceInfo() failed with hr=%x", hr)); return hr; } pLockInfo = (LockInfo*) pDevice->m_pLockInfo; // // Special case exists if device has been marked for removal. In this // case, we want to unlock now (definitely not schedule for later). // if (pDevice->QueryFlags() & STIMON_AD_FLAG_REMOVING) { if (pLockInfo) { if (pLockInfo->bDeviceIsLocked) { UnlockDevice(pDevice); } hr = ClearLockInfo(pLockInfo); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::RequestUnlockHelper, could not clear lock information")); } } return hr; } // // Decrement the usage count. If usage count == 0, then reset the // lock information, but don't actually unlock. (To improve (burst) // performance, we will hold onto the lock for a maximum idle period // specified by pLockInfo->lHoldingTime). Only if lHoldingTime is 0, // do we unlock straightaway. // if (pLockInfo->lInUse > 0) { pLockInfo->lInUse--; } if (pLockInfo->lInUse <= 0) { // // Only unlock if holding is 0 and we're in the server process. // if ((pLockInfo->lHoldingTime == 0) && bInServerProcess) { UnlockDevice(pDevice); bDidNotUnlock = FALSE; } hr = ClearLockInfo(pLockInfo); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::RequestUnlockHelper, failed to clear lock information")); } // // If we're not in the server process, nothing left to do, // so return. // if (!bInServerProcess) { pLockInfo->bDeviceIsLocked = FALSE; return hr; } } // // If we did not unlock the device, then schedule the unlock // callback to unlock it later. // if (bDidNotUnlock) { m_lSchedWaitTime = pLockInfo->lHoldingTime; if (!m_bSched) { if (ScheduleWorkItem((PFN_SCHED_CALLBACK) UnlockTimedCallback, this, m_lSchedWaitTime, NULL)) { m_bSched = TRUE; } } } return hr; } /**************************************************************************\ * CreateLockInfo * * Allocates and initializes a new LockInfo struct. * * Arguments: * * pDevice - The STI ACTIVE_DEVICE object. * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::CreateLockInfo(ACTIVE_DEVICE *pDevice) { DBG_FN(StiLockMgr::CreateLockInfo); HRESULT hr = S_OK; LockInfo *pLockInfo = NULL; USES_CONVERSION; // // Allocate memory for the structure // pLockInfo = (LockInfo*) LocalAlloc(LPTR, sizeof(LockInfo)); if (pLockInfo) { memset(pLockInfo, 0, sizeof(LockInfo)); pLockInfo->hDeviceIsFree = CreateEvent(NULL, FALSE, TRUE, NULL); if (pLockInfo->hDeviceIsFree) { // // Get any relevant lock information // pLockInfo->lHoldingTime = pDevice->m_DrvWrapper.getLockHoldingTime(); DBG_TRC(("StiLockMgr::CreateLockInfo, Lock holding time set to %d for device %S", pLockInfo->lHoldingTime, pDevice->GetDeviceID())); pLockInfo->lTimeLeft = pLockInfo->lHoldingTime; // // Everything's OK, so set the device's lock information // pDevice->m_pLockInfo = pLockInfo; } else { DBG_ERR(("StiLockMgr::CreateLockInfo, could not create event")); LocalFree(pLockInfo); hr = E_FAIL; } } else { DBG_ERR(("StiLockMgr::CreateLockInfo, out of memory")); hr = E_OUTOFMEMORY; } return hr; } /**************************************************************************\ * ClearLockInfo * * Clears information stored in a lockinfo struct. Also signals that the * device is free. Note: it does not unlock the device. * * Arguments: * * pLockInfo - a pointer to the device's LockInfo struct. * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::ClearLockInfo(LockInfo *pLockInfo) { DBG_FN(StiLockMgr::ClearLockInfo); // // Note: As much lock information is reset as can be done without // unlocking the device. This method is only called when lInuse // is 0, indicating that the device is no longer being actively used. // InterlockedExchange((LONG*)&pLockInfo->dwThreadId, 0); pLockInfo->lInUse = 0; // // Signal the device is free. // if (SetEvent(pLockInfo->hDeviceIsFree)) { return S_OK; } else { DBG_ERR(("StiLockMgr::ClearLockInfo, could not signal event")); return E_FAIL; } } /**************************************************************************\ * LockDevice * * Calls the USD to lock itsself and updates the relevant Lock information. * * Arguments: * * pDevice - pointer to the ACTIVE_DEVICE node * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::LockDevice(ACTIVE_DEVICE *pDevice) { DBG_FN(StiLockMgr::LockDevice); HRESULT hr = S_OK; LockInfo *pLockInfo = (LockInfo*)pDevice->m_pLockInfo; IStiUSD *pIStiUSD = NULL; __try { // // Check whether device is currently locked. We know that the device // is not busy, so it is safe to simply keep the lock open. This is how // we improve "burst" performance. // if (!pLockInfo->bDeviceIsLocked) { hr = pDevice->m_DrvWrapper.STI_LockDevice(); } else { DBG_TRC(("StiLockMgr::LockDevice, Device is already locked.")); } if (hr == S_OK) { pLockInfo->bDeviceIsLocked = TRUE; } else { HRESULT hres = E_FAIL; pLockInfo->bDeviceIsLocked = FALSE; hres = ClearLockInfo(pLockInfo); DBG_ERR(("StiLockMgr::LockDevice, USD error locking the device (0x%X)", hr)); } } __except (EXCEPTION_EXECUTE_HANDLER) { DBG_ERR(("StiLockMgr::LockDevice, exception")); hr = HRESULT_FROM_WIN32(GetExceptionCode()); } return hr; } /**************************************************************************\ * UnlockDevice * * Calls the USD to unlock itsself and updates the relevant Lock * information. * * Arguments: * * pDevice - pointer to the ACTIVE_DEVICE node * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::UnlockDevice(ACTIVE_DEVICE *pDevice) { DBG_FN(StiLockMgr::UnlockDevice); HRESULT hr = S_OK; LockInfo *pLockInfo = (LockInfo*)pDevice->m_pLockInfo; IStiUSD *pIStiUSD = NULL; __try { // // Unlock the device and mark that device has been unlocked. // hr = pDevice->m_DrvWrapper.STI_UnLockDevice(); if (SUCCEEDED(hr)) { pLockInfo->bDeviceIsLocked = FALSE; } if (hr != S_OK) { pLockInfo->bDeviceIsLocked = TRUE; DBG_ERR(("StiLockMgr::UnlockDevice, USD error unlocking the device")); } } __except (EXCEPTION_EXECUTE_HANDLER) { DBG_ERR(("StiLockMgr::UnlockDevice, exception")); hr = HRESULT_FROM_WIN32(GetExceptionCode()); } return hr; } HRESULT StiLockMgr::CheckDeviceInfo(ACTIVE_DEVICE *pDevice) { HRESULT hr = E_FAIL; TAKE_ACTIVE_DEVICE _tad(pDevice); if (!pDevice) { DBG_ERR(("StiLockMgr::CheckDeviceInfo, pDevice is NULL!")); return E_POINTER; } // // Check whether the device is valid and is not being removed // if (pDevice->IsValid() && !(pDevice->QueryFlags() & STIMON_AD_FLAG_REMOVING)) { // // Check whether lock information for this device exists yet. If // not, create a new LockInfo struct for this device. // if (pDevice->m_pLockInfo) { hr = S_OK; } else { hr = CreateLockInfo(pDevice); } } else { DBG_ERR(("StiLockMgr::CheckDeviceInfo, ACTIVE_DEVICE is not valid!")); hr = E_FAIL; } return hr; } #ifdef USE_ROT /**************************************************************************\ * WriteCookieNameToRegistry * * Writes the specified name to the registry. This is so clients know what * name to bind to when trying to get this instance of the Lock Manager * from the ROT (see Initialize). * * Arguments: * * szCookieName - string containing the cookie name * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ HRESULT StiLockMgr::WriteCookieNameToRegistry(CHAR *szCookieName) { HRESULT hr; HKEY hKey; LONG lErr; DWORD dwType = REG_SZ; DWORD dwSize = strlen(szCookieName) + 1; // // Write Lock Manager instance name to the registry. // lErr = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_STICONTROL, 0, TEXT(""), REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); if (lErr == ERROR_SUCCESS) { lErr = ::RegSetValueExA(hKey, REGSTR_VAL_LOCK_MGR_COOKIE_A, 0, dwType, (BYTE*) szCookieName, dwSize); if (lErr != ERROR_SUCCESS) { DBG_ERR(("StiLockMgr::WriteCookieNameToRegistry, could not write to registry")); } else { return S_OK; } RegCloseKey(hKey); } return E_FAIL; } /**************************************************************************\ * DeleteCookieFromRegistry * * Delete the cookie name from the registry. It is only needed while this * instance of the lock manager is running. * * Arguments: * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/ VOID StiLockMgr::DeleteCookieFromRegistry() { HRESULT hr; HKEY hKey; LONG lErr; // // Remove Lock Manager instance name from registry. // lErr = ::RegOpenKeyEx(HKEY_DYN_DATA, REGSTR_PATH_STICONTROL, 0, KEY_WRITE, &hKey); if (lErr == ERROR_SUCCESS) { lErr = ::RegDeleteValue(hKey, REGSTR_VAL_LOCK_MGR_COOKIE); RegCloseKey(hKey); } } #endif /**************************************************************************\ * AutoUnlock * * Scans the device list to check whether any device's idle time has * expired and needs to be unlocked. * * Arguments: * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/ VOID StiLockMgr::AutoUnlock() { EnumContext Ctx; m_bSched = FALSE; // // Enumerate through device list. At each locked device, update it's // lTimeLeft. If lTimeLeft has expired, unlock the device. // If not, mark that unlock callback needs to be scheduled. // // This logic is done in the UpdateLockInfoStatus method called by the // EnumDeviceCallback function on each device. // Ctx.This = this; Ctx.lShortestWaitTime = LONG_MAX; Ctx.bMustSchedule = FALSE; g_pDevMan->EnumerateActiveDevicesWithCallback(EnumDeviceCallback, &Ctx); // // Schedule next callback, if needed // if (Ctx.bMustSchedule && !m_bSched) { m_bSched = TRUE; m_lSchedWaitTime = Ctx.lShortestWaitTime; if (ScheduleWorkItem((PFN_SCHED_CALLBACK) UnlockTimedCallback, this, m_lSchedWaitTime, NULL)) { return; } else { DBG_ERR(("StiLockMgr::AutoUnlock, failed to schedule UnlockTimedCallback")); } } } /**************************************************************************\ * UpdateLockInfoStatus * * Updates a device's lock information. If the device's idle time has * expired, it is unlocked. If it is still busy, it's amount of idle time * left is updated. * * Arguments: * * pDevice - A pointer to the ACTIVE_DEVICE node. * pWaitTime - This is a pointer to the shortest wait time left. * This is used as the time the AutoUnlock() method * needs to re-schedule itsself. * pbMustSchedule - A pointer to a BOOL indicating whether the * AutoUnlock() needs to be re-scheduled. * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/ VOID StiLockMgr::UpdateLockInfoStatus(ACTIVE_DEVICE *pDevice, LONG *pWaitTime, BOOL *pbMustSchedule) { DBG_FN(UpdateLockInfoStatus); LockInfo *pLockInfo = NULL; DWORD dwWait; HRESULT hr = S_OK;; // // NOTE: Do not modify pWaitTime or pbMustSchedule unless a new wait // time is scheduled (see where pLockInfo->lTimeLeft < *pWaitTime) // // // Get a pointer to the lock information // if (pDevice) { pLockInfo = (LockInfo*) pDevice->m_pLockInfo; } if (!pLockInfo) { return; } // // Check whether device is free. If the device is busy, don't bother // scheduling a callback, since it will be rescheduled if needed when // the call to RequestUnlock is made. // dwWait = WaitForSingleObject(pLockInfo->hDeviceIsFree, 0); if (dwWait == WAIT_OBJECT_0) { // // Check whether device is locked (we're only interested in devices // that are locked). // if (pLockInfo->bDeviceIsLocked) { // // Decrease the amount of time left. If lTimeLeft <= 0, then no // idle time remains and device should be unlocked. // // If time does remain, check whether it is smaller than // the current wait time (pWaitTime). If it is smaller, mark // that this is the new wait time, and that the unlock callback // must be scheduled to unlock this later. // pLockInfo->lTimeLeft -= m_lSchedWaitTime; if (pLockInfo->lTimeLeft <= 0) { pLockInfo->lTimeLeft = 0; hr = UnlockDevice(pDevice); if (SUCCEEDED(hr)) { hr = ClearLockInfo(pLockInfo); return; } } else { if (pLockInfo->lTimeLeft < *pWaitTime) { *pWaitTime = pLockInfo->lTimeLeft; } *pbMustSchedule = TRUE; } } // // We are finished updating the information, so re-signal // that device is free. // SetEvent(pLockInfo->hDeviceIsFree); } } /**************************************************************************\ * EnumDeviceCallback * * This function is called once for every device in the ACTIVE_DEVICE * enumeration. * * Arguments: * * pDevice - A pointer to the ACTIVE_DEVICE node. * pContext - This is a pointer to an enumeration context. * The context contains a pointer to the Lock Manager, * the shortest wait time, and a bool indicating whether * AutoUnlock needs to be re-scheduled. * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/ VOID WINAPI EnumDeviceCallback(ACTIVE_DEVICE *pDevice, VOID *pContext) { DBG_FN(EnumDeviceCallback); EnumContext *pCtx = (EnumContext*) pContext; if (pCtx) { // // Update the lock status on this device // pCtx->This->UpdateLockInfoStatus(pDevice, &pCtx->lShortestWaitTime, &pCtx->bMustSchedule); } } /**************************************************************************\ * UnlockTimedCallback * * This function gets called when a lock is still active and may need to * be unlocked if the amount of idle time expires. * * Arguments: * * pArg - A pointer to the Lock Manager. * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/ VOID WINAPI UnlockTimedCallback(VOID *pArg) { DBG_FN(UnlockTimedCallback); StiLockMgr *pLockMgr = (StiLockMgr*) pArg; if (pLockMgr) { __try { pLockMgr->AutoUnlock(); } __except (EXCEPTION_EXECUTE_HANDLER) { #ifdef DEBUG OutputDebugStringA("Exception in UnlockTimedCallback"); #endif } } }