windows-nt/Source/XPSP1/NT/net/config/netman/ncqueue/ncqueue.cpp

1353 lines
38 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+--------------------------------------------------------------------------
//
// 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;
}