问题描述
GetConsoleScreenBufferInfoEx无效的返回值
我正在尝试更改控制台的调色板。为此,我首先需要Get
ConsoleScreenBufferInfoEx
,然后Set
。问题是我什至无法从ConsoleScreenBufferInfoEx
获得有效的STD_OUTPUT_HANDLE
。
下面的代码引发此错误消息:
System.ArgumentException:'值不在预期范围内。'
句柄有效,但出现此错误。我已经对每个数据类型和相关的pinvoke条目进行了四重检查-一切对我来说都很不错。 GetConsoleScreenBufferInfoEx
没有示例代码,我还没有找到可行的解决方案。
我的来源:
- pinvoke: ConsoleFunctions (kernel32)
- msdocs: CONSOLE_SCREEN_BUFFER_INFOEX structure
- msdocs: COLORREF
示例应用程序(.NET Core 3.1):
为了使此代码起作用,项目的构建属性必须允许使用不安全的代码。
[属性->构建->允许使用不安全的代码]
using Microsoft.Win32.SafeHandles;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ScreenBufferInfoExample
{
class Program
{
[DllImport("kernel32.dll",SetLastError = true)]
public static extern SafeFileHandle GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll",SetLastError = true)]
public static extern unsafe bool GetConsoleScreenBufferInfoEx(SafeFileHandle hConsoleOutput,out CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfo);
static void Main()
{
SafeFileHandle stdOut = GetStdHandle(-11);
if(stdOut.IsInvalid)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
CONSOLE_SCREEN_BUFFER_INFO_EX info = new CONSOLE_SCREEN_BUFFER_INFO_EX();
info.cbSize = (uint)Marshal.SizeOf(info);
if(!GetConsoleScreenBufferInfoEx(stdOut,out info)) {
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());// <- this gets thrown
// System.ArgumentException: 'Value does not fall within the expected range.'
}
Console.ReadKey(true);
}
}
[StructLayout(LayoutKind.Sequential)]
struct CONSOLE_SCREEN_BUFFER_INFO_EX
{
public uint cbSize;
public COORD dwSize;
public COORD dwCursorPosition;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
public ushort wPopupAttributes;
public bool bFullscreenSupported;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 16)]
public COLORREF[] ColorTable;
}
[StructLayout(LayoutKind.Sequential)]
struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct COLORREF
{
public uint ColorDWORD;
public COLORREF(int r,int g,int b)
: this(Color.FromArgb(r,g,b)) { }
public COLORREF(Color color)
{
ColorDWORD = (uint)color.R
+ (((uint)color.G) << 8)
+ (((uint)color.B) << 16);
}
public Color GetColor()
{
return Color.FromArgb((int)(0x000000FFU & ColorDWORD),(int)(0x0000FF00U & ColorDWORD) >> 8,(int)(0x00FF0000U & ColorDWORD) >> 16);
}
public void SetColor(Color color)
{
ColorDWORD = (uint)color.R
+ (((uint)color.G) << 8)
+ (((uint)color.B) << 16);
}
}
}
解决方法
pinvoke.net通常很有用,但并不总是正确的。
由于您需要将信息传入和传出GetConsoleScreenBufferBufferInfoEx方法,因此参数不能为out
,而必须为ref
。
所以正确的声明应该是
[DllImport("kernel32.dll",SetLastError = true)]
public static extern unsafe bool GetConsoleScreenBufferInfoEx(SafeFileHandle hConsoleOutput,ref CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfo);
称为GetConsoleScreenBufferInfoEx(stdOut,ref info)
。
(我只是相应地更正了pinvoke.net上的页面)