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