??xml version="1.0" encoding="utf-8" standalone="yes"?>久久91精品国产91久久户,九九99精品久久久久久,99久久无码一区人妻a黑http://www.shnenglu.com/xushaohua/category/2730.html如有恒,何须三更P半夜眠;最怕莫Q三天打g天晒|,竹篮打水一场空Q?/description>zh-cnTue, 17 Feb 2009 05:42:31 GMTTue, 17 Feb 2009 05:42:31 GMT60[转]Win32应用E序中进E间通信Ҏ分析与比?/title><link>http://www.shnenglu.com/xushaohua/archive/2009/02/17/74015.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Tue, 17 Feb 2009 03:18:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2009/02/17/74015.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/74015.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2009/02/17/74015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/74015.html</wfw:commentRss><trackback:ping>http://www.shnenglu.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>应用E序中进E间通信Ҏ分析与比?/strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "> </p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>1 </strong><strong>q程与进E通信</strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "> </p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">  q程是装入内存ƈ准备执行的程序,每个q程都有U有的虚拟地址I间Q由代码、数据以及它可利用的pȝ资源(如文件、管道等)l成。多q程/多线E是Windows操作pȝ的一个基本特征。Microsoft Win32应用~程接口(Application Programming Interface, API)提供了大量支持应用程序间数据׃n和交换的机制Q这些机制行使的zdUCؓq程间通信(InterProcess Communication, IPC)Q进E通信是指不同进E间q行数据׃n和数据交换?br>  正因Z用Win32 APIq行q程通信方式有多U,如何选择恰当的通信方式成为应用开发中的一个重要问题,下面本文对Win32中进E通信的几U方法加以分析和比较?/p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "> </p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>2 </strong><strong>q程通信Ҏ</strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>2.1 </strong><strong>文g映射</strong><br>  文g映射(Memory-Mapped Files)能ɘq程把文件内容当作进E地址区间一块内存那h对待。因此,q程不必使用文gI/O操作Q只需单的指针操作可d和修Ҏ件的内容?br>  Win32 API允许多个q程讉K同一文g映射对象Q各个进E在它自q地址I间里接收内存的指针。通过使用q些指针Q不同进E就可以L修改文g的内容,实现了对文g中数据的׃n?br>  应用E序有三U方法来使多个进E共享一个文件映对象?br>  (1)l承Q第一个进E徏立文件映对象,它的子进E承该对象的句柄?br>  (2)命名文g映射Q第一个进E在建立文g映射对象时可以给该对象指定一个名?可与文g名不?。第二个q程可通过q个名字打开此文件映对象。另外,W一个进E也可以通过一些其它IPC机制(有名道、邮件槽{?把名字传l第二个q程?br>  (3)句柄复制Q第一个进E徏立文件映对象,然后通过其它IPC机制(有名道、邮件槽{?把对象句柄传递给W二个进E。第二个q程复制该句柄就取得对该文g映射对象的访问权限?br>  文g映射是在多个q程间共享数据的非常有效ҎQ有较好的安全性。但文g映射只能用于本地机器的进E之_不能用于|络中,而开发者还必须控制q程间的同步?br><strong>2.2 </strong><strong>׃n内存</strong><br>  Win32 API中共享内?Shared Memory)实际是文g映射的一U特D情c进E在创徏文g映射对象时用0xFFFFFFFF来代替文件句?HANDLE)Q就表示了对应的文g映射对象是从操作pȝ面文g讉K内存Q其它进E打开该文件映对象就可以讉K该内存块。由于共享内存是用文件映实现的Q所以它也有较好的安全性,也只能运行于同一计算Z的进E之间?br><strong>2.3 </strong><strong>匿名道</strong><br>  道(Pipe)是一U具有两个端点的通信通道Q有一端句柄的q程可以和有另一端句柄的q程通信。管道可以是单向Q一端是只读的,另一端点是只写的Q也可以是双向的一道的两端点既可M可写?br>  匿名道(Anonymous Pipe)是在父进E和子进E之_或同一父进E的两个子进E之间传输数据的无名字的单向道。通常qq程创徏道Q然后由要通信的子q程l承通道的读端点句柄或写端点句柄Q然后实现通信。父q程q可以徏立两个或更多个承匿名管道读和写句柄的子q程。这些子q程可以使用道直接通信Q不需要通过父进E?br>  匿名道是单Z实现子进E标准I/O重定向的有效ҎQ它不能在网上用,也不能用于两个不相关的进E之间?br><strong>2.4 </strong><strong>命名道</strong><br>  命名道(Named Pipe)是服务器q程和一个或多个客户q程之间通信的单向或双向道。不同于匿名道的是命名道可以在不相关的进E之间和不同计算Z间用,服务器徏立命名管道时l它指定一个名字,Mq程都可以通过该名字打开道的另一端,Ҏl定的权限和服务器进E通信?br>  命名道提供了相对简单的~程接口Q通过|络传输数据q不比同一计算Z两进E之间通信更困难,不过如果要同时和多个q程通信它就力不从心了?br><strong>2.5 </strong><strong>邮g?/strong><br>  邮g?Mailslots)提供q程间单向通信能力QQ何进E都能徏立邮件槽成ؓ邮g槽服务器。其它进E,UCؓ邮g槽客P可以通过邮g槽的名字l邮件槽服务器进E发送消息。进来的消息一直放在邮件槽中,直到服务器进E读取它为止。一个进E既可以是邮件槽服务器也可以是邮件槽客户Q因此可建立多个邮g槽实现进E间的双向通信?br>  通过邮g槽可以给本地计算Z的邮件槽、其它计机上的邮g槽或指定|络区域中所有计机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超q?00字节Q非q播消息的长度则受邮件槽服务器指定的最大消息长度的限制?br>  邮g槽与命名道怼Q不q它传输数据是通过不可靠的数据?如TCP/IP协议中的UDP?完成的,一旦网l发生错误则无法保证消息正确地接Ӟ而命名管道传输数据则是徏立在可靠q接基础上的。不q邮件槽有简化的~程接口和给指定|络区域内的所有计机q播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一U选择?br><strong>2.6 </strong><strong>剪脓?/strong><br>  剪脓?Clipped Board)实质是Win32 API中一l用来传输数据的函数和消息,为Windows应用E序之间q行数据׃n提供了一个中介,Windows已徏立的剪切(复制)Q粘贴的机制Z同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时Q应用程序把选取的数据用一U或多种格式攑֜剪脓板上。然后Q何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择适合自己的格式?br>  剪脓板是一个非常松散的交换媒介Q可以支持Q何数据格式,每一格式׃无符h数标识,Ҏ?预定?剪脓板格式,该值是Win32 API定义的常量;寚w标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板q行交换的数据只需在数据格式上一致或都可以{化ؓ某种格式p。但剪脓板只能在ZWindows的程序中使用Q不能在|络上用?br><strong>2.7 </strong><strong>动态数据交?/strong><br>  动态数据交?DDE)是用共享内存在应用E序之间q行数据交换的一U进E间通信形式。应用程序可以用DDEq行一ơ性数据传输,也可以当出现新数据时Q通过发送更新值在应用E序间动态交换数据?br>  DDE和剪贴板一h支持标准数据格式(如文本、位囄)Q又可以支持自己定义的数据格式。但它们的数据传输机制却不同Q一个明昑֌别是剪脓板操作几乎L用作对用h定操作的一ơ性应{-如从菜单中选择Paste命o。尽DDE也可以由用户启动Q但它l发挥作用一般不必用戯一步干预。DDE有三U数据交换方式:<br>  (1) 冷链Q数据交换是一ơ性数据传输,与剪贴板相同?br>  (2) 温链Q当数据交换时服务器通知客户Q然后客户必请求新的数据?br>  (3) 热链Q当数据交换时服务器自动l客户发送数据?br>  DDE交换可以发生在单机或|络中不同计机的应用程序之间。开发者还可以定义定制的DDE数据格式q行应用E序之间特别目的IPCQ它们有更紧密耦合的通信要求。大多数ZWindows的应用程序都支持DDE?br><strong>2.8 </strong><strong>对象q接与嵌?/strong><br>  应用E序利用对象q接与嵌?OLE)技术管理复合文?由多U数据格式组成的文档)QOLE提供使某应用E序更容易调用其它应用程序进行数据编辑的服务。例如,OLE支持的字处理器可以嵌套电子表|当用戯~辑电子表格时OLE库可自动启动电子表格~辑器。当用户退出电子表格编辑器Ӟ该表格已在原始字处理器文档中得到更新。在q里电子表格~辑器变成了字处理器的扩展,而如果用DDEQ用戯昑ּ地启动电子表格编辑器?br>  同DDE技术相同,大多数基于Windows的应用程序都支持OLE技术?br><strong>2.9 </strong><strong>动态连接库</strong><br>  Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进E共享,q就又给q程间通信开辟了一条新的途径Q当然访问时要注意同步问题?br>  虽然可以通过DLLq行q程间数据共享,但从数据安全的角度考虑Q我们ƈ不提倡这U方法,使用带有讉K权限控制的共享内存的Ҏ更好一些?br><strong>2.10 </strong><strong>q程q程调用</strong><br>  Win32 API提供的远E过E调?RPC)使应用程序可以用远E调用函敎ͼq在网l上用RPCq行q程通信像函数调用那样单。RPC既可以在单机不同q程间用也可以在网l中使用?br>  ׃Win32 API提供的RPC服从OSF-DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过Win32 API~写的RPC应用E序能与其它操作pȝ上支持DEC的RPC应用E序通信。用RPC开发者可以徏立高性能、紧密耦合的分布式应用E序?br><strong>2.11 NetBios</strong><strong>函数</strong><br>  Win32 API提供NetBios函数用于处理低|络控制Q这主要是ؓIBM NetBiospȝ~写与Windows的接口。除非那些有Ҏ低|络功能要求的应用程序,其它应用E序最好不要用NetBios函数来进行进E间通信?br><strong>2.12 Sockets</strong><br>  Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口例定义的一套Windows下的|络~程接口。除了Berkeley Socket原有的库函数以外Q还扩展了一l针对Windows的函敎ͼ使程序员可以充分利用Windows的消息机制进行编E?br>  现在通过Sockets实现q程通信的网l应用越来越多,q主要的原因是Sockets的跨q_性要比其它IPC机制好得多,另外WinSock 2.0不仅支持TCP/IP协议Q而且q支持其它协?如IPX)。Sockets的唯一~点是它支持的是底层通信操作Q这使得在单机的q程间进行简单数据传递不太方便,q时使用下面介l的WM_COPYDATA消息更合适些?br><strong>2.13 WM_COPYDATA</strong><strong>消息</strong><br>  WM_COPYDATA是一U非常强大却鲜ؓ人知的消息。当一个应用向另一个应用传送数据时Q发送方只需使用调用SendMessage函数Q参数是目的H口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息Q这h发双方就实现了数据共享?br>  WM_COPYDATA是一U非常简单的ҎQ它在底层实际上是通过文g映射来实现的。它的缺Ҏ灉|性不高,q且它只能用于Windowsq_的单机环境下?/p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "> </p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; "><strong>3 </strong><strong>l束?/strong></p><p style="line-height: 150%; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; ">  Win32 API为应用程序实现进E间通信提供了如此多U选择ҎQ那么开发者如何进行选择呢?通常在决定用哪UIPCҎ之前应考虑下一些问题,如应用程序是在网l环境下q是在单机环境下工作{?/p></span> <img src ="http://www.shnenglu.com/xushaohua/aggbug/74015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2009-02-17 11:18 <a href="http://www.shnenglu.com/xushaohua/archive/2009/02/17/74015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BCD?/title><link>http://www.shnenglu.com/xushaohua/archive/2007/12/24/39499.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Mon, 24 Dec 2007 04:34:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2007/12/24/39499.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/39499.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2007/12/24/39499.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/39499.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/xushaohua/services/trackbacks/39499.html</trackback:ping><description><![CDATA[即BCD代码。Binary-Coded Decimal‎Q简UBCDQ称BCD码或?十进制代码,亦称二进码十q数。是一U?a target=_blank><u><font color=#0000ff>二进?/font></u></a>的数字编码Ş式,用二q制~码?a target=_blank><u><font color=#0000ff>十进?/font></u></a>代码。这U编码Ş式利用了四个位元来储存一个十q制的数码,使二q制和十q制之间的{换得以快Lq行。这U编码技巧,最常用于会计系l的设计里,因ؓ会计制度l常需要对很长的数字串作准的计算。相对于一般的点式记数法Q采用BCD码,既可保存数值的_度,又可免却使电脑作点q算时所耗费的时间。此外,对于其他需要高_度的计算QBCD~码亦很常用?br><br>׃十进制数共有0???#8230;…?十个数码Q因此,臛_需?位二q制码来表示1位十q制数?位二q制码共?^4=16U码l,在这16U代码中Q可以Q?0U来表示10个十q制数码Q共有N=16Q?Q?6-10Q!U等?.9乘以10?0ơ方U方案。常用的BCD代码列于末?br><br><strong>常用BCD~码方式</strong><br>最常用的BCD~码Q就是?0"?9"q十个数值的二进码来表示。这U编码方式,在中国大陆称之ؓ“8421?#8221;。除此以外,对应不同需求,各h亦开发了不同的编码方法,以适应不同的需求。这些编码,大致可以分成有权码和无权码两U:<br><br>有权BCD码,如:8421(最常用)?421?421… <br>无权BCD码,如:?码、格L… <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> </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> </p> </td> </tr> </tbody> </table> </div> <p> </p> </td> </tr> </tbody> </table> </div> <span><font size=3>非压~式和压~式BCD?br>    BCD又分ZU,非压~式和压~式两种?br>    前面q种81存成 “08,01” 是非压羃式,而压~式会存?“81h” (直接以十六进制储?。非压羃的BCD码只有低四位有效Q而压~的BCD码则高四位也用上了Q就是说一个字节有两个BCD码。BCD是用0?表示十进Ӟ?000表示0Q?001表示1Q?010表示2。而压~的BCD是用00表示0Q?1表示1Q?0表示2Q?10表示3{?br>    例:<br>    1234表示成非压羃的BCD码是00000001000000100000001100000100Q也是0x01020304Q而压~BCD码则表示?001001000110100Q也是0x1234?br>    但压~的BCDq不固定Q可看情况而定Q所要的是用最的位数表示可能多的数?/font><br></span> <img src ="http://www.shnenglu.com/xushaohua/aggbug/39499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2007-12-24 12:34 <a href="http://www.shnenglu.com/xushaohua/archive/2007/12/24/39499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]~写高效的数据库q接?/title><link>http://www.shnenglu.com/xushaohua/archive/2007/12/21/39172.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Thu, 20 Dec 2007 17:42:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2007/12/21/39172.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/39172.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2007/12/21/39172.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/39172.html</wfw:commentRss><trackback:ping>http://www.shnenglu.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>q接?/font> <li><font face="Times New Roman" size=2>引用记数</font> <li><font face="Times New Roman" size=2>多线E?/font> <li><font face="Times New Roman" size=2>Timerc运行基?/font> <li><font face="Times New Roman" size=2>C#.Net</font>   </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>pȝ分析?/font> <li><font face="Times New Roman" size=2>模块设计?/font> <li>有一定功底的E序?</li> </ul> <p>目录</p> <ul> <li><font size=2>引言</font> <ol> <li><font face="Times New Roman" size=2>数据库连接池QConnection PoolQ的工作原理</font> </li> </ol> <li><font face="Times New Roman" size=2>q接池关键问题分?/font> <ol> <li><font face="Times New Roman" size=2>q发问题</font> <li><font face="Times New Roman" size=2>事务处理</font> <li><font face="Times New Roman" size=2>q接池的分配与释?/font> <li><font face="Times New Roman" size=2>q接池的配置与维?/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> 理q接?/div> </div> </li> </ol> <li> <div align=left> <div align=left>l合代码说明</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>甌 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>如何定q接是否失效</font></div> <li> <div align=left><font size=2>使用U程理q接?/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> <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>qh作生数据库操作</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>关闭应用E序</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>  在服务器端程序设计上与数据库的操作显得十分重?因ؓ你要处理的数据操作十分巨?如果频繁创徏数据库连接频J关闭数据库q接则会引v效率低下甚至引发E序崩溃.<br>  也许我们可以有另一U操作数据库的Ş?我们可以在程序运行时打开一个数据库q接,让这个连接永久存在直到程?M',那么q样做也有不安全隐患,我们知道一个对象存在时间越长或被用次数越多则它表现的不E_,着不稳定因素是׃对象内部可能存在的潜在设计问题?对于数据库连接对象道理也一?我们不能保证一个Connection对象里面能一炚w题不存在.所以我们也不敢长时间将它长旉占用内存.<br></font><font face="Times New Roman" size=2>  既然有这么多的问题由此我们需要一个能帮我们维护数据库q接的东?它就?font color=#ff0000>q接?/font>,|上有很多的q接池例?但是多数都是单的例子,或者介l比较复杂的q接池原?没有一个比较完整介l和实现q接池的例子.q里׃l你如何自己制作一个连接池.<br></font><font face="Times New Roman" size=2>  对于׃n资源Q有一个很著名的设计模式:资源池(Resource PoolQ。该模式正是Z解决资源的频J分配﹑释放所造成的问题。ؓ解决我们的问题,可以采用数据库连接池技术。数据库q接池的基本思想是为数据库q接建立一?#8220;~冲?#8221;。预先在~冲池中攑օ一定数量的q接Q当需要徏立数据库q接Ӟ只需?#8220;~冲?#8221;中取Z个,使用完毕之后再放回去。我们可以通过讑֮q接池最大连接数来防止系l无的与数据库q接。更为重要的是我们可以通过q接池的理机制监视数据库的q接的数量﹑使用情况Qؓpȝ开发﹑试及性能调整提供依据。连接池的基本工作原理见下图?/font></p> <p align=center><font face="Times New Roman" size=3>数据库连接池QConnection PoolQ的工作原理</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> q接池关键问题分?/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>  Z使连接管理服务具有最大的通用性,必须考虑多线E环境,卛_ƈ发问题。这个问题相Ҏ较好解决Q因为各个语a自n提供了对q发理的支持像java,c#{等Q用synchronized(java)lock(C#)关键字即可确保线E是同步的。用方法可以参考,相关文献?/font></p> <p align=left><font face="Times New Roman" size=2>  Q、事务处?/font></p> <p align=left><font face="Times New Roman" size=2>  我们知道Q事务具有原子性,此时要求Ҏ据库的操作符?#8220;ALL-ALL-NOTHING”原则,卛_于一lSQL语句要么全做Q要么全不做?br></font><font face="Times New Roman" size=2>  我们知道当2个线E公用一个连接Connection对象Q而且各自都有自己的事务要处理时候,对于q接池是一个很头疼的问题,因ؓ即ConnectioncL供了相应的事务支持,可是我们仍然不能定那个数据库操作是对应那个事务的,q是׃我们有2个线E都在进行事务操作而引L。ؓ此我们可以?/font><font face="Times New Roman" size=2>每一个事务独占一个连接来实现Q虽然这U方法有Ҏ费连接池资源但是可以大大降低事务理的复杂性?/font></p> <p align=left><font face="Times New Roman" size=2>  Q、连接池的分配与释放</font></p> <p align=left><font face="Times New Roman" size=2>  q接池的分配与释放,对系l的性能有很大的影响。合理的分配与释放,可以提高q接的复用度Q从而降低徏立新q接的开销Q同时还可以加快用户的访问速度?br></font><font face="Times New Roman" size=2>  对于q接的管理可使用一个List。即把已l创建的q接都放入List中去l一理。每当用戯求一个连接时Q系l检查这个List中有没有可以分配的连接。如果有把那个最合适的q接分配l他<font color=#339966>Q如何能扑ֈ最合适的q接文章在关键议题中指?/font>Q;如果没有抛Z个异常给用户QList中连接是否可以被分配׃个线E来专门理捎后我会介绍q个U程的具体实现?</font></p> <p> <font face="Times New Roman" size=2>  Q、连接池的配|与l护</font></p> <p align=left><font face="Times New Roman" size=2>  q接池中到底应该攄多少q接Q才能ɾpȝ的性能最佻Ipȝ可采取设|最连接数QminConnectionQ和最大连接数QmaxConnectionQ等参数来控制连接池中的q接。比方说Q最连接数是系l启动时q接池所创徏的连接数。如果创多,则系l启动就慢,但创建后pȝ的响应速度会很快;如果创徏q少Q则pȝ启动的很快,响应h却慢。这P可以在开发时Q设|较的最连接数Q开发v来会快,而在pȝ实际使用时设|较大的Q因样对讉K客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体讄多少Q要看系l的讉K量,可通过软g需求上得到?br></font><font face="Times New Roman" size=2>  如何保q接池中的最连接数呢?有动态和静态两U策略。动态即每隔一定时间就对连接池q行,如果发现q接数量于最连接数Q则补充相应数量的新q接,以保证连接池的正常运转。静态是发现I闲q接不够时再L查?</font></p> <img src ="http://www.shnenglu.com/xushaohua/aggbug/39172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2007-12-21 01:42 <a href="http://www.shnenglu.com/xushaohua/archive/2007/12/21/39172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[MSDN]使用q接?/title><link>http://www.shnenglu.com/xushaohua/archive/2007/12/21/39171.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Thu, 20 Dec 2007 17:33:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2007/12/21/39171.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/39171.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2007/12/21/39171.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/39171.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/xushaohua/services/trackbacks/39171.html</trackback:ping><description><![CDATA[<div id="37rlhxh" class=title>使用q接?!----></div> <!--content type: DocStudio. Transform: devdiv2mtps.xslt.--> <div class="vjb7rvl" id=mainSection> <div class="1vtlflb" id=mainBody> <p> <p>q接到数据库服务器通常由几个需要很长时间的步骤l成。必d立物理通道Q例如套接字或命名管道)Q必M服务器进行初ơ握手,必须分析q接字符串信息,必须由服务器对连接进行n份验证,必须q行查以便在当前事务中登讎ͼ{等?</p> <p>实际上,大多数应用程序仅使用一个或几个不同的连接配|。这意味着在执行应用程序期_许多相同的连接将反复地打开和关闭。ؓ了打开的连接成本最低,ADO.NET 使用UCؓ<em>q接?/em>的优化方法?/p> <p>q接池减新q接需要打开的次数?em>池进E?/em>保持物理q接的所有权。通过为每个给定的q接配置保留一l活动连接来理q接。只要用户在q接上调?OpenQ池q程׃查池中是否有可用的连接。如果某个池q接可用Q会该q接q回l调用者,而不是打开新连接。应用程序在该连接上调用 Close Ӟ池进E会连接返回到zdq接池集中,而不是真正关闭连接。连接返回到池中之后Q即可在下一?Open 调用中重复用?/p> <p>只有配置相同的连接可以徏立池q接。ADO.NET 同时保留多个池,每个配置一个池。连接由q接字符串以?Windows 标识Q在使用集成的安全性时Q分为多个池?/p> <p>池连接可以大大提高应用程序的性能和可~放性。默认情况下QADO.NET 中启用连接池。除非显式禁用,否则Q连接在应用E序中打开和关闭时Q池q程对q接q行优化。还可以提供几个q接字符串修饰符来控制连接池的行为。有x多信息,请参见本主题后面?#8220;使用q接字符串关键字控制q接?#8221;?/p> <h1 class=heading>池的创徏和分?/h1> <div id="tvlfjrp" class=seeAlsoNoToggleSection id=sectionSection0> <p>在初ơ打开q接Ӟ根据完全匹配算法创接池Q该法池与连接中的连接字W串兌。每个连接池与不同的q接字符串关联。打开新连接时Q如果连接字W串q与现有池完全匚wQ将创徏一个新池。按q程、按应用E序域、按q接字符串以及(在用集成的安全性时Q按 Windows 标识来徏立池q接?/p> <p>在以?C# CZ中创Z三个新的 <a id=ctl00_rs1_mainContentContainer_ctl01 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);" tabIndex=0 ><u><font color=#0000ff>SqlConnection</font></u></a> 对象Q但是管理时只需要两个连接池。注意,Ҏ?<tt>Initial Catalog</tt> 分配的|W一个和W二个连接字W串有所不同?/p> <div id="r73rlfx" 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 在连接字W串中未指定或指定ؓӞ池中的连接将在一D|间不zd后关闭。但是,如果指定?MinPoolSize 大于Ӟ?AppDomain 被卸载ƈ且进E结束之前,q接池不会被破坏。非zd或空池的l护只需要最的pȝ开销?/p> <div id="xzjl7nl" 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>如果发生致命错误Q例如故障{UL注册表中的别名更改)Q池自动清除?/p> </td> </tr> </tbody> </table> </div> </div> <h1 class=heading>dq接</h1> <div id="xnrrvdn" class=seeAlsoNoToggleSection id=sectionSection1> <p>q接池是为每个唯一的连接字W串创徏的。当创徏一个池后,创建多个连接对象ƈ其d到该池中Q以满最池大小的要求。连接根据需要添加到池中Q但是不能超q指定的最大池大小Q默认gؓ 100Q。连接在关闭或断开旉攑֛池中?</p> <p>在请?SqlConnection 对象Ӟ如果存在可用的连接,从池中获取该对象。连接要可用Q必L使用Q具有匹配的事务上下文或未与M事务上下文关联,q且h与服务器的有效链接?/p> <p>q接池进E通过在连接释攑֛池中旉新分配连接,来满些连接请求。如果已辑ֈ最大池大小且不存在可用的连接,则该h会排队。然后,池进E尝试重新徏立Q何连接,直到到达时旉Q默认gؓ 15 U)。如果池q程在连接超时之前无法满求,引发异常?</p> <div id="ljvz9vf" 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>我们您在使用完连接时一定要关闭q接Q以便连接可以返回池。要关闭q接Q可以?Connection 对象?Close ?Dispose ҎQ也可以通过?C# ?using 语句中或?Visual Basic ?Using 语句中打开所有连接。不是显式关闭的q接可能不会d或返回到池中。例如,如果q接已超围但没有昑ּ关闭Q则仅当辑ֈ最大池大小而该q接仍然有效Ӟ该连接才会返回到q接池中。有x多信息,请参?Visual Basic ?a id=ctl00_rs1_mainContentContainer_ctl03 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);" tabIndex=0 ><u><font color=#0000ff>using 语句QC# 参考)</font></u></a>?a id=ctl00_rs1_mainContentContainer_ctl04 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);" tabIndex=0 ><u><font color=#0000ff>如何Q释攄l资?/font></u></a>?/p> </td> </tr> </tbody> </table> </div> <div id="zvzjvfx" 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 或Q何其他托对象调?Close ?Dispose。在l结器中Q仅释放cȝ接拥有的非托资源。如果类不拥有Q何非托管资源Q则不要在类定义中包?Finalize Ҏ。有x多信息,请参?a id=ctl00_rs1_mainContentContainer_ctl05 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);" tabIndex=0 ><u><font color=#0000ff>垃圾回收</font></u></a>?/p> </td> </tr> </tbody> </table> </div> </div> <h1 class=heading>U除q接</h1> <div id="9t3tbt3" class=seeAlsoNoToggleSection id=sectionSection2> <p>q接池进E定期扫描连接池Q查找没有通过 Close ?Dispose 关闭的未用连接,q新徏立找到的q接。如果应用程序没有显式关闭或断开其连接,q接池进E可能需要很长时间才能重新徏立连接,所以,最好确保在q接中显式调?Close ?Dispose?/p> <p>如果q接长时间空Ԍ或池q程到与服务器的连接已断开Q连接池q程会将该连接从池中U除。注意,只有在尝试与服务器进行通信之后才能到断开的连接。如果发现某q接不再q接到服务器Q则会将其标Cؓ无效。无效连接只有在关闭或重新徏立后Q才会从q接池中U除?/p> <p>如果存在与已消失的服务器的连接,那么即ɘq接池管理程序未到已断开的连接ƈ其标记为无效,仍有可能此q接从池中取出。这U情冉|因ؓ查连接是否仍有效的系l开销造成与服务器的另一ơ往q,从而抵消了池进E的优势。发生此情况Ӟ初次试使用该连接将连接是否曾断开Qƈ引发异常?/p> </div> <h1 class=heading>清除?/h1> <div id="1l5pxld" class=seeAlsoNoToggleSection id=sectionSection3> <p>ADO.NET 2.0 引入了两U新的方法来清除池:<a id=ctl00_rs1_mainContentContainer_ctl06 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl06',this);" tabIndex=0 ><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 ><u><font color=#0000ff>ClearPool</font></u></a>。ClearAllPools 清除l定提供E序的连接池QClearPool 清除与特定连接关联的q接池。如果在调用时连接正在用,进行相应的标记。连接关闭时Q将被丢弃,而不是返回池中?/p> </div> <h1 class=heading>事务支持</h1> <div id="1jbld7t" class=seeAlsoNoToggleSection id=sectionSection4> <p>q接是根据事务上下文来从池中取出q进行分配的。除非在q接字符串中指定?<tt>Enlist=false</tt>Q否则,q接池将保q接?<a id=ctl00_rs1_mainContentContainer_ctl08 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl08',this);" tabIndex=0 ><u><font color=#0000ff>Current</font></u></a> 上下文中登记。如果连接用登记的 System.Transactions 事务关闭q返回池中,q接保留在池中Q以便用相?System.Transactions 事务对该q接池的下一ơ请求将q回相同的连接。如果该事务没有可用q接Q在该连接打开Ӟ自动注册该q接?/p> <p>当连接关闭时Q它被释放回池中,q根据其事务上下文放入相应的子部分。因此,即分布式事务仍然挂P仍可以关闭该q接而不会生成错误。这P您就可以在随后提交或中止分布式事务?/p> </div> <h1 class=heading>使用q接字符串关键字控制q接?/h1> <div id="l31fvp3" class=seeAlsoNoToggleSection id=sectionSection5> <p>SqlConnection 对象?ConnectionString 属性支持连接字W串?值对Q可以用于调整连接池逻辑的行为。有x多信息,请参?<a id=ctl00_rs1_mainContentContainer_ctl09 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl09',this);" tabIndex=0 ><u><font color=#0000ff>ConnectionString</font></u></a>?/p> </div> <h1 class=heading>池碎?/h1> <div id="ltrztl3" class=seeAlsoNoToggleSection id=sectionSection6> <p>池碎片是许多 Web 应用E序中的一个常见问题,应用E序可能会创建大量在q程退出后才会释放的池。这P打开大量的连接,占用许多内存Q从而媄响性能?/p> <h3 class=subHeading>因ؓ集成安全性生的池碎?/h3> <div id="xtltb97" class=subSection> <p>q接Ҏq接字符串以及用h识来建立池连接。因此,如果使用|站上的基本w䆾验证?Windows w䆾验证以及集成的安全登录,每个用户获得一个池。尽这样可以提高单个用L后箋数据库请求的性能Q但是该用户无法利用其他用户建立的连接。这栯使每个用戯生一个与数据库服务器的连接。这对特?Web 应用E序l构会生副作用Q因为开发h员需要衡量安全性和审计要求?/p> </div> <h3 class=subHeading>因ؓ许多数据库生的池碎?/h3> <div id="vh3zr73" class=subSection> <p>许多 Internet 服务提供商在一台服务器上托多个网站。他们可能用单个数据库认H体w䆾验证dQ然后ؓ该用h用户l打开与特定数据库的连接。与w䆾验证数据库的q接徏立池q接Q供每个用户使用。但是,每个数据库的q接存在一个独立的池,因此增加了与服务器的q接数?/p> <p>q也会对应用E序设计产生副作用。但是,可以通过一个相对简单的方式避免此副作用Q而又不会影响q接 SQL Server 时的安全性。不是ؓ每个用户或组q接独立的数据库Q而是q接到服务器上的相同数据库,然后执行 Transact-SQL USE 语句来切换ؓ所需的数据库。以下代码段演示入如何创Z master 数据库的初始q接Q然后切换到 <tt>databaseName</tt> 字符串变量中指定的所需数据库?/p> <div id="hzjd7x7" 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>  <span style="COLOR: maroon">"Server=MSSQL1;uid=xxx;pwd=xxx;database=master"</span>))<br>  {<br>    connection.Open();<br>    command.ExecuteNonQuery(<span style="COLOR: maroon">"USE "</span> + databaseName);<br>  }<br></div> </div> </div> <h1 class=heading>应用E序角色和连接池</h1> <div id="bl7f79z" class=seeAlsoNoToggleSection id=sectionSection7> <p>通过调用 sp_setapprole pȝ存储q程ȀzM SQL Server 应用E序角色之后Q该q接的安全上下文无法重置。但是,如果启用了池Q连接将q回池,在重复用池q接时会出错?/p> <p>如果使用的是 SQL Server 应用E序角色Q我们徏议您在连接字W串中ؓ应用E序用q接池。有x多信息,请参见知识库文章“<a id=ctl00_rs1_mainContentContainer_ctl12 onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl12',this);" tabIndex=0 ><u><font color=#0000ff>SQL application role errors with OLE DB resource pooling</font></u></a>”?/p> </div> </div> </div> <img src ="http://www.shnenglu.com/xushaohua/aggbug/39171.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2007-12-21 01:33 <a href="http://www.shnenglu.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.shnenglu.com/xushaohua/archive/2007/12/04/37817.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Tue, 04 Dec 2007 12:56:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2007/12/04/37817.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/37817.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2007/12/04/37817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/37817.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/xushaohua/services/trackbacks/37817.html</trackback:ping><description><![CDATA[中国U学院光甉|术研I所 <a href="mailto:yzyseal@126.com"><u><font color=#0000ff>游志?/font></u></a><br><br>在工业生产控制系l中Q有许多需要定时完成的操作Q如定时昄当前旉Q定时刷新屏q上的进度条Q上?wbr> 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的实时控制系l和数据采集pȝ中,更需要精定时操作?br>  众所周知QWindows 是基于消息机制的pȝQQ何事件的执行都是通过发送和接收消息来完成的?wbr> q样带来了一些问题,如一旦计机的CPU被某个进E占用,或系l资源紧张时Q发送到消息队列<wbr> 中的消息暂时被挂vQ得不到实时处理。因此,不能单地通过Windows消息引发一个对定时要求<wbr> 严格的事件。另外,׃在Windows中已l封装了计算机底层硬件的讉KQ所以,要想通过直接利用<wbr> 讉Kg来完成精定Ӟ也比较困难。所以在实际应用Ӟ应针对具体定时精度的要求Q采取相?应的定时Ҏ?br>  VC中提供了很多关于旉操作的函敎ͼ利用它们控制E序能够_地完成定时和计时操作。本文详l介l了<wbr> VC中基于Windows的精定时的七种方式Q如下图所C:<br><img height=419 alt="" src="http://www.shnenglu.com/images/cppblog_com/xushaohua/MultiTimerDemoimg.gif" width=639 border=0><br>图一 囑փ描述 <br><br>  方式一QVC中的WM_TIMER消息映射能进行简单的旉控制。首先调用函数SetTimer()讄定时<wbr> 间隔Q如SetTimer(0,200,NULL)即ؓ讄200ms的时间间隔。然后在应用E序中增加定时响应函?wbr> OnTimer()Qƈ在该函数中添加响应的处理语句Q用来完成到辑֮时时间的操作。这U定时方法非?wbr> 单,可以实现一定的定时功能Q但其定时功能如同Sleep()函数的g时功能一P_ֺ非常低,最?wbr> 计时_ֺ仅ؓ30msQCPU占用低,且定时器消息在多d操作pȝ中的优先U很低,不能得到及时?wbr> 应,往往不能满实时控制环境下的应用。只可以用来实现诸如位图的动态显C等对定时精度要求不高的情况。如CZ工程中的Timer1?<br>  方式二:VC中用sleep()函数实现延时Q它的单位是msQ如延时2U,用sleep(2000)。精度非?wbr> 低,最计时精度仅?0msQ用sleep函数的不利处在于延时期间不能处理其他的消息,如果旉?wbr> 长,好象死ZPCPU占用率非帔RQ只能用于要求不高的延时E序中。如CZ工程中的Timer2?br>  方式三:利用COleDateTimecdCOleDateTimeSpancȝ合WINDOWS的消息处理过E来实现U延时。如CZ工程中的Timer3和Timer3_1。以下是实现2U的延时代码Q?br>COleDateTime      start_time = COleDateTime::GetCurrentTime();<br>      COleDateTimeSpan  end_time= COleDateTime::GetCurrentTime()-start_time;<br>      while(end_time.GetTotalSeconds()< 2) //实现延时2U?br>     { <br>              MSG   msg;<br>              GetMessage(&msg,NULL,0,0);<br>              TranslateMessage(&msg); <br>              DispatchMessage(&msg);<br>              <br>             //以上四行是实现在延时或定时期间能处理其他的消息,<br>       //虽然q样可以降低CPU的占有率Q?br>             //但降低了延时或定时精度,实际应用中可以去掉?br>             end_time = COleDateTime::GetCurrentTime()-start_time;<br>      }//q样在g时的时候我们也能够处理其他的消息?nbsp;     <br>方式四:在精度要求较高的情况下,VC中可以利用GetTickCount()函数Q该函数的返回值是<wbr>  DWORD型,表示以ms为单位的计算机启动后l历的时间间隔。精度比WM_TIMER消息映射高,在较<wbr> 短的定时中其计时误差?5msQ在较长的定时中其计时误差较低,如果定时旉太长Q就好象L一PCPU占用率非帔RQ只能用于要求不高的延时E序中。如CZ工程中的Timer4和Timer4_1。下列代码可以实?0ms的精定Ӟ<br> <pre> DWORD dwStart = GetTickCount(); DWORD dwEnd = dwStart; do { dwEnd = GetTickCount()-dwStart; }while(dwEnd <50);</pre> ZGetTickCount()函数在g时或定时期间能处理其他的消息Q可以把代码改ؓQ?br> <pre> DWORD dwStart = GetTickCount(); DWORD dwEnd = dwStart; do { MSG msg; GetMessage(&msg,NULL,0,0); TranslateMessage(&msg); DispatchMessage(&msg); dwEnd = GetTickCount()-dwStart; }while(dwEnd <50);</pre> 虽然q样可以降低CPU的占有率Qƈ在g时或定时期间也能处理其他的消息,但降低了延时或定时精度?br>  方式五:与GetTickCount()函数cM的多媒体定时器函数DWORD timeGetTime(void)Q该函数定时_?wbr> 度ؓmsU,q回从Windows启动开始经q的毫秒数。微软公司在其多媒体Windows中提供了_定时器的?wbr> 层API持,利用多媒体定时器可以很精地dpȝ的当前时_q且能在非常_的时间间隔内完成一<wbr> 个事件、函数或q程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须?Winmm.lib <wbr> ?Mmsystem.h d到工E中Q否则在~译时提CDWORD timeGetTime(void)函数未定义。由于用该<wbr> 函数是通过查询的方式进行定时控制的Q所以,应该建立定时循环来进行定时事件的控制。如CZ工程中的Timer5和Timer5_1?br>  方式六:使用多媒体定时器timeSetEvent()函数Q该函数定时_ֺ为msU。利用该函数可以实现周期性的函数调用。如CZ工程中的Timer6和Timer6_1。函数的原型如下Q?<br> <pre> MMRESULT timeSetEventQ?UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, WORD dwUser, UINT fuEvent Q?/pre>   该函数设|一个定时回调事Ӟ此事件可以是一个一ơ性事件或周期性事件。事件一旦被Ȁz,便调用指定的回调函数Q?wbr> 成功后返回事件的标识W代码,否则q回NULL。函数的参数说明如下Q?br> <pre> uDelayQ以毫秒指定事g的周期? UresolutionQ以毫秒指定延时的精度,数D定时器事g分L率越高。缺省gؓ1ms? LpTimeProcQ指向一个回调函数? DwUserQ存攄h供的回调数据? FuEventQ指定定时器事gcdQ? TIME_ONESHOTQuDelay毫秒后只产生一ơ事? TIME_PERIODIC Q每隔uDelay毫秒周期性地产生事g? </pre>   具体应用Ӟ可以通过调用timeSetEvent()函数Q将需要周期性执行的d定义在LpTimeProc回调函数<wbr> ?如:定时采样、控制等)Q从而完成所需处理的事件。需要注意的是,d处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,<wbr> 应及时调用timeKillEvent()之释放?<br>  方式七:对于_度要求更高的定时操作Q则应该使用QueryPerformanceFrequency()?wbr> QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后箋版本使用的精时间函敎ͼq要求计机从硬件上支持_定时器。如CZ工程中的Timer7、Timer7_1、Timer7_2、Timer7_3?br>QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:<br> <pre> BOOL QueryPerformanceFrequency(LARGE_INTEGER QlpFrequency); BOOL QueryPerformanceCounter(LARGE_INTEGER QlpCount);</pre>   数据cdARGE_INTEGER既可以是一?字节长的整型敎ͼ也可以是两个4字节长的整型数的联合l构Q?wbr> 其具体用法根据编译器是否支持64位而定。该cd的定义如下:<br> <pre> typedef union _LARGE_INTEGER { struct { DWORD LowPart ;// 4字节整型? LONG HighPart;// 4字节整型? }; LONGLONG QuadPart ;// 8字节整型? }LARGE_INTEGER ;</pre>   在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的旉频率Q?wbr> 然后在需要严格定时的事g发生之前和发生之后分别调用QueryPerformanceCounter()函数Q利用两ơ获得的计数之差及时钟频率,计算Z件经<wbr> 历的_旉。下列代码实?ms的精定Ӟ<br> <pre> LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)litmp.QuadPart;// 获得计数器的旉频率 QueryPerformanceCounter(&litmp); QPart1 = litmp.QuadPart;// 获得初始? do { QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//获得中止? dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 获得对应的时间|单位为秒 }while(dfTim<0.001);</pre>   其定时误差不过1微秒Q精度与CPU{机器配|有兟?下面的程序用来测试函数Sleep(100)的精持l时_<br> <pre> LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)litmp.QuadPart;// 获得计数器的旉频率 QueryPerformanceCounter(&litmp); QPart1 = litmp.QuadPart;// 获得初始? Sleep(100); QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//获得中止? dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 获得对应的时间|单位为秒 </pre>   ׃Sleep()函数自n的误差,上述E序每次执行的结果都会有微小误差。下列代码实?微秒的精定Ӟ<br> <pre> LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)litmp.QuadPart;// 获得计数器的旉频率 QueryPerformanceCounter(&litmp); QPart1 = litmp.QuadPart;// 获得初始? do { QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//获得中止? dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 获得对应的时间|单位为秒 }while(dfTim<0.000001);</pre> 其定时误差一般不过0.5微秒Q精度与CPU{机器配|有兟? <img src ="http://www.shnenglu.com/xushaohua/aggbug/37817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2007-12-04 20:56 <a href="http://www.shnenglu.com/xushaohua/archive/2007/12/04/37817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Z Visual C++6.0 ?DLL ~程实现http://www.shnenglu.com/xushaohua/archive/2007/12/04/37783.htmlshaohuashaohuaTue, 04 Dec 2007 05:44:00 GMThttp://www.shnenglu.com/xushaohua/archive/2007/12/04/37783.htmlhttp://www.shnenglu.com/xushaohua/comments/37783.htmlhttp://www.shnenglu.com/xushaohua/archive/2007/12/04/37783.html#Feedback1http://www.shnenglu.com/xushaohua/comments/commentRss/37783.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/37783.html一、前a

  自从微Y推出 16 位的 Windows 操作pȝP此后每种版本?/span> Windows 操作pȝ都非怾赖于动态链接库 (DLL) 中的函数和数据,实际?/span> Windows 操作pȝ中几乎所有的内容都由 DLL 以一U或另外一UŞ式代表着Q例如显C的字体和图标存储在 GDI DLL 中、显C?/span> Windows 桌面和处理用L输入所需要的代码被存储在一?/span> User DLL 中?/span> Windows ~程所需要的大量?/span> API 函数也被包含?/span> Kernel DLL 中?/span>

  ?/span> Windows 操作pȝ中?/span> DLL 有很多优点,最主要的一Ҏ多个应用E序、甚x不同语言~写的应用程序可以共享一?/span> DLL 文gQ真正实C资源 " ׃n " Q大大羃了应用E序的执行代码,更加有效的利用了内存Q?/span> DLL 的另一个优Ҏ DLL 文g作ؓ一个单独的E序模块Q封装性、独立性好Q在软g需要升U的时候,开发h员只需要修改相应的 DLL 文g可以了Q而且Q当 DLL 中的函数改变后,只要不是参数的改?/span> , E序代码q不需要重新编译。这在编E时十分有用Q大大提高了软g开发和l护的效率?/span>

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

  二?/strong> DLL 的概?/span>

   DLL 是徏立在客户 / 服务器通信的概念上Q包含若q函数、类或资源的库文Ӟ函数和数据被存储在一?/span> DLL Q服务器Q上q由一个或多个客户导出而用,q些客户可以是应用程序或者是其它?/span> DLL ?/span> DLL 库不同于静态库Q在静态库情况下,函数和数据被~译q一个二q制文gQ通常扩展名ؓ *.LIB Q, Visual C++ 的编译器在处理程序代码时从静态库中恢复这些函数和数据q把他们和应用程序中的其他模块组合在一L成可执行文g。这个过E称?/span> " 静态链?/span> " Q此时因为应用程序所需的全部内定w是从库中复制了出来,所以静态库本nq不需要与可执行文件一起发行?/span>

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

  微Y?/span> Visual C++ 支持三种 DLL Q它们分别是 Non-MFC Dll Q非 MFC 动态库Q?/span> Regular Dll Q常?/span> DLL Q?/span> Extension Dll Q扩?/span> DLL Q?/span> Non-MFC DLL 指的是不?/span> MFC 的类库结构,直接?/span> C 语言写的 DLL Q其导出的函数是标准?/span> C 接口Q能被非 MFC ?/span> MFC ~写的应用程序所调用?/span> Regular DLL: 和下q的 Extension Dlls 一P是用 MFC cd~写的,它的一个明昄特点是在源文仉有一个?/span> CWinApp 的类Q注意:此类 DLL 虽然?/span> CWinApp zQ但没有消息循环Q?/span> , 被导出的函数?/span> C 函数?/span> C++ cL?/span> C++ 成员函数Q注意不要把术语 C++ cM MFC 的微软基 C++ cȝhQ,调用常规 DLL 的应用程序不必是 MFC 应用E序Q只要是能调用类 C 函数的应用程序就可以Q它们可以是?/span> Visual C++ ?/span> Dephi ?/span> Visual Basic ?/span> Borland C {编译环境下利用 DLL 开发应用程序?/span>

  常规 DLL 又可l分成静态链接到 MFC 和动态链接到 MFC 上的Q这两种常规 DLL 的区别将在下面介l。与常规 DLL 相比Q用扩?/span> DLL 用于导出增强 MFC 基础cȝ函数或子c,用这U类型的动态链接库Q可以用来输Z个从 MFC 所l承下来的类?/span>

  扩展 DLL 是?/span> MFC 的动态链接版本所创徏的,q且它只被用 MFC cd所~写的应用程序所调用。例如你已经创徏了一个从 MFC ?/span> CtoolBar cȝzcȝ于创Z个新的工hQؓ了导个类Q你必须把它攑ֈ一?/span> MFC 扩展?/span> DLL 中。扩?/span> DLL 和常?/span> DLL 不一P它没有一个从 CWinApp l承而来的类的对象,所以,开发h员必d DLL 中的 DllMain 函数d初始化代码和l束代码?/span>

三、动态链接库的创?/font>

  ?/span> Visual C++6.0 开发环境下Q打开 FileNewProject 选项Q可以选择 Win32 Dynamic-Link Library ?/span> MFC AppWizard[dll] 来以不同的方式来创徏 Non-MFC Dll ?/span> Regular Dll ?/span> Extension Dll {不同种cȝ动态链接库?/span>

   1 Q?/span> Win32 Dynamic-Link Library 方式创徏 Non-MFC DLL 动态链接库

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

 

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 是动态库被调用时所传递来的一个指向自q句柄 ( 实际上,它是指向 _DGROUP D늚一个选择W?/span> ) Q?/span> ul_reason_for_call 是一个说明动态库被调原因的标志,当进E或U程装入或卸载动态链接库的时候,操作pȝ调用入口函数Qƈ说明动态链接库被调用的原因Q它所有的可能gؓQ?/span> DLL_PROCESS_ATTACH: q程被调用?/span> DLL_THREAD_ATTACH: U程被调用?/span> DLL_PROCESS_DETACH: q程被停止?/span> DLL_THREAD_DETACH: U程被停止; lpReserved Z留参数。到此ؓ止, DLL 的入口函数已l写了,剩下部分的实C不难Q你可以?/span> DLL 工程中加入你所惌输出的函数或变量了?/span>

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

   1 Q用导出函数关键字 _declspec(dllexport) 创徏 MyDll.dll Q该动态链接库中有两个函数Q分别用来实现得C个数的最大和最数。在 MyDll.h ?/span> MyDLL.cpp 文g中分别输入如下原代码Q?/span>
 

//MyDLL.h
extern "C" _declspec(dllexport) int Max(int a, int b);
extern "C" _declspec(dllexport) int Min(int a, int b);
//MyDll.cpp
Qi nclude
Qi nclude"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 目录Q可以看?/span> MyDll.dll ?/span> MyDll.lib 两个文g?/span> LIB 文g中包?/span> DLL 文g名和 DLL 文g中的函数名等Q该 LIB 文g只是对应?/span> DLL 文g?/span> " 映像文g " Q与 DLL 文g中, LIB 文g的长度要的多,在进行隐式链?/span> DLL 时要用到它。读者可能已l注意到?/span> MyDll.h 中有关键?/span> "extern C" Q它可以使其他编E语a讉K你编写的 DLL 中的函数?/span>

   2 Q用 .def 文g创徏工程 MyDll

  Z?/span> .def 文g创徏 DLL Q请先删除上个例子创建的工程中的 MyDll.h 文gQ保?/span> MyDll.cpp q在该文件头删除 Qi nclude MyDll.h 语句Q同时往该工E中加入一个文本文Ӟ命名?/span> MyDll.def Q再在该文g中加入如下代码:


LIBRARY MyDll
EXPORTS
Max
Min

  其中 LIBRARY 语句说明?/span> def 文g是属于相?/span> DLL 的, EXPORTS 语句下列导出的函数名U。我们可以在 .def 文g中的导出函数后加 @n Q如 Max@1 Q?/span> Min@2 Q表C导出的函数顺序号Q在q行昑ּq时可以用到它。该 DLL ~译成功后,打开工程中的 Debug 目录Q同样也会看?/span> MyDll.dll ?/span> MyDll.lib 文g?/span>

   2 Q?/span> MFC AppWizard[dll] 方式生成常规 / 扩展 DLL

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

 

AFX_MANAGE_STATE(AfxGetStaticModuleState( )) // 此语句用来正地切换 MFC 模块状?/span>

  最后一U是 MFC 扩展 DLL Q这U?/span> DLL 特点是用来徏?/span> MFC 的派生类Q?/span> Dll 只被?/span> MFC cd所~写的应用程序所调用。前面我们已l介l过Q?/span> Extension DLLs ?/span> Regular DLLs 不一P它没有一个从 CWinApp l承而来的类的对象,~译器默认了一?/span> DLL 入口函数 DLLMain() 作ؓ?/span> DLL 的初始化Q你可以在此函数中实现初始化 , 代码如下Q?/span>
 

BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll Q?/span> DWORD reason Q?/span> LPVOID flmpload)
{
switch(reason)
{
……………//
初始化代码;

}
return true;
}

  参数 hinstDll 存放 DLL 的句柄,参数 reason 指明调用函数的原因, lpReserved 是一个被pȝ所保留的参数。对于隐式链接是一个非零|对于昑ּ链接值是零?/span>

  ?/span> MFC 下徏?/span> DLL 文gQ会自动生成 def 文g框架Q其它与建立传统?/span> Non-MFC DLL 没有什么区别,只要在相应的头文件写入关键字 _declspec(dllexport) 函数cd和函数名{,或在生成?/span> def 文g?/span> EXPORTS 下输入函数名可以了。需要注意的是在向其它开发h员分?/span> MFC 扩展 DLL Ӟ不要忘记提供描述 DLL 中类的头文g以及相应?/span> .LIB 文g?/span> DLL 本nQ此后开发h员就能充分利用你开发的扩展 DLL 了?br>
 应用E序使用DLL可以采用两种方式Q一U是隐式链接Q另一U是昑ּ链接。在使用DLL之前首先要知?/span>DLL中函数的l构信息?/span>Visual C++6.0?/span>VCin目录下提供了一个名?/span>Dumpbin.exe的小E序Q用它可以查?/span>DLL文g中的函数l构。另外,Windowspȝ遵循下面的搜烦序来定?/span>DLLQ?/span> 1Q包?/span>EXE文g的目录,2Q进E的当前工作目录Q?/span> 3Q?/span>Windowspȝ目录Q?/span> 4Q?/span>Windows目录Q?/span>5Q列?/span>Path环境变量中的一pd目录?/span>

  1Q隐式链?/span>

  隐式链接是在程序开始执行时将DLL文g加蝲到应用程序当中。实现隐式链接很ҎQ只要将导入函数关键?/span>_declspec(dllimport)函数名等写到应用E序相应的头文g中就可以了。下面的例子通过隐式链接调用MyDll.dll库中?/span>Min函数。首先生成一个项目ؓTestDllQ在DllTest.h?/span>DllTest.cpp文g中分别输入如下代码:
 

 

 

//Dlltest.h
#pragma comment(lib
Q?/span> "MyDll.lib")
extern "C"_declspec(dllimport) int Max(int a,int b);
extern "C"_declspec(dllimport) int Min(int a,int b);
//TestDll.cpp
Qi nclude
Qi nclude"Dlltest.h"
void main()
{int a;
a=min(8,10)
printf("
比较的结果ؓ %d " Q?/span> a);
}
 

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

   2 Q显式链?/span>

  昑ּ链接是应用程序在执行q程中随时可以加?/span> DLL 文gQ也可以随时卸蝲 DLL 文gQ这是隐式链接所无法作到的,所以显式链接具有更好的灉|性,对于解释性语a更ؓ合适。不q实现显式链接要ȝ一些。在应用E序中用 LoadLibrary ?/span> MFC 提供?/span> AfxLoadLibrary 昑ּ的将自己所做的动态链接库调进来,动态链接库的文件名x上述两个函数的参敎ͼ此后再用 GetProcAddress() 获取惌引入的函数。自此,你就可以象用如同在应用E序自定义的函数一h调用此引入函C。在应用E序退Z前,应该?/span> FreeLibrary ?/span> MFC 提供?/span> AfxFreeLibrary 释放动态链接库。下面是通过昑ּ链接调用 DLL 中的 Max 函数的例子?/span>
 

 

Qi nclude
Qi nclude
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 文gQ?/span>
Max=(pMax)GetProcAddress(hDLL,"Max");
A=Max(5,8);
Printf("
比较的结果ؓ %d " Q?/span> a);
FreeLibrary(hDLL);//
卸蝲 MyDll.dll 文gQ?/span>
}

  在上例中使用cd定义关键?/span> typedef Q定义指向和 DLL 中相同的函数原型指针Q然后通过 LoadLibray() ?/span> DLL 加蝲到当前的应用E序中ƈq回当前 DLL 文g的句柄,然后通过 GetProcAddress() 函数获取导入到应用程序中的函数指针,函数调用完毕后,使用 FreeLibrary() 卸蝲 DLL 文g。在~译E序之前Q首先要?/span> DLL 文g拯到工E所在的目录?/span> Windows pȝ目录下?/span>

  使用昑ּ链接应用E序~译时不需要用相应的 Lib 文g。另外,使用 GetProcAddress() 函数Ӟ可以利用 MAKEINTRESOURCE() 函数直接使用 DLL 中函数出现的序P如将 GetProcAddress(hDLL,"Min") 改ؓ GetProcAddress(hDLL, MAKEINTRESOURCE(2)) Q函?/span> Min() ?/span> DLL 中的序h 2 Q,q样调用 DLL 中的函数速度很快Q但是要C函数的用序P否则会发生错误?/span> 



shaohua 2007-12-04 13:44 发表评论
]]>
[后记]大瓶子与瓶子的问题 http://www.shnenglu.com/xushaohua/archive/2006/11/15/15181.htmlshaohuashaohuaWed, 15 Nov 2006 06:09:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/15/15181.htmlhttp://www.shnenglu.com/xushaohua/comments/15181.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/15/15181.html#Feedback1http://www.shnenglu.com/xushaohua/comments/commentRss/15181.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/15181.html
using System;

