【问题标题】:How to create a console window from a console window on Windows?如何从 Windows 上的控制台窗口创建控制台窗口?
【发布时间】:2012-09-16 17:26:45
【问题描述】:

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

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

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 我在你的 Q 中没有看到任何理由来继承控制台(?)窗口。而且,没有理由创建新的控制台窗口。想想,你问错了问题。
  • 那么,如何获取已创建窗口的消息?

标签: windows shell console window subclass


【解决方案1】:

来自MSDN documentation for AllocConsole

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

所以你需要在调用AllocConsole之前调用FreeConsole

【讨论】:

    【解决方案2】:

    我已开发此代码以在我的项目中使用。我希望这就是你所需要的,还有更多:)

    /// <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);
    

    【讨论】:

    • 请注意,此控制台 onclose 事件也会关闭托管程序。
    • 在调用 Win32 API 函数时应始终检查错误。另外,为什么FindConsoleHandle 不直接使用GetConsoleWindow 而不是使用窗口标题和未记录的FindWindowByCaption?最后,因为这段代码在调用AllocConsole之前没有调用FreeConsole,它实际上并不能解决OPs问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 2014-04-11
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多