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