sbAutoCOMInitializer.h
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4  *=BEGIN SONGBIRD GPL
5  *
6  * This file is part of the Songbird web player.
7  *
8  * Copyright(c) 2005-2011 POTI, Inc.
9  * http://www.songbirdnest.com
10  *
11  * This file may be licensed under the terms of of the
12  * GNU General Public License Version 2 (the ``GPL'').
13  *
14  * Software distributed under the License is distributed
15  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
16  * express or implied. See the GPL for the specific language
17  * governing rights and limitations.
18  *
19  * You should have received a copy of the GPL along with this
20  * program. If not, go to http://www.gnu.org/licenses/gpl.html
21  * or write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  *=END SONGBIRD GPL
25  */
26 
27 #ifndef SBAUTOCOMINITIALIZER_H_
28 #define SBAUTOCOMINITIALIZER_H_
29 
30 // Platform includes
31 #include <assert.h>
32 #include <Objbase.h>
33 #include <windows.h>
34 
43 {
44 public:
48  sbAutoCOMInitializer(DWORD aInitFlags) : mInitialized(0), mFailureResult(S_OK)
49  {
50  Initialize(aInitFlags);
51  }
52 
58  mFailureResult(S_OK),
59  mInitThreadId(0)
60  {
61  }
62 
67  {
68  // Only uninitialized if we have been initialized. Some users will
69  // manually manage COM initialization and uninitialization
70  if (Initialized()) {
71  Uninitialize();
72  }
73  // While we might have never been uninitialized we should never leave
74  // an Initialize call outstanding.
75  assert(mInitialized == 0);
76  }
77 
84  HRESULT Initialize(DWORD aInitFlags = COINIT_MULTITHREADED)
85  {
86  HRESULT result = S_OK;
87  if (++mInitialized == 1) {
88  // Save off the thread ID we're initialize COM so we can test on
89  // tear down
90  mInitThreadId = ::GetCurrentThreadId();
91  result = ::CoInitializeEx(NULL, aInitFlags);
92  // If we fail for any reason then we don't want to call CoUninitialize
93  if (FAILED(result)) {
94  --mInitialized;
95  }
96  // If we get a change mode error, then COM was previously initialized on
97  // this thread using a different threading model. Changing the threading
98  // model could have negative impact on the code that actually set it.
99  // We don't need to actually error in this case since the thread is
100  // already COM initialized by something else.
101  // This happens with XULRunner initializes COM with the apartment model
102  // on the main thread. And then if some code calls this it gets that
103  // error.
104  if (FAILED(result) && result != RPC_E_CHANGED_MODE) {
105  mFailureResult = result;
106  }
107  else {
108  mFailureResult = S_OK;
109  result = S_OK;
110  }
111  }
112  return result;
113  }
114 
120  {
121  // There are cases where if an error occurs and Uninitialize can get called
122  // manually when no Initialize call has been made. It's not ideal but
123  // we need to handle it.
124  if (Initialized()) {
125  if (--mInitialized == 0) {
126  // Make sure we're tearing down COM on the thread we initialized it on
127  // There could be some edge cases in an error path where Uninitialize
128  // gets called from the destructor but not on the thread it was
129  // initialized on. It's better to leak in this case than to tear down
130  // COM on the wrong thread
131  if (mInitThreadId == ::GetCurrentThreadId()) {
132  ::CoUninitialize();
133  }
134  }
135  }
136  }
137 
142  HRESULT GetHRESULT() const
143  {
144  return mFailureResult;
145  }
146 
151  bool Succeeded() const
152  {
153  return SUCCEEDED(mFailureResult);
154  }
155 
156  bool Initialized() const
157  {
158  return mInitialized > 0;
159  }
160 public:
166 
170  HRESULT mFailureResult;
171 
177 };
178 
179 #endif
HRESULT Initialize(DWORD aInitFlags=COINIT_MULTITHREADED)
sbAutoCOMInitializer(DWORD aInitFlags)
HRESULT GetHRESULT() const