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.

2085 lines
67 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. Handles incoming notifications and requests for consumers and providers
  7. Author:
  8. 16-Jan-1997 AlanWar
  9. Revision History:
  10. --*/
  11. #include <nt.h>
  12. #include "wmiump.h"
  13. #include "evntrace.h"
  14. #include "trcapi.h"
  15. ULONG WmipEventPumpFromKernel(
  16. PVOID Param
  17. );
  18. void WmipExternalNotification(
  19. NOTIFICATIONCALLBACK Callback,
  20. ULONG_PTR Context,
  21. PWNODE_HEADER Wnode
  22. )
  23. /*++
  24. Routine Description:
  25. This routine dispatches an event to the appropriate callback
  26. routine. This process only receives events from the WMI service that
  27. need to be dispatched within this process. The callback address for the
  28. specific event is passed by the wmi service in Wnode->Linkage.
  29. Arguments:
  30. Callback is address to callback
  31. Context is the context to callback with
  32. Wnode has event to deliver
  33. Return Value:
  34. --*/
  35. {
  36. try
  37. {
  38. (*Callback)(Wnode, Context);
  39. } except(EXCEPTION_EXECUTE_HANDLER) {
  40. WmipDebugPrint(("NotificationCallbackRoutine threw exception %d\n",
  41. GetExceptionCode()));
  42. }
  43. }
  44. #ifdef MEMPHIS
  45. ULONG WmipExternalNotificationThread(
  46. PNOTIFDELIVERYCTX NDContext
  47. )
  48. /*++
  49. Routine Description:
  50. This routine is the thread function used to deliver events to event
  51. consumers on memphis.
  52. Arguments:
  53. NDContext specifies the information about how to callback the application
  54. with the event.
  55. Return Value:
  56. --*/
  57. {
  58. WmipExternalNotification(NDContext->Callback,
  59. NDContext->Context,
  60. NDContext->Wnode);
  61. WmipFree(NDContext);
  62. return(0);
  63. }
  64. #endif
  65. void
  66. WmipProcessExternalEvent(
  67. PWNODE_HEADER Wnode,
  68. ULONG WnodeSize,
  69. PVOID DeliveryInfo,
  70. ULONG_PTR DeliveryContext,
  71. ULONG NotificationFlags
  72. )
  73. {
  74. HANDLE ThreadHandle;
  75. PNOTIFDELIVERYCTX NDContext;
  76. PWNODE_HEADER *WnodePtr;
  77. BOOLEAN WnodePtrOk;
  78. PWNODE_HEADER WnodeCopy;
  79. BOOLEAN PostOk;
  80. ULONG Status;
  81. PVOID NotificationAddress;
  82. PVOID NotificationContext;
  83. NotificationAddress = DeliveryInfo;
  84. NotificationContext = (PVOID)DeliveryContext;
  85. if (NotificationFlags &
  86. NOTIFICATION_FLAG_CALLBACK_DIRECT)
  87. {
  88. //
  89. // Callback notifications can happen in this thread or a new
  90. // thread. It is up to the server to decide.
  91. #ifdef MEMPHIS
  92. if (NotificationFlags & DCREF_FLAG_NO_EXTRA_THREAD)
  93. {
  94. WmipExternalNotification(
  95. (NOTIFICATIONCALLBACK)NotificationAddress,
  96. (ULONG_PTR)NotificationContext,
  97. Wnode);
  98. } else {
  99. NDContext = WmipAlloc(FIELD_OFFSET(NOTIFDELIVERYCTX,
  100. WnodeBuffer) + WnodeSize);
  101. if (NDContext != NULL)
  102. {
  103. NDContext->Callback = (NOTIFICATIONCALLBACK)NotificationAddress;
  104. NDContext->Context = (ULONG_PTR)NotificationContext;
  105. WnodeCopy = (PWNODE_HEADER)NDContext->WnodeBuffer;
  106. memcpy(WnodeCopy, Wnode, WnodeSize);
  107. NDContext->Wnode = WnodeCopy;
  108. ThreadHandle = WmipCreateThread(NULL,
  109. 0,
  110. WmipExternalNotificationThread,
  111. NDContext,
  112. 0,
  113. NULL);
  114. if (ThreadHandle != NULL)
  115. {
  116. WmipCloseHandle(ThreadHandle);
  117. } else {
  118. WmipDebugPrint(("WMI: Event dropped due to thread creation failure\n"));
  119. }
  120. } else {
  121. WmipDebugPrint(("WMI: Event dropped due to lack of memory\n"));
  122. }
  123. }
  124. #else
  125. //
  126. // On NT we deliver events in this thread since
  127. // the service is using async rpc.
  128. WmipExternalNotification(
  129. (NOTIFICATIONCALLBACK)NotificationAddress,
  130. (ULONG_PTR)NotificationContext,
  131. Wnode);
  132. #endif
  133. }
  134. }
  135. void WmipInternalNotification(
  136. IN PWNODE_HEADER Wnode,
  137. IN NOTIFICATIONCALLBACK Callback,
  138. IN ULONG_PTR DeliveryContext,
  139. IN BOOLEAN IsAnsi
  140. )
  141. {
  142. ULONG ActionCode, Cookie;
  143. PUCHAR Buffer = (PUCHAR)Wnode;
  144. //
  145. // This is an internal event, which is really a callback
  146. // from kernel mode
  147. //
  148. ActionCode = Wnode->ProviderId;
  149. Cookie = Wnode->CountLost;
  150. #if DBG
  151. WmipDebugPrint(("WMI: Got internal event Action %d, Cookie %x\n",
  152. ActionCode, Cookie));
  153. #endif
  154. // if this is a trace guid enable/disable call use the cookie
  155. // to get the address
  156. if ( (Wnode->Flags & WNODE_FLAG_TRACED_GUID) || (ActionCode == WmiMBRequest) )
  157. {
  158. switch (ActionCode) {
  159. case WmiEnableEvents:
  160. case WmiDisableEvents:
  161. {
  162. WMIDPREQUEST WmiDPRequest;
  163. PVOID RequestAddress;
  164. PVOID RequestContext;
  165. ULONG Status;
  166. ULONG BufferSize = Wnode->BufferSize;
  167. if (Wnode->BufferSize >= (sizeof(WNODE_HEADER) + sizeof(ULONG64)) ) {
  168. PULONG64 pLoggerContext = (PULONG64)(Buffer + sizeof(WNODE_HEADER));
  169. Wnode->HistoricalContext = *pLoggerContext;
  170. }
  171. else {
  172. #if DBG
  173. WmipDebugPrint(("WMI: Wnode size %d is small for Trace notifications\n", Wnode->BufferSize));
  174. #endif
  175. }
  176. if (WmipLookupCookie(Cookie,
  177. &Wnode->Guid,
  178. &RequestAddress,
  179. &RequestContext)) {
  180. WmiDPRequest = (WMIDPREQUEST)RequestAddress;
  181. try
  182. {
  183. #ifndef MEMPHIS
  184. WmipGenericTraceEnable(Wnode->ProviderId, Buffer, (PVOID*)&WmiDPRequest);
  185. #endif
  186. if (*WmiDPRequest != NULL) {
  187. Status = (*WmiDPRequest)(Wnode->ProviderId,
  188. RequestContext,
  189. &BufferSize,
  190. Buffer);
  191. }
  192. else
  193. Status = ERROR_WMI_DP_NOT_FOUND;
  194. } except (EXCEPTION_EXECUTE_HANDLER) {
  195. #if DBG
  196. Status = GetExceptionCode();
  197. WmipDebugPrint(("WMI: Service request call caused an exception %d\n",
  198. Status));
  199. #endif
  200. Status = ERROR_WMI_DP_FAILED;
  201. }
  202. }
  203. break;
  204. }
  205. case WmiMBRequest:
  206. {
  207. PGUIDNOTIFICATION GNEntry;
  208. PVOID DeliveryInfo = NULL;
  209. ULONG_PTR DeliveryContext1;
  210. ULONG i;
  211. PWMI_LOGGER_INFORMATION LoggerInfo;
  212. if (Wnode->BufferSize < (sizeof(WNODE_HEADER) + sizeof(WMI_LOGGER_INFORMATION)) )
  213. {
  214. #if DBG
  215. WmipSetLastError(ERROR_WMI_DP_FAILED);
  216. WmipDebugPrint(("WMI: WmiMBRequest with invalid buffer size %d\n",
  217. Wnode->BufferSize));
  218. #endif
  219. return;
  220. }
  221. LoggerInfo = (PWMI_LOGGER_INFORMATION) ((PUCHAR)Wnode + sizeof(WNODE_HEADER));
  222. GNEntry = WmipFindGuidNotification(&LoggerInfo->Wnode.Guid);
  223. if (GNEntry != NULL)
  224. {
  225. WmipEnterPMCritSection();
  226. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  227. {
  228. if (GNEntry->Notifyee[i].DeliveryInfo != NULL)
  229. {
  230. //
  231. // TODO: We expect only one entry here. Restrict at registration.
  232. //
  233. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  234. DeliveryContext1 = GNEntry->Notifyee[i].DeliveryContext;
  235. break;
  236. }
  237. }
  238. WmipLeavePMCritSection();
  239. WmipDereferenceGNEntry(GNEntry);
  240. }
  241. if (DeliveryInfo != NULL)
  242. {
  243. LoggerInfo->Wnode.CountLost = Wnode->CountLost;
  244. WmipProcessUMRequest(LoggerInfo, DeliveryInfo, Wnode->Version);
  245. }
  246. break;
  247. }
  248. default:
  249. {
  250. #if DBG
  251. WmipSetLastError(ERROR_WMI_DP_FAILED);
  252. WmipDebugPrint(("WMI: WmiMBRequest failed. Delivery Info not found\n" ));
  253. #endif
  254. }
  255. }
  256. } else if (IsEqualGUID(&Wnode->Guid, &GUID_MOF_RESOURCE_ADDED_NOTIFICATION) ||
  257. IsEqualGUID(&Wnode->Guid, &GUID_MOF_RESOURCE_REMOVED_NOTIFICATION) )
  258. {
  259. switch (ActionCode)
  260. {
  261. case MOFEVENT_ACTION_IMAGE_PATH:
  262. case MOFEVENT_ACTION_REGISTRY_PATH:
  263. {
  264. //
  265. // We got a MOF resource added or removed notification. We have
  266. // to convert from regpath to imagepath and then get the list
  267. // of MUI image paths
  268. //
  269. /* WmipProcessMofAddRemoveEvent((PWNODE_SINGLE_INSTANCE)Wnode,
  270. Callback,
  271. DeliveryContext,
  272. IsAnsi);*/
  273. break;
  274. }
  275. case MOFEVENT_ACTION_LANGUAGE_CHANGE:
  276. {
  277. //
  278. // This is a notification for adding or removing a language
  279. // from the system. We need to figure out which language is
  280. // coming or going and then build a list of the affected mof
  281. // resources and send mof added or removed notifications for
  282. // all mof resources
  283. //
  284. /* WmipProcessLanguageAddRemoveEvent((PWNODE_SINGLE_INSTANCE)Wnode,
  285. Callback,
  286. DeliveryContext,
  287. IsAnsi);*/
  288. break;
  289. }
  290. default:
  291. {
  292. WmipAssert(FALSE);
  293. }
  294. }
  295. }
  296. }
  297. void WmipConvertEventToAnsi(
  298. PWNODE_HEADER Wnode
  299. )
  300. {
  301. PWCHAR WPtr;
  302. if (Wnode->Flags & WNODE_FLAG_ALL_DATA)
  303. {
  304. WmipConvertWADToAnsi((PWNODE_ALL_DATA)Wnode);
  305. } else if ((Wnode->Flags & WNODE_FLAG_SINGLE_INSTANCE) ||
  306. (Wnode->Flags & WNODE_FLAG_SINGLE_ITEM)) {
  307. WPtr = (PWCHAR)OffsetToPtr(Wnode,
  308. ((PWNODE_SINGLE_INSTANCE)Wnode)->OffsetInstanceName);
  309. WmipCountedUnicodeToCountedAnsi(WPtr, (PCHAR)WPtr);
  310. }
  311. Wnode->Flags |= WNODE_FLAG_ANSI_INSTANCENAMES;
  312. }
  313. void WmipDeliverAllEvents(
  314. PUCHAR Buffer,
  315. ULONG BufferSize
  316. )
  317. /*++
  318. Routine Description:
  319. Arguments:
  320. Return Value:
  321. --*/
  322. {
  323. PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer;
  324. ULONG Linkage = 1;
  325. ULONG CompositeFlags;
  326. ULONG i;
  327. PGUIDNOTIFICATION GNEntry;
  328. ULONG Flags;
  329. PVOID DeliveryInfo;
  330. ULONG_PTR DeliveryContext;
  331. ULONG WnodeSize;
  332. ULONG CurrentOffset;
  333. #if DBG
  334. PWNODE_HEADER LastWnode;
  335. #endif
  336. CurrentOffset = 0;
  337. while (Linkage != 0)
  338. {
  339. //
  340. // External notifications are handled here
  341. Linkage = Wnode->Linkage;
  342. Wnode->Linkage = 0;
  343. if (Wnode->Flags & WNODE_FLAG_INTERNAL)
  344. {
  345. //
  346. // This is an internal event, which is really a callback
  347. // from kernel mode
  348. //
  349. WmipInternalNotification(Wnode,
  350. NULL,
  351. 0,
  352. FALSE);
  353. } else {
  354. //
  355. // This is a plain old event, figure out who owns it and'
  356. // go deliver it
  357. //
  358. GNEntry = WmipFindGuidNotification(&Wnode->Guid);
  359. if (GNEntry != NULL)
  360. {
  361. CompositeFlags = 0;
  362. WnodeSize = Wnode->BufferSize;
  363. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  364. {
  365. WmipEnterPMCritSection();
  366. Flags = GNEntry->Notifyee[i].Flags;
  367. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  368. DeliveryContext = GNEntry->Notifyee[i].DeliveryContext;
  369. WmipLeavePMCritSection();
  370. if ((DeliveryInfo != NULL) &&
  371. ((Flags & DCREF_FLAG_ANSI) == 0))
  372. {
  373. WmipProcessExternalEvent(Wnode,
  374. WnodeSize,
  375. DeliveryInfo,
  376. DeliveryContext,
  377. Flags);
  378. }
  379. CompositeFlags |= Flags;
  380. }
  381. //
  382. // If there is any demand for ANSI events then convert
  383. // event to ansi and send it off
  384. if (CompositeFlags & DCREF_FLAG_ANSI)
  385. {
  386. //
  387. // Caller wants ansi notification - convert
  388. // instance names
  389. //
  390. WmipConvertEventToAnsi(Wnode);
  391. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  392. {
  393. WmipEnterPMCritSection();
  394. Flags = GNEntry->Notifyee[i].Flags;
  395. DeliveryInfo = GNEntry->Notifyee[i].DeliveryInfo;
  396. DeliveryContext = GNEntry->Notifyee[i].DeliveryContext;
  397. WmipLeavePMCritSection();
  398. if ((DeliveryInfo != NULL) &&
  399. (Flags & DCREF_FLAG_ANSI))
  400. {
  401. WmipProcessExternalEvent(Wnode,
  402. WnodeSize,
  403. DeliveryInfo,
  404. DeliveryContext,
  405. Flags);
  406. }
  407. }
  408. }
  409. WmipDereferenceGNEntry(GNEntry);
  410. }
  411. }
  412. #if DBG
  413. LastWnode = Wnode;
  414. #endif
  415. Wnode = (PWNODE_HEADER)OffsetToPtr(Wnode, Linkage);
  416. CurrentOffset += Linkage;
  417. if (CurrentOffset >= BufferSize)
  418. {
  419. WmipDebugPrint(("WMI: Invalid linkage field 0x%x in WNODE %p. Buffer %p, Length 0x%x\n",
  420. Linkage, LastWnode, Buffer, BufferSize));
  421. Linkage = 0;
  422. }
  423. }
  424. }
  425. LIST_ENTRY WmipGNHead = {&WmipGNHead, &WmipGNHead};
  426. PLIST_ENTRY WmipGNHeadPtr = &WmipGNHead;
  427. void
  428. WmipDereferenceGNEntry(
  429. PGUIDNOTIFICATION GNEntry
  430. )
  431. {
  432. ULONG RefCount;
  433. #if DBG
  434. ULONG i;
  435. #endif
  436. WmipEnterPMCritSection();
  437. RefCount = InterlockedDecrement(&GNEntry->RefCount);
  438. if (RefCount == 0)
  439. {
  440. RemoveEntryList(&GNEntry->GNList);
  441. WmipLeavePMCritSection();
  442. #if DBG
  443. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  444. {
  445. WmipAssert(GNEntry->Notifyee[i].DeliveryInfo == NULL);
  446. }
  447. #endif
  448. if (GNEntry->NotifyeeCount != STATIC_NOTIFYEE_COUNT)
  449. {
  450. WmipFree(GNEntry->Notifyee);
  451. }
  452. WmipFreeGNEntry(GNEntry);
  453. } else {
  454. WmipLeavePMCritSection();
  455. }
  456. }
  457. PGUIDNOTIFICATION
  458. WmipFindGuidNotification(
  459. LPGUID Guid
  460. )
  461. {
  462. PLIST_ENTRY GNList;
  463. PGUIDNOTIFICATION GNEntry;
  464. WmipEnterPMCritSection();
  465. GNList = WmipGNHead.Flink;
  466. while (GNList != &WmipGNHead)
  467. {
  468. GNEntry = (PGUIDNOTIFICATION)CONTAINING_RECORD(GNList,
  469. GUIDNOTIFICATION,
  470. GNList);
  471. if (IsEqualGUID(Guid, &GNEntry->Guid))
  472. {
  473. WmipAssert(GNEntry->RefCount > 0);
  474. WmipReferenceGNEntry(GNEntry);
  475. WmipLeavePMCritSection();
  476. return(GNEntry);
  477. }
  478. GNList = GNList->Flink;
  479. }
  480. WmipLeavePMCritSection();
  481. return(NULL);
  482. }
  483. ULONG
  484. WmipAddToGNList(
  485. LPGUID Guid,
  486. PVOID DeliveryInfo,
  487. ULONG_PTR DeliveryContext,
  488. ULONG Flags,
  489. HANDLE GuidHandle
  490. )
  491. {
  492. PGUIDNOTIFICATION GNEntry;
  493. ULONG NewCount;
  494. PNOTIFYEE NewNotifyee;
  495. BOOLEAN AllFull;
  496. ULONG EmptySlot;
  497. ULONG i;
  498. #if DBG
  499. CHAR s[MAX_PATH];
  500. #endif
  501. WmipEnterPMCritSection();
  502. GNEntry = WmipFindGuidNotification(Guid);
  503. if (GNEntry == NULL)
  504. {
  505. GNEntry = WmipAllocGNEntry();
  506. if (GNEntry == NULL)
  507. {
  508. WmipLeavePMCritSection();
  509. return(ERROR_NOT_ENOUGH_MEMORY);
  510. }
  511. memset(GNEntry, 0, sizeof(GUIDNOTIFICATION));
  512. GNEntry->Guid = *Guid;
  513. GNEntry->RefCount = 1;
  514. GNEntry->NotifyeeCount = STATIC_NOTIFYEE_COUNT;
  515. GNEntry->Notifyee = GNEntry->StaticNotifyee;
  516. InsertHeadList(&WmipGNHead, &GNEntry->GNList);
  517. }
  518. //
  519. // We have got a GUIDNOTIFICATION by newly allocating one or by finding
  520. // an existing one.
  521. AllFull = TRUE;
  522. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  523. {
  524. if (GNEntry->Notifyee[i].DeliveryInfo == DeliveryInfo)
  525. {
  526. WmipDebugPrint(("WMI: Duplicate Notification Enable for guid %s, 0x%x\n",
  527. GuidToStringA(s, Guid), DeliveryInfo));
  528. WmipLeavePMCritSection();
  529. WmipDereferenceGNEntry(GNEntry);
  530. return(ERROR_WMI_ALREADY_ENABLED);
  531. } else if (AllFull && (GNEntry->Notifyee[i].DeliveryInfo == NULL)) {
  532. EmptySlot = i;
  533. AllFull = FALSE;
  534. }
  535. }
  536. if (! AllFull)
  537. {
  538. GNEntry->Notifyee[EmptySlot].DeliveryInfo = DeliveryInfo;
  539. GNEntry->Notifyee[EmptySlot].DeliveryContext = DeliveryContext;
  540. GNEntry->Notifyee[EmptySlot].Flags = Flags;
  541. GNEntry->Notifyee[EmptySlot].GuidHandle = GuidHandle;
  542. WmipLeavePMCritSection();
  543. return(ERROR_SUCCESS);
  544. }
  545. //
  546. // All Notifyee structs are full so allocate a new chunk
  547. NewCount = GNEntry->NotifyeeCount * 2;
  548. NewNotifyee = WmipAlloc(NewCount * sizeof(NOTIFYEE));
  549. if (NewNotifyee == NULL)
  550. {
  551. WmipLeavePMCritSection();
  552. WmipDereferenceGNEntry(GNEntry);
  553. return(ERROR_NOT_ENOUGH_MEMORY);
  554. }
  555. memset(NewNotifyee, 0, NewCount * sizeof(NOTIFYEE));
  556. memcpy(NewNotifyee, GNEntry->Notifyee,
  557. GNEntry->NotifyeeCount * sizeof(NOTIFYEE));
  558. if (GNEntry->NotifyeeCount != STATIC_NOTIFYEE_COUNT)
  559. {
  560. WmipFree(GNEntry->Notifyee);
  561. }
  562. GNEntry->Notifyee = NewNotifyee;
  563. GNEntry->NotifyeeCount = NewCount;
  564. GNEntry->Notifyee[i].DeliveryInfo = DeliveryInfo;
  565. GNEntry->Notifyee[i].DeliveryContext = DeliveryContext;
  566. GNEntry->Notifyee[i].Flags = Flags;
  567. GNEntry->Notifyee[i].GuidHandle = GuidHandle;
  568. WmipLeavePMCritSection();
  569. return(ERROR_SUCCESS);
  570. }
  571. ULONG
  572. WmipRemoveFromGNList(
  573. LPGUID Guid,
  574. PVOID DeliveryInfo,
  575. BOOLEAN ActuallyRemove
  576. )
  577. {
  578. PGUIDNOTIFICATION GNEntry;
  579. ULONG i;
  580. ULONG Count;
  581. ULONG Status;
  582. GNEntry = WmipFindGuidNotification(Guid);
  583. if (GNEntry != NULL)
  584. {
  585. Status = ERROR_INVALID_PARAMETER;
  586. Count = 0;
  587. WmipEnterPMCritSection();
  588. for (i = 0; i < GNEntry->NotifyeeCount; i++)
  589. {
  590. if ((DeliveryInfo != NULL) &&
  591. (GNEntry->Notifyee[i].DeliveryInfo == DeliveryInfo) &&
  592. (Status != ERROR_SUCCESS))
  593. {
  594. if (ActuallyRemove)
  595. {
  596. GNEntry->Notifyee[i].DeliveryInfo = NULL;
  597. WmipCloseHandle(GNEntry->Notifyee[i].GuidHandle);
  598. WmipDereferenceGNEntry(GNEntry);
  599. }
  600. Status = ERROR_SUCCESS;
  601. break;
  602. } else if (GNEntry->Notifyee[i].DeliveryInfo != NULL) {
  603. Count++;
  604. }
  605. }
  606. //
  607. // This hack will allow removal from the GNLIST in the case that the
  608. // passed DeliveryInfo does not match the DeliveryInfo in the GNEntry.
  609. // This is allowed only when there is only one NOTIFYEE in the GNENTRY
  610. // In the past we only supported one notifyee per guid in a process
  611. // and so we allowed the caller not to pass a valid DeliveryInfo when
  612. // unrefistering.
  613. if ((Status != ERROR_SUCCESS) &&
  614. (GNEntry->NotifyeeCount == STATIC_NOTIFYEE_COUNT) &&
  615. (GNEntry->Notifyee[0].DeliveryInfo != NULL) &&
  616. Count == 1)
  617. {
  618. if (ActuallyRemove)
  619. {
  620. GNEntry->Notifyee[0].DeliveryInfo = NULL;
  621. WmipCloseHandle(GNEntry->Notifyee[0].GuidHandle);
  622. WmipDereferenceGNEntry(GNEntry);
  623. }
  624. Status = ERROR_SUCCESS;
  625. }
  626. WmipLeavePMCritSection();
  627. WmipDereferenceGNEntry(GNEntry);
  628. } else {
  629. Status = ERROR_WMI_ALREADY_DISABLED;
  630. }
  631. return(Status);
  632. }
  633. PVOID WmipAllocDontFail(
  634. ULONG SizeNeeded,
  635. BOOLEAN *HoldCritSect
  636. )
  637. {
  638. PVOID Buffer;
  639. do
  640. {
  641. Buffer = WmipAlloc(SizeNeeded);
  642. if (Buffer != NULL)
  643. {
  644. return(Buffer);
  645. }
  646. //
  647. // Out of memory so we'll sleep and hope that things will get
  648. // better later
  649. //
  650. if (*HoldCritSect)
  651. {
  652. //
  653. // If we are holding the PM critical section then we need
  654. // to release it. The caller is going to need to check if
  655. // the critical section was released and if so then deal
  656. // with it
  657. //
  658. *HoldCritSect = FALSE;
  659. WmipLeavePMCritSection();
  660. }
  661. WmipSleep(250);
  662. } while (1);
  663. }
  664. void WmipProcessEventBuffer(
  665. PUCHAR Buffer,
  666. ULONG ReturnSize,
  667. PUCHAR *PrimaryBuffer,
  668. ULONG *PrimaryBufferSize,
  669. PUCHAR *BackupBuffer,
  670. ULONG *BackupBufferSize,
  671. BOOLEAN ReallocateBuffers
  672. )
  673. {
  674. PWNODE_TOO_SMALL WnodeTooSmall;
  675. ULONG SizeNeeded;
  676. BOOLEAN HoldCritSection;
  677. WnodeTooSmall = (PWNODE_TOO_SMALL)Buffer;
  678. if ((ReturnSize == sizeof(WNODE_TOO_SMALL)) &&
  679. (WnodeTooSmall->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL))
  680. {
  681. //
  682. // The buffer passed to kernel mode was too small
  683. // so we need to make it larger and then try the
  684. // request again.
  685. //
  686. if (ReallocateBuffers)
  687. {
  688. //
  689. // Only do this if the caller is prepared for us to
  690. // allocate a new set of buffers
  691. //
  692. SizeNeeded = WnodeTooSmall->SizeNeeded;
  693. WmipAssert(*PrimaryBuffer != NULL);
  694. WmipFree(*PrimaryBuffer);
  695. WmipDebugPrint(("WMI: [%x - %x] Free primary %x\n",
  696. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  697. *PrimaryBuffer));
  698. HoldCritSection = FALSE;
  699. *PrimaryBuffer = WmipAllocDontFail(SizeNeeded, &HoldCritSection);
  700. WmipDebugPrint(("WMI: [%x - %x] Realloc primary %x\n",
  701. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  702. *PrimaryBuffer));
  703. *PrimaryBufferSize = SizeNeeded;
  704. WmipAssert(*BackupBuffer != NULL);
  705. WmipFree(*BackupBuffer);
  706. WmipDebugPrint(("WMI: [%x - %x] Free backup %x\n",
  707. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  708. *BackupBuffer));
  709. HoldCritSection = FALSE;
  710. *BackupBuffer = WmipAllocDontFail(SizeNeeded, &HoldCritSection);
  711. WmipDebugPrint(("WMI: [%x - %x] Realloc backup %x\n",
  712. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  713. *BackupBuffer));
  714. *BackupBufferSize = SizeNeeded;
  715. }
  716. } else if (ReturnSize >= sizeof(WNODE_HEADER)) {
  717. //
  718. // The buffer return from kernel looks good so go and
  719. // deliver the events returned
  720. //
  721. WmipDeliverAllEvents(Buffer, ReturnSize);
  722. } else {
  723. //
  724. // If this completes successfully then we expect a decent size, but
  725. // we didn't get one
  726. //
  727. WmipDebugPrint(("WMI: Bad size 0x%x returned for notification query %p\n",
  728. ReturnSize, Buffer));
  729. WmipAssert(FALSE);
  730. }
  731. }
  732. ULONG
  733. WmipReceiveNotifications(
  734. IN ULONG HandleCount,
  735. IN HANDLE *HandleList,
  736. IN NOTIFICATIONCALLBACK Callback,
  737. IN ULONG_PTR DeliveryContext,
  738. IN BOOLEAN IsAnsi,
  739. IN ULONG Action,
  740. IN PUSER_THREAD_START_ROUTINE UserModeCallback,
  741. IN HANDLE ProcessHandle
  742. )
  743. {
  744. ULONG Status;
  745. ULONG ReturnSize;
  746. PWMIRECEIVENOTIFICATION RcvNotification;
  747. ULONG RcvNotificationSize;
  748. PUCHAR Buffer;
  749. ULONG BufferSize;
  750. PWNODE_TOO_SMALL WnodeTooSmall;
  751. PWNODE_HEADER Wnode;
  752. ULONG i;
  753. ULONG Linkage;
  754. if (HandleCount == 0)
  755. {
  756. WmipSetLastError(ERROR_INVALID_PARAMETER);
  757. return(ERROR_INVALID_PARAMETER);
  758. }
  759. RcvNotificationSize = sizeof(WMIRECEIVENOTIFICATION) +
  760. ((HandleCount-1) * sizeof(HANDLE3264));
  761. RcvNotification = WmipAlloc(RcvNotificationSize);
  762. if (RcvNotification != NULL)
  763. {
  764. Status = ERROR_SUCCESS;
  765. RcvNotification->Action = Action;
  766. WmipSetPVoid3264(RcvNotification->UserModeCallback, UserModeCallback);
  767. WmipSetHandle3264(RcvNotification->UserModeProcess, ProcessHandle);
  768. RcvNotification->HandleCount = HandleCount;
  769. for (i = 0; i < HandleCount; i++)
  770. {
  771. try
  772. {
  773. RcvNotification->Handles[i].Handle = HandleList[i];
  774. } except(EXCEPTION_EXECUTE_HANDLER) {
  775. Status = ERROR_INVALID_PARAMETER;
  776. break;
  777. }
  778. }
  779. BufferSize = 0x1000;
  780. Status = ERROR_INSUFFICIENT_BUFFER;
  781. while (Status == ERROR_INSUFFICIENT_BUFFER)
  782. {
  783. Buffer = WmipAlloc(BufferSize);
  784. if (Buffer != NULL)
  785. {
  786. Status = WmipSendWmiKMRequest(NULL,
  787. IOCTL_WMI_RECEIVE_NOTIFICATIONS,
  788. RcvNotification,
  789. RcvNotificationSize,
  790. Buffer,
  791. BufferSize,
  792. &ReturnSize,
  793. NULL);
  794. if (Status == ERROR_SUCCESS)
  795. {
  796. WnodeTooSmall = (PWNODE_TOO_SMALL)Buffer;
  797. if ((ReturnSize == sizeof(WNODE_TOO_SMALL)) &&
  798. (WnodeTooSmall->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL))
  799. {
  800. //
  801. // The buffer passed to kernel mode was too small
  802. // so we need to make it larger and then try the
  803. // request again
  804. //
  805. BufferSize = WnodeTooSmall->SizeNeeded;
  806. Status = ERROR_INSUFFICIENT_BUFFER;
  807. } else {
  808. //
  809. // We got a buffer of notifications so lets go
  810. // process them and callback the caller
  811. //
  812. Wnode = (PWNODE_HEADER)Buffer;
  813. do
  814. {
  815. Linkage = Wnode->Linkage;
  816. Wnode->Linkage = 0;
  817. if (Wnode->Flags & WNODE_FLAG_INTERNAL)
  818. {
  819. //
  820. // Go and process the internal
  821. // notification
  822. //
  823. WmipInternalNotification(Wnode,
  824. Callback,
  825. DeliveryContext,
  826. IsAnsi);
  827. } else {
  828. if (IsAnsi)
  829. {
  830. //
  831. // Caller wants ansi notification - convert
  832. // instance names
  833. //
  834. WmipConvertEventToAnsi(Wnode);
  835. }
  836. //
  837. // Now go and deliver this event
  838. //
  839. WmipExternalNotification(Callback,
  840. DeliveryContext,
  841. Wnode);
  842. }
  843. Wnode = (PWNODE_HEADER)OffsetToPtr(Wnode, Linkage);
  844. } while (Linkage != 0);
  845. }
  846. }
  847. WmipFree(Buffer);
  848. } else {
  849. Status = ERROR_NOT_ENOUGH_MEMORY;
  850. }
  851. }
  852. WmipFree(RcvNotification);
  853. } else {
  854. Status = ERROR_NOT_ENOUGH_MEMORY;
  855. }
  856. WmipSetLastError(Status);
  857. return(Status);
  858. }
  859. void WmipMakeEventCallbacks(
  860. IN PWNODE_HEADER Wnode,
  861. IN NOTIFICATIONCALLBACK Callback,
  862. IN ULONG_PTR DeliveryContext,
  863. IN BOOLEAN IsAnsi
  864. )
  865. {
  866. WmipAssert((Wnode->Flags & WNODE_FLAG_INTERNAL) == 0);
  867. if (Callback == NULL)
  868. {
  869. //
  870. // This event needs to be sent to all consumers
  871. //
  872. WmipDeliverAllEvents((PUCHAR)Wnode,
  873. Wnode->BufferSize);
  874. } else {
  875. //
  876. // This event is targetted at a specific consumer
  877. //
  878. if (IsAnsi)
  879. {
  880. //
  881. // Caller wants ansi notification - convert
  882. // instance names
  883. //
  884. WmipConvertEventToAnsi(Wnode);
  885. }
  886. //
  887. // Now go and deliver this event
  888. //
  889. WmipExternalNotification(Callback,
  890. DeliveryContext,
  891. Wnode);
  892. }
  893. }
  894. //
  895. // These globals are essentially parameters passed from the thread crating
  896. // the event pump. The creating thread will alloc all of these resources
  897. // so that it can know whether the pump thread will be successful or not.
  898. // If we ever wanted to be able to have multiple pump threads then we'd
  899. // need to move the globals into a structure and pass the struructure to the
  900. // pump thread.
  901. //
  902. HANDLE WmipEventDeviceHandle;
  903. HANDLE WmipPumpCommandEvent;
  904. HANDLE WmipMyProcessHandle;
  905. OVERLAPPED WmipOverlapped1, WmipOverlapped2;
  906. PUCHAR WmipEventBuffer1, WmipEventBuffer2;
  907. ULONG WmipEventBufferSize1, WmipEventBufferSize2;
  908. //
  909. // How long to wait before the event pump thread times out
  910. //
  911. #if DBG
  912. #define EVENT_NOTIFICATION_WAIT (5 * 1000)
  913. #else
  914. #define EVENT_NOTIFICATION_WAIT (5 * 60 * 1000)
  915. #endif
  916. ULONG WmipEventNotificationWait = EVENT_NOTIFICATION_WAIT;
  917. typedef enum
  918. {
  919. EVENT_PUMP_ZERO, // Pump thread has not been started yet
  920. EVENT_PUMP_IDLE, // Pump thread was started, but then exited
  921. EVENT_PUMP_RUNNING, // Pump thread is running
  922. EVENT_PUMP_STOPPING // Pump thread is in process of stopping
  923. } EVENTPUMPSTATE, *PEVENTPUMPSTATE;
  924. EVENTPUMPSTATE WmipPumpState = EVENT_PUMP_ZERO;
  925. BOOLEAN WmipNewPumpThreadPending;
  926. #define WmipSendPumpCommand() WmipSetEvent(WmipPumpCommandEvent);
  927. #define WmipIsPumpStopping() \
  928. ((WmipPumpState == EVENT_PUMP_STOPPING) ? TRUE : FALSE)
  929. void WmipBuildReceiveNotification(
  930. PUCHAR *BufferPtr,
  931. ULONG *BufferSizePtr,
  932. ULONG *RequestSize,
  933. ULONG Action,
  934. HANDLE ProcessHandle
  935. )
  936. {
  937. ULONG GuidCount;
  938. PUCHAR Buffer;
  939. ULONG BufferSize;
  940. PLIST_ENTRY GuidNotificationList;
  941. PGUIDNOTIFICATION GuidNotification;
  942. PWMIRECEIVENOTIFICATION ReceiveNotification;
  943. ULONG SizeNeeded;
  944. ULONG i;
  945. PNOTIFYEE Notifyee;
  946. ULONG ReturnSize;
  947. ULONG Status;
  948. BOOLEAN HoldCritSection;
  949. BOOLEAN HaveGroupHandle;
  950. Buffer = *BufferPtr;
  951. BufferSize = *BufferSizePtr;
  952. ReceiveNotification = (PWMIRECEIVENOTIFICATION)Buffer;
  953. TryAgain:
  954. GuidCount = 0;
  955. SizeNeeded = FIELD_OFFSET(WMIRECEIVENOTIFICATION, Handles);
  956. //
  957. // Loop over all guid notifications and build an ioctl request for
  958. // all of them
  959. //
  960. WmipEnterPMCritSection();
  961. GuidNotificationList = WmipGNHead.Flink;
  962. while (GuidNotificationList != &WmipGNHead)
  963. {
  964. GuidNotification = CONTAINING_RECORD(GuidNotificationList,
  965. GUIDNOTIFICATION,
  966. GNList);
  967. HaveGroupHandle = FALSE;
  968. for (i = 0; i < GuidNotification->NotifyeeCount; i++)
  969. {
  970. Notifyee = &GuidNotification->Notifyee[i];
  971. if ((Notifyee->DeliveryInfo != NULL) &&
  972. ((! HaveGroupHandle) ||
  973. ((Notifyee->Flags & NOTIFICATION_FLAG_GROUPED_EVENT) == 0)))
  974. {
  975. //
  976. // If there is an active handle in the notifyee slot
  977. // and we either have not already inserted the group
  978. // handle for this guid or the slot is not part of the
  979. // guid group, then we insert the handle into the list
  980. //
  981. SizeNeeded += sizeof(HANDLE3264);
  982. if (SizeNeeded > BufferSize)
  983. {
  984. //
  985. // We need to grow the size of the buffer. Alloc a
  986. // bigger buffer, copy over
  987. //
  988. BufferSize *= 2;
  989. HoldCritSection = TRUE;
  990. Buffer = WmipAllocDontFail(BufferSize, &HoldCritSection);
  991. memcpy(Buffer, ReceiveNotification, *BufferSizePtr);
  992. WmipFree(*BufferPtr);
  993. *BufferPtr = Buffer;
  994. *BufferSizePtr = BufferSize;
  995. ReceiveNotification = (PWMIRECEIVENOTIFICATION)Buffer;
  996. if (! HoldCritSection)
  997. {
  998. //
  999. // Critical section was released within
  1000. // WmipAllocDontFail since we had to block. So
  1001. // everything could have changed. We need to go
  1002. // back and start over again
  1003. //
  1004. goto TryAgain;
  1005. }
  1006. }
  1007. WmipSetHandle3264(ReceiveNotification->Handles[GuidCount],
  1008. Notifyee->GuidHandle);
  1009. GuidCount++;
  1010. if (Notifyee->Flags & NOTIFICATION_FLAG_GROUPED_EVENT)
  1011. {
  1012. //
  1013. // This was a guid group handle and we did insert
  1014. // it into the list so we don't want to insert it
  1015. // again
  1016. //
  1017. HaveGroupHandle = TRUE;
  1018. }
  1019. }
  1020. }
  1021. GuidNotificationList = GuidNotificationList->Flink;
  1022. }
  1023. WmipLeavePMCritSection();
  1024. ReceiveNotification->HandleCount = GuidCount;
  1025. ReceiveNotification->Action = Action;
  1026. WmipSetPVoid3264(ReceiveNotification->UserModeCallback, WmipEventPumpFromKernel);
  1027. WmipSetHandle3264(ReceiveNotification->UserModeProcess, ProcessHandle);
  1028. *RequestSize = SizeNeeded;
  1029. }
  1030. #if _MSC_FULL_VER >= 13008827
  1031. #pragma warning(push)
  1032. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  1033. #endif
  1034. ULONG WmipEventPump(
  1035. PVOID Param
  1036. )
  1037. {
  1038. LPOVERLAPPED ActiveOverlapped, DeadOverlapped;
  1039. LPOVERLAPPED PrimaryOverlapped;
  1040. LPOVERLAPPED BackupOverlapped;
  1041. PUCHAR ActiveBuffer, DeadBuffer;
  1042. ULONG ActiveBufferSize, DeadBufferSize;
  1043. PUCHAR PrimaryBuffer, BackupBuffer;
  1044. ULONG PrimaryBufferSize, BackupBufferSize;
  1045. ULONG ReturnSize, DeadReturnSize;
  1046. ULONG Status, WaitStatus;
  1047. HANDLE HandleArray[2];
  1048. ULONG RequestSize;
  1049. WmipDebugPrint(("WMI: [%x - %x] New pump thread is started\n",
  1050. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1051. //
  1052. // We need to hold off on letting the thread into the routine until
  1053. // the previous pump thread has had a chance to finish. This could
  1054. // occur if a GN is added/removed while the previous thread is
  1055. // finishing up or if an event is received as the previous thread
  1056. // is finishing up.
  1057. //
  1058. while (WmipIsPumpStopping())
  1059. {
  1060. //
  1061. // wait 50ms for the previous thread to finish up
  1062. //
  1063. WmipDebugPrint(("WMI: [%x - %x] waiting for pump to be stopped\n",
  1064. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1065. WmipSleep(50);
  1066. }
  1067. //
  1068. // Next thing to do is to make sure that another pump thread isn't
  1069. // already running. This can happen in the case that both a GN is
  1070. // added or removed and an event reaches kernel and the kernel
  1071. // creates a new thread too. Right here we only let one of them
  1072. // win.
  1073. //
  1074. WmipEnterPMCritSection();
  1075. if ((WmipPumpState != EVENT_PUMP_IDLE) &&
  1076. (WmipPumpState != EVENT_PUMP_ZERO))
  1077. {
  1078. WmipDebugPrint(("WMI: [%x - %x] Exit pump since state is %d\n",
  1079. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  1080. WmipPumpState));
  1081. WmipLeavePMCritSection();
  1082. WmipExitThread(0);
  1083. } else {
  1084. WmipDebugPrint(("WMI: [%x - %x] pump thread now running\n",
  1085. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1086. WmipPumpState = EVENT_PUMP_RUNNING;
  1087. WmipNewPumpThreadPending = FALSE;
  1088. WmipLeavePMCritSection();
  1089. }
  1090. //
  1091. // Make sure we have all resources we'll need to pump out events
  1092. // since there is no way that we can return an error to the original
  1093. // caller since we are on a new thread
  1094. //
  1095. WmipAssert(WmipEventDeviceHandle != NULL);
  1096. WmipAssert(WmipPumpCommandEvent != NULL);
  1097. WmipAssert(WmipMyProcessHandle != NULL);
  1098. WmipAssert(WmipEventBuffer1 != NULL);
  1099. WmipAssert(WmipEventBuffer2 != NULL);
  1100. WmipAssert(WmipOverlapped1.hEvent != NULL);
  1101. WmipAssert(WmipOverlapped2.hEvent != NULL);
  1102. ActiveOverlapped = NULL;
  1103. PrimaryOverlapped = &WmipOverlapped1;
  1104. PrimaryBuffer = WmipEventBuffer1;
  1105. PrimaryBufferSize = WmipEventBufferSize1;
  1106. BackupOverlapped = &WmipOverlapped2;
  1107. BackupBuffer = WmipEventBuffer2;
  1108. BackupBufferSize = WmipEventBufferSize2;
  1109. WmipDebugPrint(("WMI: [%x - %x] 1 buffer is %x, 2 buffer id %x\n",
  1110. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  1111. WmipEventBuffer1, WmipEventBuffer2));
  1112. HandleArray[0] = WmipPumpCommandEvent;
  1113. while(TRUE)
  1114. {
  1115. //
  1116. // Build request to receive events for all guids that are
  1117. // registered
  1118. //
  1119. WmipEnterPMCritSection();
  1120. if (IsListEmpty(&WmipGNHead))
  1121. {
  1122. //
  1123. // There are no events to be received so we cancel any
  1124. // outstanding requests and quietly exit this thread. Note
  1125. // that once we leave the critsec there could be another
  1126. // pump thread running so all we can do after that is exit.
  1127. //
  1128. WmipCancelIo(WmipEventDeviceHandle);
  1129. //
  1130. // Enter the idle state which implies that all of the
  1131. // pump resources stay allocated when the thread is not
  1132. // running
  1133. //
  1134. WmipEventBuffer1 = PrimaryBuffer;
  1135. WmipEventBufferSize1 = PrimaryBufferSize;
  1136. WmipEventBuffer2 = BackupBuffer;
  1137. WmipEventBufferSize2 = BackupBufferSize;
  1138. WmipPumpState = EVENT_PUMP_IDLE;
  1139. WmipDebugPrint(("WMI: [%x - %x] No more GN, pump exiting, pump state EVENT_PUMP_IDLE\n",
  1140. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1141. WmipLeavePMCritSection();
  1142. WmipExitThread(0);
  1143. }
  1144. WmipLeavePMCritSection();
  1145. if (ActiveOverlapped != NULL)
  1146. {
  1147. //
  1148. // If there was a previously outstanding request then
  1149. // we remember it and switch to the backup overlapped and
  1150. // and data buffer
  1151. //
  1152. DeadOverlapped = ActiveOverlapped;
  1153. DeadBuffer = ActiveBuffer;
  1154. DeadBufferSize = ActiveBufferSize;
  1155. //
  1156. // The request being mooted should be the current primary
  1157. //
  1158. WmipAssert(DeadOverlapped == PrimaryOverlapped);
  1159. WmipAssert(DeadBuffer == PrimaryBuffer);
  1160. //
  1161. // Use the backup request as the new primary
  1162. //
  1163. WmipAssert(BackupOverlapped != NULL);
  1164. WmipAssert(BackupBuffer != NULL);
  1165. PrimaryOverlapped = BackupOverlapped;
  1166. PrimaryBuffer = BackupBuffer;
  1167. PrimaryBufferSize = BackupBufferSize;
  1168. BackupOverlapped = NULL;
  1169. BackupBuffer = NULL;
  1170. } else {
  1171. //
  1172. // If there is no outstanding request then we don't worry about
  1173. // it
  1174. //
  1175. DeadOverlapped = NULL;
  1176. }
  1177. //
  1178. // Build and send the request down to kernel to receive events
  1179. //
  1180. RebuildRequest:
  1181. WmipBuildReceiveNotification(&PrimaryBuffer,
  1182. &PrimaryBufferSize,
  1183. &RequestSize,
  1184. WmipIsPumpStopping() ? RECEIVE_ACTION_CREATE_THREAD :
  1185. RECEIVE_ACTION_NONE,
  1186. WmipMyProcessHandle);
  1187. ActiveOverlapped = PrimaryOverlapped;
  1188. ActiveBuffer = PrimaryBuffer;
  1189. ActiveBufferSize = PrimaryBufferSize;
  1190. Status = WmipSendWmiKMRequest(WmipEventDeviceHandle,
  1191. IOCTL_WMI_RECEIVE_NOTIFICATIONS,
  1192. ActiveBuffer,
  1193. RequestSize,
  1194. ActiveBuffer,
  1195. ActiveBufferSize,
  1196. &ReturnSize,
  1197. ActiveOverlapped);
  1198. if (DeadOverlapped != NULL)
  1199. {
  1200. if ((Status != ERROR_SUCCESS) &&
  1201. (Status != ERROR_IO_PENDING) &&
  1202. (Status != ERROR_OPERATION_ABORTED))
  1203. {
  1204. //
  1205. // There was a previous request which won't be cleared
  1206. // unless the new request returns pending, cancelled
  1207. // or success. So if the new request returns something
  1208. // else then we need to retry the request
  1209. //
  1210. WmipDebugPrint(("WMI: Event Poll error %d\n", Status));
  1211. WmipSleep(100);
  1212. goto RebuildRequest;
  1213. }
  1214. //
  1215. // The new request should have caused the old one to
  1216. // be completed
  1217. //
  1218. if (WmipGetOverlappedResult(WmipEventDeviceHandle,
  1219. DeadOverlapped,
  1220. &DeadReturnSize,
  1221. TRUE))
  1222. {
  1223. //
  1224. // The dead request did succeed and was not failed by
  1225. // the receipt of the new request. This is a unlikely
  1226. // race condition where the requests crossed paths. So we
  1227. // need to process the events returned in the dead request.
  1228. // Now if the buffer returned was a WNODE_TOO_SMALL we want
  1229. // to ignore it at this point since we are not at a
  1230. // good position to reallocate the buffers - the
  1231. // primary buffer is already attached to the new
  1232. // request. That request is also going to return a
  1233. // WNODE_TOO_SMALL and in the processing of that one we will
  1234. // grow the buffers. So it is safe to ignore here.
  1235. // However we will still need to dispatch any real
  1236. // events received as they have been purged from KM.
  1237. //
  1238. if (DeadReturnSize != 0)
  1239. {
  1240. WmipProcessEventBuffer(DeadBuffer,
  1241. DeadReturnSize,
  1242. &PrimaryBuffer,
  1243. &PrimaryBufferSize,
  1244. &BackupBuffer,
  1245. &BackupBufferSize,
  1246. FALSE);
  1247. } else {
  1248. WmipAssert(WmipIsPumpStopping());
  1249. }
  1250. }
  1251. //
  1252. // Now make the completed request the backup request
  1253. //
  1254. WmipAssert(BackupOverlapped == NULL);
  1255. WmipAssert(BackupBuffer == NULL);
  1256. BackupOverlapped = DeadOverlapped;
  1257. BackupBuffer = DeadBuffer;
  1258. BackupBufferSize = DeadBufferSize;
  1259. }
  1260. if (Status == ERROR_IO_PENDING)
  1261. {
  1262. //
  1263. // if the ioctl pended then we wait until either an event
  1264. // is returned or a command needs processed
  1265. //
  1266. HandleArray[1] = ActiveOverlapped->hEvent;
  1267. WaitStatus = WmipWaitForMultipleObjectsEx(2,
  1268. HandleArray,
  1269. FALSE,
  1270. WmipEventNotificationWait,
  1271. TRUE);
  1272. WmipDebugPrint(("WMI: [%x - %x] Done Waiting for RCV or command -> %d\n",
  1273. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  1274. WaitStatus));
  1275. } else {
  1276. //
  1277. // the ioctl completed immediately so we fake out the wait
  1278. //
  1279. WaitStatus = WAIT_OBJECT_0 + 1;
  1280. }
  1281. if (WaitStatus == WAIT_OBJECT_0 + 1)
  1282. {
  1283. if (Status == ERROR_IO_PENDING)
  1284. {
  1285. if (WmipGetOverlappedResult(WmipEventDeviceHandle,
  1286. ActiveOverlapped,
  1287. &ReturnSize,
  1288. TRUE))
  1289. {
  1290. Status = ERROR_SUCCESS;
  1291. } else {
  1292. Status = WmipGetLastError();
  1293. }
  1294. }
  1295. if (Status == ERROR_SUCCESS)
  1296. {
  1297. //
  1298. // We received some events from KM so we want to go and
  1299. // process them. If we got a WNODE_TOO_SMALL then the
  1300. // primary and backup buffers will get reallocated with
  1301. // the new size that is needed.
  1302. //
  1303. WmipDebugPrint(("WMI: [%x - %x] Process Active overlapped %p events %p (0x%x)\n",
  1304. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  1305. ActiveOverlapped, ActiveBuffer, ReturnSize));
  1306. if (ReturnSize != 0)
  1307. {
  1308. WmipProcessEventBuffer(ActiveBuffer,
  1309. ReturnSize,
  1310. &PrimaryBuffer,
  1311. &PrimaryBufferSize,
  1312. &BackupBuffer,
  1313. &BackupBufferSize,
  1314. TRUE);
  1315. //
  1316. // In the case that we are shutting down the event
  1317. // pump and the buffer passed to clear out all of
  1318. // the events was too small we need to call back
  1319. // down to the kernel to get the rest of the events
  1320. // since we cannot exit the thread with events that
  1321. // are not delivered. The kernel will not set the
  1322. // flag that a new thread is needed unless the irp
  1323. // clears all outstanding events
  1324. //
  1325. } else {
  1326. WmipAssert(WmipIsPumpStopping());
  1327. if (WmipIsPumpStopping())
  1328. {
  1329. //
  1330. // The irp just completed should have not only
  1331. // just cleared all events out of kernel mode
  1332. // but also setup flags that new events should
  1333. // cause a new pump thread to be created. So
  1334. // there may be a new pump thread already created
  1335. // Also note there could be yet
  1336. // another event pump thread that was created
  1337. // if a GN was added or removed. Once we set
  1338. // the pump state to IDLE we are off to the
  1339. // races (See code at top of function)
  1340. //
  1341. WmipEnterPMCritSection();
  1342. WmipPumpState = EVENT_PUMP_IDLE;
  1343. WmipDebugPrint(("WMI: [%x - %x] Pump entered IDLE\n",
  1344. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1345. WmipEventBuffer1 = PrimaryBuffer;
  1346. WmipEventBufferSize1 = PrimaryBufferSize;
  1347. WmipEventBuffer2 = BackupBuffer;
  1348. WmipEventBufferSize2 = BackupBufferSize;
  1349. WmipLeavePMCritSection();
  1350. WmipExitThread(0);
  1351. }
  1352. }
  1353. } else {
  1354. //
  1355. // For some reason the request failed. All we can do is
  1356. // wait a bit and hope that the problem will clear up.
  1357. // If we are stopping the thread we still need to wait
  1358. // and try again as all events may not have been
  1359. // cleared from the kernel. We really don't know if the
  1360. // irp even made it to the kernel.
  1361. //
  1362. WmipDebugPrint(("WMI: [%x - %x] Error %d from Ioctl\n",
  1363. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  1364. Status));
  1365. WmipSleep(250);
  1366. }
  1367. //
  1368. // Flag that there is no longer a request outstanding
  1369. //
  1370. ActiveOverlapped = NULL;
  1371. } else if (WaitStatus == STATUS_TIMEOUT) {
  1372. //
  1373. // The wait for events timed out so we go into the thread
  1374. // stopping state to indicate that we are going to terminate
  1375. // the thread once all events are cleared out of kernel. At
  1376. // this point we are commited to stopping the thread. If any
  1377. // GN are added/removed after going into the stopping state,
  1378. // a new (and suspended) thread will be created. Right
  1379. // before exiting we check if that thread is pending and if
  1380. // so resume it.
  1381. //
  1382. WmipEnterPMCritSection();
  1383. WmipDebugPrint(("WMI: [%x - %x] Pump thread entering EVENT_PUMP_STOPPING\n",
  1384. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1385. WmipPumpState = EVENT_PUMP_STOPPING;
  1386. WmipLeavePMCritSection();
  1387. }
  1388. }
  1389. //
  1390. // Should never break out of infinite loop
  1391. //
  1392. WmipAssert(FALSE);
  1393. WmipExitThread(0);
  1394. }
  1395. #if _MSC_FULL_VER >= 13008827
  1396. #pragma warning(pop)
  1397. #endif
  1398. ULONG WmipEventPumpFromKernel(
  1399. PVOID Param
  1400. )
  1401. {
  1402. //
  1403. // Note that we MUST call WmipExitThread when we want to shutdown the
  1404. // thread and not return() since the thread has been created by
  1405. // kernel mode and there is nothing on the stack to return to, so
  1406. // we'd just AV
  1407. //
  1408. WmipEnterPMCritSection();
  1409. if ((WmipNewPumpThreadPending == FALSE) &&
  1410. (WmipPumpState == EVENT_PUMP_IDLE) ||
  1411. (WmipPumpState == EVENT_PUMP_STOPPING))
  1412. {
  1413. //
  1414. // If the pump is currently idle or stopping and there is not
  1415. // another pump thread that is pending we want our thread
  1416. // to be the one that gets the pump going again. We mark the
  1417. // that there is a pump thread pending which means that no more
  1418. // pump threads will be created when adding/removing GN
  1419. // and any pump threads created by kernel will just exit quickly
  1420. //
  1421. WmipNewPumpThreadPending = TRUE;
  1422. WmipDebugPrint(("WMI: [%x - %x] KM pump thread created and pump state now START PENDING\n",
  1423. WmipGetCurrentProcessId(), WmipGetCurrentThreadId()));
  1424. WmipLeavePMCritSection();
  1425. //
  1426. // ISSUE: We cannot call WmipEventPump with Param (ie, the
  1427. // parameter that is passed to this function) because when the
  1428. // thread is created by a Win64 kernel on a x86 app running
  1429. // under win64, Param is not actually passed on the stack since
  1430. // the code that creates the context forgets to do so
  1431. //
  1432. WmipExitThread(WmipEventPump(0));
  1433. }
  1434. WmipDebugPrint(("WMI: [%x - %x] KM pump thread exiting since pump state %d, %s\n",
  1435. WmipGetCurrentProcessId(), WmipGetCurrentThreadId(),
  1436. WmipPumpState,
  1437. (WmipNewPumpThreadPending == TRUE) ?
  1438. "Pump Thread Pending" : "No Pump Thread Pending"));
  1439. WmipLeavePMCritSection();
  1440. WmipExitThread(0);
  1441. return 0;
  1442. }
  1443. ULONG WmipEstablishEventPump(
  1444. )
  1445. {
  1446. #if DBG
  1447. #define INITIALEVENTBUFFERSIZE 0x38
  1448. #else
  1449. #define INITIALEVENTBUFFERSIZE 0x1000
  1450. #endif
  1451. HANDLE ThreadHandle;
  1452. CLIENT_ID ClientId;
  1453. ULONG Status;
  1454. BOOL b;
  1455. #if DBG
  1456. //
  1457. // On checked builds update the length of time to wait before a
  1458. // pump thread times out
  1459. //
  1460. /* WmipGetRegistryValue(PumpTimeoutRegValueText,
  1461. &WmipEventNotificationWait);*/
  1462. #endif
  1463. //
  1464. // Make sure the event pump thread is running. We check both the
  1465. // pump state and that the device handle is not created since there
  1466. // is a window after the handle is created and the thread starts
  1467. // running and changes the pump state
  1468. //
  1469. WmipEnterPMCritSection();
  1470. if ((WmipPumpState == EVENT_PUMP_ZERO) &&
  1471. (WmipEventDeviceHandle == NULL))
  1472. {
  1473. //
  1474. // Not only is pump not running, but the resources for it
  1475. // haven't been allocated
  1476. //
  1477. WmipAssert(WmipPumpCommandEvent == NULL);
  1478. WmipAssert(WmipMyProcessHandle == NULL);
  1479. WmipAssert(WmipOverlapped1.hEvent == NULL);
  1480. WmipAssert(WmipOverlapped2.hEvent == NULL);
  1481. WmipAssert(WmipEventBuffer1 == NULL);
  1482. WmipAssert(WmipEventBuffer2 == NULL);
  1483. //
  1484. // Preallocate all of the resources that the event pump will need
  1485. // so that it has no excuse to fail
  1486. //
  1487. WmipEventDeviceHandle = WmipCreateFileA(WMIDataDeviceName,
  1488. GENERIC_READ | GENERIC_WRITE,
  1489. 0,
  1490. NULL,
  1491. OPEN_EXISTING,
  1492. FILE_ATTRIBUTE_NORMAL |
  1493. FILE_FLAG_OVERLAPPED,
  1494. NULL);
  1495. if (WmipEventDeviceHandle == INVALID_HANDLE_VALUE)
  1496. {
  1497. Status = WmipGetLastError();
  1498. goto Cleanup;
  1499. }
  1500. WmipPumpCommandEvent = WmipCreateEventA(NULL, FALSE, FALSE, NULL);
  1501. if (WmipPumpCommandEvent == NULL)
  1502. {
  1503. Status = WmipGetLastError();
  1504. goto Cleanup;
  1505. }
  1506. b = WmipDuplicateHandle(WmipGetCurrentProcess(),
  1507. WmipGetCurrentProcess(),
  1508. WmipGetCurrentProcess(),
  1509. &WmipMyProcessHandle,
  1510. 0,
  1511. FALSE,
  1512. DUPLICATE_SAME_ACCESS);
  1513. if (! b)
  1514. {
  1515. Status = WmipGetLastError();
  1516. goto Cleanup;
  1517. }
  1518. WmipOverlapped1.hEvent = WmipCreateEventA(NULL, FALSE, FALSE, NULL);
  1519. if (WmipOverlapped1.hEvent == NULL)
  1520. {
  1521. Status = WmipGetLastError();
  1522. goto Cleanup;
  1523. }
  1524. WmipOverlapped2.hEvent = WmipCreateEventA(NULL, FALSE, FALSE, NULL);
  1525. if (WmipOverlapped2.hEvent == NULL)
  1526. {
  1527. Status = WmipGetLastError();
  1528. goto Cleanup;
  1529. }
  1530. WmipEventBuffer1 = WmipAlloc(INITIALEVENTBUFFERSIZE);
  1531. if (WmipEventBuffer1 == NULL)
  1532. {
  1533. Status = ERROR_NOT_ENOUGH_MEMORY;
  1534. goto Cleanup;
  1535. }
  1536. WmipEventBufferSize1 = INITIALEVENTBUFFERSIZE;
  1537. WmipEventBuffer2 = WmipAlloc(INITIALEVENTBUFFERSIZE);
  1538. if (WmipEventBuffer2 == NULL)
  1539. {
  1540. Status = ERROR_NOT_ENOUGH_MEMORY;
  1541. goto Cleanup;
  1542. }
  1543. WmipEventBufferSize2 = INITIALEVENTBUFFERSIZE;
  1544. ThreadHandle = WmipCreateThread(NULL,
  1545. 0,
  1546. WmipEventPump,
  1547. NULL,
  1548. 0,
  1549. (LPDWORD)&ClientId);
  1550. if (ThreadHandle != NULL)
  1551. {
  1552. WmipNewPumpThreadPending = TRUE;
  1553. WmipDebugPrint(("WMI: [%x] Created initiail pump thread %x\n",
  1554. WmipGetCurrentProcessId(), ClientId.UniqueThread
  1555. ));
  1556. WmipCloseHandle(ThreadHandle);
  1557. } else {
  1558. //
  1559. // Since we were able to allocate all of our pump
  1560. // resources, but didn't get the pump thread started,
  1561. // we will hang onto our resources and move the pump
  1562. // state to idle. In this way when the pump is started
  1563. // again we do not have to reallocate our resources
  1564. //
  1565. WmipPumpState = EVENT_PUMP_IDLE;
  1566. Status = WmipGetLastError();
  1567. goto Done;
  1568. }
  1569. WmipLeavePMCritSection();
  1570. return(ERROR_SUCCESS);
  1571. } else {
  1572. //
  1573. // Pump resources should already be allocated
  1574. //
  1575. WmipAssert(WmipPumpCommandEvent != NULL);
  1576. WmipAssert(WmipMyProcessHandle != NULL);
  1577. WmipAssert(WmipOverlapped1.hEvent != NULL);
  1578. WmipAssert(WmipOverlapped2.hEvent != NULL);
  1579. WmipAssert(WmipEventBuffer1 != NULL);
  1580. WmipAssert(WmipEventBuffer2 != NULL);
  1581. if ((WmipNewPumpThreadPending == FALSE) &&
  1582. (WmipPumpState == EVENT_PUMP_STOPPING) ||
  1583. (WmipPumpState == EVENT_PUMP_IDLE))
  1584. {
  1585. //
  1586. // If pump is stopping or is idle then we need to fire up a
  1587. // new thread
  1588. //
  1589. ThreadHandle = WmipCreateThread(NULL,
  1590. 0,
  1591. WmipEventPump,
  1592. NULL,
  1593. 0,
  1594. (LPDWORD)&ClientId);
  1595. if (ThreadHandle != NULL)
  1596. {
  1597. WmipDebugPrint(("WMI: [%x] Created new pump thread %x (%d %d)\n",
  1598. WmipGetCurrentProcessId(), ClientId.UniqueThread,
  1599. WmipPumpState, WmipNewPumpThreadPending
  1600. ));
  1601. WmipNewPumpThreadPending = TRUE;
  1602. WmipCloseHandle(ThreadHandle);
  1603. } else {
  1604. Status = WmipGetLastError();
  1605. goto Done;
  1606. }
  1607. } else {
  1608. WmipAssert((WmipPumpState == EVENT_PUMP_RUNNING) ||
  1609. (WmipNewPumpThreadPending == TRUE));
  1610. }
  1611. WmipLeavePMCritSection();
  1612. return(ERROR_SUCCESS);
  1613. }
  1614. Cleanup:
  1615. if (WmipEventDeviceHandle != NULL)
  1616. {
  1617. WmipCloseHandle(WmipEventDeviceHandle);
  1618. WmipEventDeviceHandle = NULL;
  1619. }
  1620. if (WmipPumpCommandEvent != NULL)
  1621. {
  1622. WmipCloseHandle(WmipPumpCommandEvent);
  1623. WmipPumpCommandEvent = NULL;
  1624. }
  1625. if (WmipMyProcessHandle != NULL)
  1626. {
  1627. WmipCloseHandle(WmipMyProcessHandle);
  1628. WmipMyProcessHandle = NULL;
  1629. }
  1630. if (WmipOverlapped1.hEvent != NULL)
  1631. {
  1632. WmipCloseHandle(WmipOverlapped1.hEvent);
  1633. WmipOverlapped1.hEvent = NULL;
  1634. }
  1635. if (WmipOverlapped2.hEvent != NULL)
  1636. {
  1637. WmipCloseHandle(WmipOverlapped2.hEvent);
  1638. WmipOverlapped2.hEvent = NULL;
  1639. }
  1640. if (WmipEventBuffer1 != NULL)
  1641. {
  1642. WmipFree(WmipEventBuffer1);
  1643. WmipEventBuffer1 = NULL;
  1644. }
  1645. if (WmipEventBuffer2 != NULL)
  1646. {
  1647. WmipFree(WmipEventBuffer2);
  1648. WmipEventBuffer2 = NULL;
  1649. }
  1650. Done:
  1651. WmipLeavePMCritSection();
  1652. return(Status);
  1653. }
  1654. ULONG WmipAddHandleToEventPump(
  1655. LPGUID Guid,
  1656. PVOID DeliveryInfo,
  1657. ULONG_PTR DeliveryContext,
  1658. ULONG NotificationFlags,
  1659. HANDLE GuidHandle
  1660. )
  1661. {
  1662. ULONG Status;
  1663. Status = WmipAddToGNList(Guid,
  1664. DeliveryInfo,
  1665. DeliveryContext,
  1666. NotificationFlags,
  1667. GuidHandle);
  1668. if (Status == ERROR_SUCCESS)
  1669. {
  1670. Status = WmipEstablishEventPump();
  1671. if (Status == ERROR_SUCCESS)
  1672. {
  1673. WmipSendPumpCommand();
  1674. }
  1675. }
  1676. return(Status);
  1677. }
  1678. /*
  1679. ULONG
  1680. WmipNotificationRegistration(
  1681. IN LPGUID InGuid,
  1682. IN BOOLEAN Enable,
  1683. IN PVOID DeliveryInfo,
  1684. IN ULONG_PTR DeliveryContext,
  1685. IN ULONG64 LoggerContext,
  1686. IN ULONG Flags,
  1687. IN BOOLEAN IsAnsi
  1688. )
  1689. {
  1690. HANDLE GuidHandle;
  1691. GUID Guid;
  1692. PVOID NotificationDeliveryContext;
  1693. PVOID NotificationDeliveryInfo;
  1694. ULONG NotificationFlags;
  1695. ULONG Status;
  1696. HANDLE ThreadHandle;
  1697. DWORD ThreadId;
  1698. ULONG ReturnSize;
  1699. WmipInitProcessHeap();
  1700. //
  1701. // Validate input parameters and flags
  1702. //
  1703. if (InGuid == NULL)
  1704. {
  1705. WmipSetLastError(ERROR_INVALID_PARAMETER);
  1706. return(ERROR_INVALID_PARAMETER);
  1707. }
  1708. try
  1709. {
  1710. Guid = *InGuid;
  1711. } except(EXCEPTION_EXECUTE_HANDLER) {
  1712. WmipSetLastError(ERROR_INVALID_PARAMETER);
  1713. return(ERROR_INVALID_PARAMETER);
  1714. }
  1715. if (Flags == NOTIFICATION_CHECK_ACCESS)
  1716. {
  1717. //
  1718. // Caller just wants to check that he has does have permission
  1719. // to enable the notification
  1720. //
  1721. #ifdef MEMPHIS
  1722. return(ERROR_SUCCESS);
  1723. #else
  1724. Status = WmipCheckGuidAccess(&Guid, WMIGUID_NOTIFICATION);
  1725. WmipSetLastError(Status);
  1726. return(Status);
  1727. #endif
  1728. }
  1729. //
  1730. // Validate that flags are correct
  1731. //
  1732. if (Enable)
  1733. {
  1734. if ((Flags != NOTIFICATION_TRACE_FLAG) &&
  1735. (Flags != NOTIFICATION_CALLBACK_DIRECT))
  1736. {
  1737. //
  1738. // Invalid Flags were passed
  1739. Status = ERROR_INVALID_PARAMETER;
  1740. } else if (Flags == NOTIFICATION_TRACE_FLAG) {
  1741. Status = ERROR_SUCCESS;
  1742. } else if ((Flags == NOTIFICATION_CALLBACK_DIRECT) &&
  1743. (DeliveryInfo == NULL)) {
  1744. //
  1745. // Not a valid callback function
  1746. Status = ERROR_INVALID_PARAMETER;
  1747. } else {
  1748. Status = ERROR_SUCCESS;
  1749. }
  1750. if (Status != ERROR_SUCCESS)
  1751. {
  1752. WmipSetLastError(Status);
  1753. return(Status);
  1754. }
  1755. }
  1756. NotificationDeliveryInfo = (PVOID)DeliveryInfo;
  1757. NotificationDeliveryContext = (PVOID)DeliveryContext;
  1758. NotificationFlags = IsAnsi ? DCREF_FLAG_ANSI : 0;
  1759. if (Flags & NOTIFICATION_TRACE_FLAG)
  1760. {
  1761. //
  1762. // This is a tracelog enable/disable request so send it down the
  1763. // fast lane to KM so it can be processed.
  1764. //
  1765. WMITRACEENABLEDISABLEINFO TraceEnableInfo;
  1766. TraceEnableInfo.Guid = Guid;
  1767. TraceEnableInfo.LoggerContext = LoggerContext;
  1768. TraceEnableInfo.Enable = Enable;
  1769. Status = WmipSendWmiKMRequest(NULL,
  1770. IOCTL_WMI_ENABLE_DISABLE_TRACELOG,
  1771. &TraceEnableInfo,
  1772. sizeof(WMITRACEENABLEDISABLEINFO),
  1773. NULL,
  1774. 0,
  1775. &ReturnSize,
  1776. NULL);
  1777. } else {
  1778. //
  1779. // This is a WMI event enable/disable event request so fixup the
  1780. // flags and send a request off to the event pump thread.
  1781. //
  1782. if (Flags & NOTIFICATION_CALLBACK_DIRECT) {
  1783. NotificationFlags |= NOTIFICATION_FLAG_CALLBACK_DIRECT;
  1784. } else {
  1785. NotificationFlags |= Flags;
  1786. }
  1787. if (Enable)
  1788. {
  1789. //
  1790. // Since we are enabling, make sure we have access to the
  1791. // guid and then make sure we can get the notification pump
  1792. // thread running.
  1793. //
  1794. Status = WmipOpenKernelGuid(&Guid,
  1795. WMIGUID_NOTIFICATION,
  1796. &GuidHandle,
  1797. IOCTL_WMI_OPEN_GUID_FOR_EVENTS);
  1798. if (Status == ERROR_SUCCESS)
  1799. {
  1800. Status = WmipAddHandleToEventPump(&Guid,
  1801. DeliveryInfo,
  1802. DeliveryContext,
  1803. NotificationFlags |
  1804. NOTIFICATION_FLAG_GROUPED_EVENT,
  1805. GuidHandle);
  1806. if (Status != ERROR_SUCCESS)
  1807. {
  1808. //
  1809. // if we had opened the guid handle, but enabling
  1810. // failed for some other reason, we need to make sure
  1811. // we do not leave any opened guid handles
  1812. //
  1813. WmipCloseHandle(GuidHandle);
  1814. }
  1815. }
  1816. } else {
  1817. Status = WmipRemoveFromGNList(&Guid,
  1818. DeliveryInfo,
  1819. TRUE);
  1820. if (Status == ERROR_SUCCESS)
  1821. {
  1822. WmipSendPumpCommand();
  1823. }
  1824. if (Status == ERROR_INVALID_PARAMETER)
  1825. {
  1826. CHAR s[MAX_PATH];
  1827. WmipDebugPrint(("WMI: Invalid DeliveryInfo %x passed to unregister for notification %s\n",
  1828. DeliveryInfo,
  1829. GuidToStringA(s, &Guid)));
  1830. Status = ERROR_WMI_ALREADY_DISABLED;
  1831. }
  1832. }
  1833. }
  1834. WmipSetLastError(Status);
  1835. return(Status);
  1836. }*/