Browse Source

Attempt a second fix for #139

ghorsington 4 years ago
parent
commit
a55939c947
1 changed files with 23 additions and 14 deletions
  1. 23 14
      BepInEx/Console/Windows/WindowsConsoleDriver.cs

+ 23 - 14
BepInEx/Console/Windows/WindowsConsoleDriver.cs

@@ -1,7 +1,10 @@
 using System;
 using System.IO;
+using System.Linq;
+using System.Reflection;
 using System.Text;
 using BepInEx.ConsoleUtil;
+using HarmonyLib;
 using Microsoft.Win32.SafeHandles;
 using UnityInjector.ConsoleUtil;
 
@@ -9,11 +12,6 @@ namespace BepInEx
 {
 	internal class WindowsConsoleDriver : IConsoleDriver
 	{
-		// We need to save stdout and conout as SafeFileHandles because we can't pass it to FileStream (check comments in CreateConsole)
-		// However, on some versions of Unity (e.g. 2018.4) using old mono causes crashes on game close if
-		// the stdout and conout are not saved into a file handle (check #139)
-		private SafeFileHandle _originalOutHandle, _consoleOutHandle;
-		
 		public TextWriter StandardOut { get; private set; }
 		public TextWriter ConsoleOut { get; private set; }
 
@@ -27,18 +25,32 @@ namespace BepInEx
 			StandardOut = Console.Out;
 		}
 
+		// Apparently on some versions of Unity (e.g. 2018.4) using old mono causes crashes on game close if
+		// IntPtr overload is used for file streams (check #139).
+		// On the other hand, not all Unity games come with SafeFileHandle overload for FileStream
+		// As such, we're trying to use SafeFileHandle when it's available and go back to IntPtr overload if not available
+		private static readonly ConstructorInfo FileStreamCtor = new[]
+		{
+			AccessTools.Constructor(typeof(FileStream), new[] { typeof(SafeFileHandle), typeof(FileAccess) }),
+			AccessTools.Constructor(typeof(FileStream), new[] { typeof(IntPtr), typeof(FileAccess) }),
+		}.FirstOrDefault(m => m != null);
+
+		private static FileStream OpenFileStream(IntPtr handle)
+		{
+			var fileHandle = new SafeFileHandle(handle, false);
+			var ctorParams = AccessTools.ActualParameters(FileStreamCtor, new object[] { fileHandle, fileHandle.DangerousGetHandle(), FileAccess.Write });
+			return (FileStream)Activator.CreateInstance(typeof(FileStream), ctorParams);
+		}
+
 		public void CreateConsole(uint codepage)
 		{
-			// On some Unity mono builds the SafeFileHandle overload for FileStream is missing
-			// so we use the older but always included one instead
-#pragma warning disable 618
 			ConsoleWindow.Attach();
 
 			// Make sure of ConsoleEncoding helper class because on some Monos
 			// Encoding.GetEncoding throws NotImplementedException on most codepages
 			// NOTE: We don't set Console.OutputEncoding because it resets any existing Console.Out writers
 			ConsoleEncoding.ConsoleCodePage = codepage;
-			
+
 			// If stdout exists, write to it, otherwise make it the same as console out
 			// Not sure if this is needed? Does the original Console.Out still work?
 			var stdout = GetOutHandle();
@@ -49,22 +61,19 @@ namespace BepInEx
 				return;
 			}
 
-			_originalOutHandle = new SafeFileHandle(stdout, false);
-			var originalOutStream = new FileStream(_originalOutHandle.DangerousGetHandle(), FileAccess.Write);
+			var originalOutStream = OpenFileStream(stdout);
 			StandardOut = new StreamWriter(originalOutStream, new UTF8Encoding(false))
 			{
 				AutoFlush = true
 			};
 
-			_consoleOutHandle = new SafeFileHandle(ConsoleWindow.ConsoleOutHandle, false);
-			var consoleOutStream = new FileStream(_consoleOutHandle.DangerousGetHandle(), FileAccess.Write);
+			var consoleOutStream = OpenFileStream(ConsoleWindow.ConsoleOutHandle);
 			// Can't use Console.OutputEncoding because it can be null (i.e. not preference by user)
 			ConsoleOut = new StreamWriter(consoleOutStream, ConsoleEncoding.OutputEncoding)
 			{
 				AutoFlush = true
 			};
 			ConsoleActive = true;
-#pragma warning restore 618
 		}
 
 		private IntPtr GetOutHandle()