aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Msi
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Msi')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Database.cs303
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs363
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs697
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs78
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs116
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Record.cs182
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Session.cs45
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs245
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/View.cs189
9 files changed, 2218 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
new file mode 100644
index 00000000..801ebdde
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
@@ -0,0 +1,303 @@
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.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using System.Globalization;
8 using System.IO;
9 using System.Text;
10 using System.Threading;
11 using WixToolset.Data;
12 using WixToolset.Core.Native;
13
14 /// <summary>
15 /// Wrapper class for managing MSI API database handles.
16 /// </summary>
17 internal sealed class Database : MsiHandle
18 {
19 private const int STG_E_LOCKVIOLATION = unchecked((int)0x80030021);
20
21 /// <summary>
22 /// Constructor that opens an MSI database.
23 /// </summary>
24 /// <param name="path">Path to the database to be opened.</param>
25 /// <param name="type">Persist mode to use when opening the database.</param>
26 public Database(string path, OpenDatabase type)
27 {
28 uint handle = 0;
29 int error = MsiInterop.MsiOpenDatabase(path, new IntPtr((int)type), out handle);
30 if (0 != error)
31 {
32 throw new MsiException(error);
33 }
34 this.Handle = handle;
35 }
36
37 public void ApplyTransform(string transformFile)
38 {
39 // get the curret validation bits
40 TransformErrorConditions conditions = TransformErrorConditions.None;
41 using (SummaryInformation summaryInfo = new SummaryInformation(transformFile))
42 {
43 string value = summaryInfo.GetProperty((int)SummaryInformation.Transform.ValidationFlags);
44 try
45 {
46 int validationFlags = Int32.Parse(value, CultureInfo.InvariantCulture);
47 conditions = (TransformErrorConditions)(validationFlags & 0xffff);
48 }
49 catch (FormatException)
50 {
51 // fallback to default of None
52 }
53 }
54
55 this.ApplyTransform(transformFile, conditions);
56 }
57
58 /// <summary>
59 /// Applies a transform to this database.
60 /// </summary>
61 /// <param name="transformFile">Path to the transform file being applied.</param>
62 /// <param name="errorConditions">Specifies the error conditions that are to be suppressed.</param>
63 public void ApplyTransform(string transformFile, TransformErrorConditions errorConditions)
64 {
65 int error = MsiInterop.MsiDatabaseApplyTransform(this.Handle, transformFile, errorConditions);
66 if (0 != error)
67 {
68 throw new MsiException(error);
69 }
70 }
71
72 /// <summary>
73 /// Commits changes made to the database.
74 /// </summary>
75 public void Commit()
76 {
77 // Retry this call 3 times to deal with an MSI internal locking problem.
78 const int retryWait = 300;
79 const int retryLimit = 3;
80 int error = 0;
81
82 for (int i = 1; i <= retryLimit; ++i)
83 {
84 error = MsiInterop.MsiDatabaseCommit(this.Handle);
85
86 if (0 == error)
87 {
88 return;
89 }
90 else
91 {
92 MsiException exception = new MsiException(error);
93
94 // We need to see if the error code is contained in any of the strings in ErrorInfo.
95 // Join the array together and search for the error code to cover the string array.
96 if (!String.Join(", ", exception.ErrorInfo).Contains(STG_E_LOCKVIOLATION.ToString()))
97 {
98 break;
99 }
100
101 Console.Error.WriteLine(String.Format("Failed to create the database. Info: {0}. Retrying ({1} of {2})", String.Join(", ", exception.ErrorInfo), i, retryLimit));
102 Thread.Sleep(retryWait);
103 }
104 }
105
106 throw new MsiException(error);
107 }
108
109 /// <summary>
110 /// Creates and populates the summary information stream of an existing transform file.
111 /// </summary>
112 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
113 /// <param name="transformFile">The name of the generated transform file.</param>
114 /// <param name="errorConditions">Required error conditions that should be suppressed when the transform is applied.</param>
115 /// <param name="validations">Required when the transform is applied to a database;
116 /// shows which properties should be validated to verify that this transform can be applied to the database.</param>
117 public void CreateTransformSummaryInfo(Database referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations)
118 {
119 int error = MsiInterop.MsiCreateTransformSummaryInfo(this.Handle, referenceDatabase.Handle, transformFile, errorConditions, validations);
120 if (0 != error)
121 {
122 throw new MsiException(error);
123 }
124 }
125
126 /// <summary>
127 /// Imports an installer text archive table (idt file) into an open database.
128 /// </summary>
129 /// <param name="idtPath">Specifies the path to the file to import.</param>
130 /// <exception cref="WixInvalidIdtException">Attempted to import an IDT file with an invalid format or unsupported data.</exception>
131 /// <exception cref="MsiException">Another error occured while importing the IDT file.</exception>
132 public void Import(string idtPath)
133 {
134 string folderPath = Path.GetFullPath(Path.GetDirectoryName(idtPath));
135 string fileName = Path.GetFileName(idtPath);
136
137 int error = MsiInterop.MsiDatabaseImport(this.Handle, folderPath, fileName);
138 if (1627 == error) // ERROR_FUNCTION_FAILED
139 {
140 throw new WixInvalidIdtException(idtPath);
141 }
142 else if (0 != error)
143 {
144 throw new MsiException(error);
145 }
146 }
147
148 /// <summary>
149 /// Exports an installer table from an open database to a text archive file (idt file).
150 /// </summary>
151 /// <param name="tableName">Specifies the name of the table to export.</param>
152 /// <param name="folderPath">Specifies the name of the folder that contains archive files. If null or empty string, uses current directory.</param>
153 /// <param name="fileName">Specifies the name of the exported table archive file.</param>
154 public void Export(string tableName, string folderPath, string fileName)
155 {
156 if (null == folderPath || 0 == folderPath.Length)
157 {
158 folderPath = System.Environment.CurrentDirectory;
159 }
160
161 int error = MsiInterop.MsiDatabaseExport(this.Handle, tableName, folderPath, fileName);
162 if (0 != error)
163 {
164 throw new MsiException(error);
165 }
166 }
167
168 /// <summary>
169 /// Creates a transform that, when applied to the reference database, results in this database.
170 /// </summary>
171 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
172 /// <param name="transformFile">The name of the generated transform file. This is optional.</param>
173 /// <returns>true if a transform is generated; false if a transform is not generated because
174 /// there are no differences between the two databases.</returns>
175 public bool GenerateTransform(Database referenceDatabase, string transformFile)
176 {
177 int error = MsiInterop.MsiDatabaseGenerateTransform(this.Handle, referenceDatabase.Handle, transformFile, 0, 0);
178 if (0 != error && 0xE8 != error) // ERROR_NO_DATA(0xE8) means no differences were found
179 {
180 throw new MsiException(error);
181 }
182
183 return (0xE8 != error);
184 }
185
186 /// <summary>
187 /// Merges two databases together.
188 /// </summary>
189 /// <param name="mergeDatabase">The database to merge into the base database.</param>
190 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
191 public void Merge(Database mergeDatabase, string tableName)
192 {
193 int error = MsiInterop.MsiDatabaseMerge(this.Handle, mergeDatabase.Handle, tableName);
194 if (0 != error)
195 {
196 throw new MsiException(error);
197 }
198 }
199
200 /// <summary>
201 /// Prepares a database query and creates a <see cref="View">View</see> object.
202 /// </summary>
203 /// <param name="query">Specifies a SQL query string for querying the database.</param>
204 /// <returns>A view object is returned if the query was successful.</returns>
205 public View OpenView(string query)
206 {
207 return new View(this, query);
208 }
209
210 /// <summary>
211 /// Prepares and executes a database query and creates a <see cref="View">View</see> object.
212 /// </summary>
213 /// <param name="query">Specifies a SQL query string for querying the database.</param>
214 /// <returns>A view object is returned if the query was successful.</returns>
215 public View OpenExecuteView(string query)
216 {
217 View view = new View(this, query);
218
219 view.Execute();
220 return view;
221 }
222
223 /// <summary>
224 /// Verifies the existence or absence of a table.
225 /// </summary>
226 /// <param name="tableName">Table name to to verify the existence of.</param>
227 /// <returns>Returns true if the table exists, false if it does not.</returns>
228 public bool TableExists(string tableName)
229 {
230 int result = MsiInterop.MsiDatabaseIsTablePersistent(this.Handle, tableName);
231 return MsiInterop.MSICONDITIONTRUE == result;
232 }
233
234 /// <summary>
235 /// Returns a <see cref="Record">Record</see> containing the names of all the primary
236 /// key columns for a specified table.
237 /// </summary>
238 /// <param name="tableName">Specifies the name of the table from which to obtain
239 /// primary key names.</param>
240 /// <returns>Returns a <see cref="Record">Record</see> containing the names of all the
241 /// primary key columns for a specified table.</returns>
242 public Record PrimaryKeys(string tableName)
243 {
244 uint recordHandle;
245 int error = MsiInterop.MsiDatabaseGetPrimaryKeys(this.Handle, tableName, out recordHandle);
246 if (0 != error)
247 {
248 throw new MsiException(error);
249 }
250
251 return new Record(recordHandle);
252 }
253
254 /// <summary>
255 /// Imports a table into the database.
256 /// </summary>
257 /// <param name="codepage">Codepage of the database to import table to.</param>
258 /// <param name="table">Table to import into database.</param>
259 /// <param name="baseDirectory">The base directory where intermediate files are created.</param>
260 /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param>
261 public void ImportTable(int codepage, Table table, string baseDirectory, bool keepAddedColumns)
262 {
263 // write out the table to an IDT file
264 string idtPath = Path.Combine(baseDirectory, String.Concat(table.Name, ".idt"));
265 Encoding encoding;
266
267 // If UTF8 encoding, use the UTF8-specific constructor to avoid writing
268 // the byte order mark at the beginning of the file
269 if (Encoding.UTF8.CodePage == codepage)
270 {
271 encoding = new UTF8Encoding(false, true);
272 }
273 else
274 {
275 if (0 == codepage)
276 {
277 codepage = Encoding.ASCII.CodePage;
278 }
279
280 encoding = Encoding.GetEncoding(codepage, new EncoderExceptionFallback(), new DecoderExceptionFallback());
281 }
282
283 using (StreamWriter idtWriter = new StreamWriter(idtPath, false, encoding))
284 {
285 table.ToIdtDefinition(idtWriter, keepAddedColumns);
286 }
287
288 // try to import the table into the MSI
289 try
290 {
291 this.Import(idtPath);
292 }
293 catch (WixInvalidIdtException)
294 {
295 table.ValidateRows();
296
297 // If ValidateRows finds anything it doesn't like, it throws. Otherwise, we'll
298 // throw WixInvalidIdtException here which is caught in light and turns off tidy.
299 throw new WixInvalidIdtException(idtPath, table.Name);
300 }
301 }
302 }
303}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
new file mode 100644
index 00000000..f8bce602
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
@@ -0,0 +1,363 @@
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.Msi
4{
5 using System;
6 using System.Diagnostics;
7 using System.Text;
8 using WixToolset.Core.Native;
9
10 /// <summary>
11 /// Windows Installer message types.
12 /// </summary>
13 [Flags]
14 internal enum InstallMessage
15 {
16 /// <summary>
17 /// Premature termination, possibly fatal out of memory.
18 /// </summary>
19 FatalExit = 0x00000000,
20
21 /// <summary>
22 /// Formatted error message, [1] is message number in Error table.
23 /// </summary>
24 Error = 0x01000000,
25
26 /// <summary>
27 /// Formatted warning message, [1] is message number in Error table.
28 /// </summary>
29 Warning = 0x02000000,
30
31 /// <summary>
32 /// User request message, [1] is message number in Error table.
33 /// </summary>
34 User = 0x03000000,
35
36 /// <summary>
37 /// Informative message for log, not to be displayed.
38 /// </summary>
39 Info = 0x04000000,
40
41 /// <summary>
42 /// List of files in use that need to be replaced.
43 /// </summary>
44 FilesInUse = 0x05000000,
45
46 /// <summary>
47 /// Request to determine a valid source location.
48 /// </summary>
49 ResolveSource = 0x06000000,
50
51 /// <summary>
52 /// Insufficient disk space message.
53 /// </summary>
54 OutOfDiskSpace = 0x07000000,
55
56 /// <summary>
57 /// Progress: start of action, [1] action name, [2] description, [3] template for ACTIONDATA messages.
58 /// </summary>
59 ActionStart = 0x08000000,
60
61 /// <summary>
62 /// Action data. Record fields correspond to the template of ACTIONSTART message.
63 /// </summary>
64 ActionData = 0x09000000,
65
66 /// <summary>
67 /// Progress bar information. See the description of record fields below.
68 /// </summary>
69 Progress = 0x0A000000,
70
71 /// <summary>
72 /// To enable the Cancel button set [1] to 2 and [2] to 1. To disable the Cancel button set [1] to 2 and [2] to 0.
73 /// </summary>
74 CommonData = 0x0B000000,
75
76 /// <summary>
77 /// Sent prior to UI initialization, no string data.
78 /// </summary>
79 Initilize = 0x0C000000,
80
81 /// <summary>
82 /// Sent after UI termination, no string data.
83 /// </summary>
84 Terminate = 0x0D000000,
85
86 /// <summary>
87 /// Sent prior to display or authored dialog or wizard.
88 /// </summary>
89 ShowDialog = 0x0E000000
90 }
91
92 /// <summary>
93 /// Windows Installer log modes.
94 /// </summary>
95 [Flags]
96 internal enum InstallLogModes
97 {
98 /// <summary>
99 /// Premature termination of installation.
100 /// </summary>
101 FatalExit = (1 << ((int)InstallMessage.FatalExit >> 24)),
102
103 /// <summary>
104 /// The error messages are logged.
105 /// </summary>
106 Error = (1 << ((int)InstallMessage.Error >> 24)),
107
108 /// <summary>
109 /// The warning messages are logged.
110 /// </summary>
111 Warning = (1 << ((int)InstallMessage.Warning >> 24)),
112
113 /// <summary>
114 /// The user requests are logged.
115 /// </summary>
116 User = (1 << ((int)InstallMessage.User >> 24)),
117
118 /// <summary>
119 /// The status messages that are not displayed are logged.
120 /// </summary>
121 Info = (1 << ((int)InstallMessage.Info >> 24)),
122
123 /// <summary>
124 /// Request to determine a valid source location.
125 /// </summary>
126 ResolveSource = (1 << ((int)InstallMessage.ResolveSource >> 24)),
127
128 /// <summary>
129 /// The was insufficient disk space.
130 /// </summary>
131 OutOfDiskSpace = (1 << ((int)InstallMessage.OutOfDiskSpace >> 24)),
132
133 /// <summary>
134 /// The start of new installation actions are logged.
135 /// </summary>
136 ActionStart = (1 << ((int)InstallMessage.ActionStart >> 24)),
137
138 /// <summary>
139 /// The data record with the installation action is logged.
140 /// </summary>
141 ActionData = (1 << ((int)InstallMessage.ActionData >> 24)),
142
143 /// <summary>
144 /// The parameters for user-interface initialization are logged.
145 /// </summary>
146 CommonData = (1 << ((int)InstallMessage.CommonData >> 24)),
147
148 /// <summary>
149 /// Logs the property values at termination.
150 /// </summary>
151 PropertyDump = (1 << ((int)InstallMessage.Progress >> 24)),
152
153 /// <summary>
154 /// Sends large amounts of information to a log file not generally useful to users.
155 /// May be used for technical support.
156 /// </summary>
157 Verbose = (1 << ((int)InstallMessage.Initilize >> 24)),
158
159 /// <summary>
160 /// Sends extra debugging information, such as handle creation information, to the log file.
161 /// </summary>
162 ExtraDebug = (1 << ((int)InstallMessage.Terminate >> 24)),
163
164 /// <summary>
165 /// Progress bar information. This message includes information on units so far and total number of units.
166 /// See MsiProcessMessage for an explanation of the message format.
167 /// This message is only sent to an external user interface and is not logged.
168 /// </summary>
169 Progress = (1 << ((int)InstallMessage.Progress >> 24)),
170
171 /// <summary>
172 /// If this is not a quiet installation, then the basic UI has been initialized.
173 /// If this is a full UI installation, the full UI is not yet initialized.
174 /// This message is only sent to an external user interface and is not logged.
175 /// </summary>
176 Initialize = (1 << ((int)InstallMessage.Initilize >> 24)),
177
178 /// <summary>
179 /// If a full UI is being used, the full UI has ended.
180 /// If this is not a quiet installation, the basic UI has not yet ended.
181 /// This message is only sent to an external user interface and is not logged.
182 /// </summary>
183 Terminate = (1 << ((int)InstallMessage.Terminate >> 24)),
184
185 /// <summary>
186 /// Sent prior to display of the full UI dialog.
187 /// This message is only sent to an external user interface and is not logged.
188 /// </summary>
189 ShowDialog = (1 << ((int)InstallMessage.ShowDialog >> 24)),
190
191 /// <summary>
192 /// Files in use information. When this message is received, a FilesInUse Dialog should be displayed.
193 /// </summary>
194 FilesInUse = (1 << ((int)InstallMessage.FilesInUse >> 24))
195 }
196
197 /// <summary>
198 /// Windows Installer UI levels.
199 /// </summary>
200 [Flags]
201 internal enum InstallUILevels
202 {
203 /// <summary>
204 /// No change in the UI level. However, if phWnd is not Null, the parent window can change.
205 /// </summary>
206 NoChange = 0,
207
208 /// <summary>
209 /// The installer chooses an appropriate user interface level.
210 /// </summary>
211 Default = 1,
212
213 /// <summary>
214 /// Completely silent installation.
215 /// </summary>
216 None = 2,
217
218 /// <summary>
219 /// Simple progress and error handling.
220 /// </summary>
221 Basic = 3,
222
223 /// <summary>
224 /// Authored user interface with wizard dialog boxes suppressed.
225 /// </summary>
226 Reduced = 4,
227
228 /// <summary>
229 /// Authored user interface with wizards, progress, and errors.
230 /// </summary>
231 Full = 5,
232
233 /// <summary>
234 /// If combined with the Basic value, the installer shows simple progress dialog boxes but
235 /// does not display a Cancel button on the dialog. This prevents users from canceling the install.
236 /// Available with Windows Installer version 2.0.
237 /// </summary>
238 HideCancel = 0x20,
239
240 /// <summary>
241 /// If combined with the Basic value, the installer shows simple progress
242 /// dialog boxes but does not display any modal dialog boxes or error dialog boxes.
243 /// </summary>
244 ProgressOnly = 0x40,
245
246 /// <summary>
247 /// If combined with any above value, the installer displays a modal dialog
248 /// box at the end of a successful installation or if there has been an error.
249 /// No dialog box is displayed if the user cancels.
250 /// </summary>
251 EndDialog = 0x80,
252
253 /// <summary>
254 /// If this value is combined with the None value, the installer displays only the dialog
255 /// boxes used for source resolution. No other dialog boxes are shown. This value has no
256 /// effect if the UI level is not INSTALLUILEVEL_NONE. It is used with an external user
257 /// interface designed to handle all of the UI except for source resolution. In this case,
258 /// the installer handles source resolution. This value is only available with Windows Installer 2.0 and later.
259 /// </summary>
260 SourceResOnly = 0x100
261 }
262
263 /// <summary>
264 /// Represents the Windows Installer, provides wrappers to
265 /// create the top-level objects and access their methods.
266 /// </summary>
267 internal sealed class Installer
268 {
269 /// <summary>
270 /// Protect the constructor.
271 /// </summary>
272 private Installer()
273 {
274 }
275
276 /// <summary>
277 /// Takes the path to a file and returns a 128-bit hash of that file.
278 /// </summary>
279 /// <param name="filePath">Path to file that is to be hashed.</param>
280 /// <param name="options">The value in this column must be 0. This parameter is reserved for future use.</param>
281 /// <param name="hash">Int array that receives the returned file hash information.</param>
282 internal static void GetFileHash(string filePath, int options, out int[] hash)
283 {
284 MsiInterop.MSIFILEHASHINFO hashInterop = new MsiInterop.MSIFILEHASHINFO();
285 hashInterop.FileHashInfoSize = 20;
286
287 int error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), hashInterop);
288 if (0 != error)
289 {
290 throw new MsiException(error);
291 }
292
293 Debug.Assert(20 == hashInterop.FileHashInfoSize);
294
295 hash = new int[4];
296 hash[0] = hashInterop.Data0;
297 hash[1] = hashInterop.Data1;
298 hash[2] = hashInterop.Data2;
299 hash[3] = hashInterop.Data3;
300 }
301
302 /// <summary>
303 /// Returns the version string and language string in the format that the installer
304 /// expects to find them in the database. If you just want version information, set
305 /// lpLangBuf and pcchLangBuf to zero. If you just want language information, set
306 /// lpVersionBuf and pcchVersionBuf to zero.
307 /// </summary>
308 /// <param name="filePath">Specifies the path to the file.</param>
309 /// <param name="version">Returns the file version. Set to 0 for language information only.</param>
310 /// <param name="language">Returns the file language. Set to 0 for version information only.</param>
311 internal static void GetFileVersion(string filePath, out string version, out string language)
312 {
313 int versionLength = 20;
314 int languageLength = 20;
315 StringBuilder versionBuffer = new StringBuilder(versionLength);
316 StringBuilder languageBuffer = new StringBuilder(languageLength);
317
318 int error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength);
319 if (234 == error)
320 {
321 versionBuffer.EnsureCapacity(++versionLength);
322 languageBuffer.EnsureCapacity(++languageLength);
323 error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength);
324 }
325 else if (1006 == error)
326 {
327 // file has no version or language, so no error
328 error = 0;
329 }
330
331 if (0 != error)
332 {
333 throw new MsiException(error);
334 }
335
336 version = versionBuffer.ToString();
337 language = languageBuffer.ToString();
338 }
339
340 /// <summary>
341 /// Enables an external user-interface handler.
342 /// </summary>
343 /// <param name="installUIHandler">Specifies a callback function.</param>
344 /// <param name="messageFilter">Specifies which messages to handle using the external message handler.</param>
345 /// <param name="context">Pointer to an application context that is passed to the callback function.</param>
346 /// <returns>The return value is the previously set external handler, or null if there was no previously set handler.</returns>
347 internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context)
348 {
349 return MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context);
350 }
351
352 /// <summary>
353 /// Enables the installer's internal user interface.
354 /// </summary>
355 /// <param name="uiLevel">Specifies the level of complexity of the user interface.</param>
356 /// <param name="hwnd">Pointer to a window. This window becomes the owner of any user interface created.</param>
357 /// <returns>The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned.</returns>
358 internal static int SetInternalUI(int uiLevel, ref IntPtr hwnd)
359 {
360 return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd);
361 }
362 }
363}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs
new file mode 100644
index 00000000..054289ee
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Interop/MsiInterop.cs
@@ -0,0 +1,697 @@
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#if false
3namespace WixToolset.Msi.Interop
4{
5 using System;
6 using System.Text;
7 using System.Runtime.InteropServices;
8 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
9
10 /// <summary>
11 /// A callback function that the installer calls for progress notification and error messages.
12 /// </summary>
13 /// <param name="context">Pointer to an application context.
14 /// This parameter can be used for error checking.</param>
15 /// <param name="messageType">Specifies a combination of one message box style,
16 /// one message box icon type, one default button, and one installation message type.</param>
17 /// <param name="message">Specifies the message text.</param>
18 /// <returns>-1 for an error, 0 if no action was taken, 1 if OK, 3 to abort.</returns>
19 internal delegate int InstallUIHandler(IntPtr context, uint messageType, [MarshalAs(UnmanagedType.LPWStr)] string message);
20
21 /// <summary>
22 /// Class exposing static functions and structs from MSI API.
23 /// </summary>
24 internal sealed class MsiInterop
25 {
26 // Patching constants
27 internal const int MsiMaxStreamNameLength = 62; // http://msdn2.microsoft.com/library/aa370551.aspx
28
29 // Component.Attributes
30 internal const int MsidbComponentAttributesLocalOnly = 0;
31 internal const int MsidbComponentAttributesSourceOnly = 1;
32 internal const int MsidbComponentAttributesOptional = 2;
33 internal const int MsidbComponentAttributesRegistryKeyPath = 4;
34 internal const int MsidbComponentAttributesSharedDllRefCount = 8;
35 internal const int MsidbComponentAttributesPermanent = 16;
36 internal const int MsidbComponentAttributesODBCDataSource = 32;
37 internal const int MsidbComponentAttributesTransitive = 64;
38 internal const int MsidbComponentAttributesNeverOverwrite = 128;
39 internal const int MsidbComponentAttributes64bit = 256;
40 internal const int MsidbComponentAttributesDisableRegistryReflection = 512;
41 internal const int MsidbComponentAttributesUninstallOnSupersedence = 1024;
42 internal const int MsidbComponentAttributesShared = 2048;
43
44 // BBControl.Attributes & Control.Attributes
45 internal const int MsidbControlAttributesVisible = 0x00000001;
46 internal const int MsidbControlAttributesEnabled = 0x00000002;
47 internal const int MsidbControlAttributesSunken = 0x00000004;
48 internal const int MsidbControlAttributesIndirect = 0x00000008;
49 internal const int MsidbControlAttributesInteger = 0x00000010;
50 internal const int MsidbControlAttributesRTLRO = 0x00000020;
51 internal const int MsidbControlAttributesRightAligned = 0x00000040;
52 internal const int MsidbControlAttributesLeftScroll = 0x00000080;
53 internal const int MsidbControlAttributesBiDi = MsidbControlAttributesRTLRO | MsidbControlAttributesRightAligned | MsidbControlAttributesLeftScroll;
54
55 // Text controls
56 internal const int MsidbControlAttributesTransparent = 0x00010000;
57 internal const int MsidbControlAttributesNoPrefix = 0x00020000;
58 internal const int MsidbControlAttributesNoWrap = 0x00040000;
59 internal const int MsidbControlAttributesFormatSize = 0x00080000;
60 internal const int MsidbControlAttributesUsersLanguage = 0x00100000;
61
62 // Edit controls
63 internal const int MsidbControlAttributesMultiline = 0x00010000;
64 internal const int MsidbControlAttributesPasswordInput = 0x00200000;
65
66 // ProgressBar controls
67 internal const int MsidbControlAttributesProgress95 = 0x00010000;
68
69 // VolumeSelectCombo and DirectoryCombo controls
70 internal const int MsidbControlAttributesRemovableVolume = 0x00010000;
71 internal const int MsidbControlAttributesFixedVolume = 0x00020000;
72 internal const int MsidbControlAttributesRemoteVolume = 0x00040000;
73 internal const int MsidbControlAttributesCDROMVolume = 0x00080000;
74 internal const int MsidbControlAttributesRAMDiskVolume = 0x00100000;
75 internal const int MsidbControlAttributesFloppyVolume = 0x00200000;
76
77 // VolumeCostList controls
78 internal const int MsidbControlShowRollbackCost = 0x00400000;
79
80 // ListBox and ComboBox controls
81 internal const int MsidbControlAttributesSorted = 0x00010000;
82 internal const int MsidbControlAttributesComboList = 0x00020000;
83
84 // picture button controls
85 internal const int MsidbControlAttributesImageHandle = 0x00010000;
86 internal const int MsidbControlAttributesPushLike = 0x00020000;
87 internal const int MsidbControlAttributesBitmap = 0x00040000;
88 internal const int MsidbControlAttributesIcon = 0x00080000;
89 internal const int MsidbControlAttributesFixedSize = 0x00100000;
90 internal const int MsidbControlAttributesIconSize16 = 0x00200000;
91 internal const int MsidbControlAttributesIconSize32 = 0x00400000;
92 internal const int MsidbControlAttributesIconSize48 = 0x00600000;
93 internal const int MsidbControlAttributesElevationShield = 0x00800000;
94
95 // RadioButton controls
96 internal const int MsidbControlAttributesHasBorder = 0x01000000;
97
98 // CustomAction.Type
99 // executable types
100 internal const int MsidbCustomActionTypeDll = 0x00000001; // Target = entry point name
101 internal const int MsidbCustomActionTypeExe = 0x00000002; // Target = command line args
102 internal const int MsidbCustomActionTypeTextData = 0x00000003; // Target = text string to be formatted and set into property
103 internal const int MsidbCustomActionTypeJScript = 0x00000005; // Target = entry point name; null if none to call
104 internal const int MsidbCustomActionTypeVBScript = 0x00000006; // Target = entry point name; null if none to call
105 internal const int MsidbCustomActionTypeInstall = 0x00000007; // Target = property list for nested engine initialization
106 internal const int MsidbCustomActionTypeSourceBits = 0x00000030;
107 internal const int MsidbCustomActionTypeTargetBits = 0x00000007;
108 internal const int MsidbCustomActionTypeReturnBits = 0x000000C0;
109 internal const int MsidbCustomActionTypeExecuteBits = 0x00000700;
110
111 // source of code
112 internal const int MsidbCustomActionTypeBinaryData = 0x00000000; // Source = Binary.Name; data stored in stream
113 internal const int MsidbCustomActionTypeSourceFile = 0x00000010; // Source = File.File; file part of installation
114 internal const int MsidbCustomActionTypeDirectory = 0x00000020; // Source = Directory.Directory; folder containing existing file
115 internal const int MsidbCustomActionTypeProperty = 0x00000030; // Source = Property.Property; full path to executable
116
117 // return processing; default is syncronous execution; process return code
118 internal const int MsidbCustomActionTypeContinue = 0x00000040; // ignore action return status; continue running
119 internal const int MsidbCustomActionTypeAsync = 0x00000080; // run asynchronously
120
121 // execution scheduling flags; default is execute whenever sequenced
122 internal const int MsidbCustomActionTypeFirstSequence = 0x00000100; // skip if UI sequence already run
123 internal const int MsidbCustomActionTypeOncePerProcess = 0x00000200; // skip if UI sequence already run in same process
124 internal const int MsidbCustomActionTypeClientRepeat = 0x00000300; // run on client only if UI already run on client
125 internal const int MsidbCustomActionTypeInScript = 0x00000400; // queue for execution within script
126 internal const int MsidbCustomActionTypeRollback = 0x00000100; // in conjunction with InScript: queue in Rollback script
127 internal const int MsidbCustomActionTypeCommit = 0x00000200; // in conjunction with InScript: run Commit ops from script on success
128
129 // security context flag; default to impersonate as user; valid only if InScript
130 internal const int MsidbCustomActionTypeNoImpersonate = 0x00000800; // no impersonation; run in system context
131 internal const int MsidbCustomActionTypeTSAware = 0x00004000; // impersonate for per-machine installs on TS machines
132 internal const int MsidbCustomActionType64BitScript = 0x00001000; // script should run in 64bit process
133 internal const int MsidbCustomActionTypeHideTarget = 0x00002000; // don't record the contents of the Target field in the log file.
134
135 internal const int MsidbCustomActionTypePatchUninstall = 0x00008000; // run on patch uninstall
136
137 // Dialog.Attributes
138 internal const int MsidbDialogAttributesVisible = 0x00000001;
139 internal const int MsidbDialogAttributesModal = 0x00000002;
140 internal const int MsidbDialogAttributesMinimize = 0x00000004;
141 internal const int MsidbDialogAttributesSysModal = 0x00000008;
142 internal const int MsidbDialogAttributesKeepModeless = 0x00000010;
143 internal const int MsidbDialogAttributesTrackDiskSpace = 0x00000020;
144 internal const int MsidbDialogAttributesUseCustomPalette = 0x00000040;
145 internal const int MsidbDialogAttributesRTLRO = 0x00000080;
146 internal const int MsidbDialogAttributesRightAligned = 0x00000100;
147 internal const int MsidbDialogAttributesLeftScroll = 0x00000200;
148 internal const int MsidbDialogAttributesBiDi = MsidbDialogAttributesRTLRO | MsidbDialogAttributesRightAligned | MsidbDialogAttributesLeftScroll;
149 internal const int MsidbDialogAttributesError = 0x00010000;
150 internal const int CommonControlAttributesInvert = MsidbControlAttributesVisible + MsidbControlAttributesEnabled;
151 internal const int DialogAttributesInvert = MsidbDialogAttributesVisible + MsidbDialogAttributesModal + MsidbDialogAttributesMinimize;
152
153 // Feature.Attributes
154 internal const int MsidbFeatureAttributesFavorLocal = 0;
155 internal const int MsidbFeatureAttributesFavorSource = 1;
156 internal const int MsidbFeatureAttributesFollowParent = 2;
157 internal const int MsidbFeatureAttributesFavorAdvertise = 4;
158 internal const int MsidbFeatureAttributesDisallowAdvertise = 8;
159 internal const int MsidbFeatureAttributesUIDisallowAbsent = 16;
160 internal const int MsidbFeatureAttributesNoUnsupportedAdvertise = 32;
161
162 // File.Attributes
163 internal const int MsidbFileAttributesReadOnly = 1;
164 internal const int MsidbFileAttributesHidden = 2;
165 internal const int MsidbFileAttributesSystem = 4;
166 internal const int MsidbFileAttributesVital = 512;
167 internal const int MsidbFileAttributesChecksum = 1024;
168 internal const int MsidbFileAttributesPatchAdded = 4096;
169 internal const int MsidbFileAttributesNoncompressed = 8192;
170 internal const int MsidbFileAttributesCompressed = 16384;
171
172 // IniFile.Action & RemoveIniFile.Action
173 internal const int MsidbIniFileActionAddLine = 0;
174 internal const int MsidbIniFileActionCreateLine = 1;
175 internal const int MsidbIniFileActionRemoveLine = 2;
176 internal const int MsidbIniFileActionAddTag = 3;
177 internal const int MsidbIniFileActionRemoveTag = 4;
178
179 // MoveFile.Options
180 internal const int MsidbMoveFileOptionsMove = 1;
181
182 // ServiceInstall.Attributes
183 internal const int MsidbServiceInstallOwnProcess = 0x00000010;
184 internal const int MsidbServiceInstallShareProcess = 0x00000020;
185 internal const int MsidbServiceInstallInteractive = 0x00000100;
186 internal const int MsidbServiceInstallAutoStart = 0x00000002;
187 internal const int MsidbServiceInstallDemandStart = 0x00000003;
188 internal const int MsidbServiceInstallDisabled = 0x00000004;
189 internal const int MsidbServiceInstallErrorIgnore = 0x00000000;
190 internal const int MsidbServiceInstallErrorNormal = 0x00000001;
191 internal const int MsidbServiceInstallErrorCritical = 0x00000003;
192 internal const int MsidbServiceInstallErrorControlVital = 0x00008000;
193
194 // ServiceConfig.Event
195 internal const int MsidbServiceConfigEventInstall = 0x00000001;
196 internal const int MsidbServiceConfigEventUninstall = 0x00000002;
197 internal const int MsidbServiceConfigEventReinstall = 0x00000004;
198
199 // ServiceControl.Attributes
200 internal const int MsidbServiceControlEventStart = 0x00000001;
201 internal const int MsidbServiceControlEventStop = 0x00000002;
202 internal const int MsidbServiceControlEventDelete = 0x00000008;
203 internal const int MsidbServiceControlEventUninstallStart = 0x00000010;
204 internal const int MsidbServiceControlEventUninstallStop = 0x00000020;
205 internal const int MsidbServiceControlEventUninstallDelete = 0x00000080;
206
207 // TextStyle.StyleBits
208 internal const int MsidbTextStyleStyleBitsBold = 1;
209 internal const int MsidbTextStyleStyleBitsItalic = 2;
210 internal const int MsidbTextStyleStyleBitsUnderline = 4;
211 internal const int MsidbTextStyleStyleBitsStrike = 8;
212
213 // Upgrade.Attributes
214 internal const int MsidbUpgradeAttributesMigrateFeatures = 0x00000001;
215 internal const int MsidbUpgradeAttributesOnlyDetect = 0x00000002;
216 internal const int MsidbUpgradeAttributesIgnoreRemoveFailure = 0x00000004;
217 internal const int MsidbUpgradeAttributesVersionMinInclusive = 0x00000100;
218 internal const int MsidbUpgradeAttributesVersionMaxInclusive = 0x00000200;
219 internal const int MsidbUpgradeAttributesLanguagesExclusive = 0x00000400;
220
221 // Registry Hive Roots
222 internal const int MsidbRegistryRootClassesRoot = 0;
223 internal const int MsidbRegistryRootCurrentUser = 1;
224 internal const int MsidbRegistryRootLocalMachine = 2;
225 internal const int MsidbRegistryRootUsers = 3;
226
227 // Locator Types
228 internal const int MsidbLocatorTypeDirectory = 0;
229 internal const int MsidbLocatorTypeFileName = 1;
230 internal const int MsidbLocatorTypeRawValue = 2;
231 internal const int MsidbLocatorType64bit = 16;
232
233 internal const int MsidbClassAttributesRelativePath = 1;
234
235 // RemoveFile.InstallMode
236 internal const int MsidbRemoveFileInstallModeOnInstall = 0x00000001;
237 internal const int MsidbRemoveFileInstallModeOnRemove = 0x00000002;
238 internal const int MsidbRemoveFileInstallModeOnBoth = 0x00000003;
239
240 // ODBCDataSource.Registration
241 internal const int MsidbODBCDataSourceRegistrationPerMachine = 0;
242 internal const int MsidbODBCDataSourceRegistrationPerUser = 1;
243
244 // ModuleConfiguration.Format
245 internal const int MsidbModuleConfigurationFormatText = 0;
246 internal const int MsidbModuleConfigurationFormatKey = 1;
247 internal const int MsidbModuleConfigurationFormatInteger = 2;
248 internal const int MsidbModuleConfigurationFormatBitfield = 3;
249
250 // ModuleConfiguration.Attributes
251 internal const int MsidbMsmConfigurableOptionKeyNoOrphan = 1;
252 internal const int MsidbMsmConfigurableOptionNonNullable = 2;
253
254 // ' Windows API function ShowWindow constants - used in Shortcut table
255 internal const int SWSHOWNORMAL = 0x00000001;
256 internal const int SWSHOWMAXIMIZED = 0x00000003;
257 internal const int SWSHOWMINNOACTIVE = 0x00000007;
258
259 // NameToBit arrays
260 // UI elements
261 internal static readonly string[] CommonControlAttributes = { "Hidden", "Disabled", "Sunken", "Indirect", "Integer", "RightToLeft", "RightAligned", "LeftScroll" };
262 internal static readonly string[] TextControlAttributes = { "Transparent", "NoPrefix", "NoWrap", "FormatSize", "UserLanguage" };
263 internal static readonly string[] HyperlinkControlAttributes = { "Transparent" };
264 internal static readonly string[] EditControlAttributes = { "Multiline", null, null, null, null, "Password" };
265 internal static readonly string[] ProgressControlAttributes = { "ProgressBlocks" };
266 internal static readonly string[] VolumeControlAttributes = { "Removable", "Fixed", "Remote", "CDROM", "RAMDisk", "Floppy", "ShowRollbackCost" };
267 internal static readonly string[] ListboxControlAttributes = { "Sorted", null, null, null, "UserLanguage" };
268 internal static readonly string[] ListviewControlAttributes = { "Sorted", null, null, null, "FixedSize", "Icon16", "Icon32" };
269 internal static readonly string[] ComboboxControlAttributes = { "Sorted", "ComboList", null, null, "UserLanguage" };
270 internal static readonly string[] RadioControlAttributes = { "Image", "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32", null, "HasBorder" };
271 internal static readonly string[] ButtonControlAttributes = { "Image", null, "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32", "ElevationShield" };
272 internal static readonly string[] IconControlAttributes = { "Image", null, null, null, "FixedSize", "Icon16", "Icon32" };
273 internal static readonly string[] BitmapControlAttributes = { "Image", null, null, null, "FixedSize" };
274 internal static readonly string[] CheckboxControlAttributes = { null, "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32" };
275
276 internal const int MsidbEmbeddedUI = 0x01;
277 internal const int MsidbEmbeddedHandlesBasic = 0x02;
278
279 internal const int INSTALLLOGMODE_FATALEXIT = 0x00001;
280 internal const int INSTALLLOGMODE_ERROR = 0x00002;
281 internal const int INSTALLLOGMODE_WARNING = 0x00004;
282 internal const int INSTALLLOGMODE_USER = 0x00008;
283 internal const int INSTALLLOGMODE_INFO = 0x00010;
284 internal const int INSTALLLOGMODE_FILESINUSE = 0x00020;
285 internal const int INSTALLLOGMODE_RESOLVESOURCE = 0x00040;
286 internal const int INSTALLLOGMODE_OUTOFDISKSPACE = 0x00080;
287 internal const int INSTALLLOGMODE_ACTIONSTART = 0x00100;
288 internal const int INSTALLLOGMODE_ACTIONDATA = 0x00200;
289 internal const int INSTALLLOGMODE_PROGRESS = 0x00400;
290 internal const int INSTALLLOGMODE_COMMONDATA = 0x00800;
291 internal const int INSTALLLOGMODE_INITIALIZE = 0x01000;
292 internal const int INSTALLLOGMODE_TERMINATE = 0x02000;
293 internal const int INSTALLLOGMODE_SHOWDIALOG = 0x04000;
294 internal const int INSTALLLOGMODE_RMFILESINUSE = 0x02000000;
295 internal const int INSTALLLOGMODE_INSTALLSTART = 0x04000000;
296 internal const int INSTALLLOGMODE_INSTALLEND = 0x08000000;
297
298 internal const int MSICONDITIONFALSE = 0; // The table is temporary.
299 internal const int MSICONDITIONTRUE = 1; // The table is persistent.
300 internal const int MSICONDITIONNONE = 2; // The table is unknown.
301 internal const int MSICONDITIONERROR = 3; // An invalid handle or invalid parameter was passed to the function.
302
303 internal const int MSIDBOPENREADONLY = 0;
304 internal const int MSIDBOPENTRANSACT = 1;
305 internal const int MSIDBOPENDIRECT = 2;
306 internal const int MSIDBOPENCREATE = 3;
307 internal const int MSIDBOPENCREATEDIRECT = 4;
308 internal const int MSIDBOPENPATCHFILE = 32;
309
310 internal const int MSIMODIFYSEEK = -1; // Refreshes the information in the supplied record without changing the position in the result set and without affecting subsequent fetch operations. The record may then be used for subsequent Update, Delete, and Refresh. All primary key columns of the table must be in the query and the record must have at least as many fields as the query. Seek cannot be used with multi-table queries. This mode cannot be used with a view containing joins. See also the remarks.
311 internal const int MSIMODIFYREFRESH = 0; // Refreshes the information in the record. Must first call MsiViewFetch with the same record. Fails for a deleted row. Works with read-write and read-only records.
312 internal const int MSIMODIFYINSERT = 1; // Inserts a record. Fails if a row with the same primary keys exists. Fails with a read-only database. This mode cannot be used with a view containing joins.
313 internal const int MSIMODIFYUPDATE = 2; // Updates an existing record. Nonprimary keys only. Must first call MsiViewFetch. Fails with a deleted record. Works only with read-write records.
314 internal const int MSIMODIFYASSIGN = 3; // Writes current data in the cursor to a table row. Updates record if the primary keys match an existing row and inserts if they do not match. Fails with a read-only database. This mode cannot be used with a view containing joins.
315 internal const int MSIMODIFYREPLACE = 4; // Updates or deletes and inserts a record into a table. Must first call MsiViewFetch with the same record. Updates record if the primary keys are unchanged. Deletes old row and inserts new if primary keys have changed. Fails with a read-only database. This mode cannot be used with a view containing joins.
316 internal const int MSIMODIFYMERGE = 5; // Inserts or validates a record in a table. Inserts if primary keys do not match any row and validates if there is a match. Fails if the record does not match the data in the table. Fails if there is a record with a duplicate key that is not identical. Works only with read-write records. This mode cannot be used with a view containing joins.
317 internal const int MSIMODIFYDELETE = 6; // Remove a row from the table. You must first call the MsiViewFetch function with the same record. Fails if the row has been deleted. Works only with read-write records. This mode cannot be used with a view containing joins.
318 internal const int MSIMODIFYINSERTTEMPORARY = 7; // Inserts a temporary record. The information is not persistent. Fails if a row with the same primary key exists. Works only with read-write records. This mode cannot be used with a view containing joins.
319 internal const int MSIMODIFYVALIDATE = 8; // Validates a record. Does not validate across joins. You must first call the MsiViewFetch function with the same record. Obtain validation errors with MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
320 internal const int MSIMODIFYVALIDATENEW = 9; // Validate a new record. Does not validate across joins. Checks for duplicate keys. Obtain validation errors by calling MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
321 internal const int MSIMODIFYVALIDATEFIELD = 10; // Validates fields of a fetched or new record. Can validate one or more fields of an incomplete record. Obtain validation errors by calling MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
322 internal const int MSIMODIFYVALIDATEDELETE = 11; // Validates a record that will be deleted later. You must first call MsiViewFetch. Fails if another row refers to the primary keys of this row. Validation does not check for the existence of the primary keys of this row in properties or strings. Does not check if a column is a foreign key to multiple tables. Obtain validation errors by calling MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
323
324 internal const uint VTI2 = 2;
325 internal const uint VTI4 = 3;
326 internal const uint VTLPWSTR = 30;
327 internal const uint VTFILETIME = 64;
328
329 internal const int MSICOLINFONAMES = 0; // return column names
330 internal const int MSICOLINFOTYPES = 1; // return column definitions, datatype code followed by width
331
332 /// <summary>
333 /// Protect the constructor.
334 /// </summary>
335 private MsiInterop()
336 {
337 }
338
339 /// <summary>
340 /// PInvoke of MsiCloseHandle.
341 /// </summary>
342 /// <param name="database">Handle to a database.</param>
343 /// <returns>Error code.</returns>
344 [DllImport("msi.dll", EntryPoint = "MsiCloseHandle", CharSet = CharSet.Unicode, ExactSpelling = true)]
345 internal static extern int MsiCloseHandle(uint database);
346
347 /// <summary>
348 /// PInvoke of MsiCreateRecord
349 /// </summary>
350 /// <param name="parameters">Count of columns in the record.</param>
351 /// <returns>Handle referencing the record.</returns>
352 [DllImport("msi.dll", EntryPoint = "MsiCreateRecord", CharSet = CharSet.Unicode, ExactSpelling = true)]
353 internal static extern uint MsiCreateRecord(int parameters);
354
355 /// <summary>
356 /// Creates summary information of an existing transform to include validation and error conditions.
357 /// </summary>
358 /// <param name="database">The handle to the database that contains the new database summary information.</param>
359 /// <param name="referenceDatabase">The handle to the database that contains the original summary information.</param>
360 /// <param name="transformFile">The name of the transform to which the summary information is added.</param>
361 /// <param name="errorConditions">The error conditions that should be suppressed when the transform is applied.</param>
362 /// <param name="validations">Specifies the properties to be validated to verify that the transform can be applied to the database.</param>
363 /// <returns>Error code.</returns>
364 [DllImport("msi.dll", EntryPoint = "MsiCreateTransformSummaryInfoW", CharSet = CharSet.Unicode, ExactSpelling = true)]
365 internal static extern int MsiCreateTransformSummaryInfo(uint database, uint referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations);
366
367 /// <summary>
368 /// Applies a transform to a database.
369 /// </summary>
370 /// <param name="database">Handle to the database obtained from MsiOpenDatabase to transform.</param>
371 /// <param name="transformFile">Specifies the name of the transform file to apply.</param>
372 /// <param name="errorConditions">Error conditions that should be suppressed.</param>
373 /// <returns>Error code.</returns>
374 [DllImport("msi.dll", EntryPoint = "MsiDatabaseApplyTransformW", CharSet = CharSet.Unicode, ExactSpelling = true)]
375 internal static extern int MsiDatabaseApplyTransform(uint database, string transformFile, TransformErrorConditions errorConditions);
376
377 /// <summary>
378 /// PInvoke of MsiDatabaseCommit.
379 /// </summary>
380 /// <param name="database">Handle to a databse.</param>
381 /// <returns>Error code.</returns>
382 [DllImport("msi.dll", EntryPoint = "MsiDatabaseCommit", CharSet = CharSet.Unicode, ExactSpelling = true)]
383 internal static extern int MsiDatabaseCommit(uint database);
384
385 /// <summary>
386 /// PInvoke of MsiDatabaseExportW.
387 /// </summary>
388 /// <param name="database">Handle to a database.</param>
389 /// <param name="tableName">Table name.</param>
390 /// <param name="folderPath">Folder path.</param>
391 /// <param name="fileName">File name.</param>
392 /// <returns>Error code.</returns>
393 [DllImport("msi.dll", EntryPoint = "MsiDatabaseExportW", CharSet = CharSet.Unicode, ExactSpelling = true)]
394 internal static extern int MsiDatabaseExport(uint database, string tableName, string folderPath, string fileName);
395
396 /// <summary>
397 /// Generates a transform file of differences between two databases.
398 /// </summary>
399 /// <param name="database">Handle to the database obtained from MsiOpenDatabase that includes the changes.</param>
400 /// <param name="databaseReference">Handle to the database obtained from MsiOpenDatabase that does not include the changes.</param>
401 /// <param name="transformFile">A null-terminated string that specifies the name of the transform file being generated.
402 /// This parameter can be null. If szTransformFile is null, you can use MsiDatabaseGenerateTransform to test whether two
403 /// databases are identical without creating a transform. If the databases are identical, the function returns ERROR_NO_DATA.
404 /// If the databases are different the function returns NOERROR.</param>
405 /// <param name="reserved1">This is a reserved argument and must be set to 0.</param>
406 /// <param name="reserved2">This is a reserved argument and must be set to 0.</param>
407 /// <returns>Error code.</returns>
408 [DllImport("msi.dll", EntryPoint = "MsiDatabaseGenerateTransformW", CharSet = CharSet.Unicode, ExactSpelling = true)]
409 internal static extern int MsiDatabaseGenerateTransform(uint database, uint databaseReference, string transformFile, int reserved1, int reserved2);
410
411 /// <summary>
412 /// PInvoke of MsiDatabaseImportW.
413 /// </summary>
414 /// <param name="database">Handle to a database.</param>
415 /// <param name="folderPath">Folder path.</param>
416 /// <param name="fileName">File name.</param>
417 /// <returns>Error code.</returns>
418 [DllImport("msi.dll", EntryPoint = "MsiDatabaseImportW", CharSet = CharSet.Unicode, ExactSpelling = true)]
419 internal static extern int MsiDatabaseImport(uint database, string folderPath, string fileName);
420
421 /// <summary>
422 /// PInvoke of MsiDatabaseMergeW.
423 /// </summary>
424 /// <param name="database">The handle to the database obtained from MsiOpenDatabase.</param>
425 /// <param name="databaseMerge">The handle to the database obtained from MsiOpenDatabase to merge into the base database.</param>
426 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
427 /// <returns>Error code.</returns>
428 [DllImport("msi.dll", EntryPoint = "MsiDatabaseMergeW", CharSet = CharSet.Unicode, ExactSpelling = true)]
429 internal static extern int MsiDatabaseMerge(uint database, uint databaseMerge, string tableName);
430
431 /// <summary>
432 /// PInvoke of MsiDatabaseOpenViewW.
433 /// </summary>
434 /// <param name="database">Handle to a database.</param>
435 /// <param name="query">SQL query.</param>
436 /// <param name="view">View handle.</param>
437 /// <returns>Error code.</returns>
438 [DllImport("msi.dll", EntryPoint = "MsiDatabaseOpenViewW", CharSet = CharSet.Unicode, ExactSpelling = true)]
439 internal static extern int MsiDatabaseOpenView(uint database, string query, out uint view);
440
441 /// <summary>
442 /// PInvoke of MsiGetFileHashW.
443 /// </summary>
444 /// <param name="filePath">File path.</param>
445 /// <param name="options">Hash options (must be 0).</param>
446 /// <param name="hash">Buffer to recieve hash.</param>
447 /// <returns>Error code.</returns>
448 [DllImport("msi.dll", EntryPoint = "MsiGetFileHashW", CharSet = CharSet.Unicode, ExactSpelling = true)]
449 internal static extern int MsiGetFileHash(string filePath, uint options, MSIFILEHASHINFO hash);
450
451 /// <summary>
452 /// PInvoke of MsiGetFileVersionW.
453 /// </summary>
454 /// <param name="filePath">File path.</param>
455 /// <param name="versionBuf">Buffer to receive version info.</param>
456 /// <param name="versionBufSize">Size of version buffer.</param>
457 /// <param name="langBuf">Buffer to recieve lang info.</param>
458 /// <param name="langBufSize">Size of lang buffer.</param>
459 /// <returns>Error code.</returns>
460 [DllImport("msi.dll", EntryPoint = "MsiGetFileVersionW", CharSet = CharSet.Unicode, ExactSpelling = true)]
461 internal static extern int MsiGetFileVersion(string filePath, StringBuilder versionBuf, ref int versionBufSize, StringBuilder langBuf, ref int langBufSize);
462
463 /// <summary>
464 /// PInvoke of MsiGetLastErrorRecord.
465 /// </summary>
466 /// <returns>Handle to error record if one exists.</returns>
467 [DllImport("msi.dll", EntryPoint = "MsiGetLastErrorRecord", CharSet = CharSet.Unicode, ExactSpelling = true)]
468 internal static extern uint MsiGetLastErrorRecord();
469
470 /// <summary>
471 /// PInvoke of MsiDatabaseGetPrimaryKeysW.
472 /// </summary>
473 /// <param name="database">Handle to a database.</param>
474 /// <param name="tableName">Table name.</param>
475 /// <param name="record">Handle to receive resulting record.</param>
476 /// <returns>Error code.</returns>
477 [DllImport("msi.dll", EntryPoint = "MsiDatabaseGetPrimaryKeysW", CharSet = CharSet.Unicode, ExactSpelling = true)]
478 internal static extern int MsiDatabaseGetPrimaryKeys(uint database, string tableName, out uint record);
479
480 /// <summary>
481 /// PInvoke of MsiDoActionW.
482 /// </summary>
483 /// <param name="product">Handle to the installation provided to a DLL custom action or
484 /// obtained through MsiOpenPackage, MsiOpenPackageEx, or MsiOpenProduct.</param>
485 /// <param name="action">Specifies the action to execute.</param>
486 /// <returns>Error code.</returns>
487 [DllImport("msi.dll", EntryPoint = "MsiDoActionW", CharSet = CharSet.Unicode, ExactSpelling = true)]
488 internal static extern int MsiDoAction(uint product, string action);
489
490 /// <summary>
491 /// PInvoke of MsiGetSummaryInformationW. Can use either database handle or database path as input.
492 /// </summary>
493 /// <param name="database">Handle to a database.</param>
494 /// <param name="databasePath">Path to a database.</param>
495 /// <param name="updateCount">Max number of updated values.</param>
496 /// <param name="summaryInfo">Handle to summary information.</param>
497 /// <returns>Error code.</returns>
498 [DllImport("msi.dll", EntryPoint = "MsiGetSummaryInformationW", CharSet = CharSet.Unicode, ExactSpelling = true)]
499 internal static extern int MsiGetSummaryInformation(uint database, string databasePath, uint updateCount, ref uint summaryInfo);
500
501 /// <summary>
502 /// PInvoke of MsiDatabaseIsTablePersitentW.
503 /// </summary>
504 /// <param name="database">Handle to a database.</param>
505 /// <param name="tableName">Table name.</param>
506 /// <returns>MSICONDITION</returns>
507 [DllImport("msi.dll", EntryPoint = "MsiDatabaseIsTablePersistentW", CharSet = CharSet.Unicode, ExactSpelling = true)]
508 internal static extern int MsiDatabaseIsTablePersistent(uint database, string tableName);
509
510 /// <summary>
511 /// PInvoke of MsiOpenDatabaseW.
512 /// </summary>
513 /// <param name="databasePath">Path to database.</param>
514 /// <param name="persist">Persist mode.</param>
515 /// <param name="database">Handle to database.</param>
516 /// <returns>Error code.</returns>
517 [DllImport("msi.dll", EntryPoint = "MsiOpenDatabaseW", CharSet = CharSet.Unicode, ExactSpelling = true)]
518 internal static extern int MsiOpenDatabase(string databasePath, IntPtr persist, out uint database);
519
520 /// <summary>
521 /// PInvoke of MsiOpenPackageW.
522 /// </summary>
523 /// <param name="packagePath">The path to the package.</param>
524 /// <param name="product">A pointer to a variable that receives the product handle.</param>
525 /// <returns>Error code.</returns>
526 [DllImport("msi.dll", EntryPoint = "MsiOpenPackageW", CharSet = CharSet.Unicode, ExactSpelling = true)]
527 internal static extern int MsiOpenPackage(string packagePath, out uint product);
528
529 /// <summary>
530 /// PInvoke of MsiRecordIsNull.
531 /// </summary>
532 /// <param name="record">MSI Record handle.</param>
533 /// <param name="field">Index of field to check for null value.</param>
534 /// <returns>true if the field is null, false if not, and an error code for any error.</returns>
535 [DllImport("msi.dll", EntryPoint = "MsiRecordIsNull", CharSet = CharSet.Unicode, ExactSpelling = true)]
536 internal static extern int MsiRecordIsNull(uint record, int field);
537
538 /// <summary>
539 /// PInvoke of MsiRecordGetInteger.
540 /// </summary>
541 /// <param name="record">MSI Record handle.</param>
542 /// <param name="field">Index of field to retrieve integer from.</param>
543 /// <returns>Integer value.</returns>
544 [DllImport("msi.dll", EntryPoint = "MsiRecordGetInteger", CharSet = CharSet.Unicode, ExactSpelling = true)]
545 internal static extern int MsiRecordGetInteger(uint record, int field);
546
547 /// <summary>
548 /// PInvoke of MsiRectordSetInteger.
549 /// </summary>
550 /// <param name="record">MSI Record handle.</param>
551 /// <param name="field">Index of field to set integer value in.</param>
552 /// <param name="value">Value to set field to.</param>
553 /// <returns>Error code.</returns>
554 [DllImport("msi.dll", EntryPoint = "MsiRecordSetInteger", CharSet = CharSet.Unicode, ExactSpelling = true)]
555 internal static extern int MsiRecordSetInteger(uint record, int field, int value);
556
557 /// <summary>
558 /// PInvoke of MsiRecordGetStringW.
559 /// </summary>
560 /// <param name="record">MSI Record handle.</param>
561 /// <param name="field">Index of field to get string value from.</param>
562 /// <param name="valueBuf">Buffer to recieve value.</param>
563 /// <param name="valueBufSize">Size of buffer.</param>
564 /// <returns>Error code.</returns>
565 [DllImport("msi.dll", EntryPoint = "MsiRecordGetStringW", CharSet = CharSet.Unicode, ExactSpelling = true)]
566 internal static extern int MsiRecordGetString(uint record, int field, StringBuilder valueBuf, ref int valueBufSize);
567
568 /// <summary>
569 /// PInvoke of MsiRecordSetStringW.
570 /// </summary>
571 /// <param name="record">MSI Record handle.</param>
572 /// <param name="field">Index of field to set string value in.</param>
573 /// <param name="value">String value.</param>
574 /// <returns>Error code.</returns>
575 [DllImport("msi.dll", EntryPoint = "MsiRecordSetStringW", CharSet = CharSet.Unicode, ExactSpelling = true)]
576 internal static extern int MsiRecordSetString(uint record, int field, string value);
577
578 /// <summary>
579 /// PInvoke of MsiRecordSetStreamW.
580 /// </summary>
581 /// <param name="record">MSI Record handle.</param>
582 /// <param name="field">Index of field to set stream value in.</param>
583 /// <param name="filePath">Path to file to set stream value to.</param>
584 /// <returns>Error code.</returns>
585 [DllImport("msi.dll", EntryPoint = "MsiRecordSetStreamW", CharSet = CharSet.Unicode, ExactSpelling = true)]
586 internal static extern int MsiRecordSetStream(uint record, int field, string filePath);
587
588 /// <summary>
589 /// PInvoke of MsiRecordReadStreamW.
590 /// </summary>
591 /// <param name="record">MSI Record handle.</param>
592 /// <param name="field">Index of field to read stream from.</param>
593 /// <param name="dataBuf">Data buffer to recieve stream value.</param>
594 /// <param name="dataBufSize">Size of data buffer.</param>
595 /// <returns>Error code.</returns>
596 [DllImport("msi.dll", EntryPoint = "MsiRecordReadStream", CharSet = CharSet.Unicode, ExactSpelling = true)]
597 internal static extern int MsiRecordReadStream(uint record, int field, byte[] dataBuf, ref int dataBufSize);
598
599 /// <summary>
600 /// PInvoke of MsiRecordGetFieldCount.
601 /// </summary>
602 /// <param name="record">MSI Record handle.</param>
603 /// <returns>Count of fields in the record.</returns>
604 [DllImport("msi.dll", EntryPoint = "MsiRecordGetFieldCount", CharSet = CharSet.Unicode, ExactSpelling = true)]
605 internal static extern int MsiRecordGetFieldCount(uint record);
606
607 /// <summary>
608 /// PInvoke of MsiSetExternalUIW.
609 /// </summary>
610 /// <param name="installUIHandler">Specifies a callback function that conforms to the INSTALLUI_HANDLER specification.</param>
611 /// <param name="installLogMode">Specifies which messages to handle using the external message handler. If the external
612 /// handler returns a non-zero result, then that message will not be sent to the UI, instead the message will be logged
613 /// if logging has been enabled.</param>
614 /// <param name="context">Pointer to an application context that is passed to the callback function.
615 /// This parameter can be used for error checking.</param>
616 /// <returns>The return value is the previously set external handler, or zero (0) if there was no previously set handler.</returns>
617 [DllImport("msi.dll", EntryPoint = "MsiSetExternalUIW", CharSet = CharSet.Unicode, ExactSpelling = true)]
618 internal static extern InstallUIHandler MsiSetExternalUI(InstallUIHandler installUIHandler, int installLogMode, IntPtr context);
619
620 /// <summary>
621 /// PInvoke of MsiSetInternalUI.
622 /// </summary>
623 /// <param name="uiLevel">Specifies the level of complexity of the user interface.</param>
624 /// <param name="hwnd">Pointer to a window. This window becomes the owner of any user interface created.
625 /// A pointer to the previous owner of the user interface is returned.
626 /// If this parameter is null, the owner of the user interface does not change.</param>
627 /// <returns>The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned.</returns>
628 [DllImport("msi.dll", EntryPoint = "MsiSetInternalUI", CharSet = CharSet.Unicode, ExactSpelling = true)]
629 internal static extern int MsiSetInternalUI(int uiLevel, ref IntPtr hwnd);
630
631 /// <summary>
632 /// PInvoke of MsiSummaryInfoGetPropertyW.
633 /// </summary>
634 /// <param name="summaryInfo">Handle to summary info.</param>
635 /// <param name="property">Property to get value from.</param>
636 /// <param name="dataType">Data type of property.</param>
637 /// <param name="integerValue">Integer to receive integer value.</param>
638 /// <param name="fileTimeValue">File time to receive file time value.</param>
639 /// <param name="stringValueBuf">String buffer to receive string value.</param>
640 /// <param name="stringValueBufSize">Size of string buffer.</param>
641 /// <returns>Error code.</returns>
642 [DllImport("msi.dll", EntryPoint = "MsiSummaryInfoGetPropertyW", CharSet = CharSet.Unicode, ExactSpelling = true)]
643 internal static extern int MsiSummaryInfoGetProperty(uint summaryInfo, int property, out uint dataType, out int integerValue, ref FILETIME fileTimeValue, StringBuilder stringValueBuf, ref int stringValueBufSize);
644
645 /// <summary>
646 /// PInvoke of MsiViewGetColumnInfo.
647 /// </summary>
648 /// <param name="view">Handle to view.</param>
649 /// <param name="columnInfo">Column info.</param>
650 /// <param name="record">Handle for returned record.</param>
651 /// <returns>Error code.</returns>
652 [DllImport("msi.dll", EntryPoint = "MsiViewGetColumnInfo", CharSet = CharSet.Unicode, ExactSpelling = true)]
653 internal static extern int MsiViewGetColumnInfo(uint view, int columnInfo, out uint record);
654
655 /// <summary>
656 /// PInvoke of MsiViewExecute.
657 /// </summary>
658 /// <param name="view">Handle of view to execute.</param>
659 /// <param name="record">Handle to a record that supplies the parameters for the view.</param>
660 /// <returns>Error code.</returns>
661 [DllImport("msi.dll", EntryPoint = "MsiViewExecute", CharSet = CharSet.Unicode, ExactSpelling = true)]
662 internal static extern int MsiViewExecute(uint view, uint record);
663
664 /// <summary>
665 /// PInvoke of MsiViewFetch.
666 /// </summary>
667 /// <param name="view">Handle of view to fetch a row from.</param>
668 /// <param name="record">Handle to receive record info.</param>
669 /// <returns>Error code.</returns>
670 [DllImport("msi.dll", EntryPoint = "MsiViewFetch", CharSet = CharSet.Unicode, ExactSpelling = true)]
671 internal static extern int MsiViewFetch(uint view, out uint record);
672
673 /// <summary>
674 /// PInvoke of MsiViewModify.
675 /// </summary>
676 /// <param name="view">Handle of view to modify.</param>
677 /// <param name="modifyMode">Modify mode.</param>
678 /// <param name="record">Handle of record.</param>
679 /// <returns>Error code.</returns>
680 [DllImport("msi.dll", EntryPoint = "MsiViewModify", CharSet = CharSet.Unicode, ExactSpelling = true)]
681 internal static extern int MsiViewModify(uint view, int modifyMode, uint record);
682
683 /// <summary>
684 /// contains the file hash information returned by MsiGetFileHash and used in the MsiFileHash table.
685 /// </summary>
686 [StructLayout(LayoutKind.Explicit)]
687 internal class MSIFILEHASHINFO
688 {
689 [FieldOffset(0)] internal uint FileHashInfoSize;
690 [FieldOffset(4)] internal int Data0;
691 [FieldOffset(8)] internal int Data1;
692 [FieldOffset(12)]internal int Data2;
693 [FieldOffset(16)]internal int Data3;
694 }
695 }
696}
697#endif \ No newline at end of file
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
new file mode 100644
index 00000000..b33bf27a
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
@@ -0,0 +1,78 @@
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.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using WixToolset.Core.Native;
8
9 /// <summary>
10 /// Exception that wraps MsiGetLastError().
11 /// </summary>
12 [Serializable]
13 public class MsiException : Win32Exception
14 {
15 /// <summary>
16 /// Instantiate a new MsiException with a given error.
17 /// </summary>
18 /// <param name="error">The error code from the MsiXxx() function call.</param>
19 public MsiException(int error) : base(error)
20 {
21 uint handle = MsiInterop.MsiGetLastErrorRecord();
22 if (0 != handle)
23 {
24 using (Record record = new Record(handle))
25 {
26 this.MsiError = record.GetInteger(1);
27
28 int errorInfoCount = record.GetFieldCount() - 1;
29 this.ErrorInfo = new string[errorInfoCount];
30 for (int i = 0; i < errorInfoCount; ++i)
31 {
32 this.ErrorInfo[i] = record.GetString(i + 2);
33 }
34 }
35 }
36 else
37 {
38 this.MsiError = 0;
39 this.ErrorInfo = new string[0];
40 }
41
42 this.Error = error;
43 }
44
45 /// <summary>
46 /// Gets the error number.
47 /// </summary>
48 public int Error { get; private set; }
49
50 /// <summary>
51 /// Gets the internal MSI error number.
52 /// </summary>
53 public int MsiError { get; private set; }
54
55 /// <summary>
56 /// Gets any additional the error information.
57 /// </summary>
58 public string[] ErrorInfo { get; private set; }
59
60 /// <summary>
61 /// Overrides Message property to return useful error message.
62 /// </summary>
63 public override string Message
64 {
65 get
66 {
67 if (0 == this.MsiError)
68 {
69 return base.Message;
70 }
71 else
72 {
73 return String.Format("Internal MSI failure. Win32 error: {0}, MSI error: {1}, detail: {2}", this.Error, this.MsiError, String.Join(", ", this.ErrorInfo));
74 }
75 }
76 }
77 }
78}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
new file mode 100644
index 00000000..6d2dc984
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
@@ -0,0 +1,116 @@
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.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using System.Diagnostics;
8 using System.Threading;
9 using WixToolset.Core.Native;
10
11 /// <summary>
12 /// Wrapper class for MSI handle.
13 /// </summary>
14 public class MsiHandle : IDisposable
15 {
16 private bool disposed;
17 private uint handle;
18 private int owningThread;
19#if DEBUG
20 private string creationStack;
21#endif
22
23 /// <summary>
24 /// MSI handle destructor.
25 /// </summary>
26 ~MsiHandle()
27 {
28 this.Dispose(false);
29 }
30
31 /// <summary>
32 /// Gets or sets the MSI handle.
33 /// </summary>
34 /// <value>The MSI handle.</value>
35 internal uint Handle
36 {
37 get
38 {
39 if (this.disposed)
40 {
41 throw new ObjectDisposedException("MsiHandle");
42 }
43
44 return this.handle;
45 }
46
47 set
48 {
49 if (this.disposed)
50 {
51 throw new ObjectDisposedException("MsiHandle");
52 }
53
54 this.handle = value;
55 this.owningThread = Thread.CurrentThread.ManagedThreadId;
56#if DEBUG
57 this.creationStack = Environment.StackTrace;
58#endif
59 }
60 }
61
62 /// <summary>
63 /// Close the MSI handle.
64 /// </summary>
65 public void Close()
66 {
67 this.Dispose();
68 }
69
70 /// <summary>
71 /// Disposes the managed and unmanaged objects in this object.
72 /// </summary>
73 public void Dispose()
74 {
75 this.Dispose(true);
76 GC.SuppressFinalize(this);
77 }
78
79 /// <summary>
80 /// Disposes the managed and unmanaged objects in this object.
81 /// </summary>
82 /// <param name="disposing">true to dispose the managed objects.</param>
83 protected virtual void Dispose(bool disposing)
84 {
85 if (!this.disposed)
86 {
87 if (0 != this.handle)
88 {
89 if (Thread.CurrentThread.ManagedThreadId == this.owningThread)
90 {
91 int error = MsiInterop.MsiCloseHandle(this.handle);
92 if (0 != error)
93 {
94 throw new Win32Exception(error);
95 }
96 this.handle = 0;
97 }
98 else
99 {
100 // Don't try to close the handle on a different thread than it was opened.
101 // This will occasionally cause MSI to AV.
102 string message = String.Format("Leaked msi handle {0} created on thread {1} by type {2}. This handle cannot be closed on thread {3}",
103 this.handle, this.owningThread, this.GetType(), Thread.CurrentThread.ManagedThreadId);
104#if DEBUG
105 throw new InvalidOperationException(String.Format("{0}. Created {1}", message, this.creationStack));
106#else
107 Debug.WriteLine(message);
108#endif
109 }
110 }
111
112 this.disposed = true;
113 }
114 }
115 }
116}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
new file mode 100644
index 00000000..438aa3b0
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
@@ -0,0 +1,182 @@
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.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using System.Text;
8 using WixToolset.Core.Native;
9
10 /// <summary>
11 /// Wrapper class around msi.dll interop for a record.
12 /// </summary>
13 public sealed class Record : MsiHandle
14 {
15 /// <summary>
16 /// Creates a record with the specified number of fields.
17 /// </summary>
18 /// <param name="fieldCount">Number of fields in record.</param>
19 public Record(int fieldCount)
20 {
21 this.Handle = MsiInterop.MsiCreateRecord(fieldCount);
22 if (0 == this.Handle)
23 {
24 throw new OutOfMemoryException();
25 }
26 }
27
28 /// <summary>
29 /// Creates a record from a handle.
30 /// </summary>
31 /// <param name="handle">Handle to create record from.</param>
32 internal Record(uint handle)
33 {
34 this.Handle = handle;
35 }
36
37 /// <summary>
38 /// Gets a string value at specified location.
39 /// </summary>
40 /// <param name="field">Index into record to get string.</param>
41 public string this[int field]
42 {
43 get { return this.GetString(field); }
44 set { this.SetString(field, (string)value); }
45 }
46
47 /// <summary>
48 /// Determines if the value is null at the specified location.
49 /// </summary>
50 /// <param name="field">Index into record of the field to query.</param>
51 /// <returns>true if the value is null, false otherwise.</returns>
52 public bool IsNull(int field)
53 {
54 int error = MsiInterop.MsiRecordIsNull(this.Handle, field);
55
56 switch (error)
57 {
58 case 0:
59 return false;
60 case 1:
61 return true;
62 default:
63 throw new Win32Exception(error);
64 }
65 }
66
67 /// <summary>
68 /// Gets integer value at specified location.
69 /// </summary>
70 /// <param name="field">Index into record to get integer</param>
71 /// <returns>Integer value</returns>
72 public int GetInteger(int field)
73 {
74 return MsiInterop.MsiRecordGetInteger(this.Handle, field);
75 }
76
77 /// <summary>
78 /// Sets integer value at specified location.
79 /// </summary>
80 /// <param name="field">Index into record to set integer.</param>
81 /// <param name="value">Value to set into record.</param>
82 public void SetInteger(int field, int value)
83 {
84 int error = MsiInterop.MsiRecordSetInteger(this.Handle, field, value);
85 if (0 != error)
86 {
87 throw new Win32Exception(error);
88 }
89 }
90
91 /// <summary>
92 /// Gets string value at specified location.
93 /// </summary>
94 /// <param name="field">Index into record to get string.</param>
95 /// <returns>String value</returns>
96 public string GetString(int field)
97 {
98 int bufferSize = 255;
99 StringBuilder buffer = new StringBuilder(bufferSize);
100 int error = MsiInterop.MsiRecordGetString(this.Handle, field, buffer, ref bufferSize);
101 if (234 == error)
102 {
103 buffer.EnsureCapacity(++bufferSize);
104 error = MsiInterop.MsiRecordGetString(this.Handle, field, buffer, ref bufferSize);
105 }
106
107 if (0 != error)
108 {
109 throw new Win32Exception(error);
110 }
111
112 return (0 < buffer.Length ? buffer.ToString() : null);
113 }
114
115 /// <summary>
116 /// Set string value at specified location
117 /// </summary>
118 /// <param name="field">Index into record to set string.</param>
119 /// <param name="value">Value to set into record</param>
120 public void SetString(int field, string value)
121 {
122 int error = MsiInterop.MsiRecordSetString(this.Handle, field, value);
123 if (0 != error)
124 {
125 throw new Win32Exception(error);
126 }
127 }
128
129 /// <summary>
130 /// Get stream at specified location.
131 /// </summary>
132 /// <param name="field">Index into record to get stream.</param>
133 /// <param name="buffer">buffer to receive bytes from stream.</param>
134 /// <param name="requestedBufferSize">Buffer size to read.</param>
135 /// <returns>Stream read into string.</returns>
136 public int GetStream(int field, byte[] buffer, int requestedBufferSize)
137 {
138 int bufferSize = 255;
139 if (requestedBufferSize > 0)
140 {
141 bufferSize = requestedBufferSize;
142 }
143
144 int error = MsiInterop.MsiRecordReadStream(this.Handle, field, buffer, ref bufferSize);
145 if (0 != error)
146 {
147 throw new Win32Exception(error);
148 }
149
150 return bufferSize;
151 }
152
153 /// <summary>
154 /// Sets a stream at a specified location.
155 /// </summary>
156 /// <param name="field">Index into record to set stream.</param>
157 /// <param name="path">Path to file to read into stream.</param>
158 public void SetStream(int field, string path)
159 {
160 int error = MsiInterop.MsiRecordSetStream(this.Handle, field, path);
161 if (0 != error)
162 {
163 throw new Win32Exception(error);
164 }
165 }
166
167 /// <summary>
168 /// Gets the number of fields in record.
169 /// </summary>
170 /// <returns>Count of fields in record.</returns>
171 public int GetFieldCount()
172 {
173 int size = MsiInterop.MsiRecordGetFieldCount(this.Handle);
174 if (0 > size)
175 {
176 throw new Win32Exception();
177 }
178
179 return size;
180 }
181 }
182}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
new file mode 100644
index 00000000..d3a19711
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
@@ -0,0 +1,45 @@
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.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using System.Globalization;
8 using WixToolset.Core.Native;
9
10 /// <summary>
11 /// Controls the installation process.
12 /// </summary>
13 internal sealed class Session : MsiHandle
14 {
15 /// <summary>
16 /// Instantiate a new Session.
17 /// </summary>
18 /// <param name="database">The database to open.</param>
19 public Session(Database database)
20 {
21 string packagePath = String.Format(CultureInfo.InvariantCulture, "#{0}", (uint)database.Handle);
22
23 uint handle = 0;
24 int error = MsiInterop.MsiOpenPackage(packagePath, out handle);
25 if (0 != error)
26 {
27 throw new MsiException(error);
28 }
29 this.Handle = handle;
30 }
31
32 /// <summary>
33 /// Executes a built-in action, custom action, or user-interface wizard action.
34 /// </summary>
35 /// <param name="action">Specifies the action to execute.</param>
36 public void DoAction(string action)
37 {
38 int error = MsiInterop.MsiDoAction(this.Handle, action);
39 if (0 != error)
40 {
41 throw new MsiException(error);
42 }
43 }
44 }
45}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs b/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
new file mode 100644
index 00000000..26831731
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
@@ -0,0 +1,245 @@
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.Msi
4{
5 using System;
6 using System.Globalization;
7 using System.Text;
8 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
9 using WixToolset.Core.Native;
10
11 /// <summary>
12 /// Summary information for the MSI files.
13 /// </summary>
14 internal sealed class SummaryInformation : MsiHandle
15 {
16 /// <summary>
17 /// Summary information properties for transforms.
18 /// </summary>
19 public enum Transform
20 {
21 /// <summary>PID_CODEPAGE = code page for the summary information stream</summary>
22 CodePage = 1,
23
24 /// <summary>PID_TITLE = typically just "Transform"</summary>
25 Title = 2,
26
27 /// <summary>PID_SUBJECT = original subject of target</summary>
28 TargetSubject = 3,
29
30 /// <summary>PID_AUTHOR = original manufacturer of target</summary>
31 TargetManufacturer = 4,
32
33 /// <summary>PID_KEYWORDS = keywords for the transform, typically including at least "Installer"</summary>
34 Keywords = 5,
35
36 /// <summary>PID_COMMENTS = describes what this package does</summary>
37 Comments = 6,
38
39 /// <summary>PID_TEMPLATE = target platform;language</summary>
40 TargetPlatformAndLanguage = 7,
41
42 /// <summary>PID_LASTAUTHOR = updated platform;language</summary>
43 UpdatedPlatformAndLanguage = 8,
44
45 /// <summary>PID_REVNUMBER = {productcode}version;{newproductcode}newversion;upgradecode</summary>
46 ProductCodes = 9,
47
48 /// <summary>PID_LASTPRINTED should be null for transforms</summary>
49 Reserved11 = 11,
50
51 ///.<summary>PID_CREATE_DTM = the timestamp when the transform was created</summary>
52 CreationTime = 12,
53
54 /// <summary>PID_PAGECOUNT = minimum installer version</summary>
55 InstallerRequirement = 14,
56
57 /// <summary>PID_CHARCOUNT = validation and error flags</summary>
58 ValidationFlags = 16,
59
60 /// <summary>PID_APPNAME = the application that created the transform</summary>
61 CreatingApplication = 18,
62
63 /// <summary>PID_SECURITY = whether read-only is enforced; should always be 4 for transforms</summary>
64 Security = 19,
65 }
66
67 /// <summary>
68 /// Summary information properties for patches.
69 /// </summary>
70 public enum Patch
71 {
72 /// <summary>PID_CODEPAGE = code page of the summary information stream</summary>
73 CodePage = 1,
74
75 /// <summary>PID_TITLE = a brief description of the package type</summary>
76 Title = 2,
77
78 /// <summary>PID_SUBJECT = package name</summary>
79 PackageName = 3,
80
81 /// <summary>PID_AUTHOR = manufacturer of the patch package</summary>
82 Manufacturer = 4,
83
84 /// <summary>PID_KEYWORDS = alternate sources for the patch package</summary>
85 Sources = 5,
86
87 /// <summary>PID_COMMENTS = general purpose of the patch package</summary>
88 Comments = 6,
89
90 /// <summary>PID_TEMPLATE = semicolon delimited list of ProductCodes</summary>
91 ProductCodes = 7,
92
93 /// <summary>PID_LASTAUTHOR = semicolon delimited list of transform names</summary>
94 TransformNames = 8,
95
96 /// <summary>PID_REVNUMBER = GUID patch code</summary>
97 PatchCode = 9,
98
99 /// <summary>PID_LASTPRINTED should be null for patches</summary>
100 Reserved11 = 11,
101
102 /// <summary>PID_PAGECOUNT should be null for patches</summary>
103 Reserved14 = 14,
104
105 /// <summary>PID_WORDCOUNT = minimum installer version</summary>
106 InstallerRequirement = 15,
107
108 /// <summary>PID_CHARCOUNT should be null for patches</summary>
109 Reserved16 = 16,
110
111 /// <summary>PID_SECURITY = read-only attribute of the patch package</summary>
112 Security = 19,
113 }
114
115 /// <summary>
116 /// Summary information values for the InstallerRequirement property.
117 /// </summary>
118 public enum InstallerRequirement
119 {
120 /// <summary>Any version of the installer will do</summary>
121 Version10 = 1,
122
123 /// <summary>At least 1.2</summary>
124 Version12 = 2,
125
126 /// <summary>At least 2.0</summary>
127 Version20 = 3,
128
129 /// <summary>At least 3.0</summary>
130 Version30 = 4,
131
132 /// <summary>At least 3.1</summary>
133 Version31 = 5,
134 }
135
136 /// <summary>
137 /// Instantiate a new SummaryInformation class from an open database.
138 /// </summary>
139 /// <param name="db">Database to retrieve summary information from.</param>
140 public SummaryInformation(Database db)
141 {
142 if (null == db)
143 {
144 throw new ArgumentNullException("db");
145 }
146
147 uint handle = 0;
148 int error = MsiInterop.MsiGetSummaryInformation(db.Handle, null, 0, ref handle);
149 if (0 != error)
150 {
151 throw new MsiException(error);
152 }
153 this.Handle = handle;
154 }
155
156 /// <summary>
157 /// Instantiate a new SummaryInformation class from a database file.
158 /// </summary>
159 /// <param name="databaseFile">The database file.</param>
160 public SummaryInformation(string databaseFile)
161 {
162 if (null == databaseFile)
163 {
164 throw new ArgumentNullException("databaseFile");
165 }
166
167 uint handle = 0;
168 int error = MsiInterop.MsiGetSummaryInformation(0, databaseFile, 0, ref handle);
169 if (0 != error)
170 {
171 throw new MsiException(error);
172 }
173 this.Handle = handle;
174 }
175
176 /// <summary>
177 /// Variant types in the summary information table.
178 /// </summary>
179 private enum VT : uint
180 {
181 /// <summary>Variant has not been assigned.</summary>
182 EMPTY = 0,
183
184 /// <summary>Null variant type.</summary>
185 NULL = 1,
186
187 /// <summary>16-bit integer variant type.</summary>
188 I2 = 2,
189
190 /// <summary>32-bit integer variant type.</summary>
191 I4 = 3,
192
193 /// <summary>String variant type.</summary>
194 LPSTR = 30,
195
196 /// <summary>Date time (FILETIME, converted to Variant time) variant type.</summary>
197 FILETIME = 64,
198 }
199
200 /// <summary>
201 /// Gets a summary information property.
202 /// </summary>
203 /// <param name="index">Index of the summary information property.</param>
204 /// <returns>The summary information property.</returns>
205 public string GetProperty(int index)
206 {
207 uint dataType;
208 StringBuilder stringValue = new StringBuilder("");
209 int bufSize = 0;
210 int intValue;
211 FILETIME timeValue;
212 timeValue.dwHighDateTime = 0;
213 timeValue.dwLowDateTime = 0;
214
215 int error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
216 if (234 == error)
217 {
218 stringValue.EnsureCapacity(++bufSize);
219 error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
220 }
221
222 if (0 != error)
223 {
224 throw new MsiException(error);
225 }
226
227 switch ((VT)dataType)
228 {
229 case VT.EMPTY:
230 return String.Empty;
231 case VT.LPSTR:
232 return stringValue.ToString();
233 case VT.I2:
234 case VT.I4:
235 return Convert.ToString(intValue, CultureInfo.InvariantCulture);
236 case VT.FILETIME:
237 long longFileTime = (((long)timeValue.dwHighDateTime) << 32) | unchecked((uint)timeValue.dwLowDateTime);
238 DateTime dateTime = DateTime.FromFileTime(longFileTime);
239 return dateTime.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture);
240 default:
241 throw new InvalidOperationException();
242 }
243 }
244 }
245}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/View.cs b/src/WixToolset.Core.WindowsInstaller/Msi/View.cs
new file mode 100644
index 00000000..d6542824
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Msi/View.cs
@@ -0,0 +1,189 @@
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.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using System.Globalization;
8 using WixToolset.Core.Native;
9
10 /// <summary>
11 /// Enumeration of different modify modes.
12 /// </summary>
13 public enum ModifyView
14 {
15 /// <summary>
16 /// Writes current data in the cursor to a table row. Updates record if the primary
17 /// keys match an existing row and inserts if they do not match. Fails with a read-only
18 /// database. This mode cannot be used with a view containing joins.
19 /// </summary>
20 Assign = MsiInterop.MSIMODIFYASSIGN,
21
22 /// <summary>
23 /// Remove a row from the table. You must first call the Fetch function with the same
24 /// record. Fails if the row has been deleted. Works only with read-write records. This
25 /// mode cannot be used with a view containing joins.
26 /// </summary>
27 Delete = MsiInterop.MSIMODIFYDELETE,
28
29 /// <summary>
30 /// Inserts a record. Fails if a row with the same primary keys exists. Fails with a read-only
31 /// database. This mode cannot be used with a view containing joins.
32 /// </summary>
33 Insert = MsiInterop.MSIMODIFYINSERT,
34
35 /// <summary>
36 /// Inserts a temporary record. The information is not persistent. Fails if a row with the
37 /// same primary key exists. Works only with read-write records. This mode cannot be
38 /// used with a view containing joins.
39 /// </summary>
40 InsertTemporary = MsiInterop.MSIMODIFYINSERTTEMPORARY,
41
42 /// <summary>
43 /// Inserts or validates a record in a table. Inserts if primary keys do not match any row
44 /// and validates if there is a match. Fails if the record does not match the data in
45 /// the table. Fails if there is a record with a duplicate key that is not identical.
46 /// Works only with read-write records. This mode cannot be used with a view containing joins.
47 /// </summary>
48 Merge = MsiInterop.MSIMODIFYMERGE,
49
50 /// <summary>
51 /// Refreshes the information in the record. Must first call Fetch with the
52 /// same record. Fails for a deleted row. Works with read-write and read-only records.
53 /// </summary>
54 Refresh = MsiInterop.MSIMODIFYREFRESH,
55
56 /// <summary>
57 /// Updates or deletes and inserts a record into a table. Must first call Fetch with
58 /// the same record. Updates record if the primary keys are unchanged. Deletes old row and
59 /// inserts new if primary keys have changed. Fails with a read-only database. This mode cannot
60 /// be used with a view containing joins.
61 /// </summary>
62 Replace = MsiInterop.MSIMODIFYREPLACE,
63
64 /// <summary>
65 /// Refreshes the information in the supplied record without changing the position in the
66 /// result set and without affecting subsequent fetch operations. The record may then
67 /// be used for subsequent Update, Delete, and Refresh. All primary key columns of the
68 /// table must be in the query and the record must have at least as many fields as the
69 /// query. Seek cannot be used with multi-table queries. This mode cannot be used with
70 /// a view containing joins. See also the remarks.
71 /// </summary>
72 Seek = MsiInterop.MSIMODIFYSEEK,
73
74 /// <summary>
75 /// Updates an existing record. Non-primary keys only. Must first call Fetch. Fails with a
76 /// deleted record. Works only with read-write records.
77 /// </summary>
78 Update = MsiInterop.MSIMODIFYUPDATE
79 }
80
81 /// <summary>
82 /// Wrapper class for MSI API views.
83 /// </summary>
84 internal sealed class View : MsiHandle
85 {
86 /// <summary>
87 /// Constructor that creates a view given a database handle and a query.
88 /// </summary>
89 /// <param name="db">Handle to the database to run the query on.</param>
90 /// <param name="query">Query to be executed.</param>
91 public View(Database db, string query)
92 {
93 if (null == db)
94 {
95 throw new ArgumentNullException("db");
96 }
97
98 if (null == query)
99 {
100 throw new ArgumentNullException("query");
101 }
102
103 uint handle = 0;
104
105 int error = MsiInterop.MsiDatabaseOpenView(db.Handle, query, out handle);
106 if (0 != error)
107 {
108 throw new MsiException(error);
109 }
110
111 this.Handle = handle;
112 }
113
114 /// <summary>
115 /// Executes a view with no customizable parameters.
116 /// </summary>
117 public void Execute()
118 {
119 this.Execute(null);
120 }
121
122 /// <summary>
123 /// Executes a query substituing the values from the records into the customizable parameters
124 /// in the view.
125 /// </summary>
126 /// <param name="record">Record containing parameters to be substituded into the view.</param>
127 public void Execute(Record record)
128 {
129 int error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle);
130 if (0 != error)
131 {
132 throw new MsiException(error);
133 }
134 }
135
136 /// <summary>
137 /// Fetches the next row in the view.
138 /// </summary>
139 /// <returns>Returns the fetched record; otherwise null.</returns>
140 public Record Fetch()
141 {
142 uint recordHandle;
143
144 int error = MsiInterop.MsiViewFetch(this.Handle, out recordHandle);
145 if (259 == error)
146 {
147 return null;
148 }
149 else if (0 != error)
150 {
151 throw new MsiException(error);
152 }
153
154 return new Record(recordHandle);
155 }
156
157 /// <summary>
158 /// Updates a fetched record.
159 /// </summary>
160 /// <param name="type">Type of modification mode.</param>
161 /// <param name="record">Record to be modified.</param>
162 public void Modify(ModifyView type, Record record)
163 {
164 int error = MsiInterop.MsiViewModify(this.Handle, Convert.ToInt32(type, CultureInfo.InvariantCulture), record.Handle);
165 if (0 != error)
166 {
167 throw new MsiException(error);
168 }
169 }
170
171 /// <summary>
172 /// Returns a record containing column names or definitions.
173 /// </summary>
174 /// <param name="columnType">Specifies a flag indicating what type of information is needed. Either MSICOLINFO_NAMES or MSICOLINFO_TYPES.</param>
175 /// <returns>The record containing information about the column.</returns>
176 public Record GetColumnInfo(int columnType)
177 {
178 uint recordHandle;
179
180 int error = MsiInterop.MsiViewGetColumnInfo(this.Handle, columnType, out recordHandle);
181 if (0 != error)
182 {
183 throw new MsiException(error);
184 }
185
186 return new Record(recordHandle);
187 }
188 }
189}