“我常常在想,我应该写一本关于ATL如何工作的书。当我阅读了这本书之后,我觉得我已经没有必要再写这样的书了。”
—— Jim Springfield,ATL发明人,Microsoft Corporation
一年前我第一次看到这本书的英文版,抽时间阅读之后,我知道这是一本重量级的技术书籍。去年5月份曾经为这本书写过一篇长长的介绍(有一万多字),也曾拜托朋友在有关的电脑杂志上发表我的书评,竟然无人敢接。似乎高阶技术在国内总是不被看好,倒是初阶技术的资料大行其道,浪费和重复自不必说,问题是高阶技术也有广阔的市场,为什么商家就看不到呢?我这么说是有根据的,因为当我把书评放到Internet上之后,不断有朋友来问讯这本书的出版消息,很多读者希望看到这样的书。幸运的是,中国电力出版社引进了这本书,而我也有机会为大家翻译这本书,并再一次向您作介绍。
ATL是专门用于开发COM组件的模板库,当前的最新版本是ATL 3.0(随Visual C++ 6.0发布),尽管它已经有将近5年的历史了,但是在国内ATL并没有被普及开来,我想原因有多个方面。第一,ATL涉及到的技术比较复杂,首先它是用来开发COM组件的,而COM本身比较偏于底层,从而导致ATL的背景要复杂得多;其次ATL是建立在C++的模板(template)语法基础上的,所以要想掌握ATL,必须具备比较好的C++基础,而这一点并不是很容易就能做到的。第二,ATL的参考资料很少,虽然ATL的源码是完全开放的,但是源码背后隐藏着许多概念和思想,这些概念和思想并不是直接体现出来的,好的资料源可以帮助我们抓住这些要点,并用好ATL。毫无疑问,本书是最好的ATL参考资料。
本书不是一本讲述如何使用ATL的向导指南,它并不是简单地教您如何利用ATL中的模板类实现COM应用。相反,本书深入地讲解了ATL中模板类的结构、ATL如何实现COM的各种功能,并且也向您展示了ATL中大量的技巧,同时还指出了当前ATL中存在的一些错误和缺陷。虽然本书介绍的技术有相当的深度,但是作者的语言非常简练、表达清晰、结构安排也非常合理,所以全书读起来并不艰涩,期望中译本仍能够保持住这样的风格。
学习ATL需要具备良好的背景知识,同样地,阅读本书也需要具备必要的基础知识。概括起来,您需要以下几方面的准备知识:
(1) COM基础。因为ATL是针对COM的,所以扎实的COM知识可以助您一臂之力。COM书籍目前已经很丰富了,在阅读本书之前,您可以先找一本书来打打底。
(2) C++模板知识。如果您能够很熟练地运用C++,但是没有用过C++模板语法,那么您可以阅读本书的附录A,增加一点营养。
(3) Windows基础知识,特别是Win32中有关窗口部分的知识。ATL 3.0中有许多模板类涉及到窗口的概念,如果您想用ATL开发一些包含用户界面(UI)的应用,这些知识也是必需的。本书最后三章讲述了有关窗口、控制和控制包容的内容。
(4) STL知识。STL已经被纳入到C++标准中,并且成为C++标准库中最重要的一员。ATL与STL在数据集合(collection)管理上有相通之处,如果您有STL作基础,那么理解ATL中的集合和枚举器部分可以轻松自如。
在这些准备知识中,前三条比较重要,而且大多数C++/COM程序员都已经具备,所以这本书可以为他们进入ATL提供一条捷径。最后一条不是必需的,缺乏STL的知识不影响对于ATL大局的理解。因为目前并没有很好的STL参考资料(也许很快会有),所以读者可以自行斟酌决定。
最后,我要说明的是,这本书不是我一个人独立完成的。我和新语的合作方式不是“你译一部分,我译一部分,然后合起来交稿”,而是由新语完成全部初稿,再由我审阅全部的内容。书中的每一句话我都检查过,如果读者发现任何翻译错误,责任都在我,敬请原谅。
潘爱民
2001年2月于北京大学燕北园
ATL之深入解析
——介绍一本关于ATL的书《ATL Internals》
潘爱民,5月7日,2000年
北京大学计算机研究所,100871
引言
面对计算机图书市场的繁荣景象,我经常感叹今天学习计算机开发技术的同道们是多么幸运。十年前,我们学习计算机语言非常不容易,要掌握各种开发工具只有靠自己的摸索和极少量的参考手册。我记得,94年我学习Visual C++和MFC的时候,基本上只有靠软件自带的联机帮助;现在情形大不同了,书店中的计算机图书琳琅满目,关于Visual C++和MFC的书籍尤其多。有几位编辑朋友劝我写一点这方面的书,我觉得不大有必要了,因为Visual C++的好书已经不少了,适合各种读者层次的书籍几乎都可以买到,而且有一些书还相当不错。不过,在98年的时候,我感觉关于COM书籍实在太少,几乎没有,于是我下定决心,自己写一本关于COM的书籍,在99年底的时候由清华出版社出版。很快地,关于COM的书籍已经很多了,有些国外的名著也相继引入国内。从这十多年的计算机开发技术历史来看,一门技术只要有很多的书籍来介绍,那么这项技术很快就会普及,否则就难以推广。
说到COM,相信在Windows平台上有过开发经验的朋友一定接触过,它是Windows操作系统的基本软件模型,从93年建立以来,为Windows平台的推广和发展做出了不可磨灭的贡献,而且其自身还在不断发展。但是要真正开发COM组件并不是很轻松,在Visual C++中,我们既可以使用MFC也可以使用ATL。MFC完全面向Windows应用,它用C++的封装技术建立了一套适合于开发Windows应用的C++类库,虽然在后期的版本中MFC提供了大量的COM支持,但是从基本的设计结构上讲,MFC不适合于开发专业的COM组件,它适合于在Windows应用的基础上提供相应的COM支持。
ATL则不同于MFC,它完全面向COM组件,其技术路线也不同于MFC,MFC使用的是C++中的继承、封装、嵌套等常规技术,而ATL使用了C++中模板、多继承等高级技术,甚至还用到了STL。所以学习和使用ATL要求我们必须熟悉这些C++高级特性。另一方面,ATL结构完全针对COM中的诸多规范,这就要求使用人员必须非常了解COM规范,才有可能真正把ATL用好。
虽然目前关于MFC的书籍很多,但是完全介绍ATL的书籍非常少,甚至根本没有,这不能不说是一个遗憾。我有幸在今年2月份看到一本ATL的英文原版书《ATL Internals》,本文将为大家介绍这本书。
在看这本书之前,我对ATL已经有了基本的了解,98年底由于写作的需要,我曾经读过ATL的部分源码,对于ATL的基本结构还算清楚。我刚开始看到这本书的时候,快速读了一章,并没有感觉这本书有多好,后来由于工作忙碌的原因,一直没有得闲,直到最近,我才仔细把这本书读了一遍,感觉这是一本不可多得的好书。以前我很少仔细阅读开发技术类的书籍,一则是由于自己读书太慢,二则是往往开发技术类的书籍不大值得精读。但是这本书我读得很仔细,因为这本书把ATL的精华几乎全表述出来了,ATL中的许多内容都能让你为之心动,作为一个程序员,这也是一个学习和提高的机会。
我写这篇文章的意图不仅仅是向读者介绍这本书,我也希望能够把我在阅读过程中的心得与大家分享。同时我还希望能够按照这本书的路线,向大家介绍一下ATL的结构和机理。
准备阅读
《ATL Internals》由Addison-Wesley出版社出版,作者为Brent Rector和Chris Sells,出版时间为1999年2月,全书600多页。关于这本书的背景知识可能对于许多COM迷和ATL迷来说很有意思。首先,这本书的序言是由ATL的发明人Jim Springfield所撰写,在序言中,Jim介绍了ATL的历史,对于书中所介绍的内容大加赞赏,称赞“阅读此书可以学到许多阅读源码所不能掌握的内容”,在序言的最后,Jim还谈到了ATL的将来。
其次,在作者写的自序中,他们提到了ATL离不开COM,要想掌握ATL,就一定要先掌握COM。ATL是一个产生C++/COM代码的框架,就如同C语言是一个产生汇编代码的框架,这个观点颇为新颖,仔细想来,确是如此。作者特别推荐了Don Box的书《Essential COM》,其实在英文书中,COM书籍不少,这显示出两位作者与Don Box的关系非同一般。我记得Don Box也曾经推荐过《ATL Internals》这本书,如果读者有机会到亚马逊网上书店(www.amazon.com)看看本书的书评就可以知道他们之间有很亲密的关系,其中作者之一Chris Sells与Don Box以及另外两人合作写了一本COM的书《Effective COM》(本书文中有多处推荐了这本书)。我相信他们的互荐是基于相互之间绝对了解的基础之上的,他们都是COM顶尖高手,也是ATL顶尖高手,读者经常可以在MSJ(Microsoft System Journal)杂志上看到他们的文章。
诚如作者所言,阅读此书需要极强的预备知识,按照我阅读此书之后的理解,读者在阅读此书之前应该有以下几方面的准备知识:
(1) 一定要懂COM。ATL完全针对COM,许多细节都是为了更好地实现COM而设计。如果读者仅仅看过《Inside COM》(清华大学出版社出版的《COM技术内幕》),那么要想通篇阅读《ATL Internals》还不够,建议读者再找其他的资料看一看。
(2) 一定要懂C++的模板技术。ATL充分发挥模板的优势,其整个体系结构完全建立在模板的基础之上,如果读者不熟悉模板,那么几乎无法阅读ATL。
(3) 基本了解STL(可选)。ATL的集合类和枚举类用到了STL,当然,如果读者不懂STL,基本不影响全局的理解,但是STL中容器的思想和COM集合对象的思想是相通的,ATL把两者有机地统一起来了。
所以说,《ATL Internals》是一本起点很高的书,原因在于ATL是一门起点很高的技术。据我所知,现在有许多程序员已经在使用ATL了,这是好现象,说明我们国内的程序员水平相当高,虽然我们的资料信息还不够丰富(至少对于ATL而言是这样),但是我们仍然紧跟这些新的技术。尽管如此,要想真正用好ATL,一定要了解ATL的机理,这不同于MFC的情形。假如我们不懂MFC的机理,一样可以做出很好的程序,利用MFC,在不懂OLE细节的情况下,一样可以提供OLE的支持。ATL要求我们很细致地调节它的类,根据需要选择合适的模板类,必要时还要修正它的行为。ATL尽管已经到了3.0版本,但是仍然有不少的错误,这本书已经指出了一些错误,但我相信肯定还会有更多的错误,这对程序员提出了更高的要求,确实是这样,因此结论是:使用ATL一定要懂ATL!
尽管我这样说,但我还是认为ATL是一项好技术、是一个好的COM模板类库。而且我也深感好书对于ATL程序员的重要性,有些东西是不能从源码和参考手册获得的,既然我看到了这本好书,那我应该把这本书介绍出来,让大家知道这本书。也让大家分享我的体会。如果有那家出版社能够引进这本书的话,则是我们广大ATL程序员的福音了。
内容介绍
下面我按照《ATL Internals》的叙述顺序,逐项介绍ATL的内容,希望读者不仅能够了解本书的内容,也能够借此了解ATL的机理。原书共包括十一章,我按照每一章所介绍的内容把这十一章分成四个部分。
(原文有1万字左右的内容介绍,此处略。)
结束语
对于COM应用的开发,ATL无疑是首选的工具,与MFC相比,ATL的规模还不算大,但是从上述的介绍我们可以看出,ATL涉及到了COM的方方面面。实际上,ATL的内容还要多得多,比如OLE DB的支持、MTS的支持等,尽管如此,如果我们有了这本书中的内容为基础,那么再去学习这些扩展的内容就会容易得多,结合ATL中实现COM的基本手段加上这些应用技术的背景知识,我们可以很容易地掌握这些开发技术。
但是如果我们要想熟练掌握甚至精通ATL的话,那么这只是一个开头,前面还有漫长的路要走。原因有多方面,一则COM本身异常复杂,不下苦功难窥全貌;二则ATL确实奥妙很多,它体现了C++语法的博大精深;三则ATL还存在很多错误,虽然本书作者指出了一些错误,但实际的错误肯定更多,这就对ATL使用者提出了更高的要求,如果使用过程中不能发现这些错误或者避开这些错误,那么用ATL反而会阻碍我们的工作。
虽然ATL比较精深,但是这本书的讲解非常通俗易懂,语言比较简练,条理非常清楚。即使在读完这本书之后,它仍然可以作为参考书指导我们的开发和学习工作。我想,这就是好书的价值所在吧。
序
当我第一次看到这本书的标题的时候,我告诉Chris Sells,这本书听起来正像是我一直想写的那本书。虽然我们已经发布了ATL,但是我们当中常有人这样说,“我想我应该写一本书来讲述ATL是如何工作的”。在读过“深入解析ATL”(ATL Internals)之后,我不再认为我们还有必要写这样的书了。实际上,这也是一种解脱的感觉。在这里,我认为这本书已经涵盖了ATL绝大部分的内容,而且它也提供了关于ATL内部知识的绝好的信息源。所以,Chris请我提供一些无法从ATL源代码推断出来的信息。
ATL历史简介
我第一次接触模板(template)是在1995年下半年,当时我是MFC项目组的开发人员。当时,项目组的一位朋友正在为Visual C++产品对各种STL实现进行评估,他向我谈起了许多关于模板的信息。我开始玩起了模板技术,但是真正做的并不多。很快地,VC项目组分出一个企业项目组专门集中于Visual C++ 4.2企业版(VCEE),这是我们的第一个VC企业产品。我被调动到这个项目组中,负责VCEE中库(library)的工作。当时我们研究了好多个不同的想法。然后Microsoft Transaction Server项目开始启动了,我们与他们讨论了许多关于COM、事务(transaction)、数据库和中间件业务对象的话题。很快地,我们意识到我们需要一种更好的机制,用于在C++中创建COM对象。Jan Falkin和Christian Beaumont当时正在为我工作。Jan在致力于连接ODBC数据源的自动化接口,Christian正在致力于基于模板的ODBC数据源访问方法(现在的OLEDB消费者模板的前身)。而我正在致力于COM基础设施,因为在那以后一切都将转移到COM上来。
刚开始,我只是玩一玩COM和模板,但是渐渐地有一些很重要的概念稳定下来了。开始时我打算支持所有的线程模型,但是我并不打算为此付出代价,除非是真正需要这些线程模型。对于聚合也同样如此。我不希望任何人都有不使用ATL的理由。(我不希望听到“是的,ATL很酷,但是如果我自己来做的话,我可以省下大量字节。”)所以,当我们必须要做出选择的时候,性能和灵活性比易用性更为重要。
ATL 1.0
由此我们推断出的一个概念是,用户编写的类并不是真正被实例化的类。这就使得许多优化措施成为可能,而这些优化在其他情况下是根本不可能发生的。其他一些概念包括针对接口的多重继承、“创建者”函数和数据驱动的COM映射表。我们开始展示这些概念和机制,并获得了大量的正面反馈。有些人认为我们应该尽可能地把这些概念介绍给客户,所以在96年初夏的时候我们决定发布到Web上(RTW,Release to the Web)。这就是ATL 1.0。
我们这套库的工作名字曾经是MEC(Microsoft Enterprise Classes),但是市场人员认为我们应该起一个更能反映出我们所做的工作的名字。由于我们的焦点集中在COM上,并且在当时的市场环境下,几乎所有的产品或者技术都被冠以“Active”,所以我们选择了活动模板库(Active Template Library)。
ATL 1.1
我们的ATL得到了客户很好的接纳,于是在96年夏季结束的时候我们发布了ATL 1.1。在那时,Jan和Christian已经开始直接工作于ATL。ATL 1.1修改了一些错误,并且支持更多的特征,比如连接点(connection point)、NT服务、RGS注册支持和安全性。
ATL 2.0和2.1
在ATL 1.1之后,我们开始ATL 2.0的工作。它的主要目标是能够创建ActiveX控制(ActiveX control)。Jan和Christian在这上面做了很多工作,而我的工作仍然集中于核心的素材(比如重写了连接点部分以便使它们更加小巧)。当时Nenad Stefanovic也加入到我们组里来,他开始从事ATL的窗口支持工作,以及在VC 6.0中对于复合控制的支持。我们最初计划在Web上发布针对VC4.2的ATL 2.0。然而,我们的计划改变了,我们决定在VC 5.0(12/96)中发布ATL 2.0,并且在Visual C++ 5.0的Alpha版本中发布ATL 2.1。ATL 2.0和ATL 2.1之间唯一的区别在于,ATL 2.1为Alpha、MIPS和PowerPC修改了一些错误。同时我们也在Web上发布了ATL 2.1,为VC 4.2增加了AppWizard和ObjectWizard支持。
ATL 3.0
在ATL 3.0(当时被称为 ATL 2.5)上工作了几个月之后,Christian和我离开了一段时间,Jan接管我们的工作,成为ATL的负责人。几个月之后,我们又回来了,Christian成为ATL的负责人,而我则继续研究Visual C++其他的技术和支持,但我仍然时不时地进入到源代码中。
ATL的将来
我们在98年6月份发布了VC 6.0,当我写作这份序言的时候,我们正在从事下一个版本的开发工作。期望在ATL中看到更多既酷又新的内容,以及获得ATL功能的新方法。我非常高兴看到ATL继续向前发展,同时又能保持最初的目标(产生小巧的、高效的代码)。所以,我建议读者看一看这本书,学习一些新的技巧,从而对于ATL如何工作有深入的理解。
Jim Springfield
ATL发明人
Microsoft Corporation
前言
C语言是产生汇编语言代码(ASM)的框架。大多数ASM程序员在转移到C的过程中,要花费大量的时间来检查和替换编译器产生的代码,因为他们不相信编译器能够产生正确的代码。随着时间的流逝以及编译器变得越来越好,ASM程序员渐渐地认识到,他们可以信任编译器能够产生不仅正确的代码,而且也是高效的代码。当然,聪明的C程序员永远也不会忘记编译器在产生ASM代码。他们知道,当他们需要超越语言本身的能力的时候,他们仍然能够到达指定的区域并且控制被产生的代码。
Microsoft的Active Template Library(ATL)是一个产生C++/COM代码的框架。大多数C++程序员在转移到ATL的时候,都非常害怕检查(更不用说替换)ATL产生的代码。他们根本不信任ATL,比ASM程序员不信任C编译器有过之而无不及,因为对于初窥ATL的新手来说,ATL的源代码很是令人费解。然而,ATL为C++/COM程序员完成了同样的服务,就如同C编译器为ASM程序员完成了同样的任务一样;也就是说,ATL提供了一种更简单的方法来产生正确的、高效的样板代码。当然,聪明的ATL程序员永远也不会忘记ATL在产生C++/COM代码。当他们需要超越ATL本身的能力的时候,他们仍然能够到达指定的区域并且控制被产生的代码。
本书就是为正在转移到ATL 3.0(由Visual C++ 6.0提供)的C++/COM程序员而写的。因为ATL是建立在一组假设的基础上的,所以要想成为一名有效率的ATL程序员,您不仅需要理解ATL是如何建立起来的,也需要理解ATL为什么要这样建立起来。当然,为了理解ATL为什么是这样的,您必须要理解ATL开发所在的环境,也就是COM。本书并不把所有必要的COM知识浓缩到一章或者两章,而是假定您已经知道了COM,并向您展示ATL的设计、用法和内部要点。如果您希望在深入ATL之前先充实一下COM知识的话,则Don Box的“Essential COM”(Addison-Wesley,1998,其中文版“COM本质论”已经由中国电力出版社引进,潘爱民译)是您获得COM知识的一个很好来源。
除了第一章之外,本书从ATL的最底层一直到达最高层次,每一章都建立在前面章节的内容的基础上。第一章是一个简短的描述,讲述了有关ATL的一些常见用法,以及使用过程中涉及到的一些向导(wizards)。然而,在第一章中,一旦讲解过程中涉及到细节问题,我会向您指出后面哪一章提供了进一步的深度讲解。第2章到第5章展示了ATL的核心。第2章讲述了ATL智能类型,比如CComPtr、CComQIPtr、CComBSTR和CComVariant。第3章讨论了ATL中如何实现对象,焦点集中在当我们实现IUnknown时我们所拥有的大量选择方案。第4章讨论了一些粘合代码,当我们从COM服务器中暴露COM对象时这些代码是必需的。第5章再次深入研究了IUnknown的实现,这一次的焦点集中在如何实现QueryInterface上,并展示了一些技术,诸如tear-off接口和聚合等。第6、7、8三章讨论了一些COM预制的标准接口的实现,ATL提供这些接口实现以便支持永久性、连接点、COM集合和枚举器,每一章讨论一个话题。不管组件是否提供自己的用户接口,它们往往会利用这些标准接口来提供相应的服务。另一方面,第9、10、11章焦点集中在建立独立的应用和用户界面组件上。这些章节分别讲述了ATL窗口类、控制(control)和控制包容(control containment)。ATL源代码之所以难以阅读,是因为它大量用到了模板的高级用法,附录A提供了一组例子,用来展示如何使用模板以及如何建立模板。如果您以前曾经阅读过ATL的源码,并且对于“为什么要把派生类的名字传递给基类模板”不太明白的话,那么您会发现附录A非常有用。附录B提供了重要的ATL类的列表,以及声明这些类的对应头文件(通常也包含这些模板类的实现)。
在写作这些章节的时候,不仅显示图表和示例用法代码很有必要,而且展示内部的ATL实现代码也很有用。实际上,本书通常会成为您阅读ATL源代码的向导指南,所以您必须要区分哪些是作者编写的代码,哪些是Microsoft雇员产生的代码,我们采用下面的约定:
// This code is author-generated and is an example of what you’d type.
// Bold-faced code requires your particular attention.
CComBSTR bstr = OLESTR(“Hello, World.”);
// This code is part of ATL or Windows.
CComBSTR(LPCOLESTR pSrc) {m_str = ::SysAllocString(pSrv); }
因为ATL开发小组并没有按照图书出版的形式来编写它们的代码,所以通常有必要对ATL代码重新格式化,甚至对原来的ATL代码作一些简化。我尽了很大的努力来保持源代码的本质,但是,不管怎么样,ATL源代码总是最终的标准。当您跟踪ATL类的实际实现代码时,您会发现附录B非常有用。
如果您发现在本书的例子代码或者其他部分有任何错误,您可以与Brent Rector(http://www.wiseowl.com)和Chris Sells(http://www.sellsbrothers.com)联系。本书使用的源代码可以通过http://www.wiseowl.com/ATLInternals.htm获得。
作者感谢所有为本书做出贡献的家人。虽然没有他们的努力,这本书也有可能完成,但是无论如何它不可能是现在的样子。Chris要感谢它的妻子Melissa以及他的儿子John和Tom,感谢他们在无数个夜晚和周末陪伴他写作。Chris也要感谢Brent Rector,让他加入到本书的写作工程上来。
Brent要感谢他的妻子Lisa以及孩子们Carly和Sean,因为他们耽搁了本书的提交日程。如果不是因为他们,他有可能连续几天不会离开计算机。Brent也要感谢Chris的聪明才智、耐心和他好看的相貌。
Brent和Chris要感谢许多朋友。特别感谢Christian Beaumont、Jim Springfield、Walter Sullivan和Mark Kramer,因为他们要承受两位作者不停的提问,他们总是花时间来回答这些问题。也要特别感谢Don Box,因为他的MSJ ATL特写,这些内容影响到了有关的课程,并进而影响到本书。感谢下面的各位评论家:Don Box、Keith Brown、Jon Flanders、Mike Francis、Kevin Jones、Stanley Lippman、Dharma Shukla、Jim Springfield、Jeff Stalls、Jaganathan Thangavelu和Jason Whittington。特别感谢Dharma,因为他的特殊的、具有教育意义的评论。感谢DevelopMentor的朋友,讲师Fritz Onion深入钻研了ATL的控制包容。感谢以前的一名学员Vladan Vidakovic,因为他鼓励Chris深入钻研HTML控制。感谢Tim Ewald、Jim Springfield和Don Box,因为他们帮助开发了传递夹层(forwarding shim)技巧。感谢ATL和DCOM邮件列表的成员,特别是Don Box、Tim Ewald、Charlie Kindel、Valery Pryamikov、Mark Ryland和Zane Thomas。最后(但不是最不重要)感谢Addison-Wesley,特别是J. Carter Shanklin和Krysia Bebick,因为提供了我们实际想要写作的环境。
我们也要感谢所有曾经仔细阅读了本书以前的印刷版本并发现了小错误的读者,这些错误在这个版本中都已经被改正了。这样的读者有:Elias Andersson、Ruben Bartelink、Vince Blasberg、Yao Cai、Yun Chang、Dan Corpron、Darron Devlin、Matevz Dolenc、Jerry Dungee、Jonathan Flanders、Nigel Foster、Michael Getfen、Thomas Goering、Khurram Haroon、Robert D. Herrick、Felipe Hervias、Robert Hill、Alan Kang、Yu-Ming Kao、Roland King、Adam Krumholz、Eric Lamontagne、Tasch Matthias、Godson Menezes、Jeffrey Metos、Hieu Nguyen、A. Nonymous、Michael Nyman、Joe O’Leary,Alan Parker、George Reilly、Kwak SeoungTae、Vivian De Smedt、Dan Sullivan、Jonathan Sun、Jeroen Thur、Alan Williamson和Ochi Wu。特别感谢Ravi Singh,因为他建议改进BullyEye控制,以便它可以被用于更为广泛的包容器环境中。
