2006年06月28日

Writen by interma@BMY ( interma@stu.xjtu.edu.cn )
2004.12 初稿
2005.6修订
声明: 以下所有信息都是本人自我感悟,因此难免有疏漏和错误之处,欢迎大家指正。
同时这也是面向DotNet新手的,各位大牛们感到太肤浅的话,不看也罢。
一. 什么是DotNet,为什么要学习DotNet:
[1] 什么是DotNet:
首先我先给出微软对DotNet定义:
“Microsoft.NET 是 Microsoft.NET XML Web Services 平台。XML Web Services 允许应用程序通过 Internet 进行通讯和共享数据,而不管采用的是哪种操作系统,设备或编程语言。Microsoft.NET 平台提供XML Web Services 并将这些服务集成在一起,为个人用户的好处是无缝的,吸引人的体验。”
但这只是官方的定义,对于我们来说DotNet只是一个为了实现上述目标的技术集,其中包含未来几年内可能应用到的先进技术,它的出现会使开发人员更容易更好地开发面对未来的复杂应用。
[2] 为什么学习DotNet:
在这里首先打一个总的基调,那就是:DotNet经过近4年(出现于2000年)的发展,它已经不再是那个稚气未脱的孩子,人们已经不再关注是否要学DotNet,而是关注于什么时候开始学习DotNet!
下面我将给出我的学习原因:
(1) 它本身的魅力:
一个技术对大家影响最深,令大家印象最深刻的应该在语言层面,这本小节中,我主要说明DotNet中的语言魅力。
DotNet强调的是多语言单平台。它其中的语言种类繁多(C#,VB,C++,J#,Delphi, Python等),这便给了程序员们极大的自由,由于这几乎覆盖了所有的流行语言,因此无论你以前是使用的语言,你都可以很快的过渡到DotNet上来,让你以前的辛苦没有白费。
DotNet中的语言是面向构件(组件)编程的典范。在这里我看到了语言层次的进步,C那代语言是面向过程,C++那代是面向对象,而到了DotNet则是面向构件。
在这一代又一代语言发展中无不体现出一个特点(包括近几年流行的动态语言):那就是对问题域的抽象越来越容易,越来越精确,越来越以人为本。随着计算机硬件性能的提升,性能已不是唯一的关注点了。
DotNet中的语言具有优美性。这在里我主要是指C# ———- 这个没有历史包袱的语言。在看C#的语言特性中我看到了美丽,它没有了那些面向过程的乌七八糟的东西,模式和重构都能随心应手的使用,轻量级的开发过程和相应的工具也能做到有的放矢。这无疑和C# 优良的语言特性不无关系,而且我还看到了它的轻快的发展脚步,C# 2.0中的范型(引自C++)和匿名方法(引自Java)等语言特性的出现,无不体现它的美好明天。
DotNet中的语言和基类库(BCL)简单易学。这个我在版上同很多人交流过,但是结果我感到非常遗憾,大家对它的这个特点很不认可,多数表出“DotNet太简单了,没什么可学的”的态度,关于这个问题我在[FAQ]中会进行说明,在这里先给出一个观点:简单易学决不是缺点,只能是优点。
(2) 未来的形势:
因为未来的不确定性,因此在这里我不多说,只谈2点:
DotNet中的技术都是当前或未来几年内的技术热点,这些技术在未来几年内有很大的用处。
Windows的下一代平台Longhorn将用DotNet Framework作为基础来取代现在的Win32 API,也就是说托管API在将来将大行其道,Win32API只会变成2等公民,将来Longhorn下的应用大多都将用托管代码写成。(修订注:最新的MS路线图表明Longhorn不会完全采用托管API,但是托管API一定会比现在的XP中的那个要强得多)
顺便说一个不可不提的事情,那就是微软在将来一定会大力推进DotNet的应用,从最近的Imagine Cup中你看到了什么?(提示一下: 那就是所有编程语言都必须使用DotNet系列语言之一)
(3) 更好的解决问题:
在这里我只强调一个问题,这个是编程的根本:就是为了解决实际问题。
无论某个技术是多先进,还是多么烂,只要能解决实践问题就very well。我们无需卖弄自己采用的技术有多先进,也无需阐明有过高深,多奇妙,我们要的只是解决问题 ————— This is our final target。
而恰恰我看到了DotNet的解决实际问题的强大能力:从桌面系统到企业应用,无所不能。(当然,我不是说无所不精)这个我从很多朋友们那都有所了解,他们不是搞DotNet的,但是他们拿DotNet做过项目!
它让我们能高效地解决实际问题,这就是我们选择它的原因。
[3] 小结:
不知道看了上边的文章,你对DotNet产生了学习兴趣没有。
如果有的话,那我非常高兴,因为你在学习它的过程中一定会找到编程的快乐。
如果没有,也没关系,那应该是我的表达能力不好,没有说出DotNet的迷人之处,你可以在其他开发社区中,看看其他人的观点,再作出你的决定。
如果你已经有了兴趣,那让我们看看应该怎样学习它吧。进入第2部分。
二. 学习路线:
在下边的每个环节中我都会推荐几本书,如果是我没看过的,我会说明的。
因此大家不用担心书的质量。(我以下都未说这些书的作者和出版社,因为这些书我们学校的图书馆一般都有,不会弄混,如果你未找到,请和我联系,我会告诉你它的详细信息)
[1]初始阶段:
目标:首先你应精通一门DotNet语言(包括Asp.net开发人员,我总感觉Asp.net开发人员的基础好象不扎实)
具体实现:建议使用C#,因为这是为DotNet量身定做的语言,它没有历史包袱,最能体现出CLI的特性。如果熟悉VB6同时特别喜欢VB的话,可以选择VB.net。(不过我猜你最后肯定也会C#了)
在这里,你的第一本DotNet语言学习书籍没有过多的限制,中国的外国的都行,不过记得别选太厚的。
之后你将看到一本必读书:《.NET Framework 程序设计》(修订版),不多介绍了,这本书必读,否则你就不叫懂DotNet,这本书的有些内容你可能有点看不懂,不过没关系,随着你以后的深入,你就会明白了。
成果:你这时只需比较了解语言特性,不必编出任何程序。
[2]提高阶段:
目标:可以开发程序了,You are a programmer now!
具体实现:在这里我们要区分一下,分为桌面开发人员和网站开发人员(Asp.net)。
对于网站开发人员(Asp.net),我在这里向你们推荐的Asp.net入门书是:
《Asp.net 揭秘》(电力出版社)和《Asp.net权威编程》(电力出版社),这2本书非常不错,很系统的介绍了Asp.net 1.1的种种特性,只不过有点厚。 唉,在这我要说一句很遗憾的话,“对于Asp.net开发人员,我能对你的帮助到此为止了”,我不熟悉这个领域,但是在这阶段之后,你要多做一些项目,这会对你的开发水平有极大的提高,记住,一定要做项目,边作边学。在这之后,你的水平会有显著的提高,你那时应该可以知道自己的下一步该怎么走了。(顺便说一句:yboys@BMY是个牛人,去问他吧。)
对于桌面开发人员,在本阶段,你们要做的和网站开发人员差不多。先推荐一本关于Winform的书籍,这个有很多,建议看《Winform 高级编程》和《Windows forms 程序设计》(此本我未看过),这2本都有点厚,时间不多的话,选若干章节看就行了。当然选其它关于Winform书也行,不过推荐外国人写的。
之后就去开发桌面程序吧,从小到大,逐个深入,在这里你主要是熟悉DotNet的基类库和各种可视化控件(你的课程设计就可以用DotNet搞), 为将来打好基础。
成果:你已经能开发出具体的小项目了,呵呵,在同学的眼里,你已经算半个编程高手了。
[3] 提高阶段:
目标:你要变得professional 。不过这个阶段有点漫长。
具体实现:我们要对一些关于DotNet的基础知识和开发经验进行一下复习与总结了,同时也可以搞懂你在《.NET Framework 程序设计》中不懂的问题。遗憾的是,DotNet现在还没有这种书籍,不过没关系,我们要到Java那里找些帮助:《effective java》和
《practical java》,呵呵,放心,你绝对看得懂。
接下来,你要了解一些开发准则了,首先Clear你的代码,让它变得美丽,这是一个Programmer的基本素质,推荐那本《高质量程序设计指南—-C/C++语言》,相信这本书你不会陌生,这里边讲到了一些作为programmer的最基本素质,无论是编码还是做人。
下面2本重量级的书出场了,《设计模式》(我未读过)和《重构》,软工双杰的称号不是吹出来的,呵呵,开始吧。不过,我推荐看《C#设计模式》,因为那本《设计模式》据说太过抽象,很难看懂,并且里边还是用C++描述的,不适合初学者阅读。看完之后还推荐一本《设计模式精解》,书如其名,相信可以给你带来新的感受。
在这同时你还应了解一下软件工程的知识轻量级的软件过程方法(如:XP,TDD等敏捷开发方法),注意:只是了解,不求深入。
至于传统软工的冬冬,随便找一本介绍那些的书看看就行了,只要看完可以基本看懂一些简单的UML图就可以了,之后推荐看一本《java程序员UML手册》就行了,这本写得很好。
看到这里,你可能会疑惑,难道对软工的知识如此轻视吗?就看这几本的书就行了?
当然不行!但是你要明白软工知识必须要和具体的开发能力和项目经验相结合才有好的效果。对于我们学生来说,根本没经过大的,规范的软件项目的熏陶,因此根本无法深入软工这个领域,因此软工的知识等你工作了或上研了,在深入也来得及,因此现在只要求有所了解,不求深入。
归根结底,本阶段是要提高你的Programmer素质和代码控制能力。不过,在学完这些之后别忘了开发一个大一点的程序检验一下你的所学阿。
成果:你已经变得专业了,可以写出几千行(<5000)高质量的代码了。(顺便说一句,如果你现在刚好本科毕业,应该很抢手的。)
[4]深入阶段:
目标:学习DotNet的高级技术
具体实现:关于DotNet的高级技术,有很多方面(太多了),其中有很多我们可能到工作阶段才能够深入了解。但是现在了解一些不无好处, 我在下面只列出了很少的几个,你可以从中选择几个学习。其中应该会有你已经熟悉的技术了,这是很正常的,本阶段不像开始的那几个阶段,比较灵活,它的开始和结束具有灵活性,由你进行控制。
(1)线程操作:
这是提高软件并发性的基础,无论是桌面开发还是网站编程,相信你都会遇到它。强烈建议打好基础。在这里DotNet下还没有本专题的好书,只能推荐这本《C#/VB.net线程手册》,不过此书正如其名,它只是带着你将托管线程的类库浏览了一番,关键的东西都未提到,因此你只有到网上看一些有关线程的资料了。不过在这里,我又要拿Java出来了,《Java线程编程》(我未看过),这本Java线程方面的经典之著应该不会让你失望。
(2)网络编程:
这个重要性不说了吧,就是套接字那套东西。不过这可不是令人头疼的Winsock了,DotNet对Winsock进行了包装,使其变得好用很多。强烈推荐:《C# 网络编程》,它非
常系统的介绍了网络编程的原理和方法,看完之后,编个QQ或Serv-U应该不成问题,总之不容错过。
(3)数据库编程:
这个好没意思阿,我也不大懂,又不能帮你了。基本的就是一些数据库sql 语句和存储过程,同时还要对常见的DBMS有一些了解就行了。如果想在这个领域深入的话,那就要熟悉某种DBMS的性能特点,同时提高数据库分析和设计的能力。
(4)CLR和DotNet底层知识:
学了之后短期内应该没用,不过如果像我一样待着没事的话,看看DotNet的内部运作机理还是挺有意思的。推荐:《高级.NET程序设计》和《.NET本质论》(此本有难度)。
(5)分布式:
它就是.NET Remoting,这是非常有用的一种技术,我也在学习中。推荐《.NET remoting 技术手册》,不过还要到网上多看别人的例子。(呵呵,让我们忘掉复杂的COBRA吧)
(6)Web Services:
这是将来的基础应用平台,现在应用的也很广泛。我对这个知识了解一些基础的知识。
没法推荐高深的,只推荐一本O’Reilly的《.Net Web服务》,是本很好的入门教材。
(7)Pocker PC和Smartphone:
热门领域,不过同样也没什么书籍可以参考,同样我也没法帮你,到网上找找吧。
成果:你现在对这几个DotNet的高级技术都有了了解,将来(应当是工作时了)你应该会精通其中的某几项技术,这是你就是DotNet技术专家了。当然,别忘了,学习完这些技术后,编几个东西熟悉一下啊。
[5]superman阶段:
目标:提高系统分析和设计的能力,提高OOA/OOD的能力,还有着众多的项目经验。
同时还对DotNet中各项技术都有了新的认识,不再拘泥于技术细节,能做到在最恰当的场合应用最合理的技术(不一定是最先进的技术),同时还能对将来的技术趋势做出自己的分析和把握。
过程:。。。。。。
成果:我该怎么称呼你呢?我亲爱的DotNet架构师。
[6]小结:
我已经将我认为的DotNet开发人员的成长路线展示给你了,你一定会有自己的主意了,希望你能在学习它的过程中找到快乐。
在第3部分中,我将列出网上的一些参考资源,供你参考之用。
三. 参考资源:
在这里我只推荐很少的一部分资源,但因为我们的时间和精力都有限,这些已经足够你
成为高手中的高手了。
[1]开发工具及相关下载:
(1) 我的ftp :
202.200.238.199 user: guest pass: guest
在这里有绝大多数的开发工具(当然包括DotNet), 我同时也会经常更新。在你找某个开发工具时,可先来这里看一下。
(2) MSDN下载:
MSDN开发中心下载(中文):
http://www.microsoft.com/downloads/search.aspx?displaylang=zh-cn&categoryid=10
MSDN Download and Code Center (英文):
http://msdn.microsoft.com/downloads/
这是微软的官方站点,英文的资源多一点,如果在中文站点没找到,可去英文的看看。
[2]开发社区及学习资料:
(1) CSDN:
首页:
http://www.csdn.net/
CSDN技术社区[C#]: http://community.csdn.net/expert/forum.asp?url=/Expert/ForumList.asp?roomid=5201&typenum=1&xmlsrc=&whichpage=1
CSDN技术中心[DotNet文档列表]:
http://dev.csdn.net/articlelist.aspx?c=14
CSDN就啥都不说了,号称亚洲最大,其实啥人都有,别看花眼啊。
(2) MSDN [中文网站]:
http://www.microsoft.com/china/msdn/
基本都是精品,包含DotNet的各方面信息。
(3) 微软新闻组:
news.bentium.com (用OE上)
通过奔腾转信,进入: Microsoft.public.cn.dotnet.language.相应的语言。
其实就是对应微软社区。有很多的MVP帮你,感觉比CSDN强多了,也许还能碰
到clear和imcc呢。
(4)
博客堂:
http://blog.joycode.com/
博客园:
http://www.cnblogs.com/
这2个有关于DotNet的新闻和技术动态,以较为实用的工程技术知识为主。
[3]其它:
(1) SourceForge:
http://sourceforge.net/
这个应该不必说了吧?大名鼎鼎的sf.net 。
现在DotNet的开源项目也有很多了, 学习他们优秀的代码设计也是必不可少的一项课程。
进入其中的Software Map 之后,选择某种DotNet语言,就能看到相应的项目了。
(2) ME:
在你没成为高手之前,我也许能帮上你的忙,呵呵,欢迎你与我交流。(QQ: 165194384)
四. 其他:(FAQ):
这些FAQ,都是我曾经扪心自问的,其中不涉及到任何技术问题,都是一些我这几年对开发程序的思考。这些大多与DotNet无关,但是我希望你能从我这里找到问题的答案。如果你有不同的看法,十分欢迎你与我交流。
[1]为什么要开发程序?为什么要当程序员?
Only one reason,我们喜欢,这就是我们的生活方式。我们不是为了享受众人羡慕的眼光,也不是为了去挣大把大把的金钱。我们对技术的态度就像恶魔猎人对力量的无限渴求一样。我们可以把手放在胸前,对天发誓:我们真心喜欢编程。
[2]不喜欢编程能当好程序员吗?
你应该已经能猜到我的观点了,那就是:不能,就算能你也不快乐。(没人愿意干自己不感兴趣的事情,过得不快乐,你这辈子就算白活了)
[3]开发水平低怎么办?成为不了高手怎么办?
难道成为高手你就快乐了吗?我看不出来。
[4]快速提高水平的秘诀是什么?
唯有刻苦+勤奋。
[5]中国软件业的未来怎么办?
我还在思考。。。。。。
[6]关于语言的选择?
语言只是为了解决问题的,最终的要点是要解决问题,因此不同的语言有它擅长的应用领域。
[7]C++的阴影?
这个其实和上个问题一样,有这样一种观点,DotNet的出现把程序员们都弄傻了,只会用那些控件(like vb)。其实没有傻的技术,只有傻的人,没有人会因为使用DotNet而变得愚蠢。CPPer们攻击DotNet的原因无非就是DotNet太简单了,白痴都会,做不了底层应用,代码效率低。我想不通,一个技术容易上手有什么不好,在同样的应用产出下,你的学习成本的降低,开发效率的提高难道是坏事?至于做不了底层系统和代码效率的问题,这是因为DotNet不是面向这个领域,这就是C++将来的地盘,DotNet有它的领域,一个企业3层应用平台,全部用C++开发怎能快速,灵活的搞定。过于偏激的排外或爱好一种技术,只会给自己带来灾难性的后果。最后要说一个历史趋势,那就是随着计算机性能和问题的逻辑复杂度的提高,高效率的代码已经不再是唯一的衡量开发的标准了,从汇编,C,C++,JAVA,DotNet的发展,你看到了什么?那就是抽象的程度越来越高,程序员们的编码难度逐步降低(也就是次要复杂度的降低),但是系统分析和设计的难度则在逐层提升,所面对的问题的逻辑复杂度在逐渐升高(主要复杂度的升高),这正是DotNet(Java)出现的契机,它降低我们开发代码的次要复杂度,使我们可以集中我们的全部精力投入到主要复杂度的解决上来,这也正是MDA出现的原因。
[8]程序员的基本素质?
正直,善良,刻苦,勤奋,平和,坦然。
[9]最后来各搞笑点的话题,程序员真的不好找mm吗?
确实不好找。。。呜呜~~~
为了我们的程序,认了吧。
五. 最后的话:
非常感谢你能有耐心读完我的文章,希望看完之后你能找到自己的方向,期待你与我的进一步交流,bye~~~~。

2006年06月20日

一、前言

  自从微软推出16位的Windows操作系统起,此后每种版本的Windows操作系统都非常依赖于动态链接库(DLL)中的函数和数据,实际上Windows操作系统中几乎所有的内容都由DLL以一种或另外一种形式代表着,例如显示的字体和图标存储在GDI DLL中、显示Windows桌面和处理用户的输入所需要的代码被存储在一个User DLL中、Windows编程所需要的大量的API函数也被包含在Kernel DLL中。

  在Windows操作系统中使用DLL有很多优点,最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个DLL文件,真正实现了资源"共享",大大缩小了应用程序的执行代码,更加有效的利用了内存;使用DLL的另一个优点是DLL文件作为一个单独的程序模块,封装性、独立性好,在软件需要升级的时候,开发人员只需要修改相应的DLL文件就可以了,而且,当DLL中的函数改变后,只要不是参数的改变,程序代码并不需要重新编译。这在编程时十分有用,大大提高了软件开发和维护的效率。

  既然DLL那么重要,所以搞清楚什么是DLL、如何在Windows操作系统中开发使用DLL是程序开发人员不得不解决的一个问题。本文针对这些问题,通过一个简单的例子,即在一个DLL中实现比较最大、最小整数这两个简单函数,全面地解析了在Visual C++编译环境下编程实现DLL的过程,文章中所用到的程序代码在Windows98系统、Visual C++6.0编译环境下通过。

  二、DLL的概念

  DLL是建立在客户/服务器通信的概念上,包含若干函数、类或资源的库文件,函数和数据被存储在一个DLL(服务器)上并由一个或多个客户导出而使用,这些客户可以是应用程序或者是其它的DLL。DLL库不同于静态库,在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为*.LIB),Visual C++的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为"静态链接",此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。

  在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。

  微软的Visual C++支持三种DLL,它们分别是Non-MFC Dll(非MFC动态库)、Regular Dll(常规DLL)、Extension Dll(扩展DLL)。Non-MFC DLL指的是不用MFC的类库结构,直接用C语言写的DLL,其导出的函数是标准的C接口,能被非MFC或MFC编写的应用程序所调用。Regular DLL:和下述的Extension Dlls一样,是用MFC类库编写的,它的一个明显的特点是在源文件里有一个继承CWinApp的类(注意:此类DLL虽然从CWinApp派生,但没有消息循环),被导出的函数是C函数、C++类或者C++成员函数(注意不要把术语C++类与MFC的微软基础C++类相混淆),调用常规DLL的应用程序不必是MFC应用程序,只要是能调用类C函数的应用程序就可以,它们可以是在Visual C++、Dephi、Visual Basic、Borland C等编译环境下利用DLL开发应用程序。

  常规DLL又可细分成静态链接到MFC和动态链接到MFC上的,这两种常规DLL的区别将在下面介绍。与常规DLL相比,使用扩展DLL用于导出增强MFC基础类的函数或子类,用这种类型的动态链接库,可以用来输出一个从MFC所继承下来的类。

  扩展DLL是使用MFC的动态链接版本所创建的,并且它只被用MFC类库所编写的应用程序所调用。例如你已经创建了一个从MFC的CtoolBar类的派生类用于创建一个新的工具栏,为了导出这个类,你必须把它放到一个MFC扩展的DLL中。扩展DLL 和常规DLL不一样,它没有一个从CWinApp继承而来的类的对象,所以,开发人员必须在DLL中的DllMain函数添加初始化代码和结束代码。

  三、动态链接库的创建

  在Visual C++6.0开发环境下,打开File\New\Project选项,可以选择Win32 Dynamic-Link Library或MFC AppWizard[dll]来以不同的方式来创建Non-MFC Dll、Regular Dll、Extension Dll等不同种类的动态链接库。

  1. Win32 Dynamic-Link Library方式创建Non-MFC DLL动态链接库

  每一个DLL必须有一个入口点,这就象我们用C编写的应用程序一样,必须有一个WINMAIN函数一样。在Non-MFC DLL中DllMain是一个缺省的入口函数,你不需要编写自己的DLL入口函数,用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化。如果应用程序的DLL需要分配额外的内存或资源时,或者说需要对每个进程或线程初始化和清除操作时,需要在相应的DLL工程的.CPP文件中对DllMain()函数按照下面的格式书写。


BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
…….
case DLL_THREAD_ATTACH:
…….
case DLL_THREAD_DETACH:
…….
case DLL_PROCESS_DETACH:
…….
}
return TRUE;
}

  参数中,hMoudle是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_DGROUP段的一个选择符);ul_reason_for_call是一个说明动态库被调原因的标志,当进程或线程装入或卸载动态链接库的时候,操作系统调用入口函数,并说明动态链接库被调用的原因,它所有的可能值为:DLL_PROCESS_ATTACH: 进程被调用、DLL_THREAD_ATTACH: 线程被调用、DLL_PROCESS_DETACH: 进程被停止、DLL_THREAD_DETACH: 线程被停止;lpReserved为保留参数。到此为止,DLL的入口函数已经写了,剩下部分的实现也不难,你可以在DLL工程中加入你所想要输出的函数或变量了。

  我们已经知道DLL是包含若干个函数的库文件,应用程序使用DLL中的函数之前,应该先导出这些函数,以便供给应用程序使用。要导出这些函数有两种方法,一是在定义函数时使用导出关键字_declspec(dllexport),另外一种方法是在创建DLL文件时使用模块定义文件.Def。需要读者注意的是在使用第一种方法的时候,不能使用DEF文件。下面通过两个例子来说明如何使用这两种方法创建DLL文件。

  1)使用导出函数关键字_declspec(dllexport)创建MyDll.dll,该动态链接库中有两个函数,分别用来实现得到两个数的最大和最小数。在MyDll.h和MyDLL.cpp文件中分别输入如下原代码:


//MyDLL.h
extern "C" _declspec(dllexport) int Max(int a, int b);
extern "C" _declspec(dllexport) int Min(int a, int b);
//MyDll.cpp
#include
#include"MyDll.h"
int Max(int a, int b)
{
if(a>=b)return a;
else
return b;
}
int Min(int a, int b)
{
if(a>=b)return b;
else
return a;
}

  该动态链接库编译成功后,打开MyDll工程中的debug目录,可以看到MyDll.dll、MyDll.lib两个文件。LIB文件中包含DLL文件名和DLL文件中的函数名等,该LIB文件只是对应该DLL文件的"映像文件",与DLL文件中,LIB文件的长度要小的多,在进行隐式链接DLL时要用到它。读者可能已经注意到在MyDll.h中有关键字"extern C",它可以使其他编程语言访问你编写的DLL中的函数。

  2)用.def文件创建工程MyDll

  为了用.def文件创建DLL,请先删除上个例子创建的工程中的MyDll.h文件,保留MyDll.cpp并在该文件头删除#include MyDll.h语句,同时往该工程中加入一个文本文件,命名为MyDll.def,再在该文件中加入如下代码:

LIBRARY MyDll
EXPORTS
Max
Min

  其中LIBRARY语句说明该def文件是属于相应DLL的,EXPORTS语句下列出要导出的函数名称。我们可以在.def文件中的导出函数后加@n,如Max@1,Min@2,表示要导出的函数顺序号,在进行显式连时可以用到它。该DLL编译成功后,打开工程中的Debug目录,同样也会看到MyDll.dll和MyDll.lib文件。

  2.MFC AppWizard[dll]方式生成常规/扩展DLL

  在MFC AppWizard[dll]下生成DLL文件又有三种方式,在创建DLL是,要根据实际情况选择创建DLL的方式。一种是常规DLL静态链接到MFC,另一种是常规DLL动态链接到MFC。两者的区别是:前者使用的是MFC的静态链接库,生成的DLL文件长度大,一般不使用这种方式,后者使用MFC的动态链接库,生成的DLL文件长度小;动态链接到MFC的规则DLL所有输出的函数应该以如下语句开始:


