Leaked source code of windows server 2003
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.

1349 lines
39 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1998.
  5. //
  6. // File: N C Q U E U E . C P P
  7. //
  8. // Contents: NetCfg queued installer actions
  9. //
  10. // Notes:
  11. //
  12. // Author: billbe 19 Aug 1998
  13. //
  14. //----------------------------------------------------------------------------
  15. #include "pch.h"
  16. #pragma hdrstop
  17. #include "nceh.h"
  18. #include "ncmisc.h"
  19. #include "ncnetcfg.h"
  20. #include "ncqueue.h"
  21. #include "ncreg.h"
  22. #include "ncsetup.h"
  23. #include "ncui.h"
  24. #include "nceh.h"
  25. #include "wizentry.h"
  26. const WCHAR c_szRegKeyNcQueue[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\NcQueue";
  27. const DWORD c_cchQueueValueNameLen = 9;
  28. const DWORD c_cbQueueValueNameLen = c_cchQueueValueNameLen * sizeof(WCHAR);
  29. enum RO_ACTION
  30. {
  31. RO_ADD,
  32. RO_CLEAR,
  33. };
  34. extern const WCHAR c_szRegValueNetCfgInstanceId[];
  35. CRITICAL_SECTION g_csRefCount;
  36. DWORD g_dwRefCount = 0;
  37. HANDLE g_hLastThreadExitEvent;
  38. DWORD WINAPI
  39. InstallQueueWorkItem(PVOID pvContext);
  40. inline VOID
  41. IncrementRefCount()
  42. {
  43. EnterCriticalSection(&g_csRefCount);
  44. // If 0 is the current count and we have an event to reset...
  45. if (!g_dwRefCount && g_hLastThreadExitEvent)
  46. {
  47. ResetEvent(g_hLastThreadExitEvent);
  48. }
  49. ++g_dwRefCount;
  50. LeaveCriticalSection(&g_csRefCount);
  51. }
  52. inline VOID
  53. DecrementRefCount()
  54. {
  55. EnterCriticalSection(&g_csRefCount);
  56. --g_dwRefCount;
  57. // If the count is 0 and we have an event to signal...
  58. if (!g_dwRefCount && g_hLastThreadExitEvent)
  59. {
  60. SetEvent(g_hLastThreadExitEvent);
  61. }
  62. LeaveCriticalSection(&g_csRefCount);
  63. }
  64. //+---------------------------------------------------------------------------
  65. //
  66. // Function: WaitForInstallQueueToExit
  67. //
  68. // Purpose: This function waits until the last thread Called to continue processing the queue after processing was
  69. // stopped due to a shutdown (of teh Netman service or system)
  70. //
  71. // Arguments:
  72. // none
  73. //
  74. // Returns: nothing
  75. //
  76. // Author: billbe 8 Sep 1998
  77. //
  78. // Notes:
  79. //
  80. VOID
  81. WaitForInstallQueueToExit()
  82. {
  83. // Wait on the event if it was successfully created.
  84. if (g_hLastThreadExitEvent)
  85. {
  86. TraceTag(ttidInstallQueue, "Waiting on LastThreadExitEvent");
  87. (VOID) WaitForSingleObject(g_hLastThreadExitEvent, INFINITE);
  88. TraceTag(ttidInstallQueue, "Event signaled");
  89. }
  90. else
  91. {
  92. // If the event was not created, fall back to simply looping
  93. // on the ref count
  94. while (g_dwRefCount);
  95. }
  96. }
  97. //+---------------------------------------------------------------------------
  98. //
  99. // Function: ProcessQueue
  100. //
  101. // Purpose: Called to continue processing the queue after processing was
  102. // stopped due to a shutdown (of the Netman service or system)
  103. //
  104. // Arguments:
  105. // none
  106. //
  107. // Returns: nothing
  108. //
  109. // Author: billbe 8 Sep 1998
  110. //
  111. // Notes:
  112. //
  113. EXTERN_C VOID WINAPI
  114. ProcessQueue()
  115. {
  116. HRESULT hr;
  117. INetInstallQueue* pniq;
  118. BOOL fInitCom = TRUE;
  119. TraceTag(ttidInstallQueue, "ProcessQueue called");
  120. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
  121. if (RPC_E_CHANGED_MODE == hr)
  122. {
  123. hr = S_OK;
  124. fInitCom = FALSE;
  125. }
  126. if (SUCCEEDED(hr))
  127. {
  128. // Create the install queue object and get the install queue interface
  129. hr = CoCreateInstance(CLSID_InstallQueue, NULL,
  130. CLSCTX_SERVER | CLSCTX_NO_CODE_DOWNLOAD,
  131. IID_INetInstallQueue,
  132. reinterpret_cast<LPVOID *>(&pniq));
  133. if (S_OK == hr)
  134. {
  135. // Process whatever was left in the queue
  136. //
  137. pniq->ProcessItems();
  138. pniq->Release();
  139. }
  140. if (fInitCom)
  141. {
  142. CoUninitialize();
  143. }
  144. }
  145. }
  146. //+---------------------------------------------------------------------------
  147. //
  148. // Function: RunOnceAddOrClearItem
  149. //
  150. // Purpose: Adds or clears an entry to/from the RunOnce registry key.
  151. //
  152. // Arguments:
  153. // pszValueName [in] The value name of the run once item
  154. // pszItemToRun [in] The actual command to "Run Once"
  155. // eAction [in] RO_ADD to add the item, RO_CLEAR to clear the item.
  156. //
  157. // Returns: nothing
  158. //
  159. // Author: billbe 8 Sep 1998
  160. //
  161. // Notes:
  162. //
  163. VOID
  164. RunOnceAddOrClearItem (
  165. IN PCWSTR pszValueName,
  166. IN PCWSTR pszItemToRun,
  167. IN RO_ACTION eAction)
  168. {
  169. static const WCHAR c_szRegKeyRunOnce[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
  170. HRESULT hr;
  171. HKEY hkey;
  172. // Open the RunOnce key
  173. hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE, c_szRegKeyRunOnce,
  174. KEY_WRITE, &hkey);
  175. if (S_OK == hr)
  176. {
  177. if (RO_ADD == eAction)
  178. {
  179. // Set the command line to run when the user logs in next.
  180. (VOID) HrRegSetSz (hkey, pszValueName, pszItemToRun);
  181. TraceTag(ttidInstallQueue, "Added %S RunOnce entry", pszValueName);
  182. }
  183. else if (RO_CLEAR == eAction)
  184. {
  185. // Remove the command line.
  186. (VOID) HrRegDeleteValue (hkey, pszValueName);
  187. TraceTag(ttidInstallQueue, "Cleared %S RunOnce entry", pszValueName);
  188. }
  189. RegCloseKey(hkey);
  190. }
  191. }
  192. //+---------------------------------------------------------------------------
  193. //
  194. // Member: CInstallQueue::CInstallQueue
  195. //
  196. // Purpose: CInstall queue constructor
  197. //
  198. // Arguments:
  199. // (none)
  200. //
  201. // Returns: nothing
  202. //
  203. // Author: BillBe 10 Sep 1998
  204. //
  205. // Notes:
  206. //
  207. CInstallQueue::CInstallQueue() throw (SE_Exception):
  208. m_dwNextAvailableIndex(0),
  209. m_hkey(NULL),
  210. m_nCurrentIndex(-1),
  211. m_cItems(0),
  212. m_aszItems(NULL),
  213. m_cItemsToDelete(0),
  214. m_aszItemsToDelete(NULL),
  215. m_fQueueIsOpen(FALSE)
  216. {
  217. TraceTag(ttidInstallQueue, "Installer queue processor being created");
  218. InitializeCriticalSection (&m_csReadLock);
  219. InitializeCriticalSection (&m_csWriteLock);
  220. InitializeCriticalSection (&g_csRefCount);
  221. // Create an event that we will use to signal to interested parties
  222. // that we are done. This is used by netman to wait for our threads
  223. // to exit before destroying this object
  224. g_hLastThreadExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  225. // If the event could not be created, we can still go on, we just won't
  226. // use the event to signal our exit.
  227. if (!g_hLastThreadExitEvent)
  228. {
  229. TraceTag(ttidInstallQueue, "Error creating last thread exit "
  230. "event %d", GetLastError());
  231. }
  232. // Set the next available queue index so insertions won't overlap
  233. SetNextAvailableIndex();
  234. }
  235. //+---------------------------------------------------------------------------
  236. //
  237. // Member: CInstallQueue::FinalRelease
  238. //
  239. // Purpose: COM destructor
  240. //
  241. // Arguments:
  242. // (none)
  243. //
  244. // Returns: nothing
  245. //
  246. // Author: BillBe 10 Sep 1998
  247. //
  248. // Notes:
  249. //
  250. VOID
  251. CInstallQueue::FinalRelease ()
  252. {
  253. DeleteCriticalSection (&m_csWriteLock);
  254. DeleteCriticalSection (&m_csReadLock);
  255. DeleteCriticalSection (&g_csRefCount);
  256. }
  257. // INetInstallQueue
  258. //+---------------------------------------------------------------------------
  259. //
  260. // Function: CInstallQueue::AddItem
  261. //
  262. // Purpose: Add item to the queue
  263. //
  264. // Arguments:
  265. // pGuid [in] The class guid of the device that was
  266. // modified (installed. updated, or removed)
  267. // pszDeviceInstanceId [in] The instance id of the device
  268. // pszInfId [in] The inf id of the device
  269. // dwCharacter [in] The device's characteristics
  270. // eType [in] The install type (event) - indicates
  271. // whether the device was installed, updated,
  272. // or removed
  273. //
  274. // Returns: HRESULT. S_OK if successful, an error code otherwise.
  275. //
  276. // Author: billbe 25 Aug 1998
  277. //
  278. // Notes: If the device was removed, the device instance id will be the
  279. // instance guid of the device. If the device was installed
  280. // or updated then the id will be the PnP instance id
  281. //
  282. STDMETHODIMP
  283. CInstallQueue::AddItem (
  284. const NIQ_INFO* pInfo)
  285. {
  286. Assert(pInfo);
  287. Assert(pInfo->pszPnpId);
  288. Assert(pInfo->pszInfId);
  289. if (!pInfo)
  290. {
  291. return E_POINTER;
  292. }
  293. if (!pInfo->pszPnpId)
  294. {
  295. return E_POINTER;
  296. }
  297. if (!pInfo->pszInfId)
  298. {
  299. return E_POINTER;
  300. }
  301. // Increment our refcount since we will be queueing a thread
  302. IncrementRefCount();
  303. // Add the item to the queue
  304. HRESULT hr = HrAddItem (pInfo);
  305. if (S_OK == hr)
  306. {
  307. // Start processing the queue on another thread
  308. hr = HrQueueWorkItem();
  309. }
  310. TraceHr (ttidError, FAL, hr, FALSE, "CInstallQueue::AddItem");
  311. return hr;
  312. }
  313. // CInstallQueue
  314. //
  315. //+---------------------------------------------------------------------------
  316. //
  317. // Function: CInstallQueue::HrQueueWorkItem
  318. //
  319. // Purpose: Start processing the queue on another thread
  320. //
  321. // Arguments:
  322. // (none)
  323. //
  324. // Returns: HRESULT. S_OK if successful, an error code otherwise.
  325. //
  326. // Author: billbe 25 Aug 1998
  327. //
  328. // Notes:
  329. //
  330. HRESULT
  331. CInstallQueue::HrQueueWorkItem()
  332. {
  333. HRESULT hr = S_OK;
  334. // Add ref our object since we will need it independent of whoever
  335. // called us
  336. AddRef();
  337. // Queue a work item thread
  338. if (!QueueUserWorkItem(InstallQueueWorkItem, this, WT_EXECUTEDEFAULT))
  339. {
  340. hr = HrFromLastWin32Error();
  341. Release();
  342. // The thread wasn't queued so reduce the ref count
  343. DecrementRefCount();
  344. }
  345. return hr;
  346. }
  347. //+---------------------------------------------------------------------------
  348. //
  349. // Function: CInstallQueue::SetNextAvailableIndex
  350. //
  351. // Purpose: Sets the member variable m_dwNextAvailableIndex to the next
  352. // available queue position (registry valuename)
  353. //
  354. // Arguments:
  355. // none
  356. //
  357. // Returns: nothing
  358. //
  359. // Author: billbe 25 Aug 1998
  360. //
  361. // Notes:
  362. //
  363. VOID
  364. CInstallQueue::SetNextAvailableIndex()
  365. {
  366. TraceTag(ttidInstallQueue, "Setting Next Available index");
  367. EnterCriticalSection(&m_csWriteLock);
  368. DWORD dwTempCount;
  369. HKEY hkey;
  370. // Open the NcQueue registry key
  371. HRESULT hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyNcQueue,
  372. KEY_QUERY_VALUE, &hkey);
  373. if (S_OK == hr)
  374. {
  375. WCHAR szValueName[c_cchQueueValueNameLen];
  376. DWORD cbValueName;
  377. PWSTR pszStopString;
  378. DWORD dwIndex = 0;
  379. DWORD dwType;
  380. do
  381. {
  382. cbValueName = c_cchQueueValueNameLen;
  383. // Enumerate each value name
  384. hr = HrRegEnumValue(hkey, dwIndex, szValueName, &cbValueName,
  385. &dwType, NULL, NULL);
  386. if (S_OK == hr)
  387. {
  388. // Convert the value name to a number
  389. dwTempCount = wcstoul(szValueName, &pszStopString, c_nBase16);
  390. // If the number is greater than our current count
  391. // adjust the current count
  392. if (dwTempCount >= m_dwNextAvailableIndex)
  393. {
  394. m_dwNextAvailableIndex = dwTempCount + 1;
  395. }
  396. }
  397. ++dwIndex;
  398. } while (S_OK == hr);
  399. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
  400. {
  401. hr = S_OK;
  402. }
  403. RegCloseKey(hkey);
  404. }
  405. else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
  406. {
  407. hr = S_OK;
  408. }
  409. TraceTag(ttidInstallQueue, "Next Available index set %d", m_dwNextAvailableIndex);
  410. LeaveCriticalSection(&m_csWriteLock);
  411. }
  412. // Compare strings given pointers to PCWSTRs
  413. inline int __cdecl
  414. iCompare(const void* ppszArg1, const void* ppszArg2)
  415. {
  416. return lstrcmpW(*((PCWSTR*)(void*)ppszArg1), *((PCWSTR*)(void*)ppszArg2));
  417. }
  418. //+---------------------------------------------------------------------------
  419. //
  420. // Function: CInstallQueue::PncqiCreateItem
  421. //
  422. // Purpose: Creates a queue item
  423. //
  424. // Arguments:
  425. // pguidClass [in] The class guid of a device.
  426. // pszDeviceInstanceId [in] The device id of the device.
  427. // a pnp instance id if the device is being
  428. // added or updated, a netcfg instance guid
  429. // if it is being removed.
  430. // pszInfId [in] The device's inf id.
  431. // dwCharacter [in] The device's characteristics.
  432. // eType [in] The notification for the item. Whether
  433. // the device was installed, removed,
  434. // or reinstalled.
  435. //
  436. // Returns: NCQUEUE_ITEM. The newly created item.
  437. //
  438. // Author: billbe 25 Aug 1998
  439. //
  440. // Notes:
  441. //
  442. NCQUEUE_ITEM*
  443. CInstallQueue::PncqiCreateItem(
  444. const NIQ_INFO* pInfo)
  445. {
  446. Assert(pInfo);
  447. Assert(pInfo->pszPnpId);
  448. Assert(pInfo->pszInfId);
  449. // The size of the item is the size of the structure plus the size of
  450. // the device id we are appending to the structure
  451. DWORD cbPnpId = CbOfSzAndTerm (pInfo->pszPnpId);
  452. DWORD cbInfId = CbOfSzAndTerm (pInfo->pszInfId);
  453. DWORD cbSize = sizeof(NCQUEUE_ITEM) + cbPnpId + cbInfId;
  454. NCQUEUE_ITEM* pncqi = (NCQUEUE_ITEM*)MemAlloc(cbSize);
  455. if (pncqi)
  456. {
  457. pncqi->cbSize = sizeof(NCQUEUE_ITEM);
  458. pncqi->eType = pInfo->eType;
  459. pncqi->dwCharacter = pInfo->dwCharacter;
  460. pncqi->dwDeipFlags = pInfo->dwDeipFlags;
  461. pncqi->cchPnpId = wcslen(pInfo->pszPnpId);
  462. pncqi->cchInfId = wcslen(pInfo->pszInfId);
  463. pncqi->ClassGuid = pInfo->ClassGuid;
  464. pncqi->InstanceGuid = pInfo->InstanceGuid;
  465. CopyMemory((BYTE*)pncqi + pncqi->cbSize, pInfo->pszPnpId, cbPnpId);
  466. CopyMemory((BYTE*)pncqi + pncqi->cbSize + cbPnpId,
  467. pInfo->pszInfId, cbInfId);
  468. }
  469. return pncqi;
  470. }
  471. //+---------------------------------------------------------------------------
  472. //
  473. // Function: HrAddItem
  474. //
  475. // Purpose: Worker function that adds an item to the queue
  476. //
  477. // Arguments:
  478. // pguidClass [in] The class guid of a device
  479. // pszwDeviceInstanceId [in] The device id of the device
  480. // a pnp instance id if the device is being
  481. // added or updated, a netcfg instance guid
  482. // if it is being removed
  483. // eType [in] The notification for the item. Whether the device
  484. // was installed, removed, or reinstalled.
  485. //
  486. // Returns: HRESULT. S_OK if successful, an error code otherwise.
  487. //
  488. // Author: billbe 25 Aug 1998
  489. //
  490. // Notes:
  491. //
  492. HRESULT
  493. CInstallQueue::HrAddItem(
  494. const NIQ_INFO* pInfo)
  495. {
  496. HRESULT hr = S_OK;
  497. Assert(pInfo->pszPnpId);
  498. Assert(pInfo->pszInfId);
  499. EnterCriticalSection(&m_csWriteLock);
  500. // Create the structure to be stored in the registry
  501. NCQUEUE_ITEM* pncqi = PncqiCreateItem(pInfo);
  502. if (pncqi)
  503. {
  504. // Open the NcQueue registry key
  505. //
  506. HKEY hkey;
  507. hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyNcQueue,
  508. 0, KEY_READ_WRITE, NULL, &hkey, NULL);
  509. if (S_OK == hr)
  510. {
  511. // Store the queue item under the next available valuename
  512. //
  513. WCHAR szValue[c_cchQueueValueNameLen];
  514. wsprintfW(szValue, L"%.8X", m_dwNextAvailableIndex);
  515. hr = HrRegSetValueEx(hkey, szValue, REG_BINARY, (BYTE*)pncqi,
  516. DwSizeOfItem(pncqi));
  517. if (S_OK == hr)
  518. {
  519. // Update the global count string
  520. ++m_dwNextAvailableIndex;
  521. }
  522. RegCloseKey(hkey);
  523. }
  524. MemFree(pncqi);
  525. }
  526. LeaveCriticalSection(&m_csWriteLock);
  527. TraceError("CInstallQueue::HrAddItem", hr);
  528. return hr;
  529. }
  530. //+---------------------------------------------------------------------------
  531. //
  532. // Function: CInstallQueue::DeleteMarkedItems
  533. //
  534. // Purpose: Deletes, from the registry, any values that have been
  535. // marked for delete.
  536. //
  537. // Arguments:
  538. // none
  539. //
  540. // Returns: nothing
  541. //
  542. // Author: billbe 25 Aug 1998
  543. //
  544. // Notes:
  545. //
  546. VOID
  547. CInstallQueue::DeleteMarkedItems()
  548. {
  549. Assert(m_hkey);
  550. // If we have items to delete...
  551. if (m_cItemsToDelete)
  552. {
  553. Assert(m_aszItemsToDelete);
  554. // Remove each one from the registry
  555. //
  556. for (DWORD dw = 0; dw < m_cItemsToDelete; ++dw)
  557. {
  558. RegDeleteValue(m_hkey, m_aszItemsToDelete[dw]);
  559. }
  560. }
  561. // Free the array and reset the pointer and counter
  562. //
  563. MemFree(m_aszItemsToDelete);
  564. m_aszItemsToDelete = NULL;
  565. m_cItemsToDelete = 0;
  566. }
  567. //+---------------------------------------------------------------------------
  568. //
  569. // Function: CInstallQueue::HrRefresh
  570. //
  571. // Purpose: Refreshs our snapshot of the queue.
  572. //
  573. // Arguments:
  574. // none
  575. //
  576. // Returns: HRESULT. S_OK if successful and the queue has items,
  577. // S_FALSE if the queue is empty,
  578. // an error code otherwise.
  579. ///
  580. // Author: billbe 25 Aug 1998
  581. //
  582. // Notes:
  583. //
  584. HRESULT
  585. CInstallQueue::HrRefresh()
  586. {
  587. Assert(m_hkey);
  588. // We don't want items being added to the queue while we are
  589. // refreshing our snapshot, so we use a critical section to keep
  590. // things
  591. //
  592. EnterCriticalSection(&m_csWriteLock);
  593. // Do some housecleaning before the refresh
  594. //
  595. DeleteMarkedItems();
  596. FreeAszItems();
  597. // Retrieve the number of items in the queue
  598. HRESULT hr = HrRegQueryInfoKey(m_hkey, NULL, NULL, NULL, NULL,
  599. NULL, &m_cItems, NULL, NULL, NULL, NULL);
  600. if (S_OK == hr)
  601. {
  602. Assert(0 <= (INT) m_cItems);
  603. // If the queue is not empty...
  604. if (0 < m_cItems)
  605. {
  606. DWORD cbValueLen;
  607. // Allocate the array of pointers to strings for the items.
  608. // Also, allocate the same quantity of pointers to hold
  609. // items we will delete from the queue
  610. DWORD cbArraySize = m_cItems * sizeof(PWSTR*);
  611. m_aszItems =
  612. reinterpret_cast<PWSTR*>(MemAlloc(cbArraySize));
  613. if (m_aszItems)
  614. {
  615. m_aszItemsToDelete =
  616. reinterpret_cast<PWSTR*>(MemAlloc(cbArraySize));
  617. if (m_aszItemsToDelete)
  618. {
  619. // Store all the value names
  620. // We will need to sort them so we can process each device in the
  621. // correct order
  622. //
  623. DWORD dwType;
  624. for (DWORD dw = 0; dw < m_cItems; ++dw)
  625. {
  626. m_aszItems[dw] =
  627. reinterpret_cast<PWSTR>(MemAlloc(c_cbQueueValueNameLen));
  628. if (m_aszItems[dw])
  629. {
  630. cbValueLen = c_cchQueueValueNameLen;
  631. (void) HrRegEnumValue(m_hkey, dw,
  632. m_aszItems[dw], &cbValueLen,
  633. &dwType, NULL, NULL);
  634. }
  635. else
  636. {
  637. hr = E_OUTOFMEMORY;
  638. }
  639. }
  640. // Sort the value names in ascending order. The value names
  641. // are string versions of numbers padded to the left with zeroes
  642. // e.g. 00000001
  643. qsort(m_aszItems, m_cItems, sizeof(PWSTR), iCompare);
  644. }
  645. else
  646. {
  647. MemFree(m_aszItems);
  648. hr = E_OUTOFMEMORY;
  649. }
  650. }
  651. else
  652. {
  653. hr = E_OUTOFMEMORY;
  654. }
  655. }
  656. else
  657. {
  658. // no items in the queue
  659. hr = S_FALSE;
  660. // The next items entered should start with valuename 00000000
  661. m_dwNextAvailableIndex = 0;
  662. }
  663. }
  664. else
  665. {
  666. // Refresh not possible so invalidate the key
  667. RegCloseKey(m_hkey);
  668. m_hkey = NULL;
  669. }
  670. // Reset Queue Index to just before the first element since
  671. // retrieval is always done on the next element
  672. m_nCurrentIndex = -1;
  673. // Items can now be added to the queue
  674. LeaveCriticalSection(&m_csWriteLock);
  675. TraceError("CInstallQueue::HrRefresh",
  676. (S_FALSE == hr) ? S_OK : hr);
  677. return hr;
  678. }
  679. //+---------------------------------------------------------------------------
  680. //
  681. // Function: CInstallQueue::HrOpen
  682. //
  683. // Purpose: Opens the netcfg installer queue
  684. //
  685. // Arguments:
  686. // None
  687. //
  688. // Returns: HRESULT. S_OK if successful and the queue has items,
  689. // S_FALSE if the queue is empty,
  690. // an error code otherwise.
  691. //
  692. // Author: billbe 25 Aug 1998
  693. //
  694. // Notes: When the queue is opened, it is a snapshot of the current queue
  695. // state. i.e. items could be added after the fact. To refresh the
  696. // snapshot, use RefreshQueue.
  697. //
  698. HRESULT
  699. CInstallQueue::HrOpen()
  700. {
  701. HRESULT hr = S_OK;
  702. // We don't want any other thread to process the queue since we will
  703. // continue to retrieve new items that are added while we are
  704. // processing the initial set
  705. EnterCriticalSection(&m_csReadLock);
  706. AssertSz(!m_hkey, "Reopening NcQueue without closing first!");
  707. // We might have been waiting for a bit. Make sure the system
  708. // is not shutting down before continuing
  709. //
  710. if (SERVICE_RUNNING == _Module.DwServiceStatus ())
  711. {
  712. hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyNcQueue,
  713. KEY_ALL_ACCESS, &m_hkey);
  714. if (S_OK == hr)
  715. {
  716. // Get a current snapshot of what's in the queue
  717. // by refreshing
  718. hr = HrRefresh();
  719. if (SUCCEEDED(hr))
  720. {
  721. // The queue is officially open
  722. m_fQueueIsOpen = TRUE;
  723. }
  724. }
  725. }
  726. else
  727. {
  728. TraceTag(ttidInstallQueue, "HrOpen::System is shutting down");
  729. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  730. }
  731. TraceError("CInstallQueue::HrOpen",
  732. ((S_FALSE == hr) ||
  733. (HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS)) == hr) ?
  734. S_OK : hr);
  735. return hr;
  736. }
  737. //+---------------------------------------------------------------------------
  738. //
  739. // Function: CInstallQueue::Close
  740. //
  741. // Purpose: Closes the netcfg installer queue
  742. //
  743. // Arguments:
  744. // None
  745. //
  746. // Returns: nothing
  747. //
  748. // Author: billbe 25 Aug 1998
  749. //
  750. // Notes:
  751. //
  752. VOID
  753. CInstallQueue::Close()
  754. {
  755. if (m_fQueueIsOpen)
  756. {
  757. // Housecleaning
  758. //
  759. // Delete anything so marked
  760. DeleteMarkedItems();
  761. // Free up the list of value names (aka queue items)
  762. FreeAszItems();
  763. RegSafeCloseKey(m_hkey);
  764. m_hkey = NULL;
  765. // Queue is now closed
  766. m_fQueueIsOpen = FALSE;
  767. }
  768. // Other threads may have a chance at the queue now
  769. LeaveCriticalSection(&m_csReadLock);
  770. }
  771. //+---------------------------------------------------------------------------
  772. //
  773. // Function: CInstallQueue::MarkCurrentItemForDeletion
  774. //
  775. // Purpose: Marks the current item for deletion from the registry
  776. // when the queue is refreshed or closed
  777. //
  778. // Arguments:
  779. // None
  780. //
  781. // Returns: nothing
  782. //
  783. // Author: billbe 25 Aug 1998
  784. //
  785. // Notes:
  786. //
  787. VOID
  788. CInstallQueue::MarkCurrentItemForDeletion()
  789. {
  790. AssertSz(FIsQueueIndexInRange(), "Queue index out of range");
  791. if (FIsQueueIndexInRange())
  792. {
  793. // The number of items to delete should never exceed the number
  794. // of queue items in our snapshot
  795. if (m_cItemsToDelete < m_cItems)
  796. {
  797. // Just store a pointer, in m_aszItemsToDelete, to the value name
  798. // pointed to by m_aszItems
  799. //
  800. m_aszItemsToDelete[m_cItemsToDelete] = m_aszItems[m_nCurrentIndex];
  801. ++m_cItemsToDelete;
  802. }
  803. else
  804. {
  805. TraceTag(ttidError, "Too many items marked for deletion");
  806. }
  807. }
  808. }
  809. //+---------------------------------------------------------------------------
  810. //
  811. // Function: CInstallQueue::HrGetNextItem
  812. //
  813. // Purpose: Get's the next item in the queue
  814. //
  815. // Arguments:
  816. // None
  817. //
  818. // Returns: HRESULT. S_OK is successful,
  819. // ERROR_NO_MORE_ITEMS (hresult version), if there are no
  820. // more items in the queue. An error code otherwise.
  821. //
  822. // Author: billbe 25 Aug 1998
  823. //
  824. // Notes:
  825. //
  826. HRESULT
  827. CInstallQueue::HrGetNextItem(NCQUEUE_ITEM** ppncqi)
  828. {
  829. Assert(ppncqi);
  830. HRESULT hr;
  831. // Increment index to the next value
  832. ++m_nCurrentIndex;
  833. // If we haven't gone past the end of the queue...
  834. if (FIsQueueIndexInRange())
  835. {
  836. // assign convenience pointer
  837. PCWSTR pszItem = m_aszItems[m_nCurrentIndex];
  838. DWORD cbData;
  839. // Get the next queue item from the registry
  840. //
  841. hr = HrRegQueryValueEx(m_hkey, pszItem, NULL, NULL, &cbData);
  842. if (S_OK == hr)
  843. {
  844. *ppncqi = (NCQUEUE_ITEM*)MemAlloc(cbData);
  845. if( *ppncqi )
  846. {
  847. DWORD dwType;
  848. hr = HrRegQueryValueEx(m_hkey, pszItem, &dwType,
  849. (BYTE*)(*ppncqi), &cbData);
  850. if (S_OK == hr)
  851. {
  852. Assert(REG_BINARY == dwType);
  853. Assert((*ppncqi)->cchPnpId == (DWORD)
  854. (wcslen((PWSTR)((BYTE*)(*ppncqi) +
  855. (*ppncqi)->cbSize))));
  856. Assert((*ppncqi)->cchInfId == (DWORD)
  857. (wcslen((PWSTR)((BYTE*)(*ppncqi) +
  858. (*ppncqi)->cbSize +
  859. ((*ppncqi)->cchPnpId + 1) * sizeof(WCHAR)))));
  860. // change union variable from the count of characters
  861. // to the actual string pointer
  862. SetItemStringPtrs(*ppncqi);
  863. }
  864. else
  865. {
  866. MemFree(*ppncqi);
  867. }
  868. }
  869. else
  870. {
  871. hr = E_OUTOFMEMORY;
  872. }
  873. }
  874. }
  875. else
  876. {
  877. hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
  878. }
  879. TraceError("CInstallQueue::HrGetNextItem",
  880. (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) ? S_OK : hr);
  881. return hr;
  882. }
  883. //+---------------------------------------------------------------------------
  884. //
  885. // Function: EnumerateQueueItemsAndDoNotifications
  886. //
  887. // Purpose: Enumerates each item in the queue and notifies INetCfg
  888. // of the modification (event)
  889. //
  890. // Arguments:
  891. // pINetCfg [in] INetCfg interface
  892. // pinq [in] The INetInstallQueue interface
  893. // hdi [in] See Device Installer Api for more info
  894. // pfReboot [out] TRUE if INetCfg requested a reboot,
  895. // FALSE otherwise
  896. //
  897. // Returns: HRESULT. S_OK is successful, an error code otherwise
  898. //
  899. // Author: billbe 8 Sep 1998
  900. //
  901. // Notes:
  902. //
  903. BOOL
  904. EnumerateQueueItemsAndDoNotifications(
  905. INetCfg* pINetCfg,
  906. INetCfgInternalSetup* pInternalSetup,
  907. CInstallQueue* pniq,
  908. HDEVINFO hdi,
  909. BOOL* pfReboot)
  910. {
  911. Assert(pINetCfg);
  912. Assert(pniq);
  913. Assert(IsValidHandle(hdi));
  914. Assert(pfReboot);
  915. NCQUEUE_ITEM* pncqi;
  916. SP_DEVINFO_DATA deid;
  917. HRESULT hr;
  918. BOOL fStatusOk = TRUE;
  919. // Go through each item in the queue and add to INetCfg
  920. //
  921. while (S_OK == (hr = pniq->HrGetNextItem(&pncqi)))
  922. {
  923. // If we are not shutting down...
  924. if (SERVICE_RUNNING == _Module.DwServiceStatus ())
  925. {
  926. if (NCI_INSTALL == pncqi->eType)
  927. {
  928. NIQ_INFO Info;
  929. ZeroMemory(&Info, sizeof(Info));
  930. Info.ClassGuid = pncqi->ClassGuid;
  931. Info.InstanceGuid = pncqi->InstanceGuid;
  932. Info.dwCharacter = pncqi->dwCharacter;
  933. Info.dwDeipFlags = pncqi->dwDeipFlags;
  934. Info.pszPnpId = pncqi->pszPnpId;
  935. Info.pszInfId = pncqi->pszInfId;
  936. // Notify INetCfg
  937. hr = HrDiAddComponentToINetCfg(
  938. pINetCfg, pInternalSetup, &Info);
  939. }
  940. else if (NCI_UPDATE == pncqi->eType)
  941. {
  942. pInternalSetup->EnumeratedComponentUpdated(pncqi->pszPnpId);
  943. }
  944. else if (NCI_REMOVE == pncqi->eType)
  945. {
  946. hr = pInternalSetup->EnumeratedComponentRemoved (
  947. pncqi->pszPnpId);
  948. }
  949. if (SUCCEEDED(hr))
  950. {
  951. // Store the reboot result
  952. if (NETCFG_S_REBOOT == hr)
  953. {
  954. *pfReboot = TRUE;
  955. }
  956. TraceTag(ttidInstallQueue, "Deleting item");
  957. pniq->MarkCurrentItemForDeletion();
  958. }
  959. else
  960. {
  961. if (NETCFG_E_NEED_REBOOT == hr)
  962. {
  963. // Stop processing the queue since INetCfg will
  964. // refuse updates.
  965. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  966. fStatusOk = FALSE;
  967. }
  968. else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
  969. {
  970. // INetCfg couldn't find the adapter. Maybe someone
  971. // removed it before we could notify INetCfg.
  972. //
  973. if (NCI_REMOVE != pncqi->eType)
  974. {
  975. HDEVINFO hdi;
  976. SP_DEVINFO_DATA deid;
  977. // Double check if the device is there.
  978. // If it is, we need to remove it since INetCfg
  979. // refuses to acknowledge its presence.
  980. //
  981. hr = HrSetupDiCreateDeviceInfoList (&pncqi->ClassGuid,
  982. NULL, &hdi);
  983. if (S_OK == hr)
  984. {
  985. hr = HrSetupDiOpenDeviceInfo (hdi,
  986. pncqi->pszPnpId, NULL, 0, &deid);
  987. if (S_OK == hr)
  988. {
  989. (VOID) HrSetupDiRemoveDevice (hdi, &deid);
  990. }
  991. SetupDiDestroyDeviceInfoList (hdi);
  992. }
  993. // Stop trying to notify INetCfg.
  994. //
  995. pniq->MarkCurrentItemForDeletion();
  996. }
  997. }
  998. else
  999. {
  1000. // Display message on error??
  1001. TraceHr (ttidError, FAL, hr, FALSE,
  1002. "EnumerateQueueItemsAndDoNotifications");
  1003. }
  1004. }
  1005. }
  1006. else
  1007. {
  1008. TraceTag(ttidInstallQueue, "System is shutting down during processing");
  1009. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  1010. }
  1011. MemFree(pncqi);
  1012. if (HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS) == hr)
  1013. {
  1014. break;
  1015. }
  1016. }
  1017. // This error is expected when enumeration is complete
  1018. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
  1019. {
  1020. hr = S_OK;
  1021. }
  1022. TraceError("EnumerateQueueItemsAndDoNotifications", hr);
  1023. return fStatusOk;
  1024. }
  1025. //+---------------------------------------------------------------------------
  1026. //
  1027. // Function: InstallerQueueWorkItem
  1028. //
  1029. // Purpose: The LPTHREAD_START_ROUTINE passed to QueueUserWorkItem to
  1030. // handle the work of notifying INetCfg and the Connections
  1031. // wizard of an installation event.
  1032. //
  1033. // Arguments:
  1034. // pvContext [in] A pointer to a CInstallQueue class.
  1035. //
  1036. // Returns: NOERROR
  1037. //
  1038. // Author: billbe 25 Aug 1998
  1039. //
  1040. // Notes: The CInstallQueue was AddRef'd to insure its existence
  1041. // while we use it. This function must release it before
  1042. // exiting.
  1043. //
  1044. DWORD WINAPI
  1045. InstallQueueWorkItem(PVOID pvContext)
  1046. {
  1047. const WCHAR c_szInstallQueue[] = L"Install Queue";
  1048. const WCHAR c_szProcessQueue[] = L"rundll32 netman.dll,ProcessQueue";
  1049. const WCHAR c_szRegValueNcInstallQueue[] = L"NCInstallQueue";
  1050. CInstallQueue* pniq = reinterpret_cast<CInstallQueue*>(pvContext);
  1051. Assert(pniq);
  1052. BOOL fReboot = FALSE;
  1053. BOOL fInitCom = TRUE;
  1054. // We need to continue processing when the system is rebooted.
  1055. RunOnceAddOrClearItem (c_szRegValueNcInstallQueue,
  1056. c_szProcessQueue, RO_ADD);
  1057. HRESULT hr = CoInitializeEx (NULL,
  1058. COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
  1059. if (RPC_E_CHANGED_MODE == hr)
  1060. {
  1061. hr = S_OK;
  1062. fInitCom = FALSE;
  1063. }
  1064. TraceHr (ttidError, FAL, hr, FALSE, "InstallQueueWorkItem: "
  1065. "CoInitializeEx failed");
  1066. if (SUCCEEDED(hr))
  1067. {
  1068. // Open the queue, this will give us a snapshot of what is in the queue
  1069. // at this time
  1070. hr = pniq->HrOpen();
  1071. if (S_OK == hr)
  1072. {
  1073. // Create an HDEVINFO
  1074. HDEVINFO hdi;
  1075. hr = HrSetupDiCreateDeviceInfoList(NULL, NULL, &hdi);
  1076. if (S_OK == hr)
  1077. {
  1078. INetCfg* pINetCfg;
  1079. INetCfgInternalSetup* pInternalSetup;
  1080. DWORD cmsTimeout = 500;
  1081. // As long as we are not shutting down. keep trying to get a
  1082. // writable INetCfg.
  1083. do
  1084. {
  1085. // Increase the time we wait each iteration.
  1086. cmsTimeout = cmsTimeout >= 512000 ? 512000 : cmsTimeout * 2;
  1087. // If we are not in the process of shutting down...
  1088. if (SERVICE_RUNNING == _Module.DwServiceStatus())
  1089. {
  1090. // Try to get a writable INetCfg
  1091. hr = HrCreateAndInitializeINetCfg(NULL, &pINetCfg,
  1092. TRUE, cmsTimeout, c_szInstallQueue, NULL);
  1093. if (NETCFG_E_NEED_REBOOT == hr)
  1094. {
  1095. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  1096. break;
  1097. }
  1098. }
  1099. else
  1100. {
  1101. // Times up! Pencils down! Netman is shutting down
  1102. // we need to stop processing
  1103. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  1104. break;
  1105. }
  1106. } while (FAILED(hr));
  1107. if (S_OK == hr)
  1108. {
  1109. hr = pINetCfg->QueryInterface (IID_INetCfgInternalSetup,
  1110. (void**)&pInternalSetup);
  1111. if (S_OK == hr)
  1112. {
  1113. // Go through the queue notifying interested modules
  1114. do
  1115. {
  1116. if (!EnumerateQueueItemsAndDoNotifications(pINetCfg,
  1117. pInternalSetup, pniq, hdi, &fReboot))
  1118. {
  1119. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  1120. continue;
  1121. }
  1122. if (SERVICE_RUNNING == _Module.DwServiceStatus ())
  1123. {
  1124. TraceTag(ttidInstallQueue, "Refreshing queue");
  1125. // Check to see if any items were added to the queue
  1126. // after we started processing it
  1127. hr = pniq->HrRefresh();
  1128. if (S_FALSE == hr)
  1129. {
  1130. // We are finished so we can remove
  1131. // the entry in runonce that would
  1132. // start the queue processing on login.
  1133. RunOnceAddOrClearItem (
  1134. c_szRegValueNcInstallQueue,
  1135. c_szProcessQueue, RO_CLEAR);
  1136. }
  1137. }
  1138. else
  1139. {
  1140. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  1141. }
  1142. } while (S_OK == hr);
  1143. ReleaseObj (pInternalSetup);
  1144. }
  1145. (VOID) HrUninitializeAndReleaseINetCfg(FALSE, pINetCfg,
  1146. TRUE);
  1147. }
  1148. SetupDiDestroyDeviceInfoList(hdi);
  1149. }
  1150. }
  1151. // Close the queue
  1152. pniq->Close();
  1153. DecrementRefCount();
  1154. if (fInitCom)
  1155. {
  1156. CoUninitialize();
  1157. }
  1158. }
  1159. if (FAILED(hr))
  1160. {
  1161. // Display error.
  1162. }
  1163. // If a reboot is required and we are not in setup or already shutting
  1164. // down prompt the user.
  1165. //
  1166. if (fReboot && (SERVICE_RUNNING == _Module.DwServiceStatus()) &&
  1167. !FInSystemSetup())
  1168. {
  1169. // Handle reboot prompt
  1170. DWORD dwFlags = QUFR_REBOOT | QUFR_PROMPT;
  1171. (VOID) HrNcQueryUserForReboot(_Module.GetResourceInstance(),
  1172. NULL, IDS_INSTALLQUEUE_CAPTION,
  1173. IDS_INSTALLQUEUE_REBOOT_REQUIRED,
  1174. dwFlags);
  1175. }
  1176. TraceTag(ttidInstallQueue, "User Work Item Completed");
  1177. TraceError("InstallQueueWorkItem", (S_FALSE == hr) ? S_OK : hr);
  1178. return NOERROR;
  1179. }