diff options
author | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-12 20:10:49 +0000 |
---|---|---|
committer | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-12 20:10:49 +0000 |
commit | 49457c32741599d3c79e7f345ba51400748fb807 (patch) | |
tree | ba17debd9c2dc5569952f6d1bd5abab683a4bfd3 /base | |
parent | 2786f3db2489aa1fd2453fa85745e5c5658b1cd1 (diff) | |
download | chromium_src-49457c32741599d3c79e7f345ba51400748fb807.zip chromium_src-49457c32741599d3c79e7f345ba51400748fb807.tar.gz chromium_src-49457c32741599d3c79e7f345ba51400748fb807.tar.bz2 |
com_ptr implementation. This is a fairly simple implementation of a smart class specifically meant for IUnknown derived COM interfaces.
The class gets much of its functionality from the already existing scoped_refptr but adds a few COM specific methods and some that scoped_refptr doesn't already have, yet are often necessary such as Receive(), Detach() and Attach().
I went with caps for the first letter of method names, but for the three I just mentioned, it might be prettier to have them in all lower case to better match with methods from the parent class. Let me know what you think.
Review URL: http://codereview.chromium.org/17473
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7890 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base_unittests.scons | 2 | ||||
-rw-r--r-- | base/build/base.vcproj | 8 | ||||
-rw-r--r-- | base/build/base_unittests.vcproj | 4 | ||||
-rw-r--r-- | base/scoped_comptr.h | 151 | ||||
-rw-r--r-- | base/scoped_comptr_unittest.cc | 51 |
5 files changed, 214 insertions, 2 deletions
diff --git a/base/base_unittests.scons b/base/base_unittests.scons index e76939c..3dd851e 100644 --- a/base/base_unittests.scons +++ b/base/base_unittests.scons @@ -84,6 +84,7 @@ input_files = ChromeFileList([ 'rand_util_unittest.cc', 'gfx/rect_unittest.cc', 'ref_counted_unittest.cc', + 'scoped_comptr_unittest.cc', 'scoped_ptr_unittest.cc', 'sha2_unittest.cc', 'shared_memory_unittest.cc', @@ -152,6 +153,7 @@ if not env.Bit('windows'): 'file_version_info_unittest.cc', 'object_watcher_unittest.cc', 'pe_image_unittest.cc', + 'scoped_comptr_unittest.cc', 'system_monitor_unittest.cc', 'sys_string_conversions_unittest.cc', 'time_unittest_win.cc', diff --git a/base/build/base.vcproj b/base/build/base.vcproj index 10c7dad..606d530 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -694,6 +694,10 @@ > </File> <File + RelativePath="..\scoped_comptr.h" + > + </File> + <File RelativePath="..\scoped_handle.h" > </File> @@ -834,11 +838,11 @@ > </File> <File - RelativePath="..\test_file_util_win.cc" + RelativePath="..\test_file_util.h" > </File> <File - RelativePath="..\test_file_util.h" + RelativePath="..\test_file_util_win.cc" > </File> <File diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj index 8263e074..eb148d5 100644 --- a/base/build/base_unittests.vcproj +++ b/base/build/base_unittests.vcproj @@ -280,6 +280,10 @@ > </File> <File + RelativePath="..\scoped_comptr_unittest.cc" + > + </File> + <File RelativePath="..\scoped_ptr_unittest.cc" > </File> diff --git a/base/scoped_comptr.h b/base/scoped_comptr.h new file mode 100644 index 0000000..e8f1275 --- /dev/null +++ b/base/scoped_comptr.h @@ -0,0 +1,151 @@ +// 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_SCOPED_COMPTR_H_ +#define BASE_SCOPED_COMPTR_H_ + +#include "base/logging.h" +#include "base/ref_counted.h" + +#if defined(OS_WIN) +#include <unknwn.h> + +// Utility template to prevent users of scoped_comptr from calling AddRef and/or +// Release() without going through the scoped_comptr class. +template <class Interface>
+class BlockIUnknownMethods : public Interface {
+ private:
+ STDMETHOD(QueryInterface)(REFIID iid, void** object) = 0; + STDMETHOD_(ULONG, AddRef)() = 0;
+ STDMETHOD_(ULONG, Release)() = 0;
+};
+ +// 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 scoped_comptr : public scoped_refptr<Interface> { + public: + typedef scoped_refptr<Interface> ParentClass; + + scoped_comptr() { + } + + explicit scoped_comptr(Interface* p) : ParentClass(p) { + } + + explicit scoped_comptr(const scoped_comptr<Interface, interface_id>& p) + : ParentClass(p) { + } + + ~scoped_comptr() { + // We don't want the smart pointer class to be bigger than the pointer + // it wraps. + COMPILE_ASSERT(sizeof(scoped_comptr<Interface, interface_id>) == + sizeof(Interface*), ScopedComPtrSize); + } + + // Explicit Release() of the held object. Useful for reuse of the + // scoped_comptr 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); + } + + // 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;
+
+ scoped_comptr<IUnknown> my_identity;
+ QueryInterface(my_identity.Receive());
+
+ scoped_comptr<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: + // scoped_comptr<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 scoped_comptr 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_); + } + + // static methods + + static const IID& iid() { + return *interface_id; + } +}; + +#endif // #if defined(OS_WIN) + +#endif // BASE_SCOPED_COMPTR_H_ diff --git a/base/scoped_comptr_unittest.cc b/base/scoped_comptr_unittest.cc new file mode 100644 index 0000000..91f592a --- /dev/null +++ b/base/scoped_comptr_unittest.cc @@ -0,0 +1,51 @@ +// 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/scoped_comptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "chrome/common/win_util.h" + +#if defined(OS_WIN) +#include <shlobj.h> + +TEST(ScopedComPtrTest, ScopedComPtr) { + EXPECT_TRUE(memcmp(&scoped_comptr<IUnknown>::iid(), &IID_IUnknown, + sizeof(IID)) == 0); + + EXPECT_TRUE(SUCCEEDED(::CoInitialize(NULL))); + + { + scoped_comptr<IUnknown> unk; + EXPECT_TRUE(SUCCEEDED(unk.CreateInstance(CLSID_ShellLink))); + scoped_comptr<IUnknown> unk2; + unk2.Attach(unk.Detach()); + EXPECT_TRUE(unk == NULL); + EXPECT_TRUE(unk2 != NULL); + + scoped_comptr<IMalloc> mem_alloc; + EXPECT_TRUE(SUCCEEDED(CoGetMalloc(1, mem_alloc.Receive()))); + + // test scoped_comptr& constructor + scoped_comptr<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 + copy1.Release(); + EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid, copy1 is not + + // test Interface* constructor + scoped_comptr<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(); +} + +#endif // defined(OS_WIN)
\ No newline at end of file |