// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/device_signals/core/system_signals/win/wmi_client_impl.h"
#include <windows.h>
#include <wbemidl.h>
#include <wrl/client.h>
#include <algorithm>
#include <optional>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/com_init_util.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_variant.h"
#include "base/win/windows_version.h"
#include "base/win/wmi.h"
using Microsoft::WRL::ComPtr;
namespace device_signals {
namespace {
// Parses a string value from `class_object` named `property_name`.
std::optional<std::string> ParseString(
const std::wstring& property_name,
const ComPtr<IWbemClassObject>& class_object) {
base::win::ScopedVariant string_variant;
HRESULT hr = class_object->Get(property_name.c_str(), 0,
string_variant.Receive(), 0, 0);
if (FAILED(hr) || string_variant.type() != VT_BSTR) {
return std::nullopt;
}
// Owned by ScopedVariant.
BSTR temp_bstr = V_BSTR(string_variant.ptr());
return base::SysWideToUTF8(
std::wstring(temp_bstr, ::SysStringLen(temp_bstr)));
}
} // namespace
WmiClientImpl::WmiClientImpl()
: run_query_callback_(base::BindRepeating(base::win::RunWmiQuery)) {}
WmiClientImpl::WmiClientImpl(RunWmiQueryCallback run_query_callback)
: run_query_callback_(std::move(run_query_callback)) {}
WmiClientImpl::~WmiClientImpl() = default;
WmiHotfixesResponse WmiClientImpl::GetInstalledHotfixes() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
ComPtr<IEnumWbemClassObject> enumerator;
auto error_code = run_query_callback_.Run(
base::win::kCimV2ServerName, L"SELECT * FROM Win32_QuickFixEngineering",
&enumerator);
WmiHotfixesResponse response;
if (error_code.has_value()) {
response.query_error = error_code.value();
return response;
}
HRESULT hr;
while (true) {
ComPtr<IWbemClassObject> class_object;
ULONG items_returned = 0U;
hr = enumerator->Next(WBEM_INFINITE, 1, &class_object, &items_returned);
if (hr == WBEM_S_FALSE || items_returned == 0U) {
// Reached the end of the enumerator.
break;
}
// Something went wrong, and it wasn't the end of the enumerator.
if (FAILED(hr)) {
response.parsing_errors.push_back(
WmiParsingError::kFailedToIterateResults);
continue;
}
std::optional<std::string> hotfix_id =
ParseString(L"HotFixId", class_object);
if (!hotfix_id.has_value()) {
response.parsing_errors.push_back(WmiParsingError::kFailedToGetName);
continue;
}
InstalledHotfix hotfix;
hotfix.hotfix_id = hotfix_id.value();
// If all values were parsed properly, add `hotfix` into the response
// vector. If any value could not be parsed properly, the item was
// discarded.
response.hotfixes.push_back(hotfix);
}
return response;
}
} // namespace device_signals