file_chooser_main_win.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Copyright 2019 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. #include <shlobj.h>
  5. #include <windows.h>
  6. #include <wrl/client.h>
  7. #include <cstdio>
  8. #include <cstdlib>
  9. #include "base/bind.h"
  10. #include "base/files/file_path.h"
  11. #include "base/logging.h"
  12. #include "base/memory/scoped_refptr.h"
  13. #include "base/run_loop.h"
  14. #include "base/task/thread_pool/thread_pool_instance.h"
  15. #include "base/timer/timer.h"
  16. #include "base/win/scoped_co_mem.h"
  17. #include "base/win/scoped_com_initializer.h"
  18. #include "mojo/public/cpp/bindings/message.h"
  19. #include "remoting/host/file_transfer/file_chooser.h"
  20. #include "remoting/host/file_transfer/file_chooser_common_win.h"
  21. #include "remoting/host/mojom/desktop_session.mojom.h"
  22. #include "remoting/host/win/core_resource.h"
  23. #include "remoting/protocol/file_transfer_helpers.h"
  24. namespace remoting {
  25. namespace {
  26. // Converts an HRESULT to a FileTransferResult.
  27. protocol::FileTransfer_Error LogFailedHrAndMakeError(base::Location from_here,
  28. const char* operation,
  29. HRESULT hr) {
  30. DCHECK(FAILED(hr));
  31. bool canceled = (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED));
  32. if (!canceled) {
  33. LOG(ERROR) << "Error displaying file dialog (" << operation << "): " << hr;
  34. }
  35. return protocol::MakeFileTransferError(
  36. from_here,
  37. canceled ? protocol::FileTransfer_Error_Type_CANCELED
  38. : protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR,
  39. hr);
  40. }
  41. // Loads an embedded string resource from the specified module.
  42. protocol::FileTransferResult<std::wstring> LoadStringResource(int resource_id) {
  43. // GetModuleHandle doesn't increment the ref count, so the handle doesn't need
  44. // to be freed.
  45. HMODULE resource_module = GetModuleHandle(L"remoting_core.dll");
  46. if (resource_module == nullptr) {
  47. PLOG(ERROR) << "GetModuleHandle() failed";
  48. return protocol::MakeFileTransferError(
  49. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR,
  50. GetLastError());
  51. }
  52. const wchar_t* string_resource = nullptr;
  53. // Specifying 0 for the last parameter (buffer size) signifies that we want a
  54. // read-only pointer to the resource instead of copying the string into a
  55. // buffer (which we do ourselves).
  56. int string_length =
  57. LoadString(resource_module, resource_id,
  58. reinterpret_cast<wchar_t*>(&string_resource), 0);
  59. if (string_length <= 0) {
  60. PLOG(ERROR) << "LoadString() failed";
  61. return protocol::MakeFileTransferError(
  62. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR,
  63. GetLastError());
  64. }
  65. return std::wstring(string_resource, string_length);
  66. }
  67. FileChooser::Result ShowFileChooser() {
  68. HRESULT hr;
  69. Microsoft::WRL::ComPtr<IFileOpenDialog> file_open_dialog;
  70. hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER,
  71. IID_PPV_ARGS(&file_open_dialog));
  72. if (FAILED(hr)) {
  73. return LogFailedHrAndMakeError(FROM_HERE, "create", hr);
  74. }
  75. protocol::FileTransferResult<std::wstring> title =
  76. LoadStringResource(IDS_DOWNLOAD_FILE_DIALOG_TITLE);
  77. if (!title) {
  78. return title.error();
  79. }
  80. hr = file_open_dialog->SetTitle(title->c_str());
  81. if (FAILED(hr)) {
  82. return LogFailedHrAndMakeError(FROM_HERE, "set title", hr);
  83. }
  84. FILEOPENDIALOGOPTIONS options;
  85. hr = file_open_dialog->GetOptions(&options);
  86. if (FAILED(hr)) {
  87. return LogFailedHrAndMakeError(FROM_HERE, "get options", hr);
  88. }
  89. options |= FOS_FORCEFILESYSTEM;
  90. hr = file_open_dialog->SetOptions(options);
  91. if (FAILED(hr)) {
  92. return LogFailedHrAndMakeError(FROM_HERE, "set options", hr);
  93. }
  94. hr = file_open_dialog->Show(nullptr);
  95. if (FAILED(hr)) {
  96. return LogFailedHrAndMakeError(FROM_HERE, "show", hr);
  97. }
  98. Microsoft::WRL::ComPtr<IShellItem> shell_item;
  99. hr = file_open_dialog->GetResult(&shell_item);
  100. if (FAILED(hr)) {
  101. return LogFailedHrAndMakeError(FROM_HERE, "get result", hr);
  102. }
  103. base::win::ScopedCoMem<wchar_t> path;
  104. hr = shell_item->GetDisplayName(SIGDN_FILESYSPATH, &path);
  105. if (FAILED(hr)) {
  106. return LogFailedHrAndMakeError(FROM_HERE, "get path", hr);
  107. }
  108. return base::FilePath(path.get());
  109. }
  110. } // namespace
  111. int FileChooserMain() {
  112. base::ThreadPoolInstance::CreateAndStartWithDefaultParams("FileChooser");
  113. base::win::ScopedCOMInitializer com;
  114. FileChooser::Result result = ShowFileChooser();
  115. mojo::Message serialized_message =
  116. mojom::FileChooserResult::SerializeAsMessage(&result);
  117. // Highly unlikely, but we want to know if it happens.
  118. if (serialized_message.data_num_bytes() > kFileChooserPipeBufferSize) {
  119. FileChooser::Result error_result(protocol::MakeFileTransferError(
  120. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR));
  121. serialized_message =
  122. mojom::FileChooserResult::SerializeAsMessage(&error_result);
  123. }
  124. HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
  125. if (stdout_handle == INVALID_HANDLE_VALUE) {
  126. PLOG(ERROR) << "Could not get stdout handle";
  127. return EXIT_FAILURE;
  128. }
  129. DWORD bytes_written;
  130. if (!WriteFile(stdout_handle, serialized_message.data(),
  131. serialized_message.data_num_bytes(), &bytes_written,
  132. nullptr)) {
  133. PLOG(ERROR) << "Failed to write file chooser result";
  134. }
  135. // While the pipe buffer is expected to be at least
  136. // kFileChooserPipeBufferSize, the parent process sets it to non-blocking just
  137. // in case. Check that all bytes were written successfully, and return an
  138. // error code if not to signal the parent that it shouldn't try to parse the
  139. // output.
  140. if (bytes_written != serialized_message.data_num_bytes()) {
  141. LOG(ERROR) << "Failed to write all bytes to pipe. (Buffer full?) Expected: "
  142. << serialized_message.data_num_bytes()
  143. << " Actual: " << bytes_written;
  144. return EXIT_FAILURE;
  145. }
  146. return EXIT_SUCCESS;
  147. }
  148. } // namespace remoting