• 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     // Cannot rewrite this pointer to raw_ptr<>, because this pointer
111     // comes from the operating system and may have been laundered
112     // if rewritten it may generate incorrect DPD error.
113     RAW_PTR_EXCLUSION SAFEARRAY* safearray_ = nullptr;
114     VARTYPE vartype_ = VT_EMPTY;
115     pointer array_ = nullptr;
116     size_t array_size_ = 0U;
117 
118     friend class ScopedSafearray;
119   };
120 
121   explicit ScopedSafearray(SAFEARRAY* safearray = nullptr)
safearray_(safearray)122       : safearray_(safearray) {}
123 
124   ScopedSafearray(const ScopedSafearray&) = delete;
125   ScopedSafearray& operator=(const ScopedSafearray&) = delete;
126 
127   // Move constructor
ScopedSafearray(ScopedSafearray && r)128   ScopedSafearray(ScopedSafearray&& r) noexcept : safearray_(r.safearray_) {
129     r.safearray_ = nullptr;
130   }
131 
132   // Move operator=. Allows assignment from a ScopedSafearray rvalue.
133   ScopedSafearray& operator=(ScopedSafearray&& rvalue) {
134     Reset(rvalue.Release());
135     return *this;
136   }
137 
~ScopedSafearray()138   ~ScopedSafearray() { Destroy(); }
139 
140   // Creates a LockScope for accessing the contents of a
141   // single-dimensional SAFEARRAYs.
142   template <VARTYPE ElementVartype>
CreateLockScope()143   absl::optional<LockScope<ElementVartype>> CreateLockScope() const {
144     if (!safearray_ || SafeArrayGetDim(safearray_) != 1)
145       return absl::nullopt;
146 
147     VARTYPE vartype;
148     HRESULT hr = SafeArrayGetVartype(safearray_, &vartype);
149     if (FAILED(hr) ||
150         !internal::VariantConverter<ElementVartype>::IsConvertibleTo(vartype)) {
151       return absl::nullopt;
152     }
153 
154     typename LockScope<ElementVartype>::pointer array = nullptr;
155     hr = SafeArrayAccessData(safearray_, reinterpret_cast<void**>(&array));
156     if (FAILED(hr))
157       return absl::nullopt;
158 
159     const size_t array_size = GetCount();
160     return LockScope<ElementVartype>(safearray_, vartype, array, array_size);
161   }
162 
Destroy()163   void Destroy() {
164     if (safearray_) {
165       HRESULT hr = SafeArrayDestroy(safearray_);
166       DCHECK_EQ(S_OK, hr);
167       safearray_ = nullptr;
168     }
169   }
170 
171   // Give ScopedSafearray ownership over an already allocated SAFEARRAY or
172   // nullptr.
173   void Reset(SAFEARRAY* safearray = nullptr) {
174     if (safearray != safearray_) {
175       Destroy();
176       safearray_ = safearray;
177     }
178   }
179 
180   // Releases ownership of the SAFEARRAY to the caller.
Release()181   SAFEARRAY* Release() {
182     SAFEARRAY* safearray = safearray_;
183     safearray_ = nullptr;
184     return safearray;
185   }
186 
187   // Retrieves the pointer address.
188   // Used to receive SAFEARRAYs as out arguments (and take ownership).
189   // This function releases any existing references because it will leak
190   // the existing ref otherwise.
191   // Usage: GetSafearray(safearray.Receive());
Receive()192   SAFEARRAY** Receive() {
193     Destroy();
194     return &safearray_;
195   }
196 
197   // Returns the number of elements in a dimension of the array.
198   size_t GetCount(UINT dimension = 0) const {
199     DCHECK(safearray_);
200     // Initialize |lower| and |upper| so this method will return zero if either
201     // SafeArrayGetLBound or SafeArrayGetUBound returns failure because they
202     // only write to the output parameter when successful.
203     LONG lower = 0;
204     LONG upper = -1;
205     DCHECK_LT(dimension, SafeArrayGetDim(safearray_));
206     HRESULT hr = SafeArrayGetLBound(safearray_, dimension + 1, &lower);
207     DCHECK(SUCCEEDED(hr));
208     hr = SafeArrayGetUBound(safearray_, dimension + 1, &upper);
209     DCHECK(SUCCEEDED(hr));
210     LONG count = upper - lower + 1;
211     // SafeArrays may have negative lower bounds, so check for wraparound.
212     DCHECK_GT(count, 0);
213     return static_cast<size_t>(count);
214   }
215 
216   // Returns the internal pointer.
Get()217   SAFEARRAY* Get() const { return safearray_; }
218 
219   // Forbid comparison of ScopedSafearray types.  You should never have the same
220   // SAFEARRAY owned by two different scoped_ptrs.
221   bool operator==(const ScopedSafearray& safearray2) const = delete;
222   bool operator!=(const ScopedSafearray& safearray2) const = delete;
223 
224  private:
225   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
226   // #addr-of
227   RAW_PTR_EXCLUSION SAFEARRAY* safearray_;
228 };
229 
230 }  // namespace win
231 }  // namespace base
232 
233 #endif  // BASE_WIN_SCOPED_SAFEARRAY_H_
234