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