diff options
Diffstat (limited to 'src/samples/Dtf/Tools/SfxCA/ClrHost.cpp')
-rw-r--r-- | src/samples/Dtf/Tools/SfxCA/ClrHost.cpp | 262 |
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 | |||
5 | void 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> | ||
32 | bool 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> | ||
125 | bool 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> | ||
213 | bool 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 | } | ||