diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.rc b/src/cascadia/WindowsTerminal/WindowsTerminal.rc index 4dfe44e35..d1911d7b1 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.rc +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.rc @@ -17,6 +17,7 @@ #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -24,18 +25,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -44,11 +45,36 @@ END #endif // APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico" + + ///////////////////////////////////////////////////////////////////////////// // // String Table // +STRINGTABLE +BEGIN + IDS_ERROR_DIALOG_TITLE "Error" + IDS_ERROR_ARCHITECTURE_FORMAT + "Windows Terminal is designed to run on your system's native architecture (%s).\nYou are currently using the %s version.\n\nPlease use the version of Windows Terminal that matches your system's native architecture." + IDS_X86_ARCHITECTURE "i386" +END + +STRINGTABLE +BEGIN + IDS_AMD64_ARCHITECTURE "AMD64" + IDS_ARM64_ARCHITECTURE "ARM64" + IDS_ARM_ARCHITECTURE "ARM" + IDS_UNKNOWN_ARCHITECTURE "Unknown" +END #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// @@ -65,4 +91,3 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED -IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico" diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index e6efb55e2..3b357f9c4 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "AppHost.h" +#include "resource.h" using namespace winrt; using namespace Windows::UI; @@ -10,8 +11,94 @@ using namespace Windows::UI::Composition; using namespace Windows::UI::Xaml::Hosting; using namespace Windows::Foundation::Numerics; +// Routine Description: +// - Retrieves the string resource from the current module with the given ID +// from the resources files. See resource.h and the .rc definitions for valid IDs. +// Arguments: +// - id - Resource ID +// Return Value: +// - String resource retrieved from that ID. +static std::wstring GetStringResource(const UINT id) +{ + // Calling LoadStringW with a pointer-sized storage and no length will return a read-only pointer + // directly to the resource data instead of copying it immediately into a buffer. + LPWSTR readOnlyResource = nullptr; + const auto length = LoadStringW(wil::GetModuleInstanceHandle(), + id, + reinterpret_cast(&readOnlyResource), + 0); + + // However, the pointer and length given are NOT guaranteed to be zero-terminated + // and most uses of this data will probably want a zero-terminated string. + // So we're going to construct and return a std::wstring copy from the pointer/length + // since those are certainly zero-terminated. + return { readOnlyResource, gsl::narrow(length) }; +} + +// Routine Description: +// - Takes an image architecture and locates a string resource that maps to that architecture. +// Arguments: +// - imageArchitecture - An IMAGE_FILE_MACHINE architecture enum value +// - See https://docs.microsoft.com/en-us/windows/win32/sysinfo/image-file-machine-constants +// Return Value: +// - A string value representing the human-readable name of this architecture. +static std::wstring ImageArchitectureToString(USHORT imageArchitecture) +{ + // clang-format off + const auto id = imageArchitecture == IMAGE_FILE_MACHINE_I386 ? IDS_X86_ARCHITECTURE : + imageArchitecture == IMAGE_FILE_MACHINE_AMD64 ? IDS_AMD64_ARCHITECTURE : + imageArchitecture == IMAGE_FILE_MACHINE_ARM64 ? IDS_ARM64_ARCHITECTURE : + imageArchitecture == IMAGE_FILE_MACHINE_ARM ? IDS_ARM_ARCHITECTURE : + IDS_UNKNOWN_ARCHITECTURE; + // clang-format on + + return GetStringResource(id); +} + +// Routine Description: +// - Blocks the user from launching the application with a message box dialog and early exit +// if the process architecture doesn't match the system platform native architecture. +// - This is because the conhost.exe must match the condrv.sys on the system and the PTY +// infrastructure that powers everything won't work if we have a mismatch. +// Arguments: +// - +// Return Value: +// - +static void EnsureNativeArchitecture() +{ + USHORT processMachine{}; + USHORT nativeMachine{}; + THROW_IF_WIN32_BOOL_FALSE(IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine)); + if (processMachine != IMAGE_FILE_MACHINE_UNKNOWN && processMachine != nativeMachine) + { + const auto formatPattern = GetStringResource(IDS_ERROR_ARCHITECTURE_FORMAT); + + const auto nativeArchitecture = ImageArchitectureToString(nativeMachine); + const auto processArchitecture = ImageArchitectureToString(processMachine); + + const auto lengthRequired = _scwprintf(formatPattern.data(), nativeArchitecture.data(), processArchitecture.data()); + const auto bufferSize = lengthRequired + 1; + + std::wstring buffer; + buffer.resize(bufferSize); + + swprintf_s(buffer.data(), buffer.size(), formatPattern.data(), nativeArchitecture.data(), processArchitecture.data()); + + MessageBoxW(nullptr, + buffer.data(), + GetStringResource(IDS_ERROR_DIALOG_TITLE).data(), + MB_OK | MB_ICONERROR); + ExitProcess(0); + } +} + int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { + // Block the user from starting if they launched the incorrect architecture version of the project. + // This should only be applicable to developer versions. The package installation process + // should choose and install the correct one from the bundle. + EnsureNativeArchitecture(); + // Make sure to call this so we get WM_POINTER messages. EnableMouseInPointer(true); diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 0491d5bd3..9537c7469 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -50,3 +50,4 @@ Abstract: #include #include +#include diff --git a/src/cascadia/WindowsTerminal/resource.h b/src/cascadia/WindowsTerminal/resource.h index ccee0e222..23b2098e3 100644 --- a/src/cascadia/WindowsTerminal/resource.h +++ b/src/cascadia/WindowsTerminal/resource.h @@ -1,16 +1,23 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by Cascadia.EXE.rc +// Used by WindowsTerminal.rc +// +#define IDI_APPICON 101 +#define IDS_ERROR_DIALOG_TITLE 105 +#define IDS_ERROR_ARCHITECTURE_FORMAT 110 +#define IDS_X86_ARCHITECTURE 111 +#define IDS_AMD64_ARCHITECTURE 112 +#define IDS_ARM64_ARCHITECTURE 113 +#define IDS_ARM_ARCHITECTURE 114 +#define IDS_UNKNOWN_ARCHITECTURE 115 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif - -#define IDI_APPICON 101