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
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 @@
     '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 @@
       '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..606d530b 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..e8f12755
--- /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