diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-16 03:46:05 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-16 03:46:05 +0000 |
commit | ce0e7246e92f2da8e6b865dd51ae626f9867ca4f (patch) | |
tree | 8181f26fbe349c35ff37a9ae837603c3f11be7f8 /base/win | |
parent | 21e74460f5b7cfe6f622557b20d40eecf3b8e98b (diff) | |
download | chromium_src-ce0e7246e92f2da8e6b865dd51ae626f9867ca4f.zip chromium_src-ce0e7246e92f2da8e6b865dd51ae626f9867ca4f.tar.gz chromium_src-ce0e7246e92f2da8e6b865dd51ae626f9867ca4f.tar.bz2 |
Move the windows-specific scoped_* stuff from base to base/win and in the base::win namespace.
This keeps old headers that forward to the new versions and have using declarations that allow the existing code to compile. I fixed all the callers in base to use the new ones, and also the other files I happened to touch.
This splits out the stuff from scoped_handle into a few separate files. I just deleted ScopedFindFile since it was only used in one place and it wasn't even really helping there.
I removed StackBstr which was a #define and used the "regular" ScopedBstr in the 7 places that used it. This is an optimization to avoid an extra allocation, but none of the callers are remotely performance critical.
TEST=it compiles
BUG=none
Review URL: http://codereview.chromium.org/3781009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62843 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/win')
-rw-r--r-- | base/win/scoped_bstr.cc | 71 | ||||
-rw-r--r-- | base/win/scoped_bstr.h | 97 | ||||
-rw-r--r-- | base/win/scoped_bstr_unittest.cc | 77 | ||||
-rw-r--r-- | base/win/scoped_comptr.h | 165 | ||||
-rw-r--r-- | base/win/scoped_comptr_unittest.cc | 113 | ||||
-rw-r--r-- | base/win/scoped_gdi_object.h | 78 | ||||
-rw-r--r-- | base/win/scoped_handle.h | 90 | ||||
-rw-r--r-- | base/win/scoped_hdc.h | 55 | ||||
-rw-r--r-- | base/win/scoped_hglobal.h | 53 | ||||
-rw-r--r-- | base/win/scoped_variant.cc | 276 | ||||
-rw-r--r-- | base/win/scoped_variant.h | 166 | ||||
-rw-r--r-- | base/win/scoped_variant_unittest.cc | 263 |
12 files changed, 1504 insertions, 0 deletions
diff --git a/base/win/scoped_bstr.cc b/base/win/scoped_bstr.cc new file mode 100644 index 0000000..18ffe6a --- /dev/null +++ b/base/win/scoped_bstr.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2010 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/win/scoped_bstr.h" + +#include "base/logging.h" + +namespace base { +namespace win { + +ScopedBstr::ScopedBstr(const char16* non_bstr) + : bstr_(SysAllocString(non_bstr)) { +} + +ScopedBstr::~ScopedBstr() { + COMPILE_ASSERT(sizeof(ScopedBstr) == sizeof(BSTR), ScopedBstrSize); + SysFreeString(bstr_); +} + +void ScopedBstr::Reset(BSTR bstr) { + if (bstr != bstr_) { + // if |bstr_| is NULL, SysFreeString does nothing. + SysFreeString(bstr_); + bstr_ = bstr; + } +} + +BSTR ScopedBstr::Release() { + BSTR bstr = bstr_; + bstr_ = NULL; + return bstr; +} + +void ScopedBstr::Swap(ScopedBstr& bstr2) { + BSTR tmp = bstr_; + bstr_ = bstr2.bstr_; + bstr2.bstr_ = tmp; +} + +BSTR* ScopedBstr::Receive() { + DCHECK(bstr_ == NULL) << "BSTR leak."; + return &bstr_; +} + +BSTR ScopedBstr::Allocate(const char16* str) { + Reset(SysAllocString(str)); + return bstr_; +} + +BSTR ScopedBstr::AllocateBytes(size_t bytes) { + Reset(SysAllocStringByteLen(NULL, static_cast<UINT>(bytes))); + return bstr_; +} + +void ScopedBstr::SetByteLen(size_t bytes) { + DCHECK(bstr_ != NULL) << "attempting to modify a NULL bstr"; + uint32* data = reinterpret_cast<uint32*>(bstr_); + data[-1] = static_cast<uint32>(bytes); +} + +size_t ScopedBstr::Length() const { + return SysStringLen(bstr_); +} + +size_t ScopedBstr::ByteLength() const { + return SysStringByteLen(bstr_); +} + +} // namespace win +} // namespace base diff --git a/base/win/scoped_bstr.h b/base/win/scoped_bstr.h new file mode 100644 index 0000000..944562e --- /dev/null +++ b/base/win/scoped_bstr.h @@ -0,0 +1,97 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_WIN_SCOPED_BSTR_H_ +#define BASE_WIN_SCOPED_BSTR_H_ +#pragma once + +#include <windows.h> +#include <oleauto.h> + +#include "base/logging.h" +#include "base/string16.h" + +namespace base { +namespace win { + +// Manages a BSTR string pointer. +// The class interface is based on scoped_ptr. +class ScopedBstr { + public: + ScopedBstr() : bstr_(NULL) { + } + + // Constructor to create a new BSTR. + // + // NOTE: Do not pass a BSTR to this constructor expecting ownership to + // be transferred - even though it compiles! ;-) + explicit ScopedBstr(const char16* non_bstr); + ~ScopedBstr(); + + // Give ScopedBstr ownership over an already allocated BSTR or NULL. + // If you need to allocate a new BSTR instance, use |allocate| instead. + void Reset(BSTR bstr = NULL); + + // Releases ownership of the BSTR to the caller. + BSTR Release(); + + // Creates a new BSTR from a 16-bit C-style string. + // + // If you already have a BSTR and want to transfer ownership to the + // ScopedBstr instance, call |reset| instead. + // + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR Allocate(const char16* str); + + // Allocates a new BSTR with the specified number of bytes. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR AllocateBytes(size_t bytes); + + // Sets the allocated length field of the already-allocated BSTR to be + // |bytes|. This is useful when the BSTR was preallocated with e.g. + // SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and then + // not all the bytes are being used. + // + // Note that if you want to set the length to a specific number of + // characters, you need to multiply by sizeof(wchar_t). Oddly, there's no + // public API to set the length, so we do this ourselves by hand. + // + // NOTE: The actual allocated size of the BSTR MUST be >= bytes. That + // responsibility is with the caller. + void SetByteLen(size_t bytes); + + // Swap values of two ScopedBstr's. + void Swap(ScopedBstr& bstr2); + + // Retrieves the pointer address. + // Used to receive BSTRs as out arguments (and take ownership). + // The function DCHECKs on the current value being NULL. + // Usage: GetBstr(bstr.Receive()); + BSTR* Receive(); + + // Returns number of chars in the BSTR. + size_t Length() const; + + // Returns the number of bytes allocated for the BSTR. + size_t ByteLength() const; + + operator BSTR() const { + return bstr_; + } + + protected: + BSTR bstr_; + + private: + // Forbid comparison of ScopedBstr types. You should never have the same + // BSTR owned by two different scoped_ptrs. + bool operator==(const ScopedBstr& bstr2) const; + bool operator!=(const ScopedBstr& bstr2) const; + DISALLOW_COPY_AND_ASSIGN(ScopedBstr); +}; + +} // namespace win +} // namespace base + +#endif // BASE_SCOPED_BSTR_H_ diff --git a/base/win/scoped_bstr_unittest.cc b/base/win/scoped_bstr_unittest.cc new file mode 100644 index 0000000..5f6f7df --- /dev/null +++ b/base/win/scoped_bstr_unittest.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2010 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/win/scoped_bstr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace win { + +namespace { + +static const wchar_t kTestString1[] = L"123"; +static const wchar_t kTestString2[] = L"456789"; +size_t test1_len = arraysize(kTestString1) - 1; +size_t test2_len = arraysize(kTestString2) - 1; + +void DumbBstrTests() { + ScopedBstr b; + EXPECT_TRUE(b == NULL); + EXPECT_EQ(0, b.Length()); + EXPECT_EQ(0, b.ByteLength()); + b.Reset(NULL); + EXPECT_TRUE(b == NULL); + EXPECT_TRUE(b.Release() == NULL); + ScopedBstr b2; + b.Swap(b2); + EXPECT_TRUE(b2 == NULL); +} + +void GiveMeABstr(BSTR* ret) { + *ret = SysAllocString(kTestString1); +} + +void BasicBstrTests() { + ScopedBstr b1(kTestString1); + EXPECT_EQ(test1_len, b1.Length()); + EXPECT_EQ(test1_len * sizeof(kTestString1[0]), b1.ByteLength()); + + ScopedBstr b2; + b1.Swap(b2); + EXPECT_EQ(test1_len, b2.Length()); + EXPECT_EQ(0, b1.Length()); + EXPECT_EQ(0, lstrcmp(b2, kTestString1)); + BSTR tmp = b2.Release(); + EXPECT_TRUE(tmp != NULL); + EXPECT_EQ(0, lstrcmp(tmp, kTestString1)); + EXPECT_TRUE(b2 == NULL); + SysFreeString(tmp); + + GiveMeABstr(b2.Receive()); + EXPECT_TRUE(b2 != NULL); + b2.Reset(); + EXPECT_TRUE(b2.AllocateBytes(100) != NULL); + EXPECT_EQ(100, b2.ByteLength()); + EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length()); + lstrcpy(static_cast<BSTR>(b2), kTestString1); + EXPECT_EQ(test1_len, lstrlen(b2)); + EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length()); + b2.SetByteLen(lstrlen(b2) * sizeof(kTestString2[0])); + EXPECT_EQ(b2.Length(), lstrlen(b2)); + + EXPECT_TRUE(b1.Allocate(kTestString2) != NULL); + EXPECT_EQ(test2_len, b1.Length()); + b1.SetByteLen((test2_len - 1) * sizeof(kTestString2[0])); + EXPECT_EQ(test2_len - 1, b1.Length()); +} + +} // namespace + +TEST(ScopedBstrTest, ScopedBstr) { + DumbBstrTests(); + BasicBstrTests(); +} + +} // namespace win +} // namespace base diff --git a/base/win/scoped_comptr.h b/base/win/scoped_comptr.h new file mode 100644 index 0000000..385c79f --- /dev/null +++ b/base/win/scoped_comptr.h @@ -0,0 +1,165 @@ +// Copyright (c) 2006-2008 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. + +#ifndef BASE_WIN_SCOPED_COMPTR_H_ +#define BASE_WIN_SCOPED_COMPTR_H_ +#pragma once + +#include <unknwn.h> + +#include "base/logging.h" +#include "base/ref_counted.h" + +namespace base { +namespace win { + +// A fairly minimalistic smart class for COM interface pointers. +// Uses scoped_refptr for the basic smart pointer functionality +// and adds a few IUnknown specific services. +template <class Interface, const IID* interface_id = &__uuidof(Interface)> +class ScopedComPtr : public scoped_refptr<Interface> { + public: + // Utility template to prevent users of ScopedComPtr from calling AddRef + // and/or Release() without going through the ScopedComPtr class. + template <class Interface> + class BlockIUnknownMethods : public Interface { + private: + STDMETHOD(QueryInterface)(REFIID iid, void** object) = 0; + STDMETHOD_(ULONG, AddRef)() = 0; + STDMETHOD_(ULONG, Release)() = 0; + }; + + typedef scoped_refptr<Interface> ParentClass; + + ScopedComPtr() { + } + + explicit ScopedComPtr(Interface* p) : ParentClass(p) { + } + + ScopedComPtr(const ScopedComPtr<Interface, interface_id>& p) + : ParentClass(p) { + } + + ~ScopedComPtr() { + // We don't want the smart pointer class to be bigger than the pointer + // it wraps. + COMPILE_ASSERT(sizeof(ScopedComPtr<Interface, interface_id>) == + sizeof(Interface*), ScopedComPtrSize); + } + + // Explicit Release() of the held object. Useful for reuse of the + // ScopedComPtr instance. + // Note that this function equates to IUnknown::Release and should not + // be confused with e.g. scoped_ptr::release(). + void Release() { + if (ptr_ != NULL) { + ptr_->Release(); + ptr_ = NULL; + } + } + + // Sets the internal pointer to NULL and returns the held object without + // releasing the reference. + Interface* Detach() { + Interface* p = ptr_; + ptr_ = NULL; + return p; + } + + // Accepts an interface pointer that has already been addref-ed. + void Attach(Interface* p) { + DCHECK(ptr_ == NULL); + ptr_ = p; + } + + // Retrieves the pointer address. + // Used to receive object pointers as out arguments (and take ownership). + // The function DCHECKs on the current value being NULL. + // Usage: Foo(p.Receive()); + Interface** Receive() { + DCHECK(ptr_ == NULL) << "Object leak. Pointer must be NULL"; + return &ptr_; + } + + template <class Query> + HRESULT QueryInterface(Query** p) { + DCHECK(p != NULL); + DCHECK(ptr_ != NULL); + // IUnknown already has a template version of QueryInterface + // so the iid parameter is implicit here. The only thing this + // function adds are the DCHECKs. + return ptr_->QueryInterface(p); + } + + // QI for times when the IID is not associated with the type. + HRESULT QueryInterface(const IID& iid, void** obj) { + DCHECK(obj != NULL); + DCHECK(ptr_ != NULL); + return ptr_->QueryInterface(iid, obj); + } + + // Queries |other| for the interface this object wraps and returns the + // error code from the other->QueryInterface operation. + HRESULT QueryFrom(IUnknown* object) { + DCHECK(object != NULL); + return object->QueryInterface(Receive()); + } + + // Convenience wrapper around CoCreateInstance + HRESULT CreateInstance(const CLSID& clsid, IUnknown* outer = NULL, + DWORD context = CLSCTX_ALL) { + DCHECK(ptr_ == NULL); + HRESULT hr = ::CoCreateInstance(clsid, outer, context, *interface_id, + reinterpret_cast<void**>(&ptr_)); + return hr; + } + + // Checks if the identity of |other| and this object is the same. + bool IsSameObject(IUnknown* other) { + if (!other && !ptr_) + return true; + + if (!other || !ptr_) + return false; + + ScopedComPtr<IUnknown> my_identity; + QueryInterface(my_identity.Receive()); + + ScopedComPtr<IUnknown> other_identity; + other->QueryInterface(other_identity.Receive()); + + return static_cast<IUnknown*>(my_identity) == + static_cast<IUnknown*>(other_identity); + } + + // Provides direct access to the interface. + // Here we use a well known trick to make sure we block access to + // IUknown methods so that something bad like this doesn't happen: + // ScopedComPtr<IUnknown> p(Foo()); + // p->Release(); + // ... later the destructor runs, which will Release() again. + // and to get the benefit of the DCHECKs we add to QueryInterface. + // There's still a way to call these methods if you absolutely must + // by statically casting the ScopedComPtr instance to the wrapped interface + // and then making the call... but generally that shouldn't be necessary. + BlockIUnknownMethods<Interface>* operator->() const { + DCHECK(ptr_ != NULL); + return reinterpret_cast<BlockIUnknownMethods<Interface>*>(ptr_); + } + + // Pull in operator=() from the parent class. + using scoped_refptr<Interface>::operator=; + + // static methods + + static const IID& iid() { + return *interface_id; + } +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_COMPTR_H_ diff --git a/base/win/scoped_comptr_unittest.cc b/base/win/scoped_comptr_unittest.cc new file mode 100644 index 0000000..fcd0791 --- /dev/null +++ b/base/win/scoped_comptr_unittest.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2006-2008 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/win/scoped_comptr.h" + +#include <shlobj.h> + +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace win { + +namespace { + +struct Dummy { + Dummy() : adds(0), releases(0) { } + void AddRef() { ++adds; } + void Release() { ++releases; } + + int adds; + int releases; +}; + +extern const IID dummy_iid; +const IID dummy_iid = { 0x12345678u, 0x1234u, 0x5678u, 01, 23, 45, 67, 89, + 01, 23, 45 }; + +} // namespace + +TEST(ScopedComPtrTest, ScopedComPtr) { + EXPECT_TRUE(memcmp(&ScopedComPtr<IUnknown>::iid(), &IID_IUnknown, + sizeof(IID)) == 0); + + EXPECT_TRUE(SUCCEEDED(::CoInitialize(NULL))); + + { + ScopedComPtr<IUnknown> unk; + EXPECT_TRUE(SUCCEEDED(unk.CreateInstance(CLSID_ShellLink))); + ScopedComPtr<IUnknown> unk2; + unk2.Attach(unk.Detach()); + EXPECT_TRUE(unk == NULL); + EXPECT_TRUE(unk2 != NULL); + + ScopedComPtr<IMalloc> mem_alloc; + EXPECT_TRUE(SUCCEEDED(CoGetMalloc(1, mem_alloc.Receive()))); + + ScopedComPtr<IUnknown> qi_test; + EXPECT_HRESULT_SUCCEEDED(mem_alloc.QueryInterface(IID_IUnknown, + reinterpret_cast<void**>(qi_test.Receive()))); + EXPECT_TRUE(qi_test.get() != NULL); + qi_test.Release(); + + // test ScopedComPtr& constructor + ScopedComPtr<IMalloc> copy1(mem_alloc); + EXPECT_TRUE(copy1.IsSameObject(mem_alloc)); + EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid but different + EXPECT_FALSE(copy1.IsSameObject(unk)); // unk is NULL + + IMalloc* naked_copy = copy1.Detach(); + copy1 = naked_copy; // Test the =(T*) operator. + naked_copy->Release(); + + copy1.Release(); + EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid, copy1 is not + + // test Interface* constructor + ScopedComPtr<IMalloc> copy2(static_cast<IMalloc*>(mem_alloc)); + EXPECT_TRUE(copy2.IsSameObject(mem_alloc)); + + EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc))); + EXPECT_TRUE(unk != NULL); + unk.Release(); + EXPECT_TRUE(unk == NULL); + EXPECT_TRUE(unk.IsSameObject(copy1)); // both are NULL + } + + ::CoUninitialize(); +} + +TEST(ScopedComPtrTest, ScopedComPtrVector) { + // Verify we don't get error C2558. + typedef ScopedComPtr<Dummy, &dummy_iid> Ptr; + std::vector<Ptr> bleh; + + scoped_ptr<Dummy> p(new Dummy); + { + Ptr p2(p.get()); + EXPECT_EQ(p->adds, 1); + EXPECT_EQ(p->releases, 0); + Ptr p3 = p2; + EXPECT_EQ(p->adds, 2); + EXPECT_EQ(p->releases, 0); + p3 = p2; + EXPECT_EQ(p->adds, 3); + EXPECT_EQ(p->releases, 1); + // To avoid hitting a reallocation. + bleh.reserve(1); + bleh.push_back(p2); + EXPECT_EQ(p->adds, 4); + EXPECT_EQ(p->releases, 1); + EXPECT_EQ(bleh[0], p.get()); + bleh.pop_back(); + EXPECT_EQ(p->adds, 4); + EXPECT_EQ(p->releases, 2); + } + EXPECT_EQ(p->adds, 4); + EXPECT_EQ(p->releases, 4); +} + +} // namespace win +} // namespace base
\ No newline at end of file diff --git a/base/win/scoped_gdi_object.h b/base/win/scoped_gdi_object.h new file mode 100644 index 0000000..3ce2245 --- /dev/null +++ b/base/win/scoped_gdi_object.h @@ -0,0 +1,78 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_WIN_SCOPED_GDI_OBJECT_H_ +#define BASE_WIN_SCOPED_GDI_OBJECT_H_ +#pragma once + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace base { +namespace win { + +// Like ScopedHandle but for GDI objects. +template<class T> +class ScopedGDIObject { + public: + ScopedGDIObject() : object_(NULL) {} + explicit ScopedGDIObject(T object) : object_(object) {} + + ~ScopedGDIObject() { + Close(); + } + + T Get() { + return object_; + } + + void Set(T object) { + if (object_ && object != object_) + Close(); + object_ = object; + } + + ScopedGDIObject& operator=(T object) { + Set(object); + return *this; + } + + T release() { + T object = object_; + object_ = NULL; + return object; + } + + operator T() { return object_; } + + private: + void Close() { + if (object_) + DeleteObject(object_); + } + + T object_; + DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject); +}; + +// An explicit specialization for HICON because we have to call DestroyIcon() +// instead of DeleteObject() for HICON. +template<> +void ScopedGDIObject<HICON>::Close() { + if (object_) + DestroyIcon(object_); +} + +// Typedefs for some common use cases. +typedef ScopedGDIObject<HBITMAP> ScopedBitmap; +typedef ScopedGDIObject<HRGN> ScopedRegion; +typedef ScopedGDIObject<HFONT> ScopedHFONT; +typedef ScopedGDIObject<HICON> ScopedHICON; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_GDI_OBJECT_H_ diff --git a/base/win/scoped_handle.h b/base/win/scoped_handle.h new file mode 100644 index 0000000..3bb0279 --- /dev/null +++ b/base/win/scoped_handle.h @@ -0,0 +1,90 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_WIN_SCOPED_HANDLE_H_ +#define BASE_WIN_SCOPED_HANDLE_H_ +#pragma once + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace base { +namespace win { + +// Used so we always remember to close the handle. +// The class interface matches that of ScopedStdioHandle in addition to an +// IsValid() method since invalid handles on windows can be either NULL or +// INVALID_HANDLE_VALUE (-1). +// +// Example: +// ScopedHandle hfile(CreateFile(...)); +// if (!hfile.Get()) +// ...process error +// ReadFile(hfile.Get(), ...); +// +// To sqirrel the handle away somewhere else: +// secret_handle_ = hfile.Take(); +// +// To explicitly close the handle: +// hfile.Close(); +class ScopedHandle { + public: + ScopedHandle() : handle_(NULL) { + } + + explicit ScopedHandle(HANDLE h) : handle_(NULL) { + Set(h); + } + + ~ScopedHandle() { + Close(); + } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { + return handle_ != NULL; + } + + void Set(HANDLE new_handle) { + Close(); + + // Windows is inconsistent about invalid handles, so we always use NULL + if (new_handle != INVALID_HANDLE_VALUE) + handle_ = new_handle; + } + + HANDLE Get() { + return handle_; + } + + operator HANDLE() { return handle_; } + + HANDLE Take() { + // transfers ownership away from this object + HANDLE h = handle_; + handle_ = NULL; + return h; + } + + void Close() { + if (handle_) { + if (!::CloseHandle(handle_)) { + NOTREACHED(); + } + handle_ = NULL; + } + } + + private: + HANDLE handle_; + DISALLOW_COPY_AND_ASSIGN(ScopedHandle); +}; + +} // namespace win +} // namespace base + +#endif // BASE_SCOPED_HANDLE_WIN_H_ diff --git a/base/win/scoped_hdc.h b/base/win/scoped_hdc.h new file mode 100644 index 0000000..73d369a7 --- /dev/null +++ b/base/win/scoped_hdc.h @@ -0,0 +1,55 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_WIN_SCOPED_HDC_H_ +#define BASE_WIN_SCOPED_HDC_H_ +#pragma once + +#include <windows.h> + +#include "base/basictypes.h" + +namespace base { +namespace win { + +// Like ScopedHandle but for HDC. Only use this on HDCs returned from +// CreateCompatibleDC. For an HDC returned by GetDC, use ReleaseDC instead. +class ScopedHDC { + public: + ScopedHDC() : hdc_(NULL) { } + explicit ScopedHDC(HDC h) : hdc_(h) { } + + ~ScopedHDC() { + Close(); + } + + HDC Get() { + return hdc_; + } + + void Set(HDC h) { + Close(); + hdc_ = h; + } + + operator HDC() { return hdc_; } + + private: + void Close() { +#ifdef NOGDI + assert(false); +#else + if (hdc_) + DeleteDC(hdc_); +#endif // NOGDI + } + + HDC hdc_; + DISALLOW_COPY_AND_ASSIGN(ScopedHDC); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_HDC_H_ diff --git a/base/win/scoped_hglobal.h b/base/win/scoped_hglobal.h new file mode 100644 index 0000000..23b36e4 --- /dev/null +++ b/base/win/scoped_hglobal.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_WIN_SCOPED_HGLOBAL_H_ +#define BASE_WIN_SCOPED_HGLOBAL_H_ +#pragma once + +#include <windows.h> + +#include "base/basictypes.h" + +namespace base { +namespace win { + +// Like ScopedHandle except for HGLOBAL. +template<class T> +class ScopedHGlobal { + public: + explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) { + data_ = static_cast<T*>(GlobalLock(glob_)); + } + ~ScopedHGlobal() { + GlobalUnlock(glob_); + } + + T* get() { return data_; } + + size_t Size() const { return GlobalSize(glob_); } + + T* operator->() const { + assert(data_ != 0); + return data_; + } + + T* release() { + T* data = data_; + data_ = NULL; + return data; + } + + private: + HGLOBAL glob_; + + T* data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedHGlobal); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_SCOPED_HGLOBAL_H_ diff --git a/base/win/scoped_variant.cc b/base/win/scoped_variant.cc new file mode 100644 index 0000000..278e976 --- /dev/null +++ b/base/win/scoped_variant.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2010 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" + +namespace base { +namespace win { + +// 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; +} + +} // namespace win +} // namespace base diff --git a/base/win/scoped_variant.h b/base/win/scoped_variant.h new file mode 100644 index 0000000..6c4d23f --- /dev/null +++ b/base/win/scoped_variant.h @@ -0,0 +1,166 @@ +// Copyright (c) 2010 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. + +#ifndef BASE_WIN_SCOPED_VARIANT_H_ +#define BASE_WIN_SCOPED_VARIANT_H_ +#pragma once + +#include <windows.h> +#include <oleauto.h> + +#include "base/basictypes.h" + +namespace base { +namespace win { + +// Scoped VARIANT class for automatically freeing a COM VARIANT at the +// end of a scope. Additionally provides a few functions to make the +// encapsulated VARIANT easier to use. +// Instead of inheriting from VARIANT, we take the containment approach +// in order to have more control over the usage of the variant and guard +// against memory leaks. +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); + + // Creates a new double-precision type variant. |vt| must be either VT_R8 + // or VT_DATE. + explicit ScopedVariant(double value, VARTYPE vt = VT_R8); + + // VT_DISPATCH + explicit ScopedVariant(IDispatch* dispatch); + + // VT_UNKNOWN + explicit ScopedVariant(IUnknown* unknown); + + // SAFEARRAY + explicit ScopedVariant(SAFEARRAY* safearray); + + // 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); +}; + +} // namespace win +} // namesoace base + +#endif // BASE_WIN_SCOPED_VARIANT_H_ diff --git a/base/win/scoped_variant_unittest.cc b/base/win/scoped_variant_unittest.cc new file mode 100644 index 0000000..2cd5be1 --- /dev/null +++ b/base/win/scoped_variant_unittest.cc @@ -0,0 +1,263 @@ +// 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/win/scoped_variant.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace win { + +namespace { + +static const wchar_t kTestString1[] = L"Used to create BSTRs"; +static const wchar_t kTestString2[] = L"Also used to create BSTRs"; + +void GiveMeAVariant(VARIANT* ret) { + EXPECT_TRUE(ret != NULL); + ret->vt = VT_BSTR; + V_BSTR(ret) = ::SysAllocString(kTestString1); +} + +// A dummy IDispatch implementation (if you can call it that). +// The class does nothing intelligent really. Only increments a counter +// when AddRef is called and decrements it when Release is called. +class FakeComObject : public IDispatch { + public: + FakeComObject() : ref_(0) { + } + + STDMETHOD_(DWORD, AddRef)() { + ref_++; + return ref_; + } + + STDMETHOD_(DWORD, Release)() { + ref_--; + return ref_; + } + + STDMETHOD(QueryInterface)(REFIID, void**) { + return E_NOTIMPL; + } + + STDMETHOD(GetTypeInfoCount)(UINT*) { + return E_NOTIMPL; + } + + STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) { + return E_NOTIMPL; + } + + STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) { + return E_NOTIMPL; + } + + STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, + EXCEPINFO*, UINT*) { + return E_NOTIMPL; + } + + // A way to check the internal reference count of the class. + int ref_count() const { + return ref_; + } + + protected: + int ref_; +}; + +} // namespace + +TEST(ScopedVariantTest, ScopedVariant) { + ScopedVariant var; + EXPECT_TRUE(var.type() == VT_EMPTY); + // V_BSTR(&var) = NULL; <- NOTE: Assignment like that is not supported + + ScopedVariant var_bstr(L"VT_BSTR"); + EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + EXPECT_TRUE(V_BSTR(&var_bstr) != NULL); // can't use EXPECT_NE for BSTR + var_bstr.Reset(); + EXPECT_NE(VT_BSTR, V_VT(&var_bstr)); + var_bstr.Set(kTestString2); + EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + + VARIANT tmp = var_bstr.Release(); + EXPECT_EQ(VT_EMPTY, V_VT(&var_bstr)); + EXPECT_EQ(VT_BSTR, V_VT(&tmp)); + EXPECT_EQ(0, lstrcmp(V_BSTR(&tmp), kTestString2)); + + var.Reset(tmp); + EXPECT_EQ(VT_BSTR, V_VT(&var)); + EXPECT_EQ(0, lstrcmpW(V_BSTR(&var), kTestString2)); + + var_bstr.Swap(var); + EXPECT_EQ(VT_EMPTY, V_VT(&var)); + EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + EXPECT_EQ(0, lstrcmpW(V_BSTR(&var_bstr), kTestString2)); + var_bstr.Reset(); + + // Test the Compare and Copy routines. + GiveMeAVariant(var_bstr.Receive()); + ScopedVariant var_bstr2(V_BSTR(&var_bstr)); + EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(); + EXPECT_NE(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(var_bstr.Copy()); + EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(); + var_bstr2.Set(V_BSTR(&var_bstr)); + EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(); + var_bstr.Reset(); + + // Test for the SetDate setter. + SYSTEMTIME sys_time; + ::GetSystemTime(&sys_time); + DATE date; + ::SystemTimeToVariantTime(&sys_time, &date); + var.Reset(); + var.SetDate(date); + EXPECT_EQ(VT_DATE, var.type()); + EXPECT_EQ(date, V_DATE(&var)); + + // Simple setter tests. These do not require resetting the variant + // after each test since the variant type is not "leakable" (i.e. doesn't + // need to be freed explicitly). + + // We need static cast here since char defaults to int (!?). + var.Set(static_cast<int8>('v')); + EXPECT_EQ(VT_I1, var.type()); + EXPECT_EQ('v', V_I1(&var)); + + var.Set(static_cast<short>(123)); + EXPECT_EQ(VT_I2, var.type()); + EXPECT_EQ(123, V_I2(&var)); + + var.Set(static_cast<int32>(123)); + EXPECT_EQ(VT_I4, var.type()); + EXPECT_EQ(123, V_I4(&var)); + + var.Set(static_cast<int64>(123)); + EXPECT_EQ(VT_I8, var.type()); + EXPECT_EQ(123, V_I8(&var)); + + var.Set(static_cast<uint8>(123)); + EXPECT_EQ(VT_UI1, var.type()); + EXPECT_EQ(123, V_UI1(&var)); + + var.Set(static_cast<unsigned short>(123)); + EXPECT_EQ(VT_UI2, var.type()); + EXPECT_EQ(123, V_UI2(&var)); + + var.Set(static_cast<uint32>(123)); + EXPECT_EQ(VT_UI4, var.type()); + EXPECT_EQ(123, V_UI4(&var)); + + var.Set(static_cast<uint64>(123)); + EXPECT_EQ(VT_UI8, var.type()); + EXPECT_EQ(123, V_UI8(&var)); + + var.Set(123.123f); + EXPECT_EQ(VT_R4, var.type()); + EXPECT_EQ(123.123f, V_R4(&var)); + + var.Set(static_cast<double>(123.123)); + EXPECT_EQ(VT_R8, var.type()); + EXPECT_EQ(123.123, V_R8(&var)); + + var.Set(true); + EXPECT_EQ(VT_BOOL, var.type()); + EXPECT_EQ(VARIANT_TRUE, V_BOOL(&var)); + var.Set(false); + EXPECT_EQ(VT_BOOL, var.type()); + EXPECT_EQ(VARIANT_FALSE, V_BOOL(&var)); + + // Com interface tests + + var.Set(static_cast<IDispatch*>(NULL)); + EXPECT_EQ(VT_DISPATCH, var.type()); + EXPECT_EQ(NULL, V_DISPATCH(&var)); + var.Reset(); + + var.Set(static_cast<IUnknown*>(NULL)); + EXPECT_EQ(VT_UNKNOWN, var.type()); + EXPECT_EQ(NULL, V_UNKNOWN(&var)); + var.Reset(); + + FakeComObject faker; + EXPECT_EQ(0, faker.ref_count()); + var.Set(static_cast<IDispatch*>(&faker)); + EXPECT_EQ(VT_DISPATCH, var.type()); + EXPECT_EQ(&faker, V_DISPATCH(&var)); + EXPECT_EQ(1, faker.ref_count()); + var.Reset(); + EXPECT_EQ(0, faker.ref_count()); + + var.Set(static_cast<IUnknown*>(&faker)); + EXPECT_EQ(VT_UNKNOWN, var.type()); + EXPECT_EQ(&faker, V_UNKNOWN(&var)); + EXPECT_EQ(1, faker.ref_count()); + var.Reset(); + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant disp_var(&faker); + EXPECT_EQ(VT_DISPATCH, disp_var.type()); + EXPECT_EQ(&faker, V_DISPATCH(&disp_var)); + EXPECT_EQ(1, faker.ref_count()); + } + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant ref1(&faker); + EXPECT_EQ(1, faker.ref_count()); + ScopedVariant ref2(static_cast<const VARIANT&>(ref1)); + EXPECT_EQ(2, faker.ref_count()); + ScopedVariant ref3; + ref3 = static_cast<const VARIANT&>(ref2); + EXPECT_EQ(3, faker.ref_count()); + } + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant unk_var(static_cast<IUnknown*>(&faker)); + EXPECT_EQ(VT_UNKNOWN, unk_var.type()); + EXPECT_EQ(&faker, V_UNKNOWN(&unk_var)); + EXPECT_EQ(1, faker.ref_count()); + } + EXPECT_EQ(0, faker.ref_count()); + + VARIANT raw; + raw.vt = VT_UNKNOWN; + raw.punkVal = &faker; + EXPECT_EQ(0, faker.ref_count()); + var.Set(raw); + EXPECT_EQ(1, faker.ref_count()); + var.Reset(); + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant number(123); + EXPECT_EQ(VT_I4, number.type()); + EXPECT_EQ(123, V_I4(&number)); + } + + // SAFEARRAY tests + var.Set(static_cast<SAFEARRAY*>(NULL)); + EXPECT_EQ(VT_EMPTY, var.type()); + + SAFEARRAY* sa = ::SafeArrayCreateVector(VT_UI1, 0, 100); + ASSERT_TRUE(sa != NULL); + + var.Set(sa); +#ifndef OFFICIAL_BUILD + EXPECT_TRUE(ScopedVariant::IsLeakableVarType(var.type())); +#endif + EXPECT_EQ(VT_ARRAY | VT_UI1, var.type()); + EXPECT_EQ(sa, V_ARRAY(&var)); + // The array is destroyed in the destructor of var. +} + +} // namespace win +} // namespace base |