Gharbad The Weak
Gharbad The Weak

Reputation: 1641

c# trouble displaying unicode card suits between different versions of Windows

Using c# .net 4.7.1, I'm trying to make a console app Blackjack game and I'm having trouble displaying the card suits to the console output with different versions of Windows. For Windows 7, this is the Main method that displays the suits correctly:

static void Main(string[] args)
{
    string[] Suits = new string[] { "♠", "♣", "♥", "♦" };
    Methods.Print(Suits[0] + " " + Suits[1] + " " + Suits[2] + " " + Suits[3]);
    Console.ReadLine();
    ....
}

the suits display as I want them, like this:

enter image description here

But if I run my program with this Main method in it on my Windows 10 machine they display like this:

enter image description here

I've found that if I include this line in my Main method on my Windows 10 machine then the suits display as I want them to:

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

But then that makes it so the suits don't display correctly on my Windows 7 machine. Can anyone help me out with how I can have these card suits display properly regardless of what Windows OS the program is run on? Thanks in advance.

Upvotes: 0

Views: 771

Answers (2)

Gharbad The Weak
Gharbad The Weak

Reputation: 1641

Thanks to @tymtam for the insight on the issue I'm having. I looked into the possibility of changing the console font as a solution. I found this article that shows how to programmatically change the console font to Lucida Console which is a true type font. Here's my formatted code from that link:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace BlackJack
{
    class BlackJack
    {       
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool SetCurrentConsoleFontEx(IntPtr consoleOutput, bool maximumWindow, ref CONSOLE_FONT_INFO_EX consoleCurrentFontEx);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr GetStdHandle(int dwType);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern int SetConsoleFont(IntPtr hOut, uint dwFontNum);

        private const int STD_OUTPUT_HANDLE = -11;
        private const int TMPF_TRUETYPE = 4;
        private const int LF_FACESIZE = 32;
        private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal unsafe struct CONSOLE_FONT_INFO_EX
        {
            internal uint cbSize;
            internal uint nFont;
            internal COORD dwFontSize;
            internal int FontFamily;
            internal int FontWeight;
            internal fixed char FaceName[LF_FACESIZE];
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct COORD
        {
            internal short X;
            internal short Y;

            internal COORD(short x, short y)
            {
                X = x;
                Y = y;
            }
        }

        public static void SetConsoleFont(string fontName = "Lucida Console")
        {
            unsafe
            {
                IntPtr hnd = GetStdHandle(STD_OUTPUT_HANDLE);
                if (hnd != INVALID_HANDLE_VALUE)
                {
                    CONSOLE_FONT_INFO_EX info = new CONSOLE_FONT_INFO_EX();
                    info.cbSize = (uint)Marshal.SizeOf(info);

                    // Set console font to Lucida Console.
                    CONSOLE_FONT_INFO_EX newInfo = new CONSOLE_FONT_INFO_EX();
                    newInfo.cbSize = (uint)Marshal.SizeOf(newInfo);
                    newInfo.FontFamily = TMPF_TRUETYPE;
                    IntPtr ptr = new IntPtr(newInfo.FaceName);
                    Marshal.Copy(fontName.ToCharArray(), 0, ptr, fontName.Length);

                    // Get some settings from current font.
                    newInfo.dwFontSize = new COORD(info.dwFontSize.X, info.dwFontSize.Y);
                    newInfo.FontWeight = info.FontWeight;
                    SetCurrentConsoleFontEx(hnd, false, ref newInfo);
                }
            }
        }

        static void Main(string[] args)
        {            
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            SetConsoleFont();
            ....
        }

Two things to note

  1. I had to add this using statement for it to work:

    using System.Runtime.InteropServices;

  2. I had to check the Allow unsafe code checkbox located in the Project>Properties>Build screen as shown below:

enter image description here

After making these changes the program runs on both Windows 7 and Windows 10 and it displays the card suits as I want them to. Like I said before, I don't have access to machines that have other versions of Windows on them, so I can only say that this runs on Windows 7 and Windows 10 for sure.

Upvotes: 0

tmaj
tmaj

Reputation: 35075

If you want it to work reliably in the console then here is my solution:

static void Main(string[] args)
{
    Console.WriteLine("D, C, H, S");
    Console.ReadLine();
}

Here are 2 other options:

  • Check windows versions and test all scenarios using , , , and Encoding.UTF8;
  • Gui application.

Windows 7

The problem with Windows7 console and

Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine("♠, ♣, ♥, ♦");
Console.ReadLine();

is most likely the font in console app.

From Console.OutputEncoding Property:

Note that successfully displaying Unicode characters to the console requires the following:

The console must use a TrueType font, such as Lucida Console or Consolas, to display characters.

You can change the font in the Console app's properties:

enter image description here

Upvotes: 1

Related Questions