master
h 882 lines 31 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #pragma once
4
5 // cpp headers
6 #include <algorithm>
7 #include <iterator>
8 #include <exception>
9 #include <memory>
10 #include <stdexcept>
11 #include <utility>
12 // os headers
13 // os headers
14 #include <Windows.h>
15 #include <objbase.h>
16 #include <oleauto.h>
17 #include <WbemIdl.h>
18 // wil headers
19 #include <wil/stl.h>
20 #include <wil/com.h>
21 #include <wil/resource.h>
22
23 #include "WmiVariant.h"
24
25 namespace wsl::core {
26 // Callers must instantiate a WmiService instance in order to use any of the Wmi* classes
27 // This class tracks the WMI initialization of the IWbemLocator and IWbemService interfaces
28 // which maintain a connection to the specified WMI Service through which WMI calls are made
29 class WmiService
30 {
31 public:
32 // CoInitializeSecurity is not called by the Wmi* classes. This security
33 // policy should be defined by the code consuming these libraries, as these
34 // libraries cannot assume the security context to apply to the process.
35 explicit WmiService(PCWSTR path)
36 {
37 m_wbemLocator = wil::CoCreateInstance<WbemLocator, IWbemLocator>();
38
39 THROW_IF_FAILED(m_wbemLocator->ConnectServer(
40 wil::make_bstr(path).get(), // Object path of WMI namespace
41 nullptr, // User name. NULL = current user
42 nullptr, // User password. NULL = current
43 nullptr, // Locale. NULL indicates current
44 0, // Security flags.
45 nullptr, // Authority (e.g. Kerberos)
46 nullptr, // Context object
47 m_wbemServices.put())); // receive pointer to IWbemServices proxy
48
49 THROW_IF_FAILED(CoSetProxyBlanket(
50 m_wbemServices.get(), // Indicates the proxy to set
51 RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
52 RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
53 nullptr, // Server principal name
54 RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
55 RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
56 nullptr, // client identity
57 EOAC_NONE)); // proxy capabilities
58 }
59
60 ~WmiService() = default;
61 WmiService(const WmiService& service) noexcept = default;
62 WmiService& operator=(const WmiService& service) noexcept = default;
63 WmiService(WmiService&& rhs) noexcept = default;
64 WmiService& operator=(WmiService&& rhs) noexcept = default;
65
66 IWbemServices* operator->() noexcept
67 {
68 return m_wbemServices.get();
69 }
70
71 const IWbemServices* operator->() const noexcept
72 {
73 return m_wbemServices.get();
74 }
75
76 bool operator==(const WmiService& service) const noexcept
77 {
78 return m_wbemLocator == service.m_wbemLocator && m_wbemServices == service.m_wbemServices;
79 }
80
81 bool operator!=(const WmiService& service) const noexcept
82 {
83 return !(*this == service);
84 }
85
86 IWbemServices* get() noexcept
87 {
88 return m_wbemServices.get();
89 }
90
91 [[nodiscard]] const IWbemServices* get() const noexcept
92 {
93 return m_wbemServices.get();
94 }
95
96 void delete_path(PCWSTR objPath, const wil::com_ptr<IWbemContext>& context) const
97 {
98 wil::com_ptr<IWbemCallResult> result;
99 THROW_IF_FAILED(m_wbemServices->DeleteInstance(
100 wil::make_bstr(objPath).get(), WBEM_FLAG_RETURN_IMMEDIATELY, context.get(), result.addressof()));
101 // wait for the call to complete
102 HRESULT status;
103 THROW_IF_FAILED(result->GetCallStatus(WBEM_INFINITE, &status));
104 THROW_IF_FAILED(status);
105 }
106
107 // Deletes the WMI object based off the object path specified in the input
108 // The object path takes the form of:
109 // MyClass.MyProperty1='33',MyProperty2='value'
110 void delete_path(PCWSTR objPath) const
111 {
112 const wil::com_ptr<IWbemContext> nullcontext;
113 delete_path(objPath, nullcontext.get());
114 }
115
116 private:
117 wil::com_ptr<IWbemLocator> m_wbemLocator{};
118 wil::com_ptr<IWbemServices> m_wbemServices{};
119 };
120
121 class WmiClassObject
122 {
123 public:
124 //
125 // forward declare iterator classes
126 //
127 class property_iterator;
128 class method_iterator;
129
130 WmiClassObject(WmiService wbemServices, wil::com_ptr<IWbemClassObject> wbemClass) noexcept :
131 m_wbemServices(std::move(wbemServices)), m_wbemClassObject(std::move(wbemClass))
132 {
133 }
134
135 WmiClassObject(WmiService wbemServices, PCWSTR className) : m_wbemServices(std::move(wbemServices))
136 {
137 THROW_IF_FAILED(m_wbemServices->GetObject(wil::make_bstr(className).get(), 0, nullptr, m_wbemClassObject.put(), nullptr));
138 }
139
140 WmiClassObject(WmiService wbemServices, BSTR className) : m_wbemServices(std::move(wbemServices))
141 {
142 THROW_IF_FAILED(m_wbemServices->GetObjectW(className, 0, nullptr, m_wbemClassObject.put(), nullptr));
143 }
144
145 [[nodiscard]] wil::com_ptr<IWbemClassObject> get_class_object() const noexcept
146 {
147 return m_wbemClassObject;
148 }
149
150 [[nodiscard]] property_iterator property_begin(bool fNonSystemPropertiesOnly = true) const
151 {
152 return property_iterator(m_wbemClassObject, fNonSystemPropertiesOnly);
153 }
154
155 [[nodiscard]] static property_iterator property_end() noexcept
156 {
157 return {};
158 }
159
160 // A forward property_iterator class type to enable forward-traversing instances of the queried WMI provider
161 class property_iterator
162 {
163 public:
164 property_iterator() = default;
165
166 property_iterator(wil::com_ptr<IWbemClassObject> classObj, bool fNonSystemPropertiesOnly) :
167 m_wbemClassObj(std::move(classObj)), m_index(0)
168 {
169 THROW_IF_FAILED(m_wbemClassObj->BeginEnumeration(fNonSystemPropertiesOnly ? WBEM_FLAG_NONSYSTEM_ONLY : 0));
170 increment();
171 }
172
173 ~property_iterator() noexcept = default;
174 property_iterator(const property_iterator&) = default;
175 property_iterator& operator=(const property_iterator&) = default;
176 property_iterator(property_iterator&&) = default;
177 property_iterator& operator=(property_iterator&&) = default;
178
179 void swap(property_iterator& rhs) noexcept
180 {
181 using std::swap;
182 swap(m_index, rhs.m_index);
183 swap(m_wbemClassObj, rhs.m_wbemClassObj);
184 swap(m_propertyName, rhs.m_propertyName);
185 swap(m_propertyType, rhs.m_propertyType);
186 }
187
188 ////////////////////////////////////////////////////////////////////////////////
189 ///
190 /// accessors:
191 /// - dereference operators to access the property name
192 /// - explicit type() method to expose its CIM type
193 ///
194 ////////////////////////////////////////////////////////////////////////////////
195 BSTR operator*()
196 {
197 if (m_index == c_endIteratorIndex)
198 {
199 throw std::out_of_range("WmiClassObject::property_iterator::operator * - invalid subscript");
200 }
201 return m_propertyName.get();
202 }
203
204 BSTR operator*() const
205 {
206 if (m_index == c_endIteratorIndex)
207 {
208 throw std::out_of_range("WmiClassObject::property_iterator::operator * - invalid subscript");
209 }
210 return m_propertyName.get();
211 }
212
213 BSTR* operator->()
214 {
215 if (m_index == c_endIteratorIndex)
216 {
217 throw std::out_of_range("WmiClassObject::property_iterator::operator-> - invalid subscript");
218 }
219 return m_propertyName.addressof();
220 }
221
222 [[nodiscard]] CIMTYPE type() const
223 {
224 if (m_index == c_endIteratorIndex)
225 {
226 throw std::out_of_range("WmiClassObject::property_iterator::type - invalid subscript");
227 }
228 return m_propertyType;
229 }
230
231 bool operator==(const property_iterator& iter) const noexcept
232 {
233 if (m_index != c_endIteratorIndex)
234 {
235 return m_index == iter.m_index && m_wbemClassObj == iter.m_wbemClassObj;
236 }
237 return m_index == iter.m_index;
238 }
239
240 bool operator!=(const property_iterator& iter) const noexcept
241 {
242 return !(*this == iter);
243 }
244
245 // preincrement
246 property_iterator& operator++()
247 {
248 increment();
249 return *this;
250 }
251
252 // postincrement
253 property_iterator operator++(int)
254 {
255 property_iterator temp(*this);
256 increment();
257 return temp;
258 }
259
260 // increment by integer
261 property_iterator& operator+=(uint32_t inc)
262 {
263 for (auto loop = 0ul; loop < inc; ++loop)
264 {
265 increment();
266 if (m_index == c_endIteratorIndex)
267 {
268 throw std::out_of_range("WmiClassObject::property_iterator::operator+= - invalid subscript");
269 }
270 }
271 return *this;
272 }
273
274 // property_iterator_traits
275 // - allows <algorithm> functions to be used
276 using iterator_category = std::forward_iterator_tag;
277 using value_type = wil::shared_bstr;
278 using difference_type = int;
279 using pointer = BSTR;
280 using reference = wil::shared_bstr&;
281
282 private:
283 static constexpr uint32_t c_endIteratorIndex = ULONG_MAX;
284
285 wil::com_ptr<IWbemClassObject> m_wbemClassObj{};
286 wil::shared_bstr m_propertyName;
287 CIMTYPE m_propertyType = 0;
288 uint32_t m_index = c_endIteratorIndex;
289
290 void increment()
291 {
292 if (m_index == c_endIteratorIndex)
293 {
294 throw std::out_of_range("WmiClassObject::property_iterator - cannot increment: at the end");
295 }
296
297 CIMTYPE nextCimtype{};
298 wil::shared_bstr nextName;
299 switch (const auto hr = m_wbemClassObj->Next(0, nextName.put(), nullptr, &nextCimtype, nullptr))
300 {
301 case WBEM_S_NO_ERROR:
302 {
303 // update the instance members
304 ++m_index;
305 using std::swap;
306 swap(m_propertyName, nextName);
307 swap(m_propertyType, nextCimtype);
308 break;
309 }
310 case WBEM_S_NO_MORE_DATA:
311 {
312 // at the end...
313 m_index = c_endIteratorIndex;
314 m_propertyName.reset();
315 m_propertyType = 0;
316 break;
317 }
318
319 default:
320 THROW_HR(hr);
321 }
322 }
323 };
324
325 private:
326 WmiService m_wbemServices;
327 wil::com_ptr<IWbemClassObject> m_wbemClassObject{};
328 };
329
330 class WmiInstance
331 {
332 public:
333 // Constructors:
334 // requires a IWbemServices object already connected to WMI
335 // - one c'tor creates an empty instance (if set later)
336 // - one c'tor takes the WMI class name to instantiate a new instance
337 // - one c'tor takes an existing IWbemClassObject instance
338 explicit WmiInstance(WmiService service) noexcept : m_wbemServices(std::move(service))
339 {
340 }
341
342 WmiInstance(WmiService service, PCWSTR className) : m_wbemServices(std::move(service))
343 {
344 // get the object from the WMI service
345 wil::com_ptr<IWbemClassObject> classObject;
346 THROW_IF_FAILED(m_wbemServices->GetObject(wil::make_bstr(className).get(), 0, nullptr, classObject.addressof(), nullptr));
347 // spawn an instance of this object
348 THROW_IF_FAILED(classObject->SpawnInstance(0, m_instanceObject.put()));
349 }
350
351 WmiInstance(WmiService service, wil::com_ptr<IWbemClassObject> classObject) noexcept :
352 m_wbemServices(std::move(service)), m_instanceObject(std::move(classObject))
353 {
354 }
355
356 bool operator==(const WmiInstance& obj) const noexcept
357 {
358 return m_wbemServices == obj.m_wbemServices && m_instanceObject == obj.m_instanceObject;
359 }
360
361 bool operator!=(const WmiInstance& obj) const noexcept
362 {
363 return !(*this == obj);
364 }
365
366 [[nodiscard]] wil::com_ptr<IWbemClassObject> get_instance() const noexcept
367 {
368 return m_instanceObject;
369 }
370
371 [[nodiscard]] wil::unique_bstr get_path() const
372 {
373 wil::unique_variant objectPathVariant;
374 get(L"__RELPATH", objectPathVariant.addressof());
375
376 if (IsVariantEmptyOrNull(objectPathVariant.addressof()))
377 {
378 return nullptr;
379 }
380
381 if (V_VT(&objectPathVariant) != VT_BSTR)
382 {
383 THROW_HR(E_INVALIDARG);
384 }
385
386 return wil::make_bstr(V_BSTR(&objectPathVariant));
387 }
388
389 [[nodiscard]] WmiService get_service() const noexcept
390 {
391 return m_wbemServices;
392 }
393
394 // Retrieves the class name this WmiInstance is representing if any
395 [[nodiscard]] wil::unique_bstr get_class_name() const
396 {
397 wil::unique_variant classVariant;
398 get(L"__CLASS", classVariant.addressof());
399
400 if (IsVariantEmptyOrNull(classVariant.addressof()))
401 {
402 return nullptr;
403 }
404 if (V_VT(&classVariant) != VT_BSTR)
405 {
406 THROW_HR(E_INVALIDARG);
407 }
408
409 return wil::make_bstr(V_BSTR(&classVariant));
410 }
411
412 // Returns a class object for the class represented by this instance
413 [[nodiscard]] WmiClassObject get_class_object() const noexcept
414 {
415 return {m_wbemServices, m_instanceObject};
416 }
417
418 // Writes the instantiated object to the WMI repository
419 // Supported wbemFlags:
420 // WBEM_FLAG_CREATE_OR_UPDATE
421 // WBEM_FLAG_UPDATE_ONLY
422 // WBEM_FLAG_CREATE_ONLY
423 void write_instance(_In_opt_ IWbemContext* context, const LONG wbemFlags = WBEM_FLAG_CREATE_OR_UPDATE)
424 {
425 wil::com_ptr<IWbemCallResult> result;
426 THROW_IF_FAILED(m_wbemServices->PutInstance(
427 m_instanceObject.get(), wbemFlags | WBEM_FLAG_RETURN_IMMEDIATELY, context, result.addressof()));
428 // wait for the call to complete
429 HRESULT status;
430 THROW_IF_FAILED(result->GetCallStatus(WBEM_INFINITE, &status));
431 THROW_IF_FAILED(status);
432 }
433
434 void write_instance(LONG wbemFlags = WBEM_FLAG_CREATE_OR_UPDATE)
435 {
436 write_instance(nullptr, wbemFlags);
437 }
438
439 void delete_instance()
440 {
441 // delete the instance based off the __REPATH property
442 wil::com_ptr<IWbemCallResult> result;
443 THROW_IF_FAILED(m_wbemServices->DeleteInstance(get_path().get(), WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, result.addressof()));
444 // wait for the call to complete
445 HRESULT status;
446 THROW_IF_FAILED(result->GetCallStatus(WBEM_INFINITE, &status));
447 THROW_IF_FAILED(status);
448 }
449
450 // Invokes an instance method with zero -> 5 arguments from the instantiated IWbemClassObject
451 // Returns a WmiInstance containing the [out] parameters from the method call
452 // (the property "ReturnValue" contains the return value)
453 WmiInstance execute_method(PCWSTR method)
454 {
455 return execute_method_impl(method, nullptr);
456 }
457
458 template <typename Arg1>
459 WmiInstance execute_method(PCWSTR method, Arg1 arg1)
460 {
461 // establish the class object for the [in] params to the method
462 wil::com_ptr<IWbemClassObject> inParamsDefinition;
463 THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));
464
465 // spawn an instance to store the params
466 wil::com_ptr<IWbemClassObject> inParamsInstance;
467 THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));
468
469 // Instantiate a class object to iterate through each property
470 const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);
471 auto propertyIterator = propertyObject.property_begin();
472
473 // write the property
474 WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);
475 propertyclassObject.set(*propertyIterator, arg1);
476
477 // execute the method with the properties set
478 return execute_method_impl(method, inParamsInstance.get());
479 }
480
481 template <typename Arg1, typename Arg2>
482 WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2)
483 {
484 // establish the class object for the [in] params to the method
485 wil::com_ptr<IWbemClassObject> inParamsDefinition;
486 THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));
487
488 // spawn an instance to store the params
489 wil::com_ptr<IWbemClassObject> inParamsInstance;
490 THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));
491
492 // Instantiate a class object to iterate through each property
493 const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);
494 auto propertyIterator = propertyObject.property_begin();
495
496 // write each property
497 WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);
498 propertyclassObject.set(*propertyIterator, arg1);
499 ++propertyIterator;
500 propertyclassObject.set(*propertyIterator, arg2);
501
502 // execute the method with the properties set
503 return execute_method_impl(method, inParamsInstance.get());
504 }
505
506 template <typename Arg1, typename Arg2, typename Arg3>
507 WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2, Arg3 arg3)
508 {
509 // establish the class object for the [in] params to the method
510 wil::com_ptr<IWbemClassObject> inParamsDefinition;
511 THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));
512
513 // spawn an instance to store the params
514 wil::com_ptr<IWbemClassObject> inParamsInstance;
515 THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));
516
517 // Instantiate a class object to iterate through each property
518 const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);
519 auto propertyIterator = propertyObject.property_begin();
520
521 // write each property
522 WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);
523 propertyclassObject.set(*propertyIterator, arg1);
524 ++propertyIterator;
525 propertyclassObject.set(*propertyIterator, arg2);
526 ++propertyIterator;
527 propertyclassObject.set(*propertyIterator, arg3);
528
529 // execute the method with the properties set
530 return execute_method_impl(method, inParamsInstance.get());
531 }
532
533 template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
534 WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
535 {
536 // establish the class object for the [in] params to the method
537 wil::com_ptr<IWbemClassObject> inParamsDefinition;
538 THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));
539
540 // spawn an instance to store the params
541 wil::com_ptr<IWbemClassObject> inParamsInstance;
542 THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));
543
544 // Instantiate a class object to iterate through each property
545 const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);
546 auto propertyIterator = propertyObject.property_begin();
547
548 // write each property
549 WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);
550 propertyclassObject.set(*propertyIterator, arg1);
551 ++propertyIterator;
552 propertyclassObject.set(*propertyIterator, arg2);
553 ++propertyIterator;
554 propertyclassObject.set(*propertyIterator, arg3);
555 ++propertyIterator;
556 propertyclassObject.set(*propertyIterator, arg4);
557
558 // execute the method with the properties set
559 return execute_method_impl(method, inParamsInstance.get());
560 }
561
562 template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
563 WmiInstance execute_method(PCWSTR method, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
564 {
565 // establish the class object for the [in] params to the method
566 wil::com_ptr<IWbemClassObject> inParamsDefinition;
567 THROW_IF_FAILED(m_instanceObject->GetMethod(method, 0, inParamsDefinition.addressof(), nullptr));
568
569 // spawn an instance to store the params
570 wil::com_ptr<IWbemClassObject> inParamsInstance;
571 THROW_IF_FAILED(inParamsDefinition->SpawnInstance(0, inParamsInstance.addressof()));
572
573 // Instantiate a class object to iterate through each property
574 const WmiClassObject propertyObject(m_wbemServices, inParamsDefinition);
575 auto propertyIterator = propertyObject.property_begin();
576
577 // write each property
578 //
579 WmiInstance propertyclassObject(m_wbemServices, inParamsInstance);
580 propertyclassObject.set(*propertyIterator, arg1);
581 ++propertyIterator;
582 propertyclassObject.set(*propertyIterator, arg2);
583 ++propertyIterator;
584 propertyclassObject.set(*propertyIterator, arg3);
585 ++propertyIterator;
586 propertyclassObject.set(*propertyIterator, arg4);
587 ++propertyIterator;
588 propertyclassObject.set(*propertyIterator, arg5);
589
590 // execute the method with the properties set
591 return execute_method_impl(method, inParamsInstance.get());
592 }
593
594 bool is_null(PCWSTR propname) const
595 {
596 wil::unique_variant variant;
597 get_property(propname, variant.addressof());
598 return V_VT(&variant) == VT_NULL;
599 }
600
601 // get() and set()
602 //
603 // Exposes the properties of the WMI object instantiated
604 // WMI instances don't use all VARIANT types - some specializations
605 // exist because, for example, 64-bit integers actually get passed through
606 // WMI as BSTRs (even though variants support 64-bit integers directly).
607 // See the MSDN documentation for WMI MOF Data Types (Numbers):
608 // http://msdn.microsoft.com/en-us/library/aa392716(v=VS.85).aspx
609 //
610 // Even though VARIANTs support 16- and 32-bit unsigned integers, WMI passes them both
611 // around as 32-bit signed integers. Yes, that means you can't pass very large UINT32 values
612 // correctly through WMI directly.
613 //
614 // get() returns false if the value is empty or null
615 // returns true if retrieved the matching type
616
617 bool get(PCWSTR propname, _Inout_ VARIANT* value) const
618 {
619 VariantClear(value);
620 get_property(propname, value);
621 return !IsVariantEmptyOrNull(value);
622 }
623
624 template <typename T>
625 bool get(PCWSTR propname, _Out_ T* value) const
626 {
627 wil::unique_variant variant;
628 get_property(propname, variant.addressof());
629 return WmiReadFromVariant(variant.addressof(), value);
630 }
631
632 void set(PCWSTR propname, _In_ const VARIANT* value) const
633 {
634 set_property(propname, value);
635 }
636
637 template <typename T>
638 void set(PCWSTR propname, const T value) const
639 {
640 set_property(propname, WmiMakeVariant(value).addressof());
641 }
642
643 // Calling IWbemClassObject::Delete on a property of an instance resets to the default value.
644 void set_default(PCWSTR propname) const
645 {
646 THROW_IF_FAILED(m_instanceObject->Delete(propname));
647 }
648
649 private:
650 void get_property(PCWSTR propertyName, _Inout_ VARIANT* pVariant) const
651 {
652 auto* pInstance = m_instanceObject.get();
653 THROW_IF_FAILED(pInstance->Get(propertyName, 0, pVariant, nullptr, nullptr));
654 }
655
656 void set_property(PCWSTR propname, _In_ const VARIANT* pVariant) const
657 {
658 THROW_IF_FAILED(m_instanceObject->Put(
659 propname,
660 0,
661 const_cast<VARIANT*>(pVariant), // COM is not const-correct
662 0));
663 }
664
665 WmiInstance execute_method_impl(PCWSTR method, _In_opt_ IWbemClassObject* pParams)
666 {
667 // exec the method semi-synchronously from this instance based off the __REPATH property
668 wil::com_ptr<IWbemCallResult> result;
669 THROW_IF_FAILED(m_wbemServices->ExecMethod(
670 get_path().get(), wil::make_bstr(method).get(), WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, pParams, nullptr, result.addressof()));
671
672 // wait for the call to complete - and get the [out] param object
673 wil::com_ptr<IWbemClassObject> outParamsInstance;
674 THROW_IF_FAILED(result->GetResultObject(WBEM_INFINITE, outParamsInstance.addressof()));
675
676 // the call went through - return a WmiInstance from this retrieved instance
677 return {m_wbemServices, outParamsInstance};
678 }
679
680 WmiService m_wbemServices;
681 wil::com_ptr<IWbemClassObject> m_instanceObject{};
682 };
683
684 // Exposes enumerating instances of a WMI Provider through an iterator interface.
685 class WmiEnumerate
686 {
687 public:
688 // A forward iterator class type to enable forward-traversing instances of the queried WMI provider
689 //
690 class iterator
691 {
692 public:
693 explicit iterator(WmiService service) noexcept : m_wbemServices(std::move(service))
694 {
695 }
696
697 iterator(WmiService service, wil::com_ptr<IEnumWbemClassObject> wbemEnumerator) :
698 m_index(0), m_wbemServices(std::move(service)), m_wbemEnumerator(std::move(wbemEnumerator))
699 {
700 increment();
701 }
702
703 ~iterator() noexcept = default;
704 iterator(const iterator&) noexcept = default;
705 iterator& operator=(const iterator&) noexcept = default;
706 iterator(iterator&&) noexcept = default;
707 iterator& operator=(iterator&&) noexcept = default;
708
709 void swap(_Inout_ iterator& rhs) noexcept
710 {
711 using std::swap;
712 swap(m_index, rhs.m_index);
713 swap(m_wbemServices, rhs.m_wbemServices);
714 swap(m_wbemEnumerator, rhs.m_wbemEnumerator);
715 swap(m_wmiInstance, rhs.m_wmiInstance);
716 }
717
718 [[nodiscard]] uint32_t location() const noexcept
719 {
720 return m_index;
721 }
722
723 WmiInstance& operator*() const noexcept
724 {
725 return *m_wmiInstance;
726 }
727
728 WmiInstance* operator->() const noexcept
729 {
730 return m_wmiInstance.get();
731 }
732
733 bool operator==(const iterator&) const noexcept;
734 bool operator!=(const iterator&) const noexcept;
735
736 iterator& operator++(); // preincrement
737 iterator operator++(int); // postincrement
738 iterator& operator+=(uint32_t); // increment by integer
739
740 // iterator_traits
741 // - allows <algorithm> functions to be used
742 using iterator_category = std::forward_iterator_tag;
743 using value_type = WmiInstance;
744 using difference_type = int;
745 using pointer = WmiInstance*;
746 using reference = WmiInstance&;
747
748 private:
749 void increment();
750
751 static constexpr uint32_t c_endIteratorIndex = ULONG_MAX;
752 uint32_t m_index = c_endIteratorIndex;
753 WmiService m_wbemServices;
754 wil::com_ptr<IEnumWbemClassObject> m_wbemEnumerator;
755 std::shared_ptr<WmiInstance> m_wmiInstance;
756 };
757
758 explicit WmiEnumerate(WmiService wbemServices) noexcept : m_wbemServices(std::move(wbemServices))
759 {
760 }
761
762 // Allows for executing a WMI query against the WMI service for an enumeration of WMI objects.
763 // Assumes the query is of the WQL query language.
764 const WmiEnumerate& query(_In_ PCWSTR query)
765 {
766 THROW_IF_FAILED(m_wbemServices->ExecQuery(
767 wil::make_bstr(L"WQL").get(), wil::make_bstr(query).get(), WBEM_FLAG_BIDIRECTIONAL, nullptr, m_wbemEnumerator.put()));
768 return *this;
769 }
770
771 const WmiEnumerate& query(_In_ PCWSTR query, const wil::com_ptr<IWbemContext>& context)
772 {
773 THROW_IF_FAILED(m_wbemServices->ExecQuery(
774 wil::make_bstr(L"WQL").get(), wil::make_bstr(query).get(), WBEM_FLAG_BIDIRECTIONAL, context.get(), m_wbemEnumerator.put()));
775 return *this;
776 }
777
778 iterator begin() const
779 {
780 if (nullptr == m_wbemEnumerator.get())
781 {
782 return end();
783 }
784 THROW_IF_FAILED(m_wbemEnumerator->Reset());
785 return iterator(m_wbemServices, m_wbemEnumerator);
786 }
787
788 iterator end() const noexcept
789 {
790 return iterator(m_wbemServices);
791 }
792
793 iterator cbegin() const
794 {
795 if (nullptr == m_wbemEnumerator.get())
796 {
797 return cend();
798 }
799 THROW_IF_FAILED(m_wbemEnumerator->Reset());
800 return iterator(m_wbemServices, m_wbemEnumerator);
801 }
802
803 iterator cend() const noexcept
804 {
805 return iterator(m_wbemServices);
806 }
807
808 private:
809 WmiService m_wbemServices;
810 // Marking wbemEnumerator mutable to allow for const correctness of begin() and end()
811 // specifically, invoking Reset() is an implementation detail and should not affect external contracts
812 mutable wil::com_ptr<IEnumWbemClassObject> m_wbemEnumerator;
813 };
814
815 inline bool WmiEnumerate::iterator::operator==(const iterator& iter) const noexcept
816 {
817 if (m_index != c_endIteratorIndex)
818 {
819 return m_index == iter.m_index && m_wbemServices == iter.m_wbemServices && m_wbemEnumerator == iter.m_wbemEnumerator &&
820 m_wmiInstance == iter.m_wmiInstance;
821 }
822 return m_index == iter.m_index && m_wbemServices == iter.m_wbemServices;
823 }
824
825 inline bool WmiEnumerate::iterator::operator!=(const iterator& iter) const noexcept
826 {
827 return !(*this == iter);
828 }
829
830 // preincrement
831 inline WmiEnumerate::iterator& WmiEnumerate::iterator::operator++()
832 {
833 increment();
834 return *this;
835 }
836
837 // postincrement
838 inline WmiEnumerate::iterator WmiEnumerate::iterator::operator++(int)
839 {
840 auto temp(*this);
841 increment();
842 return temp;
843 }
844
845 // increment by integer
846 inline WmiEnumerate::iterator& WmiEnumerate::iterator::operator+=(uint32_t inc)
847 {
848 for (auto loop = 0ul; loop < inc; ++loop)
849 {
850 increment();
851 if (m_index == c_endIteratorIndex)
852 {
853 throw std::out_of_range("WmiEnumerate::iterator::operator+= - invalid subscript");
854 }
855 }
856 return *this;
857 }
858
859 inline void WmiEnumerate::iterator::increment()
860 {
861 if (m_index == c_endIteratorIndex)
862 {
863 throw std::out_of_range("WmiEnumerate::iterator::increment at the end");
864 }
865
866 ULONG uReturn;
867 wil::com_ptr<IWbemClassObject> wbemTarget;
868 THROW_IF_FAILED(m_wbemEnumerator->Next(WBEM_INFINITE, 1, wbemTarget.put(), &uReturn));
869
870 if (0 == uReturn)
871 {
872 // at the end...
873 m_index = c_endIteratorIndex;
874 m_wmiInstance.reset();
875 }
876 else
877 {
878 ++m_index;
879 m_wmiInstance = std::make_shared<WmiInstance>(m_wbemServices, wbemTarget);
880 }
881 }
882 } // namespace wsl::core