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.
4938 lines
135 KiB
4938 lines
135 KiB
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORP., 1997
|
|
*
|
|
* TITLE: DevMgr.Cpp
|
|
*
|
|
* VERSION: 2.0
|
|
*
|
|
* DATE: 26 Dec, 1997
|
|
*
|
|
* DESCRIPTION:
|
|
* Class implementation for WIA device manager.
|
|
*
|
|
*******************************************************************************/
|
|
#include "precomp.h"
|
|
#include "stiexe.h"
|
|
|
|
#include "wiamindr.h"
|
|
#include "wiamdef.h"
|
|
|
|
#include "wiacfact.h"
|
|
#include "devmgr.h"
|
|
#include "devinfo.h"
|
|
#include "tchar.h"
|
|
#include "helpers.h"
|
|
#include "ienumdc.h"
|
|
#include "shlwapi.h"
|
|
#include "device.h"
|
|
#include "wiapriv.h"
|
|
#include "lockmgr.h"
|
|
#ifdef UNICODE
|
|
#include "userenv.h"
|
|
#endif
|
|
#define INITGUID
|
|
#include "initguid.h"
|
|
#include "wiaevntp.h"
|
|
|
|
//
|
|
// Critical section protecting event node list defined in wiamain.cpp
|
|
//
|
|
|
|
extern CRITICAL_SECTION g_semEventNode;
|
|
|
|
|
|
//
|
|
// Since there is only Event Notifier needed, it is staticly allocated
|
|
//
|
|
|
|
CEventNotifier g_eventNotifier;
|
|
|
|
|
|
//
|
|
// Private look up function defined in STIDEV.CPP
|
|
//
|
|
|
|
HRESULT
|
|
WiaGetDeviceInfo(
|
|
LPCWSTR pwszDeviceName,
|
|
DWORD *pdwDeviceType,
|
|
BSTR *pbstrDeviceDescription,
|
|
ACTIVE_DEVICE **ppDevice);
|
|
|
|
//
|
|
// Helper function to look for action events
|
|
//
|
|
|
|
BOOL ActionGuidExists(
|
|
BSTR bstrDevId,
|
|
const GUID *pEventGUID);
|
|
|
|
//
|
|
// Special handler's class ID {D13E3F25-1688-45A0-9743-759EB35CDF9A}
|
|
//
|
|
|
|
DEFINE_GUID(
|
|
CLSID_DefHandler,
|
|
0xD13E3F25, 0x1688, 0x45A0,
|
|
0x97, 0x43, 0x75, 0x9E, 0xB3, 0x5C, 0xDF, 0x9A);
|
|
|
|
|
|
#ifdef UNICODE
|
|
void
|
|
PrepareCommandline(
|
|
BSTR bstrDeviceID,
|
|
const GUID &guidEvent,
|
|
LPCWSTR pwszOrigCmdline,
|
|
LPWSTR pwszCommandline);
|
|
#else
|
|
void
|
|
PrepareCommandline(
|
|
BSTR bstrDeviceID,
|
|
const GUID &guidEvent,
|
|
LPCSTR pwszOrigCmdline,
|
|
LPSTR pwszCommandline);
|
|
#endif
|
|
|
|
|
|
/**************************************************************************\
|
|
* EventThreadProc
|
|
*
|
|
* Thread is created to send events back to client. !!! may want to
|
|
* create a permanent thread to do this instead of creating a new one
|
|
* each time
|
|
*
|
|
* Arguments:
|
|
*
|
|
* lpParameter - pointer to PWIAEventThreadInfo data
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/19/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD WINAPI
|
|
EventThreadProc(
|
|
LPVOID lpParameter)
|
|
{
|
|
DBG_FN(::EventThreadProc);
|
|
HRESULT hr;
|
|
|
|
PWIAEventThreadInfo pInfo = (PWIAEventThreadInfo)lpParameter;
|
|
|
|
DBG_TRC(("Thread callback 0x%lx", pInfo->pIEventCB));
|
|
|
|
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
DBG_ERR(("Thread callback, ImageEventCallback failed (0x%X)", hr));
|
|
}
|
|
|
|
hr = pInfo->pIEventCB->ImageEventCallback(
|
|
&pInfo->eventGUID,
|
|
pInfo->bstrEventDescription,
|
|
pInfo->bstrDeviceID,
|
|
pInfo->bstrDeviceDescription,
|
|
pInfo->dwDeviceType,
|
|
pInfo->bstrFullItemName,
|
|
&pInfo->ulEventType,
|
|
pInfo->ulReserved);
|
|
|
|
pInfo->pIEventCB->Release();
|
|
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("Thread callback, ImageEventCallback failed (0x%X)", hr));
|
|
}
|
|
|
|
if (pInfo->bstrDeviceID) {
|
|
::SysFreeString(pInfo->bstrDeviceID);
|
|
}
|
|
|
|
if (pInfo->bstrEventDescription) {
|
|
::SysFreeString(pInfo->bstrEventDescription);
|
|
}
|
|
|
|
if (pInfo->bstrDeviceDescription) {
|
|
::SysFreeString(pInfo->bstrDeviceDescription);
|
|
}
|
|
|
|
if (pInfo->bstrFullItemName) {
|
|
::SysFreeString(pInfo->bstrFullItemName);
|
|
}
|
|
|
|
LocalFree(pInfo);
|
|
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* CEventNotifier
|
|
* ~CEventNotifier
|
|
*
|
|
* Class constructor/destructors
|
|
*
|
|
* History:
|
|
*
|
|
* 9/2/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
CEventNotifier::CEventNotifier()
|
|
{
|
|
m_ulRef = 0;
|
|
m_pEventDestNodes = NULL;
|
|
}
|
|
|
|
CEventNotifier::~CEventNotifier()
|
|
{
|
|
// Clean up
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::UnlinkNode
|
|
*
|
|
* remove node from double linkeed list
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pCurNode - node to remove
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 5/20/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
CEventNotifier::UnlinkNode(
|
|
PEventDestNode pCurNode)
|
|
{
|
|
DBG_FN(CEventNotifier::UnlinkNode);
|
|
//
|
|
// Unlink the current node
|
|
//
|
|
|
|
if (pCurNode->pPrev) {
|
|
pCurNode->pPrev->pNext = pCurNode->pNext;
|
|
} else {
|
|
|
|
//
|
|
// The head of the list is deleted
|
|
//
|
|
|
|
m_pEventDestNodes = pCurNode->pNext;
|
|
}
|
|
|
|
if (pCurNode->pNext) {
|
|
pCurNode->pNext->pPrev = pCurNode->pPrev;
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* CEventNotifier::LinkNode
|
|
*
|
|
* add the node to the double linked list of nodes
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pCurNode - node to add to list
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 5/20/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
CEventNotifier::LinkNode(
|
|
PEventDestNode pCurNode)
|
|
{
|
|
DBG_FN(CEventNotifier::LinkNode);
|
|
//
|
|
// Put the new node at the head of the list
|
|
//
|
|
|
|
if (m_pEventDestNodes) {
|
|
m_pEventDestNodes->pPrev = pCurNode;
|
|
}
|
|
|
|
pCurNode->pNext = m_pEventDestNodes;
|
|
pCurNode->pPrev = NULL;
|
|
m_pEventDestNodes = pCurNode;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* CEventNotifier::FireEventAsync
|
|
*
|
|
* Fires a Async event
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pMasterInfo - Thread information
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 8/9/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::FireEventAsync(
|
|
PWIAEventThreadInfo pMasterInfo)
|
|
{
|
|
DBG_FN(CEventNotifier::FireEventAsync);
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
PWIAEventThreadInfo pInfo = NULL;
|
|
DWORD dwThreadID;
|
|
HANDLE hThread;
|
|
|
|
do {
|
|
|
|
pInfo = (PWIAEventThreadInfo)
|
|
LocalAlloc(LPTR, sizeof(WIAEventThreadInfo));
|
|
if (! pInfo) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy information from the master thread info block
|
|
//
|
|
|
|
pInfo->eventGUID = pMasterInfo->eventGUID;
|
|
|
|
pInfo->bstrDeviceID =
|
|
SysAllocString(pMasterInfo->bstrDeviceID);
|
|
if (! pInfo->bstrDeviceID) {
|
|
break;
|
|
}
|
|
|
|
pInfo->bstrEventDescription =
|
|
SysAllocString(pMasterInfo->bstrEventDescription);
|
|
if (! pInfo->bstrEventDescription) {
|
|
break;
|
|
}
|
|
|
|
if (pMasterInfo->bstrDeviceDescription) {
|
|
|
|
pInfo->bstrDeviceDescription =
|
|
SysAllocString(pMasterInfo->bstrDeviceDescription);
|
|
if (! pInfo->bstrDeviceDescription) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pMasterInfo->bstrFullItemName) {
|
|
|
|
pInfo->bstrFullItemName =
|
|
SysAllocString(pMasterInfo->bstrFullItemName);
|
|
if (! pInfo->bstrFullItemName) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
pInfo->dwDeviceType = pMasterInfo->dwDeviceType;
|
|
pInfo->ulEventType = pMasterInfo->ulEventType;
|
|
pInfo->ulReserved = pMasterInfo->ulReserved;
|
|
|
|
pMasterInfo->pIEventCB->AddRef();
|
|
hr = S_OK;
|
|
|
|
pInfo->pIEventCB = pMasterInfo->pIEventCB;
|
|
|
|
//
|
|
// Fire the event callback
|
|
//
|
|
|
|
hThread = CreateThread(
|
|
NULL, 0, EventThreadProc, pInfo, 0, &dwThreadID);
|
|
if (hThread) {
|
|
|
|
//
|
|
// Close the handler, so that kernel mode thread object is
|
|
// closed when the thread finishes its mission
|
|
//
|
|
|
|
CloseHandle(hThread);
|
|
|
|
} else {
|
|
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// Don't wait for completion
|
|
//
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// The notification should free the allocated resources
|
|
//
|
|
|
|
if (hr == S_OK) {
|
|
|
|
return (hr);
|
|
}
|
|
|
|
if (hr == E_OUTOFMEMORY) {
|
|
|
|
DBG_ERR(("FireEventAsync : Memory alloc failed"));
|
|
}
|
|
|
|
//
|
|
// Garbage collection to avoid memory leak
|
|
//
|
|
|
|
if (pInfo) {
|
|
|
|
if (pInfo->bstrDeviceDescription) {
|
|
SysFreeString(pInfo->bstrDeviceDescription);
|
|
}
|
|
if (pInfo->bstrDeviceID) {
|
|
SysFreeString(pInfo->bstrDeviceID);
|
|
}
|
|
if (pInfo->bstrEventDescription) {
|
|
SysFreeString(pInfo->bstrEventDescription);
|
|
}
|
|
if (pInfo->bstrFullItemName) {
|
|
SysFreeString(pInfo->bstrFullItemName);
|
|
}
|
|
if (pInfo->pIEventCB) {
|
|
pInfo->pIEventCB->Release();
|
|
}
|
|
|
|
LocalFree(pInfo);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::NotifySTIEvent
|
|
*
|
|
* Search through list of registered events and notify anyone who
|
|
* matched current event
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pWiaNotify - Event infor
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 5/18/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::NotifySTIEvent(
|
|
PWIANOTIFY pWiaNotify,
|
|
ULONG ulEventType,
|
|
BSTR bstrFullItemName)
|
|
{
|
|
DBG_FN(CEventNotifier::NotifySTIEvent);
|
|
|
|
HRESULT hr = S_FALSE;
|
|
EventDestNode *pCurNode;
|
|
BOOL bDeviceLocked;
|
|
DWORD dwDeviceType;
|
|
BSTR bstrDevDescription = NULL;
|
|
IWiaMiniDrv *pIWiaMiniDrv = NULL;
|
|
WIA_DEV_CAP_DRV *pWiaDrvDevCap = NULL;
|
|
BSTR bstrEventDescription = NULL;
|
|
LONG lNumEntries = 0;
|
|
LONG i = 0;
|
|
LONG lDevErrVal;
|
|
WIAEventThreadInfo masterInfo;
|
|
ULONG ulNumHandlers;
|
|
EventDestNode *pDefHandlerNode;
|
|
IWiaEventCallback *pIEventCB;
|
|
ACTIVE_DEVICE *pDevice = NULL;
|
|
ULONG *pulTemp;
|
|
|
|
WiaEventInfo *pWiaEventInfo = NULL;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ZeroMemory(&masterInfo, sizeof(masterInfo));
|
|
|
|
//
|
|
// Get device information from STI active device list
|
|
//
|
|
|
|
hr = WiaGetDeviceInfo(
|
|
pWiaNotify->bstrDevId,
|
|
&dwDeviceType,
|
|
&bstrDevDescription,
|
|
&pDevice);
|
|
if (hr != S_OK) {
|
|
DBG_ERR(("Failed to get WiaGetDeviceInfo from NotifySTIEvent, 0x%X", hr));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make sure we only grab global event Critical Section AFTER we've released the
|
|
// device list Critical Section (used and released in WiaGetDeviceInfo).
|
|
//
|
|
CWiaCritSect CritSect(&g_semEventNode);
|
|
|
|
//
|
|
// QI for the WIA mini driver interface
|
|
//
|
|
|
|
hr = pDevice->m_DrvWrapper.QueryInterface(
|
|
IID_IWiaMiniDrv, (void **)&pIWiaMiniDrv);
|
|
if (FAILED(hr)) {
|
|
DBG_WRN(("Failed to QI for IWiaMini from NotifySTIEvent (0x%X)", hr));
|
|
}
|
|
|
|
//
|
|
// Hardware might be gone, access to it should not be allowed
|
|
//
|
|
|
|
bDeviceLocked = FALSE;
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
__try {
|
|
|
|
__try {
|
|
|
|
//
|
|
// Notify the mini driver of the new event.
|
|
// NOTE: We don't lock here. The event must be delivered
|
|
// to the driver regardless. A queued system would
|
|
// be better, but it MUST guarantee delivery.
|
|
//
|
|
|
|
hr = pDevice->m_DrvWrapper.WIA_drvNotifyPnpEvent(
|
|
&pWiaNotify->stiNotify.guidNotificationCode,
|
|
pWiaNotify->bstrDevId,
|
|
0);
|
|
if (FAILED(hr)) {
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// This is a "work-around" for our in-box HP scanner driver, which
|
|
// calls down to the microdriver even after it has been informed
|
|
// via drvNotifyPnPEvent that the device no longer exists...
|
|
// Only if this is not a disconnect event, do we want to
|
|
// call driver
|
|
//
|
|
if (pWiaNotify->stiNotify.guidNotificationCode != WIA_EVENT_DEVICE_DISCONNECTED) {
|
|
|
|
//
|
|
// Lock the device since the drvInitializeWia may have not been
|
|
// called and the IWiaMiniDrv can not lock the device
|
|
//
|
|
// NOTE: Timeout is 20sec
|
|
//
|
|
// NOTE: We don't lock serial devices here...
|
|
// , this function on a connection should be as fast as possible
|
|
//
|
|
|
|
if (!( pDevice->m_DrvWrapper.getHWConfig() & STI_HW_CONFIG_SERIAL) ||
|
|
!IsEqualGUID(pWiaNotify->stiNotify.guidNotificationCode,WIA_EVENT_DEVICE_CONNECTED)
|
|
) {
|
|
|
|
hr = g_pStiLockMgr->RequestLock(pDevice, 20000);
|
|
if (FAILED(hr)) {
|
|
__leave;
|
|
}
|
|
bDeviceLocked = TRUE;
|
|
}
|
|
|
|
//
|
|
// Note that a NULL context passed to the minidriver. This should be OK since
|
|
// capabilities are not tied to any item context.
|
|
//
|
|
|
|
hr = pDevice->m_DrvWrapper.WIA_drvGetCapabilities(
|
|
NULL,
|
|
WIA_DEVICE_EVENTS,
|
|
&lNumEntries,
|
|
&pWiaDrvDevCap,
|
|
&lDevErrVal);
|
|
}
|
|
}
|
|
__finally {
|
|
|
|
//
|
|
// Unlock the device first
|
|
//
|
|
|
|
if (bDeviceLocked) {
|
|
g_pStiLockMgr->RequestUnlock(pDevice);
|
|
bDeviceLocked = FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
DBG_ERR(("NotifySTIEvent() : Exception happened in the drvGetCapabilities"));
|
|
|
|
SysFreeString(bstrDevDescription);
|
|
|
|
if (pIWiaMiniDrv) {
|
|
pIWiaMiniDrv->Release();
|
|
}
|
|
return (E_FAIL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mini driver failed the operation
|
|
//
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
if (pWiaDrvDevCap) {
|
|
|
|
__try {
|
|
//
|
|
// Retrieve event related information
|
|
//
|
|
|
|
for (i = 0; i < lNumEntries; i++) {
|
|
|
|
if (pWiaDrvDevCap[i].guid != NULL) {
|
|
if (*pWiaDrvDevCap[i].guid == pWiaNotify->stiNotify.guidNotificationCode) {
|
|
|
|
if (! ulEventType) {
|
|
ulEventType = pWiaDrvDevCap[i].ulFlags;
|
|
}
|
|
bstrEventDescription = SysAllocString(pWiaDrvDevCap[i].wszDescription);
|
|
break;
|
|
}
|
|
} else {
|
|
DBG_WRN(("NotifySTIEvent() : Driver's event guid is NULL, index = %d", i));
|
|
}
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DBG_ERR(("NotifySTIEvent() : Exception occurred while accessing driver's event array"));
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
//
|
|
// The device is not supposed to generate this event
|
|
//
|
|
|
|
if ((i == lNumEntries) || (!bstrEventDescription)) {
|
|
DBG_ERR(("NotifySTIEvent() : Event description is NULL or Event GUID not found"));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else {
|
|
// Minidriver is wrong, claiming success and returning NULL
|
|
DBG_ERR(("NotifySTIEvent() got NULL cap list from drivers .") );
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure device connected and device disconnected have at least the
|
|
// notification bit set.
|
|
//
|
|
if ((pWiaNotify->stiNotify.guidNotificationCode == WIA_EVENT_DEVICE_CONNECTED) ||
|
|
(pWiaNotify->stiNotify.guidNotificationCode == WIA_EVENT_DEVICE_DISCONNECTED))
|
|
{
|
|
ulEventType |= WIA_NOTIFICATION_EVENT;
|
|
}
|
|
|
|
//
|
|
// If the event is a connect or diconnect event, always fire it.
|
|
//
|
|
if (FAILED(hr) &&
|
|
((pWiaNotify->stiNotify.guidNotificationCode == WIA_EVENT_DEVICE_CONNECTED) ||
|
|
(pWiaNotify->stiNotify.guidNotificationCode == WIA_EVENT_DEVICE_DISCONNECTED))) {
|
|
|
|
DBG_WRN(("NotifySTIEvent() : hr indicates FAILURE, but event is Connect/Disconnect"));
|
|
|
|
//
|
|
// Set the event type and string
|
|
//
|
|
|
|
if (! ulEventType) {
|
|
ulEventType = WIA_NOTIFICATION_EVENT;
|
|
}
|
|
if (pWiaNotify->stiNotify.guidNotificationCode == WIA_EVENT_DEVICE_CONNECTED) {
|
|
|
|
bstrEventDescription = SysAllocString(WIA_EVENT_DEVICE_CONNECTED_STR);
|
|
} else {
|
|
bstrEventDescription = SysAllocString(WIA_EVENT_DEVICE_DISCONNECTED_STR);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Prepare the master thread info block
|
|
//
|
|
|
|
masterInfo.eventGUID = pWiaNotify->stiNotify.guidNotificationCode;
|
|
masterInfo.bstrEventDescription = bstrEventDescription;
|
|
masterInfo.bstrDeviceID = pWiaNotify->bstrDevId;
|
|
masterInfo.bstrDeviceDescription = bstrDevDescription;
|
|
masterInfo.dwDeviceType = dwDeviceType;
|
|
|
|
//
|
|
// Retrieve the full item name set by the driver
|
|
//
|
|
|
|
masterInfo.bstrFullItemName = bstrFullItemName;
|
|
|
|
masterInfo.ulEventType = ulEventType;
|
|
masterInfo.ulReserved = 0;
|
|
masterInfo.pIEventCB = NULL;
|
|
|
|
//
|
|
// For Notification type of event
|
|
//
|
|
|
|
if (ulEventType & WIA_NOTIFICATION_EVENT) {
|
|
//
|
|
// We don't need to grab the g_semEventNode critical section for our
|
|
// current runtime implementation. So we'll simply create a WiaEventInfo object
|
|
// to describe the event, and we'll actually "fire" the event notifications
|
|
// after we're out of this block.
|
|
//
|
|
pWiaEventInfo = new WiaEventInfo();
|
|
if (pWiaEventInfo)
|
|
{
|
|
pWiaEventInfo->setEventGuid(pWiaNotify->stiNotify.guidNotificationCode);
|
|
pWiaEventInfo->setDeviceID(pWiaNotify->bstrDevId);
|
|
pWiaEventInfo->setEventDescription(bstrEventDescription);
|
|
pWiaEventInfo->setDeviceDescription(bstrDevDescription);
|
|
pWiaEventInfo->setFullItemName(bstrFullItemName);
|
|
pWiaEventInfo->setDeviceType(dwDeviceType);
|
|
pWiaEventInfo->setEventType(ulEventType);
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("Cannot notify clients of runtime event - we appear to be out of memory"));
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
/* OLD CODE. Will be removed for .NET Server and replaced with alternate
|
|
WIA runtime event behavior.
|
|
|
|
|
|
for (pCurNode = m_pEventDestNodes;
|
|
pCurNode; pCurNode = pCurNode->pNext) {
|
|
|
|
if (! pCurNode->pIEventCB) {
|
|
continue;
|
|
}
|
|
|
|
if (
|
|
(
|
|
(wcscmp(pWiaNotify->bstrDevId, pCurNode->bstrDeviceID) == 0) ||
|
|
(wcscmp(L"All", pCurNode->bstrDeviceID) == 0)
|
|
) &&
|
|
|
|
(pWiaNotify->stiNotify.guidNotificationCode == pCurNode->iidEventGUID)
|
|
) {
|
|
|
|
masterInfo.pIEventCB = pCurNode->pIEventCB;
|
|
|
|
DBG_WRN(("NotifySTIEvent() : About to FireEventAsync(...)"));
|
|
FireEventAsync(&masterInfo);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
//
|
|
// For Action type of event, find the default handler and fire it
|
|
//
|
|
|
|
|
|
if (ulEventType & WIA_ACTION_EVENT) {
|
|
|
|
#ifndef UNICODE
|
|
|
|
//
|
|
// Get whether there is an user logged in
|
|
//
|
|
|
|
HWND hWin;
|
|
|
|
hWin = FindWindow("Progman", NULL);
|
|
if (! hWin) {
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&g_RpcEvent.cs);
|
|
|
|
if(g_RpcEvent.pAsync) {
|
|
RPC_STATUS status;
|
|
int nReply = 1;
|
|
|
|
g_RpcEvent.pEvent->EventGuid = pWiaNotify->stiNotify.guidNotificationCode;
|
|
g_RpcEvent.pEvent->bstrEventDescription = SysAllocString(bstrEventDescription);
|
|
g_RpcEvent.pEvent->bstrDeviceID = SysAllocString(pWiaNotify->bstrDevId);
|
|
g_RpcEvent.pEvent->bstrDeviceDescription = SysAllocString(bstrDevDescription);
|
|
g_RpcEvent.pEvent->dwDeviceType = dwDeviceType;
|
|
g_RpcEvent.pEvent->bstrFullItemName = SysAllocString(bstrFullItemName);
|
|
g_RpcEvent.pEvent->ulEventType = ulEventType;
|
|
|
|
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;
|
|
} else {
|
|
DBG_ERR(("Did not have pAsync for this event"));
|
|
}
|
|
|
|
LeaveCriticalSection(&g_RpcEvent.cs);
|
|
|
|
#if 0
|
|
GetNumPersistentHandlerAndDefault(
|
|
pWiaNotify->bstrDevId,
|
|
&pWiaNotify->stiNotify.guidNotificationCode,
|
|
&ulNumHandlers,
|
|
&pDefHandlerNode);
|
|
|
|
if (pDefHandlerNode) {
|
|
|
|
if (pDefHandlerNode->tszCommandline[0] != '\0') {
|
|
|
|
//
|
|
// This is a traditional handler with command line
|
|
//
|
|
|
|
StartCallbackProgram(
|
|
pDefHandlerNode, &masterInfo);
|
|
|
|
} else {
|
|
|
|
hr = _CoCreateInstanceInConsoleSession(
|
|
pDefHandlerNode->ClsID,
|
|
NULL,
|
|
CLSCTX_LOCAL_SERVER,
|
|
IID_IWiaEventCallback,
|
|
(void**)&pIEventCB);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
masterInfo.pIEventCB = pIEventCB;
|
|
|
|
FireEventAsync(&masterInfo);
|
|
|
|
//
|
|
// Release the callback interface
|
|
//
|
|
|
|
pIEventCB->Release();
|
|
|
|
} else {
|
|
DBG_ERR(("NotifySTIEvent:CoCreateInstance of event callback failed (0x%X)", hr));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Check whether we need to notify runtime clients of event. We know that we need to
|
|
// notify clients if pWiaEventInfo is not NULL, since it contains the runtime event info.
|
|
//
|
|
if (pWiaEventInfo)
|
|
{
|
|
//
|
|
// Notify registered clients of the event.
|
|
//
|
|
if (g_pWiaEventNotifier)
|
|
{
|
|
g_pWiaEventNotifier->NotifyClients(pWiaEventInfo);
|
|
}
|
|
pWiaEventInfo->Release();
|
|
pWiaEventInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Release the temporary BSTRs
|
|
//
|
|
|
|
if (bstrDevDescription) {
|
|
SysFreeString(bstrDevDescription);
|
|
}
|
|
if (bstrEventDescription) {
|
|
SysFreeString(bstrEventDescription);
|
|
}
|
|
if (masterInfo.bstrFullItemName) {
|
|
SysFreeString(masterInfo.bstrFullItemName);
|
|
}
|
|
|
|
if (pDevice) {
|
|
pDevice->Release();
|
|
pDevice = NULL;
|
|
}
|
|
if (pIWiaMiniDrv) {
|
|
pIWiaMiniDrv->Release();
|
|
pIWiaMiniDrv = NULL;
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::RegisterEventCallback
|
|
*
|
|
* Register for event callbacks based on an interface
|
|
*
|
|
* Arguments:
|
|
*
|
|
* lFlags - op flags, register/unregister
|
|
* bstrDeviceID - device ID registering for
|
|
* pEventGUID - Event GUID to register for
|
|
* pIWiaEventCallback - interface to call with event
|
|
* ppEventObj -
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/19/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::RegisterEventCallback(
|
|
LONG lFlags,
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
IWiaEventCallback *pIWiaEventCallback,
|
|
IUnknown **ppEventObj)
|
|
{
|
|
DBG_FN(CEventNotifier::RegisterEventCallback);
|
|
HRESULT hr = E_FAIL;
|
|
PEventDestNode pEventNode = NULL;
|
|
|
|
DBG_TRC(("CEventNotifier::RegisterEventCallback flag %d", lFlags));
|
|
|
|
ASSERT(pIWiaEventCallback != NULL);
|
|
ASSERT(ppEventObj != NULL);
|
|
ASSERT(pEventGUID != NULL);
|
|
|
|
//
|
|
// must have exclusive access when changing list
|
|
//
|
|
|
|
CWiaCritSect CritSect(&g_semEventNode);
|
|
|
|
//
|
|
// if bstrDeviceID is not NULL, make sure it is a device ID
|
|
//
|
|
|
|
if (bstrDeviceID) {
|
|
/* No longer valid
|
|
#ifdef WINNT
|
|
|
|
if (wcslen(bstrDeviceID) != 43) { // {...}\DDDD
|
|
|
|
#else
|
|
|
|
if (wcslen(bstrDeviceID) != 10) { // Image\DDDD
|
|
|
|
#endif
|
|
|
|
DBG_ERR(("CEventNotifier::RegisterEventCallback: invalid DeviceID"));
|
|
return (E_INVALIDARG);
|
|
}
|
|
*/
|
|
}
|
|
|
|
//
|
|
// Check whether the same CB interface is already registered
|
|
//
|
|
|
|
pEventNode = FindEventCBNode(FLAG_EN_FINDCB_EXACT_MATCH,bstrDeviceID, pEventGUID, pIWiaEventCallback);
|
|
|
|
if (! pEventNode) {
|
|
hr = RegisterEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pIWiaEventCallback,
|
|
ppEventObj);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::RegisterEventCallback
|
|
*
|
|
* Register event based on CLSID
|
|
*
|
|
* Arguments:
|
|
*
|
|
* lFlags - op flags, register/unregister, set default
|
|
* bstrDeviceID - device ID registering for
|
|
* pEventGUID - Event GUID to register for
|
|
* pClsid - CLSID to call CoCreateInst on with event
|
|
* bstrDescription -
|
|
* bstrIcon -
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/8/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::RegisterEventCallback(
|
|
LONG lFlags,
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
const GUID *pClsid,
|
|
LPCTSTR ptszCommandline,
|
|
BSTR bstrName,
|
|
BSTR bstrDescription,
|
|
BSTR bstrIcon)
|
|
{
|
|
|
|
DBG_FN(CEventNotifier::RegisterEventCallback (CLSID));
|
|
|
|
HRESULT hr = S_OK;
|
|
SYSTEMTIME sysTime;
|
|
FILETIME fileTime;
|
|
PEventDestNode pEventNode = NULL;
|
|
CLSID clsidApp;
|
|
RPC_STATUS rpcStatus;
|
|
BOOL bUnRegCOMServer;
|
|
BOOL bShowPrompt = FALSE;
|
|
ULONG ulNumExistingHandlers = 0;
|
|
EventDestNode ednTempNode;
|
|
|
|
|
|
DBG_TRC(("CEventNotifier::RegisterEventCallback flag %d", lFlags));
|
|
|
|
ASSERT(pEventGUID != NULL);
|
|
|
|
//DBG_WRN(("RegisterEventCallback: CommandLine=%s"),ptszCommandline);
|
|
|
|
|
|
//
|
|
// Must have exclusive access when changing list
|
|
//
|
|
|
|
CWiaCritSect CritSect(&g_semEventNode);
|
|
|
|
//
|
|
// If there is a device ID, check if id looks proper.
|
|
//
|
|
|
|
if (bstrDeviceID) {
|
|
|
|
//
|
|
// An empty device ID is the same as NULL
|
|
//
|
|
|
|
if (wcslen(bstrDeviceID) == 0) {
|
|
bstrDeviceID = NULL;
|
|
} else {
|
|
|
|
/* No longer valid
|
|
#ifdef WINNT
|
|
|
|
if (wcslen(bstrDeviceID) != 43) { // {...}\DDDD
|
|
|
|
#else
|
|
|
|
if (wcslen(bstrDeviceID) != 10) { // Image\DDDD
|
|
|
|
#endif
|
|
|
|
DBG_ERR(("RegisterEventCallback : invalid DeviceID"));
|
|
return (E_INVALIDARG);
|
|
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
//
|
|
// Default handler is per device / per event
|
|
//
|
|
|
|
if ((lFlags == WIA_SET_DEFAULT_HANDLER) && (! bstrDeviceID)) {
|
|
|
|
DBG_ERR(("RegisterEventCallback : DeviceID required to set default handler"));
|
|
|
|
return (E_INVALIDARG);
|
|
}
|
|
|
|
//
|
|
// Check whether a callback node with the same commandline already exist
|
|
//
|
|
|
|
if (ptszCommandline) {
|
|
|
|
//
|
|
// Do parameter check. Note that we look for >= MAX_PATH because
|
|
// we still need space for terminating NULL.
|
|
//
|
|
if ((lstrlen(ptszCommandline) / sizeof(TCHAR)) >= MAX_PATH) {
|
|
DBG_ERR(("RegisterEventCallback: ptszCommandline is greater than MAX_PATH characters!"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
hr = FindCLSIDForCommandline(ptszCommandline, &clsidApp);
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// Generate a CLSID for the callbacl program
|
|
//
|
|
|
|
rpcStatus = UuidCreate(&clsidApp);
|
|
if (FAILED(rpcStatus)) {
|
|
return (rpcStatus);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Assign the faked CLSID for the callback program
|
|
//
|
|
|
|
pClsid = &clsidApp;
|
|
}
|
|
|
|
ASSERT(pClsid != NULL);
|
|
|
|
switch (lFlags) {
|
|
|
|
case WIA_REGISTER_EVENT_CALLBACK :
|
|
DBG_WRN(("RegisterEventCallback : Setting handler for %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
//
|
|
// REMOVE: This is not actually an error, but we will use error logging to gurantee
|
|
// it always get written to the log. This should be removed as soon as we know what
|
|
// causes #347835.
|
|
//
|
|
DBG_ERR(("RegisterEventCallback : Setting handler for %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
DBG_ERR(("RegisterEventCallback : Handler is %S", (bstrName) ? (bstrName) : L"NULL"));
|
|
|
|
//
|
|
// Name, description, and icon are required
|
|
//
|
|
|
|
if ((! bstrName) || (! bstrDescription) || (! bstrIcon)) {
|
|
|
|
DBG_ERR(("RegisterEventCallback : Name | Description | Icon are missing"));
|
|
return (E_INVALIDARG);
|
|
}
|
|
|
|
//
|
|
// Check whether the same CB interface is already registered
|
|
//
|
|
|
|
pEventNode = FindEventCBNode(0,bstrDeviceID, pEventGUID, pClsid);
|
|
if (pEventNode) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find the handler of the CLSID for all the devices
|
|
//
|
|
|
|
pEventNode = FindEventCBNode(0,NULL, pEventGUID, pClsid);
|
|
|
|
//
|
|
// Initialize the time stamp for the registration
|
|
//
|
|
|
|
//GetSystemTime(&sysTime);
|
|
//SystemTimeToFileTime(&sysTime, &fileTime);
|
|
memset(&fileTime, 0, sizeof(fileTime));
|
|
|
|
if (! pEventNode) {
|
|
|
|
hr = RegisterEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
ptszCommandline,
|
|
bstrName,
|
|
bstrDescription,
|
|
bstrIcon,
|
|
fileTime);
|
|
if (FAILED(hr)) {
|
|
break;
|
|
}
|
|
|
|
hr = SavePersistentEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
ptszCommandline,
|
|
bstrName,
|
|
bstrDescription,
|
|
bstrIcon,
|
|
NULL,
|
|
&ulNumExistingHandlers);
|
|
} else {
|
|
|
|
hr = RegisterEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
pEventNode->tszCommandline,
|
|
pEventNode->bstrName,
|
|
pEventNode->bstrDescription,
|
|
pEventNode->bstrIcon,
|
|
fileTime);
|
|
if (FAILED(hr)) {
|
|
break;
|
|
}
|
|
|
|
hr = SavePersistentEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
pEventNode->tszCommandline,
|
|
pEventNode->bstrName,
|
|
pEventNode->bstrDescription,
|
|
pEventNode->bstrIcon,
|
|
NULL,
|
|
&ulNumExistingHandlers);
|
|
}
|
|
|
|
//
|
|
// If this is the only event handler, make it the default. This will guarantee that
|
|
// there is always a default handler.
|
|
//
|
|
|
|
if (ulNumExistingHandlers == 0) {
|
|
RegisterEventCallback(WIA_SET_DEFAULT_HANDLER,
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
ptszCommandline,
|
|
bstrName,
|
|
bstrDescription,
|
|
bstrIcon);
|
|
};
|
|
|
|
|
|
//
|
|
// Check whether this is a registration for a global handler.
|
|
//
|
|
if (!bstrDeviceID) {
|
|
|
|
//
|
|
// This is a global event handler, so find out how many global handlers
|
|
// there are for this event. If there is more than one, make sure
|
|
// the prompt is Registered.
|
|
//
|
|
PEventDestNode pTempEventNode = NULL;
|
|
BSTR bstrGlobalDeviceID = SysAllocString(L"All");
|
|
|
|
if (bstrGlobalDeviceID) {
|
|
GetNumPersistentHandlerAndDefault(bstrGlobalDeviceID,
|
|
pEventGUID,
|
|
&ulNumExistingHandlers,
|
|
&pTempEventNode);
|
|
if (ulNumExistingHandlers > 1) {
|
|
|
|
//
|
|
// If the number of global handlers is > 1, then we must register the prompt.
|
|
//
|
|
|
|
DBG_ERR(("RegisterEventCallback : Registering Prompt Dialog as global handler"));
|
|
|
|
BSTR bstrInternalString = SysAllocString(L"Internal");
|
|
if (bstrInternalString) {
|
|
RegisterEventCallback(WIA_REGISTER_EVENT_CALLBACK,
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
&WIA_EVENT_HANDLER_PROMPT,
|
|
NULL,
|
|
bstrInternalString,
|
|
bstrInternalString,
|
|
bstrInternalString);
|
|
SysFreeString(bstrInternalString);
|
|
} else {
|
|
DBG_ERR(("RegisterEventCallback : Out of memory!"));
|
|
}
|
|
}
|
|
|
|
SysFreeString(bstrGlobalDeviceID);
|
|
bstrGlobalDeviceID = NULL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WIA_SET_DEFAULT_HANDLER :
|
|
DBG_WRN(("RegisterEventCallback (set default) : Setting default handler for for %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
//
|
|
// REMOVE: This is not actually an error, but we will use error logging to gurantee
|
|
// it always get written to the log. This should be removed as soon as we know what
|
|
// causes #347835.
|
|
//
|
|
DBG_ERR(("RegisterEventCallback (set default) : Setting handler for %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
DBG_ERR(("RegisterEventCallback (set default) : Handler is %S", (bstrName) ? (bstrName) : L"NULL"));
|
|
|
|
//
|
|
// Find the handler of the CLSID for this devices. Note that STI proxy event match is considered here
|
|
// to allow for STI handlers to be default.
|
|
//
|
|
//
|
|
|
|
DBG_WRN(("RegisterEventCallback (set default): CommandLine=%S \n",ptszCommandline));
|
|
|
|
#ifdef DEBUG
|
|
WCHAR wszGUIDStr[40];
|
|
|
|
StringFromGUID2(*pEventGUID, wszGUIDStr, 40);
|
|
|
|
DBG_WRN(("SetDefaultHandler: DevId=%S EventUID=%S Commandline=%S",
|
|
(bstrDeviceID) ? (bstrDeviceID) : L"*",
|
|
wszGUIDStr,
|
|
ptszCommandline));
|
|
#endif
|
|
|
|
{
|
|
//
|
|
// Find the existing default handler node, and clear the flag indicating it is
|
|
// the default, since it will now be replaced by a new default
|
|
//
|
|
|
|
ULONG ulNumHandlers = 0;
|
|
PEventDestNode pDefaultNode = NULL;
|
|
hr = GetNumPersistentHandlerAndDefault(bstrDeviceID,
|
|
pEventGUID,
|
|
&ulNumHandlers,
|
|
&pDefaultNode);
|
|
if (SUCCEEDED(hr) && pDefaultNode) {
|
|
|
|
//
|
|
// Clear the flag indicating that it is the default handler, since the
|
|
// current node will now replace it as the default.
|
|
//
|
|
|
|
pDefaultNode->bDeviceDefault = FALSE;
|
|
}
|
|
}
|
|
|
|
pEventNode = FindEventCBNode(0,bstrDeviceID, pEventGUID, pClsid);
|
|
if (! pEventNode) {
|
|
|
|
//
|
|
// Find the handler of the CLSID for all the devices
|
|
//
|
|
|
|
pEventNode = FindEventCBNode(0,NULL, pEventGUID, pClsid);
|
|
if (! pEventNode) {
|
|
|
|
//
|
|
// We couldn't find an existing node for this handler, so fill in
|
|
// information to the temporary event node so we can register this
|
|
// new one anyway.
|
|
//
|
|
memset(&ednTempNode, 0, sizeof(ednTempNode));
|
|
|
|
if (ptszCommandline) {
|
|
lstrcpy(ednTempNode.tszCommandline, ptszCommandline);
|
|
}
|
|
ednTempNode.bstrName = bstrName;
|
|
ednTempNode.bstrDescription = bstrDescription;
|
|
ednTempNode.bstrIcon = bstrIcon;
|
|
|
|
pEventNode = &ednTempNode;
|
|
}
|
|
|
|
//
|
|
// Register the handler of the CLSID for this device
|
|
//
|
|
|
|
GetSystemTime(&sysTime);
|
|
SystemTimeToFileTime(&sysTime, &fileTime);
|
|
|
|
DBG_WRN(("SetDefaultHandler:Found event node EvName=%S CommandLine=%S",
|
|
pEventNode->bstrName,
|
|
pEventNode->tszCommandline));
|
|
|
|
hr = RegisterEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
pEventNode->tszCommandline,
|
|
pEventNode->bstrName,
|
|
pEventNode->bstrDescription,
|
|
pEventNode->bstrIcon,
|
|
fileTime,
|
|
TRUE);
|
|
if (FAILED(hr)) {
|
|
DBG_WRN(("RegisterEventCallback : RegisterEventCB for %S failed", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Change the time stamp so that it is regarded as default
|
|
//
|
|
|
|
GetSystemTime(&sysTime);
|
|
SystemTimeToFileTime(&sysTime, &pEventNode->timeStamp);
|
|
|
|
//
|
|
// NOTE: Timestamps not valid on Win9x, so use a flag to indicate default handler.
|
|
// Change this node's flag to indicate this is the default
|
|
//
|
|
|
|
pEventNode->bDeviceDefault = TRUE;
|
|
|
|
DBG_WRN(("RegisterEventCallback : Resetting default handler for %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
}
|
|
|
|
//
|
|
// Save the persistent event callback node. Note that we specify TRUE as the last
|
|
// parameter to indicate that the default handler is now this node. This will
|
|
// cause a registry entry to be written that indicates this as the default handler.
|
|
//
|
|
|
|
hr = SavePersistentEventCB(
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
pClsid,
|
|
pEventNode->tszCommandline,
|
|
pEventNode->bstrName,
|
|
pEventNode->bstrDescription,
|
|
pEventNode->bstrIcon,
|
|
&bShowPrompt,
|
|
&ulNumExistingHandlers,
|
|
TRUE);
|
|
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("SetDefaultHandler:SavePers CommandLine=%S failed with hr = 0x%08X!!!!",pEventNode->tszCommandline, hr));
|
|
}
|
|
|
|
break;
|
|
|
|
case WIA_UNREGISTER_EVENT_CALLBACK :
|
|
|
|
hr = UnregisterEventCB(
|
|
bstrDeviceID, pEventGUID, pClsid, &bUnRegCOMServer);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CEventNotifier::RegisterEventCallback, UnregisterEventCB failed"));
|
|
break;
|
|
}
|
|
|
|
hr = DelPersistentEventCB(
|
|
bstrDeviceID, pEventGUID, pClsid, bUnRegCOMServer);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CEventNotifier::RegisterEventCallback, DelPersistentEventCB failed"));
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
if (bShowPrompt && (*pClsid != WIA_EVENT_HANDLER_PROMPT)) {
|
|
|
|
//
|
|
// This is a new event handler being registered. Our semantics are as follows:
|
|
// The new application may not simply override any existing handlers. Therefore,
|
|
// if a default handler already exists for this device, we must show a prompt.
|
|
//
|
|
|
|
if (ulNumExistingHandlers > 0) {
|
|
|
|
//
|
|
// This is a new default event registration, so
|
|
// we must show Prompt dialog
|
|
//
|
|
|
|
DBG_WRN(("RegisterEventCallback : About to Register Prompt Dialog for device %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
|
|
BSTR bstrInternalString = SysAllocString(L"Internal");
|
|
if (bstrInternalString) {
|
|
RegisterEventCallback(WIA_SET_DEFAULT_HANDLER,
|
|
bstrDeviceID,
|
|
pEventGUID,
|
|
&WIA_EVENT_HANDLER_PROMPT,
|
|
NULL,
|
|
bstrInternalString,
|
|
bstrInternalString,
|
|
bstrInternalString);
|
|
SysFreeString(bstrInternalString);
|
|
}
|
|
|
|
DBG_WRN(("RegisterEventCallback : Registered Prompt Dialog for device %S", (bstrDeviceID) ? (bstrDeviceID) : L"*"));
|
|
}
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::RegisterEventCB
|
|
*
|
|
* add event notify to list
|
|
*
|
|
* Arguments:
|
|
*
|
|
* bstrDeviceID - device event is being registered to monitor
|
|
* pEventGUID - guid that defines device event of interest
|
|
* pIWiaEventCallback - app's event interface
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/4/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::RegisterEventCB(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
IWiaEventCallback *pIWiaEventCallback,
|
|
IUnknown **ppIEventObj)
|
|
{
|
|
DBG_FN(CEventNotifier::RegisterEventCB);
|
|
HRESULT hr;
|
|
PEventDestNode pEventDestNode = NULL;
|
|
|
|
ASSERT(pIWiaEventCallback != NULL);
|
|
ASSERT(pEventGUID != NULL);
|
|
|
|
if (!pEventGUID || !pIWiaEventCallback) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Allocate and initialize the new node
|
|
//
|
|
|
|
pEventDestNode = (EventDestNode *)LocalAlloc(LPTR, sizeof(EventDestNode));
|
|
|
|
if (! pEventDestNode) {
|
|
DBG_ERR(("RegisterEventCB: Out of memory"));
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
|
|
// Initialize default flag
|
|
pEventDestNode->bDeviceDefault = FALSE;
|
|
|
|
//
|
|
// is a device name given? If not then match all devices
|
|
//
|
|
|
|
if (bstrDeviceID == NULL) {
|
|
pEventDestNode->bstrDeviceID = SysAllocString(L"All");
|
|
} else {
|
|
pEventDestNode->bstrDeviceID = SysAllocString(bstrDeviceID);
|
|
}
|
|
|
|
//
|
|
// check allocs
|
|
//
|
|
|
|
if (pEventDestNode->bstrDeviceID == NULL) {
|
|
LocalFree(pEventDestNode);
|
|
DBG_ERR(("RegisterEventCB: Out of memory"));
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// create an object to track the lifetime of this event
|
|
//
|
|
|
|
CWiaInterfaceEvent *pEventObj = new CWiaInterfaceEvent(pEventDestNode);
|
|
|
|
if (pEventObj == NULL) {
|
|
DBG_ERR(("RegisterEventCB: Out of memory"));
|
|
SysFreeString(pEventDestNode->bstrDeviceID);
|
|
LocalFree(pEventDestNode);
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// get simple iunknown from object
|
|
//
|
|
|
|
hr = pEventObj->QueryInterface(IID_IUnknown,(void **)ppIEventObj);
|
|
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("RegisterEventCB: QI of pEventObj failed"));
|
|
|
|
delete pEventObj;
|
|
SysFreeString(pEventDestNode->bstrDeviceID);
|
|
LocalFree(pEventDestNode);
|
|
return (hr);
|
|
}
|
|
|
|
//
|
|
// add info to event list
|
|
//
|
|
|
|
pEventDestNode->iidEventGUID = *pEventGUID;
|
|
|
|
pIWiaEventCallback->AddRef();
|
|
pEventDestNode->pIEventCB = pIWiaEventCallback;
|
|
memset(&pEventDestNode->ClsID, 0, sizeof(pEventDestNode->ClsID));
|
|
|
|
//
|
|
// Put the new node at the head of the list
|
|
//
|
|
|
|
LinkNode(pEventDestNode);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* RegisterEventCB
|
|
*
|
|
*
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/8/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::RegisterEventCB(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
const GUID *pClsID,
|
|
LPCTSTR ptszCommandline,
|
|
BSTR bstrName,
|
|
BSTR bstrDescription,
|
|
BSTR bstrIcon,
|
|
FILETIME &timeStamp,
|
|
BOOL bIsDeafult) // = FALSE
|
|
{
|
|
DBG_FN(CEventNotifier::RegisterEventCB);
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
EventDestNode *pEventDestNode = NULL;
|
|
|
|
ASSERT(pClsID != NULL);
|
|
ASSERT(pEventGUID != NULL);
|
|
|
|
|
|
if (!pEventGUID || !pClsID) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
do {
|
|
|
|
//
|
|
// Do parameter check. Note that we look for >= MAX_PATH because
|
|
// we still need space for terminating NULL.
|
|
//
|
|
if ((lstrlen(ptszCommandline) / sizeof(TCHAR)) >= MAX_PATH) {
|
|
DBG_ERR(("CEventNotifier::RegisterEventCB: ptszCommandline is greater than MAX_PATH characters!"));
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate and initialize the new node
|
|
//
|
|
|
|
pEventDestNode = (EventDestNode *)LocalAlloc(LPTR, sizeof(EventDestNode));
|
|
|
|
if (! pEventDestNode) {
|
|
DBG_ERR(("CEventNotifier::RegisterEventCB: Out of memory"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Is a device name given ? If not then match all devices
|
|
//
|
|
|
|
if (bstrDeviceID == NULL) {
|
|
pEventDestNode->bstrDeviceID = SysAllocString(L"All");
|
|
} else {
|
|
pEventDestNode->bstrDeviceID = SysAllocString(bstrDeviceID);
|
|
}
|
|
|
|
//
|
|
// Check callback node allocation
|
|
//
|
|
|
|
if (pEventDestNode->bstrDeviceID == NULL) {
|
|
DBG_ERR(("CEventNotifier::RegisterEventCB: Out of memory"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add info to event callback node
|
|
//
|
|
|
|
pEventDestNode->iidEventGUID = *pEventGUID;
|
|
pEventDestNode->pIEventCB = NULL;
|
|
pEventDestNode->ClsID = *pClsID;
|
|
|
|
pEventDestNode->bstrName = SysAllocString(bstrName);
|
|
if (! pEventDestNode->bstrName) {
|
|
break;
|
|
}
|
|
|
|
pEventDestNode->bstrDescription = SysAllocString(bstrDescription);
|
|
if (! pEventDestNode->bstrDescription) {
|
|
break;
|
|
}
|
|
|
|
pEventDestNode->bstrIcon = SysAllocString(bstrIcon);
|
|
if (! pEventDestNode->bstrIcon) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy the commandline of the callback app
|
|
//
|
|
|
|
if ((ptszCommandline) && (ptszCommandline[0])) {
|
|
_tcscpy(pEventDestNode->tszCommandline, ptszCommandline);
|
|
} else {
|
|
pEventDestNode->tszCommandline[0] = '\0';
|
|
}
|
|
|
|
//
|
|
// Set the time stamp of registration
|
|
//
|
|
|
|
pEventDestNode->timeStamp = timeStamp;
|
|
|
|
//
|
|
// Set whether this is the default event handler
|
|
//
|
|
|
|
if (bIsDeafult) {
|
|
pEventDestNode->bDeviceDefault = TRUE;
|
|
}
|
|
|
|
hr = S_OK;
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Unwind the partial created node in case of failure
|
|
//
|
|
|
|
if (hr != S_OK) {
|
|
|
|
if (pEventDestNode) {
|
|
|
|
if (pEventDestNode->bstrDeviceID) {
|
|
SysFreeString(pEventDestNode->bstrDeviceID);
|
|
}
|
|
if (pEventDestNode->bstrName) {
|
|
SysFreeString(pEventDestNode->bstrName);
|
|
}
|
|
if (pEventDestNode->bstrDescription) {
|
|
SysFreeString(pEventDestNode->bstrDescription);
|
|
}
|
|
if (pEventDestNode->bstrIcon) {
|
|
SysFreeString(pEventDestNode->bstrIcon);
|
|
}
|
|
|
|
LocalFree(pEventDestNode);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
//
|
|
// Put the new node at the header of the list
|
|
//
|
|
|
|
LinkNode(pEventDestNode);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::UnregisterEventCB
|
|
*
|
|
* Unregister the specified Event Callback
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/4/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::UnregisterEventCB(
|
|
PEventDestNode pCurNode)
|
|
{
|
|
DBG_FN(CEventNotifier::UnregisterEventCB);
|
|
|
|
ASSERT(pCurNode != NULL);
|
|
|
|
if (!pCurNode) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
UnlinkNode(pCurNode);
|
|
|
|
//
|
|
// Free the node
|
|
//
|
|
|
|
if (pCurNode->bstrDeviceID) {
|
|
SysFreeString(pCurNode->bstrDeviceID);
|
|
}
|
|
|
|
if (pCurNode->bstrDescription) {
|
|
SysFreeString(pCurNode->bstrDescription);
|
|
}
|
|
|
|
if (pCurNode->bstrIcon) {
|
|
SysFreeString(pCurNode->bstrIcon);
|
|
}
|
|
|
|
pCurNode->pIEventCB->Release();
|
|
|
|
LocalFree(pCurNode);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::UnregisterEventCB
|
|
*
|
|
* Unregister the specified Event Callback
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/4/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::UnregisterEventCB(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
const GUID *pClsID,
|
|
BOOL *pbUnRegCOMServer)
|
|
{
|
|
DBG_FN(CEventNotifier::UnregisterEventCB);
|
|
HRESULT hr = E_INVALIDARG;
|
|
EventDestNode *pCurNode, *pNextNode;
|
|
int nHandlerRef;
|
|
|
|
//
|
|
// Clear out the return value
|
|
//
|
|
|
|
*pbUnRegCOMServer = FALSE;
|
|
|
|
//
|
|
// Delete the handler(s) from the list
|
|
//
|
|
|
|
pCurNode = m_pEventDestNodes;
|
|
|
|
while (pCurNode) {
|
|
|
|
if ((bstrDeviceID) &&
|
|
(lstrcmpiW(pCurNode->bstrDeviceID, bstrDeviceID))) {
|
|
|
|
pCurNode = pCurNode->pNext;
|
|
continue;
|
|
}
|
|
|
|
if ((*pEventGUID != pCurNode->iidEventGUID) ||
|
|
(*pClsID != pCurNode->ClsID)) {
|
|
|
|
pCurNode = pCurNode->pNext;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Unlink the current node if it is not busy
|
|
//
|
|
|
|
pNextNode = pCurNode->pNext;
|
|
UnlinkNode(pCurNode);
|
|
|
|
//
|
|
// Need to consider deleteing the faked server
|
|
//
|
|
|
|
if (pCurNode->tszCommandline[0] != '\0') {
|
|
*pbUnRegCOMServer = TRUE;
|
|
}
|
|
|
|
//
|
|
// Free the node
|
|
//
|
|
|
|
if (pCurNode->bstrDeviceID) {
|
|
SysFreeString(pCurNode->bstrDeviceID);
|
|
}
|
|
|
|
if (pCurNode->bstrDescription) {
|
|
SysFreeString(pCurNode->bstrDescription);
|
|
}
|
|
|
|
if (pCurNode->bstrIcon) {
|
|
SysFreeString(pCurNode->bstrIcon);
|
|
}
|
|
|
|
LocalFree(pCurNode);
|
|
|
|
hr = S_OK;
|
|
|
|
//
|
|
// Delete the event handler for specific device
|
|
//
|
|
|
|
if (bstrDeviceID) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Move on to the next node
|
|
//
|
|
|
|
pCurNode = pNextNode;
|
|
}
|
|
|
|
//
|
|
// The faked COM server should not be removed if it is still in use
|
|
//
|
|
|
|
if (*pbUnRegCOMServer) {
|
|
|
|
nHandlerRef = 0;
|
|
|
|
for (pCurNode = m_pEventDestNodes;
|
|
pCurNode;
|
|
pCurNode = pCurNode->pNext) {
|
|
|
|
//
|
|
// Ignode the callback interface pointers
|
|
//
|
|
|
|
if (*pClsID == pCurNode->ClsID) {
|
|
nHandlerRef++;
|
|
}
|
|
}
|
|
|
|
if (nHandlerRef) {
|
|
*pbUnRegCOMServer = FALSE;
|
|
} else {
|
|
*pbUnRegCOMServer = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Indicate that the IWiaEventCallback is not found.
|
|
//
|
|
|
|
return (hr);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::FindEventCBNode
|
|
*
|
|
* Check whether the specified Event Callback is already registered
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/4/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PEventDestNode
|
|
CEventNotifier::FindEventCBNode(
|
|
UINT uiFlags,
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
IWiaEventCallback *pIWiaEventCallback)
|
|
{
|
|
DBG_FN(CEventNotifier::FindEventCBNode);
|
|
HRESULT hr;
|
|
EventDestNode *pCurNode;
|
|
IUnknown *pICurUnk, *pINewUnk;
|
|
|
|
//
|
|
// Retrieve the IUnknown of the new Event Callback
|
|
//
|
|
|
|
hr = pIWiaEventCallback->QueryInterface(IID_IUnknown, (void **)&pINewUnk);
|
|
if (FAILED(hr)) {
|
|
DBG_ERR(("CEventNotifier::IsDupEventCB, QI for IID_IUnknown failed"));
|
|
return (NULL);
|
|
}
|
|
|
|
for (pCurNode = m_pEventDestNodes; pCurNode; pCurNode = pCurNode->pNext) {
|
|
|
|
if (wcscmp(
|
|
pCurNode->bstrDeviceID,
|
|
bstrDeviceID ? bstrDeviceID : L"All") != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (pCurNode->iidEventGUID != *pEventGUID) {
|
|
|
|
//
|
|
// If we are instructed to allow STI proxy event matches - check node GUID against STI event proxy GUID
|
|
// If exact match is required - continue without checking
|
|
//
|
|
if ( (uiFlags & FLAG_EN_FINDCB_EXACT_MATCH ) ||
|
|
(pCurNode->iidEventGUID != WIA_EVENT_STI_PROXY)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (pCurNode->pIEventCB) {
|
|
|
|
hr = pCurNode->pIEventCB->QueryInterface(
|
|
IID_IUnknown, (void **)&pICurUnk);
|
|
if (FAILED(hr)) {
|
|
pINewUnk->Release();
|
|
return (NULL);
|
|
}
|
|
|
|
//
|
|
// COM can only guarantee that IUnknowns are the same
|
|
//
|
|
|
|
if (pICurUnk == pINewUnk) {
|
|
|
|
pICurUnk->Release();
|
|
pINewUnk->Release();
|
|
|
|
return (pCurNode);
|
|
} else {
|
|
|
|
pICurUnk->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
pINewUnk->Release();
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::FindEventCBNode
|
|
*
|
|
* Check whether the specified Event Callback is already registered
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/4/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PEventDestNode
|
|
CEventNotifier::FindEventCBNode(
|
|
UINT uiFlags,
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
const GUID *pClsID)
|
|
{
|
|
DBG_FN(CEventNotifier::FindEventCBNode);
|
|
PEventDestNode pCurNode;
|
|
|
|
for (pCurNode = m_pEventDestNodes; pCurNode; pCurNode = pCurNode->pNext) {
|
|
|
|
if (wcscmp(
|
|
pCurNode->bstrDeviceID,
|
|
bstrDeviceID ? bstrDeviceID : L"All") != 0) {
|
|
continue;
|
|
}
|
|
|
|
if (pCurNode->iidEventGUID != *pEventGUID) {
|
|
//
|
|
// If we are instructed to allow STI proxy event matches - check node GUID against STI event proxy GUID
|
|
// If exact match is required - continue without checking
|
|
//
|
|
if ( (uiFlags & FLAG_EN_FINDCB_EXACT_MATCH ) ||
|
|
(pCurNode->iidEventGUID != WIA_EVENT_STI_PROXY)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((! pCurNode->pIEventCB) && (pCurNode->ClsID == *pClsID)) {
|
|
return (pCurNode);
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::FindCLSIDForCommandline
|
|
*
|
|
* Find the CLSID for the specified commandline
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 11/4/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::FindCLSIDForCommandline(
|
|
LPCTSTR ptszCommandline,
|
|
CLSID *pClsID)
|
|
{
|
|
DBG_FN(CEventNotifier::FindCLSIDForCommandline);
|
|
PEventDestNode pCurNode;
|
|
|
|
for (pCurNode = m_pEventDestNodes; pCurNode; pCurNode = pCurNode->pNext) {
|
|
|
|
if ((pCurNode->tszCommandline[0] != '\0') &&
|
|
(_tcsicmp(pCurNode->tszCommandline, ptszCommandline) == 0)) {
|
|
|
|
*pClsID = pCurNode->ClsID;
|
|
return (S_OK);
|
|
}
|
|
}
|
|
|
|
return (E_FAIL);
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::RestoreAllPersistentCBs
|
|
*
|
|
* Restore all the persistent Event Callbacks
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/1/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::RestoreAllPersistentCBs()
|
|
{
|
|
DBG_FN(CEventNotifier::RestoreAllPresistentCBs);
|
|
HKEY hStillImage = NULL;
|
|
HKEY hMSCDevList = NULL;
|
|
DWORD dwIndex;
|
|
HRESULT hr = S_OK;
|
|
DWORD dwError = 0;
|
|
|
|
//
|
|
// Restore device specific handlers
|
|
//
|
|
g_pDevMan->ForEachDeviceInList(DEV_MAN_OP_DEV_RESTORE_EVENT, 0);
|
|
|
|
CWiaCritSect CritSect(&g_semEventNode);
|
|
|
|
//
|
|
// Restore device events for MSC Cameras under Control\StillImage\MSCDevList
|
|
//
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_WIA_MSCDEVICES_W,
|
|
0,
|
|
KEY_READ,
|
|
&hMSCDevList);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
|
|
for (dwIndex = 0; ;dwIndex++) {
|
|
|
|
WCHAR wszDeviceName[STI_MAX_INTERNAL_NAME_LENGTH];
|
|
FILETIME fileTime;
|
|
HKEY hKeyDev = NULL;
|
|
DWORD dwSize = sizeof(wszDeviceName) / sizeof(wszDeviceName[0]);
|
|
|
|
dwError = RegEnumKeyExW(hMSCDevList,
|
|
dwIndex,
|
|
wszDeviceName,
|
|
&dwSize,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&fileTime);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
//
|
|
// No more keys to enumerate
|
|
//
|
|
break;
|
|
}
|
|
wszDeviceName[STI_MAX_INTERNAL_NAME_LENGTH - 1] = L'\0';
|
|
|
|
//
|
|
// Open the device key
|
|
//
|
|
|
|
dwError = RegOpenKeyExW(hMSCDevList,
|
|
wszDeviceName,
|
|
0,
|
|
KEY_READ,
|
|
&hKeyDev);
|
|
if (dwError != ERROR_SUCCESS) {
|
|
//
|
|
// Skip this key
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Restore the event hanlders for this device
|
|
//
|
|
RestoreDevPersistentCBs(hKeyDev);
|
|
RegCloseKey(hKeyDev);
|
|
}
|
|
RegCloseKey(hMSCDevList);
|
|
hMSCDevList = NULL;
|
|
}
|
|
|
|
//
|
|
// Restore the global event callback under the Control\StillImage
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REG_PATH_STILL_IMAGE_CONTROL,
|
|
0,
|
|
KEY_READ,
|
|
&hStillImage);
|
|
|
|
if (dwError != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// The registry entry for the STI is corrupted
|
|
//
|
|
|
|
DBG_ERR(("CEventNotifier::RestoreAllPersistentCBs : Can not open STI control key."));
|
|
hr = (HRESULT_FROM_WIN32(dwError));
|
|
|
|
} else {
|
|
|
|
RestoreDevPersistentCBs(hStillImage);
|
|
|
|
//
|
|
// Close the STI control key (Control\StillImage)
|
|
//
|
|
|
|
RegCloseKey(hStillImage);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::RestoreAllPersistentCBs
|
|
*
|
|
* Restore specific devices' persistent Event Callbacks
|
|
*
|
|
* Arguments:
|
|
*
|
|
* hParentOfEventKey - This is either the device key or the still
|
|
* image key. Either way, the key must have a
|
|
* "Events" subkey.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/1/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::RestoreDevPersistentCBs(
|
|
HKEY hParentOfEventKey)
|
|
{
|
|
DBG_FN(CEventNotifier::RestoreDevPresistantCBs);
|
|
#ifdef UNICODE
|
|
WCHAR tszBuf[MAX_PATH];
|
|
#else
|
|
CHAR tszBuf[MAX_PATH];
|
|
#endif
|
|
LPTSTR ptszEvents = tszBuf;
|
|
LONG lRet;
|
|
HKEY hEvents;
|
|
DWORD dwIndex;
|
|
LPTSTR ptszEventName = tszBuf;
|
|
DWORD dwEventNameLen;
|
|
FILETIME fileTime;
|
|
HKEY hEvent;
|
|
HKEY hCBCLSID;
|
|
DWORD dwValueType;
|
|
LPTSTR ptszGUIDStr = tszBuf;
|
|
GUID eventGUID;
|
|
GUID guidDefaultDevHandler;
|
|
BOOL bIsDefault = FALSE;
|
|
DWORD dwCLSIDIndex;
|
|
DWORD dwGUIDStrLen;
|
|
LPTSTR ptszCBCLSIDStr = tszBuf;
|
|
CLSID callbackCLSID;
|
|
TCHAR tszDeviceID[STI_MAX_INTERNAL_NAME_LENGTH];
|
|
#ifdef UNICODE
|
|
LPWSTR pwszGUIDStr = tszBuf;
|
|
LPWSTR pwszCBCLSIDStr = tszBuf;
|
|
#else
|
|
WCHAR wszBuf[MAX_PATH];
|
|
LPWSTR pwszGUIDStr = wszBuf;
|
|
LPWSTR pwszCBCLSIDStr = wszBuf;
|
|
#endif
|
|
DWORD dwValueLen;
|
|
BSTR bstrName, bstrDescription;
|
|
SYSTEMTIME sysTime;
|
|
TCHAR tszCommandline[MAX_PATH];
|
|
DWORD dwType = REG_SZ;
|
|
DWORD dwSize = sizeof(tszDeviceID);
|
|
DWORD dwError = 0;
|
|
HRESULT hr = E_FAIL;
|
|
BSTR bstrDeviceID = NULL;
|
|
|
|
lstrcpy(ptszEvents, EVENTS);
|
|
|
|
//
|
|
// Attempt to read the DeviceID
|
|
//
|
|
|
|
dwError = RegQueryValueEx(hParentOfEventKey,
|
|
REGSTR_VAL_DEVICE_ID,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)tszDeviceID,
|
|
&dwSize);
|
|
if (dwError == ERROR_SUCCESS) {
|
|
bstrDeviceID = SysAllocString(T2W(tszDeviceID));
|
|
if (!bstrDeviceID) {
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, Out of memory!"));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
} else {
|
|
bstrDeviceID = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Open the Events subkey
|
|
//
|
|
|
|
lRet = RegOpenKeyEx(
|
|
hParentOfEventKey,
|
|
ptszEvents,
|
|
0,
|
|
KEY_READ,
|
|
&hEvents);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
return (HRESULT_FROM_WIN32(lRet)); // Events may not exist
|
|
}
|
|
|
|
//
|
|
// Enumerate all the events under the "Events" subkey
|
|
//
|
|
|
|
for (dwIndex = 0; ;dwIndex++) {
|
|
|
|
dwEventNameLen = sizeof(tszBuf)/sizeof(TCHAR);
|
|
lRet = RegEnumKeyEx(
|
|
hEvents,
|
|
dwIndex,
|
|
ptszEventName,
|
|
&dwEventNameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&fileTime);
|
|
|
|
if (lRet != ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Open the event subkey
|
|
//
|
|
|
|
lRet = RegOpenKeyEx(
|
|
hEvents,
|
|
ptszEventName,
|
|
0,
|
|
KEY_READ,
|
|
&hEvent);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get GUID of the default handler (if present), and save it in
|
|
// guidDefaultDevHandler
|
|
//
|
|
dwValueLen = sizeof(tszBuf);
|
|
lRet = RegQueryValueEx(
|
|
hEvent,
|
|
DEFAULT_HANDLER_VAL,
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)ptszGUIDStr,
|
|
&dwValueLen);
|
|
if ((lRet == ERROR_SUCCESS) && (dwValueType == REG_SZ)) {
|
|
|
|
WCHAR wszDefGUIDStr[MAX_PATH];
|
|
#ifndef UNICODE
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
ptszGUIDStr,
|
|
-1,
|
|
wszDefGUIDStr,
|
|
sizeof(wszDefGUIDStr) / sizeof(WCHAR));
|
|
pwszGUIDStr[38] = 0;
|
|
#else
|
|
lstrcpyW(wszDefGUIDStr, ptszGUIDStr);
|
|
#endif
|
|
|
|
if (SUCCEEDED(CLSIDFromString(wszDefGUIDStr, &guidDefaultDevHandler))) {
|
|
DBG_TRC(("CEventNotifier::RestoreDevPersistentCBs, Default guid: %S",
|
|
ptszGUIDStr));
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We zero out guidDefaultDevHandler to make sure we don't accidentaly hit
|
|
// a match later...
|
|
//
|
|
::ZeroMemory(&guidDefaultDevHandler,sizeof(guidDefaultDevHandler));
|
|
}
|
|
|
|
//
|
|
// Retrieve the GUID of the event
|
|
//
|
|
|
|
dwGUIDStrLen = 39*sizeof(TCHAR); // GUID string is 38 characters long
|
|
lRet = RegQueryValueEx(
|
|
hEvent,
|
|
TEXT("GUID"),
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)ptszGUIDStr,
|
|
&dwGUIDStrLen);
|
|
if ((lRet != ERROR_SUCCESS) || (dwValueType != REG_SZ)) {
|
|
|
|
//
|
|
// Junk event found, skip to the next
|
|
//
|
|
|
|
DBG_TRC(("CEventNotifier::RestoreDevPersistentCBs, Junk event %S found", ptszEventName));
|
|
continue;
|
|
}
|
|
#ifndef UNICODE
|
|
mbstowcs(pwszGUIDStr, ptszGUIDStr, 38);
|
|
pwszGUIDStr[38] = 0;
|
|
#endif
|
|
if (FAILED(CLSIDFromString(pwszGUIDStr, &eventGUID))) {
|
|
|
|
//
|
|
// Invalid event GUID found, skip to the next
|
|
//
|
|
|
|
DBG_TRC(("CEventNotifier::RestoreDevPersistentCBs, invalid event GUID %S found", ptszGUIDStr));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Enumerate all the event handler CLSIDs under this event
|
|
//
|
|
|
|
for (dwCLSIDIndex = 0; ;dwCLSIDIndex++) {
|
|
|
|
dwGUIDStrLen = 39; // CLSID string is 38 character long.
|
|
|
|
lRet = RegEnumKeyEx(
|
|
hEvent,
|
|
dwCLSIDIndex,
|
|
ptszCBCLSIDStr,
|
|
&dwGUIDStrLen,
|
|
NULL,
|
|
NULL, // All the other information is not interesting
|
|
NULL,
|
|
&fileTime);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
break; // End enumeration
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
mbstowcs(pwszCBCLSIDStr, ptszCBCLSIDStr, 38);
|
|
pwszCBCLSIDStr[38] = 0;
|
|
#endif
|
|
|
|
//
|
|
// Convert CLSID and register this callback
|
|
//
|
|
|
|
if (SUCCEEDED(CLSIDFromString(pwszCBCLSIDStr, &callbackCLSID))) {
|
|
|
|
hCBCLSID = NULL;
|
|
bstrName = NULL;
|
|
bstrDescription = NULL;
|
|
|
|
do {
|
|
|
|
//
|
|
// Open the event handler CLSID subkey
|
|
//
|
|
|
|
lRet = RegOpenKeyEx(
|
|
hEvent,
|
|
ptszCBCLSIDStr,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hCBCLSID);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, RegOpenKeyEx() for CLSID failed."));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Retrieve the name, description and icon
|
|
//
|
|
|
|
dwValueLen = sizeof(tszBuf);
|
|
lRet = RegQueryValueEx(
|
|
hCBCLSID,
|
|
NAME_VAL,
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)tszBuf,
|
|
&dwValueLen);
|
|
if ((lRet != ERROR_SUCCESS) || (dwValueType != REG_SZ)) {
|
|
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, RegQueryValueEx() for Name failed."));
|
|
break;
|
|
}
|
|
#ifndef UNICODE
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
tszBuf,
|
|
-1,
|
|
wszBuf,
|
|
MAX_PATH);
|
|
bstrName = SysAllocString(wszBuf);
|
|
#else
|
|
bstrName = SysAllocString(tszBuf);
|
|
#endif
|
|
if (! bstrName) {
|
|
break;
|
|
}
|
|
|
|
dwValueLen = sizeof(tszBuf);
|
|
lRet = RegQueryValueEx(
|
|
hCBCLSID,
|
|
DESC_VAL,
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)tszBuf,
|
|
&dwValueLen);
|
|
if ((lRet != ERROR_SUCCESS) || (dwValueType != REG_SZ)) {
|
|
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, RegQueryValueEx() for Desc failed."));
|
|
break;
|
|
}
|
|
#ifndef UNICODE
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
tszBuf,
|
|
-1,
|
|
wszBuf,
|
|
MAX_PATH);
|
|
bstrDescription = SysAllocString(wszBuf);
|
|
#else
|
|
bstrDescription = SysAllocString(tszBuf);
|
|
#endif
|
|
if (! bstrDescription) {
|
|
break;
|
|
}
|
|
|
|
dwValueLen = sizeof(tszBuf);
|
|
lRet = RegQueryValueEx(
|
|
hCBCLSID,
|
|
ICON_VAL,
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)tszBuf,
|
|
&dwValueLen);
|
|
if ((lRet != ERROR_SUCCESS) || (dwValueType != REG_SZ)) {
|
|
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, RegQueryValueEx() for Desc failed."));
|
|
break;
|
|
}
|
|
#ifndef UNICODE
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
tszBuf,
|
|
-1,
|
|
wszBuf,
|
|
MAX_PATH);
|
|
#endif
|
|
|
|
//
|
|
// Retrieve the command line, it may not exist
|
|
//
|
|
|
|
dwValueLen = sizeof(tszCommandline);
|
|
lRet = RegQueryValueEx(
|
|
hCBCLSID,
|
|
CMDLINE_VAL,
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)tszCommandline,
|
|
&dwValueLen);
|
|
if ((lRet != ERROR_SUCCESS) || (dwValueType != REG_SZ)) {
|
|
|
|
//
|
|
// Initialize the commandline to null
|
|
//
|
|
|
|
tszCommandline[0] = '\0';
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
FileTimeToSystemTime(&fileTime, &sysTime);
|
|
#endif
|
|
//
|
|
// Register the callback without the persistent flag
|
|
//
|
|
|
|
//
|
|
// Check whether this is the default...
|
|
//
|
|
|
|
if (callbackCLSID == guidDefaultDevHandler) {
|
|
bIsDefault = TRUE;
|
|
} else {
|
|
bIsDefault = FALSE;
|
|
}
|
|
|
|
//DBG_WRN(("=> Restoring CBs for Device %S, Program named %S",
|
|
// bstrDeviceID ? bstrDeviceID : L"NULL",
|
|
// bstrName));
|
|
DBG_TRC(("CEventNotifier::RestoreDevPersistentCBs, Restoring CBs for Device %S, Program named %S",
|
|
bstrDeviceID ? bstrDeviceID : L"NULL",
|
|
bstrName));
|
|
|
|
#ifdef UNICODE
|
|
|
|
BSTR bstrIcon = SysAllocString(tszBuf);
|
|
if (FAILED(RegisterEventCB(
|
|
bstrDeviceID,
|
|
&eventGUID,
|
|
&callbackCLSID,
|
|
tszCommandline,
|
|
bstrName,
|
|
bstrDescription,
|
|
bstrIcon,
|
|
fileTime,
|
|
bIsDefault))) {
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, RegisterEventCB() failed."));
|
|
}
|
|
SysFreeString(bstrIcon);
|
|
bstrIcon = NULL;
|
|
#else
|
|
if (FAILED(RegisterEventCB(
|
|
bstrDeviceID,
|
|
&eventGUID,
|
|
&callbackCLSID,
|
|
tszCommandline,
|
|
bstrName,
|
|
bstrDescription,
|
|
wszBuf,
|
|
fileTime,
|
|
bIsDefault))) {
|
|
DBG_ERR(("CEventNotifier::RestoreDevPersistentCBs, RegisterEventCB() failed."));
|
|
}
|
|
#endif
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Close the subkey for the event handler CLSID
|
|
//
|
|
|
|
if (hCBCLSID) {
|
|
RegCloseKey(hCBCLSID);
|
|
}
|
|
|
|
if (bstrName) {
|
|
SysFreeString(bstrName);
|
|
}
|
|
|
|
if (bstrDescription) {
|
|
SysFreeString(bstrDescription);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Close the key for the specific event
|
|
//
|
|
|
|
RegCloseKey(hEvent);
|
|
}
|
|
|
|
//
|
|
// Close the event subkey
|
|
//
|
|
|
|
RegCloseKey(hEvents);
|
|
|
|
//
|
|
// Free the deviceID, if one was allocated
|
|
//
|
|
|
|
if (bstrDeviceID) {
|
|
SysFreeString(bstrDeviceID);
|
|
bstrDeviceID = NULL;
|
|
}
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::SavePersistentEventCB
|
|
*
|
|
* Save the persistent Event Callbacks CLSID for the Device ID / Event GUID
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/1/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::SavePersistentEventCB(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
const GUID *pClsid,
|
|
LPCTSTR ptszCommandline,
|
|
BSTR bstrName,
|
|
BSTR bstrDescription,
|
|
BSTR bstrIcon,
|
|
BOOL *pbCreatedKey,
|
|
ULONG *pulNumExistingHandlers,
|
|
BOOL bMakeDefault // = FALSE
|
|
)
|
|
{
|
|
|
|
DBG_FN(CEventNotifier::SavePresistentEventCB);
|
|
HRESULT hr;
|
|
HKEY hEvent, hCBCLSID;
|
|
LONG lRet;
|
|
DWORD dwDisposition;
|
|
WCHAR wszCBClsIDStr[40];
|
|
#ifndef UNICODE
|
|
CHAR szCBClsIDStr[40];
|
|
CHAR szString[MAX_PATH];
|
|
#endif
|
|
HKEY hClsid;
|
|
HKEY hCOMServerCLSID;
|
|
HKEY hLocalServer;
|
|
|
|
//
|
|
// Initialize the resources to NULL
|
|
//
|
|
|
|
hEvent = NULL;
|
|
hCBCLSID = NULL;
|
|
hClsid = NULL;
|
|
hCOMServerCLSID = NULL;
|
|
hLocalServer = NULL;
|
|
|
|
if (pbCreatedKey) {
|
|
*pbCreatedKey = FALSE;
|
|
}
|
|
|
|
do {
|
|
|
|
//
|
|
//
|
|
// Find the event subkey
|
|
//
|
|
|
|
hr = FindEventByGUID(bstrDeviceID, pEventGUID, &hEvent);
|
|
if (hr != S_OK) {
|
|
LPOLESTR wstrGuid = NULL;
|
|
HRESULT hres;
|
|
|
|
hres = StringFromCLSID(*pEventGUID,
|
|
&wstrGuid);
|
|
if (hres == S_OK) {
|
|
DBG_ERR(("CEventNotifier::SavePersistentEventCB() FindEventByGUID() failed, GUID=%S, hr=0x%08X", wstrGuid, hr));
|
|
CoTaskMemFree(wstrGuid);
|
|
} else {
|
|
DBG_ERR(("CEventNotifier::SavePersistentEventCB() FindEventByGUID() failed, hr=0x%08X", hr));
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If asked, let's find out how many handlers already exist for this event
|
|
//
|
|
|
|
if (pulNumExistingHandlers) {
|
|
*pulNumExistingHandlers = 0;
|
|
|
|
lRet = RegQueryInfoKey(hEvent,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
pulNumExistingHandlers,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
//
|
|
// Convert the Event Callback CLSID as a string value
|
|
//
|
|
|
|
StringFromGUID2(*pClsid, wszCBClsIDStr, 40);
|
|
#ifndef UNICODE
|
|
|
|
//
|
|
// Convert the CLSID to ANSI string (including terminating NULL)
|
|
//
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
wszCBClsIDStr,
|
|
-1,
|
|
szCBClsIDStr,
|
|
40,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
|
|
//
|
|
// Open / Create the Event Handler CLSID subkey
|
|
//
|
|
|
|
lRet = RegCreateKeyEx(
|
|
hEvent,
|
|
#ifdef UNICODE
|
|
wszCBClsIDStr,
|
|
#else
|
|
szCBClsIDStr,
|
|
#endif
|
|
0,
|
|
NULL, // Mysterious class string
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE | KEY_READ | KEY_WRITE,
|
|
NULL, // Use default security descriptor
|
|
&hCBCLSID,
|
|
&dwDisposition);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("SavePersistentEventCB() RegCreateKeyEx() failed for CallbackCLSIDs subkey. lRet = %d", lRet));
|
|
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
|
|
if ((dwDisposition == REG_CREATED_NEW_KEY) && (pbCreatedKey)) {
|
|
*pbCreatedKey = TRUE;
|
|
}
|
|
|
|
//
|
|
// Set the event handler description value
|
|
//
|
|
|
|
#ifndef UNICODE
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
bstrName,
|
|
-1,
|
|
szString,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
lRet = RegSetValueEx(
|
|
hCBCLSID,
|
|
NAME_VAL,
|
|
0,
|
|
REG_SZ,
|
|
#ifdef UNICODE
|
|
(const PBYTE)bstrName,
|
|
(wcslen(bstrName) + 1) << 1);
|
|
#else
|
|
(const PBYTE)szString,
|
|
strlen(szString) + 1);
|
|
#endif
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("SavePersistentEventCB() RegSetValueEx() failed for name"));
|
|
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
bstrDescription,
|
|
-1,
|
|
szString,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
lRet = RegSetValueEx(
|
|
hCBCLSID,
|
|
DESC_VAL,
|
|
0,
|
|
REG_SZ,
|
|
#ifdef UNICODE
|
|
(const PBYTE)bstrDescription,
|
|
(wcslen(bstrDescription) + 1) << 1);
|
|
#else
|
|
(const PBYTE)szString,
|
|
strlen(szString) + 1);
|
|
#endif
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("SavePersistentEventCB() RegSetValueEx() failed for description"));
|
|
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the event handler icon value
|
|
//
|
|
|
|
#ifndef UNICODE
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
bstrIcon,
|
|
-1,
|
|
szString,
|
|
MAX_PATH,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
lRet = RegSetValueEx(
|
|
hCBCLSID,
|
|
ICON_VAL,
|
|
0,
|
|
REG_SZ,
|
|
#ifdef UNICODE
|
|
(const PBYTE)bstrIcon,
|
|
(wcslen(bstrIcon) + 1) << 1);
|
|
#else
|
|
(const PBYTE)szString,
|
|
strlen(szString) + 1);
|
|
#endif
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("SavePersistentEventCB() RegSetValueEx() failed for icon"));
|
|
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the command line and fake the program as COM local server
|
|
//
|
|
|
|
if ((ptszCommandline) && (ptszCommandline[0])) {
|
|
|
|
lRet = RegSetValueEx(
|
|
hCBCLSID,
|
|
CMDLINE_VAL,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE)ptszCommandline,
|
|
(_tcslen(ptszCommandline) + 1)*sizeof(TCHAR));
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("SavePersistentEventCB() RegSetValueEx() failed for cmdline"));
|
|
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
/* //Why is this here?
|
|
lRet = RegCreateKeyEx(
|
|
HKEY_CLASSES_ROOT,
|
|
TEXT("CLSID"),
|
|
0,
|
|
NULL, // Mysterious class string
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE | KEY_READ | KEY_WRITE,
|
|
NULL, // Use default security descriptor
|
|
&hClsid,
|
|
&dwDisposition);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
//Remove:
|
|
Trace("SavePersistentEventCB(), could not open CLSID under HKEY_CLASSES_ROOT");
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
|
|
lRet = RegCreateKeyEx(
|
|
hClsid,
|
|
#ifdef UNICODE
|
|
wszCBClsIDStr,
|
|
#else
|
|
szCBClsIDStr,
|
|
#endif
|
|
0,
|
|
NULL, // Mysterious class string
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE | KEY_READ | KEY_WRITE,
|
|
NULL, // Use default security descriptor
|
|
&hCOMServerCLSID,
|
|
&dwDisposition);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
//Remove:
|
|
Trace("SavePersistentEventCB(), could not save persistent event data (%ws) for %ws", wszCBClsIDStr, bstrName);
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
break;
|
|
}
|
|
|
|
lRet = RegCreateKeyEx(
|
|
hCOMServerCLSID,
|
|
TEXT("LocalServer32"),
|
|
0,
|
|
NULL, // Mysterious class string
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE | KEY_READ | KEY_WRITE,
|
|
NULL, // Use default security descriptor
|
|
&hLocalServer,
|
|
&dwDisposition);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
//Remove:
|
|
Trace("SavePersistentEventCB(), could not save persistent event data (LocalServer) for %ws", bstrName);
|
|
break;
|
|
}
|
|
|
|
lRet = RegSetValueEx(
|
|
hLocalServer,
|
|
NULL,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE)ptszCommandline,
|
|
(_tcslen(ptszCommandline) + 1)*sizeof(TCHAR));
|
|
hr = HRESULT_FROM_WIN32(lRet);
|
|
*/
|
|
}
|
|
|
|
//
|
|
// If asked - set as default handler for current device/event pair
|
|
//
|
|
if ( bMakeDefault ) {
|
|
DBG_WRN(("CEventNotifier::SavePersistentEventCB, Writing DEFAULT_HANDLER_VAL"));
|
|
|
|
lRet = ::RegSetValueEx(
|
|
hEvent,
|
|
DEFAULT_HANDLER_VAL,
|
|
0,
|
|
REG_SZ,
|
|
#ifdef UNICODE
|
|
(PBYTE)wszCBClsIDStr,
|
|
(lstrlen(wszCBClsIDStr) + 1)*sizeof(TCHAR));
|
|
#else
|
|
(PBYTE)szCBClsIDStr,
|
|
(lstrlen(szCBClsIDStr) + 1)*sizeof(TCHAR));
|
|
#endif
|
|
|
|
#ifdef UNICODE
|
|
DBG_TRC(("SavePersCB:: Setting default == %S lRet=%d",
|
|
wszCBClsIDStr, lRet));
|
|
#else
|
|
DBG_TRC(("SavePersCB:: Setting default == %s lRet=%d",
|
|
szCBClsIDStr, lRet));
|
|
#endif
|
|
} // endif bMakeDefault
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
if (hCBCLSID) {
|
|
RegCloseKey(hCBCLSID);
|
|
}
|
|
if (hCOMServerCLSID) {
|
|
RegCloseKey(hCOMServerCLSID);
|
|
}
|
|
if (hLocalServer) {
|
|
RegCloseKey(hLocalServer);
|
|
}
|
|
if (hEvent) {
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// Unwind the whole thing
|
|
//
|
|
|
|
#ifdef UNICODE
|
|
RegDeleteKey(hEvent, wszCBClsIDStr);
|
|
#else
|
|
RegDeleteKey(hEvent, szCBClsIDStr);
|
|
#endif
|
|
}
|
|
|
|
RegCloseKey(hEvent);
|
|
}
|
|
if (hClsid) {
|
|
if (FAILED(hr)) {
|
|
|
|
//
|
|
// Unwind the whole thing
|
|
//
|
|
|
|
#ifdef UNICODE
|
|
RegDeleteKey(hEvent, wszCBClsIDStr);
|
|
#else
|
|
RegDeleteKey(hEvent, szCBClsIDStr);
|
|
#endif
|
|
}
|
|
|
|
RegCloseKey(hClsid);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::DelPersistentEventCB(
|
|
*
|
|
* Delete the persistent Event Callback CLSID for the Device ID / Event GUID
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/1/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::DelPersistentEventCB(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
const GUID *pClsid,
|
|
BOOL bUnRegCOMServer)
|
|
{
|
|
DBG_FN(CEventNotifier::DelPersistentEventCB);
|
|
HRESULT hr;
|
|
HKEY hStillImage, hEvent;
|
|
TCHAR tcSubkeyName[8];
|
|
DWORD dwIndex, dwSubkeyNameLen;
|
|
LONG lRet;
|
|
WCHAR wszCBClsIDStr[40];
|
|
#ifndef UNICODE
|
|
CHAR szCBClsIDStr[40];
|
|
#endif
|
|
WCHAR wszDeviceID[50];
|
|
FILETIME fileTime;
|
|
HKEY hClsid;
|
|
|
|
//
|
|
// Find the event subkey
|
|
//
|
|
|
|
hr = FindEventByGUID(bstrDeviceID, pEventGUID, &hEvent);
|
|
|
|
if (hr != S_OK) {
|
|
DBG_ERR(("DelPersistentEventCB() FindEventByGUID() failed, hr=0x%08X", hr));
|
|
return (hr);
|
|
}
|
|
|
|
StringFromGUID2(*pClsid, wszCBClsIDStr, 40);
|
|
#ifndef UNICODE
|
|
|
|
//
|
|
// Convert the CLSID to ANSI string (including terminating NULL)
|
|
//
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
wszCBClsIDStr,
|
|
-1,
|
|
szCBClsIDStr,
|
|
40,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
|
|
//
|
|
// Delete the corresponding event handler CLSID key
|
|
//
|
|
|
|
lRet = RegDeleteKey(
|
|
hEvent,
|
|
#ifdef UNICODE
|
|
wszCBClsIDStr);
|
|
#else
|
|
szCBClsIDStr);
|
|
#endif
|
|
if ((lRet != ERROR_SUCCESS) && (lRet != ERROR_FILE_NOT_FOUND)) {
|
|
DBG_ERR(("DelPersistentEventCB() RegDeleteValue() failed, lRet = 0x%08X", lRet));
|
|
}
|
|
|
|
//
|
|
// Close the registry keys
|
|
//
|
|
|
|
RegCloseKey(hEvent);
|
|
|
|
if (bstrDeviceID) {
|
|
|
|
return (HRESULT_FROM_WIN32(lRet));
|
|
}
|
|
|
|
//
|
|
// Delete the event handler clsid from under devices
|
|
//
|
|
|
|
lRet = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REG_PATH_STILL_IMAGE_CLASS,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hStillImage);
|
|
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
DBG_ERR(("DelPersistentEventCBs : Can not open Image device class key."));
|
|
|
|
hr = (HRESULT_FROM_WIN32(lRet));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Enumerate all the subkey
|
|
//
|
|
|
|
for (dwIndex = 0; ;dwIndex++) {
|
|
|
|
dwSubkeyNameLen = sizeof(tcSubkeyName)/sizeof(TCHAR);
|
|
lRet = RegEnumKeyEx(
|
|
hStillImage,
|
|
dwIndex,
|
|
tcSubkeyName,
|
|
&dwSubkeyNameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&fileTime);
|
|
|
|
if (lRet == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Make up the device ID
|
|
//
|
|
CSimpleStringWide cswDeviceID;
|
|
cswDeviceID = L"{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\\";
|
|
cswDeviceID += tcSubkeyName;
|
|
|
|
//
|
|
// bstrDeviceID is NULL if we're here, so let's use it to hold our
|
|
// actual device id.
|
|
//
|
|
bstrDeviceID = SysAllocString(cswDeviceID.String());
|
|
if (bstrDeviceID)
|
|
{
|
|
hr = FindEventByGUID(bstrDeviceID, pEventGUID, &hEvent);
|
|
SysFreeString(bstrDeviceID);
|
|
bstrDeviceID = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// This event key may not exist under specific device
|
|
//
|
|
|
|
if (hr != S_OK) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Delete the corresponding event handler CLSID key
|
|
//
|
|
|
|
lRet = RegDeleteKey(
|
|
hEvent,
|
|
#ifdef UNICODE
|
|
wszCBClsIDStr);
|
|
#else
|
|
szCBClsIDStr);
|
|
#endif
|
|
if ((lRet != ERROR_SUCCESS) && (lRet != ERROR_FILE_NOT_FOUND)) {
|
|
DBG_ERR(("DelPersistentEventCB() RegDeleteValue() failed, lRet = 0x%08X", lRet));
|
|
}
|
|
|
|
//
|
|
// Close event key and device key
|
|
//
|
|
|
|
RegCloseKey(hEvent);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the image class key
|
|
//
|
|
|
|
RegCloseKey(hStillImage);
|
|
|
|
//
|
|
// If the fake COM server should be unregistered
|
|
//
|
|
|
|
if (bUnRegCOMServer) {
|
|
|
|
lRet = RegOpenKeyEx(
|
|
HKEY_CLASSES_ROOT,
|
|
TEXT("CLSID"),
|
|
0,
|
|
KEY_WRITE,
|
|
&hClsid);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Unable to recover data anyway
|
|
//
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
lRet = RegDeleteKey(
|
|
hClsid,
|
|
szCBClsIDStr);
|
|
#else
|
|
lRet = SHDeleteKey(
|
|
hClsid,
|
|
wszCBClsIDStr);
|
|
#endif
|
|
}
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::FindEventByGUID(
|
|
*
|
|
* Find the registry key for DeviceID / Event GUID pair
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 12/1/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::FindEventByGUID(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
HKEY *phEventKey)
|
|
{
|
|
DBG_FN(CEventNotifier::FindEventByGUID);
|
|
TCHAR tszBuf[96];
|
|
LPTSTR ptszEventName = tszBuf;
|
|
LONG lRet;
|
|
HKEY hEvents, hEvent;
|
|
DWORD dwSubKeyIndex, dwEventNameLen;
|
|
DWORD dwGUIDStrLen, dwValueType, dwDisp;
|
|
FILETIME fileTime;
|
|
GUID eventGUID;
|
|
#ifdef UNICODE
|
|
LPWSTR pwszGUIDStr = tszBuf;
|
|
#else
|
|
WCHAR wszGUIDStr[39]; // {CLSID} + NULL
|
|
LPWSTR pwszGUIDStr = wszGUIDStr;
|
|
#endif
|
|
HRESULT hr = E_FAIL;
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
if (!pEventGUID) {
|
|
DBG_WRN(("CEventNotifier::FindEventByGUID, Event pointer is NULL"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Initialize the return value
|
|
//
|
|
|
|
*phEventKey = NULL;
|
|
|
|
//
|
|
// Prepare the event path
|
|
//
|
|
|
|
if (bstrDeviceID) {
|
|
|
|
//
|
|
// Open the device's event sub-key
|
|
//
|
|
|
|
hEvents = g_pDevMan->GetDeviceHKey(bstrDeviceID,
|
|
EVENTS);
|
|
if (!IsValidHANDLE(hEvents)) {
|
|
DBG_TRC(("CEventNotifier::FindEventByGUID() Couldn't open Events subkey, on device %S", bstrDeviceID));
|
|
return hr;
|
|
} else {
|
|
DBG_TRC(("CEventNotifier::FindEventByGUID() Found Events key on device %S", bstrDeviceID));
|
|
hr = S_OK;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// If there's no Device ID, look under StillImage
|
|
//
|
|
|
|
_tcscpy(tszBuf, REG_PATH_STILL_IMAGE_CONTROL);
|
|
_tcscat(tszBuf, TEXT("\\Events"));
|
|
//
|
|
// Open the device specific or the global Events subkey
|
|
//
|
|
|
|
lRet = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
tszBuf,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hEvents,
|
|
&dwDisp);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
#ifdef UNICODE
|
|
DBG_WRN(("CEventNotifier::FindEventByGUID() Couldn't find Events subkey, named %S", tszBuf));
|
|
#else
|
|
DBG_WRN(("CEventNotifier::FindEventByGUID() Couldn't find Events subkey, named %s", tszBuf));
|
|
#endif
|
|
return (HRESULT_FROM_WIN32(lRet));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Enumerate all the events under Events subkey
|
|
//
|
|
|
|
for (dwSubKeyIndex = 0; ;dwSubKeyIndex++) {
|
|
|
|
dwEventNameLen = sizeof(tszBuf)/sizeof(TCHAR) - 1;
|
|
lRet = RegEnumKeyEx(
|
|
hEvents,
|
|
dwSubKeyIndex,
|
|
ptszEventName,
|
|
&dwEventNameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&fileTime);
|
|
|
|
if (lRet != ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Open the event subkey
|
|
//
|
|
|
|
dwEventNameLen = sizeof(tszBuf);
|
|
lRet = RegOpenKeyEx(
|
|
hEvents,
|
|
ptszEventName,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hEvent);
|
|
if (lRet != ERROR_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Query the GUID value
|
|
//
|
|
|
|
dwGUIDStrLen = sizeof(tszBuf);
|
|
lRet = RegQueryValueEx(
|
|
hEvent,
|
|
TEXT("GUID"),
|
|
NULL,
|
|
&dwValueType,
|
|
(LPBYTE)tszBuf,
|
|
&dwGUIDStrLen);
|
|
if ((lRet != ERROR_SUCCESS) || (dwValueType != REG_SZ)) {
|
|
|
|
if (hEvent) {
|
|
RegCloseKey(hEvent);
|
|
hEvent = NULL;
|
|
}
|
|
//
|
|
// Junk event found, skip to the next
|
|
//
|
|
|
|
#ifdef UNICODE
|
|
DBG_WRN(("CEventNotifier::FindEventByGUID() Junk event %S found", ptszEventName));
|
|
#else
|
|
DBG_WRN("CEventNotifier::FindEventByGUID() Junk event %s found", ptszEventName));
|
|
#endif
|
|
continue;
|
|
}
|
|
#ifndef UNICODE
|
|
|
|
//
|
|
// Convert the CLSID into UNICODE including the terminating NULL
|
|
//
|
|
|
|
mbstowcs(wszGUIDStr, tszBuf, 39);
|
|
#endif
|
|
if (SUCCEEDED(CLSIDFromString(pwszGUIDStr, &eventGUID))) {
|
|
if (eventGUID == *pEventGUID) {
|
|
|
|
RegCloseKey(hEvents);
|
|
|
|
*phEventKey = hEvent;
|
|
return (S_OK);
|
|
}
|
|
}
|
|
|
|
if (hEvent) {
|
|
RegCloseKey(hEvent);
|
|
hEvent = NULL;
|
|
}
|
|
} // End of for (...)
|
|
|
|
DBG_WRN(("CEventNotifier::FindEventByGUID() Event GUID not found in reg key enumeration, creating one..."));
|
|
|
|
if (ActionGuidExists(bstrDeviceID, pEventGUID)) {
|
|
|
|
//
|
|
// Someone forgot to add the EVENT entry to their INF, so create a
|
|
// sub-key with the event GUID as a value
|
|
//
|
|
|
|
#define DEFAULT_EVENT_STR TEXT("Event")
|
|
#define GUID_STR TEXT("GUID")
|
|
TCHAR Name[MAX_PATH];
|
|
HKEY hEventName = NULL;
|
|
WCHAR *wsGUID = NULL;
|
|
USES_CONVERSION;
|
|
|
|
#ifdef UNICODE
|
|
wsprintf(Name, L"%ws%d", DEFAULT_EVENT_STR, dwSubKeyIndex);
|
|
#else
|
|
sprintf(Name, "%s%d", DEFAULT_EVENT_STR, dwSubKeyIndex);
|
|
#endif
|
|
|
|
lRet = RegCreateKeyEx(
|
|
hEvents,
|
|
Name,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hEvent,
|
|
&dwDisp);
|
|
if (lRet == ERROR_SUCCESS) {
|
|
|
|
hr = StringFromCLSID(*pEventGUID, &wsGUID);
|
|
if (hr == S_OK) {
|
|
lRet = RegSetValueEx(
|
|
hEvent,
|
|
GUID_STR,
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*) W2T(wsGUID),
|
|
(lstrlen(W2T(wsGUID)) * sizeof(TCHAR)) + sizeof(TEXT('\0')));
|
|
if (lRet == ERROR_SUCCESS) {
|
|
*phEventKey = hEvent;
|
|
hr = S_OK;
|
|
} else {
|
|
hr = E_FAIL;
|
|
}
|
|
CoTaskMemFree(wsGUID);
|
|
wsGUID = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
//
|
|
// Close Events key
|
|
//
|
|
|
|
RegCloseKey(hEvents);
|
|
if (FAILED(hr)) {
|
|
if (hEvent) {
|
|
RegCloseKey(hEvent);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::CreateEnumEventInfo
|
|
*
|
|
* Build enumerator for specific device's persistent handlers
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 8/8/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::CreateEnumEventInfo(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
IEnumWIA_DEV_CAPS **ppIEnumDevCap)
|
|
{
|
|
DBG_FN(CEventNotifier::CreateEnumEventInfo);
|
|
HRESULT hr;
|
|
EventDestNode *pCurNode;
|
|
EventDestNode *pDefDevHandlerNode;
|
|
ULONG numHandlers, i;
|
|
WIA_EVENT_HANDLER *pEventHandlers, *pHandler;
|
|
CEnumDC *pEnumDC;
|
|
TCHAR tszCommandline[MAX_PATH];
|
|
#ifndef UNICODE
|
|
WCHAR wszBuf[MAX_PATH];
|
|
#endif
|
|
|
|
CWiaCritSect CritSect(&g_semEventNode);
|
|
|
|
ASSERT(bstrDeviceID);
|
|
|
|
//
|
|
// Clear the returned value
|
|
//
|
|
|
|
*ppIEnumDevCap = NULL;
|
|
|
|
//
|
|
// Find the number of handlers
|
|
//
|
|
|
|
GetNumPersistentHandlerAndDefault(
|
|
bstrDeviceID, pEventGUID, &numHandlers, &pDefDevHandlerNode);
|
|
|
|
//
|
|
// Build the enumerator
|
|
//
|
|
|
|
pEnumDC = new CEnumDC;
|
|
if (! pEnumDC) {
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// If there is no handler registered for this event
|
|
//
|
|
|
|
if (! numHandlers) {
|
|
|
|
DBG_TRC(("CreateEnumEventInfo() : No handler registered for this event"));
|
|
|
|
//
|
|
// Trivial case
|
|
//
|
|
|
|
pEnumDC->Initialize(0, (WIA_EVENT_HANDLER*)NULL);
|
|
|
|
return (pEnumDC->QueryInterface(
|
|
IID_IEnumWIA_DEV_CAPS, (void **)ppIEnumDevCap));
|
|
}
|
|
|
|
//
|
|
// Prepare the Event Handler information
|
|
//
|
|
|
|
pEventHandlers =
|
|
(WIA_EVENT_HANDLER *)LocalAlloc(
|
|
LPTR, sizeof(WIA_EVENT_HANDLER)*numHandlers);
|
|
if (! pEventHandlers) {
|
|
|
|
delete pEnumDC;
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
memset(pEventHandlers, 0, sizeof(WIA_EVENT_HANDLER) * numHandlers);
|
|
pHandler = pEventHandlers;
|
|
hr = S_OK;
|
|
for (pCurNode = m_pEventDestNodes, i = 0;
|
|
pCurNode && (i <numHandlers);
|
|
pCurNode = pCurNode->pNext) {
|
|
|
|
//
|
|
// If this handler is a callback interface ponter
|
|
//
|
|
|
|
if (pCurNode->pIEventCB) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this handler can not handle this event
|
|
//
|
|
|
|
if (( pCurNode->iidEventGUID != *pEventGUID) &&
|
|
(pCurNode->iidEventGUID != WIA_EVENT_STI_PROXY) ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this is generic fallback handler
|
|
//
|
|
|
|
if (wcscmp(pCurNode->bstrDeviceID, L"All") != 0) {
|
|
|
|
//
|
|
// If this handler is not for this device
|
|
//
|
|
|
|
if (wcscmp(pCurNode->bstrDeviceID, bstrDeviceID) != 0) {
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this handler was registered for this device as default
|
|
//
|
|
|
|
if (FindEventCBNode(0,bstrDeviceID, pEventGUID, &pCurNode->ClsID)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the information from the current node
|
|
//
|
|
|
|
pHandler->guid = pCurNode->ClsID;
|
|
pHandler->bstrName = SysAllocString(pCurNode->bstrName);
|
|
pHandler->bstrDescription = SysAllocString(pCurNode->bstrDescription);
|
|
pHandler->bstrIcon = SysAllocString(pCurNode->bstrIcon);
|
|
if (pCurNode->tszCommandline[0] != '\0') {
|
|
#ifdef UNICODE
|
|
|
|
PrepareCommandline(
|
|
bstrDeviceID,
|
|
*pEventGUID,
|
|
pCurNode->tszCommandline,
|
|
tszCommandline);
|
|
|
|
pHandler->bstrCommandline =
|
|
SysAllocString(tszCommandline);
|
|
#else
|
|
PrepareCommandline(
|
|
bstrDeviceID,
|
|
*pEventGUID,
|
|
pCurNode->tszCommandline,
|
|
tszCommandline);
|
|
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
tszCommandline,
|
|
-1,
|
|
wszBuf,
|
|
MAX_PATH);
|
|
pHandler->bstrCommandline = SysAllocString(wszBuf);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Check whether the copy is successful
|
|
//
|
|
|
|
if ((! pHandler->bstrName) ||
|
|
(! pHandler->bstrDescription) ||
|
|
(! pHandler->bstrIcon) ||
|
|
((pCurNode->tszCommandline[0] != '\0') && (! pHandler->bstrCommandline))) {
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the flag if this handler is the default one
|
|
//
|
|
|
|
if (pCurNode == pDefDevHandlerNode) {
|
|
pHandler->ulFlags = WIA_IS_DEFAULT_HANDLER;
|
|
}
|
|
|
|
pHandler++;
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Unwind the partial result if error occured
|
|
//
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
for (i = 0, pHandler = pEventHandlers;
|
|
i < numHandlers; i++, pHandler++) {
|
|
|
|
if (pHandler->bstrName) {
|
|
SysFreeString(pHandler->bstrName);
|
|
}
|
|
if (pHandler->bstrDescription) {
|
|
SysFreeString(pHandler->bstrDescription);
|
|
}
|
|
if (pHandler->bstrIcon) {
|
|
SysFreeString(pHandler->bstrIcon);
|
|
}
|
|
}
|
|
|
|
LocalFree(pEventHandlers);
|
|
|
|
delete pEnumDC;
|
|
|
|
return (hr);
|
|
}
|
|
|
|
//
|
|
// Initilization will never fail
|
|
//
|
|
|
|
pEnumDC->Initialize(numHandlers, pEventHandlers);
|
|
|
|
return (pEnumDC->QueryInterface(
|
|
IID_IEnumWIA_DEV_CAPS, (void **)ppIEnumDevCap));
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::GetNumPersistentHandlerAndDefault
|
|
*
|
|
* Find the total number of persistent handlers and the default
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 8/8/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT
|
|
CEventNotifier::GetNumPersistentHandlerAndDefault(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pEventGUID,
|
|
ULONG *pulNumHandlers,
|
|
EventDestNode **ppDefaultNode)
|
|
{
|
|
DBG_FN(CEventNotifier::GetNumPersistentHandlerAndDefault);
|
|
EventDestNode *pCurNode;
|
|
EventDestNode *pDefDevHandlerNode, *pDefGenHandlerNode, *pTempNode;
|
|
|
|
*pulNumHandlers = 0;
|
|
|
|
pDefDevHandlerNode = NULL;
|
|
pDefGenHandlerNode = NULL;
|
|
|
|
for (pCurNode = m_pEventDestNodes; pCurNode; pCurNode = pCurNode->pNext) {
|
|
|
|
//
|
|
// If this handler is a callback interface ponter
|
|
//
|
|
|
|
if (pCurNode->pIEventCB) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If this handler can not handle this event and this handler is not proxy for STI handlers
|
|
//
|
|
|
|
if ( (pCurNode->iidEventGUID != *pEventGUID) &&
|
|
(pCurNode->iidEventGUID != WIA_EVENT_STI_PROXY) ) {
|
|
|
|
//
|
|
// If the pEventGUID is the STI Proxy GUID, then we need to
|
|
// include WIA Global event handlers i.e. those with
|
|
// DeviceIDs of "ALL".
|
|
// Otherwise, just skip it.
|
|
//
|
|
if ((*pEventGUID == WIA_EVENT_STI_PROXY) && (lstrcmpW(bstrDeviceID, L"All") == 0)) {
|
|
(*pulNumHandlers)++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (wcscmp(pCurNode->bstrDeviceID, L"All") != 0) {
|
|
|
|
//
|
|
// If this handler is not for this device
|
|
//
|
|
|
|
if (wcscmp(pCurNode->bstrDeviceID, bstrDeviceID) != 0) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Remember the default handler's node
|
|
//
|
|
|
|
if (! pDefDevHandlerNode) {
|
|
pDefDevHandlerNode = pCurNode;
|
|
} else {
|
|
|
|
/* Original code
|
|
if (CompareFileTime(
|
|
&pCurNode->timeStamp,
|
|
&pDefDevHandlerNode->timeStamp) > 0) {
|
|
|
|
pDefDevHandlerNode = pCurNode;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Timestamps are not valid on Win9x, so use the flag which indicates
|
|
// whether this is the default handler for this device.
|
|
//
|
|
|
|
if (pCurNode->bDeviceDefault) {
|
|
pDefDevHandlerNode = pCurNode;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// If this handler was registered for this device as default, then skip it
|
|
// since we'll hit the device specific registration on our pass anyway.
|
|
//
|
|
|
|
if (FindEventCBNode(0,bstrDeviceID, pEventGUID, &pCurNode->ClsID)) {
|
|
|
|
if (lstrcmpW(bstrDeviceID, L"All") == 0) {
|
|
//
|
|
// If the request is for ALL devices, then we're simply being asked to count the number
|
|
// of global handlers.
|
|
//
|
|
|
|
(*pulNumHandlers)++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If no device specific handler is found, then we want to go with a global one.
|
|
//
|
|
if (! pDefDevHandlerNode) {
|
|
|
|
if (! pDefGenHandlerNode) {
|
|
|
|
//
|
|
// We found our first global Handler
|
|
//
|
|
pDefGenHandlerNode = pCurNode;
|
|
} else {
|
|
|
|
//
|
|
// Since there is more than one global handler, let's use the "Prompt", if we can find it.
|
|
// NOTE: We assume that on registration of more than one global handler, the "Prompt" handler
|
|
// will be registered; so if we don't find it, this would generally indicate a problem during
|
|
// registration. However, in cases of upgrade, this is possible, and entirely normal, so we
|
|
// don't flag it as an error here, since it's not fatal anyway.
|
|
//
|
|
|
|
pTempNode = FindEventCBNode(0, NULL, pEventGUID, &WIA_EVENT_HANDLER_PROMPT);
|
|
if (pTempNode) {
|
|
pDefGenHandlerNode = pTempNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(*pulNumHandlers)++;
|
|
}
|
|
|
|
//
|
|
// If there is no device specific default handler, fall back
|
|
//
|
|
|
|
if (! pDefDevHandlerNode) {
|
|
|
|
*ppDefaultNode = pDefGenHandlerNode;
|
|
} else {
|
|
|
|
*ppDefaultNode = pDefDevHandlerNode;
|
|
}
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* CEventNotifier::StartCallbackProgram
|
|
*
|
|
* Start the callback program in security context of the user who logged on
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Status
|
|
*
|
|
* History:
|
|
*
|
|
* 8/8/1999 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
|
|
HRESULT
|
|
CEventNotifier::StartCallbackProgram(
|
|
EventDestNode *pCBNode,
|
|
PWIAEventThreadInfo pMasterInfo)
|
|
#ifndef UNICODE
|
|
{
|
|
STARTUPINFO startupInfo;
|
|
PROCESS_INFORMATION processInfo;
|
|
int nCmdLineLen;
|
|
BOOL bRet;
|
|
CHAR szCommandline[MAX_PATH];
|
|
|
|
do {
|
|
|
|
//
|
|
// Set up start up info
|
|
//
|
|
|
|
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
|
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
startupInfo.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
//
|
|
// Set up the command line
|
|
// program /StiDevice Image\NNNN /StiEvent {GUID}
|
|
//
|
|
ZeroMemory(szCommandline, sizeof(szCommandline));
|
|
|
|
nCmdLineLen = strlen(pCBNode->tszCommandline);
|
|
if ((MAX_PATH - nCmdLineLen) < (1 + 11 + 10 + 1 + 10 + 38 + 1)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Prepare the command line
|
|
// Nb: It may be important to pick up event GUID from master info block, not from event callback node, because
|
|
// GUID match could've been found against STI proxy event GUID, in which case callback node would contain
|
|
// STI proxy event GUID, not the hardware event GUID, which we need
|
|
//
|
|
|
|
PrepareCommandline(
|
|
pMasterInfo->bstrDeviceID,
|
|
pMasterInfo->eventGUID,
|
|
//pCBNode->iidEventGUID,
|
|
pCBNode->tszCommandline,
|
|
szCommandline);
|
|
|
|
//
|
|
// Create the process in user's context
|
|
//
|
|
|
|
bRet = CreateProcess(
|
|
NULL, // Application name
|
|
szCommandline,
|
|
NULL, // Process attributes
|
|
NULL, // Thread attributes
|
|
FALSE, // Handle inheritance
|
|
0, // Creation flag
|
|
NULL, // Environment
|
|
NULL, // Current directory
|
|
&startupInfo,
|
|
&processInfo);
|
|
|
|
if (! bRet) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Close the handle passed back
|
|
//
|
|
|
|
CloseHandle(processInfo.hProcess);
|
|
CloseHandle(processInfo.hThread);
|
|
|
|
} while (FALSE);
|
|
|
|
return (HRESULT_FROM_WIN32(::GetLastError()));
|
|
}
|
|
#else
|
|
{
|
|
HANDLE hTokenUser;
|
|
STARTUPINFO startupInfo;
|
|
PROCESS_INFORMATION processInfo;
|
|
LPVOID pEnvBlock;
|
|
int nCmdLineLen;
|
|
BOOL bRet;
|
|
WCHAR wszCommandline[MAX_PATH];
|
|
|
|
hTokenUser = NULL;
|
|
pEnvBlock = NULL;
|
|
|
|
do {
|
|
|
|
nCmdLineLen = wcslen(pCBNode->tszCommandline);
|
|
if ((MAX_PATH - nCmdLineLen) < (1 + 11 + 43 + 1 + 10 + 38 + 1)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get interactive user's token
|
|
//
|
|
|
|
hTokenUser = GetUserTokenForConsoleSession();
|
|
|
|
//
|
|
// Maybe nobody is loggon in
|
|
//
|
|
|
|
if (! hTokenUser) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set up start up info
|
|
//
|
|
|
|
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
|
startupInfo.lpDesktop = L"WinSta0\\Default";
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
startupInfo.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
//
|
|
// Create the user's environment block
|
|
//
|
|
|
|
bRet = CreateEnvironmentBlock(
|
|
&pEnvBlock,
|
|
hTokenUser,
|
|
FALSE);
|
|
if (! bRet) {
|
|
DBG_WRN(("CEventNotifier::StartCallbackProgram, CreateEnvironmentBlock failed! GetLastError() = 0x%08X", GetLastError()));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Prepare the command line. Make sure we pass in the EVENT guid, not the STI proxy guid.
|
|
//
|
|
|
|
PrepareCommandline(
|
|
pMasterInfo->bstrDeviceID,
|
|
pMasterInfo->eventGUID,
|
|
pCBNode->tszCommandline,
|
|
wszCommandline);
|
|
|
|
//
|
|
// Create the process in user's context
|
|
//
|
|
|
|
bRet = CreateProcessAsUser(
|
|
hTokenUser,
|
|
NULL, // Application name
|
|
wszCommandline,
|
|
NULL, // Process attributes
|
|
NULL, // Thread attributes
|
|
FALSE, // Handle inheritance
|
|
NORMAL_PRIORITY_CLASS |
|
|
CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP,
|
|
pEnvBlock, // Environment
|
|
NULL, // Current directory
|
|
&startupInfo,
|
|
&processInfo);
|
|
|
|
if (! bRet) {
|
|
DBG_WRN(("CEventNotifier::StartCallbackProgram, CreateProcessAsUser failed! GetLastError() = 0x%08X", GetLastError()));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Close the handle passed back
|
|
//
|
|
|
|
CloseHandle(processInfo.hProcess);
|
|
CloseHandle(processInfo.hThread);
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Garbage collection
|
|
//
|
|
|
|
if (hTokenUser) {
|
|
CloseHandle(hTokenUser);
|
|
}
|
|
if (pEnvBlock) {
|
|
DestroyEnvironmentBlock(pEnvBlock);
|
|
}
|
|
|
|
return (HRESULT_FROM_WIN32(::GetLastError()));
|
|
}
|
|
#endif
|
|
|
|
|
|
/**************************************************************************\
|
|
* QueryInterface
|
|
* AddRef
|
|
* Release
|
|
*
|
|
* CWiaInterfaceEvent IUnknown Interface
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
*
|
|
* 9/2/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT __stdcall
|
|
CWiaInterfaceEvent::QueryInterface(const IID& iid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
if (iid == IID_IUnknown) {
|
|
*ppv = (IUnknown*) this;
|
|
}
|
|
else {
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG __stdcall
|
|
CWiaInterfaceEvent::AddRef()
|
|
{
|
|
InterlockedIncrement((long*) &m_cRef);
|
|
return m_cRef;
|
|
}
|
|
|
|
|
|
ULONG __stdcall
|
|
CWiaInterfaceEvent::Release()
|
|
{
|
|
ULONG ulRefCount = m_cRef - 1;
|
|
|
|
if (InterlockedDecrement((long*) &m_cRef) == 0) {
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return ulRefCount;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* CWiaInterfaceEvent
|
|
* ~CWiaInterfaceEvent
|
|
*
|
|
* CWiaInterfaceEvent Constructor/Destructor Methods.
|
|
*
|
|
* History:
|
|
*
|
|
* 9/2/1998 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
CWiaInterfaceEvent::CWiaInterfaceEvent(PEventDestNode pEventDestNode)
|
|
{
|
|
ASSERT(pEventDestNode != NULL);
|
|
|
|
m_cRef = 0;
|
|
m_pEventDestNode = pEventDestNode;
|
|
}
|
|
|
|
CWiaInterfaceEvent::~CWiaInterfaceEvent()
|
|
{
|
|
//
|
|
// must have exclusive access when changing list
|
|
//
|
|
|
|
CWiaCritSect CritSect(&g_semEventNode);
|
|
|
|
//
|
|
// make sure registered event is removed
|
|
//
|
|
|
|
if (m_pEventDestNode != NULL) {
|
|
g_eventNotifier.UnregisterEventCB(m_pEventDestNode);
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
* QueryInterface
|
|
* AddRef
|
|
* Release
|
|
*
|
|
* CWiaEventContext IUnknown Interface
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
*
|
|
* 1/6/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT __stdcall
|
|
CWiaEventContext::QueryInterface(const IID& iid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
if (iid == IID_IUnknown) {
|
|
*ppv = (IUnknown*) this;
|
|
}
|
|
else {
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG __stdcall
|
|
CWiaEventContext::AddRef()
|
|
{
|
|
InterlockedIncrement((long*) &m_cRef);
|
|
return m_cRef;
|
|
}
|
|
|
|
|
|
ULONG __stdcall
|
|
CWiaEventContext::Release()
|
|
{
|
|
ULONG ulRefCount = m_cRef - 1;
|
|
|
|
if (InterlockedDecrement((long*) &m_cRef) == 0) {
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return ulRefCount;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* CWiaEventContext
|
|
* ~CWiaEventContext
|
|
*
|
|
* CWiaEventContext Constructor/Destructor Methods.
|
|
*
|
|
* History:
|
|
*
|
|
* 1/6/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
CWiaEventContext::CWiaEventContext(
|
|
BSTR bstrDeviceID,
|
|
const GUID *pGuidEvent,
|
|
BSTR bstrFullItemName)
|
|
{
|
|
//
|
|
// Reference count initialized to 1
|
|
//
|
|
|
|
m_cRef = 1;
|
|
|
|
m_ulEventType = 0;
|
|
|
|
m_guidEvent = *pGuidEvent;
|
|
m_bstrFullItemName = bstrFullItemName;
|
|
|
|
m_bstrDeviceId = bstrDeviceID;
|
|
}
|
|
|
|
CWiaEventContext::~CWiaEventContext()
|
|
{
|
|
//
|
|
// bstrFullItemName is free in NotifySTIEvent
|
|
//
|
|
|
|
if (m_bstrDeviceId) {
|
|
SysFreeString(m_bstrDeviceId);
|
|
m_bstrDeviceId = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* WiaDelayedEvent
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
*
|
|
* 1/6/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID WINAPI
|
|
WiaDelayedEvent(
|
|
VOID *pArg)
|
|
{
|
|
CWiaEventContext *pCEventCtx;
|
|
WIANOTIFY wn;
|
|
|
|
//
|
|
// Cast back the context
|
|
//
|
|
|
|
if (! pArg) {
|
|
return;
|
|
}
|
|
|
|
pCEventCtx = (CWiaEventContext *)pArg;
|
|
|
|
//
|
|
// Prepare the notification structure
|
|
//
|
|
|
|
wn.lSize = sizeof(WIANOTIFY);
|
|
wn.bstrDevId = pCEventCtx->m_bstrDeviceId;
|
|
wn.stiNotify.dwSize = sizeof(STINOTIFY);
|
|
wn.stiNotify.guidNotificationCode = pCEventCtx->m_guidEvent;
|
|
|
|
//
|
|
// Fire the event
|
|
//
|
|
|
|
g_eventNotifier.NotifySTIEvent(
|
|
&wn,
|
|
pCEventCtx->m_ulEventType,
|
|
pCEventCtx->m_bstrFullItemName);
|
|
|
|
//
|
|
// Release the initial reference
|
|
//
|
|
|
|
pCEventCtx->Release();
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* wiasQueueEvent
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
*
|
|
* Return Value:
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
*
|
|
* 1/6/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT _stdcall
|
|
wiasQueueEvent(
|
|
BSTR bstrDeviceId,
|
|
const GUID *pEventGUID,
|
|
BSTR bstrFullItemName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BYTE *pRootItemCtx;
|
|
CWiaEventContext *pCEventCtx = NULL;
|
|
BSTR bstrDeviceIdCopy = NULL;
|
|
BSTR bstrFullItemNameCopy = NULL;
|
|
BOOL bRet;
|
|
|
|
//
|
|
// Basic sanity check of the parameters
|
|
//
|
|
|
|
if ((! bstrDeviceId) || (! pEventGUID)) {
|
|
return (E_INVALIDARG);
|
|
}
|
|
|
|
//
|
|
// Poor man's exception handler
|
|
//
|
|
|
|
do {
|
|
|
|
bstrDeviceIdCopy = SysAllocString(bstrDeviceId);
|
|
if (! bstrDeviceIdCopy) {
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
if (bstrFullItemName) {
|
|
bstrFullItemNameCopy = SysAllocString(bstrFullItemName);
|
|
if (! bstrFullItemNameCopy) {
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create an event context
|
|
//
|
|
|
|
pCEventCtx = new CWiaEventContext(
|
|
bstrDeviceIdCopy,
|
|
pEventGUID,
|
|
bstrFullItemNameCopy);
|
|
if (! pCEventCtx) {
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Queue a scheduler item
|
|
//
|
|
|
|
bRet = ScheduleWorkItem(
|
|
WiaDelayedEvent,
|
|
pCEventCtx,
|
|
0,
|
|
NULL);
|
|
if (! bRet) {
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Garbage collection
|
|
//
|
|
|
|
if (hr != S_OK) {
|
|
|
|
if (pCEventCtx) {
|
|
delete pCEventCtx;
|
|
} else {
|
|
|
|
if (bstrDeviceIdCopy) {
|
|
SysFreeString(bstrDeviceIdCopy);
|
|
}
|
|
}
|
|
|
|
if (bstrFullItemNameCopy) {
|
|
SysFreeString(bstrFullItemNameCopy);
|
|
}
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
void
|
|
PrepareCommandline(
|
|
BSTR bstrDeviceID,
|
|
const GUID &guidEvent,
|
|
LPCWSTR pwszOrigCmdline,
|
|
LPWSTR pwszResCmdline)
|
|
{
|
|
WCHAR wszGUIDStr[40];
|
|
WCHAR wszCommandline[MAX_PATH];
|
|
WCHAR *pPercentSign;
|
|
WCHAR *pTest = NULL;
|
|
|
|
//
|
|
// Fix up the commandline. First check that it has at least two %
|
|
//
|
|
|
|
pTest = wcschr(pwszOrigCmdline, '%');
|
|
if (pTest) {
|
|
pTest = wcschr(pTest + 1, '%');
|
|
}
|
|
|
|
if (!pTest) {
|
|
_snwprintf(
|
|
wszCommandline,
|
|
sizeof(wszCommandline) / sizeof( wszCommandline[0] ),
|
|
L"%s /StiDevice:%%1 /StiEvent:%%2",
|
|
pwszOrigCmdline);
|
|
} else {
|
|
wcsncpy(wszCommandline, pwszOrigCmdline, sizeof(wszCommandline) / sizeof( wszCommandline[0] ));
|
|
}
|
|
|
|
//
|
|
// enforce null termination
|
|
//
|
|
|
|
wszCommandline[ (sizeof(wszCommandline) / sizeof(wszCommandline[0])) - 1 ] = 0;
|
|
|
|
//
|
|
// Change the number {1|2} into s
|
|
//
|
|
|
|
pPercentSign = wcschr(wszCommandline, L'%');
|
|
*(pPercentSign + 1) = L's';
|
|
pPercentSign = wcschr(pPercentSign + 1, L'%');
|
|
*(pPercentSign + 1) = L's';
|
|
|
|
//
|
|
// Convert the GUID into string
|
|
//
|
|
|
|
StringFromGUID2(guidEvent, wszGUIDStr, 40);
|
|
|
|
//
|
|
// Final comand line
|
|
//
|
|
|
|
swprintf(pwszResCmdline, wszCommandline, bstrDeviceID, wszGUIDStr);
|
|
}
|
|
|
|
#else
|
|
void
|
|
PrepareCommandline(
|
|
BSTR bstrDeviceID,
|
|
const GUID &guidEvent,
|
|
LPCSTR pszOrigCmdline,
|
|
LPSTR pszResCmdline)
|
|
{
|
|
CHAR szCommandline[MAX_PATH];
|
|
CHAR *pPercentSign;
|
|
WCHAR wszGUIDStr[40];
|
|
char szGUIDStr[40];
|
|
char szDeviceID[12]; // Image\NNNN
|
|
|
|
//
|
|
// Fix up the commandline
|
|
//
|
|
DBG_WRN(("PrepareCommandLine"));
|
|
|
|
if (! strchr(pszOrigCmdline, '%')) {
|
|
_snprintf(
|
|
szCommandline,
|
|
sizeof(szCommandline) / sizeof( szCommandline[0] ),
|
|
"%s /StiDevice:%%1 /StiEvent:%%2",
|
|
pszOrigCmdline);
|
|
} else {
|
|
strncpy(szCommandline, pszOrigCmdline, sizeof(wszCommandline) / sizeof( wszCommandline[0] ));
|
|
}
|
|
|
|
//
|
|
// enforce null termination
|
|
//
|
|
|
|
szCommandline[ (sizeof(szCommandline) / sizeof(szCommandline[0])) - 1 ] = 0;
|
|
|
|
|
|
//
|
|
// Change the number {1|2} into s
|
|
//
|
|
|
|
pPercentSign = strchr(szCommandline, '%');
|
|
*(pPercentSign + 1) = 's';
|
|
pPercentSign = strchr(pPercentSign + 1, '%');
|
|
*(pPercentSign + 1) = 's';
|
|
|
|
//
|
|
// Convert the GUID into string
|
|
//
|
|
|
|
StringFromGUID2(guidEvent, wszGUIDStr, 40);
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
bstrDeviceID,
|
|
-1,
|
|
szDeviceID,
|
|
sizeof(szDeviceID),
|
|
NULL,
|
|
NULL);
|
|
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
wszGUIDStr,
|
|
-1,
|
|
szGUIDStr,
|
|
sizeof(szGUIDStr),
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Final result
|
|
//
|
|
|
|
sprintf(pszResCmdline, szCommandline, szDeviceID, szGUIDStr);
|
|
}
|
|
#endif
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* ActionGuidExists
|
|
*
|
|
* Returns true if the specified event GUID is reported as an ACTION event
|
|
* by the driver
|
|
*
|
|
* Arguments:
|
|
*
|
|
* bstrDevId - identifies the device we're interested in
|
|
* pEventGUID - identifies the event we're looking for
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE - The driver reports this event as an ACTION event
|
|
* FALSE - This is not an ACTION event for this device
|
|
*
|
|
* History:
|
|
*
|
|
* 03/01/2000 Original Version
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL ActionGuidExists(
|
|
BSTR bstrDevId,
|
|
const GUID *pEventGUID)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
IWiaDevMgr *pIDevMgr = NULL;
|
|
IWiaItem *pIItem = NULL;
|
|
IEnumWIA_DEV_CAPS *pIEnum = NULL;
|
|
WIA_DEV_CAP DevCap;
|
|
ULONG ulVal;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
//
|
|
// Get the device manager and create an item interface for that device
|
|
//
|
|
|
|
hr = CWiaDevMgr::CreateInstance(IID_IWiaDevMgr, (VOID**) &pIDevMgr);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pIDevMgr->CreateDevice(bstrDevId, &pIItem);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// Get an enumerator for the events
|
|
//
|
|
|
|
hr = pIItem->EnumDeviceCapabilities(WIA_DEVICE_EVENTS, &pIEnum);
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
//
|
|
// Iterate through events to check whether we have a
|
|
// match
|
|
//
|
|
|
|
while(pIEnum->Next(1, &DevCap, &ulVal) == S_OK) {
|
|
|
|
if (DevCap.guid == *pEventGUID) {
|
|
|
|
//
|
|
// Check whether it's an action event, and mark
|
|
// our return to TRUE if it is.
|
|
//
|
|
|
|
if (DevCap.ulFlags & WIA_ACTION_EVENT) {
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pIEnum->Release();
|
|
} else {
|
|
DBG_WRN(("ActionGuidExists() : Failed to enumerate (0x%X)", hr));
|
|
}
|
|
pIItem->Release();
|
|
} else {
|
|
DBG_WRN(("ActionGuidExists() : Failed to create device (0x%X)", hr));
|
|
}
|
|
pIDevMgr->Release();
|
|
} else {
|
|
DBG_WRN(("ActionGuidExists() : Failed to create device manager (0x%X)", hr));
|
|
}
|
|
|
|
return bRet;
|
|
}
|