class Client
{
   
public static void Main ()
   
{
      A a 
= new A();
      B b 
= new B();
      a.b 
= b;
      b.a 
= a;
   }

}


class A
{
   
public B b;
}


class B
{
   
public A a; 
}
Z么这里不用像C++语言描述那样要做一些处?而且没有报错?
pȝq行后,内存l构如下Q?br />

与C++语言有不同的?在C#语言?classq种自定义的数据cd属于引用cd,所以在声名的时候是一U引用而已.׃是引用,所以也无所谓大瓶子q是瓶子了?img src ="http://www.shnenglu.com/xushaohua/aggbug/15181.html" width = "1" height = "1" />

shaohua 2006-11-15 14:09 发表评论
]]>
大瓶子与瓶子的问题http://www.shnenglu.com/xushaohua/archive/2006/11/15/15179.htmlshaohuashaohuaWed, 15 Nov 2006 06:01:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/15/15179.htmlhttp://www.shnenglu.com/xushaohua/comments/15179.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/15/15179.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/15179.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/15179.html class  B;

class  A
{
public :
    A(
void );
    
~ A( void );
    B b;
}
;

A::A(
void )
{
}


A::
~ A( void )
{
}


class  B
{
public :
    B(
void );
    
~ B( void );
    A a;
}
;

