• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
OpenWithContext(HWND parent_window_in,const string16 & file_name_in,const string16 & file_type_class_in,int open_as_info_flags_in,const scoped_refptr<base::SingleThreadTaskRunner> & client_runner_in,const OpenWithDialogCallback & callback_in)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 
~OpenWithContext()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.
OnOpenWithDialogDone(OpenWithContext * context,HRESULT result)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.
OpenWithDialogTask(OpenWithContext * context)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 
OpenWithDialogAsync(HWND parent_window,const string16 & file_name,const string16 & file_type_class,int open_as_info_flags,const OpenWithDialogCallback & callback)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