AFX_MANAGE_STATE(AfxGetStaticModuleState( )) //此语句用来正确地切换MFC模块状态

  最后一种是MFC扩展DLL,这种DLL特点是用来建立MFC的派生类,Dll只被用MFC类库所编写的应用程序所调用。前面我们已经介绍过,Extension DLLs 和Regular DLLs不一样,它没有一个从CWinApp继承而来的类的对象,编译器默认了一个DLL入口函数DLLMain()作为对DLL的初始化,你可以在此函数中实现初始化,代码如下:


BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll,DWORD reason ,LPVOID flmpload)
{
switch(reason)
{
……………//初始化代码;
}
return true;
}

  参数hinstDll存放DLL的句柄,参数reason指明调用函数的原因,lpReserved是一个被系统所保留的参数。对于隐式链接是一个非零值,对于显式链接值是零。

  在MFC下建立DLL文件,会自动生成def文件框架,其它与建立传统的Non-MFC DLL没有什么区别,只要在相应的头文件写入关键字_declspec(dllexport)函数类型和函数名等,或在生成的def文件中EXPORTS下输入函数名就可以了。需要注意的是在向其它开发人员分发MFC扩展DLL 时,不要忘记提供描述DLL中类的头文件以及相应的.LIB文件和DLL本身,此后开发人员就能充分利用你开发的扩展DLL了。

  四、动态链接库DLL的链接

  应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。Visual C++6.0在VC\bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。另外,Windows系统将遵循下面的搜索顺序来定位DLL: 1.包含EXE文件的目录,2.进程的当前工作目录, 3.Windows系统目录, 4.Windows目录,5.列在Path环境变量中的一系列目录。

  1.隐式链接

  隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易,只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。下面的例子通过隐式链接调用MyDll.dll库中的Min函数。首先生成一个项目为TestDll,在DllTest.h、DllTest.cpp文件中分别输入如下代码:


