/******************************************************************************* * * (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 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; }