Merge pull request #14430 from noahfalk/fix_14428
[platform/upstream/coreclr.git] / src / utilcode / utilmessagebox.cpp
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
28 BOOL 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.  
60 int 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
82 int 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
119 int 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
142 int 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             HANDLE h;
205             StackSString message;
206
207             message.Printf(W(".NET Runtime version : %s - "), VER_FILEVERSION_STR_L);
208             if (lpTitle)
209                 message.Append(lpTitle);
210             if (!formattedMessage.IsEmpty())
211                 message.Append(formattedMessage);
212
213             ClrReportEvent(W(".NET Runtime"),
214                 EVENTLOG_ERROR_TYPE,    // event type 
215                 0,                      // category zero
216                 1024,                   // event identifier
217                 NULL,                   // no user security identifier
218                 message.GetUnicode());
219             
220             if(lpTitle != NULL)
221                 WszOutputDebugString(lpTitle);
222             if(!formattedMessage.IsEmpty())
223                 WszOutputDebugString(formattedMessage);
224
225             // If we are running as a service and displayForNonInteractive is FALSE then IDABORT is 
226             // the best value to return as it will most likely cause callers of this API to abort the process. 
227             // This is the right thing to do since attaching a debugger doesn't make much sense when the process isn't
228             // running in interactive mode.
229             if(!displayForNonInteractive)   
230             {
231                 fDisplayMsgBox = FALSE;
232                 result = IDABORT;
233             }
234             else
235             {
236                 // Include in the MB_DEFAULT_DESKTOP_ONLY style.
237                 uType |= MB_DEFAULT_DESKTOP_ONLY;                            
238             }
239         }
240 #endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
241
242         if (fDisplayMsgBox)
243         {
244             // We normally want to set the reading direction (right-to-left etc.) based on the resources
245             // in use.  However, outside the CLR (SELF_NO_HOST) we can't assume we have resources and
246             // in CORECLR we can't even necessarily expect that our CLR callbacks have been initialized.
247             // This code path is used for ASSERT dialogs.
248             
249             result = MessageBoxImpl(hWnd, formattedMessage, formattedTitle, details, uType);
250             
251             if (pInputFromUser != NULL)
252             {
253                 *pInputFromUser = TRUE;
254             }
255         }
256     }        
257     EX_CATCH
258     {
259         result = IDCANCEL;
260     }
261     EX_END_CATCH(SwallowAllExceptions);
262
263     return result;
264 }
265
266 int UtilMessageBox(
267                   HWND hWnd,        // Handle to Owner Window
268                   UINT uText,       // Resource Identifier for Text message
269                   UINT uTitle,      // Resource Identifier for Title
270                   UINT uType,       // Style of MessageBox
271                   BOOL displayForNonInteractive,    // Display even if the process is running non interactive 
272                   BOOL showFileNameInTitle,         // Flag to show FileName in Caption
273                   ...)              // Additional Arguments
274 {
275     CONTRACTL
276     {
277         NOTHROW;
278     }
279     CONTRACTL_END;
280
281     va_list marker;
282     va_start(marker, showFileNameInTitle);
283
284     int result = UtilMessageBoxVA(hWnd, uText, uTitle, uType, displayForNonInteractive, showFileNameInTitle, marker);
285     va_end( marker );
286
287     return result;    
288 }
289
290 int UtilMessageBoxNonLocalized(
291                   HWND hWnd,        // Handle to Owner Window
292                   LPCWSTR lpText,   // Text message
293                   LPCWSTR lpTitle,  // Title message
294                   UINT uType,       // Style of MessageBox
295                   BOOL displayForNonInteractive,    // Display even if the process is running non interactive 
296                   BOOL showFileNameInTitle,         // Flag to show FileName in Caption
297                   ... )             // Additional Arguments
298 {
299     CONTRACTL
300     {
301         NOTHROW;
302     }
303     CONTRACTL_END;
304
305     va_list marker;
306     va_start(marker, showFileNameInTitle);
307
308     int result = UtilMessageBoxNonLocalizedVA(
309         hWnd, lpText, lpTitle, uType, displayForNonInteractive, showFileNameInTitle, NULL, marker);
310     va_end( marker );
311
312     return result;
313 }
314
315 int UtilMessageBoxCatastrophic(
316                   UINT uText,       // Text for MessageBox
317                   UINT uTitle,      // Title for MessageBox
318                   UINT uType,       // Style of MessageBox
319                   BOOL showFileNameInTitle,         // Flag to show FileName in Caption
320                   ...)
321 {
322     CONTRACTL
323     {
324         NOTHROW;
325     }
326     CONTRACTL_END;
327
328     va_list marker;
329     va_start(marker, showFileNameInTitle);
330
331     int result = UtilMessageBoxCatastrophicVA(uText, uTitle, uType, showFileNameInTitle, marker);
332     va_end( marker );
333
334     return result;
335 }
336
337 int UtilMessageBoxCatastrophicNonLocalized(
338                   LPCWSTR lpText,    // Text for MessageBox
339                   LPCWSTR lpTitle,   // Title for MessageBox
340                   UINT uType,        // Style of MessageBox
341                   BOOL showFileNameInTitle,         // Flag to show FileName in Caption
342                   ...)
343 {
344     CONTRACTL
345     {
346         NOTHROW;
347     }
348     CONTRACTL_END;
349
350     va_list marker;
351     va_start(marker, showFileNameInTitle);
352
353     int result = UtilMessageBoxCatastrophicNonLocalizedVA(lpText, lpTitle, uType, showFileNameInTitle, marker);
354     va_end( marker );
355
356     return result;
357 }
358
359 int UtilMessageBoxCatastrophicVA(
360                   UINT uText,       // Text for MessageBox
361                   UINT uTitle,      // Title for MessageBox
362                   UINT uType,       // Style of MessageBox
363                   BOOL showFileNameInTitle,         // Flag to show FileName in Caption
364                   va_list args)     // Additional Arguments
365 {
366     CONTRACTL
367     {
368         NOTHROW;
369     }
370     CONTRACTL_END;
371
372     HWND hwnd = NULL;
373
374     // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going. 
375     CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
376
377     if (!ShouldDisplayMsgBoxOnCriticalFailure())
378         return IDABORT;
379
380     // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
381     // owned by the current thread and should prevent interaction with them until dismissed.
382     uType |= MB_TASKMODAL;
383
384     return UtilMessageBoxVA(hwnd, uText, uTitle, uType, TRUE, showFileNameInTitle, args);
385 }
386
387 int UtilMessageBoxCatastrophicNonLocalizedVA(
388                   LPCWSTR lpText,   // Text for MessageBox
389                   LPCWSTR lpTitle,  // Title for MessageBox
390                   UINT uType,       // Style of MessageBox
391                   BOOL showFileNameInTitle, // Flag to show FileName in Caption
392                   va_list args)     // Additional Arguments
393 {
394     CONTRACTL
395     {
396         NOTHROW;
397     }
398     CONTRACTL_END;
399
400     HWND hwnd = NULL;
401
402     // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going. 
403     CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
404
405     if (!ShouldDisplayMsgBoxOnCriticalFailure())
406         return IDABORT;
407
408     // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
409     // owned by the current thread and should prevent interaction with them until dismissed.
410     uType |= MB_TASKMODAL;
411
412     return UtilMessageBoxNonLocalizedVA(hwnd, lpText, lpTitle, uType, TRUE, showFileNameInTitle, NULL, args);
413 }
414