1 // Copyright (c) 2012 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 #ifndef BASE_WIN_SCOPED_COM_INITIALIZER_H_
6 #define BASE_WIN_SCOPED_COM_INITIALIZER_H_
7 
8 #include <objbase.h>
9 
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "build/build_config.h"
13 
14 namespace base {
15 namespace win {
16 
17 // Initializes COM in the constructor (STA or MTA), and uninitializes COM in the
18 // destructor.
19 //
20 // WARNING: This should only be used once per thread, ideally scoped to a
21 // similar lifetime as the thread itself.  You should not be using this in
22 // random utility functions that make COM calls -- instead ensure these
23 // functions are running on a COM-supporting thread!
24 class ScopedCOMInitializer {
25  public:
26   // Enum value provided to initialize the thread as an MTA instead of STA.
27   enum SelectMTA { kMTA };
28 
29   // Constructor for STA initialization.
ScopedCOMInitializer()30   ScopedCOMInitializer() {
31     Initialize(COINIT_APARTMENTTHREADED);
32   }
33 
34   // Constructor for MTA initialization.
ScopedCOMInitializer(SelectMTA mta)35   explicit ScopedCOMInitializer(SelectMTA mta) {
36     Initialize(COINIT_MULTITHREADED);
37   }
38 
~ScopedCOMInitializer()39   ~ScopedCOMInitializer() {
40 #ifndef NDEBUG
41     // Using the windows API directly to avoid dependency on platform_thread.
42     DCHECK_EQ(GetCurrentThreadId(), thread_id_);
43 #endif
44     if (succeeded())
45       CoUninitialize();
46   }
47 
succeeded()48   bool succeeded() const { return SUCCEEDED(hr_); }
49 
50  private:
Initialize(COINIT init)51   void Initialize(COINIT init) {
52 #ifndef NDEBUG
53     thread_id_ = GetCurrentThreadId();
54 #endif
55     hr_ = CoInitializeEx(NULL, init);
56 #ifndef NDEBUG
57     if (hr_ == S_FALSE)
58       LOG(ERROR) << "Multiple CoInitialize() calls for thread " << thread_id_;
59     else
60       DCHECK_NE(RPC_E_CHANGED_MODE, hr_) << "Invalid COM thread model change";
61 #endif
62   }
63 
64   HRESULT hr_;
65 #ifndef NDEBUG
66   // In debug builds we use this variable to catch a potential bug where a
67   // ScopedCOMInitializer instance is deleted on a different thread than it
68   // was initially created on.  If that ever happens it can have bad
69   // consequences and the cause can be tricky to track down.
70   DWORD thread_id_;
71 #endif
72 
73   DISALLOW_COPY_AND_ASSIGN(ScopedCOMInitializer);
74 };
75 
76 }  // namespace win
77 }  // namespace base
78 
79 #endif  // BASE_WIN_SCOPED_COM_INITIALIZER_H_
80