|
|
/*++
Copyright (c) 1996-1997 Microsoft Corporation
Module Name:
stidev.cpp
Abstract:
Code to maintain list of STI devices and poll them
Author:
Vlad Sadovsky (VladS) 11-Feb-1997 Byron Changuion (ByronC)
History:
12/15/1998 VladS "List invalidate" support for unknown ADD/REMOVE device messages 01/08/1999 VladS Support for PnP interface notifications 12/08/1999 VladS Initiate move to complete server environment by using direct calls to the driver 11/06/2000 ByronC Enabled JIT loading/unloading of drivers, and inactive enumeration
--*/
//
// Include Headers
//
#include "precomp.h"
#include "stiexe.h"
#include <rpc.h>
#include <userenv.h>
#include <mmsystem.h>
#include <stilib.h>
#include <validate.h>
#include "stiusd.h"
#include "device.h"
#include "conn.h"
#include "monui.h"
#include "wiapriv.h"
#include "lockmgr.h"
#include "fstidev.h"
#include <apiutil.h>
#include "enum.h"
#include "wiadevdp.h"
#define PRIVATE_FOR_NO_SERVICE_UI
//
// Flag passed to our persistent event handler to let it know
// whether the event is an STI device event or a WIA device event
//
#define STI_DEVICE_EVENT 0xF0000000
//
// Local defines and macros
//
//
// Delay hardware initialization till happy time.
//
#define POSTPONE_INIT2
//
// Global service window handle
extern HWND g_hStiServiceWindow; extern SERVICE_STATUS_HANDLE g_StiServiceStatusHandle;
//
// Validate guid of the event received from USD against the list of device events
//
#define VALIDATE_EVENT_GUID 1
//
// Static variables
//
//
//
// Linked list of maintained device objects, along with syncronization object
// to guard access to it
//
LIST_ENTRY g_DeviceListHead; CRIT_SECT g_DeviceListSync; BOOL g_fDeviceListInitialized = FALSE;
//
// REMOVE:
// Counter of active device objects
//
LONG g_lTotalActiveDevices = 0;
//
// Value of unique identified assigned to a device object to use as a handle.
//
LONG g_lGlobalDeviceId; // Use temporarily to identify opened device
//
// Connection list , used in connection objects methods, initialize here
//
extern LIST_ENTRY g_ConnectionListHead; extern LONG g_lTotalOpenedConnections ;
//
// Static function prototypes
//
EXTERN_C HANDLE GetCurrentUserTokenW( WCHAR Winsta[], DWORD DesiredAccess );
BOOL WINAPI DumpTokenInfo( LPTSTR pszPrefix, HANDLE hToken );
VOID WINAPI ScheduleDeviceCallback( VOID * pContext );
VOID WINAPI DelayedDeviceInitCallback( VOID * pContext );
VOID WINAPI AutoLaunchThread( LPVOID lpParameter );
VOID CleanApplicationsListForEvent( LPCTSTR pDeviceName, PDEVICEEVENT pDeviceEvent, LPCTSTR pAppName );
DWORD GetNumRegisteredApps( VOID );
BOOL inline IsWildCardEvent( PDEVICEEVENT pev ) /*++
Routine Description:
Check if given event app list represents wild-card
Arguments:
Return Value:
TRUE , FALSE
--*/
{ return !lstrcmp((LPCTSTR)pev->m_EventData,TEXT("*")); }
//
// Methods for device object
//
ACTIVE_DEVICE::ACTIVE_DEVICE(IN LPCTSTR lpszDeviceName, DEVICE_INFO *pInfo) {
DBG_FN("ACTIVE_DEVICE::ACTIVE_DEVICE");
USES_CONVERSION;
PSTI_DEVICE_INFORMATION pDevInfo;
HRESULT hres;
m_dwSignature = ADEV_SIGNATURE; m_fValid = FALSE;
m_fRefreshedBusOnFailure = FALSE;
m_hDeviceEvent = NULL; m_pLastLaunchEvent = NULL;
m_dwUserDisableNotifications = FALSE;
m_hDeviceInterface = NULL; m_hDeviceNotificationSink = NULL;
m_fLaunchableEventListNotEmpty = FALSE; m_dwSchedulerCookie = 0L; m_dwDelayedOpCookie = 0L;
m_uiPollFailureCount = 0L; m_dwLaunchEventTimeExpire = 0;
m_pLockInfo = NULL;
m_pFakeStiDevice = NULL; m_pRootDrvItem = NULL;
InitializeListHead(&m_ListEntry ); InitializeListHead(&m_ConnectionListHead); InitializeListHead(&m_DeviceEventListHead );
InterlockedIncrement(&g_lTotalActiveDevices);
SetFlags(0L);
if (!lpszDeviceName || !*lpszDeviceName) { ASSERT(("Trying to create device with invalid name", 0)); return; }
m_lDeviceId = InterlockedIncrement(&g_lGlobalDeviceId);
hres = m_DrvWrapper.Initialize(); if (FAILED(hres)) { m_fValid = FALSE; DBG_WRN(("ACTIVE_DEVICE::ACTIVE_DEVICE, Could not initialize driver wrapper class, marking this object invalid")); return; } m_DrvWrapper.setDevInfo(pInfo);
//
// Initialize device settings
//
GetDeviceSettings();
//
// object state is valid
//
m_fValid = TRUE;
//
// Check whether driver should be loaded on startup
//
if (!m_DrvWrapper.getJITLoading()) {
// Also note that if the device is not active, we simply do not load
// the driver, regardless of whether it's JIT or not.
//
if (m_DrvWrapper.getDeviceState() & DEV_STATE_ACTIVE) { //
// Note that this object is still valid, even if driver cannot be loaded.
// For example, driver will not be loaded if device is not plugged in.
//
LoadDriver(); } }
DBG_TRC(("Created active device object for device (%ws)",GetDeviceID()));
return;
} /* eop constructor */
ACTIVE_DEVICE::~ACTIVE_DEVICE( VOID ) { DBG_FN(ACTIVE_DEVICE::~ACTIVE_DEVICE);
//LIST_ENTRY * pentry;
STI_CONN *pConnection = NULL;
//
// When we are coming to a destructor it is assumed no current usage by any
// other thread. We don't need to lock device object therefore.
//
//
if (!IsValid() ) { return; }
DBG_TRC(("Removing device object for device(%ws)", GetDeviceID()));
//
// Mark device object as being removed
//
SetFlags(QueryFlags() | STIMON_AD_FLAG_REMOVING);
//
// Stop PnP notifications if is enabled
//
StopPnPNotifications();
//
// Remove any delayed operation from scheduler queue
//
if (m_dwDelayedOpCookie) { RemoveWorkItem(m_dwDelayedOpCookie); m_dwDelayedOpCookie = 0; }
//
// Unload the driver.
//
UnLoadDriver(TRUE);
//
// Destroy Fake Sti Device
//
if (m_pFakeStiDevice) { delete m_pFakeStiDevice; m_pFakeStiDevice = NULL; }
//
// Destroy Lock information
//
if (m_pLockInfo) { LockInfo *pLockInfo = (LockInfo*) m_pLockInfo; if (pLockInfo->hDeviceIsFree != NULL) { CloseHandle(pLockInfo->hDeviceIsFree); pLockInfo->hDeviceIsFree = NULL; } LocalFree(m_pLockInfo); m_pLockInfo = NULL; }
//
// Delete all connection objects
//
{ while (!IsListEmpty(&m_ConnectionListHead)) {
pConnection = CONTAINING_RECORD( m_ConnectionListHead.Flink , STI_CONN, m_DeviceListEntry );
if (pConnection->IsValid()) { DestroyDeviceConnection(pConnection->QueryID(),TRUE); } } }
//
// Remove from scheduled list if still there
//
if (m_ListEntry.Flink &&!IsListEmpty(&m_ListEntry)) {
ASSERT(("Device is destructed, but still on the list", 0));
RemoveEntryList(&m_ListEntry); InitializeListHead( &m_ListEntry ); }
//
// Close the device's event handle
//
if (m_hDeviceEvent) { CloseHandle(m_hDeviceEvent); m_hDeviceEvent = NULL; }
m_dwSignature = ADEV_SIGNATURE_FREE;
//
// We are gone completely
//
InterlockedDecrement(&g_lTotalActiveDevices); } /* eop destructor */
//
// IUnknown methods. Used only for reference counting
//
STDMETHODIMP ACTIVE_DEVICE::QueryInterface( REFIID riid, LPVOID * ppvObj) { return E_FAIL; }
STDMETHODIMP_(ULONG) ACTIVE_DEVICE::AddRef( void) { ::InterlockedIncrement(&m_cRef);
//DBG_TRC(("Device(%x)::AddRef RefCount=%d "),this,m_cRef);
return m_cRef; }
STDMETHODIMP_(ULONG) ACTIVE_DEVICE::Release( void) { LONG cNew;
//DBG_TRC(("Device(%x)::Release (before) RefCount=%d "),this,m_cRef);
if(!(cNew = ::InterlockedDecrement(&m_cRef))) { delete this; }
return cNew;
}
VOID ACTIVE_DEVICE:: GetDeviceSettings( VOID ) /*++
Routine Description:
Get settings for the device being opened , for future use. This routine also marks whether the driver should be loaded on startup, or JIT
Arguments:
Return Value:
--*/ { DBG_FN(ACTIVE_DEVICE::GetDeviceSettings);
//
// Build device event list
//
m_fLaunchableEventListNotEmpty = BuildEventList(); if (!m_fLaunchableEventListNotEmpty) { DBG_TRC(("ACTIVE_DEVICE::GetDeviceSettings, Device registry indicates no events for %ws ", GetDeviceID())); }
//
// Get value of poll timeout if device is polled
//
m_dwPollingInterval = m_DrvWrapper.getPollTimeout(); if (m_dwPollingInterval < STIDEV_MIN_POLL_TIME) { m_dwPollingInterval = g_uiDefaultPollTimeout; }
HRESULT hr = S_OK; DWORD dwType = REG_DWORD; DWORD dwSize = sizeof(m_dwUserDisableNotifications);
//
// Always read this value from the registry, just in case user changed it.
//
hr = g_pDevMan->GetDeviceValue(this, STI_DEVICE_VALUE_DISABLE_NOTIFICATIONS, &dwType, (BYTE*) &m_dwUserDisableNotifications, &dwSize);
/* TDB: When we can load normal drivers JIT
//
// Decide whether driver should be loaded on Startup or JIT. Our rules for
// determining this are:
// 1. If devices is not capable of generating ACTION events, then load it JIT
// 2. If notifications for this device are disabled, then load JIT
// 3. In all other cases, load on startup.
//
if (!m_fLaunchableEventListNotEmpty || m_dwUserDisableNotifications) { m_DrvWrapper.setJITLoading(TRUE); DBG_TRC(("ACTIVE_DEVICE::GetDeviceSettings, Driver will be loaded JIT")); } else { m_DrvWrapper.setJITLoading(FALSE); DBG_TRC(("ACTIVE_DEVICE::GetDeviceSettings, Driver will be loaded on startup")); } */
//
// Decide whether driver should be loaded on Startup or JIT. Until we enable for
// normal drivers like the comments above, our descision is based on:
// 1) Is this a volume device? If so, then load JIT
//
if (m_DrvWrapper.getInternalType() & INTERNAL_DEV_TYPE_VOL) { m_DrvWrapper.setJITLoading(TRUE); DBG_TRC(("ACTIVE_DEVICE::GetDeviceSettings, Driver will be loaded JIT")); } else { m_DrvWrapper.setJITLoading(FALSE); DBG_TRC(("ACTIVE_DEVICE::GetDeviceSettings, Driver will be loaded on startup")); }
}
BOOL ACTIVE_DEVICE:: LoadDriver( BOOL bReReadDevInfo /*= FALSE*/ ) /*++
Routine Description:
Arguments:
Return Value:
--*/ {
USES_CONVERSION;
HRESULT hres = E_FAIL; DEVICE_INFO *pDeviceInfo = NULL;
//
// We don't want to re-load the driver if it's already loaded.
//
if (m_DrvWrapper.IsDriverLoaded()) { return TRUE; }
if (m_DrvWrapper.IsPlugged()) {
HKEY hKeyDevice = g_pDevMan->GetDeviceHKey(this, NULL);
//
// If asked, re-read the device information. The easiest way to do this
// is to re-create the Dev. Info. structure.
//
if (bReReadDevInfo) {
pDeviceInfo = m_DrvWrapper.getDevInfo(); if (pDeviceInfo) { //
// This only applies to non-volume devices. Volume devices' dev. info.
// stucts are always re-created on enumeration, so are always current.
//
if (!(pDeviceInfo->dwInternalType & INTERNAL_DEV_TYPE_VOL)) {
DEVICE_INFO *pNewDeviceInfo = NULL; //
// When calling CreateDevInfoFromHKey, make sure that the
// SP_DEVICE_INTERFACE_DATA parameter is NULL for
// DevNode devices and non-NULL for interface devices.
//
SP_DEVICE_INTERFACE_DATA *pspDevInterfaceData = NULL; if (pDeviceInfo->dwInternalType & INTERNAL_DEV_TYPE_INTERFACE) { pspDevInterfaceData = &(pDeviceInfo->spDevInterfaceData); }
pNewDeviceInfo = CreateDevInfoFromHKey(hKeyDevice, pDeviceInfo->dwDeviceState, &(pDeviceInfo->spDevInfoData), pspDevInterfaceData); //
// If we successfully created the new one, destroy the old one
// and set the new one as the DevInfo for this device.
// Otherwise, leave the old one intact.
//
if (pNewDeviceInfo) { DestroyDevInfo(pDeviceInfo); m_DrvWrapper.setDevInfo(pNewDeviceInfo); } } } }
//
// Get the device information pointer here, in case it was updated above.
//
pDeviceInfo = m_DrvWrapper.getDevInfo(); if (!pDeviceInfo) { DBG_ERR(("ACTIVE_DEVICE::LoadDriver, Cannot function with NULL Device Info.!")); return FALSE; }
DBG_TRC(("ACTIVE_DEVICE::LoadDriver, Device is plugged: about to load driver")); hres = m_DrvWrapper.LoadInitDriver(hKeyDevice);
if (SUCCEEDED(hres)) {
//
// For those devices who may modify their Friendly Name (e.g. PTP),
// we refresh their settings here. Notice this only applies to
// "real", non-interface devices.
//
if (m_DrvWrapper.getInternalType() & INTERNAL_DEV_TYPE_REAL) {
if (pDeviceInfo && hKeyDevice) { RefreshDevInfoFromHKey(pDeviceInfo, hKeyDevice, pDeviceInfo->dwDeviceState, &(pDeviceInfo->spDevInfoData), &(pDeviceInfo->spDevInterfaceData));
//
// Also update the Friendly Name in device manager for
// non-interface (i.e. DevNode) devices.
// Note: Only do this if the Friendly name is non-NULL,
// not empty, and doesn't already exist in registry.
//
if (!(m_DrvWrapper.getInternalType() & INTERNAL_DEV_TYPE_INTERFACE)) {
//
// Check whether the friendly name exists
//
DWORD dwSize = 0; CM_Get_DevNode_Registry_Property(pDeviceInfo->spDevInfoData.DevInst, CM_DRP_FRIENDLYNAME, NULL, NULL, &dwSize, 0); if (dwSize == 0) {
//
// Check the our LocalName string is non-NULL and not empty
//
if (pDeviceInfo->wszLocalName && lstrlenW(pDeviceInfo->wszLocalName)) { CM_Set_DevNode_Registry_PropertyW(pDeviceInfo->spDevInfoData.DevInst, CM_DRP_FRIENDLYNAME, pDeviceInfo->wszLocalName, (lstrlenW(pDeviceInfo->wszLocalName) + 1) * sizeof(WCHAR), 0); } } }
} }
//
// Verify device capabilities require polling
//
if (m_DrvWrapper.getGenericCaps() & STI_GENCAP_NOTIFICATIONS) {
//
// Mark device object as receiving USD Notifications
//
SetFlags(QueryFlags() | STIMON_AD_FLAG_NOTIFY_CAPABLE);
//
// If timeout polling required, mark it.
//
if (m_DrvWrapper.getGenericCaps() & STI_GENCAP_POLLING_NEEDED) { DBG_TRC(("ACTIVE_DEVICE::LoadDriver, Polling device")); SetFlags(QueryFlags() | STIMON_AD_FLAG_POLLING); } else { DBG_TRC(("ACTIVE_DEVICE::LoadDriver, Device is marked for async events")); //
// No polling required - USD should support async events
//
if (!m_hDeviceEvent) { m_hDeviceEvent = CreateEvent( NULL, // Security
TRUE, // Manual reset
FALSE, // No signalled initially
NULL ); // Name
}
if (!m_hDeviceEvent) { ASSERT(("Failed to create event for notifications ", 0)); } m_dwPollingInterval = INFINITE; } }
//
// Set poll interval and initiate poll
//
SetPollingInterval(m_dwPollingInterval);
DBG_TRC(("Polling interval is set to %d sec on device (%ws)", (m_dwPollingInterval == INFINITE) ? -1 : (m_dwPollingInterval/1000), GetDeviceID()));
//
// Schedule EnableDeviceNotifications() and device reset
//
#ifdef POSTPONE_INIT2
SetFlags(QueryFlags() | STIMON_AD_FLAG_DELAYED_INIT);
//
// Constructor of active device object is called with global list critical section taken
// so we want to get out of here as soon as possible
//
m_dwDelayedOpCookie = ScheduleWorkItem((PFN_SCHED_CALLBACK) DelayedDeviceInitCallback, this, STIDEV_DELAYED_INTIT_TIME, NULL); if (!m_dwDelayedOpCookie) {
DBG_ERR(("Could not schedule EnableNotificationsCallback")); } #else
{ TAKE_ACTIVE_DEVICE _t(this);
EnableDeviceNotifications(); } #endif
if (!m_pFakeStiDevice) { //
// Set the Fake Sti Device for WIA clients
//
m_pFakeStiDevice = new FakeStiDevice(); if (m_pFakeStiDevice) { //
// Note that this form of init cannot fail
//
m_pFakeStiDevice->Init(this); } } }
if (IsValidHANDLE(hKeyDevice)) { RegCloseKey(hKeyDevice); hKeyDevice = NULL; }
return TRUE; } else { DBG_TRC(("ACTIVE_DEVICE::LoadDriver, Device is unplugged: not loading driver")); }
return FALSE; }
BOOL ACTIVE_DEVICE:: UnLoadDriver( BOOL bForceUnLoad ) /*++
Routine Description:
Arguments:
Return Value:
--*/ { DBG_FN(ACTIVE_DEVICE::UnloadSTIDevice);
BOOL bUnLoadDriver = FALSE;
//
// Decide whether driver should be unloaded. If bForceUnLoad == TRUE,
// we always unload.
//
if (bForceUnLoad) { bUnLoadDriver = TRUE; } else { //
// If this device is JIT, and there are no pending connections,
// unload it.
//
if (m_DrvWrapper.getJITLoading() && !m_DrvWrapper.getWiaClientCount()) { bUnLoadDriver = TRUE; } }
if (bUnLoadDriver) {
//
// Disable device notifications
//
DisableDeviceNotifications();
HRESULT hr = S_OK;
if (m_DrvWrapper.IsDriverLoaded()) { __try { hr = g_pStiLockMgr->RequestLock(this, INFINITE); // Should this be infinite?????
//
// Make sure we call drvUninitializeWia on any connected App. Items
//
if (m_pRootDrvItem) { m_pRootDrvItem->CallDrvUninitializeForAppItems(this); m_pRootDrvItem = NULL; } } __finally { //
// Call USD's unlock, before unloading driver. Notice that we
// don't call g_pStiLockMgr->RequestUnlock(..). This is to avoid
// a race condition between us calling RequestUnlock and
// unloading the driver. This way, the device is always locked
// for mutally exclusive acess, including when we call
// m_DrvWrapper.UnLoadDriver(). Our subsequent call to
// g_pStiLockMgr->ClearLockInfo(..) then clears the service
// lock we just acquired.
//
if (SUCCEEDED(hr)) { hr = g_pStiLockMgr->UnlockDevice(this); } } //
// Unload the driver.
//
m_DrvWrapper.UnLoadDriver();
//
// Clear the USD lock information
//
if (m_pLockInfo) { g_pStiLockMgr->ClearLockInfo((LockInfo*) m_pLockInfo); } } }
return TRUE; }
BOOL ACTIVE_DEVICE:: BuildEventList( VOID ) /*++
Routine Description:
Loads list of device notifications, which can activate application launch If device generates notification, which is not in this list, no app will start
Arguments:
Return Value:
TRUE if successfully built event list with at least one launchable application
FALSE on error or if there are no applications to be launched for any events on this device
--*/ {
DEVICEEVENT * pDeviceEvent;
TCHAR szTempString[MAX_PATH]; DWORD dwSubkeyIndex = 0;
BOOL fRet = FALSE;
UINT cEventsRead = 0; UINT cEventsLaunchables = 0;
HKEY hDevKey = NULL; HRESULT hr = S_OK;
//
// Get the device key
//
hDevKey = g_pDevMan->GetDeviceHKey(this, NULL); if (!hDevKey) { DBG_WRN(("Could not open device key for (%ws) hr = 0x%X.",GetDeviceID(), hr)); return FALSE; }
//
// Open the events sub-key
//
RegEntry reEventsList(EVENTS,hDevKey); StiCString strEventSubKeyName;
strEventSubKeyName.GetBufferSetLength(MAX_PATH); dwSubkeyIndex = 0;
//
// Clear existing list first
//
DestroyEventList();
while (reEventsList.EnumSubKey(dwSubkeyIndex++,&strEventSubKeyName)) {
//
// Open new key for individual event
//
RegEntry reEvent((LPCTSTR)strEventSubKeyName,reEventsList.GetKey()); if (!reEvent.IsValid()) { // ASSERT
continue; }
//
// Allocate and fill event structure
//
pDeviceEvent = new DEVICEEVENT; if (!pDeviceEvent) { // ASSERT
break; }
cEventsRead++;
//
// Read and parse event guid
//
pDeviceEvent->m_EventGuid = GUID_NULL; pDeviceEvent->m_EventSubKey.CopyString(strEventSubKeyName);
*szTempString = TEXT('\0'); reEvent.GetString(TEXT("GUID"),szTempString,sizeof(szTempString));
if (!IS_EMPTY_STRING(szTempString)) { ParseGUID(&pDeviceEvent->m_EventGuid,szTempString); }
//
// Get event descriptive name
//
*szTempString = TEXT('\0'); reEvent.GetString(TEXT(""),szTempString,sizeof(szTempString));
pDeviceEvent->m_EventName.CopyString(szTempString);
//
// Get applications list
//
*szTempString = TEXT('\0'); reEvent.GetString(TEXT("LaunchApplications"),szTempString,sizeof(szTempString));
pDeviceEvent->m_EventData.CopyString(szTempString);
//
// Mark launchability of the event
//
pDeviceEvent->m_fLaunchable = (BOOL)reEvent.GetNumber(TEXT("Launchable"),(long)TRUE);
if (pDeviceEvent->m_fLaunchable && (pDeviceEvent->m_EventData.GetLength()!= 0 )) { cEventsLaunchables++; fRet = TRUE; }
//
// Finally insert filled structure into the list
//
InsertTailList(&m_DeviceEventListHead, &(pDeviceEvent->m_ListEntry));
} // end while
if (hDevKey) { RegCloseKey(hDevKey); hDevKey = NULL; } DBG_TRC(("Reading event list for device:%ws Total:%d Launchable:%d ", GetDeviceID(), cEventsRead, cEventsLaunchables));
return fRet;
} // endproc BuildEventList
BOOL ACTIVE_DEVICE:: DestroyEventList( VOID ) { //
// Destroy event list
//
LIST_ENTRY * pentry; DEVICEEVENT * pDeviceEvent;
while (!IsListEmpty(&m_DeviceEventListHead)) {
pentry = m_DeviceEventListHead.Flink;
//
// Remove from the list ( reset list entry )
//
RemoveHeadList(&m_DeviceEventListHead); InitializeListHead( pentry );
pDeviceEvent = CONTAINING_RECORD( pentry, DEVICEEVENT,m_ListEntry );
delete pDeviceEvent; }
return TRUE; }
BOOL ACTIVE_DEVICE::DoPoll(VOID) { USES_CONVERSION;
HRESULT hres; BOOL fDeviceEventDetected = FALSE; STINOTIFY sNotify;
//
// verify state of the device object
//
if (!IsValid() || !m_DrvWrapper.IsDriverLoaded() || !(QueryFlags() & (STIMON_AD_FLAG_POLLING | STIMON_AD_FLAG_NOTIFY_RUNNING))) { DBG_WRN(("Polling on non-activated or non-polled device.")); return FALSE; }
m_dwSchedulerCookie = 0;
//
// Lock device to get status information
//
{ hres = g_pStiLockMgr->RequestLock(this, STIMON_AD_DEFAULT_WAIT_LOCK);
if (SUCCEEDED(hres) ) {
ZeroMemory(&m_DevStatus,sizeof(m_DevStatus)); m_DevStatus.StatusMask = STI_DEVSTATUS_EVENTS_STATE;
DBG_TRC(("Polling called on device:%ws", GetDeviceID()));
hres = m_DrvWrapper.STI_GetStatus(&m_DevStatus); if (SUCCEEDED(hres) ) { //
// If event detected, ask USD for additional information and
// unlock device
//
if (m_DevStatus.dwEventHandlingState & STI_EVENTHANDLING_PENDING ) {
fDeviceEventDetected = TRUE;
if (!FillEventFromUSD(&sNotify)) { DBG_WRN(("Device driver claimed presence of notification, but failed to fill notification block")); } }
// Reset failure skip count
m_uiPollFailureCount = STIDEV_POLL_FAILURE_REPORT_COUNT;
} else {
//
// Report error not on each polling attempt.
//
if (!m_uiPollFailureCount) {
DBG_ERR(("Device (%ws) failed get status for events. HResult=(%x)", GetDeviceID(), hres)); m_uiPollFailureCount = STIDEV_POLL_FAILURE_REPORT_COUNT;
//
// Too many subsequent polling failures - time to refresh device parent.
// Do it only once and only if failures had been due to device absence
//
if (hres == STIERR_DEVICE_NOTREADY) {
//
// Stop polling on inactive device.
// Nb: there is no way currently to restart polling
//
DBG_TRC(("Device not ready ,stopping notifications for device (%ws)",GetDeviceID()));
//
// First turn off running flag
//
m_dwFlags &= ~STIMON_AD_FLAG_NOTIFY_RUNNING;
if (g_fRefreshDeviceControllerOnFailures && !m_fRefreshedBusOnFailure ) {
DBG_WRN(("Too many polling failures , refreshing parent object for the device ")); // TDB:
//hres = g_pSti->RefreshDeviceBus(T2W((LPTSTR)GetDeviceID()));
m_fRefreshedBusOnFailure = TRUE; } } }
m_uiPollFailureCount--; }
hres = g_pStiLockMgr->RequestUnlock(this); if(FAILED(hres)) { DBG_ERR(("Failed to unlock device, hr = %x", hres)); } } else { DBG_ERR(("Device locked , could not get status . HResult=(%x)",hres)); }
} /* end block on GetLockMgrDevice */
//
// If successfully detected device event and filled notification information -
// proceed to processing method
//
if (fDeviceEventDetected) { ProcessEvent(&sNotify); }
//
// Schedule next poll, unless notifications disabled
//
if (m_dwFlags & STIMON_AD_FLAG_NOTIFY_RUNNING) { m_dwSchedulerCookie = ::ScheduleWorkItem( (PFN_SCHED_CALLBACK) ScheduleDeviceCallback, (LPVOID)this, m_dwPollingInterval, m_hDeviceEvent );
if ( !m_dwSchedulerCookie ){ ASSERT(("Polling routine could not schedule work item", 0)); return FALSE; } }
return TRUE;
} /* eop DoPoll */
BOOL ACTIVE_DEVICE::DoAsyncEvent(VOID) {
HRESULT hres; BOOL fRet;
BOOL fDeviceEventDetected = FALSE; STINOTIFY sNotify;
DBG_FN(ACTIVE_DEVICE::DoAsyncEvent); //
// verify state of the device object
//
if (!IsValid() || !m_DrvWrapper.IsDriverLoaded() || !(QueryFlags() & STIMON_AD_FLAG_NOTIFY_RUNNING)) { DBG_WRN(("Async event on non-activated device.")); return FALSE; }
m_dwSchedulerCookie = 0;
//
// Lock device to get event information
//
hres = g_pStiLockMgr->RequestLock(this, STIMON_AD_DEFAULT_WAIT_LOCK); if (SUCCEEDED(hres) ) { //
// If event detected, ask USD for additional information and
// unlock device
//
if (!FillEventFromUSD(&sNotify)) { DBG_WRN(("Device driver claimed presence of notification, but failed to fill notification block ")); }
g_pStiLockMgr->RequestUnlock(this);
fDeviceEventDetected = TRUE;
} else { DBG_TRC(("Device locked , could not get status . HResult=(%x)",hres)); }
//
// If successfully detected device event and filled notification information -
// proceed to processing method
//
if (fDeviceEventDetected) { ProcessEvent(&sNotify); }
//
// Schedule next event unless polling disabled
//
if (m_dwFlags & STIMON_AD_FLAG_NOTIFY_RUNNING) {
fRet = FALSE;
if (m_hDeviceEvent) { ::ResetEvent(m_hDeviceEvent); fRet = SetHandleForUSD(m_hDeviceEvent); }
if (!fRet) { DBG_ERR(("USD refused to take event handle , or event was not created ")); ReportError(ERROR_INVALID_PARAMETER); return FALSE; }
m_dwSchedulerCookie = ::ScheduleWorkItem( (PFN_SCHED_CALLBACK) ScheduleDeviceCallback, (LPVOID)this, m_dwPollingInterval, m_hDeviceEvent ); if ( !m_dwSchedulerCookie ){ ASSERT(("Async routine could not schedule work item", 0)); return FALSE; } }
return TRUE;
} /* eop DoAsyncEvent */
DWORD ACTIVE_DEVICE:: DisableDeviceNotifications( VOID ) { //
// First turn off running and enable flags
//
DBG_TRC(("Request to disable notifications for device (%S)",GetDeviceID()));
m_dwFlags &= ~STIMON_AD_FLAG_NOTIFY_ENABLED;
StopNotifications();
return TRUE;
}
DWORD ACTIVE_DEVICE:: EnableDeviceNotifications( VOID ) {
DWORD dwRet = FALSE;
m_dwFlags |= STIMON_AD_FLAG_NOTIFY_ENABLED;
DBG_TRC(("Request to enable notifications for device (%S)",GetDeviceID()));
if (NotificationsNeeded()) {
dwRet = StartRunningNotifications(); } else { DBG_TRC(("No notifications support needed for this device (%S)",GetDeviceID())); }
return dwRet; }
DWORD ACTIVE_DEVICE:: StopNotifications( VOID ) { BOOL fNotificationsOn = FALSE;
fNotificationsOn = (m_dwFlags & STIMON_AD_FLAG_NOTIFY_RUNNING ) ? TRUE : FALSE;
DBG_TRC(("Stopping notifications for device (%S)",GetDeviceID()));
//
// First turn off running and enable flags
//
m_dwFlags &= ~STIMON_AD_FLAG_NOTIFY_RUNNING;
//
// Remove from scheduler list
//
if (fNotificationsOn || m_dwSchedulerCookie) { RemoveWorkItem(m_dwSchedulerCookie); m_dwSchedulerCookie = NULL; }
//
// Clear event handle for USD
//
if ((m_DrvWrapper.IsDriverLoaded()) && (m_dwFlags & STIMON_AD_FLAG_NOTIFY_CAPABLE ) && !(m_dwFlags & STIMON_AD_FLAG_POLLING) ) { SetHandleForUSD(NULL); }
return TRUE;
}
DWORD ACTIVE_DEVICE:: StartRunningNotifications( VOID ) {
BOOL fRet = FALSE;
if (!(m_dwFlags & STIMON_AD_FLAG_NOTIFY_CAPABLE )) { // Device is not capable of notifications
DBG_WRN(("Trying to run notifications on non capable device ")); return FALSE; }
//
// If not enabled for notifications - return
//
if ( !(m_dwFlags & STIMON_AD_FLAG_NOTIFY_ENABLED )) { DBG_TRC(("Trying to run notifications on device (%S), disabled for notifications", GetDeviceID())); ReportError(ERROR_SERVICE_DISABLED); return FALSE; }
if ( m_dwFlags & STIMON_AD_FLAG_NOTIFY_RUNNING ) { ASSERT(("Notifications enabled, but cookie ==0", m_dwSchedulerCookie)); return TRUE; }
if (!IsDeviceAvailable()) { ReportError(ERROR_NOT_READY); return FALSE; }
//
// We are starting receiving notifications first time, flush events from USD
//
FlushDeviceNotifications();
//
// Set event handle for USD if it is capable of async notifications
//
if ( (m_dwFlags & STIMON_AD_FLAG_NOTIFY_CAPABLE ) && !(m_dwFlags & STIMON_AD_FLAG_POLLING) ) {
fRet = FALSE;
if (m_hDeviceEvent) { ::ResetEvent(m_hDeviceEvent); fRet = SetHandleForUSD(m_hDeviceEvent); }
if (!fRet) { DBG_ERR(("USD refused to take event handle , or event was not created ")); ReportError(ERROR_INVALID_PARAMETER); return FALSE; } }
fRet = FALSE;
//
// Starting accepting notifications - mark refresh flag as not done yet
//
m_fRefreshedBusOnFailure = FALSE;
//
// Schedule event processing for this device
//
m_dwSchedulerCookie = ::ScheduleWorkItem( (PFN_SCHED_CALLBACK) ScheduleDeviceCallback, (LPVOID)this, m_dwPollingInterval, m_hDeviceEvent );
if ( m_dwSchedulerCookie ){
m_dwFlags |= STIMON_AD_FLAG_NOTIFY_RUNNING;
DBG_TRC(("Started receiving notifications for device (%S)",GetDeviceID()));
fRet = TRUE; }
return fRet;
} /* eop StartRunningNotifications */
BOOL ACTIVE_DEVICE:: FlushDeviceNotifications( VOID ) /*++
Routine Description:
Arguments:
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/ { HRESULT hres; STINOTIFY sNotify;
//
// verify state of the device object
//
if (!IsValid() || m_DrvWrapper.IsDriverLoaded() || !(QueryFlags() & (STIMON_AD_FLAG_NOTIFY_ENABLED ))) { return FALSE; }
//
// Lock device to get status information
//
hres = g_pStiLockMgr->RequestLock(this, STIMON_AD_DEFAULT_WAIT_LOCK); if (SUCCEEDED(hres) ) {
ZeroMemory(&m_DevStatus,sizeof(m_DevStatus)); m_DevStatus.StatusMask = STI_DEVSTATUS_EVENTS_STATE;
hres = m_DrvWrapper.STI_GetStatus(&m_DevStatus); if (SUCCEEDED(hres) ) { //
// If event detected, ask USD for additional information and
// unlock device
//
if (m_DevStatus.dwEventHandlingState & STI_EVENTHANDLING_PENDING ) { FillEventFromUSD(&sNotify); } } g_pStiLockMgr->RequestUnlock(this); }
return TRUE;
} /* eop FlushDeviceNotifications */
BOOL ACTIVE_DEVICE:: ProcessEvent( STINOTIFY *psNotify, BOOL fForceLaunch, // = FALSE
LPCTSTR pszAppName // =NULL
) /*++
Routine Description:
Is invoked when monitored device is issuing a device notification ( either by poll , or by signalling handle).
USD is called to obtain notification parameters. If device is already connected to, notification is passed to connection, currently in focus. If device is not connected to and notification is in the list of "launchable" , attempt is made to launch application, which will acquire image from the device
Arguments:
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/ {
PDEVICEEVENT pDeviceEvent = NULL;
DWORD dwCurrentTickCount;
HANDLE hThread; DWORD dwThread;
BOOL fRet;
STIMONWPRINTF(TEXT("Processing device notification for device (%s)"),GetDeviceID());
//
// Notify WIA of event if this is a valid WIA device event.
//
if (m_DrvWrapper.IsWiaDevice() && psNotify) {
HRESULT hr;
hr = NotifyWiaDeviceEvent(GetDeviceID(), &psNotify->guidNotificationCode, &psNotify->abNotificationData[0], 0, g_dwMessagePumpThreadId); if (hr == S_FALSE) {
//
// WIA has handled this event and doesn't want to
// chain the event to STI for further processing.
//
return TRUE; } }
if (!fForceLaunch ) { //
// If there is at least one connection to this device, pass notification
// information to connection in focus
//
if (IsConnectedTo() ) {
STIMONWPRINTF(TEXT("Notification delivered to subscriber"));
STI_CONN *pConnection = NULL; LIST_ENTRY *pentry;
pentry = m_ConnectionListHead.Flink; pConnection = CONTAINING_RECORD( pentry, STI_CONN,m_DeviceListEntry );
pConnection->QueueNotificationToProcess(psNotify);
return TRUE; } }
//
// Nobody connected to this device, so we need to see if associated
// application can be launched
// STIMONWPRINTF(TEXT("ProcessEvent received device notification, requiring auto launch."));
//
// Validate event against list of launchable events, associated with device object.
// If user explicitly disabled "events" for this device - don't launch anything
//
if (m_dwUserDisableNotifications || IsListEmpty(&m_DeviceEventListHead)) { // No active launchable events , associated with this device
STIMONWPRINTF(TEXT("User disabled events or event list is empty for the device, ignoring notification")); return FALSE; }
LIST_ENTRY * pentry; LIST_ENTRY * pentryNext;
for ( pentry = m_DeviceEventListHead.Flink; pentry != &m_DeviceEventListHead; pentry = pentryNext ) {
pentryNext = pentry->Flink; pDeviceEvent = CONTAINING_RECORD( pentry,DEVICEEVENT ,m_ListEntry );
if(IsEqualIID(pDeviceEvent->m_EventGuid,psNotify->guidNotificationCode)) { break; } else { pDeviceEvent = NULL; } }
if (!pDeviceEvent || !pDeviceEvent->m_fLaunchable) { // Not launchable event - don't do anything
DBG_TRC(("Did not recognize launchable event or event list is empty, notification ignored."));
#ifdef VALIDATE_EVENT_GUID
return FALSE; #else
pDeviceEvent = CONTAINING_RECORD( m_DeviceEventListHead.Flink,DEVICEEVENT ,m_ListEntry ); DBG_ERR(("Using first event in the list for interim testing")); #endif
}
//
// If we are already in after launch period - skip event
//
if (m_dwFlags & STIMON_AD_FLAG_LAUNCH_PENDING) {
dwCurrentTickCount = ::GetTickCount();
if ( dwCurrentTickCount < m_dwLaunchEventTimeExpire ) { DBG_TRC(("Waiting since last event had not expired yet, notification ignored")); ReportError(ERROR_NOT_READY); return FALSE; }
}
//
// Launching application may cause unpredictable delays . We don't want to hold
// main event processing thread , so kick in another dedicated thread to control
// process spawning. Before device lock is released, it is marked as waiting for pending
// launch, to prevent laucnchable events in quick succession from autolaunching
//
m_dwFlags |= STIMON_AD_FLAG_LAUNCH_PENDING; //
// Set waiting period expiration limit, so we know when to start paying attention to
// launch events again
//
m_dwLaunchEventTimeExpire = ::GetTickCount() + STIMON_AD_DEFAULT_WAIT_LAUNCH;
m_pLastLaunchEvent = pDeviceEvent;
//
// Complete the outstanding AsyncRPC call to the ShellHWDetection Service.
// This will inform it of the STI device event.
//
DEVICE_INFO *pDeviceInfo = m_DrvWrapper.getDevInfo(); if (pDeviceInfo && psNotify) { EnterCriticalSection(&g_RpcEvent.cs);
if(g_RpcEvent.pAsync) { RPC_STATUS status; int nReply = 1;
g_RpcEvent.pEvent->EventGuid = psNotify->guidNotificationCode; g_RpcEvent.pEvent->bstrEventDescription = SysAllocString(L""); g_RpcEvent.pEvent->bstrDeviceID = SysAllocString(pDeviceInfo->wszDeviceInternalName); g_RpcEvent.pEvent->bstrDeviceDescription = SysAllocString(pDeviceInfo->wszDeviceDescription); g_RpcEvent.pEvent->dwDeviceType = (DWORD) pDeviceInfo->DeviceType; g_RpcEvent.pEvent->bstrFullItemName = SysAllocString(NULL); //
// Make sure WiaRPC knows this is for an STI device, and not a WIA device.
//
g_RpcEvent.pEvent->ulEventType = STI_DEVICE_EVENT;
status = RpcAsyncCompleteCall(g_RpcEvent.pAsync, &nReply); if(status) { DBG_ERR(("RpcAsyncComplete failed with error 0x%x", status)); } else { DBG_ERR(("Completed RPC call")); } g_RpcEvent.pAsync = NULL; fRet = TRUE; } else { DBG_ERR(("Did not have pAsync for this event")); } LeaveCriticalSection(&g_RpcEvent.cs); }
//
// This code has been replaced in .NET Server with an AsyncRPC implementation
//
/*
PAUTO_LAUNCH_PARAM_CONTAINER pAutoContainer = new AUTO_LAUNCH_PARAM_CONTAINER; if (!pAutoContainer) { ReportError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; }
pAutoContainer->pActiveDevice = this; pAutoContainer->pLaunchEvent = pDeviceEvent; pAutoContainer->pAppName = pszAppName;
//
// If application name already requested, we will not display UI, so perform
// syncronous call.
//
if (pszAppName) { fRet = AutoLaunch(pAutoContainer);
delete pAutoContainer; } else {
//
// AddRef here to ensure we're not unloaded or destroyed while processing this
// event.
// Note: AutoLaunchThread must Release() this refcount.
//
AddRef(); hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AutoLaunchThread, (LPVOID)pAutoContainer, 0, &dwThread);
if ( hThread ) { ::CloseHandle(hThread); }
fRet = TRUE; } */
return fRet;
} // endproc ProcessEvent
BOOL ACTIVE_DEVICE:: AutoLaunch( PAUTO_LAUNCH_PARAM_CONTAINER pAutoContainer ) /*++
Routine Description:
Attempt to automatically launch application, which is associated with active device event
Arguments:
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/
{
PDEVICEEVENT pDeviceEvent ; BOOL fRet;
BOOL fChooseAgain; BOOL fFailedFirstSelection = FALSE; BOOL fFailedUserEnv = FALSE;
STRArray saAppList; STRArray saCommandLineList;
DWORD dwError;
pDeviceEvent = (pAutoContainer->pLaunchEvent) ? (pAutoContainer->pLaunchEvent) : m_pLastLaunchEvent;
if (!pDeviceEvent) { ASSERT(("No event available to AutoLaunch routine", 0)); return FALSE; }
ASSERT(m_dwFlags & STIMON_AD_FLAG_LAUNCH_PENDING);
//
// Attract user attention
//
#ifdef PLAYSOUND_ALWAYS
::PlaySound(TEXT("StillImageDevice"),NULL,SND_ALIAS | SND_ASYNC | SND_NOWAIT | SND_NOSTOP); #endif
//
// Nothing appears to be launched, so continue with it
// Retreive command line
m_strLaunchCommandLine.CopyString(TEXT("\0"));
fRet = FALSE;
fRet = RetrieveSTILaunchInformation(pDeviceEvent, pAutoContainer->pAppName, saAppList, saCommandLineList, fFailedFirstSelection ? TRUE : FALSE );
if (fRet) {
DEVICE_INFO *pDeviceInfo = pAutoContainer->pActiveDevice->m_DrvWrapper.getDevInfo();
if (pDeviceInfo) { HRESULT hr = S_OK; WCHAR *wszDest = NULL; LONG *plSize = NULL; IWiaEventCallback *pIEventCB = NULL; ULONG ulEventType = WIA_ACTION_EVENT;
//
// Variables used as parameters for the ImageEventCallback() method.
//
BSTR bstrEventDescription = SysAllocString((LPCTSTR)pDeviceEvent->m_EventName); BSTR bstrDeviceID = SysAllocString(pDeviceInfo->wszDeviceInternalName); BSTR bstrDeviceDescription = SysAllocString(pDeviceInfo->wszDeviceDescription); DWORD dwDeviceType = (DWORD) pDeviceInfo->DeviceType;
if (bstrEventDescription && bstrDeviceID && bstrDeviceDescription) { //
// Package the Application list and the relevant command line info in a double
// NULL terminated BSTR. First calculate the number of bytes this will take.
// Our calculations are made up as follows:
// For every item in the AppList, add space for App name plus terminating NULL.
// For every item in the CommandLineList, add space for command line plus terminating NULL.
// Lastly, add space for terminating NULL (ensuring that the list is double NULL terminated)
//
// NOTE: Assumption here is that RetrieveSTILaunchInformation returns saAppList and
// saCommandLineList to have the same number of elements.
//
INT iCount; LONG lSize = 0; for (iCount = 0; iCount < saAppList.GetSize(); iCount++) { lSize += (lstrlenW((LPCTSTR)*saAppList[iCount]) * sizeof(WCHAR)) + sizeof(L'\0'); lSize += (lstrlenW((LPCTSTR)*saCommandLineList[iCount]) * sizeof(WCHAR)) + sizeof(L'\0'); } lSize += sizeof(L'\0') + sizeof(LONG);
BSTR bstrAppList = SysAllocStringByteLen(NULL, lSize); if (bstrAppList) {
//
// Copy each null termintaed string into the BSTR (including the terminating null),
// and make sure the end is double terminated.
//
wszDest = bstrAppList; for (iCount = 0; iCount < saAppList.GetSize(); iCount++) { lstrcpyW(wszDest, (LPCTSTR)*saAppList[iCount]); wszDest += lstrlenW(wszDest) + 1; lstrcpyW(wszDest, (LPCTSTR)*saCommandLineList[iCount]); wszDest += lstrlenW(wszDest) + 1; } wszDest[0] = L'\0';
//
// CoCreate our event UI handler. Note that it will not display any UI
// if there is only one application.
//
hr = _CoCreateInstanceInConsoleSession( CLSID_StiEventHandler, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaEventCallback, (void**)&pIEventCB);
if (SUCCEEDED(hr)) {
//
// Make the callback.
//
hr = pIEventCB->ImageEventCallback(&pDeviceEvent->m_EventGuid, bstrEventDescription, bstrDeviceID, bstrDeviceDescription, dwDeviceType, bstrAppList, &ulEventType, 0); pIEventCB->Release(); if (FAILED(hr)) { DBG_ERR(("ACTIVE_DEVICE::AutoLaunch, could not launch STI event handler")); fRet = FALSE; } } SysFreeString(bstrAppList);
if (SUCCEEDED(hr)) {
//
// Application was launched
//
fRet = TRUE; } } else { DBG_ERR(("ACTIVE_DEVICE::AutoLaunch, Out of memory!")); fRet = FALSE; } } else { DBG_ERR(("ACTIVE_DEVICE::AutoLaunch, Out of memory!")); fRet = FALSE; } if (bstrEventDescription) { SysFreeString(bstrEventDescription); bstrEventDescription = NULL; } if (bstrDeviceID) { SysFreeString(bstrDeviceID); bstrDeviceID = NULL; } if (bstrDeviceDescription) { SysFreeString(bstrDeviceDescription); bstrDeviceDescription = NULL; } } else { DBG_WRN(("ACTIVE_DEVICE::AutoLaunch, Device Information is NULL, ignoring event")); fRet = FALSE; } } else {
DBG_WRN(("ACTIVE_DEVICE::AutoLaunch, Could not get command line to launch application")); // m_dwFlags &= ~STIMON_AD_FLAG_LAUNCH_PENDING;
fRet = FALSE; }
//
// Clear the launch pending flag
//
m_dwFlags &= ~STIMON_AD_FLAG_LAUNCH_PENDING;
return fRet; } // endproc AutoLaunch
BOOL ACTIVE_DEVICE:: RetrieveSTILaunchInformation( PDEVICEEVENT pev, LPCTSTR pAppName, STRArray& saAppList, STRArray& saCommandLine, BOOL fForceSelection // =FALSE
) /*++
Routine Description:
Get command line for automatic process launch.
Arguments:
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/ { //
// If all registered applications are allowed to start on this event
// create array of names from registered list. Otherwise , parse list of
// allowed applications
//
ReportError(NOERROR);
if (IsWildCardEvent(pev) || fForceSelection ) {
RegEntry re(REGSTR_PATH_REG_APPS,HKEY_LOCAL_MACHINE); RegEnumValues regenum(&re);
while (ERROR_SUCCESS == regenum.Next() ) { if ( ((regenum.GetType() == REG_SZ ) ||(regenum.GetType() == REG_EXPAND_SZ )) && !IS_EMPTY_STRING(regenum.GetName())) { saAppList.Add((LPCTSTR)regenum.GetName()); } } } else { //
// Split into array of strings
//
TokenizeIntoStringArray(saAppList, (LPCTSTR)pev->m_EventData, TEXT(',')); }
//
// Work with filled array of applications
//
if (saAppList.GetSize() < 1) { return FALSE; }
//
// If application name requested, validate it against available list. Otherwise proceed
// with UI
//
if (pAppName) {
//
// Search through the list of available applications for the name of requested
//
INT iCount; BOOL fFound = FALSE;
for (iCount = 0; iCount < saAppList.GetSize(); iCount++) { if (::lstrcmpi(pAppName,(LPCTSTR)*saAppList[iCount]) == 0) {
fFound = TRUE; } }
if (!fFound) { // Invalid application name requested
ReportError(ERROR_INVALID_PARAMETER); return FALSE; } else { //
// The app list should only contain this app's name, so remove all elements
// and add this one.
//
saAppList.RemoveAll(); saAppList.Add(pAppName); } }
//
// saAppList now contains the list of Applications to launch.
// We must fill saCommandLine with the relevant command lines.
//
INT iCount;
DBG_TRC(("Processing Device Event: AppList and CommandLines are:")); for (iCount = 0; iCount < saAppList.GetSize(); iCount++) {
//
// Format command line for execution
//
RegEntry re(REGSTR_PATH_REG_APPS,HKEY_LOCAL_MACHINE); StiCString strLaunchCommandLine;
TCHAR szRegCommandLine[2*255]; TCHAR szEventName[255] = {TEXT("")}; TCHAR *pszUuidString = NULL;
*szRegCommandLine = TEXT('\0'); re.GetString((LPCTSTR)*saAppList[iCount],szRegCommandLine,sizeof(szRegCommandLine));
if(!*szRegCommandLine) { DBG_WRN(("ACTIVE_DEVICE::RetrieveSTILaunchInformation, RegEntry::GetString failed!")); return FALSE; } if (UuidToString(&pev->m_EventGuid,(RPC_STRING *)&pszUuidString) != RPC_S_OK) { DBG_WRN(("ACTIVE_DEVICE::RetrieveSTILaunchInformation, UuidToString() failed!")); return FALSE; } ASSERT(pszUuidString);
wsprintf(szEventName,TEXT("{%s}"),pszUuidString ? (TCHAR *)pszUuidString :TEXT("")); strLaunchCommandLine.FormatMessage(szRegCommandLine,GetDeviceID(),szEventName);
//
// Add this to the list of Command Lines
//
saCommandLine.Add((LPCTSTR)strLaunchCommandLine);
if (pszUuidString) { RpcStringFree((RPC_STRING *)&pszUuidString); pszUuidString = NULL; }
DBG_PRT((" AppName = (%ls)", (LPCTSTR)*saAppList[iCount])); DBG_PRT((" CommandLine = (%ls)", (LPCTSTR)*saCommandLine[iCount])); };
//
// Check that saAppList and saCommandLine have the same number of elements
//
if (saAppList.GetSize() != saCommandLine.GetSize()) { DBG_WRN(("ACTIVE_DEVICE::RetrieveSTILaunchInformation, Application list and Command Line list have different number of elements!")); return FALSE; }
return TRUE; } // endproc RetrieveAutoLaunchCommandLine
BOOL ACTIVE_DEVICE:: IsDeviceAvailable( VOID ) /*++
Routine Description:
Returns TRUE if device is available for monitoring .
Arguments:
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/ { STI_DEVICE_STATUS sds; HRESULT hRes = STI_OK; BOOL bRet;
//
// Check valid state
//
if (!IsValid() || !m_DrvWrapper.IsDriverLoaded()) { return FALSE; }
//
// Get and analyze status information from active device
//
::ZeroMemory(&sds,sizeof(sds));
sds.StatusMask = STI_DEVSTATUS_ONLINE_STATE;
hRes = g_pStiLockMgr->RequestLock(this, 1000); if (SUCCEEDED(hRes)) { hRes = m_DrvWrapper.STI_GetStatus(&sds);
g_pStiLockMgr->RequestUnlock(this); } else {
//
// Ignore locking violation, as it may indicate device is in normal
// state
//
if ((STIERR_SHARING_VIOLATION == hRes) || (WIA_ERROR_BUSY == hRes) ) { hRes = STI_OK; //
// Since we couldn't talk to the device because someone else is speaking to it,
// we assume it's online
//
sds.dwOnlineState = STI_ONLINESTATE_OPERATIONAL; } }
bRet = SUCCEEDED(hRes) && (sds.dwOnlineState & STI_ONLINESTATE_OPERATIONAL) ;
DBG_TRC(("Request to check state on device (%S). RESULT:%s", GetDeviceID(),bRet ? "Available" : "Not available"));
return bRet;
} // endproc IsDeviceAvailable
BOOL ACTIVE_DEVICE:: RemoveConnection( STI_CONN *pConnection ) { TAKE_ACTIVE_DEVICE t(this);
STI_CONN *pExistingConnection = NULL; BOOL fRet = FALSE;
pExistingConnection = FindMyConnection((HANDLE)pConnection->QueryID());
if (pExistingConnection) {
DBG_TRC(("Device(%S) removing connection (%x)", GetDeviceID(), pConnection)); pConnection->DumpObject();
RemoveEntryList(&pConnection->m_DeviceListEntry);
//
// Reset flags on device object
//
if (pConnection->QueryOpenMode() & STI_DEVICE_CREATE_DATA) { SetFlags(QueryFlags() & ~STIMON_AD_FLAG_OPENED_FOR_DATA); }
SetFlags(QueryFlags() & ~STIMON_AD_FLAG_LAUNCH_PENDING);
//
// If this was the last connection, stop notifications on a device
//
if (!NotificationsNeeded()) { StopNotifications(); }
fRet = TRUE; } else { // No connection on this device list
DBG_ERR(("Removing connection not on the list for this device (%S)",GetDeviceID())); }
return fRet;
}
BOOL ACTIVE_DEVICE:: AddConnection( STI_CONN *pConnection ) /*++
Routine Description:
This function is called when new connection is requested from client to active device
Arguments:
--*/ { TAKE_ACTIVE_DEVICE t(this);
STI_CONN *pExistingConnection = NULL; BOOL fRet = FALSE;
pExistingConnection = FindMyConnection((HANDLE)pConnection->QueryID());
if (!pExistingConnection) {
//
// Check if we are not in data mode
//
if (pConnection->QueryOpenMode() & STI_DEVICE_CREATE_DATA) { if (QueryFlags() & STIMON_AD_FLAG_OPENED_FOR_DATA) { DBG_TRC(("Device(%x) is being opened second time in data mode",this)); ::SetLastError(ERROR_ACCESS_DENIED); return FALSE; }
SetFlags(QueryFlags() | STIMON_AD_FLAG_OPENED_FOR_DATA); }
//
// Add connection object to connected list
//
InsertHeadList(&m_ConnectionListHead,&pConnection->m_DeviceListEntry);
DBG_TRC(("Device(%S) added connection (%X) ", GetDeviceID(), pConnection));
pConnection->DumpObject();
//
// Set device object flags
//
SetFlags(QueryFlags() & ~STIMON_AD_FLAG_LAUNCH_PENDING);
//
// If notifications allowed, explicitly enable them.
//
if ( QueryFlags() & STIMON_AD_FLAG_NOTIFY_ENABLED ) { StartRunningNotifications(); }
fRet = TRUE; } else { // Already present - something is wrong
ASSERT(("Device adding connection which is already there ", 0)); }
return fRet; }
STI_CONN * ACTIVE_DEVICE:: FindMyConnection( HANDLE hConnection ) /*++
Routine Description:
This function is used to locate connection object from connection handle
Arguments:
--*/ {
LIST_ENTRY *pentry; LIST_ENTRY *pentryNext;
STI_CONN *pConnection = NULL;
HANDLE hInternalHandle = hConnection;
for ( pentry = m_ConnectionListHead.Flink; pentry != &m_ConnectionListHead; pentry = pentryNext ) {
pentryNext = pentry->Flink;
pConnection = CONTAINING_RECORD( pentry, STI_CONN,m_DeviceListEntry );
if (hInternalHandle == pConnection->QueryID()) { return pConnection; } }
return NULL; }
BOOL ACTIVE_DEVICE:: FillEventFromUSD( STINOTIFY *psNotify ) /*++
Routine Description:
This function is called after USD signalled presence of hardware event and if successful it returns event descriptor filled with information about event
Arguments:
--*/ { HRESULT hres;
psNotify->dwSize = sizeof STINOTIFY; psNotify->guidNotificationCode = GUID_NULL;
if (!m_DrvWrapper.IsDriverLoaded()) { ASSERT(("FillEventFromUSD couldn't find direct driver interface", 0)); return FALSE; }
hres = m_DrvWrapper.STI_GetNotificationData(psNotify);
return SUCCEEDED(hres) ? TRUE : FALSE;
}
BOOL ACTIVE_DEVICE:: SetHandleForUSD( HANDLE hEvent ) /*++
Routine Description:
This function is called to pass event handle to USD for later signalling in case of hardware event
Arguments:
pContext - pointer to device object
--*/ { HRESULT hres = E_FAIL;
if (!IsValid() || !m_DrvWrapper.IsDriverLoaded()) { ASSERT(("SetHandleForUSD couldn't find direct driver interface", 0)); return FALSE; }
//
// Ask device object for USD interface. Should get it because sti device aggregates
// USD
//
hres = m_DrvWrapper.STI_SetNotificationHandle(hEvent); if (hres == STIERR_UNSUPPORTED) { hres = S_OK; }
return SUCCEEDED(hres) ? TRUE : FALSE; }
BOOL ACTIVE_DEVICE:: IsEventOnArrivalNeeded( VOID ) /*++
Routine Description:
Returns TRUE if this device needs to generate event on arrival.
Conditions are: - Device successully initialized - Device capabilities ( static or dynamic) include appropriate bit - Device is capable and enabled for event generation
Arguments:
None
--*/ {
HRESULT hres; STI_USD_CAPS sUsdCaps; BOOL fRet;
fRet = FALSE;
ZeroMemory(&sUsdCaps,sizeof(sUsdCaps));
hres = m_DrvWrapper.STI_GetCapabilities(&sUsdCaps); if (SUCCEEDED(hres)) {
if ( (m_dwFlags & STIMON_AD_FLAG_NOTIFY_ENABLED ) && (m_dwFlags & STIMON_AD_FLAG_NOTIFY_CAPABLE ) ) {
//
// Check that either static or dynamic capabilities mask conatins needed bit
//
if ( (sUsdCaps.dwGenericCaps | m_DrvWrapper.getGenericCaps()) & STI_GENCAP_GENERATE_ARRIVALEVENT ) { fRet = TRUE; } } }
return fRet;
} //
BOOL ACTIVE_DEVICE:: InitPnPNotifications( HWND hwnd ) /*++
Routine Description:
Assumes device object to be locked
Arguments:
None
--*/ { BOOL fRet = FALSE;
#ifdef WINNT
//
// First, stop any existing PnP notifications
//
StopPnPNotifications();
WCHAR *wszInterfaceName = NULL; DWORD dwError;
//
// Get interface name for out device
//
wszInterfaceName = g_pDevMan->AllocGetInterfaceNameFromDevInfo(m_DrvWrapper.getDevInfo()); if (wszInterfaceName) {
//
// Open handle on this interface
//
m_hDeviceInterface = ::CreateFileW(wszInterfaceName, GENERIC_READ, // Access
0, // Share mode
NULL, // Sec attributes
OPEN_EXISTING, // Disposition
0, // Attributes
NULL // Template file
); if (IS_VALID_HANDLE(m_hDeviceInterface)) { //
// Register to receive PnP notifications on interface handle
//
DEV_BROADCAST_HDR *psh; DEV_BROADCAST_HANDLE sNotificationFilter;
//
// Register to receive device notifications from PnP
//
psh = (DEV_BROADCAST_HDR *)&sNotificationFilter;
psh->dbch_size = sizeof(DEV_BROADCAST_HANDLE); psh->dbch_devicetype = DBT_DEVTYP_HANDLE; psh->dbch_reserved = 0;
sNotificationFilter.dbch_handle = m_hDeviceInterface;
DBG_TRC(("Attempting to register with PnP for interface device handle"));
m_hDeviceNotificationSink = RegisterDeviceNotification(g_StiServiceStatusHandle, (LPVOID)&sNotificationFilter, DEVICE_NOTIFY_SERVICE_HANDLE); dwError = GetLastError(); if( !m_hDeviceNotificationSink && (NOERROR != dwError)) { m_hDeviceNotificationSink = NULL; //
// Failed to create notification sink with PnP subsystem
//
DBG_ERR(("InitPnPNotifications: Attempt to register %S with PnP failed. Error:0x%X", GetDeviceID(), ::GetLastError())); } else { fRet = TRUE; } } else { DBG_WRN(("InitPnPNotifications: Attempt to open device interface on (%ws) failed. Error:0x%X", GetDeviceID(), ::GetLastError())); }
delete [] wszInterfaceName; } else { DBG_WRN(("InitPnPNotifications: Lookup for device interface name on (%ws) failed. Error:0x%X", GetDeviceID(), ::GetLastError())); }
#else
fRet = TRUE;
#endif
return fRet;
}
BOOL ACTIVE_DEVICE:: IsRegisteredForDeviceRemoval( VOID ) { //
// Check whether we registered for device notifications on this
// device's interface.
//
if (IsValidHANDLE(m_hDeviceNotificationSink)) { return TRUE; } else { return FALSE; } }
BOOL ACTIVE_DEVICE:: StopPnPNotifications( VOID ) /*++
Routine Description:
Assumes device object to be locked
Arguments:
None
--*/ {
#ifdef WINNT
//
// Unregister for PnP notifications on interface handle
//
if (IS_VALID_HANDLE(m_hDeviceNotificationSink)) { ::UnregisterDeviceNotification(m_hDeviceNotificationSink); m_hDeviceNotificationSink = NULL; } else { DBG_TRC(("StopPnPNotifications: Device sink is invalid ")); } //
// Close interface handle
//
if (IS_VALID_HANDLE(m_hDeviceInterface)) { ::CloseHandle(m_hDeviceInterface); m_hDeviceInterface = NULL; } else { DBG_TRC(("StopPnPNotifications: Device interface handle is invalid")); }
#endif
return TRUE; }
BOOL ACTIVE_DEVICE:: UpdateDeviceInformation( VOID ) /*++
Routine Description:
Updates the cached device information struct.
Arguments:
None
--*/ { USES_CONVERSION;
HRESULT hres; BOOL bRet = TRUE;
/* TBD:
if (!m_strStiDeviceName) { DBG_ERR(("Error updating device info cache, device name is invalid")); bRet = FALSE; }
//
// Update the cached WIA_DEVICE_INFORMATION in the ACTICVE_DEVICE
//
if (bRet) { PSTI_WIA_DEVICE_INFORMATION pWiaDevInfo;
hres = StiPrivateGetDeviceInfoHelperW((LPWSTR)T2CW(m_strStiDeviceName),(LPVOID *)&pWiaDevInfo );
if (!SUCCEEDED(hres) || !pWiaDevInfo) { DBG_ERR(("Loading device (%ws) . Failed to get WIA information from STI. HResult=(%x)", m_strStiDeviceName, hres)); m_pWiaDeviceInformation = NULL; bRet = FALSE; } else { m_pWiaDeviceInformation = pWiaDevInfo; } } */
return bRet; }
//
// Functions
//
//
VOID WINAPI ScheduleDeviceCallback( VOID * pContext ) /*++
Routine Description:
This function is the callback called by the scheduler thread after the specified timeout period has elapsed.
Arguments:
pContext - pointer to device object
--*/
{ ACTIVE_DEVICE* pActiveDevice = (ACTIVE_DEVICE* )pContext;
ASSERT(("Callback invoked with null context", pContext));
if (pContext) { //
// No need to take the active device here - the caller
// has already AddRef'd, and will Release when
// we're done. A dealock will occur unless we first
// take the global list CS, then the ACTIVE_DEVICE's
// CS...
//TAKE_ACTIVE_DEVICE t(pActiveDevice);
pActiveDevice->AddRef();
if (pActiveDevice->QueryFlags() & STIMON_AD_FLAG_POLLING) { pActiveDevice->DoPoll(); } else { //
// Async event arrived - call methods
//
pActiveDevice->DoAsyncEvent(); }
pActiveDevice->Release(); } }
VOID WINAPI DelayedDeviceInitCallback( VOID * pContext ) /*++
Routine Description:
This function is the callback called by the scheduler thread after the device is first created to enable notifications.
Arguments:
pContext - pointer to device object
--*/
{ ACTIVE_DEVICE* pActiveDevice = (ACTIVE_DEVICE* )pContext;
ASSERT(("Callback invoked with null context", pContext)); if (pContext) {
TAKE_ACTIVE_DEVICE t(pActiveDevice);
pActiveDevice->m_dwDelayedOpCookie = 0;
if (pActiveDevice->IsValid()) {
//
// If there is nobody to receive notifications, don't really enable them
//
pActiveDevice->EnableDeviceNotifications();
#ifdef DO_INITIAL_RESET
// NOTE:
// Resetting the device is a good way of ensuring that the device
// starts off in a stable state. Unfortunately, this can be bad
// because 1) It is often time consuming
// 2) We may wake up devices unecessarily (e.g. most
// serial cameras).
//
// Device reset is not necessary for WIA drivers, since it is a
// requirement that they are always in a stable state, so we
// could compromise and reset only non-WIA devices.
//
hres = g_pStiLockMgr->RequestLock(pActiveDevice, STIMON_AD_DEFAULT_WAIT_LOCK); if (SUCCEEDED(hres) ) { pActiveDevice->m_DrvWrapper.STI_DeviceReset(); g_pStiLockMgr->RequestUnLock(pActiveDevice); } #endif
//
// As we are done with delayed initialization - clear the flag
//
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() & ~STIMON_AD_FLAG_DELAYED_INIT);
} /* endif IsValid */ else { ASSERT(("DelayedDeviceInitCallback received invalid device object", 0)); } } }
VOID WINAPI AutoLaunchThread( LPVOID lpParameter ) /*++
Routine Description:
Worker routine for autolaunching thread. Validates parameter and invokes proper method
Arguments:
None.
Return Value:
None.
--*/ { PAUTO_LAUNCH_PARAM_CONTAINER pAutoContainer = static_cast<AUTO_LAUNCH_PARAM_CONTAINER *>(lpParameter);
if (!lpParameter || !pAutoContainer->pActiveDevice) { ASSERT(("No parameter passed to launch thread", 0)); return; }
ACTIVE_DEVICE *pActiveDevice = pAutoContainer->pActiveDevice;
pActiveDevice->AutoLaunch(pAutoContainer); pActiveDevice->Release();
delete pAutoContainer; }
//
// Adding new device to the active list.
// This function is not reentrant with adding/removal
//
BOOL AddDeviceByName( LPCTSTR pszDeviceName, BOOL fPnPInitiated // = FALSE
) { /*
USES_CONVERSION;
LIST_ENTRY * pentry; LIST_ENTRY * pentryNext;
ACTIVE_DEVICE* pActiveDevice = NULL;
BOOL fAlreadyExists = FALSE;
DBG_TRC(("Requested arrival of device (%ws) ",pszDeviceName));
// BEGIN PROTECTED CODE
{ TAKE_CRIT_SECT t(g_DeviceListSync);
for ( pentry = g_DeviceListHead.Flink; pentry != &g_DeviceListHead; pentry = pentryNext ) {
pentryNext = pentry->Flink;
pActiveDevice = CONTAINING_RECORD( pentry,ACTIVE_DEVICE ,m_ListEntry );
if ( pActiveDevice->m_dwSignature != ADEV_SIGNATURE ) { ASSERT(("Invalid device signature", 0)); break; }
if (!::lstrcmpi(pszDeviceName,(LPCTSTR)pActiveDevice->m_strStiDeviceName)) {
fAlreadyExists = TRUE; break; } }
if (!fAlreadyExists) {
pActiveDevice = new ACTIVE_DEVICE(pszDeviceName);
if (!pActiveDevice || !pActiveDevice->IsValid()) {
DBG_ERR(("Creating device failed ")); if (pActiveDevice) { delete pActiveDevice; }
return FALSE; }
// Finally insert new object into the list
InsertTailList(&g_DeviceListHead,&pActiveDevice->m_ListEntry); } else { STIMONWPRINTF(TEXT("Request to add new device found device is already maintained")); return FALSE; } } // END PROTECTED CODE
//
// If new device appeared - initialize PnP interface notifications
//
if ( pActiveDevice ) {
TAKE_ACTIVE_DEVICE t(pActiveDevice);
pActiveDevice->InitPnPNotifications(g_hStiServiceWindow);
}
//
// If this device or it's USD requests auto-generating a launch event on arrival
// schedule it here
//
// NOTE : This will also happen for WIA devices. Generally, this is what we want,
// when a new device arrives we should generate the event, since devices such as
// serial cameras wont generate this on their own.
//
//
// For STI devices we must check whether we need to generate the
// event. For WIA devices, we always want to, so it's not an issue.
//
BOOL bStiDeviceMustThrowEvent = (pActiveDevice->QueryFlags() & STIMON_AD_FLAG_NOTIFY_RUNNING) && pActiveDevice->IsEventOnArrivalNeeded(); if (fPnPInitiated && pActiveDevice) {
TAKE_ACTIVE_DEVICE t(pActiveDevice);
STINOTIFY sNotify; BOOL fRet;
//
// If this is a WIA device, then the event should be WIA_EVENT_DEVICE_CONNECTED.
// If this is an sti device, then it should be GUID_DeviceArrivedLaunch;
//
sNotify.dwSize = sizeof STINOTIFY; if (pActiveDevice->m_pWiaDeviceInformation) { sNotify.guidNotificationCode = WIA_EVENT_DEVICE_CONNECTED; } else {
//
// Check whether this STI device should throw the event
//
if (!bStiDeviceMustThrowEvent) { return TRUE; } sNotify.guidNotificationCode = GUID_DeviceArrivedLaunch; }
DBG_TRC(("::AddDeviceByName, processing CONNECT event (STI or WIA) for %ws", T2W((TCHAR*)pszDeviceName))); fRet = pActiveDevice->ProcessEvent(&sNotify);
if (!fRet) { DBG_ERR(("Attempted to generate event on device(%ws) arrival and failed ", pszDeviceName)); } } */ return TRUE; }
//
// Remove device identified by name
//
BOOL RemoveDeviceByName( LPTSTR pszDeviceName ) {
USES_CONVERSION;
DBG_FN(RemoveDeviceByName);
LIST_ENTRY * pentry; LIST_ENTRY * pentryNext;
ACTIVE_DEVICE* pActiveDevice = NULL;
BOOL fRet = FALSE;
DBG_TRC(("Requested removal of device (%ws)", pszDeviceName));
// BEGIN PROTECTED CODE
{ TAKE_CRIT_SECT t(g_DeviceListSync);
for ( pentry = g_DeviceListHead.Flink; pentry != &g_DeviceListHead; pentry = pentryNext ) {
pentryNext = pentry->Flink;
pActiveDevice = CONTAINING_RECORD( pentry,ACTIVE_DEVICE ,m_ListEntry );
if ( pActiveDevice->m_dwSignature != ADEV_SIGNATURE ) { ASSERT(("Invalid device signature", 0)); fRet = FALSE; break; } TCHAR *tszDeviceID = NULL;
tszDeviceID = W2T(pActiveDevice->GetDeviceID()); if (tszDeviceID) { if (!::lstrcmp(pszDeviceName,tszDeviceID)) {
// Mark device as being removed
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() | STIMON_AD_FLAG_REMOVING);
//
// Remove any device notification callbacks
//
pActiveDevice->DisableDeviceNotifications();
//
// Stop PnP notifications immediately. This is important to free interface handle
//
pActiveDevice->StopPnPNotifications();
//
// Remove from the list
//
RemoveEntryList(&pActiveDevice->m_ListEntry); pActiveDevice->m_ListEntry.Flink = pActiveDevice->m_ListEntry.Blink = NULL;
//
// Destroy device object if there are no references to it
//
ULONG ulRef = pActiveDevice->Release(); if (ulRef != 0) {
//
// The ACTIVE_DEVICE should have been destroyed i.e. it's
// ref count should have been 0. Someone is still holding
// an active count on it, which may indicate a problem
// since USD wont be unloaded until ACTIVE_DEVICE is
// destroyed...
//
// NOTE: If a transfer is occuring while deleteing, then
// the ACTIVE_DEVICE will not be destroyed here (since
// ref count > 0), but will be destroyed when the transfer
// finishes.
//
DBG_TRC(("* ACTIVE_DEVICE is removed from list but not yet destroyed!")); //Break();
}
fRet = TRUE;
break; } } }
} // END PROTECTED CODE
return fRet; }
//
// Mark device identified by name for removal
//
BOOL MarkDeviceForRemoval( LPTSTR pszDeviceName ) {
USES_CONVERSION;
DBG_FN(MarkDeviceForRemoval);
LIST_ENTRY * pentry; LIST_ENTRY * pentryNext;
ACTIVE_DEVICE* pActiveDevice = NULL;
BOOL fRet = FALSE;
DBG_TRC(("Requested marking of device (%S) for removal",pszDeviceName));
// BEGIN PROTECTED CODE
{ TAKE_CRIT_SECT t(g_DeviceListSync);
for ( pentry = g_DeviceListHead.Flink; pentry != &g_DeviceListHead; pentry = pentryNext ) {
pentryNext = pentry->Flink;
pActiveDevice = CONTAINING_RECORD( pentry,ACTIVE_DEVICE ,m_ListEntry );
if ( pActiveDevice->m_dwSignature != ADEV_SIGNATURE ) { ASSERT(("Invalid device signature", 0)); fRet = FALSE; break; } TCHAR *tszDeviceID = NULL;
tszDeviceID = W2T(pActiveDevice->GetDeviceID()); if (tszDeviceID) { if (!::lstrcmp(pszDeviceName, tszDeviceID)) {
// Mark device as being removed
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() | STIMON_AD_FLAG_REMOVING); fRet = TRUE; break; } } }
} // END PROTECTED CODE
return fRet; }
//
// Initialize/Terminate linked list
//
VOID InitializeDeviceList( VOID ) { InitializeListHead( &g_DeviceListHead ); InitializeListHead( &g_ConnectionListHead );
g_lTotalOpenedConnections = 0; g_lTotalActiveDevices = 0;
g_fDeviceListInitialized = TRUE; }
VOID TerminateDeviceList( VOID ) { LIST_ENTRY * pentry; ACTIVE_DEVICE* pActiveDevice = NULL;
DBG_TRC(("Destroying list of active devices"));
if (!g_fDeviceListInitialized) { return; }
TAKE_CRIT_SECT t(g_DeviceListSync);
//
// Go through the list terminating devices
//
while (!IsListEmpty(&g_DeviceListHead)) {
pentry = g_DeviceListHead.Flink;
//
// Remove from the list ( reset list entry )
//
RemoveHeadList(&g_DeviceListHead); InitializeListHead( pentry );
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
// Destroy device object
// delete pActiveDevice;
pActiveDevice->Release();
}
g_fDeviceListInitialized = FALSE; }
VOID RefreshDeviceList( WORD wCommand, WORD wFlags ) /*++
Routine Description:
Update device list. Invalidate if necessary
Arguments:
wCommand - update command code wFlags - update flags
Return Value:
None
--*/ {
BOOL fOldState;
// TDB: Call CWiaDevMan::ReEnumerateDevices
/*
//
// Pause work item scheduler
//
fOldState = SchedulerSetPauseState(TRUE);
//
// Indicate that the device list refresh is not yet complete
//
ResetEvent(g_hDevListCompleteEvent);
//
// If needed, add devices , which might appear first.
//
if (wFlags & STIMON_MSG_REFRESH_NEW) {
//
// If request been made to purge removed devices - do it in 2 steps:
// - First, mark all devices currently active as inactive
// - Second, go through all devices from PnP and for each device either create new
// active device object ( if it does not exist yet), or mark existing one as active
// - Third , purge all device objects, marked as inactive from the list.
//
//
// NOTE: None of the parameters using LongToPtr are actually pointer values! They're
// actually wParams and lParams of Windows messages (equivalents).
//
if (wFlags & STIMON_MSG_PURGE_REMOVED) {
DBG_TRC(("Purging device list. Phase1: Marking all devices inactive "));
EnumerateActiveDevicesWithCallback(&RefreshExistingDevicesCallback, (LPVOID)LongToPtr(MAKELONG(STIMON_MSG_REFRESH_SET_FLAG,STIMON_MSG_NOTIF_SET_INACTIVE))); }
EnumerateStiDevicesWithCallback(&AddNewDevicesCallback, (LPVOID)LongToPtr(MAKELONG(0,wFlags)));
if (wFlags & STIMON_MSG_PURGE_REMOVED) {
DBG_TRC(("Purging device list. Phase2: Removing unconfirmed devices "));
EnumerateActiveDevicesWithCallback(&RefreshExistingDevicesCallback, (LPVOID)LongToPtr(MAKELONG(STIMON_MSG_REFRESH_PURGE,0))); }
}
//
// If requested, go through all known active devices and refresh their settings
//
if (wFlags & STIMON_MSG_REFRESH_EXISTING) { EnumerateActiveDevicesWithCallback(&RefreshExistingDevicesCallback,(LPVOID)LongToPtr(MAKELONG(wCommand,wFlags))); }
SetEvent(g_hDevListCompleteEvent);
// UnPause work item scheduler
SchedulerSetPauseState(fOldState); */ }
//
// Set new value of interval for all polled devices
//
VOID CALLBACK ResetAllPollIntervalsCallback( ACTIVE_DEVICE *pActiveDevice, VOID *pContext ) /*++
Routine Description:
Arguments:
Return Value:
TRUE , FALSE
--*/ { ULONG ulContextLong = PtrToUlong(pContext);
TAKE_ACTIVE_DEVICE t(pActiveDevice);
//
// If device is polled - reset it's interval to new value
//
if(pActiveDevice->QueryFlags() & STIMON_AD_FLAG_POLLING) {
pActiveDevice->SetPollingInterval(ulContextLong);
DBG_TRC(("Polling interval is set to %d on device (%ws)", pActiveDevice->QueryPollingInterval(), pActiveDevice->GetDeviceID())); } }
BOOL ResetAllPollIntervals( UINT dwNewPollInterval ) /*++
Routine Description:
Arguments:
Return Value:
TRUE , FALSE
--*/ { EnumerateActiveDevicesWithCallback(&ResetAllPollIntervalsCallback,(LPVOID)LongToPtr(dwNewPollInterval));
return TRUE; }
VOID CALLBACK DumpActiveDevicesCallback( ACTIVE_DEVICE *pActiveDevice, VOID *pContext ) /*++
Routine Description:
Arguments:
Return Value:
TRUE , FALSE
--*/ { STIMONWPRINTF(TEXT("Device:%ws DeviceId:%d Flags:%4x Poll interval:%d"), pActiveDevice->GetDeviceID(), pActiveDevice->m_lDeviceId, pActiveDevice->QueryFlags(), pActiveDevice->m_dwPollingInterval); }
VOID DebugDumpDeviceList( VOID ) /*++
Routine Description:
Arguments:
Return Value:
TRUE , FALSE
--*/ { EnumerateActiveDevicesWithCallback(&DumpActiveDevicesCallback,NULL); }
VOID CALLBACK PurgeDevicesCallback( PSTI_DEVICE_INFORMATION pDevInfo, VOID *pContext ) /*++
Routine Description:
Callback routine to invoke when removing all devices
Arguments:
pDevInfo - pointer to device information block
Return Value:
None
--*/ { USES_CONVERSION;
if (RemoveDeviceByName(W2T(pDevInfo->szDeviceInternalName))) { STIMONWPRINTF(TEXT("Destroyed device object (%S)"),pDevInfo->szDeviceInternalName); } else { STIMONWPRINTF(TEXT("Attempted destroying device object (%S), but failed"),pDevInfo->szDeviceInternalName); } }
VOID DebugPurgeDeviceList( VOID *pContext ) /*++
Routine Description:
Unconditionally destroy active device list
Arguments:
Context to pass to callback
--*/ {
// Pause work item scheduler
SchedulerSetPauseState(TRUE);
// TBD: Find replacement
//EnumerateStiDevicesWithCallback(&PurgeDevicesCallback,pContext);
// UnPause work item scheduler
SchedulerSetPauseState(FALSE);
}
//
// Enumerators with callbacks
//
VOID WINAPI EnumerateStiDevicesWithCallback( PFN_DEVINFO_CALLBACK pfn, VOID *pContext ) /*++
Routine Description:
Walk the list of installed devices, calling given routine for each device
Arguments:
pfn - Address of the callback pContext- Pointer to context information to pass to callback
Return Value:
None
--*/ { /* TDB: Find out who calls this and convert them over to CWiaDevMan
if (!g_fDeviceListInitialized) { STIMONWPRINTF(TEXT("Device list not initialized")); return; }
if (!pfn) { ASSERT(("Incorrect callback", 0)); return; }
HRESULT hres;
PSTI_DEVICE_INFORMATION pDevInfo;
PVOID pBuffer;
UINT iDev; DWORD dwItemsReturned;
//
// Enumerate STI devices
//
hres = g_pSti->GetDeviceList(0, // Type
FLAG_NO_LPTENUM, // Flags
&dwItemsReturned, &pBuffer);
if (!SUCCEEDED(hres) || !pBuffer) { DBG_ERR(("Enumeration call failed - abort. HRes=%x \n",hres)); goto Cleanup; }
DBG_TRC(("EnumerateStiDevicesWithCallback, returned from GetList: counter=%d", dwItemsReturned));
pDevInfo = (PSTI_DEVICE_INFORMATION) pBuffer;
//
// Walk the device list and for each device add active object
//
for (iDev=0; iDev<dwItemsReturned ; iDev++, pDevInfo++) {
pfn(pDevInfo,pContext);
} // end_for
Cleanup:
if (pBuffer) { LocalFree(pBuffer); pBuffer = NULL; } */ }
VOID WINAPI EnumerateActiveDevicesWithCallback( PFN_ACTIVEDEVICE_CALLBACK pfn, VOID *pContext ) /*++
Routine Description:
Walk the list of known active devices, calling given routine for each device
Arguments:
pfn - Address of the callback pContext- Pointer to context information to pass to callback
Return Value:
None
--*/ {
if (!pfn) { ASSERT(("Incorrect callback", 0)); return; }
LIST_ENTRY * pentry; LIST_ENTRY * pentryNext;
ACTIVE_DEVICE* pActiveDevice;
// BEGIN PROTECTED CODE
{ TAKE_CRIT_SECT t(g_DeviceListSync);
for ( pentry = g_DeviceListHead.Flink; pentry != &g_DeviceListHead; pentry = pentryNext ) {
pentryNext = pentry->Flink;
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
if (!pActiveDevice->IsValid()) { ASSERT(("Invalid device signature", 0)); break; }
pfn(pActiveDevice,pContext); } } // END PROTECTED CODE
}
VOID CleanApplicationsListForEvent( LPCTSTR pDeviceName, PDEVICEEVENT pDeviceEvent, LPCTSTR pAppName ) /*++
Routine Description:
After it had been determined that application , associated with this event is not valid, we want to make event function as wild card ( i.e. all eligibale apps are selected)
Arguments:
Return Value:
--*/ {
USES_CONVERSION;
//
// Build up reg path for event info
//
StiCString strRegPath;
strRegPath.CopyString((LPCTSTR)(IsPlatformNT() ? REGSTR_PATH_STIDEVICES_NT : REGSTR_PATH_STIDEVICES));
strRegPath+=TEXT("\\"); strRegPath+=pDeviceName;
RegEntry reEvent((LPCTSTR)strRegPath,HKEY_LOCAL_MACHINE);
if (reEvent.IsValid()) {
reEvent.MoveToSubKey(EVENTS); reEvent.MoveToSubKey((LPCTSTR)pDeviceEvent->m_EventSubKey);
reEvent.SetValue(REGSTR_VAL_LAUNCH_APPS,TEXT("*"));
// Reset data in loaded event descriptor
pDeviceEvent->m_EventData.CopyString(TEXT("*")); }
}
DWORD GetNumRegisteredApps( VOID ) /*++
Routine Description:
Count number of currently registered applications
Arguments:
None
Return Value:
Number of registered apps
--*/ { RegEntry re(REGSTR_PATH_REG_APPS,HKEY_LOCAL_MACHINE); RegEnumValues regenum(&re);
DWORD dwCount = 0;
if (re.IsValid()) {
while (ERROR_SUCCESS == regenum.Next() ) {
#ifndef USE_QUERY_INFO
if ((regenum.GetType() == REG_SZ ) && !IS_EMPTY_STRING(regenum.GetName())) { dwCount++; } } #else
dwErrorReg = RegQueryInfoKey ( re.GetKey(), // Key
NULL, // Buffer for class string
NULL, // Size of class string buffer
NULL, // Reserved
NULL, // Number of subkeys
NULL, // Longest subkey name
NULL, // Longest class string
&dwCount, // Number of value entries
NULL, // Longest value name
NULL, // Longest value data
NULL, // Security descriptor
NULL ); // Last write time
#endif
}
return dwCount;
}
HRESULT WiaGetDeviceInfo( LPCWSTR pwszDeviceName, DWORD *pdwDeviceType, BSTR *pbstrDeviceDescription, ACTIVE_DEVICE **ppDevice)
/*++
Routine Description:
Retrieve device information of device
Arguments:
Return Value:
status
--*/
{ USES_CONVERSION;
HRESULT hr = S_FALSE; ACTIVE_DEVICE *pActiveDevice;
if (!ppDevice) { return E_POINTER; }
pActiveDevice = g_pDevMan->IsInList(DEV_MAN_IN_LIST_DEV_ID, (WCHAR*)pwszDeviceName); if (pActiveDevice) { //
// If that device is WIA capable
//
if (pActiveDevice->m_DrvWrapper.IsWiaDevice()) { *ppDevice = pActiveDevice;
//
// Copy necessary information
//
DEVICE_INFO *pDeviceInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
if (pDeviceInfo) { *pbstrDeviceDescription = SysAllocString( pDeviceInfo->wszDeviceDescription);
if (*pbstrDeviceDescription) {
*pdwDeviceType = pDeviceInfo->DeviceType;
hr = S_OK; } } } }
if (hr != S_OK) {
*pdwDeviceType = 0; *pbstrDeviceDescription = NULL; *ppDevice = NULL; }
return (hr); }
HRESULT WiaUpdateDeviceInfo()
/*++
Routine Description:
Refreshes the cached STI_WIA_DEVICE_INFORMATION in each device.
Arguments:
Return Value:
status
--*/ { RefreshDeviceList(STIMON_MSG_REFRESH_DEV_INFO, STIMON_MSG_REFRESH_EXISTING);
return S_OK; }
|