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