diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/shelutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/shelutil.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp new file mode 100644 index 00000000..2eb9a52a --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp | |||
@@ -0,0 +1,342 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
6 | // Exit macros | ||
7 | #define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) | ||
8 | #define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) | ||
9 | #define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) | ||
10 | #define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) | ||
11 | #define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) | ||
12 | #define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) | ||
13 | #define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) | ||
14 | #define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) | ||
15 | #define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) | ||
16 | #define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) | ||
17 | #define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) | ||
18 | #define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) | ||
19 | |||
20 | static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; | ||
21 | |||
22 | static HRESULT GetDesktopShellView( | ||
23 | __in REFIID riid, | ||
24 | __out void **ppv | ||
25 | ); | ||
26 | static HRESULT GetShellDispatchFromView( | ||
27 | __in IShellView *psv, | ||
28 | __in REFIID riid, | ||
29 | __out void **ppv | ||
30 | ); | ||
31 | |||
32 | /******************************************************************** | ||
33 | ShelFunctionOverride - overrides the shell functions. Typically used | ||
34 | for unit testing. | ||
35 | |||
36 | *********************************************************************/ | ||
37 | extern "C" void DAPI ShelFunctionOverride( | ||
38 | __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW | ||
39 | ) | ||
40 | { | ||
41 | vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; | ||
42 | } | ||
43 | |||
44 | |||
45 | /******************************************************************** | ||
46 | ShelExec() - executes a target. | ||
47 | |||
48 | *******************************************************************/ | ||
49 | extern "C" HRESULT DAPI ShelExec( | ||
50 | __in_z LPCWSTR wzTargetPath, | ||
51 | __in_z_opt LPCWSTR wzParameters, | ||
52 | __in_z_opt LPCWSTR wzVerb, | ||
53 | __in_z_opt LPCWSTR wzWorkingDirectory, | ||
54 | __in int nShowCmd, | ||
55 | __in_opt HWND hwndParent, | ||
56 | __out_opt HANDLE* phProcess | ||
57 | ) | ||
58 | { | ||
59 | HRESULT hr = S_OK; | ||
60 | SHELLEXECUTEINFOW shExecInfo = {}; | ||
61 | |||
62 | shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); | ||
63 | shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; | ||
64 | shExecInfo.hwnd = hwndParent; | ||
65 | shExecInfo.lpVerb = wzVerb; | ||
66 | shExecInfo.lpFile = wzTargetPath; | ||
67 | shExecInfo.lpParameters = wzParameters; | ||
68 | shExecInfo.lpDirectory = wzWorkingDirectory; | ||
69 | shExecInfo.nShow = nShowCmd; | ||
70 | |||
71 | if (!vpfnShellExecuteExW(&shExecInfo)) | ||
72 | { | ||
73 | ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); | ||
74 | } | ||
75 | |||
76 | if (phProcess) | ||
77 | { | ||
78 | *phProcess = shExecInfo.hProcess; | ||
79 | shExecInfo.hProcess = NULL; | ||
80 | } | ||
81 | |||
82 | LExit: | ||
83 | ReleaseHandle(shExecInfo.hProcess); | ||
84 | |||
85 | return hr; | ||
86 | } | ||
87 | |||
88 | |||
89 | /******************************************************************** | ||
90 | ShelExecUnelevated() - executes a target unelevated. | ||
91 | |||
92 | *******************************************************************/ | ||
93 | extern "C" HRESULT DAPI ShelExecUnelevated( | ||
94 | __in_z LPCWSTR wzTargetPath, | ||
95 | __in_z_opt LPCWSTR wzParameters, | ||
96 | __in_z_opt LPCWSTR wzVerb, | ||
97 | __in_z_opt LPCWSTR wzWorkingDirectory, | ||
98 | __in int nShowCmd | ||
99 | ) | ||
100 | { | ||
101 | HRESULT hr = S_OK; | ||
102 | BSTR bstrTargetPath = NULL; | ||
103 | VARIANT vtParameters = { }; | ||
104 | VARIANT vtVerb = { }; | ||
105 | VARIANT vtWorkingDirectory = { }; | ||
106 | VARIANT vtShow = { }; | ||
107 | IShellView* psv = NULL; | ||
108 | IShellDispatch2* psd = NULL; | ||
109 | |||
110 | bstrTargetPath = ::SysAllocString(wzTargetPath); | ||
111 | ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); | ||
112 | |||
113 | if (wzParameters && *wzParameters) | ||
114 | { | ||
115 | vtParameters.vt = VT_BSTR; | ||
116 | vtParameters.bstrVal = ::SysAllocString(wzParameters); | ||
117 | ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); | ||
118 | } | ||
119 | |||
120 | if (wzVerb && *wzVerb) | ||
121 | { | ||
122 | vtVerb.vt = VT_BSTR; | ||
123 | vtVerb.bstrVal = ::SysAllocString(wzVerb); | ||
124 | ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); | ||
125 | } | ||
126 | |||
127 | if (wzWorkingDirectory && *wzWorkingDirectory) | ||
128 | { | ||
129 | vtWorkingDirectory.vt = VT_BSTR; | ||
130 | vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); | ||
131 | ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); | ||
132 | } | ||
133 | |||
134 | vtShow.vt = VT_INT; | ||
135 | vtShow.intVal = nShowCmd; | ||
136 | |||
137 | hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); | ||
138 | ShelExitOnFailure(hr, "Failed to get desktop shell view."); | ||
139 | |||
140 | hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); | ||
141 | ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); | ||
142 | |||
143 | hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); | ||
144 | if (S_FALSE == hr) | ||
145 | { | ||
146 | hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); | ||
147 | } | ||
148 | ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); | ||
149 | |||
150 | LExit: | ||
151 | ReleaseObject(psd); | ||
152 | ReleaseObject(psv); | ||
153 | ReleaseBSTR(vtWorkingDirectory.bstrVal); | ||
154 | ReleaseBSTR(vtVerb.bstrVal); | ||
155 | ReleaseBSTR(vtParameters.bstrVal); | ||
156 | ReleaseBSTR(bstrTargetPath); | ||
157 | |||
158 | return hr; | ||
159 | } | ||
160 | |||
161 | |||
162 | /******************************************************************** | ||
163 | ShelGetFolder() - gets a folder by CSIDL. | ||
164 | |||
165 | *******************************************************************/ | ||
166 | extern "C" HRESULT DAPI ShelGetFolder( | ||
167 | __out_z LPWSTR* psczFolderPath, | ||
168 | __in int csidlFolder | ||
169 | ) | ||
170 | { | ||
171 | HRESULT hr = S_OK; | ||
172 | WCHAR wzPath[MAX_PATH]; | ||
173 | |||
174 | hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); | ||
175 | ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); | ||
176 | |||
177 | hr = StrAllocString(psczFolderPath, wzPath, 0); | ||
178 | ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); | ||
179 | |||
180 | hr = PathBackslashTerminate(psczFolderPath); | ||
181 | ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); | ||
182 | |||
183 | LExit: | ||
184 | return hr; | ||
185 | } | ||
186 | |||
187 | |||
188 | /******************************************************************** | ||
189 | ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. | ||
190 | |||
191 | Note: return E_NOTIMPL if called on pre-Vista operating systems. | ||
192 | *******************************************************************/ | ||
193 | #ifndef REFKNOWNFOLDERID | ||
194 | #define REFKNOWNFOLDERID REFGUID | ||
195 | #endif | ||
196 | |||
197 | #ifndef KF_FLAG_CREATE | ||
198 | #define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition | ||
199 | #endif | ||
200 | |||
201 | EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( | ||
202 | REFKNOWNFOLDERID rfid, | ||
203 | DWORD dwFlags, | ||
204 | HANDLE hToken, | ||
205 | PWSTR *ppszPath | ||
206 | ); | ||
207 | |||
208 | extern "C" HRESULT DAPI ShelGetKnownFolder( | ||
209 | __out_z LPWSTR* psczFolderPath, | ||
210 | __in REFKNOWNFOLDERID rfidFolder | ||
211 | ) | ||
212 | { | ||
213 | HRESULT hr = S_OK; | ||
214 | HMODULE hShell32Dll = NULL; | ||
215 | PFN_SHGetKnownFolderPath pfn = NULL; | ||
216 | LPWSTR pwzPath = NULL; | ||
217 | |||
218 | hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); | ||
219 | if (E_MODNOTFOUND == hr) | ||
220 | { | ||
221 | TraceError(hr, "Failed to load shell32.dll"); | ||
222 | ExitFunction1(hr = E_NOTIMPL); | ||
223 | } | ||
224 | ShelExitOnFailure(hr, "Failed to load shell32.dll."); | ||
225 | |||
226 | pfn = reinterpret_cast<PFN_SHGetKnownFolderPath>(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); | ||
227 | ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); | ||
228 | |||
229 | hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); | ||
230 | ShelExitOnFailure(hr, "Failed to get known folder path."); | ||
231 | |||
232 | hr = StrAllocString(psczFolderPath, pwzPath, 0); | ||
233 | ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); | ||
234 | |||
235 | hr = PathBackslashTerminate(psczFolderPath); | ||
236 | ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); | ||
237 | |||
238 | LExit: | ||
239 | if (pwzPath) | ||
240 | { | ||
241 | ::CoTaskMemFree(pwzPath); | ||
242 | } | ||
243 | |||
244 | if (hShell32Dll) | ||
245 | { | ||
246 | ::FreeLibrary(hShell32Dll); | ||
247 | } | ||
248 | |||
249 | return hr; | ||
250 | } | ||
251 | |||
252 | |||
253 | // Internal functions. | ||
254 | |||
255 | static HRESULT GetDesktopShellView( | ||
256 | __in REFIID riid, | ||
257 | __out void **ppv | ||
258 | ) | ||
259 | { | ||
260 | HRESULT hr = S_OK; | ||
261 | IShellWindows* psw = NULL; | ||
262 | HWND hwnd = NULL; | ||
263 | IDispatch* pdisp = NULL; | ||
264 | VARIANT vEmpty = {}; // VT_EMPTY | ||
265 | IShellBrowser* psb = NULL; | ||
266 | IShellFolder* psf = NULL; | ||
267 | IShellView* psv = NULL; | ||
268 | |||
269 | // use the shell view for the desktop using the shell windows automation to find the | ||
270 | // desktop web browser and then grabs its view | ||
271 | // returns IShellView, IFolderView and related interfaces | ||
272 | hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); | ||
273 | ShelExitOnFailure(hr, "Failed to get shell view."); | ||
274 | |||
275 | hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); | ||
276 | if (S_OK == hr) | ||
277 | { | ||
278 | hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); | ||
279 | ShelExitOnFailure(hr, "Failed to get desktop window."); | ||
280 | |||
281 | hr = psb->QueryActiveShellView(&psv); | ||
282 | ShelExitOnFailure(hr, "Failed to get active shell view."); | ||
283 | |||
284 | hr = psv->QueryInterface(riid, ppv); | ||
285 | ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); | ||
286 | } | ||
287 | else if (S_FALSE == hr) | ||
288 | { | ||
289 | //Windows XP | ||
290 | hr = SHGetDesktopFolder(&psf); | ||
291 | ShelExitOnFailure(hr, "Failed to get desktop folder."); | ||
292 | |||
293 | hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); | ||
294 | ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | ShelExitOnFailure(hr, "Failed to get desktop window."); | ||
299 | } | ||
300 | |||
301 | LExit: | ||
302 | ReleaseObject(psv); | ||
303 | ReleaseObject(psb); | ||
304 | ReleaseObject(psf); | ||
305 | ReleaseObject(pdisp); | ||
306 | ReleaseObject(psw); | ||
307 | |||
308 | return hr; | ||
309 | } | ||
310 | |||
311 | static HRESULT GetShellDispatchFromView( | ||
312 | __in IShellView *psv, | ||
313 | __in REFIID riid, | ||
314 | __out void **ppv | ||
315 | ) | ||
316 | { | ||
317 | HRESULT hr = S_OK; | ||
318 | IDispatch *pdispBackground = NULL; | ||
319 | IShellFolderViewDual *psfvd = NULL; | ||
320 | IDispatch *pdisp = NULL; | ||
321 | |||
322 | // From a shell view object, gets its automation interface and from that get the shell | ||
323 | // application object that implements IShellDispatch2 and related interfaces. | ||
324 | hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); | ||
325 | ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); | ||
326 | |||
327 | hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); | ||
328 | ShelExitOnFailure(hr, "Failed to get shell folder view dual."); | ||
329 | |||
330 | hr = psfvd->get_Application(&pdisp); | ||
331 | ShelExitOnFailure(hr, "Failed to application object."); | ||
332 | |||
333 | hr = pdisp->QueryInterface(riid, ppv); | ||
334 | ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); | ||
335 | |||
336 | LExit: | ||
337 | ReleaseObject(pdisp); | ||
338 | ReleaseObject(psfvd); | ||
339 | ReleaseObject(pdispBackground); | ||
340 | |||
341 | return hr; | ||
342 | } | ||