• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_WIN_SCOPED_SAFEARRAY_H_
6 #define BASE_WIN_SCOPED_SAFEARRAY_H_
7 
8 #include <objbase.h>
9 
10 #include "base/base_export.h"
11 #include "base/check_op.h"
12 #include "base/memory/raw_ptr_exclusion.h"
13 #include "base/win/variant_conversions.h"
14 #include "third_party/abseil-cpp/absl/types/optional.h"
15 
16 namespace base {
17 namespace win {
18 
19 // Manages a Windows SAFEARRAY. This is a minimal wrapper that simply provides
20 // RAII semantics and does not duplicate the extensive functionality that
21 // CComSafeArray offers.
22 class BASE_EXPORT ScopedSafearray {
23  public:
24   // LockScope<VARTYPE> class for automatically managing the lifetime of a
25   // SAFEARRAY lock, and granting easy access to the underlying data either
26   // through random access or as an iterator.
27   // It is undefined behavior if the underlying SAFEARRAY is destroyed
28   // before the LockScope.
29   // LockScope implements std::iterator_traits as a random access iterator, so
30   // that LockScope is compatible with STL methods that require these traits.
31   template <VARTYPE ElementVartype>
32   class BASE_EXPORT LockScope final {
33    public:
34     // Type declarations to support std::iterator_traits
35     using iterator_category = std::random_access_iterator_tag;
36     using value_type =
37         typename internal::VariantConverter<ElementVartype>::Type;
38     using difference_type = ptrdiff_t;
39     using reference = value_type&;
40     using const_reference = const value_type&;
41     using pointer = value_type*;
42     using const_pointer = const value_type*;
43 
44     LockScope() = default;
45 
LockScope(LockScope<ElementVartype> && other)46     LockScope(LockScope<ElementVartype>&& other)
47         : safearray_(std::exchange(other.safearray_, nullptr)),
48           vartype_(std::exchange(other.vartype_, VT_EMPTY)),
49           array_(std::exchange(other.array_, nullptr)),
50           array_size_(std::exchange(other.array_size_, 0U)) {}
51 
52     LockScope<ElementVartype>& operator=(LockScope<ElementVartype>&& other) {
53       DCHECK_NE(this, &other);
54       Reset();
55       safearray_ = std::exchange(other.safearray_, nullptr);
56       vartype_ = std::exchange(other.vartype_, VT_EMPTY);
57       array_ = std::exchange(other.array_, nullptr);
58       array_size_ = std::exchange(other.array_size_, 0U);
59       return *this;
60     }
61 
62     LockScope(const LockScope&) = delete;
63     LockScope& operator=(const LockScope&) = delete;
64 
~LockScope()65     ~LockScope() { Reset(); }
66 
Type()67     VARTYPE Type() const { return vartype_; }
68 
size()69     size_t size() const { return array_size_; }
70 
begin()71     pointer begin() { return array_; }
end()72     pointer end() { return array_ + array_size_; }
begin()73     const_pointer begin() const { return array_; }
end()74     const_pointer end() const { return array_ + array_size_; }
75 
data()76     pointer data() { return array_; }
data()77     const_pointer data() const { return array_; }
78 
79     reference operator[](size_t index) { return at(index); }
80     const_reference operator[](size_t index) const { return at(index); }
81 
at(size_t index)82     reference at(size_t index) {
83       DCHECK_NE(array_, nullptr);
84       DCHECK_LT(index, array_size_);
85       return array_[index];
86     }
at(size_t index)87     const_reference at(size_t index) const {
88       return const_cast<LockScope<ElementVartype>*>(this)->at(index);
89     }
90 
91    private:
LockScope(SAFEARRAY * safearray,VARTYPE vartype,pointer array,size_t array_size)92     LockScope(SAFEARRAY* safearray,
93               VARTYPE vartype,
94               pointer array,
95               size_t array_size)
96         : safearray_(safearray),
97           vartype_(vartype),
98           array_(array),
99           array_size_(array_size) {}
100 
Reset()101     void Reset() {
102       if (safearray_)
103         SafeArrayUnaccessData(safearray_);
104       safearray_ = nullptr;
105       vartype_ = VT_EMPTY;
106       array_ = nullptr;
107       array_size_ = 0U;
108     }
109 
110     // This field is not a raw_ptr<> because it was filtered by the rewriter
111     // for: #union
112     RAW_PTR_EXCLUSION SAFEARRAY* safearray_ = nullptr;
113     VARTYPE vartype_ = VT_EMPTY;
114     pointer array_ = nullptr;
115     size_t array_size_ = 0U;
116 
117     friend class ScopedSafearray;
118   };
119 
120   explicit ScopedSafearray(SAFEARRAY* safearray = nullptr)
safearray_(safearray)121       : safearray_(safearray) {}
122 
123   ScopedSafearray(const ScopedSafearray&) = delete;
124   ScopedSafearray& operator=(const ScopedSafearray&) = delete;
125 
126   // Move constructor
ScopedSafearray(ScopedSafearray && r)127   ScopedSafearray(ScopedSafearray&& r) noexcept : safearray_(r.safearray_) {
128     r.safearray_ = nullptr;
129   }
130 
131   // Move operator=. Allows assignment from a ScopedSafearray rvalue.
132   ScopedSafearray& operator=(ScopedSafearray&& rvalue) {
133     Reset(rvalue.Release());
134     return *this;
135   }
136 
~ScopedSafearray()137   ~ScopedSafearray() { Destroy(); }
138 
139   // Creates a LockScope for accessing the contents of a
140   // single-dimensional SAFEARRAYs.
141   template <VARTYPE ElementVartype>
CreateLockScope()142   absl::optional<LockScope<ElementVartype>> CreateLockScope() const {
143     if (!safearray_ || SafeArrayGetDim(safearray_) != 1)
144       return absl::nullopt;
145 
146     VARTYPE vartype;
147     HRESULT hr = SafeArrayGetVartype(safearray_, &vartype);
148     if (FAILED(hr) ||
149         !internal::VariantConverter<ElementVartype>::IsConvertibleTo(vartype)) {
150       return absl::nullopt;
151     }
152 
153     typename LockScope<ElementVartype>::pointer array = nullptr;
154     hr = SafeArrayAccessData(safearray_, reinterpret_cast<void**>(&array));
155     if (FAILED(hr))
156       return absl::nullopt;
157 
158     const size_t array_size = GetCount();
159     return LockScope<ElementVartype>(safearray_, vartype, array, array_size);
160   }
161 
Destroy()162   void Destroy() {
163     if (safearray_) {
164       HRESULT hr = SafeArrayDestroy(safearray_);
165       DCHECK_EQ(S_OK, hr);
166       safearray_ = nullptr;
167     }
168   }
169 
170   // Give ScopedSafearray ownership over an already allocated SAFEARRAY or
171   // nullptr.
172   void Reset(SAFEARRAY* safearray = nullptr) {
173     if (safearray != safearray_) {
174       Destroy();
175       safearray_ = safearray;
176     }
177   }
178 
179   // Releases ownership of the SAFEARRAY to the caller.
Release()180   SAFEARRAY* Release() {
181     SAFEARRAY* safearray = safearray_;
182     safearray_ = nullptr;
183     return safearray;
184   }
185 
186   // Retrieves the pointer address.
187   // Used to receive SAFEARRAYs as out arguments (and take ownership).
188   // This function releases any existing references because it will leak
189   // the existing ref otherwise.
190   // Usage: GetSafearray(safearray.Receive());
Receive()191   SAFEARRAY** Receive() {
192     Destroy();
193     return &safearray_;
194   }
195 
196   // Returns the number of elements in a dimension of the array.
197   size_t GetCount(UINT dimension = 0) const {
198     DCHECK(safearray_);
199     // Initialize |lower| and |upper| so this method will return zero if either
200     // SafeArrayGetLBound or SafeArrayGetUBound returns failure because they
201     // only write to the output parameter when successful.
202     LONG lower = 0;
203     LONG upper = -1;
204     DCHECK_LT(dimension, SafeArrayGetDim(safearray_));
205     HRESULT hr = SafeArrayGetLBound(safearray_, dimension + 1, &lower);
206     DCHECK(SUCCEEDED(hr));
207     hr = SafeArrayGetUBound(safearray_, dimension + 1, &upper);
208     DCHECK(SUCCEEDED(hr));
209     LONG count = upper - lower + 1;
210     // SafeArrays may have negative lower bounds, so check for wraparound.
211     DCHECK_GT(count, 0);
212     return static_cast<size_t>(count);
213   }
214 
215   // Returns the internal pointer.
Get()216   SAFEARRAY* Get() const { return safearray_; }
217 
218   // Forbid comparison of ScopedSafearray types.  You should never have the same
219   // SAFEARRAY owned by two different scoped_ptrs.
220   bool operator==(const ScopedSafearray& safearray2) const = delete;
221   bool operator!=(const ScopedSafearray& safearray2) const = delete;
222 
223  private:
224   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
225   // #addr-of
226   RAW_PTR_EXCLUSION SAFEARRAY* safearray_;
227 };
228 
229 }  // namespace win
230 }  // namespace base
231 
232 #endif  // BASE_WIN_SCOPED_SAFEARRAY_H_
233