1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/scoped_com_initializer.h"
6
7 #include <wrl/implements.h>
8
9 #include <ostream>
10
11 #include "base/check_op.h"
12 #include "base/win/resource_exhaustion.h"
13
14 namespace base {
15 namespace win {
16
ScopedCOMInitializer(Uninitialization uninitialization)17 ScopedCOMInitializer::ScopedCOMInitializer(Uninitialization uninitialization) {
18 Initialize(COINIT_APARTMENTTHREADED, uninitialization);
19 }
20
ScopedCOMInitializer(SelectMTA mta,Uninitialization uninitialization)21 ScopedCOMInitializer::ScopedCOMInitializer(SelectMTA mta,
22 Uninitialization uninitialization) {
23 Initialize(COINIT_MULTITHREADED, uninitialization);
24 }
25
~ScopedCOMInitializer()26 ScopedCOMInitializer::~ScopedCOMInitializer() {
27 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
28 if (Succeeded()) {
29 if (com_balancer_) {
30 com_balancer_->Disable();
31 com_balancer_.Reset();
32 }
33 CoUninitialize();
34 }
35 }
36
Succeeded() const37 bool ScopedCOMInitializer::Succeeded() const {
38 return SUCCEEDED(hr_);
39 }
40
GetCOMBalancerReferenceCountForTesting() const41 DWORD ScopedCOMInitializer::GetCOMBalancerReferenceCountForTesting() const {
42 if (com_balancer_)
43 return com_balancer_->GetReferenceCountForTesting();
44 return 0;
45 }
46
Initialize(COINIT init,Uninitialization uninitialization)47 void ScopedCOMInitializer::Initialize(COINIT init,
48 Uninitialization uninitialization) {
49 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
50 // COINIT_DISABLE_OLE1DDE is always added based on:
51 // https://docs.microsoft.com/en-us/windows/desktop/learnwin32/initializing-the-com-library
52 if (uninitialization == Uninitialization::kBlockPremature) {
53 com_balancer_ = Microsoft::WRL::Details::Make<internal::ComInitBalancer>(
54 init | COINIT_DISABLE_OLE1DDE);
55 }
56
57 hr_ = ::CoInitializeEx(nullptr, init | COINIT_DISABLE_OLE1DDE);
58 DCHECK_NE(RPC_E_CHANGED_MODE, hr_) << "Invalid COM thread model change";
59
60 // CoInitializeEx may call RegisterClassEx to get an ATOM. On failure, the
61 // call to RegisterClassEx sets the last error code to
62 // ERROR_NOT_ENOUGH_MEMORY. CoInitializeEx is retuning the converted error
63 // code (a.k.a HRESULT_FROM_WIN32(...)). The following code handles the case
64 // where HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY) is being returned by
65 // CoInitializeEx. We assume they are due to ATOM exhaustion. This appears to
66 // happen most often when the browser is being driven by automation tools,
67 // though the underlying reason for this remains a mystery
68 // (https://crbug.com/1470483). There is nothing that Chrome can do to
69 // meaningfully run until the user restarts their session by signing out of
70 // Windows or restarting their computer.
71 if (init == COINIT_APARTMENTTHREADED &&
72 hr_ == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)) {
73 base::win::OnResourceExhausted();
74 }
75 }
76
77 } // namespace win
78 } // namespace base
79