RSS 2.0 Feed
.NET General
摘要:在平时用电脑的过程中,我喜欢隔段时间就把鼠标左右调换一下,一来是想让自己一侧的肩、手臂、手腕能得到休息,尽量避免因长期固定姿势使用鼠标而带来的身体不适,另一方面也因为我本来就是个左撇子,喜欢尝试给两只手均等的做事机会。当然,自从我给电脑同时接上左右两只鼠标以后,来回调换鼠标是避免了(据我所知,有在电脑上接三四只鼠标的,真是厉害!),但系统对于鼠标左右键的安排却不听我的调换,还要费力地去控制面板中设置一番,非常麻烦。因此我想,何不写几行代码,让鼠标左右键能在最短的时间内左右切换呢?这主意不错!OK,马上到MSDN里查找一番,十几分钟写出了下面这个小程序。1. SwapMouseButton根据MSDN所述,可以通过调用SwapMouseButton这一API来实现鼠标左右键功能互换,Windows控制面板中的相关设置也是通过调用该API来实现的。该API存在于user32.dll文件中,它的原型是:BOOL SwapMouseButton(   BOOL fSwap);我们可以在.NET工程中通过P/Invoke轻松声明对该API的调用。我写的调用声明如下:// P/Invoke declarations[DllImport("user32.dll")]private extern static bool SwapMouseButton(bool fSwap);其中,fSwap是唯一需要传入的参数,当它的值为true时,系统会把鼠标左右键功能进行互换(即换成左手鼠标);当它的值为false时,系统会把鼠标左右键功能还原为默认状态(即右手鼠标)。因此,当我想使用左手鼠标时,只需向SwapMouseButton方法传入一个true变量即可实现,非常方便。2. SystemParametersInfo除了调用SwapMouseButton来实现鼠标左右值功能切换之外,Windows还提供了另外一个实现同样效果的API:SystemParametersInfo。根据MSDN,该API的原型是:BOOL SystemParametersInfo(UINT uiAction,UINT uiParam,PVOID pvParam,UINT fWinIni);用C#可以进行如下声明:[DllImport("user32.dll")]private extern static int SystemParametersInfo(uint uiAction, uintuiParam, IntPtr pvParam, uint fWinIni);其中,uiAction指定一个需要获取的系统参数,如屏幕大小、分辨率、鼠标配置等,如果传入参数SPI_SETMOUSEBUTTONSWAP就可以用来设置鼠标功能切换;当向uiParam参数传入正值时,系统就会把鼠标左右键功能进行互换(即换成左手鼠标),当向uiParam传入0时,系统还会把鼠标功能还原成默认状态(即还原为右手鼠标)。由此,我们需要在uiAction位置传入SPI_SETMOUSEBUTTONSWAP。不过很明显,SPI_SETMOUSEBUTTONSWAP只是C语言中#define编译预定义指令所定义出的一个常量,我们必须在C#代码中对它进行重新定义。OK,打开Winuser.h头文件,在其中搜索SPI_SETMOUSEBUTTONSWAP,找到如下定义(位于8623行):#define SPI_SETMOUSEBUTTONSWAP  33可见,SPI_SETMOUSEBUTTONSWAP定义的是整型值33,这样我们就可以在C#中做出如下定义:private const uint SPI_SETMOUSEBUTTONSWAP = 33;3. GetSystemMetrics通过上述两种方法,我们都可以轻松地实现鼠标功能切换了。不过,在每次切换之前,如果我们的应用程序能够获知系统当前的鼠标设 置,那么就可以决定该向哪个(相反)方向进行切换,从而给使用者带来一种更好的用户体验。那么该怎样才能获知当前系统的鼠标设置呢?GetSystemMetrics可以解决这一问题。根据MSDN,GetSystemMetrics的原型是:int GetSystemMetrics(int nIndex);其中nIndex参数有SM_SWAPBUTTON值可选,SM_SWAPBUTTON在Winuser.h头文件中的定义是:#define SM_SWAPBUTTON  23我们可以在C#中如此定义SM_SWAPBUTTON:private const int SM_SWAPBUTTON = 23;声明GetSystemMetrics:[DllImport("user32.dll")]private extern static int GetSystemMetrics(int nIndex);  // Updated 04.10.22. Many thanks to JGTM'2004 [MVP]当GetSystemMetrics返回0时,表明当前系统鼠标设为默认状态(右手鼠标),否则表明鼠标已被设为左手鼠标。这样,我们就可以在切换鼠标状态之前获知系统的当前鼠标状态,从而可以做出相反方向的切换了。OK,把上述几点连起来,就可以轻松写成一个能快速切换鼠标功能键的小程序了。我把这个小程序放在系统托盘里,只需单击一下它的图标就能在左手和右手鼠标之间进行快速切换,同时通过图标的方向变化来指示当前的鼠标状态。这样我就再也不必为切换鼠标的事而头疼了:)...[阅读全文]

