comparison foosdk/sdk/foobar2000/shared/filedialogs_vista.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 #include "shared.h"
2 #include "filedialogs.h"
3 #include <shlobj.h>
4 #include <shobjidl.h>
5 #include <shtypes.h>
6 #include <atlbase.h>
7 #include <atlcom.h>
8
9 #define dTEXT(X) pfc::stringcvt::string_os_from_utf8(X)
10
11 class FilterSpec {
12 public:
13 void clear() {
14 m_types.set_size(0); m_strings.remove_all();
15 }
16 void Sanity() {
17 if ( GetCount() > 200 ) SetAllFiles();
18 }
19 void SetAllFiles() {
20 FromString( "All files|*.*" );
21 }
22 void FromString(const char * in) {
23 clear();
24 if (in == NULL) return;
25 pfc::chain_list_v2_t<COMDLG_FILTERSPEC> types;
26
27 for(t_size inWalk = 0; ; ) {
28
29 t_size base1 = inWalk;
30 t_size delta1 = ScanForSeparator(in+base1);
31 t_size base2 = base1 + delta1;
32 if (in[base2] == 0) break;
33 ++base2;
34 t_size delta2 = ScanForSeparator(in+base2);
35 if (delta1 > 0 && delta2 > 0) {
36 COMDLG_FILTERSPEC spec;
37 spec.pszName = MakeString(in+base1,delta1);
38 spec.pszSpec = MakeString(in+base2,delta2);
39 types.add_item(spec);
40 }
41 inWalk = base2 + delta2;
42 if (in[inWalk] == 0) break;
43 ++inWalk;
44 }
45
46 pfc::list_to_array(m_types,types);
47 }
48
49 t_size GetCount() const {return m_types.get_count();}
50 const COMDLG_FILTERSPEC * GetPtr() const {return m_types.get_ptr();}
51 private:
52 static t_size ScanForSeparator(const char * in) {
53 for(t_size walk = 0; ; ++walk) {
54 if (in[walk] == 0 || in[walk] == '|') return walk;
55 }
56 }
57 WCHAR * MakeString(const char * in, t_size inLen) {
58 t_size len = pfc::stringcvt::estimate_utf8_to_wide(in,inLen);
59 WCHAR* str = AllocString(len);
60 pfc::stringcvt::convert_utf8_to_wide(str,len,in,inLen);
61 return str;
62 }
63 WCHAR * AllocString(t_size size) {
64 auto iter = m_strings.insert_last();
65 iter->set_size(size);
66 return iter->get_ptr();
67 }
68 pfc::chain_list_v2_t<pfc::array_t<WCHAR> > m_strings;
69 pfc::array_t<COMDLG_FILTERSPEC> m_types;
70 };
71
72 static HRESULT AddOptionsHelper(pfc::com_ptr_t<IFileDialog> dlg, DWORD opts) {
73 DWORD options;
74 HRESULT state;
75 if (FAILED(state = dlg->GetOptions(&options))) return state;
76 options |= opts;
77 if (FAILED(state = dlg->SetOptions( options ))) return state;
78 return S_OK;
79 }
80
81 namespace {
82
83 // SPECIAL
84 // Deal with slow or nonworking net shares, do not lockup the calling thread in such cases, just split away
85 // Particularly relevant to net shares referred by raw IP, these get us stuck for a long time
86 class PDNArg_t : public pfc::refcounted_object_root {
87 public:
88 CoTaskMemObject<LPITEMIDLIST> m_idList;
89 pfc::string8 m_path;
90 HRESULT m_result;
91 };
92
93 static unsigned CALLBACK PDNProc(void * arg) {
94 pfc::refcounted_object_ptr_t<PDNArg_t> ptr; ptr.attach( reinterpret_cast<PDNArg_t*>( arg ) );
95 CoInitialize(0);
96
97 SFGAOF dummy = {};
98 ptr->m_result = SHParseDisplayName(dTEXT(ptr->m_path),NULL,&ptr->m_idList.m_ptr,0,&dummy);
99
100 CoUninitialize();
101
102 return 0;
103 }
104 }
105
106 static HRESULT SetFolderHelper(pfc::com_ptr_t<IFileDialog> dlg, const char * folderPath) {
107 CoTaskMemObject<LPITEMIDLIST> idList;
108
109 // Do SHParseDisplayName() off-thread as it is known to lock up on bad net share references
110 pfc::refcounted_object_ptr_t<PDNArg_t> ptr = new PDNArg_t();
111 ptr->m_path = folderPath;
112 ptr->m_result = E_FAIL;
113 HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, PDNProc, reinterpret_cast<void*>(ptr._duplicate_ptr()), 0, NULL);
114 DWORD status = WaitForSingleObject( hThread, 3000 );
115 CloseHandle(hThread);
116 if (status != WAIT_OBJECT_0) return E_FAIL;
117 if (FAILED(ptr->m_result)) return ptr->m_result;
118
119 pfc::com_ptr_t<IShellItem> item;
120 HRESULT state;
121 if (FAILED(state = SHCreateShellItem(NULL,NULL,ptr->m_idList.m_ptr,item.receive_ptr()))) return state;
122 return dlg->SetFolder(item.get_ptr());
123 }
124
125 namespace {
126 class _EH {
127 public:
128 void operator<<(HRESULT hr) {
129 if (FAILED(hr)) throw exception_com(hr);
130 }
131 };
132 static _EH EH;
133 };
134
135 BOOL Vista_GetOpenFileName(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::string_base & p_filename,BOOL b_save) {
136 modal_dialog_scope modalScope(parent);
137
138 pfc::com_ptr_t<IFileDialog> dlg;
139
140 if (b_save) {
141 if (FAILED(CoCreateInstance(__uuidof(FileSaveDialog), NULL, CLSCTX_ALL, IID_IFileSaveDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented();
142 } else {
143 if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented();
144 }
145
146 {
147 FilterSpec spec; spec.FromString(p_ext_mask);
148 spec.Sanity();
149 if (FAILED(dlg->SetFileTypes((UINT)spec.GetCount(),spec.GetPtr()))) return FALSE;
150 if (def_ext_mask < spec.GetCount()) {
151 if (FAILED(dlg->SetFileTypeIndex(def_ext_mask + 1))) return FALSE;
152 }
153 }
154 if (p_def_ext != NULL) {
155 if (FAILED(dlg->SetDefaultExtension(dTEXT(p_def_ext)))) return FALSE;
156 }
157 if (p_title != NULL) {
158 if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE;
159 }
160
161 if (!p_filename.is_empty()) {
162 pfc::string path(p_filename);
163 pfc::string fn = pfc::io::path::getFileName(path);
164 pfc::string parent = pfc::io::path::getParent(path);
165 if (!parent.isEmpty()) SetFolderHelper(dlg,parent.ptr());
166 dlg->SetFileName(dTEXT(fn.ptr()));
167 } else if (p_directory != NULL) {
168 SetFolderHelper(dlg,p_directory);
169 }
170
171 if (FAILED(AddOptionsHelper(dlg, FOS_FORCEFILESYSTEM))) return FALSE;
172
173 if (FAILED( dlg->Show(parent) ) ) return FALSE;
174
175 {
176 pfc::com_ptr_t<IShellItem> result;
177 if (FAILED(dlg->GetResult(result.receive_ptr()))) return FALSE;
178
179 CoTaskMemObject<WCHAR*> nameBuf;
180 if (FAILED(result->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE;
181
182 p_filename = pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr );
183 }
184 return TRUE;
185 }
186
187 BOOL Vista_GetOpenFileNameMulti(HWND parent,const char * p_ext_mask,unsigned def_ext_mask,const char * p_def_ext,const char * p_title,const char * p_directory,pfc::ptrholder_t<uGetOpenFileNameMultiResult> & out) {
188 modal_dialog_scope modalScope(parent);
189
190 pfc::com_ptr_t<IFileOpenDialog> dlg;
191 if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented();
192
193 {
194 FilterSpec spec; spec.FromString(p_ext_mask);
195 spec.Sanity();
196 if (FAILED(dlg->SetFileTypes((UINT)spec.GetCount(),spec.GetPtr()))) return FALSE;
197 if (def_ext_mask < spec.GetCount()) {
198 if (FAILED(dlg->SetFileTypeIndex(def_ext_mask + 1))) return FALSE;
199 }
200 }
201 if (p_def_ext != NULL) {
202 if (FAILED(dlg->SetDefaultExtension(dTEXT(p_def_ext)))) return FALSE;
203 }
204 if (p_title != NULL) {
205 if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE;
206 }
207
208 if (p_directory != NULL) {
209 SetFolderHelper(dlg,p_directory);
210 }
211
212 if (FAILED(AddOptionsHelper(dlg, FOS_ALLOWMULTISELECT | FOS_FORCEFILESYSTEM))) return FALSE;
213
214 if (FAILED( dlg->Show(parent) ) ) return FALSE;
215
216 {
217 pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl;
218 pfc::com_ptr_t<IShellItemArray> results;
219 if (FAILED(dlg->GetResults(results.receive_ptr()))) return FALSE;
220 DWORD total;
221 if (FAILED(results->GetCount(&total))) return FALSE;
222 for(DWORD itemWalk = 0; itemWalk < total; ++itemWalk) {
223 pfc::com_ptr_t<IShellItem> item;
224 if (SUCCEEDED(results->GetItemAt(itemWalk,item.receive_ptr()))) {
225 CoTaskMemObject<WCHAR*> nameBuf;
226 if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE;
227
228 result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) );
229
230 }
231 }
232
233 if (result->get_count() == 0) return FALSE;
234
235 out = result.detach();
236 }
237
238 return TRUE;
239 }
240 #if 0
241 namespace {
242 class CFileDialogEvents_LocateFile : public IFileDialogEvents {
243 public:
244 CFileDialogEvents_LocateFile(pfc::stringp tofind) : m_tofind(tofind) {}
245 HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *pfd) {return S_OK;}
246
247 HRESULT STDMETHODCALLTYPE OnFolderChanging( IFileDialog *pfd, IShellItem *psiFolder) {return S_OK;}
248
249 HRESULT STDMETHODCALLTYPE OnFolderChange( IFileDialog *pfd ) {
250 //pfd->GetFolder();
251 return S_OK;
252 }
253
254 HRESULT STDMETHODCALLTYPE OnSelectionChange( IFileDialog *pfd ) {return S_OK;}
255
256 HRESULT STDMETHODCALLTYPE OnShareViolation( IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse) { return S_OK; }
257
258 HRESULT STDMETHODCALLTYPE OnTypeChange( IFileDialog *pfd ) {return S_OK; }
259
260 HRESULT STDMETHODCALLTYPE OnOverwrite( IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse) {return S_OK; }
261
262 private:
263 const pfc::string m_tofind;
264 };
265
266 }
267 #endif
268 BOOL Vista_BrowseForFolder(HWND parent, const char * p_title, pfc::string_base & path) {
269 modal_dialog_scope modalScope(parent);
270 pfc::com_ptr_t<IFileOpenDialog> dlg;
271 if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented();
272
273 if (p_title != NULL) {
274 if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE;
275 }
276
277 if (FAILED(AddOptionsHelper(dlg, FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM))) return FALSE;
278
279 if (!path.is_empty()) {
280 SetFolderHelper(dlg,path);
281 }
282
283 if (FAILED( dlg->Show(parent) ) ) return FALSE;
284
285 {
286 pfc::com_ptr_t<IShellItem> result;
287 if (FAILED(dlg->GetResult(result.receive_ptr()))) return FALSE;
288
289 CoTaskMemObject<WCHAR*> nameBuf;
290 if (FAILED(result->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE;
291
292 path = pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr );
293 }
294 return TRUE;
295 }
296
297
298 __inline HRESULT mySHLoadLibraryFromItem(
299 __in IShellItem *psiLibrary,
300 __in DWORD grfMode,
301 __in REFIID riid,
302 __deref_out void **ppv
303 )
304 {
305 *ppv = NULL;
306 IShellLibrary *plib;
307
308 HRESULT hr = CoCreateInstance(
309 CLSID_ShellLibrary,
310 NULL,
311 CLSCTX_INPROC_SERVER,
312 IID_PPV_ARGS(&plib));
313
314 if (SUCCEEDED(hr))
315 {
316 hr = plib->LoadLibraryFromItem (psiLibrary, grfMode);
317 if (SUCCEEDED(hr))
318 {
319 hr = plib->QueryInterface (riid, ppv);
320 }
321 plib->Release();
322 }
323 return hr;
324 }
325
326 //
327 // from shobjidl.h
328 //
329 __inline HRESULT mySHLoadLibraryFromKnownFolder(
330 __in REFKNOWNFOLDERID kfidLibrary,
331 __in DWORD grfMode,
332 __in REFIID riid,
333 __deref_out void **ppv)
334 {
335 *ppv = NULL;
336 IShellLibrary *plib;
337 HRESULT hr = CoCreateInstance(
338 CLSID_ShellLibrary,
339 NULL,
340 CLSCTX_INPROC_SERVER,
341 IID_PPV_ARGS(&plib));
342 if (SUCCEEDED(hr))
343 {
344 hr = plib->LoadLibraryFromKnownFolder(kfidLibrary, grfMode);
345 if (SUCCEEDED(hr))
346 {
347 hr = plib->QueryInterface(riid, ppv);
348 }
349 plib->Release();
350 }
351 return hr;
352 }
353
354 puGetOpenFileNameMultiResult Vista_BrowseForFolderEx(HWND parent,const char * p_title, const char * initPath) {
355 try {
356 modal_dialog_scope modalScope(parent);
357 pfc::com_ptr_t<IFileOpenDialog> dlg;
358 if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented();
359
360 if (p_title != NULL) {
361 EH << dlg->SetTitle(dTEXT(p_title));
362 }
363
364 EH << AddOptionsHelper(dlg, FOS_ALLOWMULTISELECT | FOS_PICKFOLDERS);
365
366 if (initPath && *initPath) {
367 SetFolderHelper(dlg,initPath);
368 }
369
370 EH << dlg->Show(parent);
371
372 pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl;
373 pfc::com_ptr_t<IShellItemArray> results;
374 EH << dlg->GetResults(results.receive_ptr());
375 DWORD total;
376 EH << results->GetCount(&total);
377 CoTaskMemObject<WCHAR*> nameBuf;
378 for(DWORD itemWalk = 0; itemWalk < total; ++itemWalk) {
379 pfc::com_ptr_t<IShellItem> item;
380 EH << results->GetItemAt(itemWalk,item.receive_ptr());
381 if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()))) {
382 result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) );
383 } else {
384 pfc::com_ptr_t<IShellLibrary> library;
385 if (SUCCEEDED(mySHLoadLibraryFromItem(item.get_ptr(), STGM_READ, IID_IShellLibrary, (void**)library.receive_ptr()))) {
386 pfc::com_ptr_t<IShellItemArray> subFolders;
387 EH << library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, (void**)subFolders.receive_ptr());
388 DWORD subTotal;
389 EH << subFolders->GetCount(&subTotal);
390 for(DWORD subWalk = 0; subWalk < subTotal; ++subWalk) {
391 pfc::com_ptr_t<IShellItem> subItem;
392 EH << subFolders->GetItemAt(subWalk,subItem.receive_ptr());
393 EH << subItem->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive());
394 result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) );
395 }
396 }
397 }
398 }
399 if (result->GetCount() == 0) return NULL;
400 return result.detach();
401 } catch(exception_com const &) {
402 return NULL;
403 }
404 }
405
406 static bool GetLegacyKnownFolder(int & out, REFKNOWNFOLDERID id) {
407 if (id == FOLDERID_Music) {
408 out = CSIDL_MYMUSIC;
409 return true;
410 } else if (id == FOLDERID_Pictures) {
411 out = CSIDL_MYPICTURES;
412 return true;
413 } else if (id == FOLDERID_Videos) {
414 out = CSIDL_MYVIDEO;
415 return true;
416 } else if (id == FOLDERID_Documents) {
417 out = CSIDL_MYDOCUMENTS;
418 return true;
419 } else if (id == FOLDERID_Desktop) {
420 out = CSIDL_DESKTOP;
421 return true;
422 } else {
423 return false;
424 }
425 }
426
427 puGetOpenFileNameMultiResult SHARED_EXPORT uEvalKnownFolder(REFKNOWNFOLDERID id) {
428 try {
429 pfc::com_ptr_t<IShellLibrary> library;
430 EH << mySHLoadLibraryFromKnownFolder(id, STGM_READ, IID_IShellLibrary, (void**)library.receive_ptr());
431
432 pfc::com_ptr_t<IShellItemArray> subFolders;
433 EH << library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, (void**)subFolders.receive_ptr());
434
435 pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl;
436
437 DWORD subTotal;
438 EH << subFolders->GetCount(&subTotal);
439 CoTaskMemObject<WCHAR*> nameBuf;
440 for(DWORD subWalk = 0; subWalk < subTotal; ++subWalk) {
441 pfc::com_ptr_t<IShellItem> subItem;
442 EH << subFolders->GetItemAt(subWalk,subItem.receive_ptr());
443 EH << subItem->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive());
444 result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) );
445 }
446 if (result->get_count() == 0) return NULL;
447 return result.detach();
448 } catch(exception_com const &) {
449 //failed
450 }
451
452
453 try {
454 CComPtr<IKnownFolderManager> mgr; CComPtr<IKnownFolder> folder; CoTaskMemObject<wchar_t*> path;
455 EH << CoCreateInstance(__uuidof(KnownFolderManager), nullptr, CLSCTX_ALL, IID_IKnownFolderManager, (void**)&mgr.p);
456 EH << mgr->GetFolder(id, &folder.p);
457 EH << folder->GetPath(0, &path.m_ptr);
458
459 pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl;
460 result->AddItem(pfc::stringcvt::string_utf8_from_os(path.m_ptr));
461 return result.detach();
462 } catch (exception_com const &) {
463
464 }
465
466 //FALLBACK
467
468
469
470 {
471 int legacyID;
472 if (GetLegacyKnownFolder(legacyID, id)) {
473 try {
474 TCHAR path[MAX_PATH+16] = {};
475 EH << SHGetFolderPath(NULL, legacyID, NULL, SHGFP_TYPE_CURRENT, path);
476 path[_countof(path)-1] = 0;
477 pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl;
478 result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( path ) );
479 return result.detach();
480 } catch(exception_com const &) {
481 //failed;
482 }
483 }
484 }
485 #if 0 // Vista code path - uninteresting, XP shit still needs to be supported, SHGetKnownFolderPath() does not exist on XP
486 try {
487 pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl;
488 CoTaskMemObject<WCHAR*> nameBuf;
489 EH << SHGetKnownFolderPath(id, 0, NULL, nameBuf.Receive());
490 result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) );
491 return result.detach();
492 } catch(exception_com const &) {
493 //failed
494 }
495 #endif
496 return NULL; //failure
497 }