diff --git a/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Environment.cs b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Environment.cs
index 040d1ba901c..b429c01c15a 100644
--- a/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Environment.cs
+++ b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Environment.cs
@@ -7,14 +7,9 @@
internal static partial class Interop
{
- private static class Libraries
- {
- internal const string SystemPrivateCoreLibNative = "System.Private.CoreLib.Native";
- }
-
internal unsafe partial class Sys
{
- [DllImport(Libraries.SystemPrivateCoreLibNative)]
+ [DllImport(Interop.Libraries.SystemPrivateCoreLibNative)]
internal static unsafe extern int GetEnvironmentVariable(byte* name, out IntPtr result);
}
}
diff --git a/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Libraries.cs b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Libraries.cs
new file mode 100644
index 00000000000..0737bd1df41
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Libraries.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+internal static partial class Interop
+{
+ internal static class Libraries
+ {
+ internal const string SystemPrivateCoreLibNative = "System.Private.CoreLib.Native";
+ }
+}
diff --git a/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Write.cs b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Write.cs
new file mode 100644
index 00000000000..cab0a2f3b72
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Private.CoreLib.Native/Interop.Write.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ ///
+ /// Writes the specified buffer to the provided open file descriptor
+ ///
+ /// The file descriptor to try and write to
+ /// The data to attempt to write
+ /// The amount of data to write, in bytes
+ ///
+ /// Returns the number of bytes written on success; otherwise, returns -1 and sets errno
+ ///
+ [DllImport(Interop.Libraries.SystemPrivateCoreLibNative, SetLastError = true)]
+ internal static unsafe extern int Write2(int fd, byte* buffer, int bufferSize);
+ }
+}
diff --git a/src/Common/src/Interop/Unix/libc/Interop.FileDescriptors.cs b/src/Common/src/Interop/Unix/libc/Interop.FileDescriptors.cs
new file mode 100644
index 00000000000..4eeec2ddbf9
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libc/Interop.FileDescriptors.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ internal static class FileDescriptors
+ {
+ internal const int STDIN_FILENO = 0;
+ internal const int STDOUT_FILENO = 1;
+ internal const int STDERR_FILENO = 2;
+ }
+ }
+}
diff --git a/src/Common/src/Interop/Windows/mincore/Interop.Console.cs b/src/Common/src/Interop/Windows/mincore/Interop.Console.cs
new file mode 100644
index 00000000000..1e4526a9011
--- /dev/null
+++ b/src/Common/src/Interop/Windows/mincore/Interop.Console.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ private static class Libraries
+ {
+ internal const string Process = "api-ms-win-core-processenvironment-l1-1-0.dll";
+ internal const string Console = "api-ms-win-core-console-l1-1-0.dll";
+ }
+
+ internal static unsafe partial class mincore
+ {
+ [DllImport("Libraries.Process")]
+ internal static extern IntPtr GetStdHandle(int nStdHandle);
+
+ [DllImport("Libraries.Console", EntryPoint = "WriteConsoleW")]
+ internal static unsafe extern bool WriteConsole(IntPtr hConsoleOutput, byte* lpBuffer, int nNumberOfCharsToWrite, out int lpNumberOfCharsWritten, IntPtr lpReservedMustBeNull);
+ }
+}
diff --git a/src/Common/src/Interop/Windows/mincore/Interop.HandleTypes.cs b/src/Common/src/Interop/Windows/mincore/Interop.HandleTypes.cs
new file mode 100644
index 00000000000..4076d0e3eb5
--- /dev/null
+++ b/src/Common/src/Interop/Windows/mincore/Interop.HandleTypes.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+internal partial class Interop
+{
+ internal partial class mincore
+ {
+ internal partial class HandleTypes
+ {
+ internal const int INVALID_HANDLE_VALUE = -1;
+ internal const int STD_ERROR_HANDLE = -12;
+ }
+ }
+}
diff --git a/src/Native/Common/pal_utilities.h b/src/Native/Common/pal_utilities.h
new file mode 100644
index 00000000000..e78f68d73a9
--- /dev/null
+++ b/src/Native/Common/pal_utilities.h
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+/**
+* Cast a positive value typed as a signed integer to the
+* appropriately sized unsigned integer type.
+*
+* We use this when we've already ensured that the value is positive,
+* but we don't want to cast to a specific unsigned type as that could
+* inadvertently defeat the compiler's narrowing conversion warnings
+* (which we treat as error).
+*/
+template
+inline typename std::make_unsigned::type UnsignedCast(T value)
+{
+ assert(value >= 0);
+ return static_cast::type>(value);
+}
+
diff --git a/src/Native/System.Private.CoreLib.Native/CMakeLists.txt b/src/Native/System.Private.CoreLib.Native/CMakeLists.txt
index b76b3964a80..cfd25e88964 100644
--- a/src/Native/System.Private.CoreLib.Native/CMakeLists.txt
+++ b/src/Native/System.Private.CoreLib.Native/CMakeLists.txt
@@ -3,6 +3,7 @@ project(System.Private.CoreLib.Native)
set(NATIVE_SOURCES
pal_environment.cpp
pal_memory.cpp
+ pal_io.cpp
)
add_library(System.Private.CoreLib.Native
diff --git a/src/Native/System.Private.CoreLib.Native/pal_io.cpp b/src/Native/System.Private.CoreLib.Native/pal_io.cpp
new file mode 100644
index 00000000000..04fed95e131
--- /dev/null
+++ b/src/Native/System.Private.CoreLib.Native/pal_io.cpp
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "pal_utilities.h"
+
+extern "C" int32_t Write2(int32_t fd, const void* buffer, int32_t bufferSize)
+{
+ assert(buffer != nullptr || bufferSize == 0);
+ assert(bufferSize >= 0);
+
+ if (bufferSize < 0)
+ {
+ errno = ERANGE;
+ return -1;
+ }
+
+ ssize_t count = write(fd, buffer, UnsignedCast(bufferSize));
+ assert(count >= -1 && count <= bufferSize);
+ return static_cast(count);
+}
+
diff --git a/src/Native/configure.cmake b/src/Native/configure.cmake
index 81fc4eeccc9..bb318957174 100644
--- a/src/Native/configure.cmake
+++ b/src/Native/configure.cmake
@@ -3,6 +3,8 @@ include(CheckStructHasMember)
include(CheckCXXSourceCompiles)
include(CheckCXXSourceRuns)
+add_compile_options(-I${CMAKE_CURRENT_SOURCE_DIR}/Common)
+
if (NOT WIN32)
include_directories(SYSTEM /usr/local/include)
endif ()
diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 64a2f5d8711..02370513c25 100644
--- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -480,7 +480,7 @@
-
+
@@ -492,7 +492,7 @@
-
+
@@ -500,6 +500,7 @@
+
diff --git a/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/ConsolePal.Unix.cs b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/ConsolePal.Unix.cs
new file mode 100644
index 00000000000..2be4652d6cc
--- /dev/null
+++ b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/ConsolePal.Unix.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace Internal.DeveloperExperience
+{
+ internal static class ConsolePal
+ {
+ internal unsafe static void WriteError(string errorMessage)
+ {
+ byte[] errorMessageAsBytes = Interop.StringHelper.GetBytesFromUTF8string(errorMessage);
+ fixed (byte* pBuffer = errorMessageAsBytes)
+ {
+ Interop.Sys.Write2(Interop.Sys.FileDescriptors.STDERR_FILENO, pBuffer, errorMessageAsBytes.Length);
+ }
+
+ // Write new line
+ byte newLine = (byte) '\n';
+ Interop.Sys.Write2(Interop.Sys.FileDescriptors.STDERR_FILENO, &newLine, 1);
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/ConsolePal.Windows.cs b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/ConsolePal.Windows.cs
new file mode 100644
index 00000000000..f9ed5eacb77
--- /dev/null
+++ b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/ConsolePal.Windows.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace Internal.DeveloperExperience
+{
+ internal static class ConsolePal
+ {
+ private static IntPtr s_consoleErrorHandle = Interop.mincore.GetStdHandle(Interop.mincore.HandleTypes.STD_ERROR_HANDLE);
+
+ internal unsafe static void WriteError(string errorMessage)
+ {
+ if (s_consoleErrorHandle == new IntPtr(Interop.mincore.HandleTypes.INVALID_HANDLE_VALUE))
+ {
+ // ensure we have a valid handle before writing to it
+ return;
+ }
+
+ fixed (char *pBuffer = errorMessage)
+ {
+ int numberOfCharsWritten;
+ Interop.mincore.WriteConsole(s_consoleErrorHandle, (byte*)pBuffer, errorMessage.Length, out numberOfCharsWritten, IntPtr.Zero);
+ }
+
+ // Write new line
+ fixed (char* pBuffer = "\r\n")
+ {
+ int numberOfCharsWritten;
+ Interop.mincore.WriteConsole(s_consoleErrorHandle, (byte*)pBuffer, 2, out numberOfCharsWritten, IntPtr.Zero);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConnector.cs b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConnector.cs
new file mode 100644
index 00000000000..c1aa191357b
--- /dev/null
+++ b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConnector.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+
+namespace Internal.DeveloperExperience
+{
+ public static class DeveloperExperienceConnectorConsole
+ {
+ // This method is targeted by the MainMethodInjector transform. This method exists in various DeveloperExperience.*.dll's.
+ // The MainMethodInjector chooses an appropriate one (or none) depending on the build configuration and app structure.
+ //
+ // The Console DeveloperExperience is chosen if the main exe has a reference to System.Console. This is for internal Microsoft use only.
+ // It should remain small enough that we don't bother shutting it off on retail builds.
+ public static void Initialize()
+ {
+ DeveloperExperience.Default = new DeveloperExperienceConsole();
+ return;
+ }
+ }
+}
+
+
diff --git a/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs
new file mode 100644
index 00000000000..17121740b18
--- /dev/null
+++ b/src/System.Private.DeveloperExperience.Console/src/Internal/DeveloperExperience/DeveloperExperienceConsole.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using global::System;
+using global::System.Diagnostics;
+using global::Internal.StackTraceGenerator;
+
+namespace Internal.DeveloperExperience
+{
+ internal sealed class DeveloperExperienceConsole : DeveloperExperience
+ {
+ public sealed override void WriteLine(String s)
+ {
+ ConsolePal.WriteError(s);
+ }
+
+ public sealed override String CreateStackTraceString(IntPtr ip, bool includeFileInfo)
+ {
+ String s = Internal.StackTraceGenerator.StackTraceGenerator.CreateStackTraceString(ip, includeFileInfo);
+ if (s != null)
+ return s;
+ return base.CreateStackTraceString(ip, includeFileInfo);
+ }
+
+ public sealed override void TryGetSourceLineInfo(IntPtr ip, out string fileName, out int lineNumber, out int columnNumber)
+ {
+ Internal.StackTraceGenerator.StackTraceGenerator.TryGetSourceLineInfo(ip, out fileName, out lineNumber, out columnNumber);
+ // we take whatever data StackTraceGenerator can get (none/partial/all). No reason to fall-back because the base-type
+ // never finds anything.
+ }
+ }
+}
+
diff --git a/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj b/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj
new file mode 100644
index 00000000000..3518511d02d
--- /dev/null
+++ b/src/System.Private.DeveloperExperience.Console/src/System.Private.DeveloperExperience.Console.csproj
@@ -0,0 +1,71 @@
+
+
+
+
+ System.Private.DeveloperExperience.Console
+ Library
+ {F9EF39E7-C8E4-4776-A952-FEF7A1FC2D3B}
+ true
+
+ v4.5
+ Portable
+ .NETPortable
+ Profile7
+ .NET Portable Subset
+ false
+
+
+
+
+ x86
+
+
+ x86
+
+
+ x64
+
+
+ x64
+
+
+ arm
+
+
+ arm
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+