Skip to content

Commit 12d94a3

Browse files
committed
Improve error message when MSI is read-only
Fixes 9113
1 parent d2ba0da commit 12d94a3

File tree

9 files changed

+82
-13
lines changed

9 files changed

+82
-13
lines changed

src/api/wix/WixToolset.Data/ErrorMessages.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,11 +1561,6 @@ public static Message NoUniqueActionSequenceNumber2(SourceLineNumber sourceLineN
15611561
return Message(sourceLineNumbers, Ids.NoUniqueActionSequenceNumber2, "The location of the sequenced action related to previous error.");
15621562
}
15631563

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-
15691564
public static Message OrderingReferenceLoopDetected(SourceLineNumber sourceLineNumbers, string loopList)
15701565
{
15711566
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 @@ public enum Ids
24792474
InvalidKeyColumn = 220,
24802475
CollidingModularizationTypes = 221,
24812476
CubeFileNotFound = 222,
2482-
OpenDatabaseFailed = 223,
24832477
OutputTypeMismatch = 224,
24842478
RealTableMissingPrimaryKeyColumn = 225,
24852479
IllegalColumnName = 226,

src/wix/WixToolset.Core.Native/Msi/Database.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public Database(string path, OpenDatabase type)
2525
{
2626
throw new MsiException(error);
2727
}
28+
2829
this.Handle = handle;
2930
}
3031

src/wix/WixToolset.Core.Native/Msi/MsiException.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ public sealed class MsiException : Win32Exception
1717
/// <param name="error">The error code from the MsiXxx() function call.</param>
1818
public MsiException(int error) : base(error)
1919
{
20-
IntPtr handle = MsiInterop.MsiGetLastErrorRecord();
20+
var handle = MsiInterop.MsiGetLastErrorRecord();
2121
if (IntPtr.Zero != handle)
2222
{
23-
using (Record record = new Record(handle))
23+
using (var record = new Record(handle))
2424
{
2525
this.MsiError = record.GetInteger(1);
2626

27-
int errorInfoCount = record.GetFieldCount() - 1;
27+
var errorInfoCount = record.GetFieldCount() - 1;
2828
this.ErrorInfo = new string[errorInfoCount];
29-
for (int i = 0; i < errorInfoCount; ++i)
29+
for (var i = 0; i < errorInfoCount; ++i)
3030
{
3131
this.ErrorInfo[i] = record.GetString(i + 2);
3232
}

src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,11 @@ public void Execute()
409409
this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false);
410410
this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true);
411411

412+
if (this.Messaging.EncounteredError)
413+
{
414+
return;
415+
}
416+
412417
// make sure the directory exists
413418
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
414419

src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@ public void Execute()
115115
}
116116
catch (IOException e)
117117
{
118-
// TODO: this error message doesn't seem specific enough
119-
throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e);
118+
this.Messaging.Write(WindowsInstallerBackendErrors.OpenDatabaseFailed(this.OutputPath, e.Message));
119+
}
120+
catch (MsiException e)
121+
{
122+
this.Messaging.Write(WindowsInstallerBackendErrors.OpenDatabaseFailed(this.OutputPath, e.Message));
120123
}
121124
}
122125

src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public WindowsInstallerData Execute()
104104
{
105105
if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
106106
{
107-
throw new WixException(ErrorMessages.OpenDatabaseFailed(this.DatabasePath));
107+
throw new WixException(WindowsInstallerBackendErrors.OpenDatabaseFailed(this.DatabasePath, e.Message));
108108
}
109109

110110
throw;

src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,23 @@ public WindowsInstallerData Execute()
8282
// Bind the schema msi.
8383
this.GenerateDatabase(schemaData);
8484

85+
if (this.Messaging.EncounteredError)
86+
{
87+
return transform;
88+
}
89+
8590
var transformViewTable = this.OpenTransformViewForAddedAndModifiedRows(schemaDatabasePath);
8691

8792
var addedRows = this.CreatePlaceholdersForModifiedRowsAndIndexAddedRows(schemaData, transformViewTable);
8893

8994
// Re-bind the schema output with the placeholder rows over top the original schema database.
9095
this.GenerateDatabase(schemaData);
9196

97+
if (this.Messaging.EncounteredError)
98+
{
99+
return transform;
100+
}
101+
92102
this.PopulateTransformFromView(schemaDatabasePath, transform, transformViewTable, addedRows);
93103

94104
return transform;

src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace WixToolset.Core.WindowsInstaller
77

88
internal static class WindowsInstallerBackendErrors
99
{
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+
1015
public static Message CannotLoadWixoutAsTransform(SourceLineNumber sourceLineNumbers, Exception exception)
1116
{
1217
var additionalDetail = exception == null ? String.Empty : ", detail: " + exception.Message;
@@ -51,6 +56,8 @@ private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string
5156

5257
public enum Ids
5358
{
59+
OpenDatabaseFailed = 223,
60+
5461
CannotLoadWixoutAsTransform = 7500,
5562
InvalidModuleVersion = 7501,
5663
ExceededMaximumAllowedComponentsInMsi = 7502,

src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,55 @@ public void CanBuildSingleFile()
5555
}
5656
}
5757

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+
58107
[Fact]
59108
public void CannotBuildMissingFile()
60109
{

0 commit comments

Comments
 (0)