aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller/Transaction.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller/Transaction.cs201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/Transaction.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/Transaction.cs
new file mode 100644
index 00000000..798dc79e
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/Transaction.cs
@@ -0,0 +1,201 @@
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.Dtf.WindowsInstaller
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Runtime.InteropServices;
9 using System.Threading;
10
11 /// <summary>
12 /// [MSI 4.5] Handle to a multi-session install transaction.
13 /// </summary>
14 /// <remarks><p>
15 /// Win32 MSI APIs:
16 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msibegintransaction.asp">MsiBeginTransaction</a>
17 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msijointransaction.asp">MsiJoinTransaction</a>
18 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiendtransaction.asp">MsiEndTransaction</a>
19 /// </p></remarks>
20 public class Transaction : InstallerHandle
21 {
22 private string name;
23 private IntPtr ownerChangeEvent;
24 private IList<EventHandler<EventArgs>> ownerChangeListeners;
25
26 /// <summary>
27 /// [MSI 4.5] Begins transaction processing of a multi-package installation.
28 /// </summary>
29 /// <param name="name">Name of the multi-package installation.</param>
30 /// <param name="attributes">Select optional behavior when beginning the transaction.</param>
31 /// <exception cref="InstallerException">The transaction could not be initialized.</exception>
32 /// <remarks><p>
33 /// Win32 MSI API:
34 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msibegintransaction.asp">MsiBeginTransaction</a>
35 /// </p></remarks>
36 public Transaction(string name, TransactionAttributes attributes)
37 : this(name, Transaction.Begin(name, attributes), true)
38 {
39 }
40
41 /// <summary>
42 /// Internal constructor.
43 /// </summary>
44 /// <remarks>
45 /// The second parameter is an array in order to receive multiple values from the initialization method.
46 /// </remarks>
47 private Transaction(string name, IntPtr[] handles, bool ownsHandle)
48 : base(handles[0], ownsHandle)
49 {
50 this.name = name;
51 this.ownerChangeEvent = handles[1];
52 this.ownerChangeListeners = new List<EventHandler<EventArgs>>();
53 }
54
55 /// <summary>
56 /// Creates a new Transaction object from an integer handle.
57 /// </summary>
58 /// <param name="handle">Integer transaction handle</param>
59 /// <param name="ownsHandle">true to close the handle when this object is disposed</param>
60 public static Transaction FromHandle(IntPtr handle, bool ownsHandle)
61 {
62 return new Transaction(handle.ToString(), new IntPtr[] { handle, IntPtr.Zero }, ownsHandle);
63 }
64
65 /// <summary>
66 /// Gets the name of the transaction.
67 /// </summary>
68 public string Name
69 {
70 get
71 {
72 return name;
73 }
74 }
75
76 /// <summary>
77 /// Notifies listeners when the process that owns the transaction changed.
78 /// </summary>
79 public event EventHandler<EventArgs> OwnerChanged
80 {
81 add
82 {
83 this.ownerChangeListeners.Add(value);
84
85 if (this.ownerChangeEvent != IntPtr.Zero && this.ownerChangeListeners.Count == 1)
86 {
87 new Thread(this.WaitForOwnerChange).Start();
88 }
89 }
90 remove
91 {
92 this.ownerChangeListeners.Remove(value);
93 }
94 }
95
96 private void OnOwnerChanged()
97 {
98 EventArgs e = new EventArgs();
99 foreach (EventHandler<EventArgs> handler in this.ownerChangeListeners)
100 {
101 handler(this, e);
102 }
103 }
104
105 private void WaitForOwnerChange()
106 {
107 int ret = NativeMethods.WaitForSingleObject(this.ownerChangeEvent, -1);
108 if (ret == 0)
109 {
110 this.OnOwnerChanged();
111 }
112 else
113 {
114 throw new InstallerException();
115 }
116 }
117
118 /// <summary>
119 /// Makes the current process the owner of the multi-package installation transaction.
120 /// </summary>
121 /// <param name="attributes">Select optional behavior when joining the transaction.</param>
122 /// <exception cref="InvalidHandleException">The transaction handle is not valid.</exception>
123 /// <exception cref="InstallerException">The transaction could not be joined.</exception>
124 /// <remarks><p>
125 /// Win32 MSI API:
126 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msijointransaction.asp">MsiJoinTransaction</a>
127 /// </p></remarks>
128 public void Join(TransactionAttributes attributes)
129 {
130 IntPtr hChangeOfOwnerEvent;
131 uint ret = NativeMethods.MsiJoinTransaction((int) this.Handle, (int) attributes, out hChangeOfOwnerEvent);
132 if (ret != 0)
133 {
134 throw InstallerException.ExceptionFromReturnCode(ret);
135 }
136
137 this.ownerChangeEvent = hChangeOfOwnerEvent;
138 if (this.ownerChangeEvent != IntPtr.Zero && this.ownerChangeListeners.Count >= 1)
139 {
140 new Thread(this.WaitForOwnerChange).Start();
141 }
142 }
143
144 /// <summary>
145 /// Ends the install transaction and commits all changes to the system belonging to the transaction.
146 /// </summary>
147 /// <exception cref="InstallerException">The transaction could not be committed.</exception>
148 /// <remarks><p>
149 /// Runs any Commit Custom Actions and commits to the system any changes to Win32 or common language
150 /// runtime assemblies. Deletes the rollback script, and after using this option, the transaction's
151 /// changes can no longer be undone with a Rollback Installation.
152 /// </p><p>
153 /// This method can only be called by the current owner of the transaction.
154 /// </p><p>
155 /// Win32 MSI API:
156 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiendtransaction.asp">MsiEndTransaction</a>
157 /// </p></remarks>
158 public void Commit()
159 {
160 this.End(true);
161 }
162
163 /// <summary>
164 /// Ends the install transaction and undoes changes to the system belonging to the transaction.
165 /// </summary>
166 /// <exception cref="InstallerException">The transaction could not be rolled back.</exception>
167 /// <remarks><p>
168 /// This method can only be called by the current owner of the transaction.
169 /// </p><p>
170 /// Win32 MSI API:
171 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiendtransaction.asp">MsiEndTransaction</a>
172 /// </p></remarks>
173 public void Rollback()
174 {
175 this.End(false);
176 }
177
178 private static IntPtr[] Begin(string transactionName, TransactionAttributes attributes)
179 {
180 int hTransaction;
181 IntPtr hChangeOfOwnerEvent;
182 uint ret = NativeMethods.MsiBeginTransaction(transactionName, (int) attributes, out hTransaction, out hChangeOfOwnerEvent);
183 if (ret != 0)
184 {
185 throw InstallerException.ExceptionFromReturnCode(ret);
186 }
187
188 return new IntPtr[] { (IntPtr) hTransaction, hChangeOfOwnerEvent };
189 }
190
191 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
192 private void End(bool commit)
193 {
194 uint ret = NativeMethods.MsiEndTransaction(commit ? 1 : 0);
195 if (ret != 0)
196 {
197 throw InstallerException.ExceptionFromReturnCode(ret);
198 }
199 }
200 }
201}