diff options
Diffstat (limited to 'src/ca')
-rw-r--r-- | src/ca/dllmain.cpp | 27 | ||||
-rw-r--r-- | src/ca/precomp.h | 18 | ||||
-rw-r--r-- | src/ca/wixdepca.cpp | 516 | ||||
-rw-r--r-- | src/ca/wixdepca.def | 8 | ||||
-rw-r--r-- | src/ca/wixdepca.vcxproj | 57 | ||||
-rw-r--r-- | src/ca/wixdepca.vcxproj.filters | 40 |
6 files changed, 666 insertions, 0 deletions
diff --git a/src/ca/dllmain.cpp b/src/ca/dllmain.cpp new file mode 100644 index 00000000..7d299feb --- /dev/null +++ b/src/ca/dllmain.cpp | |||
@@ -0,0 +1,27 @@ | |||
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 | DllMain - standard entry point for all WiX custom actions. | ||
7 | |||
8 | ********************************************************************/ | ||
9 | extern "C" BOOL WINAPI DllMain( | ||
10 | IN HINSTANCE hInstance, | ||
11 | IN ULONG ulReason, | ||
12 | IN LPVOID) | ||
13 | { | ||
14 | switch(ulReason) | ||
15 | { | ||
16 | case DLL_PROCESS_ATTACH: | ||
17 | WcaGlobalInitialize(hInstance); | ||
18 | ::DisableThreadLibraryCalls(hInstance); | ||
19 | break; | ||
20 | |||
21 | case DLL_PROCESS_DETACH: | ||
22 | WcaGlobalFinalize(); | ||
23 | break; | ||
24 | } | ||
25 | |||
26 | return TRUE; | ||
27 | } | ||
diff --git a/src/ca/precomp.h b/src/ca/precomp.h new file mode 100644 index 00000000..5fd06cff --- /dev/null +++ b/src/ca/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 <intsafe.h> | ||
8 | #include <strsafe.h> | ||
9 | |||
10 | #include "wcautil.h" | ||
11 | #include "fileutil.h" | ||
12 | #include "strutil.h" | ||
13 | #include "memutil.h" | ||
14 | #include "regutil.h" | ||
15 | #include "dictutil.h" | ||
16 | #include "deputil.h" | ||
17 | |||
18 | #include "CustomMsiErrors.h" | ||
diff --git a/src/ca/wixdepca.cpp b/src/ca/wixdepca.cpp new file mode 100644 index 00000000..154b73f2 --- /dev/null +++ b/src/ca/wixdepca.cpp | |||
@@ -0,0 +1,516 @@ | |||
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 | #define IDNOACTION 0 | ||
6 | #define INITIAL_STRINGDICT_SIZE 4 | ||
7 | |||
8 | LPCWSTR vcsDependencyProviderQuery = | ||
9 | L"SELECT `WixDependencyProvider`.`WixDependencyProvider`, `WixDependencyProvider`.`Component_`, `WixDependencyProvider`.`ProviderKey`, `WixDependencyProvider`.`Attributes` " | ||
10 | L"FROM `WixDependencyProvider`"; | ||
11 | enum eDependencyProviderQuery { dpqId = 1, dpqComponent, dpqProviderKey, dpqAttributes }; | ||
12 | |||
13 | LPCWSTR vcsDependencyQuery = | ||
14 | L"SELECT `WixDependency`.`WixDependency`, `WixDependencyProvider`.`Component_`, `WixDependency`.`ProviderKey`, `WixDependency`.`MinVersion`, `WixDependency`.`MaxVersion`, `WixDependency`.`Attributes` " | ||
15 | L"FROM `WixDependencyProvider`, `WixDependency`, `WixDependencyRef` " | ||
16 | L"WHERE `WixDependency`.`WixDependency` = `WixDependencyRef`.`WixDependency_` AND `WixDependencyProvider`.`WixDependencyProvider` = `WixDependencyRef`.`WixDependencyProvider_`"; | ||
17 | enum eDependencyComponentQuery { dqId = 1, dqComponent, dqProviderKey, dqMinVersion, dqMaxVersion, dqAttributes }; | ||
18 | |||
19 | static HRESULT EnsureRequiredDependencies( | ||
20 | __in MSIHANDLE hInstall, | ||
21 | __in BOOL fMachineContext | ||
22 | ); | ||
23 | |||
24 | static HRESULT EnsureAbsentDependents( | ||
25 | __in MSIHANDLE hInstall, | ||
26 | __in BOOL fMachineContext | ||
27 | ); | ||
28 | |||
29 | static HRESULT SplitIgnoredDependents( | ||
30 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
31 | ); | ||
32 | |||
33 | static HRESULT CreateDependencyRecord( | ||
34 | __in int iMessageId, | ||
35 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
36 | __in UINT cDependencies, | ||
37 | __out MSIHANDLE *phRecord | ||
38 | ); | ||
39 | |||
40 | static LPCWSTR LogDependencyName( | ||
41 | __in_z LPCWSTR wzName | ||
42 | ); | ||
43 | |||
44 | /*************************************************************************** | ||
45 | WixDependencyRequire - Checks that all required dependencies are installed. | ||
46 | |||
47 | ***************************************************************************/ | ||
48 | extern "C" UINT __stdcall WixDependencyRequire( | ||
49 | __in MSIHANDLE hInstall | ||
50 | ) | ||
51 | { | ||
52 | HRESULT hr = S_OK; | ||
53 | UINT er = ERROR_SUCCESS; | ||
54 | BOOL fMachineContext = FALSE; | ||
55 | |||
56 | hr = WcaInitialize(hInstall, "WixDependencyRequire"); | ||
57 | ExitOnFailure(hr, "Failed to initialize."); | ||
58 | |||
59 | hr = RegInitialize(); | ||
60 | ExitOnFailure(hr, "Failed to initialize the registry functions."); | ||
61 | |||
62 | // Determine whether we're installing per-user or per-machine. | ||
63 | fMachineContext = WcaIsPropertySet("ALLUSERS"); | ||
64 | |||
65 | // Check for any provider components being (re)installed that their requirements are already installed. | ||
66 | hr = EnsureRequiredDependencies(hInstall, fMachineContext); | ||
67 | ExitOnFailure(hr, "Failed to ensure required dependencies for (re)installing components."); | ||
68 | |||
69 | LExit: | ||
70 | RegUninitialize(); | ||
71 | |||
72 | er = FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS; | ||
73 | return WcaFinalize(er); | ||
74 | } | ||
75 | |||
76 | /*************************************************************************** | ||
77 | WixDependencyCheck - Check dependencies based on component state. | ||
78 | |||
79 | Note: may return ERROR_NO_MORE_ITEMS to terminate the session early. | ||
80 | ***************************************************************************/ | ||
81 | extern "C" UINT __stdcall WixDependencyCheck( | ||
82 | __in MSIHANDLE hInstall | ||
83 | ) | ||
84 | { | ||
85 | HRESULT hr = S_OK; | ||
86 | UINT er = ERROR_SUCCESS; | ||
87 | BOOL fMachineContext = FALSE; | ||
88 | |||
89 | hr = WcaInitialize(hInstall, "WixDependencyCheck"); | ||
90 | ExitOnFailure(hr, "Failed to initialize."); | ||
91 | |||
92 | hr = RegInitialize(); | ||
93 | ExitOnFailure(hr, "Failed to initialize the registry functions."); | ||
94 | |||
95 | // Determine whether we're installing per-user or per-machine. | ||
96 | fMachineContext = WcaIsPropertySet("ALLUSERS"); | ||
97 | |||
98 | // Check for any dependents of provider components being uninstalled. | ||
99 | hr = EnsureAbsentDependents(hInstall, fMachineContext); | ||
100 | ExitOnFailure(hr, "Failed to ensure absent dependents for uninstalling components."); | ||
101 | |||
102 | LExit: | ||
103 | RegUninitialize(); | ||
104 | |||
105 | er = FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS; | ||
106 | return WcaFinalize(er); | ||
107 | } | ||
108 | |||
109 | /*************************************************************************** | ||
110 | EnsureRequiredDependencies - Check that dependencies are installed for | ||
111 | any provider component that is being installed or reinstalled. | ||
112 | |||
113 | Note: Skipped if DISABLEDEPENDENCYCHECK is set. | ||
114 | ***************************************************************************/ | ||
115 | static HRESULT EnsureRequiredDependencies( | ||
116 | __in MSIHANDLE /*hInstall*/, | ||
117 | __in BOOL fMachineContext | ||
118 | ) | ||
119 | { | ||
120 | HRESULT hr = S_OK; | ||
121 | DWORD er = ERROR_SUCCESS; | ||
122 | STRINGDICT_HANDLE sdDependencies = NULL; | ||
123 | HKEY hkHive = NULL; | ||
124 | PMSIHANDLE hView = NULL; | ||
125 | PMSIHANDLE hRec = NULL; | ||
126 | LPWSTR sczId = NULL; | ||
127 | LPWSTR sczComponent = NULL; | ||
128 | LPWSTR sczProviderKey = NULL; | ||
129 | LPWSTR sczMinVersion = NULL; | ||
130 | LPWSTR sczMaxVersion = NULL; | ||
131 | int iAttributes = 0; | ||
132 | WCA_TODO tComponentAction = WCA_TODO_UNKNOWN; | ||
133 | DEPENDENCY* rgDependencies = NULL; | ||
134 | UINT cDependencies = 0; | ||
135 | PMSIHANDLE hDependencyRec = NULL; | ||
136 | |||
137 | // Skip the dependency check if the WixDependency table is missing (no dependencies to check for). | ||
138 | hr = WcaTableExists(L"WixDependency"); | ||
139 | if (S_FALSE == hr) | ||
140 | { | ||
141 | WcaLog(LOGMSG_STANDARD, "Skipping the dependency check since no dependencies are authored."); | ||
142 | ExitFunction1(hr = S_OK); | ||
143 | } | ||
144 | |||
145 | // If the table exists but not the others, the database was not authored correctly. | ||
146 | ExitOnFailure(hr, "Failed to check if the WixDependency table exists."); | ||
147 | |||
148 | // Initialize the dictionary to keep track of unique dependency keys. | ||
149 | hr = DictCreateStringList(&sdDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
150 | ExitOnFailure(hr, "Failed to initialize the unique dependency string list."); | ||
151 | |||
152 | // Set the registry hive to use depending on install context. | ||
153 | hkHive = fMachineContext ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
154 | |||
155 | // Loop over the provider components. | ||
156 | hr = WcaOpenExecuteView(vcsDependencyQuery, &hView); | ||
157 | ExitOnFailure(hr, "Failed to open the query view for dependencies."); | ||
158 | |||
159 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
160 | { | ||
161 | hr = WcaGetRecordString(hRec, dqId, &sczId); | ||
162 | ExitOnFailure(hr, "Failed to get WixDependency.WixDependency."); | ||
163 | |||
164 | hr = WcaGetRecordString(hRec, dqComponent, &sczComponent); | ||
165 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.Component_."); | ||
166 | |||
167 | // Skip the current component if its not being installed or reinstalled. | ||
168 | tComponentAction = WcaGetComponentToDo(sczComponent); | ||
169 | if (WCA_TODO_INSTALL != tComponentAction && WCA_TODO_REINSTALL != tComponentAction) | ||
170 | { | ||
171 | WcaLog(LOGMSG_STANDARD, "Skipping dependency check for %ls because the component %ls is not being (re)installed.", sczId, sczComponent); | ||
172 | continue; | ||
173 | } | ||
174 | |||
175 | hr = WcaGetRecordString(hRec, dqProviderKey, &sczProviderKey); | ||
176 | ExitOnFailure(hr, "Failed to get WixDependency.ProviderKey."); | ||
177 | |||
178 | hr = WcaGetRecordString(hRec, dqMinVersion, &sczMinVersion); | ||
179 | ExitOnFailure(hr, "Failed to get WixDependency.MinVersion."); | ||
180 | |||
181 | hr = WcaGetRecordString(hRec, dqMaxVersion, &sczMaxVersion); | ||
182 | ExitOnFailure(hr, "Failed to get WixDependency.MaxVersion."); | ||
183 | |||
184 | hr = WcaGetRecordInteger(hRec, dqAttributes, &iAttributes); | ||
185 | ExitOnFailure(hr, "Failed to get WixDependency.Attributes."); | ||
186 | |||
187 | // Check the registry to see if the required providers (dependencies) exist. | ||
188 | hr = DepCheckDependency(hkHive, sczProviderKey, sczMinVersion, sczMaxVersion, iAttributes, sdDependencies, &rgDependencies, &cDependencies); | ||
189 | if (E_NOTFOUND != hr) | ||
190 | { | ||
191 | ExitOnFailure(hr, "Failed dependency check for %ls.", sczId); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (E_NOMOREITEMS != hr) | ||
196 | { | ||
197 | ExitOnFailure(hr, "Failed to enumerate all of the rows in the dependency query view."); | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | hr = S_OK; | ||
202 | } | ||
203 | |||
204 | // If we collected any dependencies in the previous check, pump a message and prompt the user. | ||
205 | if (0 < cDependencies) | ||
206 | { | ||
207 | hr = CreateDependencyRecord(msierrDependencyMissingDependencies, rgDependencies, cDependencies, &hDependencyRec); | ||
208 | ExitOnFailure(hr, "Failed to create the dependency record for message %d.", msierrDependencyMissingDependencies); | ||
209 | |||
210 | // Send a yes/no message with a warning icon since continuing could be detrimental. | ||
211 | // This is sent as a USER message to better detect whether a user or dependency-aware bootstrapper is responding | ||
212 | // or if Windows Installer or a dependency-unaware boostrapper is returning a typical default response. | ||
213 | er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2), hDependencyRec); | ||
214 | switch (er) | ||
215 | { | ||
216 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDYES to continue anyway. | ||
217 | case IDYES: | ||
218 | ExitFunction1(hr = S_OK); | ||
219 | |||
220 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDNO to terminate the operation. | ||
221 | case IDNO: | ||
222 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); | ||
223 | ExitFunction1(hr = S_OK); | ||
224 | |||
225 | // A dependency-aware bootstrapper should return IDCANCEL if running silently and the operation should be canceled. | ||
226 | case IDCANCEL: | ||
227 | __fallthrough; | ||
228 | |||
229 | // Bootstrappers which are not dependency-aware may return IDOK for unhandled messages. | ||
230 | case IDOK: | ||
231 | __fallthrough; | ||
232 | |||
233 | // Windows Installer returns 0 for USER messages when silent or passive, or when a bootstrapper does not handle the message. | ||
234 | case IDNOACTION: | ||
235 | WcaSetReturnValue(ERROR_INSTALL_FAILURE); | ||
236 | ExitFunction1(hr = S_OK); | ||
237 | |||
238 | default: | ||
239 | ExitOnFailure(hr = E_UNEXPECTED, "Unexpected message response %d from user or bootstrapper application.", er); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | LExit: | ||
244 | ReleaseDependencyArray(rgDependencies, cDependencies); | ||
245 | ReleaseStr(sczId); | ||
246 | ReleaseStr(sczComponent); | ||
247 | ReleaseStr(sczProviderKey); | ||
248 | ReleaseStr(sczMinVersion); | ||
249 | ReleaseStr(sczMaxVersion); | ||
250 | ReleaseDict(sdDependencies); | ||
251 | |||
252 | return hr; | ||
253 | } | ||
254 | |||
255 | /*************************************************************************** | ||
256 | EnsureAbsentDependents - Checks that there are no dependents | ||
257 | registered for providers that are being uninstalled. | ||
258 | |||
259 | Note: Skipped if UPGRADINGPRODUCTCODE is set. | ||
260 | ***************************************************************************/ | ||
261 | static HRESULT EnsureAbsentDependents( | ||
262 | __in MSIHANDLE /*hInstall*/, | ||
263 | __in BOOL fMachineContext | ||
264 | ) | ||
265 | { | ||
266 | HRESULT hr = S_OK; | ||
267 | DWORD er = ERROR_SUCCESS; | ||
268 | STRINGDICT_HANDLE sdIgnoredDependents = NULL; | ||
269 | HKEY hkHive = NULL; | ||
270 | PMSIHANDLE hView = NULL; | ||
271 | PMSIHANDLE hRec = NULL; | ||
272 | LPWSTR sczId = NULL; | ||
273 | LPWSTR sczComponent = NULL; | ||
274 | LPWSTR sczProviderKey = NULL; | ||
275 | int iAttributes = 0; | ||
276 | WCA_TODO tComponentAction = WCA_TODO_UNKNOWN; | ||
277 | DEPENDENCY* rgDependents = NULL; | ||
278 | UINT cDependents = 0; | ||
279 | PMSIHANDLE hDependencyRec = NULL; | ||
280 | |||
281 | // Split the IGNOREDEPENDENCIES property for use below if set. If it is "ALL", then quit now. | ||
282 | hr = SplitIgnoredDependents(&sdIgnoredDependents); | ||
283 | ExitOnFailure(hr, "Failed to get the ignored dependents."); | ||
284 | |||
285 | hr = DictKeyExists(sdIgnoredDependents, L"ALL"); | ||
286 | if (E_NOTFOUND != hr) | ||
287 | { | ||
288 | ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); | ||
289 | |||
290 | // Otherwise... | ||
291 | WcaLog(LOGMSG_STANDARD, "Skipping the dependencies check since IGNOREDEPENDENCIES contains \"ALL\"."); | ||
292 | ExitFunction(); | ||
293 | } | ||
294 | else | ||
295 | { | ||
296 | // Key was not found, so proceed. | ||
297 | hr = S_OK; | ||
298 | } | ||
299 | |||
300 | // Skip the dependent check if the WixDependencyProvider table is missing (no dependency providers). | ||
301 | hr = WcaTableExists(L"WixDependencyProvider"); | ||
302 | if (S_FALSE == hr) | ||
303 | { | ||
304 | WcaLog(LOGMSG_STANDARD, "Skipping the dependents check since no dependency providers are authored."); | ||
305 | ExitFunction(); | ||
306 | } | ||
307 | |||
308 | ExitOnFailure(hr, "Failed to check if the WixDependencyProvider table exists."); | ||
309 | |||
310 | // Set the registry hive to use depending on install context. | ||
311 | hkHive = fMachineContext ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
312 | |||
313 | // Loop over the provider components. | ||
314 | hr = WcaOpenExecuteView(vcsDependencyProviderQuery, &hView); | ||
315 | ExitOnFailure(hr, "Failed to open the query view for dependency providers."); | ||
316 | |||
317 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
318 | { | ||
319 | hr = WcaGetRecordString(hRec, dpqId, &sczId); | ||
320 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.WixDependencyProvider."); | ||
321 | |||
322 | hr = WcaGetRecordString(hRec, dpqComponent, &sczComponent); | ||
323 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.Component."); | ||
324 | |||
325 | // Skip the current component if its not being uninstalled. | ||
326 | tComponentAction = WcaGetComponentToDo(sczComponent); | ||
327 | if (WCA_TODO_UNINSTALL != tComponentAction) | ||
328 | { | ||
329 | WcaLog(LOGMSG_STANDARD, "Skipping dependents check for %ls because the component %ls is not being uninstalled.", sczId, sczComponent); | ||
330 | continue; | ||
331 | } | ||
332 | |||
333 | hr = WcaGetRecordString(hRec, dpqProviderKey, &sczProviderKey); | ||
334 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.ProviderKey."); | ||
335 | |||
336 | hr = WcaGetRecordInteger(hRec, dpqAttributes, &iAttributes); | ||
337 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.Attributes."); | ||
338 | |||
339 | // Check the registry to see if the provider has any dependents registered. | ||
340 | hr = DepCheckDependents(hkHive, sczProviderKey, iAttributes, sdIgnoredDependents, &rgDependents, &cDependents); | ||
341 | ExitOnFailure(hr, "Failed dependents check for %ls.", sczId); | ||
342 | } | ||
343 | |||
344 | if (E_NOMOREITEMS != hr) | ||
345 | { | ||
346 | ExitOnFailure(hr, "Failed to enumerate all of the rows in the dependency provider query view."); | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | hr = S_OK; | ||
351 | } | ||
352 | |||
353 | // If we collected any providers with dependents in the previous check, pump a message and prompt the user. | ||
354 | if (0 < cDependents) | ||
355 | { | ||
356 | hr = CreateDependencyRecord(msierrDependencyHasDependents, rgDependents, cDependents, &hDependencyRec); | ||
357 | ExitOnFailure(hr, "Failed to create the dependency record for message %d.", msierrDependencyHasDependents); | ||
358 | |||
359 | // Send a yes/no message with a warning icon since continuing could be detrimental. | ||
360 | // This is sent as a USER message to better detect whether a user or dependency-aware bootstrapper is responding | ||
361 | // or if Windows Installer or a dependency-unaware boostrapper is returning a typical default response. | ||
362 | er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2), hDependencyRec); | ||
363 | switch (er) | ||
364 | { | ||
365 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDYES to continue anyway. | ||
366 | case IDYES: | ||
367 | ExitFunction1(hr = S_OK); | ||
368 | |||
369 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDNO to terminate the operation. | ||
370 | case IDNO: | ||
371 | __fallthrough; | ||
372 | |||
373 | // Bootstrappers which are not dependency-aware may return IDOK for unhandled messages. | ||
374 | case IDOK: | ||
375 | __fallthrough; | ||
376 | |||
377 | // Windows Installer returns 0 for USER messages when silent or passive, or when a bootstrapper does not handle the message. | ||
378 | case IDNOACTION: | ||
379 | WcaSetReturnValue(ERROR_NO_MORE_ITEMS); | ||
380 | ExitFunction1(hr = S_OK); | ||
381 | |||
382 | // A dependency-aware bootstrapper should return IDCANCEL if running silently and the operation should be canceled. | ||
383 | case IDCANCEL: | ||
384 | WcaSetReturnValue(ERROR_INSTALL_FAILURE); | ||
385 | ExitFunction1(hr = S_OK); | ||
386 | |||
387 | default: | ||
388 | hr = E_UNEXPECTED; | ||
389 | ExitOnFailure(hr, "Unexpected message response %d from user or bootstrapper application.", er); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | LExit: | ||
394 | ReleaseDependencyArray(rgDependents, cDependents); | ||
395 | ReleaseStr(sczId); | ||
396 | ReleaseStr(sczComponent); | ||
397 | ReleaseStr(sczProviderKey); | ||
398 | |||
399 | return hr; | ||
400 | } | ||
401 | |||
402 | /*************************************************************************** | ||
403 | SplitIgnoredDependents - Splits the IGNOREDEPENDENCIES property into a map. | ||
404 | |||
405 | ***************************************************************************/ | ||
406 | static HRESULT SplitIgnoredDependents( | ||
407 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
408 | ) | ||
409 | { | ||
410 | HRESULT hr = S_OK; | ||
411 | LPWSTR sczIgnoreDependencies = NULL; | ||
412 | LPCWSTR wzDelim = L";"; | ||
413 | LPWSTR wzContext = NULL; | ||
414 | |||
415 | hr = WcaGetProperty(L"IGNOREDEPENDENCIES", &sczIgnoreDependencies); | ||
416 | ExitOnFailure(hr, "Failed to get the string value of the IGNOREDEPENDENCIES property."); | ||
417 | |||
418 | hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
419 | ExitOnFailure(hr, "Failed to create the string dictionary."); | ||
420 | |||
421 | // Parse through the semicolon-delimited tokens and add to the string dictionary. | ||
422 | for (LPCWSTR wzToken = ::wcstok_s(sczIgnoreDependencies, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) | ||
423 | { | ||
424 | hr = DictAddKey(*psdIgnoredDependents, wzToken); | ||
425 | ExitOnFailure(hr, "Failed to ignored dependency \"%ls\" to the string dictionary.", wzToken); | ||
426 | } | ||
427 | |||
428 | LExit: | ||
429 | ReleaseStr(sczIgnoreDependencies); | ||
430 | |||
431 | return hr; | ||
432 | } | ||
433 | |||
434 | /*************************************************************************** | ||
435 | CreateDependencyRecord - Creates a record containing the message template | ||
436 | and records to send to the UI handler. | ||
437 | |||
438 | Notes: Callers should call WcaProcessMessage and handle return codes. | ||
439 | ***************************************************************************/ | ||
440 | static HRESULT CreateDependencyRecord( | ||
441 | __in int iMessageId, | ||
442 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
443 | __in UINT cDependencies, | ||
444 | __out MSIHANDLE *phRecord | ||
445 | ) | ||
446 | { | ||
447 | HRESULT hr = S_OK; | ||
448 | UINT er = ERROR_SUCCESS; | ||
449 | UINT cParams = 0; | ||
450 | UINT iParam = 0; | ||
451 | |||
452 | // Should not be PMSIHANDLE. | ||
453 | MSIHANDLE hRec = NULL; | ||
454 | |||
455 | // Calculate the number of parameters based on the format: | ||
456 | // msgId, count, key1, name1, key2, name2, etc. | ||
457 | cParams = 2 + 2 * cDependencies; | ||
458 | |||
459 | hRec = ::MsiCreateRecord(cParams); | ||
460 | ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Not enough memory to create the message record."); | ||
461 | |||
462 | er = ::MsiRecordSetInteger(hRec, ++iParam, iMessageId); | ||
463 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the message identifier into the message record."); | ||
464 | |||
465 | er = ::MsiRecordSetInteger(hRec, ++iParam, cDependencies); | ||
466 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the number of dependencies into the message record."); | ||
467 | |||
468 | // Now loop through each dependency and add the key and name to the record. | ||
469 | for (UINT i = 0; i < cDependencies; i++) | ||
470 | { | ||
471 | const DEPENDENCY* pDependency = &rgDependencies[i]; | ||
472 | |||
473 | // Log message type-specific information. | ||
474 | switch (iMessageId) | ||
475 | { | ||
476 | // Send a user message when installing a component that is missing some dependencies. | ||
477 | case msierrDependencyMissingDependencies: | ||
478 | WcaLog(LOGMSG_VERBOSE, "The dependency \"%ls\" is missing or is not the required version.", pDependency->sczKey); | ||
479 | break; | ||
480 | |||
481 | // Send a user message when uninstalling a component that still has registered dependents. | ||
482 | case msierrDependencyHasDependents: | ||
483 | WcaLog(LOGMSG_VERBOSE, "Found dependent \"%ls\", name: \"%ls\".", pDependency->sczKey, LogDependencyName(pDependency->sczName)); | ||
484 | break; | ||
485 | } | ||
486 | |||
487 | er = ::MsiRecordSetStringW(hRec, ++iParam, pDependency->sczKey); | ||
488 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the dependency key \"%ls\" into the message record.", pDependency->sczKey); | ||
489 | |||
490 | er = ::MsiRecordSetStringW(hRec, ++iParam, pDependency->sczName); | ||
491 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the dependency name \"%ls\" into the message record.", pDependency->sczName); | ||
492 | } | ||
493 | |||
494 | // Only assign the out parameter if successful to this point. | ||
495 | *phRecord = hRec; | ||
496 | hRec = NULL; | ||
497 | |||
498 | LExit: | ||
499 | if (hRec) | ||
500 | { | ||
501 | ::MsiCloseHandle(hRec); | ||
502 | } | ||
503 | |||
504 | return hr; | ||
505 | } | ||
506 | |||
507 | /*************************************************************************** | ||
508 | LogDependencyName - Returns the dependency name or "Unknown" if null. | ||
509 | |||
510 | ***************************************************************************/ | ||
511 | static LPCWSTR LogDependencyName( | ||
512 | __in_z LPCWSTR wzName | ||
513 | ) | ||
514 | { | ||
515 | return wzName ? wzName : L"Unknown"; | ||
516 | } | ||
diff --git a/src/ca/wixdepca.def b/src/ca/wixdepca.def new file mode 100644 index 00000000..df50e992 --- /dev/null +++ b/src/ca/wixdepca.def | |||
@@ -0,0 +1,8 @@ | |||
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 "wixdepca" | ||
5 | |||
6 | EXPORTS | ||
7 | WixDependencyRequire | ||
8 | WixDependencyCheck | ||
diff --git a/src/ca/wixdepca.vcxproj b/src/ca/wixdepca.vcxproj new file mode 100644 index 00000000..b757a35f --- /dev/null +++ b/src/ca/wixdepca.vcxproj | |||
@@ -0,0 +1,57 @@ | |||
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 | |||
5 | <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
6 | <ItemGroup Label="ProjectConfigurations"> | ||
7 | <ProjectConfiguration Include="Debug|Win32"> | ||
8 | <Configuration>Debug</Configuration> | ||
9 | <Platform>Win32</Platform> | ||
10 | </ProjectConfiguration> | ||
11 | <ProjectConfiguration Include="Release|Win32"> | ||
12 | <Configuration>Release</Configuration> | ||
13 | <Platform>Win32</Platform> | ||
14 | </ProjectConfiguration> | ||
15 | </ItemGroup> | ||
16 | <ItemGroup Label="ProjectConfigurations"> | ||
17 | <ProjectConfiguration Include="Debug|ARM"> | ||
18 | <Configuration>Debug</Configuration> | ||
19 | <Platform>ARM</Platform> | ||
20 | </ProjectConfiguration> | ||
21 | <ProjectConfiguration Include="Release|ARM"> | ||
22 | <Configuration>Release</Configuration> | ||
23 | <Platform>ARM</Platform> | ||
24 | </ProjectConfiguration> | ||
25 | </ItemGroup> | ||
26 | |||
27 | <PropertyGroup Label="Globals"> | ||
28 | <ProjectGuid>{B86AF46C-0F90-49CC-923F-A800B088D015}</ProjectGuid> | ||
29 | <ConfigurationType>DynamicLibrary</ConfigurationType> | ||
30 | <CharacterSet>Unicode</CharacterSet> | ||
31 | <TargetName>WixDepCA</TargetName> | ||
32 | <ProjectModuleDefinitionFile>wixdepca.def</ProjectModuleDefinitionFile> | ||
33 | </PropertyGroup> | ||
34 | |||
35 | <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.props" /> | ||
36 | |||
37 | <PropertyGroup> | ||
38 | <ProjectAdditionalIncludeDirectories>$(WixRoot)src\libs\dutil\inc;$(WixRoot)src\libs\wcautil;$(WixRoot)src\libs\deputil\inc</ProjectAdditionalIncludeDirectories> | ||
39 | <ProjectAdditionalLinkLibraries>msi.lib;dutil.lib;deputil.lib;wcautil.lib</ProjectAdditionalLinkLibraries> | ||
40 | </PropertyGroup> | ||
41 | |||
42 | <ItemGroup> | ||
43 | <ClCompile Include="dllmain.cpp" /> | ||
44 | <ClCompile Include="wixdepca.cpp" /> | ||
45 | </ItemGroup> | ||
46 | <ItemGroup> | ||
47 | <ClInclude Include="precomp.h" /> | ||
48 | </ItemGroup> | ||
49 | <ItemGroup> | ||
50 | <None Include="wixdepca.def" /> | ||
51 | </ItemGroup> | ||
52 | <ItemGroup> | ||
53 | <ResourceCompile Include="wixdepca.rc" /> | ||
54 | </ItemGroup> | ||
55 | |||
56 | <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" /> | ||
57 | </Project> | ||
diff --git a/src/ca/wixdepca.vcxproj.filters b/src/ca/wixdepca.vcxproj.filters new file mode 100644 index 00000000..1fdb0236 --- /dev/null +++ b/src/ca/wixdepca.vcxproj.filters | |||
@@ -0,0 +1,40 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
3 | <ItemGroup> | ||
4 | <Filter Include="Source Files"> | ||
5 | <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> | ||
6 | <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> | ||
7 | </Filter> | ||
8 | <Filter Include="Header Files"> | ||
9 | <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> | ||
10 | <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> | ||
11 | </Filter> | ||
12 | <Filter Include="Resource Files"> | ||
13 | <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> | ||
14 | <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | ||
15 | </Filter> | ||
16 | </ItemGroup> | ||
17 | <ItemGroup> | ||
18 | <ClCompile Include="wixdepca.cpp"> | ||
19 | <Filter>Source Files</Filter> | ||
20 | </ClCompile> | ||
21 | <ClCompile Include="dllmain.cpp"> | ||
22 | <Filter>Source Files</Filter> | ||
23 | </ClCompile> | ||
24 | </ItemGroup> | ||
25 | <ItemGroup> | ||
26 | <ClInclude Include="precomp.h"> | ||
27 | <Filter>Header Files</Filter> | ||
28 | </ClInclude> | ||
29 | </ItemGroup> | ||
30 | <ItemGroup> | ||
31 | <ResourceCompile Include="wixdepca.rc"> | ||
32 | <Filter>Resource Files</Filter> | ||
33 | </ResourceCompile> | ||
34 | </ItemGroup> | ||
35 | <ItemGroup> | ||
36 | <None Include="wixdepca.def"> | ||
37 | <Filter>Source Files</Filter> | ||
38 | </None> | ||
39 | </ItemGroup> | ||
40 | </Project> \ No newline at end of file | ||