银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::

前言

我们知道,Mono 是 .NET Framework 跨平台的开源实现。Mono 的源代码就是金矿,等待我们去挖掘。

目前 Mono 的最新版本是 Mono 2.8.2,可以到 http://ftp.novell.com/pub/mono/sources/mono/ 下载 mono-2.8.2.tar.bz2,文件大小是30MB。可以参阅“在 Ubuntu 10.10 操作系统安装 Mono 2.8.2”一文。

现在,让我们来看看 Mono 是如何实现 .NET Framework Base Class Library 中的 System.Console 类的。Console 类在 CUI 应用程序中有着十分重要的作用。而且,从 .NET Framework 2.0 开始,Console 类除了实现基本的控制台输入/输出功能以外,还实现了诸如更改前景色和背景色等高级功能。下图就是一个例子:

 

Microsoft 实现的 Console 类只需要考虑 Windows 操作系统的控制台就行了,但是 Mono 的 Console 类就必须考虑跨平台了,要能够工作在 Windows 和 Unix 操作系统中。所以是比较复杂的。

准备在自己的工作目录下编译出 Console.dll 程序集

为了研究 Console 类的源代码,找出和 Console 类密切相关的源代码,我准备从 Console.cs 出发在自己的工作目录下编译出一个 Console.dll 程序集。我们来看看 Console 类的源代码位于 Mono 体系的什么位置:

ben@ben-1520:~$ cd src/mono-2.8.2
ben@ben-1520:~/src/mono-2.8.2$ find . -name Console.cs
./mcs/class/corlib/System/Console.cs
ben@ben-1520:~/src/mono-2.8.2$ 

哦,因为 Console 类定义于 .NET Framework 最核心的 mscorlib.dll 程序集,并且位于 System 命名空间。所以上面的命令就显示这样的结果了。

我们现在准备一个工作目录:

ben@ben-1520:~$ cd ~/work
ben@ben-1520:~/work$ mkdir Console
ben@ben-1520:~/work$ cd Console
ben@ben-1520:~/work/Console$ 

先把上述 Console.cs 从 Mono 的源代码目录拷贝到工作目录中,然后使用 C# 编译器进行编译,根据出错信息来决定还要拷贝哪些文件。最终,发现需要 Mono 源代码 mcs/build/common、mcs/class/corlib/System 和 mcs/class/corlib/System.IO 目录下的 23 个 C# 源程序文件,在上面三个目录分别是 2 个、18 个和 3 个。我编写了一个如下内容的 mksrc.sh 脚本,用于将 Mono 2.8.2 的源代码与 Console 类相关的源文件复制到工作目录中:

01:  cd ~/work/Console
02:  rm -rf mcs
03:  mkdir mcs
04:  mkdir mcs/build
05:  mkdir mcs/build/common
06:  mkdir mcs/class
07:  mkdir mcs/class/corlib
08:  mkdir mcs/class/corlib/System
09:  mkdir mcs/class/corlib/System.IO
10:  cd ~/src/mono-2.8.2/mcs/build/common
11:  cp Locale.cs MonoTODOAttribute.cs ~/work/Console/mcs/build/common
12:  cd ~/src/mono-2.8.2/mcs/class/corlib/System.IO
13:  cp Stream.cs UnexceptionalStream*er.cs ~/work/Console/mcs/class/corlib/System.IO
14:  cd ~/src/mono-2.8.2/mcs/class/corlib/System
15:  cp Buffer.cs Control*.cs CStream*.cs *Term*.cs *Console*.cs ~/work/Console/mcs/class/corlib/System
16:  cd ~/work/Console/mcs/class/corlib/System
17:  rm ConsoleColor.cs ConsoleKey.cs ConsoleModifiers.cs ConsoleSpecialKey.cs
18:  cd ~/work/Console

从 Stream.cs 中分离出 NullStream 类的源代码

在 Mono 的源代码的 Stream.cs 文件中,包含以下三个类:

  1. public abstract class Stream : MarshalByRefObject, IDisposable
  2. internal class NullStream : Stream
  3. internal class SynchronizedStream : Stream

因为我们需要用到 NullStream 类,所以将工作目录中 mcs/class/corlib/System.IO/ 目录下的 Stream.cs 改名为 NullStream.cs,然后在该文件中删除另外两个类的源代码,只保留 NullStream 类的源代码。

虽然 Mono 是一项伟大的开源工程,大师们写的代码也非常精彩。但是在这里还是有点疏忽了。如果 Mono 的源代码的 Stream.cs 文件中只包含 Stream 类,而把 NullStream 类的源代码放在 NullStream.cs 文件中,把 SynchronizedStream 类的源代码放在 SynchronziedStream.cs 文件中。我们也就可以直接使用 NullStream.cs 文件了,不用象现在一样又是改名,又是删除代码。

编写辅助代码

现在让我们编写一些简单的辅助代码,以便能够编译生成 Console.dll 程序集文件。

ben@ben-1520:~/work/Console$ mkdir Skyiv
ben@ben-1520:~/work/Console$ cd Skyiv
ben@ben-1520:~/work/Console/Skyiv$ gedit AssemblyInfo.cs  Environment.cs  ExtensionMethods.cs  MonoIO.cs  Stub.cs
ben@ben-1520:~/work/Console/Skyiv$ 

下面就是 AssemblyInfo.cs,仅仅是简单地使用 CLSCompliantAttribute 标记程序集,指示该程序集符合公共语言规范。

1:  using System;
2:  
3:  [assembly:CLSCompliant(true)]

下面就是 Environment.cs,主要是因为在 Console.cs、ConsoleDrivers.cs 这两个源程序中使用了 Environment 静态类的 IsRunningOnWindows 静态属性,而这个属性是 internal 的。所以我们必须自己提供一个,不然程序无法通过编译。我们还必须提供在其他源程序中有使用的 NewLine 属性以及 Exit 和 GetEnvironmentVariable 方法。这三个成员原来是 public 的,但是我们自己用的话,使用 internal 来修饰是没有问题的。注意,IsRunningOnWindows 属性指示是否运行在 Windows 操作系统中,这在 Microsoft 的 .NET Framework 中是没有的,因为 Microsoft 的实现总是运行在 Windows 操作系统中。Mono 为了跨平台才需要这个属性。

01:  namespace System
02:  {
03:    static class Environment
04:    {
05:      internal static bool IsRunningOnWindows { get { return false; } }
06:      internal static string NewLine { get { return "\n"; } }
07:      internal static void Exit(int code) { }
08:      internal static string GetEnvironmentVariable(string value) { return value; }
09:    }
10:  }

下面就是 ExtensionMethdos.cs,仅提供一个扩展方法,即用于 StreamReader 类的 DataAvailable 方法。在 Mono 的StreamReader 类中,DataAvailable 方法是 internal 的,所以我们必须自己提供一个。这个 DataAvailable 方法在 TermInfoDriver.cs 源程序中有使用。

01:  namespace System.IO
02:  {
03:    static class ExtensionMethods
04:    {
05:      internal static bool DataAvailable(this StreamReader reader)
06:      {
07:        return reader.Peek() != -1;
08:      }
09:    }
10:  }

下面就是 MonoIO.cs。这个 MonoIO 类在 Console.cs 和 ConsoleDriver.cs 源程序中有使用。很奇怪,如果不提供这个 MonoIO 类,在 Ubuntu 10.10 操作系统的 Mono 2.8.2 环境下是能够编译成功的,但在 Windows 操作系统的 .NET Framework 4 环境下无法编译成功。

1:  namespace System.IO
2:  {
3:    static class MonoIO
4:    {
5:      internal static IntPtr ConsoleInput { get { return (IntPtr)0; } }
6:      internal static IntPtr ConsoleOutput { get { return (IntPtr)1; } }
7:      internal static IntPtr ConsoleError { get { return (IntPtr)2; } }
8:    }
9:  }

下面就是 Stub.cs,提供了 Encoding 和 TextWriter 类的一些 internal 静态成员,以及 FileStream 类的 internal 构造函数和一个隐式转换操作符,用以把我们的 Skyiv.Stub.FileStream 隐式转换为 System.IO.FileStream。这些东东都只在 Console.cs 源程序中使用。

01:  using System;
02:  using System.IO;
03:  
04:  namespace Skyiv.Stub
05:  {  
06:    static class Encoding
07:    {
08:      internal static System.Text.Encoding UTF8Unmarked { get { return System.Text.Encoding.UTF8; } }
09:      internal static void InternalCodePage (ref int code_page) { }
10:    }
11:    
12:    static class TextWriter
13:    {
14:      internal static System.IO.TextWriter Synchronized(System.IO.TextWriter writer, bool neverClose)
15:      {
16:        return writer;
17:      }
18:    }
19:    
20:    sealed class FileStream
21:    {
22:          internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isZeroSize)
23:          {
24:          }
25:          
26:          public static implicit operator System.IO.FileStream(FileStream value)
27:          {
28:            return null;
29:          }
30:    }
31:  }

修改 Console.cs 源程序

现在,修改 Console.cs 源程序,对第 127、131、138、144、150 和 187 行共六处的 Encoding、TextWriter 和 FileStream 前面加上“Skyiv.Stub.”,以便调用我们自己的版本。

生成编译响应文件并编译

ben@ben-1520:~/work/Console/Skyiv$ cd ..
ben@ben-1520:~/work/Console$ ls Skyiv/*.cs mcs/build/*/*.cs mcs/class/*/*/*.cs > make.rsp
ben@ben-1520:~/work/Console$ gedit make.rsp
ben@ben-1520:~/work/Console$ dmcs @make.rsp
ben@ben-1520:~/work/Console$ 

在使用 gedit 编辑 make.rsp 时,添加如下前四行的内容:

01:  -t:library
02:  -out:Console.dll
03:  -unsafe
04:  -nowarn:436
05:  mcs/build/common/Locale.cs
06:  mcs/build/common/MonoTODOAttribute.cs
07:  mcs/class/corlib/System/Buffer.cs
08:  mcs/class/corlib/System/ConsoleCancelEventArgs.cs
09:  mcs/class/corlib/System/ConsoleCancelEventHandler.cs
10:  mcs/class/corlib/System/Console.cs
11:  mcs/class/corlib/System/ConsoleDriver.cs
12:  mcs/class/corlib/System/ConsoleKeyInfo.cs
13:  mcs/class/corlib/System/ControlCharacters.cs
14:  mcs/class/corlib/System/CStreamReader.cs
15:  mcs/class/corlib/System/CStreamWriter.cs
16:  mcs/class/corlib/System/IConsoleDriver.cs
17:  mcs/class/corlib/System.IO/NullStream.cs
18:  mcs/class/corlib/System.IO/UnexceptionalStreamReader.cs
19:  mcs/class/corlib/System.IO/UnexceptionalStreamWriter.cs
20:  mcs/class/corlib/System/KnownTerminals.cs
21:  mcs/class/corlib/System/NullConsoleDriver.cs
22:  mcs/class/corlib/System/TermInfoBooleans.cs
23:  mcs/class/corlib/System/TermInfoDriver.cs
24:  mcs/class/corlib/System/TermInfoNumbers.cs
25:  mcs/class/corlib/System/TermInfoReader.cs
26:  mcs/class/corlib/System/TermInfoStrings.cs
27:  mcs/class/corlib/System/WindowsConsoleDriver.cs
28:  Skyiv/AssemblyInfo.cs
29:  Skyiv/Environment.cs
30:  Skyiv/ExtensionMethods.cs
31:  Skyiv/MonoIO.cs
32:  Skyiv/Stub.cs

终于,我们成功地使用 C# 编译器编译生成了 Console.dll 程序集文件。当然,这个 Console.dll 仅仅是用来研究 Mono 的源代码,是不能正常工作的,因为我们使用了一些自己编写的辅助代码替代了 Mono 系统的。

查看源程序的目录结构

下面就是我们工作目录的内容:

Console02

各种类型之间的关系图

Console01

在上图中,最核心的类型如下所示:

  1. Console: public static class,调用下面的 ConsoleDriver 类的静态方法和静态属性来干活。
  2. ConsoleDriver: internal class,内部持有一个类型为 IConsoleDriver 接口的 internal 静态字段。
  3. IConsoleDriver: internal interface,以下三个类均实现 IConsoleDriver 接口。
  4. NullConsoleDriver: internal class,只实现最基本的控制台输入/输出功能,用于哑终端等情况。
  5. TermInfoDriver: internal class,用于 Unix 操作系统的各种终端。
  6. WindowsConsoleDriver: internal class,用于 Windows 操作系统的控制台。

可以点击下载 Console.7z

 

(未完待续)

posted on 2011-01-22 15:01  银河  阅读(5923)  评论(9编辑  收藏  举报