我有一个需要读取窗口消息的控制台程序,但是由于不能将属于另一个进程的窗口子类化,如何创建一个新的控制台窗口?
我曾尝试使用AllocConsole,但它显示错误:"Access is denied"
一个进程只能与一个控制台关联,因此如果调用进程已经有一个控制台,则 AllocConsole 函数会失败。进程可以使用 FreeConsole 函数将自己与其当前控制台分离,然后它可以调用 AllocConsole 来创建新控制台或 AttachConsole 以附加到另一个控制台。
所以你需要在调用AllocConsole之前调用FreeConsole。
我开发了此代码以用于我的项目。我希望这就是你所需要的,还有更多:)
/// <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);