summaryrefslogtreecommitdiffstats
path: root/win8/test/open_with_dialog_async.cc
blob: e2d616787b58dbba62209cdd8c5e47f10e40fca4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// 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