|
|
//+--------------------------------------------------------------------------
//
// 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<LPVOID *>(&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<PWSTR*>(MemAlloc(cbArraySize));
if (m_aszItems) { m_aszItemsToDelete = reinterpret_cast<PWSTR*>(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<PWSTR>(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<CInstallQueue*>(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; }
|