aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2023-02-09 13:59:15 -0800
committerRob Mensching <rob@firegiant.com>2023-02-09 15:18:09 -0800
commitcff729a33833a963bbaeca9def11b7c09f8c2c0b (patch)
tree9fd34499f988442ce78f7a1209c71cf47b4dc134
parent4d0b22438b5795e7a92c227f1c824b8da853d859 (diff)
downloadwix-cff729a33833a963bbaeca9def11b7c09f8c2c0b.tar.gz
wix-cff729a33833a963bbaeca9def11b7c09f8c2c0b.tar.bz2
wix-cff729a33833a963bbaeca9def11b7c09f8c2c0b.zip
Improve instance transforms
Fix bug where the string `UpgradeCode` was being used instead of the GUID for the UpgradeCode in an instance transform. Also handle the case where the instance transform's Property does or does not exist when creating the transform. Fixes 7193
-rw-r--r--src/internal/WixInternal.TestSupport/Query.cs127
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs43
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs103
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs64
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs6
6 files changed, 202 insertions, 143 deletions
diff --git a/src/internal/WixInternal.TestSupport/Query.cs b/src/internal/WixInternal.TestSupport/Query.cs
index 38f5df64..4b396454 100644
--- a/src/internal/WixInternal.TestSupport/Query.cs
+++ b/src/internal/WixInternal.TestSupport/Query.cs
@@ -26,6 +26,20 @@ namespace WixInternal.TestSupport
26 return results.ToArray(); 26 return results.ToArray();
27 } 27 }
28 28
29 public static string[] QueryDatabase(Database db, string[] tables)
30 {
31 var results = new List<string>();
32 var resultsByTable = QueryDatabaseByTable(db, tables);
33 var sortedTables = tables.ToList();
34 sortedTables.Sort();
35 foreach (var tableName in sortedTables)
36 {
37 var rows = resultsByTable[tableName];
38 rows?.ForEach(r => results.Add($"{tableName}:{r}"));
39 }
40 return results.ToArray();
41 }
42
29 /// <summary> 43 /// <summary>
30 /// Returns rows from requested tables formatted to facilitate testing. 44 /// Returns rows from requested tables formatted to facilitate testing.
31 /// If the table did not exist in the database, its list will be null. 45 /// If the table did not exist in the database, its list will be null.
@@ -39,68 +53,89 @@ namespace WixInternal.TestSupport
39 53
40 if (tables?.Length > 0) 54 if (tables?.Length > 0)
41 { 55 {
42 var sb = new StringBuilder();
43 using (var db = new Database(path)) 56 using (var db = new Database(path))
44 { 57 {
45 foreach (var table in tables) 58 results = QueryDatabaseByTable(db, tables);
59 }
60 }
61
62 return results;
63 }
64
65 /// <summary>
66 /// Returns rows from requested tables formatted to facilitate testing.
67 /// If the table did not exist in the database, its list will be null.
68 /// </summary>
69 /// <param name="db"></param>
70 /// <param name="tables"></param>
71 /// <returns></returns>
72 public static Dictionary<string, List<string>> QueryDatabaseByTable(Database db, string[] tables)
73 {
74 var results = new Dictionary<string, List<string>>();
75
76 if (tables?.Length > 0)
77 {
78 var sb = new StringBuilder();
79
80 foreach (var table in tables)
81 {
82 if (table == "_SummaryInformation")
46 { 83 {
47 if (table == "_SummaryInformation") 84 var entries = new List<string>();
48 { 85 results.Add(table, entries);
49 var entries = new List<string>(); 86
50 results.Add(table, entries); 87 entries.Add($"Title\t{db.SummaryInfo.Title}");
51 88 entries.Add($"Subject\t{db.SummaryInfo.Subject}");
52 entries.Add($"Title\t{db.SummaryInfo.Title}"); 89 entries.Add($"Author\t{db.SummaryInfo.Author}");
53 entries.Add($"Subject\t{db.SummaryInfo.Subject}"); 90 entries.Add($"Keywords\t{db.SummaryInfo.Keywords}");
54 entries.Add($"Author\t{db.SummaryInfo.Author}"); 91 entries.Add($"Comments\t{db.SummaryInfo.Comments}");
55 entries.Add($"Keywords\t{db.SummaryInfo.Keywords}"); 92 entries.Add($"Template\t{db.SummaryInfo.Template}");
56 entries.Add($"Comments\t{db.SummaryInfo.Comments}"); 93 entries.Add($"CodePage\t{db.SummaryInfo.CodePage}");
57 entries.Add($"Template\t{db.SummaryInfo.Template}"); 94 entries.Add($"PageCount\t{db.SummaryInfo.PageCount}");
58 entries.Add($"CodePage\t{db.SummaryInfo.CodePage}"); 95 entries.Add($"WordCount\t{db.SummaryInfo.WordCount}");
59 entries.Add($"PageCount\t{db.SummaryInfo.PageCount}"); 96 entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}");
60 entries.Add($"WordCount\t{db.SummaryInfo.WordCount}"); 97 entries.Add($"Security\t{db.SummaryInfo.Security}");
61 entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}"); 98
62 entries.Add($"Security\t{db.SummaryInfo.Security}"); 99 continue;
63 100 }
64 continue;
65 }
66 101
67 if (!db.IsTablePersistent(table)) 102 if (!db.IsTablePersistent(table))
68 { 103 {
69 results.Add(table, null); 104 results.Add(table, null);
70 continue; 105 continue;
71 } 106 }
72 107
73 var rows = new List<string>(); 108 var rows = new List<string>();
74 results.Add(table, rows); 109 results.Add(table, rows);
75 110
76 using (var view = db.OpenView("SELECT * FROM `{0}`", table)) 111 using (var view = db.OpenView("SELECT * FROM `{0}`", table))
112 {
113 view.Execute();
114
115 Record record;
116 while ((record = view.Fetch()) != null)
77 { 117 {
78 view.Execute(); 118 sb.Clear();
79 119
80 Record record; 120 using (record)
81 while ((record = view.Fetch()) != null)
82 { 121 {
83 sb.Clear(); 122 for (var i = 0; i < record.FieldCount; ++i)
84
85 using (record)
86 { 123 {
87 for (var i = 0; i < record.FieldCount; ++i) 124 if (i > 0)
88 { 125 {
89 if (i > 0) 126 sb.Append("\t");
90 {
91 sb.Append("\t");
92 }
93
94 sb.Append(record[i + 1]?.ToString());
95 } 127 }
96 }
97 128
98 rows.Add(sb.ToString()); 129 sb.Append(record[i + 1]?.ToString());
130 }
99 } 131 }
132
133 rows.Add(sb.ToString());
100 } 134 }
101 rows.Sort();
102 } 135 }
103 } 136
137 rows.Sort();
138 }
104 } 139 }
105 140
106 return results; 141 return results;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index b61247a5..95cc18cd 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -76,7 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
76 } 76 }
77 else if ("UpgradeCode" == id) 77 else if ("UpgradeCode" == id)
78 { 78 {
79 updatedUpgradeCode = id; 79 updatedUpgradeCode = row.FieldAsString(1);
80 propertyTable.Rows.RemoveAt(i); 80 propertyTable.Rows.RemoveAt(i);
81 } 81 }
82 } 82 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
index 1d480250..9e5eb04e 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
@@ -41,43 +41,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind
41 41
42 if (wixInstanceTransformsSymbols.Any()) 42 if (wixInstanceTransformsSymbols.Any())
43 { 43 {
44 string targetProductCode = null;
45 string targetUpgradeCode = null;
46 string targetProductVersion = null;
47
48 var targetSummaryInformationTable = this.Output.Tables["_SummaryInformation"]; 44 var targetSummaryInformationTable = this.Output.Tables["_SummaryInformation"];
49 var targetPropertyTable = this.Output.Tables["Property"]; 45 var targetPropertyRows = new RowDictionary<PropertyRow>(this.Output.Tables["Property"]);
46 PropertyRow propertyRow = null;
50 47
51 // Get the data from target database 48 // Get the data from target database
52 foreach (var propertyRow in targetPropertyTable.Rows) 49 var targetProductCode = targetPropertyRows.TryGetValue("ProductCode", out propertyRow) ? propertyRow.Value : null;
53 { 50 var targetProductVersion = targetPropertyRows.TryGetValue("ProductVersion", out propertyRow) ? propertyRow.Value : null;
54 if ("ProductCode" == (string)propertyRow[0]) 51 var targetUpgradeCode = targetPropertyRows.TryGetValue("UpgradeCode", out propertyRow) ? propertyRow.Value : null;
55 {
56 targetProductCode = (string)propertyRow[1];
57 }
58 else if ("ProductVersion" == (string)propertyRow[0])
59 {
60 targetProductVersion = (string)propertyRow[1];
61 }
62 else if ("UpgradeCode" == (string)propertyRow[0])
63 {
64 targetUpgradeCode = (string)propertyRow[1];
65 }
66 }
67 52
68 // Index the Instance Component Rows, we'll get the Components rows from the real Component table. 53 // Index the Instance Component Rows, we'll get the Components rows from the real Component table.
69 var targetInstanceComponentTable = this.Section.Symbols.OfType<WixInstanceComponentSymbol>(); 54 var instanceComponentSymbols = this.Section.Symbols.OfType<WixInstanceComponentSymbol>().ToDictionary(t => t.Id.Id, t => (ComponentRow)null);
70 var instanceComponentGuids = targetInstanceComponentTable.ToDictionary(t => t.Id.Id, t => (ComponentRow)null);
71 55
72 if (instanceComponentGuids.Any()) 56 if (instanceComponentSymbols.Any())
73 { 57 {
74 var targetComponentTable = this.Output.Tables["Component"]; 58 var targetComponentTable = this.Output.Tables["Component"];
75 foreach (ComponentRow componentRow in targetComponentTable.Rows) 59 foreach (ComponentRow componentRow in targetComponentTable.Rows)
76 { 60 {
77 var component = (string)componentRow[0]; 61 var component = (string)componentRow[0];
78 if (instanceComponentGuids.ContainsKey(component)) 62 if (instanceComponentSymbols.ContainsKey(component))
79 { 63 {
80 instanceComponentGuids[component] = componentRow; 64 instanceComponentSymbols[component] = componentRow;
81 } 65 }
82 } 66 }
83 } 67 }
@@ -123,9 +107,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
123 productCodeRow[0] = "ProductCode"; 107 productCodeRow[0] = "ProductCode";
124 productCodeRow[1] = productCode; 108 productCodeRow[1] = productCode;
125 109
126 // Change the instance property 110 // Set the instance property. If the property exists in the MSI already, mark the row as modified in the transform.
111 // Otherwise, we need to mark the row as an add.
127 var instanceIdRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); 112 var instanceIdRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers);
128 instanceIdRow.Operation = RowOperation.Modify; 113 instanceIdRow.Operation = targetPropertyRows.ContainsKey(instanceSymbol.PropertyId) ? RowOperation.Modify : RowOperation.Add;
129 instanceIdRow.Fields[1].Modified = true; 114 instanceIdRow.Fields[1].Modified = true;
130 instanceIdRow[0] = instanceSymbol.PropertyId; 115 instanceIdRow[0] = instanceSymbol.PropertyId;
131 instanceIdRow[1] = instanceId; 116 instanceIdRow[1] = instanceId;
@@ -192,10 +177,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
192 } 177 }
193 178
194 // If there are instance Components generate new GUIDs for them. 179 // If there are instance Components generate new GUIDs for them.
195 if (0 < instanceComponentGuids.Count) 180 if (0 < instanceComponentSymbols.Count)
196 { 181 {
197 var componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); 182 var componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]);
198 foreach (var targetComponentRow in instanceComponentGuids.Values) 183 foreach (var targetComponentRow in instanceComponentSymbols.Values)
199 { 184 {
200 var guid = targetComponentRow.Guid; 185 var guid = targetComponentRow.Guid;
201 if (!String.IsNullOrEmpty(guid)) 186 if (!String.IsNullOrEmpty(guid))
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs
new file mode 100644
index 00000000..2485aaf8
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs
@@ -0,0 +1,103 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.CoreIntegration
4{
5 using System;
6 using System.IO;
7 using System.Linq;
8 using WixInternal.Core.TestPackage;
9 using WixInternal.TestSupport;
10 using WixToolset.Data.WindowsInstaller;
11 using WixToolset.Dtf.WindowsInstaller;
12 using Xunit;
13
14 public class InstanceTransformFixture
15 {
16 [Fact]
17 public void CanBuildInstanceTransform()
18 {
19 var folder = TestData.Get("TestData", "InstanceTransform");
20
21 using (var fs = new DisposableFileSystem())
22 {
23 var intermediateFolder = fs.GetFolder();
24 var msiPath = Path.Combine(intermediateFolder, "bin", "test.msi");
25 var wixpdbPath = Path.Combine(intermediateFolder, "bin", "test.wixpdb");
26 var mstPath = Path.Combine(intermediateFolder, "iii.mst");
27
28 var result = WixRunner.Execute(new[]
29 {
30 "build",
31 Path.Combine(folder, "Package.wxs"),
32 Path.Combine(folder, "PackageComponents.wxs"),
33 "-loc", Path.Combine(folder, "Package.en-us.wxl"),
34 "-bindpath", Path.Combine(folder, "data"),
35 "-intermediateFolder", intermediateFolder,
36 "-o", msiPath
37 });
38
39 result.AssertSuccess();
40
41 var output = WindowsInstallerData.Load(wixpdbPath, false);
42 var substorage = output.SubStorages.Single();
43 Assert.Equal("I1", substorage.Name);
44
45 var data = substorage.Data;
46 WixAssert.CompareLineByLine(new[]
47 {
48 "_SummaryInformation",
49 "Property",
50 "Upgrade"
51 }, data.Tables.Select(t => t.Name).ToArray());
52
53 WixAssert.CompareLineByLine(new[]
54 {
55 "INSTANCEPROPERTY\tI1",
56 "ProductName\tMsiPackage (Instance 1)",
57 }, JoinRows(data.Tables["Property"]));
58
59 WixAssert.CompareLineByLine(new[]
60 {
61 "{22222222-2222-2222-2222-222222222222}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED",
62 "{11111111-1111-1111-1111-111111111111}\t\t1.0.0.0\t1033\t1\t0\t0",
63 "{22222222-2222-2222-2222-222222222222}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED",
64 "{11111111-1111-1111-1111-111111111111}\t1.0.0.0\t\t1033\t2\t0\t0",
65 }, JoinRows(data.Tables["Upgrade"]));
66
67 var names = Query.GetSubStorageNames(msiPath);
68 Query.ExtractSubStorage(msiPath, "I1", mstPath);
69
70 using (var db = new Database(msiPath, DatabaseOpenMode.Transact))
71 {
72 db.ApplyTransform(mstPath);
73
74 var results = Query.QueryDatabase(db, new[] { "Property", "Upgrade" });
75 var resultsWithoutProductCode = results.Where(s => !s.StartsWith("Property:ProductCode\t{")).ToArray();
76 WixAssert.CompareLineByLine(new[]
77 {
78 "Property:ALLUSERS\t1",
79 "Property:INSTANCEPROPERTY\tI1",
80 "Property:Manufacturer\tExample Corporation",
81 "Property:ProductLanguage\t1033",
82 "Property:ProductName\tMsiPackage (Instance 1)",
83 "Property:ProductVersion\t1.0.0.0",
84 "Property:SecureCustomProperties\tINSTANCEPROPERTY;WIX_DOWNGRADE_DETECTED;WIX_UPGRADE_DETECTED",
85 "Property:UpgradeCode\t{22222222-2222-2222-2222-222222222222}",
86 "Upgrade:{22222222-2222-2222-2222-222222222222}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED",
87 "Upgrade:{22222222-2222-2222-2222-222222222222}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED",
88 }, resultsWithoutProductCode);
89 }
90 }
91 }
92
93 private static string[] JoinRows(Table table)
94 {
95 return table.Rows.Select(r => JoinFields(r.Fields)).ToArray();
96
97 static string JoinFields(Field[] fields)
98 {
99 return String.Join('\t', fields.Select(f => f.ToString()));
100 }
101 }
102 }
103}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
index a7dbe542..0dd6d75f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
@@ -2,12 +2,10 @@
2 2
3namespace WixToolsetTest.CoreIntegration 3namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System;
6 using System.IO; 5 using System.IO;
7 using System.Linq; 6 using System.Linq;
8 using Example.Extension;
9 using WixInternal.TestSupport;
10 using WixInternal.Core.TestPackage; 7 using WixInternal.Core.TestPackage;
8 using WixInternal.TestSupport;
11 using WixToolset.Data; 9 using WixToolset.Data;
12 using WixToolset.Data.Symbols; 10 using WixToolset.Data.Symbols;
13 using WixToolset.Data.WindowsInstaller; 11 using WixToolset.Data.WindowsInstaller;
@@ -599,56 +597,6 @@ namespace WixToolsetTest.CoreIntegration
599 } 597 }
600 598
601 [Fact] 599 [Fact]
602 public void CanBuildInstanceTransform()
603 {
604 var folder = TestData.Get(@"TestData\InstanceTransform");
605
606 using (var fs = new DisposableFileSystem())
607 {
608 var intermediateFolder = fs.GetFolder();
609
610 var result = WixRunner.Execute(new[]
611 {
612 "build",
613 Path.Combine(folder, "Package.wxs"),
614 Path.Combine(folder, "PackageComponents.wxs"),
615 "-loc", Path.Combine(folder, "Package.en-us.wxl"),
616 "-bindpath", Path.Combine(folder, "data"),
617 "-intermediateFolder", intermediateFolder,
618 "-o", Path.Combine(intermediateFolder, @"bin\test.msi")
619 });
620
621 result.AssertSuccess();
622
623 var output = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"), false);
624 var substorage = output.SubStorages.Single();
625 Assert.Equal("I1", substorage.Name);
626
627 var data = substorage.Data;
628 WixAssert.CompareLineByLine(new[]
629 {
630 "_SummaryInformation",
631 "Property",
632 "Upgrade"
633 }, data.Tables.Select(t => t.Name).ToArray());
634
635 WixAssert.CompareLineByLine(new[]
636 {
637 "INSTANCEPROPERTY\tI1",
638 "ProductName\tMsiPackage (Instance 1)",
639 }, JoinRows(data.Tables["Property"]));
640
641 WixAssert.CompareLineByLine(new[]
642 {
643 "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED",
644 "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t0\t0",
645 "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED",
646 "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t0\t0"
647 }, JoinRows(data.Tables["Upgrade"]));
648 }
649 }
650
651 [Fact]
652 public void FailsBuildAtBindTimeForMissingEnsureTable() 600 public void FailsBuildAtBindTimeForMissingEnsureTable()
653 { 601 {
654 var folder = TestData.Get(@"TestData"); 602 var folder = TestData.Get(@"TestData");
@@ -680,15 +628,5 @@ namespace WixToolsetTest.CoreIntegration
680 Assert.False(File.Exists(msiPath)); 628 Assert.False(File.Exists(msiPath));
681 } 629 }
682 } 630 }
683
684 private static string[] JoinRows(Table table)
685 {
686 return table.Rows.Select(r => JoinFields(r.Fields)).ToArray();
687
688 static string JoinFields(Field[] fields)
689 {
690 return String.Join('\t', fields.Select(f => f.ToString()));
691 }
692 }
693 } 631 }
694} 632}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs
index 7826d673..f60149a2 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs
@@ -1,13 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a" Compressed="no" InstallerVersion="200" Scope="perMachine"> 2 <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="11111111-1111-1111-1111-111111111111" Compressed="no" InstallerVersion="200" Scope="perMachine">
3
4
5 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" /> 3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
6 4
7 <Property Id="INSTANCEPROPERTY" Secure="yes" /> 5 <Property Id="INSTANCEPROPERTY" Secure="yes" />
8 6
9 <InstanceTransforms Property="INSTANCEPROPERTY"> 7 <InstanceTransforms Property="INSTANCEPROPERTY">
10 <Instance Id="I1" ProductCode="*" ProductName="MsiPackage (Instance 1)" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a" /> 8 <Instance Id="I1" ProductCode="*" ProductName="MsiPackage (Instance 1)" UpgradeCode="22222222-2222-2222-2222-222222222222" />
11 </InstanceTransforms> 9 </InstanceTransforms>
12 10
13 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)"> 11 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">