main
cpp 512 lines 15.4 KB
Raw
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 ShelExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, e, s, __VA_ARGS__)
13 #define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
14 #define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__)
15 #define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__)
16 #define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__)
17 #define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__)
18 #define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__)
19 #define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__)
20
21 static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
22
23 static HRESULT DAPI GetFolderFromCsidl(
24 __out_z LPWSTR* psczFolderPath,
25 __in int csidlFolder
26 );
27 static HRESULT GetDesktopShellView(
28 __in REFIID riid,
29 __out void **ppv
30 );
31 static HRESULT GetShellDispatchFromView(
32 __in IShellView *psv,
33 __in REFIID riid,
34 __out void **ppv
35 );
36
37 /********************************************************************
38 ShelFunctionOverride - overrides the shell functions. Typically used
39 for unit testing.
40
41 *********************************************************************/
42 extern "C" void DAPI ShelFunctionOverride(
43 __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW
44 )
45 {
46 vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW;
47 }
48
49
50 /********************************************************************
51 ShelExec() - executes a target.
52
53 *******************************************************************/
54 extern "C" HRESULT DAPI ShelExec(
55 __in_z LPCWSTR wzTargetPath,
56 __in_z_opt LPCWSTR wzParameters,
57 __in_z_opt LPCWSTR wzVerb,
58 __in_z_opt LPCWSTR wzWorkingDirectory,
59 __in int nShowCmd,
60 __in_opt HWND hwndParent,
61 __out_opt HANDLE* phProcess
62 )
63 {
64 HRESULT hr = S_OK;
65 SHELLEXECUTEINFOW shExecInfo = { };
66 size_t cchWorkingDirectory = 0;
67
68 // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
69 if (wzWorkingDirectory && FAILED(::StringCchLengthW(wzWorkingDirectory, MAX_PATH - 1, &cchWorkingDirectory)))
70 {
71 wzWorkingDirectory = NULL;
72 }
73
74 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
75 shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
76 shExecInfo.hwnd = hwndParent;
77 shExecInfo.lpVerb = wzVerb;
78 shExecInfo.lpFile = wzTargetPath;
79 shExecInfo.lpParameters = wzParameters;
80 shExecInfo.lpDirectory = wzWorkingDirectory;
81 shExecInfo.nShow = nShowCmd;
82
83 if (!vpfnShellExecuteExW(&shExecInfo))
84 {
85 ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er);
86 }
87
88 if (phProcess)
89 {
90 *phProcess = shExecInfo.hProcess;
91 shExecInfo.hProcess = NULL;
92 }
93
94 LExit:
95 ReleaseHandle(shExecInfo.hProcess);
96
97 return hr;
98 }
99
100
101 /********************************************************************
102 ShelExecUnelevated() - executes a target unelevated.
103
104 *******************************************************************/
105 extern "C" HRESULT DAPI ShelExecUnelevated(
106 __in_z LPCWSTR wzTargetPath,
107 __in_z_opt LPCWSTR wzParameters,
108 __in_z_opt LPCWSTR wzVerb,
109 __in_z_opt LPCWSTR wzWorkingDirectory,
110 __in int nShowCmd
111 )
112 {
113 HRESULT hr = S_OK;
114 BSTR bstrTargetPath = NULL;
115 VARIANT vtParameters = { };
116 VARIANT vtVerb = { };
117 VARIANT vtWorkingDirectory = { };
118 VARIANT vtShow = { };
119 IShellView* psv = NULL;
120 IShellDispatch2* psd = NULL;
121
122 bstrTargetPath = ::SysAllocString(wzTargetPath);
123 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR.");
124
125 if (wzParameters && *wzParameters)
126 {
127 vtParameters.vt = VT_BSTR;
128 vtParameters.bstrVal = ::SysAllocString(wzParameters);
129 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR.");
130 }
131
132 if (wzVerb && *wzVerb)
133 {
134 vtVerb.vt = VT_BSTR;
135 vtVerb.bstrVal = ::SysAllocString(wzVerb);
136 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR.");
137 }
138
139 if (wzWorkingDirectory && *wzWorkingDirectory)
140 {
141 vtWorkingDirectory.vt = VT_BSTR;
142 vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory);
143 ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR.");
144 }
145
146 vtShow.vt = VT_INT;
147 vtShow.intVal = nShowCmd;
148
149 hr = GetDesktopShellView(IID_PPV_ARGS(&psv));
150 ShelExitOnFailure(hr, "Failed to get desktop shell view.");
151
152 hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd));
153 ShelExitOnFailure(hr, "Failed to get shell dispatch from view.");
154
155 hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow);
156 if (S_FALSE == hr)
157 {
158 hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
159 }
160 ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath);
161
162 LExit:
163 ReleaseObject(psd);
164 ReleaseObject(psv);
165 ReleaseBSTR(vtWorkingDirectory.bstrVal);
166 ReleaseBSTR(vtVerb.bstrVal);
167 ReleaseBSTR(vtParameters.bstrVal);
168 ReleaseBSTR(bstrTargetPath);
169
170 return hr;
171 }
172
173
174 static HRESULT DAPI GetFolderFromCsidl(
175 __out_z LPWSTR* psczFolderPath,
176 __in int csidlFolder
177 )
178 {
179 HRESULT hr = S_OK;
180 WCHAR wzPath[MAX_PATH];
181
182 hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath);
183 ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder);
184
185 hr = StrAllocString(psczFolderPath, wzPath, 0);
186 ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath);
187
188 hr = PathBackslashTerminate(psczFolderPath);
189 ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath);
190
191 LExit:
192 return hr;
193 }
194
195
196 EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
197 REFKNOWNFOLDERID rfid,
198 DWORD dwFlags,
199 HANDLE hToken,
200 PWSTR *ppszPath
201 );
202
203 extern "C" HRESULT DAPI ShelGetKnownFolder(
204 __out_z LPWSTR* psczFolderPath,
205 __in REFKNOWNFOLDERID rfidFolder
206 )
207 {
208 HRESULT hr = S_OK;
209 HMODULE hShell32Dll = NULL;
210 PFN_SHGetKnownFolderPath pfn = NULL;
211 LPWSTR pwzPath = NULL;
212
213 hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll);
214 if (E_MODNOTFOUND == hr)
215 {
216 TraceError(hr, "Failed to load shell32.dll");
217 ExitFunction1(hr = E_NOTIMPL);
218 }
219 ShelExitOnFailure(hr, "Failed to load shell32.dll.");
220
221 pfn = reinterpret_cast<PFN_SHGetKnownFolderPath>(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath"));
222 ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point.");
223
224 hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath);
225 ShelExitOnFailure(hr, "Failed to get known folder path.");
226
227 hr = StrAllocString(psczFolderPath, pwzPath, 0);
228 ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath);
229
230 hr = PathBackslashTerminate(psczFolderPath);
231 ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath);
232
233 LExit:
234 if (pwzPath)
235 {
236 ::CoTaskMemFree(pwzPath);
237 }
238
239 if (hShell32Dll)
240 {
241 ::FreeLibrary(hShell32Dll);
242 }
243
244 return hr;
245 }
246
247 extern "C" HRESULT DAPI ShelGetFolder(
248 __out_z LPWSTR* psczFolderPath,
249 __in int csidlFolder
250 )
251 {
252 HRESULT hr = S_OK;
253 LPWSTR sczPath = NULL;
254 KNOWNFOLDERID rfid = { };
255
256 csidlFolder &= ~CSIDL_FLAG_MASK;
257
258 switch (csidlFolder)
259 {
260 case CSIDL_ADMINTOOLS:
261 rfid = FOLDERID_AdminTools;
262 break;
263 case CSIDL_APPDATA:
264 rfid = FOLDERID_RoamingAppData;
265 break;
266 case CSIDL_CDBURN_AREA:
267 rfid = FOLDERID_CDBurning;
268 break;
269 case CSIDL_COMMON_ADMINTOOLS:
270 rfid = FOLDERID_CommonAdminTools;
271 break;
272 case CSIDL_COMMON_APPDATA:
273 rfid = FOLDERID_ProgramData;
274 break;
275 case CSIDL_COMMON_DESKTOPDIRECTORY:
276 rfid = FOLDERID_PublicDesktop;
277 break;
278 case CSIDL_COMMON_DOCUMENTS:
279 rfid = FOLDERID_PublicDocuments;
280 break;
281 case CSIDL_COMMON_MUSIC:
282 rfid = FOLDERID_PublicMusic;
283 break;
284 case CSIDL_COMMON_OEM_LINKS:
285 rfid = FOLDERID_CommonOEMLinks;
286 break;
287 case CSIDL_COMMON_PICTURES:
288 rfid = FOLDERID_PublicPictures;
289 break;
290 case CSIDL_COMMON_PROGRAMS:
291 rfid = FOLDERID_CommonPrograms;
292 break;
293 case CSIDL_COMMON_STARTMENU:
294 rfid = FOLDERID_CommonStartMenu;
295 break;
296 case CSIDL_COMMON_STARTUP: __fallthrough;
297 case CSIDL_COMMON_ALTSTARTUP:
298 rfid = FOLDERID_CommonStartup;
299 break;
300 case CSIDL_COMMON_TEMPLATES:
301 rfid = FOLDERID_CommonTemplates;
302 break;
303 case CSIDL_COMMON_VIDEO:
304 rfid = FOLDERID_PublicVideos;
305 break;
306 case CSIDL_COOKIES:
307 rfid = FOLDERID_Cookies;
308 break;
309 case CSIDL_DESKTOP:
310 case CSIDL_DESKTOPDIRECTORY:
311 rfid = FOLDERID_Desktop;
312 break;
313 case CSIDL_FAVORITES: __fallthrough;
314 case CSIDL_COMMON_FAVORITES:
315 rfid = FOLDERID_Favorites;
316 break;
317 case CSIDL_FONTS:
318 rfid = FOLDERID_Fonts;
319 break;
320 case CSIDL_HISTORY:
321 rfid = FOLDERID_History;
322 break;
323 case CSIDL_INTERNET_CACHE:
324 rfid = FOLDERID_InternetCache;
325 break;
326 case CSIDL_LOCAL_APPDATA:
327 rfid = FOLDERID_LocalAppData;
328 break;
329 case CSIDL_MYMUSIC:
330 rfid = FOLDERID_Music;
331 break;
332 case CSIDL_MYPICTURES:
333 rfid = FOLDERID_Pictures;
334 break;
335 case CSIDL_MYVIDEO:
336 rfid = FOLDERID_Videos;
337 break;
338 case CSIDL_NETHOOD:
339 rfid = FOLDERID_NetHood;
340 break;
341 case CSIDL_PERSONAL:
342 rfid = FOLDERID_Documents;
343 break;
344 case CSIDL_PRINTHOOD:
345 rfid = FOLDERID_PrintHood;
346 break;
347 case CSIDL_PROFILE:
348 rfid = FOLDERID_Profile;
349 break;
350 case CSIDL_PROGRAM_FILES:
351 rfid = FOLDERID_ProgramFiles;
352 break;
353 case CSIDL_PROGRAM_FILESX86:
354 rfid = FOLDERID_ProgramFilesX86;
355 break;
356 case CSIDL_PROGRAM_FILES_COMMON:
357 rfid = FOLDERID_ProgramFilesCommon;
358 break;
359 case CSIDL_PROGRAM_FILES_COMMONX86:
360 rfid = FOLDERID_ProgramFilesCommonX86;
361 break;
362 case CSIDL_PROGRAMS:
363 rfid = FOLDERID_Programs;
364 break;
365 case CSIDL_RECENT:
366 rfid = FOLDERID_Recent;
367 break;
368 case CSIDL_RESOURCES:
369 rfid = FOLDERID_ResourceDir;
370 break;
371 case CSIDL_RESOURCES_LOCALIZED:
372 rfid = FOLDERID_LocalizedResourcesDir;
373 break;
374 case CSIDL_SENDTO:
375 rfid = FOLDERID_SendTo;
376 break;
377 case CSIDL_STARTMENU:
378 rfid = FOLDERID_StartMenu;
379 break;
380 case CSIDL_STARTUP:
381 case CSIDL_ALTSTARTUP:
382 rfid = FOLDERID_Startup;
383 break;
384 case CSIDL_SYSTEM:
385 rfid = FOLDERID_System;
386 break;
387 case CSIDL_SYSTEMX86:
388 rfid = FOLDERID_SystemX86;
389 break;
390 case CSIDL_TEMPLATES:
391 rfid = FOLDERID_Templates;
392 break;
393 case CSIDL_WINDOWS:
394 rfid = FOLDERID_Windows;
395 break;
396 default:
397 ShelExitWithRootFailure(hr, E_INVALIDARG, "Unknown csidl: %d", csidlFolder);
398 }
399
400 hr = ShelGetKnownFolder(&sczPath, rfid);
401 if (E_NOTIMPL == hr)
402 {
403 hr = S_FALSE;
404 }
405 ShelExitOnFailure(hr, "Failed to get known folder.");
406
407 if (S_FALSE == hr)
408 {
409 hr = GetFolderFromCsidl(&sczPath, csidlFolder);
410 ShelExitOnFailure(hr, "Failed to get csidl folder.");
411 }
412
413 *psczFolderPath = sczPath;
414 sczPath = NULL;
415
416 LExit:
417 ReleaseStr(sczPath);
418
419 return hr;
420 }
421
422
423 // Internal functions.
424
425 static HRESULT GetDesktopShellView(
426 __in REFIID riid,
427 __out void **ppv
428 )
429 {
430 HRESULT hr = S_OK;
431 IShellWindows* psw = NULL;
432 HWND hwnd = NULL;
433 IDispatch* pdisp = NULL;
434 VARIANT vEmpty = {}; // VT_EMPTY
435 IShellBrowser* psb = NULL;
436 IShellFolder* psf = NULL;
437 IShellView* psv = NULL;
438
439 // use the shell view for the desktop using the shell windows automation to find the
440 // desktop web browser and then grabs its view
441 // returns IShellView, IFolderView and related interfaces
442 hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
443 ShelExitOnFailure(hr, "Failed to get shell view.");
444
445 hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp);
446 if (S_OK == hr)
447 {
448 hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
449 ShelExitOnFailure(hr, "Failed to get desktop window.");
450
451 hr = psb->QueryActiveShellView(&psv);
452 ShelExitOnFailure(hr, "Failed to get active shell view.");
453
454 hr = psv->QueryInterface(riid, ppv);
455 ShelExitOnFailure(hr, "Failed to query for the desktop shell view.");
456 }
457 else if (S_FALSE == hr)
458 {
459 //Windows XP
460 hr = ::SHGetDesktopFolder(&psf);
461 ShelExitOnFailure(hr, "Failed to get desktop folder.");
462
463 hr = psf->CreateViewObject(NULL, IID_IShellView, ppv);
464 ShelExitOnFailure(hr, "Failed to query for the desktop shell view.");
465 }
466 else
467 {
468 ShelExitOnFailure(hr, "Failed to get desktop window.");
469 }
470
471 LExit:
472 ReleaseObject(psv);
473 ReleaseObject(psb);
474 ReleaseObject(psf);
475 ReleaseObject(pdisp);
476 ReleaseObject(psw);
477
478 return hr;
479 }
480
481 static HRESULT GetShellDispatchFromView(
482 __in IShellView *psv,
483 __in REFIID riid,
484 __out void **ppv
485 )
486 {
487 HRESULT hr = S_OK;
488 IDispatch *pdispBackground = NULL;
489 IShellFolderViewDual *psfvd = NULL;
490 IDispatch *pdisp = NULL;
491
492 // From a shell view object, gets its automation interface and from that get the shell
493 // application object that implements IShellDispatch2 and related interfaces.
494 hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
495 ShelExitOnFailure(hr, "Failed to get the automation interface for shell.");
496
497 hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
498 ShelExitOnFailure(hr, "Failed to get shell folder view dual.");
499
500 hr = psfvd->get_Application(&pdisp);
501 ShelExitOnFailure(hr, "Failed to application object.");
502
503 hr = pdisp->QueryInterface(riid, ppv);
504 ShelExitOnFailure(hr, "Failed to get IShellDispatch2.");
505
506 LExit:
507 ReleaseObject(pdisp);
508 ReleaseObject(psfvd);
509 ReleaseObject(pdispBackground);
510
511 return hr;
512 }