OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 #include "stdafx.h" | |
6 #include "win8/metro_driver/file_picker.h" | |
7 | |
8 #include <stddef.h> | |
9 #include <stdint.h> | |
10 #include <windows.storage.pickers.h> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/files/file_path.h" | |
14 #include "base/logging.h" | |
15 #include "base/macros.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/synchronization/waitable_event.h" | |
19 #include "base/win/metro.h" | |
20 #include "base/win/scoped_comptr.h" | |
21 #include "win8/metro_driver/chrome_app_view.h" | |
22 #include "win8/metro_driver/winrt_utils.h" | |
23 | |
24 namespace { | |
25 | |
26 namespace winstorage = ABI::Windows::Storage; | |
27 typedef winfoundtn::Collections::IVector<HSTRING> StringVectorItf; | |
28 | |
29 // TODO(siggi): Complete this implementation and move it to a common place. | |
30 class StringVectorImpl : public mswr::RuntimeClass<StringVectorItf> { | |
31 public: | |
32 ~StringVectorImpl() { | |
33 std::for_each(strings_.begin(), strings_.end(), ::WindowsDeleteString); | |
34 } | |
35 | |
36 HRESULT RuntimeClassInitialize(const std::vector<base::string16>& list) { | |
37 for (size_t i = 0; i < list.size(); ++i) | |
38 strings_.push_back(MakeHString(list[i])); | |
39 | |
40 return S_OK; | |
41 } | |
42 | |
43 // IVector<HSTRING> implementation. | |
44 STDMETHOD(GetAt)(unsigned index, HSTRING* item) { | |
45 if (index >= strings_.size()) | |
46 return E_INVALIDARG; | |
47 | |
48 return ::WindowsDuplicateString(strings_[index], item); | |
49 } | |
50 STDMETHOD(get_Size)(unsigned *size) { | |
51 if (strings_.size() > UINT_MAX) | |
52 return E_UNEXPECTED; | |
53 *size = static_cast<unsigned>(strings_.size()); | |
54 return S_OK; | |
55 } | |
56 STDMETHOD(GetView)(winfoundtn::Collections::IVectorView<HSTRING> **view) { | |
57 return E_NOTIMPL; | |
58 } | |
59 STDMETHOD(IndexOf)(HSTRING value, unsigned *index, boolean *found) { | |
60 return E_NOTIMPL; | |
61 } | |
62 | |
63 // write methods | |
64 STDMETHOD(SetAt)(unsigned index, HSTRING item) { | |
65 return E_NOTIMPL; | |
66 } | |
67 STDMETHOD(InsertAt)(unsigned index, HSTRING item) { | |
68 return E_NOTIMPL; | |
69 } | |
70 STDMETHOD(RemoveAt)(unsigned index) { | |
71 return E_NOTIMPL; | |
72 } | |
73 STDMETHOD(Append)(HSTRING item) { | |
74 return E_NOTIMPL; | |
75 } | |
76 STDMETHOD(RemoveAtEnd)() { | |
77 return E_NOTIMPL; | |
78 } | |
79 STDMETHOD(Clear)() { | |
80 return E_NOTIMPL; | |
81 } | |
82 | |
83 private: | |
84 std::vector<HSTRING> strings_; | |
85 }; | |
86 | |
87 class FilePickerSessionBase { | |
88 public: | |
89 // Creates a file picker for open_file_name. | |
90 explicit FilePickerSessionBase(OPENFILENAME* open_file_name); | |
91 | |
92 // Runs the picker, returns true on success. | |
93 bool Run(); | |
94 | |
95 protected: | |
96 // Creates, configures and starts a file picker. | |
97 // If the HRESULT returned is a failure code the file picker has not started, | |
98 // so no callbacks should be expected. | |
99 virtual HRESULT StartFilePicker() = 0; | |
100 | |
101 // The parameters to our picker. | |
102 OPENFILENAME* open_file_name_; | |
103 // The event Run waits on. | |
104 base::WaitableEvent event_; | |
105 // True iff a file picker has successfully finished. | |
106 bool success_; | |
107 | |
108 private: | |
109 // Initiate a file picker, must be called on the metro dispatcher's thread. | |
110 void DoFilePicker(); | |
111 | |
112 DISALLOW_COPY_AND_ASSIGN(FilePickerSessionBase); | |
113 }; | |
114 | |
115 class OpenFilePickerSession : public FilePickerSessionBase { | |
116 public: | |
117 explicit OpenFilePickerSession(OPENFILENAME* open_file_name); | |
118 | |
119 private: | |
120 HRESULT StartFilePicker() override; | |
121 | |
122 typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*> | |
123 SingleFileAsyncOp; | |
124 typedef winfoundtn::Collections::IVectorView< | |
125 winstorage::StorageFile*> StorageFileVectorCollection; | |
126 typedef winfoundtn::IAsyncOperation<StorageFileVectorCollection*> | |
127 MultiFileAsyncOp; | |
128 | |
129 // Called asynchronously when a single file picker is done. | |
130 HRESULT SinglePickerDone(SingleFileAsyncOp* async, AsyncStatus status); | |
131 | |
132 // Called asynchronously when a multi file picker is done. | |
133 HRESULT MultiPickerDone(MultiFileAsyncOp* async, AsyncStatus status); | |
134 | |
135 // Composes a multi-file result string suitable for returning to a | |
136 // from a storage file collection. | |
137 static HRESULT ComposeMultiFileResult(StorageFileVectorCollection* files, | |
138 base::string16* result); | |
139 private: | |
140 DISALLOW_COPY_AND_ASSIGN(OpenFilePickerSession); | |
141 }; | |
142 | |
143 class SaveFilePickerSession : public FilePickerSessionBase { | |
144 public: | |
145 explicit SaveFilePickerSession(OPENFILENAME* open_file_name); | |
146 | |
147 private: | |
148 HRESULT StartFilePicker() override; | |
149 | |
150 typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*> | |
151 SaveFileAsyncOp; | |
152 | |
153 // Called asynchronously when the save file picker is done. | |
154 HRESULT FilePickerDone(SaveFileAsyncOp* async, AsyncStatus status); | |
155 }; | |
156 | |
157 FilePickerSessionBase::FilePickerSessionBase(OPENFILENAME* open_file_name) | |
158 : open_file_name_(open_file_name), | |
159 event_(true, false), | |
160 success_(false) { | |
161 } | |
162 | |
163 bool FilePickerSessionBase::Run() { | |
164 DCHECK(globals.appview_msg_loop != NULL); | |
165 | |
166 // Post the picker request over to the metro thread. | |
167 bool posted = globals.appview_msg_loop->PostTask(FROM_HERE, | |
168 base::Bind(&FilePickerSessionBase::DoFilePicker, base::Unretained(this))); | |
169 if (!posted) | |
170 return false; | |
171 | |
172 // Wait for the file picker to complete. | |
173 event_.Wait(); | |
174 | |
175 return success_; | |
176 } | |
177 | |
178 void FilePickerSessionBase::DoFilePicker() { | |
179 // The file picker will fail if spawned from a snapped application, | |
180 // so let's attempt to unsnap first if we're in that state. | |
181 HRESULT hr = ChromeAppView::Unsnap(); | |
182 if (FAILED(hr)) { | |
183 LOG(ERROR) << "Failed to unsnap for file picker, error 0x" << hr; | |
184 } | |
185 | |
186 if (SUCCEEDED(hr)) | |
187 hr = StartFilePicker(); | |
188 | |
189 if (FAILED(hr)) { | |
190 LOG(ERROR) << "Failed to start file picker, error 0x" | |
191 << std::hex << hr; | |
192 | |
193 event_.Signal(); | |
194 } | |
195 } | |
196 | |
197 OpenFilePickerSession::OpenFilePickerSession(OPENFILENAME* open_file_name) | |
198 : FilePickerSessionBase(open_file_name) { | |
199 } | |
200 | |
201 HRESULT OpenFilePickerSession::SinglePickerDone(SingleFileAsyncOp* async, | |
202 AsyncStatus status) { | |
203 if (status == Completed) { | |
204 mswr::ComPtr<winstorage::IStorageFile> file; | |
205 HRESULT hr = async->GetResults(file.GetAddressOf()); | |
206 | |
207 if (file) { | |
208 mswr::ComPtr<winstorage::IStorageItem> storage_item; | |
209 if (SUCCEEDED(hr)) | |
210 hr = file.As(&storage_item); | |
211 | |
212 mswrw::HString file_path; | |
213 if (SUCCEEDED(hr)) | |
214 hr = storage_item->get_Path(file_path.GetAddressOf()); | |
215 | |
216 if (SUCCEEDED(hr)) { | |
217 UINT32 path_len = 0; | |
218 const wchar_t* path_str = | |
219 ::WindowsGetStringRawBuffer(file_path.Get(), &path_len); | |
220 | |
221 // If the selected file name is longer than the supplied buffer, | |
222 // we return false as per GetOpenFileName documentation. | |
223 if (path_len < open_file_name_->nMaxFile) { | |
224 base::wcslcpy(open_file_name_->lpstrFile, | |
225 path_str, | |
226 open_file_name_->nMaxFile); | |
227 success_ = true; | |
228 } | |
229 } | |
230 } else { | |
231 LOG(ERROR) << "NULL IStorageItem"; | |
232 } | |
233 } else { | |
234 LOG(ERROR) << "Unexpected async status " << static_cast<int>(status); | |
235 } | |
236 | |
237 event_.Signal(); | |
238 | |
239 return S_OK; | |
240 } | |
241 | |
242 HRESULT OpenFilePickerSession::MultiPickerDone(MultiFileAsyncOp* async, | |
243 AsyncStatus status) { | |
244 if (status == Completed) { | |
245 mswr::ComPtr<StorageFileVectorCollection> files; | |
246 HRESULT hr = async->GetResults(files.GetAddressOf()); | |
247 | |
248 if (files) { | |
249 base::string16 result; | |
250 if (SUCCEEDED(hr)) | |
251 hr = ComposeMultiFileResult(files.Get(), &result); | |
252 | |
253 if (SUCCEEDED(hr)) { | |
254 if (result.size() + 1 < open_file_name_->nMaxFile) { | |
255 // Because the result has embedded nulls, we must memcpy. | |
256 memcpy(open_file_name_->lpstrFile, | |
257 result.c_str(), | |
258 (result.size() + 1) * sizeof(result[0])); | |
259 success_ = true; | |
260 } | |
261 } | |
262 } else { | |
263 LOG(ERROR) << "NULL StorageFileVectorCollection"; | |
264 } | |
265 } else { | |
266 LOG(ERROR) << "Unexpected async status " << static_cast<int>(status); | |
267 } | |
268 | |
269 event_.Signal(); | |
270 | |
271 return S_OK; | |
272 } | |
273 | |
274 HRESULT OpenFilePickerSession::StartFilePicker() { | |
275 DCHECK(globals.appview_msg_loop->BelongsToCurrentThread()); | |
276 DCHECK(open_file_name_ != NULL); | |
277 | |
278 mswrw::HStringReference class_name( | |
279 RuntimeClass_Windows_Storage_Pickers_FileOpenPicker); | |
280 | |
281 // Create the file picker. | |
282 mswr::ComPtr<winstorage::Pickers::IFileOpenPicker> picker; | |
283 HRESULT hr = ::Windows::Foundation::ActivateInstance( | |
284 class_name.Get(), picker.GetAddressOf()); | |
285 CheckHR(hr); | |
286 | |
287 // Set the file type filter | |
288 mswr::ComPtr<winfoundtn::Collections::IVector<HSTRING>> filter; | |
289 hr = picker->get_FileTypeFilter(filter.GetAddressOf()); | |
290 if (FAILED(hr)) | |
291 return hr; | |
292 | |
293 if (open_file_name_->lpstrFilter == NULL) { | |
294 hr = filter->Append(mswrw::HStringReference(L"*").Get()); | |
295 if (FAILED(hr)) | |
296 return hr; | |
297 } else { | |
298 // The filter is a concatenation of zero terminated string pairs, | |
299 // where each pair is {description, extension}. The concatenation ends | |
300 // with a zero length string - e.g. a double zero terminator. | |
301 const wchar_t* walk = open_file_name_->lpstrFilter; | |
302 while (*walk != L'\0') { | |
303 // Walk past the description. | |
304 walk += wcslen(walk) + 1; | |
305 | |
306 // We should have an extension, but bail on malformed filters. | |
307 if (*walk == L'\0') | |
308 break; | |
309 | |
310 // There can be a single extension, or a list of semicolon-separated ones. | |
311 std::vector<base::string16> extensions_win32_style = base::SplitString( | |
312 walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | |
313 | |
314 // Metro wants suffixes only, not patterns. | |
315 mswrw::HString extension; | |
316 for (size_t i = 0; i < extensions_win32_style.size(); ++i) { | |
317 if (extensions_win32_style[i] == L"*.*") { | |
318 // The wildcard filter is "*" for Metro. The string "*.*" produces | |
319 // an "invalid parameter" error. | |
320 hr = extension.Set(L"*"); | |
321 } else { | |
322 // Metro wants suffixes only, not patterns. | |
323 base::string16 ext = | |
324 base::FilePath(extensions_win32_style[i]).Extension(); | |
325 if ((ext.size() < 2) || | |
326 (ext.find_first_of(L"*?") != base::string16::npos)) { | |
327 continue; | |
328 } | |
329 hr = extension.Set(ext.c_str()); | |
330 } | |
331 if (SUCCEEDED(hr)) | |
332 hr = filter->Append(extension.Get()); | |
333 if (FAILED(hr)) | |
334 return hr; | |
335 } | |
336 | |
337 // Walk past the extension. | |
338 walk += wcslen(walk) + 1; | |
339 } | |
340 } | |
341 | |
342 // Spin up a single or multi picker as appropriate. | |
343 if (open_file_name_->Flags & OFN_ALLOWMULTISELECT) { | |
344 mswr::ComPtr<MultiFileAsyncOp> completion; | |
345 hr = picker->PickMultipleFilesAsync(&completion); | |
346 if (FAILED(hr)) | |
347 return hr; | |
348 | |
349 // Create the callback method. | |
350 typedef winfoundtn::IAsyncOperationCompletedHandler< | |
351 StorageFileVectorCollection*> HandlerDoneType; | |
352 mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>( | |
353 this, &OpenFilePickerSession::MultiPickerDone)); | |
354 DCHECK(handler.Get() != NULL); | |
355 hr = completion->put_Completed(handler.Get()); | |
356 | |
357 return hr; | |
358 } else { | |
359 mswr::ComPtr<SingleFileAsyncOp> completion; | |
360 hr = picker->PickSingleFileAsync(&completion); | |
361 if (FAILED(hr)) | |
362 return hr; | |
363 | |
364 // Create the callback method. | |
365 typedef winfoundtn::IAsyncOperationCompletedHandler< | |
366 winstorage::StorageFile*> HandlerDoneType; | |
367 mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>( | |
368 this, &OpenFilePickerSession::SinglePickerDone)); | |
369 DCHECK(handler.Get() != NULL); | |
370 hr = completion->put_Completed(handler.Get()); | |
371 | |
372 return hr; | |
373 } | |
374 } | |
375 | |
376 HRESULT OpenFilePickerSession::ComposeMultiFileResult( | |
377 StorageFileVectorCollection* files, base::string16* result) { | |
378 DCHECK(files != NULL); | |
379 DCHECK(result != NULL); | |
380 | |
381 // Empty the output string. | |
382 result->clear(); | |
383 | |
384 unsigned int num_files = 0; | |
385 HRESULT hr = files->get_Size(&num_files); | |
386 if (FAILED(hr)) | |
387 return hr; | |
388 | |
389 // Make sure we return an error on an empty collection. | |
390 if (num_files == 0) { | |
391 DLOG(ERROR) << "Empty collection on input."; | |
392 return E_UNEXPECTED; | |
393 } | |
394 | |
395 // This stores the base path that should be the parent of all the files. | |
396 base::FilePath base_path; | |
397 | |
398 // Iterate through the collection and append the file paths to the result. | |
399 for (unsigned int i = 0; i < num_files; ++i) { | |
400 mswr::ComPtr<winstorage::IStorageFile> file; | |
401 hr = files->GetAt(i, file.GetAddressOf()); | |
402 if (FAILED(hr)) | |
403 return hr; | |
404 | |
405 mswr::ComPtr<winstorage::IStorageItem> storage_item; | |
406 hr = file.As(&storage_item); | |
407 if (FAILED(hr)) | |
408 return hr; | |
409 | |
410 mswrw::HString file_path_str; | |
411 hr = storage_item->get_Path(file_path_str.GetAddressOf()); | |
412 if (FAILED(hr)) | |
413 return hr; | |
414 | |
415 base::FilePath file_path(MakeStdWString(file_path_str.Get())); | |
416 if (base_path.empty()) { | |
417 DCHECK(result->empty()); | |
418 base_path = file_path.DirName(); | |
419 | |
420 // Append the path, including the terminating zero. | |
421 // We do this only for the first file. | |
422 result->append(base_path.value().c_str(), base_path.value().size() + 1); | |
423 } | |
424 DCHECK(!result->empty()); | |
425 DCHECK(!base_path.empty()); | |
426 DCHECK(base_path == file_path.DirName()); | |
427 | |
428 // Append the base name, including the terminating zero. | |
429 base::FilePath base_name = file_path.BaseName(); | |
430 result->append(base_name.value().c_str(), base_name.value().size() + 1); | |
431 } | |
432 | |
433 DCHECK(!result->empty()); | |
434 | |
435 return S_OK; | |
436 } | |
437 | |
438 SaveFilePickerSession::SaveFilePickerSession(OPENFILENAME* open_file_name) | |
439 : FilePickerSessionBase(open_file_name) { | |
440 } | |
441 | |
442 HRESULT SaveFilePickerSession::StartFilePicker() { | |
443 DCHECK(globals.appview_msg_loop->BelongsToCurrentThread()); | |
444 DCHECK(open_file_name_ != NULL); | |
445 | |
446 mswrw::HStringReference class_name( | |
447 RuntimeClass_Windows_Storage_Pickers_FileSavePicker); | |
448 | |
449 // Create the file picker. | |
450 mswr::ComPtr<winstorage::Pickers::IFileSavePicker> picker; | |
451 HRESULT hr = ::Windows::Foundation::ActivateInstance( | |
452 class_name.Get(), picker.GetAddressOf()); | |
453 CheckHR(hr); | |
454 | |
455 typedef winfoundtn::Collections::IMap<HSTRING, StringVectorItf*> | |
456 StringVectorMap; | |
457 mswr::ComPtr<StringVectorMap> choices; | |
458 hr = picker->get_FileTypeChoices(choices.GetAddressOf()); | |
459 if (FAILED(hr)) | |
460 return hr; | |
461 | |
462 if (open_file_name_->lpstrFilter) { | |
463 // The filter is a concatenation of zero terminated string pairs, | |
464 // where each pair is {description, extension list}. The concatenation ends | |
465 // with a zero length string - e.g. a double zero terminator. | |
466 const wchar_t* walk = open_file_name_->lpstrFilter; | |
467 while (*walk != L'\0') { | |
468 mswrw::HString description; | |
469 hr = description.Set(walk); | |
470 if (FAILED(hr)) | |
471 return hr; | |
472 | |
473 // Walk past the description. | |
474 walk += wcslen(walk) + 1; | |
475 | |
476 // We should have an extension, but bail on malformed filters. | |
477 if (*walk == L'\0') | |
478 break; | |
479 | |
480 // There can be a single extension, or a list of semicolon-separated ones. | |
481 std::vector<base::string16> extensions_win32_style = base::SplitString( | |
482 walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | |
483 | |
484 // Metro wants suffixes only, not patterns. Also, metro does not support | |
485 // the all files ("*") pattern in the save picker. | |
486 std::vector<base::string16> extensions; | |
487 for (size_t i = 0; i < extensions_win32_style.size(); ++i) { | |
488 base::string16 ext = | |
489 base::FilePath(extensions_win32_style[i]).Extension(); | |
490 if ((ext.size() < 2) || | |
491 (ext.find_first_of(L"*?") != base::string16::npos)) | |
492 continue; | |
493 extensions.push_back(ext); | |
494 } | |
495 | |
496 if (!extensions.empty()) { | |
497 // Convert to a Metro collection class. | |
498 mswr::ComPtr<StringVectorItf> list; | |
499 hr = mswr::MakeAndInitialize<StringVectorImpl>( | |
500 list.GetAddressOf(), extensions); | |
501 if (FAILED(hr)) | |
502 return hr; | |
503 | |
504 // Finally set the filter. | |
505 boolean replaced = FALSE; | |
506 hr = choices->Insert(description.Get(), list.Get(), &replaced); | |
507 if (FAILED(hr)) | |
508 return hr; | |
509 DCHECK_EQ(FALSE, replaced); | |
510 } | |
511 | |
512 // Walk past the extension(s). | |
513 walk += wcslen(walk) + 1; | |
514 } | |
515 } | |
516 | |
517 // The save picker requires at least one choice. Callers are strongly advised | |
518 // to provide sensible choices. If none were given, fallback to .dat. | |
519 uint32_t num_choices = 0; | |
520 hr = choices->get_Size(&num_choices); | |
521 if (FAILED(hr)) | |
522 return hr; | |
523 | |
524 if (num_choices == 0) { | |
525 mswrw::HString description; | |
526 // TODO(grt): Get a properly translated string. This can't be done from | |
527 // within metro_driver. Consider preprocessing the filter list in Chrome | |
528 // land to ensure it has this entry if all others are patterns. In that | |
529 // case, this whole block of code can be removed. | |
530 hr = description.Set(L"Data File"); | |
531 if (FAILED(hr)) | |
532 return hr; | |
533 | |
534 mswr::ComPtr<StringVectorItf> list; | |
535 hr = mswr::MakeAndInitialize<StringVectorImpl>( | |
536 list.GetAddressOf(), std::vector<base::string16>(1, L".dat")); | |
537 if (FAILED(hr)) | |
538 return hr; | |
539 | |
540 boolean replaced = FALSE; | |
541 hr = choices->Insert(description.Get(), list.Get(), &replaced); | |
542 if (FAILED(hr)) | |
543 return hr; | |
544 DCHECK_EQ(FALSE, replaced); | |
545 } | |
546 | |
547 if (open_file_name_->lpstrFile != NULL) { | |
548 hr = picker->put_SuggestedFileName( | |
549 mswrw::HStringReference( | |
550 const_cast<const wchar_t*>(open_file_name_->lpstrFile)).Get()); | |
551 if (FAILED(hr)) | |
552 return hr; | |
553 } | |
554 | |
555 mswr::ComPtr<SaveFileAsyncOp> completion; | |
556 hr = picker->PickSaveFileAsync(&completion); | |
557 if (FAILED(hr)) | |
558 return hr; | |
559 | |
560 // Create the callback method. | |
561 typedef winfoundtn::IAsyncOperationCompletedHandler< | |
562 winstorage::StorageFile*> HandlerDoneType; | |
563 mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>( | |
564 this, &SaveFilePickerSession::FilePickerDone)); | |
565 DCHECK(handler.Get() != NULL); | |
566 hr = completion->put_Completed(handler.Get()); | |
567 | |
568 return hr; | |
569 } | |
570 | |
571 HRESULT SaveFilePickerSession::FilePickerDone(SaveFileAsyncOp* async, | |
572 AsyncStatus status) { | |
573 if (status == Completed) { | |
574 mswr::ComPtr<winstorage::IStorageFile> file; | |
575 HRESULT hr = async->GetResults(file.GetAddressOf()); | |
576 | |
577 if (file) { | |
578 mswr::ComPtr<winstorage::IStorageItem> storage_item; | |
579 if (SUCCEEDED(hr)) | |
580 hr = file.As(&storage_item); | |
581 | |
582 mswrw::HString file_path; | |
583 if (SUCCEEDED(hr)) | |
584 hr = storage_item->get_Path(file_path.GetAddressOf()); | |
585 | |
586 if (SUCCEEDED(hr)) { | |
587 base::string16 path_str = MakeStdWString(file_path.Get()); | |
588 | |
589 // If the selected file name is longer than the supplied buffer, | |
590 // we return false as per GetOpenFileName documentation. | |
591 if (path_str.size() < open_file_name_->nMaxFile) { | |
592 base::wcslcpy(open_file_name_->lpstrFile, | |
593 path_str.c_str(), | |
594 open_file_name_->nMaxFile); | |
595 success_ = true; | |
596 } | |
597 } | |
598 } else { | |
599 LOG(ERROR) << "NULL IStorageItem"; | |
600 } | |
601 } else { | |
602 LOG(ERROR) << "Unexpected async status " << static_cast<int>(status); | |
603 } | |
604 | |
605 event_.Signal(); | |
606 | |
607 return S_OK; | |
608 } | |
609 | |
610 } // namespace | |
611 | |
612 BOOL MetroGetOpenFileName(OPENFILENAME* open_file_name) { | |
613 OpenFilePickerSession session(open_file_name); | |
614 | |
615 return session.Run(); | |
616 } | |
617 | |
618 BOOL MetroGetSaveFileName(OPENFILENAME* open_file_name) { | |
619 SaveFilePickerSession session(open_file_name); | |
620 | |
621 return session.Run(); | |
622 } | |
OLD | NEW |