diff --git a/BizHawk.MultiClient/LogConsole.cs b/BizHawk.MultiClient/LogConsole.cs index 11795a9c60..572b897c05 100644 --- a/BizHawk.MultiClient/LogConsole.cs +++ b/BizHawk.MultiClient/LogConsole.cs @@ -3,6 +3,8 @@ using System.Text; using System.IO; using System.Runtime.InteropServices; +//thanks! - http://sharp-developer.net/ru/CodeBank/WinForms/GuiConsole.aspx + //todo - quit using Console.WriteLine (well, we can leave it hooked up as a backstop) //use a different method instead, so we can collect unicode data //also, collect log data independently of whether the log window is open @@ -12,13 +14,6 @@ namespace BizHawk.MultiClient { static class LogConsole { - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool AllocConsole(); - - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool FreeConsole(); - - public static bool ConsoleVisible { get; @@ -88,6 +83,75 @@ namespace BizHawk.MultiClient public Action Emit; } + static IntPtr oldOut, conOut; + static bool hasConsole; + static bool attachedConsole; + static bool shouldRedirectStdout; + public static void CreateConsole() + { + //(see desmume for the basis of some of this logic) + + if (hasConsole) + return; + + if (oldOut == IntPtr.Zero) + oldOut = Win32.GetStdHandle( -11 ); //STD_OUTPUT_HANDLE + + Win32.FileType fileType = Win32.GetFileType(oldOut); + + //stdout is already connected to something. keep using it and dont let the console interfere + shouldRedirectStdout = (fileType == Win32.FileType.FileTypeUnknown || fileType == Win32.FileType.FileTypePipe); + + //attach to an existing console (if we can; this is circuitous because AttachConsole wasnt added until XP) + attachedConsole = false; + + if (Win32.AttachConsole(-1)) + attachedConsole = true; + + if (!attachedConsole) + Win32.AllocConsole(); + + if (shouldRedirectStdout) + { + conOut = Win32.CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero); + + if (!Win32.SetStdHandle(-11, conOut)) + throw new Exception("SetStdHandle() failed"); + } + + hasConsole = true; + DotNetRewireConout(); + + if (attachedConsole) + { + Console.WriteLine(); + Console.WriteLine("use cmd /c {0} to get more sensible console behaviour", System.IO.Path.GetFileName(PathManager.GetBasePathAbsolute())); + } + } + + static void DotNetRewireConout() + { + Stream cstm = Console.OpenStandardOutput(); + var cstw = new StreamWriter(cstm) { AutoFlush = true }; + Console.SetOut(cstw); + Console.SetError(cstw); + Console.Title = "BizHawk Console"; + } + + static void ReleaseConsole() + { + if (!hasConsole) + return; + + if(shouldRedirectStdout) Win32.CloseHandle(conOut); + if(!attachedConsole) Win32.FreeConsole(); + Win32.SetStdHandle(-11, oldOut); + + conOut = IntPtr.Zero; + hasConsole = false; + } + + const bool WIN32_CONSOLE = true; public static void ShowConsole() @@ -97,10 +161,14 @@ namespace BizHawk.MultiClient if (WIN32_CONSOLE) { - AllocConsole(); - var sout = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }; - Console.SetOut(sout); - Console.Title = "BizHawk Message Log"; + CreateConsole(); + //not sure whether we need to set a buffer size here + //var sout = new StreamWriter(Console.OpenStandardOutput(),Encoding.ASCII,1) { AutoFlush = true }; + //var sout = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }; + //Console.SetOut(sout); + //Console.Title = "BizHawk Message Log"; + //System.Runtime.InteropServices.SafeFi + //new Microsoft.Win32.SafeHandles.SafeFileHandle( } else { @@ -122,7 +190,7 @@ namespace BizHawk.MultiClient ConsoleVisible = false; if (WIN32_CONSOLE) { - FreeConsole(); + ReleaseConsole(); } else { diff --git a/BizHawk.Util/Win32.cs b/BizHawk.Util/Win32.cs index 4f435a58ac..fb8a457e22 100644 --- a/BizHawk.Util/Win32.cs +++ b/BizHawk.Util/Win32.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; namespace BizHawk { @@ -278,6 +279,57 @@ namespace BizHawk public int dwInterleaveEvery; } + [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + public static extern SafeFileHandle CreateFile( + string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + IntPtr SecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + [DllImport("kernel32.dll")] + public static extern FileType GetFileType(IntPtr hFile); + + public enum FileType : uint + { + FileTypeChar = 0x0002, + FileTypeDisk = 0x0001, + FileTypePipe = 0x0003, + FileTypeRemote = 0x8000, + FileTypeUnknown = 0x0000, + } + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool AttachConsole(int dwProcessId); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool AllocConsole(); + + [DllImport("kernel32.dll", SetLastError = false)] + public static extern bool FreeConsole(); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr CreateFile( + string fileName, + int desiredAccess, + int shareMode, + IntPtr securityAttributes, + int creationDisposition, + int flagsAndAttributes, + IntPtr templateFile); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + public static extern bool CloseHandle(IntPtr handle); + [DllImport("user32.dll", SetLastError = false)] public static extern IntPtr GetDesktopWindow();