Use IFileOpenDialog instead of SHBrowseForFolder and GetOpenFileName.
It is the recommended option since Vista and has better long path support.
Sean Hall committed
Aug 10, 2022 at 19:24 UTC
3756ae7c8dc60d459511d0b067ebef5efb052746
5 files changed
+234
-70
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+11
-15
@@ -1145,26 +1145,19 @@ public: // IBootstrapperApplication
1145
)
1146
{
1147
HRESULT hr = S_OK;
1148
+ LPWSTR sczPath = NULL;
1149
1150
if (BOOTSTRAPPER_CACHE_RESOLVE_NONE == *pAction && BOOTSTRAPPER_DISPLAY_FULL == m_command.display) // prompt to change the source location.
1151
{
1151
- OPENFILENAMEW ofn = { };
1152
- WCHAR wzFile[MAX_PATH] = { };
1153
-
1154
- ::StringCchCopyW(wzFile, countof(wzFile), rgSearchPaths[dwRecommendedSearchPath]);
1155
-
1156
- ofn.lStructSize = sizeof(ofn);
1157
- ofn.hwndOwner = m_hWnd;
1158
- ofn.lpstrFile = wzFile;
1159
- ofn.nMaxFile = countof(wzFile);
1160
- ofn.lpstrFilter = L"All Files\0*.*\0";
1161
- ofn.nFilterIndex = 1;
1162
- ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
1163
- ofn.lpstrTitle = m_pTheme->sczCaption;
1152
+ static COMDLG_FILTERSPEC vrgFilters[] =
1153
+ {
1154
+ { L"All Files", L"*.*" },
1155
+ };
1156
1165
- if (::GetOpenFileNameW(&ofn))
1157
+ hr = WnduShowOpenFileDialog(m_hWnd, TRUE, TRUE, m_pTheme->sczCaption, vrgFilters, countof(vrgFilters), 1, rgSearchPaths[dwRecommendedSearchPath], &sczPath);
1158
+ if (SUCCEEDED(hr))
1159
{
1167
- hr = m_pEngine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile);
1160
+ hr = m_pEngine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, sczPath);
1161
*pAction = BOOTSTRAPPER_CACHE_RESOLVE_RETRY;
1162
}
1163
else
@@ -1175,6 +1168,9 @@ public: // IBootstrapperApplication
1168
// else there's nothing more we can do in non-interactive mode
1169
1170
*pfCancel |= CheckCanceled();
1171
+
1172
+ ReleaseStr(sczPath);
1173
+
1174
return hr;
1175
}
1176
src/libs/dutil/WixToolset.DUtil/inc/wndutil.h
+27
@@ -35,6 +35,33 @@ HRESULT DAPI WnduGetControlText(
35
__inout_z LPWSTR* psczText
36
);
37
38
+/********************************************************************
39
+ WnduShowOpenFileDialog - shows the system dialog to select a file for opening.
40
+
41
+*******************************************************************/
42
+HRESULT DAPI WnduShowOpenFileDialog(
43
+ __in_opt HWND hwndParent,
44
+ __in BOOL fForcePathExists,
45
+ __in BOOL fForceFileExists,
46
+ __in_opt LPCWSTR wzTitle,
47
+ __in_opt COMDLG_FILTERSPEC* rgFilters,
48
+ __in DWORD cFilters,
49
+ __in DWORD dwDefaultFilter,
50
+ __in_opt LPCWSTR wzDefaultPath,
51
+ __inout LPWSTR* psczPath
52
+ );
53
+
54
+/********************************************************************
55
+ WnduShowOpenFolderDialog - shows the system dialog to select a folder.
56
+
57
+*******************************************************************/
58
+HRESULT DAPI WnduShowOpenFolderDialog(
59
+ __in_opt HWND hwndParent,
60
+ __in BOOL fForceFileSystem,
61
+ __in_opt LPCWSTR wzTitle,
62
+ __inout LPWSTR* psczPath
63
+ );
64
+
65
#ifdef __cplusplus
66
}
67
#endif
src/libs/dutil/WixToolset.DUtil/thmutil.cpp
+30
-37
@@ -4992,54 +4992,47 @@ static void OnBrowseDirectory(
4992
)
4993
{
4994
HRESULT hr = S_OK;
4995
- WCHAR wzPath[MAX_PATH] = { };
4996
- BROWSEINFOW browseInfo = { };
4997
- PIDLIST_ABSOLUTE pidl = NULL;
4995
+ LPWSTR sczPath = NULL;
4996
+ THEME_CONTROL* pTargetControl = NULL;
4997
+ BOOL fSetVariable = NULL != pTheme->pfnSetStringVariable;
4998
4999
- browseInfo.hwndOwner = pTheme->hwndParent;
5000
- browseInfo.pszDisplayName = wzPath;
5001
- browseInfo.lpszTitle = pTheme->sczCaption;
5002
- browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
5003
- pidl = ::SHBrowseForFolderW(&browseInfo);
5004
- if (pidl && ::SHGetPathFromIDListW(pidl, wzPath))
4999
+ hr = WnduShowOpenFolderDialog(pTheme->hwndParent, TRUE, pTheme->sczCaption, &sczPath);
5000
+ if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
5001
{
5006
- // Since editbox changes aren't immediately saved off, we have to treat them differently.
5007
- THEME_CONTROL* pTargetControl = NULL;
5008
- BOOL fSetVariable = NULL != pTheme->pfnSetStringVariable;
5009
-
5010
- for (DWORD i = 0; i < pTheme->cControls; ++i)
5011
- {
5012
- THEME_CONTROL* pControl = pTheme->rgControls + i;
5013
-
5014
- if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) &&
5015
- CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1))
5016
- {
5017
- pTargetControl = pControl;
5018
- break;
5019
- }
5020
- }
5002
+ ExitFunction();
5003
+ }
5004
+ ThmExitOnFailure(hr, "Failed to prompt user for directory.");
5005
5022
- if (pTargetControl && !pTargetControl->fDisableAutomaticFunctionality && (!fSetVariable || THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type))
5023
- {
5024
- fSetVariable = FALSE;
5025
- hr = ThemeSetTextControl(pTargetControl, wzPath);
5026
- ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName);
5027
- }
5006
+ for (DWORD i = 0; i < pTheme->cControls; ++i)
5007
+ {
5008
+ THEME_CONTROL* pControl = pTheme->rgControls + i;
5009
5029
- if (fSetVariable)
5010
+ if ((!pControl->wPageId || pControl->wPageId == pTheme->dwCurrentPageId) &&
5011
+ CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pControl->sczName, -1, pAction->BrowseDirectory.sczVariableName, -1))
5012
{
5031
- hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, wzPath, FALSE, pTheme->pvVariableContext);
5032
- ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName);
5013
+ pTargetControl = pControl;
5014
+ break;
5015
}
5016
+ }
5017
5035
- ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH);
5018
+ // Since editbox changes aren't immediately saved off, we have to treat them differently.
5019
+ if (pTargetControl && !pTargetControl->fDisableAutomaticFunctionality && (!fSetVariable || THEME_CONTROL_TYPE_EDITBOX == pTargetControl->type))
5020
+ {
5021
+ fSetVariable = FALSE;
5022
+ hr = ThemeSetTextControl(pTargetControl, sczPath);
5023
+ ThmExitOnFailure(hr, "Failed to set text on control: %ls", pTargetControl->sczName);
5024
}
5025
5038
-LExit:
5039
- if (pidl)
5026
+ if (fSetVariable)
5027
{
5041
- ::CoTaskMemFree(pidl);
5028
+ hr = pTheme->pfnSetStringVariable(pAction->BrowseDirectory.sczVariableName, sczPath, FALSE, pTheme->pvVariableContext);
5029
+ ThmExitOnFailure(hr, "Failed to set variable: %ls", pAction->BrowseDirectory.sczVariableName);
5030
}
5031
+
5032
+ ThemeShowPageEx(pTheme, pTheme->dwCurrentPageId, SW_SHOW, THEME_SHOW_PAGE_REASON_REFRESH);
5033
+
5034
+LExit:
5035
+ ReleaseStr(sczPath);
5036
}
5037
5038
static BOOL OnButtonClicked(
src/libs/dutil/WixToolset.DUtil/wndutil.cpp
+157
@@ -161,6 +161,163 @@ LExit:
161
}
162
163
164
+DAPI_(HRESULT) WnduShowOpenFileDialog(
165
+ __in_opt HWND hwndParent,
166
+ __in BOOL fForcePathExists,
167
+ __in BOOL fForceFileExists,
168
+ __in_opt LPCWSTR wzTitle,
169
+ __in COMDLG_FILTERSPEC* rgFilters,
170
+ __in DWORD cFilters,
171
+ __in DWORD dwDefaultFilter,
172
+ __in_opt LPCWSTR wzDefaultPath,
173
+ __inout LPWSTR* psczPath
174
+ )
175
+{
176
+ HRESULT hr = S_OK;
177
+ IFileOpenDialog* pFileOpenDialog = NULL;
178
+ FILEOPENDIALOGOPTIONS fos = 0;
179
+ FILEOPENDIALOGOPTIONS additionalFlags = FOS_NOCHANGEDIR;
180
+ IShellItem* psi = NULL;
181
+ LPWSTR sczDefaultDirectory = NULL;
182
+ LPWSTR sczPath = NULL;
183
+
184
+ hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpenDialog));
185
+ WnduExitOnFailure(hr, "Failed to create FileOpenDialog object.");
186
+
187
+ hr = pFileOpenDialog->SetTitle(wzTitle);
188
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog title.");
189
+
190
+ hr = pFileOpenDialog->GetOptions(&fos);
191
+ WnduExitOnFailure(hr, "Failed to get FileOpenDialog options.");
192
+
193
+ if (fForcePathExists)
194
+ {
195
+ additionalFlags |= FOS_PATHMUSTEXIST;
196
+ }
197
+
198
+ if (fForceFileExists)
199
+ {
200
+ additionalFlags |= FOS_FILEMUSTEXIST;
201
+ }
202
+
203
+ hr = pFileOpenDialog->SetOptions(fos | additionalFlags);
204
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog options.");
205
+
206
+ hr = pFileOpenDialog->SetFileTypes(cFilters, rgFilters);
207
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog file types.");
208
+
209
+ hr = pFileOpenDialog->SetFileTypeIndex(dwDefaultFilter);
210
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog default file type.");
211
+
212
+ if (wzDefaultPath && *wzDefaultPath)
213
+ {
214
+ hr = PathGetDirectory(wzDefaultPath, &sczDefaultDirectory);
215
+ if (SUCCEEDED(hr))
216
+ {
217
+ hr = ::SHCreateItemFromParsingName(sczDefaultDirectory, NULL, IID_PPV_ARGS(&psi));
218
+ if (SUCCEEDED(hr))
219
+ {
220
+ hr = pFileOpenDialog->SetFolder(psi);
221
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog folder.");
222
+ }
223
+
224
+ ReleaseNullObject(psi);
225
+ }
226
+
227
+ hr = pFileOpenDialog->SetFileName(wzDefaultPath);
228
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog file name.");
229
+ }
230
+
231
+ hr = pFileOpenDialog->Show(hwndParent);
232
+ if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
233
+ {
234
+ ExitFunction();
235
+ }
236
+ WnduExitOnFailure(hr, "Failed to show FileOpenDialog for file.");
237
+
238
+ hr = pFileOpenDialog->GetResult(&psi);
239
+ WnduExitOnFailure(hr, "Failed to get FileOpenDialog result.");
240
+
241
+ hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &sczPath);
242
+ WnduExitOnFailure(hr, "Failed to get FileOpenDialog result path.");
243
+
244
+ hr = StrAllocString(psczPath, sczPath, 0);
245
+ WnduExitOnFailure(hr, "Failed to copy FileOpenDialog result path.");
246
+
247
+LExit:
248
+ if (sczPath)
249
+ {
250
+ ::CoTaskMemFree(sczPath);
251
+ }
252
+
253
+ ReleaseStr(sczDefaultDirectory);
254
+ ReleaseObject(psi);
255
+ ReleaseObject(pFileOpenDialog);
256
+
257
+ return hr;
258
+}
259
+
260
+
261
+DAPI_(HRESULT) WnduShowOpenFolderDialog(
262
+ __in_opt HWND hwndParent,
263
+ __in BOOL fForceFileSystem,
264
+ __in_opt LPCWSTR wzTitle,
265
+ __inout LPWSTR* psczPath
266
+ )
267
+{
268
+ HRESULT hr = S_OK;
269
+ IFileOpenDialog* pFileOpenDialog = NULL;
270
+ FILEOPENDIALOGOPTIONS fos = 0;
271
+ FILEOPENDIALOGOPTIONS additionalFlags = FOS_PICKFOLDERS | FOS_NOCHANGEDIR;
272
+ IShellItem* psi = NULL;
273
+ LPWSTR sczPath = NULL;
274
+
275
+ hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpenDialog));
276
+ WnduExitOnFailure(hr, "Failed to create FileOpenDialog object.");
277
+
278
+ hr = pFileOpenDialog->SetTitle(wzTitle);
279
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog title.");
280
+
281
+ hr = pFileOpenDialog->GetOptions(&fos);
282
+ WnduExitOnFailure(hr, "Failed to get FileOpenDialog options.");
283
+
284
+ if (fForceFileSystem)
285
+ {
286
+ additionalFlags |= FOS_FORCEFILESYSTEM;
287
+ }
288
+
289
+ hr = pFileOpenDialog->SetOptions(fos | additionalFlags);
290
+ WnduExitOnFailure(hr, "Failed to set FileOpenDialog options.");
291
+
292
+ hr = pFileOpenDialog->Show(hwndParent);
293
+ if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
294
+ {
295
+ ExitFunction();
296
+ }
297
+ WnduExitOnFailure(hr, "Failed to show FileOpenDialog for folder.");
298
+
299
+ hr = pFileOpenDialog->GetResult(&psi);
300
+ WnduExitOnFailure(hr, "Failed to get FileOpenDialog result.");
301
+
302
+ hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &sczPath);
303
+ WnduExitOnFailure(hr, "Failed to get FileOpenDialog result path.");
304
+
305
+ hr = StrAllocString(psczPath, sczPath, 0);
306
+ WnduExitOnFailure(hr, "Failed to copy FileOpenDialog result path.");
307
+
308
+LExit:
309
+ if (sczPath)
310
+ {
311
+ ::CoTaskMemFree(sczPath);
312
+ }
313
+
314
+ ReleaseObject(psi);
315
+ ReleaseObject(pFileOpenDialog);
316
+
317
+ return hr;
318
+}
319
+
320
+
321
static DWORD CALLBACK RichEditStreamFromFileHandleCallback(
322
__in DWORD_PTR dwCookie,
323
__in_bcount(cb) LPBYTE pbBuff,
src/tools/thmviewer/thmviewer.cpp
+9
-18
@@ -66,6 +66,13 @@ static void CALLBACK ThmviewerTraceError(
66
__in va_list args
67
);
68
69
+static COMDLG_FILTERSPEC vrgFilters[] =
70
+{
71
+ { L"Theme Files (*.thm)", L"*.thm" },
72
+ { L"XML Files (*.xml)", L"*.xml" },
73
+ { L"All Files (*.*)", L"*.*" },
74
+};
75
+
76
77
int WINAPI wWinMain(
78
__in HINSTANCE hInstance,
@@ -110,24 +117,8 @@ int WINAPI wWinMain(
117
if (!sczThemeFile)
118
{
119
// Prompt for a path to the theme file.
113
- OPENFILENAMEW ofn = { };
114
- WCHAR wzFile[MAX_PATH] = { };
115
-
116
- ofn.lStructSize = sizeof(ofn);
117
- ofn.hwndOwner = hWnd;
118
- ofn.lpstrFile = wzFile;
119
- ofn.nMaxFile = countof(wzFile);
120
- ofn.lpstrFilter = L"Theme Files (*.thm)\0*.thm\0XML Files (*.xml)\0*.xml\0All Files (*.*)\0*.*\0";
121
- ofn.nFilterIndex = 1;
122
- ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
123
- ofn.lpstrTitle = vpTheme->sczCaption;
124
-
125
- if (::GetOpenFileNameW(&ofn))
126
- {
127
- hr = StrAllocString(&sczThemeFile, wzFile, 0);
128
- ExitOnFailure(hr, "Failed to copy opened file to theme file.");
129
- }
130
- else
120
+ hr = WnduShowOpenFileDialog(hWnd, TRUE, TRUE, vpTheme->sczCaption, vrgFilters, countof(vrgFilters), 1, NULL, &sczThemeFile);
121
+ if (FAILED(hr))
122
{
123
::MessageBoxW(hWnd, L"Must specify a path to theme file.", vpTheme->sczCaption, MB_OK | MB_ICONERROR);
124
ExitFunction1(hr = E_INVALIDARG);