// 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.thread_id(), 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