| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "platform/globals.h" |
| #if defined(HOST_OS_WINDOWS) |
| |
| #include "bin/platform.h" |
| |
| #include <crtdbg.h> |
| |
| #include "bin/file.h" |
| #include "bin/lockers.h" |
| #include "bin/log.h" |
| #if !defined(PLATFORM_DISABLE_SOCKET) |
| #include "bin/socket.h" |
| #endif |
| #include "bin/thread.h" |
| #include "bin/utils.h" |
| #include "bin/utils_win.h" |
| |
| // These are not always defined in the header files. See: |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx |
| #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT |
| #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 |
| #endif |
| |
| #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING |
| #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 |
| #endif |
| |
| namespace dart { |
| |
| // Defined in vm/os_thread_win.cc |
| extern bool private_flag_windows_run_tls_destructors; |
| |
| namespace bin { |
| |
| const char* Platform::executable_name_ = NULL; |
| char* Platform::resolved_executable_name_ = NULL; |
| int Platform::script_index_ = 1; |
| char** Platform::argv_ = NULL; |
| |
| class PlatformWin { |
| public: |
| static void InitOnce() { |
| platform_win_mutex_ = new Mutex(); |
| saved_output_cp_ = -1; |
| saved_input_cp_ = -1; |
| // Set up a no-op handler so that CRT functions return an error instead of |
| // hitting an assertion failure. |
| // See: https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx |
| _set_invalid_parameter_handler(InvalidParameterHandler); |
| // Disable the message box for assertions in the CRT in Debug builds. |
| // See: https://msdn.microsoft.com/en-us/library/1y71x448.aspx |
| _CrtSetReportMode(_CRT_ASSERT, 0); |
| // Disable dialog boxes for "critical" errors or when OpenFile cannot find |
| // the requested file. See: |
| // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx |
| SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | |
| SEM_NOGPFAULTERRORBOX); |
| // Set up a signal handler that restores the console state on a |
| // CTRL_C_EVENT signal. This will only run when there is no signal handler |
| // registered for the CTRL_C_EVENT from Dart code. |
| SetConsoleCtrlHandler(SignalHandler, TRUE); |
| #ifndef PRODUCT |
| // Set up global exception handler to be able to dump stack trace on crash. |
| SetExceptionHandler(); |
| #endif |
| } |
| |
| static BOOL WINAPI SignalHandler(DWORD signal) { |
| if (signal == CTRL_C_EVENT) { |
| // We call this without taking the lock because this is a signal |
| // handler, and because the process is about to go down. |
| RestoreConsoleLocked(); |
| } |
| return FALSE; |
| } |
| |
| static void SaveAndConfigureConsole() { |
| MutexLocker ml(platform_win_mutex_); |
| // Set both the input and output code pages to UTF8. |
| ASSERT(saved_output_cp_ == -1); |
| ASSERT(saved_input_cp_ == -1); |
| const int output_cp = GetConsoleOutputCP(); |
| const int input_cp = GetConsoleCP(); |
| if (output_cp != CP_UTF8) { |
| SetConsoleOutputCP(CP_UTF8); |
| saved_output_cp_ = output_cp; |
| } |
| if (input_cp != CP_UTF8) { |
| SetConsoleCP(CP_UTF8); |
| saved_input_cp_ = input_cp; |
| } |
| |
| // Try to set the bits for ANSI support, but swallow any failures. |
| HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); |
| DWORD out_mode; |
| if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) { |
| const DWORD request = out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING; |
| SetConsoleMode(out, request); |
| } |
| // TODO(28984): Due to issue #29104, we cannot set |
| // ENABLE_VIRTUAL_TERMINAL_INPUT here, as it causes ENABLE_PROCESSED_INPUT |
| // to be ignored. |
| } |
| |
| static void RestoreConsole() { |
| MutexLocker ml(platform_win_mutex_); |
| RestoreConsoleLocked(); |
| } |
| |
| // Windows top-level unhandled exception handler function. |
| // See MSDN documentation for UnhandledExceptionFilter. |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681401(v=vs.85).aspx |
| static LONG WINAPI |
| DartExceptionHandler(struct _EXCEPTION_POINTERS* ExceptionInfo) { |
| if ((ExceptionInfo->ExceptionRecord->ExceptionCode == |
| EXCEPTION_ACCESS_VIOLATION) || |
| (ExceptionInfo->ExceptionRecord->ExceptionCode == |
| EXCEPTION_ILLEGAL_INSTRUCTION)) { |
| Log::PrintErr( |
| "\n===== DART STANDALONE VM CRASH =====\n" |
| "version=%s\n" |
| "ExceptionCode=%d, ExceptionFlags=%d, ExceptionAddress=%p\n", |
| Dart_VersionString(), ExceptionInfo->ExceptionRecord->ExceptionCode, |
| ExceptionInfo->ExceptionRecord->ExceptionFlags, |
| ExceptionInfo->ExceptionRecord->ExceptionAddress); |
| Dart_DumpNativeStackTrace(ExceptionInfo->ContextRecord); |
| const int kAbortExitCode = 3; |
| Platform::Exit(kAbortExitCode); |
| } |
| return EXCEPTION_CONTINUE_SEARCH; |
| } |
| |
| static void SetExceptionHandler() { |
| SetUnhandledExceptionFilter(DartExceptionHandler); |
| } |
| |
| private: |
| static Mutex* platform_win_mutex_; |
| static int saved_output_cp_; |
| static int saved_input_cp_; |
| |
| static void RestoreConsoleLocked() { |
| // STD_OUTPUT_HANDLE and STD_INPUT_HANDLE may have been closed or |
| // redirected. Therefore, we explicitly open the CONOUT$ and CONIN$ |
| // devices, so that we can be sure that we are really unsetting |
| // ENABLE_VIRTUAL_TERMINAL_PROCESSING and ENABLE_VIRTUAL_TERMINAL_INPUT |
| // respectively. |
| const intptr_t kWideBufLen = 64; |
| const char* conout = "CONOUT$"; |
| wchar_t widebuf[kWideBufLen]; |
| int result = |
| MultiByteToWideChar(CP_UTF8, 0, conout, -1, widebuf, kWideBufLen); |
| ASSERT(result != 0); |
| HANDLE out = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, |
| FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); |
| if (out != INVALID_HANDLE_VALUE) { |
| SetStdHandle(STD_OUTPUT_HANDLE, out); |
| } |
| DWORD out_mode; |
| if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) { |
| DWORD request = out_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT; |
| SetConsoleMode(out, request); |
| } |
| |
| const char* conin = "CONIN$"; |
| result = MultiByteToWideChar(CP_UTF8, 0, conin, -1, widebuf, kWideBufLen); |
| ASSERT(result != 0); |
| HANDLE in = CreateFileW(widebuf, GENERIC_READ | GENERIC_WRITE, |
| FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); |
| if (in != INVALID_HANDLE_VALUE) { |
| SetStdHandle(STD_INPUT_HANDLE, in); |
| } |
| DWORD in_mode; |
| if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) { |
| DWORD request = in_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT; |
| SetConsoleMode(in, request); |
| } |
| |
| if (saved_output_cp_ != -1) { |
| SetConsoleOutputCP(saved_output_cp_); |
| saved_output_cp_ = -1; |
| } |
| if (saved_input_cp_ != -1) { |
| SetConsoleCP(saved_input_cp_); |
| saved_input_cp_ = -1; |
| } |
| } |
| |
| static void InvalidParameterHandler(const wchar_t* expression, |
| const wchar_t* function, |
| const wchar_t* file, |
| unsigned int line, |
| uintptr_t reserved) { |
| // Doing nothing here means that the CRT call that invoked it will |
| // return an error code and/or set errno. |
| } |
| |
| DISALLOW_ALLOCATION(); |
| DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformWin); |
| }; |
| |
| int PlatformWin::saved_output_cp_ = -1; |
| int PlatformWin::saved_input_cp_ = -1; |
| Mutex* PlatformWin::platform_win_mutex_ = NULL; |
| |
| bool Platform::Initialize() { |
| PlatformWin::InitOnce(); |
| PlatformWin::SaveAndConfigureConsole(); |
| return true; |
| } |
| |
| int Platform::NumberOfProcessors() { |
| SYSTEM_INFO info; |
| GetSystemInfo(&info); |
| return info.dwNumberOfProcessors; |
| } |
| |
| const char* Platform::OperatingSystem() { |
| return "windows"; |
| } |
| |
| // We pull the version number, and other version information out of the |
| // registry because GetVersionEx() and friends lie about the OS version after |
| // Windows 8.1. See: |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx |
| static const wchar_t* kCurrentVersion = |
| L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; |
| |
| static bool GetCurrentVersionDWord(const wchar_t* field, DWORD* value) { |
| DWORD value_size = sizeof(*value); |
| LONG err = RegGetValue(HKEY_LOCAL_MACHINE, kCurrentVersion, field, |
| RRF_RT_REG_DWORD, NULL, value, &value_size); |
| return err == ERROR_SUCCESS; |
| } |
| |
| static bool GetCurrentVersionString(const wchar_t* field, const char** value) { |
| wchar_t wversion[256]; |
| DWORD wversion_size = sizeof(wversion); |
| LONG err = RegGetValue(HKEY_LOCAL_MACHINE, kCurrentVersion, field, |
| RRF_RT_REG_SZ, NULL, wversion, &wversion_size); |
| if (err != ERROR_SUCCESS) { |
| return false; |
| } |
| *value = StringUtilsWin::WideToUtf8(wversion); |
| return true; |
| } |
| |
| static const char* VersionNumber() { |
| // Try to get CurrentMajorVersionNumber. If that fails, fall back on |
| // CurrentVersion. If it succeeds also get CurrentMinorVersionNumber. |
| DWORD major; |
| if (!GetCurrentVersionDWord(L"CurrentMajorVersionNumber", &major)) { |
| const char* version; |
| if (!GetCurrentVersionString(L"CurrentVersion", &version)) { |
| return NULL; |
| } |
| return version; |
| } |
| |
| DWORD minor; |
| if (!GetCurrentVersionDWord(L"CurrentMinorVersionNumber", &minor)) { |
| return NULL; |
| } |
| const char* kFormat = "%d.%d"; |
| int len = snprintf(NULL, 0, kFormat, major, minor); |
| if (len < 0) { |
| return NULL; |
| } |
| char* result = DartUtils::ScopedCString(len + 1); |
| ASSERT(result != NULL); |
| len = snprintf(result, len + 1, kFormat, major, minor); |
| if (len < 0) { |
| return NULL; |
| } |
| return result; |
| } |
| |
| const char* Platform::OperatingSystemVersion() { |
| // Get the product name, e.g. "Windows 10 Home". |
| const char* name; |
| if (!GetCurrentVersionString(L"ProductName", &name)) { |
| return NULL; |
| } |
| |
| // Get the version number, e.g. "10.0". |
| const char* version_number = VersionNumber(); |
| if (version_number == NULL) { |
| return NULL; |
| } |
| |
| // Get the build number. |
| const char* build; |
| if (!GetCurrentVersionString(L"CurrentBuild", &build)) { |
| return NULL; |
| } |
| |
| // Put it all together. |
| const char* kFormat = "\"%s\" %s (Build %s)"; |
| int len = snprintf(NULL, 0, kFormat, name, version_number, build); |
| char* result = DartUtils::ScopedCString(len + 1); |
| len = snprintf(result, len + 1, kFormat, name, version_number, build); |
| return result; |
| } |
| |
| const char* Platform::LibraryPrefix() { |
| return ""; |
| } |
| |
| const char* Platform::LibraryExtension() { |
| return "dll"; |
| } |
| |
| const char* Platform::LocaleName() { |
| wchar_t locale_name[LOCALE_NAME_MAX_LENGTH]; |
| int result = GetUserDefaultLocaleName(locale_name, LOCALE_NAME_MAX_LENGTH); |
| if (result == 0) { |
| return NULL; |
| } |
| return StringUtilsWin::WideToUtf8(locale_name); |
| } |
| |
| bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) { |
| #if defined(PLATFORM_DISABLE_SOCKET) |
| return false; |
| #else |
| if (!SocketBase::Initialize()) { |
| return false; |
| } |
| return gethostname(buffer, buffer_length) == 0; |
| #endif |
| } |
| |
| char** Platform::Environment(intptr_t* count) { |
| wchar_t* strings = GetEnvironmentStringsW(); |
| if (strings == NULL) { |
| return NULL; |
| } |
| wchar_t* tmp = strings; |
| intptr_t i = 0; |
| while (*tmp != '\0') { |
| // Skip environment strings starting with "=". |
| // These are synthetic variables corresponding to dynamic environment |
| // variables like %=C:% and %=ExitCode%, and the Dart environment does |
| // not include these. |
| if (*tmp != '=') { |
| i++; |
| } |
| tmp += (wcslen(tmp) + 1); |
| } |
| *count = i; |
| char** result; |
| result = reinterpret_cast<char**>(Dart_ScopeAllocate(i * sizeof(*result))); |
| tmp = strings; |
| for (intptr_t current = 0; current < i;) { |
| // Skip the strings that were not counted above. |
| if (*tmp != '=') { |
| result[current++] = StringUtilsWin::WideToUtf8(tmp); |
| } |
| tmp += (wcslen(tmp) + 1); |
| } |
| FreeEnvironmentStringsW(strings); |
| return result; |
| } |
| |
| const char* Platform::GetExecutableName() { |
| return executable_name_; |
| } |
| |
| const char* Platform::ResolveExecutablePath() { |
| // GetModuleFileNameW cannot directly provide information on the |
| // required buffer size, so start out with a buffer large enough to |
| // hold any Windows path. |
| const int kTmpBufferSize = 32768; |
| wchar_t* tmp_buffer = |
| reinterpret_cast<wchar_t*>(Dart_ScopeAllocate(kTmpBufferSize)); |
| // Ensure no last error before calling GetModuleFileNameW. |
| SetLastError(ERROR_SUCCESS); |
| // Get the required length of the buffer. |
| int path_length = GetModuleFileNameW(NULL, tmp_buffer, kTmpBufferSize); |
| if (GetLastError() != ERROR_SUCCESS) { |
| return NULL; |
| } |
| char* path = StringUtilsWin::WideToUtf8(tmp_buffer); |
| // Return the canonical path as the returned path might contain symlinks. |
| const char* canon_path = File::GetCanonicalPath(NULL, path); |
| return canon_path; |
| } |
| |
| void Platform::Exit(int exit_code) { |
| // TODO(zra): Remove once VM shuts down cleanly. |
| ::dart::private_flag_windows_run_tls_destructors = false; |
| // Restore the console's output code page |
| PlatformWin::RestoreConsole(); |
| // On Windows we use ExitProcess so that threads can't clobber the exit_code. |
| // See: https://code.google.com/p/nativeclient/issues/detail?id=2870 |
| ::ExitProcess(exit_code); |
| } |
| |
| } // namespace bin |
| } // namespace dart |
| |
| #endif // defined(HOST_OS_WINDOWS) |