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