233 lines
7.5 KiB
C++
233 lines
7.5 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef BASE_WIN_SCOPED_SAFEARRAY_H_
|
|
#define BASE_WIN_SCOPED_SAFEARRAY_H_
|
|
|
|
#include <objbase.h>
|
|
|
|
#include "base/base_export.h"
|
|
#include "base/check_op.h"
|
|
#include "base/memory/raw_ptr_exclusion.h"
|
|
#include "base/win/variant_conversions.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
namespace base {
|
|
namespace win {
|
|
|
|
// Manages a Windows SAFEARRAY. This is a minimal wrapper that simply provides
|
|
// RAII semantics and does not duplicate the extensive functionality that
|
|
// CComSafeArray offers.
|
|
class BASE_EXPORT ScopedSafearray {
|
|
public:
|
|
// LockScope<VARTYPE> class for automatically managing the lifetime of a
|
|
// SAFEARRAY lock, and granting easy access to the underlying data either
|
|
// through random access or as an iterator.
|
|
// It is undefined behavior if the underlying SAFEARRAY is destroyed
|
|
// before the LockScope.
|
|
// LockScope implements std::iterator_traits as a random access iterator, so
|
|
// that LockScope is compatible with STL methods that require these traits.
|
|
template <VARTYPE ElementVartype>
|
|
class BASE_EXPORT LockScope final {
|
|
public:
|
|
// Type declarations to support std::iterator_traits
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
using value_type =
|
|
typename internal::VariantConverter<ElementVartype>::Type;
|
|
using difference_type = ptrdiff_t;
|
|
using reference = value_type&;
|
|
using const_reference = const value_type&;
|
|
using pointer = value_type*;
|
|
using const_pointer = const value_type*;
|
|
|
|
LockScope() = default;
|
|
|
|
LockScope(LockScope<ElementVartype>&& other)
|
|
: safearray_(std::exchange(other.safearray_, nullptr)),
|
|
vartype_(std::exchange(other.vartype_, VT_EMPTY)),
|
|
array_(std::exchange(other.array_, nullptr)),
|
|
array_size_(std::exchange(other.array_size_, 0U)) {}
|
|
|
|
LockScope<ElementVartype>& operator=(LockScope<ElementVartype>&& other) {
|
|
DCHECK_NE(this, &other);
|
|
Reset();
|
|
safearray_ = std::exchange(other.safearray_, nullptr);
|
|
vartype_ = std::exchange(other.vartype_, VT_EMPTY);
|
|
array_ = std::exchange(other.array_, nullptr);
|
|
array_size_ = std::exchange(other.array_size_, 0U);
|
|
return *this;
|
|
}
|
|
|
|
LockScope(const LockScope&) = delete;
|
|
LockScope& operator=(const LockScope&) = delete;
|
|
|
|
~LockScope() { Reset(); }
|
|
|
|
VARTYPE Type() const { return vartype_; }
|
|
|
|
size_t size() const { return array_size_; }
|
|
|
|
pointer begin() { return array_; }
|
|
pointer end() { return array_ + array_size_; }
|
|
const_pointer begin() const { return array_; }
|
|
const_pointer end() const { return array_ + array_size_; }
|
|
|
|
pointer data() { return array_; }
|
|
const_pointer data() const { return array_; }
|
|
|
|
reference operator[](size_t index) { return at(index); }
|
|
const_reference operator[](size_t index) const { return at(index); }
|
|
|
|
reference at(size_t index) {
|
|
DCHECK_NE(array_, nullptr);
|
|
DCHECK_LT(index, array_size_);
|
|
return array_[index];
|
|
}
|
|
const_reference at(size_t index) const {
|
|
return const_cast<LockScope<ElementVartype>*>(this)->at(index);
|
|
}
|
|
|
|
private:
|
|
LockScope(SAFEARRAY* safearray,
|
|
VARTYPE vartype,
|
|
pointer array,
|
|
size_t array_size)
|
|
: safearray_(safearray),
|
|
vartype_(vartype),
|
|
array_(array),
|
|
array_size_(array_size) {}
|
|
|
|
void Reset() {
|
|
if (safearray_)
|
|
SafeArrayUnaccessData(safearray_);
|
|
safearray_ = nullptr;
|
|
vartype_ = VT_EMPTY;
|
|
array_ = nullptr;
|
|
array_size_ = 0U;
|
|
}
|
|
|
|
// This field is not a raw_ptr<> because it was filtered by the rewriter
|
|
// for: #union
|
|
RAW_PTR_EXCLUSION SAFEARRAY* safearray_ = nullptr;
|
|
VARTYPE vartype_ = VT_EMPTY;
|
|
pointer array_ = nullptr;
|
|
size_t array_size_ = 0U;
|
|
|
|
friend class ScopedSafearray;
|
|
};
|
|
|
|
explicit ScopedSafearray(SAFEARRAY* safearray = nullptr)
|
|
: safearray_(safearray) {}
|
|
|
|
ScopedSafearray(const ScopedSafearray&) = delete;
|
|
ScopedSafearray& operator=(const ScopedSafearray&) = delete;
|
|
|
|
// Move constructor
|
|
ScopedSafearray(ScopedSafearray&& r) noexcept : safearray_(r.safearray_) {
|
|
r.safearray_ = nullptr;
|
|
}
|
|
|
|
// Move operator=. Allows assignment from a ScopedSafearray rvalue.
|
|
ScopedSafearray& operator=(ScopedSafearray&& rvalue) {
|
|
Reset(rvalue.Release());
|
|
return *this;
|
|
}
|
|
|
|
~ScopedSafearray() { Destroy(); }
|
|
|
|
// Creates a LockScope for accessing the contents of a
|
|
// single-dimensional SAFEARRAYs.
|
|
template <VARTYPE ElementVartype>
|
|
absl::optional<LockScope<ElementVartype>> CreateLockScope() const {
|
|
if (!safearray_ || SafeArrayGetDim(safearray_) != 1)
|
|
return absl::nullopt;
|
|
|
|
VARTYPE vartype;
|
|
HRESULT hr = SafeArrayGetVartype(safearray_, &vartype);
|
|
if (FAILED(hr) ||
|
|
!internal::VariantConverter<ElementVartype>::IsConvertibleTo(vartype)) {
|
|
return absl::nullopt;
|
|
}
|
|
|
|
typename LockScope<ElementVartype>::pointer array = nullptr;
|
|
hr = SafeArrayAccessData(safearray_, reinterpret_cast<void**>(&array));
|
|
if (FAILED(hr))
|
|
return absl::nullopt;
|
|
|
|
const size_t array_size = GetCount();
|
|
return LockScope<ElementVartype>(safearray_, vartype, array, array_size);
|
|
}
|
|
|
|
void Destroy() {
|
|
if (safearray_) {
|
|
HRESULT hr = SafeArrayDestroy(safearray_);
|
|
DCHECK_EQ(S_OK, hr);
|
|
safearray_ = nullptr;
|
|
}
|
|
}
|
|
|
|
// Give ScopedSafearray ownership over an already allocated SAFEARRAY or
|
|
// nullptr.
|
|
void Reset(SAFEARRAY* safearray = nullptr) {
|
|
if (safearray != safearray_) {
|
|
Destroy();
|
|
safearray_ = safearray;
|
|
}
|
|
}
|
|
|
|
// Releases ownership of the SAFEARRAY to the caller.
|
|
SAFEARRAY* Release() {
|
|
SAFEARRAY* safearray = safearray_;
|
|
safearray_ = nullptr;
|
|
return safearray;
|
|
}
|
|
|
|
// Retrieves the pointer address.
|
|
// Used to receive SAFEARRAYs as out arguments (and take ownership).
|
|
// This function releases any existing references because it will leak
|
|
// the existing ref otherwise.
|
|
// Usage: GetSafearray(safearray.Receive());
|
|
SAFEARRAY** Receive() {
|
|
Destroy();
|
|
return &safearray_;
|
|
}
|
|
|
|
// Returns the number of elements in a dimension of the array.
|
|
size_t GetCount(UINT dimension = 0) const {
|
|
DCHECK(safearray_);
|
|
// Initialize |lower| and |upper| so this method will return zero if either
|
|
// SafeArrayGetLBound or SafeArrayGetUBound returns failure because they
|
|
// only write to the output parameter when successful.
|
|
LONG lower = 0;
|
|
LONG upper = -1;
|
|
DCHECK_LT(dimension, SafeArrayGetDim(safearray_));
|
|
HRESULT hr = SafeArrayGetLBound(safearray_, dimension + 1, &lower);
|
|
DCHECK(SUCCEEDED(hr));
|
|
hr = SafeArrayGetUBound(safearray_, dimension + 1, &upper);
|
|
DCHECK(SUCCEEDED(hr));
|
|
LONG count = upper - lower + 1;
|
|
// SafeArrays may have negative lower bounds, so check for wraparound.
|
|
DCHECK_GT(count, 0);
|
|
return static_cast<size_t>(count);
|
|
}
|
|
|
|
// Returns the internal pointer.
|
|
SAFEARRAY* Get() const { return safearray_; }
|
|
|
|
// Forbid comparison of ScopedSafearray types. You should never have the same
|
|
// SAFEARRAY owned by two different scoped_ptrs.
|
|
bool operator==(const ScopedSafearray& safearray2) const = delete;
|
|
bool operator!=(const ScopedSafearray& safearray2) const = delete;
|
|
|
|
private:
|
|
// This field is not a raw_ptr<> because it was filtered by the rewriter for:
|
|
// #addr-of
|
|
RAW_PTR_EXCLUSION SAFEARRAY* safearray_;
|
|
};
|
|
|
|
} // namespace win
|
|
} // namespace base
|
|
|
|
#endif // BASE_WIN_SCOPED_SAFEARRAY_H_
|