B::B(
void )
{
}


B::
~ B( void )
{
}


int  main()
{
    
return   0 ;

}
在编译器中编译要报的错误x:  error C2079: 'A::b' uses undefined class 'B'
原因是什么呢?从代码来?用了cȝ前视的啊,不应该有什么问题的?
但因为在分配内存I间的时?不知道其class B 的大?所以没有办法ؓ其分配存储空?随即报出了一个没有定义的错误.
其解决的Ҏ有两U?如下事例:
ҎA:
class B;

class A
{
public:
    A(
void);
    
~A(void);
    B b();
}
;

A::A(
void)
{
}


A::
~A(void)
{
}


class B
{
public:
    B(
void);
    
~B(void);
    A a();
}
;

B::B(
void)
{
}


B::
~B(void)
{
}


int main()
{
    
return 0;

}
昄地调用一下其构造函数构造一个具体的实例出来x了固定的存储I间
ҎB:
class B;

class A
{
public:
    A(
void);
    
~A(void);
    B 
*b;
}
;

A::A(
void)
{
}


A::
~A(void)
{
}


class B
{
public:
    B(
void);
    
~B(void);
    A 
*a;
}
;

B::B(
void)
{
}


B::
~B(void)
{
}


int main()
{
    
return 0;

}
用指针来存储其一个地址而已,具体的大等到用的时候再为其实际分配.

