首页 > 解决方案 > CONSOLE_SCREEN_BUFFER_INFOEX 无效;GetConsoleScreenBufferInfoEx (kernel32)

问题描述

GetConsoleScreenBufferInfoEx 无效返回值

我正在尝试更改控制台的调色板。为了完成这件事,我首先需要Get我的ConsoleScreenBufferInfoEx,然后Set是它。问题是我什至无法ConsoleScreenBufferInfoExSTD_OUTPUT_HANDLE. 下面的代码抛出此错误消息:
System.ArgumentException: 'Value does not fall in the expected range.'
句柄有效,但我收到此错误。我已经对每种数据类型和相关的 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);
        }
    }
}

标签: c#.net-corepinvoke

解决方案


pinvoke.net 通常很有用,但并不总是正确的。

由于需要将信息传入传出 GetConsoleScreenBufferInfoEx 方法,因此参数不能为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 上的页面)


推荐阅读