"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/druntime/src/rt/dmain2.d" (20 Nov 2020, 22578 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /**
    2  * Contains druntime startup and shutdown routines.
    3  *
    4  * Copyright: Copyright Digital Mars 2000 - 2018.
    5  * License: Distributed under the
    6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
    7  *    (See accompanying file LICENSE)
    8  * Authors:   Walter Bright, Sean Kelly
    9  * Source: $(DRUNTIMESRC rt/_dmain2.d)
   10  */
   11 
   12 module rt.dmain2;
   13 
   14 private
   15 {
   16     import rt.memory;
   17     import rt.sections;
   18     import core.atomic;
   19     import core.stdc.stddef;
   20     import core.stdc.stdlib;
   21     import core.stdc.string;
   22     import core.stdc.stdio;   // for printf()
   23     import core.stdc.errno : errno;
   24 }
   25 
   26 version (Windows)
   27 {
   28     private import core.stdc.wchar_;
   29     private import core.sys.windows.basetsd /+: HANDLE+/;
   30     private import core.sys.windows.shellapi /+: CommandLineToArgvW+/;
   31     private import core.sys.windows.winbase /+: FreeLibrary, GetCommandLineW, GetProcAddress,
   32         IsDebuggerPresent, LoadLibraryA, LoadLibraryW, LocalFree, WriteFile+/;
   33     private import core.sys.windows.wincon /+: CONSOLE_SCREEN_BUFFER_INFO, GetConsoleOutputCP, GetConsoleScreenBufferInfo+/;
   34     private import core.sys.windows.winnls /+: CP_UTF8, MultiByteToWideChar, WideCharToMultiByte+/;
   35     private import core.sys.windows.winnt /+: WCHAR+/;
   36     private import core.sys.windows.winuser /+: MB_ICONERROR, MessageBoxW+/;
   37 
   38     pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW
   39 }
   40 
   41 version (FreeBSD)
   42 {
   43     import core.stdc.fenv;
   44 }
   45 version (NetBSD)
   46 {
   47     import core.stdc.fenv;
   48 }
   49 version (DragonFlyBSD)
   50 {
   51     import core.stdc.fenv;
   52 }
   53 
   54 // not sure why we can't define this in one place, but this is to keep this
   55 // module from importing core.runtime.
   56 struct UnitTestResult
   57 {
   58     size_t executed;
   59     size_t passed;
   60     bool runMain;
   61     bool summarize;
   62 }
   63 
   64 extern (C) void _d_monitor_staticctor();
   65 extern (C) void _d_monitor_staticdtor();
   66 extern (C) void _d_critical_init();
   67 extern (C) void _d_critical_term();
   68 extern (C) void gc_init();
   69 extern (C) void gc_term();
   70 extern (C) void thread_init() @nogc;
   71 extern (C) void thread_term() @nogc;
   72 extern (C) void lifetime_init();
   73 extern (C) void rt_moduleCtor();
   74 extern (C) void rt_moduleTlsCtor();
   75 extern (C) void rt_moduleDtor();
   76 extern (C) void rt_moduleTlsDtor();
   77 extern (C) void thread_joinAll();
   78 extern (C) UnitTestResult runModuleUnitTests();
   79 extern (C) void _d_initMonoTime();
   80 
   81 version (CRuntime_Microsoft)
   82 {
   83     extern(C) void init_msvc();
   84 }
   85 
   86 /***********************************
   87  * These are a temporary means of providing a GC hook for DLL use.  They may be
   88  * replaced with some other similar functionality later.
   89  */
   90 extern (C)
   91 {
   92     void* gc_getProxy();
   93     void  gc_setProxy(void* p);
   94     void  gc_clrProxy();
   95 
   96     alias void* function()      gcGetFn;
   97     alias void  function(void*) gcSetFn;
   98     alias void  function()      gcClrFn;
   99 }
  100 
  101 version (Windows)
  102 {
  103     /*******************************************
  104      * Loads a DLL written in D with the name 'name'.
  105      * Returns:
  106      *      opaque handle to the DLL if successfully loaded
  107      *      null if failure
  108      */
  109     extern (C) void* rt_loadLibrary(const char* name)
  110     {
  111         return initLibrary(.LoadLibraryA(name));
  112     }
  113 
  114     extern (C) void* rt_loadLibraryW(const WCHAR* name)
  115     {
  116         return initLibrary(.LoadLibraryW(name));
  117     }
  118 
  119     void* initLibrary(void* mod)
  120     {
  121         // BUG: LoadLibrary() call calls rt_init(), which fails if proxy is not set!
  122         // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().)
  123         if (mod is null)
  124             return mod;
  125         gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy");
  126         if (gcSet !is null)
  127         {   // BUG: Set proxy, but too late
  128             gcSet(gc_getProxy());
  129         }
  130         return mod;
  131     }
  132 
  133     /*************************************
  134      * Unloads DLL that was previously loaded by rt_loadLibrary().
  135      * Input:
  136      *      ptr     the handle returned by rt_loadLibrary()
  137      * Returns:
  138      *      1   succeeded
  139      *      0   some failure happened
  140      */
  141     extern (C) int rt_unloadLibrary(void* ptr)
  142     {
  143         gcClrFn gcClr  = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy");
  144         if (gcClr !is null)
  145             gcClr();
  146         return FreeLibrary(ptr) != 0;
  147     }
  148 }
  149 
  150 /* To get out-of-band access to the args[] passed to main().
  151  */
  152 
  153 __gshared string[] _d_args = null;
  154 
  155 extern (C) string[] rt_args()
  156 {
  157     return _d_args;
  158 }
  159 
  160 // This variable is only ever set by a debugger on initialization so it should
  161 // be fine to leave it as __gshared.
  162 extern (C) __gshared bool rt_trapExceptions = true;
  163 
  164 alias void delegate(Throwable) ExceptionHandler;
  165 
  166 /**
  167  * Keep track of how often rt_init/rt_term were called.
  168  */
  169 shared size_t _initCount;
  170 
  171 /**********************************************
  172  * Initialize druntime.
  173  * If a C program wishes to call D code, and there's no D main(), then it
  174  * must call rt_init() and rt_term().
  175  */
  176 extern (C) int rt_init()
  177 {
  178     /* @@BUG 11380 @@ Need to synchronize rt_init/rt_term calls for
  179        version (Shared) druntime, because multiple C threads might
  180        initialize different D libraries without knowing about the
  181        shared druntime. Also we need to attach any thread that calls
  182        rt_init. */
  183     if (atomicOp!"+="(_initCount, 1) > 1) return 1;
  184 
  185     version (CRuntime_Microsoft)
  186         init_msvc();
  187 
  188     _d_monitor_staticctor();
  189     _d_critical_init();
  190 
  191     try
  192     {
  193         initSections();
  194         // this initializes mono time before anything else to allow usage
  195         // in other druntime systems.
  196         _d_initMonoTime();
  197         thread_init();
  198         // TODO: fixme - calls GC.addRange -> Initializes GC
  199         initStaticDataGC();
  200         lifetime_init();
  201         rt_moduleCtor();
  202         rt_moduleTlsCtor();
  203         return 1;
  204     }
  205     catch (Throwable t)
  206     {
  207         atomicStore!(MemoryOrder.raw)(_initCount, 0);
  208         _d_print_throwable(t);
  209     }
  210     _d_critical_term();
  211     _d_monitor_staticdtor();
  212     return 0;
  213 }
  214 
  215 /**********************************************
  216  * Terminate use of druntime.
  217  */
  218 extern (C) int rt_term()
  219 {
  220     if (atomicLoad!(MemoryOrder.raw)(_initCount) == 0) return 0; // was never initialized
  221     if (atomicOp!"-="(_initCount, 1)) return 1;
  222 
  223     try
  224     {
  225         rt_moduleTlsDtor();
  226         thread_joinAll();
  227         rt_moduleDtor();
  228         gc_term();
  229         thread_term();
  230         return 1;
  231     }
  232     catch (Throwable t)
  233     {
  234         _d_print_throwable(t);
  235     }
  236     finally
  237     {
  238         finiSections();
  239         _d_critical_term();
  240         _d_monitor_staticdtor();
  241     }
  242     return 0;
  243 }
  244 
  245 /**********************************************
  246  * Trace handler
  247  */
  248 alias Throwable.TraceInfo function(void* ptr) TraceHandler;
  249 private __gshared TraceHandler traceHandler = null;
  250 
  251 
  252 /**
  253  * Overrides the default trace hander with a user-supplied version.
  254  *
  255  * Params:
  256  *  h = The new trace handler.  Set to null to use the default handler.
  257  */
  258 extern (C) void  rt_setTraceHandler(TraceHandler h)
  259 {
  260     traceHandler = h;
  261 }
  262 
  263 /**
  264  * Return the current trace handler
  265  */
  266 extern (C) TraceHandler rt_getTraceHandler()
  267 {
  268     return traceHandler;
  269 }
  270 
  271 /**
  272  * This function will be called when an exception is constructed.  The
  273  * user-supplied trace handler will be called if one has been supplied,
  274  * otherwise no trace will be generated.
  275  *
  276  * Params:
  277  *  ptr = A pointer to the location from which to generate the trace, or null
  278  *        if the trace should be generated from within the trace handler
  279  *        itself.
  280  *
  281  * Returns:
  282  *  An object describing the current calling context or null if no handler is
  283  *  supplied.
  284  */
  285 extern (C) Throwable.TraceInfo _d_traceContext(void* ptr = null)
  286 {
  287     if (traceHandler is null)
  288         return null;
  289     return traceHandler(ptr);
  290 }
  291 
  292 /***********************************
  293  * Provide out-of-band access to the original C argc/argv
  294  * passed to this program via main(argc,argv).
  295  */
  296 
  297 struct CArgs
  298 {
  299     int argc;
  300     char** argv;
  301 }
  302 
  303 __gshared CArgs _cArgs;
  304 
  305 extern (C) CArgs rt_cArgs() @nogc
  306 {
  307     return _cArgs;
  308 }
  309 
  310 /// Type of the D main() function (`_Dmain`).
  311 private alias extern(C) int function(char[][] args) MainFunc;
  312 
  313 /**
  314  * Sets up the D char[][] command-line args, initializes druntime,
  315  * runs embedded unittests and then runs the given D main() function,
  316  * optionally catching and printing any unhandled exceptions.
  317  */
  318 extern (C) int _d_run_main(int argc, char** argv, MainFunc mainFunc)
  319 {
  320     // Set up _cArgs and array of D char[] slices, then forward to _d_run_main2
  321 
  322     // Remember the original C argc/argv
  323     _cArgs.argc = argc;
  324     _cArgs.argv = argv;
  325 
  326     version (Windows)
  327     {
  328         /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that,
  329          * we ignore argc/argv and go get the Windows command line again as UTF-16.
  330          * Then, reparse into wargc/wargs, and then use Windows API to convert
  331          * to UTF-8.
  332          */
  333         const wCommandLine = GetCommandLineW();
  334         immutable size_t wCommandLineLength = wcslen(wCommandLine);
  335         int wargc;
  336         auto wargs = CommandLineToArgvW(wCommandLine, &wargc);
  337         // assert(wargc == argc); /* argc can be broken by Unicode arguments */
  338 
  339         // Allocate args[] on the stack - use wargc
  340         char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc];
  341 
  342         // This is required because WideCharToMultiByte requires int as input.
  343         assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max");
  344 
  345         immutable size_t totalArgsLength = WideCharToMultiByte(CP_UTF8, 0, wCommandLine, cast(int)wCommandLineLength, null, 0, null, null);
  346         {
  347             char* totalArgsBuff = cast(char*) alloca(totalArgsLength);
  348             size_t j = 0;
  349             foreach (i; 0 .. wargc)
  350             {
  351                 immutable size_t wlen = wcslen(wargs[i]);
  352                 assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
  353                 immutable int len = WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, null, 0, null, null);
  354                 args[i] = totalArgsBuff[j .. j + len];
  355                 if (len == 0)
  356                     continue;
  357                 j += len;
  358                 assert(j <= totalArgsLength);
  359                 WideCharToMultiByte(CP_UTF8, 0, &wargs[i][0], cast(int) wlen, &args[i][0], len, null, null);
  360             }
  361         }
  362         LocalFree(wargs);
  363         wargs = null;
  364         wargc = 0;
  365     }
  366     else version (Posix)
  367     {
  368         // Allocate args[] on the stack
  369         char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
  370 
  371         size_t totalArgsLength = 0;
  372         foreach (i, ref arg; args)
  373         {
  374             arg = argv[i][0 .. strlen(argv[i])];
  375             totalArgsLength += arg.length;
  376         }
  377     }
  378     else
  379         static assert(0);
  380 
  381     return _d_run_main2(args, totalArgsLength, mainFunc);
  382 }
  383 
  384 /**
  385  * Windows-specific version for wide command-line arguments, e.g.,
  386  * from a wmain/wWinMain C entry point.
  387  * This wide version uses the specified arguments, unlike narrow
  388  * _d_run_main which uses the actual (wide) process arguments instead.
  389  */
  390 version (Windows)
  391 extern (C) int _d_wrun_main(int argc, wchar** wargv, MainFunc mainFunc)
  392 {
  393      // Allocate args[] on the stack
  394     char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
  395 
  396     // 1st pass: compute each argument's length as UTF-16 and UTF-8
  397     size_t totalArgsLength = 0;
  398     foreach (i; 0 .. argc)
  399     {
  400         const warg = wargv[i];
  401         const size_t wlen = wcslen(warg) + 1; // incl. terminating null
  402         assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
  403         const int len = WideCharToMultiByte(CP_UTF8, 0, warg, cast(int) wlen, null, 0, null, null);
  404         args[i] = (cast(char*) wlen)[0 .. len]; // args[i].ptr = wlen, args[i].length = len
  405         totalArgsLength += len;
  406     }
  407 
  408     // Allocate a single buffer for all (null-terminated) argument strings in UTF-8 on the stack
  409     char* utf8Buffer = cast(char*) alloca(totalArgsLength);
  410 
  411     // 2nd pass: convert to UTF-8 and finalize `args`
  412     char* utf8 = utf8Buffer;
  413     foreach (i; 0 .. argc)
  414     {
  415         const wlen = cast(int) args[i].ptr;
  416         const len = cast(int) args[i].length;
  417         WideCharToMultiByte(CP_UTF8, 0, wargv[i], wlen, utf8, len, null, null);
  418         args[i] = utf8[0 .. len-1]; // excl. terminating null
  419         utf8 += len;
  420     }
  421 
  422     // Set C argc/argv; argv is a new stack-allocated array of UTF-8 C strings
  423     char*[] argv = (cast(char**) alloca(argc * (char*).sizeof))[0 .. argc];
  424     foreach (i, ref arg; argv)
  425         arg = args[i].ptr;
  426     _cArgs.argc = argc;
  427     _cArgs.argv = argv.ptr;
  428 
  429     totalArgsLength -= argc; // excl. null terminator per arg
  430     return _d_run_main2(args, totalArgsLength, mainFunc);
  431 }
  432 
  433 private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainFunc mainFunc)
  434 {
  435     int result;
  436 
  437     version (FreeBSD) version (D_InlineAsm_X86)
  438     {
  439         /*
  440          * FreeBSD/i386 sets the FPU precision mode to 53 bit double.
  441          * Make it 64 bit extended.
  442          */
  443         ushort fpucw;
  444         asm
  445         {
  446             fstsw   fpucw;
  447             or      fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision
  448                                            // 111111: mask all FP exceptions
  449             fldcw   fpucw;
  450         }
  451     }
  452     version (CRuntime_Microsoft)
  453     {
  454         // enable full precision for reals
  455         version (GNU)
  456         {
  457             size_t fpu_cw;
  458             asm { "fstcw %0" : "=m" (fpu_cw); }
  459             fpu_cw |= 0b11_00_111111;  // 11: use 64 bit extended-precision
  460                                        // 111111: mask all FP exceptions
  461             asm { "fldcw %0" : "=m" (fpu_cw); }
  462         }
  463         else version (Win64)
  464             asm
  465             {
  466                 push    RAX;
  467                 fstcw   word ptr [RSP];
  468                 or      [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision
  469                                                // 111111: mask all FP exceptions
  470                 fldcw   word ptr [RSP];
  471                 pop     RAX;
  472             }
  473         else version (Win32)
  474         {
  475             asm
  476             {
  477                 push    EAX;
  478                 fstcw   word ptr [ESP];
  479                 or      [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision
  480                 // 111111: mask all FP exceptions
  481                 fldcw   word ptr [ESP];
  482                 pop     EAX;
  483             }
  484         }
  485     }
  486 
  487     /* Create a copy of args[] on the stack to be used for main, so that rt_args()
  488      * cannot be modified by the user.
  489      * Note that when this function returns, _d_args will refer to garbage.
  490      */
  491     {
  492         _d_args = cast(string[]) args;
  493         auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength);
  494 
  495         char[][] argsCopy = buff[0 .. args.length];
  496         auto argBuff = cast(char*) (buff + args.length);
  497         size_t j = 0;
  498         import rt.config : rt_cmdline_enabled;
  499         bool parseOpts = rt_cmdline_enabled!();
  500         foreach (arg; args)
  501         {
  502             // Do not pass Druntime options to the program
  503             if (parseOpts && arg.length >= 6 && arg[0 .. 6] == "--DRT-")
  504                 continue;
  505             // https://issues.dlang.org/show_bug.cgi?id=20459
  506             if (arg == "--")
  507                 parseOpts = false;
  508             argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]);
  509             argBuff += arg.length;
  510         }
  511         args = argsCopy[0..j];
  512     }
  513 
  514     auto useExceptionTrap = parseExceptionOptions();
  515 
  516     version (Windows)
  517     {
  518         if (IsDebuggerPresent())
  519             useExceptionTrap = false;
  520     }
  521 
  522     void tryExec(scope void delegate() dg)
  523     {
  524         if (useExceptionTrap)
  525         {
  526             try
  527             {
  528                 dg();
  529             }
  530             catch (Throwable t)
  531             {
  532                 _d_print_throwable(t);
  533                 result = EXIT_FAILURE;
  534             }
  535         }
  536         else
  537         {
  538             dg();
  539         }
  540     }
  541 
  542     // NOTE: The lifetime of a process is much like the lifetime of an object:
  543     //       it is initialized, then used, then destroyed.  If initialization
  544     //       fails, the successive two steps are never reached.  However, if
  545     //       initialization succeeds, then cleanup will occur even if the use
  546     //       step fails in some way.  Here, the use phase consists of running
  547     //       the user's main function.  If main terminates with an exception,
  548     //       the exception is handled and then cleanup begins.  An exception
  549     //       thrown during cleanup, however, will abort the cleanup process.
  550     void runAll()
  551     {
  552         if (rt_init())
  553         {
  554             auto utResult = runModuleUnitTests();
  555             assert(utResult.passed <= utResult.executed);
  556             if (utResult.passed == utResult.executed)
  557             {
  558                 if (utResult.summarize)
  559                 {
  560                     if (utResult.passed == 0)
  561                         .fprintf(.stderr, "No unittests run\n");
  562                     else
  563                         .fprintf(.stderr, "%d modules passed unittests\n",
  564                                  cast(int)utResult.passed);
  565                 }
  566                 if (utResult.runMain)
  567                     tryExec({ result = mainFunc(args); });
  568                 else
  569                     result = EXIT_SUCCESS;
  570             }
  571             else
  572             {
  573                 if (utResult.summarize)
  574                     .fprintf(.stderr, "%d/%d modules FAILED unittests\n",
  575                              cast(int)(utResult.executed - utResult.passed),
  576                              cast(int)utResult.executed);
  577                 result = EXIT_FAILURE;
  578             }
  579         }
  580         else
  581             result = EXIT_FAILURE;
  582 
  583         if (!rt_term())
  584             result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result;
  585     }
  586 
  587     tryExec(&runAll);
  588 
  589     // Issue 10344: flush stdout and return nonzero on failure
  590     if (.fflush(.stdout) != 0)
  591     {
  592         .fprintf(.stderr, "Failed to flush stdout: %s\n", .strerror(.errno));
  593         if (result == 0)
  594         {
  595             result = EXIT_FAILURE;
  596         }
  597     }
  598 
  599     return result;
  600 }
  601 
  602 private void formatThrowable(Throwable t, scope void delegate(const scope char[] s) nothrow sink)
  603 {
  604     foreach (u; t)
  605     {
  606         u.toString(sink); sink("\n");
  607 
  608         auto e = cast(Error)u;
  609         if (e is null || e.bypassedException is null) continue;
  610 
  611         sink("=== Bypassed ===\n");
  612         foreach (t2; e.bypassedException)
  613         {
  614             t2.toString(sink); sink("\n");
  615         }
  616         sink("=== ~Bypassed ===\n");
  617     }
  618 }
  619 
  620 private auto parseExceptionOptions()
  621 {
  622     import rt.config : rt_configOption;
  623     import core.internal.parseoptions : rt_parseOption;
  624     const optName = "trapExceptions";
  625     auto option = rt_configOption(optName);
  626     auto trap = rt_trapExceptions;
  627     if (option.length)
  628         rt_parseOption(optName, option, trap, "");
  629     return trap;
  630 }
  631 
  632 extern (C) void _d_print_throwable(Throwable t)
  633 {
  634     // On Windows, a console may not be present to print the output to.
  635     // Show a message box instead. If the console is present, convert to
  636     // the correct encoding.
  637     version (Windows)
  638     {
  639         static struct WSink
  640         {
  641             WCHAR* ptr; size_t len;
  642 
  643             void sink(const scope char[] s) scope nothrow
  644             {
  645                 if (!s.length) return;
  646                 int swlen = MultiByteToWideChar(
  647                         CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0);
  648                 if (!swlen) return;
  649 
  650                 auto newPtr = cast(WCHAR*)realloc(ptr,
  651                         (this.len + swlen + 1) * WCHAR.sizeof);
  652                 if (!newPtr) return;
  653                 ptr = newPtr;
  654                 auto written = MultiByteToWideChar(
  655                         CP_UTF8, 0, s.ptr, cast(int)s.length, ptr+len, swlen);
  656                 len += written;
  657             }
  658 
  659             typeof(ptr) get() { if (ptr) ptr[len] = 0; return ptr; }
  660 
  661             void free() { .free(ptr); }
  662         }
  663 
  664         HANDLE windowsHandle(int fd)
  665         {
  666             version (CRuntime_Microsoft)
  667                 return cast(HANDLE)_get_osfhandle(fd);
  668             else
  669                 return _fdToHandle(fd);
  670         }
  671 
  672         auto hStdErr = windowsHandle(fileno(stderr));
  673         CONSOLE_SCREEN_BUFFER_INFO sbi;
  674         bool isConsole = GetConsoleScreenBufferInfo(hStdErr, &sbi) != 0;
  675 
  676         // ensure the exception is shown at the beginning of the line, while also
  677         // checking whether stderr is a valid file
  678         int written = fprintf(stderr, "\n");
  679         if (written <= 0)
  680         {
  681             WSink buf;
  682             formatThrowable(t, &buf.sink);
  683 
  684             if (buf.ptr)
  685             {
  686                 WSink caption;
  687                 if (t)
  688                     caption.sink(t.classinfo.name);
  689 
  690                 // Avoid static user32.dll dependency for console applications
  691                 // by loading it dynamically as needed
  692                 auto user32 = LoadLibraryW("user32.dll");
  693                 if (user32)
  694                 {
  695                     alias typeof(&MessageBoxW) PMessageBoxW;
  696                     auto pMessageBoxW = cast(PMessageBoxW)
  697                         GetProcAddress(user32, "MessageBoxW");
  698                     if (pMessageBoxW)
  699                         pMessageBoxW(null, buf.get(), caption.get(), MB_ICONERROR);
  700                 }
  701                 FreeLibrary(user32);
  702                 caption.free();
  703                 buf.free();
  704             }
  705             return;
  706         }
  707         else if (isConsole)
  708         {
  709             WSink buf;
  710             formatThrowable(t, &buf.sink);
  711 
  712             if (buf.ptr)
  713             {
  714                 uint codepage = GetConsoleOutputCP();
  715                 int slen = WideCharToMultiByte(codepage, 0,
  716                         buf.ptr, cast(int)buf.len, null, 0, null, null);
  717                 auto sptr = cast(char*)malloc(slen * char.sizeof);
  718                 if (sptr)
  719                 {
  720                     WideCharToMultiByte(codepage, 0,
  721                         buf.ptr, cast(int)buf.len, sptr, slen, null, null);
  722                     WriteFile(hStdErr, sptr, slen, null, null);
  723                     free(sptr);
  724                 }
  725                 buf.free();
  726             }
  727             return;
  728         }
  729     }
  730 
  731     void sink(const scope char[] buf) scope nothrow
  732     {
  733         fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
  734     }
  735     formatThrowable(t, &sink);
  736 }