Mercurial > foo_out_sdl
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/foobar2000/shared/filedialogs_vista.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,497 @@ +#include "shared.h" +#include "filedialogs.h" +#include <shlobj.h> +#include <shobjidl.h> +#include <shtypes.h> +#include <atlbase.h> +#include <atlcom.h> + +#define dTEXT(X) pfc::stringcvt::string_os_from_utf8(X) + +class FilterSpec { +public: + void clear() { + m_types.set_size(0); m_strings.remove_all(); + } + void Sanity() { + if ( GetCount() > 200 ) SetAllFiles(); + } + void SetAllFiles() { + FromString( "All files|*.*" ); + } + void FromString(const char * in) { + clear(); + if (in == NULL) return; + pfc::chain_list_v2_t<COMDLG_FILTERSPEC> types; + + for(t_size inWalk = 0; ; ) { + + t_size base1 = inWalk; + t_size delta1 = ScanForSeparator(in+base1); + t_size base2 = base1 + delta1; + if (in[base2] == 0) break; + ++base2; + t_size delta2 = ScanForSeparator(in+base2); + if (delta1 > 0 && delta2 > 0) { + COMDLG_FILTERSPEC spec; + spec.pszName = MakeString(in+base1,delta1); + spec.pszSpec = MakeString(in+base2,delta2); + types.add_item(spec); + } + inWalk = base2 + delta2; + if (in[inWalk] == 0) break; + ++inWalk; + } + + pfc::list_to_array(m_types,types); + } + + t_size GetCount() const {return m_types.get_count();} + const COMDLG_FILTERSPEC * GetPtr() const {return m_types.get_ptr();} +private: + static t_size ScanForSeparator(const char * in) { + for(t_size walk = 0; ; ++walk) { + if (in[walk] == 0 || in[walk] == '|') return walk; + } + } + WCHAR * MakeString(const char * in, t_size inLen) { + t_size len = pfc::stringcvt::estimate_utf8_to_wide(in,inLen); + WCHAR* str = AllocString(len); + pfc::stringcvt::convert_utf8_to_wide(str,len,in,inLen); + return str; + } + WCHAR * AllocString(t_size size) { + auto iter = m_strings.insert_last(); + iter->set_size(size); + return iter->get_ptr(); + } + pfc::chain_list_v2_t<pfc::array_t<WCHAR> > m_strings; + pfc::array_t<COMDLG_FILTERSPEC> m_types; +}; + +static HRESULT AddOptionsHelper(pfc::com_ptr_t<IFileDialog> dlg, DWORD opts) { + DWORD options; + HRESULT state; + if (FAILED(state = dlg->GetOptions(&options))) return state; + options |= opts; + if (FAILED(state = dlg->SetOptions( options ))) return state; + return S_OK; +} + +namespace { + + // SPECIAL + // Deal with slow or nonworking net shares, do not lockup the calling thread in such cases, just split away + // Particularly relevant to net shares referred by raw IP, these get us stuck for a long time + class PDNArg_t : public pfc::refcounted_object_root { + public: + CoTaskMemObject<LPITEMIDLIST> m_idList; + pfc::string8 m_path; + HRESULT m_result; + }; + + static unsigned CALLBACK PDNProc(void * arg) { + pfc::refcounted_object_ptr_t<PDNArg_t> ptr; ptr.attach( reinterpret_cast<PDNArg_t*>( arg ) ); + CoInitialize(0); + + SFGAOF dummy = {}; + ptr->m_result = SHParseDisplayName(dTEXT(ptr->m_path),NULL,&ptr->m_idList.m_ptr,0,&dummy); + + CoUninitialize(); + + return 0; + } +} + +static HRESULT SetFolderHelper(pfc::com_ptr_t<IFileDialog> dlg, const char * folderPath) { + CoTaskMemObject<LPITEMIDLIST> idList; + + // Do SHParseDisplayName() off-thread as it is known to lock up on bad net share references + pfc::refcounted_object_ptr_t<PDNArg_t> ptr = new PDNArg_t(); + ptr->m_path = folderPath; + ptr->m_result = E_FAIL; + HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, PDNProc, reinterpret_cast<void*>(ptr._duplicate_ptr()), 0, NULL); + DWORD status = WaitForSingleObject( hThread, 3000 ); + CloseHandle(hThread); + if (status != WAIT_OBJECT_0) return E_FAIL; + if (FAILED(ptr->m_result)) return ptr->m_result; + + pfc::com_ptr_t<IShellItem> item; + HRESULT state; + if (FAILED(state = SHCreateShellItem(NULL,NULL,ptr->m_idList.m_ptr,item.receive_ptr()))) return state; + return dlg->SetFolder(item.get_ptr()); +} + +namespace { + class _EH { + public: + void operator<<(HRESULT hr) { + if (FAILED(hr)) throw exception_com(hr); + } + }; + static _EH EH; +}; + +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) { + modal_dialog_scope modalScope(parent); + + pfc::com_ptr_t<IFileDialog> dlg; + + if (b_save) { + if (FAILED(CoCreateInstance(__uuidof(FileSaveDialog), NULL, CLSCTX_ALL, IID_IFileSaveDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + } else { + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + } + + { + FilterSpec spec; spec.FromString(p_ext_mask); + spec.Sanity(); + if (FAILED(dlg->SetFileTypes((UINT)spec.GetCount(),spec.GetPtr()))) return FALSE; + if (def_ext_mask < spec.GetCount()) { + if (FAILED(dlg->SetFileTypeIndex(def_ext_mask + 1))) return FALSE; + } + } + if (p_def_ext != NULL) { + if (FAILED(dlg->SetDefaultExtension(dTEXT(p_def_ext)))) return FALSE; + } + if (p_title != NULL) { + if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE; + } + + if (!p_filename.is_empty()) { + pfc::string path(p_filename); + pfc::string fn = pfc::io::path::getFileName(path); + pfc::string parent = pfc::io::path::getParent(path); + if (!parent.isEmpty()) SetFolderHelper(dlg,parent.ptr()); + dlg->SetFileName(dTEXT(fn.ptr())); + } else if (p_directory != NULL) { + SetFolderHelper(dlg,p_directory); + } + + if (FAILED(AddOptionsHelper(dlg, FOS_FORCEFILESYSTEM))) return FALSE; + + if (FAILED( dlg->Show(parent) ) ) return FALSE; + + { + pfc::com_ptr_t<IShellItem> result; + if (FAILED(dlg->GetResult(result.receive_ptr()))) return FALSE; + + CoTaskMemObject<WCHAR*> nameBuf; + if (FAILED(result->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE; + + p_filename = pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ); + } + return TRUE; +} + +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) { + modal_dialog_scope modalScope(parent); + + pfc::com_ptr_t<IFileOpenDialog> dlg; + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + + { + FilterSpec spec; spec.FromString(p_ext_mask); + spec.Sanity(); + if (FAILED(dlg->SetFileTypes((UINT)spec.GetCount(),spec.GetPtr()))) return FALSE; + if (def_ext_mask < spec.GetCount()) { + if (FAILED(dlg->SetFileTypeIndex(def_ext_mask + 1))) return FALSE; + } + } + if (p_def_ext != NULL) { + if (FAILED(dlg->SetDefaultExtension(dTEXT(p_def_ext)))) return FALSE; + } + if (p_title != NULL) { + if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE; + } + + if (p_directory != NULL) { + SetFolderHelper(dlg,p_directory); + } + + if (FAILED(AddOptionsHelper(dlg, FOS_ALLOWMULTISELECT | FOS_FORCEFILESYSTEM))) return FALSE; + + if (FAILED( dlg->Show(parent) ) ) return FALSE; + + { + pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl; + pfc::com_ptr_t<IShellItemArray> results; + if (FAILED(dlg->GetResults(results.receive_ptr()))) return FALSE; + DWORD total; + if (FAILED(results->GetCount(&total))) return FALSE; + for(DWORD itemWalk = 0; itemWalk < total; ++itemWalk) { + pfc::com_ptr_t<IShellItem> item; + if (SUCCEEDED(results->GetItemAt(itemWalk,item.receive_ptr()))) { + CoTaskMemObject<WCHAR*> nameBuf; + if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE; + + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + + } + } + + if (result->get_count() == 0) return FALSE; + + out = result.detach(); + } + + return TRUE; +} +#if 0 +namespace { + class CFileDialogEvents_LocateFile : public IFileDialogEvents { + public: + CFileDialogEvents_LocateFile(pfc::stringp tofind) : m_tofind(tofind) {} + HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialog *pfd) {return S_OK;} + + HRESULT STDMETHODCALLTYPE OnFolderChanging( IFileDialog *pfd, IShellItem *psiFolder) {return S_OK;} + + HRESULT STDMETHODCALLTYPE OnFolderChange( IFileDialog *pfd ) { + //pfd->GetFolder(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE OnSelectionChange( IFileDialog *pfd ) {return S_OK;} + + HRESULT STDMETHODCALLTYPE OnShareViolation( IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse) { return S_OK; } + + HRESULT STDMETHODCALLTYPE OnTypeChange( IFileDialog *pfd ) {return S_OK; } + + HRESULT STDMETHODCALLTYPE OnOverwrite( IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse) {return S_OK; } + + private: + const pfc::string m_tofind; + }; + +} +#endif +BOOL Vista_BrowseForFolder(HWND parent, const char * p_title, pfc::string_base & path) { + modal_dialog_scope modalScope(parent); + pfc::com_ptr_t<IFileOpenDialog> dlg; + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + + if (p_title != NULL) { + if (FAILED(dlg->SetTitle(dTEXT(p_title)))) return FALSE; + } + + if (FAILED(AddOptionsHelper(dlg, FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM))) return FALSE; + + if (!path.is_empty()) { + SetFolderHelper(dlg,path); + } + + if (FAILED( dlg->Show(parent) ) ) return FALSE; + + { + pfc::com_ptr_t<IShellItem> result; + if (FAILED(dlg->GetResult(result.receive_ptr()))) return FALSE; + + CoTaskMemObject<WCHAR*> nameBuf; + if (FAILED(result->GetDisplayName(SIGDN_FILESYSPATH,&nameBuf.m_ptr))) return FALSE; + + path = pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ); + } + return TRUE; +} + + +__inline HRESULT mySHLoadLibraryFromItem( + __in IShellItem *psiLibrary, + __in DWORD grfMode, + __in REFIID riid, + __deref_out void **ppv +) +{ + *ppv = NULL; + IShellLibrary *plib; + + HRESULT hr = CoCreateInstance( + CLSID_ShellLibrary, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&plib)); + + if (SUCCEEDED(hr)) + { + hr = plib->LoadLibraryFromItem (psiLibrary, grfMode); + if (SUCCEEDED(hr)) + { + hr = plib->QueryInterface (riid, ppv); + } + plib->Release(); + } + return hr; +} + +// +// from shobjidl.h +// +__inline HRESULT mySHLoadLibraryFromKnownFolder( + __in REFKNOWNFOLDERID kfidLibrary, + __in DWORD grfMode, + __in REFIID riid, + __deref_out void **ppv) +{ + *ppv = NULL; + IShellLibrary *plib; + HRESULT hr = CoCreateInstance( + CLSID_ShellLibrary, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&plib)); + if (SUCCEEDED(hr)) + { + hr = plib->LoadLibraryFromKnownFolder(kfidLibrary, grfMode); + if (SUCCEEDED(hr)) + { + hr = plib->QueryInterface(riid, ppv); + } + plib->Release(); + } + return hr; +} + +puGetOpenFileNameMultiResult Vista_BrowseForFolderEx(HWND parent,const char * p_title, const char * initPath) { + try { + modal_dialog_scope modalScope(parent); + pfc::com_ptr_t<IFileOpenDialog> dlg; + if (FAILED(CoCreateInstance(__uuidof(FileOpenDialog), NULL, CLSCTX_ALL, IID_IFileOpenDialog, (void**) dlg.receive_ptr()))) throw pfc::exception_not_implemented(); + + if (p_title != NULL) { + EH << dlg->SetTitle(dTEXT(p_title)); + } + + EH << AddOptionsHelper(dlg, FOS_ALLOWMULTISELECT | FOS_PICKFOLDERS); + + if (initPath && *initPath) { + SetFolderHelper(dlg,initPath); + } + + EH << dlg->Show(parent); + + pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl; + pfc::com_ptr_t<IShellItemArray> results; + EH << dlg->GetResults(results.receive_ptr()); + DWORD total; + EH << results->GetCount(&total); + CoTaskMemObject<WCHAR*> nameBuf; + for(DWORD itemWalk = 0; itemWalk < total; ++itemWalk) { + pfc::com_ptr_t<IShellItem> item; + EH << results->GetItemAt(itemWalk,item.receive_ptr()); + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()))) { + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + } else { + pfc::com_ptr_t<IShellLibrary> library; + if (SUCCEEDED(mySHLoadLibraryFromItem(item.get_ptr(), STGM_READ, IID_IShellLibrary, (void**)library.receive_ptr()))) { + pfc::com_ptr_t<IShellItemArray> subFolders; + EH << library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, (void**)subFolders.receive_ptr()); + DWORD subTotal; + EH << subFolders->GetCount(&subTotal); + for(DWORD subWalk = 0; subWalk < subTotal; ++subWalk) { + pfc::com_ptr_t<IShellItem> subItem; + EH << subFolders->GetItemAt(subWalk,subItem.receive_ptr()); + EH << subItem->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()); + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + } + } + } + } + if (result->GetCount() == 0) return NULL; + return result.detach(); + } catch(exception_com const &) { + return NULL; + } +} + +static bool GetLegacyKnownFolder(int & out, REFKNOWNFOLDERID id) { + if (id == FOLDERID_Music) { + out = CSIDL_MYMUSIC; + return true; + } else if (id == FOLDERID_Pictures) { + out = CSIDL_MYPICTURES; + return true; + } else if (id == FOLDERID_Videos) { + out = CSIDL_MYVIDEO; + return true; + } else if (id == FOLDERID_Documents) { + out = CSIDL_MYDOCUMENTS; + return true; + } else if (id == FOLDERID_Desktop) { + out = CSIDL_DESKTOP; + return true; + } else { + return false; + } +} + +puGetOpenFileNameMultiResult SHARED_EXPORT uEvalKnownFolder(REFKNOWNFOLDERID id) { + try { + pfc::com_ptr_t<IShellLibrary> library; + EH << mySHLoadLibraryFromKnownFolder(id, STGM_READ, IID_IShellLibrary, (void**)library.receive_ptr()); + + pfc::com_ptr_t<IShellItemArray> subFolders; + EH << library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, (void**)subFolders.receive_ptr()); + + pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl; + + DWORD subTotal; + EH << subFolders->GetCount(&subTotal); + CoTaskMemObject<WCHAR*> nameBuf; + for(DWORD subWalk = 0; subWalk < subTotal; ++subWalk) { + pfc::com_ptr_t<IShellItem> subItem; + EH << subFolders->GetItemAt(subWalk,subItem.receive_ptr()); + EH << subItem->GetDisplayName(SIGDN_FILESYSPATH,nameBuf.Receive()); + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + } + if (result->get_count() == 0) return NULL; + return result.detach(); + } catch(exception_com const &) { + //failed + } + + + try { + CComPtr<IKnownFolderManager> mgr; CComPtr<IKnownFolder> folder; CoTaskMemObject<wchar_t*> path; + EH << CoCreateInstance(__uuidof(KnownFolderManager), nullptr, CLSCTX_ALL, IID_IKnownFolderManager, (void**)&mgr.p); + EH << mgr->GetFolder(id, &folder.p); + EH << folder->GetPath(0, &path.m_ptr); + + pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl; + result->AddItem(pfc::stringcvt::string_utf8_from_os(path.m_ptr)); + return result.detach(); + } catch (exception_com const &) { + + } + + //FALLBACK + + + + { + int legacyID; + if (GetLegacyKnownFolder(legacyID, id)) { + try { + TCHAR path[MAX_PATH+16] = {}; + EH << SHGetFolderPath(NULL, legacyID, NULL, SHGFP_TYPE_CURRENT, path); + path[_countof(path)-1] = 0; + pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl; + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( path ) ); + return result.detach(); + } catch(exception_com const &) { + //failed; + } + } + } +#if 0 // Vista code path - uninteresting, XP shit still needs to be supported, SHGetKnownFolderPath() does not exist on XP + try { + pfc::ptrholder_t<uGetOpenFileNameMultiResult_impl> result = new uGetOpenFileNameMultiResult_impl; + CoTaskMemObject<WCHAR*> nameBuf; + EH << SHGetKnownFolderPath(id, 0, NULL, nameBuf.Receive()); + result->AddItem(pfc::stringcvt::string_utf8_from_os_ex( nameBuf.m_ptr ) ); + return result.detach(); + } catch(exception_com const &) { + //failed + } +#endif + return NULL; //failure +}