//Dlltest.h
#pragma comment(lib,"MyDll.lib")
extern "C"_declspec(dllimport) int Max(int a,int b);
extern "C"_declspec(dllimport) int Min(int a,int b);
//TestDll.cpp
#include
#include"Dlltest.h"
void main()
{int a;
a=min(8,10)
printf("比较的结果为%d\n",a);
}

  在创建DllTest.exe文件之前,要先将MyDll.dll和MyDll.lib拷贝到当前工程所在的目录下面,也可以拷贝到windows的System目录下。如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern "C"。TestDll.h文件中的关键字Progam commit是要Visual C+的编译器在link时,链接到MyDll.lib文件,当然,开发人员也可以不使用#pragma comment(lib,"MyDll.lib")语句,而直接在工程的Setting->Link页的Object/Moduls栏填入MyDll.lib既可。

  2.显式链接

  显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用GetProcAddress()获取想要引入的函数。自此,你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Max函数的例子。


#include
#include
void main(void)
{
typedef int(*pMax)(int a,int b);
typedef int(*pMin)(int a,int b);
HINSTANCE hDLL;
PMax Max
HDLL=LoadLibrary("MyDll.dll");//加载动态链接库MyDll.dll文件;
Max=(pMax)GetProcAddress(hDLL,"Max");
A=Max(5,8);
Printf("比较的结果为%d\n",a);
FreeLibrary(hDLL);//卸载MyDll.dll文件;
}

  在上例中使用类型定义关键字typedef,定义指向和DLL中相同的函数原型指针,然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄,然后通过GetProcAddress()函数获取导入到应用程序中的函数指针,函数调用完毕后,使用FreeLibrary()卸载DLL文件。在编译程序之前,首先要将DLL文件拷贝到工程所在的目录或Windows系统目录下。

  使用显式链接应用程序编译时不需要使用相应的Lib文件。另外,使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号,如将GetProcAddress(hDLL,"Min")改为GetProcAddress(hDLL, MAKEINTRESOURCE(2))(函数Min()在DLL中的顺序号是2),这样调用DLL中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。

  本文通过通俗易懂的方式,全面介绍了动态链接库的概念、动态链接库的创建和动态链接库的链接,并给出个简单明了的例子,相信读者看了本文后,能够创建自己的动态链接库并应用到后续的软件开发当中去了,当然,读者要熟练操作DLL,还需要在大量的实践中不断摸索,希望本文能起到抛砖引玉的作用。

