class ScopedVariant {
public:
// Declaration of a global variant variable that's always VT_EMPTY
static const VARIANT kEmptyVariant;
// Default constructor.
ScopedVariant() {
// This is equivalent to what VariantInit does, but less code.
var_.vt = VT_EMPTY;
}
// Constructor to create a new VT_BSTR VARIANT.
// NOTE: Do not pass a BSTR to this constructor expecting ownership to
// be transferred
explicit ScopedVariant(const wchar_t* str);
// Creates a new VT_BSTR variant of a specified length.
explicit ScopedVariant(const wchar_t* str, UINT length);
// Creates a new integral type variant and assigns the value to
// VARIANT.lVal (32 bit sized field).
explicit ScopedVariant(int value, VARTYPE vt = VT_I4);
// VT_DISPATCH
explicit ScopedVariant(IDispatch* dispatch);
// VT_UNKNOWN
explicit ScopedVariant(IUnknown* unknown);
// Copies the variant.
explicit ScopedVariant(const VARIANT& var);
~ScopedVariant();
inline VARTYPE type() const {
return var_.vt;
}
// Give ScopedVariant ownership over an already allocated VARIANT.
void Reset(const VARIANT& var = kEmptyVariant);
// Releases ownership of the VARIANT to the caller.
VARIANT Release();
// Swap two ScopedVariant's.
void Swap(ScopedVariant& var);
// Returns a copy of the variant.
VARIANT Copy() const;
// The return value is 0 if the variants are equal, 1 if this object is
// greater than |var|, -1 if it is smaller.
int Compare(const VARIANT& var, bool ignore_case = false) const;
// Retrieves the pointer address.
// Used to receive a VARIANT as an out argument (and take ownership).
// The function DCHECKs on the current value being empty/null.
// Usage: GetVariant(var.receive());
VARIANT* Receive();
void Set(const wchar_t* str);
// Setters for simple types.
void Set(int8 i8);
void Set(uint8 ui8);
void Set(int16 i16);
void Set(uint16 ui16);
void Set(int32 i32);
void Set(uint32 ui32);
void Set(int64 i64);
void Set(uint64 ui64);
void Set(float r32);
void Set(double r64);
void Set(bool b);
// Creates a copy of |var| and assigns as this instance's value.
// Note that this is different from the Reset() method that's used to
// free the current value and assume ownership.
void Set(const VARIANT& var);
// COM object setters
void Set(IDispatch* disp);
void Set(IUnknown* unk);
// SAFEARRAY support
void Set(SAFEARRAY* array);
// Special setter for DATE since DATE is a double and we already have
// a setter for double.
void SetDate(DATE date);
// Allows const access to the contained variant without DCHECKs etc.
// This support is necessary for the V_XYZ (e.g. V_BSTR) set of macros to
// work properly but still doesn't allow modifications since we want control
// over that.
const VARIANT* operator&() const {
return &var_;
}
// Like other scoped classes (e.g scoped_refptr, ScopedComPtr, ScopedBstr)
// we support the assignment operator for the type we wrap.
ScopedVariant& operator=(const VARIANT& var);
// A hack to pass a pointer to the variant where the accepting
// function treats the variant as an input-only, read-only value
// but the function prototype requires a non const variant pointer.
// There's no DCHECK or anything here. Callers must know what they're doing.
VARIANT* AsInput() const {
// The nature of this function is const, so we declare
// it as such and cast away the constness here.
return const_cast<VARIANT*>(&var_);
}
// Allows the ScopedVariant instance to be passed to functions either by value
// or by const reference.
operator const VARIANT&() const {
return var_;
}
// Used as a debug check to see if we're leaking anything.
static bool IsLeakableVarType(VARTYPE vt);
protected:
VARIANT var_;
private:
// Comparison operators for ScopedVariant are not supported at this point.
// Use the Compare method instead.
bool operator==(const ScopedVariant& var) const;
bool operator!=(const ScopedVariant& var) const;
DISALLOW_COPY_AND_ASSIGN(ScopedVariant);
};
// 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(IDispatch* dispatch) {
var_.vt = VT_EMPTY;
Set(dispatch);
}
ScopedVariant::ScopedVariant(IUnknown* unknown) {
var_.vt = VT_EMPTY;
Set(unknown);
}
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;
}