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