1 // Copyright (c) 2013 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 // Implementation for the asynchronous interface to the Windows shell 6 // SHOpenWithDialog function. The call is made on a dedicated UI thread in a 7 // single-threaded apartment. 8 9 #include "win8/test/open_with_dialog_async.h" 10 11 #include <shlobj.h> 12 13 #include "base/bind.h" 14 #include "base/callback.h" 15 #include "base/location.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/single_thread_task_runner.h" 18 #include "base/thread_task_runner_handle.h" 19 #include "base/threading/platform_thread.h" 20 #include "base/threading/thread.h" 21 #include "base/win/windows_version.h" 22 23 namespace win8 { 24 25 namespace { 26 27 struct OpenWithContext { 28 OpenWithContext( 29 HWND parent_window_in, 30 const string16& file_name_in, 31 const string16& file_type_class_in, 32 int open_as_info_flags_in, 33 const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in, 34 const OpenWithDialogCallback& callback_in); 35 ~OpenWithContext(); 36 37 base::Thread thread; 38 HWND parent_window; 39 string16 file_name; 40 string16 file_type_class; 41 int open_as_info_flags; 42 scoped_refptr<base::SingleThreadTaskRunner> client_runner; 43 OpenWithDialogCallback callback; 44 }; 45 46 OpenWithContext::OpenWithContext( 47 HWND parent_window_in, 48 const string16& file_name_in, 49 const string16& file_type_class_in, 50 int open_as_info_flags_in, 51 const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in, 52 const OpenWithDialogCallback& callback_in) 53 : thread("OpenWithDialog"), 54 parent_window(parent_window_in), 55 file_name(file_name_in), 56 file_type_class(file_type_class_in), 57 open_as_info_flags(open_as_info_flags_in), 58 client_runner(client_runner_in), 59 callback(callback_in) { 60 thread.init_com_with_mta(false); 61 thread.Start(); 62 } 63 64 OpenWithContext::~OpenWithContext() {} 65 66 // Runs the caller-provided |callback| with the result of the call to 67 // SHOpenWithDialog on the caller's initial thread. 68 void OnOpenWithDialogDone(OpenWithContext* context, HRESULT result) { 69 DCHECK(context->client_runner->BelongsToCurrentThread()); 70 OpenWithDialogCallback callback(context->callback); 71 72 // Join with the thread. 73 delete context; 74 75 // Run the client's callback. 76 callback.Run(result); 77 } 78 79 // Calls SHOpenWithDialog (blocking), and returns the result back to the client 80 // thread. 81 void OpenWithDialogTask(OpenWithContext* context) { 82 DCHECK_EQ(context->thread.thread_id(), base::PlatformThread::CurrentId()); 83 OPENASINFO open_as_info = { 84 context->file_name.c_str(), 85 context->file_type_class.c_str(), 86 context->open_as_info_flags 87 }; 88 89 HRESULT result = ::SHOpenWithDialog(context->parent_window, &open_as_info); 90 91 // Bounce back to the calling thread to release resources and deliver the 92 // callback. 93 if (!context->client_runner->PostTask( 94 FROM_HERE, 95 base::Bind(&OnOpenWithDialogDone, context, result))) { 96 // The calling thread has gone away. There's nothing to be done but leak. 97 // In practice this is only likely to happen at shutdown, so there isn't 98 // much of a concern that it'll happen in the real world. 99 DLOG(ERROR) << "leaking OpenWith thread; result = " << std::hex << result; 100 } 101 } 102 103 } // namespace 104 105 void OpenWithDialogAsync( 106 HWND parent_window, 107 const string16& file_name, 108 const string16& file_type_class, 109 int open_as_info_flags, 110 const OpenWithDialogCallback& callback) { 111 DCHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA); 112 OpenWithContext* context = 113 new OpenWithContext(parent_window, file_name, file_type_class, 114 open_as_info_flags, 115 base::ThreadTaskRunnerHandle::Get(), callback); 116 context->thread.message_loop()->PostTask( 117 FROM_HERE, 118 base::Bind(&OpenWithDialogTask, context)); 119 } 120 121 } // namespace win8 122