如何从Windows上的控制台窗口创建控制台窗口?

时间:2012-09-16 17:26:46

标签: windows shell console window subclass

我有一个需要读取窗口消息的控制台程序,但由于无法将属于另一个进程的窗口子类化,如何创建一个新的控制台窗口?

我尝试使用AllocConsole,但它显示错误:"Access is denied"

2 个答案:

答案 0 :(得分:0)

来自MSDN documentation for AllocConsole

  

进程只能与一个控制台关联,因此如果调用进程已有控制台,则AllocConsole函数将失败。进程可以使用FreeConsole函数将其自身与当前控制台分离,然后它可以调用AllocConsole来创建新控制台或AttachConsole以附加到另一个控制台。

因此,您需要在致电FreeConsole之前致电AllocConsole

答案 1 :(得分:-1)

我开发了这个代码,用于我的项目。我希望这里有你需要的更多东西:)

/// <summary>
/// Smart Console powered by Gregor Primar s.p.
/// </summary>
public class ConsoleWindow
{


    #region EXTERNALL DLL CALLS

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool AllocConsole();

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool FreeConsole();

    [DllImport("user32.dll")]
    static extern IntPtr RemoveMenu(IntPtr hMenu, uint nPosition, uint wFlags);

    [DllImport("user32.dll")]
    static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);

    [DllImport("user32.dll")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
    static extern IntPtr FindWindowByCaption(IntPtr zeroOnly, string lpWindowName);

    internal const UInt32 SC_CLOSE = 0xF060;
    internal const UInt32 MF_GRAYED = 0x00000001;
    internal const UInt32 MF_ENABLED = 0x00000000;
    internal const uint MF_BYCOMMAND = 0x00000000;

    #endregion


    #region PROPERTIES

    /// <summary>
    /// Gets if console window is displayed
    /// </summary>
    public bool Displayed { get; internal set; }

    /// <summary>
    /// Gets or Sets console background color
    /// </summary>
    public ConsoleColor BackgroundColor
    {
        get
        {
            return Console.BackgroundColor;
        }
        set
        {
            Console.BackgroundColor = value;
        }
    }

    /// <summary>
    /// Gets or Sets console foreground color
    /// </summary>
    public ConsoleColor ForegroundColor
    {
        get
        {
            return Console.ForegroundColor;
        }
        set
        {
            Console.ForegroundColor = value;
        }
    }

    /// <summary>
    /// Gets or Sets 
    /// </summary>
    public ConsoleColor ForegroundErrorColor { get; set; }

    #endregion


    #region WRITE AND READ METHODES

    /// <summary>
    /// Clears console window
    /// </summary>
    public void Clear()
    {
        Console.Clear();
    }

    /// <summary>
    /// Writes to console with ForegroundColor
    /// </summary>
    /// <param name="value"></param>
    public void Write(string value)
    {
        Write(value, false);
    }

    /// <summary>
    /// Writes to console with ForegroundColor or ForegroundErrorColor
    /// </summary>
    /// <param name="value"></param>
    /// <param name="isError"></param>
    public void Write(string value, bool isError)
    {
        Write_internal(value, isError, false);
    }

    /// <summary>
    /// Writes blank line to console with ForegroundColor
    /// </summary>
    public void WriteLine()
    {
        this.WriteLine("");
    }

    /// <summary>
    /// Writes to console with ForegroundColor
    /// </summary>
    /// <param name="value"></param>
    public void WriteLine(string value)
    {
        WriteLine(value, false);
    }

    /// <summary>
    /// Writes line to console with ForegroundColor or ForegroundErrorColor
    /// </summary>
    /// <param name="value"></param>
    /// <param name="isError"></param>
    public void WriteLine(string value, bool isError)
    {
        Write_internal(value, isError, true);
    }

    void Write_internal(string value, bool isError, bool fullLine)
    {
        ConsoleColor defaultColor = this.ForegroundColor;
        if (isError)
        {
            this.ForegroundColor = this.ForegroundErrorColor;
        }
        if (fullLine)
        {
            Console.WriteLine(value);
        }
        else
        {
            Console.Write(value);
        }
        this.ForegroundColor = defaultColor;
    }

    void ReadLine_internal(Type type, bool allowNull, ref object returnValue, StringDictionary options)
    {
        if ((options != null) && (type != typeof(string)))
        {
            throw new Exception("ReadLine_internal allows options only when type is string!");
        }

        string currentValue = null;
        string errorMessage = null;


        do
        {
            currentValue = Console.ReadLine();

            if (allowNull && currentValue == "")
            {
                returnValue = null;
                break;
            }

            //probaj za točno določen tip...
            bool typeResolved = false;

            if (type == typeof(string))
            {
                typeResolved = true;
                if (currentValue != "")
                {
                    if (options != null)
                    {
                        foreach (DictionaryEntry option in options)
                        {
                            if (option.Key.ToString() == currentValue)
                            {
                                returnValue = currentValue;
                                return;
                            }
                        }
                        errorMessage = "Enter one of possible options!";
                    }
                    else
                    {
                        returnValue = currentValue;
                        return;
                    }
                }
                else
                {
                    errorMessage = "String value is required!";
                }
            }

            if (type == typeof(int?))
            {
                typeResolved = true;
                int iVal = 0;
                if (int.TryParse(currentValue, out iVal))
                {
                    returnValue = iVal;
                    return;
                }
                errorMessage = "Int value is required!";
            }

            if (type == typeof(decimal?))
            {
                typeResolved = true;
                decimal dVal = 0;
                if (decimal.TryParse(currentValue, out dVal))
                {
                    returnValue = dVal;
                    return;
                }
                errorMessage = "Decimal value is required!";
            }

            if (type == typeof(DateTime?))
            {
                typeResolved = true;
                DateTime dtVal = new DateTime();
                if (DateTime.TryParse(currentValue, out dtVal))
                {
                    returnValue = dtVal;
                    return;
                }
                errorMessage = "DateTime value is required!";
            }

            if (typeResolved == false)
            {
                throw new Exception("Type='" + type.ToString() + "' not supported in ReadLine_internal void!");
            }

            this.WriteLine(errorMessage, true);

        } while (1 == 1);

    }

    /// <summary>
    /// Reads line from user input and returns string
    /// </summary>
    /// <returns></returns>
    public string ReadLine()
    {
        return this.ReadLine(true);
    }

    /// <summary>
    /// Reads line from user input and returns string
    /// </summary>
    /// <returns></returns>
    public string ReadLine(bool allowNull)
    {
        object returnValue = null;
        ReadLine_internal(typeof(string), allowNull, ref returnValue, null);
        if (returnValue != null)
        {
            return returnValue.ToString();
        }
        else
        {
            return null;
        }

    }

    /// <summary>
    /// Reads line from user input and returns nullable integer
    /// </summary>
    /// <param name="allowNull"></param>
    /// <returns></returns>
    public int? ReadLineAsInt(bool allowNull)
    {
        object returnValue = null;
        ReadLine_internal(typeof(int?), allowNull, ref returnValue, null);
        return (int?)returnValue;
    }

    /// <summary>
    /// Reads line from user input and returns nullable decimal
    /// </summary>
    /// <param name="allowNull"></param>
    /// <returns></returns>
    public decimal? ReadLineAsDecimal(bool allowNull)
    {
        object returnValue = null;
        ReadLine_internal(typeof(decimal?), allowNull, ref returnValue, null);
        return (decimal?)returnValue;
    }

    /// <summary>
    /// Reads line from user input and returns nullable datetime
    /// </summary>
    /// <param name="allowNull"></param>
    /// <returns></returns>
    public DateTime? ReadLineDateTime(bool allowNull)
    {
        object returnValue = null;
        ReadLine_internal(typeof(DateTime?), allowNull, ref returnValue, null);
        return (DateTime?)returnValue;
    }

    /// <summary>
    /// Reads line from user input and returns string from options list
    /// </summary>
    /// <param name="options"></param>
    /// <param name="allowNull"></param>
    /// <returns></returns>
    public string ReadLineAsOption(StringDictionary options, bool allowNull)
    {
        if (options != null)
        {
            if (options.Count == 0)
            {
                throw new Exception("Options list can not be empty! You can pass only null or unempty options list!");
            }
            else
            {
                this.WriteLine("Enter one of following options:");
                foreach (DictionaryEntry de in options)
                {
                    string description = null;
                    if (de.Value != null)
                    {
                        description = de.Value.ToString();
                    }
                    string userLine = "[" + de.Key.ToString() + "]";
                    if (description != null)
                    {
                        userLine += " " + description;
                    }
                    this.WriteLine(userLine);
                }
            }
        }
        object returnValue = null;
        ReadLine_internal(typeof(string), allowNull, ref returnValue, options);
        if (returnValue != null)
        {
            return returnValue.ToString();
        }
        else
        {
            return null;
        }
    }



    #endregion

    const string consoleTitle = "Smart Console powered by Gregor Primar s.p.";

    /// <summary>
    /// Default constructor
    /// </summary>
    public ConsoleWindow()
    {
    }

    /// <summary>
    /// Set focus to console window
    /// </summary>
    public void SetFocus()
    {
        if (this.Displayed)
        {
            SetConsoleFocus();
        }
        else
        {
            throw new Exception("Unable to SetFocus because console is not displayed!");
        }
    }

    /// <summary>
    /// Opens console window
    /// </summary>
    public void Open()
    {
        if (this.Displayed == false)
        {

            AllocConsole();
            Console.Title = consoleTitle;
            //onemogoči zapiranje konzole...
            ChangeConsoleMenu(false);
            this.Displayed = true;
            Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);

            //nastavi default barve...
            this.BackgroundColor = ConsoleColor.DarkBlue;
            this.ForegroundColor = ConsoleColor.White;
            this.ForegroundErrorColor = ConsoleColor.Red;
            this.Clear();

            this.SetFocus();
        }
        else
        {
            throw new Exception("Console window is allready opened!");
        }            
    }

    void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
        e.Cancel = true;
    }

    /// <summary>
    /// Closes console window
    /// </summary>
    public void Close()
    {
        if (this.Displayed)
        {
            Console.CancelKeyPress -= Console_CancelKeyPress;
            ChangeConsoleMenu(true);
            FreeConsole();
            this.Displayed = false;
        }
        else
        {
            throw new Exception("Can not close console window because its not displayed!");
        }
    }

    void ChangeConsoleMenu(bool enabled)
    {
        IntPtr hConsole = FindConsoleHandle();
        IntPtr hMenu = FindMenuHandle(hConsole);
        uint value = MF_ENABLED;
        if (enabled == false)
        {
            value = MF_GRAYED;
        }
        EnableMenuItem(hMenu, SC_CLOSE, value);
    }

    void SetConsoleFocus()
    { 
        IntPtr hConsole = FindConsoleHandle();
        while (true)
        {
            if (SetForegroundWindow(hConsole))
            {
                break;
            }
            Thread.Sleep(50);
        }
    }

    /// <summary>
    /// Finds handle to console window
    /// </summary>
    /// <returns></returns>
    IntPtr FindConsoleHandle()
    {
        string originalTitle = Console.Title;
        string uniqueTitle = Guid.NewGuid().ToString();
        Console.Title = uniqueTitle;
        Thread.Sleep(50);
        IntPtr handle = FindWindowByCaption(IntPtr.Zero, uniqueTitle);
        if (handle == IntPtr.Zero)
        {
            Console.Title = originalTitle;
            throw new Exception("Unable to find console window!");
        }
        Console.Title = originalTitle;
        return handle;
    }

    /// <summary>
    /// Finds handle to main menu
    /// </summary>
    /// <param name="windowHandle"></param>
    /// <returns></returns>
    IntPtr FindMenuHandle(IntPtr windowHandle)
    {
        IntPtr hSystemMenu = GetSystemMenu(windowHandle, false);
        return hSystemMenu;
    }


}

示例代码如何使用此类:

    ConsoleWindow cw = new ConsoleWindow();
    cw.Open();
    cw.WriteLine("Some text displayed on smart console", true);