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 | } | ||
