aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/shelutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/shelutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/shelutil.cpp342
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
20static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
21
22static HRESULT GetDesktopShellView(
23 __in REFIID riid,
24 __out void **ppv
25 );
26static 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*********************************************************************/
37extern "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*******************************************************************/
49extern "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
82LExit:
83 ReleaseHandle(shExecInfo.hProcess);
84
85 return hr;
86}
87
88
89/********************************************************************
90 ShelExecUnelevated() - executes a target unelevated.
91
92*******************************************************************/
93extern "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
150LExit:
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*******************************************************************/
166extern "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
183LExit:
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
201EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
202 REFKNOWNFOLDERID rfid,
203 DWORD dwFlags,
204 HANDLE hToken,
205 PWSTR *ppszPath
206 );
207
208extern "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
238LExit:
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
255static 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
301LExit:
302 ReleaseObject(psv);
303 ReleaseObject(psb);
304 ReleaseObject(psf);
305 ReleaseObject(pdisp);
306 ReleaseObject(psw);
307
308 return hr;
309}
310
311static 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
336LExit:
337 ReleaseObject(pdisp);
338 ReleaseObject(psfvd);
339 ReleaseObject(pdispBackground);
340
341 return hr;
342}