无效的CONSOLE_SCREEN_BUFFER_INFOEX; GetConsoleScreenBufferInfoEx内核32

问题描述

GetConsoleScreenBufferInfoEx无效的返回值

我正在尝试更改控制台的调色板。为此,我首先需要Get ConsoleScreenBufferInfoEx,然后Set。问题是我什至无法从ConsoleScreenBufferInfoEx获得有效的STD_OUTPUT_HANDLE。 下面的代码引发此错误消息:
System.ArgumentException:'值不在预期范围内。'
句柄有效,但出现此错误。我已经对每个数据类型和相关的pinvoke条目进行了四重检查-一切对我来说都很不错。 GetConsoleScreenBufferInfoEx没有示例代码,我还没有找到可行的解决方案。

我的来源:

示例应用程序(.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上的页面)