首页 > 解决方案 > c#使用WriteConsoleOutput写Unicode

问题描述

我正在尝试使用WriteConsoleOutputkernel32.dll 中的函数,但是我无法正确显示 unicode 字符,它们总是显示为错误的字符。

我试图使用:

Console.OutputEncoding = System.Text.Encoding.UTF8;

将其更改为Encoding.Unicode也不起作用。

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleOutputCP(uint wCodePageID);

public void SetCP(){
   SetConsoleOutputCP(65001);
}

我已经尝试使用上述两种方法,每一种都单独使用,几乎没有任何值的组合。

我还在所有字体(包括真正的字体)之间切换,但是它们似乎都不能正确显示字符。

这是我用来使用 WriteConsoleOutput 的代码

[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "WriteConsoleOutputW", CharSet = CharSet.Unicode)]
static extern bool WriteConsoleOutputW(SafeFileHandle hConsoleOutput, CharInfo[] lpBuffer, Coord dwBufferSize, Coord dwBufferCoord, ref SmallRect lpWriteRegion);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string fileName, [MarshalAs(UnmanagedType.U4)] uint fileAccess, [MarshalAs(UnmanagedType.U4)] uint fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] int flags, IntPtr template);

private static readonly SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

public static void RegionWrite(string s, int x, int y, int width, int height)
{           
    if (!h.IsInvalid)
    {
        int length = width * height;

        // Pad any extra space we have
        string fill = s + new string(' ', length - s.Length);

        // Grab the background and foreground as integers
        int bg = (int) Console.BackgroundColor;
        int fg = (int) Console.ForegroundColor;

        // Make background and foreground into attribute value
        short attr = (short)(fg | (bg << 4));

        CharInfo[] buf = fill.Select(c => 
        {
            CharInfo info = new CharInfo();

            // Give it our character to write
            info.Char.UnicodeChar = c;

            // Use our attributes
            info.Attributes = attr;

            // Return info for this character
            return info;

        }).ToArray();

        // Make everything short so we don't have to cast all the time
        short sx = (short) x;
        short sy = (short) y;
        short swidth = (short) width;
        short sheight = (short) height;

        // Make a buffer size out our dimensions
        Coord bufferSize = new Coord(swidth, sheight);

        // Not really sure what this is but its probably important
        Coord pos = new Coord(0, 0);

        // Where do we place this?
        SmallRect rect = new SmallRect() { Left = sx, Top = sy, Right = (short) (sx + swidth), Bottom = (short) (sy + sheight) };

        bool b = WriteConsoleOutputW(h, buf, bufferSize, pos, ref rect);
    }
    else
    {
        throw new Exception("Console handle is invalid.");
    }

}

将其与标准 ASCII 字符一起使用非常有效:

RegionWrite("Hello world", 4, 4, 10, 10);

但是,当我使用高于标准 ASCII 范围的任何内容时,它无法正确显示:

RegionWrite("┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬", 4, 4, 10, 10);这输出为两行 ',' 字符,这是有道理的,因为“┬”字符的值为 9516,9516 % 128 是 44,这是 ',' 的 ascii 代码。

我知道在物理上可以正常输出这些字符Console.Write("┬┬┬┬")。我正在从 切换Console.Write到 ,WriteConsoleOutput因为性能显着提高。

这是我用来设置代码页的代码:

public void Setup()
{
    Console.BufferHeight = Console.WindowHeight;
    Console.BufferWidth = Console.WindowWidth;

    Console.OutputEncoding = System.Text.Encoding.UTF8;

    SetConsoleOutputCP(65001);

    DefaultColor();
    Console.Clear();

    Console.ReadLine();

    RegionWrite("┬┬┬┬", 4, 4, 10, 10);

    Console.WriteLine("┬┬┬┬");

    Console.ReadLine();
}

这是我的结构:

[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
    public short X;
    public short Y;

    public Coord(short X, short Y)
    {
        this.X = X;
        this.Y = Y;
    }
}

[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
    [FieldOffset(0)] public char UnicodeChar;
    [FieldOffset(0)] public byte AsciiChar;
}

[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
    [FieldOffset(0)] public CharUnion Char;
    [FieldOffset(2)] public short Attributes;
}

[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

我想我已经搞砸了其中一个变量,WriteConsoleOutput但是经过数小时的搜索答案后,我真的不确定我哪里出错了。我需要使用一些内部设置编码功能吗?

nvm修复了它

标签: c#windowsunicodeconsole

解决方案


简单的解决方案,改变

[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
    [FieldOffset(0)] public char UnicodeChar;
    [FieldOffset(0)] public byte AsciiChar;
}

[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
public struct CharUnion
{
    [FieldOffset(0)] public char UnicodeChar;
    [FieldOffset(0)] public byte AsciiChar;
}

这是因为它将默认为 ANSI,这意味着您的 unicode 字符会自动转换为 ANSI,因此 ┬ 变为 ,


推荐阅读