|
@@ -29,6 +29,172 @@
|
|
|
#include "../../core/windows/SDL_windows.h"
|
|
|
#include "../../thread/SDL_systhread.h"
|
|
|
|
|
|
+// Flags/GUIDs defined for compatibility with pre-Vista headers
|
|
|
+#ifndef __IFileDialog_INTERFACE_DEFINED__
|
|
|
+enum _FILEOPENDIALOGOPTIONS {
|
|
|
+ FOS_OVERWRITEPROMPT = 0x2,
|
|
|
+ FOS_STRICTFILETYPES = 0x4,
|
|
|
+ FOS_NOCHANGEDIR = 0x8,
|
|
|
+ FOS_PICKFOLDERS = 0x20,
|
|
|
+ FOS_FORCEFILESYSTEM = 0x40,
|
|
|
+ FOS_ALLNONSTORAGEITEMS = 0x80,
|
|
|
+ FOS_NOVALIDATE = 0x100,
|
|
|
+ FOS_ALLOWMULTISELECT = 0x200,
|
|
|
+ FOS_PATHMUSTEXIST = 0x800,
|
|
|
+ FOS_FILEMUSTEXIST = 0x1000,
|
|
|
+ FOS_CREATEPROMPT = 0x2000,
|
|
|
+ FOS_SHAREAWARE = 0x4000,
|
|
|
+ FOS_NOREADONLYRETURN = 0x8000,
|
|
|
+ FOS_NOTESTFILECREATE = 0x10000,
|
|
|
+ FOS_HIDEMRUPLACES = 0x20000,
|
|
|
+ FOS_HIDEPINNEDPLACES = 0x40000,
|
|
|
+ FOS_NODEREFERENCELINKS = 0x100000,
|
|
|
+ FOS_DONTADDTORECENT = 0x2000000,
|
|
|
+ FOS_FORCESHOWHIDDEN = 0x10000000,
|
|
|
+ FOS_DEFAULTNOMINIMODE = 0x20000000,
|
|
|
+ FOS_FORCEPREVIEWPANEON = 0x40000000,
|
|
|
+ FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
|
|
|
+};
|
|
|
+
|
|
|
+typedef DWORD FILEOPENDIALOGOPTIONS;
|
|
|
+
|
|
|
+typedef enum FDAP {
|
|
|
+ FDAP_BOTTOM = 0,
|
|
|
+ FDAP_TOP = 1
|
|
|
+} FDAP;
|
|
|
+
|
|
|
+/* *INDENT-OFF* */ // clang-format off
|
|
|
+typedef struct IFileDialogVtbl
|
|
|
+{
|
|
|
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(IFileDialog *, REFIID, void **);
|
|
|
+ ULONG (STDMETHODCALLTYPE *AddRef)(IFileDialog *);
|
|
|
+ ULONG (STDMETHODCALLTYPE *Release)(IFileDialog *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Show)(IFileDialog *, HWND);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileTypes)(IFileDialog *, UINT, const COMDLG_FILTERSPEC *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileTypeIndex)(IFileDialog *, UINT);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFileTypeIndex)(IFileDialog *, UINT *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Advise)(IFileDialog *, IFileDialogEvents *, DWORD *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Unadvise)(IFileDialog *, DWORD);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetOptions)(IFileDialog *, FILEOPENDIALOGOPTIONS);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetOptions)(IFileDialog *, FILEOPENDIALOGOPTIONS *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetDefaultFolder)(IFileDialog *, IShellItem *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFolder)(IFileDialog *, IShellItem *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFolder)(IFileDialog *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetCurrentSelection)(IFileDialog *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileName)(IFileDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFileName)(IFileDialog *, LPWSTR *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetTitle)(IFileDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetOkButtonLabel)(IFileDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileNameLabel)(IFileDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetResult)(IFileDialog *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *AddPlace)(IFileDialog *, IShellItem *, FDAP);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetDefaultExtension)(IFileDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Close)(IFileDialog *, HRESULT);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetClientGuid)(IFileDialog *, REFGUID);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *ClearClientData)(IFileDialog *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFilter)(IFileDialog *,IShellItemFilter *);
|
|
|
+} IFileDialogVtbl;
|
|
|
+/* *INDENT-ON* */ // clang-format on
|
|
|
+
|
|
|
+struct IFileDialog
|
|
|
+{
|
|
|
+ const struct IFileDialogVtbl *lpVtbl;
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef __IFileDialog2_INTERFACE_DEFINED__
|
|
|
+/* *INDENT-OFF* */ // clang-format off
|
|
|
+typedef struct IFileDialog2Vtbl
|
|
|
+{
|
|
|
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(IFileDialog2 *, REFIID, void **);
|
|
|
+ ULONG (STDMETHODCALLTYPE *AddRef)(IFileDialog2 *);
|
|
|
+ ULONG (STDMETHODCALLTYPE *Release)(IFileDialog2 *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Show)(IFileDialog2 *, HWND);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileTypes)(IFileDialog2 *, UINT, const COMDLG_FILTERSPEC *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileTypeIndex)(IFileDialog2 *, UINT);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFileTypeIndex)(IFileDialog2 *, UINT *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Advise)(IFileDialog2 *, IFileDialogEvents *, DWORD *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Unadvise)(IFileDialog2 *, DWORD);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetOptions)(IFileDialog2 *, FILEOPENDIALOGOPTIONS);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetOptions)(IFileDialog2 *, FILEOPENDIALOGOPTIONS *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetDefaultFolder)(IFileDialog2 *, IShellItem *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFolder)(IFileDialog2 *, IShellItem *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFolder)(IFileDialog2 *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetCurrentSelection)(IFileDialog2 *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileName)(IFileDialog2 *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFileName)(IFileDialog2 *, LPWSTR *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetTitle)(IFileDialog2 *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetOkButtonLabel)(IFileDialog2 *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileNameLabel)(IFileDialog2 *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetResult)(IFileDialog2 *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *AddPlace)(IFileDialog2 *, IShellItem *, FDAP);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetDefaultExtension)(IFileDialog2 *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Close)(IFileDialog2 *, HRESULT);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetClientGuid)(IFileDialog2 *, REFGUID);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *ClearClientData)(IFileDialog2 *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFilter)(IFileDialog2 *, IShellItemFilter *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetCancelButtonLabel)(IFileDialog2 *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetNavigationRoot)(IFileDialog2 *, IShellItem *);
|
|
|
+} IFileDialog2Vtbl;
|
|
|
+/* *INDENT-ON* */ // clang-format on
|
|
|
+
|
|
|
+struct IFileDialog2
|
|
|
+{
|
|
|
+ const struct IFileDialog2Vtbl *lpVtbl;
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifndef __IFileOpenDialog_INTERFACE_DEFINED__
|
|
|
+/* *INDENT-OFF* */ // clang-format off
|
|
|
+typedef struct IFileOpenDialogVtbl
|
|
|
+{
|
|
|
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(IFileOpenDialog *, REFIID, void **);
|
|
|
+ ULONG (STDMETHODCALLTYPE *AddRef)(IFileOpenDialog *);
|
|
|
+ ULONG (STDMETHODCALLTYPE *Release)(IFileOpenDialog *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Show)(IFileOpenDialog *, HWND);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileTypes)(IFileOpenDialog *, UINT, const COMDLG_FILTERSPEC *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileTypeIndex)(IFileOpenDialog *, UINT);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFileTypeIndex)(IFileOpenDialog *, UINT *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Advise)(IFileOpenDialog *, IFileDialogEvents *, DWORD *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Unadvise)(IFileOpenDialog *, DWORD);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetOptions)(IFileOpenDialog *, FILEOPENDIALOGOPTIONS);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetOptions)(IFileOpenDialog *, FILEOPENDIALOGOPTIONS *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetDefaultFolder)(IFileOpenDialog *, IShellItem *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFolder)(IFileOpenDialog *, IShellItem *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFolder)(IFileOpenDialog *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetCurrentSelection)(IFileOpenDialog *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileName)(IFileOpenDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetFileName)(IFileOpenDialog *, LPWSTR *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetTitle)(IFileOpenDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetOkButtonLabel)(IFileOpenDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFileNameLabel)(IFileOpenDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetResult)(IFileOpenDialog *, IShellItem **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *AddPlace)(IFileOpenDialog *, IShellItem *, FDAP);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetDefaultExtension)(IFileOpenDialog *, LPCWSTR);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *Close)(IFileOpenDialog *, HRESULT);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetClientGuid)(IFileOpenDialog *, REFGUID);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *ClearClientData)(IFileOpenDialog *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *SetFilter)(IFileOpenDialog *, IShellItemFilter *);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetResults)(IFileOpenDialog *, IShellItemArray **);
|
|
|
+ HRESULT (STDMETHODCALLTYPE *GetSelectedItems)(IFileOpenDialog *, IShellItemArray **);
|
|
|
+} IFileOpenDialogVtbl;
|
|
|
+/* *INDENT-ON* */ // clang-format on
|
|
|
+
|
|
|
+struct IFileOpenDialog
|
|
|
+{
|
|
|
+ const struct IFileOpenDialogVtbl *lpVtbl;
|
|
|
+};
|
|
|
+#endif
|
|
|
+
|
|
|
+/* *INDENT-OFF* */ // clang-format off
|
|
|
+static const CLSID SDL_CLSID_FileOpenDialog = { 0xdc1c5a9c, 0xe88a, 0x4dde, { 0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7 } };
|
|
|
+static const CLSID SDL_CLSID_FileSaveDialog = { 0xc0b4e2f3, 0xba21, 0x4773, { 0x8d, 0xba, 0x33, 0x5e, 0xc9, 0x46, 0xeb, 0x8b } };
|
|
|
+
|
|
|
+static const IID SDL_IID_IFileDialog = { 0x42f85136, 0xdb7e, 0x439c, { 0x85, 0xf1, 0xe4, 0x07, 0x5d, 0x13, 0x5f, 0xc8 } };
|
|
|
+static const IID SDL_IID_IFileDialog2 = { 0x61744fc7, 0x85b5, 0x4791, { 0xa9, 0xb0, 0x27, 0x22, 0x76, 0x30, 0x9b, 0x13 } };
|
|
|
+static const IID SDL_IID_IFileOpenDialog = { 0xd57c7288, 0xd4ad, 0x4768, { 0xbe, 0x02, 0x9d, 0x96, 0x95, 0x32, 0xd9, 0x60 } };
|
|
|
+/* *INDENT-ON* */ // clang-format on
|
|
|
+
|
|
|
// If this number is too small, selecting too many files will give an error
|
|
|
#define SELECTLIST_SIZE 65536
|
|
|
|
|
@@ -115,6 +281,11 @@ bool windows_ShowModernFileFolderDialog(SDL_FileDialogType dialog_type, const ch
|
|
|
allow_many = false;
|
|
|
}
|
|
|
|
|
|
+ HMODULE shell32_handle = NULL;
|
|
|
+
|
|
|
+ typedef HRESULT(WINAPI *pfnSHCreateItemFromParsingName)(PCWSTR, IBindCtx *, REFIID, void **);
|
|
|
+ pfnSHCreateItemFromParsingName pSHCreateItemFromParsingName = NULL;
|
|
|
+
|
|
|
IFileDialog *pFileDialog = NULL;
|
|
|
IFileOpenDialog *pFileOpenDialog = NULL;
|
|
|
IFileDialog2 *pFileDialog2 = NULL;
|
|
@@ -135,6 +306,17 @@ bool windows_ShowModernFileFolderDialog(SDL_FileDialogType dialog_type, const ch
|
|
|
bool success = false;
|
|
|
bool co_init = false;
|
|
|
|
|
|
+ // We can assume shell32 is already loaded here.
|
|
|
+ shell32_handle = GetModuleHandle(TEXT("shell32.dll"));
|
|
|
+ if (!shell32_handle) {
|
|
|
+ goto quit;
|
|
|
+ }
|
|
|
+
|
|
|
+ pSHCreateItemFromParsingName = (pfnSHCreateItemFromParsingName)GetProcAddress(shell32_handle, "SHCreateItemFromParsingName");
|
|
|
+ if (!pSHCreateItemFromParsingName) {
|
|
|
+ goto quit;
|
|
|
+ }
|
|
|
+
|
|
|
if (filter_wchar && nfilters > 0) {
|
|
|
wchar_t *filter_ptr = filter_wchar;
|
|
|
filter_data = SDL_calloc(sizeof(COMDLG_FILTERSPEC), nfilters);
|
|
@@ -196,11 +378,11 @@ bool windows_ShowModernFileFolderDialog(SDL_FileDialogType dialog_type, const ch
|
|
|
|
|
|
co_init = true;
|
|
|
|
|
|
- CHECK(CoCreateInstance(is_save ? &CLSID_FileSaveDialog : &CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, &IID_IFileDialog, (void**)&pFileDialog));
|
|
|
- CHECK(pFileDialog->lpVtbl->QueryInterface(pFileDialog, &IID_IFileDialog2, (void**)&pFileDialog2));
|
|
|
+ CHECK(CoCreateInstance(is_save ? &SDL_CLSID_FileSaveDialog : &SDL_CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IFileDialog, (void**)&pFileDialog));
|
|
|
+ CHECK(pFileDialog->lpVtbl->QueryInterface(pFileDialog, &SDL_IID_IFileDialog2, (void**)&pFileDialog2));
|
|
|
|
|
|
if (allow_many) {
|
|
|
- CHECK(pFileDialog->lpVtbl->QueryInterface(pFileDialog, &IID_IFileOpenDialog, (void**)&pFileOpenDialog));
|
|
|
+ CHECK(pFileDialog->lpVtbl->QueryInterface(pFileDialog, &SDL_IID_IFileOpenDialog, (void**)&pFileOpenDialog));
|
|
|
}
|
|
|
|
|
|
CHECK(pFileDialog2->lpVtbl->GetOptions(pFileDialog2, &pfos));
|
|
@@ -231,7 +413,7 @@ bool windows_ShowModernFileFolderDialog(SDL_FileDialogType dialog_type, const ch
|
|
|
// SetFolder would enforce using the same location each and every time, but
|
|
|
// Windows docs recommend against it
|
|
|
if (default_folder_w) {
|
|
|
- CHECK(SHCreateItemFromParsingName(default_folder_w, NULL, &IID_IShellItem, (void**)&pFolderItem));
|
|
|
+ CHECK(pSHCreateItemFromParsingName(default_folder_w, NULL, &IID_IShellItem, (void**)&pFolderItem));
|
|
|
CHECK(pFileDialog->lpVtbl->SetDefaultFolder(pFileDialog, pFolderItem));
|
|
|
}
|
|
|
|