· 5 years ago · Sep 28, 2020, 02:34 AM
1#include "msiregmv.h"
2
3const TCHAR szSecureSubKeyName[] = TEXT("Secure");
4
5////
6// feature-usage registration information
7const TCHAR szOldFeatureUsageKeyName[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Products");
8const TCHAR szOldFeatureUsageValueName[] = TEXT("Usage");
9
10////
11// component registration information
12const TCHAR szOldComponentKeyName[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Components");
13
14
15////
16// feature-component registration information
17const TCHAR szOldFeatureComponentKeyName[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\Features");
18
19
20bool g_fCommonUserIsSystemSID = false;
21
22/////////////////////////
23// Read component information from the Installer\Components key and places the Product,
24// Component, Path, and Permanent bit into the temporary table for later query.
25// returns ERROR_SUCCESS, ERROR_OUTOFMEMORY or ERROR_FUNCTION_FAILED
26DWORD ReadComponentRegistrationDataIntoDatabase(MSIHANDLE hDatabase)
27{
28 DEBUGMSG("Reading existing component registration data.");
29 DWORD dwResult = ERROR_SUCCESS;
30
31 // create the component table.
32 PMSIHANDLE hView;
33 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `Component` (`Product` CHAR(32), `Component` CHAR(32) NOT NULL, `Path` CHAR(0), `SecondaryPath` CHAR(0) PRIMARY KEY `Product`, `Component`, `Path`)"), &hView)) ||
34 ERROR_SUCCESS != (dwResult = MsiViewExecute(hView, 0)))
35 {
36 DEBUGMSG1("Error: Unable to create Component table. Error %d", dwResult);
37 return ERROR_FUNCTION_FAILED;
38 }
39
40 // create SharedDLL table
41 PMSIHANDLE hSharedDLLView;
42 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `SharedDLL` (`Path` CHAR(0) NOT NULL, `OldRefCount` INTEGER, `NewRefCount` INTEGER PRIMARY KEY `Path`)"), &hSharedDLLView)) ||
43 ERROR_SUCCESS != (dwResult = MsiViewExecute(hSharedDLLView, 0)))
44 {
45 DEBUGMSG1("Error: Unable to create SharedDLL table. Error %d", dwResult);
46 return ERROR_FUNCTION_FAILED;
47 }
48
49 // open the old per-machine Installer\Components key for read.
50 HKEY hComponentListKey;
51 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szOldComponentKeyName,
52 0, READ_CONTROL | KEY_ENUMERATE_SUB_KEYS,
53 &hComponentListKey)))
54 {
55 // if the reason that this failed is that the key doesn't exist, no products are installed. So return success
56 if (ERROR_FILE_NOT_FOUND != dwResult)
57 {
58 DEBUGMSG1("Error: Could not open old per-machine component key. Result: %d. ", dwResult);
59 return ERROR_FUNCTION_FAILED;
60 }
61 else
62 {
63 DEBUGMSG("No old per-machine component key. No components to migrate.");
64 return ERROR_SUCCESS;
65 }
66 }
67
68 ////
69 // check the ACL on the key to ensure that it is trustworthy.
70 if (!g_fWin9X && !FIsKeyLocalSystemOrAdminOwned(hComponentListKey))
71 {
72 DEBUGMSG("Warning: Skipping old per-machine component key, key is not owned by Admin or System.");
73 RegCloseKey(hComponentListKey);
74 return ERROR_FUNCTION_FAILED;
75 }
76
77 ////
78 // open query for insert into table
79 PMSIHANDLE hInsertView;
80 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("SELECT * FROM `Component`"), &hInsertView)) ||
81 ERROR_SUCCESS != (dwResult = MsiViewExecute(hInsertView, 0)))
82 {
83 DEBUGMSG1("Error: Unable to create insert query on Component table. Error %d", dwResult);
84 RegCloseKey(hComponentListKey);
85 return ERROR_FUNCTION_FAILED;
86 }
87
88 ////
89 // open query for insert into SharedDLL Table
90 PMSIHANDLE hRefCountInsertView;
91 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("SELECT * FROM `SharedDLL`"), &hRefCountInsertView)) ||
92 ERROR_SUCCESS != (dwResult = MsiViewExecute(hRefCountInsertView, 0)))
93 {
94 DEBUGMSG1("Error: Unable to create insert query on SharedDLL table. Error %d", dwResult);
95 RegCloseKey(hComponentListKey);
96 return ERROR_FUNCTION_FAILED;
97 }
98
99 ////
100 // open query for update of SharedDLL Table
101 PMSIHANDLE hRefCountUpdateView;
102 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("SELECT `OldRefCount` FROM `SharedDLL` WHERE `Path`=?"), &hRefCountUpdateView)))
103 {
104 DEBUGMSG1("Error: Unable to create update query on SharedDLL table. Error %d", dwResult);
105 RegCloseKey(hComponentListKey);
106 return ERROR_FUNCTION_FAILED;
107 }
108
109 DWORD dwIndex=0;
110 PMSIHANDLE hInsertRec = ::MsiCreateRecord(5);
111 while (1)
112 {
113 TCHAR rgchComponent[cchGUIDPacked+1];
114 DWORD cchComponent = cchGUIDPacked+1;
115
116 ////
117 // retrieve the next component subkey name
118 LONG lResult = RegEnumKeyEx(hComponentListKey, dwIndex++, rgchComponent,
119 &cchComponent, 0, NULL, NULL, NULL);
120 if (lResult == ERROR_MORE_DATA)
121 {
122 // key is not a valid GUID, skip to the next component key
123 DEBUGMSG("Warning: Detected too-long component key. Skipping.");
124 continue;
125 }
126 else if (lResult == ERROR_NO_MORE_ITEMS)
127 {
128 break;
129 }
130 else if (lResult != ERROR_SUCCESS)
131 {
132 DEBUGMSG1("Error: RegEnumKeyEx on per-machine component key returned %l.", lResult);
133 RegCloseKey(hComponentListKey);
134 return ERROR_FUNCTION_FAILED;
135 }
136
137 ////
138 // check if the component is a valid guid
139 if ((cchComponent != cchGUIDPacked) || !CanonicalizeAndVerifyPackedGuid(rgchComponent))
140 {
141 // key is not a valid GUID, skip to the next key
142 DEBUGMSG1("Warning: Detected invalid component key: %s. Skipping.", rgchComponent);
143 continue;
144 }
145
146 // store the component code in the record for later insertion
147 MsiRecordSetString(hInsertRec, 2, rgchComponent);
148
149 // open the subkey
150 HKEY hComponentKey;
151 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(hComponentListKey, rgchComponent,
152 0, KEY_QUERY_VALUE, &hComponentKey)))
153 {
154 DEBUGMSG2("Error: Could not open old per-machine component key for %s. Result: %d. Skipping component.", rgchComponent, dwResult);
155 return ERROR_FUNCTION_FAILED;
156 }
157
158 DWORD cchMaxValueLen = 0;
159 DWORD cValues = 0;
160 if (ERROR_SUCCESS != (dwResult = RegQueryInfoKey(hComponentKey, NULL, NULL, 0,
161 NULL, NULL, NULL, &cValues, NULL,
162 &cchMaxValueLen, NULL, NULL)))
163 {
164 DEBUGMSG2("Error: Could not retrieve key information for old per-machine component key %s. Result: %d. Skipping component.", rgchComponent, dwResult);
165 RegCloseKey(hComponentKey);
166 RegCloseKey(hComponentListKey);
167 return ERROR_FUNCTION_FAILED;
168 }
169
170 // if no products, skip
171 if (cValues == 0)
172 {
173 RegCloseKey(hComponentKey);
174 continue;
175 }
176
177 // allocate memory to grab the path from the registry
178 TCHAR *szValue = new TCHAR[cchMaxValueLen];
179 if (!szValue)
180 {
181 DEBUGMSG("Error: out of memory.");
182 RegCloseKey(hComponentListKey);
183 RegCloseKey(hComponentKey);
184 return ERROR_OUTOFMEMORY;
185 }
186 TCHAR rgchProduct[cchGUIDPacked+1];
187 DWORD cchProduct = cchGUIDPacked+1;
188
189 DWORD dwValueIndex = 0;
190 while (1)
191 {
192 cchProduct = cchGUIDPacked+1;
193 DWORD cbValue = cchMaxValueLen*sizeof(TCHAR);
194 DWORD dwType = 0;
195 LONG lResult = RegEnumValue(hComponentKey, dwValueIndex++, rgchProduct,
196 &cchProduct, 0, &dwType, reinterpret_cast<unsigned char*>(szValue), &cbValue);
197 if (lResult == ERROR_MORE_DATA)
198 {
199 // value is not a valid ProductId, skip to the next ProductId
200 DEBUGMSG1("Warning: Detected too-long product value for component %s. Skipping.", rgchComponent);
201 continue;
202 }
203 else if (lResult == ERROR_NO_MORE_ITEMS)
204 {
205 break;
206 }
207 else if (lResult != ERROR_SUCCESS)
208 {
209 DEBUGMSG2("Error: Could not enumerate products for old per-machine component %s. Result: %d.", rgchComponent, dwResult);
210 RegCloseKey(hComponentKey);
211 RegCloseKey(hComponentListKey);
212 delete[] szValue;
213 return ERROR_FUNCTION_FAILED;
214 }
215
216 // if not reg-sz or reg-multi-sz, not a valid path registration
217 if (dwType != REG_SZ && dwType != REG_MULTI_SZ)
218 {
219 DEBUGMSG1("Warning: Non-string registry value for component %s. Skipping.", rgchComponent);
220 continue;
221 }
222
223 ////
224 // check if the product is a valid guid
225 if ((cchProduct != cchGUIDPacked) || !CanonicalizeAndVerifyPackedGuid(rgchProduct))
226 {
227 // key is not a valid GUID, skip it
228 DEBUGMSG2("Warning: Invalid product value %s for component %s. Skipping.", rgchProduct, rgchComponent);
229 continue;
230 }
231
232 TCHAR *szSecondaryKeyPath = NULL;
233 if (dwType == REG_MULTI_SZ)
234 {
235 // for MULTI_SZ the secondary keypath begins one NULL after the end
236 // of the primary keypath
237 szSecondaryKeyPath = szValue + lstrlen(szValue)+1;
238 }
239
240 ////
241 // check for sharedDLL information. If the paths differ in case, it
242 // doesn't matter too much because the update algorithm can handle
243 // updating the same key twice with two different deltas. Must do
244 // this before trashing szValue for dummy permanent product IDs.
245
246 // Future: if ANSI builds, can we have DBCS drive letters?
247 if (szValue[0] != '\0' && szValue[1] == TEXT('?'))
248 {
249 PMSIHANDLE hSharedDLLRec = MsiCreateRecord(3);
250 MsiRecordSetString(hSharedDLLRec, 1, szValue);
251 MsiRecordSetInteger(hSharedDLLRec, 2, 1);
252 MsiRecordSetInteger(hSharedDLLRec, 3, 0);
253
254 if (ERROR_SUCCESS != MsiViewModify(hRefCountInsertView, MSIMODIFY_INSERT, hSharedDLLRec))
255 {
256 // record might already exist
257 if (ERROR_SUCCESS != (dwResult = MsiViewExecute(hRefCountUpdateView, hSharedDLLRec)) ||
258 ERROR_SUCCESS != (dwResult = MsiViewFetch(hRefCountUpdateView, &hSharedDLLRec)))
259 {
260 DEBUGMSG3("Error: Unable to insert SharedDLL data for component %s, product %s into SharedDLL table. Error %d", rgchComponent, rgchProduct, dwResult);
261 delete[] szValue;
262 RegCloseKey(hComponentKey);
263 RegCloseKey(hComponentListKey);
264 return ERROR_FUNCTION_FAILED;
265 }
266
267 // increment the existing old SharedDLL cont for this path
268 MsiRecordSetInteger(hSharedDLLRec, 1, MsiRecordGetInteger(hSharedDLLRec, 1)+1);
269 if (ERROR_SUCCESS != (dwResult = MsiViewModify(hRefCountUpdateView, MSIMODIFY_UPDATE, hSharedDLLRec)))
270 {
271 DEBUGMSG3("Error: Unable to insert SharedDLL data for component %s, product %s into SharedDLL table. Error %d", rgchComponent, rgchProduct, dwResult);
272 delete[] szValue;
273 RegCloseKey(hComponentKey);
274 RegCloseKey(hComponentListKey);
275 return ERROR_FUNCTION_FAILED;
276 }
277 }
278 }
279
280 // if the productId is actually a GUID <= 255, its a dummy product
281 // for permanent components. the actual GUID is uninteresting and is set to NULL
282 if (CSTR_EQUAL == CompareString(LOCALE_SYSTEM_DEFAULT, 0, rgchProduct, 30, TEXT("000000000000000000000000000000"), 30))
283 {
284 rgchProduct[0] = TEXT('\0');
285 }
286
287 MsiRecordSetString(hInsertRec, 1, rgchProduct);
288 MsiRecordSetString(hInsertRec, 3, szValue);
289 MsiRecordSetString(hInsertRec, 4, szSecondaryKeyPath);
290 MsiRecordSetString(hInsertRec, 5, 0);
291
292 if (ERROR_SUCCESS != (dwResult != MsiViewModify(hInsertView, MSIMODIFY_INSERT, hInsertRec)))
293 {
294 DEBUGMSG3("Error: Unable to insert data for component %s, product %s into Component table. Error %d", rgchComponent, rgchProduct, dwResult);
295 delete[] szValue;
296 RegCloseKey(hComponentKey);
297 RegCloseKey(hComponentListKey);
298 return ERROR_FUNCTION_FAILED;
299 }
300
301 }
302
303 // cleanup memory
304 delete[] szValue;
305 szValue = NULL;
306
307 RegCloseKey(hComponentKey);
308 }
309 RegCloseKey(hComponentListKey);
310
311 return ERROR_SUCCESS;
312}
313
314
315/////////////////
316// reads the feature-component mappings from the registry into the FeatureComponent
317// table of the temporary database. Returns ERROR_SUCCESS, ERROR_FUNCTION_FAILED, or
318// ERROR_OUT_OF_MEMORY
319DWORD ReadFeatureRegistrationDataIntoDatabase(MSIHANDLE hDatabase)
320{
321 DEBUGMSG("Reading existing feature registration data.");
322
323 PMSIHANDLE hView;
324 DWORD dwResult = ERROR_SUCCESS;
325 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `FeatureComponent` (`Product` CHAR(32) NOT NULL, `Feature` CHAR(0) NOT NULL, `Components` CHAR(0) PRIMARY KEY `Product`, `Feature`)"), &hView)) ||
326 ERROR_SUCCESS != (dwResult = MsiViewExecute(hView, 0)))
327 {
328 DEBUGMSG1("Error: Unable to create FeatureComponent table. Error %d", dwResult);
329 return ERROR_FUNCTION_FAILED;
330 }
331
332 HKEY hFeatureListKey;
333 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szOldFeatureComponentKeyName,
334 0, READ_CONTROL | KEY_ENUMERATE_SUB_KEYS,
335 &hFeatureListKey)))
336 {
337 // if the reason that this failed is that the key doesn't exist, no products are installed. So return success
338 if (ERROR_FILE_NOT_FOUND != dwResult)
339 {
340 DEBUGMSG1("Error: Could not open old per-machine feature key. Result: %d. ", dwResult);
341 return ERROR_FUNCTION_FAILED;
342 }
343 else
344 {
345 DEBUGMSG("No old per-machine feature key. No products to migrate.");
346 return ERROR_SUCCESS;
347 }
348 }
349
350 ////
351 // check the ACL on the key to ensure that it is trustworthy.
352 if (!g_fWin9X && !FIsKeyLocalSystemOrAdminOwned(hFeatureListKey))
353 {
354 DEBUGMSG("Warning: Skipping old per-machine feature key, key is not owned by Admin or System.");
355 RegCloseKey(hFeatureListKey);
356 return ERROR_FUNCTION_FAILED;
357 }
358
359 ////
360 // open query for insert into table
361 PMSIHANDLE hInsertView;
362 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("SELECT * FROM `FeatureComponent`"), &hInsertView)) ||
363 ERROR_SUCCESS != (dwResult = MsiViewExecute(hInsertView, 0)))
364 {
365 DEBUGMSG1("Error: Unable to create insert query for FeatureComponent table. Error %d", dwResult);
366 RegCloseKey(hFeatureListKey);
367 return ERROR_FUNCTION_FAILED;
368 }
369
370 DWORD dwIndex=0;
371 PMSIHANDLE hInsertRec = ::MsiCreateRecord(3);
372 while (1)
373 {
374 TCHAR rgchProduct[cchGUIDPacked+1];
375 DWORD cchProduct = cchGUIDPacked+1;
376
377 ////
378 // retrieve the next product subkey name
379 LONG lResult = RegEnumKeyEx(hFeatureListKey, dwIndex++, rgchProduct,
380 &cchProduct, 0, NULL, NULL, NULL);
381 if (lResult == ERROR_MORE_DATA)
382 {
383 // key is not a valid GUID, skip to the next product key
384 DEBUGMSG("Warning: Detected too-long product value. Skipping.");
385 continue;
386 }
387 else if (lResult == ERROR_NO_MORE_ITEMS)
388 {
389 break;
390 }
391 else if (lResult != ERROR_SUCCESS)
392 {
393 DEBUGMSG1("Error: RegEnumKeyEx on old feature key returned %l.", lResult);
394 RegCloseKey(hFeatureListKey);
395 return ERROR_FUNCTION_FAILED;
396 }
397
398 ////
399 // check if the product is a valid guid
400 if ((cchProduct != cchGUIDPacked) || !CanonicalizeAndVerifyPackedGuid(rgchProduct))
401 {
402 // key is not a valid GUID, skip to the next key. Warn if not "Secure" key.
403 if (lstrcmpi(rgchProduct, szSecureSubKeyName))
404 {
405 DEBUGMSG1("Warning: Detected non-product subkey %s. Skipping.", rgchProduct);
406 }
407 continue;
408 }
409
410 // store the product code in the record for later insertion
411 MsiRecordSetString(hInsertRec, 1, rgchProduct);
412
413 // open the subkey
414 HKEY hProductKey;
415 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(hFeatureListKey, rgchProduct,
416 0, KEY_QUERY_VALUE, &hProductKey)))
417 {
418 DEBUGMSG2("Error: Could not open old feature key for product %s. Result: %d.", rgchProduct, dwResult);
419 RegCloseKey(hFeatureListKey);
420 return ERROR_FUNCTION_FAILED;
421 }
422
423 DWORD cbMaxValueLen = 0;
424 DWORD cchMaxNameLen = 0;
425 DWORD cValues = 0;
426 if (ERROR_SUCCESS != (dwResult = RegQueryInfoKey(hProductKey, NULL, NULL, 0,
427 NULL, NULL, NULL, &cValues, &cchMaxNameLen,
428 &cbMaxValueLen, NULL, NULL)))
429 {
430 DEBUGMSG2("Error: Could not retrieve key information for old feature key for product %s. Result: %d. ", rgchProduct, dwResult);
431 RegCloseKey(hProductKey);
432 RegCloseKey(hFeatureListKey);
433 return ERROR_FUNCTION_FAILED;
434 }
435
436 // if no features, skip subkey
437 if (cValues == 0)
438 {
439 RegCloseKey(hProductKey);
440 continue;
441 }
442
443 TCHAR *szValue = new TCHAR[cbMaxValueLen/sizeof(TCHAR)];
444 if (!szValue)
445 {
446 DEBUGMSG("Error: out of memory.");
447 RegCloseKey(hProductKey);
448 RegCloseKey(hFeatureListKey);
449 return ERROR_OUTOFMEMORY;
450 }
451
452 // QueryInfoKey length does not include terminating '\0' for value names.
453 TCHAR *szName = new TCHAR[++cchMaxNameLen];
454 if (!szName)
455 {
456 DEBUGMSG("Error: out of memory.");
457 RegCloseKey(hProductKey);
458 RegCloseKey(hFeatureListKey);
459 delete[] szValue;
460 return ERROR_OUTOFMEMORY;
461 }
462
463 // enumerate through all feature values for this product
464 DWORD dwValueIndex = 0;
465 while (1)
466 {
467 DWORD cbValue = cbMaxValueLen;
468 DWORD cchValueName = cchMaxNameLen;
469 LONG lResult = RegEnumValue(hProductKey, dwValueIndex++, szName,
470 &cchValueName, 0, NULL, reinterpret_cast<unsigned char*>(szValue), &cbValue);
471 if (lResult == ERROR_NO_MORE_ITEMS)
472 {
473 break;
474 }
475 else if (lResult != ERROR_SUCCESS)
476 {
477 DEBUGMSG2("Error: Could not enumerate features for product %s. Result: %d.", rgchProduct, dwResult);
478 RegCloseKey(hProductKey);
479 RegCloseKey(hFeatureListKey);
480 delete[] szValue;
481 delete[] szName;
482 return ERROR_FUNCTION_FAILED;
483 }
484
485 MsiRecordSetString(hInsertRec, 2, szName);
486 MsiRecordSetString(hInsertRec, 3, szValue);
487
488 if (ERROR_SUCCESS != (dwResult = MsiViewModify(hInsertView, MSIMODIFY_INSERT, hInsertRec)))
489 {
490 DEBUGMSG3("Error: could not insert feature data for product %s, feature %s. Result: %d", rgchProduct, szName, dwResult);
491 RegCloseKey(hProductKey);
492 RegCloseKey(hFeatureListKey);
493 delete[] szValue;
494 delete[] szName;
495 return ERROR_FUNCTION_FAILED;
496 }
497 }
498
499 // cleanup memory
500 delete[] szValue;
501 delete[] szName;
502 szValue = NULL;
503 szName = NULL;
504
505 RegCloseKey(hProductKey);
506 }
507 RegCloseKey(hFeatureListKey);
508
509 return ERROR_SUCCESS;
510}
511
512
513
514///////////////////////////////////////////////////////////////////////
515// reads the feature usage information from the registry into the FeatureUsage
516// table of the temporary database. Returns ERROR_SUCCESS or ERROR_OUTOFMEMORY.
517// does not return ERROR_FUNCTION_FAILED, as feature usage data is useless anyway.
518DWORD ReadFeatureUsageDataIntoDatabase(MSIHANDLE hDatabase)
519{
520 DEBUGMSG("Reading existing feature usage data.");
521
522 PMSIHANDLE hView;
523 DWORD dwResult = ERROR_SUCCESS;
524 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `FeatureUsage` (`Product` CHAR(32) NOT NULL, `Feature` CHAR(0) NOT NULL, `Usage` LONG PRIMARY KEY `Product`, `Feature`)"), &hView)) ||
525 ERROR_SUCCESS != (dwResult = MsiViewExecute(hView, 0)))
526 {
527 DEBUGMSG1("Error: Unable to create FeatureUsage table. Error %d", dwResult);
528 return ERROR_SUCCESS;
529 }
530
531 HKEY hFeatureListKey;
532 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szOldFeatureUsageKeyName,
533 0, READ_CONTROL | KEY_ENUMERATE_SUB_KEYS,
534 &hFeatureListKey)))
535 {
536 // if the reason that this failed is that the key doesn't exist, no products are installed. So return success
537 if (ERROR_FILE_NOT_FOUND != dwResult)
538 {
539 DEBUGMSG1("Error: Could not open old feature usage key. Result: %d. ", dwResult);
540 }
541 else
542 {
543 DEBUGMSG("No old feature usage key to migrate.");
544 }
545 return ERROR_SUCCESS;
546 }
547
548 ////
549 // check the ACL on the key to ensure that it is trustworthy.
550 if (!g_fWin9X && !FIsKeyLocalSystemOrAdminOwned(hFeatureListKey))
551 {
552 DEBUGMSG("Skipping old feature usage key, key is not owned by Admin or System.");
553 RegCloseKey(hFeatureListKey);
554 return ERROR_SUCCESS;
555 }
556
557 ////
558 // open query for insert into table
559 PMSIHANDLE hInsertView;
560 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("SELECT * FROM `FeatureUsage`"), &hInsertView)) ||
561 ERROR_SUCCESS != (dwResult = MsiViewExecute(hInsertView, 0)))
562 {
563 RegCloseKey(hFeatureListKey);
564 DEBUGMSG1("Error: Unable to create insert query for FeatureUsage table. Error %d", dwResult);
565 return ERROR_SUCCESS;
566 }
567
568
569 DWORD dwIndex=0;
570 PMSIHANDLE hInsertRec = ::MsiCreateRecord(3);
571 while (1)
572 {
573 TCHAR rgchProduct[cchGUIDPacked+1];
574 DWORD cchProduct = cchGUIDPacked+1;
575
576 ////
577 // retrieve the next product subkey name
578 LONG lResult = RegEnumKeyEx(hFeatureListKey, dwIndex++, rgchProduct,
579 &cchProduct, 0, NULL, NULL, NULL);
580 if (lResult == ERROR_MORE_DATA)
581 {
582 // key is not a valid GUID, skip to the next product key
583 DEBUGMSG("Warning: Detected too-long product value. Skipping.");
584 continue;
585 }
586 else if (lResult == ERROR_NO_MORE_ITEMS)
587 {
588 break;
589 }
590 else if (lResult != ERROR_SUCCESS)
591 {
592 DEBUGMSG1("Error: RegEnumKeyEx on old feature usage key returned %l.", lResult);
593 RegCloseKey(hFeatureListKey);
594 return ERROR_SUCCESS;
595 }
596
597 ////
598 // check if the product is a valid guid
599 if ((cchProduct != cchGUIDPacked) || !CanonicalizeAndVerifyPackedGuid(rgchProduct))
600 {
601 // key is not a valid GUID, skip to the next key
602 if (lstrcmpi(rgchProduct, szSecureSubKeyName))
603 {
604 DEBUGMSG1("Warning: Detected non-product subkey %s. Skipping.", rgchProduct);
605 }
606 continue;
607 }
608
609 // store the product code in the record for later insertion
610 MsiRecordSetString(hInsertRec, 1, rgchProduct);
611
612 // open the subkey. Although we don't actually query any values, retrieving key info (longest subkey, etc)
613 // requires KEY_QUERY_VALUE access.
614 HKEY hProductKey;
615 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(hFeatureListKey, rgchProduct,
616 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hProductKey)))
617 {
618 DEBUGMSG2("Error: Could not open old feature usage key for %s. Result: %d. Skipping", rgchProduct, dwResult);
619 continue;
620 }
621
622 DWORD cchMaxKeyLen = 0;
623 DWORD cSubKeys = 0;
624 if (ERROR_SUCCESS != (dwResult = RegQueryInfoKey(hProductKey, NULL, NULL, 0,
625 &cSubKeys, &cchMaxKeyLen, NULL, NULL, NULL,
626 NULL, NULL, NULL)))
627 {
628 RegCloseKey(hProductKey);
629 DEBUGMSG2("Error: Could not retrieve key information for old feature usage key for %s. Result: %d. ", rgchProduct, dwResult);
630 continue;
631 }
632
633 if (cSubKeys == 0)
634 {
635 RegCloseKey(hProductKey);
636 continue;
637 }
638
639 TCHAR *szFeature = new TCHAR[++cchMaxKeyLen];
640 if (!szFeature)
641 {
642 DEBUGMSG("Error: Out of memory");
643 RegCloseKey(hFeatureListKey);
644 return ERROR_OUTOFMEMORY;
645 }
646
647 DWORD dwKeyIndex = 0;
648 while (1)
649 {
650 DWORD cchKeyName = cchMaxKeyLen;
651 DWORD dwFeatureUsage = 0;
652 DWORD cbValue = sizeof(dwFeatureUsage);
653 LONG lResult = RegEnumKeyEx(hProductKey, dwKeyIndex++, szFeature, &cchKeyName, 0, NULL, NULL, NULL);
654 if (lResult == ERROR_NO_MORE_ITEMS)
655 {
656 break;
657 }
658 else if (lResult != ERROR_SUCCESS)
659 {
660 DEBUGMSG2("Error: Could not enumerate feature usage for product %s. Result: %d.", rgchProduct, dwResult);
661 break;
662 }
663
664 HKEY hFeatureKey;
665 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(hProductKey, szFeature, 0, KEY_QUERY_VALUE, &hFeatureKey)))
666 {
667 DEBUGMSG3("Error: Could not open old feature usage key for %s, %s. Result: %d. ", rgchProduct, szFeature, dwResult);
668 continue;
669 }
670
671 if (ERROR_SUCCESS != (dwResult = RegQueryValueEx(hFeatureKey, szOldFeatureUsageValueName, 0, NULL, reinterpret_cast<unsigned char*>(&dwFeatureUsage), &cbValue)))
672 {
673 RegCloseKey(hFeatureKey);
674 if (dwResult != ERROR_FILE_NOT_FOUND)
675 {
676 DEBUGMSG3("Error: Could not retrieve usage information for old %s, %s. Result: %d. ", rgchProduct, szFeature, dwResult);
677 }
678 continue;
679
680 }
681 RegCloseKey(hFeatureKey);
682
683 MsiRecordSetString(hInsertRec, 2, szFeature);
684 MsiRecordSetInteger(hInsertRec, 3, dwFeatureUsage);
685
686 if (ERROR_SUCCESS != (dwResult = MsiViewModify(hInsertView, MSIMODIFY_INSERT, hInsertRec)))
687 {
688 DEBUGMSG3("Error: could not insert feature usage data for product %s, feature %s. Result: %d", rgchProduct, szFeature, dwResult);
689 }
690 }
691
692 // cleanup memory
693 delete[] szFeature;
694 szFeature = NULL;
695
696 RegCloseKey(hProductKey);
697 }
698 RegCloseKey(hFeatureListKey);
699
700 return ERROR_SUCCESS;
701}
702
703///////////////////////////////////////////////////////////////////////
704// Reads the provided product-list registry key and adds the product and patch
705// to the temporary database. Returns one of ERROR_SUCCESS, ERROR_FUNCTION_FAILED,
706// or ERROR_OUTOFMEMORY
707DWORD ReadProductInstallKey(MSIHANDLE hDatabase, HKEY hKey, LPCTSTR szSID, MSIHANDLE hInsertView, eManagedType eManaged)
708{
709 PMSIHANDLE hInsertRec = MsiCreateRecord(3);
710 MsiRecordSetString(hInsertRec, 1, szSID);
711 MsiRecordSetInteger(hInsertRec, 3, eManaged);
712
713 // Add all products to the list.
714 DWORD dwIndex=0;
715 while (1)
716 {
717 TCHAR rgchProduct[cchGUIDPacked+1];
718 DWORD cchProduct = cchGUIDPacked+1;
719
720 // retrieve the next product subkey name
721 LONG lResult = RegEnumKeyEx(hKey, dwIndex++, rgchProduct,
722 &cchProduct, 0, NULL, NULL, NULL);
723 if (lResult == ERROR_MORE_DATA)
724 {
725 // key is not a valid packed GUID, skip to the next product
726 DEBUGMSG("Warning: Detected too-long product value. Skipping.");
727 continue;
728 }
729 else if (lResult == ERROR_NO_MORE_ITEMS)
730 {
731 break;
732 }
733 else if (lResult != ERROR_SUCCESS)
734 {
735 DEBUGMSG2("Error: Could not enumerate product subkeys for %s. Result: %l. ", szSID, lResult);
736 return ERROR_FUNCTION_FAILED;
737 }
738
739 ////
740 // check if the product is a valid guid
741 if ((cchProduct != cchGUIDPacked) || !CanonicalizeAndVerifyPackedGuid(rgchProduct))
742 {
743 // key is not a valid packed GUID, skip to the next product
744 DEBUGMSG2("Warning: Key %s for user %s is not a valid product. Skipping.", rgchProduct, szSID);
745 continue;
746 }
747
748 // store the product code in the record
749 MsiRecordSetString(hInsertRec, 2, rgchProduct);
750
751 // most likely failure is that the product is already added for this user by another
752 // install type.
753 MsiViewModify(hInsertView, MSIMODIFY_INSERT, hInsertRec);
754
755 DWORD dwResult = ERROR_SUCCESS;
756 if (ERROR_SUCCESS != (dwResult = AddProductPatchesToPatchList(hDatabase, hKey, szSID, rgchProduct, eManaged)))
757 return dwResult;
758 }
759 return ERROR_SUCCESS;
760}
761
762///////////////////////////////////////////////////////////////////////
763// Reads HKCU registry key for per user installs for the current user
764// and adds them to the database. Returns ERROR_SUCCESS,
765// ERROR_FUNCTION_FAILED, or ERROR_OUTOFMEMORY
766DWORD ReadLocalPackagesKey(HKEY hKey, MSIHANDLE hInsertView)
767{
768 PMSIHANDLE hInsertRec = MsiCreateRecord(3);
769 TCHAR rgchProduct[cchGUIDPacked+1];
770
771 // enumerate each product key under the LocalPackages key
772 DWORD dwIndex = 0;
773 while(1)
774 {
775 // retrieve the next product subkey name
776 DWORD cchProduct = cchGUIDPacked+1;
777 LONG lResult = RegEnumKeyEx(hKey, dwIndex++, rgchProduct,
778 &cchProduct, 0, NULL, NULL, NULL);
779 if (lResult == ERROR_MORE_DATA)
780 {
781 // key is not a valid packed GUID, skip to the next product
782 DEBUGMSG("Warning: Detected too-long product value. Skipping.");
783 continue;
784 }
785 else if (lResult == ERROR_NO_MORE_ITEMS)
786 {
787 break;
788 }
789 else if (lResult != ERROR_SUCCESS)
790 {
791 DEBUGMSG1("Error: Could not enumerate product subkeys for LocalPackages Key. Result: %l. ", lResult);
792 return ERROR_FUNCTION_FAILED;
793 }
794
795 ////
796 // check if the product is a valid guid
797 if ((cchProduct != cchGUIDPacked) || !CanonicalizeAndVerifyPackedGuid(rgchProduct))
798 {
799 // key is not a valid packed GUID, skip to the next product
800 DEBUGMSG1("Warning: Key %s for LocalPackages is not a valid product. Skipping.", rgchProduct);
801 continue;
802 }
803
804 // store the product code in the record
805 MsiRecordSetString(hInsertRec, 2, rgchProduct);
806
807 // open the subkey
808 HKEY hProductKey;
809 DWORD dwResult = ERROR_SUCCESS;
810 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(hKey, rgchProduct,
811 0, KEY_QUERY_VALUE, &hProductKey)))
812 {
813 DEBUGMSG2("Error: Could not open old localpackages key for %s. Result: %d. ", rgchProduct, dwResult);
814 return ERROR_FUNCTION_FAILED;
815 }
816
817 DWORD cchMaxValueNameLen = 0;
818 DWORD cValues = 0;
819 if (ERROR_SUCCESS != (dwResult = RegQueryInfoKey(hProductKey, NULL, NULL, 0,
820 NULL, NULL, NULL, &cValues, &cchMaxValueNameLen,
821 NULL, NULL, NULL)))
822 {
823 DEBUGMSG2("Error: Could not retrieve key information for localpackages key %s. Result: %d. ", rgchProduct, dwResult);
824 RegCloseKey(hProductKey);
825 return ERROR_FUNCTION_FAILED;
826 }
827
828 // if no values, skip
829 if (cValues == 0)
830 {
831 RegCloseKey(hProductKey);
832 continue;
833 }
834
835 TCHAR *szName = new TCHAR[++cchMaxValueNameLen];
836 if (!szName)
837 {
838 DEBUGMSG("Error: Out of memory");
839 RegCloseKey(hProductKey);
840 return ERROR_OUTOFMEMORY;
841 }
842
843 DWORD dwValueIndex = 0;
844 while (1)
845 {
846 DWORD cchName = cchMaxValueNameLen;
847 LONG lResult = RegEnumValue(hProductKey, dwValueIndex++, szName,
848 &cchName, 0, NULL, NULL, NULL);
849 if (lResult == ERROR_NO_MORE_ITEMS)
850 {
851 break;
852 }
853 else if (lResult != ERROR_SUCCESS)
854 {
855 DEBUGMSG2("Could not enumerate users for product %s. Result: %d.", rgchProduct, lResult);
856 delete[] szName;
857 RegCloseKey(hProductKey);
858 return ERROR_FUNCTION_FAILED;
859 }
860
861 // asume non-managed product.
862 eManagedType eManaged = emtNonManaged;
863
864 // if the SID is the machine SID, its a managed app.
865 if (0 == lstrcmp(szName, szLocalSystemSID))
866 {
867 eManaged = emtMachineManaged;
868 }
869 else
870 {
871 // check if the name ends in "(Managed)" and strip it if it does, setting
872 // the managed flag as appropriate
873 int cchCount = lstrlen(szName) - cchManagedPackageKeyEnd + 1;
874 if (cchCount > 0 && (0 == lstrcmp(szName + cchCount, szManagedPackageKeyEnd)))
875 {
876 eManaged = emtUserManaged;
877 *(szName+cchCount) = 0;
878 }
879 else
880 eManaged = emtNonManaged;
881 }
882
883 MsiRecordSetInteger(hInsertRec, 3, eManaged);
884 MsiRecordSetString(hInsertRec, 1, szName);
885
886 // most common failure is that the product already exists. All other
887 // failures should be ignored.
888 MsiViewModify(hInsertView, MSIMODIFY_MERGE, hInsertRec);
889 }
890 RegCloseKey(hProductKey);
891 delete[] szName;
892 }
893
894 return ERROR_SUCCESS;
895}
896
897///////////////////////////////////////////////////////////////////////
898// build a mapping from product to user based on all available
899// information, including explicit product registration information
900// stored in Managed hives, per-user installs under HKCU, and cached
901// package identification under the LocalPackages key. This will not
902// catch per-user non-managed installs for the non-current user if the
903// package was never successfully recached by 1.1.
904DWORD BuildUserProductMapping(MSIHANDLE hDatabase, bool fReadHKCUAsSystem)
905{
906 DEBUGMSG("Reading product install information.");
907 DWORD dwResult = ERROR_SUCCESS;
908
909 PMSIHANDLE hView;
910 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `Products` (`User` CHAR(0) NOT NULL, `Product` CHAR(32) NOT NULL, `Managed` INTEGER PRIMARY KEY `User`, `Product`)"), &hView)) ||
911 ERROR_SUCCESS != (dwResult = MsiViewExecute(hView, 0)))
912 {
913 DEBUGMSG1("Error: Unable to create Products table. Error %d", dwResult);
914 return ERROR_FUNCTION_FAILED;
915 }
916
917 PMSIHANDLE hPatchTable;
918 if (ERROR_SUCCESS != (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `PatchApply` (`User` CHAR(0) NOT NULL, `Product` CHAR(32) NOT NULL, `Patch` CHAR(32) NOT NULL, `Known` INTEGER PRIMARY KEY `User`, `Product`, `Patch`)"), &hPatchTable)) ||
919 ERROR_SUCCESS != (dwResult = MsiViewExecute(hPatchTable, 0)))
920 {
921 DEBUGMSG1("Error: Unable to create PatchApply table. Result: %d.", dwResult);
922 return ERROR_FUNCTION_FAILED;
923 }
924
925 PMSIHANDLE hInsertView;
926 if (ERROR_SUCCESS != MsiDatabaseOpenView(hDatabase, TEXT("SELECT * FROM `Products`"), &hInsertView))
927 {
928 DEBUGMSG1("Error: Unable to create insert query for Products table. Error %d", dwResult);
929 return ERROR_FUNCTION_FAILED;
930 }
931
932 // user to product mappings come from four locations:
933 // 1. Per-Machine installed apps
934 // 2. Installer\Managed for per-user managed apps
935 // 3. HKCU for the current user non-managed
936 // 4. CachedPackage List
937 HKEY hKey = 0;
938
939 ////
940 // 1. Per-Machine installed apps. On Win9X upgrades these are per-machine
941 if (ERROR_SUCCESS == (dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szPerMachineInstallKeyName,
942 0, READ_CONTROL | KEY_ENUMERATE_SUB_KEYS, &hKey)))
943 {
944 // ACL on this key does matter
945 if (FIsKeyLocalSystemOrAdminOwned(hKey))
946 {
947 if (ERROR_SUCCESS != (dwResult = ReadProductInstallKey(hDatabase, hKey, szLocalSystemSID, hInsertView, emtMachineManaged)))
948 {
949 RegCloseKey(hKey);
950 return dwResult;
951 }
952 }
953 else
954 {
955 DEBUGMSG("Warning: Skipping per-machine installer key, key is not owned by Admin or System.");
956 }
957
958 RegCloseKey(hKey);
959 }
960 else
961 {
962 // if the reason that this failed is that the key doesn't exist, no products are installed. So we can
963 // continue. Otherwise failure.
964 if (ERROR_FILE_NOT_FOUND != dwResult)
965 {
966 DEBUGMSG1("Error: Could not open per-machine installer key. Result: %d.", dwResult);
967 return ERROR_FUNCTION_FAILED;
968 }
969 }
970
971
972 ////
973 // 2. Installer\Managed for per-user managed apps, but not on Win9X
974 if (!g_fWin9X)
975 {
976 // Although we don't actually query any values, retrieving key info (longest subkey, etc)
977 // requires KEY_QUERY_VALUE access.
978 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szPerUserManagedInstallKeyName,
979 0, READ_CONTROL | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey)))
980 {
981 // if the reason that this failed is that the key doesn't exist, no products are installed.
982 // This is not a catastrophic failure.
983 if (ERROR_FILE_NOT_FOUND != dwResult)
984 {
985 DEBUGMSG1("Error: Failed to open per-user managed key. Result: %d.", dwResult);
986 return ERROR_FUNCTION_FAILED;
987 }
988 }
989 else
990 {
991 // ACL on this key does matter. If its not owned by LocalSystem or Admins, the
992 // information can't be trusted.
993 if (!g_fWin9X && FIsKeyLocalSystemOrAdminOwned(hKey))
994 {
995 // enumerate each user SID under the Managed key
996 DWORD cchMaxKeyLen = 0;
997 DWORD cSubKeys = 0;
998 if (ERROR_SUCCESS != (dwResult = RegQueryInfoKey(hKey, NULL, NULL, 0, &cSubKeys,
999 &cchMaxKeyLen, NULL, NULL, NULL,
1000 NULL, NULL, NULL)))
1001 {
1002 DEBUGMSG1("Error: Could not retrieve key information for per-user managed. Result: %d. ", dwResult);
1003 return ERROR_FUNCTION_FAILED;
1004 }
1005 else if (cSubKeys)
1006 {
1007 // on NT, cchMaxKeyLen does not include terminating NULL for the
1008 // longest subkey name.
1009 cchMaxKeyLen++;
1010 TCHAR *szUser = new TCHAR[cchMaxKeyLen];
1011 if (!szUser)
1012 {
1013 DEBUGMSG("Error: Out of memory");
1014 RegCloseKey(hKey);
1015 return ERROR_OUTOFMEMORY;
1016 }
1017
1018 // the user key name is the user SID plus Installer\Products
1019 TCHAR *szUserKey = new TCHAR[cchMaxKeyLen+sizeof(szPerUserManagedInstallSubKeyName)];
1020 if (!szUserKey)
1021 {
1022 DEBUGMSG("Error: Out of memory");
1023 RegCloseKey(hKey);
1024 delete[] szUser;
1025 return ERROR_OUTOFMEMORY;
1026 }
1027
1028 DWORD dwKeyIndex = 0;
1029 while (1)
1030 {
1031 DWORD cchUser = cchMaxKeyLen;
1032 LONG lResult = RegEnumKeyEx(hKey, dwKeyIndex++, szUser,
1033 &cchUser, 0, NULL, NULL, NULL);
1034 if (lResult == ERROR_NO_MORE_ITEMS)
1035 {
1036 break;
1037 }
1038 else if (lResult != ERROR_SUCCESS)
1039 {
1040 DEBUGMSG1("Error: Could not enumerate users for per-user managed key. Result: %l.", lResult);
1041 RegCloseKey(hKey);
1042 delete[] szUser;
1043 delete[] szUserKey;
1044 return ERROR_FUNCTION_FAILED;
1045 }
1046
1047 // have a user SID
1048 HKEY hPerUserKey;
1049 lstrcpy(szUserKey, szUser);
1050 lstrcat(szUserKey, szPerUserManagedInstallSubKeyName);
1051 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(hKey, szUserKey, 0, KEY_ENUMERATE_SUB_KEYS, &hPerUserKey)))
1052 {
1053 // if the reason that this failed is that the key doesn't exist, no products are installed.
1054 // this is not a catastrophic failure.
1055 if (ERROR_FILE_NOT_FOUND != dwResult)
1056 {
1057 DEBUGMSG2("Error: Failed to open per-user managed key for %s. Result: %d.", szUser, dwResult);
1058 delete[] szUser;
1059 delete[] szUserKey;
1060 RegCloseKey(hKey);
1061 return ERROR_FUNCTION_FAILED;
1062 }
1063 }
1064 else
1065 {
1066 dwResult = ReadProductInstallKey(hDatabase, hPerUserKey, szUser, hInsertView, emtUserManaged);
1067 if (ERROR_SUCCESS != dwResult)
1068 {
1069 delete[] szUser;
1070 delete[] szUserKey;
1071 RegCloseKey(hKey);
1072 return dwResult;
1073 }
1074 }
1075 }
1076 delete[] szUser;
1077 delete[] szUserKey;
1078 }
1079 }
1080 else
1081 {
1082 DEBUGMSG("Warning: Skipping per-user managed installer key, key is not owned by Admin or System.");
1083 }
1084 RegCloseKey(hKey);
1085 }
1086 }
1087
1088 ////
1089 // 3. HKCU for the current user non-managed. Read on Win9X
1090 // only if profiles are not enabled (so HKCU is actually per-machine)
1091 if (!g_fWin9X || fReadHKCUAsSystem)
1092 {
1093 TCHAR szSID[cchMaxSID];
1094 if (fReadHKCUAsSystem)
1095 {
1096 lstrcpy(szSID, szLocalSystemSID);
1097 }
1098 else
1099 {
1100 dwResult = GetCurrentUserStringSID(szSID);
1101 if (ERROR_SUCCESS != dwResult)
1102 {
1103 DEBUGMSG1("Unable to retrieve current user SID string. Result: %d.", dwResult);
1104 RegCloseKey(hKey);
1105 return ERROR_FUNCTION_FAILED;
1106 }
1107 }
1108
1109 if (g_fWin9X || lstrcmp(szSID, szLocalSystemSID))
1110 {
1111 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(HKEY_CURRENT_USER, szPerUserInstallKeyName, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)))
1112 {
1113 // if the key could not be opened because it was not present, no products
1114 // are installed per-user. This is not a catastrophic failure.
1115 if (ERROR_FILE_NOT_FOUND != dwResult)
1116 {
1117 DEBUGMSG1("Error: Failed to open per-user managed key. Result: %d.", dwResult);
1118 return ERROR_FUNCTION_FAILED;
1119 }
1120 }
1121 else
1122 {
1123 // ACL on this key does not matter
1124 dwResult = ReadProductInstallKey(hDatabase, hKey, szSID, hInsertView, emtNonManaged);
1125 RegCloseKey(hKey);
1126
1127 if (ERROR_SUCCESS != dwResult)
1128 return dwResult;
1129 }
1130 }
1131 else
1132 {
1133 DEBUGMSG("Running as system. No HKCU products to detect.");
1134 }
1135 }
1136
1137
1138 ////
1139 // 4. Cached Package List
1140 if (ERROR_SUCCESS != (dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szLocalPackagesKeyName,
1141 0, READ_CONTROL | KEY_ENUMERATE_SUB_KEYS, &hKey)))
1142 {
1143 // if the reason that this failed is that the key doesn't exist, no products are installed. So return success
1144 if (ERROR_FILE_NOT_FOUND != dwResult)
1145 {
1146 DEBUGMSG1("Error: Failed to open local packages key. Result: %d.", dwResult);
1147 return ERROR_FUNCTION_FAILED;
1148 }
1149 }
1150 else
1151 {
1152 // ACL on this key does matter
1153 if (g_fWin9X || FIsKeyLocalSystemOrAdminOwned(hKey))
1154 {
1155 dwResult = ReadLocalPackagesKey(hKey, hInsertView);
1156 if (ERROR_SUCCESS != dwResult)
1157 {
1158 RegCloseKey(hKey);
1159 return dwResult;
1160 }
1161 }
1162 else
1163 {
1164 DEBUGMSG("Skipping localpackages key, key is not owned by Admin or System.");
1165 }
1166
1167 RegCloseKey(hKey);
1168 }
1169
1170 return ERROR_SUCCESS;
1171}
1172
1173///////////////////////////////////////////////////////////////////////
1174// Read all configuration informaiton into the specificied database
1175// path, including component registration, product install state,
1176// patches, transforms, and feature-component mappings. Does not
1177// write anything into the registry.
1178DWORD ReadProductRegistrationDataIntoDatabase(TCHAR* szDatabase, MSIHANDLE& hDatabase, bool fReadHKCUAsSystem)
1179{
1180 DWORD dwResult = ERROR_SUCCESS;
1181
1182 if (!CheckWinVersion())
1183 return ERROR_FUNCTION_FAILED;
1184
1185 // try to open the database for read/write
1186 if (ERROR_SUCCESS != MsiOpenDatabase(szDatabase, MSIDBOPEN_CREATE, &hDatabase))
1187 return ERROR_FUNCTION_FAILED;
1188
1189 // create a table to hold files that should be cleaned up on failure or success
1190 PMSIHANDLE hCleanUpTable;
1191 if (ERROR_SUCCESS == (dwResult = MsiDatabaseOpenView(hDatabase, TEXT("CREATE TABLE `CleanupFile` (`File` CHAR(0) NOT NULL, `OnSuccess` INTEGER PRIMARY KEY `File`)"), &hCleanUpTable)))
1192 dwResult = MsiViewExecute(hCleanUpTable, 0);
1193
1194 // Read Component path registration data into the database and compute original
1195 // MSI-based SharedDLL reference counts.
1196 if (ERROR_SUCCESS == dwResult)
1197 dwResult = ReadComponentRegistrationDataIntoDatabase(hDatabase);
1198
1199 // Read FeatureComponent data into the database
1200 if (ERROR_SUCCESS == dwResult)
1201 dwResult = ReadFeatureRegistrationDataIntoDatabase(hDatabase);
1202
1203 // It doesn't matter if feature usage data gets migrated completely or not
1204 if (ERROR_SUCCESS == dwResult)
1205 ReadFeatureUsageDataIntoDatabase(hDatabase);
1206
1207 // UserProduct mappings determine which users have products installed
1208 if (ERROR_SUCCESS == dwResult)
1209 dwResult = BuildUserProductMapping(hDatabase, fReadHKCUAsSystem);
1210
1211 // tickles all cached patches to determine what potential products they
1212 // could be applied to. This info is used to migrate non-managed per-user
1213 // installs.
1214 if (ERROR_SUCCESS == dwResult)
1215 dwResult = ScanCachedPatchesForProducts(hDatabase);
1216
1217 // cross-references per-user non-managed installs with patch data from
1218 // above. Creates a list of patches that could be applied to each
1219 // per-user install.
1220 if (ERROR_SUCCESS == dwResult)
1221 dwResult = AddPerUserPossiblePatchesToPatchList(hDatabase);
1222
1223 return ERROR_SUCCESS;
1224}
1225
1226