aboutsummaryrefslogtreecommitdiff
path: root/src/tools/burn/ManagedBundleRunner/BundleRunner.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/burn/ManagedBundleRunner/BundleRunner.cs')
-rw-r--r--src/tools/burn/ManagedBundleRunner/BundleRunner.cs212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/tools/burn/ManagedBundleRunner/BundleRunner.cs b/src/tools/burn/ManagedBundleRunner/BundleRunner.cs
new file mode 100644
index 00000000..98d92c66
--- /dev/null
+++ b/src/tools/burn/ManagedBundleRunner/BundleRunner.cs
@@ -0,0 +1,212 @@
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.Tools
4{
5 using System;
6 using System.Diagnostics;
7 using System.IO.Pipes;
8 using System.Text;
9 using System.Threading;
10
11 /// <summary>
12 /// Runs a bundle with provided command-line.
13 /// </summary>
14 public class BundleRunner
15 {
16 /// <summary>
17 /// Creates a runner for the provided bundle.
18 /// </summary>
19 /// <param name="bundle">Path to the bundle to run.</param>
20 public BundleRunner(string bundle)
21 {
22 this.Path = bundle;
23 }
24
25 /// <summary>
26 /// Fired when the bundle encounters an error.
27 /// </summary>
28 public event EventHandler<BundleErrorEventArgs> Error;
29
30 /// <summary>
31 /// Fired when the bundle progress is udpated.
32 /// </summary>
33 public event EventHandler<BundleProgressEventArgs> Progress;
34
35 /// <summary>
36 /// Gets the path to the bundle to run.
37 /// </summary>
38 public string Path { get; private set; }
39
40 /// <summary>
41 /// Runs the bundle with the provided command-line.
42 /// </summary>
43 /// <param name="commandLine">Optional command-line to pass to the bundle.</param>
44 /// <returns>Exit code from the bundle.</returns>
45 public int Run(string commandLine = null)
46 {
47 WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) };
48 int returnCode = 0;
49 int pid = Process.GetCurrentProcess().Id;
50 string pipeName = String.Concat("bpe_", pid);
51 string pipeSecret = Guid.NewGuid().ToString("N");
52
53 using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1))
54 {
55 using (Process bundleProcess = new Process())
56 {
57 bundleProcess.StartInfo.FileName = this.Path;
58 bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid);
59 bundleProcess.StartInfo.UseShellExecute = false;
60 bundleProcess.StartInfo.CreateNoWindow = true;
61 bundleProcess.Start();
62
63 Connect(pipe, pipeSecret, pid, bundleProcess.Id);
64
65 PumpMessages(pipe);
66
67 bundleProcess.WaitForExit();
68 returnCode = bundleProcess.ExitCode;
69 }
70 }
71
72 return returnCode;
73 }
74
75 /// <summary>
76 /// Called when bundle encounters an error.
77 /// </summary>
78 /// <param name="e">Additional arguments for this event.</param>
79 protected virtual void OnError(BundleErrorEventArgs e)
80 {
81 EventHandler<BundleErrorEventArgs> handler = this.Error;
82 if (handler != null)
83 {
84 handler(this, e);
85 }
86 }
87
88 /// <summary>
89 /// Called when bundle progress is updated.
90 /// </summary>
91 /// <param name="e">Additional arguments for this event.</param>
92 protected virtual void OnProgress(BundleProgressEventArgs e)
93 {
94 EventHandler<BundleProgressEventArgs> handler = this.Progress;
95 if (handler != null)
96 {
97 handler(this, e);
98 }
99 }
100
101 private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid)
102 {
103 pipe.WaitForConnection();
104
105 WriteSecretToPipe(pipe, pipeSecret);
106
107 WriteNumberToPipe(pipe, (uint)pid);
108
109 uint ack = ReadNumberFromPipe(pipe);
110 // This is not true when bundle is run under a debugger
111 //if (ack != childPid)
112 //{
113 // throw new ApplicationException("Incorrect child process.");
114 //}
115 }
116
117 private void PumpMessages(NamedPipeServerStream pipe)
118 {
119 uint messageId;
120 while (TryReadNumberFromPipe(pipe, out messageId))
121 {
122 uint messageSize = ReadNumberFromPipe(pipe);
123
124 BundleResult result = BundleResult.None;
125 switch (messageId)
126 {
127 case 1: //error
128 result = ProcessErrorMessage(pipe);
129 break;
130
131 case 2: // progress
132 result = ProcessProgressMessage(pipe);
133 break;
134
135 default: // unknown message, do nothing.
136 break;
137 }
138
139 CompleteMessage(pipe, result);
140 }
141 }
142
143 private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe)
144 {
145 BundleErrorEventArgs e = new BundleErrorEventArgs();
146 e.Code = (int)ReadNumberFromPipe(pipe);
147 e.Message = ReadStringFromPipe(pipe);
148 e.UIHint = (int)ReadNumberFromPipe(pipe);
149
150 this.OnError(e);
151
152 return e.Result;
153 }
154
155 private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe)
156 {
157 ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero.
158
159 BundleProgressEventArgs e = new BundleProgressEventArgs();
160 e.Progress = (int)ReadNumberFromPipe(pipe);
161
162 this.OnProgress(e);
163
164 return e.Result;
165 }
166
167 private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result)
168 {
169 uint complete = 0xF0000002;
170 WriteNumberToPipe(pipe, complete);
171 WriteNumberToPipe(pipe, 4); // size of message data
172 WriteNumberToPipe(pipe, (uint)result);
173 }
174
175 private uint ReadNumberFromPipe(NamedPipeServerStream pipe)
176 {
177 byte[] buffer = new byte[4];
178 pipe.Read(buffer, 0, buffer.Length);
179 return BitConverter.ToUInt32(buffer, 0);
180 }
181
182 private string ReadStringFromPipe(NamedPipeServerStream pipe)
183 {
184 uint length = ReadNumberFromPipe(pipe);
185
186 byte[] buffer = new byte[length * 2];
187 pipe.Read(buffer, 0, buffer.Length);
188
189 return Encoding.Unicode.GetString(buffer);
190 }
191
192 private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value)
193 {
194 value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected.
195 return pipe.IsConnected;
196 }
197
198 private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value)
199 {
200 byte[] buffer = BitConverter.GetBytes(value);
201 pipe.Write(buffer, 0, buffer.Length);
202 }
203
204 private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret)
205 {
206 byte[] buffer = Encoding.Unicode.GetBytes(secret);
207
208 WriteNumberToPipe(pipe, (uint)buffer.Length);
209 pipe.Write(buffer, 0, buffer.Length);
210 }
211 }
212}