1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/win/scoped_variant.h"
6#include "base/logging.h"
7
8namespace base {
9namespace win {
10
11// Global, const instance of an empty variant.
12const VARIANT ScopedVariant::kEmptyVariant = { VT_EMPTY };
13
14ScopedVariant::~ScopedVariant() {
15  COMPILE_ASSERT(sizeof(ScopedVariant) == sizeof(VARIANT), ScopedVariantSize);
16  ::VariantClear(&var_);
17}
18
19ScopedVariant::ScopedVariant(const wchar_t* str) {
20  var_.vt = VT_EMPTY;
21  Set(str);
22}
23
24ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) {
25  var_.vt = VT_BSTR;
26  var_.bstrVal = ::SysAllocStringLen(str, length);
27}
28
29ScopedVariant::ScopedVariant(int value, VARTYPE vt) {
30  var_.vt = vt;
31  var_.lVal = value;
32}
33
34ScopedVariant::ScopedVariant(double value, VARTYPE vt) {
35  DCHECK(vt == VT_R8 || vt == VT_DATE);
36  var_.vt = vt;
37  var_.dblVal = value;
38}
39
40ScopedVariant::ScopedVariant(IDispatch* dispatch) {
41  var_.vt = VT_EMPTY;
42  Set(dispatch);
43}
44
45ScopedVariant::ScopedVariant(IUnknown* unknown) {
46  var_.vt = VT_EMPTY;
47  Set(unknown);
48}
49
50ScopedVariant::ScopedVariant(SAFEARRAY* safearray) {
51  var_.vt = VT_EMPTY;
52  Set(safearray);
53}
54
55ScopedVariant::ScopedVariant(const VARIANT& var) {
56  var_.vt = VT_EMPTY;
57  Set(var);
58}
59
60void ScopedVariant::Reset(const VARIANT& var) {
61  if (&var != &var_) {
62    ::VariantClear(&var_);
63    var_ = var;
64  }
65}
66
67VARIANT ScopedVariant::Release() {
68  VARIANT var = var_;
69  var_.vt = VT_EMPTY;
70  return var;
71}
72
73void ScopedVariant::Swap(ScopedVariant& var) {
74  VARIANT tmp = var_;
75  var_ = var.var_;
76  var.var_ = tmp;
77}
78
79VARIANT* ScopedVariant::Receive() {
80  DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt;
81  return &var_;
82}
83
84VARIANT ScopedVariant::Copy() const {
85  VARIANT ret = { VT_EMPTY };
86  ::VariantCopy(&ret, &var_);
87  return ret;
88}
89
90int ScopedVariant::Compare(const VARIANT& var, bool ignore_case) const {
91  ULONG flags = ignore_case ? NORM_IGNORECASE : 0;
92  HRESULT hr = ::VarCmp(const_cast<VARIANT*>(&var_), const_cast<VARIANT*>(&var),
93                        LOCALE_USER_DEFAULT, flags);
94  int ret = 0;
95
96  switch (hr) {
97    case VARCMP_LT:
98      ret = -1;
99      break;
100
101    case VARCMP_GT:
102    case VARCMP_NULL:
103      ret = 1;
104      break;
105
106    default:
107      // Equal.
108      break;
109  }
110
111  return ret;
112}
113
114void ScopedVariant::Set(const wchar_t* str) {
115  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
116  var_.vt = VT_BSTR;
117  var_.bstrVal = ::SysAllocString(str);
118}
119
120void ScopedVariant::Set(int8 i8) {
121  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
122  var_.vt = VT_I1;
123  var_.cVal = i8;
124}
125
126void ScopedVariant::Set(uint8 ui8) {
127  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
128  var_.vt = VT_UI1;
129  var_.bVal = ui8;
130}
131
132void ScopedVariant::Set(int16 i16) {
133  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
134  var_.vt = VT_I2;
135  var_.iVal = i16;
136}
137
138void ScopedVariant::Set(uint16 ui16) {
139  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
140  var_.vt = VT_UI2;
141  var_.uiVal = ui16;
142}
143
144void ScopedVariant::Set(int32 i32) {
145  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
146  var_.vt = VT_I4;
147  var_.lVal = i32;
148}
149
150void ScopedVariant::Set(uint32 ui32) {
151  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
152  var_.vt = VT_UI4;
153  var_.ulVal = ui32;
154}
155
156void ScopedVariant::Set(int64 i64) {
157  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
158  var_.vt = VT_I8;
159  var_.llVal = i64;
160}
161
162void ScopedVariant::Set(uint64 ui64) {
163  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
164  var_.vt = VT_UI8;
165  var_.ullVal = ui64;
166}
167
168void ScopedVariant::Set(float r32) {
169  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
170  var_.vt = VT_R4;
171  var_.fltVal = r32;
172}
173
174void ScopedVariant::Set(double r64) {
175  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
176  var_.vt = VT_R8;
177  var_.dblVal = r64;
178}
179
180void ScopedVariant::SetDate(DATE date) {
181  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
182  var_.vt = VT_DATE;
183  var_.date = date;
184}
185
186void ScopedVariant::Set(IDispatch* disp) {
187  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
188  var_.vt = VT_DISPATCH;
189  var_.pdispVal = disp;
190  if (disp)
191    disp->AddRef();
192}
193
194void ScopedVariant::Set(bool b) {
195  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
196  var_.vt = VT_BOOL;
197  var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE;
198}
199
200void ScopedVariant::Set(IUnknown* unk) {
201  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
202  var_.vt = VT_UNKNOWN;
203  var_.punkVal = unk;
204  if (unk)
205    unk->AddRef();
206}
207
208void ScopedVariant::Set(SAFEARRAY* array) {
209  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
210  if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) {
211    var_.vt |= VT_ARRAY;
212    var_.parray = array;
213  } else {
214    DCHECK(!array) << "Unable to determine safearray vartype";
215    var_.vt = VT_EMPTY;
216  }
217}
218
219void ScopedVariant::Set(const VARIANT& var) {
220  DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
221  if (FAILED(::VariantCopy(&var_, &var))) {
222    DLOG(ERROR) << "VariantCopy failed";
223    var_.vt = VT_EMPTY;
224  }
225}
226
227ScopedVariant& ScopedVariant::operator=(const VARIANT& var) {
228  if (&var != &var_) {
229    VariantClear(&var_);
230    Set(var);
231  }
232  return *this;
233}
234
235bool ScopedVariant::IsLeakableVarType(VARTYPE vt) {
236  bool leakable = false;
237  switch (vt & VT_TYPEMASK) {
238    case VT_BSTR:
239    case VT_DISPATCH:
240    // we treat VT_VARIANT as leakable to err on the safe side.
241    case VT_VARIANT:
242    case VT_UNKNOWN:
243    case VT_SAFEARRAY:
244
245    // very rarely used stuff (if ever):
246    case VT_VOID:
247    case VT_PTR:
248    case VT_CARRAY:
249    case VT_USERDEFINED:
250    case VT_LPSTR:
251    case VT_LPWSTR:
252    case VT_RECORD:
253    case VT_INT_PTR:
254    case VT_UINT_PTR:
255    case VT_FILETIME:
256    case VT_BLOB:
257    case VT_STREAM:
258    case VT_STORAGE:
259    case VT_STREAMED_OBJECT:
260    case VT_STORED_OBJECT:
261    case VT_BLOB_OBJECT:
262    case VT_VERSIONED_STREAM:
263    case VT_BSTR_BLOB:
264      leakable = true;
265      break;
266  }
267
268  if (!leakable && (vt & VT_ARRAY) != 0) {
269    leakable = true;
270  }
271
272  return leakable;
273}
274
275}  // namespace win
276}  // namespace base
277