shaohua 2006-11-15 14:01 发表评论
]]>
用C描述MD5法http://www.shnenglu.com/xushaohua/archive/2006/11/09/14919.htmlshaohuashaohuaThu, 09 Nov 2006 12:54:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/09/14919.htmlhttp://www.shnenglu.com/xushaohua/comments/14919.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/09/14919.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/14919.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/14919.html阅读全文

shaohua 2006-11-09 20:54 发表评论
]]>
初识MD5法http://www.shnenglu.com/xushaohua/archive/2006/11/09/14918.htmlshaohuashaohuaThu, 09 Nov 2006 12:51:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/09/14918.htmlhttp://www.shnenglu.com/xushaohua/comments/14918.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/09/14918.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/14918.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/14918.html阅读全文

shaohua 2006-11-09 20:51 发表评论
]]>
[转]至C/C++初学?----------------堆与?/title><link>http://www.shnenglu.com/xushaohua/archive/2006/11/09/14917.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Thu, 09 Nov 2006 12:45:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2006/11/09/14917.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/14917.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2006/11/09/14917.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/14917.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/xushaohua/services/trackbacks/14917.html</trackback:ping><description><![CDATA[ <div> <p class="vcerHeading"> <img src="http://vcer.net/images/item.gif" align="top" />关键?/p> <font style="BACKGROUND-COLOR: #ffff00">?/font> <font style="BACKGROUND-COLOR: #00ffff">?/font><br /><p class="vcerHeading"><img src="http://vcer.net/images/item.gif" align="top" />摘要</p><br /><p class="vcerHeading"><img src="http://vcer.net/images/item.gif" align="top" />正文</p><div id="7tfph3t" class="vcerParagraph"><p>一、预备知识—程序的内存分配 </p><p>一个由c/C++~译的程序占用的内存分ؓ以下几个部分 </p><p>1?font style="BACKGROUND-COLOR: #00ffff">?/font>区(stackQ— ?q译器自动分配释放 Q存攑և数的参数|局部变量的值等。其操作方式cM于数据结构中?font style="BACKGROUND-COLOR: #00ffff">?/font>?</p><p>2?font style="BACKGROUND-COLOR: #ffff00">?/font>区(heapQ?— ?一般由E序员分配释放, 若程序员不释放,E序l束时可能由OS回收 。注意它与数据结构中?font style="BACKGROUND-COLOR: #ffff00">?/font>是两回事Q分配方式倒是cM于链表,呵呵?</p><p>3、全局区(静态区Q(staticQ—,全局变量和静态变量的存储是放在一块的Q初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在盔R的另一块区域?- E序l束后有pȝ释放 </p><p>4、文字常量区  —常量字W串是攑֜q里的?E序l束后由pȝ释放 </p><p>5、程序代码区—存攑ևC的二q制代码?</p><p>二、例子程?</p><p>q是一个前辈写的,非常详细 </p><p>//main.cpp </p><p>int a = 0; 全局初始化区 </p><p>char *p1; 全局未初始化?</p><p>main() </p><p>{ </p><p>int b; <font style="BACKGROUND-COLOR: #00ffff">?/font></p><p>char s[] = "abc"; <font style="BACKGROUND-COLOR: #00ffff">?/font></p><p>char *p2; <font style="BACKGROUND-COLOR: #00ffff">?/font></p><p>char *p3 = "123456"; 123456\0在常量区Qp3?font style="BACKGROUND-COLOR: #00ffff">?/font>上?</p><p>static int c =0Q?全局Q静态)初始化区 </p><p>p1 = (char *)malloc(10); </p><p>p2 = (char *)malloc(20); </p><p>分配得来?0?0字节的区域就?font style="BACKGROUND-COLOR: #ffff00">?/font>区?</p><p>strcpy(p1, "123456"); 123456\0攑֜帔R区,~译器可能会它与p3所指向?123456"优化成一个地斏V?</p><p>} </p><p>二?font style="BACKGROUND-COLOR: #ffff00">?/font>?font style="BACKGROUND-COLOR: #00ffff">?/font>的理论知?</p><p>2.1甌方式 </p><p>stack: </p><p>ql自动分配?例如Q声明在函数中一个局部变?int b; pȝ自动?font style="BACKGROUND-COLOR: #00ffff">?/font>中ؓb开辟空?</p><p>heap: </p><p>需要程序员自己甌Qƈ指明大小Q在c中malloc函数 </p><p>如p1 = (char *)malloc(10); </p><p>在C++中用newq算W?</p><p>如p2 = (char *)malloc(10); </p><p>但是注意p1、p2本n是在<font style="BACKGROUND-COLOR: #00ffff">?/font>中的?</p><p>2.2 </p><p>甌后系l的响应 </p><p><font style="BACKGROUND-COLOR: #00ffff">?/font>Q只?font style="BACKGROUND-COLOR: #00ffff">?/font>的剩余空间大于所甌I间Q系l将为程序提供内存,否则报异常提示<font style="BACKGROUND-COLOR: #00ffff">?/font>溢出?</p><p><font style="BACKGROUND-COLOR: #ffff00">?/font>Q首先应该知道操作系l有一个记录空闲内存地址的链表,当系l收到程序的甌Ӟ </p><p>会遍历该链表Q寻扄一个空间大于所甌I间?font style="BACKGROUND-COLOR: #ffff00">?/font>l点Q然后将该结点从I闲l点链表中删除,q将该结点的I间分配l程序,另外Q对于大多数pȝQ会在这块内存空间中的首地址处记录本ơ分配的大小Q这P代码中的delete语句才能正确的释放本内存I间。另外,׃扑ֈ?font style="BACKGROUND-COLOR: #ffff00">?/font>l点的大不一定正好等于申L大小Q系l会自动的将多余的那部分重新攑օI闲链表中?</p><p>2.3甌大小的限?</p><p><font style="BACKGROUND-COLOR: #00ffff">?/font>Q在Windows?<font style="BACKGROUND-COLOR: #00ffff">?/font>是向低地址扩展的数据结构,是一块连l的内存的区域。这句话的意思是<font style="BACKGROUND-COLOR: #00ffff">?/font>的地址?font style="BACKGROUND-COLOR: #00ffff">?/font>的最大容量是pȝ预先规定好的Q在WINDOWS下,<font style="BACKGROUND-COLOR: #00ffff">?/font>的大是2MQ也有的说是1MQM是一个编译时q定的常数Q,如果甌的空间超q?font style="BACKGROUND-COLOR: #00ffff">?/font>的剩余空间时Q将提示overflow。因此,能从<font style="BACKGROUND-COLOR: #00ffff">?/font>获得的空间较?</p><p><font style="BACKGROUND-COLOR: #ffff00">?/font>Q?font style="BACKGROUND-COLOR: #ffff00">?/font>是向高地址扩展的数据结构,是不q箋的内存区域。这是由于系l是用链表来存储的空闲内存地址的,自然是不q箋的,而链表的遍历方向是由低地址向高地址?font style="BACKGROUND-COLOR: #ffff00">?/font>的大受限于计算机系l中有效的虚拟内存。由此可见,<font style="BACKGROUND-COLOR: #ffff00">?/font>获得的空间比较灵z,也比较大?</p><p>2.4甌效率的比较: </p><p><font style="BACKGROUND-COLOR: #00ffff">?/font>ql自动分配,速度较快。但E序员是无法控制的?</p><p><font style="BACKGROUND-COLOR: #ffff00">?/font>是由new分配的内存,一般速度比较慢,而且Ҏ产生内存片,不过用v来最方便. </p><p>另外Q在WINDOWS下,最好的方式是用VirtualAlloc分配内存Q他不是?font style="BACKGROUND-COLOR: #ffff00">?/font>Q也不是?font style="BACKGROUND-COLOR: #00ffff">?/font>是直接在q程的地址I间中保留一快内存,虽然用v来最不方ѝ但是速度快,也最灉|?</p><p>2.5<font style="BACKGROUND-COLOR: #ffff00">?/font>?font style="BACKGROUND-COLOR: #00ffff">?/font>中的存储内容 </p><p><font style="BACKGROUND-COLOR: #00ffff">?/font>Q?在函数调用时Q第一个进<font style="BACKGROUND-COLOR: #00ffff">?/font>的是dC后的下一条指令(函数调用语句的下一条可执行语句Q的地址Q然后是函数的各个参敎ͼ在大多数的C~译器中Q参数是由右往左入<font style="BACKGROUND-COLOR: #00ffff">?/font>的,然后是函C的局部变量。注意静态变量是不入<font style="BACKGROUND-COLOR: #00ffff">?/font>的?</p><p>当本ơ函数调用结束后Q局部变量先?font style="BACKGROUND-COLOR: #00ffff">?/font>Q然后是参数Q最?font style="BACKGROUND-COLOR: #00ffff">?/font>指针指向最开始存的地址Q也是dC的下一条指令,E序p点l运行?</p><p><font style="BACKGROUND-COLOR: #ffff00">?/font>Q一般是?font style="BACKGROUND-COLOR: #ffff00">?/font>的头部用一个字节存?font style="BACKGROUND-COLOR: #ffff00">?/font>的大?font style="BACKGROUND-COLOR: #ffff00">?/font>中的具体内容有程序员安排?</p><p>2.6存取效率的比?</p><p>char s1[] = "aaaaaaaaaaaaaaa"; </p><p>char *s2 = "bbbbbbbbbbbbbbbbb"; </p><p>aaaaaaaaaaa是在q行时刻赋值的Q?</p><p>而bbbbbbbbbbb是在~译时就定的; </p><p>但是Q在以后的存取中Q在<font style="BACKGROUND-COLOR: #00ffff">?/font>上的数组比指针所指向的字W串(例如<font style="BACKGROUND-COLOR: #ffff00">?/font>)快?</p><p>比如Q?</p><p>#include </p><p>void main() </p><p>{ </p><p>char a = 1; </p><p>char c[] = "1234567890"; </p><p>char *p ="1234567890"; </p><p>a = c[1]; </p><p>a = p[1]; </p><p>return; </p><p>} </p><p>对应的汇~代?</p><p>10: a = c[1]; </p><p>00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] </p><p>0040106A 88 4D FC mov byte ptr [ebp-4],cl </p><p>11: a = p[1]; </p><p>0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] </p><p>00401070 8A 42 01 mov al,byte ptr [edx+1] </p><p>00401073 88 45 FC mov byte ptr [ebp-4],al </p><p>W一U在d时直接就把字W串中的元素d寄存器cl中,而第二种则要先把指针D到edx中,在根据edxd字符Q显然慢了?</p><p>2.7结Q?</p><p><font style="BACKGROUND-COLOR: #ffff00">?/font>?font style="BACKGROUND-COLOR: #00ffff">?/font>的区别可以用如下的比L看出Q?</p><p>使用<font style="BACKGROUND-COLOR: #00ffff">?/font>p我们去饭馆里吃饭Q只点菜(发出甌Q、付钱、和吃(使用Q,吃饱了就赎ͼ不必理会切菜、洗菜等准备工作和洗、刷锅等扫尾工作Q他的好处是快捷Q但是自由度?</p><p>使用<font style="BACKGROUND-COLOR: #ffff00">?/font>p是自己动手做喜欢吃的菜肴Q比较麻烦,但是比较W合自己的口呻I而且自由度大?/p></div></div> <img src ="http://www.shnenglu.com/xushaohua/aggbug/14917.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2006-11-09 20:45 <a href="http://www.shnenglu.com/xushaohua/archive/2006/11/09/14917.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C中的跌{语句[非goto]http://www.shnenglu.com/xushaohua/archive/2006/11/04/14672.htmlshaohuashaohuaSat, 04 Nov 2006 13:18:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/04/14672.htmlhttp://www.shnenglu.com/xushaohua/comments/14672.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/04/14672.html#Feedback4http://www.shnenglu.com/xushaohua/comments/commentRss/14672.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/14672.html  1 #include  < stdio.h >
 2 #include  < setjmp.h >
 3 jmp_buf buf;
 4
 5 static   void  babana()
 6 {
 7     printf( " in babana()\n " );
 8     longjmp(buf, 1 );
 9     printf( " never see me\n " );
10 }

