public static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC tm);
internal static extern bool GetTextMetrics(HDC hdc, out TEXTMETRIC tm);
internal static int LengthInBufferCells(string str, int offset, bool checkEscapeSequences) { Dbg.Assert(offset >= 0, "offset >= 0"); Dbg.Assert(string.IsNullOrEmpty(str) || (offset < str.Length), "offset < str.Length"); var escapeSequenceAdjustment = 0; if (checkEscapeSequences) { int i = 0; while (i < offset) { ControlSequenceLength(str, ref i); } // If offset != i, we're in the middle of a sequence, which the caller should avoid, // but we'll tolerate. while (i < str.Length) { escapeSequenceAdjustment += ControlSequenceLength(str, ref i); } } uint codePage = NativeMethods.GetConsoleOutputCP(); if (!IsAvailableFarEastCodePage(codePage)) { return str.Length - offset - escapeSequenceAdjustment; } HWND hwnd = (HWND)0; HDC hDC = (HDC)0; bool istmInitialized = false; TEXTMETRIC tm = new TEXTMETRIC(); ; int length = 0; try { int n = str.Length; for (int i = offset; i < n; i++) { char c = str[i]; length += LengthInBufferCellsFE(c, ref hwnd, ref hDC, ref istmInitialized, ref tm); } return length - escapeSequenceAdjustment; } finally { if (hwnd != (IntPtr)0 && hDC != (IntPtr)0) { NativeMethods.ReleaseDC(hwnd, hDC); } } }
private static int LengthInBufferCells(char c, uint codePage) { if (!IsAvailableFarEastCodePage(codePage)) { return 1; } HWND hwnd = (HWND)0; HDC hDC = (HDC)0; bool istmInitialized = false; TEXTMETRIC tm = new TEXTMETRIC(); ; try { return LengthInBufferCellsFE(c, ref hwnd, ref hDC, ref istmInitialized, ref tm); } finally { if (hwnd != (IntPtr)0 && hDC != (IntPtr)0) { NativeMethods.ReleaseDC(hwnd, hDC); } } }
/// <summary> /// From IsConsoleFullWidth in \windows\core\ntcon\server\dbcs.c /// Precondition: the current code page needs to be a Far East code page. /// /// char F8F8 makes this function return 1 while in CHT, CHS, and KOR it takes 2 cells. /// I don't think we should special-case this because that ought to be a bug outside of /// this code. /// </summary> /// <param name="c"></param> /// <param name="hwnd">window handle</param> /// <param name="hDC">handle to DC; it is not released by this method</param> /// <param name="istmInitialized"></param> /// <param name="tm"></param> /// <returns></returns> private static int LengthInBufferCellsFE(char c, ref HWND hwnd, ref HDC hDC, ref bool istmInitialized, ref TEXTMETRIC tm) { if (0x20 <= c && c <= 0x7e) { /* ASCII */ return 1; } else if (0x3041 <= c && c <= 0x3094) { /* Hiragana */ return 2; } else if (0x30a1 <= c && c <= 0x30f6) { /* Katakana */ return 2; } else if (0x3105 <= c && c <= 0x312c) { /* Bopomofo */ return 2; } else if (0x3131 <= c && c <= 0x318e) { /* Hangul Elements */ return 2; } else if (0xac00 <= c && c <= 0xd7a3) { /* Korean Hangul Syllables */ return 2; } else if (0xff01 <= c && c <= 0xff5e) { /* Fullwidth ASCII variants */ return 2; } else if (0xff61 <= c && c <= 0xff9f) { /* Halfwidth Katakana variants */ return 1; } else if ((0xffa0 <= c && c <= 0xffbe) || (0xffc2 <= c && c <= 0xffc7) || (0xffca <= c && c <= 0xffcf) || (0xffd2 <= c && c <= 0xffd7) || (0xffda <= c && c <= 0xffdc)) { /* Halfwidth Hangule variants */ return 1; } else if (0xffe0 <= c && c <= 0xffe6) { /* Fullwidth symbol variants */ return 2; } else if (0x4e00 <= c && c <= 0x9fa5) { /* Han Ideographic */ return 2; } else if (0xf900 <= c && c <= 0xfa2d) { /* Han Compatibility Ideographs */ return 2; } else { // GetTextMetrics / GetCharWidth32 exist in an extension API set 'ext-ms-win-gdi-font-l1-1-1.dll', which is not available in NanoServer. #if !CORECLR /* Unknown character: need to use GDI*/ if (hDC == (IntPtr)0) { hwnd = NativeMethods.GetConsoleWindow(); if ((IntPtr)0 == hwnd) { int err = Marshal.GetLastWin32Error(); //Don't throw exception so that output can continue tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the window handle to the console.", err); return 1; } hDC = NativeMethods.GetDC(hwnd); if ((IntPtr)0 == hDC) { int err = Marshal.GetLastWin32Error(); //Don't throw exception so that output can continue tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the Device Context of the console window.", err); return 1; } } bool result = true; if (!istmInitialized) { result = NativeMethods.GetTextMetrics(hDC, out tm); if (!result) { int err = Marshal.GetLastWin32Error(); //Don't throw exception so that output can continue tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the Text Metric of the console window's Device Context.", err); return 1; } istmInitialized = true; } int width; result = NativeMethods.GetCharWidth32(hDC, (uint)c, (uint)c, out width); if (!result) { int err = Marshal.GetLastWin32Error(); //Don't throw exception so that output can continue tracer.TraceError("Win32 Error 0x{0:X} occurred when getting the width of a char.", err); return 1; } if (width >= tm.tmMaxCharWidth) { return 2; } #endif } tracer.WriteLine("failed to locate char {0}, return 1", (int)c); return 1; }
public static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC tm);