aboutsummaryrefslogtreecommitdiff
path: root/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/samples/Dtf/Tools/SfxCA/ClrHost.cpp')
-rw-r--r--src/samples/Dtf/Tools/SfxCA/ClrHost.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp b/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp
new file mode 100644
index 00000000..1988fb2a
--- /dev/null
+++ b/src/samples/Dtf/Tools/SfxCA/ClrHost.cpp
@@ -0,0 +1,262 @@
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
5void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...);
6
7//---------------------------------------------------------------------
8// CLR HOSTING
9//---------------------------------------------------------------------
10
11/// <summary>
12/// Binds to the CLR after determining the appropriate version.
13/// </summary>
14/// <param name="hSession">Handle to the installer session,
15/// used just for logging.</param>
16/// <param name="version">Specific version of the CLR to load.
17/// If null, then the config file and/or primary assembly are
18/// used to determine the version.</param>
19/// <param name="szConfigFile">XML .config file which may contain
20/// a startup section to direct which version of the CLR to use.
21/// May be NULL.</param>
22/// <param name="szPrimaryAssembly">Assembly to be used to determine
23/// the version of the CLR in the absence of other configuration.
24/// May be NULL.</param>
25/// <param name="ppHost">Returned runtime host interface.</param>
26/// <returns>True if the CLR was loaded successfully, false if
27/// there was some error.</returns>
28/// <remarks>
29/// If szPrimaryAssembly is NULL and szConfigFile is also NULL or
30/// does not contain any version configuration, the CLR will not be loaded.
31/// </remarks>
32bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile,
33 const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost)
34{
35 typedef HRESULT (__stdcall *PGetRequestedRuntimeInfo)(LPCWSTR pExe, LPCWSTR pwszVersion,
36 LPCWSTR pConfigurationFile, DWORD startupFlags, DWORD runtimeInfoFlags,
37 LPWSTR pDirectory, DWORD dwDirectory, DWORD *dwDirectoryLength,
38 LPWSTR pVersion, DWORD cchBuffer, DWORD* dwlength);
39 typedef HRESULT (__stdcall *PCorBindToRuntimeEx)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor,
40 DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
41
42 HMODULE hmodMscoree = LoadLibrary(L"mscoree.dll");
43 if (hmodMscoree == NULL)
44 {
45 Log(hSession, L"Failed to load mscoree.dll (Error code %d). This custom action "
46 L"requires the .NET Framework to be installed.", GetLastError());
47 return false;
48 }
49 PGetRequestedRuntimeInfo pGetRequestedRuntimeInfo = (PGetRequestedRuntimeInfo)
50 GetProcAddress(hmodMscoree, "GetRequestedRuntimeInfo");
51 PCorBindToRuntimeEx pCorBindToRuntimeEx = (PCorBindToRuntimeEx)
52 GetProcAddress(hmodMscoree, "CorBindToRuntimeEx");
53 if (pGetRequestedRuntimeInfo == NULL || pCorBindToRuntimeEx == NULL)
54 {
55 Log(hSession, L"Failed to locate functions in mscoree.dll (Error code %d). This custom action "
56 L"requires the .NET Framework to be installed.", GetLastError());
57 FreeLibrary(hmodMscoree);
58 return false;
59 }
60
61 wchar_t szClrVersion[20];
62 HRESULT hr;
63
64 if (szVersion != NULL && szVersion[0] != L'\0')
65 {
66 wcsncpy_s(szClrVersion, 20, szVersion, 20);
67 }
68 else
69 {
70 wchar_t szVersionDir[MAX_PATH];
71 hr = pGetRequestedRuntimeInfo(szPrimaryAssembly, NULL,
72 szConfigFile, 0, 0, szVersionDir, MAX_PATH, NULL, szClrVersion, 20, NULL);
73 if (FAILED(hr))
74 {
75 Log(hSession, L"Failed to get requested CLR info. Error code 0x%x", hr);
76 Log(hSession, L"Ensure that the proper version of the .NET Framework is installed, or "
77 L"that there is a matching supportedRuntime element in CustomAction.config. "
78 L"If you are binding to .NET 4 or greater add "
79 L"useLegacyV2RuntimeActivationPolicy=true to the <startup> element.");
80 FreeLibrary(hmodMscoree);
81 return false;
82 }
83 }
84
85 Log(hSession, L"Binding to CLR version %s", szClrVersion);
86
87 ICorRuntimeHost* pHost;
88 hr = pCorBindToRuntimeEx(szClrVersion, NULL,
89 STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN,
90 CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void**) &pHost);
91 if (FAILED(hr))
92 {
93 Log(hSession, L"Failed to bind to the CLR. Error code 0x%X", hr);
94 FreeLibrary(hmodMscoree);
95 return false;
96 }
97 hr = pHost->Start();
98 if (FAILED(hr))
99 {
100 Log(hSession, L"Failed to start the CLR. Error code 0x%X", hr);
101 pHost->Release();
102 FreeLibrary(hmodMscoree);
103 return false;
104 }
105 *ppHost = pHost;
106 FreeLibrary(hmodMscoree);
107 return true;
108}
109
110/// <summary>
111/// Creates a new CLR application domain.
112/// </summary>
113/// <param name="hSession">Handle to the installer session,
114/// used just for logging</param>
115/// <param name="pHost">Interface to the runtime host where the
116/// app domain will be created.</param>
117/// <param name="szName">Name of the app domain to create.</param>
118/// <param name="szAppBase">Application base directory path, where
119/// the app domain will look first to load its assemblies.</param>
120/// <param name="szConfigFile">Optional XML .config file containing any
121/// configuration for thae app domain.</param>
122/// <param name="ppAppDomain">Returned app domain interface.</param>
123/// <returns>True if the app domain was created successfully, false if
124/// there was some error.</returns>
125bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost,
126 const wchar_t* szName, const wchar_t* szAppBase,
127 const wchar_t* szConfigFile, _AppDomain** ppAppDomain)
128{
129 IUnknown* punkAppDomainSetup = NULL;
130 IAppDomainSetup* pAppDomainSetup = NULL;
131 HRESULT hr = pHost->CreateDomainSetup(&punkAppDomainSetup);
132 if (SUCCEEDED(hr))
133 {
134 hr = punkAppDomainSetup->QueryInterface(__uuidof(IAppDomainSetup), (void**) &pAppDomainSetup);
135 punkAppDomainSetup->Release();
136 }
137 if (FAILED(hr))
138 {
139 Log(hSession, L"Failed to create app domain setup. Error code 0x%X", hr);
140 return false;
141 }
142
143 const wchar_t* szUrlPrefix = L"file:///";
144 size_t cchApplicationBase = wcslen(szUrlPrefix) + wcslen(szAppBase);
145 wchar_t* szApplicationBase = (wchar_t*) _alloca((cchApplicationBase + 1) * sizeof(wchar_t));
146 if (szApplicationBase == NULL) hr = E_OUTOFMEMORY;
147 else
148 {
149 StringCchCopy(szApplicationBase, cchApplicationBase + 1, szUrlPrefix);
150 StringCchCat(szApplicationBase, cchApplicationBase + 1, szAppBase);
151 BSTR bstrApplicationBase = SysAllocString(szApplicationBase);
152 if (bstrApplicationBase == NULL) hr = E_OUTOFMEMORY;
153 else
154 {
155 hr = pAppDomainSetup->put_ApplicationBase(bstrApplicationBase);
156 SysFreeString(bstrApplicationBase);
157 }
158 }
159
160 if (SUCCEEDED(hr) && szConfigFile != NULL)
161 {
162 BSTR bstrConfigFile = SysAllocString(szConfigFile);
163 if (bstrConfigFile == NULL) hr = E_OUTOFMEMORY;
164 else
165 {
166 hr = pAppDomainSetup->put_ConfigurationFile(bstrConfigFile);
167 SysFreeString(bstrConfigFile);
168 }
169 }
170
171 if (FAILED(hr))
172 {
173 Log(hSession, L"Failed to configure app domain setup. Error code 0x%X", hr);
174 pAppDomainSetup->Release();
175 return false;
176 }
177
178 IUnknown* punkAppDomain;
179 hr = pHost->CreateDomainEx(szName, pAppDomainSetup, NULL, &punkAppDomain);
180 pAppDomainSetup->Release();
181 if (SUCCEEDED(hr))
182 {
183 hr = punkAppDomain->QueryInterface(__uuidof(_AppDomain), (void**) ppAppDomain);
184 punkAppDomain->Release();
185 }
186
187 if (FAILED(hr))
188 {
189 Log(hSession, L"Failed to create app domain. Error code 0x%X", hr);
190 return false;
191 }
192
193 return true;
194}
195
196/// <summary>
197/// Locates a specific method in a specific class and assembly.
198/// </summary>
199/// <param name="hSession">Handle to the installer session,
200/// used just for logging</param>
201/// <param name="pAppDomain">Application domain in which to
202/// load assemblies.</param>
203/// <param name="szAssembly">Display name of the assembly
204/// containing the method.</param>
205/// <param name="szClass">Fully-qualified name of the class
206/// containing the method.</param>
207/// <param name="szMethod">Name of the method.</param>
208/// <param name="ppMethod">Returned method interface.</param>
209/// <returns>True if the method was located, otherwise false.</returns>
210/// <remarks>Only public static methods are searched. Method
211/// parameter types are not considered; if there are multiple
212/// matching methods with different parameters, an error results.</remarks>
213bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain,
214 const wchar_t* szAssembly, const wchar_t* szClass,
215 const wchar_t* szMethod, _MethodInfo** ppMethod)
216{
217 HRESULT hr;
218 _Assembly* pAssembly = NULL;
219 BSTR bstrAssemblyName = SysAllocString(szAssembly);
220 if (bstrAssemblyName == NULL) hr = E_OUTOFMEMORY;
221 else
222 {
223 hr = pAppDomain->Load_2(bstrAssemblyName, &pAssembly);
224 SysFreeString(bstrAssemblyName);
225 }
226 if (FAILED(hr))
227 {
228 Log(hSession, L"Failed to load assembly %s. Error code 0x%X", szAssembly, hr);
229 return false;
230 }
231
232 _Type* pType = NULL;
233 BSTR bstrClass = SysAllocString(szClass);
234 if (bstrClass == NULL) hr = E_OUTOFMEMORY;
235 else
236 {
237 hr = pAssembly->GetType_2(bstrClass, &pType);
238 SysFreeString(bstrClass);
239 }
240 pAssembly->Release();
241 if (FAILED(hr) || pType == NULL)
242 {
243 Log(hSession, L"Failed to load class %s. Error code 0x%X", szClass, hr);
244 return false;
245 }
246
247 BSTR bstrMethod = SysAllocString(szMethod);
248 if (bstrMethod == NULL) hr = E_OUTOFMEMORY;
249 else
250 {
251 hr = pType->GetMethod_2(bstrMethod,
252 (BindingFlags) (BindingFlags_Public | BindingFlags_Static), ppMethod);
253 SysFreeString(bstrMethod);
254 }
255 pType->Release();
256 if (FAILED(hr) || *ppMethod == NULL)
257 {
258 Log(hSession, L"Failed to get method %s. Error code 0x%X", szMethod, hr);
259 return false;
260 }
261 return true;
262}