superior win32 console support: capture output during debug mode, dont interfere with stdout redirect

This commit is contained in:
zeromus 2011-07-23 08:17:09 +00:00
parent eda56bac8c
commit fa179198a0
2 changed files with 132 additions and 12 deletions

View File

@ -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<string> 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
{

View File

@ -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();