2006年06月19日

关于c++的编译器现在有很多,各有所长。不可能每个人都全部用过。  
我想统计一下现在的主流编译器都有什么。linux,windows,dos下的什么编译器最流行,也最好!请各位发表意见并给出你所用的编译器的好处。  
—————————————————————  
我主要从事windows下的编程,所以我一般用vc编译,毕竟是当今软件霸主的产品,性能和功能自然不必问了。就是编译速度有点慢。所以我在写小程序的时候有lcc编译器。性能也不错,而且编译速度极快。  
还有就是我觉得vc对于初学者来说可能不是好的编译器,因为mfc把所有的程序执行机制都封装在它的类里面。初学者看到其代码可能不明所以然。但是lcc不错,很适合初学者。它是标准的sdk编译器。程序的运行机制和消息处理机制写的非常清楚。  
大家见仁见智吧!  
我的选择:
WINDOWS首选VC  初学者,小项目用LCC  

我主要用这两种编译器,其他的很少用,所以不敢评价。请大家评论。  
—————————————————————  
我这儿有数十种C/C++编译器。  
如下:  
GCC家族有  
   Cygwin  
   Mingw32  
   DJGPP  
   Dev-C++(Mingw32)  
   还有正宗的GNU  GCC  2.95.5~3.0.0.4版本  
MS家族有  
   MSC  5.0、6.0、7.0  
   MSQC  1.0、2.5  
   MSVC  1.0、4.2、6.0、7.0  
Borland家族有  
   TC  1.0、2.0  
   TC++  1.01、3.0  
   BC  3.0、3.1、4.0、4.5、5.0、5.02  
   BCB  3.0、5.0、6.0  
其它有  
   Intel  C/C++  5.0  
   Watcom  C/C++  11.0、11.0c  
   VectorC  1.3.3  
   IBM  VisualAge  for  C++  
   DigitalMars  C/C++  
   KAI  C/C++  4.03f  for  RedHat  7.2  
   Lcc4.1  
   LCC-WIN32  2001-09-25~2002-04-28日版  
   Small  C  
   CC386  
   Pacific  C  
另外还有C的解释器  
   Quincy  
   Eic  
   CINT  
     
   上面提到的编译器/解释器,大部分我都使用过。现在固定使用VC7.0  Cygwin  Mingw32  VectorC和LCC-WIN32这五种编译器。

   在GCC家族中GNU  GCC是根本,其它的编译器版本都是从它导出的。其中,Cygwin和Mingw32都是WIN32平台下的编译器,DJGPP是DOS下的32位编译器。大家所熟知的DEV-C++充其量只是GCC的一个外壳,它所自带的编译器就是Mingw32的一个版本。这些GCC的版本中,Cygwin是最大的,它与其说是一个编译器,倒不如说是一套编程工具。它不仅有编译器,还有其它很多的工具。其实,它就是一个UNIX系统在WIN32平台上的实现。实现了大多常用的UNIX工具,最近的版本中连Apache这样的“工具”都集成进来的。不过,Cygwin虽然功能强大,但它却不是很易用(和UNIX相似,熟悉UNIX的人用它可以很快上手),因为太多其它的工具分散了人们的注意力。相比之下Mingw32就要好用得多,它只有最基本的几个编程工具(只可惜它不自带GDB)。GCC中并不只是C/C++编译器,其中还有很多其它的编译器如JAVA,Fortran,ADA等。它是一个编译器集合,不过有些编译器只能在UNIX系统上用。MS家族的编译器就不用说了,大家对它们都很熟悉。VC  7.0(VC.NET)是它的最新产品。Borland家族也不用说,大家也是耳熟能详。最近它才推出了BCB 6.0。
  
   其它的编译器如:Intel  C/C++大家一看名称就知道是Intel的东西,它和VC6完全兼容,不过要挂在VC6下才能用。Watcom  C/C++是早先编译器四国大战中的一员,原本是很不错的东西,可惜战略不对,现在已不见声息了。倒是以它为基础的一个OpenWatcom现在还在奋战。VectorC是我近日才发现的一个好东东,它是个纯C的编译器。IBM的VisualAge for  C++原本是IBM想用来淌C++编译器这片浑水的东西,不过IBM的战略改了,它就被放弃了。DigitalMars  C/C++的前身的Symantec C++(它也是编译器四国大战中的一员),不过现在Symantec不做了,于是它的作者就把它改成了DigitalMars  C/C++开放给大家使用。以上这些都是WIN32平台上的东西。KAI  C/C++是个很强大的C/C++编译器,它是个多平台的编译器。不过现在被INTEL收购了,已经停止开发了。Lcc4.1是个纯C的编译器它是开放源代码的。不过不怎么好用。LCC-WIN32是一个在LCC基础上开发的C语言的集成开发环境,很好用,而且有很详细的资料,FREE!Pacific  C是一个纯DOS的C的集成开发环境,就不多说了。Small  C  CC386都是开放源代码的编译器,它们都很简单,应用来给大家学习编译器的。Quincy  Eic  CINT都是C的解释器,是用来让大家学习C语言的其中CINT的功能很强大,还支持一些C++的特性。  
   当然还有很多其它的编译器,这里我给出的编译器都是可以在WIN32或DOS平台上用的(除KAI外)。UNIX平台上的编译器还是以GNU的为主,其它的我就不是很清楚了。

   在以上的编译器中,最特别的就是VectorC这个东西只支持纯C。但它却号称是最快的编译器,不过经过我的试验,它的确在有些情况下强过其它编译器很多!而且它还有个交互式的优化器,可以让你直接看到C代码对映的汇编代码。Cygwin和Mingw32为一母所生,其运行效果相差不大。它们生成的代码效率都很不错,编译的速度也很快,最值得一提的是它们对C++的特性的支持算是所有编译器中最完全的,而且它们还支持C99的大部分特性。这一点很是不错!大家对MS的VC已经很熟悉了,本不用我多说。不过在它的最新的产品VC7.0中,有很大的改进。它对C++的特性的支持比6.0有了很大的提高,是我所用的编译器中是仅次于GCC的。而且它编译出的程序,运行速度很快!仅有少数时候次于VectorC与GCC,其它情况都是最快的!其平均运行速度是最快的。对Borland的产品我也无需多说。它的TC2.0与BC3.1都是我最喜欢的东西。可是现在的BCB却大不如前了,编译的速度和VC6一样慢!IDE还有较多的BUG。最令人想不通的是它生成的代码的运行速度很慢,比LCC-WIN32还慢!它唯一值得一提的就是它的RAD做的比MS的好。Intel的编译器大家可能不熟,它太贵了!还要有VC的支持,很不划算,而且编译速度比VC6还慢。不过它的代码质量很不错。DigitalMars  C/C++没有什么亮点,编译速度较快,代码执行速度适中,对C++特性支持还算不错。LCC-WIN32是个很不错的集成开发环境,它只支持纯C。它的编译速度极快!代码执行速度较慢。不过它的最大亮点在于它的IDE,在所有的FREE编程工具中,它的IDE是最专业的,有很强大的代码分析,管理功能。而且它提供了大量的编程资料。  
   我曾对一些编译器的代码执行效率做过一些测试,以下是概况:  
   1.  VectorC、VC  7.0  (极快)  
   2.  Intel  C/C++、VC  6.0、GCC  (很快)  
   3.  DigitalMars  C/C++  (一般)  
   4.  LCC-WIN32、BCB、BC5.02  (较慢)  
当然,我所做的测试比较片面。不过在很大程度上已能反映其大概状况。  
(我也曾看到过一个测试,其中Intel的编译器是最快的,比VC  6.0和GCC要快10%~50%,而我所测的结果中,GCC  >=VC6  >Intel C/C++ )  
(以上评论都是个人观点)