diff options
Diffstat (limited to '')
-rw-r--r-- | src/dtf/WixToolset.Dtf.WindowsInstaller/Transaction.cs | 201 |
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 | |||
3 | namespace 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 | } | ||