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