From 3f583916719eeef598d10a5d4e14ef14f008243b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 11 May 2021 07:36:37 -0700 Subject: Merge Dtf --- src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp | 281 +++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp (limited to 'src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp') diff --git a/src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp b/src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp new file mode 100644 index 00000000..a49cdeec --- /dev/null +++ b/src/samples/Dtf/Tools/SfxCA/EmbeddedUI.cpp @@ -0,0 +1,281 @@ +// 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" +#include "SfxUtil.h" + +// Globals for keeping track of things across UI messages. +static const wchar_t* g_szWorkingDir; +static ICorRuntimeHost* g_pClrHost; +static _AppDomain* g_pAppDomain; +static _MethodInfo* g_pProcessMessageMethod; +static _MethodInfo* g_pShutdownMethod; + +// Reserve extra space for strings to be replaced at build time. +#define NULLSPACE \ +L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + +// Prototypes for local functions. +// See the function definitions for comments. + +bool InvokeInitializeMethod(_MethodInfo* pInitMethod, MSIHANDLE hSession, + const wchar_t* szClassName, LPDWORD pdwInternalUILevel, UINT* puiResult); + +/// +/// First entry-point for the UI DLL when loaded and called by MSI. +/// Extracts the payload, hosts the CLR, and invokes the managed +/// initialize method. +/// +/// Handle to the installer session, +/// used for logging errors and to be passed on to the managed initialize method. +/// Path the directory where resources from the MsiEmbeddedUI table +/// have been extracted, and where additional payload from this package will be extracted. +/// MSI install UI level passed to and returned from +/// the managed initialize method. +extern "C" +UINT __stdcall InitializeEmbeddedUI(MSIHANDLE hSession, LPCWSTR szResourcePath, LPDWORD pdwInternalUILevel) +{ + // If the managed initialize method cannot be called, continue the installation in BASIC UI mode. + UINT uiResult = INSTALLUILEVEL_BASIC; + + const wchar_t* szClassName = L"InitializeEmbeddedUI_FullClassName" NULLSPACE; + + g_szWorkingDir = szResourcePath; + + wchar_t szModule[MAX_PATH]; + DWORD cchCopied = GetModuleFileName(g_hModule, szModule, MAX_PATH - 1); + if (cchCopied == 0) + { + Log(hSession, L"Failed to get module path. Error code %d.", GetLastError()); + return uiResult; + } + else if (cchCopied == MAX_PATH - 1) + { + Log(hSession, L"Failed to get module path -- path is too long."); + return uiResult; + } + + Log(hSession, L"Extracting embedded UI to temporary directory: %s", g_szWorkingDir); + int err = ExtractCabinet(szModule, g_szWorkingDir); + if (err != 0) + { + Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err); + Log(hSession, L"Ensure that no MsiEmbeddedUI.FileName values are the same as " + L"any file contained in the embedded UI package."); + return uiResult; + } + + wchar_t szConfigFilePath[MAX_PATH + 20]; + StringCchCopy(szConfigFilePath, MAX_PATH + 20, g_szWorkingDir); + StringCchCat(szConfigFilePath, MAX_PATH + 20, L"\\EmbeddedUI.config"); + + const wchar_t* szConfigFile = szConfigFilePath; + if (!PathFileExists(szConfigFilePath)) + { + szConfigFile = NULL; + } + + wchar_t szWIAssembly[MAX_PATH + 50]; + StringCchCopy(szWIAssembly, MAX_PATH + 50, g_szWorkingDir); + StringCchCat(szWIAssembly, MAX_PATH + 50, L"\\WixToolset.Dtf.WindowsInstaller.dll"); + + if (LoadCLR(hSession, NULL, szConfigFile, szWIAssembly, &g_pClrHost)) + { + if (CreateAppDomain(hSession, g_pClrHost, L"EmbeddedUI", g_szWorkingDir, + szConfigFile, &g_pAppDomain)) + { + const wchar_t* szMsiAssemblyName = L"WixToolset.Dtf.WindowsInstaller"; + const wchar_t* szProxyClass = L"WixToolset.Dtf.WindowsInstaller.EmbeddedUIProxy"; + const wchar_t* szInitMethod = L"Initialize"; + const wchar_t* szProcessMessageMethod = L"ProcessMessage"; + const wchar_t* szShutdownMethod = L"Shutdown"; + + if (GetMethod(hSession, g_pAppDomain, szMsiAssemblyName, + szProxyClass, szProcessMessageMethod, &g_pProcessMessageMethod) && + GetMethod(hSession, g_pAppDomain, szMsiAssemblyName, + szProxyClass, szShutdownMethod, &g_pShutdownMethod)) + { + _MethodInfo* pInitMethod; + if (GetMethod(hSession, g_pAppDomain, szMsiAssemblyName, + szProxyClass, szInitMethod, &pInitMethod)) + { + bool invokeSuccess = InvokeInitializeMethod(pInitMethod, hSession, szClassName, pdwInternalUILevel, &uiResult); + pInitMethod->Release(); + if (invokeSuccess) + { + if (uiResult == 0) + { + return ERROR_SUCCESS; + } + else if (uiResult == ERROR_INSTALL_USEREXIT) + { + // InitializeEmbeddedUI is not allowed to return ERROR_INSTALL_USEREXIT. + // So return success here and then IDCANCEL on the next progress message. + uiResult = 0; + *pdwInternalUILevel = INSTALLUILEVEL_NONE; + Log(hSession, L"Initialization canceled by user."); + } + } + } + } + + g_pProcessMessageMethod->Release(); + g_pProcessMessageMethod = NULL; + g_pShutdownMethod->Release(); + g_pShutdownMethod = NULL; + + g_pClrHost->UnloadDomain(g_pAppDomain); + g_pAppDomain->Release(); + g_pAppDomain = NULL; + } + g_pClrHost->Stop(); + g_pClrHost->Release(); + g_pClrHost = NULL; + } + + return uiResult; +} + +/// +/// Entry-point for UI progress messages received from the MSI engine during an active installation. +/// Forwards the progress messages to the managed handler method and returns its result. +/// +extern "C" +INT __stdcall EmbeddedUIHandler(UINT uiMessageType, MSIHANDLE hRecord) +{ + if (g_pProcessMessageMethod == NULL) + { + // Initialization was canceled. + return IDCANCEL; + } + + VARIANT vResult; + VariantInit(&vResult); + + VARIANT vNull; + vNull.vt = VT_EMPTY; + + SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 2); + VARIANT vMessageType; + vMessageType.vt = VT_I4; + vMessageType.lVal = (LONG) uiMessageType; + LONG index = 0; + HRESULT hr = SafeArrayPutElement(saArgs, &index, &vMessageType); + if (FAILED(hr)) goto LExit; + VARIANT vRecord; + vRecord.vt = VT_I4; + vRecord.lVal = (LONG) hRecord; + index = 1; + hr = SafeArrayPutElement(saArgs, &index, &vRecord); + if (FAILED(hr)) goto LExit; + + hr = g_pProcessMessageMethod->Invoke_3(vNull, saArgs, &vResult); + +LExit: + SafeArrayDestroy(saArgs); + if (SUCCEEDED(hr)) + { + return vResult.intVal; + } + else + { + return -1; + } +} + +/// +/// Entry-point for the UI shutdown message received from the MSI engine after installation has completed. +/// Forwards the shutdown message to the managed shutdown method, then shuts down the CLR. +/// +extern "C" +DWORD __stdcall ShutdownEmbeddedUI() +{ + if (g_pShutdownMethod != NULL) + { + VARIANT vNull; + vNull.vt = VT_EMPTY; + SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0); + g_pShutdownMethod->Invoke_3(vNull, saArgs, NULL); + SafeArrayDestroy(saArgs); + + g_pClrHost->UnloadDomain(g_pAppDomain); + g_pAppDomain->Release(); + g_pClrHost->Stop(); + g_pClrHost->Release(); + } + + return 0; +} + +/// +/// Loads and invokes the managed portion of the proxy. +/// +/// Managed initialize method to be invoked. +/// Handle to the installer session, +/// used for logging errors and to be passed on to the managed initialize method. +/// Name of the UI class to be loaded. +/// This must be of the form: AssemblyName!Namespace.Class +/// MSI install UI level passed to and returned from +/// the managed initialize method. +/// Return value of the invoked initialize method. +/// True if the managed proxy was invoked successfully, or an +/// error code if there was some error. Note the initialize method itself may +/// return an error via puiResult while this method still returns true +/// since the invocation was successful. +bool InvokeInitializeMethod(_MethodInfo* pInitMethod, MSIHANDLE hSession, const wchar_t* szClassName, LPDWORD pdwInternalUILevel, UINT* puiResult) +{ + VARIANT vResult; + VariantInit(&vResult); + + VARIANT vNull; + vNull.vt = VT_EMPTY; + + SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3); + VARIANT vSessionHandle; + vSessionHandle.vt = VT_I4; + vSessionHandle.lVal = (LONG) hSession; + LONG index = 0; + HRESULT hr = SafeArrayPutElement(saArgs, &index, &vSessionHandle); + if (FAILED(hr)) goto LExit; + VARIANT vEntryPoint; + vEntryPoint.vt = VT_BSTR; + vEntryPoint.bstrVal = SysAllocString(szClassName); + if (vEntryPoint.bstrVal == NULL) + { + hr = E_OUTOFMEMORY; + goto LExit; + } + index = 1; + hr = SafeArrayPutElement(saArgs, &index, &vEntryPoint); + if (FAILED(hr)) goto LExit; + VARIANT vUILevel; + vUILevel.vt = VT_I4; + vUILevel.ulVal = *pdwInternalUILevel; + index = 2; + hr = SafeArrayPutElement(saArgs, &index, &vUILevel); + if (FAILED(hr)) goto LExit; + + hr = pInitMethod->Invoke_3(vNull, saArgs, &vResult); + +LExit: + SafeArrayDestroy(saArgs); + if (SUCCEEDED(hr)) + { + *puiResult = (UINT) vResult.lVal; + if ((*puiResult & 0xFFFF) == 0) + { + // Due to interop limitations, the successful resulting UILevel is returned + // as the high-word of the return value instead of via a ref parameter. + *pdwInternalUILevel = *puiResult >> 16; + *puiResult = 0; + } + return true; + } + else + { + Log(hSession, L"Failed to invoke EmbeddedUI Initialize method. Error code 0x%X", hr); + return false; + } +} -- cgit v1.2.3-55-g6feb