11
12 int  main()
13 {
14      if (setjmp(buf))
15      {
16         printf( " back in main\n " );
17     }

18      else
19      {
20         printf( " first time through\n " );
21         babana();
22     }

23      return   0 ;
24 }
 setjmp()首先被调?q且q回0.
goto是不能蟩出当前函数的,而longjmp甚至可以跛_其他文g的函C?估计是Z么要在名字里加个long的原因吧,:)).
?longjmp只能跛_到曾l去q的地方,而setjmp正是标记到过的地?所以从q角度来想的?longjmp更像?从何处来"而不是goto"d?.

q也可以所是C++? catch "," throw " 的一个v源吧.

shaohua 2006-11-04 21:18 发表评论
]]>
匈牙利命名法的来历和介绍http://www.shnenglu.com/xushaohua/archive/2006/11/02/14591.htmlshaohuashaohuaThu, 02 Nov 2006 15:07:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/02/14591.htmlhttp://www.shnenglu.com/xushaohua/comments/14591.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/02/14591.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/14591.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/14591.html
举例来说Q表单的名称为formQ那么在匈牙利命名法中可以简写ؓfrmQ则当表单变量名UCؓSwitchboardӞ变量全称应该为frmSwitchboard。这样可以很Ҏ从变量名看出Switchboard是一个表单,同样Q如果此变量cd为标{,那么应命名成lblSwitchboard。可以看出,匈牙利命名法非常便于记忆Q而且使变量名非常清晰易懂Q这P增强了代码的可读性,方便各程序员之间怺交流代码?

q种命名技术是׃位能q的MicrosoftE序员查斯·西蒙?Charles Simonyi) 提出的,他出生在匈牙利。在 Microsoft 公司中和他一起工作的教会使用q种U定。这对他们来说一切都很正常。但寚w?Simonyi 领导的项目组之外的h来说却感到很奇特Q他们认是死板的表达方式Q甚臌带有q样奇怪的外观是因为它是用匈牙利文写的。从此这U命名方式就被叫做匈牙利命名法?

据说q种命名法是一位叫 Charles Simonyi 的匈牙利E序员发明的Q后来他在微软呆了几q_于是
q种命名法就通过微Y的各U品和文档资料向世界传播开了。现在,大部分程序员不管自己使用
什么Y件进行开发,或多或少都用了q种命名法。这U命名法的出发点是把量名变按Q属?cd
+对象 描述的顺序组合v来,以ɽE序员作变量时对变量的类型和其它属性有直观的了解,下面
是HN变量命名规范Q其中也有一些是我个人的偏向Q?

属性部?
全局变量
g_
帔R
c_
c++cL员变?
m_
静态变?
s_

cd部分
指针
p
函数
fn
无效
v
句柄
h
长整?
l
布尔
b
点型(有时也指文gQ?
f
双字
dw
字符?
sz
短整?
n
双精度Q?
d
计数
cQ通常用cntQ?
字符
chQ通常用cQ?
整型
iQ通常用nQ?
字节
by
?
w
实型
r
无符?
u

描述部分
最?
Max
最?
Min
初始?
Init
临时变量
TQ或TempQ?
源对?
Src
目的对象
Dest



q里Z写几个例子:
hwnd Q?h 是类型描qͼ表示句柄Q?wnd 是变量对象描qͼ表示H口Q所?hwnd 表示H口句柄Q?
pfnEatApple Q?pfn 是类型描qͼ表示指向函数的指针, EatApple 是变量对象描qͼ所以它表示
指向 EatApple 函数的函数指针变量?
g_cch Q?g_ 是属性描qͼ表示全局变量Qc ?ch 分别是计数类型和字符cdQ一赯C变量类
型,q里忽略了对象描qͼ所以它表示一个对字符q行计数的全局变量?
上面是HN命名法的一般规则?


结:匈牙利命名法

匈牙利命名法


MFC、句柄、控件及l构的命名规?Windowscd h变量 MFCc?h变量
HWND hWndQ?CWnd* pWndQ?
HDLG hDl*Q?**ialog* pDlgQ?
HDC hDCQ?CDC* pDCQ?
HGDIOBJ hGdiObjQ?CGdiObject* pGdiObjQ?
HPEN hPenQ?CPen* pPenQ?
HBRUSH hBrushQ?CBrush* pBrushQ?
HFONT hFontQ?CFont* pFontQ?
HBITMAP hBitmapQ?CBitmap* pBitmapQ?
HPALETTE hPaltteQ?CPalette* pPaletteQ?
HRGN hRgnQ?CRgn* pRgnQ?
HMENU hMenuQ?CMenu* pMenuQ?
HWND hCtlQ?CState* pStateQ?
HWND hCtlQ?CButton* pButtonQ?
HWND hCtlQ?CEdit* pEditQ?
HWND hCtlQ?CListBox* pListBoxQ?
HWND hCtlQ?CComboBox* pComboBoxQ?
HWND hCtlQ?CScrollBar* pScrollBarQ?
HSZ hszStrQ?CString pStrQ?
POINT ptQ?CPoint ptQ?
SIZE sizeQ?CSize sizeQ?
RECT rectQ?CRect rectQ?


一般前~命名规范 前缀 cd 实例
C cLl构 CDocumentQCPrintInfo
m_ 成员变量 m_pDocQm_nCustomers


变量命名规范 前缀 cd 描述 实例
ch char 8位字W?chGrade
ch TCHAR 如果_UNICODE定义Q则?6位字W?chName
b BOOL 布尔?bEnable
n int 整型Q其大小依赖于操作系l) nLength
n UINT 无符号|其大依赖于操作pȝQ?nHeight
w WORD 16位无W号?wPos
l LONG 32位有W号整型 lOffset
dw DWORD 32位无W号整型 dwRange
p * 指针 pDoc
lp FAR* q指?lpszName
lpsz LPSTR 32位字W串指针 lpszName
lpsz LPCSTR 32位常量字W串指针 lpszName
lpsz LPCTSTR 如果_UNICODE定义Q则?2位常量字W串指针 lpszName
h handle Windows对象句柄 hWnd
lpfn callback 指向CALLBACK函数的远指针


前缀 W号cd 实例 范围
IDR_ 不同cd的多个资源共享标?IDR_MAIINFRAME 1?x6FFF
IDD_ 对话框资?IDD_SPELL_CHECK 1?x6FFF
HIDD_ 对话框资源的Help上下?HIDD_SPELL_CHECK 0x20001?x26FF
IDB_ 位图资源 IDB_COMPANY_LOGO 1?x6FFF
IDC_ 光标资源 IDC_PENCIL 1?x6FFF
IDI_ 图标资源 IDI_NOTEPAD 1?x6FFF
ID_ 来自菜单Ҏ工具栏的命o ID_TOOLS_SPELLING 0x8000?xDFFF
HID_ 命oHelp上下?HID_TOOLS_SPELLING 0x18000?x1DFFF
IDP_ 消息框提C?IDP_INVALID_PARTNO 8?xDEEF
HIDP_ 消息框Help上下?HIDP_INVALID_PARTNO 0x30008?x3DEFF
IDS_ 串资?IDS_COPYRIGHT 1?x7EEF
IDC_ 对话框内的控?IDC_RECALC 8?xDEEF


Microsoft MFC宏命名规?名称 cd
_AFXDLL 唯一的动态连接库QDynamic Link LibraryQDLLQ版?
_ALPHA 仅编译DEC Alpha处理?
_DEBUG 包括诊断的调试版?
_MBCS ~译多字节字W集
_UNICODE 在一个应用程序中打开Unicode
AFXAPI MFC提供的函?
CALLBACK 通过指针回调的函?


库标识符命名?标识W?值和含义
u ANSIQNQ或UnicodeQUQ?
d 调试或发行:D = 调试Q忽略标识符为发行?


静态库版本命名规范 ?描述
NAFXCWD.LIB 调试版本QMFC静态连接库
NAFXCW.LIB 发行版本QMFC静态连接库
UAFXCWD.LIB 调试版本Q具有Unicode支持的MFC静态连接库
UAFXCW.LIB 发行版本Q具有Unicode支持的MFC静态连接库


动态连接库命名规范 名称 cd
_AFXDLL 唯一的动态连接库QDLLQ版?
WINAPI Windows所提供的函?


Windows.h中新的命名规?cd 定义描述
WINAPI 使用在API声明中的FAR PASCAL位置Q如果正在编写一个具有导出API人口点的DLLQ则可以在自qAPI中用该cd
CALLBACK 使用在应用程序回叫例E,如窗口和对话框过E中的FAR PASCAL的位|?
LPCSTR 与LPSTR相同Q只是LPCSTR用于只读串指针,其定义类|const cha* **R*Q?
UINT 可移植的无符h型类型,其大由L环境军_Q对于Windows NT和Windows 9x?2位)Q它是unsigned int的同义词
LRESULT H口E序q回值的cd
LPARAM 声明lParam所使用的类型,lParam是窗口程序的W四个参?
WPARAM 声明wParam所使用的类型,wParam是窗口程序的W三个参?
LPVOID 一般指针类型,与(void *Q相同,可以用来代替LPSTR

shaohua 2006-11-02 23:07 发表评论
]]>
回调函数http://www.shnenglu.com/xushaohua/archive/2006/11/02/14584.htmlshaohuashaohuaThu, 02 Nov 2006 14:58:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/11/02/14584.htmlhttp://www.shnenglu.com/xushaohua/comments/14584.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/11/02/14584.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/14584.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/14584.html    回调函数是一个程序员不能昑ּ调用的函敎ͼ通过回调函数的地址传给调用者从而实现调用?br />
二两个demo

----------------------------------------------------------------------------------------------------------------
void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函?*/
}

void func();

