aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs
new file mode 100644
index 00000000..08f00867
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs
@@ -0,0 +1,223 @@
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;
7 using System.Runtime.InteropServices;
8 using System.Diagnostics.CodeAnalysis;
9
10 /// <summary>
11 /// Defines a callback function that the installer calls for progress notification and error messages.
12 /// </summary>
13 public delegate MessageResult ExternalUIHandler(
14 InstallMessage messageType,
15 string message,
16 MessageButtons buttons,
17 MessageIcon icon,
18 MessageDefaultButton defaultButton);
19
20 /// <summary>
21 /// [MSI 3.1] Defines a callback function that the installer calls for record-based progress notification and error messages.
22 /// </summary>
23 public delegate MessageResult ExternalUIRecordHandler(
24 InstallMessage messageType,
25 Record messageRecord,
26 MessageButtons buttons,
27 MessageIcon icon,
28 MessageDefaultButton defaultButton);
29
30 internal delegate int NativeExternalUIHandler(IntPtr context, int messageType, [MarshalAs(UnmanagedType.LPWStr)] string message);
31
32 internal delegate int NativeExternalUIRecordHandler(IntPtr context, int messageType, int recordHandle);
33
34 internal class ExternalUIProxy
35 {
36 private ExternalUIHandler handler;
37
38 internal ExternalUIProxy(ExternalUIHandler handler)
39 {
40 this.handler = handler;
41 }
42
43 public ExternalUIHandler Handler
44 {
45 get { return this.handler; }
46 }
47
48 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
49 public int ProxyHandler(IntPtr contextPtr, int messageType, [MarshalAs(UnmanagedType.LPWStr)] string message)
50 {
51 try
52 {
53 int msgType = messageType & 0x7F000000;
54 int buttons = messageType & 0x0000000F;
55 int icon = messageType & 0x000000F0;
56 int defButton = messageType & 0x00000F00;
57
58 return (int) this.handler(
59 (InstallMessage) msgType,
60 message,
61 (MessageButtons) buttons,
62 (MessageIcon) icon,
63 (MessageDefaultButton) defButton);
64 }
65 catch
66 {
67 return (int) MessageResult.Error;
68 }
69 }
70 }
71
72 internal class ExternalUIRecordProxy
73 {
74 private ExternalUIRecordHandler handler;
75
76 internal ExternalUIRecordProxy(ExternalUIRecordHandler handler)
77 {
78 this.handler = handler;
79 }
80
81 public ExternalUIRecordHandler Handler
82 {
83 get { return this.handler; }
84 }
85
86 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
87 public int ProxyHandler(IntPtr contextPtr, int messageType, int recordHandle)
88 {
89 try
90 {
91 int msgType = messageType & 0x7F000000;
92 int buttons = messageType & 0x0000000F;
93 int icon = messageType & 0x000000F0;
94 int defButton = messageType & 0x00000F00;
95
96 Record msgRec = (recordHandle != 0 ? Record.FromHandle((IntPtr) recordHandle, false) : null);
97 using (msgRec)
98 {
99 return (int) this.handler(
100 (InstallMessage) msgType,
101 msgRec,
102 (MessageButtons) buttons,
103 (MessageIcon) icon,
104 (MessageDefaultButton) defButton);
105 }
106 }
107 catch
108 {
109 return (int) MessageResult.Error;
110 }
111 }
112 }
113
114 public static partial class Installer
115 {
116 private static IList externalUIHandlers = ArrayList.Synchronized(new ArrayList());
117
118 /// <summary>
119 /// Enables an external user-interface handler. This external UI handler is called before the
120 /// normal internal user-interface handler. The external UI handler has the option to suppress
121 /// the internal UI by returning a non-zero value to indicate that it has handled the messages.
122 /// </summary>
123 /// <param name="uiHandler">A callback delegate that handles the UI messages</param>
124 /// <param name="messageFilter">Specifies which messages to handle using the external message handler.
125 /// If the external handler returns a non-zero result, then that message will not be sent to the UI,
126 /// instead the message will be logged if logging has been enabled.</param>
127 /// <returns>The previously set external handler, or null if there was no previously set handler</returns>
128 /// <remarks><p>
129 /// To restore the previous UI handler, a second call is made to SetExternalUI using the
130 /// ExternalUIHandler returned by the first call to SetExternalUI and specifying
131 /// <see cref="InstallLogModes.None"/> as the message filter.
132 /// </p><p>
133 /// The external user interface handler does not have full control over the external user
134 /// interface unless <see cref="SetInternalUI(InstallUIOptions)"/> is called with the uiLevel parameter set to
135 /// <see cref="InstallUIOptions.Silent"/>. If SetInternalUI is not called, the internal user
136 /// interface level defaults to <see cref="InstallUIOptions.Basic"/>. As a result, any message not
137 /// handled by the external user interface handler is handled by Windows Installer. The initial
138 /// "Preparing to install..." dialog always appears even if the external user interface
139 /// handler handles all messages.
140 /// </p><p>
141 /// SetExternalUI should only be called from a bootstrapping application. You cannot call
142 /// it from a custom action
143 /// </p><p>
144 /// Win32 MSI API:
145 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetexternalui.asp">MsiSetExternalUI</a>
146 /// </p></remarks>
147 public static ExternalUIHandler SetExternalUI(ExternalUIHandler uiHandler, InstallLogModes messageFilter)
148 {
149 NativeExternalUIHandler nativeHandler = null;
150 if (uiHandler != null)
151 {
152 nativeHandler = new ExternalUIProxy(uiHandler).ProxyHandler;
153 Installer.externalUIHandlers.Add(nativeHandler);
154 }
155 NativeExternalUIHandler oldNativeHandler = NativeMethods.MsiSetExternalUI(nativeHandler, (uint) messageFilter, IntPtr.Zero);
156 if (oldNativeHandler != null && oldNativeHandler.Target is ExternalUIProxy)
157 {
158 Installer.externalUIHandlers.Remove(oldNativeHandler);
159 return ((ExternalUIProxy) oldNativeHandler.Target).Handler;
160 }
161 else
162 {
163 return null;
164 }
165 }
166
167 /// <summary>
168 /// [MSI 3.1] Enables a record-based external user-interface handler. This external UI handler is called
169 /// before the normal internal user-interface handler. The external UI handler has the option to suppress
170 /// the internal UI by returning a non-zero value to indicate that it has handled the messages.
171 /// </summary>
172 /// <param name="uiHandler">A callback delegate that handles the UI messages</param>
173 /// <param name="messageFilter">Specifies which messages to handle using the external message handler.
174 /// If the external handler returns a non-zero result, then that message will not be sent to the UI,
175 /// instead the message will be logged if logging has been enabled.</param>
176 /// <returns>The previously set external handler, or null if there was no previously set handler</returns>
177 /// <remarks><p>
178 /// To restore the previous UI handler, a second call is made to SetExternalUI using the
179 /// ExternalUIHandler returned by the first call to SetExternalUI and specifying
180 /// <see cref="InstallLogModes.None"/> as the message filter.
181 /// </p><p>
182 /// The external user interface handler does not have full control over the external user
183 /// interface unless <see cref="SetInternalUI(InstallUIOptions)"/> is called with the uiLevel parameter set to
184 /// <see cref="InstallUIOptions.Silent"/>. If SetInternalUI is not called, the internal user
185 /// interface level defaults to <see cref="InstallUIOptions.Basic"/>. As a result, any message not
186 /// handled by the external user interface handler is handled by Windows Installer. The initial
187 /// "Preparing to install..." dialog always appears even if the external user interface
188 /// handler handles all messages.
189 /// </p><p>
190 /// SetExternalUI should only be called from a bootstrapping application. You cannot call
191 /// it from a custom action
192 /// </p><p>
193 /// Win32 MSI API:
194 /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetexternaluirecord.asp">MsiSetExternalUIRecord</a>
195 /// </p></remarks>
196 public static ExternalUIRecordHandler SetExternalUI(ExternalUIRecordHandler uiHandler, InstallLogModes messageFilter)
197 {
198 NativeExternalUIRecordHandler nativeHandler = null;
199 if (uiHandler != null)
200 {
201 nativeHandler = new ExternalUIRecordProxy(uiHandler).ProxyHandler;
202 Installer.externalUIHandlers.Add(nativeHandler);
203 }
204 NativeExternalUIRecordHandler oldNativeHandler;
205 uint ret = NativeMethods.MsiSetExternalUIRecord(nativeHandler, (uint) messageFilter, IntPtr.Zero, out oldNativeHandler);
206 if (ret != 0)
207 {
208 Installer.externalUIHandlers.Remove(nativeHandler);
209 throw InstallerException.ExceptionFromReturnCode(ret);
210 }
211
212 if (oldNativeHandler != null && oldNativeHandler.Target is ExternalUIRecordProxy)
213 {
214 Installer.externalUIHandlers.Remove(oldNativeHandler);
215 return ((ExternalUIRecordProxy) oldNativeHandler.Target).Handler;
216 }
217 else
218 {
219 return null;
220 }
221 }
222 }
223}