aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Msi/Database.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Database.cs248
1 files changed, 0 insertions, 248 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
deleted file mode 100644
index 9f08a217..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
+++ /dev/null
@@ -1,248 +0,0 @@
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 WixToolset.Core.WindowsInstaller.Msi
4{
5 using System;
6 using System.Globalization;
7 using System.IO;
8 using System.Threading;
9
10 /// <summary>
11 /// Wrapper class for managing MSI API database handles.
12 /// </summary>
13 internal sealed class Database : MsiHandle
14 {
15 private const int STG_E_LOCKVIOLATION = unchecked((int)0x80030021);
16
17 /// <summary>
18 /// Constructor that opens an MSI database.
19 /// </summary>
20 /// <param name="path">Path to the database to be opened.</param>
21 /// <param name="type">Persist mode to use when opening the database.</param>
22 public Database(string path, OpenDatabase type)
23 {
24 int error = MsiInterop.MsiOpenDatabase(path, new IntPtr((int)type), out var handle);
25 if (0 != error)
26 {
27 throw new MsiException(error);
28 }
29 this.Handle = handle;
30 }
31
32 public void ApplyTransform(string transformFile)
33 {
34 // get the curret validation bits
35 TransformErrorConditions conditions = TransformErrorConditions.None;
36 using (SummaryInformation summaryInfo = new SummaryInformation(transformFile))
37 {
38 string value = summaryInfo.GetProperty((int)SummaryInformation.Transform.ValidationFlags);
39 try
40 {
41 int validationFlags = Int32.Parse(value, CultureInfo.InvariantCulture);
42 conditions = (TransformErrorConditions)(validationFlags & 0xffff);
43 }
44 catch (FormatException)
45 {
46 // fallback to default of None
47 }
48 }
49
50 this.ApplyTransform(transformFile, conditions);
51 }
52
53 /// <summary>
54 /// Applies a transform to this database.
55 /// </summary>
56 /// <param name="transformFile">Path to the transform file being applied.</param>
57 /// <param name="errorConditions">Specifies the error conditions that are to be suppressed.</param>
58 public void ApplyTransform(string transformFile, TransformErrorConditions errorConditions)
59 {
60 int error = MsiInterop.MsiDatabaseApplyTransform(this.Handle, transformFile, errorConditions);
61 if (0 != error)
62 {
63 throw new MsiException(error);
64 }
65 }
66
67 /// <summary>
68 /// Commits changes made to the database.
69 /// </summary>
70 public void Commit()
71 {
72 // Retry this call 3 times to deal with an MSI internal locking problem.
73 const int retryWait = 300;
74 const int retryLimit = 3;
75 int error = 0;
76
77 for (int i = 1; i <= retryLimit; ++i)
78 {
79 error = MsiInterop.MsiDatabaseCommit(this.Handle);
80
81 if (0 == error)
82 {
83 return;
84 }
85 else
86 {
87 MsiException exception = new MsiException(error);
88
89 // We need to see if the error code is contained in any of the strings in ErrorInfo.
90 // Join the array together and search for the error code to cover the string array.
91 if (!String.Join(", ", exception.ErrorInfo).Contains(STG_E_LOCKVIOLATION.ToString()))
92 {
93 break;
94 }
95
96 Console.Error.WriteLine(String.Format("Failed to create the database. Info: {0}. Retrying ({1} of {2})", String.Join(", ", exception.ErrorInfo), i, retryLimit));
97 Thread.Sleep(retryWait);
98 }
99 }
100
101 throw new MsiException(error);
102 }
103
104 /// <summary>
105 /// Creates and populates the summary information stream of an existing transform file.
106 /// </summary>
107 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
108 /// <param name="transformFile">The name of the generated transform file.</param>
109 /// <param name="errorConditions">Required error conditions that should be suppressed when the transform is applied.</param>
110 /// <param name="validations">Required when the transform is applied to a database;
111 /// shows which properties should be validated to verify that this transform can be applied to the database.</param>
112 public void CreateTransformSummaryInfo(Database referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations)
113 {
114 int error = MsiInterop.MsiCreateTransformSummaryInfo(this.Handle, referenceDatabase.Handle, transformFile, errorConditions, validations);
115 if (0 != error)
116 {
117 throw new MsiException(error);
118 }
119 }
120
121 /// <summary>
122 /// Imports an installer text archive table (idt file) into an open database.
123 /// </summary>
124 /// <param name="idtPath">Specifies the path to the file to import.</param>
125 /// <exception cref="WixInvalidIdtException">Attempted to import an IDT file with an invalid format or unsupported data.</exception>
126 /// <exception cref="MsiException">Another error occured while importing the IDT file.</exception>
127 public void Import(string idtPath)
128 {
129 string folderPath = Path.GetFullPath(Path.GetDirectoryName(idtPath));
130 string fileName = Path.GetFileName(idtPath);
131
132 int error = MsiInterop.MsiDatabaseImport(this.Handle, folderPath, fileName);
133 if (1627 == error) // ERROR_FUNCTION_FAILED
134 {
135 throw new WixInvalidIdtException(idtPath);
136 }
137 else if (0 != error)
138 {
139 throw new MsiException(error);
140 }
141 }
142
143 /// <summary>
144 /// Exports an installer table from an open database to a text archive file (idt file).
145 /// </summary>
146 /// <param name="tableName">Specifies the name of the table to export.</param>
147 /// <param name="folderPath">Specifies the name of the folder that contains archive files. If null or empty string, uses current directory.</param>
148 /// <param name="fileName">Specifies the name of the exported table archive file.</param>
149 public void Export(string tableName, string folderPath, string fileName)
150 {
151 if (String.IsNullOrEmpty(folderPath))
152 {
153 folderPath = Environment.CurrentDirectory;
154 }
155
156 int error = MsiInterop.MsiDatabaseExport(this.Handle, tableName, folderPath, fileName);
157 if (0 != error)
158 {
159 throw new MsiException(error);
160 }
161 }
162
163 /// <summary>
164 /// Creates a transform that, when applied to the reference database, results in this database.
165 /// </summary>
166 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
167 /// <param name="transformFile">The name of the generated transform file. This is optional.</param>
168 /// <returns>true if a transform is generated; false if a transform is not generated because
169 /// there are no differences between the two databases.</returns>
170 public bool GenerateTransform(Database referenceDatabase, string transformFile)
171 {
172 int error = MsiInterop.MsiDatabaseGenerateTransform(this.Handle, referenceDatabase.Handle, transformFile, 0, 0);
173 if (0 != error && 0xE8 != error) // ERROR_NO_DATA(0xE8) means no differences were found
174 {
175 throw new MsiException(error);
176 }
177
178 return (0xE8 != error);
179 }
180
181 /// <summary>
182 /// Merges two databases together.
183 /// </summary>
184 /// <param name="mergeDatabase">The database to merge into the base database.</param>
185 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
186 public void Merge(Database mergeDatabase, string tableName)
187 {
188 int error = MsiInterop.MsiDatabaseMerge(this.Handle, mergeDatabase.Handle, tableName);
189 if (0 != error)
190 {
191 throw new MsiException(error);
192 }
193 }
194
195 /// <summary>
196 /// Prepares a database query and creates a <see cref="View">View</see> object.
197 /// </summary>
198 /// <param name="query">Specifies a SQL query string for querying the database.</param>
199 /// <returns>A view object is returned if the query was successful.</returns>
200 public View OpenView(string query)
201 {
202 return new View(this, query);
203 }
204
205 /// <summary>
206 /// Prepares and executes a database query and creates a <see cref="View">View</see> object.
207 /// </summary>
208 /// <param name="query">Specifies a SQL query string for querying the database.</param>
209 /// <returns>A view object is returned if the query was successful.</returns>
210 public View OpenExecuteView(string query)
211 {
212 View view = new View(this, query);
213
214 view.Execute();
215 return view;
216 }
217
218 /// <summary>
219 /// Verifies the existence or absence of a table.
220 /// </summary>
221 /// <param name="tableName">Table name to to verify the existence of.</param>
222 /// <returns>Returns true if the table exists, false if it does not.</returns>
223 public bool TableExists(string tableName)
224 {
225 int result = MsiInterop.MsiDatabaseIsTablePersistent(this.Handle, tableName);
226 return MsiInterop.MSICONDITIONTRUE == result;
227 }
228
229 /// <summary>
230 /// Returns a <see cref="Record">Record</see> containing the names of all the primary
231 /// key columns for a specified table.
232 /// </summary>
233 /// <param name="tableName">Specifies the name of the table from which to obtain
234 /// primary key names.</param>
235 /// <returns>Returns a <see cref="Record">Record</see> containing the names of all the
236 /// primary key columns for a specified table.</returns>
237 public Record PrimaryKeys(string tableName)
238 {
239 var error = MsiInterop.MsiDatabaseGetPrimaryKeys(this.Handle, tableName, out var recordHandle);
240 if (error != 0)
241 {
242 throw new MsiException(error);
243 }
244
245 return new Record(recordHandle);
246 }
247 }
248}