| // Copyright (c) 2013 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. |
| |
| // Implementation for the asynchronous interface to the Windows shell |
| // SHOpenWithDialog function. The call is made on a dedicated UI thread in a |
| // single-threaded apartment. |
| |
| #include "win8/test/open_with_dialog_async.h" |
| |
| #include <shlobj.h> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/location.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/thread.h" |
| #include "base/win/windows_version.h" |
| |
| namespace win8 { |
| |
| namespace { |
| |
| struct OpenWithContext { |
| OpenWithContext( |
| HWND parent_window_in, |
| const base::string16& file_name_in, |
| const base::string16& file_type_class_in, |
| int open_as_info_flags_in, |
| const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in, |
| const OpenWithDialogCallback& callback_in); |
| ~OpenWithContext(); |
| |
| base::Thread thread; |
| HWND parent_window; |
| base::string16 file_name; |
| base::string16 file_type_class; |
| int open_as_info_flags; |
| scoped_refptr<base::SingleThreadTaskRunner> client_runner; |
| OpenWithDialogCallback callback; |
| }; |
| |
| OpenWithContext::OpenWithContext( |
| HWND parent_window_in, |
| const base::string16& file_name_in, |
| const base::string16& file_type_class_in, |
| int open_as_info_flags_in, |
| const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in, |
| const OpenWithDialogCallback& callback_in) |
| : thread("OpenWithDialog"), |
| parent_window(parent_window_in), |
| file_name(file_name_in), |
| file_type_class(file_type_class_in), |
| open_as_info_flags(open_as_info_flags_in), |
| client_runner(client_runner_in), |
| callback(callback_in) { |
| thread.init_com_with_mta(false); |
| thread.Start(); |
| } |
| |
| OpenWithContext::~OpenWithContext() {} |
| |
| // Runs the caller-provided |callback| with the result of the call to |
| // SHOpenWithDialog on the caller's initial thread. |
| void OnOpenWithDialogDone(OpenWithContext* context, HRESULT result) { |
| DCHECK(context->client_runner->BelongsToCurrentThread()); |
| OpenWithDialogCallback callback(context->callback); |
| |
| // Join with the thread. |
| delete context; |
| |
| // Run the client's callback. |
| callback.Run(result); |
| } |
| |
| // Calls SHOpenWithDialog (blocking), and returns the result back to the client |
| // thread. |
| void OpenWithDialogTask(OpenWithContext* context) { |
| DCHECK_EQ(context->thread.GetThreadId(), base::PlatformThread::CurrentId()); |
| OPENASINFO open_as_info = { |
| context->file_name.c_str(), |
| context->file_type_class.c_str(), |
| context->open_as_info_flags |
| }; |
| |
| HRESULT result = ::SHOpenWithDialog(context->parent_window, &open_as_info); |
| |
| // Bounce back to the calling thread to release resources and deliver the |
| // callback. |
| if (!context->client_runner->PostTask( |
| FROM_HERE, |
| base::Bind(&OnOpenWithDialogDone, context, result))) { |
| // The calling thread has gone away. There's nothing to be done but leak. |
| // In practice this is only likely to happen at shutdown, so there isn't |
| // much of a concern that it'll happen in the real world. |
| DLOG(ERROR) << "leaking OpenWith thread; result = " << std::hex << result; |
| } |
| } |
| |
| } // namespace |
| |
| void OpenWithDialogAsync( |
| HWND parent_window, |
| const base::string16& file_name, |
| const base::string16& file_type_class, |
| int open_as_info_flags, |
| const OpenWithDialogCallback& callback) { |
| DCHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA); |
| OpenWithContext* context = |
| new OpenWithContext(parent_window, file_name, file_type_class, |
| open_as_info_flags, |
| base::ThreadTaskRunnerHandle::Get(), callback); |
| context->thread.message_loop()->PostTask( |
| FROM_HERE, |
| base::Bind(&OpenWithDialogTask, context)); |
| } |
| |
| } // namespace win8 |