posted @ | Feedback (18) |

摘要:Nant的确是一款非常强大的build工具,但它的build文件相对来说却比较难写,因此一款合适的可视化编辑工具就显得尤为必要了。这不,我今天就在http://www.nantpad.com找到了NAntPad。目前它的最高版本是o.4Beta,可以在上述网站免费注册后下载。 ? 从运行情况上看,Nantpad的确对NAnt中众多的build选择实现了可视化管理,比如当用Nantpad打开我在上一篇blog “How to drive al.exe in NAnt?”(http://blog.joycode.com/musicland/posts/11681.aspx)中手工撰写的那个build文件后,Nantpad立刻以树型结构显示出build文件中的目录结构:? 注意,在第一次运行Nantpad的时候需要在Tools-Schema Manager中指定NAnt的schema文件(默认安装在\schema目录下),以便Nantpad获取NAnt schema信息,否则是无法分析build文件结构的。 ? 接下来就可以试一试Nantpad可视化编辑的能力,给这个HelperDemo.build文件增加一项新的功能——在编译后运行编译结果。在NAnt文档中这项功能由节点指定,我们需要做的只是增加一个(如果你想把它放在原有的节点也可以),在新增的节点下增加一个节点,配置它的属性为将要运行的程序集的名称即可。整个过程非常简单,所有操作都在右键弹出菜单中。下图就是按上述步骤在Nantpad中配置后的HelperDemo.build文件: 值得注意的是,我还给节点配置了属性,以传入程序集运行参数,因为即将运行的程序集(HelperDemo.exe)将要对传入的两个int参数执行简单的求和运算。用文本编译器打开这份配置文件查看内容如下: ? ??? ??? ??? ??? ??????? ??????????? ??????????????? ??????????????? ??????????? ??????? ??? ??? ??????? ??? ? 可见,Nantpad已经在后台自动生成了所需的build脚本,无须我们手工书写了。 最后,保存这份文件并转入cmd.exe调用nant.exe进行编译,运行结果如下: 最后需要说明的是,由于Nantpad目前还是Beta测试版本,因此在很多方面还并不完善(比如说界面中的帮助信息还没有,我在运行过程中Nantpad还几次死掉)。另外,虽然可视化编辑给开发人员带来了解脱,但对我这样的shell爱好者来说,除非已经对某些命令或脚本烂熟于心,或者为了赶项目进度,否则还是愿意高高兴兴地手动写代码。:)...[阅读全文]

posted @ | Feedback (9) |

摘要:在用NAnt的过程中一直有个问题没弄清,那就是al.exe在NAnt中的用法。我们知道,al的用途是将不同的资源文件(如图标、本地化文件)或程序集模块等连续成一个具有清单(manifest)的.NET程序集(Assembly),但是在NAnt中具体该怎样写build文件来调用al呢? ? 下面为了说明方便,我先给出一个简单的例子: ? file1: Client.cs using System; ? public class Client { ?????? public static void Main(string[] args) ?????? { ????????????? // make sure two numbers provided ????????????? if (args.Length!=2) { ???????????????????? Console.WriteLine("please provide two numbers"); ???????????????????? return; ????????????? } ? ????????????? // Call Helper.Add() function to make a simple calculate ????????????? int x=Convert.ToInt32(args[0]); ????????????? int y=Convert.ToInt32(args[1]); ? ????????????? int total=Helper.Add(x, y); ? ????????????? Console.WriteLine("{0} + {1} = {2}.", x, y, total); ?????? } } ? file2: Helper.cs public class Helper { ?????? public static int Add(int x, int y) ?????? { ????????????? return x+y; ?????? } } ? 上面是一个很简单的加法运算的例子,为了用到al.exe,我把客户端调用(Client)和业务逻辑(Helper)分离在两个单独的.cs文件中。传统的.NET SDK编译方法是: ? 1.?????? 编译Helper.cs csc /t:module Helper.cs ? 2.?????? 编译Client.cs csc /t:module /addmodule: Helper.netmodule Client.cs ? 3.?????? 连接 al Helper.netmodule Client.netmodule /t:exe /out:HelperDemo.exe /main:Client.Main ? 这样三步下来就可以使用生成的HelperDemo.exe了。不过上面这些步骤在NAnt中该怎样实现呢?我查看了NAnt安装目录下的文档,其中在(注:这是NAnt中以XML格式抓抓撰写build文档的一种方式)节点下的确有一个节点,可是其中没有给出/main的实现形式,这也就意味着代码根本无法连接成功,因为.NET运行时环境是无法调用没有.entrypoint标记的程序集的。 ? 这个问题的确让我很费脑筋。我想自己可能钻牛角尖了,因为NAnt一定会提供一个非常方便、非常直接的连接机制的。今天上午,当我再次用NAnt撰写build文件时忽然想起了什么——在节点下有一个main属性,它肯定是用来指定程序集的入口点的,而NAnt一定是把al.exe的连接过程包含在csc任务里了,这样反而简化了build流程。对!就试试! ? 于是我写成了下面的build文件: ? ??? ? ??? ??????? ??????????? ??????????????? ???????????????......[阅读全文]

posted @ | Feedback (0) |

摘要:作为一名.NET开发人员,我一直以来都有一个愿望,那就是让我的软件运行——不——是能够运行在所有的电脑里,不管它运行的是什么操作系统,不管它归属于哪个阵营。此前一直听说MONO(http://www.go-mono.org)正在做这方面的努力,然而真正让我走入跨平台世界的却是另一个名不见经传的项目——DotGNU。 ? 什么是DotGNU? ? 从名字中就可以看出,DotGNU归属GNU阵营,它的目标是把.NET环境移植到以GNU/Linux为首的多种操作平台之上,从而实现真正意义上的跨平台.NET开发。目前,该项目已经能够支持GNU/Linux、Windows、NetBSD、FreeBSD、Solaris以及MacOS X等多种操作系统。如果你也和我一样对跨平台开发.NET应用有兴趣的话,那么请到DotGNU的主页(http://www.dotgnu.org)上去更多地了解有关该项目的情况,我在这儿就不多介绍了。 ? Hello World on Linux 接下来就让我们开发一个真正跨平台的.NET程序,为简便起见,我们就写一个简单的HelloWorld吧:)另外,由于我机器上目前只有Windows和Linux两种环境,所以只能演示跨越这两个平台的应用,如果你有Mac OS、Solaris或者其它环境并且感兴趣的话,那么也请你帮我来完成在其它平台上的移植:)。 ? 让我们开始吧。首先需要配置一套DotGNU运行环境(就如同配置.NET SDK一样)。可以从DotGNU网站上免费下载最新的安装文件(约210M),直接执行安装即可。当然你也可以只下载它的源代码并在本机编译,甚至从CVS中即时获取最新的版本。安装之后,请把安装目录下的bin子目录的绝对路径添加到机器的PATH变量中,以方便随时调用编译器及其它辅助开发工具。 ? 开发环境设定好之后就可以进行编码了。请在Windows里打开你惯用的文本编辑器,写一个最简单的C#版Hello World如下(我用的是Emacs): 把它存盘成HelloWorld.cs之后就可以编译了。由于是在Windows操作系统里,所以你既可以用微软的csc也可以用DotGNU的cscc。前者大家日常接触的最多,所以在这儿我就演示如何调用cscc进行编译。在Emacs里调出shell(或者在cmd.exe中),运行以下命令(请确保HelloWorld.cs所在目录是当前目录): ? cscc –o HelloWorld.exe HelloWorld.cs ? 其中,o指示生成文件的名称。整个用法与微软的csc几乎一样。 ? 如果编译成功,接下来就可以运行HelloWorld.exe了。与编译一样,由于这是在Windows操作系统里,因此你可以有两种方法运行它。首先便是在cmd.exe里直接运行HelloWorld.exe,这也是Windows下运行应用程序的惯例;当然你也可以试试DotGNU提供的ilrun.exe工具,它的作用就是让应用程序在DotGNU提供的.NET运行时环境中运行。调用方法如下: ? ilrun HelloWorld ? 运行成功: 好,既然这段代码在Windows下可以编译和运行了,那么我们接下来就把它拿到Linux下面编译并运行,看看奇迹能不能发生:) ? 打开VMWare,启动Red Hat Linux 9虚拟机。由于.NET程序的运行需要.NET运行时环境支持,因此我们在Linux下面也同样应该安装DotGNU环境。DotGNU安装文件中针对不同的操作系统提供有不同的安装方式,按提示一步步执行正常安装即可。 ? 接下来就请找到刚才编写过的那个HelloWorld.cs文件(你可以通过与Host建立网络连接实现,也可以用最简便的方法——拷贝了事:),在Bash中(如果你安装的是其它不同的shell,如CSH,则操作方式略有不同)执行和Windows下一样的编译命令: ? cscc –o HelloWorld.exe HelloWorld.cs ? 注意,output文件不一定要采取.exe后缀,这里我只是为了延用Windows下的习惯。 ? 如果没收到任何错误提示,那么恭喜你,编译成功了!现在就可以运行ilrun来执行我们的HelloWorld.exe了: ? ilrun HelloWorld ? 哇! 由于我在源代码中增加了一个处理命令行参数的逻辑,因此你也可以像上图中那样随便输入一些字符串,HelloWorld.exe都会把它们分别打印出来。 ? 好了,我们可爱的HelloWorld真的已经实实在在地跨越了不同的平台,真正实现.NET了!如果你有兴趣,还可以给它增加不同的功能,测试看DotGNU是否已经把你关心的功能予以移植。另外,可能所有人最关心的一个问题是:DotGNU能把System.Windows.Forms命名空间移植成功吗?答案是肯定的!在DotGNU的主页上就有WinForms应用程序运行在Linux、BSD、Mac OS等上的截图,DotGNU提供有多个demo及源码以供开发人员参考,我在Cygwin下也的确成功编译并运行了WinForms应用程序。然而由于我目前还无法成功配置Linux下完整的DotGNU运行环境,因此只能编译而无法成功运行WinForms程序。不过我相信这点困难只是暂时的,我会尽快把问题解决然后给大家附上WinForms版的HelloWorld。:) ? 目前DotGNU项目还在快速地进行之中,因此我们有理由相信DotGNU的工作,相信我们喜爱的那些.NET程序总有一天会成功地运行在不同的操作系统上! ...[阅读全文]

posted @ | Feedback (3) |

摘要:如果你曾经看过MSDN TV——Lap Around Longhorn (http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20031028LHORNDB/manifest.xml),你的最大感受是什么?是酷得发眩的Avalon?还是强大得没道理的Indigo?OK,这些感受我也有,可对我而言,最大的冲击不在于Longhorn给开发人员所带来的那些梦幻(还有痛苦),而是——Don Box竟然用Emacs开发C#和XAML! ? 这实在太酷了。想想吧,一个黑衣男子坐在电脑前面,打开元祖级的编译器飞速地写着运行在未来平台下的代码,这场景简直是太绝妙了!看他运指如飞地写着代码,思路清晰地讲解和调侃,这过程就是一次彻头彻尾的享受! ? 享受过后,自己竟也种强烈的参与感——何不下载一份Emacs来试试?(虽然Don Box很快便在他的blog里声明说他也要转向IDE了)。 ? 以前在玩Linux的时候,自己也曾接触过一些Emacs的基本操作,但那时自己还未开始写软件,对计算机的掌握也属于入门级,几条指令就会让自己兴趣全无,所以每次都是虎头蛇尾,不了了之。:( ? Emacs归属GNU,它的主页位于http://www.gnu.org/software/emacs,最新版本为21.3,可以在ftp://ftp.gnu.org/gnu/emacs/下载获得。下载并解压缩后即可直接使用,也可以运行bin\addpm.exe文件在系统中自动注册Emacs,同时生成快捷方式。 ? Emacs的运行非常简单,像任何其它Windows可执行文件一样通过单击快捷方式即可。但是它的界面与一般Windows程序有较大的不同,从未用过Unix/Linux操作系统的人可能需要一段时间的适应。 ? Emacs已经运行起来了,但在用它进行开发之前还应该了解一些Emacs的基本操作方法。这里建议阅读Emacs中带的Tutorial,以及Emacs网页上所提供的相应文档。此外,IBM DeveloperWorks网站上的Living In Emacs(http://www-106.ibm.com/developerworks/linux/edu/l-dw-linuxemacs-i.html)一文也可以当很好的入门教材。通过阅读这些文档及资料,你会逐渐接受和习惯Emacs世界的游戏规则,同时(如果你比较喜欢接受新鲜事物的话)在感到陌生之余很可能还会有一种豁然开朗的感觉,因为你已经走进了一个几乎完全不同的世界。:) ? 默认情况下,Emacs已经可支持c/c++以及Java开发(其它的语言我没试过,因为我机器上只有上述几种语言的编译器),但如果你想用它来开发C#还需要做一些其它的工作。Emacs就像一把枪,在需要时可以添入不同的子弹,我们现在需要的就是找一粒刻有C#字样的子弹压入枪膛。这粒子弹的名字叫C# for Emacs mode,是由微软的一位工程师Brad Merrill利用业余时间开发的,可以在他的个人网站http://www.cybercom.net/~zbrad/处下载,最新版本为4.0。 ? 下载之后需要对所获得的package文件进行一些简单的配置。因为我此前也没有接触过Lisp语言,因此配置成了一大难题。经过一番google搜索后,终于在http://www.experts-exchange.com/Programming/Q_20815476.html找到了答案。原来配置竟是非常简单: ? 1.? 把下载文件夹中cite-lisp子目录下的文件复制到Emacs安装目录\cite-lisp\下。 2.? 把下载文件中zbrad.emacs文件(.emacs是Emacs的配置文件)也复制到Emacs安装目录\cite-lisp子目录下,然后改名为site-start.el。 3.? 重新启动Emacs,OK! ? 现在的Emacs就已经具备编辑C#源文件的能力了。利用你已经熟悉的Emacs操作方法,马上就能写一个HelloEmacs出来了。:)以下是我用Emacs写HelloEmacs的简单步骤和截图: ? 1.在Emacs编辑器中书写C#代码。可以选择开启或关闭语句色彩提示(我发现C# for Emacs Mode在排版上有一些怪,特别是文本缩进有些别扭): ? 2.在Tool菜单内可以方便地调出编译器(csc)和命令提示符工具(cmd),这样不用离开Emac也可以编译代码和执行了: ? 3.调出csc: ? 4.在另一个frame中显示编译结果(Emacs对中文的支持不是很让人满意,特别是在编辑时): ? 5.直接调用强大的命令行工具: ? 6.运行成功以后的界面(找到运行结果了吗?:): ? OK,Emacs应用起来也不是很难对吗?不过,我们上面接触的只是Emacs万千功能之中的一两种,Emacs最伟大之处就在于它几乎无限的可扩展性,通过开发或者安装适当的package,你可以在Emacs中开发软件,撰写文章,阅读新闻,浏览网页,查看电子邮件……当然,你也可以什么都不做,而仅仅是通过Emacs来换取一种新鲜感。不是吗?每天面对同样的开发工具和操作界面,你会不会常常觉得工作和生活已经毫无乐趣可言了呢?偶尔换一下工具,哪怕只有几分钟,哪怕仅仅是为了让自己也变得有那么点儿酷!:)...[阅读全文]

posted @ | Feedback (18) |

摘要:iBuySpy Store是iBuySpy系列Demo中的一部分,意在演示用ASP.NET和.NET Framework创建一个在线商店原型。从昨天开始我把它的大部分源码学了一下,除了有许多收获以外,还对设计中的两处有些看法。 ? 首先是关于DataSet和DataReader的选用。iBuySpy为了尽可能提高应用程序运行效率,几乎完全采用了SqlDataReader(我还没看完所有的代码,不知后面是否有DataSet的应用)。但是有几处用上SqlDataReader以后我认为反而增加了应用程序、数据库服务器的运行负担,同时也增大了网络流量。比如在修改购物车信息这一部分(ShoppingCart.aspx.cs),iBuySpy把SqlDataReader绑定给DataGrid控件以呈现购物车中的物品信息。为了允许用户修改,iBuySpy增加了一个UpdateShoppingCartDatabase()方法来寻找已更新的信息,然后调用一个ShoppingCartDB逻辑类实例的相应方法来逐条删除或修改信息(在数据库服务器上执行)。这样,如果用户购物信息中修改或删除的不多还可以,如果数量一多便会造成数据库服务器大量操作,造成大量网络Round-trip,从而降低应用程序运行效率。我的建议是在这里以DataSet代替DataReader。在DataSet中执行全部用户购物信息的修改或增删,最后把DataSet一次更新至服务端。这样无论从节省网络流量、减少数据库服务器执行次数,还是降低应用程序开发复杂度都有很大改善。 ? 另外一点建议是有关于一个存储过程——OrdersAdd。它的部分源码如下: ? /* Copy items from given shopping cart to OrdersDetail table for given OrderID*/ INSERT INTO OrderDetails ( ??? OrderID, ??? ProductID, ??? Quantity, ??? UnitCost ) ? SELECT ??? @OrderID, ??? ShoppingCart.ProductID, ??? Quantity, ??? Products.UnitCost ? FROM ??? ShoppingCart ? INNER JOIN Products ON ShoppingCart.ProductID = Products.ProductID ? WHERE CartID = @CartID ? 这段代码片断的用意是把购物车里的每一条商品信息(包含价格和购买数量)写入OrderDetails表。注意其中单价信息(UnitCost)是从Products表中获得的。我的看法是,如果UnitCost列同时出现在Products和OrderDetails表中,那么它是完全多余的,应该通过Denormalization给予以简化。但是很明显的一点是,商品的价格是经常变动的,用户在购买商品的时候很可能会遇到一个与原价完全不同的价格。这个变化的价格应该反映在ShoppingCart中(用户在购物车里看到的就是打完折后的价格),而Products表中的应该是不变的原价。所以,上述代码的后半部分最好应该简化为: ? INSERT INTO OrderDetails ( ??? OrderID, ??? ProductID, ??? Quantity, ??? UnitCost ) ? SELECT ??? @OrderID, ??? ProductID, ??? Quantity, ??? UnitCost ? FROM ??? ShoppingCart ? WHERE CartID = @CartID ?也就是说,在这里没Products表什么事,因为只有从ShoppingCart中才开始反映变动的价格信息,而正是这一信息应该被最终确认在OrderDetails表中。...[阅读全文]

posted @ | Feedback (2) |

摘要:在我上一篇“MSBuild入门”(http://blog.joycode.com/musicland/posts/11833.aspx)中介绍了使用MSBuild生成.NET应用程序的基本方法。通过对该文的学习,想必您已经可以比较灵活地在项目开发中应用MSBuild了,那么从本文开始,我将介绍MSBuild的一项高级操作——自定义Task。 ? 什么是Task?为什么要自定义Task? 在上一篇文章中,您已经看到了几项Task的应用,其中包括Csc、MakeDir和Exec,你可以使用Csc Task来编译C#程序,使用MadeDir Task来创建目录,使用Exec Task来执行任何一项shell命令……可以说,只有这些Task才是MSBuild生成过程中真正执行预定义操作的部分,它们是MSBuild的灵魂所在。 ? 由此,您也就不难理解创建属于自己的Task的重要性了——当MSBuild自带的几个Task无法满足您的要求时,您就必须自己开发相应的Task以满足需求,而这篇文章的目的就在于演示如何开发属于自己的MSBuild Task。 ? 在进行下一步的讲解之前,请您先看下面的这份图示(摘自MSBuild Task Extensibility Specification,作者是AKipman, RGoel, SumedhK)来看清Task的来龙去脉: 通过图示可见,每个Task都是一个继承于Microsoft.Build.Utilities.Task(该类存在于MSBuildUtilities.dll中)的类,而Microsoft.Build.Utilities.Task继承了Microsoft.Build.Framework.ITask(存在于MSBuildFramework.dll中)接口,并且实现了Execute方法,该方法将是每一个Task执行不同预定义任务的主体。这样,我们如果想开发自定义的Task就必须遵循上述原则,直接或间接继承ITask接口,同时实现Execute方法。好,让我们来一步步实现吧。 ? Demo 首先我们要创建一个简单的Task——Message,它的用途是把传入的一个字符串显示在Console上,以方便与用户的交互。在NAnt中就有类似的一个名为Echo的Task执行同样的任务,在微软提供的几份MSBuild演示资料(请参考我在上一篇文章中给出的链接)也多处用到了Message Task,但在我得到的v1.2.30703.4版MSBuild中却意外地漏掉了这个Task,因此就让我们从它开始,演练一下基本的Task创建方法: ? 首先请创建一个继承自Microsoft.Build.Utilities.Task的类。从上面的说明可以看出,我们的Message必须直接或间接继承Microsoft.Build.Framework.ITask接口,而从Microsoft.Build.Utilities.Task类开始继承是个不错的主意。如果你用的是Visual Studio .NET,那么请创建一个C#类库项目,然后增加对\MSBuildFramework.dll和\MSBuildUtilities.dll的引用。如果你用的是其它文本编辑器,那么请记得在编译时增加对上述两个dll的引用。 ? 下面是Message类代码: ? using System; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; ? namespace Musicland.Build.Tasks ? { ????? public class Message: Task ????? { ????????? private string _text; ????????? [Required] ????????? public string Text ????????? { ????????????? get {return _text;} ????????????? set {_text=value;} ????????? } ? ????????? public override bool Execute() ????????? { ????????????? Console.WriteLine(Text); ????????????? return true; ????????? } ????? } ? } ? 注意在Text属性定义之上应用了一个[Required]属性,这就要求开发人员在使用Message时必须为Text属性设值,否则MSBuild引擎会报错。在Execute方法内,我们只是简单地把传入的Text属性值打印到控制台上,这也是这个Task的目标所在。 ? OK,把上述代码编译成Message.dll文件,然后把它复制在MSBuild.exe所在目录中(在我机器上是C:\windows\Microsoft.NET\Framework\v1.2.30703\ )。这样就引出了下一个话题——怎样注册Task。 ? 既然我们把自定义功能放在单独的程序集里(Message.dll),那么怎样保证MSBuild引擎在运行时能找到这个程序集并运行其中的类的方法呢?答案是注册这个Task。根据MSBuild提供的文档表明,至少有两种方法可以用来注册Task:全局和局部。顾名思义,在全局注册的Task可以被所有的项目生成文