// 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" void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...); //--------------------------------------------------------------------- // CLR HOSTING //--------------------------------------------------------------------- /// /// Binds to the CLR after determining the appropriate version. /// /// Handle to the installer session, /// used just for logging. /// Specific version of the CLR to load. /// If null, then the config file and/or primary assembly are /// used to determine the version. /// XML .config file which may contain /// a startup section to direct which version of the CLR to use. /// May be NULL. /// Assembly to be used to determine /// the version of the CLR in the absence of other configuration. /// May be NULL. /// Returned runtime host interface. /// True if the CLR was loaded successfully, false if /// there was some error. /// /// If szPrimaryAssembly is NULL and szConfigFile is also NULL or /// does not contain any version configuration, the CLR will not be loaded. /// bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile, const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost) { typedef HRESULT (__stdcall *PGetRequestedRuntimeInfo)(LPCWSTR pExe, LPCWSTR pwszVersion, LPCWSTR pConfigurationFile, DWORD startupFlags, DWORD runtimeInfoFlags, LPWSTR pDirectory, DWORD dwDirectory, DWORD *dwDirectoryLength, LPWSTR pVersion, DWORD cchBuffer, DWORD* dwlength); typedef HRESULT (__stdcall *PCorBindToRuntimeEx)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor, DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv); HMODULE hmodMscoree = LoadLibrary(L"mscoree.dll"); if (hmodMscoree == NULL) { Log(hSession, L"Failed to load mscoree.dll (Error code %d). This custom action " L"requires the .NET Framework to be installed.", GetLastError()); return false; } PGetRequestedRuntimeInfo pGetRequestedRuntimeInfo = (PGetRequestedRuntimeInfo) GetProcAddress(hmodMscoree, "GetRequestedRuntimeInfo"); PCorBindToRuntimeEx pCorBindToRuntimeEx = (PCorBindToRuntimeEx) GetProcAddress(hmodMscoree, "CorBindToRuntimeEx"); if (pGetRequestedRuntimeInfo == NULL || pCorBindToRuntimeEx == NULL) { Log(hSession, L"Failed to locate functions in mscoree.dll (Error code %d). This custom action " L"requires the .NET Framework to be installed.", GetLastError()); FreeLibrary(hmodMscoree); return false; } wchar_t szClrVersion[20]; HRESULT hr; if (szVersion != NULL && szVersion[0] != L'\0') { wcsncpy_s(szClrVersion, 20, szVersion, 20); } else { wchar_t szVersionDir[MAX_PATH]; hr = pGetRequestedRuntimeInfo(szPrimaryAssembly, NULL, szConfigFile, 0, 0, szVersionDir, MAX_PATH, NULL, szClrVersion, 20, NULL); if (FAILED(hr)) { Log(hSession, L"Failed to get requested CLR info. Error code 0x%x", hr); Log(hSession, L"Ensure that the proper version of the .NET Framework is installed, or " L"that there is a matching supportedRuntime element in CustomAction.config. " L"If you are binding to .NET 4 or greater add " L"useLegacyV2RuntimeActivationPolicy=true to the element."); FreeLibrary(hmodMscoree); return false; } } Log(hSession, L"Binding to CLR version %s", szClrVersion); ICorRuntimeHost* pHost; hr = pCorBindToRuntimeEx(szClrVersion, NULL, STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN, CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void**) &pHost); if (FAILED(hr)) { Log(hSession, L"Failed to bind to the CLR. Error code 0x%X", hr); FreeLibrary(hmodMscoree); return false; } hr = pHost->Start(); if (FAILED(hr)) { Log(hSession, L"Failed to start the CLR. Error code 0x%X", hr); pHost->Release(); FreeLibrary(hmodMscoree); return false; } *ppHost = pHost; FreeLibrary(hmodMscoree); return true; } /// /// Creates a new CLR application domain. /// /// Handle to the installer session, /// used just for logging /// Interface to the runtime host where the /// app domain will be created. /// Name of the app domain to create. /// Application base directory path, where /// the app domain will look first to load its assemblies. /// Optional XML .config file containing any /// configuration for thae app domain. /// Returned app domain interface. /// True if the app domain was created successfully, false if /// there was some error. bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost, const wchar_t* szName, const wchar_t* szAppBase, const wchar_t* szConfigFile, _AppDomain** ppAppDomain) { IUnknown* punkAppDomainSetup = NULL; IAppDomainSetup* pAppDomainSetup = NULL; HRESULT hr = pHost->CreateDomainSetup(&punkAppDomainSetup); if (SUCCEEDED(hr)) { hr = punkAppDomainSetup->QueryInterface(__uuidof(IAppDomainSetup), (void**) &pAppDomainSetup); punkAppDomainSetup->Release(); } if (FAILED(hr)) { Log(hSession, L"Failed to create app domain setup. Error code 0x%X", hr); return false; } const wchar_t* szUrlPrefix = L"file:///"; size_t cchApplicationBase = wcslen(szUrlPrefix) + wcslen(szAppBase); wchar_t* szApplicationBase = (wchar_t*) _alloca((cchApplicationBase + 1) * sizeof(wchar_t)); if (szApplicationBase == NULL) hr = E_OUTOFMEMORY; else { StringCchCopy(szApplicationBase, cchApplicationBase + 1, szUrlPrefix); StringCchCat(szApplicationBase, cchApplicationBase + 1, szAppBase); BSTR bstrApplicationBase = SysAllocString(szApplicationBase); if (bstrApplicationBase == NULL) hr = E_OUTOFMEMORY; else { hr = pAppDomainSetup->put_ApplicationBase(bstrApplicationBase); SysFreeString(bstrApplicationBase); } } if (SUCCEEDED(hr) && szConfigFile != NULL) { BSTR bstrConfigFile = SysAllocString(szConfigFile); if (bstrConfigFile == NULL) hr = E_OUTOFMEMORY; else { hr = pAppDomainSetup->put_ConfigurationFile(bstrConfigFile); SysFreeString(bstrConfigFile); } } if (FAILED(hr)) { Log(hSession, L"Failed to configure app domain setup. Error code 0x%X", hr); pAppDomainSetup->Release(); return false; } IUnknown* punkAppDomain; hr = pHost->CreateDomainEx(szName, pAppDomainSetup, NULL, &punkAppDomain); pAppDomainSetup->Release(); if (SUCCEEDED(hr)) { hr = punkAppDomain->QueryInterface(__uuidof(_AppDomain), (void**) ppAppDomain); punkAppDomain->Release(); } if (FAILED(hr)) { Log(hSession, L"Failed to create app domain. Error code 0x%X", hr); return false; } return true; } /// /// Locates a specific method in a specific class and assembly. /// /// Handle to the installer session, /// used just for logging /// Application domain in which to /// load assemblies. /// Display name of the assembly /// containing the method. /// Fully-qualified name of the class /// containing the method. /// Name of the method. /// Returned method interface. /// True if the method was located, otherwise false. /// Only public static methods are searched. Method /// parameter types are not considered; if there are multiple /// matching methods with different parameters, an error results. bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain, const wchar_t* szAssembly, const wchar_t* szClass, const wchar_t* szMethod, _MethodInfo** ppMethod) { HRESULT hr; _Assembly* pAssembly = NULL; BSTR bstrAssemblyName = SysAllocString(szAssembly); if (bstrAssemblyName == NULL) hr = E_OUTOFMEMORY; else { hr = pAppDomain->Load_2(bstrAssemblyName, &pAssembly); SysFreeString(bstrAssemblyName); } if (FAILED(hr)) { Log(hSession, L"Failed to load assembly %s. Error code 0x%X", szAssembly, hr); return false; } _Type* pType = NULL; BSTR bstrClass = SysAllocString(szClass); if (bstrClass == NULL) hr = E_OUTOFMEMORY; else { hr = pAssembly->GetType_2(bstrClass, &pType); SysFreeString(bstrClass); } pAssembly->Release(); if (FAILED(hr) || pType == NULL) { Log(hSession, L"Failed to load class %s. Error code 0x%X", szClass, hr); return false; } BSTR bstrMethod = SysAllocString(szMethod); if (bstrMethod == NULL) hr = E_OUTOFMEMORY; else { hr = pType->GetMethod_2(bstrMethod, (BindingFlags) (BindingFlags_Public | BindingFlags_Static), ppMethod); SysFreeString(bstrMethod); } pType->Release(); if (FAILED(hr) || *ppMethod == NULL) { Log(hSession, L"Failed to get method %s. Error code 0x%X", szMethod, hr); return false; } return true; }