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.
1278 lines
33 KiB
1278 lines
33 KiB
/*******************************************************************************
|
|
*
|
|
* (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 <time.h>
|
|
#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
|
|
}
|
|
}
|
|
}
|
|
|