OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/midi/midi_manager_winrt.h" | 5 #include "media/midi/midi_manager_winrt.h" |
6 | 6 |
7 #include <comdef.h> | 7 #include <comdef.h> |
8 #include <robuffer.h> | 8 #include <robuffer.h> |
9 #include <windows.devices.enumeration.h> | 9 #include <windows.devices.enumeration.h> |
10 #include <windows.devices.midi.h> | 10 #include <windows.devices.midi.h> |
11 #include <wrl/event.h> | 11 #include <wrl/event.h> |
12 | 12 |
13 #include <iomanip> | 13 #include <iomanip> |
14 #include <unordered_map> | 14 #include <unordered_map> |
15 #include <unordered_set> | 15 #include <unordered_set> |
16 | 16 |
17 #include "base/bind.h" | 17 #include "base/bind.h" |
18 #include "base/lazy_instance.h" | |
19 #include "base/scoped_generic.h" | |
18 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
19 #include "base/threading/thread_checker.h" | 21 #include "base/threading/thread_checker.h" |
20 #include "base/threading/thread_task_runner_handle.h" | 22 #include "base/threading/thread_task_runner_handle.h" |
21 #include "base/timer/timer.h" | 23 #include "base/timer/timer.h" |
22 #include "base/win/scoped_comptr.h" | 24 #include "base/win/scoped_comptr.h" |
23 #include "base/win/windows_version.h" | 25 #include "base/win/windows_version.h" |
24 #include "media/midi/midi_scheduler.h" | 26 #include "media/midi/midi_scheduler.h" |
25 | 27 |
26 namespace media { | 28 namespace media { |
27 namespace midi { | 29 namespace midi { |
(...skipping 15 matching lines...) Expand all Loading... | |
43 }; | 45 }; |
44 | 46 |
45 std::ostream& operator<<(std::ostream& os, const PrintHr& phr) { | 47 std::ostream& operator<<(std::ostream& os, const PrintHr& phr) { |
46 std::ios_base::fmtflags ff = os.flags(); | 48 std::ios_base::fmtflags ff = os.flags(); |
47 os << _com_error(phr.hr).ErrorMessage() << " (0x" << std::hex | 49 os << _com_error(phr.hr).ErrorMessage() << " (0x" << std::hex |
48 << std::uppercase << std::setfill('0') << std::setw(8) << phr.hr << ")"; | 50 << std::uppercase << std::setfill('0') << std::setw(8) << phr.hr << ")"; |
49 os.flags(ff); | 51 os.flags(ff); |
50 return os; | 52 return os; |
51 } | 53 } |
52 | 54 |
55 // Provides access to functions in combase.dll which may not be available on | |
56 // Windows 7. Loads functions dynamically at runtime to prevent library | |
57 // dependencies. Use this class through the global LazyInstance | |
58 // |g_combase_functions|. | |
59 class CombaseFunctions { | |
60 public: | |
61 CombaseFunctions() = default; | |
62 | |
63 ~CombaseFunctions() { | |
64 if (combase_dll_) | |
65 ::FreeLibrary(combase_dll_); | |
66 } | |
67 | |
68 bool LoadFunctions() { | |
69 combase_dll_ = ::LoadLibrary(L"combase.dll"); | |
70 if (!combase_dll_) | |
71 return false; | |
72 | |
73 get_factory_func_ = reinterpret_cast<decltype(&::RoGetActivationFactory)>( | |
74 ::GetProcAddress(combase_dll_, "RoGetActivationFactory")); | |
75 if (!get_factory_func_) | |
76 return false; | |
77 | |
78 create_string_func_ = reinterpret_cast<decltype(&::WindowsCreateString)>( | |
79 ::GetProcAddress(combase_dll_, "WindowsCreateString")); | |
80 if (!create_string_func_) | |
81 return false; | |
82 | |
83 delete_string_func_ = reinterpret_cast<decltype(&::WindowsDeleteString)>( | |
84 ::GetProcAddress(combase_dll_, "WindowsDeleteString")); | |
85 if (!delete_string_func_) | |
86 return false; | |
87 | |
88 get_string_raw_buffer_func_ = | |
89 reinterpret_cast<decltype(&::WindowsGetStringRawBuffer)>( | |
90 ::GetProcAddress(combase_dll_, "WindowsGetStringRawBuffer")); | |
91 if (!get_string_raw_buffer_func_) | |
92 return false; | |
93 | |
94 return true; | |
95 } | |
96 | |
97 HRESULT RoGetActivationFactory(HSTRING class_id, | |
98 const IID& iid, | |
99 void** out_factory) { | |
100 DCHECK(get_factory_func_); | |
101 return get_factory_func_(class_id, iid, out_factory); | |
102 } | |
103 | |
104 HRESULT WindowsCreateString(const base::char16* src, | |
105 uint32_t len, | |
106 HSTRING* out_hstr) { | |
107 DCHECK(create_string_func_); | |
108 return create_string_func_(src, len, out_hstr); | |
109 } | |
110 | |
111 HRESULT WindowsDeleteString(HSTRING hstr) { | |
112 DCHECK(delete_string_func_); | |
113 return delete_string_func_(hstr); | |
114 } | |
115 | |
116 const base::char16* WindowsGetStringRawBuffer(HSTRING hstr, | |
117 uint32_t* out_len) { | |
118 DCHECK(get_string_raw_buffer_func_); | |
119 return get_string_raw_buffer_func_(hstr, out_len); | |
120 } | |
121 | |
122 private: | |
123 HMODULE combase_dll_ = nullptr; | |
124 | |
125 decltype(&::RoGetActivationFactory) get_factory_func_ = nullptr; | |
126 decltype(&::WindowsCreateString) create_string_func_ = nullptr; | |
127 decltype(&::WindowsDeleteString) delete_string_func_ = nullptr; | |
128 decltype(&::WindowsGetStringRawBuffer) get_string_raw_buffer_func_ = nullptr; | |
129 }; | |
130 | |
131 base::LazyInstance<CombaseFunctions> g_combase_functions = | |
132 LAZY_INSTANCE_INITIALIZER; | |
133 | |
134 // Scoped HSTRING class to maintain lifetime of HSTRINGs allocated with | |
135 // WindowsCreateString(). | |
136 class ScopedHStringTraits { | |
137 public: | |
138 static HSTRING InvalidValue() { return nullptr; } | |
Takashi Toyoshima
2016/09/01 05:57:04
I'm not sure, but could this be INVALID_HANDLE_VAL
Shao-Chuan Lee
2016/09/01 06:11:35
MSDN: "string [out]: A pointer to the newly create
Takashi Toyoshima
2016/09/01 07:07:25
Generally speaking, H* types of Windows use INVALI
| |
139 | |
140 static void Free(HSTRING hstr) { | |
141 g_combase_functions.Get().WindowsDeleteString(hstr); | |
142 } | |
143 }; | |
144 | |
145 class ScopedHString : public base::ScopedGeneric<HSTRING, ScopedHStringTraits> { | |
146 public: | |
147 explicit ScopedHString(const base::char16* str) : ScopedGeneric(nullptr) { | |
148 HSTRING hstr; | |
149 HRESULT hr = g_combase_functions.Get().WindowsCreateString( | |
150 str, static_cast<uint32_t>(wcslen(str)), &hstr); | |
151 if (FAILED(hr)) | |
152 VLOG(1) << "WindowsCreateString failed: " << PrintHr(hr); | |
153 else | |
154 reset(hstr); | |
155 } | |
156 }; | |
157 | |
53 // Factory functions that activate and create WinRT components. The caller takes | 158 // Factory functions that activate and create WinRT components. The caller takes |
54 // ownership of the returning ComPtr. | 159 // ownership of the returning ComPtr. |
55 template <typename InterfaceType, base::char16 const* runtime_class_id> | 160 template <typename InterfaceType, base::char16 const* runtime_class_id> |
56 ScopedComPtr<InterfaceType> WrlStaticsFactory() { | 161 ScopedComPtr<InterfaceType> WrlStaticsFactory() { |
57 ScopedComPtr<InterfaceType> com_ptr; | 162 ScopedComPtr<InterfaceType> com_ptr; |
58 | 163 |
59 HRESULT hr = GetActivationFactory( | 164 ScopedHString class_id_hstring(runtime_class_id); |
60 WRL::Wrappers::HStringReference(runtime_class_id).Get(), | 165 if (!class_id_hstring.is_valid()) { |
61 com_ptr.Receive()); | 166 com_ptr = nullptr; |
167 return com_ptr; | |
168 } | |
169 | |
170 HRESULT hr = g_combase_functions.Get().RoGetActivationFactory( | |
171 class_id_hstring.get(), __uuidof(InterfaceType), com_ptr.ReceiveVoid()); | |
62 if (FAILED(hr)) { | 172 if (FAILED(hr)) { |
63 VLOG(1) << "GetActivationFactory failed: " << PrintHr(hr); | 173 VLOG(1) << "RoGetActivationFactory failed: " << PrintHr(hr); |
64 com_ptr = nullptr; | 174 com_ptr = nullptr; |
65 } | 175 } |
66 | 176 |
67 return com_ptr; | 177 return com_ptr; |
68 } | 178 } |
69 | 179 |
70 template <typename T, HRESULT (T::*method)(HSTRING*)> | 180 template <typename T, HRESULT (T::*method)(HSTRING*)> |
71 std::string GetStringFromObjectMethod(T* obj) { | 181 std::string GetStringFromObjectMethod(T* obj) { |
72 HSTRING result; | 182 HSTRING result; |
73 HRESULT hr = (obj->*method)(&result); | 183 HRESULT hr = (obj->*method)(&result); |
74 if (FAILED(hr)) { | 184 if (FAILED(hr)) { |
75 VLOG(1) << "GetStringFromObjectMethod failed: " << PrintHr(hr); | 185 VLOG(1) << "GetStringFromObjectMethod failed: " << PrintHr(hr); |
76 return std::string(); | 186 return std::string(); |
77 } | 187 } |
78 | 188 |
79 // Note: empty HSTRINGs are represent as nullptr, and instantiating | 189 // Note: empty HSTRINGs are represent as nullptr, and instantiating |
80 // std::string with nullptr (in base::WideToUTF8) is undefined behavior. | 190 // std::string with nullptr (in base::WideToUTF8) is undefined behavior. |
81 const base::char16* buffer = WindowsGetStringRawBuffer(result, nullptr); | 191 const base::char16* buffer = |
192 g_combase_functions.Get().WindowsGetStringRawBuffer(result, nullptr); | |
82 if (buffer) | 193 if (buffer) |
83 return base::WideToUTF8(buffer); | 194 return base::WideToUTF8(buffer); |
84 return std::string(); | 195 return std::string(); |
85 } | 196 } |
86 | 197 |
87 template <typename T> | 198 template <typename T> |
88 std::string GetIdString(T* obj) { | 199 std::string GetIdString(T* obj) { |
89 return GetStringFromObjectMethod<T, &T::get_Id>(obj); | 200 return GetStringFromObjectMethod<T, &T::get_Id>(obj); |
90 } | 201 } |
91 | 202 |
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
368 base::ThreadChecker thread_checker_; | 479 base::ThreadChecker thread_checker_; |
369 | 480 |
370 private: | 481 private: |
371 // DeviceWatcher callbacks: | 482 // DeviceWatcher callbacks: |
372 void OnAdded(std::string dev_id, std::string dev_name) { | 483 void OnAdded(std::string dev_id, std::string dev_name) { |
373 DCHECK(thread_checker_.CalledOnValidThread()); | 484 DCHECK(thread_checker_.CalledOnValidThread()); |
374 CHECK(is_initialized_); | 485 CHECK(is_initialized_); |
375 | 486 |
376 port_names_[dev_id] = dev_name; | 487 port_names_[dev_id] = dev_name; |
377 | 488 |
378 WRL::Wrappers::HString dev_id_hstring; | 489 ScopedHString dev_id_hstring(base::UTF8ToWide(dev_id).c_str()); |
379 HRESULT hr = dev_id_hstring.Set(base::UTF8ToWide(dev_id).c_str()); | 490 if (!dev_id_hstring.is_valid()) |
380 if (FAILED(hr)) { | |
381 VLOG(1) << "Set failed: " << PrintHr(hr); | |
382 return; | 491 return; |
383 } | |
384 | 492 |
385 IAsyncOperation<RuntimeType*>* async_op; | 493 IAsyncOperation<RuntimeType*>* async_op; |
386 | 494 |
387 hr = midi_port_statics_->FromIdAsync(dev_id_hstring.Get(), &async_op); | 495 HRESULT hr = |
496 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op); | |
388 if (FAILED(hr)) { | 497 if (FAILED(hr)) { |
389 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr); | 498 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr); |
390 return; | 499 return; |
391 } | 500 } |
392 | 501 |
393 base::WeakPtr<MidiPortManager> weak_ptr = GetWeakPtrFromFactory(); | 502 base::WeakPtr<MidiPortManager> weak_ptr = GetWeakPtrFromFactory(); |
394 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_; | 503 scoped_refptr<base::SingleThreadTaskRunner> task_runner = task_runner_; |
395 | 504 |
396 hr = async_op->put_Completed( | 505 hr = async_op->put_Completed( |
397 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>( | 506 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>( |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
700 CHECK(!scheduler_); | 809 CHECK(!scheduler_); |
701 } | 810 } |
702 | 811 |
703 void MidiManagerWinrt::StartInitialization() { | 812 void MidiManagerWinrt::StartInitialization() { |
704 if (base::win::GetVersion() < base::win::VERSION_WIN10) { | 813 if (base::win::GetVersion() < base::win::VERSION_WIN10) { |
705 VLOG(1) << "WinRT MIDI backend is only supported on Windows 10 or later."; | 814 VLOG(1) << "WinRT MIDI backend is only supported on Windows 10 or later."; |
706 CompleteInitialization(Result::INITIALIZATION_ERROR); | 815 CompleteInitialization(Result::INITIALIZATION_ERROR); |
707 return; | 816 return; |
708 } | 817 } |
709 | 818 |
819 if (!g_combase_functions.Get().LoadFunctions()) { | |
Takashi Toyoshima
2016/09/01 05:57:04
We may want to have this check on the com thread t
Shao-Chuan Lee
2016/09/01 06:11:35
Done.
| |
820 VLOG(1) << "Failed loading functions from combase.dll: " | |
821 << PrintHr(HRESULT_FROM_WIN32(GetLastError())); | |
822 CompleteInitialization(Result::INITIALIZATION_ERROR); | |
823 return; | |
824 } | |
825 | |
710 com_thread_.init_com_with_mta(true); | 826 com_thread_.init_com_with_mta(true); |
711 com_thread_.Start(); | 827 com_thread_.Start(); |
712 | 828 |
713 com_thread_.task_runner()->PostTask( | 829 com_thread_.task_runner()->PostTask( |
714 FROM_HERE, base::Bind(&MidiManagerWinrt::InitializeOnComThread, | 830 FROM_HERE, base::Bind(&MidiManagerWinrt::InitializeOnComThread, |
715 base::Unretained(this))); | 831 base::Unretained(this))); |
716 } | 832 } |
717 | 833 |
718 void MidiManagerWinrt::Finalize() { | 834 void MidiManagerWinrt::Finalize() { |
719 com_thread_.task_runner()->PostTask( | 835 com_thread_.task_runner()->PostTask( |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
821 if (++port_manager_ready_count_ == 2) | 937 if (++port_manager_ready_count_ == 2) |
822 CompleteInitialization(Result::OK); | 938 CompleteInitialization(Result::OK); |
823 } | 939 } |
824 | 940 |
825 MidiManager* MidiManager::Create() { | 941 MidiManager* MidiManager::Create() { |
826 return new MidiManagerWinrt(); | 942 return new MidiManagerWinrt(); |
827 } | 943 } |
828 | 944 |
829 } // namespace midi | 945 } // namespace midi |
830 } // namespace media | 946 } // namespace media |
OLD | NEW |