@@ -409,6 +409,38 @@ public static string ToGestureString(this ConsoleKeyInfo key)
409
409
410
410
internal class ConhostConsole : IConsole
411
411
{
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
+
412
444
private readonly Lazy < SafeFileHandle > _inputHandle = new Lazy < SafeFileHandle > ( ( ) =>
413
445
{
414
446
// 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)
613
645
readBufferSize , readBufferCoord , ref readRegion ) ;
614
646
return result ;
615
647
}
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
+ }
616
840
}
617
841
}
0 commit comments