int main()
{
p = func;
caller(p);    /* 传递函数地址到caller ,caller调用指针p指向的函?/
}

 如果赋了不同的值给pQ不同函数地址Q,那么调用者将调用不同地址的函数。赋值可以发生在q行Ӟq样使你能实现动态绑定?br />-------------------------------------------------------------------------------------------------------------

typedef int(*CallBack)(char *p) ; // 声明CallBack cd的函数指?

int A(char *p); // 回调函数

B(CallBack lpCall,char *pProvide)
{
 ........... // B 的自己实现功能语?br /> lpCall(PpProvide); // 借助回调完成的功?Q也是A函数来处理的?
 ........... // B 的自己实现功能语?br />}
// -------------- 使用例子 -------------
main()
{
char *p = "hello!";
B(A, p);
}


shaohua 2006-11-02 22:58 发表评论
]]>
MFC中获取程序运行的路径http://www.shnenglu.com/xushaohua/archive/2006/10/31/14443.htmlshaohuashaohuaTue, 31 Oct 2006 13:56:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/10/31/14443.htmlhttp://www.shnenglu.com/xushaohua/comments/14443.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/10/31/14443.html#Feedback2http://www.shnenglu.com/xushaohua/comments/commentRss/14443.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/14443.html一、常规的做法Q?br />1.用API GetModuleFileName来获取可执行行文件的路径Q?br />2.获取的字符串中的最后一?'\\'之后的字W全部去掉;
3.剩下的字W串x我们所需要的Q?br />
 1CString CXXXX::GetPath()
 2{
 3    char path[256= "\0";
 4    GetModuleFileName(NULL, path, 256);
 5    char *= path + strlen(path);
 6    while(p != NULL && p>path && *!= '\\')
 7    {
 8            p--;
 9    }

10    char temp[256= "\0";
11    strncpy(temp,path,(p-path));
12    return CString(temp);
13}

二、利用C库函数来L后缀
 
1TCHAR path[MAX_PATH];
2 GetModuleFileName(NULL, path, MAX_PATH);
3 *strrchr(path,'\\'= '\0';    
       // path 即ؓ需要的路径


shaohua 2006-10-31 21:56 发表评论
]]>
CString, BSTR, LPCTSTR http://www.shnenglu.com/xushaohua/archive/2006/10/21/13954.htmlshaohuashaohuaSat, 21 Oct 2006 08:12:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/10/21/13954.htmlhttp://www.shnenglu.com/xushaohua/comments/13954.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/10/21/13954.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/13954.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/13954.html
CString 是一个完全独立的c,动态的TCHAR数组Q封装了 + {操作符和字W串操作Ҏ?br>typedef OLECHAR FAR* BSTR;
typedef const char * LPCTSTR;

vc++中各U字W串的表C法

首先char* 是指向ANSI字符数组的指针,其中每个字符占据8位(有效数据是除掉最高位的其?位)Q这里保持了与传l的C,C++的兼宏V?/p>

LP的含义是长指?long pointer)。LPSTR是一个指向以‘\0’l尾的ANSI字符数组的指针,与char*可以互换使用Q在win32中较多地使用LPSTR?br>而LPCSTR中增加的‘C’的含义是“CONSTANT”Q常量)Q表明这U数据类型的实例不能被用它的API函数改变Q除此之外,它与LPSTR是等同的?br>1.LP表示长指?在win16下有长指?LP)和短指针(P)的区?而在win32下是没有区别?都是32?所以这里的LP和P是等L.
2.C表示const
3.T是什么东西呢,我们知道TCHAR在采用Unicode方式~译时是wchar_t,在普通时~译成char.

Z满E序代码国际化的需要,业界推出了Unicode标准Q它提供了一U简单和一致的表达字符串的ҎQ所有字W中的字节都?6位的|其数量也可以满差不多世界上所有书面语a字符的编码需求,开发程序时使用UnicodeQ类型ؓwchar_t)是一U被鼓励的做法?/p>

LPWSTR与LPCWSTR由此产生Q它们的含义cM于LPSTR与LPCSTRQ只是字W数据是16位的wchar_t而不是char?/p>

然后Z实现两种~码的通用Q提ZTCHAR的定义:
如果定义_UNICODEQ声明如下:
typedef wchar_t TCHAR;
如果没有定义_UNICODEQ则声明如下Q?br>typedef char TCHAR;

LPTSTR和LPCTSTR中的含义是每个字符是这LTCHAR?/p>

CStringcM的字W就是被声明为TCHARcd的,它提供了一个封装好的类供用h便地使用?/p>

LPCTSTR:
     #ifdef _UNICODE
        typedef const wchar_t * LPCTSTR;
     #else
        typedef const char * LPCTSTR;
     #endif

如何CStringcd的变量赋lchar*cd的变?br>1、GetBuffer函数Q?br>使用CString::GetBuffer函数?br>char *p;
CString str="hello";
p=str.GetBuffer(str.GetLength());
str.ReleaseBuffer();

CString转换成char * ?br>CString str("aaaaaaa");
strcpy(str.GetBuffer(10),"aa");
str.ReleaseBuffer();
当我们需要字W数l时调用GetBuffer(int n),其中n为我们需要的字符数组的长?使用完成后一定要马上调用ReleaseBuffer();
q有很重要的一点就?在能使用const char *的地?׃要用char *

2、memcpyQ?
CString mCS=_T("cxl");
char mch[20];
memcpy(mch,mCS,20);

3、用LPCTSTR强制转换Q?量不?br>char *ch;
CString str;
ch=(LPSTR)(LPCTSTR)str;

CString str = "good";
char *tmp;
sprintf(tmp,"%s",(LPTSTR)(LPCTSTR)str);

4?br>CString Msg;
Msg=Msg+"abc";
LPTSTR lpsz;
lpsz = new TCHAR[Msg.GetLength()+1];
_tcscpy(lpsz, Msg);
char * psz;
strcpy(psz,lpsz);


CStringcdconst char *转换
char a[100];
CString str("aaaaaa");
strncpy(a,(LPCTSTR)str,sizeof(a));
或者如?
strncpy(a,str,sizeof(a));
以上两种用法都是正确? 因ؓstrncpy的第二个参数cd为const char *.所以编译器会自动将CStringc{换成const char *.

CString转LPCTSTR (const char *)
CString cStr;
const char *lpctStr=(LPCTSTR)cStr;

LPCTSTR转CString
LPCTSTR lpctStr;
CString cStr=lpctStr;

char*cd的变量赋lCString型的变量
可以直接赋|如:
CString myString = "This is a test";
也可以利用构造函敎ͼ如:
CString s1("Tom");

CStringcd的变量赋lchar []cd(字符?的变?br>1、sprintf()函数
CString str = "good";
char tmp[200] ;
sprintf(tmp, "%s",(LPCSTR)str);  
(LPCSTR)strq种强制转换相当?LPTSTR)(LPCTSTR)str
CStringcȝ变量需要{换ؓ(char*)的时,使用(LPTSTR)(LPCTSTR)str

然而,LPCTSTR是const char *,也就是说Q得到的字符串是不可写的Q将其强制{换成LPTSTRLconstQ是极ؓ危险的!
一不留就会完蛋!要得到char *,应该用GetBuffer()或GetBufferSetLength()Q用完后再调用ReleaseBuffer()?/p>

2、strcpy()函数
CString str;
char c[256];
strcpy(c, str);

char mychar[1024];
CString source="Hello";
strcpy((char*)&mychar,(LPCTSTR)source);


关于CString的?br>1、指?CString 形参
    对于大多数需要字W串参数的函敎ͼ最好将函数原型中的形参指定Z个指向字W?(LPCTSTR) 而非 CString ?const 指针?br>当将形参指定为指向字W的 const 指针Ӟ可将指针传递到 TCHAR 数组Q如字符?["hi there"]Q或传递到 CString 对象?br>CString 对象自动{换成 LPCTSTR。Q何能够?LPCTSTR 的地方也能够使用 CString 对象?/p>

2、如果某个Ş参将不会被修改,则也该参数指定为常数字W串引用Q即 const CString&Q。如果函数要修改该字W串Q?br>则删?const 修饰W。如果需要默认ؓI|则将其初始化为空字符?[""]Q如下所C:
void AddCustomer( const CString& name, const CString& address, const CString& comment = "" );

3、对于大多数函数l果Q按D?CString 对象卛_?/p>


串的基本q算
    对于串的基本q算Q很多高U语a均提供了相应的运符或标准的库函数来实现?br>为叙q方便,先定义几个相关的变量Q?br>    char s1[20]="dir/bin/appl",s2[20]="file.asm",s3[30],*p;
    int result;
    下面以C语言中串q算介绍串的基本q算
1、求串长
        int strlen(char *s);         //求串s的长?br>    【例】printf("%d",strlen(s1));    //输出s1的串?2

2、串复制
    char *strcpy(char *to,*from)Q?/from串复制到to串中Qƈq回to开始处指针
    【例】strcpy(s3,s1);  //s3="dir/bin/appl",s1串不?/p>


3、联?br>    char *strcat(char *to,char *from);//from串复制到to串的末尾Q?br>                                      //q返回to串开始处的指?br>    【例】strcat(s3,"/");    //s3="dir/bin/appl/"
         strcat(s3,s2);     //s3="dir/bin/appl/file.asm"

4、串比较
    int strcmp(char *s1,char *s2);//比较s1和s2的大,
     //当s1<s2、s1>s2和s1=s2Ӟ分别q回于0、大?和等?的?
    【例】result=strcmp("baker","Baker");    //result>0
            result=strcmp("12","12");       //result=0
            result=strcmp("Joe","joseph")   //result<0

5、字W定?br>    char *strchr(char *s,char c);//找c在字W串s中第一ơ出现的位置Q?br>                                 //若找刎ͼ则返回该位置Q否则返回NULL
    【例】p=strchr(s2,'.');      //p指向"file"之后的位|?br>     if(p) strcpy(p,".cpp");     //s2="file.cpp"

  注意Q?br>     ①上q操作是最基本的,其中?4个操作还有变UŞ式:strncpyQstrncath和strnchr?br>     ②其它的串操作见C?lt;string.h>。在不同的高U语a中,对串q算的种cdW号都不相?br>     ③其余的串操作一般可p些基本操作组合而成

    【例】求子串的操作可如下实现Q?br>    void substr(char *sub,char *s,int pos,int len){
         //s和sub是字W数l,用subq回串s的第pos个字Wv长度为len的子?br>         //其中0<=pos<=strlen(s)-1,且数lsub臛_可容Ulen+1个字W?br>        if (pos<0||pos>strlen(s)-1||len<0)
            Error("parameter error!");
        strncpy(sub,&s[pos],len);      //从s[pos]起复制至多len个字W到sub
--- http://www.shnenglu.com/colelhx/archive/2007/09/13/32116.html



shaohua 2006-10-21 16:12 发表评论
]]>
单文件分Ԍ06川大考研最后一题)http://www.shnenglu.com/xushaohua/archive/2006/10/18/13819.htmlshaohuashaohuaWed, 18 Oct 2006 08:50:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/10/18/13819.htmlhttp://www.shnenglu.com/xushaohua/comments/13819.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/10/18/13819.html#Feedback1http://www.shnenglu.com/xushaohua/comments/commentRss/13819.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/13819.html
 1#include <stdio.h>
 2#include <stdlib.h>
 3
 4int main(void)
 5{
 6    FILE *in,*out;
 7    
 8    int block;
 9    char *buffer;
10    int i;
11    long size;
12    int num = 1;
13    char src[32],dest[32],temp[32];
14    printf("***********************************************\n");
15    printf("误入要分割的文件的位置Q ?/span>");
16    scanf("%s",src);
17    printf("误入要分割成的文件的个数Q ?/span>");
18    scanf("%d",&num);
19    printf("误入要分割后文件保存位|:  ");
20    scanf("%s",dest);
21    if ((in = fopen(src,"rb")) == NULL)
22    {
23        printf("Open File Fail\n");
24        exit(0);
25    }

26    fseek(in,0L,2);
27    size = ftell(in);
28    rewind(in);
29    block = size/num; // 每一个文件的大小
30    buffer = (char*)malloc(block);
31
32    for(i=0;i< num;i++)
33    {
34        char ch[32];
35        strcpy(temp,dest);
36        itoa((i+1),ch,10);
37        strcat(temp,ch);
38        strcat(temp,".dat");
39        if(fread(buffer,block,1,in)!=1)
40        {
41            printf("Read File Error\n");
42            exit(0);
43        }

44        if((out = fopen(temp,"wb")) == NULL)
45        {
46            printf("Open File Fail\n");
47            exit(0);
48        }

49        if(fwrite(buffer,block,1,out)!=1)
50        {
51            printf("Write File Error\n");
52            exit(0);
53        }

54        fclose(out);
55    }

56    fclose(in);
57    free(buffer);
58    printf("Cut Over\n");
59    printf("***********************************************\n");
60    return 0;
61}

62
单就是美的了?img src ="http://www.shnenglu.com/xushaohua/aggbug/13819.html" width = "1" height = "1" />

shaohua 2006-10-18 16:50 发表评论
]]>
(?用C语言~写Windows服务E序的五个步?http://www.shnenglu.com/xushaohua/archive/2006/10/15/13714.htmlshaohuashaohuaSun, 15 Oct 2006 10:40:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/10/15/13714.htmlhttp://www.shnenglu.com/xushaohua/comments/13714.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/10/15/13714.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/13714.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/13714.html
     Windows 服务被设计用于需要在后台q行的应用程序以及实现没有用户交互的d。ؓ了学习这U控制台应用E序的基知识QCQ不是C++Q是最佳选择。本文将建立q实C个简单的服务E序Q其功能是查询系l中可用物理内存数量Q然后将l果写入一个文本文件。最后,你可以用所学知识编写自q Windows 服务?br />  当初我写W一?NT 服务Ӟ我到 MSDN 上找例子。在那里我找C一?Nigel Thompson 写的文章Q“Creating a Simple Win32 Service in C++”,q篇文章附带一?C++ 例子。虽然这文章很好地解释了服务的开发过E,但是Q我仍然感觉~少我需要的重要信息。我想理解通过什么框Ӟ调用什么函敎ͼ以及何时调用Q但 C++ 在这斚w没有让我L多少。面向对象的Ҏ固然方便Q但׃用类对底?Win32 函数调用q行了封装,它不利于学习服务E序的基本知识。这是Z么我觉得 C 更加适合于编写初U服务程序或者实现简单后CQ务的服务。在你对服务E序有了充分透彻的理解之后,?C++ ~写才能游刃有余。当我离开原来的工作岗位,不得不向另一个h转移我的知识的时候,利用我用 C 所写的例子非常容易解?NT 服务之所以然?br />  服务是一个运行在后台q实现勿需用户交互的Q务的控制台程序。Windows NT/2000/XP 操作pȝ提供为服务程序提供专门的支持。h们可以用服务控制面板来配|安装好的服务程序,也就?Windows 2000/XP 控制面板|理工具中的“服务”(或在“开始”|“运行”对话框中输?services.msc /s——译者注Q。可以将服务配置成操作系l启动时自动启动Q这样你׃必每ơ再重启pȝ后还要手动启动服务?br />  本文首先解释如何创Z个定期查询可用物理内存ƈ结果写入某个文本文件的服务。然后指g完成生成Q安装和实现服务的整个过E?br />

W一步:d数和全局定义

首先Q包含所需的头文g。例子要调用 Win32 函数Qwindows.hQ和盘文g写入Qstdio.hQ:

#include
#include
接着Q定义两个常量:

#define SLEEP_TIME 5000
#define LOGFILE "C:\\MyServices\\memstatus.txt"
SLEEP_TIME 指定两次q箋查询可用内存之间的毫U间隔。在W二步中~写服务工作循环的时候要使用该常量?br />LOGFILE 定义日志文g的\径,你将会用 WriteToLog 函数内存查询的l果输出到该文gQWriteToLog 函数定义如下Q?br />
int WriteToLog(char* str)
{
    FILE* log;
    log = fopen(LOGFILE, "a+");
    if (log == NULL)
    return -1;
    fprintf(log, "%s\n", str);
    fclose(log);
    return 0;
}
声明几个全局变量Q以便在E序的多个函C间共享它们倹{此外,做一个函数的前向定义Q?br />
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();
  现在Q准备工作已l就l,你可以开始编码了。服务程序控制台E序的一个子集。因此,开始你可以定义一?main 函数Q它是程序的入口炏V对于服务程序来_main 的代码o人惊讶地短,因ؓ它只创徏分派表ƈ启动控制分派机?br />
void main()
{
    SERVICE_TABLE_ENTRY ServiceTable[2];
    ServiceTable[0].lpServiceName = "MemoryStatus";
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
    
    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;

    // 启动服务的控制分z机U程
    StartServiceCtrlDispatcher(ServiceTable);
}
  一个程序可能包含若q个服务。每一个服务都必须列于专门的分z表中(为此该程序定义了一?ServiceTable l构数组Q。这个表中的每一w要在 SERVICE_TABLE_ENTRY l构之中。它有两个域Q?br />
lpServiceName: 指向表示服务名称字符串的指针Q当定义了多个服务时Q那么这个域必须指定Q?
lpServiceProc: 指向服务d数的指针Q服务入口点Q;
  分派表的最后一必L服务名和服务d数域?NULL 指针Q文本例子程序中只宿M个服务,所以服务名的定义是可选的?br />  服务控制理器(SCMQServices Control ManagerQ是一个管理系l所有服务的q程。当 SCM 启动某个服务Ӟ它等待某个进E的ȝE来调用 StartServiceCtrlDispatcher 函数。将分派表传递给 StartServiceCtrlDispatcher。这把调用q程的主U程转换为控制分z֙。该分派器启动一个新U程Q该U程q行分派表中每个服务?ServiceMain 函数Q本文例子中只有一个服务)分派器还监视E序中所有服务的执行情况。然后分z֙控制请求从 SCM 传给服务?br />
注意Q如?StartServiceCtrlDispatcher 函数30U没有被调用Q便会报错,Z避免q种情况Q我们必d ServiceMain 函数中(参见本文例子Q或在非d数的单独U程中初始化服务分派表。本文所描述的服务不需要防范这L情况?br />
  分派表中所有的服务执行完之后(例如Q用户通过“服务”控刉板程序停止它们)Q或者发生错误时。StartServiceCtrlDispatcher 调用q回。然后主q程l止?br />

W二步:ServiceMain 函数

  Listing 1 展示?ServiceMain 的代码。该函数是服务的入口炏V它q行在一个单独的U程当中Q这个线E是由控制分z֙创徏的。ServiceMain 应该可能早早ؓ服务注册控制处理器。这要通过调用 RegisterServiceCtrlHadler 函数来实现。你要将两个参数传递给此函敎ͼ服务名和指向 ControlHandlerfunction 的指针?br />  它指C控制分z֙调用 ControlHandler 函数处理 SCM 控制h。注册完控制处理器之后,获得状态句柄(hStatusQ。通过调用 SetServiceStatus 函数Q用 hStatus ?SCM 报告服务的状态?br />Listing 1 展示了如何指定服务特征和其当前状态来初始?ServiceStatus l构QServiceStatus l构的每个域都有其用途:

dwServiceTypeQ指C服务类型,创徏 Win32 服务。赋?SERVICE_WIN32Q?
dwCurrentStateQ指定服务的当前状态。因为服务的初始化在q里没有完成Q所以这里的状态ؓ SERVICE_START_PENDINGQ?
dwControlsAcceptedQ这个域通知 SCM 服务接受哪个域。本文例子是允许 STOP ?SHUTDOWN h。处理控制请求将在第三步讨论Q?
dwWin32ExitCode ?dwServiceSpecificExitCodeQ这两个域在你终止服务ƈ报告退出细节时很有用。初始化服务时ƈ不退出,因此Q它们的gؓ 0Q?
dwCheckPoint ?dwWaitHintQ这两个域表C初始化某个服务q程时要30U以上。本文例子服务的初始化过E很短,所以这两个域的值都?0?
  调用 SetServiceStatus 函数?SCM 报告服务的状态时。要提供 hStatus 句柄?ServiceStatus l构。注?ServiceStatus 一个全局变量Q所以你可以跨多个函C用它。ServiceMain 函数中,你给l构的几个域赋|它们在服务运行的整个q程中都保持不变Q比如:dwServiceType?br />  在报告了服务状态之后,你可以调?InitService 函数来完成初始化。这个函数只是添加一个说明性字W串到日志文件。如下面代码所C:

// 服务初始?br />int InitService()
{
    int result;
    result = WriteToLog("Monitoring started.");
    return(result);
}
  ?ServiceMain 中,?InitService 函数的返回倹{如果初始化有错Q因为有可能写日志文件失败)Q则服务状态置为终止ƈ退?ServiceMainQ?br />
error = InitService();
if (error)
{
    // 初始化失败,l止服务
    ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    ServiceStatus.dwWin32ExitCode = -1;
    SetServiceStatus(hStatus, &ServiceStatus);
    // 退?ServiceMain
    return;
}
如果初始化成功,则向 SCM 报告状态:

// ?SCM 报告q行状?
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);
接着Q启动工作@环。每五秒钟查询一个可用物理内存ƈ结果写入日志文件?br />
?Listing 1 所C,循环一直到服务的状态ؓ SERVICE_RUNNING 或日志文件写入出错ؓ止。状态可能在 ControlHandler 函数响应 SCM 控制h时修攏V?br />

W三步:处理控制h

  在第二步中,你用 ServiceMain 函数注册了控制处理器函数。控制处理器与处理各U?Windows 消息的窗口回调函数非常类伹{它?SCM 发送了什么请求ƈ采取相应行动?br />  每次你调?SetServiceStatus 函数的时候,必须指定服务接收 STOP ?SHUTDOWN h。Listing 2 C了如何在 ControlHandler 函数中处理它们?br />  STOP h?SCM l止服务的时候发送的。例如,如果用户在“服务”控刉板中手动l止服务。SHUTDOWN h是关闭机器时Q由 SCM 发送给所有运行中服务的请求。两U情늚处理方式相同Q?br />
写日志文Ӟ监视停止Q?
?SCM 报告 SERVICE_STOPPED 状态;
  ׃ ServiceStatus l构对于整个E序而言为全局量,ServiceStatus 中的工作循环在当前状态改变或服务l止后停止。其它的控制h如:PAUSE ?CONTINUE 在本文的例子没有处理?br />  控制处理器函数必L告服务状态,即便 SCM 每次发送控制请求的时候状态保持相同。因此,不管响应什么请求,都要调用 SetServiceStatus?br />
W四步:安装和配|服?br />
  E序~好了,之~译?exe 文g。本文例子创建的文g?MemoryStatus.exeQ将它拷贝到 C:\MyServices 文g夏Vؓ了在机器上安装这个服务,需要用 SC.EXE 可执行文Ӟ它是 Win32 Platform SDK 中附带的一个工兗(译者注QVisaul Studio .NET 2003 IDE 环境中也有这个工P具体存放位置在:C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\Bin\winntQ。用这个实用工具可以安装和U除服务。其它控制操作将通过服务控制面板来完成。以下是用命令行安装 MemoryStatus 服务的方法:

sc create MemoryStatus binpath= c:\MyServices\MemoryStatus.exe
  发出此创建命令。指定服务名和二q制文g的\径(注意 binpath= 和\径之间的那个I格Q。安装成功后Q便可以用服务控刉板来控制q个服务。用控制面板的工h启动和终止这个服务?br />
 MemoryStatus 的启动类型是手动Q也是说根据需要来启动q个服务。右键单击该服务Q然后选择上下文菜单中的“属性”菜单项Q此时显C服务的属性窗口。在q里可以修改启动cd以及其它讄。你q可以从“常规”标{中启动/停止服务。以下是从系l中U除服务的方法:

sc delete MemoryStatus
指定 “delete?选项和服务名。此服务被标记为删除,下次襉K重启后Q该服务被完全U除?br />W五步:试服务

  从服务控刉板启?MemoryStatus 服务。如果初始化不出错,表示启动成功。过一会儿服务停止。检查一?C:\MyServices 文g夹中 memstatus.txt 文g的服务输出。在我的机器上输出是q样的:

Monitoring started.
273469440
273379328
273133568
273084416
Monitoring stopped.
  Z试 MemoryStatus 服务在出错情况下的行为,可以?memstatus.txt 文g讄成只诅R这样一来,服务应该无法启动?br />  L只读属性,启动服务Q在文件设成只诅R服务将停止执行Q因为此时日志文件写入失败。如果你更新服务控制面板的内容,会发现服务状态是已经停止?br />原文QYevgeny Menaker
译QNorthtibet

shaohua 2006-10-15 18:40 发表评论
]]>
文gpȝ监控http://www.shnenglu.com/xushaohua/archive/2006/10/15/13707.htmlshaohuashaohuaSun, 15 Oct 2006 05:37:00 GMThttp://www.shnenglu.com/xushaohua/archive/2006/10/15/13707.htmlhttp://www.shnenglu.com/xushaohua/comments/13707.htmlhttp://www.shnenglu.com/xushaohua/archive/2006/10/15/13707.html#Feedback0http://www.shnenglu.com/xushaohua/comments/commentRss/13707.htmlhttp://www.shnenglu.com/xushaohua/services/trackbacks/13707.html阅读全文

shaohua 2006-10-15 13:37 发表评论
]]>
[转]WINDOWS键盘事g监控原理及应?/title><link>http://www.shnenglu.com/xushaohua/archive/2006/10/15/13704.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Sun, 15 Oct 2006 04:37:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2006/10/15/13704.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/13704.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2006/10/15/13704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/13704.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/xushaohua/services/trackbacks/13704.html</trackback:ping><description><![CDATA[一、在WINDOWS键盘事g上挂接监控函数的Ҏ<br /><br />WINDOW下可q行挂接的过滤函数包?1U:<br /><br />WH_CALLWNDPROC H口函数的过滤函?br /><br />WH_CBT 计算机培训过滤函?br /><br />WH_DEBUG 调试qo函数<br /><br />WH_GETMESSAGE 获取消息qo函数<br /><br />WH_HARDWARE g消息qo函数<br /><br />WH_JOURNALPLAYBACK 消息重放qo函数<br /><br />WH_JOURNALRECORD 消息记录qo函数<br /><br />WH_MOUSE 鼠标qo函数<br /><br />WH_MSGFILTER 消息qo函数<br /><br />WH_SYSMSGFILTER pȝ消息qo函数<br /><br />WH_KEYBOARD 键盘qo函数<br /><br />其中键盘qo函数是最常用最有用的过滤函数类型,不管是哪一U类型的qo函数Q其挂接的基本方法都是相同的?br />WINDOW调用挂接的反调函数时L先调用挂接链首的那个函数Q因此必d键盘挂钩函数利用函数SetWindowsHookEx()其挂接在函数链首。至于消息是否传递给函数铄下一个函数是由每个具体函数功能确定的Q如果消息需要传l给下一个函敎ͼ可调用API函数的CallNextHookEx()来实玎ͼ如果不传递直接返回即可。挂接函数可以是用来监控所有线E消息的全局性函敎ͼ也可以是单独监控某一U程的局部性函数。如果挂接函数是局部函敎ͼ可以它攑ֈ一?DLL动态链接库中,也可以放在一个局部模块中Q如果挂接函数是全局的,那么必须其攑֜一?DLL动态链接库中。挂接函数必M格按照下q格式进行声明,以键盘挂钩函Cؓ例:<br /><br />int FAR PASCAL KeyboardProc(int nCode,WORD wParam,DWORD lParam)<br /><br />其中KeyboardProc为定义挂接函数名Q该函数必须在模块定义文件中利用EXPORTS命oq行说明QnCode军_挂接函数是否对当前消息进行处理;wParam和lParam为具体的消息内容?br /><br /><br /><br />二、键盘事件挂接函数的安装与下?br /><br />在程序中可以利用函数SetWindowsHookEx()来挂接过滤函敎ͼ在挂接函数时必须指出该挂接函数的cd、函数的入口地址以及函数是全局性的q是局部性的Q挂接函数的具体调用格式如下Q?br /><br />SetWindowsHookEx(iType,iProc,hInst,iCode)<br /><br />其中iType为挂接函数类型,键盘cd为WH_KEYBOARD,iProc为挂接函数地址QhInst为挂接函数链接库实例句柄QiCode为监控代码-0表示全局性函数。如果挂接函数需要将消息传递给下一个过滤函敎ͼ则在该挂接函数返回前q需要调用一ơCallNextHookEx()函数Q当需要下载挂接函数时Q只要调用一ơUnhookWindowsHookEx(iProc)函数卛_实现。如果函数是全局性的Q那么它必须攑֜一?DLL动态链接库中,q时该函数调用方法可以和其它普?DLL函数一h三种Q?br /><br />1.在DEF定义文g中直接用函数名或序号说明Q?br /><br />EXPORTS<br /><br />WEP @1 RESIDENTNAME<br /><br />InitHooksDll @2<br /><br />InstallFilter @3<br /><br />KeyboardProc @4<br /><br />用序可明格式ؓQ链接库?函数?如本例中说明Ҏ为KEYDLL.KeyboardProc)?br /><br />2.在应用程序中利用函数直接调用Q?br /><br />首先在应用程序中利用LoadLibrary(LPSTR "链接库名")动态链接库装入Qƈ取得装蝲库模块句柄hInst,然后直接利用GetProcAddress(HINSTANCE hInst,LPSTR "函数q程?)获取函数地址Q然后直接调用该地址卛_Q程序结束前利用函数FreeLibrary( )释放装入的动态链接库卛_?br /><br />3.利用输入?LIBҎ<br /><br />利用IMPLIB.EXEE序在徏立动态链接库的同时徏立相应的输入?LIBQ然后直接在目文g中增加该输入库?br /><br /><br /><br />三、WINDOWS挂钩监控函数的实现步?br /><br />WINDOWS挂钩函数只有攑֜动态链接库DLL中才能实现所有事件的监控功能。在.DLL中Ş成挂钩监控函数基本方法及其基本结构如下:<br /><br />Q、首先声明DLL中的变量和过E;<br /><br />Q、然后编制DLLL块LibMain()Q徏立模块实例;<br /><br />Q、徏立系l退出DLL机制WEP()函数Q?br /><br />Q、完成DLL初始化函数InitHooksDll(),传递主H口E序句柄Q?br /><br />Q、编制挂钩安装和下蝲函数InstallFilter()Q?br /><br />Q、编制挂钩函数KeyboardProc()Q在其中讄监控功能Qƈ定l箋调下一个钩<br /><br />子函数还是直接返回WINDOWS应用E序?br /><br />Q、在WINDOWSȝ序中需要初始化DLLq安装相应挂钩函敎ͼ由挂接的钩子函数?br /><br />责与ȝ序通信Q?br /><br />Q、在不需要监控时׃载功能卸掉挂接函数?br /><br />四、WINDOWS下键盘挂钩监控函数的应用技?br /><br /><br /><br />目前标准?04 键盘上都有两个特D的按键Q其上分别用WINDOWE序徽标和鼠标下拉列表标识,本文暂且分别UCؓMicro左键和Micro右键Q前者用来模拟鼠标左键激zd始菜单,后者用来模拟鼠标右键激zd性菜单。这两个Ҏ按键只有在按下后立即抬v卛_?CLICKq程才能实现其功能,q且没有和其它按键进行组合用。由于WINDOWS pȝ中将按键划分得更加详l,使应用程序中很难灉|定义自己的专用快捷键Q比如在开?IME{应用程序时很难扑ֈ不与WORD8.0{其它应用程序冲H的功能按键。如果将标准104键盘中的q两个特D按键作为模拟CTRL和ALT {专用按键,使其和其它按键组合,可以在自己的应用程序中自由地设|专用功能键Qؓ应用E序实现各种功能快捷键提供灵zL。正常情况下WINDOWS 键盘事g驱动E序q不这两个按键的消息进行正常解释,q就必须利用键盘事g的挂钩监控函数来实现其特定的功能。其Ҏ如下Q?br /><br />Q、首先编制如下一个简单动态链接库E序Qƈ~译成DLL文g?br /><br />#include "windows.h"<br /><br />int FAR PASCAL LibMain(HANDLE hModule,UINT wDataSeg,UINT cbHeapSize,LPSTR lpszCmdLine);<br /><br />int WINAPI WEP(int bSystemExit);<br /><br />int WINAPI InitHooksDll(HWND hwndMainWindow);<br /><br />int WINAPI InstallFilter(BOOL nCode);<br /><br />LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam);<br /><br />static HANDLE hInstance; // 全局句柄<br /><br />static HWND hWndMain; // ȝ口句?br /><br />static int InitCalled=0; // 初始化标?br /><br />static HHOOK hKeyHook;<br /><br />FARPROC lpfnKeyHook=(FARPROC)KeyHook;<br /><br />BOOL HookStates=FALSE;<br /><br />int FAR PASCAL LibMain(<br /><br />HANDLE hModule,<br /><br />UINT wDataSeg,<br /><br />UINT cbHeapSize,<br /><br />LPSTR lpszCmdLine)<br /><br />{<br /><br />if (cbHeapSize!=0) UnlockData(0);<br /><br />hInstance = hModule;<br /><br />return 1;<br /><br />}<br /><br />int WINAPI WEP (int bSystemExit)<br /><br />{ return 1;}<br /><br />int WINAPI InitHooksDll(HWND hwndMainWindow)<br /><br />{ hWndMain = hwndMainWindow;<br /><br />InitCalled = 1;<br /><br />return (0);<br /><br />}<br /><br />int WINAPI InstallFilter(BOOL nCode)<br /><br />{ if (InitCalled==0) return (-1);<br /><br />if (nCode==TRUE) {<br /><br />hKeyHook=SetWindowsHookEx(WH_KEYBOARD,<br /><br />(HOOKPROC)lpfnKeyHook,hInstance,0);<br /><br />HookStates=TRUE;<br /><br />} else {<br /><br />UnhookWindowsHookEx(hKeyHook);<br /><br />HookStates=FALSE;<br /><br />}<br /><br />return(0);<br /><br />}<br /><br />LRESULT CALLBACK KeyHook(int nCode,WORD wParam,DWORD lParam)<br /><br />{<br /><br />static BOOL msflag=FALSE;<br /><br />if(nCode>=0) {<br /><br />if(HookStates==TRUE){<br /><br />if((wParam==0xff)|| //WIN3.X下按键?br /><br />(wParam==0x5b)||(wParam==0x5c)){//WIN95下按键?br /><br />if((i==0x15b)||(i==0x15c)){ //按键按下处理<br /><br />msflag=TRUE;<br /><br />PostMessage(hWndMain,0x7fff,0x1,0x3L);<br /><br />} else if((i==0xc15b)||(i==0xc15c)){//按键抬v处理<br /><br />msflag=FALSE;<br /><br />PostMessage(hWndMain,0x7fff,0x2,0x3L);<br /><br />}<br /><br />}<br /><br />}<br /><br />}<br /><br />return((int)CallNextHookEx(hKeyHook,nCode,wParam,lParam));<br /><br />}<br /><br />该程序的主要功能是监控键盘按键消息,两个特D按键Micro按下和抬h息{换成自定义类型的消息Qƈ自定义消息发送给应用E序ȝ口函数?br /><br />Q、在应用E序dC建立H口后,调用InitHooksDll()函数来初始化动态链接库Qƈ应用程序主H口句柄传递给链接库,然后调用InstallFilter()函数挂接键盘事g监控回调函数?br /><br />InitHooksDll(hIMEWnd); //初始化DLL<br /><br />InstallFilter(TRUE); //安装键盘回调函数<br /><br />Q、在应用E序ȝ口函数处理自定义消息Ӟ保存Micro按键的状态,供组合按键处理时判断使用?br /><br />switch (iMessage) {<br /><br />case 0x7fff: //自定义消息类?br /><br />if(lParam==0x3L){//讄Micro键的状?br /><br />if(wParam==0x1) MicroFlag=TRUE;<br /><br />else if(wParam==0x2) MicroFlag=FALSE;<br /><br />}<br /><br />break;<br /><br />Q、在q行按键l合处理Ӟ首先判断Micro键是否按下,然后再进行其它按键的判断处理?br /><br />case WM_KEYDOWN: // 按键按下处理<br /><br />if(MicroFlag==TRUE){//Micro键按?br /><br />if((BYTE)HIBYTE(wParam)==0x5b){<br /><br />//Micro+"["l合?br /><br />......//按键功能处理<br /><br />} else if((BYTE)HIBYTE(wParam)==0x5d){<br /><br />//Micro+"]"l合?br /><br />......//按键功能处理<br /><br />}<br /><br />}<br /><br />break;<br /><br />Q、当应用E序退出时应注意下载键盘监控函敎ͼ卌用InstallFilter(FALSE)函数一ơ?br /><br />Q、利用本文提供的Ҏ讄自己的应用程序功能按键,在保证程序功能按键不会与其它pȝ发生冲突的同Ӟ有效地利用了pȝ中现有资源,而且在实现应用程序功能的同时灉|应用了系l中提供的各U功能调用?img src ="http://www.shnenglu.com/xushaohua/aggbug/13704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2006-10-15 12:37 <a href="http://www.shnenglu.com/xushaohua/archive/2006/10/15/13704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]鼠标屏幕取词技术的原理和实?/title><link>http://www.shnenglu.com/xushaohua/archive/2006/10/02/13227.html</link><dc:creator>shaohua</dc:creator><author>shaohua</author><pubDate>Mon, 02 Oct 2006 12:39:00 GMT</pubDate><guid>http://www.shnenglu.com/xushaohua/archive/2006/10/02/13227.html</guid><wfw:comment>http://www.shnenglu.com/xushaohua/comments/13227.html</wfw:comment><comments>http://www.shnenglu.com/xushaohua/archive/2006/10/02/13227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/xushaohua/comments/commentRss/13227.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/xushaohua/services/trackbacks/13227.html</trackback:ping><description><![CDATA[“鼠标屏q取词”技术是在电子字怸得到q泛地应用的Q如四通利方和金山词霸{YӞq个技术看似简单,其实在WINDOWSpȝ中实现却是非常复杂的Qȝ来说有两U实现方式:<br />    W一U:采用截获寚w分GDI的API调用来实?如TextOut,TextOutA{?br />    W二U:Ҏ个设备上下文(DC)做一分Copy,q跟t所有修改上下文(DC)的操作。    ?<br />    W二U方法更强大,但兼Ҏ不好,而第一U方法用的截获WindowsAPI的调用,q项技术的强大可能q远出了您的想象,毫不夸张的说Q利用WindowsAPI拦截技术,你可以改造整个操作系l,事实上很多外挂式Windows中文q_是q么实现的!而这Ҏ术也正是q篇文章的主题?br />    截WindowsAPI的调用,具体的说来也可以分ؓ两种ҎQ?br />    W一U方法通过直接改写WinAPI 在内存中的映像,嵌入汇编代码Q之被调用时蟩转到指定的地址q行来截PW二U方法则改写IATQImport Address Table 输入地址表)Q重定向WinAPI函数的调用来实现对WinAPI的截莗?br />    W一U方法的实现较ؓJ琐Q而且在Win95?8下面更有隑ֺQ这是因然微软说WIN16的API只是Z兼容性才保留下来Q程序员应该可能地调用32位的API,实际上根本就不是q样QWIN 9X内部的大部分32位APIl过变换调用了同名的16位APIQ也是说我们需要在拦截的函C嵌入16位汇~代码!<br />    我们要介绍的是W二U拦截方法,q种Ҏ在Win95?8和NT下面q行都比较稳定,兼容性较好。由于需要用到关于Windows虚拟内存的管理、打破进E边界墙、向应用E序的进E空间中注入代码、PEQPortable ExecutableQ文件格式和IATQ输入地址表){较底层的知识,所以我们先Ҏ及到的这些知识大概地做一个介l,最后会l出拦截部分的关键代码?br />      先说Windows虚拟内存的管理。Windows9Xl每一个进E分配了4GB的地址I间Q对于NT来说Q这个数字是2GBQ系l保留了2GB?4GB之间的地址I间止q程讉KQ而在Win9X中,2GB?GBq部分虚拟地址I间实际上是由所有的WIN32q程所׃n的,q部分地址I间加蝲了共享Win32 DLL、内存映文件和VXD、内存管理器和文件系l码QWin9X中这部分对于每一个进E都是可见的Q这也是Win9X操作pȝ不够健壮的原因。Win9X中ؓ16位操作系l保留了0?MB的地址I间Q而在4MB?GB之间也就是Win32q程U有的地址I间Q由?每个q程的地址I间都是相对独立的,也就是说Q如果程序想截获其它q程中的API调用Q就必须打破q程边界墙,向其它的q程中注入截获API调用的代码,q项工作我们交给钩子函数QSetWindowsHookExQ来完成Q关于如何创Z个包含系l钩子的动态链接库Q《电脑高手杂志》在W?期已l有q专题介l了Q这里就不赘qC。所有系l钩子的函数必须要在动态库里,q样的话Q当q程隐式或显式调用一个动态库里的函数Ӟpȝ会把q个动态库映射到这个进E的虚拟地址I间里,q得DLL成ؓq程的一部分Q以q个q程的n份执行,使用q个q程的堆栈,也就是说动态链接库中的代码被钩子函数注入了其它GUIq程的地址I间Q非GUIq程Q钩子函数就无能为力了)Q?br />当包含钩子的DLL注入其它q程后,可以取得映到q个q程虚拟内存里的各个模块QEXE和DLLQ的基地址Q如Q?br />HMODULE hmodule=GetModuleHandle(“Mypro.exe?;<br />在MFCE序?我们可以用AfxGetInstanceHandle()函数来得到模块的基地址。EXE和DLL被映到虚拟内存I间的什么地Ҏ由它们的基地址军_的。它们的基地址是在链接时由链接器决定的。当你新Z个Win32工程ӞVCQ+链接器用缺省的基地址0x00400000。可以通过链接器的BASE选项改变模块的基地址。EXE通常被映到虚拟内存?x00400000处,DLL也随之有不同的基地址Q通常被映到不同q程<br />的相同的虚拟地址I间处?br />pȝEXE和DLL原封不动映射到虚拟内存空间中Q它们在内存中的l构与磁盘上的静态文件结构是一L。即PE (Portable Executable) 文g格式。我们得Cq程模块的基地址以后Q就可以ҎPE文g的格式穷举这个模块的IMAGE_IMPORT_DESCRIPTOR数组Q看看进E空间中是否引入了我们需要截L函数所在的动态链接库Q比如需要截获“TextOutA”,必L查“Gdi32.dll”是否被引入了。说到这里,我们有必要介l一下PE文g的格式,如右图,q是PE文g格式的大致框图,最前面是文件头Q我们不必理会,从PE File Optional Header后面开始,是文g中各个段的说明,说明后面才是真正的段数据Q而实际上我们兛_的只有一个段Q那是?idata”段Q这个段中包含了所有的引入函数信息Q还有IATQImport Address TableQ的RVAQRelative Virtual AddressQ地址?br />说到q里Q截获WindowsAPI的整个原理就要真相大白了。实际上所有进E对l定的API函数的调用L通过PE文g的一个地Ҏ转移的,q就是一个该模块(可以是EXE或DLL)的?idata”段中的IAT输入地址表(Import Address TableQ。在那里有所有本模块调用的其它DLL的函数名及地址。对其它DLL的函数调用实际上只是跌{到输入地址表,p入地址表再跌{到DLL真正的函数入口? <p>具体来说Q我们将通过IMAGE_IMPORT_DESCRIPTOR数组来访问?idata”段中引入的DLL的信息,然后通过IMAGE_THUNK_DATA数组来针对一个被引入的DLL讉K该DLL中被引入的每个函数的信息Q找到我们需要截L函数的蟩转地址Q然后改成我们自q函数的地址……具体的做法在后面的关键代码中会有详l的讲解?br />   讲了q么多原理,现在让我们回到“鼠标屏q取词”的专题上来。除了API函数的截P要实现“鼠标屏q取词”,q需要做一些其它的工作Q简单的说来Q可以把一个完整的取词q程归纳成以下几个步骤:<br />1Q?安装鼠标钩子Q通过钩子函数获得鼠标消息?br />使用到的API函数QSetWindowsHookEx<br />2Q?得到鼠标的当前位|,向鼠标下的窗口发重画消息Q让它调用系l函数重ȝ口?br />     使用到的API函数QWindowFromPointQScreenToClientQInvalidateRect<br />3Q?截获对系l函数的调用Q取得参敎ͼ也就是我们要取的词?br />对于大多数的Windows应用E序来说Q如果要取词Q我们需要截L是“Gdi32.dll”中的“TextOutA”函数?br />我们先仿照TextOutA函数写一个自qMyTextOutA函数Q如Q?br />BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,int cbString)<br />{<br />       // q里q行输出lpszString的处?br />           // 然后调用正版的TextOutA函数<br />}<br />把这个函数放在安装了钩子的动态连接库中,然后调用我们最后给出的HookImportFunction函数来截莯E?br />对TextOutA函数的调用,跌{到我们的MyTextOutA函数Q完成对输出字符串的捕捉。HookImportFunction?br />用法Q?br /> HOOKFUNCDESC hd;<br /> PROC         pOrigFuns;<br /> hd.szFunc=TextOutA;<br /> hd.pProc=(PROC)MyTextOutA;<br /> HookImportFunction (AfxGetInstanceHandle(),gdi32.dll,&hd,pOrigFuns);<br />下面l出了HookImportFunction的源代码Q相信详的注释一定不会让您觉得理解截获到底是怎么实现?br />很难QOk,Let’s GoQ?/p><p>///////////////////////////////////////////// Begin ///////////////////////////////////////////////////////////////<br />#include <crtdbg.h></p><p>// q里定义了一个生指针的?br />#define MakePtr(cast, ptr, AddValue) (cast)((DWORD)(ptr)+(DWORD)(AddValue))</p><p>// 定义了HOOKFUNCDESCl构,我们用这个结构作为参ClHookImportFunction函数<br />typedef struct tag_HOOKFUNCDESC<br />{<br />  LPCSTR szFunc; // The name of the function to hook.<br />  PROC pProc;    // The procedure to blast in.<br />} HOOKFUNCDESC , * LPHOOKFUNCDESC;</p><p>// q个函数监测当前pȝ是否是WindowNT<br />BOOL IsNT();</p><p>// q个函数得到hModule -- x们需要截L函数所在的DLL模块的引入描q符(import descriptor)<br />PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportModule);</p><p>// 我们的主函数<br />BOOL HookImportFunction(HMODULE hModule, LPCSTR szImportModule, <br />                         LPHOOKFUNCDESC paHookFunc, PROC* paOrigFuncs)<br />{<br />/////////////////////// 下面的代码检参数的有效?////////////////////////////<br /> _ASSERT(szImportModule);<br /> _ASSERT(!IsBadReadPtr(paHookFunc, sizeof(HOOKFUNCDESC)));<br />#ifdef _DEBUG<br /> if (paOrigFuncs) _ASSERT(!IsBadWritePtr(paOrigFuncs, sizeof(PROC)));<br /> _ASSERT(paHookFunc.szFunc);<br /> _ASSERT(*paHookFunc.szFunc != \0);<br />        _ASSERT(!IsBadCodePtr(paHookFunc.pProc));<br />#endif<br /> if ((szImportModule == NULL) || (IsBadReadPtr(paHookFunc, sizeof(HOOKFUNCDESC))))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return FALSE;<br /> }<br />//////////////////////////////////////////////////////////////////////////////</p><p> // 监测当前模块是否是在2GB虚拟内存I间之上<br /> // q部分的地址内存是属于Win32q程׃n?br /> if (!IsNT() && ((DWORD)hModule >= 0x80000000))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_HANDLE, SLE_ERROR);<br />  return FALSE;<br /> }<br />     // 清零<br /> if (paOrigFuncs) memset(paOrigFuncs, NULL, sizeof(PROC)); </p><p> // 调用GetNamedImportDescriptor()函数,来得到hModule -- x们需?br /> // 截获的函数所在的DLL模块的引入描q符(import descriptor)<br /> PIMAGE_IMPORT_DESCRIPTOR pImportDesc = GetNamedImportDescriptor(hModule, szImportModule);<br /> if (pImportDesc == NULL)<br /> return FALSE; // 若ؓI?则模块未被当前进E所引入</p><p> //  从DLL模块中得到原始的THUNK信息,因ؓpImportDesc->FirstThunk数组中的原始信息已经<br /> //  在应用程序引入该DLL时覆盖上了所有的引入信息,所以我们需要通过取得pImportDesc->OriginalFirstThunk<br /> //  指针来访问引入函数名{信?br /> PIMAGE_THUNK_DATA pOrigThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, <br />                                               pImportDesc->OriginalFirstThunk);</p><p> //  从pImportDesc->FirstThunk得到IMAGE_THUNK_DATA数组的指?׃q里在DLL被引入时已经填充?br /> //  所有的引入信息,所以真正的截获实际上正是在q里q行?br /> PIMAGE_THUNK_DATA pRealThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->FirstThunk);</p><p> //  IDIMAGE_THUNK_DATA数组,L我们需要截L函数,q是最关键的部?<br /> while (pOrigThunk->u1.Function)<br /> {<br />  // 只寻N些按函数名而不是序号引入的函数<br />  if (IMAGE_ORDINAL_FLAG != (pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG))<br />  {<br />   // 得到引入函数的函数名<br />   PIMAGE_IMPORT_BY_NAME pByName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule,<br />               pOrigThunk->u1.AddressOfData);</p><p>   // 如果函数名以NULL开?跌,l箋下一个函敊W?<br />   if (\0 == pByName->Name[0])<br />    continue;</p><p>   // bDoHook用来查是否截h?br />   BOOL bDoHook = FALSE;</p><p>   // 查是否当前函数是我们需要截L函数<br />   if ((paHookFunc.szFunc[0] == pByName->Name[0]) &&<br />    (strcmpi(paHookFunc.szFunc, (char*)pByName->Name) == 0))<br />   {<br />    // 扑ֈ?<br />    if (paHookFunc.pProc)<br />    bDoHook = TRUE;<br />   }<br />   if (bDoHook)<br />   {<br />    // 我们已经扑ֈ了所要截L函数,那么开始动手吧<br />    // 首先要做的是改变q一块虚拟内存的内存保护状?让我们可以自由存?br />    MEMORY_BASIC_INFORMATION mbi_thunk;<br />    VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));<br />    _ASSERT(VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, <br />                        PAGE_READWRITE, &mbi_thunk.Protect));</p><p>    // 保存我们所要截L函数的正蟩转地址<br />    if (paOrigFuncs)<br />      paOrigFuncs = (PROC)pRealThunk->u1.Function;</p><p>    // IMAGE_THUNK_DATA数组中的函数跌{地址改写为我们自q函数地址!<br />    // 以后所有进E对q个pȝ函数的所有调用都成为对我们自己~写的函数的调用<br />    pRealThunk->u1.Function = (PDWORD)paHookFunc.pProc;</p><p>    // 操作完毕!这一块虚拟内存改回原来的保护状?br />    DWORD dwOldProtect;<br />    _ASSERT(VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, <br />                        mbi_thunk.Protect, &dwOldProtect));<br />    SetLastError(ERROR_SUCCESS);<br />    return TRUE;<br />   }<br />  }<br />  // 讉KIMAGE_THUNK_DATA数组中的下一个元?br />  pOrigThunk++;<br />  pRealThunk++;<br /> }<br /> return TRUE;<br />}</p><p>// GetNamedImportDescriptor函数的实?br />PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportModule)<br />{<br /> // 参?br /> _ASSERT(szImportModule);<br /> _ASSERT(hModule);<br /> if ((szImportModule == NULL) || (hModule == NULL))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return NULL;<br /> }</p><p> // 得到Dos文g?br /> PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;</p><p> // 是否MZ文g?br /> if (IsBadReadPtr(pDOSHeader, sizeof(IMAGE_DOS_HEADER)) || <br />  (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return NULL;<br /> }</p><p> // 取得PE文g?br /> PIMAGE_NT_HEADERS pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew);</p><p> // 是否PE映像文g<br /> if (IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) || <br />   (pNTHeader->Signature != IMAGE_NT_SIGNATURE))<br /> {<br />  _ASSERT(FALSE);<br />  SetLastErrorEx(ERROR_INVALID_PARAMETER, SLE_ERROR);<br />  return NULL;<br /> }</p><p> // 查PE文g的引入段(?.idata section)<br /> if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)<br />  return NULL;</p><p> // 得到引入D??.idata section)的指?br /> PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDOSHeader,<br />  pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);</p><p> // IDPIMAGE_IMPORT_DESCRIPTOR数组L我们需要截L函数所在的模块<br /> while (pImportDesc->Name)<br /> {<br />  PSTR szCurrMod = MakePtr(PSTR, pDOSHeader, pImportDesc->Name);<br />  if (stricmp(szCurrMod, szImportModule) == 0)<br />      break; // 扑ֈ!中断循环<br />  // 下一个元?br />  pImportDesc++;<br /> }</p><p> // 如果没有扑ֈ,说明我们L的模块没有被当前的进E所引入!<br /> if (pImportDesc->Name == NULL)<br />  return NULL;</p><p> // q回函数所扑ֈ的模块描q符(import descriptor)<br /> return pImportDesc;<br />}</p><p>// IsNT()函数的实?br />BOOL IsNT()<br />{<br /> OSVERSIONINFO stOSVI;<br /> memset(&stOSVI, NULL, sizeof(OSVERSIONINFO));<br /> stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);<br /> BOOL bRet = GetVersionEx(&stOSVI);<br /> _ASSERT(TRUE == bRet);<br /> if (FALSE == bRet) return FALSE;<br /> return (VER_PLATFORM_WIN32_NT == stOSVI.dwPlatformId);<br />}<br />/////////////////////////////////////////////// End //////////////////////////////////////////////////////////////////////</p><p>   不知道在q篇文章问世之前Q有多少朋友试q去实现“鼠标屏q取词”这充满了挑战的技术,也只有尝试过的朋友才能体会到光的不易,其在探索API函数的截hQ手头的几篇资料没有一是涉及到关键代码的Q重要的地方都是一W代q,MSDN更是昑־苍白而无力,也不知道除了IMAGE_IMPORT_DESCRIPTOR和IMAGE_THUNK_DATAQ微软还隐藏了多秘密,好在着头皮q是把它l攻克了Q希望这文章对大家能有所帮助?/p><img src ="http://www.shnenglu.com/xushaohua/aggbug/13227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/xushaohua/" target="_blank">shaohua</a> 2006-10-02 20:39 <a href="http://www.shnenglu.com/xushaohua/archive/2006/10/02/13227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.bethesdagroup.cn" target="_blank">þþþAV</a>| <a href="http://www.990w.cn" target="_blank">鶹wwwþùƷ</a>| <a href="http://www.bushenba.cn" target="_blank">?VþþƷ</a>| <a href="http://www.sifaguojinb.cn" target="_blank">Ļ˾Ʒþò</a>| <a href="http://www.ziximaker.cn" target="_blank">þþþ޾Ʒһ</a>| <a href="http://www.abctoy.com.cn" target="_blank">һ97ձ˾þۺӰԺ</a>| <a href="http://www.aion999.cn" target="_blank">ëƬþþþþùëƬ </a>| <a href="http://www.shjinhuashiye.cn" target="_blank">þþþseɫ͵͵޾Ʒav </a>| <a href="http://www.slwte.cn" target="_blank">7777Ʒ˾þþô߽</a>| <a href="http://www.talkvo.cn" target="_blank">ۺѾƷþþ</a>| <a href="http://www.sf2777.cn" target="_blank">91þþþþþ</a>| <a href="http://www.top119.cn" target="_blank">99ȶǾƷþþþþ</a>| <a href="http://www.kkha.cn" target="_blank">˾Ʒһþþ</a>| <a href="http://www.su117q.cn" target="_blank">Ʒþþþù</a>| <a href="http://www.r10211.cn" target="_blank">þþþAVרɫ</a>| <a href="http://www.amqinhang.cn" target="_blank">þˬˬƬAV鶹 </a>| <a href="http://www.pbti.com.cn" target="_blank">þ99Ʒþþþþˮ</a>| <a href="http://www.gynp.net.cn" target="_blank">þƵۿ</a>| <a href="http://www.feimatuan.cn" target="_blank">ھƷ˾þþþӰԺ԰</a>| <a href="http://www.zzhysh.cn" target="_blank">پþþƷþ</a>| <a href="http://www.lkwg.com.cn" target="_blank">þwww˳ɾƷ㽶</a>| <a href="http://www.b24193.cn" target="_blank">רþ</a>| <a href="http://www.qlong.net.cn" target="_blank">97þþþ</a>| <a href="http://www.osgh.cn" target="_blank">޹Ʒ˾þ</a>| <a href="http://www.ter2.cn" target="_blank">ƷƵþ</a>| <a href="http://www.chaoyuemobile.com.cn" target="_blank">һһþþƷۺ</a>| <a href="http://www.rfqaod.cn" target="_blank">þۺϾþԾ99ëƬ</a>| <a href="http://www.1000su.cn" target="_blank">˾þþAV츾ɫ</a>| <a href="http://www.07sn.cn" target="_blank">þþƷ</a>| <a href="http://www.zyhyhz.cn" target="_blank">þһ99</a>| <a href="http://www.269sihu.cn" target="_blank">ɫվ޷ľþò</a>| <a href="http://www.12530downs.com.cn" target="_blank">ɫݺݾþAVۺ</a>| <a href="http://www.obsessions.cn" target="_blank">þ99ëƬѹۿ</a>| <a href="http://www.sidongliiang.cn" target="_blank">þþƷĻ</a>| <a href="http://www.ki71.cn" target="_blank">޹ƷƬþ</a>| <a href="http://www.jblw.net.cn" target="_blank">Ʒþۺ</a>| <a href="http://www.80649.cn" target="_blank">þѹƷ</a>| <a href="http://www.phb321.cn" target="_blank">ŷƷþþþþþο</a>| <a href="http://www.67yule.cn" target="_blank">ھƷ99þ</a>| <a href="http://www.qysf88.cn" target="_blank">þˬˬav</a>| <a href="http://www.pydjango.cn" target="_blank">˾þAV</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>