diff options
Diffstat (limited to 'src/dutil/shelutil.cpp')
-rw-r--r-- | src/dutil/shelutil.cpp | 327 |
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 | |||
5 | static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; | ||
6 | |||
7 | static HRESULT GetDesktopShellView( | ||
8 | __in REFIID riid, | ||
9 | __out void **ppv | ||
10 | ); | ||
11 | static 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 | *********************************************************************/ | ||
22 | extern "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 | *******************************************************************/ | ||
34 | extern "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 | |||
67 | LExit: | ||
68 | ReleaseHandle(shExecInfo.hProcess); | ||
69 | |||
70 | return hr; | ||
71 | } | ||
72 | |||
73 | |||
74 | /******************************************************************** | ||
75 | ShelExecUnelevated() - executes a target unelevated. | ||
76 | |||
77 | *******************************************************************/ | ||
78 | extern "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 | |||
135 | LExit: | ||
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 | *******************************************************************/ | ||
151 | extern "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 | |||
168 | LExit: | ||
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 | |||
186 | EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( | ||
187 | REFKNOWNFOLDERID rfid, | ||
188 | DWORD dwFlags, | ||
189 | HANDLE hToken, | ||
190 | PWSTR *ppszPath | ||
191 | ); | ||
192 | |||
193 | extern "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 | |||
223 | LExit: | ||
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 | |||
240 | static 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 | |||
286 | LExit: | ||
287 | ReleaseObject(psv); | ||
288 | ReleaseObject(psb); | ||
289 | ReleaseObject(psf); | ||
290 | ReleaseObject(pdisp); | ||
291 | ReleaseObject(psw); | ||
292 | |||
293 | return hr; | ||
294 | } | ||
295 | |||
296 | static 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 | |||
321 | LExit: | ||
322 | ReleaseObject(pdisp); | ||
323 | ReleaseObject(psfvd); | ||
324 | ReleaseObject(pdispBackground); | ||
325 | |||
326 | return hr; | ||
327 | } | ||