diff options
author | Rob Mensching <rob@firegiant.com> | 2022-03-31 11:56:14 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-03-31 18:01:06 -0700 |
commit | 47582b162368e8edf7a3b11c13b8e9dabc5f0a26 (patch) | |
tree | 2c4063eff325684bed39de0edacd7866a257ae02 /src/dtf | |
parent | 167296c42497c4e95f0d5d71168542d747655981 (diff) | |
download | wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.gz wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.bz2 wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.zip |
Provide managed CA and Embedded UI DTF libraries via NuGet
Lots of refactoring to bring the SFX tooling back into the 'dtf'
layer since they are (in the end) tightly coupled to some DTF
assemblies. Also refactored the DTF tests into their own folder
and added a couple integration tests to build using the new CA/UI
NuGet package.
Closes wixtoolset/issues#6080
Diffstat (limited to 'src/dtf')
48 files changed, 4343 insertions, 148 deletions
diff --git a/src/dtf/SfxCA/ClrHost.cpp b/src/dtf/SfxCA/ClrHost.cpp new file mode 100644 index 00000000..1988fb2a --- /dev/null +++ b/src/dtf/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 | } | ||
diff --git a/src/dtf/SfxCA/EmbeddedUI.cpp b/src/dtf/SfxCA/EmbeddedUI.cpp new file mode 100644 index 00000000..a49cdeec --- /dev/null +++ b/src/dtf/SfxCA/EmbeddedUI.cpp | |||
@@ -0,0 +1,281 @@ | |||
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 | #include "SfxUtil.h" | ||
5 | |||
6 | // Globals for keeping track of things across UI messages. | ||
7 | static const wchar_t* g_szWorkingDir; | ||
8 | static ICorRuntimeHost* g_pClrHost; | ||
9 | static _AppDomain* g_pAppDomain; | ||
10 | static _MethodInfo* g_pProcessMessageMethod; | ||
11 | static _MethodInfo* g_pShutdownMethod; | ||
12 | |||
13 | // Reserve extra space for strings to be replaced at build time. | ||
14 | #define NULLSPACE \ | ||
15 | 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" \ | ||
16 | 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" \ | ||
17 | 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" \ | ||
18 | L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | ||
19 | |||
20 | // Prototypes for local functions. | ||
21 | // See the function definitions for comments. | ||
22 | |||
23 | bool InvokeInitializeMethod(_MethodInfo* pInitMethod, MSIHANDLE hSession, | ||
24 | const wchar_t* szClassName, LPDWORD pdwInternalUILevel, UINT* puiResult); | ||
25 | |||
26 | /// <summary> | ||
27 | /// First entry-point for the UI DLL when loaded and called by MSI. | ||
28 | /// Extracts the payload, hosts the CLR, and invokes the managed | ||
29 | /// initialize method. | ||
30 | /// </summary> | ||
31 | /// <param name="hSession">Handle to the installer session, | ||
32 | /// used for logging errors and to be passed on to the managed initialize method.</param> | ||
33 | /// <param name="szResourcePath">Path the directory where resources from the MsiEmbeddedUI table | ||
34 | /// have been extracted, and where additional payload from this package will be extracted.</param> | ||
35 | /// <param name="pdwInternalUILevel">MSI install UI level passed to and returned from | ||
36 | /// the managed initialize method.</param> | ||
37 | extern "C" | ||
38 | UINT __stdcall InitializeEmbeddedUI(MSIHANDLE hSession, LPCWSTR szResourcePath, LPDWORD pdwInternalUILevel) | ||
39 | { | ||
40 | // If the managed initialize method cannot be called, continue the installation in BASIC UI mode. | ||
41 | UINT uiResult = INSTALLUILEVEL_BASIC; | ||
42 | |||
43 | const wchar_t* szClassName = L"InitializeEmbeddedUI_FullClassName" NULLSPACE; | ||
44 | |||
45 | g_szWorkingDir = szResourcePath; | ||
46 | |||
47 | wchar_t szModule[MAX_PATH]; | ||
48 | DWORD cchCopied = GetModuleFileName(g_hModule, szModule, MAX_PATH - 1); | ||
49 | if (cchCopied == 0) | ||
50 | { | ||
51 | Log(hSession, L"Failed to get module path. Error code %d.", GetLastError()); | ||
52 | return uiResult; | ||
53 | } | ||
54 | else if (cchCopied == MAX_PATH - 1) | ||
55 | { | ||
56 | Log(hSession, L"Failed to get module path -- path is too long."); | ||
57 | return uiResult; | ||
58 | } | ||
59 | |||
60 | Log(hSession, L"Extracting embedded UI to temporary directory: %s", g_szWorkingDir); | ||
61 | int err = ExtractCabinet(szModule, g_szWorkingDir); | ||
62 | if (err != 0) | ||
63 | { | ||
64 | Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err); | ||
65 | Log(hSession, L"Ensure that no MsiEmbeddedUI.FileName values are the same as " | ||
66 | L"any file contained in the embedded UI package."); | ||
67 | return uiResult; | ||
68 | } | ||
69 | |||
70 | wchar_t szConfigFilePath[MAX_PATH + 20]; | ||
71 | StringCchCopy(szConfigFilePath, MAX_PATH + 20, g_szWorkingDir); | ||
72 | StringCchCat(szConfigFilePath, MAX_PATH + 20, L"\\EmbeddedUI.config"); | ||
73 | |||
74 | const wchar_t* szConfigFile = szConfigFilePath; | ||
75 | if (!PathFileExists(szConfigFilePath)) | ||
76 | { | ||
77 | szConfigFile = NULL; | ||
78 | } | ||
79 | |||
80 | wchar_t szWIAssembly[MAX_PATH + 50]; | ||
81 | StringCchCopy(szWIAssembly, MAX_PATH + 50, g_szWorkingDir); | ||
82 | StringCchCat(szWIAssembly, MAX_PATH + 50, L"\\WixToolset.Dtf.WindowsInstaller.dll"); | ||
83 | |||
84 | if (LoadCLR(hSession, NULL, szConfigFile, szWIAssembly, &g_pClrHost)) | ||
85 | { | ||
86 | if (CreateAppDomain(hSession, g_pClrHost, L"EmbeddedUI", g_szWorkingDir, | ||
87 | szConfigFile, &g_pAppDomain)) | ||
88 | { | ||
89 | const wchar_t* szMsiAssemblyName = L"WixToolset.Dtf.WindowsInstaller"; | ||
90 | const wchar_t* szProxyClass = L"WixToolset.Dtf.WindowsInstaller.EmbeddedUIProxy"; | ||
91 | const wchar_t* szInitMethod = L"Initialize"; | ||
92 | const wchar_t* szProcessMessageMethod = L"ProcessMessage"; | ||
93 | const wchar_t* szShutdownMethod = L"Shutdown"; | ||
94 | |||
95 | if (GetMethod(hSession, g_pAppDomain, szMsiAssemblyName, | ||
96 | szProxyClass, szProcessMessageMethod, &g_pProcessMessageMethod) && | ||
97 | GetMethod(hSession, g_pAppDomain, szMsiAssemblyName, | ||
98 | szProxyClass, szShutdownMethod, &g_pShutdownMethod)) | ||
99 | { | ||
100 | _MethodInfo* pInitMethod; | ||
101 | if (GetMethod(hSession, g_pAppDomain, szMsiAssemblyName, | ||
102 | szProxyClass, szInitMethod, &pInitMethod)) | ||
103 | { | ||
104 | bool invokeSuccess = InvokeInitializeMethod(pInitMethod, hSession, szClassName, pdwInternalUILevel, &uiResult); | ||
105 | pInitMethod->Release(); | ||
106 | if (invokeSuccess) | ||
107 | { | ||
108 | if (uiResult == 0) | ||
109 | { | ||
110 | return ERROR_SUCCESS; | ||
111 | } | ||
112 | else if (uiResult == ERROR_INSTALL_USEREXIT) | ||
113 | { | ||
114 | // InitializeEmbeddedUI is not allowed to return ERROR_INSTALL_USEREXIT. | ||
115 | // So return success here and then IDCANCEL on the next progress message. | ||
116 | uiResult = 0; | ||
117 | *pdwInternalUILevel = INSTALLUILEVEL_NONE; | ||
118 | Log(hSession, L"Initialization canceled by user."); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | g_pProcessMessageMethod->Release(); | ||
125 | g_pProcessMessageMethod = NULL; | ||
126 | g_pShutdownMethod->Release(); | ||
127 | g_pShutdownMethod = NULL; | ||
128 | |||
129 | g_pClrHost->UnloadDomain(g_pAppDomain); | ||
130 | g_pAppDomain->Release(); | ||
131 | g_pAppDomain = NULL; | ||
132 | } | ||
133 | g_pClrHost->Stop(); | ||
134 | g_pClrHost->Release(); | ||
135 | g_pClrHost = NULL; | ||
136 | } | ||
137 | |||
138 | return uiResult; | ||
139 | } | ||
140 | |||
141 | /// <summary> | ||
142 | /// Entry-point for UI progress messages received from the MSI engine during an active installation. | ||
143 | /// Forwards the progress messages to the managed handler method and returns its result. | ||
144 | /// </summary> | ||
145 | extern "C" | ||
146 | INT __stdcall EmbeddedUIHandler(UINT uiMessageType, MSIHANDLE hRecord) | ||
147 | { | ||
148 | if (g_pProcessMessageMethod == NULL) | ||
149 | { | ||
150 | // Initialization was canceled. | ||
151 | return IDCANCEL; | ||
152 | } | ||
153 | |||
154 | VARIANT vResult; | ||
155 | VariantInit(&vResult); | ||
156 | |||
157 | VARIANT vNull; | ||
158 | vNull.vt = VT_EMPTY; | ||
159 | |||
160 | SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 2); | ||
161 | VARIANT vMessageType; | ||
162 | vMessageType.vt = VT_I4; | ||
163 | vMessageType.lVal = (LONG) uiMessageType; | ||
164 | LONG index = 0; | ||
165 | HRESULT hr = SafeArrayPutElement(saArgs, &index, &vMessageType); | ||
166 | if (FAILED(hr)) goto LExit; | ||
167 | VARIANT vRecord; | ||
168 | vRecord.vt = VT_I4; | ||
169 | vRecord.lVal = (LONG) hRecord; | ||
170 | index = 1; | ||
171 | hr = SafeArrayPutElement(saArgs, &index, &vRecord); | ||
172 | if (FAILED(hr)) goto LExit; | ||
173 | |||
174 | hr = g_pProcessMessageMethod->Invoke_3(vNull, saArgs, &vResult); | ||
175 | |||
176 | LExit: | ||
177 | SafeArrayDestroy(saArgs); | ||
178 | if (SUCCEEDED(hr)) | ||
179 | { | ||
180 | return vResult.intVal; | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | return -1; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /// <summary> | ||
189 | /// Entry-point for the UI shutdown message received from the MSI engine after installation has completed. | ||
190 | /// Forwards the shutdown message to the managed shutdown method, then shuts down the CLR. | ||
191 | /// </summary> | ||
192 | extern "C" | ||
193 | DWORD __stdcall ShutdownEmbeddedUI() | ||
194 | { | ||
195 | if (g_pShutdownMethod != NULL) | ||
196 | { | ||
197 | VARIANT vNull; | ||
198 | vNull.vt = VT_EMPTY; | ||
199 | SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0); | ||
200 | g_pShutdownMethod->Invoke_3(vNull, saArgs, NULL); | ||
201 | SafeArrayDestroy(saArgs); | ||
202 | |||
203 | g_pClrHost->UnloadDomain(g_pAppDomain); | ||
204 | g_pAppDomain->Release(); | ||
205 | g_pClrHost->Stop(); | ||
206 | g_pClrHost->Release(); | ||
207 | } | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /// <summary> | ||
213 | /// Loads and invokes the managed portion of the proxy. | ||
214 | /// </summary> | ||
215 | /// <param name="pInitMethod">Managed initialize method to be invoked.</param> | ||
216 | /// <param name="hSession">Handle to the installer session, | ||
217 | /// used for logging errors and to be passed on to the managed initialize method.</param> | ||
218 | /// <param name="szClassName">Name of the UI class to be loaded. | ||
219 | /// This must be of the form: AssemblyName!Namespace.Class</param> | ||
220 | /// <param name="pdwInternalUILevel">MSI install UI level passed to and returned from | ||
221 | /// the managed initialize method.</param> | ||
222 | /// <param name="puiResult">Return value of the invoked initialize method.</param> | ||
223 | /// <returns>True if the managed proxy was invoked successfully, or an | ||
224 | /// error code if there was some error. Note the initialize method itself may | ||
225 | /// return an error via puiResult while this method still returns true | ||
226 | /// since the invocation was successful.</returns> | ||
227 | bool InvokeInitializeMethod(_MethodInfo* pInitMethod, MSIHANDLE hSession, const wchar_t* szClassName, LPDWORD pdwInternalUILevel, UINT* puiResult) | ||
228 | { | ||
229 | VARIANT vResult; | ||
230 | VariantInit(&vResult); | ||
231 | |||
232 | VARIANT vNull; | ||
233 | vNull.vt = VT_EMPTY; | ||
234 | |||
235 | SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3); | ||
236 | VARIANT vSessionHandle; | ||
237 | vSessionHandle.vt = VT_I4; | ||
238 | vSessionHandle.lVal = (LONG) hSession; | ||
239 | LONG index = 0; | ||
240 | HRESULT hr = SafeArrayPutElement(saArgs, &index, &vSessionHandle); | ||
241 | if (FAILED(hr)) goto LExit; | ||
242 | VARIANT vEntryPoint; | ||
243 | vEntryPoint.vt = VT_BSTR; | ||
244 | vEntryPoint.bstrVal = SysAllocString(szClassName); | ||
245 | if (vEntryPoint.bstrVal == NULL) | ||
246 | { | ||
247 | hr = E_OUTOFMEMORY; | ||
248 | goto LExit; | ||
249 | } | ||
250 | index = 1; | ||
251 | hr = SafeArrayPutElement(saArgs, &index, &vEntryPoint); | ||
252 | if (FAILED(hr)) goto LExit; | ||
253 | VARIANT vUILevel; | ||
254 | vUILevel.vt = VT_I4; | ||
255 | vUILevel.ulVal = *pdwInternalUILevel; | ||
256 | index = 2; | ||
257 | hr = SafeArrayPutElement(saArgs, &index, &vUILevel); | ||
258 | if (FAILED(hr)) goto LExit; | ||
259 | |||
260 | hr = pInitMethod->Invoke_3(vNull, saArgs, &vResult); | ||
261 | |||
262 | LExit: | ||
263 | SafeArrayDestroy(saArgs); | ||
264 | if (SUCCEEDED(hr)) | ||
265 | { | ||
266 | *puiResult = (UINT) vResult.lVal; | ||
267 | if ((*puiResult & 0xFFFF) == 0) | ||
268 | { | ||
269 | // Due to interop limitations, the successful resulting UILevel is returned | ||
270 | // as the high-word of the return value instead of via a ref parameter. | ||
271 | *pdwInternalUILevel = *puiResult >> 16; | ||
272 | *puiResult = 0; | ||
273 | } | ||
274 | return true; | ||
275 | } | ||
276 | else | ||
277 | { | ||
278 | Log(hSession, L"Failed to invoke EmbeddedUI Initialize method. Error code 0x%X", hr); | ||
279 | return false; | ||
280 | } | ||
281 | } | ||
diff --git a/src/dtf/SfxCA/EntryPoints.def b/src/dtf/SfxCA/EntryPoints.def new file mode 100644 index 00000000..dd28b920 --- /dev/null +++ b/src/dtf/SfxCA/EntryPoints.def | |||
@@ -0,0 +1,140 @@ | |||
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 | |||
4 | LIBRARY "SfxCA" | ||
5 | |||
6 | EXPORTS | ||
7 | |||
8 | CustomActionEntryPoint000________________________________________________=CustomActionEntryPoint000 | ||
9 | CustomActionEntryPoint001________________________________________________=CustomActionEntryPoint001 | ||
10 | CustomActionEntryPoint002________________________________________________=CustomActionEntryPoint002 | ||
11 | CustomActionEntryPoint003________________________________________________=CustomActionEntryPoint003 | ||
12 | CustomActionEntryPoint004________________________________________________=CustomActionEntryPoint004 | ||
13 | CustomActionEntryPoint005________________________________________________=CustomActionEntryPoint005 | ||
14 | CustomActionEntryPoint006________________________________________________=CustomActionEntryPoint006 | ||
15 | CustomActionEntryPoint007________________________________________________=CustomActionEntryPoint007 | ||
16 | CustomActionEntryPoint008________________________________________________=CustomActionEntryPoint008 | ||
17 | CustomActionEntryPoint009________________________________________________=CustomActionEntryPoint009 | ||
18 | CustomActionEntryPoint010________________________________________________=CustomActionEntryPoint010 | ||
19 | CustomActionEntryPoint011________________________________________________=CustomActionEntryPoint011 | ||
20 | CustomActionEntryPoint012________________________________________________=CustomActionEntryPoint012 | ||
21 | CustomActionEntryPoint013________________________________________________=CustomActionEntryPoint013 | ||
22 | CustomActionEntryPoint014________________________________________________=CustomActionEntryPoint014 | ||
23 | CustomActionEntryPoint015________________________________________________=CustomActionEntryPoint015 | ||
24 | CustomActionEntryPoint016________________________________________________=CustomActionEntryPoint016 | ||
25 | CustomActionEntryPoint017________________________________________________=CustomActionEntryPoint017 | ||
26 | CustomActionEntryPoint018________________________________________________=CustomActionEntryPoint018 | ||
27 | CustomActionEntryPoint019________________________________________________=CustomActionEntryPoint019 | ||
28 | CustomActionEntryPoint020________________________________________________=CustomActionEntryPoint020 | ||
29 | CustomActionEntryPoint021________________________________________________=CustomActionEntryPoint021 | ||
30 | CustomActionEntryPoint022________________________________________________=CustomActionEntryPoint022 | ||
31 | CustomActionEntryPoint023________________________________________________=CustomActionEntryPoint023 | ||
32 | CustomActionEntryPoint024________________________________________________=CustomActionEntryPoint024 | ||
33 | CustomActionEntryPoint025________________________________________________=CustomActionEntryPoint025 | ||
34 | CustomActionEntryPoint026________________________________________________=CustomActionEntryPoint026 | ||
35 | CustomActionEntryPoint027________________________________________________=CustomActionEntryPoint027 | ||
36 | CustomActionEntryPoint028________________________________________________=CustomActionEntryPoint028 | ||
37 | CustomActionEntryPoint029________________________________________________=CustomActionEntryPoint029 | ||
38 | CustomActionEntryPoint030________________________________________________=CustomActionEntryPoint030 | ||
39 | CustomActionEntryPoint031________________________________________________=CustomActionEntryPoint031 | ||
40 | CustomActionEntryPoint032________________________________________________=CustomActionEntryPoint032 | ||
41 | CustomActionEntryPoint033________________________________________________=CustomActionEntryPoint033 | ||
42 | CustomActionEntryPoint034________________________________________________=CustomActionEntryPoint034 | ||
43 | CustomActionEntryPoint035________________________________________________=CustomActionEntryPoint035 | ||
44 | CustomActionEntryPoint036________________________________________________=CustomActionEntryPoint036 | ||
45 | CustomActionEntryPoint037________________________________________________=CustomActionEntryPoint037 | ||
46 | CustomActionEntryPoint038________________________________________________=CustomActionEntryPoint038 | ||
47 | CustomActionEntryPoint039________________________________________________=CustomActionEntryPoint039 | ||
48 | CustomActionEntryPoint040________________________________________________=CustomActionEntryPoint040 | ||
49 | CustomActionEntryPoint041________________________________________________=CustomActionEntryPoint041 | ||
50 | CustomActionEntryPoint042________________________________________________=CustomActionEntryPoint042 | ||
51 | CustomActionEntryPoint043________________________________________________=CustomActionEntryPoint043 | ||
52 | CustomActionEntryPoint044________________________________________________=CustomActionEntryPoint044 | ||
53 | CustomActionEntryPoint045________________________________________________=CustomActionEntryPoint045 | ||
54 | CustomActionEntryPoint046________________________________________________=CustomActionEntryPoint046 | ||
55 | CustomActionEntryPoint047________________________________________________=CustomActionEntryPoint047 | ||
56 | CustomActionEntryPoint048________________________________________________=CustomActionEntryPoint048 | ||
57 | CustomActionEntryPoint049________________________________________________=CustomActionEntryPoint049 | ||
58 | CustomActionEntryPoint050________________________________________________=CustomActionEntryPoint050 | ||
59 | CustomActionEntryPoint051________________________________________________=CustomActionEntryPoint051 | ||
60 | CustomActionEntryPoint052________________________________________________=CustomActionEntryPoint052 | ||
61 | CustomActionEntryPoint053________________________________________________=CustomActionEntryPoint053 | ||
62 | CustomActionEntryPoint054________________________________________________=CustomActionEntryPoint054 | ||
63 | CustomActionEntryPoint055________________________________________________=CustomActionEntryPoint055 | ||
64 | CustomActionEntryPoint056________________________________________________=CustomActionEntryPoint056 | ||
65 | CustomActionEntryPoint057________________________________________________=CustomActionEntryPoint057 | ||
66 | CustomActionEntryPoint058________________________________________________=CustomActionEntryPoint058 | ||
67 | CustomActionEntryPoint059________________________________________________=CustomActionEntryPoint059 | ||
68 | CustomActionEntryPoint060________________________________________________=CustomActionEntryPoint060 | ||
69 | CustomActionEntryPoint061________________________________________________=CustomActionEntryPoint061 | ||
70 | CustomActionEntryPoint062________________________________________________=CustomActionEntryPoint062 | ||
71 | CustomActionEntryPoint063________________________________________________=CustomActionEntryPoint063 | ||
72 | CustomActionEntryPoint064________________________________________________=CustomActionEntryPoint064 | ||
73 | CustomActionEntryPoint065________________________________________________=CustomActionEntryPoint065 | ||
74 | CustomActionEntryPoint066________________________________________________=CustomActionEntryPoint066 | ||
75 | CustomActionEntryPoint067________________________________________________=CustomActionEntryPoint067 | ||
76 | CustomActionEntryPoint068________________________________________________=CustomActionEntryPoint068 | ||
77 | CustomActionEntryPoint069________________________________________________=CustomActionEntryPoint069 | ||
78 | CustomActionEntryPoint070________________________________________________=CustomActionEntryPoint070 | ||
79 | CustomActionEntryPoint071________________________________________________=CustomActionEntryPoint071 | ||
80 | CustomActionEntryPoint072________________________________________________=CustomActionEntryPoint072 | ||
81 | CustomActionEntryPoint073________________________________________________=CustomActionEntryPoint073 | ||
82 | CustomActionEntryPoint074________________________________________________=CustomActionEntryPoint074 | ||
83 | CustomActionEntryPoint075________________________________________________=CustomActionEntryPoint075 | ||
84 | CustomActionEntryPoint076________________________________________________=CustomActionEntryPoint076 | ||
85 | CustomActionEntryPoint077________________________________________________=CustomActionEntryPoint077 | ||
86 | CustomActionEntryPoint078________________________________________________=CustomActionEntryPoint078 | ||
87 | CustomActionEntryPoint079________________________________________________=CustomActionEntryPoint079 | ||
88 | CustomActionEntryPoint080________________________________________________=CustomActionEntryPoint080 | ||
89 | CustomActionEntryPoint081________________________________________________=CustomActionEntryPoint081 | ||
90 | CustomActionEntryPoint082________________________________________________=CustomActionEntryPoint082 | ||
91 | CustomActionEntryPoint083________________________________________________=CustomActionEntryPoint083 | ||
92 | CustomActionEntryPoint084________________________________________________=CustomActionEntryPoint084 | ||
93 | CustomActionEntryPoint085________________________________________________=CustomActionEntryPoint085 | ||
94 | CustomActionEntryPoint086________________________________________________=CustomActionEntryPoint086 | ||
95 | CustomActionEntryPoint087________________________________________________=CustomActionEntryPoint087 | ||
96 | CustomActionEntryPoint088________________________________________________=CustomActionEntryPoint088 | ||
97 | CustomActionEntryPoint089________________________________________________=CustomActionEntryPoint089 | ||
98 | CustomActionEntryPoint090________________________________________________=CustomActionEntryPoint090 | ||
99 | CustomActionEntryPoint091________________________________________________=CustomActionEntryPoint091 | ||
100 | CustomActionEntryPoint092________________________________________________=CustomActionEntryPoint092 | ||
101 | CustomActionEntryPoint093________________________________________________=CustomActionEntryPoint093 | ||
102 | CustomActionEntryPoint094________________________________________________=CustomActionEntryPoint094 | ||
103 | CustomActionEntryPoint095________________________________________________=CustomActionEntryPoint095 | ||
104 | CustomActionEntryPoint096________________________________________________=CustomActionEntryPoint096 | ||
105 | CustomActionEntryPoint097________________________________________________=CustomActionEntryPoint097 | ||
106 | CustomActionEntryPoint098________________________________________________=CustomActionEntryPoint098 | ||
107 | CustomActionEntryPoint099________________________________________________=CustomActionEntryPoint099 | ||
108 | CustomActionEntryPoint100________________________________________________=CustomActionEntryPoint100 | ||
109 | CustomActionEntryPoint101________________________________________________=CustomActionEntryPoint101 | ||
110 | CustomActionEntryPoint102________________________________________________=CustomActionEntryPoint102 | ||
111 | CustomActionEntryPoint103________________________________________________=CustomActionEntryPoint103 | ||
112 | CustomActionEntryPoint104________________________________________________=CustomActionEntryPoint104 | ||
113 | CustomActionEntryPoint105________________________________________________=CustomActionEntryPoint105 | ||
114 | CustomActionEntryPoint106________________________________________________=CustomActionEntryPoint106 | ||
115 | CustomActionEntryPoint107________________________________________________=CustomActionEntryPoint107 | ||
116 | CustomActionEntryPoint108________________________________________________=CustomActionEntryPoint108 | ||
117 | CustomActionEntryPoint109________________________________________________=CustomActionEntryPoint109 | ||
118 | CustomActionEntryPoint110________________________________________________=CustomActionEntryPoint110 | ||
119 | CustomActionEntryPoint111________________________________________________=CustomActionEntryPoint111 | ||
120 | CustomActionEntryPoint112________________________________________________=CustomActionEntryPoint112 | ||
121 | CustomActionEntryPoint113________________________________________________=CustomActionEntryPoint113 | ||
122 | CustomActionEntryPoint114________________________________________________=CustomActionEntryPoint114 | ||
123 | CustomActionEntryPoint115________________________________________________=CustomActionEntryPoint115 | ||
124 | CustomActionEntryPoint116________________________________________________=CustomActionEntryPoint116 | ||
125 | CustomActionEntryPoint117________________________________________________=CustomActionEntryPoint117 | ||
126 | CustomActionEntryPoint118________________________________________________=CustomActionEntryPoint118 | ||
127 | CustomActionEntryPoint119________________________________________________=CustomActionEntryPoint119 | ||
128 | CustomActionEntryPoint120________________________________________________=CustomActionEntryPoint120 | ||
129 | CustomActionEntryPoint121________________________________________________=CustomActionEntryPoint121 | ||
130 | CustomActionEntryPoint122________________________________________________=CustomActionEntryPoint122 | ||
131 | CustomActionEntryPoint123________________________________________________=CustomActionEntryPoint123 | ||
132 | CustomActionEntryPoint124________________________________________________=CustomActionEntryPoint124 | ||
133 | CustomActionEntryPoint125________________________________________________=CustomActionEntryPoint125 | ||
134 | CustomActionEntryPoint126________________________________________________=CustomActionEntryPoint126 | ||
135 | CustomActionEntryPoint127________________________________________________=CustomActionEntryPoint127 | ||
136 | |||
137 | zzzzInvokeManagedCustomActionOutOfProcW=InvokeManagedCustomActionOutOfProc | ||
138 | zzzInitializeEmbeddedUI=InitializeEmbeddedUI | ||
139 | zzzEmbeddedUIHandler=EmbeddedUIHandler | ||
140 | zzzShutdownEmbeddedUI=ShutdownEmbeddedUI | ||
diff --git a/src/dtf/SfxCA/EntryPoints.h b/src/dtf/SfxCA/EntryPoints.h new file mode 100644 index 00000000..bd2fa970 --- /dev/null +++ b/src/dtf/SfxCA/EntryPoints.h | |||
@@ -0,0 +1,162 @@ | |||
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 | int InvokeCustomAction(MSIHANDLE hSession, | ||
4 | const wchar_t* szWorkingDir, const wchar_t* szEntryPoint); | ||
5 | |||
6 | /// <summary> | ||
7 | /// Macro for defining and exporting a custom action entrypoint. | ||
8 | /// </summary> | ||
9 | /// <param name="name">Name of the entrypoint as exported from | ||
10 | /// the DLL.</param> | ||
11 | /// <param name="method">Path to the managed custom action method, | ||
12 | /// in the form: "AssemblyName!Namespace.Class.Method"</param> | ||
13 | /// <remarks> | ||
14 | /// To prevent the exported name from being decorated, add | ||
15 | /// /EXPORT:name to the linker options for every entrypoint. | ||
16 | /// </remarks> | ||
17 | #define CUSTOMACTION_ENTRYPOINT(name,method) extern "C" int __stdcall \ | ||
18 | name(MSIHANDLE hSession) { return InvokeCustomAction(hSession, NULL, method); } | ||
19 | |||
20 | // TEMPLATE ENTRYPOINTS | ||
21 | // To be edited by the MakeSfxCA tool. | ||
22 | |||
23 | #define NULLSPACE \ | ||
24 | 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" \ | ||
25 | 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" \ | ||
26 | 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" \ | ||
27 | L"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" | ||
28 | |||
29 | #define TEMPLATE_CA_ENTRYPOINT(id,sid) CUSTOMACTION_ENTRYPOINT( \ | ||
30 | CustomActionEntryPoint##id##, \ | ||
31 | L"CustomActionEntryPoint" sid NULLSPACE) | ||
32 | |||
33 | TEMPLATE_CA_ENTRYPOINT(000,L"000"); | ||
34 | TEMPLATE_CA_ENTRYPOINT(001,L"001"); | ||
35 | TEMPLATE_CA_ENTRYPOINT(002,L"002"); | ||
36 | TEMPLATE_CA_ENTRYPOINT(003,L"003"); | ||
37 | TEMPLATE_CA_ENTRYPOINT(004,L"004"); | ||
38 | TEMPLATE_CA_ENTRYPOINT(005,L"005"); | ||
39 | TEMPLATE_CA_ENTRYPOINT(006,L"006"); | ||
40 | TEMPLATE_CA_ENTRYPOINT(007,L"007"); | ||
41 | TEMPLATE_CA_ENTRYPOINT(008,L"008"); | ||
42 | TEMPLATE_CA_ENTRYPOINT(009,L"009"); | ||
43 | TEMPLATE_CA_ENTRYPOINT(010,L"010"); | ||
44 | TEMPLATE_CA_ENTRYPOINT(011,L"011"); | ||
45 | TEMPLATE_CA_ENTRYPOINT(012,L"012"); | ||
46 | TEMPLATE_CA_ENTRYPOINT(013,L"013"); | ||
47 | TEMPLATE_CA_ENTRYPOINT(014,L"014"); | ||
48 | TEMPLATE_CA_ENTRYPOINT(015,L"015"); | ||
49 | TEMPLATE_CA_ENTRYPOINT(016,L"016"); | ||
50 | TEMPLATE_CA_ENTRYPOINT(017,L"017"); | ||
51 | TEMPLATE_CA_ENTRYPOINT(018,L"018"); | ||
52 | TEMPLATE_CA_ENTRYPOINT(019,L"019"); | ||
53 | TEMPLATE_CA_ENTRYPOINT(020,L"020"); | ||
54 | TEMPLATE_CA_ENTRYPOINT(021,L"021"); | ||
55 | TEMPLATE_CA_ENTRYPOINT(022,L"022"); | ||
56 | TEMPLATE_CA_ENTRYPOINT(023,L"023"); | ||
57 | TEMPLATE_CA_ENTRYPOINT(024,L"024"); | ||
58 | TEMPLATE_CA_ENTRYPOINT(025,L"025"); | ||
59 | TEMPLATE_CA_ENTRYPOINT(026,L"026"); | ||
60 | TEMPLATE_CA_ENTRYPOINT(027,L"027"); | ||
61 | TEMPLATE_CA_ENTRYPOINT(028,L"028"); | ||
62 | TEMPLATE_CA_ENTRYPOINT(029,L"029"); | ||
63 | TEMPLATE_CA_ENTRYPOINT(030,L"030"); | ||
64 | TEMPLATE_CA_ENTRYPOINT(031,L"031"); | ||
65 | TEMPLATE_CA_ENTRYPOINT(032,L"032"); | ||
66 | TEMPLATE_CA_ENTRYPOINT(033,L"033"); | ||
67 | TEMPLATE_CA_ENTRYPOINT(034,L"034"); | ||
68 | TEMPLATE_CA_ENTRYPOINT(035,L"035"); | ||
69 | TEMPLATE_CA_ENTRYPOINT(036,L"036"); | ||
70 | TEMPLATE_CA_ENTRYPOINT(037,L"037"); | ||
71 | TEMPLATE_CA_ENTRYPOINT(038,L"038"); | ||
72 | TEMPLATE_CA_ENTRYPOINT(039,L"039"); | ||
73 | TEMPLATE_CA_ENTRYPOINT(040,L"040"); | ||
74 | TEMPLATE_CA_ENTRYPOINT(041,L"041"); | ||
75 | TEMPLATE_CA_ENTRYPOINT(042,L"042"); | ||
76 | TEMPLATE_CA_ENTRYPOINT(043,L"043"); | ||
77 | TEMPLATE_CA_ENTRYPOINT(044,L"044"); | ||
78 | TEMPLATE_CA_ENTRYPOINT(045,L"045"); | ||
79 | TEMPLATE_CA_ENTRYPOINT(046,L"046"); | ||
80 | TEMPLATE_CA_ENTRYPOINT(047,L"047"); | ||
81 | TEMPLATE_CA_ENTRYPOINT(048,L"048"); | ||
82 | TEMPLATE_CA_ENTRYPOINT(049,L"049"); | ||
83 | TEMPLATE_CA_ENTRYPOINT(050,L"050"); | ||
84 | TEMPLATE_CA_ENTRYPOINT(051,L"051"); | ||
85 | TEMPLATE_CA_ENTRYPOINT(052,L"052"); | ||
86 | TEMPLATE_CA_ENTRYPOINT(053,L"053"); | ||
87 | TEMPLATE_CA_ENTRYPOINT(054,L"054"); | ||
88 | TEMPLATE_CA_ENTRYPOINT(055,L"055"); | ||
89 | TEMPLATE_CA_ENTRYPOINT(056,L"056"); | ||
90 | TEMPLATE_CA_ENTRYPOINT(057,L"057"); | ||
91 | TEMPLATE_CA_ENTRYPOINT(058,L"058"); | ||
92 | TEMPLATE_CA_ENTRYPOINT(059,L"059"); | ||
93 | TEMPLATE_CA_ENTRYPOINT(060,L"060"); | ||
94 | TEMPLATE_CA_ENTRYPOINT(061,L"061"); | ||
95 | TEMPLATE_CA_ENTRYPOINT(062,L"062"); | ||
96 | TEMPLATE_CA_ENTRYPOINT(063,L"063"); | ||
97 | TEMPLATE_CA_ENTRYPOINT(064,L"064"); | ||
98 | TEMPLATE_CA_ENTRYPOINT(065,L"065"); | ||
99 | TEMPLATE_CA_ENTRYPOINT(066,L"066"); | ||
100 | TEMPLATE_CA_ENTRYPOINT(067,L"067"); | ||
101 | TEMPLATE_CA_ENTRYPOINT(068,L"068"); | ||
102 | TEMPLATE_CA_ENTRYPOINT(069,L"069"); | ||
103 | TEMPLATE_CA_ENTRYPOINT(070,L"070"); | ||
104 | TEMPLATE_CA_ENTRYPOINT(071,L"071"); | ||
105 | TEMPLATE_CA_ENTRYPOINT(072,L"072"); | ||
106 | TEMPLATE_CA_ENTRYPOINT(073,L"073"); | ||
107 | TEMPLATE_CA_ENTRYPOINT(074,L"074"); | ||
108 | TEMPLATE_CA_ENTRYPOINT(075,L"075"); | ||
109 | TEMPLATE_CA_ENTRYPOINT(076,L"076"); | ||
110 | TEMPLATE_CA_ENTRYPOINT(077,L"077"); | ||
111 | TEMPLATE_CA_ENTRYPOINT(078,L"078"); | ||
112 | TEMPLATE_CA_ENTRYPOINT(079,L"079"); | ||
113 | TEMPLATE_CA_ENTRYPOINT(080,L"080"); | ||
114 | TEMPLATE_CA_ENTRYPOINT(081,L"081"); | ||
115 | TEMPLATE_CA_ENTRYPOINT(082,L"082"); | ||
116 | TEMPLATE_CA_ENTRYPOINT(083,L"083"); | ||
117 | TEMPLATE_CA_ENTRYPOINT(084,L"084"); | ||
118 | TEMPLATE_CA_ENTRYPOINT(085,L"085"); | ||
119 | TEMPLATE_CA_ENTRYPOINT(086,L"086"); | ||
120 | TEMPLATE_CA_ENTRYPOINT(087,L"087"); | ||
121 | TEMPLATE_CA_ENTRYPOINT(088,L"088"); | ||
122 | TEMPLATE_CA_ENTRYPOINT(089,L"089"); | ||
123 | TEMPLATE_CA_ENTRYPOINT(090,L"090"); | ||
124 | TEMPLATE_CA_ENTRYPOINT(091,L"091"); | ||
125 | TEMPLATE_CA_ENTRYPOINT(092,L"092"); | ||
126 | TEMPLATE_CA_ENTRYPOINT(093,L"093"); | ||
127 | TEMPLATE_CA_ENTRYPOINT(094,L"094"); | ||
128 | TEMPLATE_CA_ENTRYPOINT(095,L"095"); | ||
129 | TEMPLATE_CA_ENTRYPOINT(096,L"096"); | ||
130 | TEMPLATE_CA_ENTRYPOINT(097,L"097"); | ||
131 | TEMPLATE_CA_ENTRYPOINT(098,L"098"); | ||
132 | TEMPLATE_CA_ENTRYPOINT(099,L"099"); | ||
133 | TEMPLATE_CA_ENTRYPOINT(100,L"100"); | ||
134 | TEMPLATE_CA_ENTRYPOINT(101,L"101"); | ||
135 | TEMPLATE_CA_ENTRYPOINT(102,L"102"); | ||
136 | TEMPLATE_CA_ENTRYPOINT(103,L"103"); | ||
137 | TEMPLATE_CA_ENTRYPOINT(104,L"104"); | ||
138 | TEMPLATE_CA_ENTRYPOINT(105,L"105"); | ||
139 | TEMPLATE_CA_ENTRYPOINT(106,L"106"); | ||
140 | TEMPLATE_CA_ENTRYPOINT(107,L"107"); | ||
141 | TEMPLATE_CA_ENTRYPOINT(108,L"108"); | ||
142 | TEMPLATE_CA_ENTRYPOINT(109,L"109"); | ||
143 | TEMPLATE_CA_ENTRYPOINT(110,L"110"); | ||
144 | TEMPLATE_CA_ENTRYPOINT(111,L"111"); | ||
145 | TEMPLATE_CA_ENTRYPOINT(112,L"112"); | ||
146 | TEMPLATE_CA_ENTRYPOINT(113,L"113"); | ||
147 | TEMPLATE_CA_ENTRYPOINT(114,L"114"); | ||
148 | TEMPLATE_CA_ENTRYPOINT(115,L"115"); | ||
149 | TEMPLATE_CA_ENTRYPOINT(116,L"116"); | ||
150 | TEMPLATE_CA_ENTRYPOINT(117,L"117"); | ||
151 | TEMPLATE_CA_ENTRYPOINT(118,L"118"); | ||
152 | TEMPLATE_CA_ENTRYPOINT(119,L"119"); | ||
153 | TEMPLATE_CA_ENTRYPOINT(120,L"120"); | ||
154 | TEMPLATE_CA_ENTRYPOINT(121,L"121"); | ||
155 | TEMPLATE_CA_ENTRYPOINT(122,L"122"); | ||
156 | TEMPLATE_CA_ENTRYPOINT(123,L"123"); | ||
157 | TEMPLATE_CA_ENTRYPOINT(124,L"124"); | ||
158 | TEMPLATE_CA_ENTRYPOINT(125,L"125"); | ||
159 | TEMPLATE_CA_ENTRYPOINT(126,L"126"); | ||
160 | TEMPLATE_CA_ENTRYPOINT(127,L"127"); | ||
161 | |||
162 | // Note: Keep in sync with EntryPoints.def | ||
diff --git a/src/dtf/SfxCA/Extract.cpp b/src/dtf/SfxCA/Extract.cpp new file mode 100644 index 00000000..171cf52f --- /dev/null +++ b/src/dtf/SfxCA/Extract.cpp | |||
@@ -0,0 +1,282 @@ | |||
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 | //--------------------------------------------------------------------- | ||
6 | // CABINET EXTRACTION | ||
7 | //--------------------------------------------------------------------- | ||
8 | |||
9 | // Globals make this code unsuited for multhreaded use, | ||
10 | // but FDI doesn't provide any other way to pass context. | ||
11 | |||
12 | // Handle to the FDI (cab extraction) engine. Need access to this in a callback. | ||
13 | static HFDI g_hfdi; | ||
14 | |||
15 | // FDI is not unicode-aware, so avoid passing these paths through the callbacks. | ||
16 | static const wchar_t* g_szExtractDir; | ||
17 | static const wchar_t* g_szCabFile; | ||
18 | |||
19 | // Offset into the source file where the cabinet really starts. | ||
20 | // Used to trick FDI into extracting from a concatenated cabinet. | ||
21 | static int g_lCabOffset; | ||
22 | |||
23 | // Use the secure CRT version of _wsopen if available. | ||
24 | #ifdef __GOT_SECURE_LIB__ | ||
25 | #define _wsopen__s(hf,file,oflag,shflag,pmode) _wsopen_s(&hf,file,oflag,shflag,pmode) | ||
26 | #else | ||
27 | #define _wsopen__s(hf,file,oflag,shflag,pmode) hf = _wsopen(file,oflag,shflag,pmode) | ||
28 | #endif | ||
29 | |||
30 | /// <summary> | ||
31 | /// FDI callback to open a cabinet file. | ||
32 | /// </summary> | ||
33 | /// <param name="pszFile">Name of the file to be opened. This parameter | ||
34 | /// is ignored since with our limited use this method is only ever called | ||
35 | /// to open the main cabinet file.</param> | ||
36 | /// <param name="oflag">Type of operations allowed.</param> | ||
37 | /// <param name="pmode">Permission setting.</param> | ||
38 | /// <returns>Integer file handle, or -1 if the file could not be opened.</returns> | ||
39 | /// <remarks> | ||
40 | /// To support reading from a cabinet that is concatenated onto | ||
41 | /// another file, this function first searches for the offset of the cabinet, | ||
42 | /// then saves that offset for use in recalculating later seeks. | ||
43 | /// </remarks> | ||
44 | static FNOPEN(CabOpen) | ||
45 | { | ||
46 | UNREFERENCED_PARAMETER(pszFile); | ||
47 | int hf; | ||
48 | _wsopen__s(hf, g_szCabFile, oflag, _SH_DENYWR, pmode); | ||
49 | if (hf != -1) | ||
50 | { | ||
51 | FDICABINETINFO cabInfo; | ||
52 | int length = _lseek(hf, 0, SEEK_END); | ||
53 | for(int offset = 0; offset < length; offset += 256) | ||
54 | { | ||
55 | if (_lseek(hf, offset, SEEK_SET) != offset) break; | ||
56 | if (FDIIsCabinet(g_hfdi, hf, &cabInfo)) | ||
57 | { | ||
58 | g_lCabOffset = offset; | ||
59 | _lseek(hf, offset, SEEK_SET); | ||
60 | return hf; | ||
61 | } | ||
62 | } | ||
63 | _close(hf); | ||
64 | } | ||
65 | return -1; | ||
66 | } | ||
67 | |||
68 | /// <summary> | ||
69 | /// FDI callback to seek within a file. | ||
70 | /// </summary> | ||
71 | /// <param name="hf">File handle.</param> | ||
72 | /// <param name="dist">Seek distance</param> | ||
73 | /// <param name="seektype">Whether to seek relative to the | ||
74 | /// beginning, current position, or end of the file.</param> | ||
75 | /// <returns>Resultant position within the cabinet.</returns> | ||
76 | /// <remarks> | ||
77 | /// To support reading from a cabinet that is concatenated onto | ||
78 | /// another file, this function recalculates seeks based on the | ||
79 | /// offset that was determined when the cabinet was opened. | ||
80 | /// </remarks> | ||
81 | static FNSEEK(CabSeek) | ||
82 | { | ||
83 | if (seektype == SEEK_SET) dist += g_lCabOffset; | ||
84 | int pos = _lseek((int) hf, dist, seektype); | ||
85 | pos -= g_lCabOffset; | ||
86 | return pos; | ||
87 | } | ||
88 | |||
89 | /// <summary> | ||
90 | /// Ensures a directory and its parent directory path exists. | ||
91 | /// </summary> | ||
92 | /// <param name="szDirPath">Directory path, not including file name.</param> | ||
93 | /// <returns>0 if the directory exists or was successfully created, else nonzero.</returns> | ||
94 | /// <remarks> | ||
95 | /// This function modifies characters in szDirPath, but always restores them | ||
96 | /// regardless of error condition. | ||
97 | /// </remarks> | ||
98 | static int EnsureDirectoryExists(__inout_z wchar_t* szDirPath) | ||
99 | { | ||
100 | int ret = 0; | ||
101 | if (!::CreateDirectoryW(szDirPath, NULL)) | ||
102 | { | ||
103 | UINT err = ::GetLastError(); | ||
104 | if (err != ERROR_ALREADY_EXISTS) | ||
105 | { | ||
106 | // Directory creation failed for some reason other than already existing. | ||
107 | // Try to create the parent directory first. | ||
108 | wchar_t* szLastSlash = NULL; | ||
109 | for (wchar_t* sz = szDirPath; *sz; sz++) | ||
110 | { | ||
111 | if (*sz == L'\\') | ||
112 | { | ||
113 | szLastSlash = sz; | ||
114 | } | ||
115 | } | ||
116 | if (szLastSlash) | ||
117 | { | ||
118 | // Temporarily take one directory off the path and recurse. | ||
119 | *szLastSlash = L'\0'; | ||
120 | ret = EnsureDirectoryExists(szDirPath); | ||
121 | *szLastSlash = L'\\'; | ||
122 | |||
123 | // Try to create the directory if all parents are created. | ||
124 | if (ret == 0 && !::CreateDirectoryW(szDirPath, NULL)) | ||
125 | { | ||
126 | err = ::GetLastError(); | ||
127 | if (err != ERROR_ALREADY_EXISTS) | ||
128 | { | ||
129 | ret = -1; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | ret = -1; | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | /// <summary> | ||
143 | /// Ensures a file's directory and its parent directory path exists. | ||
144 | /// </summary> | ||
145 | /// <param name="szDirPath">Path including file name.</param> | ||
146 | /// <returns>0 if the file's directory exists or was successfully created, else nonzero.</returns> | ||
147 | /// <remarks> | ||
148 | /// This function modifies characters in szFilePath, but always restores them | ||
149 | /// regardless of error condition. | ||
150 | /// </remarks> | ||
151 | static int EnsureFileDirectoryExists(__inout_z wchar_t* szFilePath) | ||
152 | { | ||
153 | int ret = 0; | ||
154 | wchar_t* szLastSlash = NULL; | ||
155 | for (wchar_t* sz = szFilePath; *sz; sz++) | ||
156 | { | ||
157 | if (*sz == L'\\') | ||
158 | { | ||
159 | szLastSlash = sz; | ||
160 | } | ||
161 | } | ||
162 | if (szLastSlash) | ||
163 | { | ||
164 | *szLastSlash = L'\0'; | ||
165 | ret = EnsureDirectoryExists(szFilePath); | ||
166 | *szLastSlash = L'\\'; | ||
167 | } | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | /// <summary> | ||
172 | /// FDI callback for handling files in the cabinet. | ||
173 | /// </summary> | ||
174 | /// <param name="fdint">Type of notification.</param> | ||
175 | /// <param name="pfdin">Structure containing data about the notification.</param> | ||
176 | /// <remarks> | ||
177 | /// Refer to fdi.h for more comments on this notification callback. | ||
178 | /// </remarks> | ||
179 | static FNFDINOTIFY(CabNotification) | ||
180 | { | ||
181 | // fdintCOPY_FILE: | ||
182 | // Called for each file that *starts* in the current cabinet, giving | ||
183 | // the client the opportunity to request that the file be copied or | ||
184 | // skipped. | ||
185 | // Entry: | ||
186 | // pfdin->psz1 = file name in cabinet | ||
187 | // pfdin->cb = uncompressed size of file | ||
188 | // pfdin->date = file date | ||
189 | // pfdin->time = file time | ||
190 | // pfdin->attribs = file attributes | ||
191 | // pfdin->iFolder = file's folder index | ||
192 | // Exit-Success: | ||
193 | // Return non-zero file handle for destination file; FDI writes | ||
194 | // data to this file use the PFNWRITE function supplied to FDICreate, | ||
195 | // and then calls fdintCLOSE_FILE_INFO to close the file and set | ||
196 | // the date, time, and attributes. | ||
197 | // Exit-Failure: | ||
198 | // Returns 0 => Skip file, do not copy | ||
199 | // Returns -1 => Abort FDICopy() call | ||
200 | if (fdint == fdintCOPY_FILE) | ||
201 | { | ||
202 | size_t cchFile = MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, NULL, 0); | ||
203 | size_t cchFilePath = wcslen(g_szExtractDir) + 1 + cchFile; | ||
204 | wchar_t* szFilePath = (wchar_t*) _alloca((cchFilePath + 1) * sizeof(wchar_t)); | ||
205 | if (szFilePath == NULL) return -1; | ||
206 | StringCchCopyW(szFilePath, cchFilePath + 1, g_szExtractDir); | ||
207 | StringCchCatW(szFilePath, cchFilePath + 1, L"\\"); | ||
208 | MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, | ||
209 | szFilePath + cchFilePath - cchFile, (int) cchFile + 1); | ||
210 | int hf = -1; | ||
211 | if (EnsureFileDirectoryExists(szFilePath) == 0) | ||
212 | { | ||
213 | _wsopen__s(hf, szFilePath, | ||
214 | _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, | ||
215 | _SH_DENYWR, _S_IREAD | _S_IWRITE); | ||
216 | } | ||
217 | return hf; | ||
218 | } | ||
219 | |||
220 | // fdintCLOSE_FILE_INFO: | ||
221 | // Called after all of the data has been written to a target file. | ||
222 | // This function must close the file and set the file date, time, | ||
223 | // and attributes. | ||
224 | // Entry: | ||
225 | // pfdin->psz1 = file name in cabinet | ||
226 | // pfdin->hf = file handle | ||
227 | // pfdin->date = file date | ||
228 | // pfdin->time = file time | ||
229 | // pfdin->attribs = file attributes | ||
230 | // pfdin->iFolder = file's folder index | ||
231 | // pfdin->cb = Run After Extract (0 - don't run, 1 Run) | ||
232 | // Exit-Success: | ||
233 | // Returns TRUE | ||
234 | // Exit-Failure: | ||
235 | // Returns FALSE, or -1 to abort | ||
236 | else if (fdint == fdintCLOSE_FILE_INFO) | ||
237 | { | ||
238 | _close((int) pfdin->hf); | ||
239 | return TRUE; | ||
240 | } | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | /// <summary> | ||
245 | /// Extracts all contents of a cabinet file to a directory. | ||
246 | /// </summary> | ||
247 | /// <param name="szCabFile">Path to the cabinet file to be extracted. | ||
248 | /// The cabinet may actually start at some offset within the file, | ||
249 | /// as long as that offset is a multiple of 256.</param> | ||
250 | /// <param name="szExtractDir">Directory where files are to be extracted. | ||
251 | /// This directory must already exist, but should be empty.</param> | ||
252 | /// <returns>0 if the cabinet was extracted successfully, | ||
253 | /// or an error code if any error occurred.</returns> | ||
254 | /// <remarks> | ||
255 | /// The extraction will not overwrite any files in the destination | ||
256 | /// directory; extraction will be interrupted and fail if any files | ||
257 | /// with the same name already exist. | ||
258 | /// </remarks> | ||
259 | int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir) | ||
260 | { | ||
261 | ERF erf; | ||
262 | // Most of the FDI callbacks can be handled by existing CRT I/O functions. | ||
263 | // For our functionality we only need to handle the open and seek callbacks. | ||
264 | HFDI hfdi = FDICreate((PFNALLOC) malloc, (PFNFREE) free, CabOpen, | ||
265 | (PFNREAD) _read, (PFNWRITE) _write, (PFNCLOSE) _close, | ||
266 | CabSeek, cpu80386, &erf); | ||
267 | if (hfdi != NULL) | ||
268 | { | ||
269 | g_hfdi = hfdi; | ||
270 | g_szCabFile = szCabFile; | ||
271 | g_szExtractDir = szExtractDir; | ||
272 | char szEmpty[1] = {0}; | ||
273 | if (FDICopy(hfdi, szEmpty, szEmpty, 0, CabNotification, NULL, NULL)) | ||
274 | { | ||
275 | FDIDestroy(hfdi); | ||
276 | return 0; | ||
277 | } | ||
278 | FDIDestroy(hfdi); | ||
279 | } | ||
280 | |||
281 | return erf.erfOper; | ||
282 | } | ||
diff --git a/src/dtf/SfxCA/RemoteMsi.cpp b/src/dtf/SfxCA/RemoteMsi.cpp new file mode 100644 index 00000000..ba59fdf7 --- /dev/null +++ b/src/dtf/SfxCA/RemoteMsi.cpp | |||
@@ -0,0 +1,629 @@ | |||
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 | #include "RemoteMsiSession.h" | ||
5 | |||
6 | |||
7 | // | ||
8 | // Ensures that the request buffer is large enough to hold a request, | ||
9 | // reallocating the buffer if necessary. | ||
10 | // It will also reduce the buffer size if the previous allocation was very large. | ||
11 | // | ||
12 | static __success(return == 0) UINT EnsureBufSize(__deref_out_ecount(*pcchBuf) wchar_t** pszBuf, __deref_inout DWORD* pcchBuf, DWORD cchRequired) | ||
13 | { | ||
14 | // It will also reduce the buffer size if the previous allocation was very large. | ||
15 | if (*pcchBuf < cchRequired || (LARGE_BUFFER_THRESHOLD/2 < *pcchBuf && cchRequired < *pcchBuf)) | ||
16 | { | ||
17 | if (*pszBuf != NULL) | ||
18 | { | ||
19 | SecureZeroMemory(*pszBuf, *pcchBuf); | ||
20 | delete[] *pszBuf; | ||
21 | } | ||
22 | |||
23 | *pcchBuf = max(MIN_BUFFER_STRING_SIZE, cchRequired); | ||
24 | *pszBuf = new wchar_t[*pcchBuf]; | ||
25 | |||
26 | if (*pszBuf == NULL) | ||
27 | { | ||
28 | return ERROR_OUTOFMEMORY; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | return ERROR_SUCCESS; | ||
33 | } | ||
34 | |||
35 | typedef int (WINAPI *PMsiFunc_I_I)(int in1, __out int* out1); | ||
36 | typedef int (WINAPI *PMsiFunc_II_I)(int in1, int in2, __out int* out1); | ||
37 | typedef int (WINAPI *PMsiFunc_IS_I)(int in1, __in_z wchar_t* in2, __out int* out1); | ||
38 | typedef int (WINAPI *PMsiFunc_ISI_I)(int in1, __in_z wchar_t* in2, int in3, __out int* out1); | ||
39 | typedef int (WINAPI *PMsiFunc_ISII_I)(int in1, __in_z wchar_t* in2, int in3, int in4, __out int* out1); | ||
40 | typedef int (WINAPI *PMsiFunc_IS_II)(int in1, __in_z wchar_t* in2, __out int* out1, __out int* out2); | ||
41 | typedef MSIDBERROR (WINAPI *PMsiEFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
42 | typedef int (WINAPI *PMsiFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
43 | typedef int (WINAPI *PMsiFunc_II_S)(int in1, int in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
44 | typedef int (WINAPI *PMsiFunc_IS_S)(int in1, __in_z wchar_t* in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1); | ||
45 | typedef int (WINAPI *PMsiFunc_ISII_SII)(int in1, __in_z wchar_t* in2, int in3, int in4, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1, __out int* out2, __out int* out3); | ||
46 | |||
47 | UINT MsiFunc_I_I(PMsiFunc_I_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp) | ||
48 | { | ||
49 | int in1 = pReq->fields[0].iValue; | ||
50 | int out1; | ||
51 | UINT ret = (UINT) func(in1, &out1); | ||
52 | if (ret == 0) | ||
53 | { | ||
54 | pResp->fields[1].vt = VT_I4; | ||
55 | pResp->fields[1].iValue = out1; | ||
56 | } | ||
57 | return ret; | ||
58 | } | ||
59 | |||
60 | UINT MsiFunc_II_I(PMsiFunc_II_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp) | ||
61 | { | ||
62 | int in1 = pReq->fields[0].iValue; | ||
63 | int in2 = pReq->fields[1].iValue; | ||
64 | int out1; | ||
65 | UINT ret = (UINT) func(in1, in2, &out1); | ||
66 | if (ret == 0) | ||
67 | { | ||
68 | pResp->fields[1].vt = VT_I4; | ||
69 | pResp->fields[1].iValue = out1; | ||
70 | } | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | UINT MsiFunc_IS_I(PMsiFunc_IS_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp) | ||
75 | { | ||
76 | int in1 = pReq->fields[0].iValue; | ||
77 | wchar_t* in2 = pReq->fields[1].szValue; | ||
78 | int out1; | ||
79 | UINT ret = (UINT) func(in1, in2, &out1); | ||
80 | if (ret == 0) | ||
81 | { | ||
82 | pResp->fields[1].vt = VT_I4; | ||
83 | pResp->fields[1].iValue = out1; | ||
84 | } | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | UINT MsiFunc_ISI_I(PMsiFunc_ISI_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp) | ||
89 | { | ||
90 | int in1 = pReq->fields[0].iValue; | ||
91 | wchar_t* in2 = pReq->fields[1].szValue; | ||
92 | int in3 = pReq->fields[2].iValue; | ||
93 | int out1; | ||
94 | UINT ret = (UINT) func(in1, in2, in3, &out1); | ||
95 | if (ret == 0) | ||
96 | { | ||
97 | pResp->fields[1].vt = VT_I4; | ||
98 | pResp->fields[1].iValue = out1; | ||
99 | } | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | UINT MsiFunc_ISII_I(PMsiFunc_ISII_I func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp) | ||
104 | { | ||
105 | int in1 = pReq->fields[0].iValue; | ||
106 | wchar_t* in2 = pReq->fields[1].szValue; | ||
107 | int in3 = pReq->fields[2].iValue; | ||
108 | int in4 = pReq->fields[3].iValue; | ||
109 | int out1; | ||
110 | UINT ret = (UINT) func(in1, in2, in3, in4, &out1); | ||
111 | if (ret == 0) | ||
112 | { | ||
113 | pResp->fields[1].vt = VT_I4; | ||
114 | pResp->fields[1].iValue = out1; | ||
115 | } | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | UINT MsiFunc_IS_II(PMsiFunc_IS_II func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp) | ||
120 | { | ||
121 | int in1 = pReq->fields[0].iValue; | ||
122 | wchar_t* in2 = pReq->fields[1].szValue; | ||
123 | int out1, out2; | ||
124 | UINT ret = (UINT) func(in1, in2, &out1, &out2); | ||
125 | if (ret == 0) | ||
126 | { | ||
127 | pResp->fields[1].vt = VT_I4; | ||
128 | pResp->fields[1].iValue = out1; | ||
129 | pResp->fields[2].vt = VT_I4; | ||
130 | pResp->fields[2].iValue = out2; | ||
131 | } | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | UINT MsiFunc_I_S(PMsiFunc_I_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf) | ||
136 | { | ||
137 | int in1 = pReq->fields[0].iValue; | ||
138 | szBuf[0] = L'\0'; | ||
139 | DWORD cchValue = cchBuf; | ||
140 | UINT ret = (UINT) func(in1, szBuf, &cchValue); | ||
141 | if (ret == ERROR_MORE_DATA) | ||
142 | { | ||
143 | ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue); | ||
144 | if (ret == 0) | ||
145 | { | ||
146 | ret = (UINT) func(in1, szBuf, &cchValue); | ||
147 | } | ||
148 | } | ||
149 | if (ret == 0) | ||
150 | { | ||
151 | pResp->fields[1].vt = VT_LPWSTR; | ||
152 | pResp->fields[1].szValue = szBuf; | ||
153 | } | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | MSIDBERROR MsiEFunc_I_S(PMsiEFunc_I_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf) | ||
158 | { | ||
159 | int in1 = pReq->fields[0].iValue; | ||
160 | szBuf[0] = L'\0'; | ||
161 | DWORD cchValue = cchBuf; | ||
162 | MSIDBERROR ret = func(in1, szBuf, &cchValue); | ||
163 | if (ret == MSIDBERROR_MOREDATA) | ||
164 | { | ||
165 | if (0 == EnsureBufSize(&szBuf, &cchBuf, ++cchValue)) | ||
166 | { | ||
167 | ret = func(in1, szBuf, &cchValue); | ||
168 | } | ||
169 | } | ||
170 | if (ret != MSIDBERROR_MOREDATA) | ||
171 | { | ||
172 | pResp->fields[1].vt = VT_LPWSTR; | ||
173 | pResp->fields[1].szValue = szBuf; | ||
174 | } | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | UINT MsiFunc_II_S(PMsiFunc_II_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf) | ||
179 | { | ||
180 | int in1 = pReq->fields[0].iValue; | ||
181 | int in2 = pReq->fields[1].iValue; | ||
182 | szBuf[0] = L'\0'; | ||
183 | DWORD cchValue = cchBuf; | ||
184 | UINT ret = (UINT) func(in1, in2, szBuf, &cchValue); | ||
185 | if (ret == ERROR_MORE_DATA) | ||
186 | { | ||
187 | ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue); | ||
188 | if (ret == 0) | ||
189 | { | ||
190 | ret = (UINT) func(in1, in2, szBuf, &cchValue); | ||
191 | } | ||
192 | } | ||
193 | if (ret == 0) | ||
194 | { | ||
195 | pResp->fields[1].vt = VT_LPWSTR; | ||
196 | pResp->fields[1].szValue = szBuf; | ||
197 | } | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | UINT MsiFunc_IS_S(PMsiFunc_IS_S func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf) | ||
202 | { | ||
203 | int in1 = pReq->fields[0].iValue; | ||
204 | wchar_t* in2 = pReq->fields[1].szValue; | ||
205 | szBuf[0] = L'\0'; | ||
206 | DWORD cchValue = cchBuf; | ||
207 | UINT ret = (UINT) func(in1, in2, szBuf, &cchValue); | ||
208 | if (ret == ERROR_MORE_DATA) | ||
209 | { | ||
210 | ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue); | ||
211 | if (ret == 0) | ||
212 | { | ||
213 | ret = (UINT) func(in1, in2, szBuf, &cchValue); | ||
214 | } | ||
215 | } | ||
216 | if (ret == 0) | ||
217 | { | ||
218 | pResp->fields[1].vt = VT_LPWSTR; | ||
219 | pResp->fields[1].szValue = szBuf; | ||
220 | } | ||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | UINT MsiFunc_ISII_SII(PMsiFunc_ISII_SII func, const RemoteMsiSession::RequestData* pReq, RemoteMsiSession::RequestData* pResp, __deref_inout_ecount(cchBuf) wchar_t*& szBuf, __inout DWORD& cchBuf) | ||
225 | { | ||
226 | int in1 = pReq->fields[0].iValue; | ||
227 | wchar_t* in2 = pReq->fields[1].szValue; | ||
228 | int in3 = pReq->fields[2].iValue; | ||
229 | int in4 = pReq->fields[3].iValue; | ||
230 | szBuf[0] = L'\0'; | ||
231 | DWORD cchValue = cchBuf; | ||
232 | int out2, out3; | ||
233 | UINT ret = (UINT) func(in1, in2, in3, in4, szBuf, &cchValue, &out2, &out3); | ||
234 | if (ret == ERROR_MORE_DATA) | ||
235 | { | ||
236 | ret = EnsureBufSize(&szBuf, &cchBuf, ++cchValue); | ||
237 | if (ret == 0) | ||
238 | { | ||
239 | ret = (UINT) func(in1, in2, in3, in4, szBuf, &cchValue, &out2, &out3); | ||
240 | } | ||
241 | } | ||
242 | if (ret == 0) | ||
243 | { | ||
244 | pResp->fields[1].vt = VT_LPWSTR; | ||
245 | pResp->fields[1].szValue = szBuf; | ||
246 | pResp->fields[2].vt = VT_I4; | ||
247 | pResp->fields[2].iValue = out2; | ||
248 | pResp->fields[3].vt = VT_I4; | ||
249 | pResp->fields[3].iValue = out3; | ||
250 | } | ||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | void RemoteMsiSession::ProcessRequest(RequestId id, const RequestData* pReq, RequestData* pResp) | ||
255 | { | ||
256 | SecureZeroMemory(pResp, sizeof(RequestData)); | ||
257 | |||
258 | UINT ret = EnsureBufSize(&m_pBufSend, &m_cbBufSend, 1024); | ||
259 | |||
260 | if (0 == ret) | ||
261 | { | ||
262 | switch (id) | ||
263 | { | ||
264 | case RemoteMsiSession::EndSession: | ||
265 | { | ||
266 | this->ExitCode = pReq->fields[0].iValue; | ||
267 | } | ||
268 | break; | ||
269 | case RemoteMsiSession::MsiCloseHandle: | ||
270 | { | ||
271 | MSIHANDLE h = (MSIHANDLE) pReq->fields[0].iValue; | ||
272 | ret = ::MsiCloseHandle(h); | ||
273 | } | ||
274 | break; | ||
275 | case RemoteMsiSession::MsiProcessMessage: | ||
276 | { | ||
277 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
278 | INSTALLMESSAGE eMessageType = (INSTALLMESSAGE) pReq->fields[1].iValue; | ||
279 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[2].iValue; | ||
280 | ret = ::MsiProcessMessage(hInstall, eMessageType, hRecord); | ||
281 | } | ||
282 | break; | ||
283 | case RemoteMsiSession::MsiGetProperty: | ||
284 | { | ||
285 | ret = MsiFunc_IS_S((PMsiFunc_IS_S) ::MsiGetProperty, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
286 | } | ||
287 | break; | ||
288 | case RemoteMsiSession::MsiSetProperty: | ||
289 | { | ||
290 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
291 | const wchar_t* szName = pReq->fields[1].szValue; | ||
292 | const wchar_t* szValue = pReq->fields[2].szValue; | ||
293 | ret = ::MsiSetProperty(hInstall, szName, szValue); | ||
294 | } | ||
295 | break; | ||
296 | case RemoteMsiSession::MsiCreateRecord: | ||
297 | { | ||
298 | UINT cParams = pReq->fields[0].uiValue; | ||
299 | ret = ::MsiCreateRecord(cParams); | ||
300 | } | ||
301 | break; | ||
302 | case RemoteMsiSession::MsiRecordGetFieldCount: | ||
303 | { | ||
304 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
305 | ret = ::MsiRecordGetFieldCount(hRecord); | ||
306 | } | ||
307 | break; | ||
308 | case RemoteMsiSession::MsiRecordGetInteger: | ||
309 | { | ||
310 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
311 | UINT iField = pReq->fields[1].uiValue; | ||
312 | ret = ::MsiRecordGetInteger(hRecord, iField); | ||
313 | } | ||
314 | break; | ||
315 | case RemoteMsiSession::MsiRecordSetInteger: | ||
316 | { | ||
317 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
318 | UINT iField = pReq->fields[1].uiValue; | ||
319 | int iValue = pReq->fields[2].iValue; | ||
320 | ret = ::MsiRecordSetInteger(hRecord, iField, iValue); | ||
321 | } | ||
322 | break; | ||
323 | case RemoteMsiSession::MsiRecordGetString: | ||
324 | { | ||
325 | ret = MsiFunc_II_S((PMsiFunc_II_S) ::MsiRecordGetString, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
326 | } | ||
327 | break; | ||
328 | case RemoteMsiSession::MsiRecordSetString: | ||
329 | { | ||
330 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
331 | UINT iField = pReq->fields[1].uiValue; | ||
332 | const wchar_t* szValue = pReq->fields[2].szValue; | ||
333 | ret = ::MsiRecordSetString(hRecord, iField, szValue); | ||
334 | } | ||
335 | break; | ||
336 | case RemoteMsiSession::MsiRecordClearData: | ||
337 | { | ||
338 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
339 | ret = ::MsiRecordClearData(hRecord); | ||
340 | } | ||
341 | break; | ||
342 | case RemoteMsiSession::MsiRecordIsNull: | ||
343 | { | ||
344 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
345 | UINT iField = pReq->fields[1].uiValue; | ||
346 | ret = ::MsiRecordIsNull(hRecord, iField); | ||
347 | } | ||
348 | break; | ||
349 | case RemoteMsiSession::MsiFormatRecord: | ||
350 | { | ||
351 | ret = MsiFunc_II_S((PMsiFunc_II_S) ::MsiFormatRecord, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
352 | } | ||
353 | break; | ||
354 | case RemoteMsiSession::MsiGetActiveDatabase: | ||
355 | { | ||
356 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
357 | ret = (UINT) ::MsiGetActiveDatabase(hInstall); | ||
358 | } | ||
359 | break; | ||
360 | case RemoteMsiSession::MsiDatabaseOpenView: | ||
361 | { | ||
362 | ret = MsiFunc_IS_I((PMsiFunc_IS_I) ::MsiDatabaseOpenView, pReq, pResp); | ||
363 | } | ||
364 | break; | ||
365 | case RemoteMsiSession::MsiViewExecute: | ||
366 | { | ||
367 | MSIHANDLE hView = (MSIHANDLE) pReq->fields[0].iValue; | ||
368 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[1].iValue; | ||
369 | ret = ::MsiViewExecute(hView, hRecord); | ||
370 | } | ||
371 | break; | ||
372 | case RemoteMsiSession::MsiViewFetch: | ||
373 | { | ||
374 | ret = MsiFunc_I_I((PMsiFunc_I_I) ::MsiViewFetch, pReq, pResp); | ||
375 | } | ||
376 | break; | ||
377 | case RemoteMsiSession::MsiViewModify: | ||
378 | { | ||
379 | MSIHANDLE hView = (MSIHANDLE) pReq->fields[0].iValue; | ||
380 | MSIMODIFY eModifyMode = (MSIMODIFY) pReq->fields[1].iValue; | ||
381 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[2].iValue; | ||
382 | ret = ::MsiViewModify(hView, eModifyMode, hRecord); | ||
383 | } | ||
384 | break; | ||
385 | case RemoteMsiSession::MsiViewGetError: | ||
386 | { | ||
387 | ret = MsiEFunc_I_S((PMsiEFunc_I_S) ::MsiViewGetError, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
388 | } | ||
389 | break; | ||
390 | case RemoteMsiSession::MsiViewGetColumnInfo: | ||
391 | { | ||
392 | ret = MsiFunc_II_I((PMsiFunc_II_I) ::MsiViewGetColumnInfo, pReq, pResp); | ||
393 | } | ||
394 | break; | ||
395 | case RemoteMsiSession::MsiDatabaseGetPrimaryKeys: | ||
396 | { | ||
397 | ret = MsiFunc_IS_I((PMsiFunc_IS_I) ::MsiDatabaseGetPrimaryKeys, pReq, pResp); | ||
398 | } | ||
399 | break; | ||
400 | case RemoteMsiSession::MsiDatabaseIsTablePersistent: | ||
401 | { | ||
402 | MSIHANDLE hDb = (MSIHANDLE) pReq->fields[0].iValue; | ||
403 | const wchar_t* szTable = pReq->fields[1].szValue; | ||
404 | ret = ::MsiDatabaseIsTablePersistent(hDb, szTable); | ||
405 | } | ||
406 | break; | ||
407 | case RemoteMsiSession::MsiDoAction: | ||
408 | { | ||
409 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
410 | const wchar_t* szAction = pReq->fields[1].szValue; | ||
411 | ret = ::MsiDoAction(hInstall, szAction); | ||
412 | } | ||
413 | break; | ||
414 | case RemoteMsiSession::MsiEnumComponentCosts: | ||
415 | { | ||
416 | ret = MsiFunc_ISII_SII((PMsiFunc_ISII_SII) ::MsiEnumComponentCosts, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
417 | } | ||
418 | break; | ||
419 | case RemoteMsiSession::MsiEvaluateCondition: | ||
420 | { | ||
421 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
422 | const wchar_t* szCondition = pReq->fields[1].szValue; | ||
423 | ret = ::MsiEvaluateCondition(hInstall, szCondition); | ||
424 | } | ||
425 | break; | ||
426 | case RemoteMsiSession::MsiGetComponentState: | ||
427 | { | ||
428 | ret = MsiFunc_IS_II((PMsiFunc_IS_II) ::MsiGetComponentState, pReq, pResp); | ||
429 | } | ||
430 | break; | ||
431 | case RemoteMsiSession::MsiGetFeatureCost: | ||
432 | { | ||
433 | ret = MsiFunc_ISII_I((PMsiFunc_ISII_I) ::MsiGetFeatureCost, pReq, pResp); | ||
434 | } | ||
435 | break; | ||
436 | case RemoteMsiSession::MsiGetFeatureState: | ||
437 | { | ||
438 | ret = MsiFunc_IS_II((PMsiFunc_IS_II) ::MsiGetFeatureState, pReq, pResp); | ||
439 | } | ||
440 | break; | ||
441 | case RemoteMsiSession::MsiGetFeatureValidStates: | ||
442 | { | ||
443 | ret = MsiFunc_IS_I((PMsiFunc_IS_I) ::MsiGetFeatureValidStates, pReq, pResp); | ||
444 | } | ||
445 | break; | ||
446 | case RemoteMsiSession::MsiGetLanguage: | ||
447 | { | ||
448 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
449 | ret = ::MsiGetLanguage(hInstall); | ||
450 | } | ||
451 | break; | ||
452 | case RemoteMsiSession::MsiGetLastErrorRecord: | ||
453 | { | ||
454 | ret = ::MsiGetLastErrorRecord(); | ||
455 | } | ||
456 | break; | ||
457 | case RemoteMsiSession::MsiGetMode: | ||
458 | { | ||
459 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
460 | MSIRUNMODE iRunMode = (MSIRUNMODE) pReq->fields[1].iValue; | ||
461 | ret = ::MsiGetMode(hInstall, iRunMode); | ||
462 | } | ||
463 | break; | ||
464 | case RemoteMsiSession::MsiGetSourcePath: | ||
465 | { | ||
466 | ret = MsiFunc_IS_S((PMsiFunc_IS_S) ::MsiGetSourcePath, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
467 | } | ||
468 | break; | ||
469 | case RemoteMsiSession::MsiGetSummaryInformation: | ||
470 | { | ||
471 | ret = MsiFunc_ISI_I((PMsiFunc_ISI_I) ::MsiGetSummaryInformation, pReq, pResp); | ||
472 | } | ||
473 | break; | ||
474 | case RemoteMsiSession::MsiGetTargetPath: | ||
475 | { | ||
476 | ret = MsiFunc_IS_S((PMsiFunc_IS_S) ::MsiGetTargetPath, pReq, pResp, m_pBufSend, m_cbBufSend); | ||
477 | } | ||
478 | break; | ||
479 | case RemoteMsiSession::MsiRecordDataSize: | ||
480 | { | ||
481 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
482 | UINT iField = pReq->fields[1].uiValue; | ||
483 | ret = ::MsiRecordDataSize(hRecord, iField); | ||
484 | } | ||
485 | break; | ||
486 | case RemoteMsiSession::MsiRecordReadStream: | ||
487 | { | ||
488 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
489 | UINT iField = pReq->fields[1].uiValue; | ||
490 | DWORD cbRead = (DWORD) pReq->fields[2].uiValue; | ||
491 | ret = EnsureBufSize(&m_pBufSend, &m_cbBufSend, (cbRead + 1) / 2); | ||
492 | if (ret == 0) | ||
493 | { | ||
494 | ret = ::MsiRecordReadStream(hRecord, iField, (char*) m_pBufSend, &cbRead); | ||
495 | if (ret == 0) | ||
496 | { | ||
497 | pResp->fields[1].vt = VT_STREAM; | ||
498 | pResp->fields[1].szValue = m_pBufSend; | ||
499 | pResp->fields[2].vt = VT_I4; | ||
500 | pResp->fields[2].uiValue = (UINT) cbRead; | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | break; | ||
505 | case RemoteMsiSession::MsiRecordSetStream: | ||
506 | { | ||
507 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
508 | UINT iField = pReq->fields[1].uiValue; | ||
509 | const wchar_t* szFilePath = pReq->fields[2].szValue; | ||
510 | ret = ::MsiRecordSetStream(hRecord, iField, szFilePath); | ||
511 | } | ||
512 | break; | ||
513 | case RemoteMsiSession::MsiSequence: | ||
514 | { | ||
515 | MSIHANDLE hRecord = (MSIHANDLE) pReq->fields[0].iValue; | ||
516 | const wchar_t* szTable = pReq->fields[1].szValue; | ||
517 | UINT iSequenceMode = pReq->fields[2].uiValue; | ||
518 | ret = ::MsiSequence(hRecord, szTable, iSequenceMode); | ||
519 | } | ||
520 | break; | ||
521 | case RemoteMsiSession::MsiSetComponentState: | ||
522 | { | ||
523 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
524 | const wchar_t* szComponent = pReq->fields[1].szValue; | ||
525 | INSTALLSTATE iState = (INSTALLSTATE) pReq->fields[2].iValue; | ||
526 | ret = ::MsiSetComponentState(hInstall, szComponent, iState); | ||
527 | } | ||
528 | break; | ||
529 | case RemoteMsiSession::MsiSetFeatureAttributes: | ||
530 | { | ||
531 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
532 | const wchar_t* szFeature = pReq->fields[1].szValue; | ||
533 | DWORD dwAttrs = (DWORD) pReq->fields[2].uiValue; | ||
534 | ret = ::MsiSetFeatureAttributes(hInstall, szFeature, dwAttrs); | ||
535 | } | ||
536 | break; | ||
537 | case RemoteMsiSession::MsiSetFeatureState: | ||
538 | { | ||
539 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
540 | const wchar_t* szFeature = pReq->fields[1].szValue; | ||
541 | INSTALLSTATE iState = (INSTALLSTATE) pReq->fields[2].iValue; | ||
542 | ret = ::MsiSetFeatureState(hInstall, szFeature, iState); | ||
543 | } | ||
544 | break; | ||
545 | case RemoteMsiSession::MsiSetInstallLevel: | ||
546 | { | ||
547 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
548 | int iInstallLevel = pReq->fields[1].iValue; | ||
549 | ret = ::MsiSetInstallLevel(hInstall, iInstallLevel); | ||
550 | } | ||
551 | break; | ||
552 | case RemoteMsiSession::MsiSetMode: | ||
553 | { | ||
554 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
555 | MSIRUNMODE iRunMode = (MSIRUNMODE) pReq->fields[1].uiValue; | ||
556 | BOOL fState = (BOOL) pReq->fields[2].iValue; | ||
557 | ret = ::MsiSetMode(hInstall, iRunMode, fState); | ||
558 | } | ||
559 | break; | ||
560 | case RemoteMsiSession::MsiSetTargetPath: | ||
561 | { | ||
562 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
563 | const wchar_t* szFolder = pReq->fields[1].szValue; | ||
564 | const wchar_t* szFolderPath = pReq->fields[2].szValue; | ||
565 | ret = ::MsiSetTargetPath(hInstall, szFolder, szFolderPath); | ||
566 | } | ||
567 | break; | ||
568 | case RemoteMsiSession::MsiSummaryInfoGetProperty: | ||
569 | { | ||
570 | MSIHANDLE hSummaryInfo = (MSIHANDLE) pReq->fields[0].iValue; | ||
571 | UINT uiProperty = pReq->fields[1].uiValue; | ||
572 | UINT uiDataType; | ||
573 | int iValue; | ||
574 | FILETIME ftValue; | ||
575 | m_pBufSend[0] = L'\0'; | ||
576 | DWORD cchValue = m_cbBufSend; | ||
577 | ret = ::MsiSummaryInfoGetProperty(hSummaryInfo, uiProperty, &uiDataType, &iValue, &ftValue, m_pBufSend, &cchValue); | ||
578 | if (ret == ERROR_MORE_DATA) | ||
579 | { | ||
580 | ret = EnsureBufSize(&m_pBufSend, &m_cbBufSend, ++cchValue); | ||
581 | if (ret == 0) | ||
582 | { | ||
583 | ret = ::MsiSummaryInfoGetProperty(hSummaryInfo, uiProperty, &uiDataType, &iValue, &ftValue, m_pBufSend, &cchValue); | ||
584 | } | ||
585 | } | ||
586 | if (ret == 0) | ||
587 | { | ||
588 | pResp->fields[1].vt = VT_UI4; | ||
589 | pResp->fields[1].uiValue = uiDataType; | ||
590 | |||
591 | switch (uiDataType) | ||
592 | { | ||
593 | case VT_I2: | ||
594 | case VT_I4: | ||
595 | pResp->fields[2].vt = VT_I4; | ||
596 | pResp->fields[2].iValue = iValue; | ||
597 | break; | ||
598 | case VT_FILETIME: | ||
599 | pResp->fields[2].vt = VT_UI4; | ||
600 | pResp->fields[2].iValue = ftValue.dwHighDateTime; | ||
601 | pResp->fields[3].vt = VT_UI4; | ||
602 | pResp->fields[3].iValue = ftValue.dwLowDateTime; | ||
603 | break; | ||
604 | case VT_LPSTR: | ||
605 | pResp->fields[2].vt = VT_LPWSTR; | ||
606 | pResp->fields[2].szValue = m_pBufSend; | ||
607 | break; | ||
608 | } | ||
609 | } | ||
610 | } | ||
611 | break; | ||
612 | case RemoteMsiSession::MsiVerifyDiskSpace: | ||
613 | { | ||
614 | MSIHANDLE hInstall = (MSIHANDLE) pReq->fields[0].iValue; | ||
615 | ret = ::MsiVerifyDiskSpace(hInstall); | ||
616 | } | ||
617 | break; | ||
618 | |||
619 | default: | ||
620 | { | ||
621 | ret = ERROR_INVALID_FUNCTION; | ||
622 | } | ||
623 | break; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | pResp->fields[0].vt = VT_UI4; | ||
628 | pResp->fields[0].uiValue = ret; | ||
629 | } | ||
diff --git a/src/dtf/SfxCA/RemoteMsiSession.h b/src/dtf/SfxCA/RemoteMsiSession.h new file mode 100644 index 00000000..90c7c01f --- /dev/null +++ b/src/dtf/SfxCA/RemoteMsiSession.h | |||
@@ -0,0 +1,898 @@ | |||
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 | #define LARGE_BUFFER_THRESHOLD 65536 // bytes | ||
4 | #define MIN_BUFFER_STRING_SIZE 1024 // wchar_ts | ||
5 | |||
6 | /////////////////////////////////////////////////////////////////////////////////////// | ||
7 | // RemoteMsiSession // | ||
8 | ////////////////////// | ||
9 | // | ||
10 | // Allows accessing MSI APIs from another process using named pipes. | ||
11 | // | ||
12 | class RemoteMsiSession | ||
13 | { | ||
14 | public: | ||
15 | |||
16 | // This enumeration MUST stay in sync with the | ||
17 | // managed equivalent in RemotableNativeMethods.cs! | ||
18 | enum RequestId | ||
19 | { | ||
20 | EndSession = 0, | ||
21 | MsiCloseHandle, | ||
22 | MsiCreateRecord, | ||
23 | MsiDatabaseGetPrimaryKeys, | ||
24 | MsiDatabaseIsTablePersistent, | ||
25 | MsiDatabaseOpenView, | ||
26 | MsiDoAction, | ||
27 | MsiEnumComponentCosts, | ||
28 | MsiEvaluateCondition, | ||
29 | MsiFormatRecord, | ||
30 | MsiGetActiveDatabase, | ||
31 | MsiGetComponentState, | ||
32 | MsiGetFeatureCost, | ||
33 | MsiGetFeatureState, | ||
34 | MsiGetFeatureValidStates, | ||
35 | MsiGetLanguage, | ||
36 | MsiGetLastErrorRecord, | ||
37 | MsiGetMode, | ||
38 | MsiGetProperty, | ||
39 | MsiGetSourcePath, | ||
40 | MsiGetSummaryInformation, | ||
41 | MsiGetTargetPath, | ||
42 | MsiProcessMessage, | ||
43 | MsiRecordClearData, | ||
44 | MsiRecordDataSize, | ||
45 | MsiRecordGetFieldCount, | ||
46 | MsiRecordGetInteger, | ||
47 | MsiRecordGetString, | ||
48 | MsiRecordIsNull, | ||
49 | MsiRecordReadStream, | ||
50 | MsiRecordSetInteger, | ||
51 | MsiRecordSetStream, | ||
52 | MsiRecordSetString, | ||
53 | MsiSequence, | ||
54 | MsiSetComponentState, | ||
55 | MsiSetFeatureAttributes, | ||
56 | MsiSetFeatureState, | ||
57 | MsiSetInstallLevel, | ||
58 | MsiSetMode, | ||
59 | MsiSetProperty, | ||
60 | MsiSetTargetPath, | ||
61 | MsiSummaryInfoGetProperty, | ||
62 | MsiVerifyDiskSpace, | ||
63 | MsiViewExecute, | ||
64 | MsiViewFetch, | ||
65 | MsiViewGetError, | ||
66 | MsiViewGetColumnInfo, | ||
67 | MsiViewModify, | ||
68 | }; | ||
69 | |||
70 | static const int MAX_REQUEST_FIELDS = 4; | ||
71 | |||
72 | // Used to pass data back and forth for remote API calls, | ||
73 | // including in & out params & return values. | ||
74 | // Only strings and ints are supported. | ||
75 | struct RequestData | ||
76 | { | ||
77 | struct | ||
78 | { | ||
79 | VARENUM vt; | ||
80 | union { | ||
81 | int iValue; | ||
82 | UINT uiValue; | ||
83 | DWORD cchValue; | ||
84 | LPWSTR szValue; | ||
85 | BYTE* sValue; | ||
86 | DWORD cbValue; | ||
87 | }; | ||
88 | } fields[MAX_REQUEST_FIELDS]; | ||
89 | }; | ||
90 | |||
91 | public: | ||
92 | |||
93 | // This value is set from the single data parameter in the EndSession request. | ||
94 | // It saves the exit code of the out-of-proc custom action. | ||
95 | int ExitCode; | ||
96 | |||
97 | ///////////////////////////////////////////////////////////////////////////////////// | ||
98 | // RemoteMsiSession constructor | ||
99 | // | ||
100 | // Creates a new remote session instance, for use either by the server | ||
101 | // or client process. | ||
102 | // | ||
103 | // szName - Identifies the session instance being remoted. The server and | ||
104 | // the client must use the same name. The name should be unique | ||
105 | // enough to avoid conflicting with other instances on the system. | ||
106 | // | ||
107 | // fServer - True if the calling process is the server process, false if the | ||
108 | // calling process is the client process. | ||
109 | // | ||
110 | RemoteMsiSession(const wchar_t* szName, bool fServer=true) | ||
111 | : m_fServer(fServer), | ||
112 | m_szName(szName != NULL && szName[0] != L'\0' ? szName : L"RemoteMsiSession"), | ||
113 | m_szPipeName(NULL), | ||
114 | m_hPipe(NULL), | ||
115 | m_fConnecting(false), | ||
116 | m_fConnected(false), | ||
117 | m_hReceiveThread(NULL), | ||
118 | m_hReceiveStopEvent(NULL), | ||
119 | m_pBufReceive(NULL), | ||
120 | m_cbBufReceive(0), | ||
121 | m_pBufSend(NULL), | ||
122 | m_cbBufSend(0), | ||
123 | ExitCode(ERROR_INSTALL_FAILURE) | ||
124 | { | ||
125 | SecureZeroMemory(&m_overlapped, sizeof(OVERLAPPED)); | ||
126 | m_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
127 | } | ||
128 | |||
129 | ///////////////////////////////////////////////////////////////////////////////////// | ||
130 | // RemoteMsiSession destructor | ||
131 | // | ||
132 | // Closes any open handles and frees any allocated memory. | ||
133 | // | ||
134 | ~RemoteMsiSession() | ||
135 | { | ||
136 | WaitExitCode(); | ||
137 | if (m_hPipe != NULL) | ||
138 | { | ||
139 | CloseHandle(m_hPipe); | ||
140 | m_hPipe = NULL; | ||
141 | } | ||
142 | if (m_overlapped.hEvent != NULL) | ||
143 | { | ||
144 | CloseHandle(m_overlapped.hEvent); | ||
145 | m_overlapped.hEvent = NULL; | ||
146 | } | ||
147 | if (m_szPipeName != NULL) | ||
148 | { | ||
149 | delete[] m_szPipeName; | ||
150 | m_szPipeName = NULL; | ||
151 | } | ||
152 | if (m_pBufReceive != NULL) | ||
153 | { | ||
154 | SecureZeroMemory(m_pBufReceive, m_cbBufReceive); | ||
155 | delete[] m_pBufReceive; | ||
156 | m_pBufReceive = NULL; | ||
157 | } | ||
158 | if (m_pBufSend != NULL) | ||
159 | { | ||
160 | SecureZeroMemory(m_pBufSend, m_cbBufSend); | ||
161 | delete[] m_pBufSend; | ||
162 | m_pBufSend = NULL; | ||
163 | } | ||
164 | m_fConnecting = false; | ||
165 | m_fConnected = false; | ||
166 | } | ||
167 | |||
168 | ///////////////////////////////////////////////////////////////////////////////////// | ||
169 | // RemoteMsiSession::WaitExitCode() | ||
170 | // | ||
171 | // Waits for the server processing thread to complete. | ||
172 | // | ||
173 | void WaitExitCode() | ||
174 | { | ||
175 | if (m_hReceiveThread != NULL) | ||
176 | { | ||
177 | SetEvent(m_hReceiveStopEvent); | ||
178 | WaitForSingleObject(m_hReceiveThread, INFINITE); | ||
179 | CloseHandle(m_hReceiveThread); | ||
180 | m_hReceiveThread = NULL; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | ///////////////////////////////////////////////////////////////////////////////////// | ||
185 | // RemoteMsiSession::Connect() | ||
186 | // | ||
187 | // Connects the inter-process communication channel. | ||
188 | // (Currently implemented as a named pipe.) | ||
189 | // | ||
190 | // This method must be called first by the server process, then by the client | ||
191 | // process. The method does not block; the server will asynchronously wait | ||
192 | // for the client process to make the connection. | ||
193 | // | ||
194 | // Returns: 0 on success, Win32 error code on failure. | ||
195 | // | ||
196 | virtual DWORD Connect() | ||
197 | { | ||
198 | const wchar_t* szPipePrefix = L"\\\\.\\pipe\\"; | ||
199 | size_t cchPipeNameBuf = wcslen(szPipePrefix) + wcslen(m_szName) + 1; | ||
200 | m_szPipeName = new wchar_t[cchPipeNameBuf]; | ||
201 | |||
202 | if (m_szPipeName == NULL) | ||
203 | { | ||
204 | return ERROR_OUTOFMEMORY; | ||
205 | } | ||
206 | else | ||
207 | { | ||
208 | wcscpy_s(m_szPipeName, cchPipeNameBuf, szPipePrefix); | ||
209 | wcscat_s(m_szPipeName, cchPipeNameBuf, m_szName); | ||
210 | |||
211 | if (m_fServer) | ||
212 | { | ||
213 | return this->ConnectPipeServer(); | ||
214 | } | ||
215 | else | ||
216 | { | ||
217 | return this->ConnectPipeClient(); | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | |||
222 | ///////////////////////////////////////////////////////////////////////////////////// | ||
223 | // RemoteMsiSession::IsConnected() | ||
224 | // | ||
225 | // Checks if the server process and client process are currently connected. | ||
226 | // | ||
227 | virtual bool IsConnected() const | ||
228 | { | ||
229 | return m_fConnected; | ||
230 | } | ||
231 | |||
232 | ///////////////////////////////////////////////////////////////////////////////////// | ||
233 | // RemoteMsiSession::ProcessRequests() | ||
234 | // | ||
235 | // For use by the service process. Watches for requests in the input buffer and calls | ||
236 | // the callback for each one. | ||
237 | // | ||
238 | // This method does not block; it spawns a separate thread to do the work. | ||
239 | // | ||
240 | // Returns: 0 on success, Win32 error code on failure. | ||
241 | // | ||
242 | virtual DWORD ProcessRequests() | ||
243 | { | ||
244 | return this->StartProcessingReqests(); | ||
245 | } | ||
246 | |||
247 | ///////////////////////////////////////////////////////////////////////////////////// | ||
248 | // RemoteMsiSession::SendRequest() | ||
249 | // | ||
250 | // For use by the client process. Sends a request to the server and | ||
251 | // synchronously waits on a response, up to the timeout value. | ||
252 | // | ||
253 | // id - ID code of the MSI API call being requested. | ||
254 | // | ||
255 | // pRequest - Pointer to a data structure containing request parameters. | ||
256 | // | ||
257 | // ppResponse - [OUT] Pointer to a location that receives the response parameters. | ||
258 | // | ||
259 | // Returns: 0 on success, Win32 error code on failure. | ||
260 | // Returns WAIT_TIMEOUT if no response was received in time. | ||
261 | // | ||
262 | virtual DWORD SendRequest(RequestId id, const RequestData* pRequest, RequestData** ppResponse) | ||
263 | { | ||
264 | if (m_fServer) | ||
265 | { | ||
266 | return ERROR_INVALID_OPERATION; | ||
267 | } | ||
268 | |||
269 | if (!m_fConnected) | ||
270 | { | ||
271 | *ppResponse = NULL; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | DWORD dwRet = this->SendRequest(id, pRequest); | ||
276 | if (dwRet != 0) | ||
277 | { | ||
278 | return dwRet; | ||
279 | } | ||
280 | |||
281 | if (id != EndSession) | ||
282 | { | ||
283 | static RequestData response; | ||
284 | if (ppResponse != NULL) | ||
285 | { | ||
286 | *ppResponse = &response; | ||
287 | } | ||
288 | |||
289 | return this->ReceiveResponse(id, &response); | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | CloseHandle(m_hPipe); | ||
294 | m_hPipe = NULL; | ||
295 | m_fConnected = false; | ||
296 | return 0; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | private: | ||
301 | |||
302 | // | ||
303 | // Do not allow assignment. | ||
304 | // | ||
305 | RemoteMsiSession& operator=(const RemoteMsiSession&); | ||
306 | |||
307 | // | ||
308 | // Called only by the server process. | ||
309 | // Create a new thread to handle receiving requests. | ||
310 | // | ||
311 | DWORD StartProcessingReqests() | ||
312 | { | ||
313 | if (!m_fServer || m_hReceiveStopEvent != NULL) | ||
314 | { | ||
315 | return ERROR_INVALID_OPERATION; | ||
316 | } | ||
317 | |||
318 | DWORD dwRet = 0; | ||
319 | |||
320 | m_hReceiveStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
321 | |||
322 | if (m_hReceiveStopEvent == NULL) | ||
323 | { | ||
324 | dwRet = GetLastError(); | ||
325 | } | ||
326 | else | ||
327 | { | ||
328 | if (m_hReceiveThread != NULL) | ||
329 | { | ||
330 | CloseHandle(m_hReceiveThread); | ||
331 | } | ||
332 | |||
333 | m_hReceiveThread = CreateThread(NULL, 0, | ||
334 | RemoteMsiSession::ProcessRequestsThreadStatic, this, 0, NULL); | ||
335 | |||
336 | if (m_hReceiveThread == NULL) | ||
337 | { | ||
338 | dwRet = GetLastError(); | ||
339 | CloseHandle(m_hReceiveStopEvent); | ||
340 | m_hReceiveStopEvent = NULL; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | return dwRet; | ||
345 | } | ||
346 | |||
347 | // | ||
348 | // Called only by the watcher process. | ||
349 | // First verify the connection is complete. Then continually read and parse messages, | ||
350 | // invoke the callback, and send the replies. | ||
351 | // | ||
352 | static DWORD WINAPI ProcessRequestsThreadStatic(void* pv) | ||
353 | { | ||
354 | return reinterpret_cast<RemoteMsiSession*>(pv)->ProcessRequestsThread(); | ||
355 | } | ||
356 | |||
357 | DWORD ProcessRequestsThread() | ||
358 | { | ||
359 | DWORD dwRet; | ||
360 | |||
361 | dwRet = CompleteConnection(); | ||
362 | if (dwRet != 0) | ||
363 | { | ||
364 | if (dwRet == ERROR_OPERATION_ABORTED) dwRet = 0; | ||
365 | } | ||
366 | |||
367 | while (m_fConnected) | ||
368 | { | ||
369 | RequestId id; | ||
370 | RequestData req; | ||
371 | dwRet = ReceiveRequest(&id, &req); | ||
372 | if (dwRet != 0) | ||
373 | { | ||
374 | if (dwRet == ERROR_OPERATION_ABORTED || | ||
375 | dwRet == ERROR_BROKEN_PIPE || dwRet == ERROR_NO_DATA) | ||
376 | { | ||
377 | dwRet = 0; | ||
378 | } | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | RequestData resp; | ||
383 | ProcessRequest(id, &req, &resp); | ||
384 | |||
385 | if (id == EndSession) | ||
386 | { | ||
387 | break; | ||
388 | } | ||
389 | |||
390 | dwRet = SendResponse(id, &resp); | ||
391 | if (dwRet != 0 && dwRet != ERROR_BROKEN_PIPE && dwRet != ERROR_NO_DATA) | ||
392 | { | ||
393 | dwRet = 0; | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | CloseHandle(m_hReceiveStopEvent); | ||
399 | m_hReceiveStopEvent = NULL; | ||
400 | return dwRet; | ||
401 | } | ||
402 | |||
403 | // | ||
404 | // Called only by the server process's receive thread. | ||
405 | // Read one request into a RequestData object. | ||
406 | // | ||
407 | DWORD ReceiveRequest(RequestId* pId, RequestData* pReq) | ||
408 | { | ||
409 | DWORD dwRet = this->ReadPipe((BYTE*) pId, sizeof(RequestId)); | ||
410 | |||
411 | if (dwRet == 0) | ||
412 | { | ||
413 | dwRet = this->ReadRequestData(pReq); | ||
414 | } | ||
415 | |||
416 | return dwRet; | ||
417 | } | ||
418 | |||
419 | // | ||
420 | // Called by the server process's receive thread or the client's request call | ||
421 | // to read the response. Read data from the pipe, allowing interruption by the | ||
422 | // stop event if on the server. | ||
423 | // | ||
424 | DWORD ReadPipe(__out_bcount(cbRead) BYTE* pBuf, DWORD cbRead) | ||
425 | { | ||
426 | DWORD dwRet = 0; | ||
427 | DWORD dwTotalBytesRead = 0; | ||
428 | |||
429 | while (dwRet == 0 && dwTotalBytesRead < cbRead) | ||
430 | { | ||
431 | DWORD dwBytesReadThisTime; | ||
432 | ResetEvent(m_overlapped.hEvent); | ||
433 | if (!ReadFile(m_hPipe, pBuf + dwTotalBytesRead, cbRead - dwTotalBytesRead, &dwBytesReadThisTime, &m_overlapped)) | ||
434 | { | ||
435 | dwRet = GetLastError(); | ||
436 | if (dwRet == ERROR_IO_PENDING) | ||
437 | { | ||
438 | if (m_fServer) | ||
439 | { | ||
440 | HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent }; | ||
441 | dwRet = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE); | ||
442 | } | ||
443 | else | ||
444 | { | ||
445 | dwRet = WaitForSingleObject(m_overlapped.hEvent, INFINITE); | ||
446 | } | ||
447 | |||
448 | if (dwRet == WAIT_OBJECT_0) | ||
449 | { | ||
450 | if (!GetOverlappedResult(m_hPipe, &m_overlapped, &dwBytesReadThisTime, FALSE)) | ||
451 | { | ||
452 | dwRet = GetLastError(); | ||
453 | } | ||
454 | } | ||
455 | else if (dwRet == WAIT_FAILED) | ||
456 | { | ||
457 | dwRet = GetLastError(); | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | dwRet = ERROR_OPERATION_ABORTED; | ||
462 | } | ||
463 | } | ||
464 | } | ||
465 | |||
466 | dwTotalBytesRead += dwBytesReadThisTime; | ||
467 | } | ||
468 | |||
469 | if (dwRet != 0) | ||
470 | { | ||
471 | if (m_fServer) | ||
472 | { | ||
473 | CancelIo(m_hPipe); | ||
474 | DisconnectNamedPipe(m_hPipe); | ||
475 | } | ||
476 | else | ||
477 | { | ||
478 | CloseHandle(m_hPipe); | ||
479 | m_hPipe = NULL; | ||
480 | } | ||
481 | m_fConnected = false; | ||
482 | } | ||
483 | |||
484 | return dwRet; | ||
485 | } | ||
486 | |||
487 | // | ||
488 | // Called only by the server process. | ||
489 | // Given a request, invoke the MSI API and return the response. | ||
490 | // This is implemented in RemoteMsi.cpp. | ||
491 | // | ||
492 | void ProcessRequest(RequestId id, const RequestData* pReq, RequestData* pResp); | ||
493 | |||
494 | // | ||
495 | // Called only by the client process. | ||
496 | // Send request data over the pipe. | ||
497 | // | ||
498 | DWORD SendRequest(RequestId id, const RequestData* pRequest) | ||
499 | { | ||
500 | DWORD dwRet = WriteRequestData(id, pRequest); | ||
501 | |||
502 | if (dwRet != 0) | ||
503 | { | ||
504 | m_fConnected = false; | ||
505 | CloseHandle(m_hPipe); | ||
506 | m_hPipe = NULL; | ||
507 | } | ||
508 | |||
509 | return dwRet; | ||
510 | } | ||
511 | |||
512 | // | ||
513 | // Called only by the server process. | ||
514 | // Just send a response over the pipe. | ||
515 | // | ||
516 | DWORD SendResponse(RequestId id, const RequestData* pResp) | ||
517 | { | ||
518 | DWORD dwRet = WriteRequestData(id, pResp); | ||
519 | |||
520 | if (dwRet != 0) | ||
521 | { | ||
522 | DisconnectNamedPipe(m_hPipe); | ||
523 | m_fConnected = false; | ||
524 | } | ||
525 | |||
526 | return dwRet; | ||
527 | } | ||
528 | |||
529 | // | ||
530 | // Called either by the client or server process. | ||
531 | // Writes data to the pipe for a request or response. | ||
532 | // | ||
533 | DWORD WriteRequestData(RequestId id, const RequestData* pReq) | ||
534 | { | ||
535 | DWORD dwRet = 0; | ||
536 | |||
537 | RequestData req = *pReq; // Make a copy because the const data can't be changed. | ||
538 | |||
539 | dwRet = this->WritePipe((const BYTE *)&id, sizeof(RequestId)); | ||
540 | if (dwRet != 0) | ||
541 | { | ||
542 | return dwRet; | ||
543 | } | ||
544 | |||
545 | BYTE* sValues[MAX_REQUEST_FIELDS] = {0}; | ||
546 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
547 | { | ||
548 | if (req.fields[i].vt == VT_LPWSTR) | ||
549 | { | ||
550 | sValues[i] = (BYTE*) req.fields[i].szValue; | ||
551 | req.fields[i].cchValue = (DWORD) wcslen(req.fields[i].szValue); | ||
552 | } | ||
553 | else if (req.fields[i].vt == VT_STREAM) | ||
554 | { | ||
555 | sValues[i] = req.fields[i].sValue; | ||
556 | req.fields[i].cbValue = (DWORD) req.fields[i + 1].uiValue; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | dwRet = this->WritePipe((const BYTE *)&req, sizeof(RequestData)); | ||
561 | if (dwRet != 0) | ||
562 | { | ||
563 | return dwRet; | ||
564 | } | ||
565 | |||
566 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
567 | { | ||
568 | if (sValues[i] != NULL) | ||
569 | { | ||
570 | DWORD cbValue; | ||
571 | if (req.fields[i].vt == VT_LPWSTR) | ||
572 | { | ||
573 | cbValue = (req.fields[i].cchValue + 1) * sizeof(WCHAR); | ||
574 | } | ||
575 | else | ||
576 | { | ||
577 | cbValue = req.fields[i].cbValue; | ||
578 | } | ||
579 | |||
580 | dwRet = this->WritePipe(const_cast<BYTE*> (sValues[i]), cbValue); | ||
581 | if (dwRet != 0) | ||
582 | { | ||
583 | break; | ||
584 | } | ||
585 | } | ||
586 | } | ||
587 | |||
588 | return dwRet; | ||
589 | } | ||
590 | |||
591 | // | ||
592 | // Called when writing a request or response. Writes data to | ||
593 | // the pipe, allowing interruption by the stop event if on the server. | ||
594 | // | ||
595 | DWORD WritePipe(const BYTE* pBuf, DWORD cbWrite) | ||
596 | { | ||
597 | DWORD dwRet = 0; | ||
598 | DWORD dwTotalBytesWritten = 0; | ||
599 | |||
600 | while (dwRet == 0 && dwTotalBytesWritten < cbWrite) | ||
601 | { | ||
602 | DWORD dwBytesWrittenThisTime; | ||
603 | ResetEvent(m_overlapped.hEvent); | ||
604 | if (!WriteFile(m_hPipe, pBuf + dwTotalBytesWritten, cbWrite - dwTotalBytesWritten, &dwBytesWrittenThisTime, &m_overlapped)) | ||
605 | { | ||
606 | dwRet = GetLastError(); | ||
607 | if (dwRet == ERROR_IO_PENDING) | ||
608 | { | ||
609 | if (m_fServer) | ||
610 | { | ||
611 | HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent }; | ||
612 | dwRet = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE); | ||
613 | } | ||
614 | else | ||
615 | { | ||
616 | dwRet = WaitForSingleObject(m_overlapped.hEvent, INFINITE); | ||
617 | } | ||
618 | |||
619 | if (dwRet == WAIT_OBJECT_0) | ||
620 | { | ||
621 | if (!GetOverlappedResult(m_hPipe, &m_overlapped, &dwBytesWrittenThisTime, FALSE)) | ||
622 | { | ||
623 | dwRet = GetLastError(); | ||
624 | } | ||
625 | } | ||
626 | else if (dwRet == WAIT_FAILED) | ||
627 | { | ||
628 | dwRet = GetLastError(); | ||
629 | } | ||
630 | else | ||
631 | { | ||
632 | dwRet = ERROR_OPERATION_ABORTED; | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | dwTotalBytesWritten += dwBytesWrittenThisTime; | ||
638 | } | ||
639 | |||
640 | return dwRet; | ||
641 | } | ||
642 | |||
643 | // | ||
644 | // Called either by the client or server process. | ||
645 | // Reads data from the pipe for a request or response. | ||
646 | // | ||
647 | DWORD ReadRequestData(RequestData* pReq) | ||
648 | { | ||
649 | DWORD dwRet = ReadPipe((BYTE*) pReq, sizeof(RequestData)); | ||
650 | |||
651 | if (dwRet == 0) | ||
652 | { | ||
653 | DWORD cbData = 0; | ||
654 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
655 | { | ||
656 | if (pReq->fields[i].vt == VT_LPWSTR) | ||
657 | { | ||
658 | cbData += (pReq->fields[i].cchValue + 1) * sizeof(WCHAR); | ||
659 | } | ||
660 | else if (pReq->fields[i].vt == VT_STREAM) | ||
661 | { | ||
662 | cbData += pReq->fields[i].cbValue; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | if (cbData > 0) | ||
667 | { | ||
668 | if (!CheckRequestDataBuf(cbData)) | ||
669 | { | ||
670 | return ERROR_OUTOFMEMORY; | ||
671 | } | ||
672 | |||
673 | dwRet = this->ReadPipe((BYTE*) m_pBufReceive, cbData); | ||
674 | if (dwRet == 0) | ||
675 | { | ||
676 | DWORD dwOffset = 0; | ||
677 | for (int i = 0; i < MAX_REQUEST_FIELDS; i++) | ||
678 | { | ||
679 | if (pReq->fields[i].vt == VT_LPWSTR) | ||
680 | { | ||
681 | LPWSTR szTemp = (LPWSTR) (m_pBufReceive + dwOffset); | ||
682 | dwOffset += (pReq->fields[i].cchValue + 1) * sizeof(WCHAR); | ||
683 | pReq->fields[i].szValue = szTemp; | ||
684 | } | ||
685 | else if (pReq->fields[i].vt == VT_STREAM) | ||
686 | { | ||
687 | BYTE* sTemp = m_pBufReceive + dwOffset; | ||
688 | dwOffset += pReq->fields[i].cbValue; | ||
689 | pReq->fields[i].sValue = sTemp; | ||
690 | } | ||
691 | } | ||
692 | } | ||
693 | } | ||
694 | } | ||
695 | |||
696 | return dwRet; | ||
697 | } | ||
698 | |||
699 | // | ||
700 | // Called only by the client process. | ||
701 | // Wait for a response on the pipe. If no response is received before the timeout, | ||
702 | // then give up and close the connection. | ||
703 | // | ||
704 | DWORD ReceiveResponse(RequestId id, RequestData* pResp) | ||
705 | { | ||
706 | RequestId responseId; | ||
707 | DWORD dwRet = ReadPipe((BYTE*) &responseId, sizeof(RequestId)); | ||
708 | if (dwRet == 0 && responseId != id) | ||
709 | { | ||
710 | dwRet = ERROR_OPERATION_ABORTED; | ||
711 | } | ||
712 | |||
713 | if (dwRet == 0) | ||
714 | { | ||
715 | dwRet = this->ReadRequestData(pResp); | ||
716 | } | ||
717 | |||
718 | return dwRet; | ||
719 | } | ||
720 | |||
721 | // | ||
722 | // Called only by the server process's receive thread. | ||
723 | // Try to complete and verify an asynchronous connection operation. | ||
724 | // | ||
725 | DWORD CompleteConnection() | ||
726 | { | ||
727 | DWORD dwRet = 0; | ||
728 | if (m_fConnecting) | ||
729 | { | ||
730 | HANDLE hWaitHandles[] = { m_overlapped.hEvent, m_hReceiveStopEvent }; | ||
731 | DWORD dwWaitRes = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE); | ||
732 | |||
733 | if (dwWaitRes == WAIT_OBJECT_0) | ||
734 | { | ||
735 | m_fConnecting = false; | ||
736 | |||
737 | DWORD dwUnused; | ||
738 | if (GetOverlappedResult(m_hPipe, &m_overlapped, &dwUnused, FALSE)) | ||
739 | { | ||
740 | m_fConnected = true; | ||
741 | } | ||
742 | else | ||
743 | { | ||
744 | dwRet = GetLastError(); | ||
745 | } | ||
746 | } | ||
747 | else if (dwWaitRes == WAIT_FAILED) | ||
748 | { | ||
749 | CancelIo(m_hPipe); | ||
750 | dwRet = GetLastError(); | ||
751 | } | ||
752 | else | ||
753 | { | ||
754 | CancelIo(m_hPipe); | ||
755 | dwRet = ERROR_OPERATION_ABORTED; | ||
756 | } | ||
757 | } | ||
758 | return dwRet; | ||
759 | } | ||
760 | |||
761 | // | ||
762 | // Called only by the server process. | ||
763 | // Creates a named pipe instance and begins asynchronously waiting | ||
764 | // for a connection from the client process. | ||
765 | // | ||
766 | DWORD ConnectPipeServer() | ||
767 | { | ||
768 | DWORD dwRet = 0; | ||
769 | const int BUFSIZE = 1024; // Suggested pipe I/O buffer sizes | ||
770 | m_hPipe = CreateNamedPipe( | ||
771 | m_szPipeName, | ||
772 | PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, | ||
773 | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, | ||
774 | 1, BUFSIZE, BUFSIZE, 0, NULL); | ||
775 | if (m_hPipe == INVALID_HANDLE_VALUE) | ||
776 | { | ||
777 | m_hPipe = NULL; | ||
778 | dwRet = GetLastError(); | ||
779 | } | ||
780 | else if (ConnectNamedPipe(m_hPipe, &m_overlapped)) | ||
781 | { | ||
782 | m_fConnected = true; | ||
783 | } | ||
784 | else | ||
785 | { | ||
786 | dwRet = GetLastError(); | ||
787 | |||
788 | if (dwRet == ERROR_PIPE_BUSY) | ||
789 | { | ||
790 | // All pipe instances are busy, so wait for a maximum of 20 seconds | ||
791 | dwRet = 0; | ||
792 | if (WaitNamedPipe(m_szPipeName, 20000)) | ||
793 | { | ||
794 | m_fConnected = true; | ||
795 | } | ||
796 | else | ||
797 | { | ||
798 | dwRet = GetLastError(); | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if (dwRet == ERROR_IO_PENDING) | ||
803 | { | ||
804 | dwRet = 0; | ||
805 | m_fConnecting = true; | ||
806 | } | ||
807 | } | ||
808 | return dwRet; | ||
809 | } | ||
810 | |||
811 | // | ||
812 | // Called only by the client process. | ||
813 | // Attemps to open a connection to an existing named pipe instance | ||
814 | // which should have already been created by the server process. | ||
815 | // | ||
816 | DWORD ConnectPipeClient() | ||
817 | { | ||
818 | DWORD dwRet = 0; | ||
819 | m_hPipe = CreateFile( | ||
820 | m_szPipeName, GENERIC_READ | GENERIC_WRITE, | ||
821 | 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); | ||
822 | if (m_hPipe != INVALID_HANDLE_VALUE) | ||
823 | { | ||
824 | m_fConnected = true; | ||
825 | } | ||
826 | else | ||
827 | { | ||
828 | m_hPipe = NULL; | ||
829 | dwRet = GetLastError(); | ||
830 | } | ||
831 | return dwRet; | ||
832 | } | ||
833 | |||
834 | // | ||
835 | // Ensures that the request buffer is large enough to hold a request, | ||
836 | // reallocating the buffer if necessary. | ||
837 | // It will also reduce the buffer size if the previous allocation was very large. | ||
838 | // | ||
839 | BOOL CheckRequestDataBuf(DWORD cbBuf) | ||
840 | { | ||
841 | if (m_cbBufReceive < cbBuf || (LARGE_BUFFER_THRESHOLD < m_cbBufReceive && cbBuf < m_cbBufReceive)) | ||
842 | { | ||
843 | if (m_pBufReceive != NULL) | ||
844 | { | ||
845 | SecureZeroMemory(m_pBufReceive, m_cbBufReceive); | ||
846 | delete[] m_pBufReceive; | ||
847 | } | ||
848 | m_cbBufReceive = max(MIN_BUFFER_STRING_SIZE*2, cbBuf); | ||
849 | m_pBufReceive = new BYTE[m_cbBufReceive]; | ||
850 | if (m_pBufReceive == NULL) | ||
851 | { | ||
852 | m_cbBufReceive = 0; | ||
853 | } | ||
854 | } | ||
855 | return m_pBufReceive != NULL; | ||
856 | } | ||
857 | |||
858 | private: | ||
859 | |||
860 | // Name of this instance. | ||
861 | const wchar_t* m_szName; | ||
862 | |||
863 | // "\\.\pipe\name" | ||
864 | wchar_t* m_szPipeName; | ||
865 | |||
866 | // Handle to the pipe instance. | ||
867 | HANDLE m_hPipe; | ||
868 | |||
869 | // Handle to the thread that receives requests. | ||
870 | HANDLE m_hReceiveThread; | ||
871 | |||
872 | // Handle to the event used to signal the receive thread to exit. | ||
873 | HANDLE m_hReceiveStopEvent; | ||
874 | |||
875 | // All pipe I/O is done in overlapped mode to avoid unintentional blocking. | ||
876 | OVERLAPPED m_overlapped; | ||
877 | |||
878 | // Dynamically-resized buffer for receiving requests. | ||
879 | BYTE* m_pBufReceive; | ||
880 | |||
881 | // Current size of the receive request buffer. | ||
882 | DWORD m_cbBufReceive; | ||
883 | |||
884 | // Dynamically-resized buffer for sending requests. | ||
885 | wchar_t* m_pBufSend; | ||
886 | |||
887 | // Current size of the send request buffer. | ||
888 | DWORD m_cbBufSend; | ||
889 | |||
890 | // True if this is the server process, false if this is the client process. | ||
891 | const bool m_fServer; | ||
892 | |||
893 | // True if an asynchronous connection operation is currently in progress. | ||
894 | bool m_fConnecting; | ||
895 | |||
896 | // True if the pipe is currently connected. | ||
897 | bool m_fConnected; | ||
898 | }; | ||
diff --git a/src/dtf/SfxCA/SfxCA.cpp b/src/dtf/SfxCA/SfxCA.cpp new file mode 100644 index 00000000..06319f1e --- /dev/null +++ b/src/dtf/SfxCA/SfxCA.cpp | |||
@@ -0,0 +1,363 @@ | |||
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 | #include "EntryPoints.h" | ||
5 | #include "SfxUtil.h" | ||
6 | |||
7 | #define MANAGED_CAs_OUT_OF_PROC 1 | ||
8 | |||
9 | HMODULE g_hModule; | ||
10 | bool g_fRunningOutOfProc = false; | ||
11 | |||
12 | RemoteMsiSession* g_pRemote = NULL; | ||
13 | |||
14 | // Prototypes for local functions. | ||
15 | // See the function definitions for comments. | ||
16 | |||
17 | bool InvokeManagedCustomAction(MSIHANDLE hSession, | ||
18 | _AppDomain* pAppDomain, const wchar_t* szEntryPoint, int* piResult); | ||
19 | |||
20 | /// <summary> | ||
21 | /// Entry-point for the CA DLL when re-launched as a separate process; | ||
22 | /// connects the comm channel for remote MSI APIs, then invokes the | ||
23 | /// managed custom action entry-point. | ||
24 | /// </summary> | ||
25 | /// <remarks> | ||
26 | /// Do not change the parameters or calling-convention: RUNDLL32 | ||
27 | /// requires this exact signature. | ||
28 | /// </remarks> | ||
29 | extern "C" | ||
30 | void __stdcall InvokeManagedCustomActionOutOfProc( | ||
31 | __in HWND hwnd, __in HINSTANCE hinst, __in_z wchar_t* szCmdLine, int nCmdShow) | ||
32 | { | ||
33 | UNREFERENCED_PARAMETER(hwnd); | ||
34 | UNREFERENCED_PARAMETER(hinst); | ||
35 | UNREFERENCED_PARAMETER(nCmdShow); | ||
36 | |||
37 | g_fRunningOutOfProc = true; | ||
38 | |||
39 | const wchar_t* szSessionName = szCmdLine; | ||
40 | MSIHANDLE hSession; | ||
41 | const wchar_t* szEntryPoint; | ||
42 | |||
43 | int i; | ||
44 | for (i = 0; szCmdLine[i] && szCmdLine[i] != L' '; i++); | ||
45 | if (szCmdLine[i] != L'\0') szCmdLine[i++] = L'\0'; | ||
46 | hSession = _wtoi(szCmdLine + i); | ||
47 | |||
48 | for (; szCmdLine[i] && szCmdLine[i] != L' '; i++); | ||
49 | if (szCmdLine[i] != L'\0') szCmdLine[i++] = L'\0'; | ||
50 | szEntryPoint = szCmdLine + i; | ||
51 | |||
52 | g_pRemote = new RemoteMsiSession(szSessionName, false); | ||
53 | g_pRemote->Connect(); | ||
54 | |||
55 | int ret = InvokeCustomAction(hSession, NULL, szEntryPoint); | ||
56 | |||
57 | RemoteMsiSession::RequestData requestData; | ||
58 | SecureZeroMemory(&requestData, sizeof(RemoteMsiSession::RequestData)); | ||
59 | requestData.fields[0].vt = VT_I4; | ||
60 | requestData.fields[0].iValue = ret; | ||
61 | g_pRemote->SendRequest(RemoteMsiSession::EndSession, &requestData, NULL); | ||
62 | delete g_pRemote; | ||
63 | } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Re-launch this CA DLL as a separate process, and setup a comm channel | ||
67 | /// for remote MSI API calls back to this process. | ||
68 | /// </summary> | ||
69 | int InvokeOutOfProcManagedCustomAction(MSIHANDLE hSession, const wchar_t* szEntryPoint) | ||
70 | { | ||
71 | wchar_t szSessionName[100] = {0}; | ||
72 | swprintf_s(szSessionName, 100, L"SfxCA_%d", ::GetTickCount()); | ||
73 | |||
74 | RemoteMsiSession remote(szSessionName, true); | ||
75 | |||
76 | DWORD ret = remote.Connect(); | ||
77 | if (ret != 0) | ||
78 | { | ||
79 | Log(hSession, L"Failed to create communication pipe for new CA process. Error code: %d", ret); | ||
80 | return ERROR_INSTALL_FAILURE; | ||
81 | } | ||
82 | |||
83 | ret = remote.ProcessRequests(); | ||
84 | if (ret != 0) | ||
85 | { | ||
86 | Log(hSession, L"Failed to open communication pipe for new CA process. Error code: %d", ret); | ||
87 | return ERROR_INSTALL_FAILURE; | ||
88 | } | ||
89 | |||
90 | wchar_t szModule[MAX_PATH] = {0}; | ||
91 | GetModuleFileName(g_hModule, szModule, MAX_PATH); | ||
92 | |||
93 | const wchar_t* rundll32 = L"rundll32.exe"; | ||
94 | wchar_t szRunDll32Path[MAX_PATH] = {0}; | ||
95 | GetSystemDirectory(szRunDll32Path, MAX_PATH); | ||
96 | wcscat_s(szRunDll32Path, MAX_PATH, L"\\"); | ||
97 | wcscat_s(szRunDll32Path, MAX_PATH, rundll32); | ||
98 | |||
99 | const wchar_t* entry = L"zzzzInvokeManagedCustomActionOutOfProc"; | ||
100 | wchar_t szCommandLine[1024] = {0}; | ||
101 | swprintf_s(szCommandLine, 1024, L"%s \"%s\",%s %s %d %s", | ||
102 | rundll32, szModule, entry, szSessionName, hSession, szEntryPoint); | ||
103 | |||
104 | STARTUPINFO si; | ||
105 | SecureZeroMemory(&si, sizeof(STARTUPINFO)); | ||
106 | si.cb = sizeof(STARTUPINFO); | ||
107 | |||
108 | PROCESS_INFORMATION pi; | ||
109 | SecureZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); | ||
110 | |||
111 | if (!CreateProcess(szRunDll32Path, szCommandLine, NULL, NULL, FALSE, | ||
112 | 0, NULL, NULL, &si, &pi)) | ||
113 | { | ||
114 | DWORD err = GetLastError(); | ||
115 | Log(hSession, L"Failed to create new CA process via RUNDLL32. Error code: %d", err); | ||
116 | return ERROR_INSTALL_FAILURE; | ||
117 | } | ||
118 | |||
119 | DWORD dwWait = WaitForSingleObject(pi.hProcess, INFINITE); | ||
120 | if (dwWait != WAIT_OBJECT_0) | ||
121 | { | ||
122 | DWORD err = GetLastError(); | ||
123 | Log(hSession, L"Failed to wait for CA process. Error code: %d", err); | ||
124 | return ERROR_INSTALL_FAILURE; | ||
125 | } | ||
126 | |||
127 | DWORD dwExitCode; | ||
128 | BOOL bRet = GetExitCodeProcess(pi.hProcess, &dwExitCode); | ||
129 | if (!bRet) | ||
130 | { | ||
131 | DWORD err = GetLastError(); | ||
132 | Log(hSession, L"Failed to get exit code of CA process. Error code: %d", err); | ||
133 | return ERROR_INSTALL_FAILURE; | ||
134 | } | ||
135 | else if (dwExitCode != 0) | ||
136 | { | ||
137 | Log(hSession, L"RUNDLL32 returned error code: %d", dwExitCode); | ||
138 | return ERROR_INSTALL_FAILURE; | ||
139 | } | ||
140 | |||
141 | CloseHandle(pi.hThread); | ||
142 | CloseHandle(pi.hProcess); | ||
143 | |||
144 | remote.WaitExitCode(); | ||
145 | return remote.ExitCode; | ||
146 | } | ||
147 | |||
148 | /// <summary> | ||
149 | /// Entrypoint for the managed CA proxy (RemotableNativeMethods) to | ||
150 | /// call MSI APIs remotely. | ||
151 | /// </summary> | ||
152 | void __stdcall MsiRemoteInvoke(RemoteMsiSession::RequestId id, RemoteMsiSession::RequestData* pRequest, RemoteMsiSession::RequestData** ppResponse) | ||
153 | { | ||
154 | if (g_fRunningOutOfProc) | ||
155 | { | ||
156 | g_pRemote->SendRequest(id, pRequest, ppResponse); | ||
157 | } | ||
158 | else | ||
159 | { | ||
160 | *ppResponse = NULL; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /// <summary> | ||
165 | /// Invokes a managed custom action from native code by | ||
166 | /// extracting the package to a temporary working directory | ||
167 | /// then hosting the CLR and locating and calling the entrypoint. | ||
168 | /// </summary> | ||
169 | /// <param name="hSession">Handle to the installation session. | ||
170 | /// Passed to custom action entrypoints by the installer engine.</param> | ||
171 | /// <param name="szWorkingDir">Directory containing the CA binaries | ||
172 | /// and the CustomAction.config file defining the entrypoints. | ||
173 | /// This may be NULL, in which case the current module must have | ||
174 | /// a concatenated cabinet containing those files, which will be | ||
175 | /// extracted to a temporary directory.</param> | ||
176 | /// <param name="szEntryPoint">Name of the CA entrypoint to be invoked. | ||
177 | /// This must be either an explicit "AssemblyName!Namespace.Class.Method" | ||
178 | /// string, or a simple name that maps to a full entrypoint definition | ||
179 | /// in CustomAction.config.</param> | ||
180 | /// <returns>The value returned by the managed custom action method, | ||
181 | /// or ERROR_INSTALL_FAILURE if the CA could not be invoked.</returns> | ||
182 | int InvokeCustomAction(MSIHANDLE hSession, | ||
183 | const wchar_t* szWorkingDir, const wchar_t* szEntryPoint) | ||
184 | { | ||
185 | #ifdef MANAGED_CAs_OUT_OF_PROC | ||
186 | if (!g_fRunningOutOfProc && szWorkingDir == NULL) | ||
187 | { | ||
188 | return InvokeOutOfProcManagedCustomAction(hSession, szEntryPoint); | ||
189 | } | ||
190 | #endif | ||
191 | |||
192 | wchar_t szTempDir[MAX_PATH]; | ||
193 | bool fDeleteTemp = false; | ||
194 | if (szWorkingDir == NULL) | ||
195 | { | ||
196 | if (!ExtractToTempDirectory(hSession, g_hModule, szTempDir, MAX_PATH)) | ||
197 | { | ||
198 | return ERROR_INSTALL_FAILURE; | ||
199 | } | ||
200 | szWorkingDir = szTempDir; | ||
201 | fDeleteTemp = true; | ||
202 | } | ||
203 | |||
204 | wchar_t szConfigFilePath[MAX_PATH + 20]; | ||
205 | StringCchCopy(szConfigFilePath, MAX_PATH + 20, szWorkingDir); | ||
206 | StringCchCat(szConfigFilePath, MAX_PATH + 20, L"\\CustomAction.config"); | ||
207 | |||
208 | const wchar_t* szConfigFile = szConfigFilePath; | ||
209 | if (!::PathFileExists(szConfigFilePath)) | ||
210 | { | ||
211 | szConfigFile = NULL; | ||
212 | } | ||
213 | |||
214 | wchar_t szWIAssembly[MAX_PATH + 50]; | ||
215 | StringCchCopy(szWIAssembly, MAX_PATH + 50, szWorkingDir); | ||
216 | StringCchCat(szWIAssembly, MAX_PATH + 50, L"\\WixToolset.Dtf.WindowsInstaller.dll"); | ||
217 | |||
218 | int iResult = ERROR_INSTALL_FAILURE; | ||
219 | ICorRuntimeHost* pHost; | ||
220 | if (LoadCLR(hSession, NULL, szConfigFile, szWIAssembly, &pHost)) | ||
221 | { | ||
222 | _AppDomain* pAppDomain; | ||
223 | if (CreateAppDomain(hSession, pHost, L"CustomAction", szWorkingDir, | ||
224 | szConfigFile, &pAppDomain)) | ||
225 | { | ||
226 | if (!InvokeManagedCustomAction(hSession, pAppDomain, szEntryPoint, &iResult)) | ||
227 | { | ||
228 | iResult = ERROR_INSTALL_FAILURE; | ||
229 | } | ||
230 | HRESULT hr = pHost->UnloadDomain(pAppDomain); | ||
231 | if (FAILED(hr)) | ||
232 | { | ||
233 | Log(hSession, L"Failed to unload app domain. Error code 0x%X", hr); | ||
234 | } | ||
235 | pAppDomain->Release(); | ||
236 | } | ||
237 | |||
238 | pHost->Stop(); | ||
239 | pHost->Release(); | ||
240 | } | ||
241 | |||
242 | if (fDeleteTemp) | ||
243 | { | ||
244 | DeleteDirectory(szTempDir); | ||
245 | } | ||
246 | return iResult; | ||
247 | } | ||
248 | |||
249 | /// <summary> | ||
250 | /// Called by the system when the DLL is loaded. | ||
251 | /// Saves the module handle for later use. | ||
252 | /// </summary> | ||
253 | BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, void* pReserved) | ||
254 | { | ||
255 | UNREFERENCED_PARAMETER(pReserved); | ||
256 | |||
257 | switch (dwReason) | ||
258 | { | ||
259 | case DLL_PROCESS_ATTACH: | ||
260 | g_hModule = hModule; | ||
261 | break; | ||
262 | case DLL_THREAD_ATTACH: | ||
263 | case DLL_THREAD_DETACH: | ||
264 | case DLL_PROCESS_DETACH: | ||
265 | break; | ||
266 | } | ||
267 | return TRUE; | ||
268 | } | ||
269 | |||
270 | /// <summary> | ||
271 | /// Loads and invokes the managed portion of the proxy. | ||
272 | /// </summary> | ||
273 | /// <param name="hSession">Handle to the installer session, | ||
274 | /// used for logging errors and to be passed on to the custom action.</param> | ||
275 | /// <param name="pAppDomain">AppDomain which has its application | ||
276 | /// base set to the CA working directory.</param> | ||
277 | /// <param name="szEntryPoint">Name of the CA entrypoint to be invoked. | ||
278 | /// This must be either an explicit "AssemblyName!Namespace.Class.Method" | ||
279 | /// string, or a simple name that maps to a full entrypoint definition | ||
280 | /// in CustomAction.config.</param> | ||
281 | /// <param name="piResult">Return value of the invoked custom | ||
282 | /// action method.</param> | ||
283 | /// <returns>True if the managed proxy was invoked successfully, | ||
284 | /// false if there was some error. Note the custom action itself may | ||
285 | /// return an error via piResult while this method still returns true | ||
286 | /// since the invocation was successful.</returns> | ||
287 | bool InvokeManagedCustomAction(MSIHANDLE hSession, _AppDomain* pAppDomain, | ||
288 | const wchar_t* szEntryPoint, int* piResult) | ||
289 | { | ||
290 | VARIANT vResult; | ||
291 | ::VariantInit(&vResult); | ||
292 | |||
293 | const bool f64bit = (sizeof(void*) == sizeof(LONGLONG)); | ||
294 | const wchar_t* szMsiAssemblyName = L"WixToolset.Dtf.WindowsInstaller"; | ||
295 | const wchar_t* szMsiCAProxyClass = L"WixToolset.Dtf.WindowsInstaller.CustomActionProxy"; | ||
296 | const wchar_t* szMsiCAInvokeMethod = (f64bit ? L"InvokeCustomAction64" : L"InvokeCustomAction32"); | ||
297 | |||
298 | _MethodInfo* pCAInvokeMethod; | ||
299 | if (!GetMethod(hSession, pAppDomain, szMsiAssemblyName, | ||
300 | szMsiCAProxyClass, szMsiCAInvokeMethod, &pCAInvokeMethod)) | ||
301 | { | ||
302 | return false; | ||
303 | } | ||
304 | |||
305 | HRESULT hr; | ||
306 | VARIANT vNull; | ||
307 | vNull.vt = VT_EMPTY; | ||
308 | SAFEARRAY* saArgs = SafeArrayCreateVector(VT_VARIANT, 0, 3); | ||
309 | VARIANT vSessionHandle; | ||
310 | vSessionHandle.vt = VT_I4; | ||
311 | vSessionHandle.intVal = hSession; | ||
312 | LONG index = 0; | ||
313 | hr = SafeArrayPutElement(saArgs, &index, &vSessionHandle); | ||
314 | if (FAILED(hr)) goto LExit; | ||
315 | VARIANT vEntryPoint; | ||
316 | vEntryPoint.vt = VT_BSTR; | ||
317 | vEntryPoint.bstrVal = SysAllocString(szEntryPoint); | ||
318 | if (vEntryPoint.bstrVal == NULL) | ||
319 | { | ||
320 | hr = E_OUTOFMEMORY; | ||
321 | goto LExit; | ||
322 | } | ||
323 | index = 1; | ||
324 | hr = SafeArrayPutElement(saArgs, &index, &vEntryPoint); | ||
325 | if (FAILED(hr)) goto LExit; | ||
326 | VARIANT vRemotingFunctionPtr; | ||
327 | #pragma warning(push) | ||
328 | #pragma warning(disable:4127) // conditional expression is constant | ||
329 | if (f64bit) | ||
330 | #pragma warning(pop) | ||
331 | { | ||
332 | vRemotingFunctionPtr.vt = VT_I8; | ||
333 | vRemotingFunctionPtr.llVal = (LONGLONG) (g_fRunningOutOfProc ? MsiRemoteInvoke : NULL); | ||
334 | } | ||
335 | else | ||
336 | { | ||
337 | vRemotingFunctionPtr.vt = VT_I4; | ||
338 | #pragma warning(push) | ||
339 | #pragma warning(disable:4302) // truncation | ||
340 | #pragma warning(disable:4311) // pointer truncation | ||
341 | vRemotingFunctionPtr.lVal = (LONG) (g_fRunningOutOfProc ? MsiRemoteInvoke : NULL); | ||
342 | #pragma warning(pop) | ||
343 | } | ||
344 | index = 2; | ||
345 | hr = SafeArrayPutElement(saArgs, &index, &vRemotingFunctionPtr); | ||
346 | if (FAILED(hr)) goto LExit; | ||
347 | |||
348 | hr = pCAInvokeMethod->Invoke_3(vNull, saArgs, &vResult); | ||
349 | |||
350 | LExit: | ||
351 | SafeArrayDestroy(saArgs); | ||
352 | pCAInvokeMethod->Release(); | ||
353 | |||
354 | if (FAILED(hr)) | ||
355 | { | ||
356 | Log(hSession, L"Failed to invoke custom action method. Error code 0x%X", hr); | ||
357 | return false; | ||
358 | } | ||
359 | |||
360 | *piResult = vResult.intVal; | ||
361 | return true; | ||
362 | } | ||
363 | |||
diff --git a/src/dtf/SfxCA/SfxCA.rc b/src/dtf/SfxCA/SfxCA.rc new file mode 100644 index 00000000..4d78194b --- /dev/null +++ b/src/dtf/SfxCA/SfxCA.rc | |||
@@ -0,0 +1,10 @@ | |||
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 | #define VER_DLL | ||
4 | #define VER_LANG_NEUTRAL | ||
5 | #define VER_ORIGINAL_FILENAME "SfxCA.dll" | ||
6 | #define VER_INTERNAL_NAME "SfxCA" | ||
7 | #define VER_FILE_DESCRIPTION "DTF Self-Extracting Custom Action" | ||
8 | |||
9 | // Additional resources here | ||
10 | |||
diff --git a/src/dtf/SfxCA/SfxCA.vcxproj b/src/dtf/SfxCA/SfxCA.vcxproj new file mode 100644 index 00000000..96aa69e0 --- /dev/null +++ b/src/dtf/SfxCA/SfxCA.vcxproj | |||
@@ -0,0 +1,79 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- 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. --> | ||
3 | |||
4 | <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
5 | <ItemGroup Label="ProjectConfigurations"> | ||
6 | <ProjectConfiguration Include="Debug|ARM64"> | ||
7 | <Configuration>Debug</Configuration> | ||
8 | <Platform>ARM64</Platform> | ||
9 | </ProjectConfiguration> | ||
10 | <ProjectConfiguration Include="Debug|Win32"> | ||
11 | <Configuration>Debug</Configuration> | ||
12 | <Platform>Win32</Platform> | ||
13 | </ProjectConfiguration> | ||
14 | <ProjectConfiguration Include="Debug|x64"> | ||
15 | <Configuration>Debug</Configuration> | ||
16 | <Platform>x64</Platform> | ||
17 | </ProjectConfiguration> | ||
18 | <ProjectConfiguration Include="Release|ARM64"> | ||
19 | <Configuration>Release</Configuration> | ||
20 | <Platform>ARM64</Platform> | ||
21 | </ProjectConfiguration> | ||
22 | <ProjectConfiguration Include="Release|Win32"> | ||
23 | <Configuration>Release</Configuration> | ||
24 | <Platform>Win32</Platform> | ||
25 | </ProjectConfiguration> | ||
26 | <ProjectConfiguration Include="Release|x64"> | ||
27 | <Configuration>Release</Configuration> | ||
28 | <Platform>x64</Platform> | ||
29 | </ProjectConfiguration> | ||
30 | </ItemGroup> | ||
31 | |||
32 | <PropertyGroup Label="Globals"> | ||
33 | <ProjectGuid>{55D5BA28-D427-4F53-80C2-FE9EF23C1553}</ProjectGuid> | ||
34 | <ConfigurationType>DynamicLibrary</ConfigurationType> | ||
35 | <CharacterSet>Unicode</CharacterSet> | ||
36 | <SignOutput>false</SignOutput> | ||
37 | <ProjectModuleDefinitionFile>EntryPoints.def</ProjectModuleDefinitionFile> | ||
38 | </PropertyGroup> | ||
39 | |||
40 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||
41 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | ||
42 | |||
43 | <PropertyGroup> | ||
44 | <ProjectAdditionalLinkLibraries>msi.lib;cabinet.lib;shlwapi.lib</ProjectAdditionalLinkLibraries> | ||
45 | </PropertyGroup> | ||
46 | |||
47 | <ItemGroup> | ||
48 | <ClCompile Include="ClrHost.cpp" /> | ||
49 | <ClCompile Include="Extract.cpp" /> | ||
50 | <ClCompile Include="precomp.cpp"> | ||
51 | <PrecompiledHeader>Create</PrecompiledHeader> | ||
52 | </ClCompile> | ||
53 | <ClCompile Include="RemoteMsi.cpp" /> | ||
54 | <ClCompile Include="SfxCA.cpp" /> | ||
55 | <ClCompile Include="SfxUtil.cpp" /> | ||
56 | <ClCompile Include="EmbeddedUI.cpp" /> | ||
57 | </ItemGroup> | ||
58 | <ItemGroup> | ||
59 | <ClInclude Include="precomp.h" /> | ||
60 | <ClInclude Include="EntryPoints.h" /> | ||
61 | <ClInclude Include="RemoteMsiSession.h" /> | ||
62 | <ClInclude Include="SfxUtil.h" /> | ||
63 | </ItemGroup> | ||
64 | |||
65 | <ItemGroup> | ||
66 | <None Include="EntryPoints.def" /> | ||
67 | </ItemGroup> | ||
68 | |||
69 | <ItemGroup> | ||
70 | <ResourceCompile Include="SfxCA.rc" /> | ||
71 | </ItemGroup> | ||
72 | |||
73 | <ItemGroup> | ||
74 | <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> | ||
75 | <PackageReference Include="GitInfo" PrivateAssets="All" /> | ||
76 | </ItemGroup> | ||
77 | |||
78 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||
79 | </Project> | ||
diff --git a/src/dtf/SfxCA/SfxCA.vcxproj.filters b/src/dtf/SfxCA/SfxCA.vcxproj.filters new file mode 100644 index 00000000..a5ebf693 --- /dev/null +++ b/src/dtf/SfxCA/SfxCA.vcxproj.filters | |||
@@ -0,0 +1,62 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
3 | <ItemGroup> | ||
4 | <ClCompile Include="ClrHost.cpp"> | ||
5 | <Filter>Source Files</Filter> | ||
6 | </ClCompile> | ||
7 | <ClCompile Include="EmbeddedUI.cpp"> | ||
8 | <Filter>Source Files</Filter> | ||
9 | </ClCompile> | ||
10 | <ClCompile Include="Extract.cpp"> | ||
11 | <Filter>Source Files</Filter> | ||
12 | </ClCompile> | ||
13 | <ClCompile Include="RemoteMsi.cpp"> | ||
14 | <Filter>Source Files</Filter> | ||
15 | </ClCompile> | ||
16 | <ClCompile Include="SfxCA.cpp"> | ||
17 | <Filter>Source Files</Filter> | ||
18 | </ClCompile> | ||
19 | <ClCompile Include="SfxUtil.cpp"> | ||
20 | <Filter>Source Files</Filter> | ||
21 | </ClCompile> | ||
22 | <ClCompile Include="precomp.cpp"> | ||
23 | <Filter>Source Files</Filter> | ||
24 | </ClCompile> | ||
25 | </ItemGroup> | ||
26 | <ItemGroup> | ||
27 | <ClInclude Include="EntryPoints.h"> | ||
28 | <Filter>Header Files</Filter> | ||
29 | </ClInclude> | ||
30 | <ClInclude Include="precomp.h"> | ||
31 | <Filter>Header Files</Filter> | ||
32 | </ClInclude> | ||
33 | <ClInclude Include="RemoteMsiSession.h"> | ||
34 | <Filter>Header Files</Filter> | ||
35 | </ClInclude> | ||
36 | <ClInclude Include="SfxUtil.h"> | ||
37 | <Filter>Header Files</Filter> | ||
38 | </ClInclude> | ||
39 | </ItemGroup> | ||
40 | <ItemGroup> | ||
41 | <Filter Include="Resource Files"> | ||
42 | <UniqueIdentifier>{81c92f68-18c2-4cd4-a588-5c3616860dd9}</UniqueIdentifier> | ||
43 | </Filter> | ||
44 | <Filter Include="Header Files"> | ||
45 | <UniqueIdentifier>{6cdc30ee-e14d-4679-b92e-3e080535e53b}</UniqueIdentifier> | ||
46 | </Filter> | ||
47 | <Filter Include="Source Files"> | ||
48 | <UniqueIdentifier>{1666a44e-4f2e-4f13-980e-d0c3dfa7cb6d}</UniqueIdentifier> | ||
49 | </Filter> | ||
50 | </ItemGroup> | ||
51 | <ItemGroup> | ||
52 | <ResourceCompile Include="SfxCA.rc"> | ||
53 | <Filter>Resource Files</Filter> | ||
54 | </ResourceCompile> | ||
55 | </ItemGroup> | ||
56 | <ItemGroup> | ||
57 | <None Include="EntryPoints.def"> | ||
58 | <Filter>Resource Files</Filter> | ||
59 | </None> | ||
60 | <None Include="packages.config" /> | ||
61 | </ItemGroup> | ||
62 | </Project> \ No newline at end of file | ||
diff --git a/src/dtf/SfxCA/SfxUtil.cpp b/src/dtf/SfxCA/SfxUtil.cpp new file mode 100644 index 00000000..1bf2c5b2 --- /dev/null +++ b/src/dtf/SfxCA/SfxUtil.cpp | |||
@@ -0,0 +1,209 @@ | |||
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 | #include "SfxUtil.h" | ||
5 | |||
6 | /// <summary> | ||
7 | /// Writes a formatted message to the MSI log. | ||
8 | /// Does out-of-proc MSI calls if necessary. | ||
9 | /// </summary> | ||
10 | void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...) | ||
11 | { | ||
12 | const int LOG_BUFSIZE = 4096; | ||
13 | wchar_t szBuf[LOG_BUFSIZE]; | ||
14 | va_list args; | ||
15 | va_start(args, szMessage); | ||
16 | StringCchVPrintf(szBuf, LOG_BUFSIZE, szMessage, args); | ||
17 | |||
18 | if (!g_fRunningOutOfProc || NULL == g_pRemote) | ||
19 | { | ||
20 | MSIHANDLE hRec = MsiCreateRecord(1); | ||
21 | MsiRecordSetString(hRec, 0, L"SFXCA: [1]"); | ||
22 | MsiRecordSetString(hRec, 1, szBuf); | ||
23 | MsiProcessMessage(hSession, INSTALLMESSAGE_INFO, hRec); | ||
24 | MsiCloseHandle(hRec); | ||
25 | } | ||
26 | else | ||
27 | { | ||
28 | // Logging is the only remote-MSI operation done from unmanaged code. | ||
29 | // It's not very convenient here because part of the infrastructure | ||
30 | // for remote MSI APIs is on the managed side. | ||
31 | |||
32 | RemoteMsiSession::RequestData req; | ||
33 | RemoteMsiSession::RequestData* pResp = NULL; | ||
34 | SecureZeroMemory(&req, sizeof(RemoteMsiSession::RequestData)); | ||
35 | |||
36 | req.fields[0].vt = VT_UI4; | ||
37 | req.fields[0].uiValue = 1; | ||
38 | g_pRemote->SendRequest(RemoteMsiSession::MsiCreateRecord, &req, &pResp); | ||
39 | MSIHANDLE hRec = (MSIHANDLE) pResp->fields[0].iValue; | ||
40 | |||
41 | req.fields[0].vt = VT_I4; | ||
42 | req.fields[0].iValue = (int) hRec; | ||
43 | req.fields[1].vt = VT_UI4; | ||
44 | req.fields[1].uiValue = 0; | ||
45 | req.fields[2].vt = VT_LPWSTR; | ||
46 | req.fields[2].szValue = L"SFXCA: [1]"; | ||
47 | g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp); | ||
48 | |||
49 | req.fields[0].vt = VT_I4; | ||
50 | req.fields[0].iValue = (int) hRec; | ||
51 | req.fields[1].vt = VT_UI4; | ||
52 | req.fields[1].uiValue = 1; | ||
53 | req.fields[2].vt = VT_LPWSTR; | ||
54 | req.fields[2].szValue = szBuf; | ||
55 | g_pRemote->SendRequest(RemoteMsiSession::MsiRecordSetString, &req, &pResp); | ||
56 | |||
57 | req.fields[0].vt = VT_I4; | ||
58 | req.fields[0].iValue = (int) hSession; | ||
59 | req.fields[1].vt = VT_I4; | ||
60 | req.fields[1].iValue = (int) INSTALLMESSAGE_INFO; | ||
61 | req.fields[2].vt = VT_I4; | ||
62 | req.fields[2].iValue = (int) hRec; | ||
63 | g_pRemote->SendRequest(RemoteMsiSession::MsiProcessMessage, &req, &pResp); | ||
64 | |||
65 | req.fields[0].vt = VT_I4; | ||
66 | req.fields[0].iValue = (int) hRec; | ||
67 | req.fields[1].vt = VT_EMPTY; | ||
68 | req.fields[2].vt = VT_EMPTY; | ||
69 | g_pRemote->SendRequest(RemoteMsiSession::MsiCloseHandle, &req, &pResp); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | /// <summary> | ||
74 | /// Deletes a directory, including all files and subdirectories. | ||
75 | /// </summary> | ||
76 | /// <param name="szDir">Path to the directory to delete, | ||
77 | /// not including a trailing backslash.</param> | ||
78 | /// <returns>True if the directory was successfully deleted, or false | ||
79 | /// if the deletion failed (most likely because some files were locked). | ||
80 | /// </returns> | ||
81 | bool DeleteDirectory(const wchar_t* szDir) | ||
82 | { | ||
83 | size_t cchDir = wcslen(szDir); | ||
84 | size_t cchPathBuf = cchDir + 3 + MAX_PATH; | ||
85 | wchar_t* szPath = (wchar_t*) _alloca(cchPathBuf * sizeof(wchar_t)); | ||
86 | if (szPath == NULL) return false; | ||
87 | StringCchCopy(szPath, cchPathBuf, szDir); | ||
88 | StringCchCat(szPath, cchPathBuf, L"\\*"); | ||
89 | WIN32_FIND_DATA fd; | ||
90 | HANDLE hSearch = FindFirstFile(szPath, &fd); | ||
91 | while (hSearch != INVALID_HANDLE_VALUE) | ||
92 | { | ||
93 | StringCchCopy(szPath + cchDir + 1, cchPathBuf - (cchDir + 1), fd.cFileName); | ||
94 | if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) | ||
95 | { | ||
96 | if (wcscmp(fd.cFileName, L".") != 0 && wcscmp(fd.cFileName, L"..") != 0) | ||
97 | { | ||
98 | DeleteDirectory(szPath); | ||
99 | } | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | DeleteFile(szPath); | ||
104 | } | ||
105 | if (!FindNextFile(hSearch, &fd)) | ||
106 | { | ||
107 | FindClose(hSearch); | ||
108 | hSearch = INVALID_HANDLE_VALUE; | ||
109 | } | ||
110 | } | ||
111 | return RemoveDirectory(szDir) != 0; | ||
112 | } | ||
113 | |||
114 | bool DirectoryExists(const wchar_t* szDir) | ||
115 | { | ||
116 | if (szDir != NULL) | ||
117 | { | ||
118 | DWORD dwAttrs = GetFileAttributes(szDir); | ||
119 | if (dwAttrs != -1 && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0) | ||
120 | { | ||
121 | return true; | ||
122 | } | ||
123 | } | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | /// <summary> | ||
128 | /// Extracts a cabinet that is concatenated to a module | ||
129 | /// to a new temporary directory. | ||
130 | /// </summary> | ||
131 | /// <param name="hSession">Handle to the installer session, | ||
132 | /// used just for logging.</param> | ||
133 | /// <param name="hModule">Module that has the concatenated cabinet.</param> | ||
134 | /// <param name="szTempDir">Buffer for returning the path of the | ||
135 | /// created temp directory.</param> | ||
136 | /// <param name="cchTempDirBuf">Size in characters of the buffer. | ||
137 | /// <returns>True if the files were extracted, or false if the | ||
138 | /// buffer was too small or the directory could not be created | ||
139 | /// or the extraction failed for some other reason.</returns> | ||
140 | __success(return != false) | ||
141 | bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, | ||
142 | __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf) | ||
143 | { | ||
144 | wchar_t szModule[MAX_PATH]; | ||
145 | DWORD cchCopied = GetModuleFileName(hModule, szModule, MAX_PATH - 1); | ||
146 | if (cchCopied == 0) | ||
147 | { | ||
148 | Log(hSession, L"Failed to get module path. Error code %d.", GetLastError()); | ||
149 | return false; | ||
150 | } | ||
151 | else if (cchCopied == MAX_PATH - 1) | ||
152 | { | ||
153 | Log(hSession, L"Failed to get module path -- path is too long."); | ||
154 | return false; | ||
155 | } | ||
156 | |||
157 | if (szTempDir == NULL || cchTempDirBuf < wcslen(szModule) + 1) | ||
158 | { | ||
159 | Log(hSession, L"Temp directory buffer is NULL or too small."); | ||
160 | return false; | ||
161 | } | ||
162 | StringCchCopy(szTempDir, cchTempDirBuf, szModule); | ||
163 | StringCchCat(szTempDir, cchTempDirBuf, L"-"); | ||
164 | |||
165 | DWORD cchTempDir = (DWORD) wcslen(szTempDir); | ||
166 | for (int i = 0; DirectoryExists(szTempDir); i++) | ||
167 | { | ||
168 | swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i); | ||
169 | } | ||
170 | |||
171 | if (!CreateDirectory(szTempDir, NULL)) | ||
172 | { | ||
173 | cchCopied = GetTempPath(cchTempDirBuf, szTempDir); | ||
174 | if (cchCopied == 0 || cchCopied >= cchTempDirBuf) | ||
175 | { | ||
176 | Log(hSession, L"Failed to get temp directory. Error code %d", GetLastError()); | ||
177 | return false; | ||
178 | } | ||
179 | |||
180 | wchar_t* szModuleName = wcsrchr(szModule, L'\\'); | ||
181 | if (szModuleName == NULL) szModuleName = szModule; | ||
182 | else szModuleName = szModuleName + 1; | ||
183 | StringCchCat(szTempDir, cchTempDirBuf, szModuleName); | ||
184 | StringCchCat(szTempDir, cchTempDirBuf, L"-"); | ||
185 | |||
186 | cchTempDir = (DWORD) wcslen(szTempDir); | ||
187 | for (int i = 0; DirectoryExists(szTempDir); i++) | ||
188 | { | ||
189 | swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i); | ||
190 | } | ||
191 | |||
192 | if (!CreateDirectory(szTempDir, NULL)) | ||
193 | { | ||
194 | Log(hSession, L"Failed to create temp directory. Error code %d", GetLastError()); | ||
195 | return false; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | Log(hSession, L"Extracting custom action to temporary directory: %s\\", szTempDir); | ||
200 | int err = ExtractCabinet(szModule, szTempDir); | ||
201 | if (err != 0) | ||
202 | { | ||
203 | Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err); | ||
204 | DeleteDirectory(szTempDir); | ||
205 | return false; | ||
206 | } | ||
207 | return true; | ||
208 | } | ||
209 | |||
diff --git a/src/dtf/SfxCA/SfxUtil.h b/src/dtf/SfxCA/SfxUtil.h new file mode 100644 index 00000000..af12d8dd --- /dev/null +++ b/src/dtf/SfxCA/SfxUtil.h | |||
@@ -0,0 +1,31 @@ | |||
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 "RemoteMsiSession.h" | ||
4 | |||
5 | void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...); | ||
6 | |||
7 | int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir); | ||
8 | |||
9 | bool DeleteDirectory(const wchar_t* szDir); | ||
10 | |||
11 | __success(return != false) | ||
12 | bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, | ||
13 | __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf); | ||
14 | |||
15 | bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile, | ||
16 | const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost); | ||
17 | |||
18 | bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost, | ||
19 | const wchar_t* szName, const wchar_t* szAppBase, | ||
20 | const wchar_t* szConfigFile, _AppDomain** ppAppDomain); | ||
21 | |||
22 | bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain, | ||
23 | const wchar_t* szAssembly, const wchar_t* szClass, | ||
24 | const wchar_t* szMethod, _MethodInfo** ppCAMethod); | ||
25 | |||
26 | extern HMODULE g_hModule; | ||
27 | extern bool g_fRunningOutOfProc; | ||
28 | |||
29 | extern RemoteMsiSession* g_pRemote; | ||
30 | |||
31 | |||
diff --git a/src/dtf/SfxCA/precomp.cpp b/src/dtf/SfxCA/precomp.cpp new file mode 100644 index 00000000..ce82c1d7 --- /dev/null +++ b/src/dtf/SfxCA/precomp.cpp | |||
@@ -0,0 +1,3 @@ | |||
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" \ No newline at end of file | ||
diff --git a/src/dtf/SfxCA/precomp.h b/src/dtf/SfxCA/precomp.h new file mode 100644 index 00000000..48d4f011 --- /dev/null +++ b/src/dtf/SfxCA/precomp.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #pragma once | ||
2 | // 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. | ||
3 | |||
4 | |||
5 | #include <windows.h> | ||
6 | #include <msiquery.h> | ||
7 | #include <strsafe.h> | ||
8 | #include <mscoree.h> | ||
9 | #include <io.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <share.h> | ||
12 | #include <shlwapi.h> | ||
13 | #include <sys/stat.h> | ||
14 | #include <malloc.h> | ||
15 | #include <fdi.h> | ||
16 | #include <msiquery.h> | ||
17 | #import <mscorlib.tlb> raw_interfaces_only rename("ReportEvent", "CorReportEvent") | ||
18 | using namespace mscorlib; | ||
diff --git a/src/dtf/SfxCA/sfxca_t.proj b/src/dtf/SfxCA/sfxca_t.proj new file mode 100644 index 00000000..1e823be1 --- /dev/null +++ b/src/dtf/SfxCA/sfxca_t.proj | |||
@@ -0,0 +1,7 @@ | |||
1 | <Project Sdk="Microsoft.Build.Traversal"> | ||
2 | <ItemGroup> | ||
3 | <ProjectReference Include="SfxCA.vcxproj" Properties="Platform=x86" /> | ||
4 | <ProjectReference Include="SfxCA.vcxproj" Properties="Platform=x64" /> | ||
5 | <ProjectReference Include="SfxCA.vcxproj" Properties="Platform=ARM64" /> | ||
6 | </ItemGroup> | ||
7 | </Project> | ||
diff --git a/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.csproj b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.csproj new file mode 100644 index 00000000..379aa194 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.csproj | |||
@@ -0,0 +1,17 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- 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. --> | ||
3 | |||
4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
5 | <PropertyGroup> | ||
6 | <TargetFramework>net472</TargetFramework> | ||
7 | <IncludeBuildOutput>false</IncludeBuildOutput> | ||
8 | <Title>The WiX Toolset Managed CustomAction Framework.</Title> | ||
9 | <Description>The WiX Toolset lets developers create managed custom actions for the Windows Installer. This package contains the tools necessary to convert your project into a managed custom action.</Description> | ||
10 | |||
11 | <NuspecBasePath>$(OutputPath)</NuspecBasePath> | ||
12 | </PropertyGroup> | ||
13 | |||
14 | <ItemGroup> | ||
15 | <ProjectReference Include="..\WixToolset.Dtf.MakeSfxCA\WixToolset.Dtf.MakeSfxCA.csproj" /> | ||
16 | </ItemGroup> | ||
17 | </Project> | ||
diff --git a/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.nuspec b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.nuspec new file mode 100644 index 00000000..4550e629 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.nuspec | |||
@@ -0,0 +1,32 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> | ||
3 | <metadata> | ||
4 | <id>$id$</id> | ||
5 | <version>$version$</version> | ||
6 | <title>$title$</title> | ||
7 | <description>$description$</description> | ||
8 | <authors>$authors$</authors> | ||
9 | <icon>wix-white-bg.png</icon> | ||
10 | <license type="expression">MS-RL</license> | ||
11 | <requireLicenseAcceptance>false</requireLicenseAcceptance> | ||
12 | <copyright>$copyright$</copyright> | ||
13 | <projectUrl>$projectUrl$</projectUrl> | ||
14 | <repository type="$repositorytype$" url="$repositoryurl$" commit="$repositorycommit$" /> | ||
15 | <dependencies> | ||
16 | <dependency id="WixToolset.Dtf.WindowsInstaller" version="$version$" /> | ||
17 | </dependencies> | ||
18 | </metadata> | ||
19 | |||
20 | <files> | ||
21 | <file src="$projectFolder$\$id$.targets" target="build" /> | ||
22 | <file src="$projectFolder$\..\..\internal\images\wix-white-bg.png" /> | ||
23 | <file src="net472\WixToolset.Dtf.MakeSfxCA.exe" target="tools" /> | ||
24 | <file src="net472\WixToolset.Dtf.MakeSfxCA.exe.config" target="tools" /> | ||
25 | <file src="net472\WixToolset.Dtf.Compression.dll" target="tools" /> | ||
26 | <file src="net472\WixToolset.Dtf.Compression.Cab.dll" target="tools" /> | ||
27 | <file src="net472\WixToolset.Dtf.Resources.dll" target="tools" /> | ||
28 | <file src="x64\SfxCA.dll" target="tools\x64" /> | ||
29 | <file src="x86\SfxCA.dll" target="tools\x86" /> | ||
30 | <file src="ARM64\SfxCA.dll" target="tools\arm64" /> | ||
31 | </files> | ||
32 | </package> | ||
diff --git a/src/dtf/WixToolset.Dtf.MSBuild/tools/wix.ca.targets b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.targets index 4578c2d8..127bb29d 100644 --- a/src/dtf/WixToolset.Dtf.MSBuild/tools/wix.ca.targets +++ b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.targets | |||
@@ -1,8 +1,7 @@ | |||
1 | <?xml version="1.0" encoding="utf-8" ?> | 1 | <?xml version="1.0" encoding="utf-8" ?> |
2 | <!-- 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 | <!-- 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. --> |
3 | 3 | ||
4 | 4 | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
5 | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> | ||
6 | 5 | ||
7 | <Import Project="$(CustomBeforeWixCATargets)" Condition=" '$(CustomBeforeWixCATargets)' != '' and Exists('$(CustomBeforeWixCATargets)')" /> | 6 | <Import Project="$(CustomBeforeWixCATargets)" Condition=" '$(CustomBeforeWixCATargets)' != '' and Exists('$(CustomBeforeWixCATargets)')" /> |
8 | 7 | ||
@@ -11,13 +10,12 @@ | |||
11 | 10 | ||
12 | <TargetCAFileName Condition=" '$(TargetCAFileName)' == '' ">$(TargetName).CA$(TargetExt)</TargetCAFileName> | 11 | <TargetCAFileName Condition=" '$(TargetCAFileName)' == '' ">$(TargetName).CA$(TargetExt)</TargetCAFileName> |
13 | 12 | ||
14 | <WixSdkPath Condition=" '$(WixSdkPath)' == '' ">$(MSBuildThisFileDirectory)</WixSdkPath> | 13 | <MakeSfxCAPath Condition=" '$(MakeSfxCAPath)' == '' ">$(MSBuildThisFileDirectory)..\tools\</MakeSfxCAPath> |
15 | <WixSdkX86Path Condition=" '$(WixSdkX86Path)' == '' ">$(WixSdkPath)x86\</WixSdkX86Path> | ||
16 | <WixSdkX64Path Condition=" '$(WixSdkX64Path)' == '' ">$(WixSdkPath)x64\</WixSdkX64Path> | ||
17 | 14 | ||
18 | <MakeSfxCA Condition=" '$(MakeSfxCA)' == '' ">$(WixSdkPath)MakeSfxCA.exe</MakeSfxCA> | 15 | <MakeSfxCA Condition=" '$(MakeSfxCA)' == '' ">$(MakeSfxCAPath)WixToolset.Dtf.MakeSfxCA.exe</MakeSfxCA> |
19 | <SfxCADll Condition=" '$(SfxCADll)' == '' and '$(Platform)' == 'x64' ">$(WixSdkX64Path)SfxCA.dll</SfxCADll> | 16 | <SfxCADll Condition=" '$(SfxCADll)' == '' and '$(Platform)' == 'ARM64' ">$(MakeSfxCAPath)arm64\SfxCA.dll</SfxCADll> |
20 | <SfxCADll Condition=" '$(SfxCADll)' == '' ">$(WixSdkX86Path)SfxCA.dll</SfxCADll> | 17 | <SfxCADll Condition=" '$(SfxCADll)' == '' and '$(Platform)' == 'x64' ">$(MakeSfxCAPath)x64\SfxCA.dll</SfxCADll> |
18 | <SfxCADll Condition=" '$(SfxCADll)' == '' ">$(MakeSfxCAPath)x86\SfxCA.dll</SfxCADll> | ||
21 | </PropertyGroup> | 19 | </PropertyGroup> |
22 | 20 | ||
23 | <!-- | 21 | <!-- |
@@ -74,7 +72,7 @@ | |||
74 | <!-- Run the MakeSfxCA.exe CA packaging tool. --> | 72 | <!-- Run the MakeSfxCA.exe CA packaging tool. --> |
75 | <Exec Command='"$(MakeSfxCA)" "@(IntermediateCAPackage)" "$(SfxCADll)" "@(IntermediateCAAssembly)" "$(CustomActionContents)"' | 73 | <Exec Command='"$(MakeSfxCA)" "@(IntermediateCAPackage)" "$(SfxCADll)" "@(IntermediateCAAssembly)" "$(CustomActionContents)"' |
76 | WorkingDirectory="$(ProjectDir)" /> | 74 | WorkingDirectory="$(ProjectDir)" /> |
77 | 75 | ||
78 | <!-- Add modules to be copied to output dir. --> | 76 | <!-- Add modules to be copied to output dir. --> |
79 | <ItemGroup> | 77 | <ItemGroup> |
80 | <AddModules Include="@(IntermediateCAPackage)" /> | 78 | <AddModules Include="@(IntermediateCAPackage)" /> |
diff --git a/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.v3.ncrunchproject b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.v3.ncrunchproject new file mode 100644 index 00000000..cf22dfa9 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.v3.ncrunchproject | |||
@@ -0,0 +1,5 @@ | |||
1 | <ProjectConfiguration> | ||
2 | <Settings> | ||
3 | <HiddenComponentWarnings /> | ||
4 | </Settings> | ||
5 | </ProjectConfiguration> \ No newline at end of file | ||
diff --git a/src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.csproj b/src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.csproj deleted file mode 100644 index 1c81b861..00000000 --- a/src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.csproj +++ /dev/null | |||
@@ -1,40 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- 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. --> | ||
3 | |||
4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
5 | |||
6 | <PropertyGroup> | ||
7 | <TargetFramework>netcoreapp3.1</TargetFramework> | ||
8 | <IncludeBuildOutput>false</IncludeBuildOutput> | ||
9 | <Description>WiX Toolset Dtf MSBuild integration</Description> | ||
10 | <NuspecFile>$(MSBuildThisFileName).nuspec</NuspecFile> | ||
11 | <NuspecBasePath>$(OutputPath)publish\WixToolset.Dtf.MSBuild\</NuspecBasePath> | ||
12 | <NuspecProperties>Id=$(MSBuildThisFileName);Authors=$(Authors);Copyright=$(Copyright);Description=$(Description)</NuspecProperties> | ||
13 | </PropertyGroup> | ||
14 | |||
15 | <ItemGroup> | ||
16 | <None Remove="build\WixToolset.Dtf.MSBuild.props" /> | ||
17 | <None Remove="tools\wix.ca.targets" /> | ||
18 | </ItemGroup> | ||
19 | |||
20 | <ItemGroup> | ||
21 | <Content Include="build\WixToolset.Dtf.MSBuild.props"> | ||
22 | <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
23 | </Content> | ||
24 | <Content Include="tools\wix.ca.targets"> | ||
25 | <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
26 | </Content> | ||
27 | </ItemGroup> | ||
28 | |||
29 | <PropertyGroup> | ||
30 | <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuspecVersion</GenerateNuspecDependsOn> | ||
31 | </PropertyGroup> | ||
32 | |||
33 | <Target Name="SetNuspecVersion"> | ||
34 | <Error Text="Cannot pack $(MSBuildThisFileName) until all projects are published to: '$(NuspecBasePath)'. Run appveyor.cmd to publish projects properly." Condition=" !Exists('$(NuspecBasePath)') " /> | ||
35 | |||
36 | <PropertyGroup> | ||
37 | <NuspecProperties>$(NuspecProperties);Version=$(Version);ProjectFolder=$(MSBuildThisFileDirectory)</NuspecProperties> | ||
38 | </PropertyGroup> | ||
39 | </Target> | ||
40 | </Project> | ||
diff --git a/src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.nuspec b/src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.nuspec deleted file mode 100644 index 7f819cdb..00000000 --- a/src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.nuspec +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> | ||
3 | <metadata> | ||
4 | <id>$id$</id> | ||
5 | <version>$version$</version> | ||
6 | <authors>$authors$</authors> | ||
7 | <owners>$authors$</owners> | ||
8 | <license type="expression">MS-RL</license> | ||
9 | <requireLicenseAcceptance>false</requireLicenseAcceptance> | ||
10 | <description>$description$</description> | ||
11 | <copyright>$copyright$</copyright> | ||
12 | </metadata> | ||
13 | |||
14 | <files> | ||
15 | <file src="build\**\*" target="build" /> | ||
16 | <file src="tools\**\*" target="tools" /> | ||
17 | </files> | ||
18 | </package> | ||
diff --git a/src/dtf/WixToolset.Dtf.MSBuild/build/WixToolset.Dtf.MSBuild.props b/src/dtf/WixToolset.Dtf.MSBuild/build/WixToolset.Dtf.MSBuild.props deleted file mode 100644 index 06a98d6e..00000000 --- a/src/dtf/WixToolset.Dtf.MSBuild/build/WixToolset.Dtf.MSBuild.props +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- 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. --> | ||
3 | |||
4 | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> | ||
5 | <PropertyGroup> | ||
6 | <WixCATargetsPath Condition=" '$(WixCATargetsPath)' == '' ">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\tools\wix.ca.targets'))</WixCATargetsPath> | ||
7 | </PropertyGroup> | ||
8 | </Project> | ||
diff --git a/src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.cs b/src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.cs new file mode 100644 index 00000000..d701da20 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.cs | |||
@@ -0,0 +1,710 @@ | |||
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 | namespace WixToolset.Dtf.MakeSfxCA | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Collections.Generic; | ||
8 | using System.Security; | ||
9 | using System.Text; | ||
10 | using System.Reflection; | ||
11 | using WixToolset.Dtf.Compression; | ||
12 | using WixToolset.Dtf.Compression.Cab; | ||
13 | using WixToolset.Dtf.Resources; | ||
14 | using ResourceCollection = WixToolset.Dtf.Resources.ResourceCollection; | ||
15 | |||
16 | /// <summary> | ||
17 | /// Command-line tool for building self-extracting custom action packages. | ||
18 | /// Appends cabbed CA binaries to SfxCA.dll and fixes up the result's | ||
19 | /// entry-points and file version to look like the CA module. | ||
20 | /// </summary> | ||
21 | public static class MakeSfxCA | ||
22 | { | ||
23 | private const string REQUIRED_WI_ASSEMBLY = "WixToolset.Dtf.WindowsInstaller.dll"; | ||
24 | |||
25 | private static TextWriter log; | ||
26 | |||
27 | /// <summary> | ||
28 | /// Prints usage text for the tool. | ||
29 | /// </summary> | ||
30 | /// <param name="w">Console text writer.</param> | ||
31 | public static void Usage(TextWriter w) | ||
32 | { | ||
33 | w.WriteLine("WiX Toolset custom action packager version {0}", Assembly.GetExecutingAssembly().GetName().Version); | ||
34 | w.WriteLine("Copyright (C) .NET Foundation and contributors. All rights reserved."); | ||
35 | w.WriteLine(); | ||
36 | w.WriteLine("Usage: WixToolset.Dtf.MakeSfxCA <outputca.dll> SfxCA.dll <inputca.dll> [support files ...]"); | ||
37 | w.WriteLine(); | ||
38 | w.WriteLine("Makes a self-extracting managed MSI CA or UI DLL package."); | ||
39 | w.WriteLine("Support files must include " + MakeSfxCA.REQUIRED_WI_ASSEMBLY); | ||
40 | w.WriteLine("Support files optionally include CustomAction.config/EmbeddedUI.config"); | ||
41 | } | ||
42 | |||
43 | /// <summary> | ||
44 | /// Runs the MakeSfxCA command-line tool. | ||
45 | /// </summary> | ||
46 | /// <param name="args">Command-line arguments.</param> | ||
47 | /// <returns>0 on success, nonzero on failure.</returns> | ||
48 | public static int Main(string[] args) | ||
49 | { | ||
50 | if (args.Length < 3) | ||
51 | { | ||
52 | Usage(Console.Out); | ||
53 | return 1; | ||
54 | } | ||
55 | |||
56 | var output = args[0]; | ||
57 | var sfxDll = args[1]; | ||
58 | var inputs = new string[args.Length - 2]; | ||
59 | Array.Copy(args, 2, inputs, 0, inputs.Length); | ||
60 | |||
61 | try | ||
62 | { | ||
63 | Build(output, sfxDll, inputs, Console.Out); | ||
64 | return 0; | ||
65 | } | ||
66 | catch (ArgumentException ex) | ||
67 | { | ||
68 | Console.Error.WriteLine("Error: Invalid argument: " + ex.Message); | ||
69 | return 1; | ||
70 | } | ||
71 | catch (FileNotFoundException ex) | ||
72 | { | ||
73 | Console.Error.WriteLine("Error: Cannot find file: " + ex.Message); | ||
74 | return 1; | ||
75 | } | ||
76 | catch (Exception ex) | ||
77 | { | ||
78 | Console.Error.WriteLine("Error: Unexpected error: " + ex); | ||
79 | return 1; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /// <summary> | ||
84 | /// Packages up all the inputs to the output location. | ||
85 | /// </summary> | ||
86 | /// <exception cref="Exception">Various exceptions are thrown | ||
87 | /// if things go wrong.</exception> | ||
88 | public static void Build(string output, string sfxDll, IList<string> inputs, TextWriter log) | ||
89 | { | ||
90 | MakeSfxCA.log = log; | ||
91 | |||
92 | if (String.IsNullOrEmpty(output)) | ||
93 | { | ||
94 | throw new ArgumentNullException("output"); | ||
95 | } | ||
96 | |||
97 | if (String.IsNullOrEmpty(sfxDll)) | ||
98 | { | ||
99 | throw new ArgumentNullException("sfxDll"); | ||
100 | } | ||
101 | |||
102 | if (inputs == null || inputs.Count == 0) | ||
103 | { | ||
104 | throw new ArgumentNullException("inputs"); | ||
105 | } | ||
106 | |||
107 | if (!File.Exists(sfxDll)) | ||
108 | { | ||
109 | throw new FileNotFoundException(sfxDll); | ||
110 | } | ||
111 | |||
112 | var customActionAssembly = inputs[0]; | ||
113 | if (!File.Exists(customActionAssembly)) | ||
114 | { | ||
115 | throw new FileNotFoundException(customActionAssembly); | ||
116 | } | ||
117 | |||
118 | inputs = MakeSfxCA.SplitList(inputs); | ||
119 | |||
120 | var inputsMap = MakeSfxCA.GetPackFileMap(inputs); | ||
121 | |||
122 | var foundWIAssembly = false; | ||
123 | foreach (var input in inputsMap.Keys) | ||
124 | { | ||
125 | if (String.Compare(input, MakeSfxCA.REQUIRED_WI_ASSEMBLY, | ||
126 | StringComparison.OrdinalIgnoreCase) == 0) | ||
127 | { | ||
128 | foundWIAssembly = true; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | if (!foundWIAssembly) | ||
133 | { | ||
134 | throw new ArgumentException(MakeSfxCA.REQUIRED_WI_ASSEMBLY + | ||
135 | " must be included in the list of support files. " + | ||
136 | "If using the MSBuild targets, make sure the assembly reference " + | ||
137 | "has the Private (Copy Local) flag set."); | ||
138 | } | ||
139 | |||
140 | MakeSfxCA.ResolveDependentAssemblies(inputsMap, Path.GetDirectoryName(customActionAssembly)); | ||
141 | |||
142 | var entryPoints = MakeSfxCA.FindEntryPoints(customActionAssembly); | ||
143 | var uiClass = MakeSfxCA.FindEmbeddedUIClass(customActionAssembly); | ||
144 | |||
145 | if (entryPoints.Count == 0 && uiClass == null) | ||
146 | { | ||
147 | throw new ArgumentException( | ||
148 | "No CA or UI entry points found in module: " + customActionAssembly); | ||
149 | } | ||
150 | else if (entryPoints.Count > 0 && uiClass != null) | ||
151 | { | ||
152 | throw new NotSupportedException( | ||
153 | "CA and UI entry points cannot be in the same assembly: " + customActionAssembly); | ||
154 | } | ||
155 | |||
156 | var dir = Path.GetDirectoryName(output); | ||
157 | if (dir.Length > 0 && !Directory.Exists(dir)) | ||
158 | { | ||
159 | Directory.CreateDirectory(dir); | ||
160 | } | ||
161 | |||
162 | using (Stream outputStream = File.Create(output)) | ||
163 | { | ||
164 | MakeSfxCA.WriteEntryModule(sfxDll, outputStream, entryPoints, uiClass); | ||
165 | } | ||
166 | |||
167 | MakeSfxCA.CopyVersionResource(customActionAssembly, output); | ||
168 | |||
169 | MakeSfxCA.PackInputFiles(output, inputsMap); | ||
170 | |||
171 | log.WriteLine("MakeSfxCA finished: " + new FileInfo(output).FullName); | ||
172 | } | ||
173 | |||
174 | /// <summary> | ||
175 | /// Splits any list items delimited by semicolons into separate items. | ||
176 | /// </summary> | ||
177 | /// <param name="list">Read-only input list.</param> | ||
178 | /// <returns>New list with resulting split items.</returns> | ||
179 | private static IList<string> SplitList(IList<string> list) | ||
180 | { | ||
181 | var newList = new List<string>(list.Count); | ||
182 | |||
183 | foreach (var item in list) | ||
184 | { | ||
185 | if (!String.IsNullOrEmpty(item)) | ||
186 | { | ||
187 | foreach (var splitItem in item.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) | ||
188 | { | ||
189 | newList.Add(splitItem); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | return newList; | ||
195 | } | ||
196 | |||
197 | /// <summary> | ||
198 | /// Sets up a reflection-only assembly-resolve-handler to handle loading dependent assemblies during reflection. | ||
199 | /// </summary> | ||
200 | /// <param name="inputFiles">List of input files which include non-GAC dependent assemblies.</param> | ||
201 | /// <param name="inputDir">Directory to auto-locate additional dependent assemblies.</param> | ||
202 | /// <remarks> | ||
203 | /// Also searches the assembly's directory for unspecified dependent assemblies, and adds them | ||
204 | /// to the list of input files if found. | ||
205 | /// </remarks> | ||
206 | private static void ResolveDependentAssemblies(IDictionary<string, string> inputFiles, string inputDir) | ||
207 | { | ||
208 | AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += delegate(object sender, ResolveEventArgs args) | ||
209 | { | ||
210 | AssemblyName resolveName = new AssemblyName(args.Name); | ||
211 | Assembly assembly = null; | ||
212 | |||
213 | // First, try to find the assembly in the list of input files. | ||
214 | foreach (var inputFile in inputFiles.Values) | ||
215 | { | ||
216 | var inputName = Path.GetFileNameWithoutExtension(inputFile); | ||
217 | var inputExtension = Path.GetExtension(inputFile); | ||
218 | if (String.Equals(inputName, resolveName.Name, StringComparison.OrdinalIgnoreCase) && | ||
219 | (String.Equals(inputExtension, ".dll", StringComparison.OrdinalIgnoreCase) || | ||
220 | String.Equals(inputExtension, ".exe", StringComparison.OrdinalIgnoreCase))) | ||
221 | { | ||
222 | assembly = MakeSfxCA.TryLoadDependentAssembly(inputFile); | ||
223 | |||
224 | if (assembly != null) | ||
225 | { | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | // Second, try to find the assembly in the input directory. | ||
232 | if (assembly == null && inputDir != null) | ||
233 | { | ||
234 | string assemblyPath = null; | ||
235 | if (File.Exists(Path.Combine(inputDir, resolveName.Name) + ".dll")) | ||
236 | { | ||
237 | assemblyPath = Path.Combine(inputDir, resolveName.Name) + ".dll"; | ||
238 | } | ||
239 | else if (File.Exists(Path.Combine(inputDir, resolveName.Name) + ".exe")) | ||
240 | { | ||
241 | assemblyPath = Path.Combine(inputDir, resolveName.Name) + ".exe"; | ||
242 | } | ||
243 | |||
244 | if (assemblyPath != null) | ||
245 | { | ||
246 | assembly = MakeSfxCA.TryLoadDependentAssembly(assemblyPath); | ||
247 | |||
248 | if (assembly != null) | ||
249 | { | ||
250 | // Add this detected dependency to the list of files to be packed. | ||
251 | inputFiles.Add(Path.GetFileName(assemblyPath), assemblyPath); | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | // Third, try to load the assembly from the GAC. | ||
257 | if (assembly == null) | ||
258 | { | ||
259 | try | ||
260 | { | ||
261 | assembly = Assembly.ReflectionOnlyLoad(args.Name); | ||
262 | } | ||
263 | catch (FileNotFoundException) | ||
264 | { | ||
265 | } | ||
266 | } | ||
267 | |||
268 | if (assembly != null) | ||
269 | { | ||
270 | if (String.Equals(assembly.GetName().ToString(), resolveName.ToString())) | ||
271 | { | ||
272 | log.WriteLine(" Loaded dependent assembly: " + assembly.Location); | ||
273 | return assembly; | ||
274 | } | ||
275 | |||
276 | log.WriteLine(" Warning: Loaded mismatched dependent assembly: " + assembly.Location); | ||
277 | log.WriteLine(" Loaded assembly : " + assembly.GetName()); | ||
278 | log.WriteLine(" Reference assembly: " + resolveName); | ||
279 | } | ||
280 | else | ||
281 | { | ||
282 | log.WriteLine(" Error: Dependent assembly not supplied: " + resolveName); | ||
283 | } | ||
284 | |||
285 | return null; | ||
286 | }; | ||
287 | } | ||
288 | |||
289 | /// <summary> | ||
290 | /// Attempts a reflection-only load of a dependent assembly, logging the error if the load fails. | ||
291 | /// </summary> | ||
292 | /// <param name="assemblyPath">Path of the assembly file to laod.</param> | ||
293 | /// <returns>Loaded assembly, or null if the load failed.</returns> | ||
294 | private static Assembly TryLoadDependentAssembly(string assemblyPath) | ||
295 | { | ||
296 | Assembly assembly = null; | ||
297 | try | ||
298 | { | ||
299 | assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath); | ||
300 | } | ||
301 | catch (IOException ex) | ||
302 | { | ||
303 | log.WriteLine(" Error: Failed to load dependent assembly: {0}. {1}", assemblyPath, ex.Message); | ||
304 | } | ||
305 | catch (BadImageFormatException ex) | ||
306 | { | ||
307 | log.WriteLine(" Error: Failed to load dependent assembly: {0}. {1}", assemblyPath, ex.Message); | ||
308 | } | ||
309 | catch (SecurityException ex) | ||
310 | { | ||
311 | log.WriteLine(" Error: Failed to load dependent assembly: {0}. {1}", assemblyPath, ex.Message); | ||
312 | } | ||
313 | |||
314 | return assembly; | ||
315 | } | ||
316 | |||
317 | /// <summary> | ||
318 | /// Searches the types in the input assembly for a type that implements IEmbeddedUI. | ||
319 | /// </summary> | ||
320 | /// <param name="module"></param> | ||
321 | /// <returns></returns> | ||
322 | private static string FindEmbeddedUIClass(string module) | ||
323 | { | ||
324 | log.WriteLine("Searching for an embedded UI class in {0}", Path.GetFileName(module)); | ||
325 | |||
326 | string uiClass = null; | ||
327 | |||
328 | var assembly = Assembly.ReflectionOnlyLoadFrom(module); | ||
329 | |||
330 | foreach (var type in assembly.GetExportedTypes()) | ||
331 | { | ||
332 | if (!type.IsAbstract) | ||
333 | { | ||
334 | foreach (var interfaceType in type.GetInterfaces()) | ||
335 | { | ||
336 | if (interfaceType.FullName == "WixToolset.Dtf.WindowsInstaller.IEmbeddedUI") | ||
337 | { | ||
338 | if (uiClass == null) | ||
339 | { | ||
340 | uiClass = assembly.GetName().Name + "!" + type.FullName; | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | throw new ArgumentException("Multiple IEmbeddedUI implementations found."); | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | return uiClass; | ||
352 | } | ||
353 | |||
354 | /// <summary> | ||
355 | /// Reflects on an input CA module to locate custom action entry-points. | ||
356 | /// </summary> | ||
357 | /// <param name="module">Assembly module with CA entry-points.</param> | ||
358 | /// <returns>Mapping from entry-point names to assembly!class.method paths.</returns> | ||
359 | private static IDictionary<string, string> FindEntryPoints(string module) | ||
360 | { | ||
361 | log.WriteLine("Searching for custom action entry points " + | ||
362 | "in {0}", Path.GetFileName(module)); | ||
363 | |||
364 | var entryPoints = new Dictionary<string, string>(); | ||
365 | |||
366 | var assembly = Assembly.ReflectionOnlyLoadFrom(module); | ||
367 | |||
368 | foreach (var type in assembly.GetExportedTypes()) | ||
369 | { | ||
370 | foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static)) | ||
371 | { | ||
372 | var entryPointName = MakeSfxCA.GetEntryPoint(method); | ||
373 | if (entryPointName != null) | ||
374 | { | ||
375 | var entryPointPath = String.Format( | ||
376 | "{0}!{1}.{2}", | ||
377 | Path.GetFileNameWithoutExtension(module), | ||
378 | type.FullName, | ||
379 | method.Name); | ||
380 | entryPoints.Add(entryPointName, entryPointPath); | ||
381 | |||
382 | log.WriteLine(" {0}={1}", entryPointName, entryPointPath); | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | |||
387 | return entryPoints; | ||
388 | } | ||
389 | |||
390 | /// <summary> | ||
391 | /// Check for a CustomActionAttribute and return the entrypoint name for the method if it is a CA method. | ||
392 | /// </summary> | ||
393 | /// <param name="method">A public static method.</param> | ||
394 | /// <returns>Entrypoint name for the method as specified by the custom action attribute or just the method name, | ||
395 | /// or null if the method is not a custom action method.</returns> | ||
396 | private static string GetEntryPoint(MethodInfo method) | ||
397 | { | ||
398 | IList<CustomAttributeData> attributes; | ||
399 | try | ||
400 | { | ||
401 | attributes = CustomAttributeData.GetCustomAttributes(method); | ||
402 | } | ||
403 | catch (FileLoadException) | ||
404 | { | ||
405 | // Already logged load failures in the assembly-resolve-handler. | ||
406 | return null; | ||
407 | } | ||
408 | |||
409 | foreach (CustomAttributeData attribute in attributes) | ||
410 | { | ||
411 | if (attribute.ToString().StartsWith( | ||
412 | "[WixToolset.Dtf.WindowsInstaller.CustomActionAttribute(", | ||
413 | StringComparison.Ordinal)) | ||
414 | { | ||
415 | string entryPointName = null; | ||
416 | foreach (var argument in attribute.ConstructorArguments) | ||
417 | { | ||
418 | // The entry point name is the first positional argument, if specified. | ||
419 | entryPointName = (string) argument.Value; | ||
420 | break; | ||
421 | } | ||
422 | |||
423 | if (String.IsNullOrEmpty(entryPointName)) | ||
424 | { | ||
425 | entryPointName = method.Name; | ||
426 | } | ||
427 | |||
428 | return entryPointName; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | return null; | ||
433 | } | ||
434 | |||
435 | /// <summary> | ||
436 | /// Counts the number of template entrypoints in SfxCA.dll. | ||
437 | /// </summary> | ||
438 | /// <remarks> | ||
439 | /// Depending on the requirements, SfxCA.dll might be built with | ||
440 | /// more entrypoints than the default. | ||
441 | /// </remarks> | ||
442 | private static int GetEntryPointSlotCount(byte[] fileBytes, string entryPointFormat) | ||
443 | { | ||
444 | for (var count = 0; ; count++) | ||
445 | { | ||
446 | var templateName = String.Format(entryPointFormat, count); | ||
447 | var templateAsciiBytes = Encoding.ASCII.GetBytes(templateName); | ||
448 | |||
449 | var nameOffset = FindBytes(fileBytes, templateAsciiBytes); | ||
450 | if (nameOffset < 0) | ||
451 | { | ||
452 | return count; | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | |||
457 | /// <summary> | ||
458 | /// Writes a modified version of SfxCA.dll to the output stream, | ||
459 | /// with the template entry-points mapped to the CA entry-points. | ||
460 | /// </summary> | ||
461 | /// <remarks> | ||
462 | /// To avoid having to recompile SfxCA.dll for every different set of CAs, | ||
463 | /// this method looks for a preset number of template entry-points in the | ||
464 | /// binary file and overwrites their entrypoint name and string data with | ||
465 | /// CA-specific values. | ||
466 | /// </remarks> | ||
467 | private static void WriteEntryModule( | ||
468 | string sfxDll, Stream outputStream, IDictionary<string, string> entryPoints, string uiClass) | ||
469 | { | ||
470 | log.WriteLine("Modifying SfxCA.dll stub"); | ||
471 | |||
472 | byte[] fileBytes; | ||
473 | using (var readStream = File.OpenRead(sfxDll)) | ||
474 | { | ||
475 | fileBytes = new byte[(int) readStream.Length]; | ||
476 | readStream.Read(fileBytes, 0, fileBytes.Length); | ||
477 | } | ||
478 | |||
479 | const string ENTRYPOINT_FORMAT = "CustomActionEntryPoint{0:d03}"; | ||
480 | const int MAX_ENTRYPOINT_NAME = 72; | ||
481 | const int MAX_ENTRYPOINT_PATH = 160; | ||
482 | //var emptyBytes = new byte[0]; | ||
483 | |||
484 | var slotCount = MakeSfxCA.GetEntryPointSlotCount(fileBytes, ENTRYPOINT_FORMAT); | ||
485 | |||
486 | if (slotCount == 0) | ||
487 | { | ||
488 | throw new ArgumentException("Invalid SfxCA.dll file."); | ||
489 | } | ||
490 | |||
491 | if (entryPoints.Count > slotCount) | ||
492 | { | ||
493 | throw new ArgumentException(String.Format( | ||
494 | "The custom action assembly has {0} entrypoints, which is more than the maximum ({1}). " + | ||
495 | "Refactor the custom actions or add more entrypoint slots in SfxCA\\EntryPoints.h.", | ||
496 | entryPoints.Count, slotCount)); | ||
497 | } | ||
498 | |||
499 | var slotSort = new string[slotCount]; | ||
500 | for (var i = 0; i < slotCount - entryPoints.Count; i++) | ||
501 | { | ||
502 | slotSort[i] = String.Empty; | ||
503 | } | ||
504 | |||
505 | entryPoints.Keys.CopyTo(slotSort, slotCount - entryPoints.Count); | ||
506 | Array.Sort<string>(slotSort, slotCount - entryPoints.Count, entryPoints.Count, StringComparer.Ordinal); | ||
507 | |||
508 | for (var i = 0; ; i++) | ||
509 | { | ||
510 | var templateName = String.Format(ENTRYPOINT_FORMAT, i); | ||
511 | var templateAsciiBytes = Encoding.ASCII.GetBytes(templateName); | ||
512 | var templateUniBytes = Encoding.Unicode.GetBytes(templateName); | ||
513 | |||
514 | var nameOffset = MakeSfxCA.FindBytes(fileBytes, templateAsciiBytes); | ||
515 | if (nameOffset < 0) | ||
516 | { | ||
517 | break; | ||
518 | } | ||
519 | |||
520 | var pathOffset = MakeSfxCA.FindBytes(fileBytes, templateUniBytes); | ||
521 | if (pathOffset < 0) | ||
522 | { | ||
523 | break; | ||
524 | } | ||
525 | |||
526 | var entryPointName = slotSort[i]; | ||
527 | var entryPointPath = entryPointName.Length > 0 ? | ||
528 | entryPoints[entryPointName] : String.Empty; | ||
529 | |||
530 | if (entryPointName.Length > MAX_ENTRYPOINT_NAME) | ||
531 | { | ||
532 | throw new ArgumentException(String.Format( | ||
533 | "Entry point name exceeds limit of {0} characters: {1}", | ||
534 | MAX_ENTRYPOINT_NAME, | ||
535 | entryPointName)); | ||
536 | } | ||
537 | |||
538 | if (entryPointPath.Length > MAX_ENTRYPOINT_PATH) | ||
539 | { | ||
540 | throw new ArgumentException(String.Format( | ||
541 | "Entry point path exceeds limit of {0} characters: {1}", | ||
542 | MAX_ENTRYPOINT_PATH, | ||
543 | entryPointPath)); | ||
544 | } | ||
545 | |||
546 | var replaceNameBytes = Encoding.ASCII.GetBytes(entryPointName); | ||
547 | var replacePathBytes = Encoding.Unicode.GetBytes(entryPointPath); | ||
548 | |||
549 | MakeSfxCA.ReplaceBytes(fileBytes, nameOffset, MAX_ENTRYPOINT_NAME, replaceNameBytes); | ||
550 | MakeSfxCA.ReplaceBytes(fileBytes, pathOffset, MAX_ENTRYPOINT_PATH * 2, replacePathBytes); | ||
551 | } | ||
552 | |||
553 | if (entryPoints.Count == 0 && uiClass != null) | ||
554 | { | ||
555 | // Remove the zzz prefix from exported EmbeddedUI entry-points. | ||
556 | foreach (var export in new string[] { "InitializeEmbeddedUI", "EmbeddedUIHandler", "ShutdownEmbeddedUI" }) | ||
557 | { | ||
558 | var exportNameBytes = Encoding.ASCII.GetBytes("zzz" + export); | ||
559 | |||
560 | var exportOffset = MakeSfxCA.FindBytes(fileBytes, exportNameBytes); | ||
561 | if (exportOffset < 0) | ||
562 | { | ||
563 | throw new ArgumentException("Input SfxCA.dll does not contain exported entry-point: " + export); | ||
564 | } | ||
565 | |||
566 | var replaceNameBytes = Encoding.ASCII.GetBytes(export); | ||
567 | MakeSfxCA.ReplaceBytes(fileBytes, exportOffset, exportNameBytes.Length, replaceNameBytes); | ||
568 | } | ||
569 | |||
570 | if (uiClass.Length > MAX_ENTRYPOINT_PATH) | ||
571 | { | ||
572 | throw new ArgumentException(String.Format( | ||
573 | "UI class full name exceeds limit of {0} characters: {1}", | ||
574 | MAX_ENTRYPOINT_PATH, | ||
575 | uiClass)); | ||
576 | } | ||
577 | |||
578 | var templateBytes = Encoding.Unicode.GetBytes("InitializeEmbeddedUI_FullClassName"); | ||
579 | var replaceBytes = Encoding.Unicode.GetBytes(uiClass); | ||
580 | |||
581 | // Fill in the embedded UI implementor class so the proxy knows which one to load. | ||
582 | var replaceOffset = MakeSfxCA.FindBytes(fileBytes, templateBytes); | ||
583 | if (replaceOffset >= 0) | ||
584 | { | ||
585 | MakeSfxCA.ReplaceBytes(fileBytes, replaceOffset, MAX_ENTRYPOINT_PATH * 2, replaceBytes); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | outputStream.Write(fileBytes, 0, fileBytes.Length); | ||
590 | } | ||
591 | |||
592 | /// <summary> | ||
593 | /// Searches for a sub-array of bytes within a larger array of bytes. | ||
594 | /// </summary> | ||
595 | private static int FindBytes(byte[] source, byte[] find) | ||
596 | { | ||
597 | for (var i = 0; i < source.Length; i++) | ||
598 | { | ||
599 | int j; | ||
600 | for (j = 0; j < find.Length; j++) | ||
601 | { | ||
602 | if (source[i + j] != find[j]) | ||
603 | { | ||
604 | break; | ||
605 | } | ||
606 | } | ||
607 | |||
608 | if (j == find.Length) | ||
609 | { | ||
610 | return i; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | return -1; | ||
615 | } | ||
616 | |||
617 | /// <summary> | ||
618 | /// Replaces a range of bytes with new bytes, padding any extra part | ||
619 | /// of the range with zeroes. | ||
620 | /// </summary> | ||
621 | private static void ReplaceBytes( | ||
622 | byte[] source, int offset, int length, byte[] replace) | ||
623 | { | ||
624 | for (var i = 0; i < length; i++) | ||
625 | { | ||
626 | if (i < replace.Length) | ||
627 | { | ||
628 | source[offset + i] = replace[i]; | ||
629 | } | ||
630 | else | ||
631 | { | ||
632 | source[offset + i] = 0; | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | /// <summary> | ||
638 | /// Print the name of one file as it is being packed into the cab. | ||
639 | /// </summary> | ||
640 | private static void PackProgress(object source, ArchiveProgressEventArgs e) | ||
641 | { | ||
642 | if (e.ProgressType == ArchiveProgressType.StartFile && log != null) | ||
643 | { | ||
644 | log.WriteLine(" {0}", e.CurrentFileName); | ||
645 | } | ||
646 | } | ||
647 | |||
648 | /// <summary> | ||
649 | /// Gets a mapping from filenames as they will be in the cab to filenames | ||
650 | /// as they are currently on disk. | ||
651 | /// </summary> | ||
652 | /// <remarks> | ||
653 | /// By default, all files will be placed in the root of the cab. But inputs may | ||
654 | /// optionally include an alternate inside-cab file path before an equals sign. | ||
655 | /// </remarks> | ||
656 | private static IDictionary<string, string> GetPackFileMap(IList<string> inputs) | ||
657 | { | ||
658 | var fileMap = new Dictionary<string, string>(); | ||
659 | foreach (var inputFile in inputs) | ||
660 | { | ||
661 | if (inputFile.IndexOf('=') > 0) | ||
662 | { | ||
663 | var parse = inputFile.Split('='); | ||
664 | if (!fileMap.ContainsKey(parse[0])) | ||
665 | { | ||
666 | fileMap.Add(parse[0], parse[1]); | ||
667 | } | ||
668 | } | ||
669 | else | ||
670 | { | ||
671 | var fileName = Path.GetFileName(inputFile); | ||
672 | if (!fileMap.ContainsKey(fileName)) | ||
673 | { | ||
674 | fileMap.Add(fileName, inputFile); | ||
675 | } | ||
676 | } | ||
677 | } | ||
678 | return fileMap; | ||
679 | } | ||
680 | |||
681 | /// <summary> | ||
682 | /// Packs the input files into a cab that is appended to the | ||
683 | /// output SfxCA.dll. | ||
684 | /// </summary> | ||
685 | private static void PackInputFiles(string outputFile, IDictionary<string, string> fileMap) | ||
686 | { | ||
687 | log.WriteLine("Packaging files"); | ||
688 | |||
689 | var cabInfo = new CabInfo(outputFile); | ||
690 | cabInfo.PackFileSet(null, fileMap, CompressionLevel.Max, PackProgress); | ||
691 | } | ||
692 | |||
693 | /// <summary> | ||
694 | /// Copies the version resource information from the CA module to | ||
695 | /// the CA package. This gives the package the file version and | ||
696 | /// description of the CA module, instead of the version and | ||
697 | /// description of SfxCA.dll. | ||
698 | /// </summary> | ||
699 | private static void CopyVersionResource(string sourceFile, string destFile) | ||
700 | { | ||
701 | log.WriteLine("Copying file version info from {0} to {1}", | ||
702 | sourceFile, destFile); | ||
703 | |||
704 | var rc = new ResourceCollection(); | ||
705 | rc.Find(sourceFile, ResourceType.Version); | ||
706 | rc.Load(sourceFile); | ||
707 | rc.Save(destFile); | ||
708 | } | ||
709 | } | ||
710 | } | ||
diff --git a/src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.exe.manifest b/src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.exe.manifest new file mode 100644 index 00000000..5224db50 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.exe.manifest | |||
@@ -0,0 +1,11 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
2 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
3 | <assemblyIdentity name="WixToolset.Dtf.MakeSfxCA" version="4.0.0.0" processorArchitecture="x86" type="win32"/> | ||
4 | <description>WiX Toolset Compiler</description> | ||
5 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
6 | <security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security> | ||
7 | </trustInfo> | ||
8 | <application xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
9 | <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings"><ws2:longPathAware>true</ws2:longPathAware></windowsSettings> | ||
10 | </application> | ||
11 | </assembly> | ||
diff --git a/src/dtf/WixToolset.Dtf.MakeSfxCA/WixToolset.Dtf.MakeSfxCA.csproj b/src/dtf/WixToolset.Dtf.MakeSfxCA/WixToolset.Dtf.MakeSfxCA.csproj new file mode 100644 index 00000000..e62aaed3 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.MakeSfxCA/WixToolset.Dtf.MakeSfxCA.csproj | |||
@@ -0,0 +1,19 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- 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. --> | ||
3 | |||
4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
5 | <PropertyGroup> | ||
6 | <OutputType>Exe</OutputType> | ||
7 | <TargetFrameworks>net472</TargetFrameworks> | ||
8 | <IsPackable>false</IsPackable> | ||
9 | <DebugType>embedded</DebugType> | ||
10 | <AppConfig>app.config</AppConfig> | ||
11 | <ApplicationManifest>MakeSfxCA.exe.manifest</ApplicationManifest> | ||
12 | </PropertyGroup> | ||
13 | |||
14 | <ItemGroup> | ||
15 | <ProjectReference Include="..\WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj" /> | ||
16 | <ProjectReference Include="..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj" /> | ||
17 | <ProjectReference Include="..\WixToolset.Dtf.Resources\WixToolset.Dtf.Resources.csproj" /> | ||
18 | </ItemGroup> | ||
19 | </Project> | ||
diff --git a/src/dtf/WixToolset.Dtf.MakeSfxCA/app.config b/src/dtf/WixToolset.Dtf.MakeSfxCA/app.config new file mode 100644 index 00000000..29bbc006 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.MakeSfxCA/app.config | |||
@@ -0,0 +1,7 @@ | |||
1 | <?xml version="1.0" encoding="utf-8" ?> | ||
2 | <configuration> | ||
3 | <runtime> | ||
4 | <loadFromRemoteSources enabled="true"/> | ||
5 | <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" /> | ||
6 | </runtime> | ||
7 | </configuration> | ||
diff --git a/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs b/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs index b37d5311..8d46b54b 100644 --- a/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs +++ b/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs | |||
@@ -18,7 +18,7 @@ namespace WixToolset.Dtf.Resources | |||
18 | /// <remarks> | 18 | /// <remarks> |
19 | /// To use this class:<list type="number"> | 19 | /// To use this class:<list type="number"> |
20 | /// <item>Create a new ResourceCollection</item> | 20 | /// <item>Create a new ResourceCollection</item> |
21 | /// <item>Locate resources for the collection by calling one of the <see cref="ResourceCollection.Find(string)"/> methods</item> | 21 | /// <item>Locate resources for the collection by calling one of the <see cref="ResourceCollection.Find(String)"/> methods</item> |
22 | /// <item>Load data of one or more <see cref="Resource"/>s from a file by calling the <see cref="Load"/> method of the | 22 | /// <item>Load data of one or more <see cref="Resource"/>s from a file by calling the <see cref="Load"/> method of the |
23 | /// Resource class, or load them all at once (more efficient) with the <see cref="Load"/> method of the ResourceCollection.</item> | 23 | /// Resource class, or load them all at once (more efficient) with the <see cref="Load"/> method of the ResourceCollection.</item> |
24 | /// <item>Read and/or edit data of the individual Resource objects using the methods on that class.</item> | 24 | /// <item>Read and/or edit data of the individual Resource objects using the methods on that class.</item> |
@@ -28,7 +28,7 @@ namespace WixToolset.Dtf.Resources | |||
28 | /// </remarks> | 28 | /// </remarks> |
29 | public class ResourceCollection : ICollection<Resource> | 29 | public class ResourceCollection : ICollection<Resource> |
30 | { | 30 | { |
31 | private List<Resource> resources; | 31 | private readonly List<Resource> resources; |
32 | 32 | ||
33 | /// <summary> | 33 | /// <summary> |
34 | /// Creates a new, empty ResourceCollection. | 34 | /// Creates a new, empty ResourceCollection. |
@@ -48,17 +48,17 @@ namespace WixToolset.Dtf.Resources | |||
48 | { | 48 | { |
49 | this.Clear(); | 49 | this.Clear(); |
50 | 50 | ||
51 | IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); | 51 | var module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); |
52 | if (module == IntPtr.Zero) | 52 | if (module == IntPtr.Zero) |
53 | { | 53 | { |
54 | int err = Marshal.GetLastWin32Error(); | 54 | var err = Marshal.GetLastWin32Error(); |
55 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to load resource file. Error code: {0}", err)); | 55 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to load resource file. Error code: {0}", err)); |
56 | } | 56 | } |
57 | try | 57 | try |
58 | { | 58 | { |
59 | if (!NativeMethods.EnumResourceTypes(module, new NativeMethods.EnumResTypesProc(this.EnumResTypes), IntPtr.Zero)) | 59 | if (!NativeMethods.EnumResourceTypes(module, new NativeMethods.EnumResTypesProc(this.EnumResTypes), IntPtr.Zero)) |
60 | { | 60 | { |
61 | int err = Marshal.GetLastWin32Error(); | 61 | var err = Marshal.GetLastWin32Error(); |
62 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to enumerate resources. Error code: {0}", err)); | 62 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to enumerate resources. Error code: {0}", err)); |
63 | } | 63 | } |
64 | } | 64 | } |
@@ -79,12 +79,12 @@ namespace WixToolset.Dtf.Resources | |||
79 | { | 79 | { |
80 | this.Clear(); | 80 | this.Clear(); |
81 | 81 | ||
82 | IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); | 82 | var module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); |
83 | try | 83 | try |
84 | { | 84 | { |
85 | if (!NativeMethods.EnumResourceNames(module, (string) type, new NativeMethods.EnumResNamesProc(this.EnumResNames), IntPtr.Zero)) | 85 | if (!NativeMethods.EnumResourceNames(module, (string) type, new NativeMethods.EnumResNamesProc(this.EnumResNames), IntPtr.Zero)) |
86 | { | 86 | { |
87 | int err = Marshal.GetLastWin32Error(); | 87 | var err = Marshal.GetLastWin32Error(); |
88 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error. Error code: {0}", err)); | 88 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error. Error code: {0}", err)); |
89 | } | 89 | } |
90 | } | 90 | } |
@@ -106,12 +106,12 @@ namespace WixToolset.Dtf.Resources | |||
106 | { | 106 | { |
107 | this.Clear(); | 107 | this.Clear(); |
108 | 108 | ||
109 | IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); | 109 | var module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); |
110 | try | 110 | try |
111 | { | 111 | { |
112 | if (!NativeMethods.EnumResourceLanguages(module, (string) type, name, new NativeMethods.EnumResLangsProc(this.EnumResLangs), IntPtr.Zero)) | 112 | if (!NativeMethods.EnumResourceLanguages(module, (string) type, name, new NativeMethods.EnumResLangsProc(this.EnumResLangs), IntPtr.Zero)) |
113 | { | 113 | { |
114 | int err = Marshal.GetLastWin32Error(); | 114 | var err = Marshal.GetLastWin32Error(); |
115 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err)); | 115 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err)); |
116 | } | 116 | } |
117 | } | 117 | } |
@@ -123,9 +123,9 @@ namespace WixToolset.Dtf.Resources | |||
123 | 123 | ||
124 | private bool EnumResTypes(IntPtr module, IntPtr type, IntPtr param) | 124 | private bool EnumResTypes(IntPtr module, IntPtr type, IntPtr param) |
125 | { | 125 | { |
126 | if (!NativeMethods.EnumResourceNames(module, type, new NativeMethods.EnumResNamesProc(EnumResNames), IntPtr.Zero)) | 126 | if (!NativeMethods.EnumResourceNames(module, type, new NativeMethods.EnumResNamesProc(this.EnumResNames), IntPtr.Zero)) |
127 | { | 127 | { |
128 | int err = Marshal.GetLastWin32Error(); | 128 | var err = Marshal.GetLastWin32Error(); |
129 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error! Error code: {0}", err)); | 129 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error! Error code: {0}", err)); |
130 | } | 130 | } |
131 | return true; | 131 | return true; |
@@ -133,9 +133,9 @@ namespace WixToolset.Dtf.Resources | |||
133 | 133 | ||
134 | private bool EnumResNames(IntPtr module, IntPtr type, IntPtr name, IntPtr param) | 134 | private bool EnumResNames(IntPtr module, IntPtr type, IntPtr name, IntPtr param) |
135 | { | 135 | { |
136 | if (!NativeMethods.EnumResourceLanguages(module, type, name, new NativeMethods.EnumResLangsProc(EnumResLangs), IntPtr.Zero)) | 136 | if (!NativeMethods.EnumResourceLanguages(module, type, name, new NativeMethods.EnumResLangsProc(this.EnumResLangs), IntPtr.Zero)) |
137 | { | 137 | { |
138 | int err = Marshal.GetLastWin32Error(); | 138 | var err = Marshal.GetLastWin32Error(); |
139 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err)); | 139 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err)); |
140 | } | 140 | } |
141 | return true; | 141 | return true; |
@@ -179,10 +179,10 @@ namespace WixToolset.Dtf.Resources | |||
179 | /// <param name="file">The file from which resources are loaded.</param> | 179 | /// <param name="file">The file from which resources are loaded.</param> |
180 | public void Load(string file) | 180 | public void Load(string file) |
181 | { | 181 | { |
182 | IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); | 182 | var module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); |
183 | try | 183 | try |
184 | { | 184 | { |
185 | foreach (Resource res in this) | 185 | foreach (var res in this) |
186 | { | 186 | { |
187 | res.Load(module); | 187 | res.Load(module); |
188 | } | 188 | } |
@@ -199,17 +199,17 @@ namespace WixToolset.Dtf.Resources | |||
199 | /// <param name="file">The file to which resources are saved.</param> | 199 | /// <param name="file">The file to which resources are saved.</param> |
200 | public void Save(string file) | 200 | public void Save(string file) |
201 | { | 201 | { |
202 | IntPtr updateHandle = IntPtr.Zero; | 202 | var updateHandle = IntPtr.Zero; |
203 | try | 203 | try |
204 | { | 204 | { |
205 | updateHandle = NativeMethods.BeginUpdateResource(file, false); | 205 | updateHandle = NativeMethods.BeginUpdateResource(file, false); |
206 | foreach (Resource res in this) | 206 | foreach (var res in this) |
207 | { | 207 | { |
208 | res.Save(updateHandle); | 208 | res.Save(updateHandle); |
209 | } | 209 | } |
210 | if (!NativeMethods.EndUpdateResource(updateHandle, false)) | 210 | if (!NativeMethods.EndUpdateResource(updateHandle, false)) |
211 | { | 211 | { |
212 | int err = Marshal.GetLastWin32Error(); | 212 | var err = Marshal.GetLastWin32Error(); |
213 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error {0}", err)); | 213 | throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error {0}", err)); |
214 | } | 214 | } |
215 | updateHandle = IntPtr.Zero; | 215 | updateHandle = IntPtr.Zero; |
diff --git a/src/dtf/dtf.cmd b/src/dtf/dtf.cmd index dbc67c63..6b55ecfe 100644 --- a/src/dtf/dtf.cmd +++ b/src/dtf/dtf.cmd | |||
@@ -8,6 +8,8 @@ | |||
8 | 8 | ||
9 | @echo Building dtf %_C% | 9 | @echo Building dtf %_C% |
10 | 10 | ||
11 | msbuild -Restore SfxCA\sfxca_t.proj -p:Configuration=%_C% -nologo -m -warnaserror -bl:..\..\build\logs\dtf_sfxca.binlog || exit /b | ||
12 | |||
11 | msbuild -Restore -t:Pack dtf.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:..\..\build\logs\dtf_build.binlog || exit /b | 13 | msbuild -Restore -t:Pack dtf.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:..\..\build\logs\dtf_build.binlog || exit /b |
12 | 14 | ||
13 | @popd | 15 | @popd |
diff --git a/src/dtf/dtf.sln b/src/dtf/dtf.sln index fbd9452c..36592dcf 100644 --- a/src/dtf/dtf.sln +++ b/src/dtf/dtf.sln | |||
@@ -1,33 +1,39 @@ | |||
1 | | 1 | |
2 | Microsoft Visual Studio Solution File, Format Version 12.00 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 |
3 | # Visual Studio 15 | 3 | # Visual Studio Version 17 |
4 | VisualStudioVersion = 15.0.26730.8 | 4 | VisualStudioVersion = 17.1.32228.430 |
5 | MinimumVisualStudioVersion = 15.0.26124.0 | 5 | MinimumVisualStudioVersion = 15.0.26124.0 |
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Compression", "WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj", "{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}" | 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression", "WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj", "{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}" |
7 | EndProject | 7 | EndProject |
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Compression.Cab", "WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj", "{15895FD1-DD68-407B-8717-08F6DD14F02C}" | 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression.Cab", "WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj", "{15895FD1-DD68-407B-8717-08F6DD14F02C}" |
9 | EndProject | 9 | EndProject |
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Compression.Zip", "WixToolset.Dtf.Compression.Zip\WixToolset.Dtf.Compression.Zip.csproj", "{261F2857-B521-42A4-A3E0-B5165F225E50}" | 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression.Zip", "WixToolset.Dtf.Compression.Zip\WixToolset.Dtf.Compression.Zip.csproj", "{261F2857-B521-42A4-A3E0-B5165F225E50}" |
11 | EndProject | 11 | EndProject |
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Resources", "WixToolset.Dtf.Resources\WixToolset.Dtf.Resources.csproj", "{44931ECB-8D6F-4C12-A872-64E261B6A98E}" | 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Resources", "WixToolset.Dtf.Resources\WixToolset.Dtf.Resources.csproj", "{44931ECB-8D6F-4C12-A872-64E261B6A98E}" |
13 | EndProject | 13 | EndProject |
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller", "WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj", "{24121677-0ED0-41B5-833F-1B9A18E87BF4}" | 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller", "WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj", "{24121677-0ED0-41B5-833F-1B9A18E87BF4}" |
15 | EndProject | 15 | EndProject |
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller.Linq", "WixToolset.Dtf.WindowsInstaller.Linq\WixToolset.Dtf.WindowsInstaller.Linq.csproj", "{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}" | 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller.Linq", "WixToolset.Dtf.WindowsInstaller.Linq\WixToolset.Dtf.WindowsInstaller.Linq.csproj", "{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}" |
17 | EndProject | 17 | EndProject |
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller.Package", "WixToolset.Dtf.WindowsInstaller.Package\WixToolset.Dtf.WindowsInstaller.Package.csproj", "{1A9940A7-3E29-4428-B753-C4CC66058F1A}" | 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller.Package", "WixToolset.Dtf.WindowsInstaller.Package\WixToolset.Dtf.WindowsInstaller.Package.csproj", "{1A9940A7-3E29-4428-B753-C4CC66058F1A}" |
19 | EndProject | 19 | EndProject |
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression", "WixToolsetTests.Dtf.Compression\WixToolsetTests.Dtf.Compression.csproj", "{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}" | 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression", "test\WixToolsetTests.Dtf.Compression\WixToolsetTests.Dtf.Compression.csproj", "{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}" |
21 | EndProject | 21 | EndProject |
22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Cab", "WixToolsetTests.Dtf.Compression.Cab\WixToolsetTests.Dtf.Compression.Cab.csproj", "{4544158C-2D63-4146-85FF-62169280144E}" | 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Cab", "test\WixToolsetTests.Dtf.Compression.Cab\WixToolsetTests.Dtf.Compression.Cab.csproj", "{4544158C-2D63-4146-85FF-62169280144E}" |
23 | EndProject | 23 | EndProject |
24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Zip", "WixToolsetTests.Dtf.Compression.Zip\WixToolsetTests.Dtf.Compression.Zip.csproj", "{328799BB-7B03-4B28-8180-4132211FD07D}" | 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Zip", "test\WixToolsetTests.Dtf.Compression.Zip\WixToolsetTests.Dtf.Compression.Zip.csproj", "{328799BB-7B03-4B28-8180-4132211FD07D}" |
25 | EndProject | 25 | EndProject |
26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller", "WixToolsetTests.Dtf.WindowsInstaller\WixToolsetTests.Dtf.WindowsInstaller.csproj", "{16F5202F-9276-4166-975C-C9654BAF8012}" | 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller", "test\WixToolsetTests.Dtf.WindowsInstaller\WixToolsetTests.Dtf.WindowsInstaller.csproj", "{16F5202F-9276-4166-975C-C9654BAF8012}" |
27 | EndProject | 27 | EndProject |
28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.CustomActions", "WixToolsetTests.Dtf.WindowsInstaller.CustomActions\WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj", "{137D376B-989F-4FEA-9A67-01D8D38CA0DE}" | 28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.CustomActions", "test\WixToolsetTests.Dtf.WindowsInstaller.CustomActions\WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj", "{137D376B-989F-4FEA-9A67-01D8D38CA0DE}" |
29 | EndProject | 29 | EndProject |
30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.Linq", "WixToolsetTests.Dtf.WindowsInstaller.Linq\WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj", "{4F55F9B8-D8B6-41EB-8796-221B4CD98324}" | 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.Linq", "test\WixToolsetTests.Dtf.WindowsInstaller.Linq\WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj", "{4F55F9B8-D8B6-41EB-8796-221B4CD98324}" |
31 | EndProject | ||
32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{222DA0A6-5E28-4D7A-A227-B818B0C55BAB}" | ||
33 | EndProject | ||
34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.MakeSfxCA", "WixToolset.Dtf.MakeSfxCA\WixToolset.Dtf.MakeSfxCA.csproj", "{F8CA8E72-08BF-4A8A-AD32-C638616B72E2}" | ||
35 | EndProject | ||
36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.CustomAction", "WixToolset.Dtf.CustomAction\WixToolset.Dtf.CustomAction.csproj", "{D6C0D94C-80A5-495C-B573-C7440A8594F5}" | ||
31 | EndProject | 37 | EndProject |
32 | Global | 38 | Global |
33 | GlobalSection(SolutionConfigurationPlatforms) = preSolution | 39 | GlobalSection(SolutionConfigurationPlatforms) = preSolution |
@@ -129,8 +135,8 @@ Global | |||
129 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x64.Build.0 = Debug|Any CPU | 135 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x64.Build.0 = Debug|Any CPU |
130 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x86.ActiveCfg = Debug|Any CPU | 136 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x86.ActiveCfg = Debug|Any CPU |
131 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x86.Build.0 = Debug|Any CPU | 137 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Debug|x86.Build.0 = Debug|Any CPU |
132 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Any CPU.ActiveCfg = Release|Any CPU | 138 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Any CPU.ActiveCfg = Debug|Any CPU |
133 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Any CPU.Build.0 = Release|Any CPU | 139 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|Any CPU.Build.0 = Debug|Any CPU |
134 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x64.ActiveCfg = Release|Any CPU | 140 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x64.ActiveCfg = Release|Any CPU |
135 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x64.Build.0 = Release|Any CPU | 141 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x64.Build.0 = Release|Any CPU |
136 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x86.ActiveCfg = Release|Any CPU | 142 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}.Release|x86.ActiveCfg = Release|Any CPU |
@@ -141,8 +147,8 @@ Global | |||
141 | {4544158C-2D63-4146-85FF-62169280144E}.Debug|x64.Build.0 = Debug|Any CPU | 147 | {4544158C-2D63-4146-85FF-62169280144E}.Debug|x64.Build.0 = Debug|Any CPU |
142 | {4544158C-2D63-4146-85FF-62169280144E}.Debug|x86.ActiveCfg = Debug|Any CPU | 148 | {4544158C-2D63-4146-85FF-62169280144E}.Debug|x86.ActiveCfg = Debug|Any CPU |
143 | {4544158C-2D63-4146-85FF-62169280144E}.Debug|x86.Build.0 = Debug|Any CPU | 149 | {4544158C-2D63-4146-85FF-62169280144E}.Debug|x86.Build.0 = Debug|Any CPU |
144 | {4544158C-2D63-4146-85FF-62169280144E}.Release|Any CPU.ActiveCfg = Release|Any CPU | 150 | {4544158C-2D63-4146-85FF-62169280144E}.Release|Any CPU.ActiveCfg = Debug|Any CPU |
145 | {4544158C-2D63-4146-85FF-62169280144E}.Release|Any CPU.Build.0 = Release|Any CPU | 151 | {4544158C-2D63-4146-85FF-62169280144E}.Release|Any CPU.Build.0 = Debug|Any CPU |
146 | {4544158C-2D63-4146-85FF-62169280144E}.Release|x64.ActiveCfg = Release|Any CPU | 152 | {4544158C-2D63-4146-85FF-62169280144E}.Release|x64.ActiveCfg = Release|Any CPU |
147 | {4544158C-2D63-4146-85FF-62169280144E}.Release|x64.Build.0 = Release|Any CPU | 153 | {4544158C-2D63-4146-85FF-62169280144E}.Release|x64.Build.0 = Release|Any CPU |
148 | {4544158C-2D63-4146-85FF-62169280144E}.Release|x86.ActiveCfg = Release|Any CPU | 154 | {4544158C-2D63-4146-85FF-62169280144E}.Release|x86.ActiveCfg = Release|Any CPU |
@@ -153,8 +159,8 @@ Global | |||
153 | {328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x64.Build.0 = Debug|Any CPU | 159 | {328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x64.Build.0 = Debug|Any CPU |
154 | {328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x86.ActiveCfg = Debug|Any CPU | 160 | {328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x86.ActiveCfg = Debug|Any CPU |
155 | {328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x86.Build.0 = Debug|Any CPU | 161 | {328799BB-7B03-4B28-8180-4132211FD07D}.Debug|x86.Build.0 = Debug|Any CPU |
156 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|Any CPU.ActiveCfg = Release|Any CPU | 162 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|Any CPU.ActiveCfg = Debug|Any CPU |
157 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|Any CPU.Build.0 = Release|Any CPU | 163 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|Any CPU.Build.0 = Debug|Any CPU |
158 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|x64.ActiveCfg = Release|Any CPU | 164 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|x64.ActiveCfg = Release|Any CPU |
159 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|x64.Build.0 = Release|Any CPU | 165 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|x64.Build.0 = Release|Any CPU |
160 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|x86.ActiveCfg = Release|Any CPU | 166 | {328799BB-7B03-4B28-8180-4132211FD07D}.Release|x86.ActiveCfg = Release|Any CPU |
@@ -165,8 +171,8 @@ Global | |||
165 | {16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x64.Build.0 = Debug|Any CPU | 171 | {16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x64.Build.0 = Debug|Any CPU |
166 | {16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x86.ActiveCfg = Debug|Any CPU | 172 | {16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x86.ActiveCfg = Debug|Any CPU |
167 | {16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x86.Build.0 = Debug|Any CPU | 173 | {16F5202F-9276-4166-975C-C9654BAF8012}.Debug|x86.Build.0 = Debug|Any CPU |
168 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|Any CPU.ActiveCfg = Release|Any CPU | 174 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|Any CPU.ActiveCfg = Debug|Any CPU |
169 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|Any CPU.Build.0 = Release|Any CPU | 175 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|Any CPU.Build.0 = Debug|Any CPU |
170 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|x64.ActiveCfg = Release|Any CPU | 176 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|x64.ActiveCfg = Release|Any CPU |
171 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|x64.Build.0 = Release|Any CPU | 177 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|x64.Build.0 = Release|Any CPU |
172 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|x86.ActiveCfg = Release|Any CPU | 178 | {16F5202F-9276-4166-975C-C9654BAF8012}.Release|x86.ActiveCfg = Release|Any CPU |
@@ -177,8 +183,8 @@ Global | |||
177 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x64.Build.0 = Debug|Any CPU | 183 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x64.Build.0 = Debug|Any CPU |
178 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x86.ActiveCfg = Debug|Any CPU | 184 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x86.ActiveCfg = Debug|Any CPU |
179 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x86.Build.0 = Debug|Any CPU | 185 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Debug|x86.Build.0 = Debug|Any CPU |
180 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU | 186 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Any CPU.ActiveCfg = Debug|Any CPU |
181 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Any CPU.Build.0 = Release|Any CPU | 187 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|Any CPU.Build.0 = Debug|Any CPU |
182 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x64.ActiveCfg = Release|Any CPU | 188 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x64.ActiveCfg = Release|Any CPU |
183 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x64.Build.0 = Release|Any CPU | 189 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x64.Build.0 = Release|Any CPU |
184 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x86.ActiveCfg = Release|Any CPU | 190 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE}.Release|x86.ActiveCfg = Release|Any CPU |
@@ -189,29 +195,47 @@ Global | |||
189 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x64.Build.0 = Debug|Any CPU | 195 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x64.Build.0 = Debug|Any CPU |
190 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x86.ActiveCfg = Debug|Any CPU | 196 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x86.ActiveCfg = Debug|Any CPU |
191 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x86.Build.0 = Debug|Any CPU | 197 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Debug|x86.Build.0 = Debug|Any CPU |
192 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Any CPU.ActiveCfg = Release|Any CPU | 198 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Any CPU.ActiveCfg = Debug|Any CPU |
193 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Any CPU.Build.0 = Release|Any CPU | 199 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|Any CPU.Build.0 = Debug|Any CPU |
194 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x64.ActiveCfg = Release|Any CPU | 200 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x64.ActiveCfg = Release|Any CPU |
195 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x64.Build.0 = Release|Any CPU | 201 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x64.Build.0 = Release|Any CPU |
196 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x86.ActiveCfg = Release|Any CPU | 202 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x86.ActiveCfg = Release|Any CPU |
197 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x86.Build.0 = Release|Any CPU | 203 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324}.Release|x86.Build.0 = Release|Any CPU |
198 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | 204 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
199 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Debug|Any CPU.Build.0 = Debug|Any CPU | 205 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Debug|Any CPU.Build.0 = Debug|Any CPU |
200 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Debug|x64.ActiveCfg = Debug|Any CPU | 206 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Debug|x64.ActiveCfg = Debug|Any CPU |
201 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Debug|x64.Build.0 = Debug|Any CPU | 207 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Debug|x64.Build.0 = Debug|Any CPU |
202 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Debug|x86.ActiveCfg = Debug|Any CPU | 208 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Debug|x86.ActiveCfg = Debug|Any CPU |
203 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Debug|x86.Build.0 = Debug|Any CPU | 209 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Debug|x86.Build.0 = Debug|Any CPU |
204 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Release|Any CPU.ActiveCfg = Release|Any CPU | 210 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Release|Any CPU.ActiveCfg = Release|Any CPU |
205 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Release|Any CPU.Build.0 = Release|Any CPU | 211 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Release|Any CPU.Build.0 = Release|Any CPU |
206 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Release|x64.ActiveCfg = Release|Any CPU | 212 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Release|x64.ActiveCfg = Release|Any CPU |
207 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Release|x64.Build.0 = Release|Any CPU | 213 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Release|x64.Build.0 = Release|Any CPU |
208 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Release|x86.ActiveCfg = Release|Any CPU | 214 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Release|x86.ActiveCfg = Release|Any CPU |
209 | {E7A00377-A0B5-400F-8337-C0814AAC7153}.Release|x86.Build.0 = Release|Any CPU | 215 | {F8CA8E72-08BF-4A8A-AD32-C638616B72E2}.Release|x86.Build.0 = Release|Any CPU |
216 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
217 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
218 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
219 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Debug|x64.Build.0 = Debug|Any CPU | ||
220 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
221 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Debug|x86.Build.0 = Debug|Any CPU | ||
222 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
223 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Release|Any CPU.Build.0 = Release|Any CPU | ||
224 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Release|x64.ActiveCfg = Release|Any CPU | ||
225 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Release|x64.Build.0 = Release|Any CPU | ||
226 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Release|x86.ActiveCfg = Release|Any CPU | ||
227 | {D6C0D94C-80A5-495C-B573-C7440A8594F5}.Release|x86.Build.0 = Release|Any CPU | ||
210 | EndGlobalSection | 228 | EndGlobalSection |
211 | GlobalSection(SolutionProperties) = preSolution | 229 | GlobalSection(SolutionProperties) = preSolution |
212 | HideSolutionNode = FALSE | 230 | HideSolutionNode = FALSE |
213 | EndGlobalSection | 231 | EndGlobalSection |
214 | GlobalSection(NestedProjects) = preSolution | 232 | GlobalSection(NestedProjects) = preSolution |
233 | {F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9} = {222DA0A6-5E28-4D7A-A227-B818B0C55BAB} | ||
234 | {4544158C-2D63-4146-85FF-62169280144E} = {222DA0A6-5E28-4D7A-A227-B818B0C55BAB} | ||
235 | {328799BB-7B03-4B28-8180-4132211FD07D} = {222DA0A6-5E28-4D7A-A227-B818B0C55BAB} | ||
236 | {16F5202F-9276-4166-975C-C9654BAF8012} = {222DA0A6-5E28-4D7A-A227-B818B0C55BAB} | ||
237 | {137D376B-989F-4FEA-9A67-01D8D38CA0DE} = {222DA0A6-5E28-4D7A-A227-B818B0C55BAB} | ||
238 | {4F55F9B8-D8B6-41EB-8796-221B4CD98324} = {222DA0A6-5E28-4D7A-A227-B818B0C55BAB} | ||
215 | EndGlobalSection | 239 | EndGlobalSection |
216 | GlobalSection(ExtensibilityGlobals) = postSolution | 240 | GlobalSection(ExtensibilityGlobals) = postSolution |
217 | SolutionGuid = {BB57C98D-C0C2-4805-AED3-C19B47759DBD} | 241 | SolutionGuid = {BB57C98D-C0C2-4805-AED3-C19B47759DBD} |
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression.Cab/CabTest.cs b/src/dtf/test/WixToolsetTests.Dtf.Compression.Cab/CabTest.cs index 981ecc69..981ecc69 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression.Cab/CabTest.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression.Cab/CabTest.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression.Cab/WixToolsetTests.Dtf.Compression.Cab.csproj b/src/dtf/test/WixToolsetTests.Dtf.Compression.Cab/WixToolsetTests.Dtf.Compression.Cab.csproj index e751d405..636cedc6 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression.Cab/WixToolsetTests.Dtf.Compression.Cab.csproj +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression.Cab/WixToolsetTests.Dtf.Compression.Cab.csproj | |||
@@ -1,6 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Current"> |
4 | <PropertyGroup> | 4 | <PropertyGroup> |
5 | <ProjectGuid>{4544158C-2D63-4146-85FF-62169280144E}</ProjectGuid> | 5 | <ProjectGuid>{4544158C-2D63-4146-85FF-62169280144E}</ProjectGuid> |
6 | <OutputType>Library</OutputType> | 6 | <OutputType>Library</OutputType> |
@@ -24,11 +24,11 @@ | |||
24 | </ItemGroup> | 24 | </ItemGroup> |
25 | 25 | ||
26 | <ItemGroup> | 26 | <ItemGroup> |
27 | <ProjectReference Include="..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj"> | 27 | <ProjectReference Include="..\..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj"> |
28 | <Project>{45D81DAB-0559-4836-8106-CE9987FD4AB5}</Project> | 28 | <Project>{45D81DAB-0559-4836-8106-CE9987FD4AB5}</Project> |
29 | <Name>WixToolset.Dtf.Compression</Name> | 29 | <Name>WixToolset.Dtf.Compression</Name> |
30 | </ProjectReference> | 30 | </ProjectReference> |
31 | <ProjectReference Include="..\WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj"> | 31 | <ProjectReference Include="..\..\WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj"> |
32 | <Project>{E56C0ED3-FA2F-4CA9-A1C0-2E796BB0BF80}</Project> | 32 | <Project>{E56C0ED3-FA2F-4CA9-A1C0-2E796BB0BF80}</Project> |
33 | <Name>WixToolset.Dtf.Compression.Cab</Name> | 33 | <Name>WixToolset.Dtf.Compression.Cab</Name> |
34 | </ProjectReference> | 34 | </ProjectReference> |
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression.Zip/WixToolsetTests.Dtf.Compression.Zip.csproj b/src/dtf/test/WixToolsetTests.Dtf.Compression.Zip/WixToolsetTests.Dtf.Compression.Zip.csproj index 6ee102ae..d46776d8 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression.Zip/WixToolsetTests.Dtf.Compression.Zip.csproj +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression.Zip/WixToolsetTests.Dtf.Compression.Zip.csproj | |||
@@ -1,6 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Current"> |
4 | <PropertyGroup> | 4 | <PropertyGroup> |
5 | <ProjectGuid>{328799BB-7B03-4B28-8180-4132211FD07D}</ProjectGuid> | 5 | <ProjectGuid>{328799BB-7B03-4B28-8180-4132211FD07D}</ProjectGuid> |
6 | <OutputType>Library</OutputType> | 6 | <OutputType>Library</OutputType> |
@@ -22,11 +22,11 @@ | |||
22 | </ItemGroup> | 22 | </ItemGroup> |
23 | 23 | ||
24 | <ItemGroup> | 24 | <ItemGroup> |
25 | <ProjectReference Include="..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj"> | 25 | <ProjectReference Include="..\..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj"> |
26 | <Project>{45D81DAB-0559-4836-8106-CE9987FD4AB5}</Project> | 26 | <Project>{45D81DAB-0559-4836-8106-CE9987FD4AB5}</Project> |
27 | <Name>WixToolset.Dtf.Compression</Name> | 27 | <Name>WixToolset.Dtf.Compression</Name> |
28 | </ProjectReference> | 28 | </ProjectReference> |
29 | <ProjectReference Include="..\WixToolset.Dtf.Compression.Zip\WixToolset.Dtf.Compression.Zip.csproj"> | 29 | <ProjectReference Include="..\..\WixToolset.Dtf.Compression.Zip\WixToolset.Dtf.Compression.Zip.csproj"> |
30 | <Project>{E4C60A57-8AFE-4FF3-9058-ACAC6A069533}</Project> | 30 | <Project>{E4C60A57-8AFE-4FF3-9058-ACAC6A069533}</Project> |
31 | <Name>WixToolset.Dtf.Compression.Zip</Name> | 31 | <Name>WixToolset.Dtf.Compression.Zip</Name> |
32 | </ProjectReference> | 32 | </ProjectReference> |
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression.Zip/ZipTest.cs b/src/dtf/test/WixToolsetTests.Dtf.Compression.Zip/ZipTest.cs index b264ad5b..b264ad5b 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression.Zip/ZipTest.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression.Zip/ZipTest.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression/CompressionTestUtil.cs b/src/dtf/test/WixToolsetTests.Dtf.Compression/CompressionTestUtil.cs index e7a5373d..e7a5373d 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression/CompressionTestUtil.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression/CompressionTestUtil.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression/MisbehavingStreamContext.cs b/src/dtf/test/WixToolsetTests.Dtf.Compression/MisbehavingStreamContext.cs index 2531f3bc..2531f3bc 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression/MisbehavingStreamContext.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression/MisbehavingStreamContext.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression/OptionStreamContext.cs b/src/dtf/test/WixToolsetTests.Dtf.Compression/OptionStreamContext.cs index 98354d97..98354d97 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression/OptionStreamContext.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression/OptionStreamContext.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.Compression/WixToolsetTests.Dtf.Compression.csproj b/src/dtf/test/WixToolsetTests.Dtf.Compression/WixToolsetTests.Dtf.Compression.csproj index 194628a7..628d36c5 100644 --- a/src/dtf/WixToolsetTests.Dtf.Compression/WixToolsetTests.Dtf.Compression.csproj +++ b/src/dtf/test/WixToolsetTests.Dtf.Compression/WixToolsetTests.Dtf.Compression.csproj | |||
@@ -1,6 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Current"> |
4 | <PropertyGroup> | 4 | <PropertyGroup> |
5 | <ProjectGuid>{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}</ProjectGuid> | 5 | <ProjectGuid>{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}</ProjectGuid> |
6 | <OutputType>Library</OutputType> | 6 | <OutputType>Library</OutputType> |
@@ -25,7 +25,7 @@ | |||
25 | </ItemGroup> | 25 | </ItemGroup> |
26 | 26 | ||
27 | <ItemGroup> | 27 | <ItemGroup> |
28 | <ProjectReference Include="..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj"> | 28 | <ProjectReference Include="..\..\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj"> |
29 | <Project>{45D81DAB-0559-4836-8106-CE9987FD4AB5}</Project> | 29 | <Project>{45D81DAB-0559-4836-8106-CE9987FD4AB5}</Project> |
30 | <Name>WixToolset.Dtf.Compression</Name> | 30 | <Name>WixToolset.Dtf.Compression</Name> |
31 | </ProjectReference> | 31 | </ProjectReference> |
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/CustomActionTest.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/CustomActionTest.cs index bf843024..bf843024 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/CustomActionTest.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/CustomActionTest.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj index 27e0b499..a2f45fde 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj | |||
@@ -1,6 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Current"> |
4 | <PropertyGroup> | 4 | <PropertyGroup> |
5 | <ProjectGuid>{137D376B-989F-4FEA-9A67-01D8D38CA0DE}</ProjectGuid> | 5 | <ProjectGuid>{137D376B-989F-4FEA-9A67-01D8D38CA0DE}</ProjectGuid> |
6 | <OutputType>Library</OutputType> | 6 | <OutputType>Library</OutputType> |
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.Linq/LinqTest.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.Linq/LinqTest.cs index 7776a1c3..7776a1c3 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.Linq/LinqTest.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.Linq/LinqTest.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.Linq/WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.Linq/WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj index a59e64d4..c34494b7 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller.Linq/WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.Linq/WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj | |||
@@ -1,6 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Current"> |
4 | <PropertyGroup> | 4 | <PropertyGroup> |
5 | <ProjectGuid>{4F55F9B8-D8B6-41EB-8796-221B4CD98324}</ProjectGuid> | 5 | <ProjectGuid>{4F55F9B8-D8B6-41EB-8796-221B4CD98324}</ProjectGuid> |
6 | <OutputType>Library</OutputType> | 6 | <OutputType>Library</OutputType> |
@@ -23,11 +23,11 @@ | |||
23 | </ItemGroup> | 23 | </ItemGroup> |
24 | 24 | ||
25 | <ItemGroup> | 25 | <ItemGroup> |
26 | <ProjectReference Include="..\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj"> | 26 | <ProjectReference Include="..\..\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj"> |
27 | <Project>{85225597-5121-4361-8332-4E3246D5BBF5}</Project> | 27 | <Project>{85225597-5121-4361-8332-4E3246D5BBF5}</Project> |
28 | <Name>WixToolset.Dtf.WindowsInstaller</Name> | 28 | <Name>WixToolset.Dtf.WindowsInstaller</Name> |
29 | </ProjectReference> | 29 | </ProjectReference> |
30 | <ProjectReference Include="..\WixToolset.Dtf.WindowsInstaller.Linq\WixToolset.Dtf.WindowsInstaller.Linq.csproj"> | 30 | <ProjectReference Include="..\..\WixToolset.Dtf.WindowsInstaller.Linq\WixToolset.Dtf.WindowsInstaller.Linq.csproj"> |
31 | <Project>{7E66313B-C6D4-4729-8422-4D1474E0E6F7}</Project> | 31 | <Project>{7E66313B-C6D4-4729-8422-4D1474E0E6F7}</Project> |
32 | <Name>WixToolset.Dtf.WindowsInstaller.Linq</Name> | 32 | <Name>WixToolset.Dtf.WindowsInstaller.Linq</Name> |
33 | </ProjectReference> | 33 | </ProjectReference> |
@@ -39,4 +39,4 @@ | |||
39 | 39 | ||
40 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> | 40 | <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> |
41 | <Target Name="Pack" DependsOnTargets="Build" /> | 41 | <Target Name="Pack" DependsOnTargets="Build" /> |
42 | </Project> | 42 | </Project> \ No newline at end of file |
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/EmbeddedExternalUI.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/EmbeddedExternalUI.cs index b0fc00a8..b0fc00a8 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/EmbeddedExternalUI.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/EmbeddedExternalUI.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/Schema.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/Schema.cs index 26c172c9..26c172c9 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/Schema.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/Schema.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTest.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTest.cs index f994dfef..f994dfef 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTest.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTest.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTransactions.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTransactions.cs index 3bdf5acd..3bdf5acd 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTransactions.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTransactions.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerUtils.cs b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerUtils.cs index 644f1988..644f1988 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerUtils.cs +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerUtils.cs | |||
diff --git a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WixToolsetTests.Dtf.WindowsInstaller.csproj b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WixToolsetTests.Dtf.WindowsInstaller.csproj index 0d2a50fb..eaa273ed 100644 --- a/src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WixToolsetTests.Dtf.WindowsInstaller.csproj +++ b/src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WixToolsetTests.Dtf.WindowsInstaller.csproj | |||
@@ -1,6 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 3 | <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="Current"> |
4 | <PropertyGroup> | 4 | <PropertyGroup> |
5 | <ProjectGuid>{16F5202F-9276-4166-975C-C9654BAF8012}</ProjectGuid> | 5 | <ProjectGuid>{16F5202F-9276-4166-975C-C9654BAF8012}</ProjectGuid> |
6 | <OutputType>Library</OutputType> | 6 | <OutputType>Library</OutputType> |
@@ -28,7 +28,7 @@ | |||
28 | </ItemGroup> | 28 | </ItemGroup> |
29 | 29 | ||
30 | <ItemGroup> | 30 | <ItemGroup> |
31 | <ProjectReference Include="..\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj"> | 31 | <ProjectReference Include="..\..\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj"> |
32 | <Project>{85225597-5121-4361-8332-4E3246D5BBF5}</Project> | 32 | <Project>{85225597-5121-4361-8332-4E3246D5BBF5}</Project> |
33 | <Name>WixToolset.Dtf.WindowsInstaller</Name> | 33 | <Name>WixToolset.Dtf.WindowsInstaller</Name> |
34 | </ProjectReference> | 34 | </ProjectReference> |