@@ -409,6 +409,38 @@ public static string ToGestureString(this ConsoleKeyInfo key)
409409
410410 internal class ConhostConsole : IConsole
411411 {
412+ [ SuppressMessage ( "Microsoft.Reliability" , "CA2006:UseSafeHandleToEncapsulateNativeResources" ) ]
413+ private IntPtr _hwnd = ( IntPtr ) 0 ;
414+ [ SuppressMessage ( "Microsoft.Reliability" , "CA2006:UseSafeHandleToEncapsulateNativeResources" ) ]
415+ private IntPtr _hDC = ( IntPtr ) 0 ;
416+ private uint _codePage ;
417+ private bool _istmInitialized = false ;
418+ private TEXTMETRIC _tm = new TEXTMETRIC ( ) ;
419+ private bool _trueTypeInUse = false ;
420+
421+ private readonly Lazy < SafeFileHandle > _outputHandle = new Lazy < SafeFileHandle > ( ( ) =>
422+ {
423+ // We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
424+ var handle = NativeMethods . CreateFile (
425+ "CONOUT$" ,
426+ ( UInt32 ) ( AccessQualifiers . GenericRead | AccessQualifiers . GenericWrite ) ,
427+ ( UInt32 ) ShareModes . ShareWrite ,
428+ ( IntPtr ) 0 ,
429+ ( UInt32 ) CreationDisposition . OpenExisting ,
430+ 0 ,
431+ ( IntPtr ) 0 ) ;
432+
433+ if ( handle == NativeMethods . INVALID_HANDLE_VALUE )
434+ {
435+ int err = Marshal . GetLastWin32Error ( ) ;
436+ Win32Exception innerException = new Win32Exception ( err ) ;
437+ throw new Exception ( "Failed to retreive the input console handle." , innerException ) ;
438+ }
439+
440+ return new SafeFileHandle ( handle , true ) ;
441+ }
442+ ) ;
443+
412444 private readonly Lazy < SafeFileHandle > _inputHandle = new Lazy < SafeFileHandle > ( ( ) =>
413445 {
414446 // We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
@@ -613,5 +645,197 @@ public CHAR_INFO[] ReadBufferLines(int top, int count)
613645 readBufferSize , readBufferCoord , ref readRegion ) ;
614646 return result ;
615647 }
648+
649+ [ SuppressMessage ( "Microsoft.Reliability" , "CA2001:AvoidCallingProblematicMethods" ,
650+ Justification = "Then the API we pass the handle to will return an error if it is invalid. They are not exposed." ) ]
651+ internal static CONSOLE_FONT_INFO_EX GetConsoleFontInfo ( SafeFileHandle consoleHandle )
652+ {
653+
654+ CONSOLE_FONT_INFO_EX fontInfo = new CONSOLE_FONT_INFO_EX ( ) ;
655+ fontInfo . cbSize = Marshal . SizeOf ( fontInfo ) ;
656+ bool result = NativeMethods . GetCurrentConsoleFontEx ( consoleHandle . DangerousGetHandle ( ) , false , ref fontInfo ) ;
657+
658+ if ( result == false )
659+ {
660+ int err = Marshal . GetLastWin32Error ( ) ;
661+ Win32Exception innerException = new Win32Exception ( err ) ;
662+ throw new Exception ( "Failed to get console font information." , innerException ) ;
663+ }
664+ return fontInfo ;
665+ }
666+
667+ public int LengthInBufferCells ( char c )
668+ {
669+ if ( ! IsCJKOutputCodePage ( ) || ! _trueTypeInUse )
670+ return 1 ;
671+
672+ return LengthInBufferCellsFE ( c ) ;
673+ }
674+
675+ internal static bool IsAnyDBCSCharSet ( uint charSet )
676+ {
677+ const uint SHIFTJIS_CHARSET = 128 ;
678+ const uint HANGEUL_CHARSET = 129 ;
679+ const uint CHINESEBIG5_CHARSET = 136 ;
680+ const uint GB2312_CHARSET = 134 ;
681+ return charSet == SHIFTJIS_CHARSET || charSet == HANGEUL_CHARSET ||
682+ charSet == CHINESEBIG5_CHARSET || charSet == GB2312_CHARSET ;
683+ }
684+
685+ internal uint CodePageToCharSet ( )
686+ {
687+ CHARSETINFO csi ;
688+ const uint TCI_SRCCODEPAGE = 2 ;
689+ const uint OEM_CHARSET = 255 ;
690+ if ( ! NativeMethods . TranslateCharsetInfo ( ( IntPtr ) _codePage , out csi , TCI_SRCCODEPAGE ) )
691+ {
692+ csi . ciCharset = OEM_CHARSET ;
693+ }
694+ return csi . ciCharset ;
695+ }
696+
697+ /// <summary>
698+ /// Check if the output buffer code page is Japanese, Simplified Chinese, Korean, or Traditional Chinese
699+ /// </summary>
700+ /// <returns>true if it is CJK code page; otherwise, false.</returns>
701+ internal bool IsCJKOutputCodePage ( )
702+ {
703+ return _codePage == 932 || // Japanese
704+ _codePage == 936 || // Simplified Chinese
705+ _codePage == 949 || // Korean
706+ _codePage == 950 ; // Traditional Chinese
707+ }
708+
709+ internal bool IsAvailableFarEastCodePage ( )
710+ {
711+ uint charSet = CodePageToCharSet ( ) ;
712+ return IsAnyDBCSCharSet ( charSet ) ;
713+ }
714+
715+ internal int LengthInBufferCellsFE ( char c )
716+ {
717+ if ( 0x20 <= c && c <= 0x7e )
718+ {
719+ /* ASCII */
720+ return 1 ;
721+ }
722+ else if ( 0x3041 <= c && c <= 0x3094 )
723+ {
724+ /* Hiragana */
725+ return 2 ;
726+ }
727+ else if ( 0x30a1 <= c && c <= 0x30f6 )
728+ {
729+ /* Katakana */
730+ return 2 ;
731+ }
732+ else if ( 0x3105 <= c && c <= 0x312c )
733+ {
734+ /* Bopomofo */
735+ return 2 ;
736+ }
737+ else if ( 0x3131 <= c && c <= 0x318e )
738+ {
739+ /* Hangul Elements */
740+ return 2 ;
741+ }
742+ else if ( 0xac00 <= c && c <= 0xd7a3 )
743+ {
744+ /* Korean Hangul Syllables */
745+ return 2 ;
746+ }
747+ else if ( 0xff01 <= c && c <= 0xff5e )
748+ {
749+ /* Fullwidth ASCII variants */
750+ return 2 ;
751+ }
752+ else if ( 0xff61 <= c && c <= 0xff9f )
753+ {
754+ /* Halfwidth Katakana variants */
755+ return 1 ;
756+ }
757+ else if ( ( 0xffa0 <= c && c <= 0xffbe ) ||
758+ ( 0xffc2 <= c && c <= 0xffc7 ) ||
759+ ( 0xffca <= c && c <= 0xffcf ) ||
760+ ( 0xffd2 <= c && c <= 0xffd7 ) ||
761+ ( 0xffda <= c && c <= 0xffdc ) )
762+ {
763+ /* Halfwidth Hangule variants */
764+ return 1 ;
765+ }
766+ else if ( 0xffe0 <= c && c <= 0xffe6 )
767+ {
768+ /* Fullwidth symbol variants */
769+ return 2 ;
770+ }
771+ else if ( 0x4e00 <= c && c <= 0x9fa5 )
772+ {
773+ /* Han Ideographic */
774+ return 2 ;
775+ }
776+ else if ( 0xf900 <= c && c <= 0xfa2d )
777+ {
778+ /* Han Compatibility Ideographs */
779+ return 2 ;
780+ }
781+ else
782+ {
783+ /* Unknown character: need to use GDI*/
784+ if ( _hDC == ( IntPtr ) 0 )
785+ {
786+ _hwnd = NativeMethods . GetConsoleWindow ( ) ;
787+ if ( ( IntPtr ) 0 == _hwnd )
788+ {
789+ return 1 ;
790+ }
791+ _hDC = NativeMethods . GetDC ( _hwnd ) ;
792+ if ( ( IntPtr ) 0 == _hDC )
793+ {
794+ //Don't throw exception so that output can continue
795+ return 1 ;
796+ }
797+ }
798+ bool result = true ;
799+ if ( ! _istmInitialized )
800+ {
801+ result = NativeMethods . GetTextMetrics ( _hDC , out _tm ) ;
802+ if ( ! result )
803+ {
804+ return 1 ;
805+ }
806+ _istmInitialized = true ;
807+ }
808+ int width ;
809+ result = NativeMethods . GetCharWidth32 ( _hDC , ( uint ) c , ( uint ) c , out width ) ;
810+ if ( ! result )
811+ {
812+ return 1 ;
813+ }
814+ if ( width >= _tm . tmMaxCharWidth )
815+ {
816+ return 2 ;
817+ }
818+ }
819+ return 1 ;
820+ }
821+
822+ public void StartRender ( )
823+ {
824+ _codePage = NativeMethods . GetConsoleOutputCP ( ) ;
825+ _istmInitialized = false ;
826+ var consoleHandle = _outputHandle . Value ;
827+ CONSOLE_FONT_INFO_EX fontInfo = ConhostConsole . GetConsoleFontInfo ( consoleHandle ) ;
828+ int fontType = fontInfo . FontFamily & NativeMethods . FontTypeMask ;
829+ _trueTypeInUse = ( fontType & NativeMethods . TrueTypeFont ) == NativeMethods . TrueTypeFont ;
830+
831+ }
832+
833+ public void EndRender ( )
834+ {
835+ if ( _hwnd != ( IntPtr ) 0 && _hDC != ( IntPtr ) 0 )
836+ {
837+ NativeMethods . ReleaseDC ( _hwnd , _hDC ) ;
838+ }
839+ }
616840 }
617841}
0 commit comments