Create Telnet connection type and default loopback profile for… (#3858)

For our Universal terminal for development purposes, we will use telnet to escape the universal application container and empower developers to debug/diagnose issues with their own machine on loopback to the already-elevated telnet context.
This commit is contained in:
Michael Niksa 2019-12-09 11:07:08 -08:00 committed by GitHub
parent dd1c7b3e52
commit 402b7ff0e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 719 additions and 94 deletions

View file

@ -47,3 +47,33 @@ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## telnetpp
**Source**: https://github.com/KazDragon/telnetpp
### License
```
The MIT License (MIT)
Copyright (c) 2015-2017 Matthew Chaplain a.k.a KazDragon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

View file

@ -49,8 +49,8 @@ namespace winrt::TerminalApp::implementation
if (content == nullptr)
{
auto logic = Logic();
logic.RunAsUwp(); // Must set UWP status first, settings might change based on it.
logic.LoadSettings();
logic.RunAsUwp();
logic.Create();
auto page = logic.GetRoot().as<TerminalPage>();

View file

@ -129,9 +129,24 @@ namespace winrt::TerminalApp::implementation
_root = winrt::make_self<TerminalPage>();
}
// Method Decscription:
// - Called around the codebase to discover if this is a UWP where we need to turn off specific settings.
// Arguments:
// - <none> - reports internal state
// Return Value:
// - True if UWP, false otherwise.
bool AppLogic::IsUwp() const noexcept
{
return _isUwp;
}
// Method Description:
// - Called by UWP context invoker to let us know that we may have to change some of our behaviors
// for being a UWP
// Arguments:
// - <none> (sets to UWP = true, one way change)
// Return Value:
// - <none>
void AppLogic::RunAsUwp()
{
_isUwp = true;
@ -438,7 +453,7 @@ namespace winrt::TerminalApp::implementation
try
{
auto newSettings = CascadiaSettings::LoadAll();
auto newSettings = _isUwp ? CascadiaSettings::LoadUniversal() : CascadiaSettings::LoadAll();
_settings = std::move(newSettings);
const auto& warnings = _settings->GetWarnings();
hr = warnings.size() == 0 ? S_OK : S_FALSE;

View file

@ -28,6 +28,7 @@ namespace winrt::TerminalApp::implementation
~AppLogic() = default;
void Create();
bool IsUwp() const noexcept;
void RunAsUwp();
void LoadSettings();
[[nodiscard]] std::shared_ptr<::TerminalApp::CascadiaSettings> GetSettings() const noexcept;

View file

@ -22,6 +22,7 @@ namespace TerminalApp
// registered?" when it definitely is.
void Create();
Boolean IsUwp();
void RunAsUwp();
void LoadSettings();

View file

@ -48,6 +48,7 @@ public:
static std::unique_ptr<CascadiaSettings> LoadDefaults();
static std::unique_ptr<CascadiaSettings> LoadAll();
static std::unique_ptr<CascadiaSettings> LoadUniversal();
static const CascadiaSettings& GetCurrentAppSettings();

View file

@ -12,6 +12,7 @@
// defaults.h is a file containing the default json settings in a std::string_view
#include "defaults.h"
#include "defaults-universal.h"
// userDefault.h is like the above, but with a default template for the user's profiles.json.
#include "userDefaults.h"
// Both defaults.h and userDefaults.h are generated at build time into the
@ -121,6 +122,29 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
return resultPtr;
}
// Function Description:
// - Loads a batch of settings curated for the Universal variant of the terminal app
// Arguments:
// - <none>
// Return Value:
// - a unique_ptr to a CascadiaSettings with the connection types and settings for Universal terminal
std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadUniversal()
{
// We're going to do this ourselves because we want to exclude almost everything
// from the special Universal-for-developers configuration
// Create settings and get the universal defaults loaded up.
auto resultPtr = std::make_unique<CascadiaSettings>();
resultPtr->_ParseJsonString(DefaultUniversalJson, true);
resultPtr->LayerJson(resultPtr->_defaultSettings);
// Now validate.
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
return resultPtr;
}
// Function Description:
// - Creates a new CascadiaSettings object initialized with settings from the
// hardcoded defaults.json.

View file

@ -18,4 +18,5 @@ Author(s):
static constexpr std::wstring_view WslGeneratorNamespace{ L"Windows.Terminal.Wsl" };
static constexpr std::wstring_view AzureGeneratorNamespace{ L"Windows.Terminal.Azure" };
static constexpr std::wstring_view TelnetGeneratorNamespace{ L"Windows.Terminal.Telnet" };
static constexpr std::wstring_view PowershellCoreGeneratorNamespace{ L"Windows.Terminal.PowershellCore" };

View file

@ -0,0 +1,19 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- TelnetGenerator
Abstract:
- Information needed to detect a Telnet connection type.
Author(s):
- Michael Niksa - 2019-12-05
--*/
#pragma once
// {311153fb-d3f0-4ac6-b920-038de7cf5289}
static constexpr GUID TelnetConnectionType = { 0x311153fb, 0xd3f0, 0x4ac6, { 0xb9, 0x20, 0x03, 0x8d, 0xe7, 0xcf, 0x52, 0x89 } };

View file

@ -13,6 +13,7 @@
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
#include "AzureCloudShellGenerator.h" // For AzureConnectionType
#include "TelnetGenerator.h" // For TelnetConnectionType
#include "TabRowControl.h"
using namespace winrt;
@ -340,35 +341,40 @@ namespace winrt::TerminalApp::implementation
// add static items
{
// Create the settings button.
auto settingsItem = WUX::Controls::MenuFlyoutItem{};
settingsItem.Text(RS_(L"SettingsMenuItem"));
const auto isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
WUX::Controls::SymbolIcon ico{};
ico.Symbol(WUX::Controls::Symbol::Setting);
settingsItem.Icon(ico);
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
newTabFlyout.Items().Append(settingsItem);
auto settingsKeyChord = keyBindings.GetKeyBindingForAction(ShortcutAction::OpenSettings);
if (settingsKeyChord)
if (!isUwp)
{
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
// Create the settings button.
auto settingsItem = WUX::Controls::MenuFlyoutItem{};
settingsItem.Text(RS_(L"SettingsMenuItem"));
WUX::Controls::SymbolIcon ico{};
ico.Symbol(WUX::Controls::Symbol::Setting);
settingsItem.Icon(ico);
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
newTabFlyout.Items().Append(settingsItem);
auto settingsKeyChord = keyBindings.GetKeyBindingForAction(ShortcutAction::OpenSettings);
if (settingsKeyChord)
{
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
}
// Create the feedback button.
auto feedbackFlyout = WUX::Controls::MenuFlyoutItem{};
feedbackFlyout.Text(RS_(L"FeedbackMenuItem"));
WUX::Controls::FontIcon feedbackIcon{};
feedbackIcon.Glyph(L"\xE939");
feedbackIcon.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
feedbackFlyout.Icon(feedbackIcon);
feedbackFlyout.Click({ this, &TerminalPage::_FeedbackButtonOnClick });
newTabFlyout.Items().Append(feedbackFlyout);
}
// Create the feedback button.
auto feedbackFlyout = WUX::Controls::MenuFlyoutItem{};
feedbackFlyout.Text(RS_(L"FeedbackMenuItem"));
WUX::Controls::FontIcon feedbackIcon{};
feedbackIcon.Glyph(L"\xE939");
feedbackIcon.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
feedbackFlyout.Icon(feedbackIcon);
feedbackFlyout.Click({ this, &TerminalPage::_FeedbackButtonOnClick });
newTabFlyout.Items().Append(feedbackFlyout);
// Create the about button.
auto aboutFlyout = WUX::Controls::MenuFlyoutItem{};
aboutFlyout.Text(RS_(L"AboutMenuItem"));
@ -527,6 +533,12 @@ namespace winrt::TerminalApp::implementation
settings.InitialCols());
}
else if (profile->HasConnectionType() &&
profile->GetConnectionType() == TelnetConnectionType)
{
connection = TerminalConnection::TelnetConnection(settings.Commandline());
}
else
{
auto conhostConn = TerminalConnection::ConptyConnection(settings.Commandline(),

View file

@ -0,0 +1,105 @@
// THIS IS AN AUTO-GENERATED FILE! Changes to this file will be ignored.
{
"alwaysShowTabs": true,
"defaultProfile": "{550ce7b8-d500-50ad-8a1a-c400c3262db3}",
"initialCols": 120,
"initialRows": 30,
"requestedTheme": "system",
"showTabsInTitlebar": false,
"showTerminalTitleInTitlebar": true,
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
"profiles":
[
{
"guid": "{550ce7b8-d500-50ad-8a1a-c400c3262db3}",
"name": "Telnet Loopback",
"commandline": "ms-telnet-loop://127.0.0.1:23",
"connectionType" : "{311153fb-d3f0-4ac6-b920-038de7cf5289}",
"hidden": false,
"startingDirectory": "%USERPROFILE%",
"closeOnExit": "graceful",
"colorScheme": "Vintage",
"cursorColor": "#FFFFFF",
"cursorShape": "bar",
"icon": "ms-appx:///ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.png",
"padding": "8, 8, 8, 8",
"snapOnInput": true,
"useAcrylic": false
}
],
"schemes":
[
{
"name": "Vintage",
"foreground": "#C0C0C0",
"background": "#000000",
"black": "#000000",
"red": "#800000",
"green": "#008000",
"yellow": "#808000",
"blue": "#000080",
"purple": "#800080",
"cyan": "#008080",
"white": "#C0C0C0",
"brightBlack": "#808080",
"brightRed": "#FF0000",
"brightGreen": "#00FF00",
"brightYellow": "#FFFF00",
"brightBlue": "#0000FF",
"brightPurple": "#FF00FF",
"brightCyan": "#00FFFF",
"brightWhite": "#FFFFFF"
}
],
"keybindings":
[
{ "command": "closePane", "keys": [ "ctrl+shift+w" ] },
{ "command": "closeWindow", "keys": [ "alt+f4" ] },
{ "command": "copy", "keys": [ "ctrl+shift+c" ] },
{ "command": "decreaseFontSize", "keys": [ "ctrl+-" ] },
{ "command": "duplicateTab", "keys": [ "ctrl+shift+d" ] },
{ "command": "increaseFontSize", "keys": [ "ctrl+=" ] },
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": [ "alt+down" ] },
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": [ "alt+left" ] },
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": [ "alt+right" ] },
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": [ "alt+up" ] },
{ "command": "newTab", "keys": [ "ctrl+shift+t" ] },
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+1"] },
{ "command": { "action": "newTab", "index": 1 }, "keys": ["ctrl+shift+2"] },
{ "command": { "action": "newTab", "index": 2 }, "keys": ["ctrl+shift+3"] },
{ "command": { "action": "newTab", "index": 3 }, "keys": ["ctrl+shift+4"] },
{ "command": { "action": "newTab", "index": 4 }, "keys": ["ctrl+shift+5"] },
{ "command": { "action": "newTab", "index": 5 }, "keys": ["ctrl+shift+6"] },
{ "command": { "action": "newTab", "index": 6 }, "keys": ["ctrl+shift+7"] },
{ "command": { "action": "newTab", "index": 7 }, "keys": ["ctrl+shift+8"] },
{ "command": { "action": "newTab", "index": 8 }, "keys": ["ctrl+shift+9"] },
{ "command": "nextTab", "keys": [ "ctrl+tab" ] },
{ "command": "openNewTabDropdown", "keys": [ "ctrl+shift+space" ] },
{ "command": "openSettings", "keys": [ "ctrl+," ] },
{ "command": "paste", "keys": [ "ctrl+shift+v" ] },
{ "command": "prevTab", "keys": [ "ctrl+shift+tab" ] },
{ "command": "resetFontSize", "keys": ["ctrl+0"]},
{ "command": { "action": "resizePane", "direction": "down" }, "keys": [ "alt+shift+down" ] },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": [ "alt+shift+left" ] },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": [ "alt+shift+right" ] },
{ "command": { "action": "resizePane", "direction": "up" }, "keys": [ "alt+shift+up" ] },
{ "command": "scrollDown", "keys": [ "ctrl+shift+down" ] },
{ "command": "scrollDownPage", "keys": [ "ctrl+shift+pgdn" ] },
{ "command": "scrollUp", "keys": [ "ctrl+shift+up" ] },
{ "command": "scrollUpPage", "keys": [ "ctrl+shift+pgup" ] },
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": [ "alt+shift+-" ] },
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": [ "alt+shift+plus" ] },
{ "command": { "action": "switchToTab", "index": 0 }, "keys": ["ctrl+alt+1"] },
{ "command": { "action": "switchToTab", "index": 1 }, "keys": ["ctrl+alt+2"] },
{ "command": { "action": "switchToTab", "index": 2 }, "keys": ["ctrl+alt+3"] },
{ "command": { "action": "switchToTab", "index": 3 }, "keys": ["ctrl+alt+4"] },
{ "command": { "action": "switchToTab", "index": 4 }, "keys": ["ctrl+alt+5"] },
{ "command": { "action": "switchToTab", "index": 5 }, "keys": ["ctrl+alt+6"] },
{ "command": { "action": "switchToTab", "index": 6 }, "keys": ["ctrl+alt+7"] },
{ "command": { "action": "switchToTab", "index": 7 }, "keys": ["ctrl+alt+8"] },
{ "command": { "action": "switchToTab", "index": 8 }, "keys": ["ctrl+alt+9"] },
{ "command": "toggleFullscreen", "keys": [ "alt+enter" ] },
{ "command": "toggleFullscreen", "keys": [ "f11" ] }
]
}

View file

@ -10,14 +10,12 @@
<ConfigurationType>StaticLibrary</ConfigurationType>
<SubSystem>Console</SubSystem>
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<!--
DON'T REDIRECT OUR OUTPUT.
Setting this will tell cppwinrt.build.post.props to copy our output from
the default OutDir up one level, so the wapproj will be able to find it.
-->
<NoOutputRedirection>true</NoOutputRedirection>
<!--
This is an override that, puzzlingly, _forces XBF (XAML binary) embedding_.
We have to do this to overcome a layout issue in the WAP packaging project.
@ -25,10 +23,8 @@
-->
<DisableEmbeddedXbf>false</DisableEmbeddedXbf>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= XAML files ======================== -->
<ItemGroup>
<!-- HERE BE DRAGONS:
@ -54,7 +50,6 @@
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="../App.base.h" />
@ -86,27 +81,27 @@
<ClInclude Include="../PowershellCoreProfileGenerator.h" />
<ClInclude Include="../WslDistroGenerator.h" />
<ClInclude Include="../AzureCloudShellGenerator.h" />
<ClInclude Include="../TelnetGenerator.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="../ShortcutActionDispatch.h" >
<ClInclude Include="../ShortcutActionDispatch.h">
<DependentUpon>../ShortcutActionDispatch.idl</DependentUpon>
</ClInclude>
<ClInclude Include="../ActionArgs.h" >
<ClInclude Include="../ActionArgs.h">
<DependentUpon>../ActionArgs.idl</DependentUpon>
</ClInclude>
<ClInclude Include="../ActionAndArgs.h" >
<ClInclude Include="../ActionAndArgs.h">
<DependentUpon>../ActionArgs.idl</DependentUpon>
</ClInclude>
<ClInclude Include="../AppKeyBindings.h" >
<ClInclude Include="../AppKeyBindings.h">
<DependentUpon>../AppKeyBindings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="../App.h" >
<ClInclude Include="../App.h">
<DependentUpon>../App.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="../AppLogic.h" >
<ClInclude Include="../AppLogic.h">
<DependentUpon>../AppLogic.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="../init.cpp" />
@ -141,41 +136,39 @@
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="../AppKeyBindings.cpp" >
<ClCompile Include="../AppKeyBindings.cpp">
<DependentUpon>../AppKeyBindings.idl</DependentUpon>
</ClCompile>
<ClCompile Include="../ShortcutActionDispatch.cpp" >
<ClCompile Include="../ShortcutActionDispatch.cpp">
<DependentUpon>../ShortcutActionDispatch.idl</DependentUpon>
</ClCompile>
<ClCompile Include="../ActionAndArgs.cpp" >
<ClCompile Include="../ActionAndArgs.cpp">
<DependentUpon>../ActionArgs.idl</DependentUpon>
</ClCompile>
<ClCompile Include="../ActionArgs.cpp" >
<ClCompile Include="../ActionArgs.cpp">
<DependentUpon>../ActionArgs.idl</DependentUpon>
</ClCompile>
<ClCompile Include="../App.cpp" >
<ClCompile Include="../App.cpp">
<DependentUpon>../App.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="../AppActionHandlers.cpp" >
<ClCompile Include="../AppActionHandlers.cpp">
<DependentUpon>../App.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="../AppLogic.cpp" >
<ClCompile Include="../AppLogic.cpp">
<DependentUpon>../AppLogic.idl</DependentUpon>
</ClCompile>
<!-- You _NEED_ to include this file and the jsoncpp IncludePath (below) if
you want to use jsoncpp -->
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
<!-- If you add idl files here, make sure to include their implementation's
header in TerminalApp.vcxproj (as well as in this file) -->
<Midl Include="../App.idl" >
<Midl Include="../App.idl">
<DependentUpon>../App.xaml</DependentUpon>
</Midl>
<Midl Include="../ShortcutActionDispatch.idl" />
@ -199,13 +192,11 @@
<SubType>Code</SubType>
</Midl>
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<PRIResource Include="../Resources/en-US/Resources.resw" />
<None Include="../packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
<!--
@ -214,59 +205,49 @@
you also update all the consumers
-->
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<!-- For whatever reason, we can't include the TerminalControl and
TerminalSettings projects' winmds via project references. So we'll have to
manually include the winmds as References below -->
</ItemGroup>
<PropertyGroup>
<!-- A small helper for paths to the compiled cppwinrt projects -->
<_BinRoot Condition="'$(Platform)' != 'Win32'">$(OpenConsoleDir)$(Platform)\$(Configuration)\</_BinRoot>
<_BinRoot Condition="'$(Platform)' == 'Win32'">$(OpenConsoleDir)$(Configuration)\</_BinRoot>
</PropertyGroup>
<PropertyGroup>
<!-- This is a hack to get the ARM64 CI build working. See
https://github.com/Microsoft/msbuild/issues/3746 - it looks like MsBuild
just has a bug in it.-->
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>Warning</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
</PropertyGroup>
<ItemGroup>
<!-- Manually add references to each of our dependent winmds. Mark them as
private=false and CopyLocalSatelliteAssemblies=false, so that we don't
propogate them upwards (which can make referencing this project result in
duplicate type definitions)-->
<Reference Include="Microsoft.Terminal.Settings">
<HintPath>$(_BinRoot)TerminalSettings\Microsoft.Terminal.Settings.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.TerminalConnection">
<HintPath>$(_BinRoot)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.TerminalControl">
<HintPath>$(_BinRoot)TerminalControl\Microsoft.Terminal.TerminalControl.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
</ItemGroup>
<!-- ====================== Compiler & Linker Flags ===================== -->
<ItemDefinitionGroup>
<ClCompile>
@ -279,11 +260,8 @@
<AdditionalDependencies>WindowsApp.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- Manually include MUX here, instead of importing its targets file. We need
to reference its winmd, but very specifically with the
CopyLocalSatelliteAssemblies and Private properties set to false, as to not
@ -306,7 +284,6 @@
</Reference>
<ReferenceCopyLocalPaths Include="$(_MUXRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.UI.Xaml.dll" />
<ReferenceCopyLocalPaths Include="$(_MUXRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.UI.Xaml.pri" />
<!-- Microsoft.UI.XAML.Application -->
<Reference Include="$(_MUXAppRoot)lib\uap10.0\Microsoft.Toolkit.Win32.UI.XamlHost.winmd">
<Implementation>Microsoft.Toolkit.Win32.UI.XamlHost.dll</Implementation>
@ -318,7 +295,6 @@
<ReferenceCopyLocalPaths Include="$(_MUXAppRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.Toolkit.Win32.UI.XamlHost.*" />
</ItemGroup>
<!-- End MUX import -->
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
@ -326,8 +302,6 @@
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>
<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
@ -347,22 +321,18 @@
</PackagingOutputs>
</ItemGroup>
</Target>
<!-- This target will take our defaults.json and stamp it into a .h file that
we can include in the code directly. This way, we don't need to worry about
failing to load the default settings at runtime. -->
<Target Name="_TerminalAppGenerateDefaultsH"
Inputs="..\defaults.json"
Outputs="Generated Files\defaults.h"
BeforeTargets="BeforeClCompile">
<Target Name="_TerminalAppGenerateDefaultsH" Inputs="..\defaults.json" Outputs="Generated Files\defaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile ..\defaults.json -OutPath '&quot;Generated Files\defaults.h&quot;' -VariableName DefaultJson" />
</Target>
<!-- A different set of defaults for Universal variant -->
<Target Name="_TerminalAppGenerateDefaultsUniversalH" Inputs="..\defaults-universal.json" Outputs="Generated Files\defaults-universal.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile ..\defaults-universal.json -OutPath '&quot;Generated Files\defaults-universal.h&quot;' -VariableName DefaultUniversalJson" />
</Target>
<!-- Same as above, but for the default profiles.json template -->
<Target Name="_TerminalAppGenerateUserSettingsH"
Inputs="..\userDefaults.json"
Outputs="Generated Files\userDefaults.h"
BeforeTargets="BeforeClCompile">
<Target Name="_TerminalAppGenerateUserSettingsH" Inputs="..\userDefaults.json" Outputs="Generated Files\userDefaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile ..\userDefaults.json -OutPath '&quot;Generated Files\userDefaults.h&quot;' -VariableName UserSettingsJson" />
</Target>
</Project>
</Project>

View file

@ -2,6 +2,11 @@
// Licensed under the MIT license.
#include "pch.h"
// We have to define GSL here, not PCH
// because TelnetConnection has a conflicting GSL implementation.
#include <gsl/gsl>
#include "AzureConnection.h"
#include "AzureClientID.h"
#include <sstream>

View file

@ -2,6 +2,11 @@
// Licensed under the MIT license.
#include "pch.h"
// We have to define GSL here, not PCH
// because TelnetConnection has a conflicting GSL implementation.
#include <gsl/gsl>
#include "ConptyConnection.h"
#include <windows.h>

View file

@ -214,4 +214,7 @@
<comment>The first argument is the hexadecimal error code. The second is the process name.
If this resource spans multiple lines, it will not be displayed properly. Yeah.</comment>
</data>
<data name="TelnetInternetOrServerIssue" xml:space="preserve">
<value>Could not connect to telnet server.</value>
</data>
</root>

View file

@ -0,0 +1,315 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TelnetConnection.h"
#include <LibraryResources.h>
#include "TelnetConnection.g.cpp"
#include "../../types/inc/Utils.hpp"
using namespace ::Microsoft::Console;
constexpr std::wstring_view telnetScheme = L"telnet";
constexpr std::wstring_view msTelnetLoopbackScheme = L"ms-telnet-loop";
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
TelnetConnection::TelnetConnection(const hstring& uri) :
_reader{ nullptr },
_writer{ nullptr },
_uri{ uri }
{
_session.install(_nawsServer);
_nawsServer.activate([](auto&&) {});
}
// Method description:
// - ascribes to the ITerminalConnection interface
// - creates the output thread
void TelnetConnection::Start()
try
{
// Create our own output handling thread
// Each connection needs to make sure to drain the output from its backing host.
_hOutputThread.reset(CreateThread(
nullptr,
0,
[](LPVOID lpParameter) {
auto pInstance = reinterpret_cast<TelnetConnection*>(lpParameter);
return pInstance->_outputThread();
},
this,
0,
nullptr));
THROW_LAST_ERROR_IF_NULL(_hOutputThread);
_transitionToState(ConnectionState::Connecting);
// Set initial winodw title.
_TerminalOutputHandlers(L"\x1b]0;Telnet\x7");
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
_transitionToState(ConnectionState::Failed);
}
// Method description:
// - ascribes to the ITerminalConnection interface
// - handles the different possible inputs in the different states
// Arguments:
// the user's input
void TelnetConnection::WriteInput(hstring const& data)
{
if (!_isStateOneOf(ConnectionState::Connected, ConnectionState::Connecting))
{
return;
}
auto str = winrt::to_string(data);
if (str.size() == 1 && str.at(0) == L'\r')
{
str = "\r\n";
}
telnetpp::bytes bytes(reinterpret_cast<const uint8_t*>(str.data()), str.size());
_session.send(bytes, [=](telnetpp::bytes data) {
_socketSend(data);
});
}
// Method description:
// - ascribes to the ITerminalConnection interface
// - resizes the terminal
// Arguments:
// - the new rows/cols values
void TelnetConnection::Resize(uint32_t rows, uint32_t columns)
{
if (_prevResize.has_value() && _prevResize.value().first == rows && _prevResize.value().second == columns)
{
return;
}
_prevResize.emplace(std::pair{ rows, columns });
_nawsServer.set_window_size(gsl::narrow<uint16_t>(columns),
gsl::narrow<uint16_t>(rows),
[=](telnetpp::subnegotiation sub) {
_session.send(sub,
[=](telnetpp::bytes data) {
_socketBufferedSend(data);
});
_socketFlushBuffer();
});
}
// Method description:
// - ascribes to the ITerminalConnection interface
// - closes the socket connection and the output thread
void TelnetConnection::Close()
try
{
if (_transitionToState(ConnectionState::Closing))
{
_socket.Close();
if (_hOutputThread)
{
// Tear down our output thread
WaitForSingleObject(_hOutputThread.get(), INFINITE);
_hOutputThread.reset();
}
_transitionToState(ConnectionState::Closed);
}
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
_transitionToState(ConnectionState::Failed);
}
// Method description:
// - this is the output thread, where we initiate the connection to the remote host
// and establish a socket connection
// Return value:
// - return status
DWORD TelnetConnection::_outputThread()
try
{
while (true)
{
if (_isStateOneOf(ConnectionState::Failed))
{
_TerminalOutputHandlers(RS_(L"TelnetInternetOrServerIssue") + L"\r\n");
return E_FAIL;
}
else if (_isStateAtOrBeyond(ConnectionState::Closing))
{
return S_FALSE;
}
else if (_isStateOneOf(ConnectionState::Connecting))
{
try
{
const auto uri = Windows::Foundation::Uri(_uri);
const auto host = Windows::Networking::HostName(uri.Host());
bool autoLogin = false;
// If we specified the special ms loopback scheme, then set autologin and proceed below.
if (msTelnetLoopbackScheme == uri.SchemeName())
{
autoLogin = true;
}
// Otherwise, make sure we said telnet://, anything else is not supported here.
else if (telnetScheme != uri.SchemeName())
{
THROW_HR(E_INVALIDARG);
}
_socket.ConnectAsync(host, winrt::to_hstring(uri.Port())).get();
_writer = Windows::Storage::Streams::DataWriter(_socket.OutputStream());
_reader = Windows::Storage::Streams::DataReader(_socket.InputStream());
_reader.InputStreamOptions(Windows::Storage::Streams::InputStreamOptions::Partial); // returns when 1 or more bytes ready.
_transitionToState(ConnectionState::Connected);
if (autoLogin)
{
// Send newline to bypass User Name prompt.
const auto newline = winrt::to_hstring("\r\n");
WriteInput(newline);
// Wait for login.
Sleep(1000);
// Send "cls" enter to clear the thing and just look like a prompt.
const auto clearScreen = winrt::to_hstring("cls\r\n");
WriteInput(clearScreen);
}
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
_transitionToState(ConnectionState::Failed);
}
}
else if (_isStateOneOf(ConnectionState::Connected))
{
// Read from socket
const auto amountReceived = _socketReceive(_receiveBuffer);
_session.receive(
telnetpp::bytes{ _receiveBuffer.data(), amountReceived },
[=](telnetpp::bytes data,
std::function<void(telnetpp::bytes)> const& send) {
_applicationReceive(data, send);
},
[=](telnetpp::bytes data) {
_socketSend(data);
});
}
}
}
catch (...)
{
// If the exception was thrown while we were already supposed to be closing, fine. We're closed.
// This is because the socket got mad things were being torn down.
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
_transitionToState(ConnectionState::Closed);
return S_OK;
}
else
{
LOG_CAUGHT_EXCEPTION();
_transitionToState(ConnectionState::Failed);
return E_FAIL;
}
}
// Routine Description:
// - Call to buffer up bytes to send to the remote device.
// - You must flush before they'll go out.
// Arguments:
// - data - View of bytes to be sent
// Return Value:
// - <none>
void TelnetConnection::_socketBufferedSend(telnetpp::bytes data)
{
const uint8_t* first = data.data();
const uint8_t* last = data.data() + data.size();
const winrt::array_view<const uint8_t> arrayView(first, last);
_writer.WriteBytes(arrayView);
}
// Routine Description:
// - Flushes any buffered bytes to the underlying socket
// Arguments:
// - <none>
// Return Value:
// - <none>
fire_and_forget TelnetConnection::_socketFlushBuffer()
{
co_await _writer.StoreAsync();
}
// Routine Description:
// - Used to send bytes into the socket to the remote device
// Arguments:
// - data - View of bytes to be sent
// Return Value:
// - <none>
void TelnetConnection::_socketSend(telnetpp::bytes data)
{
_socketBufferedSend(data);
_socketFlushBuffer();
}
// Routine Description:
// - Reads bytes from the socket into the given array.
// Arguments:
// - buffer - The array of bytes to use for storage
// Return Value:
// - The number of bytes actually read (less than or equal to input array size)
size_t TelnetConnection::_socketReceive(gsl::span<telnetpp::byte> buffer)
{
const auto bytesLoaded = _reader.LoadAsync(gsl::narrow<uint32_t>(buffer.size())).get();
// winrt::array_view, despite having a pointer and size constructor
// hides it as protected.
// So we have to get first/last (even though cppcorechecks will be
// mad at us for it) to use a public array_view constructor.
// The WinRT team isn't fixing this because std::span is coming
// soon and that will do it.
// So just do this for now and suppress the warnings.
const auto first = buffer.data();
const auto last = first + bytesLoaded;
const winrt::array_view<uint8_t> arrayView(first, last);
_reader.ReadBytes(arrayView);
return bytesLoaded;
}
// Routine Description:
// - Called by telnetpp framework when application data is received on the channel
// In contrast, telnet metadata payload is consumed by telnetpp and not forwarded to us.
// Arguments:
// - data - The relevant application-level payload received
// - send - A function where we can send a reply to given data immediately
// in reaction to the received message.
// Return Value:
// - <none>
void TelnetConnection::_applicationReceive(telnetpp::bytes data,
std::function<void(telnetpp::bytes)> const& /*send*/)
{
// Convert telnetpp bytes to standard string_view
const auto stringView = std::string_view{ reinterpret_cast<const char*>(data.data()), gsl::narrow<size_t>(data.size()) };
// Convert to hstring
const auto hstr = winrt::to_hstring(stringView);
// Pass the output to our registered event handlers
_TerminalOutputHandlers(hstr);
}
}

View file

@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "TelnetConnection.g.h"
#include <mutex>
#include <condition_variable>
#pragma warning(push)
#pragma warning(disable : 4100)
#pragma warning(disable : 4251)
#include <telnetpp/core.hpp>
#include <telnetpp/session.hpp>
#include <telnetpp/options/naws/server.hpp>
#pragma warning(pop)
#include "winrt/Windows.Networking.Sockets.h"
#include "winrt/Windows.Storage.Streams.h"
#include "../cascadia/inc/cppwinrt_utils.h"
#include "ConnectionStateHolder.h"
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
struct TelnetConnection : TelnetConnectionT<TelnetConnection>, ConnectionStateHolder<TelnetConnection>
{
TelnetConnection(const hstring& uri);
void Start();
void WriteInput(hstring const& data);
void Resize(uint32_t rows, uint32_t columns);
void Close();
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
private:
hstring _uri;
void _applicationReceive(telnetpp::bytes data,
std::function<void(telnetpp::bytes)> const& send);
void _socketBufferedSend(telnetpp::bytes data);
fire_and_forget _socketFlushBuffer();
void _socketSend(telnetpp::bytes data);
size_t _socketReceive(gsl::span<telnetpp::byte> buffer);
telnetpp::session _session;
// NAWS = Negotiation About Window Size
telnetpp::options::naws::server _nawsServer;
Windows::Networking::Sockets::StreamSocket _socket;
Windows::Storage::Streams::DataWriter _writer;
Windows::Storage::Streams::DataReader _reader;
std::optional<std::pair<uint32_t, uint32_t>> _prevResize;
static constexpr size_t _receiveBufferSize = 1024;
std::array<telnetpp::byte, _receiveBufferSize> _receiveBuffer;
wil::unique_handle _hOutputThread;
DWORD _outputThread();
};
}
namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation
{
struct TelnetConnection : TelnetConnectionT<TelnetConnection, implementation::TelnetConnection>
{
};
}

View file

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ITerminalConnection.idl";
namespace Microsoft.Terminal.TerminalConnection
{
[default_interface]
runtimeclass TelnetConnection : ITerminalConnection
{
TelnetConnection(String uri);
};
}

View file

@ -7,7 +7,6 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
<SubSystem>Console</SubSystem>
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<!--
DON'T REDIRECT OUR OUTPUT.
Setting this will tell cppwinrt.build.post.props to copy our output from
@ -15,13 +14,13 @@
-->
<NoOutputRedirection>true</NoOutputRedirection>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemGroup>
<ClInclude Include="AzureClientID.h" />
<ClInclude Include="AzureConnection.h" />
<ClInclude Include="AzureConnection.h">
<DependentUpon>AzureConnection.idl</DependentUpon>
</ClInclude>
<ClInclude Include="pch.h" />
<ClInclude Include="ConptyConnection.h">
<DependentUpon>ConptyConnection.idl</DependentUpon>
@ -29,10 +28,15 @@
<ClInclude Include="EchoConnection.h">
<DependentUpon>EchoConnection.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TelnetConnection.h">
<DependentUpon>TelnetConnection.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="init.cpp"/>
<ClCompile Include="AzureConnection.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="AzureConnection.cpp">
<DependentUpon>AzureConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@ -43,18 +47,21 @@
<DependentUpon>ConptyConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="TelnetConnection.cpp">
<DependentUpon>TelnetConnection.idl</DependentUpon>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Midl Include="ITerminalConnection.idl" />
<Midl Include="ConptyConnection.idl" />
<Midl Include="EchoConnection.idl" />
<Midl Include="AzureConnection.idl" />
<Midl Include="TelnetConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/en-US/Resources.resw" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
<!--
@ -65,30 +72,27 @@
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj">
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="..\..\winconpty\lib\winconptylib.vcxproj">
<Project>{58a03bb2-df5a-4b66-91a0-7ef3ba01269a}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets" Condition="Exists('..\..\..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets'))" />
<Error Condition="!Exists('..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets'))" />
</Target>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>$(OpenConsoleCommonOutDir)\conptylib.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>
<Import Project="..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets" Condition="Exists('..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets')" />
</Project>

View file

@ -18,18 +18,21 @@
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="AzureConnection.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="TelnetConnection.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="EchoConnection.h" />
<ClInclude Include="AzureConnection.h" />
<ClInclude Include="AzureClientID.h" />
<ClInclude Include="TelnetConnection.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="ITerminalConnection.idl" />
<Midl Include="EchoConnection.idl" />
<Midl Include="AzureConnection.idl" />
<Midl Include="ConptyConnection.idl" />
<Midl Include="TelnetConnection.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -42,4 +45,4 @@
<Filter>Resources\en-US</Filter>
</PRIResource>
</ItemGroup>
</Project>
</Project>

View file

@ -2,4 +2,5 @@
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
<package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
</packages>
<package id="vcpkg-telnetpp" version="1.0.1" targetFramework="native" />
</packages>

View file

@ -10,6 +10,8 @@
// Needs to be defined or we get redeclaration errors
#define WIN32_LEAN_AND_MEAN
#define BLOCK_GSL
#include <LibraryIncludes.h>
// Must be included before any WinRT headers.

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="ScopedResourceLoader.cpp" />
<ClCompile Include="LibraryResources.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="inc\ScopedResourceLoader.h" />
<ClInclude Include="inc\LibraryResources.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View file

@ -43,5 +43,6 @@
<Capabilities>
<Capability Name="internetClient" />
<Capability Name="privateNetworkClientServer"/>
</Capabilities>
</Package>

View file

@ -59,8 +59,10 @@
// GSL
// Block GSL Multi Span include because it both has C++17 deprecated iterators
// and uses the C-namespaced "max" which conflicts with Windows definitions.
#ifndef BLOCK_GSL
#define GSL_MULTI_SPAN_H
#include <gsl/gsl>
#endif
// CppCoreCheck
#include <CppCoreCheck/Warnings.h>