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.
3337 lines
114 KiB
3337 lines
114 KiB
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORP., 2000
|
|
*
|
|
* TITLE: wiadevman.cpp
|
|
*
|
|
* VERSION: 1.0
|
|
*
|
|
* AUTHOR: ByronC
|
|
*
|
|
* DATE: 6 Nov, 2000
|
|
*
|
|
* DESCRIPTION:
|
|
* Declarations and definitions for the WIA device manager class.
|
|
* It controls the enumeration of devices, the internal device list, adding
|
|
* removing of devices from this list (PnP initiated) and implements the
|
|
* IWiaDevMgr interface.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#include "stiexe.h"
|
|
#include "enum.h"
|
|
#include "shpriv.h"
|
|
#include "devmgr.h"
|
|
#include "wiaevntp.h"
|
|
|
|
//
|
|
// NOTE: For Automated testing of FS devices, the volume devices need to
|
|
// be visible from normal WIA enumeration and not just Autoplay.
|
|
//
|
|
// Uncomment the "#define PRIVATE_FOR_TEST"
|
|
// This will enable ALL mass storage devices to show up as normal
|
|
// WIA devices (i.e. accessible by all WIA apps, Shell etc.). That
|
|
// includes your floppy drives, CD-ROMs, ZIP and so on.
|
|
//
|
|
// #define PRIVATE_FOR_TEST
|
|
|
|
//
|
|
// Helper functions
|
|
//
|
|
|
|
#define DEV_STATE_MASK 0xFFFFFFF8
|
|
|
|
/**************************************************************************\
|
|
* ::IsCorrectVolumeType
|
|
*
|
|
* This function checks whether the given volume is one that WIA will
|
|
* accept as a possible candidate for the FS driver. We only
|
|
* allow:
|
|
* Removable drives
|
|
* File systems that don't enforce security
|
|
*
|
|
* Arguments:
|
|
*
|
|
* wszMountPoint - The volume mount point
|
|
*
|
|
* Return Value:
|
|
*
|
|
* DeviceState
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
BOOL IsCorrectVolumeType(
|
|
LPWSTR wszMountPoint)
|
|
{
|
|
BOOL bValid = FALSE;
|
|
DWORD dwFSFlags = 0;
|
|
|
|
//
|
|
// Do parameter validation
|
|
//
|
|
if (wszMountPoint) {
|
|
|
|
UINT uDriveType = GetDriveTypeW(wszMountPoint);
|
|
//
|
|
// Check whether this is a fixed drive. We don't allow fixed drives.
|
|
// Note that we don't worry about network drives because our
|
|
// volume enumerator only enumerates local volumes.
|
|
//
|
|
if (uDriveType != DRIVE_FIXED) {
|
|
|
|
//
|
|
// Skip floppy drives
|
|
//
|
|
if ((towupper(wszMountPoint[0]) != L'A') && (towupper(wszMountPoint[0]) != L'B')) {
|
|
|
|
//
|
|
// Check whether file system is securable...
|
|
//
|
|
if (GetVolumeInformationW(wszMountPoint,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&dwFSFlags,
|
|
NULL,
|
|
0))
|
|
{
|
|
if (!(dwFSFlags & FS_PERSISTENT_ACLS)) {
|
|
bValid = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ASSERT(("NULL wszMountPoint parameter - this should never happen!", wszMountPoint));
|
|
}
|
|
|
|
return bValid;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* ::MapCMStatusToDeviceState
|
|
*
|
|
* This function translates status and problem number information returned
|
|
* from CM_Get_DevNode_STatus to our internal device state flags. The
|
|
* status of the dev node tells us whether the device is active or disabled
|
|
* etc.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* dwOldDevState - The previous device state. This contains other
|
|
* bits we wamt to carry over. Currently, this
|
|
* is only the DEV_STATE_CON_EVENT_WAS_THROWN
|
|
* bit.
|
|
* ulStatus - Status from CM_Get_DevNode_Status
|
|
* ulProblemNumber - Problem from CM_Get_DevNode_Status
|
|
*
|
|
* Return Value:
|
|
*
|
|
* DeviceState
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
DWORD MapCMStatusToDeviceState(
|
|
DWORD dwOldDevState,
|
|
ULONG ulStatus,
|
|
ULONG ulProblemNumber)
|
|
{
|
|
//
|
|
// Clear the lower 3 bits
|
|
//
|
|
DWORD dwDevState = dwOldDevState & DEV_STATE_MASK;
|
|
|
|
if (ulStatus & DN_STARTED) {
|
|
dwDevState |= DEV_STATE_ACTIVE;
|
|
} else if (ulStatus & DN_HAS_PROBLEM) {
|
|
|
|
if (CM_PROB_DISABLED) {
|
|
dwDevState |= DEV_STATE_DISABLED;
|
|
}
|
|
if (CM_PROB_HARDWARE_DISABLED) {
|
|
dwDevState |= DEV_STATE_DISABLED;
|
|
}
|
|
if (CM_PROB_WILL_BE_REMOVED) {
|
|
dwDevState |= DEV_STATE_REMOVED;
|
|
}
|
|
}
|
|
|
|
return dwDevState;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* ::MapMediaStatusToDeviceState
|
|
*
|
|
* This function translates media status to our device internal state.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* dwMediaStatus - Media status returned from Shell volume enumeration.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* DeviceState
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
DWORD MapMediaStatusToDeviceState(
|
|
DWORD dwMediaStatus
|
|
)
|
|
{
|
|
DWORD dwDevState = 0;
|
|
|
|
if (dwMediaStatus & HWDMS_PRESENT) {
|
|
dwDevState |= DEV_STATE_ACTIVE;
|
|
}
|
|
|
|
return dwDevState;
|
|
}
|
|
|
|
//
|
|
// CWiaDevMan Methods
|
|
//
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::CWiaDevMan
|
|
*
|
|
* Constructor
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
* None
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
CWiaDevMan::CWiaDevMan()
|
|
{
|
|
m_DeviceInfoSet = NULL;
|
|
m_bMakeVolumesVisible = FALSE;
|
|
m_bVolumesEnabled = TRUE;
|
|
m_dwHWCookie = 0;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::~CWiaDevMan
|
|
*
|
|
* Destructor - kills the device list and destroys our infoset
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
* None
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
CWiaDevMan::~CWiaDevMan()
|
|
{
|
|
// Destroy all our device objects
|
|
DestroyDeviceList();
|
|
|
|
// Destroy our device infoset
|
|
DestroyInfoSet();
|
|
|
|
if (m_dwHWCookie) {
|
|
|
|
//
|
|
// Unregister for notifications
|
|
//
|
|
HRESULT hr = S_OK;
|
|
IHardwareDevices *pihwdevs = NULL;
|
|
|
|
hr = CoCreateInstance(CLSID_HardwareDevices,
|
|
NULL,
|
|
CLSCTX_LOCAL_SERVER | CLSCTX_NO_FAILURE_LOG,
|
|
IID_IHardwareDevices,
|
|
(VOID**)&pihwdevs);
|
|
if (SUCCEEDED(hr)) {
|
|
pihwdevs->Unadvise(m_dwHWCookie);
|
|
m_dwHWCookie = 0;
|
|
pihwdevs->Release();
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::~CWiaDevMan, CoCreateInstance, looking for Shell interface IHardwareDevices failed"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::Initialize
|
|
*
|
|
* This method initializes the device manager object. It does not enumerate
|
|
* any devices - ReEnumerateDevices needs to be called to populate our
|
|
* device list.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* dwCallbackThreadId - This specifies the id of the thread on which we
|
|
* will receive volume notifications. Notice
|
|
* that these callbacks are done via APCs, so this
|
|
* ThreadId must not change, or we should reregister
|
|
* with the new ThreadId.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CWiaDevMan::Initialize()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHardwareDevices *pihwdevs = NULL;
|
|
|
|
//
|
|
// Initialize the device list head
|
|
//
|
|
InitializeListHead(&m_leDeviceListHead);
|
|
|
|
//
|
|
// Check that our critical section was initialized correctly
|
|
//
|
|
if (!m_csDevList.IsInitialized()) {
|
|
DBG_ERR(("CWiaDevMan::Initialize, Critical section could not be initialized"));
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
//
|
|
// Check our relevant registry settings
|
|
//
|
|
GetRegistrySettings();
|
|
|
|
if (VolumesAreEnabled()) {
|
|
|
|
/* This code has been removed for this release. It would be used
|
|
to enable scenarios based on Mass Storage Class cameras behaving
|
|
like normal WIA devices, specifically with us being able to
|
|
throw "Connect" and "Disconnect" events.
|
|
This may be re-enabled for the next release. When it does, we must
|
|
be sure to revisit our APC notification handler: CWiaDevMan::ShellHWEventAPCProc.
|
|
It should be re-written not to make any COM calls, else we'll hit a problem when
|
|
multiple APCs are queued, as soon as we make a COM invocation, we enter a wait
|
|
state, which causes the next APC request to execute. This leads to "nested"
|
|
COM calls, which is not supported by the OS.
|
|
hr = CoCreateInstance(CLSID_HardwareDevices,
|
|
NULL,
|
|
CLSCTX_LOCAL_SERVER | CLSCTX_NO_FAILURE_LOG,
|
|
IID_IHardwareDevices,
|
|
(VOID**)&pihwdevs);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
HANDLE hPseudoThread = GetCurrentThread(); // Note that this is a pseudo handle and need not be closed
|
|
HANDLE hThread = NULL; // IHardwareDevices will close this handle when it is done
|
|
if (DuplicateHandle(GetCurrentProcess(),
|
|
hPseudoThread,
|
|
GetCurrentProcess(),
|
|
&hThread,
|
|
DUPLICATE_SAME_ACCESS,
|
|
FALSE,
|
|
0)) {
|
|
//
|
|
// Register this object for Volume notifications.
|
|
//
|
|
|
|
hr = pihwdevs->Advise(GetCurrentProcessId(),
|
|
(ULONG_PTR)hThread,
|
|
(ULONG_PTR)CWiaDevMan::ShellHWEventAPCProc,
|
|
&m_dwHWCookie);
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::Initialize, DuplicateHandle failed, could not register for Volume Notifications"));
|
|
}
|
|
pihwdevs->Release();
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::Initialize, CoCreateInstance on CLSID_HardwareDevices failed, could not register for Volume Notifications"));
|
|
}
|
|
*/
|
|
}
|
|
|
|
//
|
|
// Create our infoset. Note that we overwrite hr here, since if we cannot
|
|
// see volumes, it is not fatal to us.
|
|
//
|
|
hr = CreateInfoSet();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::GetRegistrySettings
|
|
*
|
|
* This method reads certain registry entries related to the WiaDevMan
|
|
* operation. Currently, we're looking for:
|
|
*
|
|
* EnableVolumeDevices - Indicates whether we enable volumes. We
|
|
* assume they're enabled unless it's registry
|
|
* value is specifically 0.
|
|
* MakeVolumeDevicesVisible - Indicates whether volume device should be
|
|
* included in normal device enumeration. This
|
|
* makes them visible to the outside world by
|
|
* default.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None
|
|
*
|
|
* Return Value:
|
|
*
|
|
* None
|
|
*
|
|
* History:
|
|
*
|
|
* 01/27/2001 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
VOID CWiaDevMan::GetRegistrySettings()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwVal = 0;
|
|
DWORD dwRet = 0;
|
|
HKEY hKey = NULL;
|
|
|
|
//
|
|
// Open the registry in the right place
|
|
//
|
|
|
|
dwRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_STICONTROL_W,
|
|
0,
|
|
0,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
if (dwRet == ERROR_SUCCESS && IsValidHANDLE(hKey)) {
|
|
|
|
//
|
|
// Read "EnableVolumeDevices"
|
|
//
|
|
hr = ReadRegistryDWORD(hKey,
|
|
REGSTR_VAL_ENABLE_VOLUMES_W,
|
|
&dwVal);
|
|
if ((hr == S_OK) && (dwVal == 0)) {
|
|
|
|
//
|
|
// Disable volume device
|
|
//
|
|
m_bVolumesEnabled = FALSE;
|
|
DBG_TRC(("CWiaDevMan::GetRegistrySettings, volume devices disabled"));
|
|
} else {
|
|
|
|
//
|
|
// Enable volume devices
|
|
//
|
|
m_bVolumesEnabled = TRUE;
|
|
DBG_TRC(("CWiaDevMan::GetRegistrySettings, volume devices Enabled"));
|
|
}
|
|
|
|
dwVal = 0;
|
|
|
|
#ifdef PRIVATE_FOR_TEST
|
|
//
|
|
// Read "MakeVolumeDevicesVisible"
|
|
//
|
|
hr = ReadRegistryDWORD(hKey,
|
|
REGSTR_VAL_MAKE_VOLUMES_VISIBLE_W,
|
|
&dwVal);
|
|
#endif
|
|
if (dwVal == 0) {
|
|
|
|
//
|
|
// Make volume devices invisible from normal enumeration
|
|
//
|
|
m_bMakeVolumesVisible = FALSE;
|
|
DBG_TRC(("CWiaDevMan::GetRegistrySettings, volume devices invisible by default"));
|
|
} else {
|
|
|
|
//
|
|
// Make volume devices visible in normal enumeration
|
|
//
|
|
m_bMakeVolumesVisible = TRUE;
|
|
DBG_TRC(("CWiaDevMan::GetRegistrySettings, volume devices now visible by default"));
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::ReEnumerateDevices
|
|
*
|
|
* This method enumerates devices (both real WIA and volumes). Flags
|
|
* specify whether we should do a refresh or throw events.
|
|
*
|
|
* Refresh means: Re-Enumerate devices and find out whether we
|
|
* have any extra or missing entries.
|
|
* GenEvents means throw connect events for devices.
|
|
* that we noticed have arrived or left since last eneumeration. Only
|
|
* valid with Refresh. We always throw disconnect events.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ulFlags - Options for enumeration. See DEV_MAN_FULL_REFRESH
|
|
* DEV_MAN_GEN_EVENTS
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CWiaDevMan::ReEnumerateDevices(
|
|
ULONG ulFlags)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
HRESULT hr = S_OK;
|
|
|
|
ResetEvent(g_hDevListCompleteEvent);
|
|
|
|
//
|
|
// Check whether flags indicate refresh.
|
|
//
|
|
|
|
if (ulFlags & DEV_MAN_FULL_REFRESH) {
|
|
DestroyInfoSet();
|
|
hr = CreateInfoSet();
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CWiaDevMan::ReEnumerateDevices, failed to CreateInfoSet"));
|
|
SetEvent(g_hDevListCompleteEvent);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// To generate events, we do it in 3 steps:
|
|
// 1. Mark existing devices in list as "inactive".
|
|
// 2. Do low level enumeration to find out what devices exist now, and
|
|
// create new DEVICE_OBJECTS if necessary. On creation, throw connect
|
|
// event. Mark device as "active", whether newly created or not.
|
|
// 3. Traverse device list to see whether any devices in list are still
|
|
// marked "inactive" - these devices need to be removed. For each
|
|
// device that needs to be removed, throw diconnect event.
|
|
// NOTE: This method is NOT the preferred method to handle device arrivals!
|
|
//
|
|
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
|
|
//
|
|
// This is Step 1. of events
|
|
//
|
|
ForEachDeviceInList(DEV_MAN_OP_DEV_SET_FLAGS, STIMON_AD_FLAG_MARKED_INACTIVE);
|
|
}
|
|
|
|
//
|
|
// Update service status with start pending if requested
|
|
//
|
|
if (ulFlags & DEV_MAN_STATUS_STARTP) {
|
|
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
|
|
}
|
|
|
|
//
|
|
// Now, let's enumerate WIA "devnode" devices
|
|
// This is Step 2. of events
|
|
//
|
|
|
|
// NOTE: Always Continue with enumeration, so skip the usual "if (SUCCEEDED)" checks
|
|
EnumDevNodeDevices(ulFlags);
|
|
|
|
//
|
|
// Update service status with start pending if requested
|
|
//
|
|
if (ulFlags & DEV_MAN_STATUS_STARTP) {
|
|
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
|
|
}
|
|
|
|
//
|
|
// Now, let's enumerate WIA "interface" devices
|
|
//
|
|
EnumInterfaceDevices(ulFlags);
|
|
|
|
//
|
|
// Update service status with start pending if requested
|
|
//
|
|
if (ulFlags & DEV_MAN_STATUS_STARTP) {
|
|
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
|
|
}
|
|
|
|
//
|
|
// If volumes are enabled, then enumerate them.
|
|
//
|
|
//
|
|
// Issue - do we ever generate connect events for volumes?
|
|
//
|
|
if (VolumesAreEnabled()) {
|
|
EnumVolumes(ulFlags);
|
|
}
|
|
|
|
//
|
|
// Update service status with start pending if requested
|
|
//
|
|
if (ulFlags & DEV_MAN_STATUS_STARTP) {
|
|
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
|
|
}
|
|
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
|
|
//
|
|
// This is Step 3. of events
|
|
//
|
|
ForEachDeviceInList(DEV_MAN_OP_DEV_REMOVE_MATCH, STIMON_AD_FLAG_MARKED_INACTIVE);
|
|
}
|
|
|
|
//
|
|
// Update service status with start pending if requested
|
|
//
|
|
if (ulFlags & DEV_MAN_STATUS_STARTP) {
|
|
UpdateServiceStatus(SERVICE_START_PENDING,NOERROR,START_HINT);
|
|
}
|
|
|
|
SetEvent(g_hDevListCompleteEvent);
|
|
return hr;
|
|
}
|
|
|
|
// ulFlags indicate whether we should throw connect event
|
|
HRESULT CWiaDevMan::AddDevice(
|
|
ULONG ulFlags,
|
|
DEVICE_INFO *pInfo)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
HRESULT hr = S_OK;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
if (pInfo) {
|
|
pActiveDevice = new ACTIVE_DEVICE(pInfo->wszDeviceInternalName, pInfo);
|
|
//
|
|
// Note that the ACTIVE_DEVICE will decide whether it should load
|
|
// the driver or not.
|
|
//
|
|
if (pActiveDevice) {
|
|
if (pActiveDevice->IsValid()) {
|
|
//
|
|
// Add this device to the device list. TBD: We may want
|
|
// exclusive access to the list here. Do we do it, or the caller?
|
|
//
|
|
InsertTailList(&m_leDeviceListHead,&pActiveDevice->m_ListEntry);
|
|
|
|
TAKE_ACTIVE_DEVICE tad(pActiveDevice);
|
|
|
|
pActiveDevice->InitPnPNotifications(NULL);
|
|
|
|
//
|
|
// Throw CONNECT event, if we're told to. Notice that GenerateEventForDevice
|
|
// willl change WIA_EVENT_DEVICE_CONNECTED to GUID_DeviceArrivedLaunch in
|
|
// the case of STI only devices.
|
|
//
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
//
|
|
// Only throw connect event if device is active, and it is not a
|
|
// generic mass storage device (MSC cameras are marked as MSC not
|
|
// VOL).
|
|
//
|
|
if ((pInfo->dwDeviceState & DEV_STATE_ACTIVE) && !(pInfo->dwInternalType & INTERNAL_DEV_TYPE_VOL)) {
|
|
|
|
GenerateSafeConnectEvent(pActiveDevice);
|
|
}
|
|
} else {
|
|
//
|
|
// Mark that the event was generated, even though we didn't actually throw it.
|
|
// This is so that the disconnect event will be thrown correctly.
|
|
//
|
|
if (pInfo->dwDeviceState & DEV_STATE_ACTIVE)
|
|
{
|
|
//
|
|
// NOTE: We only do this if the device is ACTIVE. Basically, this case
|
|
// is used for service startup. We would miss the device arrival events if
|
|
// we did this for inactive devices, because when the device is subsequently
|
|
// plugged in, the event would not be generated (if it was already marked).
|
|
//
|
|
pActiveDevice->m_DrvWrapper.setConnectEventState(TRUE);
|
|
}
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDevMan::AddDevice, could not create the device object"));
|
|
delete pActiveDevice;
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDevMan::AddDevice, Out of memory"));
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDevMan::AddDevice, called with no device information"));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::RemoveDevice(ACTIVE_DEVICE *pActiveDevice)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pActiveDevice) {
|
|
|
|
TAKE_ACTIVE_DEVICE t(pActiveDevice);
|
|
|
|
//
|
|
// Only throw disconnect event if device is not a generic mass storage device
|
|
// (MSC cameras are marked as MSC not VOL).
|
|
//
|
|
DEVICE_INFO *pInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
|
|
if (!(pInfo->dwInternalType & INTERNAL_DEV_TYPE_VOL)) {
|
|
//
|
|
// Generate disconnect event
|
|
//
|
|
GenerateSafeDisconnectEvent(pActiveDevice);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
if (pActiveDevice) {
|
|
//
|
|
// NOTE: Make sure that TAKE_ACTIVE_DEVICE has released the critical section BEFORE
|
|
// we call the final release
|
|
// NOTE also an assumption: The device list critical section must be grabbed before
|
|
// calling this function, else it may be unsafe.
|
|
//
|
|
|
|
//
|
|
// Destroy device object if there are no references to it
|
|
//
|
|
pActiveDevice->Release();
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::RemoveDevice(DEVICE_INFO *pInfo)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
|
|
DBG_WRN(("* Not implemented method: CWiaDevMan::RemoveDevice is being called"));
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::GenerateEventForDevice(
|
|
const GUID *guidEvent,
|
|
ACTIVE_DEVICE *pActiveDevice)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bRet = FALSE;
|
|
STINOTIFY sNotify;
|
|
|
|
if (!guidEvent || !pActiveDevice) {
|
|
DBG_WRN(("CWiaDevMan::GenerateEventForDevice, one or more NULL parameters"));
|
|
return E_POINTER;
|
|
}
|
|
|
|
memset(&sNotify, 0, sizeof(sNotify));
|
|
sNotify.dwSize = sizeof(STINOTIFY);
|
|
sNotify.guidNotificationCode = *guidEvent;
|
|
|
|
if (*guidEvent == WIA_EVENT_DEVICE_CONNECTED) {
|
|
//
|
|
// If this device or it's USD requests auto-generating a launch event on arrival
|
|
// schedule it here
|
|
//
|
|
//
|
|
// 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.
|
|
//
|
|
if (!pActiveDevice->m_DrvWrapper.IsWiaDevice()) {
|
|
BOOL bStiDeviceMustThrowEvent = (pActiveDevice->QueryFlags() & STIMON_AD_FLAG_NOTIFY_RUNNING)
|
|
&& pActiveDevice->IsEventOnArrivalNeeded();
|
|
if (!bStiDeviceMustThrowEvent) {
|
|
return S_OK;
|
|
} else {
|
|
//
|
|
// Make sure we change WIA_EVENT_DEVICE_CONNECTED to the appropriate STI guid
|
|
//
|
|
sNotify.guidNotificationCode = GUID_DeviceArrivedLaunch;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Inform the ACTIVE_DEVICE to process the event
|
|
//
|
|
{
|
|
//TDB: DO we really need exclusive access to the device object?
|
|
//TAKE_ACTIVE_DEVICE t(pActiveDevice);
|
|
|
|
DBG_TRC(("CWiaDevMan::GenerateEventForDevice,, processing event (STI or WIA) for %ws", pActiveDevice->GetDeviceID()));
|
|
bRet = pActiveDevice->ProcessEvent(&sNotify);
|
|
|
|
if (!bRet) {
|
|
DBG_WRN(("CWiaDevMan::GenerateEventForDevice, Attempted to generate event on device(%ws) arrival and failed ", pActiveDevice->GetDeviceID()));
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::NotifyRunningDriversOfEvent(
|
|
const GUID *pEvent)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Walk through list of devices
|
|
//
|
|
LIST_ENTRY *pentry = NULL;
|
|
LIST_ENTRY *pentryNext = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
{
|
|
//
|
|
// For each device in list, we want to notify the driver of an event. Note
|
|
// this only applies to WIA drivers that are already loaded.
|
|
//
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
//
|
|
// Check whether this is a WIA driver and whether it is loaded
|
|
//
|
|
if (pActiveDevice->m_DrvWrapper.IsWiaDriverLoaded()) {
|
|
|
|
BSTR bstrDevId = SysAllocString(pActiveDevice->GetDeviceID());
|
|
|
|
if (bstrDevId) {
|
|
|
|
//
|
|
// Call the driver to let it know about this event. Note that we
|
|
// don't care whether it fails or not - we simply move on to the next
|
|
// device in our list.
|
|
//
|
|
pActiveDevice->m_DrvWrapper.WIA_drvNotifyPnpEvent(pEvent,
|
|
bstrDevId,
|
|
0);
|
|
SysFreeString(bstrDevId);
|
|
bstrDevId = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::ProcessDeviceArrival()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = ReEnumerateDevices(DEV_MAN_GEN_EVENTS /*| DEV_MAN_FULL_REFRESH*/);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::ProcessDeviceRemoval(
|
|
WCHAR *wszDeviceID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
if (wszDeviceID) {
|
|
|
|
DBG_TRC(("CWiaDevMan::ProcessDeviceRemoval, finding device ID '%ls'",
|
|
wszDeviceID));
|
|
|
|
//
|
|
// Attempt to find the device
|
|
//
|
|
pActiveDevice = IsInList(DEV_MAN_IN_LIST_DEV_ID, wszDeviceID);
|
|
|
|
if (pActiveDevice) {
|
|
hr = ProcessDeviceRemoval(pActiveDevice, TRUE);
|
|
//
|
|
// Release it since it was addref'd
|
|
//
|
|
pActiveDevice->Release();
|
|
}
|
|
else
|
|
{
|
|
DBG_TRC(("CWiaDevMan::ProcessDeviceRemoval, did not find device ID '%ls'",
|
|
wszDeviceID));
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::ProcessDeviceRemoval, ProcessDeviceRemoval called with NULL device ID"));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::ProcessDeviceRemoval(
|
|
ACTIVE_DEVICE *pActiveDevice,
|
|
BOOL bGenEvent)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
if (pActiveDevice) {
|
|
|
|
//
|
|
// Mark the device as inactive
|
|
//
|
|
pActiveDevice->m_DrvWrapper.setDeviceState(pActiveDevice->m_DrvWrapper.getDeviceState() & ~DEV_STATE_ACTIVE);
|
|
|
|
if (bGenEvent) {
|
|
//
|
|
// Generate disconnect event
|
|
//
|
|
|
|
DBG_TRC(("ProcessDeviceRemoval, generating SafeDisconnect Event "
|
|
"for device '%ls'", pActiveDevice->GetDeviceID()));
|
|
|
|
GenerateSafeDisconnectEvent(pActiveDevice);
|
|
}
|
|
|
|
{
|
|
//
|
|
// Note that we do not take the active device during event generation.
|
|
//
|
|
//TAKE_ACTIVE_DEVICE tad(pActiveDevice);
|
|
|
|
//
|
|
// Remove any device notification callbacks
|
|
//
|
|
pActiveDevice->DisableDeviceNotifications();
|
|
|
|
//
|
|
// Stop PnP notifications immediately. This is important to free interface handle
|
|
//
|
|
pActiveDevice->StopPnPNotifications();
|
|
|
|
//
|
|
// Unload the driver
|
|
//
|
|
pActiveDevice->UnLoadDriver(TRUE);
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::ProcessDeviceRemoval, Device not in list"));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
ACTIVE_DEVICE* CWiaDevMan::IsInList(
|
|
ULONG ulFlags,
|
|
const WCHAR *wszID)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
LIST_ENTRY *pentry = NULL;
|
|
LIST_ENTRY *pentryNext = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
DEVICE_INFO *pDevInfo = NULL;
|
|
|
|
//
|
|
// Walk through list of devices and count the ones of appropriate type
|
|
//
|
|
{
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
pDevInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
|
|
|
|
if (pDevInfo) {
|
|
|
|
//
|
|
// Decide what to compare, based on the flags. Note that if more
|
|
// that one flag is set, then the comarison will be done on more
|
|
// than one field. We will return TRUE on the first hit.
|
|
//
|
|
|
|
//
|
|
// Here's a quick workaround for volume devices: whenever we hit
|
|
// potential match, check whether this is of the correct VolumeType.
|
|
//
|
|
if (pDevInfo->dwInternalType & INTERNAL_DEV_TYPE_VOL) {
|
|
if (!IsCorrectVolumeType(pDevInfo->wszAlternateID)) {
|
|
DBG_TRC(("CWiaDevMan::IsInList, Volume (%ws) is not of correct type.", pDevInfo->wszAlternateID));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (DEV_MAN_IN_LIST_DEV_ID) {
|
|
if (lstrcmpiW(pDevInfo->wszDeviceInternalName, wszID) == 0) {
|
|
pActiveDevice->AddRef();
|
|
return pActiveDevice;
|
|
}
|
|
}
|
|
if (ulFlags & DEV_MAN_IN_LIST_ALT_ID) {
|
|
if (pDevInfo->wszAlternateID) {
|
|
if (lstrcmpiW(pDevInfo->wszAlternateID, wszID) == 0) {
|
|
pActiveDevice->AddRef();
|
|
return pActiveDevice;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG CWiaDevMan::NumDevices(ULONG ulFlags)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
LIST_ENTRY *pentry = NULL;
|
|
LIST_ENTRY *pentryNext = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
ULONG ulCount = 0;
|
|
|
|
//
|
|
// Walk through list of devices and count the ones of appropriate type
|
|
//
|
|
{
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
//
|
|
// Check whether this device is one of the ones we want to count.
|
|
// If it is, then increment the count.
|
|
//
|
|
|
|
if (IsCorrectEnumType(ulFlags, pActiveDevice->m_DrvWrapper.getDevInfo())) {
|
|
++ulCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ulCount;
|
|
}
|
|
|
|
VOID WINAPI CWiaDevMan::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 _tcs(m_csDevList);
|
|
|
|
for ( pentry = m_leDeviceListHead.Flink;
|
|
pentry != &m_leDeviceListHead;
|
|
pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
if (!pActiveDevice->IsValid()) {
|
|
ASSERT(("CWiaDevMan::EnumerateActiveDevicesWithCallback, Invalid device signature", 0));
|
|
break;
|
|
}
|
|
|
|
pfn(pActiveDevice,pContext);
|
|
}
|
|
}
|
|
// END PROTECTED CODE
|
|
|
|
}
|
|
|
|
|
|
HRESULT CWiaDevMan::GetDevInfoStgs(
|
|
ULONG ulFlags,
|
|
ULONG *pulNumDevInfoStream,
|
|
IWiaPropertyStorage ***pppOutputStorageArray)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
HRESULT hr = S_OK;
|
|
ULONG ulCount = 0;
|
|
ULONG ulIndex = 0;
|
|
IWiaPropertyStorage **ppDevInfoStgs = NULL;
|
|
IWiaPropertyStorage **ppDevAndRemoteDevStgs = NULL;
|
|
ULONG ulRemoteDevices = 0;
|
|
|
|
*pulNumDevInfoStream = 0;
|
|
*pppOutputStorageArray = NULL;
|
|
|
|
//
|
|
// Count number of devices matching our category flags
|
|
//
|
|
|
|
ulCount = NumDevices(ulFlags);
|
|
if (ulCount) {
|
|
|
|
//
|
|
// Allocate space for that many streams
|
|
//
|
|
ppDevInfoStgs = new IWiaPropertyStorage*[ulCount];
|
|
if (ppDevInfoStgs) {
|
|
|
|
memset(ppDevInfoStgs, 0, sizeof(IWiaPropertyStorage*) * ulCount);
|
|
//
|
|
// Go through device list, and for every device is our category, save
|
|
// its information to a stream
|
|
//
|
|
LIST_ENTRY *pentry = NULL;
|
|
LIST_ENTRY *pentryNext = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
{
|
|
ulIndex = 0;
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
//
|
|
// Paranoid check for overflow - break if we've reached our count
|
|
//
|
|
if (ulIndex >= ulCount) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check whether this device is one of the ones we want
|
|
//
|
|
|
|
if (IsCorrectEnumType(ulFlags, pActiveDevice->m_DrvWrapper.getDevInfo())) {
|
|
|
|
DEVICE_INFO *pDeviceInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
|
|
if (pDeviceInfo) {
|
|
|
|
//
|
|
// Here's a work-around for MSC or Volume devices. Since we cant get the
|
|
// display name when we receive the volume arrival notification, we should
|
|
// refresh it here
|
|
//
|
|
if (pDeviceInfo->dwInternalType & (INTERNAL_DEV_TYPE_VOL | INTERNAL_DEV_TYPE_MSC_CAMERA)) {
|
|
RefreshDevInfoFromMountPoint(pDeviceInfo, pDeviceInfo->wszAlternateID);
|
|
}
|
|
|
|
ppDevInfoStgs[ulIndex] = CreateDevInfoStg(pDeviceInfo);
|
|
if (!ppDevInfoStgs[ulIndex]) {
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
++ulIndex;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
} else {
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
|
|
/* Removed remote device enumeration in order to reduce our attack surface.
|
|
//
|
|
// If everything succeeded, check whether there are any remote devices installed.
|
|
// If there are, add them to the list. Skip this step if local devices only was
|
|
// requested.
|
|
//
|
|
|
|
if (SUCCEEDED(hr) && !(ulFlags & DEV_MAN_ENUM_TYPE_LOCAL_ONLY)) {
|
|
|
|
ulRemoteDevices = CountRemoteDevices(0);
|
|
if (ulRemoteDevices) {
|
|
//
|
|
// Allocate space for the new device list. It must be big enough to hold both
|
|
// local and remote dev. info. stgs.
|
|
//
|
|
|
|
ppDevAndRemoteDevStgs = new IWiaPropertyStorage*[ulCount + ulRemoteDevices];
|
|
if (ppDevAndRemoteDevStgs) {
|
|
memset(ppDevAndRemoteDevStgs, 0, sizeof(IWiaPropertyStorage*) * (ulCount + ulRemoteDevices));
|
|
|
|
//
|
|
// Copy the local dev. info. storages
|
|
//
|
|
for (ulIndex = 0; ulIndex < ulCount; ulIndex++) {
|
|
ppDevAndRemoteDevStgs[ulIndex] = ppDevInfoStgs[ulIndex];
|
|
}
|
|
|
|
//
|
|
// No need for the local only array, since we have a copy of the local
|
|
// dev. info. stgs in the ppDevAndRemoteDevStgs array.
|
|
//
|
|
if (ppDevInfoStgs) {
|
|
delete [] ppDevInfoStgs;
|
|
ppDevInfoStgs = NULL;
|
|
}
|
|
|
|
//
|
|
// Set ppDevInfoStgs to point to ppDevAndRemoteDevStgs. This is simply cosmetic,
|
|
// since our code that sets the return values can now always use the ppDevInfoStgs
|
|
// pointer.
|
|
//
|
|
ppDevInfoStgs = ppDevAndRemoteDevStgs;
|
|
|
|
//
|
|
// Create dev. info. stgs for the remote devices. We pass in the address where
|
|
// the first dev. info. stg. will reside, and the maxiumum number of dev. info. stgs
|
|
// to fill in. This is to avoid the problem where the registry might be updated
|
|
// in between us counting the number of remote devices with CountRemoteDevices(..),
|
|
// and actually enumerating them with FillRemoteDeviceStgs(..).
|
|
//
|
|
hr = FillRemoteDeviceStgs(&ppDevAndRemoteDevStgs[ulCount], &ulRemoteDevices);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// Increment the device count to be local + remote devices
|
|
//
|
|
ulCount += ulRemoteDevices;
|
|
} else {
|
|
|
|
//
|
|
// If we failed to get remote devices, that's OK since it's non-fatal.
|
|
// Do some clean-up so we return local devices only. This involves
|
|
// deleting any remote device info. stgs. that were added after the
|
|
// local dev. info. stgs.
|
|
//
|
|
|
|
for (ulIndex = ulCount; ulIndex < (ulCount + ulRemoteDevices); ulIndex++) {
|
|
if (ppDevAndRemoteDevStgs[ulIndex]) {
|
|
delete ppDevAndRemoteDevStgs[ulIndex];
|
|
ppDevAndRemoteDevStgs[ulIndex] = NULL;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Set the return
|
|
//
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
*pulNumDevInfoStream = ulCount;
|
|
*pppOutputStorageArray = ppDevInfoStgs;
|
|
|
|
if (*pulNumDevInfoStream) {
|
|
hr = S_OK;
|
|
} else {
|
|
hr = S_FALSE;
|
|
}
|
|
} else {
|
|
//
|
|
// On failure, cleanup.
|
|
//
|
|
|
|
if (ppDevInfoStgs) {
|
|
for (ulIndex = 0; ulIndex < ulCount; ulIndex++) {
|
|
if (ppDevInfoStgs[ulIndex]) {
|
|
delete ppDevInfoStgs[ulIndex];
|
|
ppDevInfoStgs[ulIndex] = NULL;
|
|
}
|
|
}
|
|
delete [] ppDevInfoStgs;
|
|
ppDevInfoStgs = NULL;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::GetDeviceValue(
|
|
ACTIVE_DEVICE *pActiveDevice,
|
|
WCHAR *pValueName,
|
|
DWORD *pType,
|
|
BYTE *pData,
|
|
DWORD *cbData)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
DEVICE_INFO *pInfo = NULL;
|
|
HKEY hDevRegKey = NULL;
|
|
HKEY hDevDataRegKey = NULL;
|
|
DWORD dwError = 0;
|
|
|
|
|
|
if (pActiveDevice) {
|
|
|
|
TAKE_ACTIVE_DEVICE tad(pActiveDevice);
|
|
|
|
pInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
|
|
if (pInfo) {
|
|
|
|
//
|
|
// Get the device registry key
|
|
//
|
|
|
|
hDevRegKey = GetDeviceHKey(pActiveDevice, NULL);
|
|
if (hDevRegKey) {
|
|
|
|
//
|
|
// Open the DeviceData section
|
|
//
|
|
dwError = RegCreateKeyExW(hDevRegKey,
|
|
REGSTR_VAL_DATA_W,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hDevDataRegKey,
|
|
NULL);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Call RegQueryValueEx.
|
|
//
|
|
dwError = RegQueryValueExW(hDevDataRegKey,
|
|
pValueName,
|
|
NULL,
|
|
pType,
|
|
pData,
|
|
cbData);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
hr = S_OK;
|
|
}
|
|
RegCloseKey(hDevDataRegKey);
|
|
}
|
|
|
|
//
|
|
// Close the device registry key
|
|
//
|
|
RegCloseKey(hDevRegKey);
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::GetDeviceValue, DeviceInfo is not valid"));
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::GetDeviceValue, called with NULL"));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
WCHAR* CWiaDevMan::AllocGetInterfaceNameFromDevInfo(DEVICE_INFO *pDevInfo)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
WCHAR *wszInterface = NULL;
|
|
GUID guidClass = GUID_DEVCLASS_IMAGE;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwStrLen = 0;
|
|
|
|
SP_DEVICE_INTERFACE_DATA *pspDevInterfaceData = NULL;
|
|
SP_DEVICE_INTERFACE_DATA spTemp;
|
|
|
|
DWORD dwDetailSize = 0;
|
|
SP_DEVICE_INTERFACE_DETAIL_DATA_W *pspDevInterfaceDetailData = NULL;
|
|
|
|
if (pDevInfo) {
|
|
//
|
|
// Check whether this is an interface or devnode device
|
|
//
|
|
if (pDevInfo->dwInternalType & INTERNAL_DEV_TYPE_INTERFACE) {
|
|
pspDevInterfaceData = &pDevInfo->spDevInterfaceData;
|
|
} else {
|
|
spTemp.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
spTemp.InterfaceClassGuid = guidClass;
|
|
bRet = SetupDiEnumDeviceInterfaces (m_DeviceInfoSet,
|
|
&pDevInfo->spDevInfoData,
|
|
&guidClass,
|
|
0,
|
|
&spTemp);
|
|
if (bRet) {
|
|
pspDevInterfaceData = &spTemp;
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::AllocGetInterfaceNameFromDevInfo, SetupDiEnumDeviceInterfaces failed to return interface information"))
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have a valid pspDevInterfaceData, then we can get the interface
|
|
// detail information, which includes the interface name
|
|
//
|
|
|
|
if (pspDevInterfaceData) {
|
|
|
|
SP_DEVINFO_DATA spDevInfoData;
|
|
|
|
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
//
|
|
// Get the required size for the interface detail
|
|
//
|
|
bRet = SetupDiGetDeviceInterfaceDetailW(m_DeviceInfoSet,
|
|
pspDevInterfaceData,
|
|
NULL,
|
|
0,
|
|
&dwDetailSize,
|
|
&spDevInfoData);
|
|
DWORD dwError = GetLastError();
|
|
if ((dwError == ERROR_INSUFFICIENT_BUFFER) && dwDetailSize) {
|
|
pspDevInterfaceDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*) new BYTE[dwDetailSize];
|
|
if (pspDevInterfaceDetailData) {
|
|
pspDevInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
|
|
//
|
|
// Get the actual interface detail
|
|
//
|
|
bRet = SetupDiGetDeviceInterfaceDetailW(m_DeviceInfoSet,
|
|
pspDevInterfaceData,
|
|
pspDevInterfaceDetailData,
|
|
dwDetailSize,
|
|
&dwDetailSize,
|
|
NULL);
|
|
if (bRet) {
|
|
|
|
dwStrLen = lstrlenW(pspDevInterfaceDetailData->DevicePath);
|
|
wszInterface = new WCHAR[dwStrLen + 1];
|
|
if (wszInterface) {
|
|
lstrcpyW(wszInterface, pspDevInterfaceDetailData->DevicePath);
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::AllocGetInterfaceNameFromDevInfo, out of memory"))
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::AllocGetInterfaceNameFromDevInfo, Could not get SP_DEVICE_INTERFACE_DETAIL_DATA"));
|
|
}
|
|
|
|
delete [] pspDevInterfaceDetailData;
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::AllocGetInterfaceNameFromDevInfo, out of memory"))
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::AllocGetInterfaceNameFromDevInfo, SetupDiGetDeviceInterfaceDetail returned an error, could not determine buffer size"))
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::AllocGetInterfaceNameFromDevInfo, Could not get SP_DEVICE_INTERFACE_DATA"));
|
|
}
|
|
}
|
|
|
|
return wszInterface;
|
|
}
|
|
|
|
//
|
|
// Look up driver name by interface name
|
|
// NOTE: In the interests of time, this was copied directly from infoset.h
|
|
|
|
BOOL
|
|
CWiaDevMan::LookupDriverNameFromInterfaceName(
|
|
LPCTSTR pszInterfaceName,
|
|
StiCString *pstrDriverName)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
BUFFER bufDetailData;
|
|
|
|
SP_DEVINFO_DATA spDevInfoData;
|
|
SP_DEVICE_INTERFACE_DATA spDevInterfaceData;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pspDevInterfaceDetailData;
|
|
|
|
TCHAR szDevDriver[STI_MAX_INTERNAL_NAME_LENGTH];
|
|
|
|
DWORD cbData = 0;
|
|
DWORD dwErr = 0;
|
|
|
|
HKEY hkDevice = (HKEY)INVALID_HANDLE_VALUE;
|
|
LONG lResult = ERROR_SUCCESS;
|
|
DWORD dwType = REG_SZ;
|
|
|
|
BOOL fRet = FALSE;
|
|
BOOL fDataAcquired = FALSE;
|
|
|
|
bufDetailData.Resize(sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) +
|
|
MAX_PATH * sizeof(TCHAR) +
|
|
16
|
|
);
|
|
|
|
pspDevInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) bufDetailData.QueryPtr();
|
|
|
|
if (!pspDevInterfaceDetailData) {
|
|
return (CR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
//
|
|
// Locate this device interface in our device information set.
|
|
//
|
|
spDevInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
|
|
if (SetupDiOpenDeviceInterface(m_DeviceInfoSet,
|
|
pszInterfaceName,
|
|
DIODI_NO_ADD,
|
|
&spDevInterfaceData)) {
|
|
|
|
|
|
//
|
|
// First try to open interface regkey.
|
|
//
|
|
|
|
hkDevice = SetupDiOpenDeviceInterfaceRegKey(m_DeviceInfoSet,
|
|
&spDevInterfaceData,
|
|
0,
|
|
KEY_READ);
|
|
if(INVALID_HANDLE_VALUE != hkDevice){
|
|
|
|
*szDevDriver = TEXT('\0');
|
|
cbData = sizeof(szDevDriver);
|
|
lResult = RegQueryValueEx(hkDevice,
|
|
REGSTR_VAL_DEVICE_ID,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szDevDriver,
|
|
&cbData);
|
|
dwErr = ::GetLastError();
|
|
RegCloseKey(hkDevice);
|
|
hkDevice = (HKEY)INVALID_HANDLE_VALUE;
|
|
|
|
if(ERROR_SUCCESS == lResult){
|
|
fDataAcquired = TRUE;
|
|
}
|
|
}
|
|
|
|
if(!fDataAcquired){
|
|
|
|
//
|
|
// Try to open devnode regkey.
|
|
//
|
|
|
|
cbData = 0;
|
|
pspDevInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
fRet = SetupDiGetDeviceInterfaceDetail(m_DeviceInfoSet,
|
|
&spDevInterfaceData,
|
|
pspDevInterfaceDetailData,
|
|
bufDetailData.QuerySize(),
|
|
&cbData,
|
|
&spDevInfoData);
|
|
if(fRet){
|
|
|
|
//
|
|
// Get device interface registry key.
|
|
//
|
|
|
|
hkDevice = SetupDiOpenDevRegKey(m_DeviceInfoSet,
|
|
&spDevInfoData,
|
|
DICS_FLAG_GLOBAL,
|
|
0,
|
|
DIREG_DRV,
|
|
KEY_READ);
|
|
dwErr = ::GetLastError();
|
|
} else {
|
|
DBG_ERR(("SetupDiGetDeviceInterfaceDetail() Failed Err=0x%x",GetLastError()));
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != hkDevice) {
|
|
|
|
*szDevDriver = TEXT('\0');
|
|
cbData = sizeof(szDevDriver);
|
|
|
|
lResult = RegQueryValueEx(hkDevice,
|
|
REGSTR_VAL_DEVICE_ID,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)szDevDriver,
|
|
&cbData);
|
|
dwErr = ::GetLastError();
|
|
RegCloseKey(hkDevice);
|
|
hkDevice = (HKEY)INVALID_HANDLE_VALUE;
|
|
|
|
if(ERROR_SUCCESS == lResult){
|
|
fDataAcquired = TRUE;
|
|
}
|
|
} else {
|
|
DBG_ERR(("SetupDiOpenDevRegKey() Failed Err=0x%x",GetLastError()));
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
|
|
if (fDataAcquired) {
|
|
//
|
|
// Got it
|
|
//
|
|
pstrDriverName->CopyString(szDevDriver);
|
|
fRet = TRUE;
|
|
}
|
|
} else {
|
|
DBG_ERR(("CWiaDevMan::LookupDriverNameFromInterfaceName() Failed Err=0x%x",GetLastError()));
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return (fRet);
|
|
}
|
|
|
|
ACTIVE_DEVICE* CWiaDevMan::LookDeviceFromPnPHandles(
|
|
HANDLE hInterfaceHandle,
|
|
HANDLE hPnPSink)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
LIST_ENTRY *pentry;
|
|
LIST_ENTRY *pentryNext;
|
|
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
ACTIVE_DEVICE *pCurrent = NULL;
|
|
|
|
{
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pCurrent = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
if ( !pCurrent->IsValid()) {
|
|
DBG_WRN(("CWiaDevMan::LookupDeviceByPnPHandles, Invalid device object, aborting search..."));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check whether this PnP notification handle returned from
|
|
// RegisterDeviceNotification is the one we're looking for
|
|
//
|
|
if ( hPnPSink == pCurrent->GetNotificationsSink()) {
|
|
pActiveDevice = pCurrent;
|
|
pActiveDevice->AddRef();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pActiveDevice;
|
|
}
|
|
|
|
VOID CWiaDevMan::DestroyInfoSet()
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
if (m_DeviceInfoSet) {
|
|
SetupDiDestroyDeviceInfoList(m_DeviceInfoSet);
|
|
m_DeviceInfoSet = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT CWiaDevMan::CreateInfoSet()
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD dwErr = 0;
|
|
HDEVINFO hdvNew = NULL;
|
|
|
|
//
|
|
// Create a blank infoset
|
|
//
|
|
|
|
m_DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);
|
|
|
|
if ( m_DeviceInfoSet && (m_DeviceInfoSet != INVALID_HANDLE_VALUE)) {
|
|
|
|
|
|
//
|
|
// Now we can retrieve the existing list of WIA devices
|
|
// into the device information set we created above.
|
|
//
|
|
|
|
//
|
|
// This adds WIA "devnode" devices
|
|
//
|
|
hdvNew = SetupDiGetClassDevsEx(&(GUID_DEVCLASS_IMAGE),
|
|
NULL,
|
|
NULL,
|
|
DIGCF_DEVICEINTERFACE,
|
|
m_DeviceInfoSet,
|
|
NULL,
|
|
NULL);
|
|
if (hdvNew == INVALID_HANDLE_VALUE) {
|
|
dwErr = ::GetLastError();
|
|
DBG_ERR(("CWiaDevMan::CreateInfoSet, SetupDiGetClassDevsEx failed with 0x%lx\n", dwErr));
|
|
} else {
|
|
|
|
//
|
|
// This adds WIA "interface" devices
|
|
//
|
|
hdvNew = SetupDiGetClassDevsEx(&(GUID_DEVCLASS_IMAGE),
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
m_DeviceInfoSet,
|
|
NULL,
|
|
NULL);
|
|
if (hdvNew == INVALID_HANDLE_VALUE) {
|
|
dwErr = ::GetLastError();
|
|
DBG_ERR(("CWiaDevMan::CreateInfoSet, second SetupDiGetClassDevsEx failed with 0x%lx\n", dwErr));
|
|
}
|
|
}
|
|
} else {
|
|
dwErr = ::GetLastError();
|
|
DBG_ERR(("CWiaDevMan::CreateInfoSet, SetupDiCreateDeviceInfoList failed with 0x%lx\n", dwErr));
|
|
}
|
|
|
|
if (dwErr) {
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
VOID CWiaDevMan::DestroyDeviceList()
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
LIST_ENTRY *pentry = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
DBG_TRC(("Destroying list of active devices"));
|
|
|
|
//
|
|
// Go through the list terminating devices
|
|
//
|
|
while (!IsListEmpty(&m_leDeviceListHead)) {
|
|
|
|
pentry = m_leDeviceListHead.Flink;
|
|
|
|
//
|
|
// Remove from the list ( reset list entry )
|
|
//
|
|
RemoveHeadList(&m_leDeviceListHead);
|
|
InitializeListHead( pentry );
|
|
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
//
|
|
// Remove any device notification callbacks
|
|
//
|
|
pActiveDevice->DisableDeviceNotifications();
|
|
|
|
// Release device object
|
|
pActiveDevice->Release();
|
|
}
|
|
|
|
}
|
|
|
|
BOOL CWiaDevMan::VolumesAreEnabled()
|
|
{
|
|
return m_bVolumesEnabled;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::ForEachDeviceInList(
|
|
ULONG ulFlags,
|
|
ULONG ulParam)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Walk through list of devices
|
|
//
|
|
LIST_ENTRY *pentry = NULL;
|
|
LIST_ENTRY *pentryNext = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
{
|
|
//
|
|
// For each device in list, call the appropriate device manager method
|
|
//
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
switch (ulFlags) {
|
|
case DEV_MAN_OP_DEV_SET_FLAGS:
|
|
{
|
|
//
|
|
// Set the flags on the ACTIVE_DEVICE
|
|
//
|
|
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() | ulParam);
|
|
break;
|
|
}
|
|
|
|
case DEV_MAN_OP_DEV_REMOVE_MATCH:
|
|
{
|
|
//
|
|
// Remove device if the ACTIVE_DEVICE flags have the specified bit set
|
|
//
|
|
if (pActiveDevice->QueryFlags() & ulParam) {
|
|
|
|
hr = RemoveDevice(pActiveDevice);
|
|
}
|
|
break;
|
|
}
|
|
case DEV_MAN_OP_DEV_REREAD:
|
|
{
|
|
//
|
|
// Get the device settings, which includes rebuilding
|
|
// the STI event list from registry
|
|
//
|
|
pActiveDevice->GetDeviceSettings();
|
|
break;
|
|
}
|
|
|
|
case DEV_MAN_OP_DEV_RESTORE_EVENT:
|
|
{
|
|
|
|
DEVICE_INFO *pDeviceInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
|
|
|
|
if (pDeviceInfo) {
|
|
|
|
//
|
|
// NOTE: We don't want to restore MSC camera device event handlers
|
|
// here. This is because they are restored along with the global handlers
|
|
// in RestoreAllPersistentCBs
|
|
//
|
|
if (!(pDeviceInfo->dwInternalType & INTERNAL_DEV_TYPE_MSC_CAMERA)) {
|
|
HKEY hKey = NULL;
|
|
|
|
//
|
|
// Get the device's HKEY and restore any device specific event handlers
|
|
//
|
|
hKey = GetDeviceHKey(pActiveDevice, NULL);
|
|
if (hKey) {
|
|
g_eventNotifier.RestoreDevPersistentCBs(hKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
// do nothing
|
|
;
|
|
};
|
|
|
|
if (FAILED(hr)) {
|
|
DBG_WRN(("CWiaDevMan::ForEachDeviceInList, failed with params (0x%8X, 0x%8X), on device %ws",
|
|
ulFlags, ulParam, pActiveDevice->GetDeviceID()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CWiaDevMan::EnumDevNodeDevices(
|
|
ULONG ulFlags)
|
|
{
|
|
HRESULT hr = S_OK; // Notice that none of these errors are fatal. We always return S_OK.
|
|
|
|
|
|
ULONG ulIndex = 0;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD dwFlags = DIGCF_PROFILE;
|
|
CONFIGRET ConfigRet = CR_SUCCESS;
|
|
ULONG ulStatus = 0;
|
|
ULONG ulProblemNumber = 0;
|
|
HKEY hDevRegKey = (HKEY)INVALID_HANDLE_VALUE;
|
|
DWORD dwDeviceState = 0;
|
|
DWORD cbData = 0;
|
|
DEVICE_INFO *pDevInfo = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
|
|
SP_DEVINFO_DATA spDevInfoData;
|
|
WCHAR wszDeviceID[STI_MAX_INTERNAL_NAME_LENGTH];
|
|
|
|
//
|
|
// Enumerate "devnode" devices.
|
|
//
|
|
|
|
spDevInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
|
|
for (ulIndex = 0; SetupDiEnumDeviceInfo (m_DeviceInfoSet, ulIndex, &spDevInfoData); ulIndex++) {
|
|
|
|
//
|
|
// See if this node is active.
|
|
//
|
|
|
|
ulStatus = 0;
|
|
ulProblemNumber = 0;
|
|
ConfigRet = CM_Get_DevNode_Status(&ulStatus,
|
|
&ulProblemNumber,
|
|
spDevInfoData.DevInst,
|
|
0);
|
|
if(CR_SUCCESS != ConfigRet){
|
|
DBG_WRN(("CWiaDevMan::EnumDevNodeDevices, On index %d, CM_Get_DevNode_Status returned error, assuming device is inactive", ulIndex));
|
|
}
|
|
|
|
//
|
|
// Get device regkey.
|
|
//
|
|
|
|
hDevRegKey = SetupDiOpenDevRegKey(m_DeviceInfoSet,
|
|
&spDevInfoData,
|
|
DICS_FLAG_GLOBAL,
|
|
0,
|
|
DIREG_DRV,
|
|
KEY_READ);
|
|
if(hDevRegKey != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// See if it has "StillImage" in SubClass key.
|
|
//
|
|
|
|
if(IsStiRegKey(hDevRegKey)){
|
|
//
|
|
// Get this device name
|
|
//
|
|
|
|
cbData = sizeof(wszDeviceID);
|
|
dwError = RegQueryValueExW(hDevRegKey,
|
|
REGSTR_VAL_DEVICE_ID_W,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)wszDeviceID,
|
|
&cbData);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
wszDeviceID[STI_MAX_INTERNAL_NAME_LENGTH-1] = L'\0';
|
|
|
|
//
|
|
// Check whether we already have the appropriate DEVICE_OBJECT in the list.
|
|
// If we do, find out whether we should generate a connect/disconnect event, else fill
|
|
// out the DeviceInformation struct and create a new DEVICE_OBJECT for it.
|
|
//
|
|
|
|
DBG_TRC(("EnumDevNodeDevices, searching for device '%ls' in list",
|
|
wszDeviceID));
|
|
|
|
pActiveDevice = IsInList(DEV_MAN_IN_LIST_DEV_ID, wszDeviceID);
|
|
if (pActiveDevice) {
|
|
//
|
|
// Mark this device as active
|
|
//
|
|
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() & ~STIMON_AD_FLAG_MARKED_INACTIVE);
|
|
|
|
DWORD dwOldDevState;
|
|
dwOldDevState = pActiveDevice->m_DrvWrapper.getDeviceState();
|
|
|
|
//
|
|
// Mark the new device state appropriately
|
|
//
|
|
|
|
dwDeviceState = MapCMStatusToDeviceState(dwOldDevState, ulStatus, ulProblemNumber);
|
|
|
|
//
|
|
// Update the device information. Certain fields are transient e.g.
|
|
// device state and port name
|
|
//
|
|
|
|
RefreshDevInfoFromHKey(pActiveDevice->m_DrvWrapper.getDevInfo(),
|
|
hDevRegKey,
|
|
dwDeviceState,
|
|
&spDevInfoData,
|
|
NULL);
|
|
DBG_TRC(("EnumDevNodeDevices, device '%ls' is in the list, "
|
|
"Old Device State = '%lu', New Device State = %lu",
|
|
wszDeviceID, dwOldDevState, dwDeviceState));
|
|
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
|
|
//
|
|
// Check whether its state changed. If it changed
|
|
// from inactive to active, throw connect event. If
|
|
// state changed from active to inactive, throw disconnect event.
|
|
//
|
|
if (((~dwOldDevState) & DEV_STATE_ACTIVE) &&
|
|
(dwDeviceState & DEV_STATE_ACTIVE)) {
|
|
|
|
//
|
|
// Load the driver
|
|
//
|
|
pActiveDevice->LoadDriver(TRUE);
|
|
GenerateSafeConnectEvent(pActiveDevice);
|
|
|
|
DBG_TRC(("EnumDevNodeDevices, generating SafeConnect Event "
|
|
"for device '%ls'", wszDeviceID));
|
|
|
|
} else if ((dwOldDevState & DEV_STATE_ACTIVE) &&
|
|
((~dwDeviceState) & DEV_STATE_ACTIVE)) {
|
|
|
|
DBG_TRC(("EnumDevNodeDevices, generating SafeDisconnect Event "
|
|
"for device '%ls'", wszDeviceID));
|
|
|
|
GenerateSafeDisconnectEvent(pActiveDevice);
|
|
pActiveDevice->UnLoadDriver(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE: In the case when we are not started yet, and the class installer
|
|
// starts us to install a device, certain timing conditions make it so that
|
|
// the device is enumerated on startup, but the device is not registred
|
|
// for PnP notifications like device removal. This is because it is not fully installed
|
|
// yet, so the lookup of the interface name to do a CreateFile on fails.
|
|
// So, to get around that, when the class installer is finished installing,
|
|
// and tells us to reenumerate, we also check whether the device is registered
|
|
// for PnP notifications. If it isn't, we attempt to register. If successful, we
|
|
// generate connect event (since we missed it the first time round)
|
|
//
|
|
if (!pActiveDevice->IsRegisteredForDeviceRemoval() && (dwDeviceState & DEV_STATE_ACTIVE)) {
|
|
|
|
//
|
|
// This device is active but is not yet registered. Let's attempt to register it
|
|
//
|
|
if (pActiveDevice->InitPnPNotifications(NULL)) {
|
|
|
|
//
|
|
// Successful. So now generate connect event if told to do so
|
|
//
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
GenerateSafeConnectEvent(pActiveDevice);
|
|
}
|
|
}
|
|
}
|
|
|
|
pActiveDevice->Release();
|
|
} else {
|
|
//
|
|
// Create and fill out a DEVICE_INFO structure. For
|
|
//
|
|
|
|
dwDeviceState = MapCMStatusToDeviceState(0, ulStatus, ulProblemNumber);
|
|
|
|
DBG_TRC(("EnumDevNodeDevices, device '%ls' is NOT in the list, "
|
|
"Device State = %lu", wszDeviceID, dwDeviceState));
|
|
|
|
pDevInfo = CreateDevInfoFromHKey(hDevRegKey, dwDeviceState, &spDevInfoData, NULL);
|
|
DumpDevInfo(pDevInfo);
|
|
AddDevice(ulFlags, pDevInfo);
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::EnumDevNodeDevices, On index %d, could not read DeviceID", ulIndex));
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::EnumDevNodeDevices, device on index %d is not StillImage", ulIndex));
|
|
}
|
|
|
|
//
|
|
// Close the device registry key
|
|
//
|
|
|
|
RegCloseKey(hDevRegKey);
|
|
hDevRegKey = NULL;
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::EnumDevNodeDevices, SetupDiOpenDevRegKey on index %d return INVALID_HANDLE_VALUE", ulIndex));
|
|
}
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CWiaDevMan::EnumInterfaceDevices(
|
|
ULONG ulFlags)
|
|
{
|
|
HRESULT hr = S_OK; // Notice that none of these errors are fatal. We always return S_OK.
|
|
|
|
|
|
ULONG ulIndex = 0;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD dwFlags = DIGCF_PROFILE;
|
|
CONFIGRET ConfigRet = CR_SUCCESS;
|
|
GUID guidInterface = GUID_DEVCLASS_IMAGE;
|
|
ULONG ulStatus = 0;
|
|
ULONG ulProblemNumber = 0;
|
|
HKEY hDevRegKey = (HKEY)INVALID_HANDLE_VALUE;
|
|
DWORD dwDeviceState = 0;
|
|
DWORD cbData = 0;
|
|
DEVICE_INFO *pDevInfo = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
DWORD dwDetailDataSize = 0;
|
|
BOOL bSkip = FALSE;
|
|
|
|
|
|
SP_DEVINFO_DATA spDevInfoData;
|
|
SP_DEVICE_INTERFACE_DATA spDevInterfaceData;
|
|
WCHAR wszDeviceID[STI_MAX_INTERNAL_NAME_LENGTH];
|
|
|
|
//
|
|
// Enumerate "devnode" devices.
|
|
//
|
|
|
|
spDevInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
|
|
spDevInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
|
|
for (ulIndex = 0; SetupDiEnumDeviceInterfaces(m_DeviceInfoSet, NULL, &guidInterface, ulIndex, &spDevInterfaceData); ulIndex++) {
|
|
|
|
|
|
|
|
dwDeviceState = 0;
|
|
bSkip = FALSE;
|
|
hDevRegKey = SetupDiOpenDeviceInterfaceRegKey(m_DeviceInfoSet,
|
|
&spDevInterfaceData,
|
|
0,
|
|
KEY_READ);
|
|
if(hDevRegKey != INVALID_HANDLE_VALUE) {
|
|
//
|
|
// See if it has "StillImage" in SubClass key.
|
|
//
|
|
|
|
if(IsStiRegKey(hDevRegKey)) {
|
|
//
|
|
// Get this device name
|
|
//
|
|
|
|
cbData = sizeof(wszDeviceID);
|
|
dwError = RegQueryValueExW(hDevRegKey,
|
|
REGSTR_VAL_DEVICE_ID_W,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)wszDeviceID,
|
|
&cbData);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
//
|
|
// Get devnode which this interface is created on.
|
|
//
|
|
|
|
dwError = SetupDiGetDeviceInterfaceDetail(m_DeviceInfoSet,
|
|
&spDevInterfaceData,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&spDevInfoData);
|
|
if(dwError == ERROR_INSUFFICIENT_BUFFER){
|
|
|
|
//
|
|
// See if this node is active.
|
|
//
|
|
|
|
ulStatus = 0;
|
|
ulProblemNumber = 0;
|
|
ConfigRet = CM_Get_DevNode_Status(&ulStatus,
|
|
&ulProblemNumber,
|
|
spDevInfoData.DevInst,
|
|
0);
|
|
|
|
if(CR_SUCCESS != ConfigRet){
|
|
DBG_WRN(("CWiaDevMan::EnumInterfaceDevices, On index %d, CM_Get_DevNode_Status returned error, assuming device is inactive", ulIndex));
|
|
}
|
|
} else {
|
|
bSkip = TRUE;
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::EnumInterfaceDevices, device on index %d, could not read DeviceID", ulIndex));
|
|
bSkip = TRUE;
|
|
}
|
|
}else {
|
|
DBG_WRN(("CWiaDevMan::EnumInterfaceDevices, device on index %d, not a StillImage", ulIndex));
|
|
bSkip = TRUE;
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::EnumInterfaceDevices, SetupDiOpenDeviceInterfaceRegKey on index %d return INVALID_HANDLE_VALUE", ulIndex));
|
|
bSkip = TRUE;
|
|
}
|
|
|
|
if (!bSkip) {
|
|
//
|
|
// If we get to here, it means we have a valid StillImage device, it's HKEY, spDevInterfaceData, and spDevInfoData
|
|
//
|
|
|
|
wszDeviceID[STI_MAX_INTERNAL_NAME_LENGTH-1] = L'\0';
|
|
|
|
//
|
|
// Check whether we already have the appropriate DEVICE_OBJECT in the list.
|
|
// If we do, find out whether we should generate a connect/disconnect event, else fill
|
|
// out the DeviceInformation struct and create a new DEVICE_OBJECT for it.
|
|
//
|
|
pActiveDevice = IsInList(DEV_MAN_IN_LIST_DEV_ID, wszDeviceID);
|
|
if (pActiveDevice) {
|
|
|
|
//
|
|
// Mark this device as active
|
|
//
|
|
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() & ~STIMON_AD_FLAG_MARKED_INACTIVE);
|
|
|
|
DWORD dwOldDevState;
|
|
dwOldDevState = pActiveDevice->m_DrvWrapper.getDeviceState();
|
|
|
|
//
|
|
// Mark the device state appropriately
|
|
//
|
|
dwDeviceState = MapCMStatusToDeviceState(dwOldDevState, ulStatus, ulProblemNumber);
|
|
|
|
DBG_TRC(("EnumInterfaceDevices, device '%ls' is in the list, "
|
|
"Old Device State = %lu, New Device State = %lu",
|
|
wszDeviceID, dwOldDevState, dwDeviceState));
|
|
|
|
//
|
|
// Update the device information. Certain fields are transient e.g.
|
|
// device state and port name
|
|
//
|
|
|
|
RefreshDevInfoFromHKey(pActiveDevice->m_DrvWrapper.getDevInfo(),
|
|
hDevRegKey,
|
|
dwDeviceState,
|
|
&spDevInfoData,
|
|
&spDevInterfaceData);
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
|
|
//
|
|
// Check whether its state changed. If it changed
|
|
// from inactive to active, throw connect event. If
|
|
// state changed from active to inactive, throw disconnect event.
|
|
//
|
|
if (((~dwOldDevState) & DEV_STATE_ACTIVE) &&
|
|
(dwDeviceState & DEV_STATE_ACTIVE)) {
|
|
|
|
//
|
|
// Load the driver
|
|
//
|
|
pActiveDevice->LoadDriver(TRUE);
|
|
GenerateSafeConnectEvent(pActiveDevice);
|
|
} else if ((dwOldDevState & DEV_STATE_ACTIVE) &&
|
|
((~dwDeviceState) & DEV_STATE_ACTIVE)) {
|
|
|
|
GenerateSafeDisconnectEvent(pActiveDevice);
|
|
pActiveDevice->UnLoadDriver(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE: In the case when we are not started yet, and the class installer
|
|
// starts us to install a device, certain timing conditions make it so that
|
|
// the device is enumerated on startup, but the device is not registred
|
|
// for PnP notifications like device removal. This is because it is not fully installed
|
|
// yet, so the lookup of the interface name to do a CreateFile on fails.
|
|
// So, to get around that, when the class installer is finished installing,
|
|
// and tells us to reenumerate, we also check whether the device is registered
|
|
// for PnP notifications. If it isn't, we attempt to register. If successful, we
|
|
// generate connect event (since we missed it the first time round)
|
|
//
|
|
if (!pActiveDevice->IsRegisteredForDeviceRemoval() && (dwDeviceState & DEV_STATE_ACTIVE)) {
|
|
|
|
//
|
|
// This device is active but is not yet registered. Let's attempt to register it
|
|
//
|
|
if (pActiveDevice->InitPnPNotifications(NULL)) {
|
|
|
|
//
|
|
// Successful. So now generate connect event if told to do so
|
|
//
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
GenerateSafeConnectEvent(pActiveDevice);
|
|
}
|
|
}
|
|
}
|
|
|
|
pActiveDevice->Release();
|
|
} else {
|
|
//
|
|
// Create and fill out a DEVICE_INFO structure. For
|
|
//
|
|
|
|
dwDeviceState = MapCMStatusToDeviceState(0, ulStatus, ulProblemNumber);
|
|
|
|
DBG_TRC(("EnumInterfaceDevices, device '%ls' is NOT in the list, "
|
|
"Device State = %lu", wszDeviceID, dwDeviceState));
|
|
|
|
pDevInfo = CreateDevInfoFromHKey(hDevRegKey, dwDeviceState, &spDevInfoData, &spDevInterfaceData);
|
|
DumpDevInfo(pDevInfo);
|
|
AddDevice(ulFlags, pDevInfo);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the device registry key
|
|
//
|
|
|
|
if (hDevRegKey != INVALID_HANDLE_VALUE) {
|
|
RegCloseKey(hDevRegKey);
|
|
hDevRegKey = NULL;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// Shortcut: for now, we're only going to enum mount points. Maybe, we might want to enumerate
|
|
// volumes, see which are removable media, cdroms etc., then match them up with corresponding
|
|
// mount points.
|
|
//
|
|
HRESULT CWiaDevMan::EnumVolumes(
|
|
ULONG ulFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHardwareDevices *pihwdevs = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
DEVICE_INFO *pDevInfo = NULL;
|
|
|
|
hr = CoCreateInstance(CLSID_HardwareDevices,
|
|
NULL,
|
|
CLSCTX_LOCAL_SERVER | CLSCTX_NO_FAILURE_LOG,
|
|
IID_IHardwareDevices,
|
|
(VOID**)&pihwdevs);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IHardwareDevicesMountPointsEnum *penum = NULL;
|
|
|
|
hr = pihwdevs->EnumMountPoints(&penum);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPWSTR pszMountPoint = NULL;
|
|
LPWSTR pszDeviceIDVolume = NULL;
|
|
|
|
while (penum->Next(&pszMountPoint, &pszDeviceIDVolume) == S_OK)
|
|
{
|
|
//
|
|
// Check if this is one of the volumes we "allow". We only
|
|
// allow:
|
|
// Removable drives with
|
|
// Non-securable file system
|
|
//
|
|
if (IsCorrectVolumeType(pszMountPoint)) {
|
|
//
|
|
// Check whether we already have the appropriate DEVICE_OBJECT in the list.
|
|
// If we do, find out whether we should generate a connect/disconnect event, else fill
|
|
// out the DeviceInformation struct and create a new DEVICE_OBJECT for it.
|
|
//
|
|
pActiveDevice = IsInList(DEV_MAN_IN_LIST_ALT_ID, pszMountPoint);
|
|
if (pActiveDevice) {
|
|
|
|
// TDB:
|
|
// We'd want to generate a connect/disconnect event for MSC camera
|
|
// Right now, there is no way to tell if it is MSC camera
|
|
// DWORD dwDevState = MapMediaStatusToDeviceState(dwMediaState);
|
|
|
|
//
|
|
// Mark this device as active
|
|
//
|
|
pActiveDevice->SetFlags(pActiveDevice->QueryFlags() & ~STIMON_AD_FLAG_MARKED_INACTIVE);
|
|
/*
|
|
|
|
DWORD dwOldDevState;
|
|
dwOldDevState = pActiveDevice->m_DrvWrapper.getDeviceState();
|
|
|
|
//
|
|
// Update the device information. Certain fields are transient e.g.
|
|
// device state and port name
|
|
//
|
|
|
|
|
|
RefreshDevInfoFromHKey(pActiveDevice->m_DrvWrapper.getDevInfo(),
|
|
hDevRegKey,
|
|
dwDeviceState,
|
|
&spDevInfoData);
|
|
if (ulFlags & DEV_MAN_GEN_EVENTS) {
|
|
|
|
//
|
|
// Check whether its state changed. If it changed
|
|
// from inactive to active, throw connect event. If
|
|
// state changed from active to inactive, throw disconnect event.
|
|
//
|
|
if (((~dwOldDevState) & DEV_STATE_ACTIVE) &&
|
|
(dwDeviceState & DEV_STATE_ACTIVE)) {
|
|
|
|
//
|
|
// Load the driver
|
|
//
|
|
//DumpDevInfo(pActiveDevice->m_DrvWrapper.getDevInfo());
|
|
//pActiveDevice->LoadDriver();
|
|
|
|
GenerateEventForDevice(&WIA_EVENT_DEVICE_CONNECTED, pActiveDevice);
|
|
} else if ((dwOldDevState & DEV_STATE_ACTIVE) &&
|
|
((~dwDeviceState) & DEV_STATE_ACTIVE)) {
|
|
GenerateSafeDisconnectEvent(pActiveDevice);
|
|
pActiveDevice->UnLoadDriver(FALSE);
|
|
}
|
|
}
|
|
*/
|
|
pActiveDevice->Release();
|
|
|
|
} else {
|
|
//
|
|
// Create and fill out a DEVICE_INFO structure. For
|
|
//
|
|
pDevInfo = CreateDevInfoForFSDriver(pszMountPoint);
|
|
DumpDevInfo(pDevInfo);
|
|
AddDevice(ulFlags, pDevInfo);
|
|
}
|
|
}
|
|
|
|
if (pszMountPoint) {
|
|
CoTaskMemFree(pszMountPoint);
|
|
pszMountPoint = NULL;
|
|
}
|
|
if (pszDeviceIDVolume) {
|
|
CoTaskMemFree(pszDeviceIDVolume);
|
|
pszDeviceIDVolume = NULL;
|
|
}
|
|
}
|
|
|
|
penum->Release();
|
|
}
|
|
|
|
pihwdevs->Release();
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::EnumVolumes, CoCreateInstance on CLSID_HardwareDevices failed"));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::FillRemoteDeviceStgs
|
|
*
|
|
* Enumerate remote devices and create a device info. storage for each
|
|
* remote device we come accross. We don't touch the network here - the
|
|
* remote devices are represented by the appropriate entries in the
|
|
* registry. Only if the calling application calls CreateDevice(..) to
|
|
* talk to the device, do we hit the remote machine.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ppRemoteDevList - Caller allocated array to store the dev. info.
|
|
* interface pointers.
|
|
* pulDevices - This is an IN/OUT parameter.
|
|
* On entry, this is the maximum number of dev. info.
|
|
* stgs to add to the ppRemoteDevList array.
|
|
* On return, this contains the actual number of dev.
|
|
* info. stgs added to the array.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 2/05/2001 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CWiaDevMan::FillRemoteDeviceStgs(
|
|
IWiaPropertyStorage **ppRemoteDevList,
|
|
ULONG *pulDevices)
|
|
{
|
|
DBG_FN(::FillRemoteDeviceStgs);
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Check parameters
|
|
//
|
|
if (!ppRemoteDevList || !pulDevices) {
|
|
DBG_WRN(("CWiaDevMan::FillRemoteDeviceStgs, NULL parameters are not allowed!"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ULONG ulMaxDevicesToAdd = *pulDevices;
|
|
ULONG ulNumDevices = 0;
|
|
|
|
//
|
|
// Enumerate remote devices and create a dev. info. storage for each one we find.
|
|
// We must not add more devices than ppRemoteDevList can hold, and we must set the
|
|
// return value to indicate how many dev. info storages we did actually add.
|
|
//
|
|
|
|
//
|
|
// find remote device entry in registry
|
|
//
|
|
LPWSTR szKeyName = REGSTR_PATH_STICONTROL_DEVLIST_W;
|
|
|
|
HKEY hKeySetup,hKeyDevice;
|
|
LONG lResult;
|
|
DWORD dwMachineIndex = 0;
|
|
|
|
if (RegOpenKeyExW (HKEY_LOCAL_MACHINE,
|
|
szKeyName,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKeySetup) == ERROR_SUCCESS) {
|
|
|
|
|
|
//
|
|
// look for machine names
|
|
//
|
|
WCHAR wszTemp[MAX_PATH+1];
|
|
WCHAR *pwszTempVal = NULL;
|
|
|
|
//
|
|
// go through enumeration, open key
|
|
//
|
|
dwMachineIndex = 0;
|
|
|
|
do {
|
|
|
|
hr = S_OK;
|
|
lResult = RegEnumKeyW(hKeySetup,dwMachineIndex,wszTemp,MAX_PATH+1);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Increment the index so we can get the next key on next
|
|
// iteration
|
|
//
|
|
dwMachineIndex++;
|
|
|
|
//
|
|
// Paranoid overflow check. If we don't have enough space for
|
|
// this, then break out of the loop.
|
|
//
|
|
if (ulNumDevices >= ulMaxDevicesToAdd) {
|
|
break;
|
|
}
|
|
|
|
lResult = RegOpenKeyExW (hKeySetup,
|
|
wszTemp,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKeyDevice);
|
|
|
|
if (lResult == ERROR_SUCCESS) {
|
|
|
|
DEVICE_INFO *pDeviceInfo = NULL;
|
|
|
|
//
|
|
// We need to create a Dev. Info. for this remote device. The
|
|
// property storage is created from the DEVICE_INFO struct we
|
|
// create from the remote device registry entry.
|
|
//
|
|
pDeviceInfo = CreateDevInfoForRemoteDevice(hKeyDevice);
|
|
if (pDeviceInfo) {
|
|
|
|
ppRemoteDevList[ulNumDevices] = CreateDevInfoStg(pDeviceInfo);
|
|
if (ppRemoteDevList[ulNumDevices]) {
|
|
|
|
//
|
|
// We successfully created a dev. info. for this remote device,
|
|
// so increment the returned dev. info. count.
|
|
//
|
|
ulNumDevices++;
|
|
}
|
|
|
|
//
|
|
// Cleanup the DEVICE_INFO struct since it's no longer needed
|
|
//
|
|
delete pDeviceInfo;
|
|
pDeviceInfo = NULL;
|
|
}
|
|
|
|
RegCloseKey(hKeyDevice);
|
|
hKeyDevice = NULL;
|
|
} else {
|
|
DBG_ERR(("CWiaDevMan::FillRemoteDeviceStgs, failed RegOpenKeyExW, status = %lx",lResult));
|
|
}
|
|
}
|
|
} while (lResult == ERROR_SUCCESS);
|
|
|
|
RegCloseKey(hKeySetup);
|
|
}
|
|
|
|
*pulDevices = ulNumDevices;
|
|
return hr;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::CountRemoteDevices
|
|
*
|
|
* This method counts the number of remote devices. The remote devices
|
|
* are represented by registry entries in the DevList section under
|
|
* the StillImage key.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ulFlags - Currently unused
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Number of remote devices.
|
|
*
|
|
* History:
|
|
*
|
|
* 2/05/2001 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
ULONG CWiaDevMan::CountRemoteDevices(
|
|
ULONG ulFlags)
|
|
{
|
|
DBG_FN(::CountRemoteDevices);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// find remote device entry in registry
|
|
//
|
|
|
|
LPWSTR szKeyName = REGSTR_PATH_STICONTROL_DEVLIST_W;
|
|
|
|
HKEY hKeyDeviceList;
|
|
LONG lResult;
|
|
DWORD dwNumDevices = 0;
|
|
|
|
if (RegOpenKeyExW (HKEY_LOCAL_MACHINE,
|
|
szKeyName,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hKeyDeviceList) == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Get the number of sub-keys. Since each remote device is stored
|
|
// under a separate key, this will give us the total number of
|
|
// remote devices.
|
|
//
|
|
lResult = RegQueryInfoKey(hKeyDeviceList,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&dwNumDevices,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
RegCloseKey(hKeyDeviceList);
|
|
}
|
|
|
|
return dwNumDevices;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::IsCorrectEnumType
|
|
*
|
|
* This function checks whether a given device (represented by pInfo)
|
|
* matches the category of devices specified in the enumeration flags
|
|
* (specified by ulEnumType)
|
|
*
|
|
* This function works on the principle that if the device is of type X, and
|
|
* you didn't ask for X, then it returns FALSE. Else, it returns TRUE.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* ulEnumType - Enumeration flags (see DEV_MAN_ENUM_TYPE_XXXX in header)
|
|
* pInfo - Pointer to DEVICE_INFO
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE - This device falls into the category of devices
|
|
* FALSE - This device does not fall into the category of devices we want
|
|
*
|
|
* History:
|
|
*
|
|
* 11/06/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
BOOL CWiaDevMan::IsCorrectEnumType(
|
|
ULONG ulEnumType,
|
|
DEVICE_INFO *pInfo)
|
|
{
|
|
|
|
if (!pInfo) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Shortcut - if ulEnumType == ALL_DEVICES return TRUE?
|
|
|
|
if (!(pInfo->dwDeviceState & DEV_STATE_ACTIVE) &&
|
|
!(ulEnumType & DEV_MAN_ENUM_TYPE_INACTIVE)) {
|
|
//
|
|
// This device is inactive and caller only wanted active
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(pInfo->dwInternalType & INTERNAL_DEV_TYPE_WIA) &&
|
|
!(ulEnumType & DEV_MAN_ENUM_TYPE_STI)) {
|
|
//
|
|
// This is an STI only device, and caller asked for WIA
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(ulEnumType & DEV_MAN_ENUM_TYPE_VOL) &&
|
|
(pInfo->dwInternalType & INTERNAL_DEV_TYPE_VOL)) {
|
|
|
|
//
|
|
// This is volume device and caller didn't ask to include volume. We
|
|
// first check whether the bMakeVolumesVisible override is set to TRUE,
|
|
// else we don't want it to show up, so we return FALSE.
|
|
//
|
|
|
|
if (!m_bMakeVolumesVisible) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ((ulEnumType & DEV_MAN_ENUM_TYPE_LOCAL_ONLY) &&
|
|
!(pInfo->dwInternalType & INTERNAL_DEV_TYPE_LOCAL)) {
|
|
|
|
//
|
|
// This is remote and asked not to include remote
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef WIA_DISABLE_VIDEO_SUPPORT
|
|
|
|
if (GET_STIDEVICE_TYPE(pInfo->DeviceType) == StiDeviceTypeStreamingVideo) {
|
|
|
|
//
|
|
// If this SKU of the OS disables WIA Video support, do not
|
|
// enumerate video devices.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::GenerateSafeConnectEvent
|
|
*
|
|
* This function generates a connect event for a device, IFF it has not
|
|
* been generated already.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pActiveDevice - Indicates which device we want to generate the event
|
|
* for.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 01/29/2001 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CWiaDevMan::GenerateSafeConnectEvent(
|
|
ACTIVE_DEVICE *pActiveDevice)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pActiveDevice) {
|
|
|
|
//
|
|
// Check whether we have already thrown a connect event for the
|
|
// device. We don't want to throw it twice, so only throw it if
|
|
// the connect event state shows it hasn't been done yet.
|
|
//
|
|
if (!pActiveDevice->m_DrvWrapper.wasConnectEventThrown()) {
|
|
DBG_PRT(("CWiaDevMan::GenerateSafeConnectEvent, generating event for device (%ws)", pActiveDevice->GetDeviceID()));
|
|
|
|
//
|
|
// Generate the connect event
|
|
//
|
|
hr = GenerateEventForDevice(&WIA_EVENT_DEVICE_CONNECTED, pActiveDevice);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// Mark that the event was generated
|
|
//
|
|
pActiveDevice->m_DrvWrapper.setConnectEventState(TRUE);
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::GenerateSafeConnectEvent, could not generate connect event for device (%ws)",
|
|
pActiveDevice->GetDeviceID()));
|
|
}
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::GenerateSafeConnectEvent, called with NULL parameter, ignoring request..."));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CWiaDevMan::GenerateSafeDisconnectEvent
|
|
*
|
|
* This function generates a disconnect event for a device, and clears the
|
|
* connect event flag set by GenerateSafeConnectEvent(...).
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pActiveDevice - Indicates which device we want to generate the event
|
|
* for.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 01/29/2001 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CWiaDevMan::GenerateSafeDisconnectEvent(
|
|
ACTIVE_DEVICE *pActiveDevice)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pActiveDevice) {
|
|
|
|
|
|
//
|
|
// Check the connect event flag for the device. We only want to
|
|
// throw the disconnect event if this bit is set, so it
|
|
// prevents us from throwing it twice.
|
|
//
|
|
if (pActiveDevice->m_DrvWrapper.wasConnectEventThrown()) {
|
|
DBG_PRT(("CWiaDevMan::GenerateSafeDisconnectEvent, generating event for device (%ws)", pActiveDevice->GetDeviceID()));
|
|
|
|
//
|
|
// Generate the disconnect event
|
|
//
|
|
hr = GenerateEventForDevice(&WIA_EVENT_DEVICE_DISCONNECTED, pActiveDevice);
|
|
|
|
//
|
|
// Whether we succeeded or not, clear the Connect Event State
|
|
//
|
|
pActiveDevice->m_DrvWrapper.setConnectEventState(FALSE);
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::GenerateSafeDisconnectEvent, called with NULL parameter, ignoring request..."));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HKEY CWiaDevMan::GetHKeyFromMountPoint(WCHAR *wszMountPoint)
|
|
{
|
|
HKEY hDevRegKey = NULL;
|
|
DWORD dwError = 0;
|
|
DWORD dwDisposition = 0;
|
|
|
|
WCHAR wszKeyPath[MAX_PATH * 2];
|
|
|
|
if (!wszMountPoint) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Create the sub-key name. It will be something like:
|
|
// System\CurrentControlSet\Control\StillImage\MSCDeviceList\F:
|
|
//
|
|
lstrcpynW(wszKeyPath, REGSTR_PATH_WIA_MSCDEVICES_W, sizeof(wszKeyPath) / sizeof(wszKeyPath[0]));
|
|
lstrcpynW(wszKeyPath + lstrlenW(wszKeyPath), L"\\", sizeof(wszKeyPath) / sizeof(wszKeyPath[0]) - lstrlenW(wszKeyPath));
|
|
if (lstrlenW(wszMountPoint) < (int)((sizeof(wszKeyPath) / sizeof(wszKeyPath[0]) - lstrlenW(wszKeyPath)))) {
|
|
lstrcatW(wszKeyPath, wszMountPoint);
|
|
|
|
//
|
|
// Strip off the \ at the end of the mount point
|
|
//
|
|
wszKeyPath[lstrlenW(wszKeyPath) - 1] = L'\0';
|
|
} else {
|
|
dwError = ERROR_BAD_ARGUMENTS;
|
|
DBG_WRN(("CWiaDevMan::GetHKeyFromMountPoint, bad parameters, returning NULL for HKEY"));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Since this is a MSC device, we don't have normal device registry key.
|
|
// So, we create a "fake" set of entries in a known place, and use those
|
|
// to store the relevant info for MSC devices. This is used mainly to
|
|
// store the user's event settings.
|
|
//
|
|
|
|
dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
wszKeyPath,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hDevRegKey,
|
|
&dwDisposition);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
|
|
if (dwDisposition == REG_CREATED_NEW_KEY) {
|
|
|
|
//
|
|
// This is a newly created key, so we have to fill in the
|
|
// relevant entries.
|
|
//
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = CreateMSCRegEntries(hDevRegKey, wszMountPoint);
|
|
}
|
|
} else {
|
|
DBG_WRN(("CWiaDevMan::GetHKeyFromMountPoint, RegCreateKeyExW on (%ws) failed!", wszKeyPath));
|
|
}
|
|
|
|
return hDevRegKey;
|
|
}
|
|
|
|
|
|
HKEY CWiaDevMan::GetHKeyFromDevInfoData(SP_DEVINFO_DATA *pspDevInfoData)
|
|
{
|
|
HKEY hDevRegKey = NULL;
|
|
|
|
//
|
|
// Get device regkey.
|
|
//
|
|
|
|
if (pspDevInfoData) {
|
|
hDevRegKey = SetupDiOpenDevRegKey(m_DeviceInfoSet,
|
|
pspDevInfoData,
|
|
DICS_FLAG_GLOBAL,
|
|
0,
|
|
DIREG_DRV,
|
|
KEY_READ | KEY_WRITE);
|
|
if(hDevRegKey == INVALID_HANDLE_VALUE){
|
|
hDevRegKey = SetupDiOpenDevRegKey(m_DeviceInfoSet,
|
|
pspDevInfoData,
|
|
DICS_FLAG_GLOBAL,
|
|
0,
|
|
DIREG_DRV,
|
|
KEY_READ);
|
|
if(hDevRegKey == INVALID_HANDLE_VALUE){
|
|
DBG_WRN(("CWiaDevMan::GetHKeyFromDevInfoData, SetupDiOpenDevRegKey returned INVALID_HANDLE_VALUE"));
|
|
hDevRegKey = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hDevRegKey;
|
|
}
|
|
|
|
HKEY CWiaDevMan::GetHKeyFromDevInterfaceData(SP_DEVICE_INTERFACE_DATA *pspDevInterfaceData)
|
|
{
|
|
HKEY hDevRegKey = NULL;
|
|
|
|
//
|
|
// Get device regkey using interface data
|
|
//
|
|
|
|
if (pspDevInterfaceData) {
|
|
hDevRegKey = SetupDiOpenDeviceInterfaceRegKey(m_DeviceInfoSet,
|
|
pspDevInterfaceData,
|
|
0,
|
|
KEY_READ | KEY_WRITE);
|
|
if(hDevRegKey == INVALID_HANDLE_VALUE){
|
|
hDevRegKey = SetupDiOpenDeviceInterfaceRegKey(m_DeviceInfoSet,
|
|
pspDevInterfaceData,
|
|
0,
|
|
KEY_READ);
|
|
if(hDevRegKey == INVALID_HANDLE_VALUE){
|
|
DBG_WRN(("CWiaDevMan::GetHKeyFromDevInterfaceData, SetupDiOpenDevRegKey returned INVALID_HANDLE_VALUE"));
|
|
hDevRegKey = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hDevRegKey;
|
|
}
|
|
|
|
HKEY CWiaDevMan::GetDeviceHKey(
|
|
WCHAR *wszDeviceID,
|
|
WCHAR *wszSubKeyName)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
HKEY hKey = NULL;
|
|
|
|
pActiveDevice = IsInList(DEV_MAN_IN_LIST_DEV_ID, wszDeviceID);
|
|
if (pActiveDevice) {
|
|
|
|
hKey = GetDeviceHKey(pActiveDevice, wszSubKeyName);
|
|
|
|
//
|
|
// Release the active device since it was addref'd by IsInList
|
|
//
|
|
pActiveDevice->Release();
|
|
}
|
|
|
|
if (!IsValidHANDLE(hKey)) {
|
|
DBG_TRC(("CWiaDevMan::GetDeviceHKey (name), Key not found for (%ws), returning NULL", wszDeviceID));
|
|
}
|
|
return hKey;
|
|
}
|
|
|
|
HKEY CWiaDevMan::GetDeviceHKey(
|
|
ACTIVE_DEVICE *pActiveDevice,
|
|
WCHAR *wszSubKeyName)
|
|
{
|
|
DEVICE_INFO *pDevInfo = NULL;
|
|
HKEY hKeyTemp = NULL;
|
|
HKEY hKey = NULL;
|
|
DWORD dwRet = 0;
|
|
|
|
if (pActiveDevice) {
|
|
|
|
//
|
|
// Get the device's HKEY
|
|
//
|
|
pDevInfo = pActiveDevice->m_DrvWrapper.getDevInfo();
|
|
if (pDevInfo) {
|
|
|
|
//
|
|
// If it's a volume device i.e. normal MSC like a card reader,
|
|
// then we don't have a Device HKEY, so skip this.
|
|
//
|
|
|
|
if (!(pDevInfo->dwInternalType & INTERNAL_DEV_TYPE_VOL)) {
|
|
//
|
|
// We have 3 cases: 1, it's a MSC camera
|
|
// 2, it's a normal DevNode device
|
|
// 3, it's an interface device
|
|
//
|
|
|
|
if (pDevInfo->dwInternalType & INTERNAL_DEV_TYPE_MSC_CAMERA) {
|
|
hKeyTemp = GetHKeyFromMountPoint(pDevInfo->wszAlternateID);
|
|
} else if (pDevInfo->dwInternalType & INTERNAL_DEV_TYPE_INTERFACE) {
|
|
hKeyTemp = GetHKeyFromDevInterfaceData(&pDevInfo->spDevInterfaceData);
|
|
} else {
|
|
hKeyTemp = GetHKeyFromDevInfoData(&pDevInfo->spDevInfoData);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the return. Note that hKey may be over written with the subkey later on
|
|
//
|
|
hKey = hKeyTemp;
|
|
}
|
|
|
|
//
|
|
// If asked, get the subkey instead
|
|
//
|
|
if (wszSubKeyName) {
|
|
|
|
//
|
|
// Check that we have a valid device registry key first
|
|
//
|
|
if (IsValidHANDLE(hKeyTemp)) {
|
|
dwRet = RegCreateKeyExW(hKeyTemp,
|
|
wszSubKeyName,
|
|
NULL,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
dwRet = RegCreateKeyExW(hKeyTemp,
|
|
wszSubKeyName,
|
|
NULL,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
hKey = NULL;
|
|
}
|
|
}
|
|
//
|
|
// Close the device registry key, we will be returning the sub-key instead
|
|
//
|
|
RegCloseKey(hKeyTemp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!IsValidHANDLE(hKey)) {
|
|
DBG_TRC(("CWiaDevMan::GetDeviceHKey, Key not found for (%ws), returning NULL", pActiveDevice->GetDeviceID()));
|
|
}
|
|
return hKey;
|
|
}
|
|
|
|
HRESULT CWiaDevMan::UpdateDeviceRegistry(
|
|
DEVICE_INFO *pDevInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HKEY hDevRegKey = NULL;
|
|
HKEY hKeyDevData = NULL;
|
|
|
|
if (!pDevInfo) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Grab the device's HKey
|
|
//
|
|
|
|
hDevRegKey = GetDeviceHKey(pDevInfo->wszDeviceInternalName, NULL);
|
|
if (IsValidHANDLE(hDevRegKey)) {
|
|
//
|
|
// Write any properties that may have changed. So far, we only allow updating of:
|
|
// Friendly name
|
|
// Port name
|
|
// BaudRate
|
|
//
|
|
|
|
DWORD dwRet = 0;
|
|
DWORD dwType = REG_SZ;
|
|
DWORD dwSize = 0;
|
|
|
|
//
|
|
// These properties are written to the device registry key
|
|
//
|
|
|
|
if (pDevInfo->wszLocalName) {
|
|
dwType = REG_SZ;
|
|
dwSize = (lstrlenW(pDevInfo->wszLocalName) + 1) * sizeof(WCHAR);
|
|
dwRet = RegSetValueExW(hDevRegKey,
|
|
REGSTR_VAL_FRIENDLY_NAME_W,
|
|
0,
|
|
dwType,
|
|
(LPBYTE) pDevInfo->wszLocalName,
|
|
dwSize);
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
DBG_WRN(("CWiaDevMan::UpdateDeviceRegistry, error updating %ws for device %ws", REGSTR_VAL_FRIENDLY_NAME_W, pDevInfo->wszDeviceInternalName));
|
|
}
|
|
}
|
|
|
|
if (pDevInfo->wszPortName) {
|
|
dwType = REG_SZ;
|
|
dwSize = (lstrlenW(pDevInfo->wszPortName) + 1) * sizeof(WCHAR);
|
|
dwRet = RegSetValueExW(hDevRegKey,
|
|
REGSTR_VAL_DEVICEPORT_W,
|
|
0,
|
|
dwType,
|
|
(LPBYTE) pDevInfo->wszPortName,
|
|
dwSize);
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
DBG_WRN(("CWiaDevMan::UpdateDeviceRegistry, error updating %ws for device %ws", REGSTR_VAL_DEVICEPORT_W, pDevInfo->wszDeviceInternalName));
|
|
}
|
|
}
|
|
|
|
//
|
|
// These properties are written to the device data registry key. Since we
|
|
// only have the device registry key open, we have to open the device data
|
|
// data key for these properties
|
|
//
|
|
|
|
dwRet = RegCreateKeyExW(hDevRegKey, REGSTR_VAL_DATA_W, NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
|
|
NULL, &hKeyDevData, NULL);
|
|
if (IsValidHANDLE(hKeyDevData)) {
|
|
if (pDevInfo->wszBaudRate) {
|
|
dwType = REG_SZ;
|
|
dwSize = (lstrlenW(pDevInfo->wszBaudRate) + 1) * sizeof(WCHAR);
|
|
dwRet = RegSetValueExW(hKeyDevData,
|
|
REGSTR_VAL_BAUDRATE,
|
|
0,
|
|
dwType,
|
|
(LPBYTE) pDevInfo->wszBaudRate,
|
|
dwSize);
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
DBG_WRN(("CWiaDevMan::UpdateDeviceRegistry, error updating %ws for device %ws", REGSTR_VAL_DEVICEPORT_W, pDevInfo->wszDeviceInternalName));
|
|
}
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::UpdateDeviceRegistry, could not find device data section in registry for %ws", pDevInfo->wszDeviceInternalName));
|
|
hr = E_INVALIDARG;
|
|
}
|
|
} else {
|
|
DBG_TRC(("CWiaDevMan::UpdateDeviceRegistry, could not find device registry key for %ws", pDevInfo->wszDeviceInternalName));
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
if (IsValidHANDLE(hDevRegKey)) {
|
|
RegCloseKey(hDevRegKey);
|
|
}
|
|
if (IsValidHANDLE(hKeyDevData)) {
|
|
RegCloseKey(hKeyDevData);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
VOID CWiaDevMan::UnloadAllDrivers(
|
|
BOOL bForceUnload,
|
|
BOOL bGenEvents)
|
|
{
|
|
TAKE_CRIT_SECT tcs(m_csDevList);
|
|
|
|
//
|
|
// Walk list and unload all drivers.
|
|
//
|
|
|
|
//
|
|
// Walk through list of devices
|
|
//
|
|
LIST_ENTRY *pentry = NULL;
|
|
LIST_ENTRY *pentryNext = NULL;
|
|
ACTIVE_DEVICE *pActiveDevice = NULL;
|
|
|
|
{
|
|
//
|
|
// For each device in list, call the UnLoadDriver(...) method
|
|
//
|
|
for ( pentry = m_leDeviceListHead.Flink; pentry != &m_leDeviceListHead; pentry = pentryNext ) {
|
|
|
|
pentryNext = pentry->Flink;
|
|
pActiveDevice = CONTAINING_RECORD( pentry, ACTIVE_DEVICE,m_ListEntry );
|
|
|
|
//
|
|
// If asked, make sure we send out disconnect event, only for WIA devices
|
|
//
|
|
if (bGenEvents && pActiveDevice->m_DrvWrapper.IsWiaDevice()) {
|
|
|
|
//
|
|
// Only throw disconnect events for devices that are active
|
|
//
|
|
if (pActiveDevice->m_DrvWrapper.getDeviceState() & DEV_STATE_ACTIVE) {
|
|
GenerateSafeDisconnectEvent(pActiveDevice);
|
|
}
|
|
}
|
|
ProcessDeviceRemoval(pActiveDevice, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CALLBACK CWiaDevMan::ShellHWEventAPCProc(ULONG_PTR ulpParam)
|
|
{
|
|
SHHARDWAREEVENT *pShellHWEvent = (SHHARDWAREEVENT*)ulpParam;
|
|
CWiaDevMan *pDevMan = g_pDevMan;
|
|
|
|
if (!pShellHWEvent || !pDevMan) {
|
|
return;
|
|
}
|
|
switch (pShellHWEvent->dwEvent)
|
|
{
|
|
case SHHARDWAREEVENT_MOUNTPOINTARRIVED:
|
|
{
|
|
DBG_PRT(("MOUNTPOINTARRIVED"));
|
|
TAKE_CRIT_SECT tcs(pDevMan->m_csDevList);
|
|
|
|
//
|
|
// ReEnumerate volumes
|
|
//
|
|
pDevMan->EnumVolumes(DEV_MAN_GEN_EVENTS);
|
|
|
|
break;
|
|
}
|
|
|
|
case SHHARDWAREEVENT_MOUNTPOINTREMOVED:
|
|
{
|
|
DBG_PRT(("MOUNTPOINTREMOVED"));
|
|
LPCWSTR pszMountPoint = (LPCWSTR)(&(pShellHWEvent->rgbPayLoad)); // Do we need to worry about alignment?
|
|
|
|
TAKE_CRIT_SECT tcs(pDevMan->m_csDevList);
|
|
//
|
|
// If volume devices are enabled, then remove this mount point
|
|
//
|
|
if (pDevMan->VolumesAreEnabled()) {
|
|
ACTIVE_DEVICE *pActiveDevice;
|
|
|
|
pActiveDevice = pDevMan->IsInList(DEV_MAN_IN_LIST_ALT_ID, pszMountPoint);
|
|
if (pActiveDevice) {
|
|
|
|
pDevMan->RemoveDevice(pActiveDevice);
|
|
pActiveDevice->Release();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SHHARDWAREEVENT_VOLUMEARRIVED:
|
|
case SHHARDWAREEVENT_VOLUMEUPDATED:
|
|
case SHHARDWAREEVENT_VOLUMEREMOVED:
|
|
case SHHARDWAREEVENT_MOUNTDEVICEARRIVED:
|
|
case SHHARDWAREEVENT_MOUNTDEVICEUPDATED:
|
|
case SHHARDWAREEVENT_MOUNTDEVICEREMOVED:
|
|
|
|
default:
|
|
{
|
|
DBG_PRT(("DEFAULT_EVENT"));
|
|
TAKE_CRIT_SECT tcs(pDevMan->m_csDevList);
|
|
|
|
//
|
|
// ReEnumerate volumes
|
|
//
|
|
pDevMan->EnumVolumes(DEV_MAN_GEN_EVENTS);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Notice that it's a VirtualFree!
|
|
//
|
|
VirtualFree((void*)ulpParam, 0, MEM_RELEASE);
|
|
}
|
|
|