1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// UtilMessageBox.cpp
6//
7
8//
9// This module contains the message box utility code for the CLR. It is used
10// by code in the CLR itself as well as other tools that build in the CLR tree.
11// For message boxes inside the ExecutionEngine, EEMessageBox must be used
12// instead of the these APIs.
13//
14//*****************************************************************************
15#include "stdafx.h" // Standard header.
16#include <utilcode.h> // Utility helpers.
17#include <corerror.h>
18#include "ndpversion.h"
19#include "../dlls/mscorrc/resource.h"
20#include "ex.h"
21#if !defined(FEATURE_CORESYSTEM)
22#undef NTDDI_VERSION
23#define NTDDI_VERSION NTDDI_WIN7
24#include "commctrl.h"
25#endif
26
27
28BOOL ShouldDisplayMsgBoxOnCriticalFailure()
29{
30 CONTRACTL
31 {
32 NOTHROW;
33 }
34 CONTRACTL_END;
35
36#ifdef _DEBUG
37 // To help find issues, we will always display dialogs for critical failures
38 // under debug builds. This includes asserts and other critical issues.
39 return TRUE;
40#else
41 // Retrieve error mode
42 UINT last = SetErrorMode(0);
43 SetErrorMode(last); //set back to previous value
44
45 // SEM_FAILCRITICALERRORS indicates that the system does not display the critical-error-handler
46 // message box. Instead, the system sends the error to the calling process.
47 return !(last & SEM_FAILCRITICALERRORS);
48#endif // _DEBUG
49}
50
51
52
53
54// We'd like to use TaskDialogIndirect for asserts coming from managed code in particular
55// to display the detailedText in a scrollable way. Also, we'd like to reuse the CLR's
56// plumbing code for the rest of parts of the assert dialog. Note that the simple
57// Win32 MessageBox does not support the detailedText value.
58// If we later refactor MessageBoxImpl into its own DLL, move the lines referencing
59// "Microsoft.Windows.Common-Controls" version 6 in stdafx.h as well.
60int MessageBoxImpl(
61 HWND hWnd, // Handle to Owner Window
62 LPCWSTR message, // Message
63 LPCWSTR title, // Dialog box title
64 LPCWSTR detailedText, // Details like a stack trace, etc.
65 UINT uType)
66{
67 CONTRACTL
68 {
69 // May pump messages. Callers should be GC_TRIGGERS and MODE_PREEMPTIVE,
70 // but we can't include EE contracts here.
71 THROWS;
72 INJECT_FAULT(return IDCANCEL;);
73
74 // Assert if none of MB_ICON is set
75 PRECONDITION((uType & MB_ICONMASK) != 0);
76 }
77 CONTRACTL_END;
78
79 return WszMessageBox(hWnd, message, title, uType);
80}
81
82int UtilMessageBoxVA(
83 HWND hWnd, // Handle to Owner Window
84 UINT uText, // Resource Identifier for Text message
85 UINT uTitle, // Resource Identifier for Title
86 UINT uType, // Style of MessageBox
87 BOOL displayForNonInteractive, // Display even if the process is running non interactive
88 BOOL showFileNameInTitle, // Flag to show FileName in Caption
89 va_list args) // Additional Arguments
90{
91 CONTRACTL
92 {
93 NOTHROW;
94 INJECT_FAULT(return IDCANCEL;);
95 }
96 CONTRACTL_END;
97
98 SString text;
99 SString title;
100 int result = IDCANCEL;
101
102 EX_TRY
103 {
104 text.LoadResource(CCompRC::Error, uText);
105 title.LoadResource(CCompRC::Error, uTitle);
106
107 result = UtilMessageBoxNonLocalizedVA(hWnd, (LPWSTR)text.GetUnicode(),
108 (LPWSTR)title.GetUnicode(), uType, displayForNonInteractive, showFileNameInTitle, NULL, args);
109 }
110 EX_CATCH
111 {
112 result = IDCANCEL;
113 }
114 EX_END_CATCH(SwallowAllExceptions);
115
116 return result;
117}
118
119int UtilMessageBoxNonLocalizedVA(
120 HWND hWnd, // Handle to Owner Window
121 LPCWSTR lpText, // Text message
122 LPCWSTR lpTitle, // Title
123 UINT uType, // Style of MessageBox
124 BOOL displayForNonInteractive, // Display even if the process is running non interactive
125 BOOL showFileNameInTitle, // Flag to show FileName in Caption
126 BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort.
127 va_list args) // Additional Arguments
128{
129 CONTRACTL
130 {
131 NOTHROW;
132 INJECT_FAULT(return IDCANCEL;);
133
134 // Assert if none of MB_ICON is set
135 PRECONDITION((uType & MB_ICONMASK) != 0);
136 }
137 CONTRACTL_END;
138
139 return UtilMessageBoxNonLocalizedVA(hWnd, lpText, lpTitle, NULL, uType, displayForNonInteractive, showFileNameInTitle, pInputFromUser, args);
140}
141
142int UtilMessageBoxNonLocalizedVA(
143 HWND hWnd, // Handle to Owner Window
144 LPCWSTR lpText, // Text message
145 LPCWSTR lpTitle, // Title
146 LPCWSTR lpDetails,// Details like a stack trace, etc.
147 UINT uType, // Style of MessageBox
148 BOOL displayForNonInteractive, // Display even if the process is running non interactive
149 BOOL showFileNameInTitle, // Flag to show FileName in Caption
150 BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort.
151 va_list args) // Additional Arguments
152{
153 CONTRACTL
154 {
155 NOTHROW;
156 INJECT_FAULT(return IDCANCEL;);
157
158 // Assert if none of MB_ICON is set
159 PRECONDITION((uType & MB_ICONMASK) != 0);
160 }
161 CONTRACTL_END;
162
163 int result = IDCANCEL;
164 if (pInputFromUser != NULL)
165 {
166 *pInputFromUser = FALSE;
167 }
168
169 EX_TRY
170 {
171 StackSString formattedMessage;
172 StackSString formattedTitle;
173 SString details(lpDetails);
174 PathString fileName;
175 BOOL fDisplayMsgBox = TRUE;
176
177 // Format message string using optional parameters
178 formattedMessage.VPrintf(lpText, args);
179
180 // Try to get filename of Module and add it to title
181 if (showFileNameInTitle && WszGetModuleFileName(NULL, fileName))
182 {
183 LPCWSTR wszName = NULL;
184 size_t cchName = 0;
185
186
187
188 SplitPathInterior(fileName, NULL, NULL, NULL, NULL, &wszName, &cchName, NULL, NULL);
189 formattedTitle.Printf(W("%s - %s"), wszName, lpTitle);
190 }
191 else
192 {
193 formattedTitle.Set(lpTitle);
194 }
195
196#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
197 // If the current process isn't interactive (a service for example), then we report the message
198 // in the event log and via OutputDebugString.
199 //
200 // We may still however attempt to display the message box if the MB_SERVICE_NOTIFICATION
201 // message box style was specified.
202 if (!RunningInteractive())
203 {
204 StackSString message;
205
206 message.Printf(W(".NET Runtime version : %s - "), VER_FILEVERSION_STR_L);
207 if (lpTitle)
208 message.Append(lpTitle);
209 if (!formattedMessage.IsEmpty())
210 message.Append(formattedMessage);
211
212 ClrReportEvent(W(".NET Runtime"),
213 EVENTLOG_ERROR_TYPE, // event type
214 0, // category zero
215 1024, // event identifier
216 NULL, // no user security identifier
217 message.GetUnicode());
218
219 if(lpTitle != NULL)
220 WszOutputDebugString(lpTitle);
221 if(!formattedMessage.IsEmpty())
222 WszOutputDebugString(formattedMessage);
223
224 // If we are running as a service and displayForNonInteractive is FALSE then IDABORT is
225 // the best value to return as it will most likely cause callers of this API to abort the process.
226 // This is the right thing to do since attaching a debugger doesn't make much sense when the process isn't
227 // running in interactive mode.
228 if(!displayForNonInteractive)
229 {
230 fDisplayMsgBox = FALSE;
231 result = IDABORT;
232 }
233 else
234 {
235 // Include in the MB_DEFAULT_DESKTOP_ONLY style.
236 uType |= MB_DEFAULT_DESKTOP_ONLY;
237 }
238 }
239#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
240
241 if (fDisplayMsgBox)
242 {
243 // We normally want to set the reading direction (right-to-left etc.) based on the resources
244 // in use. However, outside the CLR (SELF_NO_HOST) we can't assume we have resources and
245 // in CORECLR we can't even necessarily expect that our CLR callbacks have been initialized.
246 // This code path is used for ASSERT dialogs.
247
248 result = MessageBoxImpl(hWnd, formattedMessage, formattedTitle, details, uType);
249
250 if (pInputFromUser != NULL)
251 {
252 *pInputFromUser = TRUE;
253 }
254 }
255 }
256 EX_CATCH
257 {
258 result = IDCANCEL;
259 }
260 EX_END_CATCH(SwallowAllExceptions);
261
262 return result;
263}
264
265int UtilMessageBox(
266 HWND hWnd, // Handle to Owner Window
267 UINT uText, // Resource Identifier for Text message
268 UINT uTitle, // Resource Identifier for Title
269 UINT uType, // Style of MessageBox
270 BOOL displayForNonInteractive, // Display even if the process is running non interactive
271 BOOL showFileNameInTitle, // Flag to show FileName in Caption
272 ...) // Additional Arguments
273{
274 CONTRACTL
275 {
276 NOTHROW;
277 }
278 CONTRACTL_END;
279
280 va_list marker;
281 va_start(marker, showFileNameInTitle);
282
283 int result = UtilMessageBoxVA(hWnd, uText, uTitle, uType, displayForNonInteractive, showFileNameInTitle, marker);
284 va_end( marker );
285
286 return result;
287}
288
289int UtilMessageBoxNonLocalized(
290 HWND hWnd, // Handle to Owner Window
291 LPCWSTR lpText, // Text message
292 LPCWSTR lpTitle, // Title message
293 UINT uType, // Style of MessageBox
294 BOOL displayForNonInteractive, // Display even if the process is running non interactive
295 BOOL showFileNameInTitle, // Flag to show FileName in Caption
296 ... ) // Additional Arguments
297{
298 CONTRACTL
299 {
300 NOTHROW;
301 }
302 CONTRACTL_END;
303
304 va_list marker;
305 va_start(marker, showFileNameInTitle);
306
307 int result = UtilMessageBoxNonLocalizedVA(
308 hWnd, lpText, lpTitle, uType, displayForNonInteractive, showFileNameInTitle, NULL, marker);
309 va_end( marker );
310
311 return result;
312}
313
314int UtilMessageBoxCatastrophic(
315 UINT uText, // Text for MessageBox
316 UINT uTitle, // Title for MessageBox
317 UINT uType, // Style of MessageBox
318 BOOL showFileNameInTitle, // Flag to show FileName in Caption
319 ...)
320{
321 CONTRACTL
322 {
323 NOTHROW;
324 }
325 CONTRACTL_END;
326
327 va_list marker;
328 va_start(marker, showFileNameInTitle);
329
330 int result = UtilMessageBoxCatastrophicVA(uText, uTitle, uType, showFileNameInTitle, marker);
331 va_end( marker );
332
333 return result;
334}
335
336int UtilMessageBoxCatastrophicNonLocalized(
337 LPCWSTR lpText, // Text for MessageBox
338 LPCWSTR lpTitle, // Title for MessageBox
339 UINT uType, // Style of MessageBox
340 BOOL showFileNameInTitle, // Flag to show FileName in Caption
341 ...)
342{
343 CONTRACTL
344 {
345 NOTHROW;
346 }
347 CONTRACTL_END;
348
349 va_list marker;
350 va_start(marker, showFileNameInTitle);
351
352 int result = UtilMessageBoxCatastrophicNonLocalizedVA(lpText, lpTitle, uType, showFileNameInTitle, marker);
353 va_end( marker );
354
355 return result;
356}
357
358int UtilMessageBoxCatastrophicVA(
359 UINT uText, // Text for MessageBox
360 UINT uTitle, // Title for MessageBox
361 UINT uType, // Style of MessageBox
362 BOOL showFileNameInTitle, // Flag to show FileName in Caption
363 va_list args) // Additional Arguments
364{
365 CONTRACTL
366 {
367 NOTHROW;
368 }
369 CONTRACTL_END;
370
371 HWND hwnd = NULL;
372
373 // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going.
374 CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
375
376 if (!ShouldDisplayMsgBoxOnCriticalFailure())
377 return IDABORT;
378
379 // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
380 // owned by the current thread and should prevent interaction with them until dismissed.
381 uType |= MB_TASKMODAL;
382
383 return UtilMessageBoxVA(hwnd, uText, uTitle, uType, TRUE, showFileNameInTitle, args);
384}
385
386int UtilMessageBoxCatastrophicNonLocalizedVA(
387 LPCWSTR lpText, // Text for MessageBox
388 LPCWSTR lpTitle, // Title for MessageBox
389 UINT uType, // Style of MessageBox
390 BOOL showFileNameInTitle, // Flag to show FileName in Caption
391 va_list args) // Additional Arguments
392{
393 CONTRACTL
394 {
395 NOTHROW;
396 }
397 CONTRACTL_END;
398
399 HWND hwnd = NULL;
400
401 // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going.
402 CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
403
404 if (!ShouldDisplayMsgBoxOnCriticalFailure())
405 return IDABORT;
406
407 // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
408 // owned by the current thread and should prevent interaction with them until dismissed.
409 uType |= MB_TASKMODAL;
410
411 return UtilMessageBoxNonLocalizedVA(hwnd, lpText, lpTitle, uType, TRUE, showFileNameInTitle, NULL, args);
412}
413
414