mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1352 lines
38 KiB
1352 lines
38 KiB
//+--------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|