1*04e0dc4aSTimo Kreuzer /***
2*04e0dc4aSTimo Kreuzer *report_runtime_error.cpp - startup error messages
3*04e0dc4aSTimo Kreuzer *
4*04e0dc4aSTimo Kreuzer *       Copyright (c) Microsoft Corporation. All rights reserved.
5*04e0dc4aSTimo Kreuzer *
6*04e0dc4aSTimo Kreuzer *Purpose:
7*04e0dc4aSTimo Kreuzer *       Prints out banner for runtime error messages.
8*04e0dc4aSTimo Kreuzer *
9*04e0dc4aSTimo Kreuzer *******************************************************************************/
10*04e0dc4aSTimo Kreuzer 
11*04e0dc4aSTimo Kreuzer #include <corecrt_internal.h>
12*04e0dc4aSTimo Kreuzer #include <stdlib.h>
13*04e0dc4aSTimo Kreuzer 
14*04e0dc4aSTimo Kreuzer 
15*04e0dc4aSTimo Kreuzer 
16*04e0dc4aSTimo Kreuzer // This is used during the expansion of the runtime error text.
17*04e0dc4aSTimo Kreuzer #define EOL L"\r\n"
18*04e0dc4aSTimo Kreuzer 
issue_debug_notification(wchar_t const * const message)19*04e0dc4aSTimo Kreuzer static bool __cdecl issue_debug_notification(wchar_t const* const message) throw()
20*04e0dc4aSTimo Kreuzer {
21*04e0dc4aSTimo Kreuzer     // This is referenced only in the Debug CRT build
22*04e0dc4aSTimo Kreuzer     UNREFERENCED_PARAMETER(message);
23*04e0dc4aSTimo Kreuzer 
24*04e0dc4aSTimo Kreuzer #ifdef _DEBUG
25*04e0dc4aSTimo Kreuzer     switch (_CrtDbgReportW(_CRT_ERROR, nullptr, 0, nullptr, L"%ls", message))
26*04e0dc4aSTimo Kreuzer     {
27*04e0dc4aSTimo Kreuzer     case 1:
28*04e0dc4aSTimo Kreuzer         _CrtDbgBreak();
29*04e0dc4aSTimo Kreuzer         return true;
30*04e0dc4aSTimo Kreuzer 
31*04e0dc4aSTimo Kreuzer     case 0:
32*04e0dc4aSTimo Kreuzer         return true;
33*04e0dc4aSTimo Kreuzer     }
34*04e0dc4aSTimo Kreuzer #endif // _DEBUG
35*04e0dc4aSTimo Kreuzer 
36*04e0dc4aSTimo Kreuzer     return false;
37*04e0dc4aSTimo Kreuzer }
38*04e0dc4aSTimo Kreuzer 
39*04e0dc4aSTimo Kreuzer 
40*04e0dc4aSTimo Kreuzer 
41*04e0dc4aSTimo Kreuzer 
42*04e0dc4aSTimo Kreuzer // Enclaves do not support error messages outside of OutputDebugString.
43*04e0dc4aSTimo Kreuzer #ifdef _UCRT_ENCLAVE_BUILD
44*04e0dc4aSTimo Kreuzer 
__acrt_report_runtime_error(wchar_t const * const message)45*04e0dc4aSTimo Kreuzer extern "C" void __cdecl __acrt_report_runtime_error(wchar_t const* const message)
46*04e0dc4aSTimo Kreuzer {
47*04e0dc4aSTimo Kreuzer     // Report the error using the debug
48*04e0dc4aSTimo Kreuzer     issue_debug_notification(message);
49*04e0dc4aSTimo Kreuzer }
50*04e0dc4aSTimo Kreuzer 
51*04e0dc4aSTimo Kreuzer #else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */
52*04e0dc4aSTimo Kreuzer 
53*04e0dc4aSTimo Kreuzer /*
54*04e0dc4aSTimo Kreuzer  * __acrt_app_type, together with __error_mode, determine how error messages
55*04e0dc4aSTimo Kreuzer  * are written out.
56*04e0dc4aSTimo Kreuzer  */
57*04e0dc4aSTimo Kreuzer static _crt_app_type __acrt_app_type = _crt_unknown_app;
58*04e0dc4aSTimo Kreuzer 
59*04e0dc4aSTimo Kreuzer /***
60*04e0dc4aSTimo Kreuzer *void _set_app_type(int apptype) - interface to change __acrt_app_type
61*04e0dc4aSTimo Kreuzer *
62*04e0dc4aSTimo Kreuzer *Purpose:
63*04e0dc4aSTimo Kreuzer *       Set, or change, the value of __acrt_app_type.
64*04e0dc4aSTimo Kreuzer *
65*04e0dc4aSTimo Kreuzer *       Set the default debug lib report destination for console apps.
66*04e0dc4aSTimo Kreuzer *
67*04e0dc4aSTimo Kreuzer *       This function is for INTERNAL USE ONLY.
68*04e0dc4aSTimo Kreuzer *
69*04e0dc4aSTimo Kreuzer *Entry:
70*04e0dc4aSTimo Kreuzer *       int modeval =   _crt_unknown_app,   unknown
71*04e0dc4aSTimo Kreuzer *                       _crt_console_app,   console, or command line, application
72*04e0dc4aSTimo Kreuzer *                       _crt_gui_app,       GUI, or Windows, application
73*04e0dc4aSTimo Kreuzer *
74*04e0dc4aSTimo Kreuzer *Exit:
75*04e0dc4aSTimo Kreuzer *
76*04e0dc4aSTimo Kreuzer *Exceptions:
77*04e0dc4aSTimo Kreuzer *
78*04e0dc4aSTimo Kreuzer *******************************************************************************/
79*04e0dc4aSTimo Kreuzer 
_set_app_type(_crt_app_type const new_app_type)80*04e0dc4aSTimo Kreuzer extern "C" void __cdecl _set_app_type(_crt_app_type const new_app_type)
81*04e0dc4aSTimo Kreuzer {
82*04e0dc4aSTimo Kreuzer     __acrt_app_type = new_app_type;
83*04e0dc4aSTimo Kreuzer }
84*04e0dc4aSTimo Kreuzer 
_query_app_type()85*04e0dc4aSTimo Kreuzer extern "C" _crt_app_type __cdecl _query_app_type()
86*04e0dc4aSTimo Kreuzer {
87*04e0dc4aSTimo Kreuzer     return __acrt_app_type;
88*04e0dc4aSTimo Kreuzer }
89*04e0dc4aSTimo Kreuzer 
90*04e0dc4aSTimo Kreuzer 
91*04e0dc4aSTimo Kreuzer 
should_write_error_to_console()92*04e0dc4aSTimo Kreuzer static bool __cdecl should_write_error_to_console() throw()
93*04e0dc4aSTimo Kreuzer {
94*04e0dc4aSTimo Kreuzer     int const error_mode = _set_error_mode(_REPORT_ERRMODE);
95*04e0dc4aSTimo Kreuzer 
96*04e0dc4aSTimo Kreuzer     if (error_mode == _OUT_TO_STDERR)
97*04e0dc4aSTimo Kreuzer     {
98*04e0dc4aSTimo Kreuzer         return true;
99*04e0dc4aSTimo Kreuzer     }
100*04e0dc4aSTimo Kreuzer 
101*04e0dc4aSTimo Kreuzer     if (error_mode == _OUT_TO_DEFAULT && __acrt_app_type == _crt_console_app)
102*04e0dc4aSTimo Kreuzer     {
103*04e0dc4aSTimo Kreuzer         return true;
104*04e0dc4aSTimo Kreuzer     }
105*04e0dc4aSTimo Kreuzer 
106*04e0dc4aSTimo Kreuzer     return false;
107*04e0dc4aSTimo Kreuzer }
108*04e0dc4aSTimo Kreuzer 
109*04e0dc4aSTimo Kreuzer 
110*04e0dc4aSTimo Kreuzer 
write_string_to_console(wchar_t const * const wide_string)111*04e0dc4aSTimo Kreuzer static void write_string_to_console(wchar_t const* const wide_string) throw()
112*04e0dc4aSTimo Kreuzer {
113*04e0dc4aSTimo Kreuzer     HANDLE const handle = GetStdHandle(STD_ERROR_HANDLE);
114*04e0dc4aSTimo Kreuzer     if (handle == nullptr || handle == INVALID_HANDLE_VALUE)
115*04e0dc4aSTimo Kreuzer     {
116*04e0dc4aSTimo Kreuzer         return;
117*04e0dc4aSTimo Kreuzer     }
118*04e0dc4aSTimo Kreuzer 
119*04e0dc4aSTimo Kreuzer     // We convert the wide string to a narrow string by truncating each character.
120*04e0dc4aSTimo Kreuzer     // Currently, the text for each runtime error consists only of ASCII, so this
121*04e0dc4aSTimo Kreuzer     // is acceptable.  If the error text is ever localized, this would need to
122*04e0dc4aSTimo Kreuzer     // change.
123*04e0dc4aSTimo Kreuzer     size_t const narrow_buffer_count = 500;
124*04e0dc4aSTimo Kreuzer     char narrow_buffer[narrow_buffer_count];
125*04e0dc4aSTimo Kreuzer 
126*04e0dc4aSTimo Kreuzer     char* const narrow_first = narrow_buffer;
127*04e0dc4aSTimo Kreuzer     char* const narrow_last  = narrow_first + narrow_buffer_count;
128*04e0dc4aSTimo Kreuzer 
129*04e0dc4aSTimo Kreuzer     // Note that this loop copies the null terminator if the loop terminates
130*04e0dc4aSTimo Kreuzer     // befoe running out of buffer space:
131*04e0dc4aSTimo Kreuzer     char*          narrow_it = narrow_first;
132*04e0dc4aSTimo Kreuzer     wchar_t const* wide_it   = wide_string;
133*04e0dc4aSTimo Kreuzer     do
134*04e0dc4aSTimo Kreuzer     {
135*04e0dc4aSTimo Kreuzer         *narrow_it = static_cast<char>(*wide_it);
136*04e0dc4aSTimo Kreuzer     }
137*04e0dc4aSTimo Kreuzer     while (++narrow_it != narrow_last && *wide_it++ != '\0');
138*04e0dc4aSTimo Kreuzer 
139*04e0dc4aSTimo Kreuzer     // If we did run out of buffer space, this will null-terminate the text that
140*04e0dc4aSTimo Kreuzer     // we were able to copy:
141*04e0dc4aSTimo Kreuzer     *(narrow_last - 1) = '\0';
142*04e0dc4aSTimo Kreuzer 
143*04e0dc4aSTimo Kreuzer     DWORD const bytes_to_write = static_cast<DWORD>(narrow_it - narrow_first - 1); // Account for null terminator
144*04e0dc4aSTimo Kreuzer     DWORD       bytes_written  = 0;
145*04e0dc4aSTimo Kreuzer     WriteFile(handle, narrow_buffer, bytes_to_write, &bytes_written, nullptr);
146*04e0dc4aSTimo Kreuzer }
147*04e0dc4aSTimo Kreuzer 
148*04e0dc4aSTimo Kreuzer 
149*04e0dc4aSTimo Kreuzer 
__acrt_report_runtime_error(wchar_t const * const message)150*04e0dc4aSTimo Kreuzer extern "C" void __cdecl __acrt_report_runtime_error(wchar_t const* const message)
151*04e0dc4aSTimo Kreuzer {
152*04e0dc4aSTimo Kreuzer     // Before we report the error via the normal path, report the error using
153*04e0dc4aSTimo Kreuzer     // the debug
154*04e0dc4aSTimo Kreuzer     if (issue_debug_notification(message))
155*04e0dc4aSTimo Kreuzer     {
156*04e0dc4aSTimo Kreuzer         return;
157*04e0dc4aSTimo Kreuzer     }
158*04e0dc4aSTimo Kreuzer 
159*04e0dc4aSTimo Kreuzer     if (should_write_error_to_console())
160*04e0dc4aSTimo Kreuzer     {
161*04e0dc4aSTimo Kreuzer         write_string_to_console(message);
162*04e0dc4aSTimo Kreuzer     }
163*04e0dc4aSTimo Kreuzer     else
164*04e0dc4aSTimo Kreuzer     {
165*04e0dc4aSTimo Kreuzer         #define MSGTEXTPREFIX L"Runtime Error!\n\nProgram: "
166*04e0dc4aSTimo Kreuzer         static wchar_t outmsg[sizeof(MSGTEXTPREFIX) / sizeof(wchar_t) + _MAX_PATH + 2 + 500];
167*04e0dc4aSTimo Kreuzer         // runtime error msg + progname + 2 newline + runtime error text.
168*04e0dc4aSTimo Kreuzer         wchar_t* progname = &outmsg[sizeof(MSGTEXTPREFIX) / sizeof(wchar_t) - 1];
169*04e0dc4aSTimo Kreuzer         size_t progname_size = _countof(outmsg) - (progname - outmsg);
170*04e0dc4aSTimo Kreuzer         wchar_t* pch = progname;
171*04e0dc4aSTimo Kreuzer 
172*04e0dc4aSTimo Kreuzer         _ERRCHECK(wcscpy_s(outmsg, _countof(outmsg), MSGTEXTPREFIX));
173*04e0dc4aSTimo Kreuzer 
174*04e0dc4aSTimo Kreuzer         progname[MAX_PATH] = L'\0';
175*04e0dc4aSTimo Kreuzer         if (!GetModuleFileNameW(nullptr, progname, MAX_PATH))
176*04e0dc4aSTimo Kreuzer         {
177*04e0dc4aSTimo Kreuzer             _ERRCHECK(wcscpy_s(progname, progname_size, L"<program name unknown>"));
178*04e0dc4aSTimo Kreuzer         }
179*04e0dc4aSTimo Kreuzer 
180*04e0dc4aSTimo Kreuzer         #define MAXLINELEN 60
181*04e0dc4aSTimo Kreuzer         if (wcslen(pch) + 1 > MAXLINELEN)
182*04e0dc4aSTimo Kreuzer         {
183*04e0dc4aSTimo Kreuzer             pch += wcslen(progname) + 1 - MAXLINELEN;
184*04e0dc4aSTimo Kreuzer             _ERRCHECK(wcsncpy_s(pch, progname_size - (pch - progname), L"...", 3));
185*04e0dc4aSTimo Kreuzer         }
186*04e0dc4aSTimo Kreuzer 
187*04e0dc4aSTimo Kreuzer         _ERRCHECK(wcscat_s(outmsg, _countof(outmsg), L"\n\n"));
188*04e0dc4aSTimo Kreuzer         _ERRCHECK(wcscat_s(outmsg, _countof(outmsg), message));
189*04e0dc4aSTimo Kreuzer 
190*04e0dc4aSTimo Kreuzer         // Okay to ignore return value here, this is just to display the message box.
191*04e0dc4aSTimo Kreuzer         // Only caller is abort() (so we shouldn't/can't handle IDABORT), so the process
192*04e0dc4aSTimo Kreuzer         // will end shortly.
193*04e0dc4aSTimo Kreuzer         __acrt_show_wide_message_box(
194*04e0dc4aSTimo Kreuzer             outmsg,
195*04e0dc4aSTimo Kreuzer             L"Microsoft Visual C++ Runtime Library",
196*04e0dc4aSTimo Kreuzer             MB_OK | MB_ICONHAND | MB_SETFOREGROUND | MB_TASKMODAL);
197*04e0dc4aSTimo Kreuzer     }
198*04e0dc4aSTimo Kreuzer }
199*04e0dc4aSTimo Kreuzer 
200*04e0dc4aSTimo Kreuzer #endif /* _UCRT_ENCLAVE_BUILD */
201