﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-君子性非异也，善假于物也。</title><link>http://www.cppblog.com/xushaohua/</link><description>如有恒，何须三更起，半夜眠；最怕莫，三天打鱼两天晒网，竹篮打水一场空！</description><language>zh-cn</language><lastBuildDate>Thu, 09 Apr 2026 05:38:20 GMT</lastBuildDate><pubDate>Thu, 09 Apr 2026 05:38:20 GMT</pubDate><ttl>60</ttl><item><title>[转]Win32应用程序中进程间通信方法分析与比较</title><link>http://www.cppblog.com/xushaohua/archive/2009/02/17/74015.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Tue, 17 Feb 2009 03:18:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2009/02/17/74015.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/74015.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2009/02/17/74015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/74015.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/74015.html</trackback:ping><description><![CDATA[<span  style="color: rgb(75, 75, 75); font-family: Verdana; font-size: 13px; line-height: 19px; "><p align="center" style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>Win32</strong><strong>应用程序中进程间通信方法分析与比较</strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">&#160;</p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>1&#160;</strong><strong>进程与进程通信</strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">&#160;</p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">　　进程是装入内存并准备执行的程序，每个进程都有私有的虚拟地址空间，由代码、数据以及它可利用的系统资源(如文件、管道等)组成。多进程/多线程是Windows操作系统的一个基本特征。Microsoft Win32应用编程接口(Application Programming Interface, API)提供了大量支持应用程序间数据共享和交换的机制，这些机制行使的活动称为进程间通信(InterProcess Communication, IPC)，进程通信就是指不同进程间进行数据共享和数据交换。<br>　　正因为使用Win32 API进行进程通信方式有多种，如何选择恰当的通信方式就成为应用开发中的一个重要问题，下面本文将对Win32中进程通信的几种方法加以分析和比较。</p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">&#160;</p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>2&#160;</strong><strong>进程通信方法</strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>2.1&#160;</strong><strong>文件映射</strong><br>　　文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此，进程不必使用文件I/O操作，只需简单的指针操作就可读取和修改文件的内容。<br>　　Win32 API允许多个进程访问同一文件映射对象，各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针，不同进程就可以读或修改文件的内容，实现了对文件中数据的共享。<br>　　应用程序有三种方法来使多个进程共享一个文件映射对象。<br>　　(1)继承：第一个进程建立文件映射对象，它的子进程继承该对象的句柄。<br>　　(2)命名文件映射：第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外，第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。<br>　　(3)句柄复制：第一个进程建立文件映射对象，然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。<br>　　文件映射是在多个进程间共享数据的非常有效方法，有较好的安全性。但文件映射只能用于本地机器的进程之间，不能用于网络中，而开发者还必须控制进程间的同步。<br><strong>2.2&#160;</strong><strong>共享内存</strong><br>　　Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE)，就表示了对应的文件映射对象是从操作系统页面文件访问内存，其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的，所以它也有较好的安全性，也只能运行于同一计算机上的进程之间。<br><strong>2.3&#160;</strong><strong>匿名管道</strong><br>　　管道(Pipe)是一种具有两个端点的通信通道：有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向－一端是只读的，另一端点是只写的；也可以是双向的一管道的两端点既可读也可写。<br>　　匿名管道(Anonymous Pipe)是在父进程和子进程之间，或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道，然后由要通信的子进程继承通道的读端点句柄或写端点句柄，然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信，不需要通过父进程。<br>　　匿名管道是单机上实现子进程标准I/O重定向的有效方法，它不能在网上使用，也不能用于两个不相关的进程之间。<br><strong>2.4&#160;</strong><strong>命名管道</strong><br>　　命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不同计算机之间使用，服务器建立命名管道时给它指定一个名字，任何进程都可以通过该名字打开管道的另一端，根据给定的权限和服务器进程通信。<br>　　命名管道提供了相对简单的编程接口，使通过网络传输数据并不比同一计算机上两进程之间通信更困难，不过如果要同时和多个进程通信它就力不从心了。<br><strong>2.5&#160;</strong><strong>邮件槽</strong><br>　　邮件槽(Mailslots)提供进程间单向通信能力，任何进程都能建立邮件槽成为邮件槽服务器。其它进程，称为邮件槽客户，可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消息一直放在邮件槽中，直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户，因此可建立多个邮件槽实现进程间的双向通信。<br>　　通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节，非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。<br>　　邮件槽与命名管道相似，不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的，一旦网络发生错误则无法保证消息正确地接收，而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力，所以邮件槽不失为应用程序发送和接收消息的另一种选择。<br><strong>2.6&#160;</strong><strong>剪贴板</strong><br>　　剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息，为Windows应用程序之间进行数据共享提供了一个中介，Windows已建立的剪切(复制)－粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时，应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据，从给定格式中选择适合自己的格式。<br>　　剪贴板是一个非常松散的交换媒介，可以支持任何数据格式，每一格式由一无符号整数标识，对标准(预定义)剪贴板格式，该值是Win32 API定义的常量；对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用，不能在网络上使用。<br><strong>2.7&#160;</strong><strong>动态数据交换</strong><br>　　动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输，也可以当出现新数据时，通过发送更新值在应用程序间动态交换数据。<br>　　DDE和剪贴板一样既支持标准数据格式(如文本、位图等)，又可以支持自己定义的数据格式。但它们的数据传输机制却不同，一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答－如从菜单中选择Paste命令。尽管DDE也可以由用户启动，但它继续发挥作用一般不必用户进一步干预。DDE有三种数据交换方式：<br>　　(1) 冷链：数据交换是一次性数据传输，与剪贴板相同。<br>　　(2) 温链：当数据交换时服务器通知客户，然后客户必须请求新的数据。<br>　　(3) 热链：当数据交换时服务器自动给客户发送数据。<br>　　DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC，它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。<br><strong>2.8&#160;</strong><strong>对象连接与嵌入</strong><br>　　应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档)，OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的服务。例如，OLE支持的字处理器可以嵌套电子表格，当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时，该表格已在原始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展，而如果使用DDE，用户要显式地启动电子表格编辑器。<br>　　同DDE技术相同，大多数基于Windows的应用程序都支持OLE技术。<br><strong>2.9&#160;</strong><strong>动态连接库</strong><br>　　Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享，这就又给进程间通信开辟了一条新的途径，当然访问时要注意同步问题。<br>　　虽然可以通过DLL进行进程间数据共享，但从数据安全的角度考虑，我们并不提倡这种方法，使用带有访问权限控制的共享内存的方法更好一些。<br><strong>2.10&#160;</strong><strong>远程过程调用</strong><br>　　Win32 API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数，这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既可以在单机不同进程间使用也可以在网络中使用。<br>　　由于Win32 API提供的RPC服从OSF-DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过Win32 API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程序。<br><strong>2.11 NetBios</strong><strong>函数</strong><br>　　Win32 API提供NetBios函数用于处理低级网络控制，这主要是为IBM NetBios系统编写与Windows的接口。除非那些有特殊低级网络功能要求的应用程序，其它应用程序最好不要使用NetBios函数来进行进程间通信。<br><strong>2.12 Sockets</strong><br>　　Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley Socket原有的库函数以外，还扩展了一组针对Windows的函数，使程序员可以充分利用Windows的消息机制进行编程。<br>　　现在通过Sockets实现进程通信的网络应用越来越多，这主要的原因是Sockets的跨平台性要比其它IPC机制好得多，另外WinSock 2.0不仅支持TCP/IP协议，而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作，这使得在单机的进程间进行简单数据传递不太方便，这时使用下面将介绍的WM_COPYDATA消息将更合适些。<br><strong>2.13 WM_COPYDATA</strong><strong>消息</strong><br>　　WM_COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时，发送方只需使用调用SendMessage函数，参数是目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息，这样收发双方就实现了数据共享。<br>　　WM_COPYDATA是一种非常简单的方法，它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高，并且它只能用于Windows平台的单机环境下。</p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">&#160;</p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>3&#160;</strong><strong>结束语</strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">　　Win32 API为应用程序实现进程间通信提供了如此多种选择方案，那么开发者如何进行选择呢？通常在决定使用哪种IPC方法之前应考虑下一些问题，如应用程序是在网络环境下还是在单机环境下工作等。</p></span>
<img src ="http://www.cppblog.com/xushaohua/aggbug/74015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2009-02-17 11:18 <a href="http://www.cppblog.com/xushaohua/archive/2009/02/17/74015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源协议------LGPL</title><link>http://www.cppblog.com/xushaohua/archive/2009/02/15/73861.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Sun, 15 Feb 2009 04:58:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2009/02/15/73861.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/73861.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2009/02/15/73861.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/73861.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/73861.html</trackback:ping><description><![CDATA[GNU Lesser General Public License<br><br>
<p style="FONT-SIZE: 12pt">LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同。LGPL允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并发布和销售。</p>
<p style="FONT-SIZE: 12pt">但是如果修改LGPL协议的代码或者衍生，则所有修改的代码，涉及修改部分的额外代码和衍生的代码都必须采用LGPL协议。因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用，但不适合希望以LGPL协议代码为基础，通过修改和衍生的方式做二次开发的商业软件采用。</p>
<p style="FONT-SIZE: 12pt">GPL/LGPL都保障原作者的知识产权，避免有人利用开源代码复制并开发类似的产品</p>
<img src ="http://www.cppblog.com/xushaohua/aggbug/73861.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2009-02-15 12:58 <a href="http://www.cppblog.com/xushaohua/archive/2009/02/15/73861.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源协议------GPL</title><link>http://www.cppblog.com/xushaohua/archive/2009/02/15/73860.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Sun, 15 Feb 2009 04:57:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2009/02/15/73860.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/73860.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2009/02/15/73860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/73860.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/73860.html</trackback:ping><description><![CDATA[GNU General Public License<br>
<p style="FONT-SIZE: 12pt"><br>我们很熟悉的Linux就是采用了GPL。GPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用，但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux，包括商业公司的linux和linux上各种各样的由个人，组织，以及商业软件公司开发的免费软件了。</p>
<p style="FONT-SIZE: 12pt">GPL协议的主要内容是只要在一个软件中使用(&#8221;使用&#8221;指类库引用，修改后的代码或者衍生代码)GPL 协议的产品，则该软件产品必须也采用GPL协议，既必须也是开源和免费。这就是所谓的&#8221;传染性&#8221;。GPL协议的产品作为一个单独的产品使用没有任何问题，还可以享受免费的优势。</p>
<p style="FONT-SIZE: 12pt">由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议，对于使用GPL协议的开源代码，商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。</p>
<p style="FONT-SIZE: 12pt">其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似。</p>
<img src ="http://www.cppblog.com/xushaohua/aggbug/73860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2009-02-15 12:57 <a href="http://www.cppblog.com/xushaohua/archive/2009/02/15/73860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源协议------Apache Licence 2.0</title><link>http://www.cppblog.com/xushaohua/archive/2009/02/15/73858.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Sun, 15 Feb 2009 04:55:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2009/02/15/73858.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/73858.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2009/02/15/73858.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/73858.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/73858.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt">Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似，同样鼓励代码共享和尊重原作者的著作权，同样允许代码修改，再发布（作为开源或商业软件）。需要满足的条件也和BSD类似：</p>
<ol>
    <li>需要给代码的用户一份Apache Licence
    <li>如果你修改了代码，需要再被修改的文件中说明。
    <li>在延伸的代码中（修改和有源代码衍生的代码中）需要带有原来代码中的协议，商标，专利声明和其他原来作者规定需要包含的说明。
    <li>如果再发布的产品中包含一个Notice文件，则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可，但不可以表现为对Apache Licence构成更改。 </li>
</ol>
<p style="FONT-SIZE: 12pt">Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。</p>
<img src ="http://www.cppblog.com/xushaohua/aggbug/73858.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2009-02-15 12:55 <a href="http://www.cppblog.com/xushaohua/archive/2009/02/15/73858.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源协议------BSD开源协议</title><link>http://www.cppblog.com/xushaohua/archive/2009/02/15/73857.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Sun, 15 Feb 2009 04:54:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2009/02/15/73857.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/73857.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2009/02/15/73857.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/73857.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/73857.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt">BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以&#8221;为所欲为&#8221;,可以自由的使用，修改源代码，也可以将修改后的代码作为开源或者专有软件再发布。</p>
<p style="FONT-SIZE: 12pt">但&#8221;为所欲为&#8221;的前提当你发布使用了BSD协议的代码，或则以BSD协议代码为基础做二次开发自己的产品时，需要满足三个条件：</p>
<ol>
    <li>如果再发布的产品中包含源代码，则在源代码中必须带有原来代码中的BSD协议。
    <li>如果再发布的只是二进制类库/软件，则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
    <li>不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。 </li>
</ol>
<p style="FONT-SIZE: 12pt">BSD 代码鼓励代码共享，但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码，也允许使用或在BSD代码上开发商业软件发布和销售，因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议，因为可以完全控制这些第三方的代码，在必要的时候可以修改或者二次开发。</p>
<img src ="http://www.cppblog.com/xushaohua/aggbug/73857.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2009-02-15 12:54 <a href="http://www.cppblog.com/xushaohua/archive/2009/02/15/73857.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BCD码</title><link>http://www.cppblog.com/xushaohua/archive/2007/12/24/39499.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Mon, 24 Dec 2007 04:34:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2007/12/24/39499.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/39499.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2007/12/24/39499.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/39499.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/39499.html</trackback:ping><description><![CDATA[即BCD代码。Binary-Coded Decimal&#8206;，简称BCD，称BCD码或二-十进制代码，亦称二进码十进数。是一种<a href="http://baike.baidu.com/view/18536.htm" target=_blank><u><font color=#0000ff>二进制</font></u></a>的数字编码形式，用二进制编码的<a href="http://baike.baidu.com/view/359301.htm" target=_blank><u><font color=#0000ff>十进制</font></u></a>代码。这种编码形式利用了四个位元来储存一个十进制的数码，使二进制和十进制之间的转换得以快捷的进行。这种编码技巧，最常用于会计系统的设计里，因为会计制度经常需要对很长的数字串作准确的计算。相对于一般的浮点式记数法，采用BCD码，既可保存数值的精确度，又可免却使电脑作浮点运算时所耗费的时间。此外，对于其他需要高精确度的计算，BCD编码亦很常用。<br><br>由于十进制数共有0、1、2、&#8230;&#8230;、9十个数码，因此，至少需要4位二进制码来表示1位十进制数。4位二进制码共有2^4=16种码组，在这16种代码中，可以任选10种来表示10个十进制数码，共有N=16！/（16-10）！约等于2.9乘以10的10次方种方案。常用的BCD代码列于末。<br><br><strong>常用BCD编码方式</strong><br>最常用的BCD编码，就是使用"0"至"9"这十个数值的二进码来表示。这种编码方式，在中国大陆称之为&#8220;8421码&#8221;。除此以外，对应不同需求，各人亦开发了不同的编码方法，以适应不同的需求。这些编码，大致可以分成有权码和无权码两种：<br><br>有权BCD码，如：8421(最常用)、2421、5421&#8230; <br>无权BCD码，如：余3码、格雷码&#8230; <br><br>常用<span>BCD编码表</span>
<div align=center>
<table cellSpacing=0 cellPadding=0 width="74%" border=1>
    <tbody>
        <tr>
            <td>
            <div align=center>
            <table cellSpacing=0 cellPadding=0 width=410 border=0>
                <tbody>
                    <tr>
                        <td width=90>
                        <p>&#160;</p>
                        </td>
                        <td width=67>
                        <p align=center><span>8421码</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>5421码</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>2421码</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>5211码</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>余<span>3码</span></span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>0</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0000</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0000</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0000</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0000</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0000</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>1</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0001</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0001</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0001</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0001</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0100</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>2</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0010</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0010</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0010</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0100</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0101</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>3</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0011</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0011</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0011</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0101</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0110</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>4</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0100</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0100</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0100</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0111</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>0111</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>5</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0101</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1000</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0101</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1000</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1000</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>6</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0110</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1001</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0110</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1001</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1001</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>7</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>0111</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1010</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>0111</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1100</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1010</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>8</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1000</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1011</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>1110</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1101</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1011</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>9</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1001</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1100</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>1111</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>1111</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>1100</span></p>
                        </td>
                    </tr>
                    <tr>
                        <td width=90>
                        <p align=center><span>权</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>8421</span></p>
                        </td>
                        <td width=60>
                        <p align=center><span>5421</span></p>
                        </td>
                        <td width=66>
                        <p align=center><span>2421</span></p>
                        </td>
                        <td width=67>
                        <p align=center><span>5211</span></p>
                        </td>
                        <td width=60>
                        <p>&nbsp;</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            <p>&#160;</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<span><font size=3>非压缩式和压缩式BCD码<br>&nbsp;&nbsp;&nbsp; BCD又分为两种，非压缩式和压缩式两种。<br>&nbsp;&nbsp;&nbsp; 前面这种81存成 &#8220;08,01&#8221; 是非压缩式，而压缩式会存成 &#8220;81h&#8221; (直接以十六进制储存)。非压缩的BCD码只有低四位有效，而压缩的BCD码则将高四位也用上了，就是说一个字节有两个BCD码。BCD是用0和1表示十进制，如0000表示0，0001表示1，0010表示2。而压缩的BCD是用00表示0，01表示1，10表示2，110表示3等。<br>&nbsp;&nbsp;&nbsp; 例：<br>&nbsp;&nbsp;&nbsp; 1234表示成非压缩的BCD码是00000001000000100000001100000100，也就是0x01020304；而压缩BCD码则表示成0001001000110100，也就是0x1234。<br>&nbsp;&nbsp;&nbsp; 但压缩的BCD并不固定，可看情况而定，所要的就是用最少的位数表示尽可能多的数。</font><br></span>
<img src ="http://www.cppblog.com/xushaohua/aggbug/39499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2007-12-24 12:34 <a href="http://www.cppblog.com/xushaohua/archive/2007/12/24/39499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]编写高效的数据库连接池</title><link>http://www.cppblog.com/xushaohua/archive/2007/12/21/39172.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Thu, 20 Dec 2007 17:42:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2007/12/21/39172.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/39172.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2007/12/21/39172.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/39172.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/39172.html</trackback:ping><description><![CDATA[<p><font face="Times New Roman" size=2>相关技术：</font></p>
<ul>
    <li><font face="Times New Roman" size=2>连接池</font>
    <li><font face="Times New Roman" size=2>引用记数</font>
    <li><font face="Times New Roman" size=2>多线程</font>
    <li><font face="Times New Roman" size=2>Timer类运行基理</font>
    <li><font face="Times New Roman" size=2>C#.Net</font>&nbsp;&nbsp; </li>
</ul>
<p><font face="Times New Roman" size=2>适宜人群</font></p>
<ul>
    <li><font face="Times New Roman" size=2>数据库应用程序程序员</font>
    <li><font face="Times New Roman" size=2>系统分析员</font>
    <li><font face="Times New Roman" size=2>模块设计师</font>
    <li>有一定功底的程序员 </li>
</ul>
<p>目录</p>
<ul>
    <li><font size=2>引言</font>
    <ol>
        <li><font face="Times New Roman" size=2>数据库连接池（Connection Pool）的工作原理</font> </li>
    </ol>
    <li><font face="Times New Roman" size=2>连接池关键问题分析</font>
    <ol>
        <li><font face="Times New Roman" size=2>并发问题</font>
        <li><font face="Times New Roman" size=2>事务处理</font>
        <li><font face="Times New Roman" size=2>连接池的分配与释放</font>
        <li><font face="Times New Roman" size=2>连接池的配置与维护</font> </li>
    </ol>
    <li>
    <div align=left><font face="Times New Roman" size=2>关键议题</font></div>
    <ol>
        <li>
        <div align=left><font face="Times New Roman" size=2>引用记数</font></div>
        <li>
        <div align=left>如何实现事务处理</div>
        <li>
        <div align=left>
        <div align=left>&nbsp;管理连接池</div>
        </div>
        </li>
    </ol>
    <li>
    <div align=left>
    <div align=left>结合代码说明</div>
    </div>
    <ol>
        <li>
        <div align=left>构造方法</div>
        <li>
        <div align=left><font size=2>启动服务StartService</font></div>
        <li>
        <div align=left><font size=2>停止服务StopService</font></div>
        <li>
        <div align=left><font size=2>申请&nbsp;GetConnectionFormPool</font></div>
        <li>
        <div align=left><font size=2>释放DisposeConnection</font></div>
        <li>
        <div align=left><font size=2>如何更新属性</font></div>
        <li>
        <div align=left><font size=2>如何确定连接是否失效</font></div>
        <li>
        <div align=left><font size=2>使用线程管理连接池</font></div>
        <ol>
            <li>
            <div align=left><font size=2>threadCreate</font></div>
            <li>
            <div align=left><font size=2>threadCheck</font></div>
            </li>
        </ol>
        <li>
        <div align=left><font size=2></font>
        <p>&nbsp;<font size=2>其他</font></p>
        </div>
        </li>
    </ol>
    </li>
</ul>
<hr>
<p><font color=#ff0000 size=3>引言</font></p>
<p><font face="Times New Roman" size=2>一般的数据库应用程序大致都遵循下面的步骤:</font></p>
<ol>
    <li><font face="Times New Roman"><font size=2><font color=#ff99cc>初始化程序</font> </font></font>
    <li><font face="Times New Roman"><font size=2><font color=#ff99cc>用户在UI上输入操作</font> </font></font>
    <li><font face="Times New Roman"><font size=2><font color=#ff99cc>由用户操作产生数据库操作</font> </font></font>
    <li><font face="Times New Roman"><font size=2><font color=#ff99cc>将数据库操作递交到数据库服务器</font> </font></font>
    <li><font face="Times New Roman"><font color=#ff99cc size=2>.... (重复2~4)</font></font>
    <li><font face="Times New Roman"><font size=2><font color=#ff99cc>关闭应用程序</font> </font></font></li>
</ol>
<p><font face="Times New Roman" size=2>　　而本文则着重讲解上面第<font color=#ff0000>4</font>步骤.在着一步骤中我们经常是,打开数据库连接操作数据库,最后关闭数据库.<br></font><font face="Times New Roman" size=2>　　在服务器端程序设计上与数据库的操作显得十分重要,因为你要处理的数据操作十分巨大.如果频繁创建数据库连接频繁关闭数据库连接则会引起效率低下甚至引发程序崩溃.<br>　　也许我们可以有另一种操作数据库的形式,我们可以在程序运行时打开一个数据库连接,让这个连接永久存在直到程序'死亡',那么这样做也有不安全隐患,我们知道一个对象存在时间越长或被使用次数越多则它表现的越不稳定,着不稳定因素是由于对象内部可能存在的潜在设计问题产生,对于数据库连接对象道理也一样.我们不能保证一个Connection对象里面能一点问题不存在.所以我们也不敢长时间将它长时间占用内存.<br></font><font face="Times New Roman" size=2>　　既然有这么多的问题由此我们需要一个能帮我们维护数据库连接的东西-它就是<font color=#ff0000>连接池</font>,网上有很多的连接池例子,但是多数都是简单的例子,或者介绍比较复杂的连接池原理,没有一个比较完整介绍和实现连接池的例子.这里就介绍你如何自己制作一个连接池.<br></font><font face="Times New Roman" size=2>　　对于共享资源，有一个很著名的设计模式：资源池（Resource Pool）。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决我们的问题，可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个&#8220;缓冲池&#8221;。预先在缓冲池中放入一定数量的连接，当需要建立数据库连接时，只需从&#8220;缓冲池&#8221;中取出一个，使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况，为系统开发﹑测试及性能调整提供依据。连接池的基本工作原理见下图。</font></p>
<p align=center><font face="Times New Roman" size=3>数据库连接池（Connection Pool）的工作原理</font></p>
<p align=center><font face="Times New Roman" size=2><img height=227 alt="" src="http://www.mx68.com/WebDeveloper/UploadFiles_5122/200601/2006118234027410.gif" width=563></font></p>
<p align=left><font face="Times New Roman" size=2><font face=Arial color=#ff0000 size=3></font></font></p>
<p><font color=#ff0000 size=3>&nbsp;连接池关键问题分析</font></p>
<p align=left><font face="Times New Roman" size=2>　　1、并发问题</font></p>
<p align=left><font face="Times New Roman" size=2>　　为了使连接管理服务具有最大的通用性，必须考虑多线程环境，即并发问题。这个问题相对比较好解决，因为各个语言自身提供了对并发管理的支持像java,c#等等，使用synchronized(java)lock(C#)关键字即可确保线程是同步的。使用方法可以参考，相关文献。</font></p>
<p align=left><font face="Times New Roman" size=2>　　２、事务处理</font></p>
<p align=left><font face="Times New Roman" size=2>　　我们知道，事务具有原子性，此时要求对数据库的操作符合&#8220;ALL-ALL-NOTHING&#8221;原则,即对于一组SQL语句要么全做，要么全不做。<br></font><font face="Times New Roman" size=2>　　我们知道当２个线程公用一个连接Connection对象，而且各自都有自己的事务要处理时候，对于连接池是一个很头疼的问题，因为即使Connection类提供了相应的事务支持，可是我们仍然不能确定那个数据库操作是对应那个事务的，这是由于我们有２个线程都在进行事务操作而引起的。为此我们可以使用</font><font face="Times New Roman" size=2>每一个事务独占一个连接来实现，虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。</font></p>
<p align=left><font face="Times New Roman" size=2>　　３、连接池的分配与释放</font></p>
<p align=left><font face="Times New Roman" size=2>　　连接池的分配与释放，对系统的性能有很大的影响。合理的分配与释放，可以提高连接的复用度，从而降低建立新连接的开销，同时还可以加快用户的访问速度。<br></font><font face="Times New Roman" size=2>　　对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时，系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他<font color=#339966>（如何能找到最合适的连接文章将在关键议题中指出</font>）；如果没有就抛出一个异常给用户，List中连接是否可以被分配由一个线程来专门管理捎后我会介绍这个线程的具体实现。 </font></p>
<p>&nbsp;<font face="Times New Roman" size=2>　　４、连接池的配置与维护</font></p>
<p align=left><font face="Times New Roman" size=2>　　连接池中到底应该放置多少连接，才能使系统的性能最佳？系统可采取设置最小连接数（minConnection）和最大连接数（maxConnection）等参数来控制连接池中的连接。比方说，最小连接数是系统启动时连接池所创建的连接数。如果创建过多，则系统启动就慢，但创建后系统的响应速度会很快；如果创建过少，则系统启动的很快，响应起来却慢。这样，可以在开发时，设置较小的最小连接数，开发起来会快，而在系统实际使用时设置较大的，因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目，具体设置多少，要看系统的访问量，可通过软件需求上得到。<br></font><font face="Times New Roman" size=2>　　如何确保连接池中的最小连接数呢？有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测，如果发现连接数量小于最小连接数，则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。 </font></p>
<img src ="http://www.cppblog.com/xushaohua/aggbug/39172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2007-12-21 01:42 <a href="http://www.cppblog.com/xushaohua/archive/2007/12/21/39172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[MSDN]使用连接池</title><link>http://www.cppblog.com/xushaohua/archive/2007/12/21/39171.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Thu, 20 Dec 2007 17:33:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2007/12/21/39171.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/39171.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2007/12/21/39171.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/39171.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/39171.html</trackback:ping><description><![CDATA[<div class=title>使用连接池<!----></div>
<!--content type: DocStudio. Transform: devdiv2mtps.xslt.-->
<div id=mainSection>
<div id=mainBody>
<p>
<p>连接到数据库服务器通常由几个需要很长时间的步骤组成。必须建立物理通道（例如套接字或命名管道），必须与服务器进行初次握手，必须分析连接字符串信息，必须由服务器对连接进行身份验证，必须运行检查以便在当前事务中登记，等等。 </p>
<p>实际上，大多数应用程序仅使用一个或几个不同的连接配置。这意味着在执行应用程序期间，许多相同的连接将反复地打开和关闭。为了使打开的连接成本最低，ADO.NET 使用称为<em>连接池</em>的优化方法。</p>
<p>连接池减少新连接需要打开的次数。<em>池进程</em>保持物理连接的所有权。通过为每个给定的连接配置保留一组活动连接来管理连接。只要用户在连接上调用 Open，池进程就会检查池中是否有可用的连接。如果某个池连接可用，会将该连接返回给调用者，而不是打开新连接。应用程序在该连接上调用 Close 时，池进程会将连接返回到活动连接池集中，而不是真正关闭连接。连接返回到池中之后，即可在下一个 Open 调用中重复使用。</p>
<p>只有配置相同的连接可以建立池连接。ADO.NET 同时保留多个池，每个配置一个池。连接由连接字符串以及 Windows 标识（在使用集成的安全性时）分为多个池。</p>
<p>池连接可以大大提高应用程序的性能和可缩放性。默认情况下，ADO.NET 中启用连接池。除非显式禁用，否则，连接在应用程序中打开和关闭时，池进程将对连接进行优化。还可以提供几个连接字符串修饰符来控制连接池的行为。有关更多信息，请参见本主题后面的&#8220;使用连接字符串关键字控制连接池&#8221;。</p>
<h1 class=heading>池的创建和分配</h1>
<div class=seeAlsoNoToggleSection id=sectionSection0>
<p>在初次打开连接时，将根据完全匹配算法创建连接池，该算法将池与连接中的连接字符串关联。每个连接池与不同的连接字符串关联。打开新连接时，如果连接字符串并非与现有池完全匹配，将创建一个新池。按进程、按应用程序域、按连接字符串以及（在使用集成的安全性时）按 Windows 标识来建立池连接。</p>
<p>在以下 C# 示例中创建了三个新的 <a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/sd2728ad(VS.80).aspx"><u><font color=#0000ff>SqlConnection</font></u></a> 对象，但是管理时只需要两个连接池。注意，根据为 <tt>Initial Catalog</tt> 分配的值，第一个和第二个连接字符串有所不同。</p>
<div class=code id=ctl00_rs1_mainContentContainer_ctl02_other>
<pre class=code id=ctl00_rs1_mainContentContainer_ctl02other space="preserve">using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// Pool A is created.
}
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=pubs"))
{
connection.Open();
// Pool B is created because the connection strings differ.
}
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// The connection string matches pool A.
}</pre>
</div>
<p>如果 MinPoolSize 在连接字符串中未指定或指定为零，池中的连接将在一段时间不活动后关闭。但是，如果指定的 MinPoolSize 大于零，在 AppDomain 被卸载并且进程结束之前，连接池不会被破坏。非活动或空池的维护只需要最少的系统开销。</p>
<div class=alert>
<table width="100%">
    <tbody>
        <tr>
            <th align=left><img class=note alt=Note src="http://msdn2.microsoft.com/zh-cn/library/8xx3tyca.note(zh-cn,VS.80).gif">注意</th>
        </tr>
        <tr>
            <td>
            <p>如果发生致命错误（例如故障转移或注册表中的别名更改），池将自动清除。</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
</div>
<h1 class=heading>添加连接</h1>
<div class=seeAlsoNoToggleSection id=sectionSection1>
<p>连接池是为每个唯一的连接字符串创建的。当创建一个池后，将创建多个连接对象并将其添加到该池中，以满足最小池大小的要求。连接根据需要添加到池中，但是不能超过指定的最大池大小（默认值为 100）。连接在关闭或断开时释放回池中。 </p>
<p>在请求 SqlConnection 对象时，如果存在可用的连接，将从池中获取该对象。连接要可用，必须未使用，具有匹配的事务上下文或未与任何事务上下文关联，并且具有与服务器的有效链接。</p>
<p>连接池进程通过在连接释放回池中时重新分配连接，来满足这些连接请求。如果已达到最大池大小且不存在可用的连接，则该请求将会排队。然后，池进程尝试重新建立任何连接，直到到达超时时间（默认值为 15 秒）。如果池进程在连接超时之前无法满足请求，将引发异常。 </p>
<div class=alert>
<table width="100%">
    <tbody>
        <tr>
            <th align=left><img class=note alt="Caution note" src="http://msdn2.microsoft.com/zh-cn/library/8xx3tyca.Caution(zh-cn,VS.80).gif">警告</th>
        </tr>
        <tr>
            <td>
            <p>我们建议您在使用完连接时一定要关闭连接，以便连接可以返回池。要关闭连接，可以使用 Connection 对象的 Close 或 Dispose 方法，也可以通过在 C# 的 using 语句中或在 Visual Basic 的 Using 语句中打开所有连接。不是显式关闭的连接可能不会添加或返回到池中。例如，如果连接已超出范围但没有显式关闭，则仅当达到最大池大小而该连接仍然有效时，该连接才会返回到连接池中。有关更多信息，请参见 Visual Basic 的<a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/yh598w02(VS.80).aspx"><u><font color=#0000ff>using 语句（C# 参考）</font></u></a>或<a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/wydd5hkd(VS.80).aspx"><u><font color=#0000ff>如何：释放系统资源</font></u></a>。</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div class=alert>
<table width="100%">
    <tbody>
        <tr>
            <th align=left><img class=note alt=Note src="http://msdn2.microsoft.com/zh-cn/library/8xx3tyca.note(zh-cn,VS.80).gif">注意</th>
        </tr>
        <tr>
            <td>
            <p>不要在类的 Finalize 方法中对 Connection、DataReader 或任何其他托管对象调用 Close 或 Dispose。在终结器中，仅释放类直接拥有的非托管资源。如果类不拥有任何非托管资源，则不要在类定义中包含 Finalize 方法。有关更多信息，请参见<a id=ctl00_rs1_mainContentContainer_ctl05 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/0xy59wtx(VS.80).aspx"><u><font color=#0000ff>垃圾回收</font></u></a>。</p>
            </td>
        </tr>
    </tbody>
</table>
</div>
</div>
<h1 class=heading>移除连接</h1>
<div class=seeAlsoNoToggleSection id=sectionSection2>
<p>连接池进程定期扫描连接池，查找没有通过 Close 或 Dispose 关闭的未用连接，并重新建立找到的连接。如果应用程序没有显式关闭或断开其连接，连接池进程可能需要很长时间才能重新建立连接，所以，最好确保在连接中显式调用 Close 和 Dispose。</p>
<p>如果连接长时间空闲，或池进程检测到与服务器的连接已断开，连接池进程会将该连接从池中移除。注意，只有在尝试与服务器进行通信之后才能检测到断开的连接。如果发现某连接不再连接到服务器，则会将其标记为无效。无效连接只有在关闭或重新建立后，才会从连接池中移除。</p>
<p>如果存在与已消失的服务器的连接，那么即使连接池管理程序未检测到已断开的连接并将其标记为无效，仍有可能将此连接从池中取出。这种情况是因为检查连接是否仍有效的系统开销将造成与服务器的另一次往返，从而抵消了池进程的优势。发生此情况时，初次尝试使用该连接将检测连接是否曾断开，并引发异常。</p>
</div>
<h1 class=heading>清除池</h1>
<div class=seeAlsoNoToggleSection id=sectionSection3>
<p>ADO.NET 2.0 引入了两种新的方法来清除池：<a id=ctl00_rs1_mainContentContainer_ctl06 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl06',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/565tk4bs(VS.80).aspx"><u><font color=#0000ff>ClearAllPools</font></u></a> 和 <a id=ctl00_rs1_mainContentContainer_ctl07 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl07',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/tw22a676(VS.80).aspx"><u><font color=#0000ff>ClearPool</font></u></a>。ClearAllPools 清除给定提供程序的连接池，ClearPool 清除与特定连接关联的连接池。如果在调用时连接正在使用，将进行相应的标记。连接关闭时，将被丢弃，而不是返回池中。</p>
</div>
<h1 class=heading>事务支持</h1>
<div class=seeAlsoNoToggleSection id=sectionSection4>
<p>连接是根据事务上下文来从池中取出并进行分配的。除非在连接字符串中指定了 <tt>Enlist=false</tt>，否则，连接池将确保连接在 <a id=ctl00_rs1_mainContentContainer_ctl08 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl08',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/f1a9t75e(VS.80).aspx"><u><font color=#0000ff>Current</font></u></a> 上下文中登记。如果连接使用登记的 System.Transactions 事务关闭并返回池中，连接将保留在池中，以便使用相同 System.Transactions 事务对该连接池的下一次请求将返回相同的连接。如果该事务没有可用连接，在该连接打开时，将自动注册该连接。</p>
<p>当连接关闭时，它将被释放回池中，并根据其事务上下文放入相应的子部分。因此，即使分布式事务仍然挂起，仍可以关闭该连接而不会生成错误。这样，您就可以在随后提交或中止分布式事务。</p>
</div>
<h1 class=heading>使用连接字符串关键字控制连接池</h1>
<div class=seeAlsoNoToggleSection id=sectionSection5>
<p>SqlConnection 对象的 ConnectionString 属性支持连接字符串键/值对，可以用于调整连接池逻辑的行为。有关更多信息，请参见 <a id=ctl00_rs1_mainContentContainer_ctl09 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl09',this);" tabIndex=0 href="http://msdn2.microsoft.com/zh-cn/library/f28szy5b(VS.80).aspx"><u><font color=#0000ff>ConnectionString</font></u></a>。</p>
</div>
<h1 class=heading>池碎片</h1>
<div class=seeAlsoNoToggleSection id=sectionSection6>
<p>池碎片是许多 Web 应用程序中的一个常见问题，应用程序可能会创建大量在进程退出后才会释放的池。这样，将打开大量的连接，占用许多内存，从而影响性能。</p>
<h3 class=subHeading>因为集成安全性产生的池碎片</h3>
<div class=subSection>
<p>连接根据连接字符串以及用户标识来建立池连接。因此，如果使用网站上的基本身份验证或 Windows 身份验证以及集成的安全登录，每个用户将获得一个池。尽管这样可以提高单个用户的后续数据库请求的性能，但是该用户无法利用其他用户建立的连接。这样还使每个用户至少产生一个与数据库服务器的连接。这对特定 Web 应用程序结构会产生副作用，因为开发人员需要衡量安全性和审计要求。</p>
</div>
<h3 class=subHeading>因为许多数据库产生的池碎片</h3>
<div class=subSection>
<p>许多 Internet 服务提供商在一台服务器上托管多个网站。他们可能使用单个数据库确认窗体身份验证登录，然后为该用户或用户组打开与特定数据库的连接。与身份验证数据库的连接将建立池连接，供每个用户使用。但是，每个数据库的连接存在一个独立的池，因此增加了与服务器的连接数。</p>
<p>这也会对应用程序设计产生副作用。但是，可以通过一个相对简单的方式避免此副作用，而又不会影响连接 SQL Server 时的安全性。不是为每个用户或组连接独立的数据库，而是连接到服务器上的相同数据库，然后执行 Transact-SQL USE 语句来切换为所需的数据库。以下代码段演示入如何创建与 master 数据库的初始连接，然后切换到 <tt>databaseName</tt> 字符串变量中指定的所需数据库。</p>
<div class=code id=ctl00_rs1_mainContentContainer_ctl11_CSharp>C#:<br><span style="COLOR: green">// Assumes that command is a SqlCommand object.</span><br><span style="COLOR: blue">using</span> (SqlConnection connection = <span style="COLOR: blue">new</span> SqlConnection(<br>&nbsp; <span style="COLOR: maroon">"Server=MSSQL1;uid=xxx;pwd=xxx;database=master"</span>))<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; connection.Open();<br>&nbsp;&nbsp;&nbsp; command.ExecuteNonQuery(<span style="COLOR: maroon">"USE "</span> + databaseName);<br>&nbsp; }<br></div>
</div>
</div>
<h1 class=heading>应用程序角色和连接池</h1>
<div class=seeAlsoNoToggleSection id=sectionSection7>
<p>通过调用 sp_setapprole 系统存储过程激活了 SQL Server 应用程序角色之后，该连接的安全上下文无法重置。但是，如果启用了池，连接将返回池，在重复使用池连接时会出错。</p>
<p>如果使用的是 SQL Server 应用程序角色，我们建议您在连接字符串中为应用程序禁用连接池。有关更多信息，请参见知识库文章&#8220;<a id=ctl00_rs1_mainContentContainer_ctl12 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl12',this);" tabIndex=0 href="http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q229564"><u><font color=#0000ff>SQL application role errors with OLE DB resource pooling</font></u></a>&#8221;。</p>
</div>
</div>
</div>
<img src ="http://www.cppblog.com/xushaohua/aggbug/39171.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2007-12-21 01:33 <a href="http://www.cppblog.com/xushaohua/archive/2007/12/21/39171.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]VC中基于 Windows 的精确定时</title><link>http://www.cppblog.com/xushaohua/archive/2007/12/04/37817.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Tue, 04 Dec 2007 12:56:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2007/12/04/37817.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/37817.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2007/12/04/37817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/37817.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/37817.html</trackback:ping><description><![CDATA[中国科学院光电技术研究所 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#121;&#122;&#121;&#115;&#101;&#97;&#108;&#64;&#49;&#50;&#54;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>游志宇</font></u></a><br><br>在工业生产控制系统中，有许多需要定时完成的操作，如定时显示当前时间，定时刷新屏幕上的进度条，上位<wbr> 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的实时控制系统和数据采集系统中，就更需要精确定时操作。<br>　　众所周知，Windows 是基于消息机制的系统，任何事件的执行都是通过发送和接收消息来完成的。<wbr> 这样就带来了一些问题，如一旦计算机的CPU被某个进程占用，或系统资源紧张时，发送到消息队列<wbr> 中的消息就暂时被挂起，得不到实时处理。因此，不能简单地通过Windows消息引发一个对定时要求<wbr> 严格的事件。另外，由于在Windows中已经封装了计算机底层硬件的访问，所以，要想通过直接利用<wbr> 访问硬件来完成精确定时，也比较困难。所以在实际应用时，应针对具体定时精度的要求，采取相适 应的定时方法。<br>　　VC中提供了很多关于时间操作的函数，利用它们控制程序能够精确地完成定时和计时操作。本文详细介绍了<wbr> VC中基于Windows的精确定时的七种方式，如下图所示：<br><img height=419 alt="" src="http://www.cppblog.com/images/cppblog_com/xushaohua/MultiTimerDemoimg.gif" width=639 border=0><br>图一 图像描述 <br><br>　　方式一：VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时<wbr> 间隔，如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数<wbr> OnTimer()，并在该函数中添加响应的处理语句，用来完成到达定时时间的操作。这种定时方法非常<wbr> 简单，可以实现一定的定时功能，但其定时功能如同Sleep()函数的延时功能一样，精度非常低，最小<wbr> 计时精度仅为30ms，CPU占用低，且定时器消息在多任务操作系统中的优先级很低，不能得到及时响<wbr> 应，往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的Timer1。 <br>　　方式二：VC中使用sleep()函数实现延时，它的单位是ms，如延时2秒，用sleep(2000)。精度非常<wbr> 低，最小计时精度仅为30ms，用sleep函数的不利处在于延时期间不能处理其他的消息，如果时间太<wbr> 长，就好象死机一样，CPU占用率非常高，只能用于要求不高的延时程序中。如示例工程中的Timer2。<br>　　方式三：利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3和Timer3_1。以下是实现2秒的延时代码：<br>COleDateTime&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; start_time = COleDateTime::GetCurrentTime();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COleDateTimeSpan&nbsp; end_time= COleDateTime::GetCurrentTime()-start_time;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(end_time.GetTotalSeconds()&lt; 2) //实现延时2秒<br>&nbsp;&nbsp;&nbsp;&nbsp; { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSG&nbsp;&nbsp; msg;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetMessage(&amp;msg,NULL,0,0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TranslateMessage(&amp;msg); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DispatchMessage(&amp;msg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //以上四行是实现在延时或定时期间能处理其他的消息，<br>　　　　　　 //虽然这样可以降低CPU的占有率，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //但降低了延时或定时精度，实际应用中可以去掉。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end_time = COleDateTime::GetCurrentTime()-start_time;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }//这样在延时的时候我们也能够处理其他的消息。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>方式四：在精度要求较高的情况下，VC中可以利用GetTickCount()函数，该函数的返回值是<wbr> &nbsp;DWORD型，表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高，在较<wbr> 短的定时中其计时误差为15ms，在较长的定时中其计时误差较低，如果定时时间太长，就好象死机一样，CPU占用率非常高，只能用于要求不高的延时程序中。如示例工程中的Timer4和Timer4_1。下列代码可以实现50ms的精确定时：<br>
<pre>       DWORD dwStart = GetTickCount();
DWORD dwEnd   = dwStart;
do
{
dwEnd = GetTickCount()-dwStart;
}while(dwEnd &lt;50);</pre>
为使GetTickCount()函数在延时或定时期间能处理其他的消息，可以把代码改为：<br>
<pre>       DWORD dwStart = GetTickCount();
DWORD dwEnd   = dwStart;
do
{
MSG   msg;
GetMessage(&amp;msg,NULL,0,0);
TranslateMessage(&amp;msg);
DispatchMessage(&amp;msg);
dwEnd = GetTickCount()-dwStart;
}while(dwEnd &lt;50);</pre>
虽然这样可以降低CPU的占有率，并在延时或定时期间也能处理其他的消息，但降低了延时或定时精度。<br>　　方式五：与GetTickCount()函数类似的多媒体定时器函数DWORD timeGetTime(void)，该函数定时精<wbr> 度为ms级，返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底<wbr> 层API持，利用多媒体定时器可以很精确地读出系统的当前时间，并且能在非常精确的时间间隔内完成一<wbr> 个事件、函数或过程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须将 Winmm.lib&nbsp;<wbr> 和 Mmsystem.h 添加到工程中，否则在编译时提示DWORD timeGetTime(void)函数未定义。由于使用该<wbr> 函数是通过查询的方式进行定时控制的，所以，应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5和Timer5_1。<br>　　方式六：使用多媒体定时器timeSetEvent()函数，该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的Timer6和Timer6_1。函数的原型如下： <br>
<pre>       MMRESULT timeSetEvent（ UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent ）</pre>
　　该函数设置一个定时回调事件，此事件可以是一个一次性事件或周期性事件。事件一旦被激活，便调用指定的回调函数，<wbr> 成功后返回事件的标识符代码，否则返回NULL。函数的参数说明如下：<br>
<pre>       uDelay：以毫秒指定事件的周期。
Uresolution：以毫秒指定延时的精度，数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc：指向一个回调函数。
DwUser：存放用户提供的回调数据。
FuEvent：指定定时器事件类型：
TIME_ONESHOT：uDelay毫秒后只产生一次事件
TIME_PERIODIC ：每隔uDelay毫秒周期性地产生事件。      </pre>
　　具体应用时，可以通过调用timeSetEvent()函数，将需要周期性执行的任务定义在LpTimeProc回调函数<wbr> 中(如：定时采样、控制等)，从而完成所需处理的事件。需要注意的是，任务处理的时间不能大于周期间隔时间。另外，在定时器使用完毕后，<wbr> 应及时调用timeKillEvent()将之释放。 <br>　　方式七：对于精确度要求更高的定时操作，则应该使用QueryPerformanceFrequency()和<wbr> QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数，并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7、Timer7_1、Timer7_2、Timer7_3。<br>QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下：<br>
<pre>       BOOL  QueryPerformanceFrequency(LARGE_INTEGER ＊lpFrequency);
BOOL  QueryPerformanceCounter(LARGE_INTEGER ＊lpCount);</pre>
　　数据类型ARGE_INTEGER既可以是一个8字节长的整型数，也可以是两个4字节长的整型数的联合结构，<wbr> 其具体用法根据编译器是否支持64位而定。该类型的定义如下：<br>
<pre>       typedef union _LARGE_INTEGER
{
struct
{
DWORD LowPart ;// 4字节整型数
LONG  HighPart;// 4字节整型数
};
LONGLONG QuadPart ;// 8字节整型数
}LARGE_INTEGER ;</pre>
　　在进行定时之前，先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率，<wbr> 然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数，利用两次获得的计数之差及时钟频率，计算出事件经<wbr> 历的精确时间。下列代码实现1ms的精确定时：<br>
<pre>       LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&amp;litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&amp;litmp);
QPart1 = litmp.QuadPart;// 获得初始值
do
{
QueryPerformanceCounter(&amp;litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值，单位为秒
}while(dfTim&lt;0.001);</pre>
　　其定时误差不超过1微秒，精度与CPU等机器配置有关。 下面的程序用来测试函数Sleep(100)的精确持续时间：<br>
<pre>       LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&amp;litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&amp;litmp);
QPart1 = litmp.QuadPart;// 获得初始值
Sleep(100);
QueryPerformanceCounter(&amp;litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值，单位为秒     </pre>
　　由于Sleep()函数自身的误差，上述程序每次执行的结果都会有微小误差。下列代码实现1微秒的精确定时：<br>
<pre>       LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
QueryPerformanceFrequency(&amp;litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&amp;litmp);
QPart1 = litmp.QuadPart;// 获得初始值
do
{
QueryPerformanceCounter(&amp;litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值，单位为秒
}while(dfTim&lt;0.000001);</pre>
其定时误差一般不超过0.5微秒，精度与CPU等机器配置有关。
<img src ="http://www.cppblog.com/xushaohua/aggbug/37817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2007-12-04 20:56 <a href="http://www.cppblog.com/xushaohua/archive/2007/12/04/37817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于 Visual C++6.0 的 DLL 编程实现</title><link>http://www.cppblog.com/xushaohua/archive/2007/12/04/37783.html</link><dc:creator>neter</dc:creator><author>neter</author><pubDate>Tue, 04 Dec 2007 05:44:00 GMT</pubDate><guid>http://www.cppblog.com/xushaohua/archive/2007/12/04/37783.html</guid><wfw:comment>http://www.cppblog.com/xushaohua/comments/37783.html</wfw:comment><comments>http://www.cppblog.com/xushaohua/archive/2007/12/04/37783.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/xushaohua/comments/commentRss/37783.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/xushaohua/services/trackbacks/37783.html</trackback:ping><description><![CDATA[<p align=left><strong><span><font color=#f70938>一、前言</font> </span></strong><span><br><br></span><font color=#f70938><span>　　自从微软推出</span> <span>16</span> <span>位的</span> <span>Windows</span> <span>操作系统起，此后每种版本的</span> <span>Windows</span> <span>操作系统都非常依赖于动态链接库</span> <span>(DLL)</span> <span>中的函数和数据，实际上</span> <span>Windows</span> <span>操作系统中几乎所有的内容都由</span> <span>DLL</span> <span>以一种或另外一种形式代表着，例如显示的字体和图标存储在</span> <span>GDI DLL</span> <span>中、显示</span> <span>Windows</span> <span>桌面和处理用户的输入所需要的代码被存储在一个</span> <span>User DLL</span> <span>中、</span> <span>Windows</span> <span>编程所需要的大量的</span> <span>API</span> <span>函数也被包含在</span> <span>Kernel DLL</span> <span>中。</span> </font><span><br><br></span><font color=#f70938><span>　　在</span> <span>Windows</span> <span>操作系统中使用</span> <span>DLL</span> <span>有很多优点，最主要的一点是多个应用程序、甚至是不同语言编写的应用程序可以共享一个</span> <span>DLL</span> <span>文件，真正实现了资源</span> <span>"</span> <span>共享</span> <span>"</span> <span>，大大缩小了应用程序的执行代码，更加有效的利用了内存；使用</span> <span>DLL</span> <span>的另一个优点是</span> <span>DLL</span> <span>文件作为一个单独的程序模块，封装性、独立性好，在软件需要升级的时候，开发人员只需要修改相应的</span> <span>DLL</span> <span>文件就可以了，而且，当</span> <span>DLL</span> <span>中的函数改变后，只要不是参数的改变</span> <span>,</span> <span>程序代码并不需要重新编译。这在编程时十分有用，大大提高了软件开发和维护的效率。</span> </font><span><br><br></span><font color=#f70938><span>　　既然</span> <span>DLL</span> <span>那么重要，所以搞清楚什么是</span> <span>DLL</span> <span>、如何在</span> <span>Windows</span> <span>操作系统中开发使用</span> <span>DLL</span> <span>是程序开发人员不得不解决的一个问题。本文针对这些问题，通过一个简单的例子，即在一个</span> <span>DLL</span> <span>中实现比较最大、最小整数这两个简单函数，全面地解析了在</span> <span>Visual C++</span> <span>编译环境下编程实现</span> <span>DLL</span> <span>的过程，文章中所用到的程序代码在</span> <span>Windows98</span> <span>系统、</span> <span>Visual C++6.0</span> <span>编译环境下通过。</span> </font><span><br><br></span><font color=#f70938><span>　　<strong>二、</strong></span> <strong><span>DLL</span> </strong><strong><span>的概念</span> </strong></font><span><br><br></span><font color=#f70938><span>　　</span> <span>DLL</span> <span>是建立在客户</span> <span>/</span> <span>服务器通信的概念上，包含若干函数、类或资源的库文件，函数和数据被存储在一个</span> <span>DLL</span> <span>（服务器）上并由一个或多个客户导出而使用，这些客户可以是应用程序或者是其它的</span> <span>DLL</span> <span>。</span> <span>DLL</span> <span>库不同于静态库，在静态库情况下，函数和数据被编译进一个二进制文件（通常扩展名为</span> <span>*.LIB</span> <span>），</span> <span>Visual C++</span> <span>的编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为</span> <span>"</span> <span>静态链接</span> <span>"</span> <span>，此时因为应用程序所需的全部内容都是从库中复制了出来，所以静态库本身并不需要与可执行文件一起发行。</span> </font><span><br><br></span><font color=#f70938><span>　　在动态库的情况下，有两个文件，一个是引入库（</span> <span>.LIB</span> <span>）文件，一个是</span> <span>DLL</span> <span>文件，引入库文件包含被</span> <span>DLL</span> <span>导出的函数的名称和位置，</span> <span>DLL</span> <span>包含实际的函数和数据，应用程序使用</span> <span>LIB</span> <span>文件链接到所需要使用的</span> <span>DLL</span> <span>文件，库中的函数和数据并不复制到可执行文件中，因此在应用程序的可执行文件中，存放的不是被调用的函数代码，而是</span> <span>DLL</span> <span>中所要调用的函数的内存地址，这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来，从而节省了内存资源。从上面的说明可以看出，</span> <span>DLL</span> <span>和</span> <span>.LIB</span> <span>文件必须随应用程序一起发行，否则应用程序将会产生错误。</span> </font><span><br><br></span><font color=#f70938><span>　　微软的</span> <span>Visual C++</span> <span>支持三种</span> <span>DLL</span> <span>，它们分别是</span> <span>Non-MFC Dll</span> <span>（非</span> <span>MFC</span> <span>动态库）、</span> <span>Regular Dll</span> <span>（常规</span> <span>DLL</span> <span>）、</span> <span>Extension Dll</span> <span>（扩展</span> <span>DLL</span> <span>）。</span> <span>Non-MFC DLL</span> <span>指的是不用</span> <span>MFC</span> <span>的类库结构，直接用</span> <span>C</span> <span>语言写的</span> <span>DLL</span> <span>，其导出的函数是标准的</span> <span>C</span> <span>接口，能被非</span> <span>MFC</span> <span>或</span> <span>MFC</span> <span>编写的应用程序所调用。</span> <span>Regular DLL:</span> <span>和下述的</span> <span>Extension Dlls</span> <span>一样，是用</span> <span>MFC</span> <span>类库编写的，它的一个明显的特点是在源文件里有一个继承</span> <span>CWinApp</span> <span>的类（注意：此类</span> <span>DLL</span> <span>虽然从</span> <span>CWinApp</span> <span>派生，但没有消息循环）</span> <span>,</span> <span>被导出的函数是</span> <span>C</span> <span>函数、</span> <span>C++</span> <span>类或者</span> <span>C++</span> <span>成员函数（注意不要把术语</span> <span>C++</span> <span>类与</span> <span>MFC</span> <span>的微软基础</span> <span>C++</span> <span>类相混淆），调用常规</span> <span>DLL</span> <span>的应用程序不必是</span> <span>MFC</span> <span>应用程序，只要是能调用类</span> <span>C</span> <span>函数的应用程序就可以，它们可以是在</span> <span>Visual C++</span> <span>、</span> <span>Dephi</span> <span>、</span> <span>Visual Basic</span> <span>、</span> <span>Borland C</span> <span>等编译环境下利用</span> <span>DLL</span> <span>开发应用程序。</span> </font><span><br><br></span><font color=#f70938><span>　　常规</span> <span>DLL</span> <span>又可细分成静态链接到</span> <span>MFC</span> <span>和动态链接到</span> <span>MFC</span> <span>上的，这两种常规</span> <span>DLL</span> <span>的区别将在下面介绍。与常规</span> <span>DLL</span> <span>相比，使用扩展</span> <span>DLL</span> <span>用于导出增强</span> <span>MFC</span> <span>基础类的函数或子类，用这种类型的动态链接库，可以用来输出一个从</span> <span>MFC</span> <span>所继承下来的类。</span> </font><span><br><br></span><font color=#f70938><span>　　扩展</span> <span>DLL</span> <span>是使用</span> <span>MFC</span> <span>的动态链接版本所创建的，并且它只被用</span> <span>MFC</span> <span>类库所编写的应用程序所调用。例如你已经创建了一个从</span> <span>MFC</span> <span>的</span> <span>CtoolBar</span> <span>类的派生类用于创建一个新的工具栏，为了导出这个类，你必须把它放到一个</span> <span>MFC</span> <span>扩展的</span> <span>DLL</span> <span>中。扩展</span> <span>DLL </span><span>和常规</span> <span>DLL</span> <span>不一样，它没有一个从</span> <span>CWinApp</span> <span>继承而来的类的对象，所以，开发人员必须在</span> <span>DLL</span> <span>中的</span> <span>DllMain</span> <span>函数添加初始化代码和结束代码。</span> </font></p>
<p align=left><strong><span><font color=#f70938>三、动态链接库的创建</font> </span></strong><span><br><br></span><font color=#f70938><span>　　在</span> <span>Visual C++6.0</span> <span>开发环境下，打开</span> <span>FileNewProject</span> <span>选项，可以选择</span> <span>Win32 Dynamic-Link Library</span> <span>或</span> <span>MFC AppWizard[dll]</span> <span>来以不同的方式来创建</span> <span>Non-MFC Dll</span> <span>、</span> <span>Regular Dll</span> <span>、</span> <span>Extension Dll</span> <span>等不同种类的动态链接库。</span> </font><span><br><br></span><font color=#f70938><span>　　</span> <span>1</span> <span>．</span> <span>Win32 Dynamic-Link Library</span> <span>方式创建</span> <span>Non-MFC DLL</span> <span>动态链接库</span> </font><span><br><br></span><font color=#f70938><span>　　每一个</span> <span>DLL</span> <span>必须有一个入口点，这就象我们用</span> <span>C</span> <span>编写的应用程序一样，必须有一个</span> <span>WINMAIN</span> <span>函数一样。在</span> <span>Non-MFC DLL</span> <span>中</span> <span>DllMain</span> <span>是一个缺省的入口函数，你不需要编写自己的</span> <span>DLL</span> <span>入口函数，用这个缺省的入口函数就能使动态链接库被调用时得到正确的初始化。如果应用程序的</span> <span>DLL</span> <span>需要分配额外的内存或资源时，或者说需要对每个进程或线程初始化和清除操作时，需要在相应的</span> <span>DLL</span> <span>工程的</span> <span>.CPP</span> <span>文件中对</span> <span>DllMain()</span> <span>函数按照下面的格式书写。</span> </font><span><br></span><span><font color=#f70938>　</font> </span></p>
<p align=left><span><font color=#f70938>　</font> </span></p>
<table cellPadding=0 width="100%" border=0 FCK__ShowTableBorders?>
    <tbody>
        <tr>
            <td>
            <p align=left><span><font color=#f70938>BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)<br>{<br>switch( ul_reason_for_call )<br>{<br>case DLL_PROCESS_ATTACH:<br>.......<br>case DLL_THREAD_ATTACH:<br>.......<br>case DLL_THREAD_DETACH:<br>.......<br>case DLL_PROCESS_DETACH:<br>.......<br>}<br>return TRUE;<br>}</font> </span></p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><font color=#f70938><span>　　参数中，</span> <span>hMoudle</span> <span>是动态库被调用时所传递来的一个指向自己的句柄</span> <span>(</span> <span>实际上，它是指向</span> <span>_DGROUP</span> <span>段的一个选择符</span> <span>)</span> <span>；</span> <span>ul_reason_for_call</span> <span>是一个说明动态库被调原因的标志，当进程或线程装入或卸载动态链接库的时候，操作系统调用入口函数，并说明动态链接库被调用的原因，它所有的可能值为：</span> <span>DLL_PROCESS_ATTACH: </span><span>进程被调用、</span> <span>DLL_THREAD_ATTACH: </span><span>线程被调用、</span> <span>DLL_PROCESS_DETACH: </span><span>进程被停止、</span> <span>DLL_THREAD_DETACH: </span><span>线程被停止；</span> <span>lpReserved</span> <span>为保留参数。到此为止，</span> <span>DLL</span> <span>的入口函数已经写了，剩下部分的实现也不难，你可以在</span> <span>DLL</span> <span>工程中加入你所想要输出的函数或变量了。</span> </font><span><br><br></span><font color=#f70938><span>　　我们已经知道</span> <span>DLL</span> <span>是包含若干个函数的库文件，应用程序使用</span> <span>DLL</span> <span>中的函数之前，应该先导出这些函数，以便供给应用程序使用。要导出这些函数有两种方法，一是在定义函数时使用导出关键字</span> <span>_declspec(dllexport)</span> <span>，另外一种方法是在创建</span> <span>DLL</span> <span>文件时使用模块定义文件</span> <span>.Def</span> <span>。需要读者注意的是在使用第一种方法的时候，不能使用</span> <span>DEF</span> <span>文件。下面通过两个例子来说明如何使用这两种方法创建</span> <span>DLL</span> <span>文件。</span> </font><span><br><br></span><font color=#f70938><span>　　</span> <span>1</span> <span>）使用导出函数关键字</span> <span>_declspec(dllexport)</span> <span>创建</span> <span>MyDll.dll</span> <span>，该动态链接库中有两个函数，分别用来实现得到两个数的最大和最小数。在</span> <span>MyDll.h</span> <span>和</span> <span>MyDLL.cpp</span> <span>文件中分别输入如下原代码：</span> </font><span><br></span><span><font color=#f70938>　</font> </span></p>
<table cellPadding=0 width="100%" border=0 FCK__ShowTableBorders?>
    <tbody>
        <tr>
            <td>
            <p align=left><span><font color=#f70938>//MyDLL.h<br>extern "C" _declspec(dllexport) int Max(int a, int b);<br>extern "C" _declspec(dllexport) int Min(int a, int b);<br>//MyDll.cpp<br>＃i nclude<br>＃i nclude"MyDll.h"<br>int Max(int a, int b)<br>{<br>if(a&gt;=b)return a;<br>else<br>return b;<br>}<br>int Min(int a, int b)<br>{<br>if(a&gt;=b)return b;<br>else<br>return a;<br>}</font> </span></p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><font color=#f70938><span>　　该动态链接库编译成功后，打开</span> <span>MyDll</span> <span>工程中的</span> <span>debug</span> <span>目录，可以看到</span> <span>MyDll.dll</span> <span>、</span> <span>MyDll.lib</span> <span>两个文件。</span> <span>LIB</span> <span>文件中包含</span> <span>DLL</span> <span>文件名和</span> <span>DLL</span> <span>文件中的函数名等，该</span> <span>LIB</span> <span>文件只是对应该</span> <span>DLL</span> <span>文件的</span> <span>"</span> <span>映像文件</span> <span>"</span> <span>，与</span> <span>DLL</span> <span>文件中，</span> <span>LIB</span> <span>文件的长度要小的多，在进行隐式链接</span> <span>DLL</span> <span>时要用到它。读者可能已经注意到在</span> <span>MyDll.h</span> <span>中有关键字</span> <span>"extern C"</span> <span>，它可以使其他编程语言访问你编写的</span> <span>DLL</span> <span>中的函数。</span> </font><span><br><br></span><font color=#f70938><span>　　</span> <span>2</span> <span>）用</span> <span>.def</span> <span>文件创建工程</span> </font><font color=#f70938><span>MyDll<br><br></span><span>　　为了用</span> <span>.def</span> <span>文件创建</span> <span>DLL</span> <span>，请先删除上个例子创建的工程中的</span> <span>MyDll.h</span> <span>文件，保留</span> <span>MyDll.cpp</span> <span>并在该文件头删除</span> <span>＃i nclude MyDll.h</span> <span>语句，同时往该工程中加入一个文本文件，命名为</span> <span>MyDll.def</span> <span>，再在该文件中加入如下代码：</span> </font><span><br><br><font color=#f70938>LIBRARY MyDll<br>EXPORTS<br>Max<br>Min<br><br></font></span><font color=#f70938><span>　　其中</span> <span>LIBRARY</span> <span>语句说明该</span> <span>def</span> <span>文件是属于相应</span> <span>DLL</span> <span>的，</span> <span>EXPORTS</span> <span>语句下列出要导出的函数名称。我们可以在</span> <span>.def</span> <span>文件中的导出函数后加</span> <span>@n</span> <span>，如</span> <span>Max@1</span> <span>，</span> <span>Min@2</span> <span>，表示要导出的函数顺序号，在进行显式连时可以用到它。该</span> <span>DLL</span> <span>编译成功后，打开工程中的</span> <span>Debug</span> <span>目录，同样也会看到</span> <span>MyDll.dll</span> <span>和</span> <span>MyDll.lib</span> <span>文件。</span> </font><span><br><br></span><font color=#f70938><span>　　</span> <span>2</span> <span>．</span> <span>MFC AppWizard[dll]</span> <span>方式生成常规</span> <span>/</span> <span>扩展</span> </font><font color=#f70938><span>DLL<br><br></span><span>　　在</span> <span>MFC AppWizard[dll]</span> <span>下生成</span> <span>DLL</span> <span>文件又有三种方式，在创建</span> <span>DLL</span> <span>是，要根据实际情况选择创建</span> <span>DLL</span> <span>的方式。一种是常规</span> <span>DLL</span> <span>静态链接到</span> <span>MFC</span> <span>，另一种是常规</span> <span>DLL</span> <span>动态链接到</span> <span>MFC</span> <span>。两者的区别是：前者使用的是</span> <span>MFC</span> <span>的静态链接库，生成的</span> <span>DLL</span> <span>文件长度大，一般不使用这种方式，后者使用</span> <span>MFC</span> <span>的动态链接库，生成的</span> <span>DLL</span> <span>文件长度小；动态链接到</span> <span>MFC</span> <span>的规则</span> <span>DLL</span> <span>所有输出的函数应该以如下语句开始：</span> </font><font color=#f70938><span><br></span><span>　</span> </font></p>
<table cellPadding=0 width="100%" border=0 FCK__ShowTableBorders?>
    <tbody>
        <tr>
            <td>
            <p align=left><font color=#f70938><span>AFX_MANAGE_STATE(AfxGetStaticModuleState( )) //</span> <span>此语句用来正确地切换</span> <span>MFC</span> <span>模块状态</span> </font></p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><font color=#f70938><span>　　最后一种是</span> <span>MFC</span> <span>扩展</span> <span>DLL</span> <span>，这种</span> <span>DLL</span> <span>特点是用来建立</span> <span>MFC</span> <span>的派生类，</span> <span>Dll</span> <span>只被用</span> <span>MFC</span> <span>类库所编写的应用程序所调用。前面我们已经介绍过，</span> <span>Extension DLLs </span><span>和</span> <span>Regular DLLs</span> <span>不一样，它没有一个从</span> <span>CWinApp</span> <span>继承而来的类的对象，编译器默认了一个</span> <span>DLL</span> <span>入口函数</span> <span>DLLMain()</span> <span>作为对</span> <span>DLL</span> <span>的初始化，你可以在此函数中实现初始化</span> <span>,</span> <span>代码如下：</span> </font><span><br></span><span><font color=#f70938>　</font> </span></p>
<table cellPadding=0 width="100%" border=0 FCK__ShowTableBorders?>
    <tbody>
        <tr>
            <td>
            <p align=left><font color=#f70938><span>BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll</span> <span>，</span> <span>DWORD reason </span><span>，</span> </font><font color=#f70938><span>LPVOID flmpload)<br>{<br>switch(reason)<br>{<br>&#8230;&#8230;&#8230;&#8230;&#8230;//</span> <span>初始化代码；</span> </font><span><br><font color=#f70938>}<br>return true;<br>}</font> </span></p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><font color=#f70938><span>　　参数</span> <span>hinstDll</span> <span>存放</span> <span>DLL</span> <span>的句柄，参数</span> <span>reason</span> <span>指明调用函数的原因，</span> <span>lpReserved</span> <span>是一个被系统所保留的参数。对于隐式链接是一个非零值，对于显式链接值是零。</span> </font><span><br><br></span><font color=#f70938><span>　　在</span> <span>MFC</span> <span>下建立</span> <span>DLL</span> <span>文件，会自动生成</span> <span>def</span> <span>文件框架，其它与建立传统的</span> <span>Non-MFC DLL</span> <span>没有什么区别，只要在相应的头文件写入关键字</span> <span>_declspec(dllexport)</span> <span>函数类型和函数名等，或在生成的</span> <span>def</span> <span>文件中</span> <span>EXPORTS</span> <span>下输入函数名就可以了。需要注意的是在向其它开发人员分发</span> <span>MFC</span> <span>扩展</span> <span>DLL </span><span>时，不要忘记提供描述</span> <span>DLL</span> <span>中类的头文件以及相应的</span> <span>.LIB</span> <span>文件和</span> <span>DLL</span> <span>本身，此后开发人员就能充分利用你开发的扩展</span> <span>DLL</span> <span>了。<br><br>　应用程序使用<span>DLL</span><span>可以采用两种方式：一种是隐式链接，另一种是显式链接。在使用</span><span>DLL</span><span>之前首先要知道</span><span>DLL</span><span>中函数的结构信息。</span><span>Visual C++6.0</span><span>在</span><span>VCin</span><span>目录下提供了一个名为</span><span>Dumpbin.exe</span><span>的小程序，用它可以查看</span><span>DLL</span><span>文件中的函数结构。另外，</span><span>Windows</span><span>系统将遵循下面的搜索顺序来定位</span><span>DLL</span><span>：</span><span> 1</span><span>．包含</span><span>EXE</span><span>文件的目录，</span><span>2</span><span>．进程的当前工作目录，</span><span> 3</span><span>．</span><span>Windows</span><span>系统目录，</span><span> 4</span><span>．</span><span>Windows</span><span>目录，</span><span>5</span><span>．列在</span><span>Path</span><span>环境变量中的一系列目录。</span><span><br><br></span><span>　　</span><span>1</span><span>．隐式链接</span><span><br><br></span><span>　　隐式链接就是在程序开始执行时就将</span><span>DLL</span><span>文件加载到应用程序当中。实现隐式链接很容易，只要将导入函数关键字</span><span>_declspec(dllimport)</span><span>函数名等写到应用程序相应的头文件中就可以了。下面的例子通过隐式链接调用</span><span>MyDll.dll</span><span>库中的</span><span>Min</span><span>函数。首先生成一个项目为</span><span>TestDll</span><span>，在</span><span>DllTest.h</span><span>、</span><span>DllTest.cpp</span><span>文件中分别输入如下代码：</span><span><br></span><span>　</span></span> </font></p>
<p align=left><span>　</span> </p>
<p align=left>&nbsp;</p>
<table cellPadding=0 width="100%" border=0 FCK__ShowTableBorders?>
    <tbody>
        <tr>
            <td>
            <p align=left><span>//Dlltest.h<br>#pragma comment(lib</span> <span>，</span> <span>"MyDll.lib")<br>extern "C"_declspec(dllimport) int Max(int a,int b);<br>extern "C"_declspec(dllimport) int Min(int a,int b);<br>//TestDll.cpp<br>＃i nclude<br>＃i nclude"Dlltest.h"<br>void main()<br>{int a;<br>a=min(8,10)<br>printf("</span> <span>比较的结果为</span> <span>%d "</span> <span>，</span> <span>a);<br>}<br></span><span>　</span> </p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><span>　　在创建</span> <span>DllTest.exe</span> <span>文件之前，要先将</span> <span>MyDll.dll</span> <span>和</span> <span>MyDll.lib</span> <span>拷贝到当前工程所在的目录下面，也可以拷贝到</span> <span>windows</span> <span>的</span> <span>System</span> <span>目录下。如果</span> <span>DLL</span> <span>使用的是</span> <span>def</span> <span>文件，要删除</span> <span>TestDll.h</span> <span>文件中关键字</span> <span>extern "C"</span> <span>。</span> <span>TestDll.h</span> <span>文件中的关键字</span> <span>Progam commit</span> <span>是要</span> <span>Visual C+</span> <span>的编译器在</span> <span>link</span> <span>时，链接到</span> <span>MyDll.lib</span> <span>文件，当然，开发人员也可以不使用</span> <span>#pragma comment(lib</span> <span>，</span> <span>"MyDll.lib")</span> <span>语句，而直接在工程的</span> <span>Setting-&gt;Link</span> <span>页的</span> <span>Object/Moduls</span> <span>栏填入</span> <span>MyDll.lib</span> <span>既可。</span> <span><br><br></span><span>　　</span> <span>2</span> <span>．显式链接</span> <span><br><br></span><span>　　显式链接是应用程序在执行过程中随时可以加载</span> <span>DLL</span> <span>文件，也可以随时卸载</span> <span>DLL</span> <span>文件，这是隐式链接所无法作到的，所以显式链接具有更好的灵活性，对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序中用</span> <span>LoadLibrary</span> <span>或</span> <span>MFC</span> <span>提供的</span> <span>AfxLoadLibrary</span> <span>显式的将自己所做的动态链接库调进来，动态链接库的文件名即是上述两个函数的参数，此后再用</span> <span>GetProcAddress()</span> <span>获取想要引入的函数。自此，你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前，应该用</span> <span>FreeLibrary</span> <span>或</span> <span>MFC</span> <span>提供的</span> <span>AfxFreeLibrary</span> <span>释放动态链接库。下面是通过显式链接调用</span> <span>DLL</span> <span>中的</span> <span>Max</span> <span>函数的例子。</span> <span><br></span><span>　</span> </p>
<p align=left>&nbsp;</p>
<table cellPadding=0 width="100%" border=0 FCK__ShowTableBorders?>
    <tbody>
        <tr>
            <td>
            <p align=left><span>＃i nclude <br>＃i nclude<br>void main(void)<br>{<br>typedef int(*pMax)(int a,int b);<br>typedef int(*pMin)(int a,int b);<br>HINSTANCE hDLL;<br>PMax Max<br>HDLL=LoadLibrary("MyDll.dll");//</span> <span>加载动态链接库</span> <span>MyDll.dll</span> <span>文件；</span> <span><br>Max=(pMax)GetProcAddress(hDLL,"Max");<br>A=Max(5,8);<br>Printf("</span> <span>比较的结果为</span> <span>%d "</span> <span>，</span> <span>a);<br>FreeLibrary(hDLL);//</span> <span>卸载</span> <span>MyDll.dll</span> <span>文件；</span> <span><br>}</span> </p>
            </td>
        </tr>
    </tbody>
</table>
<p align=left><span>　　在上例中使用类型定义关键字</span> <span>typedef</span> <span>，定义指向和</span> <span>DLL</span> <span>中相同的函数原型指针，然后通过</span> <span>LoadLibray()</span> <span>将</span> <span>DLL</span> <span>加载到当前的应用程序中并返回当前</span> <span>DLL</span> <span>文件的句柄，然后通过</span> <span>GetProcAddress()</span> <span>函数获取导入到应用程序中的函数指针，函数调用完毕后，使用</span> <span>FreeLibrary()</span> <span>卸载</span> <span>DLL</span> <span>文件。在编译程序之前，首先要将</span> <span>DLL</span> <span>文件拷贝到工程所在的目录或</span> <span>Windows</span> <span>系统目录下。</span> <span><br><br></span><span>　　使用显式链接应用程序编译时不需要使用相应的</span> <span>Lib</span> <span>文件。另外，使用</span> <span>GetProcAddress()</span> <span>函数时，可以利用</span> <span>MAKEINTRESOURCE()</span> <span>函数直接使用</span> <span>DLL</span> <span>中函数出现的顺序号，如将</span> <span>GetProcAddress(hDLL,"Min")</span> <span>改为</span> <span>GetProcAddress(hDLL, MAKEINTRESOURCE(2))</span> <span>（函数</span> <span>Min()</span> <span>在</span> <span>DLL</span> <span>中的顺序号是</span> <span>2</span> <span>），这样调用</span> <span>DLL</span> <span>中的函数速度很快，但是要记住函数的使用序号，否则会发生错误。</span>&nbsp;</p>
<img src ="http://www.cppblog.com/xushaohua/aggbug/37783.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/xushaohua/" target="_blank">neter</a> 2007-12-04 13:44 <a href="http://www.cppblog.com/xushaohua/archive/2007/12/04/37783.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>