Provide Record enumerator on View that disposes fetched Records
Rob Mensching committed
Jan 13, 2020 at 09:10 UTC
94b941ee95a294228516097c269e27dfa41593ab
9 files changed
+389
-390
src/WixToolset.Core.TestPackage/WixRunnerResult.cs
-1
@@ -26,7 +26,6 @@ namespace WixToolset.Core.TestPackage
26
var filename = message.SourceLineNumbers?.FileName ?? "TEST";
27
var line = message.SourceLineNumbers?.LineNumber ?? -1;
28
var type = message.Level.ToString().ToLowerInvariant();
29
- var output = message.Level >= MessageLevel.Warning ? Console.Out : Console.Error;
29
30
if (line > 0)
31
{
src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+29
-37
@@ -89,46 +89,38 @@ namespace WixToolset.Core.WindowsInstaller.Bind
89
using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`"))
90
{
91
// add each file row from the merge module into the file row collection (check for errors along the way)
92
- while (true)
92
+ foreach (Record record in view.Records)
93
{
94
- using (Record record = view.Fetch())
94
+ // NOTE: this is very tricky - the merge module file rows are not added to the
95
+ // file table because they should not be created via idt import. Instead, these
96
+ // rows are created by merging in the actual modules.
97
+ var fileTuple = new FileTuple(wixMergeRow.SourceLineNumbers, new Identifier(AccessModifier.Private, record[1]));
98
+ fileTuple.Attributes = wixMergeRow.FileAttributes;
99
+ fileTuple.DirectoryRef = record[2];
100
+ fileTuple.DiskId = wixMergeRow.DiskId;
101
+ fileTuple.Source = new IntermediateFieldPathValue { Path = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]) };
102
+
103
+ var mergeModuleFileFacade = new FileFacade(true, fileTuple);
104
+
105
+ // If case-sensitive collision with another merge module or a user-authored file identifier.
106
+ if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.Id.Id, out var collidingFacade))
107
{
96
- if (null == record)
97
- {
98
- break;
99
- }
100
-
101
- // NOTE: this is very tricky - the merge module file rows are not added to the
102
- // file table because they should not be created via idt import. Instead, these
103
- // rows are created by merging in the actual modules.
104
- var fileTuple = new FileTuple(wixMergeRow.SourceLineNumbers, new Identifier(AccessModifier.Private, record[1]));
105
- fileTuple.Attributes = wixMergeRow.FileAttributes;
106
- fileTuple.DirectoryRef = record[2];
107
- fileTuple.DiskId = wixMergeRow.DiskId;
108
- fileTuple.Source = new IntermediateFieldPathValue { Path = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]) };
109
-
110
- var mergeModuleFileFacade = new FileFacade(true, fileTuple);
111
-
112
- // If case-sensitive collision with another merge module or a user-authored file identifier.
113
- if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.Id.Id, out var collidingFacade))
114
- {
115
- this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.File.Id.Id));
116
- }
117
- else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.Id.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module
118
- {
119
- this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.File.Id.Id, collidingFacade.File.Id.Id));
120
- }
121
- else // no collision
122
- {
123
- mergeModulesFileFacades.Add(mergeModuleFileFacade);
124
-
125
- // Keep updating the indexes as new rows are added.
126
- indexedFileFacades.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade);
127
- uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade);
128
- }
129
-
130
- containsFiles = true;
108
+ this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.File.Id.Id));
109
}
110
+ else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.Id.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module
111
+ {
112
+ this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.File.Id.Id, collidingFacade.File.Id.Id));
113
+ }
114
+ else // no collision
115
+ {
116
+ mergeModulesFileFacades.Add(mergeModuleFileFacade);
117
+
118
+ // Keep updating the indexes as new rows are added.
119
+ indexedFileFacades.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade);
120
+ uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade);
121
+ }
122
+
123
+ containsFiles = true;
124
}
125
}
126
}
src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+6
-16
@@ -220,14 +220,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
220
string query = String.Format(CultureInfo.InvariantCulture, "SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]);
221
222
using (View view = db.OpenExecuteView(query))
223
+ using (Record record = view.Fetch())
224
{
224
- using (Record record = view.Fetch())
225
+ if (null != record)
226
{
226
- if (null != record)
227
- {
228
- this.Messaging.Write(WarningMessages.SuppressMergedAction((string)row[1], row[0].ToString()));
229
- view.Modify(ModifyView.Delete, record);
230
- }
227
+ this.Messaging.Write(WarningMessages.SuppressMergedAction((string)row[1], row[0].ToString()));
228
+ view.Modify(ModifyView.Delete, record);
229
}
230
}
231
}
@@ -244,17 +242,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
242
243
using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName)))
244
{
247
- while (true)
245
+ foreach (Record resultRecord in view.Records)
246
{
249
- using (Record resultRecord = view.Fetch())
250
- {
251
- if (null == resultRecord)
252
- {
253
- break;
254
- }
255
-
256
- this.Messaging.Write(WarningMessages.SuppressMergedAction(resultRecord.GetString(1), tableName));
257
- }
247
+ this.Messaging.Write(WarningMessages.SuppressMergedAction(resultRecord.GetString(1), tableName));
248
}
249
}
250
src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
+6
-14
@@ -57,25 +57,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
57
58
var mediaRows = this.Section.Tuples.OfType<MediaTuple>().ToDictionary(t => t.DiskId);
59
60
- using (Database db = new Database(this.DatabasePath, OpenDatabase.ReadOnly))
60
+ using (var db = new Database(this.DatabasePath, OpenDatabase.ReadOnly))
61
{
62
- using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
62
+ using (var directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
63
{
64
- while (true)
64
+ foreach (var directoryRecord in directoryView.Records)
65
{
66
- using (Record directoryRecord = directoryView.Fetch())
67
- {
68
- if (null == directoryRecord)
69
- {
70
- break;
71
- }
66
+ var sourceName = Common.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage);
67
73
- string sourceName = Common.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage);
68
+ var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directoryRecord.GetString(2), sourceName);
69
75
- var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directoryRecord.GetString(2), sourceName);
76
-
77
- directories.Add(directoryRecord.GetString(1), resolvedDirectory);
78
- }
70
+ directories.Add(directoryRecord.GetString(1), resolvedDirectory);
71
}
72
}
73
src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+109
-133
@@ -60,47 +60,39 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
60
{
61
using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
62
{
63
- while (true)
63
+ foreach (Record digitalSignatureRecord in digitalSignatureView.Records)
64
{
65
- using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
66
- {
67
- if (null == digitalSignatureRecord)
68
- {
69
- break;
70
- }
65
+ Row digitalSignatureRow = null;
66
+ digitalSignatureRow = digitalSignatureTable.CreateRow(null);
67
72
- Row digitalSignatureRow = null;
73
- digitalSignatureRow = digitalSignatureTable.CreateRow(null);
68
+ string table = digitalSignatureRecord.GetString(0);
69
+ string signObject = digitalSignatureRecord.GetString(1);
70
75
- string table = digitalSignatureRecord.GetString(0);
76
- string signObject = digitalSignatureRecord.GetString(1);
71
+ digitalSignatureRow[0] = table;
72
+ digitalSignatureRow[1] = signObject;
73
+ digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
74
78
- digitalSignatureRow[0] = table;
79
- digitalSignatureRow[1] = signObject;
80
- digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
75
+ if (false == digitalSignatureRecord.IsNull(3))
76
+ {
77
+ // Export to a file, because the MSI API's require us to provide a file path on disk
78
+ string hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature");
79
+ string hashFileName = string.Concat(table, ".", signObject, ".bin");
80
82
- if (false == digitalSignatureRecord.IsNull(3))
83
- {
84
- // Export to a file, because the MSI API's require us to provide a file path on disk
85
- string hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature");
86
- string hashFileName = string.Concat(table, ".", signObject, ".bin");
81
+ Directory.CreateDirectory(hashPath);
82
+ hashPath = Path.Combine(hashPath, hashFileName);
83
88
- Directory.CreateDirectory(hashPath);
89
- hashPath = Path.Combine(hashPath, hashFileName);
84
+ using (FileStream fs = File.Create(hashPath))
85
+ {
86
+ int bytesRead;
87
+ byte[] buffer = new byte[1024 * 4];
88
91
- using (FileStream fs = File.Create(hashPath))
89
+ while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
90
{
93
- int bytesRead;
94
- byte[] buffer = new byte[1024 * 4];
95
-
96
- while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
97
- {
98
- fs.Write(buffer, 0, bytesRead);
99
- }
91
+ fs.Write(buffer, 0, bytesRead);
92
}
101
-
102
- digitalSignatureRow[3] = hashFileName;
93
}
94
+
95
+ digitalSignatureRow[3] = hashFileName;
96
}
97
}
98
}
@@ -111,145 +103,129 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
103
{
104
using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
105
{
114
- while (true)
106
+ foreach (Record digitalCertificateRecord in digitalCertificateView.Records)
107
{
116
- using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
117
- {
118
- if (null == digitalCertificateRecord)
119
- {
120
- break;
121
- }
108
+ string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
109
123
- string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
110
+ // Export to a file, because the MSI API's require us to provide a file path on disk
111
+ string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
112
+ Directory.CreateDirectory(certPath);
113
+ certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
114
125
- // Export to a file, because the MSI API's require us to provide a file path on disk
126
- string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
127
- Directory.CreateDirectory(certPath);
128
- certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
115
+ using (FileStream fs = File.Create(certPath))
116
+ {
117
+ int bytesRead;
118
+ byte[] buffer = new byte[1024 * 4];
119
130
- using (FileStream fs = File.Create(certPath))
120
+ while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
121
{
132
- int bytesRead;
133
- byte[] buffer = new byte[1024 * 4];
134
-
135
- while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
136
- {
137
- fs.Write(buffer, 0, bytesRead);
138
- }
122
+ fs.Write(buffer, 0, bytesRead);
123
}
124
+ }
125
141
- // Add it to our "add to MsiDigitalCertificate" table dictionary
142
- Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
143
- digitalCertificateRow[0] = certificateId;
126
+ // Add it to our "add to MsiDigitalCertificate" table dictionary
127
+ Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
128
+ digitalCertificateRow[0] = certificateId;
129
145
- // Now set the file path on disk where this binary stream will be picked up at import time
146
- digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
130
+ // Now set the file path on disk where this binary stream will be picked up at import time
131
+ digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
132
148
- // Load the cert to get it's thumbprint
149
- X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
150
- X509Certificate2 cert2 = new X509Certificate2(cert);
133
+ // Load the cert to get it's thumbprint
134
+ X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
135
+ X509Certificate2 cert2 = new X509Certificate2(cert);
136
152
- certificates.Add(cert2.Thumbprint, certificateId);
153
- }
137
+ certificates.Add(cert2.Thumbprint, certificateId);
138
}
139
}
140
}
141
142
using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
143
{
160
- while (true)
144
+ foreach (Record mediaRecord in mediaView.Records)
145
{
162
- using (Record mediaRecord = mediaView.Fetch())
146
+ X509Certificate2 cert2 = null;
147
+ Row digitalSignatureRow = null;
148
+
149
+ string cabName = mediaRecord.GetString(4); // get the name of the cab
150
+ // If there is no cabinet or it's an internal cab, skip it.
151
+ if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
152
{
164
- if (null == mediaRecord)
165
- {
166
- break;
167
- }
153
+ continue;
154
+ }
155
169
- X509Certificate2 cert2 = null;
170
- Row digitalSignatureRow = null;
156
+ string cabId = mediaRecord.GetString(1); // get the ID of the cab
157
+ string cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName);
158
172
- string cabName = mediaRecord.GetString(4); // get the name of the cab
173
- // If there is no cabinet or it's an internal cab, skip it.
174
- if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
175
- {
176
- continue;
177
- }
159
+ // If the cabs aren't there, throw an error but continue to catch the other errors
160
+ if (!File.Exists(cabPath))
161
+ {
162
+ this.Messaging.Write(ErrorMessages.WixFileNotFound(cabPath));
163
+ continue;
164
+ }
165
179
- string cabId = mediaRecord.GetString(1); // get the ID of the cab
180
- string cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName);
166
+ try
167
+ {
168
+ // Get the certificate from the cab
169
+ X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
170
+ cert2 = new X509Certificate2(signedFileCert);
171
+ }
172
+ catch (System.Security.Cryptography.CryptographicException e)
173
+ {
174
+ uint HResult = unchecked((uint)Marshal.GetHRForException(e));
175
182
- // If the cabs aren't there, throw an error but continue to catch the other errors
183
- if (!File.Exists(cabPath))
176
+ // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
177
+ if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
178
{
185
- this.Messaging.Write(ErrorMessages.WixFileNotFound(cabPath));
179
+ foundUnsignedExternals = true;
180
continue;
181
}
182
189
- try
183
+ // todo: exactly which HRESULT corresponds to this issue?
184
+ // If it's one of these exact platforms, warn the user that it may be due to their OS.
185
+ if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
186
+ (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
187
{
191
- // Get the certificate from the cab
192
- X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
193
- cert2 = new X509Certificate2(signedFileCert);
188
+ this.Messaging.Write(ErrorMessages.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
189
}
195
- catch (System.Security.Cryptography.CryptographicException e)
190
+ else // otherwise, generic error
191
{
197
- uint HResult = unchecked((uint)Marshal.GetHRForException(e));
198
-
199
- // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
200
- if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
201
- {
202
- foundUnsignedExternals = true;
203
- continue;
204
- }
205
-
206
- // todo: exactly which HRESULT corresponds to this issue?
207
- // If it's one of these exact platforms, warn the user that it may be due to their OS.
208
- if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
209
- (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
210
- {
211
- this.Messaging.Write(ErrorMessages.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
212
- }
213
- else // otherwise, generic error
214
- {
215
- this.Messaging.Write(ErrorMessages.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
216
- }
192
+ this.Messaging.Write(ErrorMessages.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
193
}
194
+ }
195
219
- // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
220
- if (!certificates.ContainsKey(cert2.Thumbprint))
221
- {
222
- // generate a stable identifier
223
- string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
224
-
225
- // Add it to our "add to MsiDigitalCertificate" table dictionary
226
- Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
227
- digitalCertificateRow[0] = certificateGeneratedId;
228
-
229
- // Export to a file, because the MSI API's require us to provide a file path on disk
230
- string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
231
- Directory.CreateDirectory(certPath);
232
- certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
233
- File.Delete(certPath);
196
+ // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
197
+ if (!certificates.ContainsKey(cert2.Thumbprint))
198
+ {
199
+ // generate a stable identifier
200
+ string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
201
235
- using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
236
- {
237
- writer.Write(cert2.RawData);
238
- writer.Close();
239
- }
202
+ // Add it to our "add to MsiDigitalCertificate" table dictionary
203
+ Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
204
+ digitalCertificateRow[0] = certificateGeneratedId;
205
241
- // Now set the file path on disk where this binary stream will be picked up at import time
242
- digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
206
+ // Export to a file, because the MSI API's require us to provide a file path on disk
207
+ string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
208
+ Directory.CreateDirectory(certPath);
209
+ certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
210
+ File.Delete(certPath);
211
244
- certificates.Add(cert2.Thumbprint, certificateGeneratedId);
212
+ using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
213
+ {
214
+ writer.Write(cert2.RawData);
215
+ writer.Close();
216
}
217
247
- digitalSignatureRow = digitalSignatureTable.CreateRow(null);
218
+ // Now set the file path on disk where this binary stream will be picked up at import time
219
+ digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
220
249
- digitalSignatureRow[0] = "Media";
250
- digitalSignatureRow[1] = cabId;
251
- digitalSignatureRow[2] = certificates[cert2.Thumbprint];
221
+ certificates.Add(cert2.Thumbprint, certificateGeneratedId);
222
}
223
+
224
+ digitalSignatureRow = digitalSignatureTable.CreateRow(null);
225
+
226
+ digitalSignatureRow[0] = "Media";
227
+ digitalSignatureRow[1] = cabId;
228
+ digitalSignatureRow[2] = certificates[cert2.Thumbprint];
229
}
230
}
231
@@ -275,7 +251,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
251
252
certificates = null;
253
278
- // If we did find external cabs but none of them were signed, give a warning
254
+ // If we did find external cabs but not all of them were signed, give a warning
255
if (foundUnsignedExternals)
256
{
257
this.Messaging.Write(WarningMessages.ExternalCabsAreNotSigned(this.Context.InputFilePath));
src/WixToolset.Core.WindowsInstaller/Msi/View.cs
+79
-4
@@ -3,6 +3,8 @@
3
namespace WixToolset.Core.WindowsInstaller.Msi
4
{
5
using System;
6
+ using System.Collections;
7
+ using System.Collections.Generic;
8
using System.Globalization;
9
10
/// <summary>
@@ -109,6 +111,11 @@ namespace WixToolset.Core.WindowsInstaller.Msi
111
this.Handle = handle;
112
}
113
114
+ /// <summary>
115
+ /// Enumerator that automatically disposes of the retrieved Records.
116
+ /// </summary>
117
+ public IEnumerable<Record> Records => new ViewEnumerable(this);
118
+
119
/// <summary>
120
/// Executes a view with no customizable parameters.
121
/// </summary>
@@ -124,7 +131,7 @@ namespace WixToolset.Core.WindowsInstaller.Msi
131
/// <param name="record">Record containing parameters to be substituded into the view.</param>
132
public void Execute(Record record)
133
{
127
- int error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle);
134
+ var error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle);
135
if (0 != error)
136
{
137
throw new MsiException(error);
@@ -137,9 +144,7 @@ namespace WixToolset.Core.WindowsInstaller.Msi
144
/// <returns>Returns the fetched record; otherwise null.</returns>
145
public Record Fetch()
146
{
140
- uint recordHandle;
141
-
142
- int error = MsiInterop.MsiViewFetch(this.Handle, out recordHandle);
147
+ var error = MsiInterop.MsiViewFetch(this.Handle, out var recordHandle);
148
if (259 == error)
149
{
150
return null;
@@ -183,5 +188,75 @@ namespace WixToolset.Core.WindowsInstaller.Msi
188
189
return new Record(recordHandle);
190
}
191
+
192
+ private class ViewEnumerable : IEnumerable<Record>
193
+ {
194
+ private readonly View view;
195
+
196
+ public ViewEnumerable(View view) => this.view = view;
197
+
198
+ public IEnumerator<Record> GetEnumerator() => new ViewEnumerator(this.view);
199
+
200
+ IEnumerator IEnumerable.GetEnumerator() => new ViewEnumerator(this.view);
201
+ }
202
+
203
+ private class ViewEnumerator : IEnumerator<Record>
204
+ {
205
+ private readonly View view;
206
+ private readonly List<Record> records = new List<Record>();
207
+ private int position = -1;
208
+ private bool disposed;
209
+
210
+ public ViewEnumerator(View view) => this.view = view;
211
+
212
+ public Record Current => this.records[this.position];
213
+
214
+ object IEnumerator.Current => this.records[this.position];
215
+
216
+ public bool MoveNext()
217
+ {
218
+ if (this.position + 1 >= this.records.Count)
219
+ {
220
+ var record = this.view.Fetch();
221
+
222
+ if (record == null)
223
+ {
224
+ return false;
225
+ }
226
+
227
+ this.records.Add(record);
228
+ this.position = this.records.Count - 1;
229
+ }
230
+ else
231
+ {
232
+ ++this.position;
233
+ }
234
+
235
+ return true;
236
+ }
237
+
238
+ public void Reset() => this.position = -1;
239
+
240
+ public void Dispose()
241
+ {
242
+ this.Dispose(true);
243
+ }
244
+
245
+ protected virtual void Dispose(bool disposing)
246
+ {
247
+ if (!this.disposed)
248
+ {
249
+ if (disposing)
250
+ {
251
+ foreach (var record in this.records)
252
+ {
253
+ record.Dispose();
254
+ }
255
+ }
256
+
257
+ this.disposed = true;
258
+ }
259
+ }
260
+ }
261
}
262
}
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+154
-171
@@ -121,142 +121,125 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
121
// get the normal tables
122
using (var tablesView = this.Database.OpenExecuteView("SELECT * FROM _Tables"))
123
{
124
- while (true)
124
+ foreach (var tableRecord in tablesView.Records)
125
{
126
- using (var tableRecord = tablesView.Fetch())
127
- {
128
- if (null == tableRecord)
129
- {
130
- break;
131
- }
126
+ var tableName = tableRecord.GetString(1);
127
133
- var tableName = tableRecord.GetString(1);
128
+ using (var tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName)))
129
+ {
130
+ var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView);
131
+ var table = new Table(tableDefinition);
132
135
- using (var tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName)))
133
+ foreach (var rowRecord in tableView.Records)
134
{
137
- var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView);
138
- var table = new Table(tableDefinition);
135
+ var recordCount = rowRecord.GetFieldCount();
136
+ var row = table.CreateRow(output.SourceLineNumbers);
137
140
- while (true)
138
+ for (var i = 0; recordCount > i && row.Fields.Length > i; i++)
139
{
142
- using (var rowRecord = tableView.Fetch())
140
+ if (rowRecord.IsNull(i + 1))
141
{
144
- if (null == rowRecord)
142
+ if (!row.Fields[i].Column.Nullable)
143
{
146
- break;
144
+ // TODO: display an error for a null value in a non-nullable field OR
145
+ // display a warning and put an empty string in the value to let the compiler handle it
146
+ // (the second option is risky because the later code may make certain assumptions about
147
+ // the contents of a row value)
148
}
148
-
149
- var recordCount = rowRecord.GetFieldCount();
150
- var row = table.CreateRow(output.SourceLineNumbers);
151
-
152
- for (var i = 0; recordCount > i && row.Fields.Length > i; i++)
149
+ }
150
+ else
151
+ {
152
+ switch (row.Fields[i].Column.Type)
153
{
154
- if (rowRecord.IsNull(i + 1))
155
- {
156
- if (!row.Fields[i].Column.Nullable)
154
+ case ColumnType.Number:
155
+ var success = false;
156
+ var intValue = rowRecord.GetInteger(i + 1);
157
+ if (row.Fields[i].Column.IsLocalizable)
158
{
158
- // TODO: display an error for a null value in a non-nullable field OR
159
- // display a warning and put an empty string in the value to let the compiler handle it
160
- // (the second option is risky because the later code may make certain assumptions about
161
- // the contents of a row value)
159
+ success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
160
}
163
- }
164
- else
165
- {
166
- switch (row.Fields[i].Column.Type)
161
+ else
162
{
168
- case ColumnType.Number:
169
- var success = false;
170
- var intValue = rowRecord.GetInteger(i + 1);
171
- if (row.Fields[i].Column.IsLocalizable)
172
- {
173
- success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
174
- }
175
- else
176
- {
177
- success = row.BestEffortSetField(i, intValue);
178
- }
163
+ success = row.BestEffortSetField(i, intValue);
164
+ }
165
180
- if (!success)
181
- {
182
- this.Messaging.Write(WarningMessages.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name));
183
- }
184
- break;
185
- case ColumnType.Object:
186
- var sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";
166
+ if (!success)
167
+ {
168
+ this.Messaging.Write(WarningMessages.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name));
169
+ }
170
+ break;
171
+ case ColumnType.Object:
172
+ var sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";
173
188
- if (null != this.ExportBasePath)
189
- {
190
- var relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
191
- sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile);
174
+ if (null != this.ExportBasePath)
175
+ {
176
+ var relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
177
+ sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile);
178
193
- // ensure the parent directory exists
194
- System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
179
+ // ensure the parent directory exists
180
+ System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
181
196
- using (var fs = System.IO.File.Create(sourceFile))
197
- {
198
- int bytesRead;
199
- var buffer = new byte[512];
182
+ using (var fs = System.IO.File.Create(sourceFile))
183
+ {
184
+ int bytesRead;
185
+ var buffer = new byte[512];
186
201
- while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
202
- {
203
- fs.Write(buffer, 0, bytesRead);
204
- }
187
+ while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
188
+ {
189
+ fs.Write(buffer, 0, bytesRead);
190
}
206
-
207
- this.exportedFiles.Add(sourceFile);
191
}
192
210
- row[i] = sourceFile;
211
- break;
212
- default:
213
- var value = rowRecord.GetString(i + 1);
193
+ this.exportedFiles.Add(sourceFile);
194
+ }
195
215
- switch (row.Fields[i].Column.Category)
216
- {
196
+ row[i] = sourceFile;
197
+ break;
198
+ default:
199
+ var value = rowRecord.GetString(i + 1);
200
+
201
+ switch (row.Fields[i].Column.Category)
202
+ {
203
case ColumnCategory.Guid:
204
value = value.ToUpper(CultureInfo.InvariantCulture);
205
break;
220
- }
206
+ }
207
222
- // de-modularize
223
- if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
224
- {
225
- var modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");
208
+ // de-modularize
209
+ if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
210
+ {
211
+ var modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");
212
227
- if (null == modularizationGuid)
213
+ if (null == modularizationGuid)
214
+ {
215
+ var match = modularization.Match(value);
216
+ if (match.Success)
217
{
229
- var match = modularization.Match(value);
230
- if (match.Success)
231
- {
232
- modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
233
- }
218
+ modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
219
}
235
-
236
- value = modularization.Replace(value, String.Empty);
220
}
221
239
- // escape "$(" for the preprocessor
240
- value = value.Replace("$(", "$$(");
222
+ value = modularization.Replace(value, String.Empty);
223
+ }
224
242
- // escape things that look like wix variables
243
- var matches = Common.WixVariableRegex.Matches(value);
244
- for (var j = matches.Count - 1; 0 <= j; j--)
245
- {
246
- value = value.Insert(matches[j].Index, "!");
247
- }
225
+ // escape "$(" for the preprocessor
226
+ value = value.Replace("$(", "$$(");
227
249
- row[i] = value;
250
- break;
228
+ // escape things that look like wix variables
229
+ var matches = Common.WixVariableRegex.Matches(value);
230
+ for (var j = matches.Count - 1; 0 <= j; j--)
231
+ {
232
+ value = value.Insert(matches[j].Index, "!");
233
}
252
- }
234
+
235
+ row[i] = value;
236
+ break;
237
}
238
}
239
}
256
-
257
- output.Tables.Add(table);
240
}
241
242
+ output.Tables.Add(table);
243
}
244
}
245
}
@@ -634,82 +617,82 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
617
{
618
switch (table.Name)
619
{
637
- case "WixFile":
638
- case "MsiFileHash":
639
- ConnectTableToSection(table, fileSectionIdIndex, 0);
640
- break;
641
- case "MsiAssembly":
642
- case "MsiAssemblyName":
643
- ConnectTableToSection(table, componentSectionIdIndex, 0);
644
- break;
645
- case "MsiPackageCertificate":
646
- case "MsiPatchCertificate":
647
- ConnectTableToSection(table, digitalCertificateSectionIdIndex, 1);
648
- break;
649
- case "CreateFolder":
650
- case "FeatureComponents":
651
- case "MoveFile":
652
- case "ReserveCost":
653
- case "ODBCTranslator":
654
- ConnectTableToSection(table, componentSectionIdIndex, 1);
655
- break;
656
- case "TypeLib":
657
- ConnectTableToSection(table, componentSectionIdIndex, 2);
658
- break;
659
- case "Shortcut":
660
- case "Environment":
661
- ConnectTableToSection(table, componentSectionIdIndex, 3);
662
- break;
663
- case "RemoveRegistry":
664
- ConnectTableToSection(table, componentSectionIdIndex, 4);
665
- break;
666
- case "ServiceControl":
667
- ConnectTableToSection(table, componentSectionIdIndex, 5);
668
- break;
669
- case "IniFile":
670
- case "RemoveIniFile":
671
- ConnectTableToSection(table, componentSectionIdIndex, 7);
672
- break;
673
- case "AppId":
674
- ConnectTableToSection(table, appIdSectionIdIndex, 0);
675
- break;
676
- case "Condition":
677
- ConnectTableToSection(table, featureSectionIdIndex, 0);
678
- break;
679
- case "ODBCSourceAttribute":
680
- ConnectTableToSection(table, odbcDataSourceSectionIdIndex, 0);
681
- break;
682
- case "ODBCAttribute":
683
- ConnectTableToSection(table, odbcDriverSectionIdIndex, 0);
684
- break;
685
- case "AdminExecuteSequence":
686
- case "AdminUISequence":
687
- case "AdvtExecuteSequence":
688
- case "AdvtUISequence":
689
- case "InstallExecuteSequence":
690
- case "InstallUISequence":
691
- ConnectTableToSection(table, customActionSectionIdIndex, 0);
692
- break;
693
- case "LockPermissions":
694
- case "MsiLockPermissions":
695
- foreach (var row in table.Rows)
696
- {
697
- var lockObject = (string)row[0];
698
- var tableName = (string)row[1];
699
- switch (tableName)
620
+ case "WixFile":
621
+ case "MsiFileHash":
622
+ ConnectTableToSection(table, fileSectionIdIndex, 0);
623
+ break;
624
+ case "MsiAssembly":
625
+ case "MsiAssemblyName":
626
+ ConnectTableToSection(table, componentSectionIdIndex, 0);
627
+ break;
628
+ case "MsiPackageCertificate":
629
+ case "MsiPatchCertificate":
630
+ ConnectTableToSection(table, digitalCertificateSectionIdIndex, 1);
631
+ break;
632
+ case "CreateFolder":
633
+ case "FeatureComponents":
634
+ case "MoveFile":
635
+ case "ReserveCost":
636
+ case "ODBCTranslator":
637
+ ConnectTableToSection(table, componentSectionIdIndex, 1);
638
+ break;
639
+ case "TypeLib":
640
+ ConnectTableToSection(table, componentSectionIdIndex, 2);
641
+ break;
642
+ case "Shortcut":
643
+ case "Environment":
644
+ ConnectTableToSection(table, componentSectionIdIndex, 3);
645
+ break;
646
+ case "RemoveRegistry":
647
+ ConnectTableToSection(table, componentSectionIdIndex, 4);
648
+ break;
649
+ case "ServiceControl":
650
+ ConnectTableToSection(table, componentSectionIdIndex, 5);
651
+ break;
652
+ case "IniFile":
653
+ case "RemoveIniFile":
654
+ ConnectTableToSection(table, componentSectionIdIndex, 7);
655
+ break;
656
+ case "AppId":
657
+ ConnectTableToSection(table, appIdSectionIdIndex, 0);
658
+ break;
659
+ case "Condition":
660
+ ConnectTableToSection(table, featureSectionIdIndex, 0);
661
+ break;
662
+ case "ODBCSourceAttribute":
663
+ ConnectTableToSection(table, odbcDataSourceSectionIdIndex, 0);
664
+ break;
665
+ case "ODBCAttribute":
666
+ ConnectTableToSection(table, odbcDriverSectionIdIndex, 0);
667
+ break;
668
+ case "AdminExecuteSequence":
669
+ case "AdminUISequence":
670
+ case "AdvtExecuteSequence":
671
+ case "AdvtUISequence":
672
+ case "InstallExecuteSequence":
673
+ case "InstallUISequence":
674
+ ConnectTableToSection(table, customActionSectionIdIndex, 0);
675
+ break;
676
+ case "LockPermissions":
677
+ case "MsiLockPermissions":
678
+ foreach (var row in table.Rows)
679
{
701
- case "File":
702
- row.SectionId = (string)fileSectionIdIndex[lockObject];
703
- break;
704
- case "Registry":
705
- row.SectionId = (string)registrySectionIdIndex[lockObject];
706
- break;
707
- case "ServiceInstall":
708
- row.SectionId = (string)serviceInstallSectionIdIndex[lockObject];
709
- break;
680
+ var lockObject = (string)row[0];
681
+ var tableName = (string)row[1];
682
+ switch (tableName)
683
+ {
684
+ case "File":
685
+ row.SectionId = (string)fileSectionIdIndex[lockObject];
686
+ break;
687
+ case "Registry":
688
+ row.SectionId = (string)registrySectionIdIndex[lockObject];
689
+ break;
690
+ case "ServiceInstall":
691
+ row.SectionId = (string)serviceInstallSectionIdIndex[lockObject];
692
+ break;
693
+ }
694
}
711
- }
712
- break;
695
+ break;
696
}
697
}
698
src/WixToolset.Core.WindowsInstaller/Validator.cs
+5
-13
@@ -201,21 +201,13 @@ namespace WixToolset.Core.WindowsInstaller
201
List<string> actions = new List<string>();
202
using (View view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`"))
203
{
204
- while (true)
204
+ foreach (Record record in view.Records)
205
{
206
- using (Record record = view.Fetch())
207
- {
208
- if (null == record)
209
- {
210
- break;
211
- }
206
+ string action = record.GetString(1);
207
213
- string action = record.GetString(1);
214
-
215
- if ((this.SuppressedICEs == null || !this.SuppressedICEs.Contains(action)) && (this.ICEs == null || this.ICEs.Contains(action)))
216
- {
217
- actions.Add(action);
218
- }
208
+ if ((this.SuppressedICEs == null || !this.SuppressedICEs.Contains(action)) && (this.ICEs == null || this.ICEs.Contains(action)))
209
+ {
210
+ actions.Add(action);
211
}
212
}
213
}
src/WixToolset.Core/CommandLine/BuildCommand.cs
+1
-1
@@ -591,7 +591,7 @@ namespace WixToolset.Core.CommandLine
591
this.OutputType = Path.GetExtension(this.OutputFile);
592
}
593
594
- switch (this.OutputType.ToLowerInvariant())
594
+ switch (this.OutputType?.ToLowerInvariant())
595
{
596
case "bundle":
597
case ".exe":