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