星期一, 4月 21, 2008

[Boo]Boo(5) - Console.ReadKey()

承接上篇的討論,經過 Hack 之後,發現原因就出在 Console.ReadKey()。

booish 與 IronPython 為了要能達到自己的需求,所以並不使用 Console.ReadLine(),而是使用 Console.ReadKey() 自行處理收到的按鍵並輸出。
為了要知道為甚麼會有問題,我試著寫了一個小程式,想知道相似的方式,是否也會有同樣的問題發生:
// Program: readkey.cs
// How to compile: gmcs /target:exe readkey.cs
using System;
public class Hello {
public static void Main( string[] args ) {
Console.TreatControlCAsInput = true;
ConsoleKeyInfo keyInfo;
do
{
keyInfo = Console.ReadKey( true );
Console.Write( keyInfo.KeyChar );
} while( keyInfo.Key!=ConsoleKey.Escape );
}
}


這個小程式編譯以後,使用 .NET 來執行,一切正常,但使用Mono Windows 版來執行,就硬是會 echo 出兩次。
經過用Reflector比對以後,發現Mono的 Console.ReadKey() 的迴圈條件式有問題。
如果將該條件式修正為

// } while( record.EventType != 1 && !record.KeyDown); // original
} while( !(record.EventType==1 && record.KeyDown ) );

// 附帶一提,這是 Windows .NET 的版本的變形,多判斷了 Character=='\0' 與 VirtualKeyCode 不是 Ctrl、alt、shift、numlock、caplock、scrolllock 的情況
//} while( !(record.EventType==1 && record.KeyDown ) || record.Character=='\0' && ( ((record.VirtualKeyCode<0x10 || record.VirtualKeyCode>0x12) && record.VirtualKeyCode!=0x14 && record.VirtualKeyCode!=0x90 ) ? (record.VirtualKeyCode==0x91) : true ) );

就可以解決問題,我想這可能是因為寫作 WindowsConsoleDriver.cs 的人對 ReadConsoleInput() 不夠清楚的緣故,不過其實我也是看了 source code 才知道要這樣去判斷。
總之,一切就此水落石出。

等等,可是為甚麼 Console.ReadLine() 沒有問題呢?嗯嗯,這邊的原因也很簡單,因為不管是 .NET 的 CLR 或是 Mono,都是調用 stdin 進行處理,所以就沒有這樣的問題了。

解決方法,目前並沒有,應急的方法是自行下載原始碼,修正Console.ReadKey()(在class/corlib/System/WindowsConsoleDriver.cs)以後,再編譯。不急的話,是先上Mono Buzilla去找找看是否有人回報過此問題,如果沒有再回報上去。
晚點要去找找看,然後回報上去,希望下個版本能解決。

沒有留言: