aboutsummaryrefslogtreecommitdiff
path: root/src/dtf
diff options
context:
space:
mode:
Diffstat (limited to 'src/dtf')
-rw-r--r--src/dtf/SfxCA/ClrHost.cpp262
-rw-r--r--src/dtf/SfxCA/EmbeddedUI.cpp281
-rw-r--r--src/dtf/SfxCA/EntryPoints.def140
-rw-r--r--src/dtf/SfxCA/EntryPoints.h162
-rw-r--r--src/dtf/SfxCA/Extract.cpp282
-rw-r--r--src/dtf/SfxCA/RemoteMsi.cpp629
-rw-r--r--src/dtf/SfxCA/RemoteMsiSession.h898
-rw-r--r--src/dtf/SfxCA/SfxCA.cpp363
-rw-r--r--src/dtf/SfxCA/SfxCA.rc10
-rw-r--r--src/dtf/SfxCA/SfxCA.vcxproj79
-rw-r--r--src/dtf/SfxCA/SfxCA.vcxproj.filters62
-rw-r--r--src/dtf/SfxCA/SfxUtil.cpp209
-rw-r--r--src/dtf/SfxCA/SfxUtil.h31
-rw-r--r--src/dtf/SfxCA/precomp.cpp3
-rw-r--r--src/dtf/SfxCA/precomp.h18
-rw-r--r--src/dtf/SfxCA/sfxca_t.proj7
-rw-r--r--src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.csproj17
-rw-r--r--src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.nuspec32
-rw-r--r--src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.targets (renamed from src/dtf/WixToolset.Dtf.MSBuild/tools/wix.ca.targets)18
-rw-r--r--src/dtf/WixToolset.Dtf.CustomAction/WixToolset.Dtf.CustomAction.v3.ncrunchproject5
-rw-r--r--src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.csproj40
-rw-r--r--src/dtf/WixToolset.Dtf.MSBuild/WixToolset.Dtf.MSBuild.nuspec18
-rw-r--r--src/dtf/WixToolset.Dtf.MSBuild/build/WixToolset.Dtf.MSBuild.props8
-rw-r--r--src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.cs710
-rw-r--r--src/dtf/WixToolset.Dtf.MakeSfxCA/MakeSfxCA.exe.manifest11
-rw-r--r--src/dtf/WixToolset.Dtf.MakeSfxCA/WixToolset.Dtf.MakeSfxCA.csproj19
-rw-r--r--src/dtf/WixToolset.Dtf.MakeSfxCA/app.config7
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs36
-rw-r--r--src/dtf/dtf.cmd2
-rw-r--r--src/dtf/dtf.sln102
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression.Cab/CabTest.cs (renamed from src/dtf/WixToolsetTests.Dtf.Compression.Cab/CabTest.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression.Cab/WixToolsetTests.Dtf.Compression.Cab.csproj (renamed from src/dtf/WixToolsetTests.Dtf.Compression.Cab/WixToolsetTests.Dtf.Compression.Cab.csproj)6
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression.Zip/WixToolsetTests.Dtf.Compression.Zip.csproj (renamed from src/dtf/WixToolsetTests.Dtf.Compression.Zip/WixToolsetTests.Dtf.Compression.Zip.csproj)6
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression.Zip/ZipTest.cs (renamed from src/dtf/WixToolsetTests.Dtf.Compression.Zip/ZipTest.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression/CompressionTestUtil.cs (renamed from src/dtf/WixToolsetTests.Dtf.Compression/CompressionTestUtil.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression/MisbehavingStreamContext.cs (renamed from src/dtf/WixToolsetTests.Dtf.Compression/MisbehavingStreamContext.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression/OptionStreamContext.cs (renamed from src/dtf/WixToolsetTests.Dtf.Compression/OptionStreamContext.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.Compression/WixToolsetTests.Dtf.Compression.csproj (renamed from src/dtf/WixToolsetTests.Dtf.Compression/WixToolsetTests.Dtf.Compression.csproj)4
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/CustomActionTest.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/CustomActionTest.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller.CustomActions/WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj)2
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.Linq/LinqTest.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller.Linq/LinqTest.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller.Linq/WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller.Linq/WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj)8
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/EmbeddedExternalUI.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller/EmbeddedExternalUI.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/Schema.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller/Schema.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTest.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTest.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTransactions.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerTransactions.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerUtils.cs (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WindowsInstallerUtils.cs)0
-rw-r--r--src/dtf/test/WixToolsetTests.Dtf.WindowsInstaller/WixToolsetTests.Dtf.WindowsInstaller.csproj (renamed from src/dtf/WixToolsetTests.Dtf.WindowsInstaller/WixToolsetTests.Dtf.WindowsInstaller.csproj)4
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
5void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...);
6
7//---------------------------------------------------------------------
8// CLR HOSTING
9//---------------------------------------------------------------------
10
11/// <summary>
12/// Binds to the CLR after determining the appropriate version.
13/// </summary>
14/// <param name="hSession">Handle to the installer session,
15/// used just for logging.</param>
16/// <param name="version">Specific version of the CLR to load.
17/// If null, then the config file and/or primary assembly are
18/// used to determine the version.</param>
19/// <param name="szConfigFile">XML .config file which may contain
20/// a startup section to direct which version of the CLR to use.
21/// May be NULL.</param>
22/// <param name="szPrimaryAssembly">Assembly to be used to determine
23/// the version of the CLR in the absence of other configuration.
24/// May be NULL.</param>
25/// <param name="ppHost">Returned runtime host interface.</param>
26/// <returns>True if the CLR was loaded successfully, false if
27/// there was some error.</returns>
28/// <remarks>
29/// If szPrimaryAssembly is NULL and szConfigFile is also NULL or
30/// does not contain any version configuration, the CLR will not be loaded.
31/// </remarks>
32bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile,
33 const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost)
34{
35 typedef HRESULT (__stdcall *PGetRequestedRuntimeInfo)(LPCWSTR pExe, LPCWSTR pwszVersion,
36 LPCWSTR pConfigurationFile, DWORD startupFlags, DWORD runtimeInfoFlags,
37 LPWSTR pDirectory, DWORD dwDirectory, DWORD *dwDirectoryLength,
38 LPWSTR pVersion, DWORD cchBuffer, DWORD* dwlength);
39 typedef HRESULT (__stdcall *PCorBindToRuntimeEx)(LPCWSTR pwszVersion, LPCWSTR pwszBuildFlavor,
40 DWORD startupFlags, REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
41
42 HMODULE hmodMscoree = LoadLibrary(L"mscoree.dll");
43 if (hmodMscoree == NULL)
44 {
45 Log(hSession, L"Failed to load mscoree.dll (Error code %d). This custom action "
46 L"requires the .NET Framework to be installed.", GetLastError());
47 return false;
48 }
49 PGetRequestedRuntimeInfo pGetRequestedRuntimeInfo = (PGetRequestedRuntimeInfo)
50 GetProcAddress(hmodMscoree, "GetRequestedRuntimeInfo");
51 PCorBindToRuntimeEx pCorBindToRuntimeEx = (PCorBindToRuntimeEx)
52 GetProcAddress(hmodMscoree, "CorBindToRuntimeEx");
53 if (pGetRequestedRuntimeInfo == NULL || pCorBindToRuntimeEx == NULL)
54 {
55 Log(hSession, L"Failed to locate functions in mscoree.dll (Error code %d). This custom action "
56 L"requires the .NET Framework to be installed.", GetLastError());
57 FreeLibrary(hmodMscoree);
58 return false;
59 }
60
61 wchar_t szClrVersion[20];
62 HRESULT hr;
63
64 if (szVersion != NULL && szVersion[0] != L'\0')
65 {
66 wcsncpy_s(szClrVersion, 20, szVersion, 20);
67 }
68 else
69 {
70 wchar_t szVersionDir[MAX_PATH];
71 hr = pGetRequestedRuntimeInfo(szPrimaryAssembly, NULL,
72 szConfigFile, 0, 0, szVersionDir, MAX_PATH, NULL, szClrVersion, 20, NULL);
73 if (FAILED(hr))
74 {
75 Log(hSession, L"Failed to get requested CLR info. Error code 0x%x", hr);
76 Log(hSession, L"Ensure that the proper version of the .NET Framework is installed, or "
77 L"that there is a matching supportedRuntime element in CustomAction.config. "
78 L"If you are binding to .NET 4 or greater add "
79 L"useLegacyV2RuntimeActivationPolicy=true to the <startup> element.");
80 FreeLibrary(hmodMscoree);
81 return false;
82 }
83 }
84
85 Log(hSession, L"Binding to CLR version %s", szClrVersion);
86
87 ICorRuntimeHost* pHost;
88 hr = pCorBindToRuntimeEx(szClrVersion, NULL,
89 STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN,
90 CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void**) &pHost);
91 if (FAILED(hr))
92 {
93 Log(hSession, L"Failed to bind to the CLR. Error code 0x%X", hr);
94 FreeLibrary(hmodMscoree);
95 return false;
96 }
97 hr = pHost->Start();
98 if (FAILED(hr))
99 {
100 Log(hSession, L"Failed to start the CLR. Error code 0x%X", hr);
101 pHost->Release();
102 FreeLibrary(hmodMscoree);
103 return false;
104 }
105 *ppHost = pHost;
106 FreeLibrary(hmodMscoree);
107 return true;
108}
109
110/// <summary>
111/// Creates a new CLR application domain.
112/// </summary>
113/// <param name="hSession">Handle to the installer session,
114/// used just for logging</param>
115/// <param name="pHost">Interface to the runtime host where the
116/// app domain will be created.</param>
117/// <param name="szName">Name of the app domain to create.</param>
118/// <param name="szAppBase">Application base directory path, where
119/// the app domain will look first to load its assemblies.</param>
120/// <param name="szConfigFile">Optional XML .config file containing any
121/// configuration for thae app domain.</param>
122/// <param name="ppAppDomain">Returned app domain interface.</param>
123/// <returns>True if the app domain was created successfully, false if
124/// there was some error.</returns>
125bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost,
126 const wchar_t* szName, const wchar_t* szAppBase,
127 const wchar_t* szConfigFile, _AppDomain** ppAppDomain)
128{
129 IUnknown* punkAppDomainSetup = NULL;
130 IAppDomainSetup* pAppDomainSetup = NULL;
131 HRESULT hr = pHost->CreateDomainSetup(&punkAppDomainSetup);
132 if (SUCCEEDED(hr))
133 {
134 hr = punkAppDomainSetup->QueryInterface(__uuidof(IAppDomainSetup), (void**) &pAppDomainSetup);
135 punkAppDomainSetup->Release();
136 }
137 if (FAILED(hr))
138 {
139 Log(hSession, L"Failed to create app domain setup. Error code 0x%X", hr);
140 return false;
141 }
142
143 const wchar_t* szUrlPrefix = L"file:///";
144 size_t cchApplicationBase = wcslen(szUrlPrefix) + wcslen(szAppBase);
145 wchar_t* szApplicationBase = (wchar_t*) _alloca((cchApplicationBase + 1) * sizeof(wchar_t));
146 if (szApplicationBase == NULL) hr = E_OUTOFMEMORY;
147 else
148 {
149 StringCchCopy(szApplicationBase, cchApplicationBase + 1, szUrlPrefix);
150 StringCchCat(szApplicationBase, cchApplicationBase + 1, szAppBase);
151 BSTR bstrApplicationBase = SysAllocString(szApplicationBase);
152 if (bstrApplicationBase == NULL) hr = E_OUTOFMEMORY;
153 else
154 {
155 hr = pAppDomainSetup->put_ApplicationBase(bstrApplicationBase);
156 SysFreeString(bstrApplicationBase);
157 }
158 }
159
160 if (SUCCEEDED(hr) && szConfigFile != NULL)
161 {
162 BSTR bstrConfigFile = SysAllocString(szConfigFile);
163 if (bstrConfigFile == NULL) hr = E_OUTOFMEMORY;
164 else
165 {
166 hr = pAppDomainSetup->put_ConfigurationFile(bstrConfigFile);
167 SysFreeString(bstrConfigFile);
168 }
169 }
170
171 if (FAILED(hr))
172 {
173 Log(hSession, L"Failed to configure app domain setup. Error code 0x%X", hr);
174 pAppDomainSetup->Release();
175 return false;
176 }
177
178 IUnknown* punkAppDomain;
179 hr = pHost->CreateDomainEx(szName, pAppDomainSetup, NULL, &punkAppDomain);
180 pAppDomainSetup->Release();
181 if (SUCCEEDED(hr))
182 {
183 hr = punkAppDomain->QueryInterface(__uuidof(_AppDomain), (void**) ppAppDomain);
184 punkAppDomain->Release();
185 }
186
187 if (FAILED(hr))
188 {
189 Log(hSession, L"Failed to create app domain. Error code 0x%X", hr);
190 return false;
191 }
192
193 return true;
194}
195
196/// <summary>
197/// Locates a specific method in a specific class and assembly.
198/// </summary>
199/// <param name="hSession">Handle to the installer session,
200/// used just for logging</param>
201/// <param name="pAppDomain">Application domain in which to
202/// load assemblies.</param>
203/// <param name="szAssembly">Display name of the assembly
204/// containing the method.</param>
205/// <param name="szClass">Fully-qualified name of the class
206/// containing the method.</param>
207/// <param name="szMethod">Name of the method.</param>
208/// <param name="ppMethod">Returned method interface.</param>
209/// <returns>True if the method was located, otherwise false.</returns>
210/// <remarks>Only public static methods are searched. Method
211/// parameter types are not considered; if there are multiple
212/// matching methods with different parameters, an error results.</remarks>
213bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain,
214 const wchar_t* szAssembly, const wchar_t* szClass,
215 const wchar_t* szMethod, _MethodInfo** ppMethod)
216{
217 HRESULT hr;
218 _Assembly* pAssembly = NULL;
219 BSTR bstrAssemblyName = SysAllocString(szAssembly);
220 if (bstrAssemblyName == NULL) hr = E_OUTOFMEMORY;
221 else
222 {
223 hr = pAppDomain->Load_2(bstrAssemblyName, &pAssembly);
224 SysFreeString(bstrAssemblyName);
225 }
226 if (FAILED(hr))
227 {
228 Log(hSession, L"Failed to load assembly %s. Error code 0x%X", szAssembly, hr);
229 return false;
230 }
231
232 _Type* pType = NULL;
233 BSTR bstrClass = SysAllocString(szClass);
234 if (bstrClass == NULL) hr = E_OUTOFMEMORY;
235 else
236 {
237 hr = pAssembly->GetType_2(bstrClass, &pType);
238 SysFreeString(bstrClass);
239 }
240 pAssembly->Release();
241 if (FAILED(hr) || pType == NULL)
242 {
243 Log(hSession, L"Failed to load class %s. Error code 0x%X", szClass, hr);
244 return false;
245 }
246
247 BSTR bstrMethod = SysAllocString(szMethod);
248 if (bstrMethod == NULL) hr = E_OUTOFMEMORY;
249 else
250 {
251 hr = pType->GetMethod_2(bstrMethod,
252 (BindingFlags) (BindingFlags_Public | BindingFlags_Static), ppMethod);
253 SysFreeString(bstrMethod);
254 }
255 pType->Release();
256 if (FAILED(hr) || *ppMethod == NULL)
257 {
258 Log(hSession, L"Failed to get method %s. Error code 0x%X", szMethod, hr);
259 return false;
260 }
261 return true;
262}
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.
7static const wchar_t* g_szWorkingDir;
8static ICorRuntimeHost* g_pClrHost;
9static _AppDomain* g_pAppDomain;
10static _MethodInfo* g_pProcessMessageMethod;
11static _MethodInfo* g_pShutdownMethod;
12
13// Reserve extra space for strings to be replaced at build time.
14#define NULLSPACE \
15L"\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" \
16L"\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" \
17L"\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" \
18L"\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
23bool 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>
37extern "C"
38UINT __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>
145extern "C"
146INT __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
176LExit:
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>
192extern "C"
193DWORD __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>
227bool 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
262LExit:
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
4LIBRARY "SfxCA"
5
6EXPORTS
7
8CustomActionEntryPoint000________________________________________________=CustomActionEntryPoint000
9CustomActionEntryPoint001________________________________________________=CustomActionEntryPoint001
10CustomActionEntryPoint002________________________________________________=CustomActionEntryPoint002
11CustomActionEntryPoint003________________________________________________=CustomActionEntryPoint003
12CustomActionEntryPoint004________________________________________________=CustomActionEntryPoint004
13CustomActionEntryPoint005________________________________________________=CustomActionEntryPoint005
14CustomActionEntryPoint006________________________________________________=CustomActionEntryPoint006
15CustomActionEntryPoint007________________________________________________=CustomActionEntryPoint007
16CustomActionEntryPoint008________________________________________________=CustomActionEntryPoint008
17CustomActionEntryPoint009________________________________________________=CustomActionEntryPoint009
18CustomActionEntryPoint010________________________________________________=CustomActionEntryPoint010
19CustomActionEntryPoint011________________________________________________=CustomActionEntryPoint011
20CustomActionEntryPoint012________________________________________________=CustomActionEntryPoint012
21CustomActionEntryPoint013________________________________________________=CustomActionEntryPoint013
22CustomActionEntryPoint014________________________________________________=CustomActionEntryPoint014
23CustomActionEntryPoint015________________________________________________=CustomActionEntryPoint015
24CustomActionEntryPoint016________________________________________________=CustomActionEntryPoint016
25CustomActionEntryPoint017________________________________________________=CustomActionEntryPoint017
26CustomActionEntryPoint018________________________________________________=CustomActionEntryPoint018
27CustomActionEntryPoint019________________________________________________=CustomActionEntryPoint019
28CustomActionEntryPoint020________________________________________________=CustomActionEntryPoint020
29CustomActionEntryPoint021________________________________________________=CustomActionEntryPoint021
30CustomActionEntryPoint022________________________________________________=CustomActionEntryPoint022
31CustomActionEntryPoint023________________________________________________=CustomActionEntryPoint023
32CustomActionEntryPoint024________________________________________________=CustomActionEntryPoint024
33CustomActionEntryPoint025________________________________________________=CustomActionEntryPoint025
34CustomActionEntryPoint026________________________________________________=CustomActionEntryPoint026
35CustomActionEntryPoint027________________________________________________=CustomActionEntryPoint027
36CustomActionEntryPoint028________________________________________________=CustomActionEntryPoint028
37CustomActionEntryPoint029________________________________________________=CustomActionEntryPoint029
38CustomActionEntryPoint030________________________________________________=CustomActionEntryPoint030
39CustomActionEntryPoint031________________________________________________=CustomActionEntryPoint031
40CustomActionEntryPoint032________________________________________________=CustomActionEntryPoint032
41CustomActionEntryPoint033________________________________________________=CustomActionEntryPoint033
42CustomActionEntryPoint034________________________________________________=CustomActionEntryPoint034
43CustomActionEntryPoint035________________________________________________=CustomActionEntryPoint035
44CustomActionEntryPoint036________________________________________________=CustomActionEntryPoint036
45CustomActionEntryPoint037________________________________________________=CustomActionEntryPoint037
46CustomActionEntryPoint038________________________________________________=CustomActionEntryPoint038
47CustomActionEntryPoint039________________________________________________=CustomActionEntryPoint039
48CustomActionEntryPoint040________________________________________________=CustomActionEntryPoint040
49CustomActionEntryPoint041________________________________________________=CustomActionEntryPoint041
50CustomActionEntryPoint042________________________________________________=CustomActionEntryPoint042
51CustomActionEntryPoint043________________________________________________=CustomActionEntryPoint043
52CustomActionEntryPoint044________________________________________________=CustomActionEntryPoint044
53CustomActionEntryPoint045________________________________________________=CustomActionEntryPoint045
54CustomActionEntryPoint046________________________________________________=CustomActionEntryPoint046
55CustomActionEntryPoint047________________________________________________=CustomActionEntryPoint047
56CustomActionEntryPoint048________________________________________________=CustomActionEntryPoint048
57CustomActionEntryPoint049________________________________________________=CustomActionEntryPoint049
58CustomActionEntryPoint050________________________________________________=CustomActionEntryPoint050
59CustomActionEntryPoint051________________________________________________=CustomActionEntryPoint051
60CustomActionEntryPoint052________________________________________________=CustomActionEntryPoint052
61CustomActionEntryPoint053________________________________________________=CustomActionEntryPoint053
62CustomActionEntryPoint054________________________________________________=CustomActionEntryPoint054
63CustomActionEntryPoint055________________________________________________=CustomActionEntryPoint055
64CustomActionEntryPoint056________________________________________________=CustomActionEntryPoint056
65CustomActionEntryPoint057________________________________________________=CustomActionEntryPoint057
66CustomActionEntryPoint058________________________________________________=CustomActionEntryPoint058
67CustomActionEntryPoint059________________________________________________=CustomActionEntryPoint059
68CustomActionEntryPoint060________________________________________________=CustomActionEntryPoint060
69CustomActionEntryPoint061________________________________________________=CustomActionEntryPoint061
70CustomActionEntryPoint062________________________________________________=CustomActionEntryPoint062
71CustomActionEntryPoint063________________________________________________=CustomActionEntryPoint063
72CustomActionEntryPoint064________________________________________________=CustomActionEntryPoint064
73CustomActionEntryPoint065________________________________________________=CustomActionEntryPoint065
74CustomActionEntryPoint066________________________________________________=CustomActionEntryPoint066
75CustomActionEntryPoint067________________________________________________=CustomActionEntryPoint067
76CustomActionEntryPoint068________________________________________________=CustomActionEntryPoint068
77CustomActionEntryPoint069________________________________________________=CustomActionEntryPoint069
78CustomActionEntryPoint070________________________________________________=CustomActionEntryPoint070
79CustomActionEntryPoint071________________________________________________=CustomActionEntryPoint071
80CustomActionEntryPoint072________________________________________________=CustomActionEntryPoint072
81CustomActionEntryPoint073________________________________________________=CustomActionEntryPoint073
82CustomActionEntryPoint074________________________________________________=CustomActionEntryPoint074
83CustomActionEntryPoint075________________________________________________=CustomActionEntryPoint075
84CustomActionEntryPoint076________________________________________________=CustomActionEntryPoint076
85CustomActionEntryPoint077________________________________________________=CustomActionEntryPoint077
86CustomActionEntryPoint078________________________________________________=CustomActionEntryPoint078
87CustomActionEntryPoint079________________________________________________=CustomActionEntryPoint079
88CustomActionEntryPoint080________________________________________________=CustomActionEntryPoint080
89CustomActionEntryPoint081________________________________________________=CustomActionEntryPoint081
90CustomActionEntryPoint082________________________________________________=CustomActionEntryPoint082
91CustomActionEntryPoint083________________________________________________=CustomActionEntryPoint083
92CustomActionEntryPoint084________________________________________________=CustomActionEntryPoint084
93CustomActionEntryPoint085________________________________________________=CustomActionEntryPoint085
94CustomActionEntryPoint086________________________________________________=CustomActionEntryPoint086
95CustomActionEntryPoint087________________________________________________=CustomActionEntryPoint087
96CustomActionEntryPoint088________________________________________________=CustomActionEntryPoint088
97CustomActionEntryPoint089________________________________________________=CustomActionEntryPoint089
98CustomActionEntryPoint090________________________________________________=CustomActionEntryPoint090
99CustomActionEntryPoint091________________________________________________=CustomActionEntryPoint091
100CustomActionEntryPoint092________________________________________________=CustomActionEntryPoint092
101CustomActionEntryPoint093________________________________________________=CustomActionEntryPoint093
102CustomActionEntryPoint094________________________________________________=CustomActionEntryPoint094
103CustomActionEntryPoint095________________________________________________=CustomActionEntryPoint095
104CustomActionEntryPoint096________________________________________________=CustomActionEntryPoint096
105CustomActionEntryPoint097________________________________________________=CustomActionEntryPoint097
106CustomActionEntryPoint098________________________________________________=CustomActionEntryPoint098
107CustomActionEntryPoint099________________________________________________=CustomActionEntryPoint099
108CustomActionEntryPoint100________________________________________________=CustomActionEntryPoint100
109CustomActionEntryPoint101________________________________________________=CustomActionEntryPoint101
110CustomActionEntryPoint102________________________________________________=CustomActionEntryPoint102
111CustomActionEntryPoint103________________________________________________=CustomActionEntryPoint103
112CustomActionEntryPoint104________________________________________________=CustomActionEntryPoint104
113CustomActionEntryPoint105________________________________________________=CustomActionEntryPoint105
114CustomActionEntryPoint106________________________________________________=CustomActionEntryPoint106
115CustomActionEntryPoint107________________________________________________=CustomActionEntryPoint107
116CustomActionEntryPoint108________________________________________________=CustomActionEntryPoint108
117CustomActionEntryPoint109________________________________________________=CustomActionEntryPoint109
118CustomActionEntryPoint110________________________________________________=CustomActionEntryPoint110
119CustomActionEntryPoint111________________________________________________=CustomActionEntryPoint111
120CustomActionEntryPoint112________________________________________________=CustomActionEntryPoint112
121CustomActionEntryPoint113________________________________________________=CustomActionEntryPoint113
122CustomActionEntryPoint114________________________________________________=CustomActionEntryPoint114
123CustomActionEntryPoint115________________________________________________=CustomActionEntryPoint115
124CustomActionEntryPoint116________________________________________________=CustomActionEntryPoint116
125CustomActionEntryPoint117________________________________________________=CustomActionEntryPoint117
126CustomActionEntryPoint118________________________________________________=CustomActionEntryPoint118
127CustomActionEntryPoint119________________________________________________=CustomActionEntryPoint119
128CustomActionEntryPoint120________________________________________________=CustomActionEntryPoint120
129CustomActionEntryPoint121________________________________________________=CustomActionEntryPoint121
130CustomActionEntryPoint122________________________________________________=CustomActionEntryPoint122
131CustomActionEntryPoint123________________________________________________=CustomActionEntryPoint123
132CustomActionEntryPoint124________________________________________________=CustomActionEntryPoint124
133CustomActionEntryPoint125________________________________________________=CustomActionEntryPoint125
134CustomActionEntryPoint126________________________________________________=CustomActionEntryPoint126
135CustomActionEntryPoint127________________________________________________=CustomActionEntryPoint127
136
137zzzzInvokeManagedCustomActionOutOfProcW=InvokeManagedCustomActionOutOfProc
138zzzInitializeEmbeddedUI=InitializeEmbeddedUI
139zzzEmbeddedUIHandler=EmbeddedUIHandler
140zzzShutdownEmbeddedUI=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
3int 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 \
24L"\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" \
25L"\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" \
26L"\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" \
27L"\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
33TEMPLATE_CA_ENTRYPOINT(000,L"000");
34TEMPLATE_CA_ENTRYPOINT(001,L"001");
35TEMPLATE_CA_ENTRYPOINT(002,L"002");
36TEMPLATE_CA_ENTRYPOINT(003,L"003");
37TEMPLATE_CA_ENTRYPOINT(004,L"004");
38TEMPLATE_CA_ENTRYPOINT(005,L"005");
39TEMPLATE_CA_ENTRYPOINT(006,L"006");
40TEMPLATE_CA_ENTRYPOINT(007,L"007");
41TEMPLATE_CA_ENTRYPOINT(008,L"008");
42TEMPLATE_CA_ENTRYPOINT(009,L"009");
43TEMPLATE_CA_ENTRYPOINT(010,L"010");
44TEMPLATE_CA_ENTRYPOINT(011,L"011");
45TEMPLATE_CA_ENTRYPOINT(012,L"012");
46TEMPLATE_CA_ENTRYPOINT(013,L"013");
47TEMPLATE_CA_ENTRYPOINT(014,L"014");
48TEMPLATE_CA_ENTRYPOINT(015,L"015");
49TEMPLATE_CA_ENTRYPOINT(016,L"016");
50TEMPLATE_CA_ENTRYPOINT(017,L"017");
51TEMPLATE_CA_ENTRYPOINT(018,L"018");
52TEMPLATE_CA_ENTRYPOINT(019,L"019");
53TEMPLATE_CA_ENTRYPOINT(020,L"020");
54TEMPLATE_CA_ENTRYPOINT(021,L"021");
55TEMPLATE_CA_ENTRYPOINT(022,L"022");
56TEMPLATE_CA_ENTRYPOINT(023,L"023");
57TEMPLATE_CA_ENTRYPOINT(024,L"024");
58TEMPLATE_CA_ENTRYPOINT(025,L"025");
59TEMPLATE_CA_ENTRYPOINT(026,L"026");
60TEMPLATE_CA_ENTRYPOINT(027,L"027");
61TEMPLATE_CA_ENTRYPOINT(028,L"028");
62TEMPLATE_CA_ENTRYPOINT(029,L"029");
63TEMPLATE_CA_ENTRYPOINT(030,L"030");
64TEMPLATE_CA_ENTRYPOINT(031,L"031");
65TEMPLATE_CA_ENTRYPOINT(032,L"032");
66TEMPLATE_CA_ENTRYPOINT(033,L"033");
67TEMPLATE_CA_ENTRYPOINT(034,L"034");
68TEMPLATE_CA_ENTRYPOINT(035,L"035");
69TEMPLATE_CA_ENTRYPOINT(036,L"036");
70TEMPLATE_CA_ENTRYPOINT(037,L"037");
71TEMPLATE_CA_ENTRYPOINT(038,L"038");
72TEMPLATE_CA_ENTRYPOINT(039,L"039");
73TEMPLATE_CA_ENTRYPOINT(040,L"040");
74TEMPLATE_CA_ENTRYPOINT(041,L"041");
75TEMPLATE_CA_ENTRYPOINT(042,L"042");
76TEMPLATE_CA_ENTRYPOINT(043,L"043");
77TEMPLATE_CA_ENTRYPOINT(044,L"044");
78TEMPLATE_CA_ENTRYPOINT(045,L"045");
79TEMPLATE_CA_ENTRYPOINT(046,L"046");
80TEMPLATE_CA_ENTRYPOINT(047,L"047");
81TEMPLATE_CA_ENTRYPOINT(048,L"048");
82TEMPLATE_CA_ENTRYPOINT(049,L"049");
83TEMPLATE_CA_ENTRYPOINT(050,L"050");
84TEMPLATE_CA_ENTRYPOINT(051,L"051");
85TEMPLATE_CA_ENTRYPOINT(052,L"052");
86TEMPLATE_CA_ENTRYPOINT(053,L"053");
87TEMPLATE_CA_ENTRYPOINT(054,L"054");
88TEMPLATE_CA_ENTRYPOINT(055,L"055");
89TEMPLATE_CA_ENTRYPOINT(056,L"056");
90TEMPLATE_CA_ENTRYPOINT(057,L"057");
91TEMPLATE_CA_ENTRYPOINT(058,L"058");
92TEMPLATE_CA_ENTRYPOINT(059,L"059");
93TEMPLATE_CA_ENTRYPOINT(060,L"060");
94TEMPLATE_CA_ENTRYPOINT(061,L"061");
95TEMPLATE_CA_ENTRYPOINT(062,L"062");
96TEMPLATE_CA_ENTRYPOINT(063,L"063");
97TEMPLATE_CA_ENTRYPOINT(064,L"064");
98TEMPLATE_CA_ENTRYPOINT(065,L"065");
99TEMPLATE_CA_ENTRYPOINT(066,L"066");
100TEMPLATE_CA_ENTRYPOINT(067,L"067");
101TEMPLATE_CA_ENTRYPOINT(068,L"068");
102TEMPLATE_CA_ENTRYPOINT(069,L"069");
103TEMPLATE_CA_ENTRYPOINT(070,L"070");
104TEMPLATE_CA_ENTRYPOINT(071,L"071");
105TEMPLATE_CA_ENTRYPOINT(072,L"072");
106TEMPLATE_CA_ENTRYPOINT(073,L"073");
107TEMPLATE_CA_ENTRYPOINT(074,L"074");
108TEMPLATE_CA_ENTRYPOINT(075,L"075");
109TEMPLATE_CA_ENTRYPOINT(076,L"076");
110TEMPLATE_CA_ENTRYPOINT(077,L"077");
111TEMPLATE_CA_ENTRYPOINT(078,L"078");
112TEMPLATE_CA_ENTRYPOINT(079,L"079");
113TEMPLATE_CA_ENTRYPOINT(080,L"080");
114TEMPLATE_CA_ENTRYPOINT(081,L"081");
115TEMPLATE_CA_ENTRYPOINT(082,L"082");
116TEMPLATE_CA_ENTRYPOINT(083,L"083");
117TEMPLATE_CA_ENTRYPOINT(084,L"084");
118TEMPLATE_CA_ENTRYPOINT(085,L"085");
119TEMPLATE_CA_ENTRYPOINT(086,L"086");
120TEMPLATE_CA_ENTRYPOINT(087,L"087");
121TEMPLATE_CA_ENTRYPOINT(088,L"088");
122TEMPLATE_CA_ENTRYPOINT(089,L"089");
123TEMPLATE_CA_ENTRYPOINT(090,L"090");
124TEMPLATE_CA_ENTRYPOINT(091,L"091");
125TEMPLATE_CA_ENTRYPOINT(092,L"092");
126TEMPLATE_CA_ENTRYPOINT(093,L"093");
127TEMPLATE_CA_ENTRYPOINT(094,L"094");
128TEMPLATE_CA_ENTRYPOINT(095,L"095");
129TEMPLATE_CA_ENTRYPOINT(096,L"096");
130TEMPLATE_CA_ENTRYPOINT(097,L"097");
131TEMPLATE_CA_ENTRYPOINT(098,L"098");
132TEMPLATE_CA_ENTRYPOINT(099,L"099");
133TEMPLATE_CA_ENTRYPOINT(100,L"100");
134TEMPLATE_CA_ENTRYPOINT(101,L"101");
135TEMPLATE_CA_ENTRYPOINT(102,L"102");
136TEMPLATE_CA_ENTRYPOINT(103,L"103");
137TEMPLATE_CA_ENTRYPOINT(104,L"104");
138TEMPLATE_CA_ENTRYPOINT(105,L"105");
139TEMPLATE_CA_ENTRYPOINT(106,L"106");
140TEMPLATE_CA_ENTRYPOINT(107,L"107");
141TEMPLATE_CA_ENTRYPOINT(108,L"108");
142TEMPLATE_CA_ENTRYPOINT(109,L"109");
143TEMPLATE_CA_ENTRYPOINT(110,L"110");
144TEMPLATE_CA_ENTRYPOINT(111,L"111");
145TEMPLATE_CA_ENTRYPOINT(112,L"112");
146TEMPLATE_CA_ENTRYPOINT(113,L"113");
147TEMPLATE_CA_ENTRYPOINT(114,L"114");
148TEMPLATE_CA_ENTRYPOINT(115,L"115");
149TEMPLATE_CA_ENTRYPOINT(116,L"116");
150TEMPLATE_CA_ENTRYPOINT(117,L"117");
151TEMPLATE_CA_ENTRYPOINT(118,L"118");
152TEMPLATE_CA_ENTRYPOINT(119,L"119");
153TEMPLATE_CA_ENTRYPOINT(120,L"120");
154TEMPLATE_CA_ENTRYPOINT(121,L"121");
155TEMPLATE_CA_ENTRYPOINT(122,L"122");
156TEMPLATE_CA_ENTRYPOINT(123,L"123");
157TEMPLATE_CA_ENTRYPOINT(124,L"124");
158TEMPLATE_CA_ENTRYPOINT(125,L"125");
159TEMPLATE_CA_ENTRYPOINT(126,L"126");
160TEMPLATE_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.
13static HFDI g_hfdi;
14
15// FDI is not unicode-aware, so avoid passing these paths through the callbacks.
16static const wchar_t* g_szExtractDir;
17static 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.
21static 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>
44static 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>
81static 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>
98static 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>
151static 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>
179static 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>
259int 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//
12static __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
35typedef int (WINAPI *PMsiFunc_I_I)(int in1, __out int* out1);
36typedef int (WINAPI *PMsiFunc_II_I)(int in1, int in2, __out int* out1);
37typedef int (WINAPI *PMsiFunc_IS_I)(int in1, __in_z wchar_t* in2, __out int* out1);
38typedef int (WINAPI *PMsiFunc_ISI_I)(int in1, __in_z wchar_t* in2, int in3, __out int* out1);
39typedef int (WINAPI *PMsiFunc_ISII_I)(int in1, __in_z wchar_t* in2, int in3, int in4, __out int* out1);
40typedef int (WINAPI *PMsiFunc_IS_II)(int in1, __in_z wchar_t* in2, __out int* out1, __out int* out2);
41typedef MSIDBERROR (WINAPI *PMsiEFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
42typedef int (WINAPI *PMsiFunc_I_S)(int in1, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
43typedef int (WINAPI *PMsiFunc_II_S)(int in1, int in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
44typedef int (WINAPI *PMsiFunc_IS_S)(int in1, __in_z wchar_t* in2, __out_ecount_full(*cchOut1) wchar_t* out1, __inout DWORD* cchOut1);
45typedef 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
47UINT 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
60UINT 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
74UINT 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
88UINT 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
103UINT 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
119UINT 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
135UINT 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
157MSIDBERROR 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
178UINT 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
201UINT 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
224UINT 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
254void 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//
12class RemoteMsiSession
13{
14public:
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
91public:
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
300private:
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
858private:
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
9HMODULE g_hModule;
10bool g_fRunningOutOfProc = false;
11
12RemoteMsiSession* g_pRemote = NULL;
13
14// Prototypes for local functions.
15// See the function definitions for comments.
16
17bool 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>
29extern "C"
30void __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>
69int 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>
152void __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 &quot;AssemblyName!Namespace.Class.Method&quot;
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>
182int 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>
253BOOL 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 &quot;AssemblyName!Namespace.Class.Method&quot;
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>
287bool 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
350LExit:
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>
10void 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>
81bool 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
114bool 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)
141bool 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
5void Log(MSIHANDLE hSession, const wchar_t* szMessage, ...);
6
7int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir);
8
9bool DeleteDirectory(const wchar_t* szDir);
10
11__success(return != false)
12bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule,
13 __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf);
14
15bool LoadCLR(MSIHANDLE hSession, const wchar_t* szVersion, const wchar_t* szConfigFile,
16 const wchar_t* szPrimaryAssembly, ICorRuntimeHost** ppHost);
17
18bool CreateAppDomain(MSIHANDLE hSession, ICorRuntimeHost* pHost,
19 const wchar_t* szName, const wchar_t* szAppBase,
20 const wchar_t* szConfigFile, _AppDomain** ppAppDomain);
21
22bool GetMethod(MSIHANDLE hSession, _AppDomain* pAppDomain,
23 const wchar_t* szAssembly, const wchar_t* szClass,
24 const wchar_t* szMethod, _MethodInfo** ppCAMethod);
25
26extern HMODULE g_hModule;
27extern bool g_fRunningOutOfProc;
28
29extern 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")
18using 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
3namespace 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
11msbuild -Restore SfxCA\sfxca_t.proj -p:Configuration=%_C% -nologo -m -warnaserror -bl:..\..\build\logs\dtf_sfxca.binlog || exit /b
12
11msbuild -Restore -t:Pack dtf.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:..\..\build\logs\dtf_build.binlog || exit /b 13msbuild -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
2Microsoft Visual Studio Solution File, Format Version 12.00 2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio 15 3# Visual Studio Version 17
4VisualStudioVersion = 15.0.26730.8 4VisualStudioVersion = 17.1.32228.430
5MinimumVisualStudioVersion = 15.0.26124.0 5MinimumVisualStudioVersion = 15.0.26124.0
6Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Compression", "WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj", "{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}" 6Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression", "WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj", "{2D62850C-9F81-4BE9-BDF3-9379389C8F7B}"
7EndProject 7EndProject
8Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Compression.Cab", "WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj", "{15895FD1-DD68-407B-8717-08F6DD14F02C}" 8Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression.Cab", "WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj", "{15895FD1-DD68-407B-8717-08F6DD14F02C}"
9EndProject 9EndProject
10Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Compression.Zip", "WixToolset.Dtf.Compression.Zip\WixToolset.Dtf.Compression.Zip.csproj", "{261F2857-B521-42A4-A3E0-B5165F225E50}" 10Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression.Zip", "WixToolset.Dtf.Compression.Zip\WixToolset.Dtf.Compression.Zip.csproj", "{261F2857-B521-42A4-A3E0-B5165F225E50}"
11EndProject 11EndProject
12Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.Resources", "WixToolset.Dtf.Resources\WixToolset.Dtf.Resources.csproj", "{44931ECB-8D6F-4C12-A872-64E261B6A98E}" 12Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Resources", "WixToolset.Dtf.Resources\WixToolset.Dtf.Resources.csproj", "{44931ECB-8D6F-4C12-A872-64E261B6A98E}"
13EndProject 13EndProject
14Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller", "WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj", "{24121677-0ED0-41B5-833F-1B9A18E87BF4}" 14Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller", "WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj", "{24121677-0ED0-41B5-833F-1B9A18E87BF4}"
15EndProject 15EndProject
16Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller.Linq", "WixToolset.Dtf.WindowsInstaller.Linq\WixToolset.Dtf.WindowsInstaller.Linq.csproj", "{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}" 16Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller.Linq", "WixToolset.Dtf.WindowsInstaller.Linq\WixToolset.Dtf.WindowsInstaller.Linq.csproj", "{CD7A37D8-9D8C-41BD-B78F-DB5E0C299D2E}"
17EndProject 17EndProject
18Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller.Package", "WixToolset.Dtf.WindowsInstaller.Package\WixToolset.Dtf.WindowsInstaller.Package.csproj", "{1A9940A7-3E29-4428-B753-C4CC66058F1A}" 18Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller.Package", "WixToolset.Dtf.WindowsInstaller.Package\WixToolset.Dtf.WindowsInstaller.Package.csproj", "{1A9940A7-3E29-4428-B753-C4CC66058F1A}"
19EndProject 19EndProject
20Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression", "WixToolsetTests.Dtf.Compression\WixToolsetTests.Dtf.Compression.csproj", "{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}" 20Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression", "test\WixToolsetTests.Dtf.Compression\WixToolsetTests.Dtf.Compression.csproj", "{F045FFC1-05F9-4EA2-9F03-E1CBDB7BC4F9}"
21EndProject 21EndProject
22Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Cab", "WixToolsetTests.Dtf.Compression.Cab\WixToolsetTests.Dtf.Compression.Cab.csproj", "{4544158C-2D63-4146-85FF-62169280144E}" 22Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Cab", "test\WixToolsetTests.Dtf.Compression.Cab\WixToolsetTests.Dtf.Compression.Cab.csproj", "{4544158C-2D63-4146-85FF-62169280144E}"
23EndProject 23EndProject
24Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Zip", "WixToolsetTests.Dtf.Compression.Zip\WixToolsetTests.Dtf.Compression.Zip.csproj", "{328799BB-7B03-4B28-8180-4132211FD07D}" 24Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.Compression.Zip", "test\WixToolsetTests.Dtf.Compression.Zip\WixToolsetTests.Dtf.Compression.Zip.csproj", "{328799BB-7B03-4B28-8180-4132211FD07D}"
25EndProject 25EndProject
26Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller", "WixToolsetTests.Dtf.WindowsInstaller\WixToolsetTests.Dtf.WindowsInstaller.csproj", "{16F5202F-9276-4166-975C-C9654BAF8012}" 26Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller", "test\WixToolsetTests.Dtf.WindowsInstaller\WixToolsetTests.Dtf.WindowsInstaller.csproj", "{16F5202F-9276-4166-975C-C9654BAF8012}"
27EndProject 27EndProject
28Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.CustomActions", "WixToolsetTests.Dtf.WindowsInstaller.CustomActions\WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj", "{137D376B-989F-4FEA-9A67-01D8D38CA0DE}" 28Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.CustomActions", "test\WixToolsetTests.Dtf.WindowsInstaller.CustomActions\WixToolsetTests.Dtf.WindowsInstaller.CustomActions.csproj", "{137D376B-989F-4FEA-9A67-01D8D38CA0DE}"
29EndProject 29EndProject
30Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.Linq", "WixToolsetTests.Dtf.WindowsInstaller.Linq\WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj", "{4F55F9B8-D8B6-41EB-8796-221B4CD98324}" 30Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolsetTests.Dtf.WindowsInstaller.Linq", "test\WixToolsetTests.Dtf.WindowsInstaller.Linq\WixToolsetTests.Dtf.WindowsInstaller.Linq.csproj", "{4F55F9B8-D8B6-41EB-8796-221B4CD98324}"
31EndProject
32Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{222DA0A6-5E28-4D7A-A227-B818B0C55BAB}"
33EndProject
34Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.MakeSfxCA", "WixToolset.Dtf.MakeSfxCA\WixToolset.Dtf.MakeSfxCA.csproj", "{F8CA8E72-08BF-4A8A-AD32-C638616B72E2}"
35EndProject
36Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.CustomAction", "WixToolset.Dtf.CustomAction\WixToolset.Dtf.CustomAction.csproj", "{D6C0D94C-80A5-495C-B573-C7440A8594F5}"
31EndProject 37EndProject
32Global 38Global
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>