From 7f642e51670bc38a4ef782a363936850bc2b0ba9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:38:23 -0700 Subject: Move dutil into libs/dutil --- src/libs/dutil/WixToolset.DUtil/shelutil.cpp | 342 +++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 src/libs/dutil/WixToolset.DUtil/shelutil.cpp (limited to 'src/libs/dutil/WixToolset.DUtil/shelutil.cpp') 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 @@ +// 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. + +#include "precomp.h" + + +// Exit macros +#define ShelExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) +#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) +#define ShelExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) +#define ShelExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_SHELUTIL, e, x, s, __VA_ARGS__) +#define ShelExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SHELUTIL, g, x, s, __VA_ARGS__) + +static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ); +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ); + +/******************************************************************** + ShelFunctionOverride - overrides the shell functions. Typically used + for unit testing. + +*********************************************************************/ +extern "C" void DAPI ShelFunctionOverride( + __in_opt PFN_SHELLEXECUTEEXW pfnShellExecuteExW + ) +{ + vpfnShellExecuteExW = pfnShellExecuteExW ? pfnShellExecuteExW : ::ShellExecuteExW; +} + + +/******************************************************************** + ShelExec() - executes a target. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExec( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd, + __in_opt HWND hwndParent, + __out_opt HANDLE* phProcess + ) +{ + HRESULT hr = S_OK; + SHELLEXECUTEINFOW shExecInfo = {}; + + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; + shExecInfo.hwnd = hwndParent; + shExecInfo.lpVerb = wzVerb; + shExecInfo.lpFile = wzTargetPath; + shExecInfo.lpParameters = wzParameters; + shExecInfo.lpDirectory = wzWorkingDirectory; + shExecInfo.nShow = nShowCmd; + + if (!vpfnShellExecuteExW(&shExecInfo)) + { + ShelExitWithLastError(hr, "ShellExecEx failed with return code: %d", Dutil_er); + } + + if (phProcess) + { + *phProcess = shExecInfo.hProcess; + shExecInfo.hProcess = NULL; + } + +LExit: + ReleaseHandle(shExecInfo.hProcess); + + return hr; +} + + +/******************************************************************** + ShelExecUnelevated() - executes a target unelevated. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelExecUnelevated( + __in_z LPCWSTR wzTargetPath, + __in_z_opt LPCWSTR wzParameters, + __in_z_opt LPCWSTR wzVerb, + __in_z_opt LPCWSTR wzWorkingDirectory, + __in int nShowCmd + ) +{ + HRESULT hr = S_OK; + BSTR bstrTargetPath = NULL; + VARIANT vtParameters = { }; + VARIANT vtVerb = { }; + VARIANT vtWorkingDirectory = { }; + VARIANT vtShow = { }; + IShellView* psv = NULL; + IShellDispatch2* psd = NULL; + + bstrTargetPath = ::SysAllocString(wzTargetPath); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); + + if (wzParameters && *wzParameters) + { + vtParameters.vt = VT_BSTR; + vtParameters.bstrVal = ::SysAllocString(wzParameters); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); + } + + if (wzVerb && *wzVerb) + { + vtVerb.vt = VT_BSTR; + vtVerb.bstrVal = ::SysAllocString(wzVerb); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); + } + + if (wzWorkingDirectory && *wzWorkingDirectory) + { + vtWorkingDirectory.vt = VT_BSTR; + vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); + ShelExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); + } + + vtShow.vt = VT_INT; + vtShow.intVal = nShowCmd; + + hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); + ShelExitOnFailure(hr, "Failed to get desktop shell view."); + + hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); + ShelExitOnFailure(hr, "Failed to get shell dispatch from view."); + + hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); + if (S_FALSE == hr) + { + hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); + } + ShelExitOnRootFailure(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); + +LExit: + ReleaseObject(psd); + ReleaseObject(psv); + ReleaseBSTR(vtWorkingDirectory.bstrVal); + ReleaseBSTR(vtVerb.bstrVal); + ReleaseBSTR(vtParameters.bstrVal); + ReleaseBSTR(bstrTargetPath); + + return hr; +} + + +/******************************************************************** + ShelGetFolder() - gets a folder by CSIDL. + +*******************************************************************/ +extern "C" HRESULT DAPI ShelGetFolder( + __out_z LPWSTR* psczFolderPath, + __in int csidlFolder + ) +{ + HRESULT hr = S_OK; + WCHAR wzPath[MAX_PATH]; + + hr = ::SHGetFolderPathW(NULL, csidlFolder | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wzPath); + ShelExitOnFailure(hr, "Failed to get folder path for CSIDL: %d", csidlFolder); + + hr = StrAllocString(psczFolderPath, wzPath, 0); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", wzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + return hr; +} + + +/******************************************************************** + ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID. + + Note: return E_NOTIMPL if called on pre-Vista operating systems. +*******************************************************************/ +#ifndef REFKNOWNFOLDERID +#define REFKNOWNFOLDERID REFGUID +#endif + +#ifndef KF_FLAG_CREATE +#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition +#endif + +EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( + REFKNOWNFOLDERID rfid, + DWORD dwFlags, + HANDLE hToken, + PWSTR *ppszPath + ); + +extern "C" HRESULT DAPI ShelGetKnownFolder( + __out_z LPWSTR* psczFolderPath, + __in REFKNOWNFOLDERID rfidFolder + ) +{ + HRESULT hr = S_OK; + HMODULE hShell32Dll = NULL; + PFN_SHGetKnownFolderPath pfn = NULL; + LPWSTR pwzPath = NULL; + + hr = LoadSystemLibrary(L"shell32.dll", &hShell32Dll); + if (E_MODNOTFOUND == hr) + { + TraceError(hr, "Failed to load shell32.dll"); + ExitFunction1(hr = E_NOTIMPL); + } + ShelExitOnFailure(hr, "Failed to load shell32.dll."); + + pfn = reinterpret_cast(::GetProcAddress(hShell32Dll, "SHGetKnownFolderPath")); + ShelExitOnNull(pfn, hr, E_NOTIMPL, "Failed to find SHGetKnownFolderPath entry point."); + + hr = pfn(rfidFolder, KF_FLAG_CREATE, NULL, &pwzPath); + ShelExitOnFailure(hr, "Failed to get known folder path."); + + hr = StrAllocString(psczFolderPath, pwzPath, 0); + ShelExitOnFailure(hr, "Failed to copy shell folder path: %ls", pwzPath); + + hr = PathBackslashTerminate(psczFolderPath); + ShelExitOnFailure(hr, "Failed to backslash terminate shell folder path: %ls", *psczFolderPath); + +LExit: + if (pwzPath) + { + ::CoTaskMemFree(pwzPath); + } + + if (hShell32Dll) + { + ::FreeLibrary(hShell32Dll); + } + + return hr; +} + + +// Internal functions. + +static HRESULT GetDesktopShellView( + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IShellWindows* psw = NULL; + HWND hwnd = NULL; + IDispatch* pdisp = NULL; + VARIANT vEmpty = {}; // VT_EMPTY + IShellBrowser* psb = NULL; + IShellFolder* psf = NULL; + IShellView* psv = NULL; + + // use the shell view for the desktop using the shell windows automation to find the + // desktop web browser and then grabs its view + // returns IShellView, IFolderView and related interfaces + hr = ::CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); + ShelExitOnFailure(hr, "Failed to get shell view."); + + hr = psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp); + if (S_OK == hr) + { + hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); + ShelExitOnFailure(hr, "Failed to get desktop window."); + + hr = psb->QueryActiveShellView(&psv); + ShelExitOnFailure(hr, "Failed to get active shell view."); + + hr = psv->QueryInterface(riid, ppv); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else if (S_FALSE == hr) + { + //Windows XP + hr = SHGetDesktopFolder(&psf); + ShelExitOnFailure(hr, "Failed to get desktop folder."); + + hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); + ShelExitOnFailure(hr, "Failed to query for the desktop shell view."); + } + else + { + ShelExitOnFailure(hr, "Failed to get desktop window."); + } + +LExit: + ReleaseObject(psv); + ReleaseObject(psb); + ReleaseObject(psf); + ReleaseObject(pdisp); + ReleaseObject(psw); + + return hr; +} + +static HRESULT GetShellDispatchFromView( + __in IShellView *psv, + __in REFIID riid, + __out void **ppv + ) +{ + HRESULT hr = S_OK; + IDispatch *pdispBackground = NULL; + IShellFolderViewDual *psfvd = NULL; + IDispatch *pdisp = NULL; + + // From a shell view object, gets its automation interface and from that get the shell + // application object that implements IShellDispatch2 and related interfaces. + hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); + ShelExitOnFailure(hr, "Failed to get the automation interface for shell."); + + hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); + ShelExitOnFailure(hr, "Failed to get shell folder view dual."); + + hr = psfvd->get_Application(&pdisp); + ShelExitOnFailure(hr, "Failed to application object."); + + hr = pdisp->QueryInterface(riid, ppv); + ShelExitOnFailure(hr, "Failed to get IShellDispatch2."); + +LExit: + ReleaseObject(pdisp); + ReleaseObject(psfvd); + ReleaseObject(pdispBackground); + + return hr; +} -- cgit v1.2.3-55-g6feb