aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2025-11-02 10:48:52 -0800
committerRob Mensching <rob@firegiant.com>2025-11-04 10:22:42 -0800
commitcf15a49da99429ef3bdce564ecb7a7a94198ead4 (patch)
tree37a50d9ca0fedb857d6198594485d9bb89e8fcc0 /src
parentd2ba0da55725f2908b67e1470afc7cfd71cb3d1f (diff)
downloadwix-cf15a49da99429ef3bdce564ecb7a7a94198ead4.tar.gz
wix-cf15a49da99429ef3bdce564ecb7a7a94198ead4.tar.bz2
wix-cf15a49da99429ef3bdce564ecb7a7a94198ead4.zip
Improve error message when MSI is read-only
Fixes 9113
Diffstat (limited to 'src')
-rw-r--r--src/api/wix/WixToolset.Data/ErrorMessages.cs6
-rw-r--r--src/wix/WixToolset.Core.Native/Msi/Database.cs1
-rw-r--r--src/wix/WixToolset.Core.Native/Msi/MsiException.cs8
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs5
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs7
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs10
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs7
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs49
9 files changed, 82 insertions, 13 deletions
diff --git a/src/api/wix/WixToolset.Data/ErrorMessages.cs b/src/api/wix/WixToolset.Data/ErrorMessages.cs
index 1d02ffd0..4052917c 100644
--- a/src/api/wix/WixToolset.Data/ErrorMessages.cs
+++ b/src/api/wix/WixToolset.Data/ErrorMessages.cs
@@ -1561,11 +1561,6 @@ namespace WixToolset.Data
1561 return Message(sourceLineNumbers, Ids.NoUniqueActionSequenceNumber2, "The location of the sequenced action related to previous error."); 1561 return Message(sourceLineNumbers, Ids.NoUniqueActionSequenceNumber2, "The location of the sequenced action related to previous error.");
1562 } 1562 }
1563 1563
1564 public static Message OpenDatabaseFailed(string databaseFile)
1565 {
1566 return Message(null, Ids.OpenDatabaseFailed, "Failed to open database '{0}'. Ensure it is a valid database, and it is not open by another process.", databaseFile);
1567 }
1568
1569 public static Message OrderingReferenceLoopDetected(SourceLineNumber sourceLineNumbers, string loopList) 1564 public static Message OrderingReferenceLoopDetected(SourceLineNumber sourceLineNumbers, string loopList)
1570 { 1565 {
1571 return Message(sourceLineNumbers, Ids.OrderingReferenceLoopDetected, "A circular reference of ordering dependencies was detected. The infinite loop includes: {0}. Ordering dependency references must form a directed acyclic graph.", loopList); 1566 return Message(sourceLineNumbers, Ids.OrderingReferenceLoopDetected, "A circular reference of ordering dependencies was detected. The infinite loop includes: {0}. Ordering dependency references must form a directed acyclic graph.", loopList);
@@ -2479,7 +2474,6 @@ namespace WixToolset.Data
2479 InvalidKeyColumn = 220, 2474 InvalidKeyColumn = 220,
2480 CollidingModularizationTypes = 221, 2475 CollidingModularizationTypes = 221,
2481 CubeFileNotFound = 222, 2476 CubeFileNotFound = 222,
2482 OpenDatabaseFailed = 223,
2483 OutputTypeMismatch = 224, 2477 OutputTypeMismatch = 224,
2484 RealTableMissingPrimaryKeyColumn = 225, 2478 RealTableMissingPrimaryKeyColumn = 225,
2485 IllegalColumnName = 226, 2479 IllegalColumnName = 226,
diff --git a/src/wix/WixToolset.Core.Native/Msi/Database.cs b/src/wix/WixToolset.Core.Native/Msi/Database.cs
index 18e5066d..ff966302 100644
--- a/src/wix/WixToolset.Core.Native/Msi/Database.cs
+++ b/src/wix/WixToolset.Core.Native/Msi/Database.cs
@@ -25,6 +25,7 @@ namespace WixToolset.Core.Native.Msi
25 { 25 {
26 throw new MsiException(error); 26 throw new MsiException(error);
27 } 27 }
28
28 this.Handle = handle; 29 this.Handle = handle;
29 } 30 }
30 31
diff --git a/src/wix/WixToolset.Core.Native/Msi/MsiException.cs b/src/wix/WixToolset.Core.Native/Msi/MsiException.cs
index 218d2a7c..fba2df78 100644
--- a/src/wix/WixToolset.Core.Native/Msi/MsiException.cs
+++ b/src/wix/WixToolset.Core.Native/Msi/MsiException.cs
@@ -17,16 +17,16 @@ namespace WixToolset.Core.Native.Msi
17 /// <param name="error">The error code from the MsiXxx() function call.</param> 17 /// <param name="error">The error code from the MsiXxx() function call.</param>
18 public MsiException(int error) : base(error) 18 public MsiException(int error) : base(error)
19 { 19 {
20 IntPtr handle = MsiInterop.MsiGetLastErrorRecord(); 20 var handle = MsiInterop.MsiGetLastErrorRecord();
21 if (IntPtr.Zero != handle) 21 if (IntPtr.Zero != handle)
22 { 22 {
23 using (Record record = new Record(handle)) 23 using (var record = new Record(handle))
24 { 24 {
25 this.MsiError = record.GetInteger(1); 25 this.MsiError = record.GetInteger(1);
26 26
27 int errorInfoCount = record.GetFieldCount() - 1; 27 var errorInfoCount = record.GetFieldCount() - 1;
28 this.ErrorInfo = new string[errorInfoCount]; 28 this.ErrorInfo = new string[errorInfoCount];
29 for (int i = 0; i < errorInfoCount; ++i) 29 for (var i = 0; i < errorInfoCount; ++i)
30 { 30 {
31 this.ErrorInfo[i] = record.GetString(i + 2); 31 this.ErrorInfo[i] = record.GetString(i + 2);
32 } 32 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index b37c7b95..e96df21f 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -409,6 +409,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
409 this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false); 409 this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false);
410 this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true); 410 this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true);
411 411
412 if (this.Messaging.EncounteredError)
413 {
414 return;
415 }
416
412 // make sure the directory exists 417 // make sure the directory exists
413 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); 418 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
414 419
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index 361f797d..3d831577 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -115,8 +115,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
115 } 115 }
116 catch (IOException e) 116 catch (IOException e)
117 { 117 {
118 // TODO: this error message doesn't seem specific enough 118 this.Messaging.Write(WindowsInstallerBackendErrors.OpenDatabaseFailed(this.OutputPath, e.Message));
119 throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e); 119 }
120 catch (MsiException e)
121 {
122 this.Messaging.Write(WindowsInstallerBackendErrors.OpenDatabaseFailed(this.OutputPath, e.Message));
120 } 123 }
121 } 124 }
122 125
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index cfa53269..ef02f5d1 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -104,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
104 { 104 {
105 if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED 105 if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
106 { 106 {
107 throw new WixException(ErrorMessages.OpenDatabaseFailed(this.DatabasePath)); 107 throw new WixException(WindowsInstallerBackendErrors.OpenDatabaseFailed(this.DatabasePath, e.Message));
108 } 108 }
109 109
110 throw; 110 throw;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
index 8846739a..aad3d34d 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
@@ -82,6 +82,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
82 // Bind the schema msi. 82 // Bind the schema msi.
83 this.GenerateDatabase(schemaData); 83 this.GenerateDatabase(schemaData);
84 84
85 if (this.Messaging.EncounteredError)
86 {
87 return transform;
88 }
89
85 var transformViewTable = this.OpenTransformViewForAddedAndModifiedRows(schemaDatabasePath); 90 var transformViewTable = this.OpenTransformViewForAddedAndModifiedRows(schemaDatabasePath);
86 91
87 var addedRows = this.CreatePlaceholdersForModifiedRowsAndIndexAddedRows(schemaData, transformViewTable); 92 var addedRows = this.CreatePlaceholdersForModifiedRowsAndIndexAddedRows(schemaData, transformViewTable);
@@ -89,6 +94,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
89 // Re-bind the schema output with the placeholder rows over top the original schema database. 94 // Re-bind the schema output with the placeholder rows over top the original schema database.
90 this.GenerateDatabase(schemaData); 95 this.GenerateDatabase(schemaData);
91 96
97 if (this.Messaging.EncounteredError)
98 {
99 return transform;
100 }
101
92 this.PopulateTransformFromView(schemaDatabasePath, transform, transformViewTable, addedRows); 102 this.PopulateTransformFromView(schemaDatabasePath, transform, transformViewTable, addedRows);
93 103
94 return transform; 104 return transform;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
index dbdbae0b..756bb5e4 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
@@ -7,6 +7,11 @@ namespace WixToolset.Core.WindowsInstaller
7 7
8 internal static class WindowsInstallerBackendErrors 8 internal static class WindowsInstallerBackendErrors
9 { 9 {
10 public static Message OpenDatabaseFailed(string databaseFile, string error)
11 {
12 return Message(null, Ids.OpenDatabaseFailed, "Failed to open database '{0}'. Ensure it is a valid database, is writable, and it is not open by another process. {1}", databaseFile, error);
13 }
14
10 public static Message CannotLoadWixoutAsTransform(SourceLineNumber sourceLineNumbers, Exception exception) 15 public static Message CannotLoadWixoutAsTransform(SourceLineNumber sourceLineNumbers, Exception exception)
11 { 16 {
12 var additionalDetail = exception == null ? String.Empty : ", detail: " + exception.Message; 17 var additionalDetail = exception == null ? String.Empty : ", detail: " + exception.Message;
@@ -51,6 +56,8 @@ namespace WixToolset.Core.WindowsInstaller
51 56
52 public enum Ids 57 public enum Ids
53 { 58 {
59 OpenDatabaseFailed = 223,
60
54 CannotLoadWixoutAsTransform = 7500, 61 CannotLoadWixoutAsTransform = 7500,
55 InvalidModuleVersion = 7501, 62 InvalidModuleVersion = 7501,
56 ExceededMaximumAllowedComponentsInMsi = 7502, 63 ExceededMaximumAllowedComponentsInMsi = 7502,
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
index 7f5dfcd0..7928b809 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs
@@ -56,6 +56,55 @@ namespace WixToolsetTest.CoreIntegration
56 } 56 }
57 57
58 [Fact] 58 [Fact]
59 public void CannotBuildReadOnlyMsi()
60 {
61 var folder = TestData.Get(@"TestData", "AllUsers");
62
63 using (var fs = new DisposableFileSystem())
64 {
65 var baseFolder = fs.GetFolder();
66 var intermediateFolder = Path.Combine(baseFolder, "obj");
67 var binFolder = Path.Combine(baseFolder, "bin");
68 var msiPath = Path.Combine(binFolder, "test.msi");
69
70 var msiFile = new FileInfo(msiPath);
71
72 try
73 {
74 msiFile.Directory.Create();
75
76 using (var stream = msiFile.CreateText())
77 {
78 stream.WriteLine("This is a read-only file.");
79 }
80
81 msiFile.IsReadOnly = true;
82
83 var result = WixRunner.Execute(
84 [
85 "build",
86 Path.Combine(folder, "PerMachine.wxs"),
87 "-bindpath", folder,
88 "-intermediateFolder", intermediateFolder,
89 "-o", msiPath
90 ], out var messages);
91 Assert.Equal(223, result);
92
93 // Note the use of substring in the message below. The error message detail may vary depending on whether extended
94 // error information is available on the system.
95 WixAssert.CompareLineByLine(
96 [
97 @"223 Error - Failed to open database '<baseFolder>\bin\test.msi'. Ensure it is a valid database, is writable, and it is not open by another process. ",
98 ], [.. messages.Select(m => $"{m.Id} {m.Level} - {m.ToString().Replace(folder, "<folder>").Replace(baseFolder, "<baseFolder>").Substring(0, 136)}")]);
99 }
100 finally
101 {
102 msiFile.IsReadOnly = false;
103 }
104 }
105 }
106
107 [Fact]
59 public void CannotBuildMissingFile() 108 public void CannotBuildMissingFile()
60 { 109 {
61 var folder = TestData.Get(@"TestData\SingleFile"); 110 var folder = TestData.Get(@"TestData\SingleFile");