//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1998. // // File: N C Q U E U E . C P P // // Contents: NetCfg queued installer actions // // Notes: // // Author: billbe 19 Aug 1998 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "nceh.h" #include "ncmisc.h" #include "ncnetcfg.h" #include "ncqueue.h" #include "ncreg.h" #include "ncsetup.h" #include "ncui.h" #include "wizentry.h" const WCHAR c_szRegKeyNcQueue[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\NcQueue"; const DWORD c_cchQueueValueNameLen = 9; const DWORD c_cbQueueValueNameLen = c_cchQueueValueNameLen * sizeof(WCHAR); enum RO_ACTION { RO_ADD, RO_CLEAR, }; extern const WCHAR c_szRegValueNetCfgInstanceId[]; CRITICAL_SECTION g_csRefCount; DWORD g_dwRefCount = 0; HANDLE g_hLastThreadExitEvent; DWORD WINAPI InstallQueueWorkItem(PVOID pvContext); inline VOID IncrementRefCount() { EnterCriticalSection(&g_csRefCount); // If 0 is the current count and we have an event to reset... if (!g_dwRefCount && g_hLastThreadExitEvent) { ResetEvent(g_hLastThreadExitEvent); } ++g_dwRefCount; LeaveCriticalSection(&g_csRefCount); } inline VOID DecrementRefCount() { EnterCriticalSection(&g_csRefCount); --g_dwRefCount; // If the count is 0 and we have an event to signal... if (!g_dwRefCount && g_hLastThreadExitEvent) { SetEvent(g_hLastThreadExitEvent); } LeaveCriticalSection(&g_csRefCount); } //+--------------------------------------------------------------------------- // // Function: WaitForInstallQueueToExit // // Purpose: This function waits until the last thread Called to continue processing the queue after processing was // stopped due to a shutdown (of teh Netman service or system) // // Arguments: // none // // Returns: nothing // // Author: billbe 8 Sep 1998 // // Notes: // VOID WaitForInstallQueueToExit() { // Wait on the event if it was successfully created. if (g_hLastThreadExitEvent) { TraceTag(ttidInstallQueue, "Waiting on LastThreadExitEvent"); (VOID) WaitForSingleObject(g_hLastThreadExitEvent, INFINITE); TraceTag(ttidInstallQueue, "Event signaled"); } else { // If the event was not created, fall back to simply looping // on the ref count while (g_dwRefCount); } } //+--------------------------------------------------------------------------- // // Function: ProcessQueue // // Purpose: Called to continue processing the queue after processing was // stopped due to a shutdown (of the Netman service or system) // // Arguments: // none // // Returns: nothing // // Author: billbe 8 Sep 1998 // // Notes: // EXTERN_C VOID WINAPI ProcessQueue() { HRESULT hr; INetInstallQueue* pniq; BOOL fInitCom = TRUE; TraceTag(ttidInstallQueue, "ProcessQueue called"); hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); if (RPC_E_CHANGED_MODE == hr) { hr = S_OK; fInitCom = FALSE; } if (SUCCEEDED(hr)) { // Create the install queue object and get the install queue interface hr = CoCreateInstance(CLSID_InstallQueue, NULL, CLSCTX_SERVER | CLSCTX_NO_CODE_DOWNLOAD, IID_INetInstallQueue, reinterpret_cast(&pniq)); if (S_OK == hr) { // Process whatever was left in the queue // pniq->ProcessItems(); pniq->Release(); } if (fInitCom) { CoUninitialize(); } } } //+--------------------------------------------------------------------------- // // Function: RunOnceAddOrClearItem // // Purpose: Adds or clears an entry to/from the RunOnce registry key. // // Arguments: // pszValueName [in] The value name of the run once item // pszItemToRun [in] The actual command to "Run Once" // eAction [in] RO_ADD to add the item, RO_CLEAR to clear the item. // // Returns: nothing // // Author: billbe 8 Sep 1998 // // Notes: // VOID RunOnceAddOrClearItem ( IN PCWSTR pszValueName, IN PCWSTR pszItemToRun, IN RO_ACTION eAction) { static const WCHAR c_szRegKeyRunOnce[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; HRESULT hr; HKEY hkey; // Open the RunOnce key hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE, c_szRegKeyRunOnce, KEY_WRITE, &hkey); if (S_OK == hr) { if (RO_ADD == eAction) { // Set the command line to run when the user logs in next. (VOID) HrRegSetSz (hkey, pszValueName, pszItemToRun); TraceTag(ttidInstallQueue, "Added %S RunOnce entry", pszValueName); } else if (RO_CLEAR == eAction) { // Remove the command line. (VOID) HrRegDeleteValue (hkey, pszValueName); TraceTag(ttidInstallQueue, "Cleared %S RunOnce entry", pszValueName); } RegCloseKey(hkey); } } //+--------------------------------------------------------------------------- // // Member: CInstallQueue::CInstallQueue // // Purpose: CInstall queue constructor // // Arguments: // (none) // // Returns: nothing // // Author: BillBe 10 Sep 1998 // // Notes: // CInstallQueue::CInstallQueue() : m_dwNextAvailableIndex(0), m_hkey(NULL), m_nCurrentIndex(-1), m_cItems(0), m_aszItems(NULL), m_cItemsToDelete(0), m_aszItemsToDelete(NULL), m_fQueueIsOpen(FALSE) { TraceTag(ttidInstallQueue, "Installer queue processor being created"); InitializeCriticalSection (&m_csReadLock); InitializeCriticalSection (&m_csWriteLock); InitializeCriticalSection (&g_csRefCount); // Create an event that we will use to signal to interested parties // that we are done. This is used by netman to wait for our threads // to exit before destroying this object g_hLastThreadExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // If the event could not be created, we can still go on, we just won't // use the event to signal our exit. if (!g_hLastThreadExitEvent) { TraceTag(ttidInstallQueue, "Error creating last thread exit " "event %d", GetLastError()); } // Set the next available queue index so insertions won't overlap SetNextAvailableIndex(); } //+--------------------------------------------------------------------------- // // Member: CInstallQueue::FinalRelease // // Purpose: COM destructor // // Arguments: // (none) // // Returns: nothing // // Author: BillBe 10 Sep 1998 // // Notes: // VOID CInstallQueue::FinalRelease () { DeleteCriticalSection (&m_csWriteLock); DeleteCriticalSection (&m_csReadLock); DeleteCriticalSection (&g_csRefCount); } // INetInstallQueue //+--------------------------------------------------------------------------- // // Function: CInstallQueue::AddItem // // Purpose: Add item to the queue // // Arguments: // pGuid [in] The class guid of the device that was // modified (installed. updated, or removed) // pszDeviceInstanceId [in] The instance id of the device // pszInfId [in] The inf id of the device // dwCharacter [in] The device's characteristics // eType [in] The install type (event) - indicates // whether the device was installed, updated, // or removed // // Returns: HRESULT. S_OK if successful, an error code otherwise. // // Author: billbe 25 Aug 1998 // // Notes: If the device was removed, the device instance id will be the // instance guid of the device. If the device was installed // or updated then the id will be the PnP instance id // STDMETHODIMP CInstallQueue::AddItem ( const NIQ_INFO* pInfo) { Assert(pInfo); Assert(pInfo->pszPnpId); Assert(pInfo->pszInfId); if (!pInfo) { return E_POINTER; } if (!pInfo->pszPnpId) { return E_POINTER; } if (!pInfo->pszInfId) { return E_POINTER; } // Increment our refcount since we will be queueing a thread IncrementRefCount(); // Add the item to the queue HRESULT hr = HrAddItem (pInfo); if (S_OK == hr) { // Start processing the queue on another thread hr = HrQueueWorkItem(); } TraceHr (ttidError, FAL, hr, FALSE, "CInstallQueue::AddItem"); return hr; } // CInstallQueue // //+--------------------------------------------------------------------------- // // Function: CInstallQueue::HrQueueWorkItem // // Purpose: Start processing the queue on another thread // // Arguments: // (none) // // Returns: HRESULT. S_OK if successful, an error code otherwise. // // Author: billbe 25 Aug 1998 // // Notes: // HRESULT CInstallQueue::HrQueueWorkItem() { HRESULT hr = S_OK; // Add ref our object since we will need it independent of whoever // called us AddRef(); // Queue a work item thread if (!QueueUserWorkItem(InstallQueueWorkItem, this, WT_EXECUTEDEFAULT)) { hr = HrFromLastWin32Error(); Release(); // The thread wasn't queued so reduce the ref count DecrementRefCount(); } return hr; } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::SetNextAvailableIndex // // Purpose: Sets the member variable m_dwNextAvailableIndex to the next // available queue position (registry valuename) // // Arguments: // none // // Returns: nothing // // Author: billbe 25 Aug 1998 // // Notes: // VOID CInstallQueue::SetNextAvailableIndex() { TraceTag(ttidInstallQueue, "Setting Next Available index"); EnterCriticalSection(&m_csWriteLock); DWORD dwTempCount; HKEY hkey; // Open the NcQueue registry key HRESULT hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyNcQueue, KEY_QUERY_VALUE, &hkey); if (S_OK == hr) { WCHAR szValueName[c_cchQueueValueNameLen]; DWORD cbValueName; PWSTR pszStopString; DWORD dwIndex = 0; DWORD dwType; do { cbValueName = c_cbQueueValueNameLen; // Enumerate each value name hr = HrRegEnumValue(hkey, dwIndex, szValueName, &cbValueName, &dwType, NULL, NULL); if (S_OK == hr) { // Convert the value name to a number dwTempCount = wcstoul(szValueName, &pszStopString, c_nBase16); // If the number is greater than our current count // adjust the current count if (dwTempCount >= m_dwNextAvailableIndex) { m_dwNextAvailableIndex = dwTempCount + 1; } } ++dwIndex; } while (S_OK == hr); if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } RegCloseKey(hkey); } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { hr = S_OK; } TraceTag(ttidInstallQueue, "Next Available index set %d", m_dwNextAvailableIndex); LeaveCriticalSection(&m_csWriteLock); } // Compare strings given pointers to PCWSTRs inline int __cdecl iCompare(const void* ppszArg1, const void* ppszArg2) { return lstrcmpW(*((PCWSTR*)(void*)ppszArg1), *((PCWSTR*)(void*)ppszArg2)); } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::PncqiCreateItem // // Purpose: Creates a queue item // // Arguments: // pguidClass [in] The class guid of a device. // pszDeviceInstanceId [in] The device id of the device. // a pnp instance id if the device is being // added or updated, a netcfg instance guid // if it is being removed. // pszInfId [in] The device's inf id. // dwCharacter [in] The device's characteristics. // eType [in] The notification for the item. Whether // the device was installed, removed, // or reinstalled. // // Returns: NCQUEUE_ITEM. The newly created item. // // Author: billbe 25 Aug 1998 // // Notes: // NCQUEUE_ITEM* CInstallQueue::PncqiCreateItem( const NIQ_INFO* pInfo) { Assert(pInfo); Assert(pInfo->pszPnpId); Assert(pInfo->pszInfId); // The size of the item is the size of the structure plus the size of // the device id we are appending to the structure DWORD cbPnpId = CbOfSzAndTerm (pInfo->pszPnpId); DWORD cbInfId = CbOfSzAndTerm (pInfo->pszInfId); DWORD cbSize = sizeof(NCQUEUE_ITEM) + cbPnpId + cbInfId; NCQUEUE_ITEM* pncqi = (NCQUEUE_ITEM*)MemAlloc(cbSize); if (pncqi) { pncqi->cbSize = sizeof(NCQUEUE_ITEM); pncqi->eType = pInfo->eType; pncqi->dwCharacter = pInfo->dwCharacter; pncqi->dwDeipFlags = pInfo->dwDeipFlags; pncqi->cchPnpId = wcslen(pInfo->pszPnpId); pncqi->cchInfId = wcslen(pInfo->pszInfId); pncqi->ClassGuid = pInfo->ClassGuid; pncqi->InstanceGuid = pInfo->InstanceGuid; CopyMemory((BYTE*)pncqi + pncqi->cbSize, pInfo->pszPnpId, cbPnpId); CopyMemory((BYTE*)pncqi + pncqi->cbSize + cbPnpId, pInfo->pszInfId, cbInfId); } return pncqi; } //+--------------------------------------------------------------------------- // // Function: HrAddItem // // Purpose: Worker function that adds an item to the queue // // Arguments: // pguidClass [in] The class guid of a device // pszwDeviceInstanceId [in] The device id of the device // a pnp instance id if the device is being // added or updated, a netcfg instance guid // if it is being removed // eType [in] The notification for the item. Whether the device // was installed, removed, or reinstalled. // // Returns: HRESULT. S_OK if successful, an error code otherwise. // // Author: billbe 25 Aug 1998 // // Notes: // HRESULT CInstallQueue::HrAddItem( const NIQ_INFO* pInfo) { Assert(pInfo->pszPnpId); Assert(pInfo->pszInfId); EnterCriticalSection(&m_csWriteLock); // Create the structure to be stored in the registry NCQUEUE_ITEM* pncqi = PncqiCreateItem(pInfo); // Open the NcQueue registry key // HKEY hkey; HRESULT hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyNcQueue, 0, KEY_READ_WRITE, NULL, &hkey, NULL); if (S_OK == hr && pncqi) { // Store the queue item under the next available valuename // WCHAR szValue[c_cchQueueValueNameLen]; wsprintfW(szValue, L"%.8X", m_dwNextAvailableIndex); hr = HrRegSetValueEx(hkey, szValue, REG_BINARY, (BYTE*)pncqi, DwSizeOfItem(pncqi)); if (S_OK == hr) { // Update the global count string ++m_dwNextAvailableIndex; } RegCloseKey(hkey); } MemFree(pncqi); LeaveCriticalSection(&m_csWriteLock); TraceError("CInstallQueue::HrAddItem", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::DeleteMarkedItems // // Purpose: Deletes, from the registry, any values that have been // marked for delete. // // Arguments: // none // // Returns: nothing // // Author: billbe 25 Aug 1998 // // Notes: // VOID CInstallQueue::DeleteMarkedItems() { Assert(m_hkey); // If we have items to delete... if (m_cItemsToDelete) { Assert(m_aszItemsToDelete); // Remove each one from the registry // for (DWORD dw = 0; dw < m_cItemsToDelete; ++dw) { RegDeleteValue(m_hkey, m_aszItemsToDelete[dw]); } } // Free the array and reset the pointer and counter // MemFree(m_aszItemsToDelete); m_aszItemsToDelete = NULL; m_cItemsToDelete = 0; } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::HrRefresh // // Purpose: Refreshs our snapshot of the queue. // // Arguments: // none // // Returns: HRESULT. S_OK if successful and the queue has items, // S_FALSE if the queue is empty, // an error code otherwise. /// // Author: billbe 25 Aug 1998 // // Notes: // HRESULT CInstallQueue::HrRefresh() { Assert(m_hkey); // We don't want items being added to the queue while we are // refreshing our snapshot, so we use a critical section to keep // things // EnterCriticalSection(&m_csWriteLock); // Do some housecleaning before the refresh // DeleteMarkedItems(); FreeAszItems(); // Retrieve the number of items in the queue HRESULT hr = HrRegQueryInfoKey(m_hkey, NULL, NULL, NULL, NULL, NULL, &m_cItems, NULL, NULL, NULL, NULL); if (S_OK == hr) { Assert(0 <= (INT) m_cItems); // If the queue is not empty... if (0 < m_cItems) { DWORD cbValueLen; // Allocate the array of pointers to strings for the items. // Also, allocate the same quantity of pointers to hold // items we will delete from the queue DWORD cbArraySize = m_cItems * sizeof(PWSTR*); m_aszItems = reinterpret_cast(MemAlloc(cbArraySize)); if (m_aszItems) { m_aszItemsToDelete = reinterpret_cast(MemAlloc(cbArraySize)); if (m_aszItemsToDelete) { // Store all the value names // We will need to sort them so we can process each device in the // correct order // DWORD dwType; for (DWORD dw = 0; dw < m_cItems; ++dw) { m_aszItems[dw] = reinterpret_cast(MemAlloc(c_cbQueueValueNameLen)); if (m_aszItems[dw]) { cbValueLen = c_cbQueueValueNameLen; (void) HrRegEnumValue(m_hkey, dw, m_aszItems[dw], &cbValueLen, &dwType, NULL, NULL); } else { hr = E_OUTOFMEMORY; } } // Sort the value names in ascending order. The value names // are string versions of numbers padded to the left with zeroes // e.g. 00000001 qsort(m_aszItems, m_cItems, sizeof(PWSTR), iCompare); } else { MemFree(m_aszItems); hr = E_OUTOFMEMORY; } } else { hr = E_OUTOFMEMORY; } } else { // no items in the queue hr = S_FALSE; // The next items entered should start with valuename 00000000 m_dwNextAvailableIndex = 0; } } else { // Refresh not possible so invalidate the key RegCloseKey(m_hkey); m_hkey = NULL; } // Reset Queue Index to just before the first element since // retrieval is always done on the next element m_nCurrentIndex = -1; // Items can now be added to the queue LeaveCriticalSection(&m_csWriteLock); TraceError("CInstallQueue::HrRefresh", (S_FALSE == hr) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::HrOpen // // Purpose: Opens the netcfg installer queue // // Arguments: // None // // Returns: HRESULT. S_OK if successful and the queue has items, // S_FALSE if the queue is empty, // an error code otherwise. // // Author: billbe 25 Aug 1998 // // Notes: When the queue is opened, it is a snapshot of the current queue // state. i.e. items could be added after the fact. To refresh the // snapshot, use RefreshQueue. // HRESULT CInstallQueue::HrOpen() { HRESULT hr = S_OK; // We don't want any other thread to process the queue since we will // continue to retrieve new items that are added while we are // processing the initial set EnterCriticalSection(&m_csReadLock); AssertSz(!m_hkey, "Reopening NcQueue without closing first!"); // We might have been waiting for a bit. Make sure the system // is not shutting down before continuing // if (SERVICE_RUNNING == _Module.DwServiceStatus ()) { hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyNcQueue, KEY_ALL_ACCESS, &m_hkey); if (S_OK == hr) { // Get a current snapshot of what's in the queue // by refreshing hr = HrRefresh(); if (SUCCEEDED(hr)) { // The queue is officially open m_fQueueIsOpen = TRUE; } } } else { TraceTag(ttidInstallQueue, "HrOpen::System is shutting down"); hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); } TraceError("CInstallQueue::HrOpen", ((S_FALSE == hr) || (HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS)) == hr) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::Close // // Purpose: Closes the netcfg installer queue // // Arguments: // None // // Returns: nothing // // Author: billbe 25 Aug 1998 // // Notes: // VOID CInstallQueue::Close() { if (m_fQueueIsOpen) { // Housecleaning // // Delete anything so marked DeleteMarkedItems(); // Free up the list of value names (aka queue items) FreeAszItems(); RegSafeCloseKey(m_hkey); m_hkey = NULL; // Queue is now closed m_fQueueIsOpen = FALSE; } // Other threads may have a chance at the queue now LeaveCriticalSection(&m_csReadLock); } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::MarkCurrentItemForDeletion // // Purpose: Marks the current item for deletion from the registry // when the queue is refreshed or closed // // Arguments: // None // // Returns: nothing // // Author: billbe 25 Aug 1998 // // Notes: // VOID CInstallQueue::MarkCurrentItemForDeletion() { AssertSz(FIsQueueIndexInRange(), "Queue index out of range"); if (FIsQueueIndexInRange()) { // The number of items to delete should never exceed the number // of queue items in our snapshot if (m_cItemsToDelete < m_cItems) { // Just store a pointer, in m_aszItemsToDelete, to the value name // pointed to by m_aszItems // m_aszItemsToDelete[m_cItemsToDelete] = m_aszItems[m_nCurrentIndex]; ++m_cItemsToDelete; } else { TraceTag(ttidError, "Too many items marked for deletion"); } } } //+--------------------------------------------------------------------------- // // Function: CInstallQueue::HrGetNextItem // // Purpose: Get's the next item in the queue // // Arguments: // None // // Returns: HRESULT. S_OK is successful, // ERROR_NO_MORE_ITEMS (hresult version), if there are no // more items in the queue. An error code otherwise. // // Author: billbe 25 Aug 1998 // // Notes: // HRESULT CInstallQueue::HrGetNextItem(NCQUEUE_ITEM** ppncqi) { Assert(ppncqi); HRESULT hr; // Increment index to the next value ++m_nCurrentIndex; // If we haven't gone past the end of the queue... if (FIsQueueIndexInRange()) { // assign convenience pointer PCWSTR pszItem = m_aszItems[m_nCurrentIndex]; DWORD cbData; // Get the next queue item from the registry // hr = HrRegQueryValueEx(m_hkey, pszItem, NULL, NULL, &cbData); if (S_OK == hr) { *ppncqi = (NCQUEUE_ITEM*)MemAlloc(cbData); if( *ppncqi ) { DWORD dwType; hr = HrRegQueryValueEx(m_hkey, pszItem, &dwType, (BYTE*)(*ppncqi), &cbData); if (S_OK == hr) { Assert(REG_BINARY == dwType); Assert((*ppncqi)->cchPnpId == (DWORD) (wcslen((PWSTR)((BYTE*)(*ppncqi) + (*ppncqi)->cbSize)))); Assert((*ppncqi)->cchInfId == (DWORD) (wcslen((PWSTR)((BYTE*)(*ppncqi) + (*ppncqi)->cbSize + ((*ppncqi)->cchPnpId + 1) * sizeof(WCHAR))))); // change union variable from the count of characters // to the actual string pointer SetItemStringPtrs(*ppncqi); } else { MemFree(*ppncqi); } } else { hr = E_OUTOFMEMORY; } } } else { hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); } TraceError("CInstallQueue::HrGetNextItem", (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // // Function: EnumerateQueueItemsAndDoNotifications // // Purpose: Enumerates each item in the queue and notifies INetCfg // of the modification (event) // // Arguments: // pINetCfg [in] INetCfg interface // pinq [in] The INetInstallQueue interface // hdi [in] See Device Installer Api for more info // pfReboot [out] TRUE if INetCfg requested a reboot, // FALSE otherwise // // Returns: HRESULT. S_OK is successful, an error code otherwise // // Author: billbe 8 Sep 1998 // // Notes: // BOOL EnumerateQueueItemsAndDoNotifications( INetCfg* pINetCfg, INetCfgInternalSetup* pInternalSetup, CInstallQueue* pniq, HDEVINFO hdi, BOOL* pfReboot) { Assert(pINetCfg); Assert(pniq); Assert(IsValidHandle(hdi)); Assert(pfReboot); NCQUEUE_ITEM* pncqi; SP_DEVINFO_DATA deid; HRESULT hr; BOOL fStatusOk = TRUE; // Go through each item in the queue and add to INetCfg // while (S_OK == (hr = pniq->HrGetNextItem(&pncqi))) { // If we are not shutting down... if (SERVICE_RUNNING == _Module.DwServiceStatus ()) { if (NCI_INSTALL == pncqi->eType) { NIQ_INFO Info; ZeroMemory(&Info, sizeof(Info)); Info.ClassGuid = pncqi->ClassGuid; Info.InstanceGuid = pncqi->InstanceGuid; Info.dwCharacter = pncqi->dwCharacter; Info.dwDeipFlags = pncqi->dwDeipFlags; Info.pszPnpId = pncqi->pszPnpId; Info.pszInfId = pncqi->pszInfId; NC_TRY { // Notify INetCfg hr = HrDiAddComponentToINetCfg( pINetCfg, pInternalSetup, &Info); } NC_CATCH_ALL { hr = E_UNEXPECTED; } } else if (NCI_UPDATE == pncqi->eType) { pInternalSetup->EnumeratedComponentUpdated(pncqi->pszPnpId); } else if (NCI_REMOVE == pncqi->eType) { hr = pInternalSetup->EnumeratedComponentRemoved ( pncqi->pszPnpId); } if (SUCCEEDED(hr)) { // Store the reboot result if (NETCFG_S_REBOOT == hr) { *pfReboot = TRUE; } TraceTag(ttidInstallQueue, "Deleting item"); pniq->MarkCurrentItemForDeletion(); } else { if (NETCFG_E_NEED_REBOOT == hr) { // Stop processing the queue since INetCfg will // refuse updates. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); fStatusOk = FALSE; } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { // INetCfg couldn't find the adapter. Maybe someone // removed it before we could notify INetCfg. // if (NCI_REMOVE != pncqi->eType) { HDEVINFO hdi; SP_DEVINFO_DATA deid; // Double check if the device is there. // If it is, we need to remove it since INetCfg // refuses to acknowledge its presence. // hr = HrSetupDiCreateDeviceInfoList (&pncqi->ClassGuid, NULL, &hdi); if (S_OK == hr) { hr = HrSetupDiOpenDeviceInfo (hdi, pncqi->pszPnpId, NULL, 0, &deid); if (S_OK == hr) { (VOID) HrSetupDiRemoveDevice (hdi, &deid); } SetupDiDestroyDeviceInfoList (hdi); } // Stop trying to notify INetCfg. // pniq->MarkCurrentItemForDeletion(); } } else { // Display message on error?? TraceHr (ttidError, FAL, hr, FALSE, "EnumerateQueueItemsAndDoNotifications"); } } } else { TraceTag(ttidInstallQueue, "System is shutting down during processing"); hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); } MemFree(pncqi); if (HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS) == hr) { break; } } // This error is expected when enumeration is complete if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } TraceError("EnumerateQueueItemsAndDoNotifications", hr); return fStatusOk; } //+--------------------------------------------------------------------------- // // Function: InstallerQueueWorkItem // // Purpose: The LPTHREAD_START_ROUTINE passed to QueueUserWorkItem to // handle the work of notifying INetCfg and the Connections // wizard of an installation event. // // Arguments: // pvContext [in] A pointer to a CInstallQueue class. // // Returns: NOERROR // // Author: billbe 25 Aug 1998 // // Notes: The CInstallQueue was AddRef'd to insure its existence // while we use it. This function must release it before // exiting. // DWORD WINAPI InstallQueueWorkItem(PVOID pvContext) { const WCHAR c_szInstallQueue[] = L"Install Queue"; const WCHAR c_szProcessQueue[] = L"rundll32 netman.dll,ProcessQueue"; const WCHAR c_szRegValueNcInstallQueue[] = L"NCInstallQueue"; CInstallQueue* pniq = reinterpret_cast(pvContext); Assert(pniq); BOOL fReboot = FALSE; BOOL fInitCom = TRUE; // We need to continue processing when the system is rebooted. RunOnceAddOrClearItem (c_szRegValueNcInstallQueue, c_szProcessQueue, RO_ADD); HRESULT hr = CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); if (RPC_E_CHANGED_MODE == hr) { hr = S_OK; fInitCom = FALSE; } TraceHr (ttidError, FAL, hr, FALSE, "InstallQueueWorkItem: " "CoInitializeEx failed"); if (SUCCEEDED(hr)) { // Open the queue, this will give us a snapshot of what is in the queue // at this time hr = pniq->HrOpen(); if (S_OK == hr) { // Create an HDEVINFO HDEVINFO hdi; hr = HrSetupDiCreateDeviceInfoList(NULL, NULL, &hdi); if (S_OK == hr) { INetCfg* pINetCfg; INetCfgInternalSetup* pInternalSetup; DWORD cmsTimeout = 500; // As long as we are not shutting down. keep trying to get a // writable INetCfg. do { // Increase the time we wait each iteration. cmsTimeout = cmsTimeout >= 512000 ? 512000 : cmsTimeout * 2; // If we are not in the process of shutting down... if (SERVICE_RUNNING == _Module.DwServiceStatus()) { // Try to get a writable INetCfg hr = HrCreateAndInitializeINetCfg(NULL, &pINetCfg, TRUE, cmsTimeout, c_szInstallQueue, NULL); if (NETCFG_E_NEED_REBOOT == hr) { hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); break; } } else { // Times up! Pencils down! Netman is shutting down // we need to stop processing hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); break; } } while (FAILED(hr)); if (S_OK == hr) { hr = pINetCfg->QueryInterface (IID_INetCfgInternalSetup, (void**)&pInternalSetup); if (S_OK == hr) { // Go through the queue notifying interested modules do { if (!EnumerateQueueItemsAndDoNotifications(pINetCfg, pInternalSetup, pniq, hdi, &fReboot)) { hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); continue; } if (SERVICE_RUNNING == _Module.DwServiceStatus ()) { TraceTag(ttidInstallQueue, "Refreshing queue"); // Check to see if any items were added to the queue // after we started processing it hr = pniq->HrRefresh(); if (S_FALSE == hr) { // We are finished so we can remove // the entry in runonce that would // start the queue processing on login. RunOnceAddOrClearItem ( c_szRegValueNcInstallQueue, c_szProcessQueue, RO_CLEAR); } } else { hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS); } } while (S_OK == hr); ReleaseObj (pInternalSetup); } (VOID) HrUninitializeAndReleaseINetCfg(FALSE, pINetCfg, TRUE); } SetupDiDestroyDeviceInfoList(hdi); } } // Close the queue pniq->Close(); DecrementRefCount(); if (fInitCom) { CoUninitialize(); } } if (FAILED(hr)) { // Display error. } // If a reboot is required and we are not in setup or already shutting // down prompt the user. // if (fReboot && (SERVICE_RUNNING == _Module.DwServiceStatus()) && !FInSystemSetup()) { // Handle reboot prompt DWORD dwFlags = QUFR_REBOOT | QUFR_PROMPT; (VOID) HrNcQueryUserForReboot(_Module.GetResourceInstance(), NULL, IDS_INSTALLQUEUE_CAPTION, IDS_INSTALLQUEUE_REBOOT_REQUIRED, dwFlags); } TraceTag(ttidInstallQueue, "User Work Item Completed"); TraceError("InstallQueueWorkItem", (S_FALSE == hr) ? S_OK : hr); return NOERROR; }