summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authortommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-12 20:10:49 +0000
committertommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-12 20:10:49 +0000
commit49457c32741599d3c79e7f345ba51400748fb807 (patch)
treeba17debd9c2dc5569952f6d1bd5abab683a4bfd3 /base
parent2786f3db2489aa1fd2453fa85745e5c5658b1cd1 (diff)
downloadchromium_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.scons2
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/scoped_comptr.h151
-rw-r--r--base/scoped_comptr_unittest.cc51
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