Source code of Windows XP (NT5)
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

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