@joebigelow / wix / commits / 3756ae7c

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);