From fadeac68861d5753296c874bd7dcebe804a55c32 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Wed, 27 May 2020 16:52:54 +0100 Subject: [PATCH 1/2] [Xamarin.Android.Build.Tasks] Catch Exception when setting Console.InputEncoding Context https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1130414 For some unknown reason the setting of `Console.InputEncoding` on a customers machine is throwing an IOException. This then causes Visual Studio to completely crash. ``` Description: The process was terminated due to an unhandled exception. Exception Info: System.IO.IOException at System.IO.__Error.WinIOError(Int32, System.String) at System.IO.__Error.WinIOError() at System.Console.set_InputEncoding(System.Text.Encoding) at Xamarin.Android.Tasks.Aapt2Daemon.Aapt2DaemonStart() at System.Threading.ThreadHelper.ThreadStart_Context(System.Object) at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Threading.ThreadHelper.ThreadStart() ``` At this time we are still unsure as to what is causing the problem. But we should at least catch the exception to that Visual Studio does not crash. We need to figure out a way of reporting this issue somehow in the exception handler so we can get more data. Note: Not sure what is going on with the line ending changes :/ For reference the reason we have to use `Console.InputEncoding` is because of [this](https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs,2154). The `System.Diagnostic.Process` uses the `Console.InputEncoding` when creating the `StreamWriter` for `StandardInput`. --- .../Utilities/Aapt2Daemon.cs | 66 ++++++++++++++----- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index 3e672418963..79c0c1ff76b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -5,8 +5,10 @@ using System.IO; using System.Text; using System.Threading; +using System.Reflection; using Microsoft.Build.Framework; using TPL = System.Threading.Tasks; +using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks { @@ -144,6 +146,33 @@ public void Stop () pendingJobs.CompleteAdding (); } + private bool SetConsoleInputEncoding (Encoding encoding) + { + try { + if (Console.InputEncoding != encoding) { + Console.InputEncoding = encoding; + return true; + } + } catch (IOException) { + //In a DesignTime Build on VS Windows sometimes this exception is raised. + //We should catch it, but there is nothing we can do about it. + } + return false; + } + + private bool SetProcessInputEncoding (ProcessStartInfo info, Encoding encoding) + { + Type type = info.GetType(); + PropertyInfo prop = type.GetRuntimeProperty("StandardInputEncoding"); + if (prop == null) + prop = type.GetProperty("StandardInputEncoding", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if(prop?.CanWrite ?? false) { + prop.SetValue(info, encoding, null); + return true; + } + return false; + } + private void Aapt2DaemonStart () { ProcessStartInfo info = new ProcessStartInfo (Aapt2) @@ -158,23 +187,24 @@ private void Aapt2DaemonStart () WorkingDirectory = Path.GetTempPath (), StandardErrorEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, + // We need to FORCE the StandardInput to be UTF8 so we can use + // accented characters. Also DONT INCLUDE A BOM!! + // otherwise aapt2 will try to interpret the BOM as an argument. // Cant use this cos its netstandard 2.1 only // and we are using netstandard 2.0 - //StandardInputEncoding = Encoding.UTF8, + //StandardInputEncoding = MonoAndroidHelper.UTF8withoutBOM, }; - // We need to FORCE the StandardInput to be UTF8 so we can use - // accented characters. Also DONT INCLUDE A BOM!! - // otherwise aapt2 will try to interpret the BOM as an argument. Process aapt2; - lock (lockObject) { - Encoding current = Console.InputEncoding; + Encoding currentEncoding = Console.InputEncoding; + lock (lockObject) { try { - Console.InputEncoding = new UTF8Encoding (false); + if (!SetProcessInputEncoding (info, MonoAndroidHelper.UTF8withoutBOM)) + SetConsoleInputEncoding (MonoAndroidHelper.UTF8withoutBOM); aapt2 = new Process (); aapt2.StartInfo = info; aapt2.Start (); } finally { - Console.InputEncoding = current; + SetConsoleInputEncoding (currentEncoding); } } try { @@ -183,15 +213,15 @@ private void Aapt2DaemonStart () bool errored = false; try { // try to write Unicode UTF8 to aapt2 - StreamWriter writer = aapt2.StandardInput; - foreach (var arg in job.Commands) - { - writer.WriteLine (arg); + using (StreamWriter writer = new StreamWriter (aapt2.StandardInput.BaseStream, MonoAndroidHelper.UTF8withoutBOM, bufferSize: 1024, leaveOpen: true)) { + foreach (var arg in job.Commands) { + writer.WriteLine (arg); + } + writer.WriteLine (); + writer.Flush (); } - writer.WriteLine (); - writer.Flush (); string line; - + Queue stdError = new Queue (); while ((line = aapt2.StandardError.ReadLine ()) != null) { if (string.Compare (line, "Done", StringComparison.OrdinalIgnoreCase) == 0) { @@ -201,8 +231,8 @@ private void Aapt2DaemonStart () errored = true; continue; } - // we have to queue the output because the "Done"/"Error" lines are - //written after all the messages. So to process the warnings/errors + // we have to queue the output because the "Done"/"Error" lines are + //written after all the messages. So to process the warnings/errors // correctly we need to do this after we know if worked or failed. stdError.Enqueue (line); } @@ -256,4 +286,4 @@ bool IsAapt2Warning (string singleLine) return false; } } -} \ No newline at end of file +} From 17676e5750e7b73f7ad16f33862ffa94705022c7 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Tue, 2 Jun 2020 11:07:43 -0400 Subject: [PATCH 2/2] Fix code formatting. --- src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs index 79c0c1ff76b..fb62768b747 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs @@ -162,12 +162,12 @@ private bool SetConsoleInputEncoding (Encoding encoding) private bool SetProcessInputEncoding (ProcessStartInfo info, Encoding encoding) { - Type type = info.GetType(); - PropertyInfo prop = type.GetRuntimeProperty("StandardInputEncoding"); + Type type = info.GetType (); + PropertyInfo prop = type.GetRuntimeProperty ("StandardInputEncoding"); if (prop == null) - prop = type.GetProperty("StandardInputEncoding", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + prop = type.GetProperty ("StandardInputEncoding", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if(prop?.CanWrite ?? false) { - prop.SetValue(info, encoding, null); + prop.SetValue (info, encoding, null); return true; } return false;