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.

1009 lines
23 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. mspthrd.cpp
  5. Abstract:
  6. Implementation for MSP thread management classes.
  7. --*/
  8. #include "precomp.h"
  9. #pragma hdrstop
  10. #include <dbt.h>
  11. CMSPThread g_Thread;
  12. extern "C" DWORD WINAPI gfThreadProc(LPVOID p)
  13. {
  14. return ((CMSPThread *)p)->ThreadProc();
  15. }
  16. HRESULT CMSPThread::Start()
  17. /*++
  18. Routine Description:
  19. Create the thread if it has not already been created. Otherwise, just
  20. keep track of how many times the thread start was performed so that
  21. we only stop the thread when all of these have been paired with a stop.
  22. Arguments:
  23. Return Value:
  24. HRESULT.
  25. --*/
  26. {
  27. LOG((MSP_TRACE, "CMSPThread::Start - enter"));
  28. CLock Lock(m_CountLock);
  29. if ( m_iStartCount == 0 )
  30. {
  31. _ASSERTE(m_hCommandEvent == NULL);
  32. _ASSERTE(m_hThread == NULL);
  33. TCHAR *ptczEventName = NULL;
  34. #if DBG
  35. //
  36. // in debug build, use named events
  37. //
  38. TCHAR tszEventName[MAX_PATH];
  39. _stprintf(tszEventName,
  40. _T("CMSPThread_CommandEvent_pid[0x%lx]CMSPThread[%p]"),
  41. GetCurrentProcessId(), this);
  42. LOG((MSP_TRACE, "CMSPThread::Start - creating event[%S]", tszEventName));
  43. ptczEventName = &tszEventName[0];
  44. #endif
  45. if ((m_hCommandEvent = ::CreateEvent(
  46. NULL,
  47. FALSE, // flag for manual-reset event
  48. FALSE, // initial state is not set.
  49. ptczEventName // No name in release builds, named in debug builds
  50. )) == NULL)
  51. {
  52. LOG((MSP_ERROR, "Can't create the command event"));
  53. return E_FAIL;
  54. }
  55. DWORD dwThreadID;
  56. m_hThread = ::CreateThread(NULL, 0, gfThreadProc, this, 0, &dwThreadID);
  57. if (m_hThread == NULL)
  58. {
  59. LOG((MSP_ERROR, "Can't create thread. %ld", GetLastError()));
  60. return E_FAIL;
  61. }
  62. }
  63. m_iStartCount++;
  64. LOG((MSP_TRACE, "CMSPThread::Start - exit S_OK"));
  65. return S_OK;
  66. }
  67. HRESULT CMSPThread::Stop()
  68. /*++
  69. Routine Description:
  70. Stop the thread.
  71. Arguments:
  72. Return Value:
  73. HRESULT.
  74. --*/
  75. {
  76. LOG((MSP_TRACE, "CMSPThread::Stop - enter"));
  77. CLock Lock(m_CountLock);
  78. //
  79. // Complain if we get more Stops than Starts.
  80. //
  81. if ( m_iStartCount == 0 )
  82. {
  83. LOG((MSP_ERROR, "CMSPThread::Stop - thread already stopped - "
  84. "exit E_FAIL"));
  85. return E_FAIL;
  86. }
  87. //
  88. // Decrement the start count. Due to the above check we should
  89. // never go below zero.
  90. //
  91. m_iStartCount--;
  92. _ASSERTE( m_iStartCount >= 0 );
  93. //
  94. // If there have now been just as many stops as starts, it's time to stop
  95. // the thread.
  96. //
  97. if ( m_iStartCount == 0 )
  98. {
  99. //
  100. // Our state should be cleaned up from before.
  101. //
  102. _ASSERTE(m_hCommandEvent != NULL);
  103. _ASSERTE(m_hThread != NULL);
  104. //
  105. // Allocate a command queue item which we will pass to the thread.
  106. //
  107. COMMAND_QUEUE_ITEM * pItem = new COMMAND_QUEUE_ITEM;
  108. if ( ! pItem )
  109. {
  110. LOG((MSP_ERROR, "CMSPThread::Stop - allocate new queue item"));
  111. return E_OUTOFMEMORY;
  112. }
  113. pItem->node.cmd = STOP;
  114. //
  115. // Put the command queue item in the command queue.
  116. //
  117. m_QueueLock.Lock();
  118. InsertTailList(&m_CommandQueue, &(pItem->link));
  119. m_QueueLock.Unlock();
  120. //
  121. // Signal thread to process this stop command.
  122. //
  123. if (SignalThreadProc() == 0)
  124. {
  125. LOG((MSP_ERROR, "CMSPThread::Stop - can't signal the thread - "
  126. "exit E_FAIL"));
  127. return E_FAIL;
  128. }
  129. //
  130. // Wait until the thread stops
  131. //
  132. if (::WaitForSingleObject(m_hThread, INFINITE) != WAIT_OBJECT_0)
  133. {
  134. LOG((MSP_ERROR, "CMSPThread::Stop - timeout while waiting for the "
  135. "thread to stop"));
  136. }
  137. //
  138. // Clean up our state.
  139. //
  140. ::CloseHandle(m_hCommandEvent);
  141. ::CloseHandle(m_hThread);
  142. m_hCommandEvent = NULL;
  143. m_hThread = NULL;
  144. }
  145. LOG((MSP_TRACE, "CMSPThread::Stop - exit S_OK"));
  146. return S_OK;
  147. }
  148. HRESULT CMSPThread::Shutdown()
  149. /*++
  150. Routine Description:
  151. Unconditionally shutdown the thread. MSPs should by default use Stop()
  152. instead of Shutdwon(), unless they cannot do matched Start() / Stop()
  153. calls because of some other issue.
  154. Arguments:
  155. Return Value:
  156. HRESULT.
  157. --*/
  158. {
  159. LOG((MSP_TRACE, "CMSPThread::Shutdown - enter"));
  160. CLock Lock(m_CountLock);
  161. //
  162. // Ignore if we are not started.
  163. //
  164. if ( m_iStartCount == 0 )
  165. {
  166. LOG((MSP_ERROR, "CMSPThread::Shutdown - thread already stopped - "
  167. "exit S_OK"));
  168. return S_OK;
  169. }
  170. //
  171. // We are started, so stop now, irrespective of the outstanding start
  172. // count.
  173. //
  174. m_iStartCount = 1;
  175. HRESULT hr = Stop();
  176. LOG((MSP_(hr), "CMSPThread::Shutodwn - exit 0x%08x", hr));
  177. return hr;
  178. }
  179. HRESULT CMSPThread::ThreadProc()
  180. /*++
  181. Routine Description:
  182. the main loop of this thread.
  183. Arguments:
  184. Return Value:
  185. HRESULT.
  186. --*/
  187. {
  188. LOG((MSP_TRACE, "CMSPThread::ThreadProc - started"));
  189. BOOL bExitFlag = FALSE;
  190. m_hDevNotifyVideo = NULL;
  191. m_hDevNotifyAudio = NULL;
  192. m_hWndNotif = NULL;
  193. HRESULT hr = E_FAIL;
  194. if (FAILED(hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED)))
  195. {
  196. LOG((MSP_ERROR, "CMSPThread::ThreadProc - ConinitialzeEx failed:%x",
  197. hr));
  198. return hr;
  199. }
  200. //
  201. // Create a window to receive PNP device notifications.
  202. //
  203. // since this is a base class that is used by more than one msp, we want
  204. // to make sure that each msp registers a window class with a unique name
  205. //
  206. // for this reason window class name is derived from threadid.
  207. //
  208. DWORD dwThreadID = GetCurrentThreadId();
  209. //
  210. // the string needs to be big enough to hold max dword number in hex +
  211. // terminating zero. 20 is more than enough.
  212. //
  213. TCHAR szWindowClassName[20];
  214. _stprintf(szWindowClassName, _T("%lx"), dwThreadID);
  215. //
  216. // configure window class structure for RegisterClass
  217. //
  218. WNDCLASS wc;
  219. ZeroMemory(&wc, sizeof(wc));
  220. wc.lpfnWndProc = NotifWndProc;
  221. wc.lpszClassName = szWindowClassName;
  222. //
  223. // perform the actual registration
  224. //
  225. ATOM atomClassRegistration = 0;
  226. atomClassRegistration = RegisterClass(&wc);
  227. if (0 == atomClassRegistration)
  228. {
  229. LOG((MSP_ERROR,
  230. "CMSPThread::ThreadProc - RegisterClass failed, last error %ld",
  231. GetLastError()));
  232. hr = E_FAIL;
  233. goto exit;
  234. }
  235. //
  236. // create window that will receive pnp notifications
  237. //
  238. m_hWndNotif = CreateWindow(szWindowClassName, _T("MSP PNP Notification Window"), 0,
  239. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, this);
  240. if (m_hWndNotif == NULL)
  241. {
  242. LOG((MSP_ERROR, "CMSPThread::ThreadProc - can't create notification window"));
  243. hr = E_FAIL;
  244. goto exit;
  245. }
  246. //
  247. // success
  248. //
  249. LOG((MSP_TRACE, "CMSPThread::ThreadProc - created notification window"));
  250. //
  251. // Register to receive PNP device notifications
  252. //
  253. DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
  254. ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
  255. NotificationFilter.dbcc_size =
  256. sizeof(DEV_BROADCAST_DEVICEINTERFACE);
  257. NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  258. NotificationFilter.dbcc_classguid = AM_KSCATEGORY_VIDEO;
  259. if ((m_hDevNotifyVideo = RegisterDeviceNotification( m_hWndNotif,
  260. &NotificationFilter,
  261. DEVICE_NOTIFY_WINDOW_HANDLE
  262. )) == NULL)
  263. {
  264. LOG((MSP_ERROR, "CMSPThread::ThreadProc - can't register for video device notification"));
  265. hr = E_FAIL;
  266. goto exit;
  267. }
  268. NotificationFilter.dbcc_classguid = AM_KSCATEGORY_AUDIO;
  269. if ((m_hDevNotifyAudio = RegisterDeviceNotification( m_hWndNotif,
  270. &NotificationFilter,
  271. DEVICE_NOTIFY_WINDOW_HANDLE
  272. )) == NULL)
  273. {
  274. LOG((MSP_ERROR, "CMSPThread::ThreadProc - can't register for audio device notification"));
  275. hr = E_FAIL;
  276. goto exit;
  277. }
  278. LOG((MSP_TRACE, "CMSPThread::ThreadProc - registered for PNP device notifications"));
  279. while (!bExitFlag)
  280. {
  281. //
  282. // Msg: Grab window messages.
  283. // Multiple: We only use 1, but Msg and Ex only exist with Multiple.
  284. // Ex: Allow flags, so we can pass in MWMO_ALERTABLE.
  285. //
  286. DWORD dwResult = ::MsgWaitForMultipleObjectsEx(
  287. 1, // wait for one event
  288. &m_hCommandEvent, // array of events to wait for
  289. INFINITE, // wait forever
  290. QS_ALLINPUT, // get all window messages
  291. MWMO_ALERTABLE // get APC requests (in case this MSP uses them)
  292. );
  293. if ( ( dwResult == WAIT_OBJECT_0 ) || ( dwResult == WAIT_OBJECT_0 + 1 ) )
  294. {
  295. LOG((MSP_TRACE, "thread is signaled"));
  296. m_QueueLock.Lock();
  297. while ( ! IsListEmpty(&m_CommandQueue) )
  298. {
  299. LIST_ENTRY * links = RemoveHeadList( &m_CommandQueue );
  300. m_QueueLock.Unlock();
  301. COMMAND_QUEUE_ITEM * pItem =
  302. CONTAINING_RECORD(links,
  303. COMMAND_QUEUE_ITEM,
  304. link);
  305. COMMAND_NODE * pNode = &(pItem->node);
  306. switch (pNode->cmd)
  307. {
  308. case WORK_ITEM:
  309. LOG((MSP_TRACE, "CMSPThread::ThreadProc - "
  310. "got command WORK_ITEM"));
  311. pNode->pfn( pNode->pContext );
  312. if ( pNode->hEvent != NULL )
  313. {
  314. if ( SetEvent( pNode->hEvent ) == 0 )
  315. {
  316. LOG((MSP_ERROR, "CMSPThread::ThreadProc - "
  317. "can't signal event for synchronous work "
  318. "item"));
  319. }
  320. }
  321. break;
  322. case STOP:
  323. LOG((MSP_TRACE, "CMSPThread::ThreadProc - "
  324. "thread is exiting"));
  325. bExitFlag = TRUE;
  326. break;
  327. }
  328. delete pItem;
  329. m_QueueLock.Lock();
  330. }
  331. m_QueueLock.Unlock();
  332. //
  333. // We have processed all commands and unblocked everyone
  334. // who is waiting for us. Now check for window messages.
  335. //
  336. MSG msg;
  337. // Retrieve the next item in the message queue.
  338. while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
  339. {
  340. TranslateMessage(&msg);
  341. DispatchMessage(&msg);
  342. }
  343. }
  344. else if ( dwResult == WAIT_IO_COMPLETION )
  345. {
  346. // FEATUREFEATURE: The base MSP does not do anything with APC /
  347. // async I/O. If the derived MSP does something with it, they must
  348. // implement this to take appropriate action. Question is, how
  349. // best to expose this to the derived MSP? We could have a method
  350. // to override, but then how would the derived thread class get
  351. // instantiated? Instead, we'll have to have a method to set
  352. // an async I/O completion callback function pointer.
  353. }
  354. else
  355. {
  356. LOG((MSP_ERROR, "CMSPThread::ThreadProc - "
  357. "WaitForMultipleObjects failed %ld", GetLastError()));
  358. break;
  359. }
  360. }
  361. hr = S_OK;
  362. exit:
  363. //
  364. // cleanup:
  365. //
  366. // Unregister from PNP device notifications
  367. // destroy window
  368. // unregister window class
  369. // couninitialize
  370. //
  371. //
  372. // unregister from video pnp events if needed
  373. //
  374. if ( NULL != m_hDevNotifyVideo )
  375. {
  376. HRESULT hr2 = UnregisterDeviceNotification(m_hDevNotifyVideo);
  377. if (FAILED(hr2))
  378. {
  379. LOG((MSP_ERROR,
  380. "CMSPThread::ThreadProc - UnregisterDeviceNotification failed for video events. "
  381. "hr = %lx", hr2));
  382. }
  383. }
  384. //
  385. // unregister from audio pnp events if needed
  386. //
  387. if ( NULL != m_hDevNotifyAudio )
  388. {
  389. HRESULT hr2 = UnregisterDeviceNotification(m_hDevNotifyAudio);
  390. if (FAILED(hr2))
  391. {
  392. LOG((MSP_ERROR,
  393. "CMSPThread::ThreadProc - UnregisterDeviceNotification failed for audio events. "
  394. "hr = %lx", hr2));
  395. }
  396. }
  397. //
  398. // destroy window if needed
  399. //
  400. if ( NULL != m_hWndNotif )
  401. {
  402. BOOL bDestroyWindowSuccess = DestroyWindow(m_hWndNotif);
  403. if ( ! bDestroyWindowSuccess )
  404. {
  405. LOG((MSP_ERROR,
  406. "CMSPThread::ThreadProc - DestroyWindow failed. LastError = %ld",
  407. GetLastError()));
  408. }
  409. }
  410. //
  411. // unregister window class
  412. //
  413. if (0 != atomClassRegistration)
  414. {
  415. BOOL bUnregisterSuccess = UnregisterClass( (LPCTSTR)atomClassRegistration, ::GetModuleHandle(NULL) );
  416. if ( ! bUnregisterSuccess )
  417. {
  418. LOG((MSP_ERROR,
  419. "CMSPThread::ThreadProc - UnregisterClass failed. LastError = %ld",
  420. GetLastError()));
  421. }
  422. }
  423. ::CoUninitialize();
  424. LOG((MSP_(hr), "CMSPThread::ThreadProc - exit. hr = 0x%lx", hr));
  425. return hr;
  426. }
  427. HRESULT CMSPThread::QueueWorkItem(
  428. LPTHREAD_START_ROUTINE Function,
  429. PVOID Context,
  430. BOOL fSynchronous
  431. )
  432. {
  433. LOG((MSP_TRACE, "CMSPThread::QueueWorkItem - enter"));
  434. //
  435. // Create a command block for this.
  436. //
  437. COMMAND_QUEUE_ITEM * pItem = new COMMAND_QUEUE_ITEM;
  438. if ( ! pItem )
  439. {
  440. LOG((MSP_ERROR, "CMSPThread::QueueWorkItem - "
  441. "can't allocate new queue item - exit E_OUTOFMEMORY"));
  442. return E_OUTOFMEMORY;
  443. }
  444. //
  445. // Create an event to wait on if this is a synchronous work item.
  446. // Otherwise the thread proc gets a NULL event handle and it knows not to
  447. // signal it since it's an asynchronous work item.
  448. //
  449. TCHAR *ptczEventName = NULL;
  450. #if DBG
  451. static LONG lSequenceNumber = 0;
  452. //
  453. // in debug build, use named events
  454. //
  455. TCHAR tszEventName[MAX_PATH];
  456. InterlockedIncrement(&lSequenceNumber);
  457. //
  458. // identify events by the address of the correspoding queue item, and by
  459. // the sequence number
  460. //
  461. _stprintf(tszEventName,
  462. _T("CMSPThread_QueueWorkitemEvent_pid[0x%lx]_CMSPThread[%p]_Event[%p]_eventNumber[%lu]"),
  463. GetCurrentProcessId(), this, pItem, lSequenceNumber);
  464. LOG((MSP_TRACE, "CMSPThread::QueueWorkItem - creating event[%S]", tszEventName));
  465. ptczEventName = &tszEventName[0];
  466. #endif
  467. HANDLE hEvent = NULL;
  468. if (fSynchronous)
  469. {
  470. hEvent = ::CreateEvent(NULL,
  471. FALSE, // flag for manual-reset event
  472. FALSE, // initial state is not set.
  473. ptczEventName); // No name in release, named in debug
  474. if ( hEvent == NULL )
  475. {
  476. LOG((MSP_ERROR, "CMSPThread::QueueWorkItem - "
  477. "Can't create the Job Done event"));
  478. delete pItem;
  479. pItem = NULL;
  480. return E_FAIL;
  481. }
  482. }
  483. //
  484. // we already have the q item, now initialize it.
  485. //
  486. pItem->node.cmd = WORK_ITEM;
  487. pItem->node.pfn = Function;
  488. pItem->node.pContext = Context;
  489. pItem->node.hEvent = hEvent;
  490. //
  491. // Put the command block on the queue. The queue is protected by a
  492. // critical section.
  493. //
  494. m_QueueLock.Lock();
  495. InsertTailList(&m_CommandQueue, &(pItem->link));
  496. //
  497. // Signal the thread to process the command.
  498. //
  499. if (SignalThreadProc() == 0)
  500. {
  501. //
  502. // failed to signal processing thread
  503. // cleanup and return error
  504. //
  505. //
  506. // remove the queue entry we have submitted
  507. //
  508. RemoveTailList(&m_CommandQueue);
  509. //
  510. // unlock the queue so other threads can use it
  511. //
  512. m_QueueLock.Unlock();
  513. //
  514. // close handle and delete pItem that we have created --
  515. // no one else is going to do this for us
  516. //
  517. if (NULL != hEvent)
  518. {
  519. ::CloseHandle(hEvent);
  520. hEvent = NULL;
  521. }
  522. delete pItem;
  523. pItem = NULL;
  524. LOG((MSP_ERROR, "CMSPThread::QueueWorkItem - "
  525. "can't signal the thread"));
  526. return E_FAIL;
  527. }
  528. //
  529. // unlock the event queue, so it can be used by processing and other
  530. // threads
  531. //
  532. m_QueueLock.Unlock();
  533. //
  534. // If this is a sychronous work item, wait for it to complete and
  535. // then close the event handle.
  536. //
  537. // FEATUREFEATURE: Rather than creating and deleting an event for each
  538. // work item, have a cache of events that can be reused.
  539. //
  540. if (fSynchronous)
  541. {
  542. LOG((MSP_TRACE, "CMSPThread::QueueWorkItem - "
  543. "blocked waiting for synchronous work item to complete"));
  544. // Wait for the synchronous work item to complete.
  545. HANDLE hEvents[2];
  546. DWORD dwEvent;
  547. hEvents[0] = hEvent;
  548. hEvents[1] = m_hThread;
  549. dwEvent = WaitForMultipleObjects(
  550. 2,
  551. hEvents,
  552. FALSE,
  553. INFINITE);
  554. switch (dwEvent)
  555. {
  556. case WAIT_OBJECT_0 + 0:
  557. break;
  558. case WAIT_OBJECT_0 + 1:
  559. LOG((MSP_ERROR, "CMSPThread::QueueWorkItem - "
  560. "thread exited"));
  561. //
  562. // if the item is still in the queue, remove it (since the thread
  563. // won't)
  564. //
  565. m_QueueLock.Lock();
  566. if (IsNodeOnList(&m_CommandQueue, &(pItem->link)))
  567. {
  568. RemoveEntryList(&(pItem->link));
  569. delete pItem;
  570. }
  571. m_QueueLock.Unlock();
  572. //
  573. // time to close event and fail
  574. //
  575. ::CloseHandle(hEvent);
  576. return E_FAIL;
  577. default:
  578. LOG((MSP_ERROR, "CMSPThread::QueueWorkItem - "
  579. "WaitForSingleObject failed"));
  580. }
  581. ::CloseHandle(hEvent);
  582. }
  583. LOG((MSP_TRACE, "CMSPThread::QueueWorkItem - exit S_OK"));
  584. return S_OK;
  585. }
  586. LRESULT CALLBACK CMSPThread::NotifWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  587. {
  588. PNOTIF_LIST pnl;
  589. if (uMsg == WM_CREATE)
  590. {
  591. SetLastError(0);
  592. if (!SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(((LPCREATESTRUCT)lParam)->lpCreateParams)))
  593. {
  594. if (GetLastError()) // It isn't really an error unless get last error says so
  595. {
  596. LOG((MSP_ERROR, "CMSPThread::NotifWndProc - SetWindowLongPtr failed %ld", GetLastError()));
  597. _ASSERTE(FALSE);
  598. return -1;
  599. }
  600. }
  601. }
  602. else
  603. {
  604. CMSPThread *me = (CMSPThread*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  605. switch (uMsg)
  606. {
  607. case WM_DEVICECHANGE:
  608. switch(wParam)
  609. {
  610. case DBT_DEVICEARRIVAL:
  611. LOG((MSP_TRACE, "CMSPThread::NotifWndProc - DBT_DEVICEARRIVAL"));
  612. me->m_NotifLock.Lock();
  613. pnl = me->m_NotifList;
  614. while (pnl != NULL)
  615. {
  616. pnl->addr->PnpNotifHandler(TRUE);
  617. pnl = pnl->next;
  618. }
  619. me->m_NotifLock.Unlock();
  620. break;
  621. case DBT_DEVICEREMOVECOMPLETE:
  622. LOG((MSP_TRACE, "CMSPThread::NotifWndProc - DBT_DEVICEREMOVECOMPLETE"));
  623. me->m_NotifLock.Lock();
  624. pnl = me->m_NotifList;
  625. while (pnl != NULL)
  626. {
  627. pnl->addr->PnpNotifHandler(FALSE);
  628. pnl = pnl->next;
  629. }
  630. me->m_NotifLock.Unlock();
  631. break;
  632. }
  633. return 0;
  634. case WM_DESTROY:
  635. return 0;
  636. default:
  637. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  638. }
  639. }
  640. return 0;
  641. }
  642. HRESULT CMSPThread::RegisterPnpNotification(CMSPAddress *pCMSPAddress)
  643. {
  644. PNOTIF_LIST pnl;
  645. HRESULT hr;
  646. if (IsBadReadPtr(pCMSPAddress, sizeof(CMSPAddress)))
  647. {
  648. LOG((MSP_ERROR, "CMSPThread::RegisterPnpNotification - bad address pointer"));
  649. return E_POINTER;
  650. }
  651. m_NotifLock.Lock();
  652. // Add a new node to the list
  653. pnl = new NOTIF_LIST;
  654. if (pnl == NULL)
  655. {
  656. LOG((MSP_ERROR, "CMSPThread::RegisterPnpNotification - out of memory"));
  657. hr = E_OUTOFMEMORY;
  658. }
  659. else
  660. {
  661. //
  662. // note that we don't keep addref the address -- it is the
  663. // caller's responsibility to ensure we are notified through
  664. // UnregisterPnpNotification when the address is going away
  665. //
  666. pnl->next = m_NotifList;
  667. pnl->addr = pCMSPAddress;
  668. m_NotifList = pnl;
  669. hr = S_OK;
  670. }
  671. m_NotifLock.Unlock();
  672. return hr;
  673. }
  674. HRESULT CMSPThread::UnregisterPnpNotification(CMSPAddress *pCMSPAddress)
  675. {
  676. PNOTIF_LIST pnl, pnlLast;
  677. HRESULT hr = E_FAIL;
  678. if (IsBadReadPtr(pCMSPAddress, sizeof(CMSPAddress)))
  679. {
  680. LOG((MSP_ERROR, "CMSPThread::UnregisterPnpNotification - bad address pointer"));
  681. return E_POINTER;
  682. }
  683. m_NotifLock.Lock();
  684. pnl = m_NotifList;
  685. if ((pnl != NULL) && (pnl->addr == pCMSPAddress))
  686. {
  687. // It is fist in the list, remove it
  688. m_NotifList = pnl->next;
  689. delete pnl;
  690. hr = S_OK;
  691. }
  692. else while (pnl != NULL)
  693. {
  694. pnlLast = pnl;
  695. pnl = pnl->next;
  696. if ((pnl != NULL) && (pnl->addr == pCMSPAddress))
  697. {
  698. // Found it in the list, remove it
  699. pnlLast->next = pnl->next;
  700. delete pnl;
  701. hr = S_OK;
  702. break;
  703. }
  704. }
  705. if (pnl == NULL)
  706. {
  707. LOG(( MSP_WARN, "CMSPThread::UnregisterPnpNotification - address pointer not found in notification list." ));
  708. hr = E_FAIL;
  709. }
  710. m_NotifLock.Unlock();
  711. return hr;
  712. }
  713. // eof