Unity+SLua的一些小零碎(2)

增加调试控制台

Unity版本:2019.3.4f1
SLua版本:1.7.0
.NET : 4.7.1

前文已经把Unity和SLua搭建起来了。这次主要是希望为Unity增加一个单独的调试控制台,方便开发,也方便后面策划跑包过程中更容易定位问题(实时调试),原理则是调用了kernel32.dll里的AllocConsole()和FreeConsole()。

控制台存在的意义:
  1. 根据Debug.Log、Debug.LogWarning和Debug.LogError区分输出,控制台输出不同颜色的字符,便于查看
  2. 打出PC包后,也能正常查看Log
  3. 当程序异常触发断点时,能通过控制台进行调试(具体可以参考网上各种debuger的实现)
当前的问题:

我之前项目是使用Unity 5.6,控制台是利用GetStdHandle进行操作的,也能正常调用kernel32.dll里的函数。但是到了2019.3,发现GetStdHandle行不通了,GetLastError也没返回任何错误,估计跟Unity一些底层实现有关。通过谷歌一些资料和尝试,结果是使用CreateFileW能正常操作,但是文字着色等操作依旧无效(跟GetStdHandle相关的操作都无效,即使GetLastError返回是0),所以下面代码只能保证控制台正常使用,而不能保证文字着色。

using UnityEngine;
using System;
using System.IO;
using System.Runtime.InteropServices;
using SLua;
using Microsoft.Win32.SafeHandles;

namespace QGNB
{
    internal class ConsoleWindow
    {
#if UNITY_EDITOR_WIN || UNITY_STANDLONE_WIN
        static ConsoleWindow g_window;

        static void HandleLog(string message, string stackTrace, LogType type)
        {
            if (type == LogType.Warning)
                g_window.SetForegroundColor(0x0e);
            else if (type == LogType.Error)
                g_window.SetForegroundColor(0x0c);
            else
                g_window.SetForegroundColor(0x0c);

            Console.WriteLine(message);
        }
        static public void DestroyWindow()
        {
            g_window.Shutdown();
        }

        static public void CreateWindow()
        {
            g_window = new ConsoleWindow();
            g_window.Initialize();
            g_window.SetTitle("QGNB");

            Application.logMessageReceived += HandleLog;
        }

        TextWriter oldOutput;
        TextReader oldInput;

        IntPtr stdOutHandler;
        IntPtr stdInHandler;

        public void SetForegroundColor(uint color)
        {
            //SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
            //Debug.Log(">> SetConsoleTextAttribute " + GetLastError());
        }

        private FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
                                FileAccess dotNetFileAccess)
        {
            var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
            if (!file.IsInvalid)
            {
                var fs = new FileStream(file, dotNetFileAccess);
                return fs;
            }
            return null;
        }

        public StreamReader standardInput;
        public StreamWriter standardOutput;

        public void Initialize()
        {
            AllocConsole();

            oldOutput = Console.Out;
            oldInput = Console.In;

            try
            {

                var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
                if (fs != null)
                {
                    standardOutput = new StreamWriter(fs) { AutoFlush = true };
                    Console.SetOut(standardOutput);
                }

                fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
                if (fs != null)
                {
                    standardInput = new StreamReader(fs);
                    Console.SetIn(standardInput);
                }

                SetConsoleOutputCP(65001);
            }
            catch (System.Exception e)
            {
                Debug.Log("Couldn't redirect output: " + e.Message);
            }
        }

        public void Shutdown()
        {
            standardOutput.Close();
            standardInput.Close();
            Console.SetOut(oldOutput);
            Console.SetIn(oldInput);
            FreeConsole();
        }

        public void SetTitle(string strName)
        {
            SetConsoleTitle(strName);
        }

        static public string ReadConsoleString()
        {
            while (true)
            {
                string str = g_window.standardInput.ReadLine();
                if(str != null)
                {
                    return str;
                }
            }
        }

        #region Win API Functions and Constants
        [DllImport("kernel32.dll",
            EntryPoint = "AllocConsole",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern int AllocConsole();

        [DllImport("kernel32.dll",
           EntryPoint = "FreeConsole",
           SetLastError = true,
           CharSet = CharSet.Auto,
           CallingConvention = CallingConvention.StdCall)]
        private static extern int FreeConsole();

        [DllImport("kernel32.dll",
            EntryPoint = "AttachConsole",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern UInt32 AttachConsole(UInt32 dwProcessId);

        [DllImport("kernel32.dll",
            EntryPoint = "GetStdHandle",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr GetStdHandle(Int32 nStdHandle);

        [DllImport("kernel32.dll",
            EntryPoint = "SetConsoleTextAttribute",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern bool SetConsoleTextAttribute(IntPtr handle, uint color);

        [DllImport("kernel32.dll",
            EntryPoint = "SetConsoleTitle",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern bool SetConsoleTitle(string title);

        [DllImport("kernel32.dll",
            EntryPoint = "SetConsoleOutputCP",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern bool SetConsoleOutputCP(uint mode);

        [DllImport("kernel32.dll",
            EntryPoint = "SetConsoleScreenBufferSize",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern bool SetConsoleScreenBufferSize(IntPtr handle, _coord coord);

        [DllImport("kernel32.dll",
            EntryPoint = "GetLastError",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern int GetLastError();

        [DllImport("kernel32.dll",
            EntryPoint = "CreateFileW",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr CreateFileW(
              string lpFileName,
              UInt32 dwDesiredAccess,
              UInt32 dwShareMode,
              IntPtr lpSecurityAttributes,
              UInt32 dwCreationDisposition,
              UInt32 dwFlagsAndAttributes,
              IntPtr hTemplateFile
            );

        private const Int32 STD_OUTPUT_HANDLE = -11;
        private const Int32 STD_INPUT_HANDLE = -10;

        private const UInt32 GENERIC_WRITE = 0x40000000;
        private const UInt32 GENERIC_READ = 0x80000000;
        private const UInt32 FILE_SHARE_READ = 0x00000001;
        private const UInt32 FILE_SHARE_WRITE = 0x00000002;
        private const UInt32 OPEN_EXISTING = 0x00000003;
        private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
        private const UInt32 ERROR_ACCESS_DENIED = 5;
        private const UInt32 ATTACH_PARRENT = 0xFFFFFFFF;

        #endregion
#else
         static public void ReadConsoleString() { return ""; }
         static public void DestroyWindow() {}
         static public void CreateWindow() {}
#endif
    }


    [CustomLuaClass]
    public class ConsoleServer : MonoBehaviour
    {
        static public string ReadConsoleString()
        {
            return ConsoleWindow.ReadConsoleString();
        }
#if UNITY_EDITOR_WIN || UNITY_STANDLONE_WIN
        void Awake()
        {
            DontDestroyOnLoad(gameObject);
            ConsoleWindow.CreateWindow();
        }

        void OnDestroy()
        {
            ConsoleWindow.DestroyWindow();
        }
#endif
    }
}

你可能感兴趣的:(Unity+SLua的一些小零碎(2))