// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/scoped_variant_win.h" #include "base/logging.h" // Global, const instance of an empty variant. const VARIANT ScopedVariant::kEmptyVariant = { VT_EMPTY }; ScopedVariant::~ScopedVariant() { COMPILE_ASSERT(sizeof(ScopedVariant) == sizeof(VARIANT), ScopedVariantSize); ::VariantClear(&var_); } ScopedVariant::ScopedVariant(const wchar_t* str) { var_.vt = VT_EMPTY; Set(str); } ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) { var_.vt = VT_BSTR; var_.bstrVal = ::SysAllocStringLen(str, length); } ScopedVariant::ScopedVariant(int value, VARTYPE vt) { var_.vt = vt; var_.lVal = value; } ScopedVariant::ScopedVariant(double value, VARTYPE vt) { DCHECK(vt == VT_R8 || vt == VT_DATE); var_.vt = vt; var_.dblVal = value; } ScopedVariant::ScopedVariant(IDispatch* dispatch) { var_.vt = VT_EMPTY; Set(dispatch); } ScopedVariant::ScopedVariant(IUnknown* unknown) { var_.vt = VT_EMPTY; Set(unknown); } ScopedVariant::ScopedVariant(SAFEARRAY* safearray) { var_.vt = VT_EMPTY; Set(safearray); } ScopedVariant::ScopedVariant(const VARIANT& var) { var_.vt = VT_EMPTY; Set(var); } void ScopedVariant::Reset(const VARIANT& var) { if (&var != &var_) { ::VariantClear(&var_); var_ = var; } } VARIANT ScopedVariant::Release() { VARIANT var = var_; var_.vt = VT_EMPTY; return var; } void ScopedVariant::Swap(ScopedVariant& var) { VARIANT tmp = var_; var_ = var.var_; var.var_ = tmp; } VARIANT* ScopedVariant::Receive() { DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt; return &var_; } VARIANT ScopedVariant::Copy() const { VARIANT ret = { VT_EMPTY }; ::VariantCopy(&ret, &var_); return ret; } int ScopedVariant::Compare(const VARIANT& var, bool ignore_case) const { ULONG flags = ignore_case ? NORM_IGNORECASE : 0; HRESULT hr = ::VarCmp(const_cast<VARIANT*>(&var_), const_cast<VARIANT*>(&var), LOCALE_USER_DEFAULT, flags); int ret = 0; switch (hr) { case VARCMP_LT: ret = -1; break; case VARCMP_GT: case VARCMP_NULL: ret = 1; break; default: // Equal. break; } return ret; } void ScopedVariant::Set(const wchar_t* str) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_BSTR; var_.bstrVal = ::SysAllocString(str); } void ScopedVariant::Set(int8 i8) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_I1; var_.cVal = i8; } void ScopedVariant::Set(uint8 ui8) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_UI1; var_.bVal = ui8; } void ScopedVariant::Set(int16 i16) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_I2; var_.iVal = i16; } void ScopedVariant::Set(uint16 ui16) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_UI2; var_.uiVal = ui16; } void ScopedVariant::Set(int32 i32) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_I4; var_.lVal = i32; } void ScopedVariant::Set(uint32 ui32) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_UI4; var_.ulVal = ui32; } void ScopedVariant::Set(int64 i64) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_I8; var_.llVal = i64; } void ScopedVariant::Set(uint64 ui64) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_UI8; var_.ullVal = ui64; } void ScopedVariant::Set(float r32) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_R4; var_.fltVal = r32; } void ScopedVariant::Set(double r64) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_R8; var_.dblVal = r64; } void ScopedVariant::SetDate(DATE date) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_DATE; var_.date = date; } void ScopedVariant::Set(IDispatch* disp) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_DISPATCH; var_.pdispVal = disp; if (disp) disp->AddRef(); } void ScopedVariant::Set(bool b) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_BOOL; var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE; } void ScopedVariant::Set(IUnknown* unk) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; var_.vt = VT_UNKNOWN; var_.punkVal = unk; if (unk) unk->AddRef(); } void ScopedVariant::Set(SAFEARRAY* array) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) { var_.vt |= VT_ARRAY; var_.parray = array; } else { DCHECK(array == NULL) << "Unable to determine safearray vartype"; var_.vt = VT_EMPTY; } } void ScopedVariant::Set(const VARIANT& var) { DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; if (FAILED(::VariantCopy(&var_, &var))) { DLOG(ERROR) << "VariantCopy failed"; var_.vt = VT_EMPTY; } } ScopedVariant& ScopedVariant::operator=(const VARIANT& var) { if (&var != &var_) { VariantClear(&var_); Set(var); } return *this; } bool ScopedVariant::IsLeakableVarType(VARTYPE vt) { bool leakable = false; switch (vt & VT_TYPEMASK) { case VT_BSTR: case VT_DISPATCH: // we treat VT_VARIANT as leakable to err on the safe side. case VT_VARIANT: case VT_UNKNOWN: case VT_SAFEARRAY: // very rarely used stuff (if ever): case VT_VOID: case VT_PTR: case VT_CARRAY: case VT_USERDEFINED: case VT_LPSTR: case VT_LPWSTR: case VT_RECORD: case VT_INT_PTR: case VT_UINT_PTR: case VT_FILETIME: case VT_BLOB: case VT_STREAM: case VT_STORAGE: case VT_STREAMED_OBJECT: case VT_STORED_OBJECT: case VT_BLOB_OBJECT: case VT_VERSIONED_STREAM: case VT_BSTR_BLOB: leakable = true; break; } if (!leakable && (vt & VT_ARRAY) != 0) { leakable = true